xref: /openbmc/linux/drivers/iio/adc/ingenic-adc.c (revision 51f2f910)
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