* [PATCH v8 0/2] Nuvoton WPCM450 clock and reset driver
@ 2023-04-28 19:02 ` Jonathan Neuschäfer
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-04-28 19:02 UTC (permalink / raw)
To: linux-clk, openbmc
Cc: linux-kernel, linux-watchdog, devicetree,
Jonathan Neuschäfer, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Avi Fishman, Tomer Maimon,
Tali Perry, Patrick Venture, Nancy Yuen, Benjamin Fair,
Daniel Lezcano, Thomas Gleixner, Philipp Zabel, Wim Van Sebroeck,
Guenter Roeck, Christophe JAILLET
This series adds support for the clock and reset controller in the Nuvoton
WPCM450 SoC. This means that the clock rates for peripherals will be calculated
automatically based on the clock tree as it was preconfigured by the bootloader.
The 24 MHz dummy clock, that is currently in the devicetree, is no longer needed.
Somewhat unfortunately, this also means that there is a breaking change once
the devicetree starts relying on the clock driver, but I find it acceptable in
this case, because WPCM450 is still at a somewhat early stage.
v8:
- Use %pe throughout the driver
v7:
- Simplified the error handling, by largely removing resource
deallocation, which:
- was already incomplete
- would only happen in a case when the system is in pretty bad state
because the clock driver didn't initialize correctly (in other
words, the clock driver isn't optional enough that complex error
handling really pays off)
v6:
- Dropped all patches except the clock binding and the clock driver, because
they have mostly been merged
- Minor correction to how RESET_SIMPLE is selected
v5:
- Dropped patch 2 (watchdog: npcm: Enable clock if provided), which
was since merged upstream
- Added patch 2 (clocksource: timer-npcm7xx: Enable timer 1 clock before use) again,
because I wasn't able to find it in linux-next
- Switched the driver to using struct clk_parent_data
- Rebased on 6.1-rc3
v4:
- Leave WDT clock running during after restart handler
- Fix reset controller initialization
- Dropped patch 2/7 (clocksource: timer-npcm7xx: Enable timer 1 clock before use),
as it was applied by Daniel Lezcano
v3:
- https://lore.kernel.org/lkml/20220508194333.2170161-1-j.neuschaefer@gmx.net/
- Changed "refclk" string to "ref"
- Fixed some dead code in the driver
- Added clk_prepare_enable call to the watchdog restart handler
- Added a few review tags
v2:
- https://lore.kernel.org/lkml/20220429172030.398011-1-j.neuschaefer@gmx.net/
- various small improvements
v1:
- https://lore.kernel.org/lkml/20220422183012.444674-1-j.neuschaefer@gmx.net/
Jonathan Neuschäfer (2):
dt-bindings: clock: Add Nuvoton WPCM450 clock/reset controller
clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
.../bindings/clock/nuvoton,wpcm450-clk.yaml | 66 ++++
drivers/clk/Makefile | 1 +
drivers/clk/clk-wpcm450.c | 374 ++++++++++++++++++
drivers/reset/Kconfig | 2 +-
.../dt-bindings/clock/nuvoton,wpcm450-clk.h | 67 ++++
5 files changed, 509 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
create mode 100644 drivers/clk/clk-wpcm450.c
create mode 100644 include/dt-bindings/clock/nuvoton,wpcm450-clk.h
--
2.39.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v8 0/2] Nuvoton WPCM450 clock and reset driver
@ 2023-04-28 19:02 ` Jonathan Neuschäfer
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-04-28 19:02 UTC (permalink / raw)
To: linux-clk, openbmc
Cc: devicetree, Wim Van Sebroeck, linux-watchdog, Stephen Boyd,
Patrick Venture, Michael Turquette, Daniel Lezcano, linux-kernel,
Jonathan Neuschäfer, Avi Fishman, Rob Herring,
Christophe JAILLET, Benjamin Fair, Philipp Zabel,
Krzysztof Kozlowski, Tali Perry, Thomas Gleixner, Guenter Roeck,
Tomer Maimon
This series adds support for the clock and reset controller in the Nuvoton
WPCM450 SoC. This means that the clock rates for peripherals will be calculated
automatically based on the clock tree as it was preconfigured by the bootloader.
The 24 MHz dummy clock, that is currently in the devicetree, is no longer needed.
Somewhat unfortunately, this also means that there is a breaking change once
the devicetree starts relying on the clock driver, but I find it acceptable in
this case, because WPCM450 is still at a somewhat early stage.
v8:
- Use %pe throughout the driver
v7:
- Simplified the error handling, by largely removing resource
deallocation, which:
- was already incomplete
- would only happen in a case when the system is in pretty bad state
because the clock driver didn't initialize correctly (in other
words, the clock driver isn't optional enough that complex error
handling really pays off)
v6:
- Dropped all patches except the clock binding and the clock driver, because
they have mostly been merged
- Minor correction to how RESET_SIMPLE is selected
v5:
- Dropped patch 2 (watchdog: npcm: Enable clock if provided), which
was since merged upstream
- Added patch 2 (clocksource: timer-npcm7xx: Enable timer 1 clock before use) again,
because I wasn't able to find it in linux-next
- Switched the driver to using struct clk_parent_data
- Rebased on 6.1-rc3
v4:
- Leave WDT clock running during after restart handler
- Fix reset controller initialization
- Dropped patch 2/7 (clocksource: timer-npcm7xx: Enable timer 1 clock before use),
as it was applied by Daniel Lezcano
v3:
- https://lore.kernel.org/lkml/20220508194333.2170161-1-j.neuschaefer@gmx.net/
- Changed "refclk" string to "ref"
- Fixed some dead code in the driver
- Added clk_prepare_enable call to the watchdog restart handler
- Added a few review tags
v2:
- https://lore.kernel.org/lkml/20220429172030.398011-1-j.neuschaefer@gmx.net/
- various small improvements
v1:
- https://lore.kernel.org/lkml/20220422183012.444674-1-j.neuschaefer@gmx.net/
Jonathan Neuschäfer (2):
dt-bindings: clock: Add Nuvoton WPCM450 clock/reset controller
clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
.../bindings/clock/nuvoton,wpcm450-clk.yaml | 66 ++++
drivers/clk/Makefile | 1 +
drivers/clk/clk-wpcm450.c | 374 ++++++++++++++++++
drivers/reset/Kconfig | 2 +-
.../dt-bindings/clock/nuvoton,wpcm450-clk.h | 67 ++++
5 files changed, 509 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
create mode 100644 drivers/clk/clk-wpcm450.c
create mode 100644 include/dt-bindings/clock/nuvoton,wpcm450-clk.h
--
2.39.2
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v8 1/2] dt-bindings: clock: Add Nuvoton WPCM450 clock/reset controller
2023-04-28 19:02 ` Jonathan Neuschäfer
@ 2023-04-28 19:02 ` Jonathan Neuschäfer
-1 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-04-28 19:02 UTC (permalink / raw)
To: linux-clk, openbmc
Cc: linux-kernel, linux-watchdog, devicetree,
Jonathan Neuschäfer, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Avi Fishman, Tomer Maimon,
Tali Perry, Patrick Venture, Nancy Yuen, Benjamin Fair,
Daniel Lezcano, Thomas Gleixner, Philipp Zabel, Wim Van Sebroeck,
Guenter Roeck, Christophe JAILLET, Krzysztof Kozlowski,
Krzysztof Kozlowski
The Nuvoton WPCM450 SoC has a combined clock and reset controller.
Add a devicetree binding for it, as well as definitions for the bit
numbers used by it.
Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
v5-v8:
- no changes
v4:
- https://lore.kernel.org/lkml/20220610072141.347795-4-j.neuschaefer@gmx.net/
- Add R-b tag
v3:
- Change clock-output-names and clock-names from "refclk" to "ref", suggested
by Krzysztof Kozlowski
v2:
- https://lore.kernel.org/lkml/20220429172030.398011-5-j.neuschaefer@gmx.net/
- Various improvements, suggested by Krzysztof Kozlowski
v1:
- https://lore.kernel.org/lkml/20220422183012.444674-5-j.neuschaefer@gmx.net/
---
.../bindings/clock/nuvoton,wpcm450-clk.yaml | 66 ++++++++++++++++++
.../dt-bindings/clock/nuvoton,wpcm450-clk.h | 67 +++++++++++++++++++
2 files changed, 133 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
create mode 100644 include/dt-bindings/clock/nuvoton,wpcm450-clk.h
diff --git a/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
new file mode 100644
index 0000000000000..525024a58df4c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/nuvoton,wpcm450-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton WPCM450 clock controller
+
+maintainers:
+ - Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+
+description:
+ The clock controller of the Nuvoton WPCM450 SoC supplies clocks and resets to
+ the rest of the chip.
+
+properties:
+ compatible:
+ const: nuvoton,wpcm450-clk
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Reference clock oscillator (should be 48 MHz)
+
+ clock-names:
+ items:
+ - const: ref
+
+ '#clock-cells':
+ const: 1
+
+ '#reset-cells':
+ const: 1
+
+additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - '#clock-cells'
+
+examples:
+ - |
+ #include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ refclk: clock-48mhz {
+ /* 48 MHz reference oscillator */
+ compatible = "fixed-clock";
+ clock-output-names = "ref";
+ clock-frequency = <48000000>;
+ #clock-cells = <0>;
+ };
+
+ clk: clock-controller@b0000200 {
+ reg = <0xb0000200 0x100>;
+ compatible = "nuvoton,wpcm450-clk";
+ clocks = <&refclk>;
+ clock-names = "ref";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
diff --git a/include/dt-bindings/clock/nuvoton,wpcm450-clk.h b/include/dt-bindings/clock/nuvoton,wpcm450-clk.h
new file mode 100644
index 0000000000000..86e1c895921b7
--- /dev/null
+++ b/include/dt-bindings/clock/nuvoton,wpcm450-clk.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H
+#define _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H
+
+/* Clocks based on CLKEN bits */
+#define WPCM450_CLK_FIU 0
+#define WPCM450_CLK_XBUS 1
+#define WPCM450_CLK_KCS 2
+#define WPCM450_CLK_SHM 4
+#define WPCM450_CLK_USB1 5
+#define WPCM450_CLK_EMC0 6
+#define WPCM450_CLK_EMC1 7
+#define WPCM450_CLK_USB0 8
+#define WPCM450_CLK_PECI 9
+#define WPCM450_CLK_AES 10
+#define WPCM450_CLK_UART0 11
+#define WPCM450_CLK_UART1 12
+#define WPCM450_CLK_SMB2 13
+#define WPCM450_CLK_SMB3 14
+#define WPCM450_CLK_SMB4 15
+#define WPCM450_CLK_SMB5 16
+#define WPCM450_CLK_HUART 17
+#define WPCM450_CLK_PWM 18
+#define WPCM450_CLK_TIMER0 19
+#define WPCM450_CLK_TIMER1 20
+#define WPCM450_CLK_TIMER2 21
+#define WPCM450_CLK_TIMER3 22
+#define WPCM450_CLK_TIMER4 23
+#define WPCM450_CLK_MFT0 24
+#define WPCM450_CLK_MFT1 25
+#define WPCM450_CLK_WDT 26
+#define WPCM450_CLK_ADC 27
+#define WPCM450_CLK_SDIO 28
+#define WPCM450_CLK_SSPI 29
+#define WPCM450_CLK_SMB0 30
+#define WPCM450_CLK_SMB1 31
+
+/* Other clocks */
+#define WPCM450_CLK_USBPHY 32
+
+#define WPCM450_NUM_CLKS 33
+
+/* Resets based on IPSRST bits */
+#define WPCM450_RESET_FIU 0
+#define WPCM450_RESET_EMC0 6
+#define WPCM450_RESET_EMC1 7
+#define WPCM450_RESET_USB0 8
+#define WPCM450_RESET_USB1 9
+#define WPCM450_RESET_AES_PECI 10
+#define WPCM450_RESET_UART 11
+#define WPCM450_RESET_MC 12
+#define WPCM450_RESET_SMB2 13
+#define WPCM450_RESET_SMB3 14
+#define WPCM450_RESET_SMB4 15
+#define WPCM450_RESET_SMB5 16
+#define WPCM450_RESET_PWM 18
+#define WPCM450_RESET_TIMER 19
+#define WPCM450_RESET_ADC 27
+#define WPCM450_RESET_SDIO 28
+#define WPCM450_RESET_SSPI 29
+#define WPCM450_RESET_SMB0 30
+#define WPCM450_RESET_SMB1 31
+
+#define WPCM450_NUM_RESETS 32
+
+#endif /* _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H */
--
2.39.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v8 1/2] dt-bindings: clock: Add Nuvoton WPCM450 clock/reset controller
@ 2023-04-28 19:02 ` Jonathan Neuschäfer
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-04-28 19:02 UTC (permalink / raw)
To: linux-clk, openbmc
Cc: Tomer Maimon, Michael Turquette, Tali Perry, Krzysztof Kozlowski,
Benjamin Fair, Daniel Lezcano, Guenter Roeck, devicetree,
linux-watchdog, Jonathan Neuschäfer, Rob Herring,
Christophe JAILLET, Thomas Gleixner, Wim Van Sebroeck,
Stephen Boyd, Patrick Venture, linux-kernel, Avi Fishman,
Krzysztof Kozlowski, Philipp Zabel, Krzysztof Kozlowski
The Nuvoton WPCM450 SoC has a combined clock and reset controller.
Add a devicetree binding for it, as well as definitions for the bit
numbers used by it.
Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
v5-v8:
- no changes
v4:
- https://lore.kernel.org/lkml/20220610072141.347795-4-j.neuschaefer@gmx.net/
- Add R-b tag
v3:
- Change clock-output-names and clock-names from "refclk" to "ref", suggested
by Krzysztof Kozlowski
v2:
- https://lore.kernel.org/lkml/20220429172030.398011-5-j.neuschaefer@gmx.net/
- Various improvements, suggested by Krzysztof Kozlowski
v1:
- https://lore.kernel.org/lkml/20220422183012.444674-5-j.neuschaefer@gmx.net/
---
.../bindings/clock/nuvoton,wpcm450-clk.yaml | 66 ++++++++++++++++++
.../dt-bindings/clock/nuvoton,wpcm450-clk.h | 67 +++++++++++++++++++
2 files changed, 133 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
create mode 100644 include/dt-bindings/clock/nuvoton,wpcm450-clk.h
diff --git a/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
new file mode 100644
index 0000000000000..525024a58df4c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/nuvoton,wpcm450-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton WPCM450 clock controller
+
+maintainers:
+ - Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+
+description:
+ The clock controller of the Nuvoton WPCM450 SoC supplies clocks and resets to
+ the rest of the chip.
+
+properties:
+ compatible:
+ const: nuvoton,wpcm450-clk
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Reference clock oscillator (should be 48 MHz)
+
+ clock-names:
+ items:
+ - const: ref
+
+ '#clock-cells':
+ const: 1
+
+ '#reset-cells':
+ const: 1
+
+additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - '#clock-cells'
+
+examples:
+ - |
+ #include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ refclk: clock-48mhz {
+ /* 48 MHz reference oscillator */
+ compatible = "fixed-clock";
+ clock-output-names = "ref";
+ clock-frequency = <48000000>;
+ #clock-cells = <0>;
+ };
+
+ clk: clock-controller@b0000200 {
+ reg = <0xb0000200 0x100>;
+ compatible = "nuvoton,wpcm450-clk";
+ clocks = <&refclk>;
+ clock-names = "ref";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
diff --git a/include/dt-bindings/clock/nuvoton,wpcm450-clk.h b/include/dt-bindings/clock/nuvoton,wpcm450-clk.h
new file mode 100644
index 0000000000000..86e1c895921b7
--- /dev/null
+++ b/include/dt-bindings/clock/nuvoton,wpcm450-clk.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H
+#define _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H
+
+/* Clocks based on CLKEN bits */
+#define WPCM450_CLK_FIU 0
+#define WPCM450_CLK_XBUS 1
+#define WPCM450_CLK_KCS 2
+#define WPCM450_CLK_SHM 4
+#define WPCM450_CLK_USB1 5
+#define WPCM450_CLK_EMC0 6
+#define WPCM450_CLK_EMC1 7
+#define WPCM450_CLK_USB0 8
+#define WPCM450_CLK_PECI 9
+#define WPCM450_CLK_AES 10
+#define WPCM450_CLK_UART0 11
+#define WPCM450_CLK_UART1 12
+#define WPCM450_CLK_SMB2 13
+#define WPCM450_CLK_SMB3 14
+#define WPCM450_CLK_SMB4 15
+#define WPCM450_CLK_SMB5 16
+#define WPCM450_CLK_HUART 17
+#define WPCM450_CLK_PWM 18
+#define WPCM450_CLK_TIMER0 19
+#define WPCM450_CLK_TIMER1 20
+#define WPCM450_CLK_TIMER2 21
+#define WPCM450_CLK_TIMER3 22
+#define WPCM450_CLK_TIMER4 23
+#define WPCM450_CLK_MFT0 24
+#define WPCM450_CLK_MFT1 25
+#define WPCM450_CLK_WDT 26
+#define WPCM450_CLK_ADC 27
+#define WPCM450_CLK_SDIO 28
+#define WPCM450_CLK_SSPI 29
+#define WPCM450_CLK_SMB0 30
+#define WPCM450_CLK_SMB1 31
+
+/* Other clocks */
+#define WPCM450_CLK_USBPHY 32
+
+#define WPCM450_NUM_CLKS 33
+
+/* Resets based on IPSRST bits */
+#define WPCM450_RESET_FIU 0
+#define WPCM450_RESET_EMC0 6
+#define WPCM450_RESET_EMC1 7
+#define WPCM450_RESET_USB0 8
+#define WPCM450_RESET_USB1 9
+#define WPCM450_RESET_AES_PECI 10
+#define WPCM450_RESET_UART 11
+#define WPCM450_RESET_MC 12
+#define WPCM450_RESET_SMB2 13
+#define WPCM450_RESET_SMB3 14
+#define WPCM450_RESET_SMB4 15
+#define WPCM450_RESET_SMB5 16
+#define WPCM450_RESET_PWM 18
+#define WPCM450_RESET_TIMER 19
+#define WPCM450_RESET_ADC 27
+#define WPCM450_RESET_SDIO 28
+#define WPCM450_RESET_SSPI 29
+#define WPCM450_RESET_SMB0 30
+#define WPCM450_RESET_SMB1 31
+
+#define WPCM450_NUM_RESETS 32
+
+#endif /* _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H */
--
2.39.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
2023-04-28 19:02 ` Jonathan Neuschäfer
@ 2023-04-28 19:02 ` Jonathan Neuschäfer
-1 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-04-28 19:02 UTC (permalink / raw)
To: linux-clk, openbmc
Cc: linux-kernel, linux-watchdog, devicetree,
Jonathan Neuschäfer, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Avi Fishman, Tomer Maimon,
Tali Perry, Patrick Venture, Nancy Yuen, Benjamin Fair,
Daniel Lezcano, Thomas Gleixner, Philipp Zabel, Wim Van Sebroeck,
Guenter Roeck, Christophe JAILLET, Joel Stanley
This driver implements the following features w.r.t. the clock and reset
controller in the WPCM450 SoC:
- It calculates the rates for all clocks managed by the clock controller
- It leaves the clock tree mostly unchanged, except that it enables/
disables clock gates based on usage.
- It exposes the reset lines managed by the controller using the
Generic Reset Controller subsystem
NOTE: If the driver and the corresponding devicetree node are present,
the driver will disable "unused" clocks. This is problem until
the clock relations are properly declared in the devicetree (in a
later patch). Until then, the clk_ignore_unused kernel parameter
can be used as a workaround.
Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Reviewed-by: Joel Stanley <joel@jms.id.au>
---
v8:
- Use %pe format specifier throughout the driver, as suggested by Philipp Zabel
- Add Joel's R-b
v7:
- https://lore.kernel.org/lkml/20230422220240.322572-3-j.neuschaefer@gmx.net/
- Simplify error handling by not deallocating resources
v6:
- Enable RESET_SIMPLE based on ARCH_WPCM450, not ARCH_NPCM, as suggested by Tomer Maimon
v5:
- https://lore.kernel.org/lkml/20221104161850.2889894-6-j.neuschaefer@gmx.net/
- Switch to using clk_parent_data
v4:
- Fix reset controller initialization
v3:
- Change reference clock name from "refclk" to "ref"
- Remove unused variable in return path of wpcm450_clk_register_pll
- Remove unused divisor tables
v2:
- no changes
---
drivers/clk/Makefile | 1 +
drivers/clk/clk-wpcm450.c | 374 ++++++++++++++++++++++++++++++++++++++
drivers/reset/Kconfig | 2 +-
3 files changed, 376 insertions(+), 1 deletion(-)
create mode 100644 drivers/clk/clk-wpcm450.c
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e3ca0d058a256..b58352d4d615d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_COMMON_CLK_RS9_PCIE) += clk-renesas-pcie.o
obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o
obj-$(CONFIG_COMMON_CLK_VC7) += clk-versaclock7.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
+obj-$(CONFIG_ARCH_WPCM450) += clk-wpcm450.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
# please keep this section sorted lexicographically by directory path name
diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
new file mode 100644
index 0000000000000..358be5befa887
--- /dev/null
+++ b/drivers/clk/clk-wpcm450.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton WPCM450 clock and reset controller driver.
+ *
+ * Copyright (C) 2022 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/reset-controller.h>
+#include <linux/reset/reset-simple.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
+
+struct wpcm450_clk_pll {
+ struct clk_hw hw;
+ void __iomem *pllcon;
+ u8 flags;
+};
+
+#define to_wpcm450_clk_pll(_hw) container_of(_hw, struct wpcm450_clk_pll, hw)
+
+#define PLLCON_FBDV GENMASK(24, 16)
+#define PLLCON_PRST BIT(13)
+#define PLLCON_PWDEN BIT(12)
+#define PLLCON_OTDV GENMASK(10, 8)
+#define PLLCON_INDV GENMASK(5, 0)
+
+static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+ unsigned long fbdv, indv, otdv;
+ u64 rate;
+ u32 pllcon;
+
+ if (parent_rate == 0) {
+ pr_err("%s: parent rate is zero", __func__);
+ return 0;
+ }
+
+ pllcon = readl_relaxed(pll->pllcon);
+
+ indv = FIELD_GET(PLLCON_INDV, pllcon) + 1;
+ fbdv = FIELD_GET(PLLCON_FBDV, pllcon) + 1;
+ otdv = FIELD_GET(PLLCON_OTDV, pllcon) + 1;
+
+ rate = (u64)parent_rate * fbdv;
+ do_div(rate, indv * otdv);
+
+ return rate;
+}
+
+static int wpcm450_clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+ u32 pllcon;
+
+ pllcon = readl_relaxed(pll->pllcon);
+
+ return !(pllcon & PLLCON_PRST);
+}
+
+static void wpcm450_clk_pll_disable(struct clk_hw *hw)
+{
+ struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+ u32 pllcon;
+
+ pllcon = readl_relaxed(pll->pllcon);
+ pllcon |= PLLCON_PRST | PLLCON_PWDEN;
+ writel(pllcon, pll->pllcon);
+}
+
+static const struct clk_ops wpcm450_clk_pll_ops = {
+ .recalc_rate = wpcm450_clk_pll_recalc_rate,
+ .is_enabled = wpcm450_clk_pll_is_enabled,
+ .disable = wpcm450_clk_pll_disable
+};
+
+static struct clk_hw *
+wpcm450_clk_register_pll(void __iomem *pllcon, const char *name,
+ const struct clk_parent_data *parent, unsigned long flags)
+{
+ struct wpcm450_clk_pll *pll;
+ struct clk_init_data init = {};
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &wpcm450_clk_pll_ops;
+ init.parent_data = parent;
+ init.num_parents = 1;
+ init.flags = flags;
+
+ pll->pllcon = pllcon;
+ pll->hw.init = &init;
+
+ ret = clk_hw_register(NULL, &pll->hw);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return &pll->hw;
+}
+
+#define REG_CLKEN 0x00
+#define REG_CLKSEL 0x04
+#define REG_CLKDIV 0x08
+#define REG_PLLCON0 0x0c
+#define REG_PLLCON1 0x10
+#define REG_PMCON 0x14
+#define REG_IRQWAKECON 0x18
+#define REG_IRQWAKEFLAG 0x1c
+#define REG_IPSRST 0x20
+
+struct wpcm450_pll_data {
+ const char *name;
+ struct clk_parent_data parent;
+ unsigned int reg;
+ unsigned long flags;
+};
+
+static const struct wpcm450_pll_data pll_data[] = {
+ { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
+ { "pll1", { .name = "ref" }, REG_PLLCON1, 0 },
+};
+
+struct wpcm450_clksel_data {
+ const char *name;
+ const struct clk_parent_data *parents;
+ unsigned int num_parents;
+ const u32 *table;
+ int shift;
+ int width;
+ int index;
+ unsigned long flags;
+};
+
+static const u32 parent_table[] = { 0, 1, 2 };
+
+static const struct clk_parent_data default_parents[] = {
+ { .name = "pll0" },
+ { .name = "pll1" },
+ { .name = "ref" },
+};
+
+static const struct clk_parent_data huart_parents[] = {
+ { .name = "ref" },
+ { .name = "refdiv2" },
+};
+
+static const struct wpcm450_clksel_data clksel_data[] = {
+ { "cpusel", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 0, 2, -1, CLK_IS_CRITICAL },
+ { "clkout", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 2, 2, -1, 0 },
+ { "usbphy", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 6, 2, -1, 0 },
+ { "uartsel", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 8, 2, WPCM450_CLK_USBPHY, 0 },
+ { "huartsel", huart_parents, ARRAY_SIZE(huart_parents),
+ parent_table, 10, 1, -1, 0 },
+};
+
+static const struct clk_div_table div_fixed2[] = {
+ { .val = 0, .div = 2 },
+ { }
+};
+
+struct wpcm450_clkdiv_data {
+ const char *name;
+ struct clk_parent_data parent;
+ int div_flags;
+ const struct clk_div_table *table;
+ int shift;
+ int width;
+ unsigned long flags;
+};
+
+static struct wpcm450_clkdiv_data clkdiv_data_early[] = {
+ { "refdiv2", { .name = "ref" }, 0, div_fixed2, 0, 0 },
+};
+
+static const struct wpcm450_clkdiv_data clkdiv_data[] = {
+ { "cpu", { .name = "cpusel" }, 0, div_fixed2, 0, 0, CLK_IS_CRITICAL },
+ { "adcdiv", { .name = "ref" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 28, 2, 0 },
+ { "apb", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 26, 2, 0 },
+ { "ahb", { .name = "cpu" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 24, 2, 0 },
+ { "uart", { .name = "uartsel" }, 0, NULL, 16, 4, 0 },
+ { "ahb3", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 8, 2, 0 },
+};
+
+struct wpcm450_clken_data {
+ const char *name;
+ struct clk_parent_data parent;
+ int bitnum;
+ unsigned long flags;
+};
+
+static const struct wpcm450_clken_data clken_data[] = {
+ { "fiu", { .name = "ahb3" }, WPCM450_CLK_FIU, 0 },
+ { "xbus", { .name = "ahb3" }, WPCM450_CLK_XBUS, 0 },
+ { "kcs", { .name = "apb" }, WPCM450_CLK_KCS, 0 },
+ { "shm", { .name = "ahb3" }, WPCM450_CLK_SHM, 0 },
+ { "usb1", { .name = "ahb" }, WPCM450_CLK_USB1, 0 },
+ { "emc0", { .name = "ahb" }, WPCM450_CLK_EMC0, 0 },
+ { "emc1", { .name = "ahb" }, WPCM450_CLK_EMC1, 0 },
+ { "usb0", { .name = "ahb" }, WPCM450_CLK_USB0, 0 },
+ { "peci", { .name = "apb" }, WPCM450_CLK_PECI, 0 },
+ { "aes", { .name = "apb" }, WPCM450_CLK_AES, 0 },
+ { "uart0", { .name = "uart" }, WPCM450_CLK_UART0, 0 },
+ { "uart1", { .name = "uart" }, WPCM450_CLK_UART1, 0 },
+ { "smb2", { .name = "apb" }, WPCM450_CLK_SMB2, 0 },
+ { "smb3", { .name = "apb" }, WPCM450_CLK_SMB3, 0 },
+ { "smb4", { .name = "apb" }, WPCM450_CLK_SMB4, 0 },
+ { "smb5", { .name = "apb" }, WPCM450_CLK_SMB5, 0 },
+ { "huart", { .name = "huartsel" }, WPCM450_CLK_HUART, 0 },
+ { "pwm", { .name = "apb" }, WPCM450_CLK_PWM, 0 },
+ { "timer0", { .name = "refdiv2" }, WPCM450_CLK_TIMER0, 0 },
+ { "timer1", { .name = "refdiv2" }, WPCM450_CLK_TIMER1, 0 },
+ { "timer2", { .name = "refdiv2" }, WPCM450_CLK_TIMER2, 0 },
+ { "timer3", { .name = "refdiv2" }, WPCM450_CLK_TIMER3, 0 },
+ { "timer4", { .name = "refdiv2" }, WPCM450_CLK_TIMER4, 0 },
+ { "mft0", { .name = "apb" }, WPCM450_CLK_MFT0, 0 },
+ { "mft1", { .name = "apb" }, WPCM450_CLK_MFT1, 0 },
+ { "wdt", { .name = "refdiv2" }, WPCM450_CLK_WDT, 0 },
+ { "adc", { .name = "adcdiv" }, WPCM450_CLK_ADC, 0 },
+ { "sdio", { .name = "ahb" }, WPCM450_CLK_SDIO, 0 },
+ { "sspi", { .name = "apb" }, WPCM450_CLK_SSPI, 0 },
+ { "smb0", { .name = "apb" }, WPCM450_CLK_SMB0, 0 },
+ { "smb1", { .name = "apb" }, WPCM450_CLK_SMB1, 0 },
+};
+
+static DEFINE_SPINLOCK(wpcm450_clk_lock);
+
+/*
+ * NOTE: Error handling is very rudimentary here. If the clock driver initial-
+ * ization fails, the system is probably in bigger trouble than what is caused
+ * by a few leaked resources.
+ */
+
+static void __init wpcm450_clk_init(struct device_node *clk_np)
+{
+ struct clk_hw_onecell_data *clk_data;
+ static struct clk_hw **hws;
+ static struct clk_hw *hw;
+ void __iomem *clk_base;
+ int i, ret;
+ struct reset_simple_data *reset;
+
+ clk_base = of_iomap(clk_np, 0);
+ if (!clk_base) {
+ pr_err("%pOFP: failed to map registers\n", clk_np);
+ of_node_put(clk_np);
+ return;
+ }
+ of_node_put(clk_np);
+
+ clk_data = kzalloc(struct_size(clk_data, hws, WPCM450_NUM_CLKS), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->num = WPCM450_NUM_CLKS;
+ hws = clk_data->hws;
+
+ for (i = 0; i < WPCM450_NUM_CLKS; i++)
+ hws[i] = ERR_PTR(-ENOENT);
+
+ // PLLs
+ for (i = 0; i < ARRAY_SIZE(pll_data); i++) {
+ const struct wpcm450_pll_data *data = &pll_data[i];
+
+ hw = wpcm450_clk_register_pll(clk_base + data->reg, data->name,
+ &data->parent, data->flags);
+ if (IS_ERR(hw)) {
+ pr_info("Failed to register PLL: %pe", hw);
+ return;
+ }
+ }
+
+ // Early divisors (REF/2)
+ for (i = 0; i < ARRAY_SIZE(clkdiv_data_early); i++) {
+ const struct wpcm450_clkdiv_data *data = &clkdiv_data_early[i];
+
+ hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
+ data->flags, clk_base + REG_CLKDIV,
+ data->shift, data->width,
+ data->div_flags, data->table,
+ &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register div table: %pe\n", hw);
+ return;
+ }
+ }
+
+ // Selects/muxes
+ for (i = 0; i < ARRAY_SIZE(clksel_data); i++) {
+ const struct wpcm450_clksel_data *data = &clksel_data[i];
+
+ hw = clk_hw_register_mux_parent_data(NULL, data->name, data->parents,
+ data->num_parents, data->flags,
+ clk_base + REG_CLKSEL, data->shift,
+ data->width, 0,
+ &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register mux: %pe\n", hw);
+ return;
+ }
+ if (data->index >= 0)
+ clk_data->hws[data->index] = hw;
+ }
+
+ // Divisors
+ for (i = 0; i < ARRAY_SIZE(clkdiv_data); i++) {
+ const struct wpcm450_clkdiv_data *data = &clkdiv_data[i];
+
+ hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
+ data->flags, clk_base + REG_CLKDIV,
+ data->shift, data->width,
+ data->div_flags, data->table,
+ &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register divider: %pe\n", hw);
+ return;
+ }
+ }
+
+ // Enables/gates
+ for (i = 0; i < ARRAY_SIZE(clken_data); i++) {
+ const struct wpcm450_clken_data *data = &clken_data[i];
+
+ hw = clk_hw_register_gate_parent_data(NULL, data->name, &data->parent, data->flags,
+ clk_base + REG_CLKEN, data->bitnum,
+ data->flags, &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register gate: %pe\n", hw);
+ return;
+ }
+ clk_data->hws[data->bitnum] = hw;
+ }
+
+ ret = of_clk_add_hw_provider(clk_np, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ pr_err("Failed to add DT provider: %pe\n", ERR_PTR(ret));
+
+ // Reset controller
+ reset = kzalloc(sizeof(*reset), GFP_KERNEL);
+ if (!reset)
+ return;
+ reset->rcdev.owner = THIS_MODULE;
+ reset->rcdev.nr_resets = WPCM450_NUM_RESETS;
+ reset->rcdev.ops = &reset_simple_ops;
+ reset->rcdev.of_node = clk_np;
+ reset->membase = clk_base + REG_IPSRST;
+ ret = reset_controller_register(&reset->rcdev);
+ if (ret)
+ pr_err("Failed to register reset controller: %pe\n", ERR_PTR(ret));
+
+ of_node_put(clk_np);
+}
+
+CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 2a52c990d4fec..16e111d213560 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -208,7 +208,7 @@ config RESET_SCMI
config RESET_SIMPLE
bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT
- default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
+ default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC || ARCH_WPCM450
depends on HAS_IOMEM
help
This enables a simple reset controller driver for reset lines that
--
2.39.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
@ 2023-04-28 19:02 ` Jonathan Neuschäfer
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-04-28 19:02 UTC (permalink / raw)
To: linux-clk, openbmc
Cc: devicetree, Wim Van Sebroeck, linux-watchdog, Stephen Boyd,
Patrick Venture, Michael Turquette, Daniel Lezcano, Joel Stanley,
linux-kernel, Jonathan Neuschäfer, Avi Fishman, Rob Herring,
Christophe JAILLET, Benjamin Fair, Philipp Zabel,
Krzysztof Kozlowski, Tali Perry, Thomas Gleixner, Guenter Roeck,
Tomer Maimon
This driver implements the following features w.r.t. the clock and reset
controller in the WPCM450 SoC:
- It calculates the rates for all clocks managed by the clock controller
- It leaves the clock tree mostly unchanged, except that it enables/
disables clock gates based on usage.
- It exposes the reset lines managed by the controller using the
Generic Reset Controller subsystem
NOTE: If the driver and the corresponding devicetree node are present,
the driver will disable "unused" clocks. This is problem until
the clock relations are properly declared in the devicetree (in a
later patch). Until then, the clk_ignore_unused kernel parameter
can be used as a workaround.
Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Reviewed-by: Joel Stanley <joel@jms.id.au>
---
v8:
- Use %pe format specifier throughout the driver, as suggested by Philipp Zabel
- Add Joel's R-b
v7:
- https://lore.kernel.org/lkml/20230422220240.322572-3-j.neuschaefer@gmx.net/
- Simplify error handling by not deallocating resources
v6:
- Enable RESET_SIMPLE based on ARCH_WPCM450, not ARCH_NPCM, as suggested by Tomer Maimon
v5:
- https://lore.kernel.org/lkml/20221104161850.2889894-6-j.neuschaefer@gmx.net/
- Switch to using clk_parent_data
v4:
- Fix reset controller initialization
v3:
- Change reference clock name from "refclk" to "ref"
- Remove unused variable in return path of wpcm450_clk_register_pll
- Remove unused divisor tables
v2:
- no changes
---
drivers/clk/Makefile | 1 +
drivers/clk/clk-wpcm450.c | 374 ++++++++++++++++++++++++++++++++++++++
drivers/reset/Kconfig | 2 +-
3 files changed, 376 insertions(+), 1 deletion(-)
create mode 100644 drivers/clk/clk-wpcm450.c
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e3ca0d058a256..b58352d4d615d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_COMMON_CLK_RS9_PCIE) += clk-renesas-pcie.o
obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o
obj-$(CONFIG_COMMON_CLK_VC7) += clk-versaclock7.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
+obj-$(CONFIG_ARCH_WPCM450) += clk-wpcm450.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
# please keep this section sorted lexicographically by directory path name
diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
new file mode 100644
index 0000000000000..358be5befa887
--- /dev/null
+++ b/drivers/clk/clk-wpcm450.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton WPCM450 clock and reset controller driver.
+ *
+ * Copyright (C) 2022 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/reset-controller.h>
+#include <linux/reset/reset-simple.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
+
+struct wpcm450_clk_pll {
+ struct clk_hw hw;
+ void __iomem *pllcon;
+ u8 flags;
+};
+
+#define to_wpcm450_clk_pll(_hw) container_of(_hw, struct wpcm450_clk_pll, hw)
+
+#define PLLCON_FBDV GENMASK(24, 16)
+#define PLLCON_PRST BIT(13)
+#define PLLCON_PWDEN BIT(12)
+#define PLLCON_OTDV GENMASK(10, 8)
+#define PLLCON_INDV GENMASK(5, 0)
+
+static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+ unsigned long fbdv, indv, otdv;
+ u64 rate;
+ u32 pllcon;
+
+ if (parent_rate == 0) {
+ pr_err("%s: parent rate is zero", __func__);
+ return 0;
+ }
+
+ pllcon = readl_relaxed(pll->pllcon);
+
+ indv = FIELD_GET(PLLCON_INDV, pllcon) + 1;
+ fbdv = FIELD_GET(PLLCON_FBDV, pllcon) + 1;
+ otdv = FIELD_GET(PLLCON_OTDV, pllcon) + 1;
+
+ rate = (u64)parent_rate * fbdv;
+ do_div(rate, indv * otdv);
+
+ return rate;
+}
+
+static int wpcm450_clk_pll_is_enabled(struct clk_hw *hw)
+{
+ struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+ u32 pllcon;
+
+ pllcon = readl_relaxed(pll->pllcon);
+
+ return !(pllcon & PLLCON_PRST);
+}
+
+static void wpcm450_clk_pll_disable(struct clk_hw *hw)
+{
+ struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+ u32 pllcon;
+
+ pllcon = readl_relaxed(pll->pllcon);
+ pllcon |= PLLCON_PRST | PLLCON_PWDEN;
+ writel(pllcon, pll->pllcon);
+}
+
+static const struct clk_ops wpcm450_clk_pll_ops = {
+ .recalc_rate = wpcm450_clk_pll_recalc_rate,
+ .is_enabled = wpcm450_clk_pll_is_enabled,
+ .disable = wpcm450_clk_pll_disable
+};
+
+static struct clk_hw *
+wpcm450_clk_register_pll(void __iomem *pllcon, const char *name,
+ const struct clk_parent_data *parent, unsigned long flags)
+{
+ struct wpcm450_clk_pll *pll;
+ struct clk_init_data init = {};
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &wpcm450_clk_pll_ops;
+ init.parent_data = parent;
+ init.num_parents = 1;
+ init.flags = flags;
+
+ pll->pllcon = pllcon;
+ pll->hw.init = &init;
+
+ ret = clk_hw_register(NULL, &pll->hw);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return &pll->hw;
+}
+
+#define REG_CLKEN 0x00
+#define REG_CLKSEL 0x04
+#define REG_CLKDIV 0x08
+#define REG_PLLCON0 0x0c
+#define REG_PLLCON1 0x10
+#define REG_PMCON 0x14
+#define REG_IRQWAKECON 0x18
+#define REG_IRQWAKEFLAG 0x1c
+#define REG_IPSRST 0x20
+
+struct wpcm450_pll_data {
+ const char *name;
+ struct clk_parent_data parent;
+ unsigned int reg;
+ unsigned long flags;
+};
+
+static const struct wpcm450_pll_data pll_data[] = {
+ { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
+ { "pll1", { .name = "ref" }, REG_PLLCON1, 0 },
+};
+
+struct wpcm450_clksel_data {
+ const char *name;
+ const struct clk_parent_data *parents;
+ unsigned int num_parents;
+ const u32 *table;
+ int shift;
+ int width;
+ int index;
+ unsigned long flags;
+};
+
+static const u32 parent_table[] = { 0, 1, 2 };
+
+static const struct clk_parent_data default_parents[] = {
+ { .name = "pll0" },
+ { .name = "pll1" },
+ { .name = "ref" },
+};
+
+static const struct clk_parent_data huart_parents[] = {
+ { .name = "ref" },
+ { .name = "refdiv2" },
+};
+
+static const struct wpcm450_clksel_data clksel_data[] = {
+ { "cpusel", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 0, 2, -1, CLK_IS_CRITICAL },
+ { "clkout", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 2, 2, -1, 0 },
+ { "usbphy", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 6, 2, -1, 0 },
+ { "uartsel", default_parents, ARRAY_SIZE(default_parents),
+ parent_table, 8, 2, WPCM450_CLK_USBPHY, 0 },
+ { "huartsel", huart_parents, ARRAY_SIZE(huart_parents),
+ parent_table, 10, 1, -1, 0 },
+};
+
+static const struct clk_div_table div_fixed2[] = {
+ { .val = 0, .div = 2 },
+ { }
+};
+
+struct wpcm450_clkdiv_data {
+ const char *name;
+ struct clk_parent_data parent;
+ int div_flags;
+ const struct clk_div_table *table;
+ int shift;
+ int width;
+ unsigned long flags;
+};
+
+static struct wpcm450_clkdiv_data clkdiv_data_early[] = {
+ { "refdiv2", { .name = "ref" }, 0, div_fixed2, 0, 0 },
+};
+
+static const struct wpcm450_clkdiv_data clkdiv_data[] = {
+ { "cpu", { .name = "cpusel" }, 0, div_fixed2, 0, 0, CLK_IS_CRITICAL },
+ { "adcdiv", { .name = "ref" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 28, 2, 0 },
+ { "apb", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 26, 2, 0 },
+ { "ahb", { .name = "cpu" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 24, 2, 0 },
+ { "uart", { .name = "uartsel" }, 0, NULL, 16, 4, 0 },
+ { "ahb3", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 8, 2, 0 },
+};
+
+struct wpcm450_clken_data {
+ const char *name;
+ struct clk_parent_data parent;
+ int bitnum;
+ unsigned long flags;
+};
+
+static const struct wpcm450_clken_data clken_data[] = {
+ { "fiu", { .name = "ahb3" }, WPCM450_CLK_FIU, 0 },
+ { "xbus", { .name = "ahb3" }, WPCM450_CLK_XBUS, 0 },
+ { "kcs", { .name = "apb" }, WPCM450_CLK_KCS, 0 },
+ { "shm", { .name = "ahb3" }, WPCM450_CLK_SHM, 0 },
+ { "usb1", { .name = "ahb" }, WPCM450_CLK_USB1, 0 },
+ { "emc0", { .name = "ahb" }, WPCM450_CLK_EMC0, 0 },
+ { "emc1", { .name = "ahb" }, WPCM450_CLK_EMC1, 0 },
+ { "usb0", { .name = "ahb" }, WPCM450_CLK_USB0, 0 },
+ { "peci", { .name = "apb" }, WPCM450_CLK_PECI, 0 },
+ { "aes", { .name = "apb" }, WPCM450_CLK_AES, 0 },
+ { "uart0", { .name = "uart" }, WPCM450_CLK_UART0, 0 },
+ { "uart1", { .name = "uart" }, WPCM450_CLK_UART1, 0 },
+ { "smb2", { .name = "apb" }, WPCM450_CLK_SMB2, 0 },
+ { "smb3", { .name = "apb" }, WPCM450_CLK_SMB3, 0 },
+ { "smb4", { .name = "apb" }, WPCM450_CLK_SMB4, 0 },
+ { "smb5", { .name = "apb" }, WPCM450_CLK_SMB5, 0 },
+ { "huart", { .name = "huartsel" }, WPCM450_CLK_HUART, 0 },
+ { "pwm", { .name = "apb" }, WPCM450_CLK_PWM, 0 },
+ { "timer0", { .name = "refdiv2" }, WPCM450_CLK_TIMER0, 0 },
+ { "timer1", { .name = "refdiv2" }, WPCM450_CLK_TIMER1, 0 },
+ { "timer2", { .name = "refdiv2" }, WPCM450_CLK_TIMER2, 0 },
+ { "timer3", { .name = "refdiv2" }, WPCM450_CLK_TIMER3, 0 },
+ { "timer4", { .name = "refdiv2" }, WPCM450_CLK_TIMER4, 0 },
+ { "mft0", { .name = "apb" }, WPCM450_CLK_MFT0, 0 },
+ { "mft1", { .name = "apb" }, WPCM450_CLK_MFT1, 0 },
+ { "wdt", { .name = "refdiv2" }, WPCM450_CLK_WDT, 0 },
+ { "adc", { .name = "adcdiv" }, WPCM450_CLK_ADC, 0 },
+ { "sdio", { .name = "ahb" }, WPCM450_CLK_SDIO, 0 },
+ { "sspi", { .name = "apb" }, WPCM450_CLK_SSPI, 0 },
+ { "smb0", { .name = "apb" }, WPCM450_CLK_SMB0, 0 },
+ { "smb1", { .name = "apb" }, WPCM450_CLK_SMB1, 0 },
+};
+
+static DEFINE_SPINLOCK(wpcm450_clk_lock);
+
+/*
+ * NOTE: Error handling is very rudimentary here. If the clock driver initial-
+ * ization fails, the system is probably in bigger trouble than what is caused
+ * by a few leaked resources.
+ */
+
+static void __init wpcm450_clk_init(struct device_node *clk_np)
+{
+ struct clk_hw_onecell_data *clk_data;
+ static struct clk_hw **hws;
+ static struct clk_hw *hw;
+ void __iomem *clk_base;
+ int i, ret;
+ struct reset_simple_data *reset;
+
+ clk_base = of_iomap(clk_np, 0);
+ if (!clk_base) {
+ pr_err("%pOFP: failed to map registers\n", clk_np);
+ of_node_put(clk_np);
+ return;
+ }
+ of_node_put(clk_np);
+
+ clk_data = kzalloc(struct_size(clk_data, hws, WPCM450_NUM_CLKS), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->num = WPCM450_NUM_CLKS;
+ hws = clk_data->hws;
+
+ for (i = 0; i < WPCM450_NUM_CLKS; i++)
+ hws[i] = ERR_PTR(-ENOENT);
+
+ // PLLs
+ for (i = 0; i < ARRAY_SIZE(pll_data); i++) {
+ const struct wpcm450_pll_data *data = &pll_data[i];
+
+ hw = wpcm450_clk_register_pll(clk_base + data->reg, data->name,
+ &data->parent, data->flags);
+ if (IS_ERR(hw)) {
+ pr_info("Failed to register PLL: %pe", hw);
+ return;
+ }
+ }
+
+ // Early divisors (REF/2)
+ for (i = 0; i < ARRAY_SIZE(clkdiv_data_early); i++) {
+ const struct wpcm450_clkdiv_data *data = &clkdiv_data_early[i];
+
+ hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
+ data->flags, clk_base + REG_CLKDIV,
+ data->shift, data->width,
+ data->div_flags, data->table,
+ &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register div table: %pe\n", hw);
+ return;
+ }
+ }
+
+ // Selects/muxes
+ for (i = 0; i < ARRAY_SIZE(clksel_data); i++) {
+ const struct wpcm450_clksel_data *data = &clksel_data[i];
+
+ hw = clk_hw_register_mux_parent_data(NULL, data->name, data->parents,
+ data->num_parents, data->flags,
+ clk_base + REG_CLKSEL, data->shift,
+ data->width, 0,
+ &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register mux: %pe\n", hw);
+ return;
+ }
+ if (data->index >= 0)
+ clk_data->hws[data->index] = hw;
+ }
+
+ // Divisors
+ for (i = 0; i < ARRAY_SIZE(clkdiv_data); i++) {
+ const struct wpcm450_clkdiv_data *data = &clkdiv_data[i];
+
+ hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
+ data->flags, clk_base + REG_CLKDIV,
+ data->shift, data->width,
+ data->div_flags, data->table,
+ &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register divider: %pe\n", hw);
+ return;
+ }
+ }
+
+ // Enables/gates
+ for (i = 0; i < ARRAY_SIZE(clken_data); i++) {
+ const struct wpcm450_clken_data *data = &clken_data[i];
+
+ hw = clk_hw_register_gate_parent_data(NULL, data->name, &data->parent, data->flags,
+ clk_base + REG_CLKEN, data->bitnum,
+ data->flags, &wpcm450_clk_lock);
+ if (IS_ERR(hw)) {
+ pr_err("Failed to register gate: %pe\n", hw);
+ return;
+ }
+ clk_data->hws[data->bitnum] = hw;
+ }
+
+ ret = of_clk_add_hw_provider(clk_np, of_clk_hw_onecell_get, clk_data);
+ if (ret)
+ pr_err("Failed to add DT provider: %pe\n", ERR_PTR(ret));
+
+ // Reset controller
+ reset = kzalloc(sizeof(*reset), GFP_KERNEL);
+ if (!reset)
+ return;
+ reset->rcdev.owner = THIS_MODULE;
+ reset->rcdev.nr_resets = WPCM450_NUM_RESETS;
+ reset->rcdev.ops = &reset_simple_ops;
+ reset->rcdev.of_node = clk_np;
+ reset->membase = clk_base + REG_IPSRST;
+ ret = reset_controller_register(&reset->rcdev);
+ if (ret)
+ pr_err("Failed to register reset controller: %pe\n", ERR_PTR(ret));
+
+ of_node_put(clk_np);
+}
+
+CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 2a52c990d4fec..16e111d213560 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -208,7 +208,7 @@ config RESET_SCMI
config RESET_SIMPLE
bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT
- default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
+ default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC || ARCH_WPCM450
depends on HAS_IOMEM
help
This enables a simple reset controller driver for reset lines that
--
2.39.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
2023-04-28 19:02 ` Jonathan Neuschäfer
@ 2023-07-21 0:02 ` Stephen Boyd
-1 siblings, 0 replies; 14+ messages in thread
From: Stephen Boyd @ 2023-07-21 0:02 UTC (permalink / raw)
To: Jonathan Neuschäfer, linux-clk, openbmc
Cc: linux-kernel, linux-watchdog, devicetree,
Jonathan Neuschäfer, Michael Turquette, Rob Herring,
Krzysztof Kozlowski, Avi Fishman, Tomer Maimon, Tali Perry,
Patrick Venture, Nancy Yuen, Benjamin Fair, Daniel Lezcano,
Thomas Gleixner, Philipp Zabel, Wim Van Sebroeck, Guenter Roeck,
Christophe JAILLET, Joel Stanley
Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
> diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
> new file mode 100644
> index 0000000000000..358be5befa887
> --- /dev/null
> +++ b/drivers/clk/clk-wpcm450.c
> @@ -0,0 +1,374 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Nuvoton WPCM450 clock and reset controller driver.
> + *
> + * Copyright (C) 2022 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/reset-controller.h>
> +#include <linux/reset/reset-simple.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
> +
> +struct wpcm450_clk_pll {
> + struct clk_hw hw;
> + void __iomem *pllcon;
> + u8 flags;
> +};
> +
> +#define to_wpcm450_clk_pll(_hw) container_of(_hw, struct wpcm450_clk_pll, hw)
> +
> +#define PLLCON_FBDV GENMASK(24, 16)
> +#define PLLCON_PRST BIT(13)
> +#define PLLCON_PWDEN BIT(12)
> +#define PLLCON_OTDV GENMASK(10, 8)
> +#define PLLCON_INDV GENMASK(5, 0)
> +
> +static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> + unsigned long fbdv, indv, otdv;
> + u64 rate;
> + u32 pllcon;
> +
> + if (parent_rate == 0) {
> + pr_err("%s: parent rate is zero", __func__);
This can happen more often than you think, so probably best to remove
it.
> + return 0;
> + }
> +
> + pllcon = readl_relaxed(pll->pllcon);
> +
> + indv = FIELD_GET(PLLCON_INDV, pllcon) + 1;
> + fbdv = FIELD_GET(PLLCON_FBDV, pllcon) + 1;
> + otdv = FIELD_GET(PLLCON_OTDV, pllcon) + 1;
> +
> + rate = (u64)parent_rate * fbdv;
> + do_div(rate, indv * otdv);
> +
> + return rate;
> +}
> +
> +static int wpcm450_clk_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> + u32 pllcon;
> +
> + pllcon = readl_relaxed(pll->pllcon);
> +
> + return !(pllcon & PLLCON_PRST);
> +}
> +
> +static void wpcm450_clk_pll_disable(struct clk_hw *hw)
> +{
> + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> + u32 pllcon;
> +
> + pllcon = readl_relaxed(pll->pllcon);
> + pllcon |= PLLCON_PRST | PLLCON_PWDEN;
> + writel(pllcon, pll->pllcon);
> +}
> +
> +static const struct clk_ops wpcm450_clk_pll_ops = {
> + .recalc_rate = wpcm450_clk_pll_recalc_rate,
> + .is_enabled = wpcm450_clk_pll_is_enabled,
> + .disable = wpcm450_clk_pll_disable
> +};
> +
> +static struct clk_hw *
> +wpcm450_clk_register_pll(void __iomem *pllcon, const char *name,
> + const struct clk_parent_data *parent, unsigned long flags)
> +{
> + struct wpcm450_clk_pll *pll;
> + struct clk_init_data init = {};
> + int ret;
> +
> + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = &wpcm450_clk_pll_ops;
> + init.parent_data = parent;
> + init.num_parents = 1;
> + init.flags = flags;
> +
> + pll->pllcon = pllcon;
> + pll->hw.init = &init;
> +
> + ret = clk_hw_register(NULL, &pll->hw);
> + if (ret) {
> + kfree(pll);
> + return ERR_PTR(ret);
> + }
> +
> + return &pll->hw;
> +}
> +
> +#define REG_CLKEN 0x00
> +#define REG_CLKSEL 0x04
> +#define REG_CLKDIV 0x08
> +#define REG_PLLCON0 0x0c
> +#define REG_PLLCON1 0x10
> +#define REG_PMCON 0x14
> +#define REG_IRQWAKECON 0x18
> +#define REG_IRQWAKEFLAG 0x1c
> +#define REG_IPSRST 0x20
> +
> +struct wpcm450_pll_data {
> + const char *name;
> + struct clk_parent_data parent;
> + unsigned int reg;
> + unsigned long flags;
> +};
> +
> +static const struct wpcm450_pll_data pll_data[] = {
> + { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
This is new code, please don't use .name. Instead use .fw_name or .index with preference to
.index first and .hw if the pointer is available in this driver.
> + { "pll1", { .name = "ref" }, REG_PLLCON1, 0 },
> +};
> +
> +struct wpcm450_clksel_data {
> + const char *name;
> + const struct clk_parent_data *parents;
> + unsigned int num_parents;
> + const u32 *table;
> + int shift;
> + int width;
> + int index;
> + unsigned long flags;
> +};
> +
> +static const u32 parent_table[] = { 0, 1, 2 };
> +
> +static const struct clk_parent_data default_parents[] = {
> + { .name = "pll0" },
> + { .name = "pll1" },
> + { .name = "ref" },
> +};
> +
> +static const struct clk_parent_data huart_parents[] = {
> + { .name = "ref" },
> + { .name = "refdiv2" },
> +};
> +
> +static const struct wpcm450_clksel_data clksel_data[] = {
> + { "cpusel", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 0, 2, -1, CLK_IS_CRITICAL },
> + { "clkout", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 2, 2, -1, 0 },
> + { "usbphy", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 6, 2, -1, 0 },
> + { "uartsel", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 8, 2, WPCM450_CLK_USBPHY, 0 },
> + { "huartsel", huart_parents, ARRAY_SIZE(huart_parents),
> + parent_table, 10, 1, -1, 0 },
> +};
> +
> +static const struct clk_div_table div_fixed2[] = {
> + { .val = 0, .div = 2 },
> + { }
> +};
> +
> +struct wpcm450_clkdiv_data {
> + const char *name;
> + struct clk_parent_data parent;
> + int div_flags;
> + const struct clk_div_table *table;
> + int shift;
> + int width;
> + unsigned long flags;
> +};
> +
> +static struct wpcm450_clkdiv_data clkdiv_data_early[] = {
> + { "refdiv2", { .name = "ref" }, 0, div_fixed2, 0, 0 },
> +};
> +
> +static const struct wpcm450_clkdiv_data clkdiv_data[] = {
> + { "cpu", { .name = "cpusel" }, 0, div_fixed2, 0, 0, CLK_IS_CRITICAL },
> + { "adcdiv", { .name = "ref" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 28, 2, 0 },
> + { "apb", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 26, 2, 0 },
> + { "ahb", { .name = "cpu" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 24, 2, 0 },
> + { "uart", { .name = "uartsel" }, 0, NULL, 16, 4, 0 },
> + { "ahb3", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 8, 2, 0 },
> +};
> +
> +struct wpcm450_clken_data {
> + const char *name;
> + struct clk_parent_data parent;
> + int bitnum;
> + unsigned long flags;
> +};
> +
> +static const struct wpcm450_clken_data clken_data[] = {
> + { "fiu", { .name = "ahb3" }, WPCM450_CLK_FIU, 0 },
> + { "xbus", { .name = "ahb3" }, WPCM450_CLK_XBUS, 0 },
> + { "kcs", { .name = "apb" }, WPCM450_CLK_KCS, 0 },
> + { "shm", { .name = "ahb3" }, WPCM450_CLK_SHM, 0 },
> + { "usb1", { .name = "ahb" }, WPCM450_CLK_USB1, 0 },
> + { "emc0", { .name = "ahb" }, WPCM450_CLK_EMC0, 0 },
> + { "emc1", { .name = "ahb" }, WPCM450_CLK_EMC1, 0 },
> + { "usb0", { .name = "ahb" }, WPCM450_CLK_USB0, 0 },
> + { "peci", { .name = "apb" }, WPCM450_CLK_PECI, 0 },
> + { "aes", { .name = "apb" }, WPCM450_CLK_AES, 0 },
> + { "uart0", { .name = "uart" }, WPCM450_CLK_UART0, 0 },
> + { "uart1", { .name = "uart" }, WPCM450_CLK_UART1, 0 },
> + { "smb2", { .name = "apb" }, WPCM450_CLK_SMB2, 0 },
> + { "smb3", { .name = "apb" }, WPCM450_CLK_SMB3, 0 },
> + { "smb4", { .name = "apb" }, WPCM450_CLK_SMB4, 0 },
> + { "smb5", { .name = "apb" }, WPCM450_CLK_SMB5, 0 },
> + { "huart", { .name = "huartsel" }, WPCM450_CLK_HUART, 0 },
> + { "pwm", { .name = "apb" }, WPCM450_CLK_PWM, 0 },
> + { "timer0", { .name = "refdiv2" }, WPCM450_CLK_TIMER0, 0 },
> + { "timer1", { .name = "refdiv2" }, WPCM450_CLK_TIMER1, 0 },
> + { "timer2", { .name = "refdiv2" }, WPCM450_CLK_TIMER2, 0 },
> + { "timer3", { .name = "refdiv2" }, WPCM450_CLK_TIMER3, 0 },
> + { "timer4", { .name = "refdiv2" }, WPCM450_CLK_TIMER4, 0 },
> + { "mft0", { .name = "apb" }, WPCM450_CLK_MFT0, 0 },
> + { "mft1", { .name = "apb" }, WPCM450_CLK_MFT1, 0 },
> + { "wdt", { .name = "refdiv2" }, WPCM450_CLK_WDT, 0 },
> + { "adc", { .name = "adcdiv" }, WPCM450_CLK_ADC, 0 },
> + { "sdio", { .name = "ahb" }, WPCM450_CLK_SDIO, 0 },
> + { "sspi", { .name = "apb" }, WPCM450_CLK_SSPI, 0 },
> + { "smb0", { .name = "apb" }, WPCM450_CLK_SMB0, 0 },
> + { "smb1", { .name = "apb" }, WPCM450_CLK_SMB1, 0 },
> +};
> +
> +static DEFINE_SPINLOCK(wpcm450_clk_lock);
> +
> +/*
> + * NOTE: Error handling is very rudimentary here. If the clock driver initial-
> + * ization fails, the system is probably in bigger trouble than what is caused
> + * by a few leaked resources.
> + */
> +
> +static void __init wpcm450_clk_init(struct device_node *clk_np)
> +{
> + struct clk_hw_onecell_data *clk_data;
> + static struct clk_hw **hws;
> + static struct clk_hw *hw;
> + void __iomem *clk_base;
> + int i, ret;
> + struct reset_simple_data *reset;
> +
> + clk_base = of_iomap(clk_np, 0);
> + if (!clk_base) {
> + pr_err("%pOFP: failed to map registers\n", clk_np);
> + of_node_put(clk_np);
> + return;
> + }
> + of_node_put(clk_np);
> +
> + clk_data = kzalloc(struct_size(clk_data, hws, WPCM450_NUM_CLKS), GFP_KERNEL);
> + if (!clk_data)
> + return;
> +
> + clk_data->num = WPCM450_NUM_CLKS;
> + hws = clk_data->hws;
> +
> + for (i = 0; i < WPCM450_NUM_CLKS; i++)
> + hws[i] = ERR_PTR(-ENOENT);
> +
> + // PLLs
Please use /* comments like this */
> + for (i = 0; i < ARRAY_SIZE(pll_data); i++) {
> + const struct wpcm450_pll_data *data = &pll_data[i];
> +
> + hw = wpcm450_clk_register_pll(clk_base + data->reg, data->name,
> + &data->parent, data->flags);
> + if (IS_ERR(hw)) {
> + pr_info("Failed to register PLL: %pe", hw);
Missing newline?
> + return;
> + }
> + }
> +
> + // Early divisors (REF/2)
> + for (i = 0; i < ARRAY_SIZE(clkdiv_data_early); i++) {
> + const struct wpcm450_clkdiv_data *data = &clkdiv_data_early[i];
> +
> + hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
> + data->flags, clk_base + REG_CLKDIV,
> + data->shift, data->width,
> + data->div_flags, data->table,
> + &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register div table: %pe\n", hw);
> + return;
> + }
> + }
> +
> + // Selects/muxes
> + for (i = 0; i < ARRAY_SIZE(clksel_data); i++) {
> + const struct wpcm450_clksel_data *data = &clksel_data[i];
> +
> + hw = clk_hw_register_mux_parent_data(NULL, data->name, data->parents,
> + data->num_parents, data->flags,
> + clk_base + REG_CLKSEL, data->shift,
> + data->width, 0,
> + &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register mux: %pe\n", hw);
> + return;
> + }
> + if (data->index >= 0)
> + clk_data->hws[data->index] = hw;
> + }
> +
> + // Divisors
> + for (i = 0; i < ARRAY_SIZE(clkdiv_data); i++) {
> + const struct wpcm450_clkdiv_data *data = &clkdiv_data[i];
> +
> + hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
> + data->flags, clk_base + REG_CLKDIV,
> + data->shift, data->width,
> + data->div_flags, data->table,
> + &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register divider: %pe\n", hw);
> + return;
> + }
> + }
> +
> + // Enables/gates
> + for (i = 0; i < ARRAY_SIZE(clken_data); i++) {
> + const struct wpcm450_clken_data *data = &clken_data[i];
> +
> + hw = clk_hw_register_gate_parent_data(NULL, data->name, &data->parent, data->flags,
> + clk_base + REG_CLKEN, data->bitnum,
> + data->flags, &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register gate: %pe\n", hw);
> + return;
> + }
> + clk_data->hws[data->bitnum] = hw;
> + }
> +
> + ret = of_clk_add_hw_provider(clk_np, of_clk_hw_onecell_get, clk_data);
> + if (ret)
> + pr_err("Failed to add DT provider: %pe\n", ERR_PTR(ret));
> +
> + // Reset controller
> + reset = kzalloc(sizeof(*reset), GFP_KERNEL);
> + if (!reset)
> + return;
> + reset->rcdev.owner = THIS_MODULE;
> + reset->rcdev.nr_resets = WPCM450_NUM_RESETS;
> + reset->rcdev.ops = &reset_simple_ops;
> + reset->rcdev.of_node = clk_np;
> + reset->membase = clk_base + REG_IPSRST;
> + ret = reset_controller_register(&reset->rcdev);
> + if (ret)
> + pr_err("Failed to register reset controller: %pe\n", ERR_PTR(ret));
> +
> + of_node_put(clk_np);
> +}
> +
> +CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
Is something preventing this from being a platform driver?
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
@ 2023-07-21 0:02 ` Stephen Boyd
0 siblings, 0 replies; 14+ messages in thread
From: Stephen Boyd @ 2023-07-21 0:02 UTC (permalink / raw)
To: Jonathan Neuschäfer, linux-clk, openbmc
Cc: devicetree, Wim Van Sebroeck, linux-watchdog, Avi Fishman,
Patrick Venture, Michael Turquette, Daniel Lezcano, Joel Stanley,
linux-kernel, Jonathan Neuschäfer, Rob Herring,
Christophe JAILLET, Benjamin Fair, Philipp Zabel,
Krzysztof Kozlowski, Tali Perry, Thomas Gleixner, Guenter Roeck,
Tomer Maimon
Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
> diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
> new file mode 100644
> index 0000000000000..358be5befa887
> --- /dev/null
> +++ b/drivers/clk/clk-wpcm450.c
> @@ -0,0 +1,374 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Nuvoton WPCM450 clock and reset controller driver.
> + *
> + * Copyright (C) 2022 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/reset-controller.h>
> +#include <linux/reset/reset-simple.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
> +
> +struct wpcm450_clk_pll {
> + struct clk_hw hw;
> + void __iomem *pllcon;
> + u8 flags;
> +};
> +
> +#define to_wpcm450_clk_pll(_hw) container_of(_hw, struct wpcm450_clk_pll, hw)
> +
> +#define PLLCON_FBDV GENMASK(24, 16)
> +#define PLLCON_PRST BIT(13)
> +#define PLLCON_PWDEN BIT(12)
> +#define PLLCON_OTDV GENMASK(10, 8)
> +#define PLLCON_INDV GENMASK(5, 0)
> +
> +static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> + unsigned long fbdv, indv, otdv;
> + u64 rate;
> + u32 pllcon;
> +
> + if (parent_rate == 0) {
> + pr_err("%s: parent rate is zero", __func__);
This can happen more often than you think, so probably best to remove
it.
> + return 0;
> + }
> +
> + pllcon = readl_relaxed(pll->pllcon);
> +
> + indv = FIELD_GET(PLLCON_INDV, pllcon) + 1;
> + fbdv = FIELD_GET(PLLCON_FBDV, pllcon) + 1;
> + otdv = FIELD_GET(PLLCON_OTDV, pllcon) + 1;
> +
> + rate = (u64)parent_rate * fbdv;
> + do_div(rate, indv * otdv);
> +
> + return rate;
> +}
> +
> +static int wpcm450_clk_pll_is_enabled(struct clk_hw *hw)
> +{
> + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> + u32 pllcon;
> +
> + pllcon = readl_relaxed(pll->pllcon);
> +
> + return !(pllcon & PLLCON_PRST);
> +}
> +
> +static void wpcm450_clk_pll_disable(struct clk_hw *hw)
> +{
> + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> + u32 pllcon;
> +
> + pllcon = readl_relaxed(pll->pllcon);
> + pllcon |= PLLCON_PRST | PLLCON_PWDEN;
> + writel(pllcon, pll->pllcon);
> +}
> +
> +static const struct clk_ops wpcm450_clk_pll_ops = {
> + .recalc_rate = wpcm450_clk_pll_recalc_rate,
> + .is_enabled = wpcm450_clk_pll_is_enabled,
> + .disable = wpcm450_clk_pll_disable
> +};
> +
> +static struct clk_hw *
> +wpcm450_clk_register_pll(void __iomem *pllcon, const char *name,
> + const struct clk_parent_data *parent, unsigned long flags)
> +{
> + struct wpcm450_clk_pll *pll;
> + struct clk_init_data init = {};
> + int ret;
> +
> + pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> + if (!pll)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = name;
> + init.ops = &wpcm450_clk_pll_ops;
> + init.parent_data = parent;
> + init.num_parents = 1;
> + init.flags = flags;
> +
> + pll->pllcon = pllcon;
> + pll->hw.init = &init;
> +
> + ret = clk_hw_register(NULL, &pll->hw);
> + if (ret) {
> + kfree(pll);
> + return ERR_PTR(ret);
> + }
> +
> + return &pll->hw;
> +}
> +
> +#define REG_CLKEN 0x00
> +#define REG_CLKSEL 0x04
> +#define REG_CLKDIV 0x08
> +#define REG_PLLCON0 0x0c
> +#define REG_PLLCON1 0x10
> +#define REG_PMCON 0x14
> +#define REG_IRQWAKECON 0x18
> +#define REG_IRQWAKEFLAG 0x1c
> +#define REG_IPSRST 0x20
> +
> +struct wpcm450_pll_data {
> + const char *name;
> + struct clk_parent_data parent;
> + unsigned int reg;
> + unsigned long flags;
> +};
> +
> +static const struct wpcm450_pll_data pll_data[] = {
> + { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
This is new code, please don't use .name. Instead use .fw_name or .index with preference to
.index first and .hw if the pointer is available in this driver.
> + { "pll1", { .name = "ref" }, REG_PLLCON1, 0 },
> +};
> +
> +struct wpcm450_clksel_data {
> + const char *name;
> + const struct clk_parent_data *parents;
> + unsigned int num_parents;
> + const u32 *table;
> + int shift;
> + int width;
> + int index;
> + unsigned long flags;
> +};
> +
> +static const u32 parent_table[] = { 0, 1, 2 };
> +
> +static const struct clk_parent_data default_parents[] = {
> + { .name = "pll0" },
> + { .name = "pll1" },
> + { .name = "ref" },
> +};
> +
> +static const struct clk_parent_data huart_parents[] = {
> + { .name = "ref" },
> + { .name = "refdiv2" },
> +};
> +
> +static const struct wpcm450_clksel_data clksel_data[] = {
> + { "cpusel", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 0, 2, -1, CLK_IS_CRITICAL },
> + { "clkout", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 2, 2, -1, 0 },
> + { "usbphy", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 6, 2, -1, 0 },
> + { "uartsel", default_parents, ARRAY_SIZE(default_parents),
> + parent_table, 8, 2, WPCM450_CLK_USBPHY, 0 },
> + { "huartsel", huart_parents, ARRAY_SIZE(huart_parents),
> + parent_table, 10, 1, -1, 0 },
> +};
> +
> +static const struct clk_div_table div_fixed2[] = {
> + { .val = 0, .div = 2 },
> + { }
> +};
> +
> +struct wpcm450_clkdiv_data {
> + const char *name;
> + struct clk_parent_data parent;
> + int div_flags;
> + const struct clk_div_table *table;
> + int shift;
> + int width;
> + unsigned long flags;
> +};
> +
> +static struct wpcm450_clkdiv_data clkdiv_data_early[] = {
> + { "refdiv2", { .name = "ref" }, 0, div_fixed2, 0, 0 },
> +};
> +
> +static const struct wpcm450_clkdiv_data clkdiv_data[] = {
> + { "cpu", { .name = "cpusel" }, 0, div_fixed2, 0, 0, CLK_IS_CRITICAL },
> + { "adcdiv", { .name = "ref" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 28, 2, 0 },
> + { "apb", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 26, 2, 0 },
> + { "ahb", { .name = "cpu" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 24, 2, 0 },
> + { "uart", { .name = "uartsel" }, 0, NULL, 16, 4, 0 },
> + { "ahb3", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 8, 2, 0 },
> +};
> +
> +struct wpcm450_clken_data {
> + const char *name;
> + struct clk_parent_data parent;
> + int bitnum;
> + unsigned long flags;
> +};
> +
> +static const struct wpcm450_clken_data clken_data[] = {
> + { "fiu", { .name = "ahb3" }, WPCM450_CLK_FIU, 0 },
> + { "xbus", { .name = "ahb3" }, WPCM450_CLK_XBUS, 0 },
> + { "kcs", { .name = "apb" }, WPCM450_CLK_KCS, 0 },
> + { "shm", { .name = "ahb3" }, WPCM450_CLK_SHM, 0 },
> + { "usb1", { .name = "ahb" }, WPCM450_CLK_USB1, 0 },
> + { "emc0", { .name = "ahb" }, WPCM450_CLK_EMC0, 0 },
> + { "emc1", { .name = "ahb" }, WPCM450_CLK_EMC1, 0 },
> + { "usb0", { .name = "ahb" }, WPCM450_CLK_USB0, 0 },
> + { "peci", { .name = "apb" }, WPCM450_CLK_PECI, 0 },
> + { "aes", { .name = "apb" }, WPCM450_CLK_AES, 0 },
> + { "uart0", { .name = "uart" }, WPCM450_CLK_UART0, 0 },
> + { "uart1", { .name = "uart" }, WPCM450_CLK_UART1, 0 },
> + { "smb2", { .name = "apb" }, WPCM450_CLK_SMB2, 0 },
> + { "smb3", { .name = "apb" }, WPCM450_CLK_SMB3, 0 },
> + { "smb4", { .name = "apb" }, WPCM450_CLK_SMB4, 0 },
> + { "smb5", { .name = "apb" }, WPCM450_CLK_SMB5, 0 },
> + { "huart", { .name = "huartsel" }, WPCM450_CLK_HUART, 0 },
> + { "pwm", { .name = "apb" }, WPCM450_CLK_PWM, 0 },
> + { "timer0", { .name = "refdiv2" }, WPCM450_CLK_TIMER0, 0 },
> + { "timer1", { .name = "refdiv2" }, WPCM450_CLK_TIMER1, 0 },
> + { "timer2", { .name = "refdiv2" }, WPCM450_CLK_TIMER2, 0 },
> + { "timer3", { .name = "refdiv2" }, WPCM450_CLK_TIMER3, 0 },
> + { "timer4", { .name = "refdiv2" }, WPCM450_CLK_TIMER4, 0 },
> + { "mft0", { .name = "apb" }, WPCM450_CLK_MFT0, 0 },
> + { "mft1", { .name = "apb" }, WPCM450_CLK_MFT1, 0 },
> + { "wdt", { .name = "refdiv2" }, WPCM450_CLK_WDT, 0 },
> + { "adc", { .name = "adcdiv" }, WPCM450_CLK_ADC, 0 },
> + { "sdio", { .name = "ahb" }, WPCM450_CLK_SDIO, 0 },
> + { "sspi", { .name = "apb" }, WPCM450_CLK_SSPI, 0 },
> + { "smb0", { .name = "apb" }, WPCM450_CLK_SMB0, 0 },
> + { "smb1", { .name = "apb" }, WPCM450_CLK_SMB1, 0 },
> +};
> +
> +static DEFINE_SPINLOCK(wpcm450_clk_lock);
> +
> +/*
> + * NOTE: Error handling is very rudimentary here. If the clock driver initial-
> + * ization fails, the system is probably in bigger trouble than what is caused
> + * by a few leaked resources.
> + */
> +
> +static void __init wpcm450_clk_init(struct device_node *clk_np)
> +{
> + struct clk_hw_onecell_data *clk_data;
> + static struct clk_hw **hws;
> + static struct clk_hw *hw;
> + void __iomem *clk_base;
> + int i, ret;
> + struct reset_simple_data *reset;
> +
> + clk_base = of_iomap(clk_np, 0);
> + if (!clk_base) {
> + pr_err("%pOFP: failed to map registers\n", clk_np);
> + of_node_put(clk_np);
> + return;
> + }
> + of_node_put(clk_np);
> +
> + clk_data = kzalloc(struct_size(clk_data, hws, WPCM450_NUM_CLKS), GFP_KERNEL);
> + if (!clk_data)
> + return;
> +
> + clk_data->num = WPCM450_NUM_CLKS;
> + hws = clk_data->hws;
> +
> + for (i = 0; i < WPCM450_NUM_CLKS; i++)
> + hws[i] = ERR_PTR(-ENOENT);
> +
> + // PLLs
Please use /* comments like this */
> + for (i = 0; i < ARRAY_SIZE(pll_data); i++) {
> + const struct wpcm450_pll_data *data = &pll_data[i];
> +
> + hw = wpcm450_clk_register_pll(clk_base + data->reg, data->name,
> + &data->parent, data->flags);
> + if (IS_ERR(hw)) {
> + pr_info("Failed to register PLL: %pe", hw);
Missing newline?
> + return;
> + }
> + }
> +
> + // Early divisors (REF/2)
> + for (i = 0; i < ARRAY_SIZE(clkdiv_data_early); i++) {
> + const struct wpcm450_clkdiv_data *data = &clkdiv_data_early[i];
> +
> + hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
> + data->flags, clk_base + REG_CLKDIV,
> + data->shift, data->width,
> + data->div_flags, data->table,
> + &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register div table: %pe\n", hw);
> + return;
> + }
> + }
> +
> + // Selects/muxes
> + for (i = 0; i < ARRAY_SIZE(clksel_data); i++) {
> + const struct wpcm450_clksel_data *data = &clksel_data[i];
> +
> + hw = clk_hw_register_mux_parent_data(NULL, data->name, data->parents,
> + data->num_parents, data->flags,
> + clk_base + REG_CLKSEL, data->shift,
> + data->width, 0,
> + &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register mux: %pe\n", hw);
> + return;
> + }
> + if (data->index >= 0)
> + clk_data->hws[data->index] = hw;
> + }
> +
> + // Divisors
> + for (i = 0; i < ARRAY_SIZE(clkdiv_data); i++) {
> + const struct wpcm450_clkdiv_data *data = &clkdiv_data[i];
> +
> + hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
> + data->flags, clk_base + REG_CLKDIV,
> + data->shift, data->width,
> + data->div_flags, data->table,
> + &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register divider: %pe\n", hw);
> + return;
> + }
> + }
> +
> + // Enables/gates
> + for (i = 0; i < ARRAY_SIZE(clken_data); i++) {
> + const struct wpcm450_clken_data *data = &clken_data[i];
> +
> + hw = clk_hw_register_gate_parent_data(NULL, data->name, &data->parent, data->flags,
> + clk_base + REG_CLKEN, data->bitnum,
> + data->flags, &wpcm450_clk_lock);
> + if (IS_ERR(hw)) {
> + pr_err("Failed to register gate: %pe\n", hw);
> + return;
> + }
> + clk_data->hws[data->bitnum] = hw;
> + }
> +
> + ret = of_clk_add_hw_provider(clk_np, of_clk_hw_onecell_get, clk_data);
> + if (ret)
> + pr_err("Failed to add DT provider: %pe\n", ERR_PTR(ret));
> +
> + // Reset controller
> + reset = kzalloc(sizeof(*reset), GFP_KERNEL);
> + if (!reset)
> + return;
> + reset->rcdev.owner = THIS_MODULE;
> + reset->rcdev.nr_resets = WPCM450_NUM_RESETS;
> + reset->rcdev.ops = &reset_simple_ops;
> + reset->rcdev.of_node = clk_np;
> + reset->membase = clk_base + REG_IPSRST;
> + ret = reset_controller_register(&reset->rcdev);
> + if (ret)
> + pr_err("Failed to register reset controller: %pe\n", ERR_PTR(ret));
> +
> + of_node_put(clk_np);
> +}
> +
> +CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
Is something preventing this from being a platform driver?
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
2023-07-21 0:02 ` Stephen Boyd
@ 2023-07-22 15:15 ` Jonathan Neuschäfer
-1 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-07-22 15:15 UTC (permalink / raw)
To: Stephen Boyd
Cc: Jonathan Neuschäfer, linux-clk, openbmc, linux-kernel,
linux-watchdog, devicetree, Michael Turquette, Rob Herring,
Krzysztof Kozlowski, Avi Fishman, Tomer Maimon, Tali Perry,
Patrick Venture, Nancy Yuen, Benjamin Fair, Daniel Lezcano,
Thomas Gleixner, Philipp Zabel, Wim Van Sebroeck, Guenter Roeck,
Christophe JAILLET, Joel Stanley
[-- Attachment #1: Type: text/plain, Size: 2139 bytes --]
On Thu, Jul 20, 2023 at 05:02:15PM -0700, Stephen Boyd wrote:
> Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
> > diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
[...]
> > +static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
> > + unsigned long parent_rate)
> > +{
> > + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> > + unsigned long fbdv, indv, otdv;
> > + u64 rate;
> > + u32 pllcon;
> > +
> > + if (parent_rate == 0) {
> > + pr_err("%s: parent rate is zero", __func__);
>
> This can happen more often than you think, so probably best to remove
> it.
Alright.
> > +static const struct wpcm450_pll_data pll_data[] = {
> > + { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
>
> This is new code, please don't use .name. Instead use .fw_name or .index with preference to
> .index first and .hw if the pointer is available in this driver.
As far as I can see, .fw_name and .index depend on a struct device*
being passed to clk_hw_register, which won't be available unless I
actually convert the driver to a platform driver.
Not relying on .name would indeed be nice.
> > + // PLLs
>
> Please use /* comments like this */
Ok.
>
> > + for (i = 0; i < ARRAY_SIZE(pll_data); i++) {
> > + const struct wpcm450_pll_data *data = &pll_data[i];
> > +
> > + hw = wpcm450_clk_register_pll(clk_base + data->reg, data->name,
> > + &data->parent, data->flags);
> > + if (IS_ERR(hw)) {
> > + pr_info("Failed to register PLL: %pe", hw);
>
> Missing newline?
Indeed.
> > +CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
>
> Is something preventing this from being a platform driver?
As far as I remember I have tried to convert it to a platform driver but
wasn't very successful/satisfied. Unfortunately I didn't take detailed notes.
I'll give it another try.
Best regards,
Jonathan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
@ 2023-07-22 15:15 ` Jonathan Neuschäfer
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-07-22 15:15 UTC (permalink / raw)
To: Stephen Boyd
Cc: Tomer Maimon, Michael Turquette, Tali Perry, linux-clk,
Benjamin Fair, openbmc, Daniel Lezcano, Joel Stanley,
Guenter Roeck, devicetree, linux-watchdog,
Jonathan Neuschäfer, Rob Herring, Christophe JAILLET,
Thomas Gleixner, Wim Van Sebroeck, Avi Fishman, Patrick Venture,
linux-kernel, Philipp Zabel, Krzysztof Kozlowski
[-- Attachment #1: Type: text/plain, Size: 2139 bytes --]
On Thu, Jul 20, 2023 at 05:02:15PM -0700, Stephen Boyd wrote:
> Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
> > diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
[...]
> > +static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
> > + unsigned long parent_rate)
> > +{
> > + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
> > + unsigned long fbdv, indv, otdv;
> > + u64 rate;
> > + u32 pllcon;
> > +
> > + if (parent_rate == 0) {
> > + pr_err("%s: parent rate is zero", __func__);
>
> This can happen more often than you think, so probably best to remove
> it.
Alright.
> > +static const struct wpcm450_pll_data pll_data[] = {
> > + { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
>
> This is new code, please don't use .name. Instead use .fw_name or .index with preference to
> .index first and .hw if the pointer is available in this driver.
As far as I can see, .fw_name and .index depend on a struct device*
being passed to clk_hw_register, which won't be available unless I
actually convert the driver to a platform driver.
Not relying on .name would indeed be nice.
> > + // PLLs
>
> Please use /* comments like this */
Ok.
>
> > + for (i = 0; i < ARRAY_SIZE(pll_data); i++) {
> > + const struct wpcm450_pll_data *data = &pll_data[i];
> > +
> > + hw = wpcm450_clk_register_pll(clk_base + data->reg, data->name,
> > + &data->parent, data->flags);
> > + if (IS_ERR(hw)) {
> > + pr_info("Failed to register PLL: %pe", hw);
>
> Missing newline?
Indeed.
> > +CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
>
> Is something preventing this from being a platform driver?
As far as I remember I have tried to convert it to a platform driver but
wasn't very successful/satisfied. Unfortunately I didn't take detailed notes.
I'll give it another try.
Best regards,
Jonathan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
2023-07-21 0:02 ` Stephen Boyd
@ 2023-07-22 21:55 ` Jonathan Neuschäfer
-1 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-07-22 21:55 UTC (permalink / raw)
To: Stephen Boyd
Cc: Jonathan Neuschäfer, linux-clk, openbmc, linux-kernel,
linux-watchdog, devicetree, Michael Turquette, Rob Herring,
Krzysztof Kozlowski, Avi Fishman, Tomer Maimon, Tali Perry,
Patrick Venture, Nancy Yuen, Benjamin Fair, Daniel Lezcano,
Thomas Gleixner, Philipp Zabel, Wim Van Sebroeck, Guenter Roeck,
Christophe JAILLET, Joel Stanley
[-- Attachment #1: Type: text/plain, Size: 577 bytes --]
On Thu, Jul 20, 2023 at 05:02:15PM -0700, Stephen Boyd wrote:
> Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
[...]
> > +CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
>
> Is something preventing this from being a platform driver?
Ok, when I tried this again, I ran into the issue that the clocks need
to be ready before the timer-npcm7xx driver is initialized, which is
done with TIMER_OF_DECLARE. So, AFAIUI, I need to use the old
CLK_OF_DECLARE mechanism unless I also convert timer-npcm7xx to a
platform driver.
Jonathan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
@ 2023-07-22 21:55 ` Jonathan Neuschäfer
0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Neuschäfer @ 2023-07-22 21:55 UTC (permalink / raw)
To: Stephen Boyd
Cc: Tomer Maimon, Michael Turquette, Tali Perry, linux-clk,
Benjamin Fair, openbmc, Daniel Lezcano, Joel Stanley,
Guenter Roeck, devicetree, linux-watchdog,
Jonathan Neuschäfer, Rob Herring, Christophe JAILLET,
Thomas Gleixner, Wim Van Sebroeck, Avi Fishman, Patrick Venture,
linux-kernel, Philipp Zabel, Krzysztof Kozlowski
[-- Attachment #1: Type: text/plain, Size: 577 bytes --]
On Thu, Jul 20, 2023 at 05:02:15PM -0700, Stephen Boyd wrote:
> Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
[...]
> > +CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);
>
> Is something preventing this from being a platform driver?
Ok, when I tried this again, I ran into the issue that the clocks need
to be ready before the timer-npcm7xx driver is initialized, which is
done with TIMER_OF_DECLARE. So, AFAIUI, I need to use the old
CLK_OF_DECLARE mechanism unless I also convert timer-npcm7xx to a
platform driver.
Jonathan
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
2023-07-22 15:15 ` Jonathan Neuschäfer
@ 2023-08-01 19:14 ` Stephen Boyd
-1 siblings, 0 replies; 14+ messages in thread
From: Stephen Boyd @ 2023-08-01 19:14 UTC (permalink / raw)
To: Jonathan Neuschäfer
Cc: Jonathan Neuschäfer, linux-clk, openbmc, linux-kernel,
linux-watchdog, devicetree, Michael Turquette, Rob Herring,
Krzysztof Kozlowski, Avi Fishman, Tomer Maimon, Tali Perry,
Patrick Venture, Nancy Yuen, Benjamin Fair, Daniel Lezcano,
Thomas Gleixner, Philipp Zabel, Wim Van Sebroeck, Guenter Roeck,
Christophe JAILLET, Joel Stanley
Quoting Jonathan Neuschäfer (2023-07-22 08:15:43)
> On Thu, Jul 20, 2023 at 05:02:15PM -0700, Stephen Boyd wrote:
> > Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
> > > diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
> [...]
> > > +static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
> > > + unsigned long parent_rate)
> > > +{
> > > + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
[...]
>
> > > +static const struct wpcm450_pll_data pll_data[] = {
> > > + { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
> >
> > This is new code, please don't use .name. Instead use .fw_name or .index with preference to
> > .index first and .hw if the pointer is available in this driver.
>
> As far as I can see, .fw_name and .index depend on a struct device*
> being passed to clk_hw_register, which won't be available unless I
> actually convert the driver to a platform driver.
You can call of_clk_hw_register(), but a conversion to a platform driver
is preferred.
>
> Not relying on .name would indeed be nice.
Cool.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
@ 2023-08-01 19:14 ` Stephen Boyd
0 siblings, 0 replies; 14+ messages in thread
From: Stephen Boyd @ 2023-08-01 19:14 UTC (permalink / raw)
To: Jonathan Neuschäfer
Cc: Tomer Maimon, Michael Turquette, Tali Perry, linux-clk,
Benjamin Fair, openbmc, Daniel Lezcano, Joel Stanley,
Guenter Roeck, devicetree, linux-watchdog,
Jonathan Neuschäfer, Rob Herring, Christophe JAILLET,
Thomas Gleixner, Wim Van Sebroeck, Avi Fishman, Patrick Venture,
linux-kernel, Philipp Zabel, Krzysztof Kozlowski
Quoting Jonathan Neuschäfer (2023-07-22 08:15:43)
> On Thu, Jul 20, 2023 at 05:02:15PM -0700, Stephen Boyd wrote:
> > Quoting Jonathan Neuschäfer (2023-04-28 12:02:26)
> > > diff --git a/drivers/clk/clk-wpcm450.c b/drivers/clk/clk-wpcm450.c
> [...]
> > > +static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
> > > + unsigned long parent_rate)
> > > +{
> > > + struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
[...]
>
> > > +static const struct wpcm450_pll_data pll_data[] = {
> > > + { "pll0", { .name = "ref" }, REG_PLLCON0, 0 },
> >
> > This is new code, please don't use .name. Instead use .fw_name or .index with preference to
> > .index first and .hw if the pointer is available in this driver.
>
> As far as I can see, .fw_name and .index depend on a struct device*
> being passed to clk_hw_register, which won't be available unless I
> actually convert the driver to a platform driver.
You can call of_clk_hw_register(), but a conversion to a platform driver
is preferred.
>
> Not relying on .name would indeed be nice.
Cool.
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2023-08-01 19:15 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-28 19:02 [PATCH v8 0/2] Nuvoton WPCM450 clock and reset driver Jonathan Neuschäfer
2023-04-28 19:02 ` Jonathan Neuschäfer
2023-04-28 19:02 ` [PATCH v8 1/2] dt-bindings: clock: Add Nuvoton WPCM450 clock/reset controller Jonathan Neuschäfer
2023-04-28 19:02 ` Jonathan Neuschäfer
2023-04-28 19:02 ` [PATCH v8 2/2] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver Jonathan Neuschäfer
2023-04-28 19:02 ` Jonathan Neuschäfer
2023-07-21 0:02 ` Stephen Boyd
2023-07-21 0:02 ` Stephen Boyd
2023-07-22 15:15 ` Jonathan Neuschäfer
2023-07-22 15:15 ` Jonathan Neuschäfer
2023-08-01 19:14 ` Stephen Boyd
2023-08-01 19:14 ` Stephen Boyd
2023-07-22 21:55 ` Jonathan Neuschäfer
2023-07-22 21:55 ` Jonathan Neuschäfer
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.