diff options
-rw-r--r-- | arch/arm/mach-pxa/include/mach/mmc.h | 5 | ||||
-rw-r--r-- | drivers/mmc/host/pxamci.c | 81 |
2 files changed, 85 insertions, 1 deletions
diff --git a/arch/arm/mach-pxa/include/mach/mmc.h b/arch/arm/mach-pxa/include/mach/mmc.h index 6d1304c9270f..02a69dc2ee63 100644 --- a/arch/arm/mach-pxa/include/mach/mmc.h +++ b/arch/arm/mach-pxa/include/mach/mmc.h | |||
@@ -14,6 +14,11 @@ struct pxamci_platform_data { | |||
14 | int (*get_ro)(struct device *); | 14 | int (*get_ro)(struct device *); |
15 | void (*setpower)(struct device *, unsigned int); | 15 | void (*setpower)(struct device *, unsigned int); |
16 | void (*exit)(struct device *, void *); | 16 | void (*exit)(struct device *, void *); |
17 | int gpio_card_detect; /* gpio detecting card insertion */ | ||
18 | int gpio_card_ro; /* gpio detecting read only toggle */ | ||
19 | bool gpio_card_ro_invert; /* gpio ro is inverted */ | ||
20 | int gpio_power; /* gpio powering up MMC bus */ | ||
21 | bool gpio_power_invert; /* gpio power is inverted */ | ||
17 | }; | 22 | }; |
18 | 23 | ||
19 | extern void pxa_set_mci_info(struct pxamci_platform_data *info); | 24 | extern void pxa_set_mci_info(struct pxamci_platform_data *info); |
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index e55ac792d68c..972efa8d2c8e 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/mmc/host.h> | 28 | #include <linux/mmc/host.h> |
29 | #include <linux/io.h> | 29 | #include <linux/io.h> |
30 | #include <linux/regulator/consumer.h> | 30 | #include <linux/regulator/consumer.h> |
31 | #include <linux/gpio.h> | ||
31 | 32 | ||
32 | #include <asm/sizes.h> | 33 | #include <asm/sizes.h> |
33 | 34 | ||
@@ -96,10 +97,18 @@ static inline void pxamci_init_ocr(struct pxamci_host *host) | |||
96 | 97 | ||
97 | static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) | 98 | static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd) |
98 | { | 99 | { |
100 | int on; | ||
101 | |||
99 | #ifdef CONFIG_REGULATOR | 102 | #ifdef CONFIG_REGULATOR |
100 | if (host->vcc) | 103 | if (host->vcc) |
101 | mmc_regulator_set_ocr(host->vcc, vdd); | 104 | mmc_regulator_set_ocr(host->vcc, vdd); |
102 | #endif | 105 | #endif |
106 | if (!host->vcc && host->pdata && | ||
107 | gpio_is_valid(host->pdata->gpio_power)) { | ||
108 | on = ((1 << vdd) & host->pdata->ocr_mask); | ||
109 | gpio_set_value(host->pdata->gpio_power, | ||
110 | !!on ^ host->pdata->gpio_power_invert); | ||
111 | } | ||
103 | if (!host->vcc && host->pdata && host->pdata->setpower) | 112 | if (!host->vcc && host->pdata && host->pdata->setpower) |
104 | host->pdata->setpower(mmc_dev(host->mmc), vdd); | 113 | host->pdata->setpower(mmc_dev(host->mmc), vdd); |
105 | } | 114 | } |
@@ -421,6 +430,12 @@ static int pxamci_get_ro(struct mmc_host *mmc) | |||
421 | { | 430 | { |
422 | struct pxamci_host *host = mmc_priv(mmc); | 431 | struct pxamci_host *host = mmc_priv(mmc); |
423 | 432 | ||
433 | if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro)) { | ||
434 | if (host->pdata->gpio_card_ro_invert) | ||
435 | return !gpio_get_value(host->pdata->gpio_card_ro); | ||
436 | else | ||
437 | return gpio_get_value(host->pdata->gpio_card_ro); | ||
438 | } | ||
424 | if (host->pdata && host->pdata->get_ro) | 439 | if (host->pdata && host->pdata->get_ro) |
425 | return !!host->pdata->get_ro(mmc_dev(mmc)); | 440 | return !!host->pdata->get_ro(mmc_dev(mmc)); |
426 | /* | 441 | /* |
@@ -534,7 +549,7 @@ static int pxamci_probe(struct platform_device *pdev) | |||
534 | struct mmc_host *mmc; | 549 | struct mmc_host *mmc; |
535 | struct pxamci_host *host = NULL; | 550 | struct pxamci_host *host = NULL; |
536 | struct resource *r, *dmarx, *dmatx; | 551 | struct resource *r, *dmarx, *dmatx; |
537 | int ret, irq; | 552 | int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; |
538 | 553 | ||
539 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 554 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
540 | irq = platform_get_irq(pdev, 0); | 555 | irq = platform_get_irq(pdev, 0); |
@@ -661,13 +676,63 @@ static int pxamci_probe(struct platform_device *pdev) | |||
661 | } | 676 | } |
662 | host->dma_drcmrtx = dmatx->start; | 677 | host->dma_drcmrtx = dmatx->start; |
663 | 678 | ||
679 | if (host->pdata) { | ||
680 | gpio_cd = host->pdata->gpio_card_detect; | ||
681 | gpio_ro = host->pdata->gpio_card_ro; | ||
682 | gpio_power = host->pdata->gpio_power; | ||
683 | } | ||
684 | if (gpio_is_valid(gpio_power)) { | ||
685 | ret = gpio_request(gpio_power, "mmc card power"); | ||
686 | if (ret) { | ||
687 | dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", gpio_power); | ||
688 | goto out; | ||
689 | } | ||
690 | gpio_direction_output(gpio_power, | ||
691 | host->pdata->gpio_power_invert); | ||
692 | } | ||
693 | if (gpio_is_valid(gpio_ro)) { | ||
694 | ret = gpio_request(gpio_ro, "mmc card read only"); | ||
695 | if (ret) { | ||
696 | dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_power); | ||
697 | goto err_gpio_ro; | ||
698 | } | ||
699 | gpio_direction_input(gpio_ro); | ||
700 | } | ||
701 | if (gpio_is_valid(gpio_cd)) { | ||
702 | ret = gpio_request(gpio_cd, "mmc card detect"); | ||
703 | if (ret) { | ||
704 | dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_power); | ||
705 | goto err_gpio_cd; | ||
706 | } | ||
707 | gpio_direction_input(gpio_cd); | ||
708 | |||
709 | ret = request_irq(gpio_to_irq(gpio_cd), pxamci_detect_irq, | ||
710 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
711 | "mmc card detect", mmc); | ||
712 | if (ret) { | ||
713 | dev_err(&pdev->dev, "failed to request card detect IRQ\n"); | ||
714 | goto err_request_irq; | ||
715 | } | ||
716 | } | ||
717 | |||
664 | if (host->pdata && host->pdata->init) | 718 | if (host->pdata && host->pdata->init) |
665 | host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); | 719 | host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); |
666 | 720 | ||
721 | if (gpio_is_valid(gpio_power) && host->pdata->setpower) | ||
722 | dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); | ||
723 | if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) | ||
724 | dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); | ||
725 | |||
667 | mmc_add_host(mmc); | 726 | mmc_add_host(mmc); |
668 | 727 | ||
669 | return 0; | 728 | return 0; |
670 | 729 | ||
730 | err_request_irq: | ||
731 | gpio_free(gpio_cd); | ||
732 | err_gpio_cd: | ||
733 | gpio_free(gpio_ro); | ||
734 | err_gpio_ro: | ||
735 | gpio_free(gpio_power); | ||
671 | out: | 736 | out: |
672 | if (host) { | 737 | if (host) { |
673 | if (host->dma >= 0) | 738 | if (host->dma >= 0) |
@@ -688,12 +753,26 @@ static int pxamci_probe(struct platform_device *pdev) | |||
688 | static int pxamci_remove(struct platform_device *pdev) | 753 | static int pxamci_remove(struct platform_device *pdev) |
689 | { | 754 | { |
690 | struct mmc_host *mmc = platform_get_drvdata(pdev); | 755 | struct mmc_host *mmc = platform_get_drvdata(pdev); |
756 | int gpio_cd = -1, gpio_ro = -1, gpio_power = -1; | ||
691 | 757 | ||
692 | platform_set_drvdata(pdev, NULL); | 758 | platform_set_drvdata(pdev, NULL); |
693 | 759 | ||
694 | if (mmc) { | 760 | if (mmc) { |
695 | struct pxamci_host *host = mmc_priv(mmc); | 761 | struct pxamci_host *host = mmc_priv(mmc); |
696 | 762 | ||
763 | if (host->pdata) { | ||
764 | gpio_cd = host->pdata->gpio_card_detect; | ||
765 | gpio_ro = host->pdata->gpio_card_ro; | ||
766 | gpio_power = host->pdata->gpio_power; | ||
767 | } | ||
768 | if (gpio_is_valid(gpio_cd)) { | ||
769 | free_irq(gpio_to_irq(gpio_cd), mmc); | ||
770 | gpio_free(gpio_cd); | ||
771 | } | ||
772 | if (gpio_is_valid(gpio_ro)) | ||
773 | gpio_free(gpio_ro); | ||
774 | if (gpio_is_valid(gpio_power)) | ||
775 | gpio_free(gpio_power); | ||
697 | if (host->vcc) | 776 | if (host->vcc) |
698 | regulator_put(host->vcc); | 777 | regulator_put(host->vcc); |
699 | 778 | ||