Unverified Commit 53fbbc29 authored by Stephen Boyd's avatar Stephen Boyd
Browse files

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

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

 - SCMI clock-ids and max-clk-number removal from dt-binding on RK3568
 - Clock drivers for the new Rockchip SoCs RV1126B and RK3506

* tag 'v6.19-rockchip-clk1' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip:
  clk: rockchip: Add clock and reset driver for RK3506
  dt-bindings: clock: rockchip: Add RK3506 clock and reset unit
  clk: rockchip: Add clock controller for the RV1126B
  dt-bindings: clock, reset: Add support for rv1126b
  clk: rockchip: Implement rockchip_clk_register_armclk_multi_pll()
  dt-bindings: clock: rk3568: Drop CLK_NR_CLKS define
  clk: rockchip: rk3568: Drop CLK_NR_CLKS usage
  dt-bindings: clock: rk3568: Add SCMI clock ids
parents 3a866087 18191dd7
Loading
Loading
Loading
Loading
+55 −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,rk3506-cru.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Rockchip RK3506 Clock and Reset Unit (CRU)

maintainers:
  - Finley Xiao <finley.xiao@rock-chips.com>
  - Heiko Stuebner <heiko@sntech.de>

description:
  The RK3506 CRU generates the clock and also implements reset for SoC
  peripherals.

properties:
  compatible:
    const: rockchip,rk3506-cru

  reg:
    maxItems: 1

  "#clock-cells":
    const: 1

  "#reset-cells":
    const: 1

  clocks:
    maxItems: 1

  clock-names:
    const: xin

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

additionalProperties: false

examples:
  - |
    clock-controller@ff9a0000 {
      compatible = "rockchip,rk3506-cru";
      reg = <0xff9a0000 0x20000>;
      #clock-cells = <1>;
      #reset-cells = <1>;
      clocks = <&xin24m>;
      clock-names = "xin";
    };
+52 −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,rv1126b-cru.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Rockchip RV1126B Clock and Reset Unit

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

description:
  The rv1126b clock controller generates the clock and also implements a
  reset controller for SoC peripherals.

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

  reg:
    maxItems: 1

  "#clock-cells":
    const: 1

  "#reset-cells":
    const: 1

  clocks:
    maxItems: 1

  clock-names:
    const: xin24m

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

additionalProperties: false

examples:
  - |
    clock-controller@20000000 {
      compatible = "rockchip,rv1126b-cru";
      reg = <0x20000000 0xc0000>;
      #clock-cells = <1>;
      #reset-cells = <1>;
    };
+14 −0
Original line number Diff line number Diff line
@@ -30,6 +30,13 @@ config CLK_RV1126
	help
	  Build the driver for RV1126 Clock Driver.

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

config CLK_RK3036
	bool "Rockchip RK3036 clock controller support"
	depends on ARM || COMPILE_TEST
@@ -93,6 +100,13 @@ config CLK_RK3399
	help
	  Build the driver for RK3399 Clock Driver.

config CLK_RK3506
	bool "Rockchip RK3506 clock controller support"
	depends on ARM || COMPILE_TEST
	default y
	help
	  Build the driver for RK3506 Clock Driver.

config CLK_RK3528
	bool "Rockchip RK3528 clock controller support"
	depends on ARM64 || COMPILE_TEST
+2 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o
obj-$(CONFIG_CLK_PX30)          += clk-px30.o
obj-$(CONFIG_CLK_RV110X)        += clk-rv1108.o
obj-$(CONFIG_CLK_RV1126)        += clk-rv1126.o
obj-$(CONFIG_CLK_RV1126B)	+= clk-rv1126b.o rst-rv1126b.o
obj-$(CONFIG_CLK_RK3036)        += clk-rk3036.o
obj-$(CONFIG_CLK_RK312X)        += clk-rk3128.o
obj-$(CONFIG_CLK_RK3188)        += clk-rk3188.o
@@ -29,6 +30,7 @@ obj-$(CONFIG_CLK_RK3308) += clk-rk3308.o
obj-$(CONFIG_CLK_RK3328)        += clk-rk3328.o
obj-$(CONFIG_CLK_RK3368)        += clk-rk3368.o
obj-$(CONFIG_CLK_RK3399)        += clk-rk3399.o
obj-$(CONFIG_CLK_RK3506)	+= clk-rk3506.o rst-rk3506.o
obj-$(CONFIG_CLK_RK3528)	+= clk-rk3528.o rst-rk3528.o
obj-$(CONFIG_CLK_RK3562)	+= clk-rk3562.o rst-rk3562.o
obj-$(CONFIG_CLK_RK3568)	+= clk-rk3568.o
+165 −0
Original line number Diff line number Diff line
@@ -396,3 +396,168 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
	kfree(cpuclk);
	return ERR_PTR(ret);
}

static int rockchip_cpuclk_multi_pll_pre_rate_change(struct rockchip_cpuclk *cpuclk,
						     struct clk_notifier_data *ndata)
{
	unsigned long new_rate = roundup(ndata->new_rate, 1000);
	const struct rockchip_cpuclk_rate_table *rate;
	unsigned long flags;

	rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
	if (!rate) {
		pr_err("%s: Invalid rate : %lu for cpuclk\n",
		       __func__, new_rate);
		return -EINVAL;
	}

	if (new_rate > ndata->old_rate) {
		spin_lock_irqsave(cpuclk->lock, flags);
		rockchip_cpuclk_set_dividers(cpuclk, rate);
		spin_unlock_irqrestore(cpuclk->lock, flags);
	}

	return 0;
}

static int rockchip_cpuclk_multi_pll_post_rate_change(struct rockchip_cpuclk *cpuclk,
						      struct clk_notifier_data *ndata)
{
	unsigned long new_rate = roundup(ndata->new_rate, 1000);
	const struct rockchip_cpuclk_rate_table *rate;
	unsigned long flags;

	rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
	if (!rate) {
		pr_err("%s: Invalid rate : %lu for cpuclk\n",
		       __func__, new_rate);
		return -EINVAL;
	}

	if (new_rate < ndata->old_rate) {
		spin_lock_irqsave(cpuclk->lock, flags);
		rockchip_cpuclk_set_dividers(cpuclk, rate);
		spin_unlock_irqrestore(cpuclk->lock, flags);
	}

	return 0;
}

static int rockchip_cpuclk_multi_pll_notifier_cb(struct notifier_block *nb,
						 unsigned long event, void *data)
{
	struct clk_notifier_data *ndata = data;
	struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
	int ret = 0;

	pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
		 __func__, event, ndata->old_rate, ndata->new_rate);
	if (event == PRE_RATE_CHANGE)
		ret = rockchip_cpuclk_multi_pll_pre_rate_change(cpuclk, ndata);
	else if (event == POST_RATE_CHANGE)
		ret = rockchip_cpuclk_multi_pll_post_rate_change(cpuclk, ndata);

	return notifier_from_errno(ret);
}

struct clk *rockchip_clk_register_cpuclk_multi_pll(const char *name,
						   const char *const *parent_names,
						   u8 num_parents, void __iomem *base,
						   int muxdiv_offset, u8 mux_shift,
						   u8 mux_width, u8 mux_flags,
						   int div_offset, u8 div_shift,
						   u8 div_width, u8 div_flags,
						   unsigned long flags, spinlock_t *lock,
						   const struct rockchip_cpuclk_rate_table *rates,
						   int nrates)
{
	struct rockchip_cpuclk *cpuclk;
	struct clk_hw *hw;
	struct clk_mux *mux = NULL;
	struct clk_divider *div = NULL;
	const struct clk_ops *mux_ops = NULL, *div_ops = NULL;
	int ret;

	if (num_parents > 1) {
		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
		if (!mux)
			return ERR_PTR(-ENOMEM);

		mux->reg = base + muxdiv_offset;
		mux->shift = mux_shift;
		mux->mask = BIT(mux_width) - 1;
		mux->flags = mux_flags;
		mux->lock = lock;
		mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
							: &clk_mux_ops;
	}

	if (div_width > 0) {
		div = kzalloc(sizeof(*div), GFP_KERNEL);
		if (!div) {
			ret = -ENOMEM;
			goto free_mux;
		}

		div->flags = div_flags;
		if (div_offset)
			div->reg = base + div_offset;
		else
			div->reg = base + muxdiv_offset;
		div->shift = div_shift;
		div->width = div_width;
		div->lock = lock;
		div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
						? &clk_divider_ro_ops
						: &clk_divider_ops;
	}

	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
				       mux ? &mux->hw : NULL, mux_ops,
				       div ? &div->hw : NULL, div_ops,
				       NULL, NULL, flags);
	if (IS_ERR(hw)) {
		ret = PTR_ERR(hw);
		goto free_div;
	}

	cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
	if (!cpuclk) {
		ret = -ENOMEM;
		goto unregister_clk;
	}

	cpuclk->reg_base = base;
	cpuclk->lock = lock;
	cpuclk->clk_nb.notifier_call = rockchip_cpuclk_multi_pll_notifier_cb;
	ret = clk_notifier_register(hw->clk, &cpuclk->clk_nb);
	if (ret) {
		pr_err("%s: failed to register clock notifier for %s\n",
		       __func__, name);
		goto free_cpuclk;
	}

	if (nrates > 0) {
		cpuclk->rate_count = nrates;
		cpuclk->rate_table = kmemdup(rates,
					     sizeof(*rates) * nrates,
					     GFP_KERNEL);
		if (!cpuclk->rate_table) {
			ret = -ENOMEM;
			goto free_cpuclk;
		}
	}

	return hw->clk;

free_cpuclk:
	kfree(cpuclk);
unregister_clk:
	clk_hw_unregister_composite(hw);
free_div:
	kfree(div);
free_mux:
	kfree(mux);

	return ERR_PTR(ret);
}
Loading