xref: /openbmc/linux/drivers/iio/adc/imx7d_adc.c (revision 8cc393bf)
116846ebeSHaibo Chen /*
216846ebeSHaibo Chen  * Freescale i.MX7D ADC driver
316846ebeSHaibo Chen  *
416846ebeSHaibo Chen  * Copyright (C) 2015 Freescale Semiconductor, Inc.
516846ebeSHaibo Chen  *
616846ebeSHaibo Chen  * This program is free software; you can redistribute it and/or modify
716846ebeSHaibo Chen  * it under the terms of the GNU General Public License as published by
816846ebeSHaibo Chen  * the Free Software Foundation; either version 2 of the License, or
916846ebeSHaibo Chen  * (at your option) any later version.
1016846ebeSHaibo Chen  */
1116846ebeSHaibo Chen 
1216846ebeSHaibo Chen #include <linux/clk.h>
1316846ebeSHaibo Chen #include <linux/completion.h>
1416846ebeSHaibo Chen #include <linux/err.h>
1516846ebeSHaibo Chen #include <linux/interrupt.h>
1616846ebeSHaibo Chen #include <linux/io.h>
1716846ebeSHaibo Chen #include <linux/kernel.h>
1816846ebeSHaibo Chen #include <linux/module.h>
1916846ebeSHaibo Chen #include <linux/platform_device.h>
2016846ebeSHaibo Chen #include <linux/regulator/consumer.h>
2116846ebeSHaibo Chen 
2216846ebeSHaibo Chen #include <linux/iio/iio.h>
2316846ebeSHaibo Chen #include <linux/iio/driver.h>
2416846ebeSHaibo Chen #include <linux/iio/sysfs.h>
2516846ebeSHaibo Chen 
2616846ebeSHaibo Chen /* ADC register */
2716846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_A_CFG1			0x00
2816846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_A_CFG2			0x10
2916846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_B_CFG1			0x20
3016846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_B_CFG2			0x30
3116846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_C_CFG1			0x40
3216846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_C_CFG2			0x50
3316846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_D_CFG1			0x60
3416846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_D_CFG2			0x70
3516846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_SW_CFG			0x80
3616846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT		0x90
3716846ebeSHaibo Chen #define IMX7D_REG_ADC_DMA_FIFO			0xa0
3816846ebeSHaibo Chen #define IMX7D_REG_ADC_FIFO_STATUS		0xb0
3916846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_SIG_EN		0xc0
4016846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_EN			0xd0
4116846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_STATUS		0xe0
4216846ebeSHaibo Chen #define IMX7D_REG_ADC_CHA_B_CNV_RSLT		0xf0
4316846ebeSHaibo Chen #define IMX7D_REG_ADC_CHC_D_CNV_RSLT		0x100
4416846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_SW_CNV_RSLT		0x110
4516846ebeSHaibo Chen #define IMX7D_REG_ADC_DMA_FIFO_DAT		0x120
4616846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG			0x130
4716846ebeSHaibo Chen 
4816846ebeSHaibo Chen #define IMX7D_REG_ADC_CHANNEL_CFG2_BASE		0x10
4916846ebeSHaibo Chen #define IMX7D_EACH_CHANNEL_REG_OFFSET		0x20
5016846ebeSHaibo Chen 
5116846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN			(0x1 << 31)
5216846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE			BIT(30)
5316846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN			BIT(29)
5416846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x)			((x) << 24)
5516846ebeSHaibo Chen 
5616846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4				(0x0 << 12)
5716846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8				(0x1 << 12)
5816846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16			(0x2 << 12)
5916846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32			(0x3 << 12)
6016846ebeSHaibo Chen 
6116846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4			(0x0 << 29)
6216846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8			(0x1 << 29)
6316846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16			(0x2 << 29)
6416846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32			(0x3 << 29)
6516846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64			(0x4 << 29)
6616846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128			(0x5 << 29)
6716846ebeSHaibo Chen 
6816846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN			BIT(31)
6916846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN			BIT(1)
7016846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG_ADC_EN				BIT(0)
7116846ebeSHaibo Chen 
7216846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHA_COV_INT_EN			BIT(8)
7316846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHB_COV_INT_EN			BIT(9)
7416846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHC_COV_INT_EN			BIT(10)
7516846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHD_COV_INT_EN			BIT(11)
7616846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \
7716846ebeSHaibo Chen 	(IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \
7816846ebeSHaibo Chen 	 IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \
7916846ebeSHaibo Chen 	 IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \
8016846ebeSHaibo Chen 	 IMX7D_REG_ADC_INT_CHD_COV_INT_EN)
8116846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS		0xf00
8216846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT		0xf0000
8316846ebeSHaibo Chen 
8416846ebeSHaibo Chen #define IMX7D_ADC_TIMEOUT		msecs_to_jiffies(100)
8516846ebeSHaibo Chen 
8616846ebeSHaibo Chen enum imx7d_adc_clk_pre_div {
8716846ebeSHaibo Chen 	IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
8816846ebeSHaibo Chen 	IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
8916846ebeSHaibo Chen 	IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
9016846ebeSHaibo Chen 	IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
9116846ebeSHaibo Chen 	IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
9216846ebeSHaibo Chen 	IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
9316846ebeSHaibo Chen };
9416846ebeSHaibo Chen 
9516846ebeSHaibo Chen enum imx7d_adc_average_num {
9616846ebeSHaibo Chen 	IMX7D_ADC_AVERAGE_NUM_4,
9716846ebeSHaibo Chen 	IMX7D_ADC_AVERAGE_NUM_8,
9816846ebeSHaibo Chen 	IMX7D_ADC_AVERAGE_NUM_16,
9916846ebeSHaibo Chen 	IMX7D_ADC_AVERAGE_NUM_32,
10016846ebeSHaibo Chen };
10116846ebeSHaibo Chen 
10216846ebeSHaibo Chen struct imx7d_adc_feature {
10316846ebeSHaibo Chen 	enum imx7d_adc_clk_pre_div clk_pre_div;
10416846ebeSHaibo Chen 	enum imx7d_adc_average_num avg_num;
10516846ebeSHaibo Chen 
10616846ebeSHaibo Chen 	u32 core_time_unit;	/* impact the sample rate */
10716846ebeSHaibo Chen 
10816846ebeSHaibo Chen 	bool average_en;
10916846ebeSHaibo Chen };
11016846ebeSHaibo Chen 
11116846ebeSHaibo Chen struct imx7d_adc {
11216846ebeSHaibo Chen 	struct device *dev;
11316846ebeSHaibo Chen 	void __iomem *regs;
11416846ebeSHaibo Chen 	struct clk *clk;
11516846ebeSHaibo Chen 
11616846ebeSHaibo Chen 	u32 vref_uv;
11716846ebeSHaibo Chen 	u32 value;
11816846ebeSHaibo Chen 	u32 channel;
11916846ebeSHaibo Chen 	u32 pre_div_num;
12016846ebeSHaibo Chen 
12116846ebeSHaibo Chen 	struct regulator *vref;
12216846ebeSHaibo Chen 	struct imx7d_adc_feature adc_feature;
12316846ebeSHaibo Chen 
12416846ebeSHaibo Chen 	struct completion completion;
12516846ebeSHaibo Chen };
12616846ebeSHaibo Chen 
12716846ebeSHaibo Chen struct imx7d_adc_analogue_core_clk {
12816846ebeSHaibo Chen 	u32 pre_div;
12916846ebeSHaibo Chen 	u32 reg_config;
13016846ebeSHaibo Chen };
13116846ebeSHaibo Chen 
13216846ebeSHaibo Chen #define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) {	\
13316846ebeSHaibo Chen 	.pre_div = (_pre_div),					\
13416846ebeSHaibo Chen 	.reg_config = (_reg_conf),				\
13516846ebeSHaibo Chen }
13616846ebeSHaibo Chen 
13716846ebeSHaibo Chen static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
13816846ebeSHaibo Chen 	IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
13916846ebeSHaibo Chen 	IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
14016846ebeSHaibo Chen 	IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
14116846ebeSHaibo Chen 	IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
14216846ebeSHaibo Chen 	IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
14316846ebeSHaibo Chen 	IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
14416846ebeSHaibo Chen };
14516846ebeSHaibo Chen 
14616846ebeSHaibo Chen #define IMX7D_ADC_CHAN(_idx) {					\
14716846ebeSHaibo Chen 	.type = IIO_VOLTAGE,					\
14816846ebeSHaibo Chen 	.indexed = 1,						\
14916846ebeSHaibo Chen 	.channel = (_idx),					\
15016846ebeSHaibo Chen 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
15116846ebeSHaibo Chen 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\
15216846ebeSHaibo Chen 				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
15316846ebeSHaibo Chen }
15416846ebeSHaibo Chen 
15516846ebeSHaibo Chen static const struct iio_chan_spec imx7d_adc_iio_channels[] = {
15616846ebeSHaibo Chen 	IMX7D_ADC_CHAN(0),
15716846ebeSHaibo Chen 	IMX7D_ADC_CHAN(1),
15816846ebeSHaibo Chen 	IMX7D_ADC_CHAN(2),
15916846ebeSHaibo Chen 	IMX7D_ADC_CHAN(3),
16016846ebeSHaibo Chen 	IMX7D_ADC_CHAN(4),
16116846ebeSHaibo Chen 	IMX7D_ADC_CHAN(5),
16216846ebeSHaibo Chen 	IMX7D_ADC_CHAN(6),
16316846ebeSHaibo Chen 	IMX7D_ADC_CHAN(7),
16416846ebeSHaibo Chen 	IMX7D_ADC_CHAN(8),
16516846ebeSHaibo Chen 	IMX7D_ADC_CHAN(9),
16616846ebeSHaibo Chen 	IMX7D_ADC_CHAN(10),
16716846ebeSHaibo Chen 	IMX7D_ADC_CHAN(11),
16816846ebeSHaibo Chen 	IMX7D_ADC_CHAN(12),
16916846ebeSHaibo Chen 	IMX7D_ADC_CHAN(13),
17016846ebeSHaibo Chen 	IMX7D_ADC_CHAN(14),
17116846ebeSHaibo Chen 	IMX7D_ADC_CHAN(15),
17216846ebeSHaibo Chen };
17316846ebeSHaibo Chen 
17416846ebeSHaibo Chen static const u32 imx7d_adc_average_num[] = {
17516846ebeSHaibo Chen 	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
17616846ebeSHaibo Chen 	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
17716846ebeSHaibo Chen 	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
17816846ebeSHaibo Chen 	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
17916846ebeSHaibo Chen };
18016846ebeSHaibo Chen 
18116846ebeSHaibo Chen static void imx7d_adc_feature_config(struct imx7d_adc *info)
18216846ebeSHaibo Chen {
18316846ebeSHaibo Chen 	info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
18416846ebeSHaibo Chen 	info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
18516846ebeSHaibo Chen 	info->adc_feature.core_time_unit = 1;
18616846ebeSHaibo Chen 	info->adc_feature.average_en = true;
18716846ebeSHaibo Chen }
18816846ebeSHaibo Chen 
18916846ebeSHaibo Chen static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
19016846ebeSHaibo Chen {
19116846ebeSHaibo Chen 	struct imx7d_adc_feature *adc_feature = &info->adc_feature;
19216846ebeSHaibo Chen 	struct imx7d_adc_analogue_core_clk adc_analogure_clk;
19316846ebeSHaibo Chen 	u32 i;
19416846ebeSHaibo Chen 	u32 tmp_cfg1;
19516846ebeSHaibo Chen 	u32 sample_rate = 0;
19616846ebeSHaibo Chen 
19716846ebeSHaibo Chen 	/*
19816846ebeSHaibo Chen 	 * Before sample set, disable channel A,B,C,D. Here we
19916846ebeSHaibo Chen 	 * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
20016846ebeSHaibo Chen 	 */
20116846ebeSHaibo Chen 	for (i = 0; i < 4; i++) {
20216846ebeSHaibo Chen 		tmp_cfg1 =
20316846ebeSHaibo Chen 			readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
20416846ebeSHaibo Chen 		tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
20516846ebeSHaibo Chen 		writel(tmp_cfg1,
20616846ebeSHaibo Chen 		       info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
20716846ebeSHaibo Chen 	}
20816846ebeSHaibo Chen 
20916846ebeSHaibo Chen 	adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
21016846ebeSHaibo Chen 	sample_rate |= adc_analogure_clk.reg_config;
21116846ebeSHaibo Chen 	info->pre_div_num = adc_analogure_clk.pre_div;
21216846ebeSHaibo Chen 
21316846ebeSHaibo Chen 	sample_rate |= adc_feature->core_time_unit;
21416846ebeSHaibo Chen 	writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
21516846ebeSHaibo Chen }
21616846ebeSHaibo Chen 
21716846ebeSHaibo Chen static void imx7d_adc_hw_init(struct imx7d_adc *info)
21816846ebeSHaibo Chen {
21916846ebeSHaibo Chen 	u32 cfg;
22016846ebeSHaibo Chen 
22116846ebeSHaibo Chen 	/* power up and enable adc analogue core */
22216846ebeSHaibo Chen 	cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
22316846ebeSHaibo Chen 	cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
22416846ebeSHaibo Chen 		 IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
22516846ebeSHaibo Chen 	cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
22616846ebeSHaibo Chen 	writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
22716846ebeSHaibo Chen 
22816846ebeSHaibo Chen 	/* enable channel A,B,C,D interrupt */
22916846ebeSHaibo Chen 	writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
23016846ebeSHaibo Chen 	       info->regs + IMX7D_REG_ADC_INT_SIG_EN);
23116846ebeSHaibo Chen 	writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
23216846ebeSHaibo Chen 	       info->regs + IMX7D_REG_ADC_INT_EN);
23316846ebeSHaibo Chen 
23416846ebeSHaibo Chen 	imx7d_adc_sample_rate_set(info);
23516846ebeSHaibo Chen }
23616846ebeSHaibo Chen 
23716846ebeSHaibo Chen static void imx7d_adc_channel_set(struct imx7d_adc *info)
23816846ebeSHaibo Chen {
23916846ebeSHaibo Chen 	u32 cfg1 = 0;
24016846ebeSHaibo Chen 	u32 cfg2;
24116846ebeSHaibo Chen 	u32 channel;
24216846ebeSHaibo Chen 
24316846ebeSHaibo Chen 	channel = info->channel;
24416846ebeSHaibo Chen 
24516846ebeSHaibo Chen 	/* the channel choose single conversion, and enable average mode */
24616846ebeSHaibo Chen 	cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
24716846ebeSHaibo Chen 		 IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE);
24816846ebeSHaibo Chen 	if (info->adc_feature.average_en)
24916846ebeSHaibo Chen 		cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN;
25016846ebeSHaibo Chen 
25116846ebeSHaibo Chen 	/*
25216846ebeSHaibo Chen 	 * physical channel 0 chose logical channel A
25316846ebeSHaibo Chen 	 * physical channel 1 chose logical channel B
25416846ebeSHaibo Chen 	 * physical channel 2 chose logical channel C
25516846ebeSHaibo Chen 	 * physical channel 3 chose logical channel D
25616846ebeSHaibo Chen 	 */
25716846ebeSHaibo Chen 	cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
25816846ebeSHaibo Chen 
25916846ebeSHaibo Chen 	/*
26016846ebeSHaibo Chen 	 * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
26116846ebeSHaibo Chen 	 * channel chosen
26216846ebeSHaibo Chen 	 */
26316846ebeSHaibo Chen 	cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
26416846ebeSHaibo Chen 		     IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
26516846ebeSHaibo Chen 
26616846ebeSHaibo Chen 	cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
26716846ebeSHaibo Chen 
26816846ebeSHaibo Chen 	/*
26916846ebeSHaibo Chen 	 * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
27016846ebeSHaibo Chen 	 * the channel chosen
27116846ebeSHaibo Chen 	 */
27216846ebeSHaibo Chen 	writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
27316846ebeSHaibo Chen 	       IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
27416846ebeSHaibo Chen 	writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);
27516846ebeSHaibo Chen }
27616846ebeSHaibo Chen 
27716846ebeSHaibo Chen static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
27816846ebeSHaibo Chen {
27916846ebeSHaibo Chen 	/* input clock is always 24MHz */
28016846ebeSHaibo Chen 	u32 input_clk = 24000000;
28116846ebeSHaibo Chen 	u32 analogue_core_clk;
28216846ebeSHaibo Chen 	u32 core_time_unit = info->adc_feature.core_time_unit;
28316846ebeSHaibo Chen 	u32 tmp;
28416846ebeSHaibo Chen 
28516846ebeSHaibo Chen 	analogue_core_clk = input_clk / info->pre_div_num;
28616846ebeSHaibo Chen 	tmp = (core_time_unit + 1) * 6;
28716846ebeSHaibo Chen 
28816846ebeSHaibo Chen 	return analogue_core_clk / tmp;
28916846ebeSHaibo Chen }
29016846ebeSHaibo Chen 
29116846ebeSHaibo Chen static int imx7d_adc_read_raw(struct iio_dev *indio_dev,
29216846ebeSHaibo Chen 			struct iio_chan_spec const *chan,
29316846ebeSHaibo Chen 			int *val,
29416846ebeSHaibo Chen 			int *val2,
29516846ebeSHaibo Chen 			long mask)
29616846ebeSHaibo Chen {
29716846ebeSHaibo Chen 	struct imx7d_adc *info = iio_priv(indio_dev);
29816846ebeSHaibo Chen 
29916846ebeSHaibo Chen 	u32 channel;
30016846ebeSHaibo Chen 	long ret;
30116846ebeSHaibo Chen 
30216846ebeSHaibo Chen 	switch (mask) {
30316846ebeSHaibo Chen 	case IIO_CHAN_INFO_RAW:
30416846ebeSHaibo Chen 		mutex_lock(&indio_dev->mlock);
30516846ebeSHaibo Chen 		reinit_completion(&info->completion);
30616846ebeSHaibo Chen 
30716846ebeSHaibo Chen 		channel = chan->channel & 0x03;
30816846ebeSHaibo Chen 		info->channel = channel;
30916846ebeSHaibo Chen 		imx7d_adc_channel_set(info);
31016846ebeSHaibo Chen 
31116846ebeSHaibo Chen 		ret = wait_for_completion_interruptible_timeout
31216846ebeSHaibo Chen 				(&info->completion, IMX7D_ADC_TIMEOUT);
31316846ebeSHaibo Chen 		if (ret == 0) {
31416846ebeSHaibo Chen 			mutex_unlock(&indio_dev->mlock);
31516846ebeSHaibo Chen 			return -ETIMEDOUT;
31616846ebeSHaibo Chen 		}
31716846ebeSHaibo Chen 		if (ret < 0) {
31816846ebeSHaibo Chen 			mutex_unlock(&indio_dev->mlock);
31916846ebeSHaibo Chen 			return ret;
32016846ebeSHaibo Chen 		}
32116846ebeSHaibo Chen 
32216846ebeSHaibo Chen 		*val = info->value;
32316846ebeSHaibo Chen 		mutex_unlock(&indio_dev->mlock);
32416846ebeSHaibo Chen 		return IIO_VAL_INT;
32516846ebeSHaibo Chen 
32616846ebeSHaibo Chen 	case IIO_CHAN_INFO_SCALE:
32716846ebeSHaibo Chen 		info->vref_uv = regulator_get_voltage(info->vref);
32816846ebeSHaibo Chen 		*val = info->vref_uv / 1000;
32916846ebeSHaibo Chen 		*val2 = 12;
33016846ebeSHaibo Chen 		return IIO_VAL_FRACTIONAL_LOG2;
33116846ebeSHaibo Chen 
33216846ebeSHaibo Chen 	case IIO_CHAN_INFO_SAMP_FREQ:
33316846ebeSHaibo Chen 		*val = imx7d_adc_get_sample_rate(info);
33416846ebeSHaibo Chen 		return IIO_VAL_INT;
33516846ebeSHaibo Chen 
33616846ebeSHaibo Chen 	default:
33716846ebeSHaibo Chen 		return -EINVAL;
33816846ebeSHaibo Chen 	}
33916846ebeSHaibo Chen }
34016846ebeSHaibo Chen 
34116846ebeSHaibo Chen static int imx7d_adc_read_data(struct imx7d_adc *info)
34216846ebeSHaibo Chen {
34316846ebeSHaibo Chen 	u32 channel;
34416846ebeSHaibo Chen 	u32 value;
34516846ebeSHaibo Chen 
34616846ebeSHaibo Chen 	channel = info->channel & 0x03;
34716846ebeSHaibo Chen 
34816846ebeSHaibo Chen 	/*
34916846ebeSHaibo Chen 	 * channel A and B conversion result share one register,
35016846ebeSHaibo Chen 	 * bit[27~16] is the channel B conversion result,
35116846ebeSHaibo Chen 	 * bit[11~0] is the channel A conversion result.
35216846ebeSHaibo Chen 	 * channel C and D is the same.
35316846ebeSHaibo Chen 	 */
35416846ebeSHaibo Chen 	if (channel < 2)
35516846ebeSHaibo Chen 		value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
35616846ebeSHaibo Chen 	else
35716846ebeSHaibo Chen 		value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
35816846ebeSHaibo Chen 	if (channel & 0x1)	/* channel B or D */
35916846ebeSHaibo Chen 		value = (value >> 16) & 0xFFF;
36016846ebeSHaibo Chen 	else			/* channel A or C */
36116846ebeSHaibo Chen 		value &= 0xFFF;
36216846ebeSHaibo Chen 
36316846ebeSHaibo Chen 	return value;
36416846ebeSHaibo Chen }
36516846ebeSHaibo Chen 
36616846ebeSHaibo Chen static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
36716846ebeSHaibo Chen {
3680b568b3cSsimran singhal 	struct imx7d_adc *info = dev_id;
36916846ebeSHaibo Chen 	int status;
37016846ebeSHaibo Chen 
37116846ebeSHaibo Chen 	status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
37216846ebeSHaibo Chen 	if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
37316846ebeSHaibo Chen 		info->value = imx7d_adc_read_data(info);
37416846ebeSHaibo Chen 		complete(&info->completion);
37516846ebeSHaibo Chen 
37616846ebeSHaibo Chen 		/*
37716846ebeSHaibo Chen 		 * The register IMX7D_REG_ADC_INT_STATUS can't clear
37816846ebeSHaibo Chen 		 * itself after read operation, need software to write
37916846ebeSHaibo Chen 		 * 0 to the related bit. Here we clear the channel A/B/C/D
38016846ebeSHaibo Chen 		 * conversion finished flag.
38116846ebeSHaibo Chen 		 */
38216846ebeSHaibo Chen 		status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
38316846ebeSHaibo Chen 		writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
38416846ebeSHaibo Chen 	}
38516846ebeSHaibo Chen 
38616846ebeSHaibo Chen 	/*
38716846ebeSHaibo Chen 	 * If the channel A/B/C/D conversion timeout, report it and clear these
38816846ebeSHaibo Chen 	 * timeout flags.
38916846ebeSHaibo Chen 	 */
39016846ebeSHaibo Chen 	if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
3918cc393bfSAndrey Smirnov 		dev_err(info->dev,
3928cc393bfSAndrey Smirnov 			"ADC got conversion time out interrupt: 0x%08x\n",
3938cc393bfSAndrey Smirnov 			status);
39416846ebeSHaibo Chen 		status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
39516846ebeSHaibo Chen 		writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
39616846ebeSHaibo Chen 	}
39716846ebeSHaibo Chen 
39816846ebeSHaibo Chen 	return IRQ_HANDLED;
39916846ebeSHaibo Chen }
40016846ebeSHaibo Chen 
40116846ebeSHaibo Chen static int imx7d_adc_reg_access(struct iio_dev *indio_dev,
40216846ebeSHaibo Chen 			unsigned reg, unsigned writeval,
40316846ebeSHaibo Chen 			unsigned *readval)
40416846ebeSHaibo Chen {
40516846ebeSHaibo Chen 	struct imx7d_adc *info = iio_priv(indio_dev);
40616846ebeSHaibo Chen 
40716846ebeSHaibo Chen 	if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)
40816846ebeSHaibo Chen 		return -EINVAL;
40916846ebeSHaibo Chen 
41016846ebeSHaibo Chen 	*readval = readl(info->regs + reg);
41116846ebeSHaibo Chen 
41216846ebeSHaibo Chen 	return 0;
41316846ebeSHaibo Chen }
41416846ebeSHaibo Chen 
41516846ebeSHaibo Chen static const struct iio_info imx7d_adc_iio_info = {
41616846ebeSHaibo Chen 	.read_raw = &imx7d_adc_read_raw,
41716846ebeSHaibo Chen 	.debugfs_reg_access = &imx7d_adc_reg_access,
41816846ebeSHaibo Chen };
41916846ebeSHaibo Chen 
42016846ebeSHaibo Chen static const struct of_device_id imx7d_adc_match[] = {
42116846ebeSHaibo Chen 	{ .compatible = "fsl,imx7d-adc", },
42216846ebeSHaibo Chen 	{ /* sentinel */ }
42316846ebeSHaibo Chen };
42416846ebeSHaibo Chen MODULE_DEVICE_TABLE(of, imx7d_adc_match);
42516846ebeSHaibo Chen 
42616846ebeSHaibo Chen static void imx7d_adc_power_down(struct imx7d_adc *info)
42716846ebeSHaibo Chen {
42816846ebeSHaibo Chen 	u32 adc_cfg;
42916846ebeSHaibo Chen 
43016846ebeSHaibo Chen 	adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
43116846ebeSHaibo Chen 	adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
43216846ebeSHaibo Chen 		   IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
43316846ebeSHaibo Chen 	adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
43416846ebeSHaibo Chen 	writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
43516846ebeSHaibo Chen }
43616846ebeSHaibo Chen 
43716846ebeSHaibo Chen static int imx7d_adc_probe(struct platform_device *pdev)
43816846ebeSHaibo Chen {
43916846ebeSHaibo Chen 	struct imx7d_adc *info;
44016846ebeSHaibo Chen 	struct iio_dev *indio_dev;
44116846ebeSHaibo Chen 	struct resource *mem;
442aa21a28eSAndrey Smirnov 	struct device *dev = &pdev->dev;
44316846ebeSHaibo Chen 	int irq;
44416846ebeSHaibo Chen 	int ret;
44516846ebeSHaibo Chen 
446aa21a28eSAndrey Smirnov 	indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
44716846ebeSHaibo Chen 	if (!indio_dev) {
44816846ebeSHaibo Chen 		dev_err(&pdev->dev, "Failed allocating iio device\n");
44916846ebeSHaibo Chen 		return -ENOMEM;
45016846ebeSHaibo Chen 	}
45116846ebeSHaibo Chen 
45216846ebeSHaibo Chen 	info = iio_priv(indio_dev);
453aa21a28eSAndrey Smirnov 	info->dev = dev;
45416846ebeSHaibo Chen 
45516846ebeSHaibo Chen 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
456aa21a28eSAndrey Smirnov 	info->regs = devm_ioremap_resource(dev, mem);
45716846ebeSHaibo Chen 	if (IS_ERR(info->regs)) {
45816846ebeSHaibo Chen 		ret = PTR_ERR(info->regs);
459aa21a28eSAndrey Smirnov 		dev_err(dev, "Failed to remap adc memory, err = %d\n", ret);
46016846ebeSHaibo Chen 		return ret;
46116846ebeSHaibo Chen 	}
46216846ebeSHaibo Chen 
46316846ebeSHaibo Chen 	irq = platform_get_irq(pdev, 0);
46416846ebeSHaibo Chen 	if (irq < 0) {
465aa21a28eSAndrey Smirnov 		dev_err(dev, "No irq resource?\n");
46616846ebeSHaibo Chen 		return irq;
46716846ebeSHaibo Chen 	}
46816846ebeSHaibo Chen 
469aa21a28eSAndrey Smirnov 	info->clk = devm_clk_get(dev, "adc");
47016846ebeSHaibo Chen 	if (IS_ERR(info->clk)) {
47116846ebeSHaibo Chen 		ret = PTR_ERR(info->clk);
472aa21a28eSAndrey Smirnov 		dev_err(dev, "Failed getting clock, err = %d\n", ret);
47316846ebeSHaibo Chen 		return ret;
47416846ebeSHaibo Chen 	}
47516846ebeSHaibo Chen 
476aa21a28eSAndrey Smirnov 	info->vref = devm_regulator_get(dev, "vref");
47716846ebeSHaibo Chen 	if (IS_ERR(info->vref)) {
47816846ebeSHaibo Chen 		ret = PTR_ERR(info->vref);
479aa21a28eSAndrey Smirnov 		dev_err(dev,
48016846ebeSHaibo Chen 			"Failed getting reference voltage, err = %d\n", ret);
48116846ebeSHaibo Chen 		return ret;
48216846ebeSHaibo Chen 	}
48316846ebeSHaibo Chen 
48416846ebeSHaibo Chen 	ret = regulator_enable(info->vref);
48516846ebeSHaibo Chen 	if (ret) {
486aa21a28eSAndrey Smirnov 		dev_err(dev,
48716846ebeSHaibo Chen 			"Can't enable adc reference top voltage, err = %d\n",
48816846ebeSHaibo Chen 			ret);
48916846ebeSHaibo Chen 		return ret;
49016846ebeSHaibo Chen 	}
49116846ebeSHaibo Chen 
49216846ebeSHaibo Chen 	platform_set_drvdata(pdev, indio_dev);
49316846ebeSHaibo Chen 
49416846ebeSHaibo Chen 	init_completion(&info->completion);
49516846ebeSHaibo Chen 
496aa21a28eSAndrey Smirnov 	indio_dev->name = dev_name(dev);
497aa21a28eSAndrey Smirnov 	indio_dev->dev.parent = dev;
49816846ebeSHaibo Chen 	indio_dev->info = &imx7d_adc_iio_info;
49916846ebeSHaibo Chen 	indio_dev->modes = INDIO_DIRECT_MODE;
50016846ebeSHaibo Chen 	indio_dev->channels = imx7d_adc_iio_channels;
50116846ebeSHaibo Chen 	indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
50216846ebeSHaibo Chen 
50316846ebeSHaibo Chen 	ret = clk_prepare_enable(info->clk);
50416846ebeSHaibo Chen 	if (ret) {
505aa21a28eSAndrey Smirnov 		dev_err(dev, "Could not prepare or enable the clock.\n");
50616846ebeSHaibo Chen 		goto error_adc_clk_enable;
50716846ebeSHaibo Chen 	}
50816846ebeSHaibo Chen 
509aa21a28eSAndrey Smirnov 	ret = devm_request_irq(dev, irq,
51016846ebeSHaibo Chen 			       imx7d_adc_isr, 0,
511aa21a28eSAndrey Smirnov 			       dev_name(dev), info);
51216846ebeSHaibo Chen 	if (ret < 0) {
513aa21a28eSAndrey Smirnov 		dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
51416846ebeSHaibo Chen 		goto error_iio_device_register;
51516846ebeSHaibo Chen 	}
51616846ebeSHaibo Chen 
51716846ebeSHaibo Chen 	imx7d_adc_feature_config(info);
51816846ebeSHaibo Chen 	imx7d_adc_hw_init(info);
51916846ebeSHaibo Chen 
52016846ebeSHaibo Chen 	ret = iio_device_register(indio_dev);
52116846ebeSHaibo Chen 	if (ret) {
52216846ebeSHaibo Chen 		imx7d_adc_power_down(info);
52316846ebeSHaibo Chen 		dev_err(&pdev->dev, "Couldn't register the device.\n");
52416846ebeSHaibo Chen 		goto error_iio_device_register;
52516846ebeSHaibo Chen 	}
52616846ebeSHaibo Chen 
52716846ebeSHaibo Chen 	return 0;
52816846ebeSHaibo Chen 
52916846ebeSHaibo Chen error_iio_device_register:
53016846ebeSHaibo Chen 	clk_disable_unprepare(info->clk);
53116846ebeSHaibo Chen error_adc_clk_enable:
53216846ebeSHaibo Chen 	regulator_disable(info->vref);
53316846ebeSHaibo Chen 
53416846ebeSHaibo Chen 	return ret;
53516846ebeSHaibo Chen }
53616846ebeSHaibo Chen 
53716846ebeSHaibo Chen static int imx7d_adc_remove(struct platform_device *pdev)
53816846ebeSHaibo Chen {
53916846ebeSHaibo Chen 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
54016846ebeSHaibo Chen 	struct imx7d_adc *info = iio_priv(indio_dev);
54116846ebeSHaibo Chen 
54216846ebeSHaibo Chen 	iio_device_unregister(indio_dev);
54316846ebeSHaibo Chen 
54416846ebeSHaibo Chen 	imx7d_adc_power_down(info);
54516846ebeSHaibo Chen 
54616846ebeSHaibo Chen 	clk_disable_unprepare(info->clk);
54716846ebeSHaibo Chen 	regulator_disable(info->vref);
54816846ebeSHaibo Chen 
54916846ebeSHaibo Chen 	return 0;
55016846ebeSHaibo Chen }
55116846ebeSHaibo Chen 
55216846ebeSHaibo Chen static int __maybe_unused imx7d_adc_suspend(struct device *dev)
55316846ebeSHaibo Chen {
55416846ebeSHaibo Chen 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
55516846ebeSHaibo Chen 	struct imx7d_adc *info = iio_priv(indio_dev);
55616846ebeSHaibo Chen 
55716846ebeSHaibo Chen 	imx7d_adc_power_down(info);
55816846ebeSHaibo Chen 
55916846ebeSHaibo Chen 	clk_disable_unprepare(info->clk);
56016846ebeSHaibo Chen 	regulator_disable(info->vref);
56116846ebeSHaibo Chen 
56216846ebeSHaibo Chen 	return 0;
56316846ebeSHaibo Chen }
56416846ebeSHaibo Chen 
56516846ebeSHaibo Chen static int __maybe_unused imx7d_adc_resume(struct device *dev)
56616846ebeSHaibo Chen {
56716846ebeSHaibo Chen 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
56816846ebeSHaibo Chen 	struct imx7d_adc *info = iio_priv(indio_dev);
56916846ebeSHaibo Chen 	int ret;
57016846ebeSHaibo Chen 
57116846ebeSHaibo Chen 	ret = regulator_enable(info->vref);
57216846ebeSHaibo Chen 	if (ret) {
57316846ebeSHaibo Chen 		dev_err(info->dev,
57416846ebeSHaibo Chen 			"Can't enable adc reference top voltage, err = %d\n",
57516846ebeSHaibo Chen 			ret);
57616846ebeSHaibo Chen 		return ret;
57716846ebeSHaibo Chen 	}
57816846ebeSHaibo Chen 
57916846ebeSHaibo Chen 	ret = clk_prepare_enable(info->clk);
58016846ebeSHaibo Chen 	if (ret) {
58116846ebeSHaibo Chen 		dev_err(info->dev,
58216846ebeSHaibo Chen 			"Could not prepare or enable clock.\n");
58316846ebeSHaibo Chen 		regulator_disable(info->vref);
58416846ebeSHaibo Chen 		return ret;
58516846ebeSHaibo Chen 	}
58616846ebeSHaibo Chen 
58716846ebeSHaibo Chen 	imx7d_adc_hw_init(info);
58816846ebeSHaibo Chen 
58916846ebeSHaibo Chen 	return 0;
59016846ebeSHaibo Chen }
59116846ebeSHaibo Chen 
59216846ebeSHaibo Chen static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
59316846ebeSHaibo Chen 
59416846ebeSHaibo Chen static struct platform_driver imx7d_adc_driver = {
59516846ebeSHaibo Chen 	.probe		= imx7d_adc_probe,
59616846ebeSHaibo Chen 	.remove		= imx7d_adc_remove,
59716846ebeSHaibo Chen 	.driver		= {
59816846ebeSHaibo Chen 		.name	= "imx7d_adc",
59916846ebeSHaibo Chen 		.of_match_table = imx7d_adc_match,
60016846ebeSHaibo Chen 		.pm	= &imx7d_adc_pm_ops,
60116846ebeSHaibo Chen 	},
60216846ebeSHaibo Chen };
60316846ebeSHaibo Chen 
60416846ebeSHaibo Chen module_platform_driver(imx7d_adc_driver);
60516846ebeSHaibo Chen 
60616846ebeSHaibo Chen MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
607dd63b4faSColin Ian King MODULE_DESCRIPTION("Freescale IMX7D ADC driver");
60816846ebeSHaibo Chen MODULE_LICENSE("GPL v2");
609