]>
Commit | Line | Data |
---|---|---|
dae2db30 AM |
1 | /* |
2 | * Copyright (C) ST-Ericsson SA 2010 | |
3 | * | |
4 | * License Terms: GNU General Public License v2 | |
5 | * Author: Arun R Murthy <[email protected]> | |
6 | */ | |
7 | #include <linux/init.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/device.h> | |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/spinlock.h> | |
12 | #include <linux/delay.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/completion.h> | |
15 | #include <linux/regulator/consumer.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/mfd/ab8500.h> | |
19 | #include <linux/mfd/abx500.h> | |
cf169439 | 20 | #include <linux/mfd/ab8500/ab8500-gpadc.h> |
dae2db30 AM |
21 | |
22 | /* | |
23 | * GPADC register offsets | |
24 | * Bank : 0x0A | |
25 | */ | |
26 | #define AB8500_GPADC_CTRL1_REG 0x00 | |
27 | #define AB8500_GPADC_CTRL2_REG 0x01 | |
28 | #define AB8500_GPADC_CTRL3_REG 0x02 | |
29 | #define AB8500_GPADC_AUTO_TIMER_REG 0x03 | |
30 | #define AB8500_GPADC_STAT_REG 0x04 | |
31 | #define AB8500_GPADC_MANDATAL_REG 0x05 | |
32 | #define AB8500_GPADC_MANDATAH_REG 0x06 | |
33 | #define AB8500_GPADC_AUTODATAL_REG 0x07 | |
34 | #define AB8500_GPADC_AUTODATAH_REG 0x08 | |
35 | #define AB8500_GPADC_MUX_CTRL_REG 0x09 | |
36 | ||
37 | /* gpadc constants */ | |
38 | #define EN_VINTCORE12 0x04 | |
39 | #define EN_VTVOUT 0x02 | |
40 | #define EN_GPADC 0x01 | |
41 | #define DIS_GPADC 0x00 | |
42 | #define SW_AVG_16 0x60 | |
43 | #define ADC_SW_CONV 0x04 | |
44 | #define EN_BUF 0x40 | |
45 | #define DIS_ZERO 0x00 | |
46 | #define GPADC_BUSY 0x01 | |
47 | ||
48 | /** | |
49 | * struct ab8500_gpadc - ab8500 GPADC device information | |
50 | * @dev: pointer to the struct device | |
51 | * @parent: pointer to the parent device structure ab8500 | |
52 | * @ab8500_gpadc_complete: pointer to the struct completion, to indicate | |
53 | * the completion of gpadc conversion | |
54 | * @ab8500_gpadc_lock: structure of type mutex | |
55 | * @regu: pointer to the struct regulator | |
56 | * @irq: interrupt number that is used by gpadc | |
57 | */ | |
58 | static struct ab8500_gpadc { | |
59 | struct device *dev; | |
60 | struct ab8500 *parent; | |
61 | struct completion ab8500_gpadc_complete; | |
62 | struct mutex ab8500_gpadc_lock; | |
63 | struct regulator *regu; | |
64 | int irq; | |
65 | } *di; | |
66 | ||
67 | /** | |
68 | * ab8500_gpadc_convert() - gpadc conversion | |
69 | * @input: analog input to be converted to digital data | |
70 | * | |
71 | * This function converts the selected analog i/p to digital | |
72 | * data. Thereafter calibration has to be made to obtain the | |
73 | * data in the required quantity measurement. | |
74 | */ | |
75 | int ab8500_gpadc_convert(u8 input) | |
76 | { | |
77 | int ret; | |
78 | u16 data = 0; | |
79 | int looplimit = 0; | |
80 | u8 val, low_data, high_data; | |
81 | ||
82 | if (!di) | |
83 | return -ENODEV; | |
84 | ||
85 | mutex_lock(&di->ab8500_gpadc_lock); | |
86 | /* Enable VTVout LDO this is required for GPADC */ | |
87 | regulator_enable(di->regu); | |
88 | ||
89 | /* Check if ADC is not busy, lock and proceed */ | |
90 | do { | |
91 | ret = abx500_get_register_interruptible(di->dev, AB8500_GPADC, | |
92 | AB8500_GPADC_STAT_REG, &val); | |
93 | if (ret < 0) | |
94 | goto out; | |
95 | if (!(val & GPADC_BUSY)) | |
96 | break; | |
97 | msleep(10); | |
98 | } while (++looplimit < 10); | |
99 | if (looplimit >= 10 && (val & GPADC_BUSY)) { | |
100 | dev_err(di->dev, "gpadc_conversion: GPADC busy"); | |
101 | ret = -EINVAL; | |
102 | goto out; | |
103 | } | |
104 | ||
105 | /* Enable GPADC */ | |
106 | ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_GPADC, | |
107 | AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC); | |
108 | if (ret < 0) { | |
109 | dev_err(di->dev, "gpadc_conversion: enable gpadc failed\n"); | |
110 | goto out; | |
111 | } | |
112 | /* Select the input source and set average samples to 16 */ | |
113 | ret = abx500_set_register_interruptible(di->dev, AB8500_GPADC, | |
114 | AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); | |
115 | if (ret < 0) { | |
116 | dev_err(di->dev, | |
117 | "gpadc_conversion: set avg samples failed\n"); | |
118 | goto out; | |
119 | } | |
120 | /* Enable ADC, Buffering and select rising edge, start Conversion */ | |
121 | ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_GPADC, | |
122 | AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); | |
123 | if (ret < 0) { | |
124 | dev_err(di->dev, | |
125 | "gpadc_conversion: select falling edge failed\n"); | |
126 | goto out; | |
127 | } | |
128 | ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_GPADC, | |
129 | AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV); | |
130 | if (ret < 0) { | |
131 | dev_err(di->dev, | |
132 | "gpadc_conversion: start s/w conversion failed\n"); | |
133 | goto out; | |
134 | } | |
135 | /* wait for completion of conversion */ | |
136 | if (!wait_for_completion_timeout(&di->ab8500_gpadc_complete, 2*HZ)) { | |
137 | dev_err(di->dev, | |
138 | "timeout: didnt recieve GPADC conversion interrupt\n"); | |
139 | ret = -EINVAL; | |
140 | goto out; | |
141 | } | |
142 | ||
143 | /* Read the converted RAW data */ | |
144 | ret = abx500_get_register_interruptible(di->dev, AB8500_GPADC, | |
145 | AB8500_GPADC_MANDATAL_REG, &low_data); | |
146 | if (ret < 0) { | |
147 | dev_err(di->dev, "gpadc_conversion: read low data failed\n"); | |
148 | goto out; | |
149 | } | |
150 | ||
151 | ret = abx500_get_register_interruptible(di->dev, AB8500_GPADC, | |
152 | AB8500_GPADC_MANDATAH_REG, &high_data); | |
153 | if (ret < 0) { | |
154 | dev_err(di->dev, "gpadc_conversion: read high data failed\n"); | |
155 | goto out; | |
156 | } | |
157 | ||
158 | data = (high_data << 8) | low_data; | |
159 | /* Disable GPADC */ | |
160 | ret = abx500_set_register_interruptible(di->dev, AB8500_GPADC, | |
161 | AB8500_GPADC_CTRL1_REG, DIS_GPADC); | |
162 | if (ret < 0) { | |
163 | dev_err(di->dev, "gpadc_conversion: disable gpadc failed\n"); | |
164 | goto out; | |
165 | } | |
166 | /* Disable VTVout LDO this is required for GPADC */ | |
167 | regulator_disable(di->regu); | |
168 | mutex_unlock(&di->ab8500_gpadc_lock); | |
169 | return data; | |
170 | ||
171 | out: | |
172 | /* | |
173 | * It has shown to be needed to turn off the GPADC if an error occurs, | |
174 | * otherwise we might have problem when waiting for the busy bit in the | |
175 | * GPADC status register to go low. In V1.1 there wait_for_completion | |
176 | * seems to timeout when waiting for an interrupt.. Not seen in V2.0 | |
177 | */ | |
178 | (void) abx500_set_register_interruptible(di->dev, AB8500_GPADC, | |
179 | AB8500_GPADC_CTRL1_REG, DIS_GPADC); | |
180 | regulator_disable(di->regu); | |
181 | mutex_unlock(&di->ab8500_gpadc_lock); | |
182 | dev_err(di->dev, "gpadc_conversion: Failed to AD convert channel %d\n", | |
183 | input); | |
184 | return ret; | |
185 | } | |
186 | EXPORT_SYMBOL(ab8500_gpadc_convert); | |
187 | ||
188 | /** | |
189 | * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion | |
190 | * @irq: irq number | |
191 | * @data: pointer to the data passed during request irq | |
192 | * | |
193 | * This is a interrupt service routine for s/w gpadc conversion completion. | |
194 | * Notifies the gpadc completion is completed and the converted raw value | |
195 | * can be read from the registers. | |
196 | * Returns IRQ status(IRQ_HANDLED) | |
197 | */ | |
198 | static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_di) | |
199 | { | |
200 | struct ab8500_gpadc *gpadc = _di; | |
201 | ||
202 | complete(&gpadc->ab8500_gpadc_complete); | |
203 | ||
204 | return IRQ_HANDLED; | |
205 | } | |
206 | ||
207 | static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) | |
208 | { | |
209 | int ret = 0; | |
210 | struct ab8500_gpadc *gpadc; | |
211 | ||
212 | gpadc = kzalloc(sizeof(struct ab8500_gpadc), GFP_KERNEL); | |
213 | if (!gpadc) { | |
214 | dev_err(&pdev->dev, "Error: No memory\n"); | |
215 | return -ENOMEM; | |
216 | } | |
217 | ||
218 | gpadc->parent = dev_get_drvdata(pdev->dev.parent); | |
219 | gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); | |
220 | if (gpadc->irq < 0) { | |
221 | dev_err(gpadc->dev, "failed to get platform irq-%d\n", di->irq); | |
222 | ret = gpadc->irq; | |
223 | goto fail; | |
224 | } | |
225 | ||
226 | gpadc->dev = &pdev->dev; | |
227 | mutex_init(&di->ab8500_gpadc_lock); | |
228 | ||
229 | /* Initialize completion used to notify completion of conversion */ | |
230 | init_completion(&gpadc->ab8500_gpadc_complete); | |
231 | ||
232 | /* Register interrupt - SwAdcComplete */ | |
233 | ret = request_threaded_irq(gpadc->irq, NULL, | |
234 | ab8500_bm_gpswadcconvend_handler, | |
235 | IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc); | |
236 | if (ret < 0) { | |
237 | dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n", | |
238 | gpadc->irq); | |
239 | goto fail; | |
240 | } | |
241 | ||
242 | /* VTVout LDO used to power up ab8500-GPADC */ | |
243 | gpadc->regu = regulator_get(&pdev->dev, "vddadc"); | |
244 | if (IS_ERR(gpadc->regu)) { | |
245 | ret = PTR_ERR(gpadc->regu); | |
246 | dev_err(gpadc->dev, "failed to get vtvout LDO\n"); | |
247 | goto fail; | |
248 | } | |
249 | di = gpadc; | |
250 | dev_dbg(gpadc->dev, "probe success\n"); | |
251 | return 0; | |
252 | fail: | |
253 | kfree(gpadc); | |
254 | gpadc = NULL; | |
255 | return ret; | |
256 | } | |
257 | ||
258 | static int __devexit ab8500_gpadc_remove(struct platform_device *pdev) | |
259 | { | |
260 | struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev); | |
261 | ||
262 | /* remove interrupt - completion of Sw ADC conversion */ | |
263 | free_irq(gpadc->irq, di); | |
264 | /* disable VTVout LDO that is being used by GPADC */ | |
265 | regulator_put(gpadc->regu); | |
266 | kfree(gpadc); | |
267 | gpadc = NULL; | |
268 | return 0; | |
269 | } | |
270 | ||
271 | static struct platform_driver ab8500_gpadc_driver = { | |
272 | .probe = ab8500_gpadc_probe, | |
273 | .remove = __devexit_p(ab8500_gpadc_remove), | |
274 | .driver = { | |
275 | .name = "ab8500-gpadc", | |
276 | .owner = THIS_MODULE, | |
277 | }, | |
278 | }; | |
279 | ||
280 | static int __init ab8500_gpadc_init(void) | |
281 | { | |
282 | return platform_driver_register(&ab8500_gpadc_driver); | |
283 | } | |
284 | ||
285 | static void __exit ab8500_gpadc_exit(void) | |
286 | { | |
287 | platform_driver_unregister(&ab8500_gpadc_driver); | |
288 | } | |
289 | ||
290 | subsys_initcall_sync(ab8500_gpadc_init); | |
291 | module_exit(ab8500_gpadc_exit); | |
292 | ||
293 | MODULE_LICENSE("GPL v2"); | |
294 | MODULE_AUTHOR("Arun R Murthy"); | |
295 | MODULE_ALIAS("platform:ab8500_gpadc"); | |
296 | MODULE_DESCRIPTION("AB8500 GPADC driver"); |