Commit | Line | Data |
---|---|---|
08c3e06a BR |
1 | /* NXP PCF50633 ADC Driver |
2 | * | |
3 | * (C) 2006-2008 by Openmoko, Inc. | |
4 | * Author: Balaji Rao <balajirrao@openmoko.org> | |
5 | * All rights reserved. | |
6 | * | |
7 | * Broken down from monstrous PCF50633 driver mainly by | |
8 | * Harald Welte, Andy Green and Werner Almesberger | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
15 | * NOTE: This driver does not yet support subtractive ADC mode, which means | |
16 | * you can do only one measurement per read request. | |
17 | */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/init.h> | |
22 | #include <linux/device.h> | |
23 | #include <linux/platform_device.h> | |
24 | #include <linux/completion.h> | |
25 | ||
26 | #include <linux/mfd/pcf50633/core.h> | |
27 | #include <linux/mfd/pcf50633/adc.h> | |
28 | ||
29 | struct pcf50633_adc_request { | |
30 | int mux; | |
31 | int avg; | |
32 | int result; | |
33 | void (*callback)(struct pcf50633 *, void *, int); | |
34 | void *callback_param; | |
35 | ||
36 | /* Used in case of sync requests */ | |
37 | struct completion completion; | |
38 | ||
39 | }; | |
40 | ||
41 | #define PCF50633_MAX_ADC_FIFO_DEPTH 8 | |
42 | ||
43 | struct pcf50633_adc { | |
44 | struct pcf50633 *pcf; | |
45 | ||
46 | /* Private stuff */ | |
47 | struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH]; | |
48 | int queue_head; | |
49 | int queue_tail; | |
50 | struct mutex queue_mutex; | |
51 | }; | |
52 | ||
53 | static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf) | |
54 | { | |
55 | return platform_get_drvdata(pcf->adc_pdev); | |
56 | } | |
57 | ||
58 | static void adc_setup(struct pcf50633 *pcf, int channel, int avg) | |
59 | { | |
60 | channel &= PCF50633_ADCC1_ADCMUX_MASK; | |
61 | ||
62 | /* kill ratiometric, but enable ACCSW biasing */ | |
63 | pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00); | |
64 | pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01); | |
65 | ||
66 | /* start ADC conversion on selected channel */ | |
67 | pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg | | |
68 | PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT); | |
69 | } | |
70 | ||
71 | static void trigger_next_adc_job_if_any(struct pcf50633 *pcf) | |
72 | { | |
73 | struct pcf50633_adc *adc = __to_adc(pcf); | |
74 | int head; | |
75 | ||
76 | mutex_lock(&adc->queue_mutex); | |
77 | ||
78 | head = adc->queue_head; | |
79 | ||
80 | if (!adc->queue[head]) { | |
81 | mutex_unlock(&adc->queue_mutex); | |
82 | return; | |
83 | } | |
84 | mutex_unlock(&adc->queue_mutex); | |
85 | ||
86 | adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg); | |
87 | } | |
88 | ||
89 | static int | |
90 | adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req) | |
91 | { | |
92 | struct pcf50633_adc *adc = __to_adc(pcf); | |
93 | int head, tail; | |
94 | ||
95 | mutex_lock(&adc->queue_mutex); | |
96 | ||
97 | head = adc->queue_head; | |
98 | tail = adc->queue_tail; | |
99 | ||
100 | if (adc->queue[tail]) { | |
101 | mutex_unlock(&adc->queue_mutex); | |
102 | return -EBUSY; | |
103 | } | |
104 | ||
105 | adc->queue[tail] = req; | |
106 | adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1); | |
107 | ||
108 | mutex_unlock(&adc->queue_mutex); | |
109 | ||
110 | trigger_next_adc_job_if_any(pcf); | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | static void | |
116 | pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result) | |
117 | { | |
118 | struct pcf50633_adc_request *req = param; | |
119 | ||
120 | req->result = result; | |
121 | complete(&req->completion); | |
122 | } | |
123 | ||
124 | int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg) | |
125 | { | |
126 | struct pcf50633_adc_request *req; | |
127 | ||
128 | /* req is freed when the result is ready, in interrupt handler */ | |
129 | req = kzalloc(sizeof(*req), GFP_KERNEL); | |
130 | if (!req) | |
131 | return -ENOMEM; | |
132 | ||
133 | req->mux = mux; | |
134 | req->avg = avg; | |
135 | req->callback = pcf50633_adc_sync_read_callback; | |
136 | req->callback_param = req; | |
137 | ||
138 | init_completion(&req->completion); | |
139 | adc_enqueue_request(pcf, req); | |
140 | wait_for_completion(&req->completion); | |
141 | ||
142 | return req->result; | |
143 | } | |
144 | EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read); | |
145 | ||
146 | int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg, | |
147 | void (*callback)(struct pcf50633 *, void *, int), | |
148 | void *callback_param) | |
149 | { | |
150 | struct pcf50633_adc_request *req; | |
151 | ||
152 | /* req is freed when the result is ready, in interrupt handler */ | |
153 | req = kmalloc(sizeof(*req), GFP_KERNEL); | |
154 | if (!req) | |
155 | return -ENOMEM; | |
156 | ||
157 | req->mux = mux; | |
158 | req->avg = avg; | |
159 | req->callback = callback; | |
160 | req->callback_param = callback_param; | |
161 | ||
162 | adc_enqueue_request(pcf, req); | |
163 | ||
164 | return 0; | |
165 | } | |
166 | EXPORT_SYMBOL_GPL(pcf50633_adc_async_read); | |
167 | ||
168 | static int adc_result(struct pcf50633 *pcf) | |
169 | { | |
170 | u8 adcs1, adcs3; | |
171 | u16 result; | |
172 | ||
173 | adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1); | |
174 | adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3); | |
175 | result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK); | |
176 | ||
177 | dev_dbg(pcf->dev, "adc result = %d\n", result); | |
178 | ||
179 | return result; | |
180 | } | |
181 | ||
182 | static void pcf50633_adc_irq(int irq, void *data) | |
183 | { | |
184 | struct pcf50633_adc *adc = data; | |
185 | struct pcf50633 *pcf = adc->pcf; | |
186 | struct pcf50633_adc_request *req; | |
187 | int head; | |
188 | ||
189 | mutex_lock(&adc->queue_mutex); | |
190 | head = adc->queue_head; | |
191 | ||
192 | req = adc->queue[head]; | |
193 | if (WARN_ON(!req)) { | |
194 | dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n"); | |
195 | mutex_unlock(&adc->queue_mutex); | |
196 | return; | |
197 | } | |
198 | adc->queue[head] = NULL; | |
199 | adc->queue_head = (head + 1) & | |
200 | (PCF50633_MAX_ADC_FIFO_DEPTH - 1); | |
201 | ||
202 | mutex_unlock(&adc->queue_mutex); | |
203 | ||
204 | req->callback(pcf, req->callback_param, adc_result(pcf)); | |
205 | kfree(req); | |
206 | ||
207 | trigger_next_adc_job_if_any(pcf); | |
208 | } | |
209 | ||
210 | static int __devinit pcf50633_adc_probe(struct platform_device *pdev) | |
211 | { | |
212 | struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data; | |
213 | struct pcf50633_adc *adc; | |
214 | ||
215 | adc = kzalloc(sizeof(*adc), GFP_KERNEL); | |
216 | if (!adc) | |
217 | return -ENOMEM; | |
218 | ||
219 | adc->pcf = pdata->pcf; | |
220 | platform_set_drvdata(pdev, adc); | |
221 | ||
222 | pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ADCRDY, | |
223 | pcf50633_adc_irq, adc); | |
224 | ||
225 | mutex_init(&adc->queue_mutex); | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
230 | static int __devexit pcf50633_adc_remove(struct platform_device *pdev) | |
231 | { | |
232 | struct pcf50633_adc *adc = platform_get_drvdata(pdev); | |
233 | int i, head; | |
234 | ||
235 | pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY); | |
236 | ||
237 | mutex_lock(&adc->queue_mutex); | |
238 | head = adc->queue_head; | |
239 | ||
240 | if (WARN_ON(adc->queue[head])) | |
241 | dev_err(adc->pcf->dev, | |
242 | "adc driver removed with request pending\n"); | |
243 | ||
244 | for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++) | |
245 | kfree(adc->queue[i]); | |
246 | ||
247 | mutex_unlock(&adc->queue_mutex); | |
248 | kfree(adc); | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
253 | static struct platform_driver pcf50633_adc_driver = { | |
254 | .driver = { | |
255 | .name = "pcf50633-adc", | |
256 | }, | |
257 | .probe = pcf50633_adc_probe, | |
258 | .remove = __devexit_p(pcf50633_adc_remove), | |
259 | }; | |
260 | ||
261 | static int __init pcf50633_adc_init(void) | |
262 | { | |
263 | return platform_driver_register(&pcf50633_adc_driver); | |
264 | } | |
265 | module_init(pcf50633_adc_init); | |
266 | ||
267 | static void __exit pcf50633_adc_exit(void) | |
268 | { | |
269 | platform_driver_unregister(&pcf50633_adc_driver); | |
270 | } | |
271 | module_exit(pcf50633_adc_exit); | |
272 | ||
273 | MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); | |
274 | MODULE_DESCRIPTION("PCF50633 adc driver"); | |
275 | MODULE_LICENSE("GPL"); | |
276 | MODULE_ALIAS("platform:pcf50633-adc"); | |
277 |