1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
244d6f2efSHeiko Stübner /*
344d6f2efSHeiko Stübner * Rockchip Successive Approximation Register (SAR) A/D Converter
444d6f2efSHeiko Stübner * Copyright (C) 2014 ROCKCHIP, Inc.
544d6f2efSHeiko Stübner */
644d6f2efSHeiko Stübner
7757953f8SSimon Xue #include <linux/bitfield.h>
844d6f2efSHeiko Stübner #include <linux/module.h>
9bb690935SNuno Sá #include <linux/mutex.h>
1044d6f2efSHeiko Stübner #include <linux/platform_device.h>
1144d6f2efSHeiko Stübner #include <linux/interrupt.h>
1244d6f2efSHeiko Stübner #include <linux/io.h>
1344d6f2efSHeiko Stübner #include <linux/of.h>
1444d6f2efSHeiko Stübner #include <linux/clk.h>
1544d6f2efSHeiko Stübner #include <linux/completion.h>
16543852afSCaesar Wang #include <linux/delay.h>
17543852afSCaesar Wang #include <linux/reset.h>
1844d6f2efSHeiko Stübner #include <linux/regulator/consumer.h>
194e130dc7SSimon Xue #include <linux/iio/buffer.h>
2044d6f2efSHeiko Stübner #include <linux/iio/iio.h>
214e130dc7SSimon Xue #include <linux/iio/trigger_consumer.h>
224e130dc7SSimon Xue #include <linux/iio/triggered_buffer.h>
2344d6f2efSHeiko Stübner
2444d6f2efSHeiko Stübner #define SARADC_DATA 0x00
2544d6f2efSHeiko Stübner
2644d6f2efSHeiko Stübner #define SARADC_STAS 0x04
2744d6f2efSHeiko Stübner #define SARADC_STAS_BUSY BIT(0)
2844d6f2efSHeiko Stübner
2944d6f2efSHeiko Stübner #define SARADC_CTRL 0x08
3044d6f2efSHeiko Stübner #define SARADC_CTRL_IRQ_STATUS BIT(6)
3144d6f2efSHeiko Stübner #define SARADC_CTRL_IRQ_ENABLE BIT(5)
3244d6f2efSHeiko Stübner #define SARADC_CTRL_POWER_CTRL BIT(3)
3344d6f2efSHeiko Stübner #define SARADC_CTRL_CHN_MASK 0x7
3444d6f2efSHeiko Stübner
3544d6f2efSHeiko Stübner #define SARADC_DLY_PU_SOC 0x0c
3644d6f2efSHeiko Stübner #define SARADC_DLY_PU_SOC_MASK 0x3f
3744d6f2efSHeiko Stübner
3844d6f2efSHeiko Stübner #define SARADC_TIMEOUT msecs_to_jiffies(100)
397786da3bSSimon Xue #define SARADC_MAX_CHANNELS 8
4044d6f2efSHeiko Stübner
41757953f8SSimon Xue /* v2 registers */
42757953f8SSimon Xue #define SARADC2_CONV_CON 0x000
43757953f8SSimon Xue #define SARADC_T_PD_SOC 0x004
44757953f8SSimon Xue #define SARADC_T_DAS_SOC 0x00c
45757953f8SSimon Xue #define SARADC2_END_INT_EN 0x104
46757953f8SSimon Xue #define SARADC2_ST_CON 0x108
47757953f8SSimon Xue #define SARADC2_STATUS 0x10c
48757953f8SSimon Xue #define SARADC2_END_INT_ST 0x110
49757953f8SSimon Xue #define SARADC2_DATA_BASE 0x120
50757953f8SSimon Xue
51757953f8SSimon Xue #define SARADC2_EN_END_INT BIT(0)
52757953f8SSimon Xue #define SARADC2_START BIT(4)
53757953f8SSimon Xue #define SARADC2_SINGLE_MODE BIT(5)
54757953f8SSimon Xue
55b0a4546dSQuentin Schulz #define SARADC2_CONV_CHANNELS GENMASK(3, 0)
56757953f8SSimon Xue
57fb1c13d5SSimon Xue struct rockchip_saradc;
58fb1c13d5SSimon Xue
594c21bbb4SHeiko Stübner struct rockchip_saradc_data {
604c21bbb4SHeiko Stübner const struct iio_chan_spec *channels;
614c21bbb4SHeiko Stübner int num_channels;
624c21bbb4SHeiko Stübner unsigned long clk_rate;
63fb1c13d5SSimon Xue void (*start)(struct rockchip_saradc *info, int chn);
64fb1c13d5SSimon Xue int (*read)(struct rockchip_saradc *info);
65fb1c13d5SSimon Xue void (*power_down)(struct rockchip_saradc *info);
664c21bbb4SHeiko Stübner };
674c21bbb4SHeiko Stübner
6844d6f2efSHeiko Stübner struct rockchip_saradc {
6944d6f2efSHeiko Stübner void __iomem *regs;
7044d6f2efSHeiko Stübner struct clk *pclk;
7144d6f2efSHeiko Stübner struct clk *clk;
7244d6f2efSHeiko Stübner struct completion completion;
7344d6f2efSHeiko Stübner struct regulator *vref;
74bb690935SNuno Sá /* lock to protect against multiple access to the device */
75bb690935SNuno Sá struct mutex lock;
76cabd6e9cSDavid Wu int uv_vref;
77543852afSCaesar Wang struct reset_control *reset;
784c21bbb4SHeiko Stübner const struct rockchip_saradc_data *data;
7944d6f2efSHeiko Stübner u16 last_val;
804e130dc7SSimon Xue const struct iio_chan_spec *last_chan;
81cabd6e9cSDavid Wu struct notifier_block nb;
8244d6f2efSHeiko Stübner };
8344d6f2efSHeiko Stübner
84fb1c13d5SSimon Xue static void rockchip_saradc_reset_controller(struct reset_control *reset);
85fb1c13d5SSimon Xue
rockchip_saradc_start_v1(struct rockchip_saradc * info,int chn)86fb1c13d5SSimon Xue static void rockchip_saradc_start_v1(struct rockchip_saradc *info, int chn)
87fb1c13d5SSimon Xue {
88fb1c13d5SSimon Xue /* 8 clock periods as delay between power up and start cmd */
89fb1c13d5SSimon Xue writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);
90fb1c13d5SSimon Xue /* Select the channel to be used and trigger conversion */
91fb1c13d5SSimon Xue writel(SARADC_CTRL_POWER_CTRL | (chn & SARADC_CTRL_CHN_MASK) |
92fb1c13d5SSimon Xue SARADC_CTRL_IRQ_ENABLE, info->regs + SARADC_CTRL);
93fb1c13d5SSimon Xue }
94fb1c13d5SSimon Xue
rockchip_saradc_start_v2(struct rockchip_saradc * info,int chn)95757953f8SSimon Xue static void rockchip_saradc_start_v2(struct rockchip_saradc *info, int chn)
96757953f8SSimon Xue {
97757953f8SSimon Xue int val;
98757953f8SSimon Xue
99757953f8SSimon Xue if (info->reset)
100757953f8SSimon Xue rockchip_saradc_reset_controller(info->reset);
101757953f8SSimon Xue
102757953f8SSimon Xue writel_relaxed(0xc, info->regs + SARADC_T_DAS_SOC);
103757953f8SSimon Xue writel_relaxed(0x20, info->regs + SARADC_T_PD_SOC);
104757953f8SSimon Xue val = FIELD_PREP(SARADC2_EN_END_INT, 1);
1055b4e4b72SQuentin Schulz val |= SARADC2_EN_END_INT << 16;
106757953f8SSimon Xue writel_relaxed(val, info->regs + SARADC2_END_INT_EN);
107757953f8SSimon Xue val = FIELD_PREP(SARADC2_START, 1) |
108757953f8SSimon Xue FIELD_PREP(SARADC2_SINGLE_MODE, 1) |
109757953f8SSimon Xue FIELD_PREP(SARADC2_CONV_CHANNELS, chn);
1105b4e4b72SQuentin Schulz val |= (SARADC2_START | SARADC2_SINGLE_MODE | SARADC2_CONV_CHANNELS) << 16;
111757953f8SSimon Xue writel(val, info->regs + SARADC2_CONV_CON);
112757953f8SSimon Xue }
113757953f8SSimon Xue
rockchip_saradc_start(struct rockchip_saradc * info,int chn)114fb1c13d5SSimon Xue static void rockchip_saradc_start(struct rockchip_saradc *info, int chn)
115fb1c13d5SSimon Xue {
116fb1c13d5SSimon Xue info->data->start(info, chn);
117fb1c13d5SSimon Xue }
118fb1c13d5SSimon Xue
rockchip_saradc_read_v1(struct rockchip_saradc * info)119fb1c13d5SSimon Xue static int rockchip_saradc_read_v1(struct rockchip_saradc *info)
120fb1c13d5SSimon Xue {
121fb1c13d5SSimon Xue return readl_relaxed(info->regs + SARADC_DATA);
122fb1c13d5SSimon Xue }
123fb1c13d5SSimon Xue
rockchip_saradc_read_v2(struct rockchip_saradc * info)124757953f8SSimon Xue static int rockchip_saradc_read_v2(struct rockchip_saradc *info)
125757953f8SSimon Xue {
126757953f8SSimon Xue int offset;
127757953f8SSimon Xue
128757953f8SSimon Xue /* Clear irq */
129757953f8SSimon Xue writel_relaxed(0x1, info->regs + SARADC2_END_INT_ST);
130757953f8SSimon Xue
131757953f8SSimon Xue offset = SARADC2_DATA_BASE + info->last_chan->channel * 0x4;
132757953f8SSimon Xue
133757953f8SSimon Xue return readl_relaxed(info->regs + offset);
134757953f8SSimon Xue }
135757953f8SSimon Xue
rockchip_saradc_read(struct rockchip_saradc * info)136fb1c13d5SSimon Xue static int rockchip_saradc_read(struct rockchip_saradc *info)
137fb1c13d5SSimon Xue {
138fb1c13d5SSimon Xue return info->data->read(info);
139fb1c13d5SSimon Xue }
140fb1c13d5SSimon Xue
rockchip_saradc_power_down_v1(struct rockchip_saradc * info)141fb1c13d5SSimon Xue static void rockchip_saradc_power_down_v1(struct rockchip_saradc *info)
142fb1c13d5SSimon Xue {
143fb1c13d5SSimon Xue writel_relaxed(0, info->regs + SARADC_CTRL);
144fb1c13d5SSimon Xue }
145fb1c13d5SSimon Xue
rockchip_saradc_power_down(struct rockchip_saradc * info)1464e130dc7SSimon Xue static void rockchip_saradc_power_down(struct rockchip_saradc *info)
1474e130dc7SSimon Xue {
148fb1c13d5SSimon Xue if (info->data->power_down)
149fb1c13d5SSimon Xue info->data->power_down(info);
1504e130dc7SSimon Xue }
1514e130dc7SSimon Xue
rockchip_saradc_conversion(struct rockchip_saradc * info,struct iio_chan_spec const * chan)1524e130dc7SSimon Xue static int rockchip_saradc_conversion(struct rockchip_saradc *info,
1534e130dc7SSimon Xue struct iio_chan_spec const *chan)
1544e130dc7SSimon Xue {
1554e130dc7SSimon Xue reinit_completion(&info->completion);
1564e130dc7SSimon Xue
1574e130dc7SSimon Xue info->last_chan = chan;
158fb1c13d5SSimon Xue rockchip_saradc_start(info, chan->channel);
1594e130dc7SSimon Xue
1604e130dc7SSimon Xue if (!wait_for_completion_timeout(&info->completion, SARADC_TIMEOUT))
1614e130dc7SSimon Xue return -ETIMEDOUT;
1624e130dc7SSimon Xue
1634e130dc7SSimon Xue return 0;
1644e130dc7SSimon Xue }
1654e130dc7SSimon Xue
rockchip_saradc_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)16644d6f2efSHeiko Stübner static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
16744d6f2efSHeiko Stübner struct iio_chan_spec const *chan,
16844d6f2efSHeiko Stübner int *val, int *val2, long mask)
16944d6f2efSHeiko Stübner {
17044d6f2efSHeiko Stübner struct rockchip_saradc *info = iio_priv(indio_dev);
17144d6f2efSHeiko Stübner int ret;
17244d6f2efSHeiko Stübner
17344d6f2efSHeiko Stübner switch (mask) {
17444d6f2efSHeiko Stübner case IIO_CHAN_INFO_RAW:
175bb690935SNuno Sá mutex_lock(&info->lock);
17644d6f2efSHeiko Stübner
1774e130dc7SSimon Xue ret = rockchip_saradc_conversion(info, chan);
1784e130dc7SSimon Xue if (ret) {
1794e130dc7SSimon Xue rockchip_saradc_power_down(info);
180bb690935SNuno Sá mutex_unlock(&info->lock);
1814e130dc7SSimon Xue return ret;
18244d6f2efSHeiko Stübner }
18344d6f2efSHeiko Stübner
18444d6f2efSHeiko Stübner *val = info->last_val;
185bb690935SNuno Sá mutex_unlock(&info->lock);
18644d6f2efSHeiko Stübner return IIO_VAL_INT;
18744d6f2efSHeiko Stübner case IIO_CHAN_INFO_SCALE:
188cabd6e9cSDavid Wu *val = info->uv_vref / 1000;
1894e130dc7SSimon Xue *val2 = chan->scan_type.realbits;
19044d6f2efSHeiko Stübner return IIO_VAL_FRACTIONAL_LOG2;
19144d6f2efSHeiko Stübner default:
19244d6f2efSHeiko Stübner return -EINVAL;
19344d6f2efSHeiko Stübner }
19444d6f2efSHeiko Stübner }
19544d6f2efSHeiko Stübner
rockchip_saradc_isr(int irq,void * dev_id)19644d6f2efSHeiko Stübner static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
19744d6f2efSHeiko Stübner {
1980b568b3cSsimran singhal struct rockchip_saradc *info = dev_id;
19944d6f2efSHeiko Stübner
20044d6f2efSHeiko Stübner /* Read value */
201fb1c13d5SSimon Xue info->last_val = rockchip_saradc_read(info);
2024e130dc7SSimon Xue info->last_val &= GENMASK(info->last_chan->scan_type.realbits - 1, 0);
20344d6f2efSHeiko Stübner
2044e130dc7SSimon Xue rockchip_saradc_power_down(info);
20544d6f2efSHeiko Stübner
20644d6f2efSHeiko Stübner complete(&info->completion);
20744d6f2efSHeiko Stübner
20844d6f2efSHeiko Stübner return IRQ_HANDLED;
20944d6f2efSHeiko Stübner }
21044d6f2efSHeiko Stübner
21144d6f2efSHeiko Stübner static const struct iio_info rockchip_saradc_iio_info = {
21244d6f2efSHeiko Stübner .read_raw = rockchip_saradc_read_raw,
21344d6f2efSHeiko Stübner };
21444d6f2efSHeiko Stübner
2154e130dc7SSimon Xue #define SARADC_CHANNEL(_index, _id, _res) { \
21644d6f2efSHeiko Stübner .type = IIO_VOLTAGE, \
21744d6f2efSHeiko Stübner .indexed = 1, \
21844d6f2efSHeiko Stübner .channel = _index, \
21944d6f2efSHeiko Stübner .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
22044d6f2efSHeiko Stübner .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
22144d6f2efSHeiko Stübner .datasheet_name = _id, \
2224e130dc7SSimon Xue .scan_index = _index, \
2234e130dc7SSimon Xue .scan_type = { \
2244e130dc7SSimon Xue .sign = 'u', \
2254e130dc7SSimon Xue .realbits = _res, \
2264e130dc7SSimon Xue .storagebits = 16, \
2274e130dc7SSimon Xue .endianness = IIO_CPU, \
2284e130dc7SSimon Xue }, \
22944d6f2efSHeiko Stübner }
23044d6f2efSHeiko Stübner
23144d6f2efSHeiko Stübner static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
2324e130dc7SSimon Xue SARADC_CHANNEL(0, "adc0", 10),
2334e130dc7SSimon Xue SARADC_CHANNEL(1, "adc1", 10),
2344e130dc7SSimon Xue SARADC_CHANNEL(2, "adc2", 10),
23544d6f2efSHeiko Stübner };
23644d6f2efSHeiko Stübner
2374c21bbb4SHeiko Stübner static const struct rockchip_saradc_data saradc_data = {
2384c21bbb4SHeiko Stübner .channels = rockchip_saradc_iio_channels,
2394c21bbb4SHeiko Stübner .num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),
2404c21bbb4SHeiko Stübner .clk_rate = 1000000,
241fb1c13d5SSimon Xue .start = rockchip_saradc_start_v1,
242fb1c13d5SSimon Xue .read = rockchip_saradc_read_v1,
243fb1c13d5SSimon Xue .power_down = rockchip_saradc_power_down_v1,
2444c21bbb4SHeiko Stübner };
2454c21bbb4SHeiko Stübner
2464c21bbb4SHeiko Stübner static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {
2474e130dc7SSimon Xue SARADC_CHANNEL(0, "adc0", 12),
2484e130dc7SSimon Xue SARADC_CHANNEL(1, "adc1", 12),
2494c21bbb4SHeiko Stübner };
2504c21bbb4SHeiko Stübner
2514c21bbb4SHeiko Stübner static const struct rockchip_saradc_data rk3066_tsadc_data = {
2524c21bbb4SHeiko Stübner .channels = rockchip_rk3066_tsadc_iio_channels,
2534c21bbb4SHeiko Stübner .num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),
2544c21bbb4SHeiko Stübner .clk_rate = 50000,
255fb1c13d5SSimon Xue .start = rockchip_saradc_start_v1,
256fb1c13d5SSimon Xue .read = rockchip_saradc_read_v1,
257fb1c13d5SSimon Xue .power_down = rockchip_saradc_power_down_v1,
2584c21bbb4SHeiko Stübner };
2594c21bbb4SHeiko Stübner
260ae549a72SDavid Wu static const struct iio_chan_spec rockchip_rk3399_saradc_iio_channels[] = {
2614e130dc7SSimon Xue SARADC_CHANNEL(0, "adc0", 10),
2624e130dc7SSimon Xue SARADC_CHANNEL(1, "adc1", 10),
2634e130dc7SSimon Xue SARADC_CHANNEL(2, "adc2", 10),
2644e130dc7SSimon Xue SARADC_CHANNEL(3, "adc3", 10),
2654e130dc7SSimon Xue SARADC_CHANNEL(4, "adc4", 10),
2664e130dc7SSimon Xue SARADC_CHANNEL(5, "adc5", 10),
267ae549a72SDavid Wu };
268ae549a72SDavid Wu
269ae549a72SDavid Wu static const struct rockchip_saradc_data rk3399_saradc_data = {
270ae549a72SDavid Wu .channels = rockchip_rk3399_saradc_iio_channels,
271ae549a72SDavid Wu .num_channels = ARRAY_SIZE(rockchip_rk3399_saradc_iio_channels),
272ae549a72SDavid Wu .clk_rate = 1000000,
273fb1c13d5SSimon Xue .start = rockchip_saradc_start_v1,
274fb1c13d5SSimon Xue .read = rockchip_saradc_read_v1,
275fb1c13d5SSimon Xue .power_down = rockchip_saradc_power_down_v1,
276ae549a72SDavid Wu };
277ae549a72SDavid Wu
2787786da3bSSimon Xue static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = {
2797786da3bSSimon Xue SARADC_CHANNEL(0, "adc0", 10),
2807786da3bSSimon Xue SARADC_CHANNEL(1, "adc1", 10),
2817786da3bSSimon Xue SARADC_CHANNEL(2, "adc2", 10),
2827786da3bSSimon Xue SARADC_CHANNEL(3, "adc3", 10),
2837786da3bSSimon Xue SARADC_CHANNEL(4, "adc4", 10),
2847786da3bSSimon Xue SARADC_CHANNEL(5, "adc5", 10),
2857786da3bSSimon Xue SARADC_CHANNEL(6, "adc6", 10),
2867786da3bSSimon Xue SARADC_CHANNEL(7, "adc7", 10),
2877786da3bSSimon Xue };
2887786da3bSSimon Xue
2897786da3bSSimon Xue static const struct rockchip_saradc_data rk3568_saradc_data = {
2907786da3bSSimon Xue .channels = rockchip_rk3568_saradc_iio_channels,
2917786da3bSSimon Xue .num_channels = ARRAY_SIZE(rockchip_rk3568_saradc_iio_channels),
2927786da3bSSimon Xue .clk_rate = 1000000,
293fb1c13d5SSimon Xue .start = rockchip_saradc_start_v1,
294fb1c13d5SSimon Xue .read = rockchip_saradc_read_v1,
295fb1c13d5SSimon Xue .power_down = rockchip_saradc_power_down_v1,
2967786da3bSSimon Xue };
2977786da3bSSimon Xue
298757953f8SSimon Xue static const struct iio_chan_spec rockchip_rk3588_saradc_iio_channels[] = {
299757953f8SSimon Xue SARADC_CHANNEL(0, "adc0", 12),
300757953f8SSimon Xue SARADC_CHANNEL(1, "adc1", 12),
301757953f8SSimon Xue SARADC_CHANNEL(2, "adc2", 12),
302757953f8SSimon Xue SARADC_CHANNEL(3, "adc3", 12),
303757953f8SSimon Xue SARADC_CHANNEL(4, "adc4", 12),
304757953f8SSimon Xue SARADC_CHANNEL(5, "adc5", 12),
305757953f8SSimon Xue SARADC_CHANNEL(6, "adc6", 12),
306757953f8SSimon Xue SARADC_CHANNEL(7, "adc7", 12),
307757953f8SSimon Xue };
308757953f8SSimon Xue
309757953f8SSimon Xue static const struct rockchip_saradc_data rk3588_saradc_data = {
310757953f8SSimon Xue .channels = rockchip_rk3588_saradc_iio_channels,
311757953f8SSimon Xue .num_channels = ARRAY_SIZE(rockchip_rk3588_saradc_iio_channels),
312757953f8SSimon Xue .clk_rate = 1000000,
313757953f8SSimon Xue .start = rockchip_saradc_start_v2,
314757953f8SSimon Xue .read = rockchip_saradc_read_v2,
315757953f8SSimon Xue };
316757953f8SSimon Xue
3174c21bbb4SHeiko Stübner static const struct of_device_id rockchip_saradc_match[] = {
3184c21bbb4SHeiko Stübner {
3194c21bbb4SHeiko Stübner .compatible = "rockchip,saradc",
3204c21bbb4SHeiko Stübner .data = &saradc_data,
3214c21bbb4SHeiko Stübner }, {
3224c21bbb4SHeiko Stübner .compatible = "rockchip,rk3066-tsadc",
3234c21bbb4SHeiko Stübner .data = &rk3066_tsadc_data,
324ae549a72SDavid Wu }, {
325ae549a72SDavid Wu .compatible = "rockchip,rk3399-saradc",
326ae549a72SDavid Wu .data = &rk3399_saradc_data,
3277786da3bSSimon Xue }, {
3287786da3bSSimon Xue .compatible = "rockchip,rk3568-saradc",
3297786da3bSSimon Xue .data = &rk3568_saradc_data,
330757953f8SSimon Xue }, {
331757953f8SSimon Xue .compatible = "rockchip,rk3588-saradc",
332757953f8SSimon Xue .data = &rk3588_saradc_data,
3334c21bbb4SHeiko Stübner },
334*09e3bdfeSJonathan Cameron { }
3354c21bbb4SHeiko Stübner };
3364c21bbb4SHeiko Stübner MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
3374c21bbb4SHeiko Stübner
3382c7c8dceSLee Jones /*
339543852afSCaesar Wang * Reset SARADC Controller.
340543852afSCaesar Wang */
rockchip_saradc_reset_controller(struct reset_control * reset)341543852afSCaesar Wang static void rockchip_saradc_reset_controller(struct reset_control *reset)
342543852afSCaesar Wang {
343543852afSCaesar Wang reset_control_assert(reset);
344543852afSCaesar Wang usleep_range(10, 20);
345543852afSCaesar Wang reset_control_deassert(reset);
346543852afSCaesar Wang }
347543852afSCaesar Wang
rockchip_saradc_regulator_disable(void * data)348d0389d4eSHeiko Stuebner static void rockchip_saradc_regulator_disable(void *data)
349d0389d4eSHeiko Stuebner {
350d0389d4eSHeiko Stuebner struct rockchip_saradc *info = data;
351d0389d4eSHeiko Stuebner
352d0389d4eSHeiko Stuebner regulator_disable(info->vref);
353d0389d4eSHeiko Stuebner }
354d0389d4eSHeiko Stuebner
rockchip_saradc_trigger_handler(int irq,void * p)3554e130dc7SSimon Xue static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p)
3564e130dc7SSimon Xue {
3574e130dc7SSimon Xue struct iio_poll_func *pf = p;
3584e130dc7SSimon Xue struct iio_dev *i_dev = pf->indio_dev;
3594e130dc7SSimon Xue struct rockchip_saradc *info = iio_priv(i_dev);
3604e130dc7SSimon Xue /*
3614e130dc7SSimon Xue * @values: each channel takes an u16 value
3624e130dc7SSimon Xue * @timestamp: will be 8-byte aligned automatically
3634e130dc7SSimon Xue */
3644e130dc7SSimon Xue struct {
3654e130dc7SSimon Xue u16 values[SARADC_MAX_CHANNELS];
3664e130dc7SSimon Xue int64_t timestamp;
3674e130dc7SSimon Xue } data;
3684e130dc7SSimon Xue int ret;
3694e130dc7SSimon Xue int i, j = 0;
3704e130dc7SSimon Xue
371bb690935SNuno Sá mutex_lock(&info->lock);
3724e130dc7SSimon Xue
37310616c3cSNuno Sa iio_for_each_active_channel(i_dev, i) {
3744e130dc7SSimon Xue const struct iio_chan_spec *chan = &i_dev->channels[i];
3754e130dc7SSimon Xue
3764e130dc7SSimon Xue ret = rockchip_saradc_conversion(info, chan);
3774e130dc7SSimon Xue if (ret) {
3784e130dc7SSimon Xue rockchip_saradc_power_down(info);
3794e130dc7SSimon Xue goto out;
3804e130dc7SSimon Xue }
3814e130dc7SSimon Xue
3824e130dc7SSimon Xue data.values[j] = info->last_val;
3834e130dc7SSimon Xue j++;
3844e130dc7SSimon Xue }
3854e130dc7SSimon Xue
3864e130dc7SSimon Xue iio_push_to_buffers_with_timestamp(i_dev, &data, iio_get_time_ns(i_dev));
3874e130dc7SSimon Xue out:
388bb690935SNuno Sá mutex_unlock(&info->lock);
3894e130dc7SSimon Xue
3904e130dc7SSimon Xue iio_trigger_notify_done(i_dev->trig);
3914e130dc7SSimon Xue
3924e130dc7SSimon Xue return IRQ_HANDLED;
3934e130dc7SSimon Xue }
3944e130dc7SSimon Xue
rockchip_saradc_volt_notify(struct notifier_block * nb,unsigned long event,void * data)395cabd6e9cSDavid Wu static int rockchip_saradc_volt_notify(struct notifier_block *nb,
39610bec68fSShreeya Patel unsigned long event, void *data)
397cabd6e9cSDavid Wu {
398cabd6e9cSDavid Wu struct rockchip_saradc *info =
399cabd6e9cSDavid Wu container_of(nb, struct rockchip_saradc, nb);
400cabd6e9cSDavid Wu
401cabd6e9cSDavid Wu if (event & REGULATOR_EVENT_VOLTAGE_CHANGE)
402cabd6e9cSDavid Wu info->uv_vref = (unsigned long)data;
403cabd6e9cSDavid Wu
404cabd6e9cSDavid Wu return NOTIFY_OK;
405cabd6e9cSDavid Wu }
406cabd6e9cSDavid Wu
rockchip_saradc_regulator_unreg_notifier(void * data)407cabd6e9cSDavid Wu static void rockchip_saradc_regulator_unreg_notifier(void *data)
408cabd6e9cSDavid Wu {
409cabd6e9cSDavid Wu struct rockchip_saradc *info = data;
410cabd6e9cSDavid Wu
411cabd6e9cSDavid Wu regulator_unregister_notifier(info->vref, &info->nb);
412cabd6e9cSDavid Wu }
413cabd6e9cSDavid Wu
rockchip_saradc_probe(struct platform_device * pdev)41444d6f2efSHeiko Stübner static int rockchip_saradc_probe(struct platform_device *pdev)
41544d6f2efSHeiko Stübner {
41647902344SShreeya Patel const struct rockchip_saradc_data *match_data;
41744d6f2efSHeiko Stübner struct rockchip_saradc *info = NULL;
41844d6f2efSHeiko Stübner struct device_node *np = pdev->dev.of_node;
41944d6f2efSHeiko Stübner struct iio_dev *indio_dev = NULL;
42044d6f2efSHeiko Stübner int ret;
42144d6f2efSHeiko Stübner int irq;
42244d6f2efSHeiko Stübner
42344d6f2efSHeiko Stübner if (!np)
42444d6f2efSHeiko Stübner return -ENODEV;
42544d6f2efSHeiko Stübner
42644d6f2efSHeiko Stübner indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
4275e63d7a2SShreeya Patel if (!indio_dev)
4285e63d7a2SShreeya Patel return dev_err_probe(&pdev->dev, -ENOMEM,
4295e63d7a2SShreeya Patel "failed allocating iio device\n");
4305e63d7a2SShreeya Patel
43144d6f2efSHeiko Stübner info = iio_priv(indio_dev);
43244d6f2efSHeiko Stübner
43347902344SShreeya Patel match_data = of_device_get_match_data(&pdev->dev);
4345e63d7a2SShreeya Patel if (!match_data)
4355e63d7a2SShreeya Patel return dev_err_probe(&pdev->dev, -ENODEV,
4365e63d7a2SShreeya Patel "failed to match device\n");
43736d311bcSGustavo A. R. Silva
43847902344SShreeya Patel info->data = match_data;
4394c21bbb4SHeiko Stübner
4404e130dc7SSimon Xue /* Sanity check for possible later IP variants with more channels */
4415e63d7a2SShreeya Patel if (info->data->num_channels > SARADC_MAX_CHANNELS)
4425e63d7a2SShreeya Patel return dev_err_probe(&pdev->dev, -EINVAL,
4435e63d7a2SShreeya Patel "max channels exceeded");
4444e130dc7SSimon Xue
445948b3b3dSCai Huoqing info->regs = devm_platform_ioremap_resource(pdev, 0);
44644d6f2efSHeiko Stübner if (IS_ERR(info->regs))
44744d6f2efSHeiko Stübner return PTR_ERR(info->regs);
44844d6f2efSHeiko Stübner
449543852afSCaesar Wang /*
450543852afSCaesar Wang * The reset should be an optional property, as it should work
451543852afSCaesar Wang * with old devicetrees as well
452543852afSCaesar Wang */
4539443c19cSQuentin Schulz info->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
45487587016SPhilipp Zabel "saradc-apb");
455543852afSCaesar Wang if (IS_ERR(info->reset)) {
456543852afSCaesar Wang ret = PTR_ERR(info->reset);
4579443c19cSQuentin Schulz return dev_err_probe(&pdev->dev, ret, "failed to get saradc-apb\n");
458543852afSCaesar Wang }
459543852afSCaesar Wang
46044d6f2efSHeiko Stübner init_completion(&info->completion);
46144d6f2efSHeiko Stübner
46244d6f2efSHeiko Stübner irq = platform_get_irq(pdev, 0);
4637c279229SStephen Boyd if (irq < 0)
464089c1e11SRuan Jinjie return irq;
46544d6f2efSHeiko Stübner
46644d6f2efSHeiko Stübner ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
46744d6f2efSHeiko Stübner 0, dev_name(&pdev->dev), info);
46844d6f2efSHeiko Stübner if (ret < 0) {
46944d6f2efSHeiko Stübner dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
47044d6f2efSHeiko Stübner return ret;
47144d6f2efSHeiko Stübner }
47244d6f2efSHeiko Stübner
47344d6f2efSHeiko Stübner info->vref = devm_regulator_get(&pdev->dev, "vref");
4748f46a93bSCai Huoqing if (IS_ERR(info->vref))
4758f46a93bSCai Huoqing return dev_err_probe(&pdev->dev, PTR_ERR(info->vref),
4768f46a93bSCai Huoqing "failed to get regulator\n");
47744d6f2efSHeiko Stübner
478543852afSCaesar Wang if (info->reset)
479543852afSCaesar Wang rockchip_saradc_reset_controller(info->reset);
480543852afSCaesar Wang
48144d6f2efSHeiko Stübner /*
4824c21bbb4SHeiko Stübner * Use a default value for the converter clock.
48344d6f2efSHeiko Stübner * This may become user-configurable in the future.
48444d6f2efSHeiko Stübner */
4854c21bbb4SHeiko Stübner ret = clk_set_rate(info->clk, info->data->clk_rate);
4865e63d7a2SShreeya Patel if (ret < 0)
4875e63d7a2SShreeya Patel return dev_err_probe(&pdev->dev, ret,
4885e63d7a2SShreeya Patel "failed to set adc clk rate\n");
48944d6f2efSHeiko Stübner
49044d6f2efSHeiko Stübner ret = regulator_enable(info->vref);
4915e63d7a2SShreeya Patel if (ret < 0)
4925e63d7a2SShreeya Patel return dev_err_probe(&pdev->dev, ret,
4935e63d7a2SShreeya Patel "failed to enable vref regulator\n");
4945e63d7a2SShreeya Patel
495d0389d4eSHeiko Stuebner ret = devm_add_action_or_reset(&pdev->dev,
496d0389d4eSHeiko Stuebner rockchip_saradc_regulator_disable, info);
4975e63d7a2SShreeya Patel if (ret)
4985e63d7a2SShreeya Patel return dev_err_probe(&pdev->dev, ret,
4995e63d7a2SShreeya Patel "failed to register devm action\n");
50044d6f2efSHeiko Stübner
501cabd6e9cSDavid Wu ret = regulator_get_voltage(info->vref);
502cabd6e9cSDavid Wu if (ret < 0)
503cabd6e9cSDavid Wu return ret;
504cabd6e9cSDavid Wu
505cabd6e9cSDavid Wu info->uv_vref = ret;
506cabd6e9cSDavid Wu
50797ad10bbSShreeya Patel info->pclk = devm_clk_get_enabled(&pdev->dev, "apb_pclk");
50897ad10bbSShreeya Patel if (IS_ERR(info->pclk))
50997ad10bbSShreeya Patel return dev_err_probe(&pdev->dev, PTR_ERR(info->pclk),
51097ad10bbSShreeya Patel "failed to get pclk\n");
51144d6f2efSHeiko Stübner
51297ad10bbSShreeya Patel info->clk = devm_clk_get_enabled(&pdev->dev, "saradc");
51397ad10bbSShreeya Patel if (IS_ERR(info->clk))
51497ad10bbSShreeya Patel return dev_err_probe(&pdev->dev, PTR_ERR(info->clk),
51597ad10bbSShreeya Patel "failed to get adc clock\n");
51644d6f2efSHeiko Stübner
51744d6f2efSHeiko Stübner platform_set_drvdata(pdev, indio_dev);
51844d6f2efSHeiko Stübner
51944d6f2efSHeiko Stübner indio_dev->name = dev_name(&pdev->dev);
52044d6f2efSHeiko Stübner indio_dev->info = &rockchip_saradc_iio_info;
52144d6f2efSHeiko Stübner indio_dev->modes = INDIO_DIRECT_MODE;
52244d6f2efSHeiko Stübner
5234c21bbb4SHeiko Stübner indio_dev->channels = info->data->channels;
5244c21bbb4SHeiko Stübner indio_dev->num_channels = info->data->num_channels;
5254e130dc7SSimon Xue ret = devm_iio_triggered_buffer_setup(&indio_dev->dev, indio_dev, NULL,
5264e130dc7SSimon Xue rockchip_saradc_trigger_handler,
5274e130dc7SSimon Xue NULL);
5284e130dc7SSimon Xue if (ret)
5294e130dc7SSimon Xue return ret;
53044d6f2efSHeiko Stübner
531cabd6e9cSDavid Wu info->nb.notifier_call = rockchip_saradc_volt_notify;
532cabd6e9cSDavid Wu ret = regulator_register_notifier(info->vref, &info->nb);
533cabd6e9cSDavid Wu if (ret)
534cabd6e9cSDavid Wu return ret;
535cabd6e9cSDavid Wu
536cabd6e9cSDavid Wu ret = devm_add_action_or_reset(&pdev->dev,
537cabd6e9cSDavid Wu rockchip_saradc_regulator_unreg_notifier,
538cabd6e9cSDavid Wu info);
539cabd6e9cSDavid Wu if (ret)
540cabd6e9cSDavid Wu return ret;
541cabd6e9cSDavid Wu
542bb690935SNuno Sá mutex_init(&info->lock);
543bb690935SNuno Sá
544d0389d4eSHeiko Stuebner return devm_iio_device_register(&pdev->dev, indio_dev);
54544d6f2efSHeiko Stübner }
54644d6f2efSHeiko Stübner
rockchip_saradc_suspend(struct device * dev)54744d6f2efSHeiko Stübner static int rockchip_saradc_suspend(struct device *dev)
54844d6f2efSHeiko Stübner {
54944d6f2efSHeiko Stübner struct iio_dev *indio_dev = dev_get_drvdata(dev);
55044d6f2efSHeiko Stübner struct rockchip_saradc *info = iio_priv(indio_dev);
55144d6f2efSHeiko Stübner
55244d6f2efSHeiko Stübner clk_disable_unprepare(info->clk);
55344d6f2efSHeiko Stübner clk_disable_unprepare(info->pclk);
55444d6f2efSHeiko Stübner regulator_disable(info->vref);
55544d6f2efSHeiko Stübner
55644d6f2efSHeiko Stübner return 0;
55744d6f2efSHeiko Stübner }
55844d6f2efSHeiko Stübner
rockchip_saradc_resume(struct device * dev)55944d6f2efSHeiko Stübner static int rockchip_saradc_resume(struct device *dev)
56044d6f2efSHeiko Stübner {
56144d6f2efSHeiko Stübner struct iio_dev *indio_dev = dev_get_drvdata(dev);
56244d6f2efSHeiko Stübner struct rockchip_saradc *info = iio_priv(indio_dev);
56344d6f2efSHeiko Stübner int ret;
56444d6f2efSHeiko Stübner
56544d6f2efSHeiko Stübner ret = regulator_enable(info->vref);
56644d6f2efSHeiko Stübner if (ret)
56744d6f2efSHeiko Stübner return ret;
56844d6f2efSHeiko Stübner
56944d6f2efSHeiko Stübner ret = clk_prepare_enable(info->pclk);
57044d6f2efSHeiko Stübner if (ret)
57144d6f2efSHeiko Stübner return ret;
57244d6f2efSHeiko Stübner
57344d6f2efSHeiko Stübner ret = clk_prepare_enable(info->clk);
57444d6f2efSHeiko Stübner if (ret)
575560c6b91SQinglang Miao clk_disable_unprepare(info->pclk);
57644d6f2efSHeiko Stübner
57744d6f2efSHeiko Stübner return ret;
57844d6f2efSHeiko Stübner }
57944d6f2efSHeiko Stübner
580cc0595a9SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
581cc0595a9SJonathan Cameron rockchip_saradc_suspend,
582cc0595a9SJonathan Cameron rockchip_saradc_resume);
58344d6f2efSHeiko Stübner
58444d6f2efSHeiko Stübner static struct platform_driver rockchip_saradc_driver = {
58544d6f2efSHeiko Stübner .probe = rockchip_saradc_probe,
58644d6f2efSHeiko Stübner .driver = {
58744d6f2efSHeiko Stübner .name = "rockchip-saradc",
58844d6f2efSHeiko Stübner .of_match_table = rockchip_saradc_match,
589cc0595a9SJonathan Cameron .pm = pm_sleep_ptr(&rockchip_saradc_pm_ops),
59044d6f2efSHeiko Stübner },
59144d6f2efSHeiko Stübner };
59244d6f2efSHeiko Stübner
59344d6f2efSHeiko Stübner module_platform_driver(rockchip_saradc_driver);
594dc7b8d98SHeiko Stuebner
595dc7b8d98SHeiko Stuebner MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
596dc7b8d98SHeiko Stuebner MODULE_DESCRIPTION("Rockchip SARADC driver");
597dc7b8d98SHeiko Stuebner MODULE_LICENSE("GPL v2");
598