File Index
Files and directories
Sign in
FILE

88pm886-gpadc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/adc/88pm886-gpadc.c C
  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
 28struct pm886_gpadc {
 29	struct regmap *map;
 30};
 31
 32enum 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
 52static 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
105static 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
125static 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
131static 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
144static int
145gpadc_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
153static int
154gpadc_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
206static int
207gpadc_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);
222out:
223	gpadc_set_bias(gpadc, chan->channel, false);
224	return ret;
225}
226
227static 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
265static 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
281static 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
299static int pm886_gpadc_hw_disable(struct regmap *map)
300{
301	return regmap_clear_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0));
302}
303
304static const struct iio_info pm886_gpadc_iio_info = {
305	.read_raw = pm886_gpadc_read_raw,
306};
307
308static 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
355static 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
363static 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
371static DEFINE_RUNTIME_DEV_PM_OPS(pm886_gpadc_pm_ops,
372				 pm886_gpadc_runtime_suspend,
373				 pm886_gpadc_runtime_resume, NULL);
374
375static const struct platform_device_id pm886_gpadc_id[] = {
376	{ "88pm886-gpadc" },
377	{ }
378};
379MODULE_DEVICE_TABLE(platform, pm886_gpadc_id);
380
381static 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};
389module_platform_driver(pm886_gpadc_driver);
390
391MODULE_AUTHOR("Duje Mihanović <duje@dujemihanovic.xyz>");
392MODULE_DESCRIPTION("Marvell 88PM886 GPADC driver");
393MODULE_LICENSE("GPL");
~/ Read-only file preview.