xref: /linux/drivers/iio/adc/88pm886-gpadc.c (revision 4f38da1f027ea2c9f01bb71daa7a299c191b6940)
1*3422b4bcSDuje Mihanović // SPDX-License-Identifier: GPL-2.0-only
2*3422b4bcSDuje Mihanović /*
3*3422b4bcSDuje Mihanović  * Copyright 2025, Duje Mihanović <duje@dujemihanovic.xyz>
4*3422b4bcSDuje Mihanović  */
5*3422b4bcSDuje Mihanović 
6*3422b4bcSDuje Mihanović #include <linux/bits.h>
7*3422b4bcSDuje Mihanović #include <linux/bug.h>
8*3422b4bcSDuje Mihanović #include <linux/delay.h>
9*3422b4bcSDuje Mihanović #include <linux/device.h>
10*3422b4bcSDuje Mihanović #include <linux/err.h>
11*3422b4bcSDuje Mihanović #include <linux/i2c.h>
12*3422b4bcSDuje Mihanović #include <linux/math.h>
13*3422b4bcSDuje Mihanović #include <linux/mod_devicetable.h>
14*3422b4bcSDuje Mihanović #include <linux/module.h>
15*3422b4bcSDuje Mihanović #include <linux/platform_device.h>
16*3422b4bcSDuje Mihanović #include <linux/pm_runtime.h>
17*3422b4bcSDuje Mihanović #include <linux/regmap.h>
18*3422b4bcSDuje Mihanović #include <linux/types.h>
19*3422b4bcSDuje Mihanović #include <linux/units.h>
20*3422b4bcSDuje Mihanović 
21*3422b4bcSDuje Mihanović #include <asm/byteorder.h>
22*3422b4bcSDuje Mihanović 
23*3422b4bcSDuje Mihanović #include <linux/iio/iio.h>
24*3422b4bcSDuje Mihanović #include <linux/iio/types.h>
25*3422b4bcSDuje Mihanović 
26*3422b4bcSDuje Mihanović #include <linux/mfd/88pm886.h>
27*3422b4bcSDuje Mihanović 
28*3422b4bcSDuje Mihanović struct pm886_gpadc {
29*3422b4bcSDuje Mihanović 	struct regmap *map;
30*3422b4bcSDuje Mihanović };
31*3422b4bcSDuje Mihanović 
32*3422b4bcSDuje Mihanović enum pm886_gpadc_channel {
33*3422b4bcSDuje Mihanović 	VSC_CHAN,
34*3422b4bcSDuje Mihanović 	VCHG_PWR_CHAN,
35*3422b4bcSDuje Mihanović 	VCF_OUT_CHAN,
36*3422b4bcSDuje Mihanović 	VBAT_CHAN,
37*3422b4bcSDuje Mihanović 	VBAT_SLP_CHAN,
38*3422b4bcSDuje Mihanović 	VBUS_CHAN,
39*3422b4bcSDuje Mihanović 
40*3422b4bcSDuje Mihanović 	GPADC0_CHAN,
41*3422b4bcSDuje Mihanović 	GPADC1_CHAN,
42*3422b4bcSDuje Mihanović 	GPADC2_CHAN,
43*3422b4bcSDuje Mihanović 	GPADC3_CHAN,
44*3422b4bcSDuje Mihanović 
45*3422b4bcSDuje Mihanović 	GND_DET1_CHAN,
46*3422b4bcSDuje Mihanović 	GND_DET2_CHAN,
47*3422b4bcSDuje Mihanović 	MIC_DET_CHAN,
48*3422b4bcSDuje Mihanović 
49*3422b4bcSDuje Mihanović 	TINT_CHAN,
50*3422b4bcSDuje Mihanović };
51*3422b4bcSDuje Mihanović 
52*3422b4bcSDuje Mihanović static const int pm886_gpadc_regs[] = {
53*3422b4bcSDuje Mihanović 	[VSC_CHAN] = PM886_REG_GPADC_VSC,
54*3422b4bcSDuje Mihanović 	[VCHG_PWR_CHAN] = PM886_REG_GPADC_VCHG_PWR,
55*3422b4bcSDuje Mihanović 	[VCF_OUT_CHAN] = PM886_REG_GPADC_VCF_OUT,
56*3422b4bcSDuje Mihanović 	[VBAT_CHAN] = PM886_REG_GPADC_VBAT,
57*3422b4bcSDuje Mihanović 	[VBAT_SLP_CHAN] = PM886_REG_GPADC_VBAT_SLP,
58*3422b4bcSDuje Mihanović 	[VBUS_CHAN] = PM886_REG_GPADC_VBUS,
59*3422b4bcSDuje Mihanović 
60*3422b4bcSDuje Mihanović 	[GPADC0_CHAN] = PM886_REG_GPADC_GPADC0,
61*3422b4bcSDuje Mihanović 	[GPADC1_CHAN] = PM886_REG_GPADC_GPADC1,
62*3422b4bcSDuje Mihanović 	[GPADC2_CHAN] = PM886_REG_GPADC_GPADC2,
63*3422b4bcSDuje Mihanović 	[GPADC3_CHAN] = PM886_REG_GPADC_GPADC3,
64*3422b4bcSDuje Mihanović 
65*3422b4bcSDuje Mihanović 	[GND_DET1_CHAN] = PM886_REG_GPADC_GND_DET1,
66*3422b4bcSDuje Mihanović 	[GND_DET2_CHAN] = PM886_REG_GPADC_GND_DET2,
67*3422b4bcSDuje Mihanović 	[MIC_DET_CHAN] = PM886_REG_GPADC_MIC_DET,
68*3422b4bcSDuje Mihanović 
69*3422b4bcSDuje Mihanović 	[TINT_CHAN] = PM886_REG_GPADC_TINT,
70*3422b4bcSDuje Mihanović };
71*3422b4bcSDuje Mihanović 
72*3422b4bcSDuje Mihanović #define ADC_CHANNEL_VOLTAGE(index, lsb, name)		\
73*3422b4bcSDuje Mihanović {							\
74*3422b4bcSDuje Mihanović 	.type = IIO_VOLTAGE,				\
75*3422b4bcSDuje Mihanović 	.indexed = 1,					\
76*3422b4bcSDuje Mihanović 	.channel = index,				\
77*3422b4bcSDuje Mihanović 	.address = lsb,					\
78*3422b4bcSDuje Mihanović 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\
79*3422b4bcSDuje Mihanović 			      BIT(IIO_CHAN_INFO_SCALE),	\
80*3422b4bcSDuje Mihanović 	.datasheet_name = name,				\
81*3422b4bcSDuje Mihanović }
82*3422b4bcSDuje Mihanović 
83*3422b4bcSDuje Mihanović #define ADC_CHANNEL_RESISTANCE(index, lsb, name)		\
84*3422b4bcSDuje Mihanović {								\
85*3422b4bcSDuje Mihanović 	.type = IIO_RESISTANCE,					\
86*3422b4bcSDuje Mihanović 	.indexed = 1,						\
87*3422b4bcSDuje Mihanović 	.channel = index,					\
88*3422b4bcSDuje Mihanović 	.address = lsb,						\
89*3422b4bcSDuje Mihanović 	.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),	\
90*3422b4bcSDuje Mihanović 	.datasheet_name = name,					\
91*3422b4bcSDuje Mihanović }
92*3422b4bcSDuje Mihanović 
93*3422b4bcSDuje Mihanović #define ADC_CHANNEL_TEMPERATURE(index, lsb, name)		\
94*3422b4bcSDuje Mihanović {								\
95*3422b4bcSDuje Mihanović 	.type = IIO_TEMP,					\
96*3422b4bcSDuje Mihanović 	.indexed = 1,						\
97*3422b4bcSDuje Mihanović 	.channel = index,					\
98*3422b4bcSDuje Mihanović 	.address = lsb,						\
99*3422b4bcSDuje Mihanović 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
100*3422b4bcSDuje Mihanović 			      BIT(IIO_CHAN_INFO_SCALE) |	\
101*3422b4bcSDuje Mihanović 			      BIT(IIO_CHAN_INFO_OFFSET),	\
102*3422b4bcSDuje Mihanović 	.datasheet_name = name,					\
103*3422b4bcSDuje Mihanović }
104*3422b4bcSDuje Mihanović 
105*3422b4bcSDuje Mihanović static const struct iio_chan_spec pm886_gpadc_channels[] = {
106*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(VSC_CHAN, 1367, "vsc"),
107*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(VCHG_PWR_CHAN, 1709, "vchg_pwr"),
108*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(VCF_OUT_CHAN, 1367, "vcf_out"),
109*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(VBAT_CHAN, 1367, "vbat"),
110*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(VBAT_SLP_CHAN, 1367, "vbat_slp"),
111*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(VBUS_CHAN, 1709, "vbus"),
112*3422b4bcSDuje Mihanović 
113*3422b4bcSDuje Mihanović 	ADC_CHANNEL_RESISTANCE(GPADC0_CHAN, 342, "gpadc0"),
114*3422b4bcSDuje Mihanović 	ADC_CHANNEL_RESISTANCE(GPADC1_CHAN, 342, "gpadc1"),
115*3422b4bcSDuje Mihanović 	ADC_CHANNEL_RESISTANCE(GPADC2_CHAN, 342, "gpadc2"),
116*3422b4bcSDuje Mihanović 	ADC_CHANNEL_RESISTANCE(GPADC3_CHAN, 342, "gpadc3"),
117*3422b4bcSDuje Mihanović 
118*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(GND_DET1_CHAN, 342, "gnddet1"),
119*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(GND_DET2_CHAN, 342, "gnddet2"),
120*3422b4bcSDuje Mihanović 	ADC_CHANNEL_VOLTAGE(MIC_DET_CHAN, 1367, "mic_det"),
121*3422b4bcSDuje Mihanović 
122*3422b4bcSDuje Mihanović 	ADC_CHANNEL_TEMPERATURE(TINT_CHAN, 104, "tint"),
123*3422b4bcSDuje Mihanović };
124*3422b4bcSDuje Mihanović 
125*3422b4bcSDuje Mihanović static const struct regmap_config pm886_gpadc_regmap_config = {
126*3422b4bcSDuje Mihanović 	.reg_bits = 8,
127*3422b4bcSDuje Mihanović 	.val_bits = 8,
128*3422b4bcSDuje Mihanović 	.max_register = PM886_GPADC_MAX_REGISTER,
129*3422b4bcSDuje Mihanović };
130*3422b4bcSDuje Mihanović 
131*3422b4bcSDuje Mihanović static int gpadc_get_raw(struct iio_dev *iio, enum pm886_gpadc_channel chan)
132*3422b4bcSDuje Mihanović {
133*3422b4bcSDuje Mihanović 	struct pm886_gpadc *gpadc = iio_priv(iio);
134*3422b4bcSDuje Mihanović 	__be16 buf;
135*3422b4bcSDuje Mihanović 	int ret;
136*3422b4bcSDuje Mihanović 
137*3422b4bcSDuje Mihanović 	ret = regmap_bulk_read(gpadc->map, pm886_gpadc_regs[chan], &buf, sizeof(buf));
138*3422b4bcSDuje Mihanović 	if (ret)
139*3422b4bcSDuje Mihanović 		return ret;
140*3422b4bcSDuje Mihanović 
141*3422b4bcSDuje Mihanović 	return be16_to_cpu(buf) >> 4;
142*3422b4bcSDuje Mihanović }
143*3422b4bcSDuje Mihanović 
144*3422b4bcSDuje Mihanović static int
145*3422b4bcSDuje Mihanović gpadc_set_bias(struct pm886_gpadc *gpadc, enum pm886_gpadc_channel chan, bool on)
146*3422b4bcSDuje Mihanović {
147*3422b4bcSDuje Mihanović 	unsigned int gpadc_num = chan - GPADC0_CHAN;
148*3422b4bcSDuje Mihanović 	unsigned int bits = BIT(gpadc_num + 4) | BIT(gpadc_num);
149*3422b4bcSDuje Mihanović 
150*3422b4bcSDuje Mihanović 	return regmap_assign_bits(gpadc->map, PM886_REG_GPADC_CONFIG(0x14), bits, on);
151*3422b4bcSDuje Mihanović }
152*3422b4bcSDuje Mihanović 
153*3422b4bcSDuje Mihanović static int
154*3422b4bcSDuje Mihanović gpadc_find_bias_current(struct iio_dev *iio, struct iio_chan_spec const *chan,
155*3422b4bcSDuje Mihanović 			unsigned int *raw_uV, unsigned int *raw_uA)
156*3422b4bcSDuje Mihanović {
157*3422b4bcSDuje Mihanović 	struct pm886_gpadc *gpadc = iio_priv(iio);
158*3422b4bcSDuje Mihanović 	unsigned int gpadc_num = chan->channel - GPADC0_CHAN;
159*3422b4bcSDuje Mihanović 	unsigned int reg = PM886_REG_GPADC_CONFIG(0xb + gpadc_num);
160*3422b4bcSDuje Mihanović 	unsigned long lsb = chan->address;
161*3422b4bcSDuje Mihanović 	int ret;
162*3422b4bcSDuje Mihanović 
163*3422b4bcSDuje Mihanović 	for (unsigned int i = 0; i < PM886_GPADC_BIAS_LEVELS; i++) {
164*3422b4bcSDuje Mihanović 		ret = regmap_update_bits(gpadc->map, reg, GENMASK(3, 0), i);
165*3422b4bcSDuje Mihanović 		if (ret)
166*3422b4bcSDuje Mihanović 			return ret;
167*3422b4bcSDuje Mihanović 
168*3422b4bcSDuje Mihanović 		/* Wait for the new bias level to apply. */
169*3422b4bcSDuje Mihanović 		fsleep(5 * USEC_PER_MSEC);
170*3422b4bcSDuje Mihanović 
171*3422b4bcSDuje Mihanović 		*raw_uA = PM886_GPADC_INDEX_TO_BIAS_uA(i);
172*3422b4bcSDuje Mihanović 		*raw_uV = gpadc_get_raw(iio, chan->channel) * lsb;
173*3422b4bcSDuje Mihanović 
174*3422b4bcSDuje Mihanović 		/*
175*3422b4bcSDuje Mihanović 		 * Vendor kernel errors out above 1.25 V, but testing shows
176*3422b4bcSDuje Mihanović 		 * that the resistance of the battery detection channel (GPADC2
177*3422b4bcSDuje Mihanović 		 * on coreprimevelte) reaches about 1.4 MΩ when the battery is
178*3422b4bcSDuje Mihanović 		 * removed, which can't be measured with such a low upper
179*3422b4bcSDuje Mihanović 		 * limit. Therefore, to be able to detect the battery without
180*3422b4bcSDuje Mihanović 		 * ugly externs as used in the vendor fuel gauge driver,
181*3422b4bcSDuje Mihanović 		 * increase this limit a bit.
182*3422b4bcSDuje Mihanović 		 */
183*3422b4bcSDuje Mihanović 		if (WARN_ON(*raw_uV > 1500 * (MICRO / MILLI)))
184*3422b4bcSDuje Mihanović 			return -EIO;
185*3422b4bcSDuje Mihanović 
186*3422b4bcSDuje Mihanović 		/*
187*3422b4bcSDuje Mihanović 		 * Vendor kernel errors out under 300 mV, but for the same
188*3422b4bcSDuje Mihanović 		 * reason as above (except the channel hovers around 3.5 kΩ
189*3422b4bcSDuje Mihanović 		 * with battery present) reduce this limit.
190*3422b4bcSDuje Mihanović 		 */
191*3422b4bcSDuje Mihanović 		if (*raw_uV < 200 * (MICRO / MILLI)) {
192*3422b4bcSDuje Mihanović 			dev_dbg(&iio->dev, "bad bias for chan %d: %d uA @ %d uV\n",
193*3422b4bcSDuje Mihanović 				chan->channel, *raw_uA, *raw_uV);
194*3422b4bcSDuje Mihanović 			continue;
195*3422b4bcSDuje Mihanović 		}
196*3422b4bcSDuje Mihanović 
197*3422b4bcSDuje Mihanović 		dev_dbg(&iio->dev, "good bias for chan %d: %d uA @ %d uV\n",
198*3422b4bcSDuje Mihanović 			chan->channel, *raw_uA, *raw_uV);
199*3422b4bcSDuje Mihanović 		return 0;
200*3422b4bcSDuje Mihanović 	}
201*3422b4bcSDuje Mihanović 
202*3422b4bcSDuje Mihanović 	dev_err(&iio->dev, "failed to find good bias for chan %d\n", chan->channel);
203*3422b4bcSDuje Mihanović 	return -EINVAL;
204*3422b4bcSDuje Mihanović }
205*3422b4bcSDuje Mihanović 
206*3422b4bcSDuje Mihanović static int
207*3422b4bcSDuje Mihanović gpadc_get_resistance_ohm(struct iio_dev *iio, struct iio_chan_spec const *chan)
208*3422b4bcSDuje Mihanović {
209*3422b4bcSDuje Mihanović 	struct pm886_gpadc *gpadc = iio_priv(iio);
210*3422b4bcSDuje Mihanović 	unsigned int raw_uV, raw_uA;
211*3422b4bcSDuje Mihanović 	int ret;
212*3422b4bcSDuje Mihanović 
213*3422b4bcSDuje Mihanović 	ret = gpadc_set_bias(gpadc, chan->channel, true);
214*3422b4bcSDuje Mihanović 	if (ret)
215*3422b4bcSDuje Mihanović 		goto out;
216*3422b4bcSDuje Mihanović 
217*3422b4bcSDuje Mihanović 	ret = gpadc_find_bias_current(iio, chan, &raw_uV, &raw_uA);
218*3422b4bcSDuje Mihanović 	if (ret)
219*3422b4bcSDuje Mihanović 		goto out;
220*3422b4bcSDuje Mihanović 
221*3422b4bcSDuje Mihanović 	ret = DIV_ROUND_CLOSEST(raw_uV, raw_uA);
222*3422b4bcSDuje Mihanović out:
223*3422b4bcSDuje Mihanović 	gpadc_set_bias(gpadc, chan->channel, false);
224*3422b4bcSDuje Mihanović 	return ret;
225*3422b4bcSDuje Mihanović }
226*3422b4bcSDuje Mihanović 
227*3422b4bcSDuje Mihanović static int
228*3422b4bcSDuje Mihanović __pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan,
229*3422b4bcSDuje Mihanović 		       int *val, int *val2, long mask)
230*3422b4bcSDuje Mihanović {
231*3422b4bcSDuje Mihanović 	unsigned long lsb = chan->address;
232*3422b4bcSDuje Mihanović 
233*3422b4bcSDuje Mihanović 	switch (mask) {
234*3422b4bcSDuje Mihanović 	case IIO_CHAN_INFO_RAW:
235*3422b4bcSDuje Mihanović 		*val = gpadc_get_raw(iio, chan->channel);
236*3422b4bcSDuje Mihanović 		if (*val < 0)
237*3422b4bcSDuje Mihanović 			return *val;
238*3422b4bcSDuje Mihanović 
239*3422b4bcSDuje Mihanović 		return IIO_VAL_INT;
240*3422b4bcSDuje Mihanović 	case IIO_CHAN_INFO_SCALE:
241*3422b4bcSDuje Mihanović 		*val = lsb;
242*3422b4bcSDuje Mihanović 
243*3422b4bcSDuje Mihanović 		if (chan->type == IIO_VOLTAGE) {
244*3422b4bcSDuje Mihanović 			*val2 = MILLI;
245*3422b4bcSDuje Mihanović 			return IIO_VAL_FRACTIONAL;
246*3422b4bcSDuje Mihanović 		} else {
247*3422b4bcSDuje Mihanović 			return IIO_VAL_INT;
248*3422b4bcSDuje Mihanović 		}
249*3422b4bcSDuje Mihanović 	case IIO_CHAN_INFO_OFFSET:
250*3422b4bcSDuje Mihanović 		/* Raw value is 104 millikelvin/LSB, convert it to 104 millicelsius/LSB */
251*3422b4bcSDuje Mihanović 		*val = ABSOLUTE_ZERO_MILLICELSIUS;
252*3422b4bcSDuje Mihanović 		*val2 = lsb;
253*3422b4bcSDuje Mihanović 		return IIO_VAL_FRACTIONAL;
254*3422b4bcSDuje Mihanović 	case IIO_CHAN_INFO_PROCESSED:
255*3422b4bcSDuje Mihanović 		*val = gpadc_get_resistance_ohm(iio, chan);
256*3422b4bcSDuje Mihanović 		if (*val < 0)
257*3422b4bcSDuje Mihanović 			return *val;
258*3422b4bcSDuje Mihanović 
259*3422b4bcSDuje Mihanović 		return IIO_VAL_INT;
260*3422b4bcSDuje Mihanović 	default:
261*3422b4bcSDuje Mihanović 		return -EINVAL;
262*3422b4bcSDuje Mihanović 	}
263*3422b4bcSDuje Mihanović }
264*3422b4bcSDuje Mihanović 
265*3422b4bcSDuje Mihanović static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan,
266*3422b4bcSDuje Mihanović 				int *val, int *val2, long mask)
267*3422b4bcSDuje Mihanović {
268*3422b4bcSDuje Mihanović 	struct device *dev = iio->dev.parent;
269*3422b4bcSDuje Mihanović 	int ret;
270*3422b4bcSDuje Mihanović 
271*3422b4bcSDuje Mihanović 	ret = pm_runtime_resume_and_get(dev);
272*3422b4bcSDuje Mihanović 	if (ret)
273*3422b4bcSDuje Mihanović 		return ret;
274*3422b4bcSDuje Mihanović 
275*3422b4bcSDuje Mihanović 	ret = __pm886_gpadc_read_raw(iio, chan, val, val2, mask);
276*3422b4bcSDuje Mihanović 
277*3422b4bcSDuje Mihanović 	pm_runtime_put_autosuspend(dev);
278*3422b4bcSDuje Mihanović 	return ret;
279*3422b4bcSDuje Mihanović }
280*3422b4bcSDuje Mihanović 
281*3422b4bcSDuje Mihanović static int pm886_gpadc_hw_enable(struct regmap *map)
282*3422b4bcSDuje Mihanović {
283*3422b4bcSDuje Mihanović 	const u8 config[] = {
284*3422b4bcSDuje Mihanović 		PM886_GPADC_CONFIG1_EN_ALL,
285*3422b4bcSDuje Mihanović 		PM886_GPADC_CONFIG2_EN_ALL,
286*3422b4bcSDuje Mihanović 		PM886_GPADC_GND_DET2_EN,
287*3422b4bcSDuje Mihanović 	};
288*3422b4bcSDuje Mihanović 	int ret;
289*3422b4bcSDuje Mihanović 
290*3422b4bcSDuje Mihanović 	/* Enable the ADC block. */
291*3422b4bcSDuje Mihanović 	ret = regmap_set_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0));
292*3422b4bcSDuje Mihanović 	if (ret)
293*3422b4bcSDuje Mihanović 		return ret;
294*3422b4bcSDuje Mihanović 
295*3422b4bcSDuje Mihanović 	/* Enable all channels. */
296*3422b4bcSDuje Mihanović 	return regmap_bulk_write(map, PM886_REG_GPADC_CONFIG(0x1), config, ARRAY_SIZE(config));
297*3422b4bcSDuje Mihanović }
298*3422b4bcSDuje Mihanović 
299*3422b4bcSDuje Mihanović static int pm886_gpadc_hw_disable(struct regmap *map)
300*3422b4bcSDuje Mihanović {
301*3422b4bcSDuje Mihanović 	return regmap_clear_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0));
302*3422b4bcSDuje Mihanović }
303*3422b4bcSDuje Mihanović 
304*3422b4bcSDuje Mihanović static const struct iio_info pm886_gpadc_iio_info = {
305*3422b4bcSDuje Mihanović 	.read_raw = pm886_gpadc_read_raw,
306*3422b4bcSDuje Mihanović };
307*3422b4bcSDuje Mihanović 
308*3422b4bcSDuje Mihanović static int pm886_gpadc_probe(struct platform_device *pdev)
309*3422b4bcSDuje Mihanović {
310*3422b4bcSDuje Mihanović 	struct device *dev = &pdev->dev;
311*3422b4bcSDuje Mihanović 	struct pm886_chip *chip = dev_get_drvdata(dev->parent);
312*3422b4bcSDuje Mihanović 	struct i2c_client *client = chip->client;
313*3422b4bcSDuje Mihanović 	struct pm886_gpadc *gpadc;
314*3422b4bcSDuje Mihanović 	struct i2c_client *page;
315*3422b4bcSDuje Mihanović 	struct iio_dev *iio;
316*3422b4bcSDuje Mihanović 	int ret;
317*3422b4bcSDuje Mihanović 
318*3422b4bcSDuje Mihanović 	iio = devm_iio_device_alloc(dev, sizeof(*gpadc));
319*3422b4bcSDuje Mihanović 	if (!iio)
320*3422b4bcSDuje Mihanović 		return -ENOMEM;
321*3422b4bcSDuje Mihanović 
322*3422b4bcSDuje Mihanović 	gpadc = iio_priv(iio);
323*3422b4bcSDuje Mihanović 	dev_set_drvdata(dev, iio);
324*3422b4bcSDuje Mihanović 
325*3422b4bcSDuje Mihanović 	page = devm_i2c_new_dummy_device(dev, client->adapter,
326*3422b4bcSDuje Mihanović 					 client->addr + PM886_PAGE_OFFSET_GPADC);
327*3422b4bcSDuje Mihanović 	if (IS_ERR(page))
328*3422b4bcSDuje Mihanović 		return dev_err_probe(dev, PTR_ERR(page), "Failed to initialize GPADC page\n");
329*3422b4bcSDuje Mihanović 
330*3422b4bcSDuje Mihanović 	gpadc->map = devm_regmap_init_i2c(page, &pm886_gpadc_regmap_config);
331*3422b4bcSDuje Mihanović 	if (IS_ERR(gpadc->map))
332*3422b4bcSDuje Mihanović 		return dev_err_probe(dev, PTR_ERR(gpadc->map),
333*3422b4bcSDuje Mihanović 				     "Failed to initialize GPADC regmap\n");
334*3422b4bcSDuje Mihanović 
335*3422b4bcSDuje Mihanović 	iio->name = "88pm886-gpadc";
336*3422b4bcSDuje Mihanović 	iio->modes = INDIO_DIRECT_MODE;
337*3422b4bcSDuje Mihanović 	iio->info = &pm886_gpadc_iio_info;
338*3422b4bcSDuje Mihanović 	iio->channels = pm886_gpadc_channels;
339*3422b4bcSDuje Mihanović 	iio->num_channels = ARRAY_SIZE(pm886_gpadc_channels);
340*3422b4bcSDuje Mihanović 	device_set_node(&iio->dev, dev_fwnode(dev->parent));
341*3422b4bcSDuje Mihanović 
342*3422b4bcSDuje Mihanović 	ret = devm_pm_runtime_enable(dev);
343*3422b4bcSDuje Mihanović 	if (ret)
344*3422b4bcSDuje Mihanović 		return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
345*3422b4bcSDuje Mihanović 
346*3422b4bcSDuje Mihanović 	pm_runtime_set_autosuspend_delay(dev, 50);
347*3422b4bcSDuje Mihanović 	pm_runtime_use_autosuspend(dev);
348*3422b4bcSDuje Mihanović 	ret = devm_iio_device_register(dev, iio);
349*3422b4bcSDuje Mihanović 	if (ret)
350*3422b4bcSDuje Mihanović 		return dev_err_probe(dev, ret, "Failed to register ADC\n");
351*3422b4bcSDuje Mihanović 
352*3422b4bcSDuje Mihanović 	return 0;
353*3422b4bcSDuje Mihanović }
354*3422b4bcSDuje Mihanović 
355*3422b4bcSDuje Mihanović static int pm886_gpadc_runtime_resume(struct device *dev)
356*3422b4bcSDuje Mihanović {
357*3422b4bcSDuje Mihanović 	struct iio_dev *iio = dev_get_drvdata(dev);
358*3422b4bcSDuje Mihanović 	struct pm886_gpadc *gpadc = iio_priv(iio);
359*3422b4bcSDuje Mihanović 
360*3422b4bcSDuje Mihanović 	return pm886_gpadc_hw_enable(gpadc->map);
361*3422b4bcSDuje Mihanović }
362*3422b4bcSDuje Mihanović 
363*3422b4bcSDuje Mihanović static int pm886_gpadc_runtime_suspend(struct device *dev)
364*3422b4bcSDuje Mihanović {
365*3422b4bcSDuje Mihanović 	struct iio_dev *iio = dev_get_drvdata(dev);
366*3422b4bcSDuje Mihanović 	struct pm886_gpadc *gpadc = iio_priv(iio);
367*3422b4bcSDuje Mihanović 
368*3422b4bcSDuje Mihanović 	return pm886_gpadc_hw_disable(gpadc->map);
369*3422b4bcSDuje Mihanović }
370*3422b4bcSDuje Mihanović 
371*3422b4bcSDuje Mihanović static DEFINE_RUNTIME_DEV_PM_OPS(pm886_gpadc_pm_ops,
372*3422b4bcSDuje Mihanović 				 pm886_gpadc_runtime_suspend,
373*3422b4bcSDuje Mihanović 				 pm886_gpadc_runtime_resume, NULL);
374*3422b4bcSDuje Mihanović 
375*3422b4bcSDuje Mihanović static const struct platform_device_id pm886_gpadc_id[] = {
376*3422b4bcSDuje Mihanović 	{ "88pm886-gpadc" },
377*3422b4bcSDuje Mihanović 	{ }
378*3422b4bcSDuje Mihanović };
379*3422b4bcSDuje Mihanović MODULE_DEVICE_TABLE(platform, pm886_gpadc_id);
380*3422b4bcSDuje Mihanović 
381*3422b4bcSDuje Mihanović static struct platform_driver pm886_gpadc_driver = {
382*3422b4bcSDuje Mihanović 	.driver = {
383*3422b4bcSDuje Mihanović 		.name = "88pm886-gpadc",
384*3422b4bcSDuje Mihanović 		.pm = pm_ptr(&pm886_gpadc_pm_ops),
385*3422b4bcSDuje Mihanović 	},
386*3422b4bcSDuje Mihanović 	.probe = pm886_gpadc_probe,
387*3422b4bcSDuje Mihanović 	.id_table = pm886_gpadc_id,
388*3422b4bcSDuje Mihanović };
389*3422b4bcSDuje Mihanović module_platform_driver(pm886_gpadc_driver);
390*3422b4bcSDuje Mihanović 
391*3422b4bcSDuje Mihanović MODULE_AUTHOR("Duje Mihanović <duje@dujemihanovic.xyz>");
392*3422b4bcSDuje Mihanović MODULE_DESCRIPTION("Marvell 88PM886 GPADC driver");
393*3422b4bcSDuje Mihanović MODULE_LICENSE("GPL");
394