Introduce Nuvoton Arbel NPCM8XX BMC SoC
Subsystems (9)
Tomer Maimon
maintainerv8 cover Introduce Nuvoton Arbel NPCM8XX BMC SoC
This patchset adds initial support for the Nuvoton Arbel NPCM8XX Board Management controller (BMC) SoC family.
The Nuvoton Arbel NPCM8XX SoC is a fourth-generation BMC. The NPCM8XX computing subsystem comprises a quadcore ARM Cortex A35 ARM-V8 architecture.
This patchset adds minimal architecture and drivers such as:
Clocksource, Clock, Reset, and WD.
Some of the Arbel NPCM8XX peripherals are based on Poleg NPCM7XX.
This patchset was tested on the Arbel NPCM8XX evaluation board.
Changes since version 7:
- NPCM8XX clock driver
- The clock and reset registers using the same memory region, due to it the clock driver should claim the ioremap directly without checking the memory region.
Changes since version 6:
- NPCM reset driver
- Modify warning message.
- dt-bindings: serial: 8250: Add npcm845 compatible string patch accepted, due to it the patch removed from the patchset.
Changes since version 5:
- NPCM8XX clock driver
- Remove refclk if devm_of_clk_add_hw_provider function failed.
- NPCM8XX clock source driver
- Remove NPCM8XX TIMER_OF_DECLARE support, using the same as NPCM7XX.
Changes since version 4:
- NPCM8XX clock driver
- Use the same quote in the dt-binding file.
Changes since version 3:
- NPCM8XX clock driver
- Rename NPCM8xx clock dt-binding header file.
- Remove unused structures.
- Improve Handling the clocks registration.
- NPCM reset driver
- Add ref phandle to dt-binding.
Changes since version 2:
- Remove NPCM8xx WDT compatible patch.
- Remove NPCM8xx UART compatible patch.
- NPCM8XX clock driver
- Add debug new line.
- Add 25M fixed rate clock.
- Remove unused clocks and clock name from dt-binding.
- NPCM reset driver
- Revert to npcm7xx dt-binding.
- Skip dt binding quotes.
- Adding DTS backward compatibility.
- Remove NPCM8xx binding include file.
- Warp commit message.
- NPCM8XX device tree:
- Remove unused clock nodes (used in the clock driver)
- Modify gcr and rst node names.
Changes since version 1:
- NPCM8XX clock driver
- Modify dt-binding.
- Remove unsed definition and include.
- Include alphabetically.
- Use clock devm.
- NPCM reset driver
- Modify dt-binding.
- Modify syscon name.
- Add syscon support to NPCM7XX dts reset node.
- use data structure.
- NPCM8XX device tree:
- Modify evb compatible name.
- Add NPCM7xx compatible.
- Remove disable nodes from the EVB DTS.
Tomer Maimon (16):
dt-bindings: timer: npcm: Add npcm845 compatible string
dt-bindings: watchdog: npcm: Add npcm845 compatible string
dt-binding: clk: npcm845: Add binding for Nuvoton NPCM8XX Clock
clk: npcm8xx: add clock controller
dt-bindings: reset: npcm: add GCR syscon property
ARM: dts: nuvoton: add reset syscon property
reset: npcm: using syscon instead of device data
dt-bindings: reset: npcm: Add support for NPCM8XX
reset: npcm: Add NPCM8XX support
dt-bindings: arm: npcm: Add maintainer
dt-bindings: arm: npcm: Add nuvoton,npcm845 compatible string
dt-bindings: arm: npcm: Add nuvoton,npcm845 GCR compatible string
arm64: npcm: Add support for Nuvoton NPCM8XX BMC SoC
arm64: dts: nuvoton: Add initial NPCM8XX device tree
arm64: dts: nuvoton: Add initial NPCM845 EVB device tree
arm64: defconfig: Add Nuvoton NPCM family support
.../devicetree/bindings/arm/npcm/npcm.yaml | 7 + .../bindings/arm/npcm/nuvoton,gcr.yaml | 2 + .../bindings/clock/nuvoton,npcm845-clk.yaml | 49 ++ .../bindings/reset/nuvoton,npcm750-reset.yaml | 10 +- .../bindings/timer/nuvoton,npcm7xx-timer.yaml | 2 + .../bindings/watchdog/nuvoton,npcm-wdt.txt | 3 +- MAINTAINERS | 2 + arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 1 + arch/arm64/Kconfig.platforms | 11 + arch/arm64/boot/dts/Makefile | 1 + arch/arm64/boot/dts/nuvoton/Makefile | 2 + .../dts/nuvoton/nuvoton-common-npcm8xx.dtsi | 170 +++++ .../boot/dts/nuvoton/nuvoton-npcm845-evb.dts | 30 + .../boot/dts/nuvoton/nuvoton-npcm845.dtsi | 76 +++ arch/arm64/configs/defconfig | 3 + drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-npcm8xx.c | 610 ++++++++++++++++++ drivers/reset/reset-npcm.c | 207 +++++- .../dt-bindings/clock/nuvoton,npcm845-clk.h | 49 ++ 20 files changed, 1206 insertions(+), 36 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,npcm845-clk.yaml create mode 100644 arch/arm64/boot/dts/nuvoton/Makefile create mode 100644 arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi create mode 100644 arch/arm64/boot/dts/nuvoton/nuvoton-npcm845-evb.dts create mode 100644 arch/arm64/boot/dts/nuvoton/nuvoton-npcm845.dtsi create mode 100644 drivers/clk/clk-npcm8xx.c create mode 100644 include/dt-bindings/clock/nuvoton,npcm845-clk.h
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 1/16 dt-bindings: timer: npcm: Add npcm845 compatible string
Add a compatible string for Nuvoton BMC NPCM845 timer.
--- .../devicetree/bindings/timer/nuvoton,npcm7xx-timer.yaml | 2 ++ 1 file changed, 2 insertions(+)
Documentation/devicetree/bindings/timer/nuvoton,npcm7xx-timer.yaml +2 -0
@@ -8,12 +8,14 @@ title: Nuvoton NPCM7xx timer maintainers: - Jonathan Neuschäfer <[email protected]> + - Tomer Maimon <[email protected]> properties: compatible: enum: - nuvoton,wpcm450-timer # for Hermon WPCM450 - nuvoton,npcm750-timer # for Poleg NPCM750 + - nuvoton,npcm845-timer # for Arbel NPCM845 reg: maxItems: 1
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 2/16 dt-bindings: watchdog: npcm: Add npcm845 compatible string
In reply to Tomer MaimonAdd a compatible string for Nuvoton BMC NPCM845 watchdog.
--- .../devicetree/bindings/watchdog/nuvoton,npcm-wdt.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/watchdog/nuvoton,npcm-wdt.txt +2 -1
@@ -6,7 +6,8 @@ expiry. Required properties: - compatible : "nuvoton,npcm750-wdt" for NPCM750 (Poleg), or - "nuvoton,wpcm450-wdt" for WPCM450 (Hermon). + "nuvoton,wpcm450-wdt" for WPCM450 (Hermon), or + "nuvoton,npcm845-wdt" for NPCM845 (Arbel). - reg : Offset and length of the register set for the device. - interrupts : Contain the timer interrupt with flags for falling edge.
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 3/16 dt-binding: clk: npcm845: Add binding for Nuvoton NPCM8XX Clock
In reply to Tomer MaimonAdd binding for the Arbel BMC NPCM8XX Clock controller.
--- .../bindings/clock/nuvoton,npcm845-clk.yaml | 49 +++++++++++++++++++ .../dt-bindings/clock/nuvoton,npcm845-clk.h | 49 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,npcm845-clk.yaml create mode 100644 include/dt-bindings/clock/nuvoton,npcm845-clk.h
Documentation/devicetree/bindings/clock/nuvoton,npcm845-clk.yaml new file +49 -0
@@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/nuvoton,npcm845-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton NPCM8XX Clock Controller Binding + +maintainers: + - Tomer Maimon <[email protected]> + +description: | + Nuvoton Arbel BMC NPCM8XX contains an integrated clock controller, which + generates and supplies clocks to all modules within the BMC. + +properties: + compatible: + enum: + - nuvoton,npcm845-clk + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + description: + See include/dt-bindings/clock/nuvoton,npcm8xx-clock.h for the full + list of NPCM8XX clock IDs. + +required: + - compatible + - reg + - '#clock-cells' + +additionalProperties: false + +examples: + - | + ahb { + #address-cells = <2>; + #size-cells = <2>; + + clock-controller@f0801000 { + compatible = "nuvoton,npcm845-clk"; + reg = <0x0 0xf0801000 0x0 0x1000>; + #clock-cells = <1>; + }; + }; +...
include/dt-bindings/clock/nuvoton,npcm845-clk.h new file +49 -0
49 lines not on this page · raw message
Signature · 1 line
2.33.0
Re: [PATCH v8 03/16] dt-binding: clk: npcm845: Add binding for Nuvoton NPCM8XX Clock
Quoting Tomer Maimon (2022-07-11 05:35:06)
Quoted 5 lines
Add binding for the Arbel BMC NPCM8XX Clock controller.
Signed-off-by Tomer Maimon <[email protected]>Reviewed-by Krzysztof Kozlowski <[email protected]>---
Tomer Maimon
maintainerPATCH v8 4/16 clk: npcm8xx: add clock controller
In reply to Tomer MaimonNuvoton Arbel BMC NPCM8XX contains an integrated clock controller which generates and supplies clocks to all modules within the BMC.
--- drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-npcm8xx.c | 610 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 617 insertions(+) create mode 100644 drivers/clk/clk-npcm8xx.c
drivers/clk/Kconfig +6 -0
@@ -428,6 +428,12 @@ config COMMON_CLK_K210 help Support for the Canaan Kendryte K210 RISC-V SoC clocks. +config COMMON_CLK_NPCM8XX + tristate "Clock driver for the NPCM8XX SoC Family" + depends on ARCH_NPCM || COMPILE_TEST + help + This driver supports the clocks on the Nuvoton BMC NPCM8XX SoC Family. + source "drivers/clk/actions/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/baikal-t1/Kconfig"
drivers/clk/Makefile +1 -0
@@ -49,6 +49,7 @@ obj-$(CONFIG_ARCH_MILBEAUT_M10V) += clk-milbeaut.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NPCM7XX) += clk-npcm7xx.o +obj-$(CONFIG_COMMON_CLK_NPCM8XX) += clk-npcm8xx.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_COMMON_CLK_OXNAS) += clk-oxnas.o obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o
drivers/clk/clk-npcm8xx.c new file +610 -0
610 lines not on this page · raw message
Signature · 1 line
2.33.0
Stephen Boyd
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Quoting Tomer Maimon (2022-07-11 05:35:07)
Quoted 16 lines
Nuvoton Arbel BMC NPCM8XX contains an integrated clock controller which generates and supplies clocks to all modules within the BMC.
Signed-off-by Tomer Maimon <[email protected]>--- drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-npcm8xx.c | 610 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 617 insertions(+) create mode 100644 drivers/clk/clk-npcm8xx.cdrivers/clk/Kconfig +0
-0
drivers/clk/Kconfig config COMMON_CLK_K210 · line 435
Quoted 8 lines
drivers/clk/Kconfig +5
-0@@ -428,6 +428,12 @@ config COMMON_CLK_K210 help Support for the Canaan Kendryte K210 RISC-V SoC clocks. +config COMMON_CLK_NPCM8XX + tristate "Clock driver for the NPCM8XX SoC Family" + depends on ARCH_NPCM || COMPILE_TEST + help + This driver supports the clocks on the Nuvoton BMC NPCM8XX SoC Family.
Please put this next to COMMON_CLK_NXP so that it is sort of sorted based on Kconfig symbol.
Quoted 172 lines
drivers/clk/Kconfig +1
-0+ source "drivers/clk/actions/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/baikal-t1/Kconfig"drivers/clk/clk-npcm8xx.c new file +162
-0@@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Nuvoton NPCM8xx Clock Generator +* All the clocks are initialized by the bootloader, so this driver allow only +* reading of current settings directly from the hardware. +* +* Copyright (C) 2020 Nuvoton Technologies +* Author: Tomer Maimon <[email protected]> +*/ + +#include <linux/bitfield.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/nuvoton,npcm845-clk.h> + +#define NPCM8XX_REF_CLK 25000000 + +struct npcm8xx_clk_pll { +struct clk_hw hw; +void __iomem *pllcon; +u8 flags; +}; + +#define to_npcm8xx_clk_pll (_hw) container_of(_hw, struct npcm8xx_clk_pll, hw) + +#define PLLCON_LOKI BIT(31) +#define PLLCON_LOKS BIT(30) +#define PLLCON_FBDV GENMASK(27, 16) +#define PLLCON_OTDV2 GENMASK(15, 13) +#define PLLCON_PWDEN BIT(12) +#define PLLCON_OTDV1 GENMASK(10, 8) +#define PLLCON_INDV GENMASK(5, 0) + +static unsigned long npcm8xx_clk_pll_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct npcm8xx_clk_pll *pll =to_npcm8xx_clk_pll (hw); +unsigned long fbdv, indv, otdv1, otdv2; +unsigned int val; +u64 ret; + +if (parent_rate ==0 ) { +pr_debug ("%s: parent rate is zero\n" , __func__); +return 0 ; + } + + val =readl_relaxed (pll->pllcon); + + indv =FIELD_GET (PLLCON_INDV , val); + fbdv =FIELD_GET (PLLCON_FBDV , val); + otdv1 =FIELD_GET (PLLCON_OTDV1 , val); + otdv2 =FIELD_GET (PLLCON_OTDV2 , val); + + ret = (u64 )parent_rate * fbdv; +do_div (ret, indv * otdv1 * otdv2); + +return ret; +} + +static const struct clk_ops npcm8xx_clk_pll_ops = { + .recalc_rate = npcm8xx_clk_pll_recalc_rate, +}; + +static struct clk_hw * +npcm8xx_clk_register_pll (struct device *dev,void __iomem *pllcon, +const char *name,const char *parent_name, +unsigned long flags) +{ +struct npcm8xx_clk_pll *pll; +struct clk_init_data init; +struct clk_hw *hw; +int ret; + + pll =devm_kzalloc (dev,sizeof (*pll),GFP_KERNEL ); +if (!pll) +return ERR_PTR (-ENOMEM ); + +pr_debug ("%s reg, name=%s, p=%s\n" , __func__, name, parent_name); + + init.name = name; + init.ops = &npcm8xx_clk_pll_ops; + init.parent_names = &parent_name; + init.num_parents =1 ; + init.flags = flags; + + pll->pllcon = pllcon; + pll->hw.init = &init; + + hw = &pll->hw; + + ret =devm_clk_hw_register (dev, hw); +if (ret) +return ERR_PTR (ret); + +return hw; +} + +#define NPCM8XX_CLKEN1 (0x00) +#define NPCM8XX_CLKEN2 (0x28) +#define NPCM8XX_CLKEN3 (0x30) +#define NPCM8XX_CLKEN4 (0x70) +#define NPCM8XX_CLKSEL (0x04) +#define NPCM8XX_CLKDIV1 (0x08) +#define NPCM8XX_CLKDIV2 (0x2C) +#define NPCM8XX_CLKDIV3 (0x58) +#define NPCM8XX_CLKDIV4 (0x7C) +#define NPCM8XX_PLLCON0 (0x0C) +#define NPCM8XX_PLLCON1 (0x10) +#define NPCM8XX_PLLCON2 (0x54) +#define NPCM8XX_SWRSTR (0x14) +#define NPCM8XX_IRQWAKECON (0x18) +#define NPCM8XX_IRQWAKEFLAG (0x1C) +#define NPCM8XX_IPSRST1 (0x20) +#define NPCM8XX_IPSRST2 (0x24) +#define NPCM8XX_IPSRST3 (0x34) +#define NPCM8XX_WD0RCR (0x38) +#define NPCM8XX_WD1RCR (0x3C) +#define NPCM8XX_WD2RCR (0x40) +#define NPCM8XX_SWRSTC1 (0x44) +#define NPCM8XX_SWRSTC2 (0x48) +#define NPCM8XX_SWRSTC3 (0x4C) +#define NPCM8XX_SWRSTC4 (0x50) +#define NPCM8XX_CORSTC (0x5C) +#define NPCM8XX_PLLCONG (0x60) +#define NPCM8XX_AHBCKFI (0x64) +#define NPCM8XX_SECCNT (0x68) +#define NPCM8XX_CNTR25M (0x6C) +#define NPCM8XX_THRTL_CNT (0xC0) + +struct npcm8xx_clk_mux_data { +u8 shift; +u8 mask; +u32 *table; +const char *name; +const char *const *parent_names; +u8 num_parents; +unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +}; + +struct npcm8xx_clk_div_data { +u32 reg; +u8 shift; +u8 width; +const char *name; +const char *parent_name; +u8 clk_divider_flags; +unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant
drivers/clk/clk-npcm8xx.c line 172
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +}; + +struct npcm8xx_clk_pll_data { +u32 reg; +const char *name; +const char *parent_name;
Any reason why we're not using clk_parent_data or direct clk_hw pointers?
Quoted 234 lines
drivers/clk/clk-npcm8xx.c new file +234
-0+unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +}; + +/* +* Single copy of strings used to refer to clocks within this driver indexed by +* above enum. +*/ +#define NPCM8XX_CLK_S_REFCLK "refclk" +#define NPCM8XX_CLK_S_SYSBYPCK "sysbypck" +#define NPCM8XX_CLK_S_MCBYPCK "mcbypck" +#define NPCM8XX_CLK_S_PLL0 "pll0" +#define NPCM8XX_CLK_S_PLL1 "pll1" +#define NPCM8XX_CLK_S_PLL1_DIV2 "pll1_div2" +#define NPCM8XX_CLK_S_PLL2 "pll2" +#define NPCM8XX_CLK_S_PLL_GFX "pll_gfx" +#define NPCM8XX_CLK_S_PLL2_DIV2 "pll2_div2" +#define NPCM8XX_CLK_S_PIX_MUX "gfx_pixel" +#define NPCM8XX_CLK_S_MC_MUX "mc_phy" +#define NPCM8XX_CLK_S_CPU_MUX "cpu"/* AKA system clock */ +#define NPCM8XX_CLK_S_MC "mc" +#define NPCM8XX_CLK_S_AXI "axi"/* AKA CLK2 */ +#define NPCM8XX_CLK_S_AHB "ahb"/* AKA CLK4 */ +#define NPCM8XX_CLK_S_CLKOUT_MUX "clkout_mux" +#define NPCM8XX_CLK_S_UART_MUX "uart_mux" +#define NPCM8XX_CLK_S_SD_MUX "sd_mux" +#define NPCM8XX_CLK_S_GFXM_MUX "gfxm_mux" +#define NPCM8XX_CLK_S_SU_MUX "serial_usb_mux" +#define NPCM8XX_CLK_S_DVC_MUX "dvc_mux" +#define NPCM8XX_CLK_S_GFX_MUX "gfx_mux" +#define NPCM8XX_CLK_S_ADC_MUX "adc_mux" +#define NPCM8XX_CLK_S_SPI0 "spi0" +#define NPCM8XX_CLK_S_SPI1 "spi1" +#define NPCM8XX_CLK_S_SPI3 "spi3" +#define NPCM8XX_CLK_S_SPIX "spix" +#define NPCM8XX_CLK_S_APB1 "apb1" +#define NPCM8XX_CLK_S_APB2 "apb2" +#define NPCM8XX_CLK_S_APB3 "apb3" +#define NPCM8XX_CLK_S_APB4 "apb4" +#define NPCM8XX_CLK_S_APB5 "apb5" +#define NPCM8XX_CLK_S_APB19 "apb19" +#define NPCM8XX_CLK_S_TOCK "tock" +#define NPCM8XX_CLK_S_CLKOUT "clkout" +#define NPCM8XX_CLK_S_PRE_ADC "pre adc" +#define NPCM8XX_CLK_S_UART "uart" +#define NPCM8XX_CLK_S_UART2 "uart2" +#define NPCM8XX_CLK_S_TIMER "timer" +#define NPCM8XX_CLK_S_MMC "mmc" +#define NPCM8XX_CLK_S_SDHC "sdhc" +#define NPCM8XX_CLK_S_ADC "adc" +#define NPCM8XX_CLK_S_GFX "gfx0_gfx1_mem" +#define NPCM8XX_CLK_S_USBIF "serial_usbif" +#define NPCM8XX_CLK_S_USB_HOST "usb_host" +#define NPCM8XX_CLK_S_USB_BRIDGE "usb_bridge" +#define NPCM8XX_CLK_S_PCI "pci" +#define NPCM8XX_CLK_S_TH "th" +#define NPCM8XX_CLK_S_ATB "atb" +#define NPCM8XX_CLK_S_PRE_CLK "pre_clk" + +#define NPCM8XX_CLK_S_RG_MUX "rg_mux" +#define NPCM8XX_CLK_S_RCP_MUX "rcp_mux" +#define NPCM8XX_CLK_S_RG "rg" +#define NPCM8XX_CLK_S_RCP "rcp" + +static u32 pll_mux_table[] = {0 ,1 ,2 ,3 }; +static const char *const pll_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 cpuck_mux_table[] = {0 ,1 ,2 ,3 ,7 }; +static const char *const cpuck_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_SYSBYPCK , +NPCM8XX_CLK_S_PLL2 , +}; + +static u32 pixcksel_mux_table[] = {0 ,2 }; +static const char *const pixcksel_mux_parents[] = { +NPCM8XX_CLK_S_PLL_GFX , +NPCM8XX_CLK_S_REFCLK , +}; + +static u32 sucksel_mux_table[] = {2 ,3 }; +static const char *const sucksel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 mccksel_mux_table[] = {0 ,2 ,3 }; +static const char *const mccksel_mux_parents[] = { +NPCM8XX_CLK_S_PLL1_DIV2 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_MCBYPCK , +}; + +static u32 clkoutsel_mux_table[] = {0 ,1 ,2 ,3 ,4 }; +static const char *const clkoutsel_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL_GFX ,// divided by 2 +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 gfxmsel_mux_table[] = {2 ,3 }; +static const char *const gfxmsel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 dvcssel_mux_table[] = {2 ,3 }; +static const char *const dvcssel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2 , +}; + +static const struct npcm8xx_clk_pll_data npcm8xx_plls[] = { + {NPCM8XX_PLLCON0 ,NPCM8XX_CLK_S_PLL0 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCON1 ,NPCM8XX_CLK_S_PLL1 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCON2 ,NPCM8XX_CLK_S_PLL2 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCONG ,NPCM8XX_CLK_S_PLL_GFX ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, +}; + +static const struct npcm8xx_clk_mux_data npcm8xx_muxes[] = { + {0 ,GENMASK (1 ,0 ), cpuck_mux_table,NPCM8XX_CLK_S_CPU_MUX , + cpuck_mux_parents,ARRAY_SIZE (cpuck_mux_parents),CLK_IS_CRITICAL , +NPCM8XX_CLK_CPU }, + + {4 ,GENMASK (1 ,0 ), pixcksel_mux_table,NPCM8XX_CLK_S_PIX_MUX , + pixcksel_mux_parents,ARRAY_SIZE (pixcksel_mux_parents),0 , +NPCM8XX_CLK_GFX_PIXEL }, + + {6 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_SD_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {8 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_UART_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {10 ,GENMASK (1 ,0 ), sucksel_mux_table,NPCM8XX_CLK_S_SU_MUX , + sucksel_mux_parents,ARRAY_SIZE (sucksel_mux_parents),0 ,-1 }, + + {12 ,GENMASK (1 ,0 ), mccksel_mux_table,NPCM8XX_CLK_S_MC_MUX , + mccksel_mux_parents,ARRAY_SIZE (mccksel_mux_parents),0 ,-1 }, + + {14 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_ADC_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {16 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_GFX_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {18 ,GENMASK (2 ,0 ), clkoutsel_mux_table,NPCM8XX_CLK_S_CLKOUT_MUX , + clkoutsel_mux_parents,ARRAY_SIZE (clkoutsel_mux_parents),0 ,-1 }, + + {21 ,GENMASK (1 ,0 ), gfxmsel_mux_table,NPCM8XX_CLK_S_GFXM_MUX , + gfxmsel_mux_parents,ARRAY_SIZE (gfxmsel_mux_parents),0 ,-1 }, + + {23 ,GENMASK (1 ,0 ), dvcssel_mux_table,NPCM8XX_CLK_S_DVC_MUX , + dvcssel_mux_parents,ARRAY_SIZE (dvcssel_mux_parents),0 ,-1 }, + + {25 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_RG_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {27 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_RCP_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, +}; + +/* configurable dividers: */ +static const struct npcm8xx_clk_div_data npcm8xx_divs[] = { + {NPCM8XX_CLKDIV1 ,28 ,3 ,NPCM8XX_CLK_S_ADC ,NPCM8XX_CLK_S_PRE_ADC , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_ADC }, +/* bit 30-28 ADCCKDIV*/ + {NPCM8XX_CLKDIV1 ,26 ,2 ,NPCM8XX_CLK_S_AHB ,NPCM8XX_CLK_S_PRE_CLK , +CLK_DIVIDER_READ_ONLY ,CLK_IS_CRITICAL ,NPCM8XX_CLK_AHB }, +/* bit 28-26 CLK4DIV*/ + {NPCM8XX_CLKDIV1 ,21 ,5 ,NPCM8XX_CLK_S_PRE_ADC , +NPCM8XX_CLK_S_ADC_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_PRE_ADC }, +/* bit 25-21 PRE-ADCCKDIV*/ + {NPCM8XX_CLKDIV1 ,16 ,5 ,NPCM8XX_CLK_S_UART , +NPCM8XX_CLK_S_UART_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_UART }, +/* bit 20-16 UARTDIV*/ + {NPCM8XX_CLKDIV1 ,11 ,5 ,NPCM8XX_CLK_S_MMC , +NPCM8XX_CLK_S_SD_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_MMC }, +/* bit 15-11 MMCCKDIV*/ + {NPCM8XX_CLKDIV1 ,6 ,5 ,NPCM8XX_CLK_S_SPI3 , +NPCM8XX_CLK_S_AHB ,0 ,0 ,NPCM8XX_CLK_SPI3 }, +/* bit 10-6 AHB3CKDIV*/ + {NPCM8XX_CLKDIV1 ,2 ,4 ,NPCM8XX_CLK_S_PCI , +NPCM8XX_CLK_S_GFX_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_PCI }, +/* bit 5-2 PCICKDIV*/ + + {NPCM8XX_CLKDIV2 ,30 ,2 ,NPCM8XX_CLK_S_APB4 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB4 }, +/* bit 31-30 APB4CKDIV*/ + {NPCM8XX_CLKDIV2 ,28 ,2 ,NPCM8XX_CLK_S_APB3 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB3 }, +/* bit 29-28 APB3CKDIV*/ + {NPCM8XX_CLKDIV2 ,26 ,2 ,NPCM8XX_CLK_S_APB2 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB2 }, +/* bit 28-26 APB2CKDIV*/ + {NPCM8XX_CLKDIV2 ,24 ,2 ,NPCM8XX_CLK_S_APB1 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB1 }, +/* bit 25-24 APB1CKDIV*/ + {NPCM8XX_CLKDIV2 ,22 ,2 ,NPCM8XX_CLK_S_APB5 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB5 }, +/* bit 23-22 APB5CKDIV*/ + {NPCM8XX_CLKDIV2 ,16 ,5 ,NPCM8XX_CLK_S_CLKOUT ,NPCM8XX_CLK_S_CLKOUT_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_CLKOUT }, +/* bit 20-16 CLKOUTDIV*/ + {NPCM8XX_CLKDIV2 ,13 ,3 ,NPCM8XX_CLK_S_GFX ,NPCM8XX_CLK_S_GFX_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_GFX }, +/* bit 15-13 GFXCKDIV*/ + {NPCM8XX_CLKDIV2 ,8 ,5 ,NPCM8XX_CLK_S_USB_BRIDGE ,NPCM8XX_CLK_S_SU_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SU }, +/* bit 12-8 SUCKDIV*/ + {NPCM8XX_CLKDIV2 ,4 ,4 ,NPCM8XX_CLK_S_USB_HOST ,NPCM8XX_CLK_S_SU_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SU48 }, +/* bit 8-4 SU48CKDIV*/ + {NPCM8XX_CLKDIV2 ,0 ,4 ,NPCM8XX_CLK_S_SDHC ,
drivers/clk/clk-npcm8xx.c line 416
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+ NPCM8XX_CLK_S_SD_MUX, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SDHC} + ,/* bit 3-0 SD1CKDIV*/ + + {NPCM8XX_CLKDIV3, 16, 8, NPCM8XX_CLK_S_SPI1, + NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI1}, +/* bit 23-16 SPI1CKDV*/ + {NPCM8XX_CLKDIV3, 11, 5, NPCM8XX_CLK_S_UART2, + NPCM8XX_CLK_S_UART_MUX, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_UART2}, +/* bit 15-11 UARTDIV2*/ + {NPCM8XX_CLKDIV3, 6, 5, NPCM8XX_CLK_S_SPI0,
Please run checkpatch, add a space after '{' and before '}'
Quoted 26 lines
drivers/clk/clk-npcm8xx.c new file +26
-0+ NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI0}, +/* bit 10-6 SPI0CKDV*/ + {NPCM8XX_CLKDIV3, 1, 5, NPCM8XX_CLK_S_SPIX, + NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPIX}, +/* bit 5-1 SPIXCKDV*/ + + {NPCM8XX_CLKDIV4, 28, 4, NPCM8XX_CLK_S_RG, NPCM8XX_CLK_S_RG_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RG}, +/* bit 31-28 RGREFDIV*/ + {NPCM8XX_CLKDIV4, 12, 4, NPCM8XX_CLK_S_RCP, NPCM8XX_CLK_S_RCP_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RCP}, +/* bit 15-12 RCPREFDIV*/ + {NPCM8XX_THRTL_CNT, 0, 2, NPCM8XX_CLK_S_TH, NPCM8XX_CLK_S_CPU_MUX, + CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_TH}, + /* bit 1-0 TH_DIV*/ +}; + +static DEFINE_SPINLOCK (npcm8xx_clk_lock); + +static int npcm8xx_clk_probe (struct platform_device *pdev) +{ +struct clk_hw_onecell_data *npcm8xx_clk_data; +struct device *dev = &pdev->dev; +struct device_node *np = dev->of_node; +void __iomem *clk_base; +struct resource res;
drivers/clk/clk-npcm8xx.c line 452
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+struct clk_hw *hw; +int i, err; + + npcm8xx_clk_data =devm_kzalloc (dev,struct_size (npcm8xx_clk_data, hws, +NPCM8XX_NUM_CLOCKS ), +GFP_KERNEL ); +if (!npcm8xx_clk_data) +return -ENOMEM ; + + err =of_address_to_resource (np,0 , &res);
Why can't we use platform_get_resource()?
drivers/clk/clk-npcm8xx.c line 458
Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+if (err) { +dev_err (dev,"Failed to get resource, ret %d\n" , err); +return err; + } + + clk_base =ioremap (res.start,resource_size (&res));
And use devm_platform_ioremap_resource()?
drivers/clk/clk-npcm8xx.c line 469
Quoted 11 lines
drivers/clk/clk-npcm8xx.c new file +11
-0+if (!clk_base) { +dev_err (&pdev->dev,"Failed to remap I/O memory\n" ); +return -ENOMEM ; + } + + npcm8xx_clk_data->num =NPCM8XX_NUM_CLOCKS ; + +for (i =0 ; i <NPCM8XX_NUM_CLOCKS ; i++) + npcm8xx_clk_data->hws[i] =ERR_PTR (-EPROBE_DEFER ); + +/* Reference 25MHz clock */
Does this exist on the board? If so, I'd make a fixed rate clk in the dts and have 'refclk' be an input in the binding for this clk controller.
Quoted 97 lines
drivers/clk/clk-npcm8xx.c new file +97
-0+ hw =clk_hw_register_fixed_rate (dev,"refclk" ,NULL ,0 ,NPCM8XX_REF_CLK ); +if (IS_ERR (hw)) +return PTR_ERR (hw); + npcm8xx_clk_data->hws[NPCM8XX_CLK_REFCLK ] = hw; + +/* Register plls */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_plls); i++) { +const struct npcm8xx_clk_pll_data *pll_data = &npcm8xx_plls[i]; + + hw =npcm8xx_clk_register_pll (dev, clk_base + pll_data->reg, + pll_data->name, + pll_data->parent_name, + pll_data->flags); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register pll\n" ); + goto unregister_refclk; + } + +if (pll_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[pll_data->onecell_idx] = hw; + } + +/* Register fixed dividers */ + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PLL1_DIV2 , +NPCM8XX_CLK_S_PLL1 ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register fixed div\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PLL2_DIV2 , +NPCM8XX_CLK_S_PLL2 ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register pll div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PRE_CLK , +NPCM8XX_CLK_S_CPU_MUX ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register ckclk div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_AXI , +NPCM8XX_CLK_S_TH ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register axi div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_ATB , +NPCM8XX_CLK_S_AXI ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register atb div2\n" ); + goto unregister_refclk; + } + +/* Register clock dividers specified in npcm8xx_divs */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_divs); i++) { +const struct npcm8xx_clk_div_data *div_data = &npcm8xx_divs[i]; + + hw =devm_clk_hw_register_divider (dev, div_data->name, + div_data->parent_name, + div_data->flags, + clk_base + div_data->reg, + div_data->shift, + div_data->width, + div_data->clk_divider_flags, + &npcm8xx_clk_lock); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register div table\n" ); + goto unregister_refclk; + } + +if (div_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[div_data->onecell_idx] = hw; + } + +/* Register muxes */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_muxes); i++) { +const struct npcm8xx_clk_mux_data *mux_data = &npcm8xx_muxes[i]; + + hw =clk_hw_register_mux_table (dev, mux_data->name, + mux_data->parent_names, + mux_data->num_parents, + mux_data->flags, + clk_base +NPCM8XX_CLKSEL , + mux_data->shift, + mux_data->mask,0 , + mux_data->table, + &npcm8xx_clk_lock); + +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register mux\n" ); + goto err_mux_clk; + }
drivers/clk/clk-npcm8xx.c line 576
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+ +if (mux_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[mux_data->onecell_idx] = hw; + } + + err =devm_of_clk_add_hw_provider (dev, of_clk_hw_onecell_get, + npcm8xx_clk_data); +if (err) { +dev_err (dev,"unable to add clk provider\n" ); + goto unregister_refclk;
The 'hw' pointer doesn't contain 'err' here so probe will return some positive number when this fails?
Quoted 24 lines
drivers/clk/clk-npcm8xx.c new file +24
-0+ } + +return err; + +err_mux_clk: +while (i--) { +if (npcm8xx_muxes[i].onecell_idx >=0 ) +clk_hw_unregister_mux (npcm8xx_clk_data->hws[npcm8xx_muxes[i].onecell_idx]); + } +unregister_refclk: +clk_hw_unregister (npcm8xx_clk_data->hws[NPCM8XX_CLK_REFCLK ]); +return PTR_ERR (hw); +} + +static const struct of_device_id npcm8xx_clk_dt_ids[] = { + { .compatible ="nuvoton,npcm845-clk" , }, + { } +}; +MODULE_DEVICE_TABLE (of, npcm8xx_clk_dt_ids); + +static struct platform_driver npcm8xx_clk_driver = { + .probe = npcm8xx_clk_probe, + .driver = { + .name ="npcm8xx_clk" ,
drivers/clk/clk-npcm8xx.c line 610
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+ .of_match_table = npcm8xx_clk_dt_ids, + }, +}; + +static int __init npcm8xx_clk_driver_init (void ) +{ +return platform_driver_register (&npcm8xx_clk_driver); +} +arch_initcall (npcm8xx_clk_driver_init); +
If it can be a module it needs to unregister the driver on module exit.
Tomer Maimon
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Hi Stephen,
Thanks for your comments
Quoted 21 lines · Stephen Boyd
Quoting Tomer Maimon (2022-07-11 05:35:07)
Quoted 19 lines
Nuvoton Arbel BMC NPCM8XX contains an integrated clock controller which generates and supplies clocks to all modules within the BMC.
Signed-off-by Tomer Maimon <[email protected]>--- drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-npcm8xx.c | 610 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 617 insertions(+) create mode 100644 drivers/clk/clk-npcm8xx.cdrivers/clk/Kconfig +0
-0@@ -428,6 +428,12 @@ config COMMON_CLK_K210 help Support for the Canaan Kendryte K210 RISC-V SoC clocks.
Quoted 8 lines
drivers/clk/Kconfig config COMMON_CLK_K210 · line 435
Quoted 5 lines
drivers/clk/Kconfig +5
-0+config COMMON_CLK_NPCM8XX + tristate "Clock driver for the NPCM8XX SoC Family" + depends on ARCH_NPCM || COMPILE_TEST + help + This driver supports the clocks on the Nuvoton BMC NPCM8XX SoC Family.Please put this next to
COMMON_CLK_NXPso that it is sort of sorted based onKconfigsymbol.
Will do
Quoted 177 lines
Quoted 177 lines
drivers/clk/Kconfig +1
-0+ source "drivers/clk/actions/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/baikal-t1/Kconfig"drivers/clk/clk-npcm8xx.c new file +167
-0@@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Nuvoton NPCM8xx Clock Generator +* All the clocks are initialized by the bootloader, so this driver allow only +* reading of current settings directly from the hardware. +* +* Copyright (C) 2020 Nuvoton Technologies +* Author: Tomer Maimon <[email protected]> +*/ + +#include <linux/bitfield.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/nuvoton,npcm845-clk.h> + +#define NPCM8XX_REF_CLK 25000000 + +struct npcm8xx_clk_pll { +struct clk_hw hw; +void __iomem *pllcon; +u8 flags; +}; + +#define to_npcm8xx_clk_pll (_hw) container_of(_hw, struct npcm8xx_clk_pll, hw) + +#define PLLCON_LOKI BIT(31) +#define PLLCON_LOKS BIT(30) +#define PLLCON_FBDV GENMASK(27, 16) +#define PLLCON_OTDV2 GENMASK(15, 13) +#define PLLCON_PWDEN BIT(12) +#define PLLCON_OTDV1 GENMASK(10, 8) +#define PLLCON_INDV GENMASK(5, 0) + +static unsigned long npcm8xx_clk_pll_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct npcm8xx_clk_pll *pll =to_npcm8xx_clk_pll (hw); +unsigned long fbdv, indv, otdv1, otdv2; +unsigned int val; +u64 ret; + +if (parent_rate ==0 ) { +pr_debug ("%s: parent rate is zero\n" , __func__); +return 0 ; + } + + val =readl_relaxed (pll->pllcon); + + indv =FIELD_GET (PLLCON_INDV , val); + fbdv =FIELD_GET (PLLCON_FBDV , val); + otdv1 =FIELD_GET (PLLCON_OTDV1 , val); + otdv2 =FIELD_GET (PLLCON_OTDV2 , val); + + ret = (u64 )parent_rate * fbdv; +do_div (ret, indv * otdv1 * otdv2); + +return ret; +} + +static const struct clk_ops npcm8xx_clk_pll_ops = { + .recalc_rate = npcm8xx_clk_pll_recalc_rate, +}; + +static struct clk_hw * +npcm8xx_clk_register_pll (struct device *dev,void __iomem *pllcon, +const char *name,const char *parent_name, +unsigned long flags) +{ +struct npcm8xx_clk_pll *pll; +struct clk_init_data init; +struct clk_hw *hw; +int ret; + + pll =devm_kzalloc (dev,sizeof (*pll),GFP_KERNEL ); +if (!pll) +return ERR_PTR (-ENOMEM ); + +pr_debug ("%s reg, name=%s, p=%s\n" , __func__, name, parent_name); + + init.name = name; + init.ops = &npcm8xx_clk_pll_ops; + init.parent_names = &parent_name; + init.num_parents =1 ; + init.flags = flags; + + pll->pllcon = pllcon; + pll->hw.init = &init; + + hw = &pll->hw; + + ret =devm_clk_hw_register (dev, hw); +if (ret) +return ERR_PTR (ret); + +return hw; +} + +#define NPCM8XX_CLKEN1 (0x00) +#define NPCM8XX_CLKEN2 (0x28) +#define NPCM8XX_CLKEN3 (0x30) +#define NPCM8XX_CLKEN4 (0x70) +#define NPCM8XX_CLKSEL (0x04) +#define NPCM8XX_CLKDIV1 (0x08) +#define NPCM8XX_CLKDIV2 (0x2C) +#define NPCM8XX_CLKDIV3 (0x58) +#define NPCM8XX_CLKDIV4 (0x7C) +#define NPCM8XX_PLLCON0 (0x0C) +#define NPCM8XX_PLLCON1 (0x10) +#define NPCM8XX_PLLCON2 (0x54) +#define NPCM8XX_SWRSTR (0x14) +#define NPCM8XX_IRQWAKECON (0x18) +#define NPCM8XX_IRQWAKEFLAG (0x1C) +#define NPCM8XX_IPSRST1 (0x20) +#define NPCM8XX_IPSRST2 (0x24) +#define NPCM8XX_IPSRST3 (0x34) +#define NPCM8XX_WD0RCR (0x38) +#define NPCM8XX_WD1RCR (0x3C) +#define NPCM8XX_WD2RCR (0x40) +#define NPCM8XX_SWRSTC1 (0x44) +#define NPCM8XX_SWRSTC2 (0x48) +#define NPCM8XX_SWRSTC3 (0x4C) +#define NPCM8XX_SWRSTC4 (0x50) +#define NPCM8XX_CORSTC (0x5C) +#define NPCM8XX_PLLCONG (0x60) +#define NPCM8XX_AHBCKFI (0x64) +#define NPCM8XX_SECCNT (0x68) +#define NPCM8XX_CNTR25M (0x6C) +#define NPCM8XX_THRTL_CNT (0xC0) + +struct npcm8xx_clk_mux_data { +u8 shift; +u8 mask; +u32 *table; +const char *name; +const char *const *parent_names; +u8 num_parents; +unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +}; + +struct npcm8xx_clk_div_data { +u32 reg; +u8 shift; +u8 width; +const char *name; +const char *parent_name; +u8 clk_divider_flags; +unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +};
Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 172
Quoted 5 lines
drivers/clk/clk-npcm8xx.c new file +5
-0+ +struct npcm8xx_clk_pll_data { +u32 reg; +const char *name; +const char *parent_name;Any reason why we're not using
clk_parent_dataor directclk_hwpointers?
For more historical reasons, I did the same method as done in the NPCM7XX driver.
The clk_init_data struct can use * const *parent_names,
https://elixir.bootlin.com/linux/v5.19-rc6/source/include/linux/clk-provider.h#L289
Is it problematic?
Quoted 239 lines
Quoted 239 lines
drivers/clk/clk-npcm8xx.c new file +238
-0+unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +}; + +/* +* Single copy of strings used to refer to clocks within this driver indexed by +* above enum. +*/ +#define NPCM8XX_CLK_S_REFCLK "refclk" +#define NPCM8XX_CLK_S_SYSBYPCK "sysbypck" +#define NPCM8XX_CLK_S_MCBYPCK "mcbypck" +#define NPCM8XX_CLK_S_PLL0 "pll0" +#define NPCM8XX_CLK_S_PLL1 "pll1" +#define NPCM8XX_CLK_S_PLL1_DIV2 "pll1_div2" +#define NPCM8XX_CLK_S_PLL2 "pll2" +#define NPCM8XX_CLK_S_PLL_GFX "pll_gfx" +#define NPCM8XX_CLK_S_PLL2_DIV2 "pll2_div2" +#define NPCM8XX_CLK_S_PIX_MUX "gfx_pixel" +#define NPCM8XX_CLK_S_MC_MUX "mc_phy" +#define NPCM8XX_CLK_S_CPU_MUX "cpu"/* AKA system clock */ +#define NPCM8XX_CLK_S_MC "mc" +#define NPCM8XX_CLK_S_AXI "axi"/* AKA CLK2 */ +#define NPCM8XX_CLK_S_AHB "ahb"/* AKA CLK4 */ +#define NPCM8XX_CLK_S_CLKOUT_MUX "clkout_mux" +#define NPCM8XX_CLK_S_UART_MUX "uart_mux" +#define NPCM8XX_CLK_S_SD_MUX "sd_mux" +#define NPCM8XX_CLK_S_GFXM_MUX "gfxm_mux" +#define NPCM8XX_CLK_S_SU_MUX "serial_usb_mux" +#define NPCM8XX_CLK_S_DVC_MUX "dvc_mux" +#define NPCM8XX_CLK_S_GFX_MUX "gfx_mux" +#define NPCM8XX_CLK_S_ADC_MUX "adc_mux" +#define NPCM8XX_CLK_S_SPI0 "spi0" +#define NPCM8XX_CLK_S_SPI1 "spi1" +#define NPCM8XX_CLK_S_SPI3 "spi3" +#define NPCM8XX_CLK_S_SPIX "spix" +#define NPCM8XX_CLK_S_APB1 "apb1" +#define NPCM8XX_CLK_S_APB2 "apb2" +#define NPCM8XX_CLK_S_APB3 "apb3" +#define NPCM8XX_CLK_S_APB4 "apb4" +#define NPCM8XX_CLK_S_APB5 "apb5" +#define NPCM8XX_CLK_S_APB19 "apb19" +#define NPCM8XX_CLK_S_TOCK "tock" +#define NPCM8XX_CLK_S_CLKOUT "clkout" +#define NPCM8XX_CLK_S_PRE_ADC "pre adc" +#define NPCM8XX_CLK_S_UART "uart" +#define NPCM8XX_CLK_S_UART2 "uart2" +#define NPCM8XX_CLK_S_TIMER "timer" +#define NPCM8XX_CLK_S_MMC "mmc" +#define NPCM8XX_CLK_S_SDHC "sdhc" +#define NPCM8XX_CLK_S_ADC "adc" +#define NPCM8XX_CLK_S_GFX "gfx0_gfx1_mem" +#define NPCM8XX_CLK_S_USBIF "serial_usbif" +#define NPCM8XX_CLK_S_USB_HOST "usb_host" +#define NPCM8XX_CLK_S_USB_BRIDGE "usb_bridge" +#define NPCM8XX_CLK_S_PCI "pci" +#define NPCM8XX_CLK_S_TH "th" +#define NPCM8XX_CLK_S_ATB "atb" +#define NPCM8XX_CLK_S_PRE_CLK "pre_clk" + +#define NPCM8XX_CLK_S_RG_MUX "rg_mux" +#define NPCM8XX_CLK_S_RCP_MUX "rcp_mux" +#define NPCM8XX_CLK_S_RG "rg" +#define NPCM8XX_CLK_S_RCP "rcp" + +static u32 pll_mux_table[] = {0 ,1 ,2 ,3 }; +static const char *const pll_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 cpuck_mux_table[] = {0 ,1 ,2 ,3 ,7 }; +static const char *const cpuck_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_SYSBYPCK , +NPCM8XX_CLK_S_PLL2 , +}; + +static u32 pixcksel_mux_table[] = {0 ,2 }; +static const char *const pixcksel_mux_parents[] = { +NPCM8XX_CLK_S_PLL_GFX , +NPCM8XX_CLK_S_REFCLK , +}; + +static u32 sucksel_mux_table[] = {2 ,3 }; +static const char *const sucksel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 mccksel_mux_table[] = {0 ,2 ,3 }; +static const char *const mccksel_mux_parents[] = { +NPCM8XX_CLK_S_PLL1_DIV2 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_MCBYPCK , +}; + +static u32 clkoutsel_mux_table[] = {0 ,1 ,2 ,3 ,4 }; +static const char *const clkoutsel_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL_GFX ,// divided by 2 +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 gfxmsel_mux_table[] = {2 ,3 }; +static const char *const gfxmsel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 dvcssel_mux_table[] = {2 ,3 }; +static const char *const dvcssel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2 , +}; + +static const struct npcm8xx_clk_pll_data npcm8xx_plls[] = { + {NPCM8XX_PLLCON0 ,NPCM8XX_CLK_S_PLL0 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCON1 ,NPCM8XX_CLK_S_PLL1 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCON2 ,NPCM8XX_CLK_S_PLL2 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCONG ,NPCM8XX_CLK_S_PLL_GFX ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, +}; + +static const struct npcm8xx_clk_mux_data npcm8xx_muxes[] = { + {0 ,GENMASK (1 ,0 ), cpuck_mux_table,NPCM8XX_CLK_S_CPU_MUX , + cpuck_mux_parents,ARRAY_SIZE (cpuck_mux_parents),CLK_IS_CRITICAL , +NPCM8XX_CLK_CPU }, + + {4 ,GENMASK (1 ,0 ), pixcksel_mux_table,NPCM8XX_CLK_S_PIX_MUX , + pixcksel_mux_parents,ARRAY_SIZE (pixcksel_mux_parents),0 , +NPCM8XX_CLK_GFX_PIXEL }, + + {6 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_SD_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {8 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_UART_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {10 ,GENMASK (1 ,0 ), sucksel_mux_table,NPCM8XX_CLK_S_SU_MUX , + sucksel_mux_parents,ARRAY_SIZE (sucksel_mux_parents),0 ,-1 }, + + {12 ,GENMASK (1 ,0 ), mccksel_mux_table,NPCM8XX_CLK_S_MC_MUX , + mccksel_mux_parents,ARRAY_SIZE (mccksel_mux_parents),0 ,-1 }, + + {14 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_ADC_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {16 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_GFX_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {18 ,GENMASK (2 ,0 ), clkoutsel_mux_table,NPCM8XX_CLK_S_CLKOUT_MUX , + clkoutsel_mux_parents,ARRAY_SIZE (clkoutsel_mux_parents),0 ,-1 }, + + {21 ,GENMASK (1 ,0 ), gfxmsel_mux_table,NPCM8XX_CLK_S_GFXM_MUX , + gfxmsel_mux_parents,ARRAY_SIZE (gfxmsel_mux_parents),0 ,-1 }, + + {23 ,GENMASK (1 ,0 ), dvcssel_mux_table,NPCM8XX_CLK_S_DVC_MUX , + dvcssel_mux_parents,ARRAY_SIZE (dvcssel_mux_parents),0 ,-1 }, + + {25 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_RG_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {27 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_RCP_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, +}; + +/* configurable dividers: */ +static const struct npcm8xx_clk_div_data npcm8xx_divs[] = { + {NPCM8XX_CLKDIV1 ,28 ,3 ,NPCM8XX_CLK_S_ADC ,NPCM8XX_CLK_S_PRE_ADC , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_ADC }, +/* bit 30-28 ADCCKDIV*/ + {NPCM8XX_CLKDIV1 ,26 ,2 ,NPCM8XX_CLK_S_AHB ,NPCM8XX_CLK_S_PRE_CLK , +CLK_DIVIDER_READ_ONLY ,CLK_IS_CRITICAL ,NPCM8XX_CLK_AHB }, +/* bit 28-26 CLK4DIV*/ + {NPCM8XX_CLKDIV1 ,21 ,5 ,NPCM8XX_CLK_S_PRE_ADC , +NPCM8XX_CLK_S_ADC_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_PRE_ADC }, +/* bit 25-21 PRE-ADCCKDIV*/ + {NPCM8XX_CLKDIV1 ,16 ,5 ,NPCM8XX_CLK_S_UART , +NPCM8XX_CLK_S_UART_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_UART }, +/* bit 20-16 UARTDIV*/ + {NPCM8XX_CLKDIV1 ,11 ,5 ,NPCM8XX_CLK_S_MMC , +NPCM8XX_CLK_S_SD_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_MMC }, +/* bit 15-11 MMCCKDIV*/ + {NPCM8XX_CLKDIV1 ,6 ,5 ,NPCM8XX_CLK_S_SPI3 , +NPCM8XX_CLK_S_AHB ,0 ,0 ,NPCM8XX_CLK_SPI3 }, +/* bit 10-6 AHB3CKDIV*/ + {NPCM8XX_CLKDIV1 ,2 ,4 ,NPCM8XX_CLK_S_PCI , +NPCM8XX_CLK_S_GFX_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_PCI }, +/* bit 5-2 PCICKDIV*/ + + {NPCM8XX_CLKDIV2 ,30 ,2 ,NPCM8XX_CLK_S_APB4 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB4 }, +/* bit 31-30 APB4CKDIV*/ + {NPCM8XX_CLKDIV2 ,28 ,2 ,NPCM8XX_CLK_S_APB3 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB3 }, +/* bit 29-28 APB3CKDIV*/ + {NPCM8XX_CLKDIV2 ,26 ,2 ,NPCM8XX_CLK_S_APB2 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB2 }, +/* bit 28-26 APB2CKDIV*/ + {NPCM8XX_CLKDIV2 ,24 ,2 ,NPCM8XX_CLK_S_APB1 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB1 }, +/* bit 25-24 APB1CKDIV*/ + {NPCM8XX_CLKDIV2 ,22 ,2 ,NPCM8XX_CLK_S_APB5 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB5 }, +/* bit 23-22 APB5CKDIV*/ + {NPCM8XX_CLKDIV2 ,16 ,5 ,NPCM8XX_CLK_S_CLKOUT ,NPCM8XX_CLK_S_CLKOUT_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_CLKOUT }, +/* bit 20-16 CLKOUTDIV*/ + {NPCM8XX_CLKDIV2 ,13 ,3 ,NPCM8XX_CLK_S_GFX ,NPCM8XX_CLK_S_GFX_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_GFX }, +/* bit 15-13 GFXCKDIV*/ + {NPCM8XX_CLKDIV2 ,8 ,5 ,NPCM8XX_CLK_S_USB_BRIDGE ,NPCM8XX_CLK_S_SU_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SU }, +/* bit 12-8 SUCKDIV*/ + {NPCM8XX_CLKDIV2 ,4 ,4 ,NPCM8XX_CLK_S_USB_HOST ,NPCM8XX_CLK_S_SU_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SU48 }, +/* bit 8-4 SU48CKDIV*/ + {NPCM8XX_CLKDIV2 ,0 ,4 ,NPCM8XX_CLK_S_SDHC , +NPCM8XX_CLK_S_SD_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SDHC } + ,/* bit 3-0 SD1CKDIV*/ + + {NPCM8XX_CLKDIV3 ,16 ,8 ,NPCM8XX_CLK_S_SPI1 ,
Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 416
Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+ NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI1}, +/* bit 23-16 SPI1CKDV*/ + {NPCM8XX_CLKDIV3, 11, 5, NPCM8XX_CLK_S_UART2, + NPCM8XX_CLK_S_UART_MUX, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_UART2}, +/* bit 15-11 UARTDIV2*/ + {NPCM8XX_CLKDIV3, 6, 5, NPCM8XX_CLK_S_SPI0,Please run checkpatch, add a space after '{' and before '}'
I did run checkpatch, it weird I didn't got a warning about it. will be fixed.
Quoted 40 lines
Quoted 27 lines
drivers/clk/clk-npcm8xx.c new file +26
-0+ NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI0}, +/* bit 10-6 SPI0CKDV*/ + {NPCM8XX_CLKDIV3, 1, 5, NPCM8XX_CLK_S_SPIX, + NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPIX}, +/* bit 5-1 SPIXCKDV*/ + + {NPCM8XX_CLKDIV4, 28, 4, NPCM8XX_CLK_S_RG, NPCM8XX_CLK_S_RG_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RG}, +/* bit 31-28 RGREFDIV*/ + {NPCM8XX_CLKDIV4, 12, 4, NPCM8XX_CLK_S_RCP, NPCM8XX_CLK_S_RCP_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RCP}, +/* bit 15-12 RCPREFDIV*/ + {NPCM8XX_THRTL_CNT, 0, 2, NPCM8XX_CLK_S_TH, NPCM8XX_CLK_S_CPU_MUX, + CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_TH}, + /* bit 1-0 TH_DIV*/ +}; + +static DEFINE_SPINLOCK (npcm8xx_clk_lock); + +static int npcm8xx_clk_probe (struct platform_device *pdev) +{ +struct clk_hw_onecell_data *npcm8xx_clk_data; +struct device *dev = &pdev->dev; +struct device_node *np = dev->of_node; +void __iomem *clk_base; +struct resource res;drivers/clk/clk-npcm8xx.c line 452
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+struct clk_hw *hw; +int i, err; + + npcm8xx_clk_data =devm_kzalloc (dev,struct_size (npcm8xx_clk_data, hws, +NPCM8XX_NUM_CLOCKS ), +GFP_KERNEL ); +if (!npcm8xx_clk_data) +return -ENOMEM ; + + err =of_address_to_resource (np,0 , &res);Why can't we use
platform_get_resource()?
Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 458
Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+if (err) { +dev_err (dev,"Failed to get resource, ret %d\n" , err); +return err; + } + + clk_base =ioremap (res.start,resource_size (&res));And use
devm_platform_ioremap_resource()?
Clock and reset driver use the same memory register map 0xF0801000 - 0xF0801FFF. For historical reasons the registers of both modules are mixed in the memory range 0xF0801000 - 0xF0801FFF this is why we can't have a separate region for each module.
In case I will use devm_platform_ioremap_resource function the reset ioremap will fail so the driver using the method above.
Quoted 15 lines
drivers/clk/clk-npcm8xx.c line 469
Quoted 12 lines
drivers/clk/clk-npcm8xx.c new file +11
-0+if (!clk_base) { +dev_err (&pdev->dev,"Failed to remap I/O memory\n" ); +return -ENOMEM ; + } + + npcm8xx_clk_data->num =NPCM8XX_NUM_CLOCKS ; + +for (i =0 ; i <NPCM8XX_NUM_CLOCKS ; i++) + npcm8xx_clk_data->hws[i] =ERR_PTR (-EPROBE_DEFER ); + +/* Reference 25MHz clock */ Does this exist on the board? If so, I'd make a fixed rate clk in the dts and have 'refclk' be an input in the binding for this clk controller.
No, it is an internal clock in the SoC, this is why it is in the driver.
Quoted 103 lines
Quoted 103 lines
drivers/clk/clk-npcm8xx.c new file +102
-0+ hw =clk_hw_register_fixed_rate (dev,"refclk" ,NULL ,0 ,NPCM8XX_REF_CLK ); +if (IS_ERR (hw)) +return PTR_ERR (hw); + npcm8xx_clk_data->hws[NPCM8XX_CLK_REFCLK ] = hw; + +/* Register plls */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_plls); i++) { +const struct npcm8xx_clk_pll_data *pll_data = &npcm8xx_plls[i]; + + hw =npcm8xx_clk_register_pll (dev, clk_base + pll_data->reg, + pll_data->name, + pll_data->parent_name, + pll_data->flags); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register pll\n" ); + goto unregister_refclk; + } + +if (pll_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[pll_data->onecell_idx] = hw; + } + +/* Register fixed dividers */ + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PLL1_DIV2 , +NPCM8XX_CLK_S_PLL1 ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register fixed div\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PLL2_DIV2 , +NPCM8XX_CLK_S_PLL2 ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register pll div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PRE_CLK , +NPCM8XX_CLK_S_CPU_MUX ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register ckclk div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_AXI , +NPCM8XX_CLK_S_TH ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register axi div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_ATB , +NPCM8XX_CLK_S_AXI ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register atb div2\n" ); + goto unregister_refclk; + } + +/* Register clock dividers specified in npcm8xx_divs */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_divs); i++) { +const struct npcm8xx_clk_div_data *div_data = &npcm8xx_divs[i]; + + hw =devm_clk_hw_register_divider (dev, div_data->name, + div_data->parent_name, + div_data->flags, + clk_base + div_data->reg, + div_data->shift, + div_data->width, + div_data->clk_divider_flags, + &npcm8xx_clk_lock); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register div table\n" ); + goto unregister_refclk; + } + +if (div_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[div_data->onecell_idx] = hw; + } + +/* Register muxes */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_muxes); i++) { +const struct npcm8xx_clk_mux_data *mux_data = &npcm8xx_muxes[i]; + + hw =clk_hw_register_mux_table (dev, mux_data->name, + mux_data->parent_names, + mux_data->num_parents, + mux_data->flags, + clk_base +NPCM8XX_CLKSEL , + mux_data->shift, + mux_data->mask,0 , + mux_data->table, + &npcm8xx_clk_lock); + +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register mux\n" ); + goto err_mux_clk; + } + +if (mux_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[mux_data->onecell_idx] = hw; + } +
Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 576
Quoted 5 lines
drivers/clk/clk-npcm8xx.c new file +5
-0+ err =devm_of_clk_add_hw_provider (dev, of_clk_hw_onecell_get, + npcm8xx_clk_data); +if (err) { +dev_err (dev,"unable to add clk provider\n" ); + goto unregister_refclk;The 'hw' pointer doesn't contain 'err' here so probe will return some positive number when this fails?
Indeed, will be fixed.
Quoted 29 lines
Quoted 29 lines
drivers/clk/clk-npcm8xx.c new file +28
-0+ } + +return err; + +err_mux_clk: +while (i--) { +if (npcm8xx_muxes[i].onecell_idx >=0 ) +clk_hw_unregister_mux (npcm8xx_clk_data->hws[npcm8xx_muxes[i].onecell_idx]); + } +unregister_refclk: +clk_hw_unregister (npcm8xx_clk_data->hws[NPCM8XX_CLK_REFCLK ]); +return PTR_ERR (hw); +} + +static const struct of_device_id npcm8xx_clk_dt_ids[] = { + { .compatible ="nuvoton,npcm845-clk" , }, + { } +}; +MODULE_DEVICE_TABLE (of, npcm8xx_clk_dt_ids); + +static struct platform_driver npcm8xx_clk_driver = { + .probe = npcm8xx_clk_probe, + .driver = { + .name ="npcm8xx_clk" , + .of_match_table = npcm8xx_clk_dt_ids, + }, +}; +
Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 610
Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+static int __init npcm8xx_clk_driver_init (void ) +{ +return platform_driver_register (&npcm8xx_clk_driver); +} +arch_initcall (npcm8xx_clk_driver_init); +If it can be a module it needs to unregister the driver on module exit.
Will remove the MODULE_DEVICE_TABLE, the driver clock should be a built-in kernel driver.
Thanks,
Tomer
Tomer Maimon
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Hi Stephen,
Appreciate if you can address my answers to your comments
Quoted 694 lines · Tomer Maimon
Hi Stephen,
Thanks for your comments
Quoted 21 lines · Stephen Boyd
Quoting Tomer Maimon (2022-07-11 05:35:07)
Quoted 19 lines
Nuvoton Arbel BMC NPCM8XX contains an integrated clock controller which generates and supplies clocks to all modules within the BMC.
Signed-off-by Tomer Maimon <[email protected]>--- drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/clk-npcm8xx.c | 610 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 617 insertions(+) create mode 100644 drivers/clk/clk-npcm8xx.cdrivers/clk/Kconfig +0
-0@@ -428,6 +428,12 @@ config COMMON_CLK_K210 help Support for the Canaan Kendryte K210 RISC-V SoC clocks.Quoted 8 lines
drivers/clk/Kconfig config COMMON_CLK_K210 · line 435
Quoted 5 lines
drivers/clk/Kconfig +5
-0+config COMMON_CLK_NPCM8XX + tristate "Clock driver for the NPCM8XX SoC Family" + depends on ARCH_NPCM || COMPILE_TEST + help + This driver supports the clocks on the Nuvoton BMC NPCM8XX SoC Family.Please put this next to
COMMON_CLK_NXPso that it is sort of sorted based onKconfigsymbol.Will do
Quoted 177 lines
Quoted 177 lines
drivers/clk/Kconfig +1
-0+ source "drivers/clk/actions/Kconfig" source "drivers/clk/analogbits/Kconfig" source "drivers/clk/baikal-t1/Kconfig"drivers/clk/clk-npcm8xx.c new file +167
-0@@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Nuvoton NPCM8xx Clock Generator +* All the clocks are initialized by the bootloader, so this driver allow only +* reading of current settings directly from the hardware. +* +* Copyright (C) 2020 Nuvoton Technologies +* Author: Tomer Maimon <[email protected]> +*/ + +#include <linux/bitfield.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/nuvoton,npcm845-clk.h> + +#define NPCM8XX_REF_CLK 25000000 + +struct npcm8xx_clk_pll { +struct clk_hw hw; +void __iomem *pllcon; +u8 flags; +}; + +#define to_npcm8xx_clk_pll (_hw) container_of(_hw, struct npcm8xx_clk_pll, hw) + +#define PLLCON_LOKI BIT(31) +#define PLLCON_LOKS BIT(30) +#define PLLCON_FBDV GENMASK(27, 16) +#define PLLCON_OTDV2 GENMASK(15, 13) +#define PLLCON_PWDEN BIT(12) +#define PLLCON_OTDV1 GENMASK(10, 8) +#define PLLCON_INDV GENMASK(5, 0) + +static unsigned long npcm8xx_clk_pll_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct npcm8xx_clk_pll *pll =to_npcm8xx_clk_pll (hw); +unsigned long fbdv, indv, otdv1, otdv2; +unsigned int val; +u64 ret; + +if (parent_rate ==0 ) { +pr_debug ("%s: parent rate is zero\n" , __func__); +return 0 ; + } + + val =readl_relaxed (pll->pllcon); + + indv =FIELD_GET (PLLCON_INDV , val); + fbdv =FIELD_GET (PLLCON_FBDV , val); + otdv1 =FIELD_GET (PLLCON_OTDV1 , val); + otdv2 =FIELD_GET (PLLCON_OTDV2 , val); + + ret = (u64 )parent_rate * fbdv; +do_div (ret, indv * otdv1 * otdv2); + +return ret; +} + +static const struct clk_ops npcm8xx_clk_pll_ops = { + .recalc_rate = npcm8xx_clk_pll_recalc_rate, +}; + +static struct clk_hw * +npcm8xx_clk_register_pll (struct device *dev,void __iomem *pllcon, +const char *name,const char *parent_name, +unsigned long flags) +{ +struct npcm8xx_clk_pll *pll; +struct clk_init_data init; +struct clk_hw *hw; +int ret; + + pll =devm_kzalloc (dev,sizeof (*pll),GFP_KERNEL ); +if (!pll) +return ERR_PTR (-ENOMEM ); + +pr_debug ("%s reg, name=%s, p=%s\n" , __func__, name, parent_name); + + init.name = name; + init.ops = &npcm8xx_clk_pll_ops; + init.parent_names = &parent_name; + init.num_parents =1 ; + init.flags = flags; + + pll->pllcon = pllcon; + pll->hw.init = &init; + + hw = &pll->hw; + + ret =devm_clk_hw_register (dev, hw); +if (ret) +return ERR_PTR (ret); + +return hw; +} + +#define NPCM8XX_CLKEN1 (0x00) +#define NPCM8XX_CLKEN2 (0x28) +#define NPCM8XX_CLKEN3 (0x30) +#define NPCM8XX_CLKEN4 (0x70) +#define NPCM8XX_CLKSEL (0x04) +#define NPCM8XX_CLKDIV1 (0x08) +#define NPCM8XX_CLKDIV2 (0x2C) +#define NPCM8XX_CLKDIV3 (0x58) +#define NPCM8XX_CLKDIV4 (0x7C) +#define NPCM8XX_PLLCON0 (0x0C) +#define NPCM8XX_PLLCON1 (0x10) +#define NPCM8XX_PLLCON2 (0x54) +#define NPCM8XX_SWRSTR (0x14) +#define NPCM8XX_IRQWAKECON (0x18) +#define NPCM8XX_IRQWAKEFLAG (0x1C) +#define NPCM8XX_IPSRST1 (0x20) +#define NPCM8XX_IPSRST2 (0x24) +#define NPCM8XX_IPSRST3 (0x34) +#define NPCM8XX_WD0RCR (0x38) +#define NPCM8XX_WD1RCR (0x3C) +#define NPCM8XX_WD2RCR (0x40) +#define NPCM8XX_SWRSTC1 (0x44) +#define NPCM8XX_SWRSTC2 (0x48) +#define NPCM8XX_SWRSTC3 (0x4C) +#define NPCM8XX_SWRSTC4 (0x50) +#define NPCM8XX_CORSTC (0x5C) +#define NPCM8XX_PLLCONG (0x60) +#define NPCM8XX_AHBCKFI (0x64) +#define NPCM8XX_SECCNT (0x68) +#define NPCM8XX_CNTR25M (0x6C) +#define NPCM8XX_THRTL_CNT (0xC0) + +struct npcm8xx_clk_mux_data { +u8 shift; +u8 mask; +u32 *table; +const char *name; +const char *const *parent_names; +u8 num_parents; +unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +}; + +struct npcm8xx_clk_div_data { +u32 reg; +u8 shift; +u8 width; +const char *name; +const char *parent_name; +u8 clk_divider_flags; +unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +};Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 172
Quoted 5 lines
drivers/clk/clk-npcm8xx.c new file +5
-0+ +struct npcm8xx_clk_pll_data { +u32 reg; +const char *name; +const char *parent_name;Any reason why we're not using
clk_parent_dataor directclk_hwpointers?For more historical reasons, I did the same method as done in the NPCM7XX driver.
Theclk_init_datastruct can use * const*parent_names,
https://elixir.bootlin.com/linux/v5.19-rc6/source/include/linux/clk-provider.h#L289
Is it problematic?Quoted 239 lines
Quoted 239 lines
drivers/clk/clk-npcm8xx.c new file +238
-0+unsigned long flags; +/* +* If this clock is exported via DT, set onecell_idx to constant +* defined in include/dt-bindings/clock/nuvoton, NPCM8XX-clock.h for +* this specific clock. Otherwise, set to -1. +*/ +int onecell_idx; +}; + +/* +* Single copy of strings used to refer to clocks within this driver indexed by +* above enum. +*/ +#define NPCM8XX_CLK_S_REFCLK "refclk" +#define NPCM8XX_CLK_S_SYSBYPCK "sysbypck" +#define NPCM8XX_CLK_S_MCBYPCK "mcbypck" +#define NPCM8XX_CLK_S_PLL0 "pll0" +#define NPCM8XX_CLK_S_PLL1 "pll1" +#define NPCM8XX_CLK_S_PLL1_DIV2 "pll1_div2" +#define NPCM8XX_CLK_S_PLL2 "pll2" +#define NPCM8XX_CLK_S_PLL_GFX "pll_gfx" +#define NPCM8XX_CLK_S_PLL2_DIV2 "pll2_div2" +#define NPCM8XX_CLK_S_PIX_MUX "gfx_pixel" +#define NPCM8XX_CLK_S_MC_MUX "mc_phy" +#define NPCM8XX_CLK_S_CPU_MUX "cpu"/* AKA system clock */ +#define NPCM8XX_CLK_S_MC "mc" +#define NPCM8XX_CLK_S_AXI "axi"/* AKA CLK2 */ +#define NPCM8XX_CLK_S_AHB "ahb"/* AKA CLK4 */ +#define NPCM8XX_CLK_S_CLKOUT_MUX "clkout_mux" +#define NPCM8XX_CLK_S_UART_MUX "uart_mux" +#define NPCM8XX_CLK_S_SD_MUX "sd_mux" +#define NPCM8XX_CLK_S_GFXM_MUX "gfxm_mux" +#define NPCM8XX_CLK_S_SU_MUX "serial_usb_mux" +#define NPCM8XX_CLK_S_DVC_MUX "dvc_mux" +#define NPCM8XX_CLK_S_GFX_MUX "gfx_mux" +#define NPCM8XX_CLK_S_ADC_MUX "adc_mux" +#define NPCM8XX_CLK_S_SPI0 "spi0" +#define NPCM8XX_CLK_S_SPI1 "spi1" +#define NPCM8XX_CLK_S_SPI3 "spi3" +#define NPCM8XX_CLK_S_SPIX "spix" +#define NPCM8XX_CLK_S_APB1 "apb1" +#define NPCM8XX_CLK_S_APB2 "apb2" +#define NPCM8XX_CLK_S_APB3 "apb3" +#define NPCM8XX_CLK_S_APB4 "apb4" +#define NPCM8XX_CLK_S_APB5 "apb5" +#define NPCM8XX_CLK_S_APB19 "apb19" +#define NPCM8XX_CLK_S_TOCK "tock" +#define NPCM8XX_CLK_S_CLKOUT "clkout" +#define NPCM8XX_CLK_S_PRE_ADC "pre adc" +#define NPCM8XX_CLK_S_UART "uart" +#define NPCM8XX_CLK_S_UART2 "uart2" +#define NPCM8XX_CLK_S_TIMER "timer" +#define NPCM8XX_CLK_S_MMC "mmc" +#define NPCM8XX_CLK_S_SDHC "sdhc" +#define NPCM8XX_CLK_S_ADC "adc" +#define NPCM8XX_CLK_S_GFX "gfx0_gfx1_mem" +#define NPCM8XX_CLK_S_USBIF "serial_usbif" +#define NPCM8XX_CLK_S_USB_HOST "usb_host" +#define NPCM8XX_CLK_S_USB_BRIDGE "usb_bridge" +#define NPCM8XX_CLK_S_PCI "pci" +#define NPCM8XX_CLK_S_TH "th" +#define NPCM8XX_CLK_S_ATB "atb" +#define NPCM8XX_CLK_S_PRE_CLK "pre_clk" + +#define NPCM8XX_CLK_S_RG_MUX "rg_mux" +#define NPCM8XX_CLK_S_RCP_MUX "rcp_mux" +#define NPCM8XX_CLK_S_RG "rg" +#define NPCM8XX_CLK_S_RCP "rcp" + +static u32 pll_mux_table[] = {0 ,1 ,2 ,3 }; +static const char *const pll_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 cpuck_mux_table[] = {0 ,1 ,2 ,3 ,7 }; +static const char *const cpuck_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_SYSBYPCK , +NPCM8XX_CLK_S_PLL2 , +}; + +static u32 pixcksel_mux_table[] = {0 ,2 }; +static const char *const pixcksel_mux_parents[] = { +NPCM8XX_CLK_S_PLL_GFX , +NPCM8XX_CLK_S_REFCLK , +}; + +static u32 sucksel_mux_table[] = {2 ,3 }; +static const char *const sucksel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 mccksel_mux_table[] = {0 ,2 ,3 }; +static const char *const mccksel_mux_parents[] = { +NPCM8XX_CLK_S_PLL1_DIV2 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_MCBYPCK , +}; + +static u32 clkoutsel_mux_table[] = {0 ,1 ,2 ,3 ,4 }; +static const char *const clkoutsel_mux_parents[] = { +NPCM8XX_CLK_S_PLL0 , +NPCM8XX_CLK_S_PLL1 , +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL_GFX ,// divided by 2 +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 gfxmsel_mux_table[] = {2 ,3 }; +static const char *const gfxmsel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2_DIV2 , +}; + +static u32 dvcssel_mux_table[] = {2 ,3 }; +static const char *const dvcssel_mux_parents[] = { +NPCM8XX_CLK_S_REFCLK , +NPCM8XX_CLK_S_PLL2 , +}; + +static const struct npcm8xx_clk_pll_data npcm8xx_plls[] = { + {NPCM8XX_PLLCON0 ,NPCM8XX_CLK_S_PLL0 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCON1 ,NPCM8XX_CLK_S_PLL1 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCON2 ,NPCM8XX_CLK_S_PLL2 ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, + {NPCM8XX_PLLCONG ,NPCM8XX_CLK_S_PLL_GFX ,NPCM8XX_CLK_S_REFCLK ,0 ,-1 }, +}; + +static const struct npcm8xx_clk_mux_data npcm8xx_muxes[] = { + {0 ,GENMASK (1 ,0 ), cpuck_mux_table,NPCM8XX_CLK_S_CPU_MUX , + cpuck_mux_parents,ARRAY_SIZE (cpuck_mux_parents),CLK_IS_CRITICAL , +NPCM8XX_CLK_CPU }, + + {4 ,GENMASK (1 ,0 ), pixcksel_mux_table,NPCM8XX_CLK_S_PIX_MUX , + pixcksel_mux_parents,ARRAY_SIZE (pixcksel_mux_parents),0 , +NPCM8XX_CLK_GFX_PIXEL }, + + {6 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_SD_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {8 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_UART_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {10 ,GENMASK (1 ,0 ), sucksel_mux_table,NPCM8XX_CLK_S_SU_MUX , + sucksel_mux_parents,ARRAY_SIZE (sucksel_mux_parents),0 ,-1 }, + + {12 ,GENMASK (1 ,0 ), mccksel_mux_table,NPCM8XX_CLK_S_MC_MUX , + mccksel_mux_parents,ARRAY_SIZE (mccksel_mux_parents),0 ,-1 }, + + {14 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_ADC_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {16 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_GFX_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {18 ,GENMASK (2 ,0 ), clkoutsel_mux_table,NPCM8XX_CLK_S_CLKOUT_MUX , + clkoutsel_mux_parents,ARRAY_SIZE (clkoutsel_mux_parents),0 ,-1 }, + + {21 ,GENMASK (1 ,0 ), gfxmsel_mux_table,NPCM8XX_CLK_S_GFXM_MUX , + gfxmsel_mux_parents,ARRAY_SIZE (gfxmsel_mux_parents),0 ,-1 }, + + {23 ,GENMASK (1 ,0 ), dvcssel_mux_table,NPCM8XX_CLK_S_DVC_MUX , + dvcssel_mux_parents,ARRAY_SIZE (dvcssel_mux_parents),0 ,-1 }, + + {25 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_RG_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, + + {27 ,GENMASK (1 ,0 ), pll_mux_table,NPCM8XX_CLK_S_RCP_MUX , + pll_mux_parents,ARRAY_SIZE (pll_mux_parents),0 ,-1 }, +}; + +/* configurable dividers: */ +static const struct npcm8xx_clk_div_data npcm8xx_divs[] = { + {NPCM8XX_CLKDIV1 ,28 ,3 ,NPCM8XX_CLK_S_ADC ,NPCM8XX_CLK_S_PRE_ADC , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_ADC }, +/* bit 30-28 ADCCKDIV*/ + {NPCM8XX_CLKDIV1 ,26 ,2 ,NPCM8XX_CLK_S_AHB ,NPCM8XX_CLK_S_PRE_CLK , +CLK_DIVIDER_READ_ONLY ,CLK_IS_CRITICAL ,NPCM8XX_CLK_AHB }, +/* bit 28-26 CLK4DIV*/ + {NPCM8XX_CLKDIV1 ,21 ,5 ,NPCM8XX_CLK_S_PRE_ADC , +NPCM8XX_CLK_S_ADC_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_PRE_ADC }, +/* bit 25-21 PRE-ADCCKDIV*/ + {NPCM8XX_CLKDIV1 ,16 ,5 ,NPCM8XX_CLK_S_UART , +NPCM8XX_CLK_S_UART_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_UART }, +/* bit 20-16 UARTDIV*/ + {NPCM8XX_CLKDIV1 ,11 ,5 ,NPCM8XX_CLK_S_MMC , +NPCM8XX_CLK_S_SD_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_MMC }, +/* bit 15-11 MMCCKDIV*/ + {NPCM8XX_CLKDIV1 ,6 ,5 ,NPCM8XX_CLK_S_SPI3 , +NPCM8XX_CLK_S_AHB ,0 ,0 ,NPCM8XX_CLK_SPI3 }, +/* bit 10-6 AHB3CKDIV*/ + {NPCM8XX_CLKDIV1 ,2 ,4 ,NPCM8XX_CLK_S_PCI , +NPCM8XX_CLK_S_GFX_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_PCI }, +/* bit 5-2 PCICKDIV*/ + + {NPCM8XX_CLKDIV2 ,30 ,2 ,NPCM8XX_CLK_S_APB4 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB4 }, +/* bit 31-30 APB4CKDIV*/ + {NPCM8XX_CLKDIV2 ,28 ,2 ,NPCM8XX_CLK_S_APB3 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB3 }, +/* bit 29-28 APB3CKDIV*/ + {NPCM8XX_CLKDIV2 ,26 ,2 ,NPCM8XX_CLK_S_APB2 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB2 }, +/* bit 28-26 APB2CKDIV*/ + {NPCM8XX_CLKDIV2 ,24 ,2 ,NPCM8XX_CLK_S_APB1 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB1 }, +/* bit 25-24 APB1CKDIV*/ + {NPCM8XX_CLKDIV2 ,22 ,2 ,NPCM8XX_CLK_S_APB5 ,NPCM8XX_CLK_S_AHB , +CLK_DIVIDER_READ_ONLY |CLK_DIVIDER_POWER_OF_TWO ,0 , +NPCM8XX_CLK_APB5 }, +/* bit 23-22 APB5CKDIV*/ + {NPCM8XX_CLKDIV2 ,16 ,5 ,NPCM8XX_CLK_S_CLKOUT ,NPCM8XX_CLK_S_CLKOUT_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_CLKOUT }, +/* bit 20-16 CLKOUTDIV*/ + {NPCM8XX_CLKDIV2 ,13 ,3 ,NPCM8XX_CLK_S_GFX ,NPCM8XX_CLK_S_GFX_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_GFX }, +/* bit 15-13 GFXCKDIV*/ + {NPCM8XX_CLKDIV2 ,8 ,5 ,NPCM8XX_CLK_S_USB_BRIDGE ,NPCM8XX_CLK_S_SU_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SU }, +/* bit 12-8 SUCKDIV*/ + {NPCM8XX_CLKDIV2 ,4 ,4 ,NPCM8XX_CLK_S_USB_HOST ,NPCM8XX_CLK_S_SU_MUX , +CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SU48 }, +/* bit 8-4 SU48CKDIV*/ + {NPCM8XX_CLKDIV2 ,0 ,4 ,NPCM8XX_CLK_S_SDHC , +NPCM8XX_CLK_S_SD_MUX ,CLK_DIVIDER_READ_ONLY ,0 ,NPCM8XX_CLK_SDHC } + ,/* bit 3-0 SD1CKDIV*/ + + {NPCM8XX_CLKDIV3 ,16 ,8 ,NPCM8XX_CLK_S_SPI1 ,Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 416
Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+ NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI1}, +/* bit 23-16 SPI1CKDV*/ + {NPCM8XX_CLKDIV3, 11, 5, NPCM8XX_CLK_S_UART2, + NPCM8XX_CLK_S_UART_MUX, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_UART2}, +/* bit 15-11 UARTDIV2*/ + {NPCM8XX_CLKDIV3, 6, 5, NPCM8XX_CLK_S_SPI0,Please run checkpatch, add a space after '{' and before '}'
I did run checkpatch, it weird I didn't got a warning about it. will be fixed.
Quoted 40 lines
Quoted 27 lines
drivers/clk/clk-npcm8xx.c new file +26
-0+ NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI0}, +/* bit 10-6 SPI0CKDV*/ + {NPCM8XX_CLKDIV3, 1, 5, NPCM8XX_CLK_S_SPIX, + NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPIX}, +/* bit 5-1 SPIXCKDV*/ + + {NPCM8XX_CLKDIV4, 28, 4, NPCM8XX_CLK_S_RG, NPCM8XX_CLK_S_RG_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RG}, +/* bit 31-28 RGREFDIV*/ + {NPCM8XX_CLKDIV4, 12, 4, NPCM8XX_CLK_S_RCP, NPCM8XX_CLK_S_RCP_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RCP}, +/* bit 15-12 RCPREFDIV*/ + {NPCM8XX_THRTL_CNT, 0, 2, NPCM8XX_CLK_S_TH, NPCM8XX_CLK_S_CPU_MUX, + CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_TH}, + /* bit 1-0 TH_DIV*/ +}; + +static DEFINE_SPINLOCK (npcm8xx_clk_lock); + +static int npcm8xx_clk_probe (struct platform_device *pdev) +{ +struct clk_hw_onecell_data *npcm8xx_clk_data; +struct device *dev = &pdev->dev; +struct device_node *np = dev->of_node; +void __iomem *clk_base; +struct resource res;drivers/clk/clk-npcm8xx.c line 452
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+struct clk_hw *hw; +int i, err; + + npcm8xx_clk_data =devm_kzalloc (dev,struct_size (npcm8xx_clk_data, hws, +NPCM8XX_NUM_CLOCKS ), +GFP_KERNEL ); +if (!npcm8xx_clk_data) +return -ENOMEM ; + + err =of_address_to_resource (np,0 , &res);Why can't we use
platform_get_resource()?Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 458
Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+if (err) { +dev_err (dev,"Failed to get resource, ret %d\n" , err); +return err; + } + + clk_base =ioremap (res.start,resource_size (&res));And use
devm_platform_ioremap_resource()?Clock and reset driver use the same memory register map
0xF0801000-0xF0801FFF. For historical reasons the registers of both modules are mixed in the memory range0xF0801000-0xF0801FFFthis is why we can't have a separate region for each module.
In case I will usedevm_platform_ioremap_resourcefunction the reset ioremap will fail so the driver using the method above.Quoted 15 lines
drivers/clk/clk-npcm8xx.c line 469
Quoted 12 lines
drivers/clk/clk-npcm8xx.c new file +11
-0+if (!clk_base) { +dev_err (&pdev->dev,"Failed to remap I/O memory\n" ); +return -ENOMEM ; + } + + npcm8xx_clk_data->num =NPCM8XX_NUM_CLOCKS ; + +for (i =0 ; i <NPCM8XX_NUM_CLOCKS ; i++) + npcm8xx_clk_data->hws[i] =ERR_PTR (-EPROBE_DEFER ); + +/* Reference 25MHz clock */ Does this exist on the board? If so, I'd make a fixed rate clk in the dts and have 'refclk' be an input in the binding for this clk controller.
No, it is an internal clock in the SoC, this is why it is in the driver.
Quoted 103 lines
Quoted 103 lines
drivers/clk/clk-npcm8xx.c new file +102
-0+ hw =clk_hw_register_fixed_rate (dev,"refclk" ,NULL ,0 ,NPCM8XX_REF_CLK ); +if (IS_ERR (hw)) +return PTR_ERR (hw); + npcm8xx_clk_data->hws[NPCM8XX_CLK_REFCLK ] = hw; + +/* Register plls */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_plls); i++) { +const struct npcm8xx_clk_pll_data *pll_data = &npcm8xx_plls[i]; + + hw =npcm8xx_clk_register_pll (dev, clk_base + pll_data->reg, + pll_data->name, + pll_data->parent_name, + pll_data->flags); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register pll\n" ); + goto unregister_refclk; + } + +if (pll_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[pll_data->onecell_idx] = hw; + } + +/* Register fixed dividers */ + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PLL1_DIV2 , +NPCM8XX_CLK_S_PLL1 ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register fixed div\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PLL2_DIV2 , +NPCM8XX_CLK_S_PLL2 ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register pll div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_PRE_CLK , +NPCM8XX_CLK_S_CPU_MUX ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register ckclk div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_AXI , +NPCM8XX_CLK_S_TH ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register axi div2\n" ); + goto unregister_refclk; + } + + hw =devm_clk_hw_register_fixed_factor (dev,NPCM8XX_CLK_S_ATB , +NPCM8XX_CLK_S_AXI ,0 ,1 ,2 ); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register atb div2\n" ); + goto unregister_refclk; + } + +/* Register clock dividers specified in npcm8xx_divs */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_divs); i++) { +const struct npcm8xx_clk_div_data *div_data = &npcm8xx_divs[i]; + + hw =devm_clk_hw_register_divider (dev, div_data->name, + div_data->parent_name, + div_data->flags, + clk_base + div_data->reg, + div_data->shift, + div_data->width, + div_data->clk_divider_flags, + &npcm8xx_clk_lock); +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register div table\n" ); + goto unregister_refclk; + } + +if (div_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[div_data->onecell_idx] = hw; + } + +/* Register muxes */ +for (i =0 ; i <ARRAY_SIZE (npcm8xx_muxes); i++) { +const struct npcm8xx_clk_mux_data *mux_data = &npcm8xx_muxes[i]; + + hw =clk_hw_register_mux_table (dev, mux_data->name, + mux_data->parent_names, + mux_data->num_parents, + mux_data->flags, + clk_base +NPCM8XX_CLKSEL , + mux_data->shift, + mux_data->mask,0 , + mux_data->table, + &npcm8xx_clk_lock); + +if (IS_ERR (hw)) { +dev_err (dev,"npcm8xx_clk: Can't register mux\n" ); + goto err_mux_clk; + } + +if (mux_data->onecell_idx >=0 ) + npcm8xx_clk_data->hws[mux_data->onecell_idx] = hw; + } +Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 576
Quoted 5 lines
drivers/clk/clk-npcm8xx.c new file +5
-0+ err =devm_of_clk_add_hw_provider (dev, of_clk_hw_onecell_get, + npcm8xx_clk_data); +if (err) { +dev_err (dev,"unable to add clk provider\n" ); + goto unregister_refclk;The 'hw' pointer doesn't contain 'err' here so probe will return some positive number when this fails?
Indeed, will be fixed.
Quoted 29 lines
Quoted 29 lines
drivers/clk/clk-npcm8xx.c new file +28
-0+ } + +return err; + +err_mux_clk: +while (i--) { +if (npcm8xx_muxes[i].onecell_idx >=0 ) +clk_hw_unregister_mux (npcm8xx_clk_data->hws[npcm8xx_muxes[i].onecell_idx]); + } +unregister_refclk: +clk_hw_unregister (npcm8xx_clk_data->hws[NPCM8XX_CLK_REFCLK ]); +return PTR_ERR (hw); +} + +static const struct of_device_id npcm8xx_clk_dt_ids[] = { + { .compatible ="nuvoton,npcm845-clk" , }, + { } +}; +MODULE_DEVICE_TABLE (of, npcm8xx_clk_dt_ids); + +static struct platform_driver npcm8xx_clk_driver = { + .probe = npcm8xx_clk_probe, + .driver = { + .name ="npcm8xx_clk" , + .of_match_table = npcm8xx_clk_dt_ids, + }, +}; +Quoted 8 lines
drivers/clk/clk-npcm8xx.c line 610
Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+static int __init npcm8xx_clk_driver_init (void ) +{ +return platform_driver_register (&npcm8xx_clk_driver); +} +arch_initcall (npcm8xx_clk_driver_init); +If it can be a module it needs to unregister the driver on module exit.
Quoted 6 lines
Will remove the
MODULE_DEVICE_TABLE, the driver clock should be a built-in kernel driver.Thanks,
Tomer
Thanks a lot,
Tomer
Stephen Boyd
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
In reply to Tomer MaimonQuoting Tomer Maimon (2022-07-12 00:28:30)
Quoted 14 lines · Stephen Boyd
Quoted 13 lines
Quoting Tomer Maimon (2022-07-11 05:35:07)
drivers/clk/clk-npcm8xx.c line 172
Quoted 8 lines
drivers/clk/clk-npcm8xx.c new file +8
-0+*/ +int onecell_idx; +}; + +struct npcm8xx_clk_pll_data { +u32 reg; +const char *name; +const char *parent_name;Any reason why we're not using
clk_parent_dataor directclk_hwpointers?
Quoted 5 lines
For more historical reasons, I did the same method as done in the NPCM7XX driver.
Theclk_init_datastruct can use * const*parent_names,
https://elixir.bootlin.com/linux/v5.19-rc6/source/include/linux/clk-provider.h#L289
Is it problematic?
It will need to be changed to not use global string matching. Ideally new drivers use clk_parent_data or clk_hw pointers directly. It's faster and preferred.
Quoted 47 lines
Quoted 47 lines
Quoted 27 lines
drivers/clk/clk-npcm8xx.c new file +26
-0+ NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI0}, +/* bit 10-6 SPI0CKDV*/ + {NPCM8XX_CLKDIV3, 1, 5, NPCM8XX_CLK_S_SPIX, + NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPIX}, +/* bit 5-1 SPIXCKDV*/ + + {NPCM8XX_CLKDIV4, 28, 4, NPCM8XX_CLK_S_RG, NPCM8XX_CLK_S_RG_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RG}, +/* bit 31-28 RGREFDIV*/ + {NPCM8XX_CLKDIV4, 12, 4, NPCM8XX_CLK_S_RCP, NPCM8XX_CLK_S_RCP_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RCP}, +/* bit 15-12 RCPREFDIV*/ + {NPCM8XX_THRTL_CNT, 0, 2, NPCM8XX_CLK_S_TH, NPCM8XX_CLK_S_CPU_MUX, + CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_TH}, + /* bit 1-0 TH_DIV*/ +}; + +static DEFINE_SPINLOCK (npcm8xx_clk_lock); + +static int npcm8xx_clk_probe (struct platform_device *pdev) +{ +struct clk_hw_onecell_data *npcm8xx_clk_data; +struct device *dev = &pdev->dev; +struct device_node *np = dev->of_node; +void __iomem *clk_base; +struct resource res;drivers/clk/clk-npcm8xx.c line 452
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+struct clk_hw *hw; +int i, err; + + npcm8xx_clk_data =devm_kzalloc (dev,struct_size (npcm8xx_clk_data, hws, +NPCM8XX_NUM_CLOCKS ), +GFP_KERNEL ); +if (!npcm8xx_clk_data) +return -ENOMEM ; + + err =of_address_to_resource (np,0 , &res);Why can't we use
platform_get_resource()?Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+if (err) { +dev_err (dev,"Failed to get resource, ret %d\n" , err); +return err; + } + + clk_base =ioremap (res.start,resource_size (&res));
Quoted 7 lines
Quoted 1 line
And use
devm_platform_ioremap_resource()?Clock and reset driver use the same memory register map
0xF0801000-0xF0801FFF. For historical reasons the registers of both modules are mixed in the memory range0xF0801000-0xF0801FFFthis is why we can't have a separate region for each module.
In case I will usedevm_platform_ioremap_resourcefunction the reset ioremap will fail so the driver using the method above.
So the clk and reset driver should be the same driver, or one driver should register the other and use the auxiliary bus to express the relationship. That way we know that the drivers are tightly coupled and aren't going to stomp over each other.
Quoted 15 lines
Quoted 15 lines
drivers/clk/clk-npcm8xx.c line 469
Quoted 12 lines
drivers/clk/clk-npcm8xx.c new file +11
-0+if (!clk_base) { +dev_err (&pdev->dev,"Failed to remap I/O memory\n" ); +return -ENOMEM ; + } + + npcm8xx_clk_data->num =NPCM8XX_NUM_CLOCKS ; + +for (i =0 ; i <NPCM8XX_NUM_CLOCKS ; i++) + npcm8xx_clk_data->hws[i] =ERR_PTR (-EPROBE_DEFER ); + +/* Reference 25MHz clock */ Does this exist on the board? If so, I'd make a fixed rate clk in the dts and have 'refclk' be an input in the binding for this clk controller.
Quoted 1 line
No, it is an internal clock in the SoC, this is why it is in the driver.
Ok. I suppose that could be inside the 'soc' node for this device as a fixed rate clk but registering it here is also fine.
Tomer Maimon
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Hi Stephen,
Thanks for your reply.
Quoted 22 lines · Stephen Boyd
Quoting Tomer Maimon (2022-07-12 00:28:30)
Quoted 14 lines · Stephen Boyd
Quoted 13 lines
Quoting Tomer Maimon (2022-07-11 05:35:07)
drivers/clk/clk-npcm8xx.c line 172
Quoted 8 lines
drivers/clk/clk-npcm8xx.c new file +8
-0+*/ +int onecell_idx; +}; + +struct npcm8xx_clk_pll_data { +u32 reg; +const char *name; +const char *parent_name;Any reason why we're not using
clk_parent_dataor directclk_hwpointers?Quoted 5 lines
For more historical reasons, I did the same method as done in the NPCM7XX driver.
Theclk_init_datastruct can use * const*parent_names,
https://elixir.bootlin.com/linux/v5.19-rc6/source/include/linux/clk-provider.h#L289
Is it problematic?
Quoted 3 lines
It will need to be changed to not use global string matching. Ideally new drivers use
clk_parent_dataorclk_hwpointers directly. It's faster and preferred.
Will be modified V10.
Quoted 56 lines
Quoted 48 lines
Quoted 48 lines
Quoted 28 lines
drivers/clk/clk-npcm8xx.c new file +26
-0+ NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPI0}, +/* bit 10-6 SPI0CKDV*/ + {NPCM8XX_CLKDIV3, 1, 5, NPCM8XX_CLK_S_SPIX, + NPCM8XX_CLK_S_AHB, CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_SPIX}, +/* bit 5-1 SPIXCKDV*/ + + {NPCM8XX_CLKDIV4, 28, 4, NPCM8XX_CLK_S_RG, NPCM8XX_CLK_S_RG_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RG}, +/* bit 31-28 RGREFDIV*/ + {NPCM8XX_CLKDIV4, 12, 4, NPCM8XX_CLK_S_RCP, NPCM8XX_CLK_S_RCP_MUX, + CLK_DIVIDER_READ_ONLY, 0, NPCM8XX_CLK_RCP}, +/* bit 15-12 RCPREFDIV*/ + {NPCM8XX_THRTL_CNT, 0, 2, NPCM8XX_CLK_S_TH, NPCM8XX_CLK_S_CPU_MUX, + CLK_DIVIDER_READ_ONLY | CLK_DIVIDER_POWER_OF_TWO, 0, NPCM8XX_CLK_TH}, + /* bit 1-0 TH_DIV*/ +}; + +static DEFINE_SPINLOCK (npcm8xx_clk_lock); + +static int npcm8xx_clk_probe (struct platform_device *pdev) +{ +struct clk_hw_onecell_data *npcm8xx_clk_data; +struct device *dev = &pdev->dev; +struct device_node *np = dev->of_node; +void __iomem *clk_base; +struct resource res;drivers/clk/clk-npcm8xx.c line 452
Quoted 10 lines
drivers/clk/clk-npcm8xx.c new file +10
-0+struct clk_hw *hw; +int i, err; + + npcm8xx_clk_data =devm_kzalloc (dev,struct_size (npcm8xx_clk_data, hws, +NPCM8XX_NUM_CLOCKS ), +GFP_KERNEL ); +if (!npcm8xx_clk_data) +return -ENOMEM ; + + err =of_address_to_resource (np,0 , &res);Why can't we use
platform_get_resource()?Quoted 6 lines
drivers/clk/clk-npcm8xx.c new file +6
-0+if (err) { +dev_err (dev,"Failed to get resource, ret %d\n" , err); +return err; + } + + clk_base =ioremap (res.start,resource_size (&res));Quoted 7 lines
Quoted 1 line
And use
devm_platform_ioremap_resource()?Clock and reset driver use the same memory register map
0xF0801000-0xF0801FFF. For historical reasons the registers of both modules are mixed in the memory range0xF0801000-0xF0801FFFthis is why we can't have a separate region for each module.
In case I will usedevm_platform_ioremap_resourcefunction the reset ioremap will fail so the driver using the method above.
Quoted 4 lines
So the clk and reset driver should be the same driver, or one driver should register the other and use the auxiliary bus to express the relationship. That way we know that the drivers are tightly coupled and aren't going to stomp over each other.
I think it is very problematic to use the same driver for the reset and the clocks also because The NPCM reset driver is an old driver that was used also to the older NPCM BMC SoC so it will be problematic to use the clock and reset driver in the same space.
indeed the reset and clocks are using the same memory region but they are not using the same registers, is it not enough?
Please be aware that the NPCM reset driver is checking that it is using the reset registers before calling I/O functions.
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
drivers/clk/clk-npcm8xx.c line 469
Quoted 12 lines
drivers/clk/clk-npcm8xx.c new file +11
-0+if (!clk_base) { +dev_err (&pdev->dev,"Failed to remap I/O memory\n" ); +return -ENOMEM ; + } + + npcm8xx_clk_data->num =NPCM8XX_NUM_CLOCKS ; + +for (i =0 ; i <NPCM8XX_NUM_CLOCKS ; i++) + npcm8xx_clk_data->hws[i] =ERR_PTR (-EPROBE_DEFER ); + +/* Reference 25MHz clock */ Does this exist on the board? If so, I'd make a fixed rate clk in the dts and have 'refclk' be an input in the binding for this clk controller.
Quoted 4 lines
Quoted 1 line
No, it is an internal clock in the SoC, this is why it is in the driver.
Ok. I suppose that could be inside the 'soc' node for this device as a fixed rate clk but registering it here is also fine.
Best regards,
Tomer
Stephen Boyd
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Quoting Tomer Maimon (2022-07-19 03:04:43)
Quoted 7 lines · Stephen Boyd
Quoted 6 lines
So the clk and reset driver should be the same driver, or one driver should register the other and use the auxiliary bus to express the relationship. That way we know that the drivers are tightly coupled and aren't going to stomp over each other.
Quoted 8 lines
I think it is very problematic to use the same driver for the reset and the clocks also because The NPCM reset driver is an old driver that was used also to the older NPCM BMC SoC so it will be problematic to use the clock and reset driver in the same space.
indeed the reset and clocks are using the same memory region but they are not using the same registers, is it not enough?
Please be aware that the NPCM reset driver is checking that it is using the reset registers before calling I/O functions.
To put it simply, platform device drivers should use platform device APIs. The platform device APIs hide the fact that the firmware is ACPI or DT or nothing at all. The usage of of_address_to_resource() is problematic.
After converting that to platform APIs you'll get janitor style cleanups trying to convert to devm_platform_ioremap_resource(). We'll have to discuss this again when that happens, even if there's a comment in the code indicating we can't reserve the IO space because there's another driver. These problems have happened in the past, fun times!
Furthermore, in DT, reg properties aren't supposed to overlap. When that happens it usually indicates the DT is being written to describe driver structure instead of the IP blocks that are delivered by the hardware engineer. In this case it sounds like a combined clk and reset IP block because they piled all the SoC glue stuff into a register range. Are there more features in this IO range?
Tomer Maimon
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Hi Stephen,
Thanks for your detailed explanation!
Quoted 29 lines · Stephen Boyd
Quoting Tomer Maimon (2022-07-19 03:04:43)
Quoted 7 lines · Stephen Boyd
Quoted 6 lines
So the clk and reset driver should be the same driver, or one driver should register the other and use the auxiliary bus to express the relationship. That way we know that the drivers are tightly coupled and aren't going to stomp over each other.
Quoted 8 lines
I think it is very problematic to use the same driver for the reset and the clocks also because The NPCM reset driver is an old driver that was used also to the older NPCM BMC SoC so it will be problematic to use the clock and reset driver in the same space.
indeed the reset and clocks are using the same memory region but they are not using the same registers, is it not enough?
Please be aware that the NPCM reset driver is checking that it is using the reset registers before calling I/O functions.To put it simply, platform device drivers should use platform device APIs. The platform device APIs hide the fact that the firmware is ACPI or DT or nothing at all. The usage of
of_address_to_resource()is problematic.After converting that to platform APIs you'll get janitor style cleanups trying to convert to
devm_platform_ioremap_resource(). We'll have to discuss this again when that happens, even if there's a comment in the code indicating we can't reserve the IO space because there's another driver. These problems have happened in the past, fun times!
Quoted 6 lines
Furthermore, in DT, reg properties aren't supposed to overlap. When that happens it usually indicates the DT is being written to describe driver structure instead of the IP blocks that are delivered by the hardware engineer. In this case it sounds like a combined clk and reset IP block because they piled all the SoC glue stuff into a register range. Are there more features in this IO range?
No, this range only combined the reset and clock together, but it combined in a way that we cannot split it to two or even three different registers...
I do see a way to combine the clock and the reset driver, the NPCM reset driver is serving other NPCM BMC's.
Should we use regmap to handle the clock registers instead of ioremap?
Best regards,
Tomer
Stephen Boyd
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Quoting Tomer Maimon (2022-07-24 02:06:54)
Quoted 11 lines · Stephen Boyd
Quoted 6 lines
Furthermore, in DT, reg properties aren't supposed to overlap. When that happens it usually indicates the DT is being written to describe driver structure instead of the IP blocks that are delivered by the hardware engineer. In this case it sounds like a combined clk and reset IP block because they piled all the SoC glue stuff into a register range. Are there more features in this IO range?
No, this range only combined the reset and clock together, but it combined in a way that we cannot split it to two or even three different registers...
Because it is jumbled in some range?
Quoted 4 lines
I do see a way to combine the clock and the reset driver, the NPCM reset driver is serving other NPCM BMC's.
Should we use regmap to handle the clock registers instead of ioremap?
Sure? Using regmap or not looks like a parallel discussion. How does it help use platform APIs?
Tomer Maimon
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Quoted 10 lines · Stephen Boyd
Quoting Tomer Maimon (2022-07-24 02:06:54)
Quoted 8 lines · Stephen Boyd
Quoted 6 lines
Furthermore, in DT, reg properties aren't supposed to overlap. When that happens it usually indicates the DT is being written to describe driver structure instead of the IP blocks that are delivered by the hardware engineer. In this case it sounds like a combined clk and reset IP block because they piled all the SoC glue stuff into a register range. Are there more features in this IO range?
Quoted 5 lines
Quoted 3 lines
No, this range only combined the reset and clock together, but it combined in a way that we cannot split it to two or even three different registers...
Because it is jumbled in some range?
Yes.
Quoted 8 lines
Quoted 5 lines
I do see a way to combine the clock and the reset driver, the NPCM reset driver is serving other NPCM BMC's.
Should we use regmap to handle the clock registers instead of ioremap?Sure? Using regmap or not looks like a parallel discussion. How does it help use platform APIs?
I mean to use regmap API instead of platform API for handing the clock and reset registers.
the regmap API gives only one user access to R/W (lock).
I will be happy to get more suggestions, on how should we solve this situation.
Thanks,
Tomer
Stephen Boyd
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Quoting Tomer Maimon (2022-08-04 07:01:30)
Quoted 10 lines · Stephen Boyd
Quoted 2 lines
Because it is jumbled in some range?
Yes.
Quoted 6 lines
Quoted 5 lines
I do see a way to combine the clock and the reset driver, the NPCM reset driver is serving other NPCM BMC's.
Should we use regmap to handle the clock registers instead of ioremap?
Quoted 7 lines
Quoted 2 lines
Sure? Using regmap or not looks like a parallel discussion. How does it help use platform APIs?
I mean to use regmap API instead of platform API for handing the clock and reset registers.
the regmap API gives only one user access to R/W (lock).
I will be happy to get more suggestions, on how should we solve this situation.
Using platform APIs means using platform_*() functions, not of_*() functions, which are open-firmware/DT related. Regmap can be used to operate on registers mapped as __iomem, which is different from platform APIs.
Is having a lock even necessary? Do the reset and clk controls live within a shared register where we would need to prevent one driver from accessing that register at the same time as the other?
Tomer Maimon
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Hi Stephen,
Thanks for your reply.
Quoted 20 lines · Stephen Boyd
Quoting Tomer Maimon (2022-08-04 07:01:30)
Quoted 10 lines · Stephen Boyd
Quoted 2 lines
Because it is jumbled in some range?
Yes.
Quoted 6 lines
Quoted 5 lines
I do see a way to combine the clock and the reset driver, the NPCM reset driver is serving other NPCM BMC's.
Should we use regmap to handle the clock registers instead of ioremap?Quoted 7 lines
Quoted 2 lines
Sure? Using regmap or not looks like a parallel discussion. How does it help use platform APIs?
I mean to use regmap API instead of platform API for handing the clock and reset registers.
the regmap API gives only one user access to R/W (lock).
I will be happy to get more suggestions, on how should we solve this situation.
Quoted 4 lines
Using platform APIs means using platform_*() functions, not of_*() functions, which are open-firmware/DT related. Regmap can be used to operate on registers mapped as
__iomem, which is different from platform APIs.
I will use platform_get_resource() and devm_ioremap_resource() functions in the next version.
Quoted 2 lines
Is having a lock even necessary? Do the reset and clk controls live
You are right, lock use is not necessary.
Quoted 2 lines
within a shared register where we would need to prevent one driver from accessing that register at the same time as the other?
reset and clk drivers are living fine with shared registers, we don't need to handle the register access between the clk and the reset drivers.
Best regards,
Tomer
Tomer Maimon
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Hi Stephen,
Sorry, just to make it clear.
Quoted 26 lines · Tomer Maimon
Hi Stephen,
Thanks for your reply.
Quoted 20 lines · Stephen Boyd
Quoting Tomer Maimon (2022-08-04 07:01:30)
Quoted 17 lines · Stephen Boyd
Quoted 2 lines
Because it is jumbled in some range?
Yes.
Quoted 8 lines
Quoted 5 lines
I do see a way to combine the clock and the reset driver, the NPCM reset driver is serving other NPCM BMC's.
Should we use regmap to handle the clock registers instead of ioremap?Sure? Using regmap or not looks like a parallel discussion. How does it help use platform APIs?
I mean to use regmap API instead of platform API for handing the clock and reset registers.
the regmap API gives only one user access to R/W (lock).
I will be happy to get more suggestions, on how should we solve this situation.
Quoted 6 lines
Quoted 4 lines
Using platform APIs means using platform_*() functions, not of_*() functions, which are open-firmware/DT related. Regmap can be used to operate on registers mapped as
__iomem, which is different from platform APIs.I will use
platform_get_resource()anddevm_ioremap_resource()functions in the next version.
I will use platform_get_resource() and ioremap() function next veriosn, is it fine?
Quoted 6 lines
Quoted 3 lines
Is having a lock even necessary? Do the reset and clk controls live
You are right, lock use is not necessary.
Quoted 2 lines
within a shared register where we would need to prevent one driver from accessing that register at the same time as the other?
Quoted 7 lines
reset and clk drivers are living fine with shared registers, we don't need to handle the register access between the clk and the reset drivers.
Best regards,
Tomer
Best regards,
Tomer
Stephen Boyd
maintainerRe: [PATCH v8 04/16] clk: npcm8xx: add clock controller
Quoting Tomer Maimon (2022-08-08 06:08:08)
Quoted 9 lines · Tomer Maimon
Quoted 6 lines
Quoted 4 lines
Using platform APIs means using platform_*() functions, not of_*() functions, which are open-firmware/DT related. Regmap can be used to operate on registers mapped as
__iomem, which is different from platform APIs.I will use
platform_get_resource()anddevm_ioremap_resource()functions in the next version.I will use
platform_get_resource()andioremap()function next veriosn, is it fine?
As stated earlier it will work for now but eventually you'll get patches from janitors trying to convert to a devm based API that reserves the register region. Can you ioremap the register once and register an auxiliary device and driver for the reset (or clk) part so that the driver can be moved out to the drivers/reset/ path?
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Tomer Maimon
maintainerPATCH v8 5/16 dt-bindings: reset: npcm: add GCR syscon property
In reply to Tomer MaimonDescribe syscon property that handles general control registers (GCR) in Nuvoton BMC NPCM reset driver.
--- .../devicetree/bindings/reset/nuvoton,npcm750-reset.yaml | 6 ++++++ 1 file changed, 6 insertions(+)
Documentation/devicetree/bindings/reset/nuvoton,npcm750-reset.yaml +6 -0
@@ -19,6 +19,10 @@ properties: '#reset-cells': const: 2 + nuvoton,sysgcr: + $ref: /schemas/types.yaml#/definitions/phandle + description: a phandle to access GCR registers. + nuvoton,sw-reset-number: $ref: /schemas/types.yaml#/definitions/uint32 minimum: 1
@@ -31,6 +35,7 @@ required: - compatible - reg - '#reset-cells' + - nuvoton,sysgcr additionalProperties: false
@@ -41,6 +46,7 @@ examples: compatible = "nuvoton,npcm750-reset"; reg = <0xf0801000 0x70>; #reset-cells = <2>; + nuvoton,sysgcr = <&gcr>; nuvoton,sw-reset-number = <2>; };
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 6/16 ARM: dts: nuvoton: add reset syscon property
In reply to Tomer MaimonAdd nuvoton,sysgcr syscon property to the reset node to handle the general control registers.
--- arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 1 + 1 file changed, 1 insertion(+)
arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi +1 -0
@@ -110,6 +110,7 @@ rstc: rstc@f0801000 { compatible = "nuvoton,npcm750-reset"; reg = <0xf0801000 0x70>; #reset-cells = <2>; + nuvoton,sysgcr = <&gcr>; }; clk: clock-controller@f0801000 {
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 7/16 reset: npcm: using syscon instead of device data
In reply to Tomer MaimonUsing syscon device tree property instead of device data to handle the NPCM general control registers.
In case the syscon not found the code still search for nuvoton,npcm750-gcr to support DTS backward compatibility.
--- drivers/reset/reset-npcm.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/reset/reset-npcm.c +9 -5
@@ -138,8 +138,7 @@ static int npcm_reset_xlate(struct reset_controller_dev *rcdev, }static const struct of_device_id npcm_rc_match[] = { - { .compatible ="nuvoton,npcm750-reset" , - .data = (void *)"nuvoton,npcm750-gcr" }, + { .compatible ="nuvoton,npcm750-reset" }, { } };
@@ -160,10 +159,15 @@ static int npcm_usb_reset(struct platform_device *pdev, struct npcm_rc_data *rc) gcr_dt = (const char *)of_match_device (dev->driver->of_match_table, dev)->data; - gcr_regmap =syscon_regmap_lookup_by_compatible (gcr_dt); + gcr_regmap =syscon_regmap_lookup_by_phandle (dev->of_node,"nuvoton,sysgcr" );if (IS_ERR (gcr_regmap)) { -dev_err (&pdev->dev,"Failed to find %s\n" , gcr_dt); -return PTR_ERR (gcr_regmap); +dev_warn (&pdev->dev,"Failed to find nuvoton,sysgcr property, please update the device tree\n" ); +dev_info (&pdev->dev,"Using nuvoton,npcm750-gcr for Poleg backward compatibility\n" ); + gcr_regmap =syscon_regmap_lookup_by_compatible ("nuvoton,npcm750-gcr" ); +if (IS_ERR (gcr_regmap)) { +dev_err (&pdev->dev,"Failed to find nuvoton,npcm750-gcr" ); +return PTR_ERR (gcr_regmap); + } }/* checking which USB device is enabled */
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 8/16 dt-bindings: reset: npcm: Add support for NPCM8XX
In reply to Tomer MaimonAdd binding document and device tree binding constants for Nuvoton BMC NPCM8XX reset controller.
--- .../devicetree/bindings/reset/nuvoton,npcm750-reset.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/reset/nuvoton,npcm750-reset.yaml +3 -1
@@ -11,7 +11,9 @@ maintainers: properties: compatible: - const: nuvoton,npcm750-reset + enum: + - nuvoton,npcm750-reset # Poleg NPCM7XX SoC + - nuvoton,npcm845-reset # Arbel NPCM8XX SoC reg: maxItems: 1
Signature · 1 line
2.33.0
Re: [PATCH v8 08/16] dt-bindings: reset: npcm: Add support for NPCM8XX
Quoted 5 lines · Tomer Maimon
Add binding document and device tree binding constants for Nuvoton BMC NPCM8XX reset controller.
Signed-off-by Tomer Maimon <[email protected]>
regards
Philipp
Tomer Maimon
maintainerPATCH v8 9/16 reset: npcm: Add NPCM8XX support
In reply to Tomer MaimonUpdated the NPCM reset driver to add support for Nuvoton BMC NPCM8XX SoC. As part of adding NPCM8XX support:
- Add NPCM8XX specific compatible string.
- Add NPCM8XX USB reset.
- Add data to handle architecture-specific reset parameters.
- Some of the Reset Id and number of resets are different from NPCM7XX.
--- drivers/reset/reset-npcm.c | 211 ++++++++++++++++++++++++++++++------- 1 file changed, 173 insertions(+), 38 deletions(-)
drivers/reset/reset-npcm.c +173 -38
@@ -17,13 +17,20 @@/* NPCM7xx GCR registers */ #define NPCM_MDLR_OFFSET 0x7C -#define NPCM_MDLR_USBD0 BIT(9) -#define NPCM_MDLR_USBD1 BIT(8) -#define NPCM_MDLR_USBD2_4 BIT(21) -#define NPCM_MDLR_USBD5_9 BIT(22) +#define NPCM7XX_MDLR_USBD0 BIT(9) +#define NPCM7XX_MDLR_USBD1 BIT(8) +#define NPCM7XX_MDLR_USBD2_4 BIT(21) +#define NPCM7XX_MDLR_USBD5_9 BIT(22) + +/* NPCM8xx MDLR bits */ +#define NPCM8XX_MDLR_USBD0_3 BIT(9) +#define NPCM8XX_MDLR_USBD4_7 BIT(22) +#define NPCM8XX_MDLR_USBD8 BIT(24) +#define NPCM8XX_MDLR_USBD9 BIT(21)#define NPCM_USB1PHYCTL_OFFSET 0x140#define NPCM_USB2PHYCTL_OFFSET 0x144 +#define NPCM_USB3PHYCTL_OFFSET 0x148#define NPCM_USBXPHYCTL_RS BIT(28)/* NPCM7xx Reset registers */
@@ -49,12 +56,38 @@#define NPCM_IPSRST3_USBPHY1 BIT(24)#define NPCM_IPSRST3_USBPHY2 BIT(25) +#define NPCM_IPSRST4 0x74 +#define NPCM_IPSRST4_USBPHY3 BIT(25) +#define NPCM_IPSRST4_USB_HOST2 BIT(31) +#define NPCM_RC_RESETS_PER_REG 32#define NPCM_MASK_RESETS GENMASK(4, 0) +enum { +BMC_NPCM7XX =0 , +BMC_NPCM8XX , +}; + +static const u32 npxm7xx_ipsrst[] = {NPCM_IPSRST1 ,NPCM_IPSRST2 ,NPCM_IPSRST3 }; +static const u32 npxm8xx_ipsrst[] = {NPCM_IPSRST1 ,NPCM_IPSRST2 ,NPCM_IPSRST3 , +NPCM_IPSRST4 }; + +struct npcm_reset_info { +u32 bmc_id; +u32 num_ipsrst; +const u32 *ipsrst; +}; + +static const struct npcm_reset_info npxm7xx_reset_info[] = { + {.bmc_id =BMC_NPCM7XX , .num_ipsrst =3 , .ipsrst = npxm7xx_ipsrst}}; +static const struct npcm_reset_info npxm8xx_reset_info[] = { + {.bmc_id =BMC_NPCM8XX , .num_ipsrst =4 , .ipsrst = npxm8xx_ipsrst}}; +struct npcm_rc_data {struct reset_controller_dev rcdev;struct notifier_block restart_nb; +const struct npcm_reset_info *info; +struct regmap *gcr_regmap;u32 sw_reset_number;void __iomem *base;spinlock_t lock;
@@ -120,14 +153,24 @@ static int npcm_rc_status(struct reset_controller_dev *rcdev,static int npcm_reset_xlate (struct reset_controller_dev *rcdev,const struct of_phandle_args *reset_spec) { +struct npcm_rc_data *rc =to_rc_data (rcdev);unsigned int offset, bit; +bool offset_found = false; +int off_num; offset = reset_spec->args[0 ]; -if (offset !=NPCM_IPSRST1 && offset !=NPCM_IPSRST2 && - offset !=NPCM_IPSRST3 ) { +for (off_num =0 ; off_num < rc->info->num_ipsrst ; off_num++) { +if (offset == rc->info->ipsrst[off_num]) { + offset_found = true; +break ; + } + } + +if (!offset_found) {dev_err (rcdev->dev,"Error reset register (0x%x)\n" , offset);return -EINVAL ; } + bit = reset_spec->args[1 ];if (bit >=NPCM_RC_RESETS_PER_REG ) {dev_err (rcdev->dev,"Error reset number (%d)\n" , bit);
@@ -138,49 +181,29 @@ static int npcm_reset_xlate(struct reset_controller_dev *rcdev, }static const struct of_device_id npcm_rc_match[] = { - { .compatible ="nuvoton,npcm750-reset" }, + { .compatible ="nuvoton,npcm750-reset" , .data = &npxm7xx_reset_info}, + { .compatible ="nuvoton,npcm845-reset" , .data = &npxm8xx_reset_info}, { } }; -/* -* The following procedure should be observed in USB PHY, USB device and -* USB host initialization at BMC boot -*/ -static int npcm_usb_reset (struct platform_device *pdev,struct npcm_rc_data *rc) +static void npcm_usb_reset_npcm7xx (struct npcm_rc_data *rc) {u32 mdlr, iprst1, iprst2, iprst3; -struct device *dev = &pdev->dev; -struct regmap *gcr_regmap;u32 ipsrst1_bits =0 ;u32 ipsrst2_bits =NPCM_IPSRST2_USB_HOST ;u32 ipsrst3_bits =0 ; -const char *gcr_dt; - - gcr_dt = (const char *) -of_match_device (dev->driver->of_match_table, dev)->data; - - gcr_regmap =syscon_regmap_lookup_by_phandle (dev->of_node,"nuvoton,sysgcr" ); -if (IS_ERR (gcr_regmap)) { -dev_warn (&pdev->dev,"Failed to find nuvoton,sysgcr property, please update the device tree\n" ); -dev_info (&pdev->dev,"Using nuvoton,npcm750-gcr for Poleg backward compatibility\n" ); - gcr_regmap =syscon_regmap_lookup_by_compatible ("nuvoton,npcm750-gcr" ); -if (IS_ERR (gcr_regmap)) { -dev_err (&pdev->dev,"Failed to find nuvoton,npcm750-gcr" ); -return PTR_ERR (gcr_regmap); - } - }/* checking which USB device is enabled */ -regmap_read (gcr_regmap,NPCM_MDLR_OFFSET , &mdlr); -if (!(mdlr &NPCM_MDLR_USBD0 )) +regmap_read (rc->gcr_regmap,NPCM_MDLR_OFFSET , &mdlr); +if (!(mdlr &NPCM7XX_MDLR_USBD0 )) ipsrst3_bits |=NPCM_IPSRST3_USBD0 ; -if (!(mdlr &NPCM_MDLR_USBD1 )) +if (!(mdlr &NPCM7XX_MDLR_USBD1 )) ipsrst1_bits |=NPCM_IPSRST1_USBD1 ; -if (!(mdlr &NPCM_MDLR_USBD2_4 )) +if (!(mdlr &NPCM7XX_MDLR_USBD2_4 )) ipsrst1_bits |= (NPCM_IPSRST1_USBD2 |NPCM_IPSRST1_USBD3 |NPCM_IPSRST1_USBD4 ); -if (!(mdlr &NPCM_MDLR_USBD0 )) { +if (!(mdlr &NPCM7XX_MDLR_USBD0 )) { ipsrst1_bits |= (NPCM_IPSRST1_USBD5 |NPCM_IPSRST1_USBD6 ); ipsrst3_bits |= (NPCM_IPSRST3_USBD7 |
@@ -203,9 +226,9 @@ static int npcm_usb_reset(struct platform_device *pdev, struct npcm_rc_data *rc)writel (iprst3, rc->base +NPCM_IPSRST3 );/* clear USB PHY RS bit */ -regmap_update_bits (gcr_regmap,NPCM_USB1PHYCTL_OFFSET , +regmap_update_bits (rc->gcr_regmap,NPCM_USB1PHYCTL_OFFSET ,NPCM_USBXPHYCTL_RS ,0 ); -regmap_update_bits (gcr_regmap,NPCM_USB2PHYCTL_OFFSET , +regmap_update_bits (rc->gcr_regmap,NPCM_USB2PHYCTL_OFFSET ,NPCM_USBXPHYCTL_RS ,0 );/* deassert reset USB PHY */
@@ -215,9 +238,9 @@ static int npcm_usb_reset(struct platform_device *pdev, struct npcm_rc_data *rc)udelay (50 );/* set USB PHY RS bit */ -regmap_update_bits (gcr_regmap,NPCM_USB1PHYCTL_OFFSET , +regmap_update_bits (rc->gcr_regmap,NPCM_USB1PHYCTL_OFFSET ,NPCM_USBXPHYCTL_RS ,NPCM_USBXPHYCTL_RS ); -regmap_update_bits (gcr_regmap,NPCM_USB2PHYCTL_OFFSET , +regmap_update_bits (rc->gcr_regmap,NPCM_USB2PHYCTL_OFFSET ,NPCM_USBXPHYCTL_RS ,NPCM_USBXPHYCTL_RS );/* deassert reset USB devices*/
@@ -228,6 +251,118 @@ static int npcm_usb_reset(struct platform_device *pdev, struct npcm_rc_data *rc)writel (iprst1, rc->base +NPCM_IPSRST1 );writel (iprst2, rc->base +NPCM_IPSRST2 );writel (iprst3, rc->base +NPCM_IPSRST3 ); +} + +static void npcm_usb_reset_npcm8xx (struct npcm_rc_data *rc) +{ +u32 mdlr, iprst1, iprst2, iprst3, iprst4; +u32 ipsrst1_bits =0 ; +u32 ipsrst2_bits =NPCM_IPSRST2_USB_HOST ; +u32 ipsrst3_bits =0 ; +u32 ipsrst4_bits =NPCM_IPSRST4_USB_HOST2 |NPCM_IPSRST4_USBPHY3 ; + +/* checking which USB device is enabled */ +regmap_read (rc->gcr_regmap,NPCM_MDLR_OFFSET , &mdlr); +if (!(mdlr &NPCM8XX_MDLR_USBD0_3 )) { + ipsrst3_bits |=NPCM_IPSRST3_USBD0 ; + ipsrst1_bits |= (NPCM_IPSRST1_USBD1 | +NPCM_IPSRST1_USBD2 | +NPCM_IPSRST1_USBD3 ); + } +if (!(mdlr &NPCM8XX_MDLR_USBD4_7 )) { + ipsrst1_bits |= (NPCM_IPSRST1_USBD4 | +NPCM_IPSRST1_USBD5 | +NPCM_IPSRST1_USBD6 ); + ipsrst3_bits |=NPCM_IPSRST3_USBD7 ; + } + +if (!(mdlr &NPCM8XX_MDLR_USBD8 )) + ipsrst3_bits |=NPCM_IPSRST3_USBD8 ; +if (!(mdlr &NPCM8XX_MDLR_USBD9 )) + ipsrst3_bits |=NPCM_IPSRST3_USBD9 ; + +/* assert reset USB PHY and USB devices */ + iprst1 =readl (rc->base +NPCM_IPSRST1 ); + iprst2 =readl (rc->base +NPCM_IPSRST2 ); + iprst3 =readl (rc->base +NPCM_IPSRST3 ); + iprst4 =readl (rc->base +NPCM_IPSRST4 ); + + iprst1 |= ipsrst1_bits; + iprst2 |= ipsrst2_bits; + iprst3 |= (ipsrst3_bits |NPCM_IPSRST3_USBPHY1 | +NPCM_IPSRST3_USBPHY2 ); + iprst2 |= ipsrst4_bits; + +writel (iprst1, rc->base +NPCM_IPSRST1 ); +writel (iprst2, rc->base +NPCM_IPSRST2 ); +writel (iprst3, rc->base +NPCM_IPSRST3 ); +writel (iprst4, rc->base +NPCM_IPSRST4 ); + +/* clear USB PHY RS bit */ +regmap_update_bits (rc->gcr_regmap,NPCM_USB1PHYCTL_OFFSET , +NPCM_USBXPHYCTL_RS ,0 ); +regmap_update_bits (rc->gcr_regmap,NPCM_USB2PHYCTL_OFFSET , +NPCM_USBXPHYCTL_RS ,0 ); +regmap_update_bits (rc->gcr_regmap,NPCM_USB3PHYCTL_OFFSET , +NPCM_USBXPHYCTL_RS ,0 ); + +/* deassert reset USB PHY */ + iprst3 &= ~(NPCM_IPSRST3_USBPHY1 |NPCM_IPSRST3_USBPHY2 ); +writel (iprst3, rc->base +NPCM_IPSRST3 ); + iprst4 &= ~NPCM_IPSRST4_USBPHY3 ; +writel (iprst4, rc->base +NPCM_IPSRST4 ); + +/* set USB PHY RS bit */ +regmap_update_bits (rc->gcr_regmap,NPCM_USB1PHYCTL_OFFSET , +NPCM_USBXPHYCTL_RS ,NPCM_USBXPHYCTL_RS ); +regmap_update_bits (rc->gcr_regmap,NPCM_USB2PHYCTL_OFFSET , +NPCM_USBXPHYCTL_RS ,NPCM_USBXPHYCTL_RS ); +regmap_update_bits (rc->gcr_regmap,NPCM_USB3PHYCTL_OFFSET , +NPCM_USBXPHYCTL_RS ,NPCM_USBXPHYCTL_RS ); + +/* deassert reset USB devices*/ + iprst1 &= ~ipsrst1_bits; + iprst2 &= ~ipsrst2_bits; + iprst3 &= ~ipsrst3_bits; + iprst4 &= ~ipsrst4_bits; + +writel (iprst1, rc->base +NPCM_IPSRST1 ); +writel (iprst2, rc->base +NPCM_IPSRST2 ); +writel (iprst3, rc->base +NPCM_IPSRST3 ); +writel (iprst4, rc->base +NPCM_IPSRST4 ); +} + +/* +* The following procedure should be observed in USB PHY, USB device and +* USB host initialization at BMC boot +*/ +static int npcm_usb_reset (struct platform_device *pdev,struct npcm_rc_data *rc) +{ +struct device *dev = &pdev->dev; + + rc->gcr_regmap =syscon_regmap_lookup_by_phandle (dev->of_node,"nuvoton,sysgcr" ); +if (IS_ERR (rc->gcr_regmap)) { +dev_warn (&pdev->dev,"Failed to find nuvoton,sysgcr property, please update the device tree\n" ); +dev_info (&pdev->dev,"Using nuvoton,npcm750-gcr for Poleg backward compatibility\n" ); + rc->gcr_regmap =syscon_regmap_lookup_by_compatible ("nuvoton,npcm750-gcr" ); +if (IS_ERR (rc->gcr_regmap)) { +dev_err (&pdev->dev,"Failed to find nuvoton,npcm750-gcr" ); +return PTR_ERR (rc->gcr_regmap); + } + } + + rc->info = (const struct npcm_reset_info *) +of_match_device (dev->driver->of_match_table, dev)->data; +switch (rc->info->bmc_id) { +case BMC_NPCM7XX : +npcm_usb_reset_npcm7xx (rc); +break ; +case BMC_NPCM8XX : +npcm_usb_reset_npcm8xx (rc); +break ; +default : +return -ENODEV ; + }return 0 ; }
Signature · 1 line
2.33.0
Re: [PATCH v8 09/16] reset: npcm: Add NPCM8XX support
Quoted 8 lines · Tomer Maimon
Updated the NPCM reset driver to add support for Nuvoton BMC NPCM8XX SoC. As part of adding NPCM8XX support:
- Add NPCM8XX specific compatible string.
- Add NPCM8XX USB reset.
- Add data to handle architecture-specific reset parameters.
- Some of the Reset Id and number of resets are different from NPCM7XX.Signed-off-by Tomer Maimon <[email protected]>
regards
Philipp
Tomer Maimon
maintainerPATCH v8 10/16 dt-bindings: arm: npcm: Add maintainer
In reply to Tomer MaimonAdd Tomer Maimon to the maintainers list.
--- Documentation/devicetree/bindings/arm/npcm/npcm.yaml | 1 + Documentation/devicetree/bindings/arm/npcm/nuvoton,gcr.yaml | 1 + 2 files changed, 2 insertions(+)
Documentation/devicetree/bindings/arm/npcm/npcm.yaml +1 -0
@@ -8,6 +8,7 @@ title: NPCM Platforms Device Tree Bindings maintainers: - Jonathan Neuschäfer <[email protected]> + - Tomer Maimon <[email protected]> properties: $nodename:
Documentation/devicetree/bindings/arm/npcm/nuvoton,gcr.yaml +1 -0
@@ -8,6 +8,7 @@ title: Global Control Registers block in Nuvoton SoCs maintainers: - Jonathan Neuschäfer <[email protected]> + - Tomer Maimon <[email protected]> description: The Global Control Registers (GCR) are a block of registers in Nuvoton SoCs
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 11/16 dt-bindings: arm: npcm: Add nuvoton,npcm845 compatible string
In reply to Tomer MaimonAdd a compatible string for Nuvoton BMC NPCM845 SoC and a board specific device tree for the NPCM845 (Arbel) evaluation board.
--- Documentation/devicetree/bindings/arm/npcm/npcm.yaml | 6 ++++++ 1 file changed, 6 insertions(+)
Documentation/devicetree/bindings/arm/npcm/npcm.yaml +6 -0
@@ -27,4 +27,10 @@ properties: - nuvoton,npcm750-evb # NPCM750 evaluation board - const: nuvoton,npcm750 + - description: NPCM845 based boards + items: + - enum: + - nuvoton,npcm845-evb # NPCM845 evaluation board + - const: nuvoton,npcm845 + additionalProperties: true
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 12/16 dt-bindings: arm: npcm: Add nuvoton,npcm845 GCR compatible string
In reply to Tomer MaimonAdd a compatible string for Nuvoton BMC NPCM845 global control registers.
--- Documentation/devicetree/bindings/arm/npcm/nuvoton,gcr.yaml | 1 + 1 file changed, 1 insertion(+)
Documentation/devicetree/bindings/arm/npcm/nuvoton,gcr.yaml +1 -0
@@ -21,6 +21,7 @@ properties: - enum: - nuvoton,wpcm450-gcr - nuvoton,npcm750-gcr + - nuvoton,npcm845-gcr - const: syscon - const: simple-mfd
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 13/16 arm64: npcm: Add support for Nuvoton NPCM8XX BMC SoC
In reply to Tomer MaimonThis adds support for the Nuvoton NPCM8XX Board Management controller (BMC) SoC family.
The NPCM8XX based quad-core Cortex-A35 ARMv8 architecture.
--- MAINTAINERS | 2 ++ arch/arm64/Kconfig.platforms | 11 +++++++++++ 2 files changed, 13 insertions(+)
MAINTAINERS +2 -0
@@ -2448,9 +2448,11 @@ F: Documentation/devicetree/bindings/*/*npcm* F: Documentation/devicetree/bindings/arm/npcm/* F: arch/arm/boot/dts/nuvoton-npcm* F: arch/arm/mach-npcm/ +F: arch/arm64/boot/dts/nuvoton/ F: drivers/*/*npcm* F: drivers/*/*/*npcm* F: include/dt-bindings/clock/nuvoton,npcm7xx-clock.h +F: include/dt-bindings/clock/nuvoton,npcm8xx-clock.h ARM/NUVOTON WPCM450 ARCHITECTURE M: Jonathan Neuschäfer <[email protected]>
arch/arm64/Kconfig.platforms +11 -0
@@ -202,6 +202,17 @@ config ARCH_MXC This enables support for the ARMv8 based SoCs in the NXP i.MX family. +config ARCH_NPCM + bool "Nuvoton NPCM Architecture" + select PINCTRL + select GPIOLIB + select NPCM7XX_TIMER + select RESET_CONTROLLER + select MFD_SYSCON + help + General support for NPCM8xx BMC (Arbel). + Nuvoton NPCM8xx BMC based on the Cortex A35. + config ARCH_QCOM bool "Qualcomm Platforms" select GPIOLIB
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 14/16 arm64: dts: nuvoton: Add initial NPCM8XX device tree
In reply to Tomer MaimonThis adds initial device tree support for the Nuvoton NPCM845 Board Management controller (BMC) SoC family.
The NPCM845 based quad-core Cortex-A35 ARMv8 architecture and have various peripheral IPs.
--- arch/arm64/boot/dts/Makefile | 1 + .../dts/nuvoton/nuvoton-common-npcm8xx.dtsi | 170 ++++++++++++++++++ .../boot/dts/nuvoton/nuvoton-npcm845.dtsi | 76 ++++++++ 3 files changed, 247 insertions(+) create mode 100644 arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi create mode 100644 arch/arm64/boot/dts/nuvoton/nuvoton-npcm845.dtsi
arch/arm64/boot/dts/Makefile +1 -0
@@ -19,6 +19,7 @@ subdir-y += lg subdir-y += marvell subdir-y += mediatek subdir-y += microchip +subdir-y += nuvoton subdir-y += nvidia subdir-y += qcom subdir-y += realtek
arch/arm64/boot/dts/nuvoton/nuvoton-common-npcm8xx.dtsi new file +170 -0
170 lines not on this page · raw message
arch/arm64/boot/dts/nuvoton/nuvoton-npcm845.dtsi new file +76 -0
76 lines not on this page · raw message
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 15/16 arm64: dts: nuvoton: Add initial NPCM845 EVB device tree
In reply to Tomer MaimonAdd initial Nuvoton NPCM845 evaluation board device tree.
--- arch/arm64/boot/dts/nuvoton/Makefile | 2 ++ .../boot/dts/nuvoton/nuvoton-npcm845-evb.dts | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 arch/arm64/boot/dts/nuvoton/Makefile create mode 100644 arch/arm64/boot/dts/nuvoton/nuvoton-npcm845-evb.dts
arch/arm64/boot/dts/nuvoton/Makefile new file +2 -0
@@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_NPCM) += nuvoton-npcm845-evb.dtb
arch/arm64/boot/dts/nuvoton/nuvoton-npcm845-evb.dts new file +30 -0
@@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021 Nuvoton Technology [email protected] + +/dts-v1/; +#include "nuvoton-npcm845.dtsi" + +/ { + model = "Nuvoton npcm845 Development Board (Device Tree)"; + compatible = "nuvoton,npcm845-evb", "nuvoton,npcm845"; + + aliases { + serial0 = &serial0; + }; + + chosen { + stdout-path = &serial0; + }; + + memory { + reg = <0x0 0x0 0x0 0x40000000>; + }; +}; + +&serial0 { + status = "okay"; +}; + +&watchdog1 { + status = "okay"; +};
Signature · 1 line
2.33.0
Tomer Maimon
maintainerPATCH v8 16/16 arm64: defconfig: Add Nuvoton NPCM family support
In reply to Tomer MaimonEnable basic drivers for NPCM8XX booting up support: Architecture, Clock, and WD.
--- arch/arm64/configs/defconfig | 3 +++ 1 file changed, 3 insertions(+)
arch/arm64/configs/defconfig +3 -0
@@ -49,6 +49,7 @@ CONFIG_ARCH_MEDIATEK=y CONFIG_ARCH_MESON=y CONFIG_ARCH_MVEBU=y CONFIG_ARCH_MXC=y +CONFIG_ARCH_NPCM=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_RENESAS=y CONFIG_ARCH_ROCKCHIP=y
@@ -627,6 +628,7 @@ CONFIG_RENESAS_RZG2LWDT=y CONFIG_UNIPHIER_WATCHDOG=y CONFIG_PM8916_WATCHDOG=m CONFIG_BCM2835_WDT=y +CONFIG_NPCM7XX_WATCHDOG=y CONFIG_MFD_ALTERA_SYSMGR=y CONFIG_MFD_BD9571MWV=y CONFIG_MFD_AXP20X_I2C=y
@@ -1021,6 +1023,7 @@ CONFIG_COMMON_CLK_FSL_SAI=y CONFIG_COMMON_CLK_S2MPS11=y CONFIG_COMMON_CLK_PWM=y CONFIG_COMMON_CLK_VC5=y +CONFIG_COMMON_CLK_NPCM8XX=y CONFIG_COMMON_CLK_BD718XX=m CONFIG_CLK_RASPBERRYPI=m CONFIG_CLK_IMX8MM=y
Signature · 1 line
2.33.0