diff options
Diffstat (limited to 'drivers/mmc')
31 files changed, 1759 insertions, 290 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 2c71a434c915..95b41c0891d0 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -408,38 +408,6 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr, return 0; } -static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, - u32 retries_max) -{ - int err; - u32 retry_count = 0; - - if (!status || !retries_max) - return -EINVAL; - - do { - err = __mmc_send_status(card, status, 5); - if (err) - break; - - if (!R1_STATUS(*status) && - (R1_CURRENT_STATE(*status) != R1_STATE_PRG)) - break; /* RPMB programming operation complete */ - - /* - * Rechedule to give the MMC device a chance to continue - * processing the previous command without being polled too - * frequently. - */ - usleep_range(1000, 5000); - } while (++retry_count < retries_max); - - if (retry_count == retries_max) - err = -EPERM; - - return err; -} - static int ioctl_do_sanitize(struct mmc_card *card) { int err; @@ -468,6 +436,58 @@ out: return err; } +static inline bool mmc_blk_in_tran_state(u32 status) +{ + /* + * Some cards mishandle the status bits, so make sure to check both the + * busy indication and the card state. + */ + return status & R1_READY_FOR_DATA && + (R1_CURRENT_STATE(status) == R1_STATE_TRAN); +} + +static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, + u32 *resp_errs) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); + int err = 0; + u32 status; + + do { + bool done = time_after(jiffies, timeout); + + err = __mmc_send_status(card, &status, 5); + if (err) { + dev_err(mmc_dev(card->host), + "error %d requesting status\n", err); + return err; + } + + /* Accumulate any response error bits seen */ + if (resp_errs) + *resp_errs |= status; + + /* + * Timeout if the device never becomes ready for data and never + * leaves the program state. + */ + if (done) { + dev_err(mmc_dev(card->host), + "Card stuck in wrong state! %s status: %#x\n", + __func__, status); + return -ETIMEDOUT; + } + + /* + * Some cards mishandle the status bits, + * so make sure to check both the busy + * indication and the card state. + */ + } while (!mmc_blk_in_tran_state(status)); + + return err; +} + static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, struct mmc_blk_ioc_data *idata) { @@ -477,7 +497,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, struct scatterlist sg; int err; unsigned int target_part; - u32 status = 0; if (!card || !md || !idata) return -EINVAL; @@ -611,16 +630,12 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp)); - if (idata->rpmb) { + if (idata->rpmb || (cmd.flags & MMC_RSP_R1B)) { /* - * Ensure RPMB command has completed by polling CMD13 + * Ensure RPMB/R1B command has completed by polling CMD13 * "Send Status". */ - err = ioctl_rpmb_card_status_poll(card, &status, 5); - if (err) - dev_err(mmc_dev(card->host), - "%s: Card Status=0x%08X, error %d\n", - __func__, status, err); + err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, NULL); } return err; @@ -970,58 +985,6 @@ static unsigned int mmc_blk_data_timeout_ms(struct mmc_host *host, return ms; } -static inline bool mmc_blk_in_tran_state(u32 status) -{ - /* - * Some cards mishandle the status bits, so make sure to check both the - * busy indication and the card state. - */ - return status & R1_READY_FOR_DATA && - (R1_CURRENT_STATE(status) == R1_STATE_TRAN); -} - -static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, - struct request *req, u32 *resp_errs) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); - int err = 0; - u32 status; - - do { - bool done = time_after(jiffies, timeout); - - err = __mmc_send_status(card, &status, 5); - if (err) { - pr_err("%s: error %d requesting status\n", - req->rq_disk->disk_name, err); - return err; - } - - /* Accumulate any response error bits seen */ - if (resp_errs) - *resp_errs |= status; - - /* - * Timeout if the device never becomes ready for data and never - * leaves the program state. - */ - if (done) { - pr_err("%s: Card stuck in wrong state! %s %s status: %#x\n", - mmc_hostname(card->host), - req->rq_disk->disk_name, __func__, status); - return -ETIMEDOUT; - } - - /* - * Some cards mishandle the status bits, - * so make sure to check both the busy - * indication and the card state. - */ - } while (!mmc_blk_in_tran_state(status)); - - return err; -} - static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host, int type) { @@ -1671,7 +1634,7 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req) mmc_blk_send_stop(card, timeout); - err = card_busy_detect(card, timeout, req, NULL); + err = card_busy_detect(card, timeout, NULL); mmc_retune_release(card->host); @@ -1895,7 +1858,7 @@ static int mmc_blk_card_busy(struct mmc_card *card, struct request *req) if (mmc_host_is_spi(card->host) || rq_data_dir(req) == READ) return 0; - err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, req, &status); + err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, &status); /* * Do not assume data transferred correctly if there are any error bits diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 221127324709..abf8f5eb0a1c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1469,8 +1469,7 @@ void mmc_detach_bus(struct mmc_host *host) mmc_bus_put(host); } -static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, - bool cd_irq) +void _mmc_detect_change(struct mmc_host *host, unsigned long delay, bool cd_irq) { /* * If the device is configured as wakeup, we prevent a new sleep for @@ -2129,7 +2128,7 @@ int mmc_hw_reset(struct mmc_host *host) ret = host->bus_ops->hw_reset(host); mmc_bus_put(host); - if (ret) + if (ret < 0) pr_warn("%s: tried to HW reset card, got error %d\n", mmc_hostname(host), ret); @@ -2297,11 +2296,8 @@ void mmc_rescan(struct work_struct *work) mmc_bus_get(host); - /* - * if there is a _removable_ card registered, check whether it is - * still present - */ - if (host->bus_ops && !host->bus_dead && mmc_card_is_removable(host)) + /* Verify a registered card to be functional, else remove it. */ + if (host->bus_ops && !host->bus_dead) host->bus_ops->detect(host); host->detect_change = 0; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 328c78dbee66..575ac0257af2 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -70,6 +70,8 @@ void mmc_rescan(struct work_struct *work); void mmc_start_host(struct mmc_host *host); void mmc_stop_host(struct mmc_host *host); +void _mmc_detect_change(struct mmc_host *host, unsigned long delay, + bool cd_irq); int _mmc_detect_card_removed(struct mmc_host *host); int mmc_detect_card_removed(struct mmc_host *host); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 26cabd53ddc5..ebb387aa5158 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1048,9 +1048,35 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host) return ret; } +/* + * SDIO HW reset + * + * Returns 0 if the HW reset was executed synchronously, returns 1 if the HW + * reset was asynchronously scheduled, else a negative error code. + */ static int mmc_sdio_hw_reset(struct mmc_host *host) { - mmc_power_cycle(host, host->card->ocr); + struct mmc_card *card = host->card; + + /* + * In case the card is shared among multiple func drivers, reset the + * card through a rescan work. In this way it will be removed and + * re-detected, thus all func drivers becomes informed about it. + */ + if (atomic_read(&card->sdio_funcs_probed) > 1) { + if (mmc_card_removed(card)) + return 1; + host->rescan_entered = 0; + mmc_card_set_removed(card); + _mmc_detect_change(host, 0, false); + return 1; + } + + /* + * A single func driver has been probed, then let's skip the heavy + * hotplug dance above and execute the reset immediately. + */ + mmc_power_cycle(host, card->ocr); return mmc_sdio_reinit_card(host); } diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 2963e6542958..3cc928282af7 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -138,6 +138,8 @@ static int sdio_bus_probe(struct device *dev) if (ret) return ret; + atomic_inc(&func->card->sdio_funcs_probed); + /* Unbound SDIO functions are always suspended. * During probe, the function is set active and the usage count * is incremented. If the driver supports runtime PM, @@ -153,7 +155,10 @@ static int sdio_bus_probe(struct device *dev) /* Set the default block size so the driver is sure it's something * sensible. */ sdio_claim_host(func); - ret = sdio_set_block_size(func, 0); + if (mmc_card_removed(func->card)) + ret = -ENOMEDIUM; + else + ret = sdio_set_block_size(func, 0); sdio_release_host(func); if (ret) goto disable_runtimepm; @@ -165,6 +170,7 @@ static int sdio_bus_probe(struct device *dev) return 0; disable_runtimepm: + atomic_dec(&func->card->sdio_funcs_probed); if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) pm_runtime_put_noidle(dev); dev_pm_domain_detach(dev, false); @@ -181,6 +187,7 @@ static int sdio_bus_remove(struct device *dev) pm_runtime_get_sync(dev); drv->remove(func); + atomic_dec(&func->card->sdio_funcs_probed); if (func->irq_handler) { pr_warn("WARNING: driver %s did not remove its interrupt handler!\n", diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 49ea02c467bf..7cb96d663059 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -368,6 +368,17 @@ config MMC_SDHCI_F_SDH30 If unsure, say N. +config MMC_SDHCI_MILBEAUT + tristate "SDHCI support for Socionext Milbeaut Serieas using F_SDH30" + depends on MMC_SDHCI_PLTFM + depends on OF + help + This selects the Secure Digital Host Controller Interface (SDHCI) + Needed by Milbeaut SoC for MMC / SD / SDIO support. + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_IPROC tristate "SDHCI support for the BCM2835 & iProc SD/MMC Controller" depends on ARCH_BCM2835 || ARCH_BCM_IPROC || COMPILE_TEST @@ -1019,3 +1030,11 @@ config MMC_SDHCI_AM654 If you have a controller with this interface, say Y or M here. If unsure, say N. + +config MMC_OWL + tristate "Actions Semi Owl SD/MMC Host Controller support" + depends on HAS_DMA + depends on ARCH_ACTIONS || COMPILE_TEST + help + This selects support for the SD/MMC Host Controller on + Actions Semi Owl SoCs. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 11c4598e91d9..21d9089e5eda 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o +obj-$(CONFIG_MMC_SDHCI_MILBEAUT) += sdhci-milbeaut.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_SDHCI_AM654) += sdhci_am654.o obj-$(CONFIG_MMC_WBSD) += wbsd.o @@ -73,6 +74,7 @@ obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o obj-$(CONFIG_MMC_BCM2835) += bcm2835.o +obj-$(CONFIG_MMC_OWL) += owl-mmc.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 c26fbe5f2222..581b99f9e113 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2347,8 +2347,7 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot, static int atmci_configure_dma(struct atmel_mci *host) { - host->dma.chan = dma_request_slave_channel_reason(&host->pdev->dev, - "rxtx"); + host->dma.chan = dma_request_chan(&host->pdev->dev, "rxtx"); if (PTR_ERR(host->dma.chan) == -ENODEV) { struct mci_platform_data *pdata = host->pdev->dev.platform_data; diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 148414d7f0c9..99f61fd2a658 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1357,7 +1357,6 @@ static int bcm2835_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct clk *clk; - struct resource *iomem; struct bcm2835_host *host; struct mmc_host *mmc; const __be32 *regaddr_p; @@ -1373,8 +1372,7 @@ static int bcm2835_probe(struct platform_device *pdev) host->pdev = pdev; spin_lock_init(&host->lock); - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->ioaddr = devm_ioremap_resource(dev, iomem); + host->ioaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(host->ioaddr)) { ret = PTR_ERR(host->ioaddr); goto err; diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 22aded1065ae..916746c6c2c7 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -148,7 +148,6 @@ static int octeon_mmc_probe(struct platform_device *pdev) { struct device_node *cn, *node = pdev->dev.of_node; struct cvm_mmc_host *host; - struct resource *res; void __iomem *base; int mmc_irq[9]; int i, ret = 0; @@ -205,23 +204,13 @@ static int octeon_mmc_probe(struct platform_device *pdev) host->last_slot = -1; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Platform resource[0] is missing\n"); - return -ENXIO; - } - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); host->base = (void __iomem *)base; host->reg_off = 0; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&pdev->dev, "Platform resource[1] is missing\n"); - return -EINVAL; - } - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(base)) return PTR_ERR(base); host->dma_base = (void __iomem *)base; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 79c55c7b4afd..bf0048ebbda3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3441,8 +3441,8 @@ int dw_mci_runtime_resume(struct device *dev) * Restore the initial value at FIFOTH register * And Invalidate the prev_blksz with zero */ - mci_writel(host, FIFOTH, host->fifoth_val); - host->prev_blksz = 0; + mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; /* Put in max timeout */ mci_writel(host, TMOUT, 0xFFFFFFFF); diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index f816c06ef916..78383f60a3dc 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -41,6 +41,7 @@ #define JZ_REG_MMC_RESP_FIFO 0x34 #define JZ_REG_MMC_RXFIFO 0x38 #define JZ_REG_MMC_TXFIFO 0x3C +#define JZ_REG_MMC_LPM 0x40 #define JZ_REG_MMC_DMAC 0x44 #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) @@ -77,6 +78,8 @@ #define JZ_MMC_CMDAT_IO_ABORT BIT(11) #define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) +#define JZ_MMC_CMDAT_BUS_WIDTH_8BIT (BIT(10) | BIT(9)) +#define JZ_MMC_CMDAT_BUS_WIDTH_MASK (BIT(10) | BIT(9)) #define JZ_MMC_CMDAT_DMA_EN BIT(8) #define JZ_MMC_CMDAT_INIT BIT(7) #define JZ_MMC_CMDAT_BUSY BIT(6) @@ -98,12 +101,20 @@ #define JZ_MMC_DMAC_DMA_SEL BIT(1) #define JZ_MMC_DMAC_DMA_EN BIT(0) +#define JZ_MMC_LPM_DRV_RISING BIT(31) +#define JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY BIT(31) +#define JZ_MMC_LPM_DRV_RISING_1NS_DLY BIT(30) +#define JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY BIT(29) +#define JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0) + #define JZ_MMC_CLK_RATE 24000000 enum jz4740_mmc_version { JZ_MMC_JZ4740, JZ_MMC_JZ4725B, + JZ_MMC_JZ4760, JZ_MMC_JZ4780, + JZ_MMC_X1000, }; enum jz4740_mmc_state { @@ -852,6 +863,22 @@ static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) } writew(div, host->base + JZ_REG_MMC_CLKRT); + + if (real_rate > 25000000) { + if (host->version >= JZ_MMC_X1000) { + writel(JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY | + JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY | + JZ_MMC_LPM_LOW_POWER_MODE_EN, + host->base + JZ_REG_MMC_LPM); + } else if (host->version >= JZ_MMC_JZ4760) { + writel(JZ_MMC_LPM_DRV_RISING | + JZ_MMC_LPM_LOW_POWER_MODE_EN, + host->base + JZ_REG_MMC_LPM); + } else if (host->version >= JZ_MMC_JZ4725B) + writel(JZ_MMC_LPM_LOW_POWER_MODE_EN, + host->base + JZ_REG_MMC_LPM); + } + return real_rate; } @@ -895,11 +922,16 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->bus_width) { case MMC_BUS_WIDTH_1: - host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_4BIT; + host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; break; case MMC_BUS_WIDTH_4: + host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; break; + case MMC_BUS_WIDTH_8: + host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; + host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_8BIT; + break; default: break; } @@ -924,7 +956,9 @@ static const struct mmc_host_ops jz4740_mmc_ops = { static const struct of_device_id jz4740_mmc_of_match[] = { { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, { .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B }, + { .compatible = "ingenic,jz4760-mmc", .data = (void *) JZ_MMC_JZ4760 }, { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 }, + { .compatible = "ingenic,x1000-mmc", .data = (void *) JZ_MMC_X1000 }, {}, }; MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); @@ -1025,11 +1059,12 @@ static int jz4740_mmc_probe(struct platform_device* pdev) dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); goto err_release_dma; } - dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); + dev_info(&pdev->dev, "Ingenic SD/MMC card driver registered\n"); dev_info(&pdev->dev, "Using %s, %d-bit mode\n", host->use_dma ? "DMA" : "PIO", - (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1); + (mmc->caps & MMC_CAP_8_BIT_DATA) ? 8 : + ((mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1)); return 0; diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 66e354d51ee9..74c6cfbf9172 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1421,7 +1421,7 @@ static int mmc_spi_probe(struct spi_device *spi) * Index 0 is card detect * Old boardfiles were specifying 1 ms as debounce */ - status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1, NULL); + status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1000, NULL); if (status == -EPROBE_DEFER) goto fail_add_host; if (!status) { diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c37e70dbe250..40e72c30ea84 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -44,6 +44,7 @@ #define DRIVER_NAME "mmci-pl18x" static void mmci_variant_init(struct mmci_host *host); +static void ux500_variant_init(struct mmci_host *host); static void ux500v2_variant_init(struct mmci_host *host); static unsigned int fmax = 515633; @@ -184,7 +185,7 @@ static struct variant_data variant_ux500 = { .irq_pio_mask = MCI_IRQ_PIO_MASK, .start_err = MCI_STARTBITERR, .opendrain = MCI_OD, - .init = mmci_variant_init, + .init = ux500_variant_init, }; static struct variant_data variant_ux500v2 = { @@ -261,6 +262,10 @@ static struct variant_data variant_stm32_sdmmc = { .datalength_bits = 25, .datactrl_blocksz = 14, .stm32_idmabsize_mask = GENMASK(12, 5), + .busy_timeout = true, + .busy_detect = true, + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, .init = sdmmc_variant_init, }; @@ -419,7 +424,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) mmci_write_clkreg(host, clk); } -void mmci_dma_release(struct mmci_host *host) +static void mmci_dma_release(struct mmci_host *host) { if (host->ops && host->ops->dma_release) host->ops->dma_release(host); @@ -427,7 +432,7 @@ void mmci_dma_release(struct mmci_host *host) host->use_dma = false; } -void mmci_dma_setup(struct mmci_host *host) +static void mmci_dma_setup(struct mmci_host *host) { if (!host->ops || !host->ops->dma_setup) return; @@ -462,7 +467,7 @@ static int mmci_validate_data(struct mmci_host *host, return 0; } -int mmci_prep_data(struct mmci_host *host, struct mmc_data *data, bool next) +static int mmci_prep_data(struct mmci_host *host, struct mmc_data *data, bool next) { int err; @@ -478,7 +483,7 @@ int mmci_prep_data(struct mmci_host *host, struct mmc_data *data, bool next) return err; } -void mmci_unprep_data(struct mmci_host *host, struct mmc_data *data, +static void mmci_unprep_data(struct mmci_host *host, struct mmc_data *data, int err) { if (host->ops && host->ops->unprep_data) @@ -487,7 +492,7 @@ void mmci_unprep_data(struct mmci_host *host, struct mmc_data *data, data->host_cookie = 0; } -void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) +static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) { WARN_ON(data->host_cookie && data->host_cookie != host->next_cookie); @@ -495,7 +500,7 @@ void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) host->ops->get_next_data(host, data); } -int mmci_dma_start(struct mmci_host *host, unsigned int datactrl) +static int mmci_dma_start(struct mmci_host *host, unsigned int datactrl) { struct mmc_data *data = host->data; int ret; @@ -530,7 +535,7 @@ int mmci_dma_start(struct mmci_host *host, unsigned int datactrl) return 0; } -void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) +static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) { if (!host->use_dma) return; @@ -539,7 +544,7 @@ void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) host->ops->dma_finalize(host, data); } -void mmci_dma_error(struct mmci_host *host) +static void mmci_dma_error(struct mmci_host *host) { if (!host->use_dma) return; @@ -610,6 +615,67 @@ static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host) return MCI_DPSM_ENABLE | (host->data->blksz << 16); } +static bool ux500_busy_complete(struct mmci_host *host, u32 status, u32 err_msk) +{ + void __iomem *base = host->base; + + /* + * Before unmasking for the busy end IRQ, confirm that the + * command was sent successfully. To keep track of having a + * command in-progress, waiting for busy signaling to end, + * store the status in host->busy_status. + * + * Note that, the card may need a couple of clock cycles before + * it starts signaling busy on DAT0, hence re-read the + * MMCISTATUS register here, to allow the busy bit to be set. + * Potentially we may even need to poll the register for a + * while, to allow it to be set, but tests indicates that it + * isn't needed. + */ + if (!host->busy_status && !(status & err_msk) && + (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { + writel(readl(base + MMCIMASK0) | + host->variant->busy_detect_mask, + base + MMCIMASK0); + + host->busy_status = status & (MCI_CMDSENT | MCI_CMDRESPEND); + return false; + } + + /* + * If there is a command in-progress that has been successfully + * sent, then bail out if busy status is set and wait for the + * busy end IRQ. + * + * Note that, the HW triggers an IRQ on both edges while + * monitoring DAT0 for busy completion, but there is only one + * status bit in MMCISTATUS for the busy state. Therefore + * both the start and the end interrupts needs to be cleared, + * one after the other. So, clear the busy start IRQ here. + */ + if (host->busy_status && + (status & host->variant->busy_detect_flag)) { + writel(host->variant->busy_detect_mask, base + MMCICLEAR); + return false; + } + + /* + * If there is a command in-progress that has been successfully + * sent and the busy bit isn't set, it means we have received + * the busy end IRQ. Clear and mask the IRQ, then continue to + * process the command. + */ + if (host->busy_status) { + writel(host->variant->busy_detect_mask, base + MMCICLEAR); + + writel(readl(base + MMCIMASK0) & + ~host->variant->busy_detect_mask, base + MMCIMASK0); + host->busy_status = 0; + } + + return true; +} + /* * All the DMA operation mode stuff goes inside this ifdef. * This assumes that you have a generic DMA device interface, @@ -948,14 +1014,21 @@ static struct mmci_host_ops mmci_variant_ops = { }; #endif -void mmci_variant_init(struct mmci_host *host) +static void mmci_variant_init(struct mmci_host *host) +{ + host->ops = &mmci_variant_ops; +} + +static void ux500_variant_init(struct mmci_host *host) { host->ops = &mmci_variant_ops; + host->ops->busy_complete = ux500_busy_complete; } -void ux500v2_variant_init(struct mmci_host *host) +static void ux500v2_variant_init(struct mmci_host *host) { host->ops = &mmci_variant_ops; + host->ops->busy_complete = ux500_busy_complete; host->ops->get_datactrl_cfg = ux500v2_get_dctrl_cfg; } @@ -1075,6 +1148,7 @@ static void mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) { void __iomem *base = host->base; + unsigned long long clks; dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n", cmd->opcode, cmd->arg, cmd->flags); @@ -1097,6 +1171,16 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) else c |= host->variant->cmdreg_srsp; } + + if (host->variant->busy_timeout && cmd->flags & MMC_RSP_BUSY) { + if (!cmd->busy_timeout) + cmd->busy_timeout = 10 * MSEC_PER_SEC; + + clks = (unsigned long long)cmd->busy_timeout * host->cclk; + do_div(clks, MSEC_PER_SEC); + writel_relaxed(clks, host->base + MMCIDATATIMER); + } + if (/*interrupt*/0) c |= MCI_CPSM_INTERRUPT; @@ -1201,6 +1285,7 @@ static void mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { + u32 err_msk = MCI_CMDCRCFAIL | MCI_CMDTIMEOUT; void __iomem *base = host->base; bool sbc, busy_resp; @@ -1215,74 +1300,17 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, * handling. Note that we tag on any latent IRQs postponed * due to waiting for busy status. */ - if (!((status|host->busy_status) & - (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND))) + if (host->variant->busy_timeout && busy_resp) + err_msk |= MCI_DATATIMEOUT; + + if (!((status | host->busy_status) & + (err_msk | MCI_CMDSENT | MCI_CMDRESPEND))) return; /* Handle busy detection on DAT0 if the variant supports it. */ - if (busy_resp && host->variant->busy_detect) { - - /* - * Before unmasking for the busy end IRQ, confirm that the - * command was sent successfully. To keep track of having a - * command in-progress, waiting for busy signaling to end, - * store the status in host->busy_status. - * - * Note that, the card may need a couple of clock cycles before - * it starts signaling busy on DAT0, hence re-read the - * MMCISTATUS register here, to allow the busy bit to be set. - * Potentially we may even need to poll the register for a - * while, to allow it to be set, but tests indicates that it - * isn't needed. - */ - if (!host->busy_status && - !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && - (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { - - writel(readl(base + MMCIMASK0) | - host->variant->busy_detect_mask, - base + MMCIMASK0); - - host->busy_status = - status & (MCI_CMDSENT|MCI_CMDRESPEND); - return; - } - - /* - * If there is a command in-progress that has been successfully - * sent, then bail out if busy status is set and wait for the - * busy end IRQ. - * - * Note that, the HW triggers an IRQ on both edges while - * monitoring DAT0 for busy completion, but there is only one - * status bit in MMCISTATUS for the busy state. Therefore - * both the start and the end interrupts needs to be cleared, - * one after the other. So, clear the busy start IRQ here. - */ - if (host->busy_status && - (status & host->variant->busy_detect_flag)) { - writel(host->variant->busy_detect_mask, - host->base + MMCICLEAR); + if (busy_resp && host->variant->busy_detect) + if (!host->ops->busy_complete(host, status, err_msk)) return; - } - - /* - * If there is a command in-progress that has been successfully - * sent and the busy bit isn't set, it means we have received - * the busy end IRQ. Clear and mask the IRQ, then continue to - * process the command. - */ - if (host->busy_status) { - - writel(host->variant->busy_detect_mask, - host->base + MMCICLEAR); - - writel(readl(base + MMCIMASK0) & - ~host->variant->busy_detect_mask, - base + MMCIMASK0); - host->busy_status = 0; - } - } host->cmd = NULL; @@ -1290,6 +1318,9 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, cmd->error = -ETIMEDOUT; } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { cmd->error = -EILSEQ; + } else if (host->variant->busy_timeout && busy_resp && + status & MCI_DATATIMEOUT) { + cmd->error = -ETIMEDOUT; } else { cmd->resp[0] = readl(base + MMCIRESPONSE0); cmd->resp[1] = readl(base + MMCIRESPONSE1); @@ -1583,6 +1614,20 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->lock, flags); } +static void mmci_set_max_busy_timeout(struct mmc_host *mmc) +{ + struct mmci_host *host = mmc_priv(mmc); + u32 max_busy_timeout = 0; + + if (!host->variant->busy_detect) + return; + + if (host->variant->busy_timeout && mmc->actual_clock) + max_busy_timeout = ~0UL / (mmc->actual_clock / MSEC_PER_SEC); + + mmc->max_busy_timeout = max_busy_timeout; +} + static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmci_host *host = mmc_priv(mmc); @@ -1687,6 +1732,8 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else mmci_set_clkreg(host, ios->clock); + mmci_set_max_busy_timeout(mmc); + if (host->ops && host->ops->set_pwrreg) host->ops->set_pwrreg(host, pwr); else @@ -1957,7 +2004,6 @@ static int mmci_probe(struct amba_device *dev, mmci_write_datactrlreg(host, host->variant->busy_dpsm_flag); mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; - mmc->max_busy_timeout = 0; } /* Prepare a CMD12 - needed to clear the DPSM on some variants. */ diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 833236ecb31e..158e1231aa23 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -164,6 +164,7 @@ #define MCI_ST_CARDBUSY (1 << 24) /* Extended status bits for the STM32 variants */ #define MCI_STM32_BUSYD0 BIT(20) +#define MCI_STM32_BUSYD0END BIT(21) #define MMCICLEAR 0x038 #define MCI_CMDCRCFAILCLR (1 << 0) @@ -287,6 +288,8 @@ struct mmci_host; * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock * @busy_detect: true if the variant supports busy detection on DAT0. + * @busy_timeout: true if the variant starts data timer when the DPSM + * enter in Wait_R or Busy state. * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register * indicating that the card is busy @@ -333,6 +336,7 @@ struct variant_data { u8 signal_direction:1; u8 pwrreg_clkgate:1; u8 busy_detect:1; + u8 busy_timeout:1; u32 busy_dpsm_flag; u32 busy_detect_flag; u32 busy_detect_mask; @@ -366,6 +370,7 @@ struct mmci_host_ops { void (*dma_error)(struct mmci_host *host); void (*set_clkreg)(struct mmci_host *host, unsigned int desired); void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr); + bool (*busy_complete)(struct mmci_host *host, u32 status, u32 err_msk); }; struct mmci_host { diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index 8e83ae6920ae..a4f7e8e689d3 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -25,8 +25,8 @@ struct sdmmc_priv { void *sg_cpu; }; -int sdmmc_idma_validate_data(struct mmci_host *host, - struct mmc_data *data) +static int sdmmc_idma_validate_data(struct mmci_host *host, + struct mmc_data *data) { struct scatterlist *sg; int i; @@ -282,6 +282,47 @@ static u32 sdmmc_get_dctrl_cfg(struct mmci_host *host) return datactrl; } +static bool sdmmc_busy_complete(struct mmci_host *host, u32 status, u32 err_msk) +{ + void __iomem *base = host->base; + u32 busy_d0, busy_d0end, mask, sdmmc_status; + + mask = readl_relaxed(base + MMCIMASK0); + sdmmc_status = readl_relaxed(base + MMCISTATUS); + busy_d0end = sdmmc_status & MCI_STM32_BUSYD0END; + busy_d0 = sdmmc_status & MCI_STM32_BUSYD0; + + /* complete if there is an error or busy_d0end */ + if ((status & err_msk) || busy_d0end) + goto complete; + + /* + * On response the busy signaling is reflected in the BUSYD0 flag. + * if busy_d0 is in-progress we must activate busyd0end interrupt + * to wait this completion. Else this request has no busy step. + */ + if (busy_d0) { + if (!host->busy_status) { + writel_relaxed(mask | host->variant->busy_detect_mask, + base + MMCIMASK0); + host->busy_status = status & + (MCI_CMDSENT | MCI_CMDRESPEND); + } + return false; + } + +complete: + if (host->busy_status) { + writel_relaxed(mask & ~host->variant->busy_detect_mask, + base + MMCIMASK0); + writel_relaxed(host->variant->busy_detect_mask, + base + MMCICLEAR); + host->busy_status = 0; + } + + return true; +} + static struct mmci_host_ops sdmmc_variant_ops = { .validate_data = sdmmc_idma_validate_data, .prep_data = sdmmc_idma_prep_data, @@ -292,6 +333,7 @@ static struct mmci_host_ops sdmmc_variant_ops = { .dma_finalize = sdmmc_idma_finalize, .set_clkreg = mmci_sdmmc_set_clkreg, .set_pwrreg = mmci_sdmmc_set_pwrreg, + .busy_complete = sdmmc_busy_complete, }; void sdmmc_variant_init(struct mmci_host *host) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index a0670e9cd012..fc6b9cf27d0b 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -608,8 +608,8 @@ static int moxart_probe(struct platform_device *pdev) 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 = dma_request_slave_channel_reason(dev, "tx"); - host->dma_chan_rx = dma_request_slave_channel_reason(dev, "rx"); + host->dma_chan_tx = dma_request_chan(dev, "tx"); + host->dma_chan_rx = dma_request_chan(dev, "rx"); spin_lock_init(&host->lock); diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c new file mode 100644 index 000000000000..771e3d00f1bb --- /dev/null +++ b/drivers/mmc/host/owl-mmc.c @@ -0,0 +1,696 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Actions Semi Owl SoCs SD/MMC driver + * + * Copyright (c) 2014 Actions Semi Inc. + * Copyright (c) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + * + * TODO: SDIO support + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/mmc/host.h> +#include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/reset.h> +#include <linux/spinlock.h> + +/* + * SDC registers + */ +#define OWL_REG_SD_EN 0x0000 +#define OWL_REG_SD_CTL 0x0004 +#define OWL_REG_SD_STATE 0x0008 +#define OWL_REG_SD_CMD 0x000c +#define OWL_REG_SD_ARG 0x0010 +#define OWL_REG_SD_RSPBUF0 0x0014 +#define OWL_REG_SD_RSPBUF1 0x0018 +#define OWL_REG_SD_RSPBUF2 0x001c +#define OWL_REG_SD_RSPBUF3 0x0020 +#define OWL_REG_SD_RSPBUF4 0x0024 +#define OWL_REG_SD_DAT 0x0028 +#define OWL_REG_SD_BLK_SIZE 0x002c +#define OWL_REG_SD_BLK_NUM 0x0030 +#define OWL_REG_SD_BUF_SIZE 0x0034 + +/* SD_EN Bits */ +#define OWL_SD_EN_RANE BIT(31) +#define OWL_SD_EN_RAN_SEED(x) (((x) & 0x3f) << 24) +#define OWL_SD_EN_S18EN BIT(12) +#define OWL_SD_EN_RESE BIT(10) +#define OWL_SD_EN_DAT1_S BIT(9) +#define OWL_SD_EN_CLK_S BIT(8) +#define OWL_SD_ENABLE BIT(7) +#define OWL_SD_EN_BSEL BIT(6) +#define OWL_SD_EN_SDIOEN BIT(3) +#define OWL_SD_EN_DDREN BIT(2) +#define OWL_SD_EN_DATAWID(x) (((x) & 0x3) << 0) + +/* SD_CTL Bits */ +#define OWL_SD_CTL_TOUTEN BIT(31) +#define OWL_SD_CTL_TOUTCNT(x) (((x) & 0x7f) << 24) +#define OWL_SD_CTL_DELAY_MSK GENMASK(23, 16) +#define OWL_SD_CTL_RDELAY(x) (((x) & 0xf) << 20) +#define OWL_SD_CTL_WDELAY(x) (((x) & 0xf) << 16) +#define OWL_SD_CTL_CMDLEN BIT(13) +#define OWL_SD_CTL_SCC BIT(12) +#define OWL_SD_CTL_TCN(x) (((x) & 0xf) << 8) +#define OWL_SD_CTL_TS BIT(7) +#define OWL_SD_CTL_LBE BIT(6) +#define OWL_SD_CTL_C7EN BIT(5) +#define OWL_SD_CTL_TM(x) (((x) & 0xf) << 0) + +#define OWL_SD_DELAY_LOW_CLK 0x0f +#define OWL_SD_DELAY_MID_CLK 0x0a +#define OWL_SD_DELAY_HIGH_CLK 0x09 +#define OWL_SD_RDELAY_DDR50 0x0a +#define OWL_SD_WDELAY_DDR50 0x08 + +/* SD_STATE Bits */ +#define OWL_SD_STATE_DAT1BS BIT(18) +#define OWL_SD_STATE_SDIOB_P BIT(17) +#define OWL_SD_STATE_SDIOB_EN BIT(16) +#define OWL_SD_STATE_TOUTE BIT(15) +#define OWL_SD_STATE_BAEP BIT(14) +#define OWL_SD_STATE_MEMRDY BIT(12) +#define OWL_SD_STATE_CMDS BIT(11) +#define OWL_SD_STATE_DAT1AS BIT(10) +#define OWL_SD_STATE_SDIOA_P BIT(9) +#define OWL_SD_STATE_SDIOA_EN BIT(8) +#define OWL_SD_STATE_DAT0S BIT(7) +#define OWL_SD_STATE_TEIE BIT(6) +#define OWL_SD_STATE_TEI BIT(5) +#define OWL_SD_STATE_CLNR BIT(4) +#define OWL_SD_STATE_CLC BIT(3) +#define OWL_SD_STATE_WC16ER BIT(2) +#define OWL_SD_STATE_RC16ER BIT(1) +#define OWL_SD_STATE_CRC7ER BIT(0) + +struct owl_mmc_host { + struct device *dev; + struct reset_control *reset; + void __iomem *base; + struct clk *clk; + struct completion sdc_complete; + spinlock_t lock; + int irq; + u32 clock; + bool ddr_50; + + enum dma_data_direction dma_dir; + struct dma_chan *dma; + struct dma_async_tx_descriptor *desc; + struct dma_slave_config dma_cfg; + struct completion dma_complete; + + struct mmc_host *mmc; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; +}; + +static void owl_mmc_update_reg(void __iomem *reg, unsigned int val, bool state) +{ + unsigned int regval; + + regval = readl(reg); + + if (state) + regval |= val; + else + regval &= ~val; + + writel(regval, reg); +} + +static irqreturn_t owl_irq_handler(int irq, void *devid) +{ + struct owl_mmc_host *owl_host = devid; + unsigned long flags; + u32 state; + + spin_lock_irqsave(&owl_host->lock, flags); + + state = readl(owl_host->base + OWL_REG_SD_STATE); + if (state & OWL_SD_STATE_TEI) { + state = readl(owl_host->base + OWL_REG_SD_STATE); + state |= OWL_SD_STATE_TEI; + writel(state, owl_host->base + OWL_REG_SD_STATE); + complete(&owl_host->sdc_complete); + } + + spin_unlock_irqrestore(&owl_host->lock, flags); + + return IRQ_HANDLED; +} + +static void owl_mmc_finish_request(struct owl_mmc_host *owl_host) +{ + struct mmc_request *mrq = owl_host->mrq; + struct mmc_data *data = mrq->data; + + /* Should never be NULL */ + WARN_ON(!mrq); + + owl_host->mrq = NULL; + + if (data) + dma_unmap_sg(owl_host->dma->device->dev, data->sg, data->sg_len, + owl_host->dma_dir); + + /* Finally finish request */ + mmc_request_done(owl_host->mmc, mrq); +} + +static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host, + struct mmc_command *cmd, + struct mmc_data *data) +{ + u32 mode, state, resp[2]; + u32 cmd_rsp_mask = 0; + + init_completion(&owl_host->sdc_complete); + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + mode = OWL_SD_CTL_TM(0); + break; + + case MMC_RSP_R1: + if (data) { + if (data->flags & MMC_DATA_READ) + mode = OWL_SD_CTL_TM(4); + else + mode = OWL_SD_CTL_TM(5); + } else { + mode = OWL_SD_CTL_TM(1); + } + cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER; + + break; + + case MMC_RSP_R1B: + mode = OWL_SD_CTL_TM(3); + cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER; + break; + + case MMC_RSP_R2: + mode = OWL_SD_CTL_TM(2); + cmd_rsp_mask = OWL_SD_STATE_CLNR | OWL_SD_STATE_CRC7ER; + break; + + case MMC_RSP_R3: + mode = OWL_SD_CTL_TM(1); + cmd_rsp_mask = OWL_SD_STATE_CLNR; + break; + + default: + dev_warn(owl_host->dev, "Unknown MMC command\n"); + cmd->error = -EINVAL; + return; + } + + /* Keep current WDELAY and RDELAY */ + mode |= (readl(owl_host->base + OWL_REG_SD_CTL) & (0xff << 16)); + + /* Start to send corresponding command type */ + writel(cmd->arg, owl_host->base + OWL_REG_SD_ARG); + writel(cmd->opcode, owl_host->base + OWL_REG_SD_CMD); + + /* Set LBE to send clk at the end of last read block */ + if (data) { + mode |= (OWL_SD_CTL_TS | OWL_SD_CTL_LBE | 0x64000000); + } else { + mode &= ~(OWL_SD_CTL_TOUTEN | OWL_SD_CTL_LBE); + mode |= OWL_SD_CTL_TS; + } + + owl_host->cmd = cmd; + + /* Start transfer */ + writel(mode, owl_host->base + OWL_REG_SD_CTL); + + if (data) + return; + + if (!wait_for_completion_timeout(&owl_host->sdc_complete, 30 * HZ)) { + dev_err(owl_host->dev, "CMD interrupt timeout\n"); + cmd->error = -ETIMEDOUT; + return; + } + + state = readl(owl_host->base + OWL_REG_SD_STATE); + if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) { + if (cmd_rsp_mask & state) { + if (state & OWL_SD_STATE_CLNR) { + dev_err(owl_host->dev, "Error CMD_NO_RSP\n"); + cmd->error = -EILSEQ; + return; + } + + if (state & OWL_SD_STATE_CRC7ER) { + dev_err(owl_host->dev, "Error CMD_RSP_CRC\n"); + cmd->error = -EILSEQ; + return; + } + } + + if (mmc_resp_type(cmd) & MMC_RSP_136) { + cmd->resp[3] = readl(owl_host->base + OWL_REG_SD_RSPBUF0); + cmd->resp[2] = readl(owl_host->base + OWL_REG_SD_RSPBUF1); + cmd->resp[1] = readl(owl_host->base + OWL_REG_SD_RSPBUF2); + cmd->resp[0] = readl(owl_host->base + OWL_REG_SD_RSPBUF3); + } else { + resp[0] = readl(owl_host->base + OWL_REG_SD_RSPBUF0); + resp[1] = readl(owl_host->base + OWL_REG_SD_RSPBUF1); + cmd->resp[0] = resp[1] << 24 | resp[0] >> 8; + cmd->resp[1] = resp[1] >> 8; + } + } +} + +static void owl_mmc_dma_complete(void *param) +{ + struct owl_mmc_host *owl_host = param; + struct mmc_data *data = owl_host->data; + + if (data) + complete(&owl_host->dma_complete); +} + +static int owl_mmc_prepare_data(struct owl_mmc_host *owl_host, + struct mmc_data *data) +{ + u32 total; + + owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, OWL_SD_EN_BSEL, + true); + writel(data->blocks, owl_host->base + OWL_REG_SD_BLK_NUM); + writel(data->blksz, owl_host->base + OWL_REG_SD_BLK_SIZE); + total = data->blksz * data->blocks; + + if (total < 512) + writel(total, owl_host->base + OWL_REG_SD_BUF_SIZE); + else + writel(512, owl_host->base + OWL_REG_SD_BUF_SIZE); + + if (data->flags & MMC_DATA_WRITE) { + owl_host->dma_dir = DMA_TO_DEVICE; + owl_host->dma_cfg.direction = DMA_MEM_TO_DEV; + } else { + owl_host->dma_dir = DMA_FROM_DEVICE; + owl_host->dma_cfg.direction = DMA_DEV_TO_MEM; + } + + dma_map_sg(owl_host->dma->device->dev, data->sg, + data->sg_len, owl_host->dma_dir); + + dmaengine_slave_config(owl_host->dma, &owl_host->dma_cfg); + owl_host->desc = dmaengine_prep_slave_sg(owl_host->dma, data->sg, + data->sg_len, + owl_host->dma_cfg.direction, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!owl_host->desc) { + dev_err(owl_host->dev, "Can't prepare slave sg\n"); + return -EBUSY; + } + + owl_host->data = data; + + owl_host->desc->callback = owl_mmc_dma_complete; + owl_host->desc->callback_param = (void *)owl_host; + data->error = 0; + + return 0; +} + +static void owl_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct owl_mmc_host *owl_host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + int ret; + + owl_host->mrq = mrq; + if (mrq->data) { + ret = owl_mmc_prepare_data(owl_host, data); + if (ret < 0) { + data->error = ret; + goto err_out; + } + + init_completion(&owl_host->dma_complete); + dmaengine_submit(owl_host->desc); + dma_async_issue_pending(owl_host->dma); + } + + owl_mmc_send_cmd(owl_host, mrq->cmd, data); + + if (data) { + if (!wait_for_completion_timeout(&owl_host->sdc_complete, + 10 * HZ)) { + dev_err(owl_host->dev, "CMD interrupt timeout\n"); + mrq->cmd->error = -ETIMEDOUT; + dmaengine_terminate_all(owl_host->dma); + goto err_out; + } + + if (!wait_for_completion_timeout(&owl_host->dma_complete, + 5 * HZ)) { + dev_err(owl_host->dev, "DMA interrupt timeout\n"); + mrq->cmd->error = -ETIMEDOUT; + dmaengine_terminate_all(owl_host->dma); + goto err_out; + } + + if (data->stop) + owl_mmc_send_cmd(owl_host, data->stop, NULL); + + data->bytes_xfered = data->blocks * data->blksz; + } + +err_out: + owl_mmc_finish_request(owl_host); +} + +static int owl_mmc_set_clk_rate(struct owl_mmc_host *owl_host, + unsigned int rate) +{ + unsigned long clk_rate; + int ret; + u32 reg; + + reg = readl(owl_host->base + OWL_REG_SD_CTL); + reg &= ~OWL_SD_CTL_DELAY_MSK; + + /* Set RDELAY and WDELAY based on the clock */ + if (rate <= 1000000) { + writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_LOW_CLK) | + OWL_SD_CTL_WDELAY(OWL_SD_DELAY_LOW_CLK), + owl_host->base + OWL_REG_SD_CTL); + } else if ((rate > 1000000) && (rate <= 26000000)) { + writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_MID_CLK) | + OWL_SD_CTL_WDELAY(OWL_SD_DELAY_MID_CLK), + owl_host->base + OWL_REG_SD_CTL); + } else if ((rate > 26000000) && (rate <= 52000000) && !owl_host->ddr_50) { + writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_DELAY_HIGH_CLK) | + OWL_SD_CTL_WDELAY(OWL_SD_DELAY_HIGH_CLK), + owl_host->base + OWL_REG_SD_CTL); + /* DDR50 mode has special delay chain */ + } else if ((rate > 26000000) && (rate <= 52000000) && owl_host->ddr_50) { + writel(reg | OWL_SD_CTL_RDELAY(OWL_SD_RDELAY_DDR50) | + OWL_SD_CTL_WDELAY(OWL_SD_WDELAY_DDR50), + owl_host->base + OWL_REG_SD_CTL); + } else { + dev_err(owl_host->dev, "SD clock rate not supported\n"); + return -EINVAL; + } + + clk_rate = clk_round_rate(owl_host->clk, rate << 1); + ret = clk_set_rate(owl_host->clk, clk_rate); + + return ret; +} + +static void owl_mmc_set_clk(struct owl_mmc_host *owl_host, struct mmc_ios *ios) +{ + if (!ios->clock) + return; + + owl_host->clock = ios->clock; + owl_mmc_set_clk_rate(owl_host, ios->clock); +} + +static void owl_mmc_set_bus_width(struct owl_mmc_host *owl_host, + struct mmc_ios *ios) +{ + u32 reg; + + reg = readl(owl_host->base + OWL_REG_SD_EN); + reg &= ~0x03; + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + break; + case MMC_BUS_WIDTH_4: + reg |= OWL_SD_EN_DATAWID(1); + break; + case MMC_BUS_WIDTH_8: + reg |= OWL_SD_EN_DATAWID(2); + break; + } + + writel(reg, owl_host->base + OWL_REG_SD_EN); +} + +static void owl_mmc_ctr_reset(struct owl_mmc_host *owl_host) +{ + reset_control_assert(owl_host->reset); + udelay(20); + reset_control_deassert(owl_host->reset); +} + +static void owl_mmc_power_on(struct owl_mmc_host *owl_host) +{ + u32 mode; + + init_completion(&owl_host->sdc_complete); + + /* Enable transfer end IRQ */ + owl_mmc_update_reg(owl_host->base + OWL_REG_SD_STATE, + OWL_SD_STATE_TEIE, true); + + /* Send init clk */ + mode = (readl(owl_host->base + OWL_REG_SD_CTL) & (0xff << 16)); + mode |= OWL_SD_CTL_TS | OWL_SD_CTL_TCN(5) | OWL_SD_CTL_TM(8); + writel(mode, owl_host->base + OWL_REG_SD_CTL); + + if (!wait_for_completion_timeout(&owl_host->sdc_complete, HZ)) { + dev_err(owl_host->dev, "CMD interrupt timeout\n"); + return; + } +} + +static void owl_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct owl_mmc_host *owl_host = mmc_priv(mmc); + + switch (ios->power_mode) { + case MMC_POWER_UP: + dev_dbg(owl_host->dev, "Powering card up\n"); + + /* Reset the SDC controller to clear all previous states */ + owl_mmc_ctr_reset(owl_host); + clk_prepare_enable(owl_host->clk); + writel(OWL_SD_ENABLE | OWL_SD_EN_RESE, + owl_host->base + OWL_REG_SD_EN); + + break; + + case MMC_POWER_ON: + dev_dbg(owl_host->dev, "Powering card on\n"); + owl_mmc_power_on(owl_host); + + break; + + case MMC_POWER_OFF: + dev_dbg(owl_host->dev, "Powering card off\n"); + clk_disable_unprepare(owl_host->clk); + + return; + + default: + dev_dbg(owl_host->dev, "Ignoring unknown card power state\n"); + break; + } + + if (ios->clock != owl_host->clock) + owl_mmc_set_clk(owl_host, ios); + + owl_mmc_set_bus_width(owl_host, ios); + + /* Enable DDR mode if requested */ + if (ios->timing == MMC_TIMING_UHS_DDR50) { + owl_host->ddr_50 = 1; + owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, + OWL_SD_EN_DDREN, true); + } else { + owl_host->ddr_50 = 0; + } +} + +static int owl_mmc_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct owl_mmc_host *owl_host = mmc_priv(mmc); + + /* It is enough to change the pad ctrl bit for voltage switch */ + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, + OWL_SD_EN_S18EN, false); + break; + case MMC_SIGNAL_VOLTAGE_180: + owl_mmc_update_reg(owl_host->base + OWL_REG_SD_EN, + OWL_SD_EN_S18EN, true); + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +static const struct mmc_host_ops owl_mmc_ops = { + .request = owl_mmc_request, + .set_ios = owl_mmc_set_ios, + .get_ro = mmc_gpio_get_ro, + .get_cd = mmc_gpio_get_cd, + .start_signal_voltage_switch = owl_mmc_start_signal_voltage_switch, +}; + +static int owl_mmc_probe(struct platform_device *pdev) +{ + struct owl_mmc_host *owl_host; + struct mmc_host *mmc; + struct resource *res; + int ret; + + mmc = mmc_alloc_host(sizeof(struct owl_mmc_host), &pdev->dev); + if (!mmc) { + dev_err(&pdev->dev, "mmc alloc host failed\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, mmc); + + owl_host = mmc_priv(mmc); + owl_host->dev = &pdev->dev; + owl_host->mmc = mmc; + spin_lock_init(&owl_host->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + owl_host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(owl_host->base)) { + dev_err(&pdev->dev, "Failed to remap registers\n"); + ret = PTR_ERR(owl_host->base); + goto err_free_host; + } + + owl_host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(owl_host->clk)) { + dev_err(&pdev->dev, "No clock defined\n"); + ret = PTR_ERR(owl_host->clk); + goto err_free_host; + } + + owl_host->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(owl_host->reset)) { + dev_err(&pdev->dev, "Could not get reset control\n"); + ret = PTR_ERR(owl_host->reset); + goto err_free_host; + } + + mmc->ops = &owl_mmc_ops; + mmc->max_blk_count = 512; + mmc->max_blk_size = 512; + mmc->max_segs = 256; + mmc->max_seg_size = 262144; + mmc->max_req_size = 262144; + /* 100kHz ~ 52MHz */ + mmc->f_min = 100000; + mmc->f_max = 52000000; + mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | + MMC_CAP_4_BIT_DATA; + mmc->caps2 = (MMC_CAP2_BOOTPART_NOACC | MMC_CAP2_NO_SDIO); + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | + MMC_VDD_165_195; + + ret = mmc_of_parse(mmc); + if (ret) + goto err_free_host; + + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + owl_host->dma = dma_request_slave_channel(&pdev->dev, "mmc"); + if (!owl_host->dma) { + dev_err(owl_host->dev, "Failed to get external DMA channel.\n"); + ret = -ENXIO; + goto err_free_host; + } + + dev_info(&pdev->dev, "Using %s for DMA transfers\n", + dma_chan_name(owl_host->dma)); + + owl_host->dma_cfg.src_addr = res->start + OWL_REG_SD_DAT; + owl_host->dma_cfg.dst_addr = res->start + OWL_REG_SD_DAT; + owl_host->dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + owl_host->dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + owl_host->dma_cfg.device_fc = false; + + owl_host->irq = platform_get_irq(pdev, 0); + if (owl_host->irq < 0) { + ret = -EINVAL; + goto err_free_host; + } + + ret = devm_request_irq(&pdev->dev, owl_host->irq, owl_irq_handler, + 0, dev_name(&pdev->dev), owl_host); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %d\n", + owl_host->irq); + goto err_free_host; + } + + ret = mmc_add_host(mmc); + if (ret) { + dev_err(&pdev->dev, "Failed to add host\n"); + goto err_free_host; + } + + dev_dbg(&pdev->dev, "Owl MMC Controller Initialized\n"); + + return 0; + +err_free_host: + mmc_free_host(mmc); + + return ret; +} + +static int owl_mmc_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct owl_mmc_host *owl_host = mmc_priv(mmc); + + mmc_remove_host(mmc); + disable_irq(owl_host->irq); + mmc_free_host(mmc); + + return 0; +} + +static const struct of_device_id owl_mmc_of_match[] = { + {.compatible = "actions,owl-mmc",}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, owl_mmc_of_match); + +static struct platform_driver owl_mmc_driver = { + .driver = { + .name = "owl_mmc", + .of_match_table = of_match_ptr(owl_mmc_of_match), + }, + .probe = owl_mmc_probe, + .remove = owl_mmc_remove, +}; +module_platform_driver(owl_mmc_driver); + +MODULE_DESCRIPTION("Actions Semi Owl SoCs SD/MMC Driver"); +MODULE_AUTHOR("Actions Semi"); +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index a66f8d6d61d1..18839a10594c 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -308,6 +308,7 @@ static const struct soc_device_attribute soc_whitelist[] = { .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, /* generic ones */ { .soc_id = "r8a774a1" }, + { .soc_id = "r8a774b1" }, { .soc_id = "r8a774c0" }, { .soc_id = "r8a77470" }, { .soc_id = "r8a7795" }, diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 57b582bf73d9..9289bb4d633e 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -51,6 +51,11 @@ #define ESDHC_CLOCK_HCKEN 0x00000002 #define ESDHC_CLOCK_IPGEN 0x00000001 +/* System Control 2 Register */ +#define ESDHC_SYSTEM_CONTROL_2 0x3c +#define ESDHC_SMPCLKSEL 0x00800000 +#define ESDHC_EXTN 0x00400000 + /* Host Controller Capabilities Register 2 */ #define ESDHC_CAPABILITIES_1 0x114 @@ -59,7 +64,16 @@ #define ESDHC_HS400_WNDW_ADJUST 0x00000040 #define ESDHC_HS400_MODE 0x00000010 #define ESDHC_TB_EN 0x00000004 +#define ESDHC_TB_MODE_MASK 0x00000003 +#define ESDHC_TB_MODE_SW 0x00000003 +#define ESDHC_TB_MODE_3 0x00000002 + +#define ESDHC_TBSTAT 0x124 + #define ESDHC_TBPTR 0x128 +#define ESDHC_WNDW_STRT_PTR_SHIFT 8 +#define ESDHC_WNDW_STRT_PTR_MASK (0x7f << 8) +#define ESDHC_WNDW_END_PTR_MASK 0x7f /* SD Clock Control Register */ #define ESDHC_SDCLKCTL 0x144 diff --git a/drivers/mmc/host/sdhci-milbeaut.c b/drivers/mmc/host/sdhci-milbeaut.c new file mode 100644 index 000000000000..a1aa21b9ae1c --- /dev/null +++ b/drivers/mmc/host/sdhci-milbeaut.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd + * Vincent Yang <vincent.yang@tw.fujitsu.com> + * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org> + * Copyright (C) 2019 Socionext Inc. + * Takao Orito <orito.takao@socionext.com> + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/property.h> + +#include "sdhci-pltfm.h" +#include "sdhci_f_sdh30.h" + +/* milbeaut bridge controller register */ +#define MLB_SOFT_RESET 0x0200 +#define MLB_SOFT_RESET_RSTX BIT(0) + +#define MLB_WP_CD_LED_SET 0x0210 +#define MLB_WP_CD_LED_SET_LED_INV BIT(2) + +#define MLB_CR_SET 0x0220 +#define MLB_CR_SET_CR_TOCLKUNIT BIT(24) +#define MLB_CR_SET_CR_TOCLKFREQ_SFT (16) +#define MLB_CR_SET_CR_TOCLKFREQ_MASK (0x3F << MLB_CR_SET_CR_TOCLKFREQ_SFT) +#define MLB_CR_SET_CR_BCLKFREQ_SFT (8) +#define MLB_CR_SET_CR_BCLKFREQ_MASK (0xFF << MLB_CR_SET_CR_BCLKFREQ_SFT) +#define MLB_CR_SET_CR_RTUNTIMER_SFT (4) +#define MLB_CR_SET_CR_RTUNTIMER_MASK (0xF << MLB_CR_SET_CR_RTUNTIMER_SFT) + +#define MLB_SD_TOCLK_I_DIV 16 +#define MLB_TOCLKFREQ_UNIT_THRES 16000000 +#define MLB_CAL_TOCLKFREQ_MHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000000) +#define MLB_CAL_TOCLKFREQ_KHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000) +#define MLB_TOCLKFREQ_MAX 63 +#define MLB_TOCLKFREQ_MIN 1 + +#define MLB_SD_BCLK_I_DIV 4 +#define MLB_CAL_BCLKFREQ(rate) (rate / MLB_SD_BCLK_I_DIV / 1000000) +#define MLB_BCLKFREQ_MAX 255 +#define MLB_BCLKFREQ_MIN 1 + +#define MLB_CDR_SET 0x0230 +#define MLB_CDR_SET_CLK2POW16 3 + +struct f_sdhost_priv { + struct clk *clk_iface; + struct clk *clk; + struct device *dev; + bool enable_cmd_dat_delay; +}; + +static void sdhci_milbeaut_soft_voltage_switch(struct sdhci_host *host) +{ + u32 ctrl = 0; + + usleep_range(2500, 3000); + ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2); + ctrl |= F_SDH30_CRES_O_DN; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + ctrl |= F_SDH30_MSEL_O_1_8; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + + ctrl &= ~F_SDH30_CRES_O_DN; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + usleep_range(2500, 3000); + + ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING); + ctrl |= F_SDH30_CMD_CHK_DIS; + sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING); +} + +static unsigned int sdhci_milbeaut_get_min_clock(struct sdhci_host *host) +{ + return F_SDH30_MIN_CLOCK; +} + +static void sdhci_milbeaut_reset(struct sdhci_host *host, u8 mask) +{ + struct f_sdhost_priv *priv = sdhci_priv(host); + u16 clk; + u32 ctl; + ktime_t timeout; + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk = (clk & ~SDHCI_CLOCK_CARD_EN) | SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + sdhci_reset(host, mask); + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + timeout = ktime_add_ms(ktime_get(), 10); + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (clk & SDHCI_CLOCK_INT_STABLE) + break; + if (timedout) { + pr_err("%s: Internal clock never stabilised.\n", + mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + return; + } + udelay(10); + } + + if (priv->enable_cmd_dat_delay) { + ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL); + ctl |= F_SDH30_CMD_DAT_DELAY; + sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL); + } +} + +static void sdhci_milbeaut_set_power(struct sdhci_host *host, + unsigned char mode, unsigned short vdd) +{ + if (!IS_ERR(host->mmc->supply.vmmc)) { + struct mmc_host *mmc = host->mmc; + + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + } + sdhci_set_power_noreg(host, mode, vdd); +} + +static const struct sdhci_ops sdhci_milbeaut_ops = { + .voltage_switch = sdhci_milbeaut_soft_voltage_switch, + .get_min_clock = sdhci_milbeaut_get_min_clock, + .reset = sdhci_milbeaut_reset, + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_power = sdhci_milbeaut_set_power, +}; + +static void sdhci_milbeaut_bridge_reset(struct sdhci_host *host, + int reset_flag) +{ + if (reset_flag) + sdhci_writel(host, 0, MLB_SOFT_RESET); + else + sdhci_writel(host, MLB_SOFT_RESET_RSTX, MLB_SOFT_RESET); +} + +static void sdhci_milbeaut_bridge_init(struct sdhci_host *host, + int rate) +{ + u32 val, clk; + + /* IO_SDIO_CR_SET should be set while reset */ + val = sdhci_readl(host, MLB_CR_SET); + val &= ~(MLB_CR_SET_CR_TOCLKFREQ_MASK | MLB_CR_SET_CR_TOCLKUNIT | + MLB_CR_SET_CR_BCLKFREQ_MASK); + if (rate >= MLB_TOCLKFREQ_UNIT_THRES) { + clk = MLB_CAL_TOCLKFREQ_MHZ(rate); + clk = min_t(u32, MLB_TOCLKFREQ_MAX, clk); + val |= MLB_CR_SET_CR_TOCLKUNIT | + (clk << MLB_CR_SET_CR_TOCLKFREQ_SFT); + } else { + clk = MLB_CAL_TOCLKFREQ_KHZ(rate); + clk = min_t(u32, MLB_TOCLKFREQ_MAX, clk); + clk = max_t(u32, MLB_TOCLKFREQ_MIN, clk); + val |= clk << MLB_CR_SET_CR_TOCLKFREQ_SFT; + } + + clk = MLB_CAL_BCLKFREQ(rate); + clk = min_t(u32, MLB_BCLKFREQ_MAX, clk); + clk = max_t(u32, MLB_BCLKFREQ_MIN, clk); + val |= clk << MLB_CR_SET_CR_BCLKFREQ_SFT; + val &= ~MLB_CR_SET_CR_RTUNTIMER_MASK; + sdhci_writel(host, val, MLB_CR_SET); + + sdhci_writel(host, MLB_CDR_SET_CLK2POW16, MLB_CDR_SET); + + sdhci_writel(host, MLB_WP_CD_LED_SET_LED_INV, MLB_WP_CD_LED_SET); +} + +static void sdhci_milbeaut_vendor_init(struct sdhci_host *host) +{ + struct f_sdhost_priv *priv = sdhci_priv(host); + u32 ctl; + + ctl = sdhci_readl(host, F_SDH30_IO_CONTROL2); + ctl |= F_SDH30_CRES_O_DN; + sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2); + ctl &= ~F_SDH30_MSEL_O_1_8; + sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2); + ctl &= ~F_SDH30_CRES_O_DN; + sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2); + + ctl = sdhci_readw(host, F_SDH30_AHB_CONFIG); + ctl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 | + F_SDH30_AHB_INCR_4; + ctl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN); + sdhci_writew(host, ctl, F_SDH30_AHB_CONFIG); + + if (priv->enable_cmd_dat_delay) { + ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL); + ctl |= F_SDH30_CMD_DAT_DELAY; + sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL); + } +} + +static const struct of_device_id mlb_dt_ids[] = { + { + .compatible = "socionext,milbeaut-m10v-sdhci-3.0", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mlb_dt_ids); + +static void sdhci_milbeaut_init(struct sdhci_host *host) +{ + struct f_sdhost_priv *priv = sdhci_priv(host); + int rate = clk_get_rate(priv->clk); + u16 ctl; + + sdhci_milbeaut_bridge_reset(host, 0); + + ctl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + ctl &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN); + sdhci_writew(host, ctl, SDHCI_CLOCK_CONTROL); + + sdhci_milbeaut_bridge_reset(host, 1); + + sdhci_milbeaut_bridge_init(host, rate); + sdhci_milbeaut_bridge_reset(host, 0); + + sdhci_milbeaut_vendor_init(host); +} + +static int sdhci_milbeaut_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct device *dev = &pdev->dev; + struct resource *res; + int irq, ret = 0; + struct f_sdhost_priv *priv; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "%s: no irq specified\n", __func__); + return irq; + } + + host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + priv = sdhci_priv(host); + priv->dev = dev; + + host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT | + SDHCI_QUIRK_CLOCK_BEFORE_RESET | + SDHCI_QUIRK_DELAY_AFTER_POWER; + host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE | + SDHCI_QUIRK2_TUNING_WORK_AROUND | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN; + + priv->enable_cmd_dat_delay = device_property_read_bool(dev, + "fujitsu,cmd-dat-delay-select"); + + ret = mmc_of_parse(host->mmc); + if (ret) + goto err; + + platform_set_drvdata(pdev, host); + + host->hw_name = "f_sdh30"; + host->ops = &sdhci_milbeaut_ops; + host->irq = irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->ioaddr)) { + ret = PTR_ERR(host->ioaddr); + goto err; + } + + if (dev_of_node(dev)) { + sdhci_get_of_property(pdev); + + priv->clk_iface = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(priv->clk_iface)) { + ret = PTR_ERR(priv->clk_iface); + goto err; + } + + ret = clk_prepare_enable(priv->clk_iface); + if (ret) + goto err; + + priv->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto err_clk; + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + goto err_clk; + } + + sdhci_milbeaut_init(host); + + ret = sdhci_add_host(host); + if (ret) + goto err_add_host; + + return 0; + +err_add_host: + clk_disable_unprepare(priv->clk); +err_clk: + clk_disable_unprepare(priv->clk_iface); +err: + sdhci_free_host(host); + return ret; +} + +static int sdhci_milbeaut_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct f_sdhost_priv *priv = sdhci_priv(host); + + sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) == + 0xffffffff); + + clk_disable_unprepare(priv->clk_iface); + clk_disable_unprepare(priv->clk); + + sdhci_free_host(host); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver sdhci_milbeaut_driver = { + .driver = { + .name = "sdhci-milbeaut", + .of_match_table = of_match_ptr(mlb_dt_ids), + }, + .probe = sdhci_milbeaut_probe, + .remove = sdhci_milbeaut_remove, +}; + +module_platform_driver(sdhci_milbeaut_driver); + +MODULE_DESCRIPTION("MILBEAUT SD Card Controller driver"); +MODULE_AUTHOR("Takao Orito <orito.takao@socionext.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdhci-milbeaut"); diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 7023cbec4017..55de839a8a5e 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -120,6 +120,12 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_emmc_soc_ctl_map = { .hiword_update = false, }; +static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = { + .baseclkfreq = { .reg = 0x80, .width = 8, .shift = 2 }, + .clockmultiplier = { .reg = 0, .width = -1, .shift = -1 }, + .hiword_update = false, +}; + /** * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers * @@ -384,6 +390,11 @@ static struct sdhci_arasan_of_data intel_lgm_emmc_data = { .pdata = &sdhci_arasan_cqe_pdata, }; +static struct sdhci_arasan_of_data intel_lgm_sdxc_data = { + .soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map, + .pdata = &sdhci_arasan_cqe_pdata, +}; + #ifdef CONFIG_PM_SLEEP /** * sdhci_arasan_suspend - Suspend method for the driver @@ -489,6 +500,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = { .compatible = "intel,lgm-sdhci-5.1-emmc", .data = &intel_lgm_emmc_data, }, + { + .compatible = "intel,lgm-sdhci-5.1-sdxc", + .data = &intel_lgm_sdxc_data, + }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 0ae986c42bc8..5959e394b416 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -27,6 +27,9 @@ #define SDMMC_CACR 0x230 #define SDMMC_CACR_CAPWREN BIT(0) #define SDMMC_CACR_KEY (0x46 << 8) +#define SDMMC_CALCR 0x240 +#define SDMMC_CALCR_EN BIT(0) +#define SDMMC_CALCR_ALWYSON BIT(4) #define SDHCI_AT91_PRESET_COMMON_CONF 0x400 /* drv type B, programmable clock mode */ @@ -35,6 +38,7 @@ struct sdhci_at91_priv { struct clk *gck; struct clk *mainck; bool restore_needed; + bool cal_always_on; }; static void sdhci_at91_set_force_card_detect(struct sdhci_host *host) @@ -116,10 +120,17 @@ static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, static void sdhci_at91_reset(struct sdhci_host *host, u8 mask) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); + sdhci_reset(host, mask); if (host->mmc->caps & MMC_CAP_NONREMOVABLE) sdhci_at91_set_force_card_detect(host); + + if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) + sdhci_writel(host, SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN, + SDMMC_CALCR); } static const struct sdhci_ops sdhci_at91_sama5d2_ops = { @@ -345,6 +356,14 @@ static int sdhci_at91_probe(struct platform_device *pdev) priv->restore_needed = false; + /* + * if SDCAL pin is wrongly connected, we must enable + * the analog calibration cell permanently. + */ + priv->cal_always_on = + device_property_read_bool(&pdev->dev, + "microchip,sdcal-inverted"); + ret = mmc_of_parse(host->mmc); if (ret) goto clocks_disable_unprepare; diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 1d1953dfc54b..5cca3fa4610b 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -77,8 +77,10 @@ struct sdhci_esdhc { bool quirk_incorrect_hostver; bool quirk_limited_clk_division; bool quirk_unreliable_pulse_detection; - bool quirk_fixup_tuning; + bool quirk_tuning_erratum_type1; + bool quirk_tuning_erratum_type2; bool quirk_ignore_data_inhibit; + bool in_sw_tuning; unsigned int peripheral_clock; const struct esdhc_clk_fixup *clk_fixup; u32 div_ratio; @@ -408,6 +410,8 @@ static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg) static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); int base = reg & ~0x3; u32 value; u32 ret; @@ -416,10 +420,24 @@ static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg) ret = esdhc_writew_fixup(host, reg, val, value); if (reg != SDHCI_TRANSFER_MODE) iowrite32be(ret, host->ioaddr + base); + + /* Starting SW tuning requires ESDHC_SMPCLKSEL to be set + * 1us later after ESDHC_EXTN is set. + */ + if (base == ESDHC_SYSTEM_CONTROL_2) { + if (!(value & ESDHC_EXTN) && (ret & ESDHC_EXTN) && + esdhc->in_sw_tuning) { + udelay(1); + ret |= ESDHC_SMPCLKSEL; + iowrite32be(ret, host->ioaddr + base); + } + } } static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); int base = reg & ~0x3; u32 value; u32 ret; @@ -428,6 +446,18 @@ static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg) ret = esdhc_writew_fixup(host, reg, val, value); if (reg != SDHCI_TRANSFER_MODE) iowrite32(ret, host->ioaddr + base); + + /* Starting SW tuning requires ESDHC_SMPCLKSEL to be set + * 1us later after ESDHC_EXTN is set. + */ + if (base == ESDHC_SYSTEM_CONTROL_2) { + if (!(value & ESDHC_EXTN) && (ret & ESDHC_EXTN) && + esdhc->in_sw_tuning) { + udelay(1); + ret |= ESDHC_SMPCLKSEL; + iowrite32(ret, host->ioaddr + base); + } + } } static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg) @@ -560,6 +590,32 @@ static void esdhc_clock_enable(struct sdhci_host *host, bool enable) } } +static void esdhc_flush_async_fifo(struct sdhci_host *host) +{ + ktime_t timeout; + u32 val; + + val = sdhci_readl(host, ESDHC_DMA_SYSCTL); + val |= ESDHC_FLUSH_ASYNC_FIFO; + sdhci_writel(host, val, ESDHC_DMA_SYSCTL); + + /* Wait max 20 ms */ + timeout = ktime_add_ms(ktime_get(), 20); + while (1) { + bool timedout = ktime_after(ktime_get(), timeout); + + if (!(sdhci_readl(host, ESDHC_DMA_SYSCTL) & + ESDHC_FLUSH_ASYNC_FIFO)) + break; + if (timedout) { + pr_err("%s: flushing asynchronous FIFO timeout.\n", + mmc_hostname(host->mmc)); + break; + } + usleep_range(10, 20); + } +} + static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -652,9 +708,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL); esdhc_clock_enable(host, false); - temp = sdhci_readl(host, ESDHC_DMA_SYSCTL); - temp |= ESDHC_FLUSH_ASYNC_FIFO; - sdhci_writel(host, temp, ESDHC_DMA_SYSCTL); + esdhc_flush_async_fifo(host); } /* Wait max 20 ms */ @@ -796,16 +850,21 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc, } } -static struct soc_device_attribute soc_fixup_tuning[] = { +static struct soc_device_attribute soc_tuning_erratum_type1[] = { + { .family = "QorIQ T1023", .revision = "1.0", }, { .family = "QorIQ T1040", .revision = "1.0", }, { .family = "QorIQ T2080", .revision = "1.0", }, - { .family = "QorIQ T1023", .revision = "1.0", }, { .family = "QorIQ LS1021A", .revision = "1.0", }, - { .family = "QorIQ LS1080A", .revision = "1.0", }, - { .family = "QorIQ LS2080A", .revision = "1.0", }, + { }, +}; + +static struct soc_device_attribute soc_tuning_erratum_type2[] = { { .family = "QorIQ LS1012A", .revision = "1.0", }, { .family = "QorIQ LS1043A", .revision = "1.*", }, { .family = "QorIQ LS1046A", .revision = "1.0", }, + { .family = "QorIQ LS1080A", .revision = "1.0", }, + { .family = "QorIQ LS2080A", .revision = "1.0", }, + { .family = "QorIQ LA1575A", .revision = "1.0", }, { }, }; @@ -814,10 +873,7 @@ static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable) u32 val; esdhc_clock_enable(host, false); - - val = sdhci_readl(host, ESDHC_DMA_SYSCTL); - val |= ESDHC_FLUSH_ASYNC_FIFO; - sdhci_writel(host, val, ESDHC_DMA_SYSCTL); + esdhc_flush_async_fifo(host); val = sdhci_readl(host, ESDHC_TBCTL); if (enable) @@ -829,15 +885,97 @@ static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable) esdhc_clock_enable(host, true); } +static void esdhc_prepare_sw_tuning(struct sdhci_host *host, u8 *window_start, + u8 *window_end) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); + u8 tbstat_15_8, tbstat_7_0; + u32 val; + + if (esdhc->quirk_tuning_erratum_type1) { + *window_start = 5 * esdhc->div_ratio; + *window_end = 3 * esdhc->div_ratio; + return; + } + + /* Write TBCTL[11:8]=4'h8 */ + val = sdhci_readl(host, ESDHC_TBCTL); + val &= ~(0xf << 8); + val |= 8 << 8; + sdhci_writel(host, val, ESDHC_TBCTL); + + mdelay(1); + + /* Read TBCTL[31:0] register and rewrite again */ + val = sdhci_readl(host, ESDHC_TBCTL); + sdhci_writel(host, val, ESDHC_TBCTL); + + mdelay(1); + + /* Read the TBSTAT[31:0] register twice */ + val = sdhci_readl(host, ESDHC_TBSTAT); + val = sdhci_readl(host, ESDHC_TBSTAT); + + /* Reset data lines by setting ESDHCCTL[RSTD] */ + sdhci_reset(host, SDHCI_RESET_DATA); + /* Write 32'hFFFF_FFFF to IRQSTAT register */ + sdhci_writel(host, 0xFFFFFFFF, SDHCI_INT_STATUS); + + /* If TBSTAT[15:8]-TBSTAT[7:0] > 4 * div_ratio + * or TBSTAT[7:0]-TBSTAT[15:8] > 4 * div_ratio, + * then program TBPTR[TB_WNDW_END_PTR] = 4 * div_ratio + * and program TBPTR[TB_WNDW_START_PTR] = 8 * div_ratio. + */ + tbstat_7_0 = val & 0xff; + tbstat_15_8 = (val >> 8) & 0xff; + + if (abs(tbstat_15_8 - tbstat_7_0) > (4 * esdhc->div_ratio)) { + *window_start = 8 * esdhc->div_ratio; + *window_end = 4 * esdhc->div_ratio; + } else { + *window_start = 5 * esdhc->div_ratio; + *window_end = 3 * esdhc->div_ratio; + } +} + +static int esdhc_execute_sw_tuning(struct mmc_host *mmc, u32 opcode, + u8 window_start, u8 window_end) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); + u32 val; + int ret; + + /* Program TBPTR[TB_WNDW_END_PTR] and TBPTR[TB_WNDW_START_PTR] */ + val = ((u32)window_start << ESDHC_WNDW_STRT_PTR_SHIFT) & + ESDHC_WNDW_STRT_PTR_MASK; + val |= window_end & ESDHC_WNDW_END_PTR_MASK; + sdhci_writel(host, val, ESDHC_TBPTR); + + /* Program the software tuning mode by setting TBCTL[TB_MODE]=2'h3 */ + val = sdhci_readl(host, ESDHC_TBCTL); + val &= ~ESDHC_TB_MODE_MASK; + val |= ESDHC_TB_MODE_SW; + sdhci_writel(host, val, ESDHC_TBCTL); + + esdhc->in_sw_tuning = true; + ret = sdhci_execute_tuning(mmc, opcode); + esdhc->in_sw_tuning = false; + return ret; +} + static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); + u8 window_start, window_end; + int ret, retries = 1; bool hs400_tuning; unsigned int clk; u32 val; - int ret; /* For tuning mode, the sd clock divisor value * must be larger than 3 according to reference manual. @@ -846,39 +984,73 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->clock > clk) esdhc_of_set_clock(host, clk); - if (esdhc->quirk_limited_clk_division && - host->flags & SDHCI_HS400_TUNING) - esdhc_of_set_clock(host, host->clock); - esdhc_tuning_block_enable(host, true); hs400_tuning = host->flags & SDHCI_HS400_TUNING; - ret = sdhci_execute_tuning(mmc, opcode); - if (hs400_tuning) { - val = sdhci_readl(host, ESDHC_SDTIMNGCTL); - val |= ESDHC_FLW_CTL_BG; - sdhci_writel(host, val, ESDHC_SDTIMNGCTL); - } + do { + if (esdhc->quirk_limited_clk_division && + hs400_tuning) + esdhc_of_set_clock(host, host->clock); - if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) { + /* Do HW tuning */ + val = sdhci_readl(host, ESDHC_TBCTL); + val &= ~ESDHC_TB_MODE_MASK; + val |= ESDHC_TB_MODE_3; + sdhci_writel(host, val, ESDHC_TBCTL); - /* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and - * program TBPTR[TB_WNDW_START_PTR] = 5*DIV_RATIO - */ - val = sdhci_readl(host, ESDHC_TBPTR); - val = (val & ~((0x7f << 8) | 0x7f)) | - (3 * esdhc->div_ratio) | ((5 * esdhc->div_ratio) << 8); - sdhci_writel(host, val, ESDHC_TBPTR); + ret = sdhci_execute_tuning(mmc, opcode); + if (ret) + break; - /* program the software tuning mode by setting - * TBCTL[TB_MODE]=2'h3 + /* If HW tuning fails and triggers erratum, + * try workaround. */ - val = sdhci_readl(host, ESDHC_TBCTL); - val |= 0x3; - sdhci_writel(host, val, ESDHC_TBCTL); - sdhci_execute_tuning(mmc, opcode); + ret = host->tuning_err; + if (ret == -EAGAIN && + (esdhc->quirk_tuning_erratum_type1 || + esdhc->quirk_tuning_erratum_type2)) { + /* Recover HS400 tuning flag */ + if (hs400_tuning) + host->flags |= SDHCI_HS400_TUNING; + pr_info("%s: Hold on to use fixed sampling clock. Try SW tuning!\n", + mmc_hostname(mmc)); + /* Do SW tuning */ + esdhc_prepare_sw_tuning(host, &window_start, + &window_end); + ret = esdhc_execute_sw_tuning(mmc, opcode, + window_start, + window_end); + if (ret) + break; + + /* Retry both HW/SW tuning with reduced clock. */ + ret = host->tuning_err; + if (ret == -EAGAIN && retries) { + /* Recover HS400 tuning flag */ + if (hs400_tuning) + host->flags |= SDHCI_HS400_TUNING; + + clk = host->max_clk / (esdhc->div_ratio + 1); + esdhc_of_set_clock(host, clk); + pr_info("%s: Hold on to use fixed sampling clock. Try tuning with reduced clock!\n", + mmc_hostname(mmc)); + } else { + break; + } + } else { + break; + } + } while (retries--); + + if (ret) { + esdhc_tuning_block_enable(host, false); + } else if (hs400_tuning) { + val = sdhci_readl(host, ESDHC_SDTIMNGCTL); + val |= ESDHC_FLW_CTL_BG; + sdhci_writel(host, val, ESDHC_SDTIMNGCTL); } + return ret; } @@ -1114,10 +1286,15 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); esdhc = sdhci_pltfm_priv(pltfm_host); - if (soc_device_match(soc_fixup_tuning)) - esdhc->quirk_fixup_tuning = true; + if (soc_device_match(soc_tuning_erratum_type1)) + esdhc->quirk_tuning_erratum_type1 = true; + else + esdhc->quirk_tuning_erratum_type1 = false; + + if (soc_device_match(soc_tuning_erratum_type2)) + esdhc->quirk_tuning_erratum_type2 = true; else - esdhc->quirk_fixup_tuning = false; + esdhc->quirk_tuning_erratum_type2 = false; if (esdhc->vendor_ver == VENDOR_V_22) host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index eaffa85bc728..acefb76b4e15 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -21,6 +21,7 @@ #include <linux/mmc/mmc.h> #include <linux/scatterlist.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/gpio.h> #include <linux/pm_runtime.h> #include <linux/mmc/slot-gpio.h> @@ -1590,11 +1591,59 @@ static int amd_probe(struct sdhci_pci_chip *chip) return 0; } +static u32 sdhci_read_present_state(struct sdhci_host *host) +{ + return sdhci_readl(host, SDHCI_PRESENT_STATE); +} + +static void amd_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + struct pci_dev *pdev = slot->chip->pdev; + u32 present_state; + + /* + * SDHC 0x7906 requires a hard reset to clear all internal state. + * Otherwise it can get into a bad state where the DATA lines are always + * read as zeros. + */ + if (pdev->device == 0x7906 && (mask & SDHCI_RESET_ALL)) { + pci_clear_master(pdev); + + pci_save_state(pdev); + + pci_set_power_state(pdev, PCI_D3cold); + pr_debug("%s: power_state=%u\n", mmc_hostname(host->mmc), + pdev->current_state); + pci_set_power_state(pdev, PCI_D0); + + pci_restore_state(pdev); + + /* + * SDHCI_RESET_ALL says the card detect logic should not be + * reset, but since we need to reset the entire controller + * we should wait until the card detect logic has stabilized. + * + * This normally takes about 40ms. + */ + readx_poll_timeout( + sdhci_read_present_state, + host, + present_state, + present_state & SDHCI_CD_STABLE, + 10000, + 100000 + ); + } + + return sdhci_reset(host, mask); +} + static const struct sdhci_ops amd_sdhci_pci_ops = { .set_clock = sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_reset, + .reset = amd_sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; @@ -1673,6 +1722,8 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(INTEL, CML_EMMC, intel_glk_emmc), SDHCI_PCI_DEVICE(INTEL, CML_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, CMLH_SD, intel_byt_sd), + SDHCI_PCI_DEVICE(INTEL, JSL_EMMC, intel_glk_emmc), + SDHCI_PCI_DEVICE(INTEL, JSL_SD, intel_byt_sd), SDHCI_PCI_DEVICE(O2, 8120, o2), SDHCI_PCI_DEVICE(O2, 8220, o2), SDHCI_PCI_DEVICE(O2, 8221, o2), diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 558202fe64c6..981bbbe63aff 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -55,6 +55,8 @@ #define PCI_DEVICE_ID_INTEL_CML_EMMC 0x02c4 #define PCI_DEVICE_ID_INTEL_CML_SD 0x02f5 #define PCI_DEVICE_ID_INTEL_CMLH_SD 0x06f5 +#define PCI_DEVICE_ID_INTEL_JSL_EMMC 0x4dc4 +#define PCI_DEVICE_ID_INTEL_JSL_SD 0x4df8 #define PCI_DEVICE_ID_SYSKONNECT_8000 0x8000 #define PCI_DEVICE_ID_VIA_95D0 0x95d0 diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b056400e34b1..09cdbe86d9f9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -337,8 +337,19 @@ static void sdhci_init(struct sdhci_host *host, int soft) static void sdhci_reinit(struct sdhci_host *host) { + u32 cd = host->ier & (SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); + sdhci_init(host, 0); sdhci_enable_card_detection(host); + + /* + * A change to the card detect bits indicates a change in present state, + * refer sdhci_set_card_detection(). A card detect interrupt might have + * been missed while the host controller was being reset, so trigger a + * rescan to check. + */ + if (cd != (host->ier & (SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT))) + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); } static void __sdhci_led_activate(struct sdhci_host *host) diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index f8b939e63e02..fa0dfc657c22 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -16,31 +16,7 @@ #include <linux/clk.h> #include "sdhci-pltfm.h" - -/* F_SDH30 extended Controller registers */ -#define F_SDH30_AHB_CONFIG 0x100 -#define F_SDH30_AHB_BIGED 0x00000040 -#define F_SDH30_BUSLOCK_DMA 0x00000020 -#define F_SDH30_BUSLOCK_EN 0x00000010 -#define F_SDH30_SIN 0x00000008 -#define F_SDH30_AHB_INCR_16 0x00000004 -#define F_SDH30_AHB_INCR_8 0x00000002 -#define F_SDH30_AHB_INCR_4 0x00000001 - -#define F_SDH30_TUNING_SETTING 0x108 -#define F_SDH30_CMD_CHK_DIS 0x00010000 - -#define F_SDH30_IO_CONTROL2 0x114 -#define F_SDH30_CRES_O_DN 0x00080000 -#define F_SDH30_MSEL_O_1_8 0x00040000 - -#define F_SDH30_ESD_CONTROL 0x124 -#define F_SDH30_EMMC_RST 0x00000002 -#define F_SDH30_EMMC_HS200 0x01000000 - -#define F_SDH30_CMD_DAT_DELAY 0x200 - -#define F_SDH30_MIN_CLOCK 400000 +#include "sdhci_f_sdh30.h" struct f_sdhost_priv { struct clk *clk_iface; diff --git a/drivers/mmc/host/sdhci_f_sdh30.h b/drivers/mmc/host/sdhci_f_sdh30.h new file mode 100644 index 000000000000..fc1ad28f7ca9 --- /dev/null +++ b/drivers/mmc/host/sdhci_f_sdh30.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd + * Vincent Yang <vincent.yang@tw.fujitsu.com> + * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org> + * Copyright (C) 2019 Socionext Inc. + * + */ + +/* F_SDH30 extended Controller registers */ +#define F_SDH30_AHB_CONFIG 0x100 +#define F_SDH30_AHB_BIGED BIT(6) +#define F_SDH30_BUSLOCK_DMA BIT(5) +#define F_SDH30_BUSLOCK_EN BIT(4) +#define F_SDH30_SIN BIT(3) +#define F_SDH30_AHB_INCR_16 BIT(2) +#define F_SDH30_AHB_INCR_8 BIT(1) +#define F_SDH30_AHB_INCR_4 BIT(0) + +#define F_SDH30_TUNING_SETTING 0x108 +#define F_SDH30_CMD_CHK_DIS BIT(16) + +#define F_SDH30_IO_CONTROL2 0x114 +#define F_SDH30_CRES_O_DN BIT(19) +#define F_SDH30_MSEL_O_1_8 BIT(18) + +#define F_SDH30_ESD_CONTROL 0x124 +#define F_SDH30_EMMC_RST BIT(1) +#define F_SDH30_CMD_DAT_DELAY BIT(9) +#define F_SDH30_EMMC_HS200 BIT(24) + +#define F_SDH30_MIN_CLOCK 400000 diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 9b6e1001e77c..63dc37481fba 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -1209,15 +1209,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) _host->reset = tmio_mmc_reset; /* - * On Gen2+, eMMC with NONREMOVABLE currently fails because native - * hotplug gets disabled. It seems RuntimePM related yet we need further - * research. Since we are planning a PM overhaul anyway, let's enforce - * for now the device being active by enabling native hotplug always. - */ - if (pdata->flags & TMIO_MMC_MIN_RCAR2) - _host->native_hotplug = true; - - /* * While using internal tmio hardware logic for card detection, we need * to ensure it stays powered for it to work. */ diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index a3680c900689..6ced1b7f642f 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2070,18 +2070,11 @@ static void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable) kref_put(&vub300->kref, vub300_delete); } -static void vub300_init_card(struct mmc_host *mmc, struct mmc_card *card) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = mmc_priv(mmc); - dev_info(&vub300->udev->dev, "NO host QUIRKS for this card\n"); -} - static const struct mmc_host_ops vub300_mmc_ops = { .request = vub300_mmc_request, .set_ios = vub300_mmc_set_ios, .get_ro = vub300_mmc_get_ro, .enable_sdio_irq = vub300_enable_sdio_irq, - .init_card = vub300_init_card, }; static int vub300_probe(struct usb_interface *interface, |