162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale i.MX7D ADC driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/completion.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/mutex.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/iio/iio.h>
2162306a36Sopenharmony_ci#include <linux/iio/driver.h>
2262306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* ADC register */
2562306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_A_CFG1			0x00
2662306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_A_CFG2			0x10
2762306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_B_CFG1			0x20
2862306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_B_CFG2			0x30
2962306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_C_CFG1			0x40
3062306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_C_CFG2			0x50
3162306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_D_CFG1			0x60
3262306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_D_CFG2			0x70
3362306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_SW_CFG			0x80
3462306a36Sopenharmony_ci#define IMX7D_REG_ADC_TIMER_UNIT		0x90
3562306a36Sopenharmony_ci#define IMX7D_REG_ADC_DMA_FIFO			0xa0
3662306a36Sopenharmony_ci#define IMX7D_REG_ADC_FIFO_STATUS		0xb0
3762306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_SIG_EN		0xc0
3862306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_EN			0xd0
3962306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_STATUS		0xe0
4062306a36Sopenharmony_ci#define IMX7D_REG_ADC_CHA_B_CNV_RSLT		0xf0
4162306a36Sopenharmony_ci#define IMX7D_REG_ADC_CHC_D_CNV_RSLT		0x100
4262306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_SW_CNV_RSLT		0x110
4362306a36Sopenharmony_ci#define IMX7D_REG_ADC_DMA_FIFO_DAT		0x120
4462306a36Sopenharmony_ci#define IMX7D_REG_ADC_ADC_CFG			0x130
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE		0x10
4762306a36Sopenharmony_ci#define IMX7D_EACH_CHANNEL_REG_OFFSET		0x20
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN			(0x1 << 31)
5062306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE			BIT(30)
5162306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN			BIT(29)
5262306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x)			((x) << 24)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4				(0x0 << 12)
5562306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8				(0x1 << 12)
5662306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16			(0x2 << 12)
5762306a36Sopenharmony_ci#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32			(0x3 << 12)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4			(0x0 << 29)
6062306a36Sopenharmony_ci#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8			(0x1 << 29)
6162306a36Sopenharmony_ci#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16			(0x2 << 29)
6262306a36Sopenharmony_ci#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32			(0x3 << 29)
6362306a36Sopenharmony_ci#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64			(0x4 << 29)
6462306a36Sopenharmony_ci#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128			(0x5 << 29)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN			BIT(31)
6762306a36Sopenharmony_ci#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN			BIT(1)
6862306a36Sopenharmony_ci#define IMX7D_REG_ADC_ADC_CFG_ADC_EN				BIT(0)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN			BIT(8)
7162306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN			BIT(9)
7262306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN			BIT(10)
7362306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN			BIT(11)
7462306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \
7562306a36Sopenharmony_ci	(IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \
7662306a36Sopenharmony_ci	 IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \
7762306a36Sopenharmony_ci	 IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \
7862306a36Sopenharmony_ci	 IMX7D_REG_ADC_INT_CHD_COV_INT_EN)
7962306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS		0xf00
8062306a36Sopenharmony_ci#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT		0xf0000
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define IMX7D_ADC_TIMEOUT		msecs_to_jiffies(100)
8362306a36Sopenharmony_ci#define IMX7D_ADC_INPUT_CLK		24000000
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cienum imx7d_adc_clk_pre_div {
8662306a36Sopenharmony_ci	IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
8762306a36Sopenharmony_ci	IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
8862306a36Sopenharmony_ci	IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
8962306a36Sopenharmony_ci	IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
9062306a36Sopenharmony_ci	IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
9162306a36Sopenharmony_ci	IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cienum imx7d_adc_average_num {
9562306a36Sopenharmony_ci	IMX7D_ADC_AVERAGE_NUM_4,
9662306a36Sopenharmony_ci	IMX7D_ADC_AVERAGE_NUM_8,
9762306a36Sopenharmony_ci	IMX7D_ADC_AVERAGE_NUM_16,
9862306a36Sopenharmony_ci	IMX7D_ADC_AVERAGE_NUM_32,
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct imx7d_adc_feature {
10262306a36Sopenharmony_ci	enum imx7d_adc_clk_pre_div clk_pre_div;
10362306a36Sopenharmony_ci	enum imx7d_adc_average_num avg_num;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	u32 core_time_unit;	/* impact the sample rate */
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistruct imx7d_adc {
10962306a36Sopenharmony_ci	struct device *dev;
11062306a36Sopenharmony_ci	void __iomem *regs;
11162306a36Sopenharmony_ci	struct clk *clk;
11262306a36Sopenharmony_ci	/* lock to protect against multiple access to the device */
11362306a36Sopenharmony_ci	struct mutex lock;
11462306a36Sopenharmony_ci	u32 vref_uv;
11562306a36Sopenharmony_ci	u32 value;
11662306a36Sopenharmony_ci	u32 channel;
11762306a36Sopenharmony_ci	u32 pre_div_num;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	struct regulator *vref;
12062306a36Sopenharmony_ci	struct imx7d_adc_feature adc_feature;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	struct completion completion;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct imx7d_adc_analogue_core_clk {
12662306a36Sopenharmony_ci	u32 pre_div;
12762306a36Sopenharmony_ci	u32 reg_config;
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) {	\
13162306a36Sopenharmony_ci	.pre_div = (_pre_div),					\
13262306a36Sopenharmony_ci	.reg_config = (_reg_conf),				\
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
13662306a36Sopenharmony_ci	IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
13762306a36Sopenharmony_ci	IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
13862306a36Sopenharmony_ci	IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
13962306a36Sopenharmony_ci	IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
14062306a36Sopenharmony_ci	IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
14162306a36Sopenharmony_ci	IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#define IMX7D_ADC_CHAN(_idx) {					\
14562306a36Sopenharmony_ci	.type = IIO_VOLTAGE,					\
14662306a36Sopenharmony_ci	.indexed = 1,						\
14762306a36Sopenharmony_ci	.channel = (_idx),					\
14862306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
14962306a36Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\
15062306a36Sopenharmony_ci				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic const struct iio_chan_spec imx7d_adc_iio_channels[] = {
15462306a36Sopenharmony_ci	IMX7D_ADC_CHAN(0),
15562306a36Sopenharmony_ci	IMX7D_ADC_CHAN(1),
15662306a36Sopenharmony_ci	IMX7D_ADC_CHAN(2),
15762306a36Sopenharmony_ci	IMX7D_ADC_CHAN(3),
15862306a36Sopenharmony_ci	IMX7D_ADC_CHAN(4),
15962306a36Sopenharmony_ci	IMX7D_ADC_CHAN(5),
16062306a36Sopenharmony_ci	IMX7D_ADC_CHAN(6),
16162306a36Sopenharmony_ci	IMX7D_ADC_CHAN(7),
16262306a36Sopenharmony_ci	IMX7D_ADC_CHAN(8),
16362306a36Sopenharmony_ci	IMX7D_ADC_CHAN(9),
16462306a36Sopenharmony_ci	IMX7D_ADC_CHAN(10),
16562306a36Sopenharmony_ci	IMX7D_ADC_CHAN(11),
16662306a36Sopenharmony_ci	IMX7D_ADC_CHAN(12),
16762306a36Sopenharmony_ci	IMX7D_ADC_CHAN(13),
16862306a36Sopenharmony_ci	IMX7D_ADC_CHAN(14),
16962306a36Sopenharmony_ci	IMX7D_ADC_CHAN(15),
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic const u32 imx7d_adc_average_num[] = {
17362306a36Sopenharmony_ci	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
17462306a36Sopenharmony_ci	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
17562306a36Sopenharmony_ci	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
17662306a36Sopenharmony_ci	IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void imx7d_adc_feature_config(struct imx7d_adc *info)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
18262306a36Sopenharmony_ci	info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
18362306a36Sopenharmony_ci	info->adc_feature.core_time_unit = 1;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct imx7d_adc_feature *adc_feature = &info->adc_feature;
18962306a36Sopenharmony_ci	struct imx7d_adc_analogue_core_clk adc_analogure_clk;
19062306a36Sopenharmony_ci	u32 i;
19162306a36Sopenharmony_ci	u32 tmp_cfg1;
19262306a36Sopenharmony_ci	u32 sample_rate = 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/*
19562306a36Sopenharmony_ci	 * Before sample set, disable channel A,B,C,D. Here we
19662306a36Sopenharmony_ci	 * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
19962306a36Sopenharmony_ci		tmp_cfg1 =
20062306a36Sopenharmony_ci			readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
20162306a36Sopenharmony_ci		tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
20262306a36Sopenharmony_ci		writel(tmp_cfg1,
20362306a36Sopenharmony_ci		       info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
20762306a36Sopenharmony_ci	sample_rate |= adc_analogure_clk.reg_config;
20862306a36Sopenharmony_ci	info->pre_div_num = adc_analogure_clk.pre_div;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	sample_rate |= adc_feature->core_time_unit;
21162306a36Sopenharmony_ci	writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void imx7d_adc_hw_init(struct imx7d_adc *info)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	u32 cfg;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* power up and enable adc analogue core */
21962306a36Sopenharmony_ci	cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
22062306a36Sopenharmony_ci	cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
22162306a36Sopenharmony_ci		 IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
22262306a36Sopenharmony_ci	cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
22362306a36Sopenharmony_ci	writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* enable channel A,B,C,D interrupt */
22662306a36Sopenharmony_ci	writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
22762306a36Sopenharmony_ci	       info->regs + IMX7D_REG_ADC_INT_SIG_EN);
22862306a36Sopenharmony_ci	writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
22962306a36Sopenharmony_ci	       info->regs + IMX7D_REG_ADC_INT_EN);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	imx7d_adc_sample_rate_set(info);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic void imx7d_adc_channel_set(struct imx7d_adc *info)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	u32 cfg1 = 0;
23762306a36Sopenharmony_ci	u32 cfg2;
23862306a36Sopenharmony_ci	u32 channel;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	channel = info->channel;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* the channel choose single conversion, and enable average mode */
24362306a36Sopenharmony_ci	cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
24462306a36Sopenharmony_ci		 IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
24562306a36Sopenharmony_ci		 IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/*
24862306a36Sopenharmony_ci	 * physical channel 0 chose logical channel A
24962306a36Sopenharmony_ci	 * physical channel 1 chose logical channel B
25062306a36Sopenharmony_ci	 * physical channel 2 chose logical channel C
25162306a36Sopenharmony_ci	 * physical channel 3 chose logical channel D
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/*
25662306a36Sopenharmony_ci	 * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
25762306a36Sopenharmony_ci	 * channel chosen
25862306a36Sopenharmony_ci	 */
25962306a36Sopenharmony_ci	cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
26062306a36Sopenharmony_ci		     IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/*
26562306a36Sopenharmony_ci	 * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
26662306a36Sopenharmony_ci	 * the channel chosen
26762306a36Sopenharmony_ci	 */
26862306a36Sopenharmony_ci	writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
26962306a36Sopenharmony_ci	       IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
27062306a36Sopenharmony_ci	writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	u32 analogue_core_clk;
27662306a36Sopenharmony_ci	u32 core_time_unit = info->adc_feature.core_time_unit;
27762306a36Sopenharmony_ci	u32 tmp;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
28062306a36Sopenharmony_ci	tmp = (core_time_unit + 1) * 6;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return analogue_core_clk / tmp;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int imx7d_adc_read_raw(struct iio_dev *indio_dev,
28662306a36Sopenharmony_ci			struct iio_chan_spec const *chan,
28762306a36Sopenharmony_ci			int *val,
28862306a36Sopenharmony_ci			int *val2,
28962306a36Sopenharmony_ci			long mask)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct imx7d_adc *info = iio_priv(indio_dev);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	u32 channel;
29462306a36Sopenharmony_ci	long ret;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	switch (mask) {
29762306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
29862306a36Sopenharmony_ci		mutex_lock(&info->lock);
29962306a36Sopenharmony_ci		reinit_completion(&info->completion);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		channel = chan->channel & 0x03;
30262306a36Sopenharmony_ci		info->channel = channel;
30362306a36Sopenharmony_ci		imx7d_adc_channel_set(info);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		ret = wait_for_completion_interruptible_timeout
30662306a36Sopenharmony_ci				(&info->completion, IMX7D_ADC_TIMEOUT);
30762306a36Sopenharmony_ci		if (ret == 0) {
30862306a36Sopenharmony_ci			mutex_unlock(&info->lock);
30962306a36Sopenharmony_ci			return -ETIMEDOUT;
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci		if (ret < 0) {
31262306a36Sopenharmony_ci			mutex_unlock(&info->lock);
31362306a36Sopenharmony_ci			return ret;
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		*val = info->value;
31762306a36Sopenharmony_ci		mutex_unlock(&info->lock);
31862306a36Sopenharmony_ci		return IIO_VAL_INT;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
32162306a36Sopenharmony_ci		info->vref_uv = regulator_get_voltage(info->vref);
32262306a36Sopenharmony_ci		*val = info->vref_uv / 1000;
32362306a36Sopenharmony_ci		*val2 = 12;
32462306a36Sopenharmony_ci		return IIO_VAL_FRACTIONAL_LOG2;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	case IIO_CHAN_INFO_SAMP_FREQ:
32762306a36Sopenharmony_ci		*val = imx7d_adc_get_sample_rate(info);
32862306a36Sopenharmony_ci		return IIO_VAL_INT;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	default:
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int imx7d_adc_read_data(struct imx7d_adc *info)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	u32 channel;
33862306a36Sopenharmony_ci	u32 value;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	channel = info->channel & 0x03;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * channel A and B conversion result share one register,
34462306a36Sopenharmony_ci	 * bit[27~16] is the channel B conversion result,
34562306a36Sopenharmony_ci	 * bit[11~0] is the channel A conversion result.
34662306a36Sopenharmony_ci	 * channel C and D is the same.
34762306a36Sopenharmony_ci	 */
34862306a36Sopenharmony_ci	if (channel < 2)
34962306a36Sopenharmony_ci		value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
35062306a36Sopenharmony_ci	else
35162306a36Sopenharmony_ci		value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
35262306a36Sopenharmony_ci	if (channel & 0x1)	/* channel B or D */
35362306a36Sopenharmony_ci		value = (value >> 16) & 0xFFF;
35462306a36Sopenharmony_ci	else			/* channel A or C */
35562306a36Sopenharmony_ci		value &= 0xFFF;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return value;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct imx7d_adc *info = dev_id;
36362306a36Sopenharmony_ci	int status;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
36662306a36Sopenharmony_ci	if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
36762306a36Sopenharmony_ci		info->value = imx7d_adc_read_data(info);
36862306a36Sopenharmony_ci		complete(&info->completion);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/*
37162306a36Sopenharmony_ci		 * The register IMX7D_REG_ADC_INT_STATUS can't clear
37262306a36Sopenharmony_ci		 * itself after read operation, need software to write
37362306a36Sopenharmony_ci		 * 0 to the related bit. Here we clear the channel A/B/C/D
37462306a36Sopenharmony_ci		 * conversion finished flag.
37562306a36Sopenharmony_ci		 */
37662306a36Sopenharmony_ci		status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
37762306a36Sopenharmony_ci		writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/*
38162306a36Sopenharmony_ci	 * If the channel A/B/C/D conversion timeout, report it and clear these
38262306a36Sopenharmony_ci	 * timeout flags.
38362306a36Sopenharmony_ci	 */
38462306a36Sopenharmony_ci	if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
38562306a36Sopenharmony_ci		dev_err(info->dev,
38662306a36Sopenharmony_ci			"ADC got conversion time out interrupt: 0x%08x\n",
38762306a36Sopenharmony_ci			status);
38862306a36Sopenharmony_ci		status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
38962306a36Sopenharmony_ci		writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return IRQ_HANDLED;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int imx7d_adc_reg_access(struct iio_dev *indio_dev,
39662306a36Sopenharmony_ci			unsigned reg, unsigned writeval,
39762306a36Sopenharmony_ci			unsigned *readval)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct imx7d_adc *info = iio_priv(indio_dev);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)
40262306a36Sopenharmony_ci		return -EINVAL;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	*readval = readl(info->regs + reg);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic const struct iio_info imx7d_adc_iio_info = {
41062306a36Sopenharmony_ci	.read_raw = &imx7d_adc_read_raw,
41162306a36Sopenharmony_ci	.debugfs_reg_access = &imx7d_adc_reg_access,
41262306a36Sopenharmony_ci};
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic const struct of_device_id imx7d_adc_match[] = {
41562306a36Sopenharmony_ci	{ .compatible = "fsl,imx7d-adc", },
41662306a36Sopenharmony_ci	{ /* sentinel */ }
41762306a36Sopenharmony_ci};
41862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx7d_adc_match);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic void imx7d_adc_power_down(struct imx7d_adc *info)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	u32 adc_cfg;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
42562306a36Sopenharmony_ci	adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
42662306a36Sopenharmony_ci		   IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
42762306a36Sopenharmony_ci	adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
42862306a36Sopenharmony_ci	writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int imx7d_adc_enable(struct device *dev)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
43462306a36Sopenharmony_ci	struct imx7d_adc *info = iio_priv(indio_dev);
43562306a36Sopenharmony_ci	int ret;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	ret = regulator_enable(info->vref);
43862306a36Sopenharmony_ci	if (ret) {
43962306a36Sopenharmony_ci		dev_err(info->dev,
44062306a36Sopenharmony_ci			"Can't enable adc reference top voltage, err = %d\n",
44162306a36Sopenharmony_ci			ret);
44262306a36Sopenharmony_ci		return ret;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	ret = clk_prepare_enable(info->clk);
44662306a36Sopenharmony_ci	if (ret) {
44762306a36Sopenharmony_ci		dev_err(info->dev,
44862306a36Sopenharmony_ci			"Could not prepare or enable clock.\n");
44962306a36Sopenharmony_ci		regulator_disable(info->vref);
45062306a36Sopenharmony_ci		return ret;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	imx7d_adc_hw_init(info);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return 0;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic int imx7d_adc_disable(struct device *dev)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct iio_dev *indio_dev = dev_get_drvdata(dev);
46162306a36Sopenharmony_ci	struct imx7d_adc *info = iio_priv(indio_dev);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	imx7d_adc_power_down(info);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	clk_disable_unprepare(info->clk);
46662306a36Sopenharmony_ci	regulator_disable(info->vref);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return 0;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic void __imx7d_adc_disable(void *data)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	imx7d_adc_disable(data);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int imx7d_adc_probe(struct platform_device *pdev)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct imx7d_adc *info;
47962306a36Sopenharmony_ci	struct iio_dev *indio_dev;
48062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
48162306a36Sopenharmony_ci	int irq;
48262306a36Sopenharmony_ci	int ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
48562306a36Sopenharmony_ci	if (!indio_dev) {
48662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed allocating iio device\n");
48762306a36Sopenharmony_ci		return -ENOMEM;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	info = iio_priv(indio_dev);
49162306a36Sopenharmony_ci	info->dev = dev;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	info->regs = devm_platform_ioremap_resource(pdev, 0);
49462306a36Sopenharmony_ci	if (IS_ERR(info->regs))
49562306a36Sopenharmony_ci		return PTR_ERR(info->regs);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
49862306a36Sopenharmony_ci	if (irq < 0)
49962306a36Sopenharmony_ci		return irq;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	info->clk = devm_clk_get(dev, "adc");
50262306a36Sopenharmony_ci	if (IS_ERR(info->clk))
50362306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n");
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	info->vref = devm_regulator_get(dev, "vref");
50662306a36Sopenharmony_ci	if (IS_ERR(info->vref))
50762306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(info->vref),
50862306a36Sopenharmony_ci				     "Failed getting reference voltage\n");
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	platform_set_drvdata(pdev, indio_dev);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	init_completion(&info->completion);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	indio_dev->name = dev_name(dev);
51562306a36Sopenharmony_ci	indio_dev->info = &imx7d_adc_iio_info;
51662306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
51762306a36Sopenharmony_ci	indio_dev->channels = imx7d_adc_iio_channels;
51862306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info);
52162306a36Sopenharmony_ci	if (ret < 0) {
52262306a36Sopenharmony_ci		dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
52362306a36Sopenharmony_ci		return ret;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	imx7d_adc_feature_config(info);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	ret = imx7d_adc_enable(dev);
52962306a36Sopenharmony_ci	if (ret)
53062306a36Sopenharmony_ci		return ret;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, dev);
53362306a36Sopenharmony_ci	if (ret)
53462306a36Sopenharmony_ci		return ret;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	mutex_init(&info->lock);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	ret = devm_iio_device_register(dev, indio_dev);
53962306a36Sopenharmony_ci	if (ret) {
54062306a36Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't register the device.\n");
54162306a36Sopenharmony_ci		return ret;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable,
54862306a36Sopenharmony_ci				imx7d_adc_enable);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic struct platform_driver imx7d_adc_driver = {
55162306a36Sopenharmony_ci	.probe		= imx7d_adc_probe,
55262306a36Sopenharmony_ci	.driver		= {
55362306a36Sopenharmony_ci		.name	= "imx7d_adc",
55462306a36Sopenharmony_ci		.of_match_table = imx7d_adc_match,
55562306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&imx7d_adc_pm_ops),
55662306a36Sopenharmony_ci	},
55762306a36Sopenharmony_ci};
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cimodule_platform_driver(imx7d_adc_driver);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ciMODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
56262306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale IMX7D ADC driver");
56362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
564