* [PATCH v5 1/3] dt-bindings: mmc: Add support for rk3576 dw-mshc
[not found] <20240828152446.42896-1-detlev.casanova@collabora.com>
@ 2024-08-28 15:24 ` Detlev Casanova
2024-08-29 11:38 ` Heiko Stübner
2024-08-28 15:24 ` [PATCH v5 2/3] mmc: dw_mmc-rockchip: Add internal phase support Detlev Casanova
2024-08-28 15:24 ` [PATCH v5 3/3] mmc: dw_mmc-rockchip: Add support for rk3576 SoCs Detlev Casanova
2 siblings, 1 reply; 6+ messages in thread
From: Detlev Casanova @ 2024-08-28 15:24 UTC (permalink / raw)
To: linux-kernel
Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, Jaehoon Chung, linux-mmc, devicetree,
linux-arm-kernel, linux-rockchip, kernel, Detlev Casanova,
Krzysztof Kozlowski
Add the compatible string for rockchip,rk3576-dw-mshc in its own new
block, for devices that have internal phase settings instead of external
clocks.
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
index 211cd0b0bc5f..06df1269f247 100644
--- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
+++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
@@ -43,6 +43,8 @@ properties:
- rockchip,rv1108-dw-mshc
- rockchip,rv1126-dw-mshc
- const: rockchip,rk3288-dw-mshc
+ # for Rockchip RK3576 with phase tuning inside the controller
+ - const: rockchip,rk3576-dw-mshc
reg:
maxItems: 1
--
2.46.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v5 2/3] mmc: dw_mmc-rockchip: Add internal phase support
[not found] <20240828152446.42896-1-detlev.casanova@collabora.com>
2024-08-28 15:24 ` [PATCH v5 1/3] dt-bindings: mmc: Add support for rk3576 dw-mshc Detlev Casanova
@ 2024-08-28 15:24 ` Detlev Casanova
2024-08-29 11:41 ` Heiko Stübner
2024-08-28 15:24 ` [PATCH v5 3/3] mmc: dw_mmc-rockchip: Add support for rk3576 SoCs Detlev Casanova
2 siblings, 1 reply; 6+ messages in thread
From: Detlev Casanova @ 2024-08-28 15:24 UTC (permalink / raw)
To: linux-kernel
Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, Jaehoon Chung, linux-mmc, devicetree,
linux-arm-kernel, linux-rockchip, kernel, Shawn Lin,
Detlev Casanova
From: Shawn Lin <shawn.lin@rock-chips.com>
Some Rockchip devices put the phase settings into the dw_mmc controller.
When the feature is present, the ciu-drive and ciu-sample clocks are
not used and the phase configuration is done directly through the mmc
controller.
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
Acked-by: Shawn Lin <shawn.lin@rock-chips.com>
---
drivers/mmc/host/dw_mmc-rockchip.c | 171 +++++++++++++++++++++++++++--
1 file changed, 160 insertions(+), 11 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index b07190ba4b7a..75e9ac4bcd08 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -15,7 +15,17 @@
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
-#define RK3288_CLKGEN_DIV 2
+#define RK3288_CLKGEN_DIV 2
+#define SDMMC_TIMING_CON0 0x130
+#define SDMMC_TIMING_CON1 0x134
+#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
+#define ROCKCHIP_MMC_DEGREE_MASK 0x3
+#define ROCKCHIP_MMC_DEGREE_OFFSET 1
+#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
+#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
+#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
static const unsigned int freqs[] = { 100000, 200000, 300000, 400000 };
@@ -24,8 +34,143 @@ struct dw_mci_rockchip_priv_data {
struct clk *sample_clk;
int default_sample_phase;
int num_phases;
+ bool internal_phase;
};
+/*
+ * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
+ */
+static int rockchip_mmc_get_internal_phase(struct dw_mci *host, bool sample)
+{
+ unsigned long rate = clk_get_rate(host->ciu_clk);
+ u32 raw_value;
+ u16 degrees;
+ u32 delay_num = 0;
+
+ /* Constant signal, no measurable phase shift */
+ if (!rate)
+ return 0;
+
+ if (sample)
+ raw_value = mci_readl(host, TIMING_CON1);
+ else
+ raw_value = mci_readl(host, TIMING_CON0);
+
+ raw_value >>= ROCKCHIP_MMC_DEGREE_OFFSET;
+ degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
+
+ if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
+ /* degrees/delaynum * 1000000 */
+ unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
+ 36 * (rate / 10000);
+
+ delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
+ delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
+ degrees += DIV_ROUND_CLOSEST(delay_num * factor, 1000000);
+ }
+
+ return degrees % 360;
+}
+
+static int rockchip_mmc_get_phase(struct dw_mci *host, bool sample)
+{
+ struct dw_mci_rockchip_priv_data *priv = host->priv;
+ struct clk *clock = sample ? priv->sample_clk : priv->drv_clk;
+
+ if (priv->internal_phase)
+ return rockchip_mmc_get_internal_phase(host, sample);
+ else
+ return clk_get_phase(clock);
+}
+
+static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int degrees)
+{
+ unsigned long rate = clk_get_rate(host->ciu_clk);
+ u8 nineties, remainder;
+ u8 delay_num;
+ u32 raw_value;
+ u32 delay;
+
+ /*
+ * The below calculation is based on the output clock from
+ * MMC host to the card, which expects the phase clock inherits
+ * the clock rate from its parent, namely the output clock
+ * provider of MMC host. However, things may go wrong if
+ * (1) It is orphan.
+ * (2) It is assigned to the wrong parent.
+ *
+ * This check help debug the case (1), which seems to be the
+ * most likely problem we often face and which makes it difficult
+ * for people to debug unstable mmc tuning results.
+ */
+ if (!rate) {
+ dev_err(host->dev, "%s: invalid clk rate\n", __func__);
+ return -EINVAL;
+ }
+
+ nineties = degrees / 90;
+ remainder = (degrees % 90);
+
+ /*
+ * Due to the inexact nature of the "fine" delay, we might
+ * actually go non-monotonic. We don't go _too_ monotonic
+ * though, so we should be OK. Here are options of how we may
+ * work:
+ *
+ * Ideally we end up with:
+ * 1.0, 2.0, ..., 69.0, 70.0, ..., 89.0, 90.0
+ *
+ * On one extreme (if delay is actually 44ps):
+ * .73, 1.5, ..., 50.6, 51.3, ..., 65.3, 90.0
+ * The other (if delay is actually 77ps):
+ * 1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90
+ *
+ * It's possible we might make a delay that is up to 25
+ * degrees off from what we think we're making. That's OK
+ * though because we should be REALLY far from any bad range.
+ */
+
+ /*
+ * Convert to delay; do a little extra work to make sure we
+ * don't overflow 32-bit / 64-bit numbers.
+ */
+ delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
+ delay *= remainder;
+ delay = DIV_ROUND_CLOSEST(delay,
+ (rate / 1000) * 36 *
+ (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
+
+ delay_num = (u8) min_t(u32, delay, 255);
+
+ raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
+ raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
+ raw_value |= nineties;
+
+ if (sample)
+ mci_writel(host, TIMING_CON1, HIWORD_UPDATE(raw_value, 0x07ff, 1));
+ else
+ mci_writel(host, TIMING_CON0, HIWORD_UPDATE(raw_value, 0x07ff, 1));
+
+ dev_dbg(host->dev, "set %s_phase(%d) delay_nums=%u actual_degrees=%d\n",
+ sample ? "sample" : "drv", degrees, delay_num,
+ rockchip_mmc_get_phase(host, sample)
+ );
+
+ return 0;
+}
+
+static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int degrees)
+{
+ struct dw_mci_rockchip_priv_data *priv = host->priv;
+ struct clk *clock = sample ? priv->sample_clk : priv->drv_clk;
+
+ if (priv->internal_phase)
+ return rockchip_mmc_set_internal_phase(host, sample, degrees);
+ else
+ return clk_set_phase(clock, degrees);
+}
+
static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
struct dw_mci_rockchip_priv_data *priv = host->priv;
@@ -64,7 +209,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
/* Make sure we use phases which we can enumerate with */
if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS)
- clk_set_phase(priv->sample_clk, priv->default_sample_phase);
+ rockchip_mmc_set_phase(host, true, priv->default_sample_phase);
/*
* Set the drive phase offset based on speed mode to achieve hold times.
@@ -127,7 +272,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
break;
}
- clk_set_phase(priv->drv_clk, phase);
+ rockchip_mmc_set_phase(host, false, phase);
}
}
@@ -151,6 +296,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
int longest_range_len = -1;
int longest_range = -1;
int middle_phase;
+ int phase;
if (IS_ERR(priv->sample_clk)) {
dev_err(host->dev, "Tuning clock (sample_clk) not defined.\n");
@@ -164,8 +310,10 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
/* Try each phase and extract good ranges */
for (i = 0; i < priv->num_phases; ) {
- clk_set_phase(priv->sample_clk,
- TUNING_ITERATION_TO_PHASE(i, priv->num_phases));
+ rockchip_mmc_set_phase(host, true,
+ TUNING_ITERATION_TO_PHASE(
+ i,
+ priv->num_phases));
v = !mmc_send_tuning(mmc, opcode, NULL);
@@ -211,7 +359,8 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
}
if (ranges[0].start == 0 && ranges[0].end == priv->num_phases - 1) {
- clk_set_phase(priv->sample_clk, priv->default_sample_phase);
+ rockchip_mmc_set_phase(host, true, priv->default_sample_phase);
+
dev_info(host->dev, "All phases work, using default phase %d.",
priv->default_sample_phase);
goto free;
@@ -248,12 +397,10 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
middle_phase = ranges[longest_range].start + longest_range_len / 2;
middle_phase %= priv->num_phases;
- dev_info(host->dev, "Successfully tuned phase to %d\n",
- TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases));
+ phase = TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases);
+ dev_info(host->dev, "Successfully tuned phase to %d\n", phase);
- clk_set_phase(priv->sample_clk,
- TUNING_ITERATION_TO_PHASE(middle_phase,
- priv->num_phases));
+ rockchip_mmc_set_phase(host, true, phase);
free:
kfree(ranges);
@@ -287,6 +434,8 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
host->priv = priv;
+ priv->internal_phase = false;
+
return 0;
}
--
2.46.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v5 3/3] mmc: dw_mmc-rockchip: Add support for rk3576 SoCs
[not found] <20240828152446.42896-1-detlev.casanova@collabora.com>
2024-08-28 15:24 ` [PATCH v5 1/3] dt-bindings: mmc: Add support for rk3576 dw-mshc Detlev Casanova
2024-08-28 15:24 ` [PATCH v5 2/3] mmc: dw_mmc-rockchip: Add internal phase support Detlev Casanova
@ 2024-08-28 15:24 ` Detlev Casanova
2024-08-29 11:46 ` Heiko Stübner
2 siblings, 1 reply; 6+ messages in thread
From: Detlev Casanova @ 2024-08-28 15:24 UTC (permalink / raw)
To: linux-kernel
Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, Jaehoon Chung, linux-mmc, devicetree,
linux-arm-kernel, linux-rockchip, kernel, Detlev Casanova
On rk3576 the tunable clocks are inside the controller itself, removing
the need for the "ciu-drive" and "ciu-sample" clocks.
That makes it a new type of controller that has its own dt_parse function.
Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
---
drivers/mmc/host/dw_mmc-rockchip.c | 48 ++++++++++++++++++++++++++----
1 file changed, 43 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 75e9ac4bcd08..f96260fd143b 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -407,7 +407,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
return ret;
}
-static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
+static int dw_mci_common_parse_dt(struct dw_mci *host)
{
struct device_node *np = host->dev->of_node;
struct dw_mci_rockchip_priv_data *priv;
@@ -417,13 +417,29 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
return -ENOMEM;
if (of_property_read_u32(np, "rockchip,desired-num-phases",
- &priv->num_phases))
+ &priv->num_phases))
priv->num_phases = 360;
if (of_property_read_u32(np, "rockchip,default-sample-phase",
- &priv->default_sample_phase))
+ &priv->default_sample_phase))
priv->default_sample_phase = 0;
+ host->priv = priv;
+
+ return 0;
+}
+
+static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
+{
+ struct dw_mci_rockchip_priv_data *priv;
+ int err;
+
+ err = dw_mci_common_parse_dt(host);
+ if (err)
+ return err;
+
+ priv = host->priv;
+
priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
if (IS_ERR(priv->drv_clk))
dev_dbg(host->dev, "ciu-drive not available\n");
@@ -432,13 +448,25 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
if (IS_ERR(priv->sample_clk))
dev_dbg(host->dev, "ciu-sample not available\n");
- host->priv = priv;
-
priv->internal_phase = false;
return 0;
}
+static int dw_mci_rk3576_parse_dt(struct dw_mci *host)
+{
+ struct dw_mci_rockchip_priv_data *priv;
+ int err = dw_mci_common_parse_dt(host);
+ if (err)
+ return err;
+
+ priv = host->priv;
+
+ priv->internal_phase = true;
+
+ return 0;
+}
+
static int dw_mci_rockchip_init(struct dw_mci *host)
{
int ret, i;
@@ -480,11 +508,21 @@ static const struct dw_mci_drv_data rk3288_drv_data = {
.init = dw_mci_rockchip_init,
};
+static const struct dw_mci_drv_data rk3576_drv_data = {
+ .common_caps = MMC_CAP_CMD23,
+ .set_ios = dw_mci_rk3288_set_ios,
+ .execute_tuning = dw_mci_rk3288_execute_tuning,
+ .parse_dt = dw_mci_rk3576_parse_dt,
+ .init = dw_mci_rockchip_init,
+};
+
static const struct of_device_id dw_mci_rockchip_match[] = {
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rk2928_drv_data },
{ .compatible = "rockchip,rk3288-dw-mshc",
.data = &rk3288_drv_data },
+ { .compatible = "rockchip,rk3576-dw-mshc",
+ .data = &rk3576_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);
--
2.46.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v5 1/3] dt-bindings: mmc: Add support for rk3576 dw-mshc
2024-08-28 15:24 ` [PATCH v5 1/3] dt-bindings: mmc: Add support for rk3576 dw-mshc Detlev Casanova
@ 2024-08-29 11:38 ` Heiko Stübner
0 siblings, 0 replies; 6+ messages in thread
From: Heiko Stübner @ 2024-08-29 11:38 UTC (permalink / raw)
To: linux-kernel, Detlev Casanova
Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jaehoon Chung, linux-mmc, devicetree, linux-arm-kernel,
linux-rockchip, kernel, Detlev Casanova, Krzysztof Kozlowski
Am Mittwoch, 28. August 2024, 17:24:53 CEST schrieb Detlev Casanova:
> Add the compatible string for rockchip,rk3576-dw-mshc in its own new
> block, for devices that have internal phase settings instead of external
> clocks.
>
> Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Heiko Stuebner <heiko@sntech.de>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5 2/3] mmc: dw_mmc-rockchip: Add internal phase support
2024-08-28 15:24 ` [PATCH v5 2/3] mmc: dw_mmc-rockchip: Add internal phase support Detlev Casanova
@ 2024-08-29 11:41 ` Heiko Stübner
0 siblings, 0 replies; 6+ messages in thread
From: Heiko Stübner @ 2024-08-29 11:41 UTC (permalink / raw)
To: linux-kernel, Detlev Casanova
Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jaehoon Chung, linux-mmc, devicetree, linux-arm-kernel,
linux-rockchip, kernel, Shawn Lin, Detlev Casanova
Am Mittwoch, 28. August 2024, 17:24:55 CEST schrieb Detlev Casanova:
> From: Shawn Lin <shawn.lin@rock-chips.com>
>
> Some Rockchip devices put the phase settings into the dw_mmc controller.
>
> When the feature is present, the ciu-drive and ciu-sample clocks are
> not used and the phase configuration is done directly through the mmc
> controller.
>
> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
> Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
> Acked-by: Shawn Lin <shawn.lin@rock-chips.com>
Reviewed-by: Heiko Stuebner <heiko@sntech.de>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v5 3/3] mmc: dw_mmc-rockchip: Add support for rk3576 SoCs
2024-08-28 15:24 ` [PATCH v5 3/3] mmc: dw_mmc-rockchip: Add support for rk3576 SoCs Detlev Casanova
@ 2024-08-29 11:46 ` Heiko Stübner
0 siblings, 0 replies; 6+ messages in thread
From: Heiko Stübner @ 2024-08-29 11:46 UTC (permalink / raw)
To: linux-kernel, Detlev Casanova
Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jaehoon Chung, linux-mmc, devicetree, linux-arm-kernel,
linux-rockchip, kernel, Detlev Casanova
Am Mittwoch, 28. August 2024, 17:24:56 CEST schrieb Detlev Casanova:
> On rk3576 the tunable clocks are inside the controller itself, removing
> the need for the "ciu-drive" and "ciu-sample" clocks.
>
> That makes it a new type of controller that has its own dt_parse function.
>
> Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
Reviewed-by: Heiko Stuebner <heiko@sntech.de>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-08-29 11:46 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20240828152446.42896-1-detlev.casanova@collabora.com>
2024-08-28 15:24 ` [PATCH v5 1/3] dt-bindings: mmc: Add support for rk3576 dw-mshc Detlev Casanova
2024-08-29 11:38 ` Heiko Stübner
2024-08-28 15:24 ` [PATCH v5 2/3] mmc: dw_mmc-rockchip: Add internal phase support Detlev Casanova
2024-08-29 11:41 ` Heiko Stübner
2024-08-28 15:24 ` [PATCH v5 3/3] mmc: dw_mmc-rockchip: Add support for rk3576 SoCs Detlev Casanova
2024-08-29 11:46 ` Heiko Stübner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).