History

iio: adc: Add driver for Marvell 88PM886 PMIC ADC

Marvell's 88PM886 PMIC has a so-called General Purpose ADC used for monitoring various system voltages and temperatures. Add the relevant register definitions to the MFD header and a driver for the ADC.

Duje Mihanović committed 1 year ago

Commit 3422b4b1 parent 0f06e285 files · +470 −0Browse files

Unified · Split

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