11a78daeaSArtur Rojek // SPDX-License-Identifier: GPL-2.0
21a78daeaSArtur Rojek /*
31a78daeaSArtur Rojek * ADC driver for the Ingenic JZ47xx SoCs
41a78daeaSArtur Rojek * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
51a78daeaSArtur Rojek *
61a78daeaSArtur Rojek * based on drivers/mfd/jz4740-adc.c
71a78daeaSArtur Rojek */
81a78daeaSArtur Rojek
91a78daeaSArtur Rojek #include <dt-bindings/iio/adc/ingenic,adc.h>
101a78daeaSArtur Rojek #include <linux/clk.h>
11b96952f4SArtur Rojek #include <linux/iio/buffer.h>
121a78daeaSArtur Rojek #include <linux/iio/iio.h>
13b96952f4SArtur Rojek #include <linux/interrupt.h>
141a78daeaSArtur Rojek #include <linux/io.h>
151a78daeaSArtur Rojek #include <linux/iopoll.h>
165a304e1aSMaarten ter Huurne #include <linux/kernel.h>
171a78daeaSArtur Rojek #include <linux/module.h>
18a07a4fe5SJonathan Cameron #include <linux/mod_devicetable.h>
191a78daeaSArtur Rojek #include <linux/mutex.h>
204738b57aSNuno Sá #include <linux/of.h>
211a78daeaSArtur Rojek #include <linux/platform_device.h>
224738b57aSNuno Sá #include <linux/property.h>
231a78daeaSArtur Rojek
241a78daeaSArtur Rojek #define JZ_ADC_REG_ENABLE 0x00
251a78daeaSArtur Rojek #define JZ_ADC_REG_CFG 0x04
261a78daeaSArtur Rojek #define JZ_ADC_REG_CTRL 0x08
271a78daeaSArtur Rojek #define JZ_ADC_REG_STATUS 0x0c
28b96952f4SArtur Rojek #define JZ_ADC_REG_ADSAME 0x10
29b96952f4SArtur Rojek #define JZ_ADC_REG_ADWAIT 0x14
301a78daeaSArtur Rojek #define JZ_ADC_REG_ADTCH 0x18
311a78daeaSArtur Rojek #define JZ_ADC_REG_ADBDAT 0x1c
321a78daeaSArtur Rojek #define JZ_ADC_REG_ADSDAT 0x20
33b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD 0x24
345a304e1aSMaarten ter Huurne #define JZ_ADC_REG_ADCLK 0x28
351a78daeaSArtur Rojek
36a515d648SArtur Rojek #define JZ_ADC_REG_ENABLE_PD BIT(7)
37a515d648SArtur Rojek #define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
381a78daeaSArtur Rojek #define JZ_ADC_REG_CFG_BAT_MD BIT(4)
39b96952f4SArtur Rojek #define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10)
40b96952f4SArtur Rojek #define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16)
41b96952f4SArtur Rojek #define JZ_ADC_REG_CFG_CMD_SEL BIT(22)
42bf1b2418SChristophe Branchereau #define JZ_ADC_REG_CFG_VBAT_SEL BIT(30)
43b96952f4SArtur Rojek #define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10))
445a304e1aSMaarten ter Huurne #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
45a515d648SArtur Rojek #define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
46a515d648SArtur Rojek #define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
47a515d648SArtur Rojek #define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16
481a78daeaSArtur Rojek
49b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_YNADC BIT(7)
50b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_YPADC BIT(8)
51b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_XNADC BIT(9)
52b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_XPADC BIT(10)
53b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFPYP BIT(11)
54b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFPXP BIT(12)
55b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFPXN BIT(13)
56b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFPAUX BIT(14)
57b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFPVDD33 BIT(15)
58b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFNYN BIT(16)
59b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFNXP BIT(17)
60b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFNXN BIT(18)
61b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_VREFAUX BIT(19)
62b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_YNGRU BIT(20)
63b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_XNGRU BIT(21)
64b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_XPGRU BIT(22)
65b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_YPSUP BIT(23)
66b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_XNSUP BIT(24)
67b96952f4SArtur Rojek #define JZ_ADC_REG_ADCMD_XPSUP BIT(25)
68b96952f4SArtur Rojek
691a78daeaSArtur Rojek #define JZ_ADC_AUX_VREF 3300
701a78daeaSArtur Rojek #define JZ_ADC_AUX_VREF_BITS 12
711a78daeaSArtur Rojek #define JZ_ADC_BATTERY_LOW_VREF 2500
721a78daeaSArtur Rojek #define JZ_ADC_BATTERY_LOW_VREF_BITS 12
731a78daeaSArtur Rojek #define JZ4725B_ADC_BATTERY_HIGH_VREF 7500
741a78daeaSArtur Rojek #define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
751a78daeaSArtur Rojek #define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
761a78daeaSArtur Rojek #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
77b9e9bdd4SChristophe Branchereau #define JZ4760_ADC_BATTERY_VREF 2500
78c91ebcc5SPaul Cercueil #define JZ4770_ADC_BATTERY_VREF 1200
79a515d648SArtur Rojek #define JZ4770_ADC_BATTERY_VREF_BITS 12
801a78daeaSArtur Rojek
81b96952f4SArtur Rojek #define JZ_ADC_IRQ_AUX BIT(0)
82b96952f4SArtur Rojek #define JZ_ADC_IRQ_BATTERY BIT(1)
83b96952f4SArtur Rojek #define JZ_ADC_IRQ_TOUCH BIT(2)
84b96952f4SArtur Rojek #define JZ_ADC_IRQ_PEN_DOWN BIT(3)
85b96952f4SArtur Rojek #define JZ_ADC_IRQ_PEN_UP BIT(4)
86b96952f4SArtur Rojek #define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5)
87b96952f4SArtur Rojek #define JZ_ADC_IRQ_SLEEP BIT(7)
88b96952f4SArtur Rojek
895a304e1aSMaarten ter Huurne struct ingenic_adc;
905a304e1aSMaarten ter Huurne
911a78daeaSArtur Rojek struct ingenic_adc_soc_data {
921a78daeaSArtur Rojek unsigned int battery_high_vref;
931a78daeaSArtur Rojek unsigned int battery_high_vref_bits;
941a78daeaSArtur Rojek const int *battery_raw_avail;
951a78daeaSArtur Rojek size_t battery_raw_avail_size;
961a78daeaSArtur Rojek const int *battery_scale_avail;
971a78daeaSArtur Rojek size_t battery_scale_avail_size;
98a515d648SArtur Rojek unsigned int battery_vref_mode: 1;
999c5eb724SChristophe Branchereau unsigned int has_aux_md: 1;
1006a294b41SPaul Cercueil const struct iio_chan_spec *channels;
1016a294b41SPaul Cercueil unsigned int num_channels;
1025a304e1aSMaarten ter Huurne int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
1031a78daeaSArtur Rojek };
1041a78daeaSArtur Rojek
1051a78daeaSArtur Rojek struct ingenic_adc {
1061a78daeaSArtur Rojek void __iomem *base;
1071a78daeaSArtur Rojek struct clk *clk;
1081a78daeaSArtur Rojek struct mutex lock;
109a515d648SArtur Rojek struct mutex aux_lock;
1101a78daeaSArtur Rojek const struct ingenic_adc_soc_data *soc_data;
1111a78daeaSArtur Rojek bool low_vref_mode;
1121a78daeaSArtur Rojek };
1131a78daeaSArtur Rojek
ingenic_adc_set_adcmd(struct iio_dev * iio_dev,unsigned long mask)114b96952f4SArtur Rojek static void ingenic_adc_set_adcmd(struct iio_dev *iio_dev, unsigned long mask)
115b96952f4SArtur Rojek {
116b96952f4SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
117b96952f4SArtur Rojek
118b96952f4SArtur Rojek mutex_lock(&adc->lock);
119b96952f4SArtur Rojek
120b96952f4SArtur Rojek /* Init ADCMD */
121b96952f4SArtur Rojek readl(adc->base + JZ_ADC_REG_ADCMD);
122b96952f4SArtur Rojek
123b96952f4SArtur Rojek if (mask & 0x3) {
124b96952f4SArtur Rojek /* Second channel (INGENIC_ADC_TOUCH_YP): sample YP vs. GND */
125b96952f4SArtur Rojek writel(JZ_ADC_REG_ADCMD_XNGRU
126b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33
127b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_YPADC,
128b96952f4SArtur Rojek adc->base + JZ_ADC_REG_ADCMD);
129b96952f4SArtur Rojek
130b96952f4SArtur Rojek /* First channel (INGENIC_ADC_TOUCH_XP): sample XP vs. GND */
131b96952f4SArtur Rojek writel(JZ_ADC_REG_ADCMD_YNGRU
132b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33
133b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_XPADC,
134b96952f4SArtur Rojek adc->base + JZ_ADC_REG_ADCMD);
135b96952f4SArtur Rojek }
136b96952f4SArtur Rojek
137b96952f4SArtur Rojek if (mask & 0xc) {
138b96952f4SArtur Rojek /* Fourth channel (INGENIC_ADC_TOUCH_YN): sample YN vs. GND */
139b96952f4SArtur Rojek writel(JZ_ADC_REG_ADCMD_XNGRU
140b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33
141b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_YNADC,
142b96952f4SArtur Rojek adc->base + JZ_ADC_REG_ADCMD);
143b96952f4SArtur Rojek
144b96952f4SArtur Rojek /* Third channel (INGENIC_ADC_TOUCH_XN): sample XN vs. GND */
145b96952f4SArtur Rojek writel(JZ_ADC_REG_ADCMD_YNGRU
146b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33
147b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_XNADC,
148b96952f4SArtur Rojek adc->base + JZ_ADC_REG_ADCMD);
149b96952f4SArtur Rojek }
150b96952f4SArtur Rojek
151b96952f4SArtur Rojek if (mask & 0x30) {
152b96952f4SArtur Rojek /* Sixth channel (INGENIC_ADC_TOUCH_YD): sample YP vs. YN */
153b96952f4SArtur Rojek writel(JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33
154b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_YPADC,
155b96952f4SArtur Rojek adc->base + JZ_ADC_REG_ADCMD);
156b96952f4SArtur Rojek
157b96952f4SArtur Rojek /* Fifth channel (INGENIC_ADC_TOUCH_XD): sample XP vs. XN */
158b96952f4SArtur Rojek writel(JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33
159b96952f4SArtur Rojek | JZ_ADC_REG_ADCMD_XPADC,
160b96952f4SArtur Rojek adc->base + JZ_ADC_REG_ADCMD);
161b96952f4SArtur Rojek }
162b96952f4SArtur Rojek
163b96952f4SArtur Rojek /* We're done */
164b96952f4SArtur Rojek writel(0, adc->base + JZ_ADC_REG_ADCMD);
165b96952f4SArtur Rojek
166b96952f4SArtur Rojek mutex_unlock(&adc->lock);
167b96952f4SArtur Rojek }
168b96952f4SArtur Rojek
ingenic_adc_set_config(struct ingenic_adc * adc,uint32_t mask,uint32_t val)1691a78daeaSArtur Rojek static void ingenic_adc_set_config(struct ingenic_adc *adc,
1701a78daeaSArtur Rojek uint32_t mask,
1711a78daeaSArtur Rojek uint32_t val)
1721a78daeaSArtur Rojek {
1731a78daeaSArtur Rojek uint32_t cfg;
1741a78daeaSArtur Rojek
1751a78daeaSArtur Rojek mutex_lock(&adc->lock);
1761a78daeaSArtur Rojek
1771a78daeaSArtur Rojek cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask;
1781a78daeaSArtur Rojek cfg |= val;
1791a78daeaSArtur Rojek writel(cfg, adc->base + JZ_ADC_REG_CFG);
1801a78daeaSArtur Rojek
1811a78daeaSArtur Rojek mutex_unlock(&adc->lock);
1821a78daeaSArtur Rojek }
1831a78daeaSArtur Rojek
ingenic_adc_enable_unlocked(struct ingenic_adc * adc,int engine,bool enabled)1846d6aa290SPaul Cercueil static void ingenic_adc_enable_unlocked(struct ingenic_adc *adc,
1851a78daeaSArtur Rojek int engine,
1861a78daeaSArtur Rojek bool enabled)
1871a78daeaSArtur Rojek {
1881a78daeaSArtur Rojek u8 val;
1891a78daeaSArtur Rojek
1901a78daeaSArtur Rojek val = readb(adc->base + JZ_ADC_REG_ENABLE);
1911a78daeaSArtur Rojek
1921a78daeaSArtur Rojek if (enabled)
1931a78daeaSArtur Rojek val |= BIT(engine);
1941a78daeaSArtur Rojek else
1951a78daeaSArtur Rojek val &= ~BIT(engine);
1961a78daeaSArtur Rojek
1971a78daeaSArtur Rojek writeb(val, adc->base + JZ_ADC_REG_ENABLE);
1986d6aa290SPaul Cercueil }
1996d6aa290SPaul Cercueil
ingenic_adc_enable(struct ingenic_adc * adc,int engine,bool enabled)2006d6aa290SPaul Cercueil static void ingenic_adc_enable(struct ingenic_adc *adc,
2016d6aa290SPaul Cercueil int engine,
2026d6aa290SPaul Cercueil bool enabled)
2036d6aa290SPaul Cercueil {
2046d6aa290SPaul Cercueil mutex_lock(&adc->lock);
2056d6aa290SPaul Cercueil ingenic_adc_enable_unlocked(adc, engine, enabled);
2061a78daeaSArtur Rojek mutex_unlock(&adc->lock);
2071a78daeaSArtur Rojek }
2081a78daeaSArtur Rojek
ingenic_adc_capture(struct ingenic_adc * adc,int engine)2091a78daeaSArtur Rojek static int ingenic_adc_capture(struct ingenic_adc *adc,
2101a78daeaSArtur Rojek int engine)
2111a78daeaSArtur Rojek {
2126d6aa290SPaul Cercueil u32 cfg;
2131a78daeaSArtur Rojek u8 val;
2141a78daeaSArtur Rojek int ret;
2151a78daeaSArtur Rojek
2166d6aa290SPaul Cercueil /*
2176d6aa290SPaul Cercueil * Disable CMD_SEL temporarily, because it causes wrong VBAT readings,
2186d6aa290SPaul Cercueil * probably due to the switch of VREF. We must keep the lock here to
2196d6aa290SPaul Cercueil * avoid races with the buffer enable/disable functions.
2206d6aa290SPaul Cercueil */
2216d6aa290SPaul Cercueil mutex_lock(&adc->lock);
2226d6aa290SPaul Cercueil cfg = readl(adc->base + JZ_ADC_REG_CFG);
2236d6aa290SPaul Cercueil writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG);
2246d6aa290SPaul Cercueil
2256d6aa290SPaul Cercueil ingenic_adc_enable_unlocked(adc, engine, true);
2261a78daeaSArtur Rojek ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val,
2271a78daeaSArtur Rojek !(val & BIT(engine)), 250, 1000);
2281a78daeaSArtur Rojek if (ret)
2296d6aa290SPaul Cercueil ingenic_adc_enable_unlocked(adc, engine, false);
2306d6aa290SPaul Cercueil
2316d6aa290SPaul Cercueil writel(cfg, adc->base + JZ_ADC_REG_CFG);
2326d6aa290SPaul Cercueil mutex_unlock(&adc->lock);
2331a78daeaSArtur Rojek
2341a78daeaSArtur Rojek return ret;
2351a78daeaSArtur Rojek }
2361a78daeaSArtur Rojek
ingenic_adc_write_raw(struct iio_dev * iio_dev,struct iio_chan_spec const * chan,int val,int val2,long m)2371a78daeaSArtur Rojek static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
2381a78daeaSArtur Rojek struct iio_chan_spec const *chan,
2391a78daeaSArtur Rojek int val,
2401a78daeaSArtur Rojek int val2,
2411a78daeaSArtur Rojek long m)
2421a78daeaSArtur Rojek {
2431a78daeaSArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
2441a99dc46SArtur Rojek struct device *dev = iio_dev->dev.parent;
2451a99dc46SArtur Rojek int ret;
2461a78daeaSArtur Rojek
2471a78daeaSArtur Rojek switch (m) {
2481a78daeaSArtur Rojek case IIO_CHAN_INFO_SCALE:
2491a78daeaSArtur Rojek switch (chan->channel) {
2501a78daeaSArtur Rojek case INGENIC_ADC_BATTERY:
251a515d648SArtur Rojek if (!adc->soc_data->battery_vref_mode)
252a515d648SArtur Rojek return -EINVAL;
2531a99dc46SArtur Rojek
2541a99dc46SArtur Rojek ret = clk_enable(adc->clk);
2551a99dc46SArtur Rojek if (ret) {
2561a99dc46SArtur Rojek dev_err(dev, "Failed to enable clock: %d\n",
2571a99dc46SArtur Rojek ret);
2581a99dc46SArtur Rojek return ret;
2591a99dc46SArtur Rojek }
2601a99dc46SArtur Rojek
2611a78daeaSArtur Rojek if (val > JZ_ADC_BATTERY_LOW_VREF) {
2621a78daeaSArtur Rojek ingenic_adc_set_config(adc,
2631a78daeaSArtur Rojek JZ_ADC_REG_CFG_BAT_MD,
2641a78daeaSArtur Rojek 0);
2651a78daeaSArtur Rojek adc->low_vref_mode = false;
2661a78daeaSArtur Rojek } else {
2671a78daeaSArtur Rojek ingenic_adc_set_config(adc,
2681a78daeaSArtur Rojek JZ_ADC_REG_CFG_BAT_MD,
2691a78daeaSArtur Rojek JZ_ADC_REG_CFG_BAT_MD);
2701a78daeaSArtur Rojek adc->low_vref_mode = true;
2711a78daeaSArtur Rojek }
2721a99dc46SArtur Rojek
2731a99dc46SArtur Rojek clk_disable(adc->clk);
2741a99dc46SArtur Rojek
2751a78daeaSArtur Rojek return 0;
2761a78daeaSArtur Rojek default:
2771a78daeaSArtur Rojek return -EINVAL;
2781a78daeaSArtur Rojek }
2791a78daeaSArtur Rojek default:
2801a78daeaSArtur Rojek return -EINVAL;
2811a78daeaSArtur Rojek }
2821a78daeaSArtur Rojek }
2831a78daeaSArtur Rojek
2841a78daeaSArtur Rojek static const int jz4725b_adc_battery_raw_avail[] = {
2851a78daeaSArtur Rojek 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
2861a78daeaSArtur Rojek };
2871a78daeaSArtur Rojek
2881a78daeaSArtur Rojek static const int jz4725b_adc_battery_scale_avail[] = {
2891a78daeaSArtur Rojek JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
2901a78daeaSArtur Rojek JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
2911a78daeaSArtur Rojek };
2921a78daeaSArtur Rojek
2931a78daeaSArtur Rojek static const int jz4740_adc_battery_raw_avail[] = {
2941a78daeaSArtur Rojek 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
2951a78daeaSArtur Rojek };
2961a78daeaSArtur Rojek
2971a78daeaSArtur Rojek static const int jz4740_adc_battery_scale_avail[] = {
2981a78daeaSArtur Rojek JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
2991a78daeaSArtur Rojek JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
3001a78daeaSArtur Rojek };
3011a78daeaSArtur Rojek
302b9e9bdd4SChristophe Branchereau static const int jz4760_adc_battery_scale_avail[] = {
303b9e9bdd4SChristophe Branchereau JZ4760_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS,
304b9e9bdd4SChristophe Branchereau };
305b9e9bdd4SChristophe Branchereau
306a515d648SArtur Rojek static const int jz4770_adc_battery_raw_avail[] = {
307a515d648SArtur Rojek 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1,
308a515d648SArtur Rojek };
309a515d648SArtur Rojek
310a515d648SArtur Rojek static const int jz4770_adc_battery_scale_avail[] = {
311a515d648SArtur Rojek JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS,
312a515d648SArtur Rojek };
313a515d648SArtur Rojek
jz4725b_adc_init_clk_div(struct device * dev,struct ingenic_adc * adc)3145a304e1aSMaarten ter Huurne static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
3155a304e1aSMaarten ter Huurne {
3165a304e1aSMaarten ter Huurne struct clk *parent_clk;
3175a304e1aSMaarten ter Huurne unsigned long parent_rate, rate;
3185a304e1aSMaarten ter Huurne unsigned int div_main, div_10us;
3195a304e1aSMaarten ter Huurne
3205a304e1aSMaarten ter Huurne parent_clk = clk_get_parent(adc->clk);
3215a304e1aSMaarten ter Huurne if (!parent_clk) {
3225a304e1aSMaarten ter Huurne dev_err(dev, "ADC clock has no parent\n");
3235a304e1aSMaarten ter Huurne return -ENODEV;
3245a304e1aSMaarten ter Huurne }
3255a304e1aSMaarten ter Huurne parent_rate = clk_get_rate(parent_clk);
3265a304e1aSMaarten ter Huurne
3275a304e1aSMaarten ter Huurne /*
3285a304e1aSMaarten ter Huurne * The JZ4725B ADC works at 500 kHz to 8 MHz.
3295a304e1aSMaarten ter Huurne * We pick the highest rate possible.
3305a304e1aSMaarten ter Huurne * In practice we typically get 6 MHz, half of the 12 MHz EXT clock.
3315a304e1aSMaarten ter Huurne */
3325a304e1aSMaarten ter Huurne div_main = DIV_ROUND_UP(parent_rate, 8000000);
3335a304e1aSMaarten ter Huurne div_main = clamp(div_main, 1u, 64u);
3345a304e1aSMaarten ter Huurne rate = parent_rate / div_main;
3355a304e1aSMaarten ter Huurne if (rate < 500000 || rate > 8000000) {
3365a304e1aSMaarten ter Huurne dev_err(dev, "No valid divider for ADC main clock\n");
3375a304e1aSMaarten ter Huurne return -EINVAL;
3385a304e1aSMaarten ter Huurne }
3395a304e1aSMaarten ter Huurne
3405a304e1aSMaarten ter Huurne /* We also need a divider that produces a 10us clock. */
3415a304e1aSMaarten ter Huurne div_10us = DIV_ROUND_UP(rate, 100000);
3425a304e1aSMaarten ter Huurne
343a515d648SArtur Rojek writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) |
344a515d648SArtur Rojek (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
345a515d648SArtur Rojek adc->base + JZ_ADC_REG_ADCLK);
346a515d648SArtur Rojek
347a515d648SArtur Rojek return 0;
348a515d648SArtur Rojek }
349a515d648SArtur Rojek
jz4770_adc_init_clk_div(struct device * dev,struct ingenic_adc * adc)350a515d648SArtur Rojek static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
351a515d648SArtur Rojek {
352a515d648SArtur Rojek struct clk *parent_clk;
353a515d648SArtur Rojek unsigned long parent_rate, rate;
354a515d648SArtur Rojek unsigned int div_main, div_ms, div_10us;
355a515d648SArtur Rojek
356a515d648SArtur Rojek parent_clk = clk_get_parent(adc->clk);
357a515d648SArtur Rojek if (!parent_clk) {
358a515d648SArtur Rojek dev_err(dev, "ADC clock has no parent\n");
359a515d648SArtur Rojek return -ENODEV;
360a515d648SArtur Rojek }
361a515d648SArtur Rojek parent_rate = clk_get_rate(parent_clk);
362a515d648SArtur Rojek
363a515d648SArtur Rojek /*
364a515d648SArtur Rojek * The JZ4770 ADC works at 20 kHz to 200 kHz.
365a515d648SArtur Rojek * We pick the highest rate possible.
366a515d648SArtur Rojek */
367a515d648SArtur Rojek div_main = DIV_ROUND_UP(parent_rate, 200000);
368a515d648SArtur Rojek div_main = clamp(div_main, 1u, 256u);
369a515d648SArtur Rojek rate = parent_rate / div_main;
370a515d648SArtur Rojek if (rate < 20000 || rate > 200000) {
371a515d648SArtur Rojek dev_err(dev, "No valid divider for ADC main clock\n");
372a515d648SArtur Rojek return -EINVAL;
373a515d648SArtur Rojek }
374a515d648SArtur Rojek
375a515d648SArtur Rojek /* We also need a divider that produces a 10us clock. */
376a515d648SArtur Rojek div_10us = DIV_ROUND_UP(rate, 10000);
377a515d648SArtur Rojek /* And another, which produces a 1ms clock. */
378a515d648SArtur Rojek div_ms = DIV_ROUND_UP(rate, 1000);
379a515d648SArtur Rojek
380a515d648SArtur Rojek writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) |
381a515d648SArtur Rojek ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) |
3825a304e1aSMaarten ter Huurne (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
3835a304e1aSMaarten ter Huurne adc->base + JZ_ADC_REG_ADCLK);
3845a304e1aSMaarten ter Huurne
3855a304e1aSMaarten ter Huurne return 0;
3865a304e1aSMaarten ter Huurne }
3875a304e1aSMaarten ter Huurne
3886a294b41SPaul Cercueil static const struct iio_chan_spec jz4740_channels[] = {
3896a294b41SPaul Cercueil {
3906a294b41SPaul Cercueil .extend_name = "aux",
3916a294b41SPaul Cercueil .type = IIO_VOLTAGE,
3926a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
3936a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE),
3946a294b41SPaul Cercueil .indexed = 1,
3956a294b41SPaul Cercueil .channel = INGENIC_ADC_AUX,
3966a294b41SPaul Cercueil .scan_index = -1,
3976a294b41SPaul Cercueil },
3986a294b41SPaul Cercueil {
3996a294b41SPaul Cercueil .extend_name = "battery",
4006a294b41SPaul Cercueil .type = IIO_VOLTAGE,
4016a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
4026a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE),
4036a294b41SPaul Cercueil .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
4046a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE),
4056a294b41SPaul Cercueil .indexed = 1,
4066a294b41SPaul Cercueil .channel = INGENIC_ADC_BATTERY,
4076a294b41SPaul Cercueil .scan_index = -1,
4086a294b41SPaul Cercueil },
4096a294b41SPaul Cercueil };
4106a294b41SPaul Cercueil
411b9e9bdd4SChristophe Branchereau static const struct iio_chan_spec jz4760_channels[] = {
412b9e9bdd4SChristophe Branchereau {
413b9e9bdd4SChristophe Branchereau .extend_name = "aux",
414b9e9bdd4SChristophe Branchereau .type = IIO_VOLTAGE,
415b9e9bdd4SChristophe Branchereau .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
416b9e9bdd4SChristophe Branchereau BIT(IIO_CHAN_INFO_SCALE),
417b9e9bdd4SChristophe Branchereau .indexed = 1,
418b9e9bdd4SChristophe Branchereau .channel = INGENIC_ADC_AUX0,
419b9e9bdd4SChristophe Branchereau .scan_index = -1,
420b9e9bdd4SChristophe Branchereau },
421b9e9bdd4SChristophe Branchereau {
422b9e9bdd4SChristophe Branchereau .extend_name = "aux1",
423b9e9bdd4SChristophe Branchereau .type = IIO_VOLTAGE,
424b9e9bdd4SChristophe Branchereau .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
425b9e9bdd4SChristophe Branchereau BIT(IIO_CHAN_INFO_SCALE),
426b9e9bdd4SChristophe Branchereau .indexed = 1,
427b9e9bdd4SChristophe Branchereau .channel = INGENIC_ADC_AUX,
428b9e9bdd4SChristophe Branchereau .scan_index = -1,
429b9e9bdd4SChristophe Branchereau },
430b9e9bdd4SChristophe Branchereau {
431b9e9bdd4SChristophe Branchereau .extend_name = "aux2",
432b9e9bdd4SChristophe Branchereau .type = IIO_VOLTAGE,
433b9e9bdd4SChristophe Branchereau .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
434b9e9bdd4SChristophe Branchereau BIT(IIO_CHAN_INFO_SCALE),
435b9e9bdd4SChristophe Branchereau .indexed = 1,
436b9e9bdd4SChristophe Branchereau .channel = INGENIC_ADC_AUX2,
437b9e9bdd4SChristophe Branchereau .scan_index = -1,
438b9e9bdd4SChristophe Branchereau },
439b9e9bdd4SChristophe Branchereau {
440b9e9bdd4SChristophe Branchereau .extend_name = "battery",
441b9e9bdd4SChristophe Branchereau .type = IIO_VOLTAGE,
442b9e9bdd4SChristophe Branchereau .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
443b9e9bdd4SChristophe Branchereau BIT(IIO_CHAN_INFO_SCALE),
444b9e9bdd4SChristophe Branchereau .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
445b9e9bdd4SChristophe Branchereau BIT(IIO_CHAN_INFO_SCALE),
446b9e9bdd4SChristophe Branchereau .indexed = 1,
447b9e9bdd4SChristophe Branchereau .channel = INGENIC_ADC_BATTERY,
448b9e9bdd4SChristophe Branchereau .scan_index = -1,
449b9e9bdd4SChristophe Branchereau },
450b9e9bdd4SChristophe Branchereau };
451b9e9bdd4SChristophe Branchereau
4526a294b41SPaul Cercueil static const struct iio_chan_spec jz4770_channels[] = {
4536a294b41SPaul Cercueil {
454b96952f4SArtur Rojek .type = IIO_VOLTAGE,
455b96952f4SArtur Rojek .indexed = 1,
456b96952f4SArtur Rojek .channel = INGENIC_ADC_TOUCH_XP,
457b96952f4SArtur Rojek .scan_index = 0,
458b96952f4SArtur Rojek .scan_type = {
459b96952f4SArtur Rojek .sign = 'u',
460b96952f4SArtur Rojek .realbits = 12,
461b96952f4SArtur Rojek .storagebits = 16,
462b96952f4SArtur Rojek },
463b96952f4SArtur Rojek },
464b96952f4SArtur Rojek {
465b96952f4SArtur Rojek .type = IIO_VOLTAGE,
466b96952f4SArtur Rojek .indexed = 1,
467b96952f4SArtur Rojek .channel = INGENIC_ADC_TOUCH_YP,
468b96952f4SArtur Rojek .scan_index = 1,
469b96952f4SArtur Rojek .scan_type = {
470b96952f4SArtur Rojek .sign = 'u',
471b96952f4SArtur Rojek .realbits = 12,
472b96952f4SArtur Rojek .storagebits = 16,
473b96952f4SArtur Rojek },
474b96952f4SArtur Rojek },
475b96952f4SArtur Rojek {
476b96952f4SArtur Rojek .type = IIO_VOLTAGE,
477b96952f4SArtur Rojek .indexed = 1,
478b96952f4SArtur Rojek .channel = INGENIC_ADC_TOUCH_XN,
479b96952f4SArtur Rojek .scan_index = 2,
480b96952f4SArtur Rojek .scan_type = {
481b96952f4SArtur Rojek .sign = 'u',
482b96952f4SArtur Rojek .realbits = 12,
483b96952f4SArtur Rojek .storagebits = 16,
484b96952f4SArtur Rojek },
485b96952f4SArtur Rojek },
486b96952f4SArtur Rojek {
487b96952f4SArtur Rojek .type = IIO_VOLTAGE,
488b96952f4SArtur Rojek .indexed = 1,
489b96952f4SArtur Rojek .channel = INGENIC_ADC_TOUCH_YN,
490b96952f4SArtur Rojek .scan_index = 3,
491b96952f4SArtur Rojek .scan_type = {
492b96952f4SArtur Rojek .sign = 'u',
493b96952f4SArtur Rojek .realbits = 12,
494b96952f4SArtur Rojek .storagebits = 16,
495b96952f4SArtur Rojek },
496b96952f4SArtur Rojek },
497b96952f4SArtur Rojek {
498b96952f4SArtur Rojek .type = IIO_VOLTAGE,
499b96952f4SArtur Rojek .indexed = 1,
500b96952f4SArtur Rojek .channel = INGENIC_ADC_TOUCH_XD,
501b96952f4SArtur Rojek .scan_index = 4,
502b96952f4SArtur Rojek .scan_type = {
503b96952f4SArtur Rojek .sign = 'u',
504b96952f4SArtur Rojek .realbits = 12,
505b96952f4SArtur Rojek .storagebits = 16,
506b96952f4SArtur Rojek },
507b96952f4SArtur Rojek },
508b96952f4SArtur Rojek {
509b96952f4SArtur Rojek .type = IIO_VOLTAGE,
510b96952f4SArtur Rojek .indexed = 1,
511b96952f4SArtur Rojek .channel = INGENIC_ADC_TOUCH_YD,
512b96952f4SArtur Rojek .scan_index = 5,
513b96952f4SArtur Rojek .scan_type = {
514b96952f4SArtur Rojek .sign = 'u',
515b96952f4SArtur Rojek .realbits = 12,
516b96952f4SArtur Rojek .storagebits = 16,
517b96952f4SArtur Rojek },
518b96952f4SArtur Rojek },
519b96952f4SArtur Rojek {
5206a294b41SPaul Cercueil .extend_name = "aux",
5216a294b41SPaul Cercueil .type = IIO_VOLTAGE,
5226a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
5236a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE),
5246a294b41SPaul Cercueil .indexed = 1,
5256a294b41SPaul Cercueil .channel = INGENIC_ADC_AUX,
5266a294b41SPaul Cercueil .scan_index = -1,
5276a294b41SPaul Cercueil },
5286a294b41SPaul Cercueil {
5296a294b41SPaul Cercueil .extend_name = "battery",
5306a294b41SPaul Cercueil .type = IIO_VOLTAGE,
5316a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
5326a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE),
5336a294b41SPaul Cercueil .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
5346a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE),
5356a294b41SPaul Cercueil .indexed = 1,
5366a294b41SPaul Cercueil .channel = INGENIC_ADC_BATTERY,
5376a294b41SPaul Cercueil .scan_index = -1,
5386a294b41SPaul Cercueil },
5396a294b41SPaul Cercueil {
5406a294b41SPaul Cercueil .extend_name = "aux2",
5416a294b41SPaul Cercueil .type = IIO_VOLTAGE,
5426a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
5436a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE),
5446a294b41SPaul Cercueil .indexed = 1,
5456a294b41SPaul Cercueil .channel = INGENIC_ADC_AUX2,
5466a294b41SPaul Cercueil .scan_index = -1,
5476a294b41SPaul Cercueil },
5486a294b41SPaul Cercueil };
5496a294b41SPaul Cercueil
5501a78daeaSArtur Rojek static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
5511a78daeaSArtur Rojek .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF,
5521a78daeaSArtur Rojek .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
5531a78daeaSArtur Rojek .battery_raw_avail = jz4725b_adc_battery_raw_avail,
5541a78daeaSArtur Rojek .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
5551a78daeaSArtur Rojek .battery_scale_avail = jz4725b_adc_battery_scale_avail,
5561a78daeaSArtur Rojek .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
557a515d648SArtur Rojek .battery_vref_mode = true,
5589c5eb724SChristophe Branchereau .has_aux_md = false,
5596a294b41SPaul Cercueil .channels = jz4740_channels,
5606a294b41SPaul Cercueil .num_channels = ARRAY_SIZE(jz4740_channels),
5615a304e1aSMaarten ter Huurne .init_clk_div = jz4725b_adc_init_clk_div,
5621a78daeaSArtur Rojek };
5631a78daeaSArtur Rojek
5641a78daeaSArtur Rojek static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
5651a78daeaSArtur Rojek .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF,
5661a78daeaSArtur Rojek .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
5671a78daeaSArtur Rojek .battery_raw_avail = jz4740_adc_battery_raw_avail,
5681a78daeaSArtur Rojek .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
5691a78daeaSArtur Rojek .battery_scale_avail = jz4740_adc_battery_scale_avail,
5701a78daeaSArtur Rojek .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
571a515d648SArtur Rojek .battery_vref_mode = true,
5729c5eb724SChristophe Branchereau .has_aux_md = false,
5736a294b41SPaul Cercueil .channels = jz4740_channels,
5746a294b41SPaul Cercueil .num_channels = ARRAY_SIZE(jz4740_channels),
5755a304e1aSMaarten ter Huurne .init_clk_div = NULL, /* no ADCLK register on JZ4740 */
5761a78daeaSArtur Rojek };
5771a78daeaSArtur Rojek
578b9e9bdd4SChristophe Branchereau static const struct ingenic_adc_soc_data jz4760_adc_soc_data = {
579b9e9bdd4SChristophe Branchereau .battery_high_vref = JZ4760_ADC_BATTERY_VREF,
580b9e9bdd4SChristophe Branchereau .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS,
581b9e9bdd4SChristophe Branchereau .battery_raw_avail = jz4770_adc_battery_raw_avail,
582b9e9bdd4SChristophe Branchereau .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail),
583b9e9bdd4SChristophe Branchereau .battery_scale_avail = jz4760_adc_battery_scale_avail,
584b9e9bdd4SChristophe Branchereau .battery_scale_avail_size = ARRAY_SIZE(jz4760_adc_battery_scale_avail),
585b9e9bdd4SChristophe Branchereau .battery_vref_mode = false,
586b9e9bdd4SChristophe Branchereau .has_aux_md = true,
587b9e9bdd4SChristophe Branchereau .channels = jz4760_channels,
588b9e9bdd4SChristophe Branchereau .num_channels = ARRAY_SIZE(jz4760_channels),
589b9e9bdd4SChristophe Branchereau .init_clk_div = jz4770_adc_init_clk_div,
590b9e9bdd4SChristophe Branchereau };
591b9e9bdd4SChristophe Branchereau
592a515d648SArtur Rojek static const struct ingenic_adc_soc_data jz4770_adc_soc_data = {
593a515d648SArtur Rojek .battery_high_vref = JZ4770_ADC_BATTERY_VREF,
594a515d648SArtur Rojek .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS,
595a515d648SArtur Rojek .battery_raw_avail = jz4770_adc_battery_raw_avail,
596a515d648SArtur Rojek .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail),
597a515d648SArtur Rojek .battery_scale_avail = jz4770_adc_battery_scale_avail,
598a515d648SArtur Rojek .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail),
599a515d648SArtur Rojek .battery_vref_mode = false,
6009c5eb724SChristophe Branchereau .has_aux_md = true,
6016a294b41SPaul Cercueil .channels = jz4770_channels,
6026a294b41SPaul Cercueil .num_channels = ARRAY_SIZE(jz4770_channels),
603a515d648SArtur Rojek .init_clk_div = jz4770_adc_init_clk_div,
604a515d648SArtur Rojek };
605a515d648SArtur Rojek
ingenic_adc_read_avail(struct iio_dev * iio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long m)6061a78daeaSArtur Rojek static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
6071a78daeaSArtur Rojek struct iio_chan_spec const *chan,
6081a78daeaSArtur Rojek const int **vals,
6091a78daeaSArtur Rojek int *type,
6101a78daeaSArtur Rojek int *length,
6111a78daeaSArtur Rojek long m)
6121a78daeaSArtur Rojek {
6131a78daeaSArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
6141a78daeaSArtur Rojek
6151a78daeaSArtur Rojek switch (m) {
6161a78daeaSArtur Rojek case IIO_CHAN_INFO_RAW:
6171a78daeaSArtur Rojek *type = IIO_VAL_INT;
6181a78daeaSArtur Rojek *length = adc->soc_data->battery_raw_avail_size;
6191a78daeaSArtur Rojek *vals = adc->soc_data->battery_raw_avail;
6201a78daeaSArtur Rojek return IIO_AVAIL_RANGE;
6211a78daeaSArtur Rojek case IIO_CHAN_INFO_SCALE:
6221a78daeaSArtur Rojek *type = IIO_VAL_FRACTIONAL_LOG2;
6231a78daeaSArtur Rojek *length = adc->soc_data->battery_scale_avail_size;
6241a78daeaSArtur Rojek *vals = adc->soc_data->battery_scale_avail;
6251a78daeaSArtur Rojek return IIO_AVAIL_LIST;
6261a78daeaSArtur Rojek default:
6271a78daeaSArtur Rojek return -EINVAL;
6284a6261c3STom Rix }
6291a78daeaSArtur Rojek }
6301a78daeaSArtur Rojek
ingenic_adc_read_chan_info_raw(struct iio_dev * iio_dev,struct iio_chan_spec const * chan,int * val)6311a99dc46SArtur Rojek static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev,
6321a78daeaSArtur Rojek struct iio_chan_spec const *chan,
633a515d648SArtur Rojek int *val)
6341a78daeaSArtur Rojek {
635b9e9bdd4SChristophe Branchereau int cmd, ret, engine = (chan->channel == INGENIC_ADC_BATTERY);
6361a99dc46SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
6371a99dc46SArtur Rojek
6381a99dc46SArtur Rojek ret = clk_enable(adc->clk);
6391a99dc46SArtur Rojek if (ret) {
6401a99dc46SArtur Rojek dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n",
6411a99dc46SArtur Rojek ret);
6421a99dc46SArtur Rojek return ret;
6431a99dc46SArtur Rojek }
6441a78daeaSArtur Rojek
645b9e9bdd4SChristophe Branchereau /* We cannot sample the aux channels in parallel. */
646a515d648SArtur Rojek mutex_lock(&adc->aux_lock);
6479c5eb724SChristophe Branchereau if (adc->soc_data->has_aux_md && engine == 0) {
648b9e9bdd4SChristophe Branchereau switch (chan->channel) {
649b9e9bdd4SChristophe Branchereau case INGENIC_ADC_AUX0:
650b9e9bdd4SChristophe Branchereau cmd = 0;
651b9e9bdd4SChristophe Branchereau break;
652b9e9bdd4SChristophe Branchereau case INGENIC_ADC_AUX:
653b9e9bdd4SChristophe Branchereau cmd = 1;
654b9e9bdd4SChristophe Branchereau break;
655b9e9bdd4SChristophe Branchereau case INGENIC_ADC_AUX2:
656b9e9bdd4SChristophe Branchereau cmd = 2;
657b9e9bdd4SChristophe Branchereau break;
658b9e9bdd4SChristophe Branchereau }
659b9e9bdd4SChristophe Branchereau
660b9e9bdd4SChristophe Branchereau ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, cmd);
6611a78daeaSArtur Rojek }
6621a78daeaSArtur Rojek
663a515d648SArtur Rojek ret = ingenic_adc_capture(adc, engine);
664a515d648SArtur Rojek if (ret)
665a515d648SArtur Rojek goto out;
666a515d648SArtur Rojek
6671a78daeaSArtur Rojek switch (chan->channel) {
668b9e9bdd4SChristophe Branchereau case INGENIC_ADC_AUX0:
6691a78daeaSArtur Rojek case INGENIC_ADC_AUX:
670a515d648SArtur Rojek case INGENIC_ADC_AUX2:
6711a78daeaSArtur Rojek *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
6721a78daeaSArtur Rojek break;
6731a78daeaSArtur Rojek case INGENIC_ADC_BATTERY:
6741a78daeaSArtur Rojek *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
6751a78daeaSArtur Rojek break;
6761a78daeaSArtur Rojek }
6771a78daeaSArtur Rojek
678a515d648SArtur Rojek ret = IIO_VAL_INT;
679a515d648SArtur Rojek out:
680a515d648SArtur Rojek mutex_unlock(&adc->aux_lock);
6811a99dc46SArtur Rojek clk_disable(adc->clk);
6821a78daeaSArtur Rojek
683a515d648SArtur Rojek return ret;
684a515d648SArtur Rojek }
685a515d648SArtur Rojek
ingenic_adc_read_raw(struct iio_dev * iio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long m)686a515d648SArtur Rojek static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
687a515d648SArtur Rojek struct iio_chan_spec const *chan,
688a515d648SArtur Rojek int *val,
689a515d648SArtur Rojek int *val2,
690a515d648SArtur Rojek long m)
691a515d648SArtur Rojek {
692a515d648SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
693a515d648SArtur Rojek
694a515d648SArtur Rojek switch (m) {
695a515d648SArtur Rojek case IIO_CHAN_INFO_RAW:
6961a99dc46SArtur Rojek return ingenic_adc_read_chan_info_raw(iio_dev, chan, val);
6971a78daeaSArtur Rojek case IIO_CHAN_INFO_SCALE:
6981a78daeaSArtur Rojek switch (chan->channel) {
699b9e9bdd4SChristophe Branchereau case INGENIC_ADC_AUX0:
7001a78daeaSArtur Rojek case INGENIC_ADC_AUX:
701a515d648SArtur Rojek case INGENIC_ADC_AUX2:
7021a78daeaSArtur Rojek *val = JZ_ADC_AUX_VREF;
7031a78daeaSArtur Rojek *val2 = JZ_ADC_AUX_VREF_BITS;
7041a78daeaSArtur Rojek break;
7051a78daeaSArtur Rojek case INGENIC_ADC_BATTERY:
7061a78daeaSArtur Rojek if (adc->low_vref_mode) {
7071a78daeaSArtur Rojek *val = JZ_ADC_BATTERY_LOW_VREF;
7081a78daeaSArtur Rojek *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS;
7091a78daeaSArtur Rojek } else {
7101a78daeaSArtur Rojek *val = adc->soc_data->battery_high_vref;
7111a78daeaSArtur Rojek *val2 = adc->soc_data->battery_high_vref_bits;
7121a78daeaSArtur Rojek }
7131a78daeaSArtur Rojek break;
7141a78daeaSArtur Rojek }
7151a78daeaSArtur Rojek
7161a78daeaSArtur Rojek return IIO_VAL_FRACTIONAL_LOG2;
7171a78daeaSArtur Rojek default:
7181a78daeaSArtur Rojek return -EINVAL;
7191a78daeaSArtur Rojek }
7201a78daeaSArtur Rojek }
7211a78daeaSArtur Rojek
ingenic_adc_fwnode_xlate(struct iio_dev * iio_dev,const struct fwnode_reference_args * iiospec)7229ac07597SNuno Sá static int ingenic_adc_fwnode_xlate(struct iio_dev *iio_dev,
7239ac07597SNuno Sá const struct fwnode_reference_args *iiospec)
724155e41efSArtur Rojek {
725155e41efSArtur Rojek int i;
726155e41efSArtur Rojek
7279ac07597SNuno Sá if (!iiospec->nargs)
728155e41efSArtur Rojek return -EINVAL;
729155e41efSArtur Rojek
730155e41efSArtur Rojek for (i = 0; i < iio_dev->num_channels; ++i)
731155e41efSArtur Rojek if (iio_dev->channels[i].channel == iiospec->args[0])
732155e41efSArtur Rojek return i;
733155e41efSArtur Rojek
734155e41efSArtur Rojek return -EINVAL;
735155e41efSArtur Rojek }
736155e41efSArtur Rojek
7371a78daeaSArtur Rojek static const struct iio_info ingenic_adc_info = {
7381a78daeaSArtur Rojek .write_raw = ingenic_adc_write_raw,
7391a78daeaSArtur Rojek .read_raw = ingenic_adc_read_raw,
7401a78daeaSArtur Rojek .read_avail = ingenic_adc_read_avail,
7419ac07597SNuno Sá .fwnode_xlate = ingenic_adc_fwnode_xlate,
7421a78daeaSArtur Rojek };
7431a78daeaSArtur Rojek
ingenic_adc_buffer_enable(struct iio_dev * iio_dev)744b96952f4SArtur Rojek static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev)
7451a78daeaSArtur Rojek {
746b96952f4SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
747b96952f4SArtur Rojek int ret;
748b96952f4SArtur Rojek
749b96952f4SArtur Rojek ret = clk_enable(adc->clk);
750b96952f4SArtur Rojek if (ret) {
751b96952f4SArtur Rojek dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n",
752b96952f4SArtur Rojek ret);
753b96952f4SArtur Rojek return ret;
754b96952f4SArtur Rojek }
755b96952f4SArtur Rojek
756b96952f4SArtur Rojek /* It takes significant time for the touchscreen hw to stabilize. */
757b96952f4SArtur Rojek msleep(50);
758b96952f4SArtur Rojek ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK,
759b96952f4SArtur Rojek JZ_ADC_REG_CFG_SAMPLE_NUM(4) |
760b96952f4SArtur Rojek JZ_ADC_REG_CFG_PULL_UP(4));
761b96952f4SArtur Rojek
762b96952f4SArtur Rojek writew(80, adc->base + JZ_ADC_REG_ADWAIT);
763b96952f4SArtur Rojek writew(2, adc->base + JZ_ADC_REG_ADSAME);
764b96952f4SArtur Rojek writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL);
765b96952f4SArtur Rojek writel(0, adc->base + JZ_ADC_REG_ADTCH);
766b96952f4SArtur Rojek
767b96952f4SArtur Rojek ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL,
768b96952f4SArtur Rojek JZ_ADC_REG_CFG_CMD_SEL);
769b96952f4SArtur Rojek ingenic_adc_set_adcmd(iio_dev, iio_dev->active_scan_mask[0]);
770b96952f4SArtur Rojek
771b96952f4SArtur Rojek ingenic_adc_enable(adc, 2, true);
772b96952f4SArtur Rojek
773b96952f4SArtur Rojek return 0;
774b96952f4SArtur Rojek }
775b96952f4SArtur Rojek
ingenic_adc_buffer_disable(struct iio_dev * iio_dev)776b96952f4SArtur Rojek static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev)
7771a78daeaSArtur Rojek {
778b96952f4SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
779b96952f4SArtur Rojek
780b96952f4SArtur Rojek ingenic_adc_enable(adc, 2, false);
781b96952f4SArtur Rojek
782b96952f4SArtur Rojek ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, 0);
783b96952f4SArtur Rojek
784b96952f4SArtur Rojek writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
785b96952f4SArtur Rojek writeb(0xff, adc->base + JZ_ADC_REG_STATUS);
786b96952f4SArtur Rojek ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0);
787b96952f4SArtur Rojek writew(0, adc->base + JZ_ADC_REG_ADSAME);
788b96952f4SArtur Rojek writew(0, adc->base + JZ_ADC_REG_ADWAIT);
789b96952f4SArtur Rojek clk_disable(adc->clk);
790b96952f4SArtur Rojek
791b96952f4SArtur Rojek return 0;
792b96952f4SArtur Rojek }
793b96952f4SArtur Rojek
794b96952f4SArtur Rojek static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops = {
795b96952f4SArtur Rojek .postenable = &ingenic_adc_buffer_enable,
796b96952f4SArtur Rojek .predisable = &ingenic_adc_buffer_disable
7971a78daeaSArtur Rojek };
7981a78daeaSArtur Rojek
ingenic_adc_irq(int irq,void * data)799b96952f4SArtur Rojek static irqreturn_t ingenic_adc_irq(int irq, void *data)
800b96952f4SArtur Rojek {
801b96952f4SArtur Rojek struct iio_dev *iio_dev = data;
802b96952f4SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev);
803b96952f4SArtur Rojek unsigned long mask = iio_dev->active_scan_mask[0];
804b96952f4SArtur Rojek unsigned int i;
805b96952f4SArtur Rojek u32 tdat[3];
806b96952f4SArtur Rojek
807b96952f4SArtur Rojek for (i = 0; i < ARRAY_SIZE(tdat); mask >>= 2, i++) {
808b96952f4SArtur Rojek if (mask & 0x3)
809b96952f4SArtur Rojek tdat[i] = readl(adc->base + JZ_ADC_REG_ADTCH);
810b96952f4SArtur Rojek else
811b96952f4SArtur Rojek tdat[i] = 0;
812b96952f4SArtur Rojek }
813b96952f4SArtur Rojek
814b96952f4SArtur Rojek iio_push_to_buffers(iio_dev, tdat);
815b96952f4SArtur Rojek writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS);
816b96952f4SArtur Rojek
817b96952f4SArtur Rojek return IRQ_HANDLED;
818b96952f4SArtur Rojek }
819b96952f4SArtur Rojek
ingenic_adc_probe(struct platform_device * pdev)8201a78daeaSArtur Rojek static int ingenic_adc_probe(struct platform_device *pdev)
8211a78daeaSArtur Rojek {
8221a78daeaSArtur Rojek struct device *dev = &pdev->dev;
8231a78daeaSArtur Rojek struct iio_dev *iio_dev;
8241a78daeaSArtur Rojek struct ingenic_adc *adc;
8251a78daeaSArtur Rojek const struct ingenic_adc_soc_data *soc_data;
826b96952f4SArtur Rojek int irq, ret;
8271a78daeaSArtur Rojek
8281a78daeaSArtur Rojek soc_data = device_get_match_data(dev);
8291a78daeaSArtur Rojek if (!soc_data)
8301a78daeaSArtur Rojek return -EINVAL;
8311a78daeaSArtur Rojek
8321a78daeaSArtur Rojek iio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
8331a78daeaSArtur Rojek if (!iio_dev)
8341a78daeaSArtur Rojek return -ENOMEM;
8351a78daeaSArtur Rojek
8361a78daeaSArtur Rojek adc = iio_priv(iio_dev);
8371a78daeaSArtur Rojek mutex_init(&adc->lock);
838a515d648SArtur Rojek mutex_init(&adc->aux_lock);
8391a78daeaSArtur Rojek adc->soc_data = soc_data;
8401a78daeaSArtur Rojek
841b96952f4SArtur Rojek irq = platform_get_irq(pdev, 0);
842b96952f4SArtur Rojek if (irq < 0)
843b96952f4SArtur Rojek return irq;
844b96952f4SArtur Rojek
845b96952f4SArtur Rojek ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0,
846b96952f4SArtur Rojek dev_name(dev), iio_dev);
847b96952f4SArtur Rojek if (ret < 0) {
848b96952f4SArtur Rojek dev_err(dev, "Failed to request irq: %d\n", ret);
849b96952f4SArtur Rojek return ret;
850b96952f4SArtur Rojek }
851b96952f4SArtur Rojek
852f449aa3eSJonathan Cameron adc->base = devm_platform_ioremap_resource(pdev, 0);
85376838a8fSWei Yongjun if (IS_ERR(adc->base))
8541a78daeaSArtur Rojek return PTR_ERR(adc->base);
8551a78daeaSArtur Rojek
856*51f2f910SUwe Kleine-König adc->clk = devm_clk_get_prepared(dev, "adc");
8571a78daeaSArtur Rojek if (IS_ERR(adc->clk)) {
8581a78daeaSArtur Rojek dev_err(dev, "Unable to get clock\n");
8591a78daeaSArtur Rojek return PTR_ERR(adc->clk);
8601a78daeaSArtur Rojek }
8611a78daeaSArtur Rojek
862*51f2f910SUwe Kleine-König ret = clk_enable(adc->clk);
8631a78daeaSArtur Rojek if (ret) {
8641a78daeaSArtur Rojek dev_err(dev, "Failed to enable clock\n");
8651a78daeaSArtur Rojek return ret;
8661a78daeaSArtur Rojek }
8671a78daeaSArtur Rojek
8685a304e1aSMaarten ter Huurne /* Set clock dividers. */
8695a304e1aSMaarten ter Huurne if (soc_data->init_clk_div) {
8705a304e1aSMaarten ter Huurne ret = soc_data->init_clk_div(dev, adc);
8715a304e1aSMaarten ter Huurne if (ret) {
8725a304e1aSMaarten ter Huurne clk_disable_unprepare(adc->clk);
8735a304e1aSMaarten ter Huurne return ret;
8745a304e1aSMaarten ter Huurne }
8755a304e1aSMaarten ter Huurne }
8765a304e1aSMaarten ter Huurne
8771a78daeaSArtur Rojek /* Put hardware in a known passive state. */
8781a78daeaSArtur Rojek writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
8791a78daeaSArtur Rojek writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
880bf1b2418SChristophe Branchereau
881bf1b2418SChristophe Branchereau /* JZ4760B specific */
882bf1b2418SChristophe Branchereau if (device_property_present(dev, "ingenic,use-internal-divider"))
883bf1b2418SChristophe Branchereau ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL,
884bf1b2418SChristophe Branchereau JZ_ADC_REG_CFG_VBAT_SEL);
885bf1b2418SChristophe Branchereau else
886bf1b2418SChristophe Branchereau ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, 0);
887bf1b2418SChristophe Branchereau
888a515d648SArtur Rojek usleep_range(2000, 3000); /* Must wait at least 2ms. */
8891a78daeaSArtur Rojek clk_disable(adc->clk);
8901a78daeaSArtur Rojek
8911a78daeaSArtur Rojek iio_dev->name = "jz-adc";
892b96952f4SArtur Rojek iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
893b96952f4SArtur Rojek iio_dev->setup_ops = &ingenic_buffer_setup_ops;
8946a294b41SPaul Cercueil iio_dev->channels = soc_data->channels;
8956a294b41SPaul Cercueil iio_dev->num_channels = soc_data->num_channels;
8961a78daeaSArtur Rojek iio_dev->info = &ingenic_adc_info;
8971a78daeaSArtur Rojek
8981a78daeaSArtur Rojek ret = devm_iio_device_register(dev, iio_dev);
8991a78daeaSArtur Rojek if (ret)
9001a78daeaSArtur Rojek dev_err(dev, "Unable to register IIO device\n");
9011a78daeaSArtur Rojek
9021a78daeaSArtur Rojek return ret;
9031a78daeaSArtur Rojek }
9041a78daeaSArtur Rojek
9051a78daeaSArtur Rojek static const struct of_device_id ingenic_adc_of_match[] = {
9061a78daeaSArtur Rojek { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
9071a78daeaSArtur Rojek { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
908b9e9bdd4SChristophe Branchereau { .compatible = "ingenic,jz4760-adc", .data = &jz4760_adc_soc_data, },
909bf1b2418SChristophe Branchereau { .compatible = "ingenic,jz4760b-adc", .data = &jz4760_adc_soc_data, },
910a515d648SArtur Rojek { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, },
9111a78daeaSArtur Rojek { },
9121a78daeaSArtur Rojek };
9131a78daeaSArtur Rojek MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
9141a78daeaSArtur Rojek
9151a78daeaSArtur Rojek static struct platform_driver ingenic_adc_driver = {
9161a78daeaSArtur Rojek .driver = {
9171a78daeaSArtur Rojek .name = "ingenic-adc",
918a07a4fe5SJonathan Cameron .of_match_table = ingenic_adc_of_match,
9191a78daeaSArtur Rojek },
9201a78daeaSArtur Rojek .probe = ingenic_adc_probe,
9211a78daeaSArtur Rojek };
9221a78daeaSArtur Rojek module_platform_driver(ingenic_adc_driver);
9231a78daeaSArtur Rojek MODULE_LICENSE("GPL v2");
924