diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2018-04-30 11:09:28 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2018-04-30 11:09:28 +1000 |
commit | e4c0caaa01ec91f169c87787c56f09193483e14a (patch) | |
tree | 5f0520325a8e5a7acf238982436e4e566520ca02 /drivers | |
parent | 051449c52fc4c5e3fa5d85da27f7e8677b1b9523 (diff) | |
parent | 5bec8e5878e28777a14a4e0cc0d136979ebcdbe2 (diff) |
Merge remote-tracking branch 'mmc/next'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/core/block.c | 3 | ||||
-rw-r--r-- | drivers/mmc/core/card.h | 6 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 8 | ||||
-rw-r--r-- | drivers/mmc/core/pwrseq_simple.c | 14 | ||||
-rw-r--r-- | drivers/mmc/core/quirks.h | 3 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 8 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 14 | ||||
-rw-r--r-- | drivers/mmc/host/Kconfig | 23 | ||||
-rw-r--r-- | drivers/mmc/host/davinci_mmc.c | 6 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc-rockchip.c | 5 | ||||
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 4 | ||||
-rw-r--r-- | drivers/mmc/host/jz4740_mmc.c | 203 | ||||
-rw-r--r-- | drivers/mmc/host/meson-gx-mmc.c | 63 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 5 | ||||
-rw-r--r-- | drivers/mmc/host/mtk-sd.c | 63 | ||||
-rw-r--r-- | drivers/mmc/host/renesas_sdhi_internal_dmac.c | 30 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-cadence.c | 22 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-esdhc-imx.c | 22 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-msm.c | 84 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-of-arasan.c | 9 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pci-core.c | 38 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-pci.h | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sunxi-mmc.c | 185 | ||||
-rw-r--r-- | drivers/mmc/host/wmt-sdmmc.c | 6 |
24 files changed, 604 insertions, 221 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 9e923cd1d80e..03e3d48b083e 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2353,7 +2353,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, set_disk_ro(md->disk, md->read_only || default_ro); md->disk->flags = GENHD_FL_EXT_DEVT; if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT)) - md->disk->flags |= GENHD_FL_NO_PART_SCAN; + md->disk->flags |= GENHD_FL_NO_PART_SCAN + | GENHD_FL_SUPPRESS_PARTITION_INFO; /* * As discussed on lkml, GENHD_FL_REMOVABLE should: diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index 9c821eedd156..1170feb8f969 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -149,6 +149,12 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) card->quirks &= ~data; } +static inline void __maybe_unused add_limit_rate_quirk(struct mmc_card *card, + int data) +{ + card->quirk_max_rate = data; +} + /* * Quirk add/remove for MMC products. */ diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6f8ebd6caa4c..915b58f9bfc5 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1830,6 +1830,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + if (host->caps2 & MMC_CAP2_AVOID_3_3V && + host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + pr_err("%s: Host failed to negotiate down from 3.3V\n", + mmc_hostname(host)); + err = -EINVAL; + goto free_card; + } + if (!oldcard) host->card = card; diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 13ef162cf066..a8b9fee4d62a 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -40,14 +40,18 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, struct gpio_descs *reset_gpios = pwrseq->reset_gpios; if (!IS_ERR(reset_gpios)) { - int i; - int values[reset_gpios->ndescs]; + int i, *values; + int nvalues = reset_gpios->ndescs; - for (i = 0; i < reset_gpios->ndescs; i++) + values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL); + if (!values) + return; + + for (i = 0; i < nvalues; i++) values[i] = value; - gpiod_set_array_value_cansleep( - reset_gpios->ndescs, reset_gpios->desc, values); + gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values); + kfree(values); } } diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 5153577754f0..dd2f73af8f2c 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -132,6 +132,9 @@ static const struct mmc_fixup sdio_fixup_methods[] = { SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0, add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING), + SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887WLAN, + add_limit_rate_quirk, 150000000), + END_FIXUP }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index baf3d5da4ccb..7f87a5354f9f 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1058,6 +1058,14 @@ retry: mmc_set_bus_width(host, MMC_BUS_WIDTH_4); } } + + if (host->caps2 & MMC_CAP2_AVOID_3_3V && + host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + pr_err("%s: Host failed to negotiate down from 3.3V\n", + mmc_hostname(host)); + err = -EINVAL; + goto free_card; + } done: host->card = card; return 0; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c599a628a387..87e53a721a10 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -444,6 +444,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) unsigned int bus_speed, timing; int err; unsigned char speed; + unsigned int max_rate; /* * If the host doesn't support any of the UHS-I modes, fallback on @@ -500,9 +501,12 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) if (err) return err; + max_rate = min_not_zero(card->quirk_max_rate, + card->sw_caps.uhs_max_dtr); + if (bus_speed) { mmc_set_timing(card->host, timing); - mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr); + mmc_set_clock(card->host, max_rate); } return 0; @@ -788,6 +792,14 @@ try_again: if (err) goto remove; } + + if (host->caps2 & MMC_CAP2_AVOID_3_3V && + host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + pr_err("%s: Host failed to negotiate down from 3.3V\n", + mmc_hostname(host)); + err = -EINVAL; + goto remove; + } finish: if (!oldcard) host->card = card; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9589f9c9046f..d70d11d0abab 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -345,11 +345,11 @@ config MMC_SDHCI_IPROC If unsure, say N. config MMC_MESON_GX - tristate "Amlogic S905/GX* SD/MMC Host Controller support" + tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support" depends on ARCH_MESON && MMC help This selects support for the Amlogic SD/MMC Host Controller - found on the S905/GX* family of SoCs. This controller is + found on the S905/GX*/AXG family of SoCs. This controller is MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces. If you have a controller with this interface, say Y here. @@ -358,7 +358,6 @@ config MMC_MESON_MX_SDIO tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support" depends on ARCH_MESON || COMPILE_TEST depends on COMMON_CLK - depends on HAS_DMA depends on OF help This selects support for the SD/MMC Host Controller on @@ -401,7 +400,6 @@ config MMC_OMAP config MMC_OMAP_HS tristate "TI OMAP High Speed Multimedia Card Interface support" - depends on HAS_DMA depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST help This selects the TI OMAP High Speed Multimedia card Interface. @@ -511,7 +509,6 @@ config MMC_DAVINCI config MMC_GOLDFISH tristate "goldfish qemu Multimedia Card Interface support" - depends on HAS_DMA depends on GOLDFISH || COMPILE_TEST help This selects the Goldfish Multimedia card Interface emulation @@ -605,7 +602,7 @@ config MMC_SDHI config MMC_SDHI_SYS_DMAC tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC" - depends on MMC_SDHI && HAS_DMA + depends on MMC_SDHI default MMC_SDHI if (SUPERH || ARM) help This provides DMA support for SDHI SD/SDIO controllers @@ -615,7 +612,7 @@ config MMC_SDHI_SYS_DMAC config MMC_SDHI_INTERNAL_DMAC tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering" depends on ARM64 || COMPILE_TEST - depends on MMC_SDHI && HAS_DMA + depends on MMC_SDHI default MMC_SDHI if ARM64 help This provides DMA support for SDHI SD/SDIO controllers @@ -669,7 +666,6 @@ config MMC_CAVIUM_THUNDERX config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" - depends on HAS_DMA depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP @@ -748,7 +744,6 @@ config MMC_DW_ZX config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" - depends on HAS_DMA depends on SUPERH || ARCH_RENESAS || COMPILE_TEST help This selects the MMC Host Interface controller (MMCIF) found in various @@ -756,11 +751,12 @@ config MMC_SH_MMCIF config MMC_JZ4740 - tristate "JZ4740 SD/Multimedia Card Interface support" - depends on MACH_JZ4740 + tristate "Ingenic JZ47xx SD/Multimedia Card Interface support" + depends on MACH_JZ4740 || MACH_JZ4780 help - This selects support for the SD/MMC controller on Ingenic JZ4740 - SoCs. + This selects support for the SD/MMC controller on Ingenic + JZ4740, JZ4750, JZ4770 and JZ4780 SoCs. + If you have a board based on such a SoC and with a SD/MMC slot, say Y or M here. @@ -868,7 +864,6 @@ config MMC_TOSHIBA_PCI config MMC_BCM2835 tristate "Broadcom BCM2835 SDHOST MMC Controller support" depends on ARCH_BCM2835 || COMPILE_TEST - depends on HAS_DMA help This selects the BCM2835 SDHOST MMC controller. If you have a BCM2835 platform with SD or MMC devices, say Y or M here. diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 8e363174f9d6..9e68c3645e22 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1377,8 +1377,7 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int davinci_mmcsd_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mmc_davinci_host *host = platform_get_drvdata(pdev); + struct mmc_davinci_host *host = dev_get_drvdata(dev); writel(0, host->base + DAVINCI_MMCIM); mmc_davinci_reset_ctrl(host, 1); @@ -1389,8 +1388,7 @@ static int davinci_mmcsd_suspend(struct device *dev) static int davinci_mmcsd_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mmc_davinci_host *host = platform_get_drvdata(pdev); + struct mmc_davinci_host *host = dev_get_drvdata(dev); clk_enable(host->clk); mmc_davinci_reset_ctrl(host, 0); diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 40d7de2eea12..8c86a800a8fd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -44,9 +44,8 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * bus_hz = cclkin / RK3288_CLKGEN_DIV * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) * - * Note: div can only be 0 or 1 - * if DDR50 8bit mode(only emmc work in 8bit mode), - * div must be set 1 + * Note: div can only be 0 or 1, but div must be set to 1 for eMMC + * DDR52 8-bit mode. */ if (ios->bus_width == MMC_BUS_WIDTH_8 && ios->timing == MMC_TIMING_MMC_DDR52) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 29a1afa81f66..623f4d27fa01 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1230,6 +1230,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if (host->state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + slot->mmc->actual_clock = 0; + if (!clock) { mci_writel(host, CLKENA, 0); mci_send_cmd(slot, sdmmc_cmd_bits, 0); @@ -1288,6 +1290,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* keep the last clock value that was requested from core */ slot->__clk_old = clock; + slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : + host->bus_hz; } host->current_speed = clock; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index a0168e9e4fce..993386c9ea50 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2013, Imagination Technologies + * * JZ4740 SD/MMC controller driver * * This program is free software; you can redistribute it and/or modify it @@ -13,24 +15,25 @@ * */ -#include <linux/mmc/host.h> -#include <linux/mmc/slot-gpio.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> #include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> -#include <linux/interrupt.h> +#include <linux/mmc/host.h> +#include <linux/mmc/slot-gpio.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> -#include <linux/delay.h> #include <linux/scatterlist.h> -#include <linux/clk.h> -#include <linux/bitops.h> -#include <linux/gpio.h> #include <asm/cacheflush.h> -#include <linux/dma-mapping.h> -#include <linux/dmaengine.h> #include <asm/mach-jz4740/dma.h> #include <asm/mach-jz4740/jz4740_mmc.h> @@ -51,6 +54,7 @@ #define JZ_REG_MMC_RESP_FIFO 0x34 #define JZ_REG_MMC_RXFIFO 0x38 #define JZ_REG_MMC_TXFIFO 0x3C +#define JZ_REG_MMC_DMAC 0x44 #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) @@ -104,9 +108,17 @@ #define JZ_MMC_IRQ_PRG_DONE BIT(1) #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) +#define JZ_MMC_DMAC_DMA_SEL BIT(1) +#define JZ_MMC_DMAC_DMA_EN BIT(0) #define JZ_MMC_CLK_RATE 24000000 +enum jz4740_mmc_version { + JZ_MMC_JZ4740, + JZ_MMC_JZ4750, + JZ_MMC_JZ4780, +}; + enum jz4740_mmc_state { JZ4740_MMC_STATE_READ_RESPONSE, JZ4740_MMC_STATE_TRANSFER_DATA, @@ -125,6 +137,8 @@ struct jz4740_mmc_host { struct jz4740_mmc_platform_data *pdata; struct clk *clk; + enum jz4740_mmc_version version; + int irq; int card_detect_irq; @@ -137,7 +151,7 @@ struct jz4740_mmc_host { uint32_t cmdat; - uint16_t irq_mask; + uint32_t irq_mask; spinlock_t lock; @@ -159,6 +173,32 @@ struct jz4740_mmc_host { #define JZ4740_MMC_FIFO_HALF_SIZE 8 }; +static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host, + uint32_t val) +{ + if (host->version >= JZ_MMC_JZ4750) + return writel(val, host->base + JZ_REG_MMC_IMASK); + else + return writew(val, host->base + JZ_REG_MMC_IMASK); +} + +static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host, + uint32_t val) +{ + if (host->version >= JZ_MMC_JZ4780) + return writel(val, host->base + JZ_REG_MMC_IREG); + else + return writew(val, host->base + JZ_REG_MMC_IREG); +} + +static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host) +{ + if (host->version >= JZ_MMC_JZ4780) + return readl(host->base + JZ_REG_MMC_IREG); + else + return readw(host->base + JZ_REG_MMC_IREG); +} + /*----------------------------------------------------------------------------*/ /* DMA infrastructure */ @@ -173,31 +213,23 @@ static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->dma_tx = dma_request_channel(mask, NULL, host); - if (!host->dma_tx) { + host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); + if (IS_ERR(host->dma_tx)) { dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); - return -ENODEV; + return PTR_ERR(host->dma_tx); } - host->dma_rx = dma_request_channel(mask, NULL, host); - if (!host->dma_rx) { + host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); + if (IS_ERR(host->dma_rx)) { dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); - goto free_master_write; + dma_release_channel(host->dma_tx); + return PTR_ERR(host->dma_rx); } /* Initialize DMA pre request cookie */ host->next_data.cookie = 1; return 0; - -free_master_write: - dma_release_channel(host->dma_tx); - return -ENODEV; } static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, @@ -363,7 +395,7 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, else host->irq_mask |= irq; - writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); + jz4740_mmc_write_irq_mask(host, host->irq_mask); spin_unlock_irqrestore(&host->lock, flags); } @@ -415,10 +447,10 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, unsigned int irq) { unsigned int timeout = 0x800; - uint16_t status; + uint32_t status; do { - status = readw(host->base + JZ_REG_MMC_IREG); + status = jz4740_mmc_read_irq_reg(host); } while (!(status & irq) && --timeout); if (timeout == 0) { @@ -518,7 +550,7 @@ static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; uint32_t *buf; uint32_t d; - uint16_t status; + uint32_t status; size_t i, j; unsigned int timeout; @@ -654,8 +686,25 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, cmdat |= JZ_MMC_CMDAT_DATA_EN; if (cmd->data->flags & MMC_DATA_WRITE) cmdat |= JZ_MMC_CMDAT_WRITE; - if (host->use_dma) - cmdat |= JZ_MMC_CMDAT_DMA_EN; + if (host->use_dma) { + /* + * The 4780's MMC controller has integrated DMA ability + * in addition to being able to use the external DMA + * controller. It moves DMA control bits to a separate + * register. The DMA_SEL bit chooses the external + * controller over the integrated one. Earlier SoCs + * can only use the external controller, and have a + * single DMA enable bit in CMDAT. + */ + if (host->version >= JZ_MMC_JZ4780) { + writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL, + host->base + JZ_REG_MMC_DMAC); + } else { + cmdat |= JZ_MMC_CMDAT_DMA_EN; + } + } else if (host->version >= JZ_MMC_JZ4780) { + writel(0, host->base + JZ_REG_MMC_DMAC); + } writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); @@ -736,7 +785,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) host->state = JZ4740_MMC_STATE_SEND_STOP; break; } - writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE); case JZ4740_MMC_STATE_SEND_STOP: if (!req->stop) @@ -766,9 +815,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) { struct jz4740_mmc_host *host = devid; struct mmc_command *cmd = host->cmd; - uint16_t irq_reg, status, tmp; + uint32_t irq_reg, status, tmp; - irq_reg = readw(host->base + JZ_REG_MMC_IREG); + status = readl(host->base + JZ_REG_MMC_STATUS); + irq_reg = jz4740_mmc_read_irq_reg(host); tmp = irq_reg; irq_reg &= ~host->irq_mask; @@ -777,10 +827,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); if (tmp != irq_reg) - writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg); if (irq_reg & JZ_MMC_IRQ_SDIO) { - writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO); mmc_signal_sdio_irq(host->mmc); irq_reg &= ~JZ_MMC_IRQ_SDIO; } @@ -789,8 +839,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) if (test_and_clear_bit(0, &host->waiting)) { del_timer(&host->timeout_timer); - status = readl(host->base + JZ_REG_MMC_STATUS); - if (status & JZ_MMC_STATUS_TIMEOUT_RES) { cmd->error = -ETIMEDOUT; } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { @@ -803,7 +851,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) } jz4740_mmc_set_irq_enabled(host, irq_reg, false); - writew(irq_reg, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, irq_reg); return IRQ_WAKE_THREAD; } @@ -818,7 +866,7 @@ static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) int real_rate; jz4740_mmc_clock_disable(host); - clk_set_rate(host->clk, JZ_MMC_CLK_RATE); + clk_set_rate(host->clk, host->mmc->f_max); real_rate = clk_get_rate(host->clk); @@ -837,9 +885,7 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) host->req = req; - writew(0xffff, host->base + JZ_REG_MMC_IREG); - - writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, ~0); jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); host->state = JZ4740_MMC_STATE_READ_RESPONSE; @@ -857,7 +903,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: jz4740_mmc_reset(host); - if (gpio_is_valid(host->pdata->gpio_power)) + if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) gpio_set_value(host->pdata->gpio_power, !host->pdata->power_active_low); host->cmdat |= JZ_MMC_CMDAT_INIT; @@ -866,7 +912,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_ON: break; default: - if (gpio_is_valid(host->pdata->gpio_power)) + if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) gpio_set_value(host->pdata->gpio_power, host->pdata->power_active_low); clk_disable_unprepare(host->clk); @@ -926,7 +972,7 @@ static int jz4740_mmc_request_gpio(struct device *dev, int gpio, static int jz4740_mmc_request_gpios(struct mmc_host *mmc, struct platform_device *pdev) { - struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; + struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret = 0; if (!pdata) @@ -955,7 +1001,7 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc, static void jz4740_mmc_free_gpios(struct platform_device *pdev) { - struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; + struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); if (!pdata) return; @@ -964,14 +1010,22 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev) gpio_free(pdata->gpio_power); } +static const struct of_device_id jz4740_mmc_of_match[] = { + { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, + { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); + static int jz4740_mmc_probe(struct platform_device* pdev) { int ret; struct mmc_host *mmc; struct jz4740_mmc_host *host; + const struct of_device_id *match; struct jz4740_mmc_platform_data *pdata; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); if (!mmc) { @@ -982,6 +1036,27 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host = mmc_priv(mmc); host->pdata = pdata; + match = of_match_device(jz4740_mmc_of_match, &pdev->dev); + if (match) { + host->version = (enum jz4740_mmc_version)match->data; + ret = mmc_of_parse(mmc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "could not parse of data: %d\n", ret); + goto err_free_host; + } + } else { + /* JZ4740 should be the only one using legacy probe */ + host->version = JZ_MMC_JZ4740; + mmc->caps |= MMC_CAP_SDIO_IRQ; + if (!(pdata && pdata->data_1bit)) + mmc->caps |= MMC_CAP_4_BIT_DATA; + ret = jz4740_mmc_request_gpios(mmc, pdev); + if (ret) + goto err_free_host; + } + host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { ret = host->irq; @@ -1004,16 +1079,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_host; } - ret = jz4740_mmc_request_gpios(mmc, pdev); - if (ret) - goto err_release_dma; - mmc->ops = &jz4740_mmc_ops; - mmc->f_min = JZ_MMC_CLK_RATE / 128; - mmc->f_max = JZ_MMC_CLK_RATE; + if (!mmc->f_max) + mmc->f_max = JZ_MMC_CLK_RATE; + mmc->f_min = mmc->f_max / 128; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA; - mmc->caps |= MMC_CAP_SDIO_IRQ; mmc->max_blk_size = (1 << 10) - 1; mmc->max_blk_count = (1 << 15) - 1; @@ -1025,7 +1095,9 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host->mmc = mmc; host->pdev = pdev; spin_lock_init(&host->lock); - host->irq_mask = 0xffff; + host->irq_mask = ~0; + + jz4740_mmc_reset(host); ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, dev_name(&pdev->dev), host); @@ -1034,20 +1106,20 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_gpios; } - jz4740_mmc_reset(host); jz4740_mmc_clock_disable(host); timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); - host->use_dma = true; - if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0) - host->use_dma = false; + ret = jz4740_mmc_acquire_dma_channels(host); + if (ret == -EPROBE_DEFER) + goto err_free_irq; + host->use_dma = !ret; platform_set_drvdata(pdev, host); ret = mmc_add_host(mmc); if (ret) { dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); - goto err_free_irq; + goto err_release_dma; } dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); @@ -1057,13 +1129,13 @@ static int jz4740_mmc_probe(struct platform_device* pdev) return 0; +err_release_dma: + if (host->use_dma) + jz4740_mmc_release_dma_channels(host); err_free_irq: free_irq(host->irq, host); err_free_gpios: jz4740_mmc_free_gpios(pdev); -err_release_dma: - if (host->use_dma) - jz4740_mmc_release_dma_channels(host); err_free_host: mmc_free_host(mmc); @@ -1116,6 +1188,7 @@ static struct platform_driver jz4740_mmc_driver = { .remove = jz4740_mmc_remove, .driver = { .name = "jz4740-mmc", + .of_match_table = of_match_ptr(jz4740_mmc_of_match), .pm = JZ4740_MMC_PM_OPS, }, }; diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 4f972b879fe6..55bbd67177df 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -47,15 +47,29 @@ #define CLK_CORE_PHASE_MASK GENMASK(9, 8) #define CLK_TX_PHASE_MASK GENMASK(11, 10) #define CLK_RX_PHASE_MASK GENMASK(13, 12) -#define CLK_TX_DELAY_MASK GENMASK(19, 16) -#define CLK_RX_DELAY_MASK GENMASK(23, 20) +#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16) +#define CLK_V2_RX_DELAY_MASK GENMASK(23, 20) +#define CLK_V2_ALWAYS_ON BIT(24) + +#define CLK_V3_TX_DELAY_MASK GENMASK(21, 16) +#define CLK_V3_RX_DELAY_MASK GENMASK(27, 22) +#define CLK_V3_ALWAYS_ON BIT(28) + #define CLK_DELAY_STEP_PS 200 #define CLK_PHASE_STEP 30 #define CLK_PHASE_POINT_NUM (360 / CLK_PHASE_STEP) -#define CLK_ALWAYS_ON BIT(24) + +#define CLK_TX_DELAY_MASK(h) (h->data->tx_delay_mask) +#define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask) +#define CLK_ALWAYS_ON(h) (h->data->always_on) #define SD_EMMC_DELAY 0x4 #define SD_EMMC_ADJUST 0x8 + +#define SD_EMMC_DELAY1 0x4 +#define SD_EMMC_DELAY2 0x8 +#define SD_EMMC_V3_ADJUST 0xc + #define SD_EMMC_CALOUT 0x10 #define SD_EMMC_START 0x40 #define START_DESC_INIT BIT(0) @@ -122,6 +136,12 @@ #define MUX_CLK_NUM_PARENTS 2 +struct meson_mmc_data { + unsigned int tx_delay_mask; + unsigned int rx_delay_mask; + unsigned int always_on; +}; + struct sd_emmc_desc { u32 cmd_cfg; u32 cmd_arg; @@ -131,6 +151,7 @@ struct sd_emmc_desc { struct meson_host { struct device *dev; + struct meson_mmc_data *data; struct mmc_host *mmc; struct mmc_command *cmd; @@ -474,7 +495,7 @@ static int meson_mmc_clk_init(struct meson_host *host) /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ clk_reg = 0; - clk_reg |= CLK_ALWAYS_ON; + clk_reg |= CLK_ALWAYS_ON(host); clk_reg |= CLK_DIV_MASK; writel(clk_reg, host->regs + SD_EMMC_CLOCK); @@ -574,7 +595,7 @@ static int meson_mmc_clk_init(struct meson_host *host) tx->reg = host->regs + SD_EMMC_CLOCK; tx->phase_mask = CLK_TX_PHASE_MASK; - tx->delay_mask = CLK_TX_DELAY_MASK; + tx->delay_mask = CLK_TX_DELAY_MASK(host); tx->delay_step_ps = CLK_DELAY_STEP_PS; tx->hw.init = &init; @@ -597,7 +618,7 @@ static int meson_mmc_clk_init(struct meson_host *host) rx->reg = host->regs + SD_EMMC_CLOCK; rx->phase_mask = CLK_RX_PHASE_MASK; - rx->delay_mask = CLK_RX_DELAY_MASK; + rx->delay_mask = CLK_RX_DELAY_MASK(host); rx->delay_step_ps = CLK_DELAY_STEP_PS; rx->hw.init = &init; @@ -1184,6 +1205,13 @@ static int meson_mmc_probe(struct platform_device *pdev) goto free_host; } + host->data = (struct meson_mmc_data *) + of_device_get_match_data(&pdev->dev); + if (!host->data) { + ret = -EINVAL; + goto free_host; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs)) { @@ -1315,11 +1343,24 @@ static int meson_mmc_remove(struct platform_device *pdev) return 0; } +static const struct meson_mmc_data meson_gx_data = { + .tx_delay_mask = CLK_V2_TX_DELAY_MASK, + .rx_delay_mask = CLK_V2_RX_DELAY_MASK, + .always_on = CLK_V2_ALWAYS_ON, +}; + +static const struct meson_mmc_data meson_axg_data = { + .tx_delay_mask = CLK_V3_TX_DELAY_MASK, + .rx_delay_mask = CLK_V3_RX_DELAY_MASK, + .always_on = CLK_V3_ALWAYS_ON, +}; + static const struct of_device_id meson_mmc_of_match[] = { - { .compatible = "amlogic,meson-gx-mmc", }, - { .compatible = "amlogic,meson-gxbb-mmc", }, - { .compatible = "amlogic,meson-gxl-mmc", }, - { .compatible = "amlogic,meson-gxm-mmc", }, + { .compatible = "amlogic,meson-gx-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxbb-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxl-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxm-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-axg-mmc", .data = &meson_axg_data }, {} }; MODULE_DEVICE_TABLE(of, meson_mmc_of_match); @@ -1335,6 +1376,6 @@ static struct platform_driver meson_mmc_driver = { module_platform_driver(meson_mmc_driver); -MODULE_DESCRIPTION("Amlogic S905*/GX* SD/eMMC driver"); +MODULE_DESCRIPTION("Amlogic S905*/GX*/AXG SD/eMMC driver"); MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 70b0df8b9c78..f1849775e47e 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1253,15 +1253,12 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) struct sg_mapping_iter *sg_miter = &host->sg_miter; struct variant_data *variant = host->variant; void __iomem *base = host->base; - unsigned long flags; u32 status; status = readl(base + MMCISTATUS); dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); - local_irq_save(flags); - do { unsigned int remain, len; char *buffer; @@ -1301,8 +1298,6 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) sg_miter_stop(sg_miter); - local_irq_restore(flags); - /* * If we have less than the fifo 'half-full' threshold to transfer, * trigger a PIO interrupt as soon as any data is available. diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index cb274e822293..04841386b65d 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -19,6 +19,7 @@ #include <linux/ioport.h> #include <linux/irq.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> @@ -70,6 +71,7 @@ #define SDC_ADV_CFG0 0x64 #define EMMC_IOCON 0x7c #define SDC_ACMD_RESP 0x80 +#define DMA_SA_H4BIT 0x8c #define MSDC_DMA_SA 0x90 #define MSDC_DMA_CTRL 0x98 #define MSDC_DMA_CFG 0x9c @@ -194,6 +196,9 @@ /* SDC_ADV_CFG0 mask */ #define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */ +/* DMA_SA_H4BIT mask */ +#define DMA_ADDR_HIGH_4BIT (0xf << 0) /* RW */ + /* MSDC_DMA_CTRL mask */ #define MSDC_DMA_CTRL_START (0x1 << 0) /* W */ #define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */ @@ -227,6 +232,7 @@ #define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */ #define MSDC_PATCH_BIT2_CFGCRCSTS (0x1 << 28) /* RW */ +#define MSDC_PB2_SUPPORT_64G (0x1 << 1) /* RW */ #define MSDC_PB2_RESPWAIT (0x3 << 2) /* RW */ #define MSDC_PB2_RESPSTSENSEL (0x7 << 16) /* RW */ #define MSDC_PB2_CRCSTSENSEL (0x7 << 29) /* RW */ @@ -280,6 +286,8 @@ struct mt_gpdma_desc { #define GPDMA_DESC_BDP (0x1 << 1) #define GPDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ #define GPDMA_DESC_INT (0x1 << 16) +#define GPDMA_DESC_NEXT_H4 (0xf << 24) +#define GPDMA_DESC_PTR_H4 (0xf << 28) u32 next; u32 ptr; u32 gpd_data_len; @@ -296,6 +304,8 @@ struct mt_bdma_desc { #define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ #define BDMA_DESC_BLKPAD (0x1 << 17) #define BDMA_DESC_DWPAD (0x1 << 18) +#define BDMA_DESC_NEXT_H4 (0xf << 24) +#define BDMA_DESC_PTR_H4 (0xf << 28) u32 next; u32 ptr; u32 bd_data_len; @@ -334,6 +344,7 @@ struct mtk_mmc_compatible { bool busy_check; bool stop_clk_fix; bool enhance_rx; + bool support_64g; }; struct msdc_tune_para { @@ -403,6 +414,7 @@ static const struct mtk_mmc_compatible mt8135_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt8173_compat = { @@ -414,6 +426,7 @@ static const struct mtk_mmc_compatible mt8173_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt2701_compat = { @@ -425,6 +438,7 @@ static const struct mtk_mmc_compatible mt2701_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt2712_compat = { @@ -436,6 +450,7 @@ static const struct mtk_mmc_compatible mt2712_compat = { .busy_check = true, .stop_clk_fix = true, .enhance_rx = true, + .support_64g = true, }; static const struct mtk_mmc_compatible mt7622_compat = { @@ -447,6 +462,7 @@ static const struct mtk_mmc_compatible mt7622_compat = { .busy_check = true, .stop_clk_fix = true, .enhance_rx = true, + .support_64g = false, }; static const struct of_device_id msdc_of_ids[] = { @@ -556,7 +572,12 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, /* init bd */ bd[j].bd_info &= ~BDMA_DESC_BLKPAD; bd[j].bd_info &= ~BDMA_DESC_DWPAD; - bd[j].ptr = (u32)dma_address; + bd[j].ptr = lower_32_bits(dma_address); + if (host->dev_comp->support_64g) { + bd[j].bd_info &= ~BDMA_DESC_PTR_H4; + bd[j].bd_info |= (upper_32_bits(dma_address) & 0xf) + << 28; + } bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN; bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN); @@ -575,7 +596,10 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, dma_ctrl &= ~(MSDC_DMA_CTRL_BRUSTSZ | MSDC_DMA_CTRL_MODE); dma_ctrl |= (MSDC_BURST_64B << 12 | 1 << 8); writel_relaxed(dma_ctrl, host->base + MSDC_DMA_CTRL); - writel((u32)dma->gpd_addr, host->base + MSDC_DMA_SA); + if (host->dev_comp->support_64g) + sdr_set_field(host->base + DMA_SA_H4BIT, DMA_ADDR_HIGH_4BIT, + upper_32_bits(dma->gpd_addr) & 0xf); + writel(lower_32_bits(dma->gpd_addr), host->base + MSDC_DMA_SA); } static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq) @@ -1366,6 +1390,9 @@ static void msdc_init_hw(struct msdc_host *host) MSDC_PATCH_BIT2_CFGCRCSTS); } + if (host->dev_comp->support_64g) + sdr_set_bits(host->base + MSDC_PATCH_BIT2, + MSDC_PB2_SUPPORT_64G); if (host->dev_comp->data_tune) { sdr_set_bits(host->base + tune_reg, MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL); @@ -1407,19 +1434,32 @@ static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma) { struct mt_gpdma_desc *gpd = dma->gpd; struct mt_bdma_desc *bd = dma->bd; + dma_addr_t dma_addr; int i; memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2); + dma_addr = dma->gpd_addr + sizeof(struct mt_gpdma_desc); gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */ - gpd->ptr = (u32)dma->bd_addr; /* physical address */ /* gpd->next is must set for desc DMA * That's why must alloc 2 gpd structure. */ - gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc); + gpd->next = lower_32_bits(dma_addr); + if (host->dev_comp->support_64g) + gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 24; + + dma_addr = dma->bd_addr; + gpd->ptr = lower_32_bits(dma->bd_addr); /* physical address */ + if (host->dev_comp->support_64g) + gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 28; + memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM); - for (i = 0; i < (MAX_BD_NUM - 1); i++) - bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1); + for (i = 0; i < (MAX_BD_NUM - 1); i++) { + dma_addr = dma->bd_addr + sizeof(*bd) * (i + 1); + bd[i].next = lower_32_bits(dma_addr); + if (host->dev_comp->support_64g) + bd[i].bd_info |= (upper_32_bits(dma_addr) & 0xf) << 24; + } } static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -1820,7 +1860,6 @@ static int msdc_drv_probe(struct platform_device *pdev) struct mmc_host *mmc; struct msdc_host *host; struct resource *res; - const struct of_device_id *of_id; int ret; if (!pdev->dev.of_node) { @@ -1828,9 +1867,6 @@ static int msdc_drv_probe(struct platform_device *pdev) return -EINVAL; } - of_id = of_match_node(msdc_of_ids, pdev->dev.of_node); - if (!of_id) - return -EINVAL; /* Allocate MMC host for this device */ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev); if (!mmc) @@ -1899,7 +1935,7 @@ static int msdc_drv_probe(struct platform_device *pdev) msdc_of_property_parse(pdev, host); host->dev = &pdev->dev; - host->dev_comp = of_id->data; + host->dev_comp = of_device_get_match_data(&pdev->dev); host->mmc = mmc; host->src_clk_freq = clk_get_rate(host->src_clk); /* Set host parameters to mmc */ @@ -1916,7 +1952,10 @@ static int msdc_drv_probe(struct platform_device *pdev) mmc->max_blk_size = 2048; mmc->max_req_size = 512 * 1024; mmc->max_blk_count = mmc->max_req_size / 512; - host->dma_mask = DMA_BIT_MASK(32); + if (host->dev_comp->support_64g) + host->dma_mask = DMA_BIT_MASK(36); + else + host->dma_mask = DMA_BIT_MASK(32); mmc_dev(mmc)->dma_mask = &host->dma_mask; host->timeout_clks = 3 * 1048576; diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 6af946d16d24..a6bf12348bdc 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -91,7 +91,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { .scc_offset = 0x1000, .taps = rcar_gen3_scc_taps, .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), - /* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */ + /* DMAC can handle 0xffffffff blk count but only 1 segment */ .max_blk_count = 0xffffffff, .max_segs = 1, }; @@ -157,38 +157,34 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, { struct scatterlist *sg = host->sg_ptr; u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE; - enum dma_data_direction dir; - int ret; - /* This DMAC cannot handle if sg_len is not 1 */ - WARN_ON(host->sg_len > 1); + if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len, + mmc_get_dma_dir(data))) + goto force_pio; /* This DMAC cannot handle if buffer is not 8-bytes alignment */ - if (!IS_ALIGNED(sg->offset, 8)) + if (!IS_ALIGNED(sg_dma_address(sg), 8)) { + dma_unmap_sg(&host->pdev->dev, sg, host->sg_len, + mmc_get_dma_dir(data)); goto force_pio; + } if (data->flags & MMC_DATA_READ) { dtran_mode |= DTRAN_MODE_CH_NUM_CH1; - dir = DMA_FROM_DEVICE; if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) && test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags)) goto force_pio; } else { dtran_mode |= DTRAN_MODE_CH_NUM_CH0; - dir = DMA_TO_DEVICE; } - ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir); - if (ret == 0) - goto force_pio; - renesas_sdhi_internal_dmac_enable_dma(host, true); /* set dma parameters */ renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE, dtran_mode); renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR, - sg->dma_address); + sg_dma_address(sg)); return; @@ -272,12 +268,16 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = { * implementation as others may use a different implementation. */ static const struct soc_device_attribute gen3_soc_whitelist[] = { + /* specific ones */ { .soc_id = "r8a7795", .revision = "ES1.*", .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, - { .soc_id = "r8a7795", .revision = "ES2.0" }, { .soc_id = "r8a7796", .revision = "ES1.0", .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, - { .soc_id = "r8a77995", .revision = "ES1.0" }, + /* generic ones */ + { .soc_id = "r8a7795" }, + { .soc_id = "r8a7796" }, + { .soc_id = "r8a77980" }, + { .soc_id = "r8a77995" }, { /* sentinel */ } }; diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 0f589e26ee63..7a343b87b5e5 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -253,6 +253,7 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06; u32 tmp; + int i, ret; if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) return -EINVAL; @@ -260,11 +261,24 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) tmp = readl(reg); tmp &= ~SDHCI_CDNS_HRS06_TUNE; tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val); - tmp |= SDHCI_CDNS_HRS06_TUNE_UP; - writel(tmp, reg); - return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), - 0, 1); + /* + * Workaround for IP errata: + * The IP6116 SD/eMMC PHY design has a timing issue on receive data + * path. Send tune request twice. + */ + for (i = 0; i < 2; i++) { + tmp |= SDHCI_CDNS_HRS06_TUNE_UP; + writel(tmp, reg); + + ret = readl_poll_timeout(reg, tmp, + !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), + 0, 1); + if (ret) + return ret; + } + + return 0; } static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index cd2b5f643a15..d6aef70d34fa 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -41,6 +41,12 @@ #define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8) #define ESDHC_WTMK_LVL 0x44 #define ESDHC_WTMK_DEFAULT_VAL 0x10401040 +#define ESDHC_WTMK_LVL_RD_WML_MASK 0x000000FF +#define ESDHC_WTMK_LVL_RD_WML_SHIFT 0 +#define ESDHC_WTMK_LVL_WR_WML_MASK 0x00FF0000 +#define ESDHC_WTMK_LVL_WR_WML_SHIFT 16 +#define ESDHC_WTMK_LVL_WML_VAL_DEF 64 +#define ESDHC_WTMK_LVL_WML_VAL_MAX 128 #define ESDHC_MIX_CTRL 0x48 #define ESDHC_MIX_CTRL_DDREN (1 << 3) #define ESDHC_MIX_CTRL_AC23EN (1 << 7) @@ -516,6 +522,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } if (esdhc_is_usdhc(imx_data)) { + u32 wml; u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); /* Swap AC23 bit */ if (val & SDHCI_TRNS_AUTO_CMD23) { @@ -524,6 +531,21 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK); writel(m, host->ioaddr + ESDHC_MIX_CTRL); + + /* Set watermark levels for PIO access to maximum value + * (128 words) to accommodate full 512 bytes buffer. + * For DMA access restore the levels to default value. + */ + m = readl(host->ioaddr + ESDHC_WTMK_LVL); + if (val & SDHCI_TRNS_DMA) + wml = ESDHC_WTMK_LVL_WML_VAL_DEF; + else + wml = ESDHC_WTMK_LVL_WML_VAL_MAX; + m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK | + ESDHC_WTMK_LVL_WR_WML_MASK); + m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) | + (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT); + writel(m, host->ioaddr + ESDHC_WTMK_LVL); } else { /* * Postpone this write, we must do it together with a diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291db705..bb11916c58a1 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/iopoll.h> +#include <linux/regulator/consumer.h> #include "sdhci-pltfm.h" @@ -77,10 +78,16 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400 (6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) +#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIB BIT(16) #define CORE_HW_AUTOCAL_ENA BIT(17) @@ -148,6 +155,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1103,8 +1111,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; int retry = 10; - int pwr_state = 0, io_level = 0; - + u32 pwr_state = 0, io_level = 0; + u32 config; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; @@ -1161,6 +1169,38 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* + * If we don't have info regarding the voltage levels supported by + * regulators, don't change the IO PAD PWR SWITCH. + */ + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + u32 new_config; + /* + * We should unset IO PAD PWR switch only if the register write + * can set IO lines high and the regulator also switches to 3 V. + * Else, we should keep the IO PAD PWR switch set. + * This is applicable to certain targets where eMMC vccq supply + * is only 1.8V. In such targets, even during REQ_IO_HIGH, the + * IO PAD PWR switch must be kept set to reflect actual + * regulator voltage. This way, during initialization of + * controllers with only 1.8V, we will set the IO PAD bit + * without waiting for a REQ_IO_LOW. + */ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + new_config = config; + + if ((io_level & REQ_IO_HIGH) && + (msm_host->caps_0 & CORE_3_0V_SUPPORT)) + new_config &= ~CORE_IO_PAD_PWR_SWITCH; + else if ((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) + new_config |= CORE_IO_PAD_PWR_SWITCH; + + if (config ^ new_config) + writel_relaxed(new_config, + host->ioaddr + CORE_VENDOR_SPEC); + } + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) @@ -1313,6 +1353,45 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + u32 caps = 0, config; + struct sdhci_host *host = mmc_priv(mmc); + + if (!IS_ERR(mmc->supply.vqmmc)) { + if (regulator_is_supported_voltage(supply, 1700000, 1950000)) + caps |= CORE_1_8V_SUPPORT; + if (regulator_is_supported_voltage(supply, 2700000, 3600000)) + caps |= CORE_3_0V_SUPPORT; + + if (!caps) + pr_warn("%s: 1.8/3V not supported for vqmmc\n", + mmc_hostname(mmc)); + } + + if (caps) { + /* + * Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH + * bit can be used as required later on. + */ + u32 io_level = msm_host->curr_io_level; + + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config |= CORE_IO_PAD_PWR_SWITCH_EN; + + if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT)) + config &= ~CORE_IO_PAD_PWR_SWITCH; + else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT)) + config |= CORE_IO_PAD_PWR_SWITCH; + + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + } + msm_host->caps_0 |= caps; + pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1609,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + sdhci_msm_set_regulator_caps(msm_host); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index c33a5f7393bd..e3332a522a5d 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -290,7 +290,8 @@ static const struct sdhci_pltfm_data sdhci_arasan_pdata = { .ops = &sdhci_arasan_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, }; static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask) @@ -359,8 +360,7 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = { */ static int sdhci_arasan_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); int ret; @@ -403,8 +403,7 @@ static int sdhci_arasan_suspend(struct device *dev) */ static int sdhci_arasan_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); int ret; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 78c25ad35fd2..77dd3521daae 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -453,6 +453,7 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = { enum { INTEL_DSM_FNS = 0, INTEL_DSM_V18_SWITCH = 3, + INTEL_DSM_V33_SWITCH = 4, INTEL_DSM_DRV_STRENGTH = 9, INTEL_DSM_D3_RETUNE = 10, }; @@ -620,17 +621,37 @@ static void intel_hs400_enhanced_strobe(struct mmc_host *mmc, sdhci_writel(host, val, INTEL_HS400_ES_REG); } -static void sdhci_intel_voltage_switch(struct sdhci_host *host) +static int intel_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) { + struct device *dev = mmc_dev(mmc); + struct sdhci_host *host = mmc_priv(mmc); struct sdhci_pci_slot *slot = sdhci_priv(host); struct intel_host *intel_host = sdhci_pci_priv(slot); - struct device *dev = &slot->chip->pdev->dev; + unsigned int fn; u32 result = 0; int err; - err = intel_dsm(intel_host, dev, INTEL_DSM_V18_SWITCH, &result); - pr_debug("%s: %s DSM error %d result %u\n", - mmc_hostname(host->mmc), __func__, err, result); + err = sdhci_start_signal_voltage_switch(mmc, ios); + if (err) + return err; + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + fn = INTEL_DSM_V33_SWITCH; + break; + case MMC_SIGNAL_VOLTAGE_180: + fn = INTEL_DSM_V18_SWITCH; + break; + default: + return 0; + } + + err = intel_dsm(intel_host, dev, fn, &result); + pr_debug("%s: %s DSM fn %u error %d result %u\n", + mmc_hostname(mmc), __func__, fn, err, result); + + return 0; } static const struct sdhci_ops sdhci_intel_byt_ops = { @@ -641,7 +662,6 @@ static const struct sdhci_ops sdhci_intel_byt_ops = { .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, - .voltage_switch = sdhci_intel_voltage_switch, }; static const struct sdhci_ops sdhci_intel_glk_ops = { @@ -652,7 +672,6 @@ static const struct sdhci_ops sdhci_intel_glk_ops = { .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, - .voltage_switch = sdhci_intel_voltage_switch, .irq = sdhci_cqhci_irq, }; @@ -691,6 +710,7 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot) byt_read_dsm(slot); ops->execute_tuning = intel_execute_tuning; + ops->start_signal_voltage_switch = intel_start_signal_voltage_switch; } static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) @@ -832,6 +852,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_SD) slot->host->mmc_host_ops.get_cd = bxt_get_cd; + if (slot->chip->pdev->subsystem_vendor == PCI_VENDOR_ID_NI && + slot->chip->pdev->subsystem_device == PCI_SUBDEVICE_ID_NI_78E3) + slot->host->mmc->caps2 |= MMC_CAP2_AVOID_3_3V; + return 0; } diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 5cbcdc448f98..db9cb54ef700 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -54,6 +54,7 @@ #define PCI_DEVICE_ID_REALTEK_5250 0x5250 #define PCI_SUBDEVICE_ID_NI_7884 0x7884 +#define PCI_SUBDEVICE_ID_NI_78E3 0x78e3 #define PCI_VENDOR_ID_ARASAN 0x16e6 #define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670 diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 20cfb20418f3..97c6b79b7d6f 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -13,36 +13,34 @@ * the License, or (at your option) any later version. */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/err.h> - #include <linux/clk.h> #include <linux/clk/sunxi-ng.h> -#include <linux/gpio.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/scatterlist.h> +#include <linux/delay.h> +#include <linux/device.h> #include <linux/dma-mapping.h> -#include <linux/slab.h> -#include <linux/reset.h> -#include <linux/regulator/consumer.h> - -#include <linux/of_address.h> -#include <linux/of_gpio.h> -#include <linux/of_platform.h> - +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mmc/card.h> +#include <linux/mmc/core.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> #include <linux/mmc/sdio.h> -#include <linux/mmc/mmc.h> -#include <linux/mmc/core.h> -#include <linux/mmc/card.h> #include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include <linux/spinlock.h> /* register offset definitions */ #define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */ @@ -322,10 +320,9 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host) return 0; } -static int sunxi_mmc_init_host(struct mmc_host *mmc) +static int sunxi_mmc_init_host(struct sunxi_mmc_host *host) { u32 rval; - struct sunxi_mmc_host *host = mmc_priv(mmc); if (sunxi_mmc_reset_host(host)) return -EIO; @@ -859,17 +856,48 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, return 0; } -static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +static void sunxi_mmc_set_bus_width(struct sunxi_mmc_host *host, + unsigned char width) +{ + switch (width) { + case MMC_BUS_WIDTH_1: + mmc_writel(host, REG_WIDTH, SDXC_WIDTH1); + break; + case MMC_BUS_WIDTH_4: + mmc_writel(host, REG_WIDTH, SDXC_WIDTH4); + break; + case MMC_BUS_WIDTH_8: + mmc_writel(host, REG_WIDTH, SDXC_WIDTH8); + break; + } +} + +static void sunxi_mmc_set_clk(struct sunxi_mmc_host *host, struct mmc_ios *ios) { - struct sunxi_mmc_host *host = mmc_priv(mmc); u32 rval; - /* Set the power state */ - switch (ios->power_mode) { - case MMC_POWER_ON: - break; + /* set ddr mode */ + rval = mmc_readl(host, REG_GCTRL); + if (ios->timing == MMC_TIMING_UHS_DDR50 || + ios->timing == MMC_TIMING_MMC_DDR52) + rval |= SDXC_DDR_MODE; + else + rval &= ~SDXC_DDR_MODE; + mmc_writel(host, REG_GCTRL, rval); + + host->ferror = sunxi_mmc_clk_set_rate(host, ios); + /* Android code had a usleep_range(50000, 55000); here */ +} +static void sunxi_mmc_card_power(struct sunxi_mmc_host *host, + struct mmc_ios *ios) +{ + struct mmc_host *mmc = host->mmc; + + switch (ios->power_mode) { case MMC_POWER_UP: + dev_dbg(mmc_dev(mmc), "Powering card up\n"); + if (!IS_ERR(mmc->supply.vmmc)) { host->ferror = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, @@ -887,53 +915,33 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } host->vqmmc_enabled = true; } - - host->ferror = sunxi_mmc_init_host(mmc); - if (host->ferror) - return; - - dev_dbg(mmc_dev(mmc), "power on!\n"); break; case MMC_POWER_OFF: - dev_dbg(mmc_dev(mmc), "power off!\n"); - sunxi_mmc_reset_host(host); + dev_dbg(mmc_dev(mmc), "Powering card off\n"); + if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; break; - } - /* set bus width */ - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - mmc_writel(host, REG_WIDTH, SDXC_WIDTH1); - break; - case MMC_BUS_WIDTH_4: - mmc_writel(host, REG_WIDTH, SDXC_WIDTH4); - break; - case MMC_BUS_WIDTH_8: - mmc_writel(host, REG_WIDTH, SDXC_WIDTH8); + default: + dev_dbg(mmc_dev(mmc), "Ignoring unknown card power state\n"); break; } +} - /* set ddr mode */ - rval = mmc_readl(host, REG_GCTRL); - if (ios->timing == MMC_TIMING_UHS_DDR50 || - ios->timing == MMC_TIMING_MMC_DDR52) - rval |= SDXC_DDR_MODE; - else - rval &= ~SDXC_DDR_MODE; - mmc_writel(host, REG_GCTRL, rval); +static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sunxi_mmc_host *host = mmc_priv(mmc); - /* set up clock */ - if (ios->power_mode) { - host->ferror = sunxi_mmc_clk_set_rate(host, ios); - /* Android code had a usleep_range(50000, 55000); here */ - } + sunxi_mmc_card_power(host, ios); + sunxi_mmc_set_bus_width(host, ios->bus_width); + sunxi_mmc_set_clk(host, ios); } static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) @@ -955,6 +963,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) unsigned long flags; u32 imask; + if (enable) + pm_runtime_get_noresume(host->dev); + spin_lock_irqsave(&host->lock, flags); imask = mmc_readl(host, REG_IMASK); @@ -967,6 +978,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) } mmc_writel(host, REG_IMASK, imask); spin_unlock_irqrestore(&host->lock, flags); + + if (!enable) + pm_runtime_put_noidle(host->mmc->parent); } static void sunxi_mmc_hw_reset(struct mmc_host *mmc) @@ -1380,6 +1394,15 @@ static int sunxi_mmc_probe(struct platform_device *pdev) if (ret) goto error_free_dma; + ret = sunxi_mmc_init_host(host); + if (ret) + goto error_free_dma; + + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = mmc_add_host(mmc); if (ret) goto error_free_dma; @@ -1400,6 +1423,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev) struct sunxi_mmc_host *host = mmc_priv(mmc); mmc_remove_host(mmc); + pm_runtime_force_suspend(&pdev->dev); disable_irq(host->irq); sunxi_mmc_disable(host); dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); @@ -1408,10 +1432,45 @@ static int sunxi_mmc_remove(struct platform_device *pdev) return 0; } +static int sunxi_mmc_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct sunxi_mmc_host *host = mmc_priv(mmc); + int ret; + + ret = sunxi_mmc_enable(host); + if (ret) + return ret; + + sunxi_mmc_init_host(host); + sunxi_mmc_set_bus_width(host, mmc->ios.bus_width); + sunxi_mmc_set_clk(host, &mmc->ios); + + return 0; +} + +static int sunxi_mmc_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct sunxi_mmc_host *host = mmc_priv(mmc); + + sunxi_mmc_reset_host(host); + sunxi_mmc_disable(host); + + return 0; +} + +static const struct dev_pm_ops sunxi_mmc_pm_ops = { + SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, + sunxi_mmc_runtime_resume, + NULL) +}; + static struct platform_driver sunxi_mmc_driver = { .driver = { .name = "sunxi-mmc", .of_match_table = of_match_ptr(sunxi_mmc_of_match), + .pm = &sunxi_mmc_pm_ops, }, .probe = sunxi_mmc_probe, .remove = sunxi_mmc_remove, diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index fd30ac7da5e5..3ba42f508014 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -928,8 +928,7 @@ static int wmt_mci_remove(struct platform_device *pdev) static int wmt_mci_suspend(struct device *dev) { u32 reg_tmp; - struct platform_device *pdev = to_platform_device(dev); - struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mmc_host *mmc = dev_get_drvdata(dev); struct wmt_mci_priv *priv; if (!mmc) @@ -953,8 +952,7 @@ static int wmt_mci_suspend(struct device *dev) static int wmt_mci_resume(struct device *dev) { u32 reg_tmp; - struct platform_device *pdev = to_platform_device(dev); - struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mmc_host *mmc = dev_get_drvdata(dev); struct wmt_mci_priv *priv; if (mmc) { |