* [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.