]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
hsmmc gpio updates
authorDavid Brownell <dbrownell@users.sourceforge.net>
Fri, 7 Nov 2008 05:15:30 +0000 (21:15 -0800)
committerTony Lindgren <tony@atomide.com>
Fri, 7 Nov 2008 19:51:08 +0000 (11:51 -0800)
Rework card detect GPIO handling in the twl4030 MMC glue:  drive
it *only* from the hsmmc_info passed; remove most of remaining
"we know we're always a twl4030 GPIO" logic.

Add write-protect switch detection support to that glue.  Stub
in a not-present WP GPIO into most boards.  (Beagle's is real.)

Teach the hsmmc driver how to use the card detect and writeprotect
methods, and move some data structure init earlier so that when IRQs
come in, more of the data used by their handlers is initialized.

Verified on Beagle (WP, card detect events) and Overo (boots, both
card and wlan are seen).  Beagle behaves fully, and is the model
to follow for the common case where the TWL4030 gpio-0 card detect
magic is used.

Most hsmmc boards need to list what GPIOs they use with MMC...

Note that supporting card detect and writeprotect GPIOs on a
second MMC slot, or using the MMC3 controller, requires some
interface updates that aren't part of this patch.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Tony Lindgren <tony@atomide.com>
arch/arm/mach-omap2/board-2430sdp.c
arch/arm/mach-omap2/board-3430sdp.c
arch/arm/mach-omap2/board-ldp.c
arch/arm/mach-omap2/board-omap2evm.c
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/board-omap3evm.c
arch/arm/mach-omap2/board-omap3pandora.c
arch/arm/mach-omap2/board-overo.c
arch/arm/mach-omap2/mmc-twl4030.c
arch/arm/mach-omap2/mmc-twl4030.h
drivers/mmc/host/omap_hsmmc.c

index 19c1062d98945e744e01767d3fb4e429fa5c86ca..6aa83ace4c523eb00e3d9ac77fca4045343a7289 100644 (file)
@@ -395,6 +395,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = {
                .mmc            = 1,
                .wires          = 4,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {}      /* Terminator */
 };
index f2dea78c16c998fcaaf860a494a1a85439771704..7ee85e9b7c8b078025eb0a5008d5d0e2ae94491a 100644 (file)
@@ -450,11 +450,13 @@ static struct twl4030_hsmmc_info mmc[] __initdata = {
                .mmc            = 1,
                .wires          = 8,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {
                .mmc            = 2,
                .wires          = 8,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {}      /* Terminator */
 };
index 3ec8264ed19ef20997909e6733df94c86450bcaf..c2bb7262e1631f3a6ee7a962aadc0671ca2e3129 100644 (file)
@@ -346,6 +346,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = {
                .mmc            = 1,
                .wires          = 4,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {}      /* Terminator */
 };
index 1ad727e35ecd54f7af27e86689d102a95b23877e..84aeacf50aa9e8a1de22b38d18380ca3c09d5ea3 100644 (file)
@@ -345,6 +345,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = {
                .mmc            = 1,
                .wires          = 4,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {}      /* Terminator */
 };
index a4dc140ab3ecf4b3a30abe0d4308d0cc1658774c..c1de79554b4507ba013c5eb2301b63a257351d55 100644 (file)
@@ -116,12 +116,24 @@ static struct twl4030_usb_data beagle_usb_data = {
        .usb_mode       = T2_USB_MODE_ULPI,
 };
 
+static struct twl4030_hsmmc_info mmc[] __initdata = {
+       {
+               .mmc            = 1,
+               .wires          = 8,
+               .gpio_wp        = 29,
+       },
+       {}      /* Terminator */
+};
+
 static struct gpio_led gpio_leds[];
 
 static int beagle_twl_gpio_setup(struct device *dev,
                unsigned gpio, unsigned ngpio)
 {
        /* gpio + 0 is "mmc0_cd" (input/IRQ) */
+       omap_cfg_reg(AH8_34XX_GPIO29);
+       mmc[0].gpio_cd = gpio + 0;
+       hsmmc_init(mmc);
 
        /* REVISIT: need ehci-omap hooks for external VBUS
         * power switch and overcurrent detect
@@ -299,15 +311,6 @@ static void __init omap3beagle_flash_init(void)
        }
 }
 
-static struct twl4030_hsmmc_info mmc[] __initdata = {
-       {
-               .mmc            = 1,
-               .wires          = 8,
-               .gpio_cd        = TWL4030_GPIO_IRQ_NO(0),
-       },
-       {}      /* Terminator */
-};
-
 static void __init omap3_beagle_init(void)
 {
        omap3_beagle_i2c_init();
@@ -317,11 +320,6 @@ static void __init omap3_beagle_init(void)
        omap_board_config_size = ARRAY_SIZE(omap3_beagle_config);
        omap_serial_init();
 
-       omap_cfg_reg(AH8_34XX_GPIO29);
-       gpio_request(29, "mmc0_wp");
-       gpio_direction_input(29);
-       hsmmc_init(mmc);
-
        omap_cfg_reg(J25_34XX_GPIO170);
        gpio_request(170, "DVI_nPD");
        /* REVISIT leave DVI powered down until it's needed ... */
index dd8077e009f3c778dab77e0930e2fd06152801a8..42ab8264b0bed3b968c941f419dbe41bfec5ecdd 100644 (file)
@@ -240,6 +240,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = {
                .mmc            = 1,
                .wires          = 4,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {}      /* Terminator */
 };
index 9fd3499f71a59d63d60f239ea85de8f771447cef..8a6c75fea78ceb8e9940295510cdb544cf294d72 100644 (file)
@@ -208,6 +208,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = {
                .mmc            = 1,
                .wires          = 4,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {}      /* Terminator */
 };
index a45a44029cd8492a52c04d49ae6c2ad3ca849eb3..a1b3aba7a8cf9fc83558620e56bc4accdf7178bb 100644 (file)
@@ -213,11 +213,13 @@ static struct twl4030_hsmmc_info mmc[] __initdata = {
                .mmc            = 1,
                .wires          = 4,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {
                .mmc            = 2,
                .wires          = 4,
                .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
        },
        {}      /* Terminator */
 };
index db05f9016c7f134aa805928d39a6f1bf1b8aa133..ca8e358095abbca9bdc7f319b2b93d7184b2f21a 100644 (file)
 
 #if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
 
-#define TWL_GPIO_IMR1A         0x1C
-#define TWL_GPIO_ISR1A         0x19
 #define LDO_CLR                        0x00
 #define VSEL_S2_CLR            0x40
-#define GPIO_0_BIT_POS         (1 << 0)
 
 #define VMMC1_DEV_GRP          0x27
 #define VMMC1_CLR              0x00
@@ -58,27 +55,36 @@ static struct twl_mmc_controller {
        u16             control_devconf_offset;
        u32             devconf_loopback_clock;
        int             card_detect_gpio;
+       unsigned        card_wp_gpio;
        u8              twl_vmmc_dev_grp;
        u8              twl_mmc_dedicated;
 } hsmmc[] = {
        {
                .control_devconf_offset         = OMAP2_CONTROL_DEVCONF0,
                .devconf_loopback_clock         = OMAP2_MMCSDIO1ADPCLKISEL,
-               .card_detect_gpio               = OMAP_MAX_GPIO_LINES,
+               .card_detect_gpio               = -EINVAL,
                .twl_vmmc_dev_grp               = VMMC1_DEV_GRP,
                .twl_mmc_dedicated              = VMMC1_DEDICATED,
        },
        {
                /* control_devconf_offset set dynamically */
                .devconf_loopback_clock         = OMAP2_MMCSDIO2ADPCLKISEL,
+               .card_detect_gpio               = -EINVAL,
                .twl_vmmc_dev_grp               = VMMC2_DEV_GRP,
                .twl_mmc_dedicated              = VMMC2_DEDICATED,
        },
- };
+};
 
 static int twl_mmc1_card_detect(int irq)
 {
-       return gpio_get_value_cansleep(hsmmc[0].card_detect_gpio);
+       /* NOTE: assumes card detect signal is active-low */
+       return !gpio_get_value_cansleep(hsmmc[0].card_detect_gpio);
+}
+
+static int twl_mmc1_get_ro(struct device *dev, int slot)
+{
+       /* NOTE: assumes write protect signal is active-high */
+       return gpio_get_value_cansleep(hsmmc[0].card_wp_gpio);
 }
 
 /*
@@ -86,15 +92,19 @@ static int twl_mmc1_card_detect(int irq)
  */
 static int twl_mmc1_late_init(struct device *dev)
 {
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
        int ret = 0;
 
-       /*
-        * Configure TWL4030 GPIO parameters for MMC hotplug irq
-        */
        ret = gpio_request(hsmmc[0].card_detect_gpio, "mmc0_cd");
+       if (ret)
+               goto done;
+       ret = gpio_direction_input(hsmmc[0].card_detect_gpio);
        if (ret)
                goto err;
 
+       /* FIXME assumes this uses (a) TWL4030 and (b) GPIO-0 ...
+        * but that's not actually required.
+        */
        ret = twl4030_set_gpio_debounce(0, true);
        if (ret)
                goto err;
@@ -102,8 +112,10 @@ static int twl_mmc1_late_init(struct device *dev)
        return ret;
 
 err:
-       dev_err(dev, "Failed to configure TWL4030 GPIO IRQ\n");
-
+       dev_err(dev, "Failed to configure TWL4030 card detect\n");
+done:
+       mmc->slots[0].card_detect_irq = 0;
+       mmc->slots[0].card_detect = NULL;
        return ret;
 }
 
@@ -114,62 +126,25 @@ static void twl_mmc1_cleanup(struct device *dev)
 
 #ifdef CONFIG_PM
 
-/*
- * Mask and unmask MMC Card Detect Interrupt
- * mask : 1
- * unmask : 0
- */
-static int twl_mmc_mask_cd_interrupt(int mask)
-{
-       u8 reg = 0, ret = 0;
-
-       ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &reg, TWL_GPIO_IMR1A);
-       if (ret)
-               goto err;
-
-       reg = (mask == 1) ? (reg | GPIO_0_BIT_POS) : (reg & ~GPIO_0_BIT_POS);
-
-       ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, reg, TWL_GPIO_IMR1A);
-       if (ret)
-               goto err;
-
-       ret = twl4030_i2c_read_u8(TWL4030_MODULE_GPIO, &reg, TWL_GPIO_ISR1A);
-       if (ret)
-               goto err;
-
-       reg = (mask == 1) ? (reg | GPIO_0_BIT_POS) : (reg & ~GPIO_0_BIT_POS);
-
-       ret = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, reg, TWL_GPIO_ISR1A);
-       if (ret)
-               goto err;
-
-err:
-       return ret;
-}
-
-static int twl_mmc1_suspend(struct device *dev, int slot)
+static int twl_mmc_suspend(struct device *dev, int slot)
 {
-       int ret = 0;
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
 
-       disable_irq(hsmmc[0].card_detect_gpio);
-       ret = twl_mmc_mask_cd_interrupt(1);
-
-       return ret;
+       disable_irq(mmc->slots[0].card_detect_irq);
+       return 0;
 }
 
-static int twl_mmc1_resume(struct device *dev, int slot)
+static int twl_mmc_resume(struct device *dev, int slot)
 {
-       int ret = 0;
-
-       enable_irq(hsmmc[0].card_detect_gpio);
-       ret = twl_mmc_mask_cd_interrupt(0);
+       struct omap_mmc_platform_data *mmc = dev->platform_data;
 
-       return ret;
+       enable_irq(mmc->slots[0].card_detect_irq);
+       return 0;
 }
 
 #else
-#define twl_mmc1_suspend       NULL
-#define twl_mmc1_resume                NULL
+#define twl_mmc_suspend        NULL
+#define twl_mmc_resume NULL
 #endif
 
 /*
@@ -362,26 +337,52 @@ void __init hsmmc_init(struct twl4030_hsmmc_info *controllers)
                                        MMC_VDD_29_30 |
                                        MMC_VDD_30_31 | MMC_VDD_31_32;
                mmc->slots[0].wires = c->wires;
-               if (c->gpio_cd != -EINVAL)
-                       mmc->slots[0].card_detect_irq = c->gpio_cd;
                mmc->dma_mask = 0xffffffff;
 
+               /* NOTE:  we assume OMAP's MMC1 and MMC2 use
+                * the TWL4030's VMMC1 and VMMC2, respectively;
+                * and that OMAP's MMC3 isn't used.
+                */
+
                switch (c->mmc) {
                case 1:
-                       mmc->init = twl_mmc1_late_init;
-                       mmc->cleanup = twl_mmc1_cleanup;
-                       mmc->suspend = twl_mmc1_suspend;
-                       mmc->resume = twl_mmc1_resume;
                        mmc->slots[0].set_power = twl_mmc1_set_power;
-                       mmc->slots[0].card_detect = twl_mmc1_card_detect;
+                       if (gpio_is_valid(c->gpio_cd)) {
+                               mmc->slots[0].card_detect_irq =
+                                               gpio_to_irq(c->gpio_cd);
+                               mmc->suspend = twl_mmc_suspend;
+                               mmc->resume = twl_mmc_resume;
+
+                               /* NOTE: hsmmc[0] is hard-wired ... */
+                               hsmmc[0].card_detect_gpio = c->gpio_cd;
+                               mmc->init = twl_mmc1_late_init;
+                               mmc->cleanup = twl_mmc1_cleanup;
+                               mmc->slots[0].card_detect =
+                                               twl_mmc1_card_detect;
+                       }
+                       if (gpio_is_valid(c->gpio_wp)) {
+                               gpio_request(c->gpio_wp, "mmc0_wp");
+                               gpio_direction_input(c->gpio_wp);
+
+                               /* NOTE: hsmmc[0] is hard-wired ... */
+                               hsmmc[0].card_wp_gpio = c->gpio_wp;
+                               mmc->slots[0].get_ro = twl_mmc1_get_ro;
+                       }
                        hsmmc_data[0] = mmc;
                        break;
                case 2:
+                       /* FIXME rework interfaces so that mmc2 (and mmc3) can
+                        * be fully functional... hsmmc[] shouldn't hold gpios.
+                        */
                        mmc->slots[0].set_power = twl_mmc2_set_power;
+                       if (gpio_is_valid(c->gpio_cd))
+                               pr_warning("MMC2 detect nyet supported!\n");
+                       if (gpio_is_valid(c->gpio_wp))
+                               pr_warning("MMC2 WP nyet supported!\n");
                        hsmmc_data[1] = mmc;
                        break;
                default:
-                       pr_err("Unknown MMC configuration!\n");
+                       pr_err("MMC%d configuration not supported!\n", c->mmc);
                        return;
                }
        }
index 5d2f7801a31222a54c46fc1cece1cee4f40f778e..a2e60fed79827c2413bcc9a7d527a8ee6d8a1497 100644 (file)
@@ -10,6 +10,7 @@ struct twl4030_hsmmc_info {
        u8      mmc;            /* controller 1/2/3 */
        u8      wires;          /* 1/4/8 wires */
        int     gpio_cd;        /* or -EINVAL */
+       int     gpio_wp;        /* or -EINVAL */
 };
 
 #if    defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
index 8f92f8092778fcf5b303dacb185df374b40d7f7d..0df684181a03139c1d6efaa7c409851044f77c55 100644 (file)
@@ -825,10 +825,33 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                OMAP_HSMMC_WRITE(host->base, CON,
                                OMAP_HSMMC_READ(host->base, CON) | OD);
 }
-/* NOTE: Read only switch not supported yet */
+
+static int omap_hsmmc_get_cd(struct mmc_host *mmc)
+{
+       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_mmc_platform_data *pdata = host->pdata;
+
+       if (!pdata->slots[0].card_detect)
+               return -ENOSYS;
+       return pdata->slots[0].card_detect(pdata->slots[0].card_detect_irq);
+}
+
+static int omap_hsmmc_get_ro(struct mmc_host *mmc)
+{
+       struct mmc_omap_host *host = mmc_priv(mmc);
+       struct omap_mmc_platform_data *pdata = host->pdata;
+
+       if (!pdata->slots[0].get_ro)
+               return -ENOSYS;
+       return pdata->slots[0].get_ro(host->dev, 0);
+}
+
 static struct mmc_host_ops mmc_omap_ops = {
        .request = omap_mmc_request,
        .set_ios = omap_mmc_set_ios,
+       .get_cd = omap_hsmmc_get_cd,
+       .get_ro = omap_hsmmc_get_ro,
+       /* NYET -- enable_sdio_irq */
 };
 
 static int __init omap_mmc_probe(struct platform_device *pdev)
@@ -878,6 +901,10 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        host->slot_id   = 0;
        host->mapbase   = res->start;
        host->base      = ioremap(host->mapbase, SZ_4K);
+
+       platform_set_drvdata(pdev, host);
+       INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
+
        mmc->ops        = &mmc_omap_ops;
        mmc->f_min      = 400000;
        mmc->f_max      = 52000000;
@@ -970,6 +997,14 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
+       if (pdata->init != NULL) {
+               if (pdata->init(&pdev->dev) != 0) {
+                       dev_dbg(mmc_dev(host->mmc),
+                               "Unable to configure MMC IRQs\n");
+                       goto err_irq_cd_init;
+               }
+       }
+
        /* Request IRQ for card detect */
        if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) {
                ret = request_irq(mmc_slot(host).card_detect_irq,
@@ -984,19 +1019,9 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                }
        }
 
-       INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
-       if (pdata->init != NULL) {
-               if (pdata->init(&pdev->dev) != 0) {
-                       dev_dbg(mmc_dev(host->mmc),
-                               "Unable to configure MMC IRQs\n");
-                       goto err_irq_cd_init;
-               }
-       }
-
        OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
        OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
 
-       platform_set_drvdata(pdev, host);
        mmc_add_host(mmc);
 
        if (host->pdata->slots[host->slot_id].name != NULL) {
@@ -1017,9 +1042,9 @@ err_cover_switch:
        device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
 err_slot_name:
        mmc_remove_host(mmc);
-err_irq_cd_init:
-       free_irq(mmc_slot(host).card_detect_irq, host);
 err_irq_cd:
+       free_irq(mmc_slot(host).card_detect_irq, host);
+err_irq_cd_init:
        free_irq(host->irq, host);
 err_irq:
        clk_disable(host->fclk);