From: Josselin Costanzi <josselin.costanzi@mobile-devices.fr>
To: linux-iio@vger.kernel.org
Subject: Re: [PATCH] iio: adc: add CSR SiRFSoC internal ADC driver
Date: Wed, 11 Jun 2014 10:46:14 +0200 [thread overview]
Message-ID: <ln950m$3v1$1@ger.gmane.org> (raw)
In-Reply-To: 1402307665-3007-1-git-send-email-21cnbao@gmail.com
Barry Song wrote:
> From: Guoying Zhang <Guoying.Zhang@csr.com>
>
> SiRFSoC internal ADC provides touchscreen single or dual touch
> channels, and provides several auxiliary channels to measure
> temperature, battery and so on.
>
> Signed-off-by: Guoying Zhang <Guoying.Zhang@csr.com>
> Signed-off-by: Barry Song <Baohua.Song@csr.com>
> ---
> drivers/iio/adc/Kconfig | 9 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/sirfsoc_adc.c | 722 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 732 insertions(+)
> create mode 100644 drivers/iio/adc/sirfsoc_adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index a80d236..8ea6e4b 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -187,6 +187,15 @@ config NAU7802
> To compile this driver as a module, choose M here: the
> module will be called nau7802.
>
> +config SIRFSOC_ADC
> + tristate "SiRFSoC ADC"
> + depends on ARCH_SIRF
> + help
> + If you say yes here you get support for CSR SiRFSoC internal ADC.
> +
> + This driver can also be built as a module. If so, the module will be
> + called sirfsoc_adc.
> +
> config TI_ADC081C
> tristate "Texas Instruments ADC081C021/027"
> depends on I2C
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 9d60f2d..2912e58 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
> obj-$(CONFIG_MCP3422) += mcp3422.o
> obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
> obj-$(CONFIG_NAU7802) += nau7802.o
> +obj-$(CONFIG_SIRFSOC_ADC) += sirfsoc_adc.o
> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
> obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
> diff --git a/drivers/iio/adc/sirfsoc_adc.c b/drivers/iio/adc/sirfsoc_adc.c
> new file mode 100644
> index 0000000..568ae1c
> --- /dev/null
> +++ b/drivers/iio/adc/sirfsoc_adc.c
> @@ -0,0 +1,722 @@
> +/*
> +* ADC Driver for CSR SiRFprimaII/AtlasVI
> +*
> +* Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company.
> +*
> +* Licensed under GPLv2.
> +*/
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/of.h>
> +#include <linux/pm.h>
> +#include <linux/platform_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/reset.h>
> +#include <linux/rtc/sirfsoc_rtciobrg.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/machine.h>
> +#include <linux/iio/driver.h>
> +
> +#define DRIVER_NAME "sirfsoc_adc"
> +
> +#define PWR_WAKEEN_TSC_SHIFT 23
> +#define PWR_WAKEEN_TS_SHIFT 5
> +#define SIRFSOC_PWRC_TRIGGER_EN 0x8
> +#define SIRFSOC_PWRC_BASE 0x3000
> +
> +#define DATA_SHIFT_BITS 14
> +
> +#define ADC_CONTROL1 0x00
> +#define ADC_CONTROL2 0x04
> +#define ADC_INTR 0x08
> +#define ADC_COORD 0x0C
> +#define ADC_PRESSURE 0x10
> +#define ADC_AUX1 0x14
> +#define ADC_AUX2 0x18
> +#define ADC_AUX3 0x1C
> +#define ADC_AUX4 0x20
> +#define ADC_AUX5 0x24
> +#define ADC_AUX6 0x28
> +#define ADC_CB 0x2C /* Read Back calibration register */
> +#define ADC_COORD2 0x30
> +#define ADC_COORD3 0x34
> +#define ADC_COORD4 0x38
> +#define ADC_CONTROL3 0x3C
> +
> +/* CTRL1 defines */
> +#define ADC_RESET_QUANT_EN BIT(24)
> +#define ADC_RST_B BIT(23)
> +#define ADC_RESOLUTION_12 BIT(22)
> +#define ADC_RBAT_DISABLE (0x0 << 21)
> +#define ADC_RBAT_ENABLE (0x1 << 21)
> +#define ADC_EXTCM_MASK (0x3 << 19)
> +#define ADC_EXTCM(x) (((x) & 0x3) << 19)
> +#define ADC_SGAIN_MASK (0x7 << 16)
> +#define ADC_SGAIN(x) (((x) & 0x7) << 16)
> +#define ADC_POLL BIT(15)
> +#define ADC_SEL_MASK (0xF << 11)
> +#define ADC_SEL(x) (((x) & 0xF) << 11)
> +#define ADC_FREQ_6K (0x0 << 8)
> +#define ADC_FREQ_13K (0x1 << 8)
> +#define ADC_DEL_SET_MASK (0xF << 4)
> +#define ADC_DEL_SET(x) (((x) & 0xF) << 4)
> +#define ADC_TP_TIME_MASK (0x7)
> +#define ADC_TP_TIME(x) (((x) & 0x7) << 0)
> +
> +/* CTRL2 defines */
> +#define ADC_PRP_MASK (3 << 14)
> +/* Pen detector off, digitizer off */
> +#define ADC_PRP_MODE0 (0 << 14)
> +/* Pen detector on, digitizer off, digitizer wakes up on pen detect */
> +#define ADC_PRP_MODE1 BIT(14)
> +/* Pen detector on, digitizer off, no wake up on pen detect */
> +#define ADC_PRP_MODE2 (2 << 14)
> +/* Pen detector on, digitizer on */
> +#define ADC_PRP_MODE3 (3 << 14)
> +#define ADC_RTOUCH_MASK (0x3 << 12)
> +#define ADC_RTOUCH(x) (((x) & 0x3) << 12)
> +#define ADC_DEL_AUTO_MASK (0xF << 8)
> +#define ADC_DEL_AUTO(x) (((x) & 0xF) << 8)
> +#define ADC_DEL_PRE(x) (((x) & 0xF) << 4)
> +#define ADC_DEL_DIS(x) (((x) & 0xF) << 0)
> +
> +/* INTR register defines */
> +#define PEN_INTR_EN BIT(5)
> +#define DATA_INTR_EN BIT(4)
> +#define PEN_INTR BIT(1)
> +#define DATA_INTR BIT(0)
> +
> +/* DATA register defines */
> +#define PEN_DOWN BIT(31)
> +#define DATA_YVALID BIT(30)
> +#define DATA_XVALID BIT(29)
> +#define DATA_Z2VALID BIT(30)
> +#define DATA_Z1VALID BIT(29)
> +#define DATA_AUXVALID BIT(30)
> +#define DATA_CB_VALID BIT(30)
> +#define DATA_Y2VALID BIT(30)
> +#define DATA_X2VALID BIT(29)
> +#define DATA_6VALID BIT(30)
> +#define DATA_5VALID BIT(29)
> +#define DATA_8VALID BIT(30)
> +#define DATA_7VALID BIT(29)
> +
> +#define ADC_DATA_MASK(x) (0x3FFF << (x))
> +#define DATA_XMASK ADC_DATA_MASK(0)
> +#define DATA_YMASK ADC_DATA_MASK(DATA_SHIFT_BITS)
> +#define DATA_Z1MASK ADC_DATA_MASK(0)
> +#define DATA_Z2MASK ADC_DATA_MASK(DATA_SHIFT_BITS)
> +#define DATA_AUXMASK ADC_DATA_MASK(0)
> +#define DATA_CBMASK ADC_DATA_MASK(0)
> +#define DATA_X2MASK ADC_DATA_MASK(0)
> +#define DATA_Y2MASK ADC_DATA_MASK(DATA_SHIFT_BITS)
> +#define DATA_5MASK ADC_DATA_MASK(0)
> +#define DATA_6MASK ADC_DATA_MASK(DATA_SHIFT_BITS)
> +#define DATA_7MASK ADC_DATA_MASK(0)
> +#define DATA_8MASK ADC_DATA_MASK(DATA_SHIFT_BITS)
> +
> +#define ADC_IDEAL_RELA_RESULT 11597
> +#define ADC_IDEAL_ABSO_RESULT 9446
> +
> +#define ADC_MORE_CTL1 (of_machine_is_compatible("sirf,atlas6") ?\
> + (ADC_RESET_QUANT_EN | ADC_RST_B) : (0))
> +
> +/* Select AD samples to read (SEL bits in ADC_CONTROL1 register) */
> +#define SIRFSOC_ADC_AUX1_SEL 0x04
> +#define SIRFSOC_ADC_AUX4_SEL 0x07
> +#define SIRFSOC_ADC_AUX5_SEL 0x08
> +#define SIRFSOC_ADC_AUX6_SEL 0x09
> +#define SIRFSOC_ADC_TS_SEL 0x0A /* xy sample */
> +#define SIRFSOC_ADC_TS_SEL_DUAL 0x0F /* samples for dual touch */
> +#define SIRFSOC_ADC_TS_SAMPLE_SIZE 4
> +#define SIRFSOC_ADC_CTL1(sel) (ADC_POLL | ADC_SEL(sel) | ADC_DEL_SET(6) \
> + | ADC_FREQ_6K | ADC_TP_TIME(0) | ADC_SGAIN(0) \
> + | ADC_EXTCM(0) | ADC_RBAT_DISABLE | ADC_MORE_CTL1)
> +
> +#define SIRFSOC_ADC_CHANNEL(_index) { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .channel = _index, \
> + .address = _index, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> +}
> +
> +/* high priority queue with the bigger value */
> +enum sirfsoc_adc_service_t {
> + SIRFSOC_ADC_SERVICE_AUX = 0,
> + SIRFSOC_ADC_SERVICE_TS_PARADOX,
> + SIRFSOC_ADC_SERVICE_MAX,
> +};
> +
> +enum sirfsoc_adc_req_status_t {
> + SIRFSOC_ADC_REQ_NONE = 0,
> + SIRFSOC_ADC_REQ_ACTIVE,
> + SIRFSOC_ADC_REQ_BUSY,
> + SIRFSOC_ADC_REQ_MAX,
> +};
> +
> +enum sirfsoc_adc_iio_channel {
> + CHANNEL_COORD,
> + CHANNEL_COORD_DUAL,
> + CHANNEL_AUX1,
> + CHANNEL_AUX4,
> + CHANNEL_AUX5,
> + CHANNEL_AUX6,
> +};
> +
> +struct sirfsoc_adc_data {
> + u16 x;
> + u16 y;
> + u16 z1;
> + u16 z2;
> + u16 aux;
> + u8 datavalid;
> +};
> +
> +struct sirfsoc_adc_request {
> + enum sirfsoc_adc_service_t type;
> + struct sirfsoc_adc_data adc_data;
> + u16 mode;
> + u16 aux;
> + u16 reference;
> + u8 delay_bits;
> + u32 s_gain_bits;
> + enum sirfsoc_adc_req_status_t req_status;
> +};
> +
> +struct sirfsoc_adc {
> + struct clk *clk;
> + void __iomem *base;
> + struct sirfsoc_adc_request req;
> + struct completion done;
> + struct mutex adc_lock;
> +};
> +
> +static int sirfsoc_adc_send_request(struct sirfsoc_adc_request *req)
> +{
> + struct sirfsoc_adc *adc = container_of(req, struct sirfsoc_adc, req);
> + int control1, control2, intr;
> + int data, reg_offset;
> + int ret = 0;
> +
> + mutex_lock(&adc->adc_lock);
> +
> + intr = readl(adc->base + ADC_INTR);
> + control1 = readl(adc->base + ADC_CONTROL1);
> + control2 = readl(adc->base + ADC_CONTROL2);
> + writel(intr | DATA_INTR_EN | DATA_INTR, (adc->base + ADC_INTR));
> +
> + writel(ADC_PRP_MODE3 | req->reference,
> + adc->base + ADC_CONTROL2);
> +
> + writel(ADC_POLL | ADC_MORE_CTL1 | req->mode |
> + req->aux | req->delay_bits | ADC_RESOLUTION_12,
> + adc->base + ADC_CONTROL1);
> +
> + if (!wait_for_completion_timeout(&adc->done,
> + msecs_to_jiffies(50))) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + switch (req->mode) {
> + case ADC_SEL(3):
> + data = readl(adc->base + ADC_PRESSURE);
> + if ((data & DATA_Z1VALID) && (data & DATA_Z2VALID)) {
> + req->adc_data.z1 = data & DATA_Z1MASK;
> + req->adc_data.z2 =
> + (data & DATA_Z2MASK) >> DATA_SHIFT_BITS;
> + req->adc_data.datavalid = 1;
> + }
> + break;
> + case ADC_SEL(4):
> + case ADC_SEL(5):
> + case ADC_SEL(6):
> + case ADC_SEL(7):
> + case ADC_SEL(8):
> + case ADC_SEL(9):
> + reg_offset = 0x14 + ((req->mode >> 11) - 0x04) * 4;
> + data = readl(adc->base + reg_offset);
> + if ((data & DATA_AUXVALID)) {
> + req->adc_data.aux = data & DATA_AUXMASK;
> + req->adc_data.datavalid = 1;
> + }
> + break;
> + case ADC_SEL(11):
> + case ADC_SEL(12):
> + case ADC_SEL(13):
> + reg_offset = ADC_CB;
> + data = readl(adc->base + reg_offset);
> + if ((data & DATA_AUXVALID)) {
> + req->adc_data.aux = data & DATA_AUXMASK;
> + req->adc_data.datavalid = 1;
> + }
> + break;
> + default:
> + break;
> + }
> +
> +out:
> + writel(intr, adc->base + ADC_INTR);
> + writel(control1, adc->base + ADC_CONTROL1);
> + writel(control2, adc->base + ADC_CONTROL2);
> + mutex_unlock(&adc->adc_lock);
> + return ret;
> +}
> +
> +/*struct store params to calibrate*/
> +struct sirfsoc_adc_cali_data {
> + u32 digital_offset;
> + u32 digital_again;
> + u32 digital_ideal;
> + bool is_calibration;
> +};
> +
> +static u32 sirfsoc_adc_offset_cali(struct sirfsoc_adc_request *req)
> +{
This function accepts a pointer to any sirfsoc_adc_request. This request
is then passed to sirfsoc_adc_send_request() which does a
container_of(req, struct sirfsoc_adc, req). So in reality you can't pass
"any" request pointer to this function but only one contained in a
struct sirfsoc_adc. Since it's all internal to this driver, I think you
should change sirfsoc_adc_send_request, sirfsoc_adc_*_cali to use a
struct sirfsoc_adc * as a parameter.
> + u32 i, digital_offset = 0, count = 0, sum = 0;
> + /* To set the reigsters in order to get the ADC offset */
> + req->mode = ADC_SEL(11);
> + req->aux = ADC_CB;
> + req->s_gain_bits = ADC_SGAIN(7);
> + req->delay_bits = ADC_DEL_SET(4);
> + req->req_status = SIRFSOC_ADC_REQ_NONE;
> +
> + for (i = 0; i < 10; i++) {
> + if (unlikely(sirfsoc_adc_send_request(req)))
> + break;
> + digital_offset = req->adc_data.aux;
> + /* Maybe the value is wrong, so remove it use experience */
> + if (digital_offset < 230 && digital_offset > 130) {
> + sum += digital_offset;
> + count++;
> + }
> + }
> + if (!sum || !count)
> + digital_offset = 170;
> + else
> + digital_offset = sum / count;
> +
> + return digital_offset;
> +}
> +
> +
> +/* Gain Calibration calibrates the ADC gain error */
> +static u32 sirfsoc_adc_gain_cali(struct sirfsoc_adc_request *req)
> +{
> + u32 i, digital_gain = 0, count = 0, sum = 0;
> + /* To set the reigsters in order to get the ADC gain */
> + req->mode = ADC_SEL(12);
> + req->aux = ADC_CB;
> + req->s_gain_bits = ADC_SGAIN(0);
> + req->delay_bits = ADC_DEL_SET(4);
> + req->req_status = SIRFSOC_ADC_REQ_NONE;
> +
> + for (i = 0; i < 10; i++) {
> + if (unlikely(sirfsoc_adc_send_request(req)))
> + break;
> + digital_gain = req->adc_data.aux;
> + /* Maybe the value is wrong, so remove it use experience */
> + if (digital_gain < 6500 && digital_gain > 5500) {
> + sum += digital_gain;
> + count++;
> + }
> + }
> + if (!sum || !count)
> + digital_gain = 5555;
> + else
> + digital_gain = sum / count;
> +
> + return digital_gain;
> +}
> +
> +/* absolute gain calibration */
> +static int sirfsoc_adc_adc_cali(struct sirfsoc_adc_request *req,
> + struct sirfsoc_adc_cali_data *cali_data)
> +{
> + cali_data->digital_offset = sirfsoc_adc_offset_cali(req);
> + if (!(cali_data->digital_offset))
> + return -EINVAL;
> + cali_data->digital_again = sirfsoc_adc_gain_cali(req);
> + if (!(cali_data->digital_again))
> + return -EINVAL;
> + /*
> + * see Equation 3.2 of SiRFprimaII? Internal ADC and Touch
> + * User Guide
> + */
> + cali_data->digital_ideal = (16384 * 1200) / (14 * 333);
> + return 0;
> +}
> +
> +
> +/* get voltage after ADC conversion */
> +static u32 sirfsoc_adc_get_adc_volt(struct sirfsoc_adc *adc,
> + struct sirfsoc_adc_cali_data *cali_data)
> +{
> + u32 digital_out, digital_convert, volt;
> + struct sirfsoc_adc_request *req = &adc->req;
> + if (!(cali_data->is_calibration)) {
> + if (sirfsoc_adc_adc_cali(req, cali_data))
> + return 0;
You should return an error -ENODATA/-EIO?
> + cali_data->is_calibration = true;
> + }
> + req->s_gain_bits = ADC_SGAIN(0);
> + req->delay_bits = ADC_DEL_SET(4);
> +
> + sirfsoc_adc_send_request(req);
> + if (req->adc_data.aux) {
> + digital_out = req->adc_data.aux;
> + /*
> + * see Equation 3.3 of SiRFprimaII? Internal ADC and Touch
> + */
> + digital_convert = ((digital_out - 2 * cali_data->digital_offset
> + * 11645 / 140000) * cali_data->digital_ideal)
> + / (cali_data->digital_again -
> + 2 * cali_data->digital_offset
> + * 11645 / 140000);
> + volt = (1200 * digital_convert) / cali_data->digital_ideal;
> + volt = volt * 2;
> +
> + } else {
> + return 0;
Same, return an error
> + }
> +
> + return volt;
> +}
> +
> +/* get touchscreen coordinates for single touch */
> +static int sirfsoc_adc_single_ts_sample(struct sirfsoc_adc *adc, int *sample)
> +{
> + int adc_intr;
> +
> + adc_intr = readl(adc->base + ADC_INTR);
> + if (adc_intr & PEN_INTR)
> + writel(PEN_INTR | PEN_INTR_EN | DATA_INTR_EN,
> + adc->base + ADC_INTR);
> +
> + /* check pen status */
> + if (!(readl(adc->base + ADC_COORD) & PEN_DOWN))
> + return -EINVAL;
> +
> + writel(SIRFSOC_ADC_CTL1(SIRFSOC_ADC_TS_SEL),
> + adc->base + ADC_CONTROL1);
> +
> + if (!wait_for_completion_timeout(&adc->done,
> + msecs_to_jiffies(50))) {
> + return -EBUSY;
> + }
> +
> + *sample = readl(adc->base + ADC_COORD);
> + return 0;
> +}
> +
> +static const u32 sirfsoc_adc_ts_reg[SIRFSOC_ADC_TS_SAMPLE_SIZE] = {
> + ADC_COORD, ADC_COORD2, ADC_COORD3, ADC_COORD4
> +};
> +
> +/* get touchscreen coordinates for dual touch */
> +static int sirfsoc_adc_dual_ts_sample(struct sirfsoc_adc *adc, int *samples)
> +{
> + int adc_intr;
> + int i;
> +
> + adc_intr = readl(adc->base + ADC_INTR);
> + if (adc_intr & PEN_INTR)
> + writel(PEN_INTR | PEN_INTR_EN | DATA_INTR_EN,
> + adc->base + ADC_INTR);
> +
> + /* check pen status */
> + if (!(readl(adc->base + ADC_COORD) & PEN_DOWN))
> + return -EINVAL;
> +
> + writel(SIRFSOC_ADC_CTL1(SIRFSOC_ADC_TS_SEL_DUAL),
> + adc->base + ADC_CONTROL1);
> +
> + if (!wait_for_completion_timeout(&adc->done,
> + msecs_to_jiffies(50))) {
> + return -EBUSY;
> + }
> +
> + for (i = 0; i < SIRFSOC_ADC_TS_SAMPLE_SIZE; i++)
> + samples[i] = readl(adc->base + sirfsoc_adc_ts_reg[i]);
> +
> + return 0;
> +}
> +
> +static int sirfsoc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val,
> + int *val2,
> + long mask)
> +{
> + struct sirfsoc_adc *adc = iio_priv(indio_dev);
> + struct sirfsoc_adc_cali_data cali_data;
> + int ret = 0;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + switch (chan->channel) {
> + case CHANNEL_COORD:
> + ret = sirfsoc_adc_single_ts_sample(adc, val);
> + break;
> + case CHANNEL_COORD_DUAL:
> + ret = sirfsoc_adc_dual_ts_sample(adc, val);
> + break;
> + case CHANNEL_AUX1:
> + adc->req.mode = ADC_SEL(SIRFSOC_ADC_AUX1_SEL);
> + adc->req.aux = ADC_AUX1;
> + *val = sirfsoc_adc_get_adc_volt(adc,
> + &cali_data);
> + break;
> + case CHANNEL_AUX4:
> + adc->req.mode = ADC_SEL(SIRFSOC_ADC_AUX4_SEL);
> + adc->req.aux = ADC_AUX4;
> + *val = sirfsoc_adc_get_adc_volt(adc,
> + &cali_data);
> + break;
> + case CHANNEL_AUX5:
> + adc->req.mode = ADC_SEL(SIRFSOC_ADC_AUX5_SEL);
> + adc->req.aux = ADC_AUX5;
> + *val = sirfsoc_adc_get_adc_volt(adc,
> + &cali_data);
> + break;
> + case CHANNEL_AUX6:
> + adc->req.mode = ADC_SEL(SIRFSOC_ADC_AUX6_SEL);
> + adc->req.aux = ADC_AUX6;
> + *val = sirfsoc_adc_get_adc_volt(adc,
> + &cali_data);
> + break;
I'm not an expert on IIO but shouldn't the conversion from raw be "on
demand"? Here you seem to return the voltage already processed when we
expect the raw value plus a IIO_CHAN_INFO_SCALE attribute
> + }
> +
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static irqreturn_t sirfsoc_adc_data_irq(int irq, void *handle)
> +{
> + struct iio_dev *indio_dev = handle;
> + struct sirfsoc_adc *adc = iio_priv(indio_dev);
> + int val;
> +
> + val = readl(adc->base + ADC_INTR);
> +
> + if (val & DATA_INTR) {
> + writel(PEN_INTR_EN | DATA_INTR | DATA_INTR_EN,
> + adc->base + ADC_INTR);
> + complete(&adc->done);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int sirfsoc_adc_suspend(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct sirfsoc_adc *adc = iio_priv(indio_dev);
> +
> + sirfsoc_rtc_iobrg_writel(sirfsoc_rtc_iobrg_readl(
> + SIRFSOC_PWRC_BASE + SIRFSOC_PWRC_TRIGGER_EN)
> + & ~(1 << PWR_WAKEEN_TSC_SHIFT),
> + SIRFSOC_PWRC_BASE + SIRFSOC_PWRC_TRIGGER_EN);
> +
> + clk_disable_unprepare(adc->clk);
> + return 0;
> +}
> +
> +static int sirfsoc_adc_resume(struct device *dev)
> +{
> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
> + struct sirfsoc_adc *adc = iio_priv(indio_dev);
> + int ret;
> + int val;
> +
> + clk_prepare_enable(adc->clk);
> +
> + sirfsoc_rtc_iobrg_writel(sirfsoc_rtc_iobrg_readl(
> + SIRFSOC_PWRC_BASE + SIRFSOC_PWRC_TRIGGER_EN)
> + | (1 << PWR_WAKEEN_TS_SHIFT),
> + SIRFSOC_PWRC_BASE + SIRFSOC_PWRC_TRIGGER_EN);
> +
> + ret = device_reset(dev);
> + if (ret) {
> + dev_err(dev, "Failed to reset\n");
> + return ret;
> + }
> +
> + writel(ADC_PRP_MODE3 | ADC_RTOUCH(1) | ADC_DEL_PRE(2) |
> + ADC_DEL_DIS(5), adc->base + ADC_CONTROL2);
> +
> + val = readl(adc->base + ADC_INTR);
> +
> + /* Clear interrupts and enable PEN interrupt */
> + writel(val | PEN_INTR | DATA_INTR | PEN_INTR_EN |
> + DATA_INTR_EN, adc->base + ADC_INTR);
> +
> + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops sirfsoc_adc_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_adc_suspend, sirfsoc_adc_resume)
> +};
> +
> +static const struct of_device_id sirfsoc_adc_of_match[] = {
> + { .compatible = "sirf,prima2-adc",},
> + {}
> +};
> +
> +static struct iio_chan_spec const sirfsoc_adc_iio_channels[] = {
> + SIRFSOC_ADC_CHANNEL(CHANNEL_COORD),
> + SIRFSOC_ADC_CHANNEL(CHANNEL_COORD_DUAL),
> + SIRFSOC_ADC_CHANNEL(CHANNEL_AUX1),
> + /* AtlasVI has no AUX2 and AUX3 */
> + SIRFSOC_ADC_CHANNEL(CHANNEL_AUX4),
> + SIRFSOC_ADC_CHANNEL(CHANNEL_AUX5),
> + SIRFSOC_ADC_CHANNEL(CHANNEL_AUX6),
> +};
> +
> +static const struct iio_info sirfsoc_adc_info = {
> + .read_raw = &sirfsoc_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int sirfsoc_adc_probe(struct platform_device *pdev)
> +{
> + struct resource *mem_res;
> + struct sirfsoc_adc *adc;
> + struct iio_dev *indio_dev;
> + int irq;
> + int ret = 0;
> +
> + indio_dev = iio_device_alloc(sizeof(struct sirfsoc_adc));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + indio_dev->info = &sirfsoc_adc_info;
> + indio_dev->channels = sirfsoc_adc_iio_channels;
> + indio_dev->num_channels = ARRAY_SIZE(sirfsoc_adc_iio_channels);
> + indio_dev->name = "sirfsoc adc";
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + adc = iio_priv(indio_dev);
> +
> + platform_set_drvdata(pdev, indio_dev);
> +
> + adc->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(adc->clk)) {
> + dev_err(&pdev->dev, "get adc clk err\n");
> + ret = -ENOMEM;
> + goto err;
> + }
> + clk_prepare_enable(adc->clk);
> +
> + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem_res) {
> + dev_err(&pdev->dev, "Unalbe to get io resource\n");
> + ret = -ENODEV;
> + goto err;
> + }
> +
> + adc->base = devm_request_and_ioremap(&pdev->dev, mem_res);
> + if (adc->base == NULL) {
> + dev_err(&pdev->dev, "IO remap failed!\n");
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + init_completion(&adc->done);
> + mutex_init(&adc->adc_lock);
> +
> + sirfsoc_rtc_iobrg_writel(sirfsoc_rtc_iobrg_readl(SIRFSOC_PWRC_BASE +
> + SIRFSOC_PWRC_TRIGGER_EN) | (1 << PWR_WAKEEN_TS_SHIFT),
> + SIRFSOC_PWRC_BASE + SIRFSOC_PWRC_TRIGGER_EN);
> +
> + ret = device_reset(&pdev->dev);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to reset\n");
> + goto err;
> + }
> +
> + writel(ADC_PRP_MODE3 | ADC_RTOUCH(1) | ADC_DEL_PRE(2) |
> + ADC_DEL_DIS(5), adc->base + ADC_CONTROL2);
> +
> + /* Clear interrupts and enable PEN INTR */
> + writel(readl(adc->base + ADC_INTR) | PEN_INTR | DATA_INTR |
> + PEN_INTR_EN | DATA_INTR_EN, adc->base + ADC_INTR);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev, "Failed to get IRQ!\n");
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + ret = devm_request_irq(&pdev->dev, irq, sirfsoc_adc_data_irq,
> + 0, DRIVER_NAME, indio_dev);
> +
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Failed to register irq handler\n");
> + ret = -ENODEV;
> + goto err;
> + }
> +
> + ret = of_platform_populate(pdev->dev.of_node, sirfsoc_adc_of_match,
> + NULL, &pdev->dev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Failed to add child nodes\n");
> + goto err;
> + }
> +
> + ret = iio_device_register(indio_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to register adc iio dev\n");
> + goto err;
> + }
> +
> + return 0;
> +
> +err:
> + iio_device_free(indio_dev);
> + return ret;
> +}
> +
> +static int sirfsoc_adc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> + struct sirfsoc_adc *adc = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> + clk_disable_unprepare(adc->clk);
> + iio_device_free(indio_dev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver sirfsoc_adc_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = sirfsoc_adc_of_match,
> + .pm = &sirfsoc_adc_pm_ops,
> + },
> + .probe = sirfsoc_adc_probe,
> + .remove = sirfsoc_adc_remove,
> +};
> +
> +module_platform_driver(sirfsoc_adc_driver);
> +
> +MODULE_AUTHOR("Guoying Zhang <Guoying.Zhang@csr.com>");
> +MODULE_DESCRIPTION("SiRF SoC On-chip ADC driver");
> +MODULE_LICENSE("GPL v2");
next prev parent reply other threads:[~2014-06-11 8:50 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-06-09 9:54 [PATCH] iio: adc: add CSR SiRFSoC internal ADC driver Barry Song
2014-06-10 14:29 ` Peter Meerwald
2014-06-11 8:46 ` Josselin Costanzi [this message]
2014-06-14 16:58 ` Jonathan Cameron
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='ln950m$3v1$1@ger.gmane.org' \
--to=josselin.costanzi@mobile-devices.fr \
--cc=linux-iio@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).