From 4c5f4bf412eabc355b9c2bc99f8283ca0bd7d6ef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:44 +0200 Subject: [PATCH 01/68] mmc: sunxi: Disable sample clks on remove When support for the sample clks was added calls to prepare_enable were added to the probe path, but matching calls to disable_unprepare were forgotten in the remove path, this fixes this. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 2ee4c21ec55ee..d7dadb5522bae 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1160,6 +1160,8 @@ static int sunxi_mmc_remove(struct platform_device *pdev) if (!IS_ERR(host->reset)) reset_control_assert(host->reset); + clk_disable_unprepare(host->clk_sample); + clk_disable_unprepare(host->clk_output); clk_disable_unprepare(host->clk_mmc); clk_disable_unprepare(host->clk_ahb); From 86a93317ed71f9ad8bca7baff12930c47c235f80 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:45 +0200 Subject: [PATCH 02/68] mmc: sunxi: Introduce a sunxi_mmc_cfg struct Create a struct to hold the various model / compatible string dependend settings. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 77 +++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index d7dadb5522bae..84bbf43390d40 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -229,9 +229,15 @@ struct sunxi_idma_des { u32 buf_addr_ptr2; }; +struct sunxi_mmc_cfg { + u32 idma_des_size_bits; + const struct sunxi_mmc_clk_delay *clk_delays; +}; + struct sunxi_mmc_host { struct mmc_host *mmc; struct reset_control *reset; + const struct sunxi_mmc_cfg *cfg; /* IO mapping base */ void __iomem *reg_base; @@ -241,7 +247,6 @@ struct sunxi_mmc_host { struct clk *clk_mmc; struct clk *clk_sample; struct clk *clk_output; - const struct sunxi_mmc_clk_delay *clk_delays; /* irq */ spinlock_t lock; @@ -250,7 +255,6 @@ struct sunxi_mmc_host { u32 sdio_imask; /* dma */ - u32 idma_des_size_bits; dma_addr_t sg_dma; void *sg_cpu; bool wait_dma; @@ -322,7 +326,7 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, { struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu; dma_addr_t next_desc = host->sg_dma; - int i, max_len = (1 << host->idma_des_size_bits); + int i, max_len = (1 << host->cfg->idma_des_size_bits); for (i = 0; i < data->sg_len; i++) { pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN | @@ -656,6 +660,7 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, struct mmc_ios *ios) { + const struct sunxi_mmc_clk_delay *clk_delays = host->cfg->clk_delays; u32 rate, oclk_dly, rval, sclk_dly; u32 clock = ios->clock; int ret; @@ -694,22 +699,22 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, /* determine delays */ if (rate <= 400000) { - oclk_dly = host->clk_delays[SDXC_CLK_400K].output; - sclk_dly = host->clk_delays[SDXC_CLK_400K].sample; + oclk_dly = clk_delays[SDXC_CLK_400K].output; + sclk_dly = clk_delays[SDXC_CLK_400K].sample; } else if (rate <= 25000000) { - oclk_dly = host->clk_delays[SDXC_CLK_25M].output; - sclk_dly = host->clk_delays[SDXC_CLK_25M].sample; + oclk_dly = clk_delays[SDXC_CLK_25M].output; + sclk_dly = clk_delays[SDXC_CLK_25M].sample; } else if (rate <= 52000000) { if (ios->timing != MMC_TIMING_UHS_DDR50 && ios->timing != MMC_TIMING_MMC_DDR52) { - oclk_dly = host->clk_delays[SDXC_CLK_50M].output; - sclk_dly = host->clk_delays[SDXC_CLK_50M].sample; + oclk_dly = clk_delays[SDXC_CLK_50M].output; + sclk_dly = clk_delays[SDXC_CLK_50M].sample; } else if (ios->bus_width == MMC_BUS_WIDTH_8) { - oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].output; - sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR_8BIT].sample; + oclk_dly = clk_delays[SDXC_CLK_50M_DDR_8BIT].output; + sclk_dly = clk_delays[SDXC_CLK_50M_DDR_8BIT].sample; } else { - oclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].output; - sclk_dly = host->clk_delays[SDXC_CLK_50M_DDR].sample; + oclk_dly = clk_delays[SDXC_CLK_50M_DDR].output; + sclk_dly = clk_delays[SDXC_CLK_50M_DDR].sample; } } else { return -EINVAL; @@ -938,14 +943,6 @@ static int sunxi_mmc_card_busy(struct mmc_host *mmc) return !!(mmc_readl(host, REG_STAS) & SDXC_CARD_DATA_BUSY); } -static const struct of_device_id sunxi_mmc_of_match[] = { - { .compatible = "allwinner,sun4i-a10-mmc", }, - { .compatible = "allwinner,sun5i-a13-mmc", }, - { .compatible = "allwinner,sun9i-a80-mmc", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); - static struct mmc_host_ops sunxi_mmc_ops = { .request = sunxi_mmc_request, .set_ios = sunxi_mmc_set_ios, @@ -974,21 +971,37 @@ static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = { [SDXC_CLK_50M_DDR_8BIT] = { .output = 72, .sample = 72 }, }; +static const struct sunxi_mmc_cfg sun4i_a10_cfg = { + .idma_des_size_bits = 13, + .clk_delays = sunxi_mmc_clk_delays, +}; + +static const struct sunxi_mmc_cfg sun5i_a13_cfg = { + .idma_des_size_bits = 16, + .clk_delays = sunxi_mmc_clk_delays, +}; + +static const struct sunxi_mmc_cfg sun9i_a80_cfg = { + .idma_des_size_bits = 16, + .clk_delays = sun9i_mmc_clk_delays, +}; + +static const struct of_device_id sunxi_mmc_of_match[] = { + { .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg }, + { .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg }, + { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); + static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; int ret; - if (of_device_is_compatible(np, "allwinner,sun4i-a10-mmc")) - host->idma_des_size_bits = 13; - else - host->idma_des_size_bits = 16; - - if (of_device_is_compatible(np, "allwinner,sun9i-a80-mmc")) - host->clk_delays = sun9i_mmc_clk_delays; - else - host->clk_delays = sunxi_mmc_clk_delays; + host->cfg = of_device_get_match_data(&pdev->dev); + if (!host->cfg) + return -EINVAL; ret = mmc_regulator_get_supply(host->mmc); if (ret) { @@ -1120,7 +1133,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) mmc->max_blk_count = 8192; mmc->max_blk_size = 4096; mmc->max_segs = PAGE_SIZE / sizeof(struct sunxi_idma_des); - mmc->max_seg_size = (1 << host->idma_des_size_bits); + mmc->max_seg_size = (1 << host->cfg->idma_des_size_bits); mmc->max_req_size = mmc->max_seg_size * mmc->max_segs; /* 400kHz ~ 52MHz */ mmc->f_min = 400000; From f2cecb70941c8d4a9445ee85926202f7157e1222 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:46 +0200 Subject: [PATCH 03/68] mmc: sunxi: Factor out clock phase setting code into a helper function Add a sunxi_mmc_clk_set_phase() helper function. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 61 +++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 84bbf43390d40..af30d87a56db2 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -657,12 +657,39 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) return 0; } +static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, + struct mmc_ios *ios, u32 rate) +{ + int index; + + /* determine delays */ + if (rate <= 400000) { + index = SDXC_CLK_400K; + } else if (rate <= 25000000) { + index = SDXC_CLK_25M; + } else if (rate <= 52000000) { + if (ios->timing != MMC_TIMING_UHS_DDR50 && + ios->timing != MMC_TIMING_MMC_DDR52) { + index = SDXC_CLK_50M; + } else if (ios->bus_width == MMC_BUS_WIDTH_8) { + index = SDXC_CLK_50M_DDR_8BIT; + } else { + index = SDXC_CLK_50M_DDR; + } + } else { + return -EINVAL; + } + + clk_set_phase(host->clk_sample, host->cfg->clk_delays[index].sample); + clk_set_phase(host->clk_output, host->cfg->clk_delays[index].output); + + return 0; +} + static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, struct mmc_ios *ios) { - const struct sunxi_mmc_clk_delay *clk_delays = host->cfg->clk_delays; - u32 rate, oclk_dly, rval, sclk_dly; - u32 clock = ios->clock; + u32 rate, rval, clock = ios->clock; int ret; /* 8 bit DDR requires a higher module clock */ @@ -697,31 +724,9 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, } mmc_writel(host, REG_CLKCR, rval); - /* determine delays */ - if (rate <= 400000) { - oclk_dly = clk_delays[SDXC_CLK_400K].output; - sclk_dly = clk_delays[SDXC_CLK_400K].sample; - } else if (rate <= 25000000) { - oclk_dly = clk_delays[SDXC_CLK_25M].output; - sclk_dly = clk_delays[SDXC_CLK_25M].sample; - } else if (rate <= 52000000) { - if (ios->timing != MMC_TIMING_UHS_DDR50 && - ios->timing != MMC_TIMING_MMC_DDR52) { - oclk_dly = clk_delays[SDXC_CLK_50M].output; - sclk_dly = clk_delays[SDXC_CLK_50M].sample; - } else if (ios->bus_width == MMC_BUS_WIDTH_8) { - oclk_dly = clk_delays[SDXC_CLK_50M_DDR_8BIT].output; - sclk_dly = clk_delays[SDXC_CLK_50M_DDR_8BIT].sample; - } else { - oclk_dly = clk_delays[SDXC_CLK_50M_DDR].output; - sclk_dly = clk_delays[SDXC_CLK_50M_DDR].sample; - } - } else { - return -EINVAL; - } - - clk_set_phase(host->clk_sample, sclk_dly); - clk_set_phase(host->clk_output, oclk_dly); + ret = sunxi_mmc_clk_set_phase(host, ios, rate); + if (ret) + return ret; return sunxi_mmc_oclk_onoff(host, 1); } From b465646ef41f2f8a397f42a956d9788e898185d7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:47 +0200 Subject: [PATCH 04/68] mmc: sunxi: sun4i / sun5i do not have sample clocks It turns out that sun4i (A10) and sun5i (A13 & co) do not have sample clocks, so add a new sun7i-a20-mmc compatible and do not try to use sample clocks on sun4i / sun5i. Since sun4i / sun5i do not have sample clocks, they cannot (reliably) do DDR rates, so only set MMC_CAP_1_8V_DDR when we do have sample clks. Note this patch leaves the clk_prepare_enable() / clk_disable_unprepare() calls to the sample clks as-is, without adding checks for them being NULL. All the clk_foo calls accept a NULL clk and will return success when called with a NULL clk. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard Acked-by: Rob Herring Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/sunxi-mmc.txt | 6 +++- drivers/mmc/host/sunxi-mmc.c | 35 +++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt index 4bf41d8338046..904ff9f898936 100644 --- a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt +++ b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt @@ -8,7 +8,11 @@ as the speed of SD standard 3.0. Absolute maximum transfer rate is 200MB/s Required properties: - - compatible : "allwinner,sun4i-a10-mmc" or "allwinner,sun5i-a13-mmc" + - compatible : should be one of: + * "allwinner,sun4i-a10-mmc" + * "allwinner,sun5i-a13-mmc" + * "allwinner,sun7i-a20-mmc" + * "allwinner,sun9i-a80-mmc" - reg : mmc controller base registers - clocks : a list with 4 phandle + clock specifier pairs - clock-names : must contain "ahb", "mmc", "output" and "sample" diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index af30d87a56db2..2ec91ce1fb0a5 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -662,6 +662,9 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, { int index; + if (!host->cfg->clk_delays) + return 0; + /* determine delays */ if (rate <= 400000) { index = SDXC_CLK_400K; @@ -978,10 +981,15 @@ static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = { static const struct sunxi_mmc_cfg sun4i_a10_cfg = { .idma_des_size_bits = 13, - .clk_delays = sunxi_mmc_clk_delays, + .clk_delays = NULL, }; static const struct sunxi_mmc_cfg sun5i_a13_cfg = { + .idma_des_size_bits = 16, + .clk_delays = NULL, +}; + +static const struct sunxi_mmc_cfg sun7i_a20_cfg = { .idma_des_size_bits = 16, .clk_delays = sunxi_mmc_clk_delays, }; @@ -994,6 +1002,7 @@ static const struct sunxi_mmc_cfg sun9i_a80_cfg = { static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg }, { .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg }, + { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, { /* sentinel */ } }; @@ -1032,16 +1041,18 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, return PTR_ERR(host->clk_mmc); } - host->clk_output = devm_clk_get(&pdev->dev, "output"); - if (IS_ERR(host->clk_output)) { - dev_err(&pdev->dev, "Could not get output clock\n"); - return PTR_ERR(host->clk_output); - } + if (host->cfg->clk_delays) { + host->clk_output = devm_clk_get(&pdev->dev, "output"); + if (IS_ERR(host->clk_output)) { + dev_err(&pdev->dev, "Could not get output clock\n"); + return PTR_ERR(host->clk_output); + } - host->clk_sample = devm_clk_get(&pdev->dev, "sample"); - if (IS_ERR(host->clk_sample)) { - dev_err(&pdev->dev, "Could not get sample clock\n"); - return PTR_ERR(host->clk_sample); + host->clk_sample = devm_clk_get(&pdev->dev, "sample"); + if (IS_ERR(host->clk_sample)) { + dev_err(&pdev->dev, "Could not get sample clock\n"); + return PTR_ERR(host->clk_sample); + } } host->reset = devm_reset_control_get_optional(&pdev->dev, "ahb"); @@ -1144,9 +1155,11 @@ static int sunxi_mmc_probe(struct platform_device *pdev) mmc->f_min = 400000; mmc->f_max = 52000000; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_1_8V_DDR | MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ; + if (host->cfg->clk_delays) + mmc->caps |= MMC_CAP_1_8V_DDR; + ret = mmc_of_parse(mmc); if (ret) goto error_free_dma; From 57af711d79bc196896e1e54c78330f7c92edc21c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:48 +0200 Subject: [PATCH 05/68] ARM: dts: sunxi: Use new sun7i-a20-mmc compatible on sun7i and newer Use the new sun7i-a20-mmc compatible for the mmc controllers on sun7i and newer. Signed-off-by: Hans de Goede Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- arch/arm/boot/dts/sun6i-a31.dtsi | 8 ++++---- arch/arm/boot/dts/sun7i-a20.dtsi | 8 ++++---- arch/arm/boot/dts/sun8i-a23-a33.dtsi | 6 +++--- arch/arm/boot/dts/sun8i-h3.dtsi | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 1867af24ff529..0d24f107ede0b 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -469,7 +469,7 @@ }; mmc0: mmc@01c0f000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c0f000 0x1000>; clocks = <&ahb1_gates 8>, <&mmc0_clk 0>, @@ -488,7 +488,7 @@ }; mmc1: mmc@01c10000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c10000 0x1000>; clocks = <&ahb1_gates 9>, <&mmc1_clk 0>, @@ -507,7 +507,7 @@ }; mmc2: mmc@01c11000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c11000 0x1000>; clocks = <&ahb1_gates 10>, <&mmc2_clk 0>, @@ -526,7 +526,7 @@ }; mmc3: mmc@01c12000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c12000 0x1000>; clocks = <&ahb1_gates 11>, <&mmc3_clk 0>, diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index bd0c476602438..94cf5a1c71723 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -905,7 +905,7 @@ }; mmc0: mmc@01c0f000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c0f000 0x1000>; clocks = <&ahb_gates 8>, <&mmc0_clk 0>, @@ -922,7 +922,7 @@ }; mmc1: mmc@01c10000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c10000 0x1000>; clocks = <&ahb_gates 9>, <&mmc1_clk 0>, @@ -939,7 +939,7 @@ }; mmc2: mmc@01c11000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c11000 0x1000>; clocks = <&ahb_gates 10>, <&mmc2_clk 0>, @@ -956,7 +956,7 @@ }; mmc3: mmc@01c12000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c12000 0x1000>; clocks = <&ahb_gates 11>, <&mmc3_clk 0>, diff --git a/arch/arm/boot/dts/sun8i-a23-a33.dtsi b/arch/arm/boot/dts/sun8i-a23-a33.dtsi index 7e05e09e61c7e..e3b196e08ccf5 100644 --- a/arch/arm/boot/dts/sun8i-a23-a33.dtsi +++ b/arch/arm/boot/dts/sun8i-a23-a33.dtsi @@ -266,7 +266,7 @@ }; mmc0: mmc@01c0f000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c0f000 0x1000>; clocks = <&ahb1_gates 8>, <&mmc0_clk 0>, @@ -285,7 +285,7 @@ }; mmc1: mmc@01c10000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c10000 0x1000>; clocks = <&ahb1_gates 9>, <&mmc1_clk 0>, @@ -304,7 +304,7 @@ }; mmc2: mmc@01c11000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c11000 0x1000>; clocks = <&ahb1_gates 10>, <&mmc2_clk 0>, diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi index fdf9fdbda2678..8a95e3613488d 100644 --- a/arch/arm/boot/dts/sun8i-h3.dtsi +++ b/arch/arm/boot/dts/sun8i-h3.dtsi @@ -150,7 +150,7 @@ }; mmc0: mmc@01c0f000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c0f000 0x1000>; clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>, @@ -169,7 +169,7 @@ }; mmc1: mmc@01c10000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c10000 0x1000>; clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>, @@ -188,7 +188,7 @@ }; mmc2: mmc@01c11000 { - compatible = "allwinner,sun5i-a13-mmc"; + compatible = "allwinner,sun7i-a20-mmc"; reg = <0x01c11000 0x1000>; clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>, From 4cbc6dbd52516363c61ad8f116355ad63fe9cdf2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 17:03:50 +0200 Subject: [PATCH 06/68] dt: bindings: Make compatible optional for mmc function nodes On some boards (android tablets) different batches use different sdio wifi modules. This is not a problem since mmc/sdio is an enumerable bus, so we only need to describe and activate the mmc controller in dt and then the kernel will automatically load the right driver. Sometimes it is useful to specify certain ethernet properties for these "unknown" sdio devices, specifically we want the boot-loader to be able to set "local-mac-address" as some of these sdio wifi modules come without an eeprom / without a factory programmed mac address. Since the exact device is unknown (differs per batch) we cannot use a wifi-chip specific compatible, thus sometimes it is desirable to have a mmc function node, without having to make up an otherwise unused compatible for the node, so make the compatible property optional. Cc: Arnd Bergmann Cc: Maxime Ripard Signed-off-by: Hans de Goede Acked-by: Rob Herring Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 22d1e1f3f38bb..ac7b6a1d835a2 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -102,11 +102,13 @@ Required host node properties when using function subnodes: - #size-cells: should be zero. Required function subnode properties: -- compatible: name of SDIO function following generic names recommended practice - reg: Must contain the SDIO function number of the function this subnode describes. A value of 0 denotes the memory SD function, values from 1 to 7 denote the SDIO functions. +Optional function subnode properties: +- compatible: name of SDIO function following generic names recommended practice + Examples -------- From 721e0497172f0fa661eed2d63367cddf479f35e8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Aug 2016 21:02:38 +0200 Subject: [PATCH 07/68] mmc: pwrseq-simple: Add an optional post-power-on-delay Some devices need a while to boot their firmware after providing clks / de-asserting resets before they are ready to receive sdio commands. This commits adds a post-power-on-delay-ms devicetree property to mmc-pwrseq-simple for use with such devices. Signed-off-by: Hans de Goede Acked-by: Rob Herring Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 2 ++ drivers/mmc/core/pwrseq_simple.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt index ce0e767496719..e25436861867f 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt @@ -16,6 +16,8 @@ Optional properties: See ../clocks/clock-bindings.txt for details. - clock-names : Must include the following entry: "ext_clock" (External clock provided to the card). +- post-power-on-delay-ms : Delay in ms after powering the card and + de-asserting the reset-gpios (if any) Example: diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 450d907c6e6c6..1304160de1682 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include @@ -24,6 +26,7 @@ struct mmc_pwrseq_simple { struct mmc_pwrseq pwrseq; bool clk_enabled; + u32 post_power_on_delay_ms; struct clk *ext_clk; struct gpio_descs *reset_gpios; }; @@ -64,6 +67,9 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); + + if (pwrseq->post_power_on_delay_ms) + msleep(pwrseq->post_power_on_delay_ms); } static void mmc_pwrseq_simple_power_off(struct mmc_host *host) @@ -111,6 +117,9 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) return PTR_ERR(pwrseq->reset_gpios); } + device_property_read_u32(dev, "post-power-on-delay-ms", + &pwrseq->post_power_on_delay_ms); + pwrseq->pwrseq.dev = dev; pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; pwrseq->pwrseq.owner = THIS_MODULE; From 41f469cac2663a41a7b0c84cb94e8f7024385ae4 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 25 Jul 2016 19:59:23 +0200 Subject: [PATCH 08/68] mmc: moxart: fix wait_for_completion_interruptible_timeout return variable type wait_for_completion_timeout_interruptible returns long not unsigned long so dma_time, which is used exclusively here, is changed to long. Fixes: 1b66e94e6b99 ("mmc: moxart: Add MOXA ART SD/MMC driver") Signed-off-by: Nicholas Mc Guire Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 79905ce895ada..bbad309679cf8 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -257,7 +257,7 @@ static void moxart_dma_complete(void *param) static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host) { u32 len, dir_data, dir_slave; - unsigned long dma_time; + long dma_time; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *dma_chan; @@ -397,7 +397,8 @@ static void moxart_prepare_data(struct moxart_host *host) static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct moxart_host *host = mmc_priv(mmc); - unsigned long pio_time, flags; + long pio_time; + unsigned long flags; u32 status; spin_lock_irqsave(&host->lock, flags); From 66fe6ac5d5e120b85c2ed310eb25b15a77c110c0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 28 Jul 2016 16:17:44 +0000 Subject: [PATCH 09/68] mmc: sdhci-bcm-kona: fix error return code in sdhci_bcm_kona_probe() In clk_set_rate() or clk_prepare_enable() error handling case, the error return code ret is not set, so sdhci_bcm_kona_probe() return 0 in those error cases. Signed-off-by: Wei Yongjun Acked-by: Ray Jui Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-bcm-kona.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index e5c634bdfdd90..51dd2fd65000c 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -253,12 +253,14 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) goto err_pltfm_free; } - if (clk_set_rate(pltfm_priv->clk, host->mmc->f_max) != 0) { + ret = clk_set_rate(pltfm_priv->clk, host->mmc->f_max); + if (ret) { dev_err(dev, "Failed to set rate core clock\n"); goto err_pltfm_free; } - if (clk_prepare_enable(pltfm_priv->clk) != 0) { + ret = clk_prepare_enable(pltfm_priv->clk); + if (ret) { dev_err(dev, "Failed to enable core clock\n"); goto err_pltfm_free; } From 2f7649f5048cfca9c236a8120c51ed762569a5e8 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 1 Aug 2016 10:22:11 +0100 Subject: [PATCH 10/68] mmc: core: Add the vmmc/vmmcq regulator info The core MMC code adds two (optional) regulator properites that drivers should use to get their supplies. This is not documented anywhere so add information on it. Signed-off-by: Ben Dooks Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index ac7b6a1d835a2..8a377827695bd 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -75,6 +75,17 @@ Optional SDIO properties: - wakeup-source: Enables wake up of host system on SDIO IRQ assertion (Legacy property supported: "enable-sdio-wakeup") +MMC power +--------- + +Controllers may implement power control from both the connected cards and +the IO signaling (for example to change to high-speed 1.8V signalling). If +the system supports this, then the following two properties should point +to valid regulator nodes: + +- vqmmc-supply: supply node for IO line power +- vmmc-supply: supply node for card's power + MMC power sequences: -------------------- From 150d4240254be03aafb2a86c5b1a4b41665bf1b1 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 5 Aug 2016 10:56:46 +0200 Subject: [PATCH 11/68] mmc: sdhci-of-esdhc: use of_property_read_bool Use of_property_read_bool to check for the existence of a property. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression e1,e2; statement S2,S1; @@ - if (of_get_property(e1,e2,NULL)) + if (of_property_read_bool(e1,e2)) S1 else S2 // Signed-off-by: Julia Lawall Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 239be2fde242f..fb71c866eacc7 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -583,7 +583,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) np = pdev->dev.of_node; - if (of_get_property(np, "little-endian", NULL)) + if (of_property_read_bool(np, "little-endian")) host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, sizeof(struct sdhci_esdhc)); else From d51c50525f0a4c6b58fdd493a933017c62d98731 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 11 Aug 2016 23:03:53 +0200 Subject: [PATCH 12/68] mmc: vub300: don't print error when allocating urb fails kmalloc will print enough information in case of failure. Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 1e819f98b94f5..bb3e0d1dd3550 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2116,13 +2116,11 @@ static int vub300_probe(struct usb_interface *interface, command_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_out_urb) { retval = -ENOMEM; - dev_err(&udev->dev, "not enough memory for command_out_urb\n"); goto error0; } command_res_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_res_urb) { retval = -ENOMEM; - dev_err(&udev->dev, "not enough memory for command_res_urb\n"); goto error1; } /* this also allocates memory for our VUB300 mmc host device */ From 5275a652d296711aaf7f2f4173c8db153e5777c3 Mon Sep 17 00:00:00 2001 From: Uri Yanai Date: Sun, 14 Aug 2016 11:46:36 +0300 Subject: [PATCH 13/68] =?UTF-8?q?mmc:=20sd:=20Export=20SD=20Status=20via?= =?UTF-8?q?=20=E2=80=9Cssr=E2=80=9D=20device=20attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SD Status register contains several important fields related to the SD Card proprietary features. Those fields may be used by user space applications for vendor specific usage. None of those fields are exported today by the driver to user space. In this patch, we are reading the SD Status register and exporting (using MMC_DEV_ATTR) the SD Status register to the user space. Signed-off-by: Uri Yanai Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 37 +++++++++++++++++++------------------ include/linux/mmc/card.h | 1 + 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 0123936241b06..73c762a28dfed 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -223,8 +223,7 @@ static int mmc_decode_scr(struct mmc_card *card) static int mmc_read_ssr(struct mmc_card *card) { unsigned int au, es, et, eo; - int err, i; - u32 *ssr; + int i; if (!(card->csd.cmdclass & CCC_APP_SPEC)) { pr_warn("%s: card lacks mandatory SD Status function\n", @@ -232,33 +231,27 @@ static int mmc_read_ssr(struct mmc_card *card) return 0; } - ssr = kmalloc(64, GFP_KERNEL); - if (!ssr) - return -ENOMEM; - - err = mmc_app_sd_status(card, ssr); - if (err) { + if (mmc_app_sd_status(card, card->raw_ssr)) { pr_warn("%s: problem reading SD Status register\n", mmc_hostname(card->host)); - err = 0; - goto out; + return 0; } for (i = 0; i < 16; i++) - ssr[i] = be32_to_cpu(ssr[i]); + card->raw_ssr[i] = be32_to_cpu(card->raw_ssr[i]); /* * UNSTUFF_BITS only works with four u32s so we have to offset the * bitfield positions accordingly. */ - au = UNSTUFF_BITS(ssr, 428 - 384, 4); + au = UNSTUFF_BITS(card->raw_ssr, 428 - 384, 4); if (au) { if (au <= 9 || card->scr.sda_spec3) { card->ssr.au = sd_au_size[au]; - es = UNSTUFF_BITS(ssr, 408 - 384, 16); - et = UNSTUFF_BITS(ssr, 402 - 384, 6); + es = UNSTUFF_BITS(card->raw_ssr, 408 - 384, 16); + et = UNSTUFF_BITS(card->raw_ssr, 402 - 384, 6); if (es && et) { - eo = UNSTUFF_BITS(ssr, 400 - 384, 2); + eo = UNSTUFF_BITS(card->raw_ssr, 400 - 384, 2); card->ssr.erase_timeout = (et * 1000) / es; card->ssr.erase_offset = eo * 1000; } @@ -267,9 +260,8 @@ static int mmc_read_ssr(struct mmc_card *card) mmc_hostname(card->host)); } } -out: - kfree(ssr); - return err; + + return 0; } /* @@ -666,6 +658,14 @@ MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); +MMC_DEV_ATTR(ssr, + "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x\n", + card->raw_ssr[0], card->raw_ssr[1], card->raw_ssr[2], + card->raw_ssr[3], card->raw_ssr[4], card->raw_ssr[5], + card->raw_ssr[6], card->raw_ssr[7], card->raw_ssr[8], + card->raw_ssr[9], card->raw_ssr[10], card->raw_ssr[11], + card->raw_ssr[12], card->raw_ssr[13], card->raw_ssr[14], + card->raw_ssr[15]); MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); @@ -698,6 +698,7 @@ static struct attribute *sd_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, &dev_attr_scr.attr, + &dev_attr_ssr.attr, &dev_attr_date.attr, &dev_attr_erase_size.attr, &dev_attr_preferred_erase_size.attr, diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d8673ca968ba2..73fad83acbcb6 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -292,6 +292,7 @@ struct mmc_card { u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ u32 raw_scr[2]; /* raw card SCR */ + u32 raw_ssr[16]; /* raw card SSR */ struct mmc_cid cid; /* card identification */ struct mmc_csd csd; /* card specific */ struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ From 4ad90cf9a8c62ca247917eb1ea1560a4a13a0731 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 15 Aug 2016 07:32:45 +0200 Subject: [PATCH 14/68] mmc: sdhci-brcmstb: Delete owner assignment The field "owner" is set by core. Thus delete an extra initialisation. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Markus Elfring Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-brcmstb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index cce10fe3e19e4..609ba5b6dd4e9 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -128,7 +128,6 @@ MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); static struct platform_driver sdhci_brcmstb_driver = { .driver = { .name = "sdhci-brcmstb", - .owner = THIS_MODULE, .pm = &sdhci_brcmstb_pmops, .of_match_table = of_match_ptr(sdhci_brcm_of_match), }, From a215186d7f057a5ff159824b9f869f835c69e952 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 15 Aug 2016 16:19:38 +0800 Subject: [PATCH 15/68] mmc: sdhci-esdhc-imx: do not touch other bit when config DTOCV Now, when call esdhc_set_timeout() to set the data timeout counter value, IPP_RST_N(bit 23) is wrongly affected. This patch add a mask to avoid this. Signed-off-by: Haibo Chen Acked-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 99e0b334f9dfa..437c44887fe75 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -31,6 +31,7 @@ #include "sdhci-pltfm.h" #include "sdhci-esdhc.h" +#define ESDHC_SYS_CTRL_DTOCV_MASK 0x0f #define ESDHC_CTRL_D3CD 0x08 #define ESDHC_BURST_LEN_EN_INCR (1 << 27) /* VENDOR SPEC register */ @@ -937,7 +938,8 @@ static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); /* use maximum timeout counter */ - sdhci_writeb(host, esdhc_is_usdhc(imx_data) ? 0xF : 0xE, + esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK, + esdhc_is_usdhc(imx_data) ? 0xF : 0xE, SDHCI_TIMEOUT_CONTROL); } From 2fb0b02b79d1c5ca88e223750a2eb9b0a3679bb0 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 15 Aug 2016 16:31:33 +0800 Subject: [PATCH 16/68] mmc: sdhci-esdhc-imx: correct the max timeout count i.MX USDHC Reference Manual has a mistake, for the register SYS_CTRL, the DTOCV(bit 19~16) means the data timeout counter value. When DTOCV is set to 0xF, it means SDCLK << 29, not SDCLK << 28. Signed-off-by: Haibo Chen Acked-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 437c44887fe75..1f54fd8755c8e 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -929,7 +929,8 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); - return esdhc_is_usdhc(imx_data) ? 1 << 28 : 1 << 27; + /* Doc Errata: the uSDHC actual maximum timeout count is 1 << 29 */ + return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27; } static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) From 622b5f35dab374138cfc8ab76d0fcf6b342eb744 Mon Sep 17 00:00:00 2001 From: Christopher Freeman Date: Wed, 17 Aug 2016 13:34:27 -0400 Subject: [PATCH 17/68] mmc: sdhci: Do not allow tuning procedure to be interrupted wait_event_interruptible_timeout() will return early if the blocked process receives a signal, causing the driver to abort the tuning procedure and possibly leaving the controller in a bad state. Since the tuning command is expected to complete quickly (<50ms) and we've set a timeout, use wait_event_timeout() instead. Signed-off-by: Christopher Freeman Tested-by: Robert Foss Signed-off-by: Robert Foss Reviewed-by: Benson Leung Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cd65d474afa2b..0851a4bba4138 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2062,7 +2062,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) spin_unlock_irqrestore(&host->lock, flags); /* Wait for Buffer Read Ready interrupt */ - wait_event_interruptible_timeout(host->buf_ready_int, + wait_event_timeout(host->buf_ready_int, (host->tuning_done == 1), msecs_to_jiffies(50)); spin_lock_irqsave(&host->lock, flags); From b2db9c6743f2622f279af3a022a47a08e6e6f2c9 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 18 Aug 2016 10:26:38 -0700 Subject: [PATCH 18/68] mmc: sdhci-of-arasan: Don't power PHY w/ slow/no clock PHY intended to be used with the Arasan SDHCI 5.1 controller has trouble turning on when the card clock is slow or off. Strangely these problems appear to show up consistently on some boards while other boards work fine, but on the boards where it shows up the problem reproduces 100% of the time and is quite consistent in its behavior. These problems can be fixed by always making sure that we power on the PHY (and turn on its DLL) when the card clock is faster than about 50 MHz. Once on, we need to make sure that we never power down the PHY / turn off its DLL until the clock is faster again. We'll add logic for handling this into the sdhci-of-arasan driver. Note that right now the only user of a PHY in the sdhci-of-arasan driver is arasan,sdhci-5.1. It's presumed that all arasan,sdhci-5.1 PHY implementations need this workaround, so the logic is only contingent on having a PHY to control. If future Arasan controllers don't have this problem we can add code to decide if we want this flow or not. Also note that we check for slow clocks by checking for <= 400 kHz rather than checking for 50 MHz. This keeps things the most consistent and also means we can power the PHY on at max speed (where the DLL will lock fastest). Presumably anyone who intends to run with a card clock of < 50 MHz and > 400 kHz will be running on a device where this problem is fixed anyway. I believe this brings some resolution to the problems reported before. See the commit 6fc09244d74d ("mmc: sdhci-of-arasan: Revert: Always power the PHY off/on when clock changes"). Signed-off-by: Douglas Anderson Acked-by: Adrian Hunter Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 63 +++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index e0f193f7e3e50..0b3a9cfed2df7 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -35,6 +35,8 @@ #define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) #define CLK_CTRL_TIMEOUT_MIN_EXP 13 +#define PHY_CLK_TOO_SLOW_HZ 400000 + /* * On some SoCs the syscon area has a feature where the upper 16-bits of * each 32-bit register act as a write mask for the lower 16-bits. This allows @@ -77,6 +79,7 @@ struct sdhci_arasan_soc_ctl_map { * @host: Pointer to the main SDHCI host structure. * @clk_ahb: Pointer to the AHB clock * @phy: Pointer to the generic phy + * @is_phy_on: True if the PHY is on; false if not. * @sdcardclk_hw: Struct for the clock we might provide to a PHY. * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers. @@ -86,6 +89,7 @@ struct sdhci_arasan_data { struct sdhci_host *host; struct clk *clk_ahb; struct phy *phy; + bool is_phy_on; struct clk_hw sdcardclk_hw; struct clk *sdcardclk; @@ -170,13 +174,47 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); bool ctrl_phy = false; - if (clock > MMC_HIGH_52_MAX_DTR && (!IS_ERR(sdhci_arasan->phy))) - ctrl_phy = true; + if (!IS_ERR(sdhci_arasan->phy)) { + if (!sdhci_arasan->is_phy_on && clock <= PHY_CLK_TOO_SLOW_HZ) { + /* + * If PHY off, set clock to max speed and power PHY on. + * + * Although PHY docs apparently suggest power cycling + * when changing the clock the PHY doesn't like to be + * powered on while at low speeds like those used in ID + * mode. Even worse is powering the PHY on while the + * clock is off. + * + * To workaround the PHY limitations, the best we can + * do is to power it on at a faster speed and then slam + * through low speeds without power cycling. + */ + sdhci_set_clock(host, host->max_clk); + spin_unlock_irq(&host->lock); + phy_power_on(sdhci_arasan->phy); + spin_lock_irq(&host->lock); + sdhci_arasan->is_phy_on = true; + + /* + * We'll now fall through to the below case with + * ctrl_phy = false (so we won't turn off/on). The + * sdhci_set_clock() will set the real clock. + */ + } else if (clock > PHY_CLK_TOO_SLOW_HZ) { + /* + * At higher clock speeds the PHY is fine being power + * cycled and docs say you _should_ power cycle when + * changing clock speeds. + */ + ctrl_phy = true; + } + } - if (ctrl_phy) { + if (ctrl_phy && sdhci_arasan->is_phy_on) { spin_unlock_irq(&host->lock); phy_power_off(sdhci_arasan->phy); spin_lock_irq(&host->lock); + sdhci_arasan->is_phy_on = false; } sdhci_set_clock(host, clock); @@ -185,6 +223,7 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) spin_unlock_irq(&host->lock); phy_power_on(sdhci_arasan->phy); spin_lock_irq(&host->lock); + sdhci_arasan->is_phy_on = true; } } @@ -239,13 +278,14 @@ static int sdhci_arasan_suspend(struct device *dev) if (ret) return ret; - if (!IS_ERR(sdhci_arasan->phy)) { + if (!IS_ERR(sdhci_arasan->phy) && sdhci_arasan->is_phy_on) { ret = phy_power_off(sdhci_arasan->phy); if (ret) { dev_err(dev, "Cannot power off phy.\n"); sdhci_resume_host(host); return ret; } + sdhci_arasan->is_phy_on = false; } clk_disable(pltfm_host->clk); @@ -281,12 +321,13 @@ static int sdhci_arasan_resume(struct device *dev) return ret; } - if (!IS_ERR(sdhci_arasan->phy)) { + if (!IS_ERR(sdhci_arasan->phy) && host->mmc->actual_clock) { ret = phy_power_on(sdhci_arasan->phy); if (ret) { dev_err(dev, "Cannot power on phy.\n"); return ret; } + sdhci_arasan->is_phy_on = true; } return sdhci_resume_host(host); @@ -547,12 +588,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev) goto unreg_clk; } - ret = phy_power_on(sdhci_arasan->phy); - if (ret < 0) { - dev_err(&pdev->dev, "phy_power_on err.\n"); - goto err_phy_power; - } - host->mmc_host_ops.hs400_enhanced_strobe = sdhci_arasan_hs400_enhanced_strobe; } @@ -564,9 +599,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev) return 0; err_add_host: - if (!IS_ERR(sdhci_arasan->phy)) - phy_power_off(sdhci_arasan->phy); -err_phy_power: if (!IS_ERR(sdhci_arasan->phy)) phy_exit(sdhci_arasan->phy); unreg_clk: @@ -589,7 +621,8 @@ static int sdhci_arasan_remove(struct platform_device *pdev) struct clk *clk_ahb = sdhci_arasan->clk_ahb; if (!IS_ERR(sdhci_arasan->phy)) { - phy_power_off(sdhci_arasan->phy); + if (sdhci_arasan->is_phy_on) + phy_power_off(sdhci_arasan->phy); phy_exit(sdhci_arasan->phy); } From 63311bece02abad40ae442e6deba2a2e522949fb Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 23 Aug 2016 10:51:04 +0200 Subject: [PATCH 19/68] mmc: sunxi: Check the value returned by clk_round_rate clk_round_rate() may return an error. Check it. Signed-off-by: Jean-Francois Moine Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 2ec91ce1fb0a5..142ab3ff579ce 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -692,7 +692,8 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, struct mmc_ios *ios) { - u32 rate, rval, clock = ios->clock; + long rate; + u32 rval, clock = ios->clock; int ret; /* 8 bit DDR requires a higher module clock */ @@ -701,13 +702,18 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, clock <<= 1; rate = clk_round_rate(host->clk_mmc, clock); - dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %d\n", + if (rate < 0) { + dev_err(mmc_dev(host->mmc), "error rounding clk to %d: %ld\n", + clock, rate); + return rate; + } + dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %ld\n", clock, rate); /* setting clock rate */ ret = clk_set_rate(host->clk_mmc, rate); if (ret) { - dev_err(mmc_dev(host->mmc), "error setting clk to %d: %d\n", + dev_err(mmc_dev(host->mmc), "error setting clk to %ld: %d\n", rate, ret); return ret; } From 2dd110b27d6621e881c796d1aea97da1b3143a5d Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Mon, 22 Aug 2016 18:42:18 +0200 Subject: [PATCH 20/68] mmc: sunxi-mmc: change idma descriptor to __le32 The sunxi-mmc driver does not take into account the processor may be big endian when writing the DMA descriptors. This causes cards not to be detected when running a big-endian kernel. Change the descriptors for IDMA to use __le32 and ensure they are suitably swapped before writing. Tested successfully on the Cubieboard2. Signed-off-by: Michael Weiser Acked-by: Maxime Ripard Cc: Ulf Hansson Cc: Chen-Yu Tsai Cc: linux-mmc@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 142ab3ff579ce..5fd4c5febf6c9 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -223,10 +223,10 @@ struct sunxi_mmc_clk_delay { }; struct sunxi_idma_des { - u32 config; - u32 buf_size; - u32 buf_addr_ptr1; - u32 buf_addr_ptr2; + __le32 config; + __le32 buf_size; + __le32 buf_addr_ptr1; + __le32 buf_addr_ptr2; }; struct sunxi_mmc_cfg { @@ -329,22 +329,25 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, int i, max_len = (1 << host->cfg->idma_des_size_bits); for (i = 0; i < data->sg_len; i++) { - pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN | - SDXC_IDMAC_DES0_DIC; + pdes[i].config = cpu_to_le32(SDXC_IDMAC_DES0_CH | + SDXC_IDMAC_DES0_OWN | + SDXC_IDMAC_DES0_DIC); if (data->sg[i].length == max_len) pdes[i].buf_size = 0; /* 0 == max_len */ else - pdes[i].buf_size = data->sg[i].length; + pdes[i].buf_size = cpu_to_le32(data->sg[i].length); next_desc += sizeof(struct sunxi_idma_des); - pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]); - pdes[i].buf_addr_ptr2 = (u32)next_desc; + pdes[i].buf_addr_ptr1 = + cpu_to_le32(sg_dma_address(&data->sg[i])); + pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc); } - pdes[0].config |= SDXC_IDMAC_DES0_FD; - pdes[i - 1].config |= SDXC_IDMAC_DES0_LD | SDXC_IDMAC_DES0_ER; - pdes[i - 1].config &= ~SDXC_IDMAC_DES0_DIC; + pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD); + pdes[i - 1].config |= cpu_to_le32(SDXC_IDMAC_DES0_LD | + SDXC_IDMAC_DES0_ER); + pdes[i - 1].config &= cpu_to_le32(~SDXC_IDMAC_DES0_DIC); pdes[i - 1].buf_addr_ptr2 = 0; /* From 6a4679f312357ac7c74c0e1b996efdd1d0a612fa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 24 Aug 2016 11:34:37 +0200 Subject: [PATCH 21/68] mmc: host: sh_mobile_sdhi: move card_busy from tmio to sdhi card_busy is only used/tested on SDHI for R-Car Gen2 and later. Move it to the SDHI driver, so we can then activate it conditionally depending on the SDHI type. Signed-off-by: Wolfram Sang Tested-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 8 ++++++++ drivers/mmc/host/tmio_mmc.h | 1 + drivers/mmc/host/tmio_mmc_pio.c | 9 +-------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index c3b651bf89cb4..c4e63b7790d71 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -213,6 +213,13 @@ static void sh_mobile_sdhi_clk_disable(struct tmio_mmc_host *host) clk_disable_unprepare(priv->clk); } +static int sh_mobile_sdhi_card_busy(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_DAT0); +} + static int sh_mobile_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) { @@ -369,6 +376,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->clk_update = sh_mobile_sdhi_clk_update; host->clk_disable = sh_mobile_sdhi_clk_disable; host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; + host->card_busy = sh_mobile_sdhi_card_busy; host->start_signal_voltage_switch = sh_mobile_sdhi_start_signal_voltage_switch; /* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */ diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 7f63ec05bdf41..1f1cb2628f673 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -158,6 +158,7 @@ struct tmio_mmc_host { void (*clk_disable)(struct tmio_mmc_host *host); int (*multi_io_quirk)(struct mmc_card *card, unsigned int direction, int blk_size); + int (*card_busy)(struct mmc_host *mmc); int (*start_signal_voltage_switch)(struct mmc_host *mmc, struct mmc_ios *ios); }; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 92467efc4e2c9..192817850dffa 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -960,20 +960,12 @@ static int tmio_multi_io_quirk(struct mmc_card *card, return blk_size; } -static int tmio_mmc_card_busy(struct mmc_host *mmc) -{ - struct tmio_mmc_host *host = mmc_priv(mmc); - - return !(sd_ctrl_read16_and_16_as_32(host, CTL_STATUS) & TMIO_STAT_DAT0); -} - static struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, .get_ro = tmio_mmc_get_ro, .get_cd = mmc_gpio_get_cd, .enable_sdio_irq = tmio_mmc_enable_sdio_irq, - .card_busy = tmio_mmc_card_busy, .multi_io_quirk = tmio_multi_io_quirk, }; @@ -1072,6 +1064,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, goto host_free; } + tmio_mmc_ops.card_busy = _host->card_busy; tmio_mmc_ops.start_signal_voltage_switch = _host->start_signal_voltage_switch; mmc->ops = &tmio_mmc_ops; From ff026099d775c74548839f1d627eb0b2bec0b857 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 24 Aug 2016 11:34:38 +0200 Subject: [PATCH 22/68] mmc: host: sh_mobile_sdhi: don't populate unneeded functions Populating card_busy caused a side-effect on a chip variant we don't have documentation for (r8a73a4). So, enable it and voltage switching only on devices known to support those features. Signed-off-by: Wolfram Sang Tested-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson Fixes: 452e5eef6d31 ("mmc: tmio: Add UHS-I mode support") --- drivers/mmc/host/sh_mobile_sdhi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index c4e63b7790d71..d679c8a533b6e 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -376,8 +376,14 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->clk_update = sh_mobile_sdhi_clk_update; host->clk_disable = sh_mobile_sdhi_clk_disable; host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; - host->card_busy = sh_mobile_sdhi_card_busy; - host->start_signal_voltage_switch = sh_mobile_sdhi_start_signal_voltage_switch; + + /* SDR speeds are only available on Gen2+ */ + if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) { + /* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */ + host->card_busy = sh_mobile_sdhi_card_busy; + host->start_signal_voltage_switch = + sh_mobile_sdhi_start_signal_voltage_switch; + } /* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */ if (!host->bus_shift && resource_size(res) > 0x100) /* old way to determine the shift */ From 67d35960f445176d5548c660a1b1708cad1c2042 Mon Sep 17 00:00:00 2001 From: Jungseung Lee Date: Wed, 24 Aug 2016 19:34:09 +0900 Subject: [PATCH 23/68] mmc: core: Add error message when switching fails in mmc_select_hs() The switch failure message in mmc_select_timing() had been removed since that is invalid: commit 0400ed0a083a ("mmc: core: remove the invalid message in mmc_select_timing") Now, in the case when mmc_select_hs() return error in mmc_select_timing(), there is nothing to print failure message. Let's make for mmc_select_hs() print message itself in the failure case. Signed-off-by: Jungseung Lee Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f2d185cf8a8ba..3486bc7fbb64a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1029,6 +1029,10 @@ static int mmc_select_hs(struct mmc_card *card) err = mmc_switch_status(card); } + if (err) + pr_warn("%s: switch to high-speed failed, err:%d\n", + mmc_hostname(card->host), err); + return err; } @@ -1265,11 +1269,8 @@ static int mmc_select_hs400es(struct mmc_card *card) /* Switch card to HS mode */ err = mmc_select_hs(card); - if (err) { - pr_err("%s: switch to high-speed failed, err:%d\n", - mmc_hostname(host), err); + if (err) goto out_err; - } err = mmc_switch_status(card); if (err) From 96e52daa508bf349582e41911da66461b54dcc12 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 26 Aug 2016 08:49:55 +0800 Subject: [PATCH 24/68] mmc: block: remove the check of packed for packed request routine packed should always exist without calling its cleanup function explicitly. Moreover, we have use it when preparing packed list. So I don't believe we should ever fall into this check again when doing mmc_blk_packed_hdr_wrq_prep or mmc_blk_end_packed_req,etc. And the code of mmc_blk_end_packed_req is trying to use packed before checking it which makes it quite weird. This patch is trying to remove these two checks and move it to the mmc_blk_prep_packed_list. If we find packed is null, then we should never use MMC_BLK_PACKED_CMD. By doing this, we could fall back to non-packed request if finding null packed, though it's impossible theoretically. After removing these two BUG_ONs, we also remove all other similar checks within the routine of mmc_blk_issue_rw_rq which checks the error handling of packed request. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 2206d4477dbbd..74c7625ee0dbc 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -142,8 +142,6 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) { struct mmc_packed *packed = mqrq->packed; - BUG_ON(!packed); - mqrq->cmd_type = MMC_PACKED_NONE; packed->nr_entries = MMC_PACKED_NR_ZERO; packed->idx_failure = MMC_PACKED_NR_IDX; @@ -1443,8 +1441,6 @@ static int mmc_blk_packed_err_check(struct mmc_card *card, int err, check, status; u8 *ext_csd; - BUG_ON(!packed); - packed->retries--; check = mmc_blk_err_check(card, areq); err = get_card_status(card, &status, 0); @@ -1673,6 +1669,18 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req) u8 max_packed_rw = 0; u8 reqs = 0; + /* + * We don't need to check packed for any further + * operation of packed stuff as we set MMC_PACKED_NONE + * and return zero for reqs if geting null packed. Also + * we clean the flag of MMC_BLK_PACKED_CMD to avoid doing + * it again when removing blk req. + */ + if (!mqrq->packed) { + md->flags &= (~MMC_BLK_PACKED_CMD); + goto no_packed; + } + if (!(md->flags & MMC_BLK_PACKED_CMD)) goto no_packed; @@ -1782,8 +1790,6 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq, u8 hdr_blocks; u8 i = 1; - BUG_ON(!packed); - mqrq->cmd_type = MMC_PACKED_WRITE; packed->blocks = 0; packed->idx_failure = MMC_PACKED_NR_IDX; @@ -1887,8 +1893,6 @@ static int mmc_blk_end_packed_req(struct mmc_queue_req *mq_rq) int idx = packed->idx_failure, i = 0; int ret = 0; - BUG_ON(!packed); - while (!list_empty(&packed->list)) { prq = list_entry_rq(packed->list.next); if (idx == i) { @@ -1917,8 +1921,6 @@ static void mmc_blk_abort_packed_req(struct mmc_queue_req *mq_rq) struct request *prq; struct mmc_packed *packed = mq_rq->packed; - BUG_ON(!packed); - while (!list_empty(&packed->list)) { prq = list_entry_rq(packed->list.next); list_del_init(&prq->queuelist); @@ -1935,8 +1937,6 @@ static void mmc_blk_revert_packed_req(struct mmc_queue *mq, struct request_queue *q = mq->queue; struct mmc_packed *packed = mq_rq->packed; - BUG_ON(!packed); - while (!list_empty(&packed->list)) { prq = list_entry_rq(packed->list.prev); if (prq->queuelist.prev != &packed->list) { From 923dff87373708801f501cbe8993df98a91b566e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 26 Aug 2016 08:49:56 +0800 Subject: [PATCH 25/68] mmc: sdio: deploy error handling instead of triggering BUG_ON When using mmc_io_rw_extended, it's intent to avoid null pointer of card and invalid func number. But actually it didn't prevent that as the seg_size already use the card. Currently the wrapper function sdio_io_rw_ext_helper already use card before calling mmc_io_rw_extended, so we should move this check to there. As to the func number, it was token from '(ocr & 0x70000000) >> 28' which should be enough to guarantee that it won't be larger than 7. But we should prevent the caller like wifi drivers modify this value. So let's move this check into sdio_io_rw_ext_helper either. Also we remove the BUG_ON for mmc_send_io_op_cond since all possible paths calling this function are protected by checking the arguments in advance. After deploying these changes, we could not see any panic within SDIO API even if func drivers abuse the SDIO func APIs. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio_io.c | 47 ++++++++++++++++++++++++------------- drivers/mmc/core/sdio_ops.c | 9 ++----- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 78cb4d5d9d581..406e5f037e320 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -26,8 +26,8 @@ */ void sdio_claim_host(struct sdio_func *func) { - BUG_ON(!func); - BUG_ON(!func->card); + if (WARN_ON(!func)) + return; mmc_claim_host(func->card->host); } @@ -42,8 +42,8 @@ EXPORT_SYMBOL_GPL(sdio_claim_host); */ void sdio_release_host(struct sdio_func *func) { - BUG_ON(!func); - BUG_ON(!func->card); + if (WARN_ON(!func)) + return; mmc_release_host(func->card->host); } @@ -62,8 +62,8 @@ int sdio_enable_func(struct sdio_func *func) unsigned char reg; unsigned long timeout; - BUG_ON(!func); - BUG_ON(!func->card); + if (!func) + return -EINVAL; pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func)); @@ -112,8 +112,8 @@ int sdio_disable_func(struct sdio_func *func) int ret; unsigned char reg; - BUG_ON(!func); - BUG_ON(!func->card); + if (!func) + return -EINVAL; pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func)); @@ -307,6 +307,9 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, unsigned max_blocks; int ret; + if (!func || (func->num > 7)) + return -EINVAL; + /* Do the bulk of the transfer using block mode (if supported). */ if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) { /* Blocks per command is limited by host count, host transfer @@ -367,7 +370,10 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) int ret; u8 val; - BUG_ON(!func); + if (!func) { + *err_ret = -EINVAL; + return 0xFF; + } if (err_ret) *err_ret = 0; @@ -398,7 +404,10 @@ void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret) { int ret; - BUG_ON(!func); + if (!func) { + *err_ret = -EINVAL; + return; + } ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL); if (err_ret) @@ -623,7 +632,10 @@ unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr, int ret; unsigned char val; - BUG_ON(!func); + if (!func) { + *err_ret = -EINVAL; + return 0xFF; + } if (err_ret) *err_ret = 0; @@ -658,7 +670,10 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, { int ret; - BUG_ON(!func); + if (!func) { + *err_ret = -EINVAL; + return; + } if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) { if (err_ret) @@ -684,8 +699,8 @@ EXPORT_SYMBOL_GPL(sdio_f0_writeb); */ mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func) { - BUG_ON(!func); - BUG_ON(!func->card); + if (!func) + return 0; return func->card->host->pm_caps; } @@ -707,8 +722,8 @@ int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags) { struct mmc_host *host; - BUG_ON(!func); - BUG_ON(!func->card); + if (!func) + return -EINVAL; host = func->card->host; diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index 34f6e80153064..90fe5545c6771 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -24,8 +24,6 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) struct mmc_command cmd = {0}; int i, err = 0; - BUG_ON(!host); - cmd.opcode = SD_IO_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR; @@ -71,8 +69,8 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, struct mmc_command cmd = {0}; int err; - BUG_ON(!host); - BUG_ON(fn > 7); + if (fn > 7) + return -EINVAL; /* sanity check */ if (addr & ~0x1FFFF) @@ -114,7 +112,6 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, unsigned addr, u8 in, u8 *out) { - BUG_ON(!card); return mmc_io_rw_direct_host(card->host, write, fn, addr, in, out); } @@ -129,8 +126,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, unsigned int nents, left_size, i; unsigned int seg_size = card->host->max_seg_size; - BUG_ON(!card); - BUG_ON(fn > 7); WARN_ON(blksz == 0); /* sanity check */ From c10bc3722378e5b387880c3c8baf19efc9676fe2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Aug 2016 14:59:13 +0300 Subject: [PATCH 26/68] mmc: sdhci-pci: Convert to use managed functions (part2) The commit 52ac7acf412b ("mmc: sdhci-pci: Convert to use managed functions pcim_* and devm_*") converted ->probe() / ->remove() functions to use device managed resource API. Here is a follow up to cover sdhci_pci_probe_slot() and sdhci_pci_remove_slot(). Signed-off-by: Andy Shevchenko Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 34 ++++++------------------------- drivers/mmc/host/sdhci-pci.h | 1 - 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 897cfd24ca2e8..93bb0ff82f241 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -156,7 +156,7 @@ static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) if (!gpio_is_valid(gpio)) return; - err = gpio_request(gpio, "sd_cd"); + err = devm_gpio_request(&slot->chip->pdev->dev, gpio, "sd_cd"); if (err < 0) goto out; @@ -179,7 +179,7 @@ static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) return; out_free: - gpio_free(gpio); + devm_gpio_free(&slot->chip->pdev->dev, gpio); out: dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n"); } @@ -188,8 +188,6 @@ static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) { if (slot->cd_irq >= 0) free_irq(slot->cd_irq, slot); - if (gpio_is_valid(slot->cd_gpio)) - gpio_free(slot->cd_gpio); } #else @@ -1615,7 +1613,6 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( slot->chip = chip; slot->host = host; - slot->pci_bar = bar; slot->rst_n_gpio = -EINVAL; slot->cd_gpio = -EINVAL; slot->cd_idx = -1; @@ -1643,27 +1640,22 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( host->irq = pdev->irq; - ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); + ret = pcim_iomap_regions(pdev, BIT(bar), mmc_hostname(host->mmc)); if (ret) { dev_err(&pdev->dev, "cannot request region\n"); goto cleanup; } - host->ioaddr = pci_ioremap_bar(pdev, bar); - if (!host->ioaddr) { - dev_err(&pdev->dev, "failed to remap registers\n"); - ret = -ENOMEM; - goto release; - } + host->ioaddr = pcim_iomap_table(pdev)[bar]; if (chip->fixes && chip->fixes->probe_slot) { ret = chip->fixes->probe_slot(slot); if (ret) - goto unmap; + goto cleanup; } if (gpio_is_valid(slot->rst_n_gpio)) { - if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) { + if (!devm_gpio_request(&pdev->dev, slot->rst_n_gpio, "eMMC_reset")) { gpio_direction_output(slot->rst_n_gpio, 1); slot->host->mmc->caps |= MMC_CAP_HW_RESET; slot->hw_reset = sdhci_pci_gpio_hw_reset; @@ -1702,18 +1694,9 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( return slot; remove: - if (gpio_is_valid(slot->rst_n_gpio)) - gpio_free(slot->rst_n_gpio); - if (chip->fixes && chip->fixes->remove_slot) chip->fixes->remove_slot(slot, 0); -unmap: - iounmap(host->ioaddr); - -release: - pci_release_region(pdev, bar); - cleanup: if (slot->data && slot->data->cleanup) slot->data->cleanup(slot->data); @@ -1738,17 +1721,12 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) sdhci_remove_host(slot->host, dead); - if (gpio_is_valid(slot->rst_n_gpio)) - gpio_free(slot->rst_n_gpio); - if (slot->chip->fixes && slot->chip->fixes->remove_slot) slot->chip->fixes->remove_slot(slot, dead); if (slot->data && slot->data->cleanup) slot->data->cleanup(slot->data); - pci_release_region(slot->chip->pdev, slot->pci_bar); - sdhci_free_host(slot->host); } diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 7e0788712e1a1..9c7c08b932238 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -72,7 +72,6 @@ struct sdhci_pci_slot { struct sdhci_host *host; struct sdhci_pci_data *data; - int pci_bar; int rst_n_gpio; int cd_gpio; int cd_irq; From 92add82e0ce0946d7aa6f2ae71cd160d914d2f48 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 30 Aug 2016 09:23:12 +0200 Subject: [PATCH 27/68] mmc: sdhci: Remove ->platform_init() callback as it's no longer used The commit 1ef5e49e46b9 ("mmc: sdhci-of-esdhc: add/remove some quirks according to vendor version") moved sdhci-of-esdhc away from using the ->platform_init() callback. As it was the only user of it and that it seems reasonable to believe that it won't be needed again, let's just remove it. Signed-off-by: Ulf Hansson Acked-by: Adrian Hunter --- drivers/mmc/host/sdhci-pltfm.c | 7 ------- drivers/mmc/host/sdhci.h | 1 - 2 files changed, 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 1d17dcfc3ffb5..ad49bfaf5bf8d 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -156,13 +156,6 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, host->quirks2 = pdata->quirks2; } - /* - * Some platforms need to probe the controller to be able to - * determine which caps should be used. - */ - if (host->ops && host->ops->platform_init) - host->ops->platform_init(host); - platform_set_drvdata(pdev, host); return host; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 0411c9f364619..a2bc9e111a3ab 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -555,7 +555,6 @@ struct sdhci_ops { void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*hw_reset)(struct sdhci_host *host); void (*adma_workaround)(struct sdhci_host *host, u32 intmask); - void (*platform_init)(struct sdhci_host *host); void (*card_event)(struct sdhci_host *host); void (*voltage_switch)(struct sdhci_host *host); int (*select_drive_strength)(struct sdhci_host *host, From 0ed50abb2d8fc81570b53af25621dad560cd49b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gl=C3=B6ckner?= Date: Tue, 30 Aug 2016 14:17:30 +0200 Subject: [PATCH 28/68] mmc: block: don't use CMD23 with very old MMC cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMD23 aka SET_BLOCK_COUNT was introduced with MMC v3.1. Older versions of the specification allowed to terminate multi-block transfers only with CMD12. The patch fixes the following problem: mmc0: new MMC card at address 0001 mmcblk0: mmc0:0001 SDMB-16 15.3 MiB mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 ... blk_update_request: I/O error, dev mmcblk0, sector 0 Buffer I/O error on dev mmcblk0, logical block 0, async page read mmcblk0: unable to read partition table Signed-off-by: Daniel Glöckner Cc: Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 74c7625ee0dbc..03670aa5ac8e5 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2303,7 +2303,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, set_capacity(md->disk, size); if (mmc_host_cmd23(card->host)) { - if (mmc_card_mmc(card) || + if ((mmc_card_mmc(card) && + card->csd.mmca_vsn >= CSD_SPEC_VER_3) || (mmc_card_sd(card) && card->scr.cmds & SD_SCR_CMD23_SUPPORT)) md->flags |= MMC_BLK_CMD23; From 941a659ffdefe31af8222451ca2a74ebab8d98dd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 14 Jul 2016 15:22:27 +0200 Subject: [PATCH 29/68] mmc: dw_mmc: exynos: Warn if HS400 is being used on non-Exynos5420 chipset Chipsets before Exynos5420 did not support HS400 so if MMC core tries to configure HS400 timing, this might or might not work. Warn in such cases because this is DTB misconfiguration. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Alim Akhtar Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index da0ef1765735f..7ab3d749b5aee 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -225,8 +225,12 @@ static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) * Not supported to configure register * related to HS400 */ - if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) + if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420) { + if (timing == MMC_TIMING_MMC_HS400) + dev_warn(host->dev, + "cannot configure HS400, unsupported chipset\n"); return; + } dqs = priv->saved_dqs_en; strobe = priv->saved_strobe_ctrl; From 00f400b73b8deba86d75804e990527b0bab21149 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 15 Jul 2016 09:47:34 +0800 Subject: [PATCH 30/68] mmc: dw_mmc: remove parsing for each slot subnode The intention to remove it comes from the conflict of what the mmc-core does with the way dw_mmc treats disable-wp. We could see that 'disable-wp' is supported by core but it's deprecated by dw_mmc as we don't expect it to be existed for each slot subnode but should be in the parent node. Based on searching for all the upstream dts using dw_mmc, we're confident that none of them use the deprecated way. Maybe we should take old dtb in consideration but it was a flag day since the time we was considering to take it away. The fact is that there are none of dts using the deprecated way since v3.18 or even earlier. So personally I don't believe the old dtb would/could bootup current kernel(may not?). Let's remove it now. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 43 --------------------------------------- 1 file changed, 43 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 767af2026f8b4..cea26ab4f97d2 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2527,47 +2527,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_OF -/* given a slot, find out the device node representing that slot */ -static struct device_node *dw_mci_of_find_slot_node(struct dw_mci_slot *slot) -{ - struct device *dev = slot->mmc->parent; - struct device_node *np; - const __be32 *addr; - int len; - - if (!dev || !dev->of_node) - return NULL; - - for_each_child_of_node(dev->of_node, np) { - addr = of_get_property(np, "reg", &len); - if (!addr || (len < sizeof(int))) - continue; - if (be32_to_cpup(addr) == slot->id) - return np; - } - return NULL; -} - -static void dw_mci_slot_of_parse(struct dw_mci_slot *slot) -{ - struct device_node *np = dw_mci_of_find_slot_node(slot); - - if (!np) - return; - - if (of_property_read_bool(np, "disable-wp")) { - slot->mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; - dev_warn(slot->mmc->parent, - "Slot quirk 'disable-wp' is deprecated\n"); - } -} -#else /* CONFIG_OF */ -static void dw_mci_slot_of_parse(struct dw_mci_slot *slot) -{ -} -#endif /* CONFIG_OF */ - static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) { struct mmc_host *mmc; @@ -2630,8 +2589,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->caps2) mmc->caps2 = host->pdata->caps2; - dw_mci_slot_of_parse(slot); - ret = mmc_of_parse(mmc); if (ret) goto err_host_allocated; From 7037f3beae352faa478ff5729c308756233ef4d3 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Fri, 15 Jul 2016 10:54:08 +0900 Subject: [PATCH 31/68] mmc: dw_mmc: remove the unnecessary IS_ERR() checking for ciu/biu clock If ciu/biu clock are NULL, clk_disable_unprepare should be just returned. In clk_disable_unprepare(), already checked whether clk is error or NULL. Signed-off-by: Jaehoon Chung Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index cea26ab4f97d2..1a1e1e1ac7c14 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3151,12 +3151,10 @@ int dw_mci_probe(struct dw_mci *host) host->dma_ops->exit(host); err_clk_ciu: - if (!IS_ERR(host->ciu_clk)) - clk_disable_unprepare(host->ciu_clk); + clk_disable_unprepare(host->ciu_clk); err_clk_biu: - if (!IS_ERR(host->biu_clk)) - clk_disable_unprepare(host->biu_clk); + clk_disable_unprepare(host->biu_clk); return ret; } @@ -3182,11 +3180,8 @@ void dw_mci_remove(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - if (!IS_ERR(host->ciu_clk)) - clk_disable_unprepare(host->ciu_clk); - - if (!IS_ERR(host->biu_clk)) - clk_disable_unprepare(host->biu_clk); + clk_disable_unprepare(host->ciu_clk); + clk_disable_unprepare(host->biu_clk); } EXPORT_SYMBOL(dw_mci_remove); From fdc22b6b1f259d939ace88dff21f167e79eadd67 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 12 Aug 2016 16:51:25 +0800 Subject: [PATCH 32/68] Documentation: synopsys-dw-mshc: add binding for resets Add resets property to synopsys-dw-mshc bindings. It is intended to represent the hardware reset signal present internally in some host controller IC designs. See Documentation/devicetree/bindings/reset/reset.txt for details. Signed-off-by: Guodong Xu Reviewed-by: Shawn Lin Acked-by: Rob Herring Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt index 8636f5ae97e51..4e00e859e885a 100644 --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt @@ -39,6 +39,10 @@ Required Properties: Optional properties: +* resets: phandle + reset specifier pair, intended to represent hardware + reset signal present internally in some host controller IC designs. + See Documentation/devicetree/bindings/reset/reset.txt for details. + * clocks: from common clock binding: handle to biu and ciu clocks for the bus interface unit clock and the card interface unit clock. From d6786fefe816ba60c794f8a41a73b0dd3a4df097 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 12 Aug 2016 16:51:26 +0800 Subject: [PATCH 33/68] mmc: dw_mmc: add reset support to dwmmc host controller Dwmmc host controller may in unknown state when entering kernel boot. One example is when booting from eMMC, bootloader need initialize MMC host controller into some state so it can read. In order to make sure MMC host controller in a clean initial state, this reset support is added. With this patch, a 'resets' property can be added into dw_mmc device tree node. The hardware logic is: dwmmc host controller IP receives a reset signal from a 'reset provider' (eg. power management unit). The 'resets' property points to this reset signal. So, during dwmmc driver probe, it can use this signal to reset itself. Refer to [1] for more information. [1] Documentation/devicetree/bindings/reset/reset.txt Signed-off-by: Guodong Xu Signed-off-by: Xinwei Kong Signed-off-by: Zhangfei Gao Reviewed-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 23 ++++++++++++++++++++++- include/linux/mmc/dw_mmc.h | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1a1e1e1ac7c14..e7eef75a65ef0 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2876,6 +2876,13 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (!pdata) return ERR_PTR(-ENOMEM); + /* find reset controller when exist */ + pdata->rstc = devm_reset_control_get_optional(dev, NULL); + if (IS_ERR(pdata->rstc)) { + if (PTR_ERR(pdata->rstc) == -EPROBE_DEFER) + return ERR_PTR(-EPROBE_DEFER); + } + /* find out number of slots supported */ of_property_read_u32(np, "num-slots", &pdata->num_slots); @@ -2947,7 +2954,9 @@ int dw_mci_probe(struct dw_mci *host) if (!host->pdata) { host->pdata = dw_mci_parse_dt(host); - if (IS_ERR(host->pdata)) { + if (PTR_ERR(host->pdata) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(host->pdata)) { dev_err(host->dev, "platform data not available\n"); return -EINVAL; } @@ -3001,6 +3010,12 @@ int dw_mci_probe(struct dw_mci *host) } } + if (!IS_ERR(host->pdata->rstc)) { + reset_control_assert(host->pdata->rstc); + usleep_range(10, 50); + reset_control_deassert(host->pdata->rstc); + } + setup_timer(&host->cmd11_timer, dw_mci_cmd11_timer, (unsigned long)host); @@ -3150,6 +3165,9 @@ int dw_mci_probe(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); + if (!IS_ERR(host->pdata->rstc)) + reset_control_assert(host->pdata->rstc); + err_clk_ciu: clk_disable_unprepare(host->ciu_clk); @@ -3180,6 +3198,9 @@ void dw_mci_remove(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); + if (!IS_ERR(host->pdata->rstc)) + reset_control_assert(host->pdata->rstc); + clk_disable_unprepare(host->ciu_clk); clk_disable_unprepare(host->biu_clk); } diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 83b0edfce471c..f5af2bd35e7fd 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -17,6 +17,7 @@ #include #include #include +#include #define MAX_MCI_SLOTS 2 @@ -259,6 +260,7 @@ struct dw_mci_board { /* delay in mS before detecting cards after interrupt */ u32 detect_delay_ms; + struct reset_control *rstc; struct dw_mci_dma_ops *dma_ops; struct dma_pdata *data; }; From e7a1dec19c09973c8c994a5b895627e0c7082b17 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 22 Aug 2016 10:57:16 +0800 Subject: [PATCH 34/68] mmc: dw_mmc: return -EILSEQ for EBE and SBE error The following log we found indicate the fact that dw_mmc didn't treat EBE or SBE as a similar problem as CRC error. -EIO is quite not informative as it may indicate that the device is broken rather than that of tuning stuff. ... [ 89.057226] bcmsdh_sdmmc: Failed to Read byte F1:@0x1001f=ff, Err: -5 [ 89.058811] bcmsdh_sdmmc: Failed to Read byte F1:@0x1001f=ff, Err: -5 [ 89.059415] bcmsdh_sdmmc: Failed to Read byte F1:@0x1000e=ff, Err: -84 [ 89.254248] dwmmc_rockchip fe310000.dwmmc: Successfully tuned phase to 199 [ 89.273912] dhd_set_suspend: Remove extra suspend setting [ 89.274478] dhd_enable_packet_filter: enter, value = 0 64 bytes from 112.90.83.112: icmp_seq=24 ttl=53 time=1321 ms 64 bytes from 112.90.83.112: icmp_seq=25 ttl=53 time=319 ms 64 bytes from 112.90.83.112: icmp_seq=26 ttl=53 time=69.8 ms 64 bytes from 112.90.83.112: icmp_seq=27 ttl=53 time=37.5 ms ... For the host, when failing to sample cmd's response due to tuning stuff, we still return -EIO as it's quite vague to figure out whether it related to signal or just the broken devices, especially for the card type detection when booting kernel as all things go well but the cmd set used. But for the data phase, if receiving the cmd's response which carriess data transfer, we should have more confidence that it is very probably related to the tuning stuff. Just as the log shown above, we sometimes suffer too much this kind of pain as the dw_mmc return -EIO for the case, so mmc-core will not do retune and caller drivers like bcm's wifi driver, still retry the failure more and more until dw_mmc finally generate CRC. Adrian suggested that drivers who care the specific cases should call mmc_retune_needed rather than doing it in mmc core. It makes sense but I'm considering that -EILSEQ actually means illegal sequence , so we use it for CRC cases. Meanwhile, SBE/EBE indicate the illegal sequence of start bit or end bit for data0~7. So I realize that we should use -EILSEQ for them both as well CRC cases. Suggested-by: Adrian Hunter Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e7eef75a65ef0..c23e252c037f7 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1695,11 +1695,11 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->error = -ETIMEDOUT; } else if (host->dir_status == DW_MCI_RECV_STATUS) { - data->error = -EIO; + data->error = -EILSEQ; } } else { /* SDMMC_INT_SBE is included */ - data->error = -EIO; + data->error = -EILSEQ; } dev_dbg(host->dev, "data error, status 0x%08x\n", status); From 3203a82724b86cc221e63a519313544d5f192289 Mon Sep 17 00:00:00 2001 From: Jin Guojun Date: Thu, 4 Aug 2016 10:16:13 +0800 Subject: [PATCH 35/68] mmc: dw_mmc: k3: UHS-SD card for Hisilicon Hikey Hisilicon Hikey have no tuning function in dw_mmc-k3.c, so we must do the tuning function stub when we init UHS card. Signed-off-by: Jin Guojun Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-k3.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 8e9d886bfcda3..624789496dcea 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -131,11 +131,17 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios) host->bus_hz = clk_get_rate(host->biu_clk); } +static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +{ + return 0; +} + static const struct dw_mci_drv_data hi6220_data = { .caps = dw_mci_hi6220_caps, .switch_voltage = dw_mci_hi6220_switch_voltage, .set_ios = dw_mci_hi6220_set_ios, .parse_dt = dw_mci_hi6220_parse_dt, + .execute_tuning = dw_mci_hi6220_execute_tuning, }; static const struct of_device_id dw_mci_k3_match[] = { From 2e57bbe22c15916a020c4563ebcad5c848c0183c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 29 Aug 2016 12:33:39 +0300 Subject: [PATCH 36/68] sdhci-pci: refactor intel_mrfld_mmc_probe_slot() Refactor intel_mrfld_mmc_probe_slot() to use switch case. The change allows to add a support for SD and SDIO interfaces without any pain. Signed-off-by: Andy Shevchenko Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 93bb0ff82f241..40f4fe833abac 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -422,14 +422,19 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) { - if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_0) && - (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_1)) + unsigned int func = PCI_FUNC(slot->chip->pdev->devfn); + + switch (func) { + case INTEL_MRFLD_EMMC_0: + case INTEL_MRFLD_EMMC_1: + slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE | + MMC_CAP_8_BIT_DATA | + MMC_CAP_1_8V_DDR; + break; + default: /* SD support is not ready yet */ return -ENODEV; - - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_1_8V_DDR; - + } return 0; } From d55655773ab23d170b4295666f7feff976124ae3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 29 Aug 2016 12:33:40 +0300 Subject: [PATCH 37/68] sdhci-pci: enable SDIO interface on Intel Merrifield Intel Merrifield is known to have an SDIO interface and on Intel Edison board a WiFi card is wired to it. Enable the interface here to allow WiFi card enumeration. Signed-off-by: Andy Shevchenko Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 40f4fe833abac..d38d701f4f2dc 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -419,6 +419,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { /* Define Host controllers for Intel Merrifield platform */ #define INTEL_MRFLD_EMMC_0 0 #define INTEL_MRFLD_EMMC_1 1 +#define INTEL_MRFLD_SDIO 3 static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) { @@ -431,6 +432,10 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) MMC_CAP_8_BIT_DATA | MMC_CAP_1_8V_DDR; break; + case INTEL_MRFLD_SDIO: + slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE | + MMC_CAP_POWER_OFF_CARD; + break; default: /* SD support is not ready yet */ return -ENODEV; From 4674b6c87019ba8e3e53c91712fff07b93f4efa7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 29 Aug 2016 12:33:41 +0300 Subject: [PATCH 38/68] sdhci-pci: enable SD card interface on Merrifield Intel Merrifield provides an interface to an external SD card. Enable it here. Signed-off-by: Andy Shevchenko Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index d38d701f4f2dc..3833c5fb28da7 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -419,6 +419,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { /* Define Host controllers for Intel Merrifield platform */ #define INTEL_MRFLD_EMMC_0 0 #define INTEL_MRFLD_EMMC_1 1 +#define INTEL_MRFLD_SD 2 #define INTEL_MRFLD_SDIO 3 static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) @@ -432,12 +433,14 @@ static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot) MMC_CAP_8_BIT_DATA | MMC_CAP_1_8V_DDR; break; + case INTEL_MRFLD_SD: + slot->host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + break; case INTEL_MRFLD_SDIO: slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD; break; default: - /* SD support is not ready yet */ return -ENODEV; } return 0; From b2ca77c98390304722c2baf289b181d6f0fa3c49 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 26 Aug 2016 16:51:15 +0800 Subject: [PATCH 39/68] mmc: sdhci-of-arasan: Properly set corecfg_clockmultiplier on rk3399 corecfg_clockmultiplier indicates clock multiplier value of programmable clock generator which should be the same value of SDHCI_CAPABILITIES_1. The default value of the register, corecfg_clockmultiplier, is 0x10. But actually it is a mistake by designer as our intention was to set it to be zero which means we don't support programmable clock generator. So we have to make it to be zero on bootloader which seems work fine until now. But now we find an issue that when deploying genpd support for it, the remove callback will trigger the genpd to poweroff the power domain for sdhci-of-arasan which manage the controller, phy and corecfg_* stuff. So when we do bind/unbind the driver, we have already reinit the controller and phy, but without doing that for corecfg_*. Regarding to only the corecfg_clockmultipler is wrong, let's fix it by explicitly marking it to be zero when probing. With this change, we could do bind/unbind successfully. Reported-by: Ziyuan Xu Cc: Douglas Anderson Signed-off-by: Shawn Lin Reviewed-by: Ziyuan Xu Tested-by: Ziyuan Xu Reviewed-by: Douglas Anderson Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 46 ++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 0b3a9cfed2df7..33601a817ea67 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -67,10 +67,12 @@ struct sdhci_arasan_soc_ctl_field { * accessible via the syscon API. * * @baseclkfreq: Where to find corecfg_baseclkfreq + * @clockmultiplier: Where to find corecfg_clockmultiplier * @hiword_update: If true, use HIWORD_UPDATE to access the syscon */ struct sdhci_arasan_soc_ctl_map { struct sdhci_arasan_soc_ctl_field baseclkfreq; + struct sdhci_arasan_soc_ctl_field clockmultiplier; bool hiword_update; }; @@ -100,6 +102,7 @@ struct sdhci_arasan_data { static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 }, + .clockmultiplier = { .reg = 0xf02c, .width = 8, .shift = 0}, .hiword_update = true, }; @@ -378,6 +381,45 @@ static const struct clk_ops arasan_sdcardclk_ops = { .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate, }; +/** + * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier + * + * The corecfg_clockmultiplier is supposed to contain clock multiplier + * value of programmable clock generator. + * + * NOTES: + * - Many existing devices don't seem to do this and work fine. To keep + * compatibility for old hardware where the device tree doesn't provide a + * register map, this function is a noop if a soc_ctl_map hasn't been provided + * for this platform. + * - The value of corecfg_clockmultiplier should sync with that of corresponding + * value reading from sdhci_capability_register. So this function is called + * once at probe time and never called again. + * + * @host: The sdhci_host + */ +static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host, + u32 value) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = + sdhci_arasan->soc_ctl_map; + + /* Having a map is optional */ + if (!soc_ctl_map) + return; + + /* If we have a map, we expect to have a syscon */ + if (!sdhci_arasan->soc_ctl_base) { + pr_warn("%s: Have regmap, but no soc-ctl-syscon\n", + mmc_hostname(host->mmc)); + return; + } + + sdhci_arasan_syscon_write(host, &soc_ctl_map->clockmultiplier, value); +} + /** * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq * @@ -559,6 +601,10 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_get_of_property(pdev); pltfm_host->clk = clk_xin; + if (of_device_is_compatible(pdev->dev.of_node, + "rockchip,rk3399-sdhci-5.1")) + sdhci_arasan_update_clockmultiplier(host, 0x0); + sdhci_arasan_update_baseclkfreq(host); ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); From 4ae12588e028f66a505b2287e8237a1815ee31a3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 1 Sep 2016 13:46:17 +0200 Subject: [PATCH 40/68] mmc: tegra: Mark 64-bit DMA broken on Tegra124 According to the TRM, the SD/MMC controller on Tegra124 supports 34-bit addressing, but testing shows that this doesn't work. On a device which has more than 2 GiB of RAM and LPAE enabled, buffer allocations can use addresses above the 32-bit boundary. One way to work around this would be to enable IOMMU physical to virtual address translations for the SD/MMC controllers, but that's not easy to implement without breaking existing use-cases. It's also not obvious why 34-bit addressing doesn't work as advertised. In order to fix this for existing users, add the SDHCI_QUIRK2_BROKEN_64_BIT_DMA quirk for now. Reported-by: Paul Kocialkowski Acked-by: Stephen Warren Acked-by: Arnd Bergmann Acked-by: Adrian Hunter Signed-off-by: Thierry Reding Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 1e93dc4e303e4..20b6ff5b4af11 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -391,6 +391,31 @@ static const struct sdhci_tegra_soc_data soc_data_tegra114 = { .pdata = &sdhci_tegra114_pdata, }; +static const struct sdhci_pltfm_data sdhci_tegra124_pdata = { + .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_SINGLE_POWER_WRITE | + SDHCI_QUIRK_NO_HISPD_BIT | + SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + /* + * The TRM states that the SD/MMC controller found on + * Tegra124 can address 34 bits (the maximum supported by + * the Tegra memory controller), but tests show that DMA + * to or from above 4 GiB doesn't work. This is possibly + * caused by missing programming, though it's not obvious + * what sequence is required. Mark 64-bit DMA broken for + * now to fix this for existing users (e.g. Nyan boards). + */ + SDHCI_QUIRK2_BROKEN_64_BIT_DMA, + .ops = &tegra114_sdhci_ops, +}; + +static const struct sdhci_tegra_soc_data soc_data_tegra124 = { + .pdata = &sdhci_tegra124_pdata, +}; + static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | @@ -408,7 +433,7 @@ static const struct sdhci_tegra_soc_data soc_data_tegra210 = { static const struct of_device_id sdhci_tegra_dt_match[] = { { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, - { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 }, + { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra124 }, { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, From 12182affc74a8ce1e95bd4feeb1638c55d2ab6fd Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 00:04:03 +0200 Subject: [PATCH 41/68] mmc: core: Use a default maximum erase timeout In cases when the host->max_busy_timeout isn't specified, the calculated number of maximum discard sectors defaults to UINT_MAX. This may cause a too long timeout for a discard request. Avoid this by using a default maximum erase timeout of 60s, used when we calculate the maximum number of sectors that are allowed to be discarded per request. Do note that the minimum number of sectors to be discarded is still at least one "preferred erase size". Signed-off-by: Ulf Hansson Reviewed-by: Shawn Lin --- drivers/mmc/core/core.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e55cde6d436dd..59b452d5dc819 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -58,6 +58,9 @@ */ #define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */ +/* The max erase timeout, used when host->max_busy_timeout isn't specified */ +#define MMC_ERASE_TIMEOUT_MS (60 * 1000) /* 60 s */ + static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; /* @@ -2352,6 +2355,8 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card, struct mmc_host *host = card->host; unsigned int max_discard, x, y, qty = 0, max_qty, min_qty, timeout; unsigned int last_timeout = 0; + unsigned int max_busy_timeout = host->max_busy_timeout ? + host->max_busy_timeout : MMC_ERASE_TIMEOUT_MS; if (card->erase_shift) { max_qty = UINT_MAX >> card->erase_shift; @@ -2374,15 +2379,15 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card, * matter what size of 'host->max_busy_timeout', but if the * 'host->max_busy_timeout' is large enough for more discard sectors, * then we can continue to increase the max discard sectors until we - * get a balance value. + * get a balance value. In cases when the 'host->max_busy_timeout' + * isn't specified, use the default max erase timeout. */ do { y = 0; for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) { timeout = mmc_erase_timeout(card, arg, qty + x); - if (qty + x > min_qty && - timeout > host->max_busy_timeout) + if (qty + x > min_qty && timeout > max_busy_timeout) break; if (timeout < last_timeout) @@ -2427,9 +2432,6 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card) struct mmc_host *host = card->host; unsigned int max_discard, max_trim; - if (!host->max_busy_timeout) - return UINT_MAX; - /* * Without erase_group_def set, MMC erase timeout depends on clock * frequence which can change. In that case, the best choice is @@ -2447,7 +2449,8 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card) max_discard = 0; } pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n", - mmc_hostname(host), max_discard, host->max_busy_timeout); + mmc_hostname(host), max_discard, host->max_busy_timeout ? + host->max_busy_timeout : MMC_ERASE_TIMEOUT_MS); return max_discard; } EXPORT_SYMBOL(mmc_calc_max_discard); From 7428e0bf7e87627af880b013f5f4648f07f70842 Mon Sep 17 00:00:00 2001 From: Ai Kyuse Date: Tue, 6 Sep 2016 12:38:38 +0200 Subject: [PATCH 42/68] mmc: sh_mobile_sdhi: Add r8a7796 support Add support for r8a7796 SoC. Signed-off-by: Ai Kyuse Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/tmio_mmc.txt | 1 + drivers/mmc/host/sh_mobile_sdhi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt index 0f610d4b5b005..13df9c2399c38 100644 --- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt +++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt @@ -23,6 +23,7 @@ Required properties: "renesas,sdhi-r8a7793" - SDHI IP on R8A7793 SoC "renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC "renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC + "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC Optional properties: - toshiba,mmc-wrprotect-disable: write-protect detection is unavailable diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index d679c8a533b6e..49edff7fee49b 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -94,6 +94,7 @@ static const struct of_device_id sh_mobile_sdhi_of_match[] = { { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, {}, }; MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); From 902a8a0b50d7d13cf88211d0bde3a7c27fd98fa5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 Sep 2016 14:56:09 +0200 Subject: [PATCH 43/68] mmc: davinci: remove incorrect NO_IRQ use platform_get_irq() returns an error value on failure, not NO_IRQ, so the error handling here could never work. This changes the code to propagate the error value instead. Signed-off-by: Arnd Bergmann Signed-off-by: Ulf Hansson --- drivers/mmc/host/davinci_mmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index a56373c759831..8fa478c3b0db7 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1216,9 +1216,11 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev) } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!r || irq == NO_IRQ) + if (!r) return -ENODEV; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; mem_size = resource_size(r); mem = devm_request_mem_region(&pdev->dev, r->start, mem_size, From 71085123d27dc5d28ce523344f32ac0d20c5f0a5 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 7 Sep 2016 10:38:24 +0800 Subject: [PATCH 44/68] mmc: core: Factor out the alignment of erase size In order to clean up the mmc_erase() function and do some optimization for erase size alignment, factor out the guts of erase size alignment into mmc_align_erase_size() function. Signed-off-by: Baolin Wang Tested-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 48 +++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 59b452d5dc819..9ce6c25a8f540 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2205,6 +2205,36 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, return err; } +static unsigned int mmc_align_erase_size(struct mmc_card *card, + unsigned int *from, + unsigned int *to, + unsigned int nr) +{ + unsigned int from_new = *from, nr_new = nr, rem; + + rem = from_new % card->erase_size; + if (rem) { + rem = card->erase_size - rem; + from_new += rem; + if (nr_new > rem) + nr_new -= rem; + else + return 0; + } + + rem = nr_new % card->erase_size; + if (rem) + nr_new -= rem; + + if (nr_new == 0) + return 0; + + *to = from_new + nr_new; + *from = from_new; + + return nr_new; +} + /** * mmc_erase - erase sectors. * @card: card to erase @@ -2243,26 +2273,12 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, return -EINVAL; } - if (arg == MMC_ERASE_ARG) { - rem = from % card->erase_size; - if (rem) { - rem = card->erase_size - rem; - from += rem; - if (nr > rem) - nr -= rem; - else - return 0; - } - rem = nr % card->erase_size; - if (rem) - nr -= rem; - } + if (arg == MMC_ERASE_ARG) + nr = mmc_align_erase_size(card, &from, &to, nr); if (nr == 0) return 0; - to = from + nr; - if (to <= from) return -EINVAL; From 6c689886fbe41b6492bd8ee9334ff66893274810 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 7 Sep 2016 10:38:25 +0800 Subject: [PATCH 45/68] mmc: core: Optimize the mmc erase size alignment In most cases the 'card->erase_size' is power of 2, then the round_up/down() function is more efficient than '%' operation when the 'card->erase_size' is power of 2. Signed-off-by: Baolin Wang Tested-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9ce6c25a8f540..d083d2e57abdd 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2212,19 +2212,37 @@ static unsigned int mmc_align_erase_size(struct mmc_card *card, { unsigned int from_new = *from, nr_new = nr, rem; - rem = from_new % card->erase_size; - if (rem) { - rem = card->erase_size - rem; - from_new += rem; + /* + * When the 'card->erase_size' is power of 2, we can use round_up/down() + * to align the erase size efficiently. + */ + if (is_power_of_2(card->erase_size)) { + unsigned int temp = from_new; + + from_new = round_up(temp, card->erase_size); + rem = from_new - temp; + if (nr_new > rem) nr_new -= rem; else return 0; - } - rem = nr_new % card->erase_size; - if (rem) - nr_new -= rem; + nr_new = round_down(nr_new, card->erase_size); + } else { + rem = from_new % card->erase_size; + if (rem) { + rem = card->erase_size - rem; + from_new += rem; + if (nr_new > rem) + nr_new -= rem; + else + return 0; + } + + rem = nr_new % card->erase_size; + if (rem) + nr_new -= rem; + } if (nr_new == 0) return 0; From 3d254b54f743c19f137034163cc8d81e2a6f2858 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 5 Aug 2016 04:57:14 +0200 Subject: [PATCH 46/68] Documentation: dt: Add new compatible to sunxi mmc driver bindings Signed-off-by: Icenowy Zheng Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sunxi-mmc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt index 904ff9f898936..55cdd804cdbac 100644 --- a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt +++ b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt @@ -13,6 +13,7 @@ Required properties: * "allwinner,sun5i-a13-mmc" * "allwinner,sun7i-a20-mmc" * "allwinner,sun9i-a80-mmc" + * "allwinner,sun50i-a64-mmc" - reg : mmc controller base registers - clocks : a list with 4 phandle + clock specifier pairs - clock-names : must contain "ahb", "mmc", "output" and "sample" From e1b8dfd1b1c61b3fde3622a2f244a008faca2916 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 5 Aug 2016 04:57:15 +0200 Subject: [PATCH 47/68] mmc: sunxi: add support for A64 mmc controller A64 SoC features a MMC controller which need only the mod clock, and can calibrate delay by itself. This patch adds support for the new MMC controller IP core. Signed-off-by: Icenowy Zheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 5fd4c5febf6c9..c0a5c676d0e82 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -72,6 +72,13 @@ #define SDXC_REG_CHDA (0x90) #define SDXC_REG_CBDA (0x94) +/* New registers introduced in A64 */ +#define SDXC_REG_A12A 0x058 /* SMC Auto Command 12 Register */ +#define SDXC_REG_SD_NTSR 0x05C /* SMC New Timing Set Register */ +#define SDXC_REG_DRV_DL 0x140 /* Drive Delay Control Register */ +#define SDXC_REG_SAMP_DL_REG 0x144 /* SMC sample delay control */ +#define SDXC_REG_DS_DL_REG 0x148 /* SMC data strobe delay control */ + #define mmc_readl(host, reg) \ readl((host)->reg_base + SDXC_##reg) #define mmc_writel(host, reg, value) \ @@ -217,6 +224,17 @@ #define SDXC_CLK_50M_DDR 3 #define SDXC_CLK_50M_DDR_8BIT 4 +#define SDXC_2X_TIMING_MODE BIT(31) + +#define SDXC_CAL_START BIT(15) +#define SDXC_CAL_DONE BIT(14) +#define SDXC_CAL_DL_SHIFT 8 +#define SDXC_CAL_DL_SW_EN BIT(7) +#define SDXC_CAL_DL_SW_SHIFT 0 +#define SDXC_CAL_DL_MASK 0x3f + +#define SDXC_CAL_TIMEOUT 3 /* in seconds, 3s is enough*/ + struct sunxi_mmc_clk_delay { u32 output; u32 sample; @@ -232,6 +250,9 @@ struct sunxi_idma_des { struct sunxi_mmc_cfg { u32 idma_des_size_bits; const struct sunxi_mmc_clk_delay *clk_delays; + + /* does the IP block support autocalibration? */ + bool can_calibrate; }; struct sunxi_mmc_host { @@ -660,6 +681,47 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) return 0; } +static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off) +{ + u32 reg = readl(host->reg_base + reg_off); + u32 delay; + unsigned long timeout; + + if (!host->cfg->can_calibrate) + return 0; + + reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT); + reg &= ~SDXC_CAL_DL_SW_EN; + + writel(reg | SDXC_CAL_START, host->reg_base + reg_off); + + dev_dbg(mmc_dev(host->mmc), "calibration started\n"); + + timeout = jiffies + HZ * SDXC_CAL_TIMEOUT; + + while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) { + if (time_before(jiffies, timeout)) + cpu_relax(); + else { + reg &= ~SDXC_CAL_START; + writel(reg, host->reg_base + reg_off); + + return -ETIMEDOUT; + } + } + + delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK; + + reg &= ~SDXC_CAL_START; + reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN; + + writel(reg, host->reg_base + reg_off); + + dev_dbg(mmc_dev(host->mmc), "calibration ended, reg is 0x%x\n", reg); + + return 0; +} + static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, struct mmc_ios *ios, u32 rate) { @@ -740,6 +802,12 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, if (ret) return ret; + ret = sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL_REG); + if (ret) + return ret; + + /* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */ + return sunxi_mmc_oclk_onoff(host, 1); } @@ -991,21 +1059,31 @@ static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = { static const struct sunxi_mmc_cfg sun4i_a10_cfg = { .idma_des_size_bits = 13, .clk_delays = NULL, + .can_calibrate = false, }; static const struct sunxi_mmc_cfg sun5i_a13_cfg = { .idma_des_size_bits = 16, .clk_delays = NULL, + .can_calibrate = false, }; static const struct sunxi_mmc_cfg sun7i_a20_cfg = { .idma_des_size_bits = 16, .clk_delays = sunxi_mmc_clk_delays, + .can_calibrate = false, }; static const struct sunxi_mmc_cfg sun9i_a80_cfg = { .idma_des_size_bits = 16, .clk_delays = sun9i_mmc_clk_delays, + .can_calibrate = false, +}; + +static const struct sunxi_mmc_cfg sun50i_a64_cfg = { + .idma_des_size_bits = 16, + .clk_delays = NULL, + .can_calibrate = true, }; static const struct of_device_id sunxi_mmc_of_match[] = { @@ -1013,6 +1091,7 @@ static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg }, { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, + { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); From 5a3ab2815c6a3772e9c70e1b3f778f1a23fc8671 Mon Sep 17 00:00:00 2001 From: Jaedon Shin Date: Fri, 9 Sep 2016 11:08:39 +0900 Subject: [PATCH 48/68] mmc: DT: sdhci-brcmstb: Bindings document for common sdhci-brcmstb Changes to the DT binding document to separate the BCM7425 and the BCM7445. A compatible string "brcm,bcm7425-sdhci" was representing the BCM7425 SDHCI host controller with all BRCMSTB SoCs including the BCM7445. Now it should be separated because vary a bit in initialize each host controller. - Renames the DT binding document to common name. - Adds a compatible string "brcm,bcm7445-sdhci" that is representing the BCM7445 with thereafter 28nm generation ARM based SoCs. Signed-off-by: Jaedon Shin Signed-off-by: Ulf Hansson Acked-by: Rob Herring --- .../mmc/{brcm,bcm7425-sdhci.txt => brcm,sdhci-brcmstb.txt} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename Documentation/devicetree/bindings/mmc/{brcm,bcm7425-sdhci.txt => brcm,sdhci-brcmstb.txt} (89%) diff --git a/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt similarity index 89% rename from Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt rename to Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt index 82847174c37d2..733b64a4d8eb7 100644 --- a/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt @@ -8,7 +8,9 @@ on Device Tree properties to enable them for SoC/Board combinations that support them. Required properties: -- compatible: "brcm,bcm7425-sdhci" +- compatible: should be one of the following + - "brcm,bcm7425-sdhci" + - "brcm,bcm7445-sdhci" Refer to clocks/clock-bindings.txt for generic clock consumer properties. From 6a3d8ced09482bd5b0e831740d83ed722aa2c5e6 Mon Sep 17 00:00:00 2001 From: Jaedon Shin Date: Fri, 9 Sep 2016 11:08:40 +0900 Subject: [PATCH 49/68] mmc: sdhci-brcmstb: Fix incorrect capability Clear incorrect SDHCI_CAN_64BIT capability on Broadcom MIPS based SoCs. The MIPS based SoCs are using ADMA only, but the several SoCs have the incorrect capability bit about ADMA 64-bit. The "brcm,bcm7425-sdhci" is compatible string for MIPS based SoC. Signed-off-by: Jaedon Shin Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-brcmstb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 609ba5b6dd4e9..159f6f64c68e7 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -98,6 +98,8 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) * properties through mmc_of_parse(). */ host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm7425-sdhci")) + host->caps &= ~SDHCI_CAN_64BIT; host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_DDR50); @@ -121,6 +123,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) static const struct of_device_id sdhci_brcm_of_match[] = { { .compatible = "brcm,bcm7425-sdhci" }, + { .compatible = "brcm,bcm7445-sdhci" }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); From 5163af5a5e2e69c9a5a854b92ffa7e2f7672dbf7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:11 +0300 Subject: [PATCH 50/68] mmc: core: Add support for sending commands during data transfer A host controller driver exposes its capability using caps flag MMC_CAP_CMD_DURING_TFR. A driver with that capability can accept requests that are marked mrq->cap_cmd_during_tfr = true. Then the driver informs the upper layers when the command line is available for further commands by calling mmc_command_done(). Because of that, the driver will not then automatically send STOP commands, and it is the responsibility of the upper layer to send a STOP command if it is required. For requests submitted through the mmc_wait_for_req() interface, the caller sets mrq->cap_cmd_during_tfr = true which causes mmc_wait_for_req() in fact not to wait. The caller can then send commands that do not use the data lines. Finally the caller can wait for the transfer to complete by calling mmc_wait_for_req_done() which is now exported. For requests submitted through the mmc_start_req() interface, the caller again sets mrq->cap_cmd_during_tfr = true, but mmc_start_req() anyway does not wait. The caller can then send commands that do not use the data lines. Finally the caller can wait for the transfer to complete in the normal way i.e. calling mmc_start_req() again. Irrespective of how a cap_cmd_during_tfr request is started, mmc_is_req_done() can be called if the upper layer needs to determine if the request is done. However the appropriate waiting function (either mmc_wait_for_req_done() or mmc_start_req()) must still be called. The implementation consists primarily of a new completion mrq->cmd_completion which notifies when the command line is available for further commands. That completion is completed by mmc_command_done(). When there is an ongoing data transfer, calls to mmc_wait_for_req() will automatically wait on that completion, so the caller does not have to do anything special. Note, in the case of errors, the driver may call mmc_request_done() without calling mmc_command_done() because mmc_request_done() always calls mmc_command_done(). Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 95 +++++++++++++++++++++++++++++++++++++--- include/linux/mmc/core.h | 7 +++ include/linux/mmc/host.h | 5 +++ 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d083d2e57abdd..f0ed0afe033dc 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -120,6 +120,24 @@ static inline void mmc_should_fail_request(struct mmc_host *host, #endif /* CONFIG_FAIL_MMC_REQUEST */ +static inline void mmc_complete_cmd(struct mmc_request *mrq) +{ + if (mrq->cap_cmd_during_tfr && !completion_done(&mrq->cmd_completion)) + complete_all(&mrq->cmd_completion); +} + +void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq) +{ + if (!mrq->cap_cmd_during_tfr) + return; + + mmc_complete_cmd(mrq); + + pr_debug("%s: cmd done, tfr ongoing (CMD%u)\n", + mmc_hostname(host), mrq->cmd->opcode); +} +EXPORT_SYMBOL(mmc_command_done); + /** * mmc_request_done - finish processing an MMC request * @host: MMC host which completed request @@ -146,6 +164,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) cmd->retries = 0; } + if (host->ongoing_mrq == mrq) + host->ongoing_mrq = NULL; + + mmc_complete_cmd(mrq); + trace_mmc_request_done(host, mrq); if (err && cmd->retries && !mmc_card_removed(host->card)) { @@ -158,7 +181,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) } else { mmc_should_fail_request(host, mrq); - led_trigger_event(host->led, LED_OFF); + if (!host->ongoing_mrq) + led_trigger_event(host->led, LED_OFF); if (mrq->sbc) { pr_debug("%s: req done : %d: %08x %08x %08x %08x\n", @@ -223,6 +247,15 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) } } + if (mrq->cap_cmd_during_tfr) { + host->ongoing_mrq = mrq; + /* + * Retry path could come through here without having waiting on + * cmd_completion, so ensure it is reinitialised. + */ + reinit_completion(&mrq->cmd_completion); + } + trace_mmc_request_start(host, mrq); host->ops->request(host, mrq); @@ -389,6 +422,18 @@ static void mmc_wait_done(struct mmc_request *mrq) complete(&mrq->completion); } +static inline void mmc_wait_ongoing_tfr_cmd(struct mmc_host *host) +{ + struct mmc_request *ongoing_mrq = READ_ONCE(host->ongoing_mrq); + + /* + * If there is an ongoing transfer, wait for the command line to become + * available. + */ + if (ongoing_mrq && !completion_done(&ongoing_mrq->cmd_completion)) + wait_for_completion(&ongoing_mrq->cmd_completion); +} + /* *__mmc_start_data_req() - starts data request * @host: MMC host to start the request @@ -396,17 +441,24 @@ static void mmc_wait_done(struct mmc_request *mrq) * * Sets the done callback to be called when request is completed by the card. * Starts data mmc request execution + * If an ongoing transfer is already in progress, wait for the command line + * to become available before sending another command. */ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) { int err; + mmc_wait_ongoing_tfr_cmd(host); + mrq->done = mmc_wait_data_done; mrq->host = host; + init_completion(&mrq->cmd_completion); + err = mmc_start_request(host, mrq); if (err) { mrq->cmd->error = err; + mmc_complete_cmd(mrq); mmc_wait_data_done(mrq); } @@ -417,12 +469,17 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { int err; + mmc_wait_ongoing_tfr_cmd(host); + init_completion(&mrq->completion); mrq->done = mmc_wait_done; + init_completion(&mrq->cmd_completion); + err = mmc_start_request(host, mrq); if (err) { mrq->cmd->error = err; + mmc_complete_cmd(mrq); complete(&mrq->completion); } @@ -486,8 +543,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host, return err; } -static void mmc_wait_for_req_done(struct mmc_host *host, - struct mmc_request *mrq) +void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq) { struct mmc_command *cmd; @@ -528,6 +584,28 @@ static void mmc_wait_for_req_done(struct mmc_host *host, mmc_retune_release(host); } +EXPORT_SYMBOL(mmc_wait_for_req_done); + +/** + * mmc_is_req_done - Determine if a 'cap_cmd_during_tfr' request is done + * @host: MMC host + * @mrq: MMC request + * + * mmc_is_req_done() is used with requests that have + * mrq->cap_cmd_during_tfr = true. mmc_is_req_done() must be called after + * starting a request and before waiting for it to complete. That is, + * either in between calls to mmc_start_req(), or after mmc_wait_for_req() + * and before mmc_wait_for_req_done(). If it is called at other times the + * result is not meaningful. + */ +bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq) +{ + if (host->areq) + return host->context_info.is_done_rcv; + else + return completion_done(&mrq->completion); +} +EXPORT_SYMBOL(mmc_is_req_done); /** * mmc_pre_req - Prepare for a new request @@ -648,13 +726,18 @@ EXPORT_SYMBOL(mmc_start_req); * @mrq: MMC request to start * * Start a new MMC custom command request for a host, and wait - * for the command to complete. Does not attempt to parse the - * response. + * for the command to complete. In the case of 'cap_cmd_during_tfr' + * requests, the transfer is ongoing and the caller can issue further + * commands that do not use the data lines, and then wait by calling + * mmc_wait_for_req_done(). + * Does not attempt to parse the response. */ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) { __mmc_start_req(host, mrq); - mmc_wait_for_req_done(host, mrq); + + if (!mrq->cap_cmd_during_tfr) + mmc_wait_for_req_done(host, mrq); } EXPORT_SYMBOL(mmc_wait_for_req); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index b01e77de1a74d..368bed70aa9d5 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -133,8 +133,12 @@ struct mmc_request { struct mmc_command *stop; struct completion completion; + struct completion cmd_completion; void (*done)(struct mmc_request *);/* completion function */ struct mmc_host *host; + + /* Allow other commands during this ongoing data transfer or busy wait */ + bool cap_cmd_during_tfr; }; struct mmc_card; @@ -146,6 +150,9 @@ extern struct mmc_async_req *mmc_start_req(struct mmc_host *, struct mmc_async_req *, int *); extern int mmc_interrupt_hpi(struct mmc_card *); extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); +extern void mmc_wait_for_req_done(struct mmc_host *host, + struct mmc_request *mrq); +extern bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq); extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index aa4bfbf129e45..0b2439441cc8c 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -281,6 +281,7 @@ struct mmc_host { #define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */ #define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */ #define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */ +#define MMC_CAP_CMD_DURING_TFR (1 << 29) /* Commands during data transfer */ #define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */ #define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */ @@ -382,6 +383,9 @@ struct mmc_host { struct mmc_async_req *areq; /* active async req */ struct mmc_context_info context_info; /* async synchronization info */ + /* Ongoing data transfer that allows commands during transfer */ + struct mmc_request *ongoing_mrq; + #ifdef CONFIG_FAIL_MMC_REQUEST struct fault_attr fail_mmc_request; #endif @@ -418,6 +422,7 @@ int mmc_power_restore_host(struct mmc_host *host); void mmc_detect_change(struct mmc_host *, unsigned long delay); void mmc_request_done(struct mmc_host *, struct mmc_request *); +void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq); static inline void mmc_signal_sdio_irq(struct mmc_host *host) { From 4bbb9aac9a9a6cd4b2718e43f998c5e4e3b382cd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:12 +0300 Subject: [PATCH 51/68] mmc: mmc_test: Add tests for sending commands during transfer Add 6 tests for sending commands during transfer. The tests are: * Commands during read - no Set Block Count (CMD23). * Commands during write - no Set Block Count (CMD23). * Commands during read - use Set Block Count (CMD23). * Commands during write - use Set Block Count (CMD23). * Commands during non-blocking read - use Set Block Count (CMD23). * Commands during non-blocking write - use Set Block Count (CMD23). For a range of transfer sizes, the tests start an ongoing data transfer and then repeatedly send the status command (CMD13) while the transfer continues. The tests pass if all requests complete with no errors. The host controller driver must support MMC_CAP_CMD_DURING_TFR. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/card/mmc_test.c | 308 ++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index c032eef45762c..5a8dc5a76e0df 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -184,6 +184,29 @@ static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size) return mmc_set_blocklen(test->card, size); } +static bool mmc_test_card_cmd23(struct mmc_card *card) +{ + return mmc_card_mmc(card) || + (mmc_card_sd(card) && card->scr.cmds & SD_SCR_CMD23_SUPPORT); +} + +static void mmc_test_prepare_sbc(struct mmc_test_card *test, + struct mmc_request *mrq, unsigned int blocks) +{ + struct mmc_card *card = test->card; + + if (!mrq->sbc || !mmc_host_cmd23(card->host) || + !mmc_test_card_cmd23(card) || !mmc_op_multi(mrq->cmd->opcode) || + (card->quirks & MMC_QUIRK_BLK_NO_CMD23)) { + mrq->sbc = NULL; + return; + } + + mrq->sbc->opcode = MMC_SET_BLOCK_COUNT; + mrq->sbc->arg = blocks; + mrq->sbc->flags = MMC_RSP_R1 | MMC_CMD_AC; +} + /* * Fill in the mmc_request structure given a set of transfer parameters. */ @@ -221,6 +244,8 @@ static void mmc_test_prepare_mrq(struct mmc_test_card *test, mrq->data->sg = sg; mrq->data->sg_len = sg_len; + mmc_test_prepare_sbc(test, mrq, blocks); + mmc_set_data_timeout(mrq->data, test->card); } @@ -693,6 +718,8 @@ static int mmc_test_check_result(struct mmc_test_card *test, ret = 0; + if (mrq->sbc && mrq->sbc->error) + ret = mrq->sbc->error; if (!ret && mrq->cmd->error) ret = mrq->cmd->error; if (!ret && mrq->data->error) @@ -2278,6 +2305,245 @@ static int mmc_test_reset(struct mmc_test_card *test) return RESULT_FAIL; } +struct mmc_test_req { + struct mmc_request mrq; + struct mmc_command sbc; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_command status; + struct mmc_data data; +}; + +static struct mmc_test_req *mmc_test_req_alloc(void) +{ + struct mmc_test_req *rq = kzalloc(sizeof(*rq), GFP_KERNEL); + + if (rq) { + rq->mrq.cmd = &rq->cmd; + rq->mrq.data = &rq->data; + rq->mrq.stop = &rq->stop; + } + + return rq; +} + +static int mmc_test_send_status(struct mmc_test_card *test, + struct mmc_command *cmd) +{ + memset(cmd, 0, sizeof(*cmd)); + + cmd->opcode = MMC_SEND_STATUS; + if (!mmc_host_is_spi(test->card->host)) + cmd->arg = test->card->rca << 16; + cmd->flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + + return mmc_wait_for_cmd(test->card->host, cmd, 0); +} + +static int mmc_test_ongoing_transfer(struct mmc_test_card *test, + unsigned int dev_addr, int use_sbc, + int repeat_cmd, int write, int use_areq) +{ + struct mmc_test_req *rq = mmc_test_req_alloc(); + struct mmc_host *host = test->card->host; + struct mmc_test_area *t = &test->area; + struct mmc_async_req areq; + struct mmc_request *mrq; + unsigned long timeout; + bool expired = false; + int ret = 0, cmd_ret; + u32 status = 0; + int count = 0; + + if (!rq) + return -ENOMEM; + + mrq = &rq->mrq; + if (use_sbc) + mrq->sbc = &rq->sbc; + mrq->cap_cmd_during_tfr = true; + + areq.mrq = mrq; + areq.err_check = mmc_test_check_result_async; + + mmc_test_prepare_mrq(test, mrq, t->sg, t->sg_len, dev_addr, t->blocks, + 512, write); + + if (use_sbc && t->blocks > 1 && !mrq->sbc) { + ret = mmc_host_cmd23(host) ? + RESULT_UNSUP_CARD : + RESULT_UNSUP_HOST; + goto out_free; + } + + /* Start ongoing data request */ + if (use_areq) { + mmc_start_req(host, &areq, &ret); + if (ret) + goto out_free; + } else { + mmc_wait_for_req(host, mrq); + } + + timeout = jiffies + msecs_to_jiffies(3000); + do { + count += 1; + + /* Send status command while data transfer in progress */ + cmd_ret = mmc_test_send_status(test, &rq->status); + if (cmd_ret) + break; + + status = rq->status.resp[0]; + if (status & R1_ERROR) { + cmd_ret = -EIO; + break; + } + + if (mmc_is_req_done(host, mrq)) + break; + + expired = time_after(jiffies, timeout); + if (expired) { + pr_info("%s: timeout waiting for Tran state status %#x\n", + mmc_hostname(host), status); + cmd_ret = -ETIMEDOUT; + break; + } + } while (repeat_cmd && R1_CURRENT_STATE(status) != R1_STATE_TRAN); + + /* Wait for data request to complete */ + if (use_areq) + mmc_start_req(host, NULL, &ret); + else + mmc_wait_for_req_done(test->card->host, mrq); + + /* + * For cap_cmd_during_tfr request, upper layer must send stop if + * required. + */ + if (mrq->data->stop && (mrq->data->error || !mrq->sbc)) { + if (ret) + mmc_wait_for_cmd(host, mrq->data->stop, 0); + else + ret = mmc_wait_for_cmd(host, mrq->data->stop, 0); + } + + if (ret) + goto out_free; + + if (cmd_ret) { + pr_info("%s: Send Status failed: status %#x, error %d\n", + mmc_hostname(test->card->host), status, cmd_ret); + } + + ret = mmc_test_check_result(test, mrq); + if (ret) + goto out_free; + + ret = mmc_test_wait_busy(test); + if (ret) + goto out_free; + + if (repeat_cmd && (t->blocks + 1) << 9 > t->max_tfr) + pr_info("%s: %d commands completed during transfer of %u blocks\n", + mmc_hostname(test->card->host), count, t->blocks); + + if (cmd_ret) + ret = cmd_ret; +out_free: + kfree(rq); + + return ret; +} + +static int __mmc_test_cmds_during_tfr(struct mmc_test_card *test, + unsigned long sz, int use_sbc, int write, + int use_areq) +{ + struct mmc_test_area *t = &test->area; + int ret; + + if (!(test->card->host->caps & MMC_CAP_CMD_DURING_TFR)) + return RESULT_UNSUP_HOST; + + ret = mmc_test_area_map(test, sz, 0, 0); + if (ret) + return ret; + + ret = mmc_test_ongoing_transfer(test, t->dev_addr, use_sbc, 0, write, + use_areq); + if (ret) + return ret; + + return mmc_test_ongoing_transfer(test, t->dev_addr, use_sbc, 1, write, + use_areq); +} + +static int mmc_test_cmds_during_tfr(struct mmc_test_card *test, int use_sbc, + int write, int use_areq) +{ + struct mmc_test_area *t = &test->area; + unsigned long sz; + int ret; + + for (sz = 512; sz <= t->max_tfr; sz += 512) { + ret = __mmc_test_cmds_during_tfr(test, sz, use_sbc, write, + use_areq); + if (ret) + return ret; + } + return 0; +} + +/* + * Commands during read - no Set Block Count (CMD23). + */ +static int mmc_test_cmds_during_read(struct mmc_test_card *test) +{ + return mmc_test_cmds_during_tfr(test, 0, 0, 0); +} + +/* + * Commands during write - no Set Block Count (CMD23). + */ +static int mmc_test_cmds_during_write(struct mmc_test_card *test) +{ + return mmc_test_cmds_during_tfr(test, 0, 1, 0); +} + +/* + * Commands during read - use Set Block Count (CMD23). + */ +static int mmc_test_cmds_during_read_cmd23(struct mmc_test_card *test) +{ + return mmc_test_cmds_during_tfr(test, 1, 0, 0); +} + +/* + * Commands during write - use Set Block Count (CMD23). + */ +static int mmc_test_cmds_during_write_cmd23(struct mmc_test_card *test) +{ + return mmc_test_cmds_during_tfr(test, 1, 1, 0); +} + +/* + * Commands during non-blocking read - use Set Block Count (CMD23). + */ +static int mmc_test_cmds_during_read_cmd23_nonblock(struct mmc_test_card *test) +{ + return mmc_test_cmds_during_tfr(test, 1, 0, 1); +} + +/* + * Commands during non-blocking write - use Set Block Count (CMD23). + */ +static int mmc_test_cmds_during_write_cmd23_nonblock(struct mmc_test_card *test) +{ + return mmc_test_cmds_during_tfr(test, 1, 1, 1); +} + static const struct mmc_test_case mmc_test_cases[] = { { .name = "Basic write (no data verification)", @@ -2605,6 +2871,48 @@ static const struct mmc_test_case mmc_test_cases[] = { .name = "Reset test", .run = mmc_test_reset, }, + + { + .name = "Commands during read - no Set Block Count (CMD23)", + .prepare = mmc_test_area_prepare, + .run = mmc_test_cmds_during_read, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Commands during write - no Set Block Count (CMD23)", + .prepare = mmc_test_area_prepare, + .run = mmc_test_cmds_during_write, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Commands during read - use Set Block Count (CMD23)", + .prepare = mmc_test_area_prepare, + .run = mmc_test_cmds_during_read_cmd23, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Commands during write - use Set Block Count (CMD23)", + .prepare = mmc_test_area_prepare, + .run = mmc_test_cmds_during_write_cmd23, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Commands during non-blocking read - use Set Block Count (CMD23)", + .prepare = mmc_test_area_prepare, + .run = mmc_test_cmds_during_read_cmd23_nonblock, + .cleanup = mmc_test_area_cleanup, + }, + + { + .name = "Commands during non-blocking write - use Set Block Count (CMD23)", + .prepare = mmc_test_area_prepare, + .run = mmc_test_cmds_during_write_cmd23_nonblock, + .cleanup = mmc_test_area_cleanup, + }, }; static DEFINE_MUTEX(mmc_test_lock); From 20845beff7d74f2021a60abe3789bb9c8881c2c1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:13 +0300 Subject: [PATCH 52/68] mmc: sdhci: Support cap_cmd_during_tfr requests Now SDHCI supports commands during transfer, enable support for the core API. There are 3 small changes needed: First, auto-CMD12 cannot be used with a cap_cmd_during_tfr request because the host controller cannot expect the command line to be available. Secondly, a cap_cmd_during_tfr request must not send a stop command, again because the host controller cannot expect the command line to be available. Thirdly, when a cap_cmd_during_tfr command completes, use mmc_command_complete() to notify the upper layers that the command line is now available for further commands. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0851a4bba4138..48055666c6557 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -888,7 +888,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) static inline bool sdhci_auto_cmd12(struct sdhci_host *host, struct mmc_request *mrq) { - return !mrq->sbc && (host->flags & SDHCI_AUTO_CMD12); + return !mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) && + !mrq->cap_cmd_during_tfr; } static void sdhci_set_transfer_mode(struct sdhci_host *host, @@ -1031,9 +1032,18 @@ static void sdhci_finish_data(struct sdhci_host *host) sdhci_do_reset(host, SDHCI_RESET_DATA); } - /* Avoid triggering warning in sdhci_send_command() */ - host->cmd = NULL; - sdhci_send_command(host, data->stop); + /* + * 'cap_cmd_during_tfr' request must not use the command line + * after mmc_command_done() has been called. It is upper layer's + * responsibility to send the stop command if required. + */ + if (data->mrq->cap_cmd_during_tfr) { + sdhci_finish_mrq(host, data->mrq); + } else { + /* Avoid triggering warning in sdhci_send_command() */ + host->cmd = NULL; + sdhci_send_command(host, data->stop); + } } else { sdhci_finish_mrq(host, data->mrq); } @@ -1165,6 +1175,9 @@ static void sdhci_finish_command(struct sdhci_host *host) } } + if (cmd->mrq->cap_cmd_during_tfr && cmd == cmd->mrq->cmd) + mmc_command_done(host->mmc, cmd->mrq); + /* * The host can send and interrupt when the busy state has * ended, allowing us to wait without wasting CPU cycles. From 32828857dba32c04e7383ba47db19e3bf37fce9d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:14 +0300 Subject: [PATCH 53/68] mmc: sdhci-pci: Set MMC_CAP_CMD_DURING_TFR for Intel eMMC controllers Set MMC_CAP_CMD_DURING_TFR for Intel BYT and related eMMC host controllers. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 3833c5fb28da7..72a1f1f5180a9 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -354,6 +354,7 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | + MMC_CAP_CMD_DURING_TFR | MMC_CAP_WAIT_WHILE_BUSY; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; slot->hw_reset = sdhci_pci_int_hw_reset; From c80f275fa1766cfdbc24c7a5d3d870df1fe730db Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:15 +0300 Subject: [PATCH 54/68] mmc: sdhci-acpi: Set MMC_CAP_CMD_DURING_TFR for Intel eMMC controllers Set MMC_CAP_CMD_DURING_TFR for Intel BYT and related eMMC host controllers. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 8fe0756c8e1e7..81d4dc034793d 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -275,7 +275,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .chip = &sdhci_acpi_chip_int, .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | - MMC_CAP_WAIT_WHILE_BUSY, + MMC_CAP_CMD_DURING_TFR | MMC_CAP_WAIT_WHILE_BUSY, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, From 29eb7bd01e80df316ab9d1da1a4ee580fae89188 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Sep 2016 11:34:38 +0200 Subject: [PATCH 55/68] mmc: card: do away with indirection pointer We have enough vtables in the kernel as it is, we don't need this one to create even more artificial separation of concerns. As is proved by the Makefile: obj-$(CONFIG_MMC_BLOCK) += mmc_block.o mmc_block-objs := block.o queue.o block.c and queue.c are baked into the same mmc_block.o object. So why would one of these objects access a function in the other object by dereferencing a pointer? Create a new block.h header file for the single shared function from block to queue and remove the function pointer and just call the queue request function. Apart from making the code more readable, this also makes link optimizations possible and probably speeds up the call as well. Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 3 +-- drivers/mmc/card/block.h | 1 + drivers/mmc/card/queue.c | 4 +++- drivers/mmc/card/queue.h | 2 -- 4 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 drivers/mmc/card/block.h diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 03670aa5ac8e5..c3335112e68c2 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2144,7 +2144,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) return 0; } -static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) +int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { int ret; struct mmc_blk_data *md = mq->data; @@ -2265,7 +2265,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, if (ret) goto err_putdisk; - md->queue.issue_fn = mmc_blk_issue_rq; md->queue.data = md; md->disk->major = MMC_BLOCK_MAJOR; diff --git a/drivers/mmc/card/block.h b/drivers/mmc/card/block.h new file mode 100644 index 0000000000000..cdabb2ee74be9 --- /dev/null +++ b/drivers/mmc/card/block.h @@ -0,0 +1 @@ +int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req); diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 708057261b389..8037f73a109a1 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -19,7 +19,9 @@ #include #include + #include "queue.h" +#include "block.h" #define MMC_QUEUE_BOUNCESZ 65536 @@ -68,7 +70,7 @@ static int mmc_queue_thread(void *d) bool req_is_special = mmc_req_is_special(req); set_current_state(TASK_RUNNING); - mq->issue_fn(mq, req); + mmc_blk_issue_rq(mq, req); cond_resched(); if (mq->flags & MMC_QUEUE_NEW_REQUEST) { mq->flags &= ~MMC_QUEUE_NEW_REQUEST; diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index fee5e12714651..3c15a75bae862 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -57,8 +57,6 @@ struct mmc_queue { unsigned int flags; #define MMC_QUEUE_SUSPENDED (1 << 0) #define MMC_QUEUE_NEW_REQUEST (1 << 1) - - int (*issue_fn)(struct mmc_queue *, struct request *); void *data; struct request_queue *queue; struct mmc_queue_req mqrq[2]; From 51b50c961676428cd356d6fa494a2e9f53dc77bf Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:45 +0200 Subject: [PATCH 56/68] mmc: add define for R1 response without CRC The core uses it for polling. Give drivers a proper define handle this case like for other response types. Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- include/linux/mmc/core.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 368bed70aa9d5..2b953eb8ceae1 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -55,6 +55,9 @@ struct mmc_command { #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +/* Can be used by core to poll after switch to MMC HS mode */ +#define MMC_RSP_R1_NO_CRC (MMC_RSP_PRESENT|MMC_RSP_OPCODE) + #define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE)) /* From 8c8d0ecbd87fff5cfca0a9a72fa388c3f61dab8f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:46 +0200 Subject: [PATCH 57/68] mmc: rtsx_pci: use new macro for R1 without CRC Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 396c9b7e4121b..3ccaa1415f33b 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -126,7 +126,7 @@ static int sd_response_type(struct mmc_command *cmd) return SD_RSP_TYPE_R0; case MMC_RSP_R1: return SD_RSP_TYPE_R1; - case MMC_RSP_R1 & ~MMC_RSP_CRC: + case MMC_RSP_R1_NO_CRC: return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; case MMC_RSP_R1B: return SD_RSP_TYPE_R1b; From f7dd5462dac0447851079051e40af35d0af7579e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:47 +0200 Subject: [PATCH 58/68] mmc: rtsx_usb: use new macro for R1 without CRC Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_usb_sdmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 6c71fc9f76c7e..4106295527b9d 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -324,7 +324,7 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host, case MMC_RSP_R1: rsp_type = SD_RSP_TYPE_R1; break; - case MMC_RSP_R1 & ~MMC_RSP_CRC: + case MMC_RSP_R1_NO_CRC: rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; break; case MMC_RSP_R1B: From 0bc0b6e86524526c92a9409faea79d53db8e7e6e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:48 +0200 Subject: [PATCH 59/68] mmc: tmio: add eMMC support We need to add R1 without CRC support, refactor the bus width routine a little and extend a quirk check. To support "non-removable;" we need a workaround which will be hopefully removed when reworking PM soon. Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc.h | 3 +++ drivers/mmc/host/tmio_mmc_pio.c | 38 ++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 1f1cb2628f673..8e126afd988cc 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -79,6 +79,9 @@ #define CLK_CTL_DIV_MASK 0xff #define CLK_CTL_SCLKEN BIT(8) +#define CARD_OPT_WIDTH8 BIT(13) +#define CARD_OPT_WIDTH BIT(15) + #define TMIO_BBS 512 /* Boot block size */ /* Definitions for values the CTRL_SDIO_STATUS register can take. */ diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 192817850dffa..700567603107a 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -336,7 +336,9 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: c |= RESP_NONE; break; - case MMC_RSP_R1: c |= RESP_R1; break; + case MMC_RSP_R1: + case MMC_RSP_R1_NO_CRC: + c |= RESP_R1; break; case MMC_RSP_R1B: c |= RESP_R1B; break; case MMC_RSP_R2: c |= RESP_R2; break; case MMC_RSP_R3: c |= RESP_R3; break; @@ -730,12 +732,13 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host, pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n", data->blksz, data->blocks); - /* Some hardware cannot perform 2 byte requests in 4 bit mode */ - if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + /* Some hardware cannot perform 2 byte requests in 4/8 bit mode */ + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4 || + host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) { int blksz_2bytes = pdata->flags & TMIO_MMC_BLKSZ_2BYTES; if (data->blksz < 2 || (data->blksz < 4 && !blksz_2bytes)) { - pr_err("%s: %d byte block unsupported in 4 bit mode\n", + pr_err("%s: %d byte block unsupported in 4/8 bit mode\n", mmc_hostname(host->mmc), data->blksz); return -EINVAL; } @@ -857,14 +860,16 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host) static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host, unsigned char bus_width) { - switch (bus_width) { - case MMC_BUS_WIDTH_1: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); - break; - case MMC_BUS_WIDTH_4: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); - break; - } + u16 reg = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT) + & ~(CARD_OPT_WIDTH | CARD_OPT_WIDTH8); + + /* reg now applies to MMC_BUS_WIDTH_4 */ + if (bus_width == MMC_BUS_WIDTH_1) + reg |= CARD_OPT_WIDTH; + else if (bus_width == MMC_BUS_WIDTH_8) + reg |= CARD_OPT_WIDTH8; + + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, reg); } /* Set MMC clock / power. @@ -1082,6 +1087,15 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, !mmc_card_is_removable(mmc) || mmc->slot.cd_irq >= 0); + /* + * On Gen2+, eMMC with NONREMOVABLE currently fails because native + * hotplug gets disabled. It seems RuntimePM related yet we need further + * research. Since we are planning a PM overhaul anyway, let's enforce + * for now the device being active by enabling native hotplug always. + */ + if (pdata->flags & TMIO_MMC_MIN_RCAR2) + _host->native_hotplug = true; + if (tmio_mmc_clk_enable(_host) < 0) { mmc->f_max = pdata->hclk; mmc->f_min = mmc->f_max / 512; From ae0c12cc4e4b0405ffd48368f742ba0d5a01f7f1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 16 Sep 2016 10:01:41 -0500 Subject: [PATCH 60/68] dt: sdhci-of-arasan: Add device tree option xlnx, fails-without-test-cd The sdhci controller on xilinx zynq devices will not function unless the CD bit is provided. http://www.xilinx.com/support/answers/61064.html In cases where it is impossible to provide the CD bit in hardware, setting the controller to test mode and then setting inserted to true will get the controller to function without the CD bit. The device property "xlnx,fails-without-test-cd" will let the arasan driver know the controller does not have the CD line wired and that the controller does not function without it. Signed-off-by: Zach Brown Acked-by: Rob Herring Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt index 3404afa9b9384..49df630bd44ff 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt @@ -36,6 +36,9 @@ Optional Properties: - #clock-cells: If specified this should be the value <0>. With this property in place we will export a clock representing the Card Clock. This clock is expected to be consumed by our PHY. You must also specify + - xlnx,fails-without-test-cd: when present, the controller doesn't work when + the CD line is not connected properly, and the line is not connected + properly. Test mode can be used to force the controller to function. Example: sdhci@e0100000 { From 3794c542641f1a7a329f31c87927e2c5f5417670 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 16 Sep 2016 10:01:42 -0500 Subject: [PATCH 61/68] mmc: sdhci-of-arasan: Set controller to test mode when no CD bit The sdhci controller on xilinx zynq devices will not function unless the CD bit is provided. http://www.xilinx.com/support/answers/61064.html In cases where it is impossible to provide the CD bit in hardware, setting the controller to test mode and then setting inserted to true will get the controller to function without the CD bit. When the device has the property xlnx,fails-without-test-cd the driver changes the controller to test mode and sets test inserted to true to make the controller function. Signed-off-by: Zach Brown Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 27 ++++++++++++++++++++++++++- drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 33601a817ea67..da8e40af6f85e 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -26,6 +26,7 @@ #include #include #include "sdhci-pltfm.h" +#include #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 @@ -98,6 +99,10 @@ struct sdhci_arasan_data { struct regmap *soc_ctl_base; const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; + unsigned int quirks; /* Arasan deviations from spec */ + +/* Controller does not have CD wired and will not function normally without */ +#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) }; static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { @@ -245,12 +250,27 @@ static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc, writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER); } +void sdhci_arasan_reset(struct sdhci_host *host, u8 mask) +{ + u8 ctrl; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + + sdhci_reset(host, mask); + + if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) { + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + } +} + static struct sdhci_ops sdhci_arasan_ops = { .set_clock = sdhci_arasan_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_timeout_clock = sdhci_arasan_get_timeout_clock, .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_reset, + .reset = sdhci_arasan_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; @@ -545,6 +565,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; struct sdhci_arasan_data *sdhci_arasan; + struct device_node *np = pdev->dev.of_node; host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, sizeof(*sdhci_arasan)); @@ -599,6 +620,10 @@ static int sdhci_arasan_probe(struct platform_device *pdev) } sdhci_get_of_property(pdev); + + if (of_property_read_bool(np, "xlnx,fails-without-test-cd")) + sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST; + pltfm_host->clk = clk_xin; if (of_device_is_compatible(pdev->dev.of_node, diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index a2bc9e111a3ab..c722cd23205cd 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -84,6 +84,8 @@ #define SDHCI_CTRL_ADMA32 0x10 #define SDHCI_CTRL_ADMA64 0x18 #define SDHCI_CTRL_8BITBUS 0x20 +#define SDHCI_CTRL_CDTEST_INS 0x40 +#define SDHCI_CTRL_CDTEST_EN 0x80 #define SDHCI_POWER_CONTROL 0x29 #define SDHCI_POWER_ON 0x01 From 1712c9373f98ae8ed41599a8d7841a6fba29c264 Mon Sep 17 00:00:00 2001 From: Ziyuan Xu Date: Wed, 21 Sep 2016 09:43:49 +0800 Subject: [PATCH 62/68] mmc: core: don't try to switch block size for dual rate mode Per spec, block size should always be 512 bytes for dual rate mode, so any attempts to switch the block size under dual rate mode should be neglected. Signed-off-by: Ziyuan Xu Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f0ed0afe033dc..2553d903a82b9 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2576,7 +2576,8 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) { struct mmc_command cmd = {0}; - if (mmc_card_blockaddr(card) || mmc_card_ddr52(card)) + if (mmc_card_blockaddr(card) || mmc_card_ddr52(card) || + mmc_card_hs400(card) || mmc_card_hs400es(card)) return 0; cmd.opcode = MMC_SET_BLOCKLEN; From ec0baaa6b33932a25432e17e0bca8d96083caffa Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:36 +0800 Subject: [PATCH 63/68] mmc: dw_mmc: split out preparation of desc for IDMAC32 and IDMAC64 We intend to add more check for descriptors when preparing desc. Let's spilt out the separate body to make the dw_mci_translate_sglist not so lengthy. After spliting out these two functions, we could remove dw_mci_translate_sglist and call both of them when staring idmac. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 149 ++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 70 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index c23e252c037f7..ff9b0fcf83005 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -467,112 +467,121 @@ static void dw_mci_dmac_complete_dma(void *arg) } } -static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, - unsigned int sg_len) +static inline void dw_mci_prepare_desc64(struct dw_mci *host, + struct mmc_data *data, + unsigned int sg_len) { unsigned int desc_len; + struct idmac_desc_64addr *desc_first, *desc_last, *desc; int i; - if (host->dma_64bit_address == 1) { - struct idmac_desc_64addr *desc_first, *desc_last, *desc; - - desc_first = desc_last = desc = host->sg_cpu; + desc_first = desc_last = desc = host->sg_cpu; - for (i = 0; i < sg_len; i++) { - unsigned int length = sg_dma_len(&data->sg[i]); + for (i = 0; i < sg_len; i++) { + unsigned int length = sg_dma_len(&data->sg[i]); - u64 mem_addr = sg_dma_address(&data->sg[i]); + u64 mem_addr = sg_dma_address(&data->sg[i]); - for ( ; length ; desc++) { - desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? - length : DW_MCI_DESC_DATA_LENGTH; + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; - length -= desc_len; + length -= desc_len; - /* - * Set the OWN bit and disable interrupts - * for this descriptor - */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | - IDMAC_DES0_CH; + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; - /* Buffer length */ - IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); - /* Physical address to DMA to/from */ - desc->des4 = mem_addr & 0xffffffff; - desc->des5 = mem_addr >> 32; + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; - /* Update physical address for the next desc */ - mem_addr += desc_len; + /* Update physical address for the next desc */ + mem_addr += desc_len; - /* Save pointer to the last descriptor */ - desc_last = desc; - } + /* Save pointer to the last descriptor */ + desc_last = desc; } + } - /* Set first descriptor */ - desc_first->des0 |= IDMAC_DES0_FD; + /* Set first descriptor */ + desc_first->des0 |= IDMAC_DES0_FD; - /* Set last descriptor */ - desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc_last->des0 |= IDMAC_DES0_LD; + /* Set last descriptor */ + desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc_last->des0 |= IDMAC_DES0_LD; +} - } else { - struct idmac_desc *desc_first, *desc_last, *desc; - desc_first = desc_last = desc = host->sg_cpu; +static inline void dw_mci_prepare_desc32(struct dw_mci *host, + struct mmc_data *data, + unsigned int sg_len) +{ + unsigned int desc_len; + struct idmac_desc *desc_first, *desc_last, *desc; + int i; - for (i = 0; i < sg_len; i++) { - unsigned int length = sg_dma_len(&data->sg[i]); + desc_first = desc_last = desc = host->sg_cpu; - u32 mem_addr = sg_dma_address(&data->sg[i]); + for (i = 0; i < sg_len; i++) { + unsigned int length = sg_dma_len(&data->sg[i]); - for ( ; length ; desc++) { - desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? - length : DW_MCI_DESC_DATA_LENGTH; + u32 mem_addr = sg_dma_address(&data->sg[i]); - length -= desc_len; + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; - /* - * Set the OWN bit and disable interrupts - * for this descriptor - */ - desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | - IDMAC_DES0_DIC | - IDMAC_DES0_CH); + length -= desc_len; - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, desc_len); + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | + IDMAC_DES0_DIC | + IDMAC_DES0_CH); - /* Physical address to DMA to/from */ - desc->des2 = cpu_to_le32(mem_addr); + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, desc_len); - /* Update physical address for the next desc */ - mem_addr += desc_len; + /* Physical address to DMA to/from */ + desc->des2 = cpu_to_le32(mem_addr); - /* Save pointer to the last descriptor */ - desc_last = desc; - } - } - - /* Set first descriptor */ - desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); + /* Update physical address for the next desc */ + mem_addr += desc_len; - /* Set last descriptor */ - desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | - IDMAC_DES0_DIC)); - desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + /* Save pointer to the last descriptor */ + desc_last = desc; + } } - wmb(); /* drain writebuffer */ + /* Set first descriptor */ + desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); + + /* Set last descriptor */ + desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | + IDMAC_DES0_DIC)); + desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); } static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) { u32 temp; - dw_mci_translate_sglist(host, host->data, sg_len); + if (host->dma_64bit_address == 1) + dw_mci_prepare_desc64(host, host->data, sg_len); + else + dw_mci_prepare_desc32(host, host->data, sg_len); + + /* drain writebuffer */ + wmb(); /* Make sure to reset DMA in case we did PIO before this */ dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET); From 3b2a067b98b45f7a7dafe21c34a3ae744c697f0f Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:37 +0800 Subject: [PATCH 64/68] mmc: dw_mmc: avoid race condition of cpu and IDMAC We could see an obvious race condition by test that the former write operation by IDMAC aiming to clear OWN bit reach right after the later configuration of the same desc, which makes the IDMAC be in SUSPEND state as the OWN bit was cleared by the asynchronous write operation of IDMAC. The bug can be very easy reproduced on RK3288 or similar when we reduce the running rate of system buses and keep the CPU running faster. So as two separate masters, IDMAC and cpu write the same descriptor stored on the same address, and this should be protected by adding check of OWN bit before preparing new descriptors. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 208 +++++++++++++++++++++++--------------- 1 file changed, 129 insertions(+), 79 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index ff9b0fcf83005..38099bafcd4ce 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -467,12 +467,87 @@ static void dw_mci_dmac_complete_dma(void *arg) } } -static inline void dw_mci_prepare_desc64(struct dw_mci *host, +static int dw_mci_idmac_init(struct dw_mci *host) +{ + int i; + + if (host->dma_64bit_address == 1) { + struct idmac_desc_64addr *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr); + + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; + i++, p++) { + p->des6 = (host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) & 0xffffffff; + + p->des7 = (u64)(host->sg_dma + + (sizeof(struct idmac_desc_64addr) * + (i + 1))) >> 32; + /* Initialize reserved and buffer size fields to "0" */ + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } + + /* Set the last descriptor as the end-of-ring descriptor */ + p->des6 = host->sg_dma & 0xffffffff; + p->des7 = (u64)host->sg_dma >> 32; + p->des0 = IDMAC_DES0_ER; + + } else { + struct idmac_desc *p; + /* Number of descriptors in the ring buffer */ + host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); + + /* Forward link the descriptor list */ + for (i = 0, p = host->sg_cpu; + i < host->ring_size - 1; + i++, p++) { + p->des3 = cpu_to_le32(host->sg_dma + + (sizeof(struct idmac_desc) * (i + 1))); + p->des1 = 0; + } + + /* Set the last descriptor as the end-of-ring descriptor */ + p->des3 = cpu_to_le32(host->sg_dma); + p->des0 = cpu_to_le32(IDMAC_DES0_ER); + } + + dw_mci_idmac_reset(host); + + if (host->dma_64bit_address == 1) { + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS64, IDMAC_INT_CLR); + mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + + /* Set the descriptor base address */ + mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff); + mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32); + + } else { + /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS, IDMAC_INT_CLR); + mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + + /* Set the descriptor base address */ + mci_writel(host, DBADDR, host->sg_dma); + } + + return 0; +} + +static inline int dw_mci_prepare_desc64(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) { unsigned int desc_len; struct idmac_desc_64addr *desc_first, *desc_last, *desc; + unsigned long timeout; int i; desc_first = desc_last = desc = host->sg_cpu; @@ -488,6 +563,19 @@ static inline void dw_mci_prepare_desc64(struct dw_mci *host, length -= desc_len; + /* + * Wait for the former clear OWN bit operation + * of IDMAC to make sure that this descriptor + * isn't still owned by IDMAC as IDMAC's write + * ops and CPU's read ops are asynchronous. + */ + timeout = jiffies + msecs_to_jiffies(100); + while (readl(&desc->des0) & IDMAC_DES0_OWN) { + if (time_after(jiffies, timeout)) + goto err_own_bit; + udelay(10); + } + /* * Set the OWN bit and disable interrupts * for this descriptor @@ -516,15 +604,24 @@ static inline void dw_mci_prepare_desc64(struct dw_mci *host, /* Set last descriptor */ desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); desc_last->des0 |= IDMAC_DES0_LD; + + return 0; +err_own_bit: + /* restore the descriptor chain as it's polluted */ + dev_dbg(host->dev, "desciptor is still owned by IDMAC.\n"); + memset(host->sg_cpu, 0, PAGE_SIZE); + dw_mci_idmac_init(host); + return -EINVAL; } -static inline void dw_mci_prepare_desc32(struct dw_mci *host, +static inline int dw_mci_prepare_desc32(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) { unsigned int desc_len; struct idmac_desc *desc_first, *desc_last, *desc; + unsigned long timeout; int i; desc_first = desc_last = desc = host->sg_cpu; @@ -540,6 +637,20 @@ static inline void dw_mci_prepare_desc32(struct dw_mci *host, length -= desc_len; + /* + * Wait for the former clear OWN bit operation + * of IDMAC to make sure that this descriptor + * isn't still owned by IDMAC as IDMAC's write + * ops and CPU's read ops are asynchronous. + */ + timeout = jiffies + msecs_to_jiffies(100); + while (readl(&desc->des0) & + cpu_to_le32(IDMAC_DES0_OWN)) { + if (time_after(jiffies, timeout)) + goto err_own_bit; + udelay(10); + } + /* * Set the OWN bit and disable interrupts * for this descriptor @@ -569,16 +680,28 @@ static inline void dw_mci_prepare_desc32(struct dw_mci *host, desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + + return 0; +err_own_bit: + /* restore the descriptor chain as it's polluted */ + dev_dbg(host->dev, "desciptor is still owned by IDMAC.\n"); + memset(host->sg_cpu, 0, PAGE_SIZE); + dw_mci_idmac_init(host); + return -EINVAL; } static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) { u32 temp; + int ret; if (host->dma_64bit_address == 1) - dw_mci_prepare_desc64(host, host->data, sg_len); + ret = dw_mci_prepare_desc64(host, host->data, sg_len); else - dw_mci_prepare_desc32(host, host->data, sg_len); + ret = dw_mci_prepare_desc32(host, host->data, sg_len); + + if (ret) + goto out; /* drain writebuffer */ wmb(); @@ -603,81 +726,8 @@ static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) /* Start it running */ mci_writel(host, PLDMND, 1); - return 0; -} - -static int dw_mci_idmac_init(struct dw_mci *host) -{ - int i; - - if (host->dma_64bit_address == 1) { - struct idmac_desc_64addr *p; - /* Number of descriptors in the ring buffer */ - host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr); - - /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; - i++, p++) { - p->des6 = (host->sg_dma + - (sizeof(struct idmac_desc_64addr) * - (i + 1))) & 0xffffffff; - - p->des7 = (u64)(host->sg_dma + - (sizeof(struct idmac_desc_64addr) * - (i + 1))) >> 32; - /* Initialize reserved and buffer size fields to "0" */ - p->des1 = 0; - p->des2 = 0; - p->des3 = 0; - } - - /* Set the last descriptor as the end-of-ring descriptor */ - p->des6 = host->sg_dma & 0xffffffff; - p->des7 = (u64)host->sg_dma >> 32; - p->des0 = IDMAC_DES0_ER; - - } else { - struct idmac_desc *p; - /* Number of descriptors in the ring buffer */ - host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); - - /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; - i < host->ring_size - 1; - i++, p++) { - p->des3 = cpu_to_le32(host->sg_dma + - (sizeof(struct idmac_desc) * (i + 1))); - p->des1 = 0; - } - - /* Set the last descriptor as the end-of-ring descriptor */ - p->des3 = cpu_to_le32(host->sg_dma); - p->des0 = cpu_to_le32(IDMAC_DES0_ER); - } - - dw_mci_idmac_reset(host); - - if (host->dma_64bit_address == 1) { - /* Mask out interrupts - get Tx & Rx complete only */ - mci_writel(host, IDSTS64, IDMAC_INT_CLR); - mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI | - SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); - - /* Set the descriptor base address */ - mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff); - mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32); - - } else { - /* Mask out interrupts - get Tx & Rx complete only */ - mci_writel(host, IDSTS, IDMAC_INT_CLR); - mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | - SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); - - /* Set the descriptor base address */ - mci_writel(host, DBADDR, host->sg_dma); - } - - return 0; +out: + return ret; } static const struct dw_mci_dma_ops dw_mci_idmac_ops = { From d12d0cb1d7dc00605112bf1d5dcc157f2908a068 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:38 +0800 Subject: [PATCH 65/68] mmc: dw_mmc: fix misleading error print if failing to do DMA transfer The original log didn't figure out that we could still finish this transfer by PIO mode even if failing to use DMA. And it should be kept for debug level instead of error one. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 38099bafcd4ce..c59a7b5c69a1f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1057,8 +1057,10 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) spin_unlock_irqrestore(&host->irq_lock, irqflags); if (host->dma_ops->start(host, sg_len)) { - /* We can't do DMA */ - dev_err(host->dev, "%s: failed to start DMA.\n", __func__); + /* We can't do DMA, try PIO for this one */ + dev_dbg(host->dev, + "%s: fall back to PIO mode for current transfer\n", + __func__); return -ENODEV; } From cc190d4c6499b1b3fca6693866df62cad18ed833 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:39 +0800 Subject: [PATCH 66/68] mmc: dw_mmc: use macro to define ring buffer size It's very prone to make mistake as we might forget to replace all PAGE_SIZEs with new values if we try to modify the ring buffer size for whatever reasons. Let's use a macro to define it. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index c59a7b5c69a1f..84cf60e6390ca 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -61,6 +61,8 @@ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ SDMMC_IDMAC_INT_TI) +#define DESC_RING_BUF_SZ PAGE_SIZE + struct idmac_desc_64addr { u32 des0; /* Control Descriptor */ @@ -474,7 +476,8 @@ static int dw_mci_idmac_init(struct dw_mci *host) if (host->dma_64bit_address == 1) { struct idmac_desc_64addr *p; /* Number of descriptors in the ring buffer */ - host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr); + host->ring_size = + DESC_RING_BUF_SZ / sizeof(struct idmac_desc_64addr); /* Forward link the descriptor list */ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; @@ -500,7 +503,8 @@ static int dw_mci_idmac_init(struct dw_mci *host) } else { struct idmac_desc *p; /* Number of descriptors in the ring buffer */ - host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); + host->ring_size = + DESC_RING_BUF_SZ / sizeof(struct idmac_desc); /* Forward link the descriptor list */ for (i = 0, p = host->sg_cpu; @@ -609,7 +613,7 @@ static inline int dw_mci_prepare_desc64(struct dw_mci *host, err_own_bit: /* restore the descriptor chain as it's polluted */ dev_dbg(host->dev, "desciptor is still owned by IDMAC.\n"); - memset(host->sg_cpu, 0, PAGE_SIZE); + memset(host->sg_cpu, 0, DESC_RING_BUF_SZ); dw_mci_idmac_init(host); return -EINVAL; } @@ -685,7 +689,7 @@ static inline int dw_mci_prepare_desc32(struct dw_mci *host, err_own_bit: /* restore the descriptor chain as it's polluted */ dev_dbg(host->dev, "desciptor is still owned by IDMAC.\n"); - memset(host->sg_cpu, 0, PAGE_SIZE); + memset(host->sg_cpu, 0, DESC_RING_BUF_SZ); dw_mci_idmac_init(host); return -EINVAL; } @@ -2754,7 +2758,8 @@ static void dw_mci_init_dma(struct dw_mci *host) } /* Alloc memory for sg translation */ - host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, + host->sg_cpu = dmam_alloc_coherent(host->dev, + DESC_RING_BUF_SZ, &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { dev_err(host->dev, From 207535698e7dbbeebddce2dbe9dfbecb55b4e999 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 21 Sep 2016 10:40:25 +0800 Subject: [PATCH 67/68] mmc: dw_mmc: minor cleanup for dw_mci_adjust_fifoth msize and rx_wmark are properly initialized, we dont't need to assign them again. Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 84cf60e6390ca..8ee1c86b6eff4 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -939,11 +939,8 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) * MSIZE is '1', * if blksz is not a multiple of the FIFO width */ - if (blksz % fifo_width) { - msize = 0; - rx_wmark = 1; + if (blksz % fifo_width) goto done; - } do { if (!((blksz_depth % mszs[idx]) || From 0f75c404503cc49cbe92555fbab80a584c1f4ae2 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 28 Jul 2016 18:55:27 +0900 Subject: [PATCH 68/68] mmc: dw_mmc: remove the deprecated "supports-highspeed" property Remvoe the deprecated "supports-highspeed" property. DWMMC controller will not use this property anymore. Signed-off-by: Jaehoon Chung Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8ee1c86b6eff4..4fcbc4012ed03 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2964,11 +2964,6 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(ret); } - if (of_find_property(np, "supports-highspeed", NULL)) { - dev_info(dev, "supports-highspeed property is deprecated.\n"); - pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; - } - return pdata; }