> SiRFSoC internal ADC provides touchscreen single or dual touch > channels, and provides several auxiliary channels to measure > temperature, battery and so on. comments inline > 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. there is different information which part this driver is for: sometimes SiRFSoC, sometimes SiRFprimaII/AtlasVI > + > + 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 defines should be (consistently) prefixed with SIRFSOC_ADC_ (or maybe SIRFSOC_) > + > +#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 AUX2/3 are supported on some chip variants only? it would be nice if the driver exposes all ADC of a particular variant > +#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) BIT()? > +#define ADC_DEL_SET_MASK (0xF << 4) > +#define ADC_DEL_SET(x) (((x) & 0xF) << 4) > +#define ADC_TP_TIME_MASK (0x7) () not needed > +#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) many of these #defines are not used > + > +#define ADC_IDEAL_RELA_RESULT 11597 > +#define ADC_IDEAL_ABSO_RESULT 9446 these are not used > + > +#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, \ .address is not used it would make sense to put the sirfsoc_adc_iio_channel there and not assume that IIO channel index corresponds to sirfsoc_adc_iio_channel order > + .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; bool datavalid? is only set, not read > +}; > + > +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; below EBUSY was used for a similar loop; maybe EIO is the right error for a timeout? > + 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 .. */ > +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) > +{ > + u32 i, digital_offset = 0, count = 0, sum = 0; > + /* To set the reigsters in order to get the ADC offset */ registers > + 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 */ the comment is not clear, what are the constants below? > + 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 */ registers > + 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)) () not needed > + 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; > + 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; > + } > + > + 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, sirfsoc_adc_read_raw(), consistent function prefixing > + 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: very similar code for AUX1, AUX4 .. AUX6, use a lookup table? > + 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; > + } > + > + 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",}, add some whitespace, e.g. { .compatible = "sirf,prima2-adc", }, > + {} > +}; > + > +static struct iio_chan_spec const sirfsoc_adc_iio_channels[] = { ususally const is after static, so static const struct > + SIRFSOC_ADC_CHANNEL(CHANNEL_COORD), > + SIRFSOC_ADC_CHANNEL(CHANNEL_COORD_DUAL), > + SIRFSOC_ADC_CHANNEL(CHANNEL_AUX1), > + /* AtlasVI has no AUX2 and AUX3 */ but primaII has? > + 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; no need to init ret > + > + 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"); Unable > + 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); > + no newline here > + 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: clk_disable_unprepare()? > + 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@xxxxxxx>"); > +MODULE_DESCRIPTION("SiRF SoC On-chip ADC driver"); > +MODULE_LICENSE("GPL v2"); > -- Peter Meerwald +43-664-2444418 (mobile)