Commit 09febae2 authored by Stephen Boyd's avatar Stephen Boyd
Browse files

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

Merge tag 'v6.16-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:

 - Ability to handle different "General Register Files" syscons, not
   just a single system-one, plus ability to model individual gates
   found there.

 - For whatever reason Rockchip also moved the mmc-phase-clocks from the
   clock-unit for the GRF on some newer socs like the rk3528 (before
   moving them fully to the mmc controller itself on the rk3576), so add
   a new clock-variant for the phases, reusing the new GRF handling.

 - The old rk3036 got real handling of the usb480m mux and some PLL
   rates were added.

* tag 'v6.16-rockchip-clk1' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip:
  clk: rockchip: rk3528: add slab.h header include
  clk: rockchip: rk3576: add missing slab.h include
  clk: rockchip: rename gate-grf clk file
  clk: rockchip: rename branch_muxgrf to branch_grf_mux
  clk: rockchip: Pass NULL as reg pointer when registering GRF MMC clocks
  clk: rockchip: rk3036: mark ddrphy as critical
  clk: rockchip: rk3036: fix implementation of usb480m clock mux
  dt-bindings: clock: rk3036: add SCLK_USB480M clock-id
  clk: rockchip: rk3528: Add SD/SDIO tuning clocks in GRF region
  clk: rockchip: Support MMC clocks in GRF region
  dt-bindings: clock: Add GRF clock definition for RK3528
  clk: rockchip: add GATE_GRFs for SAI MCLKOUT to rk3576
  clk: rockchip: introduce GRF gates
  clk: rockchip: introduce auxiliary GRFs
  dt-bindings: clock: rk3576: add IOC gated clocks
  clk: rockchip: rk3568: Add PLL rate for 33.3MHz
  clk: rockchip: Drop empty init callback for rk3588 PLL type
  clk: rockchip: rk3588: Add PLL rate for 1500 MHz
parents 0af2f6be 27603628
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK_ROCKCHIP) += clk-rockchip.o
clk-rockchip-y += clk.o
clk-rockchip-y += clk-pll.o
clk-rockchip-y += clk-cpu.o
clk-rockchip-y += clk-gate-grf.o
clk-rockchip-y += clk-half-divider.o
clk-rockchip-y += clk-inverter.o
clk-rockchip-y += clk-mmc-phase.o
+105 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2025 Collabora Ltd.
 * Author: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
 *
 * Certain clocks on Rockchip are "gated" behind an additional register bit
 * write in a GRF register, such as the SAI MCLKs on RK3576. This code
 * implements a clock driver for these types of gates, based on regmaps.
 */

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include "clk.h"

struct rockchip_gate_grf {
	struct clk_hw		hw;
	struct regmap		*regmap;
	unsigned int		reg;
	unsigned int		shift;
	u8			flags;
};

#define to_gate_grf(_hw) container_of(_hw, struct rockchip_gate_grf, hw)

static int rockchip_gate_grf_enable(struct clk_hw *hw)
{
	struct rockchip_gate_grf *gate = to_gate_grf(hw);
	u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? BIT(gate->shift) : 0;
	u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16);
	int ret;

	ret = regmap_update_bits(gate->regmap, gate->reg,
				 hiword | BIT(gate->shift), hiword | val);

	return ret;
}

static void rockchip_gate_grf_disable(struct clk_hw *hw)
{
	struct rockchip_gate_grf *gate = to_gate_grf(hw);
	u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : BIT(gate->shift);
	u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16);

	regmap_update_bits(gate->regmap, gate->reg,
			   hiword | BIT(gate->shift), hiword | val);
}

static int rockchip_gate_grf_is_enabled(struct clk_hw *hw)
{
	struct rockchip_gate_grf *gate = to_gate_grf(hw);
	bool invert = !!(gate->flags & CLK_GATE_SET_TO_DISABLE);
	int ret;

	ret = regmap_test_bits(gate->regmap, gate->reg, BIT(gate->shift));
	if (ret < 0)
		ret = 0;

	return invert ? 1 - ret : ret;

}

static const struct clk_ops rockchip_gate_grf_ops = {
	.enable = rockchip_gate_grf_enable,
	.disable = rockchip_gate_grf_disable,
	.is_enabled = rockchip_gate_grf_is_enabled,
};

struct clk *rockchip_clk_register_gate_grf(const char *name,
		const char *parent_name, unsigned long flags,
		struct regmap *regmap, unsigned int reg, unsigned int shift,
		u8 gate_flags)
{
	struct rockchip_gate_grf *gate;
	struct clk_init_data init;
	struct clk *clk;

	if (IS_ERR(regmap)) {
		pr_err("%s: regmap not available\n", __func__);
		return ERR_PTR(-EOPNOTSUPP);
	}

	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
	if (!gate)
		return ERR_PTR(-ENOMEM);

	init.name = name;
	init.flags = flags;
	init.num_parents = parent_name ? 1 : 0;
	init.parent_names = parent_name ? &parent_name : NULL;
	init.ops = &rockchip_gate_grf_ops;

	gate->hw.init = &init;
	gate->regmap = regmap;
	gate->reg = reg;
	gate->shift = shift;
	gate->flags = gate_flags;

	clk = clk_register(NULL, &gate->hw);
	if (IS_ERR(clk))
		kfree(gate);

	return clk;
}
+20 −4
Original line number Diff line number Diff line
@@ -9,11 +9,14 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/regmap.h>
#include "clk.h"

struct rockchip_mmc_clock {
	struct clk_hw	hw;
	void __iomem	*reg;
	struct regmap	*grf;
	int		grf_reg;
	int		shift;
	int		cached_phase;
	struct notifier_block clk_rate_change_nb;
@@ -54,7 +57,12 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
	if (!rate)
		return 0;

	raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
	if (mmc_clock->grf)
		regmap_read(mmc_clock->grf, mmc_clock->grf_reg, &raw_value);
	else
		raw_value = readl(mmc_clock->reg);

	raw_value >>= mmc_clock->shift;

	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;

@@ -134,8 +142,12 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
	raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
	raw_value |= nineties;
	writel(HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift),
	       mmc_clock->reg);
	raw_value = HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift);

	if (mmc_clock->grf)
		regmap_write(mmc_clock->grf, mmc_clock->grf_reg, raw_value);
	else
		writel(raw_value, mmc_clock->reg);

	pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%03x actual_degrees=%d\n",
		clk_hw_get_name(hw), degrees, delay_num,
@@ -189,7 +201,9 @@ static int rockchip_mmc_clk_rate_notify(struct notifier_block *nb,

struct clk *rockchip_clk_register_mmc(const char *name,
				const char *const *parent_names, u8 num_parents,
				void __iomem *reg, int shift)
				void __iomem *reg,
				struct regmap *grf, int grf_reg,
				int shift)
{
	struct clk_init_data init;
	struct rockchip_mmc_clock *mmc_clock;
@@ -208,6 +222,8 @@ struct clk *rockchip_clk_register_mmc(const char *name,

	mmc_clock->hw.init = &init;
	mmc_clock->reg = reg;
	mmc_clock->grf = grf;
	mmc_clock->grf_reg = grf_reg;
	mmc_clock->shift = shift;

	clk = clk_register(NULL, &mmc_clock->hw);
+0 −11
Original line number Diff line number Diff line
@@ -1027,16 +1027,6 @@ static int rockchip_rk3588_pll_is_enabled(struct clk_hw *hw)
	return !(pllcon & RK3588_PLLCON1_PWRDOWN);
}

static int rockchip_rk3588_pll_init(struct clk_hw *hw)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);

	if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
		return 0;

	return 0;
}

static const struct clk_ops rockchip_rk3588_pll_clk_norate_ops = {
	.recalc_rate = rockchip_rk3588_pll_recalc_rate,
	.enable = rockchip_rk3588_pll_enable,
@@ -1051,7 +1041,6 @@ static const struct clk_ops rockchip_rk3588_pll_clk_ops = {
	.enable = rockchip_rk3588_pll_enable,
	.disable = rockchip_rk3588_pll_disable,
	.is_enabled = rockchip_rk3588_pll_is_enabled,
	.init = rockchip_rk3588_pll_init,
};

/*
+5 −6
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ PNAME(mux_timer_p) = { "xin24m", "pclk_peri_src" };
PNAME(mux_pll_src_apll_dpll_gpll_usb480m_p)	= { "apll", "dpll", "gpll", "usb480m" };
PNAME(mux_pll_src_dmyapll_dpll_gpll_xin24_p)   = { "dummy_apll", "dpll", "gpll", "xin24m" };

PNAME(mux_usb480m_p)	= { "usb480m_phy", "xin24m" };
PNAME(mux_mmc_src_p)	= { "apll", "dpll", "gpll", "xin24m" };
PNAME(mux_i2s_pre_p)	= { "i2s_src", "i2s_frac", "ext_i2s", "xin12m" };
PNAME(mux_i2s_clkout_p)	= { "i2s_pre", "xin12m" };
@@ -423,6 +424,9 @@ static struct rockchip_clk_branch rk3036_clk_branches[] __initdata = {
	GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 9, GFLAGS),
	GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 10, GFLAGS),
	GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 11, GFLAGS),

	MUX(SCLK_USB480M, "usb480m", mux_usb480m_p, CLK_SET_RATE_PARENT,
			RK2928_MISC_CON, 15, 1, MFLAGS),
};

static const char *const rk3036_critical_clocks[] __initconst = {
@@ -431,6 +435,7 @@ static const char *const rk3036_critical_clocks[] __initconst = {
	"hclk_peri",
	"pclk_peri",
	"pclk_ddrupctl",
	"ddrphy",
};

static void __init rk3036_clk_init(struct device_node *np)
@@ -438,7 +443,6 @@ static void __init rk3036_clk_init(struct device_node *np)
	struct rockchip_clk_provider *ctx;
	unsigned long clk_nr_clks;
	void __iomem *reg_base;
	struct clk *clk;

	reg_base = of_iomap(np, 0);
	if (!reg_base) {
@@ -462,11 +466,6 @@ static void __init rk3036_clk_init(struct device_node *np)
		return;
	}

	clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1);
	if (IS_ERR(clk))
		pr_warn("%s: could not register clock usb480m: %ld\n",
			__func__, PTR_ERR(clk));

	rockchip_clk_register_plls(ctx, rk3036_pll_clks,
				   ARRAY_SIZE(rk3036_pll_clks),
				   RK3036_GRF_SOC_STATUS0);
Loading