All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v15 0/5] riscv: sophgo: add clock support for sg2042
@ 2024-04-26  3:32 ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:32 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang

From: Chen Wang <unicorn_wang@outlook.com>

This series adds clock controller support for sophgo sg2042.

Thanks,
Chen

---

Changes in v15:

  The patch series is based on v6.9-rc5.

  Improved the dirvier code as per 3rd review comments from Stephen Boyd.
  - Converted all parents described by strings to use clk_parent_data or
    clk_hw directly.
  - Just use struct clk_init_data::parent_hws when only have a clk_hw.
  - Removed extra cleanup when use devm.
  - Some misc code improvements.

Changes in v14:

  The patch series is based on v6.9-rc1. You can simply review or test the
  patches at the link [15].

  Improved the dirvier code as per 2nd review comments from Stephen Boyd.
  - Inline the header file into source file.
  - Use devm_xxx functions for pll/div/gate registeration.
  - Use clk_parent_data for mux clocks initialization.
  - Use u32 for registers readl/writel.
  - Use devm_platform_ioremap_resource instead of devm_of_iomap.
  - Cleanup some dead code and add definitions for some magic numbers.
  - Add include files missed.
  - Use kernel-doc to improve comments for some structure and functions.
  - Other misc code cleanup work as per input from reviewers.

Changes in v13:

  The patch series is based on v6.9-rc1. You can simply review or test the
  patches at the link [14].

  Just added a minor fix for clk driver which was missed in v12.

Changes in v12:

  The patch series is based on v6.9-rc1. You can simply review or test the
  patches at the link [13].

  Improved the dirvier code as per review comments from Stephen Boyd.
  - Remove default y for CLK_SOPHGO_SG2042.
  - Optimize sg2042_pll_get_postdiv_1_2, move postdiv1_2 to the function.
    scope and add more explaniation.
  - Optimize sg2042_get_pll_ctl_setting.
  - Switch to platform driver.
  - Use clk_hw for initialization of struct clks.
  - Don't use ignore_unused when using critical.
  - Other code cleanup as per input form the reviewers.

Changes in v11:

  The patch series is based on v6.8-rc5. You can simply review or test the
  patches at the link [12].

  Quick fixed some dt_binding_check errors reported by Rob.

Changes in v10:

  The patch series is based on v6.8-rc4. You can simply review or test the
  patches at the link [11].

  Add input clocks for rpgate & clkgen.

Changes in v9:
  The patch series is based on v6.8-rc2. You can simply review or test the
  patches at the link [10].

  From this version, drop the system-controller node due to there is no actual
  device corresponding to it in IC design. SYS_CTRL is just a registers segment
  defined on TRM for misc functions. Now three clock-controllers are defined for
  SG2042, the control registers of the three clock-controllers are scattered in
  different memory address spaces:
  - the first one is for pll clocks;
  - the second one is for gate clocks for RP subsystem;
  - the third one is for div/mux, and gate clocks working for other subsystem
    than RP subsystem.

Changes in v8:
  The patch series is based on v6.7. You can simply review or test the
  patches at the link [9].
  
  In this version, the main change is to split one clock provider into two.
  Strictly follow the hardware instructions, in the memoymap, the control
  registers of some clocks are defined in the SYS_CTRL segment, and the
  control registers of other clocks are defined in the CLOCK segment.
  Therefore, the new design defines two clock controllers, one as a child
  node of the system control and the other as an independent clock controller
  node.

  This modification involves a major modification to the binding files, so
  the reviewed-by tags has been deleted.

Changes in v7:
  The patch series is based on v6.7. You can simply review or test the
  patches at the link [8].
  - fixed initval issue.
  - fixed pll clk crash issue.
  - fixed warning reported by <lkp@intel.com>
  - code optimization as per review comments.
  - code cleanup and style improvements as per review comments and checkpatch
    with "--strict"

Changes in v6:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [7].
  - fixed some warnings/errors reported by kernel test robot <lkp@intel.com>.

Changes in v5:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [6].
  - dt-bindings: improved yaml, such as:
    - add vendor prefix for system-ctrl property for clock generator.
    - Add explanation for system-ctrl property.
  - move sophgo,sg2042-clkgen.yaml to directly under clock folder.
  - fixed bugs for driver Makefile/Kconfig
  - continue cleaning-up debug print for driver code.

Changes in v4:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [5].
  - dt-bindings: fixed a dt_binding_check error.

Changes in v3:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [3].
  - DTS: don't use syscon but define sg2042 specific system control node. More
    background info can read [4].
  - Updating minor issues in dt-bindings as per input from reviews.

Changes in v2:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [2].
  - Squashed the patch adding clock definitions with the patch adding the
    binding for the clock controller.
  - Updating dt-binding for syscon, remove oneOf for property compatible;
    define clock controller as child of syscon.
  - DTS changes: merge sg2042-clock.dtsi into sg2042.dtsi; move clock-frequency
    property of osc to board devicethree due to the oscillator is outside the
    SoC.
  - Fixed some bugs in driver code during testing, including removing warnings
    for rv32_defconfig.
  - Updated MAINTAINERS info.

Changes in v1:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [1].

Link: https://lore.kernel.org/linux-riscv/cover.1699879741.git.unicorn_wang@outlook.com/ [1]
Link: https://lore.kernel.org/linux-riscv/cover.1701044106.git.unicorn_wang@outlook.com/ [2]
Link: https://lore.kernel.org/linux-riscv/cover.1701691923.git.unicorn_wang@outlook.com/ [3]
Link: https://lore.kernel.org/linux-riscv/MA0P287MB03329AE180378E1A2E034374FE82A@MA0P287MB0332.INDP287.PROD.OUTLOOK.COM/ [4]
Link: https://lore.kernel.org/linux-riscv/cover.1701734442.git.unicorn_wang@outlook.com/ [5]
Link: https://lore.kernel.org/linux-riscv/cover.1701938395.git.unicorn_wang@outlook.com/ [6]
Link: https://lore.kernel.org/linux-riscv/cover.1701997033.git.unicorn_wang@outlook.com/ [7]
Link: https://lore.kernel.org/linux-riscv/cover.1704694903.git.unicorn_wang@outlook.com/ [8]
Link: https://lore.kernel.org/linux-riscv/cover.1705388518.git.unicorn_wang@outlook.com/ [9]
Link: https://lore.kernel.org/linux-riscv/cover.1706854074.git.unicorn_wang@outlook.com/ [10]
Link: https://lore.kernel.org/linux-riscv/cover.1708223519.git.unicorn_wang@outlook.com/ [11]
Link: https://lore.kernel.org/linux-riscv/cover.1708397315.git.unicorn_wang@outlook.com/ [12]
Link: https://lore.kernel.org/linux-riscv/cover.1711527932.git.unicorn_wang@outlook.com/ [13]
Link: https://lore.kernel.org/linux-riscv/cover.1711692169.git.unicorn_wang@outlook.com/ [14]
Link: https://lore.kernel.org/linux-riscv/cover.1713164546.git.unicorn_wang@outlook.com/ [15]

---

Chen Wang (5):
  dt-bindings: clock: sophgo: add pll clocks for SG2042
  dt-bindings: clock: sophgo: add RP gate clocks for SG2042
  dt-bindings: clock: sophgo: add clkgen for SG2042
  clk: sophgo: Add SG2042 clock driver
  riscv: dts: add clock generator for Sophgo SG2042 SoC

 .../bindings/clock/sophgo,sg2042-clkgen.yaml  |   61 +
 .../bindings/clock/sophgo,sg2042-pll.yaml     |   53 +
 .../bindings/clock/sophgo,sg2042-rpgate.yaml  |   49 +
 .../boot/dts/sophgo/sg2042-milkv-pioneer.dts  |   12 +
 arch/riscv/boot/dts/sophgo/sg2042.dtsi        |   55 +-
 drivers/clk/Kconfig                           |    1 +
 drivers/clk/Makefile                          |    1 +
 drivers/clk/sophgo/Kconfig                    |    8 +
 drivers/clk/sophgo/Makefile                   |    2 +
 drivers/clk/sophgo/clk-sophgo-sg2042.c        | 1870 +++++++++++++++++
 .../dt-bindings/clock/sophgo,sg2042-clkgen.h  |  111 +
 include/dt-bindings/clock/sophgo,sg2042-pll.h |   14 +
 .../dt-bindings/clock/sophgo,sg2042-rpgate.h  |   58 +
 13 files changed, 2294 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
 create mode 100644 drivers/clk/sophgo/Kconfig
 create mode 100644 drivers/clk/sophgo/Makefile
 create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-clkgen.h
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-pll.h
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-rpgate.h


base-commit: ed30a4a51bb196781c8058073ea720133a65596f
-- 
2.25.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v15 0/5] riscv: sophgo: add clock support for sg2042
@ 2024-04-26  3:32 ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:32 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang

From: Chen Wang <unicorn_wang@outlook.com>

This series adds clock controller support for sophgo sg2042.

Thanks,
Chen

---

Changes in v15:

  The patch series is based on v6.9-rc5.

  Improved the dirvier code as per 3rd review comments from Stephen Boyd.
  - Converted all parents described by strings to use clk_parent_data or
    clk_hw directly.
  - Just use struct clk_init_data::parent_hws when only have a clk_hw.
  - Removed extra cleanup when use devm.
  - Some misc code improvements.

Changes in v14:

  The patch series is based on v6.9-rc1. You can simply review or test the
  patches at the link [15].

  Improved the dirvier code as per 2nd review comments from Stephen Boyd.
  - Inline the header file into source file.
  - Use devm_xxx functions for pll/div/gate registeration.
  - Use clk_parent_data for mux clocks initialization.
  - Use u32 for registers readl/writel.
  - Use devm_platform_ioremap_resource instead of devm_of_iomap.
  - Cleanup some dead code and add definitions for some magic numbers.
  - Add include files missed.
  - Use kernel-doc to improve comments for some structure and functions.
  - Other misc code cleanup work as per input from reviewers.

Changes in v13:

  The patch series is based on v6.9-rc1. You can simply review or test the
  patches at the link [14].

  Just added a minor fix for clk driver which was missed in v12.

Changes in v12:

  The patch series is based on v6.9-rc1. You can simply review or test the
  patches at the link [13].

  Improved the dirvier code as per review comments from Stephen Boyd.
  - Remove default y for CLK_SOPHGO_SG2042.
  - Optimize sg2042_pll_get_postdiv_1_2, move postdiv1_2 to the function.
    scope and add more explaniation.
  - Optimize sg2042_get_pll_ctl_setting.
  - Switch to platform driver.
  - Use clk_hw for initialization of struct clks.
  - Don't use ignore_unused when using critical.
  - Other code cleanup as per input form the reviewers.

Changes in v11:

  The patch series is based on v6.8-rc5. You can simply review or test the
  patches at the link [12].

  Quick fixed some dt_binding_check errors reported by Rob.

Changes in v10:

  The patch series is based on v6.8-rc4. You can simply review or test the
  patches at the link [11].

  Add input clocks for rpgate & clkgen.

Changes in v9:
  The patch series is based on v6.8-rc2. You can simply review or test the
  patches at the link [10].

  From this version, drop the system-controller node due to there is no actual
  device corresponding to it in IC design. SYS_CTRL is just a registers segment
  defined on TRM for misc functions. Now three clock-controllers are defined for
  SG2042, the control registers of the three clock-controllers are scattered in
  different memory address spaces:
  - the first one is for pll clocks;
  - the second one is for gate clocks for RP subsystem;
  - the third one is for div/mux, and gate clocks working for other subsystem
    than RP subsystem.

Changes in v8:
  The patch series is based on v6.7. You can simply review or test the
  patches at the link [9].
  
  In this version, the main change is to split one clock provider into two.
  Strictly follow the hardware instructions, in the memoymap, the control
  registers of some clocks are defined in the SYS_CTRL segment, and the
  control registers of other clocks are defined in the CLOCK segment.
  Therefore, the new design defines two clock controllers, one as a child
  node of the system control and the other as an independent clock controller
  node.

  This modification involves a major modification to the binding files, so
  the reviewed-by tags has been deleted.

Changes in v7:
  The patch series is based on v6.7. You can simply review or test the
  patches at the link [8].
  - fixed initval issue.
  - fixed pll clk crash issue.
  - fixed warning reported by <lkp@intel.com>
  - code optimization as per review comments.
  - code cleanup and style improvements as per review comments and checkpatch
    with "--strict"

Changes in v6:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [7].
  - fixed some warnings/errors reported by kernel test robot <lkp@intel.com>.

Changes in v5:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [6].
  - dt-bindings: improved yaml, such as:
    - add vendor prefix for system-ctrl property for clock generator.
    - Add explanation for system-ctrl property.
  - move sophgo,sg2042-clkgen.yaml to directly under clock folder.
  - fixed bugs for driver Makefile/Kconfig
  - continue cleaning-up debug print for driver code.

Changes in v4:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [5].
  - dt-bindings: fixed a dt_binding_check error.

Changes in v3:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [3].
  - DTS: don't use syscon but define sg2042 specific system control node. More
    background info can read [4].
  - Updating minor issues in dt-bindings as per input from reviews.

Changes in v2:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [2].
  - Squashed the patch adding clock definitions with the patch adding the
    binding for the clock controller.
  - Updating dt-binding for syscon, remove oneOf for property compatible;
    define clock controller as child of syscon.
  - DTS changes: merge sg2042-clock.dtsi into sg2042.dtsi; move clock-frequency
    property of osc to board devicethree due to the oscillator is outside the
    SoC.
  - Fixed some bugs in driver code during testing, including removing warnings
    for rv32_defconfig.
  - Updated MAINTAINERS info.

Changes in v1:
  The patch series is based on v6.7-rc1. You can simply review or test the
  patches at the link [1].

Link: https://lore.kernel.org/linux-riscv/cover.1699879741.git.unicorn_wang@outlook.com/ [1]
Link: https://lore.kernel.org/linux-riscv/cover.1701044106.git.unicorn_wang@outlook.com/ [2]
Link: https://lore.kernel.org/linux-riscv/cover.1701691923.git.unicorn_wang@outlook.com/ [3]
Link: https://lore.kernel.org/linux-riscv/MA0P287MB03329AE180378E1A2E034374FE82A@MA0P287MB0332.INDP287.PROD.OUTLOOK.COM/ [4]
Link: https://lore.kernel.org/linux-riscv/cover.1701734442.git.unicorn_wang@outlook.com/ [5]
Link: https://lore.kernel.org/linux-riscv/cover.1701938395.git.unicorn_wang@outlook.com/ [6]
Link: https://lore.kernel.org/linux-riscv/cover.1701997033.git.unicorn_wang@outlook.com/ [7]
Link: https://lore.kernel.org/linux-riscv/cover.1704694903.git.unicorn_wang@outlook.com/ [8]
Link: https://lore.kernel.org/linux-riscv/cover.1705388518.git.unicorn_wang@outlook.com/ [9]
Link: https://lore.kernel.org/linux-riscv/cover.1706854074.git.unicorn_wang@outlook.com/ [10]
Link: https://lore.kernel.org/linux-riscv/cover.1708223519.git.unicorn_wang@outlook.com/ [11]
Link: https://lore.kernel.org/linux-riscv/cover.1708397315.git.unicorn_wang@outlook.com/ [12]
Link: https://lore.kernel.org/linux-riscv/cover.1711527932.git.unicorn_wang@outlook.com/ [13]
Link: https://lore.kernel.org/linux-riscv/cover.1711692169.git.unicorn_wang@outlook.com/ [14]
Link: https://lore.kernel.org/linux-riscv/cover.1713164546.git.unicorn_wang@outlook.com/ [15]

---

Chen Wang (5):
  dt-bindings: clock: sophgo: add pll clocks for SG2042
  dt-bindings: clock: sophgo: add RP gate clocks for SG2042
  dt-bindings: clock: sophgo: add clkgen for SG2042
  clk: sophgo: Add SG2042 clock driver
  riscv: dts: add clock generator for Sophgo SG2042 SoC

 .../bindings/clock/sophgo,sg2042-clkgen.yaml  |   61 +
 .../bindings/clock/sophgo,sg2042-pll.yaml     |   53 +
 .../bindings/clock/sophgo,sg2042-rpgate.yaml  |   49 +
 .../boot/dts/sophgo/sg2042-milkv-pioneer.dts  |   12 +
 arch/riscv/boot/dts/sophgo/sg2042.dtsi        |   55 +-
 drivers/clk/Kconfig                           |    1 +
 drivers/clk/Makefile                          |    1 +
 drivers/clk/sophgo/Kconfig                    |    8 +
 drivers/clk/sophgo/Makefile                   |    2 +
 drivers/clk/sophgo/clk-sophgo-sg2042.c        | 1870 +++++++++++++++++
 .../dt-bindings/clock/sophgo,sg2042-clkgen.h  |  111 +
 include/dt-bindings/clock/sophgo,sg2042-pll.h |   14 +
 .../dt-bindings/clock/sophgo,sg2042-rpgate.h  |   58 +
 13 files changed, 2294 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
 create mode 100644 drivers/clk/sophgo/Kconfig
 create mode 100644 drivers/clk/sophgo/Makefile
 create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-clkgen.h
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-pll.h
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-rpgate.h


base-commit: ed30a4a51bb196781c8058073ea720133a65596f
-- 
2.25.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v15 1/5] dt-bindings: clock: sophgo: add pll clocks for SG2042
  2024-04-26  3:32 ` Chen Wang
@ 2024-04-26  3:33   ` Chen Wang
  -1 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:33 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang, Rob Herring

From: Chen Wang <unicorn_wang@outlook.com>

Add bindings for the pll clocks for Sophgo SG2042.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Guo Ren <guoren@kernel.org>
---
 .../bindings/clock/sophgo,sg2042-pll.yaml     | 53 +++++++++++++++++++
 include/dt-bindings/clock/sophgo,sg2042-pll.h | 14 +++++
 2 files changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-pll.h

diff --git a/Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml b/Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
new file mode 100644
index 000000000000..1a417a627dd2
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sophgo,sg2042-pll.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo SG2042 PLL Clock Generator
+
+maintainers:
+  - Chen Wang <unicorn_wang@outlook.com>
+
+properties:
+  compatible:
+    const: sophgo,sg2042-pll
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Oscillator(Clock Generation IC) for Main/Fixed PLL (25 MHz)
+      - description: Oscillator(Clock Generation IC) for DDR PLL 0 (25 MHz)
+      - description: Oscillator(Clock Generation IC) for DDR PLL 1 (25 MHz)
+
+  clock-names:
+    items:
+      - const: cgi_main
+      - const: cgi_dpll0
+      - const: cgi_dpll1
+
+  '#clock-cells':
+    const: 1
+    description:
+      See <dt-bindings/clock/sophgo,sg2042-pll.h> for valid indices.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@10000000 {
+      compatible = "sophgo,sg2042-pll";
+      reg = <0x10000000 0x10000>;
+      clocks = <&cgi_main>, <&cgi_dpll0>, <&cgi_dpll1>;
+      clock-names = "cgi_main", "cgi_dpll0", "cgi_dpll1";
+      #clock-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/sophgo,sg2042-pll.h b/include/dt-bindings/clock/sophgo,sg2042-pll.h
new file mode 100644
index 000000000000..2d519b3bf51c
--- /dev/null
+++ b/include/dt-bindings/clock/sophgo,sg2042-pll.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2023 Sophgo Technology Inc. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_SOPHGO_SG2042_PLL_H__
+#define __DT_BINDINGS_SOPHGO_SG2042_PLL_H__
+
+#define MPLL_CLK			0
+#define FPLL_CLK			1
+#define DPLL0_CLK			2
+#define DPLL1_CLK			3
+
+#endif /* __DT_BINDINGS_SOPHGO_SG2042_PLL_H__ */
-- 
2.25.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 1/5] dt-bindings: clock: sophgo: add pll clocks for SG2042
@ 2024-04-26  3:33   ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:33 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang, Rob Herring

From: Chen Wang <unicorn_wang@outlook.com>

Add bindings for the pll clocks for Sophgo SG2042.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Guo Ren <guoren@kernel.org>
---
 .../bindings/clock/sophgo,sg2042-pll.yaml     | 53 +++++++++++++++++++
 include/dt-bindings/clock/sophgo,sg2042-pll.h | 14 +++++
 2 files changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-pll.h

diff --git a/Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml b/Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
new file mode 100644
index 000000000000..1a417a627dd2
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sophgo,sg2042-pll.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo SG2042 PLL Clock Generator
+
+maintainers:
+  - Chen Wang <unicorn_wang@outlook.com>
+
+properties:
+  compatible:
+    const: sophgo,sg2042-pll
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Oscillator(Clock Generation IC) for Main/Fixed PLL (25 MHz)
+      - description: Oscillator(Clock Generation IC) for DDR PLL 0 (25 MHz)
+      - description: Oscillator(Clock Generation IC) for DDR PLL 1 (25 MHz)
+
+  clock-names:
+    items:
+      - const: cgi_main
+      - const: cgi_dpll0
+      - const: cgi_dpll1
+
+  '#clock-cells':
+    const: 1
+    description:
+      See <dt-bindings/clock/sophgo,sg2042-pll.h> for valid indices.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@10000000 {
+      compatible = "sophgo,sg2042-pll";
+      reg = <0x10000000 0x10000>;
+      clocks = <&cgi_main>, <&cgi_dpll0>, <&cgi_dpll1>;
+      clock-names = "cgi_main", "cgi_dpll0", "cgi_dpll1";
+      #clock-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/sophgo,sg2042-pll.h b/include/dt-bindings/clock/sophgo,sg2042-pll.h
new file mode 100644
index 000000000000..2d519b3bf51c
--- /dev/null
+++ b/include/dt-bindings/clock/sophgo,sg2042-pll.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2023 Sophgo Technology Inc. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_SOPHGO_SG2042_PLL_H__
+#define __DT_BINDINGS_SOPHGO_SG2042_PLL_H__
+
+#define MPLL_CLK			0
+#define FPLL_CLK			1
+#define DPLL0_CLK			2
+#define DPLL1_CLK			3
+
+#endif /* __DT_BINDINGS_SOPHGO_SG2042_PLL_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 2/5] dt-bindings: clock: sophgo: add RP gate clocks for SG2042
  2024-04-26  3:32 ` Chen Wang
@ 2024-04-26  3:34   ` Chen Wang
  -1 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:34 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang, Rob Herring

From: Chen Wang <unicorn_wang@outlook.com>

Add bindings for the gate clocks of RP subsystem for Sophgo SG2042.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/clock/sophgo,sg2042-rpgate.yaml  | 49 ++++++++++++++++
 .../dt-bindings/clock/sophgo,sg2042-rpgate.h  | 58 +++++++++++++++++++
 2 files changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-rpgate.h

diff --git a/Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml b/Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
new file mode 100644
index 000000000000..1491fb8ef6a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sophgo,sg2042-rpgate.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo SG2042 Gate Clock Generator for RP(riscv processors) subsystem
+
+maintainers:
+  - Chen Wang <unicorn_wang@outlook.com>
+
+properties:
+  compatible:
+    const: sophgo,sg2042-rpgate
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Gate clock for RP subsystem
+
+  clock-names:
+    items:
+      - const: rpgate
+
+  '#clock-cells':
+    const: 1
+    description:
+      See <dt-bindings/clock/sophgo,sg2042-rpgate.h> for valid indices.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@20000000 {
+      compatible = "sophgo,sg2042-rpgate";
+      reg = <0x20000000 0x10000>;
+      clocks = <&clkgen 85>;
+      clock-names = "rpgate";
+      #clock-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/sophgo,sg2042-rpgate.h b/include/dt-bindings/clock/sophgo,sg2042-rpgate.h
new file mode 100644
index 000000000000..8b4522d5f559
--- /dev/null
+++ b/include/dt-bindings/clock/sophgo,sg2042-rpgate.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2023 Sophgo Technology Inc. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_SOPHGO_SG2042_RPGATE_H__
+#define __DT_BINDINGS_SOPHGO_SG2042_RPGATE_H__
+
+#define GATE_CLK_RXU0			0
+#define GATE_CLK_RXU1			1
+#define GATE_CLK_RXU2			2
+#define GATE_CLK_RXU3			3
+#define GATE_CLK_RXU4			4
+#define GATE_CLK_RXU5			5
+#define GATE_CLK_RXU6			6
+#define GATE_CLK_RXU7			7
+#define GATE_CLK_RXU8			8
+#define GATE_CLK_RXU9			9
+#define GATE_CLK_RXU10			10
+#define GATE_CLK_RXU11			11
+#define GATE_CLK_RXU12			12
+#define GATE_CLK_RXU13			13
+#define GATE_CLK_RXU14			14
+#define GATE_CLK_RXU15			15
+#define GATE_CLK_RXU16			16
+#define GATE_CLK_RXU17			17
+#define GATE_CLK_RXU18			18
+#define GATE_CLK_RXU19			19
+#define GATE_CLK_RXU20			20
+#define GATE_CLK_RXU21			21
+#define GATE_CLK_RXU22			22
+#define GATE_CLK_RXU23			23
+#define GATE_CLK_RXU24			24
+#define GATE_CLK_RXU25			25
+#define GATE_CLK_RXU26			26
+#define GATE_CLK_RXU27			27
+#define GATE_CLK_RXU28			28
+#define GATE_CLK_RXU29			29
+#define GATE_CLK_RXU30			30
+#define GATE_CLK_RXU31			31
+#define GATE_CLK_MP0			32
+#define GATE_CLK_MP1			33
+#define GATE_CLK_MP2			34
+#define GATE_CLK_MP3			35
+#define GATE_CLK_MP4			36
+#define GATE_CLK_MP5			37
+#define GATE_CLK_MP6			38
+#define GATE_CLK_MP7			39
+#define GATE_CLK_MP8			40
+#define GATE_CLK_MP9			41
+#define GATE_CLK_MP10			42
+#define GATE_CLK_MP11			43
+#define GATE_CLK_MP12			44
+#define GATE_CLK_MP13			45
+#define GATE_CLK_MP14			46
+#define GATE_CLK_MP15			47
+
+#endif /* __DT_BINDINGS_SOPHGO_SG2042_RPGATE_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 2/5] dt-bindings: clock: sophgo: add RP gate clocks for SG2042
@ 2024-04-26  3:34   ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:34 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang, Rob Herring

From: Chen Wang <unicorn_wang@outlook.com>

Add bindings for the gate clocks of RP subsystem for Sophgo SG2042.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/clock/sophgo,sg2042-rpgate.yaml  | 49 ++++++++++++++++
 .../dt-bindings/clock/sophgo,sg2042-rpgate.h  | 58 +++++++++++++++++++
 2 files changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-rpgate.h

diff --git a/Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml b/Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
new file mode 100644
index 000000000000..1491fb8ef6a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sophgo,sg2042-rpgate.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo SG2042 Gate Clock Generator for RP(riscv processors) subsystem
+
+maintainers:
+  - Chen Wang <unicorn_wang@outlook.com>
+
+properties:
+  compatible:
+    const: sophgo,sg2042-rpgate
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Gate clock for RP subsystem
+
+  clock-names:
+    items:
+      - const: rpgate
+
+  '#clock-cells':
+    const: 1
+    description:
+      See <dt-bindings/clock/sophgo,sg2042-rpgate.h> for valid indices.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@20000000 {
+      compatible = "sophgo,sg2042-rpgate";
+      reg = <0x20000000 0x10000>;
+      clocks = <&clkgen 85>;
+      clock-names = "rpgate";
+      #clock-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/sophgo,sg2042-rpgate.h b/include/dt-bindings/clock/sophgo,sg2042-rpgate.h
new file mode 100644
index 000000000000..8b4522d5f559
--- /dev/null
+++ b/include/dt-bindings/clock/sophgo,sg2042-rpgate.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2023 Sophgo Technology Inc. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_SOPHGO_SG2042_RPGATE_H__
+#define __DT_BINDINGS_SOPHGO_SG2042_RPGATE_H__
+
+#define GATE_CLK_RXU0			0
+#define GATE_CLK_RXU1			1
+#define GATE_CLK_RXU2			2
+#define GATE_CLK_RXU3			3
+#define GATE_CLK_RXU4			4
+#define GATE_CLK_RXU5			5
+#define GATE_CLK_RXU6			6
+#define GATE_CLK_RXU7			7
+#define GATE_CLK_RXU8			8
+#define GATE_CLK_RXU9			9
+#define GATE_CLK_RXU10			10
+#define GATE_CLK_RXU11			11
+#define GATE_CLK_RXU12			12
+#define GATE_CLK_RXU13			13
+#define GATE_CLK_RXU14			14
+#define GATE_CLK_RXU15			15
+#define GATE_CLK_RXU16			16
+#define GATE_CLK_RXU17			17
+#define GATE_CLK_RXU18			18
+#define GATE_CLK_RXU19			19
+#define GATE_CLK_RXU20			20
+#define GATE_CLK_RXU21			21
+#define GATE_CLK_RXU22			22
+#define GATE_CLK_RXU23			23
+#define GATE_CLK_RXU24			24
+#define GATE_CLK_RXU25			25
+#define GATE_CLK_RXU26			26
+#define GATE_CLK_RXU27			27
+#define GATE_CLK_RXU28			28
+#define GATE_CLK_RXU29			29
+#define GATE_CLK_RXU30			30
+#define GATE_CLK_RXU31			31
+#define GATE_CLK_MP0			32
+#define GATE_CLK_MP1			33
+#define GATE_CLK_MP2			34
+#define GATE_CLK_MP3			35
+#define GATE_CLK_MP4			36
+#define GATE_CLK_MP5			37
+#define GATE_CLK_MP6			38
+#define GATE_CLK_MP7			39
+#define GATE_CLK_MP8			40
+#define GATE_CLK_MP9			41
+#define GATE_CLK_MP10			42
+#define GATE_CLK_MP11			43
+#define GATE_CLK_MP12			44
+#define GATE_CLK_MP13			45
+#define GATE_CLK_MP14			46
+#define GATE_CLK_MP15			47
+
+#endif /* __DT_BINDINGS_SOPHGO_SG2042_RPGATE_H__ */
-- 
2.25.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 3/5] dt-bindings: clock: sophgo: add clkgen for SG2042
  2024-04-26  3:32 ` Chen Wang
@ 2024-04-26  3:35   ` Chen Wang
  -1 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:35 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang, Rob Herring

From: Chen Wang <unicorn_wang@outlook.com>

Add bindings for the clock generator of divider/mux and gates working
for other subsystem than RP subsystem for Sophgo SG2042.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/clock/sophgo,sg2042-clkgen.yaml  |  61 ++++++++++
 .../dt-bindings/clock/sophgo,sg2042-clkgen.h  | 111 ++++++++++++++++++
 2 files changed, 172 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-clkgen.h

diff --git a/Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml b/Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
new file mode 100644
index 000000000000..e7a9255bcb58
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sophgo,sg2042-clkgen.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo SG2042 Clock Generator for divider/mux/gate
+
+maintainers:
+  - Chen Wang <unicorn_wang@outlook.com>
+
+properties:
+  compatible:
+    const: sophgo,sg2042-clkgen
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Main PLL
+      - description: Fixed PLL
+      - description: DDR PLL 0
+      - description: DDR PLL 1
+
+  clock-names:
+    items:
+      - const: mpll
+      - const: fpll
+      - const: dpll0
+      - const: dpll1
+
+  '#clock-cells':
+    const: 1
+    description:
+      See <dt-bindings/clock/sophgo,sg2042-clkgen.h> for valid indices.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@30012000 {
+      compatible = "sophgo,sg2042-clkgen";
+      reg = <0x30012000 0x1000>;
+      clocks = <&pllclk 0>,
+               <&pllclk 1>,
+               <&pllclk 2>,
+               <&pllclk 3>;
+      clock-names = "mpll",
+                    "fpll",
+                    "dpll0",
+                    "dpll1";
+      #clock-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/sophgo,sg2042-clkgen.h b/include/dt-bindings/clock/sophgo,sg2042-clkgen.h
new file mode 100644
index 000000000000..84f7857317a2
--- /dev/null
+++ b/include/dt-bindings/clock/sophgo,sg2042-clkgen.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2023 Sophgo Technology Inc. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_SOPHGO_SG2042_CLKGEN_H__
+#define __DT_BINDINGS_SOPHGO_SG2042_CLKGEN_H__
+
+#define DIV_CLK_MPLL_RP_CPU_NORMAL_0	0
+#define DIV_CLK_MPLL_AXI_DDR_0		1
+#define DIV_CLK_FPLL_DDR01_1		2
+#define DIV_CLK_FPLL_DDR23_1		3
+#define DIV_CLK_FPLL_RP_CPU_NORMAL_1	4
+#define DIV_CLK_FPLL_50M_A53		5
+#define DIV_CLK_FPLL_TOP_RP_CMN_DIV2	6
+#define DIV_CLK_FPLL_UART_500M		7
+#define DIV_CLK_FPLL_AHB_LPC		8
+#define DIV_CLK_FPLL_EFUSE		9
+#define DIV_CLK_FPLL_TX_ETH0		10
+#define DIV_CLK_FPLL_PTP_REF_I_ETH0	11
+#define DIV_CLK_FPLL_REF_ETH0		12
+#define DIV_CLK_FPLL_EMMC		13
+#define DIV_CLK_FPLL_SD			14
+#define DIV_CLK_FPLL_TOP_AXI0		15
+#define DIV_CLK_FPLL_TOP_AXI_HSPERI	16
+#define DIV_CLK_FPLL_AXI_DDR_1		17
+#define DIV_CLK_FPLL_DIV_TIMER1		18
+#define DIV_CLK_FPLL_DIV_TIMER2		19
+#define DIV_CLK_FPLL_DIV_TIMER3		20
+#define DIV_CLK_FPLL_DIV_TIMER4		21
+#define DIV_CLK_FPLL_DIV_TIMER5		22
+#define DIV_CLK_FPLL_DIV_TIMER6		23
+#define DIV_CLK_FPLL_DIV_TIMER7		24
+#define DIV_CLK_FPLL_DIV_TIMER8		25
+#define DIV_CLK_FPLL_100K_EMMC		26
+#define DIV_CLK_FPLL_100K_SD		27
+#define DIV_CLK_FPLL_GPIO_DB		28
+#define DIV_CLK_DPLL0_DDR01_0		29
+#define DIV_CLK_DPLL1_DDR23_0		30
+
+#define GATE_CLK_RP_CPU_NORMAL_DIV0	31
+#define GATE_CLK_AXI_DDR_DIV0		32
+
+#define GATE_CLK_RP_CPU_NORMAL_DIV1	33
+#define GATE_CLK_A53_50M		34
+#define GATE_CLK_TOP_RP_CMN_DIV2	35
+#define GATE_CLK_HSDMA			36
+#define GATE_CLK_EMMC_100M		37
+#define GATE_CLK_SD_100M		38
+#define GATE_CLK_TX_ETH0		39
+#define GATE_CLK_PTP_REF_I_ETH0		40
+#define GATE_CLK_REF_ETH0		41
+#define GATE_CLK_UART_500M		42
+#define GATE_CLK_EFUSE			43
+
+#define GATE_CLK_AHB_LPC		44
+#define GATE_CLK_AHB_ROM		45
+#define GATE_CLK_AHB_SF			46
+
+#define GATE_CLK_APB_UART		47
+#define GATE_CLK_APB_TIMER		48
+#define GATE_CLK_APB_EFUSE		49
+#define GATE_CLK_APB_GPIO		50
+#define GATE_CLK_APB_GPIO_INTR		51
+#define GATE_CLK_APB_SPI		52
+#define GATE_CLK_APB_I2C		53
+#define GATE_CLK_APB_WDT		54
+#define GATE_CLK_APB_PWM		55
+#define GATE_CLK_APB_RTC		56
+
+#define GATE_CLK_AXI_PCIE0		57
+#define GATE_CLK_AXI_PCIE1		58
+#define GATE_CLK_SYSDMA_AXI		59
+#define GATE_CLK_AXI_DBG_I2C		60
+#define GATE_CLK_AXI_SRAM		61
+#define GATE_CLK_AXI_ETH0		62
+#define GATE_CLK_AXI_EMMC		63
+#define GATE_CLK_AXI_SD			64
+#define GATE_CLK_TOP_AXI0		65
+#define GATE_CLK_TOP_AXI_HSPERI		66
+
+#define GATE_CLK_TIMER1			67
+#define GATE_CLK_TIMER2			68
+#define GATE_CLK_TIMER3			69
+#define GATE_CLK_TIMER4			70
+#define GATE_CLK_TIMER5			71
+#define GATE_CLK_TIMER6			72
+#define GATE_CLK_TIMER7			73
+#define GATE_CLK_TIMER8			74
+#define GATE_CLK_100K_EMMC		75
+#define GATE_CLK_100K_SD		76
+#define GATE_CLK_GPIO_DB		77
+
+#define GATE_CLK_AXI_DDR_DIV1		78
+#define GATE_CLK_DDR01_DIV1		79
+#define GATE_CLK_DDR23_DIV1		80
+
+#define GATE_CLK_DDR01_DIV0		81
+#define GATE_CLK_DDR23_DIV0		82
+
+#define GATE_CLK_DDR01			83
+#define GATE_CLK_DDR23			84
+#define GATE_CLK_RP_CPU_NORMAL		85
+#define GATE_CLK_AXI_DDR		86
+
+#define MUX_CLK_DDR01			87
+#define MUX_CLK_DDR23			88
+#define MUX_CLK_RP_CPU_NORMAL		89
+#define MUX_CLK_AXI_DDR			90
+
+#endif /* __DT_BINDINGS_SOPHGO_SG2042_CLKGEN_H__ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 3/5] dt-bindings: clock: sophgo: add clkgen for SG2042
@ 2024-04-26  3:35   ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:35 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang, Rob Herring

From: Chen Wang <unicorn_wang@outlook.com>

Add bindings for the clock generator of divider/mux and gates working
for other subsystem than RP subsystem for Sophgo SG2042.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/clock/sophgo,sg2042-clkgen.yaml  |  61 ++++++++++
 .../dt-bindings/clock/sophgo,sg2042-clkgen.h  | 111 ++++++++++++++++++
 2 files changed, 172 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
 create mode 100644 include/dt-bindings/clock/sophgo,sg2042-clkgen.h

diff --git a/Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml b/Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
new file mode 100644
index 000000000000..e7a9255bcb58
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sophgo,sg2042-clkgen.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sophgo SG2042 Clock Generator for divider/mux/gate
+
+maintainers:
+  - Chen Wang <unicorn_wang@outlook.com>
+
+properties:
+  compatible:
+    const: sophgo,sg2042-clkgen
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Main PLL
+      - description: Fixed PLL
+      - description: DDR PLL 0
+      - description: DDR PLL 1
+
+  clock-names:
+    items:
+      - const: mpll
+      - const: fpll
+      - const: dpll0
+      - const: dpll1
+
+  '#clock-cells':
+    const: 1
+    description:
+      See <dt-bindings/clock/sophgo,sg2042-clkgen.h> for valid indices.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@30012000 {
+      compatible = "sophgo,sg2042-clkgen";
+      reg = <0x30012000 0x1000>;
+      clocks = <&pllclk 0>,
+               <&pllclk 1>,
+               <&pllclk 2>,
+               <&pllclk 3>;
+      clock-names = "mpll",
+                    "fpll",
+                    "dpll0",
+                    "dpll1";
+      #clock-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/sophgo,sg2042-clkgen.h b/include/dt-bindings/clock/sophgo,sg2042-clkgen.h
new file mode 100644
index 000000000000..84f7857317a2
--- /dev/null
+++ b/include/dt-bindings/clock/sophgo,sg2042-clkgen.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (C) 2023 Sophgo Technology Inc. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_SOPHGO_SG2042_CLKGEN_H__
+#define __DT_BINDINGS_SOPHGO_SG2042_CLKGEN_H__
+
+#define DIV_CLK_MPLL_RP_CPU_NORMAL_0	0
+#define DIV_CLK_MPLL_AXI_DDR_0		1
+#define DIV_CLK_FPLL_DDR01_1		2
+#define DIV_CLK_FPLL_DDR23_1		3
+#define DIV_CLK_FPLL_RP_CPU_NORMAL_1	4
+#define DIV_CLK_FPLL_50M_A53		5
+#define DIV_CLK_FPLL_TOP_RP_CMN_DIV2	6
+#define DIV_CLK_FPLL_UART_500M		7
+#define DIV_CLK_FPLL_AHB_LPC		8
+#define DIV_CLK_FPLL_EFUSE		9
+#define DIV_CLK_FPLL_TX_ETH0		10
+#define DIV_CLK_FPLL_PTP_REF_I_ETH0	11
+#define DIV_CLK_FPLL_REF_ETH0		12
+#define DIV_CLK_FPLL_EMMC		13
+#define DIV_CLK_FPLL_SD			14
+#define DIV_CLK_FPLL_TOP_AXI0		15
+#define DIV_CLK_FPLL_TOP_AXI_HSPERI	16
+#define DIV_CLK_FPLL_AXI_DDR_1		17
+#define DIV_CLK_FPLL_DIV_TIMER1		18
+#define DIV_CLK_FPLL_DIV_TIMER2		19
+#define DIV_CLK_FPLL_DIV_TIMER3		20
+#define DIV_CLK_FPLL_DIV_TIMER4		21
+#define DIV_CLK_FPLL_DIV_TIMER5		22
+#define DIV_CLK_FPLL_DIV_TIMER6		23
+#define DIV_CLK_FPLL_DIV_TIMER7		24
+#define DIV_CLK_FPLL_DIV_TIMER8		25
+#define DIV_CLK_FPLL_100K_EMMC		26
+#define DIV_CLK_FPLL_100K_SD		27
+#define DIV_CLK_FPLL_GPIO_DB		28
+#define DIV_CLK_DPLL0_DDR01_0		29
+#define DIV_CLK_DPLL1_DDR23_0		30
+
+#define GATE_CLK_RP_CPU_NORMAL_DIV0	31
+#define GATE_CLK_AXI_DDR_DIV0		32
+
+#define GATE_CLK_RP_CPU_NORMAL_DIV1	33
+#define GATE_CLK_A53_50M		34
+#define GATE_CLK_TOP_RP_CMN_DIV2	35
+#define GATE_CLK_HSDMA			36
+#define GATE_CLK_EMMC_100M		37
+#define GATE_CLK_SD_100M		38
+#define GATE_CLK_TX_ETH0		39
+#define GATE_CLK_PTP_REF_I_ETH0		40
+#define GATE_CLK_REF_ETH0		41
+#define GATE_CLK_UART_500M		42
+#define GATE_CLK_EFUSE			43
+
+#define GATE_CLK_AHB_LPC		44
+#define GATE_CLK_AHB_ROM		45
+#define GATE_CLK_AHB_SF			46
+
+#define GATE_CLK_APB_UART		47
+#define GATE_CLK_APB_TIMER		48
+#define GATE_CLK_APB_EFUSE		49
+#define GATE_CLK_APB_GPIO		50
+#define GATE_CLK_APB_GPIO_INTR		51
+#define GATE_CLK_APB_SPI		52
+#define GATE_CLK_APB_I2C		53
+#define GATE_CLK_APB_WDT		54
+#define GATE_CLK_APB_PWM		55
+#define GATE_CLK_APB_RTC		56
+
+#define GATE_CLK_AXI_PCIE0		57
+#define GATE_CLK_AXI_PCIE1		58
+#define GATE_CLK_SYSDMA_AXI		59
+#define GATE_CLK_AXI_DBG_I2C		60
+#define GATE_CLK_AXI_SRAM		61
+#define GATE_CLK_AXI_ETH0		62
+#define GATE_CLK_AXI_EMMC		63
+#define GATE_CLK_AXI_SD			64
+#define GATE_CLK_TOP_AXI0		65
+#define GATE_CLK_TOP_AXI_HSPERI		66
+
+#define GATE_CLK_TIMER1			67
+#define GATE_CLK_TIMER2			68
+#define GATE_CLK_TIMER3			69
+#define GATE_CLK_TIMER4			70
+#define GATE_CLK_TIMER5			71
+#define GATE_CLK_TIMER6			72
+#define GATE_CLK_TIMER7			73
+#define GATE_CLK_TIMER8			74
+#define GATE_CLK_100K_EMMC		75
+#define GATE_CLK_100K_SD		76
+#define GATE_CLK_GPIO_DB		77
+
+#define GATE_CLK_AXI_DDR_DIV1		78
+#define GATE_CLK_DDR01_DIV1		79
+#define GATE_CLK_DDR23_DIV1		80
+
+#define GATE_CLK_DDR01_DIV0		81
+#define GATE_CLK_DDR23_DIV0		82
+
+#define GATE_CLK_DDR01			83
+#define GATE_CLK_DDR23			84
+#define GATE_CLK_RP_CPU_NORMAL		85
+#define GATE_CLK_AXI_DDR		86
+
+#define MUX_CLK_DDR01			87
+#define MUX_CLK_DDR23			88
+#define MUX_CLK_RP_CPU_NORMAL		89
+#define MUX_CLK_AXI_DDR			90
+
+#endif /* __DT_BINDINGS_SOPHGO_SG2042_CLKGEN_H__ */
-- 
2.25.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 4/5] clk: sophgo: Add SG2042 clock driver
  2024-04-26  3:32 ` Chen Wang
@ 2024-04-26  3:35   ` Chen Wang
  -1 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:35 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang

From: Chen Wang <unicorn_wang@outlook.com>

Add a driver for the SOPHGO SG2042 clocks.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
---
 drivers/clk/Kconfig                    |    1 +
 drivers/clk/Makefile                   |    1 +
 drivers/clk/sophgo/Kconfig             |    8 +
 drivers/clk/sophgo/Makefile            |    2 +
 drivers/clk/sophgo/clk-sophgo-sg2042.c | 1870 ++++++++++++++++++++++++
 5 files changed, 1882 insertions(+)
 create mode 100644 drivers/clk/sophgo/Kconfig
 create mode 100644 drivers/clk/sophgo/Makefile
 create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 50af5fc7f570..bc28502ec3c9 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
 source "drivers/clk/samsung/Kconfig"
 source "drivers/clk/sifive/Kconfig"
 source "drivers/clk/socfpga/Kconfig"
+source "drivers/clk/sophgo/Kconfig"
 source "drivers/clk/sprd/Kconfig"
 source "drivers/clk/starfive/Kconfig"
 source "drivers/clk/sunxi/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 14fa8d4ecc1f..4abe16c8ccdf 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
 obj-$(CONFIG_CLK_SIFIVE)		+= sifive/
 obj-y					+= socfpga/
+obj-y					+= sophgo/
 obj-$(CONFIG_PLAT_SPEAR)		+= spear/
 obj-y					+= sprd/
 obj-$(CONFIG_ARCH_STI)			+= st/
diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig
new file mode 100644
index 000000000000..82516adb0f97
--- /dev/null
+++ b/drivers/clk/sophgo/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# common clock support for SOPHGO SoC family.
+
+config CLK_SOPHGO_SG2042
+	bool "Sophgo SG2042 clock support"
+	depends on ARCH_SOPHGO || COMPILE_TEST
+	help
+	  Say yes here to support the clock controller on the Sophgo SG2042 SoC.
diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile
new file mode 100644
index 000000000000..13834cce260c
--- /dev/null
+++ b/drivers/clk/sophgo/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CLK_SOPHGO_SG2042)	+= clk-sophgo-sg2042.o
diff --git a/drivers/clk/sophgo/clk-sophgo-sg2042.c b/drivers/clk/sophgo/clk-sophgo-sg2042.c
new file mode 100644
index 000000000000..e64e1cb4872b
--- /dev/null
+++ b/drivers/clk/sophgo/clk-sophgo-sg2042.c
@@ -0,0 +1,1870 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sophgo SG2042 Clock Generator Driver
+ *
+ * Copyright (C) 2024 Sophgo Technology Inc. All rights reserved.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <asm/div64.h>
+
+/*
+ * The clock of SG2042 is composed of three parts.
+ * The registers of these three parts of the clock are scattered in three
+ * different memory address spaces:
+ * - pll clocks
+ * - gate clocks for RP subsystem
+ * - div/mux, and gate clocks working for other subsystem than RP subsystem
+ */
+#include <dt-bindings/clock/sophgo,sg2042-pll.h>
+#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
+#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
+
+/* Registers defined in SYS_CTRL */
+#define R_PLL_BEGIN		0xC0
+#define R_PLL_STAT		(0xC0 - R_PLL_BEGIN)
+#define R_PLL_CLKEN_CONTROL	(0xC4 - R_PLL_BEGIN)
+#define R_MPLL_CONTROL		(0xE8 - R_PLL_BEGIN)
+#define R_FPLL_CONTROL		(0xF4 - R_PLL_BEGIN)
+#define R_DPLL0_CONTROL		(0xF8 - R_PLL_BEGIN)
+#define R_DPLL1_CONTROL		(0xFC - R_PLL_BEGIN)
+
+#define R_SYSGATE_BEGIN		0x0368
+#define R_RP_RXU_CLK_ENABLE	(0x0368 - R_SYSGATE_BEGIN)
+#define R_MP0_STATUS_REG	(0x0380 - R_SYSGATE_BEGIN)
+#define R_MP0_CONTROL_REG	(0x0384 - R_SYSGATE_BEGIN)
+#define R_MP1_STATUS_REG	(0x0388 - R_SYSGATE_BEGIN)
+#define R_MP1_CONTROL_REG	(0x038C - R_SYSGATE_BEGIN)
+#define R_MP2_STATUS_REG	(0x0390 - R_SYSGATE_BEGIN)
+#define R_MP2_CONTROL_REG	(0x0394 - R_SYSGATE_BEGIN)
+#define R_MP3_STATUS_REG	(0x0398 - R_SYSGATE_BEGIN)
+#define R_MP3_CONTROL_REG	(0x039C - R_SYSGATE_BEGIN)
+#define R_MP4_STATUS_REG	(0x03A0 - R_SYSGATE_BEGIN)
+#define R_MP4_CONTROL_REG	(0x03A4 - R_SYSGATE_BEGIN)
+#define R_MP5_STATUS_REG	(0x03A8 - R_SYSGATE_BEGIN)
+#define R_MP5_CONTROL_REG	(0x03AC - R_SYSGATE_BEGIN)
+#define R_MP6_STATUS_REG	(0x03B0 - R_SYSGATE_BEGIN)
+#define R_MP6_CONTROL_REG	(0x03B4 - R_SYSGATE_BEGIN)
+#define R_MP7_STATUS_REG	(0x03B8 - R_SYSGATE_BEGIN)
+#define R_MP7_CONTROL_REG	(0x03BC - R_SYSGATE_BEGIN)
+#define R_MP8_STATUS_REG	(0x03C0 - R_SYSGATE_BEGIN)
+#define R_MP8_CONTROL_REG	(0x03C4 - R_SYSGATE_BEGIN)
+#define R_MP9_STATUS_REG	(0x03C8 - R_SYSGATE_BEGIN)
+#define R_MP9_CONTROL_REG	(0x03CC - R_SYSGATE_BEGIN)
+#define R_MP10_STATUS_REG	(0x03D0 - R_SYSGATE_BEGIN)
+#define R_MP10_CONTROL_REG	(0x03D4 - R_SYSGATE_BEGIN)
+#define R_MP11_STATUS_REG	(0x03D8 - R_SYSGATE_BEGIN)
+#define R_MP11_CONTROL_REG	(0x03DC - R_SYSGATE_BEGIN)
+#define R_MP12_STATUS_REG	(0x03E0 - R_SYSGATE_BEGIN)
+#define R_MP12_CONTROL_REG	(0x03E4 - R_SYSGATE_BEGIN)
+#define R_MP13_STATUS_REG	(0x03E8 - R_SYSGATE_BEGIN)
+#define R_MP13_CONTROL_REG	(0x03EC - R_SYSGATE_BEGIN)
+#define R_MP14_STATUS_REG	(0x03F0 - R_SYSGATE_BEGIN)
+#define R_MP14_CONTROL_REG	(0x03F4 - R_SYSGATE_BEGIN)
+#define R_MP15_STATUS_REG	(0x03F8 - R_SYSGATE_BEGIN)
+#define R_MP15_CONTROL_REG	(0x03FC - R_SYSGATE_BEGIN)
+
+/* Registers defined in CLOCK */
+#define R_CLKENREG0		0x00
+#define R_CLKENREG1		0x04
+#define R_CLKSELREG0		0x20
+#define R_CLKDIVREG0		0x40
+#define R_CLKDIVREG1		0x44
+#define R_CLKDIVREG2		0x48
+#define R_CLKDIVREG3		0x4C
+#define R_CLKDIVREG4		0x50
+#define R_CLKDIVREG5		0x54
+#define R_CLKDIVREG6		0x58
+#define R_CLKDIVREG7		0x5C
+#define R_CLKDIVREG8		0x60
+#define R_CLKDIVREG9		0x64
+#define R_CLKDIVREG10		0x68
+#define R_CLKDIVREG11		0x6C
+#define R_CLKDIVREG12		0x70
+#define R_CLKDIVREG13		0x74
+#define R_CLKDIVREG14		0x78
+#define R_CLKDIVREG15		0x7C
+#define R_CLKDIVREG16		0x80
+#define R_CLKDIVREG17		0x84
+#define R_CLKDIVREG18		0x88
+#define R_CLKDIVREG19		0x8C
+#define R_CLKDIVREG20		0x90
+#define R_CLKDIVREG21		0x94
+#define R_CLKDIVREG22		0x98
+#define R_CLKDIVREG23		0x9C
+#define R_CLKDIVREG24		0xA0
+#define R_CLKDIVREG25		0xA4
+#define R_CLKDIVREG26		0xA8
+#define R_CLKDIVREG27		0xAC
+#define R_CLKDIVREG28		0xB0
+#define R_CLKDIVREG29		0xB4
+#define R_CLKDIVREG30		0xB8
+
+/* All following shift value are the same for all DIV registers */
+#define SHIFT_DIV_RESET_CTRL	0
+#define SHIFT_DIV_FACTOR_SEL	3
+#define SHIFT_DIV_FACTOR	16
+
+/**
+ * struct sg2042_clk_data - Common data of clock-controller
+ * @iobase: base address of clock-controller
+ * @onecell_data: used for adding providers.
+ *
+ * Note: this structure will be used both by clkgen & sysclk.
+ */
+struct sg2042_clk_data {
+	void __iomem *iobase;
+	struct clk_hw_onecell_data onecell_data;
+};
+
+/**
+ * struct sg2042_pll_clock - PLL clock
+ * @hw:				clk_hw for initialization
+ * @id:				used to map clk_onecell_data
+ * @base:			used for readl/writel.
+ *				**NOTE**: PLL registers are all in SYS_CTRL!
+ * @lock:			spinlock to protect register access, modification
+ *				of frequency can only be served one at the time.
+ * @offset_status:		offset of pll status registers
+ * @offset_enable:		offset of pll enable registers
+ * @offset_ctrl:		offset of pll control registers
+ * @shift_status_lock:		shift of XXX_LOCK in pll status register
+ * @shift_status_updating:	shift of UPDATING_XXX in pll status register
+ * @shift_enable:		shift of XXX_CLK_EN in pll enable register
+ */
+struct sg2042_pll_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+	void __iomem *base;
+	/* protect register access */
+	spinlock_t *lock;
+
+	u32 offset_status;
+	u32 offset_enable;
+	u32 offset_ctrl;
+	u8 shift_status_lock;
+	u8 shift_status_updating;
+	u8 shift_enable;
+};
+
+#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
+
+/**
+ * struct sg2042_divider_clock - Divider clock
+ * @hw:			clk_hw for initialization
+ * @id:			used to map clk_onecell_data
+ * @reg:		used for readl/writel.
+ *			**NOTE**: DIV registers are ALL in CLOCK!
+ * @lock:		spinlock to protect register access, modification of
+ *			frequency can only be served one at the time
+ * @offset_ctrl:	offset of divider control registers
+ * @shift:		shift of "Clock Divider Factor" in divider control register
+ * @width:		width of "Clock Divider Factor" in divider control register
+ * @div_flags:		private flags for this clock, not for framework-specific
+ * @initval:		In the divider control register, we can configure whether
+ *			to use the value of "Clock Divider Factor" or just use
+ *			the initial value pre-configured by IC. BIT[3] controls
+ *			this and by default (value is 0), means initial value
+ *			is used.
+ *			**NOTE** that we cannot read the initial value (default
+ *			value when poweron) and default value of "Clock Divider
+ *			Factor" is zero, which I think is a hardware design flaw
+ *			and should be sync-ed with the initial value. So in
+ *			software we have to add a configuration item (initval)
+ *			to manually configure this value and use it when BIT[3]
+ *			is zero.
+ */
+struct sg2042_divider_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+
+	void __iomem *reg;
+	/* protect register access */
+	spinlock_t *lock;
+
+	u32 offset_ctrl;
+	u8 shift;
+	u8 width;
+	u8 div_flags;
+	u32 initval;
+};
+
+#define to_sg2042_clk_divider(_hw)	\
+	container_of(_hw, struct sg2042_divider_clock, hw)
+
+/**
+ * struct sg2042_gate_clock - Gate clock
+ * @hw:			clk_hw for initialization
+ * @id:			used to map clk_onecell_data
+ * @offset_enable:	offset of gate enable registers
+ * @bit_idx:		which bit in the register controls gating of this clock
+ */
+struct sg2042_gate_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+
+	u32 offset_enable;
+	u8 bit_idx;
+};
+
+/**
+ * struct sg2042_mux_clock - Mux clock
+ * @hw:			clk_hw for initialization
+ * @id:			used to map clk_onecell_data
+ * @offset_select:	offset of mux selection registers
+ *			**NOTE**: MUX registers are ALL in CLOCK!
+ * @shift:		shift of "Clock Select" in mux selection register
+ * @width:		width of "Clock Select" in mux selection register
+ * @clk_nb:		used for notification
+ * @original_index:	set by notifier callback
+ */
+struct sg2042_mux_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+
+	u32 offset_select;
+	u8 shift;
+	u8 width;
+
+	struct notifier_block clk_nb;
+	u8 original_index;
+};
+
+#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb)
+
+#define KHZ 1000UL
+#define MHZ (KHZ * KHZ)
+
+#define REFDIV_MIN 1
+#define REFDIV_MAX 63
+#define FBDIV_MIN 16
+#define FBDIV_MAX 320
+
+#define PLL_FREF_SG2042 (25 * MHZ)
+
+#define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
+#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
+
+#define PLL_FOUTVCO_MIN (800 * MHZ)
+#define PLL_FOUTVCO_MAX (3200 * MHZ)
+
+struct sg2042_pll_ctrl {
+	unsigned long freq;
+	unsigned int fbdiv;
+	unsigned int postdiv1;
+	unsigned int postdiv2;
+	unsigned int refdiv;
+};
+
+#define PLLCTRL_FBDIV_SHIFT	16
+#define PLLCTRL_FBDIV_MASK	(GENMASK(27, 16) >> PLLCTRL_FBDIV_SHIFT)
+#define PLLCTRL_POSTDIV2_SHIFT	12
+#define PLLCTRL_POSTDIV2_MASK	(GENMASK(14, 12) >> PLLCTRL_POSTDIV2_SHIFT)
+#define PLLCTRL_POSTDIV1_SHIFT	8
+#define PLLCTRL_POSTDIV1_MASK	(GENMASK(10, 8) >> PLLCTRL_POSTDIV1_SHIFT)
+#define PLLCTRL_REFDIV_SHIFT	0
+#define PLLCTRL_REFDIV_MASK	(GENMASK(5, 0) >> PLLCTRL_REFDIV_SHIFT)
+
+static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
+{
+	return ((ctrl->fbdiv & PLLCTRL_FBDIV_MASK) << PLLCTRL_FBDIV_SHIFT) |
+	       ((ctrl->postdiv2 & PLLCTRL_POSTDIV2_MASK) << PLLCTRL_POSTDIV2_SHIFT) |
+	       ((ctrl->postdiv1 & PLLCTRL_POSTDIV1_MASK) << PLLCTRL_POSTDIV1_SHIFT) |
+	       ((ctrl->refdiv & PLLCTRL_REFDIV_MASK) << PLLCTRL_REFDIV_SHIFT);
+}
+
+static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
+					  struct sg2042_pll_ctrl *ctrl)
+{
+	ctrl->fbdiv = (reg_value >> PLLCTRL_FBDIV_SHIFT) & PLLCTRL_FBDIV_MASK;
+	ctrl->refdiv = (reg_value >> PLLCTRL_REFDIV_SHIFT) & PLLCTRL_REFDIV_MASK;
+	ctrl->postdiv1 = (reg_value >> PLLCTRL_POSTDIV1_SHIFT) & PLLCTRL_POSTDIV1_MASK;
+	ctrl->postdiv2 = (reg_value >> PLLCTRL_POSTDIV2_SHIFT) & PLLCTRL_POSTDIV2_MASK;
+}
+
+static inline int sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
+{
+	u32 value;
+
+	if (en) {
+		/* wait pll lock */
+		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
+					      value,
+					      ((value >> pll->shift_status_lock) & 0x1),
+					      0,
+					      100000))
+			pr_warn("%s not locked\n", pll->hw.init->name);
+
+		/* wait pll updating */
+		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
+					      value,
+					      !((value >> pll->shift_status_updating) & 0x1),
+					      0,
+					      100000))
+			pr_warn("%s still updating\n", pll->hw.init->name);
+
+		/* enable pll */
+		value = readl(pll->base + pll->offset_enable);
+		writel(value | (1 << pll->shift_enable), pll->base + pll->offset_enable);
+	} else {
+		/* disable pll */
+		value = readl(pll->base + pll->offset_enable);
+		writel(value & (~(1 << pll->shift_enable)), pll->base + pll->offset_enable);
+	}
+
+	return 0;
+}
+
+/**
+ * sg2042_pll_recalc_rate() - Calculate rate for plls
+ * @reg_value: current register value
+ * @parent_rate: parent frequency
+ *
+ * This function is used to calculate below "rate" in equation
+ * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
+ *      = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
+ *
+ * Return: The rate calculated.
+ */
+static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
+					    unsigned long parent_rate)
+{
+	struct sg2042_pll_ctrl ctrl_table;
+	u64 numerator, denominator;
+
+	sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
+
+	numerator = parent_rate * ctrl_table.fbdiv;
+	denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
+	do_div(numerator, denominator);
+	return numerator;
+}
+
+/**
+ * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
+ * look up the postdiv1_2 table to get the closest postdiiv combination.
+ * @rate: FOUTPOSTDIV
+ * @prate: parent rate, i.e. FREF
+ * @fbdiv: FBDIV
+ * @refdiv: REFDIV
+ * @postdiv1: POSTDIV1, output
+ * @postdiv2: POSTDIV2, output
+ *
+ * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
+ * for example:
+ * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
+ *
+ * See TRM:
+ * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
+ * So we get following formula to get POSTDIV1 and POSTDIV2:
+ * POSTDIV = (prate/REFDIV) x FBDIV/rate
+ * above POSTDIV = POSTDIV1*POSTDIV2
+ *
+ * Return:
+ * %0 - OK
+ * %-EINVAL - invalid argument, which means Failed to get the postdivs.
+ */
+static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
+				      unsigned long prate,
+				      unsigned int fbdiv,
+				      unsigned int refdiv,
+				      unsigned int *postdiv1,
+				      unsigned int *postdiv2)
+{
+	int index;
+	u64 tmp0;
+
+	/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
+	#define	POSTDIV_RESULT_INDEX	2
+
+	static const int postdiv1_2[][3] = {
+		{2, 4,  8}, {3, 3,  9}, {2, 5, 10}, {2, 6, 12},
+		{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
+		{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
+		{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
+		{6, 7, 42}, {7, 7, 49}
+	};
+
+	/* prate/REFDIV and result save to tmp0 */
+	tmp0 = prate;
+	do_div(tmp0, refdiv);
+
+	/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
+	tmp0 *= fbdiv;
+
+	/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
+	do_div(tmp0, rate);
+
+	/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
+	if (tmp0 <= 7) {
+		/* (div1 * div2) <= 7, no need to use array search */
+		*postdiv1 = tmp0;
+		*postdiv2 = 1;
+		return 0;
+	}
+
+	/* (div1 * div2) > 7, use array search */
+	for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
+		if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
+			continue;
+		} else {
+			/* found it */
+			*postdiv1 = postdiv1_2[index][1];
+			*postdiv2 = postdiv1_2[index][0];
+			return 0;
+		}
+	}
+	pr_warn("%s can not find in postdiv array!\n", __func__);
+	return -EINVAL;
+}
+
+/**
+ * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
+ * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
+ * register.
+ * @req_rate: expected output clock rate, i.e. FOUTPISTDIV
+ * @parent_rate: input parent clock rate, i.e. FREF
+ * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
+ *
+ * Return:
+ * %0 - OK
+ * %-EINVAL - invalid argument
+ */
+static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
+				      unsigned long req_rate,
+				      unsigned long parent_rate)
+{
+	int ret;
+	unsigned int fbdiv, refdiv, postdiv1, postdiv2;
+	unsigned long foutpostdiv;
+	u64 tmp;
+	u64 foutvco;
+
+	if (parent_rate != PLL_FREF_SG2042) {
+		pr_err("INVALID FREF: %ld\n", parent_rate);
+		return -EINVAL;
+	}
+
+	if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
+		pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
+		return -EINVAL;
+	}
+
+	memset(best, 0, sizeof(struct sg2042_pll_ctrl));
+
+	for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
+		/* required by hardware: FREF/REFDIV must > 10 */
+		tmp = parent_rate;
+		do_div(tmp, refdiv);
+		if (tmp <= 10)
+			continue;
+
+		for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
+			/*
+			 * FOUTVCO = FREF*FBDIV/REFDIV validation
+			 * required by hardware, FOUTVCO must [800MHz, 3200MHz]
+			 */
+			foutvco = parent_rate * fbdiv;
+			do_div(foutvco, refdiv);
+			if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
+				continue;
+
+			ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
+							 fbdiv, refdiv,
+							 &postdiv1, &postdiv2);
+			if (ret)
+				continue;
+
+			/*
+			 * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
+			 *             = FOUTVCO/(POSTDIV1*POSTDIV2)
+			 */
+			tmp = foutvco;
+			do_div(tmp, (postdiv1 * postdiv2));
+			foutpostdiv = (unsigned long)tmp;
+			/* Iterative to approach the expected value */
+			if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
+				best->freq = foutpostdiv;
+				best->refdiv = refdiv;
+				best->fbdiv = fbdiv;
+				best->postdiv1 = postdiv1;
+				best->postdiv2 = postdiv2;
+				if (foutpostdiv == req_rate)
+					return 0;
+			}
+			continue;
+		}
+	}
+
+	if (best->freq == 0)
+		return -EINVAL;
+	else
+		return 0;
+}
+
+/**
+ * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
+ * @hw: ccf use to hook get sg2042_pll_clock
+ * @parent_rate: parent rate
+ *
+ * The is function will be called through clk_get_rate
+ * and return current rate after decoding reg value
+ *
+ * Return: Current rate recalculated.
+ */
+static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	u32 value;
+	unsigned long rate;
+	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
+
+	value = readl(pll->base + pll->offset_ctrl);
+	rate = sg2042_pll_recalc_rate(value, parent_rate);
+
+	pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
+		 clk_hw_get_name(hw), rate);
+	return rate;
+}
+
+static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
+				      unsigned long req_rate,
+				      unsigned long *prate)
+{
+	unsigned int value;
+	struct sg2042_pll_ctrl pctrl_table;
+	long proper_rate;
+	int ret;
+
+	ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
+	if (ret) {
+		proper_rate = 0;
+		goto out;
+	}
+
+	value = sg2042_pll_ctrl_encode(&pctrl_table);
+	proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
+
+out:
+	pr_debug("--> %s: pll_round_rate: val = %ld\n",
+		 clk_hw_get_name(hw), proper_rate);
+	return proper_rate;
+}
+
+static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
+					 struct clk_rate_request *req)
+{
+	req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
+					      &req->best_parent_rate);
+	pr_debug("--> %s: pll_determine_rate: val = %ld\n",
+		 clk_hw_get_name(hw), req->rate);
+	return 0;
+}
+
+static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
+				   unsigned long rate,
+				   unsigned long parent_rate)
+{
+	unsigned long flags;
+	u32 value;
+	int ret = 0;
+	struct sg2042_pll_ctrl pctrl_table;
+	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
+
+	spin_lock_irqsave(pll->lock, flags);
+	if (sg2042_pll_enable(pll, 0)) {
+		pr_warn("Can't disable pll(%s), status error\n", pll->hw.init->name);
+		goto out;
+	}
+	ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
+	if (ret) {
+		pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
+		goto out2;
+	}
+
+	value = sg2042_pll_ctrl_encode(&pctrl_table);
+
+	/* write the value to top register */
+	writel(value, pll->base + pll->offset_ctrl);
+
+out2:
+	sg2042_pll_enable(pll, 1);
+out:
+	spin_unlock_irqrestore(pll->lock, flags);
+
+	pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
+		 clk_hw_get_name(hw), value);
+	return ret;
+}
+
+static const struct clk_ops sg2042_clk_pll_ops = {
+	.recalc_rate = sg2042_clk_pll_recalc_rate,
+	.round_rate = sg2042_clk_pll_round_rate,
+	.determine_rate = sg2042_clk_pll_determine_rate,
+	.set_rate = sg2042_clk_pll_set_rate,
+};
+
+static const struct clk_ops sg2042_clk_pll_ro_ops = {
+	.recalc_rate = sg2042_clk_pll_recalc_rate,
+	.round_rate = sg2042_clk_pll_round_rate,
+};
+
+static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,
+						    unsigned long parent_rate)
+{
+	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
+	u32 val;
+	unsigned long ret_rate;
+
+	if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
+		val = divider->initval;
+	} else {
+		val = readl(divider->reg) >> divider->shift;
+		val &= clk_div_mask(divider->width);
+	}
+
+	ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL,
+				       divider->div_flags, divider->width);
+
+	pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n",
+		 clk_hw_get_name(hw), ret_rate);
+	return ret_rate;
+}
+
+static long sg2042_clk_divider_round_rate(struct clk_hw *hw,
+					  unsigned long rate,
+					  unsigned long *prate)
+{
+	u32 bestdiv;
+	unsigned long ret_rate;
+	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
+
+	/* if read only, just return current value */
+	if (divider->div_flags & CLK_DIVIDER_READ_ONLY) {
+		if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
+			bestdiv = divider->initval;
+		} else {
+			bestdiv = readl(divider->reg) >> divider->shift;
+			bestdiv &= clk_div_mask(divider->width);
+		}
+		ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
+	} else {
+		ret_rate = divider_round_rate(hw, rate, prate, NULL,
+					      divider->width, divider->div_flags);
+	}
+
+	pr_debug("--> %s: divider_round_rate: val = %ld\n",
+		 clk_hw_get_name(hw), ret_rate);
+	return ret_rate;
+}
+
+static int sg2042_clk_divider_set_rate(struct clk_hw *hw,
+				       unsigned long rate,
+				       unsigned long parent_rate)
+{
+	u32 value;
+	u32 val, val2;
+	unsigned long flags = 0;
+	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
+
+	value = divider_get_val(rate, parent_rate, NULL,
+				divider->width, divider->div_flags);
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+	else
+		__acquire(divider->lock);
+
+	/*
+	 * The sequence of clock frequency modification is:
+	 * Assert to reset divider.
+	 * Modify the value of Clock Divide Factor (and High Wide if needed).
+	 * De-assert to restore divided clock with new frequency.
+	 */
+	val = readl(divider->reg);
+
+	/* assert */
+	val &= ~BIT(SHIFT_DIV_RESET_CTRL);
+	writel(val, divider->reg);
+
+	if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {
+		val = clk_div_mask(divider->width) << (divider->shift + 16);
+	} else {
+		val = readl(divider->reg);
+		val &= ~(clk_div_mask(divider->width) << divider->shift);
+	}
+	val |= value << divider->shift;
+	val |= BIT(SHIFT_DIV_FACTOR_SEL);
+	writel(val, divider->reg);
+	val2 = val;
+
+	/* de-assert */
+	val |= BIT(SHIFT_DIV_RESET_CTRL);
+	writel(val, divider->reg);
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+	else
+		__release(divider->lock);
+
+	pr_debug("--> %s: divider_set_rate: register val = 0x%x\n",
+		 clk_hw_get_name(hw), val2);
+	return 0;
+}
+
+static const struct clk_ops sg2042_clk_divider_ops = {
+	.recalc_rate = sg2042_clk_divider_recalc_rate,
+	.round_rate = sg2042_clk_divider_round_rate,
+	.set_rate = sg2042_clk_divider_set_rate,
+};
+
+static const struct clk_ops sg2042_clk_divider_ro_ops = {
+	.recalc_rate = sg2042_clk_divider_recalc_rate,
+	.round_rate = sg2042_clk_divider_round_rate,
+};
+
+/*
+ * Clock initialization macro naming rules:
+ * FW: use CLK_HW_INIT_FW_NAME
+ * HW: use CLK_HW_INIT_HW
+ * HWS: use CLK_HW_INIT_HWS
+ * RO: means Read-Only
+ */
+#define SG2042_PLL_FW(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
+	{								\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_pll_ops,			\
+				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.offset_status = _r_stat,				\
+		.offset_enable = _r_enable,				\
+		.shift_status_lock = 8 + (_shift),			\
+		.shift_status_updating = _shift,			\
+		.shift_enable = _shift,					\
+	}
+
+#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
+	{								\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_pll_ro_ops,			\
+				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.offset_status = _r_stat,				\
+		.offset_enable = _r_enable,				\
+		.shift_status_lock = 8 + (_shift),			\
+		.shift_status_updating = _shift,			\
+		.shift_enable = _shift,					\
+	}
+
+static struct sg2042_pll_clock sg2042_pll_clks[] = {
+	SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main",
+		      R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_MPLL_CONTROL, 0),
+	SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main",
+			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_FPLL_CONTROL, 3),
+	SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0",
+			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL0_CONTROL, 4),
+	SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1",
+			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL1_CONTROL, 5),
+};
+
+#define SG2042_DIV_FW(_id, _name, _parent,				\
+		      _r_ctrl, _shift, _width,				\
+		      _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = _div_flag,					\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_FW_RO(_id, _name, _parent,				\
+		  _r_ctrl, _shift, _width,				\
+		  _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ro_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HW(_id, _name, _parent,				\
+		      _r_ctrl, _shift, _width,				\
+		      _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_HW(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = _div_flag,					\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HW_RO(_id, _name, _parent,				\
+			 _r_ctrl, _shift, _width,			\
+			 _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_HW(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ro_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HWS(_id, _name, _parent,				\
+		       _r_ctrl, _shift, _width,				\
+		       _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_HWS(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = _div_flag,					\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\
+			  _r_ctrl, _shift, _width,			\
+			  _div_flag, _initval) {			\
+		.hw.init = CLK_HW_INIT_HWS(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ro_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
+		.initval = _initval,					\
+	}
+
+#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\
+			_r_enable, _bit_idx) {		\
+		.hw.init = CLK_HW_INIT_HWS(		\
+				_name,			\
+				_parent,		\
+				NULL,			\
+				_flags),		\
+		.id = _id,				\
+		.offset_enable = _r_enable,		\
+		.bit_idx = _bit_idx,			\
+	}
+
+#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\
+		       _r_enable, _bit_idx) {		\
+		.hw.init = CLK_HW_INIT_HW(		\
+				_name,			\
+				_parent,		\
+				NULL,			\
+				_flags),		\
+		.id = _id,				\
+		.offset_enable = _r_enable,		\
+		.bit_idx = _bit_idx,			\
+	}
+
+#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\
+		       _r_enable, _bit_idx) {		\
+		.hw.init = CLK_HW_INIT_FW_NAME(		\
+				_name,			\
+				_parent,		\
+				NULL,			\
+				_flags),		\
+		.id = _id,				\
+		.offset_enable = _r_enable,		\
+		.bit_idx = _bit_idx,			\
+	}
+
+#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \
+		.hw.init = CLK_HW_INIT_PARENTS_HW(		\
+				_name,				\
+				_parents,			\
+				NULL,				\
+				_flags),			\
+		.id = _id,					\
+		.offset_select = _r_select,			\
+		.shift = _shift,				\
+		.width = _width,				\
+	}
+
+/*
+ * Clock items in the array are sorted according to the clock-tree diagram,
+ * from top to bottom, from upstream to downstream. Read TRM for details.
+ */
+
+/* updated during probe/registration */
+static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL };
+static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL };
+static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL };
+static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL };
+static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL };
+static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL };
+static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL };
+static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL };
+
+static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = {
+	SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0",
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+		       R_CLKDIVREG27, 4),
+	SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG28, 4),
+
+	SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1",
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+		       R_CLKDIVREG29, 4),
+	SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG30, 4),
+
+	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0,
+		       "clk_gate_rp_cpu_normal_div0", "mpll",
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKDIVREG0, 4),
+	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1,
+		       "clk_gate_rp_cpu_normal_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG1, 4),
+
+	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll",
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKDIVREG25, 4),
+	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG26, 4),
+};
+
+#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO)
+
+static struct sg2042_divider_clock sg2042_div_clks_level_1[] = {
+	SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0,
+			  "clk_div_ddr01_0", clk_gate_ddr01_div0,
+			  R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1),
+	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1,
+			  "clk_div_ddr01_1", clk_gate_ddr01_div1,
+			  R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1),
+
+	SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0,
+			  "clk_div_ddr23_0", clk_gate_ddr23_div0,
+			  R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1),
+	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1,
+			  "clk_div_ddr23_1", clk_gate_ddr23_div1,
+			  R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1),
+
+	SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0,
+		       "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0,
+		       R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1),
+	SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1,
+		       "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1,
+		       R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1),
+
+	SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0,
+		       "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0,
+		       R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2),
+	SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1,
+		       "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1,
+		       R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1),
+};
+
+/*
+ * Note: regarding names for mux clock, "0/1" or "div0/div1" means the
+ * first/second parent input source, not the register value.
+ * For example:
+ * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and
+ * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",
+ * they are both controlled by register CLKDIVREG27;
+ * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and
+ * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1",
+ * they are both controlled by register CLKDIVREG28;
+ * While for register value of mux selection, use Clock Select for DDR01’s clock
+ * as example, see CLKSELREG0, bit[2].
+ * 1: Select in_dpll0_clk as clock source, correspondng to the parent input
+ *    source from "clk_div_ddr01_0".
+ * 0: Select in_fpll_clk as clock source, corresponding to the parent input
+ *    source from "clk_div_ddr01_1".
+ * So we need a table to define the array of register values corresponding to
+ * the parent index and tell CCF about this when registering mux clock.
+ */
+static const u32 sg2042_mux_table[] = {1, 0};
+
+/* Aliases just for easy reading */
+#define clk_div_ddr01_0		(&sg2042_div_clks_level_1[0].hw)
+#define clk_div_ddr01_1		(&sg2042_div_clks_level_1[1].hw)
+#define clk_div_ddr23_0		(&sg2042_div_clks_level_1[2].hw)
+#define clk_div_ddr23_1		(&sg2042_div_clks_level_1[3].hw)
+#define clk_div_rp_cpu_normal_0	(&sg2042_div_clks_level_1[4].hw)
+#define clk_div_rp_cpu_normal_1	(&sg2042_div_clks_level_1[5].hw)
+#define clk_div_axi_ddr_0	(&sg2042_div_clks_level_1[6].hw)
+#define clk_div_axi_ddr_1	(&sg2042_div_clks_level_1[7].hw)
+
+static const struct clk_hw *clk_mux_ddr01_p[] = {
+	clk_div_ddr01_0,
+	clk_div_ddr01_1,
+};
+
+static const struct clk_hw *clk_mux_ddr23_p[] = {
+	clk_div_ddr23_0,
+	clk_div_ddr23_1,
+};
+
+static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = {
+	clk_div_rp_cpu_normal_0,
+	clk_div_rp_cpu_normal_1,
+};
+
+static const struct clk_hw *clk_mux_axi_ddr_p[] = {
+	clk_div_axi_ddr_0,
+	clk_div_axi_ddr_1,
+};
+
+/* Mux clocks to be updated during probe/registration */
+static const struct clk_hw *clk_mux_ddr01[] = { NULL };
+static const struct clk_hw *clk_mux_ddr23[] = { NULL };
+static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL };
+static const struct clk_hw *clk_mux_axi_ddr[] = { NULL };
+
+static struct sg2042_mux_clock sg2042_mux_clks[] = {
+	SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
+		   R_CLKSELREG0, 2, 1),
+	SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
+		   R_CLKSELREG0, 3, 1),
+	SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+		   R_CLKSELREG0, 0, 1),
+	SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+		   R_CLKSELREG0, 1, 1),
+};
+
+/* Aliases just for easy reading */
+#define clk_div_top_rp_cmn_div2	(&sg2042_div_clks_level_2[0].hw)
+#define clk_div_50m_a53		(&sg2042_div_clks_level_2[1].hw)
+#define clk_div_timer1		(&sg2042_div_clks_level_2[2].hw)
+#define clk_div_timer2		(&sg2042_div_clks_level_2[3].hw)
+#define clk_div_timer3		(&sg2042_div_clks_level_2[4].hw)
+#define clk_div_timer4		(&sg2042_div_clks_level_2[5].hw)
+#define clk_div_timer5		(&sg2042_div_clks_level_2[6].hw)
+#define clk_div_timer6		(&sg2042_div_clks_level_2[7].hw)
+#define clk_div_timer7		(&sg2042_div_clks_level_2[8].hw)
+#define clk_div_timer8		(&sg2042_div_clks_level_2[9].hw)
+#define clk_div_uart_500m	(&sg2042_div_clks_level_2[10].hw)
+#define clk_div_ahb_lpc		(&sg2042_div_clks_level_2[11].hw)
+#define clk_div_efuse		(&sg2042_div_clks_level_2[12].hw)
+#define clk_div_tx_eth0		(&sg2042_div_clks_level_2[13].hw)
+#define clk_div_ptp_ref_i_eth0	(&sg2042_div_clks_level_2[14].hw)
+#define clk_div_ref_eth0	(&sg2042_div_clks_level_2[15].hw)
+#define clk_div_emmc		(&sg2042_div_clks_level_2[16].hw)
+#define clk_div_sd		(&sg2042_div_clks_level_2[17].hw)
+#define clk_div_top_axi0	(&sg2042_div_clks_level_2[18].hw)
+#define clk_div_100k_emmc	(&sg2042_div_clks_level_2[19].hw)
+#define clk_div_100k_sd		(&sg2042_div_clks_level_2[20].hw)
+#define clk_div_gpio_db		(&sg2042_div_clks_level_2[21].hw)
+#define clk_div_top_axi_hsperi	(&sg2042_div_clks_level_2[22].hw)
+
+static struct sg2042_divider_clock sg2042_div_clks_level_2[] = {
+	SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2,
+		       "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal,
+		       R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2),
+
+	SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll",
+		      R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20),
+	/* downstream of div_50m_a53 */
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53,
+		      R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53,
+		      R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53,
+		      R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53,
+		      R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53,
+		      R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53,
+		      R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53,
+		      R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53,
+		      R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1),
+
+	/*
+	 * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too
+	 * narrow for us to produce 115200. Use UART internal divider directly.
+	 */
+	SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll",
+			 R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2),
+	SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll",
+		      R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5),
+	SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll",
+		      R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40),
+	SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll",
+		      R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8),
+	SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0,
+		      "clk_div_ptp_ref_i_eth0", "fpll",
+		      R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20),
+	SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll",
+		      R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40),
+	SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll",
+		      R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10),
+	SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll",
+		      R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10),
+
+	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll",
+		      R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10),
+	/* downstream of div_top_axi0 */
+	SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0,
+		      R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000),
+	SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0,
+		      R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000),
+	SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0,
+		      R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000),
+
+	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI,
+		      "clk_div_top_axi_hsperi", "fpll",
+		      R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4),
+};
+
+/* Gate clocks to be updated during probe/registration */
+static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL };
+static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL };
+
+static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = {
+	SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG1, 14),
+
+	SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG1, 15),
+
+	SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL,
+			"clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG0, 0),
+
+	SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG1, 13),
+
+	/* upon are gate clocks directly downstream of muxes */
+
+	/* downstream of clk_div_top_rp_cmn_div2 */
+	SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2,
+		       "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2,
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2),
+	SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2,
+			CLK_SET_RATE_PARENT, R_CLKENREG1, 10),
+
+	/*
+	 * downstream of clk_gate_rp_cpu_normal
+	 *
+	 * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal
+	 * and clk_gate_axi_pcie0/clk_gate_axi_pcie1.
+	 * But the 1/2 DIV is fixed and no configurable register exported, so
+	 * when reading from these two clocks, the rate value are still the
+	 * same as that of clk_gate_rp_cpu_normal, it's not correct.
+	 * This just affects the value read.
+	 */
+	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0,
+			"clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,
+			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),
+	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1,
+			"clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,
+			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),
+
+	/* downstream of div_50m_a53 */
+	SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53,
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1),
+	SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 12),
+	SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 13),
+	SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 14),
+	SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 15),
+	SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 16),
+	SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 17),
+	SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 18),
+	SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 19),
+
+	/* gate clocks downstream from div clocks one-to-one */
+	SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m,
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4),
+	SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 7),
+	SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 20),
+	SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 30),
+	SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0,
+		       "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 0),
+	SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 1),
+	SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 3),
+	SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 6),
+
+	/* downstream of clk_div_top_axi0 */
+	SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0,
+		       0, R_CLKENREG0, 8),
+	SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0,
+		       0, R_CLKENREG0, 9),
+	SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0,
+		       CLK_IGNORE_UNUSED, R_CLKENREG0, 10),
+	SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0,
+		       CLK_IGNORE_UNUSED, R_CLKENREG0, 11),
+	SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0,
+		       0, R_CLKENREG0, 21),
+	SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0,
+		       0, R_CLKENREG0, 22),
+	SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR,
+		       "clk_gate_apb_gpio_intr", clk_div_top_axi0,
+		       CLK_IS_CRITICAL, R_CLKENREG0, 23),
+	SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0,
+		       0, R_CLKENREG0, 26),
+	SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0,
+		       0, R_CLKENREG0, 27),
+	SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0,
+		       0, R_CLKENREG0, 28),
+	SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0,
+		       0, R_CLKENREG0, 29),
+	SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0,
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKENREG1, 11),
+	/* downstream of DIV clocks which are sourced from clk_div_top_axi0 */
+	SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 24),
+	SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 4),
+	SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 7),
+
+	/* downstream of clk_div_top_axi_hsperi */
+	SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI,
+		       "clk_gate_sysdma_axi", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 3),
+	SG2042_GATE_HW(GATE_CLK_APB_UART,
+		       "clk_gate_apb_uart", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 5),
+	SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C,
+		       "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 6),
+	SG2042_GATE_HW(GATE_CLK_APB_SPI,
+		       "clk_gate_apb_spi", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 25),
+	SG2042_GATE_HW(GATE_CLK_AXI_ETH0,
+		       "clk_gate_axi_eth0", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 31),
+	SG2042_GATE_HW(GATE_CLK_AXI_EMMC,
+		       "clk_gate_axi_emmc", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 2),
+	SG2042_GATE_HW(GATE_CLK_AXI_SD,
+		       "clk_gate_axi_sd", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 5),
+	SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI,
+		       "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKENREG1, 12),
+};
+
+/*
+ * Gate clocks for RP subsystem (including the MP subsystem), which control
+ * registers are defined in SYS_CTRL.
+ */
+static const struct sg2042_gate_clock sg2042_gate_rp[] = {
+	/* downstream of clk_gate_rp_cpu_normal about rxu */
+	SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 0),
+	SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 1),
+	SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 2),
+	SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 3),
+	SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 4),
+	SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 5),
+	SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 6),
+	SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 7),
+	SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 8),
+	SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 9),
+	SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 10),
+	SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 11),
+	SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 12),
+	SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 13),
+	SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 14),
+	SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 15),
+	SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 16),
+	SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 17),
+	SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 18),
+	SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 19),
+	SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 20),
+	SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 21),
+	SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 22),
+	SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 23),
+	SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 24),
+	SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 25),
+	SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 26),
+	SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 27),
+	SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 28),
+	SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 29),
+	SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 30),
+	SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 31),
+
+	/* downstream of clk_gate_rp_cpu_normal about mp */
+	SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate",
+		       CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate",
+		       CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate",
+		       CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate",
+		       CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate",
+		       CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate",
+		       CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate",
+		       CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate",
+		       CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate",
+		       CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate",
+		       CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate",
+		       CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate",
+		       CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate",
+		       CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate",
+		       CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate",
+		       CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate",
+		       CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0),
+};
+
+static DEFINE_SPINLOCK(sg2042_clk_lock);
+
+static int sg2042_clk_register_plls(struct device *dev,
+				    struct sg2042_clk_data *clk_data,
+				    struct sg2042_pll_clock pll_clks[],
+				    int num_pll_clks)
+{
+	struct clk_hw *hw;
+	struct sg2042_pll_clock *pll;
+	int i, ret = 0;
+
+	for (i = 0; i < num_pll_clks; i++) {
+		pll = &pll_clks[i];
+		/* assign these for ops usage during registration */
+		pll->base = clk_data->iobase;
+		pll->lock = &sg2042_clk_lock;
+
+		hw = &pll->hw;
+		ret = devm_clk_hw_register(dev, hw);
+		if (ret) {
+			pr_err("failed to register clock %s\n", pll->hw.init->name);
+			break;
+		}
+
+		clk_data->onecell_data.hws[pll->id] = hw;
+	}
+
+	return ret;
+}
+
+static int sg2042_clk_register_divs(struct device *dev,
+				    struct sg2042_clk_data *clk_data,
+				    struct sg2042_divider_clock div_clks[],
+				    int num_div_clks)
+{
+	struct clk_hw *hw;
+	struct sg2042_divider_clock *div;
+	int i, ret = 0;
+
+	for (i = 0; i < num_div_clks; i++) {
+		div = &div_clks[i];
+
+		if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) {
+			if (div->width + div->shift > 16) {
+				pr_warn("divider value exceeds LOWORD field\n");
+				ret = -EINVAL;
+				break;
+			}
+		}
+
+		div->reg = clk_data->iobase + div->offset_ctrl;
+		div->lock = &sg2042_clk_lock;
+
+		hw = &div->hw;
+		ret = devm_clk_hw_register(dev, hw);
+		if (ret) {
+			pr_err("failed to register clock %s\n", div->hw.init->name);
+			break;
+		}
+
+		clk_data->onecell_data.hws[div->id] = hw;
+	}
+
+	return ret;
+}
+
+static int sg2042_clk_register_gates(struct device *dev,
+				     struct sg2042_clk_data *clk_data,
+				     const struct sg2042_gate_clock gate_clks[],
+				     int num_gate_clks)
+{
+	struct clk_hw *hw;
+	const struct sg2042_gate_clock *gate;
+	int i, ret = 0;
+
+	for (i = 0; i < num_gate_clks; i++) {
+		gate = &gate_clks[i];
+		hw = __devm_clk_hw_register_gate
+			(dev,
+			 NULL,
+			 gate->hw.init->name,
+			 NULL,
+			 gate->hw.init->parent_hws[0],
+			 NULL,
+			 gate->hw.init->flags,
+			 clk_data->iobase + gate->offset_enable,
+			 gate->bit_idx,
+			 0,
+			 &sg2042_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("failed to register clock %s\n", gate->hw.init->name);
+			ret = PTR_ERR(hw);
+			break;
+		}
+
+		clk_data->onecell_data.hws[gate->id] = hw;
+
+		/* Updated some clocks which take the role of parent */
+		switch (gate->id) {
+		case GATE_CLK_RP_CPU_NORMAL:
+			*clk_gate_rp_cpu_normal = hw;
+			break;
+		case GATE_CLK_TOP_RP_CMN_DIV2:
+			*clk_gate_top_rp_cmn_div2 = hw;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int sg2042_clk_register_gates_fw(struct device *dev,
+					struct sg2042_clk_data *clk_data,
+					const struct sg2042_gate_clock gate_clks[],
+					int num_gate_clks)
+{
+	struct clk_hw *hw;
+	const struct sg2042_gate_clock *gate;
+	int i, ret = 0;
+
+	for (i = 0; i < num_gate_clks; i++) {
+		gate = &gate_clks[i];
+		hw = devm_clk_hw_register_gate_parent_data
+			(dev,
+			 gate->hw.init->name,
+			 gate->hw.init->parent_data,
+			 gate->hw.init->flags,
+			 clk_data->iobase + gate->offset_enable,
+			 gate->bit_idx,
+			 0,
+			 &sg2042_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("failed to register clock %s\n", gate->hw.init->name);
+			ret = PTR_ERR(hw);
+			break;
+		}
+
+		clk_data->onecell_data.hws[gate->id] = hw;
+
+		/* Updated some clocks which take the role of parent */
+		switch (gate->id) {
+		case GATE_CLK_DDR01_DIV0:
+			*clk_gate_ddr01_div0 = hw;
+			break;
+		case GATE_CLK_DDR01_DIV1:
+			*clk_gate_ddr01_div1 = hw;
+			break;
+		case GATE_CLK_DDR23_DIV0:
+			*clk_gate_ddr23_div0 = hw;
+			break;
+		case GATE_CLK_DDR23_DIV1:
+			*clk_gate_ddr23_div1 = hw;
+			break;
+		case GATE_CLK_RP_CPU_NORMAL_DIV0:
+			*clk_gate_rp_cpu_normal_div0 = hw;
+			break;
+		case GATE_CLK_RP_CPU_NORMAL_DIV1:
+			*clk_gate_rp_cpu_normal_div1 = hw;
+			break;
+		case GATE_CLK_AXI_DDR_DIV0:
+			*clk_gate_axi_ddr_div0 = hw;
+			break;
+		case GATE_CLK_AXI_DDR_DIV1:
+			*clk_gate_axi_ddr_div1 = hw;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int sg2042_mux_notifier_cb(struct notifier_block *nb,
+				  unsigned long event,
+				  void *data)
+{
+	int ret = 0;
+	struct clk_notifier_data *ndata = data;
+	struct clk_hw *hw = __clk_get_hw(ndata->clk);
+	const struct clk_ops *ops = &clk_mux_ops;
+	struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb);
+
+	/* To switch to fpll before changing rate and restore after that */
+	if (event == PRE_RATE_CHANGE) {
+		mux->original_index = ops->get_parent(hw);
+
+		/*
+		 * "1" is the array index of the second parent input source of
+		 * mux. For SG2042, it's fpll for all mux clocks.
+		 * "0" is the array index of the frist parent input source of
+		 * mux, For SG2042, it's mpll.
+		 * FIXME, any good idea to avoid magic number?
+		 */
+		if (mux->original_index == 0)
+			ret = ops->set_parent(hw, 1);
+	} else if (event == POST_RATE_CHANGE) {
+		ret = ops->set_parent(hw, mux->original_index);
+	}
+
+	return notifier_from_errno(ret);
+}
+
+static int sg2042_clk_register_muxs(struct device *dev,
+				    struct sg2042_clk_data *clk_data,
+				    struct sg2042_mux_clock mux_clks[],
+				    int num_mux_clks)
+{
+	struct clk_hw *hw;
+	struct sg2042_mux_clock *mux;
+	int i, ret = 0;
+
+	for (i = 0; i < num_mux_clks; i++) {
+		mux = &mux_clks[i];
+
+		hw = __devm_clk_hw_register_mux
+			(dev,
+			 NULL,
+			 mux->hw.init->name,
+			 mux->hw.init->num_parents,
+			 NULL,
+			 mux->hw.init->parent_hws,
+			 NULL,
+			 mux->hw.init->flags,
+			 clk_data->iobase + mux->offset_select,
+			 mux->shift,
+			 BIT(mux->width) - 1,
+			 0,
+			 sg2042_mux_table,
+			 &sg2042_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("failed to register clock %s\n", mux->hw.init->name);
+			ret = PTR_ERR(hw);
+			break;
+		}
+
+		clk_data->onecell_data.hws[mux->id] = hw;
+
+		/* Updated some clocks which takes the role of parent */
+		switch (mux->id) {
+		case MUX_CLK_DDR01:
+			*clk_mux_ddr01 = hw;
+			break;
+		case MUX_CLK_DDR23:
+			*clk_mux_ddr23 = hw;
+			break;
+		case MUX_CLK_RP_CPU_NORMAL:
+			*clk_mux_rp_cpu_normal = hw;
+			break;
+		case MUX_CLK_AXI_DDR:
+			*clk_mux_axi_ddr = hw;
+			break;
+		}
+
+		/*
+		 * FIXME: Theoretically, we should set parent for the
+		 * mux, but seems hardware has done this for us with
+		 * default value, so we don't set parent again here.
+		 */
+
+		if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {
+			mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;
+			ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb);
+			if (ret) {
+				pr_err("failed to register clock notifier for %s\n",
+				       mux->hw.init->name);
+				break;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int sg2042_init_clkdata(struct platform_device *pdev,
+			       int num_clks,
+			       struct sg2042_clk_data **pp_clk_data)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+
+	clk_data = devm_kzalloc(&pdev->dev,
+				struct_size(clk_data, onecell_data.hws, num_clks),
+				GFP_KERNEL);
+	if (!clk_data)
+		return -ENOMEM;
+
+	clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
+	if (WARN_ON(IS_ERR(clk_data->iobase)))
+		return PTR_ERR(clk_data->iobase);
+
+	clk_data->onecell_data.num = num_clks;
+
+	*pp_clk_data = clk_data;
+
+	return 0;
+}
+
+static int sg2042_clkgen_probe(struct platform_device *pdev)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+	int ret = 0;
+	int num_clks = 0;
+
+	num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) +
+		   ARRAY_SIZE(sg2042_div_clks_level_2) +
+		   ARRAY_SIZE(sg2042_gate_clks_level_1) +
+		   ARRAY_SIZE(sg2042_gate_clks_level_2) +
+		   ARRAY_SIZE(sg2042_mux_clks);
+
+	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
+	if (ret)
+		goto error_out;
+
+	/* level-1 gates */
+	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,
+					   sg2042_gate_clks_level_1,
+					   ARRAY_SIZE(sg2042_gate_clks_level_1));
+	if (ret)
+		goto error_out;
+
+	/* level-1 div */
+	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,
+				       ARRAY_SIZE(sg2042_div_clks_level_1));
+	if (ret)
+		goto error_out;
+
+	/* mux */
+	ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,
+				       ARRAY_SIZE(sg2042_mux_clks));
+	if (ret)
+		goto error_out;
+
+	/* level 2 div */
+	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,
+				       ARRAY_SIZE(sg2042_div_clks_level_2));
+	if (ret)
+		goto error_out;
+
+	/* level 2 gate */
+	ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,
+					ARRAY_SIZE(sg2042_gate_clks_level_2));
+	if (ret)
+		goto error_out;
+
+	return devm_of_clk_add_hw_provider(&pdev->dev,
+					   of_clk_hw_onecell_get,
+					   &clk_data->onecell_data);
+
+error_out:
+	pr_err("%s failed error number %d\n", __func__, ret);
+	return ret;
+}
+
+static int sg2042_rpgate_probe(struct platform_device *pdev)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+	int ret = 0;
+	int num_clks = 0;
+
+	num_clks = ARRAY_SIZE(sg2042_gate_rp);
+
+	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
+	if (ret)
+		goto error_out;
+
+	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data, sg2042_gate_rp,
+					   num_clks);
+	if (ret)
+		goto error_out;
+
+	return devm_of_clk_add_hw_provider(&pdev->dev,
+					   of_clk_hw_onecell_get,
+					   &clk_data->onecell_data);
+
+error_out:
+	pr_err("%s failed error number %d\n", __func__, ret);
+	return ret;
+}
+
+static int sg2042_pll_probe(struct platform_device *pdev)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+	int ret = 0;
+	int num_clks = 0;
+
+	num_clks = ARRAY_SIZE(sg2042_pll_clks);
+
+	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
+	if (ret)
+		goto error_out;
+
+	ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
+				       num_clks);
+	if (ret)
+		goto error_out;
+
+	return devm_of_clk_add_hw_provider(&pdev->dev,
+					   of_clk_hw_onecell_get,
+					   &clk_data->onecell_data);
+
+error_out:
+	pr_err("%s failed error number %d\n", __func__, ret);
+	return ret;
+}
+
+static const struct of_device_id sg2042_clkgen_match[] = {
+	{ .compatible = "sophgo,sg2042-clkgen" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver sg2042_clkgen_driver = {
+	.probe = sg2042_clkgen_probe,
+	.driver = {
+		.name = "clk-sophgo-sg2042-clkgen",
+		.of_match_table = sg2042_clkgen_match,
+		.suppress_bind_attrs = true,
+	},
+};
+builtin_platform_driver(sg2042_clkgen_driver);
+
+static const struct of_device_id sg2042_rpgate_match[] = {
+	{ .compatible = "sophgo,sg2042-rpgate" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver sg2042_rpgate_driver = {
+	.probe = sg2042_rpgate_probe,
+	.driver = {
+		.name = "clk-sophgo-sg2042-rpgate",
+		.of_match_table = sg2042_rpgate_match,
+		.suppress_bind_attrs = true,
+	},
+};
+builtin_platform_driver(sg2042_rpgate_driver);
+
+static const struct of_device_id sg2042_pll_match[] = {
+	{ .compatible = "sophgo,sg2042-pll" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver sg2042_pll_driver = {
+	.probe = sg2042_pll_probe,
+	.driver = {
+		.name = "clk-sophgo-sg2042-pll",
+		.of_match_table = sg2042_pll_match,
+		.suppress_bind_attrs = true,
+	},
+};
+builtin_platform_driver(sg2042_pll_driver);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 4/5] clk: sophgo: Add SG2042 clock driver
@ 2024-04-26  3:35   ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:35 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang

From: Chen Wang <unicorn_wang@outlook.com>

Add a driver for the SOPHGO SG2042 clocks.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
---
 drivers/clk/Kconfig                    |    1 +
 drivers/clk/Makefile                   |    1 +
 drivers/clk/sophgo/Kconfig             |    8 +
 drivers/clk/sophgo/Makefile            |    2 +
 drivers/clk/sophgo/clk-sophgo-sg2042.c | 1870 ++++++++++++++++++++++++
 5 files changed, 1882 insertions(+)
 create mode 100644 drivers/clk/sophgo/Kconfig
 create mode 100644 drivers/clk/sophgo/Makefile
 create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 50af5fc7f570..bc28502ec3c9 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
 source "drivers/clk/samsung/Kconfig"
 source "drivers/clk/sifive/Kconfig"
 source "drivers/clk/socfpga/Kconfig"
+source "drivers/clk/sophgo/Kconfig"
 source "drivers/clk/sprd/Kconfig"
 source "drivers/clk/starfive/Kconfig"
 source "drivers/clk/sunxi/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 14fa8d4ecc1f..4abe16c8ccdf 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
 obj-$(CONFIG_CLK_SIFIVE)		+= sifive/
 obj-y					+= socfpga/
+obj-y					+= sophgo/
 obj-$(CONFIG_PLAT_SPEAR)		+= spear/
 obj-y					+= sprd/
 obj-$(CONFIG_ARCH_STI)			+= st/
diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig
new file mode 100644
index 000000000000..82516adb0f97
--- /dev/null
+++ b/drivers/clk/sophgo/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# common clock support for SOPHGO SoC family.
+
+config CLK_SOPHGO_SG2042
+	bool "Sophgo SG2042 clock support"
+	depends on ARCH_SOPHGO || COMPILE_TEST
+	help
+	  Say yes here to support the clock controller on the Sophgo SG2042 SoC.
diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile
new file mode 100644
index 000000000000..13834cce260c
--- /dev/null
+++ b/drivers/clk/sophgo/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CLK_SOPHGO_SG2042)	+= clk-sophgo-sg2042.o
diff --git a/drivers/clk/sophgo/clk-sophgo-sg2042.c b/drivers/clk/sophgo/clk-sophgo-sg2042.c
new file mode 100644
index 000000000000..e64e1cb4872b
--- /dev/null
+++ b/drivers/clk/sophgo/clk-sophgo-sg2042.c
@@ -0,0 +1,1870 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sophgo SG2042 Clock Generator Driver
+ *
+ * Copyright (C) 2024 Sophgo Technology Inc. All rights reserved.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <asm/div64.h>
+
+/*
+ * The clock of SG2042 is composed of three parts.
+ * The registers of these three parts of the clock are scattered in three
+ * different memory address spaces:
+ * - pll clocks
+ * - gate clocks for RP subsystem
+ * - div/mux, and gate clocks working for other subsystem than RP subsystem
+ */
+#include <dt-bindings/clock/sophgo,sg2042-pll.h>
+#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
+#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
+
+/* Registers defined in SYS_CTRL */
+#define R_PLL_BEGIN		0xC0
+#define R_PLL_STAT		(0xC0 - R_PLL_BEGIN)
+#define R_PLL_CLKEN_CONTROL	(0xC4 - R_PLL_BEGIN)
+#define R_MPLL_CONTROL		(0xE8 - R_PLL_BEGIN)
+#define R_FPLL_CONTROL		(0xF4 - R_PLL_BEGIN)
+#define R_DPLL0_CONTROL		(0xF8 - R_PLL_BEGIN)
+#define R_DPLL1_CONTROL		(0xFC - R_PLL_BEGIN)
+
+#define R_SYSGATE_BEGIN		0x0368
+#define R_RP_RXU_CLK_ENABLE	(0x0368 - R_SYSGATE_BEGIN)
+#define R_MP0_STATUS_REG	(0x0380 - R_SYSGATE_BEGIN)
+#define R_MP0_CONTROL_REG	(0x0384 - R_SYSGATE_BEGIN)
+#define R_MP1_STATUS_REG	(0x0388 - R_SYSGATE_BEGIN)
+#define R_MP1_CONTROL_REG	(0x038C - R_SYSGATE_BEGIN)
+#define R_MP2_STATUS_REG	(0x0390 - R_SYSGATE_BEGIN)
+#define R_MP2_CONTROL_REG	(0x0394 - R_SYSGATE_BEGIN)
+#define R_MP3_STATUS_REG	(0x0398 - R_SYSGATE_BEGIN)
+#define R_MP3_CONTROL_REG	(0x039C - R_SYSGATE_BEGIN)
+#define R_MP4_STATUS_REG	(0x03A0 - R_SYSGATE_BEGIN)
+#define R_MP4_CONTROL_REG	(0x03A4 - R_SYSGATE_BEGIN)
+#define R_MP5_STATUS_REG	(0x03A8 - R_SYSGATE_BEGIN)
+#define R_MP5_CONTROL_REG	(0x03AC - R_SYSGATE_BEGIN)
+#define R_MP6_STATUS_REG	(0x03B0 - R_SYSGATE_BEGIN)
+#define R_MP6_CONTROL_REG	(0x03B4 - R_SYSGATE_BEGIN)
+#define R_MP7_STATUS_REG	(0x03B8 - R_SYSGATE_BEGIN)
+#define R_MP7_CONTROL_REG	(0x03BC - R_SYSGATE_BEGIN)
+#define R_MP8_STATUS_REG	(0x03C0 - R_SYSGATE_BEGIN)
+#define R_MP8_CONTROL_REG	(0x03C4 - R_SYSGATE_BEGIN)
+#define R_MP9_STATUS_REG	(0x03C8 - R_SYSGATE_BEGIN)
+#define R_MP9_CONTROL_REG	(0x03CC - R_SYSGATE_BEGIN)
+#define R_MP10_STATUS_REG	(0x03D0 - R_SYSGATE_BEGIN)
+#define R_MP10_CONTROL_REG	(0x03D4 - R_SYSGATE_BEGIN)
+#define R_MP11_STATUS_REG	(0x03D8 - R_SYSGATE_BEGIN)
+#define R_MP11_CONTROL_REG	(0x03DC - R_SYSGATE_BEGIN)
+#define R_MP12_STATUS_REG	(0x03E0 - R_SYSGATE_BEGIN)
+#define R_MP12_CONTROL_REG	(0x03E4 - R_SYSGATE_BEGIN)
+#define R_MP13_STATUS_REG	(0x03E8 - R_SYSGATE_BEGIN)
+#define R_MP13_CONTROL_REG	(0x03EC - R_SYSGATE_BEGIN)
+#define R_MP14_STATUS_REG	(0x03F0 - R_SYSGATE_BEGIN)
+#define R_MP14_CONTROL_REG	(0x03F4 - R_SYSGATE_BEGIN)
+#define R_MP15_STATUS_REG	(0x03F8 - R_SYSGATE_BEGIN)
+#define R_MP15_CONTROL_REG	(0x03FC - R_SYSGATE_BEGIN)
+
+/* Registers defined in CLOCK */
+#define R_CLKENREG0		0x00
+#define R_CLKENREG1		0x04
+#define R_CLKSELREG0		0x20
+#define R_CLKDIVREG0		0x40
+#define R_CLKDIVREG1		0x44
+#define R_CLKDIVREG2		0x48
+#define R_CLKDIVREG3		0x4C
+#define R_CLKDIVREG4		0x50
+#define R_CLKDIVREG5		0x54
+#define R_CLKDIVREG6		0x58
+#define R_CLKDIVREG7		0x5C
+#define R_CLKDIVREG8		0x60
+#define R_CLKDIVREG9		0x64
+#define R_CLKDIVREG10		0x68
+#define R_CLKDIVREG11		0x6C
+#define R_CLKDIVREG12		0x70
+#define R_CLKDIVREG13		0x74
+#define R_CLKDIVREG14		0x78
+#define R_CLKDIVREG15		0x7C
+#define R_CLKDIVREG16		0x80
+#define R_CLKDIVREG17		0x84
+#define R_CLKDIVREG18		0x88
+#define R_CLKDIVREG19		0x8C
+#define R_CLKDIVREG20		0x90
+#define R_CLKDIVREG21		0x94
+#define R_CLKDIVREG22		0x98
+#define R_CLKDIVREG23		0x9C
+#define R_CLKDIVREG24		0xA0
+#define R_CLKDIVREG25		0xA4
+#define R_CLKDIVREG26		0xA8
+#define R_CLKDIVREG27		0xAC
+#define R_CLKDIVREG28		0xB0
+#define R_CLKDIVREG29		0xB4
+#define R_CLKDIVREG30		0xB8
+
+/* All following shift value are the same for all DIV registers */
+#define SHIFT_DIV_RESET_CTRL	0
+#define SHIFT_DIV_FACTOR_SEL	3
+#define SHIFT_DIV_FACTOR	16
+
+/**
+ * struct sg2042_clk_data - Common data of clock-controller
+ * @iobase: base address of clock-controller
+ * @onecell_data: used for adding providers.
+ *
+ * Note: this structure will be used both by clkgen & sysclk.
+ */
+struct sg2042_clk_data {
+	void __iomem *iobase;
+	struct clk_hw_onecell_data onecell_data;
+};
+
+/**
+ * struct sg2042_pll_clock - PLL clock
+ * @hw:				clk_hw for initialization
+ * @id:				used to map clk_onecell_data
+ * @base:			used for readl/writel.
+ *				**NOTE**: PLL registers are all in SYS_CTRL!
+ * @lock:			spinlock to protect register access, modification
+ *				of frequency can only be served one at the time.
+ * @offset_status:		offset of pll status registers
+ * @offset_enable:		offset of pll enable registers
+ * @offset_ctrl:		offset of pll control registers
+ * @shift_status_lock:		shift of XXX_LOCK in pll status register
+ * @shift_status_updating:	shift of UPDATING_XXX in pll status register
+ * @shift_enable:		shift of XXX_CLK_EN in pll enable register
+ */
+struct sg2042_pll_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+	void __iomem *base;
+	/* protect register access */
+	spinlock_t *lock;
+
+	u32 offset_status;
+	u32 offset_enable;
+	u32 offset_ctrl;
+	u8 shift_status_lock;
+	u8 shift_status_updating;
+	u8 shift_enable;
+};
+
+#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
+
+/**
+ * struct sg2042_divider_clock - Divider clock
+ * @hw:			clk_hw for initialization
+ * @id:			used to map clk_onecell_data
+ * @reg:		used for readl/writel.
+ *			**NOTE**: DIV registers are ALL in CLOCK!
+ * @lock:		spinlock to protect register access, modification of
+ *			frequency can only be served one at the time
+ * @offset_ctrl:	offset of divider control registers
+ * @shift:		shift of "Clock Divider Factor" in divider control register
+ * @width:		width of "Clock Divider Factor" in divider control register
+ * @div_flags:		private flags for this clock, not for framework-specific
+ * @initval:		In the divider control register, we can configure whether
+ *			to use the value of "Clock Divider Factor" or just use
+ *			the initial value pre-configured by IC. BIT[3] controls
+ *			this and by default (value is 0), means initial value
+ *			is used.
+ *			**NOTE** that we cannot read the initial value (default
+ *			value when poweron) and default value of "Clock Divider
+ *			Factor" is zero, which I think is a hardware design flaw
+ *			and should be sync-ed with the initial value. So in
+ *			software we have to add a configuration item (initval)
+ *			to manually configure this value and use it when BIT[3]
+ *			is zero.
+ */
+struct sg2042_divider_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+
+	void __iomem *reg;
+	/* protect register access */
+	spinlock_t *lock;
+
+	u32 offset_ctrl;
+	u8 shift;
+	u8 width;
+	u8 div_flags;
+	u32 initval;
+};
+
+#define to_sg2042_clk_divider(_hw)	\
+	container_of(_hw, struct sg2042_divider_clock, hw)
+
+/**
+ * struct sg2042_gate_clock - Gate clock
+ * @hw:			clk_hw for initialization
+ * @id:			used to map clk_onecell_data
+ * @offset_enable:	offset of gate enable registers
+ * @bit_idx:		which bit in the register controls gating of this clock
+ */
+struct sg2042_gate_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+
+	u32 offset_enable;
+	u8 bit_idx;
+};
+
+/**
+ * struct sg2042_mux_clock - Mux clock
+ * @hw:			clk_hw for initialization
+ * @id:			used to map clk_onecell_data
+ * @offset_select:	offset of mux selection registers
+ *			**NOTE**: MUX registers are ALL in CLOCK!
+ * @shift:		shift of "Clock Select" in mux selection register
+ * @width:		width of "Clock Select" in mux selection register
+ * @clk_nb:		used for notification
+ * @original_index:	set by notifier callback
+ */
+struct sg2042_mux_clock {
+	struct clk_hw hw;
+
+	unsigned int id;
+
+	u32 offset_select;
+	u8 shift;
+	u8 width;
+
+	struct notifier_block clk_nb;
+	u8 original_index;
+};
+
+#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb)
+
+#define KHZ 1000UL
+#define MHZ (KHZ * KHZ)
+
+#define REFDIV_MIN 1
+#define REFDIV_MAX 63
+#define FBDIV_MIN 16
+#define FBDIV_MAX 320
+
+#define PLL_FREF_SG2042 (25 * MHZ)
+
+#define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
+#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
+
+#define PLL_FOUTVCO_MIN (800 * MHZ)
+#define PLL_FOUTVCO_MAX (3200 * MHZ)
+
+struct sg2042_pll_ctrl {
+	unsigned long freq;
+	unsigned int fbdiv;
+	unsigned int postdiv1;
+	unsigned int postdiv2;
+	unsigned int refdiv;
+};
+
+#define PLLCTRL_FBDIV_SHIFT	16
+#define PLLCTRL_FBDIV_MASK	(GENMASK(27, 16) >> PLLCTRL_FBDIV_SHIFT)
+#define PLLCTRL_POSTDIV2_SHIFT	12
+#define PLLCTRL_POSTDIV2_MASK	(GENMASK(14, 12) >> PLLCTRL_POSTDIV2_SHIFT)
+#define PLLCTRL_POSTDIV1_SHIFT	8
+#define PLLCTRL_POSTDIV1_MASK	(GENMASK(10, 8) >> PLLCTRL_POSTDIV1_SHIFT)
+#define PLLCTRL_REFDIV_SHIFT	0
+#define PLLCTRL_REFDIV_MASK	(GENMASK(5, 0) >> PLLCTRL_REFDIV_SHIFT)
+
+static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
+{
+	return ((ctrl->fbdiv & PLLCTRL_FBDIV_MASK) << PLLCTRL_FBDIV_SHIFT) |
+	       ((ctrl->postdiv2 & PLLCTRL_POSTDIV2_MASK) << PLLCTRL_POSTDIV2_SHIFT) |
+	       ((ctrl->postdiv1 & PLLCTRL_POSTDIV1_MASK) << PLLCTRL_POSTDIV1_SHIFT) |
+	       ((ctrl->refdiv & PLLCTRL_REFDIV_MASK) << PLLCTRL_REFDIV_SHIFT);
+}
+
+static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
+					  struct sg2042_pll_ctrl *ctrl)
+{
+	ctrl->fbdiv = (reg_value >> PLLCTRL_FBDIV_SHIFT) & PLLCTRL_FBDIV_MASK;
+	ctrl->refdiv = (reg_value >> PLLCTRL_REFDIV_SHIFT) & PLLCTRL_REFDIV_MASK;
+	ctrl->postdiv1 = (reg_value >> PLLCTRL_POSTDIV1_SHIFT) & PLLCTRL_POSTDIV1_MASK;
+	ctrl->postdiv2 = (reg_value >> PLLCTRL_POSTDIV2_SHIFT) & PLLCTRL_POSTDIV2_MASK;
+}
+
+static inline int sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
+{
+	u32 value;
+
+	if (en) {
+		/* wait pll lock */
+		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
+					      value,
+					      ((value >> pll->shift_status_lock) & 0x1),
+					      0,
+					      100000))
+			pr_warn("%s not locked\n", pll->hw.init->name);
+
+		/* wait pll updating */
+		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
+					      value,
+					      !((value >> pll->shift_status_updating) & 0x1),
+					      0,
+					      100000))
+			pr_warn("%s still updating\n", pll->hw.init->name);
+
+		/* enable pll */
+		value = readl(pll->base + pll->offset_enable);
+		writel(value | (1 << pll->shift_enable), pll->base + pll->offset_enable);
+	} else {
+		/* disable pll */
+		value = readl(pll->base + pll->offset_enable);
+		writel(value & (~(1 << pll->shift_enable)), pll->base + pll->offset_enable);
+	}
+
+	return 0;
+}
+
+/**
+ * sg2042_pll_recalc_rate() - Calculate rate for plls
+ * @reg_value: current register value
+ * @parent_rate: parent frequency
+ *
+ * This function is used to calculate below "rate" in equation
+ * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
+ *      = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
+ *
+ * Return: The rate calculated.
+ */
+static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
+					    unsigned long parent_rate)
+{
+	struct sg2042_pll_ctrl ctrl_table;
+	u64 numerator, denominator;
+
+	sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
+
+	numerator = parent_rate * ctrl_table.fbdiv;
+	denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
+	do_div(numerator, denominator);
+	return numerator;
+}
+
+/**
+ * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
+ * look up the postdiv1_2 table to get the closest postdiiv combination.
+ * @rate: FOUTPOSTDIV
+ * @prate: parent rate, i.e. FREF
+ * @fbdiv: FBDIV
+ * @refdiv: REFDIV
+ * @postdiv1: POSTDIV1, output
+ * @postdiv2: POSTDIV2, output
+ *
+ * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
+ * for example:
+ * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
+ *
+ * See TRM:
+ * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
+ * So we get following formula to get POSTDIV1 and POSTDIV2:
+ * POSTDIV = (prate/REFDIV) x FBDIV/rate
+ * above POSTDIV = POSTDIV1*POSTDIV2
+ *
+ * Return:
+ * %0 - OK
+ * %-EINVAL - invalid argument, which means Failed to get the postdivs.
+ */
+static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
+				      unsigned long prate,
+				      unsigned int fbdiv,
+				      unsigned int refdiv,
+				      unsigned int *postdiv1,
+				      unsigned int *postdiv2)
+{
+	int index;
+	u64 tmp0;
+
+	/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
+	#define	POSTDIV_RESULT_INDEX	2
+
+	static const int postdiv1_2[][3] = {
+		{2, 4,  8}, {3, 3,  9}, {2, 5, 10}, {2, 6, 12},
+		{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
+		{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
+		{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
+		{6, 7, 42}, {7, 7, 49}
+	};
+
+	/* prate/REFDIV and result save to tmp0 */
+	tmp0 = prate;
+	do_div(tmp0, refdiv);
+
+	/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
+	tmp0 *= fbdiv;
+
+	/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
+	do_div(tmp0, rate);
+
+	/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
+	if (tmp0 <= 7) {
+		/* (div1 * div2) <= 7, no need to use array search */
+		*postdiv1 = tmp0;
+		*postdiv2 = 1;
+		return 0;
+	}
+
+	/* (div1 * div2) > 7, use array search */
+	for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
+		if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
+			continue;
+		} else {
+			/* found it */
+			*postdiv1 = postdiv1_2[index][1];
+			*postdiv2 = postdiv1_2[index][0];
+			return 0;
+		}
+	}
+	pr_warn("%s can not find in postdiv array!\n", __func__);
+	return -EINVAL;
+}
+
+/**
+ * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
+ * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
+ * register.
+ * @req_rate: expected output clock rate, i.e. FOUTPISTDIV
+ * @parent_rate: input parent clock rate, i.e. FREF
+ * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
+ *
+ * Return:
+ * %0 - OK
+ * %-EINVAL - invalid argument
+ */
+static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
+				      unsigned long req_rate,
+				      unsigned long parent_rate)
+{
+	int ret;
+	unsigned int fbdiv, refdiv, postdiv1, postdiv2;
+	unsigned long foutpostdiv;
+	u64 tmp;
+	u64 foutvco;
+
+	if (parent_rate != PLL_FREF_SG2042) {
+		pr_err("INVALID FREF: %ld\n", parent_rate);
+		return -EINVAL;
+	}
+
+	if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
+		pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
+		return -EINVAL;
+	}
+
+	memset(best, 0, sizeof(struct sg2042_pll_ctrl));
+
+	for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
+		/* required by hardware: FREF/REFDIV must > 10 */
+		tmp = parent_rate;
+		do_div(tmp, refdiv);
+		if (tmp <= 10)
+			continue;
+
+		for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
+			/*
+			 * FOUTVCO = FREF*FBDIV/REFDIV validation
+			 * required by hardware, FOUTVCO must [800MHz, 3200MHz]
+			 */
+			foutvco = parent_rate * fbdiv;
+			do_div(foutvco, refdiv);
+			if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
+				continue;
+
+			ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
+							 fbdiv, refdiv,
+							 &postdiv1, &postdiv2);
+			if (ret)
+				continue;
+
+			/*
+			 * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
+			 *             = FOUTVCO/(POSTDIV1*POSTDIV2)
+			 */
+			tmp = foutvco;
+			do_div(tmp, (postdiv1 * postdiv2));
+			foutpostdiv = (unsigned long)tmp;
+			/* Iterative to approach the expected value */
+			if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
+				best->freq = foutpostdiv;
+				best->refdiv = refdiv;
+				best->fbdiv = fbdiv;
+				best->postdiv1 = postdiv1;
+				best->postdiv2 = postdiv2;
+				if (foutpostdiv == req_rate)
+					return 0;
+			}
+			continue;
+		}
+	}
+
+	if (best->freq == 0)
+		return -EINVAL;
+	else
+		return 0;
+}
+
+/**
+ * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
+ * @hw: ccf use to hook get sg2042_pll_clock
+ * @parent_rate: parent rate
+ *
+ * The is function will be called through clk_get_rate
+ * and return current rate after decoding reg value
+ *
+ * Return: Current rate recalculated.
+ */
+static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	u32 value;
+	unsigned long rate;
+	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
+
+	value = readl(pll->base + pll->offset_ctrl);
+	rate = sg2042_pll_recalc_rate(value, parent_rate);
+
+	pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
+		 clk_hw_get_name(hw), rate);
+	return rate;
+}
+
+static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
+				      unsigned long req_rate,
+				      unsigned long *prate)
+{
+	unsigned int value;
+	struct sg2042_pll_ctrl pctrl_table;
+	long proper_rate;
+	int ret;
+
+	ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
+	if (ret) {
+		proper_rate = 0;
+		goto out;
+	}
+
+	value = sg2042_pll_ctrl_encode(&pctrl_table);
+	proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
+
+out:
+	pr_debug("--> %s: pll_round_rate: val = %ld\n",
+		 clk_hw_get_name(hw), proper_rate);
+	return proper_rate;
+}
+
+static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
+					 struct clk_rate_request *req)
+{
+	req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
+					      &req->best_parent_rate);
+	pr_debug("--> %s: pll_determine_rate: val = %ld\n",
+		 clk_hw_get_name(hw), req->rate);
+	return 0;
+}
+
+static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
+				   unsigned long rate,
+				   unsigned long parent_rate)
+{
+	unsigned long flags;
+	u32 value;
+	int ret = 0;
+	struct sg2042_pll_ctrl pctrl_table;
+	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
+
+	spin_lock_irqsave(pll->lock, flags);
+	if (sg2042_pll_enable(pll, 0)) {
+		pr_warn("Can't disable pll(%s), status error\n", pll->hw.init->name);
+		goto out;
+	}
+	ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
+	if (ret) {
+		pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
+		goto out2;
+	}
+
+	value = sg2042_pll_ctrl_encode(&pctrl_table);
+
+	/* write the value to top register */
+	writel(value, pll->base + pll->offset_ctrl);
+
+out2:
+	sg2042_pll_enable(pll, 1);
+out:
+	spin_unlock_irqrestore(pll->lock, flags);
+
+	pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
+		 clk_hw_get_name(hw), value);
+	return ret;
+}
+
+static const struct clk_ops sg2042_clk_pll_ops = {
+	.recalc_rate = sg2042_clk_pll_recalc_rate,
+	.round_rate = sg2042_clk_pll_round_rate,
+	.determine_rate = sg2042_clk_pll_determine_rate,
+	.set_rate = sg2042_clk_pll_set_rate,
+};
+
+static const struct clk_ops sg2042_clk_pll_ro_ops = {
+	.recalc_rate = sg2042_clk_pll_recalc_rate,
+	.round_rate = sg2042_clk_pll_round_rate,
+};
+
+static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,
+						    unsigned long parent_rate)
+{
+	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
+	u32 val;
+	unsigned long ret_rate;
+
+	if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
+		val = divider->initval;
+	} else {
+		val = readl(divider->reg) >> divider->shift;
+		val &= clk_div_mask(divider->width);
+	}
+
+	ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL,
+				       divider->div_flags, divider->width);
+
+	pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n",
+		 clk_hw_get_name(hw), ret_rate);
+	return ret_rate;
+}
+
+static long sg2042_clk_divider_round_rate(struct clk_hw *hw,
+					  unsigned long rate,
+					  unsigned long *prate)
+{
+	u32 bestdiv;
+	unsigned long ret_rate;
+	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
+
+	/* if read only, just return current value */
+	if (divider->div_flags & CLK_DIVIDER_READ_ONLY) {
+		if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
+			bestdiv = divider->initval;
+		} else {
+			bestdiv = readl(divider->reg) >> divider->shift;
+			bestdiv &= clk_div_mask(divider->width);
+		}
+		ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
+	} else {
+		ret_rate = divider_round_rate(hw, rate, prate, NULL,
+					      divider->width, divider->div_flags);
+	}
+
+	pr_debug("--> %s: divider_round_rate: val = %ld\n",
+		 clk_hw_get_name(hw), ret_rate);
+	return ret_rate;
+}
+
+static int sg2042_clk_divider_set_rate(struct clk_hw *hw,
+				       unsigned long rate,
+				       unsigned long parent_rate)
+{
+	u32 value;
+	u32 val, val2;
+	unsigned long flags = 0;
+	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
+
+	value = divider_get_val(rate, parent_rate, NULL,
+				divider->width, divider->div_flags);
+
+	if (divider->lock)
+		spin_lock_irqsave(divider->lock, flags);
+	else
+		__acquire(divider->lock);
+
+	/*
+	 * The sequence of clock frequency modification is:
+	 * Assert to reset divider.
+	 * Modify the value of Clock Divide Factor (and High Wide if needed).
+	 * De-assert to restore divided clock with new frequency.
+	 */
+	val = readl(divider->reg);
+
+	/* assert */
+	val &= ~BIT(SHIFT_DIV_RESET_CTRL);
+	writel(val, divider->reg);
+
+	if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {
+		val = clk_div_mask(divider->width) << (divider->shift + 16);
+	} else {
+		val = readl(divider->reg);
+		val &= ~(clk_div_mask(divider->width) << divider->shift);
+	}
+	val |= value << divider->shift;
+	val |= BIT(SHIFT_DIV_FACTOR_SEL);
+	writel(val, divider->reg);
+	val2 = val;
+
+	/* de-assert */
+	val |= BIT(SHIFT_DIV_RESET_CTRL);
+	writel(val, divider->reg);
+
+	if (divider->lock)
+		spin_unlock_irqrestore(divider->lock, flags);
+	else
+		__release(divider->lock);
+
+	pr_debug("--> %s: divider_set_rate: register val = 0x%x\n",
+		 clk_hw_get_name(hw), val2);
+	return 0;
+}
+
+static const struct clk_ops sg2042_clk_divider_ops = {
+	.recalc_rate = sg2042_clk_divider_recalc_rate,
+	.round_rate = sg2042_clk_divider_round_rate,
+	.set_rate = sg2042_clk_divider_set_rate,
+};
+
+static const struct clk_ops sg2042_clk_divider_ro_ops = {
+	.recalc_rate = sg2042_clk_divider_recalc_rate,
+	.round_rate = sg2042_clk_divider_round_rate,
+};
+
+/*
+ * Clock initialization macro naming rules:
+ * FW: use CLK_HW_INIT_FW_NAME
+ * HW: use CLK_HW_INIT_HW
+ * HWS: use CLK_HW_INIT_HWS
+ * RO: means Read-Only
+ */
+#define SG2042_PLL_FW(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
+	{								\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_pll_ops,			\
+				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.offset_status = _r_stat,				\
+		.offset_enable = _r_enable,				\
+		.shift_status_lock = 8 + (_shift),			\
+		.shift_status_updating = _shift,			\
+		.shift_enable = _shift,					\
+	}
+
+#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
+	{								\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_pll_ro_ops,			\
+				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.offset_status = _r_stat,				\
+		.offset_enable = _r_enable,				\
+		.shift_status_lock = 8 + (_shift),			\
+		.shift_status_updating = _shift,			\
+		.shift_enable = _shift,					\
+	}
+
+static struct sg2042_pll_clock sg2042_pll_clks[] = {
+	SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main",
+		      R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_MPLL_CONTROL, 0),
+	SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main",
+			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_FPLL_CONTROL, 3),
+	SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0",
+			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL0_CONTROL, 4),
+	SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1",
+			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL1_CONTROL, 5),
+};
+
+#define SG2042_DIV_FW(_id, _name, _parent,				\
+		      _r_ctrl, _shift, _width,				\
+		      _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = _div_flag,					\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_FW_RO(_id, _name, _parent,				\
+		  _r_ctrl, _shift, _width,				\
+		  _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_FW_NAME(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ro_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HW(_id, _name, _parent,				\
+		      _r_ctrl, _shift, _width,				\
+		      _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_HW(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = _div_flag,					\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HW_RO(_id, _name, _parent,				\
+			 _r_ctrl, _shift, _width,			\
+			 _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_HW(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ro_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HWS(_id, _name, _parent,				\
+		       _r_ctrl, _shift, _width,				\
+		       _div_flag, _initval) {				\
+		.hw.init = CLK_HW_INIT_HWS(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = _div_flag,					\
+		.initval = _initval,					\
+	}
+
+#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\
+			  _r_ctrl, _shift, _width,			\
+			  _div_flag, _initval) {			\
+		.hw.init = CLK_HW_INIT_HWS(				\
+				_name,					\
+				_parent,				\
+				&sg2042_clk_divider_ro_ops,		\
+				0),					\
+		.id = _id,						\
+		.offset_ctrl = _r_ctrl,					\
+		.shift = _shift,					\
+		.width = _width,					\
+		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
+		.initval = _initval,					\
+	}
+
+#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\
+			_r_enable, _bit_idx) {		\
+		.hw.init = CLK_HW_INIT_HWS(		\
+				_name,			\
+				_parent,		\
+				NULL,			\
+				_flags),		\
+		.id = _id,				\
+		.offset_enable = _r_enable,		\
+		.bit_idx = _bit_idx,			\
+	}
+
+#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\
+		       _r_enable, _bit_idx) {		\
+		.hw.init = CLK_HW_INIT_HW(		\
+				_name,			\
+				_parent,		\
+				NULL,			\
+				_flags),		\
+		.id = _id,				\
+		.offset_enable = _r_enable,		\
+		.bit_idx = _bit_idx,			\
+	}
+
+#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\
+		       _r_enable, _bit_idx) {		\
+		.hw.init = CLK_HW_INIT_FW_NAME(		\
+				_name,			\
+				_parent,		\
+				NULL,			\
+				_flags),		\
+		.id = _id,				\
+		.offset_enable = _r_enable,		\
+		.bit_idx = _bit_idx,			\
+	}
+
+#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \
+		.hw.init = CLK_HW_INIT_PARENTS_HW(		\
+				_name,				\
+				_parents,			\
+				NULL,				\
+				_flags),			\
+		.id = _id,					\
+		.offset_select = _r_select,			\
+		.shift = _shift,				\
+		.width = _width,				\
+	}
+
+/*
+ * Clock items in the array are sorted according to the clock-tree diagram,
+ * from top to bottom, from upstream to downstream. Read TRM for details.
+ */
+
+/* updated during probe/registration */
+static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL };
+static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL };
+static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL };
+static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL };
+static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL };
+static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL };
+static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL };
+static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL };
+
+static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = {
+	SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0",
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+		       R_CLKDIVREG27, 4),
+	SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG28, 4),
+
+	SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1",
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+		       R_CLKDIVREG29, 4),
+	SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG30, 4),
+
+	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0,
+		       "clk_gate_rp_cpu_normal_div0", "mpll",
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKDIVREG0, 4),
+	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1,
+		       "clk_gate_rp_cpu_normal_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG1, 4),
+
+	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll",
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKDIVREG25, 4),
+	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll",
+		       CLK_IS_CRITICAL,
+		       R_CLKDIVREG26, 4),
+};
+
+#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO)
+
+static struct sg2042_divider_clock sg2042_div_clks_level_1[] = {
+	SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0,
+			  "clk_div_ddr01_0", clk_gate_ddr01_div0,
+			  R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1),
+	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1,
+			  "clk_div_ddr01_1", clk_gate_ddr01_div1,
+			  R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1),
+
+	SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0,
+			  "clk_div_ddr23_0", clk_gate_ddr23_div0,
+			  R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1),
+	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1,
+			  "clk_div_ddr23_1", clk_gate_ddr23_div1,
+			  R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1),
+
+	SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0,
+		       "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0,
+		       R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1),
+	SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1,
+		       "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1,
+		       R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1),
+
+	SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0,
+		       "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0,
+		       R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2),
+	SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1,
+		       "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1,
+		       R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1),
+};
+
+/*
+ * Note: regarding names for mux clock, "0/1" or "div0/div1" means the
+ * first/second parent input source, not the register value.
+ * For example:
+ * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and
+ * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",
+ * they are both controlled by register CLKDIVREG27;
+ * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and
+ * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1",
+ * they are both controlled by register CLKDIVREG28;
+ * While for register value of mux selection, use Clock Select for DDR01’s clock
+ * as example, see CLKSELREG0, bit[2].
+ * 1: Select in_dpll0_clk as clock source, correspondng to the parent input
+ *    source from "clk_div_ddr01_0".
+ * 0: Select in_fpll_clk as clock source, corresponding to the parent input
+ *    source from "clk_div_ddr01_1".
+ * So we need a table to define the array of register values corresponding to
+ * the parent index and tell CCF about this when registering mux clock.
+ */
+static const u32 sg2042_mux_table[] = {1, 0};
+
+/* Aliases just for easy reading */
+#define clk_div_ddr01_0		(&sg2042_div_clks_level_1[0].hw)
+#define clk_div_ddr01_1		(&sg2042_div_clks_level_1[1].hw)
+#define clk_div_ddr23_0		(&sg2042_div_clks_level_1[2].hw)
+#define clk_div_ddr23_1		(&sg2042_div_clks_level_1[3].hw)
+#define clk_div_rp_cpu_normal_0	(&sg2042_div_clks_level_1[4].hw)
+#define clk_div_rp_cpu_normal_1	(&sg2042_div_clks_level_1[5].hw)
+#define clk_div_axi_ddr_0	(&sg2042_div_clks_level_1[6].hw)
+#define clk_div_axi_ddr_1	(&sg2042_div_clks_level_1[7].hw)
+
+static const struct clk_hw *clk_mux_ddr01_p[] = {
+	clk_div_ddr01_0,
+	clk_div_ddr01_1,
+};
+
+static const struct clk_hw *clk_mux_ddr23_p[] = {
+	clk_div_ddr23_0,
+	clk_div_ddr23_1,
+};
+
+static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = {
+	clk_div_rp_cpu_normal_0,
+	clk_div_rp_cpu_normal_1,
+};
+
+static const struct clk_hw *clk_mux_axi_ddr_p[] = {
+	clk_div_axi_ddr_0,
+	clk_div_axi_ddr_1,
+};
+
+/* Mux clocks to be updated during probe/registration */
+static const struct clk_hw *clk_mux_ddr01[] = { NULL };
+static const struct clk_hw *clk_mux_ddr23[] = { NULL };
+static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL };
+static const struct clk_hw *clk_mux_axi_ddr[] = { NULL };
+
+static struct sg2042_mux_clock sg2042_mux_clks[] = {
+	SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
+		   R_CLKSELREG0, 2, 1),
+	SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
+		   R_CLKSELREG0, 3, 1),
+	SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+		   R_CLKSELREG0, 0, 1),
+	SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p,
+		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+		   R_CLKSELREG0, 1, 1),
+};
+
+/* Aliases just for easy reading */
+#define clk_div_top_rp_cmn_div2	(&sg2042_div_clks_level_2[0].hw)
+#define clk_div_50m_a53		(&sg2042_div_clks_level_2[1].hw)
+#define clk_div_timer1		(&sg2042_div_clks_level_2[2].hw)
+#define clk_div_timer2		(&sg2042_div_clks_level_2[3].hw)
+#define clk_div_timer3		(&sg2042_div_clks_level_2[4].hw)
+#define clk_div_timer4		(&sg2042_div_clks_level_2[5].hw)
+#define clk_div_timer5		(&sg2042_div_clks_level_2[6].hw)
+#define clk_div_timer6		(&sg2042_div_clks_level_2[7].hw)
+#define clk_div_timer7		(&sg2042_div_clks_level_2[8].hw)
+#define clk_div_timer8		(&sg2042_div_clks_level_2[9].hw)
+#define clk_div_uart_500m	(&sg2042_div_clks_level_2[10].hw)
+#define clk_div_ahb_lpc		(&sg2042_div_clks_level_2[11].hw)
+#define clk_div_efuse		(&sg2042_div_clks_level_2[12].hw)
+#define clk_div_tx_eth0		(&sg2042_div_clks_level_2[13].hw)
+#define clk_div_ptp_ref_i_eth0	(&sg2042_div_clks_level_2[14].hw)
+#define clk_div_ref_eth0	(&sg2042_div_clks_level_2[15].hw)
+#define clk_div_emmc		(&sg2042_div_clks_level_2[16].hw)
+#define clk_div_sd		(&sg2042_div_clks_level_2[17].hw)
+#define clk_div_top_axi0	(&sg2042_div_clks_level_2[18].hw)
+#define clk_div_100k_emmc	(&sg2042_div_clks_level_2[19].hw)
+#define clk_div_100k_sd		(&sg2042_div_clks_level_2[20].hw)
+#define clk_div_gpio_db		(&sg2042_div_clks_level_2[21].hw)
+#define clk_div_top_axi_hsperi	(&sg2042_div_clks_level_2[22].hw)
+
+static struct sg2042_divider_clock sg2042_div_clks_level_2[] = {
+	SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2,
+		       "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal,
+		       R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2),
+
+	SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll",
+		      R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20),
+	/* downstream of div_50m_a53 */
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53,
+		      R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53,
+		      R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53,
+		      R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53,
+		      R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53,
+		      R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53,
+		      R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53,
+		      R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1),
+	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53,
+		      R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1),
+
+	/*
+	 * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too
+	 * narrow for us to produce 115200. Use UART internal divider directly.
+	 */
+	SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll",
+			 R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2),
+	SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll",
+		      R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5),
+	SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll",
+		      R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40),
+	SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll",
+		      R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8),
+	SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0,
+		      "clk_div_ptp_ref_i_eth0", "fpll",
+		      R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20),
+	SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll",
+		      R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40),
+	SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll",
+		      R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10),
+	SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll",
+		      R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10),
+
+	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll",
+		      R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10),
+	/* downstream of div_top_axi0 */
+	SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0,
+		      R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000),
+	SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0,
+		      R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000),
+	SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0,
+		      R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000),
+
+	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI,
+		      "clk_div_top_axi_hsperi", "fpll",
+		      R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4),
+};
+
+/* Gate clocks to be updated during probe/registration */
+static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL };
+static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL };
+
+static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = {
+	SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG1, 14),
+
+	SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG1, 15),
+
+	SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL,
+			"clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG0, 0),
+
+	SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr,
+			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			R_CLKENREG1, 13),
+
+	/* upon are gate clocks directly downstream of muxes */
+
+	/* downstream of clk_div_top_rp_cmn_div2 */
+	SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2,
+		       "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2,
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2),
+	SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2,
+			CLK_SET_RATE_PARENT, R_CLKENREG1, 10),
+
+	/*
+	 * downstream of clk_gate_rp_cpu_normal
+	 *
+	 * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal
+	 * and clk_gate_axi_pcie0/clk_gate_axi_pcie1.
+	 * But the 1/2 DIV is fixed and no configurable register exported, so
+	 * when reading from these two clocks, the rate value are still the
+	 * same as that of clk_gate_rp_cpu_normal, it's not correct.
+	 * This just affects the value read.
+	 */
+	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0,
+			"clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,
+			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),
+	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1,
+			"clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,
+			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),
+
+	/* downstream of div_50m_a53 */
+	SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53,
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1),
+	SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 12),
+	SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 13),
+	SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 14),
+	SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 15),
+	SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 16),
+	SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 17),
+	SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 18),
+	SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 19),
+
+	/* gate clocks downstream from div clocks one-to-one */
+	SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m,
+		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4),
+	SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 7),
+	SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 20),
+	SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 30),
+	SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0,
+		       "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 0),
+	SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 1),
+	SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 3),
+	SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 6),
+
+	/* downstream of clk_div_top_axi0 */
+	SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0,
+		       0, R_CLKENREG0, 8),
+	SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0,
+		       0, R_CLKENREG0, 9),
+	SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0,
+		       CLK_IGNORE_UNUSED, R_CLKENREG0, 10),
+	SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0,
+		       CLK_IGNORE_UNUSED, R_CLKENREG0, 11),
+	SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0,
+		       0, R_CLKENREG0, 21),
+	SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0,
+		       0, R_CLKENREG0, 22),
+	SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR,
+		       "clk_gate_apb_gpio_intr", clk_div_top_axi0,
+		       CLK_IS_CRITICAL, R_CLKENREG0, 23),
+	SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0,
+		       0, R_CLKENREG0, 26),
+	SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0,
+		       0, R_CLKENREG0, 27),
+	SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0,
+		       0, R_CLKENREG0, 28),
+	SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0,
+		       0, R_CLKENREG0, 29),
+	SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0,
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKENREG1, 11),
+	/* downstream of DIV clocks which are sourced from clk_div_top_axi0 */
+	SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 24),
+	SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 4),
+	SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 7),
+
+	/* downstream of clk_div_top_axi_hsperi */
+	SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI,
+		       "clk_gate_sysdma_axi", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 3),
+	SG2042_GATE_HW(GATE_CLK_APB_UART,
+		       "clk_gate_apb_uart", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 5),
+	SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C,
+		       "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 6),
+	SG2042_GATE_HW(GATE_CLK_APB_SPI,
+		       "clk_gate_apb_spi", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 25),
+	SG2042_GATE_HW(GATE_CLK_AXI_ETH0,
+		       "clk_gate_axi_eth0", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG0, 31),
+	SG2042_GATE_HW(GATE_CLK_AXI_EMMC,
+		       "clk_gate_axi_emmc", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 2),
+	SG2042_GATE_HW(GATE_CLK_AXI_SD,
+		       "clk_gate_axi_sd", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT, R_CLKENREG1, 5),
+	SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI,
+		       "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi,
+		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+		       R_CLKENREG1, 12),
+};
+
+/*
+ * Gate clocks for RP subsystem (including the MP subsystem), which control
+ * registers are defined in SYS_CTRL.
+ */
+static const struct sg2042_gate_clock sg2042_gate_rp[] = {
+	/* downstream of clk_gate_rp_cpu_normal about rxu */
+	SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 0),
+	SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 1),
+	SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 2),
+	SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 3),
+	SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 4),
+	SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 5),
+	SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 6),
+	SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 7),
+	SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 8),
+	SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 9),
+	SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 10),
+	SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 11),
+	SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 12),
+	SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 13),
+	SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 14),
+	SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 15),
+	SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 16),
+	SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 17),
+	SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 18),
+	SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 19),
+	SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 20),
+	SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 21),
+	SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 22),
+	SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 23),
+	SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 24),
+	SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 25),
+	SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 26),
+	SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 27),
+	SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 28),
+	SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 29),
+	SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 30),
+	SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate",
+		       0, R_RP_RXU_CLK_ENABLE, 31),
+
+	/* downstream of clk_gate_rp_cpu_normal about mp */
+	SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate",
+		       CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate",
+		       CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate",
+		       CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate",
+		       CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate",
+		       CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate",
+		       CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate",
+		       CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate",
+		       CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate",
+		       CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate",
+		       CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate",
+		       CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate",
+		       CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate",
+		       CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate",
+		       CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate",
+		       CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0),
+	SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate",
+		       CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0),
+};
+
+static DEFINE_SPINLOCK(sg2042_clk_lock);
+
+static int sg2042_clk_register_plls(struct device *dev,
+				    struct sg2042_clk_data *clk_data,
+				    struct sg2042_pll_clock pll_clks[],
+				    int num_pll_clks)
+{
+	struct clk_hw *hw;
+	struct sg2042_pll_clock *pll;
+	int i, ret = 0;
+
+	for (i = 0; i < num_pll_clks; i++) {
+		pll = &pll_clks[i];
+		/* assign these for ops usage during registration */
+		pll->base = clk_data->iobase;
+		pll->lock = &sg2042_clk_lock;
+
+		hw = &pll->hw;
+		ret = devm_clk_hw_register(dev, hw);
+		if (ret) {
+			pr_err("failed to register clock %s\n", pll->hw.init->name);
+			break;
+		}
+
+		clk_data->onecell_data.hws[pll->id] = hw;
+	}
+
+	return ret;
+}
+
+static int sg2042_clk_register_divs(struct device *dev,
+				    struct sg2042_clk_data *clk_data,
+				    struct sg2042_divider_clock div_clks[],
+				    int num_div_clks)
+{
+	struct clk_hw *hw;
+	struct sg2042_divider_clock *div;
+	int i, ret = 0;
+
+	for (i = 0; i < num_div_clks; i++) {
+		div = &div_clks[i];
+
+		if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) {
+			if (div->width + div->shift > 16) {
+				pr_warn("divider value exceeds LOWORD field\n");
+				ret = -EINVAL;
+				break;
+			}
+		}
+
+		div->reg = clk_data->iobase + div->offset_ctrl;
+		div->lock = &sg2042_clk_lock;
+
+		hw = &div->hw;
+		ret = devm_clk_hw_register(dev, hw);
+		if (ret) {
+			pr_err("failed to register clock %s\n", div->hw.init->name);
+			break;
+		}
+
+		clk_data->onecell_data.hws[div->id] = hw;
+	}
+
+	return ret;
+}
+
+static int sg2042_clk_register_gates(struct device *dev,
+				     struct sg2042_clk_data *clk_data,
+				     const struct sg2042_gate_clock gate_clks[],
+				     int num_gate_clks)
+{
+	struct clk_hw *hw;
+	const struct sg2042_gate_clock *gate;
+	int i, ret = 0;
+
+	for (i = 0; i < num_gate_clks; i++) {
+		gate = &gate_clks[i];
+		hw = __devm_clk_hw_register_gate
+			(dev,
+			 NULL,
+			 gate->hw.init->name,
+			 NULL,
+			 gate->hw.init->parent_hws[0],
+			 NULL,
+			 gate->hw.init->flags,
+			 clk_data->iobase + gate->offset_enable,
+			 gate->bit_idx,
+			 0,
+			 &sg2042_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("failed to register clock %s\n", gate->hw.init->name);
+			ret = PTR_ERR(hw);
+			break;
+		}
+
+		clk_data->onecell_data.hws[gate->id] = hw;
+
+		/* Updated some clocks which take the role of parent */
+		switch (gate->id) {
+		case GATE_CLK_RP_CPU_NORMAL:
+			*clk_gate_rp_cpu_normal = hw;
+			break;
+		case GATE_CLK_TOP_RP_CMN_DIV2:
+			*clk_gate_top_rp_cmn_div2 = hw;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int sg2042_clk_register_gates_fw(struct device *dev,
+					struct sg2042_clk_data *clk_data,
+					const struct sg2042_gate_clock gate_clks[],
+					int num_gate_clks)
+{
+	struct clk_hw *hw;
+	const struct sg2042_gate_clock *gate;
+	int i, ret = 0;
+
+	for (i = 0; i < num_gate_clks; i++) {
+		gate = &gate_clks[i];
+		hw = devm_clk_hw_register_gate_parent_data
+			(dev,
+			 gate->hw.init->name,
+			 gate->hw.init->parent_data,
+			 gate->hw.init->flags,
+			 clk_data->iobase + gate->offset_enable,
+			 gate->bit_idx,
+			 0,
+			 &sg2042_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("failed to register clock %s\n", gate->hw.init->name);
+			ret = PTR_ERR(hw);
+			break;
+		}
+
+		clk_data->onecell_data.hws[gate->id] = hw;
+
+		/* Updated some clocks which take the role of parent */
+		switch (gate->id) {
+		case GATE_CLK_DDR01_DIV0:
+			*clk_gate_ddr01_div0 = hw;
+			break;
+		case GATE_CLK_DDR01_DIV1:
+			*clk_gate_ddr01_div1 = hw;
+			break;
+		case GATE_CLK_DDR23_DIV0:
+			*clk_gate_ddr23_div0 = hw;
+			break;
+		case GATE_CLK_DDR23_DIV1:
+			*clk_gate_ddr23_div1 = hw;
+			break;
+		case GATE_CLK_RP_CPU_NORMAL_DIV0:
+			*clk_gate_rp_cpu_normal_div0 = hw;
+			break;
+		case GATE_CLK_RP_CPU_NORMAL_DIV1:
+			*clk_gate_rp_cpu_normal_div1 = hw;
+			break;
+		case GATE_CLK_AXI_DDR_DIV0:
+			*clk_gate_axi_ddr_div0 = hw;
+			break;
+		case GATE_CLK_AXI_DDR_DIV1:
+			*clk_gate_axi_ddr_div1 = hw;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int sg2042_mux_notifier_cb(struct notifier_block *nb,
+				  unsigned long event,
+				  void *data)
+{
+	int ret = 0;
+	struct clk_notifier_data *ndata = data;
+	struct clk_hw *hw = __clk_get_hw(ndata->clk);
+	const struct clk_ops *ops = &clk_mux_ops;
+	struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb);
+
+	/* To switch to fpll before changing rate and restore after that */
+	if (event == PRE_RATE_CHANGE) {
+		mux->original_index = ops->get_parent(hw);
+
+		/*
+		 * "1" is the array index of the second parent input source of
+		 * mux. For SG2042, it's fpll for all mux clocks.
+		 * "0" is the array index of the frist parent input source of
+		 * mux, For SG2042, it's mpll.
+		 * FIXME, any good idea to avoid magic number?
+		 */
+		if (mux->original_index == 0)
+			ret = ops->set_parent(hw, 1);
+	} else if (event == POST_RATE_CHANGE) {
+		ret = ops->set_parent(hw, mux->original_index);
+	}
+
+	return notifier_from_errno(ret);
+}
+
+static int sg2042_clk_register_muxs(struct device *dev,
+				    struct sg2042_clk_data *clk_data,
+				    struct sg2042_mux_clock mux_clks[],
+				    int num_mux_clks)
+{
+	struct clk_hw *hw;
+	struct sg2042_mux_clock *mux;
+	int i, ret = 0;
+
+	for (i = 0; i < num_mux_clks; i++) {
+		mux = &mux_clks[i];
+
+		hw = __devm_clk_hw_register_mux
+			(dev,
+			 NULL,
+			 mux->hw.init->name,
+			 mux->hw.init->num_parents,
+			 NULL,
+			 mux->hw.init->parent_hws,
+			 NULL,
+			 mux->hw.init->flags,
+			 clk_data->iobase + mux->offset_select,
+			 mux->shift,
+			 BIT(mux->width) - 1,
+			 0,
+			 sg2042_mux_table,
+			 &sg2042_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("failed to register clock %s\n", mux->hw.init->name);
+			ret = PTR_ERR(hw);
+			break;
+		}
+
+		clk_data->onecell_data.hws[mux->id] = hw;
+
+		/* Updated some clocks which takes the role of parent */
+		switch (mux->id) {
+		case MUX_CLK_DDR01:
+			*clk_mux_ddr01 = hw;
+			break;
+		case MUX_CLK_DDR23:
+			*clk_mux_ddr23 = hw;
+			break;
+		case MUX_CLK_RP_CPU_NORMAL:
+			*clk_mux_rp_cpu_normal = hw;
+			break;
+		case MUX_CLK_AXI_DDR:
+			*clk_mux_axi_ddr = hw;
+			break;
+		}
+
+		/*
+		 * FIXME: Theoretically, we should set parent for the
+		 * mux, but seems hardware has done this for us with
+		 * default value, so we don't set parent again here.
+		 */
+
+		if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {
+			mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;
+			ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb);
+			if (ret) {
+				pr_err("failed to register clock notifier for %s\n",
+				       mux->hw.init->name);
+				break;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int sg2042_init_clkdata(struct platform_device *pdev,
+			       int num_clks,
+			       struct sg2042_clk_data **pp_clk_data)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+
+	clk_data = devm_kzalloc(&pdev->dev,
+				struct_size(clk_data, onecell_data.hws, num_clks),
+				GFP_KERNEL);
+	if (!clk_data)
+		return -ENOMEM;
+
+	clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
+	if (WARN_ON(IS_ERR(clk_data->iobase)))
+		return PTR_ERR(clk_data->iobase);
+
+	clk_data->onecell_data.num = num_clks;
+
+	*pp_clk_data = clk_data;
+
+	return 0;
+}
+
+static int sg2042_clkgen_probe(struct platform_device *pdev)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+	int ret = 0;
+	int num_clks = 0;
+
+	num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) +
+		   ARRAY_SIZE(sg2042_div_clks_level_2) +
+		   ARRAY_SIZE(sg2042_gate_clks_level_1) +
+		   ARRAY_SIZE(sg2042_gate_clks_level_2) +
+		   ARRAY_SIZE(sg2042_mux_clks);
+
+	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
+	if (ret)
+		goto error_out;
+
+	/* level-1 gates */
+	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,
+					   sg2042_gate_clks_level_1,
+					   ARRAY_SIZE(sg2042_gate_clks_level_1));
+	if (ret)
+		goto error_out;
+
+	/* level-1 div */
+	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,
+				       ARRAY_SIZE(sg2042_div_clks_level_1));
+	if (ret)
+		goto error_out;
+
+	/* mux */
+	ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,
+				       ARRAY_SIZE(sg2042_mux_clks));
+	if (ret)
+		goto error_out;
+
+	/* level 2 div */
+	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,
+				       ARRAY_SIZE(sg2042_div_clks_level_2));
+	if (ret)
+		goto error_out;
+
+	/* level 2 gate */
+	ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,
+					ARRAY_SIZE(sg2042_gate_clks_level_2));
+	if (ret)
+		goto error_out;
+
+	return devm_of_clk_add_hw_provider(&pdev->dev,
+					   of_clk_hw_onecell_get,
+					   &clk_data->onecell_data);
+
+error_out:
+	pr_err("%s failed error number %d\n", __func__, ret);
+	return ret;
+}
+
+static int sg2042_rpgate_probe(struct platform_device *pdev)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+	int ret = 0;
+	int num_clks = 0;
+
+	num_clks = ARRAY_SIZE(sg2042_gate_rp);
+
+	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
+	if (ret)
+		goto error_out;
+
+	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data, sg2042_gate_rp,
+					   num_clks);
+	if (ret)
+		goto error_out;
+
+	return devm_of_clk_add_hw_provider(&pdev->dev,
+					   of_clk_hw_onecell_get,
+					   &clk_data->onecell_data);
+
+error_out:
+	pr_err("%s failed error number %d\n", __func__, ret);
+	return ret;
+}
+
+static int sg2042_pll_probe(struct platform_device *pdev)
+{
+	struct sg2042_clk_data *clk_data = NULL;
+	int ret = 0;
+	int num_clks = 0;
+
+	num_clks = ARRAY_SIZE(sg2042_pll_clks);
+
+	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
+	if (ret)
+		goto error_out;
+
+	ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
+				       num_clks);
+	if (ret)
+		goto error_out;
+
+	return devm_of_clk_add_hw_provider(&pdev->dev,
+					   of_clk_hw_onecell_get,
+					   &clk_data->onecell_data);
+
+error_out:
+	pr_err("%s failed error number %d\n", __func__, ret);
+	return ret;
+}
+
+static const struct of_device_id sg2042_clkgen_match[] = {
+	{ .compatible = "sophgo,sg2042-clkgen" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver sg2042_clkgen_driver = {
+	.probe = sg2042_clkgen_probe,
+	.driver = {
+		.name = "clk-sophgo-sg2042-clkgen",
+		.of_match_table = sg2042_clkgen_match,
+		.suppress_bind_attrs = true,
+	},
+};
+builtin_platform_driver(sg2042_clkgen_driver);
+
+static const struct of_device_id sg2042_rpgate_match[] = {
+	{ .compatible = "sophgo,sg2042-rpgate" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver sg2042_rpgate_driver = {
+	.probe = sg2042_rpgate_probe,
+	.driver = {
+		.name = "clk-sophgo-sg2042-rpgate",
+		.of_match_table = sg2042_rpgate_match,
+		.suppress_bind_attrs = true,
+	},
+};
+builtin_platform_driver(sg2042_rpgate_driver);
+
+static const struct of_device_id sg2042_pll_match[] = {
+	{ .compatible = "sophgo,sg2042-pll" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver sg2042_pll_driver = {
+	.probe = sg2042_pll_probe,
+	.driver = {
+		.name = "clk-sophgo-sg2042-pll",
+		.of_match_table = sg2042_pll_match,
+		.suppress_bind_attrs = true,
+	},
+};
+builtin_platform_driver(sg2042_pll_driver);
-- 
2.25.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 5/5] riscv: dts: add clock generator for Sophgo SG2042 SoC
  2024-04-26  3:32 ` Chen Wang
@ 2024-04-26  3:35   ` Chen Wang
  -1 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:35 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang

From: Chen Wang <unicorn_wang@outlook.com>

Add clock generator node to device tree for SG2042, and enable clock for
uart.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Guo Ren <guoren@kernel.org>
---
 .../boot/dts/sophgo/sg2042-milkv-pioneer.dts  | 12 ++++
 arch/riscv/boot/dts/sophgo/sg2042.dtsi        | 55 ++++++++++++++++++-
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts b/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts
index 49b4b9c2c101..80cb017974d8 100644
--- a/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts
+++ b/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts
@@ -14,6 +14,18 @@ chosen {
 	};
 };
 
+&cgi_main {
+	clock-frequency = <25000000>;
+};
+
+&cgi_dpll0 {
+	clock-frequency = <25000000>;
+};
+
+&cgi_dpll1 {
+	clock-frequency = <25000000>;
+};
+
 &uart0 {
 	status = "okay";
 };
diff --git a/arch/riscv/boot/dts/sophgo/sg2042.dtsi b/arch/riscv/boot/dts/sophgo/sg2042.dtsi
index 81fda312f988..34c802bd3f9b 100644
--- a/arch/riscv/boot/dts/sophgo/sg2042.dtsi
+++ b/arch/riscv/boot/dts/sophgo/sg2042.dtsi
@@ -4,8 +4,10 @@
  */
 
 /dts-v1/;
+#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
+#include <dt-bindings/clock/sophgo,sg2042-pll.h>
+#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
 #include <dt-bindings/interrupt-controller/irq.h>
-
 #include <dt-bindings/reset/sophgo,sg2042-reset.h>
 
 #include "sg2042-cpus.dtsi"
@@ -20,12 +22,60 @@ aliases {
 		serial0 = &uart0;
 	};
 
+	cgi_main: oscillator0 {
+		compatible = "fixed-clock";
+		clock-output-names = "cgi_main";
+		#clock-cells = <0>;
+	};
+
+	cgi_dpll0: oscillator1 {
+		compatible = "fixed-clock";
+		clock-output-names = "cgi_dpll0";
+		#clock-cells = <0>;
+	};
+
+	cgi_dpll1: oscillator2 {
+		compatible = "fixed-clock";
+		clock-output-names = "cgi_dpll1";
+		#clock-cells = <0>;
+	};
+
 	soc: soc {
 		compatible = "simple-bus";
 		#address-cells = <2>;
 		#size-cells = <2>;
 		ranges;
 
+		pllclk: clock-controller@70300100c0 {
+			compatible = "sophgo,sg2042-pll";
+			reg = <0x70 0x300100c0 0x0 0x40>;
+			clocks = <&cgi_main>, <&cgi_dpll0>, <&cgi_dpll1>;
+			clock-names = "cgi_main", "cgi_dpll0", "cgi_dpll1";
+			#clock-cells = <1>;
+		};
+
+		rpgate: clock-controller@7030010368 {
+			compatible = "sophgo,sg2042-rpgate";
+			reg = <0x70 0x30010368 0x0 0x98>;
+			clocks = <&clkgen GATE_CLK_RP_CPU_NORMAL>;
+			clock-names = "rpgate";
+			#clock-cells = <1>;
+		};
+
+		clkgen: clock-controller@7030012000 {
+			compatible = "sophgo,sg2042-clkgen";
+			reg = <0x70 0x30012000 0x0 0x1000>;
+			clocks = <&pllclk MPLL_CLK>,
+				 <&pllclk FPLL_CLK>,
+				 <&pllclk DPLL0_CLK>,
+				 <&pllclk DPLL1_CLK>;
+			clock-names = "mpll",
+				      "fpll",
+				      "dpll0",
+				      "dpll1";
+			#clock-cells = <1>;
+		};
+
 		clint_mswi: interrupt-controller@7094000000 {
 			compatible = "sophgo,sg2042-aclint-mswi", "thead,c900-aclint-mswi";
 			reg = <0x00000070 0x94000000 0x00000000 0x00004000>;
@@ -341,6 +391,9 @@ uart0: serial@7040000000 {
 			interrupt-parent = <&intc>;
 			interrupts = <112 IRQ_TYPE_LEVEL_HIGH>;
 			clock-frequency = <500000000>;
+			clocks = <&clkgen GATE_CLK_UART_500M>,
+				 <&clkgen GATE_CLK_APB_UART>;
+			clock-names = "baudclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			resets = <&rstgen RST_UART0>;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v15 5/5] riscv: dts: add clock generator for Sophgo SG2042 SoC
@ 2024-04-26  3:35   ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:35 UTC (permalink / raw)
  To: aou, chao.wei, conor, krzysztof.kozlowski+dt, mturquette, palmer,
	paul.walmsley, richardcochran, robh+dt, sboyd, devicetree,
	linux-clk, linux-kernel, linux-riscv, haijiao.liu,
	xiaoguang.xing, guoren, jszhang, inochiama, samuel.holland
  Cc: Chen Wang

From: Chen Wang <unicorn_wang@outlook.com>

Add clock generator node to device tree for SG2042, and enable clock for
uart.

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Guo Ren <guoren@kernel.org>
---
 .../boot/dts/sophgo/sg2042-milkv-pioneer.dts  | 12 ++++
 arch/riscv/boot/dts/sophgo/sg2042.dtsi        | 55 ++++++++++++++++++-
 2 files changed, 66 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts b/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts
index 49b4b9c2c101..80cb017974d8 100644
--- a/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts
+++ b/arch/riscv/boot/dts/sophgo/sg2042-milkv-pioneer.dts
@@ -14,6 +14,18 @@ chosen {
 	};
 };
 
+&cgi_main {
+	clock-frequency = <25000000>;
+};
+
+&cgi_dpll0 {
+	clock-frequency = <25000000>;
+};
+
+&cgi_dpll1 {
+	clock-frequency = <25000000>;
+};
+
 &uart0 {
 	status = "okay";
 };
diff --git a/arch/riscv/boot/dts/sophgo/sg2042.dtsi b/arch/riscv/boot/dts/sophgo/sg2042.dtsi
index 81fda312f988..34c802bd3f9b 100644
--- a/arch/riscv/boot/dts/sophgo/sg2042.dtsi
+++ b/arch/riscv/boot/dts/sophgo/sg2042.dtsi
@@ -4,8 +4,10 @@
  */
 
 /dts-v1/;
+#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
+#include <dt-bindings/clock/sophgo,sg2042-pll.h>
+#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
 #include <dt-bindings/interrupt-controller/irq.h>
-
 #include <dt-bindings/reset/sophgo,sg2042-reset.h>
 
 #include "sg2042-cpus.dtsi"
@@ -20,12 +22,60 @@ aliases {
 		serial0 = &uart0;
 	};
 
+	cgi_main: oscillator0 {
+		compatible = "fixed-clock";
+		clock-output-names = "cgi_main";
+		#clock-cells = <0>;
+	};
+
+	cgi_dpll0: oscillator1 {
+		compatible = "fixed-clock";
+		clock-output-names = "cgi_dpll0";
+		#clock-cells = <0>;
+	};
+
+	cgi_dpll1: oscillator2 {
+		compatible = "fixed-clock";
+		clock-output-names = "cgi_dpll1";
+		#clock-cells = <0>;
+	};
+
 	soc: soc {
 		compatible = "simple-bus";
 		#address-cells = <2>;
 		#size-cells = <2>;
 		ranges;
 
+		pllclk: clock-controller@70300100c0 {
+			compatible = "sophgo,sg2042-pll";
+			reg = <0x70 0x300100c0 0x0 0x40>;
+			clocks = <&cgi_main>, <&cgi_dpll0>, <&cgi_dpll1>;
+			clock-names = "cgi_main", "cgi_dpll0", "cgi_dpll1";
+			#clock-cells = <1>;
+		};
+
+		rpgate: clock-controller@7030010368 {
+			compatible = "sophgo,sg2042-rpgate";
+			reg = <0x70 0x30010368 0x0 0x98>;
+			clocks = <&clkgen GATE_CLK_RP_CPU_NORMAL>;
+			clock-names = "rpgate";
+			#clock-cells = <1>;
+		};
+
+		clkgen: clock-controller@7030012000 {
+			compatible = "sophgo,sg2042-clkgen";
+			reg = <0x70 0x30012000 0x0 0x1000>;
+			clocks = <&pllclk MPLL_CLK>,
+				 <&pllclk FPLL_CLK>,
+				 <&pllclk DPLL0_CLK>,
+				 <&pllclk DPLL1_CLK>;
+			clock-names = "mpll",
+				      "fpll",
+				      "dpll0",
+				      "dpll1";
+			#clock-cells = <1>;
+		};
+
 		clint_mswi: interrupt-controller@7094000000 {
 			compatible = "sophgo,sg2042-aclint-mswi", "thead,c900-aclint-mswi";
 			reg = <0x00000070 0x94000000 0x00000000 0x00004000>;
@@ -341,6 +391,9 @@ uart0: serial@7040000000 {
 			interrupt-parent = <&intc>;
 			interrupts = <112 IRQ_TYPE_LEVEL_HIGH>;
 			clock-frequency = <500000000>;
+			clocks = <&clkgen GATE_CLK_UART_500M>,
+				 <&clkgen GATE_CLK_APB_UART>;
+			clock-names = "baudclk", "apb_pclk";
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			resets = <&rstgen RST_UART0>;
-- 
2.25.1


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v15 0/5] riscv: sophgo: add clock support for sg2042
  2024-04-26  3:32 ` Chen Wang
@ 2024-04-26  3:40   ` Chen Wang
  -1 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:40 UTC (permalink / raw)
  To: Chen Wang, aou, chao.wei, conor, krzysztof.kozlowski+dt,
	mturquette, palmer, paul.walmsley, richardcochran, robh+dt,
	sboyd, devicetree, linux-clk, linux-kernel, linux-riscv,
	haijiao.liu, xiaoguang.xing, guoren, jszhang, inochiama,
	samuel.holland

Hi,  Rob Herring & Guo Ren,

For this version, I updated bindings. Due to the change is minor(just 
add one clock-names property), so I reserve your "Reviewed-by:". Any 
question please feel free let me know.

Thanks,

Chen

On 2024/4/26 11:32, Chen Wang wrote:
> From: Chen Wang <unicorn_wang@outlook.com>
>
> This series adds clock controller support for sophgo sg2042.
>
> Thanks,
> Chen
>
> ---
>
> Changes in v15:
>
>    The patch series is based on v6.9-rc5.
>
>    Improved the dirvier code as per 3rd review comments from Stephen Boyd.
>    - Converted all parents described by strings to use clk_parent_data or
>      clk_hw directly.
>    - Just use struct clk_init_data::parent_hws when only have a clk_hw.
>    - Removed extra cleanup when use devm.
>    - Some misc code improvements.
>
> Changes in v14:
>
>    The patch series is based on v6.9-rc1. You can simply review or test the
>    patches at the link [15].
>
>    Improved the dirvier code as per 2nd review comments from Stephen Boyd.
>    - Inline the header file into source file.
>    - Use devm_xxx functions for pll/div/gate registeration.
>    - Use clk_parent_data for mux clocks initialization.
>    - Use u32 for registers readl/writel.
>    - Use devm_platform_ioremap_resource instead of devm_of_iomap.
>    - Cleanup some dead code and add definitions for some magic numbers.
>    - Add include files missed.
>    - Use kernel-doc to improve comments for some structure and functions.
>    - Other misc code cleanup work as per input from reviewers.
>
> Changes in v13:
>
>    The patch series is based on v6.9-rc1. You can simply review or test the
>    patches at the link [14].
>
>    Just added a minor fix for clk driver which was missed in v12.
>
> Changes in v12:
>
>    The patch series is based on v6.9-rc1. You can simply review or test the
>    patches at the link [13].
>
>    Improved the dirvier code as per review comments from Stephen Boyd.
>    - Remove default y for CLK_SOPHGO_SG2042.
>    - Optimize sg2042_pll_get_postdiv_1_2, move postdiv1_2 to the function.
>      scope and add more explaniation.
>    - Optimize sg2042_get_pll_ctl_setting.
>    - Switch to platform driver.
>    - Use clk_hw for initialization of struct clks.
>    - Don't use ignore_unused when using critical.
>    - Other code cleanup as per input form the reviewers.
>
> Changes in v11:
>
>    The patch series is based on v6.8-rc5. You can simply review or test the
>    patches at the link [12].
>
>    Quick fixed some dt_binding_check errors reported by Rob.
>
> Changes in v10:
>
>    The patch series is based on v6.8-rc4. You can simply review or test the
>    patches at the link [11].
>
>    Add input clocks for rpgate & clkgen.
>
> Changes in v9:
>    The patch series is based on v6.8-rc2. You can simply review or test the
>    patches at the link [10].
>
>    From this version, drop the system-controller node due to there is no actual
>    device corresponding to it in IC design. SYS_CTRL is just a registers segment
>    defined on TRM for misc functions. Now three clock-controllers are defined for
>    SG2042, the control registers of the three clock-controllers are scattered in
>    different memory address spaces:
>    - the first one is for pll clocks;
>    - the second one is for gate clocks for RP subsystem;
>    - the third one is for div/mux, and gate clocks working for other subsystem
>      than RP subsystem.
>
> Changes in v8:
>    The patch series is based on v6.7. You can simply review or test the
>    patches at the link [9].
>    
>    In this version, the main change is to split one clock provider into two.
>    Strictly follow the hardware instructions, in the memoymap, the control
>    registers of some clocks are defined in the SYS_CTRL segment, and the
>    control registers of other clocks are defined in the CLOCK segment.
>    Therefore, the new design defines two clock controllers, one as a child
>    node of the system control and the other as an independent clock controller
>    node.
>
>    This modification involves a major modification to the binding files, so
>    the reviewed-by tags has been deleted.
>
> Changes in v7:
>    The patch series is based on v6.7. You can simply review or test the
>    patches at the link [8].
>    - fixed initval issue.
>    - fixed pll clk crash issue.
>    - fixed warning reported by <lkp@intel.com>
>    - code optimization as per review comments.
>    - code cleanup and style improvements as per review comments and checkpatch
>      with "--strict"
>
> Changes in v6:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [7].
>    - fixed some warnings/errors reported by kernel test robot <lkp@intel.com>.
>
> Changes in v5:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [6].
>    - dt-bindings: improved yaml, such as:
>      - add vendor prefix for system-ctrl property for clock generator.
>      - Add explanation for system-ctrl property.
>    - move sophgo,sg2042-clkgen.yaml to directly under clock folder.
>    - fixed bugs for driver Makefile/Kconfig
>    - continue cleaning-up debug print for driver code.
>
> Changes in v4:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [5].
>    - dt-bindings: fixed a dt_binding_check error.
>
> Changes in v3:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [3].
>    - DTS: don't use syscon but define sg2042 specific system control node. More
>      background info can read [4].
>    - Updating minor issues in dt-bindings as per input from reviews.
>
> Changes in v2:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [2].
>    - Squashed the patch adding clock definitions with the patch adding the
>      binding for the clock controller.
>    - Updating dt-binding for syscon, remove oneOf for property compatible;
>      define clock controller as child of syscon.
>    - DTS changes: merge sg2042-clock.dtsi into sg2042.dtsi; move clock-frequency
>      property of osc to board devicethree due to the oscillator is outside the
>      SoC.
>    - Fixed some bugs in driver code during testing, including removing warnings
>      for rv32_defconfig.
>    - Updated MAINTAINERS info.
>
> Changes in v1:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [1].
>
> Link: https://lore.kernel.org/linux-riscv/cover.1699879741.git.unicorn_wang@outlook.com/ [1]
> Link: https://lore.kernel.org/linux-riscv/cover.1701044106.git.unicorn_wang@outlook.com/ [2]
> Link: https://lore.kernel.org/linux-riscv/cover.1701691923.git.unicorn_wang@outlook.com/ [3]
> Link: https://lore.kernel.org/linux-riscv/MA0P287MB03329AE180378E1A2E034374FE82A@MA0P287MB0332.INDP287.PROD.OUTLOOK.COM/ [4]
> Link: https://lore.kernel.org/linux-riscv/cover.1701734442.git.unicorn_wang@outlook.com/ [5]
> Link: https://lore.kernel.org/linux-riscv/cover.1701938395.git.unicorn_wang@outlook.com/ [6]
> Link: https://lore.kernel.org/linux-riscv/cover.1701997033.git.unicorn_wang@outlook.com/ [7]
> Link: https://lore.kernel.org/linux-riscv/cover.1704694903.git.unicorn_wang@outlook.com/ [8]
> Link: https://lore.kernel.org/linux-riscv/cover.1705388518.git.unicorn_wang@outlook.com/ [9]
> Link: https://lore.kernel.org/linux-riscv/cover.1706854074.git.unicorn_wang@outlook.com/ [10]
> Link: https://lore.kernel.org/linux-riscv/cover.1708223519.git.unicorn_wang@outlook.com/ [11]
> Link: https://lore.kernel.org/linux-riscv/cover.1708397315.git.unicorn_wang@outlook.com/ [12]
> Link: https://lore.kernel.org/linux-riscv/cover.1711527932.git.unicorn_wang@outlook.com/ [13]
> Link: https://lore.kernel.org/linux-riscv/cover.1711692169.git.unicorn_wang@outlook.com/ [14]
> Link: https://lore.kernel.org/linux-riscv/cover.1713164546.git.unicorn_wang@outlook.com/ [15]
>
> ---
>
> Chen Wang (5):
>    dt-bindings: clock: sophgo: add pll clocks for SG2042
>    dt-bindings: clock: sophgo: add RP gate clocks for SG2042
>    dt-bindings: clock: sophgo: add clkgen for SG2042
>    clk: sophgo: Add SG2042 clock driver
>    riscv: dts: add clock generator for Sophgo SG2042 SoC
>
>   .../bindings/clock/sophgo,sg2042-clkgen.yaml  |   61 +
>   .../bindings/clock/sophgo,sg2042-pll.yaml     |   53 +
>   .../bindings/clock/sophgo,sg2042-rpgate.yaml  |   49 +
>   .../boot/dts/sophgo/sg2042-milkv-pioneer.dts  |   12 +
>   arch/riscv/boot/dts/sophgo/sg2042.dtsi        |   55 +-
>   drivers/clk/Kconfig                           |    1 +
>   drivers/clk/Makefile                          |    1 +
>   drivers/clk/sophgo/Kconfig                    |    8 +
>   drivers/clk/sophgo/Makefile                   |    2 +
>   drivers/clk/sophgo/clk-sophgo-sg2042.c        | 1870 +++++++++++++++++
>   .../dt-bindings/clock/sophgo,sg2042-clkgen.h  |  111 +
>   include/dt-bindings/clock/sophgo,sg2042-pll.h |   14 +
>   .../dt-bindings/clock/sophgo,sg2042-rpgate.h  |   58 +
>   13 files changed, 2294 insertions(+), 1 deletion(-)
>   create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
>   create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
>   create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
>   create mode 100644 drivers/clk/sophgo/Kconfig
>   create mode 100644 drivers/clk/sophgo/Makefile
>   create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
>   create mode 100644 include/dt-bindings/clock/sophgo,sg2042-clkgen.h
>   create mode 100644 include/dt-bindings/clock/sophgo,sg2042-pll.h
>   create mode 100644 include/dt-bindings/clock/sophgo,sg2042-rpgate.h
>
>
> base-commit: ed30a4a51bb196781c8058073ea720133a65596f

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v15 0/5] riscv: sophgo: add clock support for sg2042
@ 2024-04-26  3:40   ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-26  3:40 UTC (permalink / raw)
  To: Chen Wang, aou, chao.wei, conor, krzysztof.kozlowski+dt,
	mturquette, palmer, paul.walmsley, richardcochran, robh+dt,
	sboyd, devicetree, linux-clk, linux-kernel, linux-riscv,
	haijiao.liu, xiaoguang.xing, guoren, jszhang, inochiama,
	samuel.holland

Hi,  Rob Herring & Guo Ren,

For this version, I updated bindings. Due to the change is minor(just 
add one clock-names property), so I reserve your "Reviewed-by:". Any 
question please feel free let me know.

Thanks,

Chen

On 2024/4/26 11:32, Chen Wang wrote:
> From: Chen Wang <unicorn_wang@outlook.com>
>
> This series adds clock controller support for sophgo sg2042.
>
> Thanks,
> Chen
>
> ---
>
> Changes in v15:
>
>    The patch series is based on v6.9-rc5.
>
>    Improved the dirvier code as per 3rd review comments from Stephen Boyd.
>    - Converted all parents described by strings to use clk_parent_data or
>      clk_hw directly.
>    - Just use struct clk_init_data::parent_hws when only have a clk_hw.
>    - Removed extra cleanup when use devm.
>    - Some misc code improvements.
>
> Changes in v14:
>
>    The patch series is based on v6.9-rc1. You can simply review or test the
>    patches at the link [15].
>
>    Improved the dirvier code as per 2nd review comments from Stephen Boyd.
>    - Inline the header file into source file.
>    - Use devm_xxx functions for pll/div/gate registeration.
>    - Use clk_parent_data for mux clocks initialization.
>    - Use u32 for registers readl/writel.
>    - Use devm_platform_ioremap_resource instead of devm_of_iomap.
>    - Cleanup some dead code and add definitions for some magic numbers.
>    - Add include files missed.
>    - Use kernel-doc to improve comments for some structure and functions.
>    - Other misc code cleanup work as per input from reviewers.
>
> Changes in v13:
>
>    The patch series is based on v6.9-rc1. You can simply review or test the
>    patches at the link [14].
>
>    Just added a minor fix for clk driver which was missed in v12.
>
> Changes in v12:
>
>    The patch series is based on v6.9-rc1. You can simply review or test the
>    patches at the link [13].
>
>    Improved the dirvier code as per review comments from Stephen Boyd.
>    - Remove default y for CLK_SOPHGO_SG2042.
>    - Optimize sg2042_pll_get_postdiv_1_2, move postdiv1_2 to the function.
>      scope and add more explaniation.
>    - Optimize sg2042_get_pll_ctl_setting.
>    - Switch to platform driver.
>    - Use clk_hw for initialization of struct clks.
>    - Don't use ignore_unused when using critical.
>    - Other code cleanup as per input form the reviewers.
>
> Changes in v11:
>
>    The patch series is based on v6.8-rc5. You can simply review or test the
>    patches at the link [12].
>
>    Quick fixed some dt_binding_check errors reported by Rob.
>
> Changes in v10:
>
>    The patch series is based on v6.8-rc4. You can simply review or test the
>    patches at the link [11].
>
>    Add input clocks for rpgate & clkgen.
>
> Changes in v9:
>    The patch series is based on v6.8-rc2. You can simply review or test the
>    patches at the link [10].
>
>    From this version, drop the system-controller node due to there is no actual
>    device corresponding to it in IC design. SYS_CTRL is just a registers segment
>    defined on TRM for misc functions. Now three clock-controllers are defined for
>    SG2042, the control registers of the three clock-controllers are scattered in
>    different memory address spaces:
>    - the first one is for pll clocks;
>    - the second one is for gate clocks for RP subsystem;
>    - the third one is for div/mux, and gate clocks working for other subsystem
>      than RP subsystem.
>
> Changes in v8:
>    The patch series is based on v6.7. You can simply review or test the
>    patches at the link [9].
>    
>    In this version, the main change is to split one clock provider into two.
>    Strictly follow the hardware instructions, in the memoymap, the control
>    registers of some clocks are defined in the SYS_CTRL segment, and the
>    control registers of other clocks are defined in the CLOCK segment.
>    Therefore, the new design defines two clock controllers, one as a child
>    node of the system control and the other as an independent clock controller
>    node.
>
>    This modification involves a major modification to the binding files, so
>    the reviewed-by tags has been deleted.
>
> Changes in v7:
>    The patch series is based on v6.7. You can simply review or test the
>    patches at the link [8].
>    - fixed initval issue.
>    - fixed pll clk crash issue.
>    - fixed warning reported by <lkp@intel.com>
>    - code optimization as per review comments.
>    - code cleanup and style improvements as per review comments and checkpatch
>      with "--strict"
>
> Changes in v6:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [7].
>    - fixed some warnings/errors reported by kernel test robot <lkp@intel.com>.
>
> Changes in v5:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [6].
>    - dt-bindings: improved yaml, such as:
>      - add vendor prefix for system-ctrl property for clock generator.
>      - Add explanation for system-ctrl property.
>    - move sophgo,sg2042-clkgen.yaml to directly under clock folder.
>    - fixed bugs for driver Makefile/Kconfig
>    - continue cleaning-up debug print for driver code.
>
> Changes in v4:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [5].
>    - dt-bindings: fixed a dt_binding_check error.
>
> Changes in v3:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [3].
>    - DTS: don't use syscon but define sg2042 specific system control node. More
>      background info can read [4].
>    - Updating minor issues in dt-bindings as per input from reviews.
>
> Changes in v2:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [2].
>    - Squashed the patch adding clock definitions with the patch adding the
>      binding for the clock controller.
>    - Updating dt-binding for syscon, remove oneOf for property compatible;
>      define clock controller as child of syscon.
>    - DTS changes: merge sg2042-clock.dtsi into sg2042.dtsi; move clock-frequency
>      property of osc to board devicethree due to the oscillator is outside the
>      SoC.
>    - Fixed some bugs in driver code during testing, including removing warnings
>      for rv32_defconfig.
>    - Updated MAINTAINERS info.
>
> Changes in v1:
>    The patch series is based on v6.7-rc1. You can simply review or test the
>    patches at the link [1].
>
> Link: https://lore.kernel.org/linux-riscv/cover.1699879741.git.unicorn_wang@outlook.com/ [1]
> Link: https://lore.kernel.org/linux-riscv/cover.1701044106.git.unicorn_wang@outlook.com/ [2]
> Link: https://lore.kernel.org/linux-riscv/cover.1701691923.git.unicorn_wang@outlook.com/ [3]
> Link: https://lore.kernel.org/linux-riscv/MA0P287MB03329AE180378E1A2E034374FE82A@MA0P287MB0332.INDP287.PROD.OUTLOOK.COM/ [4]
> Link: https://lore.kernel.org/linux-riscv/cover.1701734442.git.unicorn_wang@outlook.com/ [5]
> Link: https://lore.kernel.org/linux-riscv/cover.1701938395.git.unicorn_wang@outlook.com/ [6]
> Link: https://lore.kernel.org/linux-riscv/cover.1701997033.git.unicorn_wang@outlook.com/ [7]
> Link: https://lore.kernel.org/linux-riscv/cover.1704694903.git.unicorn_wang@outlook.com/ [8]
> Link: https://lore.kernel.org/linux-riscv/cover.1705388518.git.unicorn_wang@outlook.com/ [9]
> Link: https://lore.kernel.org/linux-riscv/cover.1706854074.git.unicorn_wang@outlook.com/ [10]
> Link: https://lore.kernel.org/linux-riscv/cover.1708223519.git.unicorn_wang@outlook.com/ [11]
> Link: https://lore.kernel.org/linux-riscv/cover.1708397315.git.unicorn_wang@outlook.com/ [12]
> Link: https://lore.kernel.org/linux-riscv/cover.1711527932.git.unicorn_wang@outlook.com/ [13]
> Link: https://lore.kernel.org/linux-riscv/cover.1711692169.git.unicorn_wang@outlook.com/ [14]
> Link: https://lore.kernel.org/linux-riscv/cover.1713164546.git.unicorn_wang@outlook.com/ [15]
>
> ---
>
> Chen Wang (5):
>    dt-bindings: clock: sophgo: add pll clocks for SG2042
>    dt-bindings: clock: sophgo: add RP gate clocks for SG2042
>    dt-bindings: clock: sophgo: add clkgen for SG2042
>    clk: sophgo: Add SG2042 clock driver
>    riscv: dts: add clock generator for Sophgo SG2042 SoC
>
>   .../bindings/clock/sophgo,sg2042-clkgen.yaml  |   61 +
>   .../bindings/clock/sophgo,sg2042-pll.yaml     |   53 +
>   .../bindings/clock/sophgo,sg2042-rpgate.yaml  |   49 +
>   .../boot/dts/sophgo/sg2042-milkv-pioneer.dts  |   12 +
>   arch/riscv/boot/dts/sophgo/sg2042.dtsi        |   55 +-
>   drivers/clk/Kconfig                           |    1 +
>   drivers/clk/Makefile                          |    1 +
>   drivers/clk/sophgo/Kconfig                    |    8 +
>   drivers/clk/sophgo/Makefile                   |    2 +
>   drivers/clk/sophgo/clk-sophgo-sg2042.c        | 1870 +++++++++++++++++
>   .../dt-bindings/clock/sophgo,sg2042-clkgen.h  |  111 +
>   include/dt-bindings/clock/sophgo,sg2042-pll.h |   14 +
>   .../dt-bindings/clock/sophgo,sg2042-rpgate.h  |   58 +
>   13 files changed, 2294 insertions(+), 1 deletion(-)
>   create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-clkgen.yaml
>   create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-pll.yaml
>   create mode 100644 Documentation/devicetree/bindings/clock/sophgo,sg2042-rpgate.yaml
>   create mode 100644 drivers/clk/sophgo/Kconfig
>   create mode 100644 drivers/clk/sophgo/Makefile
>   create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
>   create mode 100644 include/dt-bindings/clock/sophgo,sg2042-clkgen.h
>   create mode 100644 include/dt-bindings/clock/sophgo,sg2042-pll.h
>   create mode 100644 include/dt-bindings/clock/sophgo,sg2042-rpgate.h
>
>
> base-commit: ed30a4a51bb196781c8058073ea720133a65596f

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v15 4/5] clk: sophgo: Add SG2042 clock driver
  2024-04-26  3:35   ` Chen Wang
@ 2024-04-30  0:29     ` Chen Wang
  -1 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-30  0:29 UTC (permalink / raw)
  To: Chen Wang, aou, chao.wei, conor, krzysztof.kozlowski+dt,
	mturquette, palmer, paul.walmsley, richardcochran, robh+dt,
	sboyd, devicetree, linux-clk, linux-kernel, linux-riscv,
	haijiao.liu, xiaoguang.xing, guoren, jszhang, inochiama,
	samuel.holland

Ping ~~~

Hi, Stephen,

Can you please take another look? I've improved the code based on your 
last comment.

Thanks,

Chen

On 2024/4/26 11:35, Chen Wang wrote:
> From: Chen Wang <unicorn_wang@outlook.com>
>
> Add a driver for the SOPHGO SG2042 clocks.
>
> Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
> ---
>   drivers/clk/Kconfig                    |    1 +
>   drivers/clk/Makefile                   |    1 +
>   drivers/clk/sophgo/Kconfig             |    8 +
>   drivers/clk/sophgo/Makefile            |    2 +
>   drivers/clk/sophgo/clk-sophgo-sg2042.c | 1870 ++++++++++++++++++++++++
>   5 files changed, 1882 insertions(+)
>   create mode 100644 drivers/clk/sophgo/Kconfig
>   create mode 100644 drivers/clk/sophgo/Makefile
>   create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 50af5fc7f570..bc28502ec3c9 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
>   source "drivers/clk/samsung/Kconfig"
>   source "drivers/clk/sifive/Kconfig"
>   source "drivers/clk/socfpga/Kconfig"
> +source "drivers/clk/sophgo/Kconfig"
>   source "drivers/clk/sprd/Kconfig"
>   source "drivers/clk/starfive/Kconfig"
>   source "drivers/clk/sunxi/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 14fa8d4ecc1f..4abe16c8ccdf 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
>   obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
>   obj-$(CONFIG_CLK_SIFIVE)		+= sifive/
>   obj-y					+= socfpga/
> +obj-y					+= sophgo/
>   obj-$(CONFIG_PLAT_SPEAR)		+= spear/
>   obj-y					+= sprd/
>   obj-$(CONFIG_ARCH_STI)			+= st/
> diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig
> new file mode 100644
> index 000000000000..82516adb0f97
> --- /dev/null
> +++ b/drivers/clk/sophgo/Kconfig
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# common clock support for SOPHGO SoC family.
> +
> +config CLK_SOPHGO_SG2042
> +	bool "Sophgo SG2042 clock support"
> +	depends on ARCH_SOPHGO || COMPILE_TEST
> +	help
> +	  Say yes here to support the clock controller on the Sophgo SG2042 SoC.
> diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile
> new file mode 100644
> index 000000000000..13834cce260c
> --- /dev/null
> +++ b/drivers/clk/sophgo/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_CLK_SOPHGO_SG2042)	+= clk-sophgo-sg2042.o
> diff --git a/drivers/clk/sophgo/clk-sophgo-sg2042.c b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> new file mode 100644
> index 000000000000..e64e1cb4872b
> --- /dev/null
> +++ b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> @@ -0,0 +1,1870 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sophgo SG2042 Clock Generator Driver
> + *
> + * Copyright (C) 2024 Sophgo Technology Inc. All rights reserved.
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/platform_device.h>
> +#include <asm/div64.h>
> +
> +/*
> + * The clock of SG2042 is composed of three parts.
> + * The registers of these three parts of the clock are scattered in three
> + * different memory address spaces:
> + * - pll clocks
> + * - gate clocks for RP subsystem
> + * - div/mux, and gate clocks working for other subsystem than RP subsystem
> + */
> +#include <dt-bindings/clock/sophgo,sg2042-pll.h>
> +#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
> +#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
> +
> +/* Registers defined in SYS_CTRL */
> +#define R_PLL_BEGIN		0xC0
> +#define R_PLL_STAT		(0xC0 - R_PLL_BEGIN)
> +#define R_PLL_CLKEN_CONTROL	(0xC4 - R_PLL_BEGIN)
> +#define R_MPLL_CONTROL		(0xE8 - R_PLL_BEGIN)
> +#define R_FPLL_CONTROL		(0xF4 - R_PLL_BEGIN)
> +#define R_DPLL0_CONTROL		(0xF8 - R_PLL_BEGIN)
> +#define R_DPLL1_CONTROL		(0xFC - R_PLL_BEGIN)
> +
> +#define R_SYSGATE_BEGIN		0x0368
> +#define R_RP_RXU_CLK_ENABLE	(0x0368 - R_SYSGATE_BEGIN)
> +#define R_MP0_STATUS_REG	(0x0380 - R_SYSGATE_BEGIN)
> +#define R_MP0_CONTROL_REG	(0x0384 - R_SYSGATE_BEGIN)
> +#define R_MP1_STATUS_REG	(0x0388 - R_SYSGATE_BEGIN)
> +#define R_MP1_CONTROL_REG	(0x038C - R_SYSGATE_BEGIN)
> +#define R_MP2_STATUS_REG	(0x0390 - R_SYSGATE_BEGIN)
> +#define R_MP2_CONTROL_REG	(0x0394 - R_SYSGATE_BEGIN)
> +#define R_MP3_STATUS_REG	(0x0398 - R_SYSGATE_BEGIN)
> +#define R_MP3_CONTROL_REG	(0x039C - R_SYSGATE_BEGIN)
> +#define R_MP4_STATUS_REG	(0x03A0 - R_SYSGATE_BEGIN)
> +#define R_MP4_CONTROL_REG	(0x03A4 - R_SYSGATE_BEGIN)
> +#define R_MP5_STATUS_REG	(0x03A8 - R_SYSGATE_BEGIN)
> +#define R_MP5_CONTROL_REG	(0x03AC - R_SYSGATE_BEGIN)
> +#define R_MP6_STATUS_REG	(0x03B0 - R_SYSGATE_BEGIN)
> +#define R_MP6_CONTROL_REG	(0x03B4 - R_SYSGATE_BEGIN)
> +#define R_MP7_STATUS_REG	(0x03B8 - R_SYSGATE_BEGIN)
> +#define R_MP7_CONTROL_REG	(0x03BC - R_SYSGATE_BEGIN)
> +#define R_MP8_STATUS_REG	(0x03C0 - R_SYSGATE_BEGIN)
> +#define R_MP8_CONTROL_REG	(0x03C4 - R_SYSGATE_BEGIN)
> +#define R_MP9_STATUS_REG	(0x03C8 - R_SYSGATE_BEGIN)
> +#define R_MP9_CONTROL_REG	(0x03CC - R_SYSGATE_BEGIN)
> +#define R_MP10_STATUS_REG	(0x03D0 - R_SYSGATE_BEGIN)
> +#define R_MP10_CONTROL_REG	(0x03D4 - R_SYSGATE_BEGIN)
> +#define R_MP11_STATUS_REG	(0x03D8 - R_SYSGATE_BEGIN)
> +#define R_MP11_CONTROL_REG	(0x03DC - R_SYSGATE_BEGIN)
> +#define R_MP12_STATUS_REG	(0x03E0 - R_SYSGATE_BEGIN)
> +#define R_MP12_CONTROL_REG	(0x03E4 - R_SYSGATE_BEGIN)
> +#define R_MP13_STATUS_REG	(0x03E8 - R_SYSGATE_BEGIN)
> +#define R_MP13_CONTROL_REG	(0x03EC - R_SYSGATE_BEGIN)
> +#define R_MP14_STATUS_REG	(0x03F0 - R_SYSGATE_BEGIN)
> +#define R_MP14_CONTROL_REG	(0x03F4 - R_SYSGATE_BEGIN)
> +#define R_MP15_STATUS_REG	(0x03F8 - R_SYSGATE_BEGIN)
> +#define R_MP15_CONTROL_REG	(0x03FC - R_SYSGATE_BEGIN)
> +
> +/* Registers defined in CLOCK */
> +#define R_CLKENREG0		0x00
> +#define R_CLKENREG1		0x04
> +#define R_CLKSELREG0		0x20
> +#define R_CLKDIVREG0		0x40
> +#define R_CLKDIVREG1		0x44
> +#define R_CLKDIVREG2		0x48
> +#define R_CLKDIVREG3		0x4C
> +#define R_CLKDIVREG4		0x50
> +#define R_CLKDIVREG5		0x54
> +#define R_CLKDIVREG6		0x58
> +#define R_CLKDIVREG7		0x5C
> +#define R_CLKDIVREG8		0x60
> +#define R_CLKDIVREG9		0x64
> +#define R_CLKDIVREG10		0x68
> +#define R_CLKDIVREG11		0x6C
> +#define R_CLKDIVREG12		0x70
> +#define R_CLKDIVREG13		0x74
> +#define R_CLKDIVREG14		0x78
> +#define R_CLKDIVREG15		0x7C
> +#define R_CLKDIVREG16		0x80
> +#define R_CLKDIVREG17		0x84
> +#define R_CLKDIVREG18		0x88
> +#define R_CLKDIVREG19		0x8C
> +#define R_CLKDIVREG20		0x90
> +#define R_CLKDIVREG21		0x94
> +#define R_CLKDIVREG22		0x98
> +#define R_CLKDIVREG23		0x9C
> +#define R_CLKDIVREG24		0xA0
> +#define R_CLKDIVREG25		0xA4
> +#define R_CLKDIVREG26		0xA8
> +#define R_CLKDIVREG27		0xAC
> +#define R_CLKDIVREG28		0xB0
> +#define R_CLKDIVREG29		0xB4
> +#define R_CLKDIVREG30		0xB8
> +
> +/* All following shift value are the same for all DIV registers */
> +#define SHIFT_DIV_RESET_CTRL	0
> +#define SHIFT_DIV_FACTOR_SEL	3
> +#define SHIFT_DIV_FACTOR	16
> +
> +/**
> + * struct sg2042_clk_data - Common data of clock-controller
> + * @iobase: base address of clock-controller
> + * @onecell_data: used for adding providers.
> + *
> + * Note: this structure will be used both by clkgen & sysclk.
> + */
> +struct sg2042_clk_data {
> +	void __iomem *iobase;
> +	struct clk_hw_onecell_data onecell_data;
> +};
> +
> +/**
> + * struct sg2042_pll_clock - PLL clock
> + * @hw:				clk_hw for initialization
> + * @id:				used to map clk_onecell_data
> + * @base:			used for readl/writel.
> + *				**NOTE**: PLL registers are all in SYS_CTRL!
> + * @lock:			spinlock to protect register access, modification
> + *				of frequency can only be served one at the time.
> + * @offset_status:		offset of pll status registers
> + * @offset_enable:		offset of pll enable registers
> + * @offset_ctrl:		offset of pll control registers
> + * @shift_status_lock:		shift of XXX_LOCK in pll status register
> + * @shift_status_updating:	shift of UPDATING_XXX in pll status register
> + * @shift_enable:		shift of XXX_CLK_EN in pll enable register
> + */
> +struct sg2042_pll_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +	void __iomem *base;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_status;
> +	u32 offset_enable;
> +	u32 offset_ctrl;
> +	u8 shift_status_lock;
> +	u8 shift_status_updating;
> +	u8 shift_enable;
> +};
> +
> +#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
> +
> +/**
> + * struct sg2042_divider_clock - Divider clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @reg:		used for readl/writel.
> + *			**NOTE**: DIV registers are ALL in CLOCK!
> + * @lock:		spinlock to protect register access, modification of
> + *			frequency can only be served one at the time
> + * @offset_ctrl:	offset of divider control registers
> + * @shift:		shift of "Clock Divider Factor" in divider control register
> + * @width:		width of "Clock Divider Factor" in divider control register
> + * @div_flags:		private flags for this clock, not for framework-specific
> + * @initval:		In the divider control register, we can configure whether
> + *			to use the value of "Clock Divider Factor" or just use
> + *			the initial value pre-configured by IC. BIT[3] controls
> + *			this and by default (value is 0), means initial value
> + *			is used.
> + *			**NOTE** that we cannot read the initial value (default
> + *			value when poweron) and default value of "Clock Divider
> + *			Factor" is zero, which I think is a hardware design flaw
> + *			and should be sync-ed with the initial value. So in
> + *			software we have to add a configuration item (initval)
> + *			to manually configure this value and use it when BIT[3]
> + *			is zero.
> + */
> +struct sg2042_divider_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	void __iomem *reg;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_ctrl;
> +	u8 shift;
> +	u8 width;
> +	u8 div_flags;
> +	u32 initval;
> +};
> +
> +#define to_sg2042_clk_divider(_hw)	\
> +	container_of(_hw, struct sg2042_divider_clock, hw)
> +
> +/**
> + * struct sg2042_gate_clock - Gate clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_enable:	offset of gate enable registers
> + * @bit_idx:		which bit in the register controls gating of this clock
> + */
> +struct sg2042_gate_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_enable;
> +	u8 bit_idx;
> +};
> +
> +/**
> + * struct sg2042_mux_clock - Mux clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_select:	offset of mux selection registers
> + *			**NOTE**: MUX registers are ALL in CLOCK!
> + * @shift:		shift of "Clock Select" in mux selection register
> + * @width:		width of "Clock Select" in mux selection register
> + * @clk_nb:		used for notification
> + * @original_index:	set by notifier callback
> + */
> +struct sg2042_mux_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_select;
> +	u8 shift;
> +	u8 width;
> +
> +	struct notifier_block clk_nb;
> +	u8 original_index;
> +};
> +
> +#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb)
> +
> +#define KHZ 1000UL
> +#define MHZ (KHZ * KHZ)
> +
> +#define REFDIV_MIN 1
> +#define REFDIV_MAX 63
> +#define FBDIV_MIN 16
> +#define FBDIV_MAX 320
> +
> +#define PLL_FREF_SG2042 (25 * MHZ)
> +
> +#define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
> +#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
> +
> +#define PLL_FOUTVCO_MIN (800 * MHZ)
> +#define PLL_FOUTVCO_MAX (3200 * MHZ)
> +
> +struct sg2042_pll_ctrl {
> +	unsigned long freq;
> +	unsigned int fbdiv;
> +	unsigned int postdiv1;
> +	unsigned int postdiv2;
> +	unsigned int refdiv;
> +};
> +
> +#define PLLCTRL_FBDIV_SHIFT	16
> +#define PLLCTRL_FBDIV_MASK	(GENMASK(27, 16) >> PLLCTRL_FBDIV_SHIFT)
> +#define PLLCTRL_POSTDIV2_SHIFT	12
> +#define PLLCTRL_POSTDIV2_MASK	(GENMASK(14, 12) >> PLLCTRL_POSTDIV2_SHIFT)
> +#define PLLCTRL_POSTDIV1_SHIFT	8
> +#define PLLCTRL_POSTDIV1_MASK	(GENMASK(10, 8) >> PLLCTRL_POSTDIV1_SHIFT)
> +#define PLLCTRL_REFDIV_SHIFT	0
> +#define PLLCTRL_REFDIV_MASK	(GENMASK(5, 0) >> PLLCTRL_REFDIV_SHIFT)
> +
> +static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
> +{
> +	return ((ctrl->fbdiv & PLLCTRL_FBDIV_MASK) << PLLCTRL_FBDIV_SHIFT) |
> +	       ((ctrl->postdiv2 & PLLCTRL_POSTDIV2_MASK) << PLLCTRL_POSTDIV2_SHIFT) |
> +	       ((ctrl->postdiv1 & PLLCTRL_POSTDIV1_MASK) << PLLCTRL_POSTDIV1_SHIFT) |
> +	       ((ctrl->refdiv & PLLCTRL_REFDIV_MASK) << PLLCTRL_REFDIV_SHIFT);
> +}
> +
> +static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
> +					  struct sg2042_pll_ctrl *ctrl)
> +{
> +	ctrl->fbdiv = (reg_value >> PLLCTRL_FBDIV_SHIFT) & PLLCTRL_FBDIV_MASK;
> +	ctrl->refdiv = (reg_value >> PLLCTRL_REFDIV_SHIFT) & PLLCTRL_REFDIV_MASK;
> +	ctrl->postdiv1 = (reg_value >> PLLCTRL_POSTDIV1_SHIFT) & PLLCTRL_POSTDIV1_MASK;
> +	ctrl->postdiv2 = (reg_value >> PLLCTRL_POSTDIV2_SHIFT) & PLLCTRL_POSTDIV2_MASK;
> +}
> +
> +static inline int sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
> +{
> +	u32 value;
> +
> +	if (en) {
> +		/* wait pll lock */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      ((value >> pll->shift_status_lock) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s not locked\n", pll->hw.init->name);
> +
> +		/* wait pll updating */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      !((value >> pll->shift_status_updating) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s still updating\n", pll->hw.init->name);
> +
> +		/* enable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value | (1 << pll->shift_enable), pll->base + pll->offset_enable);
> +	} else {
> +		/* disable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value & (~(1 << pll->shift_enable)), pll->base + pll->offset_enable);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * sg2042_pll_recalc_rate() - Calculate rate for plls
> + * @reg_value: current register value
> + * @parent_rate: parent frequency
> + *
> + * This function is used to calculate below "rate" in equation
> + * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
> + *      = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
> + *
> + * Return: The rate calculated.
> + */
> +static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
> +					    unsigned long parent_rate)
> +{
> +	struct sg2042_pll_ctrl ctrl_table;
> +	u64 numerator, denominator;
> +
> +	sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
> +
> +	numerator = parent_rate * ctrl_table.fbdiv;
> +	denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
> +	do_div(numerator, denominator);
> +	return numerator;
> +}
> +
> +/**
> + * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
> + * look up the postdiv1_2 table to get the closest postdiiv combination.
> + * @rate: FOUTPOSTDIV
> + * @prate: parent rate, i.e. FREF
> + * @fbdiv: FBDIV
> + * @refdiv: REFDIV
> + * @postdiv1: POSTDIV1, output
> + * @postdiv2: POSTDIV2, output
> + *
> + * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
> + * for example:
> + * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
> + *
> + * See TRM:
> + * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
> + * So we get following formula to get POSTDIV1 and POSTDIV2:
> + * POSTDIV = (prate/REFDIV) x FBDIV/rate
> + * above POSTDIV = POSTDIV1*POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument, which means Failed to get the postdivs.
> + */
> +static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
> +				      unsigned long prate,
> +				      unsigned int fbdiv,
> +				      unsigned int refdiv,
> +				      unsigned int *postdiv1,
> +				      unsigned int *postdiv2)
> +{
> +	int index;
> +	u64 tmp0;
> +
> +	/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
> +	#define	POSTDIV_RESULT_INDEX	2
> +
> +	static const int postdiv1_2[][3] = {
> +		{2, 4,  8}, {3, 3,  9}, {2, 5, 10}, {2, 6, 12},
> +		{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
> +		{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
> +		{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
> +		{6, 7, 42}, {7, 7, 49}
> +	};
> +
> +	/* prate/REFDIV and result save to tmp0 */
> +	tmp0 = prate;
> +	do_div(tmp0, refdiv);
> +
> +	/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
> +	tmp0 *= fbdiv;
> +
> +	/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
> +	do_div(tmp0, rate);
> +
> +	/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
> +	if (tmp0 <= 7) {
> +		/* (div1 * div2) <= 7, no need to use array search */
> +		*postdiv1 = tmp0;
> +		*postdiv2 = 1;
> +		return 0;
> +	}
> +
> +	/* (div1 * div2) > 7, use array search */
> +	for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
> +		if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
> +			continue;
> +		} else {
> +			/* found it */
> +			*postdiv1 = postdiv1_2[index][1];
> +			*postdiv2 = postdiv1_2[index][0];
> +			return 0;
> +		}
> +	}
> +	pr_warn("%s can not find in postdiv array!\n", __func__);
> +	return -EINVAL;
> +}
> +
> +/**
> + * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
> + * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
> + * register.
> + * @req_rate: expected output clock rate, i.e. FOUTPISTDIV
> + * @parent_rate: input parent clock rate, i.e. FREF
> + * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument
> + */
> +static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
> +				      unsigned long req_rate,
> +				      unsigned long parent_rate)
> +{
> +	int ret;
> +	unsigned int fbdiv, refdiv, postdiv1, postdiv2;
> +	unsigned long foutpostdiv;
> +	u64 tmp;
> +	u64 foutvco;
> +
> +	if (parent_rate != PLL_FREF_SG2042) {
> +		pr_err("INVALID FREF: %ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
> +		pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
> +		return -EINVAL;
> +	}
> +
> +	memset(best, 0, sizeof(struct sg2042_pll_ctrl));
> +
> +	for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
> +		/* required by hardware: FREF/REFDIV must > 10 */
> +		tmp = parent_rate;
> +		do_div(tmp, refdiv);
> +		if (tmp <= 10)
> +			continue;
> +
> +		for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
> +			/*
> +			 * FOUTVCO = FREF*FBDIV/REFDIV validation
> +			 * required by hardware, FOUTVCO must [800MHz, 3200MHz]
> +			 */
> +			foutvco = parent_rate * fbdiv;
> +			do_div(foutvco, refdiv);
> +			if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
> +				continue;
> +
> +			ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
> +							 fbdiv, refdiv,
> +							 &postdiv1, &postdiv2);
> +			if (ret)
> +				continue;
> +
> +			/*
> +			 * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
> +			 *             = FOUTVCO/(POSTDIV1*POSTDIV2)
> +			 */
> +			tmp = foutvco;
> +			do_div(tmp, (postdiv1 * postdiv2));
> +			foutpostdiv = (unsigned long)tmp;
> +			/* Iterative to approach the expected value */
> +			if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
> +				best->freq = foutpostdiv;
> +				best->refdiv = refdiv;
> +				best->fbdiv = fbdiv;
> +				best->postdiv1 = postdiv1;
> +				best->postdiv2 = postdiv2;
> +				if (foutpostdiv == req_rate)
> +					return 0;
> +			}
> +			continue;
> +		}
> +	}
> +
> +	if (best->freq == 0)
> +		return -EINVAL;
> +	else
> +		return 0;
> +}
> +
> +/**
> + * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
> + * @hw: ccf use to hook get sg2042_pll_clock
> + * @parent_rate: parent rate
> + *
> + * The is function will be called through clk_get_rate
> + * and return current rate after decoding reg value
> + *
> + * Return: Current rate recalculated.
> + */
> +static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	u32 value;
> +	unsigned long rate;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	value = readl(pll->base + pll->offset_ctrl);
> +	rate = sg2042_pll_recalc_rate(value, parent_rate);
> +
> +	pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), rate);
> +	return rate;
> +}
> +
> +static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
> +				      unsigned long req_rate,
> +				      unsigned long *prate)
> +{
> +	unsigned int value;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	long proper_rate;
> +	int ret;
> +
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
> +	if (ret) {
> +		proper_rate = 0;
> +		goto out;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +	proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
> +
> +out:
> +	pr_debug("--> %s: pll_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), proper_rate);
> +	return proper_rate;
> +}
> +
> +static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
> +					 struct clk_rate_request *req)
> +{
> +	req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
> +					      &req->best_parent_rate);
> +	pr_debug("--> %s: pll_determine_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), req->rate);
> +	return 0;
> +}
> +
> +static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
> +				   unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	unsigned long flags;
> +	u32 value;
> +	int ret = 0;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	spin_lock_irqsave(pll->lock, flags);
> +	if (sg2042_pll_enable(pll, 0)) {
> +		pr_warn("Can't disable pll(%s), status error\n", pll->hw.init->name);
> +		goto out;
> +	}
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
> +	if (ret) {
> +		pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
> +		goto out2;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +
> +	/* write the value to top register */
> +	writel(value, pll->base + pll->offset_ctrl);
> +
> +out2:
> +	sg2042_pll_enable(pll, 1);
> +out:
> +	spin_unlock_irqrestore(pll->lock, flags);
> +
> +	pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
> +		 clk_hw_get_name(hw), value);
> +	return ret;
> +}
> +
> +static const struct clk_ops sg2042_clk_pll_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +	.determine_rate = sg2042_clk_pll_determine_rate,
> +	.set_rate = sg2042_clk_pll_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_pll_ro_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +};
> +
> +static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,
> +						    unsigned long parent_rate)
> +{
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +	u32 val;
> +	unsigned long ret_rate;
> +
> +	if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +		val = divider->initval;
> +	} else {
> +		val = readl(divider->reg) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +	}
> +
> +	ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL,
> +				       divider->div_flags, divider->width);
> +
> +	pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static long sg2042_clk_divider_round_rate(struct clk_hw *hw,
> +					  unsigned long rate,
> +					  unsigned long *prate)
> +{
> +	u32 bestdiv;
> +	unsigned long ret_rate;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->div_flags & CLK_DIVIDER_READ_ONLY) {
> +		if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +			bestdiv = divider->initval;
> +		} else {
> +			bestdiv = readl(divider->reg) >> divider->shift;
> +			bestdiv &= clk_div_mask(divider->width);
> +		}
> +		ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
> +	} else {
> +		ret_rate = divider_round_rate(hw, rate, prate, NULL,
> +					      divider->width, divider->div_flags);
> +	}
> +
> +	pr_debug("--> %s: divider_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static int sg2042_clk_divider_set_rate(struct clk_hw *hw,
> +				       unsigned long rate,
> +				       unsigned long parent_rate)
> +{
> +	u32 value;
> +	u32 val, val2;
> +	unsigned long flags = 0;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	value = divider_get_val(rate, parent_rate, NULL,
> +				divider->width, divider->div_flags);
> +
> +	if (divider->lock)
> +		spin_lock_irqsave(divider->lock, flags);
> +	else
> +		__acquire(divider->lock);
> +
> +	/*
> +	 * The sequence of clock frequency modification is:
> +	 * Assert to reset divider.
> +	 * Modify the value of Clock Divide Factor (and High Wide if needed).
> +	 * De-assert to restore divided clock with new frequency.
> +	 */
> +	val = readl(divider->reg);
> +
> +	/* assert */
> +	val &= ~BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +		val = clk_div_mask(divider->width) << (divider->shift + 16);
> +	} else {
> +		val = readl(divider->reg);
> +		val &= ~(clk_div_mask(divider->width) << divider->shift);
> +	}
> +	val |= value << divider->shift;
> +	val |= BIT(SHIFT_DIV_FACTOR_SEL);
> +	writel(val, divider->reg);
> +	val2 = val;
> +
> +	/* de-assert */
> +	val |= BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->lock)
> +		spin_unlock_irqrestore(divider->lock, flags);
> +	else
> +		__release(divider->lock);
> +
> +	pr_debug("--> %s: divider_set_rate: register val = 0x%x\n",
> +		 clk_hw_get_name(hw), val2);
> +	return 0;
> +}
> +
> +static const struct clk_ops sg2042_clk_divider_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +	.set_rate = sg2042_clk_divider_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_divider_ro_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +};
> +
> +/*
> + * Clock initialization macro naming rules:
> + * FW: use CLK_HW_INIT_FW_NAME
> + * HW: use CLK_HW_INIT_HW
> + * HWS: use CLK_HW_INIT_HWS
> + * RO: means Read-Only
> + */
> +#define SG2042_PLL_FW(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}
> +
> +#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ro_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}
> +
> +static struct sg2042_pll_clock sg2042_pll_clks[] = {
> +	SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main",
> +		      R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_MPLL_CONTROL, 0),
> +	SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_FPLL_CONTROL, 3),
> +	SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL0_CONTROL, 4),
> +	SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL1_CONTROL, 5),
> +};
> +
> +#define SG2042_DIV_FW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_FW_RO(_id, _name, _parent,				\
> +		  _r_ctrl, _shift, _width,				\
> +		  _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW_RO(_id, _name, _parent,				\
> +			 _r_ctrl, _shift, _width,			\
> +			 _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS(_id, _name, _parent,				\
> +		       _r_ctrl, _shift, _width,				\
> +		       _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\
> +			  _r_ctrl, _shift, _width,			\
> +			  _div_flag, _initval) {			\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\
> +			_r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HWS(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HW(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_FW_NAME(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \
> +		.hw.init = CLK_HW_INIT_PARENTS_HW(		\
> +				_name,				\
> +				_parents,			\
> +				NULL,				\
> +				_flags),			\
> +		.id = _id,					\
> +		.offset_select = _r_select,			\
> +		.shift = _shift,				\
> +		.width = _width,				\
> +	}
> +
> +/*
> + * Clock items in the array are sorted according to the clock-tree diagram,
> + * from top to bottom, from upstream to downstream. Read TRM for details.
> + */
> +
> +/* updated during probe/registration */
> +static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = {
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG27, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG28, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG29, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG30, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0,
> +		       "clk_gate_rp_cpu_normal_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG0, 4),
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1,
> +		       "clk_gate_rp_cpu_normal_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG1, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG25, 4),
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG26, 4),
> +};
> +
> +#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_1[] = {
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0,
> +			  "clk_div_ddr01_0", clk_gate_ddr01_div0,
> +			  R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1,
> +			  "clk_div_ddr01_1", clk_gate_ddr01_div1,
> +			  R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0,
> +			  "clk_div_ddr23_0", clk_gate_ddr23_div0,
> +			  R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1,
> +			  "clk_div_ddr23_1", clk_gate_ddr23_div1,
> +			  R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0,
> +		       "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0,
> +		       R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1,
> +		       "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1,
> +		       R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0,
> +		       "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0,
> +		       R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1,
> +		       "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1,
> +		       R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1),
> +};
> +
> +/*
> + * Note: regarding names for mux clock, "0/1" or "div0/div1" means the
> + * first/second parent input source, not the register value.
> + * For example:
> + * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and
> + * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",
> + * they are both controlled by register CLKDIVREG27;
> + * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and
> + * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1",
> + * they are both controlled by register CLKDIVREG28;
> + * While for register value of mux selection, use Clock Select for DDR01’s clock
> + * as example, see CLKSELREG0, bit[2].
> + * 1: Select in_dpll0_clk as clock source, correspondng to the parent input
> + *    source from "clk_div_ddr01_0".
> + * 0: Select in_fpll_clk as clock source, corresponding to the parent input
> + *    source from "clk_div_ddr01_1".
> + * So we need a table to define the array of register values corresponding to
> + * the parent index and tell CCF about this when registering mux clock.
> + */
> +static const u32 sg2042_mux_table[] = {1, 0};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_ddr01_0		(&sg2042_div_clks_level_1[0].hw)
> +#define clk_div_ddr01_1		(&sg2042_div_clks_level_1[1].hw)
> +#define clk_div_ddr23_0		(&sg2042_div_clks_level_1[2].hw)
> +#define clk_div_ddr23_1		(&sg2042_div_clks_level_1[3].hw)
> +#define clk_div_rp_cpu_normal_0	(&sg2042_div_clks_level_1[4].hw)
> +#define clk_div_rp_cpu_normal_1	(&sg2042_div_clks_level_1[5].hw)
> +#define clk_div_axi_ddr_0	(&sg2042_div_clks_level_1[6].hw)
> +#define clk_div_axi_ddr_1	(&sg2042_div_clks_level_1[7].hw)
> +
> +static const struct clk_hw *clk_mux_ddr01_p[] = {
> +	clk_div_ddr01_0,
> +	clk_div_ddr01_1,
> +};
> +
> +static const struct clk_hw *clk_mux_ddr23_p[] = {
> +	clk_div_ddr23_0,
> +	clk_div_ddr23_1,
> +};
> +
> +static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = {
> +	clk_div_rp_cpu_normal_0,
> +	clk_div_rp_cpu_normal_1,
> +};
> +
> +static const struct clk_hw *clk_mux_axi_ddr_p[] = {
> +	clk_div_axi_ddr_0,
> +	clk_div_axi_ddr_1,
> +};
> +
> +/* Mux clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_mux_ddr01[] = { NULL };
> +static const struct clk_hw *clk_mux_ddr23[] = { NULL };
> +static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_mux_axi_ddr[] = { NULL };
> +
> +static struct sg2042_mux_clock sg2042_mux_clks[] = {
> +	SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 2, 1),
> +	SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 3, 1),
> +	SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 0, 1),
> +	SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 1, 1),
> +};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_top_rp_cmn_div2	(&sg2042_div_clks_level_2[0].hw)
> +#define clk_div_50m_a53		(&sg2042_div_clks_level_2[1].hw)
> +#define clk_div_timer1		(&sg2042_div_clks_level_2[2].hw)
> +#define clk_div_timer2		(&sg2042_div_clks_level_2[3].hw)
> +#define clk_div_timer3		(&sg2042_div_clks_level_2[4].hw)
> +#define clk_div_timer4		(&sg2042_div_clks_level_2[5].hw)
> +#define clk_div_timer5		(&sg2042_div_clks_level_2[6].hw)
> +#define clk_div_timer6		(&sg2042_div_clks_level_2[7].hw)
> +#define clk_div_timer7		(&sg2042_div_clks_level_2[8].hw)
> +#define clk_div_timer8		(&sg2042_div_clks_level_2[9].hw)
> +#define clk_div_uart_500m	(&sg2042_div_clks_level_2[10].hw)
> +#define clk_div_ahb_lpc		(&sg2042_div_clks_level_2[11].hw)
> +#define clk_div_efuse		(&sg2042_div_clks_level_2[12].hw)
> +#define clk_div_tx_eth0		(&sg2042_div_clks_level_2[13].hw)
> +#define clk_div_ptp_ref_i_eth0	(&sg2042_div_clks_level_2[14].hw)
> +#define clk_div_ref_eth0	(&sg2042_div_clks_level_2[15].hw)
> +#define clk_div_emmc		(&sg2042_div_clks_level_2[16].hw)
> +#define clk_div_sd		(&sg2042_div_clks_level_2[17].hw)
> +#define clk_div_top_axi0	(&sg2042_div_clks_level_2[18].hw)
> +#define clk_div_100k_emmc	(&sg2042_div_clks_level_2[19].hw)
> +#define clk_div_100k_sd		(&sg2042_div_clks_level_2[20].hw)
> +#define clk_div_gpio_db		(&sg2042_div_clks_level_2[21].hw)
> +#define clk_div_top_axi_hsperi	(&sg2042_div_clks_level_2[22].hw)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_2[] = {
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2,
> +		       "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal,
> +		       R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll",
> +		      R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20),
> +	/* downstream of div_50m_a53 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53,
> +		      R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53,
> +		      R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53,
> +		      R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53,
> +		      R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53,
> +		      R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53,
> +		      R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53,
> +		      R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53,
> +		      R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1),
> +
> +	/*
> +	 * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too
> +	 * narrow for us to produce 115200. Use UART internal divider directly.
> +	 */
> +	SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll",
> +			 R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll",
> +		      R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll",
> +		      R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll",
> +		      R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0,
> +		      "clk_div_ptp_ref_i_eth0", "fpll",
> +		      R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll",
> +		      R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll",
> +		      R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll",
> +		      R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll",
> +		      R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10),
> +	/* downstream of div_top_axi0 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0,
> +		      R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0,
> +		      R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0,
> +		      R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI,
> +		      "clk_div_top_axi_hsperi", "fpll",
> +		      R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4),
> +};
> +
> +/* Gate clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = {
> +	SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 14),
> +
> +	SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 15),
> +
> +	SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL,
> +			"clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG0, 0),
> +
> +	SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 13),
> +
> +	/* upon are gate clocks directly downstream of muxes */
> +
> +	/* downstream of clk_div_top_rp_cmn_div2 */
> +	SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2,
> +		       "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2),
> +	SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2,
> +			CLK_SET_RATE_PARENT, R_CLKENREG1, 10),
> +
> +	/*
> +	 * downstream of clk_gate_rp_cpu_normal
> +	 *
> +	 * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal
> +	 * and clk_gate_axi_pcie0/clk_gate_axi_pcie1.
> +	 * But the 1/2 DIV is fixed and no configurable register exported, so
> +	 * when reading from these two clocks, the rate value are still the
> +	 * same as that of clk_gate_rp_cpu_normal, it's not correct.
> +	 * This just affects the value read.
> +	 */
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0,
> +			"clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1,
> +			"clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),
> +
> +	/* downstream of div_50m_a53 */
> +	SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1),
> +	SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 12),
> +	SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 13),
> +	SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 14),
> +	SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 15),
> +	SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 16),
> +	SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 17),
> +	SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 18),
> +	SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 19),
> +
> +	/* gate clocks downstream from div clocks one-to-one */
> +	SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4),
> +	SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 7),
> +	SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 20),
> +	SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 30),
> +	SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0,
> +		       "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 0),
> +	SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 1),
> +	SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 3),
> +	SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 6),
> +
> +	/* downstream of clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 8),
> +	SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 9),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 10),
> +	SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 11),
> +	SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 21),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 22),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR,
> +		       "clk_gate_apb_gpio_intr", clk_div_top_axi0,
> +		       CLK_IS_CRITICAL, R_CLKENREG0, 23),
> +	SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 26),
> +	SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 27),
> +	SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 28),
> +	SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 29),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 11),
> +	/* downstream of DIV clocks which are sourced from clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 24),
> +	SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 4),
> +	SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 7),
> +
> +	/* downstream of clk_div_top_axi_hsperi */
> +	SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI,
> +		       "clk_gate_sysdma_axi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 3),
> +	SG2042_GATE_HW(GATE_CLK_APB_UART,
> +		       "clk_gate_apb_uart", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 5),
> +	SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C,
> +		       "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 6),
> +	SG2042_GATE_HW(GATE_CLK_APB_SPI,
> +		       "clk_gate_apb_spi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 25),
> +	SG2042_GATE_HW(GATE_CLK_AXI_ETH0,
> +		       "clk_gate_axi_eth0", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 31),
> +	SG2042_GATE_HW(GATE_CLK_AXI_EMMC,
> +		       "clk_gate_axi_emmc", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 2),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SD,
> +		       "clk_gate_axi_sd", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 5),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI,
> +		       "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 12),
> +};
> +
> +/*
> + * Gate clocks for RP subsystem (including the MP subsystem), which control
> + * registers are defined in SYS_CTRL.
> + */
> +static const struct sg2042_gate_clock sg2042_gate_rp[] = {
> +	/* downstream of clk_gate_rp_cpu_normal about rxu */
> +	SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 0),
> +	SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 1),
> +	SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 2),
> +	SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 3),
> +	SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 4),
> +	SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 5),
> +	SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 6),
> +	SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 7),
> +	SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 8),
> +	SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 9),
> +	SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 10),
> +	SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 11),
> +	SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 12),
> +	SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 13),
> +	SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 14),
> +	SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 15),
> +	SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 16),
> +	SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 17),
> +	SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 18),
> +	SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 19),
> +	SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 20),
> +	SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 21),
> +	SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 22),
> +	SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 23),
> +	SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 24),
> +	SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 25),
> +	SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 26),
> +	SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 27),
> +	SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 28),
> +	SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 29),
> +	SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 30),
> +	SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 31),
> +
> +	/* downstream of clk_gate_rp_cpu_normal about mp */
> +	SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0),
> +};
> +
> +static DEFINE_SPINLOCK(sg2042_clk_lock);
> +
> +static int sg2042_clk_register_plls(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_pll_clock pll_clks[],
> +				    int num_pll_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_pll_clock *pll;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_pll_clks; i++) {
> +		pll = &pll_clks[i];
> +		/* assign these for ops usage during registration */
> +		pll->base = clk_data->iobase;
> +		pll->lock = &sg2042_clk_lock;
> +
> +		hw = &pll->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", pll->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[pll->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_divs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_divider_clock div_clks[],
> +				    int num_div_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_divider_clock *div;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_div_clks; i++) {
> +		div = &div_clks[i];
> +
> +		if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +			if (div->width + div->shift > 16) {
> +				pr_warn("divider value exceeds LOWORD field\n");
> +				ret = -EINVAL;
> +				break;
> +			}
> +		}
> +
> +		div->reg = clk_data->iobase + div->offset_ctrl;
> +		div->lock = &sg2042_clk_lock;
> +
> +		hw = &div->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", div->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[div->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates(struct device *dev,
> +				     struct sg2042_clk_data *clk_data,
> +				     const struct sg2042_gate_clock gate_clks[],
> +				     int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = __devm_clk_hw_register_gate
> +			(dev,
> +			 NULL,
> +			 gate->hw.init->name,
> +			 NULL,
> +			 gate->hw.init->parent_hws[0],
> +			 NULL,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_RP_CPU_NORMAL:
> +			*clk_gate_rp_cpu_normal = hw;
> +			break;
> +		case GATE_CLK_TOP_RP_CMN_DIV2:
> +			*clk_gate_top_rp_cmn_div2 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates_fw(struct device *dev,
> +					struct sg2042_clk_data *clk_data,
> +					const struct sg2042_gate_clock gate_clks[],
> +					int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = devm_clk_hw_register_gate_parent_data
> +			(dev,
> +			 gate->hw.init->name,
> +			 gate->hw.init->parent_data,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_DDR01_DIV0:
> +			*clk_gate_ddr01_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR01_DIV1:
> +			*clk_gate_ddr01_div1 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV0:
> +			*clk_gate_ddr23_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV1:
> +			*clk_gate_ddr23_div1 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV0:
> +			*clk_gate_rp_cpu_normal_div0 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV1:
> +			*clk_gate_rp_cpu_normal_div1 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV0:
> +			*clk_gate_axi_ddr_div0 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV1:
> +			*clk_gate_axi_ddr_div1 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_mux_notifier_cb(struct notifier_block *nb,
> +				  unsigned long event,
> +				  void *data)
> +{
> +	int ret = 0;
> +	struct clk_notifier_data *ndata = data;
> +	struct clk_hw *hw = __clk_get_hw(ndata->clk);
> +	const struct clk_ops *ops = &clk_mux_ops;
> +	struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb);
> +
> +	/* To switch to fpll before changing rate and restore after that */
> +	if (event == PRE_RATE_CHANGE) {
> +		mux->original_index = ops->get_parent(hw);
> +
> +		/*
> +		 * "1" is the array index of the second parent input source of
> +		 * mux. For SG2042, it's fpll for all mux clocks.
> +		 * "0" is the array index of the frist parent input source of
> +		 * mux, For SG2042, it's mpll.
> +		 * FIXME, any good idea to avoid magic number?
> +		 */
> +		if (mux->original_index == 0)
> +			ret = ops->set_parent(hw, 1);
> +	} else if (event == POST_RATE_CHANGE) {
> +		ret = ops->set_parent(hw, mux->original_index);
> +	}
> +
> +	return notifier_from_errno(ret);
> +}
> +
> +static int sg2042_clk_register_muxs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_mux_clock mux_clks[],
> +				    int num_mux_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_mux_clock *mux;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_mux_clks; i++) {
> +		mux = &mux_clks[i];
> +
> +		hw = __devm_clk_hw_register_mux
> +			(dev,
> +			 NULL,
> +			 mux->hw.init->name,
> +			 mux->hw.init->num_parents,
> +			 NULL,
> +			 mux->hw.init->parent_hws,
> +			 NULL,
> +			 mux->hw.init->flags,
> +			 clk_data->iobase + mux->offset_select,
> +			 mux->shift,
> +			 BIT(mux->width) - 1,
> +			 0,
> +			 sg2042_mux_table,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", mux->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[mux->id] = hw;
> +
> +		/* Updated some clocks which takes the role of parent */
> +		switch (mux->id) {
> +		case MUX_CLK_DDR01:
> +			*clk_mux_ddr01 = hw;
> +			break;
> +		case MUX_CLK_DDR23:
> +			*clk_mux_ddr23 = hw;
> +			break;
> +		case MUX_CLK_RP_CPU_NORMAL:
> +			*clk_mux_rp_cpu_normal = hw;
> +			break;
> +		case MUX_CLK_AXI_DDR:
> +			*clk_mux_axi_ddr = hw;
> +			break;
> +		}
> +
> +		/*
> +		 * FIXME: Theoretically, we should set parent for the
> +		 * mux, but seems hardware has done this for us with
> +		 * default value, so we don't set parent again here.
> +		 */
> +
> +		if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {
> +			mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;
> +			ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb);
> +			if (ret) {
> +				pr_err("failed to register clock notifier for %s\n",
> +				       mux->hw.init->name);
> +				break;
> +			}
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_init_clkdata(struct platform_device *pdev,
> +			       int num_clks,
> +			       struct sg2042_clk_data **pp_clk_data)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +
> +	clk_data = devm_kzalloc(&pdev->dev,
> +				struct_size(clk_data, onecell_data.hws, num_clks),
> +				GFP_KERNEL);
> +	if (!clk_data)
> +		return -ENOMEM;
> +
> +	clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
> +	if (WARN_ON(IS_ERR(clk_data->iobase)))
> +		return PTR_ERR(clk_data->iobase);
> +
> +	clk_data->onecell_data.num = num_clks;
> +
> +	*pp_clk_data = clk_data;
> +
> +	return 0;
> +}
> +
> +static int sg2042_clkgen_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_div_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_mux_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 gates */
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,
> +					   sg2042_gate_clks_level_1,
> +					   ARRAY_SIZE(sg2042_gate_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,
> +				       ARRAY_SIZE(sg2042_div_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* mux */
> +	ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,
> +				       ARRAY_SIZE(sg2042_mux_clks));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,
> +				       ARRAY_SIZE(sg2042_div_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 gate */
> +	ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,
> +					ARRAY_SIZE(sg2042_gate_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_rpgate_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_gate_rp);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data, sg2042_gate_rp,
> +					   num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_pll_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_pll_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
> +				       num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id sg2042_clkgen_match[] = {
> +	{ .compatible = "sophgo,sg2042-clkgen" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_clkgen_driver = {
> +	.probe = sg2042_clkgen_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-clkgen",
> +		.of_match_table = sg2042_clkgen_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_clkgen_driver);
> +
> +static const struct of_device_id sg2042_rpgate_match[] = {
> +	{ .compatible = "sophgo,sg2042-rpgate" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_rpgate_driver = {
> +	.probe = sg2042_rpgate_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-rpgate",
> +		.of_match_table = sg2042_rpgate_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_rpgate_driver);
> +
> +static const struct of_device_id sg2042_pll_match[] = {
> +	{ .compatible = "sophgo,sg2042-pll" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_pll_driver = {
> +	.probe = sg2042_pll_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-pll",
> +		.of_match_table = sg2042_pll_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_pll_driver);

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v15 4/5] clk: sophgo: Add SG2042 clock driver
@ 2024-04-30  0:29     ` Chen Wang
  0 siblings, 0 replies; 18+ messages in thread
From: Chen Wang @ 2024-04-30  0:29 UTC (permalink / raw)
  To: Chen Wang, aou, chao.wei, conor, krzysztof.kozlowski+dt,
	mturquette, palmer, paul.walmsley, richardcochran, robh+dt,
	sboyd, devicetree, linux-clk, linux-kernel, linux-riscv,
	haijiao.liu, xiaoguang.xing, guoren, jszhang, inochiama,
	samuel.holland

Ping ~~~

Hi, Stephen,

Can you please take another look? I've improved the code based on your 
last comment.

Thanks,

Chen

On 2024/4/26 11:35, Chen Wang wrote:
> From: Chen Wang <unicorn_wang@outlook.com>
>
> Add a driver for the SOPHGO SG2042 clocks.
>
> Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
> ---
>   drivers/clk/Kconfig                    |    1 +
>   drivers/clk/Makefile                   |    1 +
>   drivers/clk/sophgo/Kconfig             |    8 +
>   drivers/clk/sophgo/Makefile            |    2 +
>   drivers/clk/sophgo/clk-sophgo-sg2042.c | 1870 ++++++++++++++++++++++++
>   5 files changed, 1882 insertions(+)
>   create mode 100644 drivers/clk/sophgo/Kconfig
>   create mode 100644 drivers/clk/sophgo/Makefile
>   create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 50af5fc7f570..bc28502ec3c9 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
>   source "drivers/clk/samsung/Kconfig"
>   source "drivers/clk/sifive/Kconfig"
>   source "drivers/clk/socfpga/Kconfig"
> +source "drivers/clk/sophgo/Kconfig"
>   source "drivers/clk/sprd/Kconfig"
>   source "drivers/clk/starfive/Kconfig"
>   source "drivers/clk/sunxi/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 14fa8d4ecc1f..4abe16c8ccdf 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
>   obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
>   obj-$(CONFIG_CLK_SIFIVE)		+= sifive/
>   obj-y					+= socfpga/
> +obj-y					+= sophgo/
>   obj-$(CONFIG_PLAT_SPEAR)		+= spear/
>   obj-y					+= sprd/
>   obj-$(CONFIG_ARCH_STI)			+= st/
> diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig
> new file mode 100644
> index 000000000000..82516adb0f97
> --- /dev/null
> +++ b/drivers/clk/sophgo/Kconfig
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# common clock support for SOPHGO SoC family.
> +
> +config CLK_SOPHGO_SG2042
> +	bool "Sophgo SG2042 clock support"
> +	depends on ARCH_SOPHGO || COMPILE_TEST
> +	help
> +	  Say yes here to support the clock controller on the Sophgo SG2042 SoC.
> diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile
> new file mode 100644
> index 000000000000..13834cce260c
> --- /dev/null
> +++ b/drivers/clk/sophgo/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_CLK_SOPHGO_SG2042)	+= clk-sophgo-sg2042.o
> diff --git a/drivers/clk/sophgo/clk-sophgo-sg2042.c b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> new file mode 100644
> index 000000000000..e64e1cb4872b
> --- /dev/null
> +++ b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> @@ -0,0 +1,1870 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sophgo SG2042 Clock Generator Driver
> + *
> + * Copyright (C) 2024 Sophgo Technology Inc. All rights reserved.
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/platform_device.h>
> +#include <asm/div64.h>
> +
> +/*
> + * The clock of SG2042 is composed of three parts.
> + * The registers of these three parts of the clock are scattered in three
> + * different memory address spaces:
> + * - pll clocks
> + * - gate clocks for RP subsystem
> + * - div/mux, and gate clocks working for other subsystem than RP subsystem
> + */
> +#include <dt-bindings/clock/sophgo,sg2042-pll.h>
> +#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
> +#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
> +
> +/* Registers defined in SYS_CTRL */
> +#define R_PLL_BEGIN		0xC0
> +#define R_PLL_STAT		(0xC0 - R_PLL_BEGIN)
> +#define R_PLL_CLKEN_CONTROL	(0xC4 - R_PLL_BEGIN)
> +#define R_MPLL_CONTROL		(0xE8 - R_PLL_BEGIN)
> +#define R_FPLL_CONTROL		(0xF4 - R_PLL_BEGIN)
> +#define R_DPLL0_CONTROL		(0xF8 - R_PLL_BEGIN)
> +#define R_DPLL1_CONTROL		(0xFC - R_PLL_BEGIN)
> +
> +#define R_SYSGATE_BEGIN		0x0368
> +#define R_RP_RXU_CLK_ENABLE	(0x0368 - R_SYSGATE_BEGIN)
> +#define R_MP0_STATUS_REG	(0x0380 - R_SYSGATE_BEGIN)
> +#define R_MP0_CONTROL_REG	(0x0384 - R_SYSGATE_BEGIN)
> +#define R_MP1_STATUS_REG	(0x0388 - R_SYSGATE_BEGIN)
> +#define R_MP1_CONTROL_REG	(0x038C - R_SYSGATE_BEGIN)
> +#define R_MP2_STATUS_REG	(0x0390 - R_SYSGATE_BEGIN)
> +#define R_MP2_CONTROL_REG	(0x0394 - R_SYSGATE_BEGIN)
> +#define R_MP3_STATUS_REG	(0x0398 - R_SYSGATE_BEGIN)
> +#define R_MP3_CONTROL_REG	(0x039C - R_SYSGATE_BEGIN)
> +#define R_MP4_STATUS_REG	(0x03A0 - R_SYSGATE_BEGIN)
> +#define R_MP4_CONTROL_REG	(0x03A4 - R_SYSGATE_BEGIN)
> +#define R_MP5_STATUS_REG	(0x03A8 - R_SYSGATE_BEGIN)
> +#define R_MP5_CONTROL_REG	(0x03AC - R_SYSGATE_BEGIN)
> +#define R_MP6_STATUS_REG	(0x03B0 - R_SYSGATE_BEGIN)
> +#define R_MP6_CONTROL_REG	(0x03B4 - R_SYSGATE_BEGIN)
> +#define R_MP7_STATUS_REG	(0x03B8 - R_SYSGATE_BEGIN)
> +#define R_MP7_CONTROL_REG	(0x03BC - R_SYSGATE_BEGIN)
> +#define R_MP8_STATUS_REG	(0x03C0 - R_SYSGATE_BEGIN)
> +#define R_MP8_CONTROL_REG	(0x03C4 - R_SYSGATE_BEGIN)
> +#define R_MP9_STATUS_REG	(0x03C8 - R_SYSGATE_BEGIN)
> +#define R_MP9_CONTROL_REG	(0x03CC - R_SYSGATE_BEGIN)
> +#define R_MP10_STATUS_REG	(0x03D0 - R_SYSGATE_BEGIN)
> +#define R_MP10_CONTROL_REG	(0x03D4 - R_SYSGATE_BEGIN)
> +#define R_MP11_STATUS_REG	(0x03D8 - R_SYSGATE_BEGIN)
> +#define R_MP11_CONTROL_REG	(0x03DC - R_SYSGATE_BEGIN)
> +#define R_MP12_STATUS_REG	(0x03E0 - R_SYSGATE_BEGIN)
> +#define R_MP12_CONTROL_REG	(0x03E4 - R_SYSGATE_BEGIN)
> +#define R_MP13_STATUS_REG	(0x03E8 - R_SYSGATE_BEGIN)
> +#define R_MP13_CONTROL_REG	(0x03EC - R_SYSGATE_BEGIN)
> +#define R_MP14_STATUS_REG	(0x03F0 - R_SYSGATE_BEGIN)
> +#define R_MP14_CONTROL_REG	(0x03F4 - R_SYSGATE_BEGIN)
> +#define R_MP15_STATUS_REG	(0x03F8 - R_SYSGATE_BEGIN)
> +#define R_MP15_CONTROL_REG	(0x03FC - R_SYSGATE_BEGIN)
> +
> +/* Registers defined in CLOCK */
> +#define R_CLKENREG0		0x00
> +#define R_CLKENREG1		0x04
> +#define R_CLKSELREG0		0x20
> +#define R_CLKDIVREG0		0x40
> +#define R_CLKDIVREG1		0x44
> +#define R_CLKDIVREG2		0x48
> +#define R_CLKDIVREG3		0x4C
> +#define R_CLKDIVREG4		0x50
> +#define R_CLKDIVREG5		0x54
> +#define R_CLKDIVREG6		0x58
> +#define R_CLKDIVREG7		0x5C
> +#define R_CLKDIVREG8		0x60
> +#define R_CLKDIVREG9		0x64
> +#define R_CLKDIVREG10		0x68
> +#define R_CLKDIVREG11		0x6C
> +#define R_CLKDIVREG12		0x70
> +#define R_CLKDIVREG13		0x74
> +#define R_CLKDIVREG14		0x78
> +#define R_CLKDIVREG15		0x7C
> +#define R_CLKDIVREG16		0x80
> +#define R_CLKDIVREG17		0x84
> +#define R_CLKDIVREG18		0x88
> +#define R_CLKDIVREG19		0x8C
> +#define R_CLKDIVREG20		0x90
> +#define R_CLKDIVREG21		0x94
> +#define R_CLKDIVREG22		0x98
> +#define R_CLKDIVREG23		0x9C
> +#define R_CLKDIVREG24		0xA0
> +#define R_CLKDIVREG25		0xA4
> +#define R_CLKDIVREG26		0xA8
> +#define R_CLKDIVREG27		0xAC
> +#define R_CLKDIVREG28		0xB0
> +#define R_CLKDIVREG29		0xB4
> +#define R_CLKDIVREG30		0xB8
> +
> +/* All following shift value are the same for all DIV registers */
> +#define SHIFT_DIV_RESET_CTRL	0
> +#define SHIFT_DIV_FACTOR_SEL	3
> +#define SHIFT_DIV_FACTOR	16
> +
> +/**
> + * struct sg2042_clk_data - Common data of clock-controller
> + * @iobase: base address of clock-controller
> + * @onecell_data: used for adding providers.
> + *
> + * Note: this structure will be used both by clkgen & sysclk.
> + */
> +struct sg2042_clk_data {
> +	void __iomem *iobase;
> +	struct clk_hw_onecell_data onecell_data;
> +};
> +
> +/**
> + * struct sg2042_pll_clock - PLL clock
> + * @hw:				clk_hw for initialization
> + * @id:				used to map clk_onecell_data
> + * @base:			used for readl/writel.
> + *				**NOTE**: PLL registers are all in SYS_CTRL!
> + * @lock:			spinlock to protect register access, modification
> + *				of frequency can only be served one at the time.
> + * @offset_status:		offset of pll status registers
> + * @offset_enable:		offset of pll enable registers
> + * @offset_ctrl:		offset of pll control registers
> + * @shift_status_lock:		shift of XXX_LOCK in pll status register
> + * @shift_status_updating:	shift of UPDATING_XXX in pll status register
> + * @shift_enable:		shift of XXX_CLK_EN in pll enable register
> + */
> +struct sg2042_pll_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +	void __iomem *base;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_status;
> +	u32 offset_enable;
> +	u32 offset_ctrl;
> +	u8 shift_status_lock;
> +	u8 shift_status_updating;
> +	u8 shift_enable;
> +};
> +
> +#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
> +
> +/**
> + * struct sg2042_divider_clock - Divider clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @reg:		used for readl/writel.
> + *			**NOTE**: DIV registers are ALL in CLOCK!
> + * @lock:		spinlock to protect register access, modification of
> + *			frequency can only be served one at the time
> + * @offset_ctrl:	offset of divider control registers
> + * @shift:		shift of "Clock Divider Factor" in divider control register
> + * @width:		width of "Clock Divider Factor" in divider control register
> + * @div_flags:		private flags for this clock, not for framework-specific
> + * @initval:		In the divider control register, we can configure whether
> + *			to use the value of "Clock Divider Factor" or just use
> + *			the initial value pre-configured by IC. BIT[3] controls
> + *			this and by default (value is 0), means initial value
> + *			is used.
> + *			**NOTE** that we cannot read the initial value (default
> + *			value when poweron) and default value of "Clock Divider
> + *			Factor" is zero, which I think is a hardware design flaw
> + *			and should be sync-ed with the initial value. So in
> + *			software we have to add a configuration item (initval)
> + *			to manually configure this value and use it when BIT[3]
> + *			is zero.
> + */
> +struct sg2042_divider_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	void __iomem *reg;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_ctrl;
> +	u8 shift;
> +	u8 width;
> +	u8 div_flags;
> +	u32 initval;
> +};
> +
> +#define to_sg2042_clk_divider(_hw)	\
> +	container_of(_hw, struct sg2042_divider_clock, hw)
> +
> +/**
> + * struct sg2042_gate_clock - Gate clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_enable:	offset of gate enable registers
> + * @bit_idx:		which bit in the register controls gating of this clock
> + */
> +struct sg2042_gate_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_enable;
> +	u8 bit_idx;
> +};
> +
> +/**
> + * struct sg2042_mux_clock - Mux clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_select:	offset of mux selection registers
> + *			**NOTE**: MUX registers are ALL in CLOCK!
> + * @shift:		shift of "Clock Select" in mux selection register
> + * @width:		width of "Clock Select" in mux selection register
> + * @clk_nb:		used for notification
> + * @original_index:	set by notifier callback
> + */
> +struct sg2042_mux_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_select;
> +	u8 shift;
> +	u8 width;
> +
> +	struct notifier_block clk_nb;
> +	u8 original_index;
> +};
> +
> +#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb)
> +
> +#define KHZ 1000UL
> +#define MHZ (KHZ * KHZ)
> +
> +#define REFDIV_MIN 1
> +#define REFDIV_MAX 63
> +#define FBDIV_MIN 16
> +#define FBDIV_MAX 320
> +
> +#define PLL_FREF_SG2042 (25 * MHZ)
> +
> +#define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
> +#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
> +
> +#define PLL_FOUTVCO_MIN (800 * MHZ)
> +#define PLL_FOUTVCO_MAX (3200 * MHZ)
> +
> +struct sg2042_pll_ctrl {
> +	unsigned long freq;
> +	unsigned int fbdiv;
> +	unsigned int postdiv1;
> +	unsigned int postdiv2;
> +	unsigned int refdiv;
> +};
> +
> +#define PLLCTRL_FBDIV_SHIFT	16
> +#define PLLCTRL_FBDIV_MASK	(GENMASK(27, 16) >> PLLCTRL_FBDIV_SHIFT)
> +#define PLLCTRL_POSTDIV2_SHIFT	12
> +#define PLLCTRL_POSTDIV2_MASK	(GENMASK(14, 12) >> PLLCTRL_POSTDIV2_SHIFT)
> +#define PLLCTRL_POSTDIV1_SHIFT	8
> +#define PLLCTRL_POSTDIV1_MASK	(GENMASK(10, 8) >> PLLCTRL_POSTDIV1_SHIFT)
> +#define PLLCTRL_REFDIV_SHIFT	0
> +#define PLLCTRL_REFDIV_MASK	(GENMASK(5, 0) >> PLLCTRL_REFDIV_SHIFT)
> +
> +static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
> +{
> +	return ((ctrl->fbdiv & PLLCTRL_FBDIV_MASK) << PLLCTRL_FBDIV_SHIFT) |
> +	       ((ctrl->postdiv2 & PLLCTRL_POSTDIV2_MASK) << PLLCTRL_POSTDIV2_SHIFT) |
> +	       ((ctrl->postdiv1 & PLLCTRL_POSTDIV1_MASK) << PLLCTRL_POSTDIV1_SHIFT) |
> +	       ((ctrl->refdiv & PLLCTRL_REFDIV_MASK) << PLLCTRL_REFDIV_SHIFT);
> +}
> +
> +static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
> +					  struct sg2042_pll_ctrl *ctrl)
> +{
> +	ctrl->fbdiv = (reg_value >> PLLCTRL_FBDIV_SHIFT) & PLLCTRL_FBDIV_MASK;
> +	ctrl->refdiv = (reg_value >> PLLCTRL_REFDIV_SHIFT) & PLLCTRL_REFDIV_MASK;
> +	ctrl->postdiv1 = (reg_value >> PLLCTRL_POSTDIV1_SHIFT) & PLLCTRL_POSTDIV1_MASK;
> +	ctrl->postdiv2 = (reg_value >> PLLCTRL_POSTDIV2_SHIFT) & PLLCTRL_POSTDIV2_MASK;
> +}
> +
> +static inline int sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
> +{
> +	u32 value;
> +
> +	if (en) {
> +		/* wait pll lock */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      ((value >> pll->shift_status_lock) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s not locked\n", pll->hw.init->name);
> +
> +		/* wait pll updating */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      !((value >> pll->shift_status_updating) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s still updating\n", pll->hw.init->name);
> +
> +		/* enable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value | (1 << pll->shift_enable), pll->base + pll->offset_enable);
> +	} else {
> +		/* disable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value & (~(1 << pll->shift_enable)), pll->base + pll->offset_enable);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * sg2042_pll_recalc_rate() - Calculate rate for plls
> + * @reg_value: current register value
> + * @parent_rate: parent frequency
> + *
> + * This function is used to calculate below "rate" in equation
> + * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
> + *      = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
> + *
> + * Return: The rate calculated.
> + */
> +static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
> +					    unsigned long parent_rate)
> +{
> +	struct sg2042_pll_ctrl ctrl_table;
> +	u64 numerator, denominator;
> +
> +	sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
> +
> +	numerator = parent_rate * ctrl_table.fbdiv;
> +	denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
> +	do_div(numerator, denominator);
> +	return numerator;
> +}
> +
> +/**
> + * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
> + * look up the postdiv1_2 table to get the closest postdiiv combination.
> + * @rate: FOUTPOSTDIV
> + * @prate: parent rate, i.e. FREF
> + * @fbdiv: FBDIV
> + * @refdiv: REFDIV
> + * @postdiv1: POSTDIV1, output
> + * @postdiv2: POSTDIV2, output
> + *
> + * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
> + * for example:
> + * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
> + *
> + * See TRM:
> + * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
> + * So we get following formula to get POSTDIV1 and POSTDIV2:
> + * POSTDIV = (prate/REFDIV) x FBDIV/rate
> + * above POSTDIV = POSTDIV1*POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument, which means Failed to get the postdivs.
> + */
> +static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
> +				      unsigned long prate,
> +				      unsigned int fbdiv,
> +				      unsigned int refdiv,
> +				      unsigned int *postdiv1,
> +				      unsigned int *postdiv2)
> +{
> +	int index;
> +	u64 tmp0;
> +
> +	/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
> +	#define	POSTDIV_RESULT_INDEX	2
> +
> +	static const int postdiv1_2[][3] = {
> +		{2, 4,  8}, {3, 3,  9}, {2, 5, 10}, {2, 6, 12},
> +		{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
> +		{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
> +		{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
> +		{6, 7, 42}, {7, 7, 49}
> +	};
> +
> +	/* prate/REFDIV and result save to tmp0 */
> +	tmp0 = prate;
> +	do_div(tmp0, refdiv);
> +
> +	/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
> +	tmp0 *= fbdiv;
> +
> +	/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
> +	do_div(tmp0, rate);
> +
> +	/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
> +	if (tmp0 <= 7) {
> +		/* (div1 * div2) <= 7, no need to use array search */
> +		*postdiv1 = tmp0;
> +		*postdiv2 = 1;
> +		return 0;
> +	}
> +
> +	/* (div1 * div2) > 7, use array search */
> +	for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
> +		if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
> +			continue;
> +		} else {
> +			/* found it */
> +			*postdiv1 = postdiv1_2[index][1];
> +			*postdiv2 = postdiv1_2[index][0];
> +			return 0;
> +		}
> +	}
> +	pr_warn("%s can not find in postdiv array!\n", __func__);
> +	return -EINVAL;
> +}
> +
> +/**
> + * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
> + * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
> + * register.
> + * @req_rate: expected output clock rate, i.e. FOUTPISTDIV
> + * @parent_rate: input parent clock rate, i.e. FREF
> + * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument
> + */
> +static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
> +				      unsigned long req_rate,
> +				      unsigned long parent_rate)
> +{
> +	int ret;
> +	unsigned int fbdiv, refdiv, postdiv1, postdiv2;
> +	unsigned long foutpostdiv;
> +	u64 tmp;
> +	u64 foutvco;
> +
> +	if (parent_rate != PLL_FREF_SG2042) {
> +		pr_err("INVALID FREF: %ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
> +		pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
> +		return -EINVAL;
> +	}
> +
> +	memset(best, 0, sizeof(struct sg2042_pll_ctrl));
> +
> +	for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
> +		/* required by hardware: FREF/REFDIV must > 10 */
> +		tmp = parent_rate;
> +		do_div(tmp, refdiv);
> +		if (tmp <= 10)
> +			continue;
> +
> +		for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
> +			/*
> +			 * FOUTVCO = FREF*FBDIV/REFDIV validation
> +			 * required by hardware, FOUTVCO must [800MHz, 3200MHz]
> +			 */
> +			foutvco = parent_rate * fbdiv;
> +			do_div(foutvco, refdiv);
> +			if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
> +				continue;
> +
> +			ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
> +							 fbdiv, refdiv,
> +							 &postdiv1, &postdiv2);
> +			if (ret)
> +				continue;
> +
> +			/*
> +			 * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
> +			 *             = FOUTVCO/(POSTDIV1*POSTDIV2)
> +			 */
> +			tmp = foutvco;
> +			do_div(tmp, (postdiv1 * postdiv2));
> +			foutpostdiv = (unsigned long)tmp;
> +			/* Iterative to approach the expected value */
> +			if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
> +				best->freq = foutpostdiv;
> +				best->refdiv = refdiv;
> +				best->fbdiv = fbdiv;
> +				best->postdiv1 = postdiv1;
> +				best->postdiv2 = postdiv2;
> +				if (foutpostdiv == req_rate)
> +					return 0;
> +			}
> +			continue;
> +		}
> +	}
> +
> +	if (best->freq == 0)
> +		return -EINVAL;
> +	else
> +		return 0;
> +}
> +
> +/**
> + * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
> + * @hw: ccf use to hook get sg2042_pll_clock
> + * @parent_rate: parent rate
> + *
> + * The is function will be called through clk_get_rate
> + * and return current rate after decoding reg value
> + *
> + * Return: Current rate recalculated.
> + */
> +static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	u32 value;
> +	unsigned long rate;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	value = readl(pll->base + pll->offset_ctrl);
> +	rate = sg2042_pll_recalc_rate(value, parent_rate);
> +
> +	pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), rate);
> +	return rate;
> +}
> +
> +static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
> +				      unsigned long req_rate,
> +				      unsigned long *prate)
> +{
> +	unsigned int value;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	long proper_rate;
> +	int ret;
> +
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
> +	if (ret) {
> +		proper_rate = 0;
> +		goto out;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +	proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
> +
> +out:
> +	pr_debug("--> %s: pll_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), proper_rate);
> +	return proper_rate;
> +}
> +
> +static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
> +					 struct clk_rate_request *req)
> +{
> +	req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
> +					      &req->best_parent_rate);
> +	pr_debug("--> %s: pll_determine_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), req->rate);
> +	return 0;
> +}
> +
> +static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
> +				   unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	unsigned long flags;
> +	u32 value;
> +	int ret = 0;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	spin_lock_irqsave(pll->lock, flags);
> +	if (sg2042_pll_enable(pll, 0)) {
> +		pr_warn("Can't disable pll(%s), status error\n", pll->hw.init->name);
> +		goto out;
> +	}
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
> +	if (ret) {
> +		pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
> +		goto out2;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +
> +	/* write the value to top register */
> +	writel(value, pll->base + pll->offset_ctrl);
> +
> +out2:
> +	sg2042_pll_enable(pll, 1);
> +out:
> +	spin_unlock_irqrestore(pll->lock, flags);
> +
> +	pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
> +		 clk_hw_get_name(hw), value);
> +	return ret;
> +}
> +
> +static const struct clk_ops sg2042_clk_pll_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +	.determine_rate = sg2042_clk_pll_determine_rate,
> +	.set_rate = sg2042_clk_pll_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_pll_ro_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +};
> +
> +static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,
> +						    unsigned long parent_rate)
> +{
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +	u32 val;
> +	unsigned long ret_rate;
> +
> +	if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +		val = divider->initval;
> +	} else {
> +		val = readl(divider->reg) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +	}
> +
> +	ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL,
> +				       divider->div_flags, divider->width);
> +
> +	pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static long sg2042_clk_divider_round_rate(struct clk_hw *hw,
> +					  unsigned long rate,
> +					  unsigned long *prate)
> +{
> +	u32 bestdiv;
> +	unsigned long ret_rate;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->div_flags & CLK_DIVIDER_READ_ONLY) {
> +		if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +			bestdiv = divider->initval;
> +		} else {
> +			bestdiv = readl(divider->reg) >> divider->shift;
> +			bestdiv &= clk_div_mask(divider->width);
> +		}
> +		ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
> +	} else {
> +		ret_rate = divider_round_rate(hw, rate, prate, NULL,
> +					      divider->width, divider->div_flags);
> +	}
> +
> +	pr_debug("--> %s: divider_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static int sg2042_clk_divider_set_rate(struct clk_hw *hw,
> +				       unsigned long rate,
> +				       unsigned long parent_rate)
> +{
> +	u32 value;
> +	u32 val, val2;
> +	unsigned long flags = 0;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	value = divider_get_val(rate, parent_rate, NULL,
> +				divider->width, divider->div_flags);
> +
> +	if (divider->lock)
> +		spin_lock_irqsave(divider->lock, flags);
> +	else
> +		__acquire(divider->lock);
> +
> +	/*
> +	 * The sequence of clock frequency modification is:
> +	 * Assert to reset divider.
> +	 * Modify the value of Clock Divide Factor (and High Wide if needed).
> +	 * De-assert to restore divided clock with new frequency.
> +	 */
> +	val = readl(divider->reg);
> +
> +	/* assert */
> +	val &= ~BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +		val = clk_div_mask(divider->width) << (divider->shift + 16);
> +	} else {
> +		val = readl(divider->reg);
> +		val &= ~(clk_div_mask(divider->width) << divider->shift);
> +	}
> +	val |= value << divider->shift;
> +	val |= BIT(SHIFT_DIV_FACTOR_SEL);
> +	writel(val, divider->reg);
> +	val2 = val;
> +
> +	/* de-assert */
> +	val |= BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->lock)
> +		spin_unlock_irqrestore(divider->lock, flags);
> +	else
> +		__release(divider->lock);
> +
> +	pr_debug("--> %s: divider_set_rate: register val = 0x%x\n",
> +		 clk_hw_get_name(hw), val2);
> +	return 0;
> +}
> +
> +static const struct clk_ops sg2042_clk_divider_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +	.set_rate = sg2042_clk_divider_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_divider_ro_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +};
> +
> +/*
> + * Clock initialization macro naming rules:
> + * FW: use CLK_HW_INIT_FW_NAME
> + * HW: use CLK_HW_INIT_HW
> + * HWS: use CLK_HW_INIT_HWS
> + * RO: means Read-Only
> + */
> +#define SG2042_PLL_FW(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}
> +
> +#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ro_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}
> +
> +static struct sg2042_pll_clock sg2042_pll_clks[] = {
> +	SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main",
> +		      R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_MPLL_CONTROL, 0),
> +	SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_FPLL_CONTROL, 3),
> +	SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL0_CONTROL, 4),
> +	SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL1_CONTROL, 5),
> +};
> +
> +#define SG2042_DIV_FW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_FW_RO(_id, _name, _parent,				\
> +		  _r_ctrl, _shift, _width,				\
> +		  _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW_RO(_id, _name, _parent,				\
> +			 _r_ctrl, _shift, _width,			\
> +			 _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS(_id, _name, _parent,				\
> +		       _r_ctrl, _shift, _width,				\
> +		       _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\
> +			  _r_ctrl, _shift, _width,			\
> +			  _div_flag, _initval) {			\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\
> +			_r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HWS(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HW(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_FW_NAME(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \
> +		.hw.init = CLK_HW_INIT_PARENTS_HW(		\
> +				_name,				\
> +				_parents,			\
> +				NULL,				\
> +				_flags),			\
> +		.id = _id,					\
> +		.offset_select = _r_select,			\
> +		.shift = _shift,				\
> +		.width = _width,				\
> +	}
> +
> +/*
> + * Clock items in the array are sorted according to the clock-tree diagram,
> + * from top to bottom, from upstream to downstream. Read TRM for details.
> + */
> +
> +/* updated during probe/registration */
> +static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = {
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG27, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG28, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG29, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG30, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0,
> +		       "clk_gate_rp_cpu_normal_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG0, 4),
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1,
> +		       "clk_gate_rp_cpu_normal_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG1, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG25, 4),
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG26, 4),
> +};
> +
> +#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_1[] = {
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0,
> +			  "clk_div_ddr01_0", clk_gate_ddr01_div0,
> +			  R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1,
> +			  "clk_div_ddr01_1", clk_gate_ddr01_div1,
> +			  R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0,
> +			  "clk_div_ddr23_0", clk_gate_ddr23_div0,
> +			  R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1,
> +			  "clk_div_ddr23_1", clk_gate_ddr23_div1,
> +			  R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0,
> +		       "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0,
> +		       R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1,
> +		       "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1,
> +		       R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0,
> +		       "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0,
> +		       R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1,
> +		       "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1,
> +		       R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1),
> +};
> +
> +/*
> + * Note: regarding names for mux clock, "0/1" or "div0/div1" means the
> + * first/second parent input source, not the register value.
> + * For example:
> + * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and
> + * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",
> + * they are both controlled by register CLKDIVREG27;
> + * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and
> + * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1",
> + * they are both controlled by register CLKDIVREG28;
> + * While for register value of mux selection, use Clock Select for DDR01’s clock
> + * as example, see CLKSELREG0, bit[2].
> + * 1: Select in_dpll0_clk as clock source, correspondng to the parent input
> + *    source from "clk_div_ddr01_0".
> + * 0: Select in_fpll_clk as clock source, corresponding to the parent input
> + *    source from "clk_div_ddr01_1".
> + * So we need a table to define the array of register values corresponding to
> + * the parent index and tell CCF about this when registering mux clock.
> + */
> +static const u32 sg2042_mux_table[] = {1, 0};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_ddr01_0		(&sg2042_div_clks_level_1[0].hw)
> +#define clk_div_ddr01_1		(&sg2042_div_clks_level_1[1].hw)
> +#define clk_div_ddr23_0		(&sg2042_div_clks_level_1[2].hw)
> +#define clk_div_ddr23_1		(&sg2042_div_clks_level_1[3].hw)
> +#define clk_div_rp_cpu_normal_0	(&sg2042_div_clks_level_1[4].hw)
> +#define clk_div_rp_cpu_normal_1	(&sg2042_div_clks_level_1[5].hw)
> +#define clk_div_axi_ddr_0	(&sg2042_div_clks_level_1[6].hw)
> +#define clk_div_axi_ddr_1	(&sg2042_div_clks_level_1[7].hw)
> +
> +static const struct clk_hw *clk_mux_ddr01_p[] = {
> +	clk_div_ddr01_0,
> +	clk_div_ddr01_1,
> +};
> +
> +static const struct clk_hw *clk_mux_ddr23_p[] = {
> +	clk_div_ddr23_0,
> +	clk_div_ddr23_1,
> +};
> +
> +static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = {
> +	clk_div_rp_cpu_normal_0,
> +	clk_div_rp_cpu_normal_1,
> +};
> +
> +static const struct clk_hw *clk_mux_axi_ddr_p[] = {
> +	clk_div_axi_ddr_0,
> +	clk_div_axi_ddr_1,
> +};
> +
> +/* Mux clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_mux_ddr01[] = { NULL };
> +static const struct clk_hw *clk_mux_ddr23[] = { NULL };
> +static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_mux_axi_ddr[] = { NULL };
> +
> +static struct sg2042_mux_clock sg2042_mux_clks[] = {
> +	SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 2, 1),
> +	SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 3, 1),
> +	SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 0, 1),
> +	SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 1, 1),
> +};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_top_rp_cmn_div2	(&sg2042_div_clks_level_2[0].hw)
> +#define clk_div_50m_a53		(&sg2042_div_clks_level_2[1].hw)
> +#define clk_div_timer1		(&sg2042_div_clks_level_2[2].hw)
> +#define clk_div_timer2		(&sg2042_div_clks_level_2[3].hw)
> +#define clk_div_timer3		(&sg2042_div_clks_level_2[4].hw)
> +#define clk_div_timer4		(&sg2042_div_clks_level_2[5].hw)
> +#define clk_div_timer5		(&sg2042_div_clks_level_2[6].hw)
> +#define clk_div_timer6		(&sg2042_div_clks_level_2[7].hw)
> +#define clk_div_timer7		(&sg2042_div_clks_level_2[8].hw)
> +#define clk_div_timer8		(&sg2042_div_clks_level_2[9].hw)
> +#define clk_div_uart_500m	(&sg2042_div_clks_level_2[10].hw)
> +#define clk_div_ahb_lpc		(&sg2042_div_clks_level_2[11].hw)
> +#define clk_div_efuse		(&sg2042_div_clks_level_2[12].hw)
> +#define clk_div_tx_eth0		(&sg2042_div_clks_level_2[13].hw)
> +#define clk_div_ptp_ref_i_eth0	(&sg2042_div_clks_level_2[14].hw)
> +#define clk_div_ref_eth0	(&sg2042_div_clks_level_2[15].hw)
> +#define clk_div_emmc		(&sg2042_div_clks_level_2[16].hw)
> +#define clk_div_sd		(&sg2042_div_clks_level_2[17].hw)
> +#define clk_div_top_axi0	(&sg2042_div_clks_level_2[18].hw)
> +#define clk_div_100k_emmc	(&sg2042_div_clks_level_2[19].hw)
> +#define clk_div_100k_sd		(&sg2042_div_clks_level_2[20].hw)
> +#define clk_div_gpio_db		(&sg2042_div_clks_level_2[21].hw)
> +#define clk_div_top_axi_hsperi	(&sg2042_div_clks_level_2[22].hw)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_2[] = {
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2,
> +		       "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal,
> +		       R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll",
> +		      R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20),
> +	/* downstream of div_50m_a53 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53,
> +		      R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53,
> +		      R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53,
> +		      R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53,
> +		      R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53,
> +		      R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53,
> +		      R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53,
> +		      R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53,
> +		      R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1),
> +
> +	/*
> +	 * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too
> +	 * narrow for us to produce 115200. Use UART internal divider directly.
> +	 */
> +	SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll",
> +			 R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll",
> +		      R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll",
> +		      R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll",
> +		      R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0,
> +		      "clk_div_ptp_ref_i_eth0", "fpll",
> +		      R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll",
> +		      R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll",
> +		      R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll",
> +		      R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll",
> +		      R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10),
> +	/* downstream of div_top_axi0 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0,
> +		      R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0,
> +		      R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0,
> +		      R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI,
> +		      "clk_div_top_axi_hsperi", "fpll",
> +		      R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4),
> +};
> +
> +/* Gate clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = {
> +	SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 14),
> +
> +	SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 15),
> +
> +	SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL,
> +			"clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG0, 0),
> +
> +	SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 13),
> +
> +	/* upon are gate clocks directly downstream of muxes */
> +
> +	/* downstream of clk_div_top_rp_cmn_div2 */
> +	SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2,
> +		       "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2),
> +	SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2,
> +			CLK_SET_RATE_PARENT, R_CLKENREG1, 10),
> +
> +	/*
> +	 * downstream of clk_gate_rp_cpu_normal
> +	 *
> +	 * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal
> +	 * and clk_gate_axi_pcie0/clk_gate_axi_pcie1.
> +	 * But the 1/2 DIV is fixed and no configurable register exported, so
> +	 * when reading from these two clocks, the rate value are still the
> +	 * same as that of clk_gate_rp_cpu_normal, it's not correct.
> +	 * This just affects the value read.
> +	 */
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0,
> +			"clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1,
> +			"clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),
> +
> +	/* downstream of div_50m_a53 */
> +	SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1),
> +	SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 12),
> +	SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 13),
> +	SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 14),
> +	SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 15),
> +	SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 16),
> +	SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 17),
> +	SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 18),
> +	SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 19),
> +
> +	/* gate clocks downstream from div clocks one-to-one */
> +	SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4),
> +	SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 7),
> +	SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 20),
> +	SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 30),
> +	SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0,
> +		       "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 0),
> +	SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 1),
> +	SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 3),
> +	SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 6),
> +
> +	/* downstream of clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 8),
> +	SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 9),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 10),
> +	SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 11),
> +	SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 21),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 22),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR,
> +		       "clk_gate_apb_gpio_intr", clk_div_top_axi0,
> +		       CLK_IS_CRITICAL, R_CLKENREG0, 23),
> +	SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 26),
> +	SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 27),
> +	SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 28),
> +	SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 29),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 11),
> +	/* downstream of DIV clocks which are sourced from clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 24),
> +	SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 4),
> +	SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 7),
> +
> +	/* downstream of clk_div_top_axi_hsperi */
> +	SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI,
> +		       "clk_gate_sysdma_axi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 3),
> +	SG2042_GATE_HW(GATE_CLK_APB_UART,
> +		       "clk_gate_apb_uart", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 5),
> +	SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C,
> +		       "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 6),
> +	SG2042_GATE_HW(GATE_CLK_APB_SPI,
> +		       "clk_gate_apb_spi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 25),
> +	SG2042_GATE_HW(GATE_CLK_AXI_ETH0,
> +		       "clk_gate_axi_eth0", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 31),
> +	SG2042_GATE_HW(GATE_CLK_AXI_EMMC,
> +		       "clk_gate_axi_emmc", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 2),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SD,
> +		       "clk_gate_axi_sd", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 5),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI,
> +		       "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 12),
> +};
> +
> +/*
> + * Gate clocks for RP subsystem (including the MP subsystem), which control
> + * registers are defined in SYS_CTRL.
> + */
> +static const struct sg2042_gate_clock sg2042_gate_rp[] = {
> +	/* downstream of clk_gate_rp_cpu_normal about rxu */
> +	SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 0),
> +	SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 1),
> +	SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 2),
> +	SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 3),
> +	SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 4),
> +	SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 5),
> +	SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 6),
> +	SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 7),
> +	SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 8),
> +	SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 9),
> +	SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 10),
> +	SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 11),
> +	SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 12),
> +	SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 13),
> +	SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 14),
> +	SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 15),
> +	SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 16),
> +	SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 17),
> +	SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 18),
> +	SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 19),
> +	SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 20),
> +	SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 21),
> +	SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 22),
> +	SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 23),
> +	SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 24),
> +	SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 25),
> +	SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 26),
> +	SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 27),
> +	SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 28),
> +	SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 29),
> +	SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 30),
> +	SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 31),
> +
> +	/* downstream of clk_gate_rp_cpu_normal about mp */
> +	SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0),
> +};
> +
> +static DEFINE_SPINLOCK(sg2042_clk_lock);
> +
> +static int sg2042_clk_register_plls(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_pll_clock pll_clks[],
> +				    int num_pll_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_pll_clock *pll;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_pll_clks; i++) {
> +		pll = &pll_clks[i];
> +		/* assign these for ops usage during registration */
> +		pll->base = clk_data->iobase;
> +		pll->lock = &sg2042_clk_lock;
> +
> +		hw = &pll->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", pll->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[pll->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_divs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_divider_clock div_clks[],
> +				    int num_div_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_divider_clock *div;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_div_clks; i++) {
> +		div = &div_clks[i];
> +
> +		if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +			if (div->width + div->shift > 16) {
> +				pr_warn("divider value exceeds LOWORD field\n");
> +				ret = -EINVAL;
> +				break;
> +			}
> +		}
> +
> +		div->reg = clk_data->iobase + div->offset_ctrl;
> +		div->lock = &sg2042_clk_lock;
> +
> +		hw = &div->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", div->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[div->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates(struct device *dev,
> +				     struct sg2042_clk_data *clk_data,
> +				     const struct sg2042_gate_clock gate_clks[],
> +				     int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = __devm_clk_hw_register_gate
> +			(dev,
> +			 NULL,
> +			 gate->hw.init->name,
> +			 NULL,
> +			 gate->hw.init->parent_hws[0],
> +			 NULL,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_RP_CPU_NORMAL:
> +			*clk_gate_rp_cpu_normal = hw;
> +			break;
> +		case GATE_CLK_TOP_RP_CMN_DIV2:
> +			*clk_gate_top_rp_cmn_div2 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates_fw(struct device *dev,
> +					struct sg2042_clk_data *clk_data,
> +					const struct sg2042_gate_clock gate_clks[],
> +					int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = devm_clk_hw_register_gate_parent_data
> +			(dev,
> +			 gate->hw.init->name,
> +			 gate->hw.init->parent_data,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_DDR01_DIV0:
> +			*clk_gate_ddr01_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR01_DIV1:
> +			*clk_gate_ddr01_div1 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV0:
> +			*clk_gate_ddr23_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV1:
> +			*clk_gate_ddr23_div1 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV0:
> +			*clk_gate_rp_cpu_normal_div0 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV1:
> +			*clk_gate_rp_cpu_normal_div1 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV0:
> +			*clk_gate_axi_ddr_div0 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV1:
> +			*clk_gate_axi_ddr_div1 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_mux_notifier_cb(struct notifier_block *nb,
> +				  unsigned long event,
> +				  void *data)
> +{
> +	int ret = 0;
> +	struct clk_notifier_data *ndata = data;
> +	struct clk_hw *hw = __clk_get_hw(ndata->clk);
> +	const struct clk_ops *ops = &clk_mux_ops;
> +	struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb);
> +
> +	/* To switch to fpll before changing rate and restore after that */
> +	if (event == PRE_RATE_CHANGE) {
> +		mux->original_index = ops->get_parent(hw);
> +
> +		/*
> +		 * "1" is the array index of the second parent input source of
> +		 * mux. For SG2042, it's fpll for all mux clocks.
> +		 * "0" is the array index of the frist parent input source of
> +		 * mux, For SG2042, it's mpll.
> +		 * FIXME, any good idea to avoid magic number?
> +		 */
> +		if (mux->original_index == 0)
> +			ret = ops->set_parent(hw, 1);
> +	} else if (event == POST_RATE_CHANGE) {
> +		ret = ops->set_parent(hw, mux->original_index);
> +	}
> +
> +	return notifier_from_errno(ret);
> +}
> +
> +static int sg2042_clk_register_muxs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_mux_clock mux_clks[],
> +				    int num_mux_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_mux_clock *mux;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_mux_clks; i++) {
> +		mux = &mux_clks[i];
> +
> +		hw = __devm_clk_hw_register_mux
> +			(dev,
> +			 NULL,
> +			 mux->hw.init->name,
> +			 mux->hw.init->num_parents,
> +			 NULL,
> +			 mux->hw.init->parent_hws,
> +			 NULL,
> +			 mux->hw.init->flags,
> +			 clk_data->iobase + mux->offset_select,
> +			 mux->shift,
> +			 BIT(mux->width) - 1,
> +			 0,
> +			 sg2042_mux_table,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", mux->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[mux->id] = hw;
> +
> +		/* Updated some clocks which takes the role of parent */
> +		switch (mux->id) {
> +		case MUX_CLK_DDR01:
> +			*clk_mux_ddr01 = hw;
> +			break;
> +		case MUX_CLK_DDR23:
> +			*clk_mux_ddr23 = hw;
> +			break;
> +		case MUX_CLK_RP_CPU_NORMAL:
> +			*clk_mux_rp_cpu_normal = hw;
> +			break;
> +		case MUX_CLK_AXI_DDR:
> +			*clk_mux_axi_ddr = hw;
> +			break;
> +		}
> +
> +		/*
> +		 * FIXME: Theoretically, we should set parent for the
> +		 * mux, but seems hardware has done this for us with
> +		 * default value, so we don't set parent again here.
> +		 */
> +
> +		if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {
> +			mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;
> +			ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb);
> +			if (ret) {
> +				pr_err("failed to register clock notifier for %s\n",
> +				       mux->hw.init->name);
> +				break;
> +			}
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_init_clkdata(struct platform_device *pdev,
> +			       int num_clks,
> +			       struct sg2042_clk_data **pp_clk_data)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +
> +	clk_data = devm_kzalloc(&pdev->dev,
> +				struct_size(clk_data, onecell_data.hws, num_clks),
> +				GFP_KERNEL);
> +	if (!clk_data)
> +		return -ENOMEM;
> +
> +	clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
> +	if (WARN_ON(IS_ERR(clk_data->iobase)))
> +		return PTR_ERR(clk_data->iobase);
> +
> +	clk_data->onecell_data.num = num_clks;
> +
> +	*pp_clk_data = clk_data;
> +
> +	return 0;
> +}
> +
> +static int sg2042_clkgen_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_div_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_mux_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 gates */
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,
> +					   sg2042_gate_clks_level_1,
> +					   ARRAY_SIZE(sg2042_gate_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,
> +				       ARRAY_SIZE(sg2042_div_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* mux */
> +	ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,
> +				       ARRAY_SIZE(sg2042_mux_clks));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,
> +				       ARRAY_SIZE(sg2042_div_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 gate */
> +	ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,
> +					ARRAY_SIZE(sg2042_gate_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_rpgate_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_gate_rp);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data, sg2042_gate_rp,
> +					   num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_pll_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_pll_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
> +				       num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id sg2042_clkgen_match[] = {
> +	{ .compatible = "sophgo,sg2042-clkgen" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_clkgen_driver = {
> +	.probe = sg2042_clkgen_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-clkgen",
> +		.of_match_table = sg2042_clkgen_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_clkgen_driver);
> +
> +static const struct of_device_id sg2042_rpgate_match[] = {
> +	{ .compatible = "sophgo,sg2042-rpgate" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_rpgate_driver = {
> +	.probe = sg2042_rpgate_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-rpgate",
> +		.of_match_table = sg2042_rpgate_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_rpgate_driver);
> +
> +static const struct of_device_id sg2042_pll_match[] = {
> +	{ .compatible = "sophgo,sg2042-pll" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_pll_driver = {
> +	.probe = sg2042_pll_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-pll",
> +		.of_match_table = sg2042_pll_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_pll_driver);

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v15 4/5] clk: sophgo: Add SG2042 clock driver
  2024-04-26  3:35   ` Chen Wang
@ 2024-04-30  7:48     ` Emil Renner Berthing
  -1 siblings, 0 replies; 18+ messages in thread
From: Emil Renner Berthing @ 2024-04-30  7:48 UTC (permalink / raw)
  To: Chen Wang, aou, chao.wei, conor, krzysztof.kozlowski+dt,
	mturquette, palmer, paul.walmsley, richardcochran, robh+dt,
	sboyd, devicetree, linux-clk, linux-kernel, linux-riscv,
	haijiao.liu, xiaoguang.xing, guoren, jszhang, inochiama,
	samuel.holland
  Cc: Chen Wang

Chen Wang wrote:
> From: Chen Wang <unicorn_wang@outlook.com>
>
> Add a driver for the SOPHGO SG2042 clocks.
>
> Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
> ---
>  drivers/clk/Kconfig                    |    1 +
>  drivers/clk/Makefile                   |    1 +
>  drivers/clk/sophgo/Kconfig             |    8 +
>  drivers/clk/sophgo/Makefile            |    2 +
>  drivers/clk/sophgo/clk-sophgo-sg2042.c | 1870 ++++++++++++++++++++++++
>  5 files changed, 1882 insertions(+)
>  create mode 100644 drivers/clk/sophgo/Kconfig
>  create mode 100644 drivers/clk/sophgo/Makefile
>  create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 50af5fc7f570..bc28502ec3c9 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
>  source "drivers/clk/samsung/Kconfig"
>  source "drivers/clk/sifive/Kconfig"
>  source "drivers/clk/socfpga/Kconfig"
> +source "drivers/clk/sophgo/Kconfig"
>  source "drivers/clk/sprd/Kconfig"
>  source "drivers/clk/starfive/Kconfig"
>  source "drivers/clk/sunxi/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 14fa8d4ecc1f..4abe16c8ccdf 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
>  obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
>  obj-$(CONFIG_CLK_SIFIVE)		+= sifive/
>  obj-y					+= socfpga/
> +obj-y					+= sophgo/
>  obj-$(CONFIG_PLAT_SPEAR)		+= spear/
>  obj-y					+= sprd/
>  obj-$(CONFIG_ARCH_STI)			+= st/
> diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig
> new file mode 100644
> index 000000000000..82516adb0f97
> --- /dev/null
> +++ b/drivers/clk/sophgo/Kconfig
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# common clock support for SOPHGO SoC family.
> +
> +config CLK_SOPHGO_SG2042
> +	bool "Sophgo SG2042 clock support"
> +	depends on ARCH_SOPHGO || COMPILE_TEST
> +	help
> +	  Say yes here to support the clock controller on the Sophgo SG2042 SoC.
> diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile
> new file mode 100644
> index 000000000000..13834cce260c
> --- /dev/null
> +++ b/drivers/clk/sophgo/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_CLK_SOPHGO_SG2042)	+= clk-sophgo-sg2042.o
> diff --git a/drivers/clk/sophgo/clk-sophgo-sg2042.c b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> new file mode 100644
> index 000000000000..e64e1cb4872b
> --- /dev/null
> +++ b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> @@ -0,0 +1,1870 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sophgo SG2042 Clock Generator Driver
> + *
> + * Copyright (C) 2024 Sophgo Technology Inc. All rights reserved.
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/platform_device.h>
> +#include <asm/div64.h>
> +
> +/*
> + * The clock of SG2042 is composed of three parts.
> + * The registers of these three parts of the clock are scattered in three
> + * different memory address spaces:
> + * - pll clocks
> + * - gate clocks for RP subsystem
> + * - div/mux, and gate clocks working for other subsystem than RP subsystem
> + */
> +#include <dt-bindings/clock/sophgo,sg2042-pll.h>
> +#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
> +#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
> +
> +/* Registers defined in SYS_CTRL */
> +#define R_PLL_BEGIN		0xC0
> +#define R_PLL_STAT		(0xC0 - R_PLL_BEGIN)
> +#define R_PLL_CLKEN_CONTROL	(0xC4 - R_PLL_BEGIN)
> +#define R_MPLL_CONTROL		(0xE8 - R_PLL_BEGIN)
> +#define R_FPLL_CONTROL		(0xF4 - R_PLL_BEGIN)
> +#define R_DPLL0_CONTROL		(0xF8 - R_PLL_BEGIN)
> +#define R_DPLL1_CONTROL		(0xFC - R_PLL_BEGIN)
> +
> +#define R_SYSGATE_BEGIN		0x0368
> +#define R_RP_RXU_CLK_ENABLE	(0x0368 - R_SYSGATE_BEGIN)
> +#define R_MP0_STATUS_REG	(0x0380 - R_SYSGATE_BEGIN)
> +#define R_MP0_CONTROL_REG	(0x0384 - R_SYSGATE_BEGIN)
> +#define R_MP1_STATUS_REG	(0x0388 - R_SYSGATE_BEGIN)
> +#define R_MP1_CONTROL_REG	(0x038C - R_SYSGATE_BEGIN)
> +#define R_MP2_STATUS_REG	(0x0390 - R_SYSGATE_BEGIN)
> +#define R_MP2_CONTROL_REG	(0x0394 - R_SYSGATE_BEGIN)
> +#define R_MP3_STATUS_REG	(0x0398 - R_SYSGATE_BEGIN)
> +#define R_MP3_CONTROL_REG	(0x039C - R_SYSGATE_BEGIN)
> +#define R_MP4_STATUS_REG	(0x03A0 - R_SYSGATE_BEGIN)
> +#define R_MP4_CONTROL_REG	(0x03A4 - R_SYSGATE_BEGIN)
> +#define R_MP5_STATUS_REG	(0x03A8 - R_SYSGATE_BEGIN)
> +#define R_MP5_CONTROL_REG	(0x03AC - R_SYSGATE_BEGIN)
> +#define R_MP6_STATUS_REG	(0x03B0 - R_SYSGATE_BEGIN)
> +#define R_MP6_CONTROL_REG	(0x03B4 - R_SYSGATE_BEGIN)
> +#define R_MP7_STATUS_REG	(0x03B8 - R_SYSGATE_BEGIN)
> +#define R_MP7_CONTROL_REG	(0x03BC - R_SYSGATE_BEGIN)
> +#define R_MP8_STATUS_REG	(0x03C0 - R_SYSGATE_BEGIN)
> +#define R_MP8_CONTROL_REG	(0x03C4 - R_SYSGATE_BEGIN)
> +#define R_MP9_STATUS_REG	(0x03C8 - R_SYSGATE_BEGIN)
> +#define R_MP9_CONTROL_REG	(0x03CC - R_SYSGATE_BEGIN)
> +#define R_MP10_STATUS_REG	(0x03D0 - R_SYSGATE_BEGIN)
> +#define R_MP10_CONTROL_REG	(0x03D4 - R_SYSGATE_BEGIN)
> +#define R_MP11_STATUS_REG	(0x03D8 - R_SYSGATE_BEGIN)
> +#define R_MP11_CONTROL_REG	(0x03DC - R_SYSGATE_BEGIN)
> +#define R_MP12_STATUS_REG	(0x03E0 - R_SYSGATE_BEGIN)
> +#define R_MP12_CONTROL_REG	(0x03E4 - R_SYSGATE_BEGIN)
> +#define R_MP13_STATUS_REG	(0x03E8 - R_SYSGATE_BEGIN)
> +#define R_MP13_CONTROL_REG	(0x03EC - R_SYSGATE_BEGIN)
> +#define R_MP14_STATUS_REG	(0x03F0 - R_SYSGATE_BEGIN)
> +#define R_MP14_CONTROL_REG	(0x03F4 - R_SYSGATE_BEGIN)
> +#define R_MP15_STATUS_REG	(0x03F8 - R_SYSGATE_BEGIN)
> +#define R_MP15_CONTROL_REG	(0x03FC - R_SYSGATE_BEGIN)

All these seem pretty regular to me. Couldn't they just be
#define R_MP_STATUS_REG(x)	(0x380 + 8*(x) - R_SYSGATE_BEGIN)
#define R_MP_CONTROL_REG(x)	(0x384 + 8*(x) - R_SYSGATE_BEGIN)

> +
> +/* Registers defined in CLOCK */
> +#define R_CLKENREG0		0x00
> +#define R_CLKENREG1		0x04
> +#define R_CLKSELREG0		0x20
> +#define R_CLKDIVREG0		0x40
> +#define R_CLKDIVREG1		0x44
> +#define R_CLKDIVREG2		0x48
> +#define R_CLKDIVREG3		0x4C
> +#define R_CLKDIVREG4		0x50
> +#define R_CLKDIVREG5		0x54
> +#define R_CLKDIVREG6		0x58
> +#define R_CLKDIVREG7		0x5C
> +#define R_CLKDIVREG8		0x60
> +#define R_CLKDIVREG9		0x64
> +#define R_CLKDIVREG10		0x68
> +#define R_CLKDIVREG11		0x6C
> +#define R_CLKDIVREG12		0x70
> +#define R_CLKDIVREG13		0x74
> +#define R_CLKDIVREG14		0x78
> +#define R_CLKDIVREG15		0x7C
> +#define R_CLKDIVREG16		0x80
> +#define R_CLKDIVREG17		0x84
> +#define R_CLKDIVREG18		0x88
> +#define R_CLKDIVREG19		0x8C
> +#define R_CLKDIVREG20		0x90
> +#define R_CLKDIVREG21		0x94
> +#define R_CLKDIVREG22		0x98
> +#define R_CLKDIVREG23		0x9C
> +#define R_CLKDIVREG24		0xA0
> +#define R_CLKDIVREG25		0xA4
> +#define R_CLKDIVREG26		0xA8
> +#define R_CLKDIVREG27		0xAC
> +#define R_CLKDIVREG28		0xB0
> +#define R_CLKDIVREG29		0xB4
> +#define R_CLKDIVREG30		0xB8

#define R_CLKDIVREG(x)	(0x40 + 4*(x))

> +
> +/* All following shift value are the same for all DIV registers */
> +#define SHIFT_DIV_RESET_CTRL	0
> +#define SHIFT_DIV_FACTOR_SEL	3
> +#define SHIFT_DIV_FACTOR	16
> +
> +/**
> + * struct sg2042_clk_data - Common data of clock-controller
> + * @iobase: base address of clock-controller
> + * @onecell_data: used for adding providers.
> + *
> + * Note: this structure will be used both by clkgen & sysclk.
> + */
> +struct sg2042_clk_data {
> +	void __iomem *iobase;
> +	struct clk_hw_onecell_data onecell_data;
> +};
> +
> +/**
> + * struct sg2042_pll_clock - PLL clock
> + * @hw:				clk_hw for initialization
> + * @id:				used to map clk_onecell_data
> + * @base:			used for readl/writel.
> + *				**NOTE**: PLL registers are all in SYS_CTRL!
> + * @lock:			spinlock to protect register access, modification
> + *				of frequency can only be served one at the time.
> + * @offset_status:		offset of pll status registers
> + * @offset_enable:		offset of pll enable registers
> + * @offset_ctrl:		offset of pll control registers
> + * @shift_status_lock:		shift of XXX_LOCK in pll status register
> + * @shift_status_updating:	shift of UPDATING_XXX in pll status register
> + * @shift_enable:		shift of XXX_CLK_EN in pll enable register
> + */
> +struct sg2042_pll_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +	void __iomem *base;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_status;
> +	u32 offset_enable;
> +	u32 offset_ctrl;
> +	u8 shift_status_lock;
> +	u8 shift_status_updating;
> +	u8 shift_enable;
> +};
> +
> +#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
> +
> +/**
> + * struct sg2042_divider_clock - Divider clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @reg:		used for readl/writel.
> + *			**NOTE**: DIV registers are ALL in CLOCK!
> + * @lock:		spinlock to protect register access, modification of
> + *			frequency can only be served one at the time
> + * @offset_ctrl:	offset of divider control registers
> + * @shift:		shift of "Clock Divider Factor" in divider control register
> + * @width:		width of "Clock Divider Factor" in divider control register
> + * @div_flags:		private flags for this clock, not for framework-specific
> + * @initval:		In the divider control register, we can configure whether
> + *			to use the value of "Clock Divider Factor" or just use
> + *			the initial value pre-configured by IC. BIT[3] controls
> + *			this and by default (value is 0), means initial value
> + *			is used.
> + *			**NOTE** that we cannot read the initial value (default
> + *			value when poweron) and default value of "Clock Divider
> + *			Factor" is zero, which I think is a hardware design flaw
> + *			and should be sync-ed with the initial value. So in
> + *			software we have to add a configuration item (initval)
> + *			to manually configure this value and use it when BIT[3]
> + *			is zero.
> + */
> +struct sg2042_divider_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	void __iomem *reg;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_ctrl;
> +	u8 shift;
> +	u8 width;
> +	u8 div_flags;
> +	u32 initval;
> +};
> +
> +#define to_sg2042_clk_divider(_hw)	\
> +	container_of(_hw, struct sg2042_divider_clock, hw)
> +
> +/**
> + * struct sg2042_gate_clock - Gate clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_enable:	offset of gate enable registers
> + * @bit_idx:		which bit in the register controls gating of this clock
> + */
> +struct sg2042_gate_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_enable;
> +	u8 bit_idx;
> +};
> +
> +/**
> + * struct sg2042_mux_clock - Mux clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_select:	offset of mux selection registers
> + *			**NOTE**: MUX registers are ALL in CLOCK!
> + * @shift:		shift of "Clock Select" in mux selection register
> + * @width:		width of "Clock Select" in mux selection register
> + * @clk_nb:		used for notification
> + * @original_index:	set by notifier callback
> + */
> +struct sg2042_mux_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_select;
> +	u8 shift;
> +	u8 width;
> +
> +	struct notifier_block clk_nb;
> +	u8 original_index;
> +};
> +
> +#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb)
> +
> +#define KHZ 1000UL
> +#define MHZ (KHZ * KHZ)
> +
> +#define REFDIV_MIN 1
> +#define REFDIV_MAX 63
> +#define FBDIV_MIN 16
> +#define FBDIV_MAX 320
> +
> +#define PLL_FREF_SG2042 (25 * MHZ)
> +
> +#define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
> +#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
> +
> +#define PLL_FOUTVCO_MIN (800 * MHZ)
> +#define PLL_FOUTVCO_MAX (3200 * MHZ)
> +
> +struct sg2042_pll_ctrl {
> +	unsigned long freq;
> +	unsigned int fbdiv;
> +	unsigned int postdiv1;
> +	unsigned int postdiv2;
> +	unsigned int refdiv;
> +};
> +
> +#define PLLCTRL_FBDIV_SHIFT	16
> +#define PLLCTRL_FBDIV_MASK	(GENMASK(27, 16) >> PLLCTRL_FBDIV_SHIFT)
> +#define PLLCTRL_POSTDIV2_SHIFT	12
> +#define PLLCTRL_POSTDIV2_MASK	(GENMASK(14, 12) >> PLLCTRL_POSTDIV2_SHIFT)
> +#define PLLCTRL_POSTDIV1_SHIFT	8
> +#define PLLCTRL_POSTDIV1_MASK	(GENMASK(10, 8) >> PLLCTRL_POSTDIV1_SHIFT)
> +#define PLLCTRL_REFDIV_SHIFT	0
> +#define PLLCTRL_REFDIV_MASK	(GENMASK(5, 0) >> PLLCTRL_REFDIV_SHIFT)

You'll only need half of these defines if you use..

> +
> +static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
> +{
> +	return ((ctrl->fbdiv & PLLCTRL_FBDIV_MASK) << PLLCTRL_FBDIV_SHIFT) |
> +	       ((ctrl->postdiv2 & PLLCTRL_POSTDIV2_MASK) << PLLCTRL_POSTDIV2_SHIFT) |
> +	       ((ctrl->postdiv1 & PLLCTRL_POSTDIV1_MASK) << PLLCTRL_POSTDIV1_SHIFT) |
> +	       ((ctrl->refdiv & PLLCTRL_REFDIV_MASK) << PLLCTRL_REFDIV_SHIFT);

..the FIELD_PREP macro here..

> +}
> +
> +static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
> +					  struct sg2042_pll_ctrl *ctrl)
> +{
> +	ctrl->fbdiv = (reg_value >> PLLCTRL_FBDIV_SHIFT) & PLLCTRL_FBDIV_MASK;
> +	ctrl->refdiv = (reg_value >> PLLCTRL_REFDIV_SHIFT) & PLLCTRL_REFDIV_MASK;
> +	ctrl->postdiv1 = (reg_value >> PLLCTRL_POSTDIV1_SHIFT) & PLLCTRL_POSTDIV1_MASK;
> +	ctrl->postdiv2 = (reg_value >> PLLCTRL_POSTDIV2_SHIFT) & PLLCTRL_POSTDIV2_MASK;

..and the FIELD_GET macro here.

> +}
> +
> +static inline int sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
> +{
> +	u32 value;
> +
> +	if (en) {
> +		/* wait pll lock */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      ((value >> pll->shift_status_lock) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s not locked\n", pll->hw.init->name);
> +
> +		/* wait pll updating */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      !((value >> pll->shift_status_updating) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s still updating\n", pll->hw.init->name);
> +
> +		/* enable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value | (1 << pll->shift_enable), pll->base + pll->offset_enable);
> +	} else {
> +		/* disable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value & (~(1 << pll->shift_enable)), pll->base + pll->offset_enable);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * sg2042_pll_recalc_rate() - Calculate rate for plls
> + * @reg_value: current register value
> + * @parent_rate: parent frequency
> + *
> + * This function is used to calculate below "rate" in equation
> + * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
> + *      = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
> + *
> + * Return: The rate calculated.
> + */
> +static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
> +					    unsigned long parent_rate)
> +{
> +	struct sg2042_pll_ctrl ctrl_table;
> +	u64 numerator, denominator;
> +
> +	sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
> +
> +	numerator = parent_rate * ctrl_table.fbdiv;
> +	denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
> +	do_div(numerator, denominator);
> +	return numerator;
> +}
> +
> +/**
> + * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
> + * look up the postdiv1_2 table to get the closest postdiiv combination.
> + * @rate: FOUTPOSTDIV
> + * @prate: parent rate, i.e. FREF
> + * @fbdiv: FBDIV
> + * @refdiv: REFDIV
> + * @postdiv1: POSTDIV1, output
> + * @postdiv2: POSTDIV2, output
> + *
> + * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
> + * for example:
> + * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
> + *
> + * See TRM:
> + * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
> + * So we get following formula to get POSTDIV1 and POSTDIV2:
> + * POSTDIV = (prate/REFDIV) x FBDIV/rate
> + * above POSTDIV = POSTDIV1*POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument, which means Failed to get the postdivs.
> + */
> +static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
> +				      unsigned long prate,
> +				      unsigned int fbdiv,
> +				      unsigned int refdiv,
> +				      unsigned int *postdiv1,
> +				      unsigned int *postdiv2)
> +{
> +	int index;
> +	u64 tmp0;
> +
> +	/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
> +	#define	POSTDIV_RESULT_INDEX	2
> +
> +	static const int postdiv1_2[][3] = {
> +		{2, 4,  8}, {3, 3,  9}, {2, 5, 10}, {2, 6, 12},
> +		{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
> +		{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
> +		{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
> +		{6, 7, 42}, {7, 7, 49}
> +	};
> +
> +	/* prate/REFDIV and result save to tmp0 */
> +	tmp0 = prate;
> +	do_div(tmp0, refdiv);
> +
> +	/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
> +	tmp0 *= fbdiv;
> +
> +	/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
> +	do_div(tmp0, rate);
> +
> +	/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
> +	if (tmp0 <= 7) {
> +		/* (div1 * div2) <= 7, no need to use array search */
> +		*postdiv1 = tmp0;
> +		*postdiv2 = 1;
> +		return 0;
> +	}
> +
> +	/* (div1 * div2) > 7, use array search */
> +	for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
> +		if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
> +			continue;
> +		} else {
> +			/* found it */
> +			*postdiv1 = postdiv1_2[index][1];
> +			*postdiv2 = postdiv1_2[index][0];
> +			return 0;
> +		}
> +	}
> +	pr_warn("%s can not find in postdiv array!\n", __func__);
> +	return -EINVAL;
> +}
> +
> +/**
> + * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
> + * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
> + * register.
> + * @req_rate: expected output clock rate, i.e. FOUTPISTDIV
> + * @parent_rate: input parent clock rate, i.e. FREF
> + * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument
> + */
> +static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
> +				      unsigned long req_rate,
> +				      unsigned long parent_rate)
> +{
> +	int ret;
> +	unsigned int fbdiv, refdiv, postdiv1, postdiv2;
> +	unsigned long foutpostdiv;
> +	u64 tmp;
> +	u64 foutvco;
> +
> +	if (parent_rate != PLL_FREF_SG2042) {
> +		pr_err("INVALID FREF: %ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
> +		pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
> +		return -EINVAL;
> +	}
> +
> +	memset(best, 0, sizeof(struct sg2042_pll_ctrl));
> +
> +	for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
> +		/* required by hardware: FREF/REFDIV must > 10 */
> +		tmp = parent_rate;
> +		do_div(tmp, refdiv);
> +		if (tmp <= 10)
> +			continue;
> +
> +		for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
> +			/*
> +			 * FOUTVCO = FREF*FBDIV/REFDIV validation
> +			 * required by hardware, FOUTVCO must [800MHz, 3200MHz]
> +			 */
> +			foutvco = parent_rate * fbdiv;
> +			do_div(foutvco, refdiv);
> +			if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
> +				continue;
> +
> +			ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
> +							 fbdiv, refdiv,
> +							 &postdiv1, &postdiv2);
> +			if (ret)
> +				continue;
> +
> +			/*
> +			 * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
> +			 *             = FOUTVCO/(POSTDIV1*POSTDIV2)
> +			 */
> +			tmp = foutvco;
> +			do_div(tmp, (postdiv1 * postdiv2));
> +			foutpostdiv = (unsigned long)tmp;
> +			/* Iterative to approach the expected value */
> +			if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
> +				best->freq = foutpostdiv;
> +				best->refdiv = refdiv;
> +				best->fbdiv = fbdiv;
> +				best->postdiv1 = postdiv1;
> +				best->postdiv2 = postdiv2;
> +				if (foutpostdiv == req_rate)
> +					return 0;
> +			}
> +			continue;
> +		}
> +	}
> +
> +	if (best->freq == 0)
> +		return -EINVAL;
> +	else
> +		return 0;
> +}
> +
> +/**
> + * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
> + * @hw: ccf use to hook get sg2042_pll_clock
> + * @parent_rate: parent rate
> + *
> + * The is function will be called through clk_get_rate
> + * and return current rate after decoding reg value
> + *
> + * Return: Current rate recalculated.
> + */
> +static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	u32 value;
> +	unsigned long rate;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	value = readl(pll->base + pll->offset_ctrl);
> +	rate = sg2042_pll_recalc_rate(value, parent_rate);
> +
> +	pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), rate);
> +	return rate;
> +}
> +
> +static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
> +				      unsigned long req_rate,
> +				      unsigned long *prate)
> +{
> +	unsigned int value;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	long proper_rate;
> +	int ret;
> +
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
> +	if (ret) {
> +		proper_rate = 0;
> +		goto out;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +	proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
> +
> +out:
> +	pr_debug("--> %s: pll_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), proper_rate);
> +	return proper_rate;
> +}
> +
> +static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
> +					 struct clk_rate_request *req)
> +{
> +	req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
> +					      &req->best_parent_rate);
> +	pr_debug("--> %s: pll_determine_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), req->rate);
> +	return 0;
> +}
> +
> +static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
> +				   unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	unsigned long flags;
> +	u32 value;
> +	int ret = 0;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	spin_lock_irqsave(pll->lock, flags);
> +	if (sg2042_pll_enable(pll, 0)) {
> +		pr_warn("Can't disable pll(%s), status error\n", pll->hw.init->name);
> +		goto out;
> +	}
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
> +	if (ret) {
> +		pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
> +		goto out2;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +
> +	/* write the value to top register */
> +	writel(value, pll->base + pll->offset_ctrl);
> +
> +out2:
> +	sg2042_pll_enable(pll, 1);
> +out:
> +	spin_unlock_irqrestore(pll->lock, flags);
> +
> +	pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
> +		 clk_hw_get_name(hw), value);
> +	return ret;
> +}
> +
> +static const struct clk_ops sg2042_clk_pll_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +	.determine_rate = sg2042_clk_pll_determine_rate,
> +	.set_rate = sg2042_clk_pll_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_pll_ro_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +};
> +
> +static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,
> +						    unsigned long parent_rate)
> +{
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +	u32 val;
> +	unsigned long ret_rate;
> +
> +	if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +		val = divider->initval;
> +	} else {
> +		val = readl(divider->reg) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +	}
> +
> +	ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL,
> +				       divider->div_flags, divider->width);
> +
> +	pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static long sg2042_clk_divider_round_rate(struct clk_hw *hw,
> +					  unsigned long rate,
> +					  unsigned long *prate)
> +{
> +	u32 bestdiv;
> +	unsigned long ret_rate;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->div_flags & CLK_DIVIDER_READ_ONLY) {
> +		if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +			bestdiv = divider->initval;
> +		} else {
> +			bestdiv = readl(divider->reg) >> divider->shift;
> +			bestdiv &= clk_div_mask(divider->width);
> +		}
> +		ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
> +	} else {
> +		ret_rate = divider_round_rate(hw, rate, prate, NULL,
> +					      divider->width, divider->div_flags);
> +	}
> +
> +	pr_debug("--> %s: divider_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static int sg2042_clk_divider_set_rate(struct clk_hw *hw,
> +				       unsigned long rate,
> +				       unsigned long parent_rate)
> +{
> +	u32 value;
> +	u32 val, val2;
> +	unsigned long flags = 0;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	value = divider_get_val(rate, parent_rate, NULL,
> +				divider->width, divider->div_flags);
> +
> +	if (divider->lock)
> +		spin_lock_irqsave(divider->lock, flags);
> +	else
> +		__acquire(divider->lock);
> +
> +	/*
> +	 * The sequence of clock frequency modification is:
> +	 * Assert to reset divider.
> +	 * Modify the value of Clock Divide Factor (and High Wide if needed).
> +	 * De-assert to restore divided clock with new frequency.
> +	 */
> +	val = readl(divider->reg);
> +
> +	/* assert */
> +	val &= ~BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +		val = clk_div_mask(divider->width) << (divider->shift + 16);
> +	} else {
> +		val = readl(divider->reg);
> +		val &= ~(clk_div_mask(divider->width) << divider->shift);
> +	}
> +	val |= value << divider->shift;
> +	val |= BIT(SHIFT_DIV_FACTOR_SEL);
> +	writel(val, divider->reg);
> +	val2 = val;
> +
> +	/* de-assert */
> +	val |= BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->lock)
> +		spin_unlock_irqrestore(divider->lock, flags);
> +	else
> +		__release(divider->lock);
> +
> +	pr_debug("--> %s: divider_set_rate: register val = 0x%x\n",
> +		 clk_hw_get_name(hw), val2);
> +	return 0;
> +}
> +
> +static const struct clk_ops sg2042_clk_divider_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +	.set_rate = sg2042_clk_divider_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_divider_ro_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +};
> +
> +/*
> + * Clock initialization macro naming rules:
> + * FW: use CLK_HW_INIT_FW_NAME
> + * HW: use CLK_HW_INIT_HW
> + * HWS: use CLK_HW_INIT_HWS
> + * RO: means Read-Only
> + */
> +#define SG2042_PLL_FW(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}
> +
> +#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ro_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}

These macros are pretty confusing. Please at least try to keep the arguments
and assignments in close to the same order.

> +
> +static struct sg2042_pll_clock sg2042_pll_clks[] = {
> +	SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main",
> +		      R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_MPLL_CONTROL, 0),
> +	SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_FPLL_CONTROL, 3),
> +	SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL0_CONTROL, 4),
> +	SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL1_CONTROL, 5),

Also the STAT and CLK_GEN_CONTROL registers seem to be the same offset for all
the PLLs. Why do you need to store them in memory?

> +};
> +
> +#define SG2042_DIV_FW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_FW_RO(_id, _name, _parent,				\
> +		  _r_ctrl, _shift, _width,				\
> +		  _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW_RO(_id, _name, _parent,				\
> +			 _r_ctrl, _shift, _width,			\
> +			 _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS(_id, _name, _parent,				\
> +		       _r_ctrl, _shift, _width,				\
> +		       _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\
> +			  _r_ctrl, _shift, _width,			\
> +			  _div_flag, _initval) {			\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\
> +			_r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HWS(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HW(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_FW_NAME(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \
> +		.hw.init = CLK_HW_INIT_PARENTS_HW(		\
> +				_name,				\
> +				_parents,			\
> +				NULL,				\
> +				_flags),			\
> +		.id = _id,					\
> +		.offset_select = _r_select,			\
> +		.shift = _shift,				\
> +		.width = _width,				\
> +	}
> +
> +/*
> + * Clock items in the array are sorted according to the clock-tree diagram,
> + * from top to bottom, from upstream to downstream. Read TRM for details.
> + */
> +
> +/* updated during probe/registration */
> +static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = {
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG27, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG28, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG29, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG30, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0,
> +		       "clk_gate_rp_cpu_normal_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG0, 4),
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1,
> +		       "clk_gate_rp_cpu_normal_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG1, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG25, 4),
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG26, 4),
> +};
> +
> +#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_1[] = {
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0,
> +			  "clk_div_ddr01_0", clk_gate_ddr01_div0,
> +			  R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1,
> +			  "clk_div_ddr01_1", clk_gate_ddr01_div1,
> +			  R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0,
> +			  "clk_div_ddr23_0", clk_gate_ddr23_div0,
> +			  R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1,
> +			  "clk_div_ddr23_1", clk_gate_ddr23_div1,
> +			  R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0,
> +		       "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0,
> +		       R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1,
> +		       "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1,
> +		       R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0,
> +		       "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0,
> +		       R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1,
> +		       "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1,
> +		       R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1),
> +};
> +
> +/*
> + * Note: regarding names for mux clock, "0/1" or "div0/div1" means the
> + * first/second parent input source, not the register value.
> + * For example:
> + * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and
> + * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",
> + * they are both controlled by register CLKDIVREG27;
> + * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and
> + * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1",
> + * they are both controlled by register CLKDIVREG28;
> + * While for register value of mux selection, use Clock Select for DDR01’s clock
> + * as example, see CLKSELREG0, bit[2].
> + * 1: Select in_dpll0_clk as clock source, correspondng to the parent input
> + *    source from "clk_div_ddr01_0".
> + * 0: Select in_fpll_clk as clock source, corresponding to the parent input
> + *    source from "clk_div_ddr01_1".
> + * So we need a table to define the array of register values corresponding to
> + * the parent index and tell CCF about this when registering mux clock.
> + */
> +static const u32 sg2042_mux_table[] = {1, 0};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_ddr01_0		(&sg2042_div_clks_level_1[0].hw)
> +#define clk_div_ddr01_1		(&sg2042_div_clks_level_1[1].hw)
> +#define clk_div_ddr23_0		(&sg2042_div_clks_level_1[2].hw)
> +#define clk_div_ddr23_1		(&sg2042_div_clks_level_1[3].hw)
> +#define clk_div_rp_cpu_normal_0	(&sg2042_div_clks_level_1[4].hw)
> +#define clk_div_rp_cpu_normal_1	(&sg2042_div_clks_level_1[5].hw)
> +#define clk_div_axi_ddr_0	(&sg2042_div_clks_level_1[6].hw)
> +#define clk_div_axi_ddr_1	(&sg2042_div_clks_level_1[7].hw)
> +
> +static const struct clk_hw *clk_mux_ddr01_p[] = {
> +	clk_div_ddr01_0,
> +	clk_div_ddr01_1,
> +};
> +
> +static const struct clk_hw *clk_mux_ddr23_p[] = {
> +	clk_div_ddr23_0,
> +	clk_div_ddr23_1,
> +};
> +
> +static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = {
> +	clk_div_rp_cpu_normal_0,
> +	clk_div_rp_cpu_normal_1,
> +};
> +
> +static const struct clk_hw *clk_mux_axi_ddr_p[] = {
> +	clk_div_axi_ddr_0,
> +	clk_div_axi_ddr_1,
> +};
> +
> +/* Mux clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_mux_ddr01[] = { NULL };
> +static const struct clk_hw *clk_mux_ddr23[] = { NULL };
> +static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_mux_axi_ddr[] = { NULL };
> +
> +static struct sg2042_mux_clock sg2042_mux_clks[] = {
> +	SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 2, 1),
> +	SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 3, 1),
> +	SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 0, 1),
> +	SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 1, 1),
> +};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_top_rp_cmn_div2	(&sg2042_div_clks_level_2[0].hw)
> +#define clk_div_50m_a53		(&sg2042_div_clks_level_2[1].hw)
> +#define clk_div_timer1		(&sg2042_div_clks_level_2[2].hw)
> +#define clk_div_timer2		(&sg2042_div_clks_level_2[3].hw)
> +#define clk_div_timer3		(&sg2042_div_clks_level_2[4].hw)
> +#define clk_div_timer4		(&sg2042_div_clks_level_2[5].hw)
> +#define clk_div_timer5		(&sg2042_div_clks_level_2[6].hw)
> +#define clk_div_timer6		(&sg2042_div_clks_level_2[7].hw)
> +#define clk_div_timer7		(&sg2042_div_clks_level_2[8].hw)
> +#define clk_div_timer8		(&sg2042_div_clks_level_2[9].hw)
> +#define clk_div_uart_500m	(&sg2042_div_clks_level_2[10].hw)
> +#define clk_div_ahb_lpc		(&sg2042_div_clks_level_2[11].hw)
> +#define clk_div_efuse		(&sg2042_div_clks_level_2[12].hw)
> +#define clk_div_tx_eth0		(&sg2042_div_clks_level_2[13].hw)
> +#define clk_div_ptp_ref_i_eth0	(&sg2042_div_clks_level_2[14].hw)
> +#define clk_div_ref_eth0	(&sg2042_div_clks_level_2[15].hw)
> +#define clk_div_emmc		(&sg2042_div_clks_level_2[16].hw)
> +#define clk_div_sd		(&sg2042_div_clks_level_2[17].hw)
> +#define clk_div_top_axi0	(&sg2042_div_clks_level_2[18].hw)
> +#define clk_div_100k_emmc	(&sg2042_div_clks_level_2[19].hw)
> +#define clk_div_100k_sd		(&sg2042_div_clks_level_2[20].hw)
> +#define clk_div_gpio_db		(&sg2042_div_clks_level_2[21].hw)
> +#define clk_div_top_axi_hsperi	(&sg2042_div_clks_level_2[22].hw)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_2[] = {
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2,
> +		       "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal,
> +		       R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll",
> +		      R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20),
> +	/* downstream of div_50m_a53 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53,
> +		      R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53,
> +		      R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53,
> +		      R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53,
> +		      R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53,
> +		      R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53,
> +		      R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53,
> +		      R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53,
> +		      R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1),
> +
> +	/*
> +	 * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too
> +	 * narrow for us to produce 115200. Use UART internal divider directly.
> +	 */
> +	SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll",
> +			 R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll",
> +		      R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll",
> +		      R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll",
> +		      R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0,
> +		      "clk_div_ptp_ref_i_eth0", "fpll",
> +		      R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll",
> +		      R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll",
> +		      R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll",
> +		      R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll",
> +		      R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10),
> +	/* downstream of div_top_axi0 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0,
> +		      R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0,
> +		      R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0,
> +		      R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI,
> +		      "clk_div_top_axi_hsperi", "fpll",
> +		      R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4),
> +};
> +
> +/* Gate clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = {
> +	SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 14),
> +
> +	SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 15),
> +
> +	SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL,
> +			"clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG0, 0),
> +
> +	SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 13),
> +
> +	/* upon are gate clocks directly downstream of muxes */
> +
> +	/* downstream of clk_div_top_rp_cmn_div2 */
> +	SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2,
> +		       "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2),
> +	SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2,
> +			CLK_SET_RATE_PARENT, R_CLKENREG1, 10),
> +
> +	/*
> +	 * downstream of clk_gate_rp_cpu_normal
> +	 *
> +	 * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal
> +	 * and clk_gate_axi_pcie0/clk_gate_axi_pcie1.
> +	 * But the 1/2 DIV is fixed and no configurable register exported, so
> +	 * when reading from these two clocks, the rate value are still the
> +	 * same as that of clk_gate_rp_cpu_normal, it's not correct.
> +	 * This just affects the value read.
> +	 */
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0,
> +			"clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1,
> +			"clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),
> +
> +	/* downstream of div_50m_a53 */
> +	SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1),
> +	SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 12),
> +	SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 13),
> +	SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 14),
> +	SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 15),
> +	SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 16),
> +	SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 17),
> +	SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 18),
> +	SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 19),
> +
> +	/* gate clocks downstream from div clocks one-to-one */
> +	SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4),
> +	SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 7),
> +	SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 20),
> +	SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 30),
> +	SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0,
> +		       "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 0),
> +	SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 1),
> +	SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 3),
> +	SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 6),
> +
> +	/* downstream of clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 8),
> +	SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 9),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 10),
> +	SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 11),
> +	SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 21),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 22),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR,
> +		       "clk_gate_apb_gpio_intr", clk_div_top_axi0,
> +		       CLK_IS_CRITICAL, R_CLKENREG0, 23),
> +	SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 26),
> +	SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 27),
> +	SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 28),
> +	SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 29),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 11),
> +	/* downstream of DIV clocks which are sourced from clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 24),
> +	SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 4),
> +	SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 7),
> +
> +	/* downstream of clk_div_top_axi_hsperi */
> +	SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI,
> +		       "clk_gate_sysdma_axi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 3),
> +	SG2042_GATE_HW(GATE_CLK_APB_UART,
> +		       "clk_gate_apb_uart", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 5),
> +	SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C,
> +		       "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 6),
> +	SG2042_GATE_HW(GATE_CLK_APB_SPI,
> +		       "clk_gate_apb_spi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 25),
> +	SG2042_GATE_HW(GATE_CLK_AXI_ETH0,
> +		       "clk_gate_axi_eth0", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 31),
> +	SG2042_GATE_HW(GATE_CLK_AXI_EMMC,
> +		       "clk_gate_axi_emmc", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 2),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SD,
> +		       "clk_gate_axi_sd", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 5),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI,
> +		       "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 12),
> +};
> +
> +/*
> + * Gate clocks for RP subsystem (including the MP subsystem), which control
> + * registers are defined in SYS_CTRL.
> + */
> +static const struct sg2042_gate_clock sg2042_gate_rp[] = {
> +	/* downstream of clk_gate_rp_cpu_normal about rxu */
> +	SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 0),
> +	SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 1),
> +	SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 2),
> +	SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 3),
> +	SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 4),
> +	SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 5),
> +	SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 6),
> +	SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 7),
> +	SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 8),
> +	SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 9),
> +	SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 10),
> +	SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 11),
> +	SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 12),
> +	SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 13),
> +	SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 14),
> +	SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 15),
> +	SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 16),
> +	SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 17),
> +	SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 18),
> +	SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 19),
> +	SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 20),
> +	SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 21),
> +	SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 22),
> +	SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 23),
> +	SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 24),
> +	SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 25),
> +	SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 26),
> +	SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 27),
> +	SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 28),
> +	SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 29),
> +	SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 30),
> +	SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 31),
> +
> +	/* downstream of clk_gate_rp_cpu_normal about mp */
> +	SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0),
> +};
> +
> +static DEFINE_SPINLOCK(sg2042_clk_lock);
> +
> +static int sg2042_clk_register_plls(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_pll_clock pll_clks[],
> +				    int num_pll_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_pll_clock *pll;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_pll_clks; i++) {
> +		pll = &pll_clks[i];
> +		/* assign these for ops usage during registration */
> +		pll->base = clk_data->iobase;
> +		pll->lock = &sg2042_clk_lock;
> +
> +		hw = &pll->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", pll->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[pll->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_divs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_divider_clock div_clks[],
> +				    int num_div_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_divider_clock *div;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_div_clks; i++) {
> +		div = &div_clks[i];
> +
> +		if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +			if (div->width + div->shift > 16) {
> +				pr_warn("divider value exceeds LOWORD field\n");
> +				ret = -EINVAL;
> +				break;
> +			}
> +		}
> +
> +		div->reg = clk_data->iobase + div->offset_ctrl;
> +		div->lock = &sg2042_clk_lock;
> +
> +		hw = &div->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", div->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[div->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates(struct device *dev,
> +				     struct sg2042_clk_data *clk_data,
> +				     const struct sg2042_gate_clock gate_clks[],
> +				     int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = __devm_clk_hw_register_gate
> +			(dev,
> +			 NULL,
> +			 gate->hw.init->name,
> +			 NULL,
> +			 gate->hw.init->parent_hws[0],
> +			 NULL,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_RP_CPU_NORMAL:
> +			*clk_gate_rp_cpu_normal = hw;
> +			break;
> +		case GATE_CLK_TOP_RP_CMN_DIV2:
> +			*clk_gate_top_rp_cmn_div2 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates_fw(struct device *dev,
> +					struct sg2042_clk_data *clk_data,
> +					const struct sg2042_gate_clock gate_clks[],
> +					int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = devm_clk_hw_register_gate_parent_data
> +			(dev,
> +			 gate->hw.init->name,
> +			 gate->hw.init->parent_data,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_DDR01_DIV0:
> +			*clk_gate_ddr01_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR01_DIV1:
> +			*clk_gate_ddr01_div1 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV0:
> +			*clk_gate_ddr23_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV1:
> +			*clk_gate_ddr23_div1 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV0:
> +			*clk_gate_rp_cpu_normal_div0 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV1:
> +			*clk_gate_rp_cpu_normal_div1 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV0:
> +			*clk_gate_axi_ddr_div0 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV1:
> +			*clk_gate_axi_ddr_div1 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_mux_notifier_cb(struct notifier_block *nb,
> +				  unsigned long event,
> +				  void *data)
> +{
> +	int ret = 0;
> +	struct clk_notifier_data *ndata = data;
> +	struct clk_hw *hw = __clk_get_hw(ndata->clk);
> +	const struct clk_ops *ops = &clk_mux_ops;
> +	struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb);

Please order these by line length where possible. That goes for the whole file.

Eg. look up 'reverse xmas tree' in fx.
Documentation/process/maintainer-netdev.rst

> +
> +	/* To switch to fpll before changing rate and restore after that */
> +	if (event == PRE_RATE_CHANGE) {
> +		mux->original_index = ops->get_parent(hw);
> +
> +		/*
> +		 * "1" is the array index of the second parent input source of
> +		 * mux. For SG2042, it's fpll for all mux clocks.
> +		 * "0" is the array index of the frist parent input source of
> +		 * mux, For SG2042, it's mpll.
> +		 * FIXME, any good idea to avoid magic number?
> +		 */
> +		if (mux->original_index == 0)
> +			ret = ops->set_parent(hw, 1);
> +	} else if (event == POST_RATE_CHANGE) {
> +		ret = ops->set_parent(hw, mux->original_index);
> +	}
> +
> +	return notifier_from_errno(ret);
> +}
> +
> +static int sg2042_clk_register_muxs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_mux_clock mux_clks[],
> +				    int num_mux_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_mux_clock *mux;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_mux_clks; i++) {
> +		mux = &mux_clks[i];
> +
> +		hw = __devm_clk_hw_register_mux
> +			(dev,
> +			 NULL,
> +			 mux->hw.init->name,
> +			 mux->hw.init->num_parents,
> +			 NULL,
> +			 mux->hw.init->parent_hws,
> +			 NULL,
> +			 mux->hw.init->flags,
> +			 clk_data->iobase + mux->offset_select,
> +			 mux->shift,
> +			 BIT(mux->width) - 1,
> +			 0,
> +			 sg2042_mux_table,
> +			 &sg2042_clk_lock);

Does checkpatch.pl --strict not warn about this interesting indentation?

> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", mux->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[mux->id] = hw;
> +
> +		/* Updated some clocks which takes the role of parent */
> +		switch (mux->id) {
> +		case MUX_CLK_DDR01:
> +			*clk_mux_ddr01 = hw;
> +			break;
> +		case MUX_CLK_DDR23:
> +			*clk_mux_ddr23 = hw;
> +			break;
> +		case MUX_CLK_RP_CPU_NORMAL:
> +			*clk_mux_rp_cpu_normal = hw;
> +			break;
> +		case MUX_CLK_AXI_DDR:
> +			*clk_mux_axi_ddr = hw;
> +			break;
> +		}
> +
> +		/*
> +		 * FIXME: Theoretically, we should set parent for the
> +		 * mux, but seems hardware has done this for us with
> +		 * default value, so we don't set parent again here.
> +		 */
> +
> +		if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {
> +			mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;
> +			ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb);
> +			if (ret) {
> +				pr_err("failed to register clock notifier for %s\n",
> +				       mux->hw.init->name);
> +				break;
> +			}
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_init_clkdata(struct platform_device *pdev,
> +			       int num_clks,
> +			       struct sg2042_clk_data **pp_clk_data)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;

No need to initialize this. You're setting it on the line just below. There are
many other instances of this throughout the file.

> + +	clk_data = devm_kzalloc(&pdev->dev,
> +				struct_size(clk_data, onecell_data.hws, num_clks),
> +				GFP_KERNEL);
> +	if (!clk_data)
> +		return -ENOMEM;
> +
> +	clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
> +	if (WARN_ON(IS_ERR(clk_data->iobase)))
> +		return PTR_ERR(clk_data->iobase);
> +
> +	clk_data->onecell_data.num = num_clks;
> +
> +	*pp_clk_data = clk_data;
> +
> +	return 0;
> +}
> +
> +static int sg2042_clkgen_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_div_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_mux_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 gates */
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,
> +					   sg2042_gate_clks_level_1,
> +					   ARRAY_SIZE(sg2042_gate_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,
> +				       ARRAY_SIZE(sg2042_div_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* mux */
> +	ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,
> +				       ARRAY_SIZE(sg2042_mux_clks));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,
> +				       ARRAY_SIZE(sg2042_div_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 gate */
> +	ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,
> +					ARRAY_SIZE(sg2042_gate_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_rpgate_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_gate_rp);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data, sg2042_gate_rp,
> +					   num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_pll_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_pll_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
> +				       num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id sg2042_clkgen_match[] = {
> +	{ .compatible = "sophgo,sg2042-clkgen" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_clkgen_driver = {
> +	.probe = sg2042_clkgen_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-clkgen",
> +		.of_match_table = sg2042_clkgen_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_clkgen_driver);
> +
> +static const struct of_device_id sg2042_rpgate_match[] = {
> +	{ .compatible = "sophgo,sg2042-rpgate" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_rpgate_driver = {
> +	.probe = sg2042_rpgate_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-rpgate",
> +		.of_match_table = sg2042_rpgate_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_rpgate_driver);
> +
> +static const struct of_device_id sg2042_pll_match[] = {
> +	{ .compatible = "sophgo,sg2042-pll" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_pll_driver = {
> +	.probe = sg2042_pll_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-pll",
> +		.of_match_table = sg2042_pll_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_pll_driver);

You have 3 different drivers in one file. Could this not be split into 3
different modules? This file is almost 2k lines long already.

Also do they all need to be built in? Eg. could some of them not be loaded as a
module later in the boot process or are they all critical to get to loading the
initramfs?

/Emil

> --
> 2.25.1
>
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v15 4/5] clk: sophgo: Add SG2042 clock driver
@ 2024-04-30  7:48     ` Emil Renner Berthing
  0 siblings, 0 replies; 18+ messages in thread
From: Emil Renner Berthing @ 2024-04-30  7:48 UTC (permalink / raw)
  To: Chen Wang, aou, chao.wei, conor, krzysztof.kozlowski+dt,
	mturquette, palmer, paul.walmsley, richardcochran, robh+dt,
	sboyd, devicetree, linux-clk, linux-kernel, linux-riscv,
	haijiao.liu, xiaoguang.xing, guoren, jszhang, inochiama,
	samuel.holland
  Cc: Chen Wang

Chen Wang wrote:
> From: Chen Wang <unicorn_wang@outlook.com>
>
> Add a driver for the SOPHGO SG2042 clocks.
>
> Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
> ---
>  drivers/clk/Kconfig                    |    1 +
>  drivers/clk/Makefile                   |    1 +
>  drivers/clk/sophgo/Kconfig             |    8 +
>  drivers/clk/sophgo/Makefile            |    2 +
>  drivers/clk/sophgo/clk-sophgo-sg2042.c | 1870 ++++++++++++++++++++++++
>  5 files changed, 1882 insertions(+)
>  create mode 100644 drivers/clk/sophgo/Kconfig
>  create mode 100644 drivers/clk/sophgo/Makefile
>  create mode 100644 drivers/clk/sophgo/clk-sophgo-sg2042.c
>
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 50af5fc7f570..bc28502ec3c9 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
>  source "drivers/clk/samsung/Kconfig"
>  source "drivers/clk/sifive/Kconfig"
>  source "drivers/clk/socfpga/Kconfig"
> +source "drivers/clk/sophgo/Kconfig"
>  source "drivers/clk/sprd/Kconfig"
>  source "drivers/clk/starfive/Kconfig"
>  source "drivers/clk/sunxi/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 14fa8d4ecc1f..4abe16c8ccdf 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
>  obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
>  obj-$(CONFIG_CLK_SIFIVE)		+= sifive/
>  obj-y					+= socfpga/
> +obj-y					+= sophgo/
>  obj-$(CONFIG_PLAT_SPEAR)		+= spear/
>  obj-y					+= sprd/
>  obj-$(CONFIG_ARCH_STI)			+= st/
> diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig
> new file mode 100644
> index 000000000000..82516adb0f97
> --- /dev/null
> +++ b/drivers/clk/sophgo/Kconfig
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# common clock support for SOPHGO SoC family.
> +
> +config CLK_SOPHGO_SG2042
> +	bool "Sophgo SG2042 clock support"
> +	depends on ARCH_SOPHGO || COMPILE_TEST
> +	help
> +	  Say yes here to support the clock controller on the Sophgo SG2042 SoC.
> diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile
> new file mode 100644
> index 000000000000..13834cce260c
> --- /dev/null
> +++ b/drivers/clk/sophgo/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_CLK_SOPHGO_SG2042)	+= clk-sophgo-sg2042.o
> diff --git a/drivers/clk/sophgo/clk-sophgo-sg2042.c b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> new file mode 100644
> index 000000000000..e64e1cb4872b
> --- /dev/null
> +++ b/drivers/clk/sophgo/clk-sophgo-sg2042.c
> @@ -0,0 +1,1870 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Sophgo SG2042 Clock Generator Driver
> + *
> + * Copyright (C) 2024 Sophgo Technology Inc. All rights reserved.
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/platform_device.h>
> +#include <asm/div64.h>
> +
> +/*
> + * The clock of SG2042 is composed of three parts.
> + * The registers of these three parts of the clock are scattered in three
> + * different memory address spaces:
> + * - pll clocks
> + * - gate clocks for RP subsystem
> + * - div/mux, and gate clocks working for other subsystem than RP subsystem
> + */
> +#include <dt-bindings/clock/sophgo,sg2042-pll.h>
> +#include <dt-bindings/clock/sophgo,sg2042-rpgate.h>
> +#include <dt-bindings/clock/sophgo,sg2042-clkgen.h>
> +
> +/* Registers defined in SYS_CTRL */
> +#define R_PLL_BEGIN		0xC0
> +#define R_PLL_STAT		(0xC0 - R_PLL_BEGIN)
> +#define R_PLL_CLKEN_CONTROL	(0xC4 - R_PLL_BEGIN)
> +#define R_MPLL_CONTROL		(0xE8 - R_PLL_BEGIN)
> +#define R_FPLL_CONTROL		(0xF4 - R_PLL_BEGIN)
> +#define R_DPLL0_CONTROL		(0xF8 - R_PLL_BEGIN)
> +#define R_DPLL1_CONTROL		(0xFC - R_PLL_BEGIN)
> +
> +#define R_SYSGATE_BEGIN		0x0368
> +#define R_RP_RXU_CLK_ENABLE	(0x0368 - R_SYSGATE_BEGIN)
> +#define R_MP0_STATUS_REG	(0x0380 - R_SYSGATE_BEGIN)
> +#define R_MP0_CONTROL_REG	(0x0384 - R_SYSGATE_BEGIN)
> +#define R_MP1_STATUS_REG	(0x0388 - R_SYSGATE_BEGIN)
> +#define R_MP1_CONTROL_REG	(0x038C - R_SYSGATE_BEGIN)
> +#define R_MP2_STATUS_REG	(0x0390 - R_SYSGATE_BEGIN)
> +#define R_MP2_CONTROL_REG	(0x0394 - R_SYSGATE_BEGIN)
> +#define R_MP3_STATUS_REG	(0x0398 - R_SYSGATE_BEGIN)
> +#define R_MP3_CONTROL_REG	(0x039C - R_SYSGATE_BEGIN)
> +#define R_MP4_STATUS_REG	(0x03A0 - R_SYSGATE_BEGIN)
> +#define R_MP4_CONTROL_REG	(0x03A4 - R_SYSGATE_BEGIN)
> +#define R_MP5_STATUS_REG	(0x03A8 - R_SYSGATE_BEGIN)
> +#define R_MP5_CONTROL_REG	(0x03AC - R_SYSGATE_BEGIN)
> +#define R_MP6_STATUS_REG	(0x03B0 - R_SYSGATE_BEGIN)
> +#define R_MP6_CONTROL_REG	(0x03B4 - R_SYSGATE_BEGIN)
> +#define R_MP7_STATUS_REG	(0x03B8 - R_SYSGATE_BEGIN)
> +#define R_MP7_CONTROL_REG	(0x03BC - R_SYSGATE_BEGIN)
> +#define R_MP8_STATUS_REG	(0x03C0 - R_SYSGATE_BEGIN)
> +#define R_MP8_CONTROL_REG	(0x03C4 - R_SYSGATE_BEGIN)
> +#define R_MP9_STATUS_REG	(0x03C8 - R_SYSGATE_BEGIN)
> +#define R_MP9_CONTROL_REG	(0x03CC - R_SYSGATE_BEGIN)
> +#define R_MP10_STATUS_REG	(0x03D0 - R_SYSGATE_BEGIN)
> +#define R_MP10_CONTROL_REG	(0x03D4 - R_SYSGATE_BEGIN)
> +#define R_MP11_STATUS_REG	(0x03D8 - R_SYSGATE_BEGIN)
> +#define R_MP11_CONTROL_REG	(0x03DC - R_SYSGATE_BEGIN)
> +#define R_MP12_STATUS_REG	(0x03E0 - R_SYSGATE_BEGIN)
> +#define R_MP12_CONTROL_REG	(0x03E4 - R_SYSGATE_BEGIN)
> +#define R_MP13_STATUS_REG	(0x03E8 - R_SYSGATE_BEGIN)
> +#define R_MP13_CONTROL_REG	(0x03EC - R_SYSGATE_BEGIN)
> +#define R_MP14_STATUS_REG	(0x03F0 - R_SYSGATE_BEGIN)
> +#define R_MP14_CONTROL_REG	(0x03F4 - R_SYSGATE_BEGIN)
> +#define R_MP15_STATUS_REG	(0x03F8 - R_SYSGATE_BEGIN)
> +#define R_MP15_CONTROL_REG	(0x03FC - R_SYSGATE_BEGIN)

All these seem pretty regular to me. Couldn't they just be
#define R_MP_STATUS_REG(x)	(0x380 + 8*(x) - R_SYSGATE_BEGIN)
#define R_MP_CONTROL_REG(x)	(0x384 + 8*(x) - R_SYSGATE_BEGIN)

> +
> +/* Registers defined in CLOCK */
> +#define R_CLKENREG0		0x00
> +#define R_CLKENREG1		0x04
> +#define R_CLKSELREG0		0x20
> +#define R_CLKDIVREG0		0x40
> +#define R_CLKDIVREG1		0x44
> +#define R_CLKDIVREG2		0x48
> +#define R_CLKDIVREG3		0x4C
> +#define R_CLKDIVREG4		0x50
> +#define R_CLKDIVREG5		0x54
> +#define R_CLKDIVREG6		0x58
> +#define R_CLKDIVREG7		0x5C
> +#define R_CLKDIVREG8		0x60
> +#define R_CLKDIVREG9		0x64
> +#define R_CLKDIVREG10		0x68
> +#define R_CLKDIVREG11		0x6C
> +#define R_CLKDIVREG12		0x70
> +#define R_CLKDIVREG13		0x74
> +#define R_CLKDIVREG14		0x78
> +#define R_CLKDIVREG15		0x7C
> +#define R_CLKDIVREG16		0x80
> +#define R_CLKDIVREG17		0x84
> +#define R_CLKDIVREG18		0x88
> +#define R_CLKDIVREG19		0x8C
> +#define R_CLKDIVREG20		0x90
> +#define R_CLKDIVREG21		0x94
> +#define R_CLKDIVREG22		0x98
> +#define R_CLKDIVREG23		0x9C
> +#define R_CLKDIVREG24		0xA0
> +#define R_CLKDIVREG25		0xA4
> +#define R_CLKDIVREG26		0xA8
> +#define R_CLKDIVREG27		0xAC
> +#define R_CLKDIVREG28		0xB0
> +#define R_CLKDIVREG29		0xB4
> +#define R_CLKDIVREG30		0xB8

#define R_CLKDIVREG(x)	(0x40 + 4*(x))

> +
> +/* All following shift value are the same for all DIV registers */
> +#define SHIFT_DIV_RESET_CTRL	0
> +#define SHIFT_DIV_FACTOR_SEL	3
> +#define SHIFT_DIV_FACTOR	16
> +
> +/**
> + * struct sg2042_clk_data - Common data of clock-controller
> + * @iobase: base address of clock-controller
> + * @onecell_data: used for adding providers.
> + *
> + * Note: this structure will be used both by clkgen & sysclk.
> + */
> +struct sg2042_clk_data {
> +	void __iomem *iobase;
> +	struct clk_hw_onecell_data onecell_data;
> +};
> +
> +/**
> + * struct sg2042_pll_clock - PLL clock
> + * @hw:				clk_hw for initialization
> + * @id:				used to map clk_onecell_data
> + * @base:			used for readl/writel.
> + *				**NOTE**: PLL registers are all in SYS_CTRL!
> + * @lock:			spinlock to protect register access, modification
> + *				of frequency can only be served one at the time.
> + * @offset_status:		offset of pll status registers
> + * @offset_enable:		offset of pll enable registers
> + * @offset_ctrl:		offset of pll control registers
> + * @shift_status_lock:		shift of XXX_LOCK in pll status register
> + * @shift_status_updating:	shift of UPDATING_XXX in pll status register
> + * @shift_enable:		shift of XXX_CLK_EN in pll enable register
> + */
> +struct sg2042_pll_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +	void __iomem *base;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_status;
> +	u32 offset_enable;
> +	u32 offset_ctrl;
> +	u8 shift_status_lock;
> +	u8 shift_status_updating;
> +	u8 shift_enable;
> +};
> +
> +#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
> +
> +/**
> + * struct sg2042_divider_clock - Divider clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @reg:		used for readl/writel.
> + *			**NOTE**: DIV registers are ALL in CLOCK!
> + * @lock:		spinlock to protect register access, modification of
> + *			frequency can only be served one at the time
> + * @offset_ctrl:	offset of divider control registers
> + * @shift:		shift of "Clock Divider Factor" in divider control register
> + * @width:		width of "Clock Divider Factor" in divider control register
> + * @div_flags:		private flags for this clock, not for framework-specific
> + * @initval:		In the divider control register, we can configure whether
> + *			to use the value of "Clock Divider Factor" or just use
> + *			the initial value pre-configured by IC. BIT[3] controls
> + *			this and by default (value is 0), means initial value
> + *			is used.
> + *			**NOTE** that we cannot read the initial value (default
> + *			value when poweron) and default value of "Clock Divider
> + *			Factor" is zero, which I think is a hardware design flaw
> + *			and should be sync-ed with the initial value. So in
> + *			software we have to add a configuration item (initval)
> + *			to manually configure this value and use it when BIT[3]
> + *			is zero.
> + */
> +struct sg2042_divider_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	void __iomem *reg;
> +	/* protect register access */
> +	spinlock_t *lock;
> +
> +	u32 offset_ctrl;
> +	u8 shift;
> +	u8 width;
> +	u8 div_flags;
> +	u32 initval;
> +};
> +
> +#define to_sg2042_clk_divider(_hw)	\
> +	container_of(_hw, struct sg2042_divider_clock, hw)
> +
> +/**
> + * struct sg2042_gate_clock - Gate clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_enable:	offset of gate enable registers
> + * @bit_idx:		which bit in the register controls gating of this clock
> + */
> +struct sg2042_gate_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_enable;
> +	u8 bit_idx;
> +};
> +
> +/**
> + * struct sg2042_mux_clock - Mux clock
> + * @hw:			clk_hw for initialization
> + * @id:			used to map clk_onecell_data
> + * @offset_select:	offset of mux selection registers
> + *			**NOTE**: MUX registers are ALL in CLOCK!
> + * @shift:		shift of "Clock Select" in mux selection register
> + * @width:		width of "Clock Select" in mux selection register
> + * @clk_nb:		used for notification
> + * @original_index:	set by notifier callback
> + */
> +struct sg2042_mux_clock {
> +	struct clk_hw hw;
> +
> +	unsigned int id;
> +
> +	u32 offset_select;
> +	u8 shift;
> +	u8 width;
> +
> +	struct notifier_block clk_nb;
> +	u8 original_index;
> +};
> +
> +#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb)
> +
> +#define KHZ 1000UL
> +#define MHZ (KHZ * KHZ)
> +
> +#define REFDIV_MIN 1
> +#define REFDIV_MAX 63
> +#define FBDIV_MIN 16
> +#define FBDIV_MAX 320
> +
> +#define PLL_FREF_SG2042 (25 * MHZ)
> +
> +#define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
> +#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
> +
> +#define PLL_FOUTVCO_MIN (800 * MHZ)
> +#define PLL_FOUTVCO_MAX (3200 * MHZ)
> +
> +struct sg2042_pll_ctrl {
> +	unsigned long freq;
> +	unsigned int fbdiv;
> +	unsigned int postdiv1;
> +	unsigned int postdiv2;
> +	unsigned int refdiv;
> +};
> +
> +#define PLLCTRL_FBDIV_SHIFT	16
> +#define PLLCTRL_FBDIV_MASK	(GENMASK(27, 16) >> PLLCTRL_FBDIV_SHIFT)
> +#define PLLCTRL_POSTDIV2_SHIFT	12
> +#define PLLCTRL_POSTDIV2_MASK	(GENMASK(14, 12) >> PLLCTRL_POSTDIV2_SHIFT)
> +#define PLLCTRL_POSTDIV1_SHIFT	8
> +#define PLLCTRL_POSTDIV1_MASK	(GENMASK(10, 8) >> PLLCTRL_POSTDIV1_SHIFT)
> +#define PLLCTRL_REFDIV_SHIFT	0
> +#define PLLCTRL_REFDIV_MASK	(GENMASK(5, 0) >> PLLCTRL_REFDIV_SHIFT)

You'll only need half of these defines if you use..

> +
> +static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
> +{
> +	return ((ctrl->fbdiv & PLLCTRL_FBDIV_MASK) << PLLCTRL_FBDIV_SHIFT) |
> +	       ((ctrl->postdiv2 & PLLCTRL_POSTDIV2_MASK) << PLLCTRL_POSTDIV2_SHIFT) |
> +	       ((ctrl->postdiv1 & PLLCTRL_POSTDIV1_MASK) << PLLCTRL_POSTDIV1_SHIFT) |
> +	       ((ctrl->refdiv & PLLCTRL_REFDIV_MASK) << PLLCTRL_REFDIV_SHIFT);

..the FIELD_PREP macro here..

> +}
> +
> +static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
> +					  struct sg2042_pll_ctrl *ctrl)
> +{
> +	ctrl->fbdiv = (reg_value >> PLLCTRL_FBDIV_SHIFT) & PLLCTRL_FBDIV_MASK;
> +	ctrl->refdiv = (reg_value >> PLLCTRL_REFDIV_SHIFT) & PLLCTRL_REFDIV_MASK;
> +	ctrl->postdiv1 = (reg_value >> PLLCTRL_POSTDIV1_SHIFT) & PLLCTRL_POSTDIV1_MASK;
> +	ctrl->postdiv2 = (reg_value >> PLLCTRL_POSTDIV2_SHIFT) & PLLCTRL_POSTDIV2_MASK;

..and the FIELD_GET macro here.

> +}
> +
> +static inline int sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
> +{
> +	u32 value;
> +
> +	if (en) {
> +		/* wait pll lock */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      ((value >> pll->shift_status_lock) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s not locked\n", pll->hw.init->name);
> +
> +		/* wait pll updating */
> +		if (readl_poll_timeout_atomic(pll->base + pll->offset_status,
> +					      value,
> +					      !((value >> pll->shift_status_updating) & 0x1),
> +					      0,
> +					      100000))
> +			pr_warn("%s still updating\n", pll->hw.init->name);
> +
> +		/* enable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value | (1 << pll->shift_enable), pll->base + pll->offset_enable);
> +	} else {
> +		/* disable pll */
> +		value = readl(pll->base + pll->offset_enable);
> +		writel(value & (~(1 << pll->shift_enable)), pll->base + pll->offset_enable);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * sg2042_pll_recalc_rate() - Calculate rate for plls
> + * @reg_value: current register value
> + * @parent_rate: parent frequency
> + *
> + * This function is used to calculate below "rate" in equation
> + * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
> + *      = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
> + *
> + * Return: The rate calculated.
> + */
> +static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
> +					    unsigned long parent_rate)
> +{
> +	struct sg2042_pll_ctrl ctrl_table;
> +	u64 numerator, denominator;
> +
> +	sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
> +
> +	numerator = parent_rate * ctrl_table.fbdiv;
> +	denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
> +	do_div(numerator, denominator);
> +	return numerator;
> +}
> +
> +/**
> + * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
> + * look up the postdiv1_2 table to get the closest postdiiv combination.
> + * @rate: FOUTPOSTDIV
> + * @prate: parent rate, i.e. FREF
> + * @fbdiv: FBDIV
> + * @refdiv: REFDIV
> + * @postdiv1: POSTDIV1, output
> + * @postdiv2: POSTDIV2, output
> + *
> + * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
> + * for example:
> + * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
> + *
> + * See TRM:
> + * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
> + * So we get following formula to get POSTDIV1 and POSTDIV2:
> + * POSTDIV = (prate/REFDIV) x FBDIV/rate
> + * above POSTDIV = POSTDIV1*POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument, which means Failed to get the postdivs.
> + */
> +static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
> +				      unsigned long prate,
> +				      unsigned int fbdiv,
> +				      unsigned int refdiv,
> +				      unsigned int *postdiv1,
> +				      unsigned int *postdiv2)
> +{
> +	int index;
> +	u64 tmp0;
> +
> +	/* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
> +	#define	POSTDIV_RESULT_INDEX	2
> +
> +	static const int postdiv1_2[][3] = {
> +		{2, 4,  8}, {3, 3,  9}, {2, 5, 10}, {2, 6, 12},
> +		{2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
> +		{4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
> +		{4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
> +		{6, 7, 42}, {7, 7, 49}
> +	};
> +
> +	/* prate/REFDIV and result save to tmp0 */
> +	tmp0 = prate;
> +	do_div(tmp0, refdiv);
> +
> +	/* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
> +	tmp0 *= fbdiv;
> +
> +	/* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
> +	do_div(tmp0, rate);
> +
> +	/* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
> +	if (tmp0 <= 7) {
> +		/* (div1 * div2) <= 7, no need to use array search */
> +		*postdiv1 = tmp0;
> +		*postdiv2 = 1;
> +		return 0;
> +	}
> +
> +	/* (div1 * div2) > 7, use array search */
> +	for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
> +		if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
> +			continue;
> +		} else {
> +			/* found it */
> +			*postdiv1 = postdiv1_2[index][1];
> +			*postdiv2 = postdiv1_2[index][0];
> +			return 0;
> +		}
> +	}
> +	pr_warn("%s can not find in postdiv array!\n", __func__);
> +	return -EINVAL;
> +}
> +
> +/**
> + * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
> + * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
> + * register.
> + * @req_rate: expected output clock rate, i.e. FOUTPISTDIV
> + * @parent_rate: input parent clock rate, i.e. FREF
> + * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
> + *
> + * Return:
> + * %0 - OK
> + * %-EINVAL - invalid argument
> + */
> +static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
> +				      unsigned long req_rate,
> +				      unsigned long parent_rate)
> +{
> +	int ret;
> +	unsigned int fbdiv, refdiv, postdiv1, postdiv2;
> +	unsigned long foutpostdiv;
> +	u64 tmp;
> +	u64 foutvco;
> +
> +	if (parent_rate != PLL_FREF_SG2042) {
> +		pr_err("INVALID FREF: %ld\n", parent_rate);
> +		return -EINVAL;
> +	}
> +
> +	if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
> +		pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
> +		return -EINVAL;
> +	}
> +
> +	memset(best, 0, sizeof(struct sg2042_pll_ctrl));
> +
> +	for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
> +		/* required by hardware: FREF/REFDIV must > 10 */
> +		tmp = parent_rate;
> +		do_div(tmp, refdiv);
> +		if (tmp <= 10)
> +			continue;
> +
> +		for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
> +			/*
> +			 * FOUTVCO = FREF*FBDIV/REFDIV validation
> +			 * required by hardware, FOUTVCO must [800MHz, 3200MHz]
> +			 */
> +			foutvco = parent_rate * fbdiv;
> +			do_div(foutvco, refdiv);
> +			if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
> +				continue;
> +
> +			ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
> +							 fbdiv, refdiv,
> +							 &postdiv1, &postdiv2);
> +			if (ret)
> +				continue;
> +
> +			/*
> +			 * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
> +			 *             = FOUTVCO/(POSTDIV1*POSTDIV2)
> +			 */
> +			tmp = foutvco;
> +			do_div(tmp, (postdiv1 * postdiv2));
> +			foutpostdiv = (unsigned long)tmp;
> +			/* Iterative to approach the expected value */
> +			if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
> +				best->freq = foutpostdiv;
> +				best->refdiv = refdiv;
> +				best->fbdiv = fbdiv;
> +				best->postdiv1 = postdiv1;
> +				best->postdiv2 = postdiv2;
> +				if (foutpostdiv == req_rate)
> +					return 0;
> +			}
> +			continue;
> +		}
> +	}
> +
> +	if (best->freq == 0)
> +		return -EINVAL;
> +	else
> +		return 0;
> +}
> +
> +/**
> + * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
> + * @hw: ccf use to hook get sg2042_pll_clock
> + * @parent_rate: parent rate
> + *
> + * The is function will be called through clk_get_rate
> + * and return current rate after decoding reg value
> + *
> + * Return: Current rate recalculated.
> + */
> +static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	u32 value;
> +	unsigned long rate;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	value = readl(pll->base + pll->offset_ctrl);
> +	rate = sg2042_pll_recalc_rate(value, parent_rate);
> +
> +	pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), rate);
> +	return rate;
> +}
> +
> +static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
> +				      unsigned long req_rate,
> +				      unsigned long *prate)
> +{
> +	unsigned int value;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	long proper_rate;
> +	int ret;
> +
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
> +	if (ret) {
> +		proper_rate = 0;
> +		goto out;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +	proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
> +
> +out:
> +	pr_debug("--> %s: pll_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), proper_rate);
> +	return proper_rate;
> +}
> +
> +static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
> +					 struct clk_rate_request *req)
> +{
> +	req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
> +					      &req->best_parent_rate);
> +	pr_debug("--> %s: pll_determine_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), req->rate);
> +	return 0;
> +}
> +
> +static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
> +				   unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	unsigned long flags;
> +	u32 value;
> +	int ret = 0;
> +	struct sg2042_pll_ctrl pctrl_table;
> +	struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
> +
> +	spin_lock_irqsave(pll->lock, flags);
> +	if (sg2042_pll_enable(pll, 0)) {
> +		pr_warn("Can't disable pll(%s), status error\n", pll->hw.init->name);
> +		goto out;
> +	}
> +	ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
> +	if (ret) {
> +		pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
> +		goto out2;
> +	}
> +
> +	value = sg2042_pll_ctrl_encode(&pctrl_table);
> +
> +	/* write the value to top register */
> +	writel(value, pll->base + pll->offset_ctrl);
> +
> +out2:
> +	sg2042_pll_enable(pll, 1);
> +out:
> +	spin_unlock_irqrestore(pll->lock, flags);
> +
> +	pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
> +		 clk_hw_get_name(hw), value);
> +	return ret;
> +}
> +
> +static const struct clk_ops sg2042_clk_pll_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +	.determine_rate = sg2042_clk_pll_determine_rate,
> +	.set_rate = sg2042_clk_pll_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_pll_ro_ops = {
> +	.recalc_rate = sg2042_clk_pll_recalc_rate,
> +	.round_rate = sg2042_clk_pll_round_rate,
> +};
> +
> +static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw,
> +						    unsigned long parent_rate)
> +{
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +	u32 val;
> +	unsigned long ret_rate;
> +
> +	if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +		val = divider->initval;
> +	} else {
> +		val = readl(divider->reg) >> divider->shift;
> +		val &= clk_div_mask(divider->width);
> +	}
> +
> +	ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL,
> +				       divider->div_flags, divider->width);
> +
> +	pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static long sg2042_clk_divider_round_rate(struct clk_hw *hw,
> +					  unsigned long rate,
> +					  unsigned long *prate)
> +{
> +	u32 bestdiv;
> +	unsigned long ret_rate;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	/* if read only, just return current value */
> +	if (divider->div_flags & CLK_DIVIDER_READ_ONLY) {
> +		if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) {
> +			bestdiv = divider->initval;
> +		} else {
> +			bestdiv = readl(divider->reg) >> divider->shift;
> +			bestdiv &= clk_div_mask(divider->width);
> +		}
> +		ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
> +	} else {
> +		ret_rate = divider_round_rate(hw, rate, prate, NULL,
> +					      divider->width, divider->div_flags);
> +	}
> +
> +	pr_debug("--> %s: divider_round_rate: val = %ld\n",
> +		 clk_hw_get_name(hw), ret_rate);
> +	return ret_rate;
> +}
> +
> +static int sg2042_clk_divider_set_rate(struct clk_hw *hw,
> +				       unsigned long rate,
> +				       unsigned long parent_rate)
> +{
> +	u32 value;
> +	u32 val, val2;
> +	unsigned long flags = 0;
> +	struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw);
> +
> +	value = divider_get_val(rate, parent_rate, NULL,
> +				divider->width, divider->div_flags);
> +
> +	if (divider->lock)
> +		spin_lock_irqsave(divider->lock, flags);
> +	else
> +		__acquire(divider->lock);
> +
> +	/*
> +	 * The sequence of clock frequency modification is:
> +	 * Assert to reset divider.
> +	 * Modify the value of Clock Divide Factor (and High Wide if needed).
> +	 * De-assert to restore divided clock with new frequency.
> +	 */
> +	val = readl(divider->reg);
> +
> +	/* assert */
> +	val &= ~BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +		val = clk_div_mask(divider->width) << (divider->shift + 16);
> +	} else {
> +		val = readl(divider->reg);
> +		val &= ~(clk_div_mask(divider->width) << divider->shift);
> +	}
> +	val |= value << divider->shift;
> +	val |= BIT(SHIFT_DIV_FACTOR_SEL);
> +	writel(val, divider->reg);
> +	val2 = val;
> +
> +	/* de-assert */
> +	val |= BIT(SHIFT_DIV_RESET_CTRL);
> +	writel(val, divider->reg);
> +
> +	if (divider->lock)
> +		spin_unlock_irqrestore(divider->lock, flags);
> +	else
> +		__release(divider->lock);
> +
> +	pr_debug("--> %s: divider_set_rate: register val = 0x%x\n",
> +		 clk_hw_get_name(hw), val2);
> +	return 0;
> +}
> +
> +static const struct clk_ops sg2042_clk_divider_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +	.set_rate = sg2042_clk_divider_set_rate,
> +};
> +
> +static const struct clk_ops sg2042_clk_divider_ro_ops = {
> +	.recalc_rate = sg2042_clk_divider_recalc_rate,
> +	.round_rate = sg2042_clk_divider_round_rate,
> +};
> +
> +/*
> + * Clock initialization macro naming rules:
> + * FW: use CLK_HW_INIT_FW_NAME
> + * HW: use CLK_HW_INIT_HW
> + * HWS: use CLK_HW_INIT_HWS
> + * RO: means Read-Only
> + */
> +#define SG2042_PLL_FW(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}
> +
> +#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_stat, _r_enable, _r_ctrl, _shift) \
> +	{								\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_pll_ro_ops,			\
> +				CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.offset_status = _r_stat,				\
> +		.offset_enable = _r_enable,				\
> +		.shift_status_lock = 8 + (_shift),			\
> +		.shift_status_updating = _shift,			\
> +		.shift_enable = _shift,					\
> +	}

These macros are pretty confusing. Please at least try to keep the arguments
and assignments in close to the same order.

> +
> +static struct sg2042_pll_clock sg2042_pll_clks[] = {
> +	SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main",
> +		      R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_MPLL_CONTROL, 0),
> +	SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_FPLL_CONTROL, 3),
> +	SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL0_CONTROL, 4),
> +	SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1",
> +			 R_PLL_STAT, R_PLL_CLKEN_CONTROL, R_DPLL1_CONTROL, 5),

Also the STAT and CLK_GEN_CONTROL registers seem to be the same offset for all
the PLLs. Why do you need to store them in memory?

> +};
> +
> +#define SG2042_DIV_FW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_FW_RO(_id, _name, _parent,				\
> +		  _r_ctrl, _shift, _width,				\
> +		  _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_FW_NAME(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW(_id, _name, _parent,				\
> +		      _r_ctrl, _shift, _width,				\
> +		      _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HW_RO(_id, _name, _parent,				\
> +			 _r_ctrl, _shift, _width,			\
> +			 _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HW(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS(_id, _name, _parent,				\
> +		       _r_ctrl, _shift, _width,				\
> +		       _div_flag, _initval) {				\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = _div_flag,					\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_DIV_HWS_RO(_id, _name, _parent,				\
> +			  _r_ctrl, _shift, _width,			\
> +			  _div_flag, _initval) {			\
> +		.hw.init = CLK_HW_INIT_HWS(				\
> +				_name,					\
> +				_parent,				\
> +				&sg2042_clk_divider_ro_ops,		\
> +				0),					\
> +		.id = _id,						\
> +		.offset_ctrl = _r_ctrl,					\
> +		.shift = _shift,					\
> +		.width = _width,					\
> +		.div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY,	\
> +		.initval = _initval,					\
> +	}
> +
> +#define SG2042_GATE_HWS(_id, _name, _parent, _flags,	\
> +			_r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HWS(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_HW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_HW(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_GATE_FW(_id, _name, _parent, _flags,	\
> +		       _r_enable, _bit_idx) {		\
> +		.hw.init = CLK_HW_INIT_FW_NAME(		\
> +				_name,			\
> +				_parent,		\
> +				NULL,			\
> +				_flags),		\
> +		.id = _id,				\
> +		.offset_enable = _r_enable,		\
> +		.bit_idx = _bit_idx,			\
> +	}
> +
> +#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \
> +		.hw.init = CLK_HW_INIT_PARENTS_HW(		\
> +				_name,				\
> +				_parents,			\
> +				NULL,				\
> +				_flags),			\
> +		.id = _id,					\
> +		.offset_select = _r_select,			\
> +		.shift = _shift,				\
> +		.width = _width,				\
> +	}
> +
> +/*
> + * Clock items in the array are sorted according to the clock-tree diagram,
> + * from top to bottom, from upstream to downstream. Read TRM for details.
> + */
> +
> +/* updated during probe/registration */
> +static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL };
> +static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = {
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG27, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG28, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1",
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> +		       R_CLKDIVREG29, 4),
> +	SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG30, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0,
> +		       "clk_gate_rp_cpu_normal_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG0, 4),
> +	SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1,
> +		       "clk_gate_rp_cpu_normal_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG1, 4),
> +
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll",
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKDIVREG25, 4),
> +	SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll",
> +		       CLK_IS_CRITICAL,
> +		       R_CLKDIVREG26, 4),
> +};
> +
> +#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_1[] = {
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0,
> +			  "clk_div_ddr01_0", clk_gate_ddr01_div0,
> +			  R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1,
> +			  "clk_div_ddr01_1", clk_gate_ddr01_div1,
> +			  R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0,
> +			  "clk_div_ddr23_0", clk_gate_ddr23_div0,
> +			  R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1,
> +			  "clk_div_ddr23_1", clk_gate_ddr23_div1,
> +			  R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0,
> +		       "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0,
> +		       R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1,
> +		       "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1,
> +		       R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1),
> +
> +	SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0,
> +		       "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0,
> +		       R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2),
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1,
> +		       "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1,
> +		       R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1),
> +};
> +
> +/*
> + * Note: regarding names for mux clock, "0/1" or "div0/div1" means the
> + * first/second parent input source, not the register value.
> + * For example:
> + * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and
> + * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0",
> + * they are both controlled by register CLKDIVREG27;
> + * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and
> + * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1",
> + * they are both controlled by register CLKDIVREG28;
> + * While for register value of mux selection, use Clock Select for DDR01’s clock
> + * as example, see CLKSELREG0, bit[2].
> + * 1: Select in_dpll0_clk as clock source, correspondng to the parent input
> + *    source from "clk_div_ddr01_0".
> + * 0: Select in_fpll_clk as clock source, corresponding to the parent input
> + *    source from "clk_div_ddr01_1".
> + * So we need a table to define the array of register values corresponding to
> + * the parent index and tell CCF about this when registering mux clock.
> + */
> +static const u32 sg2042_mux_table[] = {1, 0};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_ddr01_0		(&sg2042_div_clks_level_1[0].hw)
> +#define clk_div_ddr01_1		(&sg2042_div_clks_level_1[1].hw)
> +#define clk_div_ddr23_0		(&sg2042_div_clks_level_1[2].hw)
> +#define clk_div_ddr23_1		(&sg2042_div_clks_level_1[3].hw)
> +#define clk_div_rp_cpu_normal_0	(&sg2042_div_clks_level_1[4].hw)
> +#define clk_div_rp_cpu_normal_1	(&sg2042_div_clks_level_1[5].hw)
> +#define clk_div_axi_ddr_0	(&sg2042_div_clks_level_1[6].hw)
> +#define clk_div_axi_ddr_1	(&sg2042_div_clks_level_1[7].hw)
> +
> +static const struct clk_hw *clk_mux_ddr01_p[] = {
> +	clk_div_ddr01_0,
> +	clk_div_ddr01_1,
> +};
> +
> +static const struct clk_hw *clk_mux_ddr23_p[] = {
> +	clk_div_ddr23_0,
> +	clk_div_ddr23_1,
> +};
> +
> +static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = {
> +	clk_div_rp_cpu_normal_0,
> +	clk_div_rp_cpu_normal_1,
> +};
> +
> +static const struct clk_hw *clk_mux_axi_ddr_p[] = {
> +	clk_div_axi_ddr_0,
> +	clk_div_axi_ddr_1,
> +};
> +
> +/* Mux clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_mux_ddr01[] = { NULL };
> +static const struct clk_hw *clk_mux_ddr23[] = { NULL };
> +static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_mux_axi_ddr[] = { NULL };
> +
> +static struct sg2042_mux_clock sg2042_mux_clks[] = {
> +	SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 2, 1),
> +	SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY,
> +		   R_CLKSELREG0, 3, 1),
> +	SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 0, 1),
> +	SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p,
> +		   CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> +		   R_CLKSELREG0, 1, 1),
> +};
> +
> +/* Aliases just for easy reading */
> +#define clk_div_top_rp_cmn_div2	(&sg2042_div_clks_level_2[0].hw)
> +#define clk_div_50m_a53		(&sg2042_div_clks_level_2[1].hw)
> +#define clk_div_timer1		(&sg2042_div_clks_level_2[2].hw)
> +#define clk_div_timer2		(&sg2042_div_clks_level_2[3].hw)
> +#define clk_div_timer3		(&sg2042_div_clks_level_2[4].hw)
> +#define clk_div_timer4		(&sg2042_div_clks_level_2[5].hw)
> +#define clk_div_timer5		(&sg2042_div_clks_level_2[6].hw)
> +#define clk_div_timer6		(&sg2042_div_clks_level_2[7].hw)
> +#define clk_div_timer7		(&sg2042_div_clks_level_2[8].hw)
> +#define clk_div_timer8		(&sg2042_div_clks_level_2[9].hw)
> +#define clk_div_uart_500m	(&sg2042_div_clks_level_2[10].hw)
> +#define clk_div_ahb_lpc		(&sg2042_div_clks_level_2[11].hw)
> +#define clk_div_efuse		(&sg2042_div_clks_level_2[12].hw)
> +#define clk_div_tx_eth0		(&sg2042_div_clks_level_2[13].hw)
> +#define clk_div_ptp_ref_i_eth0	(&sg2042_div_clks_level_2[14].hw)
> +#define clk_div_ref_eth0	(&sg2042_div_clks_level_2[15].hw)
> +#define clk_div_emmc		(&sg2042_div_clks_level_2[16].hw)
> +#define clk_div_sd		(&sg2042_div_clks_level_2[17].hw)
> +#define clk_div_top_axi0	(&sg2042_div_clks_level_2[18].hw)
> +#define clk_div_100k_emmc	(&sg2042_div_clks_level_2[19].hw)
> +#define clk_div_100k_sd		(&sg2042_div_clks_level_2[20].hw)
> +#define clk_div_gpio_db		(&sg2042_div_clks_level_2[21].hw)
> +#define clk_div_top_axi_hsperi	(&sg2042_div_clks_level_2[22].hw)
> +
> +static struct sg2042_divider_clock sg2042_div_clks_level_2[] = {
> +	SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2,
> +		       "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal,
> +		       R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll",
> +		      R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20),
> +	/* downstream of div_50m_a53 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53,
> +		      R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53,
> +		      R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53,
> +		      R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53,
> +		      R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53,
> +		      R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53,
> +		      R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53,
> +		      R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53,
> +		      R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1),
> +
> +	/*
> +	 * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too
> +	 * narrow for us to produce 115200. Use UART internal divider directly.
> +	 */
> +	SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll",
> +			 R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll",
> +		      R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll",
> +		      R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll",
> +		      R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0,
> +		      "clk_div_ptp_ref_i_eth0", "fpll",
> +		      R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll",
> +		      R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll",
> +		      R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10),
> +	SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll",
> +		      R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll",
> +		      R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10),
> +	/* downstream of div_top_axi0 */
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0,
> +		      R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0,
> +		      R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000),
> +	SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0,
> +		      R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000),
> +
> +	SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI,
> +		      "clk_div_top_axi_hsperi", "fpll",
> +		      R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4),
> +};
> +
> +/* Gate clocks to be updated during probe/registration */
> +static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL };
> +static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL };
> +
> +static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = {
> +	SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 14),
> +
> +	SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 15),
> +
> +	SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL,
> +			"clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG0, 0),
> +
> +	SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr,
> +			CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +			R_CLKENREG1, 13),
> +
> +	/* upon are gate clocks directly downstream of muxes */
> +
> +	/* downstream of clk_div_top_rp_cmn_div2 */
> +	SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2,
> +		       "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2),
> +	SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2,
> +			CLK_SET_RATE_PARENT, R_CLKENREG1, 10),
> +
> +	/*
> +	 * downstream of clk_gate_rp_cpu_normal
> +	 *
> +	 * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal
> +	 * and clk_gate_axi_pcie0/clk_gate_axi_pcie1.
> +	 * But the 1/2 DIV is fixed and no configurable register exported, so
> +	 * when reading from these two clocks, the rate value are still the
> +	 * same as that of clk_gate_rp_cpu_normal, it's not correct.
> +	 * This just affects the value read.
> +	 */
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0,
> +			"clk_gate_axi_pcie0", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8),
> +	SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1,
> +			"clk_gate_axi_pcie1", clk_gate_rp_cpu_normal,
> +			CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9),
> +
> +	/* downstream of div_50m_a53 */
> +	SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1),
> +	SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 12),
> +	SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 13),
> +	SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 14),
> +	SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 15),
> +	SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 16),
> +	SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 17),
> +	SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 18),
> +	SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 19),
> +
> +	/* gate clocks downstream from div clocks one-to-one */
> +	SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m,
> +		       CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4),
> +	SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 7),
> +	SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 20),
> +	SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 30),
> +	SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0,
> +		       "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 0),
> +	SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 1),
> +	SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 3),
> +	SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 6),
> +
> +	/* downstream of clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 8),
> +	SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 9),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 10),
> +	SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0,
> +		       CLK_IGNORE_UNUSED, R_CLKENREG0, 11),
> +	SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 21),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 22),
> +	SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR,
> +		       "clk_gate_apb_gpio_intr", clk_div_top_axi0,
> +		       CLK_IS_CRITICAL, R_CLKENREG0, 23),
> +	SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 26),
> +	SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 27),
> +	SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 28),
> +	SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0,
> +		       0, R_CLKENREG0, 29),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 11),
> +	/* downstream of DIV clocks which are sourced from clk_div_top_axi0 */
> +	SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 24),
> +	SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 4),
> +	SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 7),
> +
> +	/* downstream of clk_div_top_axi_hsperi */
> +	SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI,
> +		       "clk_gate_sysdma_axi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 3),
> +	SG2042_GATE_HW(GATE_CLK_APB_UART,
> +		       "clk_gate_apb_uart", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 5),
> +	SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C,
> +		       "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 6),
> +	SG2042_GATE_HW(GATE_CLK_APB_SPI,
> +		       "clk_gate_apb_spi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 25),
> +	SG2042_GATE_HW(GATE_CLK_AXI_ETH0,
> +		       "clk_gate_axi_eth0", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG0, 31),
> +	SG2042_GATE_HW(GATE_CLK_AXI_EMMC,
> +		       "clk_gate_axi_emmc", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 2),
> +	SG2042_GATE_HW(GATE_CLK_AXI_SD,
> +		       "clk_gate_axi_sd", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT, R_CLKENREG1, 5),
> +	SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI,
> +		       "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi,
> +		       CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> +		       R_CLKENREG1, 12),
> +};
> +
> +/*
> + * Gate clocks for RP subsystem (including the MP subsystem), which control
> + * registers are defined in SYS_CTRL.
> + */
> +static const struct sg2042_gate_clock sg2042_gate_rp[] = {
> +	/* downstream of clk_gate_rp_cpu_normal about rxu */
> +	SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 0),
> +	SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 1),
> +	SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 2),
> +	SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 3),
> +	SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 4),
> +	SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 5),
> +	SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 6),
> +	SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 7),
> +	SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 8),
> +	SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 9),
> +	SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 10),
> +	SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 11),
> +	SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 12),
> +	SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 13),
> +	SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 14),
> +	SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 15),
> +	SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 16),
> +	SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 17),
> +	SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 18),
> +	SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 19),
> +	SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 20),
> +	SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 21),
> +	SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 22),
> +	SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 23),
> +	SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 24),
> +	SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 25),
> +	SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 26),
> +	SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 27),
> +	SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 28),
> +	SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 29),
> +	SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 30),
> +	SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate",
> +		       0, R_RP_RXU_CLK_ENABLE, 31),
> +
> +	/* downstream of clk_gate_rp_cpu_normal about mp */
> +	SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0),
> +	SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate",
> +		       CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0),
> +};
> +
> +static DEFINE_SPINLOCK(sg2042_clk_lock);
> +
> +static int sg2042_clk_register_plls(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_pll_clock pll_clks[],
> +				    int num_pll_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_pll_clock *pll;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_pll_clks; i++) {
> +		pll = &pll_clks[i];
> +		/* assign these for ops usage during registration */
> +		pll->base = clk_data->iobase;
> +		pll->lock = &sg2042_clk_lock;
> +
> +		hw = &pll->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", pll->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[pll->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_divs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_divider_clock div_clks[],
> +				    int num_div_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_divider_clock *div;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_div_clks; i++) {
> +		div = &div_clks[i];
> +
> +		if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) {
> +			if (div->width + div->shift > 16) {
> +				pr_warn("divider value exceeds LOWORD field\n");
> +				ret = -EINVAL;
> +				break;
> +			}
> +		}
> +
> +		div->reg = clk_data->iobase + div->offset_ctrl;
> +		div->lock = &sg2042_clk_lock;
> +
> +		hw = &div->hw;
> +		ret = devm_clk_hw_register(dev, hw);
> +		if (ret) {
> +			pr_err("failed to register clock %s\n", div->hw.init->name);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[div->id] = hw;
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates(struct device *dev,
> +				     struct sg2042_clk_data *clk_data,
> +				     const struct sg2042_gate_clock gate_clks[],
> +				     int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = __devm_clk_hw_register_gate
> +			(dev,
> +			 NULL,
> +			 gate->hw.init->name,
> +			 NULL,
> +			 gate->hw.init->parent_hws[0],
> +			 NULL,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_RP_CPU_NORMAL:
> +			*clk_gate_rp_cpu_normal = hw;
> +			break;
> +		case GATE_CLK_TOP_RP_CMN_DIV2:
> +			*clk_gate_top_rp_cmn_div2 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_clk_register_gates_fw(struct device *dev,
> +					struct sg2042_clk_data *clk_data,
> +					const struct sg2042_gate_clock gate_clks[],
> +					int num_gate_clks)
> +{
> +	struct clk_hw *hw;
> +	const struct sg2042_gate_clock *gate;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_gate_clks; i++) {
> +		gate = &gate_clks[i];
> +		hw = devm_clk_hw_register_gate_parent_data
> +			(dev,
> +			 gate->hw.init->name,
> +			 gate->hw.init->parent_data,
> +			 gate->hw.init->flags,
> +			 clk_data->iobase + gate->offset_enable,
> +			 gate->bit_idx,
> +			 0,
> +			 &sg2042_clk_lock);
> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", gate->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[gate->id] = hw;
> +
> +		/* Updated some clocks which take the role of parent */
> +		switch (gate->id) {
> +		case GATE_CLK_DDR01_DIV0:
> +			*clk_gate_ddr01_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR01_DIV1:
> +			*clk_gate_ddr01_div1 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV0:
> +			*clk_gate_ddr23_div0 = hw;
> +			break;
> +		case GATE_CLK_DDR23_DIV1:
> +			*clk_gate_ddr23_div1 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV0:
> +			*clk_gate_rp_cpu_normal_div0 = hw;
> +			break;
> +		case GATE_CLK_RP_CPU_NORMAL_DIV1:
> +			*clk_gate_rp_cpu_normal_div1 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV0:
> +			*clk_gate_axi_ddr_div0 = hw;
> +			break;
> +		case GATE_CLK_AXI_DDR_DIV1:
> +			*clk_gate_axi_ddr_div1 = hw;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_mux_notifier_cb(struct notifier_block *nb,
> +				  unsigned long event,
> +				  void *data)
> +{
> +	int ret = 0;
> +	struct clk_notifier_data *ndata = data;
> +	struct clk_hw *hw = __clk_get_hw(ndata->clk);
> +	const struct clk_ops *ops = &clk_mux_ops;
> +	struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb);

Please order these by line length where possible. That goes for the whole file.

Eg. look up 'reverse xmas tree' in fx.
Documentation/process/maintainer-netdev.rst

> +
> +	/* To switch to fpll before changing rate and restore after that */
> +	if (event == PRE_RATE_CHANGE) {
> +		mux->original_index = ops->get_parent(hw);
> +
> +		/*
> +		 * "1" is the array index of the second parent input source of
> +		 * mux. For SG2042, it's fpll for all mux clocks.
> +		 * "0" is the array index of the frist parent input source of
> +		 * mux, For SG2042, it's mpll.
> +		 * FIXME, any good idea to avoid magic number?
> +		 */
> +		if (mux->original_index == 0)
> +			ret = ops->set_parent(hw, 1);
> +	} else if (event == POST_RATE_CHANGE) {
> +		ret = ops->set_parent(hw, mux->original_index);
> +	}
> +
> +	return notifier_from_errno(ret);
> +}
> +
> +static int sg2042_clk_register_muxs(struct device *dev,
> +				    struct sg2042_clk_data *clk_data,
> +				    struct sg2042_mux_clock mux_clks[],
> +				    int num_mux_clks)
> +{
> +	struct clk_hw *hw;
> +	struct sg2042_mux_clock *mux;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < num_mux_clks; i++) {
> +		mux = &mux_clks[i];
> +
> +		hw = __devm_clk_hw_register_mux
> +			(dev,
> +			 NULL,
> +			 mux->hw.init->name,
> +			 mux->hw.init->num_parents,
> +			 NULL,
> +			 mux->hw.init->parent_hws,
> +			 NULL,
> +			 mux->hw.init->flags,
> +			 clk_data->iobase + mux->offset_select,
> +			 mux->shift,
> +			 BIT(mux->width) - 1,
> +			 0,
> +			 sg2042_mux_table,
> +			 &sg2042_clk_lock);

Does checkpatch.pl --strict not warn about this interesting indentation?

> +		if (IS_ERR(hw)) {
> +			pr_err("failed to register clock %s\n", mux->hw.init->name);
> +			ret = PTR_ERR(hw);
> +			break;
> +		}
> +
> +		clk_data->onecell_data.hws[mux->id] = hw;
> +
> +		/* Updated some clocks which takes the role of parent */
> +		switch (mux->id) {
> +		case MUX_CLK_DDR01:
> +			*clk_mux_ddr01 = hw;
> +			break;
> +		case MUX_CLK_DDR23:
> +			*clk_mux_ddr23 = hw;
> +			break;
> +		case MUX_CLK_RP_CPU_NORMAL:
> +			*clk_mux_rp_cpu_normal = hw;
> +			break;
> +		case MUX_CLK_AXI_DDR:
> +			*clk_mux_axi_ddr = hw;
> +			break;
> +		}
> +
> +		/*
> +		 * FIXME: Theoretically, we should set parent for the
> +		 * mux, but seems hardware has done this for us with
> +		 * default value, so we don't set parent again here.
> +		 */
> +
> +		if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) {
> +			mux->clk_nb.notifier_call = sg2042_mux_notifier_cb;
> +			ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb);
> +			if (ret) {
> +				pr_err("failed to register clock notifier for %s\n",
> +				       mux->hw.init->name);
> +				break;
> +			}
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int sg2042_init_clkdata(struct platform_device *pdev,
> +			       int num_clks,
> +			       struct sg2042_clk_data **pp_clk_data)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;

No need to initialize this. You're setting it on the line just below. There are
many other instances of this throughout the file.

> + +	clk_data = devm_kzalloc(&pdev->dev,
> +				struct_size(clk_data, onecell_data.hws, num_clks),
> +				GFP_KERNEL);
> +	if (!clk_data)
> +		return -ENOMEM;
> +
> +	clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
> +	if (WARN_ON(IS_ERR(clk_data->iobase)))
> +		return PTR_ERR(clk_data->iobase);
> +
> +	clk_data->onecell_data.num = num_clks;
> +
> +	*pp_clk_data = clk_data;
> +
> +	return 0;
> +}
> +
> +static int sg2042_clkgen_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_div_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_1) +
> +		   ARRAY_SIZE(sg2042_gate_clks_level_2) +
> +		   ARRAY_SIZE(sg2042_mux_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 gates */
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data,
> +					   sg2042_gate_clks_level_1,
> +					   ARRAY_SIZE(sg2042_gate_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level-1 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1,
> +				       ARRAY_SIZE(sg2042_div_clks_level_1));
> +	if (ret)
> +		goto error_out;
> +
> +	/* mux */
> +	ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks,
> +				       ARRAY_SIZE(sg2042_mux_clks));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 div */
> +	ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2,
> +				       ARRAY_SIZE(sg2042_div_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	/* level 2 gate */
> +	ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2,
> +					ARRAY_SIZE(sg2042_gate_clks_level_2));
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_rpgate_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_gate_rp);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data, sg2042_gate_rp,
> +					   num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static int sg2042_pll_probe(struct platform_device *pdev)
> +{
> +	struct sg2042_clk_data *clk_data = NULL;
> +	int ret = 0;
> +	int num_clks = 0;
> +
> +	num_clks = ARRAY_SIZE(sg2042_pll_clks);
> +
> +	ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
> +	if (ret)
> +		goto error_out;
> +
> +	ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
> +				       num_clks);
> +	if (ret)
> +		goto error_out;
> +
> +	return devm_of_clk_add_hw_provider(&pdev->dev,
> +					   of_clk_hw_onecell_get,
> +					   &clk_data->onecell_data);
> +
> +error_out:
> +	pr_err("%s failed error number %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static const struct of_device_id sg2042_clkgen_match[] = {
> +	{ .compatible = "sophgo,sg2042-clkgen" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_clkgen_driver = {
> +	.probe = sg2042_clkgen_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-clkgen",
> +		.of_match_table = sg2042_clkgen_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_clkgen_driver);
> +
> +static const struct of_device_id sg2042_rpgate_match[] = {
> +	{ .compatible = "sophgo,sg2042-rpgate" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_rpgate_driver = {
> +	.probe = sg2042_rpgate_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-rpgate",
> +		.of_match_table = sg2042_rpgate_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_rpgate_driver);
> +
> +static const struct of_device_id sg2042_pll_match[] = {
> +	{ .compatible = "sophgo,sg2042-pll" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver sg2042_pll_driver = {
> +	.probe = sg2042_pll_probe,
> +	.driver = {
> +		.name = "clk-sophgo-sg2042-pll",
> +		.of_match_table = sg2042_pll_match,
> +		.suppress_bind_attrs = true,
> +	},
> +};
> +builtin_platform_driver(sg2042_pll_driver);

You have 3 different drivers in one file. Could this not be split into 3
different modules? This file is almost 2k lines long already.

Also do they all need to be built in? Eg. could some of them not be loaded as a
module later in the boot process or are they all critical to get to loading the
initramfs?

/Emil

> --
> 2.25.1
>
>
> _______________________________________________
> linux-riscv mailing list
> linux-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-riscv

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2024-04-30  7:49 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-26  3:32 [PATCH v15 0/5] riscv: sophgo: add clock support for sg2042 Chen Wang
2024-04-26  3:32 ` Chen Wang
2024-04-26  3:33 ` [PATCH v15 1/5] dt-bindings: clock: sophgo: add pll clocks for SG2042 Chen Wang
2024-04-26  3:33   ` Chen Wang
2024-04-26  3:34 ` [PATCH v15 2/5] dt-bindings: clock: sophgo: add RP gate " Chen Wang
2024-04-26  3:34   ` Chen Wang
2024-04-26  3:35 ` [PATCH v15 3/5] dt-bindings: clock: sophgo: add clkgen " Chen Wang
2024-04-26  3:35   ` Chen Wang
2024-04-26  3:35 ` [PATCH v15 4/5] clk: sophgo: Add SG2042 clock driver Chen Wang
2024-04-26  3:35   ` Chen Wang
2024-04-30  0:29   ` Chen Wang
2024-04-30  0:29     ` Chen Wang
2024-04-30  7:48   ` Emil Renner Berthing
2024-04-30  7:48     ` Emil Renner Berthing
2024-04-26  3:35 ` [PATCH v15 5/5] riscv: dts: add clock generator for Sophgo SG2042 SoC Chen Wang
2024-04-26  3:35   ` Chen Wang
2024-04-26  3:40 ` [PATCH v15 0/5] riscv: sophgo: add clock support for sg2042 Chen Wang
2024-04-26  3:40   ` Chen Wang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.