From 4c5f4bf412eabc355b9c2bc99f8283ca0bd7d6ef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:44 +0200 Subject: 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 diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 2ee4c21..d7dadb5 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); -- cgit v0.10.2 From 86a93317ed71f9ad8bca7baff12930c47c235f80 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:45 +0200 Subject: 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 diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index d7dadb5..84bbf43 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; -- cgit v0.10.2 From f2cecb70941c8d4a9445ee85926202f7157e1222 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:46 +0200 Subject: 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 diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 84bbf43..af30d87 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); } -- cgit v0.10.2 From b465646ef41f2f8a397f42a956d9788e898185d7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:47 +0200 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt index 4bf41d8..904ff9f 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 af30d87..2ec91ce 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,11 +981,16 @@ 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; -- cgit v0.10.2 From 57af711d79bc196896e1e54c78330f7c92edc21c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 16:25:48 +0200 Subject: 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 diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index 1867af2..0d24f10 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 bd0c476..94cf5a1 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 7e05e09..e3b196e 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 fdf9fdb..8a95e36 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>, -- cgit v0.10.2 From 4cbc6dbd52516363c61ad8f116355ad63fe9cdf2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 30 Jul 2016 17:03:50 +0200 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 22d1e1f..ac7b6a1 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 -------- -- cgit v0.10.2 From 721e0497172f0fa661eed2d63367cddf479f35e8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Aug 2016 21:02:38 +0200 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt index ce0e767..e254368 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 450d907..1304160 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; -- cgit v0.10.2 From 41f469cac2663a41a7b0c84cb94e8f7024385ae4 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 25 Jul 2016 19:59:23 +0200 Subject: 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 diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 79905ce..bbad309 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); -- cgit v0.10.2 From 66fe6ac5d5e120b85c2ed310eb25b15a77c110c0 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 28 Jul 2016 16:17:44 +0000 Subject: 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 diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index e5c634b..51dd2fd 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; } -- cgit v0.10.2 From 2f7649f5048cfca9c236a8120c51ed762569a5e8 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 1 Aug 2016 10:22:11 +0100 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index ac7b6a1..8a37782 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: -------------------- -- cgit v0.10.2 From 150d4240254be03aafb2a86c5b1a4b41665bf1b1 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 5 Aug 2016 10:56:46 +0200 Subject: 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 diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 239be2f..fb71c86 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 -- cgit v0.10.2 From d51c50525f0a4c6b58fdd493a933017c62d98731 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 11 Aug 2016 23:03:53 +0200 Subject: 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 diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 1e819f9..bb3e0d1 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 */ -- cgit v0.10.2 From 5275a652d296711aaf7f2f4173c8db153e5777c3 Mon Sep 17 00:00:00 2001 From: Uri Yanai Date: Sun, 14 Aug 2016 11:46:36 +0300 Subject: =?UTF-8?q?mmc:=20sd:=20Export=20SD=20Status=20via=20=E2=80=9Cssr?= =?UTF-8?q?=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 diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 0123936..73c762a 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 d8673ca..73fad83 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 */ -- cgit v0.10.2 From 4ad90cf9a8c62ca247917eb1ea1560a4a13a0731 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 15 Aug 2016 07:32:45 +0200 Subject: 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 diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index cce10fe..609ba5b 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), }, -- cgit v0.10.2 From a215186d7f057a5ff159824b9f869f835c69e952 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 15 Aug 2016 16:19:38 +0800 Subject: 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 diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 99e0b33..437c448 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); } -- cgit v0.10.2 From 2fb0b02b79d1c5ca88e223750a2eb9b0a3679bb0 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 15 Aug 2016 16:31:33 +0800 Subject: 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 diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 437c448..1f54fd8 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) -- cgit v0.10.2 From 622b5f35dab374138cfc8ab76d0fcf6b342eb744 Mon Sep 17 00:00:00 2001 From: Christopher Freeman Date: Wed, 17 Aug 2016 13:34:27 -0400 Subject: 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 diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cd65d47..0851a4b 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); -- cgit v0.10.2 From b2db9c6743f2622f279af3a022a47a08e6e6f2c9 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 18 Aug 2016 10:26:38 -0700 Subject: 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 diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index e0f193f..0b3a9cf 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; } @@ -565,9 +600,6 @@ static int sdhci_arasan_probe(struct platform_device *pdev) 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: sdhci_arasan_unregister_sdclk(&pdev->dev); @@ -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); } -- cgit v0.10.2 From 63311bece02abad40ae442e6deba2a2e522949fb Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 23 Aug 2016 10:51:04 +0200 Subject: 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 diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 2ec91ce..142ab3f 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; } -- cgit v0.10.2 From 2dd110b27d6621e881c796d1aea97da1b3143a5d Mon Sep 17 00:00:00 2001 From: Michael Weiser Date: Mon, 22 Aug 2016 18:42:18 +0200 Subject: 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 diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 142ab3f..5fd4c5f 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; /* -- cgit v0.10.2 From 6a4679f312357ac7c74c0e1b996efdd1d0a612fa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 24 Aug 2016 11:34:37 +0200 Subject: 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 diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index c3b651b..c4e63b7 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 7f63ec0..1f1cb26 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 92467ef..1928178 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; -- cgit v0.10.2 From ff026099d775c74548839f1d627eb0b2bec0b857 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 24 Aug 2016 11:34:38 +0200 Subject: 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") diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index c4e63b7..d679c8a 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 */ -- cgit v0.10.2 From 67d35960f445176d5548c660a1b1708cad1c2042 Mon Sep 17 00:00:00 2001 From: Jungseung Lee Date: Wed, 24 Aug 2016 19:34:09 +0900 Subject: 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 diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f2d185c..3486bc7 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) -- cgit v0.10.2 From 96e52daa508bf349582e41911da66461b54dcc12 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 26 Aug 2016 08:49:55 +0800 Subject: 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 diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 2206d44..74c7625 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) { -- cgit v0.10.2 From 923dff87373708801f501cbe8993df98a91b566e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 26 Aug 2016 08:49:56 +0800 Subject: 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 diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 78cb4d5..406e5f0 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 34f6e80..90fe554 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 */ -- cgit v0.10.2 From c10bc3722378e5b387880c3c8baf19efc9676fe2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Aug 2016 14:59:13 +0300 Subject: 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 diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 897cfd2..93bb0ff 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 7e07887..9c7c08b 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; -- cgit v0.10.2 From 92add82e0ce0946d7aa6f2ae71cd160d914d2f48 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 30 Aug 2016 09:23:12 +0200 Subject: 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 diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 1d17dcf..ad49bfa 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 0411c9f..a2bc9e1 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, -- cgit v0.10.2 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: 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 diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 74c7625..03670aa5 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2303,7 +2303,8 @@ again: 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; -- cgit v0.10.2 From 941a659ffdefe31af8222451ca2a74ebab8d98dd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 14 Jul 2016 15:22:27 +0200 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index da0ef17..7ab3d74 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; -- cgit v0.10.2 From 00f400b73b8deba86d75804e990527b0bab21149 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 15 Jul 2016 09:47:34 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 767af20..cea26ab 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; -- cgit v0.10.2 From 7037f3beae352faa478ff5729c308756233ef4d3 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Fri, 15 Jul 2016 10:54:08 +0900 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index cea26ab..1a1e1e1 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3151,12 +3151,10 @@ err_dmaunmap: 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); -- cgit v0.10.2 From fdc22b6b1f259d939ace88dff21f167e79eadd67 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 12 Aug 2016 16:51:25 +0800 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt index 8636f5a..4e00e85 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. -- cgit v0.10.2 From d6786fefe816ba60c794f8a41a73b0dd3a4df097 Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Fri, 12 Aug 2016 16:51:26 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1a1e1e1..e7eef75 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 @@ err_dmaunmap: 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 83b0edfc..f5af2bd 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; }; -- cgit v0.10.2 From e7a1dec19c09973c8c994a5b895627e0c7082b17 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 22 Aug 2016 10:57:16 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e7eef75..c23e252 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); -- cgit v0.10.2 From 3203a82724b86cc221e63a519313544d5f192289 Mon Sep 17 00:00:00 2001 From: Jin Guojun Date: Thu, 4 Aug 2016 10:16:13 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 8e9d886..6247894 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[] = { -- cgit v0.10.2 From 2e57bbe22c15916a020c4563ebcad5c848c0183c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 29 Aug 2016 12:33:39 +0300 Subject: 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 diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 93bb0ff..40f4fe8 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; } -- cgit v0.10.2 From d55655773ab23d170b4295666f7feff976124ae3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 29 Aug 2016 12:33:40 +0300 Subject: 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 diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 40f4fe8..d38d701 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; -- cgit v0.10.2 From 4674b6c87019ba8e3e53c91712fff07b93f4efa7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 29 Aug 2016 12:33:41 +0300 Subject: 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 diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index d38d701..3833c5f 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; -- cgit v0.10.2 From b2ca77c98390304722c2baf289b181d6f0fa3c49 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 26 Aug 2016 16:51:15 +0800 Subject: 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 diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 0b3a9cf..33601a8 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, }; @@ -379,6 +382,45 @@ static const struct clk_ops arasan_sdcardclk_ops = { }; /** + * 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 * * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This @@ -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); -- cgit v0.10.2 From 4ae12588e028f66a505b2287e8237a1815ee31a3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 1 Sep 2016 13:46:17 +0200 Subject: 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 diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 1e93dc4..20b6ff5 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 }, -- cgit v0.10.2 From 12182affc74a8ce1e95bd4feeb1638c55d2ab6fd Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jul 2016 00:04:03 +0200 Subject: 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 diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e55cde6..59b452d 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); -- cgit v0.10.2 From 7428e0bf7e87627af880b013f5f4648f07f70842 Mon Sep 17 00:00:00 2001 From: Ai Kyuse Date: Tue, 6 Sep 2016 12:38:38 +0200 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt index 0f610d4..13df9c2 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 d679c8a..49edff7 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); -- cgit v0.10.2 From 902a8a0b50d7d13cf88211d0bde3a7c27fd98fa5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 Sep 2016 14:56:09 +0200 Subject: 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 diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index a56373c..8fa478c 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, -- cgit v0.10.2 From 71085123d27dc5d28ce523344f32ac0d20c5f0a5 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 7 Sep 2016 10:38:24 +0800 Subject: 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 diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 59b452d..9ce6c25 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2205,6 +2205,36 @@ out: 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; -- cgit v0.10.2 From 6c689886fbe41b6492bd8ee9334ff66893274810 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 7 Sep 2016 10:38:25 +0800 Subject: 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 diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9ce6c25..d083d2e 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; -- cgit v0.10.2 From 3d254b54f743c19f137034163cc8d81e2a6f2858 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 5 Aug 2016 04:57:14 +0200 Subject: Documentation: dt: Add new compatible to sunxi mmc driver bindings Signed-off-by: Icenowy Zheng Signed-off-by: Ulf Hansson diff --git a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt index 904ff9f..55cdd80 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" -- cgit v0.10.2 From e1b8dfd1b1c61b3fde3622a2f244a008faca2916 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 5 Aug 2016 04:57:15 +0200 Subject: 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 diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 5fd4c5f..c0a5c67 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); -- cgit v0.10.2 From 5a3ab2815c6a3772e9c70e1b3f778f1a23fc8671 Mon Sep 17 00:00:00 2001 From: Jaedon Shin Date: Fri, 9 Sep 2016 11:08:39 +0900 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt deleted file mode 100644 index 8284717..0000000 --- a/Documentation/devicetree/bindings/mmc/brcm,bcm7425-sdhci.txt +++ /dev/null @@ -1,36 +0,0 @@ -* BROADCOM BRCMSTB/BMIPS SDHCI Controller - -This file documents differences between the core properties in mmc.txt -and the properties used by the sdhci-brcmstb driver. - -NOTE: The driver disables all UHS speed modes by default and depends -on Device Tree properties to enable them for SoC/Board combinations -that support them. - -Required properties: -- compatible: "brcm,bcm7425-sdhci" - -Refer to clocks/clock-bindings.txt for generic clock consumer properties. - -Example: - - sdhci@f03e0100 { - compatible = "brcm,bcm7425-sdhci"; - reg = <0xf03e0000 0x100>; - interrupts = <0x0 0x26 0x0>; - sdhci,auto-cmd12; - clocks = <&sw_sdio>; - sd-uhs-sdr50; - sd-uhs-ddr50; - }; - - sdhci@f03e0300 { - non-removable; - bus-width = <0x8>; - compatible = "brcm,bcm7425-sdhci"; - reg = <0xf03e0200 0x100>; - interrupts = <0x0 0x27 0x0>; - sdhci,auto-cmd12; - clocks = ; - mmc-hs200-1_8v; - }; diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt new file mode 100644 index 0000000..733b64a --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.txt @@ -0,0 +1,38 @@ +* BROADCOM BRCMSTB/BMIPS SDHCI Controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the sdhci-brcmstb driver. + +NOTE: The driver disables all UHS speed modes by default and depends +on Device Tree properties to enable them for SoC/Board combinations +that support them. + +Required properties: +- compatible: should be one of the following + - "brcm,bcm7425-sdhci" + - "brcm,bcm7445-sdhci" + +Refer to clocks/clock-bindings.txt for generic clock consumer properties. + +Example: + + sdhci@f03e0100 { + compatible = "brcm,bcm7425-sdhci"; + reg = <0xf03e0000 0x100>; + interrupts = <0x0 0x26 0x0>; + sdhci,auto-cmd12; + clocks = <&sw_sdio>; + sd-uhs-sdr50; + sd-uhs-ddr50; + }; + + sdhci@f03e0300 { + non-removable; + bus-width = <0x8>; + compatible = "brcm,bcm7425-sdhci"; + reg = <0xf03e0200 0x100>; + interrupts = <0x0 0x27 0x0>; + sdhci,auto-cmd12; + clocks = ; + mmc-hs200-1_8v; + }; -- cgit v0.10.2 From 6a3d8ced09482bd5b0e831740d83ed722aa2c5e6 Mon Sep 17 00:00:00 2001 From: Jaedon Shin Date: Fri, 9 Sep 2016 11:08:40 +0900 Subject: 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 diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 609ba5b..159f6f6 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 @@ err_clk: 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); -- cgit v0.10.2 From 5163af5a5e2e69c9a5a854b92ffa7e2f7672dbf7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:11 +0300 Subject: 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 diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d083d2e..f0ed0af 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 b01e77d..368bed7 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 aa4bfbf..0b24394 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) { -- cgit v0.10.2 From 4bbb9aac9a9a6cd4b2718e43f998c5e4e3b382cd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:12 +0300 Subject: 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 diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index c032eef..5a8dc5a 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); -- cgit v0.10.2 From 20845beff7d74f2021a60abe3789bb9c8881c2c1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:13 +0300 Subject: 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 diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0851a4b..4805566 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. -- cgit v0.10.2 From 32828857dba32c04e7383ba47db19e3bf37fce9d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:14 +0300 Subject: 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 diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 3833c5f..72a1f1f 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; -- cgit v0.10.2 From c80f275fa1766cfdbc24c7a5d3d870df1fe730db Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 16 Aug 2016 13:44:15 +0300 Subject: 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 diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 8fe0756..81d4dc0 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, -- cgit v0.10.2 From 29eb7bd01e80df316ab9d1da1a4ee580fae89188 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Sep 2016 11:34:38 +0200 Subject: 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 diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 03670aa5..c333511 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 @@ again: 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 0000000..cdabb2e --- /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 7080572..8037f73 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 fee5e12..3c15a75 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]; -- cgit v0.10.2 From 51b50c961676428cd356d6fa494a2e9f53dc77bf Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:45 +0200 Subject: 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 diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 368bed7..2b953eb 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)) /* -- cgit v0.10.2 From 8c8d0ecbd87fff5cfca0a9a72fa388c3f61dab8f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:46 +0200 Subject: mmc: rtsx_pci: use new macro for R1 without CRC Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 396c9b7..3ccaa14 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; -- cgit v0.10.2 From f7dd5462dac0447851079051e40af35d0af7579e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:47 +0200 Subject: mmc: rtsx_usb: use new macro for R1 without CRC Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 6c71fc9..4106295 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: -- cgit v0.10.2 From 0bc0b6e86524526c92a9409faea79d53db8e7e6e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 19 Sep 2016 22:57:48 +0200 Subject: 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 diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 1f1cb26..8e126af 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 1928178..7005676 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; -- cgit v0.10.2 From ae0c12cc4e4b0405ffd48368f742ba0d5a01f7f1 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 16 Sep 2016 10:01:41 -0500 Subject: 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 diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt index 3404afa..49df630 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 { -- cgit v0.10.2 From 3794c542641f1a7a329f31c87927e2c5f5417670 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 16 Sep 2016 10:01:42 -0500 Subject: 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 diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 33601a8..da8e40a 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 a2bc9e1..c722cd2 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 -- cgit v0.10.2 From 1712c9373f98ae8ed41599a8d7841a6fba29c264 Mon Sep 17 00:00:00 2001 From: Ziyuan Xu Date: Wed, 21 Sep 2016 09:43:49 +0800 Subject: 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 diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f0ed0af..2553d90 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; -- cgit v0.10.2 From ec0baaa6b33932a25432e17e0bca8d96083caffa Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:36 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index c23e252..ff9b0fc 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); -- cgit v0.10.2 From 3b2a067b98b45f7a7dafe21c34a3ae744c697f0f Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:37 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index ff9b0fc..38099ba 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; @@ -489,6 +564,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; @@ -541,6 +638,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 = { -- cgit v0.10.2 From d12d0cb1d7dc00605112bf1d5dcc157f2908a068 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:38 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 38099ba..c59a7b5 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; } -- cgit v0.10.2 From cc190d4c6499b1b3fca6693866df62cad18ed833 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 2 Sep 2016 12:14:39 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index c59a7b5..84cf60e 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, -- cgit v0.10.2 From 207535698e7dbbeebddce2dbe9dfbecb55b4e999 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 21 Sep 2016 10:40:25 +0800 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 84cf60e..8ee1c86 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]) || -- cgit v0.10.2 From 0f75c404503cc49cbe92555fbab80a584c1f4ae2 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 28 Jul 2016 18:55:27 +0900 Subject: 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 diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8ee1c86..4fcbc40 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; } -- cgit v0.10.2