soc.lore.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v15 0/2] Introduce Nuvoton ma35d1 SoC
@ 2023-06-19  3:23 Jacky Huang
  2023-06-19  3:23 ` [PATCH v15 1/2] tty: serial: Add Nuvoton ma35d1 serial driver support Jacky Huang
  2023-06-19  3:23 ` [PATCH 2/2] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
  0 siblings, 2 replies; 4+ messages in thread
From: Jacky Huang @ 2023-06-19  3:23 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, tmaimon77, catalin.marinas, will
  Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
	linux-serial, arnd, soc, schung, mjchen, 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.4.0-rc6
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

v15:
  - Modified the serial driver
    - Instead of adding a new port type PORT_MA35 to serial_core.h,
      now using port->type = 1.
    - Updated Kconfig descriptions.
    - Added a description about the name ttyNVT to the patch log.
  - Modified the clock driver
    - Added clk-ma35d1.h for external functions.
    - Updated all parent strings as clk_parent_data.

v14:
  - Removed patches PATCH v13 1/10 ~ 9/10 as they were applied
  - Modify serial driver
    - Fixed coding style issues

v13:
  - Modify serial driver
    - Added a check for oops_in_progress in ma35d1serial_console_write to
      determine whether to perform the spin_lock.
    - Rebased drivers/tty/serial/Kconfig and recreate the patch
    - Rebased MAINTAINERS and recreate the patch

v12:
  - Modify serial driver
    - Added PORT_MA35 to include/uapi/linux/serial_core.h, and apply to
      the port->type of ma35d1 serial driver
    - Added check for the return value of ioremap()
    - Fixed several coding issues
  - Rebase MAINTAINERS and recreate the patch

v11:
  - Rebase on top of 2023.05.24
  - Modify serial driver
    - Fixed several coding style issues
    - Fixed ma35d1serial_set_mctrl()
    - Added the 'MA35_' prefix to all register and bit field definitions.
    - Used 'ttyNVT' instead of 'ttyS'
  - Modify clock driver
    - Added 'source nuvoton/Kconfig' to drivers/clk/Kconfig
    - Fixed several coding issues
    - Removed unnecessary inline specifier
  - Modify reset driver
    - Fixed typo and added comments
  - Modify ma35d1.dtsi l2-cache node
    - Added cache-unified and cache-size properties

v10:
  - Change from using ARCH_NUVOTON to using ARCH_MA35. The following patch files
    have been modified:
    - patch 1 arch/arm64/Kconfig.platforms
    - patch 2 arch/arm64/configs/defconfig
    - patch 7 arch/arm64/boot/dts/nuvoton/Makefile
    - patch 8 drivers/clk/Makefile
              drivers/clk/nuvoton/Kconfig
              drivers/clk/nuvoton/Makefile
    - patch 9 drivers/reset/Kconfig
    - patch 10 drivers/tty/serial/Kconfig

v9:
  - Combine MAINTAINERS patch into patch 5 'dt-bindings: arm: Add initial bindings
    for Nuvoton platform'
  - Modify clock driver
    - Use the helper function for 64-bit division
    - Fixed minor issues
  - Modify reset driver
    - Refine coding style and add required header files
    - Add spin_lock protection
  - Add error return handling to the serial driver probe function

v8:
  - Remove '0005-dt-bindings-mfd-syscon-Add-nuvoton-ma35d1-sys-compat.patch' as it was applied.
  - Modify MAINTAINERS NUVOTON MA35 and NPCM path settings
  - Remove 'syscon' from dtsi 'sys' node and modify the corresponding yaml
  - Modify clock driver
    - Remove the header file and move definitions into .c files.
    - Use parent_data instead of parent name.
  - Modify serial driver
  - Modify reset driver
    - Modify reset register/offset lookup table to be indexed by reset id
    - Combined reset and reboot structure

v7:
  - Fixed dts system-management node and compatible driver
  - move 'nuvoton,npcm-gcr.yaml' from 'binding/arm/nuvoton' to 'binding/soc/nuvoton'
  - In ma35d1.dtsi, create the soc node for ma35d1 SoC
  - Modify the issues found in serial driver
  - Modify the issues found in clock driver
  - Modify the IDs of reset driver to be contiguous numbers and use lookup table
    to find register offset and bit position.
  - Modify MAINTAINERS NUVOTON NPCM path as npcm directory name to nuvoton

v6:
  - Combine nuvoton,ma35d1-clk.yaml and nuvoton,ma35d1-clk.h into one patch
  - Combine nuvoton,ma35d1-reset.yaml and nuvoton,ma35d1-reset.h into one patch
  - rename Documentation/devicetree/bindings/arm/npcm directory as nuvoton
  - Remove patch for adding include/linux/mfd/ma35d1-sys.h as it's not required
  - Update dtsi & dts files and move board-specific nodes to dts
  - Modify reset driver
  - Modify serial driver, fix coding style issues
  - Modify clock driver, rewrite the PLL calculation functions

v5:
  - Add ARCH_NUVOTON to arm64 Kconfig
  - Add ARCH_NUVOTON to defconfig
  - Add the clock driver
  - Add the reset driver
  - Add the serial driver
  - Add us to the maintainer

v4:
  - patch 4/5 is a resend
  - Fixed dt_binding_check errors of nuvoton,ma35d1-clk.yaml
  - Modify ma35d1.dtsi
    1. Add a node hxt_24m
    2. Fixed the base address of gic node
    3. Add clocks and clock-names to clock node
  - Fixed borad binding mistakes of nuvoton.yaml

v3:
  - added patch 4/5 and 5/5
  - introduce CONFIG_ARCH_NUVOTON option
  - add initial bindings for Nuvoton Platform boards
  - fixed coding style problem of nuvoton,ma35d1-clk.h
  - added CAPLL to clock-controller node
  - modify the chosen node of ma35d1-evb.dts
  - modify clock yaml "clk-pll-mode" to "nuvoton,clk-pll-mode"

v2:
  - fixed dt_binding_check failed of nuvoton,ma35d1-clk.yaml

Jacky Huang (2):
  tty: serial: Add Nuvoton ma35d1 serial driver support
  clk: nuvoton: Add clock driver for ma35d1 clock controller

 drivers/clk/Kconfig                      |    1 +
 drivers/clk/Makefile                     |    1 +
 drivers/clk/nuvoton/Kconfig              |   19 +
 drivers/clk/nuvoton/Makefile             |    4 +
 drivers/clk/nuvoton/clk-ma35d1-divider.c |  132 +++
 drivers/clk/nuvoton/clk-ma35d1-pll.c     |  360 ++++++++
 drivers/clk/nuvoton/clk-ma35d1.c         | 1046 ++++++++++++++++++++++
 drivers/clk/nuvoton/clk-ma35d1.h         |   18 +
 drivers/tty/serial/Kconfig               |   23 +
 drivers/tty/serial/Makefile              |    1 +
 drivers/tty/serial/ma35d1_serial.c       |  821 +++++++++++++++++
 11 files changed, 2426 insertions(+)
 create mode 100644 drivers/clk/nuvoton/Kconfig
 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/tty/serial/ma35d1_serial.c

-- 
2.34.1


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

* [PATCH v15 1/2] tty: serial: Add Nuvoton ma35d1 serial driver support
  2023-06-19  3:23 [PATCH v15 0/2] Introduce Nuvoton ma35d1 SoC Jacky Huang
@ 2023-06-19  3:23 ` Jacky Huang
  2023-06-19  3:23 ` [PATCH 2/2] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
  1 sibling, 0 replies; 4+ messages in thread
From: Jacky Huang @ 2023-06-19  3:23 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, tmaimon77, catalin.marinas, will
  Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
	linux-serial, arnd, soc, schung, mjchen, Jacky Huang,
	Ilpo Järvinen

From: Jacky Huang <ychuang3@nuvoton.com>

This adds UART and console driver for Nuvoton ma35d1 Soc.
It supports full-duplex communication, FIFO control, and
hardware flow control.
Command line set "console=ttyNVT0,115200", NVT means
Nuvoton MA35 UART port. The UART driver probe will
create path named "/dev/ttyNVTx".

Signed-off-by: Jacky Huang <ychuang3@nuvoton.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
 drivers/tty/serial/Kconfig         |  23 +
 drivers/tty/serial/Makefile        |   1 +
 drivers/tty/serial/ma35d1_serial.c | 821 +++++++++++++++++++++++++++++
 3 files changed, 845 insertions(+)
 create mode 100644 drivers/tty/serial/ma35d1_serial.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 3e3fb377d90d..444a31ff18b5 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1555,6 +1555,29 @@ 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_MA35 || 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. If you enable this option, Ma35D1 serial ports in the system
+	  will be registered as ttyNVTx.
+
+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.
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default,
+	  but you can alter that using a kernel command line option such as
+	  "console=ttyNVTx".
+
 endmenu
 
 config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cd9afd9e3018..0e823851c42c 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..2604b4d9fb78
--- /dev/null
+++ b/drivers/tty/serial/ma35d1_serial.c
@@ -0,0 +1,821 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  MA35D1 serial driver
+ *  Copyright (C) 2023 Nuvoton Technology Corp.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/iopoll.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+#include <linux/units.h>
+
+#define MA35_UART_NR		17
+
+#define MA35_RBR_REG		0x00
+#define MA35_THR_REG		0x00
+#define MA35_IER_REG		0x04
+#define MA35_FCR_REG		0x08
+#define MA35_LCR_REG		0x0C
+#define MA35_MCR_REG		0x10
+#define MA35_MSR_REG		0x14
+#define MA35_FSR_REG		0x18
+#define MA35_ISR_REG		0x1C
+#define MA35_TOR_REG		0x20
+#define MA35_BAUD_REG		0x24
+#define MA35_ALTCTL_REG		0x2C
+#define MA35_FUN_SEL_REG	0x30
+#define MA35_WKCTL_REG		0x40
+#define MA35_WKSTS_REG		0x44
+
+/* MA35_IER_REG - Interrupt Enable Register */
+#define MA35_IER_RDA_IEN	BIT(0)  /* RBR Available Interrupt Enable */
+#define MA35_IER_THRE_IEN	BIT(1)  /* THR Empty Interrupt Enable */
+#define MA35_IER_RLS_IEN	BIT(2)  /* RX Line Status Interrupt Enable */
+#define MA35_IER_RTO_IEN	BIT(4)  /* RX Time-out Interrupt Enable */
+#define MA35_IER_BUFERR_IEN	BIT(5)  /* Buffer Error Interrupt Enable */
+#define MA35_IER_TIME_OUT_EN	BIT(11) /* RX Buffer Time-out Counter Enable */
+#define MA35_IER_AUTO_RTS	BIT(12) /* nRTS Auto-flow Control Enable */
+#define MA35_IER_AUTO_CTS	BIT(13) /* nCTS Auto-flow Control Enable */
+
+/* MA35_FCR_REG - FIFO Control Register */
+#define MA35_FCR_RFR		BIT(1)  /* RX Field Software Reset */
+#define MA35_FCR_TFR		BIT(2)  /* TX Field Software Reset */
+#define MA35_FCR_RFITL_MASK	GENMASK(7, 4) /* RX FIFO Interrupt Trigger Level */
+#define MA35_FCR_RFITL_1BYTE	FIELD_PREP(MA35_FCR_RFITL_MASK, 0)
+#define MA35_FCR_RFITL_4BYTES	FIELD_PREP(MA35_FCR_RFITL_MASK, 1)
+#define MA35_FCR_RFITL_8BYTES	FIELD_PREP(MA35_FCR_RFITL_MASK, 2)
+#define MA35_FCR_RFITL_14BYTES	FIELD_PREP(MA35_FCR_RFITL_MASK, 3)
+#define MA35_FCR_RFITL_30BYTES	FIELD_PREP(MA35_FCR_RFITL_MASK, 4)
+#define MA35_FCR_RTSTL_MASK	GENMASK(19, 16) /* nRTS Trigger Level */
+#define MA35_FCR_RTSTL_1BYTE	FIELD_PREP(MA35_FCR_RTSTL_MASK, 0)
+#define MA35_FCR_RTSTL_4BYTES	FIELD_PREP(MA35_FCR_RTSTL_MASK, 1)
+#define MA35_FCR_RTSTL_8BYTES	FIELD_PREP(MA35_FCR_RTSTL_MASK, 2)
+#define MA35_FCR_RTSTL_14BYTES	FIELD_PREP(MA35_FCR_RTSTL_MASK, 3)
+#define MA35_FCR_RTSTLL_30BYTES	FIELD_PREP(MA35_FCR_RTSTL_MASK, 4)
+
+/* MA35_LCR_REG - Line Control Register */
+#define	MA35_LCR_NSB		BIT(2)  /* Number of “STOP Bit” */
+#define MA35_LCR_PBE		BIT(3)  /* Parity Bit Enable */
+#define MA35_LCR_EPE		BIT(4)  /* Even Parity Enable */
+#define MA35_LCR_SPE		BIT(5)  /* Stick Parity Enable */
+#define MA35_LCR_BREAK		BIT(6)  /* Break Control */
+#define MA35_LCR_WLS_MASK	GENMASK(1, 0) /* Word Length Selection */
+#define MA35_LCR_WLS_5BITS	FIELD_PREP(MA35_LCR_WLS_MASK, 0)
+#define MA35_LCR_WLS_6BITS	FIELD_PREP(MA35_LCR_WLS_MASK, 1)
+#define MA35_LCR_WLS_7BITS	FIELD_PREP(MA35_LCR_WLS_MASK, 2)
+#define MA35_LCR_WLS_8BITS	FIELD_PREP(MA35_LCR_WLS_MASK, 3)
+
+/* MA35_MCR_REG - Modem Control Register */
+#define MA35_MCR_RTS_CTRL	BIT(1)  /* nRTS Signal Control */
+#define MA35_MCR_RTSACTLV	BIT(9)  /* nRTS Pin Active Level */
+#define MA35_MCR_RTSSTS		BIT(13) /* nRTS Pin Status (Read Only) */
+
+/* MA35_MSR_REG - Modem Status Register */
+#define MA35_MSR_CTSDETF	BIT(0)  /* Detect nCTS State Change Flag */
+#define MA35_MSR_CTSSTS		BIT(4)  /* nCTS Pin Status (Read Only) */
+#define MA35_MSR_CTSACTLV	BIT(8)  /* nCTS Pin Active Level */
+
+/* MA35_FSR_REG - FIFO Status Register */
+#define MA35_FSR_RX_OVER_IF	BIT(0)  /* RX Overflow Error Interrupt Flag */
+#define MA35_FSR_PEF		BIT(4)  /* Parity Error Flag*/
+#define MA35_FSR_FEF		BIT(5)  /* Framing Error Flag */
+#define MA35_FSR_BIF		BIT(6)  /* Break Interrupt Flag */
+#define MA35_FSR_RX_EMPTY	BIT(14) /* Receiver FIFO Empty (Read Only) */
+#define MA35_FSR_RX_FULL	BIT(15) /* Receiver FIFO Full (Read Only) */
+#define MA35_FSR_TX_EMPTY	BIT(22) /* Transmitter FIFO Empty (Read Only) */
+#define MA35_FSR_TX_FULL	BIT(23) /* Transmitter FIFO Full (Read Only) */
+#define MA35_FSR_TX_OVER_IF	BIT(24) /* TX Overflow Error Interrupt Flag */
+#define MA35_FSR_TE_FLAG	BIT(28) /* Transmitter Empty Flag (Read Only) */
+#define MA35_FSR_RXPTR_MSK	GENMASK(13, 8) /* TX FIFO Pointer mask */
+#define MA35_FSR_TXPTR_MSK	GENMASK(21, 16) /* RX FIFO Pointer mask */
+
+/* MA35_ISR_REG - Interrupt Status Register */
+#define MA35_ISR_RDA_IF		BIT(0)  /* RBR Available Interrupt Flag */
+#define MA35_ISR_THRE_IF	BIT(1)  /* THR Empty Interrupt Flag */
+#define MA35_ISR_RLSIF		BIT(2)  /* Receive Line Interrupt Flag */
+#define MA35_ISR_MODEMIF	BIT(3)  /* MODEM Interrupt Flag */
+#define MA35_ISR_RXTO_IF	BIT(4)  /* RX Time-out Interrupt Flag */
+#define MA35_ISR_BUFEIF		BIT(5)  /* Buffer Error Interrupt Flag */
+#define MA35_ISR_WK_IF		BIT(6)  /* UART Wake-up Interrupt Flag */
+#define MA35_ISR_RDAINT		BIT(8)  /* RBR Available Interrupt Indicator */
+#define MA35_ISR_THRE_INT	BIT(9)  /* THR Empty Interrupt Indicator */
+#define MA35_ISR_ALL		0xFFFFFFFF
+
+/* MA35_BAUD_REG - Baud Rate Divider Register */
+#define	MA35_BAUD_MODE_MASK	GENMASK(29, 28)
+#define MA35_BAUD_MODE0		FIELD_PREP(MA35_BAUD_MODE_MASK, 0)
+#define MA35_BAUD_MODE1		FIELD_PREP(MA35_BAUD_MODE_MASK, 2)
+#define MA35_BAUD_MODE2		FIELD_PREP(MA35_BAUD_MODE_MASK, 3)
+#define	MA35_BAUD_MASK		GENMASK(15, 0)
+
+/* MA35_ALTCTL_REG - Alternate Control/Status Register */
+#define MA35_ALTCTL_RS485AUD	BIT(10) /* RS-485 Auto Direction Function */
+
+/* MA35_FUN_SEL_REG - Function Select Register */
+#define MA35_FUN_SEL_MASK	GENMASK(2, 0)
+#define MA35_FUN_SEL_UART	FIELD_PREP(MA35_FUN_SEL_MASK, 0)
+#define MA35_FUN_SEL_RS485	FIELD_PREP(MA35_FUN_SEL_MASK, 3)
+
+/* The constrain for MA35D1 UART baud rate divider */
+#define MA35_BAUD_DIV_MAX	0xFFFF
+#define MA35_BAUD_DIV_MIN	11
+
+/* UART FIFO depth */
+#define MA35_UART_FIFO_DEPTH	32
+/* UART console clock */
+#define MA35_UART_CONSOLE_CLK	(24 * HZ_PER_MHZ)
+/* UART register ioremap size */
+#define MA35_UART_REG_SIZE	0x100
+/* Rx Timeout */
+#define MA35_UART_RX_TOUT	0x40
+
+#define MA35_IER_CONFIG		(MA35_IER_RTO_IEN | MA35_IER_RDA_IEN | \
+				 MA35_IER_TIME_OUT_EN | MA35_IER_BUFERR_IEN)
+
+#define MA35_ISR_IF_CHECK	(MA35_ISR_RDA_IF | MA35_ISR_RXTO_IF | \
+				 MA35_ISR_THRE_INT | MA35_ISR_BUFEIF)
+
+#define MA35_FSR_TX_BOTH_EMPTY	(MA35_FSR_TE_FLAG | MA35_FSR_TX_EMPTY)
+
+static struct uart_driver ma35d1serial_reg;
+
+struct uart_ma35d1_port {
+	struct uart_port port;
+	struct clk *clk;
+	u16 capabilities; /* port capabilities */
+	u8 ier;
+	u8 lcr;
+	u8 mcr;
+	u32 baud_rate;
+	u32 console_baud_rate;
+	u32 console_line;
+	u32 console_int;
+};
+
+static struct uart_ma35d1_port ma35d1serial_ports[MA35_UART_NR];
+
+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, u32 offset)
+{
+	return readl_relaxed(p->port.membase + offset);
+}
+
+static void serial_out(struct uart_ma35d1_port *p, u32 offset, u32 value)
+{
+	writel_relaxed(value, p->port.membase + offset);
+}
+
+static void __stop_tx(struct uart_ma35d1_port *p)
+{
+	u32 ier;
+
+	ier = serial_in(p, MA35_IER_REG);
+	if (ier & MA35_IER_THRE_IEN)
+		serial_out(p, MA35_IER_REG, ier & ~MA35_IER_THRE_IEN);
+}
+
+static void ma35d1serial_stop_tx(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+
+	__stop_tx(up);
+}
+
+static void transmit_chars(struct uart_ma35d1_port *up)
+{
+	u32 count;
+	u8 ch;
+
+	if (uart_tx_stopped(&up->port)) {
+		ma35d1serial_stop_tx(&up->port);
+		return;
+	}
+	count = MA35_UART_FIFO_DEPTH - FIELD_GET(MA35_FSR_TXPTR_MSK,
+						 serial_in(up, MA35_FSR_REG));
+	uart_port_tx_limited(&up->port, ch, count,
+			     !(serial_in(up, MA35_FSR_REG) & MA35_FSR_TX_FULL),
+			     serial_out(up, MA35_THR_REG, ch),
+			     ({}));
+}
+
+static void ma35d1serial_start_tx(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	u32 ier;
+
+	ier = serial_in(up, MA35_IER_REG);
+	serial_out(up, MA35_IER_REG, ier & ~MA35_IER_THRE_IEN);
+	transmit_chars(up);
+	serial_out(up, MA35_IER_REG, ier | MA35_IER_THRE_IEN);
+}
+
+static void ma35d1serial_stop_rx(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	u32 ier;
+
+	ier = serial_in(up, MA35_IER_REG);
+	ier &= ~MA35_IER_RDA_IEN;
+	serial_out(up, MA35_IER_REG, ier);
+}
+
+static void receive_chars(struct uart_ma35d1_port *up)
+{
+	int max_count = 256;
+	u8 ch, flag;
+	u32 fsr;
+
+	fsr = serial_in(up, MA35_FSR_REG);
+	do {
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(fsr & (MA35_FSR_BIF | MA35_FSR_FEF |
+				    MA35_FSR_PEF | MA35_FSR_RX_OVER_IF))) {
+			if (fsr & MA35_FSR_BIF) {
+				up->port.icount.brk++;
+				if (uart_handle_break(&up->port))
+					continue;
+			}
+			if (fsr & MA35_FSR_FEF)
+				up->port.icount.frame++;
+			if (fsr & MA35_FSR_PEF)
+				up->port.icount.parity++;
+			if (fsr & MA35_FSR_RX_OVER_IF)
+				up->port.icount.overrun++;
+
+			serial_out(up, MA35_FSR_REG,
+				   fsr & (MA35_FSR_BIF | MA35_FSR_FEF |
+					  MA35_FSR_PEF | MA35_FSR_RX_OVER_IF));
+			if (fsr & MA35_FSR_BIF)
+				flag = TTY_BREAK;
+			else if (fsr & MA35_FSR_PEF)
+				flag = TTY_PARITY;
+			else if (fsr & MA35_FSR_FEF)
+				flag = TTY_FRAME;
+		}
+
+		ch = serial_in(up, MA35_RBR_REG);
+		if (uart_handle_sysrq_char(&up->port, ch))
+			continue;
+
+		spin_lock(&up->port.lock);
+		uart_insert_char(&up->port, fsr, MA35_FSR_RX_OVER_IF, ch, flag);
+		spin_unlock(&up->port.lock);
+
+		fsr = serial_in(up, MA35_FSR_REG);
+	} while (!(fsr & MA35_FSR_RX_EMPTY) && (max_count-- > 0));
+
+	spin_lock(&up->port.lock);
+	tty_flip_buffer_push(&up->port.state->port);
+	spin_unlock(&up->port.lock);
+}
+
+static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	u32 isr, fsr;
+
+	isr = serial_in(up, MA35_ISR_REG);
+	fsr = serial_in(up, MA35_FSR_REG);
+
+	if (!(isr & MA35_ISR_IF_CHECK))
+		return IRQ_NONE;
+
+	if (isr & (MA35_ISR_RDA_IF | MA35_ISR_RXTO_IF))
+		receive_chars(up);
+	if (isr & MA35_ISR_THRE_INT)
+		transmit_chars(up);
+	if (fsr & MA35_FSR_TX_OVER_IF)
+		serial_out(up, MA35_FSR_REG, MA35_FSR_TX_OVER_IF);
+
+	return IRQ_HANDLED;
+}
+
+static u32 ma35d1serial_tx_empty(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	u32 fsr;
+
+	fsr = serial_in(up, MA35_FSR_REG);
+	if ((fsr & MA35_FSR_TX_BOTH_EMPTY) == MA35_FSR_TX_BOTH_EMPTY)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static u32 ma35d1serial_get_mctrl(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	u32 status;
+	u32 ret = 0;
+
+	status = serial_in(up, MA35_MSR_REG);
+	if (!(status & MA35_MSR_CTSSTS))
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+static void ma35d1serial_set_mctrl(struct uart_port *port, u32 mctrl)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	u32 mcr, msr, ier;
+
+	mcr = serial_in(up, MA35_MCR_REG);
+	mcr &= ~MA35_MCR_RTS_CTRL;
+
+	if (mctrl & TIOCM_RTS)
+		mcr |= MA35_MCR_RTSACTLV;
+	else
+		mcr &= ~MA35_MCR_RTSACTLV;
+
+	if (up->mcr & UART_MCR_AFE) {
+		ier = serial_in(up, MA35_IER_REG);
+		ier |= MA35_IER_AUTO_RTS | MA35_IER_AUTO_CTS;
+		serial_out(up, MA35_IER_REG, ier);
+		up->port.flags |= UPF_HARD_FLOW;
+	} else {
+		ier = serial_in(up, MA35_IER_REG);
+		ier &= ~(MA35_IER_AUTO_RTS | MA35_IER_AUTO_CTS);
+		serial_out(up, MA35_IER_REG, ier);
+		up->port.flags &= ~UPF_HARD_FLOW;
+	}
+
+	msr = serial_in(up, MA35_MSR_REG);
+	msr |= MA35_MSR_CTSACTLV;
+	serial_out(up, MA35_MSR_REG, msr);
+	serial_out(up, MA35_MCR_REG, mcr);
+}
+
+static void ma35d1serial_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	unsigned long flags;
+	u32 lcr;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	lcr = serial_in(up, MA35_LCR_REG);
+	if (break_state != 0)
+		lcr |= MA35_LCR_BREAK;
+	else
+		lcr &= ~MA35_LCR_BREAK;
+	serial_out(up, MA35_LCR_REG, lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int ma35d1serial_startup(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	u32 fcr;
+	int retval;
+
+	/* Reset FIFO */
+	serial_out(up, MA35_FCR_REG, MA35_FCR_TFR | MA35_FCR_RFR);
+
+	/* Clear pending interrupts */
+	serial_out(up, MA35_ISR_REG, MA35_ISR_ALL);
+
+	retval = request_irq(port->irq, ma35d1serial_interrupt, 0,
+			     dev_name(port->dev), port);
+	if (retval) {
+		dev_err(up->port.dev, "request irq failed.\n");
+		return retval;
+	}
+
+	fcr = serial_in(up, MA35_FCR_REG);
+	fcr |= MA35_FCR_RFITL_4BYTES | MA35_FCR_RTSTL_8BYTES;
+	serial_out(up, MA35_FCR_REG, fcr);
+	serial_out(up, MA35_LCR_REG, MA35_LCR_WLS_8BITS);
+	serial_out(up, MA35_TOR_REG, MA35_UART_RX_TOUT);
+	serial_out(up, MA35_IER_REG, MA35_IER_CONFIG);
+	return 0;
+}
+
+static void ma35d1serial_shutdown(struct uart_port *port)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+
+	serial_out(up, MA35_IER_REG, 0);
+	free_irq(port->irq, port);
+}
+
+static void ma35d1serial_set_termios(struct uart_port *port,
+				     struct ktermios *termios,
+				     const struct ktermios *old)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+	unsigned long flags;
+	u32 baud, quot;
+	u32 lcr = 0;
+
+	lcr = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));
+
+	if (termios->c_cflag & CSTOPB)
+		lcr |= MA35_LCR_NSB;
+	if (termios->c_cflag & PARENB)
+		lcr |= MA35_LCR_PBE;
+	if (!(termios->c_cflag & PARODD))
+		lcr |= MA35_LCR_EPE;
+	if (termios->c_cflag & CMSPAR)
+		lcr |= MA35_LCR_SPE;
+
+	baud = uart_get_baud_rate(port, termios, old,
+				  port->uartclk / MA35_BAUD_DIV_MAX,
+				  port->uartclk / MA35_BAUD_DIV_MIN);
+
+	/* MA35D1 UART baud rate equation: baudrate = UART_CLK / (quot + 2) */
+	quot = (port->uartclk / baud) - 2;
+
+	/*
+	 * 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 = MA35_FSR_RX_OVER_IF;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= MA35_FSR_FEF | MA35_FSR_PEF;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= MA35_FSR_BIF;
+
+	/* Characteres to ignore */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= MA35_FSR_FEF | MA35_FSR_PEF;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= MA35_FSR_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 |= MA35_FSR_RX_OVER_IF;
+	}
+	if (termios->c_cflag & CRTSCTS)
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr &= ~UART_MCR_AFE;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	ma35d1serial_set_mctrl(&up->port, up->port.mctrl);
+
+	serial_out(up, MA35_BAUD_REG, MA35_BAUD_MODE2 | FIELD_PREP(MA35_BAUD_MASK, quot));
+
+	serial_out(up, MA35_LCR_REG, lcr);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static const char *ma35d1serial_type(struct uart_port *port)
+{
+	return "ma35d1-uart";
+}
+
+static void ma35d1serial_config_port(struct uart_port *port, int flags)
+{
+	/*
+	 * Driver core for serial ports forces a non-zero value for port type.
+	 * Write an arbitrary value here to accommodate the serial core driver,
+	 * as ID part of UAPI is redundant.
+	 */
+	port->type = 1;
+}
+
+static int ma35d1serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if (port->type != PORT_UNKNOWN && ser->type != 1)
+		return -EINVAL;
+
+	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,
+	.config_port  = ma35d1serial_config_port,
+	.verify_port  = ma35d1serial_verify_port,
+};
+
+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 struct device_node *ma35d1serial_uart_nodes[MA35_UART_NR];
+
+static void wait_for_xmitr(struct uart_ma35d1_port *up)
+{
+	unsigned int reg = 0;
+
+	read_poll_timeout_atomic(serial_in, reg, reg & MA35_FSR_TX_EMPTY,
+				 1, 10000, false,
+				 up, MA35_FSR_REG);
+}
+
+static void ma35d1serial_console_putchar(struct uart_port *port, unsigned char ch)
+{
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+
+	wait_for_xmitr(up);
+	serial_out(up, MA35_THR_REG, 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;
+	int locked = 1;
+	u32 ier;
+
+	if (up->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress)
+		locked = spin_trylock_irqsave(&up->port.lock, flags);
+	else
+		spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 *  First save the IER then disable the interrupts
+	 */
+	ier = serial_in(up, MA35_IER_REG);
+	serial_out(up, MA35_IER_REG, 0);
+
+	uart_console_write(&up->port, s, count, ma35d1serial_console_putchar);
+
+	wait_for_xmitr(up);
+	serial_out(up, MA35_IER_REG, ier);
+
+	if (locked)
+		spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int __init ma35d1serial_console_setup(struct console *co, char *options)
+{
+	struct device_node *np;
+	struct uart_ma35d1_port *p;
+	u32 val32[4];
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if ((co->index < 0) || (co->index >= MA35_UART_NR)) {
+		pr_debug("Console Port%x out of range\n", co->index);
+		return -EINVAL;
+	}
+
+	np = ma35d1serial_uart_nodes[co->index];
+	p = &ma35d1serial_ports[co->index];
+	if (!np || !p)
+		return -ENODEV;
+
+	if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0)
+		return -EINVAL;
+
+	p->port.iobase = val32[1];
+	p->port.membase = ioremap(p->port.iobase, MA35_UART_REG_SIZE);
+	if (!p->port.membase)
+		return -ENOMEM;
+
+	p->port.ops = &ma35d1serial_ops;
+	p->port.line = 0;
+	p->port.uartclk = MA35_UART_CONSOLE_CLK;
+
+	port = &ma35d1serial_ports[co->index].port;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console ma35d1serial_console = {
+	.name    = "ttyNVT",
+	.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)
+{
+	u32 i = 0;
+	struct device_node *np;
+
+	for_each_matching_node(np, ma35d1_serial_of_match) {
+		if (ma35d1serial_uart_nodes[i] == NULL) {
+			of_node_get(np);
+			ma35d1serial_uart_nodes[i] = np;
+			i++;
+			if (i == MA35_UART_NR)
+				break;
+		}
+	}
+}
+
+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     = "ttyNVT",
+	.major        = TTY_MAJOR,
+	.minor        = 64,
+	.cons         = MA35D1SERIAL_CONSOLE,
+	.nr           = MA35_UART_NR,
+};
+
+/*
+ * 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 = 0;
+
+	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, MA35_UART_REG_SIZE);
+	up->port.ops = &ma35d1serial_ops;
+
+	spin_lock_init(&up->port.lock);
+
+	up->clk = of_clk_get(pdev->dev.of_node, 0);
+	if (IS_ERR(up->clk)) {
+		ret = PTR_ERR(up->clk);
+		dev_err(&pdev->dev, "failed to get core clk: %d\n", ret);
+		goto err_iounmap;
+	}
+
+	ret = clk_prepare_enable(up->clk);
+	if (ret)
+		goto err_iounmap;
+
+	if (up->port.line != 0)
+		up->port.uartclk = clk_get_rate(up->clk);
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0)
+		goto err_clk_disable;
+
+	up->port.irq = ret;
+	up->port.dev = &pdev->dev;
+	up->port.flags = UPF_BOOT_AUTOCONF;
+
+	platform_set_drvdata(pdev, up);
+
+	ret = uart_add_one_port(&ma35d1serial_reg, &up->port);
+	if (ret < 0)
+		goto err_free_irq;
+
+	return 0;
+
+err_free_irq:
+	free_irq(up->port.irq, &up->port);
+
+err_clk_disable:
+	clk_disable_unprepare(up->clk);
+
+err_iounmap:
+	iounmap(up->port.membase);
+	return ret;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int ma35d1serial_remove(struct platform_device *dev)
+{
+	struct uart_port *port = platform_get_drvdata(dev);
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+
+	uart_remove_one_port(&ma35d1serial_reg, port);
+	clk_disable_unprepare(up->clk);
+	return 0;
+}
+
+static int ma35d1serial_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct uart_port *port = platform_get_drvdata(dev);
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+
+	uart_suspend_port(&ma35d1serial_reg, &up->port);
+	if (up->port.line == 0) {
+		up->console_baud_rate = serial_in(up, MA35_BAUD_REG);
+		up->console_line = serial_in(up, MA35_LCR_REG);
+		up->console_int = serial_in(up, MA35_IER_REG);
+	}
+	return 0;
+}
+
+static int ma35d1serial_resume(struct platform_device *dev)
+{
+	struct uart_port *port = platform_get_drvdata(dev);
+	struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
+
+	if (up->port.line == 0) {
+		serial_out(up, MA35_BAUD_REG, up->console_baud_rate);
+		serial_out(up, MA35_LCR_REG, up->console_line);
+		serial_out(up, MA35_IER_REG, up->console_int);
+	}
+	uart_resume_port(&ma35d1serial_reg, &up->port);
+	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");
+MODULE_DESCRIPTION("MA35D1 serial driver");
-- 
2.34.1


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

* [PATCH 2/2] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-06-19  3:23 [PATCH v15 0/2] Introduce Nuvoton ma35d1 SoC Jacky Huang
  2023-06-19  3:23 ` [PATCH v15 1/2] tty: serial: Add Nuvoton ma35d1 serial driver support Jacky Huang
@ 2023-06-19  3:23 ` Jacky Huang
  2023-06-19  3:34   ` Jacky Huang
  1 sibling, 1 reply; 4+ messages in thread
From: Jacky Huang @ 2023-06-19  3:23 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, tmaimon77, catalin.marinas, will
  Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
	linux-serial, arnd, soc, schung, mjchen, Jacky Huang,
	Krzysztof Kozlowski

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>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
 drivers/clk/Kconfig                      |    1 +
 drivers/clk/Makefile                     |    1 +
 drivers/clk/nuvoton/Kconfig              |   19 +
 drivers/clk/nuvoton/Makefile             |    4 +
 drivers/clk/nuvoton/clk-ma35d1-divider.c |  132 +++
 drivers/clk/nuvoton/clk-ma35d1-pll.c     |  360 ++++++++
 drivers/clk/nuvoton/clk-ma35d1.c         | 1046 ++++++++++++++++++++++
 drivers/clk/nuvoton/clk-ma35d1.h         |   18 +
 8 files changed, 1581 insertions(+)
 create mode 100644 drivers/clk/nuvoton/Kconfig
 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/Kconfig b/drivers/clk/Kconfig
index 016814e15536..db789055e0fd 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -478,6 +478,7 @@ source "drivers/clk/meson/Kconfig"
 source "drivers/clk/mstar/Kconfig"
 source "drivers/clk/microchip/Kconfig"
 source "drivers/clk/mvebu/Kconfig"
+source "drivers/clk/nuvoton/Kconfig"
 source "drivers/clk/pistachio/Kconfig"
 source "drivers/clk/qcom/Kconfig"
 source "drivers/clk/ralink/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 0aebef17edc6..7cb000549b61 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -106,6 +106,7 @@ endif
 obj-y					+= mstar/
 obj-y					+= mvebu/
 obj-$(CONFIG_ARCH_MXS)			+= mxs/
+obj-$(CONFIG_ARCH_MA35)			+= 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/Kconfig b/drivers/clk/nuvoton/Kconfig
new file mode 100644
index 000000000000..fe4b7f62f467
--- /dev/null
+++ b/drivers/clk/nuvoton/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+# common clock support for Nuvoton SoC family.
+
+config COMMON_CLK_NUVOTON
+	bool "Nuvoton clock controller common support"
+	depends on ARCH_MA35 || COMPILE_TEST
+	default y
+	help
+	  Say y here to enable common clock controller for Nuvoton platforms.
+
+if COMMON_CLK_NUVOTON
+
+config CLK_MA35D1
+	bool "Nuvoton MA35D1 clock controller support"
+	default y
+	help
+	  Build the clock controller driver for MA35D1 SoC.
+
+endif
diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile
new file mode 100644
index 000000000000..c3c59dd9f2aa
--- /dev/null
+++ b/drivers/clk/nuvoton/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1.o
+obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1-divider.o
+obj-$(CONFIG_CLK_MA35D1) += 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..bb8c23d2b895
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
@@ -0,0 +1,132 @@
+// 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/device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#include "clk-ma35d1.h"
+
+struct ma35d1_adc_clk_div {
+	struct clk_hw hw;
+	void __iomem *reg;
+	u8 shift;
+	u8 width;
+	u32 mask;
+	const struct clk_div_table *table;
+	/* protects concurrent access to clock divider registers */
+	spinlock_t *lock;
+};
+
+static inline struct ma35d1_adc_clk_div *to_ma35d1_adc_clk_div(struct clk_hw *_hw)
+{
+	return container_of(_hw, struct ma35d1_adc_clk_div, hw);
+}
+
+static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	unsigned int val;
+	struct ma35d1_adc_clk_div *dclk = to_ma35d1_adc_clk_div(hw);
+
+	val = readl_relaxed(dclk->reg) >> dclk->shift;
+	val &= clk_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_div *dclk = to_ma35d1_adc_clk_div(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_div *dclk = to_ma35d1_adc_clk_div(hw);
+
+	value = divider_get_val(rate, parent_rate, dclk->table,
+				dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
+
+	spin_lock_irqsave(dclk->lock, flags);
+
+	data = readl_relaxed(dclk->reg);
+	data &= ~(clk_div_mask(dclk->width) << dclk->shift);
+	data |= (value - 1) << dclk->shift;
+	data |= dclk->mask;
+	writel_relaxed(data, dclk->reg);
+
+	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,
+				     struct clk_hw *parent_hw, spinlock_t *lock,
+				     unsigned long flags, void __iomem *reg,
+				     u8 shift, u8 width, u32 mask_bit)
+{
+	struct ma35d1_adc_clk_div *div;
+	struct clk_init_data init;
+	struct clk_div_table *table;
+	struct clk_parent_data pdata = { .index = 0 };
+	u32 max_div, min_div;
+	struct clk_hw *hw;
+	int ret;
+	int i;
+
+	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
+	if (!div)
+		return ERR_PTR(-ENOMEM);
+
+	max_div = clk_div_mask(width) + 1;
+	min_div = 1;
+
+	table = devm_kcalloc(dev, max_div + 1, sizeof(*table), GFP_KERNEL);
+	if (!table)
+		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;
+
+	memset(&init, 0, sizeof(init));
+	init.name = name;
+	init.ops = &ma35d1_adc_clkdiv_ops;
+	init.flags |= flags;
+	pdata.hw = parent_hw;
+	init.parent_data = &pdata;
+	init.num_parents = 1;
+
+	div->reg = reg;
+	div->shift = shift;
+	div->width = width;
+	div->mask = mask_bit ? BIT(mask_bit) : 0;
+	div->lock = lock;
+	div->hw.init = &init;
+	div->table = table;
+
+	hw = &div->hw;
+	ret = devm_clk_hw_register(dev, hw);
+	if (ret)
+		return ERR_PTR(ret);
+	return hw;
+}
+EXPORT_SYMBOL_GPL(ma35d1_reg_adc_clkdiv);
diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
new file mode 100644
index 000000000000..ff3fb8b87c24
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+#include "clk-ma35d1.h"
+
+/* PLL frequency limits */
+#define PLL_FREF_MAX_FREQ	(200 * HZ_PER_MHZ)
+#define PLL_FREF_MIN_FREQ	(1 * HZ_PER_MHZ)
+#define PLL_FREF_M_MAX_FREQ	(40 * HZ_PER_MHZ)
+#define PLL_FREF_M_MIN_FREQ	(10 * HZ_PER_MHZ)
+#define PLL_FCLK_MAX_FREQ	(2400 * HZ_PER_MHZ)
+#define PLL_FCLK_MIN_FREQ	(600 * HZ_PER_MHZ)
+#define PLL_FCLKO_MAX_FREQ	(2400 * HZ_PER_MHZ)
+#define PLL_FCLKO_MIN_FREQ	(85700 * HZ_PER_KHZ)
+#define PLL_SS_RATE		0x77
+#define PLL_SLOPE		0x58CFA
+
+#define REG_PLL_CTL0_OFFSET	0x0
+#define REG_PLL_CTL1_OFFSET	0x4
+#define REG_PLL_CTL2_OFFSET	0x8
+
+/* bit fields for REG_CLK_PLL0CTL0, which is SMIC PLL design */
+#define SPLL0_CTL0_FBDIV	GENMASK(7, 0)
+#define SPLL0_CTL0_INDIV	GENMASK(11, 8)
+#define SPLL0_CTL0_OUTDIV	GENMASK(13, 12)
+#define SPLL0_CTL0_PD		BIT(16)
+#define SPLL0_CTL0_BP		BIT(17)
+
+/* bit fields for REG_CLK_PLLxCTL0 ~ REG_CLK_PLLxCTL2, where x = 2 ~ 5 */
+#define PLL_CTL0_FBDIV		GENMASK(10, 0)
+#define PLL_CTL0_INDIV		GENMASK(17, 12)
+#define PLL_CTL0_MODE		GENMASK(19, 18)
+#define PLL_CTL0_SSRATE		GENMASK(30, 20)
+#define PLL_CTL1_PD		BIT(0)
+#define PLL_CTL1_BP		BIT(1)
+#define PLL_CTL1_OUTDIV		GENMASK(6, 4)
+#define PLL_CTL1_FRAC		GENMASK(31, 24)
+#define PLL_CTL2_SLOPE		GENMASK(23, 0)
+
+#define INDIV_MIN		1
+#define INDIV_MAX		63
+#define FBDIV_MIN		16
+#define FBDIV_MAX		2047
+#define FBDIV_FRAC_MIN		1600
+#define FBDIV_FRAC_MAX		204700
+#define OUTDIV_MIN		1
+#define OUTDIV_MAX		7
+
+#define PLL_MODE_INT            0
+#define PLL_MODE_FRAC           1
+#define PLL_MODE_SS             2
+
+struct ma35d1_clk_pll {
+	struct clk_hw hw;
+	u32 id;
+	u8 mode;
+	void __iomem *ctl0_base;
+	void __iomem *ctl1_base;
+	void __iomem *ctl2_base;
+};
+
+static inline struct ma35d1_clk_pll *to_ma35d1_clk_pll(struct clk_hw *_hw)
+{
+	return container_of(_hw, struct ma35d1_clk_pll, hw);
+}
+
+static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,
+					       unsigned long parent_rate)
+{
+	u32 m, n, p, outdiv;
+	u64 pll_freq;
+
+	if (pll0_ctl0 & SPLL0_CTL0_BP)
+		return parent_rate;
+
+	n = FIELD_GET(SPLL0_CTL0_FBDIV, pll0_ctl0);
+	m = FIELD_GET(SPLL0_CTL0_INDIV, pll0_ctl0);
+	p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);
+	outdiv = 1 << p;
+	pll_freq = (u64)parent_rate * n;
+	div_u64(pll_freq, m * outdiv);
+	return pll_freq;
+}
+
+static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long parent_rate)
+{
+	unsigned long pll_freq, x;
+	u32 m, n, p;
+
+	if (reg_ctl[1] & PLL_CTL1_BP)
+		return parent_rate;
+
+	n = FIELD_GET(PLL_CTL0_FBDIV, reg_ctl[0]);
+	m = FIELD_GET(PLL_CTL0_INDIV, reg_ctl[0]);
+	p = FIELD_GET(PLL_CTL1_OUTDIV, reg_ctl[1]);
+
+	if (mode == PLL_MODE_INT) {
+		pll_freq = (u64)parent_rate * n;
+		div_u64(pll_freq, m * p);
+	} else {
+		x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
+		/* 2 decimal places floating to integer (ex. 1.23 to 123) */
+		n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));
+		pll_freq = div_u64(parent_rate * n, 100 * m * p);
+	}
+	return pll_freq;
+}
+
+static int ma35d1_pll_find_closest(struct ma35d1_clk_pll *pll, unsigned long rate,
+				   unsigned long parent_rate, u32 *reg_ctl,
+				   unsigned long *freq)
+{
+	unsigned long min_diff = ULONG_MAX;
+	int fbdiv_min, fbdiv_max;
+	int p, m, n;
+
+	*freq = 0;
+	if (rate < PLL_FCLKO_MIN_FREQ || rate > PLL_FCLKO_MAX_FREQ)
+		return -EINVAL;
+
+	if (pll->mode == PLL_MODE_INT) {
+		fbdiv_min = FBDIV_MIN;
+		fbdiv_max = FBDIV_MAX;
+	} else {
+		fbdiv_min = FBDIV_FRAC_MIN;
+		fbdiv_max = FBDIV_FRAC_MAX;
+	}
+
+	for (m = INDIV_MIN; m <= INDIV_MAX; m++) {
+		for (n = fbdiv_min; n <= fbdiv_max; n++) {
+			for (p = OUTDIV_MIN; p <= OUTDIV_MAX; p++) {
+				unsigned long tmp, fout, fclk, diff;
+
+				tmp = div_u64(parent_rate, m);
+				if (tmp < PLL_FREF_M_MIN_FREQ ||
+				    tmp > PLL_FREF_M_MAX_FREQ)
+					continue; /* constrain */
+
+				fclk = div_u64(parent_rate * n, m);
+				/* for 2 decimal places */
+				if (pll->mode != PLL_MODE_INT)
+					fclk = div_u64(fclk, 100);
+
+				if (fclk < PLL_FCLK_MIN_FREQ ||
+				    fclk > PLL_FCLK_MAX_FREQ)
+					continue; /* constrain */
+
+				fout = div_u64(fclk, p);
+				if (fout < PLL_FCLKO_MIN_FREQ ||
+				    fout > PLL_FCLKO_MAX_FREQ)
+					continue; /* constrain */
+
+				diff = abs(rate - fout);
+				if (diff < min_diff) {
+					reg_ctl[0] = FIELD_PREP(PLL_CTL0_INDIV, m) |
+						     FIELD_PREP(PLL_CTL0_FBDIV, n);
+					reg_ctl[1] = FIELD_PREP(PLL_CTL1_OUTDIV, p);
+					*freq = fout;
+					min_diff = diff;
+					if (min_diff == 0)
+						break;
+				}
+			}
+		}
+	}
+	if (*freq == 0)
+		return -EINVAL; /* cannot find even one valid setting */
+	return 0;
+}
+
+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);
+	u32 reg_ctl[3] = { 0 };
+	unsigned long pll_freq;
+	int ret;
+
+	if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)
+		return -EINVAL;
+
+	ret = ma35d1_pll_find_closest(pll, rate, parent_rate, reg_ctl, &pll_freq);
+	if (ret != 0)
+		return ret;
+
+	switch (pll->mode) {
+	case PLL_MODE_INT:
+		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_INT);
+		break;
+	case PLL_MODE_FRAC:
+		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_FRAC);
+		break;
+	case PLL_MODE_SS:
+		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_SS) |
+			      FIELD_PREP(PLL_CTL0_SSRATE, PLL_SS_RATE);
+		reg_ctl[2] = FIELD_PREP(PLL_CTL2_SLOPE, PLL_SLOPE);
+		break;
+	}
+	reg_ctl[1] |= PLL_CTL1_PD;
+
+	writel_relaxed(reg_ctl[0], pll->ctl0_base);
+	writel_relaxed(reg_ctl[1], pll->ctl1_base);
+	writel_relaxed(reg_ctl[2], pll->ctl2_base);
+	return 0;
+}
+
+static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 reg_ctl[3];
+	unsigned long pll_freq;
+
+	if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)
+		return 0;
+
+	switch (pll->id) {
+	case CAPLL:
+		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+		pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], parent_rate);
+		return pll_freq;
+	case DDRPLL:
+	case APLL:
+	case EPLL:
+	case VPLL:
+		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+		reg_ctl[1] = readl_relaxed(pll->ctl1_base);
+		pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, parent_rate);
+		return pll_freq;
+	}
+	return 0;
+}
+
+static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long *parent_rate)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 reg_ctl[3] = { 0 };
+	unsigned long pll_freq;
+	long ret;
+
+	if (*parent_rate < PLL_FREF_MIN_FREQ || *parent_rate > PLL_FREF_MAX_FREQ)
+		return -EINVAL;
+
+	ret = ma35d1_pll_find_closest(pll, rate, *parent_rate, reg_ctl, &pll_freq);
+	if (ret < 0)
+		return ret;
+
+	switch (pll->id) {
+	case CAPLL:
+		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+		pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], *parent_rate);
+		return pll_freq;
+	case DDRPLL:
+	case APLL:
+	case EPLL:
+	case VPLL:
+		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
+		reg_ctl[1] = readl_relaxed(pll->ctl1_base);
+		pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, *parent_rate);
+		return pll_freq;
+	}
+	return 0;
+}
+
+static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 val = readl_relaxed(pll->ctl1_base);
+
+	return !(val & PLL_CTL1_PD);
+}
+
+static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 val;
+
+	val = readl_relaxed(pll->ctl1_base);
+	val &= ~PLL_CTL1_PD;
+	writel_relaxed(val, pll->ctl1_base);
+	return 0;
+}
+
+static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
+{
+	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
+	u32 val;
+
+	val = readl_relaxed(pll->ctl1_base);
+	val |= PLL_CTL1_PD;
+	writel_relaxed(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,
+};
+
+static const struct clk_ops ma35d1_clk_fixed_pll_ops = {
+	.recalc_rate = ma35d1_clk_pll_recalc_rate,
+	.round_rate = ma35d1_clk_pll_round_rate,
+};
+
+struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,
+				  struct clk_hw *parent_hw, void __iomem *base)
+{
+	struct clk_parent_data pdata = { .index = 0 };
+	struct clk_init_data init = {};
+	struct ma35d1_clk_pll *pll;
+	struct clk_hw *hw;
+	int ret;
+
+	pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->id = id;
+	pll->mode = u8mode;
+	pll->ctl0_base = base + REG_PLL_CTL0_OFFSET;
+	pll->ctl1_base = base + REG_PLL_CTL1_OFFSET;
+	pll->ctl2_base = base + REG_PLL_CTL2_OFFSET;
+
+	init.name = name;
+	init.flags = 0;
+	pdata.hw = parent_hw;
+	init.parent_data = &pdata;
+	init.num_parents = 1;
+
+	if (id == CAPLL || id == DDRPLL)
+		init.ops = &ma35d1_clk_fixed_pll_ops;
+	else
+		init.ops = &ma35d1_clk_pll_ops;
+
+	pll->hw.init = &init;
+	hw = &pll->hw;
+
+	ret = devm_clk_hw_register(dev, hw);
+	if (ret)
+		return ERR_PTR(ret);
+	return hw;
+}
+EXPORT_SYMBOL_GPL(ma35d1_reg_clk_pll);
diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
new file mode 100644
index 000000000000..e77e06a4a07c
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1.c
@@ -0,0 +1,1046 @@
+// 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/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+#include "clk-ma35d1.h"
+
+static DEFINE_SPINLOCK(ma35d1_lock);
+
+#define PLL_MAX_NUM		5
+
+/* 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
+
+#define PLL_MODE_INT            0
+#define PLL_MODE_FRAC           1
+#define PLL_MODE_SS             2
+
+static const struct clk_parent_data ca35clk_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "capll", },
+	{ .fw_name = "ddrpll", },
+};
+
+static const struct clk_parent_data sysclk0_sel_clks[] = {
+	{ .fw_name = "epll_div2", },
+	{ .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data sysclk1_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data axiclk_sel_clks[] = {
+	{ .fw_name = "capll_div2", },
+	{ .fw_name = "capll_div4", },
+};
+
+static const struct clk_parent_data ccap_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "vpll", },
+	{ .fw_name = "apll", },
+	{ .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data sdh_sel_clks[] = {
+	{ .fw_name = "syspll", },
+	{ .fw_name = "apll", },
+};
+
+static const struct clk_parent_data dcu_sel_clks[] = {
+	{ .fw_name = "epll_div2", },
+	{ .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data gfx_sel_clks[] = {
+	{ .fw_name = "epll", },
+	{ .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data dbg_sel_clks[] = {
+	{ .fw_name = "hirc", },
+	{ .fw_name = "syspll", },
+};
+
+static const struct clk_parent_data timer0_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk0", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer1_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk0", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer2_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk1", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer3_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk1", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer4_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk2", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer5_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk2", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer6_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk0", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer7_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk0", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer8_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk1", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer9_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk1", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer10_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk2", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data timer11_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk2", },
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "lirc", },
+	{ .index = -1, },
+	{ .fw_name = "hirc", },
+};
+
+static const struct clk_parent_data uart_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "sysclk1_div2", },
+};
+
+static const struct clk_parent_data wdt0_sel_clks[] = {
+	{ .index = -1, },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk3_div4096", },
+	{ .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wdt1_sel_clks[] = {
+	{ .index = -1, },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk3_div4096", },
+	{ .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wdt2_sel_clks[] = {
+	{ .index = -1, },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "pclk4_div4096", },
+	{ .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wwdt0_sel_clks[] = {
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "pclk3_div4096", },
+	{ .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wwdt1_sel_clks[] = {
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "pclk3_div4096", },
+	{ .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data wwdt2_sel_clks[] = {
+	{ .index = -1, },
+	{ .index = -1, },
+	{ .fw_name = "pclk4_div4096", },
+	{ .fw_name = "lirc", },
+};
+
+static const struct clk_parent_data spi0_sel_clks[] = {
+	{ .fw_name = "pclk1", },
+	{ .fw_name = "apll", },
+};
+
+static const struct clk_parent_data spi1_sel_clks[] = {
+	{ .fw_name = "pclk2", },
+	{ .fw_name = "apll", },
+};
+
+static const struct clk_parent_data spi2_sel_clks[] = {
+	{ .fw_name = "pclk1", },
+	{ .fw_name = "apll", },
+};
+
+static const struct clk_parent_data spi3_sel_clks[] = {
+	{ .fw_name = "pclk2", },
+	{ .fw_name = "apll", },
+};
+
+static const struct clk_parent_data qspi_sel_clks[] = {
+	{ .fw_name = "pclk0", },
+	{ .fw_name = "apll", },
+};
+
+static const struct clk_parent_data i2s_sel_clks[] = {
+	{ .fw_name = "apll", },
+	{ .fw_name = "sysclk1_div2", },
+};
+
+static const struct clk_parent_data can_sel_clks[] = {
+	{ .fw_name = "apll", },
+	{ .fw_name = "vpll", },
+};
+
+static const struct clk_parent_data cko_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "lxt", },
+	{ .fw_name = "hirc", },
+	{ .fw_name = "lirc", },
+	{ .fw_name = "capll_div4", },
+	{ .fw_name = "syspll", },
+	{ .fw_name = "ddrpll", },
+	{ .fw_name = "epll_div2", },
+	{ .fw_name = "apll", },
+	{ .fw_name = "vpll", },
+};
+
+static const struct clk_parent_data smc_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "pclk4", },
+};
+
+static const struct clk_parent_data kpi_sel_clks[] = {
+	{ .fw_name = "hxt", },
+	{ .fw_name = "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 *ma35d1_clk_fixed(const char *name, int rate)
+{
+	return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
+}
+
+static struct clk_hw *ma35d1_clk_mux(struct device *dev, const char *name,
+				     void __iomem *reg, u8 shift, u8 width,
+				     const struct clk_parent_data *pdata,
+				     int num_pdata)
+{
+	return clk_hw_register_mux_parent_data(dev, name, pdata, num_pdata,
+					       CLK_SET_RATE_NO_REPARENT, reg, shift,
+					       width, 0, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_divider(struct device *dev, const char *name,
+					 const char *parent, void __iomem *reg,
+					 u8 shift, u8 width)
+{
+	return devm_clk_hw_register_divider(dev, name, parent, CLK_SET_RATE_PARENT,
+					    reg, shift, width, 0, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_divider_pow2(struct device *dev, const char *name,
+					      const char *parent, void __iomem *reg,
+					      u8 shift, u8 width)
+{
+	return devm_clk_hw_register_divider(dev, name, parent,
+					    CLK_DIVIDER_POWER_OF_TWO, reg, shift,
+					    width, 0, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_divider_table(struct device *dev, const char *name,
+					       const char *parent, void __iomem *reg,
+					       u8 shift, u8 width,
+					       const struct clk_div_table *table)
+{
+	return devm_clk_hw_register_divider_table(dev, name, parent, 0,
+						  reg, shift, width, 0,
+						  table, &ma35d1_lock);
+}
+
+static struct clk_hw *ma35d1_clk_fixed_factor(struct device *dev, const char *name,
+					      const char *parent, unsigned int mult,
+					      unsigned int div)
+{
+	return devm_clk_hw_register_fixed_factor(dev, name, parent,
+					    CLK_SET_RATE_PARENT, mult, div);
+}
+
+static struct clk_hw *ma35d1_clk_gate(struct device *dev, const char *name, const char *parent,
+				      void __iomem *reg, u8 shift)
+{
+	return devm_clk_hw_register_gate(dev, name, parent, CLK_SET_RATE_PARENT,
+				    reg, shift, 0, &ma35d1_lock);
+}
+
+static int ma35d1_get_pll_setting(struct device_node *clk_node, u32 *pllmode)
+{
+	const char *of_str;
+	int i;
+
+	for (i = 0; i < PLL_MAX_NUM; i++) {
+		if (of_property_read_string_index(clk_node, "nuvoton,pll-mode", i, &of_str))
+			return -EINVAL;
+		if (!strcmp(of_str, "integer"))
+			pllmode[i] = PLL_MODE_INT;
+		else if (!strcmp(of_str, "fractional"))
+			pllmode[i] = PLL_MODE_FRAC;
+		else if (!strcmp(of_str, "spread-spectrum"))
+			pllmode[i] = PLL_MODE_SS;
+		else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int ma35d1_clocks_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *clk_node = pdev->dev.of_node;
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	void __iomem *clk_base;
+	static struct clk_hw **hws;
+	static struct clk_hw_onecell_data *ma35d1_hw_data;
+	u32 pllmode[PLL_MAX_NUM];
+	int ret;
+
+	ma35d1_hw_data = devm_kzalloc(dev,
+				      struct_size(ma35d1_hw_data, hws, CLK_MAX_IDX),
+				      GFP_KERNEL);
+	if (!ma35d1_hw_data)
+		return -ENOMEM;
+
+	ma35d1_hw_data->num = CLK_MAX_IDX;
+	hws = ma35d1_hw_data->hws;
+
+	clk_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(clk_base))
+		return PTR_ERR(clk_base);
+
+	ret = ma35d1_get_pll_setting(clk_node, pllmode);
+	if (ret < 0) {
+		dev_err(dev, "Invalid PLL setting!\n");
+		return -EINVAL;
+	}
+
+	hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
+	hws[HXT_GATE] = ma35d1_clk_gate(dev, "hxt_gate", "hxt",
+					clk_base + REG_CLK_PWRCTL, 0);
+	hws[LXT] = ma35d1_clk_fixed("lxt", 32768);
+	hws[LXT_GATE] = ma35d1_clk_gate(dev, "lxt_gate", "lxt",
+					clk_base + REG_CLK_PWRCTL, 1);
+	hws[HIRC] = ma35d1_clk_fixed("hirc", 12000000);
+	hws[HIRC_GATE] = ma35d1_clk_gate(dev, "hirc_gate", "hirc",
+					 clk_base + REG_CLK_PWRCTL, 2);
+	hws[LIRC] = ma35d1_clk_fixed("lirc", 32000);
+	hws[LIRC_GATE] = ma35d1_clk_gate(dev, "lirc_gate", "lirc",
+					 clk_base + REG_CLK_PWRCTL, 3);
+
+	hws[CAPLL] = ma35d1_reg_clk_pll(dev, CAPLL, pllmode[0], "capll",
+					hws[HXT], clk_base + REG_CLK_PLL0CTL0);
+	hws[SYSPLL] = ma35d1_clk_fixed("syspll", 180000000);
+	hws[DDRPLL] = ma35d1_reg_clk_pll(dev, DDRPLL, pllmode[1], "ddrpll",
+					hws[HXT], clk_base + REG_CLK_PLL2CTL0);
+	hws[APLL] = ma35d1_reg_clk_pll(dev, APLL, pllmode[2], "apll",
+				       hws[HXT], clk_base + REG_CLK_PLL3CTL0);
+	hws[EPLL] = ma35d1_reg_clk_pll(dev, EPLL, pllmode[3], "epll",
+				       hws[HXT], clk_base + REG_CLK_PLL4CTL0);
+	hws[VPLL] = ma35d1_reg_clk_pll(dev, VPLL, pllmode[4], "vpll",
+				       hws[HXT], clk_base + REG_CLK_PLL5CTL0);
+
+	hws[EPLL_DIV2] = ma35d1_clk_fixed_factor(dev, "epll_div2", "epll", 1, 2);
+	hws[EPLL_DIV4] = ma35d1_clk_fixed_factor(dev, "epll_div4", "epll", 1, 4);
+	hws[EPLL_DIV8] = ma35d1_clk_fixed_factor(dev, "epll_div8", "epll", 1, 8);
+
+	hws[CA35CLK_MUX] = ma35d1_clk_mux(dev, "ca35clk_mux", clk_base + REG_CLK_CLKSEL0, 0, 2,
+					  ca35clk_sel_clks, ARRAY_SIZE(ca35clk_sel_clks));
+
+	hws[AXICLK_DIV2] = ma35d1_clk_fixed_factor(dev, "capll_div2", "ca35clk_mux", 1, 2);
+	hws[AXICLK_DIV4] = ma35d1_clk_fixed_factor(dev, "capll_div4", "ca35clk_mux", 1, 4);
+
+	hws[AXICLK_MUX] = ma35d1_clk_mux(dev, "axiclk_mux", clk_base + REG_CLK_CLKDIV0, 26, 1,
+					 axiclk_sel_clks, ARRAY_SIZE(axiclk_sel_clks));
+
+	hws[SYSCLK0_MUX] = ma35d1_clk_mux(dev, "sysclk0_mux",
+					  clk_base + REG_CLK_CLKSEL0, 2, 1,
+					  sysclk0_sel_clks, ARRAY_SIZE(sysclk0_sel_clks));
+
+	hws[SYSCLK1_MUX] = ma35d1_clk_mux(dev, "sysclk1_mux", clk_base + REG_CLK_CLKSEL0, 4, 1,
+					  sysclk1_sel_clks, ARRAY_SIZE(sysclk1_sel_clks));
+
+	hws[SYSCLK1_DIV2] = ma35d1_clk_fixed_factor(dev, "sysclk1_div2", "sysclk1_mux", 1, 2);
+
+	/* HCLK0~3 & PCLK0~4 */
+	hws[HCLK0] = ma35d1_clk_fixed_factor(dev, "hclk0", "sysclk1_mux", 1, 1);
+	hws[HCLK1] = ma35d1_clk_fixed_factor(dev, "hclk1", "sysclk1_mux", 1, 1);
+	hws[HCLK2] = ma35d1_clk_fixed_factor(dev, "hclk2", "sysclk1_mux", 1, 1);
+	hws[PCLK0] = ma35d1_clk_fixed_factor(dev, "pclk0", "sysclk1_mux", 1, 1);
+	hws[PCLK1] = ma35d1_clk_fixed_factor(dev, "pclk1", "sysclk1_mux", 1, 1);
+	hws[PCLK2] = ma35d1_clk_fixed_factor(dev, "pclk2", "sysclk1_mux", 1, 1);
+
+	hws[HCLK3] = ma35d1_clk_fixed_factor(dev, "hclk3", "sysclk1_mux", 1, 2);
+	hws[PCLK3] = ma35d1_clk_fixed_factor(dev, "pclk3", "sysclk1_mux", 1, 2);
+	hws[PCLK4] = ma35d1_clk_fixed_factor(dev, "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(dev, "ddr0_gate", "ddrpll",
+					 clk_base + REG_CLK_SYSCLK0, 4);
+	hws[DDR6_GATE] = ma35d1_clk_gate(dev, "ddr6_gate", "ddrpll",
+					 clk_base + REG_CLK_SYSCLK0, 5);
+
+	hws[CAN0_MUX] = ma35d1_clk_mux(dev, "can0_mux", clk_base + REG_CLK_CLKSEL4, 16, 1,
+				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
+	hws[CAN0_DIV] = ma35d1_clk_divider_table(dev, "can0_div", "can0_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 0, 3, ip_div_table);
+	hws[CAN0_GATE] = ma35d1_clk_gate(dev, "can0_gate", "can0_div",
+					 clk_base + REG_CLK_SYSCLK0, 8);
+
+	hws[CAN1_MUX] = ma35d1_clk_mux(dev, "can1_mux", clk_base + REG_CLK_CLKSEL4, 17, 1,
+				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
+	hws[CAN1_DIV] = ma35d1_clk_divider_table(dev, "can1_div", "can1_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 4, 3, ip_div_table);
+	hws[CAN1_GATE] = ma35d1_clk_gate(dev, "can1_gate", "can1_div",
+					 clk_base + REG_CLK_SYSCLK0, 9);
+
+	hws[CAN2_MUX] = ma35d1_clk_mux(dev, "can2_mux", clk_base + REG_CLK_CLKSEL4, 18, 1,
+				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
+	hws[CAN2_DIV] = ma35d1_clk_divider_table(dev, "can2_div", "can2_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 8, 3, ip_div_table);
+	hws[CAN2_GATE] = ma35d1_clk_gate(dev, "can2_gate", "can2_div",
+					 clk_base + REG_CLK_SYSCLK0, 10);
+
+	hws[CAN3_MUX] = ma35d1_clk_mux(dev, "can3_mux", clk_base + REG_CLK_CLKSEL4, 19, 1,
+				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
+	hws[CAN3_DIV] = ma35d1_clk_divider_table(dev, "can3_div", "can3_mux",
+						 clk_base + REG_CLK_CLKDIV0,
+						 12, 3, ip_div_table);
+	hws[CAN3_GATE] = ma35d1_clk_gate(dev, "can3_gate", "can3_div",
+					 clk_base + REG_CLK_SYSCLK0, 11);
+
+	hws[SDH0_MUX] = ma35d1_clk_mux(dev, "sdh0_mux", clk_base + REG_CLK_CLKSEL0, 16, 2,
+				       sdh_sel_clks, ARRAY_SIZE(sdh_sel_clks));
+	hws[SDH0_GATE] = ma35d1_clk_gate(dev, "sdh0_gate", "sdh0_mux",
+					 clk_base + REG_CLK_SYSCLK0, 16);
+
+	hws[SDH1_MUX] = ma35d1_clk_mux(dev, "sdh1_mux", clk_base + REG_CLK_CLKSEL0, 18, 2,
+				       sdh_sel_clks, ARRAY_SIZE(sdh_sel_clks));
+	hws[SDH1_GATE] = ma35d1_clk_gate(dev, "sdh1_gate", "sdh1_mux",
+					 clk_base + REG_CLK_SYSCLK0, 17);
+
+	hws[NAND_GATE] = ma35d1_clk_gate(dev, "nand_gate", "hclk1",
+					 clk_base + REG_CLK_SYSCLK0, 18);
+
+	hws[USBD_GATE] = ma35d1_clk_gate(dev, "usbd_gate", "usbphy0",
+					 clk_base + REG_CLK_SYSCLK0, 19);
+	hws[USBH_GATE] = ma35d1_clk_gate(dev, "usbh_gate", "usbphy0",
+					 clk_base + REG_CLK_SYSCLK0, 20);
+	hws[HUSBH0_GATE] = ma35d1_clk_gate(dev, "husbh0_gate", "usbphy0",
+					   clk_base + REG_CLK_SYSCLK0, 21);
+	hws[HUSBH1_GATE] = ma35d1_clk_gate(dev, "husbh1_gate", "usbphy0",
+					   clk_base + REG_CLK_SYSCLK0, 22);
+
+	hws[GFX_MUX] = ma35d1_clk_mux(dev, "gfx_mux", clk_base + REG_CLK_CLKSEL0, 26, 1,
+				      gfx_sel_clks, ARRAY_SIZE(gfx_sel_clks));
+	hws[GFX_GATE] = ma35d1_clk_gate(dev, "gfx_gate", "gfx_mux",
+					clk_base + REG_CLK_SYSCLK0, 24);
+	hws[VC8K_GATE] = ma35d1_clk_gate(dev, "vc8k_gate", "sysclk0_mux",
+					 clk_base + REG_CLK_SYSCLK0, 25);
+
+	hws[DCU_MUX] = ma35d1_clk_mux(dev, "dcu_mux", clk_base + REG_CLK_CLKSEL0, 24, 1,
+				      dcu_sel_clks, ARRAY_SIZE(dcu_sel_clks));
+	hws[DCU_GATE] = ma35d1_clk_gate(dev, "dcu_gate", "dcu_mux",
+					clk_base + REG_CLK_SYSCLK0, 26);
+	hws[DCUP_DIV] = ma35d1_clk_divider_table(dev, "dcup_div", "vpll",
+						 clk_base + REG_CLK_CLKDIV0,
+						 16, 3, ip_div_table);
+
+	hws[EMAC0_GATE] = ma35d1_clk_gate(dev, "emac0_gate", "epll_div2",
+					  clk_base + REG_CLK_SYSCLK0, 27);
+	hws[EMAC1_GATE] = ma35d1_clk_gate(dev, "emac1_gate", "epll_div2",
+					  clk_base + REG_CLK_SYSCLK0, 28);
+
+	hws[CCAP0_MUX] = ma35d1_clk_mux(dev, "ccap0_mux", clk_base + REG_CLK_CLKSEL0, 12, 1,
+					ccap_sel_clks, ARRAY_SIZE(ccap_sel_clks));
+	hws[CCAP0_DIV] = ma35d1_clk_divider(dev, "ccap0_div", "ccap0_mux",
+					    clk_base + REG_CLK_CLKDIV1, 8, 4);
+	hws[CCAP0_GATE] = ma35d1_clk_gate(dev, "ccap0_gate", "ccap0_div",
+					  clk_base + REG_CLK_SYSCLK0, 29);
+
+	hws[CCAP1_MUX] = ma35d1_clk_mux(dev, "ccap1_mux", clk_base + REG_CLK_CLKSEL0, 14, 1,
+					ccap_sel_clks, ARRAY_SIZE(ccap_sel_clks));
+	hws[CCAP1_DIV] = ma35d1_clk_divider(dev, "ccap1_div", "ccap1_mux",
+					    clk_base + REG_CLK_CLKDIV1,
+					    12, 4);
+	hws[CCAP1_GATE] = ma35d1_clk_gate(dev, "ccap1_gate", "ccap1_div",
+					  clk_base + REG_CLK_SYSCLK0, 30);
+
+	hws[PDMA0_GATE] = ma35d1_clk_gate(dev, "pdma0_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 0);
+	hws[PDMA1_GATE] = ma35d1_clk_gate(dev, "pdma1_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 1);
+	hws[PDMA2_GATE] = ma35d1_clk_gate(dev, "pdma2_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 2);
+	hws[PDMA3_GATE] = ma35d1_clk_gate(dev, "pdma3_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 3);
+
+	hws[WH0_GATE] = ma35d1_clk_gate(dev, "wh0_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 4);
+	hws[WH1_GATE] = ma35d1_clk_gate(dev, "wh1_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 5);
+
+	hws[HWS_GATE] = ma35d1_clk_gate(dev, "hws_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 6);
+
+	hws[EBI_GATE] = ma35d1_clk_gate(dev, "ebi_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 7);
+
+	hws[SRAM0_GATE] = ma35d1_clk_gate(dev, "sram0_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 8);
+	hws[SRAM1_GATE] = ma35d1_clk_gate(dev, "sram1_gate", "hclk0",
+					  clk_base + REG_CLK_SYSCLK1, 9);
+
+	hws[ROM_GATE] = ma35d1_clk_gate(dev, "rom_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 10);
+
+	hws[TRA_GATE] = ma35d1_clk_gate(dev, "tra_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 11);
+
+	hws[DBG_MUX] = ma35d1_clk_mux(dev, "dbg_mux", clk_base + REG_CLK_CLKSEL0, 27, 1,
+				      dbg_sel_clks, ARRAY_SIZE(dbg_sel_clks));
+	hws[DBG_GATE] = ma35d1_clk_gate(dev, "dbg_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 12);
+
+	hws[CKO_MUX] = ma35d1_clk_mux(dev, "cko_mux", clk_base + REG_CLK_CLKSEL4, 24, 4,
+				      cko_sel_clks, ARRAY_SIZE(cko_sel_clks));
+	hws[CKO_DIV] = ma35d1_clk_divider_pow2(dev, "cko_div", "cko_mux",
+					       clk_base + REG_CLK_CLKOCTL, 0, 4);
+	hws[CKO_GATE] = ma35d1_clk_gate(dev, "cko_gate", "cko_div",
+					clk_base + REG_CLK_SYSCLK1, 13);
+
+	hws[GTMR_GATE] = ma35d1_clk_gate(dev, "gtmr_gate", "hirc",
+					 clk_base + REG_CLK_SYSCLK1, 14);
+
+	hws[GPA_GATE] = ma35d1_clk_gate(dev, "gpa_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 16);
+	hws[GPB_GATE] = ma35d1_clk_gate(dev, "gpb_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 17);
+	hws[GPC_GATE] = ma35d1_clk_gate(dev, "gpc_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 18);
+	hws[GPD_GATE] = ma35d1_clk_gate(dev, "gpd_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 19);
+	hws[GPE_GATE] = ma35d1_clk_gate(dev, "gpe_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 20);
+	hws[GPF_GATE] = ma35d1_clk_gate(dev, "gpf_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 21);
+	hws[GPG_GATE] = ma35d1_clk_gate(dev, "gpg_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 22);
+	hws[GPH_GATE] = ma35d1_clk_gate(dev, "gph_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 23);
+	hws[GPI_GATE] = ma35d1_clk_gate(dev, "gpi_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 24);
+	hws[GPJ_GATE] = ma35d1_clk_gate(dev, "gpj_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 25);
+	hws[GPK_GATE] = ma35d1_clk_gate(dev, "gpk_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 26);
+	hws[GPL_GATE] = ma35d1_clk_gate(dev, "gpl_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 27);
+	hws[GPM_GATE] = ma35d1_clk_gate(dev, "gpm_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 28);
+	hws[GPN_GATE] = ma35d1_clk_gate(dev, "gpn_gate", "hclk0",
+					clk_base + REG_CLK_SYSCLK1, 29);
+
+	hws[TMR0_MUX] = ma35d1_clk_mux(dev, "tmr0_mux", clk_base + REG_CLK_CLKSEL1, 0, 3,
+				       timer0_sel_clks, ARRAY_SIZE(timer0_sel_clks));
+	hws[TMR0_GATE] = ma35d1_clk_gate(dev, "tmr0_gate", "tmr0_mux",
+					 clk_base + REG_CLK_APBCLK0, 0);
+
+	hws[TMR1_MUX] = ma35d1_clk_mux(dev, "tmr1_mux", clk_base + REG_CLK_CLKSEL1, 4, 3,
+				       timer1_sel_clks, ARRAY_SIZE(timer1_sel_clks));
+	hws[TMR1_GATE] = ma35d1_clk_gate(dev, "tmr1_gate", "tmr1_mux",
+					 clk_base + REG_CLK_APBCLK0, 1);
+
+	hws[TMR2_MUX] = ma35d1_clk_mux(dev, "tmr2_mux", clk_base + REG_CLK_CLKSEL1, 8, 3,
+				       timer2_sel_clks, ARRAY_SIZE(timer2_sel_clks));
+	hws[TMR2_GATE] = ma35d1_clk_gate(dev, "tmr2_gate", "tmr2_mux",
+					 clk_base + REG_CLK_APBCLK0, 2);
+
+	hws[TMR3_MUX] = ma35d1_clk_mux(dev, "tmr3_mux", clk_base + REG_CLK_CLKSEL1, 12, 3,
+				       timer3_sel_clks, ARRAY_SIZE(timer3_sel_clks));
+	hws[TMR3_GATE] = ma35d1_clk_gate(dev, "tmr3_gate", "tmr3_mux",
+					 clk_base + REG_CLK_APBCLK0, 3);
+
+	hws[TMR4_MUX] = ma35d1_clk_mux(dev, "tmr4_mux", clk_base + REG_CLK_CLKSEL1, 16, 3,
+				       timer4_sel_clks, ARRAY_SIZE(timer4_sel_clks));
+	hws[TMR4_GATE] = ma35d1_clk_gate(dev, "tmr4_gate", "tmr4_mux",
+					 clk_base + REG_CLK_APBCLK0, 4);
+
+	hws[TMR5_MUX] = ma35d1_clk_mux(dev, "tmr5_mux", clk_base + REG_CLK_CLKSEL1, 20, 3,
+				       timer5_sel_clks, ARRAY_SIZE(timer5_sel_clks));
+	hws[TMR5_GATE] = ma35d1_clk_gate(dev, "tmr5_gate", "tmr5_mux",
+					 clk_base + REG_CLK_APBCLK0, 5);
+
+	hws[TMR6_MUX] = ma35d1_clk_mux(dev, "tmr6_mux", clk_base + REG_CLK_CLKSEL1, 24, 3,
+				       timer6_sel_clks, ARRAY_SIZE(timer6_sel_clks));
+	hws[TMR6_GATE] = ma35d1_clk_gate(dev, "tmr6_gate", "tmr6_mux",
+					 clk_base + REG_CLK_APBCLK0, 6);
+
+	hws[TMR7_MUX] = ma35d1_clk_mux(dev, "tmr7_mux", clk_base + REG_CLK_CLKSEL1, 28, 3,
+				       timer7_sel_clks, ARRAY_SIZE(timer7_sel_clks));
+	hws[TMR7_GATE] = ma35d1_clk_gate(dev, "tmr7_gate", "tmr7_mux",
+					 clk_base + REG_CLK_APBCLK0, 7);
+
+	hws[TMR8_MUX] = ma35d1_clk_mux(dev, "tmr8_mux", clk_base + REG_CLK_CLKSEL2, 0, 3,
+				       timer8_sel_clks, ARRAY_SIZE(timer8_sel_clks));
+	hws[TMR8_GATE] = ma35d1_clk_gate(dev, "tmr8_gate", "tmr8_mux",
+					 clk_base + REG_CLK_APBCLK0, 8);
+
+	hws[TMR9_MUX] = ma35d1_clk_mux(dev, "tmr9_mux", clk_base + REG_CLK_CLKSEL2, 4, 3,
+				       timer9_sel_clks, ARRAY_SIZE(timer9_sel_clks));
+	hws[TMR9_GATE] = ma35d1_clk_gate(dev, "tmr9_gate", "tmr9_mux",
+					 clk_base + REG_CLK_APBCLK0, 9);
+
+	hws[TMR10_MUX] = ma35d1_clk_mux(dev, "tmr10_mux", clk_base + REG_CLK_CLKSEL2, 8, 3,
+					timer10_sel_clks, ARRAY_SIZE(timer10_sel_clks));
+	hws[TMR10_GATE] = ma35d1_clk_gate(dev, "tmr10_gate", "tmr10_mux",
+					  clk_base + REG_CLK_APBCLK0, 10);
+
+	hws[TMR11_MUX] = ma35d1_clk_mux(dev, "tmr11_mux", clk_base + REG_CLK_CLKSEL2, 12, 3,
+					timer11_sel_clks, ARRAY_SIZE(timer11_sel_clks));
+	hws[TMR11_GATE] = ma35d1_clk_gate(dev, "tmr11_gate", "tmr11_mux",
+					  clk_base + REG_CLK_APBCLK0, 11);
+
+	hws[UART0_MUX] = ma35d1_clk_mux(dev, "uart0_mux", clk_base + REG_CLK_CLKSEL2, 16, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART0_DIV] = ma35d1_clk_divider(dev, "uart0_div", "uart0_mux",
+					    clk_base + REG_CLK_CLKDIV1, 16, 4);
+	hws[UART0_GATE] = ma35d1_clk_gate(dev, "uart0_gate", "uart0_div",
+					  clk_base + REG_CLK_APBCLK0, 12);
+
+	hws[UART1_MUX] = ma35d1_clk_mux(dev, "uart1_mux", clk_base + REG_CLK_CLKSEL2, 18, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART1_DIV] = ma35d1_clk_divider(dev, "uart1_div", "uart1_mux",
+					    clk_base + REG_CLK_CLKDIV1, 20, 4);
+	hws[UART1_GATE] = ma35d1_clk_gate(dev, "uart1_gate", "uart1_div",
+					  clk_base + REG_CLK_APBCLK0, 13);
+
+	hws[UART2_MUX] = ma35d1_clk_mux(dev, "uart2_mux", clk_base + REG_CLK_CLKSEL2, 20, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART2_DIV] = ma35d1_clk_divider(dev, "uart2_div", "uart2_mux",
+					    clk_base + REG_CLK_CLKDIV1, 24, 4);
+	hws[UART2_GATE] = ma35d1_clk_gate(dev, "uart2_gate", "uart2_div",
+					  clk_base + REG_CLK_APBCLK0, 14);
+
+	hws[UART3_MUX] = ma35d1_clk_mux(dev, "uart3_mux", clk_base + REG_CLK_CLKSEL2, 22, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART3_DIV] = ma35d1_clk_divider(dev, "uart3_div", "uart3_mux",
+					    clk_base + REG_CLK_CLKDIV1, 28, 4);
+	hws[UART3_GATE] = ma35d1_clk_gate(dev, "uart3_gate", "uart3_div",
+					  clk_base + REG_CLK_APBCLK0, 15);
+
+	hws[UART4_MUX] = ma35d1_clk_mux(dev, "uart4_mux", clk_base + REG_CLK_CLKSEL2, 24, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART4_DIV] = ma35d1_clk_divider(dev, "uart4_div", "uart4_mux",
+					    clk_base + REG_CLK_CLKDIV2, 0, 4);
+	hws[UART4_GATE] = ma35d1_clk_gate(dev, "uart4_gate", "uart4_div",
+					  clk_base + REG_CLK_APBCLK0, 16);
+
+	hws[UART5_MUX] = ma35d1_clk_mux(dev, "uart5_mux", clk_base + REG_CLK_CLKSEL2, 26, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART5_DIV] = ma35d1_clk_divider(dev, "uart5_div", "uart5_mux",
+					    clk_base + REG_CLK_CLKDIV2, 4, 4);
+	hws[UART5_GATE] = ma35d1_clk_gate(dev, "uart5_gate", "uart5_div",
+					  clk_base + REG_CLK_APBCLK0, 17);
+
+	hws[UART6_MUX] = ma35d1_clk_mux(dev, "uart6_mux", clk_base + REG_CLK_CLKSEL2, 28, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART6_DIV] = ma35d1_clk_divider(dev, "uart6_div", "uart6_mux",
+					    clk_base + REG_CLK_CLKDIV2, 8, 4);
+	hws[UART6_GATE] = ma35d1_clk_gate(dev, "uart6_gate", "uart6_div",
+					  clk_base + REG_CLK_APBCLK0, 18);
+
+	hws[UART7_MUX] = ma35d1_clk_mux(dev, "uart7_mux", clk_base + REG_CLK_CLKSEL2, 30, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART7_DIV] = ma35d1_clk_divider(dev, "uart7_div", "uart7_mux",
+					    clk_base + REG_CLK_CLKDIV2, 12, 4);
+	hws[UART7_GATE] = ma35d1_clk_gate(dev, "uart7_gate", "uart7_div",
+					  clk_base + REG_CLK_APBCLK0, 19);
+
+	hws[UART8_MUX] = ma35d1_clk_mux(dev, "uart8_mux", clk_base + REG_CLK_CLKSEL3, 0, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART8_DIV] = ma35d1_clk_divider(dev, "uart8_div", "uart8_mux",
+					    clk_base + REG_CLK_CLKDIV2, 16, 4);
+	hws[UART8_GATE] = ma35d1_clk_gate(dev, "uart8_gate", "uart8_div",
+					  clk_base + REG_CLK_APBCLK0, 20);
+
+	hws[UART9_MUX] = ma35d1_clk_mux(dev, "uart9_mux", clk_base + REG_CLK_CLKSEL3, 2, 1,
+					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART9_DIV] = ma35d1_clk_divider(dev, "uart9_div", "uart9_mux",
+					    clk_base + REG_CLK_CLKDIV2, 20, 4);
+	hws[UART9_GATE] = ma35d1_clk_gate(dev, "uart9_gate", "uart9_div",
+					  clk_base + REG_CLK_APBCLK0, 21);
+
+	hws[UART10_MUX] = ma35d1_clk_mux(dev, "uart10_mux", clk_base + REG_CLK_CLKSEL3, 4, 1,
+					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART10_DIV] = ma35d1_clk_divider(dev, "uart10_div", "uart10_mux",
+					     clk_base + REG_CLK_CLKDIV2, 24, 4);
+	hws[UART10_GATE] = ma35d1_clk_gate(dev, "uart10_gate", "uart10_div",
+					   clk_base + REG_CLK_APBCLK0, 22);
+
+	hws[UART11_MUX] = ma35d1_clk_mux(dev, "uart11_mux", clk_base + REG_CLK_CLKSEL3, 6, 1,
+					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART11_DIV] = ma35d1_clk_divider(dev, "uart11_div", "uart11_mux",
+					     clk_base + REG_CLK_CLKDIV2, 28, 4);
+	hws[UART11_GATE] = ma35d1_clk_gate(dev, "uart11_gate", "uart11_div",
+					   clk_base + REG_CLK_APBCLK0, 23);
+
+	hws[UART12_MUX] = ma35d1_clk_mux(dev, "uart12_mux", clk_base + REG_CLK_CLKSEL3, 8, 1,
+					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART12_DIV] = ma35d1_clk_divider(dev, "uart12_div", "uart12_mux",
+					     clk_base + REG_CLK_CLKDIV3, 0, 4);
+	hws[UART12_GATE] = ma35d1_clk_gate(dev, "uart12_gate", "uart12_div",
+					   clk_base + REG_CLK_APBCLK0, 24);
+
+	hws[UART13_MUX] = ma35d1_clk_mux(dev, "uart13_mux", clk_base + REG_CLK_CLKSEL3, 10, 1,
+					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART13_DIV] = ma35d1_clk_divider(dev, "uart13_div", "uart13_mux",
+					     clk_base + REG_CLK_CLKDIV3, 4, 4);
+	hws[UART13_GATE] = ma35d1_clk_gate(dev, "uart13_gate", "uart13_div",
+					   clk_base + REG_CLK_APBCLK0, 25);
+
+	hws[UART14_MUX] = ma35d1_clk_mux(dev, "uart14_mux", clk_base + REG_CLK_CLKSEL3, 12, 1,
+					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART14_DIV] = ma35d1_clk_divider(dev, "uart14_div", "uart14_mux",
+					     clk_base + REG_CLK_CLKDIV3, 8, 4);
+	hws[UART14_GATE] = ma35d1_clk_gate(dev, "uart14_gate", "uart14_div",
+					   clk_base + REG_CLK_APBCLK0, 26);
+
+	hws[UART15_MUX] = ma35d1_clk_mux(dev, "uart15_mux", clk_base + REG_CLK_CLKSEL3, 14, 1,
+					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART15_DIV] = ma35d1_clk_divider(dev, "uart15_div", "uart15_mux",
+					     clk_base + REG_CLK_CLKDIV3, 12, 4);
+	hws[UART15_GATE] = ma35d1_clk_gate(dev, "uart15_gate", "uart15_div",
+					   clk_base + REG_CLK_APBCLK0, 27);
+
+	hws[UART16_MUX] = ma35d1_clk_mux(dev, "uart16_mux", clk_base + REG_CLK_CLKSEL3, 16, 1,
+					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
+	hws[UART16_DIV] = ma35d1_clk_divider(dev, "uart16_div", "uart16_mux",
+					     clk_base + REG_CLK_CLKDIV3, 16, 4);
+	hws[UART16_GATE] = ma35d1_clk_gate(dev, "uart16_gate", "uart16_div",
+					   clk_base + REG_CLK_APBCLK0, 28);
+
+	hws[RTC_GATE] = ma35d1_clk_gate(dev, "rtc_gate", "lxt",
+					clk_base + REG_CLK_APBCLK0, 29);
+	hws[DDR_GATE] = ma35d1_clk_gate(dev, "ddr_gate", "ddrpll",
+					clk_base + REG_CLK_APBCLK0, 30);
+
+	hws[KPI_MUX] = ma35d1_clk_mux(dev, "kpi_mux", clk_base + REG_CLK_CLKSEL4, 30, 1,
+				      kpi_sel_clks, ARRAY_SIZE(kpi_sel_clks));
+	hws[KPI_DIV] = ma35d1_clk_divider(dev, "kpi_div", "kpi_mux",
+					  clk_base + REG_CLK_CLKDIV4, 24, 8);
+	hws[KPI_GATE] = ma35d1_clk_gate(dev, "kpi_gate", "kpi_div",
+					clk_base + REG_CLK_APBCLK0, 31);
+
+	hws[I2C0_GATE] = ma35d1_clk_gate(dev, "i2c0_gate", "pclk0",
+					 clk_base + REG_CLK_APBCLK1, 0);
+	hws[I2C1_GATE] = ma35d1_clk_gate(dev, "i2c1_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK1, 1);
+	hws[I2C2_GATE] = ma35d1_clk_gate(dev, "i2c2_gate", "pclk2",
+					 clk_base + REG_CLK_APBCLK1, 2);
+	hws[I2C3_GATE] = ma35d1_clk_gate(dev, "i2c3_gate", "pclk0",
+					 clk_base + REG_CLK_APBCLK1, 3);
+	hws[I2C4_GATE] = ma35d1_clk_gate(dev, "i2c4_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK1, 4);
+	hws[I2C5_GATE] = ma35d1_clk_gate(dev, "i2c5_gate", "pclk2",
+					 clk_base + REG_CLK_APBCLK1, 5);
+
+	hws[QSPI0_MUX] = ma35d1_clk_mux(dev, "qspi0_mux", clk_base + REG_CLK_CLKSEL4, 8, 1,
+					qspi_sel_clks, ARRAY_SIZE(qspi_sel_clks));
+	hws[QSPI0_GATE] = ma35d1_clk_gate(dev, "qspi0_gate", "qspi0_mux",
+					  clk_base + REG_CLK_APBCLK1, 6);
+
+	hws[QSPI1_MUX] = ma35d1_clk_mux(dev, "qspi1_mux", clk_base + REG_CLK_CLKSEL4, 10, 1,
+					qspi_sel_clks, ARRAY_SIZE(qspi_sel_clks));
+	hws[QSPI1_GATE] = ma35d1_clk_gate(dev, "qspi1_gate", "qspi1_mux",
+					  clk_base + REG_CLK_APBCLK1, 7);
+
+	hws[SMC0_MUX] = ma35d1_clk_mux(dev, "smc0_mux", clk_base + REG_CLK_CLKSEL4, 28, 1,
+				       smc_sel_clks, ARRAY_SIZE(smc_sel_clks));
+	hws[SMC0_DIV] = ma35d1_clk_divider(dev, "smc0_div", "smc0_mux",
+					   clk_base + REG_CLK_CLKDIV1, 0, 4);
+	hws[SMC0_GATE] = ma35d1_clk_gate(dev, "smc0_gate", "smc0_div",
+					 clk_base + REG_CLK_APBCLK1, 12);
+
+	hws[SMC1_MUX] = ma35d1_clk_mux(dev, "smc1_mux", clk_base + REG_CLK_CLKSEL4, 29, 1,
+				       smc_sel_clks, ARRAY_SIZE(smc_sel_clks));
+	hws[SMC1_DIV] = ma35d1_clk_divider(dev, "smc1_div", "smc1_mux",
+					   clk_base + REG_CLK_CLKDIV1, 4, 4);
+	hws[SMC1_GATE] = ma35d1_clk_gate(dev, "smc1_gate", "smc1_div",
+					 clk_base + REG_CLK_APBCLK1, 13);
+
+	hws[WDT0_MUX] = ma35d1_clk_mux(dev, "wdt0_mux", clk_base + REG_CLK_CLKSEL3, 20, 2,
+				       wdt0_sel_clks, ARRAY_SIZE(wdt0_sel_clks));
+	hws[WDT0_GATE] = ma35d1_clk_gate(dev, "wdt0_gate", "wdt0_mux",
+					 clk_base + REG_CLK_APBCLK1, 16);
+
+	hws[WDT1_MUX] = ma35d1_clk_mux(dev, "wdt1_mux", clk_base + REG_CLK_CLKSEL3, 24, 2,
+				       wdt1_sel_clks, ARRAY_SIZE(wdt1_sel_clks));
+	hws[WDT1_GATE] = ma35d1_clk_gate(dev, "wdt1_gate", "wdt1_mux",
+					 clk_base + REG_CLK_APBCLK1, 17);
+
+	hws[WDT2_MUX] = ma35d1_clk_mux(dev, "wdt2_mux", clk_base + REG_CLK_CLKSEL3, 28, 2,
+				       wdt2_sel_clks, ARRAY_SIZE(wdt2_sel_clks));
+	hws[WDT2_GATE] = ma35d1_clk_gate(dev, "wdt2_gate", "wdt2_mux",
+				       clk_base + REG_CLK_APBCLK1, 18);
+
+	hws[WWDT0_MUX] = ma35d1_clk_mux(dev, "wwdt0_mux", clk_base + REG_CLK_CLKSEL3, 22, 2,
+					wwdt0_sel_clks, ARRAY_SIZE(wwdt0_sel_clks));
+
+	hws[WWDT1_MUX] = ma35d1_clk_mux(dev, "wwdt1_mux", clk_base + REG_CLK_CLKSEL3, 26, 2,
+					wwdt1_sel_clks, ARRAY_SIZE(wwdt1_sel_clks));
+
+	hws[WWDT2_MUX] = ma35d1_clk_mux(dev, "wwdt2_mux", clk_base + REG_CLK_CLKSEL3, 30, 2,
+					wwdt2_sel_clks, ARRAY_SIZE(wwdt2_sel_clks));
+
+	hws[EPWM0_GATE] = ma35d1_clk_gate(dev, "epwm0_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK1, 24);
+	hws[EPWM1_GATE] = ma35d1_clk_gate(dev, "epwm1_gate", "pclk2",
+					  clk_base + REG_CLK_APBCLK1, 25);
+	hws[EPWM2_GATE] = ma35d1_clk_gate(dev, "epwm2_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK1, 26);
+
+	hws[I2S0_MUX] = ma35d1_clk_mux(dev, "i2s0_mux", clk_base + REG_CLK_CLKSEL4, 12, 1,
+				       i2s_sel_clks, ARRAY_SIZE(i2s_sel_clks));
+	hws[I2S0_GATE] = ma35d1_clk_gate(dev, "i2s0_gate", "i2s0_mux",
+					 clk_base + REG_CLK_APBCLK2, 0);
+
+	hws[I2S1_MUX] = ma35d1_clk_mux(dev, "i2s1_mux", clk_base + REG_CLK_CLKSEL4, 14, 1,
+				       i2s_sel_clks, ARRAY_SIZE(i2s_sel_clks));
+	hws[I2S1_GATE] = ma35d1_clk_gate(dev, "i2s1_gate", "i2s1_mux",
+					 clk_base + REG_CLK_APBCLK2, 1);
+
+	hws[SSMCC_GATE] = ma35d1_clk_gate(dev, "ssmcc_gate", "pclk3",
+					  clk_base + REG_CLK_APBCLK2, 2);
+	hws[SSPCC_GATE] = ma35d1_clk_gate(dev, "sspcc_gate", "pclk3",
+					  clk_base + REG_CLK_APBCLK2, 3);
+
+	hws[SPI0_MUX] = ma35d1_clk_mux(dev, "spi0_mux", clk_base + REG_CLK_CLKSEL4, 0, 1,
+				       spi0_sel_clks, ARRAY_SIZE(spi0_sel_clks));
+	hws[SPI0_GATE] = ma35d1_clk_gate(dev, "spi0_gate", "spi0_mux",
+					 clk_base + REG_CLK_APBCLK2, 4);
+
+	hws[SPI1_MUX] = ma35d1_clk_mux(dev, "spi1_mux", clk_base + REG_CLK_CLKSEL4, 2, 1,
+				       spi1_sel_clks, ARRAY_SIZE(spi1_sel_clks));
+	hws[SPI1_GATE] = ma35d1_clk_gate(dev, "spi1_gate", "spi1_mux",
+					 clk_base + REG_CLK_APBCLK2, 5);
+
+	hws[SPI2_MUX] = ma35d1_clk_mux(dev, "spi2_mux", clk_base + REG_CLK_CLKSEL4, 4, 1,
+				       spi2_sel_clks, ARRAY_SIZE(spi2_sel_clks));
+	hws[SPI2_GATE] = ma35d1_clk_gate(dev, "spi2_gate", "spi2_mux",
+					 clk_base + REG_CLK_APBCLK2, 6);
+
+	hws[SPI3_MUX] = ma35d1_clk_mux(dev, "spi3_mux", clk_base + REG_CLK_CLKSEL4, 6, 1,
+				       spi3_sel_clks, ARRAY_SIZE(spi3_sel_clks));
+	hws[SPI3_GATE] = ma35d1_clk_gate(dev, "spi3_gate", "spi3_mux",
+					 clk_base + REG_CLK_APBCLK2, 7);
+
+	hws[ECAP0_GATE] = ma35d1_clk_gate(dev, "ecap0_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK2, 8);
+	hws[ECAP1_GATE] = ma35d1_clk_gate(dev, "ecap1_gate", "pclk2",
+					  clk_base + REG_CLK_APBCLK2, 9);
+	hws[ECAP2_GATE] = ma35d1_clk_gate(dev, "ecap2_gate", "pclk1",
+					  clk_base + REG_CLK_APBCLK2, 10);
+
+	hws[QEI0_GATE] = ma35d1_clk_gate(dev, "qei0_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK2, 12);
+	hws[QEI1_GATE] = ma35d1_clk_gate(dev, "qei1_gate", "pclk2",
+					 clk_base + REG_CLK_APBCLK2, 13);
+	hws[QEI2_GATE] = ma35d1_clk_gate(dev, "qei2_gate", "pclk1",
+					 clk_base + REG_CLK_APBCLK2, 14);
+
+	hws[ADC_DIV] = ma35d1_reg_adc_clkdiv(dev, "adc_div", hws[PCLK0], &ma35d1_lock, 0,
+					     clk_base + REG_CLK_CLKDIV4, 4, 17, 0x1ffff);
+	hws[ADC_GATE] = ma35d1_clk_gate(dev, "adc_gate", "adc_div",
+					clk_base + REG_CLK_APBCLK2, 24);
+
+	hws[EADC_DIV] = ma35d1_clk_divider_table(dev, "eadc_div", "pclk2",
+						 clk_base + REG_CLK_CLKDIV4,
+						 0, 4, eadc_div_table);
+	hws[EADC_GATE] = ma35d1_clk_gate(dev, "eadc_gate", "eadc_div",
+					 clk_base + REG_CLK_APBCLK2, 25);
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, ma35d1_hw_data);
+}
+
+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");
diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
new file mode 100644
index 000000000000..3adee440f60a
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-ma35d1.h
@@ -0,0 +1,18 @@
+/* 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
+
+struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,
+				  struct clk_hw *parent_hw, void __iomem *base);
+
+struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
+				     struct clk_hw *parent_hw, spinlock_t *lock,
+				     unsigned long flags, void __iomem *reg,
+				     u8 shift, u8 width, u32 mask_bit);
+
+#endif /* __DRV_CLK_NUVOTON_MA35D1_H */
-- 
2.34.1


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

* Re: [PATCH 2/2] clk: nuvoton: Add clock driver for ma35d1 clock controller
  2023-06-19  3:23 ` [PATCH 2/2] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
@ 2023-06-19  3:34   ` Jacky Huang
  0 siblings, 0 replies; 4+ messages in thread
From: Jacky Huang @ 2023-06-19  3:34 UTC (permalink / raw)
  To: robh+dt, krzysztof.kozlowski+dt, lee, mturquette, sboyd, p.zabel,
	gregkh, jirislaby, tmaimon77, catalin.marinas, will
  Cc: devicetree, linux-clk, linux-kernel, linux-arm-kernel,
	linux-serial, arnd, soc, schung, mjchen, Jacky Huang,
	Krzysztof Kozlowski


I am sorry for forgetting to add v15 patch header. Please ignore this one.


On 2023/6/19 上午 11:23, 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>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> ---
>   drivers/clk/Kconfig                      |    1 +
>   drivers/clk/Makefile                     |    1 +
>   drivers/clk/nuvoton/Kconfig              |   19 +
>   drivers/clk/nuvoton/Makefile             |    4 +
>   drivers/clk/nuvoton/clk-ma35d1-divider.c |  132 +++
>   drivers/clk/nuvoton/clk-ma35d1-pll.c     |  360 ++++++++
>   drivers/clk/nuvoton/clk-ma35d1.c         | 1046 ++++++++++++++++++++++
>   drivers/clk/nuvoton/clk-ma35d1.h         |   18 +
>   8 files changed, 1581 insertions(+)
>   create mode 100644 drivers/clk/nuvoton/Kconfig
>   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/Kconfig b/drivers/clk/Kconfig
> index 016814e15536..db789055e0fd 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -478,6 +478,7 @@ source "drivers/clk/meson/Kconfig"
>   source "drivers/clk/mstar/Kconfig"
>   source "drivers/clk/microchip/Kconfig"
>   source "drivers/clk/mvebu/Kconfig"
> +source "drivers/clk/nuvoton/Kconfig"
>   source "drivers/clk/pistachio/Kconfig"
>   source "drivers/clk/qcom/Kconfig"
>   source "drivers/clk/ralink/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 0aebef17edc6..7cb000549b61 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -106,6 +106,7 @@ endif
>   obj-y					+= mstar/
>   obj-y					+= mvebu/
>   obj-$(CONFIG_ARCH_MXS)			+= mxs/
> +obj-$(CONFIG_ARCH_MA35)			+= 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/Kconfig b/drivers/clk/nuvoton/Kconfig
> new file mode 100644
> index 000000000000..fe4b7f62f467
> --- /dev/null
> +++ b/drivers/clk/nuvoton/Kconfig
> @@ -0,0 +1,19 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# common clock support for Nuvoton SoC family.
> +
> +config COMMON_CLK_NUVOTON
> +	bool "Nuvoton clock controller common support"
> +	depends on ARCH_MA35 || COMPILE_TEST
> +	default y
> +	help
> +	  Say y here to enable common clock controller for Nuvoton platforms.
> +
> +if COMMON_CLK_NUVOTON
> +
> +config CLK_MA35D1
> +	bool "Nuvoton MA35D1 clock controller support"
> +	default y
> +	help
> +	  Build the clock controller driver for MA35D1 SoC.
> +
> +endif
> diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile
> new file mode 100644
> index 000000000000..c3c59dd9f2aa
> --- /dev/null
> +++ b/drivers/clk/nuvoton/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1.o
> +obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1-divider.o
> +obj-$(CONFIG_CLK_MA35D1) += 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..bb8c23d2b895
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1-divider.c
> @@ -0,0 +1,132 @@
> +// 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/device.h>
> +#include <linux/regmap.h>
> +#include <linux/spinlock.h>
> +
> +#include "clk-ma35d1.h"
> +
> +struct ma35d1_adc_clk_div {
> +	struct clk_hw hw;
> +	void __iomem *reg;
> +	u8 shift;
> +	u8 width;
> +	u32 mask;
> +	const struct clk_div_table *table;
> +	/* protects concurrent access to clock divider registers */
> +	spinlock_t *lock;
> +};
> +
> +static inline struct ma35d1_adc_clk_div *to_ma35d1_adc_clk_div(struct clk_hw *_hw)
> +{
> +	return container_of(_hw, struct ma35d1_adc_clk_div, hw);
> +}
> +
> +static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> +	unsigned int val;
> +	struct ma35d1_adc_clk_div *dclk = to_ma35d1_adc_clk_div(hw);
> +
> +	val = readl_relaxed(dclk->reg) >> dclk->shift;
> +	val &= clk_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_div *dclk = to_ma35d1_adc_clk_div(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_div *dclk = to_ma35d1_adc_clk_div(hw);
> +
> +	value = divider_get_val(rate, parent_rate, dclk->table,
> +				dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
> +
> +	spin_lock_irqsave(dclk->lock, flags);
> +
> +	data = readl_relaxed(dclk->reg);
> +	data &= ~(clk_div_mask(dclk->width) << dclk->shift);
> +	data |= (value - 1) << dclk->shift;
> +	data |= dclk->mask;
> +	writel_relaxed(data, dclk->reg);
> +
> +	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,
> +				     struct clk_hw *parent_hw, spinlock_t *lock,
> +				     unsigned long flags, void __iomem *reg,
> +				     u8 shift, u8 width, u32 mask_bit)
> +{
> +	struct ma35d1_adc_clk_div *div;
> +	struct clk_init_data init;
> +	struct clk_div_table *table;
> +	struct clk_parent_data pdata = { .index = 0 };
> +	u32 max_div, min_div;
> +	struct clk_hw *hw;
> +	int ret;
> +	int i;
> +
> +	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
> +	if (!div)
> +		return ERR_PTR(-ENOMEM);
> +
> +	max_div = clk_div_mask(width) + 1;
> +	min_div = 1;
> +
> +	table = devm_kcalloc(dev, max_div + 1, sizeof(*table), GFP_KERNEL);
> +	if (!table)
> +		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;
> +
> +	memset(&init, 0, sizeof(init));
> +	init.name = name;
> +	init.ops = &ma35d1_adc_clkdiv_ops;
> +	init.flags |= flags;
> +	pdata.hw = parent_hw;
> +	init.parent_data = &pdata;
> +	init.num_parents = 1;
> +
> +	div->reg = reg;
> +	div->shift = shift;
> +	div->width = width;
> +	div->mask = mask_bit ? BIT(mask_bit) : 0;
> +	div->lock = lock;
> +	div->hw.init = &init;
> +	div->table = table;
> +
> +	hw = &div->hw;
> +	ret = devm_clk_hw_register(dev, hw);
> +	if (ret)
> +		return ERR_PTR(ret);
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(ma35d1_reg_adc_clkdiv);
> diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> new file mode 100644
> index 000000000000..ff3fb8b87c24
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
> @@ -0,0 +1,360 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023 Nuvoton Technology Corp.
> + * Author: Chi-Fang Li <cfli0@nuvoton.com>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk-provider.h>
> +#include <linux/container_of.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/math64.h>
> +#include <linux/slab.h>
> +#include <linux/units.h>
> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> +
> +#include "clk-ma35d1.h"
> +
> +/* PLL frequency limits */
> +#define PLL_FREF_MAX_FREQ	(200 * HZ_PER_MHZ)
> +#define PLL_FREF_MIN_FREQ	(1 * HZ_PER_MHZ)
> +#define PLL_FREF_M_MAX_FREQ	(40 * HZ_PER_MHZ)
> +#define PLL_FREF_M_MIN_FREQ	(10 * HZ_PER_MHZ)
> +#define PLL_FCLK_MAX_FREQ	(2400 * HZ_PER_MHZ)
> +#define PLL_FCLK_MIN_FREQ	(600 * HZ_PER_MHZ)
> +#define PLL_FCLKO_MAX_FREQ	(2400 * HZ_PER_MHZ)
> +#define PLL_FCLKO_MIN_FREQ	(85700 * HZ_PER_KHZ)
> +#define PLL_SS_RATE		0x77
> +#define PLL_SLOPE		0x58CFA
> +
> +#define REG_PLL_CTL0_OFFSET	0x0
> +#define REG_PLL_CTL1_OFFSET	0x4
> +#define REG_PLL_CTL2_OFFSET	0x8
> +
> +/* bit fields for REG_CLK_PLL0CTL0, which is SMIC PLL design */
> +#define SPLL0_CTL0_FBDIV	GENMASK(7, 0)
> +#define SPLL0_CTL0_INDIV	GENMASK(11, 8)
> +#define SPLL0_CTL0_OUTDIV	GENMASK(13, 12)
> +#define SPLL0_CTL0_PD		BIT(16)
> +#define SPLL0_CTL0_BP		BIT(17)
> +
> +/* bit fields for REG_CLK_PLLxCTL0 ~ REG_CLK_PLLxCTL2, where x = 2 ~ 5 */
> +#define PLL_CTL0_FBDIV		GENMASK(10, 0)
> +#define PLL_CTL0_INDIV		GENMASK(17, 12)
> +#define PLL_CTL0_MODE		GENMASK(19, 18)
> +#define PLL_CTL0_SSRATE		GENMASK(30, 20)
> +#define PLL_CTL1_PD		BIT(0)
> +#define PLL_CTL1_BP		BIT(1)
> +#define PLL_CTL1_OUTDIV		GENMASK(6, 4)
> +#define PLL_CTL1_FRAC		GENMASK(31, 24)
> +#define PLL_CTL2_SLOPE		GENMASK(23, 0)
> +
> +#define INDIV_MIN		1
> +#define INDIV_MAX		63
> +#define FBDIV_MIN		16
> +#define FBDIV_MAX		2047
> +#define FBDIV_FRAC_MIN		1600
> +#define FBDIV_FRAC_MAX		204700
> +#define OUTDIV_MIN		1
> +#define OUTDIV_MAX		7
> +
> +#define PLL_MODE_INT            0
> +#define PLL_MODE_FRAC           1
> +#define PLL_MODE_SS             2
> +
> +struct ma35d1_clk_pll {
> +	struct clk_hw hw;
> +	u32 id;
> +	u8 mode;
> +	void __iomem *ctl0_base;
> +	void __iomem *ctl1_base;
> +	void __iomem *ctl2_base;
> +};
> +
> +static inline struct ma35d1_clk_pll *to_ma35d1_clk_pll(struct clk_hw *_hw)
> +{
> +	return container_of(_hw, struct ma35d1_clk_pll, hw);
> +}
> +
> +static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,
> +					       unsigned long parent_rate)
> +{
> +	u32 m, n, p, outdiv;
> +	u64 pll_freq;
> +
> +	if (pll0_ctl0 & SPLL0_CTL0_BP)
> +		return parent_rate;
> +
> +	n = FIELD_GET(SPLL0_CTL0_FBDIV, pll0_ctl0);
> +	m = FIELD_GET(SPLL0_CTL0_INDIV, pll0_ctl0);
> +	p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);
> +	outdiv = 1 << p;
> +	pll_freq = (u64)parent_rate * n;
> +	div_u64(pll_freq, m * outdiv);
> +	return pll_freq;
> +}
> +
> +static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long parent_rate)
> +{
> +	unsigned long pll_freq, x;
> +	u32 m, n, p;
> +
> +	if (reg_ctl[1] & PLL_CTL1_BP)
> +		return parent_rate;
> +
> +	n = FIELD_GET(PLL_CTL0_FBDIV, reg_ctl[0]);
> +	m = FIELD_GET(PLL_CTL0_INDIV, reg_ctl[0]);
> +	p = FIELD_GET(PLL_CTL1_OUTDIV, reg_ctl[1]);
> +
> +	if (mode == PLL_MODE_INT) {
> +		pll_freq = (u64)parent_rate * n;
> +		div_u64(pll_freq, m * p);
> +	} else {
> +		x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
> +		/* 2 decimal places floating to integer (ex. 1.23 to 123) */
> +		n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));
> +		pll_freq = div_u64(parent_rate * n, 100 * m * p);
> +	}
> +	return pll_freq;
> +}
> +
> +static int ma35d1_pll_find_closest(struct ma35d1_clk_pll *pll, unsigned long rate,
> +				   unsigned long parent_rate, u32 *reg_ctl,
> +				   unsigned long *freq)
> +{
> +	unsigned long min_diff = ULONG_MAX;
> +	int fbdiv_min, fbdiv_max;
> +	int p, m, n;
> +
> +	*freq = 0;
> +	if (rate < PLL_FCLKO_MIN_FREQ || rate > PLL_FCLKO_MAX_FREQ)
> +		return -EINVAL;
> +
> +	if (pll->mode == PLL_MODE_INT) {
> +		fbdiv_min = FBDIV_MIN;
> +		fbdiv_max = FBDIV_MAX;
> +	} else {
> +		fbdiv_min = FBDIV_FRAC_MIN;
> +		fbdiv_max = FBDIV_FRAC_MAX;
> +	}
> +
> +	for (m = INDIV_MIN; m <= INDIV_MAX; m++) {
> +		for (n = fbdiv_min; n <= fbdiv_max; n++) {
> +			for (p = OUTDIV_MIN; p <= OUTDIV_MAX; p++) {
> +				unsigned long tmp, fout, fclk, diff;
> +
> +				tmp = div_u64(parent_rate, m);
> +				if (tmp < PLL_FREF_M_MIN_FREQ ||
> +				    tmp > PLL_FREF_M_MAX_FREQ)
> +					continue; /* constrain */
> +
> +				fclk = div_u64(parent_rate * n, m);
> +				/* for 2 decimal places */
> +				if (pll->mode != PLL_MODE_INT)
> +					fclk = div_u64(fclk, 100);
> +
> +				if (fclk < PLL_FCLK_MIN_FREQ ||
> +				    fclk > PLL_FCLK_MAX_FREQ)
> +					continue; /* constrain */
> +
> +				fout = div_u64(fclk, p);
> +				if (fout < PLL_FCLKO_MIN_FREQ ||
> +				    fout > PLL_FCLKO_MAX_FREQ)
> +					continue; /* constrain */
> +
> +				diff = abs(rate - fout);
> +				if (diff < min_diff) {
> +					reg_ctl[0] = FIELD_PREP(PLL_CTL0_INDIV, m) |
> +						     FIELD_PREP(PLL_CTL0_FBDIV, n);
> +					reg_ctl[1] = FIELD_PREP(PLL_CTL1_OUTDIV, p);
> +					*freq = fout;
> +					min_diff = diff;
> +					if (min_diff == 0)
> +						break;
> +				}
> +			}
> +		}
> +	}
> +	if (*freq == 0)
> +		return -EINVAL; /* cannot find even one valid setting */
> +	return 0;
> +}
> +
> +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);
> +	u32 reg_ctl[3] = { 0 };
> +	unsigned long pll_freq;
> +	int ret;
> +
> +	if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)
> +		return -EINVAL;
> +
> +	ret = ma35d1_pll_find_closest(pll, rate, parent_rate, reg_ctl, &pll_freq);
> +	if (ret != 0)
> +		return ret;
> +
> +	switch (pll->mode) {
> +	case PLL_MODE_INT:
> +		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_INT);
> +		break;
> +	case PLL_MODE_FRAC:
> +		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_FRAC);
> +		break;
> +	case PLL_MODE_SS:
> +		reg_ctl[0] |= FIELD_PREP(PLL_CTL0_MODE, PLL_MODE_SS) |
> +			      FIELD_PREP(PLL_CTL0_SSRATE, PLL_SS_RATE);
> +		reg_ctl[2] = FIELD_PREP(PLL_CTL2_SLOPE, PLL_SLOPE);
> +		break;
> +	}
> +	reg_ctl[1] |= PLL_CTL1_PD;
> +
> +	writel_relaxed(reg_ctl[0], pll->ctl0_base);
> +	writel_relaxed(reg_ctl[1], pll->ctl1_base);
> +	writel_relaxed(reg_ctl[2], pll->ctl2_base);
> +	return 0;
> +}
> +
> +static unsigned long ma35d1_clk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 reg_ctl[3];
> +	unsigned long pll_freq;
> +
> +	if (parent_rate < PLL_FREF_MIN_FREQ || parent_rate > PLL_FREF_MAX_FREQ)
> +		return 0;
> +
> +	switch (pll->id) {
> +	case CAPLL:
> +		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
> +		pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], parent_rate);
> +		return pll_freq;
> +	case DDRPLL:
> +	case APLL:
> +	case EPLL:
> +	case VPLL:
> +		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
> +		reg_ctl[1] = readl_relaxed(pll->ctl1_base);
> +		pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, parent_rate);
> +		return pll_freq;
> +	}
> +	return 0;
> +}
> +
> +static long ma35d1_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long *parent_rate)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 reg_ctl[3] = { 0 };
> +	unsigned long pll_freq;
> +	long ret;
> +
> +	if (*parent_rate < PLL_FREF_MIN_FREQ || *parent_rate > PLL_FREF_MAX_FREQ)
> +		return -EINVAL;
> +
> +	ret = ma35d1_pll_find_closest(pll, rate, *parent_rate, reg_ctl, &pll_freq);
> +	if (ret < 0)
> +		return ret;
> +
> +	switch (pll->id) {
> +	case CAPLL:
> +		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
> +		pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], *parent_rate);
> +		return pll_freq;
> +	case DDRPLL:
> +	case APLL:
> +	case EPLL:
> +	case VPLL:
> +		reg_ctl[0] = readl_relaxed(pll->ctl0_base);
> +		reg_ctl[1] = readl_relaxed(pll->ctl1_base);
> +		pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, *parent_rate);
> +		return pll_freq;
> +	}
> +	return 0;
> +}
> +
> +static int ma35d1_clk_pll_is_prepared(struct clk_hw *hw)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 val = readl_relaxed(pll->ctl1_base);
> +
> +	return !(val & PLL_CTL1_PD);
> +}
> +
> +static int ma35d1_clk_pll_prepare(struct clk_hw *hw)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 val;
> +
> +	val = readl_relaxed(pll->ctl1_base);
> +	val &= ~PLL_CTL1_PD;
> +	writel_relaxed(val, pll->ctl1_base);
> +	return 0;
> +}
> +
> +static void ma35d1_clk_pll_unprepare(struct clk_hw *hw)
> +{
> +	struct ma35d1_clk_pll *pll = to_ma35d1_clk_pll(hw);
> +	u32 val;
> +
> +	val = readl_relaxed(pll->ctl1_base);
> +	val |= PLL_CTL1_PD;
> +	writel_relaxed(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,
> +};
> +
> +static const struct clk_ops ma35d1_clk_fixed_pll_ops = {
> +	.recalc_rate = ma35d1_clk_pll_recalc_rate,
> +	.round_rate = ma35d1_clk_pll_round_rate,
> +};
> +
> +struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,
> +				  struct clk_hw *parent_hw, void __iomem *base)
> +{
> +	struct clk_parent_data pdata = { .index = 0 };
> +	struct clk_init_data init = {};
> +	struct ma35d1_clk_pll *pll;
> +	struct clk_hw *hw;
> +	int ret;
> +
> +	pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
> +	if (!pll)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pll->id = id;
> +	pll->mode = u8mode;
> +	pll->ctl0_base = base + REG_PLL_CTL0_OFFSET;
> +	pll->ctl1_base = base + REG_PLL_CTL1_OFFSET;
> +	pll->ctl2_base = base + REG_PLL_CTL2_OFFSET;
> +
> +	init.name = name;
> +	init.flags = 0;
> +	pdata.hw = parent_hw;
> +	init.parent_data = &pdata;
> +	init.num_parents = 1;
> +
> +	if (id == CAPLL || id == DDRPLL)
> +		init.ops = &ma35d1_clk_fixed_pll_ops;
> +	else
> +		init.ops = &ma35d1_clk_pll_ops;
> +
> +	pll->hw.init = &init;
> +	hw = &pll->hw;
> +
> +	ret = devm_clk_hw_register(dev, hw);
> +	if (ret)
> +		return ERR_PTR(ret);
> +	return hw;
> +}
> +EXPORT_SYMBOL_GPL(ma35d1_reg_clk_pll);
> diff --git a/drivers/clk/nuvoton/clk-ma35d1.c b/drivers/clk/nuvoton/clk-ma35d1.c
> new file mode 100644
> index 000000000000..e77e06a4a07c
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1.c
> @@ -0,0 +1,1046 @@
> +// 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/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> +
> +#include "clk-ma35d1.h"
> +
> +static DEFINE_SPINLOCK(ma35d1_lock);
> +
> +#define PLL_MAX_NUM		5
> +
> +/* 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
> +
> +#define PLL_MODE_INT            0
> +#define PLL_MODE_FRAC           1
> +#define PLL_MODE_SS             2
> +
> +static const struct clk_parent_data ca35clk_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "capll", },
> +	{ .fw_name = "ddrpll", },
> +};
> +
> +static const struct clk_parent_data sysclk0_sel_clks[] = {
> +	{ .fw_name = "epll_div2", },
> +	{ .fw_name = "syspll", },
> +};
> +
> +static const struct clk_parent_data sysclk1_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "syspll", },
> +};
> +
> +static const struct clk_parent_data axiclk_sel_clks[] = {
> +	{ .fw_name = "capll_div2", },
> +	{ .fw_name = "capll_div4", },
> +};
> +
> +static const struct clk_parent_data ccap_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "vpll", },
> +	{ .fw_name = "apll", },
> +	{ .fw_name = "syspll", },
> +};
> +
> +static const struct clk_parent_data sdh_sel_clks[] = {
> +	{ .fw_name = "syspll", },
> +	{ .fw_name = "apll", },
> +};
> +
> +static const struct clk_parent_data dcu_sel_clks[] = {
> +	{ .fw_name = "epll_div2", },
> +	{ .fw_name = "syspll", },
> +};
> +
> +static const struct clk_parent_data gfx_sel_clks[] = {
> +	{ .fw_name = "epll", },
> +	{ .fw_name = "syspll", },
> +};
> +
> +static const struct clk_parent_data dbg_sel_clks[] = {
> +	{ .fw_name = "hirc", },
> +	{ .fw_name = "syspll", },
> +};
> +
> +static const struct clk_parent_data timer0_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk0", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer1_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk0", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer2_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk1", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer3_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk1", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer4_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk2", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer5_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk2", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer6_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk0", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer7_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk0", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer8_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk1", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer9_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk1", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer10_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk2", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data timer11_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk2", },
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "lirc", },
> +	{ .index = -1, },
> +	{ .fw_name = "hirc", },
> +};
> +
> +static const struct clk_parent_data uart_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "sysclk1_div2", },
> +};
> +
> +static const struct clk_parent_data wdt0_sel_clks[] = {
> +	{ .index = -1, },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk3_div4096", },
> +	{ .fw_name = "lirc", },
> +};
> +
> +static const struct clk_parent_data wdt1_sel_clks[] = {
> +	{ .index = -1, },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk3_div4096", },
> +	{ .fw_name = "lirc", },
> +};
> +
> +static const struct clk_parent_data wdt2_sel_clks[] = {
> +	{ .index = -1, },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "pclk4_div4096", },
> +	{ .fw_name = "lirc", },
> +};
> +
> +static const struct clk_parent_data wwdt0_sel_clks[] = {
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "pclk3_div4096", },
> +	{ .fw_name = "lirc", },
> +};
> +
> +static const struct clk_parent_data wwdt1_sel_clks[] = {
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "pclk3_div4096", },
> +	{ .fw_name = "lirc", },
> +};
> +
> +static const struct clk_parent_data wwdt2_sel_clks[] = {
> +	{ .index = -1, },
> +	{ .index = -1, },
> +	{ .fw_name = "pclk4_div4096", },
> +	{ .fw_name = "lirc", },
> +};
> +
> +static const struct clk_parent_data spi0_sel_clks[] = {
> +	{ .fw_name = "pclk1", },
> +	{ .fw_name = "apll", },
> +};
> +
> +static const struct clk_parent_data spi1_sel_clks[] = {
> +	{ .fw_name = "pclk2", },
> +	{ .fw_name = "apll", },
> +};
> +
> +static const struct clk_parent_data spi2_sel_clks[] = {
> +	{ .fw_name = "pclk1", },
> +	{ .fw_name = "apll", },
> +};
> +
> +static const struct clk_parent_data spi3_sel_clks[] = {
> +	{ .fw_name = "pclk2", },
> +	{ .fw_name = "apll", },
> +};
> +
> +static const struct clk_parent_data qspi_sel_clks[] = {
> +	{ .fw_name = "pclk0", },
> +	{ .fw_name = "apll", },
> +};
> +
> +static const struct clk_parent_data i2s_sel_clks[] = {
> +	{ .fw_name = "apll", },
> +	{ .fw_name = "sysclk1_div2", },
> +};
> +
> +static const struct clk_parent_data can_sel_clks[] = {
> +	{ .fw_name = "apll", },
> +	{ .fw_name = "vpll", },
> +};
> +
> +static const struct clk_parent_data cko_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "lxt", },
> +	{ .fw_name = "hirc", },
> +	{ .fw_name = "lirc", },
> +	{ .fw_name = "capll_div4", },
> +	{ .fw_name = "syspll", },
> +	{ .fw_name = "ddrpll", },
> +	{ .fw_name = "epll_div2", },
> +	{ .fw_name = "apll", },
> +	{ .fw_name = "vpll", },
> +};
> +
> +static const struct clk_parent_data smc_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "pclk4", },
> +};
> +
> +static const struct clk_parent_data kpi_sel_clks[] = {
> +	{ .fw_name = "hxt", },
> +	{ .fw_name = "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 *ma35d1_clk_fixed(const char *name, int rate)
> +{
> +	return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
> +}
> +
> +static struct clk_hw *ma35d1_clk_mux(struct device *dev, const char *name,
> +				     void __iomem *reg, u8 shift, u8 width,
> +				     const struct clk_parent_data *pdata,
> +				     int num_pdata)
> +{
> +	return clk_hw_register_mux_parent_data(dev, name, pdata, num_pdata,
> +					       CLK_SET_RATE_NO_REPARENT, reg, shift,
> +					       width, 0, &ma35d1_lock);
> +}
> +
> +static struct clk_hw *ma35d1_clk_divider(struct device *dev, const char *name,
> +					 const char *parent, void __iomem *reg,
> +					 u8 shift, u8 width)
> +{
> +	return devm_clk_hw_register_divider(dev, name, parent, CLK_SET_RATE_PARENT,
> +					    reg, shift, width, 0, &ma35d1_lock);
> +}
> +
> +static struct clk_hw *ma35d1_clk_divider_pow2(struct device *dev, const char *name,
> +					      const char *parent, void __iomem *reg,
> +					      u8 shift, u8 width)
> +{
> +	return devm_clk_hw_register_divider(dev, name, parent,
> +					    CLK_DIVIDER_POWER_OF_TWO, reg, shift,
> +					    width, 0, &ma35d1_lock);
> +}
> +
> +static struct clk_hw *ma35d1_clk_divider_table(struct device *dev, const char *name,
> +					       const char *parent, void __iomem *reg,
> +					       u8 shift, u8 width,
> +					       const struct clk_div_table *table)
> +{
> +	return devm_clk_hw_register_divider_table(dev, name, parent, 0,
> +						  reg, shift, width, 0,
> +						  table, &ma35d1_lock);
> +}
> +
> +static struct clk_hw *ma35d1_clk_fixed_factor(struct device *dev, const char *name,
> +					      const char *parent, unsigned int mult,
> +					      unsigned int div)
> +{
> +	return devm_clk_hw_register_fixed_factor(dev, name, parent,
> +					    CLK_SET_RATE_PARENT, mult, div);
> +}
> +
> +static struct clk_hw *ma35d1_clk_gate(struct device *dev, const char *name, const char *parent,
> +				      void __iomem *reg, u8 shift)
> +{
> +	return devm_clk_hw_register_gate(dev, name, parent, CLK_SET_RATE_PARENT,
> +				    reg, shift, 0, &ma35d1_lock);
> +}
> +
> +static int ma35d1_get_pll_setting(struct device_node *clk_node, u32 *pllmode)
> +{
> +	const char *of_str;
> +	int i;
> +
> +	for (i = 0; i < PLL_MAX_NUM; i++) {
> +		if (of_property_read_string_index(clk_node, "nuvoton,pll-mode", i, &of_str))
> +			return -EINVAL;
> +		if (!strcmp(of_str, "integer"))
> +			pllmode[i] = PLL_MODE_INT;
> +		else if (!strcmp(of_str, "fractional"))
> +			pllmode[i] = PLL_MODE_FRAC;
> +		else if (!strcmp(of_str, "spread-spectrum"))
> +			pllmode[i] = PLL_MODE_SS;
> +		else
> +			return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int ma35d1_clocks_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *clk_node = pdev->dev.of_node;
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	void __iomem *clk_base;
> +	static struct clk_hw **hws;
> +	static struct clk_hw_onecell_data *ma35d1_hw_data;
> +	u32 pllmode[PLL_MAX_NUM];
> +	int ret;
> +
> +	ma35d1_hw_data = devm_kzalloc(dev,
> +				      struct_size(ma35d1_hw_data, hws, CLK_MAX_IDX),
> +				      GFP_KERNEL);
> +	if (!ma35d1_hw_data)
> +		return -ENOMEM;
> +
> +	ma35d1_hw_data->num = CLK_MAX_IDX;
> +	hws = ma35d1_hw_data->hws;
> +
> +	clk_base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(clk_base))
> +		return PTR_ERR(clk_base);
> +
> +	ret = ma35d1_get_pll_setting(clk_node, pllmode);
> +	if (ret < 0) {
> +		dev_err(dev, "Invalid PLL setting!\n");
> +		return -EINVAL;
> +	}
> +
> +	hws[HXT] = ma35d1_clk_fixed("hxt", 24000000);
> +	hws[HXT_GATE] = ma35d1_clk_gate(dev, "hxt_gate", "hxt",
> +					clk_base + REG_CLK_PWRCTL, 0);
> +	hws[LXT] = ma35d1_clk_fixed("lxt", 32768);
> +	hws[LXT_GATE] = ma35d1_clk_gate(dev, "lxt_gate", "lxt",
> +					clk_base + REG_CLK_PWRCTL, 1);
> +	hws[HIRC] = ma35d1_clk_fixed("hirc", 12000000);
> +	hws[HIRC_GATE] = ma35d1_clk_gate(dev, "hirc_gate", "hirc",
> +					 clk_base + REG_CLK_PWRCTL, 2);
> +	hws[LIRC] = ma35d1_clk_fixed("lirc", 32000);
> +	hws[LIRC_GATE] = ma35d1_clk_gate(dev, "lirc_gate", "lirc",
> +					 clk_base + REG_CLK_PWRCTL, 3);
> +
> +	hws[CAPLL] = ma35d1_reg_clk_pll(dev, CAPLL, pllmode[0], "capll",
> +					hws[HXT], clk_base + REG_CLK_PLL0CTL0);
> +	hws[SYSPLL] = ma35d1_clk_fixed("syspll", 180000000);
> +	hws[DDRPLL] = ma35d1_reg_clk_pll(dev, DDRPLL, pllmode[1], "ddrpll",
> +					hws[HXT], clk_base + REG_CLK_PLL2CTL0);
> +	hws[APLL] = ma35d1_reg_clk_pll(dev, APLL, pllmode[2], "apll",
> +				       hws[HXT], clk_base + REG_CLK_PLL3CTL0);
> +	hws[EPLL] = ma35d1_reg_clk_pll(dev, EPLL, pllmode[3], "epll",
> +				       hws[HXT], clk_base + REG_CLK_PLL4CTL0);
> +	hws[VPLL] = ma35d1_reg_clk_pll(dev, VPLL, pllmode[4], "vpll",
> +				       hws[HXT], clk_base + REG_CLK_PLL5CTL0);
> +
> +	hws[EPLL_DIV2] = ma35d1_clk_fixed_factor(dev, "epll_div2", "epll", 1, 2);
> +	hws[EPLL_DIV4] = ma35d1_clk_fixed_factor(dev, "epll_div4", "epll", 1, 4);
> +	hws[EPLL_DIV8] = ma35d1_clk_fixed_factor(dev, "epll_div8", "epll", 1, 8);
> +
> +	hws[CA35CLK_MUX] = ma35d1_clk_mux(dev, "ca35clk_mux", clk_base + REG_CLK_CLKSEL0, 0, 2,
> +					  ca35clk_sel_clks, ARRAY_SIZE(ca35clk_sel_clks));
> +
> +	hws[AXICLK_DIV2] = ma35d1_clk_fixed_factor(dev, "capll_div2", "ca35clk_mux", 1, 2);
> +	hws[AXICLK_DIV4] = ma35d1_clk_fixed_factor(dev, "capll_div4", "ca35clk_mux", 1, 4);
> +
> +	hws[AXICLK_MUX] = ma35d1_clk_mux(dev, "axiclk_mux", clk_base + REG_CLK_CLKDIV0, 26, 1,
> +					 axiclk_sel_clks, ARRAY_SIZE(axiclk_sel_clks));
> +
> +	hws[SYSCLK0_MUX] = ma35d1_clk_mux(dev, "sysclk0_mux",
> +					  clk_base + REG_CLK_CLKSEL0, 2, 1,
> +					  sysclk0_sel_clks, ARRAY_SIZE(sysclk0_sel_clks));
> +
> +	hws[SYSCLK1_MUX] = ma35d1_clk_mux(dev, "sysclk1_mux", clk_base + REG_CLK_CLKSEL0, 4, 1,
> +					  sysclk1_sel_clks, ARRAY_SIZE(sysclk1_sel_clks));
> +
> +	hws[SYSCLK1_DIV2] = ma35d1_clk_fixed_factor(dev, "sysclk1_div2", "sysclk1_mux", 1, 2);
> +
> +	/* HCLK0~3 & PCLK0~4 */
> +	hws[HCLK0] = ma35d1_clk_fixed_factor(dev, "hclk0", "sysclk1_mux", 1, 1);
> +	hws[HCLK1] = ma35d1_clk_fixed_factor(dev, "hclk1", "sysclk1_mux", 1, 1);
> +	hws[HCLK2] = ma35d1_clk_fixed_factor(dev, "hclk2", "sysclk1_mux", 1, 1);
> +	hws[PCLK0] = ma35d1_clk_fixed_factor(dev, "pclk0", "sysclk1_mux", 1, 1);
> +	hws[PCLK1] = ma35d1_clk_fixed_factor(dev, "pclk1", "sysclk1_mux", 1, 1);
> +	hws[PCLK2] = ma35d1_clk_fixed_factor(dev, "pclk2", "sysclk1_mux", 1, 1);
> +
> +	hws[HCLK3] = ma35d1_clk_fixed_factor(dev, "hclk3", "sysclk1_mux", 1, 2);
> +	hws[PCLK3] = ma35d1_clk_fixed_factor(dev, "pclk3", "sysclk1_mux", 1, 2);
> +	hws[PCLK4] = ma35d1_clk_fixed_factor(dev, "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(dev, "ddr0_gate", "ddrpll",
> +					 clk_base + REG_CLK_SYSCLK0, 4);
> +	hws[DDR6_GATE] = ma35d1_clk_gate(dev, "ddr6_gate", "ddrpll",
> +					 clk_base + REG_CLK_SYSCLK0, 5);
> +
> +	hws[CAN0_MUX] = ma35d1_clk_mux(dev, "can0_mux", clk_base + REG_CLK_CLKSEL4, 16, 1,
> +				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
> +	hws[CAN0_DIV] = ma35d1_clk_divider_table(dev, "can0_div", "can0_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 0, 3, ip_div_table);
> +	hws[CAN0_GATE] = ma35d1_clk_gate(dev, "can0_gate", "can0_div",
> +					 clk_base + REG_CLK_SYSCLK0, 8);
> +
> +	hws[CAN1_MUX] = ma35d1_clk_mux(dev, "can1_mux", clk_base + REG_CLK_CLKSEL4, 17, 1,
> +				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
> +	hws[CAN1_DIV] = ma35d1_clk_divider_table(dev, "can1_div", "can1_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 4, 3, ip_div_table);
> +	hws[CAN1_GATE] = ma35d1_clk_gate(dev, "can1_gate", "can1_div",
> +					 clk_base + REG_CLK_SYSCLK0, 9);
> +
> +	hws[CAN2_MUX] = ma35d1_clk_mux(dev, "can2_mux", clk_base + REG_CLK_CLKSEL4, 18, 1,
> +				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
> +	hws[CAN2_DIV] = ma35d1_clk_divider_table(dev, "can2_div", "can2_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 8, 3, ip_div_table);
> +	hws[CAN2_GATE] = ma35d1_clk_gate(dev, "can2_gate", "can2_div",
> +					 clk_base + REG_CLK_SYSCLK0, 10);
> +
> +	hws[CAN3_MUX] = ma35d1_clk_mux(dev, "can3_mux", clk_base + REG_CLK_CLKSEL4, 19, 1,
> +				       can_sel_clks, ARRAY_SIZE(can_sel_clks));
> +	hws[CAN3_DIV] = ma35d1_clk_divider_table(dev, "can3_div", "can3_mux",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 12, 3, ip_div_table);
> +	hws[CAN3_GATE] = ma35d1_clk_gate(dev, "can3_gate", "can3_div",
> +					 clk_base + REG_CLK_SYSCLK0, 11);
> +
> +	hws[SDH0_MUX] = ma35d1_clk_mux(dev, "sdh0_mux", clk_base + REG_CLK_CLKSEL0, 16, 2,
> +				       sdh_sel_clks, ARRAY_SIZE(sdh_sel_clks));
> +	hws[SDH0_GATE] = ma35d1_clk_gate(dev, "sdh0_gate", "sdh0_mux",
> +					 clk_base + REG_CLK_SYSCLK0, 16);
> +
> +	hws[SDH1_MUX] = ma35d1_clk_mux(dev, "sdh1_mux", clk_base + REG_CLK_CLKSEL0, 18, 2,
> +				       sdh_sel_clks, ARRAY_SIZE(sdh_sel_clks));
> +	hws[SDH1_GATE] = ma35d1_clk_gate(dev, "sdh1_gate", "sdh1_mux",
> +					 clk_base + REG_CLK_SYSCLK0, 17);
> +
> +	hws[NAND_GATE] = ma35d1_clk_gate(dev, "nand_gate", "hclk1",
> +					 clk_base + REG_CLK_SYSCLK0, 18);
> +
> +	hws[USBD_GATE] = ma35d1_clk_gate(dev, "usbd_gate", "usbphy0",
> +					 clk_base + REG_CLK_SYSCLK0, 19);
> +	hws[USBH_GATE] = ma35d1_clk_gate(dev, "usbh_gate", "usbphy0",
> +					 clk_base + REG_CLK_SYSCLK0, 20);
> +	hws[HUSBH0_GATE] = ma35d1_clk_gate(dev, "husbh0_gate", "usbphy0",
> +					   clk_base + REG_CLK_SYSCLK0, 21);
> +	hws[HUSBH1_GATE] = ma35d1_clk_gate(dev, "husbh1_gate", "usbphy0",
> +					   clk_base + REG_CLK_SYSCLK0, 22);
> +
> +	hws[GFX_MUX] = ma35d1_clk_mux(dev, "gfx_mux", clk_base + REG_CLK_CLKSEL0, 26, 1,
> +				      gfx_sel_clks, ARRAY_SIZE(gfx_sel_clks));
> +	hws[GFX_GATE] = ma35d1_clk_gate(dev, "gfx_gate", "gfx_mux",
> +					clk_base + REG_CLK_SYSCLK0, 24);
> +	hws[VC8K_GATE] = ma35d1_clk_gate(dev, "vc8k_gate", "sysclk0_mux",
> +					 clk_base + REG_CLK_SYSCLK0, 25);
> +
> +	hws[DCU_MUX] = ma35d1_clk_mux(dev, "dcu_mux", clk_base + REG_CLK_CLKSEL0, 24, 1,
> +				      dcu_sel_clks, ARRAY_SIZE(dcu_sel_clks));
> +	hws[DCU_GATE] = ma35d1_clk_gate(dev, "dcu_gate", "dcu_mux",
> +					clk_base + REG_CLK_SYSCLK0, 26);
> +	hws[DCUP_DIV] = ma35d1_clk_divider_table(dev, "dcup_div", "vpll",
> +						 clk_base + REG_CLK_CLKDIV0,
> +						 16, 3, ip_div_table);
> +
> +	hws[EMAC0_GATE] = ma35d1_clk_gate(dev, "emac0_gate", "epll_div2",
> +					  clk_base + REG_CLK_SYSCLK0, 27);
> +	hws[EMAC1_GATE] = ma35d1_clk_gate(dev, "emac1_gate", "epll_div2",
> +					  clk_base + REG_CLK_SYSCLK0, 28);
> +
> +	hws[CCAP0_MUX] = ma35d1_clk_mux(dev, "ccap0_mux", clk_base + REG_CLK_CLKSEL0, 12, 1,
> +					ccap_sel_clks, ARRAY_SIZE(ccap_sel_clks));
> +	hws[CCAP0_DIV] = ma35d1_clk_divider(dev, "ccap0_div", "ccap0_mux",
> +					    clk_base + REG_CLK_CLKDIV1, 8, 4);
> +	hws[CCAP0_GATE] = ma35d1_clk_gate(dev, "ccap0_gate", "ccap0_div",
> +					  clk_base + REG_CLK_SYSCLK0, 29);
> +
> +	hws[CCAP1_MUX] = ma35d1_clk_mux(dev, "ccap1_mux", clk_base + REG_CLK_CLKSEL0, 14, 1,
> +					ccap_sel_clks, ARRAY_SIZE(ccap_sel_clks));
> +	hws[CCAP1_DIV] = ma35d1_clk_divider(dev, "ccap1_div", "ccap1_mux",
> +					    clk_base + REG_CLK_CLKDIV1,
> +					    12, 4);
> +	hws[CCAP1_GATE] = ma35d1_clk_gate(dev, "ccap1_gate", "ccap1_div",
> +					  clk_base + REG_CLK_SYSCLK0, 30);
> +
> +	hws[PDMA0_GATE] = ma35d1_clk_gate(dev, "pdma0_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 0);
> +	hws[PDMA1_GATE] = ma35d1_clk_gate(dev, "pdma1_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 1);
> +	hws[PDMA2_GATE] = ma35d1_clk_gate(dev, "pdma2_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 2);
> +	hws[PDMA3_GATE] = ma35d1_clk_gate(dev, "pdma3_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 3);
> +
> +	hws[WH0_GATE] = ma35d1_clk_gate(dev, "wh0_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 4);
> +	hws[WH1_GATE] = ma35d1_clk_gate(dev, "wh1_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 5);
> +
> +	hws[HWS_GATE] = ma35d1_clk_gate(dev, "hws_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 6);
> +
> +	hws[EBI_GATE] = ma35d1_clk_gate(dev, "ebi_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 7);
> +
> +	hws[SRAM0_GATE] = ma35d1_clk_gate(dev, "sram0_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 8);
> +	hws[SRAM1_GATE] = ma35d1_clk_gate(dev, "sram1_gate", "hclk0",
> +					  clk_base + REG_CLK_SYSCLK1, 9);
> +
> +	hws[ROM_GATE] = ma35d1_clk_gate(dev, "rom_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 10);
> +
> +	hws[TRA_GATE] = ma35d1_clk_gate(dev, "tra_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 11);
> +
> +	hws[DBG_MUX] = ma35d1_clk_mux(dev, "dbg_mux", clk_base + REG_CLK_CLKSEL0, 27, 1,
> +				      dbg_sel_clks, ARRAY_SIZE(dbg_sel_clks));
> +	hws[DBG_GATE] = ma35d1_clk_gate(dev, "dbg_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 12);
> +
> +	hws[CKO_MUX] = ma35d1_clk_mux(dev, "cko_mux", clk_base + REG_CLK_CLKSEL4, 24, 4,
> +				      cko_sel_clks, ARRAY_SIZE(cko_sel_clks));
> +	hws[CKO_DIV] = ma35d1_clk_divider_pow2(dev, "cko_div", "cko_mux",
> +					       clk_base + REG_CLK_CLKOCTL, 0, 4);
> +	hws[CKO_GATE] = ma35d1_clk_gate(dev, "cko_gate", "cko_div",
> +					clk_base + REG_CLK_SYSCLK1, 13);
> +
> +	hws[GTMR_GATE] = ma35d1_clk_gate(dev, "gtmr_gate", "hirc",
> +					 clk_base + REG_CLK_SYSCLK1, 14);
> +
> +	hws[GPA_GATE] = ma35d1_clk_gate(dev, "gpa_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 16);
> +	hws[GPB_GATE] = ma35d1_clk_gate(dev, "gpb_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 17);
> +	hws[GPC_GATE] = ma35d1_clk_gate(dev, "gpc_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 18);
> +	hws[GPD_GATE] = ma35d1_clk_gate(dev, "gpd_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 19);
> +	hws[GPE_GATE] = ma35d1_clk_gate(dev, "gpe_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 20);
> +	hws[GPF_GATE] = ma35d1_clk_gate(dev, "gpf_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 21);
> +	hws[GPG_GATE] = ma35d1_clk_gate(dev, "gpg_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 22);
> +	hws[GPH_GATE] = ma35d1_clk_gate(dev, "gph_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 23);
> +	hws[GPI_GATE] = ma35d1_clk_gate(dev, "gpi_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 24);
> +	hws[GPJ_GATE] = ma35d1_clk_gate(dev, "gpj_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 25);
> +	hws[GPK_GATE] = ma35d1_clk_gate(dev, "gpk_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 26);
> +	hws[GPL_GATE] = ma35d1_clk_gate(dev, "gpl_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 27);
> +	hws[GPM_GATE] = ma35d1_clk_gate(dev, "gpm_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 28);
> +	hws[GPN_GATE] = ma35d1_clk_gate(dev, "gpn_gate", "hclk0",
> +					clk_base + REG_CLK_SYSCLK1, 29);
> +
> +	hws[TMR0_MUX] = ma35d1_clk_mux(dev, "tmr0_mux", clk_base + REG_CLK_CLKSEL1, 0, 3,
> +				       timer0_sel_clks, ARRAY_SIZE(timer0_sel_clks));
> +	hws[TMR0_GATE] = ma35d1_clk_gate(dev, "tmr0_gate", "tmr0_mux",
> +					 clk_base + REG_CLK_APBCLK0, 0);
> +
> +	hws[TMR1_MUX] = ma35d1_clk_mux(dev, "tmr1_mux", clk_base + REG_CLK_CLKSEL1, 4, 3,
> +				       timer1_sel_clks, ARRAY_SIZE(timer1_sel_clks));
> +	hws[TMR1_GATE] = ma35d1_clk_gate(dev, "tmr1_gate", "tmr1_mux",
> +					 clk_base + REG_CLK_APBCLK0, 1);
> +
> +	hws[TMR2_MUX] = ma35d1_clk_mux(dev, "tmr2_mux", clk_base + REG_CLK_CLKSEL1, 8, 3,
> +				       timer2_sel_clks, ARRAY_SIZE(timer2_sel_clks));
> +	hws[TMR2_GATE] = ma35d1_clk_gate(dev, "tmr2_gate", "tmr2_mux",
> +					 clk_base + REG_CLK_APBCLK0, 2);
> +
> +	hws[TMR3_MUX] = ma35d1_clk_mux(dev, "tmr3_mux", clk_base + REG_CLK_CLKSEL1, 12, 3,
> +				       timer3_sel_clks, ARRAY_SIZE(timer3_sel_clks));
> +	hws[TMR3_GATE] = ma35d1_clk_gate(dev, "tmr3_gate", "tmr3_mux",
> +					 clk_base + REG_CLK_APBCLK0, 3);
> +
> +	hws[TMR4_MUX] = ma35d1_clk_mux(dev, "tmr4_mux", clk_base + REG_CLK_CLKSEL1, 16, 3,
> +				       timer4_sel_clks, ARRAY_SIZE(timer4_sel_clks));
> +	hws[TMR4_GATE] = ma35d1_clk_gate(dev, "tmr4_gate", "tmr4_mux",
> +					 clk_base + REG_CLK_APBCLK0, 4);
> +
> +	hws[TMR5_MUX] = ma35d1_clk_mux(dev, "tmr5_mux", clk_base + REG_CLK_CLKSEL1, 20, 3,
> +				       timer5_sel_clks, ARRAY_SIZE(timer5_sel_clks));
> +	hws[TMR5_GATE] = ma35d1_clk_gate(dev, "tmr5_gate", "tmr5_mux",
> +					 clk_base + REG_CLK_APBCLK0, 5);
> +
> +	hws[TMR6_MUX] = ma35d1_clk_mux(dev, "tmr6_mux", clk_base + REG_CLK_CLKSEL1, 24, 3,
> +				       timer6_sel_clks, ARRAY_SIZE(timer6_sel_clks));
> +	hws[TMR6_GATE] = ma35d1_clk_gate(dev, "tmr6_gate", "tmr6_mux",
> +					 clk_base + REG_CLK_APBCLK0, 6);
> +
> +	hws[TMR7_MUX] = ma35d1_clk_mux(dev, "tmr7_mux", clk_base + REG_CLK_CLKSEL1, 28, 3,
> +				       timer7_sel_clks, ARRAY_SIZE(timer7_sel_clks));
> +	hws[TMR7_GATE] = ma35d1_clk_gate(dev, "tmr7_gate", "tmr7_mux",
> +					 clk_base + REG_CLK_APBCLK0, 7);
> +
> +	hws[TMR8_MUX] = ma35d1_clk_mux(dev, "tmr8_mux", clk_base + REG_CLK_CLKSEL2, 0, 3,
> +				       timer8_sel_clks, ARRAY_SIZE(timer8_sel_clks));
> +	hws[TMR8_GATE] = ma35d1_clk_gate(dev, "tmr8_gate", "tmr8_mux",
> +					 clk_base + REG_CLK_APBCLK0, 8);
> +
> +	hws[TMR9_MUX] = ma35d1_clk_mux(dev, "tmr9_mux", clk_base + REG_CLK_CLKSEL2, 4, 3,
> +				       timer9_sel_clks, ARRAY_SIZE(timer9_sel_clks));
> +	hws[TMR9_GATE] = ma35d1_clk_gate(dev, "tmr9_gate", "tmr9_mux",
> +					 clk_base + REG_CLK_APBCLK0, 9);
> +
> +	hws[TMR10_MUX] = ma35d1_clk_mux(dev, "tmr10_mux", clk_base + REG_CLK_CLKSEL2, 8, 3,
> +					timer10_sel_clks, ARRAY_SIZE(timer10_sel_clks));
> +	hws[TMR10_GATE] = ma35d1_clk_gate(dev, "tmr10_gate", "tmr10_mux",
> +					  clk_base + REG_CLK_APBCLK0, 10);
> +
> +	hws[TMR11_MUX] = ma35d1_clk_mux(dev, "tmr11_mux", clk_base + REG_CLK_CLKSEL2, 12, 3,
> +					timer11_sel_clks, ARRAY_SIZE(timer11_sel_clks));
> +	hws[TMR11_GATE] = ma35d1_clk_gate(dev, "tmr11_gate", "tmr11_mux",
> +					  clk_base + REG_CLK_APBCLK0, 11);
> +
> +	hws[UART0_MUX] = ma35d1_clk_mux(dev, "uart0_mux", clk_base + REG_CLK_CLKSEL2, 16, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART0_DIV] = ma35d1_clk_divider(dev, "uart0_div", "uart0_mux",
> +					    clk_base + REG_CLK_CLKDIV1, 16, 4);
> +	hws[UART0_GATE] = ma35d1_clk_gate(dev, "uart0_gate", "uart0_div",
> +					  clk_base + REG_CLK_APBCLK0, 12);
> +
> +	hws[UART1_MUX] = ma35d1_clk_mux(dev, "uart1_mux", clk_base + REG_CLK_CLKSEL2, 18, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART1_DIV] = ma35d1_clk_divider(dev, "uart1_div", "uart1_mux",
> +					    clk_base + REG_CLK_CLKDIV1, 20, 4);
> +	hws[UART1_GATE] = ma35d1_clk_gate(dev, "uart1_gate", "uart1_div",
> +					  clk_base + REG_CLK_APBCLK0, 13);
> +
> +	hws[UART2_MUX] = ma35d1_clk_mux(dev, "uart2_mux", clk_base + REG_CLK_CLKSEL2, 20, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART2_DIV] = ma35d1_clk_divider(dev, "uart2_div", "uart2_mux",
> +					    clk_base + REG_CLK_CLKDIV1, 24, 4);
> +	hws[UART2_GATE] = ma35d1_clk_gate(dev, "uart2_gate", "uart2_div",
> +					  clk_base + REG_CLK_APBCLK0, 14);
> +
> +	hws[UART3_MUX] = ma35d1_clk_mux(dev, "uart3_mux", clk_base + REG_CLK_CLKSEL2, 22, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART3_DIV] = ma35d1_clk_divider(dev, "uart3_div", "uart3_mux",
> +					    clk_base + REG_CLK_CLKDIV1, 28, 4);
> +	hws[UART3_GATE] = ma35d1_clk_gate(dev, "uart3_gate", "uart3_div",
> +					  clk_base + REG_CLK_APBCLK0, 15);
> +
> +	hws[UART4_MUX] = ma35d1_clk_mux(dev, "uart4_mux", clk_base + REG_CLK_CLKSEL2, 24, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART4_DIV] = ma35d1_clk_divider(dev, "uart4_div", "uart4_mux",
> +					    clk_base + REG_CLK_CLKDIV2, 0, 4);
> +	hws[UART4_GATE] = ma35d1_clk_gate(dev, "uart4_gate", "uart4_div",
> +					  clk_base + REG_CLK_APBCLK0, 16);
> +
> +	hws[UART5_MUX] = ma35d1_clk_mux(dev, "uart5_mux", clk_base + REG_CLK_CLKSEL2, 26, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART5_DIV] = ma35d1_clk_divider(dev, "uart5_div", "uart5_mux",
> +					    clk_base + REG_CLK_CLKDIV2, 4, 4);
> +	hws[UART5_GATE] = ma35d1_clk_gate(dev, "uart5_gate", "uart5_div",
> +					  clk_base + REG_CLK_APBCLK0, 17);
> +
> +	hws[UART6_MUX] = ma35d1_clk_mux(dev, "uart6_mux", clk_base + REG_CLK_CLKSEL2, 28, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART6_DIV] = ma35d1_clk_divider(dev, "uart6_div", "uart6_mux",
> +					    clk_base + REG_CLK_CLKDIV2, 8, 4);
> +	hws[UART6_GATE] = ma35d1_clk_gate(dev, "uart6_gate", "uart6_div",
> +					  clk_base + REG_CLK_APBCLK0, 18);
> +
> +	hws[UART7_MUX] = ma35d1_clk_mux(dev, "uart7_mux", clk_base + REG_CLK_CLKSEL2, 30, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART7_DIV] = ma35d1_clk_divider(dev, "uart7_div", "uart7_mux",
> +					    clk_base + REG_CLK_CLKDIV2, 12, 4);
> +	hws[UART7_GATE] = ma35d1_clk_gate(dev, "uart7_gate", "uart7_div",
> +					  clk_base + REG_CLK_APBCLK0, 19);
> +
> +	hws[UART8_MUX] = ma35d1_clk_mux(dev, "uart8_mux", clk_base + REG_CLK_CLKSEL3, 0, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART8_DIV] = ma35d1_clk_divider(dev, "uart8_div", "uart8_mux",
> +					    clk_base + REG_CLK_CLKDIV2, 16, 4);
> +	hws[UART8_GATE] = ma35d1_clk_gate(dev, "uart8_gate", "uart8_div",
> +					  clk_base + REG_CLK_APBCLK0, 20);
> +
> +	hws[UART9_MUX] = ma35d1_clk_mux(dev, "uart9_mux", clk_base + REG_CLK_CLKSEL3, 2, 1,
> +					uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART9_DIV] = ma35d1_clk_divider(dev, "uart9_div", "uart9_mux",
> +					    clk_base + REG_CLK_CLKDIV2, 20, 4);
> +	hws[UART9_GATE] = ma35d1_clk_gate(dev, "uart9_gate", "uart9_div",
> +					  clk_base + REG_CLK_APBCLK0, 21);
> +
> +	hws[UART10_MUX] = ma35d1_clk_mux(dev, "uart10_mux", clk_base + REG_CLK_CLKSEL3, 4, 1,
> +					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART10_DIV] = ma35d1_clk_divider(dev, "uart10_div", "uart10_mux",
> +					     clk_base + REG_CLK_CLKDIV2, 24, 4);
> +	hws[UART10_GATE] = ma35d1_clk_gate(dev, "uart10_gate", "uart10_div",
> +					   clk_base + REG_CLK_APBCLK0, 22);
> +
> +	hws[UART11_MUX] = ma35d1_clk_mux(dev, "uart11_mux", clk_base + REG_CLK_CLKSEL3, 6, 1,
> +					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART11_DIV] = ma35d1_clk_divider(dev, "uart11_div", "uart11_mux",
> +					     clk_base + REG_CLK_CLKDIV2, 28, 4);
> +	hws[UART11_GATE] = ma35d1_clk_gate(dev, "uart11_gate", "uart11_div",
> +					   clk_base + REG_CLK_APBCLK0, 23);
> +
> +	hws[UART12_MUX] = ma35d1_clk_mux(dev, "uart12_mux", clk_base + REG_CLK_CLKSEL3, 8, 1,
> +					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART12_DIV] = ma35d1_clk_divider(dev, "uart12_div", "uart12_mux",
> +					     clk_base + REG_CLK_CLKDIV3, 0, 4);
> +	hws[UART12_GATE] = ma35d1_clk_gate(dev, "uart12_gate", "uart12_div",
> +					   clk_base + REG_CLK_APBCLK0, 24);
> +
> +	hws[UART13_MUX] = ma35d1_clk_mux(dev, "uart13_mux", clk_base + REG_CLK_CLKSEL3, 10, 1,
> +					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART13_DIV] = ma35d1_clk_divider(dev, "uart13_div", "uart13_mux",
> +					     clk_base + REG_CLK_CLKDIV3, 4, 4);
> +	hws[UART13_GATE] = ma35d1_clk_gate(dev, "uart13_gate", "uart13_div",
> +					   clk_base + REG_CLK_APBCLK0, 25);
> +
> +	hws[UART14_MUX] = ma35d1_clk_mux(dev, "uart14_mux", clk_base + REG_CLK_CLKSEL3, 12, 1,
> +					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART14_DIV] = ma35d1_clk_divider(dev, "uart14_div", "uart14_mux",
> +					     clk_base + REG_CLK_CLKDIV3, 8, 4);
> +	hws[UART14_GATE] = ma35d1_clk_gate(dev, "uart14_gate", "uart14_div",
> +					   clk_base + REG_CLK_APBCLK0, 26);
> +
> +	hws[UART15_MUX] = ma35d1_clk_mux(dev, "uart15_mux", clk_base + REG_CLK_CLKSEL3, 14, 1,
> +					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART15_DIV] = ma35d1_clk_divider(dev, "uart15_div", "uart15_mux",
> +					     clk_base + REG_CLK_CLKDIV3, 12, 4);
> +	hws[UART15_GATE] = ma35d1_clk_gate(dev, "uart15_gate", "uart15_div",
> +					   clk_base + REG_CLK_APBCLK0, 27);
> +
> +	hws[UART16_MUX] = ma35d1_clk_mux(dev, "uart16_mux", clk_base + REG_CLK_CLKSEL3, 16, 1,
> +					 uart_sel_clks, ARRAY_SIZE(uart_sel_clks));
> +	hws[UART16_DIV] = ma35d1_clk_divider(dev, "uart16_div", "uart16_mux",
> +					     clk_base + REG_CLK_CLKDIV3, 16, 4);
> +	hws[UART16_GATE] = ma35d1_clk_gate(dev, "uart16_gate", "uart16_div",
> +					   clk_base + REG_CLK_APBCLK0, 28);
> +
> +	hws[RTC_GATE] = ma35d1_clk_gate(dev, "rtc_gate", "lxt",
> +					clk_base + REG_CLK_APBCLK0, 29);
> +	hws[DDR_GATE] = ma35d1_clk_gate(dev, "ddr_gate", "ddrpll",
> +					clk_base + REG_CLK_APBCLK0, 30);
> +
> +	hws[KPI_MUX] = ma35d1_clk_mux(dev, "kpi_mux", clk_base + REG_CLK_CLKSEL4, 30, 1,
> +				      kpi_sel_clks, ARRAY_SIZE(kpi_sel_clks));
> +	hws[KPI_DIV] = ma35d1_clk_divider(dev, "kpi_div", "kpi_mux",
> +					  clk_base + REG_CLK_CLKDIV4, 24, 8);
> +	hws[KPI_GATE] = ma35d1_clk_gate(dev, "kpi_gate", "kpi_div",
> +					clk_base + REG_CLK_APBCLK0, 31);
> +
> +	hws[I2C0_GATE] = ma35d1_clk_gate(dev, "i2c0_gate", "pclk0",
> +					 clk_base + REG_CLK_APBCLK1, 0);
> +	hws[I2C1_GATE] = ma35d1_clk_gate(dev, "i2c1_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK1, 1);
> +	hws[I2C2_GATE] = ma35d1_clk_gate(dev, "i2c2_gate", "pclk2",
> +					 clk_base + REG_CLK_APBCLK1, 2);
> +	hws[I2C3_GATE] = ma35d1_clk_gate(dev, "i2c3_gate", "pclk0",
> +					 clk_base + REG_CLK_APBCLK1, 3);
> +	hws[I2C4_GATE] = ma35d1_clk_gate(dev, "i2c4_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK1, 4);
> +	hws[I2C5_GATE] = ma35d1_clk_gate(dev, "i2c5_gate", "pclk2",
> +					 clk_base + REG_CLK_APBCLK1, 5);
> +
> +	hws[QSPI0_MUX] = ma35d1_clk_mux(dev, "qspi0_mux", clk_base + REG_CLK_CLKSEL4, 8, 1,
> +					qspi_sel_clks, ARRAY_SIZE(qspi_sel_clks));
> +	hws[QSPI0_GATE] = ma35d1_clk_gate(dev, "qspi0_gate", "qspi0_mux",
> +					  clk_base + REG_CLK_APBCLK1, 6);
> +
> +	hws[QSPI1_MUX] = ma35d1_clk_mux(dev, "qspi1_mux", clk_base + REG_CLK_CLKSEL4, 10, 1,
> +					qspi_sel_clks, ARRAY_SIZE(qspi_sel_clks));
> +	hws[QSPI1_GATE] = ma35d1_clk_gate(dev, "qspi1_gate", "qspi1_mux",
> +					  clk_base + REG_CLK_APBCLK1, 7);
> +
> +	hws[SMC0_MUX] = ma35d1_clk_mux(dev, "smc0_mux", clk_base + REG_CLK_CLKSEL4, 28, 1,
> +				       smc_sel_clks, ARRAY_SIZE(smc_sel_clks));
> +	hws[SMC0_DIV] = ma35d1_clk_divider(dev, "smc0_div", "smc0_mux",
> +					   clk_base + REG_CLK_CLKDIV1, 0, 4);
> +	hws[SMC0_GATE] = ma35d1_clk_gate(dev, "smc0_gate", "smc0_div",
> +					 clk_base + REG_CLK_APBCLK1, 12);
> +
> +	hws[SMC1_MUX] = ma35d1_clk_mux(dev, "smc1_mux", clk_base + REG_CLK_CLKSEL4, 29, 1,
> +				       smc_sel_clks, ARRAY_SIZE(smc_sel_clks));
> +	hws[SMC1_DIV] = ma35d1_clk_divider(dev, "smc1_div", "smc1_mux",
> +					   clk_base + REG_CLK_CLKDIV1, 4, 4);
> +	hws[SMC1_GATE] = ma35d1_clk_gate(dev, "smc1_gate", "smc1_div",
> +					 clk_base + REG_CLK_APBCLK1, 13);
> +
> +	hws[WDT0_MUX] = ma35d1_clk_mux(dev, "wdt0_mux", clk_base + REG_CLK_CLKSEL3, 20, 2,
> +				       wdt0_sel_clks, ARRAY_SIZE(wdt0_sel_clks));
> +	hws[WDT0_GATE] = ma35d1_clk_gate(dev, "wdt0_gate", "wdt0_mux",
> +					 clk_base + REG_CLK_APBCLK1, 16);
> +
> +	hws[WDT1_MUX] = ma35d1_clk_mux(dev, "wdt1_mux", clk_base + REG_CLK_CLKSEL3, 24, 2,
> +				       wdt1_sel_clks, ARRAY_SIZE(wdt1_sel_clks));
> +	hws[WDT1_GATE] = ma35d1_clk_gate(dev, "wdt1_gate", "wdt1_mux",
> +					 clk_base + REG_CLK_APBCLK1, 17);
> +
> +	hws[WDT2_MUX] = ma35d1_clk_mux(dev, "wdt2_mux", clk_base + REG_CLK_CLKSEL3, 28, 2,
> +				       wdt2_sel_clks, ARRAY_SIZE(wdt2_sel_clks));
> +	hws[WDT2_GATE] = ma35d1_clk_gate(dev, "wdt2_gate", "wdt2_mux",
> +				       clk_base + REG_CLK_APBCLK1, 18);
> +
> +	hws[WWDT0_MUX] = ma35d1_clk_mux(dev, "wwdt0_mux", clk_base + REG_CLK_CLKSEL3, 22, 2,
> +					wwdt0_sel_clks, ARRAY_SIZE(wwdt0_sel_clks));
> +
> +	hws[WWDT1_MUX] = ma35d1_clk_mux(dev, "wwdt1_mux", clk_base + REG_CLK_CLKSEL3, 26, 2,
> +					wwdt1_sel_clks, ARRAY_SIZE(wwdt1_sel_clks));
> +
> +	hws[WWDT2_MUX] = ma35d1_clk_mux(dev, "wwdt2_mux", clk_base + REG_CLK_CLKSEL3, 30, 2,
> +					wwdt2_sel_clks, ARRAY_SIZE(wwdt2_sel_clks));
> +
> +	hws[EPWM0_GATE] = ma35d1_clk_gate(dev, "epwm0_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK1, 24);
> +	hws[EPWM1_GATE] = ma35d1_clk_gate(dev, "epwm1_gate", "pclk2",
> +					  clk_base + REG_CLK_APBCLK1, 25);
> +	hws[EPWM2_GATE] = ma35d1_clk_gate(dev, "epwm2_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK1, 26);
> +
> +	hws[I2S0_MUX] = ma35d1_clk_mux(dev, "i2s0_mux", clk_base + REG_CLK_CLKSEL4, 12, 1,
> +				       i2s_sel_clks, ARRAY_SIZE(i2s_sel_clks));
> +	hws[I2S0_GATE] = ma35d1_clk_gate(dev, "i2s0_gate", "i2s0_mux",
> +					 clk_base + REG_CLK_APBCLK2, 0);
> +
> +	hws[I2S1_MUX] = ma35d1_clk_mux(dev, "i2s1_mux", clk_base + REG_CLK_CLKSEL4, 14, 1,
> +				       i2s_sel_clks, ARRAY_SIZE(i2s_sel_clks));
> +	hws[I2S1_GATE] = ma35d1_clk_gate(dev, "i2s1_gate", "i2s1_mux",
> +					 clk_base + REG_CLK_APBCLK2, 1);
> +
> +	hws[SSMCC_GATE] = ma35d1_clk_gate(dev, "ssmcc_gate", "pclk3",
> +					  clk_base + REG_CLK_APBCLK2, 2);
> +	hws[SSPCC_GATE] = ma35d1_clk_gate(dev, "sspcc_gate", "pclk3",
> +					  clk_base + REG_CLK_APBCLK2, 3);
> +
> +	hws[SPI0_MUX] = ma35d1_clk_mux(dev, "spi0_mux", clk_base + REG_CLK_CLKSEL4, 0, 1,
> +				       spi0_sel_clks, ARRAY_SIZE(spi0_sel_clks));
> +	hws[SPI0_GATE] = ma35d1_clk_gate(dev, "spi0_gate", "spi0_mux",
> +					 clk_base + REG_CLK_APBCLK2, 4);
> +
> +	hws[SPI1_MUX] = ma35d1_clk_mux(dev, "spi1_mux", clk_base + REG_CLK_CLKSEL4, 2, 1,
> +				       spi1_sel_clks, ARRAY_SIZE(spi1_sel_clks));
> +	hws[SPI1_GATE] = ma35d1_clk_gate(dev, "spi1_gate", "spi1_mux",
> +					 clk_base + REG_CLK_APBCLK2, 5);
> +
> +	hws[SPI2_MUX] = ma35d1_clk_mux(dev, "spi2_mux", clk_base + REG_CLK_CLKSEL4, 4, 1,
> +				       spi2_sel_clks, ARRAY_SIZE(spi2_sel_clks));
> +	hws[SPI2_GATE] = ma35d1_clk_gate(dev, "spi2_gate", "spi2_mux",
> +					 clk_base + REG_CLK_APBCLK2, 6);
> +
> +	hws[SPI3_MUX] = ma35d1_clk_mux(dev, "spi3_mux", clk_base + REG_CLK_CLKSEL4, 6, 1,
> +				       spi3_sel_clks, ARRAY_SIZE(spi3_sel_clks));
> +	hws[SPI3_GATE] = ma35d1_clk_gate(dev, "spi3_gate", "spi3_mux",
> +					 clk_base + REG_CLK_APBCLK2, 7);
> +
> +	hws[ECAP0_GATE] = ma35d1_clk_gate(dev, "ecap0_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK2, 8);
> +	hws[ECAP1_GATE] = ma35d1_clk_gate(dev, "ecap1_gate", "pclk2",
> +					  clk_base + REG_CLK_APBCLK2, 9);
> +	hws[ECAP2_GATE] = ma35d1_clk_gate(dev, "ecap2_gate", "pclk1",
> +					  clk_base + REG_CLK_APBCLK2, 10);
> +
> +	hws[QEI0_GATE] = ma35d1_clk_gate(dev, "qei0_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK2, 12);
> +	hws[QEI1_GATE] = ma35d1_clk_gate(dev, "qei1_gate", "pclk2",
> +					 clk_base + REG_CLK_APBCLK2, 13);
> +	hws[QEI2_GATE] = ma35d1_clk_gate(dev, "qei2_gate", "pclk1",
> +					 clk_base + REG_CLK_APBCLK2, 14);
> +
> +	hws[ADC_DIV] = ma35d1_reg_adc_clkdiv(dev, "adc_div", hws[PCLK0], &ma35d1_lock, 0,
> +					     clk_base + REG_CLK_CLKDIV4, 4, 17, 0x1ffff);
> +	hws[ADC_GATE] = ma35d1_clk_gate(dev, "adc_gate", "adc_div",
> +					clk_base + REG_CLK_APBCLK2, 24);
> +
> +	hws[EADC_DIV] = ma35d1_clk_divider_table(dev, "eadc_div", "pclk2",
> +						 clk_base + REG_CLK_CLKDIV4,
> +						 0, 4, eadc_div_table);
> +	hws[EADC_GATE] = ma35d1_clk_gate(dev, "eadc_gate", "eadc_div",
> +					 clk_base + REG_CLK_APBCLK2, 25);
> +
> +	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, ma35d1_hw_data);
> +}
> +
> +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");
> diff --git a/drivers/clk/nuvoton/clk-ma35d1.h b/drivers/clk/nuvoton/clk-ma35d1.h
> new file mode 100644
> index 000000000000..3adee440f60a
> --- /dev/null
> +++ b/drivers/clk/nuvoton/clk-ma35d1.h
> @@ -0,0 +1,18 @@
> +/* 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
> +
> +struct clk_hw *ma35d1_reg_clk_pll(struct device *dev, u32 id, u8 u8mode, const char *name,
> +				  struct clk_hw *parent_hw, void __iomem *base);
> +
> +struct clk_hw *ma35d1_reg_adc_clkdiv(struct device *dev, const char *name,
> +				     struct clk_hw *parent_hw, spinlock_t *lock,
> +				     unsigned long flags, void __iomem *reg,
> +				     u8 shift, u8 width, u32 mask_bit);
> +
> +#endif /* __DRV_CLK_NUVOTON_MA35D1_H */


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

end of thread, other threads:[~2023-06-19  3:34 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-19  3:23 [PATCH v15 0/2] Introduce Nuvoton ma35d1 SoC Jacky Huang
2023-06-19  3:23 ` [PATCH v15 1/2] tty: serial: Add Nuvoton ma35d1 serial driver support Jacky Huang
2023-06-19  3:23 ` [PATCH 2/2] clk: nuvoton: Add clock driver for ma35d1 clock controller Jacky Huang
2023-06-19  3:34   ` 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).