Discussion:
[PATCH 03/16] mmc: host: omap_hsmmc: Remove incorrect voltage switch sequence
Kishon Vijay Abraham I
2017-06-16 12:45:31 UTC
Permalink
ios->vdd is set only in mmc_power_up and mmc_power_off and not in
mmc_select_voltage() as mentioned in the code comment. This seems to be
legacy code that has been carried for a long time without being
tested.

This will be replaced with the correct voltage switch sequence and
populated in start_signal_voltage_switch ops to be used by mmc core
for switching voltages.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 79 -------------------------------------------
1 file changed, 79 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c9ea41bb4e3a..65330d2263ad 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1230,67 +1230,6 @@ static void set_sd_bus_power(struct omap_hsmmc_host *host)
}
}

-/*
- * Switch MMC interface voltage ... only relevant for MMC1.
- *
- * MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver.
- * The MMC2 transceiver controls are used instead of DAT4..DAT7.
- * Some chips, like eMMC ones, use internal transceivers.
- */
-static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
-{
- u32 reg_val = 0;
- int ret;
-
- /* Disable the clocks */
- if (host->dbclk)
- clk_disable_unprepare(host->dbclk);
-
- /* Turn the power off */
- ret = omap_hsmmc_set_power(host, 0, 0);
-
- /* Turn the power ON with given VDD 1.8 or 3.0v */
- if (!ret)
- ret = omap_hsmmc_set_power(host, 1, vdd);
- if (host->dbclk)
- clk_prepare_enable(host->dbclk);
-
- if (ret != 0)
- goto err;
-
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
- reg_val = OMAP_HSMMC_READ(host->base, HCTL);
-
- /*
- * If a MMC dual voltage card is detected, the set_ios fn calls
- * this fn with VDD bit set for 1.8V. Upon card removal from the
- * slot, omap_hsmmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
- *
- * Cope with a bit of slop in the range ... per data sheets:
- * - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max,
- * but recommended values are 1.71V to 1.89V
- * - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max,
- * but recommended values are 2.7V to 3.3V
- *
- * Board setup code shouldn't permit anything very out-of-range.
- * TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the
- * middle range) but VSIM can't power DAT4..DAT7 at more than 3V.
- */
- if ((1 << vdd) <= MMC_VDD_23_24)
- reg_val |= SDVS18;
- else
- reg_val |= SDVS30;
-
- OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
- set_sd_bus_power(host);
-
- return 0;
-err:
- dev_err(mmc_dev(host->mmc), "Unable to switch operating voltage\n");
- return ret;
-}
-
/* Protect the card while the cover is open */
static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
{
@@ -1664,24 +1603,6 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

omap_hsmmc_set_bus_width(host);

- if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
- /* Only MMC1 can interface at 3V without some flavor
- * of external transceiver; but they all handle 1.8V.
- */
- if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
- (ios->vdd == DUAL_VOLT_OCR_BIT)) {
- /*
- * The mmc_select_voltage fn of the core does
- * not seem to set the power_mode to
- * MMC_POWER_UP upon recalculating the voltage.
- * vdd 1.8v.
- */
- if (omap_hsmmc_switch_opcond(host, ios->vdd) != 0)
- dev_dbg(mmc_dev(host->mmc),
- "Switch operation failed\n");
- }
- }
-
omap_hsmmc_set_clock(host);

if (do_send_init_stream)
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:35 UTC
Permalink
Now that vmmc regulator is made optional, do not bail out if vmmc
regulator is not found.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 7 -------
1 file changed, 7 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index f64148b119df..f1d5e8385591 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -415,13 +415,6 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
if (mmc_pdata(host)->set_power)
return mmc_pdata(host)->set_power(host->dev, power_on, iov);

- /*
- * If we don't see a Vcc regulator, assume it's a fixed
- * voltage always-on regulator.
- */
- if (IS_ERR(mmc->supply.vmmc))
- return 0;
-
if (mmc_pdata(host)->before_set_reg)
mmc_pdata(host)->before_set_reg(host->dev, power_on, iov);
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:37 UTC
Permalink
No functional change. Add separate case statements for certain timing
like MMC_TIMING_SD_HS and MMC_TIMING_MMC_HS even though AC12_UHSMC_RES
has to be written to the AC12 register (same as for default modes).
Also have separate case sections for MMC_TIMING_UHS_SDR104 and
MMC_TIMING_UHS_HS200 even though the same UHSMC value has to be written
to the AC12 register. This is in preparation for setting iodelay
values.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 3676ffa72929..d797224152ac 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1726,6 +1726,8 @@ static void omap_hsmmc_set_timing(struct omap_hsmmc_host *host)
val &= ~AC12_UHSMC_MASK;
switch (ios->timing) {
case MMC_TIMING_UHS_SDR104:
+ val |= AC12_UHSMC_SDR104;
+ break;
case MMC_TIMING_MMC_HS200:
val |= AC12_UHSMC_SDR104;
break;
@@ -1741,6 +1743,13 @@ static void omap_hsmmc_set_timing(struct omap_hsmmc_host *host)
case MMC_TIMING_UHS_SDR12:
val |= AC12_UHSMC_SDR12;
break;
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_MMC_HS:
+ val |= AC12_UHSMC_RES;
+ break;
+ case MMC_TIMING_MMC_DDR52:
+ val |= AC12_UHSMC_RES;
+ break;
default:
val |= AC12_UHSMC_RES;
break;
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:39 UTC
Permalink
Fix the error path sequence so that clk_disable, runtime_disable etc
are done in the reverse order of how they were enabled.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 9d59fe611498..967b7c7d4bd5 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2591,16 +2591,16 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
err_slot_name:
mmc_remove_host(mmc);
err_irq:
- device_init_wakeup(&pdev->dev, false);
if (!IS_ERR_OR_NULL(host->tx_chan))
dma_release_channel(host->tx_chan);
if (!IS_ERR_OR_NULL(host->rx_chan))
dma_release_channel(host->rx_chan);
+ if (host->dbclk)
+ clk_disable_unprepare(host->dbclk);
pm_runtime_dont_use_autosuspend(host->dev);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
- if (host->dbclk)
- clk_disable_unprepare(host->dbclk);
+ device_init_wakeup(&pdev->dev, false);
err1:
err_gpio:
mmc_free_host(mmc);
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:38 UTC
Permalink
Add a new compatible string "ti,dra7-hsmmc" to support
dra7 and dra72 controllers. Also create a new controller flag
"OMAP_HSMMC_REQUIRE_IODELAY" to specify all controllers that use
"ti,dra7-hsmmc" require iodealy configuration to be set.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
Acked-by: Rob Herring <***@kernel.org>
---
Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt | 1 +
drivers/mmc/host/omap_hsmmc.c | 10 ++++++++++
include/linux/platform_data/hsmmc-omap.h | 1 +
3 files changed, 12 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
index 74166a0d460d..258e25af10f7 100644
--- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
+++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
@@ -12,6 +12,7 @@ Required properties:
Should be "ti,omap3-hsmmc", for OMAP3 controllers
Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0
Should be "ti,omap4-hsmmc", for OMAP4 controllers
+ Should be "ti,dra7-hsmmc", for dra7 and dra72 controllers
Should be "ti,am33xx-hsmmc", for AM335x controllers
- ti,hwmods: Must be "mmc<n>", n is controller instance starting 1

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index d797224152ac..9d59fe611498 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2302,6 +2302,12 @@ static const struct omap_mmc_of_data am33xx_mmc_of_data = {
.controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
};

+static const struct omap_mmc_of_data dra7_mmc_of_data = {
+ .reg_offset = 0x100,
+ .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING |
+ OMAP_HSMMC_REQUIRE_IODELAY,
+};
+
static const struct of_device_id omap_mmc_of_match[] = {
{
.compatible = "ti,omap2-hsmmc",
@@ -2321,6 +2327,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
.compatible = "ti,am33xx-hsmmc",
.data = &am33xx_mmc_of_data,
},
+ {
+ .compatible = "ti,dra7-hsmmc",
+ .data = &dra7_mmc_of_data,
+ },
{},
};
MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
diff --git a/include/linux/platform_data/hsmmc-omap.h b/include/linux/platform_data/hsmmc-omap.h
index 67bded22eae4..8e771851e07a 100644
--- a/include/linux/platform_data/hsmmc-omap.h
+++ b/include/linux/platform_data/hsmmc-omap.h
@@ -27,6 +27,7 @@
#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
+#define OMAP_HSMMC_REQUIRE_IODELAY BIT(3)

struct omap_hsmmc_dev_attr {
u8 flags;
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:43 UTC
Permalink
Use the new compatible string "ti,dra7-hsmmc" that was specifically
added for dra7 and dra72. This is required since for dra7 and dra72
processors iodelay values has to be set unlike other processors.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
arch/arm/boot/dts/dra7.dtsi | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index b9b8b4b5143a..52ce3fab483f 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -1035,7 +1035,7 @@
};

mmc1: ***@4809c000 {
- compatible = "ti,omap4-hsmmc";
+ compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc";
reg = <0x4809c000 0x400>;
interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "mmc1";
@@ -1049,7 +1049,7 @@
};

mmc2: ***@480b4000 {
- compatible = "ti,omap4-hsmmc";
+ compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc";
reg = <0x480b4000 0x400>;
interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "mmc2";
@@ -1061,7 +1061,7 @@
};

mmc3: ***@480ad000 {
- compatible = "ti,omap4-hsmmc";
+ compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc";
reg = <0x480ad000 0x400>;
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "mmc3";
@@ -1074,7 +1074,7 @@
};

mmc4: ***@480d1000 {
- compatible = "ti,omap4-hsmmc";
+ compatible = "ti,dra7-hsmmc", "ti,omap4-hsmmc";
reg = <0x480d1000 0x400>;
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "mmc4";
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:44 UTC
Permalink
On DRA7 family SoCs, MMC1 controller supports SDR104,
SDR50, DDR50, SDR25 and SDR12 UHS modes (These UHS modes
are not supported in beagle-x15 because it's not possible
to switch IO lines supply voltage to 1.8v).

MMC2 controller supports HS200 and DDR modes. (Since some of the
boards like am57xx-evm reva3, beagle-x15 and am57xx idk has
3.3v line connected to IO lines, HS200 cannot be supported
in these boards).

MMC3 controller supports SDR12, SDR25 and SDR50 modes.

MMC4 controller supports SDR12 and SDR25 modes.

Add these supported modes in device-tree file.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
arch/arm/boot/dts/am571x-idk.dts | 1 +
arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts | 1 +
arch/arm/boot/dts/am57xx-beagle-x15.dts | 6 ++++++
arch/arm/boot/dts/am57xx-idk-common.dtsi | 1 +
arch/arm/boot/dts/dra7.dtsi | 14 ++++++++++++++
5 files changed, 23 insertions(+)

diff --git a/arch/arm/boot/dts/am571x-idk.dts b/arch/arm/boot/dts/am571x-idk.dts
index 875291d1bdd7..bdf39d47948f 100644
--- a/arch/arm/boot/dts/am571x-idk.dts
+++ b/arch/arm/boot/dts/am571x-idk.dts
@@ -90,4 +90,5 @@
pinctrl-0 = <&mmc2_pins_default>;
pinctrl-1 = <&mmc2_pins_hs>;
pinctrl-2 = <&mmc2_pins_ddr_rev20 &mmc2_iodelay_ddr_conf>;
+ /delete-property/ mmc-hs200-1_8v;
};
diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts b/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts
index 5a77b334923d..17a25a74a005 100644
--- a/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts
+++ b/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts
@@ -36,6 +36,7 @@
pinctrl-0 = <&mmc2_pins_default>;
pinctrl-1 = <&mmc2_pins_hs>;
pinctrl-2 = <&mmc2_pins_ddr_3_3v_rev11 &mmc2_iodelay_ddr_3_3v_rev11_conf>;
+ /delete-property/ mmc-hs200-1_8v;
};

/* errata i880 "Ethernet RGMII2 Limited to 10/100 Mbps" */
diff --git a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts
index d6689106d2a8..8d9bdf10a7be 100644
--- a/arch/arm/boot/dts/am57xx-beagle-x15.dts
+++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts
@@ -25,6 +25,11 @@
pinctrl-1 = <&mmc1_pins_hs>;

vmmc-supply = <&ldo1_reg>;
+ /delete-property/ sd-uhs-sdr104;
+ /delete-property/ sd-uhs-sdr50;
+ /delete-property/ sd-uhs-ddr50;
+ /delete-property/ sd-uhs-sdr25;
+ /delete-property/ sd-uhs-sdr12;
};

&mmc2 {
@@ -32,6 +37,7 @@
pinctrl-0 = <&mmc2_pins_default>;
pinctrl-1 = <&mmc2_pins_hs>;
pinctrl-2 = <&mmc2_pins_ddr_3_3v_rev11 &mmc2_iodelay_ddr_3_3v_rev11_conf>;
+ /delete-property/ mmc-hs200-1_8v;
};

/* errata i880 "Ethernet RGMII2 Limited to 10/100 Mbps" */
diff --git a/arch/arm/boot/dts/am57xx-idk-common.dtsi b/arch/arm/boot/dts/am57xx-idk-common.dtsi
index 47b5b384809f..bf5bf8f52c79 100644
--- a/arch/arm/boot/dts/am57xx-idk-common.dtsi
+++ b/arch/arm/boot/dts/am57xx-idk-common.dtsi
@@ -413,6 +413,7 @@
bus-width = <8>;
ti,non-removable;
max-frequency = <96000000>;
+ /delete-property/ mmc-hs200-1_8v;
};

&dcan1 {
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index 52ce3fab483f..22b0ade96237 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -1046,6 +1046,11 @@
status = "disabled";
pbias-supply = <&pbias_mmc_reg>;
max-frequency = <192000000>;
+ sd-uhs-sdr104;
+ sd-uhs-sdr50;
+ sd-uhs-ddr50;
+ sd-uhs-sdr25;
+ sd-uhs-sdr12;
};

mmc2: ***@480b4000 {
@@ -1058,6 +1063,10 @@
dma-names = "tx", "rx";
status = "disabled";
max-frequency = <192000000>;
+ sd-uhs-sdr25;
+ sd-uhs-sdr12;
+ mmc-hs200-1_8v;
+ mmc-ddr-1_8v;
};

mmc3: ***@480ad000 {
@@ -1071,6 +1080,9 @@
status = "disabled";
/* Errata i887 limits max-frequency of MMC3 to 64 MHz */
max-frequency = <64000000>;
+ sd-uhs-sdr12;
+ sd-uhs-sdr25;
+ sd-uhs-sdr50;
};

mmc4: ***@480d1000 {
@@ -1083,6 +1095,8 @@
dma-names = "tx", "rx";
status = "disabled";
max-frequency = <192000000>;
+ sd-uhs-sdr12;
+ sd-uhs-sdr25;
};

mmu0_dsp1: ***@40d01000 {
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:32 UTC
Permalink
UHS SD card IO data line operates at 1.8V when in UHS speed
mode.

Add voltage switch support in order to enumerate UHS cards.

Also, enable CIRQ before checking for CLEV/DLEV. MMC module can
sense when the clock lines and data lines are driven high by the
card, if MMC is active and CIRQ can be used to keep the MMC
module active. This is required for voltage switching to succeed
and the card to enumerate in UHS mode.

Signed-off-by: Balaji T K <***@ti.com>
Signed-off-by: Sourav Poddar <***@ti.com>
[***@ti.com : cleanup the voltage switch sequence]
Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
[***@ti.com: make card busy functions preempt safe]
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 179 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 170 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 65330d2263ad..680934d50919 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -44,6 +44,7 @@
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
#include <linux/platform_data/hsmmc-omap.h>
+#include <linux/mmc/sd.h>

/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
@@ -111,6 +112,9 @@
/* PSTATE */
#define DLEV_DAT(x) (1 << (20 + (x)))

+/* AC12 */
+#define AC12_V1V8_SIGEN (1 << 19)
+
/* Interrupt masks for IE and ISE register */
#define CC_EN (1 << 0)
#define TC_EN (1 << 1)
@@ -150,6 +154,13 @@
#define VDD_1V8 1800000 /* 180000 uV */
#define VDD_3V0 3000000 /* 300000 uV */
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
+#define VDD_30_31 (ffs(MMC_VDD_30_31) - 1)
+
+#define CON_CLKEXTFREE (1 << 16)
+#define CON_PADEN (1 << 15)
+#define PSTATE_CLEV (1 << 24)
+#define PSTATE_DLEV (0xF << 20)
+#define PSTATE_DLEV_DAT0 (0x1 << 20)

/*
* One controller can have multiple slots, like on some omap boards using
@@ -177,6 +188,7 @@ struct omap_hsmmc_host {
struct mmc_host *mmc;
struct mmc_request *mrq;
struct mmc_command *cmd;
+ u32 last_cmd;
struct mmc_data *data;
struct clk *fclk;
struct clk *dbclk;
@@ -209,6 +221,7 @@ struct omap_hsmmc_host {
unsigned int flags;
#define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */
#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
+#define CLKEXTFREE_ENABLED (1 << 2) /* CLKEXTFREE enabled */
struct omap_hsmmc_next next_data;
struct omap_hsmmc_platform_data *pdata;

@@ -593,6 +606,9 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
if (cmd->opcode == MMC_ERASE)
irq_mask &= ~DTO_EN;

+ if (host->flags & CLKEXTFREE_ENABLED)
+ irq_mask |= CIRQ_EN;
+
spin_lock_irqsave(&host->irq_lock, flags);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
@@ -936,6 +952,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
cmdreg |= DMAE;

host->req_in_progress = 1;
+ host->last_cmd = cmd->opcode;

OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
@@ -1735,17 +1752,12 @@ static void omap_hsmmc_set_capabilities(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, CAPA, val);
}

-static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
+static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host, int iov)
{
u32 hctl, value;

- /* Only MMC1 supports 3.0V */
- if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT)
- hctl = SDVS30;
- else
- hctl = SDVS18;
-
value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK;
+ hctl = (iov == MMC_SIGNAL_VOLTAGE_180) ? SDVS18 : SDVS30;
OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl);

/* Set SD bus power bit */
@@ -1762,6 +1774,152 @@ static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
return blk_size;
}

+static int omap_hsmmc_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct omap_hsmmc_host *host;
+ u32 val = 0;
+ int ret = 0;
+
+ host = mmc_priv(mmc);
+
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+ val = OMAP_HSMMC_READ(host->base, CAPA);
+ if (!(val & VS30))
+ return -EOPNOTSUPP;
+
+ omap_hsmmc_conf_bus_power(host, ios->signal_voltage);
+
+ val = OMAP_HSMMC_READ(host->base, AC12);
+ val &= ~AC12_V1V8_SIGEN;
+ OMAP_HSMMC_WRITE(host->base, AC12, val);
+
+ ret = omap_hsmmc_set_power(host, 1, VDD_30_31);
+ if (ret) {
+ dev_err(mmc_dev(host->mmc), "failed to switch to 3v\n");
+ return ret;
+ }
+
+ dev_dbg(mmc_dev(host->mmc), " i/o voltage switch to 3V\n");
+ } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ val = OMAP_HSMMC_READ(host->base, CAPA);
+ if (!(val & VS18))
+ return -EOPNOTSUPP;
+
+ omap_hsmmc_conf_bus_power(host, ios->signal_voltage);
+
+ val = OMAP_HSMMC_READ(host->base, AC12);
+ val |= AC12_V1V8_SIGEN;
+ OMAP_HSMMC_WRITE(host->base, AC12, val);
+
+ ret = omap_hsmmc_set_power(host, 1, VDD_165_195);
+ if (ret < 0) {
+ dev_err(mmc_dev(host->mmc), "failed to switch 1.8v\n");
+ return ret;
+ }
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int omap_hsmmc_card_busy_low(struct omap_hsmmc_host *host)
+{
+ int i;
+ u32 val;
+
+ val = OMAP_HSMMC_READ(host->base, CON);
+ val &= ~CON_CLKEXTFREE;
+ val |= CON_PADEN;
+ OMAP_HSMMC_WRITE(host->base, CON, val);
+
+ /* By observation, card busy status reflects in 100 - 200us */
+ for (i = 0; i < 5; i++) {
+ val = OMAP_HSMMC_READ(host->base, PSTATE);
+ if (!(val & (PSTATE_CLEV | PSTATE_DLEV)))
+ return true;
+
+ usleep_range(100, 200);
+ }
+
+ dev_err(mmc_dev(host->mmc), "card busy\n");
+
+ return false;
+}
+
+static int omap_hsmmc_card_busy_high(struct omap_hsmmc_host *host)
+{
+ int i;
+ u32 val;
+ int ret = true;
+
+ val = OMAP_HSMMC_READ(host->base, CON);
+ val |= CLKEXTFREE;
+ OMAP_HSMMC_WRITE(host->base, CON, val);
+
+ host->flags |= CLKEXTFREE_ENABLED;
+ disable_irq(host->irq);
+ omap_hsmmc_enable_irq(host, NULL);
+
+ /* By observation, card busy status reflects in 100 - 200us */
+ for (i = 0; i < 5; i++) {
+ val = OMAP_HSMMC_READ(host->base, PSTATE);
+ if ((val & PSTATE_CLEV) && (val & PSTATE_DLEV)) {
+ val = OMAP_HSMMC_READ(host->base, CON);
+ val &= ~(CON_CLKEXTFREE | CON_PADEN);
+ OMAP_HSMMC_WRITE(host->base, CON, val);
+ ret = false;
+ goto disable_irq;
+ }
+
+ usleep_range(100, 200);
+ }
+
+ dev_err(mmc_dev(host->mmc), "card busy\n");
+
+disable_irq:
+ omap_hsmmc_disable_irq(host);
+ enable_irq(host->irq);
+ host->flags &= ~CLKEXTFREE_ENABLED;
+
+ return ret;
+}
+
+static int omap_hsmmc_card_busy(struct mmc_host *mmc)
+{
+ struct omap_hsmmc_host *host;
+ u32 val;
+ u32 reg;
+ int ret;
+
+ host = mmc_priv(mmc);
+
+ if (host->last_cmd != SD_SWITCH_VOLTAGE) {
+ /*
+ * PADEN should be set for DLEV to reflect the correct
+ * state of data lines atleast for MMC1 on AM57x.
+ */
+ reg = OMAP_HSMMC_READ(host->base, CON);
+ reg |= CON_PADEN;
+ OMAP_HSMMC_WRITE(host->base, CON, reg);
+ val = OMAP_HSMMC_READ(host->base, PSTATE);
+ reg &= ~CON_PADEN;
+ OMAP_HSMMC_WRITE(host->base, CON, reg);
+ if (val & PSTATE_DLEV_DAT0)
+ return false;
+ return true;
+ }
+
+ val = OMAP_HSMMC_READ(host->base, AC12);
+ if (val & AC12_V1V8_SIGEN)
+ ret = omap_hsmmc_card_busy_high(host);
+ else
+ ret = omap_hsmmc_card_busy_low(host);
+
+ return ret;
+}
+
static struct mmc_host_ops omap_hsmmc_ops = {
.post_req = omap_hsmmc_post_req,
.pre_req = omap_hsmmc_pre_req,
@@ -1771,6 +1929,8 @@ static struct mmc_host_ops omap_hsmmc_ops = {
.get_ro = mmc_gpio_get_ro,
.init_card = omap_hsmmc_init_card,
.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
+ .start_signal_voltage_switch = omap_hsmmc_start_signal_voltage_switch,
+ .card_busy = omap_hsmmc_card_busy,
};

#ifdef CONFIG_DEBUG_FS
@@ -2055,7 +2215,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->pm_caps |= mmc_pdata(host)->pm_caps;

omap_hsmmc_set_capabilities(host);
- omap_hsmmc_conf_bus_power(host);

host->rx_chan = dma_request_chan(&pdev->dev, "rx");
if (IS_ERR(host->rx_chan)) {
@@ -2193,6 +2352,7 @@ static int omap_hsmmc_suspend(struct device *dev)
static int omap_hsmmc_resume(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
+ struct mmc_ios *ios;

if (!host)
return 0;
@@ -2202,8 +2362,9 @@ static int omap_hsmmc_resume(struct device *dev)
if (host->dbclk)
clk_prepare_enable(host->dbclk);

+ ios = &host->mmc->ios;
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
- omap_hsmmc_conf_bus_power(host);
+ omap_hsmmc_conf_bus_power(host, ios->signal_voltage);

omap_hsmmc_protect_card(host);
pm_runtime_mark_last_busy(host->dev);
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:34 UTC
Permalink
MMC tuning procedure is required to support SD card
UHS1-SDR104 mode and EMMC HS200 mode.

The tuning function omap_execute_tuning() will only be
called by the MMC/SD core if the corresponding speed modes
are supported by the OMAP silicon which is set in the mmc
host "caps" field.

Add a separate function to set the UHSMS field to one of
SDR104, SDR50, DDR50, SDR25 or SDR12 depending on the
inserted SD card. This is required for tuning to succeed in
the case of SDR104/HS200 or SDR50.

Signed-off-by: Balaji T K <***@ti.com>
[***@ti.com: disable DEB interrupt during tuning]
Signed-off-by: Ravikumar Kattekola <***@ti.com>
Signed-off-by: Viswanath Puttagunta <***@ti.com>
Signed-off-by: Sourav Poddar <***@ti.com>
[***@ti.com : cleanup the tuning sequence]
Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
[***@ti.com: add comment for tuning timeout]
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 200 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 200 insertions(+)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 3ca18e28be01..f64148b119df 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/dmaengine.h>
#include <linux/seq_file.h>
@@ -49,6 +50,7 @@
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
+#define OMAP_HSMMC_DLL 0x0034
#define OMAP_HSMMC_SDMASA 0x0100
#define OMAP_HSMMC_BLK 0x0104
#define OMAP_HSMMC_ARG 0x0108
@@ -66,6 +68,7 @@
#define OMAP_HSMMC_ISE 0x0138
#define OMAP_HSMMC_AC12 0x013C
#define OMAP_HSMMC_CAPA 0x0140
+#define OMAP_HSMMC_CAPA2 0x0144

#define VS18 (1 << 26)
#define VS30 (1 << 25)
@@ -114,6 +117,26 @@

/* AC12 */
#define AC12_V1V8_SIGEN (1 << 19)
+#define AC12_SCLK_SEL (1 << 23)
+#define AC12_UHSMC_MASK (7 << 16)
+#define AC12_UHSMC_SDR12 (0 << 16)
+#define AC12_UHSMC_SDR25 (1 << 16)
+#define AC12_UHSMC_SDR50 (2 << 16)
+#define AC12_UHSMC_SDR104 (3 << 16)
+#define AC12_UHSMC_DDR50 (4 << 16)
+#define AC12_UHSMC_RES (0x7 << 16)
+
+/* DLL */
+#define DLL_SWT (1 << 20)
+#define DLL_FORCE_SR_C_SHIFT 13
+#define DLL_FORCE_SR_C_MASK 0x7f
+#define DLL_FORCE_VALUE (1 << 12)
+#define DLL_CALIB (1 << 1)
+
+#define MAX_PHASE_DELAY 0x7c
+
+/* CAPA2 */
+#define CAPA2_TSDR50 (1 << 13)

/* Interrupt masks for IE and ISE register */
#define CC_EN (1 << 0)
@@ -202,6 +225,7 @@ struct omap_hsmmc_host {
unsigned int dma_sg_idx;
unsigned char bus_mode;
unsigned char power_mode;
+ unsigned char timing;
int suspended;
u32 con;
u32 hctl;
@@ -225,6 +249,8 @@ struct omap_hsmmc_host {
struct omap_hsmmc_next next_data;
struct omap_hsmmc_platform_data *pdata;

+ bool is_tuning;
+
/* return MMC cover switch state, can be NULL if not supported.
*
* possible return values:
@@ -242,6 +268,7 @@ struct omap_mmc_of_data {
};

static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
+static void omap_hsmmc_disable_tuning(struct omap_hsmmc_host *host);

static int omap_hsmmc_card_detect(struct device *dev)
{
@@ -599,6 +626,15 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
u32 irq_mask = INT_EN_MASK;
unsigned long flags;

+ if (host->is_tuning)
+ /*
+ * OMAP5/DRA74X/DRA72x Errata i802:
+ * DCRC error interrupts (MMCHS_STAT[21] DCRC=0x1) can occur
+ * during the tuning procedure. So disable it during the
+ * tuning procedure.
+ */
+ irq_mask &= ~(DCRC_EN | DEB_EN);
+
if (host->use_dma)
irq_mask &= ~(BRR_EN | BWR_EN);

@@ -948,6 +984,11 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
cmdreg &= ~(DDIR);
}

+ /* Tuning command is special. Data Present Select should be set */
+ if ((cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
+ cmdreg |= DP_SELECT | DDIR;
+
if (host->use_dma)
cmdreg |= DMAE;

@@ -1595,6 +1636,41 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
omap_hsmmc_start_command(host, req->cmd, req->data);
}

+static void omap_hsmmc_set_timing(struct omap_hsmmc_host *host)
+{
+ u32 val;
+ struct mmc_ios *ios = &host->mmc->ios;
+
+ omap_hsmmc_stop_clock(host);
+
+ val = OMAP_HSMMC_READ(host->base, AC12);
+ val &= ~AC12_UHSMC_MASK;
+ switch (ios->timing) {
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ val |= AC12_UHSMC_SDR104;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ val |= AC12_UHSMC_DDR50;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ val |= AC12_UHSMC_SDR50;
+ break;
+ case MMC_TIMING_UHS_SDR25:
+ val |= AC12_UHSMC_SDR25;
+ break;
+ case MMC_TIMING_UHS_SDR12:
+ val |= AC12_UHSMC_SDR12;
+ break;
+ default:
+ val |= AC12_UHSMC_RES;
+ break;
+ }
+ OMAP_HSMMC_WRITE(host->base, AC12, val);
+
+ omap_hsmmc_start_clock(host);
+}
+
/* Routine to configure clock values. Exposed API to core */
static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
@@ -1604,6 +1680,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->power_mode != host->power_mode) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
+ omap_hsmmc_disable_tuning(host);
omap_hsmmc_set_power(host, 0, 0);
break;
case MMC_POWER_UP:
@@ -1622,6 +1699,11 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

omap_hsmmc_set_clock(host);

+ if (ios->timing != host->timing) {
+ omap_hsmmc_set_timing(host);
+ host->timing = ios->timing;
+ }
+
if (do_send_init_stream)
send_init_stream(host);

@@ -1920,6 +2002,122 @@ static int omap_hsmmc_card_busy(struct mmc_host *mmc)
return ret;
}

+static inline void omap_hsmmc_set_dll(struct omap_hsmmc_host *host, int count)
+{
+ int i;
+ u32 dll;
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll |= DLL_FORCE_VALUE;
+ dll &= ~(DLL_FORCE_SR_C_MASK << DLL_FORCE_SR_C_SHIFT);
+ dll |= (count << DLL_FORCE_SR_C_SHIFT);
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+
+ dll |= DLL_CALIB;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ for (i = 0; i < 1000; i++) {
+ if (OMAP_HSMMC_READ(host->base, DLL) & DLL_CALIB)
+ break;
+ }
+ dll &= ~DLL_CALIB;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+}
+
+static void omap_hsmmc_disable_tuning(struct omap_hsmmc_host *host)
+{
+ int val;
+
+ val = OMAP_HSMMC_READ(host->base, AC12);
+ val &= ~(AC12_SCLK_SEL);
+ OMAP_HSMMC_WRITE(host->base, AC12, val);
+
+ val = OMAP_HSMMC_READ(host->base, DLL);
+ val &= ~(DLL_FORCE_VALUE | DLL_SWT);
+ OMAP_HSMMC_WRITE(host->base, DLL, val);
+}
+
+static int omap_hsmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ u32 val;
+ u8 cur_match, prev_match = 0;
+ int ret;
+ u32 phase_delay = 0;
+ u32 start_window = 0, max_window = 0;
+ u32 length = 0, max_len = 0;
+ struct mmc_ios *ios = &mmc->ios;
+ struct omap_hsmmc_host *host;
+
+ /* clock tuning is not needed for upto 52MHz */
+ if (ios->clock <= OMAP_MMC_MAX_CLOCK)
+ return 0;
+
+ host = mmc_priv(mmc);
+
+ val = OMAP_HSMMC_READ(host->base, CAPA2);
+ if (ios->timing == MMC_TIMING_UHS_SDR50 && !(val & CAPA2_TSDR50))
+ return 0;
+
+ val = OMAP_HSMMC_READ(host->base, DLL);
+ val |= DLL_SWT;
+ OMAP_HSMMC_WRITE(host->base, DLL, val);
+
+ host->is_tuning = true;
+
+ while (phase_delay <= MAX_PHASE_DELAY) {
+ omap_hsmmc_set_dll(host, phase_delay);
+
+ cur_match = !mmc_send_tuning(mmc, opcode, NULL);
+ if (cur_match) {
+ if (prev_match) {
+ length++;
+ } else {
+ start_window = phase_delay;
+ length = 1;
+ }
+ }
+
+ if (length > max_len) {
+ max_window = start_window;
+ max_len = length;
+ }
+
+ prev_match = cur_match;
+ phase_delay += 4;
+ }
+
+ host->is_tuning = false;
+
+ if (!max_len) {
+ dev_err(mmc_dev(host->mmc), "Unable to find match\n");
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ val = OMAP_HSMMC_READ(host->base, AC12);
+ if (!(val & AC12_SCLK_SEL)) {
+ ret = -EIO;
+ goto tuning_error;
+ }
+
+ phase_delay = max_window + 4 * ((3 * max_len) >> 2);
+ omap_hsmmc_set_dll(host, phase_delay);
+
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+
+ return 0;
+
+tuning_error:
+ dev_err(mmc_dev(host->mmc),
+ "Tuning failed. Using fixed sampling clock\n");
+
+ omap_hsmmc_disable_tuning(host);
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+
+ return ret;
+}
+
static struct mmc_host_ops omap_hsmmc_ops = {
.post_req = omap_hsmmc_post_req,
.pre_req = omap_hsmmc_pre_req,
@@ -1931,6 +2129,7 @@ static struct mmc_host_ops omap_hsmmc_ops = {
.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
.start_signal_voltage_switch = omap_hsmmc_start_signal_voltage_switch,
.card_busy = omap_hsmmc_card_busy,
+ .execute_tuning = omap_hsmmc_execute_tuning,
};

#ifdef CONFIG_DEBUG_FS
@@ -2138,6 +2337,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->mapbase = res->start + pdata->reg_offset;
host->base = base + pdata->reg_offset;
host->power_mode = MMC_POWER_OFF;
+ host->timing = 0;
host->next_data.cookie = 1;
host->pbias_enabled = 0;
host->vqmmc_enabled = 0;
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:30 UTC
Permalink
Add a separate function to set the voltage capabilities of the host
controller. Voltage capabilities should be set only once during
controller initialization but bus power can be changed every time there
is a voltage switch and whenever a different card is inserted.
This allows omap_hsmmc_conf_bus_power to be invoked every time there
is a voltage switch.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 28 +++++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 48bb06788d9f..c9ea41bb4e3a 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1799,25 +1799,34 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
return ret;
}

+static void omap_hsmmc_set_capabilities(struct omap_hsmmc_host *host)
+{
+ u32 val;
+
+ val = OMAP_HSMMC_READ(host->base, CAPA);
+
+ /* Only MMC1 supports 3.0V */
+ if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT)
+ val |= (VS30 | VS18);
+ else
+ val |= VS18;
+
+ OMAP_HSMMC_WRITE(host->base, CAPA, val);
+}
+
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
- u32 hctl, capa, value;
+ u32 hctl, value;

/* Only MMC1 supports 3.0V */
- if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
+ if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT)
hctl = SDVS30;
- capa = VS30 | VS18;
- } else {
+ else
hctl = SDVS18;
- capa = VS18;
- }

value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK;
OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl);

- value = OMAP_HSMMC_READ(host->base, CAPA);
- OMAP_HSMMC_WRITE(host->base, CAPA, value | capa);
-
/* Set SD bus power bit */
set_sd_bus_power(host);
}
@@ -2124,6 +2133,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)

mmc->pm_caps |= mmc_pdata(host)->pm_caps;

+ omap_hsmmc_set_capabilities(host);
omap_hsmmc_conf_bus_power(host);

host->rx_chan = dma_request_chan(&pdev->dev, "rx");
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:36 UTC
Permalink
From: Mugunthan V N <***@ti.com>

DRA7 Errata No i834: When using high speed HS200 and SDR104
cards, the functional clock for MMC module will be 192MHz.
At this frequency, the maximum obtainable timeout (DTO =0xE)
in hardware is (1/192MHz)*2^27 = 700ms. Commands taking longer
than 700ms will be affected by this small window frame and
will be timing out frequently even without a genune timeout
from the card. Workarround for this errata is use a software
timer instead of hardware timer to provide the delay requested
by the upper layer

So adding a software timer as a work around for the errata.
Instead of using software timeout only for larger delays requested
when using HS200/SDR104 cards which results in hardware and
software timer race conditions, so move all the timeout request
to use software timer when HS200/SDR104 card is connected and
use hardware timer when other type cards are connected.

Also start the software timer after queueing to DMA to ensure
we are more likely to expire within correct limits. To be ever
more sure that we won't expire this soft timer too early, we're
adding a 1000000ns slack to the data timeout requested by the
upper layer.

[***@ti.com: fix compiler warning in sw timeout function and
use sw timeout for busy timeout]
Signed-off-by: Ravikumar Kattekola <***@ti.com>
Signed-off-by: Mugunthan V N <***@ti.com>
[***@ti.com: Fix the timeout value to account for the entire
transfer to complete here.]
Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 126 ++++++++++++++++++++++++++++++++++++------
1 file changed, 109 insertions(+), 17 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index f1d5e8385591..3676ffa72929 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -185,6 +185,11 @@
#define PSTATE_DLEV (0xF << 20)
#define PSTATE_DLEV_DAT0 (0x1 << 20)

+#define MMC_BLOCK_TRANSFER_TIME_NS(blksz, bus_width, freq) \
+ ((unsigned long long) \
+ (2 * (((blksz) * NSEC_PER_SEC * \
+ (8 / (bus_width))) / (freq))))
+
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@@ -250,6 +255,8 @@ struct omap_hsmmc_host {
struct omap_hsmmc_platform_data *pdata;

bool is_tuning;
+ struct timer_list timer;
+ unsigned long long data_timeout;

/* return MMC cover switch state, can be NULL if not supported.
*
@@ -631,8 +638,8 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
if (host->use_dma)
irq_mask &= ~(BRR_EN | BWR_EN);

- /* Disable timeout for erases */
- if (cmd->opcode == MMC_ERASE)
+ /* Disable timeout for erases or when using software timeout */
+ if (cmd && (cmd->opcode == MMC_ERASE || host->data_timeout))
irq_mask &= ~DTO_EN;

if (host->flags & CLKEXTFREE_ENABLED)
@@ -1239,8 +1246,16 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
}

OMAP_HSMMC_WRITE(host->base, STAT, status);
- if (end_cmd || ((status & CC_EN) && host->cmd))
+ if (end_cmd || ((status & CC_EN) && host->cmd)) {
omap_hsmmc_cmd_done(host, host->cmd);
+ if (host->data_timeout) {
+ unsigned long timeout;
+
+ timeout = jiffies +
+ nsecs_to_jiffies(host->data_timeout);
+ mod_timer(&host->timer, timeout);
+ }
+ }
if ((end_trans || (status & TC_EN)) && host->mrq)
omap_hsmmc_xfer_done(host, data);
}
@@ -1254,7 +1269,19 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
int status;

status = OMAP_HSMMC_READ(host->base, STAT);
+
while (status & (INT_EN_MASK | CIRQ_EN)) {
+ /*
+ * During a successful bulk data transfer command-completion
+ * interrupt and transfer-completion interrupt will be
+ * generated, but software-timeout timer should be deleted
+ * only on non-cc interrupts (transfer complete or error)
+ */
+ if (host->data_timeout && (status & (~CC_EN))) {
+ del_timer(&host->timer);
+ host->data_timeout = 0;
+ }
+
if (host->req_in_progress)
omap_hsmmc_do_irq(host, status);

@@ -1268,6 +1295,25 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}

+static void omap_hsmmc_soft_timeout(unsigned long data)
+{
+ struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)data;
+ bool end_trans = false;
+
+ omap_hsmmc_disable_irq(host);
+ if (host->data || host->response_busy) {
+ host->response_busy = 0;
+ end_trans = true;
+ }
+
+ hsmmc_command_incomplete(host, -ETIMEDOUT, 0);
+ if (end_trans && host->mrq)
+ omap_hsmmc_xfer_done(host, host->data);
+ else if (host->cmd)
+ omap_hsmmc_cmd_done(host, host->cmd);
+ host->data_timeout = 0;
+}
+
static void set_sd_bus_power(struct omap_hsmmc_host *host)
{
unsigned long i;
@@ -1462,6 +1508,9 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
unsigned long long timeout = timeout_ns;
unsigned int cycle_ns;
uint32_t reg, clkd, dto = 0;
+ struct mmc_ios *ios = &host->mmc->ios;
+ struct mmc_data *data = host->mrq->data;
+ struct mmc_command *cmd = host->mrq->cmd;

reg = OMAP_HSMMC_READ(host->base, SYSCTL);
clkd = (reg & CLKD_MASK) >> CLKD_SHIFT;
@@ -1471,23 +1520,60 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
cycle_ns = 1000000000 / (host->clk_rate / clkd);
do_div(timeout, cycle_ns);
timeout += timeout_clks;
- if (timeout) {
- while ((timeout & 0x80000000) == 0) {
- dto += 1;
- timeout <<= 1;
- }
- dto = 31 - dto;
+
+ if (!timeout)
+ goto out;
+
+ while ((timeout & 0x80000000) == 0) {
+ dto += 1;
timeout <<= 1;
- if (timeout && dto)
- dto += 1;
- if (dto >= 13)
- dto -= 13;
- else
- dto = 0;
- if (dto > 14)
- dto = 14;
+ }
+ dto = 31 - dto;
+ timeout <<= 1;
+ if (timeout && dto)
+ dto += 1;
+ if (dto >= 13)
+ dto -= 13;
+ else
+ dto = 0;
+ if (dto > 14) {
+ /*
+ * DRA7 Errata No i834: When using high speed HS200 and
+ * SDR104 cards, the functional clock for MMC module
+ * will be 192MHz. At this frequency, the maximum
+ * obtainable timeout (DTO =0xE) in hardware is
+ * (1/192MHz)*2^27 = 700ms. Commands taking longer than
+ * 700ms will be affected by this small window frame
+ * and will be timing out frequently even without a
+ * genuine timeout from the card. Workaround for
+ * this errata is use a software timer instead of
+ * hardware timer to provide the timeout requested
+ * by the upper layer.
+ *
+ * The timeout from the upper layer denotes the delay
+ * between the end bit of the read command and the
+ * start bit of the data block and in the case of
+ * multiple-read operation, they also define the
+ * typical delay between the end bit of a data
+ * block and the start bit of next data block.
+ *
+ * Calculate the total timeout value for the entire
+ * transfer to complete from the timeout value given
+ * by the upper layer.
+ */
+ if (data)
+ host->data_timeout = (data->blocks *
+ (timeout_ns +
+ MMC_BLOCK_TRANSFER_TIME_NS(
+ data->blksz, ios->bus_width,
+ ios->clock)));
+ else if (cmd->flags & MMC_RSP_BUSY)
+ host->data_timeout = timeout_ns;
+
+ dto = 14;
}

+out:
reg &= ~DTO_MASK;
reg |= dto << DTO_SHIFT;
OMAP_HSMMC_WRITE(host->base, SYSCTL, reg);
@@ -2347,6 +2433,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->ops = &omap_hsmmc_ops;

spin_lock_init(&host->irq_lock);
+ setup_timer(&host->timer, omap_hsmmc_soft_timeout,
+ (unsigned long)host);

host->fclk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(host->fclk)) {
@@ -2511,6 +2599,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
dma_release_channel(host->tx_chan);
dma_release_channel(host->rx_chan);

+ del_timer_sync(&host->timer);
+
pm_runtime_dont_use_autosuspend(host->dev);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
@@ -2544,6 +2634,8 @@ static int omap_hsmmc_suspend(struct device *dev)
if (host->dbclk)
clk_disable_unprepare(host->dbclk);

+ del_timer_sync(&host->timer);
+
pm_runtime_put_sync(host->dev);
return 0;
}
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:41 UTC
Permalink
omap_hsmmc doesn't support polled I/O. So remove *use_dma*
which is always set to '1'.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 37 ++++++++++++++++---------------------
1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 0887da4f1ff6..f47bade0d6fe 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -238,7 +238,7 @@ struct omap_hsmmc_host {
u32 capa;
int irq;
int wake_irq;
- int use_dma, dma_ch;
+ int dma_ch;
struct dma_chan *tx_chan;
struct dma_chan *rx_chan;
int response_busy;
@@ -647,8 +647,8 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
*/
irq_mask &= ~(DCRC_EN | DEB_EN);

- if (host->use_dma)
- irq_mask &= ~(BRR_EN | BWR_EN);
+ /* BRR and BWR need not be enabled for DMA */
+ irq_mask &= ~(BRR_EN | BWR_EN);

/* Disable timeout for erases or when using software timeout */
if (cmd && (cmd->opcode == MMC_ERASE || host->data_timeout))
@@ -1001,8 +1001,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
(cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
cmdreg |= DP_SELECT | DDIR;

- if (host->use_dma)
- cmdreg |= DMAE;
+ cmdreg |= DMAE;

host->req_in_progress = 1;
host->last_cmd = cmd->opcode;
@@ -1029,7 +1028,7 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req

omap_hsmmc_disable_irq(host);
/* Do not complete the request if DMA is still in progress */
- if (mrq->data && host->use_dma && dma_ch != -1)
+ if (mrq->data && dma_ch != -1)
return;
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
@@ -1116,7 +1115,7 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
host->dma_ch = -1;
spin_unlock_irqrestore(&host->irq_lock, flags);

- if (host->use_dma && dma_ch != -1) {
+ if (dma_ch != -1) {
struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);

dmaengine_terminate_all(chan);
@@ -1634,12 +1633,10 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
return 0;
}

- if (host->use_dma) {
- ret = omap_hsmmc_setup_dma_transfer(host, req);
- if (ret != 0) {
- dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
- return ret;
- }
+ ret = omap_hsmmc_setup_dma_transfer(host, req);
+ if (ret != 0) {
+ dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
+ return ret;
}
return 0;
}
@@ -1650,7 +1647,7 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
struct omap_hsmmc_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;

- if (host->use_dma && data->host_cookie) {
+ if (data->host_cookie) {
struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);

dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
@@ -1662,19 +1659,18 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
+ struct dma_chan *c = NULL;

if (mrq->data->host_cookie) {
mrq->data->host_cookie = 0;
return ;
}

- if (host->use_dma) {
- struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+ c = omap_hsmmc_get_dma_chan(host, mrq->data);

- if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
- &host->next_data, c))
- mrq->data->host_cookie = 0;
- }
+ if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
+ &host->next_data, c))
+ mrq->data->host_cookie = 0;
}

/*
@@ -2563,7 +2559,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->mmc = mmc;
host->pdata = pdata;
host->dev = &pdev->dev;
- host->use_dma = 1;
host->dma_ch = -1;
host->irq = irq;
host->mapbase = res->start + pdata->reg_offset;
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:29 UTC
Permalink
Add support for vmmc_aux to switch to 1.8v. Also use "iov" instead of
"vdd" to indicate io voltage. This is in preparation for adding support
for io signal voltage switch.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 50 ++++++++++++++++++++++++++++---------------
1 file changed, 33 insertions(+), 17 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 7c12f3715676..48bb06788d9f 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -244,11 +244,12 @@ static int omap_hsmmc_get_cover_state(struct device *dev)
return mmc_gpio_get_cd(host->mmc);
}

-static int omap_hsmmc_enable_supply(struct mmc_host *mmc)
+static int omap_hsmmc_enable_supply(struct mmc_host *mmc, int iov)
{
int ret;
struct omap_hsmmc_host *host = mmc_priv(mmc);
struct mmc_ios *ios = &mmc->ios;
+ int uvoltage;

if (!IS_ERR(mmc->supply.vmmc)) {
ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
@@ -257,7 +258,25 @@ static int omap_hsmmc_enable_supply(struct mmc_host *mmc)
}

/* Enable interface voltage rail, if needed */
- if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ if (host->vqmmc_enabled) {
+ ret = regulator_disable(mmc->supply.vqmmc);
+ if (ret) {
+ dev_err(mmc_dev(mmc),
+ "vmmc_aux reg disable failed\n");
+ goto err_vqmmc;
+ }
+ host->vqmmc_enabled = 0;
+ }
+
+ uvoltage = (iov == VDD_165_195) ? VDD_1V8 : VDD_3V0;
+ ret = regulator_set_voltage(mmc->supply.vqmmc, uvoltage,
+ uvoltage);
+ if (ret) {
+ dev_err(mmc_dev(mmc), "vmmc_aux set voltage failed\n");
+ goto err_vqmmc;
+ }
+
ret = regulator_enable(mmc->supply.vqmmc);
if (ret) {
dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n");
@@ -309,22 +328,19 @@ static int omap_hsmmc_disable_supply(struct mmc_host *mmc)
}

static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
- int vdd)
+ int iov)
{
int ret;
+ int uvoltage;

if (IS_ERR(host->pbias))
return 0;

if (power_on) {
- if (vdd <= VDD_165_195)
- ret = regulator_set_voltage(host->pbias, VDD_1V8,
- VDD_1V8);
- else
- ret = regulator_set_voltage(host->pbias, VDD_3V0,
- VDD_3V0);
- if (ret < 0) {
- dev_err(host->dev, "pbias set voltage fail\n");
+ uvoltage = (iov <= VDD_165_195) ? VDD_1V8 : VDD_3V0;
+ ret = regulator_set_voltage(host->pbias, uvoltage, uvoltage);
+ if (ret) {
+ dev_err(host->dev, "pbias set voltage failed\n");
return ret;
}

@@ -351,13 +367,13 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
}

static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
- int vdd)
+ int iov)
{
struct mmc_host *mmc = host->mmc;
int ret = 0;

if (mmc_pdata(host)->set_power)
- return mmc_pdata(host)->set_power(host->dev, power_on, vdd);
+ return mmc_pdata(host)->set_power(host->dev, power_on, iov);

/*
* If we don't see a Vcc regulator, assume it's a fixed
@@ -367,7 +383,7 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
return 0;

if (mmc_pdata(host)->before_set_reg)
- mmc_pdata(host)->before_set_reg(host->dev, power_on, vdd);
+ mmc_pdata(host)->before_set_reg(host->dev, power_on, iov);

ret = omap_hsmmc_set_pbias(host, false, 0);
if (ret)
@@ -387,11 +403,11 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
* chips/cards need an interface voltage rail too.
*/
if (power_on) {
- ret = omap_hsmmc_enable_supply(mmc);
+ ret = omap_hsmmc_enable_supply(mmc, iov);
if (ret)
return ret;

- ret = omap_hsmmc_set_pbias(host, true, vdd);
+ ret = omap_hsmmc_set_pbias(host, true, iov);
if (ret)
goto err_set_voltage;
} else {
@@ -401,7 +417,7 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
}

if (mmc_pdata(host)->after_set_reg)
- mmc_pdata(host)->after_set_reg(host->dev, power_on, vdd);
+ mmc_pdata(host)->after_set_reg(host->dev, power_on, iov);

return 0;
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:42 UTC
Permalink
omap hsmmc host controller has ADMA2 feature. Enable it here
for better read and write throughput.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
[***@ti.com: handle ADMA errors]
Signed-off-by: Misael Lopez Cruz <***@ti.com>
[***@ti.com: restore adma settings after context loss]
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 307 +++++++++++++++++++++++++++----
include/linux/platform_data/hsmmc-omap.h | 1 +
2 files changed, 271 insertions(+), 37 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index f47bade0d6fe..e8656b423541 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -48,6 +48,9 @@
#include <linux/mmc/sd.h>

/* OMAP HSMMC Host Controller Registers */
+#define OMAP_HSMMC_HL_REV 0x0000
+#define OMAP_HSMMC_HL_HWINFO 0x0004
+#define OMAP_HSMMC_HL_SYSCONFIG 0x0010
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
#define OMAP_HSMMC_DLL 0x0034
@@ -69,7 +72,10 @@
#define OMAP_HSMMC_AC12 0x013C
#define OMAP_HSMMC_CAPA 0x0140
#define OMAP_HSMMC_CAPA2 0x0144
+#define OMAP_HSMMC_ADMAES 0x0154
+#define OMAP_HSMMC_ADMASAL 0x0158

+#define MADMA_EN (1 << 0)
#define VS18 (1 << 26)
#define VS30 (1 << 25)
#define HSS (1 << 21)
@@ -79,6 +85,7 @@
#define SDVS_MASK 0x00000E00
#define SDVSCLR 0xFFFFF1FF
#define SDVSDET 0x00000400
+#define DMA_SELECT (2 << 3)
#define AUTOIDLE 0x1
#define SDBP (1 << 8)
#define DTO 0xe
@@ -100,6 +107,7 @@
#define FOUR_BIT (1 << 1)
#define HSPE (1 << 2)
#define IWE (1 << 24)
+#define DMA_MASTER (1 << 20)
#define DDR (1 << 19)
#define CLKEXTFREE (1 << 16)
#define CTPL (1 << 11)
@@ -153,10 +161,11 @@
#define DCRC_EN (1 << 21)
#define DEB_EN (1 << 22)
#define ACE_EN (1 << 24)
+#define ADMAE_EN (1 << 25)
#define CERR_EN (1 << 28)
#define BADA_EN (1 << 29)

-#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
+#define INT_EN_MASK (BADA_EN | CERR_EN | ADMAE_EN | ACE_EN | DEB_EN | DCRC_EN |\
DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
BRR_EN | BWR_EN | TC_EN | CC_EN)

@@ -206,6 +215,33 @@
#define OMAP_HSMMC_WRITE(base, reg, val) \
__raw_writel((val), (base) + OMAP_HSMMC_##reg)

+struct omap_hsmmc_adma_desc {
+ u8 attr;
+ u8 reserved;
+ u16 len;
+ u32 addr;
+} __packed;
+
+#define ADMA_DESC_SIZE 8
+
+#define ADMA_MAX_LEN 65532
+
+/* Decriptor table defines */
+#define ADMA_DESC_ATTR_VALID BIT(0)
+#define ADMA_DESC_ATTR_END BIT(1)
+#define ADMA_DESC_ATTR_INT BIT(2)
+#define ADMA_DESC_ATTR_ACT1 BIT(4)
+#define ADMA_DESC_ATTR_ACT2 BIT(5)
+
+#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2
+#define ADMA_DESC_LINK_DESC (ADMA_DESC_ATTR_ACT1 | ADMA_DESC_ATTR_ACT2)
+
+/* ADMA error status */
+#define AES_MASK 0x3
+#define ST_STOP 0x0
+#define ST_FDS 0x1
+#define ST_TFR 0x3
+
struct omap_hsmmc_next {
unsigned int dma_len;
s32 cookie;
@@ -239,6 +275,7 @@ struct omap_hsmmc_host {
int irq;
int wake_irq;
int dma_ch;
+ int use_adma;
struct dma_chan *tx_chan;
struct dma_chan *rx_chan;
int response_busy;
@@ -270,6 +307,9 @@ struct omap_hsmmc_host {
struct pinctrl_state *hs_pinctrl_state;
struct pinctrl_state *ddr_1_8v_pinctrl_state;

+ struct omap_hsmmc_adma_desc *adma_desc_table;
+ dma_addr_t adma_desc_table_addr;
+
/* return MMC cover switch state, can be NULL if not supported.
*
* possible return values:
@@ -840,6 +880,18 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, IE, 0);
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);

+ if (host->use_adma) {
+ u32 val;
+
+ val = OMAP_HSMMC_READ(host->base, CON);
+ val |= DMA_MASTER;
+ OMAP_HSMMC_WRITE(host->base, CON, val);
+
+ val = OMAP_HSMMC_READ(host->base, HCTL);
+ val |= DMA_SELECT;
+ OMAP_HSMMC_WRITE(host->base, HCTL, val);
+ }
+
/* Do not initialize card-specific things if the power is off */
if (host->power_mode == MMC_POWER_OFF)
goto out;
@@ -1054,6 +1106,10 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
return;
}

+ if (host->use_adma && host->data && !data->host_cookie)
+ dma_unmap_sg(host->dev, data->sg, data->sg_len,
+ mmc_get_dma_dir(data));
+
host->data = NULL;

if (!data->error)
@@ -1115,13 +1171,17 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
host->dma_ch = -1;
spin_unlock_irqrestore(&host->irq_lock, flags);

- if (dma_ch != -1) {
- struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
-
+ if (host->use_adma) {
+ dma_unmap_sg(host->dev, host->data->sg, host->data->sg_len,
+ mmc_get_dma_dir(host->data));
+ host->data->host_cookie = 0;
+ } else if (dma_ch != -1) {
+ struct dma_chan *chan = omap_hsmmc_get_dma_chan(host,
+ host->data);
dmaengine_terminate_all(chan);
dma_unmap_sg(chan->device->dev,
- host->data->sg, host->data->sg_len,
- mmc_get_dma_dir(host->data));
+ host->data->sg, host->data->sg_len,
+ mmc_get_dma_dir(host->data));

host->data->host_cookie = 0;
}
@@ -1216,6 +1276,35 @@ static void hsmmc_command_incomplete(struct omap_hsmmc_host *host,
host->mrq->cmd->error = err;
}

+static void omap_hsmmc_adma_err(struct omap_hsmmc_host *host)
+{
+ u32 admaes, admasal;
+
+ admaes = OMAP_HSMMC_READ(host->base, ADMAES);
+ admasal = OMAP_HSMMC_READ(host->base, ADMASAL);
+
+ switch (admaes & AES_MASK) {
+ case ST_STOP:
+ dev_err(mmc_dev(host->mmc),
+ "ADMA err: ST_STOP, desc at 0x%08x follows the erroneous one\n",
+ admasal);
+ break;
+ case ST_FDS:
+ dev_err(mmc_dev(host->mmc),
+ "ADMA err: ST_FDS, erroneous desc at 0x%08x\n",
+ admasal);
+ break;
+ case ST_TFR:
+ dev_err(mmc_dev(host->mmc),
+ "ADMA err: ST_TFR, desc at 0x%08x follows the erroneous one\n",
+ admasal);
+ break;
+ default:
+ dev_warn(mmc_dev(host->mmc), "Unexpected ADMA error state\n");
+ break;
+ }
+}
+
static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
{
struct mmc_data *data;
@@ -1234,6 +1323,13 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
end_trans = !end_cmd;
host->response_busy = 0;
}
+
+ if (status & ADMAE_EN) {
+ omap_hsmmc_adma_err(host);
+ end_trans = 1;
+ data->error = -EIO;
+ }
+
if (status & (CTO_EN | DTO_EN))
hsmmc_command_incomplete(host, -ETIMEDOUT, end_cmd);
else if (status & (CCRC_EN | DCRC_EN | DEB_EN | CEB_EN |
@@ -1415,6 +1511,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
struct dma_chan *chan)
{
int dma_len;
+ struct device *dev;

if (!next && data->host_cookie &&
data->host_cookie != host->next_data.cookie) {
@@ -1424,11 +1521,15 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
data->host_cookie = 0;
}

+ if (chan)
+ dev = chan->device->dev;
+ else
+ dev = mmc_dev(host->mmc);
+
/* Check if next job is already prepared */
if (next || data->host_cookie != host->next_data.cookie) {
- dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
+ dma_len = dma_map_sg(dev, data->sg, data->sg_len,
mmc_get_dma_dir(data));
-
} else {
dma_len = host->next_data.dma_len;
host->next_data.dma_len = 0;
@@ -1601,8 +1702,58 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
| (req->data->blocks << 16));
set_data_timeout(host, req->data->timeout_ns,
req->data->timeout_clks);
- chan = omap_hsmmc_get_dma_chan(host, req->data);
- dma_async_issue_pending(chan);
+
+ if (host->use_adma) {
+ OMAP_HSMMC_WRITE(host->base, ADMASAL,
+ (u32)host->adma_desc_table_addr);
+ } else {
+ chan = omap_hsmmc_get_dma_chan(host, req->data);
+ dma_async_issue_pending(chan);
+ }
+}
+
+static int omap_hsmmc_write_adma_desc(struct omap_hsmmc_host *host, void *desc,
+ dma_addr_t addr, u16 len, u8 attr)
+{
+ struct omap_hsmmc_adma_desc *dma_desc = desc;
+
+ dma_desc->len = len;
+ dma_desc->addr = (u32)addr;
+ dma_desc->reserved = 0;
+ dma_desc->attr = attr;
+
+ return 0;
+}
+
+static int omap_hsmmc_setup_adma_transfer(struct omap_hsmmc_host *host,
+ struct mmc_request *req)
+{
+ struct mmc_data *data = req->data;
+ struct scatterlist *sg;
+ int i;
+ int len;
+ int ret;
+ dma_addr_t addr;
+ struct omap_hsmmc_adma_desc *dma_desc;
+
+ ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, NULL);
+ if (ret)
+ return ret;
+
+ dma_desc = host->adma_desc_table;
+ for_each_sg(data->sg, sg, host->dma_len, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ WARN_ON(len > ADMA_MAX_LEN);
+ omap_hsmmc_write_adma_desc(host, dma_desc, addr, len,
+ ADMA_DESC_ATTR_VALID |
+ ADMA_DESC_TRANSFER_DATA);
+ dma_desc++;
+ }
+ omap_hsmmc_write_adma_desc(host, dma_desc, 0, 0, ADMA_DESC_ATTR_END |
+ ADMA_DESC_ATTR_VALID);
+
+ return 0;
}

/*
@@ -1633,10 +1784,18 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
return 0;
}

- ret = omap_hsmmc_setup_dma_transfer(host, req);
- if (ret != 0) {
- dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
- return ret;
+ if (host->use_adma) {
+ ret = omap_hsmmc_setup_adma_transfer(host, req);
+ if (ret != 0) {
+ dev_err(mmc_dev(host->mmc), "MMC adma setup failed\n");
+ return ret;
+ }
+ } else {
+ ret = omap_hsmmc_setup_dma_transfer(host, req);
+ if (ret != 0) {
+ dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
+ return ret;
+ }
}
return 0;
}
@@ -1646,11 +1805,18 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
+ struct device *dev;
+ struct dma_chan *c;

if (data->host_cookie) {
- struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+ if (host->use_adma) {
+ dev = mmc_dev(mmc);
+ } else {
+ c = omap_hsmmc_get_dma_chan(host, mrq->data);
+ dev = c->device->dev;
+ }

- dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+ dma_unmap_sg(dev, data->sg, data->sg_len,
mmc_get_dma_dir(data));
data->host_cookie = 0;
}
@@ -1666,7 +1832,8 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq)
return ;
}

- c = omap_hsmmc_get_dma_chan(host, mrq->data);
+ if (!host->use_adma)
+ c = omap_hsmmc_get_dma_chan(host, mrq->data);

if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
&host->next_data, c))
@@ -2325,6 +2492,7 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {

static const struct omap_mmc_of_data omap4_mmc_of_data = {
.reg_offset = 0x100,
+ .controller_flags = OMAP_HSMMC_HAS_HWPARAM,
};
static const struct omap_mmc_of_data am33xx_mmc_of_data = {
.reg_offset = 0x100,
@@ -2334,7 +2502,8 @@ static const struct omap_mmc_of_data am33xx_mmc_of_data = {
static const struct omap_mmc_of_data dra7_mmc_of_data = {
.reg_offset = 0x100,
.controller_flags = OMAP_HSMMC_SWAKEUP_MISSING |
- OMAP_HSMMC_REQUIRE_IODELAY,
+ OMAP_HSMMC_REQUIRE_IODELAY |
+ OMAP_HSMMC_HAS_HWPARAM,
};

static const struct of_device_id omap_mmc_of_match[] = {
@@ -2506,6 +2675,64 @@ static int omap_hsmmc_config_iodelay_pinctrl_state(struct omap_hsmmc_host *host)
return 0;
}

+static int omap_hsmmc_adma_init(struct omap_hsmmc_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ u32 val;
+
+ host->adma_desc_table = dma_alloc_coherent(host->dev, ADMA_DESC_SIZE *
+ (mmc->max_segs + 1),
+ &host->adma_desc_table_addr,
+ GFP_KERNEL);
+ if (!host->adma_desc_table) {
+ dev_err(host->dev, "failed to allocate adma desc table\n");
+ return -ENOMEM;
+ }
+
+ val = OMAP_HSMMC_READ(host->base, HCTL);
+ val |= DMA_SELECT;
+ OMAP_HSMMC_WRITE(host->base, HCTL, val);
+
+ val = OMAP_HSMMC_READ(host->base, CON);
+ val |= DMA_MASTER;
+ OMAP_HSMMC_WRITE(host->base, CON, val);
+
+ return 0;
+}
+
+static void omap_hsmmc_adma_exit(struct omap_hsmmc_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ dma_free_coherent(host->dev, ADMA_DESC_SIZE * (mmc->max_segs + 1),
+ host->adma_desc_table, host->adma_desc_table_addr);
+}
+
+static int omap_hsmmc_dma_init(struct omap_hsmmc_host *host)
+{
+ host->rx_chan = dma_request_chan(host->dev, "rx");
+ if (IS_ERR(host->rx_chan)) {
+ dev_err(mmc_dev(host->mmc), "RX DMA channel request failed\n");
+ return PTR_ERR(host->rx_chan);
+ }
+
+ host->tx_chan = dma_request_chan(host->dev, "tx");
+ if (IS_ERR(host->tx_chan)) {
+ dev_err(mmc_dev(host->mmc), "TX DMA channel request failed\n");
+ return PTR_ERR(host->tx_chan);
+ }
+
+ return 0;
+}
+
+static void omap_hsmmc_dma_exit(struct omap_hsmmc_host *host)
+{
+ if (!IS_ERR_OR_NULL(host->tx_chan))
+ dma_release_channel(host->tx_chan);
+ if (!IS_ERR_OR_NULL(host->rx_chan))
+ dma_release_channel(host->rx_chan);
+}
+
static int omap_hsmmc_probe(struct platform_device *pdev)
{
struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data;
@@ -2513,6 +2740,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
struct omap_hsmmc_host *host = NULL;
struct resource *res;
int ret, irq;
+ u32 val;
const struct of_device_id *match;
const struct omap_mmc_of_data *data;
void __iomem *base;
@@ -2568,6 +2796,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->next_data.cookie = 1;
host->pbias_enabled = 0;
host->vqmmc_enabled = 0;
+ host->use_adma = false;

ret = omap_hsmmc_gpio_init(mmc, host, pdata);
if (ret)
@@ -2629,6 +2858,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->dbclk = NULL;
}

+ if (host->pdata->controller_flags & OMAP_HSMMC_HAS_HWPARAM) {
+ val = OMAP_HSMMC_READ(base, HL_HWINFO);
+ if (val & MADMA_EN)
+ host->use_adma = true;
+ }
+
/* Since we do only SG emulation, we can have as many segs
* as we want. */
mmc->max_segs = 1024;
@@ -2636,7 +2871,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->max_blk_size = 512; /* Block Length at max can be 1024 */
mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
- mmc->max_seg_size = mmc->max_req_size;
+ if (host->use_adma)
+ mmc->max_seg_size = ADMA_MAX_LEN;
+ else
+ mmc->max_seg_size = mmc->max_req_size;

mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
@@ -2656,19 +2894,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (ret)
goto err_pinctrl;

- host->rx_chan = dma_request_chan(&pdev->dev, "rx");
- if (IS_ERR(host->rx_chan)) {
- dev_err(mmc_dev(host->mmc), "RX DMA channel request failed\n");
- ret = PTR_ERR(host->rx_chan);
- goto err_irq;
- }
-
- host->tx_chan = dma_request_chan(&pdev->dev, "tx");
- if (IS_ERR(host->tx_chan)) {
- dev_err(mmc_dev(host->mmc), "TX DMA channel request failed\n");
- ret = PTR_ERR(host->tx_chan);
+ if (host->use_adma)
+ ret = omap_hsmmc_adma_init(host);
+ else
+ ret = omap_hsmmc_dma_init(host);
+ if (ret)
goto err_irq;
- }

/* Request IRQ for MMC operations */
ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
@@ -2724,10 +2955,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
err_slot_name:
mmc_remove_host(mmc);
err_irq:
- if (!IS_ERR_OR_NULL(host->tx_chan))
- dma_release_channel(host->tx_chan);
- if (!IS_ERR_OR_NULL(host->rx_chan))
- dma_release_channel(host->rx_chan);
+ if (host->use_adma)
+ omap_hsmmc_adma_exit(host);
+ else
+ omap_hsmmc_dma_exit(host);
err_pinctrl:
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
@@ -2749,8 +2980,10 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
pm_runtime_get_sync(host->dev);
mmc_remove_host(host->mmc);

- dma_release_channel(host->tx_chan);
- dma_release_channel(host->rx_chan);
+ if (host->use_adma)
+ omap_hsmmc_adma_exit(host);
+ else
+ omap_hsmmc_dma_exit(host);

del_timer_sync(&host->timer);

diff --git a/include/linux/platform_data/hsmmc-omap.h b/include/linux/platform_data/hsmmc-omap.h
index 8e771851e07a..c3f2a34db97a 100644
--- a/include/linux/platform_data/hsmmc-omap.h
+++ b/include/linux/platform_data/hsmmc-omap.h
@@ -28,6 +28,7 @@
#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
#define OMAP_HSMMC_REQUIRE_IODELAY BIT(3)
+#define OMAP_HSMMC_HAS_HWPARAM BIT(4)

struct omap_hsmmc_dev_attr {
u8 flags;
--
2.11.0
Kishon Vijay Abraham I
2017-06-16 12:45:40 UTC
Permalink
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.

Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
.../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 5 +
drivers/mmc/host/omap_hsmmc.c | 152 ++++++++++++++++++++-
2 files changed, 154 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
index 258e25af10f7..dcf0b777c031 100644
--- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
+++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
@@ -20,6 +20,11 @@ Optional properties:
ti,dual-volt: boolean, supports dual voltage cards
<supply-name>-supply: phandle to the regulator device tree node
"supply-name" examples are "vmmc", "vmmc_aux" etc
+pinctrl-names: Should be a list of pinctrl state names and can be "sdr104",
+"hs200_1_8v", "ddr50", "sdr50", "sdr25", "sdr12", "hs", "ddr_1_8v" or
+"default".
+pinctrl-<num>: Phandle referencing pin configuration of the sd/emmc controller.
+See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
ti,non-removable: non-removable slot (like eMMC)
ti,needs-special-reset: Requires a special softreset sequence
ti,needs-special-hs-handling: HSMMC IP needs special setting for handling High Speed
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 967b7c7d4bd5..0887da4f1ff6 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -258,6 +258,18 @@ struct omap_hsmmc_host {
struct timer_list timer;
unsigned long long data_timeout;

+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pinctrl_state;
+ struct pinctrl_state *default_pinctrl_state;
+ struct pinctrl_state *sdr104_pinctrl_state;
+ struct pinctrl_state *hs200_1_8v_pinctrl_state;
+ struct pinctrl_state *ddr50_pinctrl_state;
+ struct pinctrl_state *sdr50_pinctrl_state;
+ struct pinctrl_state *sdr25_pinctrl_state;
+ struct pinctrl_state *sdr12_pinctrl_state;
+ struct pinctrl_state *hs_pinctrl_state;
+ struct pinctrl_state *ddr_1_8v_pinctrl_state;
+
/* return MMC cover switch state, can be NULL if not supported.
*
* possible return values:
@@ -1718,6 +1730,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
static void omap_hsmmc_set_timing(struct omap_hsmmc_host *host)
{
u32 val;
+ int ret;
+ struct pinctrl_state *pinctrl_state;
struct mmc_ios *ios = &host->mmc->ios;

omap_hsmmc_stop_clock(host);
@@ -1727,35 +1741,54 @@ static void omap_hsmmc_set_timing(struct omap_hsmmc_host *host)
switch (ios->timing) {
case MMC_TIMING_UHS_SDR104:
val |= AC12_UHSMC_SDR104;
+ pinctrl_state = host->sdr104_pinctrl_state;
break;
case MMC_TIMING_MMC_HS200:
val |= AC12_UHSMC_SDR104;
+ pinctrl_state = host->hs200_1_8v_pinctrl_state;
break;
case MMC_TIMING_UHS_DDR50:
val |= AC12_UHSMC_DDR50;
+ pinctrl_state = host->ddr50_pinctrl_state;
break;
case MMC_TIMING_UHS_SDR50:
val |= AC12_UHSMC_SDR50;
+ pinctrl_state = host->sdr50_pinctrl_state;
break;
case MMC_TIMING_UHS_SDR25:
val |= AC12_UHSMC_SDR25;
+ pinctrl_state = host->sdr25_pinctrl_state;
break;
case MMC_TIMING_UHS_SDR12:
val |= AC12_UHSMC_SDR12;
+ pinctrl_state = host->sdr12_pinctrl_state;
break;
case MMC_TIMING_SD_HS:
case MMC_TIMING_MMC_HS:
val |= AC12_UHSMC_RES;
+ pinctrl_state = host->hs_pinctrl_state;
break;
case MMC_TIMING_MMC_DDR52:
val |= AC12_UHSMC_RES;
+ pinctrl_state = host->ddr_1_8v_pinctrl_state;
break;
default:
val |= AC12_UHSMC_RES;
+ pinctrl_state = host->default_pinctrl_state;
break;
}
OMAP_HSMMC_WRITE(host->base, AC12, val);

+ if (host->pdata->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY) {
+ ret = pinctrl_select_state(host->pinctrl, pinctrl_state);
+ if (ret) {
+ dev_err(mmc_dev(host->mmc),
+ "failed to select pinctrl state\n");
+ return;
+ }
+ host->pinctrl_state = pinctrl_state;
+ }
+
omap_hsmmc_start_clock(host);
}

@@ -2345,8 +2378,14 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
return ERR_PTR(-ENOMEM); /* out of memory */

legacy = dev_get_platdata(dev);
- if (legacy && legacy->name)
- pdata->name = legacy->name;
+ if (legacy) {
+ if (legacy->name)
+ pdata->name = legacy->name;
+ if (legacy->version)
+ pdata->version = legacy->version;
+ if (legacy->max_freq > 0)
+ pdata->max_freq = legacy->max_freq;
+ }

if (of_find_property(np, "ti,dual-volt", NULL))
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
@@ -2376,6 +2415,101 @@ static inline struct omap_hsmmc_platform_data
}
#endif

+static struct pinctrl_state
+*omap_hsmmc_iodelay_pinctrl_state(struct omap_hsmmc_host *host, char *mode,
+ u32 *caps, u32 capmask)
+{
+ struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV);
+ char *version = host->pdata->version;
+ char str[20];
+
+ if (!(*caps & capmask))
+ goto ret;
+
+ if (version) {
+ sprintf(str, "%s-%s", mode, version);
+ pinctrl_state = pinctrl_lookup_state(host->pinctrl, str);
+ }
+
+ if (IS_ERR(pinctrl_state))
+ pinctrl_state = pinctrl_lookup_state(host->pinctrl, mode);
+
+ if (IS_ERR(pinctrl_state)) {
+ dev_err(host->dev, "no pinctrl state for %s mode", mode);
+ *caps &= ~capmask;
+ }
+
+ret:
+ return pinctrl_state;
+}
+
+static int omap_hsmmc_config_iodelay_pinctrl_state(struct omap_hsmmc_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ struct pinctrl_state *state;
+
+ if (!(host->pdata->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY))
+ return 0;
+
+ host->pinctrl = devm_pinctrl_get(host->dev);
+ if (IS_ERR(host->pinctrl)) {
+ dev_err(host->dev, "Cannot get pinctrl\n");
+ return PTR_ERR(host->pinctrl);
+ }
+
+ state = pinctrl_lookup_state(host->pinctrl, "default");
+ if (IS_ERR(state)) {
+ dev_err(host->dev, "no pinctrl state for default mode\n");
+ return PTR_ERR(state);
+ }
+ host->default_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "sdr104", &mmc->caps,
+ MMC_CAP_UHS_SDR104);
+ if (!IS_ERR(state))
+ host->sdr104_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "ddr50", &mmc->caps,
+ MMC_CAP_UHS_DDR50);
+ if (!IS_ERR(state))
+ host->ddr50_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "sdr50", &mmc->caps,
+ MMC_CAP_UHS_SDR50);
+ if (!IS_ERR(state))
+ host->sdr50_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "sdr25", &mmc->caps,
+ MMC_CAP_UHS_SDR25);
+ if (!IS_ERR(state))
+ host->sdr25_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "sdr12", &mmc->caps,
+ MMC_CAP_UHS_SDR12);
+ if (!IS_ERR(state))
+ host->sdr12_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "ddr_1_8v", &mmc->caps,
+ MMC_CAP_1_8V_DDR);
+ if (!IS_ERR(state))
+ host->ddr_1_8v_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "hs", &mmc->caps,
+ MMC_CAP_MMC_HIGHSPEED |
+ MMC_CAP_SD_HIGHSPEED);
+ if (!IS_ERR(state))
+ host->hs_pinctrl_state = state;
+
+ state = omap_hsmmc_iodelay_pinctrl_state(host, "hs200_1_8v",
+ &mmc->caps2,
+ MMC_CAP2_HS200_1_8V_SDR);
+ if (!IS_ERR(state))
+ host->hs200_1_8v_pinctrl_state = state;
+
+ host->pinctrl_state = host->default_pinctrl_state;
+ return 0;
+}
+
static int omap_hsmmc_probe(struct platform_device *pdev)
{
struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data;
@@ -2523,6 +2657,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)

omap_hsmmc_set_capabilities(host);

+ ret = omap_hsmmc_config_iodelay_pinctrl_state(host);
+ if (ret)
+ goto err_pinctrl;
+
host->rx_chan = dma_request_chan(&pdev->dev, "rx");
if (IS_ERR(host->rx_chan)) {
dev_err(mmc_dev(host->mmc), "RX DMA channel request failed\n");
@@ -2595,6 +2733,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
dma_release_channel(host->tx_chan);
if (!IS_ERR_OR_NULL(host->rx_chan))
dma_release_channel(host->rx_chan);
+err_pinctrl:
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
pm_runtime_dont_use_autosuspend(host->dev);
@@ -2730,6 +2869,7 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
{
struct omap_hsmmc_host *host;
unsigned long flags;
+ int ret;

host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_restore(host);
@@ -2746,7 +2886,13 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
} else {
- pinctrl_pm_select_default_state(host->dev);
+ if (host->pinctrl) {
+ ret = pinctrl_select_state(host->pinctrl,
+ host->pinctrl_state);
+ if (ret)
+ dev_err(mmc_dev(host->mmc),
+ "failed to activate pinctrl state\n");
+ }
}
spin_unlock_irqrestore(&host->irq_lock, flags);
return 0;
--
2.11.0
Tony Lindgren
2017-06-19 06:02:57 UTC
Permalink
Post by Kishon Vijay Abraham I
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.
Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.
This does not seem to apply against Linux next for me for
the binding. Fixing that, compile fails for me with:

drivers/mmc/host/omap_hsmmc.c:2384:13: error:
'struct omap_hsmmc_platform_data' has no member named 'version'

So will wait for updates before giving this a try.

Regards,

Tony
Kishon Vijay Abraham I
2017-06-19 06:36:33 UTC
Permalink
Hi Tony,
Post by Tony Lindgren
Post by Kishon Vijay Abraham I
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.
Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.
This does not seem to apply against Linux next for me for
'struct omap_hsmmc_platform_data' has no member named 'version'
This series has a dependency with
https://www.spinics.net/lists/arm-kernel/msg586215.html series

*version* member is added in patch with $subject
"ARM: OMAP2+: Add pdata-quirks for MMC/SD on DRA74x EVM"

Thanks
Kishon
Tony Lindgren
2017-06-19 07:25:23 UTC
Permalink
Post by Kishon Vijay Abraham I
Hi Tony,
Post by Tony Lindgren
Post by Kishon Vijay Abraham I
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.
Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.
This does not seem to apply against Linux next for me for
'struct omap_hsmmc_platform_data' has no member named 'version'
This series has a dependency with
https://www.spinics.net/lists/arm-kernel/msg586215.html series
*version* member is added in patch with $subject
"ARM: OMAP2+: Add pdata-quirks for MMC/SD on DRA74x EVM"
Ah sorry I keep forgetting that. Looks like the only thing
pending with that series is that you were going to send
an update for omap3-overo-base.

BTW, we really should get rid of the mach-omap2/hsmmc.c,
I think overo is the only user for it now.

Regards,

Tony
Kishon Vijay Abraham I
2017-06-19 07:54:43 UTC
Permalink
Hi Tony,
Post by Tony Lindgren
Post by Kishon Vijay Abraham I
Hi Tony,
Post by Tony Lindgren
Post by Kishon Vijay Abraham I
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.
Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.
This does not seem to apply against Linux next for me for
'struct omap_hsmmc_platform_data' has no member named 'version'
This series has a dependency with
https://www.spinics.net/lists/arm-kernel/msg586215.html series
*version* member is added in patch with $subject
"ARM: OMAP2+: Add pdata-quirks for MMC/SD on DRA74x EVM"
Ah sorry I keep forgetting that. Looks like the only thing
pending with that series is that you were going to send
an update for omap3-overo-base.
I've sent that as a separate patch [1]. But I can resend the entire series if
that's what you'd prefer.

Thanks
Kishon

[1] -> http://www.spinics.net/lists/devicetree/msg180747.html
Post by Tony Lindgren
BTW, we really should get rid of the mach-omap2/hsmmc.c,
I think overo is the only user for it now.
Regards,
Tony
Tony Lindgren
2017-06-19 14:33:39 UTC
Permalink
Post by Kishon Vijay Abraham I
Hi Tony,
Post by Tony Lindgren
Post by Kishon Vijay Abraham I
Hi Tony,
Post by Tony Lindgren
Post by Kishon Vijay Abraham I
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.
Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.
This does not seem to apply against Linux next for me for
'struct omap_hsmmc_platform_data' has no member named 'version'
This series has a dependency with
https://www.spinics.net/lists/arm-kernel/msg586215.html series
*version* member is added in patch with $subject
"ARM: OMAP2+: Add pdata-quirks for MMC/SD on DRA74x EVM"
Ah sorry I keep forgetting that. Looks like the only thing
pending with that series is that you were going to send
an update for omap3-overo-base.
I've sent that as a separate patch [1]. But I can resend the entire series if
that's what you'd prefer.
Oh sorry I had already untagged that thread, I probably did
not notice your updated patch. It's best to resend that series
right after -rc1 so we get it to Linux next for few weeks
of testing. Right now there's a dependency to the related
MMC patches, and patch 8/8 conflicts with the unused regulator
removal patch.

Regards,

Tony
Post by Kishon Vijay Abraham I
[1] -> http://www.spinics.net/lists/devicetree/msg180747.html
Rob Herring
2017-06-23 18:52:45 UTC
Permalink
Post by Kishon Vijay Abraham I
The data manual of J6/J6 Eco recommends to set different IODELAY values
depending on the mode in which the MMC/SD is enumerated in order to
ensure IO timings are met.
Add support to set the IODELAY values depending on the various MMC
modes using the pinctrl APIs.
---
.../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 5 +
drivers/mmc/host/omap_hsmmc.c | 152 ++++++++++++++++++++-
2 files changed, 154 insertions(+), 3 deletions(-)
Kishon Vijay Abraham I
2017-06-16 12:45:33 UTC
Permalink
Set the clock rate of the functional clock to the max frequency
that is passed to the driver either using pdata or dt.

Also remove unnecessary setting of host->fclk to NULL.

This is in preparation for supporting high frequency modes
of operation.

Signed-off-by: Kishon Vijay Abraham I <***@ti.com>
Signed-off-by: Sekhar Nori <***@ti.com>
---
drivers/mmc/host/omap_hsmmc.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 680934d50919..3ca18e28be01 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2153,22 +2153,29 @@ static int omap_hsmmc_probe(struct platform_device *pdev)

mmc->ops = &omap_hsmmc_ops;

- mmc->f_min = OMAP_MMC_MIN_CLOCK;
-
- if (pdata->max_freq > 0)
- mmc->f_max = pdata->max_freq;
- else if (mmc->f_max == 0)
- mmc->f_max = OMAP_MMC_MAX_CLOCK;
-
spin_lock_init(&host->irq_lock);

host->fclk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(host->fclk)) {
ret = PTR_ERR(host->fclk);
- host->fclk = NULL;
goto err1;
}

+ mmc->f_min = OMAP_MMC_MIN_CLOCK;
+ if (pdata->max_freq)
+ mmc->f_max = pdata->max_freq;
+
+ if (mmc->f_max) {
+ ret = clk_set_rate(host->fclk, mmc->f_max);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to set clock to %d\n",
+ mmc->f_max);
+ goto err1;
+ }
+ } else {
+ mmc->f_max = OMAP_MMC_MAX_CLOCK;
+ }
+
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
omap_hsmmc_ops.multi_io_quirk = omap_hsmmc_multi_io_quirk;
--
2.11.0
Ulf Hansson
2017-07-11 13:10:37 UTC
Permalink
This is the final part of the series originally sent as
part of [2].
This series adds UHS, HS200, DDR mode and ADMA support to
omap_hsmmc driver used to improve the throughput of MMC/SD in dra7
SoCs.
*) No more updating omap2plus_defconfig or multi_v7_defconfig is
required, so those patches are removed.
*) Addressed Rob Herring's comment on implementing a function
instead of having a macro while getting pinctrl state.
This series is created on top of [3], [4], [5] AND [6]
(i.e after
ARM: dts: omap3-overo: Remove "vqmmc-supply" property from MMC dt node
omap_hsmmc: use mmc_regulator_get_supply() to get regulators
omap*: Fixes/Cleanups for MMC devicetree node
ARM: dts: Add iodelay data for MMC)
The functionality implemented in this series was sent before ([1]) but
was never followed up since supporting high speed modes in dra7 required
IODelay values to be configured. With IODelay driver being merged into
kernel, sending it as a fresh series with support for configuring IODelay
values.
Is it safe to queue this via mmc tree for 4.14 or is there a
dependency I must consider? The above didn't quite tell me that, could
you please elaborate.
Suggestions of migrating to sdhci driver (from omap_hsmmc driver) is not
addressed since
*) tuning ratio of MMC in dra7 is different from sdhci
*) IOdelay is required for dra7
*) GPIO based card detect is not supported in sdhci
Lots of sdhci drivers calls mmc_of_parse(), and uses the mmc slot gpio
APIs, so I don't this this is correct statement.
*) Some of the registers don't always have correct values as in sdhci
(like PSTATE).
Supporting all of these in sdhci will result in adding lot of quirks in
sdhci driver.
Is it really that much different? It would be nice if some really took
on the challenge of converting omap_hsmmc into an sdhci variant.

Especially by looking at the number new lines added in this series, I
am starting to think that we have reached this point. That in
conjunction with that sdhci is maintained and well supported,
comparing to what the omap_hsmmc driver seems to be.
This series has been tested on beagleboard, pandaboard, beaglebone-black,
beaglebone, am335x-evm, am437x-evm, dra7xx-evm, dra72x-evm, am571x-idk
and am572x-idk.
I can split the series to go into Ulf Hansson's tree and Tony's tree
separately if that is required.
[1] -> https://lkml.org/lkml/2015/8/25/213
[2] -> https://lkml.org/lkml/2017/5/19/19
[3] -> https://lkml.org/lkml/2017/6/9/206
[4] -> https://lkml.org/lkml/2017/6/7/169
[5] -> https://www.spinics.net/lists/arm-kernel/msg586215.html /
http://www.spinics.net/lists/devicetree/msg180747.html
mmc: host: omap_hsmmc: Support pbias and vmmc_aux to switch to 1.8v
mmc: host: omap_hsmmc: Separate setting voltage capabilities from bus
power
mmc: host: omap_hsmmc: Remove incorrect voltage switch sequence
mmc: host: omap_hsmmc: Add voltage switch support for UHS SD card
mmc: host: omap_hsmmc: Set clk rate to the max frequency
mmc: host: omap_hsmmc: Add tuning support
mmc: host: omap_hsmmc: Allow io voltage switch even for fixed vdd
mmc: host: omap_hsmmc: Prepare *set_timing() to be used for iodelay
setting
mmc: host: omap_hsmmc: Add new compatible string to support dra7
mmc: host: omap_hsmmc: Fix error path sequence
mmc: host: omap_hsmmc: Add support to set IODELAY values
mmc: host: omap_hsmmc: Remove *use_dma* member
mmc: host: omap_hsmmc: Enable ADMA2
ARM: dts: dra7: Use new dra7-specific compatible string
ARM: dts: dra7: Add supported MMC/SD modes in MMC dt nodes
mmc: host: omap_hsmmc: Add software timer when timeout greater than
hardware capablility
.../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 6 +
arch/arm/boot/dts/am571x-idk.dts | 1 +
arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts | 1 +
arch/arm/boot/dts/am57xx-beagle-x15.dts | 6 +
arch/arm/boot/dts/am57xx-idk-common.dtsi | 1 +
arch/arm/boot/dts/dra7.dtsi | 22 +-
drivers/mmc/host/omap_hsmmc.c | 1179 ++++++++++++++++----
include/linux/platform_data/hsmmc-omap.h | 2 +
8 files changed, 1021 insertions(+), 197 deletions(-)
--
2.11.0
Kind regards
Ulf Hansson
Kishon Vijay Abraham I
2017-07-11 13:57:00 UTC
Permalink
Hi Ulf,
Post by Ulf Hansson
This is the final part of the series originally sent as
part of [2].
This series adds UHS, HS200, DDR mode and ADMA support to
omap_hsmmc driver used to improve the throughput of MMC/SD in dra7
SoCs.
*) No more updating omap2plus_defconfig or multi_v7_defconfig is
required, so those patches are removed.
*) Addressed Rob Herring's comment on implementing a function
instead of having a macro while getting pinctrl state.
This series is created on top of [3], [4], [5] AND [6]
(i.e after
ARM: dts: omap3-overo: Remove "vqmmc-supply" property from MMC dt node
omap_hsmmc: use mmc_regulator_get_supply() to get regulators
omap*: Fixes/Cleanups for MMC devicetree node
ARM: dts: Add iodelay data for MMC)
The functionality implemented in this series was sent before ([1]) but
was never followed up since supporting high speed modes in dra7 required
IODelay values to be configured. With IODelay driver being merged into
kernel, sending it as a fresh series with support for configuring IODelay
values.
Is it safe to queue this via mmc tree for 4.14 or is there a
dependency I must consider? The above didn't quite tell me that, could
you please elaborate.
There is a dependency with https://www.spinics.net/lists/arm-kernel/msg586215.html.

I'll resend the series after all the platform data specific stuff is merged.
Post by Ulf Hansson
Suggestions of migrating to sdhci driver (from omap_hsmmc driver) is not
addressed since
*) tuning ratio of MMC in dra7 is different from sdhci
*) IOdelay is required for dra7
*) GPIO based card detect is not supported in sdhci
Lots of sdhci drivers calls mmc_of_parse(), and uses the mmc slot gpio
APIs, so I don't this this is correct statement.
*) Some of the registers don't always have correct values as in sdhci
(like PSTATE).
Supporting all of these in sdhci will result in adding lot of quirks in
sdhci driver.
Is it really that much different? It would be nice if some really took
on the challenge of converting omap_hsmmc into an sdhci variant.
Okay. I'll give that a shot and see how far I can get.

Thanks
Kishon
Kishon Vijay Abraham I
2017-07-20 13:36:47 UTC
Permalink
Hi Ulf,
Post by Kishon Vijay Abraham I
Hi Ulf,
Post by Ulf Hansson
This is the final part of the series originally sent as
part of [2].
This series adds UHS, HS200, DDR mode and ADMA support to
omap_hsmmc driver used to improve the throughput of MMC/SD in dra7
SoCs.
*) No more updating omap2plus_defconfig or multi_v7_defconfig is
required, so those patches are removed.
*) Addressed Rob Herring's comment on implementing a function
instead of having a macro while getting pinctrl state.
This series is created on top of [3], [4], [5] AND [6]
(i.e after
ARM: dts: omap3-overo: Remove "vqmmc-supply" property from MMC dt node
omap_hsmmc: use mmc_regulator_get_supply() to get regulators
omap*: Fixes/Cleanups for MMC devicetree node
ARM: dts: Add iodelay data for MMC)
The functionality implemented in this series was sent before ([1]) but
was never followed up since supporting high speed modes in dra7 required
IODelay values to be configured. With IODelay driver being merged into
kernel, sending it as a fresh series with support for configuring IODelay
values.
Is it safe to queue this via mmc tree for 4.14 or is there a
dependency I must consider? The above didn't quite tell me that, could
you please elaborate.
There is a dependency with https://www.spinics.net/lists/arm-kernel/msg586215.html.
I'll resend the series after all the platform data specific stuff is merged.
Post by Ulf Hansson
Suggestions of migrating to sdhci driver (from omap_hsmmc driver) is not
addressed since
*) tuning ratio of MMC in dra7 is different from sdhci
*) IOdelay is required for dra7
*) GPIO based card detect is not supported in sdhci
Lots of sdhci drivers calls mmc_of_parse(), and uses the mmc slot gpio
APIs, so I don't this this is correct statement.
*) Some of the registers don't always have correct values as in sdhci
(like PSTATE).
Supporting all of these in sdhci will result in adding lot of quirks in
sdhci driver.
Is it really that much different? It would be nice if some really took
on the challenge of converting omap_hsmmc into an sdhci variant.
Okay. I'll give that a shot and see how far I can get.
I created a sdhci variant and was able to get it working with high speed mode
in dra7.

I have a query on sdhci_set_power_noreg() in sdhci.c. sdhci_set_power_noreg
sets SDHCI_POWER_CONTROL based on *vdd* value. However SDHCI_POWER_CONTROL is
for setting bus voltage and need not be based on *vdd*. IMHO
SDHCI_POWER_CONTROL should be set based on "ios->signal_voltage".

Let me know if my understanding is wrong.

Thanks
Kishon

Loading...