diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 17 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 95 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 11 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_io.c | 7 | ||||
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 50 |
6 files changed, 185 insertions, 2 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index ab37a6d9d32a..81fe13735947 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -14,3 +14,10 @@ config MMC_UNSAFE_RESUME This option is usually just for embedded systems which use a MMC/SD card for rootfs. Most people should say N here. +config MMC_EMBEDDED_SDIO + boolean "MMC embedded SDIO device support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, support will be added for embedded SDIO + devices which do not contain the necessary enumeration + support in hardware to be properly detected. diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d84c880fac84..47a90cb62939 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1028,6 +1028,23 @@ EXPORT_SYMBOL(mmc_resume_host); #endif +#ifdef CONFIG_MMC_EMBEDDED_SDIO +void mmc_set_embedded_sdio_data(struct mmc_host *host, + struct sdio_cis *cis, + struct sdio_cccr *cccr, + struct sdio_embedded_func *funcs, + int num_funcs, + unsigned int quirks) +{ + host->embedded_sdio_data.cis = cis; + host->embedded_sdio_data.cccr = cccr; + host->embedded_sdio_data.funcs = funcs; + host->embedded_sdio_data.num_funcs = num_funcs; + host->embedded_sdio_data.quirks = quirks; +} +EXPORT_SYMBOL(mmc_set_embedded_sdio_data); +#endif + static int __init mmc_init(void) { int ret; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index fb99ccff9080..4784073f3af3 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -24,6 +24,10 @@ #include "sdio_ops.h" #include "sdio_cis.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include <linux/mmc/sdio_ids.h> +#endif + static int sdio_read_fbr(struct sdio_func *func) { int ret; @@ -164,6 +168,33 @@ static int sdio_enable_wide(struct mmc_card *card) return 0; } +#ifdef CONFIG_MMC_EMBEDDED_SDIO +/* + * Disconnect the pull-up resistor on CD/DAT[3] if requested by the card. + * If card detection is not needed, this can save some power. + */ +static int sdio_disable_cd(struct mmc_card *card) +{ + int ret; + u8 ctrl; + + if (!card->cccr.disable_cd) + return 0; + + ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); + if (ret) + return ret; + + ctrl |= SDIO_BUS_CD_DISABLE; + + ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); + if (ret) + return ret; + + return 0; +} +#endif + /* * Test if the card supports high-speed mode and, if so, switch to it. */ @@ -275,12 +306,18 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ocr &= ~0x7F; } +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (!host->embedded_sdio_data.quirks & MMC_QUIRK_VDD_165_195) { +#endif if (ocr & MMC_VDD_165_195) { printk(KERN_WARNING "%s: SDIO card claims to support the " "incompletely defined 'low voltage range'. This " "will be ignored.\n", mmc_hostname(host)); ocr &= ~MMC_VDD_165_195; } +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif host->ocr = mmc_select_voltage(host, ocr); @@ -314,6 +351,11 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) */ funcs = (ocr & 0x70000000) >> 28; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) + funcs = host->embedded_sdio_data.num_funcs; +#endif + /* * Allocate card structure. */ @@ -323,6 +365,10 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) goto err; } +#ifdef CONFIG_MMC_EMBEDDED_SDIO + card->quirks = host->embedded_sdio_data.quirks; +#endif + card->type = MMC_TYPE_SDIO; card->sdio_funcs = funcs; @@ -351,9 +397,25 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) /* * Read the common registers. */ +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cccr) + memcpy(&card->cccr, host->embedded_sdio_data.cccr, + sizeof(struct sdio_cccr)); + else { +#endif err = sdio_read_cccr(card); if (err) goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif + +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.cis) + memcpy(&card->cis, host->embedded_sdio_data.cis, + sizeof(struct sdio_cis)); + else { +#endif /* * Read the common CIS tuples. @@ -361,6 +423,9 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) err = sdio_read_common_cis(card); if (err) goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif /* * Switch to high-speed (if supported). @@ -391,13 +456,43 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) if (err) goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + /* + * Disconnect card detection pull-up resistor (if requested by card). + */ + err = sdio_disable_cd(card); + if (err) + goto remove; +#endif + /* * Initialize (but don't add) all present functions. */ for (i = 0;i < funcs;i++) { +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (host->embedded_sdio_data.funcs) { + struct sdio_func *tmp; + + tmp = sdio_alloc_func(host->card); + if (IS_ERR(tmp)) + goto remove; + tmp->num = (i + 1); + card->sdio_func[i] = tmp; + tmp->class = host->embedded_sdio_data.funcs[i].f_class; + tmp->max_blksize = + host->embedded_sdio_data.funcs[i].f_maxblksize; + tmp->vendor = card->cis.vendor; + tmp->device = card->cis.device; + } else { +#endif + err = sdio_init_func(host->card, i + 1); if (err) goto remove; +#ifdef CONFIG_MMC_EMBEDDED_SDIO + } +#endif + } mmc_release_host(host); diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 46284b527397..05dd55f35110 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -20,6 +20,10 @@ #include "sdio_cis.h" #include "sdio_bus.h" +#ifdef CONFIG_MMC_EMBEDDED_SDIO +#include <linux/mmc/host.h> +#endif + #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) #define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) @@ -202,6 +206,13 @@ static void sdio_release_func(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); +#ifdef CONFIG_MMC_EMBEDDED_SDIO + /* + * If this device is embedded then we never allocated + * cis tables for this func + */ + if (!func->card->host->embedded_sdio_data.funcs) +#endif sdio_free_func_cis(func); if (func->info) diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index f61fc2d4cd0a..de4ad9d4321a 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -624,7 +624,14 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, BUG_ON(!func); +#ifdef CONFIG_MMC_EMBEDDED_SDIO +/*allow SDIO FN0 writes outside of VS CCCR*/ +#define MMC_QUIRK_LENIENT_FUNC0 (1<<1) + if ((addr < 0xF0 || addr > 0xFF) && + (!func->card->quirks & MMC_QUIRK_LENIENT_FUNC0)) { +#else if (addr < 0xF0 || addr > 0xFF) { +#endif if (err_ret) *err_ret = -EINVAL; return; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 96f595954604..7577c501502d 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -918,10 +918,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_OFF: - mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + if (mmc_slot(host).set_power) + mmc_slot(host).set_power(host->dev, + host->slot_id, 0, 0); break; case MMC_POWER_UP: - mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd); + if (mmc_slot(host).set_power) + mmc_slot(host).set_power(host->dev, + host->slot_id, 1, ios->vdd); break; } @@ -996,6 +1000,27 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) OMAP_HSMMC_READ(host->base, CON) | OD); } +#ifdef CONFIG_MMC_EMBEDDED_SDIO +static void omap_hsmmc_status_notify_cb(int card_present, void *dev_id) +{ + struct mmc_omap_host *host = dev_id; + struct omap_mmc_slot_data *slot = &mmc_slot(host); + + printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc), + card_present); + + host->carddetect = slot->card_detect(slot->card_detect_irq); + + sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); + if (host->carddetect) { + mmc_detect_change(host->mmc, (HZ * 200) / 1000); + } else { + mmc_omap_reset_controller_fsm(host, SRD); + mmc_detect_change(host->mmc, (HZ * 50) / 1000); + } +} +#endif + static int omap_hsmmc_get_cd(struct mmc_host *mmc) { struct mmc_omap_host *host = mmc_priv(mmc); @@ -1142,6 +1167,19 @@ static int __init omap_mmc_probe(struct platform_device *pdev) } #endif +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (pdev->id == CONFIG_TIWLAN_MMC_CONTROLLER-1) { + if (pdata->slots[0].embedded_sdio != NULL) { + mmc_set_embedded_sdio_data(mmc, + &pdata->slots[0].embedded_sdio->cis, + &pdata->slots[0].embedded_sdio->cccr, + pdata->slots[0].embedded_sdio->funcs, + pdata->slots[0].embedded_sdio->num_funcs, + pdata->slots[0].embedded_sdio->quirks); + } + } +#endif + platform_set_drvdata(pdev, host); INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect); @@ -1272,6 +1310,14 @@ static int __init omap_mmc_probe(struct platform_device *pdev) goto err_irq_cd; } } +#ifdef CONFIG_MMC_EMBEDDED_SDIO + else if (mmc_slot(host).register_status_notify) { + if (pdev->id == CONFIG_TIWLAN_MMC_CONTROLLER-1) { + mmc_slot(host).register_status_notify( + omap_hsmmc_status_notify_cb, host); + } + } +#endif OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); |