diff options
author | Samuel Ortiz <sameo@openedhand.com> | 2008-06-20 05:02:19 -0400 |
---|---|---|
committer | Samuel Ortiz <samuel@sortiz.org> | 2008-07-20 13:52:38 -0400 |
commit | 6f2384c4bdd4be3dc1e5d22ed5e6f0c3076fda60 (patch) | |
tree | 2b2db76100b1e7420a661ca1491b67e7b5de79b0 /drivers | |
parent | 5b664cb235e97afbf34db9c4d77f08ebd725335e (diff) |
mfd: asic3 gpiolib support
ASIC3 is, among other things, a GPIO extender. We should thus have it
supporting the current gpiolib API.
Signed-off-by: Samuel Ortiz <sameo@openedhand.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mfd/asic3.c | 224 |
1 files changed, 153 insertions, 71 deletions
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index ef8a492766a7..c70e7a5a5a90 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * | 9 | * |
10 | * Copyright 2001 Compaq Computer Corporation. | 10 | * Copyright 2001 Compaq Computer Corporation. |
11 | * Copyright 2004-2005 Phil Blundell | 11 | * Copyright 2004-2005 Phil Blundell |
12 | * Copyright 2007 OpenedHand Ltd. | 12 | * Copyright 2007-2008 OpenedHand Ltd. |
13 | * | 13 | * |
14 | * Authors: Phil Blundell <pb@handhelds.org>, | 14 | * Authors: Phil Blundell <pb@handhelds.org>, |
15 | * Samuel Ortiz <sameo@openedhand.com> | 15 | * Samuel Ortiz <sameo@openedhand.com> |
@@ -19,12 +19,26 @@ | |||
19 | #include <linux/version.h> | 19 | #include <linux/version.h> |
20 | #include <linux/kernel.h> | 20 | #include <linux/kernel.h> |
21 | #include <linux/irq.h> | 21 | #include <linux/irq.h> |
22 | #include <linux/gpio.h> | ||
22 | #include <linux/io.h> | 23 | #include <linux/io.h> |
23 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
24 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
25 | 26 | ||
26 | #include <linux/mfd/asic3.h> | 27 | #include <linux/mfd/asic3.h> |
27 | 28 | ||
29 | struct asic3 { | ||
30 | void __iomem *mapping; | ||
31 | unsigned int bus_shift; | ||
32 | unsigned int irq_nr; | ||
33 | unsigned int irq_base; | ||
34 | spinlock_t lock; | ||
35 | u16 irq_bothedge[4]; | ||
36 | struct gpio_chip gpio; | ||
37 | struct device *dev; | ||
38 | }; | ||
39 | |||
40 | static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset); | ||
41 | |||
28 | static inline void asic3_write_register(struct asic3 *asic, | 42 | static inline void asic3_write_register(struct asic3 *asic, |
29 | unsigned int reg, u32 value) | 43 | unsigned int reg, u32 value) |
30 | { | 44 | { |
@@ -251,7 +265,7 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) | |||
251 | edge &= ~bit; | 265 | edge &= ~bit; |
252 | } else if (type == IRQT_BOTHEDGE) { | 266 | } else if (type == IRQT_BOTHEDGE) { |
253 | trigger |= bit; | 267 | trigger |= bit; |
254 | if (asic3_gpio_get_value(asic, irq - asic->irq_base)) | 268 | if (asic3_gpio_get(&asic->gpio, irq - asic->irq_base)) |
255 | edge &= ~bit; | 269 | edge &= ~bit; |
256 | else | 270 | else |
257 | edge |= bit; | 271 | edge |= bit; |
@@ -350,6 +364,107 @@ static void asic3_irq_remove(struct platform_device *pdev) | |||
350 | } | 364 | } |
351 | 365 | ||
352 | /* GPIOs */ | 366 | /* GPIOs */ |
367 | static int asic3_gpio_direction(struct gpio_chip *chip, | ||
368 | unsigned offset, int out) | ||
369 | { | ||
370 | u32 mask = ASIC3_GPIO_TO_MASK(offset), out_reg; | ||
371 | unsigned int gpio_base; | ||
372 | unsigned long flags; | ||
373 | struct asic3 *asic; | ||
374 | |||
375 | asic = container_of(chip, struct asic3, gpio); | ||
376 | gpio_base = ASIC3_GPIO_TO_BASE(offset); | ||
377 | |||
378 | if (gpio_base > ASIC3_GPIO_D_Base) { | ||
379 | printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", | ||
380 | gpio_base, offset); | ||
381 | return -EINVAL; | ||
382 | } | ||
383 | |||
384 | spin_lock_irqsave(&asic->lock, flags); | ||
385 | |||
386 | out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Direction); | ||
387 | |||
388 | /* Input is 0, Output is 1 */ | ||
389 | if (out) | ||
390 | out_reg |= mask; | ||
391 | else | ||
392 | out_reg &= ~mask; | ||
393 | |||
394 | asic3_write_register(asic, gpio_base + ASIC3_GPIO_Direction, out_reg); | ||
395 | |||
396 | spin_unlock_irqrestore(&asic->lock, flags); | ||
397 | |||
398 | return 0; | ||
399 | |||
400 | } | ||
401 | |||
402 | static int asic3_gpio_direction_input(struct gpio_chip *chip, | ||
403 | unsigned offset) | ||
404 | { | ||
405 | return asic3_gpio_direction(chip, offset, 0); | ||
406 | } | ||
407 | |||
408 | static int asic3_gpio_direction_output(struct gpio_chip *chip, | ||
409 | unsigned offset, int value) | ||
410 | { | ||
411 | return asic3_gpio_direction(chip, offset, 1); | ||
412 | } | ||
413 | |||
414 | static int asic3_gpio_get(struct gpio_chip *chip, | ||
415 | unsigned offset) | ||
416 | { | ||
417 | unsigned int gpio_base; | ||
418 | u32 mask = ASIC3_GPIO_TO_MASK(offset); | ||
419 | struct asic3 *asic; | ||
420 | |||
421 | asic = container_of(chip, struct asic3, gpio); | ||
422 | gpio_base = ASIC3_GPIO_TO_BASE(offset); | ||
423 | |||
424 | if (gpio_base > ASIC3_GPIO_D_Base) { | ||
425 | printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", | ||
426 | gpio_base, offset); | ||
427 | return -EINVAL; | ||
428 | } | ||
429 | |||
430 | return asic3_read_register(asic, gpio_base + ASIC3_GPIO_Status) & mask; | ||
431 | } | ||
432 | |||
433 | static void asic3_gpio_set(struct gpio_chip *chip, | ||
434 | unsigned offset, int value) | ||
435 | { | ||
436 | u32 mask, out_reg; | ||
437 | unsigned int gpio_base; | ||
438 | unsigned long flags; | ||
439 | struct asic3 *asic; | ||
440 | |||
441 | asic = container_of(chip, struct asic3, gpio); | ||
442 | gpio_base = ASIC3_GPIO_TO_BASE(offset); | ||
443 | |||
444 | if (gpio_base > ASIC3_GPIO_D_Base) { | ||
445 | printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", | ||
446 | gpio_base, offset); | ||
447 | return; | ||
448 | } | ||
449 | |||
450 | mask = ASIC3_GPIO_TO_MASK(offset); | ||
451 | |||
452 | spin_lock_irqsave(&asic->lock, flags); | ||
453 | |||
454 | out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Out); | ||
455 | |||
456 | if (value) | ||
457 | out_reg |= mask; | ||
458 | else | ||
459 | out_reg &= ~mask; | ||
460 | |||
461 | asic3_write_register(asic, gpio_base + ASIC3_GPIO_Out, out_reg); | ||
462 | |||
463 | spin_unlock_irqrestore(&asic->lock, flags); | ||
464 | |||
465 | return; | ||
466 | } | ||
467 | |||
353 | static inline u32 asic3_get_gpio(struct asic3 *asic, unsigned int base, | 468 | static inline u32 asic3_get_gpio(struct asic3 *asic, unsigned int base, |
354 | unsigned int function) | 469 | unsigned int function) |
355 | { | 470 | { |
@@ -368,15 +483,6 @@ static void asic3_set_gpio(struct asic3 *asic, unsigned int base, | |||
368 | spin_unlock_irqrestore(&asic->lock, flags); | 483 | spin_unlock_irqrestore(&asic->lock, flags); |
369 | } | 484 | } |
370 | 485 | ||
371 | #define asic3_get_gpio_a(asic, fn) \ | ||
372 | asic3_get_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn) | ||
373 | #define asic3_get_gpio_b(asic, fn) \ | ||
374 | asic3_get_gpio(asic, ASIC3_GPIO_B_Base, ASIC3_GPIO_##fn) | ||
375 | #define asic3_get_gpio_c(asic, fn) \ | ||
376 | asic3_get_gpio(asic, ASIC3_GPIO_C_Base, ASIC3_GPIO_##fn) | ||
377 | #define asic3_get_gpio_d(asic, fn) \ | ||
378 | asic3_get_gpio(asic, ASIC3_GPIO_D_Base, ASIC3_GPIO_##fn) | ||
379 | |||
380 | #define asic3_set_gpio_a(asic, fn, bits, val) \ | 486 | #define asic3_set_gpio_a(asic, fn, bits, val) \ |
381 | asic3_set_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn, bits, val) | 487 | asic3_set_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn, bits, val) |
382 | #define asic3_set_gpio_b(asic, fn, bits, val) \ | 488 | #define asic3_set_gpio_b(asic, fn, bits, val) \ |
@@ -394,54 +500,6 @@ static void asic3_set_gpio(struct asic3 *asic, unsigned int base, | |||
394 | asic3_set_gpio_d((asic), fn, (bits), (pdata)->gpio_d.field); \ | 500 | asic3_set_gpio_d((asic), fn, (bits), (pdata)->gpio_d.field); \ |
395 | } while (0) | 501 | } while (0) |
396 | 502 | ||
397 | int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio) | ||
398 | { | ||
399 | u32 mask = ASIC3_GPIO_bit(gpio); | ||
400 | |||
401 | switch (gpio >> 4) { | ||
402 | case ASIC3_GPIO_BANK_A: | ||
403 | return asic3_get_gpio_a(asic, Status) & mask; | ||
404 | case ASIC3_GPIO_BANK_B: | ||
405 | return asic3_get_gpio_b(asic, Status) & mask; | ||
406 | case ASIC3_GPIO_BANK_C: | ||
407 | return asic3_get_gpio_c(asic, Status) & mask; | ||
408 | case ASIC3_GPIO_BANK_D: | ||
409 | return asic3_get_gpio_d(asic, Status) & mask; | ||
410 | default: | ||
411 | printk(KERN_ERR "%s: invalid GPIO value 0x%x", | ||
412 | __func__, gpio); | ||
413 | return -EINVAL; | ||
414 | } | ||
415 | } | ||
416 | EXPORT_SYMBOL(asic3_gpio_get_value); | ||
417 | |||
418 | void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val) | ||
419 | { | ||
420 | u32 mask = ASIC3_GPIO_bit(gpio); | ||
421 | u32 bitval = 0; | ||
422 | if (val) | ||
423 | bitval = mask; | ||
424 | |||
425 | switch (gpio >> 4) { | ||
426 | case ASIC3_GPIO_BANK_A: | ||
427 | asic3_set_gpio_a(asic, Out, mask, bitval); | ||
428 | return; | ||
429 | case ASIC3_GPIO_BANK_B: | ||
430 | asic3_set_gpio_b(asic, Out, mask, bitval); | ||
431 | return; | ||
432 | case ASIC3_GPIO_BANK_C: | ||
433 | asic3_set_gpio_c(asic, Out, mask, bitval); | ||
434 | return; | ||
435 | case ASIC3_GPIO_BANK_D: | ||
436 | asic3_set_gpio_d(asic, Out, mask, bitval); | ||
437 | return; | ||
438 | default: | ||
439 | printk(KERN_ERR "%s: invalid GPIO value 0x%x", | ||
440 | __func__, gpio); | ||
441 | return; | ||
442 | } | ||
443 | } | ||
444 | EXPORT_SYMBOL(asic3_gpio_set_value); | ||
445 | 503 | ||
446 | static int asic3_gpio_probe(struct platform_device *pdev) | 504 | static int asic3_gpio_probe(struct platform_device *pdev) |
447 | { | 505 | { |
@@ -472,12 +530,14 @@ static int asic3_gpio_probe(struct platform_device *pdev) | |||
472 | alt_function); | 530 | alt_function); |
473 | } | 531 | } |
474 | 532 | ||
475 | return 0; | 533 | return gpiochip_add(&asic->gpio); |
476 | } | 534 | } |
477 | 535 | ||
478 | static void asic3_gpio_remove(struct platform_device *pdev) | 536 | static int asic3_gpio_remove(struct platform_device *pdev) |
479 | { | 537 | { |
480 | return; | 538 | struct asic3 *asic = platform_get_drvdata(pdev); |
539 | |||
540 | return gpiochip_remove(&asic->gpio); | ||
481 | } | 541 | } |
482 | 542 | ||
483 | 543 | ||
@@ -488,11 +548,13 @@ static int asic3_probe(struct platform_device *pdev) | |||
488 | struct asic3 *asic; | 548 | struct asic3 *asic; |
489 | struct resource *mem; | 549 | struct resource *mem; |
490 | unsigned long clksel; | 550 | unsigned long clksel; |
491 | int ret; | 551 | int ret = 0; |
492 | 552 | ||
493 | asic = kzalloc(sizeof(struct asic3), GFP_KERNEL); | 553 | asic = kzalloc(sizeof(struct asic3), GFP_KERNEL); |
494 | if (!asic) | 554 | if (asic == NULL) { |
555 | printk(KERN_ERR "kzalloc failed\n"); | ||
495 | return -ENOMEM; | 556 | return -ENOMEM; |
557 | } | ||
496 | 558 | ||
497 | spin_lock_init(&asic->lock); | 559 | spin_lock_init(&asic->lock); |
498 | platform_set_drvdata(pdev, asic); | 560 | platform_set_drvdata(pdev, asic); |
@@ -502,14 +564,15 @@ static int asic3_probe(struct platform_device *pdev) | |||
502 | if (!mem) { | 564 | if (!mem) { |
503 | ret = -ENOMEM; | 565 | ret = -ENOMEM; |
504 | printk(KERN_ERR "asic3: no MEM resource\n"); | 566 | printk(KERN_ERR "asic3: no MEM resource\n"); |
505 | goto err_out_1; | 567 | goto out_free; |
506 | } | 568 | } |
507 | 569 | ||
570 | |||
508 | asic->mapping = ioremap(mem->start, PAGE_SIZE); | 571 | asic->mapping = ioremap(mem->start, PAGE_SIZE); |
509 | if (!asic->mapping) { | 572 | if (!asic->mapping) { |
510 | ret = -ENOMEM; | 573 | ret = -ENOMEM; |
511 | printk(KERN_ERR "asic3: couldn't ioremap\n"); | 574 | printk(KERN_ERR "asic3: couldn't ioremap\n"); |
512 | goto err_out_1; | 575 | goto out_free; |
513 | } | 576 | } |
514 | 577 | ||
515 | asic->irq_base = pdata->irq_base; | 578 | asic->irq_base = pdata->irq_base; |
@@ -525,9 +588,21 @@ static int asic3_probe(struct platform_device *pdev) | |||
525 | ret = asic3_irq_probe(pdev); | 588 | ret = asic3_irq_probe(pdev); |
526 | if (ret < 0) { | 589 | if (ret < 0) { |
527 | printk(KERN_ERR "asic3: couldn't probe IRQs\n"); | 590 | printk(KERN_ERR "asic3: couldn't probe IRQs\n"); |
528 | goto err_out_2; | 591 | goto out_unmap; |
592 | } | ||
593 | |||
594 | asic->gpio.base = pdata->gpio_base; | ||
595 | asic->gpio.ngpio = ASIC3_NUM_GPIOS; | ||
596 | asic->gpio.get = asic3_gpio_get; | ||
597 | asic->gpio.set = asic3_gpio_set; | ||
598 | asic->gpio.direction_input = asic3_gpio_direction_input; | ||
599 | asic->gpio.direction_output = asic3_gpio_direction_output; | ||
600 | |||
601 | ret = asic3_gpio_probe(pdev); | ||
602 | if (ret < 0) { | ||
603 | printk(KERN_ERR "GPIO probe failed\n"); | ||
604 | goto out_irq; | ||
529 | } | 605 | } |
530 | asic3_gpio_probe(pdev); | ||
531 | 606 | ||
532 | if (pdata->children) { | 607 | if (pdata->children) { |
533 | int i; | 608 | int i; |
@@ -541,9 +616,13 @@ static int asic3_probe(struct platform_device *pdev) | |||
541 | 616 | ||
542 | return 0; | 617 | return 0; |
543 | 618 | ||
544 | err_out_2: | 619 | out_irq: |
620 | asic3_irq_remove(pdev); | ||
621 | |||
622 | out_unmap: | ||
545 | iounmap(asic->mapping); | 623 | iounmap(asic->mapping); |
546 | err_out_1: | 624 | |
625 | out_free: | ||
547 | kfree(asic); | 626 | kfree(asic); |
548 | 627 | ||
549 | return ret; | 628 | return ret; |
@@ -551,9 +630,12 @@ static int asic3_probe(struct platform_device *pdev) | |||
551 | 630 | ||
552 | static int asic3_remove(struct platform_device *pdev) | 631 | static int asic3_remove(struct platform_device *pdev) |
553 | { | 632 | { |
633 | int ret; | ||
554 | struct asic3 *asic = platform_get_drvdata(pdev); | 634 | struct asic3 *asic = platform_get_drvdata(pdev); |
555 | 635 | ||
556 | asic3_gpio_remove(pdev); | 636 | ret = asic3_gpio_remove(pdev); |
637 | if (ret < 0) | ||
638 | return ret; | ||
557 | asic3_irq_remove(pdev); | 639 | asic3_irq_remove(pdev); |
558 | 640 | ||
559 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0); | 641 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0); |