Commit 57ea1423 authored by Stephen Boyd's avatar Stephen Boyd
Browse files

Merge tag 'v6.14-rockchip-clk1' of...

Merge tag 'v6.14-rockchip-clk1' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip into clk-rockchip

Pull Rockchip clk driver updates from Heiko Stuebner:

Real handling of the linked clocks (clocks of the interconnect port a
peripheral is connected to) on rk3588 using pm-clocks, allowing us to
stop marking them as critical and one more rk3588 critical clock, that
the kernel cannot handle otherwise right now.

* tag 'v6.14-rockchip-clk1' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip:
  clk: rockchip: rk3588: make refclko25m_ethX critical
  clk: rockchip: rk3588: drop RK3588_LINKED_CLK
  clk: rockchip: implement linked gate clock support
  clk: rockchip: expose rockchip_clk_set_lookup
  clk: rockchip: rk3588: register GATE_LINK later
  clk: rockchip: support clocks registered late
parents 40384c84 cd8b5366
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ clk-rockchip-y += clk-inverter.o
clk-rockchip-y += clk-mmc-phase.o
clk-rockchip-y += clk-muxgrf.o
clk-rockchip-y += clk-ddr.o
clk-rockchip-y += gate-link.o
clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o

obj-$(CONFIG_CLK_PX30)          += clk-px30.o
+74 −46
Original line number Diff line number Diff line
@@ -12,28 +12,6 @@
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
#include "clk.h"

/*
 * Recent Rockchip SoCs have a new hardware block called Native Interface
 * Unit (NIU), which gates clocks to devices behind them. These effectively
 * need two parent clocks.
 *
 * Downstream enables the linked clock via runtime PM whenever the gate is
 * enabled. This implementation uses separate clock nodes for each of the
 * linked gate clocks, which leaks parts of the clock tree into DT.
 *
 * The GATE_LINK macro instead takes the second parent via 'linkname', but
 * ignores the information. Once the clock framework is ready to handle it, the
 * information should be passed on here. But since these clocks are required to
 * access multiple relevant IP blocks, such as PCIe or USB, we mark all linked
 * clocks critical until a better solution is available. This will waste some
 * power, but avoids leaking implementation details into DT or hanging the
 * system.
 */
#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
	GATE(_id, cname, pname, f, o, b, gf)
#define RK3588_LINKED_CLK		CLK_IS_CRITICAL


#define RK3588_GRF_SOC_STATUS0		0x600
#define RK3588_PHYREF_ALT_GATE		0xc38

@@ -266,6 +244,8 @@ static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
	},							\
}

static struct rockchip_clk_provider *early_ctx;

static struct rockchip_cpuclk_rate_table rk3588_cpub0clk_rates[] __initdata = {
	RK3588_CPUB01CLK_RATE(2496000000, 1),
	RK3588_CPUB01CLK_RATE(2400000000, 1),
@@ -694,7 +674,7 @@ static struct rockchip_pll_clock rk3588_pll_clks[] __initdata = {
		     RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates),
};

static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
	/*
	 * CRU Clock-Architecture
	 */
@@ -792,10 +772,10 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
	COMPOSITE(MCLK_GMAC0_OUT, "mclk_gmac0_out", gpll_cpll_p, 0,
			RK3588_CLKSEL_CON(15), 7, 1, MFLAGS, 0, 7, DFLAGS,
			RK3588_CLKGATE_CON(5), 3, GFLAGS),
	COMPOSITE(REFCLKO25M_ETH0_OUT, "refclko25m_eth0_out", gpll_cpll_p, 0,
	COMPOSITE(REFCLKO25M_ETH0_OUT, "refclko25m_eth0_out", gpll_cpll_p, CLK_IS_CRITICAL,
			RK3588_CLKSEL_CON(15), 15, 1, MFLAGS, 8, 7, DFLAGS,
			RK3588_CLKGATE_CON(5), 4, GFLAGS),
	COMPOSITE(REFCLKO25M_ETH1_OUT, "refclko25m_eth1_out", gpll_cpll_p, 0,
	COMPOSITE(REFCLKO25M_ETH1_OUT, "refclko25m_eth1_out", gpll_cpll_p, CLK_IS_CRITICAL,
			RK3588_CLKSEL_CON(16), 7, 1, MFLAGS, 0, 7, DFLAGS,
			RK3588_CLKGATE_CON(5), 5, GFLAGS),
	COMPOSITE(CLK_CIFOUT_OUT, "clk_cifout_out", gpll_cpll_24m_spll_p, 0,
@@ -1456,7 +1436,7 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
	COMPOSITE_NODIV(HCLK_NVM_ROOT,  "hclk_nvm_root", mux_200m_100m_50m_24m_p, 0,
			RK3588_CLKSEL_CON(77), 0, 2, MFLAGS,
			RK3588_CLKGATE_CON(31), 0, GFLAGS),
	COMPOSITE(ACLK_NVM_ROOT, "aclk_nvm_root", gpll_cpll_p, RK3588_LINKED_CLK,
	COMPOSITE(ACLK_NVM_ROOT, "aclk_nvm_root", gpll_cpll_p, 0,
			RK3588_CLKSEL_CON(77), 7, 1, MFLAGS, 2, 5, DFLAGS,
			RK3588_CLKGATE_CON(31), 1, GFLAGS),
	GATE(ACLK_EMMC, "aclk_emmc", "aclk_nvm_root", 0,
@@ -1685,13 +1665,13 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
			RK3588_CLKGATE_CON(42), 9, GFLAGS),

	/* vdpu */
	COMPOSITE(ACLK_VDPU_ROOT, "aclk_vdpu_root", gpll_cpll_aupll_p, RK3588_LINKED_CLK,
	COMPOSITE(ACLK_VDPU_ROOT, "aclk_vdpu_root", gpll_cpll_aupll_p, 0,
			RK3588_CLKSEL_CON(98), 5, 2, MFLAGS, 0, 5, DFLAGS,
			RK3588_CLKGATE_CON(44), 0, GFLAGS),
	COMPOSITE_NODIV(ACLK_VDPU_LOW_ROOT, "aclk_vdpu_low_root", mux_400m_200m_100m_24m_p, 0,
			RK3588_CLKSEL_CON(98), 7, 2, MFLAGS,
			RK3588_CLKGATE_CON(44), 1, GFLAGS),
	COMPOSITE_NODIV(HCLK_VDPU_ROOT, "hclk_vdpu_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
	COMPOSITE_NODIV(HCLK_VDPU_ROOT, "hclk_vdpu_root", mux_200m_100m_50m_24m_p, 0,
			RK3588_CLKSEL_CON(98), 9, 2, MFLAGS,
			RK3588_CLKGATE_CON(44), 2, GFLAGS),
	COMPOSITE(ACLK_JPEG_DECODER_ROOT, "aclk_jpeg_decoder_root", gpll_cpll_aupll_spll_p, 0,
@@ -1742,9 +1722,9 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
	COMPOSITE(ACLK_RKVENC0_ROOT, "aclk_rkvenc0_root", gpll_cpll_npll_p, 0,
			RK3588_CLKSEL_CON(102), 7, 2, MFLAGS, 2, 5, DFLAGS,
			RK3588_CLKGATE_CON(47), 1, GFLAGS),
	GATE(HCLK_RKVENC0, "hclk_rkvenc0", "hclk_rkvenc0_root", RK3588_LINKED_CLK,
	GATE(HCLK_RKVENC0, "hclk_rkvenc0", "hclk_rkvenc0_root", 0,
			RK3588_CLKGATE_CON(47), 4, GFLAGS),
	GATE(ACLK_RKVENC0, "aclk_rkvenc0", "aclk_rkvenc0_root", RK3588_LINKED_CLK,
	GATE(ACLK_RKVENC0, "aclk_rkvenc0", "aclk_rkvenc0_root", 0,
			RK3588_CLKGATE_CON(47), 5, GFLAGS),
	COMPOSITE(CLK_RKVENC0_CORE, "clk_rkvenc0_core", gpll_cpll_aupll_npll_p, 0,
			RK3588_CLKSEL_CON(102), 14, 2, MFLAGS, 9, 5, DFLAGS,
@@ -1754,10 +1734,10 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
			RK3588_CLKGATE_CON(48), 6, GFLAGS),

	/* vi */
	COMPOSITE(ACLK_VI_ROOT, "aclk_vi_root", gpll_cpll_npll_aupll_spll_p, RK3588_LINKED_CLK,
	COMPOSITE(ACLK_VI_ROOT, "aclk_vi_root", gpll_cpll_npll_aupll_spll_p, 0,
			RK3588_CLKSEL_CON(106), 5, 3, MFLAGS, 0, 5, DFLAGS,
			RK3588_CLKGATE_CON(49), 0, GFLAGS),
	COMPOSITE_NODIV(HCLK_VI_ROOT, "hclk_vi_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
	COMPOSITE_NODIV(HCLK_VI_ROOT, "hclk_vi_root", mux_200m_100m_50m_24m_p, 0,
			RK3588_CLKSEL_CON(106), 8, 2, MFLAGS,
			RK3588_CLKGATE_CON(49), 1, GFLAGS),
	COMPOSITE_NODIV(PCLK_VI_ROOT, "pclk_vi_root", mux_100m_50m_24m_p, 0,
@@ -1927,10 +1907,10 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
	COMPOSITE(ACLK_VOP_ROOT, "aclk_vop_root", gpll_cpll_dmyaupll_npll_spll_p, 0,
			RK3588_CLKSEL_CON(110), 5, 3, MFLAGS, 0, 5, DFLAGS,
			RK3588_CLKGATE_CON(52), 0, GFLAGS),
	COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, RK3588_LINKED_CLK,
	COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, 0,
			RK3588_CLKSEL_CON(110), 8, 2, MFLAGS,
			RK3588_CLKGATE_CON(52), 1, GFLAGS),
	COMPOSITE_NODIV(HCLK_VOP_ROOT, "hclk_vop_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
	COMPOSITE_NODIV(HCLK_VOP_ROOT, "hclk_vop_root", mux_200m_100m_50m_24m_p, 0,
			RK3588_CLKSEL_CON(110), 10, 2, MFLAGS,
			RK3588_CLKGATE_CON(52), 2, GFLAGS),
	COMPOSITE_NODIV(PCLK_VOP_ROOT, "pclk_vop_root", mux_100m_50m_24m_p, 0,
@@ -2428,10 +2408,12 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
			RK3588_CLKGATE_CON(68), 5, GFLAGS),
	GATE(ACLK_AV1, "aclk_av1", "aclk_av1_pre", 0,
			RK3588_CLKGATE_CON(68), 2, GFLAGS),
};

static struct rockchip_clk_branch rk3588_clk_branches[] = {
	GATE_LINK(ACLK_ISP1_PRE, "aclk_isp1_pre", "aclk_isp1_root", ACLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 6, GFLAGS),
	GATE_LINK(HCLK_ISP1_PRE, "hclk_isp1_pre", "hclk_isp1_root", HCLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 8, GFLAGS),
	GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", ACLK_NVM_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(31), 2, GFLAGS),
	GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", ACLK_NVM_ROOT, 0, RK3588_CLKGATE_CON(31), 2, GFLAGS),
	GATE_LINK(ACLK_USB, "aclk_usb", "aclk_usb_root", ACLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(42), 2, GFLAGS),
	GATE_LINK(HCLK_USB, "hclk_usb", "hclk_usb_root", HCLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(42), 3, GFLAGS),
	GATE_LINK(ACLK_JPEG_DECODER_PRE, "aclk_jpeg_decoder_pre", "aclk_jpeg_decoder_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(44), 7, GFLAGS),
@@ -2443,9 +2425,9 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
	GATE_LINK(HCLK_RKVDEC1_PRE, "hclk_rkvdec1_pre", "hclk_rkvdec1_root", HCLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(41), 4, GFLAGS),
	GATE_LINK(ACLK_RKVDEC1_PRE, "aclk_rkvdec1_pre", "aclk_rkvdec1_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(41), 5, GFLAGS),
	GATE_LINK(ACLK_HDCP0_PRE, "aclk_hdcp0_pre", "aclk_vo0_root", ACLK_VOP_LOW_ROOT, 0, RK3588_CLKGATE_CON(55), 9, GFLAGS),
	GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", HCLK_VOP_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(55), 5, GFLAGS),
	GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", HCLK_VOP_ROOT, 0, RK3588_CLKGATE_CON(55), 5, GFLAGS),
	GATE_LINK(ACLK_HDCP1_PRE, "aclk_hdcp1_pre", "aclk_hdcp1_root", ACLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(59), 6, GFLAGS),
	GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", HCLK_VO1USB_TOP_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(59), 9, GFLAGS),
	GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", HCLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(59), 9, GFLAGS),
	GATE_LINK(ACLK_AV1_PRE, "aclk_av1_pre", "aclk_av1_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(68), 1, GFLAGS),
	GATE_LINK(PCLK_AV1_PRE, "pclk_av1_pre", "pclk_av1_root", HCLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(68), 4, GFLAGS),
	GATE_LINK(HCLK_SDIO_PRE, "hclk_sdio_pre", "hclk_sdio_root", HCLK_NVM, 0, RK3588_CLKGATE_CON(75), 1, GFLAGS),
@@ -2453,26 +2435,31 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
	GATE_LINK(PCLK_VO1GRF, "pclk_vo1grf", "pclk_vo1_root", HCLK_VO1, CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(59), 12, GFLAGS),
};

static void __init rk3588_clk_init(struct device_node *np)
static void __init rk3588_clk_early_init(struct device_node *np)
{
	struct rockchip_clk_provider *ctx;
	unsigned long clk_nr_clks;
	unsigned long clk_nr_clks, max_clk_id1, max_clk_id2;
	void __iomem *reg_base;

	clk_nr_clks = rockchip_clk_find_max_clk_id(rk3588_clk_branches,
					ARRAY_SIZE(rk3588_clk_branches)) + 1;
	max_clk_id1 = rockchip_clk_find_max_clk_id(rk3588_clk_branches,
					ARRAY_SIZE(rk3588_clk_branches));
	max_clk_id2 = rockchip_clk_find_max_clk_id(rk3588_early_clk_branches,
					ARRAY_SIZE(rk3588_early_clk_branches));
	clk_nr_clks = max(max_clk_id1, max_clk_id2) + 1;

	reg_base = of_iomap(np, 0);
	if (!reg_base) {
		pr_err("%s: could not map cru region\n", __func__);
		return;
	}

	ctx = rockchip_clk_init(np, reg_base, clk_nr_clks);
	ctx = rockchip_clk_init_early(np, reg_base, clk_nr_clks);
	if (IS_ERR(ctx)) {
		pr_err("%s: rockchip clk init failed\n", __func__);
		iounmap(reg_base);
		return;
	}
	early_ctx = ctx;

	rockchip_clk_register_plls(ctx, rk3588_pll_clks,
				   ARRAY_SIZE(rk3588_pll_clks),
@@ -2491,14 +2478,55 @@ static void __init rk3588_clk_init(struct device_node *np)
			&rk3588_cpub1clk_data, rk3588_cpub1clk_rates,
			ARRAY_SIZE(rk3588_cpub1clk_rates));

	rockchip_clk_register_branches(ctx, rk3588_clk_branches,
	rockchip_clk_register_branches(ctx, rk3588_early_clk_branches,
				       ARRAY_SIZE(rk3588_early_clk_branches));

	rockchip_clk_of_add_provider(np, ctx);
}
CLK_OF_DECLARE_DRIVER(rk3588_cru, "rockchip,rk3588-cru", rk3588_clk_early_init);

static int clk_rk3588_probe(struct platform_device *pdev)
{
	struct rockchip_clk_provider *ctx = early_ctx;
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;

	rockchip_clk_register_late_branches(dev, ctx, rk3588_clk_branches,
					    ARRAY_SIZE(rk3588_clk_branches));

	rk3588_rst_init(np, reg_base);
	rockchip_clk_finalize(ctx);

	rk3588_rst_init(np, ctx->reg_base);
	rockchip_register_restart_notifier(ctx, RK3588_GLB_SRST_FST, NULL);

	/*
	 * Re-add clock provider, so that the newly added clocks are also
	 * re-parented and get their defaults configured.
	 */
	of_clk_del_provider(np);
	rockchip_clk_of_add_provider(np, ctx);

	return 0;
}

CLK_OF_DECLARE(rk3588_cru, "rockchip,rk3588-cru", rk3588_clk_init);
static const struct of_device_id clk_rk3588_match_table[] = {
	{
		.compatible = "rockchip,rk3588-cru",
	},
	{ }
};

static struct platform_driver clk_rk3588_driver = {
	.probe		= clk_rk3588_probe,
	.driver		= {
		.name	= "clk-rk3588",
		.of_match_table = clk_rk3588_match_table,
		.suppress_bind_attrs = true,
	},
};

static int __init rockchip_clk_rk3588_drv_register(void)
{
	return platform_driver_register(&clk_rk3588_driver);
}
core_initcall(rockchip_clk_rk3588_drv_register);
+88 −14
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reboot.h>

@@ -197,12 +198,6 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
	clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n);
}

static void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
				    struct clk *clk, unsigned int id)
{
	ctx->clk_data.clks[id] = clk;
}

static struct clk *rockchip_clk_register_frac_branch(
		struct rockchip_clk_provider *ctx, const char *name,
		const char *const *parent_names, u8 num_parents,
@@ -292,7 +287,7 @@ static struct clk *rockchip_clk_register_frac_branch(
			return mux_clk;
		}

		rockchip_clk_add_lookup(ctx, mux_clk, child->id);
		rockchip_clk_set_lookup(ctx, mux_clk, child->id);

		/* notifier on the fraction divider to catch rate changes */
		if (frac->mux_frac_idx >= 0) {
@@ -359,14 +354,17 @@ static struct clk *rockchip_clk_register_factor_branch(const char *name,
	return hw->clk;
}

struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
						void __iomem *base,
						unsigned long nr_clks)
static struct rockchip_clk_provider *rockchip_clk_init_base(
		struct device_node *np, void __iomem *base,
		unsigned long nr_clks, bool has_late_clocks)
{
	struct rockchip_clk_provider *ctx;
	struct clk **clk_table;
	struct clk *default_clk_val;
	int i;

	default_clk_val = ERR_PTR(has_late_clocks ? -EPROBE_DEFER : -ENOENT);

	ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
	if (!ctx)
		return ERR_PTR(-ENOMEM);
@@ -376,7 +374,7 @@ struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
		goto err_free;

	for (i = 0; i < nr_clks; ++i)
		clk_table[i] = ERR_PTR(-ENOENT);
		clk_table[i] = default_clk_val;

	ctx->reg_base = base;
	ctx->clk_data.clks = clk_table;
@@ -393,8 +391,33 @@ struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
	kfree(ctx);
	return ERR_PTR(-ENOMEM);
}

struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
						void __iomem *base,
						unsigned long nr_clks)
{
	return rockchip_clk_init_base(np, base, nr_clks, false);
}
EXPORT_SYMBOL_GPL(rockchip_clk_init);

struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np,
						      void __iomem *base,
						      unsigned long nr_clks)
{
	return rockchip_clk_init_base(np, base, nr_clks, true);
}
EXPORT_SYMBOL_GPL(rockchip_clk_init_early);

void rockchip_clk_finalize(struct rockchip_clk_provider *ctx)
{
	int i;

	for (i = 0; i < ctx->clk_data.clk_num; ++i)
		if (ctx->clk_data.clks[i] == ERR_PTR(-EPROBE_DEFER))
			ctx->clk_data.clks[i] = ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL_GPL(rockchip_clk_finalize);

void rockchip_clk_of_add_provider(struct device_node *np,
				  struct rockchip_clk_provider *ctx)
{
@@ -424,7 +447,7 @@ void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
			continue;
		}

		rockchip_clk_add_lookup(ctx, clk, list->id);
		rockchip_clk_set_lookup(ctx, clk, list->id);
	}
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_plls);
@@ -446,6 +469,29 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
}
EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id);

static struct platform_device *rockchip_clk_register_gate_link(
		struct device *parent_dev,
		struct rockchip_clk_provider *ctx,
		struct rockchip_clk_branch *clkbr)
{
	struct rockchip_gate_link_platdata gate_link_pdata = {
		.ctx = ctx,
		.clkbr = clkbr,
	};

	struct platform_device_info pdevinfo = {
		.parent = parent_dev,
		.name = "rockchip-gate-link-clk",
		.id = clkbr->id,
		.fwnode = dev_fwnode(parent_dev),
		.of_node_reused = true,
		.data = &gate_link_pdata,
		.size_data = sizeof(gate_link_pdata),
	};

	return platform_device_register_full(&pdevinfo);
}

void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
				    struct rockchip_clk_branch *list,
				    unsigned int nr_clk)
@@ -571,6 +617,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
				list->div_width, list->div_flags,
				ctx->reg_base, &ctx->lock);
			break;
		case branch_linked_gate:
			/* must be registered late, fall-through for error message */
			break;
		}

		/* none of the cases above matched */
@@ -586,11 +635,36 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
			continue;
		}

		rockchip_clk_add_lookup(ctx, clk, list->id);
		rockchip_clk_set_lookup(ctx, clk, list->id);
	}
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);

void rockchip_clk_register_late_branches(struct device *dev,
					 struct rockchip_clk_provider *ctx,
					 struct rockchip_clk_branch *list,
					 unsigned int nr_clk)
{
	unsigned int idx;

	for (idx = 0; idx < nr_clk; idx++, list++) {
		struct platform_device *pdev = NULL;

		switch (list->branch_type) {
		case branch_linked_gate:
			pdev = rockchip_clk_register_gate_link(dev, ctx, list);
			break;
		default:
			dev_err(dev, "unknown clock type %d\n", list->branch_type);
			break;
		}

		if (!pdev)
			dev_err(dev, "failed to register device for clock %s\n", list->name);
	}
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_late_branches);

void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
				  unsigned int lookup_id,
				  const char *name, const char *const *parent_names,
@@ -610,7 +684,7 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
		return;
	}

	rockchip_clk_add_lookup(ctx, clk, lookup_id);
	rockchip_clk_set_lookup(ctx, clk, lookup_id);
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);

+40 −0
Original line number Diff line number Diff line
@@ -570,6 +570,7 @@ enum rockchip_clk_branch_type {
	branch_divider,
	branch_fraction_divider,
	branch_gate,
	branch_linked_gate,
	branch_mmc,
	branch_inverter,
	branch_factor,
@@ -597,6 +598,7 @@ struct rockchip_clk_branch {
	int				gate_offset;
	u8				gate_shift;
	u8				gate_flags;
	unsigned int			linked_clk_id;
	struct rockchip_clk_branch	*child;
};

@@ -895,6 +897,20 @@ struct rockchip_clk_branch {
		.gate_flags	= gf,				\
	}

#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf)	\
	{							\
		.id		= _id,				\
		.branch_type	= branch_linked_gate,		\
		.name		= cname,			\
		.parent_names	= (const char *[]){ pname },	\
		.linked_clk_id	= linkedclk,			\
		.num_parents	= 1,				\
		.flags		= f,				\
		.gate_offset	= o,				\
		.gate_shift	= b,				\
		.gate_flags	= gf,				\
	}

#define MMC(_id, cname, pname, offset, shift)			\
	{							\
		.id		= _id,				\
@@ -1022,8 +1038,28 @@ struct rockchip_clk_branch {
#define SGRF_GATE(_id, cname, pname)				\
		FACTOR(_id, cname, pname, 0, 1, 1)

static inline struct clk *rockchip_clk_get_lookup(struct rockchip_clk_provider *ctx,
						  unsigned int id)
{
	return ctx->clk_data.clks[id];
}

static inline void rockchip_clk_set_lookup(struct rockchip_clk_provider *ctx,
					   struct clk *clk, unsigned int id)
{
	ctx->clk_data.clks[id] = clk;
}

struct rockchip_gate_link_platdata {
	struct rockchip_clk_provider *ctx;
	struct rockchip_clk_branch *clkbr;
};

struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
			void __iomem *base, unsigned long nr_clks);
struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np,
			void __iomem *base, unsigned long nr_clks);
void rockchip_clk_finalize(struct rockchip_clk_provider *ctx);
void rockchip_clk_of_add_provider(struct device_node *np,
				struct rockchip_clk_provider *ctx);
unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
@@ -1031,6 +1067,10 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
				    struct rockchip_clk_branch *list,
				    unsigned int nr_clk);
void rockchip_clk_register_late_branches(struct device *dev,
					 struct rockchip_clk_provider *ctx,
					 struct rockchip_clk_branch *list,
					 unsigned int nr_clk);
void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
				struct rockchip_pll_clock *pll_list,
				unsigned int nr_pll, int grf_lock_offset);
+85 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2024 Collabora Ltd.
 * Author: Sebastian Reichel <sebastian.reichel@collabora.com>
 */

#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include "clk.h"

static int rk_clk_gate_link_register(struct device *dev,
				     struct rockchip_clk_provider *ctx,
				     struct rockchip_clk_branch *clkbr)
{
	unsigned long flags = clkbr->flags | CLK_SET_RATE_PARENT;
	struct clk *clk;

	clk = clk_register_gate(dev, clkbr->name, clkbr->parent_names[0],
				flags, ctx->reg_base + clkbr->gate_offset,
				clkbr->gate_shift, clkbr->gate_flags,
				&ctx->lock);

	if (IS_ERR(clk))
		return PTR_ERR(clk);

	rockchip_clk_set_lookup(ctx, clk, clkbr->id);
	return 0;
}

static int rk_clk_gate_link_probe(struct platform_device *pdev)
{
	struct rockchip_gate_link_platdata *pdata;
	struct device *dev = &pdev->dev;
	struct clk *linked_clk;
	int ret;

	pdata = dev_get_platdata(dev);
	if (!pdata)
		return dev_err_probe(dev, -ENODEV, "missing platform data");

	ret = devm_pm_runtime_enable(dev);
	if (ret)
		return ret;

	ret = devm_pm_clk_create(dev);
	if (ret)
		return ret;

	linked_clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->linked_clk_id);
	ret = pm_clk_add_clk(dev, linked_clk);
	if (ret)
		return ret;

	ret = rk_clk_gate_link_register(dev, pdata->ctx, pdata->clkbr);
	if (ret)
		goto err;

	return 0;

err:
	pm_clk_remove_clk(dev, linked_clk);
	return ret;
}

static const struct dev_pm_ops rk_clk_gate_link_pm_ops = {
	SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
};

static struct platform_driver rk_clk_gate_link_driver = {
	.probe		= rk_clk_gate_link_probe,
	.driver		= {
		.name	= "rockchip-gate-link-clk",
		.pm = &rk_clk_gate_link_pm_ops,
		.suppress_bind_attrs = true,
	},
};

static int __init rk_clk_gate_link_drv_register(void)
{
	return platform_driver_register(&rk_clk_gate_link_driver);
}
core_initcall(rk_clk_gate_link_drv_register);