09e3bdfe49
This is a frequent minor comment in reviews, so start cleaning up existing drivers in the hope we get fewer cases of cut and paste. There are not kernel wide rules for these, but for IIO the style that I prefer (and hence most common) is: - Space after { and before } - No comma after terminator { } This may cause merge conflicts but they should be trivial to resolve hence I have not broken this into per driver patches. Link: https://patch.msgid.link/20240818180912.719399-1-jic23@kernel.org Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
1357 lines
38 KiB
C
1357 lines
38 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Driver for the ADC present in the Atmel AT91 evaluation boards.
|
|
*
|
|
* Copyright 2011 Free Electrons
|
|
*/
|
|
|
|
#include <linux/bitmap.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/cleanup.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/input.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/wait.h>
|
|
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/trigger.h>
|
|
#include <linux/iio/trigger_consumer.h>
|
|
#include <linux/iio/triggered_buffer.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
|
/* Registers */
|
|
#define AT91_ADC_CR 0x00 /* Control Register */
|
|
#define AT91_ADC_SWRST (1 << 0) /* Software Reset */
|
|
#define AT91_ADC_START (1 << 1) /* Start Conversion */
|
|
|
|
#define AT91_ADC_MR 0x04 /* Mode Register */
|
|
#define AT91_ADC_TSAMOD (3 << 0) /* ADC mode */
|
|
#define AT91_ADC_TSAMOD_ADC_ONLY_MODE (0 << 0) /* ADC Mode */
|
|
#define AT91_ADC_TSAMOD_TS_ONLY_MODE (1 << 0) /* Touch Screen Only Mode */
|
|
#define AT91_ADC_TRGEN (1 << 0) /* Trigger Enable */
|
|
#define AT91_ADC_TRGSEL (7 << 1) /* Trigger Selection */
|
|
#define AT91_ADC_TRGSEL_TC0 (0 << 1)
|
|
#define AT91_ADC_TRGSEL_TC1 (1 << 1)
|
|
#define AT91_ADC_TRGSEL_TC2 (2 << 1)
|
|
#define AT91_ADC_TRGSEL_EXTERNAL (6 << 1)
|
|
#define AT91_ADC_LOWRES (1 << 4) /* Low Resolution */
|
|
#define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */
|
|
#define AT91_ADC_PENDET (1 << 6) /* Pen contact detection enable */
|
|
#define AT91_ADC_PRESCAL_9260 (0x3f << 8) /* Prescalar Rate Selection */
|
|
#define AT91_ADC_PRESCAL_9G45 (0xff << 8)
|
|
#define AT91_ADC_PRESCAL_(x) ((x) << 8)
|
|
#define AT91_ADC_STARTUP_9260 (0x1f << 16) /* Startup Up Time */
|
|
#define AT91_ADC_STARTUP_9G45 (0x7f << 16)
|
|
#define AT91_ADC_STARTUP_9X5 (0xf << 16)
|
|
#define AT91_ADC_STARTUP_(x) ((x) << 16)
|
|
#define AT91_ADC_SHTIM (0xf << 24) /* Sample & Hold Time */
|
|
#define AT91_ADC_SHTIM_(x) ((x) << 24)
|
|
#define AT91_ADC_PENDBC (0x0f << 28) /* Pen Debounce time */
|
|
#define AT91_ADC_PENDBC_(x) ((x) << 28)
|
|
|
|
#define AT91_ADC_TSR 0x0C
|
|
#define AT91_ADC_TSR_SHTIM (0xf << 24) /* Sample & Hold Time */
|
|
#define AT91_ADC_TSR_SHTIM_(x) ((x) << 24)
|
|
|
|
#define AT91_ADC_CHER 0x10 /* Channel Enable Register */
|
|
#define AT91_ADC_CHDR 0x14 /* Channel Disable Register */
|
|
#define AT91_ADC_CHSR 0x18 /* Channel Status Register */
|
|
#define AT91_ADC_CH(n) (1 << (n)) /* Channel Number */
|
|
|
|
#define AT91_ADC_SR 0x1C /* Status Register */
|
|
#define AT91_ADC_EOC(n) (1 << (n)) /* End of Conversion on Channel N */
|
|
#define AT91_ADC_OVRE(n) (1 << ((n) + 8))/* Overrun Error on Channel N */
|
|
#define AT91_ADC_DRDY (1 << 16) /* Data Ready */
|
|
#define AT91_ADC_GOVRE (1 << 17) /* General Overrun Error */
|
|
#define AT91_ADC_ENDRX (1 << 18) /* End of RX Buffer */
|
|
#define AT91_ADC_RXFUFF (1 << 19) /* RX Buffer Full */
|
|
|
|
#define AT91_ADC_SR_9X5 0x30 /* Status Register for 9x5 */
|
|
#define AT91_ADC_SR_DRDY_9X5 (1 << 24) /* Data Ready */
|
|
|
|
#define AT91_ADC_LCDR 0x20 /* Last Converted Data Register */
|
|
#define AT91_ADC_LDATA (0x3ff)
|
|
|
|
#define AT91_ADC_IER 0x24 /* Interrupt Enable Register */
|
|
#define AT91_ADC_IDR 0x28 /* Interrupt Disable Register */
|
|
#define AT91_ADC_IMR 0x2C /* Interrupt Mask Register */
|
|
#define AT91RL_ADC_IER_PEN (1 << 20)
|
|
#define AT91RL_ADC_IER_NOPEN (1 << 21)
|
|
#define AT91_ADC_IER_PEN (1 << 29)
|
|
#define AT91_ADC_IER_NOPEN (1 << 30)
|
|
#define AT91_ADC_IER_XRDY (1 << 20)
|
|
#define AT91_ADC_IER_YRDY (1 << 21)
|
|
#define AT91_ADC_IER_PRDY (1 << 22)
|
|
#define AT91_ADC_ISR_PENS (1 << 31)
|
|
|
|
#define AT91_ADC_CHR(n) (0x30 + ((n) * 4)) /* Channel Data Register N */
|
|
#define AT91_ADC_DATA (0x3ff)
|
|
|
|
#define AT91_ADC_CDR0_9X5 (0x50) /* Channel Data Register 0 for 9X5 */
|
|
|
|
#define AT91_ADC_ACR 0x94 /* Analog Control Register */
|
|
#define AT91_ADC_ACR_PENDETSENS (0x3 << 0) /* pull-up resistor */
|
|
|
|
#define AT91_ADC_TSMR 0xB0
|
|
#define AT91_ADC_TSMR_TSMODE (3 << 0) /* Touch Screen Mode */
|
|
#define AT91_ADC_TSMR_TSMODE_NONE (0 << 0)
|
|
#define AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS (1 << 0)
|
|
#define AT91_ADC_TSMR_TSMODE_4WIRE_PRESS (2 << 0)
|
|
#define AT91_ADC_TSMR_TSMODE_5WIRE (3 << 0)
|
|
#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */
|
|
#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4)
|
|
#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */
|
|
#define AT91_ADC_TSMR_SCTIM_(x) ((x) << 16)
|
|
#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */
|
|
#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28)
|
|
#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */
|
|
#define AT91_ADC_TSMR_PENDET_DIS (0 << 24) /* Pen contact detection disable */
|
|
#define AT91_ADC_TSMR_PENDET_ENA (1 << 24) /* Pen contact detection enable */
|
|
|
|
#define AT91_ADC_TSXPOSR 0xB4
|
|
#define AT91_ADC_TSYPOSR 0xB8
|
|
#define AT91_ADC_TSPRESSR 0xBC
|
|
|
|
#define AT91_ADC_TRGR_9260 AT91_ADC_MR
|
|
#define AT91_ADC_TRGR_9G45 0x08
|
|
#define AT91_ADC_TRGR_9X5 0xC0
|
|
|
|
/* Trigger Register bit field */
|
|
#define AT91_ADC_TRGR_TRGPER (0xffff << 16)
|
|
#define AT91_ADC_TRGR_TRGPER_(x) ((x) << 16)
|
|
#define AT91_ADC_TRGR_TRGMOD (0x7 << 0)
|
|
#define AT91_ADC_TRGR_NONE (0 << 0)
|
|
#define AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0)
|
|
|
|
#define AT91_ADC_CHAN(st, ch) \
|
|
(st->registers->channel_base + (ch * 4))
|
|
#define at91_adc_readl(st, reg) \
|
|
(readl_relaxed(st->reg_base + reg))
|
|
#define at91_adc_writel(st, reg, val) \
|
|
(writel_relaxed(val, st->reg_base + reg))
|
|
|
|
#define DRIVER_NAME "at91_adc"
|
|
#define MAX_POS_BITS 12
|
|
|
|
#define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */
|
|
#define TOUCH_PEN_DETECT_DEBOUNCE_US 200
|
|
|
|
#define MAX_RLPOS_BITS 10
|
|
#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */
|
|
#define TOUCH_SHTIM 0xa
|
|
#define TOUCH_SCTIM_US 10 /* 10us for the Touchscreen Switches Closure Time */
|
|
|
|
enum atmel_adc_ts_type {
|
|
ATMEL_ADC_TOUCHSCREEN_NONE = 0,
|
|
ATMEL_ADC_TOUCHSCREEN_4WIRE = 4,
|
|
ATMEL_ADC_TOUCHSCREEN_5WIRE = 5,
|
|
};
|
|
|
|
/**
|
|
* struct at91_adc_trigger - description of triggers
|
|
* @name: name of the trigger advertised to the user
|
|
* @value: value to set in the ADC's trigger setup register
|
|
* to enable the trigger
|
|
* @is_external: Does the trigger rely on an external pin?
|
|
*/
|
|
struct at91_adc_trigger {
|
|
const char *name;
|
|
u8 value;
|
|
bool is_external;
|
|
};
|
|
|
|
/**
|
|
* struct at91_adc_reg_desc - Various informations relative to registers
|
|
* @channel_base: Base offset for the channel data registers
|
|
* @drdy_mask: Mask of the DRDY field in the relevant registers
|
|
* (Interruptions registers mostly)
|
|
* @status_register: Offset of the Interrupt Status Register
|
|
* @trigger_register: Offset of the Trigger setup register
|
|
* @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register
|
|
* @mr_startup_mask: Mask of the STARTUP field in the adc MR register
|
|
*/
|
|
struct at91_adc_reg_desc {
|
|
u8 channel_base;
|
|
u32 drdy_mask;
|
|
u8 status_register;
|
|
u8 trigger_register;
|
|
u32 mr_prescal_mask;
|
|
u32 mr_startup_mask;
|
|
};
|
|
|
|
struct at91_adc_caps {
|
|
bool has_ts; /* Support touch screen */
|
|
bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */
|
|
/*
|
|
* Numbers of sampling data will be averaged. Can be 0~3.
|
|
* Hardware can average (2 ^ ts_filter_average) sample data.
|
|
*/
|
|
u8 ts_filter_average;
|
|
/* Pen Detection input pull-up resistor, can be 0~3 */
|
|
u8 ts_pen_detect_sensitivity;
|
|
|
|
/* startup time calculate function */
|
|
u32 (*calc_startup_ticks)(u32 startup_time, u32 adc_clk_khz);
|
|
|
|
u8 num_channels;
|
|
|
|
u8 low_res_bits;
|
|
u8 high_res_bits;
|
|
u32 trigger_number;
|
|
const struct at91_adc_trigger *triggers;
|
|
struct at91_adc_reg_desc registers;
|
|
};
|
|
|
|
struct at91_adc_state {
|
|
struct clk *adc_clk;
|
|
u16 *buffer;
|
|
unsigned long channels_mask;
|
|
struct clk *clk;
|
|
bool done;
|
|
int irq;
|
|
u16 last_value;
|
|
int chnb;
|
|
struct mutex lock;
|
|
u8 num_channels;
|
|
void __iomem *reg_base;
|
|
const struct at91_adc_reg_desc *registers;
|
|
u32 startup_time;
|
|
u8 sample_hold_time;
|
|
bool sleep_mode;
|
|
struct iio_trigger **trig;
|
|
bool use_external;
|
|
u32 vref_mv;
|
|
u32 res; /* resolution used for convertions */
|
|
wait_queue_head_t wq_data_avail;
|
|
const struct at91_adc_caps *caps;
|
|
|
|
/*
|
|
* Following ADC channels are shared by touchscreen:
|
|
*
|
|
* CH0 -- Touch screen XP/UL
|
|
* CH1 -- Touch screen XM/UR
|
|
* CH2 -- Touch screen YP/LL
|
|
* CH3 -- Touch screen YM/Sense
|
|
* CH4 -- Touch screen LR(5-wire only)
|
|
*
|
|
* The bitfields below represents the reserved channel in the
|
|
* touchscreen mode.
|
|
*/
|
|
#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 0)
|
|
#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 0)
|
|
enum atmel_adc_ts_type touchscreen_type;
|
|
struct input_dev *ts_input;
|
|
|
|
u16 ts_sample_period_val;
|
|
u32 ts_pressure_threshold;
|
|
u16 ts_pendbc;
|
|
|
|
bool ts_bufferedmeasure;
|
|
u32 ts_prev_absx;
|
|
u32 ts_prev_absy;
|
|
};
|
|
|
|
static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *idev = pf->indio_dev;
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
struct iio_chan_spec const *chan;
|
|
int i, j = 0;
|
|
|
|
iio_for_each_active_channel(idev, i) {
|
|
chan = idev->channels + i;
|
|
st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
|
|
j++;
|
|
}
|
|
|
|
iio_push_to_buffers_with_timestamp(idev, st->buffer, pf->timestamp);
|
|
|
|
iio_trigger_notify_done(idev->trig);
|
|
|
|
/* Needed to ACK the DRDY interruption */
|
|
at91_adc_readl(st, AT91_ADC_LCDR);
|
|
|
|
enable_irq(st->irq);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/* Handler for classic adc channel eoc trigger */
|
|
static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
|
|
if (iio_buffer_enabled(idev)) {
|
|
disable_irq_nosync(irq);
|
|
iio_trigger_poll(idev->trig);
|
|
} else {
|
|
st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb));
|
|
/* Needed to ACK the DRDY interruption */
|
|
at91_adc_readl(st, AT91_ADC_LCDR);
|
|
st->done = true;
|
|
wake_up_interruptible(&st->wq_data_avail);
|
|
}
|
|
}
|
|
|
|
static int at91_ts_sample(struct iio_dev *idev)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
unsigned int xscale, yscale, reg, z1, z2;
|
|
unsigned int x, y, pres, xpos, ypos;
|
|
unsigned int rxp = 1;
|
|
unsigned int factor = 1000;
|
|
|
|
unsigned int xyz_mask_bits = st->res;
|
|
unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;
|
|
|
|
/* calculate position */
|
|
/* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
|
|
reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
|
|
xpos = reg & xyz_mask;
|
|
x = (xpos << MAX_POS_BITS) - xpos;
|
|
xscale = (reg >> 16) & xyz_mask;
|
|
if (xscale == 0) {
|
|
dev_err(&idev->dev, "Error: xscale == 0!\n");
|
|
return -1;
|
|
}
|
|
x /= xscale;
|
|
|
|
/* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
|
|
reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
|
|
ypos = reg & xyz_mask;
|
|
y = (ypos << MAX_POS_BITS) - ypos;
|
|
yscale = (reg >> 16) & xyz_mask;
|
|
if (yscale == 0) {
|
|
dev_err(&idev->dev, "Error: yscale == 0!\n");
|
|
return -1;
|
|
}
|
|
y /= yscale;
|
|
|
|
/* calculate the pressure */
|
|
reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
|
|
z1 = reg & xyz_mask;
|
|
z2 = (reg >> 16) & xyz_mask;
|
|
|
|
if (z1 != 0)
|
|
pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
|
|
/ factor;
|
|
else
|
|
pres = st->ts_pressure_threshold; /* no pen contacted */
|
|
|
|
dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
|
|
xpos, xscale, ypos, yscale, z1, z2, pres);
|
|
|
|
if (pres < st->ts_pressure_threshold) {
|
|
dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
|
|
x, y, pres / factor);
|
|
input_report_abs(st->ts_input, ABS_X, x);
|
|
input_report_abs(st->ts_input, ABS_Y, y);
|
|
input_report_abs(st->ts_input, ABS_PRESSURE, pres);
|
|
input_report_key(st->ts_input, BTN_TOUCH, 1);
|
|
input_sync(st->ts_input);
|
|
} else {
|
|
dev_dbg(&idev->dev, "pressure too low: not reporting\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t at91_adc_rl_interrupt(int irq, void *private)
|
|
{
|
|
struct iio_dev *idev = private;
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
u32 status = at91_adc_readl(st, st->registers->status_register);
|
|
unsigned int reg;
|
|
|
|
status &= at91_adc_readl(st, AT91_ADC_IMR);
|
|
if (status & GENMASK(st->num_channels - 1, 0))
|
|
handle_adc_eoc_trigger(irq, idev);
|
|
|
|
if (status & AT91RL_ADC_IER_PEN) {
|
|
/* Disabling pen debounce is required to get a NOPEN irq */
|
|
reg = at91_adc_readl(st, AT91_ADC_MR);
|
|
reg &= ~AT91_ADC_PENDBC;
|
|
at91_adc_writel(st, AT91_ADC_MR, reg);
|
|
|
|
at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN);
|
|
at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_NOPEN
|
|
| AT91_ADC_EOC(3));
|
|
/* Set up period trigger for sampling */
|
|
at91_adc_writel(st, st->registers->trigger_register,
|
|
AT91_ADC_TRGR_MOD_PERIOD_TRIG |
|
|
AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
|
|
} else if (status & AT91RL_ADC_IER_NOPEN) {
|
|
reg = at91_adc_readl(st, AT91_ADC_MR);
|
|
reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC;
|
|
at91_adc_writel(st, AT91_ADC_MR, reg);
|
|
at91_adc_writel(st, st->registers->trigger_register,
|
|
AT91_ADC_TRGR_NONE);
|
|
|
|
at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_NOPEN
|
|
| AT91_ADC_EOC(3));
|
|
at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN);
|
|
st->ts_bufferedmeasure = false;
|
|
input_report_key(st->ts_input, BTN_TOUCH, 0);
|
|
input_sync(st->ts_input);
|
|
} else if (status & AT91_ADC_EOC(3) && st->ts_input) {
|
|
/* Conversion finished and we've a touchscreen */
|
|
if (st->ts_bufferedmeasure) {
|
|
/*
|
|
* Last measurement is always discarded, since it can
|
|
* be erroneous.
|
|
* Always report previous measurement
|
|
*/
|
|
input_report_abs(st->ts_input, ABS_X, st->ts_prev_absx);
|
|
input_report_abs(st->ts_input, ABS_Y, st->ts_prev_absy);
|
|
input_report_key(st->ts_input, BTN_TOUCH, 1);
|
|
input_sync(st->ts_input);
|
|
} else
|
|
st->ts_bufferedmeasure = true;
|
|
|
|
/* Now make new measurement */
|
|
st->ts_prev_absx = at91_adc_readl(st, AT91_ADC_CHAN(st, 3))
|
|
<< MAX_RLPOS_BITS;
|
|
st->ts_prev_absx /= at91_adc_readl(st, AT91_ADC_CHAN(st, 2));
|
|
|
|
st->ts_prev_absy = at91_adc_readl(st, AT91_ADC_CHAN(st, 1))
|
|
<< MAX_RLPOS_BITS;
|
|
st->ts_prev_absy /= at91_adc_readl(st, AT91_ADC_CHAN(st, 0));
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private)
|
|
{
|
|
struct iio_dev *idev = private;
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
u32 status = at91_adc_readl(st, st->registers->status_register);
|
|
const uint32_t ts_data_irq_mask =
|
|
AT91_ADC_IER_XRDY |
|
|
AT91_ADC_IER_YRDY |
|
|
AT91_ADC_IER_PRDY;
|
|
|
|
if (status & GENMASK(st->num_channels - 1, 0))
|
|
handle_adc_eoc_trigger(irq, idev);
|
|
|
|
if (status & AT91_ADC_IER_PEN) {
|
|
at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
|
|
at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
|
|
ts_data_irq_mask);
|
|
/* Set up period trigger for sampling */
|
|
at91_adc_writel(st, st->registers->trigger_register,
|
|
AT91_ADC_TRGR_MOD_PERIOD_TRIG |
|
|
AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
|
|
} else if (status & AT91_ADC_IER_NOPEN) {
|
|
at91_adc_writel(st, st->registers->trigger_register, 0);
|
|
at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
|
|
ts_data_irq_mask);
|
|
at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
|
|
|
|
input_report_key(st->ts_input, BTN_TOUCH, 0);
|
|
input_sync(st->ts_input);
|
|
} else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
|
|
/* Now all touchscreen data is ready */
|
|
|
|
if (status & AT91_ADC_ISR_PENS) {
|
|
/* validate data by pen contact */
|
|
at91_ts_sample(idev);
|
|
} else {
|
|
/* triggered by event that is no pen contact, just read
|
|
* them to clean the interrupt and discard all.
|
|
*/
|
|
at91_adc_readl(st, AT91_ADC_TSXPOSR);
|
|
at91_adc_readl(st, AT91_ADC_TSYPOSR);
|
|
at91_adc_readl(st, AT91_ADC_TSPRESSR);
|
|
}
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int at91_adc_channel_init(struct iio_dev *idev)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
struct iio_chan_spec *chan_array, *timestamp;
|
|
int bit, idx = 0;
|
|
unsigned long rsvd_mask = 0;
|
|
|
|
/* If touchscreen is enable, then reserve the adc channels */
|
|
if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
|
|
rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
|
|
else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
|
|
rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;
|
|
|
|
/* set up the channel mask to reserve touchscreen channels */
|
|
st->channels_mask &= ~rsvd_mask;
|
|
|
|
idev->num_channels = bitmap_weight(&st->channels_mask,
|
|
st->num_channels) + 1;
|
|
|
|
chan_array = devm_kzalloc(&idev->dev,
|
|
((idev->num_channels + 1) *
|
|
sizeof(struct iio_chan_spec)),
|
|
GFP_KERNEL);
|
|
|
|
if (!chan_array)
|
|
return -ENOMEM;
|
|
|
|
for_each_set_bit(bit, &st->channels_mask, st->num_channels) {
|
|
struct iio_chan_spec *chan = chan_array + idx;
|
|
|
|
chan->type = IIO_VOLTAGE;
|
|
chan->indexed = 1;
|
|
chan->channel = bit;
|
|
chan->scan_index = idx;
|
|
chan->scan_type.sign = 'u';
|
|
chan->scan_type.realbits = st->res;
|
|
chan->scan_type.storagebits = 16;
|
|
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
|
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
|
idx++;
|
|
}
|
|
timestamp = chan_array + idx;
|
|
|
|
timestamp->type = IIO_TIMESTAMP;
|
|
timestamp->channel = -1;
|
|
timestamp->scan_index = idx;
|
|
timestamp->scan_type.sign = 's';
|
|
timestamp->scan_type.realbits = 64;
|
|
timestamp->scan_type.storagebits = 64;
|
|
|
|
idev->channels = chan_array;
|
|
return idev->num_channels;
|
|
}
|
|
|
|
static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
|
|
const struct at91_adc_trigger *triggers,
|
|
const char *trigger_name)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
int i;
|
|
|
|
for (i = 0; i < st->caps->trigger_number; i++) {
|
|
char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s-dev%d-%s",
|
|
idev->name,
|
|
iio_device_id(idev),
|
|
triggers[i].name);
|
|
if (!name)
|
|
return -ENOMEM;
|
|
|
|
if (strcmp(trigger_name, name) == 0) {
|
|
if (triggers[i].value == 0)
|
|
return -EINVAL;
|
|
return triggers[i].value;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
|
|
{
|
|
struct iio_dev *idev = iio_trigger_get_drvdata(trig);
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
const struct at91_adc_reg_desc *reg = st->registers;
|
|
u32 status = at91_adc_readl(st, reg->trigger_register);
|
|
int value;
|
|
u8 bit;
|
|
|
|
value = at91_adc_get_trigger_value_by_name(idev,
|
|
st->caps->triggers,
|
|
idev->trig->name);
|
|
if (value < 0)
|
|
return value;
|
|
|
|
if (state) {
|
|
st->buffer = kmalloc(idev->scan_bytes, GFP_KERNEL);
|
|
if (st->buffer == NULL)
|
|
return -ENOMEM;
|
|
|
|
at91_adc_writel(st, reg->trigger_register,
|
|
status | value);
|
|
|
|
for_each_set_bit(bit, idev->active_scan_mask,
|
|
st->num_channels) {
|
|
struct iio_chan_spec const *chan = idev->channels + bit;
|
|
at91_adc_writel(st, AT91_ADC_CHER,
|
|
AT91_ADC_CH(chan->channel));
|
|
}
|
|
|
|
at91_adc_writel(st, AT91_ADC_IER, reg->drdy_mask);
|
|
|
|
} else {
|
|
at91_adc_writel(st, AT91_ADC_IDR, reg->drdy_mask);
|
|
|
|
at91_adc_writel(st, reg->trigger_register,
|
|
status & ~value);
|
|
|
|
for_each_set_bit(bit, idev->active_scan_mask,
|
|
st->num_channels) {
|
|
struct iio_chan_spec const *chan = idev->channels + bit;
|
|
at91_adc_writel(st, AT91_ADC_CHDR,
|
|
AT91_ADC_CH(chan->channel));
|
|
}
|
|
kfree(st->buffer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct iio_trigger_ops at91_adc_trigger_ops = {
|
|
.set_trigger_state = &at91_adc_configure_trigger,
|
|
};
|
|
|
|
static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
|
|
const struct at91_adc_trigger *trigger)
|
|
{
|
|
struct iio_trigger *trig;
|
|
int ret;
|
|
|
|
trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name,
|
|
iio_device_id(idev), trigger->name);
|
|
if (trig == NULL)
|
|
return NULL;
|
|
|
|
iio_trigger_set_drvdata(trig, idev);
|
|
trig->ops = &at91_adc_trigger_ops;
|
|
|
|
ret = iio_trigger_register(trig);
|
|
if (ret) {
|
|
iio_trigger_free(trig);
|
|
return NULL;
|
|
}
|
|
|
|
return trig;
|
|
}
|
|
|
|
static int at91_adc_trigger_init(struct iio_dev *idev)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
int i, ret;
|
|
|
|
st->trig = devm_kcalloc(&idev->dev,
|
|
st->caps->trigger_number, sizeof(*st->trig),
|
|
GFP_KERNEL);
|
|
|
|
if (st->trig == NULL) {
|
|
ret = -ENOMEM;
|
|
goto error_ret;
|
|
}
|
|
|
|
for (i = 0; i < st->caps->trigger_number; i++) {
|
|
if (st->caps->triggers[i].is_external && !(st->use_external))
|
|
continue;
|
|
|
|
st->trig[i] = at91_adc_allocate_trigger(idev,
|
|
st->caps->triggers + i);
|
|
if (st->trig[i] == NULL) {
|
|
dev_err(&idev->dev,
|
|
"Could not allocate trigger %d\n", i);
|
|
ret = -ENOMEM;
|
|
goto error_trigger;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error_trigger:
|
|
for (i--; i >= 0; i--) {
|
|
iio_trigger_unregister(st->trig[i]);
|
|
iio_trigger_free(st->trig[i]);
|
|
}
|
|
error_ret:
|
|
return ret;
|
|
}
|
|
|
|
static void at91_adc_trigger_remove(struct iio_dev *idev)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
int i;
|
|
|
|
for (i = 0; i < st->caps->trigger_number; i++) {
|
|
iio_trigger_unregister(st->trig[i]);
|
|
iio_trigger_free(st->trig[i]);
|
|
}
|
|
}
|
|
|
|
static int at91_adc_buffer_init(struct iio_dev *idev)
|
|
{
|
|
return iio_triggered_buffer_setup(idev, &iio_pollfunc_store_time,
|
|
&at91_adc_trigger_handler, NULL);
|
|
}
|
|
|
|
static void at91_adc_buffer_remove(struct iio_dev *idev)
|
|
{
|
|
iio_triggered_buffer_cleanup(idev);
|
|
}
|
|
|
|
static int at91_adc_read_raw(struct iio_dev *idev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
int ret;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
mutex_lock(&st->lock);
|
|
|
|
st->chnb = chan->channel;
|
|
at91_adc_writel(st, AT91_ADC_CHER,
|
|
AT91_ADC_CH(chan->channel));
|
|
at91_adc_writel(st, AT91_ADC_IER, BIT(chan->channel));
|
|
at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_START);
|
|
|
|
ret = wait_event_interruptible_timeout(st->wq_data_avail,
|
|
st->done,
|
|
msecs_to_jiffies(1000));
|
|
|
|
/* Disable interrupts, regardless if adc conversion was
|
|
* successful or not
|
|
*/
|
|
at91_adc_writel(st, AT91_ADC_CHDR,
|
|
AT91_ADC_CH(chan->channel));
|
|
at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));
|
|
|
|
if (ret > 0) {
|
|
/* a valid conversion took place */
|
|
*val = st->last_value;
|
|
st->last_value = 0;
|
|
st->done = false;
|
|
ret = IIO_VAL_INT;
|
|
} else if (ret == 0) {
|
|
/* conversion timeout */
|
|
dev_err(&idev->dev, "ADC Channel %d timeout.\n",
|
|
chan->channel);
|
|
ret = -ETIMEDOUT;
|
|
}
|
|
|
|
mutex_unlock(&st->lock);
|
|
return ret;
|
|
|
|
case IIO_CHAN_INFO_SCALE:
|
|
*val = st->vref_mv;
|
|
*val2 = chan->scan_type.realbits;
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
default:
|
|
break;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
static u32 calc_startup_ticks_9260(u32 startup_time, u32 adc_clk_khz)
|
|
{
|
|
/*
|
|
* Number of ticks needed to cover the startup time of the ADC
|
|
* as defined in the electrical characteristics of the board,
|
|
* divided by 8. The formula thus is :
|
|
* Startup Time = (ticks + 1) * 8 / ADC Clock
|
|
*/
|
|
return round_up((startup_time * adc_clk_khz / 1000) - 1, 8) / 8;
|
|
}
|
|
|
|
static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz)
|
|
{
|
|
/*
|
|
* For sama5d3x and at91sam9x5, the formula changes to:
|
|
* Startup Time = <lookup_table_value> / ADC Clock
|
|
*/
|
|
static const int startup_lookup[] = {
|
|
0, 8, 16, 24,
|
|
64, 80, 96, 112,
|
|
512, 576, 640, 704,
|
|
768, 832, 896, 960
|
|
};
|
|
int i, size = ARRAY_SIZE(startup_lookup);
|
|
unsigned int ticks;
|
|
|
|
ticks = startup_time * adc_clk_khz / 1000;
|
|
for (i = 0; i < size; i++)
|
|
if (ticks < startup_lookup[i])
|
|
break;
|
|
|
|
ticks = i;
|
|
if (ticks == size)
|
|
/* Reach the end of lookup table */
|
|
ticks = size - 1;
|
|
|
|
return ticks;
|
|
}
|
|
|
|
static int at91_adc_probe_dt_ts(struct device_node *node,
|
|
struct at91_adc_state *st, struct device *dev)
|
|
{
|
|
int ret;
|
|
u32 prop;
|
|
|
|
ret = of_property_read_u32(node, "atmel,adc-ts-wires", &prop);
|
|
if (ret) {
|
|
dev_info(dev, "ADC Touch screen is disabled.\n");
|
|
return 0;
|
|
}
|
|
|
|
switch (prop) {
|
|
case 4:
|
|
case 5:
|
|
st->touchscreen_type = prop;
|
|
break;
|
|
default:
|
|
dev_err(dev, "Unsupported number of touchscreen wires (%d). Should be 4 or 5.\n", prop);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!st->caps->has_tsmr)
|
|
return 0;
|
|
prop = 0;
|
|
of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop);
|
|
st->ts_pressure_threshold = prop;
|
|
if (st->ts_pressure_threshold) {
|
|
return 0;
|
|
} else {
|
|
dev_err(dev, "Invalid pressure threshold for the touchscreen\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static const struct iio_info at91_adc_info = {
|
|
.read_raw = &at91_adc_read_raw,
|
|
};
|
|
|
|
/* Touchscreen related functions */
|
|
static int atmel_ts_open(struct input_dev *dev)
|
|
{
|
|
struct at91_adc_state *st = input_get_drvdata(dev);
|
|
|
|
if (st->caps->has_tsmr)
|
|
at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
|
|
else
|
|
at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN);
|
|
return 0;
|
|
}
|
|
|
|
static void atmel_ts_close(struct input_dev *dev)
|
|
{
|
|
struct at91_adc_state *st = input_get_drvdata(dev);
|
|
|
|
if (st->caps->has_tsmr)
|
|
at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
|
|
else
|
|
at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN);
|
|
}
|
|
|
|
static int at91_ts_hw_init(struct iio_dev *idev, u32 adc_clk_khz)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
u32 reg = 0;
|
|
u32 tssctim = 0;
|
|
int i = 0;
|
|
|
|
/* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
|
|
* pen detect noise.
|
|
* The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
|
|
*/
|
|
st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz /
|
|
1000, 1);
|
|
|
|
while (st->ts_pendbc >> ++i)
|
|
; /* Empty! Find the shift offset */
|
|
if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1))))
|
|
st->ts_pendbc = i;
|
|
else
|
|
st->ts_pendbc = i - 1;
|
|
|
|
if (!st->caps->has_tsmr) {
|
|
reg = at91_adc_readl(st, AT91_ADC_MR);
|
|
reg |= AT91_ADC_TSAMOD_TS_ONLY_MODE | AT91_ADC_PENDET;
|
|
|
|
reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC;
|
|
at91_adc_writel(st, AT91_ADC_MR, reg);
|
|
|
|
reg = AT91_ADC_TSR_SHTIM_(TOUCH_SHTIM) & AT91_ADC_TSR_SHTIM;
|
|
at91_adc_writel(st, AT91_ADC_TSR, reg);
|
|
|
|
st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US_RL *
|
|
adc_clk_khz / 1000) - 1, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Touchscreen Switches Closure time needed for allowing the value to
|
|
* stabilize.
|
|
* Switch Closure Time = (TSSCTIM * 4) ADCClock periods
|
|
*/
|
|
tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4);
|
|
dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n",
|
|
adc_clk_khz, tssctim);
|
|
|
|
if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
|
|
reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
|
|
else
|
|
reg = AT91_ADC_TSMR_TSMODE_5WIRE;
|
|
|
|
reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM;
|
|
reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average)
|
|
& AT91_ADC_TSMR_TSAV;
|
|
reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC;
|
|
reg |= AT91_ADC_TSMR_NOTSDMA;
|
|
reg |= AT91_ADC_TSMR_PENDET_ENA;
|
|
reg |= 0x03 << 8; /* TSFREQ, needs to be bigger than TSAV */
|
|
|
|
at91_adc_writel(st, AT91_ADC_TSMR, reg);
|
|
|
|
/* Change adc internal resistor value for better pen detection,
|
|
* default value is 100 kOhm.
|
|
* 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
|
|
* option only available on ES2 and higher
|
|
*/
|
|
at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity
|
|
& AT91_ADC_ACR_PENDETSENS);
|
|
|
|
/* Sample Period Time = (TRGPER + 1) / ADCClock */
|
|
st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US *
|
|
adc_clk_khz / 1000) - 1, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at91_ts_register(struct iio_dev *idev,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
struct input_dev *input;
|
|
int ret;
|
|
|
|
input = input_allocate_device();
|
|
if (!input) {
|
|
dev_err(&idev->dev, "Failed to allocate TS device!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
input->name = DRIVER_NAME;
|
|
input->id.bustype = BUS_HOST;
|
|
input->dev.parent = &pdev->dev;
|
|
input->open = atmel_ts_open;
|
|
input->close = atmel_ts_close;
|
|
|
|
__set_bit(EV_ABS, input->evbit);
|
|
__set_bit(EV_KEY, input->evbit);
|
|
__set_bit(BTN_TOUCH, input->keybit);
|
|
if (st->caps->has_tsmr) {
|
|
input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1,
|
|
0, 0);
|
|
input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1,
|
|
0, 0);
|
|
input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
|
|
} else {
|
|
if (st->touchscreen_type != ATMEL_ADC_TOUCHSCREEN_4WIRE) {
|
|
dev_err(&pdev->dev,
|
|
"This touchscreen controller only support 4 wires\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
input_set_abs_params(input, ABS_X, 0, (1 << MAX_RLPOS_BITS) - 1,
|
|
0, 0);
|
|
input_set_abs_params(input, ABS_Y, 0, (1 << MAX_RLPOS_BITS) - 1,
|
|
0, 0);
|
|
}
|
|
|
|
st->ts_input = input;
|
|
input_set_drvdata(input, st);
|
|
|
|
ret = input_register_device(input);
|
|
if (ret)
|
|
goto err;
|
|
|
|
return ret;
|
|
|
|
err:
|
|
input_free_device(st->ts_input);
|
|
return ret;
|
|
}
|
|
|
|
static void at91_ts_unregister(struct at91_adc_state *st)
|
|
{
|
|
input_unregister_device(st->ts_input);
|
|
}
|
|
|
|
static int at91_adc_probe(struct platform_device *pdev)
|
|
{
|
|
unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
|
|
struct device_node *node = pdev->dev.of_node;
|
|
int ret;
|
|
struct iio_dev *idev;
|
|
struct at91_adc_state *st;
|
|
u32 reg, prop;
|
|
char *s;
|
|
|
|
idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state));
|
|
if (!idev)
|
|
return -ENOMEM;
|
|
|
|
st = iio_priv(idev);
|
|
|
|
st->caps = of_device_get_match_data(&pdev->dev);
|
|
|
|
st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
|
|
|
|
if (of_property_read_u32(node, "atmel,adc-channels-used", &prop))
|
|
return dev_err_probe(&idev->dev, -EINVAL,
|
|
"Missing adc-channels-used property in the DT.\n");
|
|
st->channels_mask = prop;
|
|
|
|
st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode");
|
|
|
|
if (of_property_read_u32(node, "atmel,adc-startup-time", &prop))
|
|
return dev_err_probe(&idev->dev, -EINVAL,
|
|
"Missing adc-startup-time property in the DT.\n");
|
|
st->startup_time = prop;
|
|
|
|
prop = 0;
|
|
of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop);
|
|
st->sample_hold_time = prop;
|
|
|
|
if (of_property_read_u32(node, "atmel,adc-vref", &prop))
|
|
return dev_err_probe(&idev->dev, -EINVAL,
|
|
"Missing adc-vref property in the DT.\n");
|
|
st->vref_mv = prop;
|
|
|
|
st->res = st->caps->high_res_bits;
|
|
if (st->caps->low_res_bits &&
|
|
!of_property_read_string(node, "atmel,adc-use-res", (const char **)&s)
|
|
&& !strcmp(s, "lowres"))
|
|
st->res = st->caps->low_res_bits;
|
|
|
|
dev_info(&idev->dev, "Resolution used: %u bits\n", st->res);
|
|
|
|
st->registers = &st->caps->registers;
|
|
st->num_channels = st->caps->num_channels;
|
|
|
|
/* Check if touchscreen is supported. */
|
|
if (st->caps->has_ts) {
|
|
ret = at91_adc_probe_dt_ts(node, st, &idev->dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, idev);
|
|
|
|
idev->name = dev_name(&pdev->dev);
|
|
idev->modes = INDIO_DIRECT_MODE;
|
|
idev->info = &at91_adc_info;
|
|
|
|
st->irq = platform_get_irq(pdev, 0);
|
|
if (st->irq < 0)
|
|
return -ENODEV;
|
|
|
|
st->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
|
if (IS_ERR(st->reg_base))
|
|
return PTR_ERR(st->reg_base);
|
|
|
|
/*
|
|
* Disable all IRQs before setting up the handler
|
|
*/
|
|
at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
|
|
at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
|
|
|
|
if (st->caps->has_tsmr)
|
|
ret = devm_request_irq(&pdev->dev, st->irq,
|
|
at91_adc_9x5_interrupt, 0,
|
|
pdev->dev.driver->name, idev);
|
|
else
|
|
ret = devm_request_irq(&pdev->dev, st->irq,
|
|
at91_adc_rl_interrupt, 0,
|
|
pdev->dev.driver->name, idev);
|
|
if (ret)
|
|
return dev_err_probe(&pdev->dev, ret,
|
|
"Failed to allocate IRQ.\n");
|
|
|
|
st->clk = devm_clk_get_enabled(&pdev->dev, "adc_clk");
|
|
if (IS_ERR(st->clk))
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(st->clk),
|
|
"Could not prepare or enable the clock.\n");
|
|
|
|
st->adc_clk = devm_clk_get_enabled(&pdev->dev, "adc_op_clk");
|
|
if (IS_ERR(st->adc_clk))
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(st->adc_clk),
|
|
"Could not prepare or enable the ADC clock.\n");
|
|
|
|
/*
|
|
* Prescaler rate computation using the formula from the Atmel's
|
|
* datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
|
|
* specified by the electrical characteristics of the board.
|
|
*/
|
|
mstrclk = clk_get_rate(st->clk);
|
|
adc_clk = clk_get_rate(st->adc_clk);
|
|
adc_clk_khz = adc_clk / 1000;
|
|
|
|
dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
|
|
mstrclk, adc_clk);
|
|
|
|
prsc = (mstrclk / (2 * adc_clk)) - 1;
|
|
|
|
if (!st->startup_time)
|
|
return dev_err_probe(&pdev->dev, -EINVAL,
|
|
"No startup time available.\n");
|
|
ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz);
|
|
|
|
/*
|
|
* a minimal Sample and Hold Time is necessary for the ADC to guarantee
|
|
* the best converted final value between two channels selection
|
|
* The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock
|
|
*/
|
|
if (st->sample_hold_time > 0)
|
|
shtim = round_up((st->sample_hold_time * adc_clk_khz / 1000)
|
|
- 1, 1);
|
|
else
|
|
shtim = 0;
|
|
|
|
reg = AT91_ADC_PRESCAL_(prsc) & st->registers->mr_prescal_mask;
|
|
reg |= AT91_ADC_STARTUP_(ticks) & st->registers->mr_startup_mask;
|
|
if (st->res == st->caps->low_res_bits)
|
|
reg |= AT91_ADC_LOWRES;
|
|
if (st->sleep_mode)
|
|
reg |= AT91_ADC_SLEEP;
|
|
reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM;
|
|
at91_adc_writel(st, AT91_ADC_MR, reg);
|
|
|
|
/* Setup the ADC channels available on the board */
|
|
ret = at91_adc_channel_init(idev);
|
|
if (ret < 0)
|
|
return dev_err_probe(&pdev->dev, ret,
|
|
"Couldn't initialize the channels.\n");
|
|
|
|
init_waitqueue_head(&st->wq_data_avail);
|
|
mutex_init(&st->lock);
|
|
|
|
/*
|
|
* Since touch screen will set trigger register as period trigger. So
|
|
* when touch screen is enabled, then we have to disable hardware
|
|
* trigger for classic adc.
|
|
*/
|
|
if (!st->touchscreen_type) {
|
|
ret = at91_adc_buffer_init(idev);
|
|
if (ret < 0)
|
|
return dev_err_probe(&pdev->dev, ret,
|
|
"Couldn't initialize the buffer.\n");
|
|
|
|
ret = at91_adc_trigger_init(idev);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
|
|
at91_adc_buffer_remove(idev);
|
|
return ret;
|
|
}
|
|
} else {
|
|
ret = at91_ts_register(idev, pdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
at91_ts_hw_init(idev, adc_clk_khz);
|
|
}
|
|
|
|
ret = iio_device_register(idev);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
|
goto error_iio_device_register;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error_iio_device_register:
|
|
if (!st->touchscreen_type) {
|
|
at91_adc_trigger_remove(idev);
|
|
at91_adc_buffer_remove(idev);
|
|
} else {
|
|
at91_ts_unregister(st);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void at91_adc_remove(struct platform_device *pdev)
|
|
{
|
|
struct iio_dev *idev = platform_get_drvdata(pdev);
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
|
|
iio_device_unregister(idev);
|
|
if (!st->touchscreen_type) {
|
|
at91_adc_trigger_remove(idev);
|
|
at91_adc_buffer_remove(idev);
|
|
} else {
|
|
at91_ts_unregister(st);
|
|
}
|
|
}
|
|
|
|
static int at91_adc_suspend(struct device *dev)
|
|
{
|
|
struct iio_dev *idev = dev_get_drvdata(dev);
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
|
|
pinctrl_pm_select_sleep_state(dev);
|
|
clk_disable_unprepare(st->clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at91_adc_resume(struct device *dev)
|
|
{
|
|
struct iio_dev *idev = dev_get_drvdata(dev);
|
|
struct at91_adc_state *st = iio_priv(idev);
|
|
|
|
clk_prepare_enable(st->clk);
|
|
pinctrl_pm_select_default_state(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend,
|
|
at91_adc_resume);
|
|
|
|
static const struct at91_adc_trigger at91sam9260_triggers[] = {
|
|
{ .name = "timer-counter-0", .value = 0x1 },
|
|
{ .name = "timer-counter-1", .value = 0x3 },
|
|
{ .name = "timer-counter-2", .value = 0x5 },
|
|
{ .name = "external", .value = 0xd, .is_external = true },
|
|
};
|
|
|
|
static struct at91_adc_caps at91sam9260_caps = {
|
|
.calc_startup_ticks = calc_startup_ticks_9260,
|
|
.num_channels = 4,
|
|
.low_res_bits = 8,
|
|
.high_res_bits = 10,
|
|
.registers = {
|
|
.channel_base = AT91_ADC_CHR(0),
|
|
.drdy_mask = AT91_ADC_DRDY,
|
|
.status_register = AT91_ADC_SR,
|
|
.trigger_register = AT91_ADC_TRGR_9260,
|
|
.mr_prescal_mask = AT91_ADC_PRESCAL_9260,
|
|
.mr_startup_mask = AT91_ADC_STARTUP_9260,
|
|
},
|
|
.triggers = at91sam9260_triggers,
|
|
.trigger_number = ARRAY_SIZE(at91sam9260_triggers),
|
|
};
|
|
|
|
static const struct at91_adc_trigger at91sam9x5_triggers[] = {
|
|
{ .name = "external-rising", .value = 0x1, .is_external = true },
|
|
{ .name = "external-falling", .value = 0x2, .is_external = true },
|
|
{ .name = "external-any", .value = 0x3, .is_external = true },
|
|
{ .name = "continuous", .value = 0x6 },
|
|
};
|
|
|
|
static struct at91_adc_caps at91sam9rl_caps = {
|
|
.has_ts = true,
|
|
.calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
|
|
.num_channels = 6,
|
|
.low_res_bits = 8,
|
|
.high_res_bits = 10,
|
|
.registers = {
|
|
.channel_base = AT91_ADC_CHR(0),
|
|
.drdy_mask = AT91_ADC_DRDY,
|
|
.status_register = AT91_ADC_SR,
|
|
.trigger_register = AT91_ADC_TRGR_9G45,
|
|
.mr_prescal_mask = AT91_ADC_PRESCAL_9260,
|
|
.mr_startup_mask = AT91_ADC_STARTUP_9G45,
|
|
},
|
|
.triggers = at91sam9x5_triggers,
|
|
.trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
|
|
};
|
|
|
|
static struct at91_adc_caps at91sam9g45_caps = {
|
|
.has_ts = true,
|
|
.calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
|
|
.num_channels = 8,
|
|
.low_res_bits = 8,
|
|
.high_res_bits = 10,
|
|
.registers = {
|
|
.channel_base = AT91_ADC_CHR(0),
|
|
.drdy_mask = AT91_ADC_DRDY,
|
|
.status_register = AT91_ADC_SR,
|
|
.trigger_register = AT91_ADC_TRGR_9G45,
|
|
.mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
|
|
.mr_startup_mask = AT91_ADC_STARTUP_9G45,
|
|
},
|
|
.triggers = at91sam9x5_triggers,
|
|
.trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
|
|
};
|
|
|
|
static struct at91_adc_caps at91sam9x5_caps = {
|
|
.has_ts = true,
|
|
.has_tsmr = true,
|
|
.ts_filter_average = 3,
|
|
.ts_pen_detect_sensitivity = 2,
|
|
.calc_startup_ticks = calc_startup_ticks_9x5,
|
|
.num_channels = 12,
|
|
.low_res_bits = 8,
|
|
.high_res_bits = 10,
|
|
.registers = {
|
|
.channel_base = AT91_ADC_CDR0_9X5,
|
|
.drdy_mask = AT91_ADC_SR_DRDY_9X5,
|
|
.status_register = AT91_ADC_SR_9X5,
|
|
.trigger_register = AT91_ADC_TRGR_9X5,
|
|
/* prescal mask is same as 9G45 */
|
|
.mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
|
|
.mr_startup_mask = AT91_ADC_STARTUP_9X5,
|
|
},
|
|
.triggers = at91sam9x5_triggers,
|
|
.trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
|
|
};
|
|
|
|
static struct at91_adc_caps sama5d3_caps = {
|
|
.has_ts = true,
|
|
.has_tsmr = true,
|
|
.ts_filter_average = 3,
|
|
.ts_pen_detect_sensitivity = 2,
|
|
.calc_startup_ticks = calc_startup_ticks_9x5,
|
|
.num_channels = 12,
|
|
.low_res_bits = 0,
|
|
.high_res_bits = 12,
|
|
.registers = {
|
|
.channel_base = AT91_ADC_CDR0_9X5,
|
|
.drdy_mask = AT91_ADC_SR_DRDY_9X5,
|
|
.status_register = AT91_ADC_SR_9X5,
|
|
.trigger_register = AT91_ADC_TRGR_9X5,
|
|
.mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
|
|
.mr_startup_mask = AT91_ADC_STARTUP_9X5,
|
|
},
|
|
.triggers = at91sam9x5_triggers,
|
|
.trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
|
|
};
|
|
|
|
static const struct of_device_id at91_adc_dt_ids[] = {
|
|
{ .compatible = "atmel,at91sam9260-adc", .data = &at91sam9260_caps },
|
|
{ .compatible = "atmel,at91sam9rl-adc", .data = &at91sam9rl_caps },
|
|
{ .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps },
|
|
{ .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps },
|
|
{ .compatible = "atmel,sama5d3-adc", .data = &sama5d3_caps },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);
|
|
|
|
static struct platform_driver at91_adc_driver = {
|
|
.probe = at91_adc_probe,
|
|
.remove_new = at91_adc_remove,
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.of_match_table = at91_adc_dt_ids,
|
|
.pm = pm_sleep_ptr(&at91_adc_pm_ops),
|
|
},
|
|
};
|
|
|
|
module_platform_driver(at91_adc_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
|
|
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
|