aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDaniel Ribeiro <drwyrm@gmail.com>2009-05-28 14:43:37 -0400
committerSamuel Ortiz <sameol@linux.intel.com>2009-06-17 13:41:35 -0400
commit13a09f93d2bf3a20c748e1d6a30160a00fc58169 (patch)
tree1dab40fb0cdf9c4a559d6b9a92a29a4cd98032d6 /drivers
parent14fa56917d73d823538151b0429d98211fa439c1 (diff)
mfd: add PCAP driver
The PCAP Asic as present on EZX phones is a multi function device with voltage regulators, ADC, touch screen controller, RTC, USB transceiver, leds controller, and audio codec. It has two SPI ports, typically one is connected to the application processor and another to the baseband, this driver provides read/write functions to its registers, irq demultiplexer and ADC queueing/abstraction. This chip is used on a lot of Motorola phones, it was manufactured by TI as a custom product with the name PTWL93017, later this design evolved into the ATLAS PMIC from Freescale (MC13783). Signed-off-by: Daniel Ribeiro <drwyrm@gmail.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig7
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/ezx-pcap.c505
3 files changed, 514 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 61f0346650b3..287d47b78e7b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -255,6 +255,13 @@ config AB3100_CORE
255 LEDs, vibrator, system power and temperature, power management 255 LEDs, vibrator, system power and temperature, power management
256 and ALSA sound. 256 and ALSA sound.
257 257
258config EZX_PCAP
259 bool "PCAP Support"
260 depends on GENERIC_HARDIRQS && SPI_MASTER
261 help
262 This enables the PCAP ASIC present on EZX Phones. This is
263 needed for MMC, TouchScreen, Sound, USB, etc..
264
258endmenu 265endmenu
259 266
260menu "Multimedia Capabilities Port drivers" 267menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f5f337143e69..6f8a9a1af20b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -26,6 +26,8 @@ obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
26 26
27obj-$(CONFIG_MFD_CORE) += mfd-core.o 27obj-$(CONFIG_MFD_CORE) += mfd-core.o
28 28
29obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o
30
29obj-$(CONFIG_MCP) += mcp-core.o 31obj-$(CONFIG_MCP) += mcp-core.o
30obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o 32obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
31obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o 33obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
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
22struct pcap_adc_request {
23 u8 bank;
24 u8 ch[2];
25 u32 flags;
26 void (*callback)(void *, u16[]);
27 void *data;
28};
29
30struct pcap_adc_sync_request {
31 u16 res[2];
32 struct completion completion;
33};
34
35struct 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 */
57static 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
79int 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}
92EXPORT_SYMBOL_GPL(ezx_pcap_write);
93
94int 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}
107EXPORT_SYMBOL_GPL(ezx_pcap_read);
108
109/* IRQ */
110static inline unsigned int irq2pcap(struct pcap_chip *pcap, int irq)
111{
112 return 1 << (irq - pcap->irq_base);
113}
114
115int pcap_to_irq(struct pcap_chip *pcap, int irq)
116{
117 return pcap->irq_base + irq;
118}
119EXPORT_SYMBOL_GPL(pcap_to_irq);
120
121static 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
129static 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
137static struct irq_chip pcap_irq_chip = {
138 .name = "pcap",
139 .mask = pcap_mask_irq,
140 .unmask = pcap_unmask_irq,
141};
142
143static 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
150static 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
187static 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 */
197static 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
206static 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
231static 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
268int 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}
300EXPORT_SYMBOL_GPL(pcap_adc_async);
301
302static 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
311int 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}
328EXPORT_SYMBOL_GPL(pcap_adc_sync);
329
330/* subdevs */
331static int pcap_remove_subdev(struct device *dev, void *unused)
332{
333 platform_device_unregister(to_platform_device(dev));
334 return 0;
335}
336
337static 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
350static 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
379static 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
466remove_subdevs:
467 device_for_each_child(&spi->dev, NULL, pcap_remove_subdev);
468/* free_adc: */
469 free_irq(adc_irq, pcap);
470free_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);
475free_pcap:
476 kfree(pcap);
477ret:
478 return ret;
479}
480
481static 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
490static int __init ezx_pcap_init(void)
491{
492 return spi_register_driver(&ezxpcap_driver);
493}
494
495static void __exit ezx_pcap_exit(void)
496{
497 spi_unregister_driver(&ezxpcap_driver);
498}
499
500module_init(ezx_pcap_init);
501module_exit(ezx_pcap_exit);
502
503MODULE_LICENSE("GPL");
504MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
505MODULE_DESCRIPTION("Motorola PCAP2 ASIC Driver");