MMC core:

- Fix a few memoryleaks
  - Minor improvements to the card initialization sequence
  - Partially support sleepy GPIO controllers for pwrseq eMMC
 
 MMC host:
  - alcor: Work with multiple-entry sglists
  - alcor: Enable DMA for writes
  - meson-gx: Improve tuning support
  - meson-gx: Avoid clock glitch when switching to DDR modes
  - meson-gx: Disable unreliable HS400 mode
  - mmci: Minor updates for support of HW busy detection
  - mmci: Support data transfers for the stm32_sdmmc variant
  - mmci: Restructure code to better support different variants
  - mtk-sd: Add support for version found on MT7620 family SOCs
  - mtk-sd: Add support for the MT8516 version
  - mtk-sd: Add Chaotian Jing as the maintainer
  - sdhci: Reorganize request-code to convert from tasklet to workqueue
  - sdhci_am654: Stabilize support for lower speed modes
  - sdhci-esdhc-imx: Add HS400 support for iMX7ULP
  - sdhci-esdhc-imx: Add support for iMX7ULP version
  - sdhci-of-arasan: Allow to disable DCMDs via DT for CQE
  - sdhci-of-esdhc: Add support for the ls1028a version
  - sdhci-of-esdhc: Several fixups for errata
  - sdhci-pci: Fix BYT OCP setting
  - sdhci-pci: Add support for Intel CML
  - sdhci-tegra: Add support for system suspend/resume
  - sdhci-tegra: Add CQE support for Tegra186 WAR
  - sdhci-tegra: Add support for Tegra194
  - sdhci-tegra: Update HW tuning process
 
 MEMSTICK:
  - I volunteered to help as a maintainer for the memstick subsystem, which is
    reflected by an update to the MAINTAINERS file. Changes are funneled through
    my MMC git and we will use the linux-mmc mailing list.
 
 MEMSTICK host:
  - A few minor cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAlzRJJsXHHVsZi5oYW5z
 c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCmhixAArz1QWF0LlpiHTqop0pPgzbKj
 Z3LwQ/iyawPWpmU4SckcuBpSNTQ47I22Mzx7VPXeRyJt84T8MDlHfgA5Kc/VbRHf
 G+hSUYpcw63xNnzuWy54QxsW03PgjcSKe40bOsbGsKLhm3DAY9uuWiJnqCX7hIrY
 ALn3uQgeWEfJkUGwctvqe6883PBRNeu6Rwgb0IFN+rdIGBIVf6zAkTceK6m5MDoQ
 iJIj5tq+z4excDQryeEpFM3kcI8lm7Eoyb35DhWVeji0sTVXgQINbkJMmFV+O2bl
 hJMkpv5Zn2tqT6485McQuSdvLlTBi9FD7QJQQOgekgRUaq/FPS0N5dnhD2vK5kLs
 cLOcb9LAXeHu/Xzos2aQ2Km7y2ezXH5bLda1QLeoizKwf0b7p+qtQN32cGHEKkEN
 hZPTsiJiu5wgkHdkoAFp7yxf9H0TZW3rYgL0zmj31VXNChZ4oyEAYPUoUGuJbm6h
 7WdXvAw19MkDl4eCHkUYqr8nwRgoBDEUYjjcc8ObsoGM1zgF3JR38Ke4dqAQZAI1
 8xGI46tboQ3bKVC0uai7tO4DscJ1X/Mu5tUdk2So1F38vZ/fJFky/gnzZRoQURgY
 JLB/CTbRjfpaCm3ny5MtsAGvz0AkEWbOVqOArzAMpVUZhd+pRhdOtTWjrTCUKfrf
 WRPjbDiIDN8EFLrKVXU=
 =Ompt
 -----END PGP SIGNATURE-----

Merge tag 'mmc-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Fix a few memoryleaks
   - Minor improvements to the card initialization sequence
   - Partially support sleepy GPIO controllers for pwrseq eMMC

  MMC host:
   - alcor: Work with multiple-entry sglists
   - alcor: Enable DMA for writes
   - meson-gx: Improve tuning support
   - meson-gx: Avoid clock glitch when switching to DDR modes
   - meson-gx: Disable unreliable HS400 mode
   - mmci: Minor updates for support of HW busy detection
   - mmci: Support data transfers for the stm32_sdmmc variant
   - mmci: Restructure code to better support different variants
   - mtk-sd: Add support for version found on MT7620 family SOCs
   - mtk-sd: Add support for the MT8516 version
   - mtk-sd: Add Chaotian Jing as the maintainer
   - sdhci: Reorganize request-code to convert from tasklet to workqueue
   - sdhci_am654: Stabilize support for lower speed modes
   - sdhci-esdhc-imx: Add HS400 support for iMX7ULP
   - sdhci-esdhc-imx: Add support for iMX7ULP version
   - sdhci-of-arasan: Allow to disable DCMDs via DT for CQE
   - sdhci-of-esdhc: Add support for the ls1028a version
   - sdhci-of-esdhc: Several fixups for errata
   - sdhci-pci: Fix BYT OCP setting
   - sdhci-pci: Add support for Intel CML
   - sdhci-tegra: Add support for system suspend/resume
   - sdhci-tegra: Add CQE support for Tegra186 WAR
   - sdhci-tegra: Add support for Tegra194
   - sdhci-tegra: Update HW tuning process

  MEMSTICK:
   - I volunteered to help as a maintainer for the memstick subsystem,
     which is reflected by an update to the MAINTAINERS file. Changes
     are funneled through my MMC git and we will use the linux-mmc
     mailing list.

  MEMSTICK host:
   - A few minor cleanups"

* tag 'mmc-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (87 commits)
  mmc: sdhci-pci: Fix BYT OCP setting
  dt-bindings: mmc: add DT bindings for ls1028a eSDHC host controller
  mmc: alcor: Drop pointer to mmc_host from alcor_sdmmc_host
  mmc: mtk-sd: select REGULATOR
  mmc: mtk-sd: enable internal card-detect logic.
  mmc: mtk-sd: add support for config found in mt7620 family SOCs.
  mmc: mtk-sd: don't hard-code interrupt trigger type
  mmc: core: Fix tag set memory leak
  dt-bindings: mmc: Add support for MT8516 to mtk-sd
  mmc: mmci: Prevent polling for busy detection in IRQ context
  mmc: mmci: Cleanup mmci_cmd_irq() for busy detect
  mmc: usdhi6rol0: mark expected switch fall-throughs
  mmc: core: Verify SD bus width
  mmc: sdhci-esdhc-imx: Add HS400 support for iMX7ULP
  mmc: sdhci-esdhc-imx: add pm_qos to interact with cpuidle
  dt-bindings: mmc: fsl-imx-esdhc: add imx7ulp compatible string
  mmc: meson-gx: add signal resampling tuning
  mmc: meson-gx: remove Rx phase tuning
  mmc: meson-gx: avoid clock glitch when switching to DDR modes
  mmc: meson-gx: disable HS400
  ...
This commit is contained in:
Linus Torvalds 2019-05-07 12:56:19 -07:00
commit 01e5d1830c
46 changed files with 1185 additions and 681 deletions

View File

@ -17,6 +17,7 @@ Required properties:
"fsl,t4240-esdhc" "fsl,t4240-esdhc"
Possible compatibles for ARM: Possible compatibles for ARM:
"fsl,ls1012a-esdhc" "fsl,ls1012a-esdhc"
"fsl,ls1028a-esdhc"
"fsl,ls1088a-esdhc" "fsl,ls1088a-esdhc"
"fsl,ls1043a-esdhc" "fsl,ls1043a-esdhc"
"fsl,ls1046a-esdhc" "fsl,ls1046a-esdhc"

View File

@ -17,6 +17,7 @@ Required properties:
"fsl,imx6sx-usdhc" "fsl,imx6sx-usdhc"
"fsl,imx6ull-usdhc" "fsl,imx6ull-usdhc"
"fsl,imx7d-usdhc" "fsl,imx7d-usdhc"
"fsl,imx7ulp-usdhc"
"fsl,imx8qxp-usdhc" "fsl,imx8qxp-usdhc"
Optional properties: Optional properties:

View File

@ -64,6 +64,8 @@ Optional properties:
whether pwrseq-simple is used. Default to 10ms if no available. whether pwrseq-simple is used. Default to 10ms if no available.
- supports-cqe : The presence of this property indicates that the corresponding - supports-cqe : The presence of this property indicates that the corresponding
MMC host controller supports HW command queue feature. MMC host controller supports HW command queue feature.
- disable-cqe-dcmd: This property indicates that the MMC controller's command
queue engine (CQE) does not support direct commands (DCMDs).
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted" polarity properties, we have to fix the meaning of the "normal" and "inverted"

View File

@ -11,10 +11,12 @@ Required properties:
"mediatek,mt8135-mmc": for mmc host ip compatible with mt8135 "mediatek,mt8135-mmc": for mmc host ip compatible with mt8135
"mediatek,mt8173-mmc": for mmc host ip compatible with mt8173 "mediatek,mt8173-mmc": for mmc host ip compatible with mt8173
"mediatek,mt8183-mmc": for mmc host ip compatible with mt8183 "mediatek,mt8183-mmc": for mmc host ip compatible with mt8183
"mediatek,mt8516-mmc": for mmc host ip compatible with mt8516
"mediatek,mt2701-mmc": for mmc host ip compatible with mt2701 "mediatek,mt2701-mmc": for mmc host ip compatible with mt2701
"mediatek,mt2712-mmc": for mmc host ip compatible with mt2712 "mediatek,mt2712-mmc": for mmc host ip compatible with mt2712
"mediatek,mt7622-mmc": for MT7622 SoC "mediatek,mt7622-mmc": for MT7622 SoC
"mediatek,mt7623-mmc", "mediatek,mt2701-mmc": for MT7623 SoC "mediatek,mt7623-mmc", "mediatek,mt2701-mmc": for MT7623 SoC
"mediatek,mt7620-mmc", for MT7621 SoC (and others)
- reg: physical base address of the controller and length - reg: physical base address of the controller and length
- interrupts: Should contain MSDC interrupt number - interrupts: Should contain MSDC interrupt number

View File

@ -14,6 +14,7 @@ Required properties:
- "nvidia,tegra124-sdhci": for Tegra124 and Tegra132 - "nvidia,tegra124-sdhci": for Tegra124 and Tegra132
- "nvidia,tegra210-sdhci": for Tegra210 - "nvidia,tegra210-sdhci": for Tegra210
- "nvidia,tegra186-sdhci": for Tegra186 - "nvidia,tegra186-sdhci": for Tegra186
- "nvidia,tegra194-sdhci": for Tegra194
- clocks : Must contain one entry, for the module clock. - clocks : Must contain one entry, for the module clock.
See ../clocks/clock-bindings.txt for details. See ../clocks/clock-bindings.txt for details.
- resets : Must contain an entry for each entry in reset-names. - resets : Must contain an entry for each entry in reset-names.

View File

@ -9785,6 +9785,12 @@ F: drivers/media/platform/mtk-vpu/
F: Documentation/devicetree/bindings/media/mediatek-vcodec.txt F: Documentation/devicetree/bindings/media/mediatek-vcodec.txt
F: Documentation/devicetree/bindings/media/mediatek-vpu.txt F: Documentation/devicetree/bindings/media/mediatek-vpu.txt
MEDIATEK MMC/SD/SDIO DRIVER
M: Chaotian Jing <chaotian.jing@mediatek.com>
S: Maintained
F: drivers/mmc/host/mtk-sd.c
F: Documentation/devicetree/bindings/mmc/mtk-sd.txt
MEDIATEK MT76 WIRELESS LAN DRIVER MEDIATEK MT76 WIRELESS LAN DRIVER
M: Felix Fietkau <nbd@nbd.name> M: Felix Fietkau <nbd@nbd.name>
M: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> M: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
@ -14484,16 +14490,15 @@ T: git git://linuxtv.org/media_tree.git
S: Maintained S: Maintained
F: drivers/media/i2c/imx355.c F: drivers/media/i2c/imx355.c
SONY MEMORYSTICK CARD SUPPORT SONY MEMORYSTICK SUBSYSTEM
M: Alex Dubov <oakad@yahoo.com>
W: http://tifmxx.berlios.de/
S: Maintained
F: drivers/memstick/host/tifm_ms.c
SONY MEMORYSTICK STANDARD SUPPORT
M: Maxim Levitsky <maximlevitsky@gmail.com> M: Maxim Levitsky <maximlevitsky@gmail.com>
M: Alex Dubov <oakad@yahoo.com>
M: Ulf Hansson <ulf.hansson@linaro.org>
L: linux-mmc@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git
S: Maintained S: Maintained
F: drivers/memstick/core/ms_block.* F: drivers/memstick/
F: include/linux/memstick.h
SONY VAIO CONTROL DEVICE DRIVER SONY VAIO CONTROL DEVICE DRIVER
M: Mattia Dongili <malattia@linux.it> M: Mattia Dongili <malattia@linux.it>
@ -15518,9 +15523,11 @@ S: Maintained
F: drivers/net/ethernet/ti/cpsw* F: drivers/net/ethernet/ti/cpsw*
F: drivers/net/ethernet/ti/davinci* F: drivers/net/ethernet/ti/davinci*
TI FLASH MEDIA INTERFACE DRIVER TI FLASH MEDIA MEMORYSTICK/MMC DRIVERS
M: Alex Dubov <oakad@yahoo.com> M: Alex Dubov <oakad@yahoo.com>
S: Maintained S: Maintained
W: http://tifmxx.berlios.de/
F: drivers/memstick/host/tifm_ms.c
F: drivers/misc/tifm* F: drivers/misc/tifm*
F: drivers/mmc/host/tifm_sd.c F: drivers/mmc/host/tifm_sd.c
F: include/linux/tifm.h F: include/linux/tifm.h

View File

@ -370,7 +370,6 @@ static int jmb38x_ms_transfer_data(struct jmb38x_ms_host *host)
static int jmb38x_ms_issue_cmd(struct memstick_host *msh) static int jmb38x_ms_issue_cmd(struct memstick_host *msh)
{ {
struct jmb38x_ms_host *host = memstick_priv(msh); struct jmb38x_ms_host *host = memstick_priv(msh);
unsigned char *data;
unsigned int data_len, cmd, t_val; unsigned int data_len, cmd, t_val;
if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) { if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) {
@ -402,8 +401,6 @@ static int jmb38x_ms_issue_cmd(struct memstick_host *msh)
cmd |= TPC_WAIT_INT; cmd |= TPC_WAIT_INT;
} }
data = host->req->data;
if (!no_dma) if (!no_dma)
host->cmd_flags |= DMA_DATA; host->cmd_flags |= DMA_DATA;

View File

@ -256,7 +256,6 @@ static unsigned int tifm_ms_transfer_data(struct tifm_ms *host)
static int tifm_ms_issue_cmd(struct tifm_ms *host) static int tifm_ms_issue_cmd(struct tifm_ms *host)
{ {
struct tifm_dev *sock = host->dev; struct tifm_dev *sock = host->dev;
unsigned char *data;
unsigned int data_len, cmd, sys_param; unsigned int data_len, cmd, sys_param;
host->cmd_flags = 0; host->cmd_flags = 0;
@ -265,8 +264,6 @@ static int tifm_ms_issue_cmd(struct tifm_ms *host)
host->io_word = 0; host->io_word = 0;
host->cmd_flags = 0; host->cmd_flags = 0;
data = host->req->data;
host->use_dma = !no_dma; host->use_dma = !no_dma;
if (host->req->long_data) { if (host->req->long_data) {

View File

@ -363,11 +363,11 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
int num_ranges, i; int num_ranges, i;
voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges); voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
if (!voltage_ranges) { if (!voltage_ranges) {
pr_debug("%pOF: voltage-ranges unspecified\n", np); pr_debug("%pOF: voltage-ranges unspecified\n", np);
return 0; return 0;
} }
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
if (!num_ranges) { if (!num_ranges) {
pr_err("%pOF: voltage-ranges empty\n", np); pr_err("%pOF: voltage-ranges empty\n", np);
return -EINVAL; return -EINVAL;
@ -429,8 +429,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
if (mmc_gpio_alloc(host)) { if (mmc_gpio_alloc(host)) {
put_device(&host->class_dev); put_device(&host->class_dev);
ida_simple_remove(&mmc_host_ida, host->index);
kfree(host);
return NULL; return NULL;
} }

View File

@ -184,11 +184,7 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
if (err) if (err)
break; break;
/* if we're just probing, do a single pass */ /* wait until reset completes */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) { if (mmc_host_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE)) if (!(cmd.resp[0] & R1_SPI_IDLE))
break; break;
@ -200,6 +196,16 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
err = -ETIMEDOUT; err = -ETIMEDOUT;
mmc_delay(10); mmc_delay(10);
/*
* According to eMMC specification v5.1 section 6.4.3, we
* should issue CMD1 repeatedly in the idle state until
* the eMMC is ready. Otherwise some eMMC devices seem to enter
* the inactive mode after mmc_init_card() issued CMD0 when
* the eMMC device is busy.
*/
if (!ocr && !mmc_host_is_spi(host))
cmd.arg = cmd.resp[0] | BIT(30);
} }
if (rocr && !mmc_host_is_spi(host)) if (rocr && !mmc_host_is_spi(host))

View File

@ -30,19 +30,14 @@ struct mmc_pwrseq_emmc {
#define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq) #define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq)
static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
{
gpiod_set_value(pwrseq->reset_gpio, 1);
udelay(1);
gpiod_set_value(pwrseq->reset_gpio, 0);
udelay(200);
}
static void mmc_pwrseq_emmc_reset(struct mmc_host *host) static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
{ {
struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq); struct mmc_pwrseq_emmc *pwrseq = to_pwrseq_emmc(host->pwrseq);
__mmc_pwrseq_emmc_reset(pwrseq); gpiod_set_value_cansleep(pwrseq->reset_gpio, 1);
udelay(1);
gpiod_set_value_cansleep(pwrseq->reset_gpio, 0);
udelay(200);
} }
static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
@ -50,8 +45,11 @@ static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
{ {
struct mmc_pwrseq_emmc *pwrseq = container_of(this, struct mmc_pwrseq_emmc *pwrseq = container_of(this,
struct mmc_pwrseq_emmc, reset_nb); struct mmc_pwrseq_emmc, reset_nb);
gpiod_set_value(pwrseq->reset_gpio, 1);
udelay(1);
gpiod_set_value(pwrseq->reset_gpio, 0);
udelay(200);
__mmc_pwrseq_emmc_reset(pwrseq);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
@ -72,14 +70,18 @@ static int mmc_pwrseq_emmc_probe(struct platform_device *pdev)
if (IS_ERR(pwrseq->reset_gpio)) if (IS_ERR(pwrseq->reset_gpio))
return PTR_ERR(pwrseq->reset_gpio); return PTR_ERR(pwrseq->reset_gpio);
/* if (!gpiod_cansleep(pwrseq->reset_gpio)) {
* register reset handler to ensure emmc reset also from /*
* emergency_reboot(), priority 255 is the highest priority * register reset handler to ensure emmc reset also from
* so it will be executed before any system reboot handler. * emergency_reboot(), priority 255 is the highest priority
*/ * so it will be executed before any system reboot handler.
pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; */
pwrseq->reset_nb.priority = 255; pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
register_restart_handler(&pwrseq->reset_nb); pwrseq->reset_nb.priority = 255;
register_restart_handler(&pwrseq->reset_nb);
} else {
dev_notice(dev, "EMMC reset pin tied to a sleepy GPIO driver; reset on emergency-reboot disabled\n");
}
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
pwrseq->pwrseq.dev = dev; pwrseq->pwrseq.dev = dev;

View File

@ -472,6 +472,7 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
blk_mq_unquiesce_queue(q); blk_mq_unquiesce_queue(q);
blk_cleanup_queue(q); blk_cleanup_queue(q);
blk_mq_free_tag_set(&mq->tag_set);
/* /*
* A request can be completed before the next request, potentially * A request can be completed before the next request, potentially

View File

@ -221,6 +221,14 @@ static int mmc_decode_scr(struct mmc_card *card)
if (scr->sda_spec3) if (scr->sda_spec3)
scr->cmds = UNSTUFF_BITS(resp, 32, 2); scr->cmds = UNSTUFF_BITS(resp, 32, 2);
/* SD Spec says: any SD Card shall set at least bits 0 and 2 */
if (!(scr->bus_widths & SD_SCR_BUS_WIDTH_1) ||
!(scr->bus_widths & SD_SCR_BUS_WIDTH_4)) {
pr_err("%s: invalid bus width\n", mmc_hostname(card->host));
return -EINVAL;
}
return 0; return 0;
} }

View File

@ -92,6 +92,7 @@ config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus" tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI depends on MMC_SDHCI && PCI
select MMC_CQHCI select MMC_CQHCI
select IOSF_MBI if X86
help help
This selects the PCI Secure Digital Host Controller Interface. This selects the PCI Secure Digital Host Controller Interface.
Most controllers found today are PCI devices. Most controllers found today are PCI devices.
@ -437,7 +438,7 @@ config MMC_WBSD
depends on ISA_DMA_API depends on ISA_DMA_API
help help
This selects the Winbond(R) W83L51xD Secure digital and This selects the Winbond(R) W83L51xD Secure digital and
Multimedia card Interface. Multimedia card Interface.
If you have a machine with a integrated W83L518D or W83L519D If you have a machine with a integrated W83L518D or W83L519D
SD/MMC card reader, say Y or M here. SD/MMC card reader, say Y or M here.
@ -515,7 +516,7 @@ config MMC_TIFM_SD
'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
(TIFM_7XX1)'. (TIFM_7XX1)'.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called tifm_sd. module will be called tifm_sd.
config MMC_MVSDIO config MMC_MVSDIO
@ -531,12 +532,12 @@ config MMC_MVSDIO
module will be called mvsdio. module will be called mvsdio.
config MMC_DAVINCI config MMC_DAVINCI
tristate "TI DAVINCI Multimedia Card Interface support" tristate "TI DAVINCI Multimedia Card Interface support"
depends on ARCH_DAVINCI depends on ARCH_DAVINCI
help help
This selects the TI DAVINCI Multimedia card Interface. This selects the TI DAVINCI Multimedia card Interface.
If you have an DAVINCI board with a Multimedia Card slot, If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N. say Y or M here. If unsure, say N.
config MMC_GOLDFISH config MMC_GOLDFISH
tristate "goldfish qemu Multimedia Card Interface support" tristate "goldfish qemu Multimedia Card Interface support"
@ -565,18 +566,18 @@ config MMC_S3C
depends on S3C24XX_DMAC depends on S3C24XX_DMAC
help help
This selects a driver for the MCI interface found in This selects a driver for the MCI interface found in
Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs. Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
If you have a board based on one of those and a MMC/SD If you have a board based on one of those and a MMC/SD
slot, say Y or M here. slot, say Y or M here.
If unsure, say N. If unsure, say N.
config MMC_S3C_HW_SDIO_IRQ config MMC_S3C_HW_SDIO_IRQ
bool "Hardware support for SDIO IRQ" bool "Hardware support for SDIO IRQ"
depends on MMC_S3C depends on MMC_S3C
help help
Enable the hardware support for SDIO interrupts instead of using Enable the hardware support for SDIO interrupts instead of using
the generic polling code. the generic polling code.
choice choice
prompt "Samsung S3C SD/MMC transfer code" prompt "Samsung S3C SD/MMC transfer code"
@ -941,6 +942,7 @@ config MMC_BCM2835
config MMC_MTK config MMC_MTK
tristate "MediaTek SD/MMC Card Interface support" tristate "MediaTek SD/MMC Card Interface support"
depends on HAS_DMA depends on HAS_DMA
select REGULATOR
help help
This selects the MediaTek(R) Secure digital and Multimedia card Interface. This selects the MediaTek(R) Secure digital and Multimedia card Interface.
If you have a machine with a integrated SD/MMC card reader, say Y or M here. If you have a machine with a integrated SD/MMC card reader, say Y or M here.
@ -948,15 +950,16 @@ config MMC_MTK
If unsure, say N. If unsure, say N.
config MMC_SDHCI_MICROCHIP_PIC32 config MMC_SDHCI_MICROCHIP_PIC32
tristate "Microchip PIC32MZDA SDHCI support" tristate "Microchip PIC32MZDA SDHCI support"
depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM
help help
This selects the Secure Digital Host Controller Interface (SDHCI) This selects the Secure Digital Host Controller Interface (SDHCI)
for PIC32MZDA platform. for PIC32MZDA platform.
If you have a controller with this interface, say Y or M here. If you have a controller with this interface, say Y or M here.
If unsure, say N.
If unsure, say N.
config MMC_SDHCI_BRCMSTB config MMC_SDHCI_BRCMSTB
tristate "Broadcom SDIO/SD/MMC support" tristate "Broadcom SDIO/SD/MMC support"
depends on ARCH_BRCMSTB || BMIPS_GENERIC depends on ARCH_BRCMSTB || BMIPS_GENERIC
@ -993,6 +996,7 @@ config MMC_SDHCI_OMAP
config MMC_SDHCI_AM654 config MMC_SDHCI_AM654
tristate "Support for the SDHCI Controller in TI's AM654 SOCs" tristate "Support for the SDHCI Controller in TI's AM654 SOCs"
depends on MMC_SDHCI_PLTFM && OF depends on MMC_SDHCI_PLTFM && OF
select MMC_SDHCI_IO_ACCESSORS
help help
This selects the Secure Digital Host Controller Interface (SDHCI) This selects the Secure Digital Host Controller Interface (SDHCI)
support present in TI's AM654 SOCs. The controller supports support present in TI's AM654 SOCs. The controller supports

View File

@ -43,7 +43,6 @@ struct alcor_sdmmc_host {
struct device *dev; struct device *dev;
struct alcor_pci_priv *alcor_pci; struct alcor_pci_priv *alcor_pci;
struct mmc_host *mmc;
struct mmc_request *mrq; struct mmc_request *mrq;
struct mmc_command *cmd; struct mmc_command *cmd;
struct mmc_data *data; struct mmc_data *data;
@ -117,6 +116,9 @@ static void alcor_reset(struct alcor_sdmmc_host *host, u8 val)
dev_err(host->dev, "%s: timeout\n", __func__); dev_err(host->dev, "%s: timeout\n", __func__);
} }
/*
* Perform DMA I/O of a single page.
*/
static void alcor_data_set_dma(struct alcor_sdmmc_host *host) static void alcor_data_set_dma(struct alcor_sdmmc_host *host)
{ {
struct alcor_pci_priv *priv = host->alcor_pci; struct alcor_pci_priv *priv = host->alcor_pci;
@ -153,12 +155,26 @@ static void alcor_trigger_data_transfer(struct alcor_sdmmc_host *host)
ctrl |= AU6601_DATA_WRITE; ctrl |= AU6601_DATA_WRITE;
if (data->host_cookie == COOKIE_MAPPED) { if (data->host_cookie == COOKIE_MAPPED) {
/*
* For DMA transfers, this function is called just once,
* at the start of the operation. The hardware can only
* perform DMA I/O on a single page at a time, so here
* we kick off the transfer with the first page, and expect
* subsequent pages to be transferred upon IRQ events
* indicating that the single-page DMA was completed.
*/
alcor_data_set_dma(host); alcor_data_set_dma(host);
ctrl |= AU6601_DATA_DMA_MODE; ctrl |= AU6601_DATA_DMA_MODE;
host->dma_on = 1; host->dma_on = 1;
alcor_write32(priv, data->sg_count * 0x1000, alcor_write32(priv, data->sg_count * 0x1000,
AU6601_REG_BLOCK_SIZE); AU6601_REG_BLOCK_SIZE);
} else { } else {
/*
* For PIO transfers, we break down each operation
* into several sector-sized transfers. When one sector has
* complete, the IRQ handler will call this function again
* to kick off the transfer of the next sector.
*/
alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE); alcor_write32(priv, data->blksz, AU6601_REG_BLOCK_SIZE);
} }
@ -276,7 +292,7 @@ static void alcor_send_cmd(struct alcor_sdmmc_host *host,
break; break;
default: default:
dev_err(host->dev, "%s: cmd->flag (0x%02x) is not valid\n", dev_err(host->dev, "%s: cmd->flag (0x%02x) is not valid\n",
mmc_hostname(host->mmc), mmc_resp_type(cmd)); mmc_hostname(mmc_from_priv(host)), mmc_resp_type(cmd));
break; break;
} }
@ -317,7 +333,7 @@ static void alcor_request_complete(struct alcor_sdmmc_host *host,
host->data = NULL; host->data = NULL;
host->dma_on = 0; host->dma_on = 0;
mmc_request_done(host->mmc, mrq); mmc_request_done(mmc_from_priv(host), mrq);
} }
static void alcor_finish_data(struct alcor_sdmmc_host *host) static void alcor_finish_data(struct alcor_sdmmc_host *host)
@ -547,7 +563,7 @@ static void alcor_cd_irq(struct alcor_sdmmc_host *host, u32 intmask)
alcor_request_complete(host, 1); alcor_request_complete(host, 1);
} }
mmc_detect_change(host->mmc, msecs_to_jiffies(1)); mmc_detect_change(mmc_from_priv(host), msecs_to_jiffies(1));
} }
static irqreturn_t alcor_irq_thread(int irq, void *d) static irqreturn_t alcor_irq_thread(int irq, void *d)
@ -771,12 +787,17 @@ static void alcor_pre_req(struct mmc_host *mmc,
data->host_cookie = COOKIE_UNMAPPED; data->host_cookie = COOKIE_UNMAPPED;
/* FIXME: looks like the DMA engine works only with CMD18 */ /* FIXME: looks like the DMA engine works only with CMD18 */
if (cmd->opcode != 18) if (cmd->opcode != MMC_READ_MULTIPLE_BLOCK
&& cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK)
return; return;
/* /*
* We don't do DMA on "complex" transfers, i.e. with * We don't do DMA on "complex" transfers, i.e. with
* non-word-aligned buffers or lengths. Also, we don't bother * non-word-aligned buffers or lengths. A future improvement
* with all the DMA setup overhead for short transfers. * could be made to use temporary DMA bounce-buffers when these
* requirements are not met.
*
* Also, we don't bother with all the DMA setup overhead for
* short transfers.
*/ */
if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE) if (data->blocks * data->blksz < AU6601_MAX_DMA_BLOCK_SIZE)
return; return;
@ -787,6 +808,8 @@ static void alcor_pre_req(struct mmc_host *mmc,
for_each_sg(data->sg, sg, data->sg_len, i) { for_each_sg(data->sg, sg, data->sg_len, i) {
if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE) if (sg->length != AU6601_MAX_DMA_BLOCK_SIZE)
return; return;
if (sg->offset != 0)
return;
} }
/* This data might be unmapped at this time */ /* This data might be unmapped at this time */
@ -1024,7 +1047,7 @@ static void alcor_hw_uninit(struct alcor_sdmmc_host *host)
static void alcor_init_mmc(struct alcor_sdmmc_host *host) static void alcor_init_mmc(struct alcor_sdmmc_host *host)
{ {
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = mmc_from_priv(host);
mmc->f_min = AU6601_MIN_CLOCK; mmc->f_min = AU6601_MIN_CLOCK;
mmc->f_max = AU6601_MAX_CLOCK; mmc->f_max = AU6601_MAX_CLOCK;
@ -1036,26 +1059,21 @@ static void alcor_init_mmc(struct alcor_sdmmc_host *host)
mmc->ops = &alcor_sdc_ops; mmc->ops = &alcor_sdc_ops;
/* The hardware does DMA data transfer of 4096 bytes to/from a single /* The hardware does DMA data transfer of 4096 bytes to/from a single
* buffer address. Scatterlists are not supported, but upon DMA * buffer address. Scatterlists are not supported at the hardware
* completion (signalled via IRQ), the original vendor driver does * level, however we can work with them at the driver level,
* then immediately set up another DMA transfer of the next 4096 * provided that each segment is exactly 4096 bytes in size.
* bytes. * Upon DMA completion of a single segment (signalled via IRQ), we
* immediately proceed to transfer the next segment from the
* scatterlist.
* *
* This means that we need to handle the I/O in 4096 byte chunks. * The overall request is limited to 240 sectors, matching the
* Lacking a way to limit the sglist entries to 4096 bytes, we instead * original vendor driver.
* impose that only one segment is provided, with maximum size 4096,
* which also happens to be the minimum size. This means that the
* single-entry sglist handled by this driver can be handed directly
* to the hardware, nice and simple.
*
* Unfortunately though, that means we only do 4096 bytes I/O per
* MMC command. A future improvement would be to make the driver
* accept sg lists and entries of any size, and simply iterate
* through them 4096 bytes at a time.
*/ */
mmc->max_segs = AU6601_MAX_DMA_SEGMENTS; mmc->max_segs = AU6601_MAX_DMA_SEGMENTS;
mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE; mmc->max_seg_size = AU6601_MAX_DMA_BLOCK_SIZE;
mmc->max_req_size = mmc->max_seg_size; mmc->max_blk_count = 240;
mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
dma_set_max_seg_size(host->dev, mmc->max_seg_size);
} }
static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
@ -1072,7 +1090,6 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
} }
host = mmc_priv(mmc); host = mmc_priv(mmc);
host->mmc = mmc;
host->dev = &pdev->dev; host->dev = &pdev->dev;
host->cur_power_mode = MMC_POWER_UNDEFINED; host->cur_power_mode = MMC_POWER_UNDEFINED;
host->alcor_pci = priv; host->alcor_pci = priv;
@ -1104,13 +1121,14 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
static int alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) static int alcor_pci_sdmmc_drv_remove(struct platform_device *pdev)
{ {
struct alcor_sdmmc_host *host = dev_get_drvdata(&pdev->dev); struct alcor_sdmmc_host *host = dev_get_drvdata(&pdev->dev);
struct mmc_host *mmc = mmc_from_priv(host);
if (cancel_delayed_work_sync(&host->timeout_work)) if (cancel_delayed_work_sync(&host->timeout_work))
alcor_request_complete(host, 0); alcor_request_complete(host, 0);
alcor_hw_uninit(host); alcor_hw_uninit(host);
mmc_remove_host(host->mmc); mmc_remove_host(mmc);
mmc_free_host(host->mmc); mmc_free_host(mmc);
return 0; return 0;
} }

View File

@ -537,6 +537,8 @@ static void cqhci_prep_dcmd_desc(struct mmc_host *mmc,
CQHCI_ACT(0x5) | CQHCI_ACT(0x5) |
CQHCI_CMD_INDEX(mrq->cmd->opcode) | CQHCI_CMD_INDEX(mrq->cmd->opcode) |
CQHCI_CMD_TIMING(timing) | CQHCI_RESP_TYPE(resp_type)); CQHCI_CMD_TIMING(timing) | CQHCI_RESP_TYPE(resp_type));
if (cq_host->ops->update_dcmd_desc)
cq_host->ops->update_dcmd_desc(mmc, mrq, &data);
*task_desc |= data; *task_desc |= data;
desc = (u8 *)task_desc; desc = (u8 *)task_desc;
pr_debug("%s: cqhci: dcmd: cmd: %d timing: %d resp: %d\n", pr_debug("%s: cqhci: dcmd: cmd: %d timing: %d resp: %d\n",

View File

@ -88,6 +88,7 @@
/* send status config 1 */ /* send status config 1 */
#define CQHCI_SSC1 0x40 #define CQHCI_SSC1 0x40
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
/* send status config 2 */ /* send status config 2 */
#define CQHCI_SSC2 0x44 #define CQHCI_SSC2 0x44
@ -147,6 +148,7 @@
struct cqhci_host_ops; struct cqhci_host_ops;
struct mmc_host; struct mmc_host;
struct mmc_request;
struct cqhci_slot; struct cqhci_slot;
struct cqhci_host { struct cqhci_host {
@ -210,6 +212,8 @@ struct cqhci_host_ops {
u32 (*read_l)(struct cqhci_host *host, int reg); u32 (*read_l)(struct cqhci_host *host, int reg);
void (*enable)(struct mmc_host *mmc); void (*enable)(struct mmc_host *mmc);
void (*disable)(struct mmc_host *mmc, bool recovery); void (*disable)(struct mmc_host *mmc, bool recovery);
void (*update_dcmd_desc)(struct mmc_host *mmc, struct mmc_request *mrq,
u64 *data);
}; };
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg) static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)

View File

@ -23,6 +23,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/iopoll.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/ioport.h> #include <linux/ioport.h>
@ -48,6 +49,8 @@
#define CLK_CORE_PHASE_MASK GENMASK(9, 8) #define CLK_CORE_PHASE_MASK GENMASK(9, 8)
#define CLK_TX_PHASE_MASK GENMASK(11, 10) #define CLK_TX_PHASE_MASK GENMASK(11, 10)
#define CLK_RX_PHASE_MASK GENMASK(13, 12) #define CLK_RX_PHASE_MASK GENMASK(13, 12)
#define CLK_PHASE_0 0
#define CLK_PHASE_180 2
#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16) #define CLK_V2_TX_DELAY_MASK GENMASK(19, 16)
#define CLK_V2_RX_DELAY_MASK GENMASK(23, 20) #define CLK_V2_RX_DELAY_MASK GENMASK(23, 20)
#define CLK_V2_ALWAYS_ON BIT(24) #define CLK_V2_ALWAYS_ON BIT(24)
@ -56,10 +59,6 @@
#define CLK_V3_RX_DELAY_MASK GENMASK(27, 22) #define CLK_V3_RX_DELAY_MASK GENMASK(27, 22)
#define CLK_V3_ALWAYS_ON BIT(28) #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_TX_DELAY_MASK(h) (h->data->tx_delay_mask) #define CLK_TX_DELAY_MASK(h) (h->data->tx_delay_mask)
#define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask) #define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask)
#define CLK_ALWAYS_ON(h) (h->data->always_on) #define CLK_ALWAYS_ON(h) (h->data->always_on)
@ -164,10 +163,10 @@ struct meson_host {
void __iomem *regs; void __iomem *regs;
struct clk *core_clk; struct clk *core_clk;
struct clk *mux_clk;
struct clk *mmc_clk; struct clk *mmc_clk;
struct clk *rx_clk;
struct clk *tx_clk;
unsigned long req_rate; unsigned long req_rate;
bool ddr;
struct pinctrl *pinctrl; struct pinctrl *pinctrl;
struct pinctrl_state *pins_default; struct pinctrl_state *pins_default;
@ -207,90 +206,6 @@ struct meson_host {
#define CMD_RESP_MASK GENMASK(31, 1) #define CMD_RESP_MASK GENMASK(31, 1)
#define CMD_RESP_SRAM BIT(0) #define CMD_RESP_SRAM BIT(0)
struct meson_mmc_phase {
struct clk_hw hw;
void __iomem *reg;
unsigned long phase_mask;
unsigned long delay_mask;
unsigned int delay_step_ps;
};
#define to_meson_mmc_phase(_hw) container_of(_hw, struct meson_mmc_phase, hw)
static int meson_mmc_clk_get_phase(struct clk_hw *hw)
{
struct meson_mmc_phase *mmc = to_meson_mmc_phase(hw);
unsigned int phase_num = 1 << hweight_long(mmc->phase_mask);
unsigned long period_ps, p, d;
int degrees;
u32 val;
val = readl(mmc->reg);
p = (val & mmc->phase_mask) >> __ffs(mmc->phase_mask);
degrees = p * 360 / phase_num;
if (mmc->delay_mask) {
period_ps = DIV_ROUND_UP((unsigned long)NSEC_PER_SEC * 1000,
clk_get_rate(hw->clk));
d = (val & mmc->delay_mask) >> __ffs(mmc->delay_mask);
degrees += d * mmc->delay_step_ps * 360 / period_ps;
degrees %= 360;
}
return degrees;
}
static void meson_mmc_apply_phase_delay(struct meson_mmc_phase *mmc,
unsigned int phase,
unsigned int delay)
{
u32 val;
val = readl(mmc->reg);
val &= ~mmc->phase_mask;
val |= phase << __ffs(mmc->phase_mask);
if (mmc->delay_mask) {
val &= ~mmc->delay_mask;
val |= delay << __ffs(mmc->delay_mask);
}
writel(val, mmc->reg);
}
static int meson_mmc_clk_set_phase(struct clk_hw *hw, int degrees)
{
struct meson_mmc_phase *mmc = to_meson_mmc_phase(hw);
unsigned int phase_num = 1 << hweight_long(mmc->phase_mask);
unsigned long period_ps, d = 0, r;
uint64_t p;
p = degrees % 360;
if (!mmc->delay_mask) {
p = DIV_ROUND_CLOSEST_ULL(p, 360 / phase_num);
} else {
period_ps = DIV_ROUND_UP((unsigned long)NSEC_PER_SEC * 1000,
clk_get_rate(hw->clk));
/* First compute the phase index (p), the remainder (r) is the
* part we'll try to acheive using the delays (d).
*/
r = do_div(p, 360 / phase_num);
d = DIV_ROUND_CLOSEST(r * period_ps,
360 * mmc->delay_step_ps);
d = min(d, mmc->delay_mask >> __ffs(mmc->delay_mask));
}
meson_mmc_apply_phase_delay(mmc, p, d);
return 0;
}
static const struct clk_ops meson_mmc_clk_phase_ops = {
.get_phase = meson_mmc_clk_get_phase,
.set_phase = meson_mmc_clk_set_phase,
};
static unsigned int meson_mmc_get_timeout_msecs(struct mmc_data *data) static unsigned int meson_mmc_get_timeout_msecs(struct mmc_data *data)
{ {
unsigned int timeout = data->timeout_ns / NSEC_PER_MSEC; unsigned int timeout = data->timeout_ns / NSEC_PER_MSEC;
@ -383,16 +298,6 @@ static void meson_mmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
mmc_get_dma_dir(data)); mmc_get_dma_dir(data));
} }
static bool meson_mmc_timing_is_ddr(struct mmc_ios *ios)
{
if (ios->timing == MMC_TIMING_MMC_DDR52 ||
ios->timing == MMC_TIMING_UHS_DDR50 ||
ios->timing == MMC_TIMING_MMC_HS400)
return true;
return false;
}
/* /*
* Gating the clock on this controller is tricky. It seems the mmc clock * Gating the clock on this controller is tricky. It seems the mmc clock
* is also used by the controller. It may crash during some operation if the * is also used by the controller. It may crash during some operation if the
@ -429,36 +334,41 @@ static void meson_mmc_clk_ungate(struct meson_host *host)
writel(cfg, host->regs + SD_EMMC_CFG); writel(cfg, host->regs + SD_EMMC_CFG);
} }
static int meson_mmc_clk_set(struct meson_host *host, struct mmc_ios *ios) static int meson_mmc_clk_set(struct meson_host *host, unsigned long rate,
bool ddr)
{ {
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = host->mmc;
unsigned long rate = ios->clock;
int ret; int ret;
u32 cfg; u32 cfg;
/* DDR modes require higher module clock */
if (meson_mmc_timing_is_ddr(ios))
rate <<= 1;
/* Same request - bail-out */ /* Same request - bail-out */
if (host->req_rate == rate) if (host->ddr == ddr && host->req_rate == rate)
return 0; return 0;
/* stop clock */ /* stop clock */
meson_mmc_clk_gate(host); meson_mmc_clk_gate(host);
host->req_rate = 0; host->req_rate = 0;
mmc->actual_clock = 0;
if (!rate) { /* return with clock being stopped */
mmc->actual_clock = 0; if (!rate)
/* return with clock being stopped */
return 0; return 0;
}
/* Stop the clock during rate change to avoid glitches */ /* Stop the clock during rate change to avoid glitches */
cfg = readl(host->regs + SD_EMMC_CFG); cfg = readl(host->regs + SD_EMMC_CFG);
cfg |= CFG_STOP_CLOCK; cfg |= CFG_STOP_CLOCK;
writel(cfg, host->regs + SD_EMMC_CFG); writel(cfg, host->regs + SD_EMMC_CFG);
if (ddr) {
/* DDR modes require higher module clock */
rate <<= 1;
cfg |= CFG_DDR;
} else {
cfg &= ~CFG_DDR;
}
writel(cfg, host->regs + SD_EMMC_CFG);
host->ddr = ddr;
ret = clk_set_rate(host->mmc_clk, rate); ret = clk_set_rate(host->mmc_clk, rate);
if (ret) { if (ret) {
dev_err(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n", dev_err(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n",
@ -470,12 +380,14 @@ static int meson_mmc_clk_set(struct meson_host *host, struct mmc_ios *ios)
mmc->actual_clock = clk_get_rate(host->mmc_clk); mmc->actual_clock = clk_get_rate(host->mmc_clk);
/* We should report the real output frequency of the controller */ /* We should report the real output frequency of the controller */
if (meson_mmc_timing_is_ddr(ios)) if (ddr) {
host->req_rate >>= 1;
mmc->actual_clock >>= 1; mmc->actual_clock >>= 1;
}
dev_dbg(host->dev, "clk rate: %u Hz\n", mmc->actual_clock); dev_dbg(host->dev, "clk rate: %u Hz\n", mmc->actual_clock);
if (ios->clock != mmc->actual_clock) if (rate != mmc->actual_clock)
dev_dbg(host->dev, "requested rate was %u\n", ios->clock); dev_dbg(host->dev, "requested rate was %lu\n", rate);
/* (re)start clock */ /* (re)start clock */
meson_mmc_clk_ungate(host); meson_mmc_clk_ungate(host);
@ -493,8 +405,6 @@ static int meson_mmc_clk_init(struct meson_host *host)
struct clk_init_data init; struct clk_init_data init;
struct clk_mux *mux; struct clk_mux *mux;
struct clk_divider *div; struct clk_divider *div;
struct meson_mmc_phase *core, *tx, *rx;
struct clk *clk;
char clk_name[32]; char clk_name[32];
int i, ret = 0; int i, ret = 0;
const char *mux_parent_names[MUX_CLK_NUM_PARENTS]; const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
@ -502,9 +412,11 @@ static int meson_mmc_clk_init(struct meson_host *host)
u32 clk_reg; u32 clk_reg;
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
clk_reg = 0; clk_reg = CLK_ALWAYS_ON(host);
clk_reg |= CLK_ALWAYS_ON(host);
clk_reg |= CLK_DIV_MASK; clk_reg |= CLK_DIV_MASK;
clk_reg |= FIELD_PREP(CLK_CORE_PHASE_MASK, CLK_PHASE_180);
clk_reg |= FIELD_PREP(CLK_TX_PHASE_MASK, CLK_PHASE_0);
clk_reg |= FIELD_PREP(CLK_RX_PHASE_MASK, CLK_PHASE_0);
writel(clk_reg, host->regs + SD_EMMC_CLOCK); writel(clk_reg, host->regs + SD_EMMC_CLOCK);
/* get the mux parents */ /* get the mux parents */
@ -540,9 +452,9 @@ static int meson_mmc_clk_init(struct meson_host *host)
mux->mask = CLK_SRC_MASK >> mux->shift; mux->mask = CLK_SRC_MASK >> mux->shift;
mux->hw.init = &init; mux->hw.init = &init;
clk = devm_clk_register(host->dev, &mux->hw); host->mux_clk = devm_clk_register(host->dev, &mux->hw);
if (WARN_ON(IS_ERR(clk))) if (WARN_ON(IS_ERR(host->mux_clk)))
return PTR_ERR(clk); return PTR_ERR(host->mux_clk);
/* create the divider */ /* create the divider */
div = devm_kzalloc(host->dev, sizeof(*div), GFP_KERNEL); div = devm_kzalloc(host->dev, sizeof(*div), GFP_KERNEL);
@ -553,7 +465,7 @@ static int meson_mmc_clk_init(struct meson_host *host)
init.name = clk_name; init.name = clk_name;
init.ops = &clk_divider_ops; init.ops = &clk_divider_ops;
init.flags = CLK_SET_RATE_PARENT; init.flags = CLK_SET_RATE_PARENT;
clk_parent[0] = __clk_get_name(clk); clk_parent[0] = __clk_get_name(host->mux_clk);
init.parent_names = clk_parent; init.parent_names = clk_parent;
init.num_parents = 1; init.num_parents = 1;
@ -563,190 +475,104 @@ static int meson_mmc_clk_init(struct meson_host *host)
div->hw.init = &init; div->hw.init = &init;
div->flags = CLK_DIVIDER_ONE_BASED; div->flags = CLK_DIVIDER_ONE_BASED;
clk = devm_clk_register(host->dev, &div->hw); host->mmc_clk = devm_clk_register(host->dev, &div->hw);
if (WARN_ON(IS_ERR(clk))) if (WARN_ON(IS_ERR(host->mmc_clk)))
return PTR_ERR(clk);
/* create the mmc core clock */
core = devm_kzalloc(host->dev, sizeof(*core), GFP_KERNEL);
if (!core)
return -ENOMEM;
snprintf(clk_name, sizeof(clk_name), "%s#core", dev_name(host->dev));
init.name = clk_name;
init.ops = &meson_mmc_clk_phase_ops;
init.flags = CLK_SET_RATE_PARENT;
clk_parent[0] = __clk_get_name(clk);
init.parent_names = clk_parent;
init.num_parents = 1;
core->reg = host->regs + SD_EMMC_CLOCK;
core->phase_mask = CLK_CORE_PHASE_MASK;
core->hw.init = &init;
host->mmc_clk = devm_clk_register(host->dev, &core->hw);
if (WARN_ON(PTR_ERR_OR_ZERO(host->mmc_clk)))
return PTR_ERR(host->mmc_clk); return PTR_ERR(host->mmc_clk);
/* create the mmc tx clock */
tx = devm_kzalloc(host->dev, sizeof(*tx), GFP_KERNEL);
if (!tx)
return -ENOMEM;
snprintf(clk_name, sizeof(clk_name), "%s#tx", dev_name(host->dev));
init.name = clk_name;
init.ops = &meson_mmc_clk_phase_ops;
init.flags = 0;
clk_parent[0] = __clk_get_name(host->mmc_clk);
init.parent_names = clk_parent;
init.num_parents = 1;
tx->reg = host->regs + SD_EMMC_CLOCK;
tx->phase_mask = CLK_TX_PHASE_MASK;
tx->delay_mask = CLK_TX_DELAY_MASK(host);
tx->delay_step_ps = CLK_DELAY_STEP_PS;
tx->hw.init = &init;
host->tx_clk = devm_clk_register(host->dev, &tx->hw);
if (WARN_ON(PTR_ERR_OR_ZERO(host->tx_clk)))
return PTR_ERR(host->tx_clk);
/* create the mmc rx clock */
rx = devm_kzalloc(host->dev, sizeof(*rx), GFP_KERNEL);
if (!rx)
return -ENOMEM;
snprintf(clk_name, sizeof(clk_name), "%s#rx", dev_name(host->dev));
init.name = clk_name;
init.ops = &meson_mmc_clk_phase_ops;
init.flags = 0;
clk_parent[0] = __clk_get_name(host->mmc_clk);
init.parent_names = clk_parent;
init.num_parents = 1;
rx->reg = host->regs + SD_EMMC_CLOCK;
rx->phase_mask = CLK_RX_PHASE_MASK;
rx->delay_mask = CLK_RX_DELAY_MASK(host);
rx->delay_step_ps = CLK_DELAY_STEP_PS;
rx->hw.init = &init;
host->rx_clk = devm_clk_register(host->dev, &rx->hw);
if (WARN_ON(PTR_ERR_OR_ZERO(host->rx_clk)))
return PTR_ERR(host->rx_clk);
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
host->mmc->f_min = clk_round_rate(host->mmc_clk, 400000); host->mmc->f_min = clk_round_rate(host->mmc_clk, 400000);
ret = clk_set_rate(host->mmc_clk, host->mmc->f_min); ret = clk_set_rate(host->mmc_clk, host->mmc->f_min);
if (ret) if (ret)
return ret; return ret;
clk_set_phase(host->mmc_clk, 180);
clk_set_phase(host->tx_clk, 0);
clk_set_phase(host->rx_clk, 0);
return clk_prepare_enable(host->mmc_clk); return clk_prepare_enable(host->mmc_clk);
} }
static void meson_mmc_shift_map(unsigned long *map, unsigned long shift) static void meson_mmc_disable_resampling(struct meson_host *host)
{ {
DECLARE_BITMAP(left, CLK_PHASE_POINT_NUM); unsigned int val = readl(host->regs + host->data->adjust);
DECLARE_BITMAP(right, CLK_PHASE_POINT_NUM);
/* val &= ~ADJUST_ADJ_EN;
* shift the bitmap right and reintroduce the dropped bits on the left writel(val, host->regs + host->data->adjust);
* of the bitmap
*/
bitmap_shift_right(right, map, shift, CLK_PHASE_POINT_NUM);
bitmap_shift_left(left, map, CLK_PHASE_POINT_NUM - shift,
CLK_PHASE_POINT_NUM);
bitmap_or(map, left, right, CLK_PHASE_POINT_NUM);
} }
static void meson_mmc_find_next_region(unsigned long *map, static void meson_mmc_reset_resampling(struct meson_host *host)
unsigned long *start,
unsigned long *stop)
{ {
*start = find_next_bit(map, CLK_PHASE_POINT_NUM, *start); unsigned int val;
*stop = find_next_zero_bit(map, CLK_PHASE_POINT_NUM, *start);
meson_mmc_disable_resampling(host);
val = readl(host->regs + host->data->adjust);
val &= ~ADJUST_ADJ_DELAY_MASK;
writel(val, host->regs + host->data->adjust);
} }
static int meson_mmc_find_tuning_point(unsigned long *test) static int meson_mmc_resampling_tuning(struct mmc_host *mmc, u32 opcode)
{
unsigned long shift, stop, offset = 0, start = 0, size = 0;
/* Get the all good/all bad situation out the way */
if (bitmap_full(test, CLK_PHASE_POINT_NUM))
return 0; /* All points are good so point 0 will do */
else if (bitmap_empty(test, CLK_PHASE_POINT_NUM))
return -EIO; /* No successful tuning point */
/*
* Now we know there is a least one region find. Make sure it does
* not wrap by the shifting the bitmap if necessary
*/
shift = find_first_zero_bit(test, CLK_PHASE_POINT_NUM);
if (shift != 0)
meson_mmc_shift_map(test, shift);
while (start < CLK_PHASE_POINT_NUM) {
meson_mmc_find_next_region(test, &start, &stop);
if ((stop - start) > size) {
offset = start;
size = stop - start;
}
start = stop;
}
/* Get the center point of the region */
offset += (size / 2);
/* Shift the result back */
offset = (offset + shift) % CLK_PHASE_POINT_NUM;
return offset;
}
static int meson_mmc_clk_phase_tuning(struct mmc_host *mmc, u32 opcode,
struct clk *clk)
{
int point, ret;
DECLARE_BITMAP(test, CLK_PHASE_POINT_NUM);
dev_dbg(mmc_dev(mmc), "%s phase/delay tunning...\n",
__clk_get_name(clk));
bitmap_zero(test, CLK_PHASE_POINT_NUM);
/* Explore tuning points */
for (point = 0; point < CLK_PHASE_POINT_NUM; point++) {
clk_set_phase(clk, point * CLK_PHASE_STEP);
ret = mmc_send_tuning(mmc, opcode, NULL);
if (!ret)
set_bit(point, test);
}
/* Find the optimal tuning point and apply it */
point = meson_mmc_find_tuning_point(test);
if (point < 0)
return point; /* tuning failed */
clk_set_phase(clk, point * CLK_PHASE_STEP);
dev_dbg(mmc_dev(mmc), "success with phase: %d\n",
clk_get_phase(clk));
return 0;
}
static int meson_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{ {
struct meson_host *host = mmc_priv(mmc); struct meson_host *host = mmc_priv(mmc);
int adj = 0; unsigned int val, dly, max_dly, i;
int ret;
/* enable signal resampling w/o delay */ /* Resampling is done using the source clock */
adj = ADJUST_ADJ_EN; max_dly = DIV_ROUND_UP(clk_get_rate(host->mux_clk),
writel(adj, host->regs + host->data->adjust); clk_get_rate(host->mmc_clk));
return meson_mmc_clk_phase_tuning(mmc, opcode, host->rx_clk); val = readl(host->regs + host->data->adjust);
val |= ADJUST_ADJ_EN;
writel(val, host->regs + host->data->adjust);
if (mmc->doing_retune)
dly = FIELD_GET(ADJUST_ADJ_DELAY_MASK, val) + 1;
else
dly = 0;
for (i = 0; i < max_dly; i++) {
val &= ~ADJUST_ADJ_DELAY_MASK;
val |= FIELD_PREP(ADJUST_ADJ_DELAY_MASK, (dly + i) % max_dly);
writel(val, host->regs + host->data->adjust);
ret = mmc_send_tuning(mmc, opcode, NULL);
if (!ret) {
dev_dbg(mmc_dev(mmc), "resampling delay: %u\n",
(dly + i) % max_dly);
return 0;
}
}
meson_mmc_reset_resampling(host);
return -EIO;
}
static int meson_mmc_prepare_ios_clock(struct meson_host *host,
struct mmc_ios *ios)
{
bool ddr;
switch (ios->timing) {
case MMC_TIMING_MMC_DDR52:
case MMC_TIMING_UHS_DDR50:
ddr = true;
break;
default:
ddr = false;
break;
}
return meson_mmc_clk_set(host, ios->clock, ddr);
}
static void meson_mmc_check_resampling(struct meson_host *host,
struct mmc_ios *ios)
{
switch (ios->timing) {
case MMC_TIMING_LEGACY:
case MMC_TIMING_MMC_HS:
case MMC_TIMING_SD_HS:
case MMC_TIMING_MMC_DDR52:
meson_mmc_disable_resampling(host);
break;
}
} }
static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@ -775,12 +601,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (!IS_ERR(mmc->supply.vmmc)) if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
/* disable signal resampling */
writel(0, host->regs + host->data->adjust);
/* Reset rx phase */
clk_set_phase(host->rx_clk, 0);
break; break;
case MMC_POWER_ON: case MMC_POWER_ON:
@ -817,20 +637,13 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
val = readl(host->regs + SD_EMMC_CFG); val = readl(host->regs + SD_EMMC_CFG);
val &= ~CFG_BUS_WIDTH_MASK; val &= ~CFG_BUS_WIDTH_MASK;
val |= FIELD_PREP(CFG_BUS_WIDTH_MASK, bus_width); val |= FIELD_PREP(CFG_BUS_WIDTH_MASK, bus_width);
writel(val, host->regs + SD_EMMC_CFG);
val &= ~CFG_DDR; meson_mmc_check_resampling(host, ios);
if (meson_mmc_timing_is_ddr(ios)) err = meson_mmc_prepare_ios_clock(host, ios);
val |= CFG_DDR;
val &= ~CFG_CHK_DS;
if (ios->timing == MMC_TIMING_MMC_HS400)
val |= CFG_CHK_DS;
err = meson_mmc_clk_set(host, ios);
if (err) if (err)
dev_err(host->dev, "Failed to set clock: %d\n,", err); dev_err(host->dev, "Failed to set clock: %d\n,", err);
writel(val, host->regs + SD_EMMC_CFG);
dev_dbg(host->dev, "SD_EMMC_CFG: 0x%08x\n", val); dev_dbg(host->dev, "SD_EMMC_CFG: 0x%08x\n", val);
} }
@ -1081,9 +894,6 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
} }
out: out:
/* ack all enabled interrupts */
writel(irq_en, host->regs + SD_EMMC_STATUS);
if (cmd->error) { if (cmd->error) {
/* Stop desc in case of errors */ /* Stop desc in case of errors */
u32 start = readl(host->regs + SD_EMMC_START); u32 start = readl(host->regs + SD_EMMC_START);
@ -1095,12 +905,14 @@ out:
if (ret == IRQ_HANDLED) if (ret == IRQ_HANDLED)
meson_mmc_request_done(host->mmc, cmd->mrq); meson_mmc_request_done(host->mmc, cmd->mrq);
/* ack all raised interrupts */
writel(status, host->regs + SD_EMMC_STATUS);
return ret; return ret;
} }
static int meson_mmc_wait_desc_stop(struct meson_host *host) static int meson_mmc_wait_desc_stop(struct meson_host *host)
{ {
int loop;
u32 status; u32 status;
/* /*
@ -1110,20 +922,10 @@ static int meson_mmc_wait_desc_stop(struct meson_host *host)
* If we don't confirm the descriptor is stopped, it might raise new * If we don't confirm the descriptor is stopped, it might raise new
* IRQs after we have called mmc_request_done() which is bad. * IRQs after we have called mmc_request_done() which is bad.
*/ */
for (loop = 50; loop; loop--) {
status = readl(host->regs + SD_EMMC_STATUS);
if (status & (STATUS_BUSY | STATUS_DESC_BUSY))
udelay(100);
else
break;
}
if (status & (STATUS_BUSY | STATUS_DESC_BUSY)) { return readl_poll_timeout(host->regs + SD_EMMC_STATUS, status,
dev_err(host->dev, "Timed out waiting for host to stop\n"); !(status & (STATUS_BUSY | STATUS_DESC_BUSY)),
return -ETIMEDOUT; 100, 5000);
}
return 0;
} }
static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
@ -1227,7 +1029,7 @@ static const struct mmc_host_ops meson_mmc_ops = {
.get_cd = meson_mmc_get_cd, .get_cd = meson_mmc_get_cd,
.pre_req = meson_mmc_pre_req, .pre_req = meson_mmc_pre_req,
.post_req = meson_mmc_post_req, .post_req = meson_mmc_post_req,
.execute_tuning = meson_mmc_execute_tuning, .execute_tuning = meson_mmc_resampling_tuning,
.card_busy = meson_mmc_card_busy, .card_busy = meson_mmc_card_busy,
.start_signal_voltage_switch = meson_mmc_voltage_switch, .start_signal_voltage_switch = meson_mmc_voltage_switch,
}; };
@ -1338,7 +1140,7 @@ static int meson_mmc_probe(struct platform_device *pdev)
host->regs + SD_EMMC_IRQ_EN); host->regs + SD_EMMC_IRQ_EN);
ret = request_threaded_irq(host->irq, meson_mmc_irq, ret = request_threaded_irq(host->irq, meson_mmc_irq,
meson_mmc_irq_thread, IRQF_SHARED, meson_mmc_irq_thread, IRQF_ONESHOT,
dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) if (ret)
goto err_init_clk; goto err_init_clk;
@ -1349,6 +1151,13 @@ static int meson_mmc_probe(struct platform_device *pdev)
mmc->max_segs = SD_EMMC_DESC_BUF_LEN / sizeof(struct sd_emmc_desc); mmc->max_segs = SD_EMMC_DESC_BUF_LEN / sizeof(struct sd_emmc_desc);
mmc->max_seg_size = mmc->max_req_size; mmc->max_seg_size = mmc->max_req_size;
/*
* At the moment, we don't know how to reliably enable HS400.
* From the different datasheets, it is not even clear if this mode
* is officially supported by any of the SoCs
*/
mmc->caps2 &= ~MMC_CAP2_HS400;
/* data bounce buffer */ /* data bounce buffer */
host->bounce_buf_size = mmc->max_req_size; host->bounce_buf_size = mmc->max_req_size;
host->bounce_buf = host->bounce_buf =

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* mmc_spi.c - Access SD/MMC cards through SPI master controllers * Access SD/MMC cards through SPI master controllers
* *
* (C) Copyright 2005, Intec Automation, * (C) Copyright 2005, Intec Automation,
* Mike Lavender (mike@steroidmicros) * Mike Lavender (mike@steroidmicros)
@ -8,21 +9,6 @@
* Hans-Peter Nilsson (hp@axis.com) * Hans-Peter Nilsson (hp@axis.com)
* (C) Copyright 2007, ATRON electronic GmbH, * (C) Copyright 2007, ATRON electronic GmbH,
* Jan Nikitenko <jan.nikitenko@gmail.com> * Jan Nikitenko <jan.nikitenko@gmail.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -197,7 +183,7 @@ mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
static int mmc_spi_skip(struct mmc_spi_host *host, unsigned long timeout, static int mmc_spi_skip(struct mmc_spi_host *host, unsigned long timeout,
unsigned n, u8 byte) unsigned n, u8 byte)
{ {
u8 *cp = host->data->status; u8 *cp = host->data->status;
unsigned long start = jiffies; unsigned long start = jiffies;
while (1) { while (1) {
@ -220,7 +206,7 @@ static int mmc_spi_skip(struct mmc_spi_host *host, unsigned long timeout,
* We use jiffies here because we want to have a relation * We use jiffies here because we want to have a relation
* between elapsed time and the blocking of the scheduler. * between elapsed time and the blocking of the scheduler.
*/ */
if (time_is_before_jiffies(start+1)) if (time_is_before_jiffies(start + 1))
schedule(); schedule();
} }
return -ETIMEDOUT; return -ETIMEDOUT;
@ -415,7 +401,7 @@ checkstatus:
default: default:
dev_dbg(&host->spi->dev, "bad response type %04x\n", dev_dbg(&host->spi->dev, "bad response type %04x\n",
mmc_spi_resp_type(cmd)); mmc_spi_resp_type(cmd));
if (value >= 0) if (value >= 0)
value = -EINVAL; value = -EINVAL;
goto done; goto done;
@ -467,8 +453,8 @@ mmc_spi_command_send(struct mmc_spi_host *host,
memset(cp, 0xff, sizeof(data->status)); memset(cp, 0xff, sizeof(data->status));
cp[1] = 0x40 | cmd->opcode; cp[1] = 0x40 | cmd->opcode;
put_unaligned_be32(cmd->arg, cp+2); put_unaligned_be32(cmd->arg, cp + 2);
cp[6] = crc7_be(0, cp+1, 5) | 0x01; cp[6] = crc7_be(0, cp + 1, 5) | 0x01;
cp += 7; cp += 7;
/* Then, read up to 13 bytes (while writing all-ones): /* Then, read up to 13 bytes (while writing all-ones):
@ -642,9 +628,7 @@ mmc_spi_setup_data_message(
if (multiple || direction == DMA_TO_DEVICE) { if (multiple || direction == DMA_TO_DEVICE) {
t = &host->early_status; t = &host->early_status;
memset(t, 0, sizeof(*t)); memset(t, 0, sizeof(*t));
t->len = (direction == DMA_TO_DEVICE) t->len = (direction == DMA_TO_DEVICE) ? sizeof(scratch->status) : 1;
? sizeof(scratch->status)
: 1;
t->tx_buf = host->ones; t->tx_buf = host->ones;
t->tx_dma = host->ones_dma; t->tx_dma = host->ones_dma;
t->rx_buf = scratch->status; t->rx_buf = scratch->status;
@ -677,8 +661,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
u32 pattern; u32 pattern;
if (host->mmc->use_spi_crc) if (host->mmc->use_spi_crc)
scratch->crc_val = cpu_to_be16( scratch->crc_val = cpu_to_be16(crc_itu_t(0, t->tx_buf, t->len));
crc_itu_t(0, t->tx_buf, t->len));
if (host->dma_dev) if (host->dma_dev)
dma_sync_single_for_device(host->dma_dev, dma_sync_single_for_device(host->dma_dev,
host->data_dma, sizeof(*scratch), host->data_dma, sizeof(*scratch),
@ -819,6 +802,10 @@ mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t,
} }
status = spi_sync_locked(spi, &host->m); status = spi_sync_locked(spi, &host->m);
if (status < 0) {
dev_dbg(&spi->dev, "read error %d\n", status);
return status;
}
if (host->dma_dev) { if (host->dma_dev) {
dma_sync_single_for_cpu(host->dma_dev, dma_sync_single_for_cpu(host->dma_dev,
@ -855,9 +842,9 @@ mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t,
be16_to_cpus(&scratch->crc_val); be16_to_cpus(&scratch->crc_val);
if (scratch->crc_val != crc) { if (scratch->crc_val != crc) {
dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, " dev_dbg(&spi->dev,
"computed=0x%04x len=%d\n", "read - crc error: crc_val=0x%04x, computed=0x%04x len=%d\n",
scratch->crc_val, crc, t->len); scratch->crc_val, crc, t->len);
return -EILSEQ; return -EILSEQ;
} }
} }
@ -945,9 +932,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
dev_dbg(&host->spi->dev, dev_dbg(&host->spi->dev,
" mmc_spi: %s block, %d bytes\n", " mmc_spi: %s block, %d bytes\n",
(direction == DMA_TO_DEVICE) (direction == DMA_TO_DEVICE) ? "write" : "read",
? "write"
: "read",
t->len); t->len);
if (direction == DMA_TO_DEVICE) if (direction == DMA_TO_DEVICE)
@ -974,8 +959,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
if (status < 0) { if (status < 0) {
data->error = status; data->error = status;
dev_dbg(&spi->dev, "%s status %d\n", dev_dbg(&spi->dev, "%s status %d\n",
(direction == DMA_TO_DEVICE) (direction == DMA_TO_DEVICE) ? "write" : "read",
? "write" : "read",
status); status);
break; break;
} }
@ -1249,8 +1233,7 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
mres = spi_setup(host->spi); mres = spi_setup(host->spi);
if (mres < 0) if (mres < 0)
dev_dbg(&host->spi->dev, dev_dbg(&host->spi->dev,
"switch back to SPI mode 3" "switch back to SPI mode 3 failed\n");
" failed\n");
} }
} }
@ -1470,7 +1453,7 @@ static int mmc_spi_probe(struct spi_device *spi)
return 0; return 0;
fail_add_host: fail_add_host:
mmc_remove_host (mmc); mmc_remove_host(mmc);
fail_glue_init: fail_glue_init:
if (host->dma_dev) if (host->dma_dev)
dma_unmap_single(host->dma_dev, host->data_dma, dma_unmap_single(host->dma_dev, host->data_dma,
@ -1485,7 +1468,6 @@ fail_ones_dma:
fail_nobuf1: fail_nobuf1:
mmc_free_host(mmc); mmc_free_host(mmc);
mmc_spi_put_pdata(spi); mmc_spi_put_pdata(spi);
dev_set_drvdata(&spi->dev, NULL);
nomem: nomem:
kfree(ones); kfree(ones);
@ -1496,32 +1478,27 @@ nomem:
static int mmc_spi_remove(struct spi_device *spi) static int mmc_spi_remove(struct spi_device *spi)
{ {
struct mmc_host *mmc = dev_get_drvdata(&spi->dev); struct mmc_host *mmc = dev_get_drvdata(&spi->dev);
struct mmc_spi_host *host; struct mmc_spi_host *host = mmc_priv(mmc);
if (mmc) { /* prevent new mmc_detect_change() calls */
host = mmc_priv(mmc); if (host->pdata && host->pdata->exit)
host->pdata->exit(&spi->dev, mmc);
/* prevent new mmc_detect_change() calls */ mmc_remove_host(mmc);
if (host->pdata && host->pdata->exit)
host->pdata->exit(&spi->dev, mmc);
mmc_remove_host(mmc); if (host->dma_dev) {
dma_unmap_single(host->dma_dev, host->ones_dma,
if (host->dma_dev) { MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
dma_unmap_single(host->dma_dev, host->ones_dma, dma_unmap_single(host->dma_dev, host->data_dma,
MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE); sizeof(*host->data), DMA_BIDIRECTIONAL);
dma_unmap_single(host->dma_dev, host->data_dma,
sizeof(*host->data), DMA_BIDIRECTIONAL);
}
kfree(host->data);
kfree(host->ones);
spi->max_speed_hz = mmc->f_max;
mmc_free_host(mmc);
mmc_spi_put_pdata(spi);
dev_set_drvdata(&spi->dev, NULL);
} }
kfree(host->data);
kfree(host->ones);
spi->max_speed_hz = mmc->f_max;
mmc_free_host(mmc);
mmc_spi_put_pdata(spi);
return 0; return 0;
} }
@ -1542,8 +1519,7 @@ static struct spi_driver mmc_spi_driver = {
module_spi_driver(mmc_spi_driver); module_spi_driver(mmc_spi_driver);
MODULE_AUTHOR("Mike Lavender, David Brownell, " MODULE_AUTHOR("Mike Lavender, David Brownell, Hans-Peter Nilsson, Jan Nikitenko");
"Hans-Peter Nilsson, Jan Nikitenko");
MODULE_DESCRIPTION("SPI SD/MMC host driver"); MODULE_DESCRIPTION("SPI SD/MMC host driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:mmc_spi"); MODULE_ALIAS("spi:mmc_spi");

View File

@ -43,21 +43,11 @@
#include <asm/io.h> #include <asm/io.h>
#include "mmci.h" #include "mmci.h"
#include "mmci_qcom_dml.h"
#define DRIVER_NAME "mmci-pl18x" #define DRIVER_NAME "mmci-pl18x"
#ifdef CONFIG_DMA_ENGINE static void mmci_variant_init(struct mmci_host *host);
void mmci_variant_init(struct mmci_host *host); static void ux500v2_variant_init(struct mmci_host *host);
#else
static inline void mmci_variant_init(struct mmci_host *host) {}
#endif
#ifdef CONFIG_MMC_STM32_SDMMC
void sdmmc_variant_init(struct mmci_host *host);
#else
static inline void sdmmc_variant_init(struct mmci_host *host) {}
#endif
static unsigned int fmax = 515633; static unsigned int fmax = 515633;
@ -70,7 +60,6 @@ static struct variant_data variant_arm = {
.cmdreg_srsp = MCI_CPSM_RESPONSE, .cmdreg_srsp = MCI_CPSM_RESPONSE,
.datalength_bits = 16, .datalength_bits = 16,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.pwrreg_powerup = MCI_PWR_UP, .pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000, .f_max = 100000000,
.reversed_irq_handling = true, .reversed_irq_handling = true,
@ -90,7 +79,6 @@ static struct variant_data variant_arm_extended_fifo = {
.cmdreg_srsp = MCI_CPSM_RESPONSE, .cmdreg_srsp = MCI_CPSM_RESPONSE,
.datalength_bits = 16, .datalength_bits = 16,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.pwrreg_powerup = MCI_PWR_UP, .pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000, .f_max = 100000000,
.mmcimask1 = true, .mmcimask1 = true,
@ -110,7 +98,6 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
.cmdreg_srsp = MCI_CPSM_RESPONSE, .cmdreg_srsp = MCI_CPSM_RESPONSE,
.datalength_bits = 16, .datalength_bits = 16,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.pwrreg_powerup = MCI_PWR_UP, .pwrreg_powerup = MCI_PWR_UP,
.f_max = 100000000, .f_max = 100000000,
.mmcimask1 = true, .mmcimask1 = true,
@ -131,7 +118,6 @@ static struct variant_data variant_u300 = {
.cmdreg_srsp = MCI_CPSM_RESPONSE, .cmdreg_srsp = MCI_CPSM_RESPONSE,
.datalength_bits = 16, .datalength_bits = 16,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true, .st_sdio = true,
.pwrreg_powerup = MCI_PWR_ON, .pwrreg_powerup = MCI_PWR_ON,
@ -157,7 +143,6 @@ static struct variant_data variant_nomadik = {
.cmdreg_srsp = MCI_CPSM_RESPONSE, .cmdreg_srsp = MCI_CPSM_RESPONSE,
.datalength_bits = 24, .datalength_bits = 24,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true, .st_sdio = true,
.st_clkdiv = true, .st_clkdiv = true,
@ -186,7 +171,6 @@ static struct variant_data variant_ux500 = {
.cmdreg_srsp = MCI_CPSM_RESPONSE, .cmdreg_srsp = MCI_CPSM_RESPONSE,
.datalength_bits = 24, .datalength_bits = 24,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true, .st_sdio = true,
.st_clkdiv = true, .st_clkdiv = true,
@ -220,11 +204,9 @@ static struct variant_data variant_ux500v2 = {
.datactrl_mask_ddrmode = MCI_DPSM_ST_DDRMODE, .datactrl_mask_ddrmode = MCI_DPSM_ST_DDRMODE,
.datalength_bits = 24, .datalength_bits = 24,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true, .st_sdio = true,
.st_clkdiv = true, .st_clkdiv = true,
.blksz_datactrl16 = true,
.pwrreg_powerup = MCI_PWR_ON, .pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000, .f_max = 100000000,
.signal_direction = true, .signal_direction = true,
@ -238,7 +220,7 @@ static struct variant_data variant_ux500v2 = {
.irq_pio_mask = MCI_IRQ_PIO_MASK, .irq_pio_mask = MCI_IRQ_PIO_MASK,
.start_err = MCI_STARTBITERR, .start_err = MCI_STARTBITERR,
.opendrain = MCI_OD, .opendrain = MCI_OD,
.init = mmci_variant_init, .init = ux500v2_variant_init,
}; };
static struct variant_data variant_stm32 = { static struct variant_data variant_stm32 = {
@ -255,7 +237,6 @@ static struct variant_data variant_stm32 = {
.irq_pio_mask = MCI_IRQ_PIO_MASK, .irq_pio_mask = MCI_IRQ_PIO_MASK,
.datalength_bits = 24, .datalength_bits = 24,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true, .st_sdio = true,
.st_clkdiv = true, .st_clkdiv = true,
@ -299,10 +280,8 @@ static struct variant_data variant_qcom = {
.cmdreg_srsp_crc = MCI_CPSM_RESPONSE, .cmdreg_srsp_crc = MCI_CPSM_RESPONSE,
.cmdreg_srsp = MCI_CPSM_RESPONSE, .cmdreg_srsp = MCI_CPSM_RESPONSE,
.data_cmd_enable = MCI_CPSM_QCOM_DATCMD, .data_cmd_enable = MCI_CPSM_QCOM_DATCMD,
.blksz_datactrl4 = true,
.datalength_bits = 24, .datalength_bits = 24,
.datactrl_blocksz = 11, .datactrl_blocksz = 11,
.datactrl_dpsm_enable = MCI_DPSM_ENABLE,
.pwrreg_powerup = MCI_PWR_UP, .pwrreg_powerup = MCI_PWR_UP,
.f_max = 208000000, .f_max = 208000000,
.explicit_mclk_control = true, .explicit_mclk_control = true,
@ -624,6 +603,16 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
} }
static u32 mmci_get_dctrl_cfg(struct mmci_host *host)
{
return MCI_DPSM_ENABLE | mmci_dctrl_blksz(host);
}
static u32 ux500v2_get_dctrl_cfg(struct mmci_host *host)
{
return MCI_DPSM_ENABLE | (host->data->blksz << 16);
}
/* /*
* All the DMA operation mode stuff goes inside this ifdef. * All the DMA operation mode stuff goes inside this ifdef.
* This assumes that you have a generic DMA device interface, * This assumes that you have a generic DMA device interface,
@ -886,15 +875,11 @@ int mmci_dmae_prep_data(struct mmci_host *host,
int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl) int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl)
{ {
struct mmci_dmae_priv *dmae = host->dma_priv; struct mmci_dmae_priv *dmae = host->dma_priv;
struct mmc_data *data = host->data;
host->dma_in_progress = true; host->dma_in_progress = true;
dmaengine_submit(dmae->desc_current); dmaengine_submit(dmae->desc_current);
dma_async_issue_pending(dmae->cur); dma_async_issue_pending(dmae->cur);
if (host->variant->qcom_dml)
dml_start_xfer(host, data);
*datactrl |= MCI_DPSM_DMAENABLE; *datactrl |= MCI_DPSM_DMAENABLE;
return 0; return 0;
@ -952,6 +937,7 @@ void mmci_dmae_unprep_data(struct mmci_host *host,
static struct mmci_host_ops mmci_variant_ops = { static struct mmci_host_ops mmci_variant_ops = {
.prep_data = mmci_dmae_prep_data, .prep_data = mmci_dmae_prep_data,
.unprep_data = mmci_dmae_unprep_data, .unprep_data = mmci_dmae_unprep_data,
.get_datactrl_cfg = mmci_get_dctrl_cfg,
.get_next_data = mmci_dmae_get_next_data, .get_next_data = mmci_dmae_get_next_data,
.dma_setup = mmci_dmae_setup, .dma_setup = mmci_dmae_setup,
.dma_release = mmci_dmae_release, .dma_release = mmci_dmae_release,
@ -959,12 +945,22 @@ static struct mmci_host_ops mmci_variant_ops = {
.dma_finalize = mmci_dmae_finalize, .dma_finalize = mmci_dmae_finalize,
.dma_error = mmci_dmae_error, .dma_error = mmci_dmae_error,
}; };
#else
static struct mmci_host_ops mmci_variant_ops = {
.get_datactrl_cfg = mmci_get_dctrl_cfg,
};
#endif
void mmci_variant_init(struct mmci_host *host) void mmci_variant_init(struct mmci_host *host)
{ {
host->ops = &mmci_variant_ops; host->ops = &mmci_variant_ops;
} }
#endif
void ux500v2_variant_init(struct mmci_host *host)
{
host->ops = &mmci_variant_ops;
host->ops->get_datactrl_cfg = ux500v2_get_dctrl_cfg;
}
static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq) static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq)
{ {
@ -1000,7 +996,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
unsigned int datactrl, timeout, irqmask; unsigned int datactrl, timeout, irqmask;
unsigned long long clks; unsigned long long clks;
void __iomem *base; void __iomem *base;
int blksz_bits;
dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n", dev_dbg(mmc_dev(host->mmc), "blksz %04x blks %04x flags %08x\n",
data->blksz, data->blocks, data->flags); data->blksz, data->blocks, data->flags);
@ -1018,18 +1013,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
writel(timeout, base + MMCIDATATIMER); writel(timeout, base + MMCIDATATIMER);
writel(host->size, base + MMCIDATALENGTH); writel(host->size, base + MMCIDATALENGTH);
blksz_bits = ffs(data->blksz) - 1; datactrl = host->ops->get_datactrl_cfg(host);
BUG_ON(1 << blksz_bits != data->blksz); datactrl |= host->data->flags & MMC_DATA_READ ? MCI_DPSM_DIRECTION : 0;
if (variant->blksz_datactrl16)
datactrl = variant->datactrl_dpsm_enable | (data->blksz << 16);
else if (variant->blksz_datactrl4)
datactrl = variant->datactrl_dpsm_enable | (data->blksz << 4);
else
datactrl = variant->datactrl_dpsm_enable | blksz_bits << 4;
if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION;
if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
u32 clk; u32 clk;
@ -1220,12 +1205,13 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
unsigned int status) unsigned int status)
{ {
void __iomem *base = host->base; void __iomem *base = host->base;
bool sbc; bool sbc, busy_resp;
if (!cmd) if (!cmd)
return; return;
sbc = (cmd == host->mrq->sbc); sbc = (cmd == host->mrq->sbc);
busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
/* /*
* We need to be one of these interrupts to be considered worth * We need to be one of these interrupts to be considered worth
@ -1239,8 +1225,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
/* /*
* ST Micro variant: handle busy detection. * ST Micro variant: handle busy detection.
*/ */
if (host->variant->busy_detect) { if (busy_resp && host->variant->busy_detect) {
bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
/* We are busy with a command, return */ /* We are busy with a command, return */
if (host->busy_status && if (host->busy_status &&
@ -1253,7 +1238,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
* that the special busy status bit is still set before * that the special busy status bit is still set before
* proceeding. * proceeding.
*/ */
if (!host->busy_status && busy_resp && if (!host->busy_status &&
!(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
(readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
@ -1550,9 +1535,10 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
} }
/* /*
* Don't poll for busy completion in irq context. * Busy detection has been handled by mmci_cmd_irq() above.
* Clear the status bit to prevent polling in IRQ context.
*/ */
if (host->variant->busy_detect && host->busy_status) if (host->variant->busy_detect_flag)
status &= ~host->variant->busy_detect_flag; status &= ~host->variant->busy_detect_flag;
ret = 1; ret = 1;

View File

@ -131,6 +131,11 @@
/* Control register extensions in the Qualcomm versions */ /* Control register extensions in the Qualcomm versions */
#define MCI_DPSM_QCOM_DATA_PEND BIT(17) #define MCI_DPSM_QCOM_DATA_PEND BIT(17)
#define MCI_DPSM_QCOM_RX_DATA_PEND BIT(20) #define MCI_DPSM_QCOM_RX_DATA_PEND BIT(20)
/* Control register extensions in STM32 versions */
#define MCI_DPSM_STM32_MODE_BLOCK (0 << 2)
#define MCI_DPSM_STM32_MODE_SDIO (1 << 2)
#define MCI_DPSM_STM32_MODE_STREAM (2 << 2)
#define MCI_DPSM_STM32_MODE_BLOCK_STOP (3 << 2)
#define MMCIDATACNT 0x030 #define MMCIDATACNT 0x030
#define MMCISTATUS 0x034 #define MMCISTATUS 0x034
@ -275,12 +280,8 @@ struct mmci_host;
* @st_clkdiv: true if using a ST-specific clock divider algorithm * @st_clkdiv: true if using a ST-specific clock divider algorithm
* @stm32_clkdiv: true if using a STM32-specific clock divider algorithm * @stm32_clkdiv: true if using a STM32-specific clock divider algorithm
* @datactrl_mask_ddrmode: ddr mode mask in datactrl register. * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
* @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
* register
* @datactrl_mask_sdio: SDIO enable mask in datactrl register * @datactrl_mask_sdio: SDIO enable mask in datactrl register
* @datactrl_blksz: block size in power of two * @datactrl_blksz: block size in power of two
* @datactrl_dpsm_enable: enable value for DPSM
* @datactrl_first: true if data must be setup before send command * @datactrl_first: true if data must be setup before send command
* @datacnt_useless: true if you could not use datacnt register to read * @datacnt_useless: true if you could not use datacnt register to read
* remaining data * remaining data
@ -325,14 +326,11 @@ struct variant_data {
unsigned int datactrl_mask_ddrmode; unsigned int datactrl_mask_ddrmode;
unsigned int datactrl_mask_sdio; unsigned int datactrl_mask_sdio;
unsigned int datactrl_blocksz; unsigned int datactrl_blocksz;
unsigned int datactrl_dpsm_enable;
u8 datactrl_first:1; u8 datactrl_first:1;
u8 datacnt_useless:1; u8 datacnt_useless:1;
u8 st_sdio:1; u8 st_sdio:1;
u8 st_clkdiv:1; u8 st_clkdiv:1;
u8 stm32_clkdiv:1; u8 stm32_clkdiv:1;
u8 blksz_datactrl16:1;
u8 blksz_datactrl4:1;
u32 pwrreg_powerup; u32 pwrreg_powerup;
u32 f_max; u32 f_max;
u8 signal_direction:1; u8 signal_direction:1;
@ -362,6 +360,7 @@ struct mmci_host_ops {
bool next); bool next);
void (*unprep_data)(struct mmci_host *host, struct mmc_data *data, void (*unprep_data)(struct mmci_host *host, struct mmc_data *data,
int err); int err);
u32 (*get_datactrl_cfg)(struct mmci_host *host);
void (*get_next_data)(struct mmci_host *host, struct mmc_data *data); void (*get_next_data)(struct mmci_host *host, struct mmc_data *data);
int (*dma_setup)(struct mmci_host *host); int (*dma_setup)(struct mmci_host *host);
void (*dma_release)(struct mmci_host *host); void (*dma_release)(struct mmci_host *host);
@ -429,6 +428,12 @@ struct mmci_host {
void mmci_write_clkreg(struct mmci_host *host, u32 clk); void mmci_write_clkreg(struct mmci_host *host, u32 clk);
void mmci_write_pwrreg(struct mmci_host *host, u32 pwr); void mmci_write_pwrreg(struct mmci_host *host, u32 pwr);
static inline u32 mmci_dctrl_blksz(struct mmci_host *host)
{
return (ffs(host->data->blksz) - 1) << 4;
}
#ifdef CONFIG_DMA_ENGINE
int mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data, int mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data,
bool next); bool next);
void mmci_dmae_unprep_data(struct mmci_host *host, struct mmc_data *data, void mmci_dmae_unprep_data(struct mmci_host *host, struct mmc_data *data,
@ -439,3 +444,16 @@ void mmci_dmae_release(struct mmci_host *host);
int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl); int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl);
void mmci_dmae_finalize(struct mmci_host *host, struct mmc_data *data); void mmci_dmae_finalize(struct mmci_host *host, struct mmc_data *data);
void mmci_dmae_error(struct mmci_host *host); void mmci_dmae_error(struct mmci_host *host);
#endif
#ifdef CONFIG_MMC_QCOM_DML
void qcom_variant_init(struct mmci_host *host);
#else
static inline void qcom_variant_init(struct mmci_host *host) {}
#endif
#ifdef CONFIG_MMC_STM32_SDMMC
void sdmmc_variant_init(struct mmci_host *host);
#else
static inline void sdmmc_variant_init(struct mmci_host *host) {}
#endif

View File

@ -54,10 +54,15 @@
#define DML_OFFSET 0x800 #define DML_OFFSET 0x800
void dml_start_xfer(struct mmci_host *host, struct mmc_data *data) static int qcom_dma_start(struct mmci_host *host, unsigned int *datactrl)
{ {
u32 config; u32 config;
void __iomem *base = host->base + DML_OFFSET; void __iomem *base = host->base + DML_OFFSET;
struct mmc_data *data = host->data;
int ret = mmci_dmae_start(host, datactrl);
if (ret)
return ret;
if (data->flags & MMC_DATA_READ) { if (data->flags & MMC_DATA_READ) {
/* Read operation: configure DML for producer operation */ /* Read operation: configure DML for producer operation */
@ -96,6 +101,7 @@ void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
/* make sure the dml is configured before dma is triggered */ /* make sure the dml is configured before dma is triggered */
wmb(); wmb();
return 0;
} }
static int of_get_dml_pipe_index(struct device_node *np, const char *name) static int of_get_dml_pipe_index(struct device_node *np, const char *name)
@ -133,7 +139,6 @@ static int qcom_dma_setup(struct mmci_host *host)
producer_id = of_get_dml_pipe_index(np, "rx"); producer_id = of_get_dml_pipe_index(np, "rx");
if (producer_id < 0 || consumer_id < 0) { if (producer_id < 0 || consumer_id < 0) {
host->variant->qcom_dml = false;
mmci_dmae_release(host); mmci_dmae_release(host);
return -EINVAL; return -EINVAL;
} }
@ -183,13 +188,19 @@ static int qcom_dma_setup(struct mmci_host *host)
return 0; return 0;
} }
static u32 qcom_get_dctrl_cfg(struct mmci_host *host)
{
return MCI_DPSM_ENABLE | (host->data->blksz << 4);
}
static struct mmci_host_ops qcom_variant_ops = { static struct mmci_host_ops qcom_variant_ops = {
.prep_data = mmci_dmae_prep_data, .prep_data = mmci_dmae_prep_data,
.unprep_data = mmci_dmae_unprep_data, .unprep_data = mmci_dmae_unprep_data,
.get_datactrl_cfg = qcom_get_dctrl_cfg,
.get_next_data = mmci_dmae_get_next_data, .get_next_data = mmci_dmae_get_next_data,
.dma_setup = qcom_dma_setup, .dma_setup = qcom_dma_setup,
.dma_release = mmci_dmae_release, .dma_release = mmci_dmae_release,
.dma_start = mmci_dmae_start, .dma_start = qcom_dma_start,
.dma_finalize = mmci_dmae_finalize, .dma_finalize = mmci_dmae_finalize,
.dma_error = mmci_dmae_error, .dma_error = mmci_dmae_error,
}; };

View File

@ -1,30 +0,0 @@
/*
*
* Copyright (c) 2011, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __MMC_QCOM_DML_H__
#define __MMC_QCOM_DML_H__
#ifdef CONFIG_MMC_QCOM_DML
void qcom_variant_init(struct mmci_host *host);
void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
#else
static inline void qcom_variant_init(struct mmci_host *host)
{
}
static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
{
}
#endif /* CONFIG_MMC_QCOM_DML */
#endif /* __MMC_QCOM_DML_H__ */

View File

@ -265,10 +265,28 @@ static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr)
} }
} }
static u32 sdmmc_get_dctrl_cfg(struct mmci_host *host)
{
u32 datactrl;
datactrl = mmci_dctrl_blksz(host);
if (host->mmc->card && mmc_card_sdio(host->mmc->card) &&
host->data->blocks == 1)
datactrl |= MCI_DPSM_STM32_MODE_SDIO;
else if (host->data->stop && !host->mrq->sbc)
datactrl |= MCI_DPSM_STM32_MODE_BLOCK_STOP;
else
datactrl |= MCI_DPSM_STM32_MODE_BLOCK;
return datactrl;
}
static struct mmci_host_ops sdmmc_variant_ops = { static struct mmci_host_ops sdmmc_variant_ops = {
.validate_data = sdmmc_idma_validate_data, .validate_data = sdmmc_idma_validate_data,
.prep_data = sdmmc_idma_prep_data, .prep_data = sdmmc_idma_prep_data,
.unprep_data = sdmmc_idma_unprep_data, .unprep_data = sdmmc_idma_unprep_data,
.get_datactrl_cfg = sdmmc_get_dctrl_cfg,
.dma_setup = sdmmc_idma_setup, .dma_setup = sdmmc_idma_setup,
.dma_start = sdmmc_idma_start, .dma_start = sdmmc_idma_start,
.dma_finalize = sdmmc_idma_finalize, .dma_finalize = sdmmc_idma_finalize,

View File

@ -300,6 +300,8 @@
#define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */ #define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */
#define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */ #define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */
#define DEFAULT_DEBOUNCE (8) /* 8 cycles CD debounce */
#define PAD_DELAY_MAX 32 /* PAD delay cells */ #define PAD_DELAY_MAX 32 /* PAD delay cells */
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/* Descriptor Structure */ /* Descriptor Structure */
@ -372,6 +374,7 @@ struct mtk_mmc_compatible {
bool stop_clk_fix; bool stop_clk_fix;
bool enhance_rx; bool enhance_rx;
bool support_64g; bool support_64g;
bool use_internal_cd;
}; };
struct msdc_tune_para { struct msdc_tune_para {
@ -430,6 +433,7 @@ struct msdc_host {
bool hs400_cmd_resp_sel_rising; bool hs400_cmd_resp_sel_rising;
/* cmd response sample selection for HS400 */ /* cmd response sample selection for HS400 */
bool hs400_mode; /* current eMMC will run at hs400 mode */ bool hs400_mode; /* current eMMC will run at hs400 mode */
bool internal_cd; /* Use internal card-detect logic */
struct msdc_save_para save_para; /* used when gate HCLK */ struct msdc_save_para save_para; /* used when gate HCLK */
struct msdc_tune_para def_tune_para; /* default tune setting */ struct msdc_tune_para def_tune_para; /* default tune setting */
struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */ struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */
@ -507,6 +511,28 @@ static const struct mtk_mmc_compatible mt7622_compat = {
.support_64g = false, .support_64g = false,
}; };
static const struct mtk_mmc_compatible mt8516_compat = {
.clk_div_bits = 12,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
};
static const struct mtk_mmc_compatible mt7620_compat = {
.clk_div_bits = 8,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE,
.async_fifo = false,
.data_tune = false,
.busy_check = false,
.stop_clk_fix = false,
.enhance_rx = false,
.use_internal_cd = true,
};
static const struct of_device_id msdc_of_ids[] = { static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat}, { .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat}, { .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
@ -514,6 +540,8 @@ static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat}, { .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat}, { .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat}, { .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
{ .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat},
{ .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat},
{} {}
}; };
MODULE_DEVICE_TABLE(of, msdc_of_ids); MODULE_DEVICE_TABLE(of, msdc_of_ids);
@ -1407,6 +1435,12 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
sdio_signal_irq(host->mmc); sdio_signal_irq(host->mmc);
} }
if ((events & event_mask) & MSDC_INT_CDSC) {
if (host->internal_cd)
mmc_detect_change(host->mmc, msecs_to_jiffies(20));
events &= ~MSDC_INT_CDSC;
}
if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ))) if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ)))
break; break;
@ -1440,14 +1474,24 @@ static void msdc_init_hw(struct msdc_host *host)
/* Reset */ /* Reset */
msdc_reset_hw(host); msdc_reset_hw(host);
/* Disable card detection */
sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
/* Disable and clear all interrupts */ /* Disable and clear all interrupts */
writel(0, host->base + MSDC_INTEN); writel(0, host->base + MSDC_INTEN);
val = readl(host->base + MSDC_INT); val = readl(host->base + MSDC_INT);
writel(val, host->base + MSDC_INT); writel(val, host->base + MSDC_INT);
/* Configure card detection */
if (host->internal_cd) {
sdr_set_field(host->base + MSDC_PS, MSDC_PS_CDDEBOUNCE,
DEFAULT_DEBOUNCE);
sdr_set_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CDSC);
sdr_set_bits(host->base + SDC_CFG, SDC_CFG_INSWKUP);
} else {
sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_INSWKUP);
sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CDSC);
}
if (host->top_base) { if (host->top_base) {
writel(0, host->top_base + EMMC_TOP_CONTROL); writel(0, host->top_base + EMMC_TOP_CONTROL);
writel(0, host->top_base + EMMC_TOP_CMD); writel(0, host->top_base + EMMC_TOP_CMD);
@ -1557,6 +1601,13 @@ static void msdc_init_hw(struct msdc_host *host)
static void msdc_deinit_hw(struct msdc_host *host) static void msdc_deinit_hw(struct msdc_host *host)
{ {
u32 val; u32 val;
if (host->internal_cd) {
/* Disabled card-detect */
sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_INSWKUP);
}
/* Disable and clear all interrupts */ /* Disable and clear all interrupts */
writel(0, host->base + MSDC_INTEN); writel(0, host->base + MSDC_INTEN);
@ -2055,13 +2106,31 @@ static void msdc_ack_sdio_irq(struct mmc_host *mmc)
__msdc_enable_sdio_irq(mmc, 1); __msdc_enable_sdio_irq(mmc, 1);
} }
static int msdc_get_cd(struct mmc_host *mmc)
{
struct msdc_host *host = mmc_priv(mmc);
int val;
if (mmc->caps & MMC_CAP_NONREMOVABLE)
return 1;
if (!host->internal_cd)
return mmc_gpio_get_cd(mmc);
val = readl(host->base + MSDC_PS) & MSDC_PS_CDSTS;
if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
return !!val;
else
return !val;
}
static const struct mmc_host_ops mt_msdc_ops = { static const struct mmc_host_ops mt_msdc_ops = {
.post_req = msdc_post_req, .post_req = msdc_post_req,
.pre_req = msdc_pre_req, .pre_req = msdc_pre_req,
.request = msdc_ops_request, .request = msdc_ops_request,
.set_ios = msdc_ops_set_ios, .set_ios = msdc_ops_set_ios,
.get_ro = mmc_gpio_get_ro, .get_ro = mmc_gpio_get_ro,
.get_cd = mmc_gpio_get_cd, .get_cd = msdc_get_cd,
.enable_sdio_irq = msdc_enable_sdio_irq, .enable_sdio_irq = msdc_enable_sdio_irq,
.ack_sdio_irq = msdc_ack_sdio_irq, .ack_sdio_irq = msdc_ack_sdio_irq,
.start_signal_voltage_switch = msdc_ops_switch_volt, .start_signal_voltage_switch = msdc_ops_switch_volt,
@ -2123,9 +2192,11 @@ static int msdc_drv_probe(struct platform_device *pdev)
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
host->top_base = devm_ioremap_resource(&pdev->dev, res); if (res) {
if (IS_ERR(host->top_base)) host->top_base = devm_ioremap_resource(&pdev->dev, res);
host->top_base = NULL; if (IS_ERR(host->top_base))
host->top_base = NULL;
}
ret = mmc_regulator_get_supply(mmc); ret = mmc_regulator_get_supply(mmc);
if (ret) if (ret)
@ -2191,6 +2262,16 @@ static int msdc_drv_probe(struct platform_device *pdev)
else else
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095); mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
if (!(mmc->caps & MMC_CAP_NONREMOVABLE) &&
!mmc_can_gpio_cd(mmc) &&
host->dev_comp->use_internal_cd) {
/*
* Is removable but no GPIO declared, so
* use internal functionality.
*/
host->internal_cd = true;
}
if (mmc->caps & MMC_CAP_SDIO_IRQ) if (mmc->caps & MMC_CAP_SDIO_IRQ)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
@ -2227,7 +2308,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
msdc_init_hw(host); msdc_init_hw(host);
ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq, ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT, pdev->name, host); IRQF_TRIGGER_NONE, pdev->name, host);
if (ret) if (ret)
goto release; goto release;

View File

@ -648,7 +648,8 @@ static int mxs_mmc_probe(struct platform_device *pdev)
/* set mmc core parameters */ /* set mmc core parameters */
mmc->ops = &mxs_mmc_ops; mmc->ops = &mxs_mmc_ops;
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23; MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23 |
MMC_CAP_ERASE;
host->broken_cd = of_property_read_bool(np, "broken-cd"); host->broken_cd = of_property_read_bool(np, "broken-cd");

View File

@ -1,14 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* /*
* OpenFirmware bindings for the MMC-over-SPI driver * OpenFirmware bindings for the MMC-over-SPI driver
* *
* Copyright (c) MontaVista Software, Inc. 2008. * Copyright (c) MontaVista Software, Inc. 2008.
* *
* Author: Anton Vorontsov <avorontsov@ru.mvista.com> * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>

View File

@ -2077,7 +2077,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
host = platform_get_drvdata(to_platform_device(dev)); host = dev_get_drvdata(dev);
omap_hsmmc_context_save(host); omap_hsmmc_context_save(host);
dev_dbg(dev, "disabled\n"); dev_dbg(dev, "disabled\n");
@ -2118,7 +2118,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
struct omap_hsmmc_host *host; struct omap_hsmmc_host *host;
unsigned long flags; unsigned long flags;
host = platform_get_drvdata(to_platform_device(dev)); host = dev_get_drvdata(dev);
omap_hsmmc_context_restore(host); omap_hsmmc_context_restore(host);
dev_dbg(dev, "enabled\n"); dev_dbg(dev, "enabled\n");

View File

@ -3,7 +3,7 @@
* Renesas Mobile SDHI * Renesas Mobile SDHI
* *
* Copyright (C) 2017 Horms Solutions Ltd., Simon Horman * Copyright (C) 2017 Horms Solutions Ltd., Simon Horman
* Copyright (C) 2017 Renesas Electronics Corporation * Copyright (C) 2017-19 Renesas Electronics Corporation
*/ */
#ifndef RENESAS_SDHI_H #ifndef RENESAS_SDHI_H

View File

@ -2,8 +2,8 @@
/* /*
* Renesas SDHI * Renesas SDHI
* *
* Copyright (C) 2015-17 Renesas Electronics Corporation * Copyright (C) 2015-19 Renesas Electronics Corporation
* Copyright (C) 2016-17 Sang Engineering, Wolfram Sang * Copyright (C) 2016-19 Sang Engineering, Wolfram Sang
* Copyright (C) 2016-17 Horms Solutions, Simon Horman * Copyright (C) 2016-17 Horms Solutions, Simon Horman
* Copyright (C) 2009 Magnus Damm * Copyright (C) 2009 Magnus Damm
* *
@ -779,14 +779,14 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX) if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
mmc_data->max_blk_count = U16_MAX; mmc_data->max_blk_count = U16_MAX;
ret = tmio_mmc_host_probe(host);
if (ret < 0)
goto edisclk;
/* One Gen2 SDHI incarnation does NOT have a CBSY bit */ /* One Gen2 SDHI incarnation does NOT have a CBSY bit */
if (ver == SDHI_VER_GEN2_SDR50) if (ver == SDHI_VER_GEN2_SDR50)
mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY; mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY;
ret = tmio_mmc_host_probe(host);
if (ret < 0)
goto edisclk;
/* Enable tuning iff we have an SCC and a supported mode */ /* Enable tuning iff we have an SCC and a supported mode */
if (of_data && of_data->scc_offset && if (of_data && of_data->scc_offset &&
(host->mmc->caps & MMC_CAP_UHS_SDR104 || (host->mmc->caps & MMC_CAP_UHS_SDR104 ||

View File

@ -2,8 +2,9 @@
/* /*
* DMA support for Internal DMAC with SDHI SD/SDIO controller * DMA support for Internal DMAC with SDHI SD/SDIO controller
* *
* Copyright (C) 2016-17 Renesas Electronics Corporation * Copyright (C) 2016-19 Renesas Electronics Corporation
* Copyright (C) 2016-17 Horms Solutions, Simon Horman * Copyright (C) 2016-17 Horms Solutions, Simon Horman
* Copyright (C) 2018-19 Sang Engineering, Wolfram Sang
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
@ -95,8 +96,8 @@ static const struct renesas_sdhi_of_data of_rza2_compatible = {
.scc_offset = 0 - 0x1000, .scc_offset = 0 - 0x1000,
.taps = rcar_gen3_scc_taps, .taps = rcar_gen3_scc_taps,
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
/* DMAC can handle 0xffffffff blk count but only 1 segment */ /* DMAC can handle 32bit blk count but only 1 segment */
.max_blk_count = 0xffffffff, .max_blk_count = UINT_MAX / TMIO_MAX_BLK_SIZE,
.max_segs = 1, .max_segs = 1,
}; };
@ -110,8 +111,8 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
.scc_offset = 0x1000, .scc_offset = 0x1000,
.taps = rcar_gen3_scc_taps, .taps = rcar_gen3_scc_taps,
.taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
/* DMAC can handle 0xffffffff blk count but only 1 segment */ /* DMAC can handle 32bit blk count but only 1 segment */
.max_blk_count = 0xffffffff, .max_blk_count = UINT_MAX / TMIO_MAX_BLK_SIZE,
.max_segs = 1, .max_segs = 1,
}; };

View File

@ -2,8 +2,8 @@
/* /*
* DMA support use of SYS DMAC with SDHI SD/SDIO controller * DMA support use of SYS DMAC with SDHI SD/SDIO controller
* *
* Copyright (C) 2016-17 Renesas Electronics Corporation * Copyright (C) 2016-19 Renesas Electronics Corporation
* Copyright (C) 2016-17 Sang Engineering, Wolfram Sang * Copyright (C) 2016-19 Sang Engineering, Wolfram Sang
* Copyright (C) 2017 Horms Solutions, Simon Horman * Copyright (C) 2017 Horms Solutions, Simon Horman
* Copyright (C) 2010-2011 Guennadi Liakhovetski * Copyright (C) 2010-2011 Guennadi Liakhovetski
*/ */
@ -65,7 +65,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
.scc_offset = 0x0300, .scc_offset = 0x0300,
.taps = rcar_gen2_scc_taps, .taps = rcar_gen2_scc_taps,
.taps_num = ARRAY_SIZE(rcar_gen2_scc_taps), .taps_num = ARRAY_SIZE(rcar_gen2_scc_taps),
.max_blk_count = 0xffffffff, .max_blk_count = UINT_MAX / TMIO_MAX_BLK_SIZE,
}; };
/* Definitions for sampling clocks */ /* Definitions for sampling clocks */

View File

@ -14,6 +14,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm_qos.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
@ -73,6 +74,7 @@
#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0) #define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1) #define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1)
#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3 #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3
#define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT (4 << 20)
#define ESDHC_STROBE_DLL_STATUS 0x74 #define ESDHC_STROBE_DLL_STATUS 0x74
#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1) #define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
@ -156,6 +158,8 @@
#define ESDHC_FLAG_HS400_ES BIT(11) #define ESDHC_FLAG_HS400_ES BIT(11)
/* The IP has Host Controller Interface for Command Queuing */ /* The IP has Host Controller Interface for Command Queuing */
#define ESDHC_FLAG_CQHCI BIT(12) #define ESDHC_FLAG_CQHCI BIT(12)
/* need request pmqos during low power */
#define ESDHC_FLAG_PMQOS BIT(13)
struct esdhc_soc_data { struct esdhc_soc_data {
u32 flags; u32 flags;
@ -204,6 +208,12 @@ static const struct esdhc_soc_data usdhc_imx7d_data = {
| ESDHC_FLAG_HS400, | ESDHC_FLAG_HS400,
}; };
static struct esdhc_soc_data usdhc_imx7ulp_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400,
};
static struct esdhc_soc_data usdhc_imx8qxp_data = { static struct esdhc_soc_data usdhc_imx8qxp_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
@ -229,6 +239,7 @@ struct pltfm_imx_data {
WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
} multiblock_status; } multiblock_status;
u32 is_ddr; u32 is_ddr;
struct pm_qos_request pm_qos_req;
}; };
static const struct platform_device_id imx_esdhc_devtype[] = { static const struct platform_device_id imx_esdhc_devtype[] = {
@ -257,6 +268,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, }, { .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, }, { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
{ .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, },
{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, }, { .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
{ /* sentinel */ } { /* sentinel */ }
}; };
@ -983,15 +995,19 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host)
/* force a reset on strobe dll */ /* force a reset on strobe dll */
writel(ESDHC_STROBE_DLL_CTRL_RESET, writel(ESDHC_STROBE_DLL_CTRL_RESET,
host->ioaddr + ESDHC_STROBE_DLL_CTRL); host->ioaddr + ESDHC_STROBE_DLL_CTRL);
/* clear the reset bit on strobe dll before any setting */
writel(0, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
/* /*
* enable strobe dll ctrl and adjust the delay target * enable strobe dll ctrl and adjust the delay target
* for the uSDHC loopback read clock * for the uSDHC loopback read clock
*/ */
v = ESDHC_STROBE_DLL_CTRL_ENABLE | v = ESDHC_STROBE_DLL_CTRL_ENABLE |
ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT |
(7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL); writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
/* wait 1us to make sure strobe dll status register stable */ /* wait 5us to make sure strobe dll status register stable */
udelay(1); udelay(5);
v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS); v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK)) if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
dev_warn(mmc_dev(host->mmc), dev_warn(mmc_dev(host->mmc),
@ -1436,6 +1452,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *) imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
pdev->id_entry->driver_data; pdev->id_entry->driver_data;
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
pm_qos_add_request(&imx_data->pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, 0);
imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(imx_data->clk_ipg)) { if (IS_ERR(imx_data->clk_ipg)) {
err = PTR_ERR(imx_data->clk_ipg); err = PTR_ERR(imx_data->clk_ipg);
@ -1557,6 +1577,8 @@ disable_ipg_clk:
disable_per_clk: disable_per_clk:
clk_disable_unprepare(imx_data->clk_per); clk_disable_unprepare(imx_data->clk_per);
free_sdhci: free_sdhci:
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
pm_qos_remove_request(&imx_data->pm_qos_req);
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
return err; return err;
} }
@ -1578,6 +1600,9 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
clk_disable_unprepare(imx_data->clk_ipg); clk_disable_unprepare(imx_data->clk_ipg);
clk_disable_unprepare(imx_data->clk_ahb); clk_disable_unprepare(imx_data->clk_ahb);
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
pm_qos_remove_request(&imx_data->pm_qos_req);
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
return 0; return 0;
@ -1649,6 +1674,9 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev)
} }
clk_disable_unprepare(imx_data->clk_ahb); clk_disable_unprepare(imx_data->clk_ahb);
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
pm_qos_remove_request(&imx_data->pm_qos_req);
return ret; return ret;
} }
@ -1659,9 +1687,13 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
int err; int err;
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
pm_qos_add_request(&imx_data->pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, 0);
err = clk_prepare_enable(imx_data->clk_ahb); err = clk_prepare_enable(imx_data->clk_ahb);
if (err) if (err)
return err; goto remove_pm_qos_request;
if (!sdhci_sdio_irq_enabled(host)) { if (!sdhci_sdio_irq_enabled(host)) {
err = clk_prepare_enable(imx_data->clk_per); err = clk_prepare_enable(imx_data->clk_per);
@ -1690,6 +1722,9 @@ disable_per_clk:
clk_disable_unprepare(imx_data->clk_per); clk_disable_unprepare(imx_data->clk_per);
disable_ahb_clk: disable_ahb_clk:
clk_disable_unprepare(imx_data->clk_ahb); clk_disable_unprepare(imx_data->clk_ahb);
remove_pm_qos_request:
if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS)
pm_qos_remove_request(&imx_data->pm_qos_req);
return err; return err;
} }
#endif #endif

View File

@ -832,7 +832,10 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
host->mmc_host_ops.start_signal_voltage_switch = host->mmc_host_ops.start_signal_voltage_switch =
sdhci_arasan_voltage_switch; sdhci_arasan_voltage_switch;
sdhci_arasan->has_cqe = true; sdhci_arasan->has_cqe = true;
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; host->mmc->caps2 |= MMC_CAP2_CQE;
if (!of_property_read_bool(np, "disable-cqe-dcmd"))
host->mmc->caps2 |= MMC_CAP2_CQE_DCMD;
} }
ret = sdhci_arasan_add_host(sdhci_arasan); ret = sdhci_arasan_add_host(sdhci_arasan);

View File

@ -24,6 +24,7 @@
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "sdhci-esdhc.h" #include "sdhci-esdhc.h"
@ -81,6 +82,7 @@ struct sdhci_esdhc {
bool quirk_limited_clk_division; bool quirk_limited_clk_division;
bool quirk_unreliable_pulse_detection; bool quirk_unreliable_pulse_detection;
bool quirk_fixup_tuning; bool quirk_fixup_tuning;
bool quirk_ignore_data_inhibit;
unsigned int peripheral_clock; unsigned int peripheral_clock;
const struct esdhc_clk_fixup *clk_fixup; const struct esdhc_clk_fixup *clk_fixup;
u32 div_ratio; u32 div_ratio;
@ -147,6 +149,19 @@ static u32 esdhc_readl_fixup(struct sdhci_host *host,
return ret; return ret;
} }
/*
* Some controllers have unreliable Data Line Active
* bit for commands with busy signal. This affects
* Command Inhibit (data) bit. Just ignore it since
* MMC core driver has already polled card status
* with CMD13 after any command with busy siganl.
*/
if ((spec_reg == SDHCI_PRESENT_STATE) &&
(esdhc->quirk_ignore_data_inhibit == true)) {
ret = value & ~SDHCI_DATA_INHIBIT;
return ret;
}
ret = value; ret = value;
return ret; return ret;
} }
@ -694,6 +709,9 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc"))
mdelay(5);
if (mask & SDHCI_RESET_ALL) { if (mask & SDHCI_RESET_ALL) {
val = sdhci_readl(host, ESDHC_TBCTL); val = sdhci_readl(host, ESDHC_TBCTL);
val &= ~ESDHC_TB_EN; val &= ~ESDHC_TB_EN;
@ -864,6 +882,25 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host,
sdhci_set_uhs_signaling(host, timing); sdhci_set_uhs_signaling(host, timing);
} }
static u32 esdhc_irq(struct sdhci_host *host, u32 intmask)
{
u32 command;
if (of_find_compatible_node(NULL, NULL,
"fsl,p2020-esdhc")) {
command = SDHCI_GET_CMD(sdhci_readw(host,
SDHCI_COMMAND));
if (command == MMC_WRITE_MULTIPLE_BLOCK &&
sdhci_readw(host, SDHCI_BLOCK_COUNT) &&
intmask & SDHCI_INT_DATA_END) {
intmask &= ~SDHCI_INT_DATA_END;
sdhci_writel(host, SDHCI_INT_DATA_END,
SDHCI_INT_STATUS);
}
}
return intmask;
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static u32 esdhc_proctl; static u32 esdhc_proctl;
static int esdhc_of_suspend(struct device *dev) static int esdhc_of_suspend(struct device *dev)
@ -911,6 +948,7 @@ static const struct sdhci_ops sdhci_esdhc_be_ops = {
.set_bus_width = esdhc_pltfm_set_bus_width, .set_bus_width = esdhc_pltfm_set_bus_width,
.reset = esdhc_reset, .reset = esdhc_reset,
.set_uhs_signaling = esdhc_set_uhs_signaling, .set_uhs_signaling = esdhc_set_uhs_signaling,
.irq = esdhc_irq,
}; };
static const struct sdhci_ops sdhci_esdhc_le_ops = { static const struct sdhci_ops sdhci_esdhc_le_ops = {
@ -928,6 +966,7 @@ static const struct sdhci_ops sdhci_esdhc_le_ops = {
.set_bus_width = esdhc_pltfm_set_bus_width, .set_bus_width = esdhc_pltfm_set_bus_width,
.reset = esdhc_reset, .reset = esdhc_reset,
.set_uhs_signaling = esdhc_set_uhs_signaling, .set_uhs_signaling = esdhc_set_uhs_signaling,
.irq = esdhc_irq,
}; };
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
@ -955,6 +994,7 @@ static struct soc_device_attribute soc_incorrect_hostver[] = {
static struct soc_device_attribute soc_fixup_sdhc_clkdivs[] = { static struct soc_device_attribute soc_fixup_sdhc_clkdivs[] = {
{ .family = "QorIQ LX2160A", .revision = "1.0", }, { .family = "QorIQ LX2160A", .revision = "1.0", },
{ .family = "QorIQ LX2160A", .revision = "2.0", },
{ }, { },
}; };
@ -1074,6 +1114,11 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
if (esdhc->vendor_ver > VENDOR_V_22) if (esdhc->vendor_ver > VENDOR_V_22)
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) {
host->quirks2 |= SDHCI_QUIRK_RESET_AFTER_REQUEST;
host->quirks2 |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
}
if (of_device_is_compatible(np, "fsl,p5040-esdhc") || if (of_device_is_compatible(np, "fsl,p5040-esdhc") ||
of_device_is_compatible(np, "fsl,p5020-esdhc") || of_device_is_compatible(np, "fsl,p5020-esdhc") ||
of_device_is_compatible(np, "fsl,p4080-esdhc") || of_device_is_compatible(np, "fsl,p4080-esdhc") ||
@ -1084,12 +1129,14 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) if (of_device_is_compatible(np, "fsl,ls1021a-esdhc"))
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
esdhc->quirk_ignore_data_inhibit = false;
if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
/* /*
* Freescale messed up with P2020 as it has a non-standard * Freescale messed up with P2020 as it has a non-standard
* host control register * host control register
*/ */
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL;
esdhc->quirk_ignore_data_inhibit = true;
} }
/* call to generic mmc_of_parse to support additional capabilities */ /* call to generic mmc_of_parse to support additional capabilities */

View File

@ -785,7 +785,7 @@ static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
sdhci_omap_start_clock(omap_host); sdhci_omap_start_clock(omap_host);
} }
void sdhci_omap_reset(struct sdhci_host *host, u8 mask) static void sdhci_omap_reset(struct sdhci_host *host, u8 mask)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);

View File

@ -31,6 +31,10 @@
#include <linux/mmc/sdhci-pci-data.h> #include <linux/mmc/sdhci-pci-data.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#ifdef CONFIG_X86
#include <asm/iosf_mbi.h>
#endif
#include "cqhci.h" #include "cqhci.h"
#include "sdhci.h" #include "sdhci.h"
@ -451,6 +455,50 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
.probe_slot = pch_hc_probe_slot, .probe_slot = pch_hc_probe_slot,
}; };
#ifdef CONFIG_X86
#define BYT_IOSF_SCCEP 0x63
#define BYT_IOSF_OCP_NETCTRL0 0x1078
#define BYT_IOSF_OCP_TIMEOUT_BASE GENMASK(10, 8)
static void byt_ocp_setting(struct pci_dev *pdev)
{
u32 val = 0;
if (pdev->device != PCI_DEVICE_ID_INTEL_BYT_EMMC &&
pdev->device != PCI_DEVICE_ID_INTEL_BYT_SDIO &&
pdev->device != PCI_DEVICE_ID_INTEL_BYT_SD &&
pdev->device != PCI_DEVICE_ID_INTEL_BYT_EMMC2)
return;
if (iosf_mbi_read(BYT_IOSF_SCCEP, MBI_CR_READ, BYT_IOSF_OCP_NETCTRL0,
&val)) {
dev_err(&pdev->dev, "%s read error\n", __func__);
return;
}
if (!(val & BYT_IOSF_OCP_TIMEOUT_BASE))
return;
val &= ~BYT_IOSF_OCP_TIMEOUT_BASE;
if (iosf_mbi_write(BYT_IOSF_SCCEP, MBI_CR_WRITE, BYT_IOSF_OCP_NETCTRL0,
val)) {
dev_err(&pdev->dev, "%s write error\n", __func__);
return;
}
dev_dbg(&pdev->dev, "%s completed\n", __func__);
}
#else
static inline void byt_ocp_setting(struct pci_dev *pdev)
{
}
#endif
enum { enum {
INTEL_DSM_FNS = 0, INTEL_DSM_FNS = 0,
INTEL_DSM_V18_SWITCH = 3, INTEL_DSM_V18_SWITCH = 3,
@ -715,6 +763,8 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot)
byt_read_dsm(slot); byt_read_dsm(slot);
byt_ocp_setting(slot->chip->pdev);
ops->execute_tuning = intel_execute_tuning; ops->execute_tuning = intel_execute_tuning;
ops->start_signal_voltage_switch = intel_start_signal_voltage_switch; ops->start_signal_voltage_switch = intel_start_signal_voltage_switch;
@ -938,7 +988,35 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int byt_resume(struct sdhci_pci_chip *chip)
{
byt_ocp_setting(chip->pdev);
return sdhci_pci_resume_host(chip);
}
#endif
#ifdef CONFIG_PM
static int byt_runtime_resume(struct sdhci_pci_chip *chip)
{
byt_ocp_setting(chip->pdev);
return sdhci_pci_runtime_resume_host(chip);
}
#endif
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
#ifdef CONFIG_PM_SLEEP
.resume = byt_resume,
#endif
#ifdef CONFIG_PM
.runtime_resume = byt_runtime_resume,
#endif
.allow_runtime_pm = true, .allow_runtime_pm = true,
.probe_slot = byt_emmc_probe_slot, .probe_slot = byt_emmc_probe_slot,
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
@ -972,6 +1050,12 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = {
}; };
static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = { static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = {
#ifdef CONFIG_PM_SLEEP
.resume = byt_resume,
#endif
#ifdef CONFIG_PM
.runtime_resume = byt_runtime_resume,
#endif
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED, SDHCI_QUIRK_NO_LED,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
@ -983,6 +1067,12 @@ static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = {
}; };
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
#ifdef CONFIG_PM_SLEEP
.resume = byt_resume,
#endif
#ifdef CONFIG_PM
.runtime_resume = byt_runtime_resume,
#endif
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED, SDHCI_QUIRK_NO_LED,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
@ -994,6 +1084,12 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
}; };
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
#ifdef CONFIG_PM_SLEEP
.resume = byt_resume,
#endif
#ifdef CONFIG_PM
.runtime_resume = byt_runtime_resume,
#endif
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED, SDHCI_QUIRK_NO_LED,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
@ -1576,6 +1672,8 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(INTEL, CNPH_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, CNPH_SD, intel_byt_sd),
SDHCI_PCI_DEVICE(INTEL, ICP_EMMC, intel_glk_emmc), SDHCI_PCI_DEVICE(INTEL, ICP_EMMC, intel_glk_emmc),
SDHCI_PCI_DEVICE(INTEL, ICP_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, ICP_SD, intel_byt_sd),
SDHCI_PCI_DEVICE(INTEL, CML_EMMC, intel_glk_emmc),
SDHCI_PCI_DEVICE(INTEL, CML_SD, intel_byt_sd),
SDHCI_PCI_DEVICE(O2, 8120, o2), SDHCI_PCI_DEVICE(O2, 8120, o2),
SDHCI_PCI_DEVICE(O2, 8220, o2), SDHCI_PCI_DEVICE(O2, 8220, o2),
SDHCI_PCI_DEVICE(O2, 8221, o2), SDHCI_PCI_DEVICE(O2, 8221, o2),

View File

@ -50,6 +50,8 @@
#define PCI_DEVICE_ID_INTEL_CNPH_SD 0xa375 #define PCI_DEVICE_ID_INTEL_CNPH_SD 0xa375
#define PCI_DEVICE_ID_INTEL_ICP_EMMC 0x34c4 #define PCI_DEVICE_ID_INTEL_ICP_EMMC 0x34c4
#define PCI_DEVICE_ID_INTEL_ICP_SD 0x34f8 #define PCI_DEVICE_ID_INTEL_ICP_SD 0x34f8
#define PCI_DEVICE_ID_INTEL_CML_EMMC 0x02c4
#define PCI_DEVICE_ID_INTEL_CML_SD 0x02f5
#define PCI_DEVICE_ID_SYSKONNECT_8000 0x8000 #define PCI_DEVICE_ID_SYSKONNECT_8000 0x8000
#define PCI_DEVICE_ID_VIA_95D0 0x95d0 #define PCI_DEVICE_ID_VIA_95D0 0x95d0

View File

@ -66,6 +66,22 @@
#define SDHCI_VNDR_TUN_CTRL0_0 0x1c0 #define SDHCI_VNDR_TUN_CTRL0_0 0x1c0
#define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000 #define SDHCI_VNDR_TUN_CTRL0_TUN_HW_TAP 0x20000
#define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK 0x03fc0000
#define SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT 18
#define SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK 0x00001fc0
#define SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT 6
#define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK 0x000e000
#define SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT 13
#define TRIES_128 2
#define TRIES_256 4
#define SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK 0x7
#define SDHCI_TEGRA_VNDR_TUN_CTRL1_0 0x1c4
#define SDHCI_TEGRA_VNDR_TUN_STATUS0 0x1C8
#define SDHCI_TEGRA_VNDR_TUN_STATUS1 0x1CC
#define SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK 0xFF
#define SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT 0x8
#define TUNING_WORD_BIT_SIZE 32
#define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4 #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4
#define SDHCI_AUTO_CAL_START BIT(31) #define SDHCI_AUTO_CAL_START BIT(31)
@ -90,6 +106,7 @@
#define NVQUIRK_HAS_PADCALIB BIT(6) #define NVQUIRK_HAS_PADCALIB BIT(6)
#define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7)
#define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8)
#define NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING BIT(9)
/* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
#define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000
@ -97,6 +114,8 @@
struct sdhci_tegra_soc_data { struct sdhci_tegra_soc_data {
const struct sdhci_pltfm_data *pdata; const struct sdhci_pltfm_data *pdata;
u32 nvquirks; u32 nvquirks;
u8 min_tap_delay;
u8 max_tap_delay;
}; };
/* Magic pull up and pull down pad calibration offsets */ /* Magic pull up and pull down pad calibration offsets */
@ -136,6 +155,8 @@ struct sdhci_tegra {
u32 default_trim; u32 default_trim;
u32 dqs_trim; u32 dqs_trim;
bool enable_hwcq; bool enable_hwcq;
unsigned long curr_clk_rate;
u8 tuned_tap_delay;
}; };
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
@ -241,6 +262,7 @@ static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
if (is_tuning_cmd) { if (is_tuning_cmd) {
udelay(1); udelay(1);
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
tegra_sdhci_configure_card_clk(host, clk_enabled); tegra_sdhci_configure_card_clk(host, clk_enabled);
} }
} }
@ -722,6 +744,7 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
*/ */
host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
clk_set_rate(pltfm_host->clk, host_clk); clk_set_rate(pltfm_host->clk, host_clk);
tegra_host->curr_clk_rate = host_clk;
if (tegra_host->ddr_signaling) if (tegra_host->ddr_signaling)
host->max_clk = host_clk; host->max_clk = host_clk;
else else
@ -770,6 +793,159 @@ static void tegra_sdhci_hs400_dll_cal(struct sdhci_host *host)
"HS400 delay line calibration timed out\n"); "HS400 delay line calibration timed out\n");
} }
static void tegra_sdhci_tap_correction(struct sdhci_host *host, u8 thd_up,
u8 thd_low, u8 fixed_tap)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
u32 val, tun_status;
u8 word, bit, edge1, tap, window;
bool tap_result;
bool start_fail = false;
bool start_pass = false;
bool end_pass = false;
bool first_fail = false;
bool first_pass = false;
u8 start_pass_tap = 0;
u8 end_pass_tap = 0;
u8 first_fail_tap = 0;
u8 first_pass_tap = 0;
u8 total_tuning_words = host->tuning_loop_count / TUNING_WORD_BIT_SIZE;
/*
* Read auto-tuned results and extract good valid passing window by
* filtering out un-wanted bubble/partial/merged windows.
*/
for (word = 0; word < total_tuning_words; word++) {
val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
val &= ~SDHCI_VNDR_TUN_CTRL0_TUN_WORD_SEL_MASK;
val |= word;
sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
tun_status = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS0);
bit = 0;
while (bit < TUNING_WORD_BIT_SIZE) {
tap = word * TUNING_WORD_BIT_SIZE + bit;
tap_result = tun_status & (1 << bit);
if (!tap_result && !start_fail) {
start_fail = true;
if (!first_fail) {
first_fail_tap = tap;
first_fail = true;
}
} else if (tap_result && start_fail && !start_pass) {
start_pass_tap = tap;
start_pass = true;
if (!first_pass) {
first_pass_tap = tap;
first_pass = true;
}
} else if (!tap_result && start_fail && start_pass &&
!end_pass) {
end_pass_tap = tap - 1;
end_pass = true;
} else if (tap_result && start_pass && start_fail &&
end_pass) {
window = end_pass_tap - start_pass_tap;
/* discard merged window and bubble window */
if (window >= thd_up || window < thd_low) {
start_pass_tap = tap;
end_pass = false;
} else {
/* set tap at middle of valid window */
tap = start_pass_tap + window / 2;
tegra_host->tuned_tap_delay = tap;
return;
}
}
bit++;
}
}
if (!first_fail) {
WARN_ON("no edge detected, continue with hw tuned delay.\n");
} else if (first_pass) {
/* set tap location at fixed tap relative to the first edge */
edge1 = first_fail_tap + (first_pass_tap - first_fail_tap) / 2;
if (edge1 - 1 > fixed_tap)
tegra_host->tuned_tap_delay = edge1 - fixed_tap;
else
tegra_host->tuned_tap_delay = edge1 + fixed_tap;
}
}
static void tegra_sdhci_post_tuning(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
u32 avg_tap_dly, val, min_tap_dly, max_tap_dly;
u8 fixed_tap, start_tap, end_tap, window_width;
u8 thdupper, thdlower;
u8 num_iter;
u32 clk_rate_mhz, period_ps, bestcase, worstcase;
/* retain HW tuned tap to use incase if no correction is needed */
val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
tegra_host->tuned_tap_delay = (val & SDHCI_CLOCK_CTRL_TAP_MASK) >>
SDHCI_CLOCK_CTRL_TAP_SHIFT;
if (soc_data->min_tap_delay && soc_data->max_tap_delay) {
min_tap_dly = soc_data->min_tap_delay;
max_tap_dly = soc_data->max_tap_delay;
clk_rate_mhz = tegra_host->curr_clk_rate / USEC_PER_SEC;
period_ps = USEC_PER_SEC / clk_rate_mhz;
bestcase = period_ps / min_tap_dly;
worstcase = period_ps / max_tap_dly;
/*
* Upper and Lower bound thresholds used to detect merged and
* bubble windows
*/
thdupper = (2 * worstcase + bestcase) / 2;
thdlower = worstcase / 4;
/*
* fixed tap is used when HW tuning result contains single edge
* and tap is set at fixed tap delay relative to the first edge
*/
avg_tap_dly = (period_ps * 2) / (min_tap_dly + max_tap_dly);
fixed_tap = avg_tap_dly / 2;
val = sdhci_readl(host, SDHCI_TEGRA_VNDR_TUN_STATUS1);
start_tap = val & SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
end_tap = (val >> SDHCI_TEGRA_VNDR_TUN_STATUS1_END_TAP_SHIFT) &
SDHCI_TEGRA_VNDR_TUN_STATUS1_TAP_MASK;
window_width = end_tap - start_tap;
num_iter = host->tuning_loop_count;
/*
* partial window includes edges of the tuning range.
* merged window includes more taps so window width is higher
* than upper threshold.
*/
if (start_tap == 0 || (end_tap == (num_iter - 1)) ||
(end_tap == num_iter - 2) || window_width >= thdupper) {
pr_debug("%s: Apply tuning correction\n",
mmc_hostname(host->mmc));
tegra_sdhci_tap_correction(host, thdupper, thdlower,
fixed_tap);
}
}
tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
}
static int tegra_sdhci_execute_hw_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
int err;
err = sdhci_execute_tuning(mmc, opcode);
if (!err && !host->tuning_err)
tegra_sdhci_post_tuning(host);
return err;
}
static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
unsigned timing) unsigned timing)
{ {
@ -778,16 +954,22 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
bool set_default_tap = false; bool set_default_tap = false;
bool set_dqs_trim = false; bool set_dqs_trim = false;
bool do_hs400_dll_cal = false; bool do_hs400_dll_cal = false;
u8 iter = TRIES_256;
u32 val;
tegra_host->ddr_signaling = false;
switch (timing) { switch (timing) {
case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_SDR50:
break;
case MMC_TIMING_UHS_SDR104: case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200: case MMC_TIMING_MMC_HS200:
/* Don't set default tap on tunable modes. */ /* Don't set default tap on tunable modes. */
iter = TRIES_128;
break; break;
case MMC_TIMING_MMC_HS400: case MMC_TIMING_MMC_HS400:
set_dqs_trim = true; set_dqs_trim = true;
do_hs400_dll_cal = true; do_hs400_dll_cal = true;
iter = TRIES_128;
break; break;
case MMC_TIMING_MMC_DDR52: case MMC_TIMING_MMC_DDR52:
case MMC_TIMING_UHS_DDR50: case MMC_TIMING_UHS_DDR50:
@ -799,11 +981,25 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
break; break;
} }
val = sdhci_readl(host, SDHCI_VNDR_TUN_CTRL0_0);
val &= ~(SDHCI_VNDR_TUN_CTRL0_TUN_ITER_MASK |
SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_MASK |
SDHCI_VNDR_TUN_CTRL0_MUL_M_MASK);
val |= (iter << SDHCI_VNDR_TUN_CTRL0_TUN_ITER_SHIFT |
0 << SDHCI_VNDR_TUN_CTRL0_START_TAP_VAL_SHIFT |
1 << SDHCI_VNDR_TUN_CTRL0_MUL_M_SHIFT);
sdhci_writel(host, val, SDHCI_VNDR_TUN_CTRL0_0);
sdhci_writel(host, 0, SDHCI_TEGRA_VNDR_TUN_CTRL1_0);
host->tuning_loop_count = (iter == TRIES_128) ? 128 : 256;
sdhci_set_uhs_signaling(host, timing); sdhci_set_uhs_signaling(host, timing);
tegra_sdhci_pad_autocalib(host); tegra_sdhci_pad_autocalib(host);
if (set_default_tap) if (tegra_host->tuned_tap_delay && !set_default_tap)
tegra_sdhci_set_tap(host, tegra_host->tuned_tap_delay);
else
tegra_sdhci_set_tap(host, tegra_host->default_tap); tegra_sdhci_set_tap(host, tegra_host->default_tap);
if (set_dqs_trim) if (set_dqs_trim)
@ -928,23 +1124,86 @@ static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
tegra_host->pad_calib_required = true; tegra_host->pad_calib_required = true;
} }
static void tegra_cqhci_writel(struct cqhci_host *cq_host, u32 val, int reg)
{
struct mmc_host *mmc = cq_host->mmc;
u8 ctrl;
ktime_t timeout;
bool timed_out;
/*
* During CQE resume/unhalt, CQHCI driver unhalts CQE prior to
* cqhci_host_ops enable where SDHCI DMA and BLOCK_SIZE registers need
* to be re-configured.
* Tegra CQHCI/SDHCI prevents write access to block size register when
* CQE is unhalted. So handling CQE resume sequence here to configure
* SDHCI block registers prior to exiting CQE halt state.
*/
if (reg == CQHCI_CTL && !(val & CQHCI_HALT) &&
cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) {
sdhci_cqe_enable(mmc);
writel(val, cq_host->mmio + reg);
timeout = ktime_add_us(ktime_get(), 50);
while (1) {
timed_out = ktime_compare(ktime_get(), timeout) > 0;
ctrl = cqhci_readl(cq_host, CQHCI_CTL);
if (!(ctrl & CQHCI_HALT) || timed_out)
break;
}
/*
* CQE usually resumes very quick, but incase if Tegra CQE
* doesn't resume retry unhalt.
*/
if (timed_out)
writel(val, cq_host->mmio + reg);
} else {
writel(val, cq_host->mmio + reg);
}
}
static void sdhci_tegra_update_dcmd_desc(struct mmc_host *mmc,
struct mmc_request *mrq, u64 *data)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(mmc_priv(mmc));
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
if (soc_data->nvquirks & NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING &&
mrq->cmd->flags & MMC_RSP_R1B)
*data |= CQHCI_CMD_TIMING(1);
}
static void sdhci_tegra_cqe_enable(struct mmc_host *mmc) static void sdhci_tegra_cqe_enable(struct mmc_host *mmc)
{ {
struct cqhci_host *cq_host = mmc->cqe_private; struct cqhci_host *cq_host = mmc->cqe_private;
u32 cqcfg = 0; u32 val;
/* /*
* Tegra SDMMC Controller design prevents write access to BLOCK_COUNT * Tegra CQHCI/SDMMC design prevents write access to sdhci block size
* registers when CQE is enabled. * register when CQE is enabled and unhalted.
* CQHCI driver enables CQE prior to activation, so disable CQE before
* programming block size in sdhci controller and enable it back.
*/ */
cqcfg = cqhci_readl(cq_host, CQHCI_CFG); if (!cq_host->activated) {
if (cqcfg & CQHCI_ENABLE) val = cqhci_readl(cq_host, CQHCI_CFG);
cqhci_writel(cq_host, (cqcfg & ~CQHCI_ENABLE), CQHCI_CFG); if (val & CQHCI_ENABLE)
cqhci_writel(cq_host, (val & ~CQHCI_ENABLE),
CQHCI_CFG);
sdhci_cqe_enable(mmc);
if (val & CQHCI_ENABLE)
cqhci_writel(cq_host, val, CQHCI_CFG);
}
sdhci_cqe_enable(mmc); /*
* CMD CRC errors are seen sometimes with some eMMC devices when status
if (cqcfg & CQHCI_ENABLE) * command is sent during transfer of last data block which is the
cqhci_writel(cq_host, cqcfg, CQHCI_CFG); * default case as send status command block counter (CBC) is 1.
* Recommended fix to set CBC to 0 allowing send status command only
* when data lines are idle.
*/
val = cqhci_readl(cq_host, CQHCI_SSC1);
val &= ~CQHCI_SSC1_CBC_MASK;
cqhci_writel(cq_host, val, CQHCI_SSC1);
} }
static void sdhci_tegra_dumpregs(struct mmc_host *mmc) static void sdhci_tegra_dumpregs(struct mmc_host *mmc)
@ -966,9 +1225,11 @@ static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
} }
static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = { static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = {
.write_l = tegra_cqhci_writel,
.enable = sdhci_tegra_cqe_enable, .enable = sdhci_tegra_cqe_enable,
.disable = sdhci_cqe_disable, .disable = sdhci_cqe_disable,
.dumpregs = sdhci_tegra_dumpregs, .dumpregs = sdhci_tegra_dumpregs,
.update_dcmd_desc = sdhci_tegra_update_dcmd_desc,
}; };
static const struct sdhci_ops tegra_sdhci_ops = { static const struct sdhci_ops tegra_sdhci_ops = {
@ -1109,6 +1370,8 @@ static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
NVQUIRK_ENABLE_SDR50 | NVQUIRK_ENABLE_SDR50 |
NVQUIRK_ENABLE_SDR104, NVQUIRK_ENABLE_SDR104,
.min_tap_delay = 106,
.max_tap_delay = 185,
}; };
static const struct sdhci_ops tegra186_sdhci_ops = { static const struct sdhci_ops tegra186_sdhci_ops = {
@ -1143,15 +1406,30 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
}; };
static const struct sdhci_tegra_soc_data soc_data_tegra186 = { static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
.pdata = &sdhci_tegra186_pdata,
.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
NVQUIRK_HAS_PADCALIB |
NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
NVQUIRK_ENABLE_SDR50 |
NVQUIRK_ENABLE_SDR104 |
NVQUIRK_CQHCI_DCMD_R1B_CMD_TIMING,
.min_tap_delay = 84,
.max_tap_delay = 136,
};
static const struct sdhci_tegra_soc_data soc_data_tegra194 = {
.pdata = &sdhci_tegra186_pdata, .pdata = &sdhci_tegra186_pdata,
.nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
NVQUIRK_HAS_PADCALIB | NVQUIRK_HAS_PADCALIB |
NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
NVQUIRK_ENABLE_SDR50 | NVQUIRK_ENABLE_SDR50 |
NVQUIRK_ENABLE_SDR104, NVQUIRK_ENABLE_SDR104,
.min_tap_delay = 96,
.max_tap_delay = 139,
}; };
static const struct of_device_id sdhci_tegra_dt_match[] = { static const struct of_device_id sdhci_tegra_dt_match[] = {
{ .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 },
{ .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 }, { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 },
@ -1250,6 +1528,10 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
host->mmc_host_ops.hs400_enhanced_strobe = host->mmc_host_ops.hs400_enhanced_strobe =
tegra_sdhci_hs400_enhanced_strobe; tegra_sdhci_hs400_enhanced_strobe;
if (!host->ops->platform_execute_tuning)
host->mmc_host_ops.execute_tuning =
tegra_sdhci_execute_hw_tuning;
rc = mmc_of_parse(host->mmc); rc = mmc_of_parse(host->mmc);
if (rc) if (rc)
goto err_parse_dt; goto err_parse_dt;
@ -1329,11 +1611,67 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
int ret;
if (host->mmc->caps2 & MMC_CAP2_CQE) {
ret = cqhci_suspend(host->mmc);
if (ret)
return ret;
}
ret = sdhci_suspend_host(host);
if (ret) {
cqhci_resume(host->mmc);
return ret;
}
clk_disable_unprepare(pltfm_host->clk);
return 0;
}
static int __maybe_unused sdhci_tegra_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
int ret;
ret = clk_prepare_enable(pltfm_host->clk);
if (ret)
return ret;
ret = sdhci_resume_host(host);
if (ret)
goto disable_clk;
if (host->mmc->caps2 & MMC_CAP2_CQE) {
ret = cqhci_resume(host->mmc);
if (ret)
goto suspend_host;
}
return 0;
suspend_host:
sdhci_suspend_host(host);
disable_clk:
clk_disable_unprepare(pltfm_host->clk);
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
sdhci_tegra_resume);
static struct platform_driver sdhci_tegra_driver = { static struct platform_driver sdhci_tegra_driver = {
.driver = { .driver = {
.name = "sdhci-tegra", .name = "sdhci-tegra",
.of_match_table = sdhci_tegra_dt_match, .of_match_table = sdhci_tegra_dt_match,
.pm = &sdhci_pltfm_pmops, .pm = &sdhci_tegra_dev_pm_ops,
}, },
.probe = sdhci_tegra_probe, .probe = sdhci_tegra_probe,
.remove = sdhci_tegra_remove, .remove = sdhci_tegra_remove,

View File

@ -446,6 +446,28 @@ static inline void sdhci_led_deactivate(struct sdhci_host *host)
#endif #endif
static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
unsigned long timeout)
{
if (sdhci_data_line_cmd(mrq->cmd))
mod_timer(&host->data_timer, timeout);
else
mod_timer(&host->timer, timeout);
}
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
if (sdhci_data_line_cmd(mrq->cmd))
del_timer(&host->data_timer);
else
del_timer(&host->timer);
}
static inline bool sdhci_has_requests(struct sdhci_host *host)
{
return host->cmd || host->data_cmd;
}
/*****************************************************************************\ /*****************************************************************************\
* * * *
* Core functions * * Core functions *
@ -1221,6 +1243,18 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{ {
int i; int i;
if (host->cmd && host->cmd->mrq == mrq)
host->cmd = NULL;
if (host->data_cmd && host->data_cmd->mrq == mrq)
host->data_cmd = NULL;
if (host->data && host->data->mrq == mrq)
host->data = NULL;
if (sdhci_needs_reset(host, mrq))
host->pending_reset = true;
for (i = 0; i < SDHCI_MAX_MRQS; i++) { for (i = 0; i < SDHCI_MAX_MRQS; i++) {
if (host->mrqs_done[i] == mrq) { if (host->mrqs_done[i] == mrq) {
WARN_ON(1); WARN_ON(1);
@ -1237,24 +1271,17 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
WARN_ON(i >= SDHCI_MAX_MRQS); WARN_ON(i >= SDHCI_MAX_MRQS);
tasklet_schedule(&host->finish_tasklet); sdhci_del_timer(host, mrq);
if (!sdhci_has_requests(host))
sdhci_led_deactivate(host);
} }
static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq) static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{ {
if (host->cmd && host->cmd->mrq == mrq)
host->cmd = NULL;
if (host->data_cmd && host->data_cmd->mrq == mrq)
host->data_cmd = NULL;
if (host->data && host->data->mrq == mrq)
host->data = NULL;
if (sdhci_needs_reset(host, mrq))
host->pending_reset = true;
__sdhci_finish_mrq(host, mrq); __sdhci_finish_mrq(host, mrq);
queue_work(host->complete_wq, &host->complete_work);
} }
static void sdhci_finish_data(struct sdhci_host *host) static void sdhci_finish_data(struct sdhci_host *host)
@ -1305,34 +1332,17 @@ static void sdhci_finish_data(struct sdhci_host *host)
* responsibility to send the stop command if required. * responsibility to send the stop command if required.
*/ */
if (data->mrq->cap_cmd_during_tfr) { if (data->mrq->cap_cmd_during_tfr) {
sdhci_finish_mrq(host, data->mrq); __sdhci_finish_mrq(host, data->mrq);
} else { } else {
/* Avoid triggering warning in sdhci_send_command() */ /* Avoid triggering warning in sdhci_send_command() */
host->cmd = NULL; host->cmd = NULL;
sdhci_send_command(host, data->stop); sdhci_send_command(host, data->stop);
} }
} else { } else {
sdhci_finish_mrq(host, data->mrq); __sdhci_finish_mrq(host, data->mrq);
} }
} }
static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
unsigned long timeout)
{
if (sdhci_data_line_cmd(mrq->cmd))
mod_timer(&host->data_timer, timeout);
else
mod_timer(&host->timer, timeout);
}
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
if (sdhci_data_line_cmd(mrq->cmd))
del_timer(&host->data_timer);
else
del_timer(&host->timer);
}
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{ {
int flags; int flags;
@ -1492,7 +1502,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
sdhci_finish_data(host); sdhci_finish_data(host);
if (!cmd->data) if (!cmd->data)
sdhci_finish_mrq(host, cmd->mrq); __sdhci_finish_mrq(host, cmd->mrq);
} }
} }
@ -2364,9 +2374,9 @@ static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
/* /*
* Issue opcode repeatedly till Execute Tuning is set to 0 or the number * Issue opcode repeatedly till Execute Tuning is set to 0 or the number
* of loops reaches 40 times. * of loops reaches tuning loop count.
*/ */
for (i = 0; i < MAX_TUNING_LOOP; i++) { for (i = 0; i < host->tuning_loop_count; i++) {
u16 ctrl; u16 ctrl;
sdhci_send_tuning(host, opcode); sdhci_send_tuning(host, opcode);
@ -2523,11 +2533,6 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq)
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED); sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
} }
static inline bool sdhci_has_requests(struct sdhci_host *host)
{
return host->cmd || host->data_cmd;
}
static void sdhci_error_out_mrqs(struct sdhci_host *host, int err) static void sdhci_error_out_mrqs(struct sdhci_host *host, int err)
{ {
if (host->data_cmd) { if (host->data_cmd) {
@ -2589,7 +2594,7 @@ static const struct mmc_host_ops sdhci_ops = {
/*****************************************************************************\ /*****************************************************************************\
* * * *
* Tasklets * * Request done *
* * * *
\*****************************************************************************/ \*****************************************************************************/
@ -2612,8 +2617,6 @@ static bool sdhci_request_done(struct sdhci_host *host)
return true; return true;
} }
sdhci_del_timer(host, mrq);
/* /*
* Always unmap the data buffers if they were mapped by * Always unmap the data buffers if they were mapped by
* sdhci_prepare_data() whenever we finish with a request. * sdhci_prepare_data() whenever we finish with a request.
@ -2695,9 +2698,6 @@ static bool sdhci_request_done(struct sdhci_host *host)
host->pending_reset = false; host->pending_reset = false;
} }
if (!sdhci_has_requests(host))
sdhci_led_deactivate(host);
host->mrqs_done[i] = NULL; host->mrqs_done[i] = NULL;
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
@ -2707,9 +2707,10 @@ static bool sdhci_request_done(struct sdhci_host *host)
return false; return false;
} }
static void sdhci_tasklet_finish(unsigned long param) static void sdhci_complete_work(struct work_struct *work)
{ {
struct sdhci_host *host = (struct sdhci_host *)param; struct sdhci_host *host = container_of(work, struct sdhci_host,
complete_work);
while (!sdhci_request_done(host)) while (!sdhci_request_done(host))
; ;
@ -2754,6 +2755,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
if (host->data) { if (host->data) {
host->data->error = -ETIMEDOUT; host->data->error = -ETIMEDOUT;
sdhci_finish_data(host); sdhci_finish_data(host);
queue_work(host->complete_wq, &host->complete_work);
} else if (host->data_cmd) { } else if (host->data_cmd) {
host->data_cmd->error = -ETIMEDOUT; host->data_cmd->error = -ETIMEDOUT;
sdhci_finish_mrq(host, host->data_cmd->mrq); sdhci_finish_mrq(host, host->data_cmd->mrq);
@ -2819,7 +2821,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
return; return;
} }
sdhci_finish_mrq(host, host->cmd->mrq); __sdhci_finish_mrq(host, host->cmd->mrq);
return; return;
} }
@ -2833,7 +2835,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { if (mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
mrq->sbc->error = err; mrq->sbc->error = err;
sdhci_finish_mrq(host, mrq); __sdhci_finish_mrq(host, mrq);
return; return;
} }
} }
@ -2897,7 +2899,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
if (intmask & SDHCI_INT_DATA_TIMEOUT) { if (intmask & SDHCI_INT_DATA_TIMEOUT) {
host->data_cmd = NULL; host->data_cmd = NULL;
data_cmd->error = -ETIMEDOUT; data_cmd->error = -ETIMEDOUT;
sdhci_finish_mrq(host, data_cmd->mrq); __sdhci_finish_mrq(host, data_cmd->mrq);
return; return;
} }
if (intmask & SDHCI_INT_DATA_END) { if (intmask & SDHCI_INT_DATA_END) {
@ -2910,7 +2912,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
if (host->cmd == data_cmd) if (host->cmd == data_cmd)
return; return;
sdhci_finish_mrq(host, data_cmd->mrq); __sdhci_finish_mrq(host, data_cmd->mrq);
return; return;
} }
} }
@ -2993,12 +2995,24 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
} }
} }
static inline bool sdhci_defer_done(struct sdhci_host *host,
struct mmc_request *mrq)
{
struct mmc_data *data = mrq->data;
return host->pending_reset ||
((host->flags & SDHCI_REQ_USE_DMA) && data &&
data->host_cookie == COOKIE_MAPPED);
}
static irqreturn_t sdhci_irq(int irq, void *dev_id) static irqreturn_t sdhci_irq(int irq, void *dev_id)
{ {
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS] = {0};
irqreturn_t result = IRQ_NONE; irqreturn_t result = IRQ_NONE;
struct sdhci_host *host = dev_id; struct sdhci_host *host = dev_id;
u32 intmask, mask, unexpected = 0; u32 intmask, mask, unexpected = 0;
int max_loops = 16; int max_loops = 16;
int i;
spin_lock(&host->lock); spin_lock(&host->lock);
@ -3092,9 +3106,30 @@ cont:
intmask = sdhci_readl(host, SDHCI_INT_STATUS); intmask = sdhci_readl(host, SDHCI_INT_STATUS);
} while (intmask && --max_loops); } while (intmask && --max_loops);
/* Determine if mrqs can be completed immediately */
for (i = 0; i < SDHCI_MAX_MRQS; i++) {
struct mmc_request *mrq = host->mrqs_done[i];
if (!mrq)
continue;
if (sdhci_defer_done(host, mrq)) {
result = IRQ_WAKE_THREAD;
} else {
mrqs_done[i] = mrq;
host->mrqs_done[i] = NULL;
}
}
out: out:
spin_unlock(&host->lock); spin_unlock(&host->lock);
/* Process mrqs ready for immediate completion */
for (i = 0; i < SDHCI_MAX_MRQS; i++) {
if (mrqs_done[i])
mmc_request_done(host->mmc, mrqs_done[i]);
}
if (unexpected) { if (unexpected) {
pr_err("%s: Unexpected interrupt 0x%08x.\n", pr_err("%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc), unexpected); mmc_hostname(host->mmc), unexpected);
@ -3110,6 +3145,9 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
unsigned long flags; unsigned long flags;
u32 isr; u32 isr;
while (!sdhci_request_done(host))
;
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
isr = host->thread_isr; isr = host->thread_isr;
host->thread_isr = 0; host->thread_isr = 0;
@ -3131,7 +3169,7 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
return isr ? IRQ_HANDLED : IRQ_NONE; return IRQ_HANDLED;
} }
/*****************************************************************************\ /*****************************************************************************\
@ -3483,6 +3521,7 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->cqe_err_ier = SDHCI_CQE_INT_ERR_MASK; host->cqe_err_ier = SDHCI_CQE_INT_ERR_MASK;
host->tuning_delay = -1; host->tuning_delay = -1;
host->tuning_loop_count = MAX_TUNING_LOOP;
host->sdma_boundary = SDHCI_DEFAULT_BOUNDARY_ARG; host->sdma_boundary = SDHCI_DEFAULT_BOUNDARY_ARG;
@ -4213,14 +4252,15 @@ EXPORT_SYMBOL_GPL(sdhci_cleanup_host);
int __sdhci_add_host(struct sdhci_host *host) int __sdhci_add_host(struct sdhci_host *host)
{ {
unsigned int flags = WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI;
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = host->mmc;
int ret; int ret;
/* host->complete_wq = alloc_workqueue("sdhci", flags, 0);
* Init tasklets. if (!host->complete_wq)
*/ return -ENOMEM;
tasklet_init(&host->finish_tasklet,
sdhci_tasklet_finish, (unsigned long)host); INIT_WORK(&host->complete_work, sdhci_complete_work);
timer_setup(&host->timer, sdhci_timeout_timer, 0); timer_setup(&host->timer, sdhci_timeout_timer, 0);
timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0); timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
@ -4234,7 +4274,7 @@ int __sdhci_add_host(struct sdhci_host *host)
if (ret) { if (ret) {
pr_err("%s: Failed to request IRQ %d: %d\n", pr_err("%s: Failed to request IRQ %d: %d\n",
mmc_hostname(mmc), host->irq, ret); mmc_hostname(mmc), host->irq, ret);
goto untasklet; goto unwq;
} }
ret = sdhci_led_register(host); ret = sdhci_led_register(host);
@ -4265,8 +4305,8 @@ unirq:
sdhci_writel(host, 0, SDHCI_INT_ENABLE); sdhci_writel(host, 0, SDHCI_INT_ENABLE);
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
free_irq(host->irq, host); free_irq(host->irq, host);
untasklet: unwq:
tasklet_kill(&host->finish_tasklet); destroy_workqueue(host->complete_wq);
return ret; return ret;
} }
@ -4328,7 +4368,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
del_timer_sync(&host->timer); del_timer_sync(&host->timer);
del_timer_sync(&host->data_timer); del_timer_sync(&host->data_timer);
tasklet_kill(&host->finish_tasklet); destroy_workqueue(host->complete_wq);
if (!IS_ERR(mmc->supply.vqmmc)) if (!IS_ERR(mmc->supply.vqmmc))
regulator_disable(mmc->supply.vqmmc); regulator_disable(mmc->supply.vqmmc);

View File

@ -560,7 +560,8 @@ struct sdhci_host {
unsigned int desc_sz; /* ADMA descriptor size */ unsigned int desc_sz; /* ADMA descriptor size */
struct tasklet_struct finish_tasklet; /* Tasklet structures */ struct workqueue_struct *complete_wq; /* Request completion wq */
struct work_struct complete_work; /* Request completion work */
struct timer_list timer; /* Timer for timeouts */ struct timer_list timer; /* Timer for timeouts */
struct timer_list data_timer; /* Timer for data timeouts */ struct timer_list data_timer; /* Timer for data timeouts */
@ -596,6 +597,7 @@ struct sdhci_host {
#define SDHCI_TUNING_MODE_3 2 #define SDHCI_TUNING_MODE_3 2
/* Delay (ms) between tuning commands */ /* Delay (ms) between tuning commands */
int tuning_delay; int tuning_delay;
int tuning_loop_count;
/* Host SDMA buffer boundary. */ /* Host SDMA buffer boundary. */
u32 sdma_boundary; u32 sdma_boundary;

View File

@ -158,6 +158,27 @@ static void sdhci_am654_set_power(struct sdhci_host *host, unsigned char mode,
sdhci_set_power_noreg(host, mode, vdd); sdhci_set_power_noreg(host, mode, vdd);
} }
static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg)
{
unsigned char timing = host->mmc->ios.timing;
if (reg == SDHCI_HOST_CONTROL) {
switch (timing) {
/*
* According to the data manual, HISPD bit
* should not be set in these speed modes.
*/
case MMC_TIMING_SD_HS:
case MMC_TIMING_MMC_HS:
case MMC_TIMING_UHS_SDR12:
case MMC_TIMING_UHS_SDR25:
val &= ~SDHCI_CTRL_HISPD;
}
}
writeb(val, host->ioaddr + reg);
}
static struct sdhci_ops sdhci_am654_ops = { static struct sdhci_ops sdhci_am654_ops = {
.get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock, .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
@ -165,6 +186,7 @@ static struct sdhci_ops sdhci_am654_ops = {
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_set_bus_width,
.set_power = sdhci_am654_set_power, .set_power = sdhci_am654_set_power,
.set_clock = sdhci_am654_set_clock, .set_clock = sdhci_am654_set_clock,
.write_b = sdhci_am654_write_b,
.reset = sdhci_reset, .reset = sdhci_reset,
}; };

View File

@ -4,8 +4,8 @@
* *
* TC6393XB TC6391XB TC6387XB T7L66XB ASIC3 * TC6393XB TC6391XB TC6387XB T7L66XB ASIC3
* *
* Copyright (C) 2015-17 Renesas Electronics Corporation * Copyright (C) 2015-19 Renesas Electronics Corporation
* Copyright (C) 2016-17 Sang Engineering, Wolfram Sang * Copyright (C) 2016-19 Sang Engineering, Wolfram Sang
* Copyright (C) 2016-17 Horms Solutions, Simon Horman * Copyright (C) 2016-17 Horms Solutions, Simon Horman
* Copyright (C) 2007 Ian Molton * Copyright (C) 2007 Ian Molton
* Copyright (C) 2004 Ian Molton * Copyright (C) 2004 Ian Molton
@ -105,6 +105,8 @@
TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) #define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
#define TMIO_MAX_BLK_SIZE 512
struct tmio_mmc_data; struct tmio_mmc_data;
struct tmio_mmc_host; struct tmio_mmc_host;

View File

@ -4,8 +4,8 @@
* *
* TC6393XB, TC6391XB, TC6387XB, T7L66XB, ASIC3, SH-Mobile SoCs * TC6393XB, TC6391XB, TC6387XB, T7L66XB, ASIC3, SH-Mobile SoCs
* *
* Copyright (C) 2015-17 Renesas Electronics Corporation * Copyright (C) 2015-19 Renesas Electronics Corporation
* Copyright (C) 2016-17 Sang Engineering, Wolfram Sang * Copyright (C) 2016-19 Sang Engineering, Wolfram Sang
* Copyright (C) 2017 Horms Solutions, Simon Horman * Copyright (C) 2017 Horms Solutions, Simon Horman
* Copyright (C) 2011 Guennadi Liakhovetski * Copyright (C) 2011 Guennadi Liakhovetski
* Copyright (C) 2007 Ian Molton * Copyright (C) 2007 Ian Molton
@ -1186,7 +1186,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities; mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
mmc->caps2 |= pdata->capabilities2; mmc->caps2 |= pdata->capabilities2;
mmc->max_segs = pdata->max_segs ? : 32; mmc->max_segs = pdata->max_segs ? : 32;
mmc->max_blk_size = 512; mmc->max_blk_size = TMIO_MAX_BLK_SIZE;
mmc->max_blk_count = pdata->max_blk_count ? : mmc->max_blk_count = pdata->max_blk_count ? :
(PAGE_SIZE / mmc->max_blk_size) * mmc->max_segs; (PAGE_SIZE / mmc->max_blk_size) * mmc->max_segs;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;

View File

@ -1339,7 +1339,7 @@ static int usdhi6_stop_cmd(struct usdhi6_host *host)
host->wait = USDHI6_WAIT_FOR_STOP; host->wait = USDHI6_WAIT_FOR_STOP;
return 0; return 0;
} }
/* Unsupported STOP command */ /* fall through - Unsupported STOP command. */
default: default:
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"unsupported stop CMD%d for CMD%d\n", "unsupported stop CMD%d for CMD%d\n",
@ -1687,7 +1687,7 @@ static void usdhi6_timeout_work(struct work_struct *work)
switch (host->wait) { switch (host->wait) {
default: default:
dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait); dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait);
/* mrq can be NULL in this actually impossible case */ /* fall through - mrq can be NULL, but is impossible. */
case USDHI6_WAIT_FOR_CMD: case USDHI6_WAIT_FOR_CMD:
usdhi6_error_code(host); usdhi6_error_code(host);
if (mrq) if (mrq)
@ -1709,10 +1709,7 @@ static void usdhi6_timeout_work(struct work_struct *work)
host->offset, data->blocks, data->blksz, data->sg_len, host->offset, data->blocks, data->blksz, data->sg_len,
sg_dma_len(sg), sg->offset); sg_dma_len(sg), sg->offset);
usdhi6_sg_unmap(host, true); usdhi6_sg_unmap(host, true);
/* /* fall through - page unmapped in USDHI6_WAIT_FOR_DATA_END. */
* If USDHI6_WAIT_FOR_DATA_END times out, we have already unmapped
* the page
*/
case USDHI6_WAIT_FOR_DATA_END: case USDHI6_WAIT_FOR_DATA_END:
usdhi6_error_code(host); usdhi6_error_code(host);
data->error = -ETIMEDOUT; data->error = -ETIMEDOUT;

View File

@ -23,7 +23,7 @@
#define AU6601_BASE_CLOCK 31000000 #define AU6601_BASE_CLOCK 31000000
#define AU6601_MIN_CLOCK 150000 #define AU6601_MIN_CLOCK 150000
#define AU6601_MAX_CLOCK 208000000 #define AU6601_MAX_CLOCK 208000000
#define AU6601_MAX_DMA_SEGMENTS 1 #define AU6601_MAX_DMA_SEGMENTS 64
#define AU6601_MAX_PIO_SEGMENTS 1 #define AU6601_MAX_PIO_SEGMENTS 1
#define AU6601_MAX_DMA_BLOCK_SIZE 0x1000 #define AU6601_MAX_DMA_BLOCK_SIZE 0x1000
#define AU6601_MAX_PIO_BLOCK_SIZE 0x200 #define AU6601_MAX_PIO_BLOCK_SIZE 0x200