Re: [PATCH] iio: adc: add CSR SiRFSoC internal ADC driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> 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)

[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux