diff options
-rw-r--r-- | drivers/mfd/Kconfig | 1 | ||||
-rw-r--r-- | drivers/mfd/asic3.c | 99 |
2 files changed, 100 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6ea325cc8d28..491ac0f800d2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -30,6 +30,7 @@ config MFD_SM501_GPIO | |||
30 | config MFD_ASIC3 | 30 | config MFD_ASIC3 |
31 | bool "Support for Compaq ASIC3" | 31 | bool "Support for Compaq ASIC3" |
32 | depends on GENERIC_HARDIRQS && GPIOLIB && ARM | 32 | depends on GENERIC_HARDIRQS && GPIOLIB && ARM |
33 | select MFD_CORE | ||
33 | ---help--- | 34 | ---help--- |
34 | This driver supports the ASIC3 multifunction chip found on many | 35 | This driver supports the ASIC3 multifunction chip found on many |
35 | PDAs (mainly iPAQ and HTC based ones) | 36 | PDAs (mainly iPAQ and HTC based ones) |
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index d5dd0dfae872..a90658e86307 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c | |||
@@ -17,6 +17,7 @@ | |||
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
20 | #include <linux/delay.h> | ||
20 | #include <linux/irq.h> | 21 | #include <linux/irq.h> |
21 | #include <linux/gpio.h> | 22 | #include <linux/gpio.h> |
22 | #include <linux/io.h> | 23 | #include <linux/io.h> |
@@ -24,6 +25,8 @@ | |||
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> |
28 | #include <linux/mfd/core.h> | ||
29 | #include <linux/mfd/ds1wm.h> | ||
27 | 30 | ||
28 | enum { | 31 | enum { |
29 | ASIC3_CLOCK_SPI, | 32 | ASIC3_CLOCK_SPI, |
@@ -616,6 +619,98 @@ static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) | |||
616 | spin_unlock_irqrestore(&asic->lock, flags); | 619 | spin_unlock_irqrestore(&asic->lock, flags); |
617 | } | 620 | } |
618 | 621 | ||
622 | /* MFD cells (SPI, PWM, LED, DS1WM, MMC) */ | ||
623 | static struct ds1wm_driver_data ds1wm_pdata = { | ||
624 | .active_high = 1, | ||
625 | }; | ||
626 | |||
627 | static struct resource ds1wm_resources[] = { | ||
628 | { | ||
629 | .start = ASIC3_OWM_BASE, | ||
630 | .end = ASIC3_OWM_BASE + 0x13, | ||
631 | .flags = IORESOURCE_MEM, | ||
632 | }, | ||
633 | { | ||
634 | .start = ASIC3_IRQ_OWM, | ||
635 | .start = ASIC3_IRQ_OWM, | ||
636 | .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, | ||
637 | }, | ||
638 | }; | ||
639 | |||
640 | static int ds1wm_enable(struct platform_device *pdev) | ||
641 | { | ||
642 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | ||
643 | |||
644 | /* Turn on external clocks and the OWM clock */ | ||
645 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); | ||
646 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); | ||
647 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]); | ||
648 | msleep(1); | ||
649 | |||
650 | /* Reset and enable DS1WM */ | ||
651 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET), | ||
652 | ASIC3_EXTCF_OWM_RESET, 1); | ||
653 | msleep(1); | ||
654 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET), | ||
655 | ASIC3_EXTCF_OWM_RESET, 0); | ||
656 | msleep(1); | ||
657 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), | ||
658 | ASIC3_EXTCF_OWM_EN, 1); | ||
659 | msleep(1); | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static int ds1wm_disable(struct platform_device *pdev) | ||
665 | { | ||
666 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | ||
667 | |||
668 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), | ||
669 | ASIC3_EXTCF_OWM_EN, 0); | ||
670 | |||
671 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_OWM]); | ||
672 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); | ||
673 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); | ||
674 | |||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | static struct mfd_cell asic3_cell_ds1wm = { | ||
679 | .name = "ds1wm", | ||
680 | .enable = ds1wm_enable, | ||
681 | .disable = ds1wm_disable, | ||
682 | .driver_data = &ds1wm_pdata, | ||
683 | .num_resources = ARRAY_SIZE(ds1wm_resources), | ||
684 | .resources = ds1wm_resources, | ||
685 | }; | ||
686 | |||
687 | static int __init asic3_mfd_probe(struct platform_device *pdev, | ||
688 | struct resource *mem) | ||
689 | { | ||
690 | struct asic3 *asic = platform_get_drvdata(pdev); | ||
691 | int ret; | ||
692 | |||
693 | /* DS1WM */ | ||
694 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), | ||
695 | ASIC3_EXTCF_OWM_SMB, 0); | ||
696 | |||
697 | ds1wm_resources[0].start >>= asic->bus_shift; | ||
698 | ds1wm_resources[0].end >>= asic->bus_shift; | ||
699 | |||
700 | asic3_cell_ds1wm.platform_data = &asic3_cell_ds1wm; | ||
701 | asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm); | ||
702 | |||
703 | ret = mfd_add_devices(&pdev->dev, pdev->id, | ||
704 | &asic3_cell_ds1wm, 1, mem, asic->irq_base); | ||
705 | |||
706 | return ret; | ||
707 | } | ||
708 | |||
709 | static void asic3_mfd_remove(struct platform_device *pdev) | ||
710 | { | ||
711 | mfd_remove_devices(&pdev->dev); | ||
712 | } | ||
713 | |||
619 | /* Core */ | 714 | /* Core */ |
620 | static int __init asic3_probe(struct platform_device *pdev) | 715 | static int __init asic3_probe(struct platform_device *pdev) |
621 | { | 716 | { |
@@ -683,6 +778,8 @@ static int __init asic3_probe(struct platform_device *pdev) | |||
683 | */ | 778 | */ |
684 | memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init)); | 779 | memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init)); |
685 | 780 | ||
781 | asic3_mfd_probe(pdev, mem); | ||
782 | |||
686 | dev_info(asic->dev, "ASIC3 Core driver\n"); | 783 | dev_info(asic->dev, "ASIC3 Core driver\n"); |
687 | 784 | ||
688 | return 0; | 785 | return 0; |
@@ -704,6 +801,8 @@ static int asic3_remove(struct platform_device *pdev) | |||
704 | int ret; | 801 | int ret; |
705 | struct asic3 *asic = platform_get_drvdata(pdev); | 802 | struct asic3 *asic = platform_get_drvdata(pdev); |
706 | 803 | ||
804 | asic3_mfd_remove(pdev); | ||
805 | |||
707 | ret = asic3_gpio_remove(pdev); | 806 | ret = asic3_gpio_remove(pdev); |
708 | if (ret < 0) | 807 | if (ret < 0) |
709 | return ret; | 808 | return ret; |