From: David Brownell Date: Fri, 7 Nov 2008 05:15:30 +0000 (-0800) Subject: hsmmc gpio updates X-Git-Tag: v2.6.28-omap1~186 X-Git-Url: http://www.pilppa.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=1ebacbfba809c558dcc05ecc752e26a8c1314ca9;p=linux-2.6-omap-h63xx.git hsmmc gpio updates 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 Signed-off-by: Tony Lindgren --- diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c index 19c1062d989..6aa83ace4c5 100644 --- a/arch/arm/mach-omap2/board-2430sdp.c +++ b/arch/arm/mach-omap2/board-2430sdp.c @@ -395,6 +395,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = { .mmc = 1, .wires = 4, .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, }, {} /* Terminator */ }; diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index f2dea78c16c..7ee85e9b7c8 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -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 */ }; diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index 3ec8264ed19..c2bb7262e16 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -346,6 +346,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = { .mmc = 1, .wires = 4, .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, }, {} /* Terminator */ }; diff --git a/arch/arm/mach-omap2/board-omap2evm.c b/arch/arm/mach-omap2/board-omap2evm.c index 1ad727e35ec..84aeacf50aa 100644 --- a/arch/arm/mach-omap2/board-omap2evm.c +++ b/arch/arm/mach-omap2/board-omap2evm.c @@ -345,6 +345,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = { .mmc = 1, .wires = 4, .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, }, {} /* Terminator */ }; diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index a4dc140ab3e..c1de79554b4 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -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 ... */ diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index dd8077e009f..42ab8264b0b 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -240,6 +240,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = { .mmc = 1, .wires = 4, .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, }, {} /* Terminator */ }; diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 9fd3499f71a..8a6c75fea78 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -208,6 +208,7 @@ static struct twl4030_hsmmc_info mmc[] __initdata = { .mmc = 1, .wires = 4, .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, }, {} /* Terminator */ }; diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index a45a44029cd..a1b3aba7a8c 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -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 */ }; diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c index db05f9016c7..ca8e358095a 100644 --- a/arch/arm/mach-omap2/mmc-twl4030.c +++ b/arch/arm/mach-omap2/mmc-twl4030.c @@ -27,11 +27,8 @@ #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, ®, 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, ®, 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; } } diff --git a/arch/arm/mach-omap2/mmc-twl4030.h b/arch/arm/mach-omap2/mmc-twl4030.h index 5d2f7801a31..a2e60fed798 100644 --- a/arch/arm/mach-omap2/mmc-twl4030.h +++ b/arch/arm/mach-omap2/mmc-twl4030.h @@ -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) || \ diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 8f92f809277..0df684181a0 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -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);