Commit 266162b7 authored by Stephen Boyd's avatar Stephen Boyd
Browse files

Merge tag 'v6.2-rockchip-clk-1' of...

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

Pull Rockchip clk driver updates from Heiko Stuebner:

 - Support for the clock and reset unit of the rk3588
 - Fix a possible memory leak in the error path of PLL creation

* tag 'v6.2-rockchip-clk-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip:
  clk: rockchip: Fix memory leak in rockchip_clk_register_pll()
  clk: rockchip: add clock controller for the RK3588
  clk: rockchip: add lookup table support
  clk: rockchip: simplify rockchip_clk_add_lookup
  clk: rockchip: allow additional mux options for cpu-clock frequency changes
  clk: rockchip: add pll type for RK3588
  clk: rockchip: add register offset of the cores select parent
  dt-bindings: clock: add rk3588 cru bindings
  dt-bindings: reset: add rk3588 reset definitions
  dt-bindings: clock: add rk3588 clock definitions
  clk: rockchip: use proper crypto0 name on rk3399
parents 9abf2313 739a6a6b
Loading
Loading
Loading
Loading
+71 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/rockchip,rk3588-cru.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Rockchip rk3588 Family Clock and Reset Control Module

maintainers:
  - Elaine Zhang <zhangqing@rock-chips.com>
  - Heiko Stuebner <heiko@sntech.de>

description: |
  The RK3588 clock controller generates the clock and also implements a reset
  controller for SoC peripherals. For example it provides SCLK_UART2 and
  PCLK_UART2, as well as SRST_P_UART2 and SRST_S_UART2 for the second UART
  module.
  Each clock is assigned an identifier and client nodes can use this identifier
  to specify the clock which they consume. All available clock and reset IDs
  are defined as preprocessor macros in dt-binding headers.

properties:
  compatible:
    enum:
      - rockchip,rk3588-cru

  reg:
    maxItems: 1

  "#clock-cells":
    const: 1

  "#reset-cells":
    const: 1

  clocks:
    minItems: 2
    maxItems: 2

  clock-names:
    items:
      - const: xin24m
      - const: xin32k

  assigned-clocks: true

  assigned-clock-rates: true

  rockchip,grf:
    $ref: /schemas/types.yaml#/definitions/phandle
    description: >
      phandle to the syscon managing the "general register files". It is used
      for GRF muxes, if missing any muxes present in the GRF will not be
      available.

required:
  - compatible
  - reg
  - "#clock-cells"
  - "#reset-cells"

additionalProperties: false

examples:
  - |
    cru: clock-controller@fd7c0000 {
      compatible = "rockchip,rk3588-cru";
      reg = <0xfd7c0000 0x5c000>;
      #clock-cells = <1>;
      #reset-cells = <1>;
    };
+8 −0
Original line number Diff line number Diff line
@@ -99,4 +99,12 @@ config CLK_RK3568
	default y
	help
	  Build the driver for RK3568 Clock Driver.

config CLK_RK3588
	bool "Rockchip RK3588 clock controller support"
	depends on ARM64 || COMPILE_TEST
	default y
	help
	  Build the driver for RK3588 Clock Driver.

endif
+1 −0
Original line number Diff line number Diff line
@@ -28,3 +28,4 @@ obj-$(CONFIG_CLK_RK3328) += clk-rk3328.o
obj-$(CONFIG_CLK_RK3368)        += clk-rk3368.o
obj-$(CONFIG_CLK_RK3399)        += clk-rk3399.o
obj-$(CONFIG_CLK_RK3568)	+= clk-rk3568.o
obj-$(CONFIG_CLK_RK3588)	+= clk-rk3588.o rst-rk3588.o
+61 −8
Original line number Diff line number Diff line
@@ -113,6 +113,42 @@ static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk,
	}
}

static void rockchip_cpuclk_set_pre_muxs(struct rockchip_cpuclk *cpuclk,
					 const struct rockchip_cpuclk_rate_table *rate)
{
	int i;

	/* alternate parent is active now. set the pre_muxs */
	for (i = 0; i < ARRAY_SIZE(rate->pre_muxs); i++) {
		const struct rockchip_cpuclk_clksel *clksel = &rate->pre_muxs[i];

		if (!clksel->reg)
			break;

		pr_debug("%s: setting reg 0x%x to 0x%x\n",
			 __func__, clksel->reg, clksel->val);
		writel(clksel->val, cpuclk->reg_base + clksel->reg);
	}
}

static void rockchip_cpuclk_set_post_muxs(struct rockchip_cpuclk *cpuclk,
					  const struct rockchip_cpuclk_rate_table *rate)
{
	int i;

	/* alternate parent is active now. set the muxs */
	for (i = 0; i < ARRAY_SIZE(rate->post_muxs); i++) {
		const struct rockchip_cpuclk_clksel *clksel = &rate->post_muxs[i];

		if (!clksel->reg)
			break;

		pr_debug("%s: setting reg 0x%x to 0x%x\n",
			 __func__, clksel->reg, clksel->val);
		writel(clksel->val, cpuclk->reg_base + clksel->reg);
	}
}

static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
					   struct clk_notifier_data *ndata)
{
@@ -165,7 +201,16 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
			       cpuclk->reg_base + reg_data->core_reg[i]);
		}
	}

	rockchip_cpuclk_set_pre_muxs(cpuclk, rate);

	/* select alternate parent */
	if (reg_data->mux_core_reg)
		writel(HIWORD_UPDATE(reg_data->mux_core_alt,
				     reg_data->mux_core_mask,
				     reg_data->mux_core_shift),
		       cpuclk->reg_base + reg_data->mux_core_reg);
	else
		writel(HIWORD_UPDATE(reg_data->mux_core_alt,
				     reg_data->mux_core_mask,
				     reg_data->mux_core_shift),
@@ -202,11 +247,19 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
	 * primary parent by the extra dividers that were needed for the alt.
	 */

	if (reg_data->mux_core_reg)
		writel(HIWORD_UPDATE(reg_data->mux_core_main,
				     reg_data->mux_core_mask,
				     reg_data->mux_core_shift),
		       cpuclk->reg_base + reg_data->mux_core_reg);
	else
		writel(HIWORD_UPDATE(reg_data->mux_core_main,
				     reg_data->mux_core_mask,
				     reg_data->mux_core_shift),
		       cpuclk->reg_base + reg_data->core_reg[0]);

	rockchip_cpuclk_set_post_muxs(cpuclk, rate);

	/* remove dividers */
	for (i = 0; i < reg_data->num_cores; i++) {
		writel(HIWORD_UPDATE(0, reg_data->div_core_mask[i],
+218 −1
Original line number Diff line number Diff line
@@ -842,6 +842,213 @@ static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
	.init = rockchip_rk3399_pll_init,
};

/*
 * PLL used in RK3588
 */

#define RK3588_PLLCON(i)               (i * 0x4)
#define RK3588_PLLCON0_M_MASK          0x3ff
#define RK3588_PLLCON0_M_SHIFT         0
#define RK3588_PLLCON1_P_MASK          0x3f
#define RK3588_PLLCON1_P_SHIFT         0
#define RK3588_PLLCON1_S_MASK          0x7
#define RK3588_PLLCON1_S_SHIFT         6
#define RK3588_PLLCON2_K_MASK          0xffff
#define RK3588_PLLCON2_K_SHIFT         0
#define RK3588_PLLCON1_PWRDOWN         BIT(13)
#define RK3588_PLLCON6_LOCK_STATUS     BIT(15)

static int rockchip_rk3588_pll_wait_lock(struct rockchip_clk_pll *pll)
{
	u32 pllcon;
	int ret;

	/*
	 * Lock time typical 250, max 500 input clock cycles @24MHz
	 * So define a very safe maximum of 1000us, meaning 24000 cycles.
	 */
	ret = readl_relaxed_poll_timeout(pll->reg_base + RK3588_PLLCON(6),
					 pllcon,
					 pllcon & RK3588_PLLCON6_LOCK_STATUS,
					 0, 1000);
	if (ret)
		pr_err("%s: timeout waiting for pll to lock\n", __func__);

	return ret;
}

static void rockchip_rk3588_pll_get_params(struct rockchip_clk_pll *pll,
					   struct rockchip_pll_rate_table *rate)
{
	u32 pllcon;

	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(0));
	rate->m = ((pllcon >> RK3588_PLLCON0_M_SHIFT) & RK3588_PLLCON0_M_MASK);

	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1));
	rate->p = ((pllcon >> RK3588_PLLCON1_P_SHIFT) & RK3588_PLLCON1_P_MASK);
	rate->s = ((pllcon >> RK3588_PLLCON1_S_SHIFT) & RK3588_PLLCON1_S_MASK);

	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(2));
	rate->k = ((pllcon >> RK3588_PLLCON2_K_SHIFT) & RK3588_PLLCON2_K_MASK);
}

static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
	struct rockchip_pll_rate_table cur;
	u64 rate64 = prate, postdiv;

	rockchip_rk3588_pll_get_params(pll, &cur);

	rate64 *= cur.m;
	do_div(rate64, cur.p);

	if (cur.k) {
		/* fractional mode */
		u64 frac_rate64 = prate * cur.k;

		postdiv = cur.p * 65535;
		do_div(frac_rate64, postdiv);
		rate64 += frac_rate64;
	}
	rate64 = rate64 >> cur.s;

	return (unsigned long)rate64;
}

static int rockchip_rk3588_pll_set_params(struct rockchip_clk_pll *pll,
					  const struct rockchip_pll_rate_table *rate)
{
	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
	struct clk_mux *pll_mux = &pll->pll_mux;
	struct rockchip_pll_rate_table cur;
	int rate_change_remuxed = 0;
	int cur_parent;
	int ret;

	pr_debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n",
		 __func__, rate->rate, rate->p, rate->m, rate->s, rate->k);

	rockchip_rk3588_pll_get_params(pll, &cur);
	cur.rate = 0;

	if (pll->type == pll_rk3588) {
		cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
		if (cur_parent == PLL_MODE_NORM) {
			pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
			rate_change_remuxed = 1;
		}
	}

	/* set pll power down */
	writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN,
			     RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3399_PLLCON(1));

	/* update pll values */
	writel_relaxed(HIWORD_UPDATE(rate->m, RK3588_PLLCON0_M_MASK, RK3588_PLLCON0_M_SHIFT),
		       pll->reg_base + RK3399_PLLCON(0));

	writel_relaxed(HIWORD_UPDATE(rate->p, RK3588_PLLCON1_P_MASK, RK3588_PLLCON1_P_SHIFT) |
		       HIWORD_UPDATE(rate->s, RK3588_PLLCON1_S_MASK, RK3588_PLLCON1_S_SHIFT),
		       pll->reg_base + RK3399_PLLCON(1));

	writel_relaxed(HIWORD_UPDATE(rate->k, RK3588_PLLCON2_K_MASK, RK3588_PLLCON2_K_SHIFT),
		       pll->reg_base + RK3399_PLLCON(2));

	/* set pll power up */
	writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3588_PLLCON(1));

	/* wait for the pll to lock */
	ret = rockchip_rk3588_pll_wait_lock(pll);
	if (ret) {
		pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
			__func__);
		rockchip_rk3588_pll_set_params(pll, &cur);
	}

	if ((pll->type == pll_rk3588) && rate_change_remuxed)
		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);

	return ret;
}

static int rockchip_rk3588_pll_set_rate(struct clk_hw *hw, unsigned long drate,
					unsigned long prate)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
	const struct rockchip_pll_rate_table *rate;

	pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
		 __func__, __clk_get_name(hw->clk), drate, prate);

	/* Get required rate settings from table */
	rate = rockchip_get_pll_settings(pll, drate);
	if (!rate) {
		pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
			drate, __clk_get_name(hw->clk));
		return -EINVAL;
	}

	return rockchip_rk3588_pll_set_params(pll, rate);
}

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

	writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3588_PLLCON(1));
	rockchip_rk3588_pll_wait_lock(pll);

	return 0;
}

static void rockchip_rk3588_pll_disable(struct clk_hw *hw)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);

	writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN, RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3588_PLLCON(1));
}

static int rockchip_rk3588_pll_is_enabled(struct clk_hw *hw)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
	u32 pllcon = readl(pll->reg_base + RK3588_PLLCON(1));

	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,
	.disable = rockchip_rk3588_pll_disable,
	.is_enabled = rockchip_rk3588_pll_is_enabled,
};

static const struct clk_ops rockchip_rk3588_pll_clk_ops = {
	.recalc_rate = rockchip_rk3588_pll_recalc_rate,
	.round_rate = rockchip_pll_round_rate,
	.set_rate = rockchip_rk3588_pll_set_rate,
	.enable = rockchip_rk3588_pll_enable,
	.disable = rockchip_rk3588_pll_disable,
	.is_enabled = rockchip_rk3588_pll_is_enabled,
	.init = rockchip_rk3588_pll_init,
};

/*
 * Common registering of pll clocks
 */
@@ -890,7 +1097,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
	if (pll_type == pll_rk3036 ||
	    pll_type == pll_rk3066 ||
	    pll_type == pll_rk3328 ||
	    pll_type == pll_rk3399)
	    pll_type == pll_rk3399 ||
	    pll_type == pll_rk3588)
		pll_mux->flags |= CLK_MUX_HIWORD_MASK;

	/* the actual muxing is xin24m, pll-output, xin32k */
@@ -957,6 +1165,14 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
		else
			init.ops = &rockchip_rk3399_pll_clk_ops;
		break;
	case pll_rk3588:
	case pll_rk3588_core:
		if (!pll->rate_table)
			init.ops = &rockchip_rk3588_pll_clk_norate_ops;
		else
			init.ops = &rockchip_rk3588_pll_clk_ops;
		init.flags = flags;
		break;
	default:
		pr_warn("%s: Unknown pll type for pll clk %s\n",
			__func__, name);
@@ -981,6 +1197,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
	return mux_clk;

err_pll:
	kfree(pll->rate_table);
	clk_unregister(mux_clk);
	mux_clk = pll_clk;
err_mux:
Loading