rockchip: rk3588: Add support for sdmmc clocks in SPL

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: 7a474df740 ("clk: rockchip: Add rk3588 clk support")
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>
This commit is contained in:
Jonas Karlman 2023-04-17 19:07:20 +00:00 committed by Kever Yang
parent 2229758118
commit 6737771600
3 changed files with 143 additions and 2 deletions

View file

@ -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

View file

@ -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

View file

@ -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