diff options
-rw-r--r-- | drivers/iio/adc/Kconfig | 6 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/adc/lp8788_adc.c | 264 |
3 files changed, 271 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 03791a6de349..492758120338 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig | |||
@@ -54,4 +54,10 @@ config AT91_ADC | |||
54 | help | 54 | help |
55 | Say yes here to build support for Atmel AT91 ADC. | 55 | Say yes here to build support for Atmel AT91 ADC. |
56 | 56 | ||
57 | config LP8788_ADC | ||
58 | bool "LP8788 ADC driver" | ||
59 | depends on MFD_LP8788 | ||
60 | help | ||
61 | Say yes here to build support for TI LP8788 ADC. | ||
62 | |||
57 | endmenu | 63 | endmenu |
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 9824a70f4fd8..900995d5e179 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile | |||
@@ -7,3 +7,4 @@ obj-$(CONFIG_AD7266) += ad7266.o | |||
7 | obj-$(CONFIG_AD7476) += ad7476.o | 7 | obj-$(CONFIG_AD7476) += ad7476.o |
8 | obj-$(CONFIG_AD7791) += ad7791.o | 8 | obj-$(CONFIG_AD7791) += ad7791.o |
9 | obj-$(CONFIG_AT91_ADC) += at91_adc.o | 9 | obj-$(CONFIG_AT91_ADC) += at91_adc.o |
10 | obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o | ||
diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c new file mode 100644 index 000000000000..a93aaf0bb841 --- /dev/null +++ b/drivers/iio/adc/lp8788_adc.c | |||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | * TI LP8788 MFD - ADC driver | ||
3 | * | ||
4 | * Copyright 2012 Texas Instruments | ||
5 | * | ||
6 | * Author: Milo(Woogyom) Kim <milo.kim@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | #include <linux/iio/iio.h> | ||
15 | #include <linux/iio/driver.h> | ||
16 | #include <linux/iio/machine.h> | ||
17 | #include <linux/mfd/lp8788.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/mutex.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | /* register address */ | ||
24 | #define LP8788_ADC_CONF 0x60 | ||
25 | #define LP8788_ADC_RAW 0x61 | ||
26 | #define LP8788_ADC_DONE 0x63 | ||
27 | |||
28 | #define ADC_CONV_START 1 | ||
29 | |||
30 | struct lp8788_adc { | ||
31 | struct lp8788 *lp; | ||
32 | struct iio_map *map; | ||
33 | struct mutex lock; | ||
34 | }; | ||
35 | |||
36 | static const int lp8788_scale[LPADC_MAX] = { | ||
37 | [LPADC_VBATT_5P5] = 1343101, | ||
38 | [LPADC_VIN_CHG] = 3052503, | ||
39 | [LPADC_IBATT] = 610500, | ||
40 | [LPADC_IC_TEMP] = 61050, | ||
41 | [LPADC_VBATT_6P0] = 1465201, | ||
42 | [LPADC_VBATT_5P0] = 1221001, | ||
43 | [LPADC_ADC1] = 610500, | ||
44 | [LPADC_ADC2] = 610500, | ||
45 | [LPADC_VDD] = 1025641, | ||
46 | [LPADC_VCOIN] = 757020, | ||
47 | [LPADC_ADC3] = 610500, | ||
48 | [LPADC_ADC4] = 610500, | ||
49 | }; | ||
50 | |||
51 | static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id, | ||
52 | int *val) | ||
53 | { | ||
54 | unsigned int msb; | ||
55 | unsigned int lsb; | ||
56 | unsigned int result; | ||
57 | u8 data; | ||
58 | u8 rawdata[2]; | ||
59 | int size = ARRAY_SIZE(rawdata); | ||
60 | int retry = 5; | ||
61 | int ret; | ||
62 | |||
63 | data = (id << 1) | ADC_CONV_START; | ||
64 | ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data); | ||
65 | if (ret) | ||
66 | goto err_io; | ||
67 | |||
68 | /* retry until adc conversion is done */ | ||
69 | data = 0; | ||
70 | while (retry--) { | ||
71 | usleep_range(100, 200); | ||
72 | |||
73 | ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data); | ||
74 | if (ret) | ||
75 | goto err_io; | ||
76 | |||
77 | /* conversion done */ | ||
78 | if (data) | ||
79 | break; | ||
80 | } | ||
81 | |||
82 | ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size); | ||
83 | if (ret) | ||
84 | goto err_io; | ||
85 | |||
86 | msb = (rawdata[0] << 4) & 0x00000ff0; | ||
87 | lsb = (rawdata[1] >> 4) & 0x0000000f; | ||
88 | result = msb | lsb; | ||
89 | *val = result; | ||
90 | |||
91 | return 0; | ||
92 | |||
93 | err_io: | ||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | static int lp8788_adc_read_raw(struct iio_dev *indio_dev, | ||
98 | struct iio_chan_spec const *chan, | ||
99 | int *val, int *val2, long mask) | ||
100 | { | ||
101 | struct lp8788_adc *adc = iio_priv(indio_dev); | ||
102 | enum lp8788_adc_id id = chan->channel; | ||
103 | int ret; | ||
104 | |||
105 | mutex_lock(&adc->lock); | ||
106 | |||
107 | switch (mask) { | ||
108 | case IIO_CHAN_INFO_RAW: | ||
109 | ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT; | ||
110 | break; | ||
111 | case IIO_CHAN_INFO_SCALE: | ||
112 | *val = lp8788_scale[id] / 1000000; | ||
113 | *val2 = lp8788_scale[id] % 1000000; | ||
114 | ret = IIO_VAL_INT_PLUS_MICRO; | ||
115 | break; | ||
116 | default: | ||
117 | ret = -EINVAL; | ||
118 | break; | ||
119 | } | ||
120 | |||
121 | mutex_unlock(&adc->lock); | ||
122 | |||
123 | return ret; | ||
124 | } | ||
125 | |||
126 | static const struct iio_info lp8788_adc_info = { | ||
127 | .read_raw = &lp8788_adc_read_raw, | ||
128 | .driver_module = THIS_MODULE, | ||
129 | }; | ||
130 | |||
131 | #define LP8788_CHAN(_id, _type) { \ | ||
132 | .type = _type, \ | ||
133 | .indexed = 1, \ | ||
134 | .channel = LPADC_##_id, \ | ||
135 | .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ | ||
136 | IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ | ||
137 | .datasheet_name = #_id, \ | ||
138 | } | ||
139 | |||
140 | static const struct iio_chan_spec lp8788_adc_channels[] = { | ||
141 | [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE), | ||
142 | [LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE), | ||
143 | [LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT), | ||
144 | [LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP), | ||
145 | [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE), | ||
146 | [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE), | ||
147 | [LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE), | ||
148 | [LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE), | ||
149 | [LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE), | ||
150 | [LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE), | ||
151 | [LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE), | ||
152 | [LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE), | ||
153 | }; | ||
154 | |||
155 | /* default maps used by iio consumer (lp8788-charger driver) */ | ||
156 | static struct iio_map lp8788_default_iio_maps[] = { | ||
157 | { | ||
158 | .consumer_dev_name = "lp8788-charger", | ||
159 | .consumer_channel = "lp8788_vbatt_5p0", | ||
160 | .adc_channel_label = "VBATT_5P0", | ||
161 | }, | ||
162 | { | ||
163 | .consumer_dev_name = "lp8788-charger", | ||
164 | .consumer_channel = "lp8788_adc1", | ||
165 | .adc_channel_label = "ADC1", | ||
166 | }, | ||
167 | { } | ||
168 | }; | ||
169 | |||
170 | static int lp8788_iio_map_register(struct iio_dev *indio_dev, | ||
171 | struct lp8788_platform_data *pdata, | ||
172 | struct lp8788_adc *adc) | ||
173 | { | ||
174 | struct iio_map *map; | ||
175 | int ret; | ||
176 | |||
177 | map = (!pdata || !pdata->adc_pdata) ? | ||
178 | lp8788_default_iio_maps : pdata->adc_pdata; | ||
179 | |||
180 | ret = iio_map_array_register(indio_dev, map); | ||
181 | if (ret) { | ||
182 | dev_err(adc->lp->dev, "iio map err: %d\n", ret); | ||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | adc->map = map; | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static inline void lp8788_iio_map_unregister(struct iio_dev *indio_dev, | ||
191 | struct lp8788_adc *adc) | ||
192 | { | ||
193 | iio_map_array_unregister(indio_dev, adc->map); | ||
194 | } | ||
195 | |||
196 | static int __devinit lp8788_adc_probe(struct platform_device *pdev) | ||
197 | { | ||
198 | struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); | ||
199 | struct iio_dev *indio_dev; | ||
200 | struct lp8788_adc *adc; | ||
201 | int ret; | ||
202 | |||
203 | indio_dev = iio_device_alloc(sizeof(*adc)); | ||
204 | if (!indio_dev) | ||
205 | return -ENOMEM; | ||
206 | |||
207 | adc = iio_priv(indio_dev); | ||
208 | adc->lp = lp; | ||
209 | platform_set_drvdata(pdev, indio_dev); | ||
210 | |||
211 | ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc); | ||
212 | if (ret) | ||
213 | goto err_iio_map; | ||
214 | |||
215 | mutex_init(&adc->lock); | ||
216 | |||
217 | indio_dev->dev.parent = lp->dev; | ||
218 | indio_dev->name = pdev->name; | ||
219 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
220 | indio_dev->info = &lp8788_adc_info; | ||
221 | indio_dev->channels = lp8788_adc_channels; | ||
222 | indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels); | ||
223 | |||
224 | ret = iio_device_register(indio_dev); | ||
225 | if (ret) { | ||
226 | dev_err(lp->dev, "iio dev register err: %d\n", ret); | ||
227 | goto err_iio_device; | ||
228 | } | ||
229 | |||
230 | return 0; | ||
231 | |||
232 | err_iio_device: | ||
233 | lp8788_iio_map_unregister(indio_dev, adc); | ||
234 | err_iio_map: | ||
235 | iio_device_free(indio_dev); | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | static int __devexit lp8788_adc_remove(struct platform_device *pdev) | ||
240 | { | ||
241 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
242 | struct lp8788_adc *adc = iio_priv(indio_dev); | ||
243 | |||
244 | iio_device_unregister(indio_dev); | ||
245 | lp8788_iio_map_unregister(indio_dev, adc); | ||
246 | iio_device_free(indio_dev); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static struct platform_driver lp8788_adc_driver = { | ||
252 | .probe = lp8788_adc_probe, | ||
253 | .remove = __devexit_p(lp8788_adc_remove), | ||
254 | .driver = { | ||
255 | .name = LP8788_DEV_ADC, | ||
256 | .owner = THIS_MODULE, | ||
257 | }, | ||
258 | }; | ||
259 | module_platform_driver(lp8788_adc_driver); | ||
260 | |||
261 | MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver"); | ||
262 | MODULE_AUTHOR("Milo Kim"); | ||
263 | MODULE_LICENSE("GPL"); | ||
264 | MODULE_ALIAS("platform:lp8788-adc"); | ||