diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-10 21:35:22 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-10 21:35:22 (GMT) |
commit | 07888238f55056605cd23aa4ea3ca97d5e15938f (patch) | |
tree | 1c89230d1d0ceab4a8b7f3a9a93a032ccca13aef /drivers/mmc/host | |
parent | 23adbe12ef7d3d4195e80800ab36b37bee28cd03 (diff) | |
parent | 11bc9381b277a65ea4b3fe994e658f4a78512139 (diff) | |
download | linux-07888238f55056605cd23aa4ea3ca97d5e15938f.tar.xz |
Merge tag 'mmc-updates-for-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC update from Chris Ball:
"MMC highlights for 3.16:
Core:
- support HS400 mode of eMMC 5.0, via DT bindings mmc-hs400-1_{2,8}v
- if card init at 3.3v doesn't work, try 1.8v and 1.2v too
Drivers:
- moxart: New driver for MOXA ART SoCs
- rtsx_usb_sdmmc: New driver for Realtek USB card readers
- sdhci: Large rework around IRQ/regulator handling, remove card_tasklet
- sdhci-pci-o2micro: Add SeaBird SeaEagle SD3 support
- sunxi: New driver for Allwinner sunxi SoCs
- usdhi6rol0: New driver for Renesas SD/SDIO controller"
* tag 'mmc-updates-for-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (95 commits)
mmc: sdhci-s3c: use mmc_of_parse and remove the card_tasklet
mmc: add a driver for the Renesas usdhi6rol0 SD/SDIO host controller
mmc: sdhci-of-esdhc: Fixup compile error
mmc: tegra: fix reporting of base clock frequency
mmc: tegra: disable UHS modes
mmc: sdhci-dove: use mmc_of_parse() and remove card_tasklet CD handler
MAINTAINERS: mmc: Add path to git tree
mmc: dove: fix missing MACH_DOVE dependency
mmc: sdhci: SD tuning is broken for some controllers
mmc: sdhci-esdhc-imx: fix mmc ddr mode regression issue
mmc: sdhci-pci-o2micro: Add SeaBird SeaEagle SD3 support
mmc: omap_hsmmc: split omap-dma header file
mmc: omap_hsmmc: fix cmd23 multiblock read/write
mmc: omap_hsmmc: use devm_ioremap_resource
mmc: omap_hsmmc: use devm_request_threaded_irq
mmc: omap_hsmmc: use devm_request_irq
mmc: omap_hsmmc: use devm_clk_get
mmc: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs
mmc: wmt-sdmmc: Use GFP_KERNEL instead of hard-coded value
mmc: omap: Use DIV_ROUND_UP instead of open coded
...
Diffstat (limited to 'drivers/mmc/host')
41 files changed, 3435 insertions, 1015 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 779368b..7fee224 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -168,7 +168,7 @@ config MMC_SDHCI_ESDHC_IMX config MMC_SDHCI_DOVE tristate "SDHCI support on Marvell's Dove SoC" - depends on ARCH_DOVE + depends on ARCH_DOVE || MACH_DOVE depends on MMC_SDHCI_PLTFM select MMC_SDHCI_IO_ACCESSORS help @@ -283,6 +283,15 @@ config MMC_SDHCI_BCM2835 If unsure, say N. +config MMC_MOXART + tristate "MOXART SD/MMC Host Controller support" + depends on ARCH_MOXART && MMC + help + This selects support for the MOXART SD/MMC Host Controller. + MOXA provides one multi-functional card reader which can + be found on some embedded hardware such as UC-7112-LX. + If you have a controller with this interface, say Y here. + config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP @@ -688,6 +697,12 @@ config MMC_WMT To compile this driver as a module, choose M here: the module will be called wmt-sdmmc. +config MMC_USDHI6ROL0 + tristate "Renesas USDHI6ROL0 SD/SDIO Host Controller support" + help + This selects support for the Renesas USDHI6ROL0 SD/SDIO + Host Controller + config MMC_REALTEK_PCI tristate "Realtek PCI-E SD/MMC Card Interface Driver" depends on MFD_RTSX_PCI diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 61cbc24..7f81ddf 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -50,7 +50,9 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_USHC) += ushc.o obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o +obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o +obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 42706ea..aece7ca 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -820,16 +820,9 @@ static void atmci_pdc_complete(struct atmel_mci *host) atmci_pdc_cleanup(host); - /* - * If the card was removed, data will be NULL. No point trying - * to send the stop command or waiting for NBUSY in this case. - */ - if (host->data) { - dev_dbg(&host->pdev->dev, - "(%s) set pending xfer complete\n", __func__); - atmci_set_pending(host, EVENT_XFER_COMPLETE); - tasklet_schedule(&host->tasklet); - } + dev_dbg(&host->pdev->dev, "(%s) set pending xfer complete\n", __func__); + atmci_set_pending(host, EVENT_XFER_COMPLETE); + tasklet_schedule(&host->tasklet); } static void atmci_dma_cleanup(struct atmel_mci *host) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 3423c5e..0fbc53a 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -187,7 +187,7 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) unsigned long actual; u8 div = priv->ciu_div + 1; - if (ios->timing == MMC_TIMING_UHS_DDR50) { + if (ios->timing == MMC_TIMING_MMC_DDR52) { mci_writel(host, CLKSEL, priv->ddr_timing); /* Should be double rate for DDR mode */ if (ios->bus_width == MMC_BUS_WIDTH_8) @@ -386,8 +386,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, /* Common capabilities of Exynos4/Exynos5 SoC */ static unsigned long exynos_dwmmc_caps[4] = { - MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR | - MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23, + MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23, MMC_CAP_CMD23, MMC_CAP_CMD23, MMC_CAP_CMD23, @@ -426,7 +425,7 @@ static int dw_mci_exynos_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, drv_data); } -const struct dev_pm_ops dw_mci_exynos_pmops = { +static const struct dev_pm_ops dw_mci_exynos_pmops = { SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume) .resume_noirq = dw_mci_exynos_resume_noirq, .thaw_noirq = dw_mci_exynos_resume_noirq, diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index cced599..1ac227c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -235,12 +235,6 @@ err: } #endif /* defined(CONFIG_DEBUG_FS) */ -static void dw_mci_set_timeout(struct dw_mci *host) -{ - /* timeout (maximum) */ - mci_writel(host, TMOUT, 0xffffffff); -} - static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct mmc_data *data; @@ -257,9 +251,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) (cmd->opcode == SD_IO_RW_DIRECT && ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) cmdr |= SDMMC_CMD_STOP; - else - if (cmd->opcode != MMC_SEND_STATUS && cmd->data) - cmdr |= SDMMC_CMD_PRV_DAT_WAIT; + else if (cmd->opcode != MMC_SEND_STATUS && cmd->data) + cmdr |= SDMMC_CMD_PRV_DAT_WAIT; if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ @@ -850,8 +843,6 @@ static void __dw_mci_start_request(struct dw_mci *host, u32 cmdflags; mrq = slot->mrq; - if (host->pdata->select_slot) - host->pdata->select_slot(slot->id); host->cur_slot = slot; host->mrq = mrq; @@ -864,7 +855,7 @@ static void __dw_mci_start_request(struct dw_mci *host, data = cmd->data; if (data) { - dw_mci_set_timeout(host); + mci_writel(host, TMOUT, 0xFFFFFFFF); mci_writel(host, BYTCNT, data->blksz*data->blocks); mci_writel(host, BLKSIZ, data->blksz); } @@ -962,7 +953,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) regs = mci_readl(slot->host, UHS_REG); /* DDR mode set */ - if (ios->timing == MMC_TIMING_UHS_DDR50) + if (ios->timing == MMC_TIMING_MMC_DDR52) regs |= ((0x1 << slot->id) << 16); else regs &= ~((0x1 << slot->id) << 16); @@ -985,17 +976,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - /* Power up slot */ - if (slot->host->pdata->setpower) - slot->host->pdata->setpower(slot->id, mmc->ocr_avail); regs = mci_readl(slot->host, PWREN); regs |= (1 << slot->id); mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_OFF: - /* Power down slot */ - if (slot->host->pdata->setpower) - slot->host->pdata->setpower(slot->id, 0); regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); mci_writel(slot->host, PWREN, regs); @@ -1009,15 +994,13 @@ static int dw_mci_get_ro(struct mmc_host *mmc) { int read_only; struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci_board *brd = slot->host->pdata; + int gpio_ro = mmc_gpio_get_ro(mmc); /* Use platform get_ro function, else try on board write protect */ if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) read_only = 0; - else if (brd->get_ro) - read_only = brd->get_ro(slot->id); - else if (gpio_is_valid(slot->wp_gpio)) - read_only = gpio_get_value(slot->wp_gpio); + else if (!IS_ERR_VALUE(gpio_ro)) + read_only = gpio_ro; else read_only = mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; @@ -1039,8 +1022,6 @@ static int dw_mci_get_cd(struct mmc_host *mmc) /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) present = 1; - else if (brd->get_cd) - present = !brd->get_cd(slot->id); else if (!IS_ERR_VALUE(gpio_cd)) present = gpio_cd; else @@ -1248,7 +1229,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->error = -EIO; } - dev_err(host->dev, "data error, status 0x%08x\n", status); + dev_dbg(host->dev, "data error, status 0x%08x\n", status); /* * After an error, there may be data lingering @@ -2045,86 +2026,15 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) return quirks; } - -/* find out bus-width for a given slot */ -static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) -{ - struct device_node *np = dw_mci_of_find_slot_node(dev, slot); - u32 bus_wd = 1; - - if (!np) - return 1; - - if (of_property_read_u32(np, "bus-width", &bus_wd)) - dev_err(dev, "bus-width property not found, assuming width" - " as 1\n"); - return bus_wd; -} - -/* find the write protect gpio for a given slot; or -1 if none specified */ -static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) -{ - struct device_node *np = dw_mci_of_find_slot_node(dev, slot); - int gpio; - - if (!np) - return -EINVAL; - - gpio = of_get_named_gpio(np, "wp-gpios", 0); - - /* Having a missing entry is valid; return silently */ - if (!gpio_is_valid(gpio)) - return -EINVAL; - - if (devm_gpio_request(dev, gpio, "dw-mci-wp")) { - dev_warn(dev, "gpio [%d] request failed\n", gpio); - return -EINVAL; - } - - return gpio; -} - -/* find the cd gpio for a given slot */ -static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, - struct mmc_host *mmc) -{ - struct device_node *np = dw_mci_of_find_slot_node(dev, slot); - int gpio; - - if (!np) - return; - - gpio = of_get_named_gpio(np, "cd-gpios", 0); - - /* Having a missing entry is valid; return silently */ - if (!gpio_is_valid(gpio)) - return; - - if (mmc_gpio_request_cd(mmc, gpio, 0)) - dev_warn(dev, "gpio [%d] request failed\n", gpio); -} #else /* CONFIG_OF */ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) { return 0; } -static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) -{ - return 1; -} static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) { return NULL; } -static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) -{ - return -EINVAL; -} -static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, - struct mmc_host *mmc) -{ - return; -} #endif /* CONFIG_OF */ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) @@ -2134,7 +2044,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) const struct dw_mci_drv_data *drv_data = host->drv_data; int ctrl_id, ret; u32 freq[2]; - u8 bus_width; mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); if (!mmc) @@ -2158,17 +2067,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->f_max = freq[1]; } - if (host->pdata->get_ocr) - mmc->ocr_avail = host->pdata->get_ocr(id); - else - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - - /* - * Start with slot power disabled, it will be enabled when a card - * is detected. - */ - if (host->pdata->setpower) - host->pdata->setpower(id, 0); + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; if (host->pdata->caps) mmc->caps = host->pdata->caps; @@ -2189,19 +2088,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->caps2) mmc->caps2 = host->pdata->caps2; - if (host->pdata->get_bus_wd) - bus_width = host->pdata->get_bus_wd(slot->id); - else if (host->dev->of_node) - bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id); - else - bus_width = 1; - - switch (bus_width) { - case 8: - mmc->caps |= MMC_CAP_8_BIT_DATA; - case 4: - mmc->caps |= MMC_CAP_4_BIT_DATA; - } + mmc_of_parse(mmc); if (host->pdata->blk_settings) { mmc->max_segs = host->pdata->blk_settings->max_segs; @@ -2226,8 +2113,10 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) #endif /* CONFIG_MMC_DW_IDMAC */ } - slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id); - dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc); + if (dw_mci_get_cd(mmc)) + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); + else + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); ret = mmc_add_host(mmc); if (ret) @@ -2249,10 +2138,6 @@ err_setup_bus: static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) { - /* Shutdown detect IRQ */ - if (slot->host->pdata->exit) - slot->host->pdata->exit(id); - /* Debugfs stuff is cleaned up by mmc core */ mmc_remove_host(slot->mmc); slot->host->slot[id] = NULL; @@ -2399,24 +2284,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(ret); } - if (of_find_property(np, "keep-power-in-suspend", NULL)) - pdata->pm_caps |= MMC_PM_KEEP_POWER; - - if (of_find_property(np, "enable-sdio-wakeup", NULL)) - pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; - if (of_find_property(np, "supports-highspeed", NULL)) pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; - if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL)) - pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR; - - if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL)) - pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR; - - if (of_get_property(np, "cd-inverted", NULL)) - pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; - return pdata; } @@ -2442,9 +2312,9 @@ int dw_mci_probe(struct dw_mci *host) } } - if (!host->pdata->select_slot && host->pdata->num_slots > 1) { + if (host->pdata->num_slots > 1) { dev_err(host->dev, - "Platform data must supply select_slot function\n"); + "Platform data must supply num_slots.\n"); return -ENODEV; } @@ -2474,12 +2344,19 @@ int dw_mci_probe(struct dw_mci *host) ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz); if (ret) dev_warn(host->dev, - "Unable to set bus rate to %ul\n", + "Unable to set bus rate to %uHz\n", host->pdata->bus_hz); } host->bus_hz = clk_get_rate(host->ciu_clk); } + if (!host->bus_hz) { + dev_err(host->dev, + "Platform data must supply bus speed\n"); + ret = -ENODEV; + goto err_clk_ciu; + } + if (drv_data && drv_data->init) { ret = drv_data->init(host); if (ret) { @@ -2516,13 +2393,6 @@ int dw_mci_probe(struct dw_mci *host) } } - if (!host->bus_hz) { - dev_err(host->dev, - "Platform data must supply bus speed\n"); - ret = -ENODEV; - goto err_regulator; - } - host->quirks = host->pdata->quirks; spin_lock_init(&host->lock); @@ -2666,8 +2536,6 @@ err_workqueue: err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - -err_regulator: if (host->vmmc) regulator_disable(host->vmmc); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 6834977..738fa24 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -195,7 +195,6 @@ extern int dw_mci_resume(struct dw_mci *host); * @mmc: The mmc_host representing this slot. * @host: The MMC controller this slot is using. * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) - * @wp_gpio: If gpio_is_valid() we'll use this to read write protect. * @ctype: Card type for this slot. * @mrq: mmc_request currently being processed or waiting to be * processed, or NULL when the slot is idle. @@ -214,7 +213,6 @@ struct dw_mci_slot { struct dw_mci *host; int quirks; - int wp_gpio; u32 ctype; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index de2139c..537d6c7 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -515,10 +515,13 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) jz4740_mmc_send_command(host, req->stop); - timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_PRG_DONE); - if (timeout) { - host->state = JZ4740_MMC_STATE_DONE; - break; + if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) { + timeout = jz4740_mmc_poll_irq(host, + JZ_MMC_IRQ_PRG_DONE); + if (timeout) { + host->state = JZ4740_MMC_STATE_DONE; + break; + } } case JZ4740_MMC_STATE_DONE: break; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index a084edd..7ad463e 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -301,7 +301,8 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) clk |= MCI_ST_8BIT_BUS; - if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) + if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || + host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) clk |= MCI_ST_UX500_NEG_EDGE; mmci_write_clkreg(host, clk); @@ -764,7 +765,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) mmci_write_clkreg(host, clk); } - if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) + if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || + host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) datactrl |= MCI_ST_DPSM_DDRMODE; /* diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c new file mode 100644 index 0000000..74924a0 --- /dev/null +++ b/drivers/mmc/host/moxart-mmc.c @@ -0,0 +1,730 @@ +/* + * MOXA ART MMC host driver. + * + * Copyright (C) 2014 Jonas Jensen + * + * Jonas Jensen <jonas.jensen@gmail.com> + * + * Based on code from + * Moxa Technologies Co., Ltd. <www.moxa.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sd.h> +#include <linux/sched.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/clk.h> +#include <linux/bitops.h> +#include <linux/of_dma.h> +#include <linux/spinlock.h> + +#define REG_COMMAND 0 +#define REG_ARGUMENT 4 +#define REG_RESPONSE0 8 +#define REG_RESPONSE1 12 +#define REG_RESPONSE2 16 +#define REG_RESPONSE3 20 +#define REG_RESPONSE_COMMAND 24 +#define REG_DATA_CONTROL 28 +#define REG_DATA_TIMER 32 +#define REG_DATA_LENGTH 36 +#define REG_STATUS 40 +#define REG_CLEAR 44 +#define REG_INTERRUPT_MASK 48 +#define REG_POWER_CONTROL 52 +#define REG_CLOCK_CONTROL 56 +#define REG_BUS_WIDTH 60 +#define REG_DATA_WINDOW 64 +#define REG_FEATURE 68 +#define REG_REVISION 72 + +/* REG_COMMAND */ +#define CMD_SDC_RESET BIT(10) +#define CMD_EN BIT(9) +#define CMD_APP_CMD BIT(8) +#define CMD_LONG_RSP BIT(7) +#define CMD_NEED_RSP BIT(6) +#define CMD_IDX_MASK 0x3f + +/* REG_RESPONSE_COMMAND */ +#define RSP_CMD_APP BIT(6) +#define RSP_CMD_IDX_MASK 0x3f + +/* REG_DATA_CONTROL */ +#define DCR_DATA_FIFO_RESET BIT(8) +#define DCR_DATA_THRES BIT(7) +#define DCR_DATA_EN BIT(6) +#define DCR_DMA_EN BIT(5) +#define DCR_DATA_WRITE BIT(4) +#define DCR_BLK_SIZE 0x0f + +/* REG_DATA_LENGTH */ +#define DATA_LEN_MASK 0xffffff + +/* REG_STATUS */ +#define WRITE_PROT BIT(12) +#define CARD_DETECT BIT(11) +/* 1-10 below can be sent to either registers, interrupt or clear. */ +#define CARD_CHANGE BIT(10) +#define FIFO_ORUN BIT(9) +#define FIFO_URUN BIT(8) +#define DATA_END BIT(7) +#define CMD_SENT BIT(6) +#define DATA_CRC_OK BIT(5) +#define RSP_CRC_OK BIT(4) +#define DATA_TIMEOUT BIT(3) +#define RSP_TIMEOUT BIT(2) +#define DATA_CRC_FAIL BIT(1) +#define RSP_CRC_FAIL BIT(0) + +#define MASK_RSP (RSP_TIMEOUT | RSP_CRC_FAIL | \ + RSP_CRC_OK | CARD_DETECT | CMD_SENT) + +#define MASK_DATA (DATA_CRC_OK | DATA_END | \ + DATA_CRC_FAIL | DATA_TIMEOUT) + +#define MASK_INTR_PIO (FIFO_URUN | FIFO_ORUN | CARD_CHANGE) + +/* REG_POWER_CONTROL */ +#define SD_POWER_ON BIT(4) +#define SD_POWER_MASK 0x0f + +/* REG_CLOCK_CONTROL */ +#define CLK_HISPD BIT(9) +#define CLK_OFF BIT(8) +#define CLK_SD BIT(7) +#define CLK_DIV_MASK 0x7f + +/* REG_BUS_WIDTH */ +#define BUS_WIDTH_8 BIT(2) +#define BUS_WIDTH_4 BIT(1) +#define BUS_WIDTH_1 BIT(0) + +#define MMC_VDD_360 23 +#define MIN_POWER (MMC_VDD_360 - SD_POWER_MASK) +#define MAX_RETRIES 500000 + +struct moxart_host { + spinlock_t lock; + + void __iomem *base; + + phys_addr_t reg_phys; + + struct dma_chan *dma_chan_tx; + struct dma_chan *dma_chan_rx; + struct dma_async_tx_descriptor *tx_desc; + struct mmc_host *mmc; + struct mmc_request *mrq; + struct scatterlist *cur_sg; + struct completion dma_complete; + struct completion pio_complete; + + u32 num_sg; + u32 data_remain; + u32 data_len; + u32 fifo_width; + u32 timeout; + u32 rate; + + long sysclk; + + bool have_dma; + bool is_removed; +}; + +static inline void moxart_init_sg(struct moxart_host *host, + struct mmc_data *data) +{ + host->cur_sg = data->sg; + host->num_sg = data->sg_len; + host->data_remain = host->cur_sg->length; + + if (host->data_remain > host->data_len) + host->data_remain = host->data_len; +} + +static inline int moxart_next_sg(struct moxart_host *host) +{ + int remain; + struct mmc_data *data = host->mrq->cmd->data; + + host->cur_sg++; + host->num_sg--; + + if (host->num_sg > 0) { + host->data_remain = host->cur_sg->length; + remain = host->data_len - data->bytes_xfered; + if (remain > 0 && remain < host->data_remain) + host->data_remain = remain; + } + + return host->num_sg; +} + +static int moxart_wait_for_status(struct moxart_host *host, + u32 mask, u32 *status) +{ + int ret = -ETIMEDOUT; + u32 i; + + for (i = 0; i < MAX_RETRIES; i++) { + *status = readl(host->base + REG_STATUS); + if (!(*status & mask)) { + udelay(5); + continue; + } + writel(*status & mask, host->base + REG_CLEAR); + ret = 0; + break; + } + + if (ret) + dev_err(mmc_dev(host->mmc), "timed out waiting for status\n"); + + return ret; +} + + +static void moxart_send_command(struct moxart_host *host, + struct mmc_command *cmd) +{ + u32 status, cmdctrl; + + writel(RSP_TIMEOUT | RSP_CRC_OK | + RSP_CRC_FAIL | CMD_SENT, host->base + REG_CLEAR); + writel(cmd->arg, host->base + REG_ARGUMENT); + + cmdctrl = cmd->opcode & CMD_IDX_MASK; + if (cmdctrl == SD_APP_SET_BUS_WIDTH || cmdctrl == SD_APP_OP_COND || + cmdctrl == SD_APP_SEND_SCR || cmdctrl == SD_APP_SD_STATUS || + cmdctrl == SD_APP_SEND_NUM_WR_BLKS) + cmdctrl |= CMD_APP_CMD; + + if (cmd->flags & MMC_RSP_PRESENT) + cmdctrl |= CMD_NEED_RSP; + + if (cmd->flags & MMC_RSP_136) + cmdctrl |= CMD_LONG_RSP; + + writel(cmdctrl | CMD_EN, host->base + REG_COMMAND); + + if (moxart_wait_for_status(host, MASK_RSP, &status) == -ETIMEDOUT) + cmd->error = -ETIMEDOUT; + + if (status & RSP_TIMEOUT) { + cmd->error = -ETIMEDOUT; + return; + } + if (status & RSP_CRC_FAIL) { + cmd->error = -EIO; + return; + } + if (status & RSP_CRC_OK) { + if (cmd->flags & MMC_RSP_136) { + cmd->resp[3] = readl(host->base + REG_RESPONSE0); + cmd->resp[2] = readl(host->base + REG_RESPONSE1); + cmd->resp[1] = readl(host->base + REG_RESPONSE2); + cmd->resp[0] = readl(host->base + REG_RESPONSE3); + } else { + cmd->resp[0] = readl(host->base + REG_RESPONSE0); + } + } +} + +static void moxart_dma_complete(void *param) +{ + struct moxart_host *host = param; + + complete(&host->dma_complete); +} + +static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host) +{ + u32 len, dir_data, dir_slave; + unsigned long dma_time; + struct dma_async_tx_descriptor *desc = NULL; + struct dma_chan *dma_chan; + + if (host->data_len == data->bytes_xfered) + return; + + if (data->flags & MMC_DATA_WRITE) { + dma_chan = host->dma_chan_tx; + dir_data = DMA_TO_DEVICE; + dir_slave = DMA_MEM_TO_DEV; + } else { + dma_chan = host->dma_chan_rx; + dir_data = DMA_FROM_DEVICE; + dir_slave = DMA_DEV_TO_MEM; + } + + len = dma_map_sg(dma_chan->device->dev, data->sg, + data->sg_len, dir_data); + + if (len > 0) { + desc = dmaengine_prep_slave_sg(dma_chan, data->sg, + len, dir_slave, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + } else { + dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n"); + } + + if (desc) { + host->tx_desc = desc; + desc->callback = moxart_dma_complete; + desc->callback_param = host; + dmaengine_submit(desc); + dma_async_issue_pending(dma_chan); + } + + data->bytes_xfered += host->data_remain; + + dma_time = wait_for_completion_interruptible_timeout( + &host->dma_complete, host->timeout); + + dma_unmap_sg(dma_chan->device->dev, + data->sg, data->sg_len, + dir_data); +} + + +static void moxart_transfer_pio(struct moxart_host *host) +{ + struct mmc_data *data = host->mrq->cmd->data; + u32 *sgp, len = 0, remain, status; + + if (host->data_len == data->bytes_xfered) + return; + + sgp = sg_virt(host->cur_sg); + remain = host->data_remain; + + if (data->flags & MMC_DATA_WRITE) { + while (remain > 0) { + if (moxart_wait_for_status(host, FIFO_URUN, &status) + == -ETIMEDOUT) { + data->error = -ETIMEDOUT; + complete(&host->pio_complete); + return; + } + for (len = 0; len < remain && len < host->fifo_width;) { + iowrite32(*sgp, host->base + REG_DATA_WINDOW); + sgp++; + len += 4; + } + remain -= len; + } + + } else { + while (remain > 0) { + if (moxart_wait_for_status(host, FIFO_ORUN, &status) + == -ETIMEDOUT) { + data->error = -ETIMEDOUT; + complete(&host->pio_complete); + return; + } + for (len = 0; len < remain && len < host->fifo_width;) { + /* SCR data must be read in big endian. */ + if (data->mrq->cmd->opcode == SD_APP_SEND_SCR) + *sgp = ioread32be(host->base + + REG_DATA_WINDOW); + else + *sgp = ioread32(host->base + + REG_DATA_WINDOW); + sgp++; + len += 4; + } + remain -= len; + } + } + + data->bytes_xfered += host->data_remain - remain; + host->data_remain = remain; + + if (host->data_len != data->bytes_xfered) + moxart_next_sg(host); + else + complete(&host->pio_complete); +} + +static void moxart_prepare_data(struct moxart_host *host) +{ + struct mmc_data *data = host->mrq->cmd->data; + u32 datactrl; + int blksz_bits; + + if (!data) + return; + + host->data_len = data->blocks * data->blksz; + blksz_bits = ffs(data->blksz) - 1; + BUG_ON(1 << blksz_bits != data->blksz); + + moxart_init_sg(host, data); + + datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE); + + if (data->flags & MMC_DATA_WRITE) + datactrl |= DCR_DATA_WRITE; + + if ((host->data_len > host->fifo_width) && host->have_dma) + datactrl |= DCR_DMA_EN; + + writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL); + writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR); + writel(host->rate, host->base + REG_DATA_TIMER); + writel(host->data_len, host->base + REG_DATA_LENGTH); + writel(datactrl, host->base + REG_DATA_CONTROL); +} + +static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct moxart_host *host = mmc_priv(mmc); + unsigned long pio_time, flags; + u32 status; + + spin_lock_irqsave(&host->lock, flags); + + init_completion(&host->dma_complete); + init_completion(&host->pio_complete); + + host->mrq = mrq; + + if (readl(host->base + REG_STATUS) & CARD_DETECT) { + mrq->cmd->error = -ETIMEDOUT; + goto request_done; + } + + moxart_prepare_data(host); + moxart_send_command(host, host->mrq->cmd); + + if (mrq->cmd->data) { + if ((host->data_len > host->fifo_width) && host->have_dma) { + + writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK); + + spin_unlock_irqrestore(&host->lock, flags); + + moxart_transfer_dma(mrq->cmd->data, host); + + spin_lock_irqsave(&host->lock, flags); + } else { + + writel(MASK_INTR_PIO, host->base + REG_INTERRUPT_MASK); + + spin_unlock_irqrestore(&host->lock, flags); + + /* PIO transfers start from interrupt. */ + pio_time = wait_for_completion_interruptible_timeout( + &host->pio_complete, host->timeout); + + spin_lock_irqsave(&host->lock, flags); + } + + if (host->is_removed) { + dev_err(mmc_dev(host->mmc), "card removed\n"); + mrq->cmd->error = -ETIMEDOUT; + goto request_done; + } + + if (moxart_wait_for_status(host, MASK_DATA, &status) + == -ETIMEDOUT) { + mrq->cmd->data->error = -ETIMEDOUT; + goto request_done; + } + + if (status & DATA_CRC_FAIL) + mrq->cmd->data->error = -ETIMEDOUT; + + if (mrq->cmd->data->stop) + moxart_send_command(host, mrq->cmd->data->stop); + } + +request_done: + spin_unlock_irqrestore(&host->lock, flags); + mmc_request_done(host->mmc, mrq); +} + +static irqreturn_t moxart_irq(int irq, void *devid) +{ + struct moxart_host *host = (struct moxart_host *)devid; + u32 status; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + status = readl(host->base + REG_STATUS); + if (status & CARD_CHANGE) { + host->is_removed = status & CARD_DETECT; + if (host->is_removed && host->have_dma) { + dmaengine_terminate_all(host->dma_chan_tx); + dmaengine_terminate_all(host->dma_chan_rx); + } + host->mrq = NULL; + writel(MASK_INTR_PIO, host->base + REG_CLEAR); + writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK); + mmc_detect_change(host->mmc, 0); + } + if (status & (FIFO_ORUN | FIFO_URUN) && host->mrq) + moxart_transfer_pio(host); + + spin_unlock_irqrestore(&host->lock, flags); + + return IRQ_HANDLED; +} + +static void moxart_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct moxart_host *host = mmc_priv(mmc); + unsigned long flags; + u8 power, div; + u32 ctrl; + + spin_lock_irqsave(&host->lock, flags); + + if (ios->clock) { + for (div = 0; div < CLK_DIV_MASK; ++div) { + if (ios->clock >= host->sysclk / (2 * (div + 1))) + break; + } + ctrl = CLK_SD | div; + host->rate = host->sysclk / (2 * (div + 1)); + if (host->rate > host->sysclk) + ctrl |= CLK_HISPD; + writel(ctrl, host->base + REG_CLOCK_CONTROL); + } + + if (ios->power_mode == MMC_POWER_OFF) { + writel(readl(host->base + REG_POWER_CONTROL) & ~SD_POWER_ON, + host->base + REG_POWER_CONTROL); + } else { + if (ios->vdd < MIN_POWER) + power = 0; + else + power = ios->vdd - MIN_POWER; + + writel(SD_POWER_ON | (u32) power, + host->base + REG_POWER_CONTROL); + } + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_4: + writel(BUS_WIDTH_4, host->base + REG_BUS_WIDTH); + break; + case MMC_BUS_WIDTH_8: + writel(BUS_WIDTH_8, host->base + REG_BUS_WIDTH); + break; + default: + writel(BUS_WIDTH_1, host->base + REG_BUS_WIDTH); + break; + } + + spin_unlock_irqrestore(&host->lock, flags); +} + + +static int moxart_get_ro(struct mmc_host *mmc) +{ + struct moxart_host *host = mmc_priv(mmc); + + return !!(readl(host->base + REG_STATUS) & WRITE_PROT); +} + +static struct mmc_host_ops moxart_ops = { + .request = moxart_request, + .set_ios = moxart_set_ios, + .get_ro = moxart_get_ro, +}; + +static int moxart_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct resource res_mmc; + struct mmc_host *mmc; + struct moxart_host *host = NULL; + struct dma_slave_config cfg; + struct clk *clk; + void __iomem *reg_mmc; + dma_cap_mask_t mask; + int irq, ret; + u32 i; + + mmc = mmc_alloc_host(sizeof(struct moxart_host), dev); + if (!mmc) { + dev_err(dev, "mmc_alloc_host failed\n"); + ret = -ENOMEM; + goto out; + } + + ret = of_address_to_resource(node, 0, &res_mmc); + if (ret) { + dev_err(dev, "of_address_to_resource failed\n"); + goto out; + } + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) { + dev_err(dev, "irq_of_parse_and_map failed\n"); + ret = -EINVAL; + goto out; + } + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) { + dev_err(dev, "of_clk_get failed\n"); + ret = PTR_ERR(clk); + goto out; + } + + reg_mmc = devm_ioremap_resource(dev, &res_mmc); + if (IS_ERR(reg_mmc)) { + ret = PTR_ERR(reg_mmc); + goto out; + } + + mmc_of_parse(mmc); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + host = mmc_priv(mmc); + host->mmc = mmc; + host->base = reg_mmc; + host->reg_phys = res_mmc.start; + host->timeout = msecs_to_jiffies(1000); + host->sysclk = clk_get_rate(clk); + host->fifo_width = readl(host->base + REG_FEATURE) << 2; + host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); + host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); + + spin_lock_init(&host->lock); + + mmc->ops = &moxart_ops; + mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2); + mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2); + mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */ + + if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { + dev_dbg(dev, "PIO mode transfer enabled\n"); + host->have_dma = false; + } else { + dev_dbg(dev, "DMA channels found (%p,%p)\n", + host->dma_chan_tx, host->dma_chan_rx); + host->have_dma = true; + + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + cfg.direction = DMA_MEM_TO_DEV; + cfg.src_addr = 0; + cfg.dst_addr = host->reg_phys + REG_DATA_WINDOW; + dmaengine_slave_config(host->dma_chan_tx, &cfg); + + cfg.direction = DMA_DEV_TO_MEM; + cfg.src_addr = host->reg_phys + REG_DATA_WINDOW; + cfg.dst_addr = 0; + dmaengine_slave_config(host->dma_chan_rx, &cfg); + } + + switch ((readl(host->base + REG_BUS_WIDTH) >> 3) & 3) { + case 1: + mmc->caps |= MMC_CAP_4_BIT_DATA; + break; + case 2: + mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; + break; + default: + break; + } + + writel(0, host->base + REG_INTERRUPT_MASK); + + writel(CMD_SDC_RESET, host->base + REG_COMMAND); + for (i = 0; i < MAX_RETRIES; i++) { + if (!(readl(host->base + REG_COMMAND) & CMD_SDC_RESET)) + break; + udelay(5); + } + + ret = devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host); + if (ret) + goto out; + + dev_set_drvdata(dev, mmc); + mmc_add_host(mmc); + + dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width); + + return 0; + +out: + if (mmc) + mmc_free_host(mmc); + return ret; +} + +static int moxart_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = dev_get_drvdata(&pdev->dev); + struct moxart_host *host = mmc_priv(mmc); + + dev_set_drvdata(&pdev->dev, NULL); + + if (mmc) { + if (!IS_ERR(host->dma_chan_tx)) + dma_release_channel(host->dma_chan_tx); + if (!IS_ERR(host->dma_chan_rx)) + dma_release_channel(host->dma_chan_rx); + mmc_remove_host(mmc); + mmc_free_host(mmc); + + writel(0, host->base + REG_INTERRUPT_MASK); + writel(0, host->base + REG_POWER_CONTROL); + writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF, + host->base + REG_CLOCK_CONTROL); + } + + kfree(host); + + return 0; +} + +static const struct of_device_id moxart_mmc_match[] = { + { .compatible = "moxa,moxart-mmc" }, + { .compatible = "faraday,ftsdc010" }, + { } +}; + +static struct platform_driver moxart_mmc_driver = { + .probe = moxart_probe, + .remove = moxart_remove, + .driver = { + .name = "mmc-moxart", + .owner = THIS_MODULE, + .of_match_table = moxart_mmc_match, + }, +}; +module_platform_driver(moxart_mmc_driver); + +MODULE_ALIAS("platform:mmc-moxart"); +MODULE_DESCRIPTION("MOXA ART MMC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>"); diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 45aa220..9377284 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -354,6 +354,20 @@ static irqreturn_t mvsd_irq(int irq, void *dev) intr_status, mvsd_read(MVSD_NOR_INTR_EN), mvsd_read(MVSD_HW_STATE)); + /* + * It looks like, SDIO IP can issue one late, spurious irq + * although all irqs should be disabled. To work around this, + * bail out early, if we didn't expect any irqs to occur. + */ + if (!mvsd_read(MVSD_NOR_INTR_EN) && !mvsd_read(MVSD_ERR_INTR_EN)) { + dev_dbg(host->dev, "spurious irq detected intr 0x%04x intr_en 0x%04x erri 0x%04x erri_en 0x%04x\n", + mvsd_read(MVSD_NOR_INTR_STATUS), + mvsd_read(MVSD_NOR_INTR_EN), + mvsd_read(MVSD_ERR_INTR_STATUS), + mvsd_read(MVSD_ERR_INTR_EN)); + return IRQ_HANDLED; + } + spin_lock(&host->lock); /* PIO handling, if needed. Messy business... */ @@ -801,10 +815,10 @@ static int mvsd_probe(struct platform_device *pdev) goto out; if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) - dev_notice(&pdev->dev, "using GPIO for card detection\n"); + dev_dbg(&pdev->dev, "using GPIO for card detection\n"); else - dev_notice(&pdev->dev, - "lacking card detect (fall back to polling)\n"); + dev_dbg(&pdev->dev, "lacking card detect (fall back to polling)\n"); + return 0; out: diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index f7199c8..ed1cb93 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -124,9 +124,8 @@ enum mxcmci_type { struct mxcmci_host { struct mmc_host *mmc; - struct resource *res; void __iomem *base; - int irq; + dma_addr_t phys_base; int detect_irq; struct dma_chan *dma; struct dma_async_tx_descriptor *desc; @@ -154,8 +153,6 @@ struct mxcmci_host { struct work_struct datawork; spinlock_t lock; - struct regulator *vcc; - int burstlen; int dmareq; struct dma_slave_config dma_slave_config; @@ -241,37 +238,15 @@ static inline void mxcmci_writew(struct mxcmci_host *host, u16 val, int reg) static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); -static inline void mxcmci_init_ocr(struct mxcmci_host *host) -{ - host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc"); - - if (IS_ERR(host->vcc)) { - host->vcc = NULL; - } else { - host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc); - if (host->pdata && host->pdata->ocr_avail) - dev_warn(mmc_dev(host->mmc), - "pdata->ocr_avail will not be used\n"); - } - - if (host->vcc == NULL) { - /* fall-back to platform data */ - if (host->pdata && host->pdata->ocr_avail) - host->mmc->ocr_avail = host->pdata->ocr_avail; - else - host->mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - } -} - -static inline void mxcmci_set_power(struct mxcmci_host *host, - unsigned char power_mode, - unsigned int vdd) +static void mxcmci_set_power(struct mxcmci_host *host, unsigned int vdd) { - if (host->vcc) { - if (power_mode == MMC_POWER_UP) - mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); - else if (power_mode == MMC_POWER_OFF) - mmc_regulator_set_ocr(host->mmc, host->vcc, 0); + if (!IS_ERR(host->mmc->supply.vmmc)) { + if (host->power_mode == MMC_POWER_UP) + mmc_regulator_set_ocr(host->mmc, + host->mmc->supply.vmmc, vdd); + else if (host->power_mode == MMC_POWER_OFF) + mmc_regulator_set_ocr(host->mmc, + host->mmc->supply.vmmc, 0); } if (host->pdata && host->pdata->setpower) @@ -299,7 +274,6 @@ static void mxcmci_softreset(struct mxcmci_host *host) mxcmci_writew(host, 0xff, MMC_REG_RES_TO); } -static int mxcmci_setup_dma(struct mmc_host *mmc); #if IS_ENABLED(CONFIG_PPC_MPC512x) static inline void buffer_swap32(u32 *buf, int len) @@ -868,8 +842,8 @@ static int mxcmci_setup_dma(struct mmc_host *mmc) struct mxcmci_host *host = mmc_priv(mmc); struct dma_slave_config *config = &host->dma_slave_config; - config->dst_addr = host->res->start + MMC_REG_BUFFER_ACCESS; - config->src_addr = host->res->start + MMC_REG_BUFFER_ACCESS; + config->dst_addr = host->phys_base + MMC_REG_BUFFER_ACCESS; + config->src_addr = host->phys_base + MMC_REG_BUFFER_ACCESS; config->dst_addr_width = 4; config->src_addr_width = 4; config->dst_maxburst = host->burstlen; @@ -911,8 +885,8 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4; if (host->power_mode != ios->power_mode) { - mxcmci_set_power(host, ios->power_mode, ios->vdd); host->power_mode = ios->power_mode; + mxcmci_set_power(host, ios->vdd); if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMD_DAT_CONT_INIT; @@ -1040,8 +1014,8 @@ static const struct mmc_host_ops mxcmci_ops = { static int mxcmci_probe(struct platform_device *pdev) { struct mmc_host *mmc; - struct mxcmci_host *host = NULL; - struct resource *iores, *r; + struct mxcmci_host *host; + struct resource *res; int ret = 0, irq; bool dat3_card_detect = false; dma_cap_mask_t mask; @@ -1052,21 +1026,25 @@ static int mxcmci_probe(struct platform_device *pdev) of_id = of_match_device(mxcmci_of_match, &pdev->dev); - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!iores || irq < 0) + if (irq < 0) return -EINVAL; - r = request_mem_region(iores->start, resource_size(iores), pdev->name); - if (!r) - return -EBUSY; + mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); - mmc = mmc_alloc_host(sizeof(struct mxcmci_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out_release_mem; + host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto out_free; } + host->phys_base = res->start; + ret = mmc_of_parse(mmc); if (ret) goto out_free; @@ -1084,13 +1062,6 @@ static int mxcmci_probe(struct platform_device *pdev) mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - host = mmc_priv(mmc); - host->base = ioremap(r->start, resource_size(r)); - if (!host->base) { - ret = -ENOMEM; - goto out_free; - } - if (of_id) { const struct platform_device_id *id_entry = of_id->data; host->devtype = id_entry->driver_data; @@ -1112,7 +1083,14 @@ static int mxcmci_probe(struct platform_device *pdev) && !of_property_read_bool(pdev->dev.of_node, "cd-gpios")) dat3_card_detect = true; - mxcmci_init_ocr(host); + ret = mmc_regulator_get_supply(mmc); + if (ret) { + if (pdata && ret != -EPROBE_DEFER) + mmc->ocr_avail = pdata->ocr_avail ? : + MMC_VDD_32_33 | MMC_VDD_33_34; + else + goto out_free; + } if (dat3_card_detect) host->default_irq_mask = @@ -1120,19 +1098,16 @@ static int mxcmci_probe(struct platform_device *pdev) else host->default_irq_mask = 0; - host->res = r; - host->irq = irq; - host->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(host->clk_ipg)) { ret = PTR_ERR(host->clk_ipg); - goto out_iounmap; + goto out_free; } host->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(host->clk_per)) { ret = PTR_ERR(host->clk_per); - goto out_iounmap; + goto out_free; } clk_prepare_enable(host->clk_per); @@ -1159,9 +1134,9 @@ static int mxcmci_probe(struct platform_device *pdev) if (!host->pdata) { host->dma = dma_request_slave_channel(&pdev->dev, "rx-tx"); } else { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r) { - host->dmareq = r->start; + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res) { + host->dmareq = res->start; host->dma_data.peripheral_type = IMX_DMATYPE_SDHC; host->dma_data.priority = DMA_PRIO_LOW; host->dma_data.dma_request = host->dmareq; @@ -1178,7 +1153,8 @@ static int mxcmci_probe(struct platform_device *pdev) INIT_WORK(&host->datawork, mxcmci_datawork); - ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host); + ret = devm_request_irq(&pdev->dev, irq, mxcmci_irq, 0, + dev_name(&pdev->dev), host); if (ret) goto out_free_dma; @@ -1188,7 +1164,7 @@ static int mxcmci_probe(struct platform_device *pdev) ret = host->pdata->init(&pdev->dev, mxcmci_detect_irq, host->mmc); if (ret) - goto out_free_irq; + goto out_free_dma; } init_timer(&host->watchdog); @@ -1199,20 +1175,17 @@ static int mxcmci_probe(struct platform_device *pdev) return 0; -out_free_irq: - free_irq(host->irq, host); out_free_dma: if (host->dma) dma_release_channel(host->dma); + out_clk_put: clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); -out_iounmap: - iounmap(host->base); + out_free: mmc_free_host(mmc); -out_release_mem: - release_mem_region(iores->start, resource_size(iores)); + return ret; } @@ -1223,30 +1196,21 @@ static int mxcmci_remove(struct platform_device *pdev) mmc_remove_host(mmc); - if (host->vcc) - regulator_put(host->vcc); - if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); - free_irq(host->irq, host); - iounmap(host->base); - if (host->dma) dma_release_channel(host->dma); clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); - release_mem_region(host->res->start, resource_size(host->res)); - mmc_free_host(mmc); return 0; } -#ifdef CONFIG_PM -static int mxcmci_suspend(struct device *dev) +static int __maybe_unused mxcmci_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); @@ -1256,7 +1220,7 @@ static int mxcmci_suspend(struct device *dev) return 0; } -static int mxcmci_resume(struct device *dev) +static int __maybe_unused mxcmci_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); @@ -1266,11 +1230,7 @@ static int mxcmci_resume(struct device *dev) return 0; } -static const struct dev_pm_ops mxcmci_pm_ops = { - .suspend = mxcmci_suspend, - .resume = mxcmci_resume, -}; -#endif +static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume); static struct platform_driver mxcmci_driver = { .probe = mxcmci_probe, @@ -1279,9 +1239,7 @@ static struct platform_driver mxcmci_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &mxcmci_pm_ops, -#endif .of_match_table = mxcmci_of_match, } }; diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 073e871..babfea0 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -70,6 +70,7 @@ struct mxs_mmc_host { unsigned char bus_width; spinlock_t lock; int sdio_irq_en; + bool broken_cd; }; static int mxs_mmc_get_cd(struct mmc_host *mmc) @@ -78,6 +79,9 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc) struct mxs_ssp *ssp = &host->ssp; int present, ret; + if (host->broken_cd) + return -ENOSYS; + ret = mmc_gpio_get_cd(mmc); if (ret >= 0) return ret; @@ -568,6 +572,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(mxs_mmc_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; struct mxs_mmc_host *host; struct mmc_host *mmc; struct resource *iores; @@ -634,6 +639,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL; + host->broken_cd = of_property_read_bool(np, "broken-cd"); + mmc->f_min = 400000; mmc->f_max = 288000000; diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 5c2e58b..81974ec 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -177,7 +177,7 @@ static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot) unsigned long tick_ns; if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) { - tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq; + tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, slot->fclk_freq); ndelay(8 * tick_ns); } } @@ -435,7 +435,7 @@ static void mmc_omap_send_stop_work(struct work_struct *work) struct mmc_data *data = host->stop_data; unsigned long tick_ns; - tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq; + tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, slot->fclk_freq); ndelay(8*tick_ns); mmc_omap_start_command(host, data->stop); @@ -477,7 +477,7 @@ mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops) u16 stat = 0; /* Sending abort takes 80 clocks. Have some extra and round up */ - timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq; + timeout = DIV_ROUND_UP(120 * USEC_PER_SEC, slot->fclk_freq); restarts = 0; while (restarts < maxloops) { OMAP_MMC_WRITE(host, STAT, 0xFFFF); @@ -677,8 +677,8 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write) if (n > host->buffer_bytes_left) n = host->buffer_bytes_left; - nwords = n / 2; - nwords += n & 1; /* handle odd number of bytes to transfer */ + /* Round up to handle odd number of bytes to transfer */ + nwords = DIV_ROUND_UP(n, 2); host->buffer_bytes_left -= n; host->total_bytes_left -= n; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index e91ee21..6b7b755 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -31,7 +31,7 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_device.h> -#include <linux/omap-dma.h> +#include <linux/omap-dmaengine.h> #include <linux/mmc/host.h> #include <linux/mmc/core.h> #include <linux/mmc/mmc.h> @@ -582,7 +582,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) * - MMC/SD clock coming out of controller > 25MHz */ if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && - (ios->timing != MMC_TIMING_UHS_DDR50) && + (ios->timing != MMC_TIMING_MMC_DDR52) && ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) { regval = OMAP_HSMMC_READ(host->base, HCTL); if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) @@ -602,7 +602,7 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host) u32 con; con = OMAP_HSMMC_READ(host->base, CON); - if (ios->timing == MMC_TIMING_UHS_DDR50) + if (ios->timing == MMC_TIMING_MMC_DDR52) con |= DDR; /* configure in DDR mode */ else con &= ~DDR; @@ -920,16 +920,17 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data) static void omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd) { - host->cmd = NULL; - if (host->mrq->sbc && (host->cmd == host->mrq->sbc) && !host->mrq->sbc->error && !(host->flags & AUTO_CMD23)) { + host->cmd = NULL; omap_hsmmc_start_dma_transfer(host); omap_hsmmc_start_command(host, host->mrq->cmd, host->mrq->data); return; } + host->cmd = NULL; + if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { /* response type 2 */ @@ -1851,6 +1852,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) unsigned tx_req, rx_req; struct pinctrl *pinctrl; const struct omap_mmc_of_data *data; + void __iomem *base; match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev); if (match) { @@ -1881,9 +1883,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (res == NULL || irq < 0) return -ENXIO; - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) - return -EBUSY; + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); ret = omap_hsmmc_gpio_init(pdata); if (ret) @@ -1904,7 +1906,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->irq = irq; host->slot_id = 0; host->mapbase = res->start + pdata->reg_offset; - host->base = ioremap(host->mapbase, SZ_4K); + host->base = base + pdata->reg_offset; host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; host->pbias_enabled = 0; @@ -1922,7 +1924,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) spin_lock_init(&host->irq_lock); - host->fclk = clk_get(&pdev->dev, "fck"); + host->fclk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); host->fclk = NULL; @@ -1941,7 +1943,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_context_save(host); - host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); + host->dbclk = devm_clk_get(&pdev->dev, "mmchsdb_fck"); /* * MMC can still work without debounce clock. */ @@ -1949,7 +1951,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->dbclk = NULL; } else if (clk_prepare_enable(host->dbclk) != 0) { dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n"); - clk_put(host->dbclk); host->dbclk = NULL; } @@ -2018,7 +2019,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) } /* Request IRQ for MMC operations */ - ret = request_irq(host->irq, omap_hsmmc_irq, 0, + ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0, mmc_hostname(mmc), host); if (ret) { dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); @@ -2029,7 +2030,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (pdata->init(&pdev->dev) != 0) { dev_err(mmc_dev(host->mmc), "Unable to configure MMC IRQs\n"); - goto err_irq_cd_init; + goto err_irq; } } @@ -2044,9 +2045,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) /* Request IRQ for card detect */ if ((mmc_slot(host).card_detect_irq)) { - ret = request_threaded_irq(mmc_slot(host).card_detect_irq, - NULL, - omap_hsmmc_detect, + ret = devm_request_threaded_irq(&pdev->dev, + mmc_slot(host).card_detect_irq, + NULL, omap_hsmmc_detect, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, mmc_hostname(mmc), host); if (ret) { @@ -2089,15 +2090,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev) err_slot_name: mmc_remove_host(mmc); - free_irq(mmc_slot(host).card_detect_irq, host); err_irq_cd: if (host->use_reg) omap_hsmmc_reg_put(host); err_reg: if (host->pdata->cleanup) host->pdata->cleanup(&pdev->dev); -err_irq_cd_init: - free_irq(host->irq, host); err_irq: if (host->tx_chan) dma_release_channel(host->tx_chan); @@ -2105,27 +2103,19 @@ err_irq: dma_release_channel(host->rx_chan); pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); - clk_put(host->fclk); - if (host->dbclk) { + if (host->dbclk) clk_disable_unprepare(host->dbclk); - clk_put(host->dbclk); - } err1: - iounmap(host->base); mmc_free_host(mmc); err_alloc: omap_hsmmc_gpio_free(pdata); err: - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); return ret; } static int omap_hsmmc_remove(struct platform_device *pdev) { struct omap_hsmmc_host *host = platform_get_drvdata(pdev); - struct resource *res; pm_runtime_get_sync(host->dev); mmc_remove_host(host->mmc); @@ -2133,9 +2123,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) omap_hsmmc_reg_put(host); if (host->pdata->cleanup) host->pdata->cleanup(&pdev->dev); - free_irq(host->irq, host); - if (mmc_slot(host).card_detect_irq) - free_irq(mmc_slot(host).card_detect_irq, host); if (host->tx_chan) dma_release_channel(host->tx_chan); @@ -2144,20 +2131,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); - clk_put(host->fclk); - if (host->dbclk) { + if (host->dbclk) clk_disable_unprepare(host->dbclk); - clk_put(host->dbclk); - } omap_hsmmc_gpio_free(host->pdata); - iounmap(host->base); mmc_free_host(host->mmc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); - return 0; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 0b9ded1..0d51964 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -236,6 +236,9 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, case MMC_RSP_R1: rsp_type = SD_RSP_TYPE_R1; break; + case MMC_RSP_R1 & ~MMC_RSP_CRC: + rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; + break; case MMC_RSP_R1B: rsp_type = SD_RSP_TYPE_R1b; break; @@ -816,6 +819,7 @@ static int sd_set_timing(struct realtek_pci_sdmmc *host, unsigned char timing) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); break; + case MMC_TIMING_MMC_DDR52: case MMC_TIMING_UHS_DDR50: rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, 0x0C | SD_ASYNC_FIFO_NOT_RST, @@ -896,6 +900,7 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->vpclk = true; host->double_clk = false; break; + case MMC_TIMING_MMC_DDR52: case MMC_TIMING_UHS_DDR50: case MMC_TIMING_UHS_SDR25: host->ssc_depth = RTSX_SSC_DEPTH_1M; diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index e11fafa..5d3766e 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -34,7 +34,8 @@ #include <linux/mfd/rtsx_usb.h> #include <asm/unaligned.h> -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#if defined(CONFIG_LEDS_CLASS) || (defined(CONFIG_LEDS_CLASS_MODULE) && \ + defined(CONFIG_MMC_REALTEK_USB_MODULE)) #include <linux/leds.h> #include <linux/workqueue.h> #define RTSX_USB_USE_LEDS_CLASS @@ -59,7 +60,7 @@ struct rtsx_usb_sdmmc { unsigned char power_mode; -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#ifdef RTSX_USB_USE_LEDS_CLASS struct led_classdev led; char led_name[32]; struct work_struct led_work; diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index ebb3f39..8ce3c28 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -102,11 +102,19 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) } static const struct sdhci_ops sdhci_acpi_ops_dflt = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_acpi_enable_dma, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_ops sdhci_acpi_ops_int = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_acpi_enable_dma, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_acpi_int_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 6f166e6..dd780c3 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -206,9 +206,13 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, } static struct sdhci_ops sdhci_bcm_kona_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_bcm_kona_get_max_clk, .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, .card_event = sdhci_bcm_kona_card_event, }; diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index f6d8d67..46af9a4 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -131,8 +131,12 @@ static const struct sdhci_ops bcm2835_sdhci_ops = { .read_l = bcm2835_sdhci_readl, .read_w = bcm2835_sdhci_readw, .read_b = bcm2835_sdhci_readb, + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_min_clock = bcm2835_sdhci_get_min_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = { diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index f2cc266..14b7407 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -30,13 +30,12 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk; unsigned long timeout; - if (clock == host->clock) - return; + host->mmc->actual_clock = 0; sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) - goto out; + return; while (host->max_clk / div > clock) { /* @@ -75,13 +74,14 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); -out: - host->clock = clock; } static const struct sdhci_ops sdhci_cns3xxx_ops = { .get_max_clock = sdhci_cns3xxx_get_max_clk, .set_clock = sdhci_cns3xxx_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { @@ -90,8 +90,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_INVERTED_WRITE_PROTECT | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_NONSTANDARD_CLOCK, + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; static int sdhci_cns3xxx_probe(struct platform_device *pdev) diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 736d7a2..e6278ec 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -21,28 +21,17 @@ #include <linux/clk.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/io.h> #include <linux/mmc/host.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include "sdhci-pltfm.h" struct sdhci_dove_priv { struct clk *clk; - int gpio_cd; }; -static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data) -{ - struct sdhci_host *host = data; - - tasklet_schedule(&host->card_tasklet); - return IRQ_HANDLED; -} - static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) { u16 ret; @@ -60,8 +49,6 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) { - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_dove_priv *priv = pltfm_host->priv; u32 ret; ret = readl(host->ioaddr + reg); @@ -71,14 +58,6 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) /* Mask the support for 3.0V */ ret &= ~SDHCI_CAN_VDD_300; break; - case SDHCI_PRESENT_STATE: - if (gpio_is_valid(priv->gpio_cd)) { - if (gpio_get_value(priv->gpio_cd) == 0) - ret |= SDHCI_CARD_PRESENT; - else - ret &= ~SDHCI_CARD_PRESENT; - } - break; } return ret; } @@ -86,6 +65,10 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) static const struct sdhci_ops sdhci_dove_ops = { .read_w = sdhci_dove_readw, .read_l = sdhci_dove_readl, + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_dove_pdata = { @@ -113,28 +96,9 @@ static int sdhci_dove_probe(struct platform_device *pdev) priv->clk = devm_clk_get(&pdev->dev, NULL); - if (pdev->dev.of_node) { - priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, - "cd-gpios", 0); - } else { - priv->gpio_cd = -EINVAL; - } - - if (gpio_is_valid(priv->gpio_cd)) { - ret = gpio_request(priv->gpio_cd, "sdhci-cd"); - if (ret) { - dev_err(&pdev->dev, "card detect gpio request failed: %d\n", - ret); - return ret; - } - gpio_direction_input(priv->gpio_cd); - } - host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0); - if (IS_ERR(host)) { - ret = PTR_ERR(host); - goto err_sdhci_pltfm_init; - } + if (IS_ERR(host)) + return PTR_ERR(host); pltfm_host = sdhci_priv(host); pltfm_host->priv = priv; @@ -142,39 +106,20 @@ static int sdhci_dove_probe(struct platform_device *pdev) if (!IS_ERR(priv->clk)) clk_prepare_enable(priv->clk); - sdhci_get_of_property(pdev); + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_sdhci_add; ret = sdhci_add_host(host); if (ret) goto err_sdhci_add; - /* - * We must request the IRQ after sdhci_add_host(), as the tasklet only - * gets setup in sdhci_add_host() and we oops. - */ - if (gpio_is_valid(priv->gpio_cd)) { - ret = request_irq(gpio_to_irq(priv->gpio_cd), - sdhci_dove_carddetect_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - mmc_hostname(host->mmc), host); - if (ret) { - dev_err(&pdev->dev, "card detect irq request failed: %d\n", - ret); - goto err_request_irq; - } - } - return 0; -err_request_irq: - sdhci_remove_host(host, 0); err_sdhci_add: if (!IS_ERR(priv->clk)) clk_disable_unprepare(priv->clk); sdhci_pltfm_free(pdev); -err_sdhci_pltfm_init: - if (gpio_is_valid(priv->gpio_cd)) - gpio_free(priv->gpio_cd); return ret; } @@ -186,11 +131,6 @@ static int sdhci_dove_remove(struct platform_device *pdev) sdhci_pltfm_unregister(pdev); - if (gpio_is_valid(priv->gpio_cd)) { - free_irq(gpio_to_irq(priv->gpio_cd), host); - gpio_free(priv->gpio_cd); - } - if (!IS_ERR(priv->clk)) clk_disable_unprepare(priv->clk); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index b841bb7..ccec0e3 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -160,7 +160,6 @@ struct pltfm_imx_data { MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ } multiblock_status; - u32 uhs_mode; u32 is_ddr; }; @@ -382,7 +381,6 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) if (val & ESDHC_MIX_CTRL_SMPCLK_SEL) ret |= SDHCI_CTRL_TUNED_CLK; - ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK); ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; return ret; @@ -429,7 +427,6 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) else new_val &= ~ESDHC_VENDOR_SPEC_VSELECT; writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); - imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK; if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); if (val & SDHCI_CTRL_TUNED_CLK) @@ -600,12 +597,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, u32 temp, val; if (clock == 0) { + host->mmc->actual_clock = 0; + if (esdhc_is_usdhc(imx_data)) { val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, host->ioaddr + ESDHC_VENDOR_SPEC); } - goto out; + return; } if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr) @@ -645,8 +644,6 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, } mdelay(1); -out: - host->clock = clock; } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) @@ -668,7 +665,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) return -ENOSYS; } -static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) +static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) { u32 ctrl; @@ -686,8 +683,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl, SDHCI_HOST_CONTROL); - - return 0; } static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) @@ -697,6 +692,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) /* FIXME: delay a bit for card to be ready for next tuning due to errors */ mdelay(1); + /* This is balanced by the runtime put in sdhci_tasklet_finish */ pm_runtime_get_sync(host->mmc->parent); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | @@ -713,13 +709,12 @@ static void esdhc_request_done(struct mmc_request *mrq) complete(&mrq->completion); } -static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) +static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode, + struct scatterlist *sg) { struct mmc_command cmd = {0}; struct mmc_request mrq = {NULL}; struct mmc_data data = {0}; - struct scatterlist sg; - char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN]; cmd.opcode = opcode; cmd.arg = 0; @@ -728,11 +723,9 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; data.blocks = 1; data.flags = MMC_DATA_READ; - data.sg = &sg; + data.sg = sg; data.sg_len = 1; - sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern)); - mrq.cmd = &cmd; mrq.cmd->mrq = &mrq; mrq.data = &data; @@ -742,14 +735,12 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) mrq.done = esdhc_request_done; init_completion(&(mrq.completion)); - disable_irq(host->irq); - spin_lock(&host->lock); + spin_lock_irq(&host->lock); host->mrq = &mrq; sdhci_send_command(host, mrq.cmd); - spin_unlock(&host->lock); - enable_irq(host->irq); + spin_unlock_irq(&host->lock); wait_for_completion(&mrq.completion); @@ -772,13 +763,21 @@ static void esdhc_post_tuning(struct sdhci_host *host) static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) { + struct scatterlist sg; + char *tuning_pattern; int min, max, avg, ret; + tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL); + if (!tuning_pattern) + return -ENOMEM; + + sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN); + /* find the mininum delay first which can pass tuning */ min = ESDHC_TUNE_CTRL_MIN; while (min < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, min); - if (!esdhc_send_tuning_cmd(host, opcode)) + if (!esdhc_send_tuning_cmd(host, opcode, &sg)) break; min += ESDHC_TUNE_CTRL_STEP; } @@ -787,7 +786,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) max = min + ESDHC_TUNE_CTRL_STEP; while (max < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, max); - if (esdhc_send_tuning_cmd(host, opcode)) { + if (esdhc_send_tuning_cmd(host, opcode, &sg)) { max -= ESDHC_TUNE_CTRL_STEP; break; } @@ -797,9 +796,11 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) /* use average delay to get the best timing */ avg = (min + max) / 2; esdhc_prepare_tuning(host, avg); - ret = esdhc_send_tuning_cmd(host, opcode); + ret = esdhc_send_tuning_cmd(host, opcode, &sg); esdhc_post_tuning(host); + kfree(tuning_pattern); + dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", ret ? "failed" : "passed", avg, ret); @@ -837,28 +838,21 @@ static int esdhc_change_pinstate(struct sdhci_host *host, return pinctrl_select_state(imx_data->pinctrl, pinctrl); } -static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) +static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; - switch (uhs) { + switch (timing) { case MMC_TIMING_UHS_SDR12: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12; - break; case MMC_TIMING_UHS_SDR25: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25; - break; case MMC_TIMING_UHS_SDR50: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50; - break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104; break; case MMC_TIMING_UHS_DDR50: - imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50; + case MMC_TIMING_MMC_DDR52: writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | ESDHC_MIX_CTRL_DDREN, host->ioaddr + ESDHC_MIX_CTRL); @@ -875,7 +869,15 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) break; } - return esdhc_change_pinstate(host, uhs); + esdhc_change_pinstate(host, timing); +} + +static void esdhc_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } static struct sdhci_ops sdhci_esdhc_ops = { @@ -888,8 +890,9 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_max_clock = esdhc_pltfm_get_max_clock, .get_min_clock = esdhc_pltfm_get_min_clock, .get_ro = esdhc_pltfm_get_ro, - .platform_bus_width = esdhc_pltfm_bus_width, + .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, + .reset = esdhc_reset, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { @@ -1170,8 +1173,10 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) ret = sdhci_runtime_suspend_host(host); - clk_disable_unprepare(imx_data->clk_per); - clk_disable_unprepare(imx_data->clk_ipg); + if (!sdhci_sdio_irq_enabled(host)) { + clk_disable_unprepare(imx_data->clk_per); + clk_disable_unprepare(imx_data->clk_ipg); + } clk_disable_unprepare(imx_data->clk_ahb); return ret; @@ -1183,8 +1188,10 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; - clk_prepare_enable(imx_data->clk_per); - clk_prepare_enable(imx_data->clk_ipg); + if (!sdhci_sdio_irq_enabled(host)) { + clk_prepare_enable(imx_data->clk_per); + clk_prepare_enable(imx_data->clk_ipg); + } clk_prepare_enable(imx_data->clk_ahb); return sdhci_runtime_resume_host(host); diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a7d9f95..3497cfa 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -20,10 +20,8 @@ #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ SDHCI_QUIRK_NO_BUSY_IRQ | \ - SDHCI_QUIRK_NONSTANDARD_CLOCK | \ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ - SDHCI_QUIRK_PIO_NEEDS_DELAY | \ - SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) + SDHCI_QUIRK_PIO_NEEDS_DELAY) #define ESDHC_SYSTEM_CONTROL 0x2c #define ESDHC_CLOCK_MASK 0x0000fff0 diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index f7c7cf6..5bd1092 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -52,8 +52,12 @@ static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) } static struct sdhci_ops sdhci_arasan_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_timeout_clock = sdhci_arasan_get_timeout_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static struct sdhci_pltfm_data sdhci_arasan_pdata = { diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0b24997..8be4dcf 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -199,13 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) { - int pre_div = 2; int div = 1; u32 temp; + host->mmc->actual_clock = 0; + if (clock == 0) - goto out; + return; /* Workaround to reduce the clock frequency for p1010 esdhc */ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { @@ -238,24 +239,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); mdelay(1); -out: - host->clock = clock; -} - -#ifdef CONFIG_PM -static u32 esdhc_proctl; -static void esdhc_of_suspend(struct sdhci_host *host) -{ - esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); } -static void esdhc_of_resume(struct sdhci_host *host) -{ - esdhc_of_enable_dma(host); - sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); -} -#endif - static void esdhc_of_platform_init(struct sdhci_host *host) { u32 vvn; @@ -269,7 +254,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host) host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; } -static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) +static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) { u32 ctrl; @@ -289,8 +274,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, ESDHC_CTRL_BUSWIDTH_MASK, ctrl); - - return 0; } static const struct sdhci_ops sdhci_esdhc_ops = { @@ -305,13 +288,46 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .get_max_clock = esdhc_of_get_max_clock, .get_min_clock = esdhc_of_get_min_clock, .platform_init = esdhc_of_platform_init, -#ifdef CONFIG_PM - .platform_suspend = esdhc_of_suspend, - .platform_resume = esdhc_of_resume, -#endif .adma_workaround = esdhci_of_adma_workaround, - .platform_bus_width = esdhc_pltfm_bus_width, + .set_bus_width = esdhc_pltfm_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +#ifdef CONFIG_PM + +static u32 esdhc_proctl; +static int esdhc_of_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + + esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + + return sdhci_suspend_host(host); +} + +static int esdhc_of_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + int ret = sdhci_resume_host(host); + + if (ret == 0) { + /* Isn't this already done by sdhci_resume_host() ? --rmk */ + esdhc_of_enable_dma(host); + sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); + } + + return ret; +} + +static const struct dev_pm_ops esdhc_pmops = { + .suspend = esdhc_of_suspend, + .resume = esdhc_of_resume, }; +#define ESDHC_PMOPS (&esdhc_pmops) +#else +#define ESDHC_PMOPS NULL +#endif static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { /* @@ -374,7 +390,7 @@ static struct platform_driver sdhci_esdhc_driver = { .name = "sdhci-esdhc", .owner = THIS_MODULE, .of_match_table = sdhci_esdhc_of_match, - .pm = SDHCI_PLTFM_PMOPS, + .pm = ESDHC_PMOPS, }, .probe = sdhci_esdhc_probe, .remove = sdhci_esdhc_remove, diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 57c514a..b341661 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -58,6 +58,10 @@ static const struct sdhci_ops sdhci_hlwd_ops = { .write_l = sdhci_hlwd_writel, .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_hlwd_pdata = { diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index f49666b..5670e38 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -21,6 +21,45 @@ #include "sdhci-pci.h" #include "sdhci-pci-o2micro.h" +static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value) +{ + u32 scratch_32; + pci_read_config_dword(chip->pdev, + O2_SD_PLL_SETTING, &scratch_32); + + scratch_32 &= 0x0000FFFF; + scratch_32 |= value; + + pci_write_config_dword(chip->pdev, + O2_SD_PLL_SETTING, scratch_32); +} + +static void o2_pci_led_enable(struct sdhci_pci_chip *chip) +{ + int ret; + u32 scratch_32; + + /* Set led of SD host function enable */ + ret = pci_read_config_dword(chip->pdev, + O2_SD_FUNC_REG0, &scratch_32); + if (ret) + return; + + scratch_32 &= ~O2_SD_FREG0_LEDOFF; + pci_write_config_dword(chip->pdev, + O2_SD_FUNC_REG0, scratch_32); + + ret = pci_read_config_dword(chip->pdev, + O2_SD_TEST_REG, &scratch_32); + if (ret) + return; + + scratch_32 |= O2_SD_LED_ENABLE; + pci_write_config_dword(chip->pdev, + O2_SD_TEST_REG, scratch_32); + +} + void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) { u32 scratch_32; @@ -216,6 +255,40 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) scratch &= 0x7f; pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + /* DevId=8520 subId= 0x11 or 0x12 Type Chip support */ + if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) { + ret = pci_read_config_dword(chip->pdev, + O2_SD_FUNC_REG0, + &scratch_32); + scratch_32 = ((scratch_32 & 0xFF000000) >> 24); + + /* Check Whether subId is 0x11 or 0x12 */ + if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) { + scratch_32 = 0x2c280000; + + /* Set Base Clock to 208MZ */ + o2_pci_set_baseclk(chip, scratch_32); + ret = pci_read_config_dword(chip->pdev, + O2_SD_FUNC_REG4, + &scratch_32); + + /* Enable Base Clk setting change */ + scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET; + pci_write_config_dword(chip->pdev, + O2_SD_FUNC_REG4, + scratch_32); + + /* Set Tuning Window to 4 */ + pci_write_config_byte(chip->pdev, + O2_SD_TUNING_CTRL, 0x44); + + break; + } + } + + /* Enable 8520 led function */ + o2_pci_led_enable(chip); + /* Set timeout CLK */ ret = pci_read_config_dword(chip->pdev, O2_SD_CLK_SETTING, &scratch_32); @@ -276,7 +349,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); ret = pci_read_config_dword(chip->pdev, - O2_SD_FUNC_REG0, &scratch_32); + O2_SD_PLL_SETTING, &scratch_32); if ((scratch_32 & 0xff000000) == 0x01000000) { scratch_32 &= 0x0000FFFF; @@ -299,6 +372,9 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) O2_SD_FUNC_REG4, scratch_32); } + /* Set Tuning Windows to 5 */ + pci_write_config_byte(chip->pdev, + O2_SD_TUNING_CTRL, 0x55); /* Lock WP */ ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); diff --git a/drivers/mmc/host/sdhci-pci-o2micro.h b/drivers/mmc/host/sdhci-pci-o2micro.h index dbec4c9..f7ffc90 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.h +++ b/drivers/mmc/host/sdhci-pci-o2micro.h @@ -57,6 +57,9 @@ #define O2_SD_UHS2_L1_CTRL 0x35C #define O2_SD_FUNC_REG3 0x3E0 #define O2_SD_FUNC_REG4 0x3E4 +#define O2_SD_LED_ENABLE BIT(6) +#define O2_SD_FREG0_LEDOFF BIT(13) +#define O2_SD_FREG4_ENABLE_CLK_SET BIT(22) #define O2_SD_VENDOR_SETTING 0x110 #define O2_SD_VENDOR_SETTING2 0x1C8 diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index fdc6121..52c42fc 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1031,7 +1031,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) return 0; } -static int sdhci_pci_bus_width(struct sdhci_host *host, int width) +static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -1052,8 +1052,6 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width) } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - - return 0; } static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) @@ -1080,8 +1078,11 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) } static const struct sdhci_ops sdhci_pci_ops = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, - .platform_bus_width = sdhci_pci_bus_width, + .set_bus_width = sdhci_pci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index bef250e..7e834fb 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -45,6 +45,10 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host) EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock); static const struct sdhci_ops sdhci_pltfm_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index d51e061..3c0f3c0 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -51,11 +51,13 @@ #define MMC_CARD 0x1000 #define MMC_WIDTH 0x0100 -static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) +static void pxav2_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + sdhci_reset(host, mask); + if (mask == SDHCI_RESET_ALL) { u16 tmp = 0; @@ -88,7 +90,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) } } -static int pxav2_mmc_set_width(struct sdhci_host *host, int width) +static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; u16 tmp; @@ -107,14 +109,14 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width) } writew(tmp, host->ioaddr + SD_CE_ATA_2); writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); - - return 0; } static const struct sdhci_ops pxav2_sdhci_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, - .platform_reset_exit = pxav2_set_private_registers, - .platform_bus_width = pxav2_mmc_set_width, + .set_bus_width = pxav2_mmc_set_bus_width, + .reset = pxav2_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 2fd73b3..f4f1289 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -112,11 +112,13 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, return 0; } -static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask) +static void pxav3_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + sdhci_reset(host, mask); + if (mask == SDHCI_RESET_ALL) { /* * tune timing of read data/command when crc error happen @@ -184,7 +186,7 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode) pxa->power_mode = power_mode; } -static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) +static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) { u16 ctrl_2; @@ -218,15 +220,16 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) dev_dbg(mmc_dev(host->mmc), "%s uhs = %d, ctrl_2 = %04X\n", __func__, uhs, ctrl_2); - - return 0; } static const struct sdhci_ops pxav3_sdhci_ops = { - .platform_reset_exit = pxav3_set_private_registers, + .set_clock = sdhci_set_clock, .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = pxav3_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static struct sdhci_pltfm_data sdhci_pxav3_pdata = { diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index d61eb5a..fa5954a 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -33,9 +33,6 @@ #define MAX_BUS_CLK (4) -/* Number of gpio's used is max data bus width + command and clock lines */ -#define NUM_GPIOS(x) (x + 2) - /** * struct sdhci_s3c - S3C SDHCI instance * @host: The SDHCI host created @@ -58,6 +55,8 @@ struct sdhci_s3c { struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; unsigned long clk_rates[MAX_BUS_CLK]; + + bool no_divider; }; /** @@ -70,6 +69,7 @@ struct sdhci_s3c { */ struct sdhci_s3c_drv_data { unsigned int sdhci_quirks; + bool no_divider; }; static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) @@ -119,7 +119,7 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, * If controller uses a non-standard clock division, find the best clock * speed possible with selected clock source and skip the division. */ - if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (ourhost->no_divider) { rate = clk_round_rate(clksrc, wanted); return wanted - rate; } @@ -161,9 +161,13 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) int src; u32 ctrl; + host->mmc->actual_clock = 0; + /* don't bother if the clock is going off. */ - if (clock == 0) + if (clock == 0) { + sdhci_set_clock(host, clock); return; + } for (src = 0; src < MAX_BUS_CLK; src++) { delta = sdhci_s3c_consider_clock(ourhost, src, clock); @@ -215,6 +219,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) if (clock < 25 * 1000000) ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2); writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3); + + sdhci_set_clock(host, clock); } /** @@ -295,10 +301,11 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) unsigned long timeout; u16 clk = 0; + host->mmc->actual_clock = 0; + /* If the clock is going off, set to 0 at clock control register */ if (clock == 0) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); - host->clock = clock; return; } @@ -306,8 +313,6 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); - host->clock = clock; - clk = SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); @@ -329,14 +334,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) } /** - * sdhci_s3c_platform_bus_width - support 8bit buswidth + * sdhci_s3c_set_bus_width - support 8bit buswidth * @host: The SDHCI host being queried * @width: MMC_BUS_WIDTH_ macro for the bus width being requested * * We have 8-bit width support but is not a v3 controller. * So we add platform_bus_width() and support 8bit width. */ -static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) +static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -358,93 +363,23 @@ static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - - return 0; } static struct sdhci_ops sdhci_s3c_ops = { .get_max_clock = sdhci_s3c_get_max_clk, .set_clock = sdhci_s3c_set_clock, .get_min_clock = sdhci_s3c_get_min_clock, - .platform_bus_width = sdhci_s3c_platform_bus_width, + .set_bus_width = sdhci_s3c_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; -static void sdhci_s3c_notify_change(struct platform_device *dev, int state) -{ - struct sdhci_host *host = platform_get_drvdata(dev); -#ifdef CONFIG_PM_RUNTIME - struct sdhci_s3c *sc = sdhci_priv(host); -#endif - unsigned long flags; - - if (host) { - spin_lock_irqsave(&host->lock, flags); - if (state) { - dev_dbg(&dev->dev, "card inserted.\n"); -#ifdef CONFIG_PM_RUNTIME - clk_prepare_enable(sc->clk_io); -#endif - host->flags &= ~SDHCI_DEVICE_DEAD; - host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; - } else { - dev_dbg(&dev->dev, "card removed.\n"); - host->flags |= SDHCI_DEVICE_DEAD; - host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; -#ifdef CONFIG_PM_RUNTIME - clk_disable_unprepare(sc->clk_io); -#endif - } - tasklet_schedule(&host->card_tasklet); - spin_unlock_irqrestore(&host->lock, flags); - } -} - -static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id) -{ - struct sdhci_s3c *sc = dev_id; - int status = gpio_get_value(sc->ext_cd_gpio); - if (sc->pdata->ext_cd_gpio_invert) - status = !status; - sdhci_s3c_notify_change(sc->pdev, status); - return IRQ_HANDLED; -} - -static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) -{ - struct s3c_sdhci_platdata *pdata = sc->pdata; - struct device *dev = &sc->pdev->dev; - - if (devm_gpio_request(dev, pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { - sc->ext_cd_gpio = pdata->ext_cd_gpio; - sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); - if (sc->ext_cd_irq && - request_threaded_irq(sc->ext_cd_irq, NULL, - sdhci_s3c_gpio_card_detect_thread, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - dev_name(dev), sc) == 0) { - int status = gpio_get_value(sc->ext_cd_gpio); - if (pdata->ext_cd_gpio_invert) - status = !status; - sdhci_s3c_notify_change(sc->pdev, status); - } else { - dev_warn(dev, "cannot request irq for card detect\n"); - sc->ext_cd_irq = 0; - } - } else { - dev_err(dev, "cannot request gpio for card detect\n"); - } -} - #ifdef CONFIG_OF static int sdhci_s3c_parse_dt(struct device *dev, struct sdhci_host *host, struct s3c_sdhci_platdata *pdata) { struct device_node *node = dev->of_node; - struct sdhci_s3c *ourhost = to_s3c(host); u32 max_width; - int gpio; /* if the bus-width property is not specified, assume width as 1 */ if (of_property_read_u32(node, "bus-width", &max_width)) @@ -462,18 +397,8 @@ static int sdhci_s3c_parse_dt(struct device *dev, return 0; } - gpio = of_get_named_gpio(node, "cd-gpios", 0); - if (gpio_is_valid(gpio)) { - pdata->cd_type = S3C_SDHCI_CD_GPIO; - pdata->ext_cd_gpio = gpio; - ourhost->ext_cd_gpio = -1; - if (of_get_property(node, "cd-inverted", NULL)) - pdata->ext_cd_gpio_invert = 1; + if (of_get_named_gpio(node, "cd-gpios", 0)) return 0; - } else if (gpio != -ENOENT) { - dev_err(dev, "invalid card detect gpio specified\n"); - return -EINVAL; - } /* assuming internal card detect that will be configured by pinctrl */ pdata->cd_type = S3C_SDHCI_CD_INTERNAL; @@ -606,8 +531,10 @@ static int sdhci_s3c_probe(struct platform_device *pdev) /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; - if (drv_data) + if (drv_data) { host->quirks |= drv_data->sdhci_quirks; + sc->no_divider = drv_data->no_divider; + } #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -656,7 +583,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev) * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (sc->no_divider) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; @@ -674,6 +601,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); + mmc_of_parse(host->mmc); + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); @@ -682,15 +611,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev) goto err_req_regs; } - /* The following two methods of card detection might call - sdhci_s3c_notify_change() immediately, so they can be called - only after sdhci_add_host(). Setup errors are ignored. */ - if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init) - pdata->ext_cd_init(&sdhci_s3c_notify_change); - if (pdata->cd_type == S3C_SDHCI_CD_GPIO && - gpio_is_valid(pdata->ext_cd_gpio)) - sdhci_s3c_setup_card_detect_gpio(sc); - #ifdef CONFIG_PM_RUNTIME if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) clk_disable_unprepare(sc->clk_io); @@ -711,16 +631,12 @@ static int sdhci_s3c_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_s3c *sc = sdhci_priv(host); - struct s3c_sdhci_platdata *pdata = sc->pdata; - - if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) - pdata->ext_cd_cleanup(&sdhci_s3c_notify_change); if (sc->ext_cd_irq) free_irq(sc->ext_cd_irq, sc); #ifdef CONFIG_PM_RUNTIME - if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) + if (sc->pdata->cd_type != S3C_SDHCI_CD_INTERNAL) clk_prepare_enable(sc->clk_io); #endif sdhci_remove_host(host, 1); @@ -797,7 +713,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = { #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { - .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, + .no_divider = true, }; #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data) #else diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 696122c..1700453 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -28,7 +28,11 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) } static struct sdhci_ops sdhci_sirf_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_sirf_get_max_clk, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static struct sdhci_pltfm_data sdhci_sirf_pdata = { diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 0316dec..9d535c7 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -38,7 +38,10 @@ struct spear_sdhci { /* sdhci ops */ static const struct sdhci_ops sdhci_pltfm_ops = { - /* Nothing to do for now. */ + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index a835898..d93a063 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -32,11 +32,17 @@ /* Tegra SDHOST controller vendor register definitions */ #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 +#define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 +#define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 +#define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200 #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) +#define NVQUIRK_DISABLE_SDR50 BIT(3) +#define NVQUIRK_DISABLE_SDR104 BIT(4) +#define NVQUIRK_DISABLE_DDR50 BIT(5) struct sdhci_tegra_soc_data { const struct sdhci_pltfm_data *pdata; @@ -48,19 +54,6 @@ struct sdhci_tegra { int power_gpio; }; -static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) -{ - u32 val; - - if (unlikely(reg == SDHCI_PRESENT_STATE)) { - /* Use wp_gpio here instead? */ - val = readl(host->ioaddr + reg); - return val | SDHCI_WRITE_PROTECT; - } - - return readl(host->ioaddr + reg); -} - static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -108,26 +101,33 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) return mmc_gpio_get_ro(host->mmc); } -static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) +static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = pltfm_host->priv; const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; + u32 misc_ctrl; + + sdhci_reset(host, mask); if (!(mask & SDHCI_RESET_ALL)) return; + misc_ctrl = sdhci_readw(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); /* Erratum: Enable SDHCI spec v3.00 support */ - if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) { - u32 misc_ctrl; - - misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); + if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; - sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); - } + /* Don't advertise UHS modes which aren't supported yet */ + if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR50) + misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50; + if (soc_data->nvquirks & NVQUIRK_DISABLE_DDR50) + misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50; + if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104) + misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; + sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); } -static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) +static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) { u32 ctrl; @@ -144,23 +144,25 @@ static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) ctrl &= ~SDHCI_CTRL_4BITBUS; } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - return 0; } static const struct sdhci_ops tegra_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, - .read_l = tegra_sdhci_readl, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, - .platform_bus_width = tegra_sdhci_buswidth, - .platform_reset_exit = tegra_sdhci_reset_exit, + .set_clock = sdhci_set_clock, + .set_bus_width = tegra_sdhci_set_bus_width, + .reset = tegra_sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | - SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, + SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .ops = &tegra_sdhci_ops, }; @@ -175,13 +177,16 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | - SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, + SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .ops = &tegra_sdhci_ops, }; static struct sdhci_tegra_soc_data soc_data_tegra30 = { .pdata = &sdhci_tegra30_pdata, - .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300, + .nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 | + NVQUIRK_DISABLE_SDR50 | + NVQUIRK_DISABLE_SDR104, }; static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { @@ -189,12 +194,16 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_NO_HISPD_BIT | - SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, + SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .ops = &tegra_sdhci_ops, }; static struct sdhci_tegra_soc_data soc_data_tegra114 = { .pdata = &sdhci_tegra114_pdata, + .nvquirks = NVQUIRK_DISABLE_SDR50 | + NVQUIRK_DISABLE_DDR50 | + NVQUIRK_DISABLE_SDR104, }; static const struct of_device_id sdhci_tegra_dt_match[] = { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9a79fc4..47055f3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -44,6 +44,8 @@ #define MAX_TUNING_LOOP 40 +#define ADMA_SIZE ((128 * 2 + 1) * 4) + static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; @@ -131,43 +133,26 @@ static void sdhci_dumpregs(struct sdhci_host *host) * * \*****************************************************************************/ -static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) -{ - u32 ier; - - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - ier &= ~clear; - ier |= set; - sdhci_writel(host, ier, SDHCI_INT_ENABLE); - sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); -} - -static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs) -{ - sdhci_clear_set_irqs(host, 0, irqs); -} - -static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs) -{ - sdhci_clear_set_irqs(host, irqs, 0); -} - static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) { - u32 present, irqs; + u32 present; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || (host->mmc->caps & MMC_CAP_NONREMOVABLE)) return; - present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; - irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; + if (enable) { + present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; - if (enable) - sdhci_unmask_irqs(host, irqs); - else - sdhci_mask_irqs(host, irqs); + host->ier |= present ? SDHCI_INT_CARD_REMOVE : + SDHCI_INT_CARD_INSERT; + } else { + host->ier &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); + } + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } static void sdhci_enable_card_detection(struct sdhci_host *host) @@ -180,22 +165,9 @@ static void sdhci_disable_card_detection(struct sdhci_host *host) sdhci_set_card_detection(host, false); } -static void sdhci_reset(struct sdhci_host *host, u8 mask) +void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; - u32 uninitialized_var(ier); - - if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT)) - return; - } - - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - - if (host->ops->platform_reset_enter) - host->ops->platform_reset_enter(host, mask); sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); @@ -220,16 +192,27 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) timeout--; mdelay(1); } +} +EXPORT_SYMBOL_GPL(sdhci_reset); + +static void sdhci_do_reset(struct sdhci_host *host, u8 mask) +{ + if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { + if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT)) + return; + } - if (host->ops->platform_reset_exit) - host->ops->platform_reset_exit(host, mask); + host->ops->reset(host, mask); - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) - sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); + if (mask & SDHCI_RESET_ALL) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->ops->enable_dma) + host->ops->enable_dma(host); + } - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { - if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) - host->ops->enable_dma(host); + /* Resetting the controller clears many */ + host->preset_enabled = false; } } @@ -238,15 +221,18 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); static void sdhci_init(struct sdhci_host *host, int soft) { if (soft) - sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); else - sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_do_reset(host, SDHCI_RESET_ALL); - sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, - SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | - SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | - SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | - SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE); + host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | + SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | + SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | + SDHCI_INT_RESPONSE; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); if (soft) { /* force clock reconfiguration */ @@ -502,11 +488,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, else direction = DMA_TO_DEVICE; - /* - * The ADMA descriptor table is mapped further down as we - * need to fill it with data first. - */ - host->align_addr = dma_map_single(mmc_dev(host->mmc), host->align_buffer, 128 * 4, direction); if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) @@ -567,7 +548,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, * If this triggers then we have a calculation bug * somewhere. :/ */ - WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4); + WARN_ON((desc - host->adma_desc) > ADMA_SIZE); } if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { @@ -595,17 +576,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->align_addr, 128 * 4, direction); } - host->adma_addr = dma_map_single(mmc_dev(host->mmc), - host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); - if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr)) - goto unmap_entries; - BUG_ON(host->adma_addr & 0x3); - return 0; -unmap_entries: - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, direction); unmap_align: dma_unmap_single(mmc_dev(host->mmc), host->align_addr, 128 * 4, direction); @@ -623,19 +595,25 @@ static void sdhci_adma_table_post(struct sdhci_host *host, u8 *align; char *buffer; unsigned long flags; + bool has_unaligned; if (data->flags & MMC_DATA_READ) direction = DMA_FROM_DEVICE; else direction = DMA_TO_DEVICE; - dma_unmap_single(mmc_dev(host->mmc), host->adma_addr, - (128 * 2 + 1) * 4, DMA_TO_DEVICE); - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, 128 * 4, direction); - if (data->flags & MMC_DATA_READ) { + /* Do a quick scan of the SG list for any unaligned mappings */ + has_unaligned = false; + for_each_sg(data->sg, sg, host->sg_count, i) + if (sg_dma_address(sg) & 3) { + has_unaligned = true; + break; + } + + if (has_unaligned && data->flags & MMC_DATA_READ) { dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, data->sg_len, direction); @@ -721,9 +699,12 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; if (host->flags & SDHCI_REQ_USE_DMA) - sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); + host->ier = (host->ier & ~pio_irqs) | dma_irqs; else - sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); + host->ier = (host->ier & ~dma_irqs) | pio_irqs; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) @@ -976,8 +957,8 @@ static void sdhci_finish_data(struct sdhci_host *host) * upon error conditions. */ if (data->error) { - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); } sdhci_send_command(host, data->stop); @@ -1107,24 +1088,23 @@ static void sdhci_finish_command(struct sdhci_host *host) static u16 sdhci_get_preset_value(struct sdhci_host *host) { - u16 ctrl, preset = 0; - - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + u16 preset = 0; - switch (ctrl & SDHCI_CTRL_UHS_MASK) { - case SDHCI_CTRL_UHS_SDR12: + switch (host->timing) { + case MMC_TIMING_UHS_SDR12: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12); break; - case SDHCI_CTRL_UHS_SDR25: + case MMC_TIMING_UHS_SDR25: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25); break; - case SDHCI_CTRL_UHS_SDR50: + case MMC_TIMING_UHS_SDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50); break; - case SDHCI_CTRL_UHS_SDR104: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104); break; - case SDHCI_CTRL_UHS_DDR50: + case MMC_TIMING_UHS_DDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); break; default: @@ -1136,32 +1116,22 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) return preset; } -static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div = 0; /* Initialized for compiler warning */ int real_div = div, clk_mul = 1; u16 clk = 0; unsigned long timeout; - if (clock && clock == host->clock) - return; - host->mmc->actual_clock = 0; - if (host->ops->set_clock) { - host->ops->set_clock(host, clock); - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) - return; - } - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) - goto out; + return; if (host->version >= SDHCI_SPEC_300) { - if (sdhci_readw(host, SDHCI_HOST_CONTROL2) & - SDHCI_CTRL_PRESET_VAL_ENABLE) { + if (host->preset_enabled) { u16 pre_val; clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); @@ -1247,26 +1217,16 @@ clock_set: clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - -out: - host->clock = clock; } +EXPORT_SYMBOL_GPL(sdhci_set_clock); -static inline void sdhci_update_clock(struct sdhci_host *host) -{ - unsigned int clock; - - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); -} - -static int sdhci_set_power(struct sdhci_host *host, unsigned short power) +static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) { u8 pwr = 0; - if (power != (unsigned short)-1) { - switch (1 << power) { + if (mode != MMC_POWER_OFF) { + switch (1 << vdd) { case MMC_VDD_165_195: pwr = SDHCI_POWER_180; break; @@ -1284,7 +1244,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) } if (host->pwr == pwr) - return -1; + return; host->pwr = pwr; @@ -1292,38 +1252,43 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) sdhci_runtime_pm_bus_off(host); - return 0; - } - - /* - * Spec says that we should clear the power reg before setting - * a new value. Some controllers don't seem to like this though. - */ - if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) - sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + vdd = 0; + } else { + /* + * Spec says that we should clear the power reg before setting + * a new value. Some controllers don't seem to like this though. + */ + if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - /* - * At least the Marvell CaFe chip gets confused if we set the voltage - * and set turn on power at the same time, so set the voltage first. - */ - if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + /* + * At least the Marvell CaFe chip gets confused if we set the + * voltage and set turn on power at the same time, so set the + * voltage first. + */ + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - pwr |= SDHCI_POWER_ON; + pwr |= SDHCI_POWER_ON; - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) - sdhci_runtime_pm_bus_on(host); + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_on(host); - /* - * Some controllers need an extra 10ms delay of 10ms before they - * can apply clock after applying power - */ - if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) - mdelay(10); + /* + * Some controllers need an extra 10ms delay of 10ms before + * they can apply clock after applying power + */ + if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) + mdelay(10); + } - return power; + if (host->vmmc) { + spin_unlock_irq(&host->lock); + mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd); + spin_lock_irq(&host->lock); + } } /*****************************************************************************\ @@ -1427,10 +1392,53 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->lock, flags); } +void sdhci_set_bus_width(struct sdhci_host *host, int width) +{ + u8 ctrl; + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + if (width == MMC_BUS_WIDTH_8) { + ctrl &= ~SDHCI_CTRL_4BITBUS; + if (host->version >= SDHCI_SPEC_300) + ctrl |= SDHCI_CTRL_8BITBUS; + } else { + if (host->version >= SDHCI_SPEC_300) + ctrl &= ~SDHCI_CTRL_8BITBUS; + if (width == MMC_BUS_WIDTH_4) + ctrl |= SDHCI_CTRL_4BITBUS; + else + ctrl &= ~SDHCI_CTRL_4BITBUS; + } + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} +EXPORT_SYMBOL_GPL(sdhci_set_bus_width); + +void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) +{ + u16 ctrl_2; + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + if ((timing == MMC_TIMING_MMC_HS200) || + (timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + else if (timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + else if (timing == MMC_TIMING_UHS_SDR25) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + else if ((timing == MMC_TIMING_UHS_DDR50) || + (timing == MMC_TIMING_MMC_DDR52)) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} +EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); + static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; - int vdd_bit = -1; u8 ctrl; spin_lock_irqsave(&host->lock, flags); @@ -1456,45 +1464,17 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host, false); - sdhci_set_clock(host, ios->clock); - - if (ios->power_mode == MMC_POWER_OFF) - vdd_bit = sdhci_set_power(host, -1); - else - vdd_bit = sdhci_set_power(host, ios->vdd); - - if (host->vmmc && vdd_bit != -1) { - spin_unlock_irqrestore(&host->lock, flags); - mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); - spin_lock_irqsave(&host->lock, flags); + if (!ios->clock || ios->clock != host->clock) { + host->ops->set_clock(host, ios->clock); + host->clock = ios->clock; } + sdhci_set_power(host, ios->power_mode, ios->vdd); + if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); - /* - * If your platform has 8-bit width support but is not a v3 controller, - * or if it requires special setup code, you should implement that in - * platform_bus_width(). - */ - if (host->ops->platform_bus_width) { - host->ops->platform_bus_width(host, ios->bus_width); - } else { - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); - if (ios->bus_width == MMC_BUS_WIDTH_8) { - ctrl &= ~SDHCI_CTRL_4BITBUS; - if (host->version >= SDHCI_SPEC_300) - ctrl |= SDHCI_CTRL_8BITBUS; - } else { - if (host->version >= SDHCI_SPEC_300) - ctrl &= ~SDHCI_CTRL_8BITBUS; - if (ios->bus_width == MMC_BUS_WIDTH_4) - ctrl |= SDHCI_CTRL_4BITBUS; - else - ctrl &= ~SDHCI_CTRL_4BITBUS; - } - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - } + host->ops->set_bus_width(host, ios->bus_width); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); @@ -1510,19 +1490,20 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) /* In case of UHS-I modes, set High Speed Enable */ if ((ios->timing == MMC_TIMING_MMC_HS200) || + (ios->timing == MMC_TIMING_MMC_DDR52) || (ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || (ios->timing == MMC_TIMING_UHS_DDR50) || (ios->timing == MMC_TIMING_UHS_SDR25)) ctrl |= SDHCI_CTRL_HISPD; - ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); - if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) { + if (!host->preset_enabled) { sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* * We only need to set Driver Strength if the * preset value enable is not set. */ + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; @@ -1546,7 +1527,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ - sdhci_update_clock(host); + host->ops->set_clock(host, host->clock); } @@ -1555,25 +1536,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) clk &= ~SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - if (host->ops->set_uhs_signaling) - host->ops->set_uhs_signaling(host, ios->timing); - else { - ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); - /* Select Bus Speed Mode for host */ - ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; - if ((ios->timing == MMC_TIMING_MMC_HS200) || - (ios->timing == MMC_TIMING_UHS_SDR104)) - ctrl_2 |= SDHCI_CTRL_UHS_SDR104; - else if (ios->timing == MMC_TIMING_UHS_SDR12) - ctrl_2 |= SDHCI_CTRL_UHS_SDR12; - else if (ios->timing == MMC_TIMING_UHS_SDR25) - ctrl_2 |= SDHCI_CTRL_UHS_SDR25; - else if (ios->timing == MMC_TIMING_UHS_SDR50) - ctrl_2 |= SDHCI_CTRL_UHS_SDR50; - else if (ios->timing == MMC_TIMING_UHS_DDR50) - ctrl_2 |= SDHCI_CTRL_UHS_DDR50; - sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); - } + host->ops->set_uhs_signaling(host, ios->timing); + host->timing = ios->timing; if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && ((ios->timing == MMC_TIMING_UHS_SDR12) || @@ -1590,7 +1554,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ - sdhci_update_clock(host); + host->ops->set_clock(host, host->clock); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -1600,7 +1564,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) * it on each ios seems to solve the problem. */ if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) - sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb(); spin_unlock_irqrestore(&host->lock, flags); @@ -1709,24 +1673,16 @@ static int sdhci_get_ro(struct mmc_host *mmc) static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { - if (host->flags & SDHCI_DEVICE_DEAD) - goto out; - - if (enable) - host->flags |= SDHCI_SDIO_IRQ_ENABLED; - else - host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; - - /* SDIO IRQ will be enabled as appropriate in runtime resume */ - if (host->runtime_suspended) - goto out; + if (!(host->flags & SDHCI_DEVICE_DEAD)) { + if (enable) + host->ier |= SDHCI_INT_CARD_INT; + else + host->ier &= ~SDHCI_INT_CARD_INT; - if (enable) - sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); - else - sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); -out: - mmiowb(); + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + mmiowb(); + } } static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1734,9 +1690,18 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; + sdhci_runtime_pm_get(host); + spin_lock_irqsave(&host->lock, flags); + if (enable) + host->flags |= SDHCI_SDIO_IRQ_ENABLED; + else + host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; + sdhci_enable_sdio_irq_nolock(host, enable); spin_unlock_irqrestore(&host->lock, flags); + + sdhci_runtime_pm_put(host); } static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, @@ -1855,22 +1820,15 @@ static int sdhci_card_busy(struct mmc_host *mmc) static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { - struct sdhci_host *host; + struct sdhci_host *host = mmc_priv(mmc); u16 ctrl; - u32 ier; int tuning_loop_counter = MAX_TUNING_LOOP; - unsigned long timeout; int err = 0; - bool requires_tuning_nonuhs = false; unsigned long flags; - host = mmc_priv(mmc); - sdhci_runtime_pm_get(host); spin_lock_irqsave(&host->lock, flags); - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - /* * The Host Controller needs tuning only in case of SDR104 mode * and for SDR50 mode when Use Tuning for SDR50 is set in the @@ -1878,15 +1836,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * If the Host Controller supports the HS200 mode then the * tuning function has to be executed. */ - if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && - (host->flags & SDHCI_SDR50_NEEDS_TUNING || - host->flags & SDHCI_SDR104_NEEDS_TUNING)) - requires_tuning_nonuhs = true; - - if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || - requires_tuning_nonuhs) - ctrl |= SDHCI_CTRL_EXEC_TUNING; - else { + switch (host->timing) { + case MMC_TIMING_MMC_HS200: + case MMC_TIMING_UHS_SDR104: + break; + + case MMC_TIMING_UHS_SDR50: + if (host->flags & SDHCI_SDR50_NEEDS_TUNING || + host->flags & SDHCI_SDR104_NEEDS_TUNING) + break; + /* FALLTHROUGH */ + + default: spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); return 0; @@ -1899,6 +1860,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) return err; } + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl |= SDHCI_CTRL_EXEC_TUNING; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); /* @@ -1911,21 +1874,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * to make sure we don't hit a controller bug, we _only_ * enable Buffer Read Ready interrupt here. */ - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); /* * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number * of loops reaches 40 times or a timeout of 150ms occurs. */ - timeout = 150; do { struct mmc_command cmd = {0}; struct mmc_request mrq = {NULL}; - if (!tuning_loop_counter && !timeout) - break; - cmd.opcode = opcode; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -1933,6 +1892,9 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) cmd.data = NULL; cmd.error = 0; + if (tuning_loop_counter-- == 0) + break; + mrq.cmd = &cmd; host->mrq = &mrq; @@ -1990,26 +1952,25 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) host->tuning_done = 0; ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - tuning_loop_counter--; - timeout--; - mdelay(1); + + /* eMMC spec does not require a delay between tuning cycles */ + if (opcode == MMC_SEND_TUNING_BLOCK) + mdelay(1); } while (ctrl & SDHCI_CTRL_EXEC_TUNING); /* * The Host Driver has exhausted the maximum number of loops allowed, * so use fixed sampling frequency. */ - if (!tuning_loop_counter || !timeout) { + if (tuning_loop_counter < 0) { ctrl &= ~SDHCI_CTRL_TUNED_CLK; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + } + if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { + pr_info(DRIVER_NAME ": Tuning procedure" + " failed, falling back to fixed sampling" + " clock\n"); err = -EIO; - } else { - if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { - pr_info(DRIVER_NAME ": Tuning procedure" - " failed, falling back to fixed sampling" - " clock\n"); - err = -EIO; - } } out: @@ -2044,7 +2005,8 @@ out: if (err && (host->flags & SDHCI_USING_RETUNING_TIMER)) err = 0; - sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); @@ -2054,26 +2016,30 @@ out: static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) { - u16 ctrl; - /* Host Controller v3.00 defines preset value registers */ if (host->version < SDHCI_SPEC_300) return; - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - /* * We only enable or disable Preset Value if they are not already * enabled or disabled respectively. Otherwise, we bail out. */ - if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { - ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; - sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - host->flags |= SDHCI_PV_ENABLED; - } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { - ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + if (host->preset_enabled != enable) { + u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + if (enable) + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; + else + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - host->flags &= ~SDHCI_PV_ENABLED; + + if (enable) + host->flags |= SDHCI_PV_ENABLED; + else + host->flags &= ~SDHCI_PV_ENABLED; + + host->preset_enabled = enable; } } @@ -2095,8 +2061,8 @@ static void sdhci_card_event(struct mmc_host *mmc) pr_err("%s: Resetting controller.\n", mmc_hostname(host->mmc)); - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); @@ -2124,15 +2090,6 @@ static const struct mmc_host_ops sdhci_ops = { * * \*****************************************************************************/ -static void sdhci_tasklet_card(unsigned long param) -{ - struct sdhci_host *host = (struct sdhci_host*)param; - - sdhci_card_event(host->mmc); - - mmc_detect_change(host->mmc, msecs_to_jiffies(200)); -} - static void sdhci_tasklet_finish(unsigned long param) { struct sdhci_host *host; @@ -2169,12 +2126,12 @@ static void sdhci_tasklet_finish(unsigned long param) /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ - sdhci_update_clock(host); + host->ops->set_clock(host, host->clock); /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); } host->mrq = NULL; @@ -2424,101 +2381,94 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) static irqreturn_t sdhci_irq(int irq, void *dev_id) { - irqreturn_t result; + irqreturn_t result = IRQ_NONE; struct sdhci_host *host = dev_id; - u32 intmask, unexpected = 0; - int cardint = 0, max_loops = 16; + u32 intmask, mask, unexpected = 0; + int max_loops = 16; spin_lock(&host->lock); - if (host->runtime_suspended) { + if (host->runtime_suspended && !sdhci_sdio_irq_enabled(host)) { spin_unlock(&host->lock); return IRQ_NONE; } intmask = sdhci_readl(host, SDHCI_INT_STATUS); - if (!intmask || intmask == 0xffffffff) { result = IRQ_NONE; goto out; } -again: - DBG("*** %s got interrupt: 0x%08x\n", - mmc_hostname(host->mmc), intmask); - - if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { - u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; - - /* - * There is a observation on i.mx esdhc. INSERT bit will be - * immediately set again when it gets cleared, if a card is - * inserted. We have to mask the irq to prevent interrupt - * storm which will freeze the system. And the REMOVE gets - * the same situation. - * - * More testing are needed here to ensure it works for other - * platforms though. - */ - sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT : - SDHCI_INT_CARD_REMOVE); - sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE : - SDHCI_INT_CARD_INSERT); - - sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | - SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); - intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); - tasklet_schedule(&host->card_tasklet); - } - - if (intmask & SDHCI_INT_CMD_MASK) { - sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, - SDHCI_INT_STATUS); - sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); - } + do { + /* Clear selected interrupts. */ + mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | + SDHCI_INT_BUS_POWER); + sdhci_writel(host, mask, SDHCI_INT_STATUS); - if (intmask & SDHCI_INT_DATA_MASK) { - sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK, - SDHCI_INT_STATUS); - sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); - } + DBG("*** %s got interrupt: 0x%08x\n", + mmc_hostname(host->mmc), intmask); - intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); + if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; - intmask &= ~SDHCI_INT_ERROR; + /* + * There is a observation on i.mx esdhc. INSERT + * bit will be immediately set again when it gets + * cleared, if a card is inserted. We have to mask + * the irq to prevent interrupt storm which will + * freeze the system. And the REMOVE gets the + * same situation. + * + * More testing are needed here to ensure it works + * for other platforms though. + */ + host->ier &= ~(SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE); + host->ier |= present ? SDHCI_INT_CARD_REMOVE : + SDHCI_INT_CARD_INSERT; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + + sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); + + host->thread_isr |= intmask & (SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE); + result = IRQ_WAKE_THREAD; + } - if (intmask & SDHCI_INT_BUS_POWER) { - pr_err("%s: Card is consuming too much power!\n", - mmc_hostname(host->mmc)); - sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS); - } + if (intmask & SDHCI_INT_CMD_MASK) + sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); - intmask &= ~SDHCI_INT_BUS_POWER; + if (intmask & SDHCI_INT_DATA_MASK) + sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); - if (intmask & SDHCI_INT_CARD_INT) - cardint = 1; + if (intmask & SDHCI_INT_BUS_POWER) + pr_err("%s: Card is consuming too much power!\n", + mmc_hostname(host->mmc)); - intmask &= ~SDHCI_INT_CARD_INT; + if (intmask & SDHCI_INT_CARD_INT) { + sdhci_enable_sdio_irq_nolock(host, false); + host->thread_isr |= SDHCI_INT_CARD_INT; + result = IRQ_WAKE_THREAD; + } - if (intmask) { - unexpected |= intmask; - sdhci_writel(host, intmask, SDHCI_INT_STATUS); - } + intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | + SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | + SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | + SDHCI_INT_CARD_INT); - result = IRQ_HANDLED; + if (intmask) { + unexpected |= intmask; + sdhci_writel(host, intmask, SDHCI_INT_STATUS); + } - intmask = sdhci_readl(host, SDHCI_INT_STATUS); + if (result == IRQ_NONE) + result = IRQ_HANDLED; - /* - * If we know we'll call the driver to signal SDIO IRQ, disregard - * further indications of Card Interrupt in the status to avoid a - * needless loop. - */ - if (cardint) - intmask &= ~SDHCI_INT_CARD_INT; - if (intmask && --max_loops) - goto again; + intmask = sdhci_readl(host, SDHCI_INT_STATUS); + } while (intmask && --max_loops); out: spin_unlock(&host->lock); @@ -2527,15 +2477,38 @@ out: mmc_hostname(host->mmc), unexpected); sdhci_dumpregs(host); } - /* - * We have to delay this as it calls back into the driver. - */ - if (cardint) - mmc_signal_sdio_irq(host->mmc); return result; } +static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) +{ + struct sdhci_host *host = dev_id; + unsigned long flags; + u32 isr; + + spin_lock_irqsave(&host->lock, flags); + isr = host->thread_isr; + host->thread_isr = 0; + spin_unlock_irqrestore(&host->lock, flags); + + if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + sdhci_card_event(host->mmc); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + } + + if (isr & SDHCI_INT_CARD_INT) { + sdio_run_irqs(host->mmc); + + spin_lock_irqsave(&host->lock, flags); + if (host->flags & SDHCI_SDIO_IRQ_ENABLED) + sdhci_enable_sdio_irq_nolock(host, true); + spin_unlock_irqrestore(&host->lock, flags); + } + + return isr ? IRQ_HANDLED : IRQ_NONE; +} + /*****************************************************************************\ * * * Suspend/resume * @@ -2572,9 +2545,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { - if (host->ops->platform_suspend) - host->ops->platform_suspend(host); - sdhci_disable_card_detection(host); /* Disable tuning since we are suspending */ @@ -2584,7 +2554,9 @@ int sdhci_suspend_host(struct sdhci_host *host) } if (!device_may_wakeup(mmc_dev(host->mmc))) { - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + host->ier = 0; + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); } else { sdhci_enable_irq_wakeups(host); @@ -2605,8 +2577,9 @@ int sdhci_resume_host(struct sdhci_host *host) } if (!device_may_wakeup(mmc_dev(host->mmc))) { - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(host->mmc), host); + ret = request_threaded_irq(host->irq, sdhci_irq, + sdhci_thread_irq, IRQF_SHARED, + mmc_hostname(host->mmc), host); if (ret) return ret; } else { @@ -2628,9 +2601,6 @@ int sdhci_resume_host(struct sdhci_host *host) sdhci_enable_card_detection(host); - if (host->ops->platform_resume) - host->ops->platform_resume(host); - /* Set the re-tuning expiration flag */ if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; @@ -2682,10 +2652,12 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) } spin_lock_irqsave(&host->lock, flags); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + host->ier &= SDHCI_INT_CARD_INT; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); spin_unlock_irqrestore(&host->lock, flags); - synchronize_irq(host->irq); + synchronize_hardirq(host->irq); spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = true; @@ -2729,7 +2701,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) host->runtime_suspended = false; /* Enable SDIO IRQ */ - if ((host->flags & SDHCI_SDIO_IRQ_ENABLED)) + if (host->flags & SDHCI_SDIO_IRQ_ENABLED) sdhci_enable_sdio_irq_nolock(host, true); /* Enable Card Detection */ @@ -2788,7 +2760,7 @@ int sdhci_add_host(struct sdhci_host *host) if (debug_quirks2) host->quirks2 = debug_quirks2; - sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_do_reset(host, SDHCI_RESET_ALL); host->version = sdhci_readw(host, SDHCI_HOST_VERSION); host->version = (host->version & SDHCI_SPEC_VER_MASK) @@ -2848,15 +2820,29 @@ int sdhci_add_host(struct sdhci_host *host) * (128) and potentially one alignment transfer for * each of those entries. */ - host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL); + host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc), + ADMA_SIZE, &host->adma_addr, + GFP_KERNEL); host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); if (!host->adma_desc || !host->align_buffer) { - kfree(host->adma_desc); + dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + host->adma_desc, host->adma_addr); kfree(host->align_buffer); pr_warning("%s: Unable to allocate ADMA " "buffers. Falling back to standard DMA.\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; + host->adma_desc = NULL; + host->align_buffer = NULL; + } else if (host->adma_addr & 3) { + pr_warning("%s: unable to allocate aligned ADMA descriptor\n", + mmc_hostname(mmc)); + host->flags &= ~SDHCI_USE_ADMA; + dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + host->adma_desc, host->adma_addr); + kfree(host->align_buffer); + host->adma_desc = NULL; + host->align_buffer = NULL; } } @@ -2941,6 +2927,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; @@ -3212,8 +3199,6 @@ int sdhci_add_host(struct sdhci_host *host) /* * Init tasklets. */ - tasklet_init(&host->card_tasklet, - sdhci_tasklet_card, (unsigned long)host); tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host); @@ -3230,8 +3215,8 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_init(host, 0); - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(mmc), host); + ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, + IRQF_SHARED, mmc_hostname(mmc), host); if (ret) { pr_err("%s: Failed to request IRQ %d: %d\n", mmc_hostname(mmc), host->irq, ret); @@ -3273,12 +3258,12 @@ int sdhci_add_host(struct sdhci_host *host) #ifdef SDHCI_USE_LEDS_CLASS reset: - sdhci_reset(host, SDHCI_RESET_ALL); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + sdhci_do_reset(host, SDHCI_RESET_ALL); + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); #endif untasklet: - tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); return ret; @@ -3315,14 +3300,14 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) #endif if (!dead) - sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_do_reset(host, SDHCI_RESET_ALL); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); del_timer_sync(&host->timer); - tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); if (host->vmmc) { @@ -3335,7 +3320,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) regulator_put(host->vqmmc); } - kfree(host->adma_desc); + if (host->adma_desc) + dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + host->adma_desc, host->adma_addr); kfree(host->align_buffer); host->adma_desc = NULL; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 0a3ed01..4a5cd5e 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -281,18 +281,14 @@ struct sdhci_ops { unsigned int (*get_max_clock)(struct sdhci_host *host); unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); - int (*platform_bus_width)(struct sdhci_host *host, - int width); + void (*set_bus_width)(struct sdhci_host *host, int width); void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); unsigned int (*get_ro)(struct sdhci_host *host); - void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); - void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); + void (*reset)(struct sdhci_host *host, u8 mask); int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); - int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); + void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*hw_reset)(struct sdhci_host *host); - void (*platform_suspend)(struct sdhci_host *host); - void (*platform_resume)(struct sdhci_host *host); void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); void (*card_event)(struct sdhci_host *host); @@ -397,6 +393,16 @@ extern void sdhci_remove_host(struct sdhci_host *host, int dead); extern void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd); +static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) +{ + return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); +} + +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); +void sdhci_set_bus_width(struct sdhci_host *host, int width); +void sdhci_reset(struct sdhci_host *host, u8 mask); +void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing); + #ifdef CONFIG_PM extern int sdhci_suspend_host(struct sdhci_host *host); extern int sdhci_resume_host(struct sdhci_host *host); diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 54730f4..656fbba 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -803,12 +803,13 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, break; } switch (host->timing) { - case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: /* * MMC core will only set this timing, if the host - * advertises the MMC_CAP_UHS_DDR50 capability. MMCIF - * implementations with this capability, e.g. sh73a0, - * will have to set it in their platform data. + * advertises the MMC_CAP_1_8V_DDR/MMC_CAP_1_2V_DDR + * capability. MMCIF implementations with this + * capability, e.g. sh73a0, will have to set it + * in their platform data. */ tmp |= CMD_SET_DARS; break; diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c new file mode 100644 index 0000000..eb2bbbe --- /dev/null +++ b/drivers/mmc/host/usdhi6rol0.c @@ -0,0 +1,1847 @@ +/* + * Copyright (C) 2013-2014 Renesas Electronics Europe Ltd. + * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/highmem.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/log2.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/sdio.h> +#include <linux/module.h> +#include <linux/pagemap.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> +#include <linux/string.h> +#include <linux/time.h> +#include <linux/virtio.h> +#include <linux/workqueue.h> + +#define USDHI6_SD_CMD 0x0000 +#define USDHI6_SD_PORT_SEL 0x0004 +#define USDHI6_SD_ARG 0x0008 +#define USDHI6_SD_STOP 0x0010 +#define USDHI6_SD_SECCNT 0x0014 +#define USDHI6_SD_RSP10 0x0018 +#define USDHI6_SD_RSP32 0x0020 +#define USDHI6_SD_RSP54 0x0028 +#define USDHI6_SD_RSP76 0x0030 +#define USDHI6_SD_INFO1 0x0038 +#define USDHI6_SD_INFO2 0x003c +#define USDHI6_SD_INFO1_MASK 0x0040 +#define USDHI6_SD_INFO2_MASK 0x0044 +#define USDHI6_SD_CLK_CTRL 0x0048 +#define USDHI6_SD_SIZE 0x004c +#define USDHI6_SD_OPTION 0x0050 +#define USDHI6_SD_ERR_STS1 0x0058 +#define USDHI6_SD_ERR_STS2 0x005c +#define USDHI6_SD_BUF0 0x0060 +#define USDHI6_SDIO_MODE 0x0068 +#define USDHI6_SDIO_INFO1 0x006c +#define USDHI6_SDIO_INFO1_MASK 0x0070 +#define USDHI6_CC_EXT_MODE 0x01b0 +#define USDHI6_SOFT_RST 0x01c0 +#define USDHI6_VERSION 0x01c4 +#define USDHI6_HOST_MODE 0x01c8 +#define USDHI6_SDIF_MODE 0x01cc + +#define USDHI6_SD_CMD_APP 0x0040 +#define USDHI6_SD_CMD_MODE_RSP_AUTO 0x0000 +#define USDHI6_SD_CMD_MODE_RSP_NONE 0x0300 +#define USDHI6_SD_CMD_MODE_RSP_R1 0x0400 /* Also R5, R6, R7 */ +#define USDHI6_SD_CMD_MODE_RSP_R1B 0x0500 /* R1b */ +#define USDHI6_SD_CMD_MODE_RSP_R2 0x0600 +#define USDHI6_SD_CMD_MODE_RSP_R3 0x0700 /* Also R4 */ +#define USDHI6_SD_CMD_DATA 0x0800 +#define USDHI6_SD_CMD_READ 0x1000 +#define USDHI6_SD_CMD_MULTI 0x2000 +#define USDHI6_SD_CMD_CMD12_AUTO_OFF 0x4000 + +#define USDHI6_CC_EXT_MODE_SDRW BIT(1) + +#define USDHI6_SD_INFO1_RSP_END BIT(0) +#define USDHI6_SD_INFO1_ACCESS_END BIT(2) +#define USDHI6_SD_INFO1_CARD_OUT BIT(3) +#define USDHI6_SD_INFO1_CARD_IN BIT(4) +#define USDHI6_SD_INFO1_CD BIT(5) +#define USDHI6_SD_INFO1_WP BIT(7) +#define USDHI6_SD_INFO1_D3_CARD_OUT BIT(8) +#define USDHI6_SD_INFO1_D3_CARD_IN BIT(9) + +#define USDHI6_SD_INFO2_CMD_ERR BIT(0) +#define USDHI6_SD_INFO2_CRC_ERR BIT(1) +#define USDHI6_SD_INFO2_END_ERR BIT(2) +#define USDHI6_SD_INFO2_TOUT BIT(3) +#define USDHI6_SD_INFO2_IWA_ERR BIT(4) +#define USDHI6_SD_INFO2_IRA_ERR BIT(5) +#define USDHI6_SD_INFO2_RSP_TOUT BIT(6) +#define USDHI6_SD_INFO2_SDDAT0 BIT(7) +#define USDHI6_SD_INFO2_BRE BIT(8) +#define USDHI6_SD_INFO2_BWE BIT(9) +#define USDHI6_SD_INFO2_SCLKDIVEN BIT(13) +#define USDHI6_SD_INFO2_CBSY BIT(14) +#define USDHI6_SD_INFO2_ILA BIT(15) + +#define USDHI6_SD_INFO1_CARD_INSERT (USDHI6_SD_INFO1_CARD_IN | USDHI6_SD_INFO1_D3_CARD_IN) +#define USDHI6_SD_INFO1_CARD_EJECT (USDHI6_SD_INFO1_CARD_OUT | USDHI6_SD_INFO1_D3_CARD_OUT) +#define USDHI6_SD_INFO1_CARD (USDHI6_SD_INFO1_CARD_INSERT | USDHI6_SD_INFO1_CARD_EJECT) +#define USDHI6_SD_INFO1_CARD_CD (USDHI6_SD_INFO1_CARD_IN | USDHI6_SD_INFO1_CARD_OUT) + +#define USDHI6_SD_INFO2_ERR (USDHI6_SD_INFO2_CMD_ERR | \ + USDHI6_SD_INFO2_CRC_ERR | USDHI6_SD_INFO2_END_ERR | \ + USDHI6_SD_INFO2_TOUT | USDHI6_SD_INFO2_IWA_ERR | \ + USDHI6_SD_INFO2_IRA_ERR | USDHI6_SD_INFO2_RSP_TOUT | \ + USDHI6_SD_INFO2_ILA) + +#define USDHI6_SD_INFO1_IRQ (USDHI6_SD_INFO1_RSP_END | USDHI6_SD_INFO1_ACCESS_END | \ + USDHI6_SD_INFO1_CARD) + +#define USDHI6_SD_INFO2_IRQ (USDHI6_SD_INFO2_ERR | USDHI6_SD_INFO2_BRE | \ + USDHI6_SD_INFO2_BWE | 0x0800 | USDHI6_SD_INFO2_ILA) + +#define USDHI6_SD_CLK_CTRL_SCLKEN BIT(8) + +#define USDHI6_SD_STOP_STP BIT(0) +#define USDHI6_SD_STOP_SEC BIT(8) + +#define USDHI6_SDIO_INFO1_IOIRQ BIT(0) +#define USDHI6_SDIO_INFO1_EXPUB52 BIT(14) +#define USDHI6_SDIO_INFO1_EXWT BIT(15) + +#define USDHI6_SD_ERR_STS1_CRC_NO_ERROR BIT(13) + +#define USDHI6_SOFT_RST_RESERVED (BIT(1) | BIT(2)) +#define USDHI6_SOFT_RST_RESET BIT(0) + +#define USDHI6_SD_OPTION_TIMEOUT_SHIFT 4 +#define USDHI6_SD_OPTION_TIMEOUT_MASK (0xf << USDHI6_SD_OPTION_TIMEOUT_SHIFT) +#define USDHI6_SD_OPTION_WIDTH_1 BIT(15) + +#define USDHI6_SD_PORT_SEL_PORTS_SHIFT 8 + +#define USDHI6_SD_CLK_CTRL_DIV_MASK 0xff + +#define USDHI6_SDIO_INFO1_IRQ (USDHI6_SDIO_INFO1_IOIRQ | 3 | \ + USDHI6_SDIO_INFO1_EXPUB52 | USDHI6_SDIO_INFO1_EXWT) + +#define USDHI6_MIN_DMA 64 + +enum usdhi6_wait_for { + USDHI6_WAIT_FOR_REQUEST, + USDHI6_WAIT_FOR_CMD, + USDHI6_WAIT_FOR_MREAD, + USDHI6_WAIT_FOR_MWRITE, + USDHI6_WAIT_FOR_READ, + USDHI6_WAIT_FOR_WRITE, + USDHI6_WAIT_FOR_DATA_END, + USDHI6_WAIT_FOR_STOP, + USDHI6_WAIT_FOR_DMA, +}; + +struct usdhi6_page { + struct page *page; + void *mapped; /* mapped page */ +}; + +struct usdhi6_host { + struct mmc_host *mmc; + struct mmc_request *mrq; + void __iomem *base; + struct clk *clk; + + /* SG memory handling */ + + /* Common for multiple and single block requests */ + struct usdhi6_page pg; /* current page from an SG */ + void *blk_page; /* either a mapped page, or the bounce buffer */ + size_t offset; /* offset within a page, including sg->offset */ + + /* Blocks, crossing a page boundary */ + size_t head_len; + struct usdhi6_page head_pg; + + /* A bounce buffer for unaligned blocks or blocks, crossing a page boundary */ + struct scatterlist bounce_sg; + u8 bounce_buf[512]; + + /* Multiple block requests only */ + struct scatterlist *sg; /* current SG segment */ + int page_idx; /* page index within an SG segment */ + + enum usdhi6_wait_for wait; + u32 status_mask; + u32 status2_mask; + u32 sdio_mask; + u32 io_error; + u32 irq_status; + unsigned long imclk; + unsigned long rate; + bool app_cmd; + + /* Timeout handling */ + struct delayed_work timeout_work; + unsigned long timeout; + + /* DMA support */ + struct dma_chan *chan_rx; + struct dma_chan *chan_tx; + bool dma_active; +}; + +/* I/O primitives */ + +static void usdhi6_write(struct usdhi6_host *host, u32 reg, u32 data) +{ + iowrite32(data, host->base + reg); + dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, + host->base, reg, data); +} + +static void usdhi6_write16(struct usdhi6_host *host, u32 reg, u16 data) +{ + iowrite16(data, host->base + reg); + dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, + host->base, reg, data); +} + +static u32 usdhi6_read(struct usdhi6_host *host, u32 reg) +{ + u32 data = ioread32(host->base + reg); + dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, + host->base, reg, data); + return data; +} + +static u16 usdhi6_read16(struct usdhi6_host *host, u32 reg) +{ + u16 data = ioread16(host->base + reg); + dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, + host->base, reg, data); + return data; +} + +static void usdhi6_irq_enable(struct usdhi6_host *host, u32 info1, u32 info2) +{ + host->status_mask = USDHI6_SD_INFO1_IRQ & ~info1; + host->status2_mask = USDHI6_SD_INFO2_IRQ & ~info2; + usdhi6_write(host, USDHI6_SD_INFO1_MASK, host->status_mask); + usdhi6_write(host, USDHI6_SD_INFO2_MASK, host->status2_mask); +} + +static void usdhi6_wait_for_resp(struct usdhi6_host *host) +{ + usdhi6_irq_enable(host, USDHI6_SD_INFO1_RSP_END | + USDHI6_SD_INFO1_ACCESS_END | USDHI6_SD_INFO1_CARD_CD, + USDHI6_SD_INFO2_ERR); +} + +static void usdhi6_wait_for_brwe(struct usdhi6_host *host, bool read) +{ + usdhi6_irq_enable(host, USDHI6_SD_INFO1_ACCESS_END | + USDHI6_SD_INFO1_CARD_CD, USDHI6_SD_INFO2_ERR | + (read ? USDHI6_SD_INFO2_BRE : USDHI6_SD_INFO2_BWE)); +} + +static void usdhi6_only_cd(struct usdhi6_host *host) +{ + /* Mask all except card hotplug */ + usdhi6_irq_enable(host, USDHI6_SD_INFO1_CARD_CD, 0); +} + +static void usdhi6_mask_all(struct usdhi6_host *host) +{ + usdhi6_irq_enable(host, 0, 0); +} + +static int usdhi6_error_code(struct usdhi6_host *host) +{ + u32 err; + + usdhi6_write(host, USDHI6_SD_STOP, USDHI6_SD_STOP_STP); + + if (host->io_error & + (USDHI6_SD_INFO2_RSP_TOUT | USDHI6_SD_INFO2_TOUT)) { + u32 rsp54 = usdhi6_read(host, USDHI6_SD_RSP54); + int opc = host->mrq ? host->mrq->cmd->opcode : -1; + + err = usdhi6_read(host, USDHI6_SD_ERR_STS2); + /* Response timeout is often normal, don't spam the log */ + if (host->wait == USDHI6_WAIT_FOR_CMD) + dev_dbg(mmc_dev(host->mmc), + "T-out sts 0x%x, resp 0x%x, state %u, CMD%d\n", + err, rsp54, host->wait, opc); + else + dev_warn(mmc_dev(host->mmc), + "T-out sts 0x%x, resp 0x%x, state %u, CMD%d\n", + err, rsp54, host->wait, opc); + return -ETIMEDOUT; + } + + err = usdhi6_read(host, USDHI6_SD_ERR_STS1); + if (err != USDHI6_SD_ERR_STS1_CRC_NO_ERROR) + dev_warn(mmc_dev(host->mmc), "Err sts 0x%x, state %u, CMD%d\n", + err, host->wait, host->mrq ? host->mrq->cmd->opcode : -1); + if (host->io_error & USDHI6_SD_INFO2_ILA) + return -EILSEQ; + + return -EIO; +} + +/* Scatter-Gather management */ + +/* + * In PIO mode we have to map each page separately, using kmap(). That way + * adjacent pages are mapped to non-adjacent virtual addresses. That's why we + * have to use a bounce buffer for blocks, crossing page boundaries. Such blocks + * have been observed with an SDIO WiFi card (b43 driver). + */ +static void usdhi6_blk_bounce(struct usdhi6_host *host, + struct scatterlist *sg) +{ + struct mmc_data *data = host->mrq->data; + size_t blk_head = host->head_len; + + dev_dbg(mmc_dev(host->mmc), "%s(): CMD%u of %u SG: %ux%u @ 0x%x\n", + __func__, host->mrq->cmd->opcode, data->sg_len, + data->blksz, data->blocks, sg->offset); + + host->head_pg.page = host->pg.page; + host->head_pg.mapped = host->pg.mapped; + host->pg.page = nth_page(host->pg.page, 1); + host->pg.mapped = kmap(host->pg.page); + + host->blk_page = host->bounce_buf; + host->offset = 0; + + if (data->flags & MMC_DATA_READ) + return; + + memcpy(host->bounce_buf, host->head_pg.mapped + PAGE_SIZE - blk_head, + blk_head); + memcpy(host->bounce_buf + blk_head, host->pg.mapped, + data->blksz - blk_head); +} + +/* Only called for multiple block IO */ +static void usdhi6_sg_prep(struct usdhi6_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_data *data = mrq->data; + + usdhi6_write(host, USDHI6_SD_SECCNT, data->blocks); + + host->sg = data->sg; + /* TODO: if we always map, this is redundant */ + host->offset = host->sg->offset; +} + +/* Map the first page in an SG segment: common for multiple and single block IO */ +static void *usdhi6_sg_map(struct usdhi6_host *host) +{ + struct mmc_data *data = host->mrq->data; + struct scatterlist *sg = data->sg_len > 1 ? host->sg : data->sg; + size_t head = PAGE_SIZE - sg->offset; + size_t blk_head = head % data->blksz; + + WARN(host->pg.page, "%p not properly unmapped!\n", host->pg.page); + if (WARN(sg_dma_len(sg) % data->blksz, + "SG size %zd isn't a multiple of block size %zd\n", + sg_dma_len(sg), data->blksz)) + return NULL; + + host->pg.page = sg_page(sg); + host->pg.mapped = kmap(host->pg.page); + host->offset = sg->offset; + + /* + * Block size must be a power of 2 for multi-block transfers, + * therefore blk_head is equal for all pages in this SG + */ + host->head_len = blk_head; + + if (head < data->blksz) + /* + * The first block in the SG crosses a page boundary. + * Max blksz = 512, so blocks can only span 2 pages + */ + usdhi6_blk_bounce(host, sg); + else + host->blk_page = host->pg.mapped; + + dev_dbg(mmc_dev(host->mmc), "Mapped %p (%lx) at %p + %u for CMD%u @ 0x%p\n", + host->pg.page, page_to_pfn(host->pg.page), host->pg.mapped, + sg->offset, host->mrq->cmd->opcode, host->mrq); + + return host->blk_page + host->offset; +} + +/* Unmap the current page: common for multiple and single block IO */ +static void usdhi6_sg_unmap(struct usdhi6_host *host, bool force) +{ + struct mmc_data *data = host->mrq->data; + struct page *page = host->head_pg.page; + + if (page) { + /* Previous block was cross-page boundary */ + struct scatterlist *sg = data->sg_len > 1 ? + host->sg : data->sg; + size_t blk_head = host->head_len; + + if (!data->error && data->flags & MMC_DATA_READ) { + memcpy(host->head_pg.mapped + PAGE_SIZE - blk_head, + host->bounce_buf, blk_head); + memcpy(host->pg.mapped, host->bounce_buf + blk_head, + data->blksz - blk_head); + } + + flush_dcache_page(page); + kunmap(page); + + host->head_pg.page = NULL; + + if (!force && sg_dma_len(sg) + sg->offset > + (host->page_idx << PAGE_SHIFT) + data->blksz - blk_head) + /* More blocks in this SG, don't unmap the next page */ + return; + } + + page = host->pg.page; + if (!page) + return; + + flush_dcache_page(page); + kunmap(page); + + host->pg.page = NULL; +} + +/* Called from MMC_WRITE_MULTIPLE_BLOCK or MMC_READ_MULTIPLE_BLOCK */ +static void usdhi6_sg_advance(struct usdhi6_host *host) +{ + struct mmc_data *data = host->mrq->data; + size_t done, total; + + /* New offset: set at the end of the previous block */ + if (host->head_pg.page) { + /* Finished a cross-page block, jump to the new page */ + host->page_idx++; + host->offset = data->blksz - host->head_len; + host->blk_page = host->pg.mapped; + usdhi6_sg_unmap(host, false); + } else { + host->offset += data->blksz; + /* The completed block didn't cross a page boundary */ + if (host->offset == PAGE_SIZE) { + /* If required, we'll map the page below */ + host->offset = 0; + host->page_idx++; + } + } + + /* + * Now host->blk_page + host->offset point at the end of our last block + * and host->page_idx is the index of the page, in which our new block + * is located, if any + */ + + done = (host->page_idx << PAGE_SHIFT) + host->offset; + total = host->sg->offset + sg_dma_len(host->sg); + + dev_dbg(mmc_dev(host->mmc), "%s(): %zu of %zu @ %u\n", __func__, + done, total, host->offset); + + if (done < total && host->offset) { + /* More blocks in this page */ + if (host->offset + data->blksz > PAGE_SIZE) + /* We approached at a block, that spans 2 pages */ + usdhi6_blk_bounce(host, host->sg); + + return; + } + + /* Finished current page or an SG segment */ + usdhi6_sg_unmap(host, false); + + if (done == total) { + /* + * End of an SG segment or the complete SG: jump to the next + * segment, we'll map it later in usdhi6_blk_read() or + * usdhi6_blk_write() + */ + struct scatterlist *next = sg_next(host->sg); + + host->page_idx = 0; + + if (!next) + host->wait = USDHI6_WAIT_FOR_DATA_END; + host->sg = next; + + if (WARN(next && sg_dma_len(next) % data->blksz, + "SG size %zd isn't a multiple of block size %zd\n", + sg_dma_len(next), data->blksz)) + data->error = -EINVAL; + + return; + } + + /* We cannot get here after crossing a page border */ + + /* Next page in the same SG */ + host->pg.page = nth_page(sg_page(host->sg), host->page_idx); + host->pg.mapped = kmap(host->pg.page); + host->blk_page = host->pg.mapped; + + dev_dbg(mmc_dev(host->mmc), "Mapped %p (%lx) at %p for CMD%u @ 0x%p\n", + host->pg.page, page_to_pfn(host->pg.page), host->pg.mapped, + host->mrq->cmd->opcode, host->mrq); +} + +/* DMA handling */ + +static void usdhi6_dma_release(struct usdhi6_host *host) +{ + host->dma_active = false; + if (host->chan_tx) { + struct dma_chan *chan = host->chan_tx; + host->chan_tx = NULL; + dma_release_channel(chan); + } + if (host->chan_rx) { + struct dma_chan *chan = host->chan_rx; + host->chan_rx = NULL; + dma_release_channel(chan); + } +} + +static void usdhi6_dma_stop_unmap(struct usdhi6_host *host) +{ + struct mmc_data *data = host->mrq->data; + + if (!host->dma_active) + return; + + usdhi6_write(host, USDHI6_CC_EXT_MODE, 0); + host->dma_active = false; + + if (data->flags & MMC_DATA_READ) + dma_unmap_sg(host->chan_rx->device->dev, data->sg, + data->sg_len, DMA_FROM_DEVICE); + else + dma_unmap_sg(host->chan_tx->device->dev, data->sg, + data->sg_len, DMA_TO_DEVICE); +} + +static void usdhi6_dma_complete(void *arg) +{ + struct usdhi6_host *host = arg; + struct mmc_request *mrq = host->mrq; + + if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion for %p!\n", + dev_name(mmc_dev(host->mmc)), mrq)) + return; + + dev_dbg(mmc_dev(host->mmc), "%s(): CMD%u DMA completed\n", __func__, + mrq->cmd->opcode); + + usdhi6_dma_stop_unmap(host); + usdhi6_wait_for_brwe(host, mrq->data->flags & MMC_DATA_READ); +} + +static int usdhi6_dma_setup(struct usdhi6_host *host, struct dma_chan *chan, + enum dma_transfer_direction dir) +{ + struct mmc_data *data = host->mrq->data; + struct scatterlist *sg = data->sg; + struct dma_async_tx_descriptor *desc = NULL; + dma_cookie_t cookie = -EINVAL; + enum dma_data_direction data_dir; + int ret; + + switch (dir) { + case DMA_MEM_TO_DEV: + data_dir = DMA_TO_DEVICE; + break; + case DMA_DEV_TO_MEM: + data_dir = DMA_FROM_DEVICE; + break; + default: + return -EINVAL; + } + + ret = dma_map_sg(chan->device->dev, sg, data->sg_len, data_dir); + if (ret > 0) { + host->dma_active = true; + desc = dmaengine_prep_slave_sg(chan, sg, ret, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + } + + if (desc) { + desc->callback = usdhi6_dma_complete; + desc->callback_param = host; + cookie = dmaengine_submit(desc); + } + + dev_dbg(mmc_dev(host->mmc), "%s(): mapped %d -> %d, cookie %d @ %p\n", + __func__, data->sg_len, ret, cookie, desc); + + if (cookie < 0) { + /* DMA failed, fall back to PIO */ + if (ret >= 0) + ret = cookie; + usdhi6_dma_release(host); + dev_warn(mmc_dev(host->mmc), + "DMA failed: %d, falling back to PIO\n", ret); + } + + return cookie; +} + +static int usdhi6_dma_start(struct usdhi6_host *host) +{ + if (!host->chan_rx || !host->chan_tx) + return -ENODEV; + + if (host->mrq->data->flags & MMC_DATA_READ) + return usdhi6_dma_setup(host, host->chan_rx, DMA_DEV_TO_MEM); + + return usdhi6_dma_setup(host, host->chan_tx, DMA_MEM_TO_DEV); +} + +static void usdhi6_dma_kill(struct usdhi6_host *host) +{ + struct mmc_data *data = host->mrq->data; + + dev_dbg(mmc_dev(host->mmc), "%s(): SG of %u: %ux%u\n", + __func__, data->sg_len, data->blocks, data->blksz); + /* Abort DMA */ + if (data->flags & MMC_DATA_READ) + dmaengine_terminate_all(host->chan_rx); + else + dmaengine_terminate_all(host->chan_tx); +} + +static void usdhi6_dma_check_error(struct usdhi6_host *host) +{ + struct mmc_data *data = host->mrq->data; + + dev_dbg(mmc_dev(host->mmc), "%s(): IO error %d, status 0x%x\n", + __func__, host->io_error, usdhi6_read(host, USDHI6_SD_INFO1)); + + if (host->io_error) { + data->error = usdhi6_error_code(host); + data->bytes_xfered = 0; + usdhi6_dma_kill(host); + usdhi6_dma_release(host); + dev_warn(mmc_dev(host->mmc), + "DMA failed: %d, falling back to PIO\n", data->error); + return; + } + + /* + * The datasheet tells us to check a response from the card, whereas + * responses only come after the command phase, not after the data + * phase. Let's check anyway. + */ + if (host->irq_status & USDHI6_SD_INFO1_RSP_END) + dev_warn(mmc_dev(host->mmc), "Unexpected response received!\n"); +} + +static void usdhi6_dma_kick(struct usdhi6_host *host) +{ + if (host->mrq->data->flags & MMC_DATA_READ) + dma_async_issue_pending(host->chan_rx); + else + dma_async_issue_pending(host->chan_tx); +} + +static void usdhi6_dma_request(struct usdhi6_host *host, phys_addr_t start) +{ + struct dma_slave_config cfg = { + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + }; + int ret; + + host->chan_tx = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); + dev_dbg(mmc_dev(host->mmc), "%s: TX: got channel %p\n", __func__, + host->chan_tx); + + if (!host->chan_tx) + return; + + cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = start + USDHI6_SD_BUF0; + cfg.dst_maxburst = 128; /* 128 words * 4 bytes = 512 bytes */ + cfg.src_addr = 0; + ret = dmaengine_slave_config(host->chan_tx, &cfg); + if (ret < 0) + goto e_release_tx; + + host->chan_rx = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); + dev_dbg(mmc_dev(host->mmc), "%s: RX: got channel %p\n", __func__, + host->chan_rx); + + if (!host->chan_rx) + goto e_release_tx; + + cfg.direction = DMA_DEV_TO_MEM; + cfg.src_addr = cfg.dst_addr; + cfg.src_maxburst = 128; /* 128 words * 4 bytes = 512 bytes */ + cfg.dst_addr = 0; + ret = dmaengine_slave_config(host->chan_rx, &cfg); + if (ret < 0) + goto e_release_rx; + + return; + +e_release_rx: + dma_release_channel(host->chan_rx); + host->chan_rx = NULL; +e_release_tx: + dma_release_channel(host->chan_tx); + host->chan_tx = NULL; +} + +/* API helpers */ + +static void usdhi6_clk_set(struct usdhi6_host *host, struct mmc_ios *ios) +{ + unsigned long rate = ios->clock; + u32 val; + unsigned int i; + + for (i = 1000; i; i--) { + if (usdhi6_read(host, USDHI6_SD_INFO2) & USDHI6_SD_INFO2_SCLKDIVEN) + break; + usleep_range(10, 100); + } + + if (!i) { + dev_err(mmc_dev(host->mmc), "SD bus busy, clock set aborted\n"); + return; + } + + val = usdhi6_read(host, USDHI6_SD_CLK_CTRL) & ~USDHI6_SD_CLK_CTRL_DIV_MASK; + + if (rate) { + unsigned long new_rate; + + if (host->imclk <= rate) { + if (ios->timing != MMC_TIMING_UHS_DDR50) { + /* Cannot have 1-to-1 clock in DDR mode */ + new_rate = host->imclk; + val |= 0xff; + } else { + new_rate = host->imclk / 2; + } + } else { + unsigned long div = + roundup_pow_of_two(DIV_ROUND_UP(host->imclk, rate)); + val |= div >> 2; + new_rate = host->imclk / div; + } + + if (host->rate == new_rate) + return; + + host->rate = new_rate; + + dev_dbg(mmc_dev(host->mmc), "target %lu, div %u, set %lu\n", + rate, (val & 0xff) << 2, new_rate); + } + + /* + * if old or new rate is equal to input rate, have to switch the clock + * off before changing and on after + */ + if (host->imclk == rate || host->imclk == host->rate || !rate) + usdhi6_write(host, USDHI6_SD_CLK_CTRL, + val & ~USDHI6_SD_CLK_CTRL_SCLKEN); + + if (!rate) { + host->rate = 0; + return; + } + + usdhi6_write(host, USDHI6_SD_CLK_CTRL, val); + + if (host->imclk == rate || host->imclk == host->rate || + !(val & USDHI6_SD_CLK_CTRL_SCLKEN)) + usdhi6_write(host, USDHI6_SD_CLK_CTRL, + val | USDHI6_SD_CLK_CTRL_SCLKEN); +} + +static void usdhi6_set_power(struct usdhi6_host *host, struct mmc_ios *ios) +{ + struct mmc_host *mmc = host->mmc; + + if (!IS_ERR(mmc->supply.vmmc)) + /* Errors ignored... */ + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->power_mode ? ios->vdd : 0); +} + +static int usdhi6_reset(struct usdhi6_host *host) +{ + int i; + + usdhi6_write(host, USDHI6_SOFT_RST, USDHI6_SOFT_RST_RESERVED); + cpu_relax(); + usdhi6_write(host, USDHI6_SOFT_RST, USDHI6_SOFT_RST_RESERVED | USDHI6_SOFT_RST_RESET); + for (i = 1000; i; i--) + if (usdhi6_read(host, USDHI6_SOFT_RST) & USDHI6_SOFT_RST_RESET) + break; + + return i ? 0 : -ETIMEDOUT; +} + +static void usdhi6_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct usdhi6_host *host = mmc_priv(mmc); + u32 option, mode; + int ret; + + dev_dbg(mmc_dev(mmc), "%uHz, OCR: %u, power %u, bus-width %u, timing %u\n", + ios->clock, ios->vdd, ios->power_mode, ios->bus_width, ios->timing); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + usdhi6_set_power(host, ios); + usdhi6_only_cd(host); + break; + case MMC_POWER_UP: + /* + * We only also touch USDHI6_SD_OPTION from .request(), which + * cannot race with MMC_POWER_UP + */ + ret = usdhi6_reset(host); + if (ret < 0) { + dev_err(mmc_dev(mmc), "Cannot reset the interface!\n"); + } else { + usdhi6_set_power(host, ios); + usdhi6_only_cd(host); + } + break; + case MMC_POWER_ON: + option = usdhi6_read(host, USDHI6_SD_OPTION); + /* + * The eMMC standard only allows 4 or 8 bits in the DDR mode, + * the same probably holds for SD cards. We check here anyway, + * since the datasheet explicitly requires 4 bits for DDR. + */ + if (ios->bus_width == MMC_BUS_WIDTH_1) { + if (ios->timing == MMC_TIMING_UHS_DDR50) + dev_err(mmc_dev(mmc), + "4 bits are required for DDR\n"); + option |= USDHI6_SD_OPTION_WIDTH_1; + mode = 0; + } else { + option &= ~USDHI6_SD_OPTION_WIDTH_1; + mode = ios->timing == MMC_TIMING_UHS_DDR50; + } + usdhi6_write(host, USDHI6_SD_OPTION, option); + usdhi6_write(host, USDHI6_SDIF_MODE, mode); + break; + } + + if (host->rate != ios->clock) + usdhi6_clk_set(host, ios); +} + +/* This is data timeout. Response timeout is fixed to 640 clock cycles */ +static void usdhi6_timeout_set(struct usdhi6_host *host) +{ + struct mmc_request *mrq = host->mrq; + u32 val; + unsigned long ticks; + + if (!mrq->data) + ticks = host->rate / 1000 * mrq->cmd->busy_timeout; + else + ticks = host->rate / 1000000 * (mrq->data->timeout_ns / 1000) + + mrq->data->timeout_clks; + + if (!ticks || ticks > 1 << 27) + /* Max timeout */ + val = 14; + else if (ticks < 1 << 13) + /* Min timeout */ + val = 0; + else + val = order_base_2(ticks) - 13; + + dev_dbg(mmc_dev(host->mmc), "Set %s timeout %lu ticks @ %lu Hz\n", + mrq->data ? "data" : "cmd", ticks, host->rate); + + /* Timeout Counter mask: 0xf0 */ + usdhi6_write(host, USDHI6_SD_OPTION, (val << USDHI6_SD_OPTION_TIMEOUT_SHIFT) | + (usdhi6_read(host, USDHI6_SD_OPTION) & ~USDHI6_SD_OPTION_TIMEOUT_MASK)); +} + +static void usdhi6_request_done(struct usdhi6_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_data *data = mrq->data; + + if (WARN(host->pg.page || host->head_pg.page, + "Page %p or %p not unmapped: wait %u, CMD%d(%c) @ +0x%x %ux%u in SG%u!\n", + host->pg.page, host->head_pg.page, host->wait, mrq->cmd->opcode, + data ? (data->flags & MMC_DATA_READ ? 'R' : 'W') : '-', + data ? host->offset : 0, data ? data->blocks : 0, + data ? data->blksz : 0, data ? data->sg_len : 0)) + usdhi6_sg_unmap(host, true); + + if (mrq->cmd->error || + (data && data->error) || + (mrq->stop && mrq->stop->error)) + dev_dbg(mmc_dev(host->mmc), "%s(CMD%d: %ux%u): err %d %d %d\n", + __func__, mrq->cmd->opcode, data ? data->blocks : 0, + data ? data->blksz : 0, + mrq->cmd->error, + data ? data->error : 1, + mrq->stop ? mrq->stop->error : 1); + + /* Disable DMA */ + usdhi6_write(host, USDHI6_CC_EXT_MODE, 0); + host->wait = USDHI6_WAIT_FOR_REQUEST; + host->mrq = NULL; + + mmc_request_done(host->mmc, mrq); +} + +static int usdhi6_cmd_flags(struct usdhi6_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = mrq->cmd; + u16 opc = cmd->opcode; + + if (host->app_cmd) { + host->app_cmd = false; + opc |= USDHI6_SD_CMD_APP; + } + + if (mrq->data) { + opc |= USDHI6_SD_CMD_DATA; + + if (mrq->data->flags & MMC_DATA_READ) + opc |= USDHI6_SD_CMD_READ; + + if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || + cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || + (cmd->opcode == SD_IO_RW_EXTENDED && + mrq->data->blocks > 1)) { + opc |= USDHI6_SD_CMD_MULTI; + if (!mrq->stop) + opc |= USDHI6_SD_CMD_CMD12_AUTO_OFF; + } + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + opc |= USDHI6_SD_CMD_MODE_RSP_NONE; + break; + case MMC_RSP_R1: + opc |= USDHI6_SD_CMD_MODE_RSP_R1; + break; + case MMC_RSP_R1B: + opc |= USDHI6_SD_CMD_MODE_RSP_R1B; + break; + case MMC_RSP_R2: + opc |= USDHI6_SD_CMD_MODE_RSP_R2; + break; + case MMC_RSP_R3: + opc |= USDHI6_SD_CMD_MODE_RSP_R3; + break; + default: + dev_warn(mmc_dev(host->mmc), + "Unknown response type %d\n", + mmc_resp_type(cmd)); + return -EINVAL; + } + } + + return opc; +} + +static int usdhi6_rq_start(struct usdhi6_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + int opc = usdhi6_cmd_flags(host); + int i; + + if (opc < 0) + return opc; + + for (i = 1000; i; i--) { + if (!(usdhi6_read(host, USDHI6_SD_INFO2) & USDHI6_SD_INFO2_CBSY)) + break; + usleep_range(10, 100); + } + + if (!i) { + dev_dbg(mmc_dev(host->mmc), "Command active, request aborted\n"); + return -EAGAIN; + } + + if (data) { + bool use_dma; + int ret = 0; + + host->page_idx = 0; + + if (cmd->opcode == SD_IO_RW_EXTENDED && data->blocks > 1) { + switch (data->blksz) { + case 512: + break; + case 32: + case 64: + case 128: + case 256: + if (mrq->stop) + ret = -EINVAL; + break; + default: + ret = -EINVAL; + } + } else if ((cmd->opcode == MMC_READ_MULTIPLE_BLOCK || + cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) && + data->blksz != 512) { + ret = -EINVAL; + } + + if (ret < 0) { + dev_warn(mmc_dev(host->mmc), "%s(): %u blocks of %u bytes\n", + __func__, data->blocks, data->blksz); + return -EINVAL; + } + + if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || + cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || + (cmd->opcode == SD_IO_RW_EXTENDED && + data->blocks > 1)) + usdhi6_sg_prep(host); + + usdhi6_write(host, USDHI6_SD_SIZE, data->blksz); + + if ((data->blksz >= USDHI6_MIN_DMA || + data->blocks > 1) && + (data->blksz % 4 || + data->sg->offset % 4)) + dev_dbg(mmc_dev(host->mmc), + "Bad SG of %u: %ux%u @ %u\n", data->sg_len, + data->blksz, data->blocks, data->sg->offset); + + /* Enable DMA for USDHI6_MIN_DMA bytes or more */ + use_dma = data->blksz >= USDHI6_MIN_DMA && + !(data->blksz % 4) && + usdhi6_dma_start(host) >= DMA_MIN_COOKIE; + + if (use_dma) + usdhi6_write(host, USDHI6_CC_EXT_MODE, USDHI6_CC_EXT_MODE_SDRW); + + dev_dbg(mmc_dev(host->mmc), + "%s(): request opcode %u, %u blocks of %u bytes in %u segments, %s %s @+0x%x%s\n", + __func__, cmd->opcode, data->blocks, data->blksz, + data->sg_len, use_dma ? "DMA" : "PIO", + data->flags & MMC_DATA_READ ? "read" : "write", + data->sg->offset, mrq->stop ? " + stop" : ""); + } else { + dev_dbg(mmc_dev(host->mmc), "%s(): request opcode %u\n", + __func__, cmd->opcode); + } + + /* We have to get a command completion interrupt with DMA too */ + usdhi6_wait_for_resp(host); + + host->wait = USDHI6_WAIT_FOR_CMD; + schedule_delayed_work(&host->timeout_work, host->timeout); + + /* SEC bit is required to enable block counting by the core */ + usdhi6_write(host, USDHI6_SD_STOP, + data && data->blocks > 1 ? USDHI6_SD_STOP_SEC : 0); + usdhi6_write(host, USDHI6_SD_ARG, cmd->arg); + + /* Kick command execution */ + usdhi6_write(host, USDHI6_SD_CMD, opc); + + return 0; +} + +static void usdhi6_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct usdhi6_host *host = mmc_priv(mmc); + int ret; + + cancel_delayed_work_sync(&host->timeout_work); + + host->mrq = mrq; + host->sg = NULL; + + usdhi6_timeout_set(host); + ret = usdhi6_rq_start(host); + if (ret < 0) { + mrq->cmd->error = ret; + usdhi6_request_done(host); + } +} + +static int usdhi6_get_cd(struct mmc_host *mmc) +{ + struct usdhi6_host *host = mmc_priv(mmc); + /* Read is atomic, no need to lock */ + u32 status = usdhi6_read(host, USDHI6_SD_INFO1) & USDHI6_SD_INFO1_CD; + +/* + * level status.CD CD_ACTIVE_HIGH card present + * 1 0 0 0 + * 1 0 1 1 + * 0 1 0 1 + * 0 1 1 0 + */ + return !status ^ !(mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); +} + +static int usdhi6_get_ro(struct mmc_host *mmc) +{ + struct usdhi6_host *host = mmc_priv(mmc); + /* No locking as above */ + u32 status = usdhi6_read(host, USDHI6_SD_INFO1) & USDHI6_SD_INFO1_WP; + +/* + * level status.WP RO_ACTIVE_HIGH card read-only + * 1 0 0 0 + * 1 0 1 1 + * 0 1 0 1 + * 0 1 1 0 + */ + return !status ^ !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); +} + +static void usdhi6_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct usdhi6_host *host = mmc_priv(mmc); + + dev_dbg(mmc_dev(mmc), "%s(): %sable\n", __func__, enable ? "en" : "dis"); + + if (enable) { + host->sdio_mask = USDHI6_SDIO_INFO1_IRQ & ~USDHI6_SDIO_INFO1_IOIRQ; + usdhi6_write(host, USDHI6_SDIO_INFO1_MASK, host->sdio_mask); + usdhi6_write(host, USDHI6_SDIO_MODE, 1); + } else { + usdhi6_write(host, USDHI6_SDIO_MODE, 0); + usdhi6_write(host, USDHI6_SDIO_INFO1_MASK, USDHI6_SDIO_INFO1_IRQ); + host->sdio_mask = USDHI6_SDIO_INFO1_IRQ; + } +} + +static struct mmc_host_ops usdhi6_ops = { + .request = usdhi6_request, + .set_ios = usdhi6_set_ios, + .get_cd = usdhi6_get_cd, + .get_ro = usdhi6_get_ro, + .enable_sdio_irq = usdhi6_enable_sdio_irq, +}; + +/* State machine handlers */ + +static void usdhi6_resp_cmd12(struct usdhi6_host *host) +{ + struct mmc_command *cmd = host->mrq->stop; + cmd->resp[0] = usdhi6_read(host, USDHI6_SD_RSP10); +} + +static void usdhi6_resp_read(struct usdhi6_host *host) +{ + struct mmc_command *cmd = host->mrq->cmd; + u32 *rsp = cmd->resp, tmp = 0; + int i; + +/* + * RSP10 39-8 + * RSP32 71-40 + * RSP54 103-72 + * RSP76 127-104 + * R2-type response: + * resp[0] = r[127..96] + * resp[1] = r[95..64] + * resp[2] = r[63..32] + * resp[3] = r[31..0] + * Other responses: + * resp[0] = r[39..8] + */ + + if (mmc_resp_type(cmd) == MMC_RSP_NONE) + return; + + if (!(host->irq_status & USDHI6_SD_INFO1_RSP_END)) { + dev_err(mmc_dev(host->mmc), + "CMD%d: response expected but is missing!\n", cmd->opcode); + return; + } + + if (mmc_resp_type(cmd) & MMC_RSP_136) + for (i = 0; i < 4; i++) { + if (i) + rsp[3 - i] = tmp >> 24; + tmp = usdhi6_read(host, USDHI6_SD_RSP10 + i * 8); + rsp[3 - i] |= tmp << 8; + } + else if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || + cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) + /* Read RSP54 to avoid conflict with auto CMD12 */ + rsp[0] = usdhi6_read(host, USDHI6_SD_RSP54); + else + rsp[0] = usdhi6_read(host, USDHI6_SD_RSP10); + + dev_dbg(mmc_dev(host->mmc), "Response 0x%x\n", rsp[0]); +} + +static int usdhi6_blk_read(struct usdhi6_host *host) +{ + struct mmc_data *data = host->mrq->data; + u32 *p; + int i, rest; + + if (host->io_error) { + data->error = usdhi6_error_code(host); + goto error; + } + + if (host->pg.page) { + p = host->blk_page + host->offset; + } else { + p = usdhi6_sg_map(host); + if (!p) { + data->error = -ENOMEM; + goto error; + } + } + + for (i = 0; i < data->blksz / 4; i++, p++) + *p = usdhi6_read(host, USDHI6_SD_BUF0); + + rest = data->blksz % 4; + for (i = 0; i < (rest + 1) / 2; i++) { + u16 d = usdhi6_read16(host, USDHI6_SD_BUF0); + ((u8 *)p)[2 * i] = ((u8 *)&d)[0]; + if (rest > 1 && !i) + ((u8 *)p)[2 * i + 1] = ((u8 *)&d)[1]; + } + + return 0; + +error: + dev_dbg(mmc_dev(host->mmc), "%s(): %d\n", __func__, data->error); + host->wait = USDHI6_WAIT_FOR_REQUEST; + return data->error; +} + +static int usdhi6_blk_write(struct usdhi6_host *host) +{ + struct mmc_data *data = host->mrq->data; + u32 *p; + int i, rest; + + if (host->io_error) { + data->error = usdhi6_error_code(host); + goto error; + } + + if (host->pg.page) { + p = host->blk_page + host->offset; + } else { + p = usdhi6_sg_map(host); + if (!p) { + data->error = -ENOMEM; + goto error; + } + } + + for (i = 0; i < data->blksz / 4; i++, p++) + usdhi6_write(host, USDHI6_SD_BUF0, *p); + + rest = data->blksz % 4; + for (i = 0; i < (rest + 1) / 2; i++) { + u16 d; + ((u8 *)&d)[0] = ((u8 *)p)[2 * i]; + if (rest > 1 && !i) + ((u8 *)&d)[1] = ((u8 *)p)[2 * i + 1]; + else + ((u8 *)&d)[1] = 0; + usdhi6_write16(host, USDHI6_SD_BUF0, d); + } + + return 0; + +error: + dev_dbg(mmc_dev(host->mmc), "%s(): %d\n", __func__, data->error); + host->wait = USDHI6_WAIT_FOR_REQUEST; + return data->error; +} + +static int usdhi6_stop_cmd(struct usdhi6_host *host) +{ + struct mmc_request *mrq = host->mrq; + + switch (mrq->cmd->opcode) { + case MMC_READ_MULTIPLE_BLOCK: + case MMC_WRITE_MULTIPLE_BLOCK: + if (mrq->stop->opcode == MMC_STOP_TRANSMISSION) { + host->wait = USDHI6_WAIT_FOR_STOP; + return 0; + } + /* Unsupported STOP command */ + default: + dev_err(mmc_dev(host->mmc), + "unsupported stop CMD%d for CMD%d\n", + mrq->stop->opcode, mrq->cmd->opcode); + mrq->stop->error = -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static bool usdhi6_end_cmd(struct usdhi6_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = mrq->cmd; + + if (host->io_error) { + cmd->error = usdhi6_error_code(host); + return false; + } + + usdhi6_resp_read(host); + + if (!mrq->data) + return false; + + if (host->dma_active) { + usdhi6_dma_kick(host); + if (!mrq->stop) + host->wait = USDHI6_WAIT_FOR_DMA; + else if (usdhi6_stop_cmd(host) < 0) + return false; + } else if (mrq->data->flags & MMC_DATA_READ) { + if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || + (cmd->opcode == SD_IO_RW_EXTENDED && + mrq->data->blocks > 1)) + host->wait = USDHI6_WAIT_FOR_MREAD; + else + host->wait = USDHI6_WAIT_FOR_READ; + } else { + if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || + (cmd->opcode == SD_IO_RW_EXTENDED && + mrq->data->blocks > 1)) + host->wait = USDHI6_WAIT_FOR_MWRITE; + else + host->wait = USDHI6_WAIT_FOR_WRITE; + } + + return true; +} + +static bool usdhi6_read_block(struct usdhi6_host *host) +{ + /* ACCESS_END IRQ is already unmasked */ + int ret = usdhi6_blk_read(host); + + /* + * Have to force unmapping both pages: the single block could have been + * cross-page, in which case for single-block IO host->page_idx == 0. + * So, if we don't force, the second page won't be unmapped. + */ + usdhi6_sg_unmap(host, true); + + if (ret < 0) + return false; + + host->wait = USDHI6_WAIT_FOR_DATA_END; + return true; +} + +static bool usdhi6_mread_block(struct usdhi6_host *host) +{ + int ret = usdhi6_blk_read(host); + + if (ret < 0) + return false; + + usdhi6_sg_advance(host); + + return !host->mrq->data->error && + (host->wait != USDHI6_WAIT_FOR_DATA_END || !host->mrq->stop); +} + +static bool usdhi6_write_block(struct usdhi6_host *host) +{ + int ret = usdhi6_blk_write(host); + + /* See comment in usdhi6_read_block() */ + usdhi6_sg_unmap(host, true); + + if (ret < 0) + return false; + + host->wait = USDHI6_WAIT_FOR_DATA_END; + return true; +} + +static bool usdhi6_mwrite_block(struct usdhi6_host *host) +{ + int ret = usdhi6_blk_write(host); + + if (ret < 0) + return false; + + usdhi6_sg_advance(host); + + return !host->mrq->data->error && + (host->wait != USDHI6_WAIT_FOR_DATA_END || !host->mrq->stop); +} + +/* Interrupt & timeout handlers */ + +static irqreturn_t usdhi6_sd_bh(int irq, void *dev_id) +{ + struct usdhi6_host *host = dev_id; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + bool io_wait = false; + + cancel_delayed_work_sync(&host->timeout_work); + + mrq = host->mrq; + if (!mrq) + return IRQ_HANDLED; + + cmd = mrq->cmd; + data = mrq->data; + + switch (host->wait) { + case USDHI6_WAIT_FOR_REQUEST: + /* We're too late, the timeout has already kicked in */ + return IRQ_HANDLED; + case USDHI6_WAIT_FOR_CMD: + /* Wait for data? */ + io_wait = usdhi6_end_cmd(host); + break; + case USDHI6_WAIT_FOR_MREAD: + /* Wait for more data? */ + io_wait = usdhi6_mread_block(host); + break; + case USDHI6_WAIT_FOR_READ: + /* Wait for data end? */ + io_wait = usdhi6_read_block(host); + break; + case USDHI6_WAIT_FOR_MWRITE: + /* Wait data to write? */ + io_wait = usdhi6_mwrite_block(host); + break; + case USDHI6_WAIT_FOR_WRITE: + /* Wait for data end? */ + io_wait = usdhi6_write_block(host); + break; + case USDHI6_WAIT_FOR_DMA: + usdhi6_dma_check_error(host); + break; + case USDHI6_WAIT_FOR_STOP: + usdhi6_write(host, USDHI6_SD_STOP, 0); + if (host->io_error) { + int ret = usdhi6_error_code(host); + if (mrq->stop) + mrq->stop->error = ret; + else + mrq->data->error = ret; + dev_warn(mmc_dev(host->mmc), "%s(): %d\n", __func__, ret); + break; + } + usdhi6_resp_cmd12(host); + mrq->stop->error = 0; + break; + case USDHI6_WAIT_FOR_DATA_END: + if (host->io_error) { + mrq->data->error = usdhi6_error_code(host); + dev_warn(mmc_dev(host->mmc), "%s(): %d\n", __func__, + mrq->data->error); + } + break; + default: + cmd->error = -EFAULT; + dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait); + usdhi6_request_done(host); + return IRQ_HANDLED; + } + + if (io_wait) { + schedule_delayed_work(&host->timeout_work, host->timeout); + /* Wait for more data or ACCESS_END */ + if (!host->dma_active) + usdhi6_wait_for_brwe(host, mrq->data->flags & MMC_DATA_READ); + return IRQ_HANDLED; + } + + if (!cmd->error) { + if (data) { + if (!data->error) { + if (host->wait != USDHI6_WAIT_FOR_STOP && + host->mrq->stop && + !host->mrq->stop->error && + !usdhi6_stop_cmd(host)) { + /* Sending STOP */ + usdhi6_wait_for_resp(host); + + schedule_delayed_work(&host->timeout_work, + host->timeout); + + return IRQ_HANDLED; + } + + data->bytes_xfered = data->blocks * data->blksz; + } else { + /* Data error: might need to unmap the last page */ + dev_warn(mmc_dev(host->mmc), "%s(): data error %d\n", + __func__, data->error); + usdhi6_sg_unmap(host, true); + } + } else if (cmd->opcode == MMC_APP_CMD) { + host->app_cmd = true; + } + } + + usdhi6_request_done(host); + + return IRQ_HANDLED; +} + +static irqreturn_t usdhi6_sd(int irq, void *dev_id) +{ + struct usdhi6_host *host = dev_id; + u16 status, status2, error; + + status = usdhi6_read(host, USDHI6_SD_INFO1) & ~host->status_mask & + ~USDHI6_SD_INFO1_CARD; + status2 = usdhi6_read(host, USDHI6_SD_INFO2) & ~host->status2_mask; + + usdhi6_only_cd(host); + + dev_dbg(mmc_dev(host->mmc), + "IRQ status = 0x%08x, status2 = 0x%08x\n", status, status2); + + if (!status && !status2) + return IRQ_NONE; + + error = status2 & USDHI6_SD_INFO2_ERR; + + /* Ack / clear interrupts */ + if (USDHI6_SD_INFO1_IRQ & status) + usdhi6_write(host, USDHI6_SD_INFO1, + 0xffff & ~(USDHI6_SD_INFO1_IRQ & status)); + + if (USDHI6_SD_INFO2_IRQ & status2) { + if (error) + /* In error cases BWE and BRE aren't cleared automatically */ + status2 |= USDHI6_SD_INFO2_BWE | USDHI6_SD_INFO2_BRE; + + usdhi6_write(host, USDHI6_SD_INFO2, + 0xffff & ~(USDHI6_SD_INFO2_IRQ & status2)); + } + + host->io_error = error; + host->irq_status = status; + + if (error) { + /* Don't pollute the log with unsupported command timeouts */ + if (host->wait != USDHI6_WAIT_FOR_CMD || + error != USDHI6_SD_INFO2_RSP_TOUT) + dev_warn(mmc_dev(host->mmc), + "%s(): INFO2 error bits 0x%08x\n", + __func__, error); + else + dev_dbg(mmc_dev(host->mmc), + "%s(): INFO2 error bits 0x%08x\n", + __func__, error); + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t usdhi6_sdio(int irq, void *dev_id) +{ + struct usdhi6_host *host = dev_id; + u32 status = usdhi6_read(host, USDHI6_SDIO_INFO1) & ~host->sdio_mask; + + dev_dbg(mmc_dev(host->mmc), "%s(): status 0x%x\n", __func__, status); + + if (!status) + return IRQ_NONE; + + usdhi6_write(host, USDHI6_SDIO_INFO1, ~status); + + mmc_signal_sdio_irq(host->mmc); + + return IRQ_HANDLED; +} + +static irqreturn_t usdhi6_cd(int irq, void *dev_id) +{ + struct usdhi6_host *host = dev_id; + struct mmc_host *mmc = host->mmc; + u16 status; + + /* We're only interested in hotplug events here */ + status = usdhi6_read(host, USDHI6_SD_INFO1) & ~host->status_mask & + USDHI6_SD_INFO1_CARD; + + if (!status) + return IRQ_NONE; + + /* Ack */ + usdhi6_write(host, USDHI6_SD_INFO1, !status); + + if (!work_pending(&mmc->detect.work) && + (((status & USDHI6_SD_INFO1_CARD_INSERT) && + !mmc->card) || + ((status & USDHI6_SD_INFO1_CARD_EJECT) && + mmc->card))) + mmc_detect_change(mmc, msecs_to_jiffies(100)); + + return IRQ_HANDLED; +} + +/* + * Actually this should not be needed, if the built-in timeout works reliably in + * the both PIO cases and DMA never fails. But if DMA does fail, a timeout + * handler might be the only way to catch the error. + */ +static void usdhi6_timeout_work(struct work_struct *work) +{ + struct delayed_work *d = container_of(work, struct delayed_work, work); + struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work); + struct mmc_request *mrq = host->mrq; + struct mmc_data *data = mrq ? mrq->data : NULL; + + dev_warn(mmc_dev(host->mmc), + "%s timeout wait %u CMD%d: IRQ 0x%08x:0x%08x, last IRQ 0x%08x\n", + host->dma_active ? "DMA" : "PIO", + host->wait, mrq ? mrq->cmd->opcode : -1, + usdhi6_read(host, USDHI6_SD_INFO1), + usdhi6_read(host, USDHI6_SD_INFO2), host->irq_status); + + if (host->dma_active) { + usdhi6_dma_kill(host); + usdhi6_dma_stop_unmap(host); + } + + switch (host->wait) { + default: + dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait); + /* mrq can be NULL in this actually impossible case */ + case USDHI6_WAIT_FOR_CMD: + usdhi6_error_code(host); + if (mrq) + mrq->cmd->error = -ETIMEDOUT; + break; + case USDHI6_WAIT_FOR_STOP: + usdhi6_error_code(host); + mrq->stop->error = -ETIMEDOUT; + break; + case USDHI6_WAIT_FOR_DMA: + case USDHI6_WAIT_FOR_MREAD: + case USDHI6_WAIT_FOR_MWRITE: + case USDHI6_WAIT_FOR_READ: + case USDHI6_WAIT_FOR_WRITE: + dev_dbg(mmc_dev(host->mmc), + "%c: page #%u @ +0x%x %ux%u in SG%u. Current SG %u bytes @ %u\n", + data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx, + host->offset, data->blocks, data->blksz, data->sg_len, + sg_dma_len(host->sg), host->sg->offset); + usdhi6_sg_unmap(host, true); + /* + * If USDHI6_WAIT_FOR_DATA_END times out, we have already unmapped + * the page + */ + case USDHI6_WAIT_FOR_DATA_END: + usdhi6_error_code(host); + data->error = -ETIMEDOUT; + } + + if (mrq) + usdhi6_request_done(host); +} + +/* Probe / release */ + +static const struct of_device_id usdhi6_of_match[] = { + {.compatible = "renesas,usdhi6rol0"}, + {} +}; +MODULE_DEVICE_TABLE(of, usdhi6_of_match); + +static int usdhi6_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mmc_host *mmc; + struct usdhi6_host *host; + struct resource *res; + int irq_cd, irq_sd, irq_sdio; + u32 version; + int ret; + + if (!dev->of_node) + return -ENODEV; + + irq_cd = platform_get_irq_byname(pdev, "card detect"); + irq_sd = platform_get_irq_byname(pdev, "data"); + irq_sdio = platform_get_irq_byname(pdev, "SDIO"); + if (irq_sd < 0 || irq_sdio < 0) + return -ENODEV; + + mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev); + if (!mmc) + return -ENOMEM; + + ret = mmc_of_parse(mmc); + if (ret < 0) + goto e_free_mmc; + + mmc_regulator_get_supply(mmc); + + host = mmc_priv(mmc); + host->mmc = mmc; + host->wait = USDHI6_WAIT_FOR_REQUEST; + host->timeout = msecs_to_jiffies(4000); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_ioremap_resource(dev, res); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto e_free_mmc; + } + + host->clk = devm_clk_get(dev, NULL); + if (IS_ERR(host->clk)) + goto e_free_mmc; + + host->imclk = clk_get_rate(host->clk); + + ret = clk_prepare_enable(host->clk); + if (ret < 0) + goto e_free_mmc; + + version = usdhi6_read(host, USDHI6_VERSION); + if ((version & 0xfff) != 0xa0d) { + dev_err(dev, "Version not recognized %x\n", version); + goto e_clk_off; + } + + dev_info(dev, "A USDHI6ROL0 SD host detected with %d ports\n", + usdhi6_read(host, USDHI6_SD_PORT_SEL) >> USDHI6_SD_PORT_SEL_PORTS_SHIFT); + + usdhi6_mask_all(host); + + if (irq_cd >= 0) { + ret = devm_request_irq(dev, irq_cd, usdhi6_cd, 0, + dev_name(dev), host); + if (ret < 0) + goto e_clk_off; + } else { + mmc->caps |= MMC_CAP_NEEDS_POLL; + } + + ret = devm_request_threaded_irq(dev, irq_sd, usdhi6_sd, usdhi6_sd_bh, 0, + dev_name(dev), host); + if (ret < 0) + goto e_clk_off; + + ret = devm_request_irq(dev, irq_sdio, usdhi6_sdio, 0, + dev_name(dev), host); + if (ret < 0) + goto e_clk_off; + + INIT_DELAYED_WORK(&host->timeout_work, usdhi6_timeout_work); + + usdhi6_dma_request(host, res->start); + + mmc->ops = &usdhi6_ops; + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50 | MMC_CAP_SDIO_IRQ; + /* Set .max_segs to some random number. Feel free to adjust. */ + mmc->max_segs = 32; + mmc->max_blk_size = 512; + mmc->max_req_size = PAGE_CACHE_SIZE * mmc->max_segs; + mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size; + /* + * Setting .max_seg_size to 1 page would simplify our page-mapping code, + * But OTOH, having large segments makes DMA more efficient. We could + * check, whether we managed to get DMA and fall back to 1 page + * segments, but if we do manage to obtain DMA and then it fails at + * run-time and we fall back to PIO, we will continue getting large + * segments. So, we wouldn't be able to get rid of the code anyway. + */ + mmc->max_seg_size = mmc->max_req_size; + if (!mmc->f_max) + mmc->f_max = host->imclk; + mmc->f_min = host->imclk / 512; + + platform_set_drvdata(pdev, host); + + ret = mmc_add_host(mmc); + if (ret < 0) + goto e_clk_off; + + return 0; + +e_clk_off: + clk_disable_unprepare(host->clk); +e_free_mmc: + mmc_free_host(mmc); + + return ret; +} + +static int usdhi6_remove(struct platform_device *pdev) +{ + struct usdhi6_host *host = platform_get_drvdata(pdev); + + mmc_remove_host(host->mmc); + + usdhi6_mask_all(host); + cancel_delayed_work_sync(&host->timeout_work); + usdhi6_dma_release(host); + clk_disable_unprepare(host->clk); + mmc_free_host(host->mmc); + + return 0; +} + +static struct platform_driver usdhi6_driver = { + .probe = usdhi6_probe, + .remove = usdhi6_remove, + .driver = { + .name = "usdhi6rol0", + .owner = THIS_MODULE, + .of_match_table = usdhi6_of_match, + }, +}; + +module_platform_driver(usdhi6_driver); + +MODULE_DESCRIPTION("Renesas usdhi6rol0 SD/SDIO host driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:usdhi6rol0"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 498d1f7..282891a 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -840,7 +840,7 @@ static int wmt_mci_probe(struct platform_device *pdev) priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev, mmc->max_blk_count * 16, &priv->dma_desc_device_addr, - 208); + GFP_KERNEL); if (!priv->dma_desc_buffer) { dev_err(&pdev->dev, "DMA alloc fail\n"); ret = -EPERM; |