diff options
Diffstat (limited to 'drivers/mfd/ezx-pcap.c')
-rw-r--r-- | drivers/mfd/ezx-pcap.c | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c new file mode 100644 index 000000000000..671a7efe86a8 --- /dev/null +++ b/drivers/mfd/ezx-pcap.c | |||
@@ -0,0 +1,505 @@ | |||
1 | /* | ||
2 | * Driver for Motorola PCAP2 as present in EZX phones | ||
3 | * | ||
4 | * Copyright (C) 2006 Harald Welte <laforge@openezx.org> | ||
5 | * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/irq.h> | ||
18 | #include <linux/mfd/ezx-pcap.h> | ||
19 | #include <linux/spi/spi.h> | ||
20 | |||
21 | #define PCAP_ADC_MAXQ 8 | ||
22 | struct pcap_adc_request { | ||
23 | u8 bank; | ||
24 | u8 ch[2]; | ||
25 | u32 flags; | ||
26 | void (*callback)(void *, u16[]); | ||
27 | void *data; | ||
28 | }; | ||
29 | |||
30 | struct pcap_adc_sync_request { | ||
31 | u16 res[2]; | ||
32 | struct completion completion; | ||
33 | }; | ||
34 | |||
35 | struct pcap_chip { | ||
36 | struct spi_device *spi; | ||
37 | |||
38 | /* IO */ | ||
39 | u32 buf; | ||
40 | struct mutex io_mutex; | ||
41 | |||
42 | /* IRQ */ | ||
43 | unsigned int irq_base; | ||
44 | u32 msr; | ||
45 | struct work_struct isr_work; | ||
46 | struct work_struct msr_work; | ||
47 | struct workqueue_struct *workqueue; | ||
48 | |||
49 | /* ADC */ | ||
50 | struct pcap_adc_request *adc_queue[PCAP_ADC_MAXQ]; | ||
51 | u8 adc_head; | ||
52 | u8 adc_tail; | ||
53 | struct mutex adc_mutex; | ||
54 | }; | ||
55 | |||
56 | /* IO */ | ||
57 | static int ezx_pcap_putget(struct pcap_chip *pcap, u32 *data) | ||
58 | { | ||
59 | struct spi_transfer t; | ||
60 | struct spi_message m; | ||
61 | int status; | ||
62 | |||
63 | memset(&t, 0, sizeof t); | ||
64 | spi_message_init(&m); | ||
65 | t.len = sizeof(u32); | ||
66 | spi_message_add_tail(&t, &m); | ||
67 | |||
68 | pcap->buf = *data; | ||
69 | t.tx_buf = (u8 *) &pcap->buf; | ||
70 | t.rx_buf = (u8 *) &pcap->buf; | ||
71 | status = spi_sync(pcap->spi, &m); | ||
72 | |||
73 | if (status == 0) | ||
74 | *data = pcap->buf; | ||
75 | |||
76 | return status; | ||
77 | } | ||
78 | |||
79 | int ezx_pcap_write(struct pcap_chip *pcap, u8 reg_num, u32 value) | ||
80 | { | ||
81 | int ret; | ||
82 | |||
83 | mutex_lock(&pcap->io_mutex); | ||
84 | value &= PCAP_REGISTER_VALUE_MASK; | ||
85 | value |= PCAP_REGISTER_WRITE_OP_BIT | ||
86 | | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); | ||
87 | ret = ezx_pcap_putget(pcap, &value); | ||
88 | mutex_unlock(&pcap->io_mutex); | ||
89 | |||
90 | return ret; | ||
91 | } | ||
92 | EXPORT_SYMBOL_GPL(ezx_pcap_write); | ||
93 | |||
94 | int ezx_pcap_read(struct pcap_chip *pcap, u8 reg_num, u32 *value) | ||
95 | { | ||
96 | int ret; | ||
97 | |||
98 | mutex_lock(&pcap->io_mutex); | ||
99 | *value = PCAP_REGISTER_READ_OP_BIT | ||
100 | | (reg_num << PCAP_REGISTER_ADDRESS_SHIFT); | ||
101 | |||
102 | ret = ezx_pcap_putget(pcap, value); | ||
103 | mutex_unlock(&pcap->io_mutex); | ||
104 | |||
105 | return ret; | ||
106 | } | ||
107 | EXPORT_SYMBOL_GPL(ezx_pcap_read); | ||
108 | |||
109 | /* IRQ */ | ||
110 | static inline unsigned int irq2pcap(struct pcap_chip *pcap, int irq) | ||
111 | { | ||
112 | return 1 << (irq - pcap->irq_base); | ||
113 | } | ||
114 | |||
115 | int pcap_to_irq(struct pcap_chip *pcap, int irq) | ||
116 | { | ||
117 | return pcap->irq_base + irq; | ||
118 | } | ||
119 | EXPORT_SYMBOL_GPL(pcap_to_irq); | ||
120 | |||
121 | static void pcap_mask_irq(unsigned int irq) | ||
122 | { | ||
123 | struct pcap_chip *pcap = get_irq_chip_data(irq); | ||
124 | |||
125 | pcap->msr |= irq2pcap(pcap, irq); | ||
126 | queue_work(pcap->workqueue, &pcap->msr_work); | ||
127 | } | ||
128 | |||
129 | static void pcap_unmask_irq(unsigned int irq) | ||
130 | { | ||
131 | struct pcap_chip *pcap = get_irq_chip_data(irq); | ||
132 | |||
133 | pcap->msr &= ~irq2pcap(pcap, irq); | ||
134 | queue_work(pcap->workqueue, &pcap->msr_work); | ||
135 | } | ||
136 | |||
137 | static struct irq_chip pcap_irq_chip = { | ||
138 | .name = "pcap", | ||
139 | .mask = pcap_mask_irq, | ||
140 | .unmask = pcap_unmask_irq, | ||
141 | }; | ||
142 | |||
143 | static void pcap_msr_work(struct work_struct *work) | ||
144 | { | ||
145 | struct pcap_chip *pcap = container_of(work, struct pcap_chip, msr_work); | ||
146 | |||
147 | ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr); | ||
148 | } | ||
149 | |||
150 | static void pcap_isr_work(struct work_struct *work) | ||
151 | { | ||
152 | struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work); | ||
153 | struct pcap_platform_data *pdata = pcap->spi->dev.platform_data; | ||
154 | u32 msr, isr, int_sel, service; | ||
155 | int irq; | ||
156 | |||
157 | ezx_pcap_read(pcap, PCAP_REG_MSR, &msr); | ||
158 | ezx_pcap_read(pcap, PCAP_REG_ISR, &isr); | ||
159 | |||
160 | /* We cant service/ack irqs that are assigned to port 2 */ | ||
161 | if (!(pdata->config & PCAP_SECOND_PORT)) { | ||
162 | ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel); | ||
163 | isr &= ~int_sel; | ||
164 | } | ||
165 | ezx_pcap_write(pcap, PCAP_REG_ISR, isr); | ||
166 | |||
167 | local_irq_disable(); | ||
168 | service = isr & ~msr; | ||
169 | |||
170 | for (irq = pcap->irq_base; service; service >>= 1, irq++) { | ||
171 | if (service & 1) { | ||
172 | struct irq_desc *desc = irq_to_desc(irq); | ||
173 | |||
174 | if (WARN(!desc, KERN_WARNING | ||
175 | "Invalid PCAP IRQ %d\n", irq)) | ||
176 | break; | ||
177 | |||
178 | if (desc->status & IRQ_DISABLED) | ||
179 | note_interrupt(irq, desc, IRQ_NONE); | ||
180 | else | ||
181 | desc->handle_irq(irq, desc); | ||
182 | } | ||
183 | } | ||
184 | local_irq_enable(); | ||
185 | } | ||
186 | |||
187 | static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc) | ||
188 | { | ||
189 | struct pcap_chip *pcap = get_irq_data(irq); | ||
190 | |||
191 | desc->chip->ack(irq); | ||
192 | queue_work(pcap->workqueue, &pcap->isr_work); | ||
193 | return; | ||
194 | } | ||
195 | |||
196 | /* ADC */ | ||
197 | static void pcap_disable_adc(struct pcap_chip *pcap) | ||
198 | { | ||
199 | u32 tmp; | ||
200 | |||
201 | ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp); | ||
202 | tmp &= ~(PCAP_ADC_ADEN|PCAP_ADC_BATT_I_ADC|PCAP_ADC_BATT_I_POLARITY); | ||
203 | ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); | ||
204 | } | ||
205 | |||
206 | static void pcap_adc_trigger(struct pcap_chip *pcap) | ||
207 | { | ||
208 | u32 tmp; | ||
209 | u8 head; | ||
210 | |||
211 | mutex_lock(&pcap->adc_mutex); | ||
212 | head = pcap->adc_head; | ||
213 | if (!pcap->adc_queue[head]) { | ||
214 | /* queue is empty, save power */ | ||
215 | pcap_disable_adc(pcap); | ||
216 | mutex_unlock(&pcap->adc_mutex); | ||
217 | return; | ||
218 | } | ||
219 | mutex_unlock(&pcap->adc_mutex); | ||
220 | |||
221 | /* start conversion on requested bank */ | ||
222 | tmp = pcap->adc_queue[head]->flags | PCAP_ADC_ADEN; | ||
223 | |||
224 | if (pcap->adc_queue[head]->bank == PCAP_ADC_BANK_1) | ||
225 | tmp |= PCAP_ADC_AD_SEL1; | ||
226 | |||
227 | ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); | ||
228 | ezx_pcap_write(pcap, PCAP_REG_ADR, PCAP_ADR_ASC); | ||
229 | } | ||
230 | |||
231 | static irqreturn_t pcap_adc_irq(int irq, void *_pcap) | ||
232 | { | ||
233 | struct pcap_chip *pcap = _pcap; | ||
234 | struct pcap_adc_request *req; | ||
235 | u16 res[2]; | ||
236 | u32 tmp; | ||
237 | |||
238 | mutex_lock(&pcap->adc_mutex); | ||
239 | req = pcap->adc_queue[pcap->adc_head]; | ||
240 | |||
241 | if (WARN(!req, KERN_WARNING "adc irq without pending request\n")) | ||
242 | return IRQ_HANDLED; | ||
243 | |||
244 | /* read requested channels results */ | ||
245 | ezx_pcap_read(pcap, PCAP_REG_ADC, &tmp); | ||
246 | tmp &= ~(PCAP_ADC_ADA1_MASK | PCAP_ADC_ADA2_MASK); | ||
247 | tmp |= (req->ch[0] << PCAP_ADC_ADA1_SHIFT); | ||
248 | tmp |= (req->ch[1] << PCAP_ADC_ADA2_SHIFT); | ||
249 | ezx_pcap_write(pcap, PCAP_REG_ADC, tmp); | ||
250 | ezx_pcap_read(pcap, PCAP_REG_ADR, &tmp); | ||
251 | res[0] = (tmp & PCAP_ADR_ADD1_MASK) >> PCAP_ADR_ADD1_SHIFT; | ||
252 | res[1] = (tmp & PCAP_ADR_ADD2_MASK) >> PCAP_ADR_ADD2_SHIFT; | ||
253 | |||
254 | pcap->adc_queue[pcap->adc_head] = NULL; | ||
255 | pcap->adc_head = (pcap->adc_head + 1) & (PCAP_ADC_MAXQ - 1); | ||
256 | mutex_unlock(&pcap->adc_mutex); | ||
257 | |||
258 | /* pass the results and release memory */ | ||
259 | req->callback(req->data, res); | ||
260 | kfree(req); | ||
261 | |||
262 | /* trigger next conversion (if any) on queue */ | ||
263 | pcap_adc_trigger(pcap); | ||
264 | |||
265 | return IRQ_HANDLED; | ||
266 | } | ||
267 | |||
268 | int pcap_adc_async(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[], | ||
269 | void *callback, void *data) | ||
270 | { | ||
271 | struct pcap_adc_request *req; | ||
272 | |||
273 | /* This will be freed after we have a result */ | ||
274 | req = kmalloc(sizeof(struct pcap_adc_request), GFP_KERNEL); | ||
275 | if (!req) | ||
276 | return -ENOMEM; | ||
277 | |||
278 | req->bank = bank; | ||
279 | req->flags = flags; | ||
280 | req->ch[0] = ch[0]; | ||
281 | req->ch[1] = ch[1]; | ||
282 | req->callback = callback; | ||
283 | req->data = data; | ||
284 | |||
285 | mutex_lock(&pcap->adc_mutex); | ||
286 | if (pcap->adc_queue[pcap->adc_tail]) { | ||
287 | mutex_unlock(&pcap->adc_mutex); | ||
288 | kfree(req); | ||
289 | return -EBUSY; | ||
290 | } | ||
291 | pcap->adc_queue[pcap->adc_tail] = req; | ||
292 | pcap->adc_tail = (pcap->adc_tail + 1) & (PCAP_ADC_MAXQ - 1); | ||
293 | mutex_unlock(&pcap->adc_mutex); | ||
294 | |||
295 | /* start conversion */ | ||
296 | pcap_adc_trigger(pcap); | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | EXPORT_SYMBOL_GPL(pcap_adc_async); | ||
301 | |||
302 | static void pcap_adc_sync_cb(void *param, u16 res[]) | ||
303 | { | ||
304 | struct pcap_adc_sync_request *req = param; | ||
305 | |||
306 | req->res[0] = res[0]; | ||
307 | req->res[1] = res[1]; | ||
308 | complete(&req->completion); | ||
309 | } | ||
310 | |||
311 | int pcap_adc_sync(struct pcap_chip *pcap, u8 bank, u32 flags, u8 ch[], | ||
312 | u16 res[]) | ||
313 | { | ||
314 | struct pcap_adc_sync_request sync_data; | ||
315 | int ret; | ||
316 | |||
317 | init_completion(&sync_data.completion); | ||
318 | ret = pcap_adc_async(pcap, bank, flags, ch, pcap_adc_sync_cb, | ||
319 | &sync_data); | ||
320 | if (ret) | ||
321 | return ret; | ||
322 | wait_for_completion(&sync_data.completion); | ||
323 | res[0] = sync_data.res[0]; | ||
324 | res[1] = sync_data.res[1]; | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | EXPORT_SYMBOL_GPL(pcap_adc_sync); | ||
329 | |||
330 | /* subdevs */ | ||
331 | static int pcap_remove_subdev(struct device *dev, void *unused) | ||
332 | { | ||
333 | platform_device_unregister(to_platform_device(dev)); | ||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static int __devinit pcap_add_subdev(struct pcap_chip *pcap, | ||
338 | struct pcap_subdev *subdev) | ||
339 | { | ||
340 | struct platform_device *pdev; | ||
341 | |||
342 | pdev = platform_device_alloc(subdev->name, subdev->id); | ||
343 | pdev->dev.parent = &pcap->spi->dev; | ||
344 | pdev->dev.platform_data = subdev->platform_data; | ||
345 | platform_set_drvdata(pdev, pcap); | ||
346 | |||
347 | return platform_device_add(pdev); | ||
348 | } | ||
349 | |||
350 | static int __devexit ezx_pcap_remove(struct spi_device *spi) | ||
351 | { | ||
352 | struct pcap_chip *pcap = dev_get_drvdata(&spi->dev); | ||
353 | struct pcap_platform_data *pdata = spi->dev.platform_data; | ||
354 | int i, adc_irq; | ||
355 | |||
356 | /* remove all registered subdevs */ | ||
357 | device_for_each_child(&spi->dev, NULL, pcap_remove_subdev); | ||
358 | |||
359 | /* cleanup ADC */ | ||
360 | adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ? | ||
361 | PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); | ||
362 | free_irq(adc_irq, pcap); | ||
363 | mutex_lock(&pcap->adc_mutex); | ||
364 | for (i = 0; i < PCAP_ADC_MAXQ; i++) | ||
365 | kfree(pcap->adc_queue[i]); | ||
366 | mutex_unlock(&pcap->adc_mutex); | ||
367 | |||
368 | /* cleanup irqchip */ | ||
369 | for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) | ||
370 | set_irq_chip_and_handler(i, NULL, NULL); | ||
371 | |||
372 | destroy_workqueue(pcap->workqueue); | ||
373 | |||
374 | kfree(pcap); | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int __devinit ezx_pcap_probe(struct spi_device *spi) | ||
380 | { | ||
381 | struct pcap_platform_data *pdata = spi->dev.platform_data; | ||
382 | struct pcap_chip *pcap; | ||
383 | int i, adc_irq; | ||
384 | int ret = -ENODEV; | ||
385 | |||
386 | /* platform data is required */ | ||
387 | if (!pdata) | ||
388 | goto ret; | ||
389 | |||
390 | pcap = kzalloc(sizeof(*pcap), GFP_KERNEL); | ||
391 | if (!pcap) { | ||
392 | ret = -ENOMEM; | ||
393 | goto ret; | ||
394 | } | ||
395 | |||
396 | mutex_init(&pcap->io_mutex); | ||
397 | mutex_init(&pcap->adc_mutex); | ||
398 | INIT_WORK(&pcap->isr_work, pcap_isr_work); | ||
399 | INIT_WORK(&pcap->msr_work, pcap_msr_work); | ||
400 | dev_set_drvdata(&spi->dev, pcap); | ||
401 | |||
402 | /* setup spi */ | ||
403 | spi->bits_per_word = 32; | ||
404 | spi->mode = SPI_MODE_0 | (pdata->config & PCAP_CS_AH ? SPI_CS_HIGH : 0); | ||
405 | ret = spi_setup(spi); | ||
406 | if (ret) | ||
407 | goto free_pcap; | ||
408 | |||
409 | pcap->spi = spi; | ||
410 | |||
411 | /* setup irq */ | ||
412 | pcap->irq_base = pdata->irq_base; | ||
413 | pcap->workqueue = create_singlethread_workqueue("pcapd"); | ||
414 | if (!pcap->workqueue) { | ||
415 | dev_err(&spi->dev, "cant create pcap thread\n"); | ||
416 | goto free_pcap; | ||
417 | } | ||
418 | |||
419 | /* redirect interrupts to AP, except adcdone2 */ | ||
420 | if (!(pdata->config & PCAP_SECOND_PORT)) | ||
421 | ezx_pcap_write(pcap, PCAP_REG_INT_SEL, | ||
422 | (1 << PCAP_IRQ_ADCDONE2)); | ||
423 | |||
424 | /* setup irq chip */ | ||
425 | for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) { | ||
426 | set_irq_chip_and_handler(i, &pcap_irq_chip, handle_simple_irq); | ||
427 | set_irq_chip_data(i, pcap); | ||
428 | #ifdef CONFIG_ARM | ||
429 | set_irq_flags(i, IRQF_VALID); | ||
430 | #else | ||
431 | set_irq_noprobe(i); | ||
432 | #endif | ||
433 | } | ||
434 | |||
435 | /* mask/ack all PCAP interrupts */ | ||
436 | ezx_pcap_write(pcap, PCAP_REG_MSR, PCAP_MASK_ALL_INTERRUPT); | ||
437 | ezx_pcap_write(pcap, PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER); | ||
438 | pcap->msr = PCAP_MASK_ALL_INTERRUPT; | ||
439 | |||
440 | set_irq_type(spi->irq, IRQ_TYPE_EDGE_RISING); | ||
441 | set_irq_data(spi->irq, pcap); | ||
442 | set_irq_chained_handler(spi->irq, pcap_irq_handler); | ||
443 | set_irq_wake(spi->irq, 1); | ||
444 | |||
445 | /* ADC */ | ||
446 | adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ? | ||
447 | PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); | ||
448 | |||
449 | ret = request_irq(adc_irq, pcap_adc_irq, 0, "ADC", pcap); | ||
450 | if (ret) | ||
451 | goto free_irqchip; | ||
452 | |||
453 | /* setup subdevs */ | ||
454 | for (i = 0; i < pdata->num_subdevs; i++) { | ||
455 | ret = pcap_add_subdev(pcap, &pdata->subdevs[i]); | ||
456 | if (ret) | ||
457 | goto remove_subdevs; | ||
458 | } | ||
459 | |||
460 | /* board specific quirks */ | ||
461 | if (pdata->init) | ||
462 | pdata->init(pcap); | ||
463 | |||
464 | return 0; | ||
465 | |||
466 | remove_subdevs: | ||
467 | device_for_each_child(&spi->dev, NULL, pcap_remove_subdev); | ||
468 | /* free_adc: */ | ||
469 | free_irq(adc_irq, pcap); | ||
470 | free_irqchip: | ||
471 | for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) | ||
472 | set_irq_chip_and_handler(i, NULL, NULL); | ||
473 | /* destroy_workqueue: */ | ||
474 | destroy_workqueue(pcap->workqueue); | ||
475 | free_pcap: | ||
476 | kfree(pcap); | ||
477 | ret: | ||
478 | return ret; | ||
479 | } | ||
480 | |||
481 | static struct spi_driver ezxpcap_driver = { | ||
482 | .probe = ezx_pcap_probe, | ||
483 | .remove = __devexit_p(ezx_pcap_remove), | ||
484 | .driver = { | ||
485 | .name = "ezx-pcap", | ||
486 | .owner = THIS_MODULE, | ||
487 | }, | ||
488 | }; | ||
489 | |||
490 | static int __init ezx_pcap_init(void) | ||
491 | { | ||
492 | return spi_register_driver(&ezxpcap_driver); | ||
493 | } | ||
494 | |||
495 | static void __exit ezx_pcap_exit(void) | ||
496 | { | ||
497 | spi_unregister_driver(&ezxpcap_driver); | ||
498 | } | ||
499 | |||
500 | module_init(ezx_pcap_init); | ||
501 | module_exit(ezx_pcap_exit); | ||
502 | |||
503 | MODULE_LICENSE("GPL"); | ||
504 | MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); | ||
505 | MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver"); | ||