All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC RFT v2 0/3] clk: meson: Refactor PLL pre-divider as a divider clock
@ 2026-10-29  9:30 ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

This series refactors the Meson PLL framework to remove the dedicated
PLL pre-divider (N) parameter from the PLL implementation and model it
as a separate divider clock.

Currently, the Meson PLL framework models the PLL pre-divider using a
dedicated n field in struct meson_clk_pll_data. This makes the
pre-divider part of the PLL-specific implementation, although the
Common Clock Framework already provides a generic divider clock.

The pre-divider is not intended to be dynamically adjusted. Each PLL
has a fixed N value as part of its configuration. Most PLLs use N = 1,
while a few special cases use N = 3 to satisfy their PLL input frequency
constraints. Modeling the pre-divider as a separate divider clock
removes PLL-specific handling and simplifies the PLL framework.

The series updates the Amlogic SoC clock drivers to use standalone
pre-divider clocks, with the PLL DCO taking the pre-divider clock as
its parent instead of the input clock directly. It also adds the
required clock IDs.

Some Amlogic PLL pre-dividers read as zero after reset, which
does not produce a usable clock. The existing divider implementation
reports the parent rate for a zero divisor, potentially hiding this
invalid state. In addition, when the requested pre-divider rate equals
the parent rate, the divider must be configured with a divisor of 1.
The zero value read from the register after reset must not prevent the
divider from being configured correctly.

To address these issues, the series introduces
CLK_DIVIDER_INVALID_ZERO, allowing dividers with an invalid zero
value to report a rate of zero instead of incorrectly reporting the
parent rate.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
Changes in v2:
- Add CLK_DIVIDER_INVALID_ZERO.
- Add all SoCs prediv clock index.
- Support table for pll m.
- Fix the `determine` logic: when `best_parent_rate` equals 0,
  recalculate a new `parent_rate`.
- Add all SoCs pll prediv clocks.
- Link to v1: https://lore.kernel.org/r/20260923-meson_refactor_n-v1-0-3a8ce27121a2@amlogic.com

---
Jian Hu (3):
      dt-bindings: clock: amlogic: Add PLL pre-divider clock IDs
      clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
      clk: meson: pll: Remove the dedicated n parameter

 drivers/clk/clk-divider.c                       |   3 +
 drivers/clk/meson/a1-pll.c                      |  69 ++++--
 drivers/clk/meson/axg.c                         | 230 ++++++++++++------
 drivers/clk/meson/c3-pll.c                      |  99 ++++++--
 drivers/clk/meson/clk-pll.c                     | 203 ++++++----------
 drivers/clk/meson/clk-pll.h                     |  17 +-
 drivers/clk/meson/clk-regmap.h                  |   2 +-
 drivers/clk/meson/g12a.c                        | 265 ++++++++++++++++-----
 drivers/clk/meson/gxbb.c                        | 304 ++++++++++++++++--------
 drivers/clk/meson/meson8-ddr.c                  |  39 ++-
 drivers/clk/meson/meson8b.c                     | 238 +++++++++++++------
 drivers/clk/meson/s4-pll.c                      | 128 +++++++---
 drivers/clk/meson/t7-pll.c                      | 186 +++++++++++----
 include/dt-bindings/clock/amlogic,a1-pll-clkc.h |   3 +
 include/dt-bindings/clock/amlogic,c3-pll-clkc.h |   3 +
 include/dt-bindings/clock/amlogic,s4-pll-clkc.h |   4 +
 include/dt-bindings/clock/amlogic,t7-pll-clkc.h |   6 +
 include/dt-bindings/clock/axg-clkc.h            |   5 +
 include/dt-bindings/clock/g12a-clkc.h           |   8 +
 include/dt-bindings/clock/gxbb-clkc.h           |   4 +
 include/dt-bindings/clock/meson8-ddr-clkc.h     |   1 +
 include/dt-bindings/clock/meson8b-clkc.h        |   4 +
 include/linux/clk-provider.h                    |   6 +
 23 files changed, 1271 insertions(+), 556 deletions(-)
---
base-commit: 43e1705ecab981c66baee89041e6f728c0436f19
change-id: 20260923-meson_refactor_n-e7f25904e536

Best regards,
-- 
Jian Hu <jian.hu@amlogic.com>


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

* [PATCH RFC RFT v2 0/3] clk: meson: Refactor PLL pre-divider as a divider clock
@ 2026-10-29  9:30 ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

This series refactors the Meson PLL framework to remove the dedicated
PLL pre-divider (N) parameter from the PLL implementation and model it
as a separate divider clock.

Currently, the Meson PLL framework models the PLL pre-divider using a
dedicated n field in struct meson_clk_pll_data. This makes the
pre-divider part of the PLL-specific implementation, although the
Common Clock Framework already provides a generic divider clock.

The pre-divider is not intended to be dynamically adjusted. Each PLL
has a fixed N value as part of its configuration. Most PLLs use N = 1,
while a few special cases use N = 3 to satisfy their PLL input frequency
constraints. Modeling the pre-divider as a separate divider clock
removes PLL-specific handling and simplifies the PLL framework.

The series updates the Amlogic SoC clock drivers to use standalone
pre-divider clocks, with the PLL DCO taking the pre-divider clock as
its parent instead of the input clock directly. It also adds the
required clock IDs.

Some Amlogic PLL pre-dividers read as zero after reset, which
does not produce a usable clock. The existing divider implementation
reports the parent rate for a zero divisor, potentially hiding this
invalid state. In addition, when the requested pre-divider rate equals
the parent rate, the divider must be configured with a divisor of 1.
The zero value read from the register after reset must not prevent the
divider from being configured correctly.

To address these issues, the series introduces
CLK_DIVIDER_INVALID_ZERO, allowing dividers with an invalid zero
value to report a rate of zero instead of incorrectly reporting the
parent rate.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
Changes in v2:
- Add CLK_DIVIDER_INVALID_ZERO.
- Add all SoCs prediv clock index.
- Support table for pll m.
- Fix the `determine` logic: when `best_parent_rate` equals 0,
  recalculate a new `parent_rate`.
- Add all SoCs pll prediv clocks.
- Link to v1: https://lore.kernel.org/r/20260923-meson_refactor_n-v1-0-3a8ce27121a2@amlogic.com

---
Jian Hu (3):
      dt-bindings: clock: amlogic: Add PLL pre-divider clock IDs
      clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
      clk: meson: pll: Remove the dedicated n parameter

 drivers/clk/clk-divider.c                       |   3 +
 drivers/clk/meson/a1-pll.c                      |  69 ++++--
 drivers/clk/meson/axg.c                         | 230 ++++++++++++------
 drivers/clk/meson/c3-pll.c                      |  99 ++++++--
 drivers/clk/meson/clk-pll.c                     | 203 ++++++----------
 drivers/clk/meson/clk-pll.h                     |  17 +-
 drivers/clk/meson/clk-regmap.h                  |   2 +-
 drivers/clk/meson/g12a.c                        | 265 ++++++++++++++++-----
 drivers/clk/meson/gxbb.c                        | 304 ++++++++++++++++--------
 drivers/clk/meson/meson8-ddr.c                  |  39 ++-
 drivers/clk/meson/meson8b.c                     | 238 +++++++++++++------
 drivers/clk/meson/s4-pll.c                      | 128 +++++++---
 drivers/clk/meson/t7-pll.c                      | 186 +++++++++++----
 include/dt-bindings/clock/amlogic,a1-pll-clkc.h |   3 +
 include/dt-bindings/clock/amlogic,c3-pll-clkc.h |   3 +
 include/dt-bindings/clock/amlogic,s4-pll-clkc.h |   4 +
 include/dt-bindings/clock/amlogic,t7-pll-clkc.h |   6 +
 include/dt-bindings/clock/axg-clkc.h            |   5 +
 include/dt-bindings/clock/g12a-clkc.h           |   8 +
 include/dt-bindings/clock/gxbb-clkc.h           |   4 +
 include/dt-bindings/clock/meson8-ddr-clkc.h     |   1 +
 include/dt-bindings/clock/meson8b-clkc.h        |   4 +
 include/linux/clk-provider.h                    |   6 +
 23 files changed, 1271 insertions(+), 556 deletions(-)
---
base-commit: 43e1705ecab981c66baee89041e6f728c0436f19
change-id: 20260923-meson_refactor_n-e7f25904e536

Best regards,
-- 
Jian Hu <jian.hu@amlogic.com>



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

* [PATCH RFC RFT v2 0/3] clk: meson: Refactor PLL pre-divider as a divider clock
@ 2026-10-29  9:30 ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

This series refactors the Meson PLL framework to remove the dedicated
PLL pre-divider (N) parameter from the PLL implementation and model it
as a separate divider clock.

Currently, the Meson PLL framework models the PLL pre-divider using a
dedicated n field in struct meson_clk_pll_data. This makes the
pre-divider part of the PLL-specific implementation, although the
Common Clock Framework already provides a generic divider clock.

The pre-divider is not intended to be dynamically adjusted. Each PLL
has a fixed N value as part of its configuration. Most PLLs use N = 1,
while a few special cases use N = 3 to satisfy their PLL input frequency
constraints. Modeling the pre-divider as a separate divider clock
removes PLL-specific handling and simplifies the PLL framework.

The series updates the Amlogic SoC clock drivers to use standalone
pre-divider clocks, with the PLL DCO taking the pre-divider clock as
its parent instead of the input clock directly. It also adds the
required clock IDs.

Some Amlogic PLL pre-dividers read as zero after reset, which
does not produce a usable clock. The existing divider implementation
reports the parent rate for a zero divisor, potentially hiding this
invalid state. In addition, when the requested pre-divider rate equals
the parent rate, the divider must be configured with a divisor of 1.
The zero value read from the register after reset must not prevent the
divider from being configured correctly.

To address these issues, the series introduces
CLK_DIVIDER_INVALID_ZERO, allowing dividers with an invalid zero
value to report a rate of zero instead of incorrectly reporting the
parent rate.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
Changes in v2:
- Add CLK_DIVIDER_INVALID_ZERO.
- Add all SoCs prediv clock index.
- Support table for pll m.
- Fix the `determine` logic: when `best_parent_rate` equals 0,
  recalculate a new `parent_rate`.
- Add all SoCs pll prediv clocks.
- Link to v1: https://lore.kernel.org/r/20260923-meson_refactor_n-v1-0-3a8ce27121a2@amlogic.com

---
Jian Hu (3):
      dt-bindings: clock: amlogic: Add PLL pre-divider clock IDs
      clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
      clk: meson: pll: Remove the dedicated n parameter

 drivers/clk/clk-divider.c                       |   3 +
 drivers/clk/meson/a1-pll.c                      |  69 ++++--
 drivers/clk/meson/axg.c                         | 230 ++++++++++++------
 drivers/clk/meson/c3-pll.c                      |  99 ++++++--
 drivers/clk/meson/clk-pll.c                     | 203 ++++++----------
 drivers/clk/meson/clk-pll.h                     |  17 +-
 drivers/clk/meson/clk-regmap.h                  |   2 +-
 drivers/clk/meson/g12a.c                        | 265 ++++++++++++++++-----
 drivers/clk/meson/gxbb.c                        | 304 ++++++++++++++++--------
 drivers/clk/meson/meson8-ddr.c                  |  39 ++-
 drivers/clk/meson/meson8b.c                     | 238 +++++++++++++------
 drivers/clk/meson/s4-pll.c                      | 128 +++++++---
 drivers/clk/meson/t7-pll.c                      | 186 +++++++++++----
 include/dt-bindings/clock/amlogic,a1-pll-clkc.h |   3 +
 include/dt-bindings/clock/amlogic,c3-pll-clkc.h |   3 +
 include/dt-bindings/clock/amlogic,s4-pll-clkc.h |   4 +
 include/dt-bindings/clock/amlogic,t7-pll-clkc.h |   6 +
 include/dt-bindings/clock/axg-clkc.h            |   5 +
 include/dt-bindings/clock/g12a-clkc.h           |   8 +
 include/dt-bindings/clock/gxbb-clkc.h           |   4 +
 include/dt-bindings/clock/meson8-ddr-clkc.h     |   1 +
 include/dt-bindings/clock/meson8b-clkc.h        |   4 +
 include/linux/clk-provider.h                    |   6 +
 23 files changed, 1271 insertions(+), 556 deletions(-)
---
base-commit: 43e1705ecab981c66baee89041e6f728c0436f19
change-id: 20260923-meson_refactor_n-e7f25904e536

Best regards,
-- 
Jian Hu <jian.hu@amlogic.com>



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

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

* [PATCH RFC RFT v2 1/3] dt-bindings: clock: amlogic: Add PLL pre-divider clock IDs
  2026-10-29  9:30 ` Jian Hu via B4 Relay
  (?)
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  -1 siblings, 0 replies; 16+ messages in thread
From: Jian Hu @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

Add clock IDs for the PLL pre-divider clocks. These IDs are
required for the subsequent conversion of the PLL pre-divider
into a standalone divider clock following the PLL framework refactoring.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 include/dt-bindings/clock/amlogic,a1-pll-clkc.h | 3 +++
 include/dt-bindings/clock/amlogic,c3-pll-clkc.h | 3 +++
 include/dt-bindings/clock/amlogic,s4-pll-clkc.h | 4 ++++
 include/dt-bindings/clock/amlogic,t7-pll-clkc.h | 6 ++++++
 include/dt-bindings/clock/axg-clkc.h            | 5 +++++
 include/dt-bindings/clock/g12a-clkc.h           | 8 ++++++++
 include/dt-bindings/clock/gxbb-clkc.h           | 4 ++++
 include/dt-bindings/clock/meson8-ddr-clkc.h     | 1 +
 include/dt-bindings/clock/meson8b-clkc.h        | 4 ++++
 9 files changed, 38 insertions(+)

diff --git a/include/dt-bindings/clock/amlogic,a1-pll-clkc.h b/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
index 0dfc5e78a2d5..c09f215bf75b 100644
--- a/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
@@ -22,5 +22,8 @@
 #define CLKID_FCLK_DIV7		9
 #define CLKID_HIFI_PLL		10
 #define CLKID_SYS_PLL		11
+#define CLKID_FIXED_PREDIV	12
+#define CLKID_HIFI_PREDIV	13
+#define CLKID_SYS_PREDIV	14
 
 #endif /* __A1_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,c3-pll-clkc.h b/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
index fcdc558715e8..771be7d53bc0 100644
--- a/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
@@ -36,5 +36,8 @@
 #define CLKID_MCLK1_SEL_EN			26
 #define CLKID_MCLK1_DIV				27
 #define CLKID_MCLK1				28
+#define CLKID_GP0_PREDIV			29
+#define CLKID_HIFI_PREDIV			30
+#define CLKID_MCLK_PREDIV			31
 
 #endif  /* _DT_BINDINGS_CLOCK_AMLOGIC_C3_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,s4-pll-clkc.h b/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
index af9f110f8b62..e194185f5be8 100644
--- a/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
@@ -39,5 +39,9 @@
 #define CLKID_MPLL2			29
 #define CLKID_MPLL3_DIV			30
 #define CLKID_MPLL3			31
+#define CLKID_FIXED_PREDIV		32
+#define CLKID_GP0_PREDIV		33
+#define CLKID_HIFI_PREDIV		34
+#define CLKID_HDMI_PREDIV		35
 
 #endif /* _DT_BINDINGS_CLOCK_AMLOGIC_S4_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,t7-pll-clkc.h b/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
index e2481f2f1163..69c90bc0be0a 100644
--- a/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
@@ -9,20 +9,24 @@
 /* GP0 */
 #define CLKID_GP0_PLL_DCO	0
 #define CLKID_GP0_PLL		1
+#define CLKID_GP0_PREDIV	2
 
 /* GP1 */
 #define CLKID_GP1_PLL_DCO	0
 #define CLKID_GP1_PLL		1
+#define CLKID_GP1_PREDIV	2
 
 /* HIFI */
 #define CLKID_HIFI_PLL_DCO	0
 #define CLKID_HIFI_PLL		1
+#define CLKID_HIFI_PREDIV	2
 
 /* PCIE */
 #define CLKID_PCIE_PLL_DCO	0
 #define CLKID_PCIE_PLL_DCO_DIV2	1
 #define CLKID_PCIE_PLL_OD	2
 #define CLKID_PCIE_PLL		3
+#define CLKID_PCIE_PREDIV	4
 
 /* MPLL */
 #define CLKID_MPLL_PREDIV	0
@@ -39,6 +43,7 @@
 #define CLKID_HDMI_PLL_DCO	0
 #define CLKID_HDMI_PLL_OD	1
 #define CLKID_HDMI_PLL		2
+#define CLKID_HDMI_PREDIV	3
 
 /* MCLK */
 #define CLKID_MCLK_PLL_DCO	0
@@ -52,5 +57,6 @@
 #define CLKID_MCLK_1_DIV2	8
 #define CLKID_MCLK_1_PRE	9
 #define CLKID_MCLK_1		10
+#define CLKID_MCLK_PREDIV	11
 
 #endif /* __T7_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/axg-clkc.h b/include/dt-bindings/clock/axg-clkc.h
index 442162822b88..798b92919f60 100644
--- a/include/dt-bindings/clock/axg-clkc.h
+++ b/include/dt-bindings/clock/axg-clkc.h
@@ -144,5 +144,10 @@
 #define CLKID_VDIN_MEAS_SEL			134
 #define CLKID_VDIN_MEAS_DIV			135
 #define CLKID_VDIN_MEAS				136
+#define CLKID_FIXED_PREDIV			137
+#define CLKID_SYS_PREDIV			138
+#define CLKID_GP0_PREDIV			139
+#define CLKID_HIFI_PREDIV			140
+#define CLKID_PCIE_PREDIV			141
 
 #endif /* __AXG_CLKC_H */
diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h
index fd09819da2ec..d2aa3df14149 100644
--- a/include/dt-bindings/clock/g12a-clkc.h
+++ b/include/dt-bindings/clock/g12a-clkc.h
@@ -287,5 +287,13 @@
 #define CLKID_MIPI_ISP_GATE			276
 #define CLKID_MIPI_ISP_CSI_PHY0			277
 #define CLKID_MIPI_ISP_CSI_PHY1			278
+#define CLKID_FIXED_PREDIV			279
+#define CLKID_SYS_PREDIV			280
+#define CLKID_GP0_PREDIV			281
+#define CLKID_HIFI_PREDIV			282
+#define CLKID_HDMI_PREDIV			283
+#define CLKID_PCIE_PREDIV			284
+#define CLKID_SYS1_PREDIV			285
+#define CLKID_GP1_PREDIV			286
 
 #endif /* __G12A_CLKC_H */
diff --git a/include/dt-bindings/clock/gxbb-clkc.h b/include/dt-bindings/clock/gxbb-clkc.h
index c0ce5e9c4151..e2e1ec60285d 100644
--- a/include/dt-bindings/clock/gxbb-clkc.h
+++ b/include/dt-bindings/clock/gxbb-clkc.h
@@ -212,5 +212,9 @@
 #define CLKID_HDMI_DIV		204
 #define CLKID_HDMI		205
 #define CLKID_ACODEC		206
+#define CLKID_FIXED_PREDIV	207
+#define CLKID_HDMI_PREDIV	208
+#define CLKID_SYS_PREDIV	209
+#define CLKID_GP0_PREDIV	210
 
 #endif /* __GXBB_CLKC_H */
diff --git a/include/dt-bindings/clock/meson8-ddr-clkc.h b/include/dt-bindings/clock/meson8-ddr-clkc.h
index a8e0fa2987ab..ce97a15c28a9 100644
--- a/include/dt-bindings/clock/meson8-ddr-clkc.h
+++ b/include/dt-bindings/clock/meson8-ddr-clkc.h
@@ -2,3 +2,4 @@
 
 #define DDR_CLKID_DDR_PLL_DCO			0
 #define DDR_CLKID_DDR_PLL			1
+#define DDR_CLKID_DDR_PREDIV			2
diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h
index 385bf243c56c..c4fd127dca1e 100644
--- a/include/dt-bindings/clock/meson8b-clkc.h
+++ b/include/dt-bindings/clock/meson8b-clkc.h
@@ -221,5 +221,9 @@
 #define CLKID_VCLK2_EN		215
 #define CLKID_VID_PLL_LVDS_EN	216
 #define CLKID_HDMI_PLL_DCO_IN   217
+#define CLKID_PLL_FIXED_PREDIV	218
+#define CLKID_PLL_SYS_PREDIV	219
+#define CLKID_HDMI_PREDIV	220
+#define CLKID_GP_PREDIV		221
 
 #endif /* __MESON8B_CLKC_H */

-- 
2.47.1


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

* [PATCH RFC RFT v2 1/3] dt-bindings: clock: amlogic: Add PLL pre-divider clock IDs
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

From: Jian Hu <jian.hu@amlogic.com>

Add clock IDs for the PLL pre-divider clocks. These IDs are
required for the subsequent conversion of the PLL pre-divider
into a standalone divider clock following the PLL framework refactoring.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 include/dt-bindings/clock/amlogic,a1-pll-clkc.h | 3 +++
 include/dt-bindings/clock/amlogic,c3-pll-clkc.h | 3 +++
 include/dt-bindings/clock/amlogic,s4-pll-clkc.h | 4 ++++
 include/dt-bindings/clock/amlogic,t7-pll-clkc.h | 6 ++++++
 include/dt-bindings/clock/axg-clkc.h            | 5 +++++
 include/dt-bindings/clock/g12a-clkc.h           | 8 ++++++++
 include/dt-bindings/clock/gxbb-clkc.h           | 4 ++++
 include/dt-bindings/clock/meson8-ddr-clkc.h     | 1 +
 include/dt-bindings/clock/meson8b-clkc.h        | 4 ++++
 9 files changed, 38 insertions(+)

diff --git a/include/dt-bindings/clock/amlogic,a1-pll-clkc.h b/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
index 0dfc5e78a2d5..c09f215bf75b 100644
--- a/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
@@ -22,5 +22,8 @@
 #define CLKID_FCLK_DIV7		9
 #define CLKID_HIFI_PLL		10
 #define CLKID_SYS_PLL		11
+#define CLKID_FIXED_PREDIV	12
+#define CLKID_HIFI_PREDIV	13
+#define CLKID_SYS_PREDIV	14
 
 #endif /* __A1_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,c3-pll-clkc.h b/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
index fcdc558715e8..771be7d53bc0 100644
--- a/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
@@ -36,5 +36,8 @@
 #define CLKID_MCLK1_SEL_EN			26
 #define CLKID_MCLK1_DIV				27
 #define CLKID_MCLK1				28
+#define CLKID_GP0_PREDIV			29
+#define CLKID_HIFI_PREDIV			30
+#define CLKID_MCLK_PREDIV			31
 
 #endif  /* _DT_BINDINGS_CLOCK_AMLOGIC_C3_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,s4-pll-clkc.h b/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
index af9f110f8b62..e194185f5be8 100644
--- a/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
@@ -39,5 +39,9 @@
 #define CLKID_MPLL2			29
 #define CLKID_MPLL3_DIV			30
 #define CLKID_MPLL3			31
+#define CLKID_FIXED_PREDIV		32
+#define CLKID_GP0_PREDIV		33
+#define CLKID_HIFI_PREDIV		34
+#define CLKID_HDMI_PREDIV		35
 
 #endif /* _DT_BINDINGS_CLOCK_AMLOGIC_S4_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,t7-pll-clkc.h b/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
index e2481f2f1163..69c90bc0be0a 100644
--- a/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
@@ -9,20 +9,24 @@
 /* GP0 */
 #define CLKID_GP0_PLL_DCO	0
 #define CLKID_GP0_PLL		1
+#define CLKID_GP0_PREDIV	2
 
 /* GP1 */
 #define CLKID_GP1_PLL_DCO	0
 #define CLKID_GP1_PLL		1
+#define CLKID_GP1_PREDIV	2
 
 /* HIFI */
 #define CLKID_HIFI_PLL_DCO	0
 #define CLKID_HIFI_PLL		1
+#define CLKID_HIFI_PREDIV	2
 
 /* PCIE */
 #define CLKID_PCIE_PLL_DCO	0
 #define CLKID_PCIE_PLL_DCO_DIV2	1
 #define CLKID_PCIE_PLL_OD	2
 #define CLKID_PCIE_PLL		3
+#define CLKID_PCIE_PREDIV	4
 
 /* MPLL */
 #define CLKID_MPLL_PREDIV	0
@@ -39,6 +43,7 @@
 #define CLKID_HDMI_PLL_DCO	0
 #define CLKID_HDMI_PLL_OD	1
 #define CLKID_HDMI_PLL		2
+#define CLKID_HDMI_PREDIV	3
 
 /* MCLK */
 #define CLKID_MCLK_PLL_DCO	0
@@ -52,5 +57,6 @@
 #define CLKID_MCLK_1_DIV2	8
 #define CLKID_MCLK_1_PRE	9
 #define CLKID_MCLK_1		10
+#define CLKID_MCLK_PREDIV	11
 
 #endif /* __T7_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/axg-clkc.h b/include/dt-bindings/clock/axg-clkc.h
index 442162822b88..798b92919f60 100644
--- a/include/dt-bindings/clock/axg-clkc.h
+++ b/include/dt-bindings/clock/axg-clkc.h
@@ -144,5 +144,10 @@
 #define CLKID_VDIN_MEAS_SEL			134
 #define CLKID_VDIN_MEAS_DIV			135
 #define CLKID_VDIN_MEAS				136
+#define CLKID_FIXED_PREDIV			137
+#define CLKID_SYS_PREDIV			138
+#define CLKID_GP0_PREDIV			139
+#define CLKID_HIFI_PREDIV			140
+#define CLKID_PCIE_PREDIV			141
 
 #endif /* __AXG_CLKC_H */
diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h
index fd09819da2ec..d2aa3df14149 100644
--- a/include/dt-bindings/clock/g12a-clkc.h
+++ b/include/dt-bindings/clock/g12a-clkc.h
@@ -287,5 +287,13 @@
 #define CLKID_MIPI_ISP_GATE			276
 #define CLKID_MIPI_ISP_CSI_PHY0			277
 #define CLKID_MIPI_ISP_CSI_PHY1			278
+#define CLKID_FIXED_PREDIV			279
+#define CLKID_SYS_PREDIV			280
+#define CLKID_GP0_PREDIV			281
+#define CLKID_HIFI_PREDIV			282
+#define CLKID_HDMI_PREDIV			283
+#define CLKID_PCIE_PREDIV			284
+#define CLKID_SYS1_PREDIV			285
+#define CLKID_GP1_PREDIV			286
 
 #endif /* __G12A_CLKC_H */
diff --git a/include/dt-bindings/clock/gxbb-clkc.h b/include/dt-bindings/clock/gxbb-clkc.h
index c0ce5e9c4151..e2e1ec60285d 100644
--- a/include/dt-bindings/clock/gxbb-clkc.h
+++ b/include/dt-bindings/clock/gxbb-clkc.h
@@ -212,5 +212,9 @@
 #define CLKID_HDMI_DIV		204
 #define CLKID_HDMI		205
 #define CLKID_ACODEC		206
+#define CLKID_FIXED_PREDIV	207
+#define CLKID_HDMI_PREDIV	208
+#define CLKID_SYS_PREDIV	209
+#define CLKID_GP0_PREDIV	210
 
 #endif /* __GXBB_CLKC_H */
diff --git a/include/dt-bindings/clock/meson8-ddr-clkc.h b/include/dt-bindings/clock/meson8-ddr-clkc.h
index a8e0fa2987ab..ce97a15c28a9 100644
--- a/include/dt-bindings/clock/meson8-ddr-clkc.h
+++ b/include/dt-bindings/clock/meson8-ddr-clkc.h
@@ -2,3 +2,4 @@
 
 #define DDR_CLKID_DDR_PLL_DCO			0
 #define DDR_CLKID_DDR_PLL			1
+#define DDR_CLKID_DDR_PREDIV			2
diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h
index 385bf243c56c..c4fd127dca1e 100644
--- a/include/dt-bindings/clock/meson8b-clkc.h
+++ b/include/dt-bindings/clock/meson8b-clkc.h
@@ -221,5 +221,9 @@
 #define CLKID_VCLK2_EN		215
 #define CLKID_VID_PLL_LVDS_EN	216
 #define CLKID_HDMI_PLL_DCO_IN   217
+#define CLKID_PLL_FIXED_PREDIV	218
+#define CLKID_PLL_SYS_PREDIV	219
+#define CLKID_HDMI_PREDIV	220
+#define CLKID_GP_PREDIV		221
 
 #endif /* __MESON8B_CLKC_H */

-- 
2.47.1



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

* [PATCH RFC RFT v2 1/3] dt-bindings: clock: amlogic: Add PLL pre-divider clock IDs
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

From: Jian Hu <jian.hu@amlogic.com>

Add clock IDs for the PLL pre-divider clocks. These IDs are
required for the subsequent conversion of the PLL pre-divider
into a standalone divider clock following the PLL framework refactoring.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 include/dt-bindings/clock/amlogic,a1-pll-clkc.h | 3 +++
 include/dt-bindings/clock/amlogic,c3-pll-clkc.h | 3 +++
 include/dt-bindings/clock/amlogic,s4-pll-clkc.h | 4 ++++
 include/dt-bindings/clock/amlogic,t7-pll-clkc.h | 6 ++++++
 include/dt-bindings/clock/axg-clkc.h            | 5 +++++
 include/dt-bindings/clock/g12a-clkc.h           | 8 ++++++++
 include/dt-bindings/clock/gxbb-clkc.h           | 4 ++++
 include/dt-bindings/clock/meson8-ddr-clkc.h     | 1 +
 include/dt-bindings/clock/meson8b-clkc.h        | 4 ++++
 9 files changed, 38 insertions(+)

diff --git a/include/dt-bindings/clock/amlogic,a1-pll-clkc.h b/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
index 0dfc5e78a2d5..c09f215bf75b 100644
--- a/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,a1-pll-clkc.h
@@ -22,5 +22,8 @@
 #define CLKID_FCLK_DIV7		9
 #define CLKID_HIFI_PLL		10
 #define CLKID_SYS_PLL		11
+#define CLKID_FIXED_PREDIV	12
+#define CLKID_HIFI_PREDIV	13
+#define CLKID_SYS_PREDIV	14
 
 #endif /* __A1_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,c3-pll-clkc.h b/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
index fcdc558715e8..771be7d53bc0 100644
--- a/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,c3-pll-clkc.h
@@ -36,5 +36,8 @@
 #define CLKID_MCLK1_SEL_EN			26
 #define CLKID_MCLK1_DIV				27
 #define CLKID_MCLK1				28
+#define CLKID_GP0_PREDIV			29
+#define CLKID_HIFI_PREDIV			30
+#define CLKID_MCLK_PREDIV			31
 
 #endif  /* _DT_BINDINGS_CLOCK_AMLOGIC_C3_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,s4-pll-clkc.h b/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
index af9f110f8b62..e194185f5be8 100644
--- a/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,s4-pll-clkc.h
@@ -39,5 +39,9 @@
 #define CLKID_MPLL2			29
 #define CLKID_MPLL3_DIV			30
 #define CLKID_MPLL3			31
+#define CLKID_FIXED_PREDIV		32
+#define CLKID_GP0_PREDIV		33
+#define CLKID_HIFI_PREDIV		34
+#define CLKID_HDMI_PREDIV		35
 
 #endif /* _DT_BINDINGS_CLOCK_AMLOGIC_S4_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/amlogic,t7-pll-clkc.h b/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
index e2481f2f1163..69c90bc0be0a 100644
--- a/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
+++ b/include/dt-bindings/clock/amlogic,t7-pll-clkc.h
@@ -9,20 +9,24 @@
 /* GP0 */
 #define CLKID_GP0_PLL_DCO	0
 #define CLKID_GP0_PLL		1
+#define CLKID_GP0_PREDIV	2
 
 /* GP1 */
 #define CLKID_GP1_PLL_DCO	0
 #define CLKID_GP1_PLL		1
+#define CLKID_GP1_PREDIV	2
 
 /* HIFI */
 #define CLKID_HIFI_PLL_DCO	0
 #define CLKID_HIFI_PLL		1
+#define CLKID_HIFI_PREDIV	2
 
 /* PCIE */
 #define CLKID_PCIE_PLL_DCO	0
 #define CLKID_PCIE_PLL_DCO_DIV2	1
 #define CLKID_PCIE_PLL_OD	2
 #define CLKID_PCIE_PLL		3
+#define CLKID_PCIE_PREDIV	4
 
 /* MPLL */
 #define CLKID_MPLL_PREDIV	0
@@ -39,6 +43,7 @@
 #define CLKID_HDMI_PLL_DCO	0
 #define CLKID_HDMI_PLL_OD	1
 #define CLKID_HDMI_PLL		2
+#define CLKID_HDMI_PREDIV	3
 
 /* MCLK */
 #define CLKID_MCLK_PLL_DCO	0
@@ -52,5 +57,6 @@
 #define CLKID_MCLK_1_DIV2	8
 #define CLKID_MCLK_1_PRE	9
 #define CLKID_MCLK_1		10
+#define CLKID_MCLK_PREDIV	11
 
 #endif /* __T7_PLL_CLKC_H */
diff --git a/include/dt-bindings/clock/axg-clkc.h b/include/dt-bindings/clock/axg-clkc.h
index 442162822b88..798b92919f60 100644
--- a/include/dt-bindings/clock/axg-clkc.h
+++ b/include/dt-bindings/clock/axg-clkc.h
@@ -144,5 +144,10 @@
 #define CLKID_VDIN_MEAS_SEL			134
 #define CLKID_VDIN_MEAS_DIV			135
 #define CLKID_VDIN_MEAS				136
+#define CLKID_FIXED_PREDIV			137
+#define CLKID_SYS_PREDIV			138
+#define CLKID_GP0_PREDIV			139
+#define CLKID_HIFI_PREDIV			140
+#define CLKID_PCIE_PREDIV			141
 
 #endif /* __AXG_CLKC_H */
diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h
index fd09819da2ec..d2aa3df14149 100644
--- a/include/dt-bindings/clock/g12a-clkc.h
+++ b/include/dt-bindings/clock/g12a-clkc.h
@@ -287,5 +287,13 @@
 #define CLKID_MIPI_ISP_GATE			276
 #define CLKID_MIPI_ISP_CSI_PHY0			277
 #define CLKID_MIPI_ISP_CSI_PHY1			278
+#define CLKID_FIXED_PREDIV			279
+#define CLKID_SYS_PREDIV			280
+#define CLKID_GP0_PREDIV			281
+#define CLKID_HIFI_PREDIV			282
+#define CLKID_HDMI_PREDIV			283
+#define CLKID_PCIE_PREDIV			284
+#define CLKID_SYS1_PREDIV			285
+#define CLKID_GP1_PREDIV			286
 
 #endif /* __G12A_CLKC_H */
diff --git a/include/dt-bindings/clock/gxbb-clkc.h b/include/dt-bindings/clock/gxbb-clkc.h
index c0ce5e9c4151..e2e1ec60285d 100644
--- a/include/dt-bindings/clock/gxbb-clkc.h
+++ b/include/dt-bindings/clock/gxbb-clkc.h
@@ -212,5 +212,9 @@
 #define CLKID_HDMI_DIV		204
 #define CLKID_HDMI		205
 #define CLKID_ACODEC		206
+#define CLKID_FIXED_PREDIV	207
+#define CLKID_HDMI_PREDIV	208
+#define CLKID_SYS_PREDIV	209
+#define CLKID_GP0_PREDIV	210
 
 #endif /* __GXBB_CLKC_H */
diff --git a/include/dt-bindings/clock/meson8-ddr-clkc.h b/include/dt-bindings/clock/meson8-ddr-clkc.h
index a8e0fa2987ab..ce97a15c28a9 100644
--- a/include/dt-bindings/clock/meson8-ddr-clkc.h
+++ b/include/dt-bindings/clock/meson8-ddr-clkc.h
@@ -2,3 +2,4 @@
 
 #define DDR_CLKID_DDR_PLL_DCO			0
 #define DDR_CLKID_DDR_PLL			1
+#define DDR_CLKID_DDR_PREDIV			2
diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h
index 385bf243c56c..c4fd127dca1e 100644
--- a/include/dt-bindings/clock/meson8b-clkc.h
+++ b/include/dt-bindings/clock/meson8b-clkc.h
@@ -221,5 +221,9 @@
 #define CLKID_VCLK2_EN		215
 #define CLKID_VID_PLL_LVDS_EN	216
 #define CLKID_HDMI_PLL_DCO_IN   217
+#define CLKID_PLL_FIXED_PREDIV	218
+#define CLKID_PLL_SYS_PREDIV	219
+#define CLKID_HDMI_PREDIV	220
+#define CLKID_GP_PREDIV		221
 
 #endif /* __MESON8B_CLKC_H */

-- 
2.47.1



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

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

* [PATCH RFC RFT v2 2/3] clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
  2026-10-29  9:30 ` Jian Hu via B4 Relay
  (?)
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  -1 siblings, 0 replies; 16+ messages in thread
From: Jian Hu @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

When a divider register reads zero, divider_recalc_rate() currently
emits a warning (unless CLK_DIVIDER_ALLOW_ZERO is set) and returns
the parent rate.

This behaviour assumes a zero divisor acts as a pass-through, which
does not apply to all hardware. Some dividers, for example PLL
pre-dividers on Amlogic SoCs, come out of reset with a zero value
that does not produce a usable clock. Returning the parent rate
hides this fault behind an incorrect, plausible value.

CLK_DIVIDER_ALLOW_ZERO only suppresses the warning while still
reporting the parent rate, so it cannot represent this hardware
behaviour.

Add CLK_DIVIDER_INVALID_ZERO for dividers where a zero divisor value
is invalid. When this flag is set, recalc_rate() returns 0, making
the faulty state visible to the clock framework rather than hiding it
as a pass-through.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 drivers/clk/clk-divider.c    | 3 +++
 include/linux/clk-provider.h | 6 ++++++
 2 files changed, 9 insertions(+)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index f68216c65d3d..6c1523d22477 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -143,6 +143,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 
 	div = _get_div(table, val, flags, width);
 	if (!div) {
+		if (flags & CLK_DIVIDER_INVALID_ZERO)
+			return 0;
+
 		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
 			clk_hw_get_name(hw));
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index dc8e161f9c2c..fe2dbbba49e3 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -744,6 +744,11 @@ struct clk_div_table {
  *	big endian.
  * CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
  *	Formula is 2 * (value read from hardware + 1).
+ * CLK_DIVIDER_INVALID_ZERO - a zero value read from the register is not a
+ *	divide by one / bypass but an invalid setting which does not produce
+ *	a usable clock.  recalc_rate() returns 0 so that the broken state is
+ *	reported to the framework instead of the parent rate, without
+ *	triggering the CLK_DIVIDER_ALLOW_ZERO warning.
  */
 struct clk_divider {
 	struct clk_hw	hw;
@@ -767,6 +772,7 @@ struct clk_divider {
 #define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
 #define CLK_DIVIDER_BIG_ENDIAN		BIT(7)
 #define CLK_DIVIDER_EVEN_INTEGERS	BIT(8)
+#define CLK_DIVIDER_INVALID_ZERO	BIT(9)
 
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ro_ops;

-- 
2.47.1


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

* [PATCH RFC RFT v2 2/3] clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

From: Jian Hu <jian.hu@amlogic.com>

When a divider register reads zero, divider_recalc_rate() currently
emits a warning (unless CLK_DIVIDER_ALLOW_ZERO is set) and returns
the parent rate.

This behaviour assumes a zero divisor acts as a pass-through, which
does not apply to all hardware. Some dividers, for example PLL
pre-dividers on Amlogic SoCs, come out of reset with a zero value
that does not produce a usable clock. Returning the parent rate
hides this fault behind an incorrect, plausible value.

CLK_DIVIDER_ALLOW_ZERO only suppresses the warning while still
reporting the parent rate, so it cannot represent this hardware
behaviour.

Add CLK_DIVIDER_INVALID_ZERO for dividers where a zero divisor value
is invalid. When this flag is set, recalc_rate() returns 0, making
the faulty state visible to the clock framework rather than hiding it
as a pass-through.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 drivers/clk/clk-divider.c    | 3 +++
 include/linux/clk-provider.h | 6 ++++++
 2 files changed, 9 insertions(+)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index f68216c65d3d..6c1523d22477 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -143,6 +143,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 
 	div = _get_div(table, val, flags, width);
 	if (!div) {
+		if (flags & CLK_DIVIDER_INVALID_ZERO)
+			return 0;
+
 		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
 			clk_hw_get_name(hw));
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index dc8e161f9c2c..fe2dbbba49e3 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -744,6 +744,11 @@ struct clk_div_table {
  *	big endian.
  * CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
  *	Formula is 2 * (value read from hardware + 1).
+ * CLK_DIVIDER_INVALID_ZERO - a zero value read from the register is not a
+ *	divide by one / bypass but an invalid setting which does not produce
+ *	a usable clock.  recalc_rate() returns 0 so that the broken state is
+ *	reported to the framework instead of the parent rate, without
+ *	triggering the CLK_DIVIDER_ALLOW_ZERO warning.
  */
 struct clk_divider {
 	struct clk_hw	hw;
@@ -767,6 +772,7 @@ struct clk_divider {
 #define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
 #define CLK_DIVIDER_BIG_ENDIAN		BIT(7)
 #define CLK_DIVIDER_EVEN_INTEGERS	BIT(8)
+#define CLK_DIVIDER_INVALID_ZERO	BIT(9)
 
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ro_ops;

-- 
2.47.1



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

* [PATCH RFC RFT v2 2/3] clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

From: Jian Hu <jian.hu@amlogic.com>

When a divider register reads zero, divider_recalc_rate() currently
emits a warning (unless CLK_DIVIDER_ALLOW_ZERO is set) and returns
the parent rate.

This behaviour assumes a zero divisor acts as a pass-through, which
does not apply to all hardware. Some dividers, for example PLL
pre-dividers on Amlogic SoCs, come out of reset with a zero value
that does not produce a usable clock. Returning the parent rate
hides this fault behind an incorrect, plausible value.

CLK_DIVIDER_ALLOW_ZERO only suppresses the warning while still
reporting the parent rate, so it cannot represent this hardware
behaviour.

Add CLK_DIVIDER_INVALID_ZERO for dividers where a zero divisor value
is invalid. When this flag is set, recalc_rate() returns 0, making
the faulty state visible to the clock framework rather than hiding it
as a pass-through.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 drivers/clk/clk-divider.c    | 3 +++
 include/linux/clk-provider.h | 6 ++++++
 2 files changed, 9 insertions(+)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index f68216c65d3d..6c1523d22477 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -143,6 +143,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 
 	div = _get_div(table, val, flags, width);
 	if (!div) {
+		if (flags & CLK_DIVIDER_INVALID_ZERO)
+			return 0;
+
 		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
 			clk_hw_get_name(hw));
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index dc8e161f9c2c..fe2dbbba49e3 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -744,6 +744,11 @@ struct clk_div_table {
  *	big endian.
  * CLK_DIVIDER_EVEN_INTEGERS - clock divisor is 2, 4, 6, 8, 10, etc.
  *	Formula is 2 * (value read from hardware + 1).
+ * CLK_DIVIDER_INVALID_ZERO - a zero value read from the register is not a
+ *	divide by one / bypass but an invalid setting which does not produce
+ *	a usable clock.  recalc_rate() returns 0 so that the broken state is
+ *	reported to the framework instead of the parent rate, without
+ *	triggering the CLK_DIVIDER_ALLOW_ZERO warning.
  */
 struct clk_divider {
 	struct clk_hw	hw;
@@ -767,6 +772,7 @@ struct clk_divider {
 #define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
 #define CLK_DIVIDER_BIG_ENDIAN		BIT(7)
 #define CLK_DIVIDER_EVEN_INTEGERS	BIT(8)
+#define CLK_DIVIDER_INVALID_ZERO	BIT(9)
 
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ro_ops;

-- 
2.47.1



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

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

* [PATCH RFC RFT v2 3/3] clk: meson: pll: Remove the dedicated n parameter
  2026-10-29  9:30 ` Jian Hu via B4 Relay
  (?)
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  -1 siblings, 0 replies; 16+ messages in thread
From: Jian Hu @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

The Meson PLL framework models the PLL pre-divider (N) using a dedicated
field in struct meson_clk_pll_data. Since the common clock framework
already provides a generic divider implementation, there is no need to
keep PLL-specific support for it.

The pre-divider is not intended to be dynamically adjusted. According to
the hardware design, each PLL has a single fixed N value, defined together
with the rest of the PLL. N = 1 for most PLLs, and N = 3 for a few special
cases (the axg PCIe PLL, gxbb Fixed PLL and the meson8b GP0 PLL). The PLL
input frequency constraints are respected by this fixed value.

Remove the dedicated n parameter from the framework and simplify the PLL
rate calculation accordingly. The pre-divider will be represented as a
separate divider clock by platform drivers where needed.

Replace the dedicated PLL pre-divider with a standalone divider clock
for all amlogic SoCs. The PLL DCO now takes the pre-divider clock
as its parent instead of the input clock directly.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 drivers/clk/meson/a1-pll.c     |  69 ++++++++--
 drivers/clk/meson/axg.c        | 230 +++++++++++++++++++++----------
 drivers/clk/meson/c3-pll.c     |  99 +++++++++++---
 drivers/clk/meson/clk-pll.c    | 203 +++++++++++----------------
 drivers/clk/meson/clk-pll.h    |  17 +--
 drivers/clk/meson/clk-regmap.h |   2 +-
 drivers/clk/meson/g12a.c       | 265 ++++++++++++++++++++++++++---------
 drivers/clk/meson/gxbb.c       | 304 +++++++++++++++++++++++++++--------------
 drivers/clk/meson/meson8-ddr.c |  39 +++++-
 drivers/clk/meson/meson8b.c    | 238 +++++++++++++++++++++-----------
 drivers/clk/meson/s4-pll.c     | 128 +++++++++++++----
 drivers/clk/meson/t7-pll.c     | 186 +++++++++++++++++++------
 12 files changed, 1224 insertions(+), 556 deletions(-)

diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
index 100c4221256e..681ba329d52b 100644
--- a/drivers/clk/meson/a1-pll.c
+++ b/drivers/clk/meson/a1-pll.c
@@ -25,6 +25,36 @@
 
 #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table a1_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap a1_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_FIXPLL_CTRL0,
+		.shift  = 10,
+		.width  = 5,
+		.table = a1_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "fixpll_in",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap a1_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -37,11 +67,6 @@ static struct clk_regmap a1_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_FIXPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_FIXPLL_CTRL1,
 			.shift   = 0,
@@ -61,8 +86,8 @@ static struct clk_regmap a1_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "fixpll_in",
+		.parent_hws = (const struct clk_hw *[]) {
+			&a1_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -83,6 +108,24 @@ static struct clk_regmap a1_fixed_pll = {
 	},
 };
 
+static struct clk_regmap a1_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = a1_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "hifipll_in",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range a1_hifi_pll_range = {
 	.min = 32,
 	.max = 64,
@@ -108,11 +151,6 @@ static struct clk_regmap a1_hifi_pll = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -140,10 +178,11 @@ static struct clk_regmap a1_hifi_pll = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "hifipll_in",
+		.parent_hws = (const struct clk_hw *[]) {
+			&a1_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -281,6 +320,7 @@ static struct clk_regmap a1_fclk_div7 = {
 
 /* Array of all clocks registered by this provider */
 static struct clk_hw *a1_pll_hw_clks[] = {
+	[CLKID_FIXED_PREDIV]	= &a1_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	= &a1_fixed_pll_dco.hw,
 	[CLKID_FIXED_PLL]	= &a1_fixed_pll.hw,
 	[CLKID_FCLK_DIV2_DIV]	= &a1_fclk_div2_div.hw,
@@ -291,6 +331,7 @@ static struct clk_hw *a1_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV3]	= &a1_fclk_div3.hw,
 	[CLKID_FCLK_DIV5]	= &a1_fclk_div5.hw,
 	[CLKID_FCLK_DIV7]	= &a1_fclk_div7.hw,
+	[CLKID_HIFI_PREDIV]	= &a1_hifi_prediv.hw,
 	[CLKID_HIFI_PLL]	= &a1_hifi_pll.hw,
 };
 
diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
index 280842e9cb37..e6e6b9a8030c 100644
--- a/drivers/clk/meson/axg.c
+++ b/drivers/clk/meson/axg.c
@@ -107,6 +107,36 @@
 #define HHI_DPLL_TOP_I			0x318
 #define HHI_DPLL_TOP2_I			0x31C
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table axg_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap axg_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap axg_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -119,11 +149,6 @@ static struct clk_regmap axg_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -143,8 +168,8 @@ static struct clk_regmap axg_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -171,6 +196,24 @@ static struct clk_regmap axg_fixed_pll = {
 	},
 };
 
+static struct clk_regmap axg_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap axg_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -183,11 +226,6 @@ static struct clk_regmap axg_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -202,10 +240,11 @@ static struct clk_regmap axg_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -227,39 +266,57 @@ static struct clk_regmap axg_sys_pll = {
 	},
 };
 
-static const struct pll_params_table axg_gp0_pll_params_table[] = {
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(41, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(67, 1),
-	PLL_PARAMS(68, 1),
+static const struct pll_mult_table axg_gp0_pll_params_table[] = {
+	PLL_MULT(40),
+	PLL_MULT(41),
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
+	PLL_MULT(67),
+	PLL_MULT(68),
 	{ /* sentinel */ },
 };
 
+static struct clk_regmap axg_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence axg_gp0_init_regs[] = {
 	{ .reg = HHI_GP0_PLL_CNTL1,	.def = 0xc084b000 },
 	{ .reg = HHI_GP0_PLL_CNTL2,	.def = 0xb75020be },
@@ -280,11 +337,6 @@ static struct clk_regmap axg_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -307,10 +359,11 @@ static struct clk_regmap axg_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -332,6 +385,24 @@ static struct clk_regmap axg_gp0_pll = {
 	},
 };
 
+static struct clk_regmap axg_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HIFI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence axg_hifi_pll_init_regs[] = {
 	{ .reg = HHI_HIFI_PLL_CNTL1,	.def = 0xc084b000 },
 	{ .reg = HHI_HIFI_PLL_CNTL2,	.def = 0xb75020be },
@@ -352,11 +423,6 @@ static struct clk_regmap axg_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HIFI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HIFI_PLL_CNTL5,
 			.shift   = 0,
@@ -380,10 +446,11 @@ static struct clk_regmap axg_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -771,11 +838,35 @@ static struct clk_regmap axg_mpll3 = {
 	},
 };
 
-static const struct pll_params_table axg_pcie_pll_params_table[] = {
-	{
-		.m = 200,
-		.n = 3,
+/*
+ * A pre-divider value of 3 provides the best performance for
+ * the AXG PCIe PLL.
+ */
+static const struct clk_div_table axg_pcie_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap axg_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_PCIE_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_pcie_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
 	},
+};
+
+static const struct pll_mult_table axg_pcie_pll_params_table[] = {
+	PLL_MULT(200),
 	{ /* sentinel */ },
 };
 
@@ -801,11 +892,6 @@ static struct clk_regmap axg_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_PCIE_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_PCIE_PLL_CNTL1,
 			.shift   = 0,
@@ -828,10 +914,11 @@ static struct clk_regmap axg_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2067,10 +2154,15 @@ static struct clk_hw *axg_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]		= &axg_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]		= &axg_gen_clk_div.hw,
 	[CLKID_GEN_CLK]			= &axg_gen_clk.hw,
+	[CLKID_SYS_PREDIV]		= &axg_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &axg_sys_pll_dco.hw,
+	[CLKID_FIXED_PREDIV]		= &axg_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &axg_fixed_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &axg_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &axg_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &axg_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &axg_hifi_pll_dco.hw,
+	[CLKID_PCIE_PREDIV]		= &axg_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &axg_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_OD]		= &axg_pcie_pll_od.hw,
 	[CLKID_VPU_0_DIV]		= &axg_vpu_0_div.hw,
diff --git a/drivers/clk/meson/c3-pll.c b/drivers/clk/meson/c3-pll.c
index dd047d17488c..57bbd0498811 100644
--- a/drivers/clk/meson/c3-pll.c
+++ b/drivers/clk/meson/c3-pll.c
@@ -230,6 +230,36 @@ static struct clk_regmap c3_fclk_div7 = {
 	},
 };
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table c3_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap c3_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_GP0PLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "top",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_gp0_pll_init_regs[] = {
 	{ .reg = ANACTRL_GP0PLL_CTRL2,	.def = 0x0 },
 	{ .reg = ANACTRL_GP0PLL_CTRL3,	.def = 0x48681c00 },
@@ -260,11 +290,6 @@ static struct clk_regmap c3_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 19,
 		},
-		.n = {
-			.reg_off = ANACTRL_GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_GP0PLL_CTRL0,
 			.shift   = 31,
@@ -282,10 +307,11 @@ static struct clk_regmap c3_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "top",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -318,6 +344,24 @@ static struct clk_regmap c3_gp0_pll = {
 	},
 };
 
+static struct clk_regmap c3_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "top",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_hifi_pll_init_regs[] = {
 	{ .reg = ANACTRL_HIFIPLL_CTRL2,	.def = 0x0 },
 	{ .reg = ANACTRL_HIFIPLL_CTRL3,	.def = 0x6a285c00 },
@@ -343,11 +387,6 @@ static struct clk_regmap c3_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 19,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
 			.shift   = 31,
@@ -366,10 +405,11 @@ static struct clk_regmap c3_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "top",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -391,6 +431,24 @@ static struct clk_regmap c3_hifi_pll = {
 	},
 };
 
+static struct clk_regmap c3_mclk_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_MPLL_CTRL0,
+		.shift = 16,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "mclk_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "mclk",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_mclk_pll_init_regs[] = {
 	{ .reg = ANACTRL_MPLL_CTRL1,	.def = 0x1420500f },
 	{ .reg = ANACTRL_MPLL_CTRL2,	.def = 0x00023041 },
@@ -415,11 +473,6 @@ static struct clk_regmap c3_mclk_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_MPLL_CTRL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_MPLL_CTRL0,
 			.shift   = 31,
@@ -437,10 +490,11 @@ static struct clk_regmap c3_mclk_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "mclk_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "mclk",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_mclk_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -636,10 +690,13 @@ static struct clk_hw *c3_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV5]	= &c3_fclk_div5.hw,
 	[CLKID_FCLK_DIV7_DIV]	= &c3_fclk_div7_div.hw,
 	[CLKID_FCLK_DIV7]	= &c3_fclk_div7.hw,
+	[CLKID_GP0_PREDIV]	= &c3_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	= &c3_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]		= &c3_gp0_pll.hw,
+	[CLKID_HIFI_PREDIV]	= &c3_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]	= &c3_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]	= &c3_hifi_pll.hw,
+	[CLKID_MCLK_PREDIV]	= &c3_mclk_prediv.hw,
 	[CLKID_MCLK_PLL_DCO]	= &c3_mclk_pll_dco.hw,
 	[CLKID_MCLK_PLL_OD]	= &c3_mclk_pll_od.hw,
 	[CLKID_MCLK_PLL]	= &c3_mclk_pll.hw,
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index 1ea6579a760f..a7e3f8c8eb78 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -14,7 +14,7 @@
  *        +--------------------------------+
  *        |                                |
  *        |             +--+               |
- *  in >>-----[ /N ]--->|  |      +-----+  |
+ *  in >>----------->|  |      +-----+  |
  *        |             |  |------| DCO |---->> out
  *        |  +--------->|  |      +--v--+  |
  *        |  |          +--+         |     |
@@ -23,7 +23,7 @@
  *        |                                |
  *        +--------------------------------+
  *
- * out = in * (m + frac / frac_max) / n
+ * out = in * (m + frac / frac_max)
  */
 
 #include <linux/clk-provider.h>
@@ -52,8 +52,7 @@ static int __pll_round_closest_mult(struct meson_clk_pll_data *pll)
 }
 
 static unsigned long __pll_params_to_rate(unsigned long parent_rate,
-					  unsigned int m, unsigned int n,
-					  unsigned int frac,
+					  unsigned int m, unsigned int frac,
 					  struct meson_clk_pll_data *pll)
 {
 	u64 rate = (u64)parent_rate * m;
@@ -66,7 +65,7 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate,
 		rate += DIV_ROUND_UP_ULL(frac_rate, frac_max);
 	}
 
-	return DIV_ROUND_UP_ULL(rate, n);
+	return rate;
 }
 
 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
@@ -74,17 +73,7 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int m, n, frac;
-
-	n = meson_parm_read(clk->map, &pll->n);
-
-	/*
-	 * On some HW, N is set to zero on init. This value is invalid as
-	 * it would result in a division by zero. The rate can't be
-	 * calculated in this case
-	 */
-	if (n == 0)
-		return 0;
+	unsigned int m, frac;
 
 	m = meson_parm_read(clk->map, &pll->m);
 
@@ -92,21 +81,20 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 		meson_parm_read(clk->map, &pll->frac) :
 		0;
 
-	return __pll_params_to_rate(parent_rate, m, n, frac, pll);
+	return __pll_params_to_rate(parent_rate, m, frac, pll);
 }
 
 static unsigned int __pll_params_with_frac(unsigned long rate,
 					   unsigned long parent_rate,
 					   unsigned int m,
-					   unsigned int n,
 					   struct meson_clk_pll_data *pll)
 {
 	unsigned int frac_max = pll->frac_max ? pll->frac_max :
 						(1 << pll->frac.width);
-	u64 val = (u64)rate * n;
+	u64 val = (u64)rate;
 
 	/* Bail out if we are already over the requested rate */
-	if (rate < parent_rate * m / n)
+	if (rate < parent_rate * m)
 		return 0;
 
 	if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
@@ -119,128 +107,76 @@ static unsigned int __pll_params_with_frac(unsigned long rate,
 	return min((unsigned int)val, (frac_max - 1));
 }
 
-static bool meson_clk_pll_is_better(unsigned long rate,
-				    unsigned long best,
-				    unsigned long now,
-				    struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_range(unsigned long rate,
+				   unsigned long parent_rate,
+				   unsigned int *m,
+				   struct meson_clk_pll_data *pll)
 {
-	if (__pll_round_closest_mult(pll)) {
-		/* Round Closest */
-		if (abs(now - rate) < abs(best - rate))
-			return true;
-	} else {
-		/* Round down */
-		if (now <= rate && best < now)
-			return true;
+	if (rate <= pll->range->min * parent_rate) {
+		*m = pll->range->min;
+		return -ENODATA;
+	} else if (rate >= pll->range->max * parent_rate) {
+		*m = pll->range->max;
+		return -ENODATA;
 	}
 
-	return false;
-}
+	if (__pll_round_closest_mult(pll))
+		*m = DIV_ROUND_CLOSEST_ULL(rate, parent_rate);
+	else
+		*m = div_u64(rate, parent_rate);
 
-static int meson_clk_get_pll_table_index(unsigned int index,
-					 unsigned int *m,
-					 unsigned int *n,
-					 struct meson_clk_pll_data *pll)
-{
-	if (!pll->table[index].n)
+	/* Multiplier exceeds hardware range */
+	if (*m >= (1 << pll->m.width))
 		return -EINVAL;
 
-	*m = pll->table[index].m;
-	*n = pll->table[index].n;
-
 	return 0;
 }
 
-static unsigned int meson_clk_get_pll_range_m(unsigned long rate,
-					      unsigned long parent_rate,
-					      unsigned int n,
-					      struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_table(unsigned long rate,
+				   unsigned long parent_rate,
+				   unsigned int *m,
+				   struct meson_clk_pll_data *pll)
 {
-	u64 val = (u64)rate * n;
-
-	if (__pll_round_closest_mult(pll))
-		return DIV_ROUND_CLOSEST_ULL(val, parent_rate);
-
-	return div_u64(val,  parent_rate);
-}
-
-static int meson_clk_get_pll_range_index(unsigned long rate,
-					 unsigned long parent_rate,
-					 unsigned int index,
-					 unsigned int *m,
-					 unsigned int *n,
-					 struct meson_clk_pll_data *pll)
-{
-	*n = index + 1;
+	unsigned int i;
+	int ret;
 
-	/* Check the predivider range */
-	if (*n >= (1 << pll->n.width))
-		return -EINVAL;
+	for (i = 0, ret = 0; !ret; i++) {
+		if (!pll->table[i].m)
+			return -EINVAL;
 
-	if (*n == 1) {
-		/* Get the boundaries out the way */
-		if (rate <= pll->range->min * parent_rate) {
-			*m = pll->range->min;
-			return -ENODATA;
-		} else if (rate >= pll->range->max * parent_rate) {
-			*m = pll->range->max;
-			return -ENODATA;
+		if (pll->table[i].m == rate / parent_rate) {
+			*m = pll->table[i].m;
+			return 0;
 		}
 	}
 
-	*m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll);
-
-	/* the pre-divider gives a multiplier too big - stop */
-	if (*m >= (1 << pll->m.width))
-		return -EINVAL;
-
 	return 0;
 }
 
-static int meson_clk_get_pll_get_index(unsigned long rate,
-				       unsigned long parent_rate,
-				       unsigned int index,
-				       unsigned int *m,
-				       unsigned int *n,
-				       struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_multiplier(unsigned long rate,
+					unsigned long parent_rate,
+					unsigned int *m,
+					struct meson_clk_pll_data *pll)
 {
 	if (pll->range)
-		return meson_clk_get_pll_range_index(rate, parent_rate,
-						     index, m, n, pll);
+		return meson_clk_get_pll_range(rate, parent_rate, m, pll);
 	else if (pll->table)
-		return meson_clk_get_pll_table_index(index, m, n, pll);
+		return meson_clk_get_pll_table(rate, parent_rate, m, pll);
 
 	return -EINVAL;
 }
 
-static int meson_clk_get_pll_settings(unsigned long rate,
-				      unsigned long parent_rate,
-				      unsigned int *best_m,
-				      unsigned int *best_n,
-				      struct meson_clk_pll_data *pll)
+static unsigned int meson_clk_get_pll_max_multiplier(struct meson_clk_pll_data *pll)
 {
-	unsigned long best = 0, now = 0;
-	unsigned int i, m, n;
-	int ret;
+	unsigned int i, max_mult = 0;
 
-	for (i = 0, ret = 0; !ret; i++) {
-		ret = meson_clk_get_pll_get_index(rate, parent_rate,
-						  i, &m, &n, pll);
-		if (ret == -EINVAL)
-			break;
-
-		now = __pll_params_to_rate(parent_rate, m, n, 0, pll);
-		if (meson_clk_pll_is_better(rate, best, now, pll)) {
-			best = now;
-			*best_m = m;
-			*best_n = n;
-
-			if (now == rate)
-				break;
-		}
-	}
+	if (pll->range)
+		return pll->range->max;
+
+	for (i = 0; pll->table && pll->table[i].m; i++)
+		max_mult = max(max_mult, pll->table[i].m);
 
-	return best ? 0 : -EINVAL;
+	return max_mult;
 }
 
 static int meson_clk_pll_determine_rate(struct clk_hw *hw,
@@ -248,16 +184,32 @@ static int meson_clk_pll_determine_rate(struct clk_hw *hw,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int m, n, frac;
+	unsigned int max_mult, m, frac;
 	unsigned long round;
 	int ret;
 
-	ret = meson_clk_get_pll_settings(req->rate, req->best_parent_rate,
-					 &m, &n, pll);
-	if (ret)
+	/*
+	 * The pre-divider resets to an invalid zero value and reports a
+	 * zero rate until it gets programmed. Ask the parent for the
+	 * lowest rate which keeps the requested rate reachable; the CCF
+	 * will program the pre-divider when applying the parent rate.
+	 */
+	if (!req->best_parent_rate) {
+		max_mult = meson_clk_get_pll_max_multiplier(pll);
+		if (!max_mult)
+			return -EINVAL;
+
+		req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw,
+							  DIV_ROUND_UP_ULL(req->rate, max_mult));
+		if (!req->best_parent_rate)
+			return -EINVAL;
+	}
+
+	ret = meson_clk_get_pll_multiplier(req->rate, req->best_parent_rate, &m, pll);
+	if (ret && ret != -ENODATA)
 		return ret;
 
-	round = __pll_params_to_rate(req->best_parent_rate, m, n, 0, pll);
+	round = __pll_params_to_rate(req->best_parent_rate, m, 0, pll);
 
 	if (!MESON_PARM_APPLICABLE(&pll->frac) || req->rate == round) {
 		req->rate = round;
@@ -268,8 +220,8 @@ static int meson_clk_pll_determine_rate(struct clk_hw *hw,
 	 * The rate provided by the setting is not an exact match, let's
 	 * try to improve the result using the fractional parameter
 	 */
-	frac = __pll_params_with_frac(req->rate, req->best_parent_rate, m, n, pll);
-	req->rate = __pll_params_to_rate(req->best_parent_rate, m, n, frac, pll);
+	frac = __pll_params_with_frac(req->rate, req->best_parent_rate, m, pll);
+	req->rate = __pll_params_to_rate(req->best_parent_rate, m, frac, pll);
 
 	return 0;
 }
@@ -420,7 +372,7 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int enabled, m, n, frac = 0;
+	unsigned int enabled, m, frac = 0;
 	unsigned long old_rate;
 	int ret;
 
@@ -429,19 +381,18 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 
 	old_rate = clk_hw_get_rate(hw);
 
-	ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll);
-	if (ret)
+	ret = meson_clk_get_pll_multiplier(rate, parent_rate, &m, pll);
+	if (ret && ret != -ENODATA)
 		return ret;
 
 	enabled = meson_parm_read(clk->map, &pll->en);
 	if (enabled)
 		meson_clk_pll_disable(hw);
 
-	meson_parm_write(clk->map, &pll->n, n);
 	meson_parm_write(clk->map, &pll->m, m);
 
 	if (MESON_PARM_APPLICABLE(&pll->frac)) {
-		frac = __pll_params_with_frac(rate, parent_rate, m, n, pll);
+		frac = __pll_params_with_frac(rate, parent_rate, m, pll);
 		meson_parm_write(clk->map, &pll->frac, frac);
 	}
 
diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h
index 949157fb7bf5..cf20deaac1ed 100644
--- a/drivers/clk/meson/clk-pll.h
+++ b/drivers/clk/meson/clk-pll.h
@@ -11,9 +11,8 @@
 #include <linux/regmap.h>
 #include "parm.h"
 
-struct pll_params_table {
-	unsigned int	m;
-	unsigned int	n;
+struct pll_mult_table {
+	unsigned int    m;
 };
 
 struct pll_mult_range {
@@ -21,11 +20,10 @@ struct pll_mult_range {
 	unsigned int	max;
 };
 
-#define PLL_PARAMS(_m, _n)						\
-	{								\
-		.m		= (_m),					\
-		.n		= (_n),					\
-	}
+#define PLL_MULT(_m)			\
+{					\
+	.m = (_m),			\
+}
 
 #define CLK_MESON_PLL_ROUND_CLOSEST	BIT(0)
 #define CLK_MESON_PLL_NOINIT_ENABLED	BIT(1)
@@ -33,7 +31,6 @@ struct pll_mult_range {
 struct meson_clk_pll_data {
 	struct parm en;
 	struct parm m;
-	struct parm n;
 	struct parm frac;
 	struct parm l;
 	struct parm rst;
@@ -41,7 +38,7 @@ struct meson_clk_pll_data {
 	struct parm l_detect;
 	const struct reg_sequence *init_regs;
 	unsigned int init_count;
-	const struct pll_params_table *table;
+	const struct pll_mult_table *table;
 	const struct pll_mult_range *range;
 	unsigned int frac_max;
 	u8 flags;
diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h
index 8e5c39b023e1..ee2a4b3408f0 100644
--- a/drivers/clk/meson/clk-regmap.h
+++ b/drivers/clk/meson/clk-regmap.h
@@ -75,7 +75,7 @@ struct clk_regmap_div_data {
 	unsigned int	offset;
 	u8		shift;
 	u8		width;
-	u8		flags;
+	u16		flags;
 	const struct clk_div_table	*table;
 };
 
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index d0d4c7b6dc82..4be367c89aca 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -137,6 +137,36 @@
 #define HHI_SYS1_PLL_CNTL5		0x394
 #define HHI_SYS1_PLL_CNTL6		0x398
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table g12a_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap g12a_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_FIX_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -149,11 +179,6 @@ static struct clk_regmap g12a_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_FIX_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_FIX_PLL_CNTL1,
 			.shift   = 0,
@@ -173,8 +198,8 @@ static struct clk_regmap g12a_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -206,6 +231,24 @@ static const struct pll_mult_range g12a_sys_pll_mult_range = {
 	.max = 250,
 };
 
+static struct clk_regmap g12a_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -218,11 +261,6 @@ static struct clk_regmap g12a_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL0,
 			.shift   = 31,
@@ -238,12 +276,12 @@ static struct clk_regmap g12a_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_sys_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the CPU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -265,6 +303,24 @@ static struct clk_regmap g12a_sys_pll = {
 	},
 };
 
+static struct clk_regmap g12b_sys1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS1_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12b_sys1_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -277,11 +333,6 @@ static struct clk_regmap g12b_sys1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_SYS1_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS1_PLL_CNTL0,
 			.shift   = 31,
@@ -297,12 +348,12 @@ static struct clk_regmap g12b_sys1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys1_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12b_sys1_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the CPU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -386,6 +437,24 @@ static struct clk_fixed_factor g12b_sys1_pll_div16 = {
 	},
 };
 
+static struct clk_regmap g12a_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range g12a_gp0_pll_mult_range = {
 	.min = 125,
 	.max = 255,
@@ -415,11 +484,6 @@ static struct clk_regmap g12a_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -442,10 +506,11 @@ static struct clk_regmap g12a_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -468,6 +533,24 @@ static struct clk_regmap g12a_gp0_pll = {
 	},
 };
 
+static struct clk_regmap sm1_gp1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP1_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap sm1_gp1_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -480,11 +563,6 @@ static struct clk_regmap sm1_gp1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_GP1_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP1_PLL_CNTL1,
 			.shift   = 0,
@@ -504,12 +582,12 @@ static struct clk_regmap sm1_gp1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp1_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&sm1_gp1_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the DSU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -531,6 +609,24 @@ static struct clk_regmap sm1_gp1_pll = {
 	},
 };
 
+static struct clk_regmap g12a_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HIFI_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Internal hifi pll emulation configuration parameters
  */
@@ -555,11 +651,6 @@ static struct clk_regmap g12a_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_HIFI_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HIFI_PLL_CNTL1,
 			.shift   = 0,
@@ -583,10 +674,11 @@ static struct clk_regmap g12a_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -609,6 +701,24 @@ static struct clk_regmap g12a_hifi_pll = {
 	},
 };
 
+static struct clk_regmap g12a_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_PCIE_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * The Meson G12A PCIE PLL is fined tuned to deliver a very precise
  * 100MHz reference clock for the PCIe Analog PHY, and thus requires
@@ -630,9 +740,9 @@ static const struct reg_sequence g12a_pcie_pll_init_regs[] = {
 };
 
 /* Keep a single entry table for recalc/round_rate() ops */
-static const struct pll_params_table g12a_pcie_pll_table[] = {
-	PLL_PARAMS(150, 1),
-	{0, 0},
+static const struct pll_mult_table g12a_pcie_pll_table[] = {
+	PLL_MULT(150),
+	{ /* sentinel */ },
 };
 
 static struct clk_regmap g12a_pcie_pll_dco = {
@@ -647,11 +757,6 @@ static struct clk_regmap g12a_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_PCIE_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_PCIE_PLL_CNTL1,
 			.shift   = 0,
@@ -674,10 +779,11 @@ static struct clk_regmap g12a_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pcie_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -729,6 +835,24 @@ static struct clk_fixed_factor g12a_pcie_pll = {
 	},
 };
 
+static struct clk_regmap g12a_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -741,11 +865,6 @@ static struct clk_regmap g12a_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HDMI_PLL_CNTL1,
 			.shift   = 0,
@@ -765,15 +884,15 @@ static struct clk_regmap g12a_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
 	},
 };
 
@@ -4593,9 +4712,13 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -4618,6 +4741,7 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -4693,6 +4817,7 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -4820,9 +4945,13 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -4845,6 +4974,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -4920,6 +5050,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -4935,6 +5066,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VDEC_HEVCF]		= &g12a_vdec_hevcf.hw,
 	[CLKID_TS_DIV]			= &g12a_ts_div.hw,
 	[CLKID_TS]			= &g12a_ts.hw,
+	[CLKID_SYS1_PREDIV]		= &g12b_sys1_prediv.hw,
 	[CLKID_SYS1_PLL_DCO]		= &g12b_sys1_pll_dco.hw,
 	[CLKID_SYS1_PLL]		= &g12b_sys1_pll.hw,
 	[CLKID_SYS1_PLL_DIV16_EN]	= &g12b_sys1_pll_div16_en.hw,
@@ -5088,9 +5220,13 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -5113,6 +5249,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -5188,6 +5325,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -5204,6 +5342,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_TS_DIV]			= &g12a_ts_div.hw,
 	[CLKID_TS]			= &g12a_ts.hw,
 	[CLKID_GP1_PLL_DCO]		= &sm1_gp1_pll_dco.hw,
+	[CLKID_GP1_PREDIV]		= &sm1_gp1_prediv.hw,
 	[CLKID_GP1_PLL]			= &sm1_gp1_pll.hw,
 	[CLKID_DSU_CLK_DYN0_SEL]	= &sm1_dsu_clk_dyn0_sel.hw,
 	[CLKID_DSU_CLK_DYN0_DIV]	= &sm1_dsu_clk_dyn0_div.hw,
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 7a7b104182ff..9d09f13c22fb 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -115,6 +115,35 @@
 #define HHI_BT656_CLK_CNTL		0x3d4
 #define HHI_SAR_CLK_CNTL		0x3d8
 
+static const struct clk_div_table gxbb_fixed_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap gxbb_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_fixed_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -127,11 +156,6 @@ static struct clk_regmap gxbb_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -151,8 +175,8 @@ static struct clk_regmap gxbb_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -192,6 +216,30 @@ static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = {
 	},
 };
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table gxbb_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap gxbb_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_hdmi_pll_pre_mult.hw
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -204,11 +252,6 @@ static struct clk_regmap gxbb_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HDMI_PLL_CNTL2,
 			.shift   = 0,
@@ -229,14 +272,32 @@ static struct clk_regmap gxbb_hdmi_pll_dco = {
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
 		.parent_hws = (const struct clk_hw *[]) {
-			&gxbb_hdmi_pll_pre_mult.hw
+			&gxbb_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap gxl_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
 	},
 };
 
@@ -252,11 +313,6 @@ static struct clk_regmap gxl_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		/*
 		 * On gxl, there is a register shift due to
 		 * HHI_HDMI_PLL_CNTL1 which does not exist on gxbb,
@@ -282,15 +338,15 @@ static struct clk_regmap gxl_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxl_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -413,6 +469,24 @@ static struct clk_regmap gxl_hdmi_pll = {
 	},
 };
 
+static struct clk_regmap gxbb_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -425,11 +499,6 @@ static struct clk_regmap gxbb_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -444,10 +513,11 @@ static struct clk_regmap gxbb_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -469,38 +539,56 @@ static struct clk_regmap gxbb_sys_pll = {
 	},
 };
 
-static const struct pll_params_table gxbb_gp0_pll_params_table[] = {
-	PLL_PARAMS(32, 1),
-	PLL_PARAMS(33, 1),
-	PLL_PARAMS(34, 1),
-	PLL_PARAMS(35, 1),
-	PLL_PARAMS(36, 1),
-	PLL_PARAMS(37, 1),
-	PLL_PARAMS(38, 1),
-	PLL_PARAMS(39, 1),
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(41, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
+static struct clk_regmap gxbb_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
+static const struct pll_mult_table gxbb_gp0_pll_params_table[] = {
+	PLL_MULT(32),
+	PLL_MULT(33),
+	PLL_MULT(34),
+	PLL_MULT(35),
+	PLL_MULT(36),
+	PLL_MULT(37),
+	PLL_MULT(38),
+	PLL_MULT(39),
+	PLL_MULT(40),
+	PLL_MULT(41),
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
 	{ /* sentinel */ },
 };
 
@@ -522,11 +610,6 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_GP0_PLL_CNTL,
 			.shift   = 31,
@@ -544,6 +627,25 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_gp0_prediv.hw
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap gxl_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
 		.parent_data = &(const struct clk_parent_data) {
 			.fw_name = "xtal",
 		},
@@ -551,32 +653,32 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 	},
 };
 
-static const struct pll_params_table gxl_gp0_pll_params_table[] = {
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
+static const struct pll_mult_table gxl_gp0_pll_params_table[] = {
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
 	{ /* sentinel */ },
 };
 
@@ -600,11 +702,6 @@ static struct clk_regmap gxl_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -627,10 +724,11 @@ static struct clk_regmap gxl_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxl_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -3006,11 +3104,15 @@ static struct clk_hw *gxbb_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]	    = &gxbb_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]	    = &gxbb_gen_clk_div.hw,
 	[CLKID_GEN_CLK]		    = &gxbb_gen_clk.hw,
+	[CLKID_FIXED_PREDIV]	    = &gxbb_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	    = &gxbb_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &gxbb_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &gxbb_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]	    = &gxbb_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]	    = &gxbb_hdmi_pll_od2.hw,
+	[CLKID_SYS_PREDIV]	    = &gxbb_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]	    = &gxbb_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]	    = &gxbb_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	    = &gxbb_gp0_pll_dco.hw,
 	[CLKID_VID_PLL_DIV]	    = &gxbb_vid_pll_div.hw,
 	[CLKID_VID_PLL_SEL]	    = &gxbb_vid_pll_sel.hw,
@@ -3213,11 +3315,15 @@ static struct clk_hw *gxl_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]	    = &gxbb_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]	    = &gxbb_gen_clk_div.hw,
 	[CLKID_GEN_CLK]		    = &gxbb_gen_clk.hw,
+	[CLKID_FIXED_PREDIV]	    = &gxbb_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	    = &gxbb_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &gxl_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &gxl_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]	    = &gxl_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]	    = &gxl_hdmi_pll_od2.hw,
+	[CLKID_SYS_PREDIV]	    = &gxbb_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]	    = &gxbb_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]	    = &gxl_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	    = &gxl_gp0_pll_dco.hw,
 	[CLKID_VID_PLL_DIV]	    = &gxbb_vid_pll_div.hw,
 	[CLKID_VID_PLL_SEL]	    = &gxbb_vid_pll_sel.hw,
diff --git a/drivers/clk/meson/meson8-ddr.c b/drivers/clk/meson/meson8-ddr.c
index 0f93774f7371..a3f4c47982d9 100644
--- a/drivers/clk/meson/meson8-ddr.c
+++ b/drivers/clk/meson/meson8-ddr.c
@@ -23,6 +23,35 @@
 #define DDR_CLK_CNTL			0x18
 #define DDR_CLK_STS			0x1c
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table meson8_ddr_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8_ddr_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = AM_DDR_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8_ddr_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "ddr_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8_ddr_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -35,11 +64,6 @@ static struct clk_regmap meson8_ddr_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = AM_DDR_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = AM_DDR_PLL_CNTL,
 			.shift   = 31,
@@ -54,8 +78,8 @@ static struct clk_regmap meson8_ddr_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "ddr_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8_ddr_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -79,6 +103,7 @@ static struct clk_regmap meson8_ddr_pll = {
 };
 
 static struct clk_hw *meson8_ddr_hw_clks[] = {
+	[DDR_CLKID_DDR_PREDIV]		= &meson8_ddr_prediv.hw,
 	[DDR_CLKID_DDR_PLL_DCO]		= &meson8_ddr_pll_dco.hw,
 	[DDR_CLKID_DDR_PLL]		= &meson8_ddr_pll.hw,
 };
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index bc029c6ab622..0c9ba079bc51 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -95,30 +95,61 @@ struct meson8b_clk_reset {
 	struct regmap *regmap;
 };
 
-static const struct pll_params_table sys_pll_params_table[] = {
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(67, 1),
-	PLL_PARAMS(68, 1),
-	PLL_PARAMS(84, 1),
+static const struct pll_mult_table sys_pll_params_table[] = {
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
+	PLL_MULT(67),
+	PLL_MULT(68),
+	PLL_MULT(84),
 	{ /* sentinel */ },
 };
 
+static const struct clk_div_table meson8b_fixed_prediv_div_table[] = {
+	{ .val = 4, .div = 4 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8b_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_fixed_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8b_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -131,11 +162,6 @@ static struct clk_regmap meson8b_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -155,10 +181,8 @@ static struct clk_regmap meson8b_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -185,15 +209,38 @@ static struct clk_regmap meson8b_fixed_pll = {
 	},
 };
 
+static const struct clk_div_table meson8b_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8b_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_VID_PLL_CNTL,
+		.shift   = 10,
+		.width   = 5,
+		.table = meson8b_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_fixed_factor hdmi_pll_dco_in = {
 	.mult = 2,
 	.div = 1,
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco_in",
 		.ops = &clk_fixed_factor_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -214,24 +261,24 @@ static const struct reg_sequence meson8b_hdmi_pll_init_regs[] = {
 	{ .reg = HHI_VID2_PLL_CNTL2,	.def = 0x0430a800 },
 };
 
-static const struct pll_params_table meson8b_hdmi_pll_params_table[] = {
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(68, 1),
-	PLL_PARAMS(71, 1),
-	PLL_PARAMS(82, 1),
+static const struct pll_mult_table meson8b_hdmi_pll_params_table[] = {
+	PLL_MULT(40),
+	PLL_MULT(42),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(49),
+	PLL_MULT(52),
+	PLL_MULT(54),
+	PLL_MULT(56),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(64),
+	PLL_MULT(66),
+	PLL_MULT(68),
+	PLL_MULT(71),
+	PLL_MULT(82),
 	{ /* sentinel */ }
 };
 
@@ -247,11 +294,6 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_VID_PLL_CNTL,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_VID_PLL_CNTL2,
 			.shift   = 0,
@@ -279,6 +321,7 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
 			&hdmi_pll_dco_in.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -318,6 +361,26 @@ static struct clk_regmap meson8b_hdmi_pll_hdmi_out = {
 	},
 };
 
+static struct clk_regmap meson8b_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8b_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -330,11 +393,6 @@ static struct clk_regmap meson8b_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -350,12 +408,11 @@ static struct clk_regmap meson8b_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2035,6 +2092,31 @@ static struct clk_regmap meson8b_mali = {
 	},
 };
 
+static const struct clk_div_table meson8b_gp_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8m2_gp_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_gp_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence meson8m2_gp_pll_init_regs[] = {
 	{ .reg = HHI_GP_PLL_CNTL2,	.def = 0x59c88000 },
 	{ .reg = HHI_GP_PLL_CNTL3,	.def = 0xca463823 },
@@ -2042,8 +2124,8 @@ static const struct reg_sequence meson8m2_gp_pll_init_regs[] = {
 	{ .reg = HHI_GP_PLL_CNTL5,	.def = 0x00003000 },
 };
 
-static const struct pll_params_table meson8m2_gp_pll_params_table[] = {
-	PLL_PARAMS(182, 3),
+static const struct pll_mult_table meson8m2_gp_pll_params_table[] = {
+	PLL_MULT(182),
 	{ /* sentinel */ },
 };
 
@@ -2059,11 +2141,6 @@ static struct clk_regmap meson8m2_gp_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_GP_PLL_CNTL,
 			.shift   = 31,
@@ -2081,12 +2158,11 @@ static struct clk_regmap meson8m2_gp_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8m2_gp_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2920,8 +2996,11 @@ static struct clk_hw *meson8_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3124,8 +3203,11 @@ static struct clk_hw *meson8b_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3339,8 +3421,11 @@ static struct clk_hw *meson8m2_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3410,6 +3495,7 @@ static struct clk_hw *meson8m2_hw_clks[] = {
 	[CLKID_MALI_1_DIV]	    = &meson8b_mali_1_div.hw,
 	[CLKID_MALI_1]		    = &meson8b_mali_1.hw,
 	[CLKID_MALI]		    = &meson8b_mali.hw,
+	[CLKID_GP_PREDIV]	    = &meson8m2_gp_prediv.hw,
 	[CLKID_GP_PLL_DCO]	    = &meson8m2_gp_pll_dco.hw,
 	[CLKID_GP_PLL]		    = &meson8m2_gp_pll.hw,
 	[CLKID_VPU_0_SEL]	    = &meson8m2_vpu_0_sel.hw,
diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c
index 56ce6f566e53..6caa95887971 100644
--- a/drivers/clk/meson/s4-pll.c
+++ b/drivers/clk/meson/s4-pll.c
@@ -44,6 +44,12 @@
 #define ANACTRL_MPLL_CTRL8                         0x1a0
 #define ANACTRL_HDMIPLL_CTRL0                      0x1c0
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table s4_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
 /*
  * These clock are a fixed value (fixed_pll is 2GHz) that is initialized by ROMcode.
  * The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable
@@ -51,6 +57,30 @@
  * Meanwhile, these clock won't ever change at runtime.
  * For the above reasons, we can only use ro_ops for fixed PLL related clocks.
  */
+static struct clk_regmap s4_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_FIXPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap s4_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -68,11 +98,6 @@ static struct clk_regmap s4_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 17,
 		},
-		.n = {
-			.reg_off = ANACTRL_FIXPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_FIXPLL_CTRL0,
 			.shift   = 31,
@@ -87,10 +112,11 @@ static struct clk_regmap s4_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_fixed_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -273,6 +299,24 @@ static struct clk_regmap s4_fclk_div2p5 = {
 	},
 };
 
+static struct clk_regmap s4_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_GP0PLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range s4_gp0_pll_mult_range = {
 	.min = 125,
 	.max = 250,
@@ -302,11 +346,6 @@ static struct clk_regmap s4_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_GP0PLL_CTRL0,
 			.shift   = 31,
@@ -324,10 +363,11 @@ static struct clk_regmap s4_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -350,6 +390,24 @@ static struct clk_regmap s4_gp0_pll = {
 	},
 };
 
+static struct clk_regmap s4_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Internal hifi pll emulation configuration parameters
  */
@@ -373,11 +431,6 @@ static struct clk_regmap s4_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -402,10 +455,11 @@ static struct clk_regmap s4_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -428,6 +482,24 @@ static struct clk_regmap s4_hifi_pll = {
 	},
 };
 
+static struct clk_regmap s4_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HDMIPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap s4_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -440,11 +512,6 @@ static struct clk_regmap s4_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HDMIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
 			.shift   = 31,
@@ -460,10 +527,11 @@ static struct clk_regmap s4_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_hdmi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -760,6 +828,7 @@ static struct clk_regmap s4_mpll3 = {
 
 /* Array of all clocks provided by this provider */
 static struct clk_hw *s4_pll_hw_clks[] = {
+	[CLKID_FIXED_PREDIV]		= &s4_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &s4_fixed_pll_dco.hw,
 	[CLKID_FIXED_PLL]		= &s4_fixed_pll.hw,
 	[CLKID_FCLK_DIV2_DIV]		= &s4_fclk_div2_div.hw,
@@ -774,10 +843,13 @@ static struct clk_hw *s4_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV7]		= &s4_fclk_div7.hw,
 	[CLKID_FCLK_DIV2P5_DIV]		= &s4_fclk_div2p5_div.hw,
 	[CLKID_FCLK_DIV2P5]		= &s4_fclk_div2p5.hw,
+	[CLKID_GP0_PREDIV]		= &s4_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &s4_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]			= &s4_gp0_pll.hw,
+	[CLKID_HIFI_PREDIV]		= &s4_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &s4_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]		= &s4_hifi_pll.hw,
+	[CLKID_HDMI_PREDIV]		= &s4_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &s4_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &s4_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL]		= &s4_hdmi_pll.hw,
diff --git a/drivers/clk/meson/t7-pll.c b/drivers/clk/meson/t7-pll.c
index 0a622f45fa36..28d563fcb1bd 100644
--- a/drivers/clk/meson/t7-pll.c
+++ b/drivers/clk/meson/t7-pll.c
@@ -71,6 +71,36 @@
 #define MCLK_PLL_CNTL4		0x10
 #define MCLK_PLL_STS		0x14
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table t7_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap t7_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = GP0PLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range t7_media_pll_mult_range = {
 	.min = 125,
 	.max = 250,
@@ -97,11 +127,6 @@ static struct clk_regmap t7_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = GP0PLL_STS,
 			.shift   = 31,
@@ -119,10 +144,11 @@ static struct clk_regmap t7_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -144,6 +170,24 @@ static struct clk_regmap t7_gp0_pll = {
 	},
 };
 
+static struct clk_regmap t7_gp1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = GP1PLL_CTRL0,
+		.shift = 16,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Compared with GP0 PLL, GP1 PLL is a newly designed PLL with
  * a DCO range of 1.6GHz to 3.2GHz.
@@ -171,11 +215,6 @@ static struct clk_regmap t7_gp1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = GP1PLL_CTRL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = GP1PLL_STS,
 			.shift   = 31,
@@ -193,10 +232,11 @@ static struct clk_regmap t7_gp1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp1_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_gp1_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -218,6 +258,24 @@ static struct clk_regmap t7_gp1_pll = {
 	},
 };
 
+static struct clk_regmap t7_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence t7_hifi_init_regs[] = {
 	{ .reg = HIFIPLL_CTRL1, .def = 0x00000000 },
 	{ .reg = HIFIPLL_CTRL2, .def = 0x00000000 },
@@ -239,11 +297,6 @@ static struct clk_regmap t7_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -267,10 +320,11 @@ static struct clk_regmap t7_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -292,6 +346,24 @@ static struct clk_regmap t7_hifi_pll = {
 	},
 };
 
+static struct clk_regmap t7_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = PCIEPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * The T7 PCIE PLL is fined tuned to deliver a very precise
  * 100MHz reference clock for the PCIe Analog PHY, and thus requires
@@ -324,11 +396,6 @@ static struct clk_regmap t7_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = PCIEPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = PCIEPLL_CTRL0,
 			.shift   = 31,
@@ -345,10 +412,11 @@ static struct clk_regmap t7_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pcie_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -624,6 +692,24 @@ static struct clk_regmap t7_mpll3 = {
 	},
 };
 
+static struct clk_regmap t7_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HDMIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence t7_hdmi_init_regs[] = {
 	{ .reg = HDMIPLL_CTRL1, .def = 0x00000000 },
 	{ .reg = HDMIPLL_CTRL2, .def = 0x00000000 },
@@ -645,11 +731,6 @@ static struct clk_regmap t7_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HDMIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HDMIPLL_CTRL0,
 			.shift   = 31,
@@ -667,10 +748,11 @@ static struct clk_regmap t7_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "in0", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_hdmi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -722,6 +804,24 @@ static const struct reg_sequence t7_mclk_init_regs[] = {
 	{ .reg = MCLK_PLL_CNTL4, .def = 0x00180303 },
 };
 
+static struct clk_regmap t7_mclk_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = MCLK_PLL_CNTL0,
+		.shift = 16,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "mclk_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap t7_mclk_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -734,11 +834,6 @@ static struct clk_regmap t7_mclk_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = MCLK_PLL_CNTL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = MCLK_PLL_CNTL0,
 			.shift   = 31,
@@ -761,10 +856,11 @@ static struct clk_regmap t7_mclk_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "mclk_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_mclk_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -939,21 +1035,25 @@ static struct clk_regmap t7_mclk_1 = {
 };
 
 static struct clk_hw *t7_gp0_hw_clks[] = {
+	[CLKID_GP0_PREDIV]		= &t7_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &t7_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]			= &t7_gp0_pll.hw,
 };
 
 static struct clk_hw *t7_gp1_hw_clks[] = {
+	[CLKID_GP1_PREDIV]		= &t7_gp1_prediv.hw,
 	[CLKID_GP1_PLL_DCO]		= &t7_gp1_pll_dco.hw,
 	[CLKID_GP1_PLL]			= &t7_gp1_pll.hw,
 };
 
 static struct clk_hw *t7_hifi_hw_clks[] = {
+	[CLKID_HIFI_PREDIV]		= &t7_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &t7_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]		= &t7_hifi_pll.hw,
 };
 
 static struct clk_hw *t7_pcie_hw_clks[] = {
+	[CLKID_PCIE_PREDIV]		= &t7_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &t7_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &t7_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &t7_pcie_pll_od.hw,
@@ -973,12 +1073,14 @@ static struct clk_hw *t7_mpll_hw_clks[] = {
 };
 
 static struct clk_hw *t7_hdmi_hw_clks[] = {
+	[CLKID_HDMI_PREDIV]		= &t7_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &t7_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &t7_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL]		= &t7_hdmi_pll.hw,
 };
 
 static struct clk_hw *t7_mclk_hw_clks[] = {
+	[CLKID_MCLK_PREDIV]		= &t7_mclk_prediv.hw,
 	[CLKID_MCLK_PLL_DCO]		= &t7_mclk_pll_dco.hw,
 	[CLKID_MCLK_PRE]		= &t7_mclk_pre_od.hw,
 	[CLKID_MCLK_PLL]		= &t7_mclk_pll.hw,

-- 
2.47.1


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

* [PATCH RFC RFT v2 3/3] clk: meson: pll: Remove the dedicated n parameter
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

From: Jian Hu <jian.hu@amlogic.com>

The Meson PLL framework models the PLL pre-divider (N) using a dedicated
field in struct meson_clk_pll_data. Since the common clock framework
already provides a generic divider implementation, there is no need to
keep PLL-specific support for it.

The pre-divider is not intended to be dynamically adjusted. According to
the hardware design, each PLL has a single fixed N value, defined together
with the rest of the PLL. N = 1 for most PLLs, and N = 3 for a few special
cases (the axg PCIe PLL, gxbb Fixed PLL and the meson8b GP0 PLL). The PLL
input frequency constraints are respected by this fixed value.

Remove the dedicated n parameter from the framework and simplify the PLL
rate calculation accordingly. The pre-divider will be represented as a
separate divider clock by platform drivers where needed.

Replace the dedicated PLL pre-divider with a standalone divider clock
for all amlogic SoCs. The PLL DCO now takes the pre-divider clock
as its parent instead of the input clock directly.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 drivers/clk/meson/a1-pll.c     |  69 ++++++++--
 drivers/clk/meson/axg.c        | 230 +++++++++++++++++++++----------
 drivers/clk/meson/c3-pll.c     |  99 +++++++++++---
 drivers/clk/meson/clk-pll.c    | 203 +++++++++++----------------
 drivers/clk/meson/clk-pll.h    |  17 +--
 drivers/clk/meson/clk-regmap.h |   2 +-
 drivers/clk/meson/g12a.c       | 265 ++++++++++++++++++++++++++---------
 drivers/clk/meson/gxbb.c       | 304 +++++++++++++++++++++++++++--------------
 drivers/clk/meson/meson8-ddr.c |  39 +++++-
 drivers/clk/meson/meson8b.c    | 238 +++++++++++++++++++++-----------
 drivers/clk/meson/s4-pll.c     | 128 +++++++++++++----
 drivers/clk/meson/t7-pll.c     | 186 +++++++++++++++++++------
 12 files changed, 1224 insertions(+), 556 deletions(-)

diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
index 100c4221256e..681ba329d52b 100644
--- a/drivers/clk/meson/a1-pll.c
+++ b/drivers/clk/meson/a1-pll.c
@@ -25,6 +25,36 @@
 
 #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table a1_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap a1_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_FIXPLL_CTRL0,
+		.shift  = 10,
+		.width  = 5,
+		.table = a1_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "fixpll_in",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap a1_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -37,11 +67,6 @@ static struct clk_regmap a1_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_FIXPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_FIXPLL_CTRL1,
 			.shift   = 0,
@@ -61,8 +86,8 @@ static struct clk_regmap a1_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "fixpll_in",
+		.parent_hws = (const struct clk_hw *[]) {
+			&a1_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -83,6 +108,24 @@ static struct clk_regmap a1_fixed_pll = {
 	},
 };
 
+static struct clk_regmap a1_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = a1_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "hifipll_in",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range a1_hifi_pll_range = {
 	.min = 32,
 	.max = 64,
@@ -108,11 +151,6 @@ static struct clk_regmap a1_hifi_pll = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -140,10 +178,11 @@ static struct clk_regmap a1_hifi_pll = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "hifipll_in",
+		.parent_hws = (const struct clk_hw *[]) {
+			&a1_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -281,6 +320,7 @@ static struct clk_regmap a1_fclk_div7 = {
 
 /* Array of all clocks registered by this provider */
 static struct clk_hw *a1_pll_hw_clks[] = {
+	[CLKID_FIXED_PREDIV]	= &a1_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	= &a1_fixed_pll_dco.hw,
 	[CLKID_FIXED_PLL]	= &a1_fixed_pll.hw,
 	[CLKID_FCLK_DIV2_DIV]	= &a1_fclk_div2_div.hw,
@@ -291,6 +331,7 @@ static struct clk_hw *a1_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV3]	= &a1_fclk_div3.hw,
 	[CLKID_FCLK_DIV5]	= &a1_fclk_div5.hw,
 	[CLKID_FCLK_DIV7]	= &a1_fclk_div7.hw,
+	[CLKID_HIFI_PREDIV]	= &a1_hifi_prediv.hw,
 	[CLKID_HIFI_PLL]	= &a1_hifi_pll.hw,
 };
 
diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
index 280842e9cb37..e6e6b9a8030c 100644
--- a/drivers/clk/meson/axg.c
+++ b/drivers/clk/meson/axg.c
@@ -107,6 +107,36 @@
 #define HHI_DPLL_TOP_I			0x318
 #define HHI_DPLL_TOP2_I			0x31C
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table axg_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap axg_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap axg_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -119,11 +149,6 @@ static struct clk_regmap axg_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -143,8 +168,8 @@ static struct clk_regmap axg_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -171,6 +196,24 @@ static struct clk_regmap axg_fixed_pll = {
 	},
 };
 
+static struct clk_regmap axg_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap axg_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -183,11 +226,6 @@ static struct clk_regmap axg_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -202,10 +240,11 @@ static struct clk_regmap axg_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -227,39 +266,57 @@ static struct clk_regmap axg_sys_pll = {
 	},
 };
 
-static const struct pll_params_table axg_gp0_pll_params_table[] = {
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(41, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(67, 1),
-	PLL_PARAMS(68, 1),
+static const struct pll_mult_table axg_gp0_pll_params_table[] = {
+	PLL_MULT(40),
+	PLL_MULT(41),
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
+	PLL_MULT(67),
+	PLL_MULT(68),
 	{ /* sentinel */ },
 };
 
+static struct clk_regmap axg_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence axg_gp0_init_regs[] = {
 	{ .reg = HHI_GP0_PLL_CNTL1,	.def = 0xc084b000 },
 	{ .reg = HHI_GP0_PLL_CNTL2,	.def = 0xb75020be },
@@ -280,11 +337,6 @@ static struct clk_regmap axg_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -307,10 +359,11 @@ static struct clk_regmap axg_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -332,6 +385,24 @@ static struct clk_regmap axg_gp0_pll = {
 	},
 };
 
+static struct clk_regmap axg_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HIFI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence axg_hifi_pll_init_regs[] = {
 	{ .reg = HHI_HIFI_PLL_CNTL1,	.def = 0xc084b000 },
 	{ .reg = HHI_HIFI_PLL_CNTL2,	.def = 0xb75020be },
@@ -352,11 +423,6 @@ static struct clk_regmap axg_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HIFI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HIFI_PLL_CNTL5,
 			.shift   = 0,
@@ -380,10 +446,11 @@ static struct clk_regmap axg_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -771,11 +838,35 @@ static struct clk_regmap axg_mpll3 = {
 	},
 };
 
-static const struct pll_params_table axg_pcie_pll_params_table[] = {
-	{
-		.m = 200,
-		.n = 3,
+/*
+ * A pre-divider value of 3 provides the best performance for
+ * the AXG PCIe PLL.
+ */
+static const struct clk_div_table axg_pcie_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap axg_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_PCIE_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_pcie_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
 	},
+};
+
+static const struct pll_mult_table axg_pcie_pll_params_table[] = {
+	PLL_MULT(200),
 	{ /* sentinel */ },
 };
 
@@ -801,11 +892,6 @@ static struct clk_regmap axg_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_PCIE_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_PCIE_PLL_CNTL1,
 			.shift   = 0,
@@ -828,10 +914,11 @@ static struct clk_regmap axg_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2067,10 +2154,15 @@ static struct clk_hw *axg_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]		= &axg_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]		= &axg_gen_clk_div.hw,
 	[CLKID_GEN_CLK]			= &axg_gen_clk.hw,
+	[CLKID_SYS_PREDIV]		= &axg_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &axg_sys_pll_dco.hw,
+	[CLKID_FIXED_PREDIV]		= &axg_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &axg_fixed_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &axg_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &axg_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &axg_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &axg_hifi_pll_dco.hw,
+	[CLKID_PCIE_PREDIV]		= &axg_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &axg_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_OD]		= &axg_pcie_pll_od.hw,
 	[CLKID_VPU_0_DIV]		= &axg_vpu_0_div.hw,
diff --git a/drivers/clk/meson/c3-pll.c b/drivers/clk/meson/c3-pll.c
index dd047d17488c..57bbd0498811 100644
--- a/drivers/clk/meson/c3-pll.c
+++ b/drivers/clk/meson/c3-pll.c
@@ -230,6 +230,36 @@ static struct clk_regmap c3_fclk_div7 = {
 	},
 };
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table c3_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap c3_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_GP0PLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "top",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_gp0_pll_init_regs[] = {
 	{ .reg = ANACTRL_GP0PLL_CTRL2,	.def = 0x0 },
 	{ .reg = ANACTRL_GP0PLL_CTRL3,	.def = 0x48681c00 },
@@ -260,11 +290,6 @@ static struct clk_regmap c3_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 19,
 		},
-		.n = {
-			.reg_off = ANACTRL_GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_GP0PLL_CTRL0,
 			.shift   = 31,
@@ -282,10 +307,11 @@ static struct clk_regmap c3_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "top",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -318,6 +344,24 @@ static struct clk_regmap c3_gp0_pll = {
 	},
 };
 
+static struct clk_regmap c3_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "top",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_hifi_pll_init_regs[] = {
 	{ .reg = ANACTRL_HIFIPLL_CTRL2,	.def = 0x0 },
 	{ .reg = ANACTRL_HIFIPLL_CTRL3,	.def = 0x6a285c00 },
@@ -343,11 +387,6 @@ static struct clk_regmap c3_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 19,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
 			.shift   = 31,
@@ -366,10 +405,11 @@ static struct clk_regmap c3_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "top",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -391,6 +431,24 @@ static struct clk_regmap c3_hifi_pll = {
 	},
 };
 
+static struct clk_regmap c3_mclk_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_MPLL_CTRL0,
+		.shift = 16,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "mclk_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "mclk",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_mclk_pll_init_regs[] = {
 	{ .reg = ANACTRL_MPLL_CTRL1,	.def = 0x1420500f },
 	{ .reg = ANACTRL_MPLL_CTRL2,	.def = 0x00023041 },
@@ -415,11 +473,6 @@ static struct clk_regmap c3_mclk_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_MPLL_CTRL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_MPLL_CTRL0,
 			.shift   = 31,
@@ -437,10 +490,11 @@ static struct clk_regmap c3_mclk_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "mclk_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "mclk",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_mclk_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -636,10 +690,13 @@ static struct clk_hw *c3_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV5]	= &c3_fclk_div5.hw,
 	[CLKID_FCLK_DIV7_DIV]	= &c3_fclk_div7_div.hw,
 	[CLKID_FCLK_DIV7]	= &c3_fclk_div7.hw,
+	[CLKID_GP0_PREDIV]	= &c3_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	= &c3_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]		= &c3_gp0_pll.hw,
+	[CLKID_HIFI_PREDIV]	= &c3_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]	= &c3_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]	= &c3_hifi_pll.hw,
+	[CLKID_MCLK_PREDIV]	= &c3_mclk_prediv.hw,
 	[CLKID_MCLK_PLL_DCO]	= &c3_mclk_pll_dco.hw,
 	[CLKID_MCLK_PLL_OD]	= &c3_mclk_pll_od.hw,
 	[CLKID_MCLK_PLL]	= &c3_mclk_pll.hw,
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index 1ea6579a760f..a7e3f8c8eb78 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -14,7 +14,7 @@
  *        +--------------------------------+
  *        |                                |
  *        |             +--+               |
- *  in >>-----[ /N ]--->|  |      +-----+  |
+ *  in >>----------->|  |      +-----+  |
  *        |             |  |------| DCO |---->> out
  *        |  +--------->|  |      +--v--+  |
  *        |  |          +--+         |     |
@@ -23,7 +23,7 @@
  *        |                                |
  *        +--------------------------------+
  *
- * out = in * (m + frac / frac_max) / n
+ * out = in * (m + frac / frac_max)
  */
 
 #include <linux/clk-provider.h>
@@ -52,8 +52,7 @@ static int __pll_round_closest_mult(struct meson_clk_pll_data *pll)
 }
 
 static unsigned long __pll_params_to_rate(unsigned long parent_rate,
-					  unsigned int m, unsigned int n,
-					  unsigned int frac,
+					  unsigned int m, unsigned int frac,
 					  struct meson_clk_pll_data *pll)
 {
 	u64 rate = (u64)parent_rate * m;
@@ -66,7 +65,7 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate,
 		rate += DIV_ROUND_UP_ULL(frac_rate, frac_max);
 	}
 
-	return DIV_ROUND_UP_ULL(rate, n);
+	return rate;
 }
 
 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
@@ -74,17 +73,7 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int m, n, frac;
-
-	n = meson_parm_read(clk->map, &pll->n);
-
-	/*
-	 * On some HW, N is set to zero on init. This value is invalid as
-	 * it would result in a division by zero. The rate can't be
-	 * calculated in this case
-	 */
-	if (n == 0)
-		return 0;
+	unsigned int m, frac;
 
 	m = meson_parm_read(clk->map, &pll->m);
 
@@ -92,21 +81,20 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 		meson_parm_read(clk->map, &pll->frac) :
 		0;
 
-	return __pll_params_to_rate(parent_rate, m, n, frac, pll);
+	return __pll_params_to_rate(parent_rate, m, frac, pll);
 }
 
 static unsigned int __pll_params_with_frac(unsigned long rate,
 					   unsigned long parent_rate,
 					   unsigned int m,
-					   unsigned int n,
 					   struct meson_clk_pll_data *pll)
 {
 	unsigned int frac_max = pll->frac_max ? pll->frac_max :
 						(1 << pll->frac.width);
-	u64 val = (u64)rate * n;
+	u64 val = (u64)rate;
 
 	/* Bail out if we are already over the requested rate */
-	if (rate < parent_rate * m / n)
+	if (rate < parent_rate * m)
 		return 0;
 
 	if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
@@ -119,128 +107,76 @@ static unsigned int __pll_params_with_frac(unsigned long rate,
 	return min((unsigned int)val, (frac_max - 1));
 }
 
-static bool meson_clk_pll_is_better(unsigned long rate,
-				    unsigned long best,
-				    unsigned long now,
-				    struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_range(unsigned long rate,
+				   unsigned long parent_rate,
+				   unsigned int *m,
+				   struct meson_clk_pll_data *pll)
 {
-	if (__pll_round_closest_mult(pll)) {
-		/* Round Closest */
-		if (abs(now - rate) < abs(best - rate))
-			return true;
-	} else {
-		/* Round down */
-		if (now <= rate && best < now)
-			return true;
+	if (rate <= pll->range->min * parent_rate) {
+		*m = pll->range->min;
+		return -ENODATA;
+	} else if (rate >= pll->range->max * parent_rate) {
+		*m = pll->range->max;
+		return -ENODATA;
 	}
 
-	return false;
-}
+	if (__pll_round_closest_mult(pll))
+		*m = DIV_ROUND_CLOSEST_ULL(rate, parent_rate);
+	else
+		*m = div_u64(rate, parent_rate);
 
-static int meson_clk_get_pll_table_index(unsigned int index,
-					 unsigned int *m,
-					 unsigned int *n,
-					 struct meson_clk_pll_data *pll)
-{
-	if (!pll->table[index].n)
+	/* Multiplier exceeds hardware range */
+	if (*m >= (1 << pll->m.width))
 		return -EINVAL;
 
-	*m = pll->table[index].m;
-	*n = pll->table[index].n;
-
 	return 0;
 }
 
-static unsigned int meson_clk_get_pll_range_m(unsigned long rate,
-					      unsigned long parent_rate,
-					      unsigned int n,
-					      struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_table(unsigned long rate,
+				   unsigned long parent_rate,
+				   unsigned int *m,
+				   struct meson_clk_pll_data *pll)
 {
-	u64 val = (u64)rate * n;
-
-	if (__pll_round_closest_mult(pll))
-		return DIV_ROUND_CLOSEST_ULL(val, parent_rate);
-
-	return div_u64(val,  parent_rate);
-}
-
-static int meson_clk_get_pll_range_index(unsigned long rate,
-					 unsigned long parent_rate,
-					 unsigned int index,
-					 unsigned int *m,
-					 unsigned int *n,
-					 struct meson_clk_pll_data *pll)
-{
-	*n = index + 1;
+	unsigned int i;
+	int ret;
 
-	/* Check the predivider range */
-	if (*n >= (1 << pll->n.width))
-		return -EINVAL;
+	for (i = 0, ret = 0; !ret; i++) {
+		if (!pll->table[i].m)
+			return -EINVAL;
 
-	if (*n == 1) {
-		/* Get the boundaries out the way */
-		if (rate <= pll->range->min * parent_rate) {
-			*m = pll->range->min;
-			return -ENODATA;
-		} else if (rate >= pll->range->max * parent_rate) {
-			*m = pll->range->max;
-			return -ENODATA;
+		if (pll->table[i].m == rate / parent_rate) {
+			*m = pll->table[i].m;
+			return 0;
 		}
 	}
 
-	*m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll);
-
-	/* the pre-divider gives a multiplier too big - stop */
-	if (*m >= (1 << pll->m.width))
-		return -EINVAL;
-
 	return 0;
 }
 
-static int meson_clk_get_pll_get_index(unsigned long rate,
-				       unsigned long parent_rate,
-				       unsigned int index,
-				       unsigned int *m,
-				       unsigned int *n,
-				       struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_multiplier(unsigned long rate,
+					unsigned long parent_rate,
+					unsigned int *m,
+					struct meson_clk_pll_data *pll)
 {
 	if (pll->range)
-		return meson_clk_get_pll_range_index(rate, parent_rate,
-						     index, m, n, pll);
+		return meson_clk_get_pll_range(rate, parent_rate, m, pll);
 	else if (pll->table)
-		return meson_clk_get_pll_table_index(index, m, n, pll);
+		return meson_clk_get_pll_table(rate, parent_rate, m, pll);
 
 	return -EINVAL;
 }
 
-static int meson_clk_get_pll_settings(unsigned long rate,
-				      unsigned long parent_rate,
-				      unsigned int *best_m,
-				      unsigned int *best_n,
-				      struct meson_clk_pll_data *pll)
+static unsigned int meson_clk_get_pll_max_multiplier(struct meson_clk_pll_data *pll)
 {
-	unsigned long best = 0, now = 0;
-	unsigned int i, m, n;
-	int ret;
+	unsigned int i, max_mult = 0;
 
-	for (i = 0, ret = 0; !ret; i++) {
-		ret = meson_clk_get_pll_get_index(rate, parent_rate,
-						  i, &m, &n, pll);
-		if (ret == -EINVAL)
-			break;
-
-		now = __pll_params_to_rate(parent_rate, m, n, 0, pll);
-		if (meson_clk_pll_is_better(rate, best, now, pll)) {
-			best = now;
-			*best_m = m;
-			*best_n = n;
-
-			if (now == rate)
-				break;
-		}
-	}
+	if (pll->range)
+		return pll->range->max;
+
+	for (i = 0; pll->table && pll->table[i].m; i++)
+		max_mult = max(max_mult, pll->table[i].m);
 
-	return best ? 0 : -EINVAL;
+	return max_mult;
 }
 
 static int meson_clk_pll_determine_rate(struct clk_hw *hw,
@@ -248,16 +184,32 @@ static int meson_clk_pll_determine_rate(struct clk_hw *hw,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int m, n, frac;
+	unsigned int max_mult, m, frac;
 	unsigned long round;
 	int ret;
 
-	ret = meson_clk_get_pll_settings(req->rate, req->best_parent_rate,
-					 &m, &n, pll);
-	if (ret)
+	/*
+	 * The pre-divider resets to an invalid zero value and reports a
+	 * zero rate until it gets programmed. Ask the parent for the
+	 * lowest rate which keeps the requested rate reachable; the CCF
+	 * will program the pre-divider when applying the parent rate.
+	 */
+	if (!req->best_parent_rate) {
+		max_mult = meson_clk_get_pll_max_multiplier(pll);
+		if (!max_mult)
+			return -EINVAL;
+
+		req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw,
+							  DIV_ROUND_UP_ULL(req->rate, max_mult));
+		if (!req->best_parent_rate)
+			return -EINVAL;
+	}
+
+	ret = meson_clk_get_pll_multiplier(req->rate, req->best_parent_rate, &m, pll);
+	if (ret && ret != -ENODATA)
 		return ret;
 
-	round = __pll_params_to_rate(req->best_parent_rate, m, n, 0, pll);
+	round = __pll_params_to_rate(req->best_parent_rate, m, 0, pll);
 
 	if (!MESON_PARM_APPLICABLE(&pll->frac) || req->rate == round) {
 		req->rate = round;
@@ -268,8 +220,8 @@ static int meson_clk_pll_determine_rate(struct clk_hw *hw,
 	 * The rate provided by the setting is not an exact match, let's
 	 * try to improve the result using the fractional parameter
 	 */
-	frac = __pll_params_with_frac(req->rate, req->best_parent_rate, m, n, pll);
-	req->rate = __pll_params_to_rate(req->best_parent_rate, m, n, frac, pll);
+	frac = __pll_params_with_frac(req->rate, req->best_parent_rate, m, pll);
+	req->rate = __pll_params_to_rate(req->best_parent_rate, m, frac, pll);
 
 	return 0;
 }
@@ -420,7 +372,7 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int enabled, m, n, frac = 0;
+	unsigned int enabled, m, frac = 0;
 	unsigned long old_rate;
 	int ret;
 
@@ -429,19 +381,18 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 
 	old_rate = clk_hw_get_rate(hw);
 
-	ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll);
-	if (ret)
+	ret = meson_clk_get_pll_multiplier(rate, parent_rate, &m, pll);
+	if (ret && ret != -ENODATA)
 		return ret;
 
 	enabled = meson_parm_read(clk->map, &pll->en);
 	if (enabled)
 		meson_clk_pll_disable(hw);
 
-	meson_parm_write(clk->map, &pll->n, n);
 	meson_parm_write(clk->map, &pll->m, m);
 
 	if (MESON_PARM_APPLICABLE(&pll->frac)) {
-		frac = __pll_params_with_frac(rate, parent_rate, m, n, pll);
+		frac = __pll_params_with_frac(rate, parent_rate, m, pll);
 		meson_parm_write(clk->map, &pll->frac, frac);
 	}
 
diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h
index 949157fb7bf5..cf20deaac1ed 100644
--- a/drivers/clk/meson/clk-pll.h
+++ b/drivers/clk/meson/clk-pll.h
@@ -11,9 +11,8 @@
 #include <linux/regmap.h>
 #include "parm.h"
 
-struct pll_params_table {
-	unsigned int	m;
-	unsigned int	n;
+struct pll_mult_table {
+	unsigned int    m;
 };
 
 struct pll_mult_range {
@@ -21,11 +20,10 @@ struct pll_mult_range {
 	unsigned int	max;
 };
 
-#define PLL_PARAMS(_m, _n)						\
-	{								\
-		.m		= (_m),					\
-		.n		= (_n),					\
-	}
+#define PLL_MULT(_m)			\
+{					\
+	.m = (_m),			\
+}
 
 #define CLK_MESON_PLL_ROUND_CLOSEST	BIT(0)
 #define CLK_MESON_PLL_NOINIT_ENABLED	BIT(1)
@@ -33,7 +31,6 @@ struct pll_mult_range {
 struct meson_clk_pll_data {
 	struct parm en;
 	struct parm m;
-	struct parm n;
 	struct parm frac;
 	struct parm l;
 	struct parm rst;
@@ -41,7 +38,7 @@ struct meson_clk_pll_data {
 	struct parm l_detect;
 	const struct reg_sequence *init_regs;
 	unsigned int init_count;
-	const struct pll_params_table *table;
+	const struct pll_mult_table *table;
 	const struct pll_mult_range *range;
 	unsigned int frac_max;
 	u8 flags;
diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h
index 8e5c39b023e1..ee2a4b3408f0 100644
--- a/drivers/clk/meson/clk-regmap.h
+++ b/drivers/clk/meson/clk-regmap.h
@@ -75,7 +75,7 @@ struct clk_regmap_div_data {
 	unsigned int	offset;
 	u8		shift;
 	u8		width;
-	u8		flags;
+	u16		flags;
 	const struct clk_div_table	*table;
 };
 
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index d0d4c7b6dc82..4be367c89aca 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -137,6 +137,36 @@
 #define HHI_SYS1_PLL_CNTL5		0x394
 #define HHI_SYS1_PLL_CNTL6		0x398
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table g12a_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap g12a_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_FIX_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -149,11 +179,6 @@ static struct clk_regmap g12a_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_FIX_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_FIX_PLL_CNTL1,
 			.shift   = 0,
@@ -173,8 +198,8 @@ static struct clk_regmap g12a_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -206,6 +231,24 @@ static const struct pll_mult_range g12a_sys_pll_mult_range = {
 	.max = 250,
 };
 
+static struct clk_regmap g12a_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -218,11 +261,6 @@ static struct clk_regmap g12a_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL0,
 			.shift   = 31,
@@ -238,12 +276,12 @@ static struct clk_regmap g12a_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_sys_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the CPU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -265,6 +303,24 @@ static struct clk_regmap g12a_sys_pll = {
 	},
 };
 
+static struct clk_regmap g12b_sys1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS1_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12b_sys1_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -277,11 +333,6 @@ static struct clk_regmap g12b_sys1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_SYS1_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS1_PLL_CNTL0,
 			.shift   = 31,
@@ -297,12 +348,12 @@ static struct clk_regmap g12b_sys1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys1_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12b_sys1_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the CPU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -386,6 +437,24 @@ static struct clk_fixed_factor g12b_sys1_pll_div16 = {
 	},
 };
 
+static struct clk_regmap g12a_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range g12a_gp0_pll_mult_range = {
 	.min = 125,
 	.max = 255,
@@ -415,11 +484,6 @@ static struct clk_regmap g12a_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -442,10 +506,11 @@ static struct clk_regmap g12a_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -468,6 +533,24 @@ static struct clk_regmap g12a_gp0_pll = {
 	},
 };
 
+static struct clk_regmap sm1_gp1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP1_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap sm1_gp1_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -480,11 +563,6 @@ static struct clk_regmap sm1_gp1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_GP1_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP1_PLL_CNTL1,
 			.shift   = 0,
@@ -504,12 +582,12 @@ static struct clk_regmap sm1_gp1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp1_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&sm1_gp1_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the DSU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -531,6 +609,24 @@ static struct clk_regmap sm1_gp1_pll = {
 	},
 };
 
+static struct clk_regmap g12a_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HIFI_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Internal hifi pll emulation configuration parameters
  */
@@ -555,11 +651,6 @@ static struct clk_regmap g12a_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_HIFI_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HIFI_PLL_CNTL1,
 			.shift   = 0,
@@ -583,10 +674,11 @@ static struct clk_regmap g12a_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -609,6 +701,24 @@ static struct clk_regmap g12a_hifi_pll = {
 	},
 };
 
+static struct clk_regmap g12a_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_PCIE_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * The Meson G12A PCIE PLL is fined tuned to deliver a very precise
  * 100MHz reference clock for the PCIe Analog PHY, and thus requires
@@ -630,9 +740,9 @@ static const struct reg_sequence g12a_pcie_pll_init_regs[] = {
 };
 
 /* Keep a single entry table for recalc/round_rate() ops */
-static const struct pll_params_table g12a_pcie_pll_table[] = {
-	PLL_PARAMS(150, 1),
-	{0, 0},
+static const struct pll_mult_table g12a_pcie_pll_table[] = {
+	PLL_MULT(150),
+	{ /* sentinel */ },
 };
 
 static struct clk_regmap g12a_pcie_pll_dco = {
@@ -647,11 +757,6 @@ static struct clk_regmap g12a_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_PCIE_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_PCIE_PLL_CNTL1,
 			.shift   = 0,
@@ -674,10 +779,11 @@ static struct clk_regmap g12a_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pcie_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -729,6 +835,24 @@ static struct clk_fixed_factor g12a_pcie_pll = {
 	},
 };
 
+static struct clk_regmap g12a_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -741,11 +865,6 @@ static struct clk_regmap g12a_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HDMI_PLL_CNTL1,
 			.shift   = 0,
@@ -765,15 +884,15 @@ static struct clk_regmap g12a_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
 	},
 };
 
@@ -4593,9 +4712,13 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -4618,6 +4741,7 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -4693,6 +4817,7 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -4820,9 +4945,13 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -4845,6 +4974,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -4920,6 +5050,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -4935,6 +5066,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VDEC_HEVCF]		= &g12a_vdec_hevcf.hw,
 	[CLKID_TS_DIV]			= &g12a_ts_div.hw,
 	[CLKID_TS]			= &g12a_ts.hw,
+	[CLKID_SYS1_PREDIV]		= &g12b_sys1_prediv.hw,
 	[CLKID_SYS1_PLL_DCO]		= &g12b_sys1_pll_dco.hw,
 	[CLKID_SYS1_PLL]		= &g12b_sys1_pll.hw,
 	[CLKID_SYS1_PLL_DIV16_EN]	= &g12b_sys1_pll_div16_en.hw,
@@ -5088,9 +5220,13 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -5113,6 +5249,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -5188,6 +5325,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -5204,6 +5342,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_TS_DIV]			= &g12a_ts_div.hw,
 	[CLKID_TS]			= &g12a_ts.hw,
 	[CLKID_GP1_PLL_DCO]		= &sm1_gp1_pll_dco.hw,
+	[CLKID_GP1_PREDIV]		= &sm1_gp1_prediv.hw,
 	[CLKID_GP1_PLL]			= &sm1_gp1_pll.hw,
 	[CLKID_DSU_CLK_DYN0_SEL]	= &sm1_dsu_clk_dyn0_sel.hw,
 	[CLKID_DSU_CLK_DYN0_DIV]	= &sm1_dsu_clk_dyn0_div.hw,
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 7a7b104182ff..9d09f13c22fb 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -115,6 +115,35 @@
 #define HHI_BT656_CLK_CNTL		0x3d4
 #define HHI_SAR_CLK_CNTL		0x3d8
 
+static const struct clk_div_table gxbb_fixed_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap gxbb_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_fixed_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -127,11 +156,6 @@ static struct clk_regmap gxbb_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -151,8 +175,8 @@ static struct clk_regmap gxbb_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -192,6 +216,30 @@ static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = {
 	},
 };
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table gxbb_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap gxbb_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_hdmi_pll_pre_mult.hw
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -204,11 +252,6 @@ static struct clk_regmap gxbb_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HDMI_PLL_CNTL2,
 			.shift   = 0,
@@ -229,14 +272,32 @@ static struct clk_regmap gxbb_hdmi_pll_dco = {
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
 		.parent_hws = (const struct clk_hw *[]) {
-			&gxbb_hdmi_pll_pre_mult.hw
+			&gxbb_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap gxl_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
 	},
 };
 
@@ -252,11 +313,6 @@ static struct clk_regmap gxl_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		/*
 		 * On gxl, there is a register shift due to
 		 * HHI_HDMI_PLL_CNTL1 which does not exist on gxbb,
@@ -282,15 +338,15 @@ static struct clk_regmap gxl_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxl_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -413,6 +469,24 @@ static struct clk_regmap gxl_hdmi_pll = {
 	},
 };
 
+static struct clk_regmap gxbb_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -425,11 +499,6 @@ static struct clk_regmap gxbb_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -444,10 +513,11 @@ static struct clk_regmap gxbb_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -469,38 +539,56 @@ static struct clk_regmap gxbb_sys_pll = {
 	},
 };
 
-static const struct pll_params_table gxbb_gp0_pll_params_table[] = {
-	PLL_PARAMS(32, 1),
-	PLL_PARAMS(33, 1),
-	PLL_PARAMS(34, 1),
-	PLL_PARAMS(35, 1),
-	PLL_PARAMS(36, 1),
-	PLL_PARAMS(37, 1),
-	PLL_PARAMS(38, 1),
-	PLL_PARAMS(39, 1),
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(41, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
+static struct clk_regmap gxbb_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
+static const struct pll_mult_table gxbb_gp0_pll_params_table[] = {
+	PLL_MULT(32),
+	PLL_MULT(33),
+	PLL_MULT(34),
+	PLL_MULT(35),
+	PLL_MULT(36),
+	PLL_MULT(37),
+	PLL_MULT(38),
+	PLL_MULT(39),
+	PLL_MULT(40),
+	PLL_MULT(41),
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
 	{ /* sentinel */ },
 };
 
@@ -522,11 +610,6 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_GP0_PLL_CNTL,
 			.shift   = 31,
@@ -544,6 +627,25 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_gp0_prediv.hw
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap gxl_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
 		.parent_data = &(const struct clk_parent_data) {
 			.fw_name = "xtal",
 		},
@@ -551,32 +653,32 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 	},
 };
 
-static const struct pll_params_table gxl_gp0_pll_params_table[] = {
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
+static const struct pll_mult_table gxl_gp0_pll_params_table[] = {
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
 	{ /* sentinel */ },
 };
 
@@ -600,11 +702,6 @@ static struct clk_regmap gxl_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -627,10 +724,11 @@ static struct clk_regmap gxl_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxl_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -3006,11 +3104,15 @@ static struct clk_hw *gxbb_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]	    = &gxbb_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]	    = &gxbb_gen_clk_div.hw,
 	[CLKID_GEN_CLK]		    = &gxbb_gen_clk.hw,
+	[CLKID_FIXED_PREDIV]	    = &gxbb_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	    = &gxbb_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &gxbb_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &gxbb_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]	    = &gxbb_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]	    = &gxbb_hdmi_pll_od2.hw,
+	[CLKID_SYS_PREDIV]	    = &gxbb_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]	    = &gxbb_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]	    = &gxbb_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	    = &gxbb_gp0_pll_dco.hw,
 	[CLKID_VID_PLL_DIV]	    = &gxbb_vid_pll_div.hw,
 	[CLKID_VID_PLL_SEL]	    = &gxbb_vid_pll_sel.hw,
@@ -3213,11 +3315,15 @@ static struct clk_hw *gxl_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]	    = &gxbb_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]	    = &gxbb_gen_clk_div.hw,
 	[CLKID_GEN_CLK]		    = &gxbb_gen_clk.hw,
+	[CLKID_FIXED_PREDIV]	    = &gxbb_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	    = &gxbb_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &gxl_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &gxl_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]	    = &gxl_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]	    = &gxl_hdmi_pll_od2.hw,
+	[CLKID_SYS_PREDIV]	    = &gxbb_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]	    = &gxbb_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]	    = &gxl_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	    = &gxl_gp0_pll_dco.hw,
 	[CLKID_VID_PLL_DIV]	    = &gxbb_vid_pll_div.hw,
 	[CLKID_VID_PLL_SEL]	    = &gxbb_vid_pll_sel.hw,
diff --git a/drivers/clk/meson/meson8-ddr.c b/drivers/clk/meson/meson8-ddr.c
index 0f93774f7371..a3f4c47982d9 100644
--- a/drivers/clk/meson/meson8-ddr.c
+++ b/drivers/clk/meson/meson8-ddr.c
@@ -23,6 +23,35 @@
 #define DDR_CLK_CNTL			0x18
 #define DDR_CLK_STS			0x1c
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table meson8_ddr_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8_ddr_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = AM_DDR_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8_ddr_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "ddr_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8_ddr_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -35,11 +64,6 @@ static struct clk_regmap meson8_ddr_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = AM_DDR_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = AM_DDR_PLL_CNTL,
 			.shift   = 31,
@@ -54,8 +78,8 @@ static struct clk_regmap meson8_ddr_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "ddr_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8_ddr_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -79,6 +103,7 @@ static struct clk_regmap meson8_ddr_pll = {
 };
 
 static struct clk_hw *meson8_ddr_hw_clks[] = {
+	[DDR_CLKID_DDR_PREDIV]		= &meson8_ddr_prediv.hw,
 	[DDR_CLKID_DDR_PLL_DCO]		= &meson8_ddr_pll_dco.hw,
 	[DDR_CLKID_DDR_PLL]		= &meson8_ddr_pll.hw,
 };
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index bc029c6ab622..0c9ba079bc51 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -95,30 +95,61 @@ struct meson8b_clk_reset {
 	struct regmap *regmap;
 };
 
-static const struct pll_params_table sys_pll_params_table[] = {
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(67, 1),
-	PLL_PARAMS(68, 1),
-	PLL_PARAMS(84, 1),
+static const struct pll_mult_table sys_pll_params_table[] = {
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
+	PLL_MULT(67),
+	PLL_MULT(68),
+	PLL_MULT(84),
 	{ /* sentinel */ },
 };
 
+static const struct clk_div_table meson8b_fixed_prediv_div_table[] = {
+	{ .val = 4, .div = 4 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8b_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_fixed_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8b_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -131,11 +162,6 @@ static struct clk_regmap meson8b_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -155,10 +181,8 @@ static struct clk_regmap meson8b_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -185,15 +209,38 @@ static struct clk_regmap meson8b_fixed_pll = {
 	},
 };
 
+static const struct clk_div_table meson8b_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8b_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_VID_PLL_CNTL,
+		.shift   = 10,
+		.width   = 5,
+		.table = meson8b_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_fixed_factor hdmi_pll_dco_in = {
 	.mult = 2,
 	.div = 1,
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco_in",
 		.ops = &clk_fixed_factor_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -214,24 +261,24 @@ static const struct reg_sequence meson8b_hdmi_pll_init_regs[] = {
 	{ .reg = HHI_VID2_PLL_CNTL2,	.def = 0x0430a800 },
 };
 
-static const struct pll_params_table meson8b_hdmi_pll_params_table[] = {
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(68, 1),
-	PLL_PARAMS(71, 1),
-	PLL_PARAMS(82, 1),
+static const struct pll_mult_table meson8b_hdmi_pll_params_table[] = {
+	PLL_MULT(40),
+	PLL_MULT(42),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(49),
+	PLL_MULT(52),
+	PLL_MULT(54),
+	PLL_MULT(56),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(64),
+	PLL_MULT(66),
+	PLL_MULT(68),
+	PLL_MULT(71),
+	PLL_MULT(82),
 	{ /* sentinel */ }
 };
 
@@ -247,11 +294,6 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_VID_PLL_CNTL,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_VID_PLL_CNTL2,
 			.shift   = 0,
@@ -279,6 +321,7 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
 			&hdmi_pll_dco_in.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -318,6 +361,26 @@ static struct clk_regmap meson8b_hdmi_pll_hdmi_out = {
 	},
 };
 
+static struct clk_regmap meson8b_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8b_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -330,11 +393,6 @@ static struct clk_regmap meson8b_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -350,12 +408,11 @@ static struct clk_regmap meson8b_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2035,6 +2092,31 @@ static struct clk_regmap meson8b_mali = {
 	},
 };
 
+static const struct clk_div_table meson8b_gp_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8m2_gp_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_gp_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence meson8m2_gp_pll_init_regs[] = {
 	{ .reg = HHI_GP_PLL_CNTL2,	.def = 0x59c88000 },
 	{ .reg = HHI_GP_PLL_CNTL3,	.def = 0xca463823 },
@@ -2042,8 +2124,8 @@ static const struct reg_sequence meson8m2_gp_pll_init_regs[] = {
 	{ .reg = HHI_GP_PLL_CNTL5,	.def = 0x00003000 },
 };
 
-static const struct pll_params_table meson8m2_gp_pll_params_table[] = {
-	PLL_PARAMS(182, 3),
+static const struct pll_mult_table meson8m2_gp_pll_params_table[] = {
+	PLL_MULT(182),
 	{ /* sentinel */ },
 };
 
@@ -2059,11 +2141,6 @@ static struct clk_regmap meson8m2_gp_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_GP_PLL_CNTL,
 			.shift   = 31,
@@ -2081,12 +2158,11 @@ static struct clk_regmap meson8m2_gp_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8m2_gp_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2920,8 +2996,11 @@ static struct clk_hw *meson8_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3124,8 +3203,11 @@ static struct clk_hw *meson8b_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3339,8 +3421,11 @@ static struct clk_hw *meson8m2_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3410,6 +3495,7 @@ static struct clk_hw *meson8m2_hw_clks[] = {
 	[CLKID_MALI_1_DIV]	    = &meson8b_mali_1_div.hw,
 	[CLKID_MALI_1]		    = &meson8b_mali_1.hw,
 	[CLKID_MALI]		    = &meson8b_mali.hw,
+	[CLKID_GP_PREDIV]	    = &meson8m2_gp_prediv.hw,
 	[CLKID_GP_PLL_DCO]	    = &meson8m2_gp_pll_dco.hw,
 	[CLKID_GP_PLL]		    = &meson8m2_gp_pll.hw,
 	[CLKID_VPU_0_SEL]	    = &meson8m2_vpu_0_sel.hw,
diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c
index 56ce6f566e53..6caa95887971 100644
--- a/drivers/clk/meson/s4-pll.c
+++ b/drivers/clk/meson/s4-pll.c
@@ -44,6 +44,12 @@
 #define ANACTRL_MPLL_CTRL8                         0x1a0
 #define ANACTRL_HDMIPLL_CTRL0                      0x1c0
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table s4_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
 /*
  * These clock are a fixed value (fixed_pll is 2GHz) that is initialized by ROMcode.
  * The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable
@@ -51,6 +57,30 @@
  * Meanwhile, these clock won't ever change at runtime.
  * For the above reasons, we can only use ro_ops for fixed PLL related clocks.
  */
+static struct clk_regmap s4_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_FIXPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap s4_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -68,11 +98,6 @@ static struct clk_regmap s4_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 17,
 		},
-		.n = {
-			.reg_off = ANACTRL_FIXPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_FIXPLL_CTRL0,
 			.shift   = 31,
@@ -87,10 +112,11 @@ static struct clk_regmap s4_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_fixed_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -273,6 +299,24 @@ static struct clk_regmap s4_fclk_div2p5 = {
 	},
 };
 
+static struct clk_regmap s4_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_GP0PLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range s4_gp0_pll_mult_range = {
 	.min = 125,
 	.max = 250,
@@ -302,11 +346,6 @@ static struct clk_regmap s4_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_GP0PLL_CTRL0,
 			.shift   = 31,
@@ -324,10 +363,11 @@ static struct clk_regmap s4_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -350,6 +390,24 @@ static struct clk_regmap s4_gp0_pll = {
 	},
 };
 
+static struct clk_regmap s4_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Internal hifi pll emulation configuration parameters
  */
@@ -373,11 +431,6 @@ static struct clk_regmap s4_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -402,10 +455,11 @@ static struct clk_regmap s4_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -428,6 +482,24 @@ static struct clk_regmap s4_hifi_pll = {
 	},
 };
 
+static struct clk_regmap s4_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HDMIPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap s4_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -440,11 +512,6 @@ static struct clk_regmap s4_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HDMIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
 			.shift   = 31,
@@ -460,10 +527,11 @@ static struct clk_regmap s4_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_hdmi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -760,6 +828,7 @@ static struct clk_regmap s4_mpll3 = {
 
 /* Array of all clocks provided by this provider */
 static struct clk_hw *s4_pll_hw_clks[] = {
+	[CLKID_FIXED_PREDIV]		= &s4_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &s4_fixed_pll_dco.hw,
 	[CLKID_FIXED_PLL]		= &s4_fixed_pll.hw,
 	[CLKID_FCLK_DIV2_DIV]		= &s4_fclk_div2_div.hw,
@@ -774,10 +843,13 @@ static struct clk_hw *s4_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV7]		= &s4_fclk_div7.hw,
 	[CLKID_FCLK_DIV2P5_DIV]		= &s4_fclk_div2p5_div.hw,
 	[CLKID_FCLK_DIV2P5]		= &s4_fclk_div2p5.hw,
+	[CLKID_GP0_PREDIV]		= &s4_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &s4_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]			= &s4_gp0_pll.hw,
+	[CLKID_HIFI_PREDIV]		= &s4_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &s4_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]		= &s4_hifi_pll.hw,
+	[CLKID_HDMI_PREDIV]		= &s4_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &s4_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &s4_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL]		= &s4_hdmi_pll.hw,
diff --git a/drivers/clk/meson/t7-pll.c b/drivers/clk/meson/t7-pll.c
index 0a622f45fa36..28d563fcb1bd 100644
--- a/drivers/clk/meson/t7-pll.c
+++ b/drivers/clk/meson/t7-pll.c
@@ -71,6 +71,36 @@
 #define MCLK_PLL_CNTL4		0x10
 #define MCLK_PLL_STS		0x14
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table t7_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap t7_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = GP0PLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range t7_media_pll_mult_range = {
 	.min = 125,
 	.max = 250,
@@ -97,11 +127,6 @@ static struct clk_regmap t7_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = GP0PLL_STS,
 			.shift   = 31,
@@ -119,10 +144,11 @@ static struct clk_regmap t7_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -144,6 +170,24 @@ static struct clk_regmap t7_gp0_pll = {
 	},
 };
 
+static struct clk_regmap t7_gp1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = GP1PLL_CTRL0,
+		.shift = 16,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Compared with GP0 PLL, GP1 PLL is a newly designed PLL with
  * a DCO range of 1.6GHz to 3.2GHz.
@@ -171,11 +215,6 @@ static struct clk_regmap t7_gp1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = GP1PLL_CTRL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = GP1PLL_STS,
 			.shift   = 31,
@@ -193,10 +232,11 @@ static struct clk_regmap t7_gp1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp1_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_gp1_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -218,6 +258,24 @@ static struct clk_regmap t7_gp1_pll = {
 	},
 };
 
+static struct clk_regmap t7_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence t7_hifi_init_regs[] = {
 	{ .reg = HIFIPLL_CTRL1, .def = 0x00000000 },
 	{ .reg = HIFIPLL_CTRL2, .def = 0x00000000 },
@@ -239,11 +297,6 @@ static struct clk_regmap t7_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -267,10 +320,11 @@ static struct clk_regmap t7_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -292,6 +346,24 @@ static struct clk_regmap t7_hifi_pll = {
 	},
 };
 
+static struct clk_regmap t7_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = PCIEPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * The T7 PCIE PLL is fined tuned to deliver a very precise
  * 100MHz reference clock for the PCIe Analog PHY, and thus requires
@@ -324,11 +396,6 @@ static struct clk_regmap t7_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = PCIEPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = PCIEPLL_CTRL0,
 			.shift   = 31,
@@ -345,10 +412,11 @@ static struct clk_regmap t7_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pcie_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -624,6 +692,24 @@ static struct clk_regmap t7_mpll3 = {
 	},
 };
 
+static struct clk_regmap t7_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HDMIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence t7_hdmi_init_regs[] = {
 	{ .reg = HDMIPLL_CTRL1, .def = 0x00000000 },
 	{ .reg = HDMIPLL_CTRL2, .def = 0x00000000 },
@@ -645,11 +731,6 @@ static struct clk_regmap t7_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HDMIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HDMIPLL_CTRL0,
 			.shift   = 31,
@@ -667,10 +748,11 @@ static struct clk_regmap t7_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "in0", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_hdmi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -722,6 +804,24 @@ static const struct reg_sequence t7_mclk_init_regs[] = {
 	{ .reg = MCLK_PLL_CNTL4, .def = 0x00180303 },
 };
 
+static struct clk_regmap t7_mclk_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = MCLK_PLL_CNTL0,
+		.shift = 16,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "mclk_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap t7_mclk_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -734,11 +834,6 @@ static struct clk_regmap t7_mclk_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = MCLK_PLL_CNTL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = MCLK_PLL_CNTL0,
 			.shift   = 31,
@@ -761,10 +856,11 @@ static struct clk_regmap t7_mclk_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "mclk_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_mclk_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -939,21 +1035,25 @@ static struct clk_regmap t7_mclk_1 = {
 };
 
 static struct clk_hw *t7_gp0_hw_clks[] = {
+	[CLKID_GP0_PREDIV]		= &t7_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &t7_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]			= &t7_gp0_pll.hw,
 };
 
 static struct clk_hw *t7_gp1_hw_clks[] = {
+	[CLKID_GP1_PREDIV]		= &t7_gp1_prediv.hw,
 	[CLKID_GP1_PLL_DCO]		= &t7_gp1_pll_dco.hw,
 	[CLKID_GP1_PLL]			= &t7_gp1_pll.hw,
 };
 
 static struct clk_hw *t7_hifi_hw_clks[] = {
+	[CLKID_HIFI_PREDIV]		= &t7_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &t7_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]		= &t7_hifi_pll.hw,
 };
 
 static struct clk_hw *t7_pcie_hw_clks[] = {
+	[CLKID_PCIE_PREDIV]		= &t7_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &t7_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &t7_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &t7_pcie_pll_od.hw,
@@ -973,12 +1073,14 @@ static struct clk_hw *t7_mpll_hw_clks[] = {
 };
 
 static struct clk_hw *t7_hdmi_hw_clks[] = {
+	[CLKID_HDMI_PREDIV]		= &t7_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &t7_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &t7_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL]		= &t7_hdmi_pll.hw,
 };
 
 static struct clk_hw *t7_mclk_hw_clks[] = {
+	[CLKID_MCLK_PREDIV]		= &t7_mclk_prediv.hw,
 	[CLKID_MCLK_PLL_DCO]		= &t7_mclk_pll_dco.hw,
 	[CLKID_MCLK_PRE]		= &t7_mclk_pre_od.hw,
 	[CLKID_MCLK_PLL]		= &t7_mclk_pll.hw,

-- 
2.47.1



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

* [PATCH RFC RFT v2 3/3] clk: meson: pll: Remove the dedicated n parameter
@ 2026-10-29  9:30   ` Jian Hu via B4 Relay
  0 siblings, 0 replies; 16+ messages in thread
From: Jian Hu via B4 Relay @ 2026-10-29  9:30 UTC (permalink / raw)
  To: Neil Armstrong, Jerome Brunet, Stephen Boyd, Brian Masney,
	Kevin Hilman, Martin Blumenstingl, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-amlogic, linux-clk, linux-arm-kernel, linux-kernel,
	devicetree, Jian Hu

From: Jian Hu <jian.hu@amlogic.com>

The Meson PLL framework models the PLL pre-divider (N) using a dedicated
field in struct meson_clk_pll_data. Since the common clock framework
already provides a generic divider implementation, there is no need to
keep PLL-specific support for it.

The pre-divider is not intended to be dynamically adjusted. According to
the hardware design, each PLL has a single fixed N value, defined together
with the rest of the PLL. N = 1 for most PLLs, and N = 3 for a few special
cases (the axg PCIe PLL, gxbb Fixed PLL and the meson8b GP0 PLL). The PLL
input frequency constraints are respected by this fixed value.

Remove the dedicated n parameter from the framework and simplify the PLL
rate calculation accordingly. The pre-divider will be represented as a
separate divider clock by platform drivers where needed.

Replace the dedicated PLL pre-divider with a standalone divider clock
for all amlogic SoCs. The PLL DCO now takes the pre-divider clock
as its parent instead of the input clock directly.

Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
 drivers/clk/meson/a1-pll.c     |  69 ++++++++--
 drivers/clk/meson/axg.c        | 230 +++++++++++++++++++++----------
 drivers/clk/meson/c3-pll.c     |  99 +++++++++++---
 drivers/clk/meson/clk-pll.c    | 203 +++++++++++----------------
 drivers/clk/meson/clk-pll.h    |  17 +--
 drivers/clk/meson/clk-regmap.h |   2 +-
 drivers/clk/meson/g12a.c       | 265 ++++++++++++++++++++++++++---------
 drivers/clk/meson/gxbb.c       | 304 +++++++++++++++++++++++++++--------------
 drivers/clk/meson/meson8-ddr.c |  39 +++++-
 drivers/clk/meson/meson8b.c    | 238 +++++++++++++++++++++-----------
 drivers/clk/meson/s4-pll.c     | 128 +++++++++++++----
 drivers/clk/meson/t7-pll.c     | 186 +++++++++++++++++++------
 12 files changed, 1224 insertions(+), 556 deletions(-)

diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
index 100c4221256e..681ba329d52b 100644
--- a/drivers/clk/meson/a1-pll.c
+++ b/drivers/clk/meson/a1-pll.c
@@ -25,6 +25,36 @@
 
 #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table a1_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap a1_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_FIXPLL_CTRL0,
+		.shift  = 10,
+		.width  = 5,
+		.table = a1_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "fixpll_in",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap a1_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -37,11 +67,6 @@ static struct clk_regmap a1_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_FIXPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_FIXPLL_CTRL1,
 			.shift   = 0,
@@ -61,8 +86,8 @@ static struct clk_regmap a1_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "fixpll_in",
+		.parent_hws = (const struct clk_hw *[]) {
+			&a1_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -83,6 +108,24 @@ static struct clk_regmap a1_fixed_pll = {
 	},
 };
 
+static struct clk_regmap a1_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = a1_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "hifipll_in",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range a1_hifi_pll_range = {
 	.min = 32,
 	.max = 64,
@@ -108,11 +151,6 @@ static struct clk_regmap a1_hifi_pll = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -140,10 +178,11 @@ static struct clk_regmap a1_hifi_pll = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "hifipll_in",
+		.parent_hws = (const struct clk_hw *[]) {
+			&a1_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -281,6 +320,7 @@ static struct clk_regmap a1_fclk_div7 = {
 
 /* Array of all clocks registered by this provider */
 static struct clk_hw *a1_pll_hw_clks[] = {
+	[CLKID_FIXED_PREDIV]	= &a1_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	= &a1_fixed_pll_dco.hw,
 	[CLKID_FIXED_PLL]	= &a1_fixed_pll.hw,
 	[CLKID_FCLK_DIV2_DIV]	= &a1_fclk_div2_div.hw,
@@ -291,6 +331,7 @@ static struct clk_hw *a1_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV3]	= &a1_fclk_div3.hw,
 	[CLKID_FCLK_DIV5]	= &a1_fclk_div5.hw,
 	[CLKID_FCLK_DIV7]	= &a1_fclk_div7.hw,
+	[CLKID_HIFI_PREDIV]	= &a1_hifi_prediv.hw,
 	[CLKID_HIFI_PLL]	= &a1_hifi_pll.hw,
 };
 
diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
index 280842e9cb37..e6e6b9a8030c 100644
--- a/drivers/clk/meson/axg.c
+++ b/drivers/clk/meson/axg.c
@@ -107,6 +107,36 @@
 #define HHI_DPLL_TOP_I			0x318
 #define HHI_DPLL_TOP2_I			0x31C
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table axg_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap axg_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap axg_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -119,11 +149,6 @@ static struct clk_regmap axg_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -143,8 +168,8 @@ static struct clk_regmap axg_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -171,6 +196,24 @@ static struct clk_regmap axg_fixed_pll = {
 	},
 };
 
+static struct clk_regmap axg_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap axg_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -183,11 +226,6 @@ static struct clk_regmap axg_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -202,10 +240,11 @@ static struct clk_regmap axg_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -227,39 +266,57 @@ static struct clk_regmap axg_sys_pll = {
 	},
 };
 
-static const struct pll_params_table axg_gp0_pll_params_table[] = {
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(41, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(67, 1),
-	PLL_PARAMS(68, 1),
+static const struct pll_mult_table axg_gp0_pll_params_table[] = {
+	PLL_MULT(40),
+	PLL_MULT(41),
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
+	PLL_MULT(67),
+	PLL_MULT(68),
 	{ /* sentinel */ },
 };
 
+static struct clk_regmap axg_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence axg_gp0_init_regs[] = {
 	{ .reg = HHI_GP0_PLL_CNTL1,	.def = 0xc084b000 },
 	{ .reg = HHI_GP0_PLL_CNTL2,	.def = 0xb75020be },
@@ -280,11 +337,6 @@ static struct clk_regmap axg_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -307,10 +359,11 @@ static struct clk_regmap axg_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -332,6 +385,24 @@ static struct clk_regmap axg_gp0_pll = {
 	},
 };
 
+static struct clk_regmap axg_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HIFI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence axg_hifi_pll_init_regs[] = {
 	{ .reg = HHI_HIFI_PLL_CNTL1,	.def = 0xc084b000 },
 	{ .reg = HHI_HIFI_PLL_CNTL2,	.def = 0xb75020be },
@@ -352,11 +423,6 @@ static struct clk_regmap axg_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HIFI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HIFI_PLL_CNTL5,
 			.shift   = 0,
@@ -380,10 +446,11 @@ static struct clk_regmap axg_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -771,11 +838,35 @@ static struct clk_regmap axg_mpll3 = {
 	},
 };
 
-static const struct pll_params_table axg_pcie_pll_params_table[] = {
-	{
-		.m = 200,
-		.n = 3,
+/*
+ * A pre-divider value of 3 provides the best performance for
+ * the AXG PCIe PLL.
+ */
+static const struct clk_div_table axg_pcie_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap axg_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_PCIE_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = axg_pcie_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
 	},
+};
+
+static const struct pll_mult_table axg_pcie_pll_params_table[] = {
+	PLL_MULT(200),
 	{ /* sentinel */ },
 };
 
@@ -801,11 +892,6 @@ static struct clk_regmap axg_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_PCIE_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_PCIE_PLL_CNTL1,
 			.shift   = 0,
@@ -828,10 +914,11 @@ static struct clk_regmap axg_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&axg_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2067,10 +2154,15 @@ static struct clk_hw *axg_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]		= &axg_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]		= &axg_gen_clk_div.hw,
 	[CLKID_GEN_CLK]			= &axg_gen_clk.hw,
+	[CLKID_SYS_PREDIV]		= &axg_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &axg_sys_pll_dco.hw,
+	[CLKID_FIXED_PREDIV]		= &axg_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &axg_fixed_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &axg_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &axg_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &axg_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &axg_hifi_pll_dco.hw,
+	[CLKID_PCIE_PREDIV]		= &axg_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &axg_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_OD]		= &axg_pcie_pll_od.hw,
 	[CLKID_VPU_0_DIV]		= &axg_vpu_0_div.hw,
diff --git a/drivers/clk/meson/c3-pll.c b/drivers/clk/meson/c3-pll.c
index dd047d17488c..57bbd0498811 100644
--- a/drivers/clk/meson/c3-pll.c
+++ b/drivers/clk/meson/c3-pll.c
@@ -230,6 +230,36 @@ static struct clk_regmap c3_fclk_div7 = {
 	},
 };
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table c3_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap c3_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_GP0PLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "top",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_gp0_pll_init_regs[] = {
 	{ .reg = ANACTRL_GP0PLL_CTRL2,	.def = 0x0 },
 	{ .reg = ANACTRL_GP0PLL_CTRL3,	.def = 0x48681c00 },
@@ -260,11 +290,6 @@ static struct clk_regmap c3_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 19,
 		},
-		.n = {
-			.reg_off = ANACTRL_GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_GP0PLL_CTRL0,
 			.shift   = 31,
@@ -282,10 +307,11 @@ static struct clk_regmap c3_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "top",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -318,6 +344,24 @@ static struct clk_regmap c3_gp0_pll = {
 	},
 };
 
+static struct clk_regmap c3_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "top",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_hifi_pll_init_regs[] = {
 	{ .reg = ANACTRL_HIFIPLL_CTRL2,	.def = 0x0 },
 	{ .reg = ANACTRL_HIFIPLL_CTRL3,	.def = 0x6a285c00 },
@@ -343,11 +387,6 @@ static struct clk_regmap c3_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 19,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
 			.shift   = 31,
@@ -366,10 +405,11 @@ static struct clk_regmap c3_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "top",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -391,6 +431,24 @@ static struct clk_regmap c3_hifi_pll = {
 	},
 };
 
+static struct clk_regmap c3_mclk_prediv = {
+	.data = &(struct clk_regmap_div_data) {
+		.offset = ANACTRL_MPLL_CTRL0,
+		.shift = 16,
+		.width = 5,
+		.table = c3_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "mclk_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "mclk",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence c3_mclk_pll_init_regs[] = {
 	{ .reg = ANACTRL_MPLL_CTRL1,	.def = 0x1420500f },
 	{ .reg = ANACTRL_MPLL_CTRL2,	.def = 0x00023041 },
@@ -415,11 +473,6 @@ static struct clk_regmap c3_mclk_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_MPLL_CTRL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_MPLL_CTRL0,
 			.shift   = 31,
@@ -437,10 +490,11 @@ static struct clk_regmap c3_mclk_pll_dco = {
 	.hw.init = &(struct clk_init_data) {
 		.name = "mclk_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "mclk",
+		.parent_hws = (const struct clk_hw *[]) {
+			&c3_mclk_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -636,10 +690,13 @@ static struct clk_hw *c3_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV5]	= &c3_fclk_div5.hw,
 	[CLKID_FCLK_DIV7_DIV]	= &c3_fclk_div7_div.hw,
 	[CLKID_FCLK_DIV7]	= &c3_fclk_div7.hw,
+	[CLKID_GP0_PREDIV]	= &c3_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	= &c3_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]		= &c3_gp0_pll.hw,
+	[CLKID_HIFI_PREDIV]	= &c3_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]	= &c3_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]	= &c3_hifi_pll.hw,
+	[CLKID_MCLK_PREDIV]	= &c3_mclk_prediv.hw,
 	[CLKID_MCLK_PLL_DCO]	= &c3_mclk_pll_dco.hw,
 	[CLKID_MCLK_PLL_OD]	= &c3_mclk_pll_od.hw,
 	[CLKID_MCLK_PLL]	= &c3_mclk_pll.hw,
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
index 1ea6579a760f..a7e3f8c8eb78 100644
--- a/drivers/clk/meson/clk-pll.c
+++ b/drivers/clk/meson/clk-pll.c
@@ -14,7 +14,7 @@
  *        +--------------------------------+
  *        |                                |
  *        |             +--+               |
- *  in >>-----[ /N ]--->|  |      +-----+  |
+ *  in >>----------->|  |      +-----+  |
  *        |             |  |------| DCO |---->> out
  *        |  +--------->|  |      +--v--+  |
  *        |  |          +--+         |     |
@@ -23,7 +23,7 @@
  *        |                                |
  *        +--------------------------------+
  *
- * out = in * (m + frac / frac_max) / n
+ * out = in * (m + frac / frac_max)
  */
 
 #include <linux/clk-provider.h>
@@ -52,8 +52,7 @@ static int __pll_round_closest_mult(struct meson_clk_pll_data *pll)
 }
 
 static unsigned long __pll_params_to_rate(unsigned long parent_rate,
-					  unsigned int m, unsigned int n,
-					  unsigned int frac,
+					  unsigned int m, unsigned int frac,
 					  struct meson_clk_pll_data *pll)
 {
 	u64 rate = (u64)parent_rate * m;
@@ -66,7 +65,7 @@ static unsigned long __pll_params_to_rate(unsigned long parent_rate,
 		rate += DIV_ROUND_UP_ULL(frac_rate, frac_max);
 	}
 
-	return DIV_ROUND_UP_ULL(rate, n);
+	return rate;
 }
 
 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
@@ -74,17 +73,7 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int m, n, frac;
-
-	n = meson_parm_read(clk->map, &pll->n);
-
-	/*
-	 * On some HW, N is set to zero on init. This value is invalid as
-	 * it would result in a division by zero. The rate can't be
-	 * calculated in this case
-	 */
-	if (n == 0)
-		return 0;
+	unsigned int m, frac;
 
 	m = meson_parm_read(clk->map, &pll->m);
 
@@ -92,21 +81,20 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
 		meson_parm_read(clk->map, &pll->frac) :
 		0;
 
-	return __pll_params_to_rate(parent_rate, m, n, frac, pll);
+	return __pll_params_to_rate(parent_rate, m, frac, pll);
 }
 
 static unsigned int __pll_params_with_frac(unsigned long rate,
 					   unsigned long parent_rate,
 					   unsigned int m,
-					   unsigned int n,
 					   struct meson_clk_pll_data *pll)
 {
 	unsigned int frac_max = pll->frac_max ? pll->frac_max :
 						(1 << pll->frac.width);
-	u64 val = (u64)rate * n;
+	u64 val = (u64)rate;
 
 	/* Bail out if we are already over the requested rate */
-	if (rate < parent_rate * m / n)
+	if (rate < parent_rate * m)
 		return 0;
 
 	if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
@@ -119,128 +107,76 @@ static unsigned int __pll_params_with_frac(unsigned long rate,
 	return min((unsigned int)val, (frac_max - 1));
 }
 
-static bool meson_clk_pll_is_better(unsigned long rate,
-				    unsigned long best,
-				    unsigned long now,
-				    struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_range(unsigned long rate,
+				   unsigned long parent_rate,
+				   unsigned int *m,
+				   struct meson_clk_pll_data *pll)
 {
-	if (__pll_round_closest_mult(pll)) {
-		/* Round Closest */
-		if (abs(now - rate) < abs(best - rate))
-			return true;
-	} else {
-		/* Round down */
-		if (now <= rate && best < now)
-			return true;
+	if (rate <= pll->range->min * parent_rate) {
+		*m = pll->range->min;
+		return -ENODATA;
+	} else if (rate >= pll->range->max * parent_rate) {
+		*m = pll->range->max;
+		return -ENODATA;
 	}
 
-	return false;
-}
+	if (__pll_round_closest_mult(pll))
+		*m = DIV_ROUND_CLOSEST_ULL(rate, parent_rate);
+	else
+		*m = div_u64(rate, parent_rate);
 
-static int meson_clk_get_pll_table_index(unsigned int index,
-					 unsigned int *m,
-					 unsigned int *n,
-					 struct meson_clk_pll_data *pll)
-{
-	if (!pll->table[index].n)
+	/* Multiplier exceeds hardware range */
+	if (*m >= (1 << pll->m.width))
 		return -EINVAL;
 
-	*m = pll->table[index].m;
-	*n = pll->table[index].n;
-
 	return 0;
 }
 
-static unsigned int meson_clk_get_pll_range_m(unsigned long rate,
-					      unsigned long parent_rate,
-					      unsigned int n,
-					      struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_table(unsigned long rate,
+				   unsigned long parent_rate,
+				   unsigned int *m,
+				   struct meson_clk_pll_data *pll)
 {
-	u64 val = (u64)rate * n;
-
-	if (__pll_round_closest_mult(pll))
-		return DIV_ROUND_CLOSEST_ULL(val, parent_rate);
-
-	return div_u64(val,  parent_rate);
-}
-
-static int meson_clk_get_pll_range_index(unsigned long rate,
-					 unsigned long parent_rate,
-					 unsigned int index,
-					 unsigned int *m,
-					 unsigned int *n,
-					 struct meson_clk_pll_data *pll)
-{
-	*n = index + 1;
+	unsigned int i;
+	int ret;
 
-	/* Check the predivider range */
-	if (*n >= (1 << pll->n.width))
-		return -EINVAL;
+	for (i = 0, ret = 0; !ret; i++) {
+		if (!pll->table[i].m)
+			return -EINVAL;
 
-	if (*n == 1) {
-		/* Get the boundaries out the way */
-		if (rate <= pll->range->min * parent_rate) {
-			*m = pll->range->min;
-			return -ENODATA;
-		} else if (rate >= pll->range->max * parent_rate) {
-			*m = pll->range->max;
-			return -ENODATA;
+		if (pll->table[i].m == rate / parent_rate) {
+			*m = pll->table[i].m;
+			return 0;
 		}
 	}
 
-	*m = meson_clk_get_pll_range_m(rate, parent_rate, *n, pll);
-
-	/* the pre-divider gives a multiplier too big - stop */
-	if (*m >= (1 << pll->m.width))
-		return -EINVAL;
-
 	return 0;
 }
 
-static int meson_clk_get_pll_get_index(unsigned long rate,
-				       unsigned long parent_rate,
-				       unsigned int index,
-				       unsigned int *m,
-				       unsigned int *n,
-				       struct meson_clk_pll_data *pll)
+static int meson_clk_get_pll_multiplier(unsigned long rate,
+					unsigned long parent_rate,
+					unsigned int *m,
+					struct meson_clk_pll_data *pll)
 {
 	if (pll->range)
-		return meson_clk_get_pll_range_index(rate, parent_rate,
-						     index, m, n, pll);
+		return meson_clk_get_pll_range(rate, parent_rate, m, pll);
 	else if (pll->table)
-		return meson_clk_get_pll_table_index(index, m, n, pll);
+		return meson_clk_get_pll_table(rate, parent_rate, m, pll);
 
 	return -EINVAL;
 }
 
-static int meson_clk_get_pll_settings(unsigned long rate,
-				      unsigned long parent_rate,
-				      unsigned int *best_m,
-				      unsigned int *best_n,
-				      struct meson_clk_pll_data *pll)
+static unsigned int meson_clk_get_pll_max_multiplier(struct meson_clk_pll_data *pll)
 {
-	unsigned long best = 0, now = 0;
-	unsigned int i, m, n;
-	int ret;
+	unsigned int i, max_mult = 0;
 
-	for (i = 0, ret = 0; !ret; i++) {
-		ret = meson_clk_get_pll_get_index(rate, parent_rate,
-						  i, &m, &n, pll);
-		if (ret == -EINVAL)
-			break;
-
-		now = __pll_params_to_rate(parent_rate, m, n, 0, pll);
-		if (meson_clk_pll_is_better(rate, best, now, pll)) {
-			best = now;
-			*best_m = m;
-			*best_n = n;
-
-			if (now == rate)
-				break;
-		}
-	}
+	if (pll->range)
+		return pll->range->max;
+
+	for (i = 0; pll->table && pll->table[i].m; i++)
+		max_mult = max(max_mult, pll->table[i].m);
 
-	return best ? 0 : -EINVAL;
+	return max_mult;
 }
 
 static int meson_clk_pll_determine_rate(struct clk_hw *hw,
@@ -248,16 +184,32 @@ static int meson_clk_pll_determine_rate(struct clk_hw *hw,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int m, n, frac;
+	unsigned int max_mult, m, frac;
 	unsigned long round;
 	int ret;
 
-	ret = meson_clk_get_pll_settings(req->rate, req->best_parent_rate,
-					 &m, &n, pll);
-	if (ret)
+	/*
+	 * The pre-divider resets to an invalid zero value and reports a
+	 * zero rate until it gets programmed. Ask the parent for the
+	 * lowest rate which keeps the requested rate reachable; the CCF
+	 * will program the pre-divider when applying the parent rate.
+	 */
+	if (!req->best_parent_rate) {
+		max_mult = meson_clk_get_pll_max_multiplier(pll);
+		if (!max_mult)
+			return -EINVAL;
+
+		req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw,
+							  DIV_ROUND_UP_ULL(req->rate, max_mult));
+		if (!req->best_parent_rate)
+			return -EINVAL;
+	}
+
+	ret = meson_clk_get_pll_multiplier(req->rate, req->best_parent_rate, &m, pll);
+	if (ret && ret != -ENODATA)
 		return ret;
 
-	round = __pll_params_to_rate(req->best_parent_rate, m, n, 0, pll);
+	round = __pll_params_to_rate(req->best_parent_rate, m, 0, pll);
 
 	if (!MESON_PARM_APPLICABLE(&pll->frac) || req->rate == round) {
 		req->rate = round;
@@ -268,8 +220,8 @@ static int meson_clk_pll_determine_rate(struct clk_hw *hw,
 	 * The rate provided by the setting is not an exact match, let's
 	 * try to improve the result using the fractional parameter
 	 */
-	frac = __pll_params_with_frac(req->rate, req->best_parent_rate, m, n, pll);
-	req->rate = __pll_params_to_rate(req->best_parent_rate, m, n, frac, pll);
+	frac = __pll_params_with_frac(req->rate, req->best_parent_rate, m, pll);
+	req->rate = __pll_params_to_rate(req->best_parent_rate, m, frac, pll);
 
 	return 0;
 }
@@ -420,7 +372,7 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct clk_regmap *clk = to_clk_regmap(hw);
 	struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
-	unsigned int enabled, m, n, frac = 0;
+	unsigned int enabled, m, frac = 0;
 	unsigned long old_rate;
 	int ret;
 
@@ -429,19 +381,18 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
 
 	old_rate = clk_hw_get_rate(hw);
 
-	ret = meson_clk_get_pll_settings(rate, parent_rate, &m, &n, pll);
-	if (ret)
+	ret = meson_clk_get_pll_multiplier(rate, parent_rate, &m, pll);
+	if (ret && ret != -ENODATA)
 		return ret;
 
 	enabled = meson_parm_read(clk->map, &pll->en);
 	if (enabled)
 		meson_clk_pll_disable(hw);
 
-	meson_parm_write(clk->map, &pll->n, n);
 	meson_parm_write(clk->map, &pll->m, m);
 
 	if (MESON_PARM_APPLICABLE(&pll->frac)) {
-		frac = __pll_params_with_frac(rate, parent_rate, m, n, pll);
+		frac = __pll_params_with_frac(rate, parent_rate, m, pll);
 		meson_parm_write(clk->map, &pll->frac, frac);
 	}
 
diff --git a/drivers/clk/meson/clk-pll.h b/drivers/clk/meson/clk-pll.h
index 949157fb7bf5..cf20deaac1ed 100644
--- a/drivers/clk/meson/clk-pll.h
+++ b/drivers/clk/meson/clk-pll.h
@@ -11,9 +11,8 @@
 #include <linux/regmap.h>
 #include "parm.h"
 
-struct pll_params_table {
-	unsigned int	m;
-	unsigned int	n;
+struct pll_mult_table {
+	unsigned int    m;
 };
 
 struct pll_mult_range {
@@ -21,11 +20,10 @@ struct pll_mult_range {
 	unsigned int	max;
 };
 
-#define PLL_PARAMS(_m, _n)						\
-	{								\
-		.m		= (_m),					\
-		.n		= (_n),					\
-	}
+#define PLL_MULT(_m)			\
+{					\
+	.m = (_m),			\
+}
 
 #define CLK_MESON_PLL_ROUND_CLOSEST	BIT(0)
 #define CLK_MESON_PLL_NOINIT_ENABLED	BIT(1)
@@ -33,7 +31,6 @@ struct pll_mult_range {
 struct meson_clk_pll_data {
 	struct parm en;
 	struct parm m;
-	struct parm n;
 	struct parm frac;
 	struct parm l;
 	struct parm rst;
@@ -41,7 +38,7 @@ struct meson_clk_pll_data {
 	struct parm l_detect;
 	const struct reg_sequence *init_regs;
 	unsigned int init_count;
-	const struct pll_params_table *table;
+	const struct pll_mult_table *table;
 	const struct pll_mult_range *range;
 	unsigned int frac_max;
 	u8 flags;
diff --git a/drivers/clk/meson/clk-regmap.h b/drivers/clk/meson/clk-regmap.h
index 8e5c39b023e1..ee2a4b3408f0 100644
--- a/drivers/clk/meson/clk-regmap.h
+++ b/drivers/clk/meson/clk-regmap.h
@@ -75,7 +75,7 @@ struct clk_regmap_div_data {
 	unsigned int	offset;
 	u8		shift;
 	u8		width;
-	u8		flags;
+	u16		flags;
 	const struct clk_div_table	*table;
 };
 
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index d0d4c7b6dc82..4be367c89aca 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -137,6 +137,36 @@
 #define HHI_SYS1_PLL_CNTL5		0x394
 #define HHI_SYS1_PLL_CNTL6		0x398
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table g12a_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap g12a_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_FIX_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -149,11 +179,6 @@ static struct clk_regmap g12a_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_FIX_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_FIX_PLL_CNTL1,
 			.shift   = 0,
@@ -173,8 +198,8 @@ static struct clk_regmap g12a_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -206,6 +231,24 @@ static const struct pll_mult_range g12a_sys_pll_mult_range = {
 	.max = 250,
 };
 
+static struct clk_regmap g12a_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -218,11 +261,6 @@ static struct clk_regmap g12a_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL0,
 			.shift   = 31,
@@ -238,12 +276,12 @@ static struct clk_regmap g12a_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_sys_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the CPU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -265,6 +303,24 @@ static struct clk_regmap g12a_sys_pll = {
 	},
 };
 
+static struct clk_regmap g12b_sys1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS1_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12b_sys1_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -277,11 +333,6 @@ static struct clk_regmap g12b_sys1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_SYS1_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS1_PLL_CNTL0,
 			.shift   = 31,
@@ -297,12 +348,12 @@ static struct clk_regmap g12b_sys1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys1_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12b_sys1_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the CPU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -386,6 +437,24 @@ static struct clk_fixed_factor g12b_sys1_pll_div16 = {
 	},
 };
 
+static struct clk_regmap g12a_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range g12a_gp0_pll_mult_range = {
 	.min = 125,
 	.max = 255,
@@ -415,11 +484,6 @@ static struct clk_regmap g12a_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -442,10 +506,11 @@ static struct clk_regmap g12a_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -468,6 +533,24 @@ static struct clk_regmap g12a_gp0_pll = {
 	},
 };
 
+static struct clk_regmap sm1_gp1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP1_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap sm1_gp1_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -480,11 +563,6 @@ static struct clk_regmap sm1_gp1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_GP1_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP1_PLL_CNTL1,
 			.shift   = 0,
@@ -504,12 +582,12 @@ static struct clk_regmap sm1_gp1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp1_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&sm1_gp1_prediv.hw
 		},
 		.num_parents = 1,
 		/* This clock feeds the DSU, avoid disabling it */
-		.flags = CLK_IS_CRITICAL,
+		.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
 	},
 };
 
@@ -531,6 +609,24 @@ static struct clk_regmap sm1_gp1_pll = {
 	},
 };
 
+static struct clk_regmap g12a_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HIFI_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Internal hifi pll emulation configuration parameters
  */
@@ -555,11 +651,6 @@ static struct clk_regmap g12a_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_HIFI_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HIFI_PLL_CNTL1,
 			.shift   = 0,
@@ -583,10 +674,11 @@ static struct clk_regmap g12a_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -609,6 +701,24 @@ static struct clk_regmap g12a_hifi_pll = {
 	},
 };
 
+static struct clk_regmap g12a_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_PCIE_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * The Meson G12A PCIE PLL is fined tuned to deliver a very precise
  * 100MHz reference clock for the PCIe Analog PHY, and thus requires
@@ -630,9 +740,9 @@ static const struct reg_sequence g12a_pcie_pll_init_regs[] = {
 };
 
 /* Keep a single entry table for recalc/round_rate() ops */
-static const struct pll_params_table g12a_pcie_pll_table[] = {
-	PLL_PARAMS(150, 1),
-	{0, 0},
+static const struct pll_mult_table g12a_pcie_pll_table[] = {
+	PLL_MULT(150),
+	{ /* sentinel */ },
 };
 
 static struct clk_regmap g12a_pcie_pll_dco = {
@@ -647,11 +757,6 @@ static struct clk_regmap g12a_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_PCIE_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_PCIE_PLL_CNTL1,
 			.shift   = 0,
@@ -674,10 +779,11 @@ static struct clk_regmap g12a_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pcie_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -729,6 +835,24 @@ static struct clk_fixed_factor g12a_pcie_pll = {
 	},
 };
 
+static struct clk_regmap g12a_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = g12a_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap g12a_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -741,11 +865,6 @@ static struct clk_regmap g12a_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HDMI_PLL_CNTL1,
 			.shift   = 0,
@@ -765,15 +884,15 @@ static struct clk_regmap g12a_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&g12a_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
 	},
 };
 
@@ -4593,9 +4712,13 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -4618,6 +4741,7 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -4693,6 +4817,7 @@ static struct clk_hw *g12a_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -4820,9 +4945,13 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -4845,6 +4974,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -4920,6 +5050,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -4935,6 +5066,7 @@ static struct clk_hw *g12b_hw_clks[] = {
 	[CLKID_VDEC_HEVCF]		= &g12a_vdec_hevcf.hw,
 	[CLKID_TS_DIV]			= &g12a_ts_div.hw,
 	[CLKID_TS]			= &g12a_ts.hw,
+	[CLKID_SYS1_PREDIV]		= &g12b_sys1_prediv.hw,
 	[CLKID_SYS1_PLL_DCO]		= &g12b_sys1_pll_dco.hw,
 	[CLKID_SYS1_PLL]		= &g12b_sys1_pll.hw,
 	[CLKID_SYS1_PLL_DIV16_EN]	= &g12b_sys1_pll_div16_en.hw,
@@ -5088,9 +5220,13 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_VCLK2_VENCLMMC]		= &g12a_vclk2_venclmmc.hw,
 	[CLKID_VCLK2_VENCL]		= &g12a_vclk2_vencl.hw,
 	[CLKID_VCLK2_OTHER1]		= &g12a_vclk2_other1.hw,
+	[CLKID_FIXED_PREDIV]		= &g12a_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &g12a_fixed_pll_dco.hw,
+	[CLKID_SYS_PREDIV]		= &g12a_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]		= &g12a_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]		= &g12a_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &g12a_gp0_pll_dco.hw,
+	[CLKID_HIFI_PREDIV]		= &g12a_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &g12a_hifi_pll_dco.hw,
 	[CLKID_DMA]			= &g12a_dma.hw,
 	[CLKID_EFUSE]			= &g12a_efuse.hw,
@@ -5113,6 +5249,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_VAPB_1]			= &g12a_vapb_1.hw,
 	[CLKID_VAPB_SEL]		= &g12a_vapb_sel.hw,
 	[CLKID_VAPB]			= &g12a_vapb.hw,
+	[CLKID_HDMI_PREDIV]		= &g12a_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &g12a_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &g12a_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]		= &g12a_hdmi_pll_od2.hw,
@@ -5188,6 +5325,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_CPU_CLK_AXI]		= &g12a_cpu_clk_axi.hw,
 	[CLKID_CPU_CLK_TRACE_DIV]	= &g12a_cpu_clk_trace_div.hw,
 	[CLKID_CPU_CLK_TRACE]		= &g12a_cpu_clk_trace.hw,
+	[CLKID_PCIE_PREDIV]		= &g12a_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &g12a_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &g12a_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &g12a_pcie_pll_od.hw,
@@ -5204,6 +5342,7 @@ static struct clk_hw *sm1_hw_clks[] = {
 	[CLKID_TS_DIV]			= &g12a_ts_div.hw,
 	[CLKID_TS]			= &g12a_ts.hw,
 	[CLKID_GP1_PLL_DCO]		= &sm1_gp1_pll_dco.hw,
+	[CLKID_GP1_PREDIV]		= &sm1_gp1_prediv.hw,
 	[CLKID_GP1_PLL]			= &sm1_gp1_pll.hw,
 	[CLKID_DSU_CLK_DYN0_SEL]	= &sm1_dsu_clk_dyn0_sel.hw,
 	[CLKID_DSU_CLK_DYN0_DIV]	= &sm1_dsu_clk_dyn0_div.hw,
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index 7a7b104182ff..9d09f13c22fb 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -115,6 +115,35 @@
 #define HHI_BT656_CLK_CNTL		0x3d4
 #define HHI_SAR_CLK_CNTL		0x3d8
 
+static const struct clk_div_table gxbb_fixed_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap gxbb_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_fixed_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -127,11 +156,6 @@ static struct clk_regmap gxbb_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -151,8 +175,8 @@ static struct clk_regmap gxbb_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -192,6 +216,30 @@ static struct clk_fixed_factor gxbb_hdmi_pll_pre_mult = {
 	},
 };
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table gxbb_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap gxbb_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_hdmi_pll_pre_mult.hw
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -204,11 +252,6 @@ static struct clk_regmap gxbb_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_HDMI_PLL_CNTL2,
 			.shift   = 0,
@@ -229,14 +272,32 @@ static struct clk_regmap gxbb_hdmi_pll_dco = {
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
 		.parent_hws = (const struct clk_hw *[]) {
-			&gxbb_hdmi_pll_pre_mult.hw
+			&gxbb_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap gxl_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_HDMI_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
 	},
 };
 
@@ -252,11 +313,6 @@ static struct clk_regmap gxl_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_HDMI_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		/*
 		 * On gxl, there is a register shift due to
 		 * HHI_HDMI_PLL_CNTL1 which does not exist on gxbb,
@@ -282,15 +338,15 @@ static struct clk_regmap gxl_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxl_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 		/*
 		 * Display directly handle hdmi pll registers ATM, we need
 		 * NOCACHE to keep our view of the clock as accurate as possible
 		 */
-		.flags = CLK_GET_RATE_NOCACHE,
+		.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -413,6 +469,24 @@ static struct clk_regmap gxl_hdmi_pll = {
 	},
 };
 
+static struct clk_regmap gxbb_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap gxbb_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -425,11 +499,6 @@ static struct clk_regmap gxbb_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -444,10 +513,11 @@ static struct clk_regmap gxbb_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -469,38 +539,56 @@ static struct clk_regmap gxbb_sys_pll = {
 	},
 };
 
-static const struct pll_params_table gxbb_gp0_pll_params_table[] = {
-	PLL_PARAMS(32, 1),
-	PLL_PARAMS(33, 1),
-	PLL_PARAMS(34, 1),
-	PLL_PARAMS(35, 1),
-	PLL_PARAMS(36, 1),
-	PLL_PARAMS(37, 1),
-	PLL_PARAMS(38, 1),
-	PLL_PARAMS(39, 1),
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(41, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
+static struct clk_regmap gxbb_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
+static const struct pll_mult_table gxbb_gp0_pll_params_table[] = {
+	PLL_MULT(32),
+	PLL_MULT(33),
+	PLL_MULT(34),
+	PLL_MULT(35),
+	PLL_MULT(36),
+	PLL_MULT(37),
+	PLL_MULT(38),
+	PLL_MULT(39),
+	PLL_MULT(40),
+	PLL_MULT(41),
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
 	{ /* sentinel */ },
 };
 
@@ -522,11 +610,6 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_GP0_PLL_CNTL,
 			.shift   = 31,
@@ -544,6 +627,25 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxbb_gp0_prediv.hw
+		},
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+	},
+};
+
+static struct clk_regmap gxl_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP0_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = gxbb_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
 		.parent_data = &(const struct clk_parent_data) {
 			.fw_name = "xtal",
 		},
@@ -551,32 +653,32 @@ static struct clk_regmap gxbb_gp0_pll_dco = {
 	},
 };
 
-static const struct pll_params_table gxl_gp0_pll_params_table[] = {
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(43, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(46, 1),
-	PLL_PARAMS(47, 1),
-	PLL_PARAMS(48, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
+static const struct pll_mult_table gxl_gp0_pll_params_table[] = {
+	PLL_MULT(42),
+	PLL_MULT(43),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(46),
+	PLL_MULT(47),
+	PLL_MULT(48),
+	PLL_MULT(49),
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
 	{ /* sentinel */ },
 };
 
@@ -600,11 +702,6 @@ static struct clk_regmap gxl_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP0_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_GP0_PLL_CNTL1,
 			.shift   = 0,
@@ -627,10 +724,11 @@ static struct clk_regmap gxl_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&gxl_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -3006,11 +3104,15 @@ static struct clk_hw *gxbb_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]	    = &gxbb_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]	    = &gxbb_gen_clk_div.hw,
 	[CLKID_GEN_CLK]		    = &gxbb_gen_clk.hw,
+	[CLKID_FIXED_PREDIV]	    = &gxbb_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	    = &gxbb_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &gxbb_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &gxbb_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]	    = &gxbb_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]	    = &gxbb_hdmi_pll_od2.hw,
+	[CLKID_SYS_PREDIV]	    = &gxbb_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]	    = &gxbb_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]	    = &gxbb_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	    = &gxbb_gp0_pll_dco.hw,
 	[CLKID_VID_PLL_DIV]	    = &gxbb_vid_pll_div.hw,
 	[CLKID_VID_PLL_SEL]	    = &gxbb_vid_pll_sel.hw,
@@ -3213,11 +3315,15 @@ static struct clk_hw *gxl_hw_clks[] = {
 	[CLKID_GEN_CLK_SEL]	    = &gxbb_gen_clk_sel.hw,
 	[CLKID_GEN_CLK_DIV]	    = &gxbb_gen_clk_div.hw,
 	[CLKID_GEN_CLK]		    = &gxbb_gen_clk.hw,
+	[CLKID_FIXED_PREDIV]	    = &gxbb_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]	    = &gxbb_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &gxl_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &gxl_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]	    = &gxl_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL_OD2]	    = &gxl_hdmi_pll_od2.hw,
+	[CLKID_SYS_PREDIV]	    = &gxbb_sys_prediv.hw,
 	[CLKID_SYS_PLL_DCO]	    = &gxbb_sys_pll_dco.hw,
+	[CLKID_GP0_PREDIV]	    = &gxl_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]	    = &gxl_gp0_pll_dco.hw,
 	[CLKID_VID_PLL_DIV]	    = &gxbb_vid_pll_div.hw,
 	[CLKID_VID_PLL_SEL]	    = &gxbb_vid_pll_sel.hw,
diff --git a/drivers/clk/meson/meson8-ddr.c b/drivers/clk/meson/meson8-ddr.c
index 0f93774f7371..a3f4c47982d9 100644
--- a/drivers/clk/meson/meson8-ddr.c
+++ b/drivers/clk/meson/meson8-ddr.c
@@ -23,6 +23,35 @@
 #define DDR_CLK_CNTL			0x18
 #define DDR_CLK_STS			0x1c
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table meson8_ddr_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8_ddr_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = AM_DDR_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8_ddr_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "ddr_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8_ddr_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -35,11 +64,6 @@ static struct clk_regmap meson8_ddr_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = AM_DDR_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = AM_DDR_PLL_CNTL,
 			.shift   = 31,
@@ -54,8 +78,8 @@ static struct clk_regmap meson8_ddr_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "ddr_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8_ddr_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -79,6 +103,7 @@ static struct clk_regmap meson8_ddr_pll = {
 };
 
 static struct clk_hw *meson8_ddr_hw_clks[] = {
+	[DDR_CLKID_DDR_PREDIV]		= &meson8_ddr_prediv.hw,
 	[DDR_CLKID_DDR_PLL_DCO]		= &meson8_ddr_pll_dco.hw,
 	[DDR_CLKID_DDR_PLL]		= &meson8_ddr_pll.hw,
 };
diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
index bc029c6ab622..0c9ba079bc51 100644
--- a/drivers/clk/meson/meson8b.c
+++ b/drivers/clk/meson/meson8b.c
@@ -95,30 +95,61 @@ struct meson8b_clk_reset {
 	struct regmap *regmap;
 };
 
-static const struct pll_params_table sys_pll_params_table[] = {
-	PLL_PARAMS(50, 1),
-	PLL_PARAMS(51, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(53, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(55, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(57, 1),
-	PLL_PARAMS(58, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(63, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(65, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(67, 1),
-	PLL_PARAMS(68, 1),
-	PLL_PARAMS(84, 1),
+static const struct pll_mult_table sys_pll_params_table[] = {
+	PLL_MULT(50),
+	PLL_MULT(51),
+	PLL_MULT(52),
+	PLL_MULT(53),
+	PLL_MULT(54),
+	PLL_MULT(55),
+	PLL_MULT(56),
+	PLL_MULT(57),
+	PLL_MULT(58),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(63),
+	PLL_MULT(64),
+	PLL_MULT(65),
+	PLL_MULT(66),
+	PLL_MULT(67),
+	PLL_MULT(68),
+	PLL_MULT(84),
 	{ /* sentinel */ },
 };
 
+static const struct clk_div_table meson8b_fixed_prediv_div_table[] = {
+	{ .val = 4, .div = 4 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8b_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_MPLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_fixed_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8b_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -131,11 +162,6 @@ static struct clk_regmap meson8b_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_MPLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_MPLL_CNTL2,
 			.shift   = 0,
@@ -155,10 +181,8 @@ static struct clk_regmap meson8b_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_fixed_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -185,15 +209,38 @@ static struct clk_regmap meson8b_fixed_pll = {
 	},
 };
 
+static const struct clk_div_table meson8b_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8b_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_VID_PLL_CNTL,
+		.shift   = 10,
+		.width   = 5,
+		.table = meson8b_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_fixed_factor hdmi_pll_dco_in = {
 	.mult = 2,
 	.div = 1,
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco_in",
 		.ops = &clk_fixed_factor_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_hdmi_prediv.hw
 		},
 		.num_parents = 1,
 	},
@@ -214,24 +261,24 @@ static const struct reg_sequence meson8b_hdmi_pll_init_regs[] = {
 	{ .reg = HHI_VID2_PLL_CNTL2,	.def = 0x0430a800 },
 };
 
-static const struct pll_params_table meson8b_hdmi_pll_params_table[] = {
-	PLL_PARAMS(40, 1),
-	PLL_PARAMS(42, 1),
-	PLL_PARAMS(44, 1),
-	PLL_PARAMS(45, 1),
-	PLL_PARAMS(49, 1),
-	PLL_PARAMS(52, 1),
-	PLL_PARAMS(54, 1),
-	PLL_PARAMS(56, 1),
-	PLL_PARAMS(59, 1),
-	PLL_PARAMS(60, 1),
-	PLL_PARAMS(61, 1),
-	PLL_PARAMS(62, 1),
-	PLL_PARAMS(64, 1),
-	PLL_PARAMS(66, 1),
-	PLL_PARAMS(68, 1),
-	PLL_PARAMS(71, 1),
-	PLL_PARAMS(82, 1),
+static const struct pll_mult_table meson8b_hdmi_pll_params_table[] = {
+	PLL_MULT(40),
+	PLL_MULT(42),
+	PLL_MULT(44),
+	PLL_MULT(45),
+	PLL_MULT(49),
+	PLL_MULT(52),
+	PLL_MULT(54),
+	PLL_MULT(56),
+	PLL_MULT(59),
+	PLL_MULT(60),
+	PLL_MULT(61),
+	PLL_MULT(62),
+	PLL_MULT(64),
+	PLL_MULT(66),
+	PLL_MULT(68),
+	PLL_MULT(71),
+	PLL_MULT(82),
 	{ /* sentinel */ }
 };
 
@@ -247,11 +294,6 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_VID_PLL_CNTL,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HHI_VID_PLL_CNTL2,
 			.shift   = 0,
@@ -279,6 +321,7 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
 			&hdmi_pll_dco_in.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -318,6 +361,26 @@ static struct clk_regmap meson8b_hdmi_pll_hdmi_out = {
 	},
 };
 
+static struct clk_regmap meson8b_sys_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_SYS_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap meson8b_sys_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -330,11 +393,6 @@ static struct clk_regmap meson8b_sys_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_SYS_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_SYS_PLL_CNTL,
 			.shift   = 31,
@@ -350,12 +408,11 @@ static struct clk_regmap meson8b_sys_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "sys_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8b_sys_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2035,6 +2092,31 @@ static struct clk_regmap meson8b_mali = {
 	},
 };
 
+static const struct clk_div_table meson8b_gp_prediv_div_table[] = {
+	{ .val = 3, .div = 3 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap meson8m2_gp_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HHI_GP_PLL_CNTL,
+		.shift   = 9,
+		.width   = 5,
+		.table = meson8b_gp_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "xtal",
+			.name = "xtal",
+			.index = -1,
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence meson8m2_gp_pll_init_regs[] = {
 	{ .reg = HHI_GP_PLL_CNTL2,	.def = 0x59c88000 },
 	{ .reg = HHI_GP_PLL_CNTL3,	.def = 0xca463823 },
@@ -2042,8 +2124,8 @@ static const struct reg_sequence meson8m2_gp_pll_init_regs[] = {
 	{ .reg = HHI_GP_PLL_CNTL5,	.def = 0x00003000 },
 };
 
-static const struct pll_params_table meson8m2_gp_pll_params_table[] = {
-	PLL_PARAMS(182, 3),
+static const struct pll_mult_table meson8m2_gp_pll_params_table[] = {
+	PLL_MULT(182),
 	{ /* sentinel */ },
 };
 
@@ -2059,11 +2141,6 @@ static struct clk_regmap meson8m2_gp_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HHI_GP_PLL_CNTL,
-			.shift   = 9,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HHI_GP_PLL_CNTL,
 			.shift   = 31,
@@ -2081,12 +2158,11 @@ static struct clk_regmap meson8m2_gp_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "xtal",
-			.name = "xtal",
-			.index = -1,
+		.parent_hws = (const struct clk_hw *[]) {
+			&meson8m2_gp_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -2920,8 +2996,11 @@ static struct clk_hw *meson8_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3124,8 +3203,11 @@ static struct clk_hw *meson8b_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3339,8 +3421,11 @@ static struct clk_hw *meson8m2_hw_clks[] = {
 	[CLKID_NAND_SEL]	    = &meson8b_nand_clk_sel.hw,
 	[CLKID_NAND_DIV]	    = &meson8b_nand_clk_div.hw,
 	[CLKID_NAND_CLK]	    = &meson8b_nand_clk.hw,
+	[CLKID_PLL_FIXED_PREDIV]    = &meson8b_fixed_prediv.hw,
 	[CLKID_PLL_FIXED_DCO]	    = &meson8b_fixed_pll_dco.hw,
+	[CLKID_HDMI_PREDIV]	    = &meson8b_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]	    = &meson8b_hdmi_pll_dco.hw,
+	[CLKID_PLL_SYS_PREDIV]	    = &meson8b_sys_prediv.hw,
 	[CLKID_PLL_SYS_DCO]	    = &meson8b_sys_pll_dco.hw,
 	[CLKID_CPU_CLK_DIV2]	    = &meson8b_cpu_clk_div2.hw,
 	[CLKID_CPU_CLK_DIV3]	    = &meson8b_cpu_clk_div3.hw,
@@ -3410,6 +3495,7 @@ static struct clk_hw *meson8m2_hw_clks[] = {
 	[CLKID_MALI_1_DIV]	    = &meson8b_mali_1_div.hw,
 	[CLKID_MALI_1]		    = &meson8b_mali_1.hw,
 	[CLKID_MALI]		    = &meson8b_mali.hw,
+	[CLKID_GP_PREDIV]	    = &meson8m2_gp_prediv.hw,
 	[CLKID_GP_PLL_DCO]	    = &meson8m2_gp_pll_dco.hw,
 	[CLKID_GP_PLL]		    = &meson8m2_gp_pll.hw,
 	[CLKID_VPU_0_SEL]	    = &meson8m2_vpu_0_sel.hw,
diff --git a/drivers/clk/meson/s4-pll.c b/drivers/clk/meson/s4-pll.c
index 56ce6f566e53..6caa95887971 100644
--- a/drivers/clk/meson/s4-pll.c
+++ b/drivers/clk/meson/s4-pll.c
@@ -44,6 +44,12 @@
 #define ANACTRL_MPLL_CTRL8                         0x1a0
 #define ANACTRL_HDMIPLL_CTRL0                      0x1c0
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table s4_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
 /*
  * These clock are a fixed value (fixed_pll is 2GHz) that is initialized by ROMcode.
  * The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable
@@ -51,6 +57,30 @@
  * Meanwhile, these clock won't ever change at runtime.
  * For the above reasons, we can only use ro_ops for fixed PLL related clocks.
  */
+static struct clk_regmap s4_fixed_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_FIXPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "fixed_prediv",
+		.ops = &clk_regmap_divider_ro_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap s4_fixed_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -68,11 +98,6 @@ static struct clk_regmap s4_fixed_pll_dco = {
 			.shift   = 0,
 			.width   = 17,
 		},
-		.n = {
-			.reg_off = ANACTRL_FIXPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_FIXPLL_CTRL0,
 			.shift   = 31,
@@ -87,10 +112,11 @@ static struct clk_regmap s4_fixed_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "fixed_pll_dco",
 		.ops = &meson_clk_pll_ro_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_fixed_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -273,6 +299,24 @@ static struct clk_regmap s4_fclk_div2p5 = {
 	},
 };
 
+static struct clk_regmap s4_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_GP0PLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range s4_gp0_pll_mult_range = {
 	.min = 125,
 	.max = 250,
@@ -302,11 +346,6 @@ static struct clk_regmap s4_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_GP0PLL_CTRL0,
 			.shift   = 31,
@@ -324,10 +363,11 @@ static struct clk_regmap s4_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -350,6 +390,24 @@ static struct clk_regmap s4_gp0_pll = {
 	},
 };
 
+static struct clk_regmap s4_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HIFIPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Internal hifi pll emulation configuration parameters
  */
@@ -373,11 +431,6 @@ static struct clk_regmap s4_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = ANACTRL_HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -402,10 +455,11 @@ static struct clk_regmap s4_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -428,6 +482,24 @@ static struct clk_regmap s4_hifi_pll = {
 	},
 };
 
+static struct clk_regmap s4_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = ANACTRL_HDMIPLL_CTRL0,
+		.shift   = 10,
+		.width   = 5,
+		.table = s4_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data){
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = (const struct clk_parent_data []) {
+			{ .fw_name = "xtal", }
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap s4_hdmi_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -440,11 +512,6 @@ static struct clk_regmap s4_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = ANACTRL_HDMIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
 			.shift   = 31,
@@ -460,10 +527,11 @@ static struct clk_regmap s4_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "xtal", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&s4_hdmi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -760,6 +828,7 @@ static struct clk_regmap s4_mpll3 = {
 
 /* Array of all clocks provided by this provider */
 static struct clk_hw *s4_pll_hw_clks[] = {
+	[CLKID_FIXED_PREDIV]		= &s4_fixed_prediv.hw,
 	[CLKID_FIXED_PLL_DCO]		= &s4_fixed_pll_dco.hw,
 	[CLKID_FIXED_PLL]		= &s4_fixed_pll.hw,
 	[CLKID_FCLK_DIV2_DIV]		= &s4_fclk_div2_div.hw,
@@ -774,10 +843,13 @@ static struct clk_hw *s4_pll_hw_clks[] = {
 	[CLKID_FCLK_DIV7]		= &s4_fclk_div7.hw,
 	[CLKID_FCLK_DIV2P5_DIV]		= &s4_fclk_div2p5_div.hw,
 	[CLKID_FCLK_DIV2P5]		= &s4_fclk_div2p5.hw,
+	[CLKID_GP0_PREDIV]		= &s4_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &s4_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]			= &s4_gp0_pll.hw,
+	[CLKID_HIFI_PREDIV]		= &s4_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &s4_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]		= &s4_hifi_pll.hw,
+	[CLKID_HDMI_PREDIV]		= &s4_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &s4_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &s4_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL]		= &s4_hdmi_pll.hw,
diff --git a/drivers/clk/meson/t7-pll.c b/drivers/clk/meson/t7-pll.c
index 0a622f45fa36..28d563fcb1bd 100644
--- a/drivers/clk/meson/t7-pll.c
+++ b/drivers/clk/meson/t7-pll.c
@@ -71,6 +71,36 @@
 #define MCLK_PLL_CNTL4		0x10
 #define MCLK_PLL_STS		0x14
 
+/* Keep the pre-divider at 1 to achieve the best PLL performance. */
+static const struct clk_div_table t7_prediv_div_table[] = {
+	{ .val = 1, .div = 1 },
+	{ /* sentinel */ }
+};
+
+static struct clk_regmap t7_gp0_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = GP0PLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		/*
+		 * The PLL pre-divider have a zero value after reset,
+		 * which does not produce a usable clock. Use
+		 * CLK_DIVIDER_INVALID_ZERO to report this state correctly.
+		 * The pre-dividers below use this flag for the same reason.
+		 */
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp0_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct pll_mult_range t7_media_pll_mult_range = {
 	.min = 125,
 	.max = 250,
@@ -97,11 +127,6 @@ static struct clk_regmap t7_gp0_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = GP0PLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = GP0PLL_STS,
 			.shift   = 31,
@@ -119,10 +144,11 @@ static struct clk_regmap t7_gp0_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp0_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_gp0_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -144,6 +170,24 @@ static struct clk_regmap t7_gp0_pll = {
 	},
 };
 
+static struct clk_regmap t7_gp1_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = GP1PLL_CTRL0,
+		.shift = 16,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "gp1_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * Compared with GP0 PLL, GP1 PLL is a newly designed PLL with
  * a DCO range of 1.6GHz to 3.2GHz.
@@ -171,11 +215,6 @@ static struct clk_regmap t7_gp1_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = GP1PLL_CTRL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = GP1PLL_STS,
 			.shift   = 31,
@@ -193,10 +232,11 @@ static struct clk_regmap t7_gp1_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "gp1_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_gp1_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -218,6 +258,24 @@ static struct clk_regmap t7_gp1_pll = {
 	},
 };
 
+static struct clk_regmap t7_hifi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HIFIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hifi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence t7_hifi_init_regs[] = {
 	{ .reg = HIFIPLL_CTRL1, .def = 0x00000000 },
 	{ .reg = HIFIPLL_CTRL2, .def = 0x00000000 },
@@ -239,11 +297,6 @@ static struct clk_regmap t7_hifi_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = HIFIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.frac = {
 			.reg_off = HIFIPLL_CTRL1,
 			.shift   = 0,
@@ -267,10 +320,11 @@ static struct clk_regmap t7_hifi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hifi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_hifi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -292,6 +346,24 @@ static struct clk_regmap t7_hifi_pll = {
 	},
 };
 
+static struct clk_regmap t7_pcie_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = PCIEPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "pcie_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 /*
  * The T7 PCIE PLL is fined tuned to deliver a very precise
  * 100MHz reference clock for the PCIe Analog PHY, and thus requires
@@ -324,11 +396,6 @@ static struct clk_regmap t7_pcie_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = PCIEPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = PCIEPLL_CTRL0,
 			.shift   = 31,
@@ -345,10 +412,11 @@ static struct clk_regmap t7_pcie_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "pcie_pll_dco",
 		.ops = &meson_clk_pcie_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_pcie_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -624,6 +692,24 @@ static struct clk_regmap t7_mpll3 = {
 	},
 };
 
+static struct clk_regmap t7_hdmi_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = HDMIPLL_CTRL0,
+		.shift = 10,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "hdmi_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static const struct reg_sequence t7_hdmi_init_regs[] = {
 	{ .reg = HDMIPLL_CTRL1, .def = 0x00000000 },
 	{ .reg = HDMIPLL_CTRL2, .def = 0x00000000 },
@@ -645,11 +731,6 @@ static struct clk_regmap t7_hdmi_pll_dco = {
 			.shift   = 0,
 			.width   = 9,
 		},
-		.n = {
-			.reg_off = HDMIPLL_CTRL0,
-			.shift   = 10,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = HDMIPLL_CTRL0,
 			.shift   = 31,
@@ -667,10 +748,11 @@ static struct clk_regmap t7_hdmi_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "hdmi_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = (const struct clk_parent_data []) {
-			{ .fw_name = "in0", }
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_hdmi_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -722,6 +804,24 @@ static const struct reg_sequence t7_mclk_init_regs[] = {
 	{ .reg = MCLK_PLL_CNTL4, .def = 0x00180303 },
 };
 
+static struct clk_regmap t7_mclk_prediv = {
+	.data = &(struct clk_regmap_div_data){
+		.offset = MCLK_PLL_CNTL0,
+		.shift = 16,
+		.width = 5,
+		.table = t7_prediv_div_table,
+		.flags = CLK_DIVIDER_INVALID_ZERO,
+	},
+	.hw.init = &(struct clk_init_data) {
+		.name = "mclk_prediv",
+		.ops = &clk_regmap_divider_ops,
+		.parent_data = &(const struct clk_parent_data) {
+			.fw_name = "in0",
+		},
+		.num_parents = 1,
+	},
+};
+
 static struct clk_regmap t7_mclk_pll_dco = {
 	.data = &(struct meson_clk_pll_data){
 		.en = {
@@ -734,11 +834,6 @@ static struct clk_regmap t7_mclk_pll_dco = {
 			.shift   = 0,
 			.width   = 8,
 		},
-		.n = {
-			.reg_off = MCLK_PLL_CNTL0,
-			.shift   = 16,
-			.width   = 5,
-		},
 		.l = {
 			.reg_off = MCLK_PLL_CNTL0,
 			.shift   = 31,
@@ -761,10 +856,11 @@ static struct clk_regmap t7_mclk_pll_dco = {
 	.hw.init = &(struct clk_init_data){
 		.name = "mclk_pll_dco",
 		.ops = &meson_clk_pll_ops,
-		.parent_data = &(const struct clk_parent_data) {
-			.fw_name = "in0",
+		.parent_hws = (const struct clk_hw *[]) {
+			&t7_mclk_prediv.hw
 		},
 		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
 	},
 };
 
@@ -939,21 +1035,25 @@ static struct clk_regmap t7_mclk_1 = {
 };
 
 static struct clk_hw *t7_gp0_hw_clks[] = {
+	[CLKID_GP0_PREDIV]		= &t7_gp0_prediv.hw,
 	[CLKID_GP0_PLL_DCO]		= &t7_gp0_pll_dco.hw,
 	[CLKID_GP0_PLL]			= &t7_gp0_pll.hw,
 };
 
 static struct clk_hw *t7_gp1_hw_clks[] = {
+	[CLKID_GP1_PREDIV]		= &t7_gp1_prediv.hw,
 	[CLKID_GP1_PLL_DCO]		= &t7_gp1_pll_dco.hw,
 	[CLKID_GP1_PLL]			= &t7_gp1_pll.hw,
 };
 
 static struct clk_hw *t7_hifi_hw_clks[] = {
+	[CLKID_HIFI_PREDIV]		= &t7_hifi_prediv.hw,
 	[CLKID_HIFI_PLL_DCO]		= &t7_hifi_pll_dco.hw,
 	[CLKID_HIFI_PLL]		= &t7_hifi_pll.hw,
 };
 
 static struct clk_hw *t7_pcie_hw_clks[] = {
+	[CLKID_PCIE_PREDIV]		= &t7_pcie_prediv.hw,
 	[CLKID_PCIE_PLL_DCO]		= &t7_pcie_pll_dco.hw,
 	[CLKID_PCIE_PLL_DCO_DIV2]	= &t7_pcie_pll_dco_div2.hw,
 	[CLKID_PCIE_PLL_OD]		= &t7_pcie_pll_od.hw,
@@ -973,12 +1073,14 @@ static struct clk_hw *t7_mpll_hw_clks[] = {
 };
 
 static struct clk_hw *t7_hdmi_hw_clks[] = {
+	[CLKID_HDMI_PREDIV]		= &t7_hdmi_prediv.hw,
 	[CLKID_HDMI_PLL_DCO]		= &t7_hdmi_pll_dco.hw,
 	[CLKID_HDMI_PLL_OD]		= &t7_hdmi_pll_od.hw,
 	[CLKID_HDMI_PLL]		= &t7_hdmi_pll.hw,
 };
 
 static struct clk_hw *t7_mclk_hw_clks[] = {
+	[CLKID_MCLK_PREDIV]		= &t7_mclk_prediv.hw,
 	[CLKID_MCLK_PLL_DCO]		= &t7_mclk_pll_dco.hw,
 	[CLKID_MCLK_PRE]		= &t7_mclk_pre_od.hw,
 	[CLKID_MCLK_PLL]		= &t7_mclk_pll.hw,

-- 
2.47.1



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

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

* Re: [PATCH RFC RFT v2 2/3] clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
  2026-10-29  9:30   ` Jian Hu via B4 Relay
@ 2026-10-29  9:47     ` sashiko-bot
  -1 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-29  9:47 UTC (permalink / raw)
  To: Jian Hu
  Cc: linux-amlogic, linux-clk, devicetree, robh, conor+dt,
	neil.armstrong

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] clk: divider: unhandled zero divisor in divider_ro_determine_rate()
--

commit 5ec3118aee2f03855552c58a4a8533e035b565bc
Author: Jian Hu <jian.hu@amlogic.com>

clk: divider: add CLK_DIVIDER_INVALID_ZERO flag

This commit introduces the CLK_DIVIDER_INVALID_ZERO flag for dividers where
a zero divisor value represents an invalid hardware state rather than a
pass-through. When this flag is set and the divisor evaluates to zero,
divider_recalc_rate() returns 0 without emitting a warning.

> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> index f68216c65d3df..6c1523d22477c 100644
> --- a/drivers/clk/clk-divider.c
> +++ b/drivers/clk/clk-divider.c
> @@ -143,6 +143,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
>  
>  	div = _get_div(table, val, flags, width);
>  	if (!div) {
> +		if (flags & CLK_DIVIDER_INVALID_ZERO)
> +			return 0;

[Severity: High]
Should divider_ro_determine_rate() in drivers/clk/clk-divider.c also handle
a zero divisor when CLK_DIVIDER_INVALID_ZERO or CLK_DIVIDER_ALLOW_ZERO is
set?

When a read-only divider has a register value val that maps to div == 0 via
_get_div() (for example, when CLK_DIVIDER_INVALID_ZERO is combined with
CLK_DIVIDER_ONE_BASED or an unmapped table entry alongside
CLK_DIVIDER_READ_ONLY), calling clk_round_rate() or clk_set_rate() invokes
clk_divider_determine_rate()->divider_ro_determine_rate():

drivers/clk/clk-divider.c:divider_ro_determine_rate() {
	int div;

	div = _get_div(table, val, flags, width);

	/* Even a read-only clock can propagate a rate change */
	if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
		if (!req->best_parent_hw)
			return -EINVAL;

		req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw,
							  req->rate * div);
	}

	req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div);

	return 0;
}

Unlike divider_recalc_rate(), divider_ro_determine_rate() does not check for
!div before calling DIV_ROUND_UP_ULL(), which will trigger a division by zero
in the kernel when div is 0.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029-meson_refactor_n-v2-0-ff4ac74e527b@amlogic.com?part=2

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

* Re: [PATCH RFC RFT v2 2/3] clk: divider: add CLK_DIVIDER_INVALID_ZERO flag
@ 2026-10-29  9:47     ` sashiko-bot
  0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-29  9:47 UTC (permalink / raw)
  To: Jian Hu
  Cc: linux-amlogic, linux-clk, devicetree, robh, conor+dt,
	neil.armstrong

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] clk: divider: unhandled zero divisor in divider_ro_determine_rate()
--

commit 5ec3118aee2f03855552c58a4a8533e035b565bc
Author: Jian Hu <jian.hu@amlogic.com>

clk: divider: add CLK_DIVIDER_INVALID_ZERO flag

This commit introduces the CLK_DIVIDER_INVALID_ZERO flag for dividers where
a zero divisor value represents an invalid hardware state rather than a
pass-through. When this flag is set and the divisor evaluates to zero,
divider_recalc_rate() returns 0 without emitting a warning.

> diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
> index f68216c65d3df..6c1523d22477c 100644
> --- a/drivers/clk/clk-divider.c
> +++ b/drivers/clk/clk-divider.c
> @@ -143,6 +143,9 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
>  
>  	div = _get_div(table, val, flags, width);
>  	if (!div) {
> +		if (flags & CLK_DIVIDER_INVALID_ZERO)
> +			return 0;

[Severity: High]
Should divider_ro_determine_rate() in drivers/clk/clk-divider.c also handle
a zero divisor when CLK_DIVIDER_INVALID_ZERO or CLK_DIVIDER_ALLOW_ZERO is
set?

When a read-only divider has a register value val that maps to div == 0 via
_get_div() (for example, when CLK_DIVIDER_INVALID_ZERO is combined with
CLK_DIVIDER_ONE_BASED or an unmapped table entry alongside
CLK_DIVIDER_READ_ONLY), calling clk_round_rate() or clk_set_rate() invokes
clk_divider_determine_rate()->divider_ro_determine_rate():

drivers/clk/clk-divider.c:divider_ro_determine_rate() {
	int div;

	div = _get_div(table, val, flags, width);

	/* Even a read-only clock can propagate a rate change */
	if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
		if (!req->best_parent_hw)
			return -EINVAL;

		req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw,
							  req->rate * div);
	}

	req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div);

	return 0;
}

Unlike divider_recalc_rate(), divider_ro_determine_rate() does not check for
!div before calling DIV_ROUND_UP_ULL(), which will trigger a division by zero
in the kernel when div is 0.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029-meson_refactor_n-v2-0-ff4ac74e527b@amlogic.com?part=2

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

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

* Re: [PATCH RFC RFT v2 3/3] clk: meson: pll: Remove the dedicated n parameter
  2026-10-29  9:30   ` Jian Hu via B4 Relay
@ 2026-10-29  9:57     ` sashiko-bot
  -1 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-29  9:57 UTC (permalink / raw)
  To: Jian Hu
  Cc: linux-amlogic, linux-clk, devicetree, robh, conor+dt,
	neil.armstrong

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] clk: meson: pll: CLK_SET_RATE_PARENT on ro_ops PLL DCOs breaks rate rounding
- [High] clk: meson: pll: meson_clk_get_pll_table() fails on non-exact multipliers
- [High] clk: meson: meson8b: missing CLK_SET_RATE_PARENT on hdmi_pll_dco_in
- [High] clk: meson: pll: cached 0 Hz pre-divider rate on directly programmed PLLs
--

commit b0439d678a94dfead2cca909902c6363ae03f5d0
Author: Jian Hu <jian.hu@amlogic.com>

clk: meson: pll: Remove the dedicated n parameter

This commit removes the dedicated pre-divider (n) field from
struct meson_clk_pll_data and simplifies the Meson PLL rate calculation.
Each PLL pre-divider is instead modeled as a standalone divider clock that
serves as the parent of the PLL DCO clock.

> diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
> index 280842e9cb371..e6e6b9a8030c0 100644
> --- a/drivers/clk/meson/axg.c
> +++ b/drivers/clk/meson/axg.c

[ ... ]

> @@ -202,10 +240,11 @@ static struct clk_regmap axg_sys_pll_dco = {
>  	.hw.init = &(struct clk_init_data){
>  		.name = "sys_pll_dco",
>  		.ops = &meson_clk_pll_ro_ops,
> -		.parent_data = &(const struct clk_parent_data) {
> -			.fw_name = "xtal",
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&axg_sys_prediv.hw
>  		},
>  		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
>  	},
>  };

[Severity: High]
Does setting CLK_SET_RATE_PARENT on a clock using meson_clk_pll_ro_ops break
rate rounding and rate calculation in the common clock framework?

meson_clk_pll_ro_ops only provides .init, .recalc_rate, and .is_enabled, and
does not implement .determine_rate or .round_rate, so clk_core_can_round() is
false.

In drivers/clk/clk.c, clk_core_round_rate_nolock() checks:

	if (clk_core_can_round(core))
		return core->ops->determine_rate(core->hw, req);
	else if (core->flags & CLK_SET_RATE_PARENT)
		return clk_core_round_rate_nolock(core->parent, req);

When CLK_SET_RATE_PARENT is set on a clock that cannot round its own rate,
clk_core_round_rate_nolock() and clk_calc_new_rates() treat the clock as a
1:1 pass-through of its parent and set the rate to the parent pre-divider
rate (24 MHz or 48 MHz) instead of the multi-GHz DCO frequency.

This affects child dividers with CLK_SET_RATE_PARENT when consumer drivers
call clk_round_rate() or clk_set_rate(). The same pattern is also added on:

  - sm1_gp1_pll_dco and g12a_hdmi_pll_dco in drivers/clk/meson/g12a.c
  - gxbb_hdmi_pll_dco, gxl_hdmi_pll_dco, and gxbb_sys_pll_dco in
    drivers/clk/meson/gxbb.c
  - s4_fixed_pll_dco in drivers/clk/meson/s4-pll.c

Should these meson_clk_pll_ro_ops DCO clocks omit CLK_SET_RATE_PARENT (and
use clk_regmap_divider_ro_ops for their pre-dividers)?

> diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
> index 1ea6579a760f3..a7e3f8c8eb783 100644
> --- a/drivers/clk/meson/clk-pll.c
> +++ b/drivers/clk/meson/clk-pll.c

[ ... ]

> +static int meson_clk_get_pll_table(unsigned long rate,
> +				   unsigned long parent_rate,
> +				   unsigned int *m,
> +				   struct meson_clk_pll_data *pll)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0, ret = 0; !ret; i++) {
> +		if (!pll->table[i].m)
> +			return -EINVAL;
> +
> +		if (pll->table[i].m == rate / parent_rate) {
> +			*m = pll->table[i].m;
> +			return 0;
> +		}
> +	}
> +
> +	return 0;
> +}

[Severity: High]
What happens in meson_clk_get_pll_table() when rate / parent_rate does not
exactly match an entry in pll->table?

Previously, meson_clk_get_pll_settings() iterated over pll->table and used
meson_clk_pll_is_better() to pick either the closest entry (when
__pll_round_closest_mult(pll) was true) or the largest table entry with a
rate less than or equal to the requested rate.

With this change, meson_clk_get_pll_table() requires an exact match for
rate / parent_rate and returns -EINVAL otherwise:

1. Sparse multiplier tables such as sys_pll_params_table (which has a gap
   between m = 68 and m = 84 where Meson8 CPU OPPs at 1800 MHz and 1992 MHz
   fall) and meson8b_hdmi_pll_params_table in drivers/clk/meson/meson8b.c
   will fail with -EINVAL in meson_clk_pll_determine_rate() and
   meson_clk_pll_set_rate() when rate / parent_rate lands in a gap.

2. Unlike meson_clk_get_pll_range(), which clamps to min and max and returns
   -ENODATA, meson_clk_get_pll_table() returns -EINVAL when rate / parent_rate
   is below the minimum or above the maximum multiplier in the table. When a
   child post-divider runs clk_divider_bestdiv(), any candidate rate * i
   outside the table range fails, and the fallback clk_hw_round_rate(parent, 1)
   also fails with -EINVAL because 1 / parent_rate is 0, causing
   clk_round_rate() to return 0 Hz.

3. meson_clk_get_pll_table() no longer checks __pll_round_closest_mult(pll)
   (for example on axg_hifi_pll_dco when fractional mode is not used), and
   leaves ret unmodified in the loop with an unreachable return 0 at the end.

Should meson_clk_get_pll_table() retain the best-match and clamping behavior
from meson_clk_get_pll_settings()?

> diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
> index bc029c6ab622d..0c9ba079bc51a 100644
> --- a/drivers/clk/meson/meson8b.c
> +++ b/drivers/clk/meson/meson8b.c

[ ... ]

> @@ -185,15 +209,38 @@ static struct clk_regmap meson8b_fixed_pll = {
>  	},
>  };
>  
> +static const struct clk_div_table meson8b_prediv_div_table[] = {
> +	{ .val = 1, .div = 1 },
> +	{ /* sentinel */ }
> +};
> +
> +static struct clk_regmap meson8b_hdmi_prediv = {
> +	.data = &(struct clk_regmap_div_data){
> +		.offset = HHI_VID_PLL_CNTL,
> +		.shift   = 10,
> +		.width   = 5,
> +		.table = meson8b_prediv_div_table,
> +		.flags = CLK_DIVIDER_INVALID_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data){
> +		.name = "hdmi_prediv",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "xtal",
> +			.index = -1,
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
>  static struct clk_fixed_factor hdmi_pll_dco_in = {
>  	.mult = 2,
>  	.div = 1,
>  	.hw.init = &(struct clk_init_data){
>  		.name = "hdmi_pll_dco_in",
>  		.ops = &clk_fixed_factor_ops,
> -		.parent_data = &(const struct clk_parent_data) {
> -			.fw_name = "xtal",
> -			.index = -1,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&meson8b_hdmi_prediv.hw
>  		},
>  		.num_parents = 1,
>  	},

[ ... ]

> @@ -279,6 +321,7 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
>  			&hdmi_pll_dco_in.hw
>  		},
>  		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
>  	},
>  };

[Severity: High]
Can rate requests from meson8b_hdmi_pll_dco reach meson8b_hdmi_prediv without
CLK_SET_RATE_PARENT on hdmi_pll_dco_in?

meson8b_hdmi_pll_dco has hdmi_pll_dco_in as its parent, and hdmi_pll_dco_in
has meson8b_hdmi_prediv as its parent:

  meson8b_hdmi_prediv -> hdmi_pll_dco_in -> meson8b_hdmi_pll_dco

Because hdmi_pll_dco_in does not set CLK_SET_RATE_PARENT, when
meson8b_hdmi_prediv resets to 0 and reports a 0 Hz rate via
CLK_DIVIDER_INVALID_ZERO, meson_clk_pll_determine_rate() calls
clk_hw_round_rate() on hdmi_pll_dco_in, and clk_factor_determine_rate()
returns 0 without rounding meson8b_hdmi_prediv.

This causes meson_clk_pll_determine_rate() to return -EINVAL and prevents
clk_calc_new_rates() from propagating rate changes to program
meson8b_hdmi_prediv. Should hdmi_pll_dco_in also have
.flags = CLK_SET_RATE_PARENT?

> diff --git a/drivers/clk/meson/t7-pll.c b/drivers/clk/meson/t7-pll.c
> index 0a622f45fa368..28d563fcb1bd2 100644
> --- a/drivers/clk/meson/t7-pll.c
> +++ b/drivers/clk/meson/t7-pll.c

[ ... ]

> @@ -292,6 +346,24 @@ static struct clk_regmap t7_hifi_pll = {
>  	},
>  };
>  
> +static struct clk_regmap t7_pcie_prediv = {
> +	.data = &(struct clk_regmap_div_data){
> +		.offset = PCIEPLL_CTRL0,
> +		.shift = 10,
> +		.width = 5,
> +		.table = t7_prediv_div_table,
> +		.flags = CLK_DIVIDER_INVALID_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "pcie_prediv",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "in0",
> +		},
> +		.num_parents = 1,
> +	},
> +};

[ ... ]

> @@ -345,10 +412,11 @@ static struct clk_regmap t7_pcie_pll_dco = {
>  	.hw.init = &(struct clk_init_data){
>  		.name = "pcie_pll_dco",
>  		.ops = &meson_clk_pcie_pll_ops,
> -		.parent_data = &(const struct clk_parent_data) {
> -			.fw_name = "in0",
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&t7_pcie_prediv.hw
>  		},
>  		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
>  	},
>  };

[Severity: High]
Will t7_pcie_prediv (as well as g12a_hdmi_prediv, gxbb_hdmi_prediv, and
gxl_hdmi_prediv) remain stuck at a cached rate of 0 Hz if the hardware
pre-divider field is 0 at clock registration time?

t7_pcie_pll_dco uses meson_clk_pcie_pll_ops and defines neither .table nor
.range; it only programs PCIEPLL_CTRL0 (including N = 1) inside
meson_clk_pcie_pll_enable(). If PCIEPLL_CTRL0 has N = 0 at registration,
t7_pcie_prediv caches a rate of 0 Hz via CLK_DIVIDER_INVALID_ZERO. Because
clk_set_rate() cannot be used on t7_pcie_pll_dco (meson_clk_get_pll_max_multiplier()
returns 0 when neither .range nor .table is set) and t7_pcie_prediv does not
have CLK_GET_RATE_NOCACHE, t7_pcie_prediv->rate is never recalculated after
meson_clk_pcie_pll_enable() writes PCIEPLL_CTRL0.

Similarly, in drivers/clk/meson/g12a.c (g12a_hdmi_prediv) and
drivers/clk/meson/gxbb.c (gxbb_hdmi_prediv and gxl_hdmi_prediv), the HDMI PLL
registers are written directly by the DRM driver in
drivers/gpu/drm/meson/meson_vclk.c rather than through clk_set_rate(). While
hdmi_pll_dco has CLK_GET_RATE_NOCACHE, clk_core_get_rate_recalc() only
recalculates downward from the clock with CLK_GET_RATE_NOCACHE using
core->parent->rate. Because the parent hdmi_prediv clocks lack
CLK_GET_RATE_NOCACHE, their cached rate stays 0 Hz even after the display
driver programs N = 1, causing meson_clk_pll_recalc_rate() on hdmi_pll_dco to
return 0 Hz.

Should pre-dividers whose registers are programmed outside clk_set_rate() be
marked with CLK_GET_RATE_NOCACHE?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029-meson_refactor_n-v2-0-ff4ac74e527b@amlogic.com?part=3

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

* Re: [PATCH RFC RFT v2 3/3] clk: meson: pll: Remove the dedicated n parameter
@ 2026-10-29  9:57     ` sashiko-bot
  0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-29  9:57 UTC (permalink / raw)
  To: Jian Hu
  Cc: linux-amlogic, linux-clk, devicetree, robh, conor+dt,
	neil.armstrong

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] clk: meson: pll: CLK_SET_RATE_PARENT on ro_ops PLL DCOs breaks rate rounding
- [High] clk: meson: pll: meson_clk_get_pll_table() fails on non-exact multipliers
- [High] clk: meson: meson8b: missing CLK_SET_RATE_PARENT on hdmi_pll_dco_in
- [High] clk: meson: pll: cached 0 Hz pre-divider rate on directly programmed PLLs
--

commit b0439d678a94dfead2cca909902c6363ae03f5d0
Author: Jian Hu <jian.hu@amlogic.com>

clk: meson: pll: Remove the dedicated n parameter

This commit removes the dedicated pre-divider (n) field from
struct meson_clk_pll_data and simplifies the Meson PLL rate calculation.
Each PLL pre-divider is instead modeled as a standalone divider clock that
serves as the parent of the PLL DCO clock.

> diff --git a/drivers/clk/meson/axg.c b/drivers/clk/meson/axg.c
> index 280842e9cb371..e6e6b9a8030c0 100644
> --- a/drivers/clk/meson/axg.c
> +++ b/drivers/clk/meson/axg.c

[ ... ]

> @@ -202,10 +240,11 @@ static struct clk_regmap axg_sys_pll_dco = {
>  	.hw.init = &(struct clk_init_data){
>  		.name = "sys_pll_dco",
>  		.ops = &meson_clk_pll_ro_ops,
> -		.parent_data = &(const struct clk_parent_data) {
> -			.fw_name = "xtal",
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&axg_sys_prediv.hw
>  		},
>  		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
>  	},
>  };

[Severity: High]
Does setting CLK_SET_RATE_PARENT on a clock using meson_clk_pll_ro_ops break
rate rounding and rate calculation in the common clock framework?

meson_clk_pll_ro_ops only provides .init, .recalc_rate, and .is_enabled, and
does not implement .determine_rate or .round_rate, so clk_core_can_round() is
false.

In drivers/clk/clk.c, clk_core_round_rate_nolock() checks:

	if (clk_core_can_round(core))
		return core->ops->determine_rate(core->hw, req);
	else if (core->flags & CLK_SET_RATE_PARENT)
		return clk_core_round_rate_nolock(core->parent, req);

When CLK_SET_RATE_PARENT is set on a clock that cannot round its own rate,
clk_core_round_rate_nolock() and clk_calc_new_rates() treat the clock as a
1:1 pass-through of its parent and set the rate to the parent pre-divider
rate (24 MHz or 48 MHz) instead of the multi-GHz DCO frequency.

This affects child dividers with CLK_SET_RATE_PARENT when consumer drivers
call clk_round_rate() or clk_set_rate(). The same pattern is also added on:

  - sm1_gp1_pll_dco and g12a_hdmi_pll_dco in drivers/clk/meson/g12a.c
  - gxbb_hdmi_pll_dco, gxl_hdmi_pll_dco, and gxbb_sys_pll_dco in
    drivers/clk/meson/gxbb.c
  - s4_fixed_pll_dco in drivers/clk/meson/s4-pll.c

Should these meson_clk_pll_ro_ops DCO clocks omit CLK_SET_RATE_PARENT (and
use clk_regmap_divider_ro_ops for their pre-dividers)?

> diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c
> index 1ea6579a760f3..a7e3f8c8eb783 100644
> --- a/drivers/clk/meson/clk-pll.c
> +++ b/drivers/clk/meson/clk-pll.c

[ ... ]

> +static int meson_clk_get_pll_table(unsigned long rate,
> +				   unsigned long parent_rate,
> +				   unsigned int *m,
> +				   struct meson_clk_pll_data *pll)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0, ret = 0; !ret; i++) {
> +		if (!pll->table[i].m)
> +			return -EINVAL;
> +
> +		if (pll->table[i].m == rate / parent_rate) {
> +			*m = pll->table[i].m;
> +			return 0;
> +		}
> +	}
> +
> +	return 0;
> +}

[Severity: High]
What happens in meson_clk_get_pll_table() when rate / parent_rate does not
exactly match an entry in pll->table?

Previously, meson_clk_get_pll_settings() iterated over pll->table and used
meson_clk_pll_is_better() to pick either the closest entry (when
__pll_round_closest_mult(pll) was true) or the largest table entry with a
rate less than or equal to the requested rate.

With this change, meson_clk_get_pll_table() requires an exact match for
rate / parent_rate and returns -EINVAL otherwise:

1. Sparse multiplier tables such as sys_pll_params_table (which has a gap
   between m = 68 and m = 84 where Meson8 CPU OPPs at 1800 MHz and 1992 MHz
   fall) and meson8b_hdmi_pll_params_table in drivers/clk/meson/meson8b.c
   will fail with -EINVAL in meson_clk_pll_determine_rate() and
   meson_clk_pll_set_rate() when rate / parent_rate lands in a gap.

2. Unlike meson_clk_get_pll_range(), which clamps to min and max and returns
   -ENODATA, meson_clk_get_pll_table() returns -EINVAL when rate / parent_rate
   is below the minimum or above the maximum multiplier in the table. When a
   child post-divider runs clk_divider_bestdiv(), any candidate rate * i
   outside the table range fails, and the fallback clk_hw_round_rate(parent, 1)
   also fails with -EINVAL because 1 / parent_rate is 0, causing
   clk_round_rate() to return 0 Hz.

3. meson_clk_get_pll_table() no longer checks __pll_round_closest_mult(pll)
   (for example on axg_hifi_pll_dco when fractional mode is not used), and
   leaves ret unmodified in the loop with an unreachable return 0 at the end.

Should meson_clk_get_pll_table() retain the best-match and clamping behavior
from meson_clk_get_pll_settings()?

> diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c
> index bc029c6ab622d..0c9ba079bc51a 100644
> --- a/drivers/clk/meson/meson8b.c
> +++ b/drivers/clk/meson/meson8b.c

[ ... ]

> @@ -185,15 +209,38 @@ static struct clk_regmap meson8b_fixed_pll = {
>  	},
>  };
>  
> +static const struct clk_div_table meson8b_prediv_div_table[] = {
> +	{ .val = 1, .div = 1 },
> +	{ /* sentinel */ }
> +};
> +
> +static struct clk_regmap meson8b_hdmi_prediv = {
> +	.data = &(struct clk_regmap_div_data){
> +		.offset = HHI_VID_PLL_CNTL,
> +		.shift   = 10,
> +		.width   = 5,
> +		.table = meson8b_prediv_div_table,
> +		.flags = CLK_DIVIDER_INVALID_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data){
> +		.name = "hdmi_prediv",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "xtal",
> +			.index = -1,
> +		},
> +		.num_parents = 1,
> +	},
> +};
> +
>  static struct clk_fixed_factor hdmi_pll_dco_in = {
>  	.mult = 2,
>  	.div = 1,
>  	.hw.init = &(struct clk_init_data){
>  		.name = "hdmi_pll_dco_in",
>  		.ops = &clk_fixed_factor_ops,
> -		.parent_data = &(const struct clk_parent_data) {
> -			.fw_name = "xtal",
> -			.index = -1,
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&meson8b_hdmi_prediv.hw
>  		},
>  		.num_parents = 1,
>  	},

[ ... ]

> @@ -279,6 +321,7 @@ static struct clk_regmap meson8b_hdmi_pll_dco = {
>  			&hdmi_pll_dco_in.hw
>  		},
>  		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
>  	},
>  };

[Severity: High]
Can rate requests from meson8b_hdmi_pll_dco reach meson8b_hdmi_prediv without
CLK_SET_RATE_PARENT on hdmi_pll_dco_in?

meson8b_hdmi_pll_dco has hdmi_pll_dco_in as its parent, and hdmi_pll_dco_in
has meson8b_hdmi_prediv as its parent:

  meson8b_hdmi_prediv -> hdmi_pll_dco_in -> meson8b_hdmi_pll_dco

Because hdmi_pll_dco_in does not set CLK_SET_RATE_PARENT, when
meson8b_hdmi_prediv resets to 0 and reports a 0 Hz rate via
CLK_DIVIDER_INVALID_ZERO, meson_clk_pll_determine_rate() calls
clk_hw_round_rate() on hdmi_pll_dco_in, and clk_factor_determine_rate()
returns 0 without rounding meson8b_hdmi_prediv.

This causes meson_clk_pll_determine_rate() to return -EINVAL and prevents
clk_calc_new_rates() from propagating rate changes to program
meson8b_hdmi_prediv. Should hdmi_pll_dco_in also have
.flags = CLK_SET_RATE_PARENT?

> diff --git a/drivers/clk/meson/t7-pll.c b/drivers/clk/meson/t7-pll.c
> index 0a622f45fa368..28d563fcb1bd2 100644
> --- a/drivers/clk/meson/t7-pll.c
> +++ b/drivers/clk/meson/t7-pll.c

[ ... ]

> @@ -292,6 +346,24 @@ static struct clk_regmap t7_hifi_pll = {
>  	},
>  };
>  
> +static struct clk_regmap t7_pcie_prediv = {
> +	.data = &(struct clk_regmap_div_data){
> +		.offset = PCIEPLL_CTRL0,
> +		.shift = 10,
> +		.width = 5,
> +		.table = t7_prediv_div_table,
> +		.flags = CLK_DIVIDER_INVALID_ZERO,
> +	},
> +	.hw.init = &(struct clk_init_data) {
> +		.name = "pcie_prediv",
> +		.ops = &clk_regmap_divider_ops,
> +		.parent_data = &(const struct clk_parent_data) {
> +			.fw_name = "in0",
> +		},
> +		.num_parents = 1,
> +	},
> +};

[ ... ]

> @@ -345,10 +412,11 @@ static struct clk_regmap t7_pcie_pll_dco = {
>  	.hw.init = &(struct clk_init_data){
>  		.name = "pcie_pll_dco",
>  		.ops = &meson_clk_pcie_pll_ops,
> -		.parent_data = &(const struct clk_parent_data) {
> -			.fw_name = "in0",
> +		.parent_hws = (const struct clk_hw *[]) {
> +			&t7_pcie_prediv.hw
>  		},
>  		.num_parents = 1,
> +		.flags = CLK_SET_RATE_PARENT,
>  	},
>  };

[Severity: High]
Will t7_pcie_prediv (as well as g12a_hdmi_prediv, gxbb_hdmi_prediv, and
gxl_hdmi_prediv) remain stuck at a cached rate of 0 Hz if the hardware
pre-divider field is 0 at clock registration time?

t7_pcie_pll_dco uses meson_clk_pcie_pll_ops and defines neither .table nor
.range; it only programs PCIEPLL_CTRL0 (including N = 1) inside
meson_clk_pcie_pll_enable(). If PCIEPLL_CTRL0 has N = 0 at registration,
t7_pcie_prediv caches a rate of 0 Hz via CLK_DIVIDER_INVALID_ZERO. Because
clk_set_rate() cannot be used on t7_pcie_pll_dco (meson_clk_get_pll_max_multiplier()
returns 0 when neither .range nor .table is set) and t7_pcie_prediv does not
have CLK_GET_RATE_NOCACHE, t7_pcie_prediv->rate is never recalculated after
meson_clk_pcie_pll_enable() writes PCIEPLL_CTRL0.

Similarly, in drivers/clk/meson/g12a.c (g12a_hdmi_prediv) and
drivers/clk/meson/gxbb.c (gxbb_hdmi_prediv and gxl_hdmi_prediv), the HDMI PLL
registers are written directly by the DRM driver in
drivers/gpu/drm/meson/meson_vclk.c rather than through clk_set_rate(). While
hdmi_pll_dco has CLK_GET_RATE_NOCACHE, clk_core_get_rate_recalc() only
recalculates downward from the clock with CLK_GET_RATE_NOCACHE using
core->parent->rate. Because the parent hdmi_prediv clocks lack
CLK_GET_RATE_NOCACHE, their cached rate stays 0 Hz even after the display
driver programs N = 1, causing meson_clk_pll_recalc_rate() on hdmi_pll_dco to
return 0 Hz.

Should pre-dividers whose registers are programmed outside clk_set_rate() be
marked with CLK_GET_RATE_NOCACHE?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029-meson_refactor_n-v2-0-ff4ac74e527b@amlogic.com?part=3

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

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

end of thread, other threads:[~2026-10-29  9:57 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29  9:30 [PATCH RFC RFT v2 0/3] clk: meson: Refactor PLL pre-divider as a divider clock Jian Hu
2026-10-29  9:30 ` Jian Hu via B4 Relay
2026-10-29  9:30 ` Jian Hu via B4 Relay
2026-10-29  9:30 ` [PATCH RFC RFT v2 1/3] dt-bindings: clock: amlogic: Add PLL pre-divider clock IDs Jian Hu
2026-10-29  9:30   ` Jian Hu via B4 Relay
2026-10-29  9:30   ` Jian Hu via B4 Relay
2026-10-29  9:30 ` [PATCH RFC RFT v2 2/3] clk: divider: add CLK_DIVIDER_INVALID_ZERO flag Jian Hu
2026-10-29  9:30   ` Jian Hu via B4 Relay
2026-10-29  9:30   ` Jian Hu via B4 Relay
2026-10-29  9:47   ` sashiko-bot
2026-10-29  9:47     ` sashiko-bot
2026-10-29  9:30 ` [PATCH RFC RFT v2 3/3] clk: meson: pll: Remove the dedicated n parameter Jian Hu
2026-10-29  9:30   ` Jian Hu via B4 Relay
2026-10-29  9:30   ` Jian Hu via B4 Relay
2026-10-29  9:57   ` sashiko-bot
2026-10-29  9:57     ` sashiko-bot

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.