12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
216846ebeSHaibo Chen /*
316846ebeSHaibo Chen * Freescale i.MX7D ADC driver
416846ebeSHaibo Chen *
516846ebeSHaibo Chen * Copyright (C) 2015 Freescale Semiconductor, Inc.
616846ebeSHaibo Chen */
716846ebeSHaibo Chen
816846ebeSHaibo Chen #include <linux/clk.h>
916846ebeSHaibo Chen #include <linux/completion.h>
1016846ebeSHaibo Chen #include <linux/err.h>
1116846ebeSHaibo Chen #include <linux/interrupt.h>
1216846ebeSHaibo Chen #include <linux/io.h>
1316846ebeSHaibo Chen #include <linux/kernel.h>
14848d1901SNuno Sá #include <linux/mod_devicetable.h>
1516846ebeSHaibo Chen #include <linux/module.h>
167dde7ec2SNuno Sá #include <linux/mutex.h>
1716846ebeSHaibo Chen #include <linux/platform_device.h>
1816846ebeSHaibo Chen #include <linux/regulator/consumer.h>
1916846ebeSHaibo Chen
2016846ebeSHaibo Chen #include <linux/iio/iio.h>
2116846ebeSHaibo Chen #include <linux/iio/driver.h>
2216846ebeSHaibo Chen #include <linux/iio/sysfs.h>
2316846ebeSHaibo Chen
2416846ebeSHaibo Chen /* ADC register */
2516846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_A_CFG1 0x00
2616846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_A_CFG2 0x10
2716846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_B_CFG1 0x20
2816846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_B_CFG2 0x30
2916846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_C_CFG1 0x40
3016846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_C_CFG2 0x50
3116846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_D_CFG1 0x60
3216846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_D_CFG2 0x70
3316846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_SW_CFG 0x80
3416846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT 0x90
3516846ebeSHaibo Chen #define IMX7D_REG_ADC_DMA_FIFO 0xa0
3616846ebeSHaibo Chen #define IMX7D_REG_ADC_FIFO_STATUS 0xb0
3716846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_SIG_EN 0xc0
3816846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_EN 0xd0
3916846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_STATUS 0xe0
4016846ebeSHaibo Chen #define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0
4116846ebeSHaibo Chen #define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100
4216846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_SW_CNV_RSLT 0x110
4316846ebeSHaibo Chen #define IMX7D_REG_ADC_DMA_FIFO_DAT 0x120
4416846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG 0x130
4516846ebeSHaibo Chen
4616846ebeSHaibo Chen #define IMX7D_REG_ADC_CHANNEL_CFG2_BASE 0x10
4716846ebeSHaibo Chen #define IMX7D_EACH_CHANNEL_REG_OFFSET 0x20
4816846ebeSHaibo Chen
4916846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31)
5016846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30)
5116846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29)
5216846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24)
5316846ebeSHaibo Chen
5416846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12)
5516846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12)
5616846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12)
5716846ebeSHaibo Chen #define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12)
5816846ebeSHaibo Chen
5916846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29)
6016846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29)
6116846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29)
6216846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29)
6316846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29)
6416846ebeSHaibo Chen #define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29)
6516846ebeSHaibo Chen
6616846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31)
6716846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1)
6816846ebeSHaibo Chen #define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0)
6916846ebeSHaibo Chen
7016846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHA_COV_INT_EN BIT(8)
7116846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHB_COV_INT_EN BIT(9)
7216846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHC_COV_INT_EN BIT(10)
7316846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHD_COV_INT_EN BIT(11)
7416846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \
7516846ebeSHaibo Chen (IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \
7616846ebeSHaibo Chen IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \
7716846ebeSHaibo Chen IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \
7816846ebeSHaibo Chen IMX7D_REG_ADC_INT_CHD_COV_INT_EN)
7916846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00
8016846ebeSHaibo Chen #define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
8116846ebeSHaibo Chen
8216846ebeSHaibo Chen #define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
839ce92da8SFabio Estevam #define IMX7D_ADC_INPUT_CLK 24000000
8416846ebeSHaibo Chen
8516846ebeSHaibo Chen enum imx7d_adc_clk_pre_div {
8616846ebeSHaibo Chen IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
8716846ebeSHaibo Chen IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
8816846ebeSHaibo Chen IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
8916846ebeSHaibo Chen IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
9016846ebeSHaibo Chen IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
9116846ebeSHaibo Chen IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
9216846ebeSHaibo Chen };
9316846ebeSHaibo Chen
9416846ebeSHaibo Chen enum imx7d_adc_average_num {
9516846ebeSHaibo Chen IMX7D_ADC_AVERAGE_NUM_4,
9616846ebeSHaibo Chen IMX7D_ADC_AVERAGE_NUM_8,
9716846ebeSHaibo Chen IMX7D_ADC_AVERAGE_NUM_16,
9816846ebeSHaibo Chen IMX7D_ADC_AVERAGE_NUM_32,
9916846ebeSHaibo Chen };
10016846ebeSHaibo Chen
10116846ebeSHaibo Chen struct imx7d_adc_feature {
10216846ebeSHaibo Chen enum imx7d_adc_clk_pre_div clk_pre_div;
10316846ebeSHaibo Chen enum imx7d_adc_average_num avg_num;
10416846ebeSHaibo Chen
10516846ebeSHaibo Chen u32 core_time_unit; /* impact the sample rate */
10616846ebeSHaibo Chen };
10716846ebeSHaibo Chen
10816846ebeSHaibo Chen struct imx7d_adc {
10916846ebeSHaibo Chen struct device *dev;
11016846ebeSHaibo Chen void __iomem *regs;
11116846ebeSHaibo Chen struct clk *clk;
1127dde7ec2SNuno Sá /* lock to protect against multiple access to the device */
1137dde7ec2SNuno Sá struct mutex lock;
11416846ebeSHaibo Chen u32 vref_uv;
11516846ebeSHaibo Chen u32 value;
11616846ebeSHaibo Chen u32 channel;
11716846ebeSHaibo Chen u32 pre_div_num;
11816846ebeSHaibo Chen
11916846ebeSHaibo Chen struct regulator *vref;
12016846ebeSHaibo Chen struct imx7d_adc_feature adc_feature;
12116846ebeSHaibo Chen
12216846ebeSHaibo Chen struct completion completion;
12316846ebeSHaibo Chen };
12416846ebeSHaibo Chen
12516846ebeSHaibo Chen struct imx7d_adc_analogue_core_clk {
12616846ebeSHaibo Chen u32 pre_div;
12716846ebeSHaibo Chen u32 reg_config;
12816846ebeSHaibo Chen };
12916846ebeSHaibo Chen
13016846ebeSHaibo Chen #define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \
13116846ebeSHaibo Chen .pre_div = (_pre_div), \
13216846ebeSHaibo Chen .reg_config = (_reg_conf), \
13316846ebeSHaibo Chen }
13416846ebeSHaibo Chen
13516846ebeSHaibo Chen static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
13616846ebeSHaibo Chen IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
13716846ebeSHaibo Chen IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
13816846ebeSHaibo Chen IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
13916846ebeSHaibo Chen IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
14016846ebeSHaibo Chen IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
14116846ebeSHaibo Chen IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
14216846ebeSHaibo Chen };
14316846ebeSHaibo Chen
14416846ebeSHaibo Chen #define IMX7D_ADC_CHAN(_idx) { \
14516846ebeSHaibo Chen .type = IIO_VOLTAGE, \
14616846ebeSHaibo Chen .indexed = 1, \
14716846ebeSHaibo Chen .channel = (_idx), \
14816846ebeSHaibo Chen .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
14916846ebeSHaibo Chen .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
15016846ebeSHaibo Chen BIT(IIO_CHAN_INFO_SAMP_FREQ), \
15116846ebeSHaibo Chen }
15216846ebeSHaibo Chen
15316846ebeSHaibo Chen static const struct iio_chan_spec imx7d_adc_iio_channels[] = {
15416846ebeSHaibo Chen IMX7D_ADC_CHAN(0),
15516846ebeSHaibo Chen IMX7D_ADC_CHAN(1),
15616846ebeSHaibo Chen IMX7D_ADC_CHAN(2),
15716846ebeSHaibo Chen IMX7D_ADC_CHAN(3),
15816846ebeSHaibo Chen IMX7D_ADC_CHAN(4),
15916846ebeSHaibo Chen IMX7D_ADC_CHAN(5),
16016846ebeSHaibo Chen IMX7D_ADC_CHAN(6),
16116846ebeSHaibo Chen IMX7D_ADC_CHAN(7),
16216846ebeSHaibo Chen IMX7D_ADC_CHAN(8),
16316846ebeSHaibo Chen IMX7D_ADC_CHAN(9),
16416846ebeSHaibo Chen IMX7D_ADC_CHAN(10),
16516846ebeSHaibo Chen IMX7D_ADC_CHAN(11),
16616846ebeSHaibo Chen IMX7D_ADC_CHAN(12),
16716846ebeSHaibo Chen IMX7D_ADC_CHAN(13),
16816846ebeSHaibo Chen IMX7D_ADC_CHAN(14),
16916846ebeSHaibo Chen IMX7D_ADC_CHAN(15),
17016846ebeSHaibo Chen };
17116846ebeSHaibo Chen
17216846ebeSHaibo Chen static const u32 imx7d_adc_average_num[] = {
17316846ebeSHaibo Chen IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
17416846ebeSHaibo Chen IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
17516846ebeSHaibo Chen IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
17616846ebeSHaibo Chen IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
17716846ebeSHaibo Chen };
17816846ebeSHaibo Chen
imx7d_adc_feature_config(struct imx7d_adc * info)17916846ebeSHaibo Chen static void imx7d_adc_feature_config(struct imx7d_adc *info)
18016846ebeSHaibo Chen {
18116846ebeSHaibo Chen info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
18216846ebeSHaibo Chen info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
18316846ebeSHaibo Chen info->adc_feature.core_time_unit = 1;
18416846ebeSHaibo Chen }
18516846ebeSHaibo Chen
imx7d_adc_sample_rate_set(struct imx7d_adc * info)18616846ebeSHaibo Chen static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
18716846ebeSHaibo Chen {
18816846ebeSHaibo Chen struct imx7d_adc_feature *adc_feature = &info->adc_feature;
18916846ebeSHaibo Chen struct imx7d_adc_analogue_core_clk adc_analogure_clk;
19016846ebeSHaibo Chen u32 i;
19116846ebeSHaibo Chen u32 tmp_cfg1;
19216846ebeSHaibo Chen u32 sample_rate = 0;
19316846ebeSHaibo Chen
19416846ebeSHaibo Chen /*
19516846ebeSHaibo Chen * Before sample set, disable channel A,B,C,D. Here we
19616846ebeSHaibo Chen * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
19716846ebeSHaibo Chen */
19816846ebeSHaibo Chen for (i = 0; i < 4; i++) {
19916846ebeSHaibo Chen tmp_cfg1 =
20016846ebeSHaibo Chen readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
20116846ebeSHaibo Chen tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
20216846ebeSHaibo Chen writel(tmp_cfg1,
20316846ebeSHaibo Chen info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
20416846ebeSHaibo Chen }
20516846ebeSHaibo Chen
20616846ebeSHaibo Chen adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
20716846ebeSHaibo Chen sample_rate |= adc_analogure_clk.reg_config;
20816846ebeSHaibo Chen info->pre_div_num = adc_analogure_clk.pre_div;
20916846ebeSHaibo Chen
21016846ebeSHaibo Chen sample_rate |= adc_feature->core_time_unit;
21116846ebeSHaibo Chen writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
21216846ebeSHaibo Chen }
21316846ebeSHaibo Chen
imx7d_adc_hw_init(struct imx7d_adc * info)21416846ebeSHaibo Chen static void imx7d_adc_hw_init(struct imx7d_adc *info)
21516846ebeSHaibo Chen {
21616846ebeSHaibo Chen u32 cfg;
21716846ebeSHaibo Chen
21816846ebeSHaibo Chen /* power up and enable adc analogue core */
21916846ebeSHaibo Chen cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
22016846ebeSHaibo Chen cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
22116846ebeSHaibo Chen IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
22216846ebeSHaibo Chen cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
22316846ebeSHaibo Chen writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
22416846ebeSHaibo Chen
22516846ebeSHaibo Chen /* enable channel A,B,C,D interrupt */
22616846ebeSHaibo Chen writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
22716846ebeSHaibo Chen info->regs + IMX7D_REG_ADC_INT_SIG_EN);
22816846ebeSHaibo Chen writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
22916846ebeSHaibo Chen info->regs + IMX7D_REG_ADC_INT_EN);
23016846ebeSHaibo Chen
23116846ebeSHaibo Chen imx7d_adc_sample_rate_set(info);
23216846ebeSHaibo Chen }
23316846ebeSHaibo Chen
imx7d_adc_channel_set(struct imx7d_adc * info)23416846ebeSHaibo Chen static void imx7d_adc_channel_set(struct imx7d_adc *info)
23516846ebeSHaibo Chen {
23616846ebeSHaibo Chen u32 cfg1 = 0;
23716846ebeSHaibo Chen u32 cfg2;
23816846ebeSHaibo Chen u32 channel;
23916846ebeSHaibo Chen
24016846ebeSHaibo Chen channel = info->channel;
24116846ebeSHaibo Chen
24216846ebeSHaibo Chen /* the channel choose single conversion, and enable average mode */
24316846ebeSHaibo Chen cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
2449f3bf94fSFabio Estevam IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
2459f3bf94fSFabio Estevam IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
24616846ebeSHaibo Chen
24716846ebeSHaibo Chen /*
24816846ebeSHaibo Chen * physical channel 0 chose logical channel A
24916846ebeSHaibo Chen * physical channel 1 chose logical channel B
25016846ebeSHaibo Chen * physical channel 2 chose logical channel C
25116846ebeSHaibo Chen * physical channel 3 chose logical channel D
25216846ebeSHaibo Chen */
25316846ebeSHaibo Chen cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
25416846ebeSHaibo Chen
25516846ebeSHaibo Chen /*
25616846ebeSHaibo Chen * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
25716846ebeSHaibo Chen * channel chosen
25816846ebeSHaibo Chen */
25916846ebeSHaibo Chen cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
26016846ebeSHaibo Chen IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
26116846ebeSHaibo Chen
26216846ebeSHaibo Chen cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
26316846ebeSHaibo Chen
26416846ebeSHaibo Chen /*
26516846ebeSHaibo Chen * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
26616846ebeSHaibo Chen * the channel chosen
26716846ebeSHaibo Chen */
26816846ebeSHaibo Chen writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
26916846ebeSHaibo Chen IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
27016846ebeSHaibo Chen writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);
27116846ebeSHaibo Chen }
27216846ebeSHaibo Chen
imx7d_adc_get_sample_rate(struct imx7d_adc * info)27316846ebeSHaibo Chen static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
27416846ebeSHaibo Chen {
27516846ebeSHaibo Chen u32 analogue_core_clk;
27616846ebeSHaibo Chen u32 core_time_unit = info->adc_feature.core_time_unit;
27716846ebeSHaibo Chen u32 tmp;
27816846ebeSHaibo Chen
2799ce92da8SFabio Estevam analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
28016846ebeSHaibo Chen tmp = (core_time_unit + 1) * 6;
28116846ebeSHaibo Chen
28216846ebeSHaibo Chen return analogue_core_clk / tmp;
28316846ebeSHaibo Chen }
28416846ebeSHaibo Chen
imx7d_adc_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)28516846ebeSHaibo Chen static int imx7d_adc_read_raw(struct iio_dev *indio_dev,
28616846ebeSHaibo Chen struct iio_chan_spec const *chan,
28716846ebeSHaibo Chen int *val,
28816846ebeSHaibo Chen int *val2,
28916846ebeSHaibo Chen long mask)
29016846ebeSHaibo Chen {
29116846ebeSHaibo Chen struct imx7d_adc *info = iio_priv(indio_dev);
29216846ebeSHaibo Chen
29316846ebeSHaibo Chen u32 channel;
29416846ebeSHaibo Chen long ret;
29516846ebeSHaibo Chen
29616846ebeSHaibo Chen switch (mask) {
29716846ebeSHaibo Chen case IIO_CHAN_INFO_RAW:
2987dde7ec2SNuno Sá mutex_lock(&info->lock);
29916846ebeSHaibo Chen reinit_completion(&info->completion);
30016846ebeSHaibo Chen
30116846ebeSHaibo Chen channel = chan->channel & 0x03;
30216846ebeSHaibo Chen info->channel = channel;
30316846ebeSHaibo Chen imx7d_adc_channel_set(info);
30416846ebeSHaibo Chen
30516846ebeSHaibo Chen ret = wait_for_completion_interruptible_timeout
30616846ebeSHaibo Chen (&info->completion, IMX7D_ADC_TIMEOUT);
30716846ebeSHaibo Chen if (ret == 0) {
3087dde7ec2SNuno Sá mutex_unlock(&info->lock);
30916846ebeSHaibo Chen return -ETIMEDOUT;
31016846ebeSHaibo Chen }
31116846ebeSHaibo Chen if (ret < 0) {
3127dde7ec2SNuno Sá mutex_unlock(&info->lock);
31316846ebeSHaibo Chen return ret;
31416846ebeSHaibo Chen }
31516846ebeSHaibo Chen
31616846ebeSHaibo Chen *val = info->value;
3177dde7ec2SNuno Sá mutex_unlock(&info->lock);
31816846ebeSHaibo Chen return IIO_VAL_INT;
31916846ebeSHaibo Chen
32016846ebeSHaibo Chen case IIO_CHAN_INFO_SCALE:
32116846ebeSHaibo Chen info->vref_uv = regulator_get_voltage(info->vref);
32216846ebeSHaibo Chen *val = info->vref_uv / 1000;
32316846ebeSHaibo Chen *val2 = 12;
32416846ebeSHaibo Chen return IIO_VAL_FRACTIONAL_LOG2;
32516846ebeSHaibo Chen
32616846ebeSHaibo Chen case IIO_CHAN_INFO_SAMP_FREQ:
32716846ebeSHaibo Chen *val = imx7d_adc_get_sample_rate(info);
32816846ebeSHaibo Chen return IIO_VAL_INT;
32916846ebeSHaibo Chen
33016846ebeSHaibo Chen default:
33116846ebeSHaibo Chen return -EINVAL;
33216846ebeSHaibo Chen }
33316846ebeSHaibo Chen }
33416846ebeSHaibo Chen
imx7d_adc_read_data(struct imx7d_adc * info)33516846ebeSHaibo Chen static int imx7d_adc_read_data(struct imx7d_adc *info)
33616846ebeSHaibo Chen {
33716846ebeSHaibo Chen u32 channel;
33816846ebeSHaibo Chen u32 value;
33916846ebeSHaibo Chen
34016846ebeSHaibo Chen channel = info->channel & 0x03;
34116846ebeSHaibo Chen
34216846ebeSHaibo Chen /*
34316846ebeSHaibo Chen * channel A and B conversion result share one register,
34416846ebeSHaibo Chen * bit[27~16] is the channel B conversion result,
34516846ebeSHaibo Chen * bit[11~0] is the channel A conversion result.
34616846ebeSHaibo Chen * channel C and D is the same.
34716846ebeSHaibo Chen */
34816846ebeSHaibo Chen if (channel < 2)
34916846ebeSHaibo Chen value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
35016846ebeSHaibo Chen else
35116846ebeSHaibo Chen value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
35216846ebeSHaibo Chen if (channel & 0x1) /* channel B or D */
35316846ebeSHaibo Chen value = (value >> 16) & 0xFFF;
35416846ebeSHaibo Chen else /* channel A or C */
35516846ebeSHaibo Chen value &= 0xFFF;
35616846ebeSHaibo Chen
35716846ebeSHaibo Chen return value;
35816846ebeSHaibo Chen }
35916846ebeSHaibo Chen
imx7d_adc_isr(int irq,void * dev_id)36016846ebeSHaibo Chen static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
36116846ebeSHaibo Chen {
3620b568b3cSsimran singhal struct imx7d_adc *info = dev_id;
36316846ebeSHaibo Chen int status;
36416846ebeSHaibo Chen
36516846ebeSHaibo Chen status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
36616846ebeSHaibo Chen if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
36716846ebeSHaibo Chen info->value = imx7d_adc_read_data(info);
36816846ebeSHaibo Chen complete(&info->completion);
36916846ebeSHaibo Chen
37016846ebeSHaibo Chen /*
37116846ebeSHaibo Chen * The register IMX7D_REG_ADC_INT_STATUS can't clear
37216846ebeSHaibo Chen * itself after read operation, need software to write
37316846ebeSHaibo Chen * 0 to the related bit. Here we clear the channel A/B/C/D
37416846ebeSHaibo Chen * conversion finished flag.
37516846ebeSHaibo Chen */
37616846ebeSHaibo Chen status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
37716846ebeSHaibo Chen writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
37816846ebeSHaibo Chen }
37916846ebeSHaibo Chen
38016846ebeSHaibo Chen /*
38116846ebeSHaibo Chen * If the channel A/B/C/D conversion timeout, report it and clear these
38216846ebeSHaibo Chen * timeout flags.
38316846ebeSHaibo Chen */
38416846ebeSHaibo Chen if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
3858cc393bfSAndrey Smirnov dev_err(info->dev,
3868cc393bfSAndrey Smirnov "ADC got conversion time out interrupt: 0x%08x\n",
3878cc393bfSAndrey Smirnov status);
38816846ebeSHaibo Chen status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
38916846ebeSHaibo Chen writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
39016846ebeSHaibo Chen }
39116846ebeSHaibo Chen
39216846ebeSHaibo Chen return IRQ_HANDLED;
39316846ebeSHaibo Chen }
39416846ebeSHaibo Chen
imx7d_adc_reg_access(struct iio_dev * indio_dev,unsigned reg,unsigned writeval,unsigned * readval)39516846ebeSHaibo Chen static int imx7d_adc_reg_access(struct iio_dev *indio_dev,
39616846ebeSHaibo Chen unsigned reg, unsigned writeval,
39716846ebeSHaibo Chen unsigned *readval)
39816846ebeSHaibo Chen {
39916846ebeSHaibo Chen struct imx7d_adc *info = iio_priv(indio_dev);
40016846ebeSHaibo Chen
40116846ebeSHaibo Chen if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)
40216846ebeSHaibo Chen return -EINVAL;
40316846ebeSHaibo Chen
40416846ebeSHaibo Chen *readval = readl(info->regs + reg);
40516846ebeSHaibo Chen
40616846ebeSHaibo Chen return 0;
40716846ebeSHaibo Chen }
40816846ebeSHaibo Chen
40916846ebeSHaibo Chen static const struct iio_info imx7d_adc_iio_info = {
41016846ebeSHaibo Chen .read_raw = &imx7d_adc_read_raw,
41116846ebeSHaibo Chen .debugfs_reg_access = &imx7d_adc_reg_access,
41216846ebeSHaibo Chen };
41316846ebeSHaibo Chen
41416846ebeSHaibo Chen static const struct of_device_id imx7d_adc_match[] = {
41516846ebeSHaibo Chen { .compatible = "fsl,imx7d-adc", },
41616846ebeSHaibo Chen { /* sentinel */ }
41716846ebeSHaibo Chen };
41816846ebeSHaibo Chen MODULE_DEVICE_TABLE(of, imx7d_adc_match);
41916846ebeSHaibo Chen
imx7d_adc_power_down(struct imx7d_adc * info)42016846ebeSHaibo Chen static void imx7d_adc_power_down(struct imx7d_adc *info)
42116846ebeSHaibo Chen {
42216846ebeSHaibo Chen u32 adc_cfg;
42316846ebeSHaibo Chen
42416846ebeSHaibo Chen adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
42516846ebeSHaibo Chen adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
42616846ebeSHaibo Chen IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
42716846ebeSHaibo Chen adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
42816846ebeSHaibo Chen writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
42916846ebeSHaibo Chen }
43016846ebeSHaibo Chen
imx7d_adc_enable(struct device * dev)4314fe86cdfSAndrey Smirnov static int imx7d_adc_enable(struct device *dev)
4324fe86cdfSAndrey Smirnov {
4334fe86cdfSAndrey Smirnov struct iio_dev *indio_dev = dev_get_drvdata(dev);
4344fe86cdfSAndrey Smirnov struct imx7d_adc *info = iio_priv(indio_dev);
4354fe86cdfSAndrey Smirnov int ret;
4364fe86cdfSAndrey Smirnov
4374fe86cdfSAndrey Smirnov ret = regulator_enable(info->vref);
4384fe86cdfSAndrey Smirnov if (ret) {
4394fe86cdfSAndrey Smirnov dev_err(info->dev,
4404fe86cdfSAndrey Smirnov "Can't enable adc reference top voltage, err = %d\n",
4414fe86cdfSAndrey Smirnov ret);
4424fe86cdfSAndrey Smirnov return ret;
4434fe86cdfSAndrey Smirnov }
4444fe86cdfSAndrey Smirnov
4454fe86cdfSAndrey Smirnov ret = clk_prepare_enable(info->clk);
4464fe86cdfSAndrey Smirnov if (ret) {
4474fe86cdfSAndrey Smirnov dev_err(info->dev,
4484fe86cdfSAndrey Smirnov "Could not prepare or enable clock.\n");
4494fe86cdfSAndrey Smirnov regulator_disable(info->vref);
4504fe86cdfSAndrey Smirnov return ret;
4514fe86cdfSAndrey Smirnov }
4524fe86cdfSAndrey Smirnov
4534fe86cdfSAndrey Smirnov imx7d_adc_hw_init(info);
4544fe86cdfSAndrey Smirnov
4554fe86cdfSAndrey Smirnov return 0;
4564fe86cdfSAndrey Smirnov }
4574fe86cdfSAndrey Smirnov
imx7d_adc_disable(struct device * dev)458b8466760SAndrey Smirnov static int imx7d_adc_disable(struct device *dev)
459b8466760SAndrey Smirnov {
460b8466760SAndrey Smirnov struct iio_dev *indio_dev = dev_get_drvdata(dev);
461b8466760SAndrey Smirnov struct imx7d_adc *info = iio_priv(indio_dev);
462b8466760SAndrey Smirnov
463b8466760SAndrey Smirnov imx7d_adc_power_down(info);
464b8466760SAndrey Smirnov
465b8466760SAndrey Smirnov clk_disable_unprepare(info->clk);
466b8466760SAndrey Smirnov regulator_disable(info->vref);
467b8466760SAndrey Smirnov
468b8466760SAndrey Smirnov return 0;
469b8466760SAndrey Smirnov }
470b8466760SAndrey Smirnov
__imx7d_adc_disable(void * data)471b8466760SAndrey Smirnov static void __imx7d_adc_disable(void *data)
472b8466760SAndrey Smirnov {
473b8466760SAndrey Smirnov imx7d_adc_disable(data);
474b8466760SAndrey Smirnov }
475b8466760SAndrey Smirnov
imx7d_adc_probe(struct platform_device * pdev)47616846ebeSHaibo Chen static int imx7d_adc_probe(struct platform_device *pdev)
47716846ebeSHaibo Chen {
47816846ebeSHaibo Chen struct imx7d_adc *info;
47916846ebeSHaibo Chen struct iio_dev *indio_dev;
480aa21a28eSAndrey Smirnov struct device *dev = &pdev->dev;
48116846ebeSHaibo Chen int irq;
48216846ebeSHaibo Chen int ret;
48316846ebeSHaibo Chen
484aa21a28eSAndrey Smirnov indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
48516846ebeSHaibo Chen if (!indio_dev) {
48616846ebeSHaibo Chen dev_err(&pdev->dev, "Failed allocating iio device\n");
48716846ebeSHaibo Chen return -ENOMEM;
48816846ebeSHaibo Chen }
48916846ebeSHaibo Chen
49016846ebeSHaibo Chen info = iio_priv(indio_dev);
491aa21a28eSAndrey Smirnov info->dev = dev;
49216846ebeSHaibo Chen
493a8427a7bSAndrey Smirnov info->regs = devm_platform_ioremap_resource(pdev, 0);
494fe931164SFabio Estevam if (IS_ERR(info->regs))
495fe931164SFabio Estevam return PTR_ERR(info->regs);
49616846ebeSHaibo Chen
49716846ebeSHaibo Chen irq = platform_get_irq(pdev, 0);
4987c279229SStephen Boyd if (irq < 0)
499*089c1e11SRuan Jinjie return irq;
50016846ebeSHaibo Chen
501aa21a28eSAndrey Smirnov info->clk = devm_clk_get(dev, "adc");
5021c17abbcSCai Huoqing if (IS_ERR(info->clk))
5031c17abbcSCai Huoqing return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n");
50416846ebeSHaibo Chen
505aa21a28eSAndrey Smirnov info->vref = devm_regulator_get(dev, "vref");
5061c17abbcSCai Huoqing if (IS_ERR(info->vref))
5071c17abbcSCai Huoqing return dev_err_probe(dev, PTR_ERR(info->vref),
5081c17abbcSCai Huoqing "Failed getting reference voltage\n");
50916846ebeSHaibo Chen
51016846ebeSHaibo Chen platform_set_drvdata(pdev, indio_dev);
51116846ebeSHaibo Chen
51216846ebeSHaibo Chen init_completion(&info->completion);
51316846ebeSHaibo Chen
514aa21a28eSAndrey Smirnov indio_dev->name = dev_name(dev);
51516846ebeSHaibo Chen indio_dev->info = &imx7d_adc_iio_info;
51616846ebeSHaibo Chen indio_dev->modes = INDIO_DIRECT_MODE;
51716846ebeSHaibo Chen indio_dev->channels = imx7d_adc_iio_channels;
51816846ebeSHaibo Chen indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
51916846ebeSHaibo Chen
520f5d2f9c2SFabio Estevam ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info);
52116846ebeSHaibo Chen if (ret < 0) {
522aa21a28eSAndrey Smirnov dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
5234fe86cdfSAndrey Smirnov return ret;
52416846ebeSHaibo Chen }
52516846ebeSHaibo Chen
52616846ebeSHaibo Chen imx7d_adc_feature_config(info);
5274fe86cdfSAndrey Smirnov
528ba1287e7SLars-Peter Clausen ret = imx7d_adc_enable(dev);
5294fe86cdfSAndrey Smirnov if (ret)
5304fe86cdfSAndrey Smirnov return ret;
53116846ebeSHaibo Chen
532ba1287e7SLars-Peter Clausen ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, dev);
533b8466760SAndrey Smirnov if (ret)
534b8466760SAndrey Smirnov return ret;
535b8466760SAndrey Smirnov
5367dde7ec2SNuno Sá mutex_init(&info->lock);
5377dde7ec2SNuno Sá
5389c612694SAndrey Smirnov ret = devm_iio_device_register(dev, indio_dev);
53916846ebeSHaibo Chen if (ret) {
54016846ebeSHaibo Chen dev_err(&pdev->dev, "Couldn't register the device.\n");
5414fe86cdfSAndrey Smirnov return ret;
54216846ebeSHaibo Chen }
54316846ebeSHaibo Chen
54416846ebeSHaibo Chen return 0;
54516846ebeSHaibo Chen }
54616846ebeSHaibo Chen
547cdb77810SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable,
548cdb77810SJonathan Cameron imx7d_adc_enable);
54916846ebeSHaibo Chen
55016846ebeSHaibo Chen static struct platform_driver imx7d_adc_driver = {
55116846ebeSHaibo Chen .probe = imx7d_adc_probe,
55216846ebeSHaibo Chen .driver = {
55316846ebeSHaibo Chen .name = "imx7d_adc",
55416846ebeSHaibo Chen .of_match_table = imx7d_adc_match,
555cdb77810SJonathan Cameron .pm = pm_sleep_ptr(&imx7d_adc_pm_ops),
55616846ebeSHaibo Chen },
55716846ebeSHaibo Chen };
55816846ebeSHaibo Chen
55916846ebeSHaibo Chen module_platform_driver(imx7d_adc_driver);
56016846ebeSHaibo Chen
56116846ebeSHaibo Chen MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
562dd63b4faSColin Ian King MODULE_DESCRIPTION("Freescale IMX7D ADC driver");
56316846ebeSHaibo Chen MODULE_LICENSE("GPL v2");
564