linux-serial.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
@ 2023-03-15  7:28 Jacky Huang
  2023-03-15  7:28 ` [PATCH 01/15] arm64: Kconfig.platforms: Add config for Nuvoton MA35 platform Jacky Huang
                   ` (16 more replies)
  0 siblings, 17 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

This patchset adds initial support for the Nuvoton ma35d1 SoC, including
initial device tree, clock driver, reset driver, and serial driver.

This patchset cover letter is based from the initial support for Nuvoton
ma35d1 to keep tracking the version history.

This patchset had been applied to Linux kernel 6.3-rc2 and tested on the
Nuvoton ma35d1 SOM evaluation board.

(ma35d1 information: https://www.nuvoton.com/products/microprocessors/arm-cortex-a35-mpus/)
MA35D1 porting on linux-5.10.y can be found at: https://github.com/OpenNuvoton/MPU-Family

Jacky Huang (15):
  arm64: Kconfig.platforms: Add config for Nuvoton MA35 platform
  arm64: defconfig: Add Nuvoton MA35 family support
  mfd: Add the header file of Nuvoton ma35d1 system manager
  dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller
  dt-bindings: reset: nuvoton: add binding for ma35d1 IP reset control
  dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible
  dt-bindings: arm: Add initial bindings for Nuvoton platform
  dt-bindings: clock: Document ma35d1 clock controller bindings
  dt-bindings: reset: Document ma35d1 reset controller bindings
  dt-bindings: serial: Document ma35d1 uart controller bindings
  arm64: dts: nuvoton: Add initial ma35d1 device tree
  clk: nuvoton: Add clock driver for ma35d1 clock controller
  reset: Add Nuvoton ma35d1 reset driver support
  tty: serial: Add Nuvoton ma35d1 serial driver support
  MAINTAINERS: Add entry for NUVOTON MA35

 .../devicetree/bindings/arm/nuvoton.yaml      |  30 +
 .../bindings/clock/nuvoton,ma35d1-clk.yaml    |  83 ++
 .../devicetree/bindings/mfd/syscon.yaml       |   1 +
 .../bindings/reset/nuvoton,ma35d1-reset.yaml  |  50 +
 .../serial/nuvoton,ma35d1-serial.yaml         |  52 +
 MAINTAINERS                                   |  12 +
 arch/arm64/Kconfig.platforms                  |   9 +
 arch/arm64/boot/dts/nuvoton/Makefile          |   2 +
 .../boot/dts/nuvoton/ma35d1-iot-512m.dts      |  24 +
 .../boot/dts/nuvoton/ma35d1-som-256m.dts      |  23 +
 arch/arm64/boot/dts/nuvoton/ma35d1.dtsi       | 272 +++++
 arch/arm64/configs/defconfig                  |   1 +
 drivers/clk/Makefile                          |   1 +
 drivers/clk/nuvoton/Makefile                  |   4 +
 drivers/clk/nuvoton/clk-ma35d1-divider.c      | 144 +++
 drivers/clk/nuvoton/clk-ma35d1-pll.c          | 534 ++++++++++
 drivers/clk/nuvoton/clk-ma35d1.c              | 970 ++++++++++++++++++
 drivers/clk/nuvoton/clk-ma35d1.h              | 198 ++++
 drivers/reset/Kconfig                         |   6 +
 drivers/reset/Makefile                        |   1 +
 drivers/reset/reset-ma35d1.c                  | 152 +++
 drivers/tty/serial/Kconfig                    |  18 +
 drivers/tty/serial/Makefile                   |   1 +
 drivers/tty/serial/ma35d1_serial.c            | 842 +++++++++++++++
 drivers/tty/serial/ma35d1_serial.h            |  93 ++
 .../dt-bindings/clock/nuvoton,ma35d1-clk.h    | 253 +++++
 .../dt-bindings/reset/nuvoton,ma35d1-reset.h  | 108 ++
 include/linux/mfd/ma35d1-sys.h                |  95 ++
 include/uapi/linux/serial_core.h              |   3 +
 29 files changed, 3982 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/nuvoton.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
 create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
 create mode 100644 Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
 create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
 create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
 create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
 create mode 100644 drivers/clk/nuvoton/Makefile
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1-divider.c
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1-pll.c
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1.c
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1.h
 create mode 100644 drivers/reset/reset-ma35d1.c
 create mode 100644 drivers/tty/serial/ma35d1_serial.c
 create mode 100644 drivers/tty/serial/ma35d1_serial.h
 create mode 100644 include/dt-bindings/clock/nuvoton,ma35d1-clk.h
 create mode 100644 include/dt-bindings/reset/nuvoton,ma35d1-reset.h
 create mode 100644 include/linux/mfd/ma35d1-sys.h

-- 
2.34.1


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

* [PATCH 01/15] arm64: Kconfig.platforms: Add config for Nuvoton MA35 platform
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-15  7:28 ` [PATCH 02/15] arm64: defconfig: Add Nuvoton MA35 family support Jacky Huang
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add ARCH_NUVOTON configuration option for Nuvoton MA35 family SoCs.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 arch/arm64/Kconfig.platforms | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 89a0b13b058d..c1f277c05569 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -236,6 +236,15 @@ config ARCH_NPCM
 	  General support for NPCM8xx BMC (Arbel).
 	  Nuvoton NPCM8xx BMC based on the Cortex A35.
 
+config ARCH_NUVOTON
+	bool "Nuvoton MA35 Platforms"
+	select GPIOLIB
+	select PINCTRL
+	select RESET_CONTROLLER
+	help
+	  This enables support for the ARMv8 based Nuvoton SoCs such
+	  as MA35D1.
+
 config ARCH_QCOM
 	bool "Qualcomm Platforms"
 	select GPIOLIB
-- 
2.34.1


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

* [PATCH 02/15] arm64: defconfig: Add Nuvoton MA35 family support
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
  2023-03-15  7:28 ` [PATCH 01/15] arm64: Kconfig.platforms: Add config for Nuvoton MA35 platform Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16 14:23   ` Arnd Bergmann
  2023-03-15  7:28 ` [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager Jacky Huang
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Enable basic drivers for ma35d1 booting up support: architecture,
device tree, clock, reset, and uart.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 7790ee42c68a..c96189acb02c 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -53,6 +53,7 @@ CONFIG_ARCH_LAYERSCAPE=y
 CONFIG_ARCH_MXC=y
 CONFIG_ARCH_S32=y
 CONFIG_ARCH_NPCM=y
+CONFIG_ARCH_NUVOTON=y
 CONFIG_ARCH_QCOM=y
 CONFIG_ARCH_RENESAS=y
 CONFIG_ARCH_ROCKCHIP=y
-- 
2.34.1


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

* [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
  2023-03-15  7:28 ` [PATCH 01/15] arm64: Kconfig.platforms: Add config for Nuvoton MA35 platform Jacky Huang
  2023-03-15  7:28 ` [PATCH 02/15] arm64: defconfig: Add Nuvoton MA35 family support Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16 13:30   ` Ilpo Järvinen
  2023-03-16 14:44   ` Arnd Bergmann
  2023-03-15  7:28 ` [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller Jacky Huang
                   ` (13 subsequent siblings)
  16 siblings, 2 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

The system manager is a set of registers used for power control,
multi-function pin control, USB phy control, IP reset, and other
miscellaneous controls. It also contains some registers that
provide SoC information and status.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 include/linux/mfd/ma35d1-sys.h | 95 ++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 include/linux/mfd/ma35d1-sys.h

diff --git a/include/linux/mfd/ma35d1-sys.h b/include/linux/mfd/ma35d1-sys.h
new file mode 100644
index 000000000000..dcd85231125d
--- /dev/null
+++ b/include/linux/mfd/ma35d1-sys.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Nuvoton Technologies.
+ * Author: Chi-Fen Li <cfli0@nuvoton.com>
+ *
+ * System management control registers of MA35D1 SoC
+ */
+#ifndef __LINUX_MFD_MA35D1_SYS_H
+#define __LINUX_MFD_MA35D1_SYS_H
+
+#define REG_SYS_PDID		(0x000) /* Product and Device Identifier */
+#define REG_SYS_PWRONOTP	(0x004) /* Power-on Setting OTP Source */
+#define REG_SYS_PWRONPIN	(0x008) /* Power-on Setting Pin Source */
+#define REG_SYS_RSTSTS		(0x010) /* Reset Source Active Status */
+#define REG_SYS_MISCRFCR	(0x014) /* Miscellaneous Reset Function */
+#define REG_SYS_RSTDEBCTL	(0x018) /* Reset Pin De-bounce Control */
+#define REG_SYS_LVRDCR		(0x01C) /* Low Voltage Reset & Detect */
+#define REG_SYS_IPRST0		(0x020) /* Reset Control Register 0 */
+#define REG_SYS_IPRST1		(0x024) /* Reset Control Register 1 */
+#define REG_SYS_IPRST2		(0x028) /* Reset Control Register 2 */
+#define REG_SYS_IPRST3		(0x02C) /* Reset Control Register 3 */
+#define REG_SYS_PMUCR		(0x030) /* Power Management Unit Control */
+#define REG_SYS_DDRCQCSR	(0x034) /* DDR Q Channel Control and Status */
+#define REG_SYS_PMUIEN		(0x038) /* PMU Interrupt Enable */
+#define REG_SYS_PMUSTS		(0x03C) /* PMU Status */
+#define REG_SYS_CA35WRBADR1	(0x040) /* A35 Core 1 Warm-boot Address */
+#define REG_SYS_CA35WRBPAR1	(0x044) /* A35 Core 1 Warm-boot Parameter */
+#define REG_SYS_CA35WRBADR2	(0x048) /* A35 Core 2 Warm-boot Address */
+#define REG_SYS_CA35WRBPAR2	(0x04C) /* A35 Core 2 Warm-boot Parameter */
+#define REG_SYS_USBPMISCR	(0x060) /* USB PHY Miscellaneous Control */
+#define REG_SYS_USBP0PCR	(0x064) /* USB Port 0 PHY Control */
+#define REG_SYS_USBP1PCR	(0x068) /* USB Port 1 PHY Control */
+#define REG_SYS_MISCFCR0	(0x070) /* Miscellaneous Function Control 0 */
+#define REG_SYS_MISCFCR1	(0x074) /* Miscellaneous Function Control 1 */
+#define REG_SYS_MISCIER		(0x078) /* Miscellaneous Interrupt Enable */
+#define REG_SYS_MISCISR		(0x07C) /* Miscellaneous Interrupt Status */
+#define REG_SYS_GPA_MFPL	(0x080) /* GPIOA Multi-Function Control LSB */
+#define REG_SYS_GPA_MFPH	(0x084) /* GPIOA Multi-Function Control MSB */
+#define REG_SYS_GPB_MFPL	(0x088) /* GPIOB Multi-Function Control LSB */
+#define REG_SYS_GPB_MFPH	(0x08C) /* GPIOB Multi-Function Control MSB */
+#define REG_SYS_GPC_MFPL	(0x090) /* GPIOC Multi-Function Control LSB */
+#define REG_SYS_GPC_MFPH	(0x094) /* GPIOC Multi-Function Control MSB */
+#define REG_SYS_GPD_MFPL	(0x098) /* GPIOD Multi-Function Control LSB */
+#define REG_SYS_GPD_MFPH	(0x09C) /* GPIOD Multi-Function Control MSB */
+#define REG_SYS_GPE_MFPL	(0x0A0) /* GPIOE Multi-Function Control LSB */
+#define REG_SYS_GPE_MFPH	(0x0A4) /* GPIOE Multi-Function Control MSB */
+#define REG_SYS_GPF_MFPL	(0x0A8) /* GPIOF Multi-Function Control LSB */
+#define REG_SYS_GPF_MFPH	(0x0AC) /* GPIOF Multi-Function Control MSB */
+#define REG_SYS_GPG_MFPL	(0x0B0) /* GPIOG Multi-Function Control LSB */
+#define REG_SYS_GPG_MFPH	(0x0B4) /* GPIOG Multi-Function Control MSB */
+#define REG_SYS_GPH_MFPL	(0x0B8) /* GPIOH Multi-Function Control LSB */
+#define REG_SYS_GPH_MFPH	(0x0BC) /* GPIOH Multi-Function Control MSB */
+#define REG_SYS_GPI_MFPL	(0x0C0) /* GPIOI Multi-Function Control LSB */
+#define REG_SYS_GPI_MFPH	(0x0C4) /* GPIOI Multi-Function Control MSB */
+#define REG_SYS_GPJ_MFPL	(0x0C8) /* GPIOJ Multi-Function Control LSB */
+#define REG_SYS_GPJ_MFPH	(0x0CC) /* GPIOJ Multi-Function Control MSB */
+#define REG_SYS_GPK_MFPL	(0x0D0) /* GPIOK Multi-Function Control LSB */
+#define REG_SYS_GPK_MFPH	(0x0D4) /* GPIOK Multi-Function Control MSB */
+#define REG_SYS_GPL_MFPL	(0x0D8) /* GPIOL Multi-Function Control LSB */
+#define REG_SYS_GPL_MFPH	(0x0DC) /* GPIOL Multi-Function Control MSB */
+#define REG_SYS_GPM_MFPL	(0x0E0) /* GPIOM Multi-Function Control LSB */
+#define REG_SYS_GPM_MFPH	(0x0E4) /* GPIOM Multi-Function Control MSB */
+#define REG_SYS_GPN_MFPL	(0x0E8) /* GPION Multi-Function Control LSB */
+#define REG_SYS_GPN_MFPH	(0x0EC) /* GPION Multi-Function Control MSB */
+#define REG_SYS_HIRCFTRIM	(0x100) /* HIRC Frequency Trim Value */
+#define REG_SYS_TSENSRFCR	(0x104) /* Temperature Sensor Control */
+#define REG_SYS_GMAC0MISCR	(0x108) /* GMAC 0 Miscellaneous Control */
+#define REG_SYS_GMAC1MISCR	(0x10C) /* GMAC 1 Miscellaneous Control */
+#define REG_SYS_MACAD0LSR	(0x110) /* MAC Address 0 LSW */
+#define REG_SYS_MACAD0HSR	(0x114) /* MAC Address 0 HSW */
+#define REG_SYS_MACAD1LSR	(0x118) /* MAC Address 1 LSW */
+#define REG_SYS_MACAD1HSR	(0x11C) /* MAC Address 1 HSW */
+#define REG_SYS_CSDBGCTL	(0x120) /* CoreSight Debug Control */
+#define REG_SYS_GPAB_MFOS	(0x140) /* GPIOA/B Output Mode Select */
+#define REG_SYS_GPCD_MFOS	(0x144) /* GPIOC/D Output Mode Select */
+#define REG_SYS_GPEF_MFOS	(0x148) /* GPIOE/F Output Mode Select */
+#define REG_SYS_GPGH_MFOS	(0x14C) /* GPIOG/H Output Mode Select */
+#define REG_SYS_GPIJ_MFOS	(0x150) /* GPIOI/J Output Mode Select */
+#define REG_SYS_GPKL_MFOS	(0x154) /* GPIOK/L Output Mode Select */
+#define REG_SYS_GPMN_MFOS	(0x158) /* GPIOM/N Output Mode Select */
+#define REG_SYS_UID0		(0x180) /* Unique Identifier Word 0 */
+#define REG_SYS_UID1		(0x184) /* Unique Identifier Word 1 */
+#define REG_SYS_UID2		(0x188) /* Unique Identifier Word 2 */
+#define REG_SYS_UCID0		(0x190) /* Unique Customer Identifier 0 */
+#define REG_SYS_UCID1		(0x194) /* Unique Customer Identifier 1 */
+#define REG_SYS_UCID2		(0x198) /* Unique Customer Identifier 2 */
+#define REG_SYS_RLKTZS		(0x1A0) /* TZS Register Lock Control */
+#define REG_SYS_RLKTZNS		(0x1A4) /* TZNS Register Lock Control */
+#define REG_SYS_RLKSUBM		(0x1A8) /* SubM Register Lock Control */
+#define REG_SYS_DPLPASWD	(0x1B0) /* Deployed Password */
+
+void ma35d1_reg_lock(void);
+void ma35d1_reg_unlock(void);
+
+#endif /* __LINUX_MFD_MA35D1_SYS_H */
-- 
2.34.1


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

* [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (2 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16  7:31   ` Krzysztof Kozlowski
  2023-03-15  7:28 ` [PATCH 05/15] dt-bindings: reset: nuvoton: add binding for ma35d1 IP reset control Jacky Huang
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add the dt-bindings header for Nuvoton ma35d1, that gets shared
between the clock controller and clock references in the dts.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 .../dt-bindings/clock/nuvoton,ma35d1-clk.h    | 253 ++++++++++++++++++
 1 file changed, 253 insertions(+)
 create mode 100644 include/dt-bindings/clock/nuvoton,ma35d1-clk.h

diff --git a/include/dt-bindings/clock/nuvoton,ma35d1-clk.h b/include/dt-bindings/clock/nuvoton,ma35d1-clk.h
new file mode 100644
index 000000000000..6c569fdd6e06
--- /dev/null
+++ b/include/dt-bindings/clock/nuvoton,ma35d1-clk.h
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Nuvoton Technologies.
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_NUVOTON_MA35D1_CLK_H
+#define __DT_BINDINGS_CLOCK_NUVOTON_MA35D1_CLK_H
+
+/* external and internal oscillator clocks */
+#define HXT		0
+#define HXT_GATE	1
+#define LXT		2
+#define LXT_GATE	3
+#define HIRC		4
+#define HIRC_GATE	5
+#define LIRC		6
+#define LIRC_GATE	7
+/* PLLs */
+#define CAPLL		8
+#define SYSPLL		9
+#define DDRPLL		10
+#define APLL		11
+#define EPLL		12
+#define VPLL		13
+/* EPLL divider */
+#define EPLL_DIV2	14
+#define EPLL_DIV4	15
+#define EPLL_DIV8	16
+/* CPU clock, system clock, AXI, HCLK and PCLK */
+#define CA35CLK_MUX	17
+#define AXICLK_DIV2	18
+#define AXICLK_DIV4	19
+#define AXICLK_MUX	20
+#define SYSCLK0_MUX	21
+#define SYSCLK1_MUX	22
+#define SYSCLK1_DIV2	23
+#define HCLK0		24
+#define HCLK1		25
+#define HCLK2		26
+#define PCLK0		27
+#define PCLK1		28
+#define PCLK2		29
+#define HCLK3		30
+#define PCLK3		31
+#define PCLK4		32
+/* AXI and AHB peripheral clocks */
+#define USBPHY0		33
+#define USBPHY1		34
+#define DDR0_GATE	35
+#define DDR6_GATE	36
+#define CAN0_MUX	37
+#define CAN0_DIV	38
+#define CAN0_GATE	39
+#define CAN1_MUX	40
+#define CAN1_DIV	41
+#define CAN1_GATE	42
+#define CAN2_MUX	43
+#define CAN2_DIV	44
+#define CAN2_GATE	45
+#define CAN3_MUX	46
+#define CAN3_DIV	47
+#define CAN3_GATE	48
+#define SDH0_MUX	49
+#define SDH0_GATE	50
+#define SDH1_MUX	51
+#define SDH1_GATE	52
+#define NAND_GATE	53
+#define USBD_GATE	54
+#define USBH_GATE	55
+#define HUSBH0_GATE	56
+#define HUSBH1_GATE	57
+#define GFX_MUX		58
+#define GFX_GATE	59
+#define VC8K_GATE	60
+#define DCU_MUX		61
+#define DCU_GATE	62
+#define DCUP_DIV	63
+#define EMAC0_GATE	64
+#define EMAC1_GATE	65
+#define CCAP0_MUX	66
+#define CCAP0_DIV	67
+#define CCAP0_GATE	68
+#define CCAP1_MUX	69
+#define CCAP1_DIV	70
+#define CCAP1_GATE	71
+#define PDMA0_GATE	72
+#define PDMA1_GATE	73
+#define PDMA2_GATE	74
+#define PDMA3_GATE	75
+#define WH0_GATE	76
+#define WH1_GATE	77
+#define HWS_GATE	78
+#define EBI_GATE	79
+#define SRAM0_GATE	80
+#define SRAM1_GATE	81
+#define ROM_GATE	82
+#define TRA_GATE	83
+#define DBG_MUX		84
+#define DBG_GATE	85
+#define CKO_MUX		86
+#define CKO_DIV		87
+#define CKO_GATE	88
+#define GTMR_GATE	89
+#define GPA_GATE	90
+#define GPB_GATE	91
+#define GPC_GATE	92
+#define GPD_GATE	93
+#define GPE_GATE	94
+#define GPF_GATE	95
+#define GPG_GATE	96
+#define GPH_GATE	97
+#define GPI_GATE	98
+#define GPJ_GATE	99
+#define GPK_GATE	100
+#define GPL_GATE	101
+#define GPM_GATE	102
+#define GPN_GATE	103
+/* APB peripheral clocks */
+#define TMR0_MUX	104
+#define TMR0_GATE	105
+#define TMR1_MUX	106
+#define TMR1_GATE	107
+#define TMR2_MUX	108
+#define TMR2_GATE	109
+#define TMR3_MUX	110
+#define TMR3_GATE	111
+#define TMR4_MUX	112
+#define TMR4_GATE	113
+#define TMR5_MUX	114
+#define TMR5_GATE	115
+#define TMR6_MUX	116
+#define TMR6_GATE	117
+#define TMR7_MUX	118
+#define TMR7_GATE	119
+#define TMR8_MUX	120
+#define TMR8_GATE	121
+#define TMR9_MUX	122
+#define TMR9_GATE	123
+#define TMR10_MUX	124
+#define TMR10_GATE	125
+#define TMR11_MUX	126
+#define TMR11_GATE	127
+#define UART0_MUX	128
+#define UART0_DIV	129
+#define UART0_GATE	130
+#define UART1_MUX	131
+#define UART1_DIV	132
+#define UART1_GATE	133
+#define UART2_MUX	134
+#define UART2_DIV	135
+#define UART2_GATE	136
+#define UART3_MUX	137
+#define UART3_DIV	138
+#define UART3_GATE	139
+#define UART4_MUX	140
+#define UART4_DIV	141
+#define UART4_GATE	142
+#define UART5_MUX	143
+#define UART5_DIV	144
+#define UART5_GATE	145
+#define UART6_MUX	146
+#define UART6_DIV	147
+#define UART6_GATE	148
+#define UART7_MUX	149
+#define UART7_DIV	150
+#define UART7_GATE	151
+#define UART8_MUX	152
+#define UART8_DIV	153
+#define UART8_GATE	154
+#define UART9_MUX	155
+#define UART9_DIV	156
+#define UART9_GATE	157
+#define UART10_MUX	158
+#define UART10_DIV	159
+#define UART10_GATE	160
+#define UART11_MUX	161
+#define UART11_DIV	162
+#define UART11_GATE	163
+#define UART12_MUX	164
+#define UART12_DIV	165
+#define UART12_GATE	166
+#define UART13_MUX	167
+#define UART13_DIV	168
+#define UART13_GATE	169
+#define UART14_MUX	170
+#define UART14_DIV	171
+#define UART14_GATE	172
+#define UART15_MUX	173
+#define UART15_DIV	174
+#define UART15_GATE	175
+#define UART16_MUX	176
+#define UART16_DIV	177
+#define UART16_GATE	178
+#define RTC_GATE	179
+#define DDR_GATE	180
+#define KPI_MUX		181
+#define KPI_DIV		182
+#define KPI_GATE	183
+#define I2C0_GATE	184
+#define I2C1_GATE	185
+#define I2C2_GATE	186
+#define I2C3_GATE	187
+#define I2C4_GATE	188
+#define I2C5_GATE	189
+#define QSPI0_MUX	190
+#define QSPI0_GATE	191
+#define QSPI1_MUX	192
+#define QSPI1_GATE	193
+#define SMC0_MUX	194
+#define SMC0_DIV	195
+#define SMC0_GATE	196
+#define SMC1_MUX	197
+#define SMC1_DIV	198
+#define SMC1_GATE	199
+#define WDT0_MUX	200
+#define WDT0_GATE	201
+#define WDT1_MUX	202
+#define WDT1_GATE	203
+#define WDT2_MUX	204
+#define WDT2_GATE	205
+#define WWDT0_MUX	206
+#define WWDT1_MUX	207
+#define WWDT2_MUX	208
+#define EPWM0_GATE	209
+#define EPWM1_GATE	210
+#define EPWM2_GATE	211
+#define I2S0_MUX	212
+#define I2S0_GATE	213
+#define I2S1_MUX	214
+#define I2S1_GATE	215
+#define SSMCC_GATE	216
+#define SSPCC_GATE	217
+#define SPI0_MUX	218
+#define SPI0_GATE	219
+#define SPI1_MUX	220
+#define SPI1_GATE	221
+#define SPI2_MUX	222
+#define SPI2_GATE	223
+#define SPI3_MUX	224
+#define SPI3_GATE	225
+#define ECAP0_GATE	226
+#define ECAP1_GATE	227
+#define ECAP2_GATE	228
+#define QEI0_GATE	229
+#define QEI1_GATE	230
+#define QEI2_GATE	231
+#define ADC_DIV		232
+#define ADC_GATE	233
+#define EADC_DIV	234
+#define EADC_GATE	235
+#define	CLK_MAX_IDX	236
+
+#endif /* __DT_BINDINGS_CLOCK_NUVOTON_MA35D1_CLK_H */
-- 
2.34.1


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

* [PATCH 05/15] dt-bindings: reset: nuvoton: add binding for ma35d1 IP reset control
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (3 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16  7:31   ` Krzysztof Kozlowski
  2023-03-15  7:28 ` [PATCH 06/15] dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible Jacky Huang
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add the dt-bindings header for Nuvoton ma35d1, that gets shared
between the reset controller and reset references in the dts.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 .../dt-bindings/reset/nuvoton,ma35d1-reset.h  | 108 ++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100644 include/dt-bindings/reset/nuvoton,ma35d1-reset.h

diff --git a/include/dt-bindings/reset/nuvoton,ma35d1-reset.h b/include/dt-bindings/reset/nuvoton,ma35d1-reset.h
new file mode 100644
index 000000000000..6d0791b04d52
--- /dev/null
+++ b/include/dt-bindings/reset/nuvoton,ma35d1-reset.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Nuvoton Technologies.
+ * Author: Chi-Fen Li <cfli0@nuvoton.com>
+ *
+ * Device Tree binding constants for MA35D1 reset controller.
+ */
+
+#ifndef __DT_BINDINGS_RESET_MA35D1_H
+#define __DT_BINDINGS_RESET_MA35D1_H
+
+#define MA35D1_RESET_CHIP	0
+#define MA35D1_RESET_CA35CR0	1
+#define MA35D1_RESET_CA35CR1	2
+#define MA35D1_RESET_CM4	3
+#define MA35D1_RESET_PDMA0	4
+#define MA35D1_RESET_PDMA1	5
+#define MA35D1_RESET_PDMA2	6
+#define MA35D1_RESET_PDMA3	7
+#define MA35D1_RESET_DISP	9
+#define MA35D1_RESET_VCAP0	10
+#define MA35D1_RESET_VCAP1	11
+#define MA35D1_RESET_GFX	12
+#define MA35D1_RESET_VDEC	13
+#define MA35D1_RESET_WHC0	14
+#define MA35D1_RESET_WHC1	15
+#define MA35D1_RESET_GMAC0	16
+#define MA35D1_RESET_GMAC1	17
+#define MA35D1_RESET_HWSEM	18
+#define MA35D1_RESET_EBI	19
+#define MA35D1_RESET_HSUSBH0	20
+#define MA35D1_RESET_HSUSBH1	21
+#define MA35D1_RESET_HSUSBD	22
+#define MA35D1_RESET_USBHL	23
+#define MA35D1_RESET_SDH0	24
+#define MA35D1_RESET_SDH1	25
+#define MA35D1_RESET_NAND	26
+#define MA35D1_RESET_GPIO	27
+#define MA35D1_RESET_MCTLP	28
+#define MA35D1_RESET_MCTLC	29
+#define MA35D1_RESET_DDRPUB	30
+#define MA35D1_RESET_TMR0	34
+#define MA35D1_RESET_TMR1	35
+#define MA35D1_RESET_TMR2	36
+#define MA35D1_RESET_TMR3	37
+#define MA35D1_RESET_I2C0	40
+#define MA35D1_RESET_I2C1	41
+#define MA35D1_RESET_I2C2	42
+#define MA35D1_RESET_I2C3	43
+#define MA35D1_RESET_QSPI0	44
+#define MA35D1_RESET_SPI0	45
+#define MA35D1_RESET_SPI1	46
+#define MA35D1_RESET_SPI2	47
+#define MA35D1_RESET_UART0	48
+#define MA35D1_RESET_UART1	49
+#define MA35D1_RESET_UART2	50
+#define MA35D1_RESET_UAER3	51
+#define MA35D1_RESET_UART4	52
+#define MA35D1_RESET_UART5	53
+#define MA35D1_RESET_UART6	54
+#define MA35D1_RESET_UART7	55
+#define MA35D1_RESET_CANFD0	56
+#define MA35D1_RESET_CANFD1	57
+#define MA35D1_RESET_EADC0	60
+#define MA35D1_RESET_I2S0	61
+#define MA35D1_RESET_SC0	64
+#define MA35D1_RESET_SC1	65
+#define MA35D1_RESET_QSPI1	68
+#define MA35D1_RESET_SPI3	70
+#define MA35D1_RESET_EPWM0	80
+#define MA35D1_RESET_EPWM1	81
+#define MA35D1_RESET_QEI0	86
+#define MA35D1_RESET_QEI1	87
+#define MA35D1_RESET_ECAP0	90
+#define MA35D1_RESET_ECAP1	91
+#define MA35D1_RESET_CANFD2	92
+#define MA35D1_RESET_ADC0	95
+#define MA35D1_RESET_TMR4	96
+#define MA35D1_RESET_TMR5	97
+#define MA35D1_RESET_TMR6	98
+#define MA35D1_RESET_TMR7	99
+#define MA35D1_RESET_TMR8	100
+#define MA35D1_RESET_TMR9	101
+#define MA35D1_RESET_TMR10	102
+#define MA35D1_RESET_TMR11	103
+#define MA35D1_RESET_UART8	104
+#define MA35D1_RESET_UART9	105
+#define MA35D1_RESET_UART10	106
+#define MA35D1_RESET_UART11	107
+#define MA35D1_RESET_UART12	108
+#define MA35D1_RESET_UART13	109
+#define MA35D1_RESET_UART14	110
+#define MA35D1_RESET_UART15	111
+#define MA35D1_RESET_UART16	112
+#define MA35D1_RESET_I2S1	113
+#define MA35D1_RESET_I2C4	114
+#define MA35D1_RESET_I2C5	115
+#define MA35D1_RESET_EPWM2	116
+#define MA35D1_RESET_ECAP2	117
+#define MA35D1_RESET_QEI2	118
+#define MA35D1_RESET_CANFD3	119
+#define MA35D1_RESET_KPI	120
+#define MA35D1_RESET_GIC	124
+#define MA35D1_RESET_SSMCC	126
+#define MA35D1_RESET_SSPCC	127
+#define MA35D1_RESET_COUNT	128
+
+#endif
-- 
2.34.1


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

* [PATCH 06/15] dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (4 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 05/15] dt-bindings: reset: nuvoton: add binding for ma35d1 IP reset control Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16  7:31   ` Krzysztof Kozlowski
  2023-03-15  7:28 ` [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform Jacky Huang
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add Nuvoton ma35d1 system registers compatible

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 Documentation/devicetree/bindings/mfd/syscon.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
index c828c4f5e4a7..e7a3c6e1e77f 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.yaml
+++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
@@ -57,6 +57,7 @@ properties:
               - microchip,sparx5-cpu-syscon
               - mstar,msc313-pmsleep
               - nuvoton,wpcm450-shm
+              - nuvoton,ma35d1-sys
               - rockchip,px30-qos
               - rockchip,rk3036-qos
               - rockchip,rk3066-qos
-- 
2.34.1


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

* [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (5 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 06/15] dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16  7:33   ` Krzysztof Kozlowski
  2023-03-15  7:28 ` [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings Jacky Huang
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add binding for ARMv8 based Nuvotn SoCs and platform boards.
Add initial bindings for ma35d1 series development boards.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 .../devicetree/bindings/arm/nuvoton.yaml      | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/nuvoton.yaml

diff --git a/Documentation/devicetree/bindings/arm/nuvoton.yaml b/Documentation/devicetree/bindings/arm/nuvoton.yaml
new file mode 100644
index 000000000000..f95e7b30711e
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/nuvoton.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/nuvoton.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35 series SoC based platforms
+
+maintainers:
+  - Jacky Huang <ychuang3@nuvoton.com>
+
+description: |
+  Boards with an ARMv8 based Nuvoton MA35 series SoC shall have
+  the following properties.
+
+properties:
+  $nodename:
+    const: '/'
+  compatible:
+    oneOf:
+
+      - description: MA35D1 based boards
+        items:
+          - enum:
+              - nuvoton,ma35d1-iot
+              - nuvoton,ma35d1-som
+          - const: nuvoton,ma35d1
+
+additionalProperties: true
+...
-- 
2.34.1


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

* [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (6 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-15 21:59   ` Stephen Boyd
  2023-03-16  7:35   ` Krzysztof Kozlowski
  2023-03-15  7:28 ` [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset " Jacky Huang
                   ` (8 subsequent siblings)
  16 siblings, 2 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add documentation to describe nuvoton ma35d1 clock driver bindings.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 .../bindings/clock/nuvoton,ma35d1-clk.yaml    | 83 +++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml

diff --git a/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
new file mode 100644
index 000000000000..5c2dea071b38
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/nuvoton,ma35d1-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 Clock Controller Module Binding
+
+maintainers:
+  - Chi-Fang Li <cfli0@nuvoton.com>
+  - Jacky Huang <ychuang3@nuvoton.com>
+
+description: |
+  The MA35D1 clock controller generates clocks for the whole chip,
+  including system clocks and all peripheral clocks.
+
+  See also:
+    include/dt-bindings/clock/ma35d1-clk.h
+
+properties:
+  compatible:
+    items:
+      - const: nuvoton,ma35d1-clk
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  "#clock-cells":
+    const: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: clk_hxt
+
+  assigned-clocks:
+    maxItems: 5
+
+  assigned-clock-rates:
+    maxItems: 5
+
+  nuvoton,pll-mode:
+    description:
+      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
+      EPLL, and VPLL in sequential. The operation mode value 0 is for
+      integer mode, 1 is for fractional mode, and 2 is for spread
+      spectrum mode.
+    $ref: /schemas/types.yaml#/definitions/uint32-array
+    maxItems: 5
+    items:
+      minimum: 0
+      maximum: 2
+
+  nuvoton,sys:
+    description:
+      Phandle to the system management controller.
+    $ref: "/schemas/types.yaml#/definitions/phandle-array"
+
+required:
+  - compatible
+  - reg
+  - "#clock-cells"
+  - clocks
+  - clock-names
+  - nuvoton,sys
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+    clk: clock-controller@40460200 {
+        compatible = "nuvoton,ma35d1-clk", "syscon";
+        reg = <0x40460200 0x100>;
+        #clock-cells = <1>;
+        clocks = <&clk_hxt>;
+        clock-names = "clk_hxt";
+        nuvoton,sys = <&sys>;
+    };
+...
-- 
2.34.1


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

* [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset controller bindings
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (7 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16  7:37   ` Krzysztof Kozlowski
  2023-03-15  7:28 ` [PATCH 10/15] dt-bindings: serial: Document ma35d1 uart " Jacky Huang
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add documentation to describe nuvoton ma35d1 reset driver bindings.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 .../bindings/reset/nuvoton,ma35d1-reset.yaml  | 50 +++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml

diff --git a/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
new file mode 100644
index 000000000000..f66c566c6dce
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reset/nuvoton,ma35d1-reset.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 Reset Controller
+
+maintainers:
+  - Chi-Fang Li <cfli0@nuvoton.com>
+  - Jacky Huang <ychuang3@nuvoton.com>
+
+description:
+  The system reset controller can be used to reset various peripheral
+  controllers in MA35D1 SoC.
+
+properties:
+  compatible:
+    const: nuvoton,ma35d1-reset
+
+  regmap:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: Phandle to the register map node.
+
+  '#reset-cells':
+    const: 1
+
+required:
+  - compatible
+  - regmap
+  - '#reset-cells'
+
+additionalProperties: false
+
+examples:
+  # system reset controller node:
+  - |
+    #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
+
+    sys: system-management@40460000 {
+        compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";
+        reg = <0x40460000 0x200>;
+
+        reset: reset-controller {
+            compatible = "nuvoton,ma35d1-reset";
+            regmap = <&sys>;
+            #reset-cells = <1>;
+        };
+    };
+...
-- 
2.34.1


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

* [PATCH 10/15] dt-bindings: serial: Document ma35d1 uart controller bindings
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (8 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset " Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16  7:40   ` Krzysztof Kozlowski
  2023-03-15  7:28 ` [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree Jacky Huang
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add documentation to describe nuvoton ma35d1 uart driver bindings.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 .../serial/nuvoton,ma35d1-serial.yaml         | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml

diff --git a/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml b/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
new file mode 100644
index 000000000000..9daa2efd4734
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/serial/nuvoton,ma35d1-serial.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 Universal Asynchronous Receiver/Transmitter (UART)
+
+maintainers:
+  - Min-Jen Chen <mjchen@nuvoton.com>
+  - Jacky Huang <ychuang3@nuvoton.com>
+
+allOf:
+  - $ref: "serial.yaml"
+
+properties:
+  compatible:
+    const: nuvoton,ma35d1-uart
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+    aliases {
+        serial0 = &uart0;
+    };
+
+    uart0:serial@40700000 {
+        compatible = "nuvoton,ma35d1-uart";
+        reg = <0x40700000 0x100>;
+        interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clk UART0_GATE>;
+    };
+...
-- 
2.34.1


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

* [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (9 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 10/15] dt-bindings: serial: Document ma35d1 uart " Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-16  7:45   ` Krzysztof Kozlowski
  2023-03-16 14:17   ` Arnd Bergmann
  2023-03-15  7:28 ` [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
                   ` (5 subsequent siblings)
  16 siblings, 2 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add initial device tree support for Nuvoton ma35d1 SoC, including
cpu, clock, reset, and serial controllers.
Add reference boards som-256m and iot-512m.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 arch/arm64/boot/dts/nuvoton/Makefile          |   2 +
 .../boot/dts/nuvoton/ma35d1-iot-512m.dts      |  24 ++
 .../boot/dts/nuvoton/ma35d1-som-256m.dts      |  23 ++
 arch/arm64/boot/dts/nuvoton/ma35d1.dtsi       | 272 ++++++++++++++++++
 4 files changed, 321 insertions(+)
 create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
 create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
 create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1.dtsi

diff --git a/arch/arm64/boot/dts/nuvoton/Makefile b/arch/arm64/boot/dts/nuvoton/Makefile
index a99dab90472a..c11ab4eac9c7 100644
--- a/arch/arm64/boot/dts/nuvoton/Makefile
+++ b/arch/arm64/boot/dts/nuvoton/Makefile
@@ -1,2 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 dtb-$(CONFIG_ARCH_NPCM) += nuvoton-npcm845-evb.dtb
+dtb-$(CONFIG_ARCH_NUVOTON) += ma35d1-iot-512m.dtb
+dtb-$(CONFIG_ARCH_NUVOTON) += ma35d1-som-256m.dtb
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
new file mode 100644
index 000000000000..dffcaef1e6d8
--- /dev/null
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Shan-Chun Hung <schung@nuvoton.com>
+ *         Jacky huang <ychuang3@nuvoton.com>
+ */
+
+/dts-v1/;
+#include "ma35d1.dtsi"
+
+/ {
+	model = "Nuvoton MA35D1-IoT";
+	compatible = "nuvoton,ma35d1-iot", "nuvoton,ma35d1";
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	mem: memory@80000000 {
+		device_type = "memory";
+		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
+	};
+};
+
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
new file mode 100644
index 000000000000..3e6c3d5469ac
--- /dev/null
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Shan-Chun Hung <schung@nuvoton.com>
+ *         Jacky huang <ychuang3@nuvoton.com>
+ */
+
+/dts-v1/;
+#include "ma35d1.dtsi"
+
+/ {
+	model = "Nuvoton MA35D1-SOM";
+	compatible = "nuvoton,ma35d1-som", "nuvoton,ma35d1";
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	mem: memory@80000000 {
+		device_type = "memory";
+		reg = <0x00000000 0x80000000 0 0x10000000>; /* 256M DRAM */
+	};
+};
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
new file mode 100644
index 000000000000..8c855f6b330a
--- /dev/null
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Shan-Chun Hung <schung@nuvoton.com>
+ *         Jacky huang <ychuang3@nuvoton.com>
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
+
+/ {
+	compatible = "nuvoton,ma35d1";
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+		serial3 = &uart3;
+		serial4 = &uart4;
+		serial5 = &uart5;
+		serial6 = &uart6;
+		serial7 = &uart7;
+		serial8 = &uart8;
+		serial9 = &uart9;
+		serial10 = &uart10;
+		serial11 = &uart11;
+		serial12 = &uart12;
+		serial13 = &uart13;
+		serial14 = &uart14;
+		serial15 = &uart15;
+		serial16 = &uart16;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	cpus {
+		#address-cells = <2>;
+		#size-cells = <0>;
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a35";
+			reg = <0x0 0x0>;
+			enable-method = "psci";
+			next-level-cache = <&L2_0>;
+		};
+		cpu1: cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a35";
+			reg = <0x0 0x1>;
+			enable-method = "psci";
+			next-level-cache = <&L2_0>;
+		};
+		L2_0: l2-cache0 {
+			compatible = "cache";
+			cache-level = <2>;
+		};
+	};
+
+	psci {
+		compatible = "arm,psci-0.2";
+		method = "smc";
+	};
+
+	clk_hxt: clock_hxt {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <24000000>;
+		clock-output-names = "clk_hxt";
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
+			      IRQ_TYPE_LEVEL_LOW)>, /* Physical Secure */
+			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) |
+			      IRQ_TYPE_LEVEL_LOW)>, /* Physical Non-Secure */
+			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
+			      IRQ_TYPE_LEVEL_LOW)>, /* Virtual */
+			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
+			      IRQ_TYPE_LEVEL_LOW)>; /* Hypervisor */
+		clock-frequency = <12000000>;
+		interrupt-parent = <&gic>;
+	};
+
+	sys: system-management@40460000 {
+		compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";
+		reg = <0x0 0x40460000 0x0 0x200>;
+
+		reset: reset-controller {
+			compatible = "nuvoton,ma35d1-reset";
+			regmap = <&sys>;
+			#reset-cells = <1>;
+		};
+	};
+
+	clk: clock-controller@40460200 {
+		compatible = "nuvoton,ma35d1-clk", "syscon";
+		reg = <0x00000000 0x40460200 0x0 0x100>;
+		#clock-cells = <1>;
+		clocks = <&clk_hxt>;
+		clock-names = "clk_hxt";
+		assigned-clocks = <&clk CAPLL>,
+				  <&clk DDRPLL>,
+				  <&clk APLL>,
+				  <&clk EPLL>,
+				  <&clk VPLL>;
+		assigned-clock-rates = <800000000>,
+				       <266000000>,
+				       <180000000>,
+				       <500000000>,
+				       <102000000>;
+		nuvoton,pll-mode = <0>, <1>, <0>, <0>, <0>;
+		nuvoton,sys = <&sys>;
+	};
+
+	gic: interrupt-controller@50801000 {
+		compatible = "arm,gic-400";
+		#interrupt-cells = <3>;
+		interrupt-parent = <&gic>;
+		interrupt-controller;
+		reg =   <0x0 0x50801000 0 0x1000>, /* GICD */
+			<0x0 0x50802000 0 0x2000>, /* GICC */
+			<0x0 0x50804000 0 0x2000>, /* GICH */
+			<0x0 0x50806000 0 0x2000>; /* GICV */
+		interrupts = <GIC_PPI 9 (GIC_CPU_MASK_RAW(0x13) |
+			      IRQ_TYPE_LEVEL_HIGH)>;
+	};
+
+	uart0:serial@40700000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40700000 0x0 0x100>;
+		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART0_GATE>;
+		status = "okay";
+	};
+
+	uart1:serial@40710000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40710000 0x0 0x100>;
+		interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART1_GATE>;
+		status = "disabled";
+	};
+
+	uart2:serial@40720000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40720000 0x0 0x100>;
+		interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART2_GATE>;
+		status = "disabled";
+	};
+
+	uart3:serial@40730000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40730000 0x0 0x100>;
+		interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART3_GATE>;
+		status = "disabled";
+	};
+
+	uart4:serial@40740000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40740000 0x0 0x100>;
+		interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART4_GATE>;
+		status = "disabled";
+	};
+
+	uart5:serial@40750000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40750000 0x0 0x100>;
+		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART5_GATE>;
+		status = "disabled";
+	};
+
+	uart6:serial@40760000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40760000 0x0 0x100>;
+		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART6_GATE>;
+		status = "disabled";
+	};
+
+	uart7:serial@40770000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40770000 0x0 0x100>;
+		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART7_GATE>;
+		status = "disabled";
+	};
+
+	uart8:serial@40780000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40780000 0x0 0x100>;
+		interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART8_GATE>;
+		status = "disabled";
+	};
+
+	uart9:serial@40790000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40790000 0x0 0x100>;
+		interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART9_GATE>;
+		status = "disabled";
+	};
+
+	uart10:serial@407a0000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x407a0000 0x0 0x100>;
+		interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART10_GATE>;
+		status = "disabled";
+	};
+
+	uart11:serial@407b0000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x407b0000 0x0 0x100>;
+		interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART11_GATE>;
+		status = "disabled";
+	};
+
+	uart12:serial@407c0000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x407c0000 0x0 0x100>;
+		interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART12_GATE>;
+		status = "disabled";
+	};
+
+	uart13:serial@407d0000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x407d0000 0x0 0x100>;
+		interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART13_GATE>;
+		status = "disabled";
+	};
+
+	uart14:serial@407e0000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x407e0000 0x0 0x100>;
+		interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART14_GATE>;
+		status = "disabled";
+	};
+
+	uart15:serial@407f0000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x407f0000 0x0 0x100>;
+		interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART15_GATE>;
+		status = "disabled";
+	};
+
+	uart16:serial@40880000 {
+		compatible = "nuvoton,ma35d1-uart";
+		reg = <0x0 0x40880000 0x0 0x100>;
+		interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&clk UART16_GATE>;
+		status = "disabled";
+	};
+};
-- 
2.34.1


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

* [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (10 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree Jacky Huang
@ 2023-03-15  7:28 ` Jacky Huang
  2023-03-15 22:07   ` kernel test robot
                     ` (3 more replies)
  2023-03-15  7:29 ` [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support Jacky Huang
                   ` (4 subsequent siblings)
  16 siblings, 4 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:28 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

The clock controller generates clocks for the whole chip, including
system clocks and all peripheral clocks. This driver support ma35d1
clock gating, divider, and individual PLL configuration.

There are 6 PLLs in ma35d1 SoC:
  - CA-PLL for the two Cortex-A35 CPU clock
  - SYS-PLL for system bus, which comes from the companion MCU
    and cannot be programmed by clock controller.
  - DDR-PLL for DDR
  - EPLL for GMAC and GFX, Display, and VDEC IPs.
  - VPLL for video output pixel clock
  - APLL for SDHC, I2S audio, and other IPs.
CA-PLL has only one operation mode.
DDR-PLL, EPLL, VPLL, and APLL are advanced PLLs which have 3
operation modes: integer mode, fraction mode, and spread specturm mode.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 drivers/clk/Makefile                     |   1 +
 drivers/clk/nuvoton/Makefile             |   4 +
 drivers/clk/nuvoton/clk-ma35d1-divider.c | 144 ++++
 drivers/clk/nuvoton/clk-ma35d1-pll.c     | 534 +++++++++++++
 drivers/clk/nuvoton/clk-ma35d1.c         | 970 +++++++++++++++++++++++
 drivers/clk/nuvoton/clk-ma35d1.h         | 198 +++++
 6 files changed, 1851 insertions(+)
 create mode 100644 drivers/clk/nuvoton/Makefile
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1-divider.c
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1-pll.c
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1.c
 create mode 100644 drivers/clk/nuvoton/clk-ma35d1.h

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e3ca0d058a25..2e7916d269e1 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -103,6 +103,7 @@ endif
 obj-y					+= mstar/
 obj-y					+= mvebu/
 obj-$(CONFIG_ARCH_MXS)			+= mxs/
+obj-$(CONFIG_ARCH_NUVOTON)		+= nuvoton/
 obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
 obj-$(CONFIG_COMMON_CLK_PISTACHIO)	+= pistachio/
 obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile
new file mode 100644
index 000000000000..d2c092541b8d
--- /dev/null
+++ b/drivers/clk/nuvoton/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1.o
+obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1-divider.o
+obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1-pll.o
diff --git a/drivers/clk/nuvoton/clk-ma35d1-divider.c b/drivers/clk/nuvoton/clk-ma35d1-divider.c
new file mode 100644
index 000000000000..5f4791531e47
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+
+#include "clk-ma35d1.h"
+
+#define div_mask(width)		((1 << (width)) - 1)
+
+struct ma35d1_adc_clk_divider {
+	struct clk_hw hw;
+	void __iomem *reg;
+	u8 shift;
+	u8 width;
+	u32 mask;
+	const struct clk_div_table *table;
+	spinlock_t *lock;
+};
+
+#define to_ma35d1_adc_clk_divider(_hw)	\
+	container_of(_hw, struct ma35d1_adc_clk_divider, hw)
+
+static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	unsigned int val;
+	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
+
+	val = readl_relaxed(dclk->reg) >> dclk->shift;
+	val &= div_mask(dclk->width);
+	val += 1;
+	return divider_recalc_rate(hw, parent_rate, val, dclk->table,
+				   CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
+}
+
+static long ma35d1_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *prate)
+{
+	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
+
+	return divider_round_rate(hw, rate, prate, dclk->table,
+				  dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
+}
+
+static int ma35d1_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	int value;
+	unsigned long flags = 0;
+	u32 data;
+	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
+
+	value = divider_get_val(rate, parent_rate, dclk->table,
+				dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
+
+	if (dclk->lock)
+		spin_lock_irqsave(dclk->lock, flags);
+
+	data = readl_relaxed(dclk->reg);
+	data &= ~(div_mask(dclk->width) << dclk->shift);
+	data |= (value - 1) << dclk->shift;
+	data |= dclk->mask;
+
+	writel_relaxed(data, dclk->reg);
+
+	if (dclk->lock)
+		spin_unlock_irqrestore(dclk->lock, flags);
+
+	return 0;
+}
+
+static const struct clk_ops ma35d1_adc_clkdiv_ops = {
+	.recalc_rate = ma35d1_clkdiv_recalc_rate,
+	.round_rate = ma35d1_clkdiv_round_rate,
+	.set_rate = ma35d1_clkdiv_set_rate,
+};
+
+struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
+				     const char *parent_name,
+				     unsigned long flags, void __iomem *reg,
+				     u8 shift, u8 width, u32 mask_bit)
+{
+	struct ma35d1_adc_clk_divider *div;
+	struct clk_init_data init;
+	struct clk_div_table *table;
+	u32 max_div, min_div;
+	struct clk_hw *hw;
+	int ret;
+	int i;
+
+	/* allocate the divider */
+	div = kzalloc(sizeof(*div), GFP_KERNEL);
+	if (!div)
+		return ERR_PTR(-ENOMEM);
+
+	/* Init the divider table */
+	max_div = div_mask(width) + 1;
+	min_div = 1;
+
+	table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
+	if (!table) {
+		kfree(div);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	for (i = 0; i < max_div; i++) {
+		table[i].val = (min_div + i);
+		table[i].div = 2 * table[i].val;
+	}
+	table[max_div].val = 0;
+	table[max_div].div = 0;
+
+	init.name = name;
+	init.ops = &ma35d1_adc_clkdiv_ops;
+	init.flags |= flags;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.num_parents = parent_name ? 1 : 0;
+
+	/* struct ma35d1_adc_clk_divider assignments */
+	div->reg = reg;
+	div->shift = shift;
+	div->width = width;
+	div->mask = mask_bit ? BIT(mask_bit) : 0;
+	div->lock = &ma35d1_lock;
+	div->hw.init = &init;
+	div->table = table;
+
+	/* Register the clock */
+	hw = &div->hw;
+	ret = clk_hw_register(NULL, hw);
+	if (ret) {
+		kfree(table);
+		kfree(div);
+		return ERR_PTR(ret);
+	}
+	return hw;
+}
diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
new file mode 100644
index 000000000000..79e724b148fa
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/bitfield.h>
+
+#include "clk-ma35d1.h"
+
+#define to_ma35d1_clk_pll(clk) \
+	(container_of(clk, struct ma35d1_clk_pll, clk))
+
+#define PLL0CTL0_FBDIV_MSK	GENMASK(7, 0)
+#define PLL0CTL0_INDIV_MSK	GENMASK(11, 8)
+#define PLL0CTL0_OUTDIV_MSK	GENMASK(13, 12)
+#define PLL0CTL0_PD_MSK		BIT(16)
+#define PLL0CTL0_BP_MSK		BIT(17)
+#define PLLXCTL0_FBDIV_MSK	GENMASK(10, 0)
+#define PLLXCTL0_INDIV_MSK	GENMASK(17, 12)
+#define PLLXCTL0_MODE_MSK	GENMASK(19, 18)
+#define PLLXCTL0_SSRATE_MSK	GENMASK(30, 20)
+#define PLLXCTL1_PD_MSK		BIT(0)
+#define PLLXCTL1_BP_MSK		BIT(1)
+#define PLLXCTL1_OUTDIV_MSK	GENMASK(6, 4)
+#define PLLXCTL1_FRAC_MSK	GENMASK(31, 8)
+#define PLLXCTL2_SLOPE_MSK	GENMASK(23, 0)
+
+struct ma35d1_clk_pll {
+	struct clk_hw hw;
+	u8 type;
+	u8 mode;
+	unsigned long rate;
+	void __iomem *ctl0_base;
+	void __iomem *ctl1_base;
+	void __iomem *ctl2_base;
+	struct regmap *regmap;
+};
+
+struct vsipll_freq_conf_reg_tbl {
+	unsigned long freq;
+	u8 mode;
+	u32 ctl0_reg;
+	u32 ctl1_reg;
+	u32 ctl2_reg;
+};
+
+static const struct vsipll_freq_conf_reg_tbl ma35d1pll_freq[] = {
+	{ 1000000000, VSIPLL_INTEGER_MODE, 0x307d, 0x10, 0 },
+	{ 884736000, VSIPLL_FRACTIONAL_MODE, 0x41024, 0xdd2f1b11, 0 },
+	{ 533000000, VSIPLL_SS_MODE, 0x12b8102c, 0x6aaaab20, 0x12317 },
+	{ }
+};
+
+static void CLK_UnLockReg(struct ma35d1_clk_pll *pll)
+{
+	int ret;
+
+	/* Unlock PLL registers */
+	do {
+		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x59);
+		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x16);
+		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x88);
+		regmap_read(pll->regmap, REG_SYS_RLKTZNS, &ret);
+	} while (ret == 0);
+}
+
+static void CLK_LockReg(struct ma35d1_clk_pll *pll)
+{
+	/* Lock PLL registers */
+	regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x0);
+}
+
+/* SMIC PLL for CAPLL */
+unsigned long CLK_GetPLLFreq_SMICPLL(struct ma35d1_clk_pll *pll,
+				     unsigned long PllSrcClk)
+{
+	u32 u32M, u32N, u32P, u32OutDiv;
+	u32 val;
+	unsigned long u64PllClk;
+	u32 clk_div_table[] = { 1, 2, 4, 8};
+
+	val = __raw_readl(pll->ctl0_base);
+
+	u32N = FIELD_GET(PLL0CTL0_FBDIV_MSK, val);
+	u32M = FIELD_GET(PLL0CTL0_INDIV_MSK, val);
+	u32P = FIELD_GET(PLL0CTL0_OUTDIV_MSK, val);
+	u32OutDiv = clk_div_table[u32P];
+
+	if (val & PLL0CTL0_BP_MSK) {
+		u64PllClk = PllSrcClk;
+	} else {
+		u64PllClk = PllSrcClk * u32N;
+		do_div(u64PllClk, u32M * u32OutDiv);
+	}
+	return u64PllClk;
+}
+
+/* VSI-PLL: INTEGER_MODE */
+unsigned long CLK_CalPLLFreq_Mode0(unsigned long PllSrcClk,
+				   unsigned long u64PllFreq, u32 *u32Reg)
+{
+	u32 u32TmpM, u32TmpN, u32TmpP;
+	u32 u32RngMinN, u32RngMinM, u32RngMinP;
+	u32 u32RngMaxN, u32RngMaxM, u32RngMaxP;
+	u32 u32Tmp, u32Min, u32MinN, u32MinM, u32MinP;
+	unsigned long u64PllClk;
+	unsigned long u64Con1, u64Con2, u64Con3;
+
+	u64PllClk = 0;
+	u32Min = (u32) -1;
+
+	if (!((u64PllFreq >= VSIPLL_FCLKO_MIN_FREQ) &&
+	    (u64PllFreq <= VSIPLL_FCLKO_MAX_FREQ))) {
+		u32Reg[0] = ma35d1pll_freq[0].ctl0_reg;
+		u32Reg[1] = ma35d1pll_freq[0].ctl1_reg;
+		u64PllClk = ma35d1pll_freq[0].freq;
+		return u64PllClk;
+	}
+
+	u32RngMinM = 1UL;
+	u32RngMaxM = 63UL;
+	u32RngMinM = ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) > 1) ?
+		     (PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) : 1;
+	u32RngMaxM = ((PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) < u32RngMaxM) ?
+		     (PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) : u32RngMaxM;
+
+	for (u32TmpM = u32RngMinM; u32TmpM < (u32RngMaxM + 1); u32TmpM++) {
+		u64Con1 = PllSrcClk / u32TmpM;
+		u32RngMinN = 16UL;
+		u32RngMaxN = 2047UL;
+		u32RngMinN = ((VSIPLL_FCLK_MIN_FREQ / u64Con1) > u32RngMinN) ?
+			     (VSIPLL_FCLK_MIN_FREQ / u64Con1) : u32RngMinN;
+		u32RngMaxN = ((VSIPLL_FCLK_MAX_FREQ / u64Con1) < u32RngMaxN) ?
+			     (VSIPLL_FCLK_MAX_FREQ / u64Con1) : u32RngMaxN;
+
+		for (u32TmpN = u32RngMinN; u32TmpN < (u32RngMaxN + 1);
+		     u32TmpN++) {
+			u64Con2 = u64Con1 * u32TmpN;
+			u32RngMinP = 1UL;
+			u32RngMaxP = 7UL;
+			u32RngMinP = ((u64Con2 / VSIPLL_FCLKO_MAX_FREQ) > 1) ?
+				      (u64Con2 / VSIPLL_FCLKO_MAX_FREQ) : 1;
+			u32RngMaxP = ((u64Con2 / VSIPLL_FCLKO_MIN_FREQ) <
+				      u32RngMaxP) ?
+				      (u64Con2 / VSIPLL_FCLKO_MIN_FREQ) :
+				      u32RngMaxP;
+			for (u32TmpP = u32RngMinP; u32TmpP < (u32RngMaxP + 1);
+			     u32TmpP++) {
+				u64Con3 = u64Con2 / u32TmpP;
+				if (u64Con3 > u64PllFreq)
+					u32Tmp = u64Con3 - u64PllFreq;
+				else
+					u32Tmp = u64PllFreq - u64Con3;
+
+				if (u32Tmp < u32Min) {
+					u32Min = u32Tmp;
+					u32MinM = u32TmpM;
+					u32MinN = u32TmpN;
+					u32MinP = u32TmpP;
+
+					if (u32Min == 0UL) {
+						u32Reg[0] = (u32MinM << 12) |
+							    (u32MinN);
+						u32Reg[1] = (u32MinP << 4);
+						return ((PllSrcClk * u32MinN) /
+							(u32MinP * u32MinM));
+					}
+				}
+			}
+		}
+	}
+
+	u32Reg[0] = (u32MinM << 12) | (u32MinN);
+	u32Reg[1] = (u32MinP << 4);
+	u64PllClk = (PllSrcClk * u32MinN) / (u32MinP * u32MinM);
+	return u64PllClk;
+}
+
+/* VSI-PLL: FRACTIONAL_MODE */
+unsigned long CLK_CalPLLFreq_Mode1(unsigned long PllSrcClk,
+				   unsigned long u64PllFreq, u32 *u32Reg)
+{
+	unsigned long u64X, u64N, u64M, u64P, u64tmp;
+	unsigned long u64PllClk, u64FCLKO;
+	u32 u32FRAC;
+
+	if (u64PllFreq > VSIPLL_FCLKO_MAX_FREQ) {
+		u32Reg[0] = ma35d1pll_freq[1].ctl0_reg;
+		u32Reg[1] = ma35d1pll_freq[1].ctl1_reg;
+		u64PllClk = ma35d1pll_freq[1].freq;
+		return u64PllClk;
+	}
+
+	if (u64PllFreq > (VSIPLL_FCLKO_MIN_FREQ/(100-1))) {
+		u64FCLKO = u64PllFreq * ((VSIPLL_FCLKO_MIN_FREQ / u64PllFreq) +
+			   ((VSIPLL_FCLKO_MIN_FREQ % u64PllFreq) ? 1 : 0));
+	} else {
+		pr_err("Failed to set rate %ld\n", u64PllFreq);
+		return 0;
+	}
+
+	u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
+	       ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
+		((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));
+
+	if ((PllSrcClk > (VSIPLL_FREFDIVM_MAX_FREQ * (64-1))) ||
+	    (PllSrcClk < VSIPLL_FREFDIVM_MIN_FREQ1))
+		return 0;
+
+	u64M = (PllSrcClk <= VSIPLL_FREFDIVM_MAX_FREQ) ? 1 :
+	       ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) +
+	       ((PllSrcClk % VSIPLL_FREFDIVM_MAX_FREQ) ? 1 : 0));
+
+	u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
+	u64N = u64tmp / 1000;
+	u64X = u64tmp % 1000;
+	u32FRAC = ((u64X << 24) + 500) / 1000;
+	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
+
+	u32Reg[0] = (u64M << 12) | (u64N);
+	u32Reg[1] = (u64P << 4) | (u32FRAC << 8);
+	return u64PllClk;
+}
+
+/* VSI-PLL: SS_MODE */
+unsigned long CLK_CalPLLFreq_Mode2(unsigned long PllSrcClk,
+				   unsigned long u64PllFreq,
+				   u32 u32SR, u32 u32Fmod, u32 *u32Reg)
+{
+	unsigned long u64X, u64N, u64M, u64P, u64tmp, u64tmpP, u64tmpM;
+	unsigned long u64SSRATE, u64SLOPE, u64PllClk, u64FCLKO;
+	u32 u32FRAC, i;
+
+	if (u64PllFreq >= VSIPLL_FCLKO_MAX_FREQ) {
+		u32Reg[0] = ma35d1pll_freq[2].ctl0_reg;
+		u32Reg[1] = ma35d1pll_freq[2].ctl1_reg;
+		u32Reg[2] = ma35d1pll_freq[2].ctl2_reg;
+		u64PllClk = ma35d1pll_freq[2].freq;
+		return u64PllClk;
+	}
+
+	if (u64PllFreq < VSIPLL_FCLKO_MIN_FREQ) {
+		u64FCLKO = 0;
+		for (i = 2; i < 8; i++) {
+			u64tmp = (i * u64PllFreq);
+			if (u64tmp > VSIPLL_FCLKO_MIN_FREQ)
+				u64FCLKO = u64tmp;
+		}
+		if (u64FCLKO == 0) {
+			pr_err("Failed to set rate %ld\n", u64PllFreq);
+			return 0;
+		}
+
+	} else
+		u64FCLKO = u64PllFreq;
+
+	u64P = 0;
+	for (i = 1; i < 8; i++) {
+		u64tmpP = i * u64FCLKO;
+		if ((u64tmpP <= VSIPLL_FCLK_MAX_FREQ) &&
+		    (u64tmpP >= VSIPLL_FCLK_MIN_FREQ)) {
+			u64P = i;
+			break;
+		}
+	}
+
+	if (u64P == 0)
+		return 0;
+
+	u64M = 0;
+	for (i = 1; i < 64; i++) {
+		u64tmpM = PllSrcClk / i;
+		if ((u64tmpM <= VSIPLL_FREFDIVM_MAX_FREQ) &&
+		    (u64tmpM >= VSIPLL_FREFDIVM_MIN_FREQ1)) {
+			u64M = i;
+			break;
+		}
+	}
+
+	if (u64M == 0)
+		return 0;
+
+	u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
+	u64N = u64tmp / 1000;
+	u64X = u64tmp % 1000;
+	u32FRAC = ((u64X << 24) + 500) / 1000;
+
+	u64SSRATE = ((PllSrcClk >> 1) / (u32Fmod * 2)) - 1;
+	u64SLOPE = ((u64tmp * u32SR / u64SSRATE) << 24) / 100 / 1000;
+
+	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
+
+	u32Reg[0] = (u64SSRATE << VSIPLLCTL0_SSRATE_POS) | (u64M <<
+		     VSIPLLCTL0_INDIV_POS) | (u64N);
+	u32Reg[1] = (u64P << VSIPLLCTL1_OUTDIV_POS) | (u32FRAC << VSIPLLCTL1_FRAC_POS);
+	u32Reg[2] = u64SLOPE;
+	return u64PllClk;
+}
+
+unsigned long CLK_SetPLLFreq(struct ma35d1_clk_pll *pll,
+			     unsigned long PllSrcClk,
+			     unsigned long u64PllFreq)
+{
+	u32 u32Reg[3] = { 0 }, val_ctl0, val_ctl1, val_ctl2;
+	unsigned long u64PllClk;
+
+	val_ctl0 = __raw_readl(pll->ctl0_base);
+	val_ctl1 = __raw_readl(pll->ctl1_base);
+	val_ctl2 = __raw_readl(pll->ctl2_base);
+
+	switch (pll->mode) {
+	case VSIPLL_INTEGER_MODE:
+		u64PllClk = CLK_CalPLLFreq_Mode0(PllSrcClk, u64PllFreq,
+						 u32Reg);
+		val_ctl0 = u32Reg[0] |
+			   (VSIPLL_INTEGER_MODE << VSIPLLCTL0_MODE_POS);
+		break;
+	case VSIPLL_FRACTIONAL_MODE:
+		u64PllClk = CLK_CalPLLFreq_Mode1(PllSrcClk, u64PllFreq,
+						 u32Reg);
+		val_ctl0 = u32Reg[0] |
+			   (VSIPLL_FRACTIONAL_MODE << VSIPLLCTL0_MODE_POS);
+		break;
+	case VSIPLL_SS_MODE:
+		u64PllClk = CLK_CalPLLFreq_Mode2(PllSrcClk, u64PllFreq,
+						 VSIPLL_MODULATION_FREQ,
+						 VSIPLL_SPREAD_RANGE, u32Reg);
+		val_ctl0 = u32Reg[0] |
+			   (VSIPLL_SS_MODE << VSIPLLCTL0_MODE_POS);
+		break;
+	}
+
+	val_ctl1 = VSIPLLCTL1_PD_MSK | u32Reg[1];
+	val_ctl2 = u32Reg[2];
+
+	__raw_writel(val_ctl0, pll->ctl0_base);
+	__raw_writel(val_ctl1, pll->ctl1_base);
+	__raw_writel(val_ctl2, pll->ctl2_base);
+	return u64PllClk;
+}
+
+unsigned long CLK_GetPLLFreq_VSIPLL(struct ma35d1_clk_pll *pll,
+				    unsigned long PllSrcClk)
+{
+	u32 u32M, u32N, u32P, u32X, u32SR, u32FMOD;
+	u32 val_ctl0, val_ctl1, val_ctl2;
+	unsigned long u64PllClk, u64X;
+
+	val_ctl0 = __raw_readl(pll->ctl0_base);
+	val_ctl1 = __raw_readl(pll->ctl1_base);
+	val_ctl2 = __raw_readl(pll->ctl2_base);
+
+	if (val_ctl1 & PLLXCTL1_BP_MSK) {
+		u64PllClk = PllSrcClk;
+		return u64PllClk;
+	}
+
+	if (pll->mode == VSIPLL_INTEGER_MODE) {
+		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
+		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
+		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
+
+		u64PllClk = PllSrcClk * u32N;
+		do_div(u64PllClk, u32M * u32P);
+
+	} else if (pll->mode == VSIPLL_FRACTIONAL_MODE) {
+		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
+		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
+		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
+		u32X = FIELD_GET(PLLXCTL1_FRAC_MSK, val_ctl1);
+		u64X = (u64) u32X;
+		u64X = (((u64X * 1000) + 500) >> 24);
+		u64PllClk = (PllSrcClk * ((u32N * 1000) + u64X)) /
+			    1000 / u32P / u32M;
+
+	} else {
+		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
+		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
+		u32SR = FIELD_GET(PLLXCTL0_SSRATE_MSK, val_ctl0);
+		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
+		u32X = FIELD_GET(PLLXCTL1_FRAC_MSK, val_ctl1);
+		u32FMOD = FIELD_GET(PLLXCTL2_SLOPE_MSK, val_ctl2);
+		u64X = (u64) u32X;
+		u64X = ((u64X * 1000) >> 24);
+		u64PllClk = (PllSrcClk * ((u32N * 1000) + u64X)) /
+			    1000 / u32P / u32M;
+	}
+	return u64PllClk;
+}
+
+static int ma35d1_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+
+	if ((parent_rate < VSIPLL_FREF_MIN_FREQ) ||
+	    (parent_rate > VSIPLL_FREF_MAX_FREQ))
+		return 0;
+
+	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
+		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
+		return -EACCES;
+	}
+	CLK_UnLockReg(pll);
+	pll->rate = CLK_SetPLLFreq(pll, parent_rate, rate);
+	CLK_LockReg(pll);
+	return 0;
+}
+
+static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	unsigned long pllfreq;
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+
+	if ((parent_rate < VSIPLL_FREF_MIN_FREQ)
+	    || (parent_rate > VSIPLL_FREF_MAX_FREQ))
+		return 0;
+
+	switch (pll->type) {
+	case MA35D1_CAPLL:
+		pllfreq = CLK_GetPLLFreq_SMICPLL(pll, parent_rate);
+		break;
+	case MA35D1_DDRPLL:
+	case MA35D1_APLL:
+	case MA35D1_EPLL:
+	case MA35D1_VPLL:
+		pllfreq = CLK_GetPLLFreq_VSIPLL(pll, parent_rate);
+		break;
+	}
+
+	return pllfreq;
+}
+
+static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long *prate)
+{
+	return rate;
+}
+
+static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 val = __raw_readl(pll->ctl1_base);
+
+	return (val & VSIPLLCTL1_PD_MSK) ? 0 : 1;
+}
+
+static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 val;
+
+	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
+		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
+		return -EACCES;
+	}
+
+	CLK_UnLockReg(pll);
+	val = __raw_readl(pll->ctl1_base);
+	val &= ~VSIPLLCTL1_PD_MSK;
+	__raw_writel(val, pll->ctl1_base);
+	CLK_LockReg(pll);
+	return 0;
+}
+
+static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 val;
+
+	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
+		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
+	} else {
+		val = __raw_readl(pll->ctl1_base);
+		val |= VSIPLLCTL1_PD_MSK;
+		__raw_writel(val, pll->ctl1_base);
+	}
+}
+
+static const struct clk_ops ma35d1_clk_pll_ops = {
+	.is_prepared = ma35d1_clk_pll_is_prepared,
+	.prepare = ma35d1_clk_pll_prepare,
+	.unprepare = ma35d1_clk_pll_unprepare,
+	.set_rate = ma35d1_clk_pll_set_rate,
+	.recalc_rate = ma35d1_clk_pll_recalc_rate,
+	.round_rate = ma35d1_clk_pll_round_rate,
+};
+
+struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type,
+				  u8 u8mode, const char *name,
+				  const char *parent,
+				  unsigned long targetFreq,
+				  void __iomem *base,
+				  struct regmap *regmap)
+{
+	struct ma35d1_clk_pll *pll;
+	struct clk_hw *hw;
+	struct clk_init_data init;
+	int ret;
+
+	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->type = type;
+	pll->mode = u8mode;
+	pll->rate = targetFreq;
+	pll->ctl0_base = base + VSIPLL_CTL0;
+	pll->ctl1_base = base + VSIPLL_CTL1;
+	pll->ctl2_base = base + VSIPLL_CTL2;
+	pll->regmap = regmap;
+
+	init.name = name;
+	init.flags = 0;
+	init.parent_names = &parent;
+	init.num_parents = 1;
+	init.ops = &ma35d1_clk_pll_ops;
+	pll->hw.init = &init;
+	hw = &pll->hw;
+
+	ret = clk_hw_register(NULL, hw);
+	if (ret) {
+		pr_err("failed to register vsi-pll clock!!!\n");
+		kfree(pll);
+		return ERR_PTR(ret);
+	}
+	return hw;
+}
diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
new file mode 100644
index 000000000000..ac8154458b81
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1.c
@@ -0,0 +1,970 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+#include "clk-ma35d1.h"
+
+DEFINE_SPINLOCK(ma35d1_lock);
+
+static const char *const ca35clk_sel_clks[] = {
+	"hxt", "capll", "ddrpll", "dummy"
+};
+
+static const char *const sysclk0_sel_clks[] = {
+	"epll_div2", "syspll"
+};
+
+static const char *const sysclk1_sel_clks[] = {
+	"hxt", "syspll"
+};
+
+static const char *const axiclk_sel_clks[] = {
+	"capll_div2", "capll_div4"
+};
+
+static const char *const ccap_sel_clks[] = {
+	"hxt", "vpll", "apll", "syspll"
+};
+
+static const char *const sdh_sel_clks[] = {
+	"syspll", "apll", "dummy", "dummy"
+};
+
+static const char *const dcu_sel_clks[] = {
+	"epll_div2", "syspll"
+};
+
+static const char *const gfx_sel_clks[] = {
+	"epll", "syspll"
+};
+
+static const char *const dbg_sel_clks[] = {
+	"hirc", "syspll"
+};
+
+static const char *const timer0_sel_clks[] = {
+	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer1_sel_clks[] = {
+	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer2_sel_clks[] = {
+	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer3_sel_clks[] = {
+	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer4_sel_clks[] = {
+	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer5_sel_clks[] = {
+	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer6_sel_clks[] = {
+	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer7_sel_clks[] = {
+	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer8_sel_clks[] = {
+	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer9_sel_clks[] = {
+	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer10_sel_clks[] = {
+	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const timer11_sel_clks[] = {
+	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
+};
+
+static const char *const uart_sel_clks[] = {
+	"hxt", "sysclk1_div2", "dummy", "dummy"
+};
+
+static const char *const wdt0_sel_clks[] = {
+	"dummy", "lxt", "pclk3_div4096", "lirc"
+};
+
+static const char *const wdt1_sel_clks[] = {
+	"dummy", "lxt", "pclk3_div4096", "lirc"
+};
+
+static const char *const wdt2_sel_clks[] = {
+	"dummy", "lxt", "pclk4_div4096", "lirc"
+};
+
+static const char *const wwdt0_sel_clks[] = {
+	"dummy", "dummy", "pclk3_div4096", "lirc"
+};
+
+static const char *const wwdt1_sel_clks[] = {
+	"dummy", "dummy", "pclk3_div4096", "lirc"
+};
+
+static const char *const wwdt2_sel_clks[] = {
+	"dummy", "dummy", "pclk4_div4096", "lirc"
+};
+
+static const char *const spi0_sel_clks[] = {
+	"pclk1", "apll", "dummy", "dummy"
+};
+
+static const char *const spi1_sel_clks[] = {
+	"pclk2", "apll", "dummy", "dummy"
+};
+
+static const char *const spi2_sel_clks[] = {
+	"pclk1", "apll", "dummy", "dummy"
+};
+
+static const char *const spi3_sel_clks[] = {
+	"pclk2", "apll", "dummy", "dummy"
+};
+
+static const char *const qspi0_sel_clks[] = {
+	"pclk0", "apll", "dummy", "dummy"
+};
+
+static const char *const qspi1_sel_clks[] = {
+	"pclk0", "apll", "dummy", "dummy"
+};
+
+static const char *const i2s0_sel_clks[] = {
+	"apll", "sysclk1_div2", "dummy", "dummy"
+};
+
+static const char *const i2s1_sel_clks[] = {
+	"apll", "sysclk1_div2", "dummy", "dummy"
+};
+
+static const char *const can_sel_clks[] = {
+	"apll", "vpll"
+};
+
+static const char *const cko_sel_clks[] = {
+	"hxt", "lxt", "hirc", "lirc", "capll_div4", "syspll",
+	"ddrpll", "epll_div2", "apll", "vpll", "dummy", "dummy",
+	"dummy", "dummy", "dummy", "dummy"
+};
+
+static const char *const smc_sel_clks[] = {
+	"hxt", "pclk4"
+};
+
+static const char *const kpi_sel_clks[] = {
+	"hxt", "lxt"
+};
+
+static const struct clk_div_table ip_div_table[] = {
+	{0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
+	{5, 12}, {6, 14}, {7, 16}, {0, 0},
+};
+
+static const struct clk_div_table eadc_div_table[] = {
+	{0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
+	{5, 12}, {6, 14}, {7, 16}, {8, 18},
+	{9, 20}, {10, 22}, {11, 24}, {12, 26},
+	{13, 28}, {14, 30}, {15, 32}, {0, 0},
+};
+
+static struct clk_hw **hws;
+static struct clk_hw_onecell_data *ma35d1_hw_data;
+
+static int ma35d1_clocks_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+	struct device_node *clk_node = dev->of_node;
+	void __iomem *clk_base;
+	struct regmap *regmap;
+	u32 pllmode[5] = { 0, 0, 0, 0, 0 };
+	u32 pllfreq[5] = { 0, 0, 0, 0, 0 };
+
+	dev_info(&pdev->dev, "Nuvoton MA35D1 Clock Driver\n");
+	ma35d1_hw_data = devm_kzalloc(&pdev->dev, struct_size(ma35d1_hw_data,
+				      hws, CLK_MAX_IDX), GFP_KERNEL);
+
+	if (WARN_ON(!ma35d1_hw_data))
+		return -ENOMEM;
+
+	ma35d1_hw_data->num = CLK_MAX_IDX;
+	hws = ma35d1_hw_data->hws;
+
+	clk_node = of_find_compatible_node(NULL, NULL, "nuvoton,ma35d1-clk");
+	clk_base = of_iomap(clk_node, 0);
+	of_node_put(clk_node);
+	if (!clk_base) {
+		pr_err("%s: could not map region\n", __func__);
+		return -ENOMEM;
+	}
+	regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+						 "nuvoton,sys");
+	if (IS_ERR(regmap))
+		pr_warn("%s: Unable to get syscon\n", __func__);
+
+	/* clock sources */
+	hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
+	hws[HXT_GATE] = ma35d1_clk_gate("hxt_gate", "hxt",
+					clk_base + REG_CLK_PWRCTL, 0);
+	hws[LXT] = ma35d1_clk_fixed("lxt", 32768);
+	hws[LXT_GATE] = ma35d1_clk_gate("lxt_gate", "lxt",
+					clk_base + REG_CLK_PWRCTL, 1);
+	hws[HIRC] = ma35d1_clk_fixed("hirc", 12000000);
+	hws[HIRC_GATE] = ma35d1_clk_gate("hirc_gate", "hirc",
+					 clk_base + REG_CLK_PWRCTL, 2);
+	hws[LIRC] = ma35d1_clk_fixed("lirc", 32000);
+	hws[LIRC_GATE] = ma35d1_clk_gate("lirc_gate", "lirc",
+					 clk_base + REG_CLK_PWRCTL, 3);
+
+	/* PLL */
+	of_property_read_u32_array(clk_node, "clock-pll-mode", pllmode,
+				   ARRAY_SIZE(pllmode));
+	of_property_read_u32_array(clk_node, "assigned-clock-rates", pllfreq,
+				   ARRAY_SIZE(pllfreq));
+
+	/* SMIC PLL */
+	hws[CAPLL] = ma35d1_reg_clk_pll(MA35D1_CAPLL, pllmode[0], "capll",
+					"hxt", pllfreq[0],
+					clk_base + REG_CLK_PLL0CTL0, regmap);
+	hws[SYSPLL] = ma35d1_clk_fixed("syspll", 180000000);
+
+	/* VSI PLL */
+	hws[DDRPLL] = ma35d1_reg_clk_pll(MA35D1_DDRPLL, pllmode[1], "ddrpll",
+					 "hxt", pllfreq[1],
+					 clk_base + REG_CLK_PLL2CTL0, regmap);
+	hws[APLL] = ma35d1_reg_clk_pll(MA35D1_APLL, pllmode[2], "apll", "hxt",
+				       pllfreq[2], clk_base + REG_CLK_PLL3CTL0,
+				       regmap);
+	hws[EPLL] = ma35d1_reg_clk_pll(MA35D1_EPLL, pllmode[3], "epll", "hxt",
+				       pllfreq[3], clk_base + REG_CLK_PLL4CTL0,
+				       regmap);
+	hws[VPLL] = ma35d1_reg_clk_pll(MA35D1_VPLL, pllmode[4], "vpll", "hxt",
+				       pllfreq[4], clk_base + REG_CLK_PLL5CTL0,
+				       regmap);
+	hws[EPLL_DIV2] = ma35d1_clk_fixed_factor("epll_div2", "epll", 1, 2);
+	hws[EPLL_DIV4] = ma35d1_clk_fixed_factor("epll_div4", "epll", 1, 4);
+	hws[EPLL_DIV8] = ma35d1_clk_fixed_factor("epll_div8", "epll", 1, 8);
+
+	/* CA35 */
+	hws[CA35CLK_MUX] = ma35d1_clk_mux("ca35clk_mux",
+					  clk_base + REG_CLK_CLKSEL0, 0,
+					  2, ca35clk_sel_clks,
+					  ARRAY_SIZE(ca35clk_sel_clks));
+
+	/* AXI */
+	hws[AXICLK_DIV2] = ma35d1_clk_fixed_factor("capll_div2", "ca35clk_mux",
+						   1, 2);
+	hws[AXICLK_DIV4] = ma35d1_clk_fixed_factor("capll_div4", "ca35clk_mux",
+						   1, 4);
+	hws[AXICLK_MUX] = ma35d1_clk_mux("axiclk_mux",
+					 clk_base + REG_CLK_CLKDIV0,
+					 26, 1, axiclk_sel_clks,
+					 ARRAY_SIZE(axiclk_sel_clks));
+
+	/* SYSCLK0 & SYSCLK1 */
+	hws[SYSCLK0_MUX] = ma35d1_clk_mux("sysclk0_mux",
+					  clk_base + REG_CLK_CLKSEL0,
+					  2, 1, sysclk0_sel_clks,
+					  ARRAY_SIZE(sysclk0_sel_clks));
+	hws[SYSCLK1_MUX] = ma35d1_clk_mux("sysclk1_mux",
+					  clk_base + REG_CLK_CLKSEL0,
+					  4, 1, sysclk1_sel_clks,
+					  ARRAY_SIZE(sysclk1_sel_clks));
+	hws[SYSCLK1_DIV2] = ma35d1_clk_fixed_factor("sysclk1_div2",
+						    "sysclk1_mux", 1, 2);
+
+	/* HCLK0~3 & PCLK0~4 */
+	hws[HCLK0] = ma35d1_clk_fixed_factor("hclk0", "sysclk1_mux", 1, 1);
+	hws[HCLK1] = ma35d1_clk_fixed_factor("hclk1", "sysclk1_mux", 1, 1);
+	hws[HCLK2] = ma35d1_clk_fixed_factor("hclk2", "sysclk1_mux", 1, 1);
+	hws[PCLK0] = ma35d1_clk_fixed_factor("pclk0", "sysclk1_mux", 1, 1);
+	hws[PCLK1] = ma35d1_clk_fixed_factor("pclk1", "sysclk1_mux", 1, 1);
+	hws[PCLK2] = ma35d1_clk_fixed_factor("pclk2", "sysclk1_mux", 1, 1);
+
+	hws[HCLK3] = ma35d1_clk_fixed_factor("hclk3", "sysclk1_mux", 1, 2);
+	hws[PCLK3] = ma35d1_clk_fixed_factor("pclk3", "sysclk1_mux", 1, 2);
+	hws[PCLK4] = ma35d1_clk_fixed_factor("pclk4", "sysclk1_mux", 1, 2);
+
+	hws[USBPHY0] = ma35d1_clk_fixed("usbphy0", 480000000);
+	hws[USBPHY1] = ma35d1_clk_fixed("usbphy1", 480000000);
+
+	/* DDR */
+	hws[DDR0_GATE] = ma35d1_clk_gate("ddr0_gate", "ddrpll",
+					 clk_base + REG_CLK_SYSCLK0, 4);
+	hws[DDR6_GATE] = ma35d1_clk_gate("ddr6_gate", "ddrpll",
+					 clk_base + REG_CLK_SYSCLK0, 5);
+
+	/* CAN0 */
+	hws[CAN0_MUX] = ma35d1_clk_mux("can0_mux", clk_base + REG_CLK_CLKSEL4,
+				       16, 1, can_sel_clks,
+				       ARRAY_SIZE(can_sel_clks));
+	hws[CAN0_DIV] = ma35d1_clk_divider_table("can0_div", "can0_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 0, 3, ip_div_table);
+	hws[CAN0_GATE] = ma35d1_clk_gate("can0_gate", "can0_div",
+					 clk_base + REG_CLK_SYSCLK0, 8);
+
+	/* CAN1 */
+	hws[CAN1_MUX] = ma35d1_clk_mux("can1_mux", clk_base + REG_CLK_CLKSEL4,
+				       17, 1, can_sel_clks,
+				       ARRAY_SIZE(can_sel_clks));
+	hws[CAN1_DIV] = ma35d1_clk_divider_table("can1_div", "can1_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 4, 3, ip_div_table);
+	hws[CAN1_GATE] = ma35d1_clk_gate("can1_gate", "can1_div",
+					 clk_base + REG_CLK_SYSCLK0, 9);
+
+	/* CAN2 */
+	hws[CAN2_MUX] = ma35d1_clk_mux("can2_mux", clk_base + REG_CLK_CLKSEL4,
+				       18, 1, can_sel_clks,
+				       ARRAY_SIZE(can_sel_clks));
+	hws[CAN2_DIV] = ma35d1_clk_divider_table("can2_div", "can2_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 8, 3, ip_div_table);
+	hws[CAN2_GATE] = ma35d1_clk_gate("can2_gate", "can2_div",
+					 clk_base + REG_CLK_SYSCLK0, 10);
+
+	/* CAN3 */
+	hws[CAN3_MUX] = ma35d1_clk_mux("can3_mux", clk_base + REG_CLK_CLKSEL4,
+				       19, 1, can_sel_clks,
+				       ARRAY_SIZE(can_sel_clks));
+	hws[CAN3_DIV] = ma35d1_clk_divider_table("can3_div", "can3_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 12, 3, ip_div_table);
+	hws[CAN3_GATE] = ma35d1_clk_gate("can3_gate", "can3_div",
+					 clk_base + REG_CLK_SYSCLK0, 11);
+
+	/* SDH0 */
+	hws[SDH0_MUX] = ma35d1_clk_mux("sdh0_mux", clk_base + REG_CLK_CLKSEL0,
+				       16, 2, sdh_sel_clks,
+				       ARRAY_SIZE(sdh_sel_clks));
+	hws[SDH0_GATE] = ma35d1_clk_gate("sdh0_gate", "sdh0_mux",
+					 clk_base + REG_CLK_SYSCLK0, 16);
+
+	/* SDH1 */
+	hws[SDH1_MUX] = ma35d1_clk_mux("sdh1_mux", clk_base + REG_CLK_CLKSEL0,
+				       18, 2, sdh_sel_clks,
+				       ARRAY_SIZE(sdh_sel_clks));
+	hws[SDH1_GATE] = ma35d1_clk_gate("sdh1_gate", "sdh1_mux",
+					 clk_base + REG_CLK_SYSCLK0, 17);
+
+	/* NAND */
+	hws[NAND_GATE] = ma35d1_clk_gate("nand_gate", "hclk1",
+					 clk_base + REG_CLK_SYSCLK0, 18);
+
+	/* USB */
+	hws[USBD_GATE] = ma35d1_clk_gate("usbd_gate", "usbphy0",
+					 clk_base + REG_CLK_SYSCLK0, 19);
+	hws[USBH_GATE] = ma35d1_clk_gate("usbh_gate", "usbphy0",
+					 clk_base + REG_CLK_SYSCLK0, 20);
+	hws[HUSBH0_GATE] = ma35d1_clk_gate("husbh0_gate", "usbphy0",
+					   clk_base + REG_CLK_SYSCLK0, 21);
+	hws[HUSBH1_GATE] = ma35d1_clk_gate("husbh1_gate", "usbphy0",
+					   clk_base + REG_CLK_SYSCLK0, 22);
+
+	/* GFX */
+	hws[GFX_MUX] = ma35d1_clk_mux("gfx_mux", clk_base + REG_CLK_CLKSEL0,
+				      26, 1, gfx_sel_clks,
+				      ARRAY_SIZE(gfx_sel_clks));
+	hws[GFX_GATE] = ma35d1_clk_gate("gfx_gate", "gfx_mux",
+					clk_base + REG_CLK_SYSCLK0, 24);
+
+	/* VC8K */
+	hws[VC8K_GATE] = ma35d1_clk_gate("vc8k_gate", "sysclk0_mux",
+					 clk_base + REG_CLK_SYSCLK0, 25);
+
+	/* DCU */
+	hws[DCU_MUX] = ma35d1_clk_mux("dcu_mux", clk_base + REG_CLK_CLKSEL0,
+				      24, 1, dcu_sel_clks,
+				      ARRAY_SIZE(dcu_sel_clks));
+	hws[DCU_GATE] = ma35d1_clk_gate("dcu_gate", "dcu_mux",
+					clk_base + REG_CLK_SYSCLK0, 26);
+
+	/* DCUP */
+	hws[DCUP_DIV] = ma35d1_clk_divider_table("dcup_div", "vpll",
+						 clk_base + REG_CLK_CLKDIV0,
+						 16, 3, ip_div_table);
+
+	/* EMAC0 */
+	hws[EMAC0_GATE] = ma35d1_clk_gate("emac0_gate", "epll_div2",
+					  clk_base + REG_CLK_SYSCLK0, 27);
+
+	/* EMAC1 */
+	hws[EMAC1_GATE] = ma35d1_clk_gate("emac1_gate", "epll_div2",
+					  clk_base + REG_CLK_SYSCLK0, 28);
+
+	/* CCAP0 */
+	hws[CCAP0_MUX] = ma35d1_clk_mux("ccap0_mux",
+					clk_base + REG_CLK_CLKSEL0,
+					12, 1, ccap_sel_clks,
+					ARRAY_SIZE(ccap_sel_clks));
+	hws[CCAP0_DIV] = ma35d1_clk_divider("ccap0_div", "ccap0_mux",
+					    clk_base + REG_CLK_CLKDIV1, 8, 4);
+	hws[CCAP0_GATE] = ma35d1_clk_gate("ccap0_gate", "ccap0_div",
+					  clk_base + REG_CLK_SYSCLK0, 29);
+
+	/* CCAP1 */
+	hws[CCAP1_MUX] = ma35d1_clk_mux("ccap1_mux",
+					clk_base + REG_CLK_CLKSEL0,
+					14, 1, ccap_sel_clks,
+					ARRAY_SIZE(ccap_sel_clks));
+	hws[CCAP1_DIV] = ma35d1_clk_divider("ccap1_div", "ccap1_mux",
+					    clk_base + REG_CLK_CLKDIV1,
+					    12, 4);
+	hws[CCAP1_GATE] = ma35d1_clk_gate("ccap1_gate", "ccap1_div",
+					  clk_base + REG_CLK_SYSCLK0, 30);
+
+	/* PDMA0~3 */
+	hws[PDMA0_GATE] = ma35d1_clk_gate("pdma0_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 0);
+	hws[PDMA1_GATE] = ma35d1_clk_gate("pdma1_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 1);
+	hws[PDMA2_GATE] = ma35d1_clk_gate("pdma2_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 2);
+	hws[PDMA3_GATE] = ma35d1_clk_gate("pdma3_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 3);
+
+	/* WH0~1 */
+	hws[WH0_GATE] = ma35d1_clk_gate("wh0_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 4);
+	hws[WH1_GATE] = ma35d1_clk_gate("wh1_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 5);
+
+	/* HWS */
+	hws[HWS_GATE] = ma35d1_clk_gate("hws_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 6);
+
+	/* EBI */
+	hws[EBI_GATE] = ma35d1_clk_gate("ebi_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 7);
+
+	/* SRAM0~1 */
+	hws[SRAM0_GATE] = ma35d1_clk_gate("sram0_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 8);
+	hws[SRAM1_GATE] = ma35d1_clk_gate("sram1_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 9);
+
+	/* ROM */
+	hws[ROM_GATE] = ma35d1_clk_gate("rom_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 10);
+
+	/* TRA */
+	hws[TRA_GATE] = ma35d1_clk_gate("tra_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 11);
+
+	/* DBG */
+	hws[DBG_MUX] = ma35d1_clk_mux("dbg_mux", clk_base + REG_CLK_CLKSEL0,
+				      27, 1, dbg_sel_clks,
+				      ARRAY_SIZE(dbg_sel_clks));
+	hws[DBG_GATE] = ma35d1_clk_gate("dbg_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 12);
+
+	/* CLKO */
+	hws[CKO_MUX] = ma35d1_clk_mux("cko_mux", clk_base + REG_CLK_CLKSEL4,
+				      24, 4, cko_sel_clks,
+				      ARRAY_SIZE(cko_sel_clks));
+	hws[CKO_DIV] = ma35d1_clk_divider_pow2("cko_div", "cko_mux",
+					       clk_base + REG_CLK_CLKOCTL,
+					       0, 4);
+	hws[CKO_GATE] = ma35d1_clk_gate("cko_gate", "cko_div",
+					clk_base + REG_CLK_SYSCLK1, 13);
+
+	/* GTMR */
+	hws[GTMR_GATE] = ma35d1_clk_gate("gtmr_gate", "hirc",
+					 clk_base + REG_CLK_SYSCLK1, 14);
+
+	/* GPIO */
+	hws[GPA_GATE] = ma35d1_clk_gate("gpa_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 16);
+	hws[GPB_GATE] = ma35d1_clk_gate("gpb_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 17);
+	hws[GPC_GATE] = ma35d1_clk_gate("gpc_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 18);
+	hws[GPD_GATE] = ma35d1_clk_gate("gpd_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 19);
+	hws[GPE_GATE] = ma35d1_clk_gate("gpe_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 20);
+	hws[GPF_GATE] = ma35d1_clk_gate("gpf_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 21);
+	hws[GPG_GATE] = ma35d1_clk_gate("gpg_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 22);
+	hws[GPH_GATE] = ma35d1_clk_gate("gph_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 23);
+	hws[GPI_GATE] = ma35d1_clk_gate("gpi_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 24);
+	hws[GPJ_GATE] = ma35d1_clk_gate("gpj_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 25);
+	hws[GPK_GATE] = ma35d1_clk_gate("gpk_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 26);
+	hws[GPL_GATE] = ma35d1_clk_gate("gpl_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 27);
+	hws[GPM_GATE] = ma35d1_clk_gate("gpm_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 28);
+	hws[GPN_GATE] = ma35d1_clk_gate("gpn_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 29);
+
+	/* TIMER0~11 */
+	hws[TMR0_MUX] = ma35d1_clk_mux("tmr0_mux", clk_base + REG_CLK_CLKSEL1,
+				       0, 3, timer0_sel_clks,
+				       ARRAY_SIZE(timer0_sel_clks));
+	hws[TMR0_GATE] = ma35d1_clk_gate("tmr0_gate", "tmr0_mux",
+					 clk_base + REG_CLK_APBCLK0, 0);
+	hws[TMR1_MUX] = ma35d1_clk_mux("tmr1_mux", clk_base + REG_CLK_CLKSEL1,
+				       4, 3, timer1_sel_clks,
+				       ARRAY_SIZE(timer1_sel_clks));
+	hws[TMR1_GATE] = ma35d1_clk_gate("tmr1_gate", "tmr1_mux",
+					 clk_base + REG_CLK_APBCLK0, 1);
+	hws[TMR2_MUX] = ma35d1_clk_mux("tmr2_mux", clk_base + REG_CLK_CLKSEL1,
+				       8, 3, timer2_sel_clks,
+				       ARRAY_SIZE(timer2_sel_clks));
+	hws[TMR2_GATE] = ma35d1_clk_gate("tmr2_gate", "tmr2_mux",
+					 clk_base + REG_CLK_APBCLK0, 2);
+	hws[TMR3_MUX] = ma35d1_clk_mux("tmr3_mux", clk_base + REG_CLK_CLKSEL1,
+				       12, 3, timer3_sel_clks,
+				       ARRAY_SIZE(timer3_sel_clks));
+	hws[TMR3_GATE] = ma35d1_clk_gate("tmr3_gate", "tmr3_mux",
+					 clk_base + REG_CLK_APBCLK0, 3);
+	hws[TMR4_MUX] = ma35d1_clk_mux("tmr4_mux", clk_base + REG_CLK_CLKSEL1,
+				       16, 3, timer4_sel_clks,
+				       ARRAY_SIZE(timer4_sel_clks));
+	hws[TMR4_GATE] = ma35d1_clk_gate("tmr4_gate", "tmr4_mux",
+					 clk_base + REG_CLK_APBCLK0, 4);
+	hws[TMR5_MUX] = ma35d1_clk_mux("tmr5_mux", clk_base + REG_CLK_CLKSEL1,
+				       20, 3, timer5_sel_clks,
+				       ARRAY_SIZE(timer5_sel_clks));
+	hws[TMR5_GATE] = ma35d1_clk_gate("tmr5_gate", "tmr5_mux",
+					 clk_base + REG_CLK_APBCLK0, 5);
+	hws[TMR6_MUX] = ma35d1_clk_mux("tmr6_mux", clk_base + REG_CLK_CLKSEL1,
+				       24, 3, timer6_sel_clks,
+				       ARRAY_SIZE(timer6_sel_clks));
+	hws[TMR6_GATE] = ma35d1_clk_gate("tmr6_gate", "tmr6_mux",
+					 clk_base + REG_CLK_APBCLK0, 6);
+	hws[TMR7_MUX] = ma35d1_clk_mux("tmr7_mux", clk_base + REG_CLK_CLKSEL1,
+				       28, 3, timer7_sel_clks,
+				       ARRAY_SIZE(timer7_sel_clks));
+	hws[TMR7_GATE] = ma35d1_clk_gate("tmr7_gate", "tmr7_mux",
+					 clk_base + REG_CLK_APBCLK0, 7);
+	hws[TMR8_MUX] = ma35d1_clk_mux("tmr8_mux", clk_base + REG_CLK_CLKSEL2,
+				       0, 3, timer8_sel_clks,
+				       ARRAY_SIZE(timer8_sel_clks));
+	hws[TMR8_GATE] = ma35d1_clk_gate("tmr8_gate", "tmr8_mux",
+					 clk_base + REG_CLK_APBCLK0, 8);
+	hws[TMR9_MUX] = ma35d1_clk_mux("tmr9_mux", clk_base + REG_CLK_CLKSEL2,
+				       4, 3, timer9_sel_clks,
+				       ARRAY_SIZE(timer9_sel_clks));
+	hws[TMR9_GATE] = ma35d1_clk_gate("tmr9_gate", "tmr9_mux",
+					 clk_base + REG_CLK_APBCLK0, 9);
+	hws[TMR10_MUX] = ma35d1_clk_mux("tmr10_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					8, 3, timer10_sel_clks,
+					ARRAY_SIZE(timer10_sel_clks));
+	hws[TMR10_GATE] = ma35d1_clk_gate("tmr10_gate", "tmr10_mux",
+					  clk_base + REG_CLK_APBCLK0, 10);
+	hws[TMR11_MUX] = ma35d1_clk_mux("tmr11_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					12, 3, timer11_sel_clks,
+					ARRAY_SIZE(timer11_sel_clks));
+	hws[TMR11_GATE] = ma35d1_clk_gate("tmr11_gate", "tmr11_mux",
+					  clk_base + REG_CLK_APBCLK0, 11);
+
+	/* UART0~16 */
+	hws[UART0_MUX] = ma35d1_clk_mux("uart0_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					16, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART0_DIV] = ma35d1_clk_divider("uart0_div", "uart0_mux",
+					    clk_base + REG_CLK_CLKDIV1,
+					    16, 4);
+	hws[UART0_GATE] = ma35d1_clk_gate("uart0_gate", "uart0_div",
+					  clk_base + REG_CLK_APBCLK0, 12);
+	hws[UART1_MUX] = ma35d1_clk_mux("uart1_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					18, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART1_DIV] = ma35d1_clk_divider("uart1_div", "uart1_mux",
+					    clk_base + REG_CLK_CLKDIV1,
+					    20, 4);
+	hws[UART1_GATE] = ma35d1_clk_gate("uart1_gate", "uart1_div",
+					  clk_base + REG_CLK_APBCLK0, 13);
+	hws[UART2_MUX] = ma35d1_clk_mux("uart2_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					20, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART2_DIV] = ma35d1_clk_divider("uart2_div", "uart2_mux",
+					    clk_base + REG_CLK_CLKDIV1,
+					    24, 4);
+	hws[UART2_GATE] = ma35d1_clk_gate("uart2_gate", "uart2_div",
+					  clk_base + REG_CLK_APBCLK0, 14);
+	hws[UART3_MUX] = ma35d1_clk_mux("uart3_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					22, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART3_DIV] = ma35d1_clk_divider("uart3_div", "uart3_mux",
+					    clk_base + REG_CLK_CLKDIV1,
+					    28, 4);
+	hws[UART3_GATE] = ma35d1_clk_gate("uart3_gate", "uart3_div",
+					  clk_base + REG_CLK_APBCLK0, 15);
+	hws[UART4_MUX] = ma35d1_clk_mux("uart4_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					24, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART4_DIV] = ma35d1_clk_divider("uart4_div", "uart4_mux",
+					    clk_base + REG_CLK_CLKDIV2,
+					    0, 4);
+	hws[UART4_GATE] = ma35d1_clk_gate("uart4_gate", "uart4_div",
+					  clk_base + REG_CLK_APBCLK0, 16);
+	hws[UART5_MUX] = ma35d1_clk_mux("uart5_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					26, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART5_DIV] = ma35d1_clk_divider("uart5_div", "uart5_mux",
+					    clk_base + REG_CLK_CLKDIV2,
+					    4, 4);
+	hws[UART5_GATE] = ma35d1_clk_gate("uart5_gate", "uart5_div",
+					  clk_base + REG_CLK_APBCLK0, 17);
+	hws[UART6_MUX] = ma35d1_clk_mux("uart6_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					28, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART6_DIV] = ma35d1_clk_divider("uart6_div", "uart6_mux",
+					    clk_base + REG_CLK_CLKDIV2,
+					    8, 4);
+	hws[UART6_GATE] = ma35d1_clk_gate("uart6_gate", "uart6_div",
+					  clk_base + REG_CLK_APBCLK0, 18);
+	hws[UART7_MUX] = ma35d1_clk_mux("uart7_mux",
+					clk_base + REG_CLK_CLKSEL2,
+					30, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART7_DIV] = ma35d1_clk_divider("uart7_div", "uart7_mux",
+					    clk_base + REG_CLK_CLKDIV2,
+					    12, 4);
+	hws[UART7_GATE] = ma35d1_clk_gate("uart7_gate", "uart7_div",
+					  clk_base + REG_CLK_APBCLK0, 19);
+	hws[UART8_MUX] = ma35d1_clk_mux("uart8_mux",
+					clk_base + REG_CLK_CLKSEL3,
+					0, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART8_DIV] = ma35d1_clk_divider("uart8_div", "uart8_mux",
+					    clk_base + REG_CLK_CLKDIV2,
+					    16, 4);
+	hws[UART8_GATE] = ma35d1_clk_gate("uart8_gate", "uart8_div",
+					  clk_base + REG_CLK_APBCLK0, 20);
+	hws[UART9_MUX] = ma35d1_clk_mux("uart9_mux",
+					clk_base + REG_CLK_CLKSEL3,
+					2, 2, uart_sel_clks,
+					ARRAY_SIZE(uart_sel_clks));
+	hws[UART9_DIV] = ma35d1_clk_divider("uart9_div", "uart9_mux",
+					    clk_base + REG_CLK_CLKDIV2,
+					    20, 4);
+	hws[UART9_GATE] = ma35d1_clk_gate("uart9_gate", "uart9_div",
+					  clk_base + REG_CLK_APBCLK0, 21);
+	hws[UART10_MUX] = ma35d1_clk_mux("uart10_mux",
+					 clk_base + REG_CLK_CLKSEL3,
+					 4, 2, uart_sel_clks,
+					 ARRAY_SIZE(uart_sel_clks));
+	hws[UART10_DIV] = ma35d1_clk_divider("uart10_div", "uart10_mux",
+					     clk_base + REG_CLK_CLKDIV2,
+					     24, 4);
+	hws[UART10_GATE] = ma35d1_clk_gate("uart10_gate", "uart10_div",
+					   clk_base + REG_CLK_APBCLK0, 22);
+	hws[UART11_MUX] = ma35d1_clk_mux("uart11_mux",
+					 clk_base + REG_CLK_CLKSEL3,
+					 6, 2, uart_sel_clks,
+					 ARRAY_SIZE(uart_sel_clks));
+	hws[UART11_DIV] = ma35d1_clk_divider("uart11_div", "uart11_mux",
+					     clk_base + REG_CLK_CLKDIV2,
+					     28, 4);
+	hws[UART11_GATE] = ma35d1_clk_gate("uart11_gate", "uart11_div",
+					   clk_base + REG_CLK_APBCLK0, 23);
+	hws[UART12_MUX] = ma35d1_clk_mux("uart12_mux",
+					 clk_base + REG_CLK_CLKSEL3,
+					 8, 2, uart_sel_clks,
+					 ARRAY_SIZE(uart_sel_clks));
+	hws[UART12_DIV] = ma35d1_clk_divider("uart12_div", "uart12_mux",
+					     clk_base + REG_CLK_CLKDIV3,
+					     0, 4);
+	hws[UART12_GATE] = ma35d1_clk_gate("uart12_gate", "uart12_div",
+					   clk_base + REG_CLK_APBCLK0, 24);
+	hws[UART13_MUX] = ma35d1_clk_mux("uart13_mux",
+					 clk_base + REG_CLK_CLKSEL3,
+					 10, 2, uart_sel_clks,
+					 ARRAY_SIZE(uart_sel_clks));
+	hws[UART13_DIV] = ma35d1_clk_divider("uart13_div", "uart13_mux",
+					     clk_base + REG_CLK_CLKDIV3,
+					     4, 4);
+	hws[UART13_GATE] = ma35d1_clk_gate("uart13_gate", "uart13_div",
+					   clk_base + REG_CLK_APBCLK0, 25);
+	hws[UART14_MUX] = ma35d1_clk_mux("uart14_mux",
+					 clk_base + REG_CLK_CLKSEL3,
+					 12, 2, uart_sel_clks,
+					 ARRAY_SIZE(uart_sel_clks));
+	hws[UART14_DIV] = ma35d1_clk_divider("uart14_div", "uart14_mux",
+					     clk_base + REG_CLK_CLKDIV3,
+					     8, 4);
+	hws[UART14_GATE] = ma35d1_clk_gate("uart14_gate", "uart14_div",
+					   clk_base + REG_CLK_APBCLK0, 26);
+	hws[UART15_MUX] = ma35d1_clk_mux("uart15_mux",
+					 clk_base + REG_CLK_CLKSEL3,
+					 14, 2, uart_sel_clks,
+					 ARRAY_SIZE(uart_sel_clks));
+	hws[UART15_DIV] = ma35d1_clk_divider("uart15_div", "uart15_mux",
+					     clk_base + REG_CLK_CLKDIV3,
+					     12, 4);
+	hws[UART15_GATE] = ma35d1_clk_gate("uart15_gate", "uart15_div",
+					   clk_base + REG_CLK_APBCLK0, 27);
+	hws[UART16_MUX] = ma35d1_clk_mux("uart16_mux",
+					 clk_base + REG_CLK_CLKSEL3,
+					 16, 2, uart_sel_clks,
+					 ARRAY_SIZE(uart_sel_clks));
+	hws[UART16_DIV] = ma35d1_clk_divider("uart16_div", "uart16_mux",
+					     clk_base + REG_CLK_CLKDIV3,
+					     16, 4);
+	hws[UART16_GATE] = ma35d1_clk_gate("uart16_gate", "uart16_div",
+					   clk_base + REG_CLK_APBCLK0, 28);
+
+	/* RTC */
+	hws[RTC_GATE] = ma35d1_clk_gate("rtc_gate", "lxt",
+					clk_base + REG_CLK_APBCLK0, 29);
+
+	/* DDRP */
+	hws[DDR_GATE] = ma35d1_clk_gate("ddr_gate", "ddrpll",
+					clk_base + REG_CLK_APBCLK0, 30);
+
+	/* KPI */
+	hws[KPI_MUX] = ma35d1_clk_mux("kpi_mux", clk_base + REG_CLK_CLKSEL4,
+				      30, 1, kpi_sel_clks,
+				      ARRAY_SIZE(kpi_sel_clks));
+	hws[KPI_DIV] = ma35d1_clk_divider("kpi_div", "kpi_mux",
+					  clk_base + REG_CLK_CLKDIV4,
+					  24, 8);
+	hws[KPI_GATE] = ma35d1_clk_gate("kpi_gate", "kpi_div",
+					clk_base + REG_CLK_APBCLK0, 31);
+
+	/* I2C0~5 */
+	hws[I2C0_GATE] = ma35d1_clk_gate("i2c0_gate", "pclk0",
+					 clk_base + REG_CLK_APBCLK1, 0);
+	hws[I2C1_GATE] = ma35d1_clk_gate("i2c1_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK1, 1);
+	hws[I2C2_GATE] = ma35d1_clk_gate("i2c2_gate", "pclk2",
+					 clk_base + REG_CLK_APBCLK1, 2);
+	hws[I2C3_GATE] = ma35d1_clk_gate("i2c3_gate", "pclk0",
+					 clk_base + REG_CLK_APBCLK1, 3);
+	hws[I2C4_GATE] = ma35d1_clk_gate("i2c4_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK1, 4);
+	hws[I2C5_GATE] = ma35d1_clk_gate("i2c5_gate", "pclk2",
+					 clk_base + REG_CLK_APBCLK1, 5);
+
+	/* QSPI0~1 */
+	hws[QSPI0_MUX] = ma35d1_clk_mux("qspi0_mux",
+					clk_base + REG_CLK_CLKSEL4,
+					8, 2, qspi0_sel_clks,
+					ARRAY_SIZE(qspi0_sel_clks));
+	hws[QSPI0_GATE] = ma35d1_clk_gate("qspi0_gate", "qspi0_mux",
+					  clk_base + REG_CLK_APBCLK1, 6);
+	hws[QSPI1_MUX] = ma35d1_clk_mux("qspi1_mux",
+					clk_base + REG_CLK_CLKSEL4,
+					10, 2, qspi1_sel_clks,
+					ARRAY_SIZE(qspi1_sel_clks));
+	hws[QSPI1_GATE] = ma35d1_clk_gate("qspi1_gate", "qspi1_mux",
+					  clk_base + REG_CLK_APBCLK1, 7);
+
+	/* SMC0~1 */
+	hws[SMC0_MUX] = ma35d1_clk_mux("smc0_mux",
+					clk_base + REG_CLK_CLKSEL4,
+					28, 1, smc_sel_clks,
+					ARRAY_SIZE(smc_sel_clks));
+	hws[SMC0_DIV] = ma35d1_clk_divider("smc0_div", "smc0_mux",
+					   clk_base + REG_CLK_CLKDIV1,
+					   0, 4);
+	hws[SMC0_GATE] = ma35d1_clk_gate("smc0_gate", "smc0_div",
+					 clk_base + REG_CLK_APBCLK1, 12);
+
+	hws[SMC1_MUX] = ma35d1_clk_mux("smc1_mux",
+					 clk_base + REG_CLK_CLKSEL4,
+					 29, 1, smc_sel_clks,
+					 ARRAY_SIZE(smc_sel_clks));
+	hws[SMC1_DIV] = ma35d1_clk_divider("smc1_div", "smc1_mux",
+					   clk_base + REG_CLK_CLKDIV1,
+					   4, 4);
+	hws[SMC1_GATE] = ma35d1_clk_gate("smc1_gate", "smc1_div",
+					 clk_base + REG_CLK_APBCLK1, 13);
+
+	/* WDT0~2 */
+	hws[WDT0_MUX] = ma35d1_clk_mux("wdt0_mux",
+				       clk_base + REG_CLK_CLKSEL3,
+				       20, 2, wdt0_sel_clks,
+				       ARRAY_SIZE(wdt0_sel_clks));
+	hws[WDT0_GATE] = ma35d1_clk_gate("wdt0_gate", "wdt0_mux",
+					 clk_base + REG_CLK_APBCLK1, 16);
+	hws[WDT1_MUX] = ma35d1_clk_mux("wdt1_mux",
+				       clk_base + REG_CLK_CLKSEL3,
+				       24, 2, wdt1_sel_clks,
+				       ARRAY_SIZE(wdt1_sel_clks));
+	hws[WDT1_GATE] = ma35d1_clk_gate("wdt1_gate", "wdt1_mux",
+					 clk_base + REG_CLK_APBCLK1, 17);
+	hws[WDT2_MUX] = ma35d1_clk_mux("wdt2_mux",
+				       clk_base + REG_CLK_CLKSEL3,
+				       28, 2, wdt2_sel_clks,
+				       ARRAY_SIZE(wdt2_sel_clks));
+	hws[WDT2_GATE] = ma35d1_clk_gate("wdt2_gate", "wdt2_mux",
+				       clk_base + REG_CLK_APBCLK1, 18);
+
+	/* WWDT0~2 */
+	hws[WWDT0_MUX] = ma35d1_clk_mux("wwdt0_mux",
+					clk_base + REG_CLK_CLKSEL3,
+					22, 2, wwdt0_sel_clks,
+					ARRAY_SIZE(wwdt0_sel_clks));
+	hws[WWDT1_MUX] = ma35d1_clk_mux("wwdt1_mux",
+					clk_base + REG_CLK_CLKSEL3,
+					26, 2, wwdt1_sel_clks,
+					ARRAY_SIZE(wwdt1_sel_clks));
+	hws[WWDT2_MUX] = ma35d1_clk_mux("wwdt2_mux",
+					clk_base + REG_CLK_CLKSEL3,
+					30, 2, wwdt2_sel_clks,
+					ARRAY_SIZE(wwdt2_sel_clks));
+
+	/* EPWM0~2 */
+	hws[EPWM0_GATE] = ma35d1_clk_gate("epwm0_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK1, 24);
+	hws[EPWM1_GATE] = ma35d1_clk_gate("epwm1_gate", "pclk2",
+					  clk_base + REG_CLK_APBCLK1, 25);
+	hws[EPWM2_GATE] = ma35d1_clk_gate("epwm2_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK1, 26);
+
+	/* I2S0~1 */
+	hws[I2S0_MUX] = ma35d1_clk_mux("i2s0_mux",
+				       clk_base + REG_CLK_CLKSEL4,
+				       12, 2, i2s0_sel_clks,
+				       ARRAY_SIZE(i2s0_sel_clks));
+	hws[I2S0_GATE] = ma35d1_clk_gate("i2s0_gate", "i2s0_mux",
+					 clk_base + REG_CLK_APBCLK2, 0);
+	hws[I2S1_MUX] = ma35d1_clk_mux("i2s1_mux",
+				       clk_base + REG_CLK_CLKSEL4,
+				       14, 2, i2s1_sel_clks,
+				       ARRAY_SIZE(i2s1_sel_clks));
+	hws[I2S1_GATE] = ma35d1_clk_gate("i2s1_gate", "i2s1_mux",
+					 clk_base + REG_CLK_APBCLK2, 1);
+
+	/* SSMCC */
+	hws[SSMCC_GATE] = ma35d1_clk_gate("ssmcc_gate", "pclk3",
+					  clk_base + REG_CLK_APBCLK2, 2);
+
+	/* SSPCC */
+	hws[SSPCC_GATE] = ma35d1_clk_gate("sspcc_gate", "pclk3",
+					  clk_base + REG_CLK_APBCLK2, 3);
+
+	/* SPI0~3 */
+	hws[SPI0_MUX] = ma35d1_clk_mux("spi0_mux",
+				       clk_base + REG_CLK_CLKSEL4,
+				       0, 2, spi0_sel_clks,
+				       ARRAY_SIZE(spi0_sel_clks));
+	hws[SPI0_GATE] = ma35d1_clk_gate("spi0_gate", "spi0_mux",
+					 clk_base + REG_CLK_APBCLK2, 4);
+	hws[SPI1_MUX] = ma35d1_clk_mux("spi1_mux",
+				       clk_base + REG_CLK_CLKSEL4,
+				       2, 2, spi1_sel_clks,
+				       ARRAY_SIZE(spi1_sel_clks));
+	hws[SPI1_GATE] = ma35d1_clk_gate("spi1_gate", "spi1_mux",
+					 clk_base + REG_CLK_APBCLK2, 5);
+	hws[SPI2_MUX] = ma35d1_clk_mux("spi2_mux",
+				       clk_base + REG_CLK_CLKSEL4,
+				       4, 2, spi2_sel_clks,
+				       ARRAY_SIZE(spi2_sel_clks));
+	hws[SPI2_GATE] = ma35d1_clk_gate("spi2_gate", "spi2_mux",
+					 clk_base + REG_CLK_APBCLK2, 6);
+	hws[SPI3_MUX] = ma35d1_clk_mux("spi3_mux",
+				       clk_base + REG_CLK_CLKSEL4,
+				       6, 2, spi3_sel_clks,
+				       ARRAY_SIZE(spi3_sel_clks));
+	hws[SPI3_GATE] = ma35d1_clk_gate("spi3_gate", "spi3_mux",
+					 clk_base + REG_CLK_APBCLK2, 7);
+
+	/* ECAP0~2 */
+	hws[ECAP0_GATE] = ma35d1_clk_gate("ecap0_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK2, 8);
+	hws[ECAP1_GATE] = ma35d1_clk_gate("ecap1_gate", "pclk2",
+					  clk_base + REG_CLK_APBCLK2, 9);
+	hws[ECAP2_GATE] = ma35d1_clk_gate("ecap2_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK2, 10);
+
+	/* QEI0~2 */
+	hws[QEI0_GATE] = ma35d1_clk_gate("qei0_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK2, 12);
+	hws[QEI1_GATE] = ma35d1_clk_gate("qei1_gate", "pclk2",
+					 clk_base + REG_CLK_APBCLK2, 13);
+	hws[QEI2_GATE] = ma35d1_clk_gate("qei2_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK2, 14);
+
+	/* ADC */
+	hws[ADC_DIV] = ma35d1_reg_adc_clkdiv(dev, "adc_div", "pclk0", 0,
+					     clk_base + REG_CLK_CLKDIV4,
+					     4, 17, 0x1ffff);
+	hws[ADC_GATE] = ma35d1_clk_gate("adc_gate", "adc_div",
+					clk_base + REG_CLK_APBCLK2, 24);
+
+	/* EADC */
+	hws[EADC_DIV] = ma35d1_clk_divider_table("eadc_div", "pclk2",
+						 clk_base + REG_CLK_CLKDIV4,
+						 0, 4, eadc_div_table);
+	hws[EADC_GATE] = ma35d1_clk_gate("eadc_gate", "eadc_div",
+					 clk_base + REG_CLK_APBCLK2, 25);
+
+	ret = of_clk_add_hw_provider(clk_node, of_clk_hw_onecell_get,
+				     ma35d1_hw_data);
+	if (ret < 0) {
+		dev_err(dev, "failed to register hws for MA35D1\n");
+		iounmap(clk_base);
+	}
+	return ret;
+}
+
+static const struct of_device_id ma35d1_clk_of_match[] = {
+	{ .compatible = "nuvoton,ma35d1-clk" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ma35d1_clk_of_match);
+
+static struct platform_driver ma35d1_clk_driver = {
+	.probe = ma35d1_clocks_probe,
+	.driver = {
+		.name = "ma35d1-clk",
+		.of_match_table = ma35d1_clk_of_match,
+	},
+};
+
+static int __init ma35d1_clocks_init(void)
+{
+	return platform_driver_register(&ma35d1_clk_driver);
+}
+
+postcore_initcall(ma35d1_clocks_init);
+
+MODULE_AUTHOR("Chi-Fang Li<cfli0@nuvoton.com>");
+MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
new file mode 100644
index 000000000000..faae5a17e425
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#ifndef __DRV_CLK_NUVOTON_MA35D1_H
+#define __DRV_CLK_NUVOTON_MA35D1_H
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/ma35d1-sys.h>
+
+enum ma35d1_pll_type {
+	MA35D1_CAPLL,
+	MA35D1_DDRPLL,
+	MA35D1_APLL,
+	MA35D1_EPLL,
+	MA35D1_VPLL,
+};
+
+enum ma35d1_pll_mode {
+	VSIPLL_INTEGER_MODE,
+	VSIPLL_FRACTIONAL_MODE,
+	VSIPLL_SS_MODE,
+};
+
+/* VSI-PLL CTL0~2 */
+#define VSIPLL_CTL0			0x0
+#define VSIPLL_CTL1			0x4
+#define VSIPLL_CTL2			0x8
+
+/* VSI-PLL Specification limits */
+#define VSIPLL_FREF_MAX_FREQ		200000000UL
+#define VSIPLL_FREF_MIN_FREQ		1000000UL
+#define VSIPLL_FREFDIVM_MAX_FREQ	40000000UL
+#define VSIPLL_FREFDIVM_MIN_FREQ0	1000000UL
+#define VSIPLL_FREFDIVM_MIN_FREQ1	10000000UL
+#define VSIPLL_FCLK_MAX_FREQ		2400000000UL
+#define VSIPLL_FCLK_MIN_FREQ		600000000UL
+#define VSIPLL_FCLKO_MAX_FREQ		2400000000UL
+#define VSIPLL_FCLKO_MIN_FREQ		85700000UL
+#define VSIPLL_SPREAD_RANGE		194
+#define VSIPLL_MODULATION_FREQ		50000
+
+/* Clock Control Registers Offset */
+#define REG_CLK_PWRCTL			(0x00)
+#define REG_CLK_SYSCLK0			(0x04)
+#define REG_CLK_SYSCLK1			(0x08)
+#define REG_CLK_APBCLK0			(0x0C)
+#define REG_CLK_APBCLK1			(0x10)
+#define REG_CLK_APBCLK2			(0x14)
+#define REG_CLK_CLKSEL0			(0x18)
+#define REG_CLK_CLKSEL1			(0x1C)
+#define REG_CLK_CLKSEL2			(0x20)
+#define REG_CLK_CLKSEL3			(0x24)
+#define REG_CLK_CLKSEL4			(0x28)
+#define REG_CLK_CLKDIV0			(0x2C)
+#define REG_CLK_CLKDIV1			(0x30)
+#define REG_CLK_CLKDIV2			(0x34)
+#define REG_CLK_CLKDIV3			(0x38)
+#define REG_CLK_CLKDIV4			(0x3C)
+#define REG_CLK_CLKOCTL			(0x40)
+#define REG_CLK_STATUS			(0x50)
+#define REG_CLK_PLL0CTL0		(0x60)
+#define REG_CLK_PLL2CTL0		(0x80)
+#define REG_CLK_PLL2CTL1		(0x84)
+#define REG_CLK_PLL2CTL2		(0x88)
+#define REG_CLK_PLL3CTL0		(0x90)
+#define REG_CLK_PLL3CTL1		(0x94)
+#define REG_CLK_PLL3CTL2		(0x98)
+#define REG_CLK_PLL4CTL0		(0xA0)
+#define REG_CLK_PLL4CTL1		(0xA4)
+#define REG_CLK_PLL4CTL2		(0xA8)
+#define REG_CLK_PLL5CTL0		(0xB0)
+#define REG_CLK_PLL5CTL1		(0xB4)
+#define REG_CLK_PLL5CTL2		(0xB8)
+#define REG_CLK_CLKDCTL			(0xC0)
+#define REG_CLK_CLKDSTS			(0xC4)
+#define REG_CLK_CDUPB			(0xC8)
+#define REG_CLK_CDLOWB			(0xCC)
+#define REG_CLK_CKFLTRCTL		(0xD0)
+#define REG_CLK_TESTCLK			(0xF0)
+#define REG_CLK_PLLCTL			(0x40)
+
+/* Constant Definitions for Clock Controller */
+#define SMICPLLCTL0_FBDIV_POS		(0)
+#define SMICPLLCTL0_FBDIV_MSK		(0xfful << SMICPLLCTL0_FBDIV_POS)
+#define SMICPLLCTL0_INDIV_POS		(8)
+#define SMICPLLCTL0_INDIV_MSK		(0xful << SMICPLLCTL0_INDIV_POS)
+#define SMICPLLCTL0_OUTDIV_POS		(12)
+#define SMICPLLCTL0_OUTDIV_MSK		(0x3ul << SMICPLLCTL0_OUTDIV_POS)
+#define SMICPLLCTL0_PD_POS		(16)
+#define SMICPLLCTL0_PD_MSK		(0x1ul << SMICPLLCTL0_PD_POS)
+#define SMICPLLCTL0_BP_POS		(17)
+#define SMICPLLCTL0_BP_MSK		(0x1ul << SMICPLLCTL0_BP_POS)
+#define VSIPLLCTL0_FBDIV_POS		(0)
+#define VSIPLLCTL0_FBDIV_MSK		(0x7fful << VSIPLLCTL0_FBDIV_POS)
+#define VSIPLLCTL0_INDIV_POS		(12)
+#define VSIPLLCTL0_INDIV_MSK		(0x3ful << VSIPLLCTL0_INDIV_POS)
+#define VSIPLLCTL0_MODE_POS		(18)
+#define VSIPLLCTL0_MODE_MSK		(0x3ul << VSIPLLCTL0_MODE_POS)
+#define VSIPLLCTL0_SSRATE_POS		(20)
+#define VSIPLLCTL0_SSRATE_MSK		(0x7fful << VSIPLLCTL0_SSRATE_POS)
+#define VSIPLLCTL1_PD_POS		(0)
+#define VSIPLLCTL1_PD_MSK		(0x1ul << VSIPLLCTL1_PD_POS)
+#define VSIPLLCTL1_BP_POS		(1)
+#define VSIPLLCTL1_BP_MSK		(0x1ul << VSIPLLCTL1_BP_POS)
+#define VSIPLLCTL1_OUTDIV_POS		(4)
+#define VSIPLLCTL1_OUTDIV_MSK		(0x7ul << VSIPLLCTL1_OUTDIV_POS)
+#define VSIPLLCTL1_FRAC_POS		(8)
+#define VSIPLLCTL1_FRAC_MSK		(0xfffffful << VSIPLLCTL1_FRAC_POS)
+#define VSIPLLCTL2_SLOPE_POS		(0)
+#define VSIPLLCTL2_SLOPE_MSK		(0xfffffful << VSIPLLCTL2_SLOPE_POS)
+
+struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type, u8 u8mode,
+				 const char *name, const char *parent,
+				 unsigned long targetFreq,
+				 void __iomem *base,
+				 struct regmap *regmap);
+
+struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev,
+				    const char *name,
+				    const char *parent_name,
+				    unsigned long flags,
+				    void __iomem *reg, u8 shift,
+				    u8 width, u32 mask_bit);
+
+extern spinlock_t ma35d1_lock;
+
+static inline struct clk_hw *ma35d1_clk_fixed(const char *name, int rate)
+{
+	return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
+}
+
+static inline struct clk_hw *ma35d1_clk_mux(const char *name,
+					    void __iomem *reg, u8 shift,
+					    u8 width,
+					    const char *const *parents,
+					    int num_parents)
+{
+	return clk_hw_register_mux(NULL, name, parents, num_parents,
+				   CLK_SET_RATE_NO_REPARENT, reg, shift,
+				   width, 0, &ma35d1_lock);
+}
+
+static inline struct clk_hw *ma35d1_clk_divider(const char *name,
+						const char *parent,
+						void __iomem *reg, u8 shift,
+						u8 width)
+{
+	return clk_hw_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+				       reg, shift, width, 0, &ma35d1_lock);
+}
+
+static inline struct clk_hw *ma35d1_clk_divider_pow2(const char *name,
+						     const char *parent,
+						     void __iomem *reg,
+						     u8 shift, u8 width)
+{
+	return clk_hw_register_divider(NULL, name, parent,
+				       CLK_DIVIDER_POWER_OF_TWO, reg, shift,
+				       width, 0, &ma35d1_lock);
+}
+
+static inline struct clk_hw *ma35d1_clk_divider_table(const char *name,
+					const char *parent,
+					void __iomem *reg,
+					u8 shift, u8 width,
+					const struct clk_div_table *table)
+{
+	return clk_hw_register_divider_table(NULL, name, parent, 0,
+					     reg, shift, width, 0, table,
+					     &ma35d1_lock);
+}
+
+static inline struct clk_hw *ma35d1_clk_fixed_factor(const char *name,
+						     const char *parent,
+						     unsigned int mult,
+						     unsigned int div)
+{
+	return clk_hw_register_fixed_factor(NULL, name, parent,
+					    CLK_SET_RATE_PARENT, mult, div);
+}
+
+static inline struct clk_hw *ma35d1_clk_gate(const char *name,
+					     const char *parent,
+					     void __iomem *reg, u8 shift)
+{
+	return clk_hw_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT,
+				    reg, shift, 0, &ma35d1_lock);
+}
+
+#endif /* __DRV_CLK_NUVOTON_MA35D1_H */
-- 
2.34.1


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

* [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (11 preceding siblings ...)
  2023-03-15  7:28 ` [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
@ 2023-03-15  7:29 ` Jacky Huang
  2023-03-16  7:51   ` Krzysztof Kozlowski
  2023-03-16 15:05   ` Ilpo Järvinen
  2023-03-15  7:29 ` [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial " Jacky Huang
                   ` (3 subsequent siblings)
  16 siblings, 2 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:29 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

This driver supports individual IP reset for ma35d1. The reset
control registers is a subset of system control registers.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 drivers/reset/Kconfig        |   6 ++
 drivers/reset/Makefile       |   1 +
 drivers/reset/reset-ma35d1.c | 152 +++++++++++++++++++++++++++++++++++
 3 files changed, 159 insertions(+)
 create mode 100644 drivers/reset/reset-ma35d1.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 2a52c990d4fe..47671060d259 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -143,6 +143,12 @@ config RESET_NPCM
 	  This enables the reset controller driver for Nuvoton NPCM
 	  BMC SoCs.
 
+config RESET_NUVOTON_MA35D1
+	bool "Nuvton MA35D1 Reset Driver"
+	default ARCH_NUVOTON
+	help
+	  This enables the reset controller driver for Nuvoton MA35D1 SoC.
+
 config RESET_OXNAS
 	bool
 
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 3e7e5fd633a8..fd52dcf66a99 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
 obj-$(CONFIG_RESET_MESON) += reset-meson.o
 obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
 obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
+obj-$(CONFIG_RESET_NUVOTON_MA35D1) += reset-ma35d1.o
 obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
 obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
 obj-$(CONFIG_RESET_POLARFIRE_SOC) += reset-mpfs.o
diff --git a/drivers/reset/reset-ma35d1.c b/drivers/reset/reset-ma35d1.c
new file mode 100644
index 000000000000..bdd39483ca4e
--- /dev/null
+++ b/drivers/reset/reset-ma35d1.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/mfd/ma35d1-sys.h>
+#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
+#include <linux/regmap.h>
+#include <linux/reboot.h>
+
+#define RST_PRE_REG	32
+
+struct ma35d1_reset_data {
+	struct reset_controller_dev rcdev;
+	struct regmap *regmap;
+};
+
+struct ma35d1_reboot_data {
+	struct notifier_block restart_handler;
+	struct regmap *regmap;
+};
+
+static int ma35d1_restart_handler(struct notifier_block *this,
+				  unsigned long mode, void *cmd)
+{
+	struct ma35d1_reboot_data *data =
+			container_of(this, struct ma35d1_reboot_data,
+				     restart_handler);
+	regmap_write(data->regmap, REG_SYS_IPRST0, 1 << MA35D1_RESET_CHIP);
+	return -EAGAIN;
+}
+
+static int ma35d1_reset_update(struct reset_controller_dev *rcdev,
+			      unsigned long id, bool assert)
+{
+	int reg;
+	int offset = (id / RST_PRE_REG) * 4;
+	struct ma35d1_reset_data *data =
+			container_of(rcdev, struct ma35d1_reset_data, rcdev);
+
+	regmap_read(data->regmap, REG_SYS_IPRST0 + offset, &reg);
+	if (assert)
+		reg |= 1 << (id % RST_PRE_REG);
+	else
+		reg &= ~(1 << (id % RST_PRE_REG));
+
+	regmap_write(data->regmap, REG_SYS_IPRST0 + offset, reg);
+	return 0;
+}
+
+static int ma35d1_reset_assert(struct reset_controller_dev *rcdev,
+			       unsigned long id)
+{
+	return ma35d1_reset_update(rcdev, id, true);
+}
+
+static int ma35d1_reset_deassert(struct reset_controller_dev *rcdev,
+				 unsigned long id)
+{
+	return ma35d1_reset_update(rcdev, id, false);
+}
+
+static int ma35d1_reset_status(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	int reg;
+	int offset = id / RST_PRE_REG;
+	struct ma35d1_reset_data *data =
+			container_of(rcdev, struct ma35d1_reset_data, rcdev);
+
+	regmap_read(data->regmap, REG_SYS_IPRST0 + offset, &reg);
+	return !!(reg & BIT(id % RST_PRE_REG));
+}
+
+static const struct reset_control_ops ma35d1_reset_ops = {
+	.assert = ma35d1_reset_assert,
+	.deassert = ma35d1_reset_deassert,
+	.status = ma35d1_reset_status,
+};
+
+static const struct of_device_id ma35d1_reset_dt_ids[] = {
+	{ .compatible = "nuvoton,ma35d1-reset" },
+	{ },
+};
+
+static int ma35d1_reset_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ma35d1_reset_data *reset_data;
+	struct ma35d1_reboot_data *reboot_data;
+	int err;
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "Device tree node not found\n");
+		return -EINVAL;
+	}
+
+	reset_data = devm_kzalloc(dev, sizeof(*reset_data), GFP_KERNEL);
+	if (!reset_data)
+		return -ENOMEM;
+
+	reboot_data = devm_kzalloc(dev, sizeof(*reboot_data), GFP_KERNEL);
+	if (!reboot_data) {
+		devm_kfree(dev, reset_data);
+		return -ENOMEM;
+	}
+
+	reset_data->regmap  = syscon_regmap_lookup_by_phandle(
+			      pdev->dev.of_node, "regmap");
+	if (IS_ERR(reset_data->regmap)) {
+		dev_err(&pdev->dev, "Failed to get SYS register base\n");
+		err = PTR_ERR(reset_data->regmap);
+		goto err_out;
+	}
+	reset_data->rcdev.owner = THIS_MODULE;
+	reset_data->rcdev.nr_resets = MA35D1_RESET_COUNT;
+	reset_data->rcdev.ops = &ma35d1_reset_ops;
+	reset_data->rcdev.of_node = dev->of_node;
+
+	reboot_data->regmap = reset_data->regmap;
+	reboot_data->restart_handler.notifier_call = ma35d1_restart_handler;
+	reboot_data->restart_handler.priority = 192;
+
+	err = register_restart_handler(&reboot_data->restart_handler);
+	if (err)
+		dev_warn(&pdev->dev, "failed to register restart handler\n");
+
+	return devm_reset_controller_register(dev, &reset_data->rcdev);
+
+err_out:
+	devm_kfree(dev, reset_data);
+	devm_kfree(dev, reboot_data);
+	return err;
+}
+
+static struct platform_driver ma35d1_reset_driver = {
+	.probe = ma35d1_reset_probe,
+	.driver = {
+		.name = "ma35d1-reset",
+		.of_match_table	= ma35d1_reset_dt_ids,
+	},
+};
+
+builtin_platform_driver(ma35d1_reset_driver);
-- 
2.34.1


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

* [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (12 preceding siblings ...)
  2023-03-15  7:29 ` [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support Jacky Huang
@ 2023-03-15  7:29 ` Jacky Huang
  2023-03-15  7:37   ` Greg KH
                     ` (3 more replies)
  2023-03-15  7:29 ` [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35 Jacky Huang
                   ` (2 subsequent siblings)
  16 siblings, 4 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:29 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

This adds UART and console driver for Nuvoton ma35d1 Soc.

MA35D1 SoC provides up to 17 UART controllers, each with one uart port.
The ma35d1 uart controller is not compatible with 8250.
The uart controller supports:
  - Full-duplex asynchronous communications
  - Separates tx and tx 32/32 bytes entry FIFO for data payloads
  - Hardware auto-flow control
  - Programmable rx buffer trigger level (1/4/8/14/30 bytes)
  - Individual programmable baud rate generator for each channel
  - Supports nCTS, incoming data, rx FIFO reached threshold and
    RS-485 Address Match (AAD mode) wake-up function
  - Supports 8-bit rx buffer time-out detection function
  - Programmable tx data delay time
  - Supports Auto-Baud Rate measurement and baud rate compensation
  - Supports break error, frame error, parity error and rx/tx buffer
    overflow detection function
  – Programmable number of data bit, 5-, 6-, 7-, 8- bit character
  – Programmable parity bit, even, odd, no parity or stick parity bit
    generation and detection
  – Programmable stop bit, 1, 1.5, or 2 stop bit generation
  - Supports IrDA SIR function mode
  - Supports RS-485 function mode
  – Supports RS-485 9-bit mode
  – Supports hardware or software enables to program nRTS pin to control
    RS-485 transmission direction
  - Supports PDMA transfer function
  - Support Single-wire function mode.

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 drivers/tty/serial/Kconfig         |  18 +
 drivers/tty/serial/Makefile        |   1 +
 drivers/tty/serial/ma35d1_serial.c | 842 +++++++++++++++++++++++++++++
 drivers/tty/serial/ma35d1_serial.h |  93 ++++
 include/uapi/linux/serial_core.h   |   3 +
 5 files changed, 957 insertions(+)
 create mode 100644 drivers/tty/serial/ma35d1_serial.c
 create mode 100644 drivers/tty/serial/ma35d1_serial.h

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 625358f44419..cb47fe804595 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1562,6 +1562,24 @@ config SERIAL_SUNPLUS_CONSOLE
 	  you can alter that using a kernel command line option such as
 	  "console=ttySUPx".
 
+config SERIAL_NUVOTON_MA35D1
+	tristate "Nuvoton MA35D1 family UART support"
+	depends on ARCH_NUVOTON || COMPILE_TEST
+	select SERIAL_CORE
+	help
+	  This driver supports Nuvoton MA35D1 family UART ports. If you would
+	  like to use them, you must answer Y or M to this option. Note that
+	  for use as console, it must be included in kernel and not as a
+	  module
+
+config SERIAL_NUVOTON_MA35D1_CONSOLE
+	bool "Console on a Nuvotn MA35D1 family UART port"
+	depends on SERIAL_NUVOTON_MA35D1=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Select this options if you'd like to use the UART port0 of the
+	  Nuvoton MA35D1 family as a console.
+
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cd9afd9e3018..71ebeba06ff2 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -93,3 +93,4 @@ obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
 
 obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
 obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
+obj-$(CONFIG_SERIAL_NUVOTON_MA35D1)	+= ma35d1_serial.o
diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c
new file mode 100644
index 000000000000..8940d07c3777
--- /dev/null
+++ b/drivers/tty/serial/ma35d1_serial.c
@@ -0,0 +1,842 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  MA35D1 serial driver
+ *  Copyright (C) 2023 Nuvoton Technology Corp.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/clk.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/nmi.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/serial.h>
+#include "ma35d1_serial.h"
+
+#define UART_NR			17
+
+static struct uart_driver ma35d1serial_reg;
+struct clk *clk;
+
+struct uart_ma35d1_port {
+	struct uart_port port;
+	u16 capabilities; /* port capabilities */
+	u8 ier;
+	u8 lcr;
+	u8 mcr;
+	u8 mcr_mask;   /* mask of user bits */
+	u8 mcr_force;  /* mask of forced bits */
+	struct serial_rs485 rs485; /* rs485 settings */
+	u32 baud_rate;
+	int rx_count;
+	u32 console_baud_rate;
+	u32 console_line;
+	u32 console_int;
+};
+
+static struct device_node *ma35d1serial_uart_nodes[UART_NR];
+static struct uart_ma35d1_port ma35d1serial_ports[UART_NR] = { 0 };
+static void __stop_tx(struct uart_ma35d1_port *p);
+static void transmit_chars(struct uart_ma35d1_port *up);
+
+static struct uart_ma35d1_port *to_ma35d1_uart_port(struct uart_port *uart)
+{
+	return container_of(uart, struct uart_ma35d1_port, port);
+}
+
+static u32 serial_in(struct uart_ma35d1_port *p, int offset)
+{
+	return __raw_readl(p->port.membase + offset);
+}
+
+static void serial_out(struct uart_ma35d1_port *p, int offset, int value)
+{
+	__raw_writel(value, p->port.membase + offset);
+}
+
+static void __stop_tx(struct uart_ma35d1_port *p)
+{
+	u32 ier;
+
+	ier = serial_in(p, UART_REG_IER);
+	if (ier & THRE_IEN)
+		serial_out(p, UART_REG_IER, ier & ~THRE_IEN);
+}
+
+static void ma35d1serial_stop_tx(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+
+	__stop_tx(up);
+}
+
+static void ma35d1serial_start_tx(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+	u32 ier;
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	ier = serial_in(up, UART_REG_IER);
+	serial_out(up, UART_REG_IER, ier & ~THRE_IEN);
+	if (uart_circ_chars_pending(xmit) <
+	    (16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0x3F)))
+		transmit_chars(up);
+	serial_out(up, UART_REG_IER, ier | THRE_IEN);
+}
+
+static void ma35d1serial_stop_rx(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+
+	serial_out(up, UART_REG_IER, serial_in(up, UART_REG_IER) & ~RDA_IEN);
+}
+
+static void
+receive_chars(struct uart_ma35d1_port *up)
+{
+	u8 ch;
+	u32 fsr;
+	u32 isr;
+	u32 dcnt;
+	char flag;
+
+	isr = serial_in(up, UART_REG_ISR);
+	fsr = serial_in(up, UART_REG_FSR);
+
+	while (!(fsr & RX_EMPTY)) {
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(fsr & (BIF | FEF | PEF | RX_OVER_IF))) {
+			if (fsr & BIF) {
+				serial_out(up, UART_REG_FSR, BIF);
+				up->port.icount.brk++;
+				if (uart_handle_break(&up->port))
+					continue;
+			}
+			if (fsr & FEF) {
+				serial_out(up, UART_REG_FSR, FEF);
+				up->port.icount.frame++;
+			}
+			if (fsr & PEF) {
+				serial_out(up, UART_REG_FSR, PEF);
+				up->port.icount.parity++;
+			}
+			if (fsr & RX_OVER_IF) {
+				serial_out(up, UART_REG_FSR, RX_OVER_IF);
+				up->port.icount.overrun++;
+			}
+			if (fsr & BIF)
+				flag = TTY_BREAK;
+			if (fsr & PEF)
+				flag = TTY_PARITY;
+			if (fsr & FEF)
+				flag = TTY_FRAME;
+		}
+		ch = (u8)serial_in(up, UART_REG_RBR);
+		if (uart_handle_sysrq_char(&up->port, ch))
+			continue;
+
+		uart_insert_char(&up->port, fsr, RX_OVER_IF, ch, flag);
+		up->rx_count++;
+		dcnt = (serial_in(up, UART_REG_FSR) >> 8) & 0x3f;
+		if (up->rx_count > 1023) {
+			spin_lock(&up->port.lock);
+			tty_flip_buffer_push(&up->port.state->port);
+			spin_unlock(&up->port.lock);
+			up->rx_count = 0;
+			if ((isr & RXTO_IF) && (dcnt == 0))
+				goto tout_end;
+		}
+		if (isr & RDA_IF) {
+			if (dcnt == 1)
+				return;
+		}
+		fsr = serial_in(up, UART_REG_FSR);
+	}
+	spin_lock(&up->port.lock);
+	tty_flip_buffer_push(&up->port.state->port);
+	spin_unlock(&up->port.lock);
+tout_end:
+	up->rx_count = 0;
+}
+
+static void transmit_chars(struct uart_ma35d1_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count = 16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0xF);
+
+	if (serial_in(up, UART_REG_FSR) & TX_FULL)
+		count = 0;
+	if (up->port.x_char) {
+		serial_out(up, UART_REG_THR, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_tx_stopped(&up->port)) {
+		ma35d1serial_stop_tx(&up->port);
+		return;
+	}
+	if (uart_circ_empty(xmit)) {
+		__stop_tx(up);
+		return;
+	}
+	while (count > 0) {
+		serial_out(up, UART_REG_THR, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		count--;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+	if (uart_circ_empty(xmit))
+		__stop_tx(up);
+}
+
+static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)dev_id;
+	u32 isr, fsr;
+
+	isr = serial_in(up, UART_REG_ISR);
+	fsr = serial_in(up, UART_REG_FSR);
+	if (isr & (RDA_IF | RXTO_IF))
+		receive_chars(up);
+	if (isr & THRE_INT)
+		transmit_chars(up);
+	if (fsr & (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF))
+		serial_out(up, UART_REG_FSR,
+			   (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF));
+
+	return IRQ_HANDLED;
+}
+
+static u32 ma35d1serial_tx_empty(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+	u32 fsr;
+
+	fsr = serial_in(up, UART_REG_FSR);
+	return (fsr & (TE_FLAG | TX_EMPTY)) == (TE_FLAG | TX_EMPTY) ?
+		TIOCSER_TEMT : 0;
+}
+
+static u32 ma35d1serial_get_mctrl(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+	u32 status;
+	u32 ret = 0;
+
+	status = serial_in(up, UART_REG_MSR);
+	if (!(status & 0x10))
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+static void ma35d1serial_set_mctrl(struct uart_port *port, u32 mctrl)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+	u32 mcr = 0;
+	u32 ier = 0;
+
+	if (mctrl & TIOCM_RTS) {
+		/* set RTS high level trigger */
+		mcr = serial_in(up, UART_REG_MCR);
+		mcr |= 0x200;
+		mcr &= ~(0x2);
+	}
+	if (up->mcr & UART_MCR_AFE) {
+		/* set RTS high level trigger */
+		mcr = serial_in(up, UART_REG_MCR);
+		mcr |= 0x200;
+		mcr &= ~(0x2);
+
+		/* enable CTS/RTS auto-flow control */
+		serial_out(up, UART_REG_IER,
+			   (serial_in(up, UART_REG_IER) | (0x3000)));
+
+		/* Set hardware flow control */
+		up->port.flags |= UPF_HARD_FLOW;
+	} else {
+		/* disable CTS/RTS auto-flow control */
+		ier = serial_in(up, UART_REG_IER);
+		ier &= ~(0x3000);
+		serial_out(up, UART_REG_IER, ier);
+
+		/* un-set hardware flow control */
+		up->port.flags &= ~UPF_HARD_FLOW;
+	}
+
+	/* set CTS high level trigger */
+	serial_out(up, UART_REG_MSR, (serial_in(up, UART_REG_MSR) | (0x100)));
+	serial_out(up, UART_REG_MCR, mcr);
+}
+
+static void ma35d1serial_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+	unsigned long flags;
+	u32 lcr;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	lcr = serial_in(up, UART_REG_LCR);
+	if (break_state != 0)
+		lcr |= BCB; /* set break */
+	else
+		lcr &= ~BCB; /* clr break */
+	serial_out(up, UART_REG_LCR, lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int ma35d1serial_startup(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+	struct tty_struct *tty = port->state->port.tty;
+	int retval;
+
+	/* Reset FIFO */
+	serial_out(up, UART_REG_FCR, TFR | RFR /* | RX_DIS */);
+
+	/* Clear pending interrupts */
+	serial_out(up, UART_REG_ISR, 0xFFFFFFFF);
+
+	retval = request_irq(port->irq, ma35d1serial_interrupt, 0,
+			     tty ? tty->name : "ma35d1_serial", port);
+	if (retval) {
+		dev_err(up->port.dev, "request irq failed.\n");
+		return retval;
+	}
+
+	/* Now, initialize the UART */
+	/* FIFO trigger level 4 byte */
+	/* RTS trigger level 8 bytes */
+	serial_out(up, UART_REG_FCR,
+		   serial_in(up, UART_REG_FCR) | 0x10 | 0x20000);
+	serial_out(up, UART_REG_LCR, 0x7); /* 8 bit */
+	serial_out(up, UART_REG_TOR, 0x40);
+	serial_out(up, UART_REG_IER,
+		   RTO_IEN | RDA_IEN | TIME_OUT_EN | BUFERR_IEN);
+	return 0;
+}
+
+static void ma35d1serial_shutdown(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+
+	free_irq(port->irq, port);
+
+	/* Disable interrupts from this port */
+	serial_out(up, UART_REG_IER, 0);
+}
+
+static u32 ma35d1serial_get_divisor(struct uart_port *port, u32 baud)
+{
+	u32 quot;
+
+	quot = (port->uartclk / baud) - 2;
+	return quot;
+}
+
+static void ma35d1serial_set_termios(struct uart_port *port,
+				     struct ktermios *termios,
+				     const struct ktermios *old)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+	u32 lcr = 0;
+	unsigned long flags;
+	u32 baud, quot;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		lcr = 0;
+		break;
+	case CS6:
+		lcr |= 1;
+		break;
+	case CS7:
+		lcr |= 2;
+		break;
+	case CS8:
+	default:
+		lcr |= 3;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		lcr |= NSB;
+	if (termios->c_cflag & PARENB)
+		lcr |= PBE;
+	if (!(termios->c_cflag & PARODD))
+		lcr |= EPE;
+	if (termios->c_cflag & CMSPAR)
+		lcr |= SPE;
+
+	baud = uart_get_baud_rate(port, termios, old, port->uartclk / 0xffff,
+				  port->uartclk / 11);
+
+	quot = ma35d1serial_get_divisor(port, baud);
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->port.read_status_mask = RX_OVER_IF;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= FEF | PEF;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= BIF;
+
+	/*
+	 * Characteres to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= FEF | PEF;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= BIF;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			up->port.ignore_status_mask |= RX_OVER_IF;
+	}
+	if (termios->c_cflag & CRTSCTS)
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr &= ~UART_MCR_AFE;
+
+	ma35d1serial_set_mctrl(&up->port, up->port.mctrl);
+	serial_out(up, UART_REG_BAUD, quot | 0x30000000);
+	serial_out(up, UART_REG_LCR, lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void ma35d1serial_release_port(struct uart_port *port)
+{
+	iounmap(port->membase);
+	port->membase = NULL;
+}
+
+static int ma35d1serial_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void ma35d1serial_config_port(struct uart_port *port, int flags)
+{
+	int ret;
+
+	/*
+	 * Find the region that we can probe for.  This in turn
+	 * tells us whether we can probe for the type of port.
+	 */
+	ret = ma35d1serial_request_port(port);
+	if (ret < 0)
+		return;
+	port->type = PORT_MA35D1;
+}
+
+static int ma35d1serial_verify_port(struct uart_port *port,
+				    struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MA35D1)
+		return -EINVAL;
+	return 0;
+}
+
+static const char *ma35d1serial_type(struct uart_port *port)
+{
+	return (port->type == PORT_MA35D1) ? "MA35D1" : NULL;
+}
+
+/* Enable or disable the rs485 support */
+static int ma35d1serial_config_rs485(struct uart_port *port,
+				     struct ktermios *termios,
+				     struct serial_rs485 *rs485conf)
+{
+	struct uart_ma35d1_port *p = to_ma35d1_uart_port(port);
+
+	p->rs485 = *rs485conf;
+
+	if (p->rs485.delay_rts_before_send >= 1000)
+		p->rs485.delay_rts_before_send = 1000;
+
+	serial_out(p, UART_FUN_SEL,
+		   (serial_in(p, UART_FUN_SEL) & ~FUN_SEL_MASK));
+
+	if (rs485conf->flags & SER_RS485_ENABLED) {
+		serial_out(p, UART_FUN_SEL,
+			   (serial_in(p, UART_FUN_SEL) | FUN_SEL_RS485));
+
+		if (rs485conf->flags & SER_RS485_RTS_ON_SEND)
+			serial_out(p, UART_REG_MCR,
+				   (serial_in(p, UART_REG_MCR) & ~0x200));
+		else
+			serial_out(p, UART_REG_MCR,
+				   (serial_in(p, UART_REG_MCR) | 0x200));
+
+		/* set auto direction mode */
+		serial_out(p, UART_REG_ALT_CSR,
+			   (serial_in(p, UART_REG_ALT_CSR) | (1 << 10)));
+	}
+	return 0;
+}
+
+static int ma35d1serial_ioctl(struct uart_port *port, u32 cmd, unsigned long arg)
+{
+	switch (cmd) {
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static const struct uart_ops ma35d1serial_ops = {
+	.tx_empty     = ma35d1serial_tx_empty,
+	.set_mctrl    = ma35d1serial_set_mctrl,
+	.get_mctrl    = ma35d1serial_get_mctrl,
+	.stop_tx      = ma35d1serial_stop_tx,
+	.start_tx     = ma35d1serial_start_tx,
+	.stop_rx      = ma35d1serial_stop_rx,
+	.break_ctl    = ma35d1serial_break_ctl,
+	.startup      = ma35d1serial_startup,
+	.shutdown     = ma35d1serial_shutdown,
+	.set_termios  = ma35d1serial_set_termios,
+	.type         = ma35d1serial_type,
+	.release_port = ma35d1serial_release_port,
+	.request_port = ma35d1serial_request_port,
+	.config_port  = ma35d1serial_config_port,
+	.verify_port  = ma35d1serial_verify_port,
+	.ioctl        = ma35d1serial_ioctl,
+};
+
+static const struct of_device_id ma35d1_serial_of_match[] = {
+	{ .compatible = "nuvoton,ma35d1-uart" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ma35d1_serial_of_match);
+
+#ifdef CONFIG_SERIAL_NUVOTON_MA35D1_CONSOLE
+
+static void ma35d1serial_console_putchar(struct uart_port *port,
+					 unsigned char ch)
+{
+	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
+
+	do {
+	} while ((serial_in(up, UART_REG_FSR) & TX_FULL));
+	serial_out(up, UART_REG_THR, ch);
+}
+
+/*
+ *  Print a string to the serial port trying not to disturb
+ *  any possible real use of the port...
+ *
+ *  The console_lock must be held when we get here.
+ */
+static void ma35d1serial_console_write(struct console *co,
+				       const char *s, u32 count)
+{
+	struct uart_ma35d1_port *up = &ma35d1serial_ports[co->index];
+	unsigned long flags;
+	u32 ier;
+
+	local_irq_save(flags);
+
+	/*
+	 *  First save the IER then disable the interrupts
+	 */
+	ier = serial_in(up, UART_REG_IER);
+	serial_out(up, UART_REG_IER, 0);
+
+	uart_console_write(&up->port, s, count, ma35d1serial_console_putchar);
+
+	/*
+	 *  Finally, wait for transmitter to become empty
+	 *  and restore the IER
+	 */
+	do {
+	} while (!(serial_in(up, UART_REG_FSR) & TX_EMPTY));
+	serial_out(up, UART_REG_IER, ier);
+	local_irq_restore(flags);
+}
+
+static int __init ma35d1serial_console_setup(struct console *co,
+					     char *options)
+{
+	struct device_node *np = ma35d1serial_uart_nodes[co->index];
+	struct uart_ma35d1_port *p = &ma35d1serial_ports[co->index];
+	u32 val32[4];
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	if ((co->index < 0) || (co->index >= UART_NR)) {
+		pr_debug("Console Port%x out of range\n", co->index);
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32_array(np, "reg", val32, 4) != 0)
+		return -EINVAL;
+	p->port.iobase = val32[1];
+	p->port.membase = ioremap(p->port.iobase, 0x10000);
+	p->port.ops = &ma35d1serial_ops;
+	p->port.line = 0;
+	p->port.uartclk = 24000000;
+
+	port = &ma35d1serial_ports[co->index].port;
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console ma35d1serial_console = {
+	.name    = "ttyS",
+	.write   = ma35d1serial_console_write,
+	.device  = uart_console_device,
+	.setup   = ma35d1serial_console_setup,
+	.flags   = CON_PRINTBUFFER | CON_ENABLED,
+	.index   = -1,
+	.data    = &ma35d1serial_reg,
+};
+
+static void
+ma35d1serial_console_init_port(void)
+{
+	int i = 0;
+	struct device_node *np;
+
+	for_each_matching_node(np, ma35d1_serial_of_match) {
+		if (ma35d1serial_uart_nodes[i] == NULL) {
+			ma35d1serial_uart_nodes[i] = np;
+			i++;
+		}
+	}
+}
+
+static int __init ma35d1serial_console_init(void)
+{
+	ma35d1serial_console_init_port();
+	register_console(&ma35d1serial_console);
+	return 0;
+}
+console_initcall(ma35d1serial_console_init);
+
+#define MA35D1SERIAL_CONSOLE    (&ma35d1serial_console)
+#else
+#define MA35D1SERIAL_CONSOLE    NULL
+#endif
+
+static struct uart_driver ma35d1serial_reg = {
+	.owner        = THIS_MODULE,
+	.driver_name  = "serial",
+	.dev_name     = "ttyS",
+	.major        = TTY_MAJOR,
+	.minor        = 64,
+	.cons         = MA35D1SERIAL_CONSOLE,
+	.nr           = UART_NR,
+};
+
+/**
+ *  Suspend one serial port.
+ */
+void ma35d1serial_suspend_port(int line)
+{
+	uart_suspend_port(&ma35d1serial_reg, &ma35d1serial_ports[line].port);
+}
+EXPORT_SYMBOL(ma35d1serial_suspend_port);
+
+/**
+ *  Resume one serial port.
+ */
+void ma35d1serial_resume_port(int line)
+{
+	struct uart_ma35d1_port *up = &ma35d1serial_ports[line];
+
+	uart_resume_port(&ma35d1serial_reg, &up->port);
+}
+EXPORT_SYMBOL(ma35d1serial_resume_port);
+
+/*
+ * Register a set of serial devices attached to a platform device.
+ * The list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+static int ma35d1serial_probe(struct platform_device *pdev)
+{
+	struct resource *res_mem;
+	struct uart_ma35d1_port *up;
+	int ret;
+	struct clk *clk;
+	int err;
+
+	if (pdev->dev.of_node) {
+		ret = of_alias_get_id(pdev->dev.of_node, "serial");
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"failed to get alias/pdev id, errno %d\n",
+				ret);
+		return ret;
+		}
+	}
+	up = &ma35d1serial_ports[ret];
+	up->port.line = ret;
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res_mem)
+		return -ENODEV;
+
+	up->port.iobase = res_mem->start;
+	up->port.membase = ioremap(up->port.iobase, 0x10000);
+	up->port.ops = &ma35d1serial_ops;
+
+	spin_lock_init(&up->port.lock);
+
+	clk = of_clk_get(pdev->dev.of_node, 0);
+	if (IS_ERR(clk)) {
+		err = PTR_ERR(clk);
+		dev_err(&pdev->dev, "failed to get core clk: %d\n", err);
+		return -ENOENT;
+	}
+	err = clk_prepare_enable(clk);
+	if (err)
+		return -ENOENT;
+
+	if (up->port.line != 0)
+		up->port.uartclk = clk_get_rate(clk);
+	up->port.irq = platform_get_irq(pdev, 0);
+	up->port.dev = &pdev->dev;
+	up->port.flags = UPF_BOOT_AUTOCONF;
+	up->port.rs485_config = ma35d1serial_config_rs485;
+	ret = uart_add_one_port(&ma35d1serial_reg, &up->port);
+	platform_set_drvdata(pdev, up);
+	return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int ma35d1serial_remove(struct platform_device *dev)
+{
+	int i;
+	struct uart_port *port = platform_get_drvdata(dev);
+
+	free_irq(port->irq, port);
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_ma35d1_port *up = &ma35d1serial_ports[i];
+
+		if (up->port.dev == &dev->dev)
+			uart_remove_one_port(&ma35d1serial_reg, &up->port);
+	}
+	return 0;
+}
+
+static int ma35d1serial_suspend(struct platform_device *dev,
+				pm_message_t state)
+{
+	int i;
+	struct uart_ma35d1_port *up;
+
+	if (dev->dev.of_node)
+		i = of_alias_get_id(dev->dev.of_node, "serial");
+	if (i < 0) {
+		dev_err(&dev->dev, "failed to get alias/pdev id, errno %d\n",
+			i);
+		return i;
+	}
+	up = &ma35d1serial_ports[i];
+	if (i == 0) {
+		up->console_baud_rate = serial_in(up, UART_REG_BAUD);
+		up->console_line = serial_in(up, UART_REG_LCR);
+		up->console_int = serial_in(up, UART_REG_IER);
+	}
+	return 0;
+}
+
+static int ma35d1serial_resume(struct platform_device *dev)
+{
+	int i;
+	struct uart_ma35d1_port *up;
+
+	if (dev->dev.of_node)
+		i = of_alias_get_id(dev->dev.of_node, "serial");
+	if (i < 0) {
+		dev_err(&dev->dev, "failed to get alias/pdev id, errno %d\n",
+			i);
+		return i;
+	}
+	up = &ma35d1serial_ports[i];
+	if (i == 0) {
+		serial_out(up, UART_REG_BAUD, up->console_baud_rate);
+		serial_out(up, UART_REG_LCR, up->console_line);
+		serial_out(up, UART_REG_IER, up->console_int);
+	}
+	return 0;
+}
+
+static struct platform_driver ma35d1serial_driver = {
+	.probe      = ma35d1serial_probe,
+	.remove     = ma35d1serial_remove,
+	.suspend    = ma35d1serial_suspend,
+	.resume     = ma35d1serial_resume,
+	.driver     = {
+		.name   = "ma35d1-uart",
+		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(ma35d1_serial_of_match),
+	},
+};
+
+static int __init ma35d1serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&ma35d1serial_reg);
+	if (ret)
+		return ret;
+	ret = platform_driver_register(&ma35d1serial_driver);
+	if (ret)
+		uart_unregister_driver(&ma35d1serial_reg);
+	return ret;
+}
+
+static void __exit ma35d1serial_exit(void)
+{
+	platform_driver_unregister(&ma35d1serial_driver);
+	uart_unregister_driver(&ma35d1serial_reg);
+}
+
+module_init(ma35d1serial_init);
+module_exit(ma35d1serial_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MA35D1 serial driver");
+MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
+
diff --git a/drivers/tty/serial/ma35d1_serial.h b/drivers/tty/serial/ma35d1_serial.h
new file mode 100644
index 000000000000..5fd845c31b29
--- /dev/null
+++ b/drivers/tty/serial/ma35d1_serial.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  MA35D1 serial driver header file
+ *  Copyright (C) 2023 Nuvoton Technology Corp.
+ */
+#ifndef __MA35D1_SERIAL_H__
+#define __MA35D1_SERIAL_H__
+
+/* UART Receive/Transmit Buffer Register */
+#define UART_REG_RBR	0x00
+#define UART_REG_THR	0x00
+
+/* UART Interrupt Enable Register */
+#define UART_REG_IER	0x04
+#define RDA_IEN		0x00000001 /* RBR Available Interrupt Enable */
+#define THRE_IEN	0x00000002 /* THR Empty Interrupt Enable */
+#define RLS_IEN		0x00000004 /* RX Line Status Interrupt Enable */
+#define RTO_IEN		0x00000010 /* RX Time-out Interrupt Enable */
+#define BUFERR_IEN	0x00000020 /* Buffer Error Interrupt Enable */
+#define TIME_OUT_EN	0x00000800 /* RX Buffer Time-out Counter Enable */
+
+/* UART FIFO Control Register */
+#define UART_REG_FCR	0x08
+#define RFR		0x00000002 /* RX Field Software Reset */
+#define TFR		0x00000004 /* TX Field Software Reset */
+
+/* UART Line Control Register */
+#define UART_REG_LCR	0x0C
+#define	NSB		0x00000004 /* Number of “STOP Bit” */
+#define PBE		0x00000008 /* Parity Bit Enable */
+#define EPE		0x00000010 /* Even Parity Enable */
+#define SPE		0x00000020 /* Stick Parity Enable */
+#define BCB		0x00000040 /* Break Control */
+
+/* UART Modem Control Register */
+#define UART_REG_MCR	0x10
+#define RTS		0x00000020 /* nRTS Signal Control */
+#define RTSACTLV	0x00000200 /* nRTS Pin Active Level */
+#define RTSSTS		0x00002000 /* nRTS Pin Status (Read Only) */
+
+/* UART Modem Status Register */
+#define UART_REG_MSR	0x14
+#define CTSDETF		0x00000001 /* Detect nCTS State Change Flag */
+#define CTSSTS		0x00000010 /* nCTS Pin Status (Read Only) */
+#define CTSACTLV	0x00000100 /* nCTS Pin Active Level */
+
+/* UART FIFO Status Register */
+#define UART_REG_FSR	0x18
+#define RX_OVER_IF	0x00000001 /* RX Overflow Error Interrupt Flag */
+#define PEF		0x00000010 /* Parity Error Flag*/
+#define FEF		0x00000020 /* Framing Error Flag */
+#define BIF		0x00000040 /* Break Interrupt Flag */
+#define RX_EMPTY	0x00004000 /* Receiver FIFO Empty (Read Only) */
+#define RX_FULL		0x00008000 /* Receiver FIFO Full (Read Only) */
+#define TX_EMPTY	0x00400000 /* Transmitter FIFO Empty (Read Only) */
+#define TX_FULL		0x00800000 /* Transmitter FIFO Full (Read Only) */
+#define TX_OVER_IF	0x01000000 /* TX Overflow Error Interrupt Flag */
+#define TE_FLAG		0x10000000 /* Transmitter Empty Flag (Read Only) */
+
+/* UART Interrupt Status Register */
+#define UART_REG_ISR	0x1C
+#define RDA_IF		0x00000001 /* RBR Available Interrupt Flag */
+#define THRE_IF		0x00000002 /* THR Empty Interrupt Flag */
+#define RLSIF		0x00000004 /* Receive Line Interrupt Flag */
+#define MODEMIF		0x00000008 /* MODEM Interrupt Flag */
+#define RXTO_IF		0x00000010 /* RX Time-out Interrupt Flag */
+#define BUFEIF		0x00000020 /* Buffer Error Interrupt Flag */
+#define WK_IF		0x00000040 /* UART Wake-up Interrupt Flag */
+#define RDAINT		0x00000100 /* RBR Available Interrupt Indicator */
+#define THRE_INT	0x00000200 /* THR Empty Interrupt Indicator */
+
+/* UART Time-out Register */
+#define UART_REG_TOR	0x20
+
+/* UART Baud Rate Divider Register */
+#define UART_REG_BAUD	0x24
+
+/* UART Alternate Control/Status Register */
+#define UART_REG_ALT_CSR 0x2C
+
+/* UART Function Select Register */
+#define UART_FUN_SEL	0x30
+#define FUN_SEL_UART	0x00000000
+#define FUN_SEL_RS485	0x00000003
+#define FUN_SEL_MASK	0x00000007
+
+/* UART Wake-up Control Register */
+#define UART_REG_WKCTL	0x40
+
+/* UART Wake-up Status Register */
+#define UART_REG_WKSTS	0x44
+
+#endif /* __MA35D1_SERIAL_H__ */
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 281fa286555c..c6d53db17042 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -279,4 +279,7 @@
 /* Sunplus UART */
 #define PORT_SUNPLUS	123
 
+/* Nuvoton MA35D1 UART */
+#define PORT_MA35D1	124
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
2.34.1


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

* [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (13 preceding siblings ...)
  2023-03-15  7:29 ` [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial " Jacky Huang
@ 2023-03-15  7:29 ` Jacky Huang
  2023-03-16 14:38   ` Arnd Bergmann
  2023-03-16  7:41 ` [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Krzysztof Kozlowski
  2023-03-16 14:05 ` Arnd Bergmann
  16 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  7:29 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

From: Jacky Huang <ychuang3@nuvoton.com>

Add entry for Nuvton ma35d1 maintainer and files

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
---
 MAINTAINERS | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index ec57c42ed544..b42d5c052863 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2535,6 +2535,18 @@ F:	arch/arm/mach-npcm/wpcm450.c
 F:	drivers/*/*/*wpcm*
 F:	drivers/*/*wpcm*
 
+ARM/NUVOTON MA35 ARCHITECTURE
+M:	Jacky Huang <ychuang3@nuvoton.com>
+M:	Shan-Chun Hung <schung@nuvoton.com>
+L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:	Supported
+F:	Documentation/devicetree/bindings/*/*nuvoton*
+F:	arch/arm64/boot/dts/nuvoton/
+F:	drivers/*/*/*ma35d1*
+F:	drivers/*/*ma35d1*
+F:	include/dt-bindings/*/*ma35d1*
+F:	include/linux/mfd/ma35d1-sys.h
+
 ARM/NXP S32G ARCHITECTURE
 M:	Chester Lin <clin@suse.com>
 R:	Andreas Färber <afaerber@suse.de>
-- 
2.34.1


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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-15  7:29 ` [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial " Jacky Huang
@ 2023-03-15  7:37   ` Greg KH
  2023-03-15  9:40     ` Jacky Huang
  2023-03-15  9:48   ` kernel test robot
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 89+ messages in thread
From: Greg KH @ 2023-03-15  7:37 UTC (permalink / raw)
  To: Jacky Huang
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	jirislaby, devicetree, linux-clk, linux-kernel, linux-serial,
	schung, Jacky Huang

On Wed, Mar 15, 2023 at 07:29:01AM +0000, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> This adds UART and console driver for Nuvoton ma35d1 Soc.
> 
> MA35D1 SoC provides up to 17 UART controllers, each with one uart port.
> The ma35d1 uart controller is not compatible with 8250.

A new UART being designed that is not an 8250 compatible?  Why????

Anyway, some minor comments:

>  drivers/tty/serial/ma35d1_serial.c | 842 +++++++++++++++++++++++++++++
>  drivers/tty/serial/ma35d1_serial.h |  93 ++++

Why do you have a .h file for only a single .c file?  Please just put
them both together into one .c file.

>  include/uapi/linux/serial_core.h   |   3 +

Why do you need this #define?  Are you using it in userspace now?  If
not, why have it?

> +static void
> +receive_chars(struct uart_ma35d1_port *up)

Please just put all one one line.


> +{
> +	u8 ch;
> +	u32 fsr;
> +	u32 isr;
> +	u32 dcnt;
> +	char flag;
> +
> +	isr = serial_in(up, UART_REG_ISR);
> +	fsr = serial_in(up, UART_REG_FSR);
> +
> +	while (!(fsr & RX_EMPTY)) {

You have no way out if the hardware is stuck?  That feels wrong.

> +static int ma35d1serial_ioctl(struct uart_port *port, u32 cmd, unsigned long arg)
> +{
> +	switch (cmd) {
> +	default:
> +		return -ENOIOCTLCMD;
> +	}
> +	return 0;
> +}

You do not need to handle any ioctls?

> +static void ma35d1serial_console_putchar(struct uart_port *port,
> +					 unsigned char ch)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +
> +	do {
> +	} while ((serial_in(up, UART_REG_FSR) & TX_FULL));

Again, no way out if this gets stuck in a loop?

> +/**
> + *  Suspend one serial port.
> + */
> +void ma35d1serial_suspend_port(int line)
> +{
> +	uart_suspend_port(&ma35d1serial_reg, &ma35d1serial_ports[line].port);
> +}
> +EXPORT_SYMBOL(ma35d1serial_suspend_port);

Why is this exported?  Who uses it?

And why not EXPORT_SYMBOL_GPL()?

> +
> +/**
> + *  Resume one serial port.
> + */
> +void ma35d1serial_resume_port(int line)
> +{
> +	struct uart_ma35d1_port *up = &ma35d1serial_ports[line];
> +
> +	uart_resume_port(&ma35d1serial_reg, &up->port);
> +}
> +EXPORT_SYMBOL(ma35d1serial_resume_port);

Same here, who calls this and why?

> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -279,4 +279,7 @@
>  /* Sunplus UART */
>  #define PORT_SUNPLUS	123
>  
> +/* Nuvoton MA35D1 UART */
> +#define PORT_MA35D1	124

Again, why is this define needed?

thanks,

greg k-h

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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-15  7:37   ` Greg KH
@ 2023-03-15  9:40     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-15  9:40 UTC (permalink / raw)
  To: Greg KH
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	jirislaby, devicetree, linux-clk, linux-kernel, linux-serial,
	schung, Jacky Huang

Dear Greg,

Thank you for your review.


On 2023/3/15 下午 03:37, Greg KH wrote:
> On Wed, Mar 15, 2023 at 07:29:01AM +0000, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> This adds UART and console driver for Nuvoton ma35d1 Soc.
>>
>> MA35D1 SoC provides up to 17 UART controllers, each with one uart port.
>> The ma35d1 uart controller is not compatible with 8250.
> A new UART being designed that is not an 8250 compatible?  Why????
>
> Anyway, some minor comments:

This UART controller was designed for over 15 years ago and was used on 
many Nuvoton chips.
The register interface is not compatible with 8250, so the 8250 driver 
cannot be applied, but
the functions are compatible.

>>   drivers/tty/serial/ma35d1_serial.c | 842 +++++++++++++++++++++++++++++
>>   drivers/tty/serial/ma35d1_serial.h |  93 ++++
> Why do you have a .h file for only a single .c file?  Please just put
> them both together into one .c file.

OK, we will put the .h into .c in the next version.

>>   include/uapi/linux/serial_core.h   |   3 +
> Why do you need this #define?  Are you using it in userspace now?  If
> not, why have it?

Actually, we do not use it from userspace. I will remove it in the next 
version.


>> +static void
>> +receive_chars(struct uart_ma35d1_port *up)
> Please just put all one one line.
>

OK, I will fix it.

>> +{
>> +	u8 ch;
>> +	u32 fsr;
>> +	u32 isr;
>> +	u32 dcnt;
>> +	char flag;
>> +
>> +	isr = serial_in(up, UART_REG_ISR);
>> +	fsr = serial_in(up, UART_REG_FSR);
>> +
>> +	while (!(fsr & RX_EMPTY)) {
> You have no way out if the hardware is stuck?  That feels wrong.

Thanks for pointing this out. I will add a timeout check to this 
infinite loop.

>> +static int ma35d1serial_ioctl(struct uart_port *port, u32 cmd, unsigned long arg)
>> +{
>> +	switch (cmd) {
>> +	default:
>> +		return -ENOIOCTLCMD;
>> +	}
>> +	return 0;
>> +}
> You do not need to handle any ioctls?

Yes, we do not handle ioctls.
I will remove both ma35d1serial_ioctl() and "ioctl  = 
ma35d1serial_ioctl," in the next version.


>> +static void ma35d1serial_console_putchar(struct uart_port *port,
>> +					 unsigned char ch)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +
>> +	do {
>> +	} while ((serial_in(up, UART_REG_FSR) & TX_FULL));
> Again, no way out if this gets stuck in a loop?

OK, we will fix it in the next version.

>> +/**
>> + *  Suspend one serial port.
>> + */
>> +void ma35d1serial_suspend_port(int line)
>> +{
>> +	uart_suspend_port(&ma35d1serial_reg, &ma35d1serial_ports[line].port);
>> +}
>> +EXPORT_SYMBOL(ma35d1serial_suspend_port);
> Why is this exported?  Who uses it?
>
> And why not EXPORT_SYMBOL_GPL()?
>
>> +
>> +/**
>> + *  Resume one serial port.
>> + */
>> +void ma35d1serial_resume_port(int line)
>> +{
>> +	struct uart_ma35d1_port *up = &ma35d1serial_ports[line];
>> +
>> +	uart_resume_port(&ma35d1serial_reg, &up->port);
>> +}
>> +EXPORT_SYMBOL(ma35d1serial_resume_port);
> Same here, who calls this and why?

The ma35d1serial_suspend_port() and ma35d1serial_resume_port() were used in
previous ARM9 projects for userspace proprietary suspend/resume control.

As it's obsoleted in ma35s1, I will remove these two functions in the 
next version.

>> --- a/include/uapi/linux/serial_core.h
>> +++ b/include/uapi/linux/serial_core.h
>> @@ -279,4 +279,7 @@
>>   /* Sunplus UART */
>>   #define PORT_SUNPLUS	123
>>   
>> +/* Nuvoton MA35D1 UART */
>> +#define PORT_MA35D1	124
> Again, why is this define needed?

As replied above, we will remove the serial_core.h modification from 
this patch.


> thanks,
>
> greg k-h

Best Regards,

Jacky Huang


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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-15  7:29 ` [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial " Jacky Huang
  2023-03-15  7:37   ` Greg KH
@ 2023-03-15  9:48   ` kernel test robot
  2023-03-15 10:13   ` Jiri Slaby
  2023-03-16 14:54   ` Ilpo Järvinen
  3 siblings, 0 replies; 89+ messages in thread
From: kernel test robot @ 2023-03-15  9:48 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: oe-kbuild-all, devicetree, linux-clk, linux-kernel, linux-serial,
	schung, Jacky Huang

Hi Jacky,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on robh/for-next]
[also build test WARNING on clk/clk-next tty/tty-testing tty/tty-next tty/tty-linus linus/master v6.3-rc2 next-20230315]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jacky-Huang/arm64-Kconfig-platforms-Add-config-for-Nuvoton-MA35-platform/20230315-153355
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20230315072902.9298-15-ychuang570808%40gmail.com
patch subject: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
config: sparc-allyesconfig (https://download.01.org/0day-ci/archive/20230315/202303151754.XvPyacT7-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/064028d2f2d911398012103aef3ce8666342ddfc
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Jacky-Huang/arm64-Kconfig-platforms-Add-config-for-Nuvoton-MA35-platform/20230315-153355
        git checkout 064028d2f2d911398012103aef3ce8666342ddfc
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sparc SHELL=/bin/bash drivers/tty/serial/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202303151754.XvPyacT7-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/tty/serial/ma35d1_serial.c:672:6: warning: no previous prototype for 'ma35d1serial_suspend_port' [-Wmissing-prototypes]
     672 | void ma35d1serial_suspend_port(int line)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/tty/serial/ma35d1_serial.c:681:6: warning: no previous prototype for 'ma35d1serial_resume_port' [-Wmissing-prototypes]
     681 | void ma35d1serial_resume_port(int line)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~
--
>> drivers/tty/serial/ma35d1_serial.c:670: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst
    *  Suspend one serial port.
   drivers/tty/serial/ma35d1_serial.c:679: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst
    *  Resume one serial port.


vim +/ma35d1serial_suspend_port +672 drivers/tty/serial/ma35d1_serial.c

   668	
   669	/**
 > 670	 *  Suspend one serial port.
   671	 */
 > 672	void ma35d1serial_suspend_port(int line)
   673	{
   674		uart_suspend_port(&ma35d1serial_reg, &ma35d1serial_ports[line].port);
   675	}
   676	EXPORT_SYMBOL(ma35d1serial_suspend_port);
   677	
   678	/**
   679	 *  Resume one serial port.
   680	 */
 > 681	void ma35d1serial_resume_port(int line)
   682	{
   683		struct uart_ma35d1_port *up = &ma35d1serial_ports[line];
   684	
   685		uart_resume_port(&ma35d1serial_reg, &up->port);
   686	}
   687	EXPORT_SYMBOL(ma35d1serial_resume_port);
   688	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-15  7:29 ` [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial " Jacky Huang
  2023-03-15  7:37   ` Greg KH
  2023-03-15  9:48   ` kernel test robot
@ 2023-03-15 10:13   ` Jiri Slaby
  2023-03-16 13:28     ` Jacky Huang
  2023-03-16 14:54   ` Ilpo Järvinen
  3 siblings, 1 reply; 89+ messages in thread
From: Jiri Slaby @ 2023-03-15 10:13 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15. 03. 23, 8:29, Jacky Huang wrote:
> --- /dev/null
> +++ b/drivers/tty/serial/ma35d1_serial.c
> @@ -0,0 +1,842 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  MA35D1 serial driver
> + *  Copyright (C) 2023 Nuvoton Technology Corp.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>

What parameters does this module have?

> +#include <linux/ioport.h>
> +#include <linux/init.h>
> +#include <linux/console.h>
> +#include <linux/sysrq.h>
> +#include <linux/delay.h>

What do you use from delay.h?

> +#include <linux/platform_device.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/clk.h>
> +#include <linux/serial_reg.h>
> +#include <linux/serial_core.h>
> +#include <linux/serial.h>
> +#include <linux/nmi.h>

nmi.h?

Please clean up all of the includes.

> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/io.h>
> +#include <asm/irq.h>
> +#include <asm/serial.h>
> +#include "ma35d1_serial.h"
> +
> +#define UART_NR			17
> +
> +static struct uart_driver ma35d1serial_reg;
> +struct clk *clk;
> +
> +struct uart_ma35d1_port {
> +	struct uart_port port;
> +	u16 capabilities; /* port capabilities */
> +	u8 ier;
> +	u8 lcr;
> +	u8 mcr;
> +	u8 mcr_mask;   /* mask of user bits */
> +	u8 mcr_force;  /* mask of forced bits */

Where are all those used?

> +	struct serial_rs485 rs485; /* rs485 settings */
> +	u32 baud_rate;

And this one.

> +	int rx_count;
> +	u32 console_baud_rate;
> +	u32 console_line;
> +	u32 console_int;
> +};
> +
> +static struct device_node *ma35d1serial_uart_nodes[UART_NR];
> +static struct uart_ma35d1_port ma35d1serial_ports[UART_NR] = { 0 };

> +static void __stop_tx(struct uart_ma35d1_port *p);

What for?

> +static void transmit_chars(struct uart_ma35d1_port *up);
> +
> +static struct uart_ma35d1_port *to_ma35d1_uart_port(struct uart_port *uart)
> +{
> +	return container_of(uart, struct uart_ma35d1_port, port);
> +}
> +
> +static u32 serial_in(struct uart_ma35d1_port *p, int offset)

Q: int? A: No.

> +{
> +	return __raw_readl(p->port.membase + offset);
> +}
> +
> +static void serial_out(struct uart_ma35d1_port *p, int offset, int value)

No ints here, please.

> +{
> +	__raw_writel(value, p->port.membase + offset);
> +}
> +
> +static void __stop_tx(struct uart_ma35d1_port *p)
> +{
> +	u32 ier;
> +
> +	ier = serial_in(p, UART_REG_IER);
> +	if (ier & THRE_IEN)
> +		serial_out(p, UART_REG_IER, ier & ~THRE_IEN);
> +}
> +
> +static void ma35d1serial_stop_tx(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;

Despite you have to_ma35d1_uart_port(), you do this?

> +
> +	__stop_tx(up);
> +}
> +
> +static void ma35d1serial_start_tx(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 ier;
> +	struct circ_buf *xmit = &up->port.state->xmit;
> +
> +	ier = serial_in(up, UART_REG_IER);
> +	serial_out(up, UART_REG_IER, ier & ~THRE_IEN);
> +	if (uart_circ_chars_pending(xmit) <
> +	    (16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0x3F)))

You look like you need a helper for this computation (hint: GENMASK()). 
What do those magic constants mean?

> +		transmit_chars(up);
> +	serial_out(up, UART_REG_IER, ier | THRE_IEN);
> +}
> +
> +static void ma35d1serial_stop_rx(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;

Bah. Nah.

> +
> +	serial_out(up, UART_REG_IER, serial_in(up, UART_REG_IER) & ~RDA_IEN);
> +}
> +
> +static void
> +receive_chars(struct uart_ma35d1_port *up)
> +{
> +	u8 ch;
> +	u32 fsr;
> +	u32 isr;
> +	u32 dcnt;
> +	char flag;

flag is u8 too. And a reverse xmas tree, please. Actually, you can put 
all those u32 to a single line.

> +
> +	isr = serial_in(up, UART_REG_ISR);
> +	fsr = serial_in(up, UART_REG_FSR);
> +
> +	while (!(fsr & RX_EMPTY)) {
> +		flag = TTY_NORMAL;
> +		up->port.icount.rx++;
> +
> +		if (unlikely(fsr & (BIF | FEF | PEF | RX_OVER_IF))) {
> +			if (fsr & BIF) {
> +				serial_out(up, UART_REG_FSR, BIF);
> +				up->port.icount.brk++;
> +				if (uart_handle_break(&up->port))
> +					continue;
> +			}
> +			if (fsr & FEF) {
> +				serial_out(up, UART_REG_FSR, FEF);
> +				up->port.icount.frame++;
> +			}
> +			if (fsr & PEF) {
> +				serial_out(up, UART_REG_FSR, PEF);
> +				up->port.icount.parity++;
> +			}
> +			if (fsr & RX_OVER_IF) {
> +				serial_out(up, UART_REG_FSR, RX_OVER_IF);
> +				up->port.icount.overrun++;
> +			}
> +			if (fsr & BIF)
> +				flag = TTY_BREAK;
> +			if (fsr & PEF)
> +				flag = TTY_PARITY;
> +			if (fsr & FEF)
> +				flag = TTY_FRAME;
> +		}
> +		ch = (u8)serial_in(up, UART_REG_RBR);
> +		if (uart_handle_sysrq_char(&up->port, ch))
> +			continue;
> +
> +		uart_insert_char(&up->port, fsr, RX_OVER_IF, ch, flag);

No lock needed?

> +		up->rx_count++;
> +		dcnt = (serial_in(up, UART_REG_FSR) >> 8) & 0x3f;

More magic constants. No.

> +		if (up->rx_count > 1023) {
> +			spin_lock(&up->port.lock);
> +			tty_flip_buffer_push(&up->port.state->port);
> +			spin_unlock(&up->port.lock);
> +			up->rx_count = 0;
> +			if ((isr & RXTO_IF) && (dcnt == 0))
> +				goto tout_end;
> +		}
> +		if (isr & RDA_IF) {
> +			if (dcnt == 1)
> +				return;
> +		}
> +		fsr = serial_in(up, UART_REG_FSR);
> +	}
> +	spin_lock(&up->port.lock);
> +	tty_flip_buffer_push(&up->port.state->port);
> +	spin_unlock(&up->port.lock);
> +tout_end:
> +	up->rx_count = 0;
> +}
> +
> +static void transmit_chars(struct uart_ma35d1_port *up)

Why this cannot use uart_port_tx()?

> +{
> +	struct circ_buf *xmit = &up->port.state->xmit;
> +	int count = 16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0xF);
> +
> +	if (serial_in(up, UART_REG_FSR) & TX_FULL)
> +		count = 0;
> +	if (up->port.x_char) {
> +		serial_out(up, UART_REG_THR, up->port.x_char);
> +		up->port.icount.tx++;
> +		up->port.x_char = 0;
> +		return;
> +	}
> +	if (uart_tx_stopped(&up->port)) {
> +		ma35d1serial_stop_tx(&up->port);
> +		return;
> +	}
> +	if (uart_circ_empty(xmit)) {
> +		__stop_tx(up);
> +		return;
> +	}
> +	while (count > 0) {
> +		serial_out(up, UART_REG_THR, xmit->buf[xmit->tail]);
> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +		up->port.icount.tx++;
> +		count--;
> +		if (uart_circ_empty(xmit))
> +			break;
> +	}
> +	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +		uart_write_wakeup(&up->port);
> +	if (uart_circ_empty(xmit))
> +		__stop_tx(up);
> +}
> +
> +static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)dev_id;
> +	u32 isr, fsr;
> +
> +	isr = serial_in(up, UART_REG_ISR);
> +	fsr = serial_in(up, UART_REG_FSR);
> +	if (isr & (RDA_IF | RXTO_IF))
> +		receive_chars(up);
> +	if (isr & THRE_INT)
> +		transmit_chars(up);
> +	if (fsr & (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF))
> +		serial_out(up, UART_REG_FSR,
> +			   (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF));
> +
> +	return IRQ_HANDLED;

You give no way for OS to disable the irq when the HW goes crazy. I.e. 
you should return IRQ_HANDLED only when you really handled the irq.

> +}
...
> +static u32 ma35d1serial_get_mctrl(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 status;
> +	u32 ret = 0;
> +
> +	status = serial_in(up, UART_REG_MSR);
> +	if (!(status & 0x10))

0x10 is magic.

> +		ret |= TIOCM_CTS;
> +	return ret;
> +}
> +
> +static void ma35d1serial_set_mctrl(struct uart_port *port, u32 mctrl)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 mcr = 0;
> +	u32 ier = 0;
> +
> +	if (mctrl & TIOCM_RTS) {
> +		/* set RTS high level trigger */
> +		mcr = serial_in(up, UART_REG_MCR);
> +		mcr |= 0x200;
> +		mcr &= ~(0x2);
> +	}
> +	if (up->mcr & UART_MCR_AFE) {
> +		/* set RTS high level trigger */
> +		mcr = serial_in(up, UART_REG_MCR);
> +		mcr |= 0x200;
> +		mcr &= ~(0x2);

This is repeated. Parentheses are superfluous. And again, 0x200, 0x2 are 
magic.

> +
> +		/* enable CTS/RTS auto-flow control */
> +		serial_out(up, UART_REG_IER,
> +			   (serial_in(up, UART_REG_IER) | (0x3000)));
> +
> +		/* Set hardware flow control */
> +		up->port.flags |= UPF_HARD_FLOW;
> +	} else {
> +		/* disable CTS/RTS auto-flow control */
> +		ier = serial_in(up, UART_REG_IER);
> +		ier &= ~(0x3000);

Detto.

> +		serial_out(up, UART_REG_IER, ier);
> +
> +		/* un-set hardware flow control */
> +		up->port.flags &= ~UPF_HARD_FLOW;
> +	}
> +
> +	/* set CTS high level trigger */
> +	serial_out(up, UART_REG_MSR, (serial_in(up, UART_REG_MSR) | (0x100)));
> +	serial_out(up, UART_REG_MCR, mcr);
> +}
...
> +static int ma35d1serial_startup(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	struct tty_struct *tty = port->state->port.tty;
> +	int retval;
> +
> +	/* Reset FIFO */
> +	serial_out(up, UART_REG_FCR, TFR | RFR /* | RX_DIS */);

So why not RX_DIS?

> +
> +	/* Clear pending interrupts */
> +	serial_out(up, UART_REG_ISR, 0xFFFFFFFF);
> +
> +	retval = request_irq(port->irq, ma35d1serial_interrupt, 0,
> +			     tty ? tty->name : "ma35d1_serial", port);
> +	if (retval) {
> +		dev_err(up->port.dev, "request irq failed.\n");
> +		return retval;
> +	}
> +
> +	/* Now, initialize the UART */
> +	/* FIFO trigger level 4 byte */
> +	/* RTS trigger level 8 bytes */
> +	serial_out(up, UART_REG_FCR,
> +		   serial_in(up, UART_REG_FCR) | 0x10 | 0x20000);
> +	serial_out(up, UART_REG_LCR, 0x7); /* 8 bit */
> +	serial_out(up, UART_REG_TOR, 0x40);

You know what.

> +	serial_out(up, UART_REG_IER,
> +		   RTO_IEN | RDA_IEN | TIME_OUT_EN | BUFERR_IEN);
> +	return 0;
> +}
> +
> +static void ma35d1serial_shutdown(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +
> +	free_irq(port->irq, port);
> +
> +	/* Disable interrupts from this port */
> +	serial_out(up, UART_REG_IER, 0);

The two lines are switched, IMO. First disable HW, then let the ISR 
finish and free it.

> +}
> +
> +static u32 ma35d1serial_get_divisor(struct uart_port *port, u32 baud)
> +{
> +	u32 quot;
> +
> +	quot = (port->uartclk / baud) - 2;
> +	return quot;

quot variable is completely superfluous.

> +}
> +
> +static void ma35d1serial_set_termios(struct uart_port *port,
> +				     struct ktermios *termios,
> +				     const struct ktermios *old)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 lcr = 0;
> +	unsigned long flags;
> +	u32 baud, quot;
> +
> +	switch (termios->c_cflag & CSIZE) {
> +	case CS5:
> +		lcr = 0;
> +		break;
> +	case CS6:
> +		lcr |= 1;
> +		break;
> +	case CS7:
> +		lcr |= 2;
> +		break;
> +	case CS8:
> +	default:
> +		lcr |= 3;
> +		break;
> +	}

IOW:
lcr = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));

> +
> +	if (termios->c_cflag & CSTOPB)
> +		lcr |= NSB;
> +	if (termios->c_cflag & PARENB)
> +		lcr |= PBE;
> +	if (!(termios->c_cflag & PARODD))
> +		lcr |= EPE;
> +	if (termios->c_cflag & CMSPAR)
> +		lcr |= SPE;
> +
> +	baud = uart_get_baud_rate(port, termios, old, port->uartclk / 0xffff,
> +				  port->uartclk / 11);
> +
> +	quot = ma35d1serial_get_divisor(port, baud);
> +
> +	/*
> +	 * Ok, we're now changing the port state.  Do it with
> +	 * interrupts disabled.
> +	 */
> +	spin_lock_irqsave(&up->port.lock, flags);
> +
> +	up->port.read_status_mask = RX_OVER_IF;
> +	if (termios->c_iflag & INPCK)
> +		up->port.read_status_mask |= FEF | PEF;
> +	if (termios->c_iflag & (BRKINT | PARMRK))
> +		up->port.read_status_mask |= BIF;
> +
> +	/*
> +	 * Characteres to ignore
> +	 */
> +	up->port.ignore_status_mask = 0;
> +	if (termios->c_iflag & IGNPAR)
> +		up->port.ignore_status_mask |= FEF | PEF;
> +	if (termios->c_iflag & IGNBRK) {
> +		up->port.ignore_status_mask |= BIF;
> +		/*
> +		 * If we're ignoring parity and break indicators,
> +		 * ignore overruns too (for real raw support).
> +		 */
> +		if (termios->c_iflag & IGNPAR)
> +			up->port.ignore_status_mask |= RX_OVER_IF;
> +	}
> +	if (termios->c_cflag & CRTSCTS)
> +		up->mcr |= UART_MCR_AFE;
> +	else
> +		up->mcr &= ~UART_MCR_AFE;
> +
> +	ma35d1serial_set_mctrl(&up->port, up->port.mctrl);
> +	serial_out(up, UART_REG_BAUD, quot | 0x30000000);
> +	serial_out(up, UART_REG_LCR, lcr);
> +	spin_unlock_irqrestore(&up->port.lock, flags);
> +}
...
> +static void ma35d1serial_config_port(struct uart_port *port, int flags)
> +{
> +	int ret;
> +
> +	/*
> +	 * Find the region that we can probe for.  This in turn
> +	 * tells us whether we can probe for the type of port.
> +	 */
> +	ret = ma35d1serial_request_port(port);
> +	if (ret < 0)
> +		return;

ma35d1serial_request_port() does nothing. You can remove it altogether.

> +	port->type = PORT_MA35D1;
> +}


> +static int ma35d1serial_ioctl(struct uart_port *port, u32 cmd, unsigned long arg)
> +{
> +	switch (cmd) {
> +	default:
> +		return -ENOIOCTLCMD;
> +	}
> +	return 0;
> +}

Drop that completely.

> +static void
> +ma35d1serial_console_init_port(void)
> +{
> +	int i = 0;
> +	struct device_node *np;
> +
> +	for_each_matching_node(np, ma35d1_serial_of_match) {
> +		if (ma35d1serial_uart_nodes[i] == NULL) {
> +			ma35d1serial_uart_nodes[i] = np;
> +			i++;

Unless the dt is broken, this is OK. But I would add a sanity check to i.

> +		}
> +	}
> +}
...
> +/*
> + * Register a set of serial devices attached to a platform device.
> + * The list is terminated with a zero flags entry, which means we expect
> + * all entries to have at least UPF_BOOT_AUTOCONF set.
> + */
> +static int ma35d1serial_probe(struct platform_device *pdev)
> +{
> +	struct resource *res_mem;
> +	struct uart_ma35d1_port *up;
> +	int ret;
> +	struct clk *clk;
> +	int err;
> +
> +	if (pdev->dev.of_node) {
> +		ret = of_alias_get_id(pdev->dev.of_node, "serial");
> +		if (ret < 0) {
> +			dev_err(&pdev->dev,
> +				"failed to get alias/pdev id, errno %d\n",
> +				ret);
> +		return ret;
> +		}
> +	}
> +	up = &ma35d1serial_ports[ret];
> +	up->port.line = ret;
> +	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res_mem)
> +		return -ENODEV;
> +
> +	up->port.iobase = res_mem->start;
> +	up->port.membase = ioremap(up->port.iobase, 0x10000);
> +	up->port.ops = &ma35d1serial_ops;
> +
> +	spin_lock_init(&up->port.lock);
> +
> +	clk = of_clk_get(pdev->dev.of_node, 0);
> +	if (IS_ERR(clk)) {
> +		err = PTR_ERR(clk);
> +		dev_err(&pdev->dev, "failed to get core clk: %d\n", err);
> +		return -ENOENT;
> +	}
> +	err = clk_prepare_enable(clk);
> +	if (err)
> +		return -ENOENT;
> +
> +	if (up->port.line != 0)
> +		up->port.uartclk = clk_get_rate(clk);
> +	up->port.irq = platform_get_irq(pdev, 0);
> +	up->port.dev = &pdev->dev;
> +	up->port.flags = UPF_BOOT_AUTOCONF;
> +	up->port.rs485_config = ma35d1serial_config_rs485;
> +	ret = uart_add_one_port(&ma35d1serial_reg, &up->port);

What if this fails?

> +	platform_set_drvdata(pdev, up);
> +	return 0;
> +}
> +
> +/*
> + * Remove serial ports registered against a platform device.
> + */
> +static int ma35d1serial_remove(struct platform_device *dev)
> +{
> +	int i;
> +	struct uart_port *port = platform_get_drvdata(dev);
> +
> +	free_irq(port->irq, port);

Hmm, this doesn't look right. You did that already, or?

> +	for (i = 0; i < UART_NR; i++) {
> +		struct uart_ma35d1_port *up = &ma35d1serial_ports[i];
> +
> +		if (up->port.dev == &dev->dev)

You did platform_set_drvdata(), so why all this?

> +			uart_remove_one_port(&ma35d1serial_reg, &up->port);
> +	}
> +	return 0;
> +}

regards,
-- 
js
suse labs


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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-15  7:28 ` [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings Jacky Huang
@ 2023-03-15 21:59   ` Stephen Boyd
  2023-03-16  3:24     ` Jacky Huang
  2023-03-16  7:35   ` Krzysztof Kozlowski
  1 sibling, 1 reply; 89+ messages in thread
From: Stephen Boyd @ 2023-03-15 21:59 UTC (permalink / raw)
  To: Jacky Huang, gregkh, jirislaby, krzysztof.kozlowski+dt, lee,
	mturquette, p.zabel, robh+dt
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Quoting Jacky Huang (2023-03-15 00:28:55)
> diff --git a/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
> new file mode 100644
> index 000000000000..5c2dea071b38
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
> @@ -0,0 +1,83 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/clock/nuvoton,ma35d1-clk.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nuvoton MA35D1 Clock Controller Module Binding

Binding is redundant.

> +
> +maintainers:
> +  - Chi-Fang Li <cfli0@nuvoton.com>
> +  - Jacky Huang <ychuang3@nuvoton.com>
> +
> +description: |
> +  The MA35D1 clock controller generates clocks for the whole chip,
> +  including system clocks and all peripheral clocks.
> +
> +  See also:
> +    include/dt-bindings/clock/ma35d1-clk.h
> +
> +properties:
> +  compatible:
> +    items:
> +      - const: nuvoton,ma35d1-clk
> +      - const: syscon

Does it really need to be a syscon?

> +
> +  reg:
> +    maxItems: 1
> +
> +  "#clock-cells":
> +    const: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  clock-names:
> +    const: clk_hxt
> +
> +  assigned-clocks:
> +    maxItems: 5
> +
> +  assigned-clock-rates:
> +    maxItems: 5

I hope the assigned clocks properties can be left out of this doc.

> +
> +  nuvoton,pll-mode:
> +    description:
> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
> +      integer mode, 1 is for fractional mode, and 2 is for spread
> +      spectrum mode.
> +    $ref: /schemas/types.yaml#/definitions/uint32-array
> +    maxItems: 5
> +    items:
> +      minimum: 0
> +      maximum: 2

Why not use a string?

> +
> +  nuvoton,sys:
> +    description:
> +      Phandle to the system management controller.
> +    $ref: "/schemas/types.yaml#/definitions/phandle-array"
> +
> +required:
> +  - compatible
> +  - reg
> +  - "#clock-cells"
> +  - clocks
> +  - clock-names
> +  - nuvoton,sys
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>

No need to include this header right?

> +
> +    clk: clock-controller@40460200 {

Drop the label?

> +        compatible = "nuvoton,ma35d1-clk", "syscon";
> +        reg = <0x40460200 0x100>;
> +        #clock-cells = <1>;
> +        clocks = <&clk_hxt>;
> +        clock-names = "clk_hxt";
> +        nuvoton,sys = <&sys>;
> +    };

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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-15  7:28 ` [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
@ 2023-03-15 22:07   ` kernel test robot
  2023-03-15 22:30   ` Stephen Boyd
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 89+ messages in thread
From: kernel test robot @ 2023-03-15 22:07 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: oe-kbuild-all, devicetree, linux-clk, linux-kernel, linux-serial,
	schung, Jacky Huang

Hi Jacky,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on robh/for-next]
[also build test WARNING on clk/clk-next tty/tty-testing tty/tty-next tty/tty-linus linus/master v6.3-rc2 next-20230315]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Jacky-Huang/arm64-Kconfig-platforms-Add-config-for-Nuvoton-MA35-platform/20230315-153355
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20230315072902.9298-13-ychuang570808%40gmail.com
patch subject: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
config: arm64-allyesconfig (https://download.01.org/0day-ci/archive/20230316/202303160558.ECjzXS4Z-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/696d8d2916e32766dba52bc51453176af883ae96
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Jacky-Huang/arm64-Kconfig-platforms-Add-config-for-Nuvoton-MA35-platform/20230315-153355
        git checkout 696d8d2916e32766dba52bc51453176af883ae96
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm64 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm64 SHELL=/bin/bash drivers/clk/nuvoton/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202303160558.ECjzXS4Z-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/clk/nuvoton/clk-ma35d1-pll.c:79:15: warning: no previous prototype for 'CLK_GetPLLFreq_SMICPLL' [-Wmissing-prototypes]
      79 | unsigned long CLK_GetPLLFreq_SMICPLL(struct ma35d1_clk_pll *pll,
         |               ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/clk/nuvoton/clk-ma35d1-pll.c:104:15: warning: no previous prototype for 'CLK_CalPLLFreq_Mode0' [-Wmissing-prototypes]
     104 | unsigned long CLK_CalPLLFreq_Mode0(unsigned long PllSrcClk,
         |               ^~~~~~~~~~~~~~~~~~~~
>> drivers/clk/nuvoton/clk-ma35d1-pll.c:185:15: warning: no previous prototype for 'CLK_CalPLLFreq_Mode1' [-Wmissing-prototypes]
     185 | unsigned long CLK_CalPLLFreq_Mode1(unsigned long PllSrcClk,
         |               ^~~~~~~~~~~~~~~~~~~~
>> drivers/clk/nuvoton/clk-ma35d1-pll.c:231:15: warning: no previous prototype for 'CLK_CalPLLFreq_Mode2' [-Wmissing-prototypes]
     231 | unsigned long CLK_CalPLLFreq_Mode2(unsigned long PllSrcClk,
         |               ^~~~~~~~~~~~~~~~~~~~
>> drivers/clk/nuvoton/clk-ma35d1-pll.c:305:15: warning: no previous prototype for 'CLK_SetPLLFreq' [-Wmissing-prototypes]
     305 | unsigned long CLK_SetPLLFreq(struct ma35d1_clk_pll *pll,
         |               ^~~~~~~~~~~~~~
>> drivers/clk/nuvoton/clk-ma35d1-pll.c:347:15: warning: no previous prototype for 'CLK_GetPLLFreq_VSIPLL' [-Wmissing-prototypes]
     347 | unsigned long CLK_GetPLLFreq_VSIPLL(struct ma35d1_clk_pll *pll,
         |               ^~~~~~~~~~~~~~~~~~~~~
   drivers/clk/nuvoton/clk-ma35d1-pll.c: In function 'CLK_GetPLLFreq_VSIPLL':
>> drivers/clk/nuvoton/clk-ma35d1-pll.c:350:44: warning: variable 'u32FMOD' set but not used [-Wunused-but-set-variable]
     350 |         u32 u32M, u32N, u32P, u32X, u32SR, u32FMOD;
         |                                            ^~~~~~~
>> drivers/clk/nuvoton/clk-ma35d1-pll.c:350:37: warning: variable 'u32SR' set but not used [-Wunused-but-set-variable]
     350 |         u32 u32M, u32N, u32P, u32X, u32SR, u32FMOD;
         |                                     ^~~~~


vim +/CLK_GetPLLFreq_SMICPLL +79 drivers/clk/nuvoton/clk-ma35d1-pll.c

    77	
    78	/* SMIC PLL for CAPLL */
  > 79	unsigned long CLK_GetPLLFreq_SMICPLL(struct ma35d1_clk_pll *pll,
    80					     unsigned long PllSrcClk)
    81	{
    82		u32 u32M, u32N, u32P, u32OutDiv;
    83		u32 val;
    84		unsigned long u64PllClk;
    85		u32 clk_div_table[] = { 1, 2, 4, 8};
    86	
    87		val = __raw_readl(pll->ctl0_base);
    88	
    89		u32N = FIELD_GET(PLL0CTL0_FBDIV_MSK, val);
    90		u32M = FIELD_GET(PLL0CTL0_INDIV_MSK, val);
    91		u32P = FIELD_GET(PLL0CTL0_OUTDIV_MSK, val);
    92		u32OutDiv = clk_div_table[u32P];
    93	
    94		if (val & PLL0CTL0_BP_MSK) {
    95			u64PllClk = PllSrcClk;
    96		} else {
    97			u64PllClk = PllSrcClk * u32N;
    98			do_div(u64PllClk, u32M * u32OutDiv);
    99		}
   100		return u64PllClk;
   101	}
   102	
   103	/* VSI-PLL: INTEGER_MODE */
 > 104	unsigned long CLK_CalPLLFreq_Mode0(unsigned long PllSrcClk,
   105					   unsigned long u64PllFreq, u32 *u32Reg)
   106	{
   107		u32 u32TmpM, u32TmpN, u32TmpP;
   108		u32 u32RngMinN, u32RngMinM, u32RngMinP;
   109		u32 u32RngMaxN, u32RngMaxM, u32RngMaxP;
   110		u32 u32Tmp, u32Min, u32MinN, u32MinM, u32MinP;
   111		unsigned long u64PllClk;
   112		unsigned long u64Con1, u64Con2, u64Con3;
   113	
   114		u64PllClk = 0;
   115		u32Min = (u32) -1;
   116	
   117		if (!((u64PllFreq >= VSIPLL_FCLKO_MIN_FREQ) &&
   118		    (u64PllFreq <= VSIPLL_FCLKO_MAX_FREQ))) {
   119			u32Reg[0] = ma35d1pll_freq[0].ctl0_reg;
   120			u32Reg[1] = ma35d1pll_freq[0].ctl1_reg;
   121			u64PllClk = ma35d1pll_freq[0].freq;
   122			return u64PllClk;
   123		}
   124	
   125		u32RngMinM = 1UL;
   126		u32RngMaxM = 63UL;
   127		u32RngMinM = ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) > 1) ?
   128			     (PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) : 1;
   129		u32RngMaxM = ((PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) < u32RngMaxM) ?
   130			     (PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) : u32RngMaxM;
   131	
   132		for (u32TmpM = u32RngMinM; u32TmpM < (u32RngMaxM + 1); u32TmpM++) {
   133			u64Con1 = PllSrcClk / u32TmpM;
   134			u32RngMinN = 16UL;
   135			u32RngMaxN = 2047UL;
   136			u32RngMinN = ((VSIPLL_FCLK_MIN_FREQ / u64Con1) > u32RngMinN) ?
   137				     (VSIPLL_FCLK_MIN_FREQ / u64Con1) : u32RngMinN;
   138			u32RngMaxN = ((VSIPLL_FCLK_MAX_FREQ / u64Con1) < u32RngMaxN) ?
   139				     (VSIPLL_FCLK_MAX_FREQ / u64Con1) : u32RngMaxN;
   140	
   141			for (u32TmpN = u32RngMinN; u32TmpN < (u32RngMaxN + 1);
   142			     u32TmpN++) {
   143				u64Con2 = u64Con1 * u32TmpN;
   144				u32RngMinP = 1UL;
   145				u32RngMaxP = 7UL;
   146				u32RngMinP = ((u64Con2 / VSIPLL_FCLKO_MAX_FREQ) > 1) ?
   147					      (u64Con2 / VSIPLL_FCLKO_MAX_FREQ) : 1;
   148				u32RngMaxP = ((u64Con2 / VSIPLL_FCLKO_MIN_FREQ) <
   149					      u32RngMaxP) ?
   150					      (u64Con2 / VSIPLL_FCLKO_MIN_FREQ) :
   151					      u32RngMaxP;
   152				for (u32TmpP = u32RngMinP; u32TmpP < (u32RngMaxP + 1);
   153				     u32TmpP++) {
   154					u64Con3 = u64Con2 / u32TmpP;
   155					if (u64Con3 > u64PllFreq)
   156						u32Tmp = u64Con3 - u64PllFreq;
   157					else
   158						u32Tmp = u64PllFreq - u64Con3;
   159	
   160					if (u32Tmp < u32Min) {
   161						u32Min = u32Tmp;
   162						u32MinM = u32TmpM;
   163						u32MinN = u32TmpN;
   164						u32MinP = u32TmpP;
   165	
   166						if (u32Min == 0UL) {
   167							u32Reg[0] = (u32MinM << 12) |
   168								    (u32MinN);
   169							u32Reg[1] = (u32MinP << 4);
   170							return ((PllSrcClk * u32MinN) /
   171								(u32MinP * u32MinM));
   172						}
   173					}
   174				}
   175			}
   176		}
   177	
   178		u32Reg[0] = (u32MinM << 12) | (u32MinN);
   179		u32Reg[1] = (u32MinP << 4);
   180		u64PllClk = (PllSrcClk * u32MinN) / (u32MinP * u32MinM);
   181		return u64PllClk;
   182	}
   183	
   184	/* VSI-PLL: FRACTIONAL_MODE */
 > 185	unsigned long CLK_CalPLLFreq_Mode1(unsigned long PllSrcClk,
   186					   unsigned long u64PllFreq, u32 *u32Reg)
   187	{
   188		unsigned long u64X, u64N, u64M, u64P, u64tmp;
   189		unsigned long u64PllClk, u64FCLKO;
   190		u32 u32FRAC;
   191	
   192		if (u64PllFreq > VSIPLL_FCLKO_MAX_FREQ) {
   193			u32Reg[0] = ma35d1pll_freq[1].ctl0_reg;
   194			u32Reg[1] = ma35d1pll_freq[1].ctl1_reg;
   195			u64PllClk = ma35d1pll_freq[1].freq;
   196			return u64PllClk;
   197		}
   198	
   199		if (u64PllFreq > (VSIPLL_FCLKO_MIN_FREQ/(100-1))) {
   200			u64FCLKO = u64PllFreq * ((VSIPLL_FCLKO_MIN_FREQ / u64PllFreq) +
   201				   ((VSIPLL_FCLKO_MIN_FREQ % u64PllFreq) ? 1 : 0));
   202		} else {
   203			pr_err("Failed to set rate %ld\n", u64PllFreq);
   204			return 0;
   205		}
   206	
   207		u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
   208		       ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
   209			((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));
   210	
   211		if ((PllSrcClk > (VSIPLL_FREFDIVM_MAX_FREQ * (64-1))) ||
   212		    (PllSrcClk < VSIPLL_FREFDIVM_MIN_FREQ1))
   213			return 0;
   214	
   215		u64M = (PllSrcClk <= VSIPLL_FREFDIVM_MAX_FREQ) ? 1 :
   216		       ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) +
   217		       ((PllSrcClk % VSIPLL_FREFDIVM_MAX_FREQ) ? 1 : 0));
   218	
   219		u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
   220		u64N = u64tmp / 1000;
   221		u64X = u64tmp % 1000;
   222		u32FRAC = ((u64X << 24) + 500) / 1000;
   223		u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
   224	
   225		u32Reg[0] = (u64M << 12) | (u64N);
   226		u32Reg[1] = (u64P << 4) | (u32FRAC << 8);
   227		return u64PllClk;
   228	}
   229	
   230	/* VSI-PLL: SS_MODE */
 > 231	unsigned long CLK_CalPLLFreq_Mode2(unsigned long PllSrcClk,
   232					   unsigned long u64PllFreq,
   233					   u32 u32SR, u32 u32Fmod, u32 *u32Reg)
   234	{
   235		unsigned long u64X, u64N, u64M, u64P, u64tmp, u64tmpP, u64tmpM;
   236		unsigned long u64SSRATE, u64SLOPE, u64PllClk, u64FCLKO;
   237		u32 u32FRAC, i;
   238	
   239		if (u64PllFreq >= VSIPLL_FCLKO_MAX_FREQ) {
   240			u32Reg[0] = ma35d1pll_freq[2].ctl0_reg;
   241			u32Reg[1] = ma35d1pll_freq[2].ctl1_reg;
   242			u32Reg[2] = ma35d1pll_freq[2].ctl2_reg;
   243			u64PllClk = ma35d1pll_freq[2].freq;
   244			return u64PllClk;
   245		}
   246	
   247		if (u64PllFreq < VSIPLL_FCLKO_MIN_FREQ) {
   248			u64FCLKO = 0;
   249			for (i = 2; i < 8; i++) {
   250				u64tmp = (i * u64PllFreq);
   251				if (u64tmp > VSIPLL_FCLKO_MIN_FREQ)
   252					u64FCLKO = u64tmp;
   253			}
   254			if (u64FCLKO == 0) {
   255				pr_err("Failed to set rate %ld\n", u64PllFreq);
   256				return 0;
   257			}
   258	
   259		} else
   260			u64FCLKO = u64PllFreq;
   261	
   262		u64P = 0;
   263		for (i = 1; i < 8; i++) {
   264			u64tmpP = i * u64FCLKO;
   265			if ((u64tmpP <= VSIPLL_FCLK_MAX_FREQ) &&
   266			    (u64tmpP >= VSIPLL_FCLK_MIN_FREQ)) {
   267				u64P = i;
   268				break;
   269			}
   270		}
   271	
   272		if (u64P == 0)
   273			return 0;
   274	
   275		u64M = 0;
   276		for (i = 1; i < 64; i++) {
   277			u64tmpM = PllSrcClk / i;
   278			if ((u64tmpM <= VSIPLL_FREFDIVM_MAX_FREQ) &&
   279			    (u64tmpM >= VSIPLL_FREFDIVM_MIN_FREQ1)) {
   280				u64M = i;
   281				break;
   282			}
   283		}
   284	
   285		if (u64M == 0)
   286			return 0;
   287	
   288		u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
   289		u64N = u64tmp / 1000;
   290		u64X = u64tmp % 1000;
   291		u32FRAC = ((u64X << 24) + 500) / 1000;
   292	
   293		u64SSRATE = ((PllSrcClk >> 1) / (u32Fmod * 2)) - 1;
   294		u64SLOPE = ((u64tmp * u32SR / u64SSRATE) << 24) / 100 / 1000;
   295	
   296		u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
   297	
   298		u32Reg[0] = (u64SSRATE << VSIPLLCTL0_SSRATE_POS) | (u64M <<
   299			     VSIPLLCTL0_INDIV_POS) | (u64N);
   300		u32Reg[1] = (u64P << VSIPLLCTL1_OUTDIV_POS) | (u32FRAC << VSIPLLCTL1_FRAC_POS);
   301		u32Reg[2] = u64SLOPE;
   302		return u64PllClk;
   303	}
   304	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-15  7:28 ` [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
  2023-03-15 22:07   ` kernel test robot
@ 2023-03-15 22:30   ` Stephen Boyd
  2023-03-17  3:07     ` Jacky Huang
  2023-03-16  7:51   ` Krzysztof Kozlowski
  2023-03-16 15:56   ` Ilpo Järvinen
  3 siblings, 1 reply; 89+ messages in thread
From: Stephen Boyd @ 2023-03-15 22:30 UTC (permalink / raw)
  To: Jacky Huang, gregkh, jirislaby, krzysztof.kozlowski+dt, lee,
	mturquette, p.zabel, robh+dt
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Quoting Jacky Huang (2023-03-15 00:28:59)
> diff --git a/drivers/clk/nuvoton/clk-ma35d1-divider.c b/drivers/clk/nuvoton/clk-ma35d1-divider.c
> new file mode 100644
> index 000000000000..5f4791531e47
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
> @@ -0,0 +1,144 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/spinlock.h>
> +
> +#include "clk-ma35d1.h"
> +
> +#define div_mask(width)                ((1 << (width)) - 1)

This is clk_div_mask()

> +
> +struct ma35d1_adc_clk_divider {
> +       struct clk_hw hw;
> +       void __iomem *reg;
> +       u8 shift;
> +       u8 width;
> +       u32 mask;
> +       const struct clk_div_table *table;
> +       spinlock_t *lock;
> +};
> +
> +#define to_ma35d1_adc_clk_divider(_hw) \
> +       container_of(_hw, struct ma35d1_adc_clk_divider, hw)
> +
> +static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw,
> +                                              unsigned long parent_rate)
> +{
> +       unsigned int val;
> +       struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
> +
> +       val = readl_relaxed(dclk->reg) >> dclk->shift;
> +       val &= div_mask(dclk->width);
> +       val += 1;
> +       return divider_recalc_rate(hw, parent_rate, val, dclk->table,
> +                                  CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
> +}
> +
> +static long ma35d1_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                    unsigned long *prate)
> +{
> +       struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
> +
> +       return divider_round_rate(hw, rate, prate, dclk->table,
> +                                 dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
> +}
> +
> +static int ma35d1_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                 unsigned long parent_rate)
> +{
> +       int value;
> +       unsigned long flags = 0;
> +       u32 data;
> +       struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
> +
> +       value = divider_get_val(rate, parent_rate, dclk->table,
> +                               dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
> +
> +       if (dclk->lock)
> +               spin_lock_irqsave(dclk->lock, flags);
> +
> +       data = readl_relaxed(dclk->reg);
> +       data &= ~(div_mask(dclk->width) << dclk->shift);
> +       data |= (value - 1) << dclk->shift;
> +       data |= dclk->mask;
> +
> +       writel_relaxed(data, dclk->reg);
> +
> +       if (dclk->lock)
> +               spin_unlock_irqrestore(dclk->lock, flags);
> +
> +       return 0;
> +}
> +
> +static const struct clk_ops ma35d1_adc_clkdiv_ops = {
> +       .recalc_rate = ma35d1_clkdiv_recalc_rate,
> +       .round_rate = ma35d1_clkdiv_round_rate,
> +       .set_rate = ma35d1_clkdiv_set_rate,
> +};
> +
> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
> +                                    const char *parent_name,
> +                                    unsigned long flags, void __iomem *reg,
> +                                    u8 shift, u8 width, u32 mask_bit)
> +{
> +       struct ma35d1_adc_clk_divider *div;
> +       struct clk_init_data init;
> +       struct clk_div_table *table;
> +       u32 max_div, min_div;
> +       struct clk_hw *hw;
> +       int ret;
> +       int i;
> +
> +       /* allocate the divider */

Please remove useless comment.

> +       div = kzalloc(sizeof(*div), GFP_KERNEL);
> +       if (!div)
> +               return ERR_PTR(-ENOMEM);
> +
> +       /* Init the divider table */

Please remove useless comment.

> +       max_div = div_mask(width) + 1;
> +       min_div = 1;
> +
> +       table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);

Use devm_ allocations please.

> +       if (!table) {
> +               kfree(div);
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       for (i = 0; i < max_div; i++) {
> +               table[i].val = (min_div + i);
> +               table[i].div = 2 * table[i].val;
> +       }
> +       table[max_div].val = 0;
> +       table[max_div].div = 0;
> +
> +       init.name = name;
> +       init.ops = &ma35d1_adc_clkdiv_ops;
> +       init.flags |= flags;
> +       init.parent_names = parent_name ? &parent_name : NULL;
> +       init.num_parents = parent_name ? 1 : 0;
> +
> +       /* struct ma35d1_adc_clk_divider assignments */

Please remove useless comment.

> +       div->reg = reg;
> +       div->shift = shift;
> +       div->width = width;
> +       div->mask = mask_bit ? BIT(mask_bit) : 0;
> +       div->lock = &ma35d1_lock;
> +       div->hw.init = &init;
> +       div->table = table;
> +
> +       /* Register the clock */

Please remove useless comment.

> +       hw = &div->hw;
> +       ret = clk_hw_register(NULL, hw);

Use devm_clk_hw_register()

> +       if (ret) {
> +               kfree(table);
> +               kfree(div);
> +               return ERR_PTR(ret);
> +       }
> +       return hw;
> +}
> diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> new file mode 100644
> index 000000000000..79e724b148fa
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> @@ -0,0 +1,534 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/clk.h>

Do you need to include this header?

> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/bitfield.h>
> +
> +#include "clk-ma35d1.h"
> +
> +#define to_ma35d1_clk_pll(clk) \
> +       (container_of(clk, struct ma35d1_clk_pll, clk))
> +
> +#define PLL0CTL0_FBDIV_MSK     GENMASK(7, 0)
> +#define PLL0CTL0_INDIV_MSK     GENMASK(11, 8)
> +#define PLL0CTL0_OUTDIV_MSK    GENMASK(13, 12)
> +#define PLL0CTL0_PD_MSK                BIT(16)
> +#define PLL0CTL0_BP_MSK                BIT(17)
> +#define PLLXCTL0_FBDIV_MSK     GENMASK(10, 0)
> +#define PLLXCTL0_INDIV_MSK     GENMASK(17, 12)
> +#define PLLXCTL0_MODE_MSK      GENMASK(19, 18)
> +#define PLLXCTL0_SSRATE_MSK    GENMASK(30, 20)
> +#define PLLXCTL1_PD_MSK                BIT(0)
> +#define PLLXCTL1_BP_MSK                BIT(1)
> +#define PLLXCTL1_OUTDIV_MSK    GENMASK(6, 4)
> +#define PLLXCTL1_FRAC_MSK      GENMASK(31, 8)
> +#define PLLXCTL2_SLOPE_MSK     GENMASK(23, 0)
> +
> +struct ma35d1_clk_pll {
> +       struct clk_hw hw;
> +       u8 type;
> +       u8 mode;
> +       unsigned long rate;
> +       void __iomem *ctl0_base;
> +       void __iomem *ctl1_base;
> +       void __iomem *ctl2_base;
> +       struct regmap *regmap;
> +};
> +
> +struct vsipll_freq_conf_reg_tbl {
> +       unsigned long freq;
> +       u8 mode;
> +       u32 ctl0_reg;
> +       u32 ctl1_reg;
> +       u32 ctl2_reg;
> +};
> +
> +static const struct vsipll_freq_conf_reg_tbl ma35d1pll_freq[] = {
> +       { 1000000000, VSIPLL_INTEGER_MODE, 0x307d, 0x10, 0 },
> +       { 884736000, VSIPLL_FRACTIONAL_MODE, 0x41024, 0xdd2f1b11, 0 },
> +       { 533000000, VSIPLL_SS_MODE, 0x12b8102c, 0x6aaaab20, 0x12317 },
> +       { }
> +};
> +
> +static void CLK_UnLockReg(struct ma35d1_clk_pll *pll)

Please don't use a mix of upper and lower case function names.
Everything should be lower case. Maybe the name should be

	ma35d1_clk_pll_unlock_reg()

> +{
> +       int ret;
> +
> +       /* Unlock PLL registers */
> +       do {
> +               regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x59);
> +               regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x16);
> +               regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x88);
> +               regmap_read(pll->regmap, REG_SYS_RLKTZNS, &ret);
> +       } while (ret == 0);
> +}
> +
> +static void CLK_LockReg(struct ma35d1_clk_pll *pll)
> +{

	ma35d1_clk_pll_lock_reg()

> +       /* Lock PLL registers */

Remove these worthless comments.

> +       regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x0);
> +}
> +
> +/* SMIC PLL for CAPLL */
> +unsigned long CLK_GetPLLFreq_SMICPLL(struct ma35d1_clk_pll *pll,
> +                                    unsigned long PllSrcClk)
> +{
> +       u32 u32M, u32N, u32P, u32OutDiv;

Variable names should not have the type in them. 'm', 'n', 'p', 'div'
should suffice.

> +       u32 val;
> +       unsigned long u64PllClk;
> +       u32 clk_div_table[] = { 1, 2, 4, 8};
> +
> +       val = __raw_readl(pll->ctl0_base);

Why do you need to use __raw_readl()? Just use readl() here.

> +
> +       u32N = FIELD_GET(PLL0CTL0_FBDIV_MSK, val);
> +       u32M = FIELD_GET(PLL0CTL0_INDIV_MSK, val);
> +       u32P = FIELD_GET(PLL0CTL0_OUTDIV_MSK, val);
> +       u32OutDiv = clk_div_table[u32P];
> +
> +       if (val & PLL0CTL0_BP_MSK) {
> +               u64PllClk = PllSrcClk;
> +       } else {
> +               u64PllClk = PllSrcClk * u32N;
> +               do_div(u64PllClk, u32M * u32OutDiv);
> +       }

Add a newline here.

> +       return u64PllClk;
> +}
> +
> +/* VSI-PLL: INTEGER_MODE */

I have no idea what this means.

> +unsigned long CLK_CalPLLFreq_Mode0(unsigned long PllSrcClk,
> +                                  unsigned long u64PllFreq, u32 *u32Reg)

Again, don't put types into the variable name.

> +{
> +       u32 u32TmpM, u32TmpN, u32TmpP;
> +       u32 u32RngMinN, u32RngMinM, u32RngMinP;
> +       u32 u32RngMaxN, u32RngMaxM, u32RngMaxP;
> +       u32 u32Tmp, u32Min, u32MinN, u32MinM, u32MinP;
> +       unsigned long u64PllClk;
> +       unsigned long u64Con1, u64Con2, u64Con3;

My eyes! Seriously, kernel style is not this way. Did checkpatch.pl pass
on this?

> +
> +       u64PllClk = 0;
> +       u32Min = (u32) -1;
> +
> +       if (!((u64PllFreq >= VSIPLL_FCLKO_MIN_FREQ) &&
> +           (u64PllFreq <= VSIPLL_FCLKO_MAX_FREQ))) {
> +               u32Reg[0] = ma35d1pll_freq[0].ctl0_reg;
> +               u32Reg[1] = ma35d1pll_freq[0].ctl1_reg;
> +               u64PllClk = ma35d1pll_freq[0].freq;
> +               return u64PllClk;
> +       }
> +
> +       u32RngMinM = 1UL;
> +       u32RngMaxM = 63UL;
> +       u32RngMinM = ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) > 1) ?
> +                    (PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) : 1;
> +       u32RngMaxM = ((PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) < u32RngMaxM) ?
> +                    (PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) : u32RngMaxM;
> +
> +       for (u32TmpM = u32RngMinM; u32TmpM < (u32RngMaxM + 1); u32TmpM++) {
> +               u64Con1 = PllSrcClk / u32TmpM;
> +               u32RngMinN = 16UL;
> +               u32RngMaxN = 2047UL;
> +               u32RngMinN = ((VSIPLL_FCLK_MIN_FREQ / u64Con1) > u32RngMinN) ?
> +                            (VSIPLL_FCLK_MIN_FREQ / u64Con1) : u32RngMinN;
> +               u32RngMaxN = ((VSIPLL_FCLK_MAX_FREQ / u64Con1) < u32RngMaxN) ?
> +                            (VSIPLL_FCLK_MAX_FREQ / u64Con1) : u32RngMaxN;

Is this clamp()?

> +
> +               for (u32TmpN = u32RngMinN; u32TmpN < (u32RngMaxN + 1);
> +                    u32TmpN++) {
> +                       u64Con2 = u64Con1 * u32TmpN;
> +                       u32RngMinP = 1UL;
> +                       u32RngMaxP = 7UL;
> +                       u32RngMinP = ((u64Con2 / VSIPLL_FCLKO_MAX_FREQ) > 1) ?
> +                                     (u64Con2 / VSIPLL_FCLKO_MAX_FREQ) : 1;

Is this clamp()?

> +                       u32RngMaxP = ((u64Con2 / VSIPLL_FCLKO_MIN_FREQ) <
> +                                     u32RngMaxP) ?
> +                                     (u64Con2 / VSIPLL_FCLKO_MIN_FREQ) :
> +                                     u32RngMaxP;
> +                       for (u32TmpP = u32RngMinP; u32TmpP < (u32RngMaxP + 1);
> +                            u32TmpP++) {
> +                               u64Con3 = u64Con2 / u32TmpP;
> +                               if (u64Con3 > u64PllFreq)
> +                                       u32Tmp = u64Con3 - u64PllFreq;
> +                               else
> +                                       u32Tmp = u64PllFreq - u64Con3;
> +
> +                               if (u32Tmp < u32Min) {
> +                                       u32Min = u32Tmp;
> +                                       u32MinM = u32TmpM;
> +                                       u32MinN = u32TmpN;
> +                                       u32MinP = u32TmpP;
> +
> +                                       if (u32Min == 0UL) {
> +                                               u32Reg[0] = (u32MinM << 12) |
> +                                                           (u32MinN);
> +                                               u32Reg[1] = (u32MinP << 4);
> +                                               return ((PllSrcClk * u32MinN) /
> +                                                       (u32MinP * u32MinM));
> +                                       }
> +                               }
> +                       }
> +               }
> +       }

It's too hard to read this code. 

> +
> +       u32Reg[0] = (u32MinM << 12) | (u32MinN);

FIELD_PREP?

> +       u32Reg[1] = (u32MinP << 4);

ditto?

> +       u64PllClk = (PllSrcClk * u32MinN) / (u32MinP * u32MinM);
> +       return u64PllClk;
> +}
> +
> +/* VSI-PLL: FRACTIONAL_MODE */
> +unsigned long CLK_CalPLLFreq_Mode1(unsigned long PllSrcClk,
> +                                  unsigned long u64PllFreq, u32 *u32Reg)
> +{
> +       unsigned long u64X, u64N, u64M, u64P, u64tmp;
> +       unsigned long u64PllClk, u64FCLKO;
> +       u32 u32FRAC;
> +
> +       if (u64PllFreq > VSIPLL_FCLKO_MAX_FREQ) {
> +               u32Reg[0] = ma35d1pll_freq[1].ctl0_reg;
> +               u32Reg[1] = ma35d1pll_freq[1].ctl1_reg;
> +               u64PllClk = ma35d1pll_freq[1].freq;
> +               return u64PllClk;
> +       }
> +
> +       if (u64PllFreq > (VSIPLL_FCLKO_MIN_FREQ/(100-1))) {

Use a local variable for the right hand side of the comparison.

> +               u64FCLKO = u64PllFreq * ((VSIPLL_FCLKO_MIN_FREQ / u64PllFreq) +
> +                          ((VSIPLL_FCLKO_MIN_FREQ % u64PllFreq) ? 1 : 0));

Is this DIV_ROUND_UP() or something like that?

> +       } else {
> +               pr_err("Failed to set rate %ld\n", u64PllFreq);
> +               return 0;
> +       }
> +
> +       u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
> +              ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
> +               ((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));
> +
> +       if ((PllSrcClk > (VSIPLL_FREFDIVM_MAX_FREQ * (64-1))) ||
> +           (PllSrcClk < VSIPLL_FREFDIVM_MIN_FREQ1))
> +               return 0;
> +
[...]
> +               break;
> +       }
> +
> +       return pllfreq;
> +}
> +
> +static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                     unsigned long *prate)
> +{
> +       return rate;

This needs to do actual math and figure out that some rate will not
work and calculate what the rate will actually be if clk_set_rate() is
called with 'rate'.

> +}
> +
> +static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
> +{
> +       struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +       u32 val = __raw_readl(pll->ctl1_base);
> +
> +       return (val & VSIPLLCTL1_PD_MSK) ? 0 : 1;
> +}
> +
> +static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
> +{
> +       struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +       u32 val;
> +
> +       if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
> +               pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
> +               return -EACCES;
> +       }
> +
> +       CLK_UnLockReg(pll);
> +       val = __raw_readl(pll->ctl1_base);
> +       val &= ~VSIPLLCTL1_PD_MSK;
> +       __raw_writel(val, pll->ctl1_base);
> +       CLK_LockReg(pll);
> +       return 0;
> +}
> +
> +static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
> +{
> +       struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +       u32 val;
> +
> +       if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
> +               pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
> +       } else {
> +               val = __raw_readl(pll->ctl1_base);
> +               val |= VSIPLLCTL1_PD_MSK;
> +               __raw_writel(val, pll->ctl1_base);
> +       }
> +}
> +
> +static const struct clk_ops ma35d1_clk_pll_ops = {
> +       .is_prepared = ma35d1_clk_pll_is_prepared,
> +       .prepare = ma35d1_clk_pll_prepare,
> +       .unprepare = ma35d1_clk_pll_unprepare,
> +       .set_rate = ma35d1_clk_pll_set_rate,
> +       .recalc_rate = ma35d1_clk_pll_recalc_rate,
> +       .round_rate = ma35d1_clk_pll_round_rate,
> +};
> +
> +struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type,
> +                                 u8 u8mode, const char *name,
> +                                 const char *parent,
> +                                 unsigned long targetFreq,
> +                                 void __iomem *base,
> +                                 struct regmap *regmap)
> +{
> +       struct ma35d1_clk_pll *pll;
> +       struct clk_hw *hw;
> +       struct clk_init_data init;
> +       int ret;
> +
> +       pll = kmalloc(sizeof(*pll), GFP_KERNEL);
> +       if (!pll)
> +               return ERR_PTR(-ENOMEM);
> +
> +       pll->type = type;
> +       pll->mode = u8mode;
> +       pll->rate = targetFreq;
> +       pll->ctl0_base = base + VSIPLL_CTL0;
> +       pll->ctl1_base = base + VSIPLL_CTL1;
> +       pll->ctl2_base = base + VSIPLL_CTL2;
> +       pll->regmap = regmap;
> +
> +       init.name = name;
> +       init.flags = 0;
> +       init.parent_names = &parent;
> +       init.num_parents = 1;
> +       init.ops = &ma35d1_clk_pll_ops;
> +       pll->hw.init = &init;
> +       hw = &pll->hw;
> +
> +       ret = clk_hw_register(NULL, hw);
> +       if (ret) {
> +               pr_err("failed to register vsi-pll clock!!!\n");
> +               kfree(pll);
> +               return ERR_PTR(ret);
> +       }
> +       return hw;
> +}
> diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
> new file mode 100644
> index 000000000000..ac8154458b81
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1.c
> @@ -0,0 +1,970 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/clk.h>

I don't see any clk consumer usage. Please remove.

> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

I don't see any clkdev usage. Please remove.

> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> +
> +#include "clk-ma35d1.h"
> +
> +DEFINE_SPINLOCK(ma35d1_lock);

Why not static?

> +
> +static const char *const ca35clk_sel_clks[] = {
> +       "hxt", "capll", "ddrpll", "dummy"

Are these parent mappings? Please use 'struct clk_parent_data' instead
if so.

> +};
> +
> +static const char *const sysclk0_sel_clks[] = {
> +       "epll_div2", "syspll"
> +};
> +
[...]
> +
> +static struct clk_hw **hws;
> +static struct clk_hw_onecell_data *ma35d1_hw_data;

Any reason to make these global pointers vs local pointers during probe?

> +
> +static int ma35d1_clocks_probe(struct platform_device *pdev)
> +{
> +       int ret;
> +       struct device *dev = &pdev->dev;
> +       struct device_node *clk_node = dev->of_node;
> +       void __iomem *clk_base;
> +       struct regmap *regmap;
> +       u32 pllmode[5] = { 0, 0, 0, 0, 0 };
> +       u32 pllfreq[5] = { 0, 0, 0, 0, 0 };
> +
> +       dev_info(&pdev->dev, "Nuvoton MA35D1 Clock Driver\n");

Drop this banner message please.

> +       ma35d1_hw_data = devm_kzalloc(&pdev->dev, struct_size(ma35d1_hw_data,
> +                                     hws, CLK_MAX_IDX), GFP_KERNEL);
> +
> +       if (WARN_ON(!ma35d1_hw_data))
> +               return -ENOMEM;
> +
> +       ma35d1_hw_data->num = CLK_MAX_IDX;
> +       hws = ma35d1_hw_data->hws;
> +
> +       clk_node = of_find_compatible_node(NULL, NULL, "nuvoton,ma35d1-clk");
> +       clk_base = of_iomap(clk_node, 0);

Use platform_device APIs as you have a platform device here ('pdev').

> +       of_node_put(clk_node);
> +       if (!clk_base) {
> +               pr_err("%s: could not map region\n", __func__);
> +               return -ENOMEM;
> +       }
> +       regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> +                                                "nuvoton,sys");

Why is it a syscon?

> +       if (IS_ERR(regmap))
> +               pr_warn("%s: Unable to get syscon\n", __func__);

How can we continue without the regmap?

> +
> +       /* clock sources */
> +       hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
[...]
> +       /* EADC */
> +       hws[EADC_DIV] = ma35d1_clk_divider_table("eadc_div", "pclk2",
> +                                                clk_base + REG_CLK_CLKDIV4,
> +                                                0, 4, eadc_div_table);
> +       hws[EADC_GATE] = ma35d1_clk_gate("eadc_gate", "eadc_div",
> +                                        clk_base + REG_CLK_APBCLK2, 25);
> +
> +       ret = of_clk_add_hw_provider(clk_node, of_clk_hw_onecell_get,

Use devm_ variant.

> +                                    ma35d1_hw_data);
> +       if (ret < 0) {
> +               dev_err(dev, "failed to register hws for MA35D1\n");
> +               iounmap(clk_base);

Use devm mapping APIs to avoid unmapping on error path.

> +       }
> +       return ret;
> +}
> +
> +static const struct of_device_id ma35d1_clk_of_match[] = {
> +       { .compatible = "nuvoton,ma35d1-clk" },
> +       { },

Drop comma above so nothing can come after this.

> +};
> +MODULE_DEVICE_TABLE(of, ma35d1_clk_of_match);
> +
> +static struct platform_driver ma35d1_clk_driver = {
> +       .probe = ma35d1_clocks_probe,
> +       .driver = {
> +               .name = "ma35d1-clk",
> +               .of_match_table = ma35d1_clk_of_match,
> +       },
> +};
> +
> +static int __init ma35d1_clocks_init(void)
> +{
> +       return platform_driver_register(&ma35d1_clk_driver);
> +}
> +
> +postcore_initcall(ma35d1_clocks_init);
> +
> +MODULE_AUTHOR("Chi-Fang Li<cfli0@nuvoton.com>");
> +MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
> new file mode 100644
> index 000000000000..faae5a17e425
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1.h
> @@ -0,0 +1,198 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#ifndef __DRV_CLK_NUVOTON_MA35D1_H
> +#define __DRV_CLK_NUVOTON_MA35D1_H
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>

Are these includes used?

> +#include <linux/clk-provider.h>
> +#include <linux/spinlock.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/ma35d1-sys.h>

These probably aren't needed to be included here. Just forward declare
structs you need and include the headers in the C file.

> +
[...]
> +
> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev,
> +                                   const char *name,
> +                                   const char *parent_name,
> +                                   unsigned long flags,
> +                                   void __iomem *reg, u8 shift,
> +                                   u8 width, u32 mask_bit);
> +
> +extern spinlock_t ma35d1_lock;

Why?

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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-15 21:59   ` Stephen Boyd
@ 2023-03-16  3:24     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-16  3:24 UTC (permalink / raw)
  To: Stephen Boyd, gregkh, jirislaby, krzysztof.kozlowski+dt, lee,
	mturquette, p.zabel, robh+dt
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang


Dear Stephen,


Thanks for your review.


On 2023/3/16 上午 05:59, Stephen Boyd wrote:
> Quoting Jacky Huang (2023-03-15 00:28:55)
>> diff --git a/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
>> new file mode 100644
>> index 000000000000..5c2dea071b38
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
>> @@ -0,0 +1,83 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/clock/nuvoton,ma35d1-clk.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton MA35D1 Clock Controller Module Binding
> Binding is redundant.

I will remove this word.

>> +
>> +maintainers:
>> +  - Chi-Fang Li <cfli0@nuvoton.com>
>> +  - Jacky Huang <ychuang3@nuvoton.com>
>> +
>> +description: |
>> +  The MA35D1 clock controller generates clocks for the whole chip,
>> +  including system clocks and all peripheral clocks.
>> +
>> +  See also:
>> +    include/dt-bindings/clock/ma35d1-clk.h
>> +
>> +properties:
>> +  compatible:
>> +    items:
>> +      - const: nuvoton,ma35d1-clk
>> +      - const: syscon
> Does it really need to be a syscon?

Some registers of the clock controller are locked against writing.
Before writing, the lock must be unlocked through the system controller.
So syscon is needed.

>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  "#clock-cells":
>> +    const: 1
>> +
>> +  clocks:
>> +    maxItems: 1
>> +
>> +  clock-names:
>> +    const: clk_hxt
>> +
>> +  assigned-clocks:
>> +    maxItems: 5
>> +
>> +  assigned-clock-rates:
>> +    maxItems: 5
> I hope the assigned clocks properties can be left out of this doc.

Sure, we will remove it.

>> +
>> +  nuvoton,pll-mode:
>> +    description:
>> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
>> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
>> +      integer mode, 1 is for fractional mode, and 2 is for spread
>> +      spectrum mode.
>> +    $ref: /schemas/types.yaml#/definitions/uint32-array
>> +    maxItems: 5
>> +    items:
>> +      minimum: 0
>> +      maximum: 2
> Why not use a string?

OK, we'll use strings instead.

>> +
>> +  nuvoton,sys:
>> +    description:
>> +      Phandle to the system management controller.
>> +    $ref: "/schemas/types.yaml#/definitions/phandle-array"
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - "#clock-cells"
>> +  - clocks
>> +  - clock-names
>> +  - nuvoton,sys
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> No need to include this header right?

Yes, I will remove this header in the next version.

>
>> +
>> +    clk: clock-controller@40460200 {
> Drop the label?

OK, I will drop this label in the next version.

>
>> +        compatible = "nuvoton,ma35d1-clk", "syscon";
>> +        reg = <0x40460200 0x100>;
>> +        #clock-cells = <1>;
>> +        clocks = <&clk_hxt>;
>> +        clock-names = "clk_hxt";
>> +        nuvoton,sys = <&sys>;
>> +    };


Best Regards,

Jacky Huang


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

* Re: [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller
  2023-03-15  7:28 ` [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller Jacky Huang
@ 2023-03-16  7:31   ` Krzysztof Kozlowski
  2023-03-16 13:35     ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:31 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add the dt-bindings header for Nuvoton ma35d1, that gets shared
> between the clock controller and clock references in the dts.

I don't see the device binding. They come together.

> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  .../dt-bindings/clock/nuvoton,ma35d1-clk.h    | 253 ++++++++++++++++++
>  1 file changed, 253 insertions(+)
>  create mode 100644 include/dt-bindings/clock/nuvoton,ma35d1-clk.h
> 
> diff --git a/include/dt-bindings/clock/nuvoton,ma35d1-clk.h b/include/dt-bindings/clock/nuvoton,ma35d1-clk.h
> new file mode 100644
> index 000000000000..6c569fdd6e06
> --- /dev/null
> +++ b/include/dt-bindings/clock/nuvoton,ma35d1-clk.h
> @@ -0,0 +1,253 @@
> +/* SPDX-License-Identifier: GPL-2.0 */

Dual license.

Best regards,
Krzysztof


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

* Re: [PATCH 05/15] dt-bindings: reset: nuvoton: add binding for ma35d1 IP reset control
  2023-03-15  7:28 ` [PATCH 05/15] dt-bindings: reset: nuvoton: add binding for ma35d1 IP reset control Jacky Huang
@ 2023-03-16  7:31   ` Krzysztof Kozlowski
  0 siblings, 0 replies; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:31 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add the dt-bindings header for Nuvoton ma35d1, that gets shared
> between the reset controller and reset references in the dts.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>

Same problems as previous patch.

Best regards,
Krzysztof


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

* Re: [PATCH 06/15] dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible
  2023-03-15  7:28 ` [PATCH 06/15] dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible Jacky Huang
@ 2023-03-16  7:31   ` Krzysztof Kozlowski
  2023-03-17  1:03     ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:31 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add Nuvoton ma35d1 system registers compatible

Missing full stop.

> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  Documentation/devicetree/bindings/mfd/syscon.yaml | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
> index c828c4f5e4a7..e7a3c6e1e77f 100644
> --- a/Documentation/devicetree/bindings/mfd/syscon.yaml
> +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
> @@ -57,6 +57,7 @@ properties:
>                - microchip,sparx5-cpu-syscon
>                - mstar,msc313-pmsleep
>                - nuvoton,wpcm450-shm
> +              - nuvoton,ma35d1-sys

Wrong order

Best regards,
Krzysztof


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

* Re: [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform
  2023-03-15  7:28 ` [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform Jacky Huang
@ 2023-03-16  7:33   ` Krzysztof Kozlowski
  2023-03-16 14:32     ` Arnd Bergmann
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:33 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add binding for ARMv8 based Nuvotn SoCs and platform boards.
> Add initial bindings for ma35d1 series development boards.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  .../devicetree/bindings/arm/nuvoton.yaml      | 30 +++++++++++++++++++

And what is npcm for? Why it was made an directory?

All these should be just one Nuvoton.

>  1 file changed, 30 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/nuvoton.yaml
> 
> diff --git a/Documentation/devicetree/bindings/arm/nuvoton.yaml b/Documentation/devicetree/bindings/arm/nuvoton.yaml
> new file mode 100644
> index 000000000000..f95e7b30711e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/nuvoton.yaml
> @@ -0,0 +1,30 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause


Best regards,
Krzysztof


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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-15  7:28 ` [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings Jacky Huang
  2023-03-15 21:59   ` Stephen Boyd
@ 2023-03-16  7:35   ` Krzysztof Kozlowski
  2023-03-17  3:47     ` Jacky Huang
  1 sibling, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:35 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add documentation to describe nuvoton ma35d1 clock driver bindings.

Subject: drop second/last, redundant "bindings". The "dt-bindings"
prefix is already stating that these are bindings.

> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  .../bindings/clock/nuvoton,ma35d1-clk.yaml    | 83 +++++++++++++++++++
>  1 file changed, 83 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
> 
> diff --git a/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
> new file mode 100644
> index 000000000000..5c2dea071b38
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
> @@ -0,0 +1,83 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/clock/nuvoton,ma35d1-clk.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nuvoton MA35D1 Clock Controller Module Binding
> +
> +maintainers:
> +  - Chi-Fang Li <cfli0@nuvoton.com>
> +  - Jacky Huang <ychuang3@nuvoton.com>
> +
> +description: |
> +  The MA35D1 clock controller generates clocks for the whole chip,
> +  including system clocks and all peripheral clocks.
> +
> +  See also:
> +    include/dt-bindings/clock/ma35d1-clk.h
> +
> +properties:
> +  compatible:
> +    items:
> +      - const: nuvoton,ma35d1-clk
> +      - const: syscon
> +
> +  reg:
> +    maxItems: 1
> +
> +  "#clock-cells":
> +    const: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  clock-names:
> +    const: clk_hxt

Drop clock-names. You do not need it for one clock.


> +
> +  assigned-clocks:
> +    maxItems: 5
> +
> +  assigned-clock-rates:
> +    maxItems: 5

Drop both properties, you do not need them in the binding.

> +
> +  nuvoton,pll-mode:
> +    description:
> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
> +      integer mode, 1 is for fractional mode, and 2 is for spread
> +      spectrum mode.
> +    $ref: /schemas/types.yaml#/definitions/uint32-array
> +    maxItems: 5
> +    items:
> +      minimum: 0
> +      maximum: 2

Why exactly this is suitable for DT?

> +
> +  nuvoton,sys:
> +    description:
> +      Phandle to the system management controller.
> +    $ref: "/schemas/types.yaml#/definitions/phandle-array"

Drop quotes.

You need here constraints, look for existing examples.



Best regards,
Krzysztof


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

* Re: [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset controller bindings
  2023-03-15  7:28 ` [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset " Jacky Huang
@ 2023-03-16  7:37   ` Krzysztof Kozlowski
  2023-03-16  7:39     ` Krzysztof Kozlowski
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:37 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add documentation to describe nuvoton ma35d1 reset driver bindings.

Subject: drop second/last, redundant "bindings". The "dt-bindings"
prefix is already stating that these are bindings.

> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  .../bindings/reset/nuvoton,ma35d1-reset.yaml  | 50 +++++++++++++++++++
>  1 file changed, 50 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
> 
> diff --git a/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
> new file mode 100644
> index 000000000000..f66c566c6dce
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
> @@ -0,0 +1,50 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/reset/nuvoton,ma35d1-reset.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nuvoton MA35D1 Reset Controller
> +
> +maintainers:
> +  - Chi-Fang Li <cfli0@nuvoton.com>
> +  - Jacky Huang <ychuang3@nuvoton.com>
> +
> +description:
> +  The system reset controller can be used to reset various peripheral
> +  controllers in MA35D1 SoC.
> +
> +properties:
> +  compatible:
> +    const: nuvoton,ma35d1-reset
> +
> +  regmap:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description: Phandle to the register map node.

You need to be specific what is this. As you can easily check, there is
no such property in any devices. I don't understand why do you need it
in the first place.

> +
> +  '#reset-cells':
> +    const: 1
> +
> +required:
> +  - compatible
> +  - regmap
> +  - '#reset-cells'
> +
> +additionalProperties: false
> +
> +examples:
> +  # system reset controller node:
> +  - |
> +    #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
> +
> +    sys: system-management@40460000 {
> +        compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";

And your patchset is not bisectable.... Test for bisectability before
sending.


Best regards,
Krzysztof


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

* Re: [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset controller bindings
  2023-03-16  7:37   ` Krzysztof Kozlowski
@ 2023-03-16  7:39     ` Krzysztof Kozlowski
  2023-03-18  4:30       ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:39 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 16/03/2023 08:37, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Add documentation to describe nuvoton ma35d1 reset driver bindings.
> 
> Subject: drop second/last, redundant "bindings". The "dt-bindings"
> prefix is already stating that these are bindings.
> 
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>  .../bindings/reset/nuvoton,ma35d1-reset.yaml  | 50 +++++++++++++++++++
>>  1 file changed, 50 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>> new file mode 100644
>> index 000000000000..f66c566c6dce
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>> @@ -0,0 +1,50 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/reset/nuvoton,ma35d1-reset.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton MA35D1 Reset Controller
>> +
>> +maintainers:
>> +  - Chi-Fang Li <cfli0@nuvoton.com>
>> +  - Jacky Huang <ychuang3@nuvoton.com>
>> +
>> +description:
>> +  The system reset controller can be used to reset various peripheral
>> +  controllers in MA35D1 SoC.
>> +
>> +properties:
>> +  compatible:
>> +    const: nuvoton,ma35d1-reset
>> +
>> +  regmap:
>> +    $ref: /schemas/types.yaml#/definitions/phandle
>> +    description: Phandle to the register map node.
> 
> You need to be specific what is this. As you can easily check, there is
> no such property in any devices. I don't understand why do you need it
> in the first place.
> 
>> +
>> +  '#reset-cells':
>> +    const: 1
>> +
>> +required:
>> +  - compatible
>> +  - regmap
>> +  - '#reset-cells'
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> +  # system reset controller node:
>> +  - |
>> +    #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
>> +
>> +    sys: system-management@40460000 {
>> +        compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";
> 
> And your patchset is not bisectable.... Test for bisectability before
> sending.

Ah, no, it's correct. I see the compatible in previous patch. You need
to clearly describe the dependencies and merging strategy/requirements
in cover letter.

Best regards,
Krzysztof


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

* Re: [PATCH 10/15] dt-bindings: serial: Document ma35d1 uart controller bindings
  2023-03-15  7:28 ` [PATCH 10/15] dt-bindings: serial: Document ma35d1 uart " Jacky Huang
@ 2023-03-16  7:40   ` Krzysztof Kozlowski
  2023-03-17  4:18     ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:40 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add documentation to describe nuvoton ma35d1 uart driver bindings.

Subject: drop second/last, redundant "bindings". The "dt-bindings"
prefix is already stating that these are bindings.

> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  .../serial/nuvoton,ma35d1-serial.yaml         | 52 +++++++++++++++++++
>  1 file changed, 52 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
> 
> diff --git a/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml b/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
> new file mode 100644
> index 000000000000..9daa2efd4734
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
> @@ -0,0 +1,52 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/serial/nuvoton,ma35d1-serial.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nuvoton MA35D1 Universal Asynchronous Receiver/Transmitter (UART)
> +
> +maintainers:
> +  - Min-Jen Chen <mjchen@nuvoton.com>
> +  - Jacky Huang <ychuang3@nuvoton.com>
> +
> +allOf:
> +  - $ref: "serial.yaml"

Drop quotes. Use some recent bindings as your starting point, so we do
not have to give comments for things which were already fixed.

> +
> +properties:
> +  compatible:
> +    const: nuvoton,ma35d1-uart
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> +
> +    aliases {
> +        serial0 = &uart0;
> +    };

Drop aliases.

> +
> +    uart0:serial@40700000 {

Drop label

Best regards,
Krzysztof


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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (14 preceding siblings ...)
  2023-03-15  7:29 ` [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35 Jacky Huang
@ 2023-03-16  7:41 ` Krzysztof Kozlowski
  2023-03-16 14:05 ` Arnd Bergmann
  16 siblings, 0 replies; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:41 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> This patchset adds initial support for the Nuvoton ma35d1 SoC, including
> initial device tree, clock driver, reset driver, and serial driver.
> 
> This patchset cover letter is based from the initial support for Nuvoton
> ma35d1 to keep tracking the version history.
> 

To maintainers: patches should not be applied independently because it
will start failing tests...

Best regards,
Krzysztof


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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-15  7:28 ` [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree Jacky Huang
@ 2023-03-16  7:45   ` Krzysztof Kozlowski
  2023-03-18  6:07     ` Jacky Huang
  2023-03-16 14:17   ` Arnd Bergmann
  1 sibling, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:45 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> Add initial device tree support for Nuvoton ma35d1 SoC, including
> cpu, clock, reset, and serial controllers.
> Add reference boards som-256m and iot-512m.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  arch/arm64/boot/dts/nuvoton/Makefile          |   2 +
>  .../boot/dts/nuvoton/ma35d1-iot-512m.dts      |  24 ++
>  .../boot/dts/nuvoton/ma35d1-som-256m.dts      |  23 ++
>  arch/arm64/boot/dts/nuvoton/ma35d1.dtsi       | 272 ++++++++++++++++++
>  4 files changed, 321 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
>  create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
>  create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
> 
> diff --git a/arch/arm64/boot/dts/nuvoton/Makefile b/arch/arm64/boot/dts/nuvoton/Makefile
> index a99dab90472a..c11ab4eac9c7 100644
> --- a/arch/arm64/boot/dts/nuvoton/Makefile
> +++ b/arch/arm64/boot/dts/nuvoton/Makefile
> @@ -1,2 +1,4 @@
>  # SPDX-License-Identifier: GPL-2.0
>  dtb-$(CONFIG_ARCH_NPCM) += nuvoton-npcm845-evb.dtb
> +dtb-$(CONFIG_ARCH_NUVOTON) += ma35d1-iot-512m.dtb
> +dtb-$(CONFIG_ARCH_NUVOTON) += ma35d1-som-256m.dtb
> diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
> new file mode 100644
> index 000000000000..dffcaef1e6d8
> --- /dev/null
> +++ b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
> @@ -0,0 +1,24 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Shan-Chun Hung <schung@nuvoton.com>
> + *         Jacky huang <ychuang3@nuvoton.com>
> + */
> +
> +/dts-v1/;
> +#include "ma35d1.dtsi"
> +
> +/ {
> +	model = "Nuvoton MA35D1-IoT";
> +	compatible = "nuvoton,ma35d1-iot", "nuvoton,ma35d1";
> +
> +	chosen {
> +		stdout-path = "serial0:115200n8";
> +	};
> +
> +	mem: memory@80000000 {
> +		device_type = "memory";
> +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
> +	};
> +};
> +
> diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
> new file mode 100644
> index 000000000000..3e6c3d5469ac
> --- /dev/null
> +++ b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Shan-Chun Hung <schung@nuvoton.com>
> + *         Jacky huang <ychuang3@nuvoton.com>
> + */
> +
> +/dts-v1/;
> +#include "ma35d1.dtsi"
> +
> +/ {
> +	model = "Nuvoton MA35D1-SOM";
> +	compatible = "nuvoton,ma35d1-som", "nuvoton,ma35d1";
> +
> +	chosen {
> +		stdout-path = "serial0:115200n8";
> +	};
> +
> +	mem: memory@80000000 {
> +		device_type = "memory";
> +		reg = <0x00000000 0x80000000 0 0x10000000>; /* 256M DRAM */
> +	};
> +};
> diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
> new file mode 100644
> index 000000000000..8c855f6b330a
> --- /dev/null
> +++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
> @@ -0,0 +1,272 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Shan-Chun Hung <schung@nuvoton.com>
> + *         Jacky huang <ychuang3@nuvoton.com>
> + */
> +
> +#include <dt-bindings/interrupt-controller/arm-gic.h>
> +#include <dt-bindings/input/input.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> +#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
> +
> +/ {
> +	compatible = "nuvoton,ma35d1";
> +	interrupt-parent = <&gic>;
> +	#address-cells = <2>;
> +	#size-cells = <2>;
> +
> +	aliases {
> +		serial0 = &uart0;
> +		serial1 = &uart1;
> +		serial2 = &uart2;
> +		serial3 = &uart3;
> +		serial4 = &uart4;
> +		serial5 = &uart5;
> +		serial6 = &uart6;
> +		serial7 = &uart7;
> +		serial8 = &uart8;
> +		serial9 = &uart9;
> +		serial10 = &uart10;
> +		serial11 = &uart11;
> +		serial12 = &uart12;
> +		serial13 = &uart13;
> +		serial14 = &uart14;
> +		serial15 = &uart15;
> +		serial16 = &uart16;

Aliases of interfaces coming out of SoC are properties of boards, not
SoC DTSI.

> +	};
> +
> +	chosen {
> +		stdout-path = "serial0:115200n8";
> +	};
> +
> +	cpus {
> +		#address-cells = <2>;
> +		#size-cells = <0>;


Blank line.

> +		cpu0: cpu@0 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a35";
> +			reg = <0x0 0x0>;
> +			enable-method = "psci";
> +			next-level-cache = <&L2_0>;
> +		};

Between every node as well.

> +		cpu1: cpu@1 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a35";
> +			reg = <0x0 0x1>;
> +			enable-method = "psci";
> +			next-level-cache = <&L2_0>;
> +		};
> +		L2_0: l2-cache0 {
> +			compatible = "cache";
> +			cache-level = <2>;
> +		};
> +	};
> +
> +	psci {
> +		compatible = "arm,psci-0.2";
> +		method = "smc";
> +	};
> +
> +	clk_hxt: clock_hxt {

No underscores in node names.

> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <24000000>;
> +		clock-output-names = "clk_hxt";

This looks like a property of boards, not SoC. Are you sure the clock
physically is in every SoC? If so, why it is not part of clock
controller? (before you start explaining what is this, have in mind that
I am pretty sure I know what is this, so rather answer the questions)

> +	};
> +
> +	timer {
> +		compatible = "arm,armv8-timer";
> +		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
> +			      IRQ_TYPE_LEVEL_LOW)>, /* Physical Secure */
> +			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) |
> +			      IRQ_TYPE_LEVEL_LOW)>, /* Physical Non-Secure */
> +			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
> +			      IRQ_TYPE_LEVEL_LOW)>, /* Virtual */
> +			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
> +			      IRQ_TYPE_LEVEL_LOW)>; /* Hypervisor */
> +		clock-frequency = <12000000>;
> +		interrupt-parent = <&gic>;
> +	};
> +
> +	sys: system-management@40460000 {
> +		compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";
> +		reg = <0x0 0x40460000 0x0 0x200>;
> +
> +		reset: reset-controller {
> +			compatible = "nuvoton,ma35d1-reset";
> +			regmap = <&sys>;
> +			#reset-cells = <1>;
> +		};
> +	};
> +
> +	clk: clock-controller@40460200 {
> +		compatible = "nuvoton,ma35d1-clk", "syscon";
> +		reg = <0x00000000 0x40460200 0x0 0x100>;
> +		#clock-cells = <1>;
> +		clocks = <&clk_hxt>;
> +		clock-names = "clk_hxt";
> +		assigned-clocks = <&clk CAPLL>,
> +				  <&clk DDRPLL>,
> +				  <&clk APLL>,
> +				  <&clk EPLL>,
> +				  <&clk VPLL>;
> +		assigned-clock-rates = <800000000>,
> +				       <266000000>,
> +				       <180000000>,
> +				       <500000000>,
> +				       <102000000>;
> +		nuvoton,pll-mode = <0>, <1>, <0>, <0>, <0>;
> +		nuvoton,sys = <&sys>;
> +	};
> +
> +	gic: interrupt-controller@50801000 {
> +		compatible = "arm,gic-400";
> +		#interrupt-cells = <3>;
> +		interrupt-parent = <&gic>;
> +		interrupt-controller;
> +		reg =   <0x0 0x50801000 0 0x1000>, /* GICD */
> +			<0x0 0x50802000 0 0x2000>, /* GICC */
> +			<0x0 0x50804000 0 0x2000>, /* GICH */
> +			<0x0 0x50806000 0 0x2000>; /* GICV */

reg is second property.

> +		interrupts = <GIC_PPI 9 (GIC_CPU_MASK_RAW(0x13) |
> +			      IRQ_TYPE_LEVEL_HIGH)>;
> +	};
> +
> +	uart0:serial@40700000 {
> +		compatible = "nuvoton,ma35d1-uart";
> +		reg = <0x0 0x40700000 0x0 0x100>;
> +		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
> +		clocks = <&clk UART0_GATE>;
> +		status = "okay";

Why? Drop the line... or convert it to disabled. Otherwise, why every
SoC has serial0 enabled? Is it used internally?


Best regards,
Krzysztof


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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-15  7:28 ` [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
  2023-03-15 22:07   ` kernel test robot
  2023-03-15 22:30   ` Stephen Boyd
@ 2023-03-16  7:51   ` Krzysztof Kozlowski
  2023-03-19  2:55     ` Jacky Huang
  2023-03-16 15:56   ` Ilpo Järvinen
  3 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:51 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> The clock controller generates clocks for the whole chip, including
> system clocks and all peripheral clocks. This driver support ma35d1
> clock gating, divider, and individual PLL configuration.
> 
> There are 6 PLLs in ma35d1 SoC:
>   - CA-PLL for the two Cortex-A35 CPU clock
>   - SYS-PLL for system bus, which comes from the companion MCU
>     and cannot be programmed by clock controller.
>   - DDR-PLL for DDR
>   - EPLL for GMAC and GFX, Display, and VDEC IPs.
>   - VPLL for video output pixel clock
>   - APLL for SDHC, I2S audio, and other IPs.
> CA-PLL has only one operation mode.
> DDR-PLL, EPLL, VPLL, and APLL are advanced PLLs which have 3
> operation modes: integer mode, fraction mode, and spread specturm mode.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  drivers/clk/Makefile                     |   1 +
>  drivers/clk/nuvoton/Makefile             |   4 +
>  drivers/clk/nuvoton/clk-ma35d1-divider.c | 144 ++++
>  drivers/clk/nuvoton/clk-ma35d1-pll.c     | 534 +++++++++++++
>  drivers/clk/nuvoton/clk-ma35d1.c         | 970 +++++++++++++++++++++++
>  drivers/clk/nuvoton/clk-ma35d1.h         | 198 +++++
>  6 files changed, 1851 insertions(+)
>  create mode 100644 drivers/clk/nuvoton/Makefile
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1-divider.c
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1-pll.c
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1.c
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1.h
> 
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index e3ca0d058a25..2e7916d269e1 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -103,6 +103,7 @@ endif
>  obj-y					+= mstar/
>  obj-y					+= mvebu/
>  obj-$(CONFIG_ARCH_MXS)			+= mxs/
> +obj-$(CONFIG_ARCH_NUVOTON)		+= nuvoton/

Missing compile test.

(...)

> +
> +MODULE_AUTHOR("Chi-Fang Li<cfli0@nuvoton.com>");
> +MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
> new file mode 100644
> index 000000000000..faae5a17e425
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1.h
> @@ -0,0 +1,198 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#ifndef __DRV_CLK_NUVOTON_MA35D1_H
> +#define __DRV_CLK_NUVOTON_MA35D1_H
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/spinlock.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/ma35d1-sys.h>
> +
> +enum ma35d1_pll_type {
> +	MA35D1_CAPLL,
> +	MA35D1_DDRPLL,
> +	MA35D1_APLL,
> +	MA35D1_EPLL,
> +	MA35D1_VPLL,
> +};
> +
> +enum ma35d1_pll_mode {
> +	VSIPLL_INTEGER_MODE,
> +	VSIPLL_FRACTIONAL_MODE,
> +	VSIPLL_SS_MODE,
> +};
> +
> +/* VSI-PLL CTL0~2 */
> +#define VSIPLL_CTL0			0x0
> +#define VSIPLL_CTL1			0x4
> +#define VSIPLL_CTL2			0x8
> +
> +/* VSI-PLL Specification limits */
> +#define VSIPLL_FREF_MAX_FREQ		200000000UL
> +#define VSIPLL_FREF_MIN_FREQ		1000000UL
> +#define VSIPLL_FREFDIVM_MAX_FREQ	40000000UL
> +#define VSIPLL_FREFDIVM_MIN_FREQ0	1000000UL
> +#define VSIPLL_FREFDIVM_MIN_FREQ1	10000000UL
> +#define VSIPLL_FCLK_MAX_FREQ		2400000000UL
> +#define VSIPLL_FCLK_MIN_FREQ		600000000UL
> +#define VSIPLL_FCLKO_MAX_FREQ		2400000000UL
> +#define VSIPLL_FCLKO_MIN_FREQ		85700000UL
> +#define VSIPLL_SPREAD_RANGE		194
> +#define VSIPLL_MODULATION_FREQ		50000
> +
> +/* Clock Control Registers Offset */
> +#define REG_CLK_PWRCTL			(0x00)
> +#define REG_CLK_SYSCLK0			(0x04)
> +#define REG_CLK_SYSCLK1			(0x08)
> +#define REG_CLK_APBCLK0			(0x0C)
> +#define REG_CLK_APBCLK1			(0x10)
> +#define REG_CLK_APBCLK2			(0x14)
> +#define REG_CLK_CLKSEL0			(0x18)
> +#define REG_CLK_CLKSEL1			(0x1C)
> +#define REG_CLK_CLKSEL2			(0x20)
> +#define REG_CLK_CLKSEL3			(0x24)
> +#define REG_CLK_CLKSEL4			(0x28)
> +#define REG_CLK_CLKDIV0			(0x2C)
> +#define REG_CLK_CLKDIV1			(0x30)
> +#define REG_CLK_CLKDIV2			(0x34)
> +#define REG_CLK_CLKDIV3			(0x38)
> +#define REG_CLK_CLKDIV4			(0x3C)
> +#define REG_CLK_CLKOCTL			(0x40)
> +#define REG_CLK_STATUS			(0x50)
> +#define REG_CLK_PLL0CTL0		(0x60)
> +#define REG_CLK_PLL2CTL0		(0x80)
> +#define REG_CLK_PLL2CTL1		(0x84)
> +#define REG_CLK_PLL2CTL2		(0x88)
> +#define REG_CLK_PLL3CTL0		(0x90)
> +#define REG_CLK_PLL3CTL1		(0x94)
> +#define REG_CLK_PLL3CTL2		(0x98)
> +#define REG_CLK_PLL4CTL0		(0xA0)
> +#define REG_CLK_PLL4CTL1		(0xA4)
> +#define REG_CLK_PLL4CTL2		(0xA8)
> +#define REG_CLK_PLL5CTL0		(0xB0)
> +#define REG_CLK_PLL5CTL1		(0xB4)
> +#define REG_CLK_PLL5CTL2		(0xB8)
> +#define REG_CLK_CLKDCTL			(0xC0)
> +#define REG_CLK_CLKDSTS			(0xC4)
> +#define REG_CLK_CDUPB			(0xC8)
> +#define REG_CLK_CDLOWB			(0xCC)
> +#define REG_CLK_CKFLTRCTL		(0xD0)
> +#define REG_CLK_TESTCLK			(0xF0)
> +#define REG_CLK_PLLCTL			(0x40)
> +
> +/* Constant Definitions for Clock Controller */
> +#define SMICPLLCTL0_FBDIV_POS		(0)
> +#define SMICPLLCTL0_FBDIV_MSK		(0xfful << SMICPLLCTL0_FBDIV_POS)
> +#define SMICPLLCTL0_INDIV_POS		(8)
> +#define SMICPLLCTL0_INDIV_MSK		(0xful << SMICPLLCTL0_INDIV_POS)
> +#define SMICPLLCTL0_OUTDIV_POS		(12)
> +#define SMICPLLCTL0_OUTDIV_MSK		(0x3ul << SMICPLLCTL0_OUTDIV_POS)
> +#define SMICPLLCTL0_PD_POS		(16)
> +#define SMICPLLCTL0_PD_MSK		(0x1ul << SMICPLLCTL0_PD_POS)
> +#define SMICPLLCTL0_BP_POS		(17)
> +#define SMICPLLCTL0_BP_MSK		(0x1ul << SMICPLLCTL0_BP_POS)
> +#define VSIPLLCTL0_FBDIV_POS		(0)
> +#define VSIPLLCTL0_FBDIV_MSK		(0x7fful << VSIPLLCTL0_FBDIV_POS)
> +#define VSIPLLCTL0_INDIV_POS		(12)
> +#define VSIPLLCTL0_INDIV_MSK		(0x3ful << VSIPLLCTL0_INDIV_POS)
> +#define VSIPLLCTL0_MODE_POS		(18)
> +#define VSIPLLCTL0_MODE_MSK		(0x3ul << VSIPLLCTL0_MODE_POS)
> +#define VSIPLLCTL0_SSRATE_POS		(20)
> +#define VSIPLLCTL0_SSRATE_MSK		(0x7fful << VSIPLLCTL0_SSRATE_POS)
> +#define VSIPLLCTL1_PD_POS		(0)
> +#define VSIPLLCTL1_PD_MSK		(0x1ul << VSIPLLCTL1_PD_POS)
> +#define VSIPLLCTL1_BP_POS		(1)
> +#define VSIPLLCTL1_BP_MSK		(0x1ul << VSIPLLCTL1_BP_POS)
> +#define VSIPLLCTL1_OUTDIV_POS		(4)
> +#define VSIPLLCTL1_OUTDIV_MSK		(0x7ul << VSIPLLCTL1_OUTDIV_POS)
> +#define VSIPLLCTL1_FRAC_POS		(8)
> +#define VSIPLLCTL1_FRAC_MSK		(0xfffffful << VSIPLLCTL1_FRAC_POS)
> +#define VSIPLLCTL2_SLOPE_POS		(0)
> +#define VSIPLLCTL2_SLOPE_MSK		(0xfffffful << VSIPLLCTL2_SLOPE_POS)
> +
> +struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type, u8 u8mode,
> +				 const char *name, const char *parent,
> +				 unsigned long targetFreq,
> +				 void __iomem *base,
> +				 struct regmap *regmap);
> +
> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev,
> +				    const char *name,
> +				    const char *parent_name,
> +				    unsigned long flags,
> +				    void __iomem *reg, u8 shift,
> +				    u8 width, u32 mask_bit);
> +
> +extern spinlock_t ma35d1_lock;

Why this is here?

> +
> +static inline struct clk_hw *ma35d1_clk_fixed(const char *name, int rate)
> +{
> +	return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
> +}
> +

Why all these are here?

> +
> +#endif /* __DRV_CLK_NUVOTON_MA35D1_H */

Best regards,
Krzysztof


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

* Re: [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support
  2023-03-15  7:29 ` [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support Jacky Huang
@ 2023-03-16  7:51   ` Krzysztof Kozlowski
  2023-03-17  7:13     ` Jacky Huang
  2023-03-16 15:05   ` Ilpo Järvinen
  1 sibling, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16  7:51 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 15/03/2023 08:29, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> This driver supports individual IP reset for ma35d1. The reset
> control registers is a subset of system control registers.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  drivers/reset/Kconfig        |   6 ++
>  drivers/reset/Makefile       |   1 +
>  drivers/reset/reset-ma35d1.c | 152 +++++++++++++++++++++++++++++++++++
>  3 files changed, 159 insertions(+)
>  create mode 100644 drivers/reset/reset-ma35d1.c
> 
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index 2a52c990d4fe..47671060d259 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -143,6 +143,12 @@ config RESET_NPCM
>  	  This enables the reset controller driver for Nuvoton NPCM
>  	  BMC SoCs.
>  
> +config RESET_NUVOTON_MA35D1
> +	bool "Nuvton MA35D1 Reset Driver"
> +	default ARCH_NUVOTON

|| COMPILE_TEST


> +	help
> +	  This enables the reset controller driver for Nuvoton MA35D1 SoC.
> +


Best regards,
Krzysztof


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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-15 10:13   ` Jiri Slaby
@ 2023-03-16 13:28     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-16 13:28 UTC (permalink / raw)
  To: Jiri Slaby, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Jiri,


Thanks for your review.

I will create #define constant to replace every occurrence of magics or
hard coding number.

For every occurrence of "struct uart_ma35d1_port *up = (struct 
uart_ma35d1_port *)port; ",
it will be replaced with to_ma35d1_uart_port() .


On 2023/3/15 下午 06:13, Jiri Slaby wrote:
> On 15. 03. 23, 8:29, Jacky Huang wrote:
>> --- /dev/null
>> +++ b/drivers/tty/serial/ma35d1_serial.c
>> @@ -0,0 +1,842 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  MA35D1 serial driver
>> + *  Copyright (C) 2023 Nuvoton Technology Corp.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/moduleparam.h>
>
> What parameters does this module have?
+#include <linux/ioport.h>
>> +#include <linux/init.h>
>> +#include <linux/console.h>
>> +#include <linux/sysrq.h>
>> +#include <linux/delay.h>
>
> What do you use from delay.h?
>
>> +#include <linux/platform_device.h>
>> +#include <linux/tty.h>
>> +#include <linux/tty_flip.h>
>> +#include <linux/clk.h>
>> +#include <linux/serial_reg.h>
>> +#include <linux/serial_core.h>
>> +#include <linux/serial.h>
>> +#include <linux/nmi.h>
>
> nmi.h?
>
> Please clean up all of the includes.
>

moduleparam.h, delay.h, and nmi.h are not used.

I will test and remove all unused #include.

>> +#include <linux/mutex.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/of.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/io.h>
>> +#include <asm/irq.h>
>> +#include <asm/serial.h>
>> +#include "ma35d1_serial.h"
>> +
>> +#define UART_NR            17
>> +
>> +static struct uart_driver ma35d1serial_reg;
>> +struct clk *clk;
>> +
>> +struct uart_ma35d1_port {
>> +    struct uart_port port;
>> +    u16 capabilities; /* port capabilities */
>> +    u8 ier;
>> +    u8 lcr;
>> +    u8 mcr;
>> +    u8 mcr_mask;   /* mask of user bits */
>> +    u8 mcr_force;  /* mask of forced bits */
>
> Where are all those used?
>

I will remove the unused structure members in the next version.

>> +    struct serial_rs485 rs485; /* rs485 settings */
>> +    u32 baud_rate;
>
> And this one.
>

It's not used. I will remove it.

>> +    int rx_count;
>> +    u32 console_baud_rate;
>> +    u32 console_line;
>> +    u32 console_int;
>> +};
>> +
>> +static struct device_node *ma35d1serial_uart_nodes[UART_NR];
>> +static struct uart_ma35d1_port ma35d1serial_ports[UART_NR] = { 0 };
>
>> +static void __stop_tx(struct uart_ma35d1_port *p);
>
> What for?

This function is used to disable TX FIFO empty interrupt.

naming issue?

>
>> +static void transmit_chars(struct uart_ma35d1_port *up);
>> +
>> +static struct uart_ma35d1_port *to_ma35d1_uart_port(struct uart_port 
>> *uart)
>> +{
>> +    return container_of(uart, struct uart_ma35d1_port, port);
>> +}
>> +
>> +static u32 serial_in(struct uart_ma35d1_port *p, int offset)
>
> Q: int? A: No.

will modify "int offset" to "u32 offset"

>
>> +{
>> +    return __raw_readl(p->port.membase + offset);
>> +}
>> +
>> +static void serial_out(struct uart_ma35d1_port *p, int offset, int 
>> value)
>
> No ints here, please.

will modify offset and value data type to u32


>
>> +{
>> +    __raw_writel(value, p->port.membase + offset);
>> +}
>> +
>> +static void __stop_tx(struct uart_ma35d1_port *p)
>> +{
>> +    u32 ier;
>> +
>> +    ier = serial_in(p, UART_REG_IER);
>> +    if (ier & THRE_IEN)
>> +        serial_out(p, UART_REG_IER, ier & ~THRE_IEN);
>> +}
>> +
>> +static void ma35d1serial_stop_tx(struct uart_port *port)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>
> Despite you have to_ma35d1_uart_port(), you do this?
>

You're right. I will use to_ma35d1_uart_port() to replace it.


>> +
>> +    __stop_tx(up);
>> +}
>> +
>> +static void ma35d1serial_start_tx(struct uart_port *port)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +    u32 ier;
>> +    struct circ_buf *xmit = &up->port.state->xmit;
>> +
>> +    ier = serial_in(up, UART_REG_IER);
>> +    serial_out(up, UART_REG_IER, ier & ~THRE_IEN);
>> +    if (uart_circ_chars_pending(xmit) <
>> +        (16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0x3F)))
>
> You look like you need a helper for this computation (hint: 
> GENMASK()). What do those magic constants mean?
>

I will add #define macro for the bit mask..

>> +        transmit_chars(up);
>> +    serial_out(up, UART_REG_IER, ier | THRE_IEN);
>> +}
>> +
>> +static void ma35d1serial_stop_rx(struct uart_port *port)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>
> Bah. Nah.

>> +
>> +    serial_out(up, UART_REG_IER, serial_in(up, UART_REG_IER) & 
>> ~RDA_IEN);
>> +}
>> +
>> +static void
>> +receive_chars(struct uart_ma35d1_port *up)
>> +{
>> +    u8 ch;
>> +    u32 fsr;
>> +    u32 isr;
>> +    u32 dcnt;
>> +    char flag;
>
> flag is u8 too. And a reverse xmas tree, please. Actually, you can put 
> all those u32 to a single line.
>

OK, I will fix it.

>> +
>> +    isr = serial_in(up, UART_REG_ISR);
>> +    fsr = serial_in(up, UART_REG_FSR);
>> +
>> +    while (!(fsr & RX_EMPTY)) {
>> +        flag = TTY_NORMAL;
>> +        up->port.icount.rx++;
>> +
>> +        if (unlikely(fsr & (BIF | FEF | PEF | RX_OVER_IF))) {
>> +            if (fsr & BIF) {
>> +                serial_out(up, UART_REG_FSR, BIF);
>> +                up->port.icount.brk++;
>> +                if (uart_handle_break(&up->port))
>> +                    continue;
>> +            }
>> +            if (fsr & FEF) {
>> +                serial_out(up, UART_REG_FSR, FEF);
>> +                up->port.icount.frame++;
>> +            }
>> +            if (fsr & PEF) {
>> +                serial_out(up, UART_REG_FSR, PEF);
>> +                up->port.icount.parity++;
>> +            }
>> +            if (fsr & RX_OVER_IF) {
>> +                serial_out(up, UART_REG_FSR, RX_OVER_IF);
>> +                up->port.icount.overrun++;
>> +            }
>> +            if (fsr & BIF)
>> +                flag = TTY_BREAK;
>> +            if (fsr & PEF)
>> +                flag = TTY_PARITY;
>> +            if (fsr & FEF)
>> +                flag = TTY_FRAME;
>> +        }
>> +        ch = (u8)serial_in(up, UART_REG_RBR);
>> +        if (uart_handle_sysrq_char(&up->port, ch))
>> +            continue;
>> +
>> +        uart_insert_char(&up->port, fsr, RX_OVER_IF, ch, flag);
>
> No lock needed?
>

I will wrap it with spin_lock and spin_unlock.

>> +        up->rx_count++;
>> +        dcnt = (serial_in(up, UART_REG_FSR) >> 8) & 0x3f;
>
> More magic constants. No.
>
>> +        if (up->rx_count > 1023) {
>> +            spin_lock(&up->port.lock);
>> + tty_flip_buffer_push(&up->port.state->port);
>> +            spin_unlock(&up->port.lock);
>> +            up->rx_count = 0;
>> +            if ((isr & RXTO_IF) && (dcnt == 0))
>> +                goto tout_end;
>> +        }
>> +        if (isr & RDA_IF) {
>> +            if (dcnt == 1)
>> +                return;
>> +        }
>> +        fsr = serial_in(up, UART_REG_FSR);
>> +    }
>> +    spin_lock(&up->port.lock);
>> +    tty_flip_buffer_push(&up->port.state->port);
>> +    spin_unlock(&up->port.lock);
>> +tout_end:
>> +    up->rx_count = 0;
>> +}
>> +
>> +static void transmit_chars(struct uart_ma35d1_port *up)
>
> Why this cannot use uart_port_tx()?

OK, I will rename it as uart_port_tx().

>
>> +{
>> +    struct circ_buf *xmit = &up->port.state->xmit;
>> +    int count = 16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0xF);
>> +
>> +    if (serial_in(up, UART_REG_FSR) & TX_FULL)
>> +        count = 0;
>> +    if (up->port.x_char) {
>> +        serial_out(up, UART_REG_THR, up->port.x_char);
>> +        up->port.icount.tx++;
>> +        up->port.x_char = 0;
>> +        return;
>> +    }
>> +    if (uart_tx_stopped(&up->port)) {
>> +        ma35d1serial_stop_tx(&up->port);
>> +        return;
>> +    }
>> +    if (uart_circ_empty(xmit)) {
>> +        __stop_tx(up);
>> +        return;
>> +    }
>> +    while (count > 0) {
>> +        serial_out(up, UART_REG_THR, xmit->buf[xmit->tail]);
>> +        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
>> +        up->port.icount.tx++;
>> +        count--;
>> +        if (uart_circ_empty(xmit))
>> +            break;
>> +    }
>> +    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
>> +        uart_write_wakeup(&up->port);
>> +    if (uart_circ_empty(xmit))
>> +        __stop_tx(up);
>> +}
>> +
>> +static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)dev_id;
>> +    u32 isr, fsr;
>> +
>> +    isr = serial_in(up, UART_REG_ISR);
>> +    fsr = serial_in(up, UART_REG_FSR);
>> +    if (isr & (RDA_IF | RXTO_IF))
>> +        receive_chars(up);
>> +    if (isr & THRE_INT)
>> +        transmit_chars(up);
>> +    if (fsr & (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF))
>> +        serial_out(up, UART_REG_FSR,
>> +               (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF));
>> +
>> +    return IRQ_HANDLED;
>
> You give no way for OS to disable the irq when the HW goes crazy. I.e. 
> you should return IRQ_HANDLED only when you really handled the irq.
>
>> +}
> ...
>> +static u32 ma35d1serial_get_mctrl(struct uart_port *port)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +    u32 status;
>> +    u32 ret = 0;
>> +
>> +    status = serial_in(up, UART_REG_MSR);
>> +    if (!(status & 0x10))
>
> 0x10 is magic.
>
>> +        ret |= TIOCM_CTS;
>> +    return ret;
>> +}
>> +
>> +static void ma35d1serial_set_mctrl(struct uart_port *port, u32 mctrl)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +    u32 mcr = 0;
>> +    u32 ier = 0;
>> +
>> +    if (mctrl & TIOCM_RTS) {
>> +        /* set RTS high level trigger */
>> +        mcr = serial_in(up, UART_REG_MCR);
>> +        mcr |= 0x200;
>> +        mcr &= ~(0x2);
>> +    }
>> +    if (up->mcr & UART_MCR_AFE) {
>> +        /* set RTS high level trigger */
>> +        mcr = serial_in(up, UART_REG_MCR);
>> +        mcr |= 0x200;
>> +        mcr &= ~(0x2);
>
> This is repeated. Parentheses are superfluous. And again, 0x200, 0x2 
> are magic.
>
>> +
>> +        /* enable CTS/RTS auto-flow control */
>> +        serial_out(up, UART_REG_IER,
>> +               (serial_in(up, UART_REG_IER) | (0x3000)));
>> +
>> +        /* Set hardware flow control */
>> +        up->port.flags |= UPF_HARD_FLOW;
>> +    } else {
>> +        /* disable CTS/RTS auto-flow control */
>> +        ier = serial_in(up, UART_REG_IER);
>> +        ier &= ~(0x3000);
>
> Detto.
>
>> +        serial_out(up, UART_REG_IER, ier);
>> +
>> +        /* un-set hardware flow control */
>> +        up->port.flags &= ~UPF_HARD_FLOW;
>> +    }
>> +
>> +    /* set CTS high level trigger */
>> +    serial_out(up, UART_REG_MSR, (serial_in(up, UART_REG_MSR) | 
>> (0x100)));
>> +    serial_out(up, UART_REG_MCR, mcr);
>> +}
> ...
>> +static int ma35d1serial_startup(struct uart_port *port)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +    struct tty_struct *tty = port->state->port.tty;
>> +    int retval;
>> +
>> +    /* Reset FIFO */
>> +    serial_out(up, UART_REG_FCR, TFR | RFR /* | RX_DIS */);
>
> So why not RX_DIS?
>
>> +
>> +    /* Clear pending interrupts */
>> +    serial_out(up, UART_REG_ISR, 0xFFFFFFFF);
>> +
>> +    retval = request_irq(port->irq, ma35d1serial_interrupt, 0,
>> +                 tty ? tty->name : "ma35d1_serial", port);
>> +    if (retval) {
>> +        dev_err(up->port.dev, "request irq failed.\n");
>> +        return retval;
>> +    }
>> +
>> +    /* Now, initialize the UART */
>> +    /* FIFO trigger level 4 byte */
>> +    /* RTS trigger level 8 bytes */
>> +    serial_out(up, UART_REG_FCR,
>> +           serial_in(up, UART_REG_FCR) | 0x10 | 0x20000);
>> +    serial_out(up, UART_REG_LCR, 0x7); /* 8 bit */
>> +    serial_out(up, UART_REG_TOR, 0x40);
>
> You know what.
>
>> +    serial_out(up, UART_REG_IER,
>> +           RTO_IEN | RDA_IEN | TIME_OUT_EN | BUFERR_IEN);
>> +    return 0;
>> +}
>> +
>> +static void ma35d1serial_shutdown(struct uart_port *port)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +
>> +    free_irq(port->irq, port);
>> +
>> +    /* Disable interrupts from this port */
>> +    serial_out(up, UART_REG_IER, 0);
>
> The two lines are switched, IMO. First disable HW, then let the ISR 
> finish and free it.

You're right. I will fix it.

>
>> +}
>> +
>> +static u32 ma35d1serial_get_divisor(struct uart_port *port, u32 baud)
>> +{
>> +    u32 quot;
>> +
>> +    quot = (port->uartclk / baud) - 2;
>> +    return quot;
>
> quot variable is completely superfluous.

remove quot
and
return (port->uartclk / baud) - 2;

>
>> +}
>> +
>> +static void ma35d1serial_set_termios(struct uart_port *port,
>> +                     struct ktermios *termios,
>> +                     const struct ktermios *old)
>> +{
>> +    struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +    u32 lcr = 0;
>> +    unsigned long flags;
>> +    u32 baud, quot;
>> +
>> +    switch (termios->c_cflag & CSIZE) {
>> +    case CS5:
>> +        lcr = 0;
>> +        break;
>> +    case CS6:
>> +        lcr |= 1;
>> +        break;
>> +    case CS7:
>> +        lcr |= 2;
>> +        break;
>> +    case CS8:
>> +    default:
>> +        lcr |= 3;
>> +        break;
>> +    }
>
> IOW:
> lcr = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));
>
All registers in this uart controller are 32-bits.
lcr will finally be written to the register UART_REG_LCR.of
which bit[1:0] represents for UART word length.
00 - 5 bits
01 - 6 bits
10 - 7 bits
11 - 8bits

Instead, I will add
#define WLS_5    0x0
#define WLS_6    0x1
#define WLS_7    0x2
#define WLS_8    0x3

and replace lcr |= 1 with
lcr |= WLS_6;

>> +
>> +    if (termios->c_cflag & CSTOPB)
>> +        lcr |= NSB;
>> +    if (termios->c_cflag & PARENB)
>> +        lcr |= PBE;
>> +    if (!(termios->c_cflag & PARODD))
>> +        lcr |= EPE;
>> +    if (termios->c_cflag & CMSPAR)
>> +        lcr |= SPE;
>> +
>> +    baud = uart_get_baud_rate(port, termios, old, port->uartclk / 
>> 0xffff,
>> +                  port->uartclk / 11);
>> +
>> +    quot = ma35d1serial_get_divisor(port, baud);
>> +
>> +    /*
>> +     * Ok, we're now changing the port state.  Do it with
>> +     * interrupts disabled.
>> +     */
>> +    spin_lock_irqsave(&up->port.lock, flags);
>> +
>> +    up->port.read_status_mask = RX_OVER_IF;
>> +    if (termios->c_iflag & INPCK)
>> +        up->port.read_status_mask |= FEF | PEF;
>> +    if (termios->c_iflag & (BRKINT | PARMRK))
>> +        up->port.read_status_mask |= BIF;
>> +
>> +    /*
>> +     * Characteres to ignore
>> +     */
>> +    up->port.ignore_status_mask = 0;
>> +    if (termios->c_iflag & IGNPAR)
>> +        up->port.ignore_status_mask |= FEF | PEF;
>> +    if (termios->c_iflag & IGNBRK) {
>> +        up->port.ignore_status_mask |= BIF;
>> +        /*
>> +         * If we're ignoring parity and break indicators,
>> +         * ignore overruns too (for real raw support).
>> +         */
>> +        if (termios->c_iflag & IGNPAR)
>> +            up->port.ignore_status_mask |= RX_OVER_IF;
>> +    }
>> +    if (termios->c_cflag & CRTSCTS)
>> +        up->mcr |= UART_MCR_AFE;
>> +    else
>> +        up->mcr &= ~UART_MCR_AFE;
>> +
>> +    ma35d1serial_set_mctrl(&up->port, up->port.mctrl);
>> +    serial_out(up, UART_REG_BAUD, quot | 0x30000000);
>> +    serial_out(up, UART_REG_LCR, lcr);
>> +    spin_unlock_irqrestore(&up->port.lock, flags);
>> +}
> ...
>> +static void ma35d1serial_config_port(struct uart_port *port, int flags)
>> +{
>> +    int ret;
>> +
>> +    /*
>> +     * Find the region that we can probe for.  This in turn
>> +     * tells us whether we can probe for the type of port.
>> +     */
>> +    ret = ma35d1serial_request_port(port);
>> +    if (ret < 0)
>> +        return;
>
> ma35d1serial_request_port() does nothing. You can remove it altogether.
>

OK, I will remove it.

>> +    port->type = PORT_MA35D1;
>> +}
>
>
>> +static int ma35d1serial_ioctl(struct uart_port *port, u32 cmd, 
>> unsigned long arg)
>> +{
>> +    switch (cmd) {
>> +    default:
>> +        return -ENOIOCTLCMD;
>> +    }
>> +    return 0;
>> +}
>
> Drop that completely.
>

OK.

>> +static void
>> +ma35d1serial_console_init_port(void)
>> +{
>> +    int i = 0;
>> +    struct device_node *np;
>> +
>> +    for_each_matching_node(np, ma35d1_serial_of_match) {
>> +        if (ma35d1serial_uart_nodes[i] == NULL) {
>> +            ma35d1serial_uart_nodes[i] = np;
>> +            i++;
>
> Unless the dt is broken, this is OK. But I would add a sanity check to i.
>

I will add
if (i == UART_NR)
     break;


>> +        }
>> +    }
>> +}
> ...
>> +/*
>> + * Register a set of serial devices attached to a platform device.
>> + * The list is terminated with a zero flags entry, which means we 
>> expect
>> + * all entries to have at least UPF_BOOT_AUTOCONF set.
>> + */
>> +static int ma35d1serial_probe(struct platform_device *pdev)
>> +{
>> +    struct resource *res_mem;
>> +    struct uart_ma35d1_port *up;
>> +    int ret;
>> +    struct clk *clk;
>> +    int err;
>> +
>> +    if (pdev->dev.of_node) {
>> +        ret = of_alias_get_id(pdev->dev.of_node, "serial");
>> +        if (ret < 0) {
>> +            dev_err(&pdev->dev,
>> +                "failed to get alias/pdev id, errno %d\n",
>> +                ret);
>> +        return ret;
>> +        }
>> +    }
>> +    up = &ma35d1serial_ports[ret];
>> +    up->port.line = ret;
>> +    res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    if (!res_mem)
>> +        return -ENODEV;
>> +
>> +    up->port.iobase = res_mem->start;
>> +    up->port.membase = ioremap(up->port.iobase, 0x10000);
>> +    up->port.ops = &ma35d1serial_ops;
>> +
>> +    spin_lock_init(&up->port.lock);
>> +
>> +    clk = of_clk_get(pdev->dev.of_node, 0);
>> +    if (IS_ERR(clk)) {
>> +        err = PTR_ERR(clk);
>> +        dev_err(&pdev->dev, "failed to get core clk: %d\n", err);
>> +        return -ENOENT;
>> +    }
>> +    err = clk_prepare_enable(clk);
>> +    if (err)
>> +        return -ENOENT;
>> +
>> +    if (up->port.line != 0)
>> +        up->port.uartclk = clk_get_rate(clk);
>> +    up->port.irq = platform_get_irq(pdev, 0);
>> +    up->port.dev = &pdev->dev;
>> +    up->port.flags = UPF_BOOT_AUTOCONF;
>> +    up->port.rs485_config = ma35d1serial_config_rs485;
>> +    ret = uart_add_one_port(&ma35d1serial_reg, &up->port);
>
> What if this fails?
>

I will add return value check.

>> +    platform_set_drvdata(pdev, up);
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Remove serial ports registered against a platform device.
>> + */
>> +static int ma35d1serial_remove(struct platform_device *dev)
>> +{
>> +    int i;
>> +    struct uart_port *port = platform_get_drvdata(dev);
>> +
>> +    free_irq(port->irq, port);
>
> Hmm, this doesn't look right. You did that already, or?
>
>> +    for (i = 0; i < UART_NR; i++) {
>> +        struct uart_ma35d1_port *up = &ma35d1serial_ports[i];
>> +
>> +        if (up->port.dev == &dev->dev)
>
> You did platform_set_drvdata(), so why all this?
>

static int ma35d1serial_remove(struct platform_device *dev)
{
     struct uart_port *port = platform_get_drvdata(dev);

     uart_remove_one_port(&ma35d1serial_reg, &up->port);
     free_irq(port->irq, port);
     return 0;
}


>> + uart_remove_one_port(&ma35d1serial_reg, &up->port);
>> +    }
>> +    return 0;
>> +}
>


Best Regards,

Jacky Huang



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

* Re: [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager
  2023-03-15  7:28 ` [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager Jacky Huang
@ 2023-03-16 13:30   ` Ilpo Järvinen
  2023-03-17  6:51     ` Jacky Huang
  2023-03-16 14:44   ` Arnd Bergmann
  1 sibling, 1 reply; 89+ messages in thread
From: Ilpo Järvinen @ 2023-03-16 13:30 UTC (permalink / raw)
  To: Jacky Huang
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, devicetree, linux-clk, linux-kernel,
	linux-serial, schung, Jacky Huang

On Wed, 15 Mar 2023, Jacky Huang wrote:

> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> The system manager is a set of registers used for power control,
> multi-function pin control, USB phy control, IP reset, and other
> miscellaneous controls. It also contains some registers that
> provide SoC information and status.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  include/linux/mfd/ma35d1-sys.h | 95 ++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>  create mode 100644 include/linux/mfd/ma35d1-sys.h
> 
> diff --git a/include/linux/mfd/ma35d1-sys.h b/include/linux/mfd/ma35d1-sys.h
> new file mode 100644
> index 000000000000..dcd85231125d
> --- /dev/null
> +++ b/include/linux/mfd/ma35d1-sys.h
> @@ -0,0 +1,95 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2023 Nuvoton Technologies.
> + * Author: Chi-Fen Li <cfli0@nuvoton.com>
> + *
> + * System management control registers of MA35D1 SoC
> + */
> +#ifndef __LINUX_MFD_MA35D1_SYS_H
> +#define __LINUX_MFD_MA35D1_SYS_H
> +
> +#define REG_SYS_PDID		(0x000) /* Product and Device Identifier */
> +#define REG_SYS_PWRONOTP	(0x004) /* Power-on Setting OTP Source */
> +#define REG_SYS_PWRONPIN	(0x008) /* Power-on Setting Pin Source */
> +#define REG_SYS_RSTSTS		(0x010) /* Reset Source Active Status */
> +#define REG_SYS_MISCRFCR	(0x014) /* Miscellaneous Reset Function */
> +#define REG_SYS_RSTDEBCTL	(0x018) /* Reset Pin De-bounce Control */
> +#define REG_SYS_LVRDCR		(0x01C) /* Low Voltage Reset & Detect */
> +#define REG_SYS_IPRST0		(0x020) /* Reset Control Register 0 */
> +#define REG_SYS_IPRST1		(0x024) /* Reset Control Register 1 */
> +#define REG_SYS_IPRST2		(0x028) /* Reset Control Register 2 */
> +#define REG_SYS_IPRST3		(0x02C) /* Reset Control Register 3 */
> +#define REG_SYS_PMUCR		(0x030) /* Power Management Unit Control */
> +#define REG_SYS_DDRCQCSR	(0x034) /* DDR Q Channel Control and Status */
> +#define REG_SYS_PMUIEN		(0x038) /* PMU Interrupt Enable */
> +#define REG_SYS_PMUSTS		(0x03C) /* PMU Status */
> +#define REG_SYS_CA35WRBADR1	(0x040) /* A35 Core 1 Warm-boot Address */
> +#define REG_SYS_CA35WRBPAR1	(0x044) /* A35 Core 1 Warm-boot Parameter */
> +#define REG_SYS_CA35WRBADR2	(0x048) /* A35 Core 2 Warm-boot Address */
> +#define REG_SYS_CA35WRBPAR2	(0x04C) /* A35 Core 2 Warm-boot Parameter */
> +#define REG_SYS_USBPMISCR	(0x060) /* USB PHY Miscellaneous Control */
> +#define REG_SYS_USBP0PCR	(0x064) /* USB Port 0 PHY Control */
> +#define REG_SYS_USBP1PCR	(0x068) /* USB Port 1 PHY Control */
> +#define REG_SYS_MISCFCR0	(0x070) /* Miscellaneous Function Control 0 */
> +#define REG_SYS_MISCFCR1	(0x074) /* Miscellaneous Function Control 1 */
> +#define REG_SYS_MISCIER		(0x078) /* Miscellaneous Interrupt Enable */
> +#define REG_SYS_MISCISR		(0x07C) /* Miscellaneous Interrupt Status */
> +#define REG_SYS_GPA_MFPL	(0x080) /* GPIOA Multi-Function Control LSB */
> +#define REG_SYS_GPA_MFPH	(0x084) /* GPIOA Multi-Function Control MSB */
> +#define REG_SYS_GPB_MFPL	(0x088) /* GPIOB Multi-Function Control LSB */
> +#define REG_SYS_GPB_MFPH	(0x08C) /* GPIOB Multi-Function Control MSB */
> +#define REG_SYS_GPC_MFPL	(0x090) /* GPIOC Multi-Function Control LSB */
> +#define REG_SYS_GPC_MFPH	(0x094) /* GPIOC Multi-Function Control MSB */
> +#define REG_SYS_GPD_MFPL	(0x098) /* GPIOD Multi-Function Control LSB */
> +#define REG_SYS_GPD_MFPH	(0x09C) /* GPIOD Multi-Function Control MSB */
> +#define REG_SYS_GPE_MFPL	(0x0A0) /* GPIOE Multi-Function Control LSB */
> +#define REG_SYS_GPE_MFPH	(0x0A4) /* GPIOE Multi-Function Control MSB */
> +#define REG_SYS_GPF_MFPL	(0x0A8) /* GPIOF Multi-Function Control LSB */
> +#define REG_SYS_GPF_MFPH	(0x0AC) /* GPIOF Multi-Function Control MSB */
> +#define REG_SYS_GPG_MFPL	(0x0B0) /* GPIOG Multi-Function Control LSB */
> +#define REG_SYS_GPG_MFPH	(0x0B4) /* GPIOG Multi-Function Control MSB */
> +#define REG_SYS_GPH_MFPL	(0x0B8) /* GPIOH Multi-Function Control LSB */
> +#define REG_SYS_GPH_MFPH	(0x0BC) /* GPIOH Multi-Function Control MSB */
> +#define REG_SYS_GPI_MFPL	(0x0C0) /* GPIOI Multi-Function Control LSB */
> +#define REG_SYS_GPI_MFPH	(0x0C4) /* GPIOI Multi-Function Control MSB */
> +#define REG_SYS_GPJ_MFPL	(0x0C8) /* GPIOJ Multi-Function Control LSB */
> +#define REG_SYS_GPJ_MFPH	(0x0CC) /* GPIOJ Multi-Function Control MSB */
> +#define REG_SYS_GPK_MFPL	(0x0D0) /* GPIOK Multi-Function Control LSB */
> +#define REG_SYS_GPK_MFPH	(0x0D4) /* GPIOK Multi-Function Control MSB */
> +#define REG_SYS_GPL_MFPL	(0x0D8) /* GPIOL Multi-Function Control LSB */
> +#define REG_SYS_GPL_MFPH	(0x0DC) /* GPIOL Multi-Function Control MSB */
> +#define REG_SYS_GPM_MFPL	(0x0E0) /* GPIOM Multi-Function Control LSB */
> +#define REG_SYS_GPM_MFPH	(0x0E4) /* GPIOM Multi-Function Control MSB */
> +#define REG_SYS_GPN_MFPL	(0x0E8) /* GPION Multi-Function Control LSB */
> +#define REG_SYS_GPN_MFPH	(0x0EC) /* GPION Multi-Function Control MSB */
> +#define REG_SYS_HIRCFTRIM	(0x100) /* HIRC Frequency Trim Value */
> +#define REG_SYS_TSENSRFCR	(0x104) /* Temperature Sensor Control */
> +#define REG_SYS_GMAC0MISCR	(0x108) /* GMAC 0 Miscellaneous Control */
> +#define REG_SYS_GMAC1MISCR	(0x10C) /* GMAC 1 Miscellaneous Control */
> +#define REG_SYS_MACAD0LSR	(0x110) /* MAC Address 0 LSW */
> +#define REG_SYS_MACAD0HSR	(0x114) /* MAC Address 0 HSW */
> +#define REG_SYS_MACAD1LSR	(0x118) /* MAC Address 1 LSW */
> +#define REG_SYS_MACAD1HSR	(0x11C) /* MAC Address 1 HSW */
> +#define REG_SYS_CSDBGCTL	(0x120) /* CoreSight Debug Control */
> +#define REG_SYS_GPAB_MFOS	(0x140) /* GPIOA/B Output Mode Select */
> +#define REG_SYS_GPCD_MFOS	(0x144) /* GPIOC/D Output Mode Select */
> +#define REG_SYS_GPEF_MFOS	(0x148) /* GPIOE/F Output Mode Select */
> +#define REG_SYS_GPGH_MFOS	(0x14C) /* GPIOG/H Output Mode Select */
> +#define REG_SYS_GPIJ_MFOS	(0x150) /* GPIOI/J Output Mode Select */
> +#define REG_SYS_GPKL_MFOS	(0x154) /* GPIOK/L Output Mode Select */
> +#define REG_SYS_GPMN_MFOS	(0x158) /* GPIOM/N Output Mode Select */
> +#define REG_SYS_UID0		(0x180) /* Unique Identifier Word 0 */
> +#define REG_SYS_UID1		(0x184) /* Unique Identifier Word 1 */
> +#define REG_SYS_UID2		(0x188) /* Unique Identifier Word 2 */
> +#define REG_SYS_UCID0		(0x190) /* Unique Customer Identifier 0 */
> +#define REG_SYS_UCID1		(0x194) /* Unique Customer Identifier 1 */
> +#define REG_SYS_UCID2		(0x198) /* Unique Customer Identifier 2 */
> +#define REG_SYS_RLKTZS		(0x1A0) /* TZS Register Lock Control */
> +#define REG_SYS_RLKTZNS		(0x1A4) /* TZNS Register Lock Control */
> +#define REG_SYS_RLKSUBM		(0x1A8) /* SubM Register Lock Control */
> +#define REG_SYS_DPLPASWD	(0x1B0) /* Deployed Password */

Remove the extra set of parenthesis from all those above. Hex numbers are 
easier to read with lowercased letters so please convert them all to 
lowercase.

-- 
 i.


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

* Re: [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller
  2023-03-16  7:31   ` Krzysztof Kozlowski
@ 2023-03-16 13:35     ` Jacky Huang
  2023-03-16 14:09       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-16 13:35 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang


Dear Krzysztof,



On 2023/3/16 下午 03:31, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Add the dt-bindings header for Nuvoton ma35d1, that gets shared
>> between the clock controller and clock references in the dts.
> I don't see the device binding. They come together.

I should move 
Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
into this patch, right?

>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   .../dt-bindings/clock/nuvoton,ma35d1-clk.h    | 253 ++++++++++++++++++
>>   1 file changed, 253 insertions(+)
>>   create mode 100644 include/dt-bindings/clock/nuvoton,ma35d1-clk.h
>>
>> diff --git a/include/dt-bindings/clock/nuvoton,ma35d1-clk.h b/include/dt-bindings/clock/nuvoton,ma35d1-clk.h
>> new file mode 100644
>> index 000000000000..6c569fdd6e06
>> --- /dev/null
>> +++ b/include/dt-bindings/clock/nuvoton,ma35d1-clk.h
>> @@ -0,0 +1,253 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
> Dual license.

OK, I will fix it.


> Best regards,
> Krzysztof
>


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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
                   ` (15 preceding siblings ...)
  2023-03-16  7:41 ` [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Krzysztof Kozlowski
@ 2023-03-16 14:05 ` Arnd Bergmann
  2023-03-17  6:30   ` Jacky Huang
  16 siblings, 1 reply; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-16 14:05 UTC (permalink / raw)
  To: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
>
> This patchset adds initial support for the Nuvoton ma35d1 SoC, including
> initial device tree, clock driver, reset driver, and serial driver.
>
> This patchset cover letter is based from the initial support for Nuvoton
> ma35d1 to keep tracking the version history.
>
> This patchset had been applied to Linux kernel 6.3-rc2 and tested on the
> Nuvoton ma35d1 SOM evaluation board.
>
> (ma35d1 information: 
> https://www.nuvoton.com/products/microprocessors/arm-cortex-a35-mpus/)
> MA35D1 porting on linux-5.10.y can be found at: 
> https://github.com/OpenNuvoton/MPU-Family

Hi Jacky,

Thanks a lot for your submission. I saw this presented at
EmbeddedWorld yesterday and asked about mainline Linux
support, but did not expect to see the patches this soon ;-)

The easiest process for getting the series merged is to
have me add it the entire series to the SoC tree after the
individual drivers have been reviewed by the respective
subsystem maintainers that are already on Cc here. When
the review is complete, you can add soc@kernel.org to Cc,
so they show up in patchwork, or alternatively send a pull
request for a git tree to that address. Until then, you
can add my own email address to Cc so I can follow the
reviews.

After the initial merge, the normal method for additional
device drivers is to have them sent for inclusion to the
subsystem maintainers. The soc tree and soc@kernel.org address
is then only used for changes in arch/arm64, i.e. updates
to the dts files, Kconfig, defconfig and MAINTAINERS, 
as well as the drivers/soc and drivers/firmware directories,
if you have anything in there.

If you have any additional questions about the process,
feel free to also ask me.

     Arnd

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

* Re: [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller
  2023-03-16 13:35     ` Jacky Huang
@ 2023-03-16 14:09       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-16 14:09 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 16/03/2023 14:35, Jacky Huang wrote:
> 
> Dear Krzysztof,
> 
> 
> 
> On 2023/3/16 下午 03:31, Krzysztof Kozlowski wrote:
>> On 15/03/2023 08:28, Jacky Huang wrote:
>>> From: Jacky Huang <ychuang3@nuvoton.com>
>>>
>>> Add the dt-bindings header for Nuvoton ma35d1, that gets shared
>>> between the clock controller and clock references in the dts.
>> I don't see the device binding. They come together.
> 
> I should move 
> Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
> into this patch, right?

Yes.



Best regards,
Krzysztof


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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-15  7:28 ` [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree Jacky Huang
  2023-03-16  7:45   ` Krzysztof Kozlowski
@ 2023-03-16 14:17   ` Arnd Bergmann
  2023-03-16 16:44     ` Lee Jones
  2023-03-18 13:17     ` Jacky Huang
  1 sibling, 2 replies; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-16 14:17 UTC (permalink / raw)
  To: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
> +	mem: memory@80000000 {
> +		device_type = "memory";
> +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
> +	};
> +};

In most machines, the memory size is detected by the boot loader
and filled in the dtb in memory before starting the kernel, so
you should not need two separate files here for the two common
memory configurations.

Since the machine is called 'som', I would assume that this is a
module that is integrated on another board, so more commonly one
would have a dtsi file for the som in addition to the one for the
soc, and have all the components of the module listed in this
file, while the dts file that includes the som.dtsi lists the
devices on the carrier board and enables the on-chip devices
that are connected to the outside.

       Arnd

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

* Re: [PATCH 02/15] arm64: defconfig: Add Nuvoton MA35 family support
  2023-03-15  7:28 ` [PATCH 02/15] arm64: defconfig: Add Nuvoton MA35 family support Jacky Huang
@ 2023-03-16 14:23   ` Arnd Bergmann
  2023-03-17  9:05     ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-16 14:23 UTC (permalink / raw)
  To: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
>
> Enable basic drivers for ma35d1 booting up support: architecture,
> device tree, clock, reset, and uart.
>
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>

The description does not seem to match the actual contents,
which only enable the platform but none of the drivers.

It's ok generally to enable options in the defconfig file
before you add the drivers, but if it's all part of a patch
series, I would probable more this bit to the end.

     Arnd

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

* Re: [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform
  2023-03-16  7:33   ` Krzysztof Kozlowski
@ 2023-03-16 14:32     ` Arnd Bergmann
  2023-03-18  1:26       ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-16 14:32 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Jacky Huang, Rob Herring,
	krzysztof.kozlowski+dt, Lee Jones, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Thu, Mar 16, 2023, at 08:33, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>> 
>> Add binding for ARMv8 based Nuvotn SoCs and platform boards.
>> Add initial bindings for ma35d1 series development boards.
>> 
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>  .../devicetree/bindings/arm/nuvoton.yaml      | 30 +++++++++++++++++++
>
> And what is npcm for? Why it was made an directory?
>
> All these should be just one Nuvoton.

npcm is an unrelated product line, so I think it would be best
to rename the npcm directory to nuvoton and move the new
file in there, though I'm not sure about the name or what the
other chips are called.

My impression is that this one is more closely related to
the older Arm9 nuc900/w90x900/n9 chips that we dropped from
the kernel a while ago, while the npcm family has a different
origin.

    Arnd

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

* Re: [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35
  2023-03-15  7:29 ` [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35 Jacky Huang
@ 2023-03-16 14:38   ` Arnd Bergmann
  2023-03-19 12:01     ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-16 14:38 UTC (permalink / raw)
  To: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby, Avi Fishman, Tali Perry,
	Tomer Maimon, Patrick Venture, Nancy Yuen, Benjamin Fair
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Wed, Mar 15, 2023, at 08:29, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
>
> Add entry for Nuvton ma35d1 maintainer and files
>
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---

> +F:	Documentation/devicetree/bindings/*/*nuvoton*
> +F:	arch/arm64/boot/dts/nuvoton/

This clashes with the existing entry for NPCM, so
contributors can easily get confused about where
to send their dts patches.

I don't have a good solution here, but maybe you can
discuss this with the npcm maintainers (added to Cc)
to see how they would like to handle this.

For me, the easiest way would be to have a single
maintainer send me all the patches for both ma35d1
and npcm, but that may not be practical for you.

> +F:	drivers/*/*/*ma35d1*
> +F:	drivers/*/*ma35d1*
> +F:	include/dt-bindings/*/*ma35d1*
> +F:	include/linux/mfd/ma35d1-sys.h

I would replace these with a single line

K:    ma35d1

that should have the same effect.

     Arnd

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

* Re: [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager
  2023-03-15  7:28 ` [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager Jacky Huang
  2023-03-16 13:30   ` Ilpo Järvinen
@ 2023-03-16 14:44   ` Arnd Bergmann
  2023-03-17  9:28     ` Jacky Huang
  1 sibling, 1 reply; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-16 14:44 UTC (permalink / raw)
  To: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
> From: Jacky Huang <ychuang3@nuvoton.com>
>
> The system manager is a set of registers used for power control,
> multi-function pin control, USB phy control, IP reset, and other
> miscellaneous controls. It also contains some registers that
> provide SoC information and status.
>
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  include/linux/mfd/ma35d1-sys.h | 95 ++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>  create mode 100644 include/linux/mfd/ma35d1-sys.h
>
> diff --git a/include/linux/mfd/ma35d1-sys.h b/include/linux/mfd/ma35d1-sys.h
> new file mode 100644
> index 000000000000..dcd85231125d
> --- /dev/null
> +++ b/include/linux/mfd/ma35d1-sys.h

> +
> +#define REG_SYS_PDID		(0x000) /* Product and Device Identifier */
> +#define REG_SYS_PWRONOTP	(0x004) /* Power-on Setting OTP Source */
> +#define REG_SYS_PWRONPIN	(0x008) /* Power-on Setting Pin Source */
> +#define REG_SYS_RSTSTS		(0x010) /* Reset Source Active Status */
...

It is a bit odd to have a header file in include/linux/mfd/
but only have the register numbers in there, and not an
actual drivers/mfd/ driver to go along with them.

I think what we often do is to just list the individual register
numbers in the drivers that need them and not have the central
header at all. On the other hand, I can see it's useful to
have this documented in one place, and we clearly don't want
to add a driver if none is needed.

Maybe Lee has a suggestion for how he'd like to handle this.

> +void ma35d1_reg_lock(void);
> +void ma35d1_reg_unlock(void);

These look like they were left over from an earlier version
of the code. Since you use the regmap framework, I think this
will take care of the locking for you.

       Arnd

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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-15  7:29 ` [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial " Jacky Huang
                     ` (2 preceding siblings ...)
  2023-03-15 10:13   ` Jiri Slaby
@ 2023-03-16 14:54   ` Ilpo Järvinen
  2023-03-20  8:23     ` Jacky Huang
  3 siblings, 1 reply; 89+ messages in thread
From: Ilpo Järvinen @ 2023-03-16 14:54 UTC (permalink / raw)
  To: Jacky Huang
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	Greg Kroah-Hartman, Jiri Slaby, devicetree, linux-clk, LKML,
	linux-serial, schung, Jacky Huang

[-- Attachment #1: Type: text/plain, Size: 33630 bytes --]

Hi,

I'll not note all things below because others have already seemingly 
commented many things.

On Wed, 15 Mar 2023, Jacky Huang wrote:

> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> This adds UART and console driver for Nuvoton ma35d1 Soc.
> 
> MA35D1 SoC provides up to 17 UART controllers, each with one uart port.
> The ma35d1 uart controller is not compatible with 8250.
> The uart controller supports:
>   - Full-duplex asynchronous communications
>   - Separates tx and tx 32/32 bytes entry FIFO for data payloads
>   - Hardware auto-flow control
>   - Programmable rx buffer trigger level (1/4/8/14/30 bytes)
>   - Individual programmable baud rate generator for each channel
>   - Supports nCTS, incoming data, rx FIFO reached threshold and
>     RS-485 Address Match (AAD mode) wake-up function
>   - Supports 8-bit rx buffer time-out detection function
>   - Programmable tx data delay time
>   - Supports Auto-Baud Rate measurement and baud rate compensation
>   - Supports break error, frame error, parity error and rx/tx buffer
>     overflow detection function
>   – Programmable number of data bit, 5-, 6-, 7-, 8- bit character
>   – Programmable parity bit, even, odd, no parity or stick parity bit
>     generation and detection
>   – Programmable stop bit, 1, 1.5, or 2 stop bit generation
>   - Supports IrDA SIR function mode
>   - Supports RS-485 function mode
>   – Supports RS-485 9-bit mode
>   – Supports hardware or software enables to program nRTS pin to control
>     RS-485 transmission direction
>   - Supports PDMA transfer function
>   - Support Single-wire function mode.

This list is probably copy-pasted from somewhere but it doesn't match what 
you implemented in the driver.

> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  drivers/tty/serial/Kconfig         |  18 +
>  drivers/tty/serial/Makefile        |   1 +
>  drivers/tty/serial/ma35d1_serial.c | 842 +++++++++++++++++++++++++++++
>  drivers/tty/serial/ma35d1_serial.h |  93 ++++
>  include/uapi/linux/serial_core.h   |   3 +
>  5 files changed, 957 insertions(+)
>  create mode 100644 drivers/tty/serial/ma35d1_serial.c
>  create mode 100644 drivers/tty/serial/ma35d1_serial.h
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index 625358f44419..cb47fe804595 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1562,6 +1562,24 @@ config SERIAL_SUNPLUS_CONSOLE
>  	  you can alter that using a kernel command line option such as
>  	  "console=ttySUPx".
>  
> +config SERIAL_NUVOTON_MA35D1
> +	tristate "Nuvoton MA35D1 family UART support"
> +	depends on ARCH_NUVOTON || COMPILE_TEST
> +	select SERIAL_CORE
> +	help
> +	  This driver supports Nuvoton MA35D1 family UART ports. If you would
> +	  like to use them, you must answer Y or M to this option. Note that
> +	  for use as console, it must be included in kernel and not as a
> +	  module
> +
> +config SERIAL_NUVOTON_MA35D1_CONSOLE
> +	bool "Console on a Nuvotn MA35D1 family UART port"
> +	depends on SERIAL_NUVOTON_MA35D1=y
> +	select SERIAL_CORE_CONSOLE
> +	help
> +	  Select this options if you'd like to use the UART port0 of the
> +	  Nuvoton MA35D1 family as a console.
> +
>  endmenu
>  
>  config SERIAL_MCTRL_GPIO
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index cd9afd9e3018..71ebeba06ff2 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -93,3 +93,4 @@ obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
>  
>  obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
>  obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
> +obj-$(CONFIG_SERIAL_NUVOTON_MA35D1)	+= ma35d1_serial.o
> diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c
> new file mode 100644
> index 000000000000..8940d07c3777
> --- /dev/null
> +++ b/drivers/tty/serial/ma35d1_serial.c
> @@ -0,0 +1,842 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  MA35D1 serial driver
> + *  Copyright (C) 2023 Nuvoton Technology Corp.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/ioport.h>
> +#include <linux/init.h>
> +#include <linux/console.h>
> +#include <linux/sysrq.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/clk.h>
> +#include <linux/serial_reg.h>
> +#include <linux/serial_core.h>
> +#include <linux/serial.h>
> +#include <linux/nmi.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/io.h>
> +#include <asm/irq.h>
> +#include <asm/serial.h>
> +#include "ma35d1_serial.h"
> +
> +#define UART_NR			17
> +
> +static struct uart_driver ma35d1serial_reg;
> +struct clk *clk;
> +
> +struct uart_ma35d1_port {
> +	struct uart_port port;
> +	u16 capabilities; /* port capabilities */
> +	u8 ier;
> +	u8 lcr;
> +	u8 mcr;
> +	u8 mcr_mask;   /* mask of user bits */
> +	u8 mcr_force;  /* mask of forced bits */
> +	struct serial_rs485 rs485; /* rs485 settings */
> +	u32 baud_rate;
> +	int rx_count;
> +	u32 console_baud_rate;
> +	u32 console_line;
> +	u32 console_int;
> +};
> +
> +static struct device_node *ma35d1serial_uart_nodes[UART_NR];
> +static struct uart_ma35d1_port ma35d1serial_ports[UART_NR] = { 0 };
> +static void __stop_tx(struct uart_ma35d1_port *p);
> +static void transmit_chars(struct uart_ma35d1_port *up);

Try to rearrange such that forward declarations are not necessary for 
functions.

> +static struct uart_ma35d1_port *to_ma35d1_uart_port(struct uart_port *uart)

static inline

> +{
> +	return container_of(uart, struct uart_ma35d1_port, port);
> +}
> +
> +static u32 serial_in(struct uart_ma35d1_port *p, int offset)
> +{
> +	return __raw_readl(p->port.membase + offset);
> +}
> +
> +static void serial_out(struct uart_ma35d1_port *p, int offset, int value)
> +{
> +	__raw_writel(value, p->port.membase + offset);
> +}
> +
> +static void __stop_tx(struct uart_ma35d1_port *p)
> +{
> +	u32 ier;
> +
> +	ier = serial_in(p, UART_REG_IER);
> +	if (ier & THRE_IEN)
> +		serial_out(p, UART_REG_IER, ier & ~THRE_IEN);
> +}
> +
> +static void ma35d1serial_stop_tx(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +
> +	__stop_tx(up);
> +}
> +
> +static void ma35d1serial_start_tx(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 ier;
> +	struct circ_buf *xmit = &up->port.state->xmit;
> +
> +	ier = serial_in(up, UART_REG_IER);
> +	serial_out(up, UART_REG_IER, ier & ~THRE_IEN);
> +	if (uart_circ_chars_pending(xmit) <
> +	    (16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0x3F)))
> +		transmit_chars(up);
> +	serial_out(up, UART_REG_IER, ier | THRE_IEN);
> +}
> +
> +static void ma35d1serial_stop_rx(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +
> +	serial_out(up, UART_REG_IER, serial_in(up, UART_REG_IER) & ~RDA_IEN);
> +}
> +
> +static void
> +receive_chars(struct uart_ma35d1_port *up)
> +{
> +	u8 ch;
> +	u32 fsr;
> +	u32 isr;
> +	u32 dcnt;
> +	char flag;
> +
> +	isr = serial_in(up, UART_REG_ISR);
> +	fsr = serial_in(up, UART_REG_FSR);
> +
> +	while (!(fsr & RX_EMPTY)) {
> +		flag = TTY_NORMAL;
> +		up->port.icount.rx++;
> +
> +		if (unlikely(fsr & (BIF | FEF | PEF | RX_OVER_IF))) {
> +			if (fsr & BIF) {
> +				serial_out(up, UART_REG_FSR, BIF);
> +				up->port.icount.brk++;
> +				if (uart_handle_break(&up->port))
> +					continue;
> +			}
> +			if (fsr & FEF) {
> +				serial_out(up, UART_REG_FSR, FEF);
> +				up->port.icount.frame++;
> +			}
> +			if (fsr & PEF) {
> +				serial_out(up, UART_REG_FSR, PEF);
> +				up->port.icount.parity++;
> +			}
> +			if (fsr & RX_OVER_IF) {
> +				serial_out(up, UART_REG_FSR, RX_OVER_IF);
> +				up->port.icount.overrun++;
> +			}

Do you need to write each of those individually to clear(?) them or could 
you just do one write here after the accounting is done:
			serial_out(up, UART_REG_FSR, fsr & (BIF|FEF|PEF|RX_OVER_IF));
?

Also, add some driver specific prefix for the flag naming (all driver 
specific ones, not just these if you have others besides these).


> +			if (fsr & BIF)
> +				flag = TTY_BREAK;
> +			if (fsr & PEF)
> +				flag = TTY_PARITY;
> +			if (fsr & FEF)
> +				flag = TTY_FRAME;

Are you sure this is the right prioritization or do you perhaps want else 
ifs like in some other serial drivers?

> +		}
> +		ch = (u8)serial_in(up, UART_REG_RBR);

Drop the case.

> +		if (uart_handle_sysrq_char(&up->port, ch))
> +			continue;
> +
> +		uart_insert_char(&up->port, fsr, RX_OVER_IF, ch, flag);
> +		up->rx_count++;
> +		dcnt = (serial_in(up, UART_REG_FSR) >> 8) & 0x3f;
> +		if (up->rx_count > 1023) {
> +			spin_lock(&up->port.lock);
> +			tty_flip_buffer_push(&up->port.state->port);
> +			spin_unlock(&up->port.lock);
> +			up->rx_count = 0;

Why is all this ->rx_count trickery necessary? What's so special with the 
size in question?

> +			if ((isr & RXTO_IF) && (dcnt == 0))
> +				goto tout_end;
> +		}
> +		if (isr & RDA_IF) {
> +			if (dcnt == 1)

Merge to the same comdition.

dcnt could probably have a more descriptive name.

> +				return;
> +		}
> +		fsr = serial_in(up, UART_REG_FSR);
> +	}
> +	spin_lock(&up->port.lock);
> +	tty_flip_buffer_push(&up->port.state->port);
> +	spin_unlock(&up->port.lock);
> +tout_end:
> +	up->rx_count = 0;
> +}
> +
> +static void transmit_chars(struct uart_ma35d1_port *up)
> +{
> +	struct circ_buf *xmit = &up->port.state->xmit;
> +	int count = 16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0xF);
> +
> +	if (serial_in(up, UART_REG_FSR) & TX_FULL)
> +		count = 0;
> +	if (up->port.x_char) {
> +		serial_out(up, UART_REG_THR, up->port.x_char);
> +		up->port.icount.tx++;
> +		up->port.x_char = 0;
> +		return;
> +	}
> +	if (uart_tx_stopped(&up->port)) {
> +		ma35d1serial_stop_tx(&up->port);
> +		return;
> +	}
> +	if (uart_circ_empty(xmit)) {
> +		__stop_tx(up);
> +		return;
> +	}
> +	while (count > 0) {
> +		serial_out(up, UART_REG_THR, xmit->buf[xmit->tail]);
> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> +		up->port.icount.tx++;
> +		count--;
> +		if (uart_circ_empty(xmit))
> +			break;
> +	}
> +	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +		uart_write_wakeup(&up->port);
> +	if (uart_circ_empty(xmit))
> +		__stop_tx(up);
> +}
> +
> +static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)dev_id;
> +	u32 isr, fsr;
> +
> +	isr = serial_in(up, UART_REG_ISR);
> +	fsr = serial_in(up, UART_REG_FSR);
> +	if (isr & (RDA_IF | RXTO_IF))
> +		receive_chars(up);
> +	if (isr & THRE_INT)
> +		transmit_chars(up);
> +	if (fsr & (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF))
> +		serial_out(up, UART_REG_FSR,
> +			   (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF));

Hmm... Why write these again here... Didn't receive_chars() already 
clear(?) most of these bits??

> +
> +	return IRQ_HANDLED;
> +}
> +
> +static u32 ma35d1serial_tx_empty(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 fsr;
> +
> +	fsr = serial_in(up, UART_REG_FSR);
> +	return (fsr & (TE_FLAG | TX_EMPTY)) == (TE_FLAG | TX_EMPTY) ?
> +		TIOCSER_TEMT : 0;
> +}
> +
> +static u32 ma35d1serial_get_mctrl(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 status;
> +	u32 ret = 0;
> +
> +	status = serial_in(up, UART_REG_MSR);
> +	if (!(status & 0x10))
> +		ret |= TIOCM_CTS;
> +	return ret;
> +}
> +
> +static void ma35d1serial_set_mctrl(struct uart_port *port, u32 mctrl)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 mcr = 0;
> +	u32 ier = 0;
> +
> +	if (mctrl & TIOCM_RTS) {
> +		/* set RTS high level trigger */
> +		mcr = serial_in(up, UART_REG_MCR);
> +		mcr |= 0x200;
> +		mcr &= ~(0x2);
> +	}
> +	if (up->mcr & UART_MCR_AFE) {
> +		/* set RTS high level trigger */
> +		mcr = serial_in(up, UART_REG_MCR);
> +		mcr |= 0x200;
> +		mcr &= ~(0x2);
> +
> +		/* enable CTS/RTS auto-flow control */
> +		serial_out(up, UART_REG_IER,
> +			   (serial_in(up, UART_REG_IER) | (0x3000)));

Once you have named the bits probably with defines, most of the comments 
such as the one above are rendered redundant and should be dropped.

> +
> +		/* Set hardware flow control */
> +		up->port.flags |= UPF_HARD_FLOW;
> +	} else {
> +		/* disable CTS/RTS auto-flow control */
> +		ier = serial_in(up, UART_REG_IER);
> +		ier &= ~(0x3000);
> +		serial_out(up, UART_REG_IER, ier);
> +
> +		/* un-set hardware flow control */
> +		up->port.flags &= ~UPF_HARD_FLOW;
> +	}
> +
> +	/* set CTS high level trigger */
> +	serial_out(up, UART_REG_MSR, (serial_in(up, UART_REG_MSR) | (0x100)));
> +	serial_out(up, UART_REG_MCR, mcr);
> +}
> +
> +static void ma35d1serial_break_ctl(struct uart_port *port, int break_state)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	unsigned long flags;
> +	u32 lcr;
> +
> +	spin_lock_irqsave(&up->port.lock, flags);
> +	lcr = serial_in(up, UART_REG_LCR);
> +	if (break_state != 0)
> +		lcr |= BCB; /* set break */
> +	else
> +		lcr &= ~BCB; /* clr break */

While BCB might come from HW naming, a better name for would make these 
very obvious (and the comments unnecessary).

> +	serial_out(up, UART_REG_LCR, lcr);
> +	spin_unlock_irqrestore(&up->port.lock, flags);
> +}
> +
> +static int ma35d1serial_startup(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	struct tty_struct *tty = port->state->port.tty;
> +	int retval;
> +
> +	/* Reset FIFO */
> +	serial_out(up, UART_REG_FCR, TFR | RFR /* | RX_DIS */);
> +
> +	/* Clear pending interrupts */
> +	serial_out(up, UART_REG_ISR, 0xFFFFFFFF);

~0 is another option.

> +
> +	retval = request_irq(port->irq, ma35d1serial_interrupt, 0,
> +			     tty ? tty->name : "ma35d1_serial", port);

Why such exceptional name trickery?

> +	if (retval) {
> +		dev_err(up->port.dev, "request irq failed.\n");
> +		return retval;
> +	}
> +
> +	/* Now, initialize the UART */
> +	/* FIFO trigger level 4 byte */
> +	/* RTS trigger level 8 bytes */
> +	serial_out(up, UART_REG_FCR,
> +		   serial_in(up, UART_REG_FCR) | 0x10 | 0x20000);
> +	serial_out(up, UART_REG_LCR, 0x7); /* 8 bit */
> +	serial_out(up, UART_REG_TOR, 0x40);
> +	serial_out(up, UART_REG_IER,
> +		   RTO_IEN | RDA_IEN | TIME_OUT_EN | BUFERR_IEN);
> +	return 0;
> +}
> +
> +static void ma35d1serial_shutdown(struct uart_port *port)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +
> +	free_irq(port->irq, port);
> +
> +	/* Disable interrupts from this port */
> +	serial_out(up, UART_REG_IER, 0);
> +}
> +
> +static u32 ma35d1serial_get_divisor(struct uart_port *port, u32 baud)
> +{
> +	u32 quot;
> +
> +	quot = (port->uartclk / baud) - 2;

Unnecessary parenthesis. (+Somebody already commented about the 
unnecessary variable.)

> +	return quot;
> +}
> +
> +static void ma35d1serial_set_termios(struct uart_port *port,
> +				     struct ktermios *termios,
> +				     const struct ktermios *old)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +	u32 lcr = 0;
> +	unsigned long flags;
> +	u32 baud, quot;
> +
> +	switch (termios->c_cflag & CSIZE) {
> +	case CS5:
> +		lcr = 0;
> +		break;
> +	case CS6:
> +		lcr |= 1;
> +		break;
> +	case CS7:
> +		lcr |= 2;
> +		break;
> +	case CS8:
> +	default:
> +		lcr |= 3;
> +		break;
> +	}
> +
> +	if (termios->c_cflag & CSTOPB)
> +		lcr |= NSB;
> +	if (termios->c_cflag & PARENB)
> +		lcr |= PBE;
> +	if (!(termios->c_cflag & PARODD))
> +		lcr |= EPE;
> +	if (termios->c_cflag & CMSPAR)
> +		lcr |= SPE;
> +
> +	baud = uart_get_baud_rate(port, termios, old, port->uartclk / 0xffff,
> +				  port->uartclk / 11);
> +
> +	quot = ma35d1serial_get_divisor(port, baud);
> +
> +	/*
> +	 * Ok, we're now changing the port state.  Do it with
> +	 * interrupts disabled.
> +	 */
> +	spin_lock_irqsave(&up->port.lock, flags);
> +
> +	up->port.read_status_mask = RX_OVER_IF;
> +	if (termios->c_iflag & INPCK)
> +		up->port.read_status_mask |= FEF | PEF;
> +	if (termios->c_iflag & (BRKINT | PARMRK))
> +		up->port.read_status_mask |= BIF;
> +
> +	/*
> +	 * Characteres to ignore
> +	 */
> +	up->port.ignore_status_mask = 0;
> +	if (termios->c_iflag & IGNPAR)
> +		up->port.ignore_status_mask |= FEF | PEF;
> +	if (termios->c_iflag & IGNBRK) {
> +		up->port.ignore_status_mask |= BIF;
> +		/*
> +		 * If we're ignoring parity and break indicators,
> +		 * ignore overruns too (for real raw support).
> +		 */
> +		if (termios->c_iflag & IGNPAR)
> +			up->port.ignore_status_mask |= RX_OVER_IF;
> +	}
> +	if (termios->c_cflag & CRTSCTS)
> +		up->mcr |= UART_MCR_AFE;
> +	else
> +		up->mcr &= ~UART_MCR_AFE;
> +
> +	ma35d1serial_set_mctrl(&up->port, up->port.mctrl);
> +	serial_out(up, UART_REG_BAUD, quot | 0x30000000);
> +	serial_out(up, UART_REG_LCR, lcr);

You need to do uart_update_timeout() in the set_termios function.

> +	spin_unlock_irqrestore(&up->port.lock, flags);
> +}
> +
> +static void ma35d1serial_release_port(struct uart_port *port)
> +{
> +	iounmap(port->membase);
> +	port->membase = NULL;
> +}
> +
> +static int ma35d1serial_request_port(struct uart_port *port)
> +{
> +	return 0;
> +}
> +
> +static void ma35d1serial_config_port(struct uart_port *port, int flags)
> +{
> +	int ret;
> +
> +	/*
> +	 * Find the region that we can probe for.  This in turn
> +	 * tells us whether we can probe for the type of port.
> +	 */
> +	ret = ma35d1serial_request_port(port);
> +	if (ret < 0)
> +		return;
> +	port->type = PORT_MA35D1;
> +}
> +
> +static int ma35d1serial_verify_port(struct uart_port *port,
> +				    struct serial_struct *ser)
> +{
> +	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MA35D1)
> +		return -EINVAL;
> +	return 0;
> +}
> +
> +static const char *ma35d1serial_type(struct uart_port *port)
> +{
> +	return (port->type == PORT_MA35D1) ? "MA35D1" : NULL;
> +}
> +
> +/* Enable or disable the rs485 support */
> +static int ma35d1serial_config_rs485(struct uart_port *port,
> +				     struct ktermios *termios,
> +				     struct serial_rs485 *rs485conf)
> +{
> +	struct uart_ma35d1_port *p = to_ma35d1_uart_port(port);
> +
> +	p->rs485 = *rs485conf;
> +
> +	if (p->rs485.delay_rts_before_send >= 1000)
> +		p->rs485.delay_rts_before_send = 1000;

Don't do this in driver, the core handles the delay limits. You don't seem 
to be using the value anyway for anything???

Please separate the RS485 support into its own patch.

> +	serial_out(p, UART_FUN_SEL,
> +		   (serial_in(p, UART_FUN_SEL) & ~FUN_SEL_MASK));
> +
> +	if (rs485conf->flags & SER_RS485_ENABLED) {
> +		serial_out(p, UART_FUN_SEL,
> +			   (serial_in(p, UART_FUN_SEL) | FUN_SEL_RS485));

Does this pair of serial_out()s glitch the RS485 line if ->rs485_config() 
is called while RS485 mode is already set?

Why you need to do serial_in() from the UART_FUN_SEL twice?

> +
> +		if (rs485conf->flags & SER_RS485_RTS_ON_SEND)
> +			serial_out(p, UART_REG_MCR,
> +				   (serial_in(p, UART_REG_MCR) & ~0x200));
> +		else
> +			serial_out(p, UART_REG_MCR,
> +				   (serial_in(p, UART_REG_MCR) | 0x200));
> +
> +		/* set auto direction mode */
> +		serial_out(p, UART_REG_ALT_CSR,
> +			   (serial_in(p, UART_REG_ALT_CSR) | (1 << 10)));
> +	}
> +	return 0;
> +}
> +
> +static int ma35d1serial_ioctl(struct uart_port *port, u32 cmd, unsigned long arg)
> +{
> +	switch (cmd) {
> +	default:
> +		return -ENOIOCTLCMD;
> +	}
> +	return 0;
> +}
> +
> +static const struct uart_ops ma35d1serial_ops = {
> +	.tx_empty     = ma35d1serial_tx_empty,
> +	.set_mctrl    = ma35d1serial_set_mctrl,
> +	.get_mctrl    = ma35d1serial_get_mctrl,
> +	.stop_tx      = ma35d1serial_stop_tx,
> +	.start_tx     = ma35d1serial_start_tx,
> +	.stop_rx      = ma35d1serial_stop_rx,
> +	.break_ctl    = ma35d1serial_break_ctl,
> +	.startup      = ma35d1serial_startup,
> +	.shutdown     = ma35d1serial_shutdown,
> +	.set_termios  = ma35d1serial_set_termios,
> +	.type         = ma35d1serial_type,
> +	.release_port = ma35d1serial_release_port,
> +	.request_port = ma35d1serial_request_port,
> +	.config_port  = ma35d1serial_config_port,
> +	.verify_port  = ma35d1serial_verify_port,
> +	.ioctl        = ma35d1serial_ioctl,
> +};
> +
> +static const struct of_device_id ma35d1_serial_of_match[] = {
> +	{ .compatible = "nuvoton,ma35d1-uart" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ma35d1_serial_of_match);
> +
> +#ifdef CONFIG_SERIAL_NUVOTON_MA35D1_CONSOLE
> +
> +static void ma35d1serial_console_putchar(struct uart_port *port,
> +					 unsigned char ch)
> +{
> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
> +
> +	do {
> +	} while ((serial_in(up, UART_REG_FSR) & TX_FULL));
> +	serial_out(up, UART_REG_THR, ch);
> +}
> +
> +/*
> + *  Print a string to the serial port trying not to disturb
> + *  any possible real use of the port...
> + *
> + *  The console_lock must be held when we get here.
> + */
> +static void ma35d1serial_console_write(struct console *co,
> +				       const char *s, u32 count)
> +{
> +	struct uart_ma35d1_port *up = &ma35d1serial_ports[co->index];
> +	unsigned long flags;
> +	u32 ier;
> +
> +	local_irq_save(flags);
> +
> +	/*
> +	 *  First save the IER then disable the interrupts
> +	 */
> +	ier = serial_in(up, UART_REG_IER);
> +	serial_out(up, UART_REG_IER, 0);
> +
> +	uart_console_write(&up->port, s, count, ma35d1serial_console_putchar);
> +
> +	/*
> +	 *  Finally, wait for transmitter to become empty
> +	 *  and restore the IER
> +	 */
> +	do {
> +	} while (!(serial_in(up, UART_REG_FSR) & TX_EMPTY));
> +	serial_out(up, UART_REG_IER, ier);
> +	local_irq_restore(flags);
> +}
> +
> +static int __init ma35d1serial_console_setup(struct console *co,
> +					     char *options)
> +{
> +	struct device_node *np = ma35d1serial_uart_nodes[co->index];
> +	struct uart_ma35d1_port *p = &ma35d1serial_ports[co->index];
> +	u32 val32[4];
> +	struct uart_port *port;
> +	int baud = 115200;
> +	int bits = 8;
> +	int parity = 'n';
> +	int flow = 'n';
> +
> +	/*
> +	 * Check whether an invalid uart number has been specified, and
> +	 * if so, search for the first available port that does have
> +	 * console support.
> +	 */
> +	if ((co->index < 0) || (co->index >= UART_NR)) {
> +		pr_debug("Console Port%x out of range\n", co->index);
> +		return -EINVAL;
> +	}
> +
> +	if (of_property_read_u32_array(np, "reg", val32, 4) != 0)
> +		return -EINVAL;
> +	p->port.iobase = val32[1];
> +	p->port.membase = ioremap(p->port.iobase, 0x10000);
> +	p->port.ops = &ma35d1serial_ops;
> +	p->port.line = 0;
> +	p->port.uartclk = 24000000;
> +
> +	port = &ma35d1serial_ports[co->index].port;
> +	return uart_set_options(port, co, baud, parity, bits, flow);
> +}
> +
> +static struct console ma35d1serial_console = {
> +	.name    = "ttyS",
> +	.write   = ma35d1serial_console_write,
> +	.device  = uart_console_device,
> +	.setup   = ma35d1serial_console_setup,
> +	.flags   = CON_PRINTBUFFER | CON_ENABLED,
> +	.index   = -1,
> +	.data    = &ma35d1serial_reg,
> +};
> +
> +static void
> +ma35d1serial_console_init_port(void)
> +{
> +	int i = 0;
> +	struct device_node *np;
> +
> +	for_each_matching_node(np, ma35d1_serial_of_match) {
> +		if (ma35d1serial_uart_nodes[i] == NULL) {
> +			ma35d1serial_uart_nodes[i] = np;
> +			i++;
> +		}
> +	}
> +}
> +
> +static int __init ma35d1serial_console_init(void)
> +{
> +	ma35d1serial_console_init_port();
> +	register_console(&ma35d1serial_console);
> +	return 0;
> +}
> +console_initcall(ma35d1serial_console_init);
> +
> +#define MA35D1SERIAL_CONSOLE    (&ma35d1serial_console)
> +#else
> +#define MA35D1SERIAL_CONSOLE    NULL
> +#endif
> +
> +static struct uart_driver ma35d1serial_reg = {
> +	.owner        = THIS_MODULE,
> +	.driver_name  = "serial",
> +	.dev_name     = "ttyS",
> +	.major        = TTY_MAJOR,
> +	.minor        = 64,
> +	.cons         = MA35D1SERIAL_CONSOLE,
> +	.nr           = UART_NR,
> +};
> +
> +/**
> + *  Suspend one serial port.
> + */
> +void ma35d1serial_suspend_port(int line)
> +{
> +	uart_suspend_port(&ma35d1serial_reg, &ma35d1serial_ports[line].port);
> +}
> +EXPORT_SYMBOL(ma35d1serial_suspend_port);
> +
> +/**
> + *  Resume one serial port.
> + */
> +void ma35d1serial_resume_port(int line)
> +{
> +	struct uart_ma35d1_port *up = &ma35d1serial_ports[line];
> +
> +	uart_resume_port(&ma35d1serial_reg, &up->port);
> +}
> +EXPORT_SYMBOL(ma35d1serial_resume_port);
> +
> +/*
> + * Register a set of serial devices attached to a platform device.
> + * The list is terminated with a zero flags entry, which means we expect
> + * all entries to have at least UPF_BOOT_AUTOCONF set.
> + */
> +static int ma35d1serial_probe(struct platform_device *pdev)
> +{
> +	struct resource *res_mem;
> +	struct uart_ma35d1_port *up;
> +	int ret;
> +	struct clk *clk;
> +	int err;
> +
> +	if (pdev->dev.of_node) {
> +		ret = of_alias_get_id(pdev->dev.of_node, "serial");
> +		if (ret < 0) {
> +			dev_err(&pdev->dev,
> +				"failed to get alias/pdev id, errno %d\n",
> +				ret);

Just put error prints to one line if you don't break 100 chars limit.

> +		return ret;

Misaligned line.

> +		}
> +	}
> +	up = &ma35d1serial_ports[ret];
> +	up->port.line = ret;
> +	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res_mem)
> +		return -ENODEV;
> +
> +	up->port.iobase = res_mem->start;
> +	up->port.membase = ioremap(up->port.iobase, 0x10000);

Define for the literal.

> +	up->port.ops = &ma35d1serial_ops;
> +
> +	spin_lock_init(&up->port.lock);
> +
> +	clk = of_clk_get(pdev->dev.of_node, 0);
> +	if (IS_ERR(clk)) {
> +		err = PTR_ERR(clk);
> +		dev_err(&pdev->dev, "failed to get core clk: %d\n", err);
> +		return -ENOENT;
> +	}
> +	err = clk_prepare_enable(clk);
> +	if (err)
> +		return -ENOENT;
> +
> +	if (up->port.line != 0)
> +		up->port.uartclk = clk_get_rate(clk);
> +	up->port.irq = platform_get_irq(pdev, 0);
> +	up->port.dev = &pdev->dev;
> +	up->port.flags = UPF_BOOT_AUTOCONF;
> +	up->port.rs485_config = ma35d1serial_config_rs485;

Please provide also .rs485_supported for the serial core to handle the 
supported features for you.

> +	ret = uart_add_one_port(&ma35d1serial_reg, &up->port);
> +	platform_set_drvdata(pdev, up);
> +	return 0;
> +}
> +
> +/*
> + * Remove serial ports registered against a platform device.
> + */
> +static int ma35d1serial_remove(struct platform_device *dev)
> +{
> +	int i;
> +	struct uart_port *port = platform_get_drvdata(dev);
> +
> +	free_irq(port->irq, port);
> +	for (i = 0; i < UART_NR; i++) {
> +		struct uart_ma35d1_port *up = &ma35d1serial_ports[i];
> +
> +		if (up->port.dev == &dev->dev)
> +			uart_remove_one_port(&ma35d1serial_reg, &up->port);
> +	}
> +	return 0;
> +}
> +
> +static int ma35d1serial_suspend(struct platform_device *dev,
> +				pm_message_t state)
> +{
> +	int i;
> +	struct uart_ma35d1_port *up;
> +
> +	if (dev->dev.of_node)
> +		i = of_alias_get_id(dev->dev.of_node, "serial");
> +	if (i < 0) {
> +		dev_err(&dev->dev, "failed to get alias/pdev id, errno %d\n",
> +			i);

Just put this to the same line with the rest.

> +		return i;
> +	}
> +	up = &ma35d1serial_ports[i];
> +	if (i == 0) {
> +		up->console_baud_rate = serial_in(up, UART_REG_BAUD);
> +		up->console_line = serial_in(up, UART_REG_LCR);
> +		up->console_int = serial_in(up, UART_REG_IER);
> +	}
> +	return 0;
> +}
> +
> +static int ma35d1serial_resume(struct platform_device *dev)
> +{
> +	int i;
> +	struct uart_ma35d1_port *up;
> +
> +	if (dev->dev.of_node)
> +		i = of_alias_get_id(dev->dev.of_node, "serial");
> +	if (i < 0) {
> +		dev_err(&dev->dev, "failed to get alias/pdev id, errno %d\n",
> +			i);

Same line.

> +		return i;
> +	}
> +	up = &ma35d1serial_ports[i];
> +	if (i == 0) {
> +		serial_out(up, UART_REG_BAUD, up->console_baud_rate);
> +		serial_out(up, UART_REG_LCR, up->console_line);
> +		serial_out(up, UART_REG_IER, up->console_int);
> +	}
> +	return 0;
> +}
> +
> +static struct platform_driver ma35d1serial_driver = {
> +	.probe      = ma35d1serial_probe,
> +	.remove     = ma35d1serial_remove,
> +	.suspend    = ma35d1serial_suspend,
> +	.resume     = ma35d1serial_resume,
> +	.driver     = {
> +		.name   = "ma35d1-uart",
> +		.owner  = THIS_MODULE,
> +		.of_match_table = of_match_ptr(ma35d1_serial_of_match),
> +	},
> +};
> +
> +static int __init ma35d1serial_init(void)
> +{
> +	int ret;
> +
> +	ret = uart_register_driver(&ma35d1serial_reg);
> +	if (ret)
> +		return ret;
> +	ret = platform_driver_register(&ma35d1serial_driver);
> +	if (ret)
> +		uart_unregister_driver(&ma35d1serial_reg);
> +	return ret;
> +}
> +
> +static void __exit ma35d1serial_exit(void)
> +{
> +	platform_driver_unregister(&ma35d1serial_driver);
> +	uart_unregister_driver(&ma35d1serial_reg);
> +}
> +
> +module_init(ma35d1serial_init);
> +module_exit(ma35d1serial_exit);
> +
> +MODULE_LICENSE("GPL v2");

"GPL" is enough for MODULE_LICENSE, the SPDX at the start of file covers 
more specific license variations.

> +MODULE_DESCRIPTION("MA35D1 serial driver");
> +MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
> +
> diff --git a/drivers/tty/serial/ma35d1_serial.h b/drivers/tty/serial/ma35d1_serial.h
> new file mode 100644
> index 000000000000..5fd845c31b29
> --- /dev/null
> +++ b/drivers/tty/serial/ma35d1_serial.h
> @@ -0,0 +1,93 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + *  MA35D1 serial driver header file
> + *  Copyright (C) 2023 Nuvoton Technology Corp.
> + */
> +#ifndef __MA35D1_SERIAL_H__
> +#define __MA35D1_SERIAL_H__
> +
> +/* UART Receive/Transmit Buffer Register */
> +#define UART_REG_RBR	0x00
> +#define UART_REG_THR	0x00
> +
> +/* UART Interrupt Enable Register */
> +#define UART_REG_IER	0x04
> +#define RDA_IEN		0x00000001 /* RBR Available Interrupt Enable */
> +#define THRE_IEN	0x00000002 /* THR Empty Interrupt Enable */
> +#define RLS_IEN		0x00000004 /* RX Line Status Interrupt Enable */
> +#define RTO_IEN		0x00000010 /* RX Time-out Interrupt Enable */
> +#define BUFERR_IEN	0x00000020 /* Buffer Error Interrupt Enable */
> +#define TIME_OUT_EN	0x00000800 /* RX Buffer Time-out Counter Enable */
> +
> +/* UART FIFO Control Register */
> +#define UART_REG_FCR	0x08
> +#define RFR		0x00000002 /* RX Field Software Reset */
> +#define TFR		0x00000004 /* TX Field Software Reset */
> +
> +/* UART Line Control Register */
> +#define UART_REG_LCR	0x0C
> +#define	NSB		0x00000004 /* Number of “STOP Bit” */
> +#define PBE		0x00000008 /* Parity Bit Enable */
> +#define EPE		0x00000010 /* Even Parity Enable */
> +#define SPE		0x00000020 /* Stick Parity Enable */
> +#define BCB		0x00000040 /* Break Control */
> +
> +/* UART Modem Control Register */
> +#define UART_REG_MCR	0x10
> +#define RTS		0x00000020 /* nRTS Signal Control */
> +#define RTSACTLV	0x00000200 /* nRTS Pin Active Level */
> +#define RTSSTS		0x00002000 /* nRTS Pin Status (Read Only) */
> +
> +/* UART Modem Status Register */
> +#define UART_REG_MSR	0x14
> +#define CTSDETF		0x00000001 /* Detect nCTS State Change Flag */
> +#define CTSSTS		0x00000010 /* nCTS Pin Status (Read Only) */
> +#define CTSACTLV	0x00000100 /* nCTS Pin Active Level */
> +
> +/* UART FIFO Status Register */
> +#define UART_REG_FSR	0x18
> +#define RX_OVER_IF	0x00000001 /* RX Overflow Error Interrupt Flag */
> +#define PEF		0x00000010 /* Parity Error Flag*/
> +#define FEF		0x00000020 /* Framing Error Flag */
> +#define BIF		0x00000040 /* Break Interrupt Flag */
> +#define RX_EMPTY	0x00004000 /* Receiver FIFO Empty (Read Only) */
> +#define RX_FULL		0x00008000 /* Receiver FIFO Full (Read Only) */
> +#define TX_EMPTY	0x00400000 /* Transmitter FIFO Empty (Read Only) */
> +#define TX_FULL		0x00800000 /* Transmitter FIFO Full (Read Only) */
> +#define TX_OVER_IF	0x01000000 /* TX Overflow Error Interrupt Flag */
> +#define TE_FLAG		0x10000000 /* Transmitter Empty Flag (Read Only) */
> +
> +/* UART Interrupt Status Register */
> +#define UART_REG_ISR	0x1C
> +#define RDA_IF		0x00000001 /* RBR Available Interrupt Flag */
> +#define THRE_IF		0x00000002 /* THR Empty Interrupt Flag */
> +#define RLSIF		0x00000004 /* Receive Line Interrupt Flag */
> +#define MODEMIF		0x00000008 /* MODEM Interrupt Flag */
> +#define RXTO_IF		0x00000010 /* RX Time-out Interrupt Flag */
> +#define BUFEIF		0x00000020 /* Buffer Error Interrupt Flag */
> +#define WK_IF		0x00000040 /* UART Wake-up Interrupt Flag */
> +#define RDAINT		0x00000100 /* RBR Available Interrupt Indicator */
> +#define THRE_INT	0x00000200 /* THR Empty Interrupt Indicator */
> +
> +/* UART Time-out Register */
> +#define UART_REG_TOR	0x20
> +
> +/* UART Baud Rate Divider Register */
> +#define UART_REG_BAUD	0x24
> +
> +/* UART Alternate Control/Status Register */
> +#define UART_REG_ALT_CSR 0x2C
> +
> +/* UART Function Select Register */
> +#define UART_FUN_SEL	0x30
> +#define FUN_SEL_UART	0x00000000
> +#define FUN_SEL_RS485	0x00000003
> +#define FUN_SEL_MASK	0x00000007

GENMASK(), then use FIELD_PREP() for the values. 

> +
> +/* UART Wake-up Control Register */
> +#define UART_REG_WKCTL	0x40
> +
> +/* UART Wake-up Status Register */
> +#define UART_REG_WKSTS	0x44
> +
> +#endif /* __MA35D1_SERIAL_H__ */
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 281fa286555c..c6d53db17042 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -279,4 +279,7 @@
>  /* Sunplus UART */
>  #define PORT_SUNPLUS	123
>  
> +/* Nuvoton MA35D1 UART */
> +#define PORT_MA35D1	124
> +
>  #endif /* _UAPILINUX_SERIAL_CORE_H */
> 

-- 
 i.

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

* Re: [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support
  2023-03-15  7:29 ` [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support Jacky Huang
  2023-03-16  7:51   ` Krzysztof Kozlowski
@ 2023-03-16 15:05   ` Ilpo Järvinen
  2023-03-19 13:10     ` Jacky Huang
  1 sibling, 1 reply; 89+ messages in thread
From: Ilpo Järvinen @ 2023-03-16 15:05 UTC (permalink / raw)
  To: Jacky Huang
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, devicetree, linux-clk, linux-kernel,
	linux-serial, schung, Jacky Huang

On Wed, 15 Mar 2023, Jacky Huang wrote:

> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> This driver supports individual IP reset for ma35d1. The reset
> control registers is a subset of system control registers.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  drivers/reset/Kconfig        |   6 ++
>  drivers/reset/Makefile       |   1 +
>  drivers/reset/reset-ma35d1.c | 152 +++++++++++++++++++++++++++++++++++
>  3 files changed, 159 insertions(+)
>  create mode 100644 drivers/reset/reset-ma35d1.c
> 
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index 2a52c990d4fe..47671060d259 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -143,6 +143,12 @@ config RESET_NPCM
>  	  This enables the reset controller driver for Nuvoton NPCM
>  	  BMC SoCs.
>  
> +config RESET_NUVOTON_MA35D1
> +	bool "Nuvton MA35D1 Reset Driver"
> +	default ARCH_NUVOTON
> +	help
> +	  This enables the reset controller driver for Nuvoton MA35D1 SoC.
> +
>  config RESET_OXNAS
>  	bool
>  
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index 3e7e5fd633a8..fd52dcf66a99 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
>  obj-$(CONFIG_RESET_MESON) += reset-meson.o
>  obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
>  obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
> +obj-$(CONFIG_RESET_NUVOTON_MA35D1) += reset-ma35d1.o
>  obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
>  obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
>  obj-$(CONFIG_RESET_POLARFIRE_SOC) += reset-mpfs.o
> diff --git a/drivers/reset/reset-ma35d1.c b/drivers/reset/reset-ma35d1.c
> new file mode 100644
> index 000000000000..bdd39483ca4e
> --- /dev/null
> +++ b/drivers/reset/reset-ma35d1.c
> @@ -0,0 +1,152 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset-controller.h>
> +#include <linux/mfd/ma35d1-sys.h>
> +#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
> +#include <linux/regmap.h>
> +#include <linux/reboot.h>
> +
> +#define RST_PRE_REG	32
> +
> +struct ma35d1_reset_data {
> +	struct reset_controller_dev rcdev;
> +	struct regmap *regmap;
> +};
> +
> +struct ma35d1_reboot_data {
> +	struct notifier_block restart_handler;
> +	struct regmap *regmap;
> +};
> +
> +static int ma35d1_restart_handler(struct notifier_block *this,
> +				  unsigned long mode, void *cmd)
> +{
> +	struct ma35d1_reboot_data *data =
> +			container_of(this, struct ma35d1_reboot_data,
> +				     restart_handler);
> +	regmap_write(data->regmap, REG_SYS_IPRST0, 1 << MA35D1_RESET_CHIP);
> +	return -EAGAIN;

This results -EAGAIN always???

> +}
> +
> +static int ma35d1_reset_update(struct reset_controller_dev *rcdev,
> +			      unsigned long id, bool assert)
> +{
> +	int reg;
> +	int offset = (id / RST_PRE_REG) * 4;
> +	struct ma35d1_reset_data *data =
> +			container_of(rcdev, struct ma35d1_reset_data, rcdev);
> +
> +	regmap_read(data->regmap, REG_SYS_IPRST0 + offset, &reg);
> +	if (assert)
> +		reg |= 1 << (id % RST_PRE_REG);
> +	else
> +		reg &= ~(1 << (id % RST_PRE_REG));
> +
> +	regmap_write(data->regmap, REG_SYS_IPRST0 + offset, reg);
> +	return 0;

This returns always 0. What about regmap_read/write() errors, should the 
be returned?

> +}
> +
> +static int ma35d1_reset_assert(struct reset_controller_dev *rcdev,
> +			       unsigned long id)
> +{
> +	return ma35d1_reset_update(rcdev, id, true);
> +}
> +
> +static int ma35d1_reset_deassert(struct reset_controller_dev *rcdev,
> +				 unsigned long id)
> +{
> +	return ma35d1_reset_update(rcdev, id, false);
> +}
> +
> +static int ma35d1_reset_status(struct reset_controller_dev *rcdev,
> +			      unsigned long id)
> +{
> +	int reg;
> +	int offset = id / RST_PRE_REG;
> +	struct ma35d1_reset_data *data =
> +			container_of(rcdev, struct ma35d1_reset_data, rcdev);
> +
> +	regmap_read(data->regmap, REG_SYS_IPRST0 + offset, &reg);

Error handling?

> +	return !!(reg & BIT(id % RST_PRE_REG));
> +}
> +
> +static const struct reset_control_ops ma35d1_reset_ops = {
> +	.assert = ma35d1_reset_assert,
> +	.deassert = ma35d1_reset_deassert,
> +	.status = ma35d1_reset_status,
> +};
> +
> +static const struct of_device_id ma35d1_reset_dt_ids[] = {
> +	{ .compatible = "nuvoton,ma35d1-reset" },
> +	{ },
> +};
> +
> +static int ma35d1_reset_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct ma35d1_reset_data *reset_data;
> +	struct ma35d1_reboot_data *reboot_data;
> +	int err;
> +
> +	if (!pdev->dev.of_node) {
> +		dev_err(&pdev->dev, "Device tree node not found\n");
> +		return -EINVAL;
> +	}
> +
> +	reset_data = devm_kzalloc(dev, sizeof(*reset_data), GFP_KERNEL);
> +	if (!reset_data)
> +		return -ENOMEM;
> +
> +	reboot_data = devm_kzalloc(dev, sizeof(*reboot_data), GFP_KERNEL);
> +	if (!reboot_data) {
> +		devm_kfree(dev, reset_data);

Unnecessary.

> +		return -ENOMEM;
> +	}
> +
> +	reset_data->regmap  = syscon_regmap_lookup_by_phandle(
> +			      pdev->dev.of_node, "regmap");
> +	if (IS_ERR(reset_data->regmap)) {
> +		dev_err(&pdev->dev, "Failed to get SYS register base\n");
> +		err = PTR_ERR(reset_data->regmap);
> +		goto err_out;
> +	}
> +	reset_data->rcdev.owner = THIS_MODULE;
> +	reset_data->rcdev.nr_resets = MA35D1_RESET_COUNT;
> +	reset_data->rcdev.ops = &ma35d1_reset_ops;
> +	reset_data->rcdev.of_node = dev->of_node;
> +
> +	reboot_data->regmap = reset_data->regmap;
> +	reboot_data->restart_handler.notifier_call = ma35d1_restart_handler;
> +	reboot_data->restart_handler.priority = 192;
> +
> +	err = register_restart_handler(&reboot_data->restart_handler);
> +	if (err)
> +		dev_warn(&pdev->dev, "failed to register restart handler\n");
> +
> +	return devm_reset_controller_register(dev, &reset_data->rcdev);
> +
> +err_out:
> +	devm_kfree(dev, reset_data);
> +	devm_kfree(dev, reboot_data);

These are unnecessary since the probe is failing.

> +	return err;
> +}
> +
> +static struct platform_driver ma35d1_reset_driver = {
> +	.probe = ma35d1_reset_probe,
> +	.driver = {
> +		.name = "ma35d1-reset",
> +		.of_match_table	= ma35d1_reset_dt_ids,
> +	},
> +};
> +
> +builtin_platform_driver(ma35d1_reset_driver);
> 

-- 
 i.


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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-15  7:28 ` [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
                     ` (2 preceding siblings ...)
  2023-03-16  7:51   ` Krzysztof Kozlowski
@ 2023-03-16 15:56   ` Ilpo Järvinen
  2023-03-19  5:16     ` Jacky Huang
  3 siblings, 1 reply; 89+ messages in thread
From: Ilpo Järvinen @ 2023-03-16 15:56 UTC (permalink / raw)
  To: Jacky Huang
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, devicetree, linux-clk, linux-kernel,
	linux-serial, schung, Jacky Huang

On Wed, 15 Mar 2023, Jacky Huang wrote:

> From: Jacky Huang <ychuang3@nuvoton.com>
> 
> The clock controller generates clocks for the whole chip, including
> system clocks and all peripheral clocks. This driver support ma35d1
> clock gating, divider, and individual PLL configuration.
> 
> There are 6 PLLs in ma35d1 SoC:
>   - CA-PLL for the two Cortex-A35 CPU clock
>   - SYS-PLL for system bus, which comes from the companion MCU
>     and cannot be programmed by clock controller.
>   - DDR-PLL for DDR
>   - EPLL for GMAC and GFX, Display, and VDEC IPs.
>   - VPLL for video output pixel clock
>   - APLL for SDHC, I2S audio, and other IPs.
> CA-PLL has only one operation mode.
> DDR-PLL, EPLL, VPLL, and APLL are advanced PLLs which have 3
> operation modes: integer mode, fraction mode, and spread specturm mode.
> 
> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> ---
>  drivers/clk/Makefile                     |   1 +
>  drivers/clk/nuvoton/Makefile             |   4 +
>  drivers/clk/nuvoton/clk-ma35d1-divider.c | 144 ++++
>  drivers/clk/nuvoton/clk-ma35d1-pll.c     | 534 +++++++++++++
>  drivers/clk/nuvoton/clk-ma35d1.c         | 970 +++++++++++++++++++++++
>  drivers/clk/nuvoton/clk-ma35d1.h         | 198 +++++
>  6 files changed, 1851 insertions(+)
>  create mode 100644 drivers/clk/nuvoton/Makefile
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1-divider.c
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1-pll.c
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1.c
>  create mode 100644 drivers/clk/nuvoton/clk-ma35d1.h
> 
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index e3ca0d058a25..2e7916d269e1 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -103,6 +103,7 @@ endif
>  obj-y					+= mstar/
>  obj-y					+= mvebu/
>  obj-$(CONFIG_ARCH_MXS)			+= mxs/
> +obj-$(CONFIG_ARCH_NUVOTON)		+= nuvoton/
>  obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
>  obj-$(CONFIG_COMMON_CLK_PISTACHIO)	+= pistachio/
>  obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
> diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile
> new file mode 100644
> index 000000000000..d2c092541b8d
> --- /dev/null
> +++ b/drivers/clk/nuvoton/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1.o
> +obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1-divider.o
> +obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1-pll.o
> diff --git a/drivers/clk/nuvoton/clk-ma35d1-divider.c b/drivers/clk/nuvoton/clk-ma35d1-divider.c
> new file mode 100644
> index 000000000000..5f4791531e47
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
> @@ -0,0 +1,144 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/spinlock.h>
> +
> +#include "clk-ma35d1.h"
> +
> +#define div_mask(width)		((1 << (width)) - 1)
> +
> +struct ma35d1_adc_clk_divider {
> +	struct clk_hw hw;
> +	void __iomem *reg;
> +	u8 shift;
> +	u8 width;
> +	u32 mask;
> +	const struct clk_div_table *table;
> +	spinlock_t *lock;

Add comment to indicate what it protects.

> +};
> +
> +#define to_ma35d1_adc_clk_divider(_hw)	\
> +	container_of(_hw, struct ma35d1_adc_clk_divider, hw)

static inline

> +static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	unsigned int val;
> +	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
> +
> +	val = readl_relaxed(dclk->reg) >> dclk->shift;
> +	val &= div_mask(dclk->width);
> +	val += 1;
> +	return divider_recalc_rate(hw, parent_rate, val, dclk->table,
> +				   CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
> +}
> +
> +static long ma35d1_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *prate)
> +{
> +	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
> +
> +	return divider_round_rate(hw, rate, prate, dclk->table,
> +				  dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
> +}
> +
> +static int ma35d1_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	int value;
> +	unsigned long flags = 0;
> +	u32 data;
> +	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
> +
> +	value = divider_get_val(rate, parent_rate, dclk->table,
> +				dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
> +
> +	if (dclk->lock)
> +		spin_lock_irqsave(dclk->lock, flags);
> +
> +	data = readl_relaxed(dclk->reg);
> +	data &= ~(div_mask(dclk->width) << dclk->shift);
> +	data |= (value - 1) << dclk->shift;
> +	data |= dclk->mask;
> +
> +	writel_relaxed(data, dclk->reg);
> +
> +	if (dclk->lock)
> +		spin_unlock_irqrestore(dclk->lock, flags);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops ma35d1_adc_clkdiv_ops = {
> +	.recalc_rate = ma35d1_clkdiv_recalc_rate,
> +	.round_rate = ma35d1_clkdiv_round_rate,
> +	.set_rate = ma35d1_clkdiv_set_rate,
> +};
> +
> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
> +				     const char *parent_name,
> +				     unsigned long flags, void __iomem *reg,
> +				     u8 shift, u8 width, u32 mask_bit)
> +{
> +	struct ma35d1_adc_clk_divider *div;
> +	struct clk_init_data init;
> +	struct clk_div_table *table;
> +	u32 max_div, min_div;
> +	struct clk_hw *hw;
> +	int ret;
> +	int i;
> +
> +	/* allocate the divider */
> +	div = kzalloc(sizeof(*div), GFP_KERNEL);
> +	if (!div)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* Init the divider table */
> +	max_div = div_mask(width) + 1;
> +	min_div = 1;
> +
> +	table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
> +	if (!table) {
> +		kfree(div);
> +		return ERR_PTR(-ENOMEM);

Use rollback to do error handling:

		ret = ERR_PTR(-ENOMEM);
		goto free_div;

> +	}
> +
> +	for (i = 0; i < max_div; i++) {
> +		table[i].val = (min_div + i);
> +		table[i].div = 2 * table[i].val;
> +	}
> +	table[max_div].val = 0;
> +	table[max_div].div = 0;
> +
> +	init.name = name;
> +	init.ops = &ma35d1_adc_clkdiv_ops;
> +	init.flags |= flags;
> +	init.parent_names = parent_name ? &parent_name : NULL;
> +	init.num_parents = parent_name ? 1 : 0;
> +
> +	/* struct ma35d1_adc_clk_divider assignments */
> +	div->reg = reg;
> +	div->shift = shift;
> +	div->width = width;
> +	div->mask = mask_bit ? BIT(mask_bit) : 0;
> +	div->lock = &ma35d1_lock;
> +	div->hw.init = &init;
> +	div->table = table;
> +
> +	/* Register the clock */
> +	hw = &div->hw;
> +	ret = clk_hw_register(NULL, hw);
> +	if (ret) {
> +		kfree(table);
> +		kfree(div);
> +		return ERR_PTR(ret);

ret = ERR_PTR(ret);
goto free_table;

> +	}
> +	return hw;

free_table:
	kfree(table);
free_div:
	kfree(div);
	return ret;

> +}
> diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> new file mode 100644
> index 000000000000..79e724b148fa
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> @@ -0,0 +1,534 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/bitfield.h>
> +
> +#include "clk-ma35d1.h"
> +
> +#define to_ma35d1_clk_pll(clk) \
> +	(container_of(clk, struct ma35d1_clk_pll, clk))

static inline

> +
> +#define PLL0CTL0_FBDIV_MSK	GENMASK(7, 0)
> +#define PLL0CTL0_INDIV_MSK	GENMASK(11, 8)
> +#define PLL0CTL0_OUTDIV_MSK	GENMASK(13, 12)
> +#define PLL0CTL0_PD_MSK		BIT(16)
> +#define PLL0CTL0_BP_MSK		BIT(17)
> +#define PLLXCTL0_FBDIV_MSK	GENMASK(10, 0)
> +#define PLLXCTL0_INDIV_MSK	GENMASK(17, 12)
> +#define PLLXCTL0_MODE_MSK	GENMASK(19, 18)
> +#define PLLXCTL0_SSRATE_MSK	GENMASK(30, 20)
> +#define PLLXCTL1_PD_MSK		BIT(0)
> +#define PLLXCTL1_BP_MSK		BIT(1)
> +#define PLLXCTL1_OUTDIV_MSK	GENMASK(6, 4)
> +#define PLLXCTL1_FRAC_MSK	GENMASK(31, 8)
> +#define PLLXCTL2_SLOPE_MSK	GENMASK(23, 0)
> +
> +struct ma35d1_clk_pll {
> +	struct clk_hw hw;
> +	u8 type;
> +	u8 mode;
> +	unsigned long rate;
> +	void __iomem *ctl0_base;
> +	void __iomem *ctl1_base;
> +	void __iomem *ctl2_base;
> +	struct regmap *regmap;
> +};
> +
> +struct vsipll_freq_conf_reg_tbl {
> +	unsigned long freq;
> +	u8 mode;
> +	u32 ctl0_reg;
> +	u32 ctl1_reg;
> +	u32 ctl2_reg;
> +};
> +
> +static const struct vsipll_freq_conf_reg_tbl ma35d1pll_freq[] = {
> +	{ 1000000000, VSIPLL_INTEGER_MODE, 0x307d, 0x10, 0 },
> +	{ 884736000, VSIPLL_FRACTIONAL_MODE, 0x41024, 0xdd2f1b11, 0 },
> +	{ 533000000, VSIPLL_SS_MODE, 0x12b8102c, 0x6aaaab20, 0x12317 },
> +	{ }
> +};
> +
> +static void CLK_UnLockReg(struct ma35d1_clk_pll *pll)

Use lowercase only.

> +{
> +	int ret;
> +
> +	/* Unlock PLL registers */
> +	do {
> +		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x59);
> +		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x16);
> +		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x88);
> +		regmap_read(pll->regmap, REG_SYS_RLKTZNS, &ret);
> +	} while (ret == 0);
> +}
> +
> +static void CLK_LockReg(struct ma35d1_clk_pll *pll)

Ditto.

> +{
> +	/* Lock PLL registers */
> +	regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x0);
> +}
> +
> +/* SMIC PLL for CAPLL */
> +unsigned long CLK_GetPLLFreq_SMICPLL(struct ma35d1_clk_pll *pll,
> +				     unsigned long PllSrcClk)

Lowercase only for function name and variable names. Please do the rest,
I won't mention more of them.

> +{
> +	u32 u32M, u32N, u32P, u32OutDiv;
> +	u32 val;
> +	unsigned long u64PllClk;
> +	u32 clk_div_table[] = { 1, 2, 4, 8};

Inconsistent whitespaces.

> +
> +	val = __raw_readl(pll->ctl0_base);
> +
> +	u32N = FIELD_GET(PLL0CTL0_FBDIV_MSK, val);
> +	u32M = FIELD_GET(PLL0CTL0_INDIV_MSK, val);
> +	u32P = FIELD_GET(PLL0CTL0_OUTDIV_MSK, val);
> +	u32OutDiv = clk_div_table[u32P];
> +
> +	if (val & PLL0CTL0_BP_MSK) {
> +		u64PllClk = PllSrcClk;
> +	} else {
> +		u64PllClk = PllSrcClk * u32N;
> +		do_div(u64PllClk, u32M * u32OutDiv);

Does this block depend on unsigned long being 64-bit? Or should you 
enforce it by using u64 that is always same sized unlike unsigned long?

> +	}
> +	return u64PllClk;
> +}
> +
> +/* VSI-PLL: INTEGER_MODE */
> +unsigned long CLK_CalPLLFreq_Mode0(unsigned long PllSrcClk,
> +				   unsigned long u64PllFreq, u32 *u32Reg)
> +{
> +	u32 u32TmpM, u32TmpN, u32TmpP;
> +	u32 u32RngMinN, u32RngMinM, u32RngMinP;
> +	u32 u32RngMaxN, u32RngMaxM, u32RngMaxP;
> +	u32 u32Tmp, u32Min, u32MinN, u32MinM, u32MinP;

Remove types from names.

> +	unsigned long u64PllClk;
> +	unsigned long u64Con1, u64Con2, u64Con3;

Okay as unsigned long or do you want always 64-bit which is u64 ?

Define these inside the loops below and remove the types from the name.

> +
> +	u64PllClk = 0;
> +	u32Min = (u32) -1;
> +
> +	if (!((u64PllFreq >= VSIPLL_FCLKO_MIN_FREQ) &&
> +	    (u64PllFreq <= VSIPLL_FCLKO_MAX_FREQ))) {
> +		u32Reg[0] = ma35d1pll_freq[0].ctl0_reg;
> +		u32Reg[1] = ma35d1pll_freq[0].ctl1_reg;
> +		u64PllClk = ma35d1pll_freq[0].freq;
> +		return u64PllClk;
> +	}
> +
> +	u32RngMinM = 1UL;
> +	u32RngMaxM = 63UL;
> +	u32RngMinM = ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) > 1) ?
> +		     (PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) : 1;

max(PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ, 1UL);

Remember to add include for it.

> +	u32RngMaxM = ((PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) < u32RngMaxM) ?
> +		     (PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) : u32RngMaxM;

min();

> +
> +	for (u32TmpM = u32RngMinM; u32TmpM < (u32RngMaxM + 1); u32TmpM++) {

<= and remove + 1

Why can't you call this loop cariable just m ?

> +		u64Con1 = PllSrcClk / u32TmpM;
> +		u32RngMinN = 16UL;
> +		u32RngMaxN = 2047UL;

Why aren't these two values in defines?

> +		u32RngMinN = ((VSIPLL_FCLK_MIN_FREQ / u64Con1) > u32RngMinN) ?
> +			     (VSIPLL_FCLK_MIN_FREQ / u64Con1) : u32RngMinN;

max();

> +		u32RngMaxN = ((VSIPLL_FCLK_MAX_FREQ / u64Con1) < u32RngMaxN) ?
> +			     (VSIPLL_FCLK_MAX_FREQ / u64Con1) : u32RngMaxN;

min();

> +
> +		for (u32TmpN = u32RngMinN; u32TmpN < (u32RngMaxN + 1);

<= and remove + 1

Name variable as n ?

> +		     u32TmpN++) {

One line.

> +			u64Con2 = u64Con1 * u32TmpN;
> +			u32RngMinP = 1UL;
> +			u32RngMaxP = 7UL;

Limits to defines?

> +			u32RngMinP = ((u64Con2 / VSIPLL_FCLKO_MAX_FREQ) > 1) ?
> +				      (u64Con2 / VSIPLL_FCLKO_MAX_FREQ) : 1;
> +			u32RngMaxP = ((u64Con2 / VSIPLL_FCLKO_MIN_FREQ) <
> +				      u32RngMaxP) ?
> +				      (u64Con2 / VSIPLL_FCLKO_MIN_FREQ) :
> +				      u32RngMaxP;

min & max.

> +			for (u32TmpP = u32RngMinP; u32TmpP < (u32RngMaxP + 1);

<= and remove +1?

Name variable as p?

> +			     u32TmpP++) {

One line.

> +				u64Con3 = u64Con2 / u32TmpP;
> +				if (u64Con3 > u64PllFreq)
> +					u32Tmp = u64Con3 - u64PllFreq;
> +				else
> +					u32Tmp = u64PllFreq - u64Con3;

abs()?

> +
> +				if (u32Tmp < u32Min) {
> +					u32Min = u32Tmp;
> +					u32MinM = u32TmpM;
> +					u32MinN = u32TmpN;
> +					u32MinP = u32TmpP;
> +
> +					if (u32Min == 0UL) {

goto out?

> +						u32Reg[0] = (u32MinM << 12) |
> +							    (u32MinN);
> +						u32Reg[1] = (u32MinP << 4);
> +						return ((PllSrcClk * u32MinN) /
> +							(u32MinP * u32MinM));
> +					}
> +				}
> +			}
> +		}
> +	}
> +

out: ?

> +	u32Reg[0] = (u32MinM << 12) | (u32MinN);

FIELD_PREP() | FIELD_PREP() ?

> +	u32Reg[1] = (u32MinP << 4);

FIELD_PREP() ?

> +	u64PllClk = (PllSrcClk * u32MinN) / (u32MinP * u32MinM);

Use the 64-bit divide from math64.h rather than leave it up to compiler.

> +	return u64PllClk;
> +}
> +
> +/* VSI-PLL: FRACTIONAL_MODE */
> +unsigned long CLK_CalPLLFreq_Mode1(unsigned long PllSrcClk,
> +				   unsigned long u64PllFreq, u32 *u32Reg)
> +{
> +	unsigned long u64X, u64N, u64M, u64P, u64tmp;
> +	unsigned long u64PllClk, u64FCLKO;
> +	u32 u32FRAC;
> +
> +	if (u64PllFreq > VSIPLL_FCLKO_MAX_FREQ) {
> +		u32Reg[0] = ma35d1pll_freq[1].ctl0_reg;
> +		u32Reg[1] = ma35d1pll_freq[1].ctl1_reg;
> +		u64PllClk = ma35d1pll_freq[1].freq;
> +		return u64PllClk;
> +	}
> +
> +	if (u64PllFreq > (VSIPLL_FCLKO_MIN_FREQ/(100-1))) {
> +		u64FCLKO = u64PllFreq * ((VSIPLL_FCLKO_MIN_FREQ / u64PllFreq) +
> +			   ((VSIPLL_FCLKO_MIN_FREQ % u64PllFreq) ? 1 : 0));

You need to rework this to do 64-bit divide and remainder with 
something that comes from math64.h.

> +	} else {
> +		pr_err("Failed to set rate %ld\n", u64PllFreq);
> +		return 0;
> +	}
> +
> +	u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
> +	       ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
> +		((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));

Ditto.

Is here some ...ROUND_UP() trick hidden too?

> +
> +	if ((PllSrcClk > (VSIPLL_FREFDIVM_MAX_FREQ * (64-1))) ||
> +	    (PllSrcClk < VSIPLL_FREFDIVM_MIN_FREQ1))
> +		return 0;
> +
> +	u64M = (PllSrcClk <= VSIPLL_FREFDIVM_MAX_FREQ) ? 1 :
> +	       ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) +
> +	       ((PllSrcClk % VSIPLL_FREFDIVM_MAX_FREQ) ? 1 : 0));

Ditto.

> +
> +	u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
> +	u64N = u64tmp / 1000;
> +	u64X = u64tmp % 1000;

math64.h x 3 (or x2 since you can get remainder for free I think).

> +	u32FRAC = ((u64X << 24) + 500) / 1000;
> +	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
> +
> +	u32Reg[0] = (u64M << 12) | (u64N);

FIELD_PREP() ?

> +	u32Reg[1] = (u64P << 4) | (u32FRAC << 8);

FIELD_PREP() ?

> +	return u64PllClk;
> +}
> +
> +/* VSI-PLL: SS_MODE */
> +unsigned long CLK_CalPLLFreq_Mode2(unsigned long PllSrcClk,
> +				   unsigned long u64PllFreq,
> +				   u32 u32SR, u32 u32Fmod, u32 *u32Reg)
> +{
> +	unsigned long u64X, u64N, u64M, u64P, u64tmp, u64tmpP, u64tmpM;
> +	unsigned long u64SSRATE, u64SLOPE, u64PllClk, u64FCLKO;
> +	u32 u32FRAC, i;
> +
> +	if (u64PllFreq >= VSIPLL_FCLKO_MAX_FREQ) {
> +		u32Reg[0] = ma35d1pll_freq[2].ctl0_reg;
> +		u32Reg[1] = ma35d1pll_freq[2].ctl1_reg;
> +		u32Reg[2] = ma35d1pll_freq[2].ctl2_reg;
> +		u64PllClk = ma35d1pll_freq[2].freq;
> +		return u64PllClk;
> +	}
> +
> +	if (u64PllFreq < VSIPLL_FCLKO_MIN_FREQ) {
> +		u64FCLKO = 0;
> +		for (i = 2; i < 8; i++) {
> +			u64tmp = (i * u64PllFreq);
> +			if (u64tmp > VSIPLL_FCLKO_MIN_FREQ)

VSIPLL_FCLKO_MAX_FREQ check is not required ?

> +				u64FCLKO = u64tmp;
> +		}
> +		if (u64FCLKO == 0) {
> +			pr_err("Failed to set rate %ld\n", u64PllFreq);
> +			return 0;
> +		}
> +
> +	} else
> +		u64FCLKO = u64PllFreq;
> +
> +	u64P = 0;
> +	for (i = 1; i < 8; i++) {
> +		u64tmpP = i * u64FCLKO;
> +		if ((u64tmpP <= VSIPLL_FCLK_MAX_FREQ) &&
> +		    (u64tmpP >= VSIPLL_FCLK_MIN_FREQ)) {
> +			u64P = i;
> +			break;
> +		}
> +	}
> +
> +	if (u64P == 0)
> +		return 0;
> +
> +	u64M = 0;
> +	for (i = 1; i < 64; i++) {
> +		u64tmpM = PllSrcClk / i;
> +		if ((u64tmpM <= VSIPLL_FREFDIVM_MAX_FREQ) &&
> +		    (u64tmpM >= VSIPLL_FREFDIVM_MIN_FREQ1)) {
> +			u64M = i;
> +			break;
> +		}
> +	}
> +
> +	if (u64M == 0)
> +		return 0;
> +
> +	u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
> +	u64N = u64tmp / 1000;
> +	u64X = u64tmp % 1000;
> +	u32FRAC = ((u64X << 24) + 500) / 1000;
> +
> +	u64SSRATE = ((PllSrcClk >> 1) / (u32Fmod * 2)) - 1;
> +	u64SLOPE = ((u64tmp * u32SR / u64SSRATE) << 24) / 100 / 1000;
> +
> +	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;

Is some *SEC_PER_*SEC define relevant for 1000 ?

Or some other units, e.g., HZ related?

> +
> +	u32Reg[0] = (u64SSRATE << VSIPLLCTL0_SSRATE_POS) | (u64M <<
> +		     VSIPLLCTL0_INDIV_POS) | (u64N);

FIELD_PREP()

> +	u32Reg[1] = (u64P << VSIPLLCTL1_OUTDIV_POS) | (u32FRAC << VSIPLLCTL1_FRAC_POS);

Instead of _POS named variables, add GENMASK one instead and use 
FIELD_PREP. You might need to use GENMASK_ULL() for the masks if you are 
dealing with true 64-bitness here instead of the quasi unsigned longs.

> +	u32Reg[2] = u64SLOPE;
> +	return u64PllClk;
> +}
> +
> +unsigned long CLK_SetPLLFreq(struct ma35d1_clk_pll *pll,
> +			     unsigned long PllSrcClk,
> +			     unsigned long u64PllFreq)
> +{
> +	u32 u32Reg[3] = { 0 }, val_ctl0, val_ctl1, val_ctl2;
> +	unsigned long u64PllClk;
> +
> +	val_ctl0 = __raw_readl(pll->ctl0_base);
> +	val_ctl1 = __raw_readl(pll->ctl1_base);
> +	val_ctl2 = __raw_readl(pll->ctl2_base);
> +
> +	switch (pll->mode) {
> +	case VSIPLL_INTEGER_MODE:
> +		u64PllClk = CLK_CalPLLFreq_Mode0(PllSrcClk, u64PllFreq,
> +						 u32Reg);

One line.

> +		val_ctl0 = u32Reg[0] |
> +			   (VSIPLL_INTEGER_MODE << VSIPLLCTL0_MODE_POS);

GENMASK() + FIELD_PREP()

> +		break;
> +	case VSIPLL_FRACTIONAL_MODE:
> +		u64PllClk = CLK_CalPLLFreq_Mode1(PllSrcClk, u64PllFreq,
> +						 u32Reg);
> +		val_ctl0 = u32Reg[0] |
> +			   (VSIPLL_FRACTIONAL_MODE << VSIPLLCTL0_MODE_POS);

Ditto.

> +		break;
> +	case VSIPLL_SS_MODE:
> +		u64PllClk = CLK_CalPLLFreq_Mode2(PllSrcClk, u64PllFreq,
> +						 VSIPLL_MODULATION_FREQ,
> +						 VSIPLL_SPREAD_RANGE, u32Reg);
> +		val_ctl0 = u32Reg[0] |
> +			   (VSIPLL_SS_MODE << VSIPLLCTL0_MODE_POS);

Ditto.

> +		break;
> +	}
> +
> +	val_ctl1 = VSIPLLCTL1_PD_MSK | u32Reg[1];
> +	val_ctl2 = u32Reg[2];
> +
> +	__raw_writel(val_ctl0, pll->ctl0_base);
> +	__raw_writel(val_ctl1, pll->ctl1_base);
> +	__raw_writel(val_ctl2, pll->ctl2_base);
> +	return u64PllClk;
> +}
> +
> +unsigned long CLK_GetPLLFreq_VSIPLL(struct ma35d1_clk_pll *pll,
> +				    unsigned long PllSrcClk)
> +{
> +	u32 u32M, u32N, u32P, u32X, u32SR, u32FMOD;
> +	u32 val_ctl0, val_ctl1, val_ctl2;
> +	unsigned long u64PllClk, u64X;
> +
> +	val_ctl0 = __raw_readl(pll->ctl0_base);
> +	val_ctl1 = __raw_readl(pll->ctl1_base);
> +	val_ctl2 = __raw_readl(pll->ctl2_base);
> +
> +	if (val_ctl1 & PLLXCTL1_BP_MSK) {
> +		u64PllClk = PllSrcClk;
> +		return u64PllClk;
> +	}
> +
> +	if (pll->mode == VSIPLL_INTEGER_MODE) {
> +		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
> +		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
> +		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
> +
> +		u64PllClk = PllSrcClk * u32N;
> +		do_div(u64PllClk, u32M * u32P);
> +
> +	} else if (pll->mode == VSIPLL_FRACTIONAL_MODE) {
> +		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
> +		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
> +		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
> +		u32X = FIELD_GET(PLLXCTL1_FRAC_MSK, val_ctl1);
> +		u64X = (u64) u32X;
> +		u64X = (((u64X * 1000) + 500) >> 24);
> +		u64PllClk = (PllSrcClk * ((u32N * 1000) + u64X)) /
> +			    1000 / u32P / u32M;

math64.h

Please fix the remaining ones w/o me noting them down.

> +
> +	} else {
> +		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
> +		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
> +		u32SR = FIELD_GET(PLLXCTL0_SSRATE_MSK, val_ctl0);
> +		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
> +		u32X = FIELD_GET(PLLXCTL1_FRAC_MSK, val_ctl1);
> +		u32FMOD = FIELD_GET(PLLXCTL2_SLOPE_MSK, val_ctl2);
> +		u64X = (u64) u32X;
> +		u64X = ((u64X * 1000) >> 24);
> +		u64PllClk = (PllSrcClk * ((u32N * 1000) + u64X)) /
> +			    1000 / u32P / u32M;
> +	}
> +	return u64PllClk;
> +}
> +
> +static int ma35d1_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +
> +	if ((parent_rate < VSIPLL_FREF_MIN_FREQ) ||
> +	    (parent_rate > VSIPLL_FREF_MAX_FREQ))
> +		return 0;
> +
> +	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
> +		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
> +		return -EACCES;
> +	}
> +	CLK_UnLockReg(pll);
> +	pll->rate = CLK_SetPLLFreq(pll, parent_rate, rate);
> +	CLK_LockReg(pll);
> +	return 0;
> +}
> +
> +static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	unsigned long pllfreq;
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +
> +	if ((parent_rate < VSIPLL_FREF_MIN_FREQ)
> +	    || (parent_rate > VSIPLL_FREF_MAX_FREQ))
> +		return 0;
> +
> +	switch (pll->type) {
> +	case MA35D1_CAPLL:
> +		pllfreq = CLK_GetPLLFreq_SMICPLL(pll, parent_rate);
> +		break;
> +	case MA35D1_DDRPLL:
> +	case MA35D1_APLL:
> +	case MA35D1_EPLL:
> +	case MA35D1_VPLL:
> +		pllfreq = CLK_GetPLLFreq_VSIPLL(pll, parent_rate);
> +		break;
> +	}
> +
> +	return pllfreq;
> +}
> +
> +static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long *prate)
> +{
> +	return rate;
> +}
> +
> +static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 val = __raw_readl(pll->ctl1_base);
> +
> +	return (val & VSIPLLCTL1_PD_MSK) ? 0 : 1;

Unnecessary parenthesis

> +}
> +
> +static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 val;
> +
> +	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
> +		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
> +		return -EACCES;
> +	}

Add helper for this, there is more than 1 copy of this.

> +
> +	CLK_UnLockReg(pll);
> +	val = __raw_readl(pll->ctl1_base);
> +	val &= ~VSIPLLCTL1_PD_MSK;
> +	__raw_writel(val, pll->ctl1_base);
> +	CLK_LockReg(pll);
> +	return 0;
> +}
> +
> +static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 val;
> +
> +	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
> +		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
> +	} else {
> +		val = __raw_readl(pll->ctl1_base);
> +		val |= VSIPLLCTL1_PD_MSK;
> +		__raw_writel(val, pll->ctl1_base);
> +	}
> +}
> +
> +static const struct clk_ops ma35d1_clk_pll_ops = {
> +	.is_prepared = ma35d1_clk_pll_is_prepared,
> +	.prepare = ma35d1_clk_pll_prepare,
> +	.unprepare = ma35d1_clk_pll_unprepare,
> +	.set_rate = ma35d1_clk_pll_set_rate,
> +	.recalc_rate = ma35d1_clk_pll_recalc_rate,
> +	.round_rate = ma35d1_clk_pll_round_rate,
> +};
> +
> +struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type,
> +				  u8 u8mode, const char *name,
> +				  const char *parent,
> +				  unsigned long targetFreq,
> +				  void __iomem *base,
> +				  struct regmap *regmap)
> +{
> +	struct ma35d1_clk_pll *pll;
> +	struct clk_hw *hw;
> +	struct clk_init_data init;
> +	int ret;
> +
> +	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
> +	if (!pll)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pll->type = type;
> +	pll->mode = u8mode;
> +	pll->rate = targetFreq;
> +	pll->ctl0_base = base + VSIPLL_CTL0;
> +	pll->ctl1_base = base + VSIPLL_CTL1;
> +	pll->ctl2_base = base + VSIPLL_CTL2;
> +	pll->regmap = regmap;
> +
> +	init.name = name;
> +	init.flags = 0;
> +	init.parent_names = &parent;
> +	init.num_parents = 1;
> +	init.ops = &ma35d1_clk_pll_ops;
> +	pll->hw.init = &init;
> +	hw = &pll->hw;
> +
> +	ret = clk_hw_register(NULL, hw);
> +	if (ret) {
> +		pr_err("failed to register vsi-pll clock!!!\n");

No need to use ! let alone 3 of them.

> +		kfree(pll);
> +		return ERR_PTR(ret);
> +	}
> +	return hw;
> +}
> diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
> new file mode 100644
> index 000000000000..ac8154458b81
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1.c
> @@ -0,0 +1,970 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> +
> +#include "clk-ma35d1.h"
> +
> +DEFINE_SPINLOCK(ma35d1_lock);
> +
> +static const char *const ca35clk_sel_clks[] = {
> +	"hxt", "capll", "ddrpll", "dummy"
> +};
> +
> +static const char *const sysclk0_sel_clks[] = {
> +	"epll_div2", "syspll"
> +};
> +
> +static const char *const sysclk1_sel_clks[] = {
> +	"hxt", "syspll"
> +};
> +
> +static const char *const axiclk_sel_clks[] = {
> +	"capll_div2", "capll_div4"
> +};
> +
> +static const char *const ccap_sel_clks[] = {
> +	"hxt", "vpll", "apll", "syspll"
> +};
> +
> +static const char *const sdh_sel_clks[] = {
> +	"syspll", "apll", "dummy", "dummy"
> +};
> +
> +static const char *const dcu_sel_clks[] = {
> +	"epll_div2", "syspll"
> +};
> +
> +static const char *const gfx_sel_clks[] = {
> +	"epll", "syspll"
> +};
> +
> +static const char *const dbg_sel_clks[] = {
> +	"hirc", "syspll"
> +};
> +
> +static const char *const timer0_sel_clks[] = {
> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer1_sel_clks[] = {
> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer2_sel_clks[] = {
> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer3_sel_clks[] = {
> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer4_sel_clks[] = {
> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer5_sel_clks[] = {
> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer6_sel_clks[] = {
> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer7_sel_clks[] = {
> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer8_sel_clks[] = {
> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer9_sel_clks[] = {
> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer10_sel_clks[] = {
> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const timer11_sel_clks[] = {
> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
> +};
> +
> +static const char *const uart_sel_clks[] = {
> +	"hxt", "sysclk1_div2", "dummy", "dummy"
> +};
> +
> +static const char *const wdt0_sel_clks[] = {
> +	"dummy", "lxt", "pclk3_div4096", "lirc"
> +};
> +
> +static const char *const wdt1_sel_clks[] = {
> +	"dummy", "lxt", "pclk3_div4096", "lirc"
> +};
> +
> +static const char *const wdt2_sel_clks[] = {
> +	"dummy", "lxt", "pclk4_div4096", "lirc"
> +};
> +
> +static const char *const wwdt0_sel_clks[] = {
> +	"dummy", "dummy", "pclk3_div4096", "lirc"
> +};
> +
> +static const char *const wwdt1_sel_clks[] = {
> +	"dummy", "dummy", "pclk3_div4096", "lirc"
> +};
> +
> +static const char *const wwdt2_sel_clks[] = {
> +	"dummy", "dummy", "pclk4_div4096", "lirc"
> +};
> +
> +static const char *const spi0_sel_clks[] = {
> +	"pclk1", "apll", "dummy", "dummy"
> +};
> +
> +static const char *const spi1_sel_clks[] = {
> +	"pclk2", "apll", "dummy", "dummy"
> +};
> +
> +static const char *const spi2_sel_clks[] = {
> +	"pclk1", "apll", "dummy", "dummy"
> +};
> +
> +static const char *const spi3_sel_clks[] = {
> +	"pclk2", "apll", "dummy", "dummy"
> +};
> +
> +static const char *const qspi0_sel_clks[] = {
> +	"pclk0", "apll", "dummy", "dummy"
> +};
> +
> +static const char *const qspi1_sel_clks[] = {
> +	"pclk0", "apll", "dummy", "dummy"
> +};
> +
> +static const char *const i2s0_sel_clks[] = {
> +	"apll", "sysclk1_div2", "dummy", "dummy"
> +};
> +
> +static const char *const i2s1_sel_clks[] = {
> +	"apll", "sysclk1_div2", "dummy", "dummy"
> +};
> +
> +static const char *const can_sel_clks[] = {
> +	"apll", "vpll"
> +};
> +
> +static const char *const cko_sel_clks[] = {
> +	"hxt", "lxt", "hirc", "lirc", "capll_div4", "syspll",
> +	"ddrpll", "epll_div2", "apll", "vpll", "dummy", "dummy",
> +	"dummy", "dummy", "dummy", "dummy"
> +};
> +
> +static const char *const smc_sel_clks[] = {
> +	"hxt", "pclk4"
> +};
> +
> +static const char *const kpi_sel_clks[] = {
> +	"hxt", "lxt"
> +};
> +
> +static const struct clk_div_table ip_div_table[] = {
> +	{0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
> +	{5, 12}, {6, 14}, {7, 16}, {0, 0},
> +};
> +
> +static const struct clk_div_table eadc_div_table[] = {
> +	{0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
> +	{5, 12}, {6, 14}, {7, 16}, {8, 18},
> +	{9, 20}, {10, 22}, {11, 24}, {12, 26},
> +	{13, 28}, {14, 30}, {15, 32}, {0, 0},
> +};
> +
> +static struct clk_hw **hws;
> +static struct clk_hw_onecell_data *ma35d1_hw_data;
> +
> +static int ma35d1_clocks_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *clk_node = dev->of_node;
> +	void __iomem *clk_base;
> +	struct regmap *regmap;
> +	u32 pllmode[5] = { 0, 0, 0, 0, 0 };
> +	u32 pllfreq[5] = { 0, 0, 0, 0, 0 };
> +
> +	dev_info(&pdev->dev, "Nuvoton MA35D1 Clock Driver\n");
> +	ma35d1_hw_data = devm_kzalloc(&pdev->dev, struct_size(ma35d1_hw_data,
> +				      hws, CLK_MAX_IDX), GFP_KERNEL);
> +
> +	if (WARN_ON(!ma35d1_hw_data))
> +		return -ENOMEM;
> +
> +	ma35d1_hw_data->num = CLK_MAX_IDX;
> +	hws = ma35d1_hw_data->hws;
> +
> +	clk_node = of_find_compatible_node(NULL, NULL, "nuvoton,ma35d1-clk");
> +	clk_base = of_iomap(clk_node, 0);
> +	of_node_put(clk_node);
> +	if (!clk_base) {
> +		pr_err("%s: could not map region\n", __func__);
> +		return -ENOMEM;
> +	}
> +	regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> +						 "nuvoton,sys");
> +	if (IS_ERR(regmap))
> +		pr_warn("%s: Unable to get syscon\n", __func__);

Don't print __func__ to user.

> +
> +	/* clock sources */
> +	hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
> +	hws[HXT_GATE] = ma35d1_clk_gate("hxt_gate", "hxt",
> +					clk_base + REG_CLK_PWRCTL, 0);
> +	hws[LXT] = ma35d1_clk_fixed("lxt", 32768);
> +	hws[LXT_GATE] = ma35d1_clk_gate("lxt_gate", "lxt",
> +					clk_base + REG_CLK_PWRCTL, 1);
> +	hws[HIRC] = ma35d1_clk_fixed("hirc", 12000000);
> +	hws[HIRC_GATE] = ma35d1_clk_gate("hirc_gate", "hirc",
> +					 clk_base + REG_CLK_PWRCTL, 2);
> +	hws[LIRC] = ma35d1_clk_fixed("lirc", 32000);
> +	hws[LIRC_GATE] = ma35d1_clk_gate("lirc_gate", "lirc",
> +					 clk_base + REG_CLK_PWRCTL, 3);
> +
> +	/* PLL */
> +	of_property_read_u32_array(clk_node, "clock-pll-mode", pllmode,
> +				   ARRAY_SIZE(pllmode));
> +	of_property_read_u32_array(clk_node, "assigned-clock-rates", pllfreq,
> +				   ARRAY_SIZE(pllfreq));
> +
> +	/* SMIC PLL */
> +	hws[CAPLL] = ma35d1_reg_clk_pll(MA35D1_CAPLL, pllmode[0], "capll",
> +					"hxt", pllfreq[0],
> +					clk_base + REG_CLK_PLL0CTL0, regmap);
> +	hws[SYSPLL] = ma35d1_clk_fixed("syspll", 180000000);
> +
> +	/* VSI PLL */
> +	hws[DDRPLL] = ma35d1_reg_clk_pll(MA35D1_DDRPLL, pllmode[1], "ddrpll",
> +					 "hxt", pllfreq[1],
> +					 clk_base + REG_CLK_PLL2CTL0, regmap);
> +	hws[APLL] = ma35d1_reg_clk_pll(MA35D1_APLL, pllmode[2], "apll", "hxt",
> +				       pllfreq[2], clk_base + REG_CLK_PLL3CTL0,
> +				       regmap);
> +	hws[EPLL] = ma35d1_reg_clk_pll(MA35D1_EPLL, pllmode[3], "epll", "hxt",
> +				       pllfreq[3], clk_base + REG_CLK_PLL4CTL0,
> +				       regmap);
> +	hws[VPLL] = ma35d1_reg_clk_pll(MA35D1_VPLL, pllmode[4], "vpll", "hxt",
> +				       pllfreq[4], clk_base + REG_CLK_PLL5CTL0,
> +				       regmap);
> +	hws[EPLL_DIV2] = ma35d1_clk_fixed_factor("epll_div2", "epll", 1, 2);
> +	hws[EPLL_DIV4] = ma35d1_clk_fixed_factor("epll_div4", "epll", 1, 4);
> +	hws[EPLL_DIV8] = ma35d1_clk_fixed_factor("epll_div8", "epll", 1, 8);
> +
> +	/* CA35 */
> +	hws[CA35CLK_MUX] = ma35d1_clk_mux("ca35clk_mux",
> +					  clk_base + REG_CLK_CLKSEL0, 0,
> +					  2, ca35clk_sel_clks,
> +					  ARRAY_SIZE(ca35clk_sel_clks));
> +
> +	/* AXI */
> +	hws[AXICLK_DIV2] = ma35d1_clk_fixed_factor("capll_div2", "ca35clk_mux",
> +						   1, 2);
> +	hws[AXICLK_DIV4] = ma35d1_clk_fixed_factor("capll_div4", "ca35clk_mux",
> +						   1, 4);
> +	hws[AXICLK_MUX] = ma35d1_clk_mux("axiclk_mux",
> +					 clk_base + REG_CLK_CLKDIV0,
> +					 26, 1, axiclk_sel_clks,
> +					 ARRAY_SIZE(axiclk_sel_clks));
> +
> +	/* SYSCLK0 & SYSCLK1 */
> +	hws[SYSCLK0_MUX] = ma35d1_clk_mux("sysclk0_mux",
> +					  clk_base + REG_CLK_CLKSEL0,
> +					  2, 1, sysclk0_sel_clks,
> +					  ARRAY_SIZE(sysclk0_sel_clks));
> +	hws[SYSCLK1_MUX] = ma35d1_clk_mux("sysclk1_mux",
> +					  clk_base + REG_CLK_CLKSEL0,
> +					  4, 1, sysclk1_sel_clks,
> +					  ARRAY_SIZE(sysclk1_sel_clks));
> +	hws[SYSCLK1_DIV2] = ma35d1_clk_fixed_factor("sysclk1_div2",
> +						    "sysclk1_mux", 1, 2);
> +
> +	/* HCLK0~3 & PCLK0~4 */
> +	hws[HCLK0] = ma35d1_clk_fixed_factor("hclk0", "sysclk1_mux", 1, 1);
> +	hws[HCLK1] = ma35d1_clk_fixed_factor("hclk1", "sysclk1_mux", 1, 1);
> +	hws[HCLK2] = ma35d1_clk_fixed_factor("hclk2", "sysclk1_mux", 1, 1);
> +	hws[PCLK0] = ma35d1_clk_fixed_factor("pclk0", "sysclk1_mux", 1, 1);
> +	hws[PCLK1] = ma35d1_clk_fixed_factor("pclk1", "sysclk1_mux", 1, 1);
> +	hws[PCLK2] = ma35d1_clk_fixed_factor("pclk2", "sysclk1_mux", 1, 1);
> +
> +	hws[HCLK3] = ma35d1_clk_fixed_factor("hclk3", "sysclk1_mux", 1, 2);
> +	hws[PCLK3] = ma35d1_clk_fixed_factor("pclk3", "sysclk1_mux", 1, 2);
> +	hws[PCLK4] = ma35d1_clk_fixed_factor("pclk4", "sysclk1_mux", 1, 2);
> +
> +	hws[USBPHY0] = ma35d1_clk_fixed("usbphy0", 480000000);
> +	hws[USBPHY1] = ma35d1_clk_fixed("usbphy1", 480000000);
> +
> +	/* DDR */
> +	hws[DDR0_GATE] = ma35d1_clk_gate("ddr0_gate", "ddrpll",
> +					 clk_base + REG_CLK_SYSCLK0, 4);
> +	hws[DDR6_GATE] = ma35d1_clk_gate("ddr6_gate", "ddrpll",
> +					 clk_base + REG_CLK_SYSCLK0, 5);
> +
> +	/* CAN0 */
> +	hws[CAN0_MUX] = ma35d1_clk_mux("can0_mux", clk_base + REG_CLK_CLKSEL4,
> +				       16, 1, can_sel_clks,
> +				       ARRAY_SIZE(can_sel_clks));
> +	hws[CAN0_DIV] = ma35d1_clk_divider_table("can0_div", "can0_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 0, 3, ip_div_table);
> +	hws[CAN0_GATE] = ma35d1_clk_gate("can0_gate", "can0_div",
> +					 clk_base + REG_CLK_SYSCLK0, 8);
> +
> +	/* CAN1 */
> +	hws[CAN1_MUX] = ma35d1_clk_mux("can1_mux", clk_base + REG_CLK_CLKSEL4,
> +				       17, 1, can_sel_clks,
> +				       ARRAY_SIZE(can_sel_clks));
> +	hws[CAN1_DIV] = ma35d1_clk_divider_table("can1_div", "can1_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 4, 3, ip_div_table);
> +	hws[CAN1_GATE] = ma35d1_clk_gate("can1_gate", "can1_div",
> +					 clk_base + REG_CLK_SYSCLK0, 9);
> +
> +	/* CAN2 */
> +	hws[CAN2_MUX] = ma35d1_clk_mux("can2_mux", clk_base + REG_CLK_CLKSEL4,
> +				       18, 1, can_sel_clks,
> +				       ARRAY_SIZE(can_sel_clks));
> +	hws[CAN2_DIV] = ma35d1_clk_divider_table("can2_div", "can2_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 8, 3, ip_div_table);
> +	hws[CAN2_GATE] = ma35d1_clk_gate("can2_gate", "can2_div",
> +					 clk_base + REG_CLK_SYSCLK0, 10);
> +
> +	/* CAN3 */
> +	hws[CAN3_MUX] = ma35d1_clk_mux("can3_mux", clk_base + REG_CLK_CLKSEL4,
> +				       19, 1, can_sel_clks,
> +				       ARRAY_SIZE(can_sel_clks));
> +	hws[CAN3_DIV] = ma35d1_clk_divider_table("can3_div", "can3_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 12, 3, ip_div_table);
> +	hws[CAN3_GATE] = ma35d1_clk_gate("can3_gate", "can3_div",
> +					 clk_base + REG_CLK_SYSCLK0, 11);
> +
> +	/* SDH0 */
> +	hws[SDH0_MUX] = ma35d1_clk_mux("sdh0_mux", clk_base + REG_CLK_CLKSEL0,
> +				       16, 2, sdh_sel_clks,
> +				       ARRAY_SIZE(sdh_sel_clks));
> +	hws[SDH0_GATE] = ma35d1_clk_gate("sdh0_gate", "sdh0_mux",
> +					 clk_base + REG_CLK_SYSCLK0, 16);
> +
> +	/* SDH1 */
> +	hws[SDH1_MUX] = ma35d1_clk_mux("sdh1_mux", clk_base + REG_CLK_CLKSEL0,
> +				       18, 2, sdh_sel_clks,
> +				       ARRAY_SIZE(sdh_sel_clks));
> +	hws[SDH1_GATE] = ma35d1_clk_gate("sdh1_gate", "sdh1_mux",
> +					 clk_base + REG_CLK_SYSCLK0, 17);
> +
> +	/* NAND */
> +	hws[NAND_GATE] = ma35d1_clk_gate("nand_gate", "hclk1",
> +					 clk_base + REG_CLK_SYSCLK0, 18);
> +
> +	/* USB */
> +	hws[USBD_GATE] = ma35d1_clk_gate("usbd_gate", "usbphy0",
> +					 clk_base + REG_CLK_SYSCLK0, 19);
> +	hws[USBH_GATE] = ma35d1_clk_gate("usbh_gate", "usbphy0",
> +					 clk_base + REG_CLK_SYSCLK0, 20);
> +	hws[HUSBH0_GATE] = ma35d1_clk_gate("husbh0_gate", "usbphy0",
> +					   clk_base + REG_CLK_SYSCLK0, 21);
> +	hws[HUSBH1_GATE] = ma35d1_clk_gate("husbh1_gate", "usbphy0",
> +					   clk_base + REG_CLK_SYSCLK0, 22);
> +
> +	/* GFX */
> +	hws[GFX_MUX] = ma35d1_clk_mux("gfx_mux", clk_base + REG_CLK_CLKSEL0,
> +				      26, 1, gfx_sel_clks,
> +				      ARRAY_SIZE(gfx_sel_clks));
> +	hws[GFX_GATE] = ma35d1_clk_gate("gfx_gate", "gfx_mux",
> +					clk_base + REG_CLK_SYSCLK0, 24);
> +
> +	/* VC8K */
> +	hws[VC8K_GATE] = ma35d1_clk_gate("vc8k_gate", "sysclk0_mux",
> +					 clk_base + REG_CLK_SYSCLK0, 25);
> +
> +	/* DCU */
> +	hws[DCU_MUX] = ma35d1_clk_mux("dcu_mux", clk_base + REG_CLK_CLKSEL0,
> +				      24, 1, dcu_sel_clks,
> +				      ARRAY_SIZE(dcu_sel_clks));
> +	hws[DCU_GATE] = ma35d1_clk_gate("dcu_gate", "dcu_mux",
> +					clk_base + REG_CLK_SYSCLK0, 26);
> +
> +	/* DCUP */
> +	hws[DCUP_DIV] = ma35d1_clk_divider_table("dcup_div", "vpll",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 16, 3, ip_div_table);
> +
> +	/* EMAC0 */
> +	hws[EMAC0_GATE] = ma35d1_clk_gate("emac0_gate", "epll_div2",
> +					  clk_base + REG_CLK_SYSCLK0, 27);
> +
> +	/* EMAC1 */
> +	hws[EMAC1_GATE] = ma35d1_clk_gate("emac1_gate", "epll_div2",
> +					  clk_base + REG_CLK_SYSCLK0, 28);
> +
> +	/* CCAP0 */
> +	hws[CCAP0_MUX] = ma35d1_clk_mux("ccap0_mux",
> +					clk_base + REG_CLK_CLKSEL0,
> +					12, 1, ccap_sel_clks,
> +					ARRAY_SIZE(ccap_sel_clks));
> +	hws[CCAP0_DIV] = ma35d1_clk_divider("ccap0_div", "ccap0_mux",
> +					    clk_base + REG_CLK_CLKDIV1, 8, 4);
> +	hws[CCAP0_GATE] = ma35d1_clk_gate("ccap0_gate", "ccap0_div",
> +					  clk_base + REG_CLK_SYSCLK0, 29);
> +
> +	/* CCAP1 */
> +	hws[CCAP1_MUX] = ma35d1_clk_mux("ccap1_mux",
> +					clk_base + REG_CLK_CLKSEL0,
> +					14, 1, ccap_sel_clks,
> +					ARRAY_SIZE(ccap_sel_clks));
> +	hws[CCAP1_DIV] = ma35d1_clk_divider("ccap1_div", "ccap1_mux",
> +					    clk_base + REG_CLK_CLKDIV1,
> +					    12, 4);
> +	hws[CCAP1_GATE] = ma35d1_clk_gate("ccap1_gate", "ccap1_div",
> +					  clk_base + REG_CLK_SYSCLK0, 30);
> +
> +	/* PDMA0~3 */
> +	hws[PDMA0_GATE] = ma35d1_clk_gate("pdma0_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 0);
> +	hws[PDMA1_GATE] = ma35d1_clk_gate("pdma1_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 1);
> +	hws[PDMA2_GATE] = ma35d1_clk_gate("pdma2_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 2);
> +	hws[PDMA3_GATE] = ma35d1_clk_gate("pdma3_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 3);
> +
> +	/* WH0~1 */
> +	hws[WH0_GATE] = ma35d1_clk_gate("wh0_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 4);
> +	hws[WH1_GATE] = ma35d1_clk_gate("wh1_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 5);
> +
> +	/* HWS */
> +	hws[HWS_GATE] = ma35d1_clk_gate("hws_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 6);
> +
> +	/* EBI */
> +	hws[EBI_GATE] = ma35d1_clk_gate("ebi_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 7);
> +
> +	/* SRAM0~1 */
> +	hws[SRAM0_GATE] = ma35d1_clk_gate("sram0_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 8);
> +	hws[SRAM1_GATE] = ma35d1_clk_gate("sram1_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 9);
> +
> +	/* ROM */
> +	hws[ROM_GATE] = ma35d1_clk_gate("rom_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 10);
> +
> +	/* TRA */
> +	hws[TRA_GATE] = ma35d1_clk_gate("tra_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 11);
> +
> +	/* DBG */
> +	hws[DBG_MUX] = ma35d1_clk_mux("dbg_mux", clk_base + REG_CLK_CLKSEL0,
> +				      27, 1, dbg_sel_clks,
> +				      ARRAY_SIZE(dbg_sel_clks));
> +	hws[DBG_GATE] = ma35d1_clk_gate("dbg_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 12);
> +
> +	/* CLKO */
> +	hws[CKO_MUX] = ma35d1_clk_mux("cko_mux", clk_base + REG_CLK_CLKSEL4,
> +				      24, 4, cko_sel_clks,
> +				      ARRAY_SIZE(cko_sel_clks));
> +	hws[CKO_DIV] = ma35d1_clk_divider_pow2("cko_div", "cko_mux",
> +					       clk_base + REG_CLK_CLKOCTL,
> +					       0, 4);
> +	hws[CKO_GATE] = ma35d1_clk_gate("cko_gate", "cko_div",
> +					clk_base + REG_CLK_SYSCLK1, 13);
> +
> +	/* GTMR */
> +	hws[GTMR_GATE] = ma35d1_clk_gate("gtmr_gate", "hirc",
> +					 clk_base + REG_CLK_SYSCLK1, 14);
> +
> +	/* GPIO */
> +	hws[GPA_GATE] = ma35d1_clk_gate("gpa_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 16);
> +	hws[GPB_GATE] = ma35d1_clk_gate("gpb_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 17);
> +	hws[GPC_GATE] = ma35d1_clk_gate("gpc_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 18);
> +	hws[GPD_GATE] = ma35d1_clk_gate("gpd_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 19);
> +	hws[GPE_GATE] = ma35d1_clk_gate("gpe_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 20);
> +	hws[GPF_GATE] = ma35d1_clk_gate("gpf_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 21);
> +	hws[GPG_GATE] = ma35d1_clk_gate("gpg_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 22);
> +	hws[GPH_GATE] = ma35d1_clk_gate("gph_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 23);
> +	hws[GPI_GATE] = ma35d1_clk_gate("gpi_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 24);
> +	hws[GPJ_GATE] = ma35d1_clk_gate("gpj_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 25);
> +	hws[GPK_GATE] = ma35d1_clk_gate("gpk_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 26);
> +	hws[GPL_GATE] = ma35d1_clk_gate("gpl_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 27);
> +	hws[GPM_GATE] = ma35d1_clk_gate("gpm_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 28);
> +	hws[GPN_GATE] = ma35d1_clk_gate("gpn_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 29);
> +
> +	/* TIMER0~11 */
> +	hws[TMR0_MUX] = ma35d1_clk_mux("tmr0_mux", clk_base + REG_CLK_CLKSEL1,
> +				       0, 3, timer0_sel_clks,
> +				       ARRAY_SIZE(timer0_sel_clks));
> +	hws[TMR0_GATE] = ma35d1_clk_gate("tmr0_gate", "tmr0_mux",
> +					 clk_base + REG_CLK_APBCLK0, 0);
> +	hws[TMR1_MUX] = ma35d1_clk_mux("tmr1_mux", clk_base + REG_CLK_CLKSEL1,
> +				       4, 3, timer1_sel_clks,
> +				       ARRAY_SIZE(timer1_sel_clks));
> +	hws[TMR1_GATE] = ma35d1_clk_gate("tmr1_gate", "tmr1_mux",
> +					 clk_base + REG_CLK_APBCLK0, 1);
> +	hws[TMR2_MUX] = ma35d1_clk_mux("tmr2_mux", clk_base + REG_CLK_CLKSEL1,
> +				       8, 3, timer2_sel_clks,
> +				       ARRAY_SIZE(timer2_sel_clks));
> +	hws[TMR2_GATE] = ma35d1_clk_gate("tmr2_gate", "tmr2_mux",
> +					 clk_base + REG_CLK_APBCLK0, 2);
> +	hws[TMR3_MUX] = ma35d1_clk_mux("tmr3_mux", clk_base + REG_CLK_CLKSEL1,
> +				       12, 3, timer3_sel_clks,
> +				       ARRAY_SIZE(timer3_sel_clks));
> +	hws[TMR3_GATE] = ma35d1_clk_gate("tmr3_gate", "tmr3_mux",
> +					 clk_base + REG_CLK_APBCLK0, 3);
> +	hws[TMR4_MUX] = ma35d1_clk_mux("tmr4_mux", clk_base + REG_CLK_CLKSEL1,
> +				       16, 3, timer4_sel_clks,
> +				       ARRAY_SIZE(timer4_sel_clks));
> +	hws[TMR4_GATE] = ma35d1_clk_gate("tmr4_gate", "tmr4_mux",
> +					 clk_base + REG_CLK_APBCLK0, 4);
> +	hws[TMR5_MUX] = ma35d1_clk_mux("tmr5_mux", clk_base + REG_CLK_CLKSEL1,
> +				       20, 3, timer5_sel_clks,
> +				       ARRAY_SIZE(timer5_sel_clks));
> +	hws[TMR5_GATE] = ma35d1_clk_gate("tmr5_gate", "tmr5_mux",
> +					 clk_base + REG_CLK_APBCLK0, 5);
> +	hws[TMR6_MUX] = ma35d1_clk_mux("tmr6_mux", clk_base + REG_CLK_CLKSEL1,
> +				       24, 3, timer6_sel_clks,
> +				       ARRAY_SIZE(timer6_sel_clks));
> +	hws[TMR6_GATE] = ma35d1_clk_gate("tmr6_gate", "tmr6_mux",
> +					 clk_base + REG_CLK_APBCLK0, 6);
> +	hws[TMR7_MUX] = ma35d1_clk_mux("tmr7_mux", clk_base + REG_CLK_CLKSEL1,
> +				       28, 3, timer7_sel_clks,
> +				       ARRAY_SIZE(timer7_sel_clks));
> +	hws[TMR7_GATE] = ma35d1_clk_gate("tmr7_gate", "tmr7_mux",
> +					 clk_base + REG_CLK_APBCLK0, 7);
> +	hws[TMR8_MUX] = ma35d1_clk_mux("tmr8_mux", clk_base + REG_CLK_CLKSEL2,
> +				       0, 3, timer8_sel_clks,
> +				       ARRAY_SIZE(timer8_sel_clks));
> +	hws[TMR8_GATE] = ma35d1_clk_gate("tmr8_gate", "tmr8_mux",
> +					 clk_base + REG_CLK_APBCLK0, 8);
> +	hws[TMR9_MUX] = ma35d1_clk_mux("tmr9_mux", clk_base + REG_CLK_CLKSEL2,
> +				       4, 3, timer9_sel_clks,
> +				       ARRAY_SIZE(timer9_sel_clks));
> +	hws[TMR9_GATE] = ma35d1_clk_gate("tmr9_gate", "tmr9_mux",
> +					 clk_base + REG_CLK_APBCLK0, 9);
> +	hws[TMR10_MUX] = ma35d1_clk_mux("tmr10_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					8, 3, timer10_sel_clks,
> +					ARRAY_SIZE(timer10_sel_clks));
> +	hws[TMR10_GATE] = ma35d1_clk_gate("tmr10_gate", "tmr10_mux",
> +					  clk_base + REG_CLK_APBCLK0, 10);
> +	hws[TMR11_MUX] = ma35d1_clk_mux("tmr11_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					12, 3, timer11_sel_clks,
> +					ARRAY_SIZE(timer11_sel_clks));
> +	hws[TMR11_GATE] = ma35d1_clk_gate("tmr11_gate", "tmr11_mux",
> +					  clk_base + REG_CLK_APBCLK0, 11);
> +
> +	/* UART0~16 */
> +	hws[UART0_MUX] = ma35d1_clk_mux("uart0_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					16, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART0_DIV] = ma35d1_clk_divider("uart0_div", "uart0_mux",
> +					    clk_base + REG_CLK_CLKDIV1,
> +					    16, 4);
> +	hws[UART0_GATE] = ma35d1_clk_gate("uart0_gate", "uart0_div",
> +					  clk_base + REG_CLK_APBCLK0, 12);
> +	hws[UART1_MUX] = ma35d1_clk_mux("uart1_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					18, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART1_DIV] = ma35d1_clk_divider("uart1_div", "uart1_mux",
> +					    clk_base + REG_CLK_CLKDIV1,
> +					    20, 4);
> +	hws[UART1_GATE] = ma35d1_clk_gate("uart1_gate", "uart1_div",
> +					  clk_base + REG_CLK_APBCLK0, 13);
> +	hws[UART2_MUX] = ma35d1_clk_mux("uart2_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					20, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART2_DIV] = ma35d1_clk_divider("uart2_div", "uart2_mux",
> +					    clk_base + REG_CLK_CLKDIV1,
> +					    24, 4);
> +	hws[UART2_GATE] = ma35d1_clk_gate("uart2_gate", "uart2_div",
> +					  clk_base + REG_CLK_APBCLK0, 14);
> +	hws[UART3_MUX] = ma35d1_clk_mux("uart3_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					22, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART3_DIV] = ma35d1_clk_divider("uart3_div", "uart3_mux",
> +					    clk_base + REG_CLK_CLKDIV1,
> +					    28, 4);
> +	hws[UART3_GATE] = ma35d1_clk_gate("uart3_gate", "uart3_div",
> +					  clk_base + REG_CLK_APBCLK0, 15);
> +	hws[UART4_MUX] = ma35d1_clk_mux("uart4_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					24, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART4_DIV] = ma35d1_clk_divider("uart4_div", "uart4_mux",
> +					    clk_base + REG_CLK_CLKDIV2,
> +					    0, 4);
> +	hws[UART4_GATE] = ma35d1_clk_gate("uart4_gate", "uart4_div",
> +					  clk_base + REG_CLK_APBCLK0, 16);
> +	hws[UART5_MUX] = ma35d1_clk_mux("uart5_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					26, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART5_DIV] = ma35d1_clk_divider("uart5_div", "uart5_mux",
> +					    clk_base + REG_CLK_CLKDIV2,
> +					    4, 4);
> +	hws[UART5_GATE] = ma35d1_clk_gate("uart5_gate", "uart5_div",
> +					  clk_base + REG_CLK_APBCLK0, 17);
> +	hws[UART6_MUX] = ma35d1_clk_mux("uart6_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					28, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART6_DIV] = ma35d1_clk_divider("uart6_div", "uart6_mux",
> +					    clk_base + REG_CLK_CLKDIV2,
> +					    8, 4);
> +	hws[UART6_GATE] = ma35d1_clk_gate("uart6_gate", "uart6_div",
> +					  clk_base + REG_CLK_APBCLK0, 18);
> +	hws[UART7_MUX] = ma35d1_clk_mux("uart7_mux",
> +					clk_base + REG_CLK_CLKSEL2,
> +					30, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART7_DIV] = ma35d1_clk_divider("uart7_div", "uart7_mux",
> +					    clk_base + REG_CLK_CLKDIV2,
> +					    12, 4);
> +	hws[UART7_GATE] = ma35d1_clk_gate("uart7_gate", "uart7_div",
> +					  clk_base + REG_CLK_APBCLK0, 19);
> +	hws[UART8_MUX] = ma35d1_clk_mux("uart8_mux",
> +					clk_base + REG_CLK_CLKSEL3,
> +					0, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART8_DIV] = ma35d1_clk_divider("uart8_div", "uart8_mux",
> +					    clk_base + REG_CLK_CLKDIV2,
> +					    16, 4);
> +	hws[UART8_GATE] = ma35d1_clk_gate("uart8_gate", "uart8_div",
> +					  clk_base + REG_CLK_APBCLK0, 20);
> +	hws[UART9_MUX] = ma35d1_clk_mux("uart9_mux",
> +					clk_base + REG_CLK_CLKSEL3,
> +					2, 2, uart_sel_clks,
> +					ARRAY_SIZE(uart_sel_clks));
> +	hws[UART9_DIV] = ma35d1_clk_divider("uart9_div", "uart9_mux",
> +					    clk_base + REG_CLK_CLKDIV2,
> +					    20, 4);
> +	hws[UART9_GATE] = ma35d1_clk_gate("uart9_gate", "uart9_div",
> +					  clk_base + REG_CLK_APBCLK0, 21);
> +	hws[UART10_MUX] = ma35d1_clk_mux("uart10_mux",
> +					 clk_base + REG_CLK_CLKSEL3,
> +					 4, 2, uart_sel_clks,
> +					 ARRAY_SIZE(uart_sel_clks));
> +	hws[UART10_DIV] = ma35d1_clk_divider("uart10_div", "uart10_mux",
> +					     clk_base + REG_CLK_CLKDIV2,
> +					     24, 4);
> +	hws[UART10_GATE] = ma35d1_clk_gate("uart10_gate", "uart10_div",
> +					   clk_base + REG_CLK_APBCLK0, 22);
> +	hws[UART11_MUX] = ma35d1_clk_mux("uart11_mux",
> +					 clk_base + REG_CLK_CLKSEL3,
> +					 6, 2, uart_sel_clks,
> +					 ARRAY_SIZE(uart_sel_clks));
> +	hws[UART11_DIV] = ma35d1_clk_divider("uart11_div", "uart11_mux",
> +					     clk_base + REG_CLK_CLKDIV2,
> +					     28, 4);
> +	hws[UART11_GATE] = ma35d1_clk_gate("uart11_gate", "uart11_div",
> +					   clk_base + REG_CLK_APBCLK0, 23);
> +	hws[UART12_MUX] = ma35d1_clk_mux("uart12_mux",
> +					 clk_base + REG_CLK_CLKSEL3,
> +					 8, 2, uart_sel_clks,
> +					 ARRAY_SIZE(uart_sel_clks));
> +	hws[UART12_DIV] = ma35d1_clk_divider("uart12_div", "uart12_mux",
> +					     clk_base + REG_CLK_CLKDIV3,
> +					     0, 4);
> +	hws[UART12_GATE] = ma35d1_clk_gate("uart12_gate", "uart12_div",
> +					   clk_base + REG_CLK_APBCLK0, 24);
> +	hws[UART13_MUX] = ma35d1_clk_mux("uart13_mux",
> +					 clk_base + REG_CLK_CLKSEL3,
> +					 10, 2, uart_sel_clks,
> +					 ARRAY_SIZE(uart_sel_clks));
> +	hws[UART13_DIV] = ma35d1_clk_divider("uart13_div", "uart13_mux",
> +					     clk_base + REG_CLK_CLKDIV3,
> +					     4, 4);
> +	hws[UART13_GATE] = ma35d1_clk_gate("uart13_gate", "uart13_div",
> +					   clk_base + REG_CLK_APBCLK0, 25);
> +	hws[UART14_MUX] = ma35d1_clk_mux("uart14_mux",
> +					 clk_base + REG_CLK_CLKSEL3,
> +					 12, 2, uart_sel_clks,
> +					 ARRAY_SIZE(uart_sel_clks));
> +	hws[UART14_DIV] = ma35d1_clk_divider("uart14_div", "uart14_mux",
> +					     clk_base + REG_CLK_CLKDIV3,
> +					     8, 4);
> +	hws[UART14_GATE] = ma35d1_clk_gate("uart14_gate", "uart14_div",
> +					   clk_base + REG_CLK_APBCLK0, 26);
> +	hws[UART15_MUX] = ma35d1_clk_mux("uart15_mux",
> +					 clk_base + REG_CLK_CLKSEL3,
> +					 14, 2, uart_sel_clks,
> +					 ARRAY_SIZE(uart_sel_clks));
> +	hws[UART15_DIV] = ma35d1_clk_divider("uart15_div", "uart15_mux",
> +					     clk_base + REG_CLK_CLKDIV3,
> +					     12, 4);
> +	hws[UART15_GATE] = ma35d1_clk_gate("uart15_gate", "uart15_div",
> +					   clk_base + REG_CLK_APBCLK0, 27);
> +	hws[UART16_MUX] = ma35d1_clk_mux("uart16_mux",
> +					 clk_base + REG_CLK_CLKSEL3,
> +					 16, 2, uart_sel_clks,
> +					 ARRAY_SIZE(uart_sel_clks));
> +	hws[UART16_DIV] = ma35d1_clk_divider("uart16_div", "uart16_mux",
> +					     clk_base + REG_CLK_CLKDIV3,
> +					     16, 4);
> +	hws[UART16_GATE] = ma35d1_clk_gate("uart16_gate", "uart16_div",
> +					   clk_base + REG_CLK_APBCLK0, 28);
> +
> +	/* RTC */
> +	hws[RTC_GATE] = ma35d1_clk_gate("rtc_gate", "lxt",
> +					clk_base + REG_CLK_APBCLK0, 29);
> +
> +	/* DDRP */
> +	hws[DDR_GATE] = ma35d1_clk_gate("ddr_gate", "ddrpll",
> +					clk_base + REG_CLK_APBCLK0, 30);
> +
> +	/* KPI */
> +	hws[KPI_MUX] = ma35d1_clk_mux("kpi_mux", clk_base + REG_CLK_CLKSEL4,
> +				      30, 1, kpi_sel_clks,
> +				      ARRAY_SIZE(kpi_sel_clks));
> +	hws[KPI_DIV] = ma35d1_clk_divider("kpi_div", "kpi_mux",
> +					  clk_base + REG_CLK_CLKDIV4,
> +					  24, 8);
> +	hws[KPI_GATE] = ma35d1_clk_gate("kpi_gate", "kpi_div",
> +					clk_base + REG_CLK_APBCLK0, 31);
> +
> +	/* I2C0~5 */
> +	hws[I2C0_GATE] = ma35d1_clk_gate("i2c0_gate", "pclk0",
> +					 clk_base + REG_CLK_APBCLK1, 0);
> +	hws[I2C1_GATE] = ma35d1_clk_gate("i2c1_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK1, 1);
> +	hws[I2C2_GATE] = ma35d1_clk_gate("i2c2_gate", "pclk2",
> +					 clk_base + REG_CLK_APBCLK1, 2);
> +	hws[I2C3_GATE] = ma35d1_clk_gate("i2c3_gate", "pclk0",
> +					 clk_base + REG_CLK_APBCLK1, 3);
> +	hws[I2C4_GATE] = ma35d1_clk_gate("i2c4_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK1, 4);
> +	hws[I2C5_GATE] = ma35d1_clk_gate("i2c5_gate", "pclk2",
> +					 clk_base + REG_CLK_APBCLK1, 5);
> +
> +	/* QSPI0~1 */
> +	hws[QSPI0_MUX] = ma35d1_clk_mux("qspi0_mux",
> +					clk_base + REG_CLK_CLKSEL4,
> +					8, 2, qspi0_sel_clks,
> +					ARRAY_SIZE(qspi0_sel_clks));
> +	hws[QSPI0_GATE] = ma35d1_clk_gate("qspi0_gate", "qspi0_mux",
> +					  clk_base + REG_CLK_APBCLK1, 6);
> +	hws[QSPI1_MUX] = ma35d1_clk_mux("qspi1_mux",
> +					clk_base + REG_CLK_CLKSEL4,
> +					10, 2, qspi1_sel_clks,
> +					ARRAY_SIZE(qspi1_sel_clks));
> +	hws[QSPI1_GATE] = ma35d1_clk_gate("qspi1_gate", "qspi1_mux",
> +					  clk_base + REG_CLK_APBCLK1, 7);
> +
> +	/* SMC0~1 */
> +	hws[SMC0_MUX] = ma35d1_clk_mux("smc0_mux",
> +					clk_base + REG_CLK_CLKSEL4,
> +					28, 1, smc_sel_clks,
> +					ARRAY_SIZE(smc_sel_clks));
> +	hws[SMC0_DIV] = ma35d1_clk_divider("smc0_div", "smc0_mux",
> +					   clk_base + REG_CLK_CLKDIV1,
> +					   0, 4);
> +	hws[SMC0_GATE] = ma35d1_clk_gate("smc0_gate", "smc0_div",
> +					 clk_base + REG_CLK_APBCLK1, 12);
> +
> +	hws[SMC1_MUX] = ma35d1_clk_mux("smc1_mux",
> +					 clk_base + REG_CLK_CLKSEL4,
> +					 29, 1, smc_sel_clks,
> +					 ARRAY_SIZE(smc_sel_clks));
> +	hws[SMC1_DIV] = ma35d1_clk_divider("smc1_div", "smc1_mux",
> +					   clk_base + REG_CLK_CLKDIV1,
> +					   4, 4);
> +	hws[SMC1_GATE] = ma35d1_clk_gate("smc1_gate", "smc1_div",
> +					 clk_base + REG_CLK_APBCLK1, 13);
> +
> +	/* WDT0~2 */
> +	hws[WDT0_MUX] = ma35d1_clk_mux("wdt0_mux",
> +				       clk_base + REG_CLK_CLKSEL3,
> +				       20, 2, wdt0_sel_clks,
> +				       ARRAY_SIZE(wdt0_sel_clks));
> +	hws[WDT0_GATE] = ma35d1_clk_gate("wdt0_gate", "wdt0_mux",
> +					 clk_base + REG_CLK_APBCLK1, 16);
> +	hws[WDT1_MUX] = ma35d1_clk_mux("wdt1_mux",
> +				       clk_base + REG_CLK_CLKSEL3,
> +				       24, 2, wdt1_sel_clks,
> +				       ARRAY_SIZE(wdt1_sel_clks));
> +	hws[WDT1_GATE] = ma35d1_clk_gate("wdt1_gate", "wdt1_mux",
> +					 clk_base + REG_CLK_APBCLK1, 17);
> +	hws[WDT2_MUX] = ma35d1_clk_mux("wdt2_mux",
> +				       clk_base + REG_CLK_CLKSEL3,
> +				       28, 2, wdt2_sel_clks,
> +				       ARRAY_SIZE(wdt2_sel_clks));
> +	hws[WDT2_GATE] = ma35d1_clk_gate("wdt2_gate", "wdt2_mux",
> +				       clk_base + REG_CLK_APBCLK1, 18);
> +
> +	/* WWDT0~2 */
> +	hws[WWDT0_MUX] = ma35d1_clk_mux("wwdt0_mux",
> +					clk_base + REG_CLK_CLKSEL3,
> +					22, 2, wwdt0_sel_clks,
> +					ARRAY_SIZE(wwdt0_sel_clks));
> +	hws[WWDT1_MUX] = ma35d1_clk_mux("wwdt1_mux",
> +					clk_base + REG_CLK_CLKSEL3,
> +					26, 2, wwdt1_sel_clks,
> +					ARRAY_SIZE(wwdt1_sel_clks));
> +	hws[WWDT2_MUX] = ma35d1_clk_mux("wwdt2_mux",
> +					clk_base + REG_CLK_CLKSEL3,
> +					30, 2, wwdt2_sel_clks,
> +					ARRAY_SIZE(wwdt2_sel_clks));
> +
> +	/* EPWM0~2 */
> +	hws[EPWM0_GATE] = ma35d1_clk_gate("epwm0_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK1, 24);
> +	hws[EPWM1_GATE] = ma35d1_clk_gate("epwm1_gate", "pclk2",
> +					  clk_base + REG_CLK_APBCLK1, 25);
> +	hws[EPWM2_GATE] = ma35d1_clk_gate("epwm2_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK1, 26);
> +
> +	/* I2S0~1 */
> +	hws[I2S0_MUX] = ma35d1_clk_mux("i2s0_mux",
> +				       clk_base + REG_CLK_CLKSEL4,
> +				       12, 2, i2s0_sel_clks,
> +				       ARRAY_SIZE(i2s0_sel_clks));
> +	hws[I2S0_GATE] = ma35d1_clk_gate("i2s0_gate", "i2s0_mux",
> +					 clk_base + REG_CLK_APBCLK2, 0);
> +	hws[I2S1_MUX] = ma35d1_clk_mux("i2s1_mux",
> +				       clk_base + REG_CLK_CLKSEL4,
> +				       14, 2, i2s1_sel_clks,
> +				       ARRAY_SIZE(i2s1_sel_clks));
> +	hws[I2S1_GATE] = ma35d1_clk_gate("i2s1_gate", "i2s1_mux",
> +					 clk_base + REG_CLK_APBCLK2, 1);
> +
> +	/* SSMCC */
> +	hws[SSMCC_GATE] = ma35d1_clk_gate("ssmcc_gate", "pclk3",
> +					  clk_base + REG_CLK_APBCLK2, 2);
> +
> +	/* SSPCC */
> +	hws[SSPCC_GATE] = ma35d1_clk_gate("sspcc_gate", "pclk3",
> +					  clk_base + REG_CLK_APBCLK2, 3);
> +
> +	/* SPI0~3 */
> +	hws[SPI0_MUX] = ma35d1_clk_mux("spi0_mux",
> +				       clk_base + REG_CLK_CLKSEL4,
> +				       0, 2, spi0_sel_clks,
> +				       ARRAY_SIZE(spi0_sel_clks));
> +	hws[SPI0_GATE] = ma35d1_clk_gate("spi0_gate", "spi0_mux",
> +					 clk_base + REG_CLK_APBCLK2, 4);
> +	hws[SPI1_MUX] = ma35d1_clk_mux("spi1_mux",
> +				       clk_base + REG_CLK_CLKSEL4,
> +				       2, 2, spi1_sel_clks,
> +				       ARRAY_SIZE(spi1_sel_clks));
> +	hws[SPI1_GATE] = ma35d1_clk_gate("spi1_gate", "spi1_mux",
> +					 clk_base + REG_CLK_APBCLK2, 5);
> +	hws[SPI2_MUX] = ma35d1_clk_mux("spi2_mux",
> +				       clk_base + REG_CLK_CLKSEL4,
> +				       4, 2, spi2_sel_clks,
> +				       ARRAY_SIZE(spi2_sel_clks));
> +	hws[SPI2_GATE] = ma35d1_clk_gate("spi2_gate", "spi2_mux",
> +					 clk_base + REG_CLK_APBCLK2, 6);
> +	hws[SPI3_MUX] = ma35d1_clk_mux("spi3_mux",
> +				       clk_base + REG_CLK_CLKSEL4,
> +				       6, 2, spi3_sel_clks,
> +				       ARRAY_SIZE(spi3_sel_clks));
> +	hws[SPI3_GATE] = ma35d1_clk_gate("spi3_gate", "spi3_mux",
> +					 clk_base + REG_CLK_APBCLK2, 7);
> +
> +	/* ECAP0~2 */
> +	hws[ECAP0_GATE] = ma35d1_clk_gate("ecap0_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK2, 8);
> +	hws[ECAP1_GATE] = ma35d1_clk_gate("ecap1_gate", "pclk2",
> +					  clk_base + REG_CLK_APBCLK2, 9);
> +	hws[ECAP2_GATE] = ma35d1_clk_gate("ecap2_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK2, 10);
> +
> +	/* QEI0~2 */
> +	hws[QEI0_GATE] = ma35d1_clk_gate("qei0_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK2, 12);
> +	hws[QEI1_GATE] = ma35d1_clk_gate("qei1_gate", "pclk2",
> +					 clk_base + REG_CLK_APBCLK2, 13);
> +	hws[QEI2_GATE] = ma35d1_clk_gate("qei2_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK2, 14);
> +
> +	/* ADC */
> +	hws[ADC_DIV] = ma35d1_reg_adc_clkdiv(dev, "adc_div", "pclk0", 0,
> +					     clk_base + REG_CLK_CLKDIV4,
> +					     4, 17, 0x1ffff);
> +	hws[ADC_GATE] = ma35d1_clk_gate("adc_gate", "adc_div",
> +					clk_base + REG_CLK_APBCLK2, 24);
> +
> +	/* EADC */
> +	hws[EADC_DIV] = ma35d1_clk_divider_table("eadc_div", "pclk2",
> +						 clk_base + REG_CLK_CLKDIV4,
> +						 0, 4, eadc_div_table);
> +	hws[EADC_GATE] = ma35d1_clk_gate("eadc_gate", "eadc_div",
> +					 clk_base + REG_CLK_APBCLK2, 25);
> +
> +	ret = of_clk_add_hw_provider(clk_node, of_clk_hw_onecell_get,
> +				     ma35d1_hw_data);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to register hws for MA35D1\n");
> +		iounmap(clk_base);
> +	}
> +	return ret;
> +}
> +
> +static const struct of_device_id ma35d1_clk_of_match[] = {
> +	{ .compatible = "nuvoton,ma35d1-clk" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, ma35d1_clk_of_match);
> +
> +static struct platform_driver ma35d1_clk_driver = {
> +	.probe = ma35d1_clocks_probe,
> +	.driver = {
> +		.name = "ma35d1-clk",
> +		.of_match_table = ma35d1_clk_of_match,
> +	},
> +};
> +
> +static int __init ma35d1_clocks_init(void)
> +{
> +	return platform_driver_register(&ma35d1_clk_driver);
> +}
> +
> +postcore_initcall(ma35d1_clocks_init);
> +
> +MODULE_AUTHOR("Chi-Fang Li<cfli0@nuvoton.com>");

Space missing.

> +MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
> +MODULE_LICENSE("GPL v2");

"GPL" is enough.

> diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
> new file mode 100644
> index 000000000000..faae5a17e425
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1.h
> @@ -0,0 +1,198 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#ifndef __DRV_CLK_NUVOTON_MA35D1_H
> +#define __DRV_CLK_NUVOTON_MA35D1_H
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/spinlock.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/ma35d1-sys.h>
> +
> +enum ma35d1_pll_type {
> +	MA35D1_CAPLL,
> +	MA35D1_DDRPLL,
> +	MA35D1_APLL,
> +	MA35D1_EPLL,
> +	MA35D1_VPLL,
> +};
> +
> +enum ma35d1_pll_mode {
> +	VSIPLL_INTEGER_MODE,
> +	VSIPLL_FRACTIONAL_MODE,
> +	VSIPLL_SS_MODE,
> +};
> +
> +/* VSI-PLL CTL0~2 */
> +#define VSIPLL_CTL0			0x0
> +#define VSIPLL_CTL1			0x4
> +#define VSIPLL_CTL2			0x8
> +
> +/* VSI-PLL Specification limits */
> +#define VSIPLL_FREF_MAX_FREQ		200000000UL
> +#define VSIPLL_FREF_MIN_FREQ		1000000UL
> +#define VSIPLL_FREFDIVM_MAX_FREQ	40000000UL
> +#define VSIPLL_FREFDIVM_MIN_FREQ0	1000000UL
> +#define VSIPLL_FREFDIVM_MIN_FREQ1	10000000UL
> +#define VSIPLL_FCLK_MAX_FREQ		2400000000UL
> +#define VSIPLL_FCLK_MIN_FREQ		600000000UL
> +#define VSIPLL_FCLKO_MAX_FREQ		2400000000UL
> +#define VSIPLL_FCLKO_MIN_FREQ		85700000UL
> +#define VSIPLL_SPREAD_RANGE		194
> +#define VSIPLL_MODULATION_FREQ		50000
> +
> +/* Clock Control Registers Offset */
> +#define REG_CLK_PWRCTL			(0x00)

Unnecessary parenthesis.

> +#define REG_CLK_SYSCLK0			(0x04)
> +#define REG_CLK_SYSCLK1			(0x08)
> +#define REG_CLK_APBCLK0			(0x0C)
> +#define REG_CLK_APBCLK1			(0x10)
> +#define REG_CLK_APBCLK2			(0x14)
> +#define REG_CLK_CLKSEL0			(0x18)
> +#define REG_CLK_CLKSEL1			(0x1C)
> +#define REG_CLK_CLKSEL2			(0x20)
> +#define REG_CLK_CLKSEL3			(0x24)
> +#define REG_CLK_CLKSEL4			(0x28)
> +#define REG_CLK_CLKDIV0			(0x2C)
> +#define REG_CLK_CLKDIV1			(0x30)
> +#define REG_CLK_CLKDIV2			(0x34)
> +#define REG_CLK_CLKDIV3			(0x38)
> +#define REG_CLK_CLKDIV4			(0x3C)
> +#define REG_CLK_CLKOCTL			(0x40)
> +#define REG_CLK_STATUS			(0x50)
> +#define REG_CLK_PLL0CTL0		(0x60)
> +#define REG_CLK_PLL2CTL0		(0x80)
> +#define REG_CLK_PLL2CTL1		(0x84)
> +#define REG_CLK_PLL2CTL2		(0x88)
> +#define REG_CLK_PLL3CTL0		(0x90)
> +#define REG_CLK_PLL3CTL1		(0x94)
> +#define REG_CLK_PLL3CTL2		(0x98)
> +#define REG_CLK_PLL4CTL0		(0xA0)
> +#define REG_CLK_PLL4CTL1		(0xA4)
> +#define REG_CLK_PLL4CTL2		(0xA8)
> +#define REG_CLK_PLL5CTL0		(0xB0)
> +#define REG_CLK_PLL5CTL1		(0xB4)
> +#define REG_CLK_PLL5CTL2		(0xB8)
> +#define REG_CLK_CLKDCTL			(0xC0)
> +#define REG_CLK_CLKDSTS			(0xC4)
> +#define REG_CLK_CDUPB			(0xC8)
> +#define REG_CLK_CDLOWB			(0xCC)
> +#define REG_CLK_CKFLTRCTL		(0xD0)
> +#define REG_CLK_TESTCLK			(0xF0)
> +#define REG_CLK_PLLCTL			(0x40)
> +
> +/* Constant Definitions for Clock Controller */
> +#define SMICPLLCTL0_FBDIV_POS		(0)
> +#define SMICPLLCTL0_FBDIV_MSK		(0xfful << SMICPLLCTL0_FBDIV_POS)
> +#define SMICPLLCTL0_INDIV_POS		(8)
> +#define SMICPLLCTL0_INDIV_MSK		(0xful << SMICPLLCTL0_INDIV_POS)
> +#define SMICPLLCTL0_OUTDIV_POS		(12)
> +#define SMICPLLCTL0_OUTDIV_MSK		(0x3ul << SMICPLLCTL0_OUTDIV_POS)

GENMASK() + remove _POS define completely.

> +#define SMICPLLCTL0_PD_POS		(16)
> +#define SMICPLLCTL0_PD_MSK		(0x1ul << SMICPLLCTL0_PD_POS)

BIT() + remove _POS.

Is this really a mask or a bit? I'd remove _MSK from the name (which is 
usually not that useful anyway even if it would be a multiple bit mask 
for real).

> +#define SMICPLLCTL0_BP_POS		(17)
> +#define SMICPLLCTL0_BP_MSK		(0x1ul << SMICPLLCTL0_BP_POS)

BIT()?

> +#define VSIPLLCTL0_FBDIV_POS		(0)
> +#define VSIPLLCTL0_FBDIV_MSK		(0x7fful << VSIPLLCTL0_FBDIV_POS)
> +#define VSIPLLCTL0_INDIV_POS		(12)
> +#define VSIPLLCTL0_INDIV_MSK		(0x3ful << VSIPLLCTL0_INDIV_POS)
> +#define VSIPLLCTL0_MODE_POS		(18)
> +#define VSIPLLCTL0_MODE_MSK		(0x3ul << VSIPLLCTL0_MODE_POS)
> +#define VSIPLLCTL0_SSRATE_POS		(20)
> +#define VSIPLLCTL0_SSRATE_MSK		(0x7fful << VSIPLLCTL0_SSRATE_POS)
> +#define VSIPLLCTL1_PD_POS		(0)
> +#define VSIPLLCTL1_PD_MSK		(0x1ul << VSIPLLCTL1_PD_POS)
> +#define VSIPLLCTL1_BP_POS		(1)
> +#define VSIPLLCTL1_BP_MSK		(0x1ul << VSIPLLCTL1_BP_POS)
> +#define VSIPLLCTL1_OUTDIV_POS		(4)
> +#define VSIPLLCTL1_OUTDIV_MSK		(0x7ul << VSIPLLCTL1_OUTDIV_POS)
> +#define VSIPLLCTL1_FRAC_POS		(8)
> +#define VSIPLLCTL1_FRAC_MSK		(0xfffffful << VSIPLLCTL1_FRAC_POS)
> +#define VSIPLLCTL2_SLOPE_POS		(0)
> +#define VSIPLLCTL2_SLOPE_MSK		(0xfffffful << VSIPLLCTL2_SLOPE_POS)

...and more of them.



-- 
 i.


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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-16 14:17   ` Arnd Bergmann
@ 2023-03-16 16:44     ` Lee Jones
  2023-03-18 13:32       ` Jacky Huang
  2023-03-18 13:17     ` Jacky Huang
  1 sibling, 1 reply; 89+ messages in thread
From: Lee Jones @ 2023-03-16 16:44 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby, devicetree, linux-clk,
	linux-kernel, linux-serial, schung, Jacky Huang

On Thu, 16 Mar 2023, Arnd Bergmann wrote:

> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
> > +	mem: memory@80000000 {
> > +		device_type = "memory";
> > +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
> > +	};
> > +};
>
> In most machines, the memory size is detected by the boot loader
> and filled in the dtb in memory before starting the kernel, so
> you should not need two separate files here for the two common
> memory configurations.
>
> Since the machine is called 'som', I would assume that this is a
> module that is integrated on another board, so more commonly one
> would have a dtsi file for the som in addition to the one for the
> soc, and have all the components of the module listed in this
> file, while the dts file that includes the som.dtsi lists the
> devices on the carrier board and enables the on-chip devices
> that are connected to the outside.

It's using syscon and simple-mfd by the looks of it.

--
Lee Jones [李琼斯]

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

* Re: [PATCH 06/15] dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible
  2023-03-16  7:31   ` Krzysztof Kozlowski
@ 2023-03-17  1:03     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  1:03 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof ,

Thank you for review.


On 2023/3/16 下午 03:31, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Add Nuvoton ma35d1 system registers compatible
> Missing full stop.

I will fix it.

>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   Documentation/devicetree/bindings/mfd/syscon.yaml | 1 +
>>   1 file changed, 1 insertion(+)
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
>> index c828c4f5e4a7..e7a3c6e1e77f 100644
>> --- a/Documentation/devicetree/bindings/mfd/syscon.yaml
>> +++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
>> @@ -57,6 +57,7 @@ properties:
>>                 - microchip,sparx5-cpu-syscon
>>                 - mstar,msc313-pmsleep
>>                 - nuvoton,wpcm450-shm
>> +              - nuvoton,ma35d1-sys
> Wrong order

I will fix the order.

>
> Best regards,
> Krzysztof
>

Best regards,

Jacky Huang


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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-15 22:30   ` Stephen Boyd
@ 2023-03-17  3:07     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  3:07 UTC (permalink / raw)
  To: Stephen Boyd, gregkh, jirislaby, krzysztof.kozlowski+dt, lee,
	mturquette, p.zabel, robh+dt
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Stephen,

Thanks for your review.


On 2023/3/16 上午 06:30, Stephen Boyd wrote:
> Quoting Jacky Huang (2023-03-15 00:28:59)
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1-divider.c b/drivers/clk/nuvoton/clk-ma35d1-divider.c
>> new file mode 100644
>> index 000000000000..5f4791531e47
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
>> @@ -0,0 +1,144 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/slab.h>
>> +#include <linux/io.h>
>> +#include <linux/err.h>
>> +#include <linux/spinlock.h>
>> +
>> +#include "clk-ma35d1.h"
>> +
>> +#define div_mask(width)                ((1 << (width)) - 1)
> This is clk_div_mask()

OK, I will rename div_mask() as clk_div_mask().

>
>> +
>> +struct ma35d1_adc_clk_divider {
>> +       struct clk_hw hw;
>> +       void __iomem *reg;
>> +       u8 shift;
>> +       u8 width;
>> +       u32 mask;
>> +       const struct clk_div_table *table;
>> +       spinlock_t *lock;
>> +};
>> +
>> +#define to_ma35d1_adc_clk_divider(_hw) \
>> +       container_of(_hw, struct ma35d1_adc_clk_divider, hw)
>> +
>> +static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw,
>> +                                              unsigned long parent_rate)
>> +{
>> +       unsigned int val;
>> +       struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
>> +
>> +       val = readl_relaxed(dclk->reg) >> dclk->shift;
>> +       val &= div_mask(dclk->width);
>> +       val += 1;
>> +       return divider_recalc_rate(hw, parent_rate, val, dclk->table,
>> +                                  CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
>> +}
>> +
>> +static long ma35d1_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
>> +                                    unsigned long *prate)
>> +{
>> +       struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
>> +
>> +       return divider_round_rate(hw, rate, prate, dclk->table,
>> +                                 dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
>> +}
>> +
>> +static int ma35d1_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
>> +                                 unsigned long parent_rate)
>> +{
>> +       int value;
>> +       unsigned long flags = 0;
>> +       u32 data;
>> +       struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
>> +
>> +       value = divider_get_val(rate, parent_rate, dclk->table,
>> +                               dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
>> +
>> +       if (dclk->lock)
>> +               spin_lock_irqsave(dclk->lock, flags);
>> +
>> +       data = readl_relaxed(dclk->reg);
>> +       data &= ~(div_mask(dclk->width) << dclk->shift);
>> +       data |= (value - 1) << dclk->shift;
>> +       data |= dclk->mask;
>> +
>> +       writel_relaxed(data, dclk->reg);
>> +
>> +       if (dclk->lock)
>> +               spin_unlock_irqrestore(dclk->lock, flags);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct clk_ops ma35d1_adc_clkdiv_ops = {
>> +       .recalc_rate = ma35d1_clkdiv_recalc_rate,
>> +       .round_rate = ma35d1_clkdiv_round_rate,
>> +       .set_rate = ma35d1_clkdiv_set_rate,
>> +};
>> +
>> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
>> +                                    const char *parent_name,
>> +                                    unsigned long flags, void __iomem *reg,
>> +                                    u8 shift, u8 width, u32 mask_bit)
>> +{
>> +       struct ma35d1_adc_clk_divider *div;
>> +       struct clk_init_data init;
>> +       struct clk_div_table *table;
>> +       u32 max_div, min_div;
>> +       struct clk_hw *hw;
>> +       int ret;
>> +       int i;
>> +
>> +       /* allocate the divider */
> Please remove useless comment.
>
>> +       div = kzalloc(sizeof(*div), GFP_KERNEL);
>> +       if (!div)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       /* Init the divider table */
> Please remove useless comment.

I will remove all useless comments.

>
>> +       max_div = div_mask(width) + 1;
>> +       min_div = 1;
>> +
>> +       table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
> Use devm_ allocations please.

OK, I will use devm_kzallloc() instead.

>> +       if (!table) {
>> +               kfree(div);
>> +               return ERR_PTR(-ENOMEM);
>> +       }
>> +
>> +       for (i = 0; i < max_div; i++) {
>> +               table[i].val = (min_div + i);
>> +               table[i].div = 2 * table[i].val;
>> +       }
>> +       table[max_div].val = 0;
>> +       table[max_div].div = 0;
>> +
>> +       init.name = name;
>> +       init.ops = &ma35d1_adc_clkdiv_ops;
>> +       init.flags |= flags;
>> +       init.parent_names = parent_name ? &parent_name : NULL;
>> +       init.num_parents = parent_name ? 1 : 0;
>> +
>> +       /* struct ma35d1_adc_clk_divider assignments */
> Please remove useless comment.
>
>> +       div->reg = reg;
>> +       div->shift = shift;
>> +       div->width = width;
>> +       div->mask = mask_bit ? BIT(mask_bit) : 0;
>> +       div->lock = &ma35d1_lock;
>> +       div->hw.init = &init;
>> +       div->table = table;
>> +
>> +       /* Register the clock */
> Please remove useless comment.
>
>> +       hw = &div->hw;
>> +       ret = clk_hw_register(NULL, hw);
> Use devm_clk_hw_register()

I will use devm_clk_hw_registers() instead.

>
>> +       if (ret) {
>> +               kfree(table);
>> +               kfree(div);
>> +               return ERR_PTR(ret);
>> +       }
>> +       return hw;
>> +}
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
>> new file mode 100644
>> index 000000000000..79e724b148fa
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
>> @@ -0,0 +1,534 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#include <linux/clk.h>
> Do you need to include this header?

I remove it and compile passed. It is not used here.
I will remove it in the next version.

>
>> +#include <linux/clk-provider.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/bitfield.h>
>> +
>> +#include "clk-ma35d1.h"
>> +
>> +#define to_ma35d1_clk_pll(clk) \
>> +       (container_of(clk, struct ma35d1_clk_pll, clk))
>> +
>> +#define PLL0CTL0_FBDIV_MSK     GENMASK(7, 0)
>> +#define PLL0CTL0_INDIV_MSK     GENMASK(11, 8)
>> +#define PLL0CTL0_OUTDIV_MSK    GENMASK(13, 12)
>> +#define PLL0CTL0_PD_MSK                BIT(16)
>> +#define PLL0CTL0_BP_MSK                BIT(17)
>> +#define PLLXCTL0_FBDIV_MSK     GENMASK(10, 0)
>> +#define PLLXCTL0_INDIV_MSK     GENMASK(17, 12)
>> +#define PLLXCTL0_MODE_MSK      GENMASK(19, 18)
>> +#define PLLXCTL0_SSRATE_MSK    GENMASK(30, 20)
>> +#define PLLXCTL1_PD_MSK                BIT(0)
>> +#define PLLXCTL1_BP_MSK                BIT(1)
>> +#define PLLXCTL1_OUTDIV_MSK    GENMASK(6, 4)
>> +#define PLLXCTL1_FRAC_MSK      GENMASK(31, 8)
>> +#define PLLXCTL2_SLOPE_MSK     GENMASK(23, 0)
>> +
>> +struct ma35d1_clk_pll {
>> +       struct clk_hw hw;
>> +       u8 type;
>> +       u8 mode;
>> +       unsigned long rate;
>> +       void __iomem *ctl0_base;
>> +       void __iomem *ctl1_base;
>> +       void __iomem *ctl2_base;
>> +       struct regmap *regmap;
>> +};
>> +
>> +struct vsipll_freq_conf_reg_tbl {
>> +       unsigned long freq;
>> +       u8 mode;
>> +       u32 ctl0_reg;
>> +       u32 ctl1_reg;
>> +       u32 ctl2_reg;
>> +};
>> +
>> +static const struct vsipll_freq_conf_reg_tbl ma35d1pll_freq[] = {
>> +       { 1000000000, VSIPLL_INTEGER_MODE, 0x307d, 0x10, 0 },
>> +       { 884736000, VSIPLL_FRACTIONAL_MODE, 0x41024, 0xdd2f1b11, 0 },
>> +       { 533000000, VSIPLL_SS_MODE, 0x12b8102c, 0x6aaaab20, 0x12317 },
>> +       { }
>> +};
>> +
>> +static void CLK_UnLockReg(struct ma35d1_clk_pll *pll)
> Please don't use a mix of upper and lower case function names.
> Everything should be lower case. Maybe the name should be
>
> 	ma35d1_clk_pll_unlock_reg()

Sure, we will review all the macros and API to have all lower case naming.

>> +{
>> +       int ret;
>> +
>> +       /* Unlock PLL registers */
>> +       do {
>> +               regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x59);
>> +               regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x16);
>> +               regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x88);
>> +               regmap_read(pll->regmap, REG_SYS_RLKTZNS, &ret);
>> +       } while (ret == 0);
>> +}
>> +
>> +static void CLK_LockReg(struct ma35d1_clk_pll *pll)
>> +{
> 	ma35d1_clk_pll_lock_reg()
>
>> +       /* Lock PLL registers */
> Remove these worthless comments.
>
>> +       regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x0);
>> +}
>> +
>> +/* SMIC PLL for CAPLL */
>> +unsigned long CLK_GetPLLFreq_SMICPLL(struct ma35d1_clk_pll *pll,
>> +                                    unsigned long PllSrcClk)
>> +{
>> +       u32 u32M, u32N, u32P, u32OutDiv;
> Variable names should not have the type in them. 'm', 'n', 'p', 'div'
> should suffice.

OK, we review all variables naming and modify them to linux coding style.

>> +       u32 val;
>> +       unsigned long u64PllClk;
>> +       u32 clk_div_table[] = { 1, 2, 4, 8};
>> +
>> +       val = __raw_readl(pll->ctl0_base);
> Why do you need to use __raw_readl()? Just use readl() here.

OK, I will modify all __raw_real() to raw_read().

>> +
>> +       u32N = FIELD_GET(PLL0CTL0_FBDIV_MSK, val);
>> +       u32M = FIELD_GET(PLL0CTL0_INDIV_MSK, val);
>> +       u32P = FIELD_GET(PLL0CTL0_OUTDIV_MSK, val);
>> +       u32OutDiv = clk_div_table[u32P];
>> +
>> +       if (val & PLL0CTL0_BP_MSK) {
>> +               u64PllClk = PllSrcClk;
>> +       } else {
>> +               u64PllClk = PllSrcClk * u32N;
>> +               do_div(u64PllClk, u32M * u32OutDiv);
>> +       }
> Add a newline here.
>
>> +       return u64PllClk;
>> +}
>> +
>> +/* VSI-PLL: INTEGER_MODE */
> I have no idea what this means.

The PLL has 3 operation modes, and integer mode in one of them.
We will add descriptions about PLL to comments.

>
>> +unsigned long CLK_CalPLLFreq_Mode0(unsigned long PllSrcClk,
>> +                                  unsigned long u64PllFreq, u32 *u32Reg)
> Again, don't put types into the variable name.
>
>> +{
>> +       u32 u32TmpM, u32TmpN, u32TmpP;
>> +       u32 u32RngMinN, u32RngMinM, u32RngMinP;
>> +       u32 u32RngMaxN, u32RngMaxM, u32RngMaxP;
>> +       u32 u32Tmp, u32Min, u32MinN, u32MinM, u32MinP;
>> +       unsigned long u64PllClk;
>> +       unsigned long u64Con1, u64Con2, u64Con3;
> My eyes! Seriously, kernel style is not this way. Did checkpatch.pl pass
> on this?

We will completely rewrite this function to make it readable.

>> +
>> +       u64PllClk = 0;
>> +       u32Min = (u32) -1;
>> +
>> +       if (!((u64PllFreq >= VSIPLL_FCLKO_MIN_FREQ) &&
>> +           (u64PllFreq <= VSIPLL_FCLKO_MAX_FREQ))) {
>> +               u32Reg[0] = ma35d1pll_freq[0].ctl0_reg;
>> +               u32Reg[1] = ma35d1pll_freq[0].ctl1_reg;
>> +               u64PllClk = ma35d1pll_freq[0].freq;
>> +               return u64PllClk;
>> +       }
>> +
>> +       u32RngMinM = 1UL;
>> +       u32RngMaxM = 63UL;
>> +       u32RngMinM = ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) > 1) ?
>> +                    (PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) : 1;
>> +       u32RngMaxM = ((PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) < u32RngMaxM) ?
>> +                    (PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) : u32RngMaxM;
>> +
>> +       for (u32TmpM = u32RngMinM; u32TmpM < (u32RngMaxM + 1); u32TmpM++) {
>> +               u64Con1 = PllSrcClk / u32TmpM;
>> +               u32RngMinN = 16UL;
>> +               u32RngMaxN = 2047UL;
>> +               u32RngMinN = ((VSIPLL_FCLK_MIN_FREQ / u64Con1) > u32RngMinN) ?
>> +                            (VSIPLL_FCLK_MIN_FREQ / u64Con1) : u32RngMinN;
>> +               u32RngMaxN = ((VSIPLL_FCLK_MAX_FREQ / u64Con1) < u32RngMaxN) ?
>> +                            (VSIPLL_FCLK_MAX_FREQ / u64Con1) : u32RngMaxN;
> Is this clamp()?
>
>> +
>> +               for (u32TmpN = u32RngMinN; u32TmpN < (u32RngMaxN + 1);
>> +                    u32TmpN++) {
>> +                       u64Con2 = u64Con1 * u32TmpN;
>> +                       u32RngMinP = 1UL;
>> +                       u32RngMaxP = 7UL;
>> +                       u32RngMinP = ((u64Con2 / VSIPLL_FCLKO_MAX_FREQ) > 1) ?
>> +                                     (u64Con2 / VSIPLL_FCLKO_MAX_FREQ) : 1;
> Is this clamp()?
>
>> +                       u32RngMaxP = ((u64Con2 / VSIPLL_FCLKO_MIN_FREQ) <
>> +                                     u32RngMaxP) ?
>> +                                     (u64Con2 / VSIPLL_FCLKO_MIN_FREQ) :
>> +                                     u32RngMaxP;
>> +                       for (u32TmpP = u32RngMinP; u32TmpP < (u32RngMaxP + 1);
>> +                            u32TmpP++) {
>> +                               u64Con3 = u64Con2 / u32TmpP;
>> +                               if (u64Con3 > u64PllFreq)
>> +                                       u32Tmp = u64Con3 - u64PllFreq;
>> +                               else
>> +                                       u32Tmp = u64PllFreq - u64Con3;
>> +
>> +                               if (u32Tmp < u32Min) {
>> +                                       u32Min = u32Tmp;
>> +                                       u32MinM = u32TmpM;
>> +                                       u32MinN = u32TmpN;
>> +                                       u32MinP = u32TmpP;
>> +
>> +                                       if (u32Min == 0UL) {
>> +                                               u32Reg[0] = (u32MinM << 12) |
>> +                                                           (u32MinN);
>> +                                               u32Reg[1] = (u32MinP << 4);
>> +                                               return ((PllSrcClk * u32MinN) /
>> +                                                       (u32MinP * u32MinM));
>> +                                       }
>> +                               }
>> +                       }
>> +               }
>> +       }
> It's too hard to read this code.

We will completely rewrite this function to make it readable.
And add formula descriptions to comments.

>> +
>> +       u32Reg[0] = (u32MinM << 12) | (u32MinN);
> FIELD_PREP?
>
>> +       u32Reg[1] = (u32MinP << 4);
> ditto?
>
>> +       u64PllClk = (PllSrcClk * u32MinN) / (u32MinP * u32MinM);
>> +       return u64PllClk;
>> +}
>> +
>> +/* VSI-PLL: FRACTIONAL_MODE */
>> +unsigned long CLK_CalPLLFreq_Mode1(unsigned long PllSrcClk,
>> +                                  unsigned long u64PllFreq, u32 *u32Reg)
>> +{
>> +       unsigned long u64X, u64N, u64M, u64P, u64tmp;
>> +       unsigned long u64PllClk, u64FCLKO;
>> +       u32 u32FRAC;
>> +
>> +       if (u64PllFreq > VSIPLL_FCLKO_MAX_FREQ) {
>> +               u32Reg[0] = ma35d1pll_freq[1].ctl0_reg;
>> +               u32Reg[1] = ma35d1pll_freq[1].ctl1_reg;
>> +               u64PllClk = ma35d1pll_freq[1].freq;
>> +               return u64PllClk;
>> +       }
>> +
>> +       if (u64PllFreq > (VSIPLL_FCLKO_MIN_FREQ/(100-1))) {
> Use a local variable for the right hand side of the comparison.

We will completely rewrite this function to make it readable.
And add formula descriptions to comments.


>> +               u64FCLKO = u64PllFreq * ((VSIPLL_FCLKO_MIN_FREQ / u64PllFreq) +
>> +                          ((VSIPLL_FCLKO_MIN_FREQ % u64PllFreq) ? 1 : 0));
> Is this DIV_ROUND_UP() or something like that?
>
>> +       } else {
>> +               pr_err("Failed to set rate %ld\n", u64PllFreq);
>> +               return 0;
>> +       }
>> +
>> +       u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
>> +              ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
>> +               ((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));
>> +
>> +       if ((PllSrcClk > (VSIPLL_FREFDIVM_MAX_FREQ * (64-1))) ||
>> +           (PllSrcClk < VSIPLL_FREFDIVM_MIN_FREQ1))
>> +               return 0;
>> +
> [...]
>> +               break;
>> +       }
>> +
>> +       return pllfreq;
>> +}
>> +
>> +static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>> +                                     unsigned long *prate)
>> +{
>> +       return rate;
> This needs to do actual math and figure out that some rate will not
> work and calculate what the rate will actually be if clk_set_rate() is
> called with 'rate'.

Got it. We will add clock rate check here.

>> +}
>> +
>> +static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
>> +{
>> +       struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +       u32 val = __raw_readl(pll->ctl1_base);
>> +
>> +       return (val & VSIPLLCTL1_PD_MSK) ? 0 : 1;
>> +}
>> +
>> +static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
>> +{
>> +       struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +       u32 val;
>> +
>> +       if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
>> +               pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
>> +               return -EACCES;
>> +       }
>> +
>> +       CLK_UnLockReg(pll);
>> +       val = __raw_readl(pll->ctl1_base);
>> +       val &= ~VSIPLLCTL1_PD_MSK;
>> +       __raw_writel(val, pll->ctl1_base);
>> +       CLK_LockReg(pll);
>> +       return 0;
>> +}
>> +
>> +static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
>> +{
>> +       struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +       u32 val;
>> +
>> +       if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
>> +               pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
>> +       } else {
>> +               val = __raw_readl(pll->ctl1_base);
>> +               val |= VSIPLLCTL1_PD_MSK;
>> +               __raw_writel(val, pll->ctl1_base);
>> +       }
>> +}
>> +
>> +static const struct clk_ops ma35d1_clk_pll_ops = {
>> +       .is_prepared = ma35d1_clk_pll_is_prepared,
>> +       .prepare = ma35d1_clk_pll_prepare,
>> +       .unprepare = ma35d1_clk_pll_unprepare,
>> +       .set_rate = ma35d1_clk_pll_set_rate,
>> +       .recalc_rate = ma35d1_clk_pll_recalc_rate,
>> +       .round_rate = ma35d1_clk_pll_round_rate,
>> +};
>> +
>> +struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type,
>> +                                 u8 u8mode, const char *name,
>> +                                 const char *parent,
>> +                                 unsigned long targetFreq,
>> +                                 void __iomem *base,
>> +                                 struct regmap *regmap)
>> +{
>> +       struct ma35d1_clk_pll *pll;
>> +       struct clk_hw *hw;
>> +       struct clk_init_data init;
>> +       int ret;
>> +
>> +       pll = kmalloc(sizeof(*pll), GFP_KERNEL);
>> +       if (!pll)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       pll->type = type;
>> +       pll->mode = u8mode;
>> +       pll->rate = targetFreq;
>> +       pll->ctl0_base = base + VSIPLL_CTL0;
>> +       pll->ctl1_base = base + VSIPLL_CTL1;
>> +       pll->ctl2_base = base + VSIPLL_CTL2;
>> +       pll->regmap = regmap;
>> +
>> +       init.name = name;
>> +       init.flags = 0;
>> +       init.parent_names = &parent;
>> +       init.num_parents = 1;
>> +       init.ops = &ma35d1_clk_pll_ops;
>> +       pll->hw.init = &init;
>> +       hw = &pll->hw;
>> +
>> +       ret = clk_hw_register(NULL, hw);
>> +       if (ret) {
>> +               pr_err("failed to register vsi-pll clock!!!\n");
>> +               kfree(pll);
>> +               return ERR_PTR(ret);
>> +       }
>> +       return hw;
>> +}
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
>> new file mode 100644
>> index 000000000000..ac8154458b81
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1.c
>> @@ -0,0 +1,970 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#include <linux/clk.h>
> I don't see any clk consumer usage. Please remove.
+#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
> I don't see any clkdev usage. Please remove.

Yes, clk.h and clkdev.h are not used here. I will remove them.


>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/spinlock.h>
>> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
>> +
>> +#include "clk-ma35d1.h"
>> +
>> +DEFINE_SPINLOCK(ma35d1_lock);
> Why not static?

ma35d1_lock is used in clk-ma35d1-divider.c only.
I will move it form clk-ma35d1.c to clk-ma35d1-divider.c as add static.

>
>> +
>> +static const char *const ca35clk_sel_clks[] = {
>> +       "hxt", "capll", "ddrpll", "dummy"
> Are these parent mappings? Please use 'struct clk_parent_data' instead
> if so.
>
>> +};
>> +
>> +static const char *const sysclk0_sel_clks[] = {
>> +       "epll_div2", "syspll"
>> +};
>> +
> [...]
>> +
>> +static struct clk_hw **hws;
>> +static struct clk_hw_onecell_data *ma35d1_hw_data;
> Any reason to make these global pointers vs local pointers during probe?

We will consider about using devm_of_clk_add_hw_provider() and remove 
these global pointers.

>
>> +
>> +static int ma35d1_clocks_probe(struct platform_device *pdev)
>> +{
>> +       int ret;
>> +       struct device *dev = &pdev->dev;
>> +       struct device_node *clk_node = dev->of_node;
>> +       void __iomem *clk_base;
>> +       struct regmap *regmap;
>> +       u32 pllmode[5] = { 0, 0, 0, 0, 0 };
>> +       u32 pllfreq[5] = { 0, 0, 0, 0, 0 };
>> +
>> +       dev_info(&pdev->dev, "Nuvoton MA35D1 Clock Driver\n");
> Drop this banner message please.

OK, I will remove it.

>
>> +       ma35d1_hw_data = devm_kzalloc(&pdev->dev, struct_size(ma35d1_hw_data,
>> +                                     hws, CLK_MAX_IDX), GFP_KERNEL);
>> +
>> +       if (WARN_ON(!ma35d1_hw_data))
>> +               return -ENOMEM;
>> +
>> +       ma35d1_hw_data->num = CLK_MAX_IDX;
>> +       hws = ma35d1_hw_data->hws;
>> +
>> +       clk_node = of_find_compatible_node(NULL, NULL, "nuvoton,ma35d1-clk");
>> +       clk_base = of_iomap(clk_node, 0);
> Use platform_device APIs as you have a platform device here ('pdev').

OK, I will fix it.

>> +       of_node_put(clk_node);
>> +       if (!clk_base) {
>> +               pr_err("%s: could not map region\n", __func__);
>> +               return -ENOMEM;
>> +       }
>> +       regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
>> +                                                "nuvoton,sys");
> Why is it a syscon?
>
>> +       if (IS_ERR(regmap))
>> +               pr_warn("%s: Unable to get syscon\n", __func__);
> How can we continue without the regmap?

I will make it return error here.

>
>> +
>> +       /* clock sources */
>> +       hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
> [...]
>> +       /* EADC */
>> +       hws[EADC_DIV] = ma35d1_clk_divider_table("eadc_div", "pclk2",
>> +                                                clk_base + REG_CLK_CLKDIV4,
>> +                                                0, 4, eadc_div_table);
>> +       hws[EADC_GATE] = ma35d1_clk_gate("eadc_gate", "eadc_div",
>> +                                        clk_base + REG_CLK_APBCLK2, 25);
>> +
>> +       ret = of_clk_add_hw_provider(clk_node, of_clk_hw_onecell_get,
> Use devm_ variant.

OK, I will fix it.

>
>> +                                    ma35d1_hw_data);
>> +       if (ret < 0) {
>> +               dev_err(dev, "failed to register hws for MA35D1\n");
>> +               iounmap(clk_base);
> Use devm mapping APIs to avoid unmapping on error path.

>> +       }
>> +       return ret;
>> +}
>> +
>> +static const struct of_device_id ma35d1_clk_of_match[] = {
>> +       { .compatible = "nuvoton,ma35d1-clk" },
>> +       { },
> Drop comma above so nothing can come after this.

I will remove the comma.

>
>> +};
>> +MODULE_DEVICE_TABLE(of, ma35d1_clk_of_match);
>> +
>> +static struct platform_driver ma35d1_clk_driver = {
>> +       .probe = ma35d1_clocks_probe,
>> +       .driver = {
>> +               .name = "ma35d1-clk",
>> +               .of_match_table = ma35d1_clk_of_match,
>> +       },
>> +};
>> +
>> +static int __init ma35d1_clocks_init(void)
>> +{
>> +       return platform_driver_register(&ma35d1_clk_driver);
>> +}
>> +
>> +postcore_initcall(ma35d1_clocks_init);
>> +
>> +MODULE_AUTHOR("Chi-Fang Li<cfli0@nuvoton.com>");
>> +MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
>> new file mode 100644
>> index 000000000000..faae5a17e425
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1.h
>> @@ -0,0 +1,198 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#ifndef __DRV_CLK_NUVOTON_MA35D1_H
>> +#define __DRV_CLK_NUVOTON_MA35D1_H
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clkdev.h>
> Are these includes used?

I remove them and compile passed, so I will will remove them in the next 
version.


>
>> +#include <linux/clk-provider.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/mfd/ma35d1-sys.h>
> These probably aren't needed to be included here. Just forward declare
> structs you need and include the headers in the C file.

Sure, we will move all #include in clk-ma35d1.h, and only include 
required .h files in C file.

>> +
> [...]
>> +
>> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev,
>> +                                   const char *name,
>> +                                   const char *parent_name,
>> +                                   unsigned long flags,
>> +                                   void __iomem *reg, u8 shift,
>> +                                   u8 width, u32 mask_bit);
>> +
>> +extern spinlock_t ma35d1_lock;
> Why?
I will remove all "extern" ma35d1_lock, as it will be static in 
clk-ma35d1-divider.c.


Best regards,

Jacky Huang



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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-16  7:35   ` Krzysztof Kozlowski
@ 2023-03-17  3:47     ` Jacky Huang
  2023-03-17  9:13       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  3:47 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof,

Thanks for your advice.


On 2023/3/16 下午 03:35, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Add documentation to describe nuvoton ma35d1 clock driver bindings.
> Subject: drop second/last, redundant "bindings". The "dt-bindings"
> prefix is already stating that these are bindings.

Got it, thanks.

>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   .../bindings/clock/nuvoton,ma35d1-clk.yaml    | 83 +++++++++++++++++++
>>   1 file changed, 83 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
>> new file mode 100644
>> index 000000000000..5c2dea071b38
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/nuvoton,ma35d1-clk.yaml
>> @@ -0,0 +1,83 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/clock/nuvoton,ma35d1-clk.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton MA35D1 Clock Controller Module Binding
>> +
>> +maintainers:
>> +  - Chi-Fang Li <cfli0@nuvoton.com>
>> +  - Jacky Huang <ychuang3@nuvoton.com>
>> +
>> +description: |
>> +  The MA35D1 clock controller generates clocks for the whole chip,
>> +  including system clocks and all peripheral clocks.
>> +
>> +  See also:
>> +    include/dt-bindings/clock/ma35d1-clk.h
>> +
>> +properties:
>> +  compatible:
>> +    items:
>> +      - const: nuvoton,ma35d1-clk
>> +      - const: syscon
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  "#clock-cells":
>> +    const: 1
>> +
>> +  clocks:
>> +    maxItems: 1
>> +
>> +  clock-names:
>> +    const: clk_hxt
> Drop clock-names. You do not need it for one clock.

I will remove it.

>
>
>> +
>> +  assigned-clocks:
>> +    maxItems: 5
>> +
>> +  assigned-clock-rates:
>> +    maxItems: 5
> Drop both properties, you do not need them in the binding.

I will drop them.

>
>> +
>> +  nuvoton,pll-mode:
>> +    description:
>> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
>> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
>> +      integer mode, 1 is for fractional mode, and 2 is for spread
>> +      spectrum mode.
>> +    $ref: /schemas/types.yaml#/definitions/uint32-array
>> +    maxItems: 5
>> +    items:
>> +      minimum: 0
>> +      maximum: 2
> Why exactly this is suitable for DT?

I will use strings instead.

>
>> +
>> +  nuvoton,sys:
>> +    description:
>> +      Phandle to the system management controller.
>> +    $ref: "/schemas/types.yaml#/definitions/phandle-array"
> Drop quotes.
>
> You need here constraints, look for existing examples.
>

I would like to modify this as:


   nuvoton,sys:
     description:
       Use to unlock and lock some clock controller registers. The lock
       control register is in system controller.
     $ref: /schemas/types.yaml#/definitions/phandle-array
     items:
       - items:
           - description: phandle to the system controller.


>
> Best regards,
> Krzysztof
>

Best regards,

Jacky Huang

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

* Re: [PATCH 10/15] dt-bindings: serial: Document ma35d1 uart controller bindings
  2023-03-16  7:40   ` Krzysztof Kozlowski
@ 2023-03-17  4:18     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  4:18 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof,


Thanks for your advice.


On 2023/3/16 下午 03:40, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Add documentation to describe nuvoton ma35d1 uart driver bindings.
> Subject: drop second/last, redundant "bindings". The "dt-bindings"
> prefix is already stating that these are bindings.

I will fix it.

>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   .../serial/nuvoton,ma35d1-serial.yaml         | 52 +++++++++++++++++++
>>   1 file changed, 52 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml b/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
>> new file mode 100644
>> index 000000000000..9daa2efd4734
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/serial/nuvoton,ma35d1-serial.yaml
>> @@ -0,0 +1,52 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/serial/nuvoton,ma35d1-serial.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton MA35D1 Universal Asynchronous Receiver/Transmitter (UART)
>> +
>> +maintainers:
>> +  - Min-Jen Chen <mjchen@nuvoton.com>
>> +  - Jacky Huang <ychuang3@nuvoton.com>
>> +
>> +allOf:
>> +  - $ref: "serial.yaml"
> Drop quotes. Use some recent bindings as your starting point, so we do
> not have to give comments for things which were already fixed.

Got it, I will remove the quotes.

>> +
>> +properties:
>> +  compatible:
>> +    const: nuvoton,ma35d1-uart
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  interrupts:
>> +    maxItems: 1
>> +
>> +  clocks:
>> +    maxItems: 1
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - interrupts
>> +  - clocks
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
>> +
>> +    aliases {
>> +        serial0 = &uart0;
>> +    };
> Drop aliases.

I will fix it.

>> +
>> +    uart0:serial@40700000 {
> Drop label

OK, I will remove the label.

>
> Best regards,
> Krzysztof
>

Best regards,

Jacky Huang


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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-16 14:05 ` Arnd Bergmann
@ 2023-03-17  6:30   ` Jacky Huang
  2023-03-17 13:21     ` Arnd Bergmann
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  6:30 UTC (permalink / raw)
  To: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang



On 2023/3/16 下午 10:05, Arnd Bergmann wrote:
> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> This patchset adds initial support for the Nuvoton ma35d1 SoC, including
>> initial device tree, clock driver, reset driver, and serial driver.
>>
>> This patchset cover letter is based from the initial support for Nuvoton
>> ma35d1 to keep tracking the version history.
>>
>> This patchset had been applied to Linux kernel 6.3-rc2 and tested on the
>> Nuvoton ma35d1 SOM evaluation board.
>>
>> (ma35d1 information:
>> https://www.nuvoton.com/products/microprocessors/arm-cortex-a35-mpus/)
>> MA35D1 porting on linux-5.10.y can be found at:
>> https://github.com/OpenNuvoton/MPU-Family
> Hi Jacky,
>
> Thanks a lot for your submission. I saw this presented at
> EmbeddedWorld yesterday and asked about mainline Linux
> support, but did not expect to see the patches this soon ;-)
>
> The easiest process for getting the series merged is to
> have me add it the entire series to the SoC tree after the
> individual drivers have been reviewed by the respective
> subsystem maintainers that are already on Cc here. When
> the review is complete, you can add soc@kernel.org to Cc,
> so they show up in patchwork, or alternatively send a pull
> request for a git tree to that address. Until then, you
> can add my own email address to Cc so I can follow the
> reviews.
>
> After the initial merge, the normal method for additional
> device drivers is to have them sent for inclusion to the
> subsystem maintainers. The soc tree and soc@kernel.org address
> is then only used for changes in arch/arm64, i.e. updates
> to the dts files, Kconfig, defconfig and MAINTAINERS,
> as well as the drivers/soc and drivers/firmware directories,
> if you have anything in there.
>
> If you have any additional questions about the process,
> feel free to also ask me.
>
>       Arnd

Hi Anrd,

Thank you very much for your kind help. You explained it so well,
I have understood the process. We got a lot of suggestions for this
patchset, and there are a lot of issues to fix. When most of the
problems get solved and acknowledged by the reviewers, I will
add you and soc@kernel.org to Cc.


Best regards,

Jacky Huang


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

* Re: [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager
  2023-03-16 13:30   ` Ilpo Järvinen
@ 2023-03-17  6:51     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  6:51 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, devicetree, linux-clk, linux-kernel,
	linux-serial, schung, Jacky Huang


Hi Ilpo,


On 2023/3/16 下午 09:30, Ilpo Järvinen wrote:
> On Wed, 15 Mar 2023, Jacky Huang wrote:
>
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> The system manager is a set of registers used for power control,
>> multi-function pin control, USB phy control, IP reset, and other
>> miscellaneous controls. It also contains some registers that
>> provide SoC information and status.
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   include/linux/mfd/ma35d1-sys.h | 95 ++++++++++++++++++++++++++++++++++
>>   1 file changed, 95 insertions(+)
>>   create mode 100644 include/linux/mfd/ma35d1-sys.h
>>
>> diff --git a/include/linux/mfd/ma35d1-sys.h b/include/linux/mfd/ma35d1-sys.h
>> new file mode 100644
>> index 000000000000..dcd85231125d
>> --- /dev/null
>> +++ b/include/linux/mfd/ma35d1-sys.h
>> @@ -0,0 +1,95 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technologies.
>> + * Author: Chi-Fen Li <cfli0@nuvoton.com>
>> + *
>> + * System management control registers of MA35D1 SoC
>> + */
>> +#ifndef __LINUX_MFD_MA35D1_SYS_H
>> +#define __LINUX_MFD_MA35D1_SYS_H
>> +
>> +#define REG_SYS_PDID		(0x000) /* Product and Device Identifier */
>> +#define REG_SYS_PWRONOTP	(0x004) /* Power-on Setting OTP Source */
>> +#define REG_SYS_PWRONPIN	(0x008) /* Power-on Setting Pin Source */
>> +#define REG_SYS_RSTSTS		(0x010) /* Reset Source Active Status */
>> +#define REG_SYS_MISCRFCR	(0x014) /* Miscellaneous Reset Function */
>> +#define REG_SYS_RSTDEBCTL	(0x018) /* Reset Pin De-bounce Control */
>> +#define REG_SYS_LVRDCR		(0x01C) /* Low Voltage Reset & Detect */
>> +#define REG_SYS_IPRST0		(0x020) /* Reset Control Register 0 */
>> +#define REG_SYS_IPRST1		(0x024) /* Reset Control Register 1 */
>> +#define REG_SYS_IPRST2		(0x028) /* Reset Control Register 2 */
>> +#define REG_SYS_IPRST3		(0x02C) /* Reset Control Register 3 */
>> +#define REG_SYS_PMUCR		(0x030) /* Power Management Unit Control */
>> +#define REG_SYS_DDRCQCSR	(0x034) /* DDR Q Channel Control and Status */
>> +#define REG_SYS_PMUIEN		(0x038) /* PMU Interrupt Enable */
>> +#define REG_SYS_PMUSTS		(0x03C) /* PMU Status */
>> +#define REG_SYS_CA35WRBADR1	(0x040) /* A35 Core 1 Warm-boot Address */
>> +#define REG_SYS_CA35WRBPAR1	(0x044) /* A35 Core 1 Warm-boot Parameter */
>> +#define REG_SYS_CA35WRBADR2	(0x048) /* A35 Core 2 Warm-boot Address */
>> +#define REG_SYS_CA35WRBPAR2	(0x04C) /* A35 Core 2 Warm-boot Parameter */
>> +#define REG_SYS_USBPMISCR	(0x060) /* USB PHY Miscellaneous Control */
>> +#define REG_SYS_USBP0PCR	(0x064) /* USB Port 0 PHY Control */
>> +#define REG_SYS_USBP1PCR	(0x068) /* USB Port 1 PHY Control */
>> +#define REG_SYS_MISCFCR0	(0x070) /* Miscellaneous Function Control 0 */
>> +#define REG_SYS_MISCFCR1	(0x074) /* Miscellaneous Function Control 1 */
>> +#define REG_SYS_MISCIER		(0x078) /* Miscellaneous Interrupt Enable */
>> +#define REG_SYS_MISCISR		(0x07C) /* Miscellaneous Interrupt Status */
>> +#define REG_SYS_GPA_MFPL	(0x080) /* GPIOA Multi-Function Control LSB */
>> +#define REG_SYS_GPA_MFPH	(0x084) /* GPIOA Multi-Function Control MSB */
>> +#define REG_SYS_GPB_MFPL	(0x088) /* GPIOB Multi-Function Control LSB */
>> +#define REG_SYS_GPB_MFPH	(0x08C) /* GPIOB Multi-Function Control MSB */
>> +#define REG_SYS_GPC_MFPL	(0x090) /* GPIOC Multi-Function Control LSB */
>> +#define REG_SYS_GPC_MFPH	(0x094) /* GPIOC Multi-Function Control MSB */
>> +#define REG_SYS_GPD_MFPL	(0x098) /* GPIOD Multi-Function Control LSB */
>> +#define REG_SYS_GPD_MFPH	(0x09C) /* GPIOD Multi-Function Control MSB */
>> +#define REG_SYS_GPE_MFPL	(0x0A0) /* GPIOE Multi-Function Control LSB */
>> +#define REG_SYS_GPE_MFPH	(0x0A4) /* GPIOE Multi-Function Control MSB */
>> +#define REG_SYS_GPF_MFPL	(0x0A8) /* GPIOF Multi-Function Control LSB */
>> +#define REG_SYS_GPF_MFPH	(0x0AC) /* GPIOF Multi-Function Control MSB */
>> +#define REG_SYS_GPG_MFPL	(0x0B0) /* GPIOG Multi-Function Control LSB */
>> +#define REG_SYS_GPG_MFPH	(0x0B4) /* GPIOG Multi-Function Control MSB */
>> +#define REG_SYS_GPH_MFPL	(0x0B8) /* GPIOH Multi-Function Control LSB */
>> +#define REG_SYS_GPH_MFPH	(0x0BC) /* GPIOH Multi-Function Control MSB */
>> +#define REG_SYS_GPI_MFPL	(0x0C0) /* GPIOI Multi-Function Control LSB */
>> +#define REG_SYS_GPI_MFPH	(0x0C4) /* GPIOI Multi-Function Control MSB */
>> +#define REG_SYS_GPJ_MFPL	(0x0C8) /* GPIOJ Multi-Function Control LSB */
>> +#define REG_SYS_GPJ_MFPH	(0x0CC) /* GPIOJ Multi-Function Control MSB */
>> +#define REG_SYS_GPK_MFPL	(0x0D0) /* GPIOK Multi-Function Control LSB */
>> +#define REG_SYS_GPK_MFPH	(0x0D4) /* GPIOK Multi-Function Control MSB */
>> +#define REG_SYS_GPL_MFPL	(0x0D8) /* GPIOL Multi-Function Control LSB */
>> +#define REG_SYS_GPL_MFPH	(0x0DC) /* GPIOL Multi-Function Control MSB */
>> +#define REG_SYS_GPM_MFPL	(0x0E0) /* GPIOM Multi-Function Control LSB */
>> +#define REG_SYS_GPM_MFPH	(0x0E4) /* GPIOM Multi-Function Control MSB */
>> +#define REG_SYS_GPN_MFPL	(0x0E8) /* GPION Multi-Function Control LSB */
>> +#define REG_SYS_GPN_MFPH	(0x0EC) /* GPION Multi-Function Control MSB */
>> +#define REG_SYS_HIRCFTRIM	(0x100) /* HIRC Frequency Trim Value */
>> +#define REG_SYS_TSENSRFCR	(0x104) /* Temperature Sensor Control */
>> +#define REG_SYS_GMAC0MISCR	(0x108) /* GMAC 0 Miscellaneous Control */
>> +#define REG_SYS_GMAC1MISCR	(0x10C) /* GMAC 1 Miscellaneous Control */
>> +#define REG_SYS_MACAD0LSR	(0x110) /* MAC Address 0 LSW */
>> +#define REG_SYS_MACAD0HSR	(0x114) /* MAC Address 0 HSW */
>> +#define REG_SYS_MACAD1LSR	(0x118) /* MAC Address 1 LSW */
>> +#define REG_SYS_MACAD1HSR	(0x11C) /* MAC Address 1 HSW */
>> +#define REG_SYS_CSDBGCTL	(0x120) /* CoreSight Debug Control */
>> +#define REG_SYS_GPAB_MFOS	(0x140) /* GPIOA/B Output Mode Select */
>> +#define REG_SYS_GPCD_MFOS	(0x144) /* GPIOC/D Output Mode Select */
>> +#define REG_SYS_GPEF_MFOS	(0x148) /* GPIOE/F Output Mode Select */
>> +#define REG_SYS_GPGH_MFOS	(0x14C) /* GPIOG/H Output Mode Select */
>> +#define REG_SYS_GPIJ_MFOS	(0x150) /* GPIOI/J Output Mode Select */
>> +#define REG_SYS_GPKL_MFOS	(0x154) /* GPIOK/L Output Mode Select */
>> +#define REG_SYS_GPMN_MFOS	(0x158) /* GPIOM/N Output Mode Select */
>> +#define REG_SYS_UID0		(0x180) /* Unique Identifier Word 0 */
>> +#define REG_SYS_UID1		(0x184) /* Unique Identifier Word 1 */
>> +#define REG_SYS_UID2		(0x188) /* Unique Identifier Word 2 */
>> +#define REG_SYS_UCID0		(0x190) /* Unique Customer Identifier 0 */
>> +#define REG_SYS_UCID1		(0x194) /* Unique Customer Identifier 1 */
>> +#define REG_SYS_UCID2		(0x198) /* Unique Customer Identifier 2 */
>> +#define REG_SYS_RLKTZS		(0x1A0) /* TZS Register Lock Control */
>> +#define REG_SYS_RLKTZNS		(0x1A4) /* TZNS Register Lock Control */
>> +#define REG_SYS_RLKSUBM		(0x1A8) /* SubM Register Lock Control */
>> +#define REG_SYS_DPLPASWD	(0x1B0) /* Deployed Password */
> Remove the extra set of parenthesis from all those above. Hex numbers are
> easier to read with lowercased letters so please convert them all to
> lowercase.
>

Got it. I will remove parenthesis and use lower case hex instead.

Best regards,

Jacky Huang



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

* Re: [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support
  2023-03-16  7:51   ` Krzysztof Kozlowski
@ 2023-03-17  7:13     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  7:13 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Hi Krzysztof,


On 2023/3/16 下午 03:51, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:29, Jacky Huang wrote:
>> From: Jacky Huang<ychuang3@nuvoton.com>
>>
>> This driver supports individual IP reset for ma35d1. The reset
>> control registers is a subset of system control registers.
>>
>> Signed-off-by: Jacky Huang<ychuang3@nuvoton.com>
>> ---
>>   drivers/reset/Kconfig        |   6 ++
>>   drivers/reset/Makefile       |   1 +
>>   drivers/reset/reset-ma35d1.c | 152 +++++++++++++++++++++++++++++++++++
>>   3 files changed, 159 insertions(+)
>>   create mode 100644 drivers/reset/reset-ma35d1.c
>>
>> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
>> index 2a52c990d4fe..47671060d259 100644
>> --- a/drivers/reset/Kconfig
>> +++ b/drivers/reset/Kconfig
>> @@ -143,6 +143,12 @@ config RESET_NPCM
>>   	  This enables the reset controller driver for Nuvoton NPCM
>>   	  BMC SoCs.
>>   
>> +config RESET_NUVOTON_MA35D1
>> +	bool "Nuvton MA35D1 Reset Driver"
>> +	default ARCH_NUVOTON
> || COMPILE_TEST

I will add this config. Thank you.

>> +	help
>> +	  This enables the reset controller driver for Nuvoton MA35D1 SoC.
>> +
> Best regards,
> Krzysztof
>

Best regards,

Jacky Huang


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

* Re: [PATCH 02/15] arm64: defconfig: Add Nuvoton MA35 family support
  2023-03-16 14:23   ` Arnd Bergmann
@ 2023-03-17  9:05     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  9:05 UTC (permalink / raw)
  To: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Hi Anrd,

Thanks for your advice.

On 2023/3/16 下午 10:23, Arnd Bergmann wrote:
> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Enable basic drivers for ma35d1 booting up support: architecture,
>> device tree, clock, reset, and uart.
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> The description does not seem to match the actual contents,
> which only enable the platform but none of the drivers.

I would like to rewrite it as:

arm64: defconfig: Add support for Nuvoton MA35 family SoCs

This adds support for the Nuvoton MA35 family SoCs which
are based on the Cortex-A35 Armv8-A 64-bit architecture.


> It's ok generally to enable options in the defconfig file
> before you add the drivers, but if it's all part of a patch
> series, I would probable more this bit to the end.
>
>       Arnd

Best regards,

Jacky Huang


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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-17  3:47     ` Jacky Huang
@ 2023-03-17  9:13       ` Krzysztof Kozlowski
  2023-03-17  9:52         ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-17  9:13 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 17/03/2023 04:47, Jacky Huang wrote:
> 
>>
>>> +
>>> +  nuvoton,pll-mode:
>>> +    description:
>>> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
>>> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
>>> +      integer mode, 1 is for fractional mode, and 2 is for spread
>>> +      spectrum mode.
>>> +    $ref: /schemas/types.yaml#/definitions/uint32-array
>>> +    maxItems: 5
>>> +    items:
>>> +      minimum: 0
>>> +      maximum: 2
>> Why exactly this is suitable for DT?
> 
> I will use strings instead.

I have doubts why PLL mode is a property of DT. Is this a board-specific
property?

> 
>>
>>> +
>>> +  nuvoton,sys:
>>> +    description:
>>> +      Phandle to the system management controller.
>>> +    $ref: "/schemas/types.yaml#/definitions/phandle-array"
>> Drop quotes.
>>
>> You need here constraints, look for existing examples.
>>
> 
> I would like to modify this as:
> 
> 
>    nuvoton,sys:
>      description:
>        Use to unlock and lock some clock controller registers. The lock
>        control register is in system controller.
>      $ref: /schemas/types.yaml#/definitions/phandle-array
>      items:
>        - items:
>            - description: phandle to the system controller.

In such case you do not have array. Just make it phandle and drop the items.



Best regards,
Krzysztof


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

* Re: [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager
  2023-03-16 14:44   ` Arnd Bergmann
@ 2023-03-17  9:28     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  9:28 UTC (permalink / raw)
  To: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Arnd,

Thanks for your advice.


On 2023/3/16 下午 10:44, Arnd Bergmann wrote:
> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> The system manager is a set of registers used for power control,
>> multi-function pin control, USB phy control, IP reset, and other
>> miscellaneous controls. It also contains some registers that
>> provide SoC information and status.
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   include/linux/mfd/ma35d1-sys.h | 95 ++++++++++++++++++++++++++++++++++
>>   1 file changed, 95 insertions(+)
>>   create mode 100644 include/linux/mfd/ma35d1-sys.h
>>
>> diff --git a/include/linux/mfd/ma35d1-sys.h b/include/linux/mfd/ma35d1-sys.h
>> new file mode 100644
>> index 000000000000..dcd85231125d
>> --- /dev/null
>> +++ b/include/linux/mfd/ma35d1-sys.h
>> +
>> +#define REG_SYS_PDID		(0x000) /* Product and Device Identifier */
>> +#define REG_SYS_PWRONOTP	(0x004) /* Power-on Setting OTP Source */
>> +#define REG_SYS_PWRONPIN	(0x008) /* Power-on Setting Pin Source */
>> +#define REG_SYS_RSTSTS		(0x010) /* Reset Source Active Status */
> ...
>
> It is a bit odd to have a header file in include/linux/mfd/
> but only have the register numbers in there, and not an
> actual drivers/mfd/ driver to go along with them.
>
> I think what we often do is to just list the individual register
> numbers in the drivers that need them and not have the central
> header at all. On the other hand, I can see it's useful to
> have this documented in one place, and we clearly don't want
> to add a driver if none is needed.
>
> Maybe Lee has a suggestion for how he'd like to handle this.

Agree with this.
We will add #define of individual system control registers to the drivers
that have to use them.
So, I will remove this patch from the patchset in the next version.

>> +void ma35d1_reg_lock(void);
>> +void ma35d1_reg_unlock(void);
> These look like they were left over from an earlier version
> of the code. Since you use the regmap framework, I think this
> will take care of the locking for you.
>
>         Arnd

Best regards,

Jacky Huang



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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-17  9:13       ` Krzysztof Kozlowski
@ 2023-03-17  9:52         ` Jacky Huang
  2023-03-17 16:03           ` Krzysztof Kozlowski
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-17  9:52 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof,

Thanks for your advice.

On 2023/3/17 下午 05:13, Krzysztof Kozlowski wrote:
> On 17/03/2023 04:47, Jacky Huang wrote:
>>>> +
>>>> +  nuvoton,pll-mode:
>>>> +    description:
>>>> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
>>>> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
>>>> +      integer mode, 1 is for fractional mode, and 2 is for spread
>>>> +      spectrum mode.
>>>> +    $ref: /schemas/types.yaml#/definitions/uint32-array
>>>> +    maxItems: 5
>>>> +    items:
>>>> +      minimum: 0
>>>> +      maximum: 2
>>> Why exactly this is suitable for DT?
>> I will use strings instead.
> I have doubts why PLL mode is a property of DT. Is this a board-specific
> property?

CA-PLL has mode 0 only.
DDRPLL, APLL, EPLL, and VPLL have the same PLL design that supports
integer mode, fractional mode, and spread spctrum mode. The PLL mode
is controlled by clock controller register. I think it's not board-specific.

>>>> +
>>>> +  nuvoton,sys:
>>>> +    description:
>>>> +      Phandle to the system management controller.
>>>> +    $ref: "/schemas/types.yaml#/definitions/phandle-array"
>>> Drop quotes.
>>>
>>> You need here constraints, look for existing examples.
>>>
>> I would like to modify this as:
>>
>>
>>     nuvoton,sys:
>>       description:
>>         Use to unlock and lock some clock controller registers. The lock
>>         control register is in system controller.
>>       $ref: /schemas/types.yaml#/definitions/phandle-array
>>       items:
>>         - items:
>>             - description: phandle to the system controller.
> In such case you do not have array. Just make it phandle and drop the items.
>

Thank you.
So, I will rewrite it as

   nuvoton,sys:
     description:
       Use to unlock and lock some clock controller registers. The lock
       control register is in system controller.
     $ref: /schemas/types.yaml#/definitions/phandle


>
> Best regards,
> Krzysztof
>

Best regards,

Jacky Huang


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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-17  6:30   ` Jacky Huang
@ 2023-03-17 13:21     ` Arnd Bergmann
  2023-03-17 16:06       ` Krzysztof Kozlowski
  2023-03-18  3:00       ` Jacky Huang
  0 siblings, 2 replies; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-17 13:21 UTC (permalink / raw)
  To: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Fri, Mar 17, 2023, at 07:30, Jacky Huang wrote:
> On 2023/3/16 下午 10:05, Arnd Bergmann wrote:
>
> Thank you very much for your kind help. You explained it so well,
> I have understood the process. We got a lot of suggestions for this
> patchset, and there are a lot of issues to fix. When most of the
> problems get solved and acknowledged by the reviewers, I will
> add you and soc@kernel.org to Cc.

Ok, sounds good. Two more clarifications from me:

1. I expect you will have to go through two or three submissions
that get more feedback before everyone is happy. Please include
my arnd@arndb.de on Cc on all the submissions, but only include
the soc@kernel.org address when all patches have an Acked-by
or Reviewed-by from the respective subsystem maintainer.

2. I think the series looks very good at this point, and most of the
feedback was about minor details, so I am optimistic that we can
actually merge it soon.

I only now saw that you had already submitted this several times
at the beginning of last year, and this is technically 'v5'
of the series, and it would make sense to add 'v6' to the subject
next time and link back to the previous [1] and this[2] submission
on lore.kernel.org.


    Arnd

[1] https://lore.kernel.org/all/20220510032558.10304-1-ychuang3@nuvoton.com/
[2] https://lore.kernel.org/all/20230315072902.9298-1-ychuang570808@gmail.com/

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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-17  9:52         ` Jacky Huang
@ 2023-03-17 16:03           ` Krzysztof Kozlowski
  2023-03-18  2:11             ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-17 16:03 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 17/03/2023 10:52, Jacky Huang wrote:
> Dear Krzysztof,
> 
> Thanks for your advice.
> 
> On 2023/3/17 下午 05:13, Krzysztof Kozlowski wrote:
>> On 17/03/2023 04:47, Jacky Huang wrote:
>>>>> +
>>>>> +  nuvoton,pll-mode:
>>>>> +    description:
>>>>> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
>>>>> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
>>>>> +      integer mode, 1 is for fractional mode, and 2 is for spread
>>>>> +      spectrum mode.
>>>>> +    $ref: /schemas/types.yaml#/definitions/uint32-array
>>>>> +    maxItems: 5
>>>>> +    items:
>>>>> +      minimum: 0
>>>>> +      maximum: 2
>>>> Why exactly this is suitable for DT?
>>> I will use strings instead.
>> I have doubts why PLL mode is a property of DT. Is this a board-specific
>> property?
> 
> CA-PLL has mode 0 only.
> DDRPLL, APLL, EPLL, and VPLL have the same PLL design that supports
> integer mode, fractional mode, and spread spctrum mode. The PLL mode
> is controlled by clock controller register. I think it's not board-specific.

You described the feature but that does not answer why this is suitable
in DT. If this is not board-specific, then it is implied by compatible,
right? Or it does not have to be in DT at all.


Best regards,
Krzysztof


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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-17 13:21     ` Arnd Bergmann
@ 2023-03-17 16:06       ` Krzysztof Kozlowski
  2023-03-18  3:07         ` Jacky Huang
  2023-03-18  3:00       ` Jacky Huang
  1 sibling, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-17 16:06 UTC (permalink / raw)
  To: Arnd Bergmann, Jacky Huang, Rob Herring, krzysztof.kozlowski+dt,
	Lee Jones, Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 17/03/2023 14:21, Arnd Bergmann wrote:
> I only now saw that you had already submitted this several times
> at the beginning of last year, and this is technically 'v5'
> of the series, and it would make sense to add 'v6' to the subject
> next time and link back to the previous [1] and this[2] submission
> on lore.kernel.org.

... and address previous feedback. Or at least make it clear in
changelog that you addressed it, so our review was not ignored.

Best regards,
Krzysztof


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

* Re: [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform
  2023-03-16 14:32     ` Arnd Bergmann
@ 2023-03-18  1:26       ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-18  1:26 UTC (permalink / raw)
  To: Arnd Bergmann, Krzysztof Kozlowski, Rob Herring,
	krzysztof.kozlowski+dt, Lee Jones, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Arnd and  Krzysztof,


Please allow me to answer the question of Krzysztof in this mail.


On 2023/3/16 下午 10:32, Arnd Bergmann wrote:
> On Thu, Mar 16, 2023, at 08:33, Krzysztof Kozlowski wrote:
>> On 15/03/2023 08:28, Jacky Huang wrote:
>>> From: Jacky Huang <ychuang3@nuvoton.com>
>>>
>>> Add binding for ARMv8 based Nuvotn SoCs and platform boards.
>>> Add initial bindings for ma35d1 series development boards.
>>>
>>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>>> ---
>>>   .../devicetree/bindings/arm/nuvoton.yaml      | 30 +++++++++++++++++++
>> And what is npcm for? Why it was made an directory?
>>
>> All these should be just one Nuvoton.


Thank you for your suggestion, then in the next version I will submit to 
rename directory npcm to nuvoton.

And rename this file nuvoton.yaml to nuvoton,ma35d1.yaml, and put in the 
nuvoton directory.


> npcm is an unrelated product line, so I think it would be best
> to rename the npcm directory to nuvoton and move the new
> file in there, though I'm not sure about the name or what the
> other chips are called.
>
> My impression is that this one is more closely related to
> the older Arm9 nuc900/w90x900/n9 chips that we dropped from
> the kernel a while ago, while the npcm family has a different
> origin.
>
>      Arnd

npcm focuses on the BMC field, and ma35 is the successor SoC of 
nuc970/nuc980,

which is mostly used in industrial control, consumer, network 
applications and other fields.

The two teams are located in different countries and regions, and there 
is little

communication and resource sharing with each other.


Best regards,

Jacky Huang



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

* Re: [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings
  2023-03-17 16:03           ` Krzysztof Kozlowski
@ 2023-03-18  2:11             ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-18  2:11 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof,


Thanks for your advice.


On 2023/3/18 上午 12:03, Krzysztof Kozlowski wrote:
> On 17/03/2023 10:52, Jacky Huang wrote:
>> Dear Krzysztof,
>>
>> Thanks for your advice.
>>
>> On 2023/3/17 下午 05:13, Krzysztof Kozlowski wrote:
>>> On 17/03/2023 04:47, Jacky Huang wrote:
>>>>>> +
>>>>>> +  nuvoton,pll-mode:
>>>>>> +    description:
>>>>>> +      A list of PLL operation mode corresponding to CAPLL, DDRPLL, APLL,
>>>>>> +      EPLL, and VPLL in sequential. The operation mode value 0 is for
>>>>>> +      integer mode, 1 is for fractional mode, and 2 is for spread
>>>>>> +      spectrum mode.
>>>>>> +    $ref: /schemas/types.yaml#/definitions/uint32-array
>>>>>> +    maxItems: 5
>>>>>> +    items:
>>>>>> +      minimum: 0
>>>>>> +      maximum: 2
>>>>> Why exactly this is suitable for DT?
>>>> I will use strings instead.
>>> I have doubts why PLL mode is a property of DT. Is this a board-specific
>>> property?
>> CA-PLL has mode 0 only.
>> DDRPLL, APLL, EPLL, and VPLL have the same PLL design that supports
>> integer mode, fractional mode, and spread spctrum mode. The PLL mode
>> is controlled by clock controller register. I think it's not board-specific.
> You described the feature but that does not answer why this is suitable
> in DT. If this is not board-specific, then it is implied by compatible,
> right? Or it does not have to be in DT at all.
>
>
> Best regards,
> Krzysztof


I got it now. Yes, at least DDR PLL and VPLL (video pixel clock) can be 
different on

different boards. You're right, it should be board specific. Thank you.

In the next version, I will move PLL property to board dts.


Best regards,

Jacky Huang


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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-17 13:21     ` Arnd Bergmann
  2023-03-17 16:06       ` Krzysztof Kozlowski
@ 2023-03-18  3:00       ` Jacky Huang
  1 sibling, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-18  3:00 UTC (permalink / raw)
  To: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Arnd,


Thanks for you advice.


On 2023/3/17 下午 09:21, Arnd Bergmann wrote:
> On Fri, Mar 17, 2023, at 07:30, Jacky Huang wrote:
>> On 2023/3/16 下午 10:05, Arnd Bergmann wrote:
>>
>> Thank you very much for your kind help. You explained it so well,
>> I have understood the process. We got a lot of suggestions for this
>> patchset, and there are a lot of issues to fix. When most of the
>> problems get solved and acknowledged by the reviewers, I will
>> add you and soc@kernel.org to Cc.
> Ok, sounds good. Two more clarifications from me:
>
> 1. I expect you will have to go through two or three submissions
> that get more feedback before everyone is happy. Please include
> my arnd@arndb.de on Cc on all the submissions, but only include
> the soc@kernel.org address when all patches have an Acked-by
> or Reviewed-by from the respective subsystem maintainer.


Sure, I will add you on Cc. Thank you.


> 2. I think the series looks very good at this point, and most of the
> feedback was about minor details, so I am optimistic that we can
> actually merge it soon.
>
> I only now saw that you had already submitted this several times
> at the beginning of last year, and this is technically 'v5'
> of the series, and it would make sense to add 'v6' to the subject
> next time and link back to the previous [1] and this[2] submission
> on lore.kernel.org.
>
>
>      Arnd
>
> [1] https://lore.kernel.org/all/20220510032558.10304-1-ychuang3@nuvoton.com/
> [2] https://lore.kernel.org/all/20230315072902.9298-1-ychuang570808@gmail.com/


In fact, I was thinking about this before submitting the patch. Looks 
like this is causing

confusion for reviewers, so thanks for the suggestion. I will submit the 
next version as v6,

and add the history of v1 ~ v4, and this version will be regarded as v5.


Best regards,

Jacky Huang


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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-17 16:06       ` Krzysztof Kozlowski
@ 2023-03-18  3:07         ` Jacky Huang
  2023-03-18  9:07           ` Arnd Bergmann
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-18  3:07 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Arnd Bergmann, Rob Herring,
	krzysztof.kozlowski+dt, Lee Jones, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang


On 2023/3/18 上午 12:06, Krzysztof Kozlowski wrote:
> On 17/03/2023 14:21, Arnd Bergmann wrote:
>> I only now saw that you had already submitted this several times
>> at the beginning of last year, and this is technically 'v5'
>> of the series, and it would make sense to add 'v6' to the subject
>> next time and link back to the previous [1] and this[2] submission
>> on lore.kernel.org.
> ... and address previous feedback. Or at least make it clear in
> changelog that you addressed it, so our review was not ignored.
>
> Best regards,
> Krzysztof


Dear Krzysztof,


Thank you.

Of course, I will add back the changelog.

And, I have a question. If subsequent modifications made to a patch, 
should the

"Reviewed-by" still be valid? Can we keep it?

Best regards,

Jacky Huang


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

* Re: [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset controller bindings
  2023-03-16  7:39     ` Krzysztof Kozlowski
@ 2023-03-18  4:30       ` Jacky Huang
  2023-03-19 11:05         ` Krzysztof Kozlowski
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-18  4:30 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof,


Thanks for your advice.


On 2023/3/16 下午 03:39, Krzysztof Kozlowski wrote:
> On 16/03/2023 08:37, Krzysztof Kozlowski wrote:
>> On 15/03/2023 08:28, Jacky Huang wrote:
>>> From: Jacky Huang <ychuang3@nuvoton.com>
>>>
>>> Add documentation to describe nuvoton ma35d1 reset driver bindings.
>> Subject: drop second/last, redundant "bindings". The "dt-bindings"
>> prefix is already stating that these are bindings.


OK, I will fix it.


>>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>>> ---
>>>   .../bindings/reset/nuvoton,ma35d1-reset.yaml  | 50 +++++++++++++++++++
>>>   1 file changed, 50 insertions(+)
>>>   create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>> new file mode 100644
>>> index 000000000000..f66c566c6dce
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>> @@ -0,0 +1,50 @@
>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/reset/nuvoton,ma35d1-reset.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: Nuvoton MA35D1 Reset Controller
>>> +
>>> +maintainers:
>>> +  - Chi-Fang Li <cfli0@nuvoton.com>
>>> +  - Jacky Huang <ychuang3@nuvoton.com>
>>> +
>>> +description:
>>> +  The system reset controller can be used to reset various peripheral
>>> +  controllers in MA35D1 SoC.
>>> +
>>> +properties:
>>> +  compatible:
>>> +    const: nuvoton,ma35d1-reset
>>> +
>>> +  regmap:
>>> +    $ref: /schemas/types.yaml#/definitions/phandle
>>> +    description: Phandle to the register map node.
>> You need to be specific what is this. As you can easily check, there is
>> no such property in any devices. I don't understand why do you need it
>> in the first place.

         reset: reset-controller {
             compatible = "nuvoton,ma35d1-reset";
             regmap = <&sys>;
             #reset-cells = <1>;
         };

The dt_binding_check check report an error about the above "regmap".

I found that add this can pass the test.



>>> +
>>> +  '#reset-cells':
>>> +    const: 1
>>> +
>>> +required:
>>> +  - compatible
>>> +  - regmap
>>> +  - '#reset-cells'
>>> +
>>> +additionalProperties: false
>>> +
>>> +examples:
>>> +  # system reset controller node:
>>> +  - |
>>> +    #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
>>> +
>>> +    sys: system-management@40460000 {
>>> +        compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";
>> And your patchset is not bisectable.... Test for bisectability before
>> sending.
> Ah, no, it's correct. I see the compatible in previous patch. You need
> to clearly describe the dependencies and merging strategy/requirements
> in cover letter.
>
> Best regards,
> Krzysztof

Sorry for the confusion.

I will add history to the cover letter.


Best regards,

Jacky Huang



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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-16  7:45   ` Krzysztof Kozlowski
@ 2023-03-18  6:07     ` Jacky Huang
  2023-03-19 11:06       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-18  6:07 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof,


Thanks for your review.


On 2023/3/16 下午 03:45, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Add initial device tree support for Nuvoton ma35d1 SoC, including
>> cpu, clock, reset, and serial controllers.
>> Add reference boards som-256m and iot-512m.
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   arch/arm64/boot/dts/nuvoton/Makefile          |   2 +
>>   .../boot/dts/nuvoton/ma35d1-iot-512m.dts      |  24 ++
>>   .../boot/dts/nuvoton/ma35d1-som-256m.dts      |  23 ++
>>   arch/arm64/boot/dts/nuvoton/ma35d1.dtsi       | 272 ++++++++++++++++++
>>   4 files changed, 321 insertions(+)
>>   create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
>>   create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
>>   create mode 100644 arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
>>
>> diff --git a/arch/arm64/boot/dts/nuvoton/Makefile b/arch/arm64/boot/dts/nuvoton/Makefile
>> index a99dab90472a..c11ab4eac9c7 100644
>> --- a/arch/arm64/boot/dts/nuvoton/Makefile
>> +++ b/arch/arm64/boot/dts/nuvoton/Makefile
>> @@ -1,2 +1,4 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   dtb-$(CONFIG_ARCH_NPCM) += nuvoton-npcm845-evb.dtb
>> +dtb-$(CONFIG_ARCH_NUVOTON) += ma35d1-iot-512m.dtb
>> +dtb-$(CONFIG_ARCH_NUVOTON) += ma35d1-som-256m.dtb
>> diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
>> new file mode 100644
>> index 000000000000..dffcaef1e6d8
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
>> @@ -0,0 +1,24 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Shan-Chun Hung <schung@nuvoton.com>
>> + *         Jacky huang <ychuang3@nuvoton.com>
>> + */
>> +
>> +/dts-v1/;
>> +#include "ma35d1.dtsi"
>> +
>> +/ {
>> +	model = "Nuvoton MA35D1-IoT";
>> +	compatible = "nuvoton,ma35d1-iot", "nuvoton,ma35d1";
>> +
>> +	chosen {
>> +		stdout-path = "serial0:115200n8";
>> +	};
>> +
>> +	mem: memory@80000000 {
>> +		device_type = "memory";
>> +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
>> +	};
>> +};
>> +
>> diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
>> new file mode 100644
>> index 000000000000..3e6c3d5469ac
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
>> @@ -0,0 +1,23 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Shan-Chun Hung <schung@nuvoton.com>
>> + *         Jacky huang <ychuang3@nuvoton.com>
>> + */
>> +
>> +/dts-v1/;
>> +#include "ma35d1.dtsi"
>> +
>> +/ {
>> +	model = "Nuvoton MA35D1-SOM";
>> +	compatible = "nuvoton,ma35d1-som", "nuvoton,ma35d1";
>> +
>> +	chosen {
>> +		stdout-path = "serial0:115200n8";
>> +	};
>> +
>> +	mem: memory@80000000 {
>> +		device_type = "memory";
>> +		reg = <0x00000000 0x80000000 0 0x10000000>; /* 256M DRAM */
>> +	};
>> +};
>> diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
>> new file mode 100644
>> index 000000000000..8c855f6b330a
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
>> @@ -0,0 +1,272 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Shan-Chun Hung <schung@nuvoton.com>
>> + *         Jacky huang <ychuang3@nuvoton.com>
>> + */
>> +
>> +#include <dt-bindings/interrupt-controller/arm-gic.h>
>> +#include <dt-bindings/input/input.h>
>> +#include <dt-bindings/gpio/gpio.h>
>> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
>> +#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
>> +
>> +/ {
>> +	compatible = "nuvoton,ma35d1";
>> +	interrupt-parent = <&gic>;
>> +	#address-cells = <2>;
>> +	#size-cells = <2>;
>> +
>> +	aliases {
>> +		serial0 = &uart0;
>> +		serial1 = &uart1;
>> +		serial2 = &uart2;
>> +		serial3 = &uart3;
>> +		serial4 = &uart4;
>> +		serial5 = &uart5;
>> +		serial6 = &uart6;
>> +		serial7 = &uart7;
>> +		serial8 = &uart8;
>> +		serial9 = &uart9;
>> +		serial10 = &uart10;
>> +		serial11 = &uart11;
>> +		serial12 = &uart12;
>> +		serial13 = &uart13;
>> +		serial14 = &uart14;
>> +		serial15 = &uart15;
>> +		serial16 = &uart16;
> Aliases of interfaces coming out of SoC are properties of boards, not
> SoC DTSI.


OK, I will remove it from dtsi, and add to dts if required.


>> +	};
>> +
>> +	chosen {
>> +		stdout-path = "serial0:115200n8";
>> +	};
>> +
>> +	cpus {
>> +		#address-cells = <2>;
>> +		#size-cells = <0>;
>
> Blank line.
>
>> +		cpu0: cpu@0 {
>> +			device_type = "cpu";
>> +			compatible = "arm,cortex-a35";
>> +			reg = <0x0 0x0>;
>> +			enable-method = "psci";
>> +			next-level-cache = <&L2_0>;
>> +		};
> Between every node as well.


OK, I will fix them.


>> +		cpu1: cpu@1 {
>> +			device_type = "cpu";
>> +			compatible = "arm,cortex-a35";
>> +			reg = <0x0 0x1>;
>> +			enable-method = "psci";
>> +			next-level-cache = <&L2_0>;
>> +		};
>> +		L2_0: l2-cache0 {
>> +			compatible = "cache";
>> +			cache-level = <2>;
>> +		};
>> +	};
>> +
>> +	psci {
>> +		compatible = "arm,psci-0.2";
>> +		method = "smc";
>> +	};
>> +
>> +	clk_hxt: clock_hxt {
> No underscores in node names.


I will rewrite as:

clk_hxt: clock-hxt {


>
>> +		compatible = "fixed-clock";
>> +		#clock-cells = <0>;
>> +		clock-frequency = <24000000>;
>> +		clock-output-names = "clk_hxt";
> This looks like a property of boards, not SoC. Are you sure the clock
> physically is in every SoC? If so, why it is not part of clock
> controller? (before you start explaining what is this, have in mind that
> I am pretty sure I know what is this, so rather answer the questions)


Yes, it's external to SoC and should be board specific.

I will move this to board dts.


>> +	};
>> +
>> +	timer {
>> +		compatible = "arm,armv8-timer";
>> +		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
>> +			      IRQ_TYPE_LEVEL_LOW)>, /* Physical Secure */
>> +			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) |
>> +			      IRQ_TYPE_LEVEL_LOW)>, /* Physical Non-Secure */
>> +			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
>> +			      IRQ_TYPE_LEVEL_LOW)>, /* Virtual */
>> +			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
>> +			      IRQ_TYPE_LEVEL_LOW)>; /* Hypervisor */
>> +		clock-frequency = <12000000>;
>> +		interrupt-parent = <&gic>;
>> +	};
>> +
>> +	sys: system-management@40460000 {
>> +		compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";
>> +		reg = <0x0 0x40460000 0x0 0x200>;
>> +
>> +		reset: reset-controller {
>> +			compatible = "nuvoton,ma35d1-reset";
>> +			regmap = <&sys>;
>> +			#reset-cells = <1>;
>> +		};
>> +	};
>> +
>> +	clk: clock-controller@40460200 {
>> +		compatible = "nuvoton,ma35d1-clk", "syscon";
>> +		reg = <0x00000000 0x40460200 0x0 0x100>;
>> +		#clock-cells = <1>;
>> +		clocks = <&clk_hxt>;
>> +		clock-names = "clk_hxt";
>> +		assigned-clocks = <&clk CAPLL>,
>> +				  <&clk DDRPLL>,
>> +				  <&clk APLL>,
>> +				  <&clk EPLL>,
>> +				  <&clk VPLL>;
>> +		assigned-clock-rates = <800000000>,
>> +				       <266000000>,
>> +				       <180000000>,
>> +				       <500000000>,
>> +				       <102000000>;
>> +		nuvoton,pll-mode = <0>, <1>, <0>, <0>, <0>;
>> +		nuvoton,sys = <&sys>;
>> +	};
>> +
>> +	gic: interrupt-controller@50801000 {
>> +		compatible = "arm,gic-400";
>> +		#interrupt-cells = <3>;
>> +		interrupt-parent = <&gic>;
>> +		interrupt-controller;
>> +		reg =   <0x0 0x50801000 0 0x1000>, /* GICD */
>> +			<0x0 0x50802000 0 0x2000>, /* GICC */
>> +			<0x0 0x50804000 0 0x2000>, /* GICH */
>> +			<0x0 0x50806000 0 0x2000>; /* GICV */
> reg is second property.


OK, I will fix it.


>
>> +		interrupts = <GIC_PPI 9 (GIC_CPU_MASK_RAW(0x13) |
>> +			      IRQ_TYPE_LEVEL_HIGH)>;
>> +	};
>> +
>> +	uart0:serial@40700000 {
>> +		compatible = "nuvoton,ma35d1-uart";
>> +		reg = <0x0 0x40700000 0x0 0x100>;
>> +		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
>> +		clocks = <&clk UART0_GATE>;
>> +		status = "okay";
> Why? Drop the line... or convert it to disabled. Otherwise, why every
> SoC has serial0 enabled? Is it used internally?


uart0 is on all the way since this SoC booting from the MaskROM boot code,

load arm-trusted-firmware, load bootloader, and finally load linux  kernel.

uart0 is also the Linux console.


>
> Best regards,
> Krzysztof


Best regards,

Jacky Huang



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

* Re: [PATCH 00/15] Introduce Nuvoton ma35d1 SoC
  2023-03-18  3:07         ` Jacky Huang
@ 2023-03-18  9:07           ` Arnd Bergmann
  0 siblings, 0 replies; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-18  9:07 UTC (permalink / raw)
  To: Jacky Huang, Krzysztof Kozlowski, Rob Herring,
	krzysztof.kozlowski+dt, Lee Jones, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Sat, Mar 18, 2023, at 04:07, Jacky Huang wrote:
> On 2023/3/18 上午 12:06, Krzysztof Kozlowski wrote:
>> On 17/03/2023 14:21, Arnd Bergmann wrote:
>>> I only now saw that you had already submitted this several times
>>> at the beginning of last year, and this is technically 'v5'
>>> of the series, and it would make sense to add 'v6' to the subject
>>> next time and link back to the previous [1] and this[2] submission
>>> on lore.kernel.org.
>> ... and address previous feedback. Or at least make it clear in
>> changelog that you addressed it, so our review was not ignored.
>>
>
> Of course, I will add back the changelog.
>
> And, I have a question. If subsequent modifications made to a patch, 
> should the
>
> "Reviewed-by" still be valid? Can we keep it?

In general yes, but it's a bit of a grey area and you have
to apply common sense. Examples where I would drop the
Reviewed-by tag are

- if you changed something based on feedback from a reviewer and
  they provided a Reviewed-by tag based on that changed, but then
  another person asked you change the same thing differently, or
  back to the original version

- if you combine a patch with another one that was not also
  reviewed by the same person.

      Arnd

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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-16 14:17   ` Arnd Bergmann
  2023-03-16 16:44     ` Lee Jones
@ 2023-03-18 13:17     ` Jacky Huang
  2023-03-18 14:04       ` Arnd Bergmann
  1 sibling, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-18 13:17 UTC (permalink / raw)
  To: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Arnd,


Thanks for your suggestion.


On 2023/3/16 下午 10:17, Arnd Bergmann wrote:
> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
>> +	mem: memory@80000000 {
>> +		device_type = "memory";
>> +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
>> +	};
>> +};
> In most machines, the memory size is detected by the boot loader
> and filled in the dtb in memory before starting the kernel, so
> you should not need two separate files here for the two common
> memory configurations.


On ma35d1, memory size is determined early before uboot.

BL1 (MaskROM boot code) -> BL2 (arm-trust-firmware) -> BL32 (op-tee) & 
BL33 (uboot).

The DDR was initialized in BL2 stage with a selected DDR setting, which

is hard coded, including DDR size.

We searched the arm64 dts and found that almost all vendors claimed

memory size in board level dtsi/dts. This seems to be common.

So, can we have it unchanged?


> Since the machine is called 'som', I would assume that this is a
> module that is integrated on another board, so more commonly one
> would have a dtsi file for the som in addition to the one for the
> soc, and have all the components of the module listed in this
> file, while the dts file that includes the som.dtsi lists the
> devices on the carrier board and enables the on-chip devices
> that are connected to the outside.
>
>         Arnd


You are right, ma35d1 som have a base board, and a cpu board on it.

It is a good suggestion that we should have a dtsi for som base board.

Consider that we are in the initial submit, and such a dtsi will be an empty

file at this stage. So, I would like to do it when peripheral drivers

upstream started. Is it ok?


Best  regards,

Jacky Huang



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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-16 16:44     ` Lee Jones
@ 2023-03-18 13:32       ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-18 13:32 UTC (permalink / raw)
  To: Lee Jones, Arnd Bergmann
  Cc: Rob Herring, krzysztof.kozlowski+dt, Michael Turquette,
	Stephen Boyd, Philipp Zabel, Greg Kroah-Hartman, Jiri Slaby,
	devicetree, linux-clk, linux-kernel, linux-serial, schung,
	Jacky Huang

Hi Lee,


Thanks for your attentions.


On 2023/3/17 上午 12:44, Lee Jones wrote:
> On Thu, 16 Mar 2023, Arnd Bergmann wrote:
>
>> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
>>> +	mem: memory@80000000 {
>>> +		device_type = "memory";
>>> +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
>>> +	};
>>> +};
>> In most machines, the memory size is detected by the boot loader
>> and filled in the dtb in memory before starting the kernel, so
>> you should not need two separate files here for the two common
>> memory configurations.
>>
>> Since the machine is called 'som', I would assume that this is a
>> module that is integrated on another board, so more commonly one
>> would have a dtsi file for the som in addition to the one for the
>> soc, and have all the components of the module listed in this
>> file, while the dts file that includes the som.dtsi lists the
>> devices on the carrier board and enables the on-chip devices
>> that are connected to the outside.
> It's using syscon and simple-mfd by the looks of it.
>
> --
> Lee Jones [李琼斯]


We just copy from what other chips's dts have done.

This seems be defined in Documentation\devicetree\bindings\numa.txt.


Best regards,

Jacky Huang


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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-18 13:17     ` Jacky Huang
@ 2023-03-18 14:04       ` Arnd Bergmann
  2023-03-20 15:38         ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Arnd Bergmann @ 2023-03-18 14:04 UTC (permalink / raw)
  To: Jacky Huang, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On Sat, Mar 18, 2023, at 14:17, Jacky Huang wrote:
> On 2023/3/16 下午 10:17, Arnd Bergmann wrote:
>> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
>>> +	mem: memory@80000000 {
>>> +		device_type = "memory";
>>> +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
>>> +	};
>>> +};
>> In most machines, the memory size is detected by the boot loader
>> and filled in the dtb in memory before starting the kernel, so
>> you should not need two separate files here for the two common
>> memory configurations.
>
>
> On ma35d1, memory size is determined early before uboot.
>
> BL1 (MaskROM boot code) -> BL2 (arm-trust-firmware) -> BL32 (op-tee) & 
> BL33 (uboot).
> The DDR was initialized in BL2 stage with a selected DDR setting, which
> is hard coded, including DDR size.
>
> We searched the arm64 dts and found that almost all vendors claimed
> memory size in board level dtsi/dts. This seems to be common.
>
> So, can we have it unchanged?

I see the memory size encoded in about one out of three .dts files,
which is more than I expected. It's clearly not harmful to have it
listed in the dts, it just shouldn't be necessary.

If it helps you with your current u-boot, then leave it in, but
consider adding detection logic into u-boot so it can override
the value in the dtb file at boot time.

>> Since the machine is called 'som', I would assume that this is a
>> module that is integrated on another board, so more commonly one
>> would have a dtsi file for the som in addition to the one for the
>> soc, and have all the components of the module listed in this
>> file, while the dts file that includes the som.dtsi lists the
>> devices on the carrier board and enables the on-chip devices
>> that are connected to the outside.
>>
>
> You are right, ma35d1 som have a base board, and a cpu board on it.
>
> It is a good suggestion that we should have a dtsi for som base board.
>
> Consider that we are in the initial submit, and such a dtsi will be an empty
> file at this stage. So, I would like to do it when peripheral drivers
> upstream started. Is it ok?

It's not a big deal either way. I if you want to keep it only with
one dts file and one dtsi file, that's fine, but maybe rename the dts
file based on the name of the carrier rather than the SoM in this
case.

     Arnd

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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-16  7:51   ` Krzysztof Kozlowski
@ 2023-03-19  2:55     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-19  2:55 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzysztof,


On 2023/3/16 下午 03:51, Krzysztof Kozlowski wrote:
> On 15/03/2023 08:28, Jacky Huang wrote:
>> From: Jacky Huang<ychuang3@nuvoton.com>
>>
>> The clock controller generates clocks for the whole chip, including
>> system clocks and all peripheral clocks. This driver support ma35d1
>> clock gating, divider, and individual PLL configuration.
>>
>> There are 6 PLLs in ma35d1 SoC:
>>    - CA-PLL for the two Cortex-A35 CPU clock
>>    - SYS-PLL for system bus, which comes from the companion MCU
>>      and cannot be programmed by clock controller.
>>    - DDR-PLL for DDR
>>    - EPLL for GMAC and GFX, Display, and VDEC IPs.
>>    - VPLL for video output pixel clock
>>    - APLL for SDHC, I2S audio, and other IPs.
>> CA-PLL has only one operation mode.
>> DDR-PLL, EPLL, VPLL, and APLL are advanced PLLs which have 3
>> operation modes: integer mode, fraction mode, and spread specturm mode.
>>
>> Signed-off-by: Jacky Huang<ychuang3@nuvoton.com>
>> ---
>>   drivers/clk/Makefile                     |   1 +
>>   drivers/clk/nuvoton/Makefile             |   4 +
>>   drivers/clk/nuvoton/clk-ma35d1-divider.c | 144 ++++
>>   drivers/clk/nuvoton/clk-ma35d1-pll.c     | 534 +++++++++++++
>>   drivers/clk/nuvoton/clk-ma35d1.c         | 970 +++++++++++++++++++++++
>>   drivers/clk/nuvoton/clk-ma35d1.h         | 198 +++++
>>   6 files changed, 1851 insertions(+)
>>   create mode 100644 drivers/clk/nuvoton/Makefile
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1-divider.c
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1-pll.c
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1.c
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1.h
>>
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index e3ca0d058a25..2e7916d269e1 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -103,6 +103,7 @@ endif
>>   obj-y					+= mstar/
>>   obj-y					+= mvebu/
>>   obj-$(CONFIG_ARCH_MXS)			+= mxs/
>> +obj-$(CONFIG_ARCH_NUVOTON)		+= nuvoton/
> Missing compile test.
>
> (...)


Thank you. We should have a Kconfig file in nuvoton directory.

I will a Kconfig file including COMPILE_TEST.


>> +
>> +MODULE_AUTHOR("Chi-Fang Li<cfli0@nuvoton.com>");
>> +MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
>> new file mode 100644
>> index 000000000000..faae5a17e425
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1.h
>> @@ -0,0 +1,198 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li<cfli0@nuvoton.com>
>> + */
>> +
>> +#ifndef __DRV_CLK_NUVOTON_MA35D1_H
>> +#define __DRV_CLK_NUVOTON_MA35D1_H
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clkdev.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/mfd/ma35d1-sys.h>
>> +
>> +enum ma35d1_pll_type {
>> +	MA35D1_CAPLL,
>> +	MA35D1_DDRPLL,
>> +	MA35D1_APLL,
>> +	MA35D1_EPLL,
>> +	MA35D1_VPLL,
>> +};
>> +
>> +enum ma35d1_pll_mode {
>> +	VSIPLL_INTEGER_MODE,
>> +	VSIPLL_FRACTIONAL_MODE,
>> +	VSIPLL_SS_MODE,
>> +};
>> +
>> +/* VSI-PLL CTL0~2 */
>> +#define VSIPLL_CTL0			0x0
>> +#define VSIPLL_CTL1			0x4
>> +#define VSIPLL_CTL2			0x8
>> +
>> +/* VSI-PLL Specification limits */
>> +#define VSIPLL_FREF_MAX_FREQ		200000000UL
>> +#define VSIPLL_FREF_MIN_FREQ		1000000UL
>> +#define VSIPLL_FREFDIVM_MAX_FREQ	40000000UL
>> +#define VSIPLL_FREFDIVM_MIN_FREQ0	1000000UL
>> +#define VSIPLL_FREFDIVM_MIN_FREQ1	10000000UL
>> +#define VSIPLL_FCLK_MAX_FREQ		2400000000UL
>> +#define VSIPLL_FCLK_MIN_FREQ		600000000UL
>> +#define VSIPLL_FCLKO_MAX_FREQ		2400000000UL
>> +#define VSIPLL_FCLKO_MIN_FREQ		85700000UL
>> +#define VSIPLL_SPREAD_RANGE		194
>> +#define VSIPLL_MODULATION_FREQ		50000
>> +
>> +/* Clock Control Registers Offset */
>> +#define REG_CLK_PWRCTL			(0x00)
>> +#define REG_CLK_SYSCLK0			(0x04)
>> +#define REG_CLK_SYSCLK1			(0x08)
>> +#define REG_CLK_APBCLK0			(0x0C)
>> +#define REG_CLK_APBCLK1			(0x10)
>> +#define REG_CLK_APBCLK2			(0x14)
>> +#define REG_CLK_CLKSEL0			(0x18)
>> +#define REG_CLK_CLKSEL1			(0x1C)
>> +#define REG_CLK_CLKSEL2			(0x20)
>> +#define REG_CLK_CLKSEL3			(0x24)
>> +#define REG_CLK_CLKSEL4			(0x28)
>> +#define REG_CLK_CLKDIV0			(0x2C)
>> +#define REG_CLK_CLKDIV1			(0x30)
>> +#define REG_CLK_CLKDIV2			(0x34)
>> +#define REG_CLK_CLKDIV3			(0x38)
>> +#define REG_CLK_CLKDIV4			(0x3C)
>> +#define REG_CLK_CLKOCTL			(0x40)
>> +#define REG_CLK_STATUS			(0x50)
>> +#define REG_CLK_PLL0CTL0		(0x60)
>> +#define REG_CLK_PLL2CTL0		(0x80)
>> +#define REG_CLK_PLL2CTL1		(0x84)
>> +#define REG_CLK_PLL2CTL2		(0x88)
>> +#define REG_CLK_PLL3CTL0		(0x90)
>> +#define REG_CLK_PLL3CTL1		(0x94)
>> +#define REG_CLK_PLL3CTL2		(0x98)
>> +#define REG_CLK_PLL4CTL0		(0xA0)
>> +#define REG_CLK_PLL4CTL1		(0xA4)
>> +#define REG_CLK_PLL4CTL2		(0xA8)
>> +#define REG_CLK_PLL5CTL0		(0xB0)
>> +#define REG_CLK_PLL5CTL1		(0xB4)
>> +#define REG_CLK_PLL5CTL2		(0xB8)
>> +#define REG_CLK_CLKDCTL			(0xC0)
>> +#define REG_CLK_CLKDSTS			(0xC4)
>> +#define REG_CLK_CDUPB			(0xC8)
>> +#define REG_CLK_CDLOWB			(0xCC)
>> +#define REG_CLK_CKFLTRCTL		(0xD0)
>> +#define REG_CLK_TESTCLK			(0xF0)
>> +#define REG_CLK_PLLCTL			(0x40)
>> +
>> +/* Constant Definitions for Clock Controller */
>> +#define SMICPLLCTL0_FBDIV_POS		(0)
>> +#define SMICPLLCTL0_FBDIV_MSK		(0xfful << SMICPLLCTL0_FBDIV_POS)
>> +#define SMICPLLCTL0_INDIV_POS		(8)
>> +#define SMICPLLCTL0_INDIV_MSK		(0xful << SMICPLLCTL0_INDIV_POS)
>> +#define SMICPLLCTL0_OUTDIV_POS		(12)
>> +#define SMICPLLCTL0_OUTDIV_MSK		(0x3ul << SMICPLLCTL0_OUTDIV_POS)
>> +#define SMICPLLCTL0_PD_POS		(16)
>> +#define SMICPLLCTL0_PD_MSK		(0x1ul << SMICPLLCTL0_PD_POS)
>> +#define SMICPLLCTL0_BP_POS		(17)
>> +#define SMICPLLCTL0_BP_MSK		(0x1ul << SMICPLLCTL0_BP_POS)
>> +#define VSIPLLCTL0_FBDIV_POS		(0)
>> +#define VSIPLLCTL0_FBDIV_MSK		(0x7fful << VSIPLLCTL0_FBDIV_POS)
>> +#define VSIPLLCTL0_INDIV_POS		(12)
>> +#define VSIPLLCTL0_INDIV_MSK		(0x3ful << VSIPLLCTL0_INDIV_POS)
>> +#define VSIPLLCTL0_MODE_POS		(18)
>> +#define VSIPLLCTL0_MODE_MSK		(0x3ul << VSIPLLCTL0_MODE_POS)
>> +#define VSIPLLCTL0_SSRATE_POS		(20)
>> +#define VSIPLLCTL0_SSRATE_MSK		(0x7fful << VSIPLLCTL0_SSRATE_POS)
>> +#define VSIPLLCTL1_PD_POS		(0)
>> +#define VSIPLLCTL1_PD_MSK		(0x1ul << VSIPLLCTL1_PD_POS)
>> +#define VSIPLLCTL1_BP_POS		(1)
>> +#define VSIPLLCTL1_BP_MSK		(0x1ul << VSIPLLCTL1_BP_POS)
>> +#define VSIPLLCTL1_OUTDIV_POS		(4)
>> +#define VSIPLLCTL1_OUTDIV_MSK		(0x7ul << VSIPLLCTL1_OUTDIV_POS)
>> +#define VSIPLLCTL1_FRAC_POS		(8)
>> +#define VSIPLLCTL1_FRAC_MSK		(0xfffffful << VSIPLLCTL1_FRAC_POS)
>> +#define VSIPLLCTL2_SLOPE_POS		(0)
>> +#define VSIPLLCTL2_SLOPE_MSK		(0xfffffful << VSIPLLCTL2_SLOPE_POS)
>> +
>> +struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type, u8 u8mode,
>> +				 const char *name, const char *parent,
>> +				 unsigned long targetFreq,
>> +				 void __iomem *base,
>> +				 struct regmap *regmap);
>> +
>> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev,
>> +				    const char *name,
>> +				    const char *parent_name,
>> +				    unsigned long flags,
>> +				    void __iomem *reg, u8 shift,
>> +				    u8 width, u32 mask_bit);
>> +
>> +extern spinlock_t ma35d1_lock;
> Why this is here?


We will remove it and use "static DEFINE_SPINLOCK(ma35d1_lock);"

and have it used in clk-ma35d1-divider.c only.


>> +
>> +static inline struct clk_hw *ma35d1_clk_fixed(const char *name, int rate)
>> +{
>> +	return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
>> +}
>> +
> Why all these are here?


These should be static functions in clk-ma35d1.c.

We will move these function to the C file in next version.


>> +
>> +#endif /* __DRV_CLK_NUVOTON_MA35D1_H */
> Best regards,
> Krzysztof


Best regards,

Jacky Huang



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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-16 15:56   ` Ilpo Järvinen
@ 2023-03-19  5:16     ` Jacky Huang
  2023-03-20 10:31       ` Ilpo Järvinen
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-19  5:16 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, devicetree, linux-clk, linux-kernel,
	linux-serial, schung, Jacky Huang


On 2023/3/16 下午 11:56, Ilpo Järvinen wrote:
> On Wed, 15 Mar 2023, Jacky Huang wrote:
>
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> The clock controller generates clocks for the whole chip, including
>> system clocks and all peripheral clocks. This driver support ma35d1
>> clock gating, divider, and individual PLL configuration.
>>
>> There are 6 PLLs in ma35d1 SoC:
>>    - CA-PLL for the two Cortex-A35 CPU clock
>>    - SYS-PLL for system bus, which comes from the companion MCU
>>      and cannot be programmed by clock controller.
>>    - DDR-PLL for DDR
>>    - EPLL for GMAC and GFX, Display, and VDEC IPs.
>>    - VPLL for video output pixel clock
>>    - APLL for SDHC, I2S audio, and other IPs.
>> CA-PLL has only one operation mode.
>> DDR-PLL, EPLL, VPLL, and APLL are advanced PLLs which have 3
>> operation modes: integer mode, fraction mode, and spread specturm mode.
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   drivers/clk/Makefile                     |   1 +
>>   drivers/clk/nuvoton/Makefile             |   4 +
>>   drivers/clk/nuvoton/clk-ma35d1-divider.c | 144 ++++
>>   drivers/clk/nuvoton/clk-ma35d1-pll.c     | 534 +++++++++++++
>>   drivers/clk/nuvoton/clk-ma35d1.c         | 970 +++++++++++++++++++++++
>>   drivers/clk/nuvoton/clk-ma35d1.h         | 198 +++++
>>   6 files changed, 1851 insertions(+)
>>   create mode 100644 drivers/clk/nuvoton/Makefile
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1-divider.c
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1-pll.c
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1.c
>>   create mode 100644 drivers/clk/nuvoton/clk-ma35d1.h
>>
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index e3ca0d058a25..2e7916d269e1 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -103,6 +103,7 @@ endif
>>   obj-y					+= mstar/
>>   obj-y					+= mvebu/
>>   obj-$(CONFIG_ARCH_MXS)			+= mxs/
>> +obj-$(CONFIG_ARCH_NUVOTON)		+= nuvoton/
>>   obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
>>   obj-$(CONFIG_COMMON_CLK_PISTACHIO)	+= pistachio/
>>   obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
>> diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile
>> new file mode 100644
>> index 000000000000..d2c092541b8d
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/Makefile
>> @@ -0,0 +1,4 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1.o
>> +obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1-divider.o
>> +obj-$(CONFIG_ARCH_NUVOTON) += clk-ma35d1-pll.o
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1-divider.c b/drivers/clk/nuvoton/clk-ma35d1-divider.c
>> new file mode 100644
>> index 000000000000..5f4791531e47
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
>> @@ -0,0 +1,144 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/slab.h>
>> +#include <linux/io.h>
>> +#include <linux/err.h>
>> +#include <linux/spinlock.h>
>> +
>> +#include "clk-ma35d1.h"
>> +
>> +#define div_mask(width)		((1 << (width)) - 1)
>> +
>> +struct ma35d1_adc_clk_divider {
>> +	struct clk_hw hw;
>> +	void __iomem *reg;
>> +	u8 shift;
>> +	u8 width;
>> +	u32 mask;
>> +	const struct clk_div_table *table;
>> +	spinlock_t *lock;
> Add comment to indicate what it protects.


OK, I will add comment:

/* protects concurrent access to clock divider registers */


>> +};
>> +
>> +#define to_ma35d1_adc_clk_divider(_hw)	\
>> +	container_of(_hw, struct ma35d1_adc_clk_divider, hw)
> static inline


I will modify these "static" functions as "static inline".


>
>> +static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw,
>> +					       unsigned long parent_rate)
>> +{
>> +	unsigned int val;
>> +	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
>> +
>> +	val = readl_relaxed(dclk->reg) >> dclk->shift;
>> +	val &= div_mask(dclk->width);
>> +	val += 1;
>> +	return divider_recalc_rate(hw, parent_rate, val, dclk->table,
>> +				   CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
>> +}
>> +
>> +static long ma35d1_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
>> +				     unsigned long *prate)
>> +{
>> +	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
>> +
>> +	return divider_round_rate(hw, rate, prate, dclk->table,
>> +				  dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
>> +}
>> +
>> +static int ma35d1_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				  unsigned long parent_rate)
>> +{
>> +	int value;
>> +	unsigned long flags = 0;
>> +	u32 data;
>> +	struct ma35d1_adc_clk_divider *dclk = to_ma35d1_adc_clk_divider(hw);
>> +
>> +	value = divider_get_val(rate, parent_rate, dclk->table,
>> +				dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
>> +
>> +	if (dclk->lock)
>> +		spin_lock_irqsave(dclk->lock, flags);
>> +
>> +	data = readl_relaxed(dclk->reg);
>> +	data &= ~(div_mask(dclk->width) << dclk->shift);
>> +	data |= (value - 1) << dclk->shift;
>> +	data |= dclk->mask;
>> +
>> +	writel_relaxed(data, dclk->reg);
>> +
>> +	if (dclk->lock)
>> +		spin_unlock_irqrestore(dclk->lock, flags);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct clk_ops ma35d1_adc_clkdiv_ops = {
>> +	.recalc_rate = ma35d1_clkdiv_recalc_rate,
>> +	.round_rate = ma35d1_clkdiv_round_rate,
>> +	.set_rate = ma35d1_clkdiv_set_rate,
>> +};
>> +
>> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
>> +				     const char *parent_name,
>> +				     unsigned long flags, void __iomem *reg,
>> +				     u8 shift, u8 width, u32 mask_bit)
>> +{
>> +	struct ma35d1_adc_clk_divider *div;
>> +	struct clk_init_data init;
>> +	struct clk_div_table *table;
>> +	u32 max_div, min_div;
>> +	struct clk_hw *hw;
>> +	int ret;
>> +	int i;
>> +
>> +	/* allocate the divider */
>> +	div = kzalloc(sizeof(*div), GFP_KERNEL);
>> +	if (!div)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	/* Init the divider table */
>> +	max_div = div_mask(width) + 1;
>> +	min_div = 1;
>> +
>> +	table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
>> +	if (!table) {
>> +		kfree(div);
>> +		return ERR_PTR(-ENOMEM);
> Use rollback to do error handling:
>
> 		ret = ERR_PTR(-ENOMEM);
> 		goto free_div;


Thank you for the advice.

We will fix it in the next version.


>> +	}
>> +
>> +	for (i = 0; i < max_div; i++) {
>> +		table[i].val = (min_div + i);
>> +		table[i].div = 2 * table[i].val;
>> +	}
>> +	table[max_div].val = 0;
>> +	table[max_div].div = 0;
>> +
>> +	init.name = name;
>> +	init.ops = &ma35d1_adc_clkdiv_ops;
>> +	init.flags |= flags;
>> +	init.parent_names = parent_name ? &parent_name : NULL;
>> +	init.num_parents = parent_name ? 1 : 0;
>> +
>> +	/* struct ma35d1_adc_clk_divider assignments */
>> +	div->reg = reg;
>> +	div->shift = shift;
>> +	div->width = width;
>> +	div->mask = mask_bit ? BIT(mask_bit) : 0;
>> +	div->lock = &ma35d1_lock;
>> +	div->hw.init = &init;
>> +	div->table = table;
>> +
>> +	/* Register the clock */
>> +	hw = &div->hw;
>> +	ret = clk_hw_register(NULL, hw);
>> +	if (ret) {
>> +		kfree(table);
>> +		kfree(div);
>> +		return ERR_PTR(ret);
> ret = ERR_PTR(ret);
> goto free_table;
>
>> +	}
>> +	return hw;
> free_table:
> 	kfree(table);
> free_div:
> 	kfree(div);
> 	return ret;


OK, we follow this.


>> +}
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
>> new file mode 100644
>> index 000000000000..79e724b148fa
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
>> @@ -0,0 +1,534 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/bitfield.h>
>> +
>> +#include "clk-ma35d1.h"
>> +
>> +#define to_ma35d1_clk_pll(clk) \
>> +	(container_of(clk, struct ma35d1_clk_pll, clk))
> static inline


I am sorry cannot get "static inline" refer to which one.

Would you give more advice here?

Thank you.


>
>> +
>> +#define PLL0CTL0_FBDIV_MSK	GENMASK(7, 0)
>> +#define PLL0CTL0_INDIV_MSK	GENMASK(11, 8)
>> +#define PLL0CTL0_OUTDIV_MSK	GENMASK(13, 12)
>> +#define PLL0CTL0_PD_MSK		BIT(16)
>> +#define PLL0CTL0_BP_MSK		BIT(17)
>> +#define PLLXCTL0_FBDIV_MSK	GENMASK(10, 0)
>> +#define PLLXCTL0_INDIV_MSK	GENMASK(17, 12)
>> +#define PLLXCTL0_MODE_MSK	GENMASK(19, 18)
>> +#define PLLXCTL0_SSRATE_MSK	GENMASK(30, 20)
>> +#define PLLXCTL1_PD_MSK		BIT(0)
>> +#define PLLXCTL1_BP_MSK		BIT(1)
>> +#define PLLXCTL1_OUTDIV_MSK	GENMASK(6, 4)
>> +#define PLLXCTL1_FRAC_MSK	GENMASK(31, 8)
>> +#define PLLXCTL2_SLOPE_MSK	GENMASK(23, 0)
>> +
>> +struct ma35d1_clk_pll {
>> +	struct clk_hw hw;
>> +	u8 type;
>> +	u8 mode;
>> +	unsigned long rate;
>> +	void __iomem *ctl0_base;
>> +	void __iomem *ctl1_base;
>> +	void __iomem *ctl2_base;
>> +	struct regmap *regmap;
>> +};
>> +
>> +struct vsipll_freq_conf_reg_tbl {
>> +	unsigned long freq;
>> +	u8 mode;
>> +	u32 ctl0_reg;
>> +	u32 ctl1_reg;
>> +	u32 ctl2_reg;
>> +};
>> +
>> +static const struct vsipll_freq_conf_reg_tbl ma35d1pll_freq[] = {
>> +	{ 1000000000, VSIPLL_INTEGER_MODE, 0x307d, 0x10, 0 },
>> +	{ 884736000, VSIPLL_FRACTIONAL_MODE, 0x41024, 0xdd2f1b11, 0 },
>> +	{ 533000000, VSIPLL_SS_MODE, 0x12b8102c, 0x6aaaab20, 0x12317 },
>> +	{ }
>> +};
>> +
>> +static void CLK_UnLockReg(struct ma35d1_clk_pll *pll)
> Use lowercase only.


Sure, we will fix them all.


>> +{
>> +	int ret;
>> +
>> +	/* Unlock PLL registers */
>> +	do {
>> +		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x59);
>> +		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x16);
>> +		regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x88);
>> +		regmap_read(pll->regmap, REG_SYS_RLKTZNS, &ret);
>> +	} while (ret == 0);
>> +}
>> +
>> +static void CLK_LockReg(struct ma35d1_clk_pll *pll)
> Ditto.
>
>> +{
>> +	/* Lock PLL registers */
>> +	regmap_write(pll->regmap, REG_SYS_RLKTZNS, 0x0);
>> +}
>> +
>> +/* SMIC PLL for CAPLL */
>> +unsigned long CLK_GetPLLFreq_SMICPLL(struct ma35d1_clk_pll *pll,
>> +				     unsigned long PllSrcClk)
> Lowercase only for function name and variable names. Please do the rest,
> I won't mention more of them.
>
>> +{
>> +	u32 u32M, u32N, u32P, u32OutDiv;
>> +	u32 val;
>> +	unsigned long u64PllClk;
>> +	u32 clk_div_table[] = { 1, 2, 4, 8};
> Inconsistent whitespaces.


will be fixed


>
>> +
>> +	val = __raw_readl(pll->ctl0_base);
>> +
>> +	u32N = FIELD_GET(PLL0CTL0_FBDIV_MSK, val);
>> +	u32M = FIELD_GET(PLL0CTL0_INDIV_MSK, val);
>> +	u32P = FIELD_GET(PLL0CTL0_OUTDIV_MSK, val);
>> +	u32OutDiv = clk_div_table[u32P];
>> +
>> +	if (val & PLL0CTL0_BP_MSK) {
>> +		u64PllClk = PllSrcClk;
>> +	} else {
>> +		u64PllClk = PllSrcClk * u32N;
>> +		do_div(u64PllClk, u32M * u32OutDiv);
> Does this block depend on unsigned long being 64-bit? Or should you
> enforce it by using u64 that is always same sized unlike unsigned long?


We will fix it to use u64 instead of "unsigned long".

All the naming with data type and upper case will be fixed in the next 
version.


>> +	}
>> +	return u64PllClk;
>> +}
>> +
>> +/* VSI-PLL: INTEGER_MODE */
>> +unsigned long CLK_CalPLLFreq_Mode0(unsigned long PllSrcClk,
>> +				   unsigned long u64PllFreq, u32 *u32Reg)
>> +{
>> +	u32 u32TmpM, u32TmpN, u32TmpP;
>> +	u32 u32RngMinN, u32RngMinM, u32RngMinP;
>> +	u32 u32RngMaxN, u32RngMaxM, u32RngMaxP;
>> +	u32 u32Tmp, u32Min, u32MinN, u32MinM, u32MinP;
> Remove types from names.


OK, we will fix them.

>
>> +	unsigned long u64PllClk;
>> +	unsigned long u64Con1, u64Con2, u64Con3;
> Okay as unsigned long or do you want always 64-bit which is u64 ?
>
> Define these inside the loops below and remove the types from the name.


We will fix the "unsigned long" to be  u64.


>> +
>> +	u64PllClk = 0;
>> +	u32Min = (u32) -1;
>> +
>> +	if (!((u64PllFreq >= VSIPLL_FCLKO_MIN_FREQ) &&
>> +	    (u64PllFreq <= VSIPLL_FCLKO_MAX_FREQ))) {
>> +		u32Reg[0] = ma35d1pll_freq[0].ctl0_reg;
>> +		u32Reg[1] = ma35d1pll_freq[0].ctl1_reg;
>> +		u64PllClk = ma35d1pll_freq[0].freq;
>> +		return u64PllClk;
>> +	}
>> +
>> +	u32RngMinM = 1UL;
>> +	u32RngMaxM = 63UL;
>> +	u32RngMinM = ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) > 1) ?
>> +		     (PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) : 1;
> max(PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ, 1UL);
>
> Remember to add include for it.
>
>> +	u32RngMaxM = ((PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) < u32RngMaxM) ?
>> +		     (PllSrcClk / VSIPLL_FREFDIVM_MIN_FREQ0) : u32RngMaxM;
> min();


OK, we will use max() and min() instead.


>> +
>> +	for (u32TmpM = u32RngMinM; u32TmpM < (u32RngMaxM + 1); u32TmpM++) {
> <= and remove + 1
>
> Why can't you call this loop cariable just m ?
>
>> +		u64Con1 = PllSrcClk / u32TmpM;
>> +		u32RngMinN = 16UL;
>> +		u32RngMaxN = 2047UL;
> Why aren't these two values in defines?


We will add #define constant for them.


>
>> +		u32RngMinN = ((VSIPLL_FCLK_MIN_FREQ / u64Con1) > u32RngMinN) ?
>> +			     (VSIPLL_FCLK_MIN_FREQ / u64Con1) : u32RngMinN;
> max();
>
>> +		u32RngMaxN = ((VSIPLL_FCLK_MAX_FREQ / u64Con1) < u32RngMaxN) ?
>> +			     (VSIPLL_FCLK_MAX_FREQ / u64Con1) : u32RngMaxN;
> min();
>
>> +
>> +		for (u32TmpN = u32RngMinN; u32TmpN < (u32RngMaxN + 1);
> <= and remove + 1
>
> Name variable as n ?


OK. we will use max() and min() and rename u32TmpN as n.


>> +		     u32TmpN++) {
> One line.
>
>> +			u64Con2 = u64Con1 * u32TmpN;
>> +			u32RngMinP = 1UL;
>> +			u32RngMaxP = 7UL;
> Limits to defines?


OK, will add #define constant for them.


>> +			u32RngMinP = ((u64Con2 / VSIPLL_FCLKO_MAX_FREQ) > 1) ?
>> +				      (u64Con2 / VSIPLL_FCLKO_MAX_FREQ) : 1;
>> +			u32RngMaxP = ((u64Con2 / VSIPLL_FCLKO_MIN_FREQ) <
>> +				      u32RngMaxP) ?
>> +				      (u64Con2 / VSIPLL_FCLKO_MIN_FREQ) :
>> +				      u32RngMaxP;
> min & max.


We will fix it.


>
>> +			for (u32TmpP = u32RngMinP; u32TmpP < (u32RngMaxP + 1);
> <= and remove +1?


I fix it as "<=".


>
> Name variable as p?


sure


>
>> +			     u32TmpP++) {
> One line.
>
>> +				u64Con3 = u64Con2 / u32TmpP;
>> +				if (u64Con3 > u64PllFreq)
>> +					u32Tmp = u64Con3 - u64PllFreq;
>> +				else
>> +					u32Tmp = u64PllFreq - u64Con3;
> abs()?


Yes, we will use abs() instead.


>
>> +
>> +				if (u32Tmp < u32Min) {
>> +					u32Min = u32Tmp;
>> +					u32MinM = u32TmpM;
>> +					u32MinN = u32TmpN;
>> +					u32MinP = u32TmpP;
>> +
>> +					if (u32Min == 0UL) {
> goto out?
>
>> +						u32Reg[0] = (u32MinM << 12) |
>> +							    (u32MinN);
>> +						u32Reg[1] = (u32MinP << 4);
>> +						return ((PllSrcClk * u32MinN) /
>> +							(u32MinP * u32MinM));
>> +					}
>> +				}
>> +			}
>> +		}
>> +	}
>> +
> out: ?


OK, we will use "goto out".


>> +	u32Reg[0] = (u32MinM << 12) | (u32MinN);
> FIELD_PREP() | FIELD_PREP() ?
>
>> +	u32Reg[1] = (u32MinP << 4);
> FIELD_PREP() ?


Yes, we will use FIELD_PREP().


>> +	u64PllClk = (PllSrcClk * u32MinN) / (u32MinP * u32MinM);
> Use the 64-bit divide from math64.h rather than leave it up to compiler.


OK, we will fix it.


>> +	return u64PllClk;
>> +}
>> +
>> +/* VSI-PLL: FRACTIONAL_MODE */
>> +unsigned long CLK_CalPLLFreq_Mode1(unsigned long PllSrcClk,
>> +				   unsigned long u64PllFreq, u32 *u32Reg)
>> +{
>> +	unsigned long u64X, u64N, u64M, u64P, u64tmp;
>> +	unsigned long u64PllClk, u64FCLKO;
>> +	u32 u32FRAC;
>> +
>> +	if (u64PllFreq > VSIPLL_FCLKO_MAX_FREQ) {
>> +		u32Reg[0] = ma35d1pll_freq[1].ctl0_reg;
>> +		u32Reg[1] = ma35d1pll_freq[1].ctl1_reg;
>> +		u64PllClk = ma35d1pll_freq[1].freq;
>> +		return u64PllClk;
>> +	}
>> +
>> +	if (u64PllFreq > (VSIPLL_FCLKO_MIN_FREQ/(100-1))) {
>> +		u64FCLKO = u64PllFreq * ((VSIPLL_FCLKO_MIN_FREQ / u64PllFreq) +
>> +			   ((VSIPLL_FCLKO_MIN_FREQ % u64PllFreq) ? 1 : 0));
> You need to rework this to do 64-bit divide and remainder with
> something that comes from math64.h.


We will fix it. Thanks for the suggestion.


>
>> +	} else {
>> +		pr_err("Failed to set rate %ld\n", u64PllFreq);
>> +		return 0;
>> +	}
>> +
>> +	u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
>> +	       ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
>> +		((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));
> Ditto.
>
> Is here some ...ROUND_UP() trick hidden too?


This follows the description of PLL spec.


>
>> +
>> +	if ((PllSrcClk > (VSIPLL_FREFDIVM_MAX_FREQ * (64-1))) ||
>> +	    (PllSrcClk < VSIPLL_FREFDIVM_MIN_FREQ1))
>> +		return 0;
>> +
>> +	u64M = (PllSrcClk <= VSIPLL_FREFDIVM_MAX_FREQ) ? 1 :
>> +	       ((PllSrcClk / VSIPLL_FREFDIVM_MAX_FREQ) +
>> +	       ((PllSrcClk % VSIPLL_FREFDIVM_MAX_FREQ) ? 1 : 0));
> Ditto.
>
>> +
>> +	u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
>> +	u64N = u64tmp / 1000;
>> +	u64X = u64tmp % 1000;
> math64.h x 3 (or x2 since you can get remainder for free I think).


Got it. Thank you.


>> +	u32FRAC = ((u64X << 24) + 500) / 1000;
>> +	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
>> +
>> +	u32Reg[0] = (u64M << 12) | (u64N);
> FIELD_PREP() ?
>
>> +	u32Reg[1] = (u64P << 4) | (u32FRAC << 8);
> FIELD_PREP() ?


Sure, we will use FIELD_PREP().


>> +	return u64PllClk;
>> +}
>> +
>> +/* VSI-PLL: SS_MODE */
>> +unsigned long CLK_CalPLLFreq_Mode2(unsigned long PllSrcClk,
>> +				   unsigned long u64PllFreq,
>> +				   u32 u32SR, u32 u32Fmod, u32 *u32Reg)
>> +{
>> +	unsigned long u64X, u64N, u64M, u64P, u64tmp, u64tmpP, u64tmpM;
>> +	unsigned long u64SSRATE, u64SLOPE, u64PllClk, u64FCLKO;
>> +	u32 u32FRAC, i;
>> +
>> +	if (u64PllFreq >= VSIPLL_FCLKO_MAX_FREQ) {
>> +		u32Reg[0] = ma35d1pll_freq[2].ctl0_reg;
>> +		u32Reg[1] = ma35d1pll_freq[2].ctl1_reg;
>> +		u32Reg[2] = ma35d1pll_freq[2].ctl2_reg;
>> +		u64PllClk = ma35d1pll_freq[2].freq;
>> +		return u64PllClk;
>> +	}
>> +
>> +	if (u64PllFreq < VSIPLL_FCLKO_MIN_FREQ) {
>> +		u64FCLKO = 0;
>> +		for (i = 2; i < 8; i++) {
>> +			u64tmp = (i * u64PllFreq);
>> +			if (u64tmp > VSIPLL_FCLKO_MIN_FREQ)
> VSIPLL_FCLKO_MAX_FREQ check is not required ?


We will add check VSIPLL_FCLKO_MAX_FREQ.


>
>> +				u64FCLKO = u64tmp;
>> +		}
>> +		if (u64FCLKO == 0) {
>> +			pr_err("Failed to set rate %ld\n", u64PllFreq);
>> +			return 0;
>> +		}
>> +
>> +	} else
>> +		u64FCLKO = u64PllFreq;
>> +
>> +	u64P = 0;
>> +	for (i = 1; i < 8; i++) {
>> +		u64tmpP = i * u64FCLKO;
>> +		if ((u64tmpP <= VSIPLL_FCLK_MAX_FREQ) &&
>> +		    (u64tmpP >= VSIPLL_FCLK_MIN_FREQ)) {
>> +			u64P = i;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (u64P == 0)
>> +		return 0;
>> +
>> +	u64M = 0;
>> +	for (i = 1; i < 64; i++) {
>> +		u64tmpM = PllSrcClk / i;
>> +		if ((u64tmpM <= VSIPLL_FREFDIVM_MAX_FREQ) &&
>> +		    (u64tmpM >= VSIPLL_FREFDIVM_MIN_FREQ1)) {
>> +			u64M = i;
>> +			break;
>> +		}
>> +	}
>> +
>> +	if (u64M == 0)
>> +		return 0;
>> +
>> +	u64tmp = (u64FCLKO * u64P * u64M * 1000) / PllSrcClk;
>> +	u64N = u64tmp / 1000;
>> +	u64X = u64tmp % 1000;
>> +	u32FRAC = ((u64X << 24) + 500) / 1000;
>> +
>> +	u64SSRATE = ((PllSrcClk >> 1) / (u32Fmod * 2)) - 1;
>> +	u64SLOPE = ((u64tmp * u32SR / u64SSRATE) << 24) / 100 / 1000;
>> +
>> +	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
> Is some *SEC_PER_*SEC define relevant for 1000 ?
>
> Or some other units, e.g., HZ related?


1000 is for kHz to MHz, and 100 is for percentage.


>
>> +
>> +	u32Reg[0] = (u64SSRATE << VSIPLLCTL0_SSRATE_POS) | (u64M <<
>> +		     VSIPLLCTL0_INDIV_POS) | (u64N);
> FIELD_PREP()


We will fix it.


>
>> +	u32Reg[1] = (u64P << VSIPLLCTL1_OUTDIV_POS) | (u32FRAC << VSIPLLCTL1_FRAC_POS);
> Instead of _POS named variables, add GENMASK one instead and use
> FIELD_PREP. You might need to use GENMASK_ULL() for the masks if you are
> dealing with true 64-bitness here instead of the quasi unsigned longs.


Got it. We will modify it.


>> +	u32Reg[2] = u64SLOPE;
>> +	return u64PllClk;
>> +}
>> +
>> +unsigned long CLK_SetPLLFreq(struct ma35d1_clk_pll *pll,
>> +			     unsigned long PllSrcClk,
>> +			     unsigned long u64PllFreq)
>> +{
>> +	u32 u32Reg[3] = { 0 }, val_ctl0, val_ctl1, val_ctl2;
>> +	unsigned long u64PllClk;
>> +
>> +	val_ctl0 = __raw_readl(pll->ctl0_base);
>> +	val_ctl1 = __raw_readl(pll->ctl1_base);
>> +	val_ctl2 = __raw_readl(pll->ctl2_base);
>> +
>> +	switch (pll->mode) {
>> +	case VSIPLL_INTEGER_MODE:
>> +		u64PllClk = CLK_CalPLLFreq_Mode0(PllSrcClk, u64PllFreq,
>> +						 u32Reg);
> One line.


It will exceed 80 characters in one line.


>
>> +		val_ctl0 = u32Reg[0] |
>> +			   (VSIPLL_INTEGER_MODE << VSIPLLCTL0_MODE_POS);
> GENMASK() + FIELD_PREP()


I will fix it.


>
>> +		break;
>> +	case VSIPLL_FRACTIONAL_MODE:
>> +		u64PllClk = CLK_CalPLLFreq_Mode1(PllSrcClk, u64PllFreq,
>> +						 u32Reg);
>> +		val_ctl0 = u32Reg[0] |
>> +			   (VSIPLL_FRACTIONAL_MODE << VSIPLLCTL0_MODE_POS);
> Ditto.
>
>> +		break;
>> +	case VSIPLL_SS_MODE:
>> +		u64PllClk = CLK_CalPLLFreq_Mode2(PllSrcClk, u64PllFreq,
>> +						 VSIPLL_MODULATION_FREQ,
>> +						 VSIPLL_SPREAD_RANGE, u32Reg);
>> +		val_ctl0 = u32Reg[0] |
>> +			   (VSIPLL_SS_MODE << VSIPLLCTL0_MODE_POS);
> Ditto.
>
>> +		break;
>> +	}
>> +
>> +	val_ctl1 = VSIPLLCTL1_PD_MSK | u32Reg[1];
>> +	val_ctl2 = u32Reg[2];
>> +
>> +	__raw_writel(val_ctl0, pll->ctl0_base);
>> +	__raw_writel(val_ctl1, pll->ctl1_base);
>> +	__raw_writel(val_ctl2, pll->ctl2_base);
>> +	return u64PllClk;
>> +}
>> +
>> +unsigned long CLK_GetPLLFreq_VSIPLL(struct ma35d1_clk_pll *pll,
>> +				    unsigned long PllSrcClk)
>> +{
>> +	u32 u32M, u32N, u32P, u32X, u32SR, u32FMOD;
>> +	u32 val_ctl0, val_ctl1, val_ctl2;
>> +	unsigned long u64PllClk, u64X;
>> +
>> +	val_ctl0 = __raw_readl(pll->ctl0_base);
>> +	val_ctl1 = __raw_readl(pll->ctl1_base);
>> +	val_ctl2 = __raw_readl(pll->ctl2_base);
>> +
>> +	if (val_ctl1 & PLLXCTL1_BP_MSK) {
>> +		u64PllClk = PllSrcClk;
>> +		return u64PllClk;
>> +	}
>> +
>> +	if (pll->mode == VSIPLL_INTEGER_MODE) {
>> +		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
>> +		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
>> +		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
>> +
>> +		u64PllClk = PllSrcClk * u32N;
>> +		do_div(u64PllClk, u32M * u32P);
>> +
>> +	} else if (pll->mode == VSIPLL_FRACTIONAL_MODE) {
>> +		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
>> +		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
>> +		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
>> +		u32X = FIELD_GET(PLLXCTL1_FRAC_MSK, val_ctl1);
>> +		u64X = (u64) u32X;
>> +		u64X = (((u64X * 1000) + 500) >> 24);
>> +		u64PllClk = (PllSrcClk * ((u32N * 1000) + u64X)) /
>> +			    1000 / u32P / u32M;
> math64.h
>
> Please fix the remaining ones w/o me noting them down.


Got it. Thank you.


>> +
>> +	} else {
>> +		u32N = FIELD_GET(PLLXCTL0_FBDIV_MSK, val_ctl0);
>> +		u32M = FIELD_GET(PLLXCTL0_INDIV_MSK, val_ctl0);
>> +		u32SR = FIELD_GET(PLLXCTL0_SSRATE_MSK, val_ctl0);
>> +		u32P = FIELD_GET(PLLXCTL1_OUTDIV_MSK, val_ctl1);
>> +		u32X = FIELD_GET(PLLXCTL1_FRAC_MSK, val_ctl1);
>> +		u32FMOD = FIELD_GET(PLLXCTL2_SLOPE_MSK, val_ctl2);
>> +		u64X = (u64) u32X;
>> +		u64X = ((u64X * 1000) >> 24);
>> +		u64PllClk = (PllSrcClk * ((u32N * 1000) + u64X)) /
>> +			    1000 / u32P / u32M;
>> +	}
>> +	return u64PllClk;
>> +}
>> +
>> +static int ma35d1_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long parent_rate)
>> +{
>> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +
>> +	if ((parent_rate < VSIPLL_FREF_MIN_FREQ) ||
>> +	    (parent_rate > VSIPLL_FREF_MAX_FREQ))
>> +		return 0;
>> +
>> +	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
>> +		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
>> +		return -EACCES;
>> +	}
>> +	CLK_UnLockReg(pll);
>> +	pll->rate = CLK_SetPLLFreq(pll, parent_rate, rate);
>> +	CLK_LockReg(pll);
>> +	return 0;
>> +}
>> +
>> +static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw,
>> +						unsigned long parent_rate)
>> +{
>> +	unsigned long pllfreq;
>> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +
>> +	if ((parent_rate < VSIPLL_FREF_MIN_FREQ)
>> +	    || (parent_rate > VSIPLL_FREF_MAX_FREQ))
>> +		return 0;
>> +
>> +	switch (pll->type) {
>> +	case MA35D1_CAPLL:
>> +		pllfreq = CLK_GetPLLFreq_SMICPLL(pll, parent_rate);
>> +		break;
>> +	case MA35D1_DDRPLL:
>> +	case MA35D1_APLL:
>> +	case MA35D1_EPLL:
>> +	case MA35D1_VPLL:
>> +		pllfreq = CLK_GetPLLFreq_VSIPLL(pll, parent_rate);
>> +		break;
>> +	}
>> +
>> +	return pllfreq;
>> +}
>> +
>> +static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long *prate)
>> +{
>> +	return rate;
>> +}
>> +
>> +static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
>> +{
>> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +	u32 val = __raw_readl(pll->ctl1_base);
>> +
>> +	return (val & VSIPLLCTL1_PD_MSK) ? 0 : 1;
> Unnecessary parenthesis


I will remove the parenthesis.


>
>> +}
>> +
>> +static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
>> +{
>> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +	u32 val;
>> +
>> +	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
>> +		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
>> +		return -EACCES;
>> +	}
> Add helper for this, there is more than 1 copy of this.
>
>> +
>> +	CLK_UnLockReg(pll);
>> +	val = __raw_readl(pll->ctl1_base);
>> +	val &= ~VSIPLLCTL1_PD_MSK;
>> +	__raw_writel(val, pll->ctl1_base);
>> +	CLK_LockReg(pll);
>> +	return 0;
>> +}
>> +
>> +static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
>> +{
>> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
>> +	u32 val;
>> +
>> +	if ((pll->type == MA35D1_CAPLL) || (pll->type == MA35D1_DDRPLL)) {
>> +		pr_warn("Nuvoton MA35D1 CAPLL/DDRPLL is read only.\n");
>> +	} else {
>> +		val = __raw_readl(pll->ctl1_base);
>> +		val |= VSIPLLCTL1_PD_MSK;
>> +		__raw_writel(val, pll->ctl1_base);
>> +	}
>> +}
>> +
>> +static const struct clk_ops ma35d1_clk_pll_ops = {
>> +	.is_prepared = ma35d1_clk_pll_is_prepared,
>> +	.prepare = ma35d1_clk_pll_prepare,
>> +	.unprepare = ma35d1_clk_pll_unprepare,
>> +	.set_rate = ma35d1_clk_pll_set_rate,
>> +	.recalc_rate = ma35d1_clk_pll_recalc_rate,
>> +	.round_rate = ma35d1_clk_pll_round_rate,
>> +};
>> +
>> +struct clk_hw *ma35d1_reg_clk_pll(enum ma35d1_pll_type type,
>> +				  u8 u8mode, const char *name,
>> +				  const char *parent,
>> +				  unsigned long targetFreq,
>> +				  void __iomem *base,
>> +				  struct regmap *regmap)
>> +{
>> +	struct ma35d1_clk_pll *pll;
>> +	struct clk_hw *hw;
>> +	struct clk_init_data init;
>> +	int ret;
>> +
>> +	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
>> +	if (!pll)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	pll->type = type;
>> +	pll->mode = u8mode;
>> +	pll->rate = targetFreq;
>> +	pll->ctl0_base = base + VSIPLL_CTL0;
>> +	pll->ctl1_base = base + VSIPLL_CTL1;
>> +	pll->ctl2_base = base + VSIPLL_CTL2;
>> +	pll->regmap = regmap;
>> +
>> +	init.name = name;
>> +	init.flags = 0;
>> +	init.parent_names = &parent;
>> +	init.num_parents = 1;
>> +	init.ops = &ma35d1_clk_pll_ops;
>> +	pll->hw.init = &init;
>> +	hw = &pll->hw;
>> +
>> +	ret = clk_hw_register(NULL, hw);
>> +	if (ret) {
>> +		pr_err("failed to register vsi-pll clock!!!\n");
> No need to use ! let alone 3 of them.
>
>> +		kfree(pll);
>> +		return ERR_PTR(ret);
>> +	}
>> +	return hw;
>> +}
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
>> new file mode 100644
>> index 000000000000..ac8154458b81
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1.c
>> @@ -0,0 +1,970 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/spinlock.h>
>> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
>> +
>> +#include "clk-ma35d1.h"
>> +
>> +DEFINE_SPINLOCK(ma35d1_lock);
>> +
>> +static const char *const ca35clk_sel_clks[] = {
>> +	"hxt", "capll", "ddrpll", "dummy"
>> +};
>> +
>> +static const char *const sysclk0_sel_clks[] = {
>> +	"epll_div2", "syspll"
>> +};
>> +
>> +static const char *const sysclk1_sel_clks[] = {
>> +	"hxt", "syspll"
>> +};
>> +
>> +static const char *const axiclk_sel_clks[] = {
>> +	"capll_div2", "capll_div4"
>> +};
>> +
>> +static const char *const ccap_sel_clks[] = {
>> +	"hxt", "vpll", "apll", "syspll"
>> +};
>> +
>> +static const char *const sdh_sel_clks[] = {
>> +	"syspll", "apll", "dummy", "dummy"
>> +};
>> +
>> +static const char *const dcu_sel_clks[] = {
>> +	"epll_div2", "syspll"
>> +};
>> +
>> +static const char *const gfx_sel_clks[] = {
>> +	"epll", "syspll"
>> +};
>> +
>> +static const char *const dbg_sel_clks[] = {
>> +	"hirc", "syspll"
>> +};
>> +
>> +static const char *const timer0_sel_clks[] = {
>> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer1_sel_clks[] = {
>> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer2_sel_clks[] = {
>> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer3_sel_clks[] = {
>> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer4_sel_clks[] = {
>> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer5_sel_clks[] = {
>> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer6_sel_clks[] = {
>> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer7_sel_clks[] = {
>> +	"hxt", "lxt", "pclk0", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer8_sel_clks[] = {
>> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer9_sel_clks[] = {
>> +	"hxt", "lxt", "pclk1", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer10_sel_clks[] = {
>> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const timer11_sel_clks[] = {
>> +	"hxt", "lxt", "pclk2", "dummy", "dummy", "lirc", "dummy", "hirc"
>> +};
>> +
>> +static const char *const uart_sel_clks[] = {
>> +	"hxt", "sysclk1_div2", "dummy", "dummy"
>> +};
>> +
>> +static const char *const wdt0_sel_clks[] = {
>> +	"dummy", "lxt", "pclk3_div4096", "lirc"
>> +};
>> +
>> +static const char *const wdt1_sel_clks[] = {
>> +	"dummy", "lxt", "pclk3_div4096", "lirc"
>> +};
>> +
>> +static const char *const wdt2_sel_clks[] = {
>> +	"dummy", "lxt", "pclk4_div4096", "lirc"
>> +};
>> +
>> +static const char *const wwdt0_sel_clks[] = {
>> +	"dummy", "dummy", "pclk3_div4096", "lirc"
>> +};
>> +
>> +static const char *const wwdt1_sel_clks[] = {
>> +	"dummy", "dummy", "pclk3_div4096", "lirc"
>> +};
>> +
>> +static const char *const wwdt2_sel_clks[] = {
>> +	"dummy", "dummy", "pclk4_div4096", "lirc"
>> +};
>> +
>> +static const char *const spi0_sel_clks[] = {
>> +	"pclk1", "apll", "dummy", "dummy"
>> +};
>> +
>> +static const char *const spi1_sel_clks[] = {
>> +	"pclk2", "apll", "dummy", "dummy"
>> +};
>> +
>> +static const char *const spi2_sel_clks[] = {
>> +	"pclk1", "apll", "dummy", "dummy"
>> +};
>> +
>> +static const char *const spi3_sel_clks[] = {
>> +	"pclk2", "apll", "dummy", "dummy"
>> +};
>> +
>> +static const char *const qspi0_sel_clks[] = {
>> +	"pclk0", "apll", "dummy", "dummy"
>> +};
>> +
>> +static const char *const qspi1_sel_clks[] = {
>> +	"pclk0", "apll", "dummy", "dummy"
>> +};
>> +
>> +static const char *const i2s0_sel_clks[] = {
>> +	"apll", "sysclk1_div2", "dummy", "dummy"
>> +};
>> +
>> +static const char *const i2s1_sel_clks[] = {
>> +	"apll", "sysclk1_div2", "dummy", "dummy"
>> +};
>> +
>> +static const char *const can_sel_clks[] = {
>> +	"apll", "vpll"
>> +};
>> +
>> +static const char *const cko_sel_clks[] = {
>> +	"hxt", "lxt", "hirc", "lirc", "capll_div4", "syspll",
>> +	"ddrpll", "epll_div2", "apll", "vpll", "dummy", "dummy",
>> +	"dummy", "dummy", "dummy", "dummy"
>> +};
>> +
>> +static const char *const smc_sel_clks[] = {
>> +	"hxt", "pclk4"
>> +};
>> +
>> +static const char *const kpi_sel_clks[] = {
>> +	"hxt", "lxt"
>> +};
>> +
>> +static const struct clk_div_table ip_div_table[] = {
>> +	{0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
>> +	{5, 12}, {6, 14}, {7, 16}, {0, 0},
>> +};
>> +
>> +static const struct clk_div_table eadc_div_table[] = {
>> +	{0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10},
>> +	{5, 12}, {6, 14}, {7, 16}, {8, 18},
>> +	{9, 20}, {10, 22}, {11, 24}, {12, 26},
>> +	{13, 28}, {14, 30}, {15, 32}, {0, 0},
>> +};
>> +
>> +static struct clk_hw **hws;
>> +static struct clk_hw_onecell_data *ma35d1_hw_data;
>> +
>> +static int ma35d1_clocks_probe(struct platform_device *pdev)
>> +{
>> +	int ret;
>> +	struct device *dev = &pdev->dev;
>> +	struct device_node *clk_node = dev->of_node;
>> +	void __iomem *clk_base;
>> +	struct regmap *regmap;
>> +	u32 pllmode[5] = { 0, 0, 0, 0, 0 };
>> +	u32 pllfreq[5] = { 0, 0, 0, 0, 0 };
>> +
>> +	dev_info(&pdev->dev, "Nuvoton MA35D1 Clock Driver\n");
>> +	ma35d1_hw_data = devm_kzalloc(&pdev->dev, struct_size(ma35d1_hw_data,
>> +				      hws, CLK_MAX_IDX), GFP_KERNEL);
>> +
>> +	if (WARN_ON(!ma35d1_hw_data))
>> +		return -ENOMEM;
>> +
>> +	ma35d1_hw_data->num = CLK_MAX_IDX;
>> +	hws = ma35d1_hw_data->hws;
>> +
>> +	clk_node = of_find_compatible_node(NULL, NULL, "nuvoton,ma35d1-clk");
>> +	clk_base = of_iomap(clk_node, 0);
>> +	of_node_put(clk_node);
>> +	if (!clk_base) {
>> +		pr_err("%s: could not map region\n", __func__);
>> +		return -ENOMEM;
>> +	}
>> +	regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
>> +						 "nuvoton,sys");
>> +	if (IS_ERR(regmap))
>> +		pr_warn("%s: Unable to get syscon\n", __func__);
> Don't print __func__ to user.


I will remove the __func__.


>
>> +
>> +	/* clock sources */
>> +	hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
>> +	hws[HXT_GATE] = ma35d1_clk_gate("hxt_gate", "hxt",
>> +					clk_base + REG_CLK_PWRCTL, 0);
>> +	hws[LXT] = ma35d1_clk_fixed("lxt", 32768);
>> +	hws[LXT_GATE] = ma35d1_clk_gate("lxt_gate", "lxt",
>> +					clk_base + REG_CLK_PWRCTL, 1);
>> +	hws[HIRC] = ma35d1_clk_fixed("hirc", 12000000);
>> +	hws[HIRC_GATE] = ma35d1_clk_gate("hirc_gate", "hirc",
>> +					 clk_base + REG_CLK_PWRCTL, 2);
>> +	hws[LIRC] = ma35d1_clk_fixed("lirc", 32000);
>> +	hws[LIRC_GATE] = ma35d1_clk_gate("lirc_gate", "lirc",
>> +					 clk_base + REG_CLK_PWRCTL, 3);
>> +
>> +	/* PLL */
>> +	of_property_read_u32_array(clk_node, "clock-pll-mode", pllmode,
>> +				   ARRAY_SIZE(pllmode));
>> +	of_property_read_u32_array(clk_node, "assigned-clock-rates", pllfreq,
>> +				   ARRAY_SIZE(pllfreq));
>> +
>> +	/* SMIC PLL */
>> +	hws[CAPLL] = ma35d1_reg_clk_pll(MA35D1_CAPLL, pllmode[0], "capll",
>> +					"hxt", pllfreq[0],
>> +					clk_base + REG_CLK_PLL0CTL0, regmap);
>> +	hws[SYSPLL] = ma35d1_clk_fixed("syspll", 180000000);
>> +
>> +	/* VSI PLL */
>> +	hws[DDRPLL] = ma35d1_reg_clk_pll(MA35D1_DDRPLL, pllmode[1], "ddrpll",
>> +					 "hxt", pllfreq[1],
>> +					 clk_base + REG_CLK_PLL2CTL0, regmap);
>> +	hws[APLL] = ma35d1_reg_clk_pll(MA35D1_APLL, pllmode[2], "apll", "hxt",
>> +				       pllfreq[2], clk_base + REG_CLK_PLL3CTL0,
>> +				       regmap);
>> +	hws[EPLL] = ma35d1_reg_clk_pll(MA35D1_EPLL, pllmode[3], "epll", "hxt",
>> +				       pllfreq[3], clk_base + REG_CLK_PLL4CTL0,
>> +				       regmap);
>> +	hws[VPLL] = ma35d1_reg_clk_pll(MA35D1_VPLL, pllmode[4], "vpll", "hxt",
>> +				       pllfreq[4], clk_base + REG_CLK_PLL5CTL0,
>> +				       regmap);
>> +	hws[EPLL_DIV2] = ma35d1_clk_fixed_factor("epll_div2", "epll", 1, 2);
>> +	hws[EPLL_DIV4] = ma35d1_clk_fixed_factor("epll_div4", "epll", 1, 4);
>> +	hws[EPLL_DIV8] = ma35d1_clk_fixed_factor("epll_div8", "epll", 1, 8);
>> +
>> +	/* CA35 */
>> +	hws[CA35CLK_MUX] = ma35d1_clk_mux("ca35clk_mux",
>> +					  clk_base + REG_CLK_CLKSEL0, 0,
>> +					  2, ca35clk_sel_clks,
>> +					  ARRAY_SIZE(ca35clk_sel_clks));
>> +
>> +	/* AXI */
>> +	hws[AXICLK_DIV2] = ma35d1_clk_fixed_factor("capll_div2", "ca35clk_mux",
>> +						   1, 2);
>> +	hws[AXICLK_DIV4] = ma35d1_clk_fixed_factor("capll_div4", "ca35clk_mux",
>> +						   1, 4);
>> +	hws[AXICLK_MUX] = ma35d1_clk_mux("axiclk_mux",
>> +					 clk_base + REG_CLK_CLKDIV0,
>> +					 26, 1, axiclk_sel_clks,
>> +					 ARRAY_SIZE(axiclk_sel_clks));
>> +
>> +	/* SYSCLK0 & SYSCLK1 */
>> +	hws[SYSCLK0_MUX] = ma35d1_clk_mux("sysclk0_mux",
>> +					  clk_base + REG_CLK_CLKSEL0,
>> +					  2, 1, sysclk0_sel_clks,
>> +					  ARRAY_SIZE(sysclk0_sel_clks));
>> +	hws[SYSCLK1_MUX] = ma35d1_clk_mux("sysclk1_mux",
>> +					  clk_base + REG_CLK_CLKSEL0,
>> +					  4, 1, sysclk1_sel_clks,
>> +					  ARRAY_SIZE(sysclk1_sel_clks));
>> +	hws[SYSCLK1_DIV2] = ma35d1_clk_fixed_factor("sysclk1_div2",
>> +						    "sysclk1_mux", 1, 2);
>> +
>> +	/* HCLK0~3 & PCLK0~4 */
>> +	hws[HCLK0] = ma35d1_clk_fixed_factor("hclk0", "sysclk1_mux", 1, 1);
>> +	hws[HCLK1] = ma35d1_clk_fixed_factor("hclk1", "sysclk1_mux", 1, 1);
>> +	hws[HCLK2] = ma35d1_clk_fixed_factor("hclk2", "sysclk1_mux", 1, 1);
>> +	hws[PCLK0] = ma35d1_clk_fixed_factor("pclk0", "sysclk1_mux", 1, 1);
>> +	hws[PCLK1] = ma35d1_clk_fixed_factor("pclk1", "sysclk1_mux", 1, 1);
>> +	hws[PCLK2] = ma35d1_clk_fixed_factor("pclk2", "sysclk1_mux", 1, 1);
>> +
>> +	hws[HCLK3] = ma35d1_clk_fixed_factor("hclk3", "sysclk1_mux", 1, 2);
>> +	hws[PCLK3] = ma35d1_clk_fixed_factor("pclk3", "sysclk1_mux", 1, 2);
>> +	hws[PCLK4] = ma35d1_clk_fixed_factor("pclk4", "sysclk1_mux", 1, 2);
>> +
>> +	hws[USBPHY0] = ma35d1_clk_fixed("usbphy0", 480000000);
>> +	hws[USBPHY1] = ma35d1_clk_fixed("usbphy1", 480000000);
>> +
>> +	/* DDR */
>> +	hws[DDR0_GATE] = ma35d1_clk_gate("ddr0_gate", "ddrpll",
>> +					 clk_base + REG_CLK_SYSCLK0, 4);
>> +	hws[DDR6_GATE] = ma35d1_clk_gate("ddr6_gate", "ddrpll",
>> +					 clk_base + REG_CLK_SYSCLK0, 5);
>> +
>> +	/* CAN0 */
>> +	hws[CAN0_MUX] = ma35d1_clk_mux("can0_mux", clk_base + REG_CLK_CLKSEL4,
>> +				       16, 1, can_sel_clks,
>> +				       ARRAY_SIZE(can_sel_clks));
>> +	hws[CAN0_DIV] = ma35d1_clk_divider_table("can0_div", "can0_mux",
>> +						 clk_base + REG_CLK_CLKDIV0,
>> +						 0, 3, ip_div_table);
>> +	hws[CAN0_GATE] = ma35d1_clk_gate("can0_gate", "can0_div",
>> +					 clk_base + REG_CLK_SYSCLK0, 8);
>> +
>> +	/* CAN1 */
>> +	hws[CAN1_MUX] = ma35d1_clk_mux("can1_mux", clk_base + REG_CLK_CLKSEL4,
>> +				       17, 1, can_sel_clks,
>> +				       ARRAY_SIZE(can_sel_clks));
>> +	hws[CAN1_DIV] = ma35d1_clk_divider_table("can1_div", "can1_mux",
>> +						 clk_base + REG_CLK_CLKDIV0,
>> +						 4, 3, ip_div_table);
>> +	hws[CAN1_GATE] = ma35d1_clk_gate("can1_gate", "can1_div",
>> +					 clk_base + REG_CLK_SYSCLK0, 9);
>> +
>> +	/* CAN2 */
>> +	hws[CAN2_MUX] = ma35d1_clk_mux("can2_mux", clk_base + REG_CLK_CLKSEL4,
>> +				       18, 1, can_sel_clks,
>> +				       ARRAY_SIZE(can_sel_clks));
>> +	hws[CAN2_DIV] = ma35d1_clk_divider_table("can2_div", "can2_mux",
>> +						 clk_base + REG_CLK_CLKDIV0,
>> +						 8, 3, ip_div_table);
>> +	hws[CAN2_GATE] = ma35d1_clk_gate("can2_gate", "can2_div",
>> +					 clk_base + REG_CLK_SYSCLK0, 10);
>> +
>> +	/* CAN3 */
>> +	hws[CAN3_MUX] = ma35d1_clk_mux("can3_mux", clk_base + REG_CLK_CLKSEL4,
>> +				       19, 1, can_sel_clks,
>> +				       ARRAY_SIZE(can_sel_clks));
>> +	hws[CAN3_DIV] = ma35d1_clk_divider_table("can3_div", "can3_mux",
>> +						 clk_base + REG_CLK_CLKDIV0,
>> +						 12, 3, ip_div_table);
>> +	hws[CAN3_GATE] = ma35d1_clk_gate("can3_gate", "can3_div",
>> +					 clk_base + REG_CLK_SYSCLK0, 11);
>> +
>> +	/* SDH0 */
>> +	hws[SDH0_MUX] = ma35d1_clk_mux("sdh0_mux", clk_base + REG_CLK_CLKSEL0,
>> +				       16, 2, sdh_sel_clks,
>> +				       ARRAY_SIZE(sdh_sel_clks));
>> +	hws[SDH0_GATE] = ma35d1_clk_gate("sdh0_gate", "sdh0_mux",
>> +					 clk_base + REG_CLK_SYSCLK0, 16);
>> +
>> +	/* SDH1 */
>> +	hws[SDH1_MUX] = ma35d1_clk_mux("sdh1_mux", clk_base + REG_CLK_CLKSEL0,
>> +				       18, 2, sdh_sel_clks,
>> +				       ARRAY_SIZE(sdh_sel_clks));
>> +	hws[SDH1_GATE] = ma35d1_clk_gate("sdh1_gate", "sdh1_mux",
>> +					 clk_base + REG_CLK_SYSCLK0, 17);
>> +
>> +	/* NAND */
>> +	hws[NAND_GATE] = ma35d1_clk_gate("nand_gate", "hclk1",
>> +					 clk_base + REG_CLK_SYSCLK0, 18);
>> +
>> +	/* USB */
>> +	hws[USBD_GATE] = ma35d1_clk_gate("usbd_gate", "usbphy0",
>> +					 clk_base + REG_CLK_SYSCLK0, 19);
>> +	hws[USBH_GATE] = ma35d1_clk_gate("usbh_gate", "usbphy0",
>> +					 clk_base + REG_CLK_SYSCLK0, 20);
>> +	hws[HUSBH0_GATE] = ma35d1_clk_gate("husbh0_gate", "usbphy0",
>> +					   clk_base + REG_CLK_SYSCLK0, 21);
>> +	hws[HUSBH1_GATE] = ma35d1_clk_gate("husbh1_gate", "usbphy0",
>> +					   clk_base + REG_CLK_SYSCLK0, 22);
>> +
>> +	/* GFX */
>> +	hws[GFX_MUX] = ma35d1_clk_mux("gfx_mux", clk_base + REG_CLK_CLKSEL0,
>> +				      26, 1, gfx_sel_clks,
>> +				      ARRAY_SIZE(gfx_sel_clks));
>> +	hws[GFX_GATE] = ma35d1_clk_gate("gfx_gate", "gfx_mux",
>> +					clk_base + REG_CLK_SYSCLK0, 24);
>> +
>> +	/* VC8K */
>> +	hws[VC8K_GATE] = ma35d1_clk_gate("vc8k_gate", "sysclk0_mux",
>> +					 clk_base + REG_CLK_SYSCLK0, 25);
>> +
>> +	/* DCU */
>> +	hws[DCU_MUX] = ma35d1_clk_mux("dcu_mux", clk_base + REG_CLK_CLKSEL0,
>> +				      24, 1, dcu_sel_clks,
>> +				      ARRAY_SIZE(dcu_sel_clks));
>> +	hws[DCU_GATE] = ma35d1_clk_gate("dcu_gate", "dcu_mux",
>> +					clk_base + REG_CLK_SYSCLK0, 26);
>> +
>> +	/* DCUP */
>> +	hws[DCUP_DIV] = ma35d1_clk_divider_table("dcup_div", "vpll",
>> +						 clk_base + REG_CLK_CLKDIV0,
>> +						 16, 3, ip_div_table);
>> +
>> +	/* EMAC0 */
>> +	hws[EMAC0_GATE] = ma35d1_clk_gate("emac0_gate", "epll_div2",
>> +					  clk_base + REG_CLK_SYSCLK0, 27);
>> +
>> +	/* EMAC1 */
>> +	hws[EMAC1_GATE] = ma35d1_clk_gate("emac1_gate", "epll_div2",
>> +					  clk_base + REG_CLK_SYSCLK0, 28);
>> +
>> +	/* CCAP0 */
>> +	hws[CCAP0_MUX] = ma35d1_clk_mux("ccap0_mux",
>> +					clk_base + REG_CLK_CLKSEL0,
>> +					12, 1, ccap_sel_clks,
>> +					ARRAY_SIZE(ccap_sel_clks));
>> +	hws[CCAP0_DIV] = ma35d1_clk_divider("ccap0_div", "ccap0_mux",
>> +					    clk_base + REG_CLK_CLKDIV1, 8, 4);
>> +	hws[CCAP0_GATE] = ma35d1_clk_gate("ccap0_gate", "ccap0_div",
>> +					  clk_base + REG_CLK_SYSCLK0, 29);
>> +
>> +	/* CCAP1 */
>> +	hws[CCAP1_MUX] = ma35d1_clk_mux("ccap1_mux",
>> +					clk_base + REG_CLK_CLKSEL0,
>> +					14, 1, ccap_sel_clks,
>> +					ARRAY_SIZE(ccap_sel_clks));
>> +	hws[CCAP1_DIV] = ma35d1_clk_divider("ccap1_div", "ccap1_mux",
>> +					    clk_base + REG_CLK_CLKDIV1,
>> +					    12, 4);
>> +	hws[CCAP1_GATE] = ma35d1_clk_gate("ccap1_gate", "ccap1_div",
>> +					  clk_base + REG_CLK_SYSCLK0, 30);
>> +
>> +	/* PDMA0~3 */
>> +	hws[PDMA0_GATE] = ma35d1_clk_gate("pdma0_gate", "hclk0",
>> +					  clk_base + REG_CLK_SYSCLK1, 0);
>> +	hws[PDMA1_GATE] = ma35d1_clk_gate("pdma1_gate", "hclk0",
>> +					  clk_base + REG_CLK_SYSCLK1, 1);
>> +	hws[PDMA2_GATE] = ma35d1_clk_gate("pdma2_gate", "hclk0",
>> +					  clk_base + REG_CLK_SYSCLK1, 2);
>> +	hws[PDMA3_GATE] = ma35d1_clk_gate("pdma3_gate", "hclk0",
>> +					  clk_base + REG_CLK_SYSCLK1, 3);
>> +
>> +	/* WH0~1 */
>> +	hws[WH0_GATE] = ma35d1_clk_gate("wh0_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 4);
>> +	hws[WH1_GATE] = ma35d1_clk_gate("wh1_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 5);
>> +
>> +	/* HWS */
>> +	hws[HWS_GATE] = ma35d1_clk_gate("hws_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 6);
>> +
>> +	/* EBI */
>> +	hws[EBI_GATE] = ma35d1_clk_gate("ebi_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 7);
>> +
>> +	/* SRAM0~1 */
>> +	hws[SRAM0_GATE] = ma35d1_clk_gate("sram0_gate", "hclk0",
>> +					  clk_base + REG_CLK_SYSCLK1, 8);
>> +	hws[SRAM1_GATE] = ma35d1_clk_gate("sram1_gate", "hclk0",
>> +					  clk_base + REG_CLK_SYSCLK1, 9);
>> +
>> +	/* ROM */
>> +	hws[ROM_GATE] = ma35d1_clk_gate("rom_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 10);
>> +
>> +	/* TRA */
>> +	hws[TRA_GATE] = ma35d1_clk_gate("tra_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 11);
>> +
>> +	/* DBG */
>> +	hws[DBG_MUX] = ma35d1_clk_mux("dbg_mux", clk_base + REG_CLK_CLKSEL0,
>> +				      27, 1, dbg_sel_clks,
>> +				      ARRAY_SIZE(dbg_sel_clks));
>> +	hws[DBG_GATE] = ma35d1_clk_gate("dbg_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 12);
>> +
>> +	/* CLKO */
>> +	hws[CKO_MUX] = ma35d1_clk_mux("cko_mux", clk_base + REG_CLK_CLKSEL4,
>> +				      24, 4, cko_sel_clks,
>> +				      ARRAY_SIZE(cko_sel_clks));
>> +	hws[CKO_DIV] = ma35d1_clk_divider_pow2("cko_div", "cko_mux",
>> +					       clk_base + REG_CLK_CLKOCTL,
>> +					       0, 4);
>> +	hws[CKO_GATE] = ma35d1_clk_gate("cko_gate", "cko_div",
>> +					clk_base + REG_CLK_SYSCLK1, 13);
>> +
>> +	/* GTMR */
>> +	hws[GTMR_GATE] = ma35d1_clk_gate("gtmr_gate", "hirc",
>> +					 clk_base + REG_CLK_SYSCLK1, 14);
>> +
>> +	/* GPIO */
>> +	hws[GPA_GATE] = ma35d1_clk_gate("gpa_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 16);
>> +	hws[GPB_GATE] = ma35d1_clk_gate("gpb_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 17);
>> +	hws[GPC_GATE] = ma35d1_clk_gate("gpc_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 18);
>> +	hws[GPD_GATE] = ma35d1_clk_gate("gpd_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 19);
>> +	hws[GPE_GATE] = ma35d1_clk_gate("gpe_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 20);
>> +	hws[GPF_GATE] = ma35d1_clk_gate("gpf_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 21);
>> +	hws[GPG_GATE] = ma35d1_clk_gate("gpg_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 22);
>> +	hws[GPH_GATE] = ma35d1_clk_gate("gph_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 23);
>> +	hws[GPI_GATE] = ma35d1_clk_gate("gpi_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 24);
>> +	hws[GPJ_GATE] = ma35d1_clk_gate("gpj_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 25);
>> +	hws[GPK_GATE] = ma35d1_clk_gate("gpk_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 26);
>> +	hws[GPL_GATE] = ma35d1_clk_gate("gpl_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 27);
>> +	hws[GPM_GATE] = ma35d1_clk_gate("gpm_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 28);
>> +	hws[GPN_GATE] = ma35d1_clk_gate("gpn_gate", "hclk0",
>> +					clk_base + REG_CLK_SYSCLK1, 29);
>> +
>> +	/* TIMER0~11 */
>> +	hws[TMR0_MUX] = ma35d1_clk_mux("tmr0_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       0, 3, timer0_sel_clks,
>> +				       ARRAY_SIZE(timer0_sel_clks));
>> +	hws[TMR0_GATE] = ma35d1_clk_gate("tmr0_gate", "tmr0_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 0);
>> +	hws[TMR1_MUX] = ma35d1_clk_mux("tmr1_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       4, 3, timer1_sel_clks,
>> +				       ARRAY_SIZE(timer1_sel_clks));
>> +	hws[TMR1_GATE] = ma35d1_clk_gate("tmr1_gate", "tmr1_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 1);
>> +	hws[TMR2_MUX] = ma35d1_clk_mux("tmr2_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       8, 3, timer2_sel_clks,
>> +				       ARRAY_SIZE(timer2_sel_clks));
>> +	hws[TMR2_GATE] = ma35d1_clk_gate("tmr2_gate", "tmr2_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 2);
>> +	hws[TMR3_MUX] = ma35d1_clk_mux("tmr3_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       12, 3, timer3_sel_clks,
>> +				       ARRAY_SIZE(timer3_sel_clks));
>> +	hws[TMR3_GATE] = ma35d1_clk_gate("tmr3_gate", "tmr3_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 3);
>> +	hws[TMR4_MUX] = ma35d1_clk_mux("tmr4_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       16, 3, timer4_sel_clks,
>> +				       ARRAY_SIZE(timer4_sel_clks));
>> +	hws[TMR4_GATE] = ma35d1_clk_gate("tmr4_gate", "tmr4_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 4);
>> +	hws[TMR5_MUX] = ma35d1_clk_mux("tmr5_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       20, 3, timer5_sel_clks,
>> +				       ARRAY_SIZE(timer5_sel_clks));
>> +	hws[TMR5_GATE] = ma35d1_clk_gate("tmr5_gate", "tmr5_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 5);
>> +	hws[TMR6_MUX] = ma35d1_clk_mux("tmr6_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       24, 3, timer6_sel_clks,
>> +				       ARRAY_SIZE(timer6_sel_clks));
>> +	hws[TMR6_GATE] = ma35d1_clk_gate("tmr6_gate", "tmr6_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 6);
>> +	hws[TMR7_MUX] = ma35d1_clk_mux("tmr7_mux", clk_base + REG_CLK_CLKSEL1,
>> +				       28, 3, timer7_sel_clks,
>> +				       ARRAY_SIZE(timer7_sel_clks));
>> +	hws[TMR7_GATE] = ma35d1_clk_gate("tmr7_gate", "tmr7_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 7);
>> +	hws[TMR8_MUX] = ma35d1_clk_mux("tmr8_mux", clk_base + REG_CLK_CLKSEL2,
>> +				       0, 3, timer8_sel_clks,
>> +				       ARRAY_SIZE(timer8_sel_clks));
>> +	hws[TMR8_GATE] = ma35d1_clk_gate("tmr8_gate", "tmr8_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 8);
>> +	hws[TMR9_MUX] = ma35d1_clk_mux("tmr9_mux", clk_base + REG_CLK_CLKSEL2,
>> +				       4, 3, timer9_sel_clks,
>> +				       ARRAY_SIZE(timer9_sel_clks));
>> +	hws[TMR9_GATE] = ma35d1_clk_gate("tmr9_gate", "tmr9_mux",
>> +					 clk_base + REG_CLK_APBCLK0, 9);
>> +	hws[TMR10_MUX] = ma35d1_clk_mux("tmr10_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					8, 3, timer10_sel_clks,
>> +					ARRAY_SIZE(timer10_sel_clks));
>> +	hws[TMR10_GATE] = ma35d1_clk_gate("tmr10_gate", "tmr10_mux",
>> +					  clk_base + REG_CLK_APBCLK0, 10);
>> +	hws[TMR11_MUX] = ma35d1_clk_mux("tmr11_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					12, 3, timer11_sel_clks,
>> +					ARRAY_SIZE(timer11_sel_clks));
>> +	hws[TMR11_GATE] = ma35d1_clk_gate("tmr11_gate", "tmr11_mux",
>> +					  clk_base + REG_CLK_APBCLK0, 11);
>> +
>> +	/* UART0~16 */
>> +	hws[UART0_MUX] = ma35d1_clk_mux("uart0_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					16, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART0_DIV] = ma35d1_clk_divider("uart0_div", "uart0_mux",
>> +					    clk_base + REG_CLK_CLKDIV1,
>> +					    16, 4);
>> +	hws[UART0_GATE] = ma35d1_clk_gate("uart0_gate", "uart0_div",
>> +					  clk_base + REG_CLK_APBCLK0, 12);
>> +	hws[UART1_MUX] = ma35d1_clk_mux("uart1_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					18, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART1_DIV] = ma35d1_clk_divider("uart1_div", "uart1_mux",
>> +					    clk_base + REG_CLK_CLKDIV1,
>> +					    20, 4);
>> +	hws[UART1_GATE] = ma35d1_clk_gate("uart1_gate", "uart1_div",
>> +					  clk_base + REG_CLK_APBCLK0, 13);
>> +	hws[UART2_MUX] = ma35d1_clk_mux("uart2_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					20, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART2_DIV] = ma35d1_clk_divider("uart2_div", "uart2_mux",
>> +					    clk_base + REG_CLK_CLKDIV1,
>> +					    24, 4);
>> +	hws[UART2_GATE] = ma35d1_clk_gate("uart2_gate", "uart2_div",
>> +					  clk_base + REG_CLK_APBCLK0, 14);
>> +	hws[UART3_MUX] = ma35d1_clk_mux("uart3_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					22, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART3_DIV] = ma35d1_clk_divider("uart3_div", "uart3_mux",
>> +					    clk_base + REG_CLK_CLKDIV1,
>> +					    28, 4);
>> +	hws[UART3_GATE] = ma35d1_clk_gate("uart3_gate", "uart3_div",
>> +					  clk_base + REG_CLK_APBCLK0, 15);
>> +	hws[UART4_MUX] = ma35d1_clk_mux("uart4_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					24, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART4_DIV] = ma35d1_clk_divider("uart4_div", "uart4_mux",
>> +					    clk_base + REG_CLK_CLKDIV2,
>> +					    0, 4);
>> +	hws[UART4_GATE] = ma35d1_clk_gate("uart4_gate", "uart4_div",
>> +					  clk_base + REG_CLK_APBCLK0, 16);
>> +	hws[UART5_MUX] = ma35d1_clk_mux("uart5_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					26, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART5_DIV] = ma35d1_clk_divider("uart5_div", "uart5_mux",
>> +					    clk_base + REG_CLK_CLKDIV2,
>> +					    4, 4);
>> +	hws[UART5_GATE] = ma35d1_clk_gate("uart5_gate", "uart5_div",
>> +					  clk_base + REG_CLK_APBCLK0, 17);
>> +	hws[UART6_MUX] = ma35d1_clk_mux("uart6_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					28, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART6_DIV] = ma35d1_clk_divider("uart6_div", "uart6_mux",
>> +					    clk_base + REG_CLK_CLKDIV2,
>> +					    8, 4);
>> +	hws[UART6_GATE] = ma35d1_clk_gate("uart6_gate", "uart6_div",
>> +					  clk_base + REG_CLK_APBCLK0, 18);
>> +	hws[UART7_MUX] = ma35d1_clk_mux("uart7_mux",
>> +					clk_base + REG_CLK_CLKSEL2,
>> +					30, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART7_DIV] = ma35d1_clk_divider("uart7_div", "uart7_mux",
>> +					    clk_base + REG_CLK_CLKDIV2,
>> +					    12, 4);
>> +	hws[UART7_GATE] = ma35d1_clk_gate("uart7_gate", "uart7_div",
>> +					  clk_base + REG_CLK_APBCLK0, 19);
>> +	hws[UART8_MUX] = ma35d1_clk_mux("uart8_mux",
>> +					clk_base + REG_CLK_CLKSEL3,
>> +					0, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART8_DIV] = ma35d1_clk_divider("uart8_div", "uart8_mux",
>> +					    clk_base + REG_CLK_CLKDIV2,
>> +					    16, 4);
>> +	hws[UART8_GATE] = ma35d1_clk_gate("uart8_gate", "uart8_div",
>> +					  clk_base + REG_CLK_APBCLK0, 20);
>> +	hws[UART9_MUX] = ma35d1_clk_mux("uart9_mux",
>> +					clk_base + REG_CLK_CLKSEL3,
>> +					2, 2, uart_sel_clks,
>> +					ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART9_DIV] = ma35d1_clk_divider("uart9_div", "uart9_mux",
>> +					    clk_base + REG_CLK_CLKDIV2,
>> +					    20, 4);
>> +	hws[UART9_GATE] = ma35d1_clk_gate("uart9_gate", "uart9_div",
>> +					  clk_base + REG_CLK_APBCLK0, 21);
>> +	hws[UART10_MUX] = ma35d1_clk_mux("uart10_mux",
>> +					 clk_base + REG_CLK_CLKSEL3,
>> +					 4, 2, uart_sel_clks,
>> +					 ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART10_DIV] = ma35d1_clk_divider("uart10_div", "uart10_mux",
>> +					     clk_base + REG_CLK_CLKDIV2,
>> +					     24, 4);
>> +	hws[UART10_GATE] = ma35d1_clk_gate("uart10_gate", "uart10_div",
>> +					   clk_base + REG_CLK_APBCLK0, 22);
>> +	hws[UART11_MUX] = ma35d1_clk_mux("uart11_mux",
>> +					 clk_base + REG_CLK_CLKSEL3,
>> +					 6, 2, uart_sel_clks,
>> +					 ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART11_DIV] = ma35d1_clk_divider("uart11_div", "uart11_mux",
>> +					     clk_base + REG_CLK_CLKDIV2,
>> +					     28, 4);
>> +	hws[UART11_GATE] = ma35d1_clk_gate("uart11_gate", "uart11_div",
>> +					   clk_base + REG_CLK_APBCLK0, 23);
>> +	hws[UART12_MUX] = ma35d1_clk_mux("uart12_mux",
>> +					 clk_base + REG_CLK_CLKSEL3,
>> +					 8, 2, uart_sel_clks,
>> +					 ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART12_DIV] = ma35d1_clk_divider("uart12_div", "uart12_mux",
>> +					     clk_base + REG_CLK_CLKDIV3,
>> +					     0, 4);
>> +	hws[UART12_GATE] = ma35d1_clk_gate("uart12_gate", "uart12_div",
>> +					   clk_base + REG_CLK_APBCLK0, 24);
>> +	hws[UART13_MUX] = ma35d1_clk_mux("uart13_mux",
>> +					 clk_base + REG_CLK_CLKSEL3,
>> +					 10, 2, uart_sel_clks,
>> +					 ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART13_DIV] = ma35d1_clk_divider("uart13_div", "uart13_mux",
>> +					     clk_base + REG_CLK_CLKDIV3,
>> +					     4, 4);
>> +	hws[UART13_GATE] = ma35d1_clk_gate("uart13_gate", "uart13_div",
>> +					   clk_base + REG_CLK_APBCLK0, 25);
>> +	hws[UART14_MUX] = ma35d1_clk_mux("uart14_mux",
>> +					 clk_base + REG_CLK_CLKSEL3,
>> +					 12, 2, uart_sel_clks,
>> +					 ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART14_DIV] = ma35d1_clk_divider("uart14_div", "uart14_mux",
>> +					     clk_base + REG_CLK_CLKDIV3,
>> +					     8, 4);
>> +	hws[UART14_GATE] = ma35d1_clk_gate("uart14_gate", "uart14_div",
>> +					   clk_base + REG_CLK_APBCLK0, 26);
>> +	hws[UART15_MUX] = ma35d1_clk_mux("uart15_mux",
>> +					 clk_base + REG_CLK_CLKSEL3,
>> +					 14, 2, uart_sel_clks,
>> +					 ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART15_DIV] = ma35d1_clk_divider("uart15_div", "uart15_mux",
>> +					     clk_base + REG_CLK_CLKDIV3,
>> +					     12, 4);
>> +	hws[UART15_GATE] = ma35d1_clk_gate("uart15_gate", "uart15_div",
>> +					   clk_base + REG_CLK_APBCLK0, 27);
>> +	hws[UART16_MUX] = ma35d1_clk_mux("uart16_mux",
>> +					 clk_base + REG_CLK_CLKSEL3,
>> +					 16, 2, uart_sel_clks,
>> +					 ARRAY_SIZE(uart_sel_clks));
>> +	hws[UART16_DIV] = ma35d1_clk_divider("uart16_div", "uart16_mux",
>> +					     clk_base + REG_CLK_CLKDIV3,
>> +					     16, 4);
>> +	hws[UART16_GATE] = ma35d1_clk_gate("uart16_gate", "uart16_div",
>> +					   clk_base + REG_CLK_APBCLK0, 28);
>> +
>> +	/* RTC */
>> +	hws[RTC_GATE] = ma35d1_clk_gate("rtc_gate", "lxt",
>> +					clk_base + REG_CLK_APBCLK0, 29);
>> +
>> +	/* DDRP */
>> +	hws[DDR_GATE] = ma35d1_clk_gate("ddr_gate", "ddrpll",
>> +					clk_base + REG_CLK_APBCLK0, 30);
>> +
>> +	/* KPI */
>> +	hws[KPI_MUX] = ma35d1_clk_mux("kpi_mux", clk_base + REG_CLK_CLKSEL4,
>> +				      30, 1, kpi_sel_clks,
>> +				      ARRAY_SIZE(kpi_sel_clks));
>> +	hws[KPI_DIV] = ma35d1_clk_divider("kpi_div", "kpi_mux",
>> +					  clk_base + REG_CLK_CLKDIV4,
>> +					  24, 8);
>> +	hws[KPI_GATE] = ma35d1_clk_gate("kpi_gate", "kpi_div",
>> +					clk_base + REG_CLK_APBCLK0, 31);
>> +
>> +	/* I2C0~5 */
>> +	hws[I2C0_GATE] = ma35d1_clk_gate("i2c0_gate", "pclk0",
>> +					 clk_base + REG_CLK_APBCLK1, 0);
>> +	hws[I2C1_GATE] = ma35d1_clk_gate("i2c1_gate", "pclk1",
>> +					 clk_base + REG_CLK_APBCLK1, 1);
>> +	hws[I2C2_GATE] = ma35d1_clk_gate("i2c2_gate", "pclk2",
>> +					 clk_base + REG_CLK_APBCLK1, 2);
>> +	hws[I2C3_GATE] = ma35d1_clk_gate("i2c3_gate", "pclk0",
>> +					 clk_base + REG_CLK_APBCLK1, 3);
>> +	hws[I2C4_GATE] = ma35d1_clk_gate("i2c4_gate", "pclk1",
>> +					 clk_base + REG_CLK_APBCLK1, 4);
>> +	hws[I2C5_GATE] = ma35d1_clk_gate("i2c5_gate", "pclk2",
>> +					 clk_base + REG_CLK_APBCLK1, 5);
>> +
>> +	/* QSPI0~1 */
>> +	hws[QSPI0_MUX] = ma35d1_clk_mux("qspi0_mux",
>> +					clk_base + REG_CLK_CLKSEL4,
>> +					8, 2, qspi0_sel_clks,
>> +					ARRAY_SIZE(qspi0_sel_clks));
>> +	hws[QSPI0_GATE] = ma35d1_clk_gate("qspi0_gate", "qspi0_mux",
>> +					  clk_base + REG_CLK_APBCLK1, 6);
>> +	hws[QSPI1_MUX] = ma35d1_clk_mux("qspi1_mux",
>> +					clk_base + REG_CLK_CLKSEL4,
>> +					10, 2, qspi1_sel_clks,
>> +					ARRAY_SIZE(qspi1_sel_clks));
>> +	hws[QSPI1_GATE] = ma35d1_clk_gate("qspi1_gate", "qspi1_mux",
>> +					  clk_base + REG_CLK_APBCLK1, 7);
>> +
>> +	/* SMC0~1 */
>> +	hws[SMC0_MUX] = ma35d1_clk_mux("smc0_mux",
>> +					clk_base + REG_CLK_CLKSEL4,
>> +					28, 1, smc_sel_clks,
>> +					ARRAY_SIZE(smc_sel_clks));
>> +	hws[SMC0_DIV] = ma35d1_clk_divider("smc0_div", "smc0_mux",
>> +					   clk_base + REG_CLK_CLKDIV1,
>> +					   0, 4);
>> +	hws[SMC0_GATE] = ma35d1_clk_gate("smc0_gate", "smc0_div",
>> +					 clk_base + REG_CLK_APBCLK1, 12);
>> +
>> +	hws[SMC1_MUX] = ma35d1_clk_mux("smc1_mux",
>> +					 clk_base + REG_CLK_CLKSEL4,
>> +					 29, 1, smc_sel_clks,
>> +					 ARRAY_SIZE(smc_sel_clks));
>> +	hws[SMC1_DIV] = ma35d1_clk_divider("smc1_div", "smc1_mux",
>> +					   clk_base + REG_CLK_CLKDIV1,
>> +					   4, 4);
>> +	hws[SMC1_GATE] = ma35d1_clk_gate("smc1_gate", "smc1_div",
>> +					 clk_base + REG_CLK_APBCLK1, 13);
>> +
>> +	/* WDT0~2 */
>> +	hws[WDT0_MUX] = ma35d1_clk_mux("wdt0_mux",
>> +				       clk_base + REG_CLK_CLKSEL3,
>> +				       20, 2, wdt0_sel_clks,
>> +				       ARRAY_SIZE(wdt0_sel_clks));
>> +	hws[WDT0_GATE] = ma35d1_clk_gate("wdt0_gate", "wdt0_mux",
>> +					 clk_base + REG_CLK_APBCLK1, 16);
>> +	hws[WDT1_MUX] = ma35d1_clk_mux("wdt1_mux",
>> +				       clk_base + REG_CLK_CLKSEL3,
>> +				       24, 2, wdt1_sel_clks,
>> +				       ARRAY_SIZE(wdt1_sel_clks));
>> +	hws[WDT1_GATE] = ma35d1_clk_gate("wdt1_gate", "wdt1_mux",
>> +					 clk_base + REG_CLK_APBCLK1, 17);
>> +	hws[WDT2_MUX] = ma35d1_clk_mux("wdt2_mux",
>> +				       clk_base + REG_CLK_CLKSEL3,
>> +				       28, 2, wdt2_sel_clks,
>> +				       ARRAY_SIZE(wdt2_sel_clks));
>> +	hws[WDT2_GATE] = ma35d1_clk_gate("wdt2_gate", "wdt2_mux",
>> +				       clk_base + REG_CLK_APBCLK1, 18);
>> +
>> +	/* WWDT0~2 */
>> +	hws[WWDT0_MUX] = ma35d1_clk_mux("wwdt0_mux",
>> +					clk_base + REG_CLK_CLKSEL3,
>> +					22, 2, wwdt0_sel_clks,
>> +					ARRAY_SIZE(wwdt0_sel_clks));
>> +	hws[WWDT1_MUX] = ma35d1_clk_mux("wwdt1_mux",
>> +					clk_base + REG_CLK_CLKSEL3,
>> +					26, 2, wwdt1_sel_clks,
>> +					ARRAY_SIZE(wwdt1_sel_clks));
>> +	hws[WWDT2_MUX] = ma35d1_clk_mux("wwdt2_mux",
>> +					clk_base + REG_CLK_CLKSEL3,
>> +					30, 2, wwdt2_sel_clks,
>> +					ARRAY_SIZE(wwdt2_sel_clks));
>> +
>> +	/* EPWM0~2 */
>> +	hws[EPWM0_GATE] = ma35d1_clk_gate("epwm0_gate", "pclk1",
>> +					  clk_base + REG_CLK_APBCLK1, 24);
>> +	hws[EPWM1_GATE] = ma35d1_clk_gate("epwm1_gate", "pclk2",
>> +					  clk_base + REG_CLK_APBCLK1, 25);
>> +	hws[EPWM2_GATE] = ma35d1_clk_gate("epwm2_gate", "pclk1",
>> +					  clk_base + REG_CLK_APBCLK1, 26);
>> +
>> +	/* I2S0~1 */
>> +	hws[I2S0_MUX] = ma35d1_clk_mux("i2s0_mux",
>> +				       clk_base + REG_CLK_CLKSEL4,
>> +				       12, 2, i2s0_sel_clks,
>> +				       ARRAY_SIZE(i2s0_sel_clks));
>> +	hws[I2S0_GATE] = ma35d1_clk_gate("i2s0_gate", "i2s0_mux",
>> +					 clk_base + REG_CLK_APBCLK2, 0);
>> +	hws[I2S1_MUX] = ma35d1_clk_mux("i2s1_mux",
>> +				       clk_base + REG_CLK_CLKSEL4,
>> +				       14, 2, i2s1_sel_clks,
>> +				       ARRAY_SIZE(i2s1_sel_clks));
>> +	hws[I2S1_GATE] = ma35d1_clk_gate("i2s1_gate", "i2s1_mux",
>> +					 clk_base + REG_CLK_APBCLK2, 1);
>> +
>> +	/* SSMCC */
>> +	hws[SSMCC_GATE] = ma35d1_clk_gate("ssmcc_gate", "pclk3",
>> +					  clk_base + REG_CLK_APBCLK2, 2);
>> +
>> +	/* SSPCC */
>> +	hws[SSPCC_GATE] = ma35d1_clk_gate("sspcc_gate", "pclk3",
>> +					  clk_base + REG_CLK_APBCLK2, 3);
>> +
>> +	/* SPI0~3 */
>> +	hws[SPI0_MUX] = ma35d1_clk_mux("spi0_mux",
>> +				       clk_base + REG_CLK_CLKSEL4,
>> +				       0, 2, spi0_sel_clks,
>> +				       ARRAY_SIZE(spi0_sel_clks));
>> +	hws[SPI0_GATE] = ma35d1_clk_gate("spi0_gate", "spi0_mux",
>> +					 clk_base + REG_CLK_APBCLK2, 4);
>> +	hws[SPI1_MUX] = ma35d1_clk_mux("spi1_mux",
>> +				       clk_base + REG_CLK_CLKSEL4,
>> +				       2, 2, spi1_sel_clks,
>> +				       ARRAY_SIZE(spi1_sel_clks));
>> +	hws[SPI1_GATE] = ma35d1_clk_gate("spi1_gate", "spi1_mux",
>> +					 clk_base + REG_CLK_APBCLK2, 5);
>> +	hws[SPI2_MUX] = ma35d1_clk_mux("spi2_mux",
>> +				       clk_base + REG_CLK_CLKSEL4,
>> +				       4, 2, spi2_sel_clks,
>> +				       ARRAY_SIZE(spi2_sel_clks));
>> +	hws[SPI2_GATE] = ma35d1_clk_gate("spi2_gate", "spi2_mux",
>> +					 clk_base + REG_CLK_APBCLK2, 6);
>> +	hws[SPI3_MUX] = ma35d1_clk_mux("spi3_mux",
>> +				       clk_base + REG_CLK_CLKSEL4,
>> +				       6, 2, spi3_sel_clks,
>> +				       ARRAY_SIZE(spi3_sel_clks));
>> +	hws[SPI3_GATE] = ma35d1_clk_gate("spi3_gate", "spi3_mux",
>> +					 clk_base + REG_CLK_APBCLK2, 7);
>> +
>> +	/* ECAP0~2 */
>> +	hws[ECAP0_GATE] = ma35d1_clk_gate("ecap0_gate", "pclk1",
>> +					  clk_base + REG_CLK_APBCLK2, 8);
>> +	hws[ECAP1_GATE] = ma35d1_clk_gate("ecap1_gate", "pclk2",
>> +					  clk_base + REG_CLK_APBCLK2, 9);
>> +	hws[ECAP2_GATE] = ma35d1_clk_gate("ecap2_gate", "pclk1",
>> +					  clk_base + REG_CLK_APBCLK2, 10);
>> +
>> +	/* QEI0~2 */
>> +	hws[QEI0_GATE] = ma35d1_clk_gate("qei0_gate", "pclk1",
>> +					 clk_base + REG_CLK_APBCLK2, 12);
>> +	hws[QEI1_GATE] = ma35d1_clk_gate("qei1_gate", "pclk2",
>> +					 clk_base + REG_CLK_APBCLK2, 13);
>> +	hws[QEI2_GATE] = ma35d1_clk_gate("qei2_gate", "pclk1",
>> +					 clk_base + REG_CLK_APBCLK2, 14);
>> +
>> +	/* ADC */
>> +	hws[ADC_DIV] = ma35d1_reg_adc_clkdiv(dev, "adc_div", "pclk0", 0,
>> +					     clk_base + REG_CLK_CLKDIV4,
>> +					     4, 17, 0x1ffff);
>> +	hws[ADC_GATE] = ma35d1_clk_gate("adc_gate", "adc_div",
>> +					clk_base + REG_CLK_APBCLK2, 24);
>> +
>> +	/* EADC */
>> +	hws[EADC_DIV] = ma35d1_clk_divider_table("eadc_div", "pclk2",
>> +						 clk_base + REG_CLK_CLKDIV4,
>> +						 0, 4, eadc_div_table);
>> +	hws[EADC_GATE] = ma35d1_clk_gate("eadc_gate", "eadc_div",
>> +					 clk_base + REG_CLK_APBCLK2, 25);
>> +
>> +	ret = of_clk_add_hw_provider(clk_node, of_clk_hw_onecell_get,
>> +				     ma35d1_hw_data);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to register hws for MA35D1\n");
>> +		iounmap(clk_base);
>> +	}
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id ma35d1_clk_of_match[] = {
>> +	{ .compatible = "nuvoton,ma35d1-clk" },
>> +	{ },
>> +};
>> +MODULE_DEVICE_TABLE(of, ma35d1_clk_of_match);
>> +
>> +static struct platform_driver ma35d1_clk_driver = {
>> +	.probe = ma35d1_clocks_probe,
>> +	.driver = {
>> +		.name = "ma35d1-clk",
>> +		.of_match_table = ma35d1_clk_of_match,
>> +	},
>> +};
>> +
>> +static int __init ma35d1_clocks_init(void)
>> +{
>> +	return platform_driver_register(&ma35d1_clk_driver);
>> +}
>> +
>> +postcore_initcall(ma35d1_clocks_init);
>> +
>> +MODULE_AUTHOR("Chi-Fang Li<cfli0@nuvoton.com>");
> Space missing.
>
>> +MODULE_DESCRIPTION("NUVOTON MA35D1 Clock Driver");
>> +MODULE_LICENSE("GPL v2");
> "GPL" is enough.


OK


>
>> diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
>> new file mode 100644
>> index 000000000000..faae5a17e425
>> --- /dev/null
>> +++ b/drivers/clk/nuvoton/clk-ma35d1.h
>> @@ -0,0 +1,198 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#ifndef __DRV_CLK_NUVOTON_MA35D1_H
>> +#define __DRV_CLK_NUVOTON_MA35D1_H
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clkdev.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/mfd/ma35d1-sys.h>
>> +
>> +enum ma35d1_pll_type {
>> +	MA35D1_CAPLL,
>> +	MA35D1_DDRPLL,
>> +	MA35D1_APLL,
>> +	MA35D1_EPLL,
>> +	MA35D1_VPLL,
>> +};
>> +
>> +enum ma35d1_pll_mode {
>> +	VSIPLL_INTEGER_MODE,
>> +	VSIPLL_FRACTIONAL_MODE,
>> +	VSIPLL_SS_MODE,
>> +};
>> +
>> +/* VSI-PLL CTL0~2 */
>> +#define VSIPLL_CTL0			0x0
>> +#define VSIPLL_CTL1			0x4
>> +#define VSIPLL_CTL2			0x8
>> +
>> +/* VSI-PLL Specification limits */
>> +#define VSIPLL_FREF_MAX_FREQ		200000000UL
>> +#define VSIPLL_FREF_MIN_FREQ		1000000UL
>> +#define VSIPLL_FREFDIVM_MAX_FREQ	40000000UL
>> +#define VSIPLL_FREFDIVM_MIN_FREQ0	1000000UL
>> +#define VSIPLL_FREFDIVM_MIN_FREQ1	10000000UL
>> +#define VSIPLL_FCLK_MAX_FREQ		2400000000UL
>> +#define VSIPLL_FCLK_MIN_FREQ		600000000UL
>> +#define VSIPLL_FCLKO_MAX_FREQ		2400000000UL
>> +#define VSIPLL_FCLKO_MIN_FREQ		85700000UL
>> +#define VSIPLL_SPREAD_RANGE		194
>> +#define VSIPLL_MODULATION_FREQ		50000
>> +
>> +/* Clock Control Registers Offset */
>> +#define REG_CLK_PWRCTL			(0x00)
> Unnecessary parenthesis.
>
>> +#define REG_CLK_SYSCLK0			(0x04)
>> +#define REG_CLK_SYSCLK1			(0x08)
>> +#define REG_CLK_APBCLK0			(0x0C)
>> +#define REG_CLK_APBCLK1			(0x10)
>> +#define REG_CLK_APBCLK2			(0x14)
>> +#define REG_CLK_CLKSEL0			(0x18)
>> +#define REG_CLK_CLKSEL1			(0x1C)
>> +#define REG_CLK_CLKSEL2			(0x20)
>> +#define REG_CLK_CLKSEL3			(0x24)
>> +#define REG_CLK_CLKSEL4			(0x28)
>> +#define REG_CLK_CLKDIV0			(0x2C)
>> +#define REG_CLK_CLKDIV1			(0x30)
>> +#define REG_CLK_CLKDIV2			(0x34)
>> +#define REG_CLK_CLKDIV3			(0x38)
>> +#define REG_CLK_CLKDIV4			(0x3C)
>> +#define REG_CLK_CLKOCTL			(0x40)
>> +#define REG_CLK_STATUS			(0x50)
>> +#define REG_CLK_PLL0CTL0		(0x60)
>> +#define REG_CLK_PLL2CTL0		(0x80)
>> +#define REG_CLK_PLL2CTL1		(0x84)
>> +#define REG_CLK_PLL2CTL2		(0x88)
>> +#define REG_CLK_PLL3CTL0		(0x90)
>> +#define REG_CLK_PLL3CTL1		(0x94)
>> +#define REG_CLK_PLL3CTL2		(0x98)
>> +#define REG_CLK_PLL4CTL0		(0xA0)
>> +#define REG_CLK_PLL4CTL1		(0xA4)
>> +#define REG_CLK_PLL4CTL2		(0xA8)
>> +#define REG_CLK_PLL5CTL0		(0xB0)
>> +#define REG_CLK_PLL5CTL1		(0xB4)
>> +#define REG_CLK_PLL5CTL2		(0xB8)
>> +#define REG_CLK_CLKDCTL			(0xC0)
>> +#define REG_CLK_CLKDSTS			(0xC4)
>> +#define REG_CLK_CDUPB			(0xC8)
>> +#define REG_CLK_CDLOWB			(0xCC)
>> +#define REG_CLK_CKFLTRCTL		(0xD0)
>> +#define REG_CLK_TESTCLK			(0xF0)
>> +#define REG_CLK_PLLCTL			(0x40)
>> +
>> +/* Constant Definitions for Clock Controller */
>> +#define SMICPLLCTL0_FBDIV_POS		(0)
>> +#define SMICPLLCTL0_FBDIV_MSK		(0xfful << SMICPLLCTL0_FBDIV_POS)
>> +#define SMICPLLCTL0_INDIV_POS		(8)
>> +#define SMICPLLCTL0_INDIV_MSK		(0xful << SMICPLLCTL0_INDIV_POS)
>> +#define SMICPLLCTL0_OUTDIV_POS		(12)
>> +#define SMICPLLCTL0_OUTDIV_MSK		(0x3ul << SMICPLLCTL0_OUTDIV_POS)
> GENMASK() + remove _POS define completely.


OK, we will fix them all.


>
>> +#define SMICPLLCTL0_PD_POS		(16)
>> +#define SMICPLLCTL0_PD_MSK		(0x1ul << SMICPLLCTL0_PD_POS)
> BIT() + remove _POS.


OK, we will fix them all.


> Is this really a mask or a bit? I'd remove _MSK from the name (which is
> usually not that useful anyway even if it would be a multiple bit mask
> for real).
>
>> +#define SMICPLLCTL0_BP_POS		(17)
>> +#define SMICPLLCTL0_BP_MSK		(0x1ul << SMICPLLCTL0_BP_POS)
> BIT()?


Yes, we will use BIT().


>> +#define VSIPLLCTL0_FBDIV_POS		(0)
>> +#define VSIPLLCTL0_FBDIV_MSK		(0x7fful << VSIPLLCTL0_FBDIV_POS)
>> +#define VSIPLLCTL0_INDIV_POS		(12)
>> +#define VSIPLLCTL0_INDIV_MSK		(0x3ful << VSIPLLCTL0_INDIV_POS)
>> +#define VSIPLLCTL0_MODE_POS		(18)
>> +#define VSIPLLCTL0_MODE_MSK		(0x3ul << VSIPLLCTL0_MODE_POS)
>> +#define VSIPLLCTL0_SSRATE_POS		(20)
>> +#define VSIPLLCTL0_SSRATE_MSK		(0x7fful << VSIPLLCTL0_SSRATE_POS)
>> +#define VSIPLLCTL1_PD_POS		(0)
>> +#define VSIPLLCTL1_PD_MSK		(0x1ul << VSIPLLCTL1_PD_POS)
>> +#define VSIPLLCTL1_BP_POS		(1)
>> +#define VSIPLLCTL1_BP_MSK		(0x1ul << VSIPLLCTL1_BP_POS)
>> +#define VSIPLLCTL1_OUTDIV_POS		(4)
>> +#define VSIPLLCTL1_OUTDIV_MSK		(0x7ul << VSIPLLCTL1_OUTDIV_POS)
>> +#define VSIPLLCTL1_FRAC_POS		(8)
>> +#define VSIPLLCTL1_FRAC_MSK		(0xfffffful << VSIPLLCTL1_FRAC_POS)
>> +#define VSIPLLCTL2_SLOPE_POS		(0)
>> +#define VSIPLLCTL2_SLOPE_MSK		(0xfffffful << VSIPLLCTL2_SLOPE_POS)
> ...and more of them.
>
>

Best regards,

Jacky Huang



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

* Re: [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset controller bindings
  2023-03-18  4:30       ` Jacky Huang
@ 2023-03-19 11:05         ` Krzysztof Kozlowski
  2023-03-20  6:26           ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-19 11:05 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 18/03/2023 05:30, Jacky Huang wrote:
> Dear Krzysztof,
> 
> 
> Thanks for your advice.
> 
> 
> On 2023/3/16 下午 03:39, Krzysztof Kozlowski wrote:
>> On 16/03/2023 08:37, Krzysztof Kozlowski wrote:
>>> On 15/03/2023 08:28, Jacky Huang wrote:
>>>> From: Jacky Huang <ychuang3@nuvoton.com>
>>>>
>>>> Add documentation to describe nuvoton ma35d1 reset driver bindings.
>>> Subject: drop second/last, redundant "bindings". The "dt-bindings"
>>> prefix is already stating that these are bindings.
> 
> 
> OK, I will fix it.
> 
> 
>>>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>>>> ---
>>>>   .../bindings/reset/nuvoton,ma35d1-reset.yaml  | 50 +++++++++++++++++++
>>>>   1 file changed, 50 insertions(+)
>>>>   create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>>> new file mode 100644
>>>> index 000000000000..f66c566c6dce
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>>> @@ -0,0 +1,50 @@
>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>> +%YAML 1.2
>>>> +---
>>>> +$id: http://devicetree.org/schemas/reset/nuvoton,ma35d1-reset.yaml#
>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>> +
>>>> +title: Nuvoton MA35D1 Reset Controller
>>>> +
>>>> +maintainers:
>>>> +  - Chi-Fang Li <cfli0@nuvoton.com>
>>>> +  - Jacky Huang <ychuang3@nuvoton.com>
>>>> +
>>>> +description:
>>>> +  The system reset controller can be used to reset various peripheral
>>>> +  controllers in MA35D1 SoC.
>>>> +
>>>> +properties:
>>>> +  compatible:
>>>> +    const: nuvoton,ma35d1-reset
>>>> +
>>>> +  regmap:
>>>> +    $ref: /schemas/types.yaml#/definitions/phandle
>>>> +    description: Phandle to the register map node.
>>> You need to be specific what is this. As you can easily check, there is
>>> no such property in any devices. I don't understand why do you need it
>>> in the first place.
> 
>          reset: reset-controller {
>              compatible = "nuvoton,ma35d1-reset";
>              regmap = <&sys>;
>              #reset-cells = <1>;
>          };
> 
> The dt_binding_check check report an error about the above "regmap".
> 
> I found that add this can pass the test.

Do not add properties to bindings to "pass the test". That's not the
goal of bindings. Add there properties because they make sense...

Anyway, you did not answer my question at all. So one by one - address them:
1. As you can easily check, there is no such property in any devices.
Explanation: do you see it anywhere in existing bindings?

2. I don't understand why do you need it in the first place.
Explanation: your binding suggest this is not needed. If you think
otherwise, you need to provide rationale.



Best regards,
Krzysztof


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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-18  6:07     ` Jacky Huang
@ 2023-03-19 11:06       ` Krzysztof Kozlowski
  2023-03-19 14:16         ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Krzysztof Kozlowski @ 2023-03-19 11:06 UTC (permalink / raw)
  To: Jacky Huang, robh+dt, krzysztof.kozlowski+dt, lee, mturquette,
	sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

On 18/03/2023 07:07, Jacky Huang wrote:
> 
>>
>>> +		interrupts = <GIC_PPI 9 (GIC_CPU_MASK_RAW(0x13) |
>>> +			      IRQ_TYPE_LEVEL_HIGH)>;
>>> +	};
>>> +
>>> +	uart0:serial@40700000 {
>>> +		compatible = "nuvoton,ma35d1-uart";
>>> +		reg = <0x0 0x40700000 0x0 0x100>;
>>> +		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
>>> +		clocks = <&clk UART0_GATE>;
>>> +		status = "okay";
>> Why? Drop the line... or convert it to disabled. Otherwise, why every
>> SoC has serial0 enabled? Is it used internally?
> 
> 
> uart0 is on all the way since this SoC booting from the MaskROM boot code,
> 
> load arm-trusted-firmware, load bootloader, and finally load linux  kernel.
> 
> uart0 is also the Linux console.

Are you sure? Maybe my board has UART0 disconnected.

Best regards,
Krzysztof


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

* Re: [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35
  2023-03-16 14:38   ` Arnd Bergmann
@ 2023-03-19 12:01     ` Jacky Huang
  2023-03-19 12:36       ` Tomer Maimon
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-19 12:01 UTC (permalink / raw)
  To: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby, Avi Fishman, Tali Perry,
	Tomer Maimon, Patrick Venture, Nancy Yuen, Benjamin Fair
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Arnd,


Thanks for your advice.


On 2023/3/16 下午 10:38, Arnd Bergmann wrote:
> On Wed, Mar 15, 2023, at 08:29, Jacky Huang wrote:
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> Add entry for Nuvton ma35d1 maintainer and files
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>> +F:	Documentation/devicetree/bindings/*/*nuvoton*
>> +F:	arch/arm64/boot/dts/nuvoton/
> This clashes with the existing entry for NPCM, so
> contributors can easily get confused about where
> to send their dts patches.
>
> I don't have a good solution here, but maybe you can
> discuss this with the npcm maintainers (added to Cc)
> to see how they would like to handle this.
>
> For me, the easiest way would be to have a single
> maintainer send me all the patches for both ma35d1
> and npcm, but that may not be practical for you.


All I can do so far is, once we receive a patch for npcm,

forward it to the maintainers of npcm, and the npcm side

does the same.

And I would like to modify it as

+F:	arch/arm64/boot/dts/nuvoton/*ma35*

>> +F:	drivers/*/*/*ma35d1*
>> +F:	drivers/*/*ma35d1*
>> +F:	include/dt-bindings/*/*ma35d1*
>> +F:	include/linux/mfd/ma35d1-sys.h
> I would replace these with a single line
>
> K:    ma35d1
>
> that should have the same effect.
>
>       Arnd


It's fine. I will use K: instead. Thank you.


Best regards,

Jacky Huang



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

* Re: [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35
  2023-03-19 12:01     ` Jacky Huang
@ 2023-03-19 12:36       ` Tomer Maimon
  0 siblings, 0 replies; 89+ messages in thread
From: Tomer Maimon @ 2023-03-19 12:36 UTC (permalink / raw)
  To: Jacky Huang
  Cc: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby, Avi Fishman, Tali Perry,
	Patrick Venture, Nancy Yuen, Benjamin Fair, devicetree,
	linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Hi Jacky and Arnd

On Sun, 19 Mar 2023 at 14:01, Jacky Huang <ychuang570808@gmail.com> wrote:
>
> Dear Arnd,
>
>
> Thanks for your advice.
>
>
> On 2023/3/16 下午 10:38, Arnd Bergmann wrote:
> > On Wed, Mar 15, 2023, at 08:29, Jacky Huang wrote:
> >> From: Jacky Huang <ychuang3@nuvoton.com>
> >>
> >> Add entry for Nuvton ma35d1 maintainer and files
> >>
> >> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> >> ---
> >> +F:  Documentation/devicetree/bindings/*/*nuvoton*
> >> +F:  arch/arm64/boot/dts/nuvoton/
> > This clashes with the existing entry for NPCM, so
> > contributors can easily get confused about where
> > to send their dts patches.
> >
> > I don't have a good solution here, but maybe you can
> > discuss this with the npcm maintainers (added to Cc)
> > to see how they would like to handle this.
> >
> > For me, the easiest way would be to have a single
> > maintainer send me all the patches for both ma35d1
> > and npcm, but that may not be practical for you.
>
>
> All I can do so far is, once we receive a patch for npcm,
>
> forward it to the maintainers of npcm, and the npcm side
We can forward ma35 DTS emails to you as well.
>
> does the same.
>
> And I would like to modify it as
>
> +F:     arch/arm64/boot/dts/nuvoton/*ma35*
About modify
F: arch/arm64/boot/dts/nuvoton/
to
F: arch/arm64/boot/dts/nuvoton/*npcm*
We can't guarantee that our customers will use npcm in the dts files name.
>
> >> +F:  drivers/*/*/*ma35d1*
> >> +F:  drivers/*/*ma35d1*
> >> +F:  include/dt-bindings/*/*ma35d1*
> >> +F:  include/linux/mfd/ma35d1-sys.h
> > I would replace these with a single line
> >
> > K:    ma35d1
> >
> > that should have the same effect.
> >
> >       Arnd
>
>
> It's fine. I will use K: instead. Thank you.
>
>
> Best regards,
>
> Jacky Huang
>
>

Thanks,

Tomer

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

* Re: [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support
  2023-03-16 15:05   ` Ilpo Järvinen
@ 2023-03-19 13:10     ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-19 13:10 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, devicetree, linux-clk, linux-kernel,
	linux-serial, schung, Jacky Huang

Dear Ilpo,


On 2023/3/16 下午 11:05, Ilpo Järvinen wrote:
> On Wed, 15 Mar 2023, Jacky Huang wrote:
>
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> This driver supports individual IP reset for ma35d1. The reset
>> control registers is a subset of system control registers.
>>
>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   drivers/reset/Kconfig        |   6 ++
>>   drivers/reset/Makefile       |   1 +
>>   drivers/reset/reset-ma35d1.c | 152 +++++++++++++++++++++++++++++++++++
>>   3 files changed, 159 insertions(+)
>>   create mode 100644 drivers/reset/reset-ma35d1.c
>>
>> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
>> index 2a52c990d4fe..47671060d259 100644
>> --- a/drivers/reset/Kconfig
>> +++ b/drivers/reset/Kconfig
>> @@ -143,6 +143,12 @@ config RESET_NPCM
>>   	  This enables the reset controller driver for Nuvoton NPCM
>>   	  BMC SoCs.
>>   
>> +config RESET_NUVOTON_MA35D1
>> +	bool "Nuvton MA35D1 Reset Driver"
>> +	default ARCH_NUVOTON
>> +	help
>> +	  This enables the reset controller driver for Nuvoton MA35D1 SoC.
>> +
>>   config RESET_OXNAS
>>   	bool
>>   
>> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
>> index 3e7e5fd633a8..fd52dcf66a99 100644
>> --- a/drivers/reset/Makefile
>> +++ b/drivers/reset/Makefile
>> @@ -20,6 +20,7 @@ obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
>>   obj-$(CONFIG_RESET_MESON) += reset-meson.o
>>   obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
>>   obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
>> +obj-$(CONFIG_RESET_NUVOTON_MA35D1) += reset-ma35d1.o
>>   obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
>>   obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
>>   obj-$(CONFIG_RESET_POLARFIRE_SOC) += reset-mpfs.o
>> diff --git a/drivers/reset/reset-ma35d1.c b/drivers/reset/reset-ma35d1.c
>> new file mode 100644
>> index 000000000000..bdd39483ca4e
>> --- /dev/null
>> +++ b/drivers/reset/reset-ma35d1.c
>> @@ -0,0 +1,152 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>> + */
>> +
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/io.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset-controller.h>
>> +#include <linux/mfd/ma35d1-sys.h>
>> +#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
>> +#include <linux/regmap.h>
>> +#include <linux/reboot.h>
>> +
>> +#define RST_PRE_REG	32
>> +
>> +struct ma35d1_reset_data {
>> +	struct reset_controller_dev rcdev;
>> +	struct regmap *regmap;
>> +};
>> +
>> +struct ma35d1_reboot_data {
>> +	struct notifier_block restart_handler;
>> +	struct regmap *regmap;
>> +};
>> +
>> +static int ma35d1_restart_handler(struct notifier_block *this,
>> +				  unsigned long mode, void *cmd)
>> +{
>> +	struct ma35d1_reboot_data *data =
>> +			container_of(this, struct ma35d1_reboot_data,
>> +				     restart_handler);
>> +	regmap_write(data->regmap, REG_SYS_IPRST0, 1 << MA35D1_RESET_CHIP);
>> +	return -EAGAIN;
> This results -EAGAIN always???


The chip will reset immediately after the write to REG_SYS_IPRST0.

The return line should never be executed. If yes, it must be failed to 
reset.

Anyway, I will modify it as

return regmap_write(data->regmap, REG_SYS_IPRST0, 1 << MA35D1_RESET_CHIP);

>
>> +}
>> +
>> +static int ma35d1_reset_update(struct reset_controller_dev *rcdev,
>> +			      unsigned long id, bool assert)
>> +{
>> +	int reg;
>> +	int offset = (id / RST_PRE_REG) * 4;
>> +	struct ma35d1_reset_data *data =
>> +			container_of(rcdev, struct ma35d1_reset_data, rcdev);
>> +
>> +	regmap_read(data->regmap, REG_SYS_IPRST0 + offset, &reg);
>> +	if (assert)
>> +		reg |= 1 << (id % RST_PRE_REG);
>> +	else
>> +		reg &= ~(1 << (id % RST_PRE_REG));
>> +
>> +	regmap_write(data->regmap, REG_SYS_IPRST0 + offset, reg);
>> +	return 0;
> This returns always 0. What about regmap_read/write() errors, should the
> be returned?


I will modify it as

return regmap_write(data->regmap, REG_SYS_IPRST0 + offset, reg);

And add return value check to the regmap_read().


>> +}
>> +
>> +static int ma35d1_reset_assert(struct reset_controller_dev *rcdev,
>> +			       unsigned long id)
>> +{
>> +	return ma35d1_reset_update(rcdev, id, true);
>> +}
>> +
>> +static int ma35d1_reset_deassert(struct reset_controller_dev *rcdev,
>> +				 unsigned long id)
>> +{
>> +	return ma35d1_reset_update(rcdev, id, false);
>> +}
>> +
>> +static int ma35d1_reset_status(struct reset_controller_dev *rcdev,
>> +			      unsigned long id)
>> +{
>> +	int reg;
>> +	int offset = id / RST_PRE_REG;
>> +	struct ma35d1_reset_data *data =
>> +			container_of(rcdev, struct ma35d1_reset_data, rcdev);
>> +
>> +	regmap_read(data->regmap, REG_SYS_IPRST0 + offset, &reg);
> Error handling?

I will modify it as

     ret = regmap_read(data->regmap, REG_SYS_IPRST0 + offset, &reg);
     if (ret < 0)
         return ret;

>
>> +	return !!(reg & BIT(id % RST_PRE_REG));
>> +}
>> +
>> +static const struct reset_control_ops ma35d1_reset_ops = {
>> +	.assert = ma35d1_reset_assert,
>> +	.deassert = ma35d1_reset_deassert,
>> +	.status = ma35d1_reset_status,
>> +};
>> +
>> +static const struct of_device_id ma35d1_reset_dt_ids[] = {
>> +	{ .compatible = "nuvoton,ma35d1-reset" },
>> +	{ },
>> +};
>> +
>> +static int ma35d1_reset_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct ma35d1_reset_data *reset_data;
>> +	struct ma35d1_reboot_data *reboot_data;
>> +	int err;
>> +
>> +	if (!pdev->dev.of_node) {
>> +		dev_err(&pdev->dev, "Device tree node not found\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	reset_data = devm_kzalloc(dev, sizeof(*reset_data), GFP_KERNEL);
>> +	if (!reset_data)
>> +		return -ENOMEM;
>> +
>> +	reboot_data = devm_kzalloc(dev, sizeof(*reboot_data), GFP_KERNEL);
>> +	if (!reboot_data) {
>> +		devm_kfree(dev, reset_data);
> Unnecessary.


OK, I will remove this devm_kfree().


>> +		return -ENOMEM;
>> +	}
>> +
>> +	reset_data->regmap  = syscon_regmap_lookup_by_phandle(
>> +			      pdev->dev.of_node, "regmap");
>> +	if (IS_ERR(reset_data->regmap)) {
>> +		dev_err(&pdev->dev, "Failed to get SYS register base\n");
>> +		err = PTR_ERR(reset_data->regmap);
>> +		goto err_out;
>> +	}
>> +	reset_data->rcdev.owner = THIS_MODULE;
>> +	reset_data->rcdev.nr_resets = MA35D1_RESET_COUNT;
>> +	reset_data->rcdev.ops = &ma35d1_reset_ops;
>> +	reset_data->rcdev.of_node = dev->of_node;
>> +
>> +	reboot_data->regmap = reset_data->regmap;
>> +	reboot_data->restart_handler.notifier_call = ma35d1_restart_handler;
>> +	reboot_data->restart_handler.priority = 192;
>> +
>> +	err = register_restart_handler(&reboot_data->restart_handler);
>> +	if (err)
>> +		dev_warn(&pdev->dev, "failed to register restart handler\n");
>> +
>> +	return devm_reset_controller_register(dev, &reset_data->rcdev);
>> +
>> +err_out:
>> +	devm_kfree(dev, reset_data);
>> +	devm_kfree(dev, reboot_data);
> These are unnecessary since the probe is failing.


OK, I will make it just return err.

>
>> +	return err;
>> +}
>> +
>> +static struct platform_driver ma35d1_reset_driver = {
>> +	.probe = ma35d1_reset_probe,
>> +	.driver = {
>> +		.name = "ma35d1-reset",
>> +		.of_match_table	= ma35d1_reset_dt_ids,
>> +	},
>> +};
>> +
>> +builtin_platform_driver(ma35d1_reset_driver);
>>
Best regards,

Jacky Huang


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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-19 11:06       ` Krzysztof Kozlowski
@ 2023-03-19 14:16         ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-19 14:16 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Krzyszto,


On 2023/3/19 下午 07:06, Krzysztof Kozlowski wrote:
> On 18/03/2023 07:07, Jacky Huang wrote:
>>>> +		interrupts = <GIC_PPI 9 (GIC_CPU_MASK_RAW(0x13) |
>>>> +			      IRQ_TYPE_LEVEL_HIGH)>;
>>>> +	};
>>>> +
>>>> +	uart0:serial@40700000 {
>>>> +		compatible = "nuvoton,ma35d1-uart";
>>>> +		reg = <0x0 0x40700000 0x0 0x100>;
>>>> +		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
>>>> +		clocks = <&clk UART0_GATE>;
>>>> +		status = "okay";
>>> Why? Drop the line... or convert it to disabled. Otherwise, why every
>>> SoC has serial0 enabled? Is it used internally?
>>
>> uart0 is on all the way since this SoC booting from the MaskROM boot code,
>>
>> load arm-trusted-firmware, load bootloader, and finally load linux  kernel.
>>
>> uart0 is also the Linux console.
> Are you sure? Maybe my board has UART0 disconnected.
>
> Best regards,
> Krzysztof


OK, I will have the uart0 disabled in dtsi, and enabled it in dts.


Best regards,

Jacky Huang



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

* Re: [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset controller bindings
  2023-03-19 11:05         ` Krzysztof Kozlowski
@ 2023-03-20  6:26           ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-20  6:26 UTC (permalink / raw)
  To: Krzysztof Kozlowski, robh+dt, krzysztof.kozlowski+dt, lee,
	mturquette, sboyd, p.zabel, gregkh, jirislaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang



On 2023/3/19 下午 07:05, Krzysztof Kozlowski wrote:
> On 18/03/2023 05:30, Jacky Huang wrote:
>> Dear Krzysztof,
>>
>>
>> Thanks for your advice.
>>
>>
>> On 2023/3/16 下午 03:39, Krzysztof Kozlowski wrote:
>>> On 16/03/2023 08:37, Krzysztof Kozlowski wrote:
>>>> On 15/03/2023 08:28, Jacky Huang wrote:
>>>>> From: Jacky Huang <ychuang3@nuvoton.com>
>>>>>
>>>>> Add documentation to describe nuvoton ma35d1 reset driver bindings.
>>>> Subject: drop second/last, redundant "bindings". The "dt-bindings"
>>>> prefix is already stating that these are bindings.
>>
>> OK, I will fix it.
>>
>>
>>>>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>>>>> ---
>>>>>    .../bindings/reset/nuvoton,ma35d1-reset.yaml  | 50 +++++++++++++++++++
>>>>>    1 file changed, 50 insertions(+)
>>>>>    create mode 100644 Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>>>> new file mode 100644
>>>>> index 000000000000..f66c566c6dce
>>>>> --- /dev/null
>>>>> +++ b/Documentation/devicetree/bindings/reset/nuvoton,ma35d1-reset.yaml
>>>>> @@ -0,0 +1,50 @@
>>>>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>>>>> +%YAML 1.2
>>>>> +---
>>>>> +$id: http://devicetree.org/schemas/reset/nuvoton,ma35d1-reset.yaml#
>>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>>> +
>>>>> +title: Nuvoton MA35D1 Reset Controller
>>>>> +
>>>>> +maintainers:
>>>>> +  - Chi-Fang Li <cfli0@nuvoton.com>
>>>>> +  - Jacky Huang <ychuang3@nuvoton.com>
>>>>> +
>>>>> +description:
>>>>> +  The system reset controller can be used to reset various peripheral
>>>>> +  controllers in MA35D1 SoC.
>>>>> +
>>>>> +properties:
>>>>> +  compatible:
>>>>> +    const: nuvoton,ma35d1-reset
>>>>> +
>>>>> +  regmap:
>>>>> +    $ref: /schemas/types.yaml#/definitions/phandle
>>>>> +    description: Phandle to the register map node.
>>>> You need to be specific what is this. As you can easily check, there is
>>>> no such property in any devices. I don't understand why do you need it
>>>> in the first place.
>>           reset: reset-controller {
>>               compatible = "nuvoton,ma35d1-reset";
>>               regmap = <&sys>;
>>               #reset-cells = <1>;
>>           };
>>
>> The dt_binding_check check report an error about the above "regmap".
>>
>> I found that add this can pass the test.
> Do not add properties to bindings to "pass the test". That's not the
> goal of bindings. Add there properties because they make sense...
>
> Anyway, you did not answer my question at all. So one by one - address them:
> 1. As you can easily check, there is no such property in any devices.
> Explanation: do you see it anywhere in existing bindings?

Yes, I cannot find it in all bindings. I know it's wrong.

> 2. I don't understand why do you need it in the first place.
> Explanation: your binding suggest this is not needed. If you think
> otherwise, you need to provide rationale.
>
>
>
> Best regards,
> Krzysztof
>

Now we have removed regmap and modify the dtsi as:

     sys: system-management@40460000 {
         compatible = "nuvoton,ma35d1-sys", "syscon", "simple-mfd";
         reg = <0x0 0x40460000 0x0 0x200>;

         reset: reset-controller {
             compatible = "nuvoton,ma35d1-reset";
             #reset-cells = <1>;
         };
     };

In the reset driver, we obtain the regmap by parent node:
     parent = of_get_parent(dev->of_node); /* parent should be syscon 
node */
     reset_data->regmap = syscon_node_to_regmap(parent);
     of_node_put(parent);

We have it tested OK on ma35d1 SOM board.
And it pass the dt_binding_check and dtbs_check.

Best regards,
Jacky Huang




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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-16 14:54   ` Ilpo Järvinen
@ 2023-03-20  8:23     ` Jacky Huang
  2023-03-20 10:04       ` Ilpo Järvinen
  0 siblings, 1 reply; 89+ messages in thread
From: Jacky Huang @ 2023-03-20  8:23 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	Greg Kroah-Hartman, Jiri Slaby, devicetree, linux-clk, LKML,
	linux-serial, schung, Jacky Huang, mjchen

Dear Ilpo,


Thanks for your advice.

On 2023/3/16 下午 10:54, Ilpo Järvinen wrote:
> Hi,
>
> I'll not note all things below because others have already seemingly
> commented many things.
>
> On Wed, 15 Mar 2023, Jacky Huang wrote:
>
>> From: Jacky Huang <ychuang3@nuvoton.com>
>>
>> This adds UART and console driver for Nuvoton ma35d1 Soc.
>>
>> MA35D1 SoC provides up to 17 UART controllers, each with one uart port.
>> The ma35d1 uart controller is not compatible with 8250.
>> The uart controller supports:
>>    - Full-duplex asynchronous communications
>>    - Separates tx and tx 32/32 bytes entry FIFO for data payloads
>>    - Hardware auto-flow control
>>    - Programmable rx buffer trigger level (1/4/8/14/30 bytes)
>>    - Individual programmable baud rate generator for each channel
>>    - Supports nCTS, incoming data, rx FIFO reached threshold and
>>      RS-485 Address Match (AAD mode) wake-up function
>>    - Supports 8-bit rx buffer time-out detection function
>>    - Programmable tx data delay time
>>    - Supports Auto-Baud Rate measurement and baud rate compensation
>>    - Supports break error, frame error, parity error and rx/tx buffer
>>      overflow detection function
>>    – Programmable number of data bit, 5-, 6-, 7-, 8- bit character
>>    – Programmable parity bit, even, odd, no parity or stick parity bit
>>      generation and detection
>>    – Programmable stop bit, 1, 1.5, or 2 stop bit generation
>>    - Supports IrDA SIR function mode
>>    - Supports RS-485 function mode
>>    – Supports RS-485 9-bit mode
>>    – Supports hardware or software enables to program nRTS pin to control
>>      RS-485 transmission direction
>>    - Supports PDMA transfer function
>>    - Support Single-wire function mode.
> This list is probably copy-pasted from somewhere but it doesn't match what
> you implemented in the driver.

I will remove them and rewrite the descriptions.

>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>> ---
>>   drivers/tty/serial/Kconfig         |  18 +
>>   drivers/tty/serial/Makefile        |   1 +
>>   drivers/tty/serial/ma35d1_serial.c | 842 +++++++++++++++++++++++++++++
>>   drivers/tty/serial/ma35d1_serial.h |  93 ++++
>>   include/uapi/linux/serial_core.h   |   3 +
>>   5 files changed, 957 insertions(+)
>>   create mode 100644 drivers/tty/serial/ma35d1_serial.c
>>   create mode 100644 drivers/tty/serial/ma35d1_serial.h
>>
>> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
>> index 625358f44419..cb47fe804595 100644
>> --- a/drivers/tty/serial/Kconfig
>> +++ b/drivers/tty/serial/Kconfig
>> @@ -1562,6 +1562,24 @@ config SERIAL_SUNPLUS_CONSOLE
>>   	  you can alter that using a kernel command line option such as
>>   	  "console=ttySUPx".
>>   
>> +config SERIAL_NUVOTON_MA35D1
>> +	tristate "Nuvoton MA35D1 family UART support"
>> +	depends on ARCH_NUVOTON || COMPILE_TEST
>> +	select SERIAL_CORE
>> +	help
>> +	  This driver supports Nuvoton MA35D1 family UART ports. If you would
>> +	  like to use them, you must answer Y or M to this option. Note that
>> +	  for use as console, it must be included in kernel and not as a
>> +	  module
>> +
>> +config SERIAL_NUVOTON_MA35D1_CONSOLE
>> +	bool "Console on a Nuvotn MA35D1 family UART port"
>> +	depends on SERIAL_NUVOTON_MA35D1=y
>> +	select SERIAL_CORE_CONSOLE
>> +	help
>> +	  Select this options if you'd like to use the UART port0 of the
>> +	  Nuvoton MA35D1 family as a console.
>> +
>>   endmenu
>>   
>>   config SERIAL_MCTRL_GPIO
>> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
>> index cd9afd9e3018..71ebeba06ff2 100644
>> --- a/drivers/tty/serial/Makefile
>> +++ b/drivers/tty/serial/Makefile
>> @@ -93,3 +93,4 @@ obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
>>   
>>   obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
>>   obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
>> +obj-$(CONFIG_SERIAL_NUVOTON_MA35D1)	+= ma35d1_serial.o
>> diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c
>> new file mode 100644
>> index 000000000000..8940d07c3777
>> --- /dev/null
>> +++ b/drivers/tty/serial/ma35d1_serial.c
>> @@ -0,0 +1,842 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  MA35D1 serial driver
>> + *  Copyright (C) 2023 Nuvoton Technology Corp.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/moduleparam.h>
>> +#include <linux/ioport.h>
>> +#include <linux/init.h>
>> +#include <linux/console.h>
>> +#include <linux/sysrq.h>
>> +#include <linux/delay.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/tty.h>
>> +#include <linux/tty_flip.h>
>> +#include <linux/clk.h>
>> +#include <linux/serial_reg.h>
>> +#include <linux/serial_core.h>
>> +#include <linux/serial.h>
>> +#include <linux/nmi.h>
>> +#include <linux/mutex.h>
>> +#include <linux/slab.h>
>> +#include <linux/uaccess.h>
>> +#include <linux/of.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/io.h>
>> +#include <asm/irq.h>
>> +#include <asm/serial.h>
>> +#include "ma35d1_serial.h"
>> +
>> +#define UART_NR			17
>> +
>> +static struct uart_driver ma35d1serial_reg;
>> +struct clk *clk;
>> +
>> +struct uart_ma35d1_port {
>> +	struct uart_port port;
>> +	u16 capabilities; /* port capabilities */
>> +	u8 ier;
>> +	u8 lcr;
>> +	u8 mcr;
>> +	u8 mcr_mask;   /* mask of user bits */
>> +	u8 mcr_force;  /* mask of forced bits */
>> +	struct serial_rs485 rs485; /* rs485 settings */
>> +	u32 baud_rate;
>> +	int rx_count;
>> +	u32 console_baud_rate;
>> +	u32 console_line;
>> +	u32 console_int;
>> +};
>> +
>> +static struct device_node *ma35d1serial_uart_nodes[UART_NR];
>> +static struct uart_ma35d1_port ma35d1serial_ports[UART_NR] = { 0 };
>> +static void __stop_tx(struct uart_ma35d1_port *p);
>> +static void transmit_chars(struct uart_ma35d1_port *up);
> Try to rearrange such that forward declarations are not necessary for
> functions.

OK, we will fix it.

>
>> +static struct uart_ma35d1_port *to_ma35d1_uart_port(struct uart_port *uart)
> static inline
>
>> +{
>> +	return container_of(uart, struct uart_ma35d1_port, port);
>> +}
>> +
>> +static u32 serial_in(struct uart_ma35d1_port *p, int offset)
>> +{
>> +	return __raw_readl(p->port.membase + offset);
>> +}
>> +
>> +static void serial_out(struct uart_ma35d1_port *p, int offset, int value)
>> +{
>> +	__raw_writel(value, p->port.membase + offset);
>> +}
>> +
>> +static void __stop_tx(struct uart_ma35d1_port *p)
>> +{
>> +	u32 ier;
>> +
>> +	ier = serial_in(p, UART_REG_IER);
>> +	if (ier & THRE_IEN)
>> +		serial_out(p, UART_REG_IER, ier & ~THRE_IEN);
>> +}
>> +
>> +static void ma35d1serial_stop_tx(struct uart_port *port)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +
>> +	__stop_tx(up);
>> +}
>> +
>> +static void ma35d1serial_start_tx(struct uart_port *port)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +	u32 ier;
>> +	struct circ_buf *xmit = &up->port.state->xmit;
>> +
>> +	ier = serial_in(up, UART_REG_IER);
>> +	serial_out(up, UART_REG_IER, ier & ~THRE_IEN);
>> +	if (uart_circ_chars_pending(xmit) <
>> +	    (16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0x3F)))
>> +		transmit_chars(up);
>> +	serial_out(up, UART_REG_IER, ier | THRE_IEN);
>> +}
>> +
>> +static void ma35d1serial_stop_rx(struct uart_port *port)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +
>> +	serial_out(up, UART_REG_IER, serial_in(up, UART_REG_IER) & ~RDA_IEN);
>> +}
>> +
>> +static void
>> +receive_chars(struct uart_ma35d1_port *up)
>> +{
>> +	u8 ch;
>> +	u32 fsr;
>> +	u32 isr;
>> +	u32 dcnt;
>> +	char flag;
>> +
>> +	isr = serial_in(up, UART_REG_ISR);
>> +	fsr = serial_in(up, UART_REG_FSR);
>> +
>> +	while (!(fsr & RX_EMPTY)) {
>> +		flag = TTY_NORMAL;
>> +		up->port.icount.rx++;
>> +
>> +		if (unlikely(fsr & (BIF | FEF | PEF | RX_OVER_IF))) {
>> +			if (fsr & BIF) {
>> +				serial_out(up, UART_REG_FSR, BIF);
>> +				up->port.icount.brk++;
>> +				if (uart_handle_break(&up->port))
>> +					continue;
>> +			}
>> +			if (fsr & FEF) {
>> +				serial_out(up, UART_REG_FSR, FEF);
>> +				up->port.icount.frame++;
>> +			}
>> +			if (fsr & PEF) {
>> +				serial_out(up, UART_REG_FSR, PEF);
>> +				up->port.icount.parity++;
>> +			}
>> +			if (fsr & RX_OVER_IF) {
>> +				serial_out(up, UART_REG_FSR, RX_OVER_IF);
>> +				up->port.icount.overrun++;
>> +			}
> Do you need to write each of those individually to clear(?) them or could
> you just do one write here after the accounting is done:
> 			serial_out(up, UART_REG_FSR, fsr & (BIF|FEF|PEF|RX_OVER_IF));
> ?

OK, we will modify it clear flags together as possible.

> Also, add some driver specific prefix for the flag naming (all driver
> specific ones, not just these if you have others besides these).
>

We will fix it.

>> +			if (fsr & BIF)
>> +				flag = TTY_BREAK;
>> +			if (fsr & PEF)
>> +				flag = TTY_PARITY;
>> +			if (fsr & FEF)
>> +				flag = TTY_FRAME;
> Are you sure this is the right prioritization or do you perhaps want else
> ifs like in some other serial drivers?

We will modify it as:

     if (fsr & BIF)
         flag = TTY_BREAK;
     else if (fsr & PEF)
         flag = TTY_PARITY;
     else if (fsr & FEF)
         flag = TTY_FRAME;



>> +		}
>> +		ch = (u8)serial_in(up, UART_REG_RBR);
> Drop the case.

I  will fix it.

>> +		if (uart_handle_sysrq_char(&up->port, ch))
>> +			continue;
>> +
>> +		uart_insert_char(&up->port, fsr, RX_OVER_IF, ch, flag);
>> +		up->rx_count++;
>> +		dcnt = (serial_in(up, UART_REG_FSR) >> 8) & 0x3f;
>> +		if (up->rx_count > 1023) {
>> +			spin_lock(&up->port.lock);
>> +			tty_flip_buffer_push(&up->port.state->port);
>> +			spin_unlock(&up->port.lock);
>> +			up->rx_count = 0;
> Why is all this ->rx_count trickery necessary? What's so special with the
> size in question?

That is the tricky inherited from the previous arm9 project.
We will remove it.

>> +			if ((isr & RXTO_IF) && (dcnt == 0))
>> +				goto tout_end;
>> +		}
>> +		if (isr & RDA_IF) {
>> +			if (dcnt == 1)
> Merge to the same comdition.
>
> dcnt could probably have a more descriptive name.

OK, We will fix it.

>> +				return;
>> +		}
>> +		fsr = serial_in(up, UART_REG_FSR);
>> +	}
>> +	spin_lock(&up->port.lock);
>> +	tty_flip_buffer_push(&up->port.state->port);
>> +	spin_unlock(&up->port.lock);
>> +tout_end:
>> +	up->rx_count = 0;
>> +}
>> +
>> +static void transmit_chars(struct uart_ma35d1_port *up)
>> +{
>> +	struct circ_buf *xmit = &up->port.state->xmit;
>> +	int count = 16 - ((serial_in(up, UART_REG_FSR) >> 16) & 0xF);
>> +
>> +	if (serial_in(up, UART_REG_FSR) & TX_FULL)
>> +		count = 0;
>> +	if (up->port.x_char) {
>> +		serial_out(up, UART_REG_THR, up->port.x_char);
>> +		up->port.icount.tx++;
>> +		up->port.x_char = 0;
>> +		return;
>> +	}
>> +	if (uart_tx_stopped(&up->port)) {
>> +		ma35d1serial_stop_tx(&up->port);
>> +		return;
>> +	}
>> +	if (uart_circ_empty(xmit)) {
>> +		__stop_tx(up);
>> +		return;
>> +	}
>> +	while (count > 0) {
>> +		serial_out(up, UART_REG_THR, xmit->buf[xmit->tail]);
>> +		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
>> +		up->port.icount.tx++;
>> +		count--;
>> +		if (uart_circ_empty(xmit))
>> +			break;
>> +	}
>> +	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
>> +		uart_write_wakeup(&up->port);
>> +	if (uart_circ_empty(xmit))
>> +		__stop_tx(up);
>> +}
>> +
>> +static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)dev_id;
>> +	u32 isr, fsr;
>> +
>> +	isr = serial_in(up, UART_REG_ISR);
>> +	fsr = serial_in(up, UART_REG_FSR);
>> +	if (isr & (RDA_IF | RXTO_IF))
>> +		receive_chars(up);
>> +	if (isr & THRE_INT)
>> +		transmit_chars(up);
>> +	if (fsr & (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF))
>> +		serial_out(up, UART_REG_FSR,
>> +			   (BIF | FEF | PEF | RX_OVER_IF | TX_OVER_IF));
> Hmm... Why write these again here... Didn't receive_chars() already
> clear(?) most of these bits??

Yes, most of these are redundant.
I will fix it as only

     if (fsr & TX_OVER_IF))
         serial_out(up, UART_REG_FSR,  TX_OVER_IF);


>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static u32 ma35d1serial_tx_empty(struct uart_port *port)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +	u32 fsr;
>> +
>> +	fsr = serial_in(up, UART_REG_FSR);
>> +	return (fsr & (TE_FLAG | TX_EMPTY)) == (TE_FLAG | TX_EMPTY) ?
>> +		TIOCSER_TEMT : 0;
>> +}
>> +
>> +static u32 ma35d1serial_get_mctrl(struct uart_port *port)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +	u32 status;
>> +	u32 ret = 0;
>> +
>> +	status = serial_in(up, UART_REG_MSR);
>> +	if (!(status & 0x10))
>> +		ret |= TIOCM_CTS;
>> +	return ret;
>> +}
>> +
>> +static void ma35d1serial_set_mctrl(struct uart_port *port, u32 mctrl)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +	u32 mcr = 0;
>> +	u32 ier = 0;
>> +
>> +	if (mctrl & TIOCM_RTS) {
>> +		/* set RTS high level trigger */
>> +		mcr = serial_in(up, UART_REG_MCR);
>> +		mcr |= 0x200;
>> +		mcr &= ~(0x2);
>> +	}
>> +	if (up->mcr & UART_MCR_AFE) {
>> +		/* set RTS high level trigger */
>> +		mcr = serial_in(up, UART_REG_MCR);
>> +		mcr |= 0x200;
>> +		mcr &= ~(0x2);
>> +
>> +		/* enable CTS/RTS auto-flow control */
>> +		serial_out(up, UART_REG_IER,
>> +			   (serial_in(up, UART_REG_IER) | (0x3000)));
> Once you have named the bits probably with defines, most of the comments
> such as the one above are rendered redundant and should be dropped.

OK, we will drop these unused comments.

>> +
>> +		/* Set hardware flow control */
>> +		up->port.flags |= UPF_HARD_FLOW;
>> +	} else {
>> +		/* disable CTS/RTS auto-flow control */
>> +		ier = serial_in(up, UART_REG_IER);
>> +		ier &= ~(0x3000);
>> +		serial_out(up, UART_REG_IER, ier);
>> +
>> +		/* un-set hardware flow control */
>> +		up->port.flags &= ~UPF_HARD_FLOW;
>> +	}
>> +
>> +	/* set CTS high level trigger */
>> +	serial_out(up, UART_REG_MSR, (serial_in(up, UART_REG_MSR) | (0x100)));
>> +	serial_out(up, UART_REG_MCR, mcr);
>> +}
>> +
>> +static void ma35d1serial_break_ctl(struct uart_port *port, int break_state)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +	unsigned long flags;
>> +	u32 lcr;
>> +
>> +	spin_lock_irqsave(&up->port.lock, flags);
>> +	lcr = serial_in(up, UART_REG_LCR);
>> +	if (break_state != 0)
>> +		lcr |= BCB; /* set break */
>> +	else
>> +		lcr &= ~BCB; /* clr break */
> While BCB might come from HW naming, a better name for would make these
> very obvious (and the comments unnecessary).

We will rename it and remove comments.

>> +	serial_out(up, UART_REG_LCR, lcr);
>> +	spin_unlock_irqrestore(&up->port.lock, flags);
>> +}
>> +
>> +static int ma35d1serial_startup(struct uart_port *port)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +	struct tty_struct *tty = port->state->port.tty;
>> +	int retval;
>> +
>> +	/* Reset FIFO */
>> +	serial_out(up, UART_REG_FCR, TFR | RFR /* | RX_DIS */);
>> +
>> +	/* Clear pending interrupts */
>> +	serial_out(up, UART_REG_ISR, 0xFFFFFFFF);
> ~0 is another option.
>
>> +
>> +	retval = request_irq(port->irq, ma35d1serial_interrupt, 0,
>> +			     tty ? tty->name : "ma35d1_serial", port);
> Why such exceptional name trickery?

I will modify it as:
     const char *name = to_platform_device(port->dev)->name;

     retval = request_irq(port->irq, ma35d1serial_interrupt, 0, name, port);

>
>> +	if (retval) {
>> +		dev_err(up->port.dev, "request irq failed.\n");
>> +		return retval;
>> +	}
>> +
>> +	/* Now, initialize the UART */
>> +	/* FIFO trigger level 4 byte */
>> +	/* RTS trigger level 8 bytes */
>> +	serial_out(up, UART_REG_FCR,
>> +		   serial_in(up, UART_REG_FCR) | 0x10 | 0x20000);
>> +	serial_out(up, UART_REG_LCR, 0x7); /* 8 bit */
>> +	serial_out(up, UART_REG_TOR, 0x40);
>> +	serial_out(up, UART_REG_IER,
>> +		   RTO_IEN | RDA_IEN | TIME_OUT_EN | BUFERR_IEN);
>> +	return 0;
>> +}
>> +
>> +static void ma35d1serial_shutdown(struct uart_port *port)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +
>> +	free_irq(port->irq, port);
>> +
>> +	/* Disable interrupts from this port */
>> +	serial_out(up, UART_REG_IER, 0);
>> +}
>> +
>> +static u32 ma35d1serial_get_divisor(struct uart_port *port, u32 baud)
>> +{
>> +	u32 quot;
>> +
>> +	quot = (port->uartclk / baud) - 2;
> Unnecessary parenthesis. (+Somebody already commented about the
> unnecessary variable.)
>
>> +	return quot;
>> +}
>> +
>> +static void ma35d1serial_set_termios(struct uart_port *port,
>> +				     struct ktermios *termios,
>> +				     const struct ktermios *old)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +	u32 lcr = 0;
>> +	unsigned long flags;
>> +	u32 baud, quot;
>> +
>> +	switch (termios->c_cflag & CSIZE) {
>> +	case CS5:
>> +		lcr = 0;
>> +		break;
>> +	case CS6:
>> +		lcr |= 1;
>> +		break;
>> +	case CS7:
>> +		lcr |= 2;
>> +		break;
>> +	case CS8:
>> +	default:
>> +		lcr |= 3;
>> +		break;
>> +	}
>> +
>> +	if (termios->c_cflag & CSTOPB)
>> +		lcr |= NSB;
>> +	if (termios->c_cflag & PARENB)
>> +		lcr |= PBE;
>> +	if (!(termios->c_cflag & PARODD))
>> +		lcr |= EPE;
>> +	if (termios->c_cflag & CMSPAR)
>> +		lcr |= SPE;
>> +
>> +	baud = uart_get_baud_rate(port, termios, old, port->uartclk / 0xffff,
>> +				  port->uartclk / 11);
>> +
>> +	quot = ma35d1serial_get_divisor(port, baud);
>> +
>> +	/*
>> +	 * Ok, we're now changing the port state.  Do it with
>> +	 * interrupts disabled.
>> +	 */
>> +	spin_lock_irqsave(&up->port.lock, flags);
>> +
>> +	up->port.read_status_mask = RX_OVER_IF;
>> +	if (termios->c_iflag & INPCK)
>> +		up->port.read_status_mask |= FEF | PEF;
>> +	if (termios->c_iflag & (BRKINT | PARMRK))
>> +		up->port.read_status_mask |= BIF;
>> +
>> +	/*
>> +	 * Characteres to ignore
>> +	 */
>> +	up->port.ignore_status_mask = 0;
>> +	if (termios->c_iflag & IGNPAR)
>> +		up->port.ignore_status_mask |= FEF | PEF;
>> +	if (termios->c_iflag & IGNBRK) {
>> +		up->port.ignore_status_mask |= BIF;
>> +		/*
>> +		 * If we're ignoring parity and break indicators,
>> +		 * ignore overruns too (for real raw support).
>> +		 */
>> +		if (termios->c_iflag & IGNPAR)
>> +			up->port.ignore_status_mask |= RX_OVER_IF;
>> +	}
>> +	if (termios->c_cflag & CRTSCTS)
>> +		up->mcr |= UART_MCR_AFE;
>> +	else
>> +		up->mcr &= ~UART_MCR_AFE;
>> +
>> +	ma35d1serial_set_mctrl(&up->port, up->port.mctrl);
>> +	serial_out(up, UART_REG_BAUD, quot | 0x30000000);
>> +	serial_out(up, UART_REG_LCR, lcr);
> You need to do uart_update_timeout() in the set_termios function.

We will fix it.

>
>> +	spin_unlock_irqrestore(&up->port.lock, flags);
>> +}
>> +
>> +static void ma35d1serial_release_port(struct uart_port *port)
>> +{
>> +	iounmap(port->membase);
>> +	port->membase = NULL;
>> +}
>> +
>> +static int ma35d1serial_request_port(struct uart_port *port)
>> +{
>> +	return 0;
>> +}
>> +
>> +static void ma35d1serial_config_port(struct uart_port *port, int flags)
>> +{
>> +	int ret;
>> +
>> +	/*
>> +	 * Find the region that we can probe for.  This in turn
>> +	 * tells us whether we can probe for the type of port.
>> +	 */
>> +	ret = ma35d1serial_request_port(port);
>> +	if (ret < 0)
>> +		return;
>> +	port->type = PORT_MA35D1;
>> +}
>> +
>> +static int ma35d1serial_verify_port(struct uart_port *port,
>> +				    struct serial_struct *ser)
>> +{
>> +	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MA35D1)
>> +		return -EINVAL;
>> +	return 0;
>> +}
>> +
>> +static const char *ma35d1serial_type(struct uart_port *port)
>> +{
>> +	return (port->type == PORT_MA35D1) ? "MA35D1" : NULL;
>> +}
>> +
>> +/* Enable or disable the rs485 support */
>> +static int ma35d1serial_config_rs485(struct uart_port *port,
>> +				     struct ktermios *termios,
>> +				     struct serial_rs485 *rs485conf)
>> +{
>> +	struct uart_ma35d1_port *p = to_ma35d1_uart_port(port);
>> +
>> +	p->rs485 = *rs485conf;
>> +
>> +	if (p->rs485.delay_rts_before_send >= 1000)
>> +		p->rs485.delay_rts_before_send = 1000;
> Don't do this in driver, the core handles the delay limits. You don't seem
> to be using the value anyway for anything???
>
> Please separate the RS485 support into its own patch.


OK, we will remove RS485 support from this initial patch.
Once this initial patch was merged, we will submit the patch for RS485 
support.

>
>> +	serial_out(p, UART_FUN_SEL,
>> +		   (serial_in(p, UART_FUN_SEL) & ~FUN_SEL_MASK));
>> +
>> +	if (rs485conf->flags & SER_RS485_ENABLED) {
>> +		serial_out(p, UART_FUN_SEL,
>> +			   (serial_in(p, UART_FUN_SEL) | FUN_SEL_RS485));
> Does this pair of serial_out()s glitch the RS485 line if ->rs485_config()
> is called while RS485 mode is already set?
>
> Why you need to do serial_in() from the UART_FUN_SEL twice?

UART_FUN_SEL (2 bits) definition:
00 - UART function
01 - IrDA function
11 - RS485 function

The first searial_in() is used to clear set as UART function.
The second one is used to set RS485 function if SER_RS485_ENABLED is true.


>> +
>> +		if (rs485conf->flags & SER_RS485_RTS_ON_SEND)
>> +			serial_out(p, UART_REG_MCR,
>> +				   (serial_in(p, UART_REG_MCR) & ~0x200));
>> +		else
>> +			serial_out(p, UART_REG_MCR,
>> +				   (serial_in(p, UART_REG_MCR) | 0x200));
>> +
>> +		/* set auto direction mode */
>> +		serial_out(p, UART_REG_ALT_CSR,
>> +			   (serial_in(p, UART_REG_ALT_CSR) | (1 << 10)));
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int ma35d1serial_ioctl(struct uart_port *port, u32 cmd, unsigned long arg)
>> +{
>> +	switch (cmd) {
>> +	default:
>> +		return -ENOIOCTLCMD;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static const struct uart_ops ma35d1serial_ops = {
>> +	.tx_empty     = ma35d1serial_tx_empty,
>> +	.set_mctrl    = ma35d1serial_set_mctrl,
>> +	.get_mctrl    = ma35d1serial_get_mctrl,
>> +	.stop_tx      = ma35d1serial_stop_tx,
>> +	.start_tx     = ma35d1serial_start_tx,
>> +	.stop_rx      = ma35d1serial_stop_rx,
>> +	.break_ctl    = ma35d1serial_break_ctl,
>> +	.startup      = ma35d1serial_startup,
>> +	.shutdown     = ma35d1serial_shutdown,
>> +	.set_termios  = ma35d1serial_set_termios,
>> +	.type         = ma35d1serial_type,
>> +	.release_port = ma35d1serial_release_port,
>> +	.request_port = ma35d1serial_request_port,
>> +	.config_port  = ma35d1serial_config_port,
>> +	.verify_port  = ma35d1serial_verify_port,
>> +	.ioctl        = ma35d1serial_ioctl,
>> +};
>> +
>> +static const struct of_device_id ma35d1_serial_of_match[] = {
>> +	{ .compatible = "nuvoton,ma35d1-uart" },
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, ma35d1_serial_of_match);
>> +
>> +#ifdef CONFIG_SERIAL_NUVOTON_MA35D1_CONSOLE
>> +
>> +static void ma35d1serial_console_putchar(struct uart_port *port,
>> +					 unsigned char ch)
>> +{
>> +	struct uart_ma35d1_port *up = (struct uart_ma35d1_port *)port;
>> +
>> +	do {
>> +	} while ((serial_in(up, UART_REG_FSR) & TX_FULL));
>> +	serial_out(up, UART_REG_THR, ch);
>> +}
>> +
>> +/*
>> + *  Print a string to the serial port trying not to disturb
>> + *  any possible real use of the port...
>> + *
>> + *  The console_lock must be held when we get here.
>> + */
>> +static void ma35d1serial_console_write(struct console *co,
>> +				       const char *s, u32 count)
>> +{
>> +	struct uart_ma35d1_port *up = &ma35d1serial_ports[co->index];
>> +	unsigned long flags;
>> +	u32 ier;
>> +
>> +	local_irq_save(flags);
>> +
>> +	/*
>> +	 *  First save the IER then disable the interrupts
>> +	 */
>> +	ier = serial_in(up, UART_REG_IER);
>> +	serial_out(up, UART_REG_IER, 0);
>> +
>> +	uart_console_write(&up->port, s, count, ma35d1serial_console_putchar);
>> +
>> +	/*
>> +	 *  Finally, wait for transmitter to become empty
>> +	 *  and restore the IER
>> +	 */
>> +	do {
>> +	} while (!(serial_in(up, UART_REG_FSR) & TX_EMPTY));
>> +	serial_out(up, UART_REG_IER, ier);
>> +	local_irq_restore(flags);
>> +}
>> +
>> +static int __init ma35d1serial_console_setup(struct console *co,
>> +					     char *options)
>> +{
>> +	struct device_node *np = ma35d1serial_uart_nodes[co->index];
>> +	struct uart_ma35d1_port *p = &ma35d1serial_ports[co->index];
>> +	u32 val32[4];
>> +	struct uart_port *port;
>> +	int baud = 115200;
>> +	int bits = 8;
>> +	int parity = 'n';
>> +	int flow = 'n';
>> +
>> +	/*
>> +	 * Check whether an invalid uart number has been specified, and
>> +	 * if so, search for the first available port that does have
>> +	 * console support.
>> +	 */
>> +	if ((co->index < 0) || (co->index >= UART_NR)) {
>> +		pr_debug("Console Port%x out of range\n", co->index);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (of_property_read_u32_array(np, "reg", val32, 4) != 0)
>> +		return -EINVAL;
>> +	p->port.iobase = val32[1];
>> +	p->port.membase = ioremap(p->port.iobase, 0x10000);
>> +	p->port.ops = &ma35d1serial_ops;
>> +	p->port.line = 0;
>> +	p->port.uartclk = 24000000;
>> +
>> +	port = &ma35d1serial_ports[co->index].port;
>> +	return uart_set_options(port, co, baud, parity, bits, flow);
>> +}
>> +
>> +static struct console ma35d1serial_console = {
>> +	.name    = "ttyS",
>> +	.write   = ma35d1serial_console_write,
>> +	.device  = uart_console_device,
>> +	.setup   = ma35d1serial_console_setup,
>> +	.flags   = CON_PRINTBUFFER | CON_ENABLED,
>> +	.index   = -1,
>> +	.data    = &ma35d1serial_reg,
>> +};
>> +
>> +static void
>> +ma35d1serial_console_init_port(void)
>> +{
>> +	int i = 0;
>> +	struct device_node *np;
>> +
>> +	for_each_matching_node(np, ma35d1_serial_of_match) {
>> +		if (ma35d1serial_uart_nodes[i] == NULL) {
>> +			ma35d1serial_uart_nodes[i] = np;
>> +			i++;
>> +		}
>> +	}
>> +}
>> +
>> +static int __init ma35d1serial_console_init(void)
>> +{
>> +	ma35d1serial_console_init_port();
>> +	register_console(&ma35d1serial_console);
>> +	return 0;
>> +}
>> +console_initcall(ma35d1serial_console_init);
>> +
>> +#define MA35D1SERIAL_CONSOLE    (&ma35d1serial_console)
>> +#else
>> +#define MA35D1SERIAL_CONSOLE    NULL
>> +#endif
>> +
>> +static struct uart_driver ma35d1serial_reg = {
>> +	.owner        = THIS_MODULE,
>> +	.driver_name  = "serial",
>> +	.dev_name     = "ttyS",
>> +	.major        = TTY_MAJOR,
>> +	.minor        = 64,
>> +	.cons         = MA35D1SERIAL_CONSOLE,
>> +	.nr           = UART_NR,
>> +};
>> +
>> +/**
>> + *  Suspend one serial port.
>> + */
>> +void ma35d1serial_suspend_port(int line)
>> +{
>> +	uart_suspend_port(&ma35d1serial_reg, &ma35d1serial_ports[line].port);
>> +}
>> +EXPORT_SYMBOL(ma35d1serial_suspend_port);
>> +
>> +/**
>> + *  Resume one serial port.
>> + */
>> +void ma35d1serial_resume_port(int line)
>> +{
>> +	struct uart_ma35d1_port *up = &ma35d1serial_ports[line];
>> +
>> +	uart_resume_port(&ma35d1serial_reg, &up->port);
>> +}
>> +EXPORT_SYMBOL(ma35d1serial_resume_port);
>> +
>> +/*
>> + * Register a set of serial devices attached to a platform device.
>> + * The list is terminated with a zero flags entry, which means we expect
>> + * all entries to have at least UPF_BOOT_AUTOCONF set.
>> + */
>> +static int ma35d1serial_probe(struct platform_device *pdev)
>> +{
>> +	struct resource *res_mem;
>> +	struct uart_ma35d1_port *up;
>> +	int ret;
>> +	struct clk *clk;
>> +	int err;
>> +
>> +	if (pdev->dev.of_node) {
>> +		ret = of_alias_get_id(pdev->dev.of_node, "serial");
>> +		if (ret < 0) {
>> +			dev_err(&pdev->dev,
>> +				"failed to get alias/pdev id, errno %d\n",
>> +				ret);
> Just put error prints to one line if you don't break 100 chars limit.

But the checkpatch limitation is 80 characters.

>> +		return ret;
> Misaligned line.

will be fixed

>
>> +		}
>> +	}
>> +	up = &ma35d1serial_ports[ret];
>> +	up->port.line = ret;
>> +	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res_mem)
>> +		return -ENODEV;
>> +
>> +	up->port.iobase = res_mem->start;
>> +	up->port.membase = ioremap(up->port.iobase, 0x10000);
> Define for the literal.

OK, we will have #define for it.

>> +	up->port.ops = &ma35d1serial_ops;
>> +
>> +	spin_lock_init(&up->port.lock);
>> +
>> +	clk = of_clk_get(pdev->dev.of_node, 0);
>> +	if (IS_ERR(clk)) {
>> +		err = PTR_ERR(clk);
>> +		dev_err(&pdev->dev, "failed to get core clk: %d\n", err);
>> +		return -ENOENT;
>> +	}
>> +	err = clk_prepare_enable(clk);
>> +	if (err)
>> +		return -ENOENT;
>> +
>> +	if (up->port.line != 0)
>> +		up->port.uartclk = clk_get_rate(clk);
>> +	up->port.irq = platform_get_irq(pdev, 0);
>> +	up->port.dev = &pdev->dev;
>> +	up->port.flags = UPF_BOOT_AUTOCONF;
>> +	up->port.rs485_config = ma35d1serial_config_rs485;
> Please provide also .rs485_supported for the serial core to handle the
> supported features for you.

OK, we will add it.

>> +	ret = uart_add_one_port(&ma35d1serial_reg, &up->port);
>> +	platform_set_drvdata(pdev, up);
>> +	return 0;
>> +}
>> +
>> +/*
>> + * Remove serial ports registered against a platform device.
>> + */
>> +static int ma35d1serial_remove(struct platform_device *dev)
>> +{
>> +	int i;
>> +	struct uart_port *port = platform_get_drvdata(dev);
>> +
>> +	free_irq(port->irq, port);
>> +	for (i = 0; i < UART_NR; i++) {
>> +		struct uart_ma35d1_port *up = &ma35d1serial_ports[i];
>> +
>> +		if (up->port.dev == &dev->dev)
>> +			uart_remove_one_port(&ma35d1serial_reg, &up->port);
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int ma35d1serial_suspend(struct platform_device *dev,
>> +				pm_message_t state)
>> +{
>> +	int i;
>> +	struct uart_ma35d1_port *up;
>> +
>> +	if (dev->dev.of_node)
>> +		i = of_alias_get_id(dev->dev.of_node, "serial");
>> +	if (i < 0) {
>> +		dev_err(&dev->dev, "failed to get alias/pdev id, errno %d\n",
>> +			i);
> Just put this to the same line with the rest.
>
>> +		return i;
>> +	}
>> +	up = &ma35d1serial_ports[i];
>> +	if (i == 0) {
>> +		up->console_baud_rate = serial_in(up, UART_REG_BAUD);
>> +		up->console_line = serial_in(up, UART_REG_LCR);
>> +		up->console_int = serial_in(up, UART_REG_IER);
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int ma35d1serial_resume(struct platform_device *dev)
>> +{
>> +	int i;
>> +	struct uart_ma35d1_port *up;
>> +
>> +	if (dev->dev.of_node)
>> +		i = of_alias_get_id(dev->dev.of_node, "serial");
>> +	if (i < 0) {
>> +		dev_err(&dev->dev, "failed to get alias/pdev id, errno %d\n",
>> +			i);
> Same line.

will be fixed

>
>> +		return i;
>> +	}
>> +	up = &ma35d1serial_ports[i];
>> +	if (i == 0) {
>> +		serial_out(up, UART_REG_BAUD, up->console_baud_rate);
>> +		serial_out(up, UART_REG_LCR, up->console_line);
>> +		serial_out(up, UART_REG_IER, up->console_int);
>> +	}
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver ma35d1serial_driver = {
>> +	.probe      = ma35d1serial_probe,
>> +	.remove     = ma35d1serial_remove,
>> +	.suspend    = ma35d1serial_suspend,
>> +	.resume     = ma35d1serial_resume,
>> +	.driver     = {
>> +		.name   = "ma35d1-uart",
>> +		.owner  = THIS_MODULE,
>> +		.of_match_table = of_match_ptr(ma35d1_serial_of_match),
>> +	},
>> +};
>> +
>> +static int __init ma35d1serial_init(void)
>> +{
>> +	int ret;
>> +
>> +	ret = uart_register_driver(&ma35d1serial_reg);
>> +	if (ret)
>> +		return ret;
>> +	ret = platform_driver_register(&ma35d1serial_driver);
>> +	if (ret)
>> +		uart_unregister_driver(&ma35d1serial_reg);
>> +	return ret;
>> +}
>> +
>> +static void __exit ma35d1serial_exit(void)
>> +{
>> +	platform_driver_unregister(&ma35d1serial_driver);
>> +	uart_unregister_driver(&ma35d1serial_reg);
>> +}
>> +
>> +module_init(ma35d1serial_init);
>> +module_exit(ma35d1serial_exit);
>> +
>> +MODULE_LICENSE("GPL v2");
> "GPL" is enough for MODULE_LICENSE, the SPDX at the start of file covers
> more specific license variations.

I got it. Thank you.

>> +MODULE_DESCRIPTION("MA35D1 serial driver");
>> +MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
>> +
>> diff --git a/drivers/tty/serial/ma35d1_serial.h b/drivers/tty/serial/ma35d1_serial.h
>> new file mode 100644
>> index 000000000000..5fd845c31b29
>> --- /dev/null
>> +++ b/drivers/tty/serial/ma35d1_serial.h
>> @@ -0,0 +1,93 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + *  MA35D1 serial driver header file
>> + *  Copyright (C) 2023 Nuvoton Technology Corp.
>> + */
>> +#ifndef __MA35D1_SERIAL_H__
>> +#define __MA35D1_SERIAL_H__
>> +
>> +/* UART Receive/Transmit Buffer Register */
>> +#define UART_REG_RBR	0x00
>> +#define UART_REG_THR	0x00
>> +
>> +/* UART Interrupt Enable Register */
>> +#define UART_REG_IER	0x04
>> +#define RDA_IEN		0x00000001 /* RBR Available Interrupt Enable */
>> +#define THRE_IEN	0x00000002 /* THR Empty Interrupt Enable */
>> +#define RLS_IEN		0x00000004 /* RX Line Status Interrupt Enable */
>> +#define RTO_IEN		0x00000010 /* RX Time-out Interrupt Enable */
>> +#define BUFERR_IEN	0x00000020 /* Buffer Error Interrupt Enable */
>> +#define TIME_OUT_EN	0x00000800 /* RX Buffer Time-out Counter Enable */
>> +
>> +/* UART FIFO Control Register */
>> +#define UART_REG_FCR	0x08
>> +#define RFR		0x00000002 /* RX Field Software Reset */
>> +#define TFR		0x00000004 /* TX Field Software Reset */
>> +
>> +/* UART Line Control Register */
>> +#define UART_REG_LCR	0x0C
>> +#define	NSB		0x00000004 /* Number of “STOP Bit” */
>> +#define PBE		0x00000008 /* Parity Bit Enable */
>> +#define EPE		0x00000010 /* Even Parity Enable */
>> +#define SPE		0x00000020 /* Stick Parity Enable */
>> +#define BCB		0x00000040 /* Break Control */
>> +
>> +/* UART Modem Control Register */
>> +#define UART_REG_MCR	0x10
>> +#define RTS		0x00000020 /* nRTS Signal Control */
>> +#define RTSACTLV	0x00000200 /* nRTS Pin Active Level */
>> +#define RTSSTS		0x00002000 /* nRTS Pin Status (Read Only) */
>> +
>> +/* UART Modem Status Register */
>> +#define UART_REG_MSR	0x14
>> +#define CTSDETF		0x00000001 /* Detect nCTS State Change Flag */
>> +#define CTSSTS		0x00000010 /* nCTS Pin Status (Read Only) */
>> +#define CTSACTLV	0x00000100 /* nCTS Pin Active Level */
>> +
>> +/* UART FIFO Status Register */
>> +#define UART_REG_FSR	0x18
>> +#define RX_OVER_IF	0x00000001 /* RX Overflow Error Interrupt Flag */
>> +#define PEF		0x00000010 /* Parity Error Flag*/
>> +#define FEF		0x00000020 /* Framing Error Flag */
>> +#define BIF		0x00000040 /* Break Interrupt Flag */
>> +#define RX_EMPTY	0x00004000 /* Receiver FIFO Empty (Read Only) */
>> +#define RX_FULL		0x00008000 /* Receiver FIFO Full (Read Only) */
>> +#define TX_EMPTY	0x00400000 /* Transmitter FIFO Empty (Read Only) */
>> +#define TX_FULL		0x00800000 /* Transmitter FIFO Full (Read Only) */
>> +#define TX_OVER_IF	0x01000000 /* TX Overflow Error Interrupt Flag */
>> +#define TE_FLAG		0x10000000 /* Transmitter Empty Flag (Read Only) */
>> +
>> +/* UART Interrupt Status Register */
>> +#define UART_REG_ISR	0x1C
>> +#define RDA_IF		0x00000001 /* RBR Available Interrupt Flag */
>> +#define THRE_IF		0x00000002 /* THR Empty Interrupt Flag */
>> +#define RLSIF		0x00000004 /* Receive Line Interrupt Flag */
>> +#define MODEMIF		0x00000008 /* MODEM Interrupt Flag */
>> +#define RXTO_IF		0x00000010 /* RX Time-out Interrupt Flag */
>> +#define BUFEIF		0x00000020 /* Buffer Error Interrupt Flag */
>> +#define WK_IF		0x00000040 /* UART Wake-up Interrupt Flag */
>> +#define RDAINT		0x00000100 /* RBR Available Interrupt Indicator */
>> +#define THRE_INT	0x00000200 /* THR Empty Interrupt Indicator */
>> +
>> +/* UART Time-out Register */
>> +#define UART_REG_TOR	0x20
>> +
>> +/* UART Baud Rate Divider Register */
>> +#define UART_REG_BAUD	0x24
>> +
>> +/* UART Alternate Control/Status Register */
>> +#define UART_REG_ALT_CSR 0x2C
>> +
>> +/* UART Function Select Register */
>> +#define UART_FUN_SEL	0x30
>> +#define FUN_SEL_UART	0x00000000
>> +#define FUN_SEL_RS485	0x00000003
>> +#define FUN_SEL_MASK	0x00000007
> GENMASK(), then use FIELD_PREP() for the values.

We will fix them all.

>> +
>> +/* UART Wake-up Control Register */
>> +#define UART_REG_WKCTL	0x40
>> +
>> +/* UART Wake-up Status Register */
>> +#define UART_REG_WKSTS	0x44
>> +
>> +#endif /* __MA35D1_SERIAL_H__ */
>> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
>> index 281fa286555c..c6d53db17042 100644
>> --- a/include/uapi/linux/serial_core.h
>> +++ b/include/uapi/linux/serial_core.h
>> @@ -279,4 +279,7 @@
>>   /* Sunplus UART */
>>   #define PORT_SUNPLUS	123
>>   
>> +/* Nuvoton MA35D1 UART */
>> +#define PORT_MA35D1	124
>> +
>>   #endif /* _UAPILINUX_SERIAL_CORE_H */
>>

Best regards,
Jacky Huang



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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-20  8:23     ` Jacky Huang
@ 2023-03-20 10:04       ` Ilpo Järvinen
  2023-03-21 14:23         ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Ilpo Järvinen @ 2023-03-20 10:04 UTC (permalink / raw)
  To: Jacky Huang
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	Greg Kroah-Hartman, Jiri Slaby, devicetree, linux-clk, LKML,
	linux-serial, schung, Jacky Huang, mjchen

[-- Attachment #1: Type: text/plain, Size: 6853 bytes --]

On Mon, 20 Mar 2023, Jacky Huang wrote:

> Dear Ilpo,
> 
> 
> Thanks for your advice.
> 
> On 2023/3/16 下午 10:54, Ilpo Järvinen wrote:
> > Hi,
> > 
> > I'll not note all things below because others have already seemingly
> > commented many things.
> > 
> > On Wed, 15 Mar 2023, Jacky Huang wrote:
> > 
> > > From: Jacky Huang <ychuang3@nuvoton.com>
> > > 
> > > This adds UART and console driver for Nuvoton ma35d1 Soc.

> > > +		}
> > > +		ch = (u8)serial_in(up, UART_REG_RBR);
> > Drop the case.
> 
> I  will fix it.

I meant "cast" in case it wasn't obvious.

> > > +/* Enable or disable the rs485 support */
> > > +static int ma35d1serial_config_rs485(struct uart_port *port,
> > > +				     struct ktermios *termios,
> > > +				     struct serial_rs485 *rs485conf)
> > > +{
> > > +	struct uart_ma35d1_port *p = to_ma35d1_uart_port(port);
> > > +
> > > +	p->rs485 = *rs485conf;
> > > +
> > > +	if (p->rs485.delay_rts_before_send >= 1000)
> > > +		p->rs485.delay_rts_before_send = 1000;
> > Don't do this in driver, the core handles the delay limits. You don't seem
> > to be using the value anyway for anything???
> > 
> > Please separate the RS485 support into its own patch.
> 
> 
> OK, we will remove RS485 support from this initial patch.
> Once this initial patch was merged, we will submit the patch for RS485
> support.

You could do that but you could just as well include it into the same 
series as another patch after the main patch.

> > > +	serial_out(p, UART_FUN_SEL,
> > > +		   (serial_in(p, UART_FUN_SEL) & ~FUN_SEL_MASK));
> > > +
> > > +	if (rs485conf->flags & SER_RS485_ENABLED) {
> > > +		serial_out(p, UART_FUN_SEL,
> > > +			   (serial_in(p, UART_FUN_SEL) | FUN_SEL_RS485));
> > Does this pair of serial_out()s glitch the RS485 line if ->rs485_config()
> > is called while RS485 mode is already set?
> > 
> > Why you need to do serial_in() from the UART_FUN_SEL twice?
> 
> UART_FUN_SEL (2 bits) definition:
> 00 - UART function
> 01 - IrDA function
> 11 - RS485 function
> 
> The first searial_in() is used to clear set as UART function.
> The second one is used to set RS485 function if SER_RS485_ENABLED is true.

I got that, but it doesn't answer either of my questions which are:

Can you clear the UART function without causing a glitch in the RS485?
->rs485_config() can be called while already in RS485 mode so does it 
cause the UART to temporarily switch away from RS485 mode to "UART 
function" until the second write.

Also, you didn't explain why you need to read the register again, does 
the HW play with other bits when you do the clearing or to they remain 
the same (in which case you can just use a temporary variable to store 
the value)? ...It would be better to just write once too so this question 
might not matter in the end.

> > > +	if (pdev->dev.of_node) {
> > > +		ret = of_alias_get_id(pdev->dev.of_node, "serial");
> > > +		if (ret < 0) {
> > > +			dev_err(&pdev->dev,
> > > +				"failed to get alias/pdev id, errno %d\n",
> > > +				ret);
> > Just put error prints to one line if you don't break 100 chars limit.
> 
> But the checkpatch limitation is 80 characters.

No, it isn't. It was changed years ago already.

> > > +++ b/drivers/tty/serial/ma35d1_serial.h
> > > @@ -0,0 +1,93 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 */
> > > +/*
> > > + *  MA35D1 serial driver header file
> > > + *  Copyright (C) 2023 Nuvoton Technology Corp.
> > > + */
> > > +#ifndef __MA35D1_SERIAL_H__
> > > +#define __MA35D1_SERIAL_H__
> > > +
> > > +/* UART Receive/Transmit Buffer Register */
> > > +#define UART_REG_RBR	0x00
> > > +#define UART_REG_THR	0x00
> > > +
> > > +/* UART Interrupt Enable Register */
> > > +#define UART_REG_IER	0x04
> > > +#define RDA_IEN		0x00000001 /* RBR Available Interrupt Enable
> > > */
> > > +#define THRE_IEN	0x00000002 /* THR Empty Interrupt Enable */
> > > +#define RLS_IEN		0x00000004 /* RX Line Status Interrupt Enable
> > > */
> > > +#define RTO_IEN		0x00000010 /* RX Time-out Interrupt Enable */
> > > +#define BUFERR_IEN	0x00000020 /* Buffer Error Interrupt Enable */
> > > +#define TIME_OUT_EN	0x00000800 /* RX Buffer Time-out Counter
> > > Enable */
> > > +
> > > +/* UART FIFO Control Register */
> > > +#define UART_REG_FCR	0x08
> > > +#define RFR		0x00000002 /* RX Field Software Reset */
> > > +#define TFR		0x00000004 /* TX Field Software Reset */
> > > +
> > > +/* UART Line Control Register */
> > > +#define UART_REG_LCR	0x0C
> > > +#define	NSB		0x00000004 /* Number of “STOP Bit” */
> > > +#define PBE		0x00000008 /* Parity Bit Enable */
> > > +#define EPE		0x00000010 /* Even Parity Enable */
> > > +#define SPE		0x00000020 /* Stick Parity Enable */
> > > +#define BCB		0x00000040 /* Break Control */
> > > +
> > > +/* UART Modem Control Register */
> > > +#define UART_REG_MCR	0x10
> > > +#define RTS		0x00000020 /* nRTS Signal Control */
> > > +#define RTSACTLV	0x00000200 /* nRTS Pin Active Level */
> > > +#define RTSSTS		0x00002000 /* nRTS Pin Status (Read Only) */
> > > +
> > > +/* UART Modem Status Register */
> > > +#define UART_REG_MSR	0x14
> > > +#define CTSDETF		0x00000001 /* Detect nCTS State Change Flag */
> > > +#define CTSSTS		0x00000010 /* nCTS Pin Status (Read Only) */
> > > +#define CTSACTLV	0x00000100 /* nCTS Pin Active Level */
> > > +
> > > +/* UART FIFO Status Register */
> > > +#define UART_REG_FSR	0x18
> > > +#define RX_OVER_IF	0x00000001 /* RX Overflow Error Interrupt Flag */
> > > +#define PEF		0x00000010 /* Parity Error Flag*/
> > > +#define FEF		0x00000020 /* Framing Error Flag */
> > > +#define BIF		0x00000040 /* Break Interrupt Flag */
> > > +#define RX_EMPTY	0x00004000 /* Receiver FIFO Empty (Read Only) */
> > > +#define RX_FULL		0x00008000 /* Receiver FIFO Full (Read Only)
> > > */
> > > +#define TX_EMPTY	0x00400000 /* Transmitter FIFO Empty (Read Only) */
> > > +#define TX_FULL		0x00800000 /* Transmitter FIFO Full (Read
> > > Only) */
> > > +#define TX_OVER_IF	0x01000000 /* TX Overflow Error Interrupt Flag */
> > > +#define TE_FLAG		0x10000000 /* Transmitter Empty Flag (Read
> > > Only) */
> > > +
> > > +/* UART Interrupt Status Register */
> > > +#define UART_REG_ISR	0x1C
> > > +#define RDA_IF		0x00000001 /* RBR Available Interrupt Flag */
> > > +#define THRE_IF		0x00000002 /* THR Empty Interrupt Flag */
> > > +#define RLSIF		0x00000004 /* Receive Line Interrupt Flag */
> > > +#define MODEMIF		0x00000008 /* MODEM Interrupt Flag */
> > > +#define RXTO_IF		0x00000010 /* RX Time-out Interrupt Flag */
> > > +#define BUFEIF		0x00000020 /* Buffer Error Interrupt Flag */
> > > +#define WK_IF		0x00000040 /* UART Wake-up Interrupt Flag */
> > > +#define RDAINT		0x00000100 /* RBR Available Interrupt
> > > Indicator */
> > > +#define THRE_INT	0x00000200 /* THR Empty Interrupt Indicator */

I forgot to mention earlier, there are many defines above which should use 
BIT().


-- 
 i.

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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-19  5:16     ` Jacky Huang
@ 2023-03-20 10:31       ` Ilpo Järvinen
  2023-03-21 15:03         ` Jacky Huang
  0 siblings, 1 reply; 89+ messages in thread
From: Ilpo Järvinen @ 2023-03-20 10:31 UTC (permalink / raw)
  To: Jacky Huang
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	Greg Kroah-Hartman, Jiri Slaby, devicetree, linux-clk, LKML,
	linux-serial, schung, Jacky Huang

[-- Attachment #1: Type: text/plain, Size: 4649 bytes --]

On Sun, 19 Mar 2023, Jacky Huang wrote:

> 
> On 2023/3/16 下午 11:56, Ilpo Järvinen wrote:
> > On Wed, 15 Mar 2023, Jacky Huang wrote:
> > 
> > > From: Jacky Huang <ychuang3@nuvoton.com>
> > > 
> > > The clock controller generates clocks for the whole chip, including
> > > system clocks and all peripheral clocks. This driver support ma35d1
> > > clock gating, divider, and individual PLL configuration.
> > > 
> > > There are 6 PLLs in ma35d1 SoC:
> > >    - CA-PLL for the two Cortex-A35 CPU clock
> > >    - SYS-PLL for system bus, which comes from the companion MCU
> > >      and cannot be programmed by clock controller.
> > >    - DDR-PLL for DDR
> > >    - EPLL for GMAC and GFX, Display, and VDEC IPs.
> > >    - VPLL for video output pixel clock
> > >    - APLL for SDHC, I2S audio, and other IPs.
> > > CA-PLL has only one operation mode.
> > > DDR-PLL, EPLL, VPLL, and APLL are advanced PLLs which have 3
> > > operation modes: integer mode, fraction mode, and spread specturm mode.
> > > 
> > > Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
> > > ---

> > > +};
> > > +
> > > +#define to_ma35d1_adc_clk_divider(_hw)	\
> > > +	container_of(_hw, struct ma35d1_adc_clk_divider, hw)
> > static inline
> 
> 
> I will modify these "static" functions as "static inline".

No, that's not what I meant. Make the container_of define static inline
function instead, no other functions. (Or if you have more than one of 
such, all of them of course).

> > > +}
> > > diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c
> > > b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> > > new file mode 100644
> > > index 000000000000..79e724b148fa
> > > --- /dev/null
> > > +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> > > @@ -0,0 +1,534 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Copyright (C) 2023 Nuvoton Technology Corp.
> > > + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> > > + */
> > > +
> > > +#include <linux/clk.h>
> > > +#include <linux/clk-provider.h>
> > > +#include <linux/io.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/bitfield.h>
> > > +
> > > +#include "clk-ma35d1.h"
> > > +
> > > +#define to_ma35d1_clk_pll(clk) \
> > > +	(container_of(clk, struct ma35d1_clk_pll, clk))
> > static inline
> 
> 
> I am sorry cannot get "static inline" refer to which one.
> 
> Would you give more advice here?
> 
> Thank you.

static inline struct ...type_here... *to_ma35d1_clk_pll(struct ...type_here... *clk)
{
	return container_of(clk, struct ma35d1_clk_pll, clk);
}


> > > +	} else {
> > > +		pr_err("Failed to set rate %ld\n", u64PllFreq);
> > > +		return 0;
> > > +	}
> > > +
> > > +	u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
> > > +	       ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
> > > +		((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));
> > Ditto.
> > 
> > Is here some ...ROUND_UP() trick hidden too?
> 
> 
> This follows the description of PLL spec.

Right but I was looking into what the math does. To me this looks like 
rounding up:
 VSIPLL_FCLK_MIN_FREQ / u64FCLKO + (VSIPLL_FCLK_MIN_FREQ % u64FCLKO ? 1 : 0)

When modulo is > 0, add one, which is round up, no?

There are helpers which you should use for rounding up, search for 
*_ROUND_UP. I think math64.h had one 64-bit one.

> > > +	u64X = u64tmp % 1000;
> > > +	u32FRAC = ((u64X << 24) + 500) / 1000;

I missed this earlier, is this rounding? ...Use a helper if it is. 
Otherwise define what 500 is. (No need to answer despite question mark, 
just do the change).

> > > +
> > > +	u64SSRATE = ((PllSrcClk >> 1) / (u32Fmod * 2)) - 1;
> > > +	u64SLOPE = ((u64tmp * u32SR / u64SSRATE) << 24) / 100 / 1000;
> > > +
> > > +	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
> > Is some *SEC_PER_*SEC define relevant for 1000 ?
> > 
> > Or some other units, e.g., HZ related?
> 
> 
> 1000 is for kHz to MHz, and 100 is for percentage.

Okay, then use KHZ_PER_MHZ from linux/units.h.

We don't have anything for percents under include/ I think so that can be 
left as literal.

> > > +	switch (pll->mode) {
> > > +	case VSIPLL_INTEGER_MODE:
> > > +		u64PllClk = CLK_CalPLLFreq_Mode0(PllSrcClk, u64PllFreq,
> > > +						 u32Reg);
> > One line.
> 
> 
> It will exceed 80 characters in one line.

Yeah, the semicolon won't fit to 80 chars :-) which means there won't be 
significant information loss even on 80 chars terminal. This kind of cases 
is why checkpatch won't complain until 100 chars. Use common sense (don't 
hide most of the logic to 80-100 but don't be afraid of breaking the 80 
chars where the information loss is not significant issue).

Besides, once you removed the types from variable names, it will be 
shorter anyway.


-- 
 i.

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

* Re: [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree
  2023-03-18 14:04       ` Arnd Bergmann
@ 2023-03-20 15:38         ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-20 15:38 UTC (permalink / raw)
  To: Arnd Bergmann, Rob Herring, krzysztof.kozlowski+dt, Lee Jones,
	Michael Turquette, Stephen Boyd, Philipp Zabel,
	Greg Kroah-Hartman, Jiri Slaby
  Cc: devicetree, linux-clk, linux-kernel, linux-serial, schung, Jacky Huang

Dear Arnd,


On 2023/3/18 下午 10:04, Arnd Bergmann wrote:
> On Sat, Mar 18, 2023, at 14:17, Jacky Huang wrote:
>> On 2023/3/16 下午 10:17, Arnd Bergmann wrote:
>>> On Wed, Mar 15, 2023, at 08:28, Jacky Huang wrote:
>>>> +	mem: memory@80000000 {
>>>> +		device_type = "memory";
>>>> +		reg = <0x00000000 0x80000000 0 0x20000000>; /* 512M DRAM */
>>>> +	};
>>>> +};
>>> In most machines, the memory size is detected by the boot loader
>>> and filled in the dtb in memory before starting the kernel, so
>>> you should not need two separate files here for the two common
>>> memory configurations.
>>
>> On ma35d1, memory size is determined early before uboot.
>>
>> BL1 (MaskROM boot code) -> BL2 (arm-trust-firmware) -> BL32 (op-tee) &
>> BL33 (uboot).
>> The DDR was initialized in BL2 stage with a selected DDR setting, which
>> is hard coded, including DDR size.
>>
>> We searched the arm64 dts and found that almost all vendors claimed
>> memory size in board level dtsi/dts. This seems to be common.
>>
>> So, can we have it unchanged?
> I see the memory size encoded in about one out of three .dts files,
> which is more than I expected. It's clearly not harmful to have it
> listed in the dts, it just shouldn't be necessary.
>
> If it helps you with your current u-boot, then leave it in, but
> consider adding detection logic into u-boot so it can override
> the value in the dtb file at boot time.


Thank you for your understanding. As more drivers are added, I think 
this memory

size encoded will look less conspicuous. In fact, in the previous arm9 
project, we

did detect the memory size by uboot, and then passed it to the kernel. 
If there is

a need in the future, we will consider to support it in ma35d1.

>>> Since the machine is called 'som', I would assume that this is a
>>> module that is integrated on another board, so more commonly one
>>> would have a dtsi file for the som in addition to the one for the
>>> soc, and have all the components of the module listed in this
>>> file, while the dts file that includes the som.dtsi lists the
>>> devices on the carrier board and enables the on-chip devices
>>> that are connected to the outside.
>>>
>> You are right, ma35d1 som have a base board, and a cpu board on it.
>>
>> It is a good suggestion that we should have a dtsi for som base board.
>>
>> Consider that we are in the initial submit, and such a dtsi will be an empty
>> file at this stage. So, I would like to do it when peripheral drivers
>> upstream started. Is it ok?
> It's not a big deal either way. I if you want to keep it only with
> one dts file and one dtsi file, that's fine, but maybe rename the dts
> file based on the name of the carrier rather than the SoM in this
> case.
>
>       Arnd


Thank you. As the dts names are consistent with the ma35d1 BSP on 
linux-5.10.y,

we would like to keep the consistence still.


Best regards,

Jacky Huang



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

* Re: [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-03-20 10:04       ` Ilpo Järvinen
@ 2023-03-21 14:23         ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-21 14:23 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	Greg Kroah-Hartman, Jiri Slaby, devicetree, linux-clk, LKML,
	linux-serial, schung, Jacky Huang, mjchen

Dear Ilpo,


On 2023/3/20 下午 06:04, Ilpo Järvinen wrote:
> On Mon, 20 Mar 2023, Jacky Huang wrote:
>
>> Dear Ilpo,
>>
>>
>> Thanks for your advice.
>>
>> On 2023/3/16 下午 10:54, Ilpo Järvinen wrote:
>>> Hi,
>>>
>>> I'll not note all things below because others have already seemingly
>>> commented many things.
>>>
>>> On Wed, 15 Mar 2023, Jacky Huang wrote:
>>>
>>>> From: Jacky Huang <ychuang3@nuvoton.com>
>>>>
>>>> This adds UART and console driver for Nuvoton ma35d1 Soc.
>>>> +		}
>>>> +		ch = (u8)serial_in(up, UART_REG_RBR);
>>> Drop the case.
>> I  will fix it.
> I meant "cast" in case it wasn't obvious.


I know that, thank you.


>>>> +/* Enable or disable the rs485 support */
>>>> +static int ma35d1serial_config_rs485(struct uart_port *port,
>>>> +				     struct ktermios *termios,
>>>> +				     struct serial_rs485 *rs485conf)
>>>> +{
>>>> +	struct uart_ma35d1_port *p = to_ma35d1_uart_port(port);
>>>> +
>>>> +	p->rs485 = *rs485conf;
>>>> +
>>>> +	if (p->rs485.delay_rts_before_send >= 1000)
>>>> +		p->rs485.delay_rts_before_send = 1000;
>>> Don't do this in driver, the core handles the delay limits. You don't seem
>>> to be using the value anyway for anything???
>>>
>>> Please separate the RS485 support into its own patch.
>>
>> OK, we will remove RS485 support from this initial patch.
>> Once this initial patch was merged, we will submit the patch for RS485
>> support.
> You could do that but you could just as well include it into the same
> series as another patch after the main patch.
>>>> +	serial_out(p, UART_FUN_SEL,
>>>> +		   (serial_in(p, UART_FUN_SEL) & ~FUN_SEL_MASK));
>>>> +
>>>> +	if (rs485conf->flags & SER_RS485_ENABLED) {
>>>> +		serial_out(p, UART_FUN_SEL,
>>>> +			   (serial_in(p, UART_FUN_SEL) | FUN_SEL_RS485));
>>> Does this pair of serial_out()s glitch the RS485 line if ->rs485_config()
>>> is called while RS485 mode is already set?
>>>
>>> Why you need to do serial_in() from the UART_FUN_SEL twice?
>> UART_FUN_SEL (2 bits) definition:
>> 00 - UART function
>> 01 - IrDA function
>> 11 - RS485 function
>>
>> The first searial_in() is used to clear set as UART function.
>> The second one is used to set RS485 function if SER_RS485_ENABLED is true.
> I got that, but it doesn't answer either of my questions which are:
>
> Can you clear the UART function without causing a glitch in the RS485?
> ->rs485_config() can be called while already in RS485 mode so does it
> cause the UART to temporarily switch away from RS485 mode to "UART
> function" until the second write.
>
> Also, you didn't explain why you need to read the register again, does
> the HW play with other bits when you do the clearing or to they remain
> the same (in which case you can just use a temporary variable to store
> the value)? ...It would be better to just write once too so this question
> might not matter in the end.


Thank you for the detailed explanation.

OK, the register won't change. I will modify the code to read once and 
write once only.


>>>> +	if (pdev->dev.of_node) {
>>>> +		ret = of_alias_get_id(pdev->dev.of_node, "serial");
>>>> +		if (ret < 0) {
>>>> +			dev_err(&pdev->dev,
>>>> +				"failed to get alias/pdev id, errno %d\n",
>>>> +				ret);
>>> Just put error prints to one line if you don't break 100 chars limit.
>> But the checkpatch limitation is 80 characters.
> No, it isn't. It was changed years ago already.


I have a test on the checkpatch script.

You are right. It won't complain about over 80 characters now.


>>>> +++ b/drivers/tty/serial/ma35d1_serial.h
>>>> @@ -0,0 +1,93 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + *  MA35D1 serial driver header file
>>>> + *  Copyright (C) 2023 Nuvoton Technology Corp.
>>>> + */
>>>> +#ifndef __MA35D1_SERIAL_H__
>>>> +#define __MA35D1_SERIAL_H__
>>>> +
>>>> +/* UART Receive/Transmit Buffer Register */
>>>> +#define UART_REG_RBR	0x00
>>>> +#define UART_REG_THR	0x00
>>>> +
>>>> +/* UART Interrupt Enable Register */
>>>> +#define UART_REG_IER	0x04
>>>> +#define RDA_IEN		0x00000001 /* RBR Available Interrupt Enable
>>>> */
>>>> +#define THRE_IEN	0x00000002 /* THR Empty Interrupt Enable */
>>>> +#define RLS_IEN		0x00000004 /* RX Line Status Interrupt Enable
>>>> */
>>>> +#define RTO_IEN		0x00000010 /* RX Time-out Interrupt Enable */
>>>> +#define BUFERR_IEN	0x00000020 /* Buffer Error Interrupt Enable */
>>>> +#define TIME_OUT_EN	0x00000800 /* RX Buffer Time-out Counter
>>>> Enable */
>>>> +
>>>> +/* UART FIFO Control Register */
>>>> +#define UART_REG_FCR	0x08
>>>> +#define RFR		0x00000002 /* RX Field Software Reset */
>>>> +#define TFR		0x00000004 /* TX Field Software Reset */
>>>> +
>>>> +/* UART Line Control Register */
>>>> +#define UART_REG_LCR	0x0C
>>>> +#define	NSB		0x00000004 /* Number of “STOP Bit” */
>>>> +#define PBE		0x00000008 /* Parity Bit Enable */
>>>> +#define EPE		0x00000010 /* Even Parity Enable */
>>>> +#define SPE		0x00000020 /* Stick Parity Enable */
>>>> +#define BCB		0x00000040 /* Break Control */
>>>> +
>>>> +/* UART Modem Control Register */
>>>> +#define UART_REG_MCR	0x10
>>>> +#define RTS		0x00000020 /* nRTS Signal Control */
>>>> +#define RTSACTLV	0x00000200 /* nRTS Pin Active Level */
>>>> +#define RTSSTS		0x00002000 /* nRTS Pin Status (Read Only) */
>>>> +
>>>> +/* UART Modem Status Register */
>>>> +#define UART_REG_MSR	0x14
>>>> +#define CTSDETF		0x00000001 /* Detect nCTS State Change Flag */
>>>> +#define CTSSTS		0x00000010 /* nCTS Pin Status (Read Only) */
>>>> +#define CTSACTLV	0x00000100 /* nCTS Pin Active Level */
>>>> +
>>>> +/* UART FIFO Status Register */
>>>> +#define UART_REG_FSR	0x18
>>>> +#define RX_OVER_IF	0x00000001 /* RX Overflow Error Interrupt Flag */
>>>> +#define PEF		0x00000010 /* Parity Error Flag*/
>>>> +#define FEF		0x00000020 /* Framing Error Flag */
>>>> +#define BIF		0x00000040 /* Break Interrupt Flag */
>>>> +#define RX_EMPTY	0x00004000 /* Receiver FIFO Empty (Read Only) */
>>>> +#define RX_FULL		0x00008000 /* Receiver FIFO Full (Read Only)
>>>> */
>>>> +#define TX_EMPTY	0x00400000 /* Transmitter FIFO Empty (Read Only) */
>>>> +#define TX_FULL		0x00800000 /* Transmitter FIFO Full (Read
>>>> Only) */
>>>> +#define TX_OVER_IF	0x01000000 /* TX Overflow Error Interrupt Flag */
>>>> +#define TE_FLAG		0x10000000 /* Transmitter Empty Flag (Read
>>>> Only) */
>>>> +
>>>> +/* UART Interrupt Status Register */
>>>> +#define UART_REG_ISR	0x1C
>>>> +#define RDA_IF		0x00000001 /* RBR Available Interrupt Flag */
>>>> +#define THRE_IF		0x00000002 /* THR Empty Interrupt Flag */
>>>> +#define RLSIF		0x00000004 /* Receive Line Interrupt Flag */
>>>> +#define MODEMIF		0x00000008 /* MODEM Interrupt Flag */
>>>> +#define RXTO_IF		0x00000010 /* RX Time-out Interrupt Flag */
>>>> +#define BUFEIF		0x00000020 /* Buffer Error Interrupt Flag */
>>>> +#define WK_IF		0x00000040 /* UART Wake-up Interrupt Flag */
>>>> +#define RDAINT		0x00000100 /* RBR Available Interrupt
>>>> Indicator */
>>>> +#define THRE_INT	0x00000200 /* THR Empty Interrupt Indicator */
> I forgot to mention earlier, there are many defines above which should use
> BIT().
>
Sure we will fix them all.


Best regards,

Jacky Huang



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

* Re: [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-03-20 10:31       ` Ilpo Järvinen
@ 2023-03-21 15:03         ` Jacky Huang
  0 siblings, 0 replies; 89+ messages in thread
From: Jacky Huang @ 2023-03-21 15:03 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	Greg Kroah-Hartman, Jiri Slaby, devicetree, linux-clk, LKML,
	linux-serial, schung, Jacky Huang

Dear Ilpo,


On 2023/3/20 下午 06:31, Ilpo Järvinen wrote:
> On Sun, 19 Mar 2023, Jacky Huang wrote:
>
>> On 2023/3/16 下午 11:56, Ilpo Järvinen wrote:
>>> On Wed, 15 Mar 2023, Jacky Huang wrote:
>>>
>>>> From: Jacky Huang <ychuang3@nuvoton.com>
>>>>
>>>> The clock controller generates clocks for the whole chip, including
>>>> system clocks and all peripheral clocks. This driver support ma35d1
>>>> clock gating, divider, and individual PLL configuration.
>>>>
>>>> There are 6 PLLs in ma35d1 SoC:
>>>>     - CA-PLL for the two Cortex-A35 CPU clock
>>>>     - SYS-PLL for system bus, which comes from the companion MCU
>>>>       and cannot be programmed by clock controller.
>>>>     - DDR-PLL for DDR
>>>>     - EPLL for GMAC and GFX, Display, and VDEC IPs.
>>>>     - VPLL for video output pixel clock
>>>>     - APLL for SDHC, I2S audio, and other IPs.
>>>> CA-PLL has only one operation mode.
>>>> DDR-PLL, EPLL, VPLL, and APLL are advanced PLLs which have 3
>>>> operation modes: integer mode, fraction mode, and spread specturm mode.
>>>>
>>>> Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
>>>> ---
>>>> +};
>>>> +
>>>> +#define to_ma35d1_adc_clk_divider(_hw)	\
>>>> +	container_of(_hw, struct ma35d1_adc_clk_divider, hw)
>>> static inline
>>
>> I will modify these "static" functions as "static inline".
> No, that's not what I meant. Make the container_of define static inline
> function instead, no other functions. (Or if you have more than one of
> such, all of them of course).
>
>>>> +}
>>>> diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c
>>>> b/drivers/clk/nuvoton/clk-ma35d1-pll.c
>>>> new file mode 100644
>>>> index 000000000000..79e724b148fa
>>>> --- /dev/null
>>>> +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
>>>> @@ -0,0 +1,534 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>> +/*
>>>> + * Copyright (C) 2023 Nuvoton Technology Corp.
>>>> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/clk-provider.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/bitfield.h>
>>>> +
>>>> +#include "clk-ma35d1.h"
>>>> +
>>>> +#define to_ma35d1_clk_pll(clk) \
>>>> +	(container_of(clk, struct ma35d1_clk_pll, clk))
>>> static inline
>>
>> I am sorry cannot get "static inline" refer to which one.
>>
>> Would you give more advice here?
>>
>> Thank you.
> static inline struct ...type_here... *to_ma35d1_clk_pll(struct ...type_here... *clk)
> {
> 	return container_of(clk, struct ma35d1_clk_pll, clk);
> }
>

OK, I got it. Thank you very much.


>>>> +	} else {
>>>> +		pr_err("Failed to set rate %ld\n", u64PllFreq);
>>>> +		return 0;
>>>> +	}
>>>> +
>>>> +	u64P = (u64FCLKO >= VSIPLL_FCLK_MIN_FREQ) ? 1 :
>>>> +	       ((VSIPLL_FCLK_MIN_FREQ / u64FCLKO) +
>>>> +		((VSIPLL_FCLK_MIN_FREQ % u64FCLKO) ? 1 : 0));
>>> Ditto.
>>>
>>> Is here some ...ROUND_UP() trick hidden too?
>>
>> This follows the description of PLL spec.
> Right but I was looking into what the math does. To me this looks like
> rounding up:
>   VSIPLL_FCLK_MIN_FREQ / u64FCLKO + (VSIPLL_FCLK_MIN_FREQ % u64FCLKO ? 1 : 0)
>
> When modulo is > 0, add one, which is round up, no?
>
> There are helpers which you should use for rounding up, search for
> *_ROUND_UP. I think math64.h had one 64-bit one.


Yes, it is a round up. We will find out all the occurrence and use 
ROUND_UP() macro instead.


>>>> +	u64X = u64tmp % 1000;
>>>> +	u32FRAC = ((u64X << 24) + 500) / 1000;
> I missed this earlier, is this rounding? ...Use a helper if it is.
> Otherwise define what 500 is. (No need to answer despite question mark,
> just do the change).
>
>>>> +
>>>> +	u64SSRATE = ((PllSrcClk >> 1) / (u32Fmod * 2)) - 1;
>>>> +	u64SLOPE = ((u64tmp * u32SR / u64SSRATE) << 24) / 100 / 1000;
>>>> +
>>>> +	u64PllClk = (PllSrcClk * u64tmp) / u64P / u64M / 1000;
>>> Is some *SEC_PER_*SEC define relevant for 1000 ?
>>>
>>> Or some other units, e.g., HZ related?
>>
>> 1000 is for kHz to MHz, and 100 is for percentage.
> Okay, then use KHZ_PER_MHZ from linux/units.h.
>
> We don't have anything for percents under include/ I think so that can be
> left as literal.


Sure, we are rewriting the pll calculation routine and add formula 
comments to make it more readable.


>>>> +	switch (pll->mode) {
>>>> +	case VSIPLL_INTEGER_MODE:
>>>> +		u64PllClk = CLK_CalPLLFreq_Mode0(PllSrcClk, u64PllFreq,
>>>> +						 u32Reg);
>>> One line.
>>
>> It will exceed 80 characters in one line.
> Yeah, the semicolon won't fit to 80 chars :-) which means there won't be
> significant information loss even on 80 chars terminal. This kind of cases
> is why checkpatch won't complain until 100 chars. Use common sense (don't
> hide most of the logic to 80-100 but don't be afraid of breaking the 80
> chars where the information loss is not significant issue).
>
> Besides, once you removed the types from variable names, it will be
> shorter anyway.
>
Got it. Thanks for your kind help and detailed explanation.


Best regards,

Jacky Huang



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

end of thread, other threads:[~2023-03-21 15:03 UTC | newest]

Thread overview: 89+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-15  7:28 [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Jacky Huang
2023-03-15  7:28 ` [PATCH 01/15] arm64: Kconfig.platforms: Add config for Nuvoton MA35 platform Jacky Huang
2023-03-15  7:28 ` [PATCH 02/15] arm64: defconfig: Add Nuvoton MA35 family support Jacky Huang
2023-03-16 14:23   ` Arnd Bergmann
2023-03-17  9:05     ` Jacky Huang
2023-03-15  7:28 ` [PATCH 03/15] mfd: Add the header file of Nuvoton ma35d1 system manager Jacky Huang
2023-03-16 13:30   ` Ilpo Järvinen
2023-03-17  6:51     ` Jacky Huang
2023-03-16 14:44   ` Arnd Bergmann
2023-03-17  9:28     ` Jacky Huang
2023-03-15  7:28 ` [PATCH 04/15] dt-bindings: clock: nuvoton: add binding for ma35d1 clock controller Jacky Huang
2023-03-16  7:31   ` Krzysztof Kozlowski
2023-03-16 13:35     ` Jacky Huang
2023-03-16 14:09       ` Krzysztof Kozlowski
2023-03-15  7:28 ` [PATCH 05/15] dt-bindings: reset: nuvoton: add binding for ma35d1 IP reset control Jacky Huang
2023-03-16  7:31   ` Krzysztof Kozlowski
2023-03-15  7:28 ` [PATCH 06/15] dt-bindings: mfd: syscon: Add nuvoton,ma35d1-sys compatible Jacky Huang
2023-03-16  7:31   ` Krzysztof Kozlowski
2023-03-17  1:03     ` Jacky Huang
2023-03-15  7:28 ` [PATCH 07/15] dt-bindings: arm: Add initial bindings for Nuvoton platform Jacky Huang
2023-03-16  7:33   ` Krzysztof Kozlowski
2023-03-16 14:32     ` Arnd Bergmann
2023-03-18  1:26       ` Jacky Huang
2023-03-15  7:28 ` [PATCH 08/15] dt-bindings: clock: Document ma35d1 clock controller bindings Jacky Huang
2023-03-15 21:59   ` Stephen Boyd
2023-03-16  3:24     ` Jacky Huang
2023-03-16  7:35   ` Krzysztof Kozlowski
2023-03-17  3:47     ` Jacky Huang
2023-03-17  9:13       ` Krzysztof Kozlowski
2023-03-17  9:52         ` Jacky Huang
2023-03-17 16:03           ` Krzysztof Kozlowski
2023-03-18  2:11             ` Jacky Huang
2023-03-15  7:28 ` [PATCH 09/15] dt-bindings: reset: Document ma35d1 reset " Jacky Huang
2023-03-16  7:37   ` Krzysztof Kozlowski
2023-03-16  7:39     ` Krzysztof Kozlowski
2023-03-18  4:30       ` Jacky Huang
2023-03-19 11:05         ` Krzysztof Kozlowski
2023-03-20  6:26           ` Jacky Huang
2023-03-15  7:28 ` [PATCH 10/15] dt-bindings: serial: Document ma35d1 uart " Jacky Huang
2023-03-16  7:40   ` Krzysztof Kozlowski
2023-03-17  4:18     ` Jacky Huang
2023-03-15  7:28 ` [PATCH 11/15] arm64: dts: nuvoton: Add initial ma35d1 device tree Jacky Huang
2023-03-16  7:45   ` Krzysztof Kozlowski
2023-03-18  6:07     ` Jacky Huang
2023-03-19 11:06       ` Krzysztof Kozlowski
2023-03-19 14:16         ` Jacky Huang
2023-03-16 14:17   ` Arnd Bergmann
2023-03-16 16:44     ` Lee Jones
2023-03-18 13:32       ` Jacky Huang
2023-03-18 13:17     ` Jacky Huang
2023-03-18 14:04       ` Arnd Bergmann
2023-03-20 15:38         ` Jacky Huang
2023-03-15  7:28 ` [PATCH 12/15] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
2023-03-15 22:07   ` kernel test robot
2023-03-15 22:30   ` Stephen Boyd
2023-03-17  3:07     ` Jacky Huang
2023-03-16  7:51   ` Krzysztof Kozlowski
2023-03-19  2:55     ` Jacky Huang
2023-03-16 15:56   ` Ilpo Järvinen
2023-03-19  5:16     ` Jacky Huang
2023-03-20 10:31       ` Ilpo Järvinen
2023-03-21 15:03         ` Jacky Huang
2023-03-15  7:29 ` [PATCH 13/15] reset: Add Nuvoton ma35d1 reset driver support Jacky Huang
2023-03-16  7:51   ` Krzysztof Kozlowski
2023-03-17  7:13     ` Jacky Huang
2023-03-16 15:05   ` Ilpo Järvinen
2023-03-19 13:10     ` Jacky Huang
2023-03-15  7:29 ` [PATCH 14/15] tty: serial: Add Nuvoton ma35d1 serial " Jacky Huang
2023-03-15  7:37   ` Greg KH
2023-03-15  9:40     ` Jacky Huang
2023-03-15  9:48   ` kernel test robot
2023-03-15 10:13   ` Jiri Slaby
2023-03-16 13:28     ` Jacky Huang
2023-03-16 14:54   ` Ilpo Järvinen
2023-03-20  8:23     ` Jacky Huang
2023-03-20 10:04       ` Ilpo Järvinen
2023-03-21 14:23         ` Jacky Huang
2023-03-15  7:29 ` [PATCH 15/15] MAINTAINERS: Add entry for NUVOTON MA35 Jacky Huang
2023-03-16 14:38   ` Arnd Bergmann
2023-03-19 12:01     ` Jacky Huang
2023-03-19 12:36       ` Tomer Maimon
2023-03-16  7:41 ` [PATCH 00/15] Introduce Nuvoton ma35d1 SoC Krzysztof Kozlowski
2023-03-16 14:05 ` Arnd Bergmann
2023-03-17  6:30   ` Jacky Huang
2023-03-17 13:21     ` Arnd Bergmann
2023-03-17 16:06       ` Krzysztof Kozlowski
2023-03-18  3:07         ` Jacky Huang
2023-03-18  9:07           ` Arnd Bergmann
2023-03-18  3:00       ` Jacky Huang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).