Booting from sdmmc on RK3588 currently works because of a workaround in
the device tree, clocks are reordered so that the driver use ciu-sample
instead of ciu, and the BootRom initializes sdmmc clocks before SPL is
loaded into DRAM.

The sdmmc clocks are normally controlled by TF-A using SCMI. However,
there is a need to control these clocks in SPL, before TF-A has started.

This adds a rk3588_scru driver to control the sdmmc clocks in SPL before
TF-A has started, using scru regs. It also adds a small glue driver to
bind the scmi clock node to the rk3588_scru driver in SPL.

Fixes: 7a474df74023 ("clk: rockchip: Add rk3588 clk support")
Signed-off-by: Jonas Karlman <jo...@kwiboo.se>
---
 arch/arm/include/asm/arch-rockchip/clock.h    |   1 -
 .../include/asm/arch-rockchip/cru_rk3588.h    |  19 ++-
 drivers/clk/rockchip/clk_rk3588.c             | 125 ++++++++++++++++++
 3 files changed, 143 insertions(+), 2 deletions(-)

diff --git a/arch/arm/include/asm/arch-rockchip/clock.h 
b/arch/arm/include/asm/arch-rockchip/clock.h
index 90e66c7da049..f002ebcb7ac1 100644
--- a/arch/arm/include/asm/arch-rockchip/clock.h
+++ b/arch/arm/include/asm/arch-rockchip/clock.h
@@ -194,6 +194,5 @@ int rockchip_get_clk(struct udevice **devp);
  * Return: 0 success, or error value
  */
 int rockchip_reset_bind(struct udevice *pdev, u32 reg_offset, u32 reg_number);
-int rockchip_get_scmi_clk(struct udevice **devp);
 
 #endif
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3588.h 
b/arch/arm/include/asm/arch-rockchip/cru_rk3588.h
index 3ea59e900861..7f4a90853929 100644
--- a/arch/arm/include/asm/arch-rockchip/cru_rk3588.h
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3588.h
@@ -11,12 +11,12 @@
 #define KHz            1000
 #define OSC_HZ         (24 * MHz)
 
-#define CPU_PVTPLL_HZ  (1008 * MHz)
 #define LPLL_HZ                (816 * MHz)
 #define GPLL_HZ                (1188 * MHz)
 #define CPLL_HZ                (1500 * MHz)
 #define NPLL_HZ         (850 * MHz)
 #define PPLL_HZ                (1100 * MHz)
+#define SPLL_HZ                (702 * MHz)
 
 /* RK3588 pll id */
 enum rk3588_pll_id {
@@ -447,5 +447,22 @@ enum {
        CLK_I2C0_SEL_MASK                       = 1 << CLK_I2C0_SEL_SHIFT,
        CLK_I2C_SEL_200M                        = 0,
        CLK_I2C_SEL_100M,
+
+       /* SECURECRU_CLKSEL_CON01 */
+       SCMI_HCLK_SD_SEL_SHIFT                  = 2,
+       SCMI_HCLK_SD_SEL_MASK                   = 3 << SCMI_HCLK_SD_SEL_SHIFT,
+       SCMI_HCLK_SD_SEL_150M                   = 0,
+       SCMI_HCLK_SD_SEL_100M,
+       SCMI_HCLK_SD_SEL_50M,
+       SCMI_HCLK_SD_SEL_24M,
+
+       /* SECURECRU_CLKSEL_CON03 */
+       SCMI_CCLK_SD_SEL_SHIFT                  = 12,
+       SCMI_CCLK_SD_SEL_MASK                   = 3 << SCMI_CCLK_SD_SEL_SHIFT,
+       SCMI_CCLK_SD_SEL_GPLL                   = 0,
+       SCMI_CCLK_SD_SEL_SPLL,
+       SCMI_CCLK_SD_SEL_24M,
+       SCMI_CCLK_SD_DIV_SHIFT                  = 6,
+       SCMI_CCLK_SD_DIV_MASK                   = 0x3f << 
SCMI_CCLK_SD_DIV_SHIFT,
 };
 #endif
diff --git a/drivers/clk/rockchip/clk_rk3588.c 
b/drivers/clk/rockchip/clk_rk3588.c
index a7df553e8750..41e31b61a55b 100644
--- a/drivers/clk/rockchip/clk_rk3588.c
+++ b/drivers/clk/rockchip/clk_rk3588.c
@@ -9,6 +9,7 @@
 #include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
+#include <scmi_protocols.h>
 #include <syscon.h>
 #include <asm/arch-rockchip/cru_rk3588.h>
 #include <asm/arch-rockchip/clock.h>
@@ -1994,3 +1995,127 @@ U_BOOT_DRIVER(rockchip_rk3588_cru) = {
        .bind           = rk3588_clk_bind,
        .probe          = rk3588_clk_probe,
 };
+
+#ifdef CONFIG_SPL_BUILD
+#define SCRU_BASE                      0xfd7d0000
+
+static ulong rk3588_scru_clk_get_rate(struct clk *clk)
+{
+       u32 con, div, sel, parent;
+
+       switch (clk->id) {
+       case SCMI_CCLK_SD:
+               con = readl(SCRU_BASE + RK3588_CLKSEL_CON(3));
+               sel = (con & SCMI_CCLK_SD_SEL_MASK) >> SCMI_CCLK_SD_SEL_SHIFT;
+               div = (con & SCMI_CCLK_SD_DIV_MASK) >> SCMI_CCLK_SD_DIV_SHIFT;
+               if (sel == SCMI_CCLK_SD_SEL_GPLL)
+                       parent = GPLL_HZ;
+               else if (sel == SCMI_CCLK_SD_SEL_SPLL)
+                       parent = SPLL_HZ;
+               else
+                       parent = OSC_HZ;
+               return DIV_TO_RATE(parent, div);
+       case SCMI_HCLK_SD:
+               con = readl(SCRU_BASE + RK3588_CLKSEL_CON(1));
+               sel = (con & SCMI_HCLK_SD_SEL_MASK) >> SCMI_HCLK_SD_SEL_SHIFT;
+               if (sel == SCMI_HCLK_SD_SEL_150M)
+                       return 150 * MHz;
+               else if (sel == SCMI_HCLK_SD_SEL_100M)
+                       return 100 * MHz;
+               else if (sel == SCMI_HCLK_SD_SEL_50M)
+                       return 50 * MHz;
+               else
+                       return OSC_HZ;
+       default:
+               return -ENOENT;
+       }
+}
+
+static ulong rk3588_scru_clk_set_rate(struct clk *clk, ulong rate)
+{
+       u32 div, sel;
+
+       switch (clk->id) {
+       case SCMI_CCLK_SD:
+               if ((OSC_HZ % rate) == 0) {
+                       sel = SCMI_CCLK_SD_SEL_24M;
+                       div = DIV_ROUND_UP(OSC_HZ, rate);
+               } else if ((SPLL_HZ % rate) == 0) {
+                       sel = SCMI_CCLK_SD_SEL_SPLL;
+                       div = DIV_ROUND_UP(SPLL_HZ, rate);
+               } else {
+                       sel = SCMI_CCLK_SD_SEL_GPLL;
+                       div = DIV_ROUND_UP(GPLL_HZ, rate);
+               }
+               rk_clrsetreg(SCRU_BASE + RK3588_CLKSEL_CON(3),
+                            SCMI_CCLK_SD_SEL_MASK | SCMI_CCLK_SD_DIV_MASK,
+                            sel << SCMI_CCLK_SD_SEL_SHIFT |
+                            (div - 1) << SCMI_CCLK_SD_DIV_SHIFT);
+               break;
+       case SCMI_HCLK_SD:
+               if (rate >= 150 * MHz)
+                       sel = SCMI_HCLK_SD_SEL_150M;
+               else if (rate >= 100 * MHz)
+                       sel = SCMI_HCLK_SD_SEL_100M;
+               else if (rate >= 50 * MHz)
+                       sel = SCMI_HCLK_SD_SEL_50M;
+               else
+                       sel = SCMI_HCLK_SD_SEL_24M;
+               rk_clrsetreg(SCRU_BASE + RK3588_CLKSEL_CON(1),
+                            SCMI_HCLK_SD_SEL_MASK,
+                            sel << SCMI_HCLK_SD_SEL_SHIFT);
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       return rk3588_scru_clk_get_rate(clk);
+}
+
+static const struct clk_ops rk3588_scru_clk_ops = {
+       .get_rate = rk3588_scru_clk_get_rate,
+       .set_rate = rk3588_scru_clk_set_rate,
+};
+
+U_BOOT_DRIVER(rockchip_rk3588_scru) = {
+       .name = "rockchip_rk3588_scru",
+       .id = UCLASS_CLK,
+       .ops = &rk3588_scru_clk_ops,
+};
+
+static int rk3588_scmi_spl_glue_bind(struct udevice *dev)
+{
+       ofnode node;
+       u32 protocol_id;
+       const char *name;
+
+       dev_for_each_subnode(node, dev) {
+               if (!ofnode_is_enabled(node))
+                       continue;
+
+               if (ofnode_read_u32(node, "reg", &protocol_id))
+                       continue;
+
+               if (protocol_id != SCMI_PROTOCOL_ID_CLOCK)
+                       continue;
+
+               name = ofnode_get_name(node);
+               return device_bind_driver_to_node(dev, "rockchip_rk3588_scru",
+                                                 name, node, NULL);
+       }
+
+       return -ENOENT;
+}
+
+static const struct udevice_id rk3588_scmi_spl_glue_ids[] = {
+       { .compatible = "arm,scmi-smc" },
+       { }
+};
+
+U_BOOT_DRIVER(rk3588_scmi_spl_glue) = {
+       .name           = "rk3588_scmi_spl_glue",
+       .id             = UCLASS_NOP,
+       .of_match       = rk3588_scmi_spl_glue_ids,
+       .bind           = rk3588_scmi_spl_glue_bind,
+};
+#endif
-- 
2.40.0

Reply via email to