aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorPhilipp Zabel <philipp.zabel@gmail.com>2008-04-09 14:20:34 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2008-04-19 06:29:07 -0400
commita1635b8fe59de2c5223cda5ca8397b875c901904 (patch)
tree047db24bdba7da410937e1f8da821d30e9052a9f /drivers/mfd
parente6816f34e4ee237fc1b54e0357e9ea31462ad411 (diff)
[ARM] 4947/1: htc-egpio, a driver for GPIO/IRQ expanders with fixed input/output pins
implemented in CPLD chips on several HTC devices. The original driver was written by Kevin O'Connor, I have adapted it to use gpiolib and made the bus/register widths configurable. Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/htc-egpio.c440
3 files changed, 450 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 0c886c882385..284b2dc03444 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -22,6 +22,14 @@ config MFD_ASIC3
22 This driver supports the ASIC3 multifunction chip found on many 22 This driver supports the ASIC3 multifunction chip found on many
23 PDAs (mainly iPAQ and HTC based ones) 23 PDAs (mainly iPAQ and HTC based ones)
24 24
25config HTC_EGPIO
26 bool "HTC EGPIO support"
27 depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB
28 help
29 This driver supports the CPLD egpio chip present on
30 several HTC phones. It provides basic support for input
31 pins, output pins, and irqs.
32
25endmenu 33endmenu
26 34
27menu "Multimedia Capabilities Port drivers" 35menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 521cd5cb68af..f81f8d2e2c03 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -5,6 +5,8 @@
5obj-$(CONFIG_MFD_SM501) += sm501.o 5obj-$(CONFIG_MFD_SM501) += sm501.o
6obj-$(CONFIG_MFD_ASIC3) += asic3.o 6obj-$(CONFIG_MFD_ASIC3) += asic3.o
7 7
8obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
9
8obj-$(CONFIG_MCP) += mcp-core.o 10obj-$(CONFIG_MCP) += mcp-core.o
9obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o 11obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
10obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o 12obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c
new file mode 100644
index 000000000000..8872cc077519
--- /dev/null
+++ b/drivers/mfd/htc-egpio.c
@@ -0,0 +1,440 @@
1/*
2 * Support for the GPIO/IRQ expander chips present on several HTC phones.
3 * These are implemented in CPLD chips present on the board.
4 *
5 * Copyright (c) 2007 Kevin O'Connor <kevin@koconnor.net>
6 * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
7 *
8 * This file may be distributed under the terms of the GNU GPL license.
9 */
10
11#include <linux/kernel.h>
12#include <linux/errno.h>
13#include <linux/interrupt.h>
14#include <linux/irq.h>
15#include <linux/io.h>
16#include <linux/spinlock.h>
17#include <linux/platform_device.h>
18#include <linux/module.h>
19#include <linux/mfd/htc-egpio.h>
20
21struct egpio_chip {
22 int reg_start;
23 int cached_values;
24 unsigned long is_out;
25 struct device *dev;
26 struct gpio_chip chip;
27};
28
29struct egpio_info {
30 spinlock_t lock;
31
32 /* iomem info */
33 void __iomem *base_addr;
34 int bus_shift; /* byte shift */
35 int reg_shift; /* bit shift */
36 int reg_mask;
37
38 /* irq info */
39 int ack_register;
40 int ack_write;
41 u16 irqs_enabled;
42 uint irq_start;
43 int nirqs;
44 uint chained_irq;
45
46 /* egpio info */
47 struct egpio_chip *chip;
48 int nchips;
49};
50
51static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg)
52{
53 writew(value, ei->base_addr + (reg << ei->bus_shift));
54}
55
56static inline u16 egpio_readw(struct egpio_info *ei, int reg)
57{
58 return readw(ei->base_addr + (reg << ei->bus_shift));
59}
60
61/*
62 * IRQs
63 */
64
65static inline void ack_irqs(struct egpio_info *ei)
66{
67 egpio_writew(ei->ack_write, ei, ei->ack_register);
68 pr_debug("EGPIO ack - write %x to base+%x\n",
69 ei->ack_write, ei->ack_register << ei->bus_shift);
70}
71
72static void egpio_ack(unsigned int irq)
73{
74}
75
76/* There does not appear to be a way to proactively mask interrupts
77 * on the egpio chip itself. So, we simply ignore interrupts that
78 * aren't desired. */
79static void egpio_mask(unsigned int irq)
80{
81 struct egpio_info *ei = get_irq_chip_data(irq);
82 ei->irqs_enabled &= ~(1 << (irq - ei->irq_start));
83 pr_debug("EGPIO mask %d %04x\n", irq, ei->irqs_enabled);
84}
85static void egpio_unmask(unsigned int irq)
86{
87 struct egpio_info *ei = get_irq_chip_data(irq);
88 ei->irqs_enabled |= 1 << (irq - ei->irq_start);
89 pr_debug("EGPIO unmask %d %04x\n", irq, ei->irqs_enabled);
90}
91
92static struct irq_chip egpio_muxed_chip = {
93 .name = "htc-egpio",
94 .ack = egpio_ack,
95 .mask = egpio_mask,
96 .unmask = egpio_unmask,
97};
98
99static void egpio_handler(unsigned int irq, struct irq_desc *desc)
100{
101 struct egpio_info *ei = get_irq_data(irq);
102 int irqpin;
103
104 /* Read current pins. */
105 unsigned long readval = egpio_readw(ei, ei->ack_register);
106 pr_debug("IRQ reg: %x\n", (unsigned int)readval);
107 /* Ack/unmask interrupts. */
108 ack_irqs(ei);
109 /* Process all set pins. */
110 readval &= ei->irqs_enabled;
111 for_each_bit(irqpin, &readval, ei->nirqs) {
112 /* Run irq handler */
113 pr_debug("got IRQ %d\n", irqpin);
114 irq = ei->irq_start + irqpin;
115 desc = &irq_desc[irq];
116 desc->handle_irq(irq, desc);
117 }
118}
119
120int htc_egpio_get_wakeup_irq(struct device *dev)
121{
122 struct egpio_info *ei = dev_get_drvdata(dev);
123
124 /* Read current pins. */
125 u16 readval = egpio_readw(ei, ei->ack_register);
126 /* Ack/unmask interrupts. */
127 ack_irqs(ei);
128 /* Return first set pin. */
129 readval &= ei->irqs_enabled;
130 return ei->irq_start + ffs(readval) - 1;
131}
132EXPORT_SYMBOL(htc_egpio_get_wakeup_irq);
133
134static inline int egpio_pos(struct egpio_info *ei, int bit)
135{
136 return bit >> ei->reg_shift;
137}
138
139static inline int egpio_bit(struct egpio_info *ei, int bit)
140{
141 return 1 << (bit & ((1 << ei->reg_shift)-1));
142}
143
144/*
145 * Input pins
146 */
147
148static int egpio_get(struct gpio_chip *chip, unsigned offset)
149{
150 struct egpio_chip *egpio;
151 struct egpio_info *ei;
152 unsigned bit;
153 int reg;
154 int value;
155
156 pr_debug("egpio_get_value(%d)\n", chip->base + offset);
157
158 egpio = container_of(chip, struct egpio_chip, chip);
159 ei = dev_get_drvdata(egpio->dev);
160 bit = egpio_bit(ei, offset);
161 reg = egpio->reg_start + egpio_pos(ei, offset);
162
163 value = egpio_readw(ei, reg);
164 pr_debug("readw(%p + %x) = %x\n",
165 ei->base_addr, reg << ei->bus_shift, value);
166 return value & bit;
167}
168
169static int egpio_direction_input(struct gpio_chip *chip, unsigned offset)
170{
171 struct egpio_chip *egpio;
172
173 egpio = container_of(chip, struct egpio_chip, chip);
174 return test_bit(offset, &egpio->is_out) ? -EINVAL : 0;
175}
176
177
178/*
179 * Output pins
180 */
181
182static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
183{
184 unsigned long flag;
185 struct egpio_chip *egpio;
186 struct egpio_info *ei;
187 unsigned bit;
188 int pos;
189 int reg;
190 int shift;
191
192 pr_debug("egpio_set(%s, %d(%d), %d)\n",
193 chip->label, offset, offset+chip->base, value);
194
195 egpio = container_of(chip, struct egpio_chip, chip);
196 ei = dev_get_drvdata(egpio->dev);
197 bit = egpio_bit(ei, offset);
198 pos = egpio_pos(ei, offset);
199 reg = egpio->reg_start + pos;
200 shift = pos << ei->reg_shift;
201
202 pr_debug("egpio %s: reg %d = 0x%04x\n", value ? "set" : "clear",
203 reg, (egpio->cached_values >> shift) & ei->reg_mask);
204
205 spin_lock_irqsave(&ei->lock, flag);
206 if (value)
207 egpio->cached_values |= (1 << offset);
208 else
209 egpio->cached_values &= ~(1 << offset);
210 egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg);
211 spin_unlock_irqrestore(&ei->lock, flag);
212}
213
214static int egpio_direction_output(struct gpio_chip *chip,
215 unsigned offset, int value)
216{
217 struct egpio_chip *egpio;
218
219 egpio = container_of(chip, struct egpio_chip, chip);
220 if (test_bit(offset, &egpio->is_out)) {
221 egpio_set(chip, offset, value);
222 return 0;
223 } else {
224 return -EINVAL;
225 }
226}
227
228static void egpio_write_cache(struct egpio_info *ei)
229{
230 int i;
231 struct egpio_chip *egpio;
232 int shift;
233
234 for (i = 0; i < ei->nchips; i++) {
235 egpio = &(ei->chip[i]);
236 if (!egpio->is_out)
237 continue;
238
239 for (shift = 0; shift < egpio->chip.ngpio;
240 shift += (1<<ei->reg_shift)) {
241
242 int reg = egpio->reg_start + egpio_pos(ei, shift);
243
244 if (!((egpio->is_out >> shift) & ei->reg_mask))
245 continue;
246
247 pr_debug("EGPIO: setting %x to %x, was %x\n", reg,
248 (egpio->cached_values >> shift) & ei->reg_mask,
249 egpio_readw(ei, reg));
250
251 egpio_writew((egpio->cached_values >> shift)
252 & ei->reg_mask, ei, reg);
253 }
254 }
255}
256
257
258/*
259 * Setup
260 */
261
262static int __init egpio_probe(struct platform_device *pdev)
263{
264 struct htc_egpio_platform_data *pdata = pdev->dev.platform_data;
265 struct resource *res;
266 struct egpio_info *ei;
267 struct gpio_chip *chip;
268 unsigned int irq, irq_end;
269 int i;
270 int ret;
271
272 /* Initialize ei data structure. */
273 ei = kzalloc(sizeof(*ei), GFP_KERNEL);
274 if (!ei)
275 return -ENOMEM;
276
277 spin_lock_init(&ei->lock);
278
279 /* Find chained irq */
280 ret = -EINVAL;
281 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
282 if (res)
283 ei->chained_irq = res->start;
284
285 /* Map egpio chip into virtual address space. */
286 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
287 if (!res)
288 goto fail;
289 ei->base_addr = ioremap_nocache(res->start, res->end - res->start);
290 if (!ei->base_addr)
291 goto fail;
292 pr_debug("EGPIO phys=%08x virt=%p\n", res->start, ei->base_addr);
293
294 if ((pdata->bus_width != 16) && (pdata->bus_width != 32))
295 goto fail;
296 ei->bus_shift = fls(pdata->bus_width - 1) - 3;
297 pr_debug("bus_shift = %d\n", ei->bus_shift);
298
299 if ((pdata->reg_width != 8) && (pdata->reg_width != 16))
300 goto fail;
301 ei->reg_shift = fls(pdata->reg_width - 1);
302 pr_debug("reg_shift = %d\n", ei->reg_shift);
303
304 ei->reg_mask = (1 << pdata->reg_width) - 1;
305
306 platform_set_drvdata(pdev, ei);
307
308 ei->nchips = pdata->num_chips;
309 ei->chip = kzalloc(sizeof(struct egpio_chip) * ei->nchips, GFP_KERNEL);
310 if (!ei) {
311 ret = -ENOMEM;
312 goto fail;
313 }
314 for (i = 0; i < ei->nchips; i++) {
315 ei->chip[i].reg_start = pdata->chip[i].reg_start;
316 ei->chip[i].cached_values = pdata->chip[i].initial_values;
317 ei->chip[i].is_out = pdata->chip[i].direction;
318 ei->chip[i].dev = &(pdev->dev);
319 chip = &(ei->chip[i].chip);
320 chip->label = "htc-egpio";
321 chip->get = egpio_get;
322 chip->set = egpio_set;
323 chip->direction_input = egpio_direction_input;
324 chip->direction_output = egpio_direction_output;
325 chip->base = pdata->chip[i].gpio_base;
326 chip->ngpio = pdata->chip[i].num_gpios;
327
328 gpiochip_add(chip);
329 }
330
331 /* Set initial pin values */
332 egpio_write_cache(ei);
333
334 ei->irq_start = pdata->irq_base;
335 ei->nirqs = pdata->num_irqs;
336 ei->ack_register = pdata->ack_register;
337
338 if (ei->chained_irq) {
339 /* Setup irq handlers */
340 ei->ack_write = 0xFFFF;
341 if (pdata->invert_acks)
342 ei->ack_write = 0;
343 irq_end = ei->irq_start + ei->nirqs;
344 for (irq = ei->irq_start; irq < irq_end; irq++) {
345 set_irq_chip(irq, &egpio_muxed_chip);
346 set_irq_chip_data(irq, ei);
347 set_irq_handler(irq, handle_simple_irq);
348 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
349 }
350 set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING);
351 set_irq_data(ei->chained_irq, ei);
352 set_irq_chained_handler(ei->chained_irq, egpio_handler);
353 ack_irqs(ei);
354
355 device_init_wakeup(&pdev->dev, 1);
356 }
357
358 return 0;
359
360fail:
361 printk(KERN_ERR "EGPIO failed to setup\n");
362 kfree(ei);
363 return ret;
364}
365
366static int __exit egpio_remove(struct platform_device *pdev)
367{
368 struct egpio_info *ei = platform_get_drvdata(pdev);
369 unsigned int irq, irq_end;
370
371 if (ei->chained_irq) {
372 irq_end = ei->irq_start + ei->nirqs;
373 for (irq = ei->irq_start; irq < irq_end; irq++) {
374 set_irq_chip(irq, NULL);
375 set_irq_handler(irq, NULL);
376 set_irq_flags(irq, 0);
377 }
378 set_irq_chained_handler(ei->chained_irq, NULL);
379 device_init_wakeup(&pdev->dev, 0);
380 }
381 iounmap(ei->base_addr);
382 kfree(ei->chip);
383 kfree(ei);
384
385 return 0;
386}
387
388#ifdef CONFIG_PM
389static int egpio_suspend(struct platform_device *pdev, pm_message_t state)
390{
391 struct egpio_info *ei = platform_get_drvdata(pdev);
392
393 if (ei->chained_irq && device_may_wakeup(&pdev->dev))
394 enable_irq_wake(ei->chained_irq);
395 return 0;
396}
397
398static int egpio_resume(struct platform_device *pdev)
399{
400 struct egpio_info *ei = platform_get_drvdata(pdev);
401
402 if (ei->chained_irq && device_may_wakeup(&pdev->dev))
403 disable_irq_wake(ei->chained_irq);
404
405 /* Update registers from the cache, in case
406 the CPLD was powered off during suspend */
407 egpio_write_cache(ei);
408 return 0;
409}
410#else
411#define egpio_suspend NULL
412#define egpio_resume NULL
413#endif
414
415
416static struct platform_driver egpio_driver = {
417 .driver = {
418 .name = "htc-egpio",
419 },
420 .remove = __exit_p(egpio_remove),
421 .suspend = egpio_suspend,
422 .resume = egpio_resume,
423};
424
425static int __init egpio_init(void)
426{
427 return platform_driver_probe(&egpio_driver, egpio_probe);
428}
429
430static void __exit egpio_exit(void)
431{
432 platform_driver_unregister(&egpio_driver);
433}
434
435/* start early for dependencies */
436subsys_initcall(egpio_init);
437module_exit(egpio_exit)
438
439MODULE_LICENSE("GPL");
440MODULE_AUTHOR("Kevin O'Connor <kevin@koconnor.net>");