2018-07-24 14:31:00

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 00/10] Update the pad autocal procedure

Hi all,

Update the tegra_sdhci_pad_autocalib() pad drive strength calibration
procedure to match the ones specified in the TRMs of the more recent
SoCs. This was tested on Tegra186, Tegra210, and Tegra124, although it
should not break things older generations either.

This series depends on the "Tegra SDHCI enable 1.8 V signaling on
Tegar210 and Tegra186" series posted earlier.

Aapo Vienamo (10):
mmc: tegra: Poll for calibration completion
mmc: tegra: Set calibration pad voltage reference
mmc: tegra: Power on the calibration pad
mmc: tegra: Disable card clock during pad calibration
dt-bindings: Add Tegra SDHCI pad pdpu offset bindings
mmc: tegra: Program pad autocal offsets from dt
arm64: dts: tegra186: Add sdmmc pad auto calibration offsets
arm64: dts: tegra210: Add sdmmc pad auto calibration offsets
mmc: tegra: Perform pad calibration after voltage switch
mmc: tegra: Enable pad calibration on Tegra210 and Tegra186

.../bindings/mmc/nvidia,tegra20-sdhci.txt | 32 +++
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 20 ++
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 12 +
drivers/mmc/host/sdhci-tegra.c | 242 ++++++++++++++++++++-
4 files changed, 297 insertions(+), 9 deletions(-)

--
2.7.4



2018-07-24 14:31:01

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 01/10] mmc: tegra: Poll for calibration completion

Implement polling with 10 ms timeout for automatic pad drive strength
calibration.

Signed-off-by: Aapo Vienamo <[email protected]>
---
drivers/mmc/host/sdhci-tegra.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index f108c48..e40ca43 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -49,6 +49,9 @@
#define SDHCI_AUTO_CAL_START BIT(31)
#define SDHCI_AUTO_CAL_ENABLE BIT(29)

+#define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
+#define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
+
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
@@ -198,13 +201,24 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)

static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
{
- u32 val;
+ unsigned timeout = 10;
+ u32 reg;

- mdelay(1);
+ reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
+ reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
+ sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
+ udelay(1);
+
+ do {
+ reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_STATUS);
+ if (!(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE))
+ break;
+ mdelay(1);
+ timeout--;
+ } while (timeout);

- val = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
- val |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
- sdhci_writel(host,val, SDHCI_TEGRA_AUTO_CAL_CONFIG);
+ if (timeout == 0)
+ dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
}

static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
--
2.7.4


2018-07-24 14:35:36

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 02/10] mmc: tegra: Set calibration pad voltage reference

Configure the voltage reference used by the automatic pad drive strength
calibration procedure. The value is a magic number from the TRM.

Signed-off-by: Aapo Vienamo <[email protected]>
---
drivers/mmc/host/sdhci-tegra.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index e40ca43..6008e2f 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -49,6 +49,10 @@
#define SDHCI_AUTO_CAL_START BIT(31)
#define SDHCI_AUTO_CAL_ENABLE BIT(29)

+#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
+#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
+#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
+
#define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
#define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)

@@ -152,7 +156,7 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
- u32 misc_ctrl, clk_ctrl;
+ u32 misc_ctrl, clk_ctrl, pad_ctrl;

sdhci_reset(host, mask);

@@ -193,8 +197,14 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);

- if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
+ if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
+ pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
+ pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
+ pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
+ sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
+
tegra_host->pad_calib_required = true;
+ }

tegra_host->ddr_signaling = false;
}
--
2.7.4


2018-07-24 14:35:51

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 05/10] dt-bindings: Add Tegra SDHCI pad pdpu offset bindings

Add bindings documentation for pad pull up and pull down offset values to be
programmed before executing automatic pad drive strength calibration.

Signed-off-by: Aapo Vienamo <[email protected]>
---
.../bindings/mmc/nvidia,tegra20-sdhci.txt | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt b/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt
index 90c214d..949f616 100644
--- a/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt
+++ b/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt
@@ -24,6 +24,7 @@ Required properties:
Optional properties:
- power-gpios : Specify GPIOs for power control

+Optional properties for Tegra210 and Tegra186:
Example:

sdhci@c8000200 {
@@ -45,6 +46,33 @@ Optional properties for Tegra210 and Tegra186:
for controllers supporting multiple voltage levels. The order of names
should correspond to the pin configuration states in pinctrl-0 and
pinctrl-1.
+- pad-autocal-pull-up-offset-3v3, pad-autocal-pull-down-offset-3v3 :
+ Specify drive strength calibration offsets for 3.3 V signaling modes.
+- pad-autocal-pull-up-offset-1v8, pad-autocal-pull-down-offset-1v8 :
+ Specify drive strength calibration offsets for 1.8 V signaling modes.
+- pad-autocal-pull-up-offset-3v3-timeout,
+ pad-autocal-pull-down-offset-3v3-timeout : Specify drive strength
+ used as a fallback in case the automatic calibration times out on a
+ 3.3 V signaling mode.
+- pad-autocal-pull-up-offset-1v8-timeout,
+ pad-autocal-pull-down-offset-1v8-timeout : Specify drive strength
+ used as a fallback in case the automatic calibration times out on a
+ 1.8 V signaling mode.
+- pad-autocal-pull-up-offset-sdr104,
+ pad-autocal-pull-down-offset-sdr104 : Specify drive strength
+ calibration offsets for SDR104 mode.
+- pad-autocal-pull-up-offset-hs400,
+ pad-autocal-pull-down-offset-hs400 : Specify drive strength
+ calibration offsets for HS400 mode.
+
+ Notes on the pad calibration pull up and pulldown offset values:
+ - The property values are drive codes which are programmed into the
+ PD_OFFSET and PU_OFFSET sections of the
+ SDHCI_TEGRA_AUTO_CAL_CONFIG register.
+ - A higher value corresponds to higher drive strength. Please refer
+ to the reference manual of the SoC for correct values.
+ - The SDR104 and HS400 timing specific values are used in
+ corresponding modes if specified.

Example:
sdhci@700b0000 {
@@ -58,5 +86,9 @@ sdhci@700b0000 {
pinctrl-names = "sdmmc-3v3", "sdmmc-1v8";
pinctrl-0 = <&sdmmc1_3v3>;
pinctrl-1 = <&sdmmc1_1v8>;
+ pad-autocal-pull-up-offset-3v3 = <0x00>;
+ pad-autocal-pull-down-offset-3v3 = <0x7d>;
+ pad-autocal-pull-up-offset-1v8 = <0x7b>;
+ pad-autocal-pull-down-offset-1v8 = <0x7b>;
status = "disabled";
};
--
2.7.4


2018-07-24 14:35:56

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 06/10] mmc: tegra: Program pad autocal offsets from dt

Parse the pad drive strength calibration offsets from the device tree.
Program the calibration offsets in accordance with the current signaling
mode.

Signed-off-by: Aapo Vienamo <[email protected]>
---
drivers/mmc/host/sdhci-tegra.c | 147 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 146 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 0de74f4..78781bd 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -48,6 +48,7 @@
#define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4
#define SDHCI_AUTO_CAL_START BIT(31)
#define SDHCI_AUTO_CAL_ENABLE BIT(29)
+#define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff

#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
@@ -71,6 +72,22 @@ struct sdhci_tegra_soc_data {
u32 nvquirks;
};

+/* Magic pull up and pull down pad calibration offsets */
+struct sdhci_tegra_autocal_offsets {
+ u8 pull_up_3v3;
+ u8 pull_down_3v3;
+ u8 pull_up_3v3_timeout;
+ u8 pull_down_3v3_timeout;
+ u8 pull_up_1v8;
+ u8 pull_down_1v8;
+ u8 pull_up_1v8_timeout;
+ u8 pull_down_1v8_timeout;
+ u8 pull_up_sdr104;
+ u8 pull_down_sdr104;
+ u8 pull_up_hs400;
+ u8 pull_down_hs400;
+};
+
struct sdhci_tegra {
const struct sdhci_tegra_soc_data *soc_data;
struct gpio_desc *power_gpio;
@@ -82,6 +99,8 @@ struct sdhci_tegra {
struct pinctrl *pinctrl_sdmmc;
struct pinctrl_state *pinctrl_state_3v3;
struct pinctrl_state *pinctrl_state_1v8;
+
+ struct sdhci_tegra_autocal_offsets autocal_offsets;
};

static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
@@ -248,12 +267,45 @@ static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
return orig_enabled;
}

+static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
+ u16 pdpu)
+{
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
+ reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
+ reg |= pdpu;
+ sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
+}
+
static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
+ struct sdhci_tegra_autocal_offsets offsets =
+ tegra_host->autocal_offsets;
+ struct mmc_ios *ios = &host->mmc->ios;
unsigned timeout = 10;
bool card_clk_enabled;
+ u16 pdpu;
u32 reg;

+ switch (ios->timing) {
+ case MMC_TIMING_UHS_SDR104:
+ pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
+ break;
+ case MMC_TIMING_MMC_HS400:
+ pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
+ break;
+ default:
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+ pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
+ else
+ pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
+ }
+
+ tegra_sdhci_set_pad_autocal_offset(host, pdpu);
+
card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);

tegra_sdhci_configure_cal_pad(host, true);
@@ -275,8 +327,99 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)

tegra_sdhci_configure_card_clk(host, card_clk_enabled);

- if (timeout == 0)
+ if (timeout == 0) {
dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
+
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+ pdpu = offsets.pull_down_1v8_timeout << 8 |
+ offsets.pull_up_1v8_timeout;
+ else
+ pdpu = offsets.pull_down_3v3_timeout << 8 |
+ offsets.pull_up_3v3_timeout;
+
+ tegra_sdhci_set_pad_autocal_offset(host, pdpu);
+ }
+}
+
+static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
+ struct sdhci_tegra_autocal_offsets *autocal =
+ &tegra_host->autocal_offsets;
+ int err;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-up-offset-3v3",
+ &autocal->pull_up_3v3);
+ if (err)
+ autocal->pull_up_3v3 = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-down-offset-3v3",
+ &autocal->pull_down_3v3);
+ if (err)
+ autocal->pull_down_3v3 = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-up-offset-1v8",
+ &autocal->pull_up_1v8);
+ if (err)
+ autocal->pull_up_1v8 = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-down-offset-1v8",
+ &autocal->pull_down_1v8);
+ if (err)
+ autocal->pull_down_1v8 = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-up-offset-3v3-timeout",
+ &autocal->pull_up_3v3);
+ if (err)
+ autocal->pull_up_3v3_timeout = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-down-offset-3v3-timeout",
+ &autocal->pull_down_3v3);
+ if (err)
+ autocal->pull_down_3v3_timeout = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-up-offset-1v8-timeout",
+ &autocal->pull_up_1v8);
+ if (err)
+ autocal->pull_up_1v8_timeout = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-down-offset-1v8-timeout",
+ &autocal->pull_down_1v8);
+ if (err)
+ autocal->pull_down_1v8_timeout = 0;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-up-offset-sdr104",
+ &autocal->pull_up_sdr104);
+ if (err)
+ autocal->pull_up_sdr104 = autocal->pull_up_1v8;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-down-offset-sdr104",
+ &autocal->pull_down_sdr104);
+ if (err)
+ autocal->pull_down_sdr104 = autocal->pull_down_1v8;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-up-offset-hs400",
+ &autocal->pull_up_hs400);
+ if (err)
+ autocal->pull_up_hs400 = autocal->pull_up_1v8;
+
+ err = device_property_read_u8(host->mmc->parent,
+ "pad-autocal-pull-down-offset-hs400",
+ &autocal->pull_down_hs400);
+ if (err)
+ autocal->pull_down_hs400 = autocal->pull_down_1v8;
}

static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
@@ -638,6 +781,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
host->mmc->caps |= MMC_CAP_1_8V_DDR;

+ tegra_sdhci_parse_pad_autocal_dt(host);
+
tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
GPIOD_OUT_HIGH);
if (IS_ERR(tegra_host->power_gpio)) {
--
2.7.4


2018-07-24 14:35:57

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 07/10] arm64: dts: tegra186: Add sdmmc pad auto calibration offsets

Add the calibration offset properties used for automatic pad drive
strength calibration.

Signed-off-by: Aapo Vienamo <[email protected]>
---
arch/arm64/boot/dts/nvidia/tegra186.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
index 7669756..ee8e6cf 100644
--- a/arch/arm64/boot/dts/nvidia/tegra186.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -240,6 +240,12 @@
pinctrl-names = "sdmmc-3v3", "sdmmc-1v8";
pinctrl-0 = <&sdmmc1_3v3>;
pinctrl-1 = <&sdmmc1_1v8>;
+ pad-autocal-pull-up-offset-3v3-timeout = <0x07>;
+ pad-autocal-pull-down-offset-3v3-timeout = <0x06>;
+ pad-autocal-pull-up-offset-1v8-timeout = <0x07>;
+ pad-autocal-pull-down-offset-1v8-timeout = <0x07>;
+ pad-autocal-pull-up-offset-sdr104 = <0x03>;
+ pad-autocal-pull-down-offset-sdr104 = <0x05>;
status = "disabled";
};

@@ -254,6 +260,10 @@
pinctrl-names = "sdmmc-3v3", "sdmmc-1v8";
pinctrl-0 = <&sdmmc2_3v3>;
pinctrl-1 = <&sdmmc2_1v8>;
+ pad-autocal-pull-up-offset-3v3-timeout = <0x07>;
+ pad-autocal-pull-down-offset-3v3-timeout = <0x06>;
+ pad-autocal-pull-up-offset-1v8-timeout = <0x07>;
+ pad-autocal-pull-down-offset-1v8-timeout = <0x07>;
status = "disabled";
};

@@ -268,6 +278,12 @@
pinctrl-names = "sdmmc-3v3", "sdmmc-1v8";
pinctrl-0 = <&sdmmc3_3v3>;
pinctrl-1 = <&sdmmc3_1v8>;
+ pad-autocal-pull-up-offset-1v8 = <0x00>;
+ pad-autocal-pull-down-offset-1v8 = <0x7a>;
+ pad-autocal-pull-up-offset-3v3-timeout = <0x07>;
+ pad-autocal-pull-down-offset-3v3-timeout = <0x06>;
+ pad-autocal-pull-up-offset-1v8-timeout = <0x07>;
+ pad-autocal-pull-down-offset-1v8-timeout = <0x07>;
status = "disabled";
};

@@ -279,6 +295,10 @@
clock-names = "sdhci";
resets = <&bpmp TEGRA186_RESET_SDMMC4>;
reset-names = "sdhci";
+ pad-autocal-pull-up-offset-hs400 = <0x05>;
+ pad-autocal-pull-down-offset-hs400 = <0x05>;
+ pad-autocal-pull-up-offset-1v8-timeout = <0x0a>;
+ pad-autocal-pull-down-offset-1v8-timeout = <0x0a>;
status = "disabled";
};

--
2.7.4


2018-07-24 14:36:00

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 08/10] arm64: dts: tegra210: Add sdmmc pad auto calibration offsets

Add the calibration offset properties used for automatic pad drive
strength calibration.

Signed-off-by: Aapo Vienamo <[email protected]>
---
arch/arm64/boot/dts/nvidia/tegra210.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra210.dtsi b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
index bc1918e..467bdfc 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210.dtsi
@@ -1051,6 +1051,10 @@
pinctrl-names = "sdmmc-3v3", "sdmmc-1v8";
pinctrl-0 = <&sdmmc1_3v3>;
pinctrl-1 = <&sdmmc1_1v8>;
+ pad-autocal-pull-up-offset-3v3 = <0x00>;
+ pad-autocal-pull-down-offset-3v3 = <0x7d>;
+ pad-autocal-pull-up-offset-1v8 = <0x7b>;
+ pad-autocal-pull-down-offset-1v8 = <0x7b>;
status = "disabled";
};

@@ -1062,6 +1066,8 @@
clock-names = "sdhci";
resets = <&tegra_car 9>;
reset-names = "sdhci";
+ pad-autocal-pull-up-offset-1v8 = <0x05>;
+ pad-autocal-pull-down-offset-1v8 = <0x05>;
status = "disabled";
};

@@ -1076,6 +1082,10 @@
pinctrl-names = "sdmmc-3v3", "sdmmc-1v8";
pinctrl-0 = <&sdmmc3_3v3>;
pinctrl-1 = <&sdmmc3_1v8>;
+ pad-autocal-pull-up-offset-3v3 = <0x00>;
+ pad-autocal-pull-down-offset-3v3 = <0x7d>;
+ pad-autocal-pull-up-offset-1v8 = <0x7b>;
+ pad-autocal-pull-down-offset-1v8 = <0x7b>;
status = "disabled";
};

@@ -1087,6 +1097,8 @@
clock-names = "sdhci";
resets = <&tegra_car 15>;
reset-names = "sdhci";
+ pad-autocal-pull-up-offset-1v8 = <0x05>;
+ pad-autocal-pull-down-offset-1v8 = <0x05>;
status = "disabled";
};

--
2.7.4


2018-07-24 14:36:01

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 09/10] mmc: tegra: Perform pad calibration after voltage switch

Run the automatic pad calibration after voltage switching if
tegra_host->pad_calib_required is set.

Signed-off-by: Aapo Vienamo <[email protected]>
---
drivers/mmc/host/sdhci-tegra.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 78781bd..529aa4e7 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -537,6 +537,8 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
int ret = 0;

if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
@@ -551,6 +553,9 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
}

+ if (tegra_host->pad_calib_required)
+ tegra_sdhci_pad_autocalib(host);
+
return ret;
}

--
2.7.4


2018-07-24 14:36:17

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 03/10] mmc: tegra: Power on the calibration pad

Automatic pad drive strength calibration is performed on a separate pad
identical to the ones used for driving the actual bus. Power on the
calibration pad during the calibration procedure and power it off
afterwards to save power.

Signed-off-by: Aapo Vienamo <[email protected]>
---
drivers/mmc/host/sdhci-tegra.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 6008e2f..61067b7 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -209,11 +209,30 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
tegra_host->ddr_signaling = false;
}

+static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
+{
+ u32 reg;
+
+ /*
+ * Enable or disable the additional I/O pad used by the drive strength
+ * calibration process.
+ */
+ reg = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
+ if (enable)
+ reg |= SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD;
+ else
+ reg &= ~SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD;
+ sdhci_writel(host, reg, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
+ udelay(1);
+}
+
static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
{
unsigned timeout = 10;
u32 reg;

+ tegra_sdhci_configure_cal_pad(host, true);
+
reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
@@ -227,6 +246,8 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
timeout--;
} while (timeout);

+ tegra_sdhci_configure_cal_pad(host, false);
+
if (timeout == 0)
dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
}
--
2.7.4


2018-07-24 15:29:00

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 04/10] mmc: tegra: Disable card clock during pad calibration

Disable the card clock during automatic pad drive strength calibration
and re-enable it aftewards.

Signed-off-by: Aapo Vienamo <[email protected]>
---
drivers/mmc/host/sdhci-tegra.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 61067b7..0de74f4 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -52,6 +52,7 @@
#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
+#define SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD BIT(31)

#define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
#define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
@@ -226,11 +227,35 @@ static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
udelay(1);
}

+static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
+{
+ bool orig_enabled;
+ u32 reg;
+
+ reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ orig_enabled = reg & SDHCI_CLOCK_CARD_EN;
+
+ if (orig_enabled == enable)
+ return orig_enabled;
+
+ if (enable)
+ reg |= SDHCI_CLOCK_CARD_EN;
+ else
+ reg &= ~SDHCI_CLOCK_CARD_EN;
+
+ sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
+
+ return orig_enabled;
+}
+
static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
{
unsigned timeout = 10;
+ bool card_clk_enabled;
u32 reg;

+ card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
+
tegra_sdhci_configure_cal_pad(host, true);

reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
@@ -248,6 +273,8 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)

tegra_sdhci_configure_cal_pad(host, false);

+ tegra_sdhci_configure_card_clk(host, card_clk_enabled);
+
if (timeout == 0)
dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
}
--
2.7.4


2018-07-25 07:05:32

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH 01/10] mmc: tegra: Poll for calibration completion

On 24.07.2018 17:29, Aapo Vienamo wrote:
> Implement polling with 10 ms timeout for automatic pad drive strength
> calibration.
>
> Signed-off-by: Aapo Vienamo <[email protected]>
> ---
> drivers/mmc/host/sdhci-tegra.c | 24 +++++++++++++++++++-----
> 1 file changed, 19 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index f108c48..e40ca43 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -49,6 +49,9 @@
> #define SDHCI_AUTO_CAL_START BIT(31)
> #define SDHCI_AUTO_CAL_ENABLE BIT(29)
>
> +#define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
> +#define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)

Please align the definition value with tabs. Also it looks like the
alignments for the defines here are all over the place, would be nice to
fix those up so they line up.

> +
> #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
> #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
> #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
> @@ -198,13 +201,24 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
>
> static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
> {
> - u32 val;
> + unsigned timeout = 10;

I prefer "unsigned int" instead of just "unsigned", but I guess that's
just a personal preference..

> + u32 reg;
>
> - mdelay(1);
> + reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> + reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
> + sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> + udelay(1);
> +
> + do {
> + reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_STATUS);
> + if (!(reg & SDHCI_TEGRA_AUTO_CAL_ACTIVE))
> + break;
> + mdelay(1);
> + timeout--;
> + } while (timeout);

Can we use readl_poll_timeout here? We'll need to calculate the address
directly but it'd still look nicer.

Cheers,
Mikko

>
> - val = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> - val |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
> - sdhci_writel(host,val, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> + if (timeout == 0)
> + dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
> }
>
> static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
>

2018-07-25 07:09:52

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH 02/10] mmc: tegra: Set calibration pad voltage reference

On 24.07.2018 17:34, Aapo Vienamo wrote:
> Configure the voltage reference used by the automatic pad drive strength
> calibration procedure. The value is a magic number from the TRM.
>
> Signed-off-by: Aapo Vienamo <[email protected]>
> ---
> drivers/mmc/host/sdhci-tegra.c | 14 ++++++++++++--
> 1 file changed, 12 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index e40ca43..6008e2f 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -49,6 +49,10 @@
> #define SDHCI_AUTO_CAL_START BIT(31)
> #define SDHCI_AUTO_CAL_ENABLE BIT(29)
>
> +#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
> +#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
> +#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
> +
> #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
> #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
>
> @@ -152,7 +156,7 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
> struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
> - u32 misc_ctrl, clk_ctrl;
> + u32 misc_ctrl, clk_ctrl, pad_ctrl;
>
> sdhci_reset(host, mask);
>
> @@ -193,8 +197,14 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
> sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
> sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
>
> - if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
> + if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
> + pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> + pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
> + pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
> + sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> +

Will this happen to only eMMC controllers or for all controllers? My
docs are saying this should be set to 0x7 for SDMMC2/4 and 0x1 or 0x2
for SDMMC1/3 depending on voltage. Not sure how downstream is
programming it, though.

> tegra_host->pad_calib_required = true;
> + }
>
> tegra_host->ddr_signaling = false;
> }
>

2018-07-25 07:12:40

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH 03/10] mmc: tegra: Power on the calibration pad

Reviewed-by: Mikko Perttunen <[email protected]>

On 24.07.2018 17:34, Aapo Vienamo wrote:
> Automatic pad drive strength calibration is performed on a separate pad
> identical to the ones used for driving the actual bus. Power on the
> calibration pad during the calibration procedure and power it off
> afterwards to save power.
>
> Signed-off-by: Aapo Vienamo <[email protected]>
> ---
> drivers/mmc/host/sdhci-tegra.c | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index 6008e2f..61067b7 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -209,11 +209,30 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
> tegra_host->ddr_signaling = false;
> }
>
> +static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
> +{
> + u32 reg;
> +
> + /*
> + * Enable or disable the additional I/O pad used by the drive strength
> + * calibration process.
> + */
> + reg = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> + if (enable)
> + reg |= SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD;
> + else
> + reg &= ~SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD;
> + sdhci_writel(host, reg, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> + udelay(1);
> +}
> +
> static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
> {
> unsigned timeout = 10;
> u32 reg;
>
> + tegra_sdhci_configure_cal_pad(host, true);
> +
> reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> reg |= SDHCI_AUTO_CAL_ENABLE | SDHCI_AUTO_CAL_START;
> sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> @@ -227,6 +246,8 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
> timeout--;
> } while (timeout);
>
> + tegra_sdhci_configure_cal_pad(host, false);
> +
> if (timeout == 0)
> dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
> }
>

2018-07-25 07:16:27

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH 04/10] mmc: tegra: Disable card clock during pad calibration

On 24.07.2018 17:34, Aapo Vienamo wrote:
> Disable the card clock during automatic pad drive strength calibration
> and re-enable it aftewards.

s/aftewards/afterwards/.

>
> Signed-off-by: Aapo Vienamo <[email protected]>
> ---
> drivers/mmc/host/sdhci-tegra.c | 27 +++++++++++++++++++++++++++
> 1 file changed, 27 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index 61067b7..0de74f4 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -52,6 +52,7 @@
> #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
> #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
> #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
> +#define SDHCI_TEGRA_PAD_E_INPUT_OR_E_PWRD BIT(31)

Looks like this should be in the previous patch.

>
> #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
> #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
> @@ -226,11 +227,35 @@ static void tegra_sdhci_configure_cal_pad(struct sdhci_host *host, bool enable)
> udelay(1);
> }
>
> +static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
> +{
> + bool orig_enabled;
> + u32 reg;
> +
> + reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> + orig_enabled = reg & SDHCI_CLOCK_CARD_EN;

I would do !!(reg & SDHCI_CLOCK_CARD_EN) here.

> +
> + if (orig_enabled == enable)
> + return orig_enabled;
> +
> + if (enable)
> + reg |= SDHCI_CLOCK_CARD_EN;
> + else
> + reg &= ~SDHCI_CLOCK_CARD_EN;
> +
> + sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
> +
> + return orig_enabled;
> +}
> +
> static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
> {
> unsigned timeout = 10;
> + bool card_clk_enabled;
> u32 reg;
>
> + card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
> +
> tegra_sdhci_configure_cal_pad(host, true);
>
> reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> @@ -248,6 +273,8 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
>
> tegra_sdhci_configure_cal_pad(host, false);
>
> + tegra_sdhci_configure_card_clk(host, card_clk_enabled);
> +
> if (timeout == 0)
> dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
> }
>

2018-07-25 07:18:21

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH 05/10] dt-bindings: Add Tegra SDHCI pad pdpu offset bindings

On 24.07.2018 17:34, Aapo Vienamo wrote:
> Add bindings documentation for pad pull up and pull down offset values to be
> programmed before executing automatic pad drive strength calibration.
>
> Signed-off-by: Aapo Vienamo <[email protected]>
> ---
> .../bindings/mmc/nvidia,tegra20-sdhci.txt | 32 ++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt b/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt
> index 90c214d..949f616 100644
> --- a/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt
> +++ b/Documentation/devicetree/bindings/mmc/nvidia,tegra20-sdhci.txt
> @@ -24,6 +24,7 @@ Required properties:
> Optional properties:
> - power-gpios : Specify GPIOs for power control
>
> +Optional properties for Tegra210 and Tegra186:
> Example:
>
> sdhci@c8000200 {
> @@ -45,6 +46,33 @@ Optional properties for Tegra210 and Tegra186:
> for controllers supporting multiple voltage levels. The order of names
> should correspond to the pin configuration states in pinctrl-0 and
> pinctrl-1.
> +- pad-autocal-pull-up-offset-3v3, pad-autocal-pull-down-offset-3v3 :
> + Specify drive strength calibration offsets for 3.3 V signaling modes.
> +- pad-autocal-pull-up-offset-1v8, pad-autocal-pull-down-offset-1v8 :
> + Specify drive strength calibration offsets for 1.8 V signaling modes.
> +- pad-autocal-pull-up-offset-3v3-timeout,
> + pad-autocal-pull-down-offset-3v3-timeout : Specify drive strength
> + used as a fallback in case the automatic calibration times out on a
> + 3.3 V signaling mode.
> +- pad-autocal-pull-up-offset-1v8-timeout,
> + pad-autocal-pull-down-offset-1v8-timeout : Specify drive strength
> + used as a fallback in case the automatic calibration times out on a
> + 1.8 V signaling mode.
> +- pad-autocal-pull-up-offset-sdr104,
> + pad-autocal-pull-down-offset-sdr104 : Specify drive strength
> + calibration offsets for SDR104 mode.
> +- pad-autocal-pull-up-offset-hs400,
> + pad-autocal-pull-down-offset-hs400 : Specify drive strength
> + calibration offsets for HS400 mode.

All of these need an "nvidia," prefix.

> +
> + Notes on the pad calibration pull up and pulldown offset values:
> + - The property values are drive codes which are programmed into the
> + PD_OFFSET and PU_OFFSET sections of the
> + SDHCI_TEGRA_AUTO_CAL_CONFIG register.
> + - A higher value corresponds to higher drive strength. Please refer
> + to the reference manual of the SoC for correct values.
> + - The SDR104 and HS400 timing specific values are used in
> + corresponding modes if specified.
>
> Example:
> sdhci@700b0000 {
> @@ -58,5 +86,9 @@ sdhci@700b0000 {
> pinctrl-names = "sdmmc-3v3", "sdmmc-1v8";
> pinctrl-0 = <&sdmmc1_3v3>;
> pinctrl-1 = <&sdmmc1_1v8>;
> + pad-autocal-pull-up-offset-3v3 = <0x00>;
> + pad-autocal-pull-down-offset-3v3 = <0x7d>;
> + pad-autocal-pull-up-offset-1v8 = <0x7b>;
> + pad-autocal-pull-down-offset-1v8 = <0x7b>;
> status = "disabled";
> };
>

2018-07-25 07:25:11

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH 06/10] mmc: tegra: Program pad autocal offsets from dt

On 24.07.2018 17:34, Aapo Vienamo wrote:
> Parse the pad drive strength calibration offsets from the device tree.
> Program the calibration offsets in accordance with the current signaling
> mode.
>
> Signed-off-by: Aapo Vienamo <[email protected]>
> ---
> drivers/mmc/host/sdhci-tegra.c | 147 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 146 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index 0de74f4..78781bd 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -48,6 +48,7 @@
> #define SDHCI_TEGRA_AUTO_CAL_CONFIG 0x1e4
> #define SDHCI_AUTO_CAL_START BIT(31)
> #define SDHCI_AUTO_CAL_ENABLE BIT(29)
> +#define SDHCI_AUTO_CAL_PDPU_OFFSET_MASK 0x0000ffff
>
> #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
> #define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
> @@ -71,6 +72,22 @@ struct sdhci_tegra_soc_data {
> u32 nvquirks;
> };
>
> +/* Magic pull up and pull down pad calibration offsets */
> +struct sdhci_tegra_autocal_offsets {
> + u8 pull_up_3v3;
> + u8 pull_down_3v3;
> + u8 pull_up_3v3_timeout;
> + u8 pull_down_3v3_timeout;
> + u8 pull_up_1v8;
> + u8 pull_down_1v8;
> + u8 pull_up_1v8_timeout;
> + u8 pull_down_1v8_timeout;
> + u8 pull_up_sdr104;
> + u8 pull_down_sdr104;
> + u8 pull_up_hs400;
> + u8 pull_down_hs400;
> +}; > +
> struct sdhci_tegra {
> const struct sdhci_tegra_soc_data *soc_data;
> struct gpio_desc *power_gpio;
> @@ -82,6 +99,8 @@ struct sdhci_tegra {
> struct pinctrl *pinctrl_sdmmc;
> struct pinctrl_state *pinctrl_state_3v3;
> struct pinctrl_state *pinctrl_state_1v8;
> +
> + struct sdhci_tegra_autocal_offsets autocal_offsets;
> };
>
> static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
> @@ -248,12 +267,45 @@ static bool tegra_sdhci_configure_card_clk(struct sdhci_host *host, bool enable)
> return orig_enabled;
> }
>
> +static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
> + u16 pdpu)
> +{
> + u32 reg;
> +
> + reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> + reg &= ~SDHCI_AUTO_CAL_PDPU_OFFSET_MASK;
> + reg |= pdpu;
> + sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
> +}
> +
> static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
> {
> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> + struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> + struct sdhci_tegra_autocal_offsets offsets =
> + tegra_host->autocal_offsets;
> + struct mmc_ios *ios = &host->mmc->ios;
> unsigned timeout = 10;
> bool card_clk_enabled;
> + u16 pdpu;
> u32 reg;
>
> + switch (ios->timing) {
> + case MMC_TIMING_UHS_SDR104:
> + pdpu = offsets.pull_down_sdr104 << 8 | offsets.pull_up_sdr104;
> + break;
> + case MMC_TIMING_MMC_HS400:
> + pdpu = offsets.pull_down_hs400 << 8 | offsets.pull_up_hs400;
> + break;
> + default:
> + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
> + pdpu = offsets.pull_down_1v8 << 8 | offsets.pull_up_1v8;
> + else
> + pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
> + }
> +
> + tegra_sdhci_set_pad_autocal_offset(host, pdpu);
> +
> card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
>
> tegra_sdhci_configure_cal_pad(host, true);
> @@ -275,8 +327,99 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
>
> tegra_sdhci_configure_card_clk(host, card_clk_enabled);
>
> - if (timeout == 0)
> + if (timeout == 0) {
> dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
> +
> + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
> + pdpu = offsets.pull_down_1v8_timeout << 8 |
> + offsets.pull_up_1v8_timeout;
> + else
> + pdpu = offsets.pull_down_3v3_timeout << 8 |
> + offsets.pull_up_3v3_timeout;
> +
> + tegra_sdhci_set_pad_autocal_offset(host, pdpu);
> + }
> +}
> +
> +static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
> +{
> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> + struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> + struct sdhci_tegra_autocal_offsets *autocal =
> + &tegra_host->autocal_offsets;
> + int err;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-up-offset-3v3",
> + &autocal->pull_up_3v3);
> + if (err)
> + autocal->pull_up_3v3 = 0;
> +

If you read these properties using read_u8, they'll need to be specified
in the device tree as "property = /bits/ 8 <...>;", which might not be
desirable.

> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-down-offset-3v3",
> + &autocal->pull_down_3v3);
> + if (err)
> + autocal->pull_down_3v3 = 0;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-up-offset-1v8",
> + &autocal->pull_up_1v8);
> + if (err)
> + autocal->pull_up_1v8 = 0;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-down-offset-1v8",
> + &autocal->pull_down_1v8);
> + if (err)
> + autocal->pull_down_1v8 = 0;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-up-offset-3v3-timeout",
> + &autocal->pull_up_3v3);
> + if (err)
> + autocal->pull_up_3v3_timeout = 0;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-down-offset-3v3-timeout",
> + &autocal->pull_down_3v3);
> + if (err)
> + autocal->pull_down_3v3_timeout = 0;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-up-offset-1v8-timeout",
> + &autocal->pull_up_1v8);
> + if (err)
> + autocal->pull_up_1v8_timeout = 0;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-down-offset-1v8-timeout",
> + &autocal->pull_down_1v8);
> + if (err)
> + autocal->pull_down_1v8_timeout = 0;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-up-offset-sdr104",
> + &autocal->pull_up_sdr104);
> + if (err)
> + autocal->pull_up_sdr104 = autocal->pull_up_1v8;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-down-offset-sdr104",
> + &autocal->pull_down_sdr104);
> + if (err)
> + autocal->pull_down_sdr104 = autocal->pull_down_1v8;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-up-offset-hs400",
> + &autocal->pull_up_hs400);
> + if (err)
> + autocal->pull_up_hs400 = autocal->pull_up_1v8;
> +
> + err = device_property_read_u8(host->mmc->parent,
> + "pad-autocal-pull-down-offset-hs400",
> + &autocal->pull_down_hs400);
> + if (err)
> + autocal->pull_down_hs400 = autocal->pull_down_1v8;
> }
>
> static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
> @@ -638,6 +781,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
> if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
> host->mmc->caps |= MMC_CAP_1_8V_DDR;
>
> + tegra_sdhci_parse_pad_autocal_dt(host);
> +
> tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
> GPIOD_OUT_HIGH);
> if (IS_ERR(tegra_host->power_gpio)) {
>

2018-07-25 07:26:43

by Mikko Perttunen

[permalink] [raw]
Subject: Re: [PATCH 09/10] mmc: tegra: Perform pad calibration after voltage switch



On 24.07.2018 17:34, Aapo Vienamo wrote:
> Run the automatic pad calibration after voltage switching if
> tegra_host->pad_calib_required is set.
>
> Signed-off-by: Aapo Vienamo <[email protected]>
> ---
> drivers/mmc/host/sdhci-tegra.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index 78781bd..529aa4e7 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -537,6 +537,8 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
> struct mmc_ios *ios)
> {
> struct sdhci_host *host = mmc_priv(mmc);
> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> + struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> int ret = 0;
>
> if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
> @@ -551,6 +553,9 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
> ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
> }
>
> + if (tegra_host->pad_calib_required)
> + tegra_sdhci_pad_autocalib(host);

What if the autocalibration fails? Should we return an error?

> +
> return ret;
> }
>
>

2018-07-25 10:01:27

by Aapo Vienamo

[permalink] [raw]
Subject: Re: [PATCH 02/10] mmc: tegra: Set calibration pad voltage reference

On Wed, 25 Jul 2018 10:08:46 +0300
Mikko Perttunen <[email protected]> wrote:

> On 24.07.2018 17:34, Aapo Vienamo wrote:
> > Configure the voltage reference used by the automatic pad drive strength
> > calibration procedure. The value is a magic number from the TRM.
> >
> > Signed-off-by: Aapo Vienamo <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-tegra.c | 14 ++++++++++++--
> > 1 file changed, 12 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> > index e40ca43..6008e2f 100644
> > --- a/drivers/mmc/host/sdhci-tegra.c
> > +++ b/drivers/mmc/host/sdhci-tegra.c
> > @@ -49,6 +49,10 @@
> > #define SDHCI_AUTO_CAL_START BIT(31)
> > #define SDHCI_AUTO_CAL_ENABLE BIT(29)
> >
> > +#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL 0x1e0
> > +#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
> > +#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
> > +
> > #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
> > #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
> >
> > @@ -152,7 +156,7 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
> > struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> > struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> > const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
> > - u32 misc_ctrl, clk_ctrl;
> > + u32 misc_ctrl, clk_ctrl, pad_ctrl;
> >
> > sdhci_reset(host, mask);
> >
> > @@ -193,8 +197,14 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
> > sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
> > sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
> >
> > - if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
> > + if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) {
> > + pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> > + pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK;
> > + pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL;
> > + sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
> > +
>
> Will this happen to only eMMC controllers or for all controllers? My
> docs are saying this should be set to 0x7 for SDMMC2/4 and 0x1 or 0x2
> for SDMMC1/3 depending on voltage. Not sure how downstream is
> programming it, though.

The Tegra210 TRM specifies that VREF_SEL should be set 0x7 for all of
the controllers, there's no mention of this depending on the mode. The
same value is also programmed by the downstream kernels for Tegra210
and Tegra186.

-Aapo

2018-07-25 10:44:17

by Aapo Vienamo

[permalink] [raw]
Subject: Re: [PATCH 09/10] mmc: tegra: Perform pad calibration after voltage switch

On Wed, 25 Jul 2018 10:25:16 +0300
Mikko Perttunen <[email protected]> wrote:

> On 24.07.2018 17:34, Aapo Vienamo wrote:
> > Run the automatic pad calibration after voltage switching if
> > tegra_host->pad_calib_required is set.
> >
> > Signed-off-by: Aapo Vienamo <[email protected]>
> > ---
> > drivers/mmc/host/sdhci-tegra.c | 5 +++++
> > 1 file changed, 5 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> > index 78781bd..529aa4e7 100644
> > --- a/drivers/mmc/host/sdhci-tegra.c
> > +++ b/drivers/mmc/host/sdhci-tegra.c
> > @@ -537,6 +537,8 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
> > struct mmc_ios *ios)
> > {
> > struct sdhci_host *host = mmc_priv(mmc);
> > + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> > + struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
> > int ret = 0;
> >
> > if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
> > @@ -551,6 +553,9 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
> > ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
> > }
> >
> > + if (tegra_host->pad_calib_required)
> > + tegra_sdhci_pad_autocalib(host);
>
> What if the autocalibration fails? Should we return an error?

I would assume that aborting the signal voltage switch due to a
calibration timeout would not be necessary as the TRM specifies drive
strength values which are to be programmed in case a timeout occurs.

However, I don't know what are the exact implications of running the bus
with the timeout values instead of the calibrated ones. The TRM does not
comment on that. I guess tuning would fail and catch the error that way
in case the timeout default values aren't sufficient.

-Aapo

> > +
> > return ret;
> > }
> >
> >


2018-07-25 12:07:31

by Peter Geis

[permalink] [raw]
Subject: Re: [PATCH 00/10] Update the pad autocal procedure

On 07/24/2018 10:29 AM, Aapo Vienamo wrote:
> Aapo Vienamo (10):
>
> mmc: tegra: Enable pad calibration on Tegra210 and Tegra186

Good Morning,

You seem to be missing patch 10/10.
Checked
https://lore.kernel.org/patchwork/project/lkml/list/?series=360525 to be
sure.

2018-07-25 14:29:36

by Aapo Vienamo

[permalink] [raw]
Subject: [PATCH 10/10] mmc: tegra: Enable pad calibration on Tegra210 and Tegra186

Set NVQUIRK_HAS_PADCALIB on Tegra210 and Tegra186 to enable automatic
pad drive strength calibration.

Signed-off-by: Aapo Vienamo <[email protected]>
---
drivers/mmc/host/sdhci-tegra.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 529aa4e7..a865160 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -709,7 +709,8 @@ static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {

static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
.pdata = &sdhci_tegra210_pdata,
- .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL,
+ .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
+ NVQUIRK_HAS_PADCALIB,
};

static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
@@ -733,7 +734,8 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {

static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
.pdata = &sdhci_tegra186_pdata,
- .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL,
+ .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
+ NVQUIRK_HAS_PADCALIB,
};

static const struct of_device_id sdhci_tegra_dt_match[] = {
--
2.7.4


2018-07-25 14:30:03

by Aapo Vienamo

[permalink] [raw]
Subject: Re: [PATCH 00/10] Update the pad autocal procedure

On Wed, 25 Jul 2018 08:06:18 -0400
Peter Geis <[email protected]> wrote:

> On 07/24/2018 10:29 AM, Aapo Vienamo wrote:
> > Aapo Vienamo (10):
> >
> > mmc: tegra: Enable pad calibration on Tegra210 and Tegra186
>
> Good Morning,
>
> You seem to be missing patch 10/10.
> Checked
> https://lore.kernel.org/patchwork/project/lkml/list/?series=360525 to be
> sure.

Missing patch sent. Thanks for pointing that out.

-Aapo