xref: /openbmc/linux/drivers/iio/adc/lp8788_adc.c (revision b7b3c35e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * TI LP8788 MFD - ADC driver
4  *
5  * Copyright 2012 Texas Instruments
6  *
7  * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
8  */
9 
10 #include <linux/delay.h>
11 #include <linux/iio/iio.h>
12 #include <linux/iio/driver.h>
13 #include <linux/iio/machine.h>
14 #include <linux/mfd/lp8788.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/platform_device.h>
18 #include <linux/slab.h>
19 
20 /* register address */
21 #define LP8788_ADC_CONF			0x60
22 #define LP8788_ADC_RAW			0x61
23 #define LP8788_ADC_DONE			0x63
24 
25 #define ADC_CONV_START			1
26 
27 struct lp8788_adc {
28 	struct lp8788 *lp;
29 	struct iio_map *map;
30 	struct mutex lock;
31 };
32 
33 static const int lp8788_scale[LPADC_MAX] = {
34 	[LPADC_VBATT_5P5] = 1343101,
35 	[LPADC_VIN_CHG]   = 3052503,
36 	[LPADC_IBATT]     = 610500,
37 	[LPADC_IC_TEMP]   = 61050,
38 	[LPADC_VBATT_6P0] = 1465201,
39 	[LPADC_VBATT_5P0] = 1221001,
40 	[LPADC_ADC1]      = 610500,
41 	[LPADC_ADC2]      = 610500,
42 	[LPADC_VDD]       = 1025641,
43 	[LPADC_VCOIN]     = 757020,
44 	[LPADC_ADC3]      = 610500,
45 	[LPADC_ADC4]      = 610500,
46 };
47 
48 static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
49 				int *val)
50 {
51 	unsigned int msb;
52 	unsigned int lsb;
53 	unsigned int result;
54 	u8 data;
55 	u8 rawdata[2];
56 	int size = ARRAY_SIZE(rawdata);
57 	int retry = 5;
58 	int ret;
59 
60 	data = (id << 1) | ADC_CONV_START;
61 	ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
62 	if (ret)
63 		goto err_io;
64 
65 	/* retry until adc conversion is done */
66 	data = 0;
67 	while (retry--) {
68 		usleep_range(100, 200);
69 
70 		ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
71 		if (ret)
72 			goto err_io;
73 
74 		/* conversion done */
75 		if (data)
76 			break;
77 	}
78 
79 	ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
80 	if (ret)
81 		goto err_io;
82 
83 	msb = (rawdata[0] << 4) & 0x00000ff0;
84 	lsb = (rawdata[1] >> 4) & 0x0000000f;
85 	result = msb | lsb;
86 	*val = result;
87 
88 	return 0;
89 
90 err_io:
91 	return ret;
92 }
93 
94 static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
95 			struct iio_chan_spec const *chan,
96 			int *val, int *val2, long mask)
97 {
98 	struct lp8788_adc *adc = iio_priv(indio_dev);
99 	enum lp8788_adc_id id = chan->channel;
100 	int ret;
101 
102 	mutex_lock(&adc->lock);
103 
104 	switch (mask) {
105 	case IIO_CHAN_INFO_RAW:
106 		ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
107 		break;
108 	case IIO_CHAN_INFO_SCALE:
109 		*val = lp8788_scale[id] / 1000000;
110 		*val2 = lp8788_scale[id] % 1000000;
111 		ret = IIO_VAL_INT_PLUS_MICRO;
112 		break;
113 	default:
114 		ret = -EINVAL;
115 		break;
116 	}
117 
118 	mutex_unlock(&adc->lock);
119 
120 	return ret;
121 }
122 
123 static const struct iio_info lp8788_adc_info = {
124 	.read_raw = &lp8788_adc_read_raw,
125 };
126 
127 #define LP8788_CHAN(_id, _type) {				\
128 		.type = _type,					\
129 		.indexed = 1,					\
130 		.channel = LPADC_##_id,				\
131 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\
132 			BIT(IIO_CHAN_INFO_SCALE),		\
133 		.datasheet_name = #_id,				\
134 }
135 
136 static const struct iio_chan_spec lp8788_adc_channels[] = {
137 	[LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
138 	[LPADC_VIN_CHG]   = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
139 	[LPADC_IBATT]     = LP8788_CHAN(IBATT, IIO_CURRENT),
140 	[LPADC_IC_TEMP]   = LP8788_CHAN(IC_TEMP, IIO_TEMP),
141 	[LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
142 	[LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
143 	[LPADC_ADC1]      = LP8788_CHAN(ADC1, IIO_VOLTAGE),
144 	[LPADC_ADC2]      = LP8788_CHAN(ADC2, IIO_VOLTAGE),
145 	[LPADC_VDD]       = LP8788_CHAN(VDD, IIO_VOLTAGE),
146 	[LPADC_VCOIN]     = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
147 	[LPADC_ADC3]      = LP8788_CHAN(ADC3, IIO_VOLTAGE),
148 	[LPADC_ADC4]      = LP8788_CHAN(ADC4, IIO_VOLTAGE),
149 };
150 
151 /* default maps used by iio consumer (lp8788-charger driver) */
152 static struct iio_map lp8788_default_iio_maps[] = {
153 	{
154 		.consumer_dev_name = "lp8788-charger",
155 		.consumer_channel = "lp8788_vbatt_5p0",
156 		.adc_channel_label = "VBATT_5P0",
157 	},
158 	{
159 		.consumer_dev_name = "lp8788-charger",
160 		.consumer_channel = "lp8788_adc1",
161 		.adc_channel_label = "ADC1",
162 	},
163 	{ }
164 };
165 
166 static int lp8788_iio_map_register(struct device *dev,
167 				struct iio_dev *indio_dev,
168 				struct lp8788_platform_data *pdata,
169 				struct lp8788_adc *adc)
170 {
171 	struct iio_map *map;
172 	int ret;
173 
174 	map = (!pdata || !pdata->adc_pdata) ?
175 		lp8788_default_iio_maps : pdata->adc_pdata;
176 
177 	ret = devm_iio_map_array_register(dev, indio_dev, map);
178 	if (ret) {
179 		dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
180 		return ret;
181 	}
182 
183 	adc->map = map;
184 	return 0;
185 }
186 
187 static int lp8788_adc_probe(struct platform_device *pdev)
188 {
189 	struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
190 	struct iio_dev *indio_dev;
191 	struct lp8788_adc *adc;
192 	int ret;
193 
194 	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
195 	if (!indio_dev)
196 		return -ENOMEM;
197 
198 	adc = iio_priv(indio_dev);
199 	adc->lp = lp;
200 
201 	ret = lp8788_iio_map_register(&pdev->dev, indio_dev, lp->pdata, adc);
202 	if (ret)
203 		return ret;
204 
205 	mutex_init(&adc->lock);
206 
207 	indio_dev->name = pdev->name;
208 	indio_dev->modes = INDIO_DIRECT_MODE;
209 	indio_dev->info = &lp8788_adc_info;
210 	indio_dev->channels = lp8788_adc_channels;
211 	indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
212 
213 	return devm_iio_device_register(&pdev->dev, indio_dev);
214 }
215 
216 static struct platform_driver lp8788_adc_driver = {
217 	.probe = lp8788_adc_probe,
218 	.driver = {
219 		.name = LP8788_DEV_ADC,
220 	},
221 };
222 module_platform_driver(lp8788_adc_driver);
223 
224 MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
225 MODULE_AUTHOR("Milo Kim");
226 MODULE_LICENSE("GPL");
227 MODULE_ALIAS("platform:lp8788-adc");
228