This series adds support for the AP sub-system clock controller in the
T-Head TH1520 [1]. Yangtao Li originally submitted this series in May
2023 [2]. Jisheng made additional improvements and then passed on the
work in progress to me.
Changes I made from the original series:
- corrected the npu_clk enable bit
- deduplicated CLK_NPU and CLK_NPU_AXI number in header
- fixed c910_i0_clk reg typo
- fixed checkpatch and dt_binding_check warnings
- rebased on v6.9-rc5
- revised commit descriptions
Changes since my RFC v1 [4]:
- squash the header file patch into the DT schema patch
- describe the changes I made to original series in the cover letter
instead of the individual patches
- fix my typo in my email address
TODO:
I am again marking this as an RFC because there is feedback from v1 that
I have not yet addressed. I am posting what I currently have as other
patch series like the TH1520 I2C driver [4] could use the clk driver.
Emil commented that the input predivider is not handled correctly in
ccu_mdiv_recalc_rate(). The PLL multiplies the input frequency and
outputs "Foutvco". This is followed by a post divider to produce
"Foutpostdiv". However, some clocks derive directly from the "Foutvco"
Emil suggested this should really be modeled as two different clocks.
Emil aslo suggested that the rest of the clocks in this driver seem to
be generic gate and mux implementations that should probably be replaced
with devm_clk_hw_register_gate*() and devm_clk_hw_register_mux*().
I'll look to address the above issues in the next revision.
Thank you,
Drew
[1] https://openbeagle.org/beaglev-ahead/beaglev-ahead/-/blob/main/docs/TH1520%20System%20User%20Manual.pdf
[2] https://lore.kernel.org/linux-riscv/[email protected]/
[3] https://lore.kernel.org/lkml/[email protected]/
[4] https://lore.kernel.org/linux-riscv/[email protected]/
---
Drew Fustini (4):
dt-bindings: clock: Document T-Head TH1520 AP_SUBSYS controller
clk: thead: Add support for T-Head TH1520 AP_SUBSYS clocks
riscv: dts: thead: Add TH1520 AP_SUBSYS clock controller
riscv: dts: thead: Add clock to TH1520 mmc controllers
.../bindings/clock/thead,th1520-clk-ap.yaml | 65 ++
MAINTAINERS | 3 +
arch/riscv/boot/dts/thead/th1520.dtsi | 15 +-
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/thead/Kconfig | 12 +
drivers/clk/thead/Makefile | 2 +
drivers/clk/thead/clk-th1520-ap.c | 1018 ++++++++++++++++++++
include/dt-bindings/clock/thead,th1520-clk-ap.h | 96 ++
9 files changed, 1210 insertions(+), 3 deletions(-)
---
base-commit: 14396a29c3cfbd42b4ea5cd0a528264831524062
change-id: 20240426-th1520-clk-v2-134bfc9bddb1
Best regards,
--
Drew Fustini <[email protected]>
Document bindings for the T-Head TH1520 AP sub-system clock controller.
Link: https://openbeagle.org/beaglev-ahead/beaglev-ahead/-/blob/main/docs/TH1520%20System%20User%20Manual.pdf
Co-developed-by: Yangtao Li <[email protected]>
Signed-off-by: Yangtao Li <[email protected]>
Signed-off-by: Drew Fustini <[email protected]>
---
.../bindings/clock/thead,th1520-clk-ap.yaml | 65 +++++++++++++++
MAINTAINERS | 2 +
include/dt-bindings/clock/thead,th1520-clk-ap.h | 96 ++++++++++++++++++++++
3 files changed, 163 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml b/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml
new file mode 100644
index 000000000000..611edc90f7b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/thead,th1520-clk-ap.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: T-HEAD TH1520 AP sub-system clock controller
+
+description: |
+ The T-HEAD TH1520 AP sub-system clock controller configures the
+ CPU, DPU, GMAC and TEE PLLs.
+
+ SoC reference manual
+ https://openbeagle.org/beaglev-ahead/beaglev-ahead/-/blob/main/docs/TH1520%20System%20User%20Manual.pdf
+
+maintainers:
+ - Jisheng Zhang <[email protected]>
+ - Wei Fu <[email protected]>
+ - Drew Fustini <[email protected]>
+
+properties:
+ compatible:
+ const: thead,th1520-clk-ap
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: main oscillator (24MHz)
+
+ clock-names:
+ items:
+ - const: osc
+
+ "#clock-cells":
+ const: 1
+ description:
+ See <dt-bindings/clock/thead,th1520-clk-ap.h> for valid indices.
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - "#clock-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/thead,th1520-clk-ap.h>
+ soc {
+
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ clk: clock-controller@ffef010000 {
+ compatible = "thead,th1520-clk-ap";
+ reg = <0xff 0xef010000 0x0 0x1000>;
+ clocks = <&osc>;
+ clock-names = "osc";
+ #clock-cells = <1>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index ebf03f5f0619..51d550cb1329 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19023,7 +19023,9 @@ M: Guo Ren <[email protected]>
M: Fu Wei <[email protected]>
L: [email protected]
S: Maintained
+F: Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml
F: arch/riscv/boot/dts/thead/
+F: include/dt-bindings/clock/thead,th1520-clk-ap.h
RNBD BLOCK DRIVERS
M: Md. Haris Iqbal <[email protected]>
diff --git a/include/dt-bindings/clock/thead,th1520-clk-ap.h b/include/dt-bindings/clock/thead,th1520-clk-ap.h
new file mode 100644
index 000000000000..d0d1ab1e672a
--- /dev/null
+++ b/include/dt-bindings/clock/thead,th1520-clk-ap.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) 2023 Vivo Communication Technology Co. Ltd.
+ * Authors: Yangtao Li <[email protected]>
+ */
+
+#ifndef _DT_BINDINGS_CLK_TH1520_H_
+#define _DT_BINDINGS_CLK_TH1520_H_
+
+#define CLK_CPU_PLL0 0
+#define CLK_CPU_PLL1 1
+#define CLK_GMAC_PLL 2
+#define CLK_VIDEO_PLL 3
+#define CLK_DPU0_PLL 4
+#define CLK_DPU1_PLL 5
+#define CLK_TEE_PLL 6
+#define CLK_C910_I0 7
+#define CLK_C910 8
+#define CLK_BROM 9
+#define CLK_BMU 10
+#define CLK_AHB2_CPUSYS_HCLK 11
+#define CLK_APB3_CPUSYS_PCLK 12
+#define CLK_AXI4_CPUSYS2_ACLK 13
+#define CLK_AON2CPU_A2X 14
+#define CLK_X2X_CPUSYS 15
+#define CLK_AXI_ACLK 16
+#define CLK_CPU2AON_X2H 17
+#define CLK_PERI_AHB_HCLK 18
+#define CLK_CPU2PERI_X2H 19
+#define CLK_PERI_APB_PCLK 20
+#define CLK_PERI2APB_PCLK 21
+#define CLK_PERI_APB1_HCLK 22
+#define CLK_PERI_APB2_HCLK 23
+#define CLK_PERI_APB3_HCLK 24
+#define CLK_PERI_APB4_HCLK 25
+#define CLK_OSC12M 26
+#define CLK_OUT1 27
+#define CLK_OUT2 28
+#define CLK_OUT3 29
+#define CLK_OUT4 30
+#define CLK_APB_PCLK 31
+#define CLK_NPU 32
+#define CLK_NPU_AXI 33
+#define CLK_VI 34
+#define CLK_VI_AHB 35
+#define CLK_VO_AXI 36
+#define CLK_VP_APB 37
+#define CLK_VP_AXI 38
+#define CLK_CPU2VP 39
+#define CLK_VENC 40
+#define CLK_DPU0 41
+#define CLK_DPU1 42
+#define CLK_EMMC_SDIO 43
+#define CLK_GMAC1 44
+#define CLK_PADCTRL1 45
+#define CLK_DSMART 46
+#define CLK_PADCTRL0 47
+#define CLK_GMAC_AXI 48
+#define CLK_GPIO3 49
+#define CLK_GMAC0 50
+#define CLK_PWM 51
+#define CLK_QSPI0 52
+#define CLK_QSPI1 53
+#define CLK_SPI 54
+#define CLK_UART0_PCLK 55
+#define CLK_UART1_PCLK 56
+#define CLK_UART2_PCLK 57
+#define CLK_UART3_PCLK 58
+#define CLK_UART4_PCLK 59
+#define CLK_UART5_PCLK 60
+#define CLK_GPIO0 61
+#define CLK_GPIO1 62
+#define CLK_GPIO2 63
+#define CLK_I2C0 64
+#define CLK_I2C1 65
+#define CLK_I2C2 66
+#define CLK_I2C3 67
+#define CLK_I2C4 68
+#define CLK_I2C5 69
+#define CLK_SPINLOCK 70
+#define CLK_DMA 71
+#define CLK_MBOX0 72
+#define CLK_MBOX1 73
+#define CLK_MBOX2 74
+#define CLK_MBOX3 75
+#define CLK_WDT0 76
+#define CLK_WDT1 77
+#define CLK_TIMER0 78
+#define CLK_TIMER1 79
+#define CLK_SRAM0 80
+#define CLK_SRAM1 81
+#define CLK_SRAM2 82
+#define CLK_SRAM3 83
+#define CLK_PLL_GMAC_100M 84
+#define CLK_UART_SCLK 85
+#endif
--
2.34.1
Add node for the AP_SUBSYS clock controller on the T-Head TH1520 SoC.
Link: https://openbeagle.org/beaglev-ahead/beaglev-ahead/-/blob/main/docs/TH1520%20System%20User%20Manual.pdf
Signed-off-by: Drew Fustini <[email protected]>
---
arch/riscv/boot/dts/thead/th1520.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi
index 8b915e206f3a..6285cdf91bd6 100644
--- a/arch/riscv/boot/dts/thead/th1520.dtsi
+++ b/arch/riscv/boot/dts/thead/th1520.dtsi
@@ -5,6 +5,7 @@
*/
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/clock/thead,th1520-clk-ap.h>
/ {
compatible = "thead,th1520";
@@ -161,6 +162,14 @@ soc {
dma-noncoherent;
ranges;
+ clk: clock-controller@ffef010000 {
+ compatible = "thead,th1520-clk-ap";
+ reg = <0xff 0xef010000 0x0 0x1000>;
+ clocks = <&osc>;
+ clock-names = "osc";
+ #clock-cells = <1>;
+ };
+
plic: interrupt-controller@ffd8000000 {
compatible = "thead,th1520-plic", "thead,c900-plic";
reg = <0xff 0xd8000000 0x0 0x01000000>;
--
2.34.1
Add support for the AP sub-system clock controller in the T-Head TH1520.
This include CPU, DPU, GMAC and TEE PLLs.
Link: https://openbeagle.org/beaglev-ahead/beaglev-ahead/-/blob/main/docs/TH1520%20System%20User%20Manual.pdf
Co-developed-by: Yangtao Li <[email protected]>
Signed-off-by: Yangtao Li <[email protected]>
Co-developed-by: Jisheng Zhang <[email protected]>
Signed-off-by: Jisheng Zhang <[email protected]>
Signed-off-by: Drew Fustini <[email protected]>
---
MAINTAINERS | 1 +
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/thead/Kconfig | 12 +
drivers/clk/thead/Makefile | 2 +
drivers/clk/thead/clk-th1520-ap.c | 1018 +++++++++++++++++++++++++++++++++++++
6 files changed, 1035 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 51d550cb1329..acd55e12d768 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19025,6 +19025,7 @@ L: [email protected]
S: Maintained
F: Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml
F: arch/riscv/boot/dts/thead/
+F: drivers/clk/thead/clk-th1520-ap.c
F: include/dt-bindings/clock/thead,th1520-clk-ap.h
RNBD BLOCK DRIVERS
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 50af5fc7f570..c9057e41df34 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -494,6 +494,7 @@ source "drivers/clk/starfive/Kconfig"
source "drivers/clk/sunxi/Kconfig"
source "drivers/clk/sunxi-ng/Kconfig"
source "drivers/clk/tegra/Kconfig"
+source "drivers/clk/thead/Kconfig"
source "drivers/clk/stm32/Kconfig"
source "drivers/clk/ti/Kconfig"
source "drivers/clk/uniphier/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 14fa8d4ecc1f..2eafc268f498 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -126,6 +126,7 @@ obj-y += starfive/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-y += sunxi-ng/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
+obj-$(CONFIG_ARCH_THEAD) += thead/
obj-y += ti/
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_U8500) += ux500/
diff --git a/drivers/clk/thead/Kconfig b/drivers/clk/thead/Kconfig
new file mode 100644
index 000000000000..1710d50bf9d4
--- /dev/null
+++ b/drivers/clk/thead/Kconfig
@@ -0,0 +1,12 @@
+#SPDX-License-Identifier: GPL-2.0
+
+config CLK_THEAD_TH1520_AP
+ bool "T-HEAD TH1520 AP clock support"
+ depends on ARCH_THEAD || COMPILE_TEST
+ default ARCH_THEAD
+ select REGMAP_MMIO
+ help
+ Say yes here to support the AP sub system clock controller
+ on the T-HEAD TH1520 SoC. This includes configuration of
+ both CPU PLLs, both DPU PLLs as well as the GMAC, VIDEO,
+ and TEE PLLs.
diff --git a/drivers/clk/thead/Makefile b/drivers/clk/thead/Makefile
new file mode 100644
index 000000000000..7ee0bec1f251
--- /dev/null
+++ b/drivers/clk/thead/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CLK_THEAD_TH1520_AP) += clk-th1520-ap.o
diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th1520-ap.c
new file mode 100644
index 000000000000..21040e7d280c
--- /dev/null
+++ b/drivers/clk/thead/clk-th1520-ap.c
@@ -0,0 +1,1018 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Jisheng Zhang <[email protected]>
+ * Copyright (C) 2023 Vivo Communication Technology Co. Ltd.
+ * Authors: Yangtao Li <[email protected]>
+ */
+
+#include <dt-bindings/clock/thead,th1520-clk-ap.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct ccu_internal {
+ u8 shift;
+ u8 width;
+};
+
+struct ccu_div_internal {
+ u8 shift;
+ u8 width;
+ u32 flags;
+};
+
+struct ccu_common {
+ struct regmap *map;
+ u16 reg;
+ struct clk_hw hw;
+};
+
+struct ccu_mux {
+ struct ccu_internal mux;
+ struct ccu_common common;
+};
+
+struct ccu_gate {
+ u32 enable;
+ struct ccu_common common;
+};
+
+struct ccu_div {
+ u32 enable;
+ struct ccu_div_internal div;
+ struct ccu_internal mux;
+ struct ccu_common common;
+};
+
+/*
+ * struct ccu_mdiv - Definition of an M-D-I-V clock
+ *
+ * Clocks based on the formula (parent * M) / (D * I * V)
+ */
+struct ccu_mdiv {
+ struct ccu_internal m;
+ struct ccu_internal d;
+ struct ccu_internal i;
+ struct ccu_internal v;
+ struct ccu_common common;
+};
+
+#define TH_CCU_ARG(_shift, _width) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ }
+
+#define TH_CCU_DIV_FLAGS(_shift, _width, _flags) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ .flags = _flags, \
+ }
+
+#define CCU_GATE(_struct, _name, _parent, _reg, _gate, _flags) \
+ struct ccu_gate _struct = { \
+ .enable = _gate, \
+ .common = { \
+ .reg = _reg, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &ccu_gate_ops, \
+ _flags), \
+ } \
+ }
+
+static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw)
+{
+ return container_of(hw, struct ccu_common, hw);
+}
+
+static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw)
+{
+ struct ccu_common *common = hw_to_ccu_common(hw);
+
+ return container_of(common, struct ccu_mux, common);
+}
+
+static inline struct ccu_mdiv *hw_to_ccu_mdiv(struct clk_hw *hw)
+{
+ struct ccu_common *common = hw_to_ccu_common(hw);
+
+ return container_of(common, struct ccu_mdiv, common);
+}
+
+static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw)
+{
+ struct ccu_common *common = hw_to_ccu_common(hw);
+
+ return container_of(common, struct ccu_div, common);
+}
+
+static inline struct ccu_gate *hw_to_ccu_gate(struct clk_hw *hw)
+{
+ struct ccu_common *common = hw_to_ccu_common(hw);
+
+ return container_of(common, struct ccu_gate, common);
+}
+
+static u8 ccu_get_parent_helper(struct ccu_common *common,
+ struct ccu_internal *mux)
+{
+ unsigned int val;
+ u8 parent;
+
+ regmap_read(common->map, common->reg, &val);
+ parent = val >> mux->shift;
+ parent &= GENMASK(mux->width - 1, 0);
+
+ return parent;
+}
+
+static int ccu_set_parent_helper(struct ccu_common *common,
+ struct ccu_internal *mux,
+ u8 index)
+{
+ return regmap_update_bits(common->map, common->reg,
+ GENMASK(mux->width - 1, 0) << mux->shift,
+ index << mux->shift);
+}
+
+static u8 ccu_mux_get_parent(struct clk_hw *hw)
+{
+ struct ccu_mux *cm = hw_to_ccu_mux(hw);
+
+ return ccu_get_parent_helper(&cm->common, &cm->mux);
+}
+
+static int ccu_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ccu_mux *cm = hw_to_ccu_mux(hw);
+
+ return ccu_set_parent_helper(&cm->common, &cm->mux, index);
+}
+
+static const struct clk_ops ccu_mux_ops = {
+ .get_parent = ccu_mux_get_parent,
+ .set_parent = ccu_mux_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+
+void ccu_disable_helper(struct ccu_common *common, u32 gate)
+{
+ if (!gate)
+ return;
+
+ regmap_update_bits(common->map, common->reg,
+ gate, ~gate);
+}
+
+int ccu_enable_helper(struct ccu_common *common, u32 gate)
+{
+ if (!gate)
+ return 0;
+
+ return regmap_update_bits(common->map, common->reg,
+ gate, gate);
+}
+
+static int ccu_is_enabled_helper(struct ccu_common *common, u32 gate)
+{
+ unsigned int val;
+
+ if (!gate)
+ return true;
+
+ regmap_read(common->map, common->reg, &val);
+ return val & gate;
+}
+
+static int ccu_gate_is_enabled(struct clk_hw *hw)
+{
+ struct ccu_gate *cg = hw_to_ccu_gate(hw);
+
+ return ccu_is_enabled_helper(&cg->common, cg->enable);
+}
+
+static void ccu_gate_disable(struct clk_hw *hw)
+{
+ struct ccu_gate *cg = hw_to_ccu_gate(hw);
+
+ ccu_disable_helper(&cg->common, cg->enable);
+}
+
+static int ccu_gate_enable(struct clk_hw *hw)
+{
+ struct ccu_gate *cg = hw_to_ccu_gate(hw);
+
+ return ccu_enable_helper(&cg->common, cg->enable);
+}
+
+static const struct clk_ops ccu_gate_ops = {
+ .disable = ccu_gate_disable,
+ .enable = ccu_gate_enable,
+ .is_enabled = ccu_gate_is_enabled,
+};
+
+static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+ unsigned int val;
+
+ regmap_read(cd->common.map, cd->common.reg, &val);
+ val = val >> cd->div.shift;
+ val &= GENMASK(cd->div.width - 1, 0);
+
+ val = divider_recalc_rate(hw, parent_rate, val, NULL,
+ cd->div.flags, cd->div.width);
+
+ return val;
+}
+
+static u8 ccu_div_get_parent(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_get_parent_helper(&cd->common, &cd->mux);
+}
+
+static int ccu_div_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_set_parent_helper(&cd->common, &cd->mux, index);
+}
+
+static void ccu_div_disable(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ ccu_disable_helper(&cd->common, cd->enable);
+}
+
+static int ccu_div_enable(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_enable_helper(&cd->common, cd->enable);
+}
+
+static int ccu_div_is_enabled(struct clk_hw *hw)
+{
+ struct ccu_div *cd = hw_to_ccu_div(hw);
+
+ return ccu_is_enabled_helper(&cd->common, cd->enable);
+}
+
+static const struct clk_ops ccu_div_ops = {
+ .disable = ccu_div_disable,
+ .enable = ccu_div_enable,
+ .is_enabled = ccu_div_is_enabled,
+ .get_parent = ccu_div_get_parent,
+ .set_parent = ccu_div_set_parent,
+ .recalc_rate = ccu_div_recalc_rate,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
+};
+
+static unsigned long ccu_mdiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ccu_mdiv *mdiv = hw_to_ccu_mdiv(hw);
+ unsigned long div, rate = parent_rate;
+ unsigned int m, d, i, v, val;
+
+ regmap_read(mdiv->common.map, mdiv->common.reg, &val);
+
+ m = val >> mdiv->m.shift;
+ m &= GENMASK(mdiv->m.width - 1, 0);
+
+ d = val >> mdiv->d.shift;
+ d &= GENMASK(mdiv->d.width - 1, 0);
+
+ i = val >> mdiv->i.shift;
+ i &= GENMASK(mdiv->i.width - 1, 0);
+
+ v = val >> mdiv->v.shift;
+ v &= GENMASK(mdiv->v.width - 1, 0);
+
+ rate = parent_rate * m;
+ div = d * i * v;
+ do_div(rate, div);
+
+ return rate;
+}
+
+static const struct clk_ops clk_mdiv_ops = {
+ .recalc_rate = ccu_mdiv_recalc_rate,
+};
+
+static struct ccu_mdiv cpu_pll0_clk = {
+ .m = TH_CCU_ARG(8, 12),
+ .d = TH_CCU_ARG(24, 3),
+ .i = TH_CCU_ARG(20, 3),
+ .v = TH_CCU_ARG(0, 6),
+ .common = {
+ .reg = 0x000,
+ .hw.init = CLK_HW_INIT("cpu-pll0", "osc_24m",
+ &clk_mdiv_ops,
+ 0),
+ },
+};
+
+static struct ccu_mdiv cpu_pll1_clk = {
+ .m = TH_CCU_ARG(8, 12),
+ .d = TH_CCU_ARG(24, 3),
+ .i = TH_CCU_ARG(20, 3),
+ .v = TH_CCU_ARG(0, 6),
+ .common = {
+ .reg = 0x010,
+ .hw.init = CLK_HW_INIT("cpu-pll1", "osc_24m",
+ &clk_mdiv_ops,
+ 0),
+ },
+};
+
+static struct ccu_mdiv gmac_pll_clk = {
+ .m = TH_CCU_ARG(8, 12),
+ .d = TH_CCU_ARG(24, 3),
+ .i = TH_CCU_ARG(20, 3),
+ .v = TH_CCU_ARG(0, 6),
+ .common = {
+ .reg = 0x020,
+ .hw.init = CLK_HW_INIT("gmac-pll", "osc_24m",
+ &clk_mdiv_ops,
+ 0),
+ },
+};
+
+static struct ccu_mdiv video_pll_clk = {
+ .m = TH_CCU_ARG(8, 12),
+ .d = TH_CCU_ARG(24, 3),
+ .i = TH_CCU_ARG(20, 3),
+ .v = TH_CCU_ARG(0, 6),
+ .common = {
+ .reg = 0x030,
+ .hw.init = CLK_HW_INIT("video-pll", "osc_24m",
+ &clk_mdiv_ops,
+ 0),
+ },
+};
+
+static struct ccu_mdiv dpu0_pll_clk = {
+ .m = TH_CCU_ARG(8, 12),
+ .d = TH_CCU_ARG(24, 3),
+ .i = TH_CCU_ARG(20, 3),
+ .v = TH_CCU_ARG(0, 6),
+ .common = {
+ .reg = 0x040,
+ .hw.init = CLK_HW_INIT("dpu0-pll", "osc_24m",
+ &clk_mdiv_ops,
+ 0),
+ },
+};
+
+static struct ccu_mdiv dpu1_pll_clk = {
+ .m = TH_CCU_ARG(8, 12),
+ .d = TH_CCU_ARG(24, 3),
+ .i = TH_CCU_ARG(20, 3),
+ .v = TH_CCU_ARG(0, 6),
+ .common = {
+ .reg = 0x050,
+ .hw.init = CLK_HW_INIT("dpu1-pll", "osc_24m",
+ &clk_mdiv_ops,
+ 0),
+ },
+};
+
+static struct ccu_mdiv tee_pll_clk = {
+ .m = TH_CCU_ARG(8, 12),
+ .d = TH_CCU_ARG(24, 3),
+ .i = TH_CCU_ARG(20, 3),
+ .v = TH_CCU_ARG(0, 6),
+ .common = {
+ .reg = 0x060,
+ .hw.init = CLK_HW_INIT("tee-pll", "osc_24m",
+ &clk_mdiv_ops,
+ 0),
+ },
+};
+
+static const char * const c910_i0_parents[] = { "cpu-pll0", "osc_24m" };
+struct ccu_mux c910_i0_clk = {
+ .mux = TH_CCU_ARG(1, 1),
+ .common = {
+ .reg = 0x100,
+ .hw.init = CLK_HW_INIT_PARENTS("c910-i0",
+ c910_i0_parents,
+ &ccu_mux_ops,
+ 0),
+ }
+};
+
+static const char * const c910_parents[] = { "c910-i0", "cpu-pll1" };
+struct ccu_mux c910_clk = {
+ .mux = TH_CCU_ARG(0, 1),
+ .common = {
+ .reg = 0x100,
+ .hw.init = CLK_HW_INIT_PARENTS("c910",
+ c910_parents,
+ &ccu_mux_ops,
+ 0),
+ }
+};
+
+static CCU_GATE(brom_clk, "brom", "ahb2-cpusys-hclk",
+ 0x100, BIT(4), 0);
+
+static CCU_GATE(bmu_clk, "bmu", "axi4-cpusys2-aclk",
+ 0x100, BIT(5), 0);
+
+static const char * const ahb2_cpusys_parents[] = { "gmac-pll", "osc_24m" };
+static struct ccu_div ahb2_cpusys_hclk = {
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .reg = 0x120,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb2-cpusys-hclk",
+ ahb2_cpusys_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div apb3_cpusys_pclk = {
+ .div = TH_CCU_ARG(0, 3),
+ .common = {
+ .reg = 0x130,
+ .hw.init = CLK_HW_INIT("apb3-cpusys-pclk", "ahb2-cpusys-hclk",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div axi4_cpusys2_aclk = {
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x134,
+ .hw.init = CLK_HW_INIT("axi4-cpusys2-aclk", "gmac-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CCU_GATE(aon2cpu_a2x_clk, "aon2cpu-a2x", "axi4-cpusys2-aclk",
+ 0x134, BIT(8), 0);
+
+static CCU_GATE(x2x_cpusys_clk, "x2x-cpusys", "axi4-cpusys2-aclk",
+ 0x134, BIT(7), 0);
+
+static const char * const axi_parents[] = { "video-pll", "osc_24m" };
+static struct ccu_div axi_aclk = {
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .reg = 0x138,
+ .hw.init = CLK_HW_INIT_PARENTS("axi-aclk",
+ axi_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CCU_GATE(cpu2aon_x2h_clk, "cpu2aon-x2h", "axi-aclk",
+ 0x138, BIT(8), 0);
+
+static const char * const perisys_ahb_hclk_parents[] = { "gmac-pll", "osc_24m" };
+static struct ccu_div perisys_ahb_hclk = {
+ .enable = BIT(6),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(5, 1),
+ .common = {
+ .reg = 0x140,
+ .hw.init = CLK_HW_INIT_PARENTS("perisys-ahb-hclk",
+ perisys_ahb_hclk_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CCU_GATE(cpu2peri_x2h_clk, "cpu2peri-x2h", "axi4-cpusys2-aclk",
+ 0x140, BIT(9), 0);
+
+static struct ccu_div perisys_apb_pclk = {
+ .div = TH_CCU_ARG(0, 3),
+ .common = {
+ .reg = 0x150,
+ .hw.init = CLK_HW_INIT("perisys-apb-pclk", "perisys-ahb-hclk",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div peri2sys_apb_pclk = {
+ .div = TH_CCU_DIV_FLAGS(4, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x150,
+ .hw.init = CLK_HW_INIT("peri2sys-apb-pclk",
+ "gmac-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CCU_GATE(perisys_apb1_hclk, "perisys-apb1-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(9), 0);
+
+static CCU_GATE(perisys_apb2_hclk, "perisys-apb2-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(10), 0);
+
+static CCU_GATE(perisys_apb3_hclk, "perisys-apb3-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(11), 0);
+
+static CCU_GATE(perisys_apb4_hclk, "perisys-apb4-hclk", "perisys-ahb-hclk",
+ 0x150, BIT(12), 0);
+
+static CLK_FIXED_FACTOR_FW_NAME(osc12m_clk, "osc_12m", "osc_24m", 2, 1, 0);
+
+static const char * const out_parents[] = { "osc_24m", "osc_12m" };
+
+static struct ccu_div out1_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .reg = 0x1b4,
+ .hw.init = CLK_HW_INIT_PARENTS("out1",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div out2_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .reg = 0x1b8,
+ .hw.init = CLK_HW_INIT_PARENTS("out2",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div out3_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .reg = 0x1bc,
+ .hw.init = CLK_HW_INIT_PARENTS("out3",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div out4_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(4, 1),
+ .common = {
+ .reg = 0x1c0,
+ .hw.init = CLK_HW_INIT_PARENTS("out4",
+ out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const char * const apb_parents[] = { "gmac-pll", "osc_24m" };
+static struct ccu_div apb_pclk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(7, 1),
+ .common = {
+ .reg = 0x1c4,
+ .hw.init = CLK_HW_INIT_PARENTS("apb-pclk",
+ apb_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static const char * const npu_parents[] = { "gmac-pll", "video-pll" };
+static struct ccu_div npu_clk = {
+ .enable = BIT(4),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .mux = TH_CCU_ARG(6, 1),
+ .common = {
+ .reg = 0x1c8,
+ .hw.init = CLK_HW_INIT_PARENTS("npu",
+ npu_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CCU_GATE(npu_axi_clk, "npu-axi", "npu-ahb",
+ 0x1c8, BIT(5), 0);
+
+static struct ccu_div vi_clk = {
+ .div = TH_CCU_DIV_FLAGS(16, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1d0,
+ .hw.init = CLK_HW_INIT("vi",
+ "video-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div vi_ahb_clk = {
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1d0,
+ .hw.init = CLK_HW_INIT("vi-ahb",
+ "video-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div vo_axi_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1dc,
+ .hw.init = CLK_HW_INIT("vo-axi",
+ "video-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div vp_apb_clk = {
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1e0,
+ .hw.init = CLK_HW_INIT("vp-apb",
+ "gmac-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div vp_axi_clk = {
+ .enable = BIT(15),
+ .div = TH_CCU_DIV_FLAGS(8, 4, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1e0,
+ .hw.init = CLK_HW_INIT("vp-axi",
+ "video-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CCU_GATE(cpu2vp_clk, "cpu2vp", "axi-aclk",
+ 0x1e0, BIT(13), 0);
+
+static struct ccu_div venc_clk = {
+ .enable = BIT(5),
+ .div = TH_CCU_DIV_FLAGS(0, 3, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1e4,
+ .hw.init = CLK_HW_INIT("venc",
+ "gmac-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div dpu0_clk = {
+ .div = TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1e8,
+ .hw.init = CLK_HW_INIT("dpu0",
+ "dpu0-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div dpu1_clk = {
+ .div = TH_CCU_DIV_FLAGS(0, 8, CLK_DIVIDER_ONE_BASED),
+ .common = {
+ .reg = 0x1ec,
+ .hw.init = CLK_HW_INIT("dpu1",
+ "dpu1-pll",
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static CCU_GATE(emmc_sdio_clk, "emmc-sdio", "video-pll", 0x204, BIT(30), 0);
+static CCU_GATE(gmac1_clk, "gmac1", "gmac-pll", 0x204, BIT(26), 0);
+static CCU_GATE(padctrl1_clk, "padctrl1", "perisys-apb-pclk", 0x204, BIT(24), 0);
+static CCU_GATE(dsmart_clk, "dsmart", "perisys-apb-pclk", 0x204, BIT(23), 0);
+static CCU_GATE(padctrl0_clk, "padctrl0", "perisys-apb-pclk", 0x204, BIT(22), 0);
+static CCU_GATE(gmac_axi_clk, "gmac-axi", "axi4-cpusys2-aclk", 0x204, BIT(21), 0);
+static CCU_GATE(gpio3_clk, "gpio3-clk", "peri2sys-apb-pclk", 0x204, BIT(20), 0);
+static CCU_GATE(gmac0_clk, "gmac0", "gmac-pll", 0x204, BIT(19), 0);
+static CCU_GATE(pwm_clk, "pwm", "perisys-apb-pclk", 0x204, BIT(18), 0);
+static CCU_GATE(qspi0_clk, "qspi0", "video-pll", 0x204, BIT(17), 0);
+static CCU_GATE(qspi1_clk, "qspi1", "video-pll", 0x204, BIT(16), 0);
+static CCU_GATE(spi_clk, "spi", "video-pll", 0x204, BIT(15), 0);
+static CCU_GATE(uart0_pclk, "uart0-pclk", "perisys-apb-pclk", 0x204, BIT(14), 0);
+static CCU_GATE(uart1_pclk, "uart1-pclk", "perisys-apb-pclk", 0x204, BIT(13), 0);
+static CCU_GATE(uart2_pclk, "uart2-pclk", "perisys-apb-pclk", 0x204, BIT(12), 0);
+static CCU_GATE(uart3_pclk, "uart3-pclk", "perisys-apb-pclk", 0x204, BIT(11), 0);
+static CCU_GATE(uart4_pclk, "uart4-pclk", "perisys-apb-pclk", 0x204, BIT(10), 0);
+static CCU_GATE(uart5_pclk, "uart5-pclk", "perisys-apb-pclk", 0x204, BIT(9), 0);
+static CCU_GATE(gpio0_clk, "gpio0-clk", "perisys-apb-pclk", 0x204, BIT(8), 0);
+static CCU_GATE(gpio1_clk, "gpio1-clk", "perisys-apb-pclk", 0x204, BIT(7), 0);
+static CCU_GATE(gpio2_clk, "gpio2-clk", "peri2sys-apb-pclk", 0x204, BIT(6), 0);
+static CCU_GATE(i2c0_clk, "i2c0", "perisys-apb-pclk", 0x204, BIT(5), 0);
+static CCU_GATE(i2c1_clk, "i2c1", "perisys-apb-pclk", 0x204, BIT(4), 0);
+static CCU_GATE(i2c2_clk, "i2c2", "perisys-apb-pclk", 0x204, BIT(3), 0);
+static CCU_GATE(i2c3_clk, "i2c3", "perisys-apb-pclk", 0x204, BIT(2), 0);
+static CCU_GATE(i2c4_clk, "i2c4", "perisys-apb-pclk", 0x204, BIT(1), 0);
+static CCU_GATE(i2c5_clk, "i2c5", "perisys-apb-pclk", 0x204, BIT(0), 0);
+
+static CCU_GATE(spinlock_clk, "spinlock", "ahb2-cpusys-hclk", 0x208, BIT(10), 0);
+static CCU_GATE(dma_clk, "dma", "axi4-cpusys2-aclk", 0x208, BIT(8), 0);
+static CCU_GATE(mbox0_clk, "mbox0", "apb3-cpusys-pclk", 0x208, BIT(7), 0);
+static CCU_GATE(mbox1_clk, "mbox1", "apb3-cpusys-pclk", 0x208, BIT(6), 0);
+static CCU_GATE(mbox2_clk, "mbox2", "apb3-cpusys-pclk", 0x208, BIT(5), 0);
+static CCU_GATE(mbox3_clk, "mbox3", "apb3-cpusys-pclk", 0x208, BIT(4), 0);
+static CCU_GATE(wdt0_clk, "wdt0", "apb3-cpusys-pclk", 0x208, BIT(3), 0);
+static CCU_GATE(wdt1_clk, "wdt1", "apb3-cpusys-pclk", 0x208, BIT(2), 0);
+static CCU_GATE(timer0_clk, "timer0", "apb3-cpusys-pclk", 0x208, BIT(1), 0);
+static CCU_GATE(timer1_clk, "timer1", "apb3-cpusys-pclk", 0x208, BIT(0), 0);
+
+static CCU_GATE(sram0_clk, "sram0", "axi-aclk", 0x20c, BIT(4), 0);
+static CCU_GATE(sram1_clk, "sram1", "axi-aclk", 0x20c, BIT(3), 0);
+static CCU_GATE(sram2_clk, "sram2", "axi-aclk", 0x20c, BIT(2), 0);
+static CCU_GATE(sram3_clk, "sram3", "axi-aclk", 0x20c, BIT(1), 0);
+
+static CLK_FIXED_FACTOR_HW(gmac_pll_clk_100m, "gmac-pll-clk-100m",
+ &gmac_pll_clk.common.hw,
+ 10, 1, 0);
+
+static const char * const uart_sclk_parents[] = { "gmac-pll-clk-100m", "osc_24m" };
+struct ccu_mux uart_sclk = {
+ .mux = TH_CCU_ARG(0, 1),
+ .common = {
+ .reg = 0x210,
+ .hw.init = CLK_HW_INIT_PARENTS("uart-sclk",
+ uart_sclk_parents,
+ &ccu_mux_ops,
+ 0),
+ }
+};
+
+static struct ccu_common *th1520_clks[] = {
+ &cpu_pll0_clk.common,
+ &cpu_pll1_clk.common,
+ &gmac_pll_clk.common,
+ &video_pll_clk.common,
+ &dpu0_pll_clk.common,
+ &dpu1_pll_clk.common,
+ &tee_pll_clk.common,
+ &c910_i0_clk.common,
+ &c910_clk.common,
+ &brom_clk.common,
+ &bmu_clk.common,
+ &ahb2_cpusys_hclk.common,
+ &apb3_cpusys_pclk.common,
+ &axi4_cpusys2_aclk.common,
+ &aon2cpu_a2x_clk.common,
+ &x2x_cpusys_clk.common,
+ &axi_aclk.common,
+ &cpu2aon_x2h_clk.common,
+ &perisys_ahb_hclk.common,
+ &cpu2peri_x2h_clk.common,
+ &perisys_apb_pclk.common,
+ &peri2sys_apb_pclk.common,
+ &perisys_apb1_hclk.common,
+ &perisys_apb2_hclk.common,
+ &perisys_apb3_hclk.common,
+ &perisys_apb4_hclk.common,
+ &out1_clk.common,
+ &out2_clk.common,
+ &out3_clk.common,
+ &out4_clk.common,
+ &apb_pclk.common,
+ &npu_clk.common,
+ &npu_axi_clk.common,
+ &vi_clk.common,
+ &vi_ahb_clk.common,
+ &vo_axi_clk.common,
+ &vp_apb_clk.common,
+ &vp_axi_clk.common,
+ &cpu2vp_clk.common,
+ &venc_clk.common,
+ &dpu0_clk.common,
+ &dpu1_clk.common,
+ &emmc_sdio_clk.common,
+ &gmac1_clk.common,
+ &padctrl1_clk.common,
+ &dsmart_clk.common,
+ &padctrl0_clk.common,
+ &gmac_axi_clk.common,
+ &gpio3_clk.common,
+ &gmac0_clk.common,
+ &pwm_clk.common,
+ &qspi0_clk.common,
+ &qspi1_clk.common,
+ &spi_clk.common,
+ &uart0_pclk.common,
+ &uart1_pclk.common,
+ &uart2_pclk.common,
+ &uart3_pclk.common,
+ &uart4_pclk.common,
+ &uart5_pclk.common,
+ &gpio0_clk.common,
+ &gpio1_clk.common,
+ &gpio2_clk.common,
+ &i2c0_clk.common,
+ &i2c1_clk.common,
+ &i2c2_clk.common,
+ &i2c3_clk.common,
+ &i2c4_clk.common,
+ &i2c5_clk.common,
+ &spinlock_clk.common,
+ &dma_clk.common,
+ &mbox0_clk.common,
+ &mbox1_clk.common,
+ &mbox2_clk.common,
+ &mbox3_clk.common,
+ &wdt0_clk.common,
+ &wdt1_clk.common,
+ &timer0_clk.common,
+ &timer1_clk.common,
+ &sram0_clk.common,
+ &sram1_clk.common,
+ &sram2_clk.common,
+ &sram3_clk.common,
+ &uart_sclk.common,
+};
+
+#define NR_CLKS (CLK_UART_SCLK + 1)
+
+static struct clk_hw_onecell_data th1520_hw_clks = {
+ .hws = {
+ [CLK_OSC12M] = &osc12m_clk.hw,
+ [CLK_CPU_PLL0] = &cpu_pll0_clk.common.hw,
+ [CLK_CPU_PLL1] = &cpu_pll1_clk.common.hw,
+ [CLK_GMAC_PLL] = &gmac_pll_clk.common.hw,
+ [CLK_VIDEO_PLL] = &video_pll_clk.common.hw,
+ [CLK_DPU0_PLL] = &dpu0_pll_clk.common.hw,
+ [CLK_DPU1_PLL] = &dpu1_pll_clk.common.hw,
+ [CLK_TEE_PLL] = &tee_pll_clk.common.hw,
+ [CLK_C910_I0] = &c910_i0_clk.common.hw,
+ [CLK_C910] = &c910_clk.common.hw,
+ [CLK_BROM] = &brom_clk.common.hw,
+ [CLK_BMU] = &bmu_clk.common.hw,
+ [CLK_AHB2_CPUSYS_HCLK] = &ahb2_cpusys_hclk.common.hw,
+ [CLK_APB3_CPUSYS_PCLK] = &apb3_cpusys_pclk.common.hw,
+ [CLK_AXI4_CPUSYS2_ACLK] = &axi4_cpusys2_aclk.common.hw,
+ [CLK_AON2CPU_A2X] = &aon2cpu_a2x_clk.common.hw,
+ [CLK_X2X_CPUSYS] = &x2x_cpusys_clk.common.hw,
+ [CLK_AXI_ACLK] = &axi_aclk.common.hw,
+ [CLK_CPU2AON_X2H] = &cpu2aon_x2h_clk.common.hw,
+ [CLK_PERI_AHB_HCLK] = &perisys_ahb_hclk.common.hw,
+ [CLK_CPU2PERI_X2H] = &cpu2peri_x2h_clk.common.hw,
+ [CLK_PERI_APB_PCLK] = &perisys_apb_pclk.common.hw,
+ [CLK_PERI2APB_PCLK] = &peri2sys_apb_pclk.common.hw,
+ [CLK_PERI_APB1_HCLK] = &perisys_apb1_hclk.common.hw,
+ [CLK_PERI_APB2_HCLK] = &perisys_apb2_hclk.common.hw,
+ [CLK_PERI_APB3_HCLK] = &perisys_apb3_hclk.common.hw,
+ [CLK_PERI_APB4_HCLK] = &perisys_apb4_hclk.common.hw,
+ [CLK_OUT1] = &out1_clk.common.hw,
+ [CLK_OUT2] = &out2_clk.common.hw,
+ [CLK_OUT3] = &out3_clk.common.hw,
+ [CLK_OUT4] = &out4_clk.common.hw,
+ [CLK_APB_PCLK] = &apb_pclk.common.hw,
+ [CLK_NPU] = &npu_clk.common.hw,
+ [CLK_NPU_AXI] = &npu_axi_clk.common.hw,
+ [CLK_VI] = &vi_clk.common.hw,
+ [CLK_VI_AHB] = &vi_ahb_clk.common.hw,
+ [CLK_VO_AXI] = &vo_axi_clk.common.hw,
+ [CLK_VP_APB] = &vp_apb_clk.common.hw,
+ [CLK_VP_AXI] = &vp_axi_clk.common.hw,
+ [CLK_CPU2VP] = &cpu2vp_clk.common.hw,
+ [CLK_VENC] = &venc_clk.common.hw,
+ [CLK_DPU0] = &dpu0_clk.common.hw,
+ [CLK_DPU1] = &dpu1_clk.common.hw,
+ [CLK_EMMC_SDIO] = &emmc_sdio_clk.common.hw,
+ [CLK_GMAC1] = &gmac1_clk.common.hw,
+ [CLK_PADCTRL1] = &padctrl1_clk.common.hw,
+ [CLK_DSMART] = &dsmart_clk.common.hw,
+ [CLK_PADCTRL0] = &padctrl0_clk.common.hw,
+ [CLK_GMAC_AXI] = &gmac_axi_clk.common.hw,
+ [CLK_GPIO3] = &gpio3_clk.common.hw,
+ [CLK_GMAC0] = &gmac0_clk.common.hw,
+ [CLK_PWM] = &pwm_clk.common.hw,
+ [CLK_QSPI0] = &qspi0_clk.common.hw,
+ [CLK_QSPI1] = &qspi1_clk.common.hw,
+ [CLK_SPI] = &spi_clk.common.hw,
+ [CLK_UART0_PCLK] = &uart0_pclk.common.hw,
+ [CLK_UART1_PCLK] = &uart1_pclk.common.hw,
+ [CLK_UART2_PCLK] = &uart2_pclk.common.hw,
+ [CLK_UART3_PCLK] = &uart3_pclk.common.hw,
+ [CLK_UART4_PCLK] = &uart4_pclk.common.hw,
+ [CLK_UART5_PCLK] = &uart5_pclk.common.hw,
+ [CLK_GPIO0] = &gpio0_clk.common.hw,
+ [CLK_GPIO1] = &gpio1_clk.common.hw,
+ [CLK_GPIO2] = &gpio2_clk.common.hw,
+ [CLK_I2C0] = &i2c0_clk.common.hw,
+ [CLK_I2C1] = &i2c1_clk.common.hw,
+ [CLK_I2C2] = &i2c2_clk.common.hw,
+ [CLK_I2C3] = &i2c3_clk.common.hw,
+ [CLK_I2C4] = &i2c4_clk.common.hw,
+ [CLK_I2C5] = &i2c5_clk.common.hw,
+ [CLK_SPINLOCK] = &spinlock_clk.common.hw,
+ [CLK_DMA] = &dma_clk.common.hw,
+ [CLK_MBOX0] = &mbox0_clk.common.hw,
+ [CLK_MBOX1] = &mbox1_clk.common.hw,
+ [CLK_MBOX2] = &mbox2_clk.common.hw,
+ [CLK_MBOX3] = &mbox3_clk.common.hw,
+ [CLK_WDT0] = &wdt0_clk.common.hw,
+ [CLK_WDT1] = &wdt1_clk.common.hw,
+ [CLK_TIMER0] = &timer0_clk.common.hw,
+ [CLK_TIMER1] = &timer1_clk.common.hw,
+ [CLK_SRAM0] = &sram0_clk.common.hw,
+ [CLK_SRAM1] = &sram1_clk.common.hw,
+ [CLK_SRAM2] = &sram2_clk.common.hw,
+ [CLK_SRAM3] = &sram3_clk.common.hw,
+ [CLK_PLL_GMAC_100M] = &gmac_pll_clk_100m.hw,
+ [CLK_UART_SCLK] = &uart_sclk.common.hw,
+ },
+ .num = NR_CLKS,
+};
+
+static const struct regmap_config th1520_clk_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+static int th1520_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regmap *map;
+ void __iomem *regs;
+ int ret, i;
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ map = devm_regmap_init_mmio(dev, regs, &th1520_clk_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ for (i = 0; i < ARRAY_SIZE(th1520_clks); i++)
+ th1520_clks[i]->map = map;
+
+ for (i = 0; i < th1520_hw_clks.num; i++) {
+ ret = devm_clk_hw_register(dev, th1520_hw_clks.hws[i]);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ &th1520_hw_clks);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id th1520_clk_match[] = {
+ {
+ .compatible = "thead,th1520-clk-ap",
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, th1520_clk_match);
+
+static struct platform_driver th1520_clk_driver = {
+ .probe = th1520_clk_probe,
+ .driver = {
+ .name = "th1520-clk",
+ .of_match_table = th1520_clk_match,
+ },
+};
+module_platform_driver(th1520_clk_driver);
+
+MODULE_DESCRIPTION("T-HEAD TH1520 AP Clock driver");
+MODULE_AUTHOR("Yangtao Li <[email protected]>");
+MODULE_AUTHOR("Jisheng Zhang <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.34.1
Change the clock property in the T-Head TH1520 mmc controller nodes to a
real clock provided by the AP_SUBSYS clock driver.
Signed-off-by: Drew Fustini <[email protected]>
---
arch/riscv/boot/dts/thead/th1520.dtsi | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi
index 6285cdf91bd6..a6f51ec9fb55 100644
--- a/arch/riscv/boot/dts/thead/th1520.dtsi
+++ b/arch/riscv/boot/dts/thead/th1520.dtsi
@@ -324,7 +324,7 @@ emmc: mmc@ffe7080000 {
compatible = "thead,th1520-dwcmshc";
reg = <0xff 0xe7080000 0x0 0x10000>;
interrupts = <62 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&sdhci_clk>;
+ clocks = <&clk CLK_EMMC_SDIO>;
clock-names = "core";
status = "disabled";
};
@@ -333,7 +333,7 @@ sdio0: mmc@ffe7090000 {
compatible = "thead,th1520-dwcmshc";
reg = <0xff 0xe7090000 0x0 0x10000>;
interrupts = <64 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&sdhci_clk>;
+ clocks = <&clk CLK_EMMC_SDIO>;
clock-names = "core";
status = "disabled";
};
@@ -342,7 +342,7 @@ sdio1: mmc@ffe70a0000 {
compatible = "thead,th1520-dwcmshc";
reg = <0xff 0xe70a0000 0x0 0x10000>;
interrupts = <71 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&sdhci_clk>;
+ clocks = <&clk CLK_EMMC_SDIO>;
clock-names = "core";
status = "disabled";
};
--
2.34.1
On 27/04/2024 02:10, Drew Fustini wrote:
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/thead,th1520-clk-ap.h>
> + soc {
> +
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + clk: clock-controller@ffef010000 {
This is a friendly reminder during the review process.
It seems my or other reviewer's previous comments were not fully
addressed. Maybe the feedback got lost between the quotes, maybe you
just forgot to apply it. Please go back to the previous discussion and
either implement all requested changes or keep discussing them.
Thank you.
> + compatible = "thead,th1520-clk-ap";
> + reg = <0xff 0xef010000 0x0 0x1000>;
> + clocks = <&osc>;
> + clock-names = "osc";
> + #clock-cells = <1>;
> + };
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ebf03f5f0619..51d550cb1329 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19023,7 +19023,9 @@ M: Guo Ren <[email protected]>
> M: Fu Wei <[email protected]>
> L: [email protected]
> S: Maintained
> +F: Documentation/devicetree/bindings/clock/thead,th1520-clk-ap.yaml
> F: arch/riscv/boot/dts/thead/
> +F: include/dt-bindings/clock/thead,th1520-clk-ap.h
>
Best regards,
Krzysztof
On 4/27/24 2:10 AM, Drew Fustini wrote:
> Add support for the AP sub-system clock controller in the T-Head TH1520.
> This include CPU, DPU, GMAC and TEE PLLs.
>
> Link: https://openbeagle.org/beaglev-ahead/beaglev-ahead/-/blob/main/docs/TH1520%20System%20User%20Manual.pdf
> Co-developed-by: Yangtao Li <[email protected]>
> Signed-off-by: Yangtao Li <[email protected]>
> Co-developed-by: Jisheng Zhang <[email protected]>
> Signed-off-by: Jisheng Zhang <[email protected]>
> Signed-off-by: Drew Fustini <[email protected]>
> ...
> +void ccu_disable_helper(struct ccu_common *common, u32 gate)
> +{
> + if (!gate)
> + return;
> +
> + regmap_update_bits(common->map, common->reg,
> + gate, ~gate);
> +}
> +
> +int ccu_enable_helper(struct ccu_common *common, u32 gate)
> +{
> + if (!gate)
> + return 0;
> +
> + return regmap_update_bits(common->map, common->reg,
> + gate, gate);
> +}
> +
Both of those functions are only used in this file, you should probably
make them static because otherwise, the compilation process will prompt
a "missing prototype" warning.
Thomas
On 4/27/24 2:10 AM, Drew Fustini wrote:
> This series adds support for the AP sub-system clock controller in the
> T-Head TH1520 [1]. Yangtao Li originally submitted this series in May
> 2023 [2]. Jisheng made additional improvements and then passed on the
> work in progress to me.
>
> Changes I made from the original series:
> - corrected the npu_clk enable bit
> - deduplicated CLK_NPU and CLK_NPU_AXI number in header
> - fixed c910_i0_clk reg typo
> - fixed checkpatch and dt_binding_check warnings
> - rebased on v6.9-rc5
> - revised commit descriptions
>
> Changes since my RFC v1 [4]:
> - squash the header file patch into the DT schema patch
> - describe the changes I made to original series in the cover letter
> instead of the individual patches
> - fix my typo in my email address
>
> TODO:
> I am again marking this as an RFC because there is feedback from v1 that
> I have not yet addressed. I am posting what I currently have as other
> patch series like the TH1520 I2C driver [4] could use the clk driver.
>
> Emil commented that the input predivider is not handled correctly in
> ccu_mdiv_recalc_rate(). The PLL multiplies the input frequency and
> outputs "Foutvco". This is followed by a post divider to produce
> "Foutpostdiv". However, some clocks derive directly from the "Foutvco"
> Emil suggested this should really be modeled as two different clocks.
>
> Emil aslo suggested that the rest of the clocks in this driver seem to
> be generic gate and mux implementations that should probably be replaced
> with devm_clk_hw_register_gate*() and devm_clk_hw_register_mux*().
>
> I'll look to address the above issues in the next revision.
>
> Thank you,
> Drew
>
> [1] https://openbeagle.org/beaglev-ahead/beaglev-ahead/-/blob/main/docs/TH1520%20System%20User%20Manual.pdf
> [2] https://lore.kernel.org/linux-riscv/[email protected]/
> [3] https://lore.kernel.org/lkml/[email protected]/
> [4] https://lore.kernel.org/linux-riscv/[email protected]/
Thank you ! It works on the Beagle-V Ahead :)
Tested-by: Thomas Bonnefille <[email protected]>
On 4/27/24 2:10 AM, Drew Fustini wrote:
> Change the clock property in the T-Head TH1520 mmc controller nodes to a
> real clock provided by the AP_SUBSYS clock driver.
>
> Signed-off-by: Drew Fustini <[email protected]>
I experienced that, when the I2C clocks were correctly configured, the
UART stopped working, likely due to their dependence on FOUTPOSTDIV.
Setting up the UART correctly, for instance:
uartx: serial@xxxxxxxxxx {
...
clocks = <&clk CLK_UART_SCLK>, <&clk
CLK_UARTX_PCLK>;
clock-names = "baudclk", "apb_pclk";
...
status = "disabled";
};
resolved the issue.
As this would be mandatory in the future, I suggest that you configure
all the nodes currently set to a fixed clock, not just the MMC controller.
Thomas
On Thu, May 02, 2024 at 09:47:43AM +0200, Thomas Bonnefille wrote:
>
>
> On 4/27/24 2:10 AM, Drew Fustini wrote:
> > Change the clock property in the T-Head TH1520 mmc controller nodes to a
> > real clock provided by the AP_SUBSYS clock driver.
> >
> > Signed-off-by: Drew Fustini <[email protected]>
>
> I experienced that, when the I2C clocks were correctly configured, the UART
> stopped working, likely due to their dependence on FOUTPOSTDIV.
> Setting up the UART correctly, for instance:
>
> uartx: serial@xxxxxxxxxx {
> ...
> clocks = <&clk CLK_UART_SCLK>, <&clk
> CLK_UARTX_PCLK>;
> clock-names = "baudclk", "apb_pclk";
> ...
> status = "disabled";
> };
> resolved the issue.
> As this would be mandatory in the future, I suggest that you configure all
> the nodes currently set to a fixed clock, not just the MMC controller.
Thank you for testing and discovering this issue.
Could you post your device tree so I can be sure I'm testing the same as
what you have?
Drew
On 5/2/24 5:53 PM, Drew Fustini wrote:
> On Thu, May 02, 2024 at 09:47:43AM +0200, Thomas Bonnefille wrote:
>>
>>
>> On 4/27/24 2:10 AM, Drew Fustini wrote:
>>> Change the clock property in the T-Head TH1520 mmc controller nodes to a
>>> real clock provided by the AP_SUBSYS clock driver.
>>>
>>> Signed-off-by: Drew Fustini <[email protected]>
>>
>> I experienced that, when the I2C clocks were correctly configured, the UART
>> stopped working, likely due to their dependence on FOUTPOSTDIV.
>> Setting up the UART correctly, for instance:
>>
>> uartx: serial@xxxxxxxxxx {
>> ...
>> clocks = <&clk CLK_UART_SCLK>, <&clk
>> CLK_UARTX_PCLK>;
>> clock-names = "baudclk", "apb_pclk";
>> ...
>> status = "disabled";
>> };
>> resolved the issue.
>> As this would be mandatory in the future, I suggest that you configure all
>> the nodes currently set to a fixed clock, not just the MMC controller.
>
> Thank you for testing and discovering this issue.
>
> Could you post your device tree so I can be sure I'm testing the same as
> what you have?
>
> Drew
Of course, I'll attach the two Device Trees used.
Note that I also use Emil's patch which adds support for pinctrl.
Thomas