Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers
@ 2019-12-03  7:35 James Tai
  2019-12-03  7:35 ` [PATCH 1/6] dt-bindings: clock: add bindings for RTD1619 clocks James Tai
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: James Tai @ 2019-12-03  7:35 UTC (permalink / raw)
  To: Andreas Färber
  Cc: devicetree, Cheng-Yu Lee, Palmer Dabbelt, linux-kernel,
	linux-mediatek, Paul Walmsley, Matthias Brugger, linux-riscv,
	linux-arm-kernel

Hi Andreas,

This series adds clock and reset controllers for the Realtek RTD1619 SoC.

Cc: Andreas Färber <afaerber@suse.de>
Cc: Cheng-Yu Lee <cylee12@realtek.com>
Cc: devicetree@vger.kernel.org

cylee12 (6):
  dt-bindings: clock: add bindings for RTD1619 clocks
  dt-bindings: reset: add bindings for rtd1619 reset controls
  clk: realtek: add common clock support for Realtek SoCs
  clk: realtek: add reset controller support for Realtek SoCs
  clk: realtek: add rtd1619 controllers
  dt-bindings: clk: realtek: add rtd1619 clock controller bindings

 .../bindings/clock/realtek,clocks.txt         |  38 ++
 drivers/clk/Kconfig                           |   1 +
 drivers/clk/Makefile                          |   1 +
 drivers/clk/realtek/Kconfig                   |  21 +
 drivers/clk/realtek/Makefile                  |  12 +
 drivers/clk/realtek/clk-pll-dif.c             |  81 +++
 drivers/clk/realtek/clk-pll-psaud.c           | 120 ++++
 drivers/clk/realtek/clk-pll.c                 | 400 +++++++++++++
 drivers/clk/realtek/clk-pll.h                 | 151 +++++
 drivers/clk/realtek/clk-regmap-gate.c         |  89 +++
 drivers/clk/realtek/clk-regmap-gate.h         |  26 +
 drivers/clk/realtek/clk-regmap-mux.c          |  63 ++
 drivers/clk/realtek/clk-regmap-mux.h          |  26 +
 drivers/clk/realtek/clk-rtd1619-cc.c          | 553 ++++++++++++++++++
 drivers/clk/realtek/clk-rtd1619-ic.c          | 112 ++++
 drivers/clk/realtek/common.c                  | 320 ++++++++++
 drivers/clk/realtek/common.h                  | 123 ++++
 drivers/clk/realtek/reset.c                   | 107 ++++
 drivers/clk/realtek/reset.h                   |  37 ++
 include/dt-bindings/clock/rtk,clock-rtd1619.h |  88 +++
 include/dt-bindings/reset/rtk,reset-rtd1619.h | 124 ++++
 21 files changed, 2493 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/realtek,clocks.txt
 create mode 100644 drivers/clk/realtek/Kconfig
 create mode 100644 drivers/clk/realtek/Makefile
 create mode 100644 drivers/clk/realtek/clk-pll-dif.c
 create mode 100644 drivers/clk/realtek/clk-pll-psaud.c
 create mode 100644 drivers/clk/realtek/clk-pll.c
 create mode 100644 drivers/clk/realtek/clk-pll.h
 create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
 create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
 create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
 create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
 create mode 100644 drivers/clk/realtek/clk-rtd1619-cc.c
 create mode 100644 drivers/clk/realtek/clk-rtd1619-ic.c
 create mode 100644 drivers/clk/realtek/common.c
 create mode 100644 drivers/clk/realtek/common.h
 create mode 100644 drivers/clk/realtek/reset.c
 create mode 100644 drivers/clk/realtek/reset.h
 create mode 100644 include/dt-bindings/clock/rtk,clock-rtd1619.h
 create mode 100644 include/dt-bindings/reset/rtk,reset-rtd1619.h

-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 1/6] dt-bindings: clock: add bindings for RTD1619 clocks
  2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
@ 2019-12-03  7:35 ` James Tai
  2019-12-03  7:35 ` [PATCH 2/6] dt-bindings: reset: add bindings for rtd1619 reset controls James Tai
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: James Tai @ 2019-12-03  7:35 UTC (permalink / raw)
  To: Andreas Färber
  Cc: Mark Rutland, devicetree, cylee12, Palmer Dabbelt, linux-kernel,
	Rob Herring, linux-mediatek, Paul Walmsley, Matthias Brugger,
	linux-riscv, linux-arm-kernel

From: cylee12 <cylee12@realtek.com>

Add devicetree binding for Realtek RTD1619 clocks.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 include/dt-bindings/clock/rtk,clock-rtd1619.h | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)
 create mode 100644 include/dt-bindings/clock/rtk,clock-rtd1619.h

diff --git a/include/dt-bindings/clock/rtk,clock-rtd1619.h b/include/dt-bindings/clock/rtk,clock-rtd1619.h
new file mode 100644
index 000000000000..497f9b914857
--- /dev/null
+++ b/include/dt-bindings/clock/rtk,clock-rtd1619.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __DT_BINDINGS_RTK_CLOCK_RTD1619_H
+#define __DT_BINDINGS_RTK_CLOCK_RTD1619_H
+
+#define CC_PLL_SCPU 0
+#define CC_PLL_BUS 2
+#define CC_CLK_SYS 3
+#define CC_CLK_SYS_SB2 4
+#define CC_PLL_DCSB 5
+#define CC_CLK_SYSH 6
+#define CC_PLL_DDSA 7
+#define CC_PLL_DDSB 8
+#define CC_PLL_GPU 9
+#define CC_CLK_GPU 10
+#define CC_PLL_VE1 11
+#define CC_PLL_VE2 12
+#define CC_CLK_VE1 13
+#define CC_CLK_VE2 14
+#define CC_CLK_VE3 15
+#define CC_CLK_VE2_BPU 16
+#define CC_PLL_DIF 17
+#define CC_PLL_PSAUD1A 18
+#define CC_PLL_PSAUD2A 19
+
+#define CC_CKE_MISC 33
+#define CC_CKE_PCIE0 34
+#define CC_CKE_GSPI 35
+#define CC_CKE_SDS 36
+#define CC_CKE_HDMI 37
+#define CC_CKE_LSADC 38
+#define CC_CKE_SE 39
+#define CC_CKE_CP 40
+#define CC_CKE_MD 41
+#define CC_CKE_TP 42
+#define CC_CKE_RSA 43
+#define CC_CKE_NF 44
+#define CC_CKE_EMMC 45
+#define CC_CKE_SD 46
+#define CC_CKE_SDIO_IP 47
+#define CC_CKE_MIPI 48
+#define CC_CKE_EMMC_IP 49
+#define CC_CKE_SDIO 50
+#define CC_CKE_SD_IP 51
+#define CC_CKE_CABLERX 52
+#define CC_CKE_TPB 53
+#define CC_CKE_SC1 54
+#define CC_CKE_I2C3 55
+#define CC_CKE_JPEG 56
+#define CC_CKE_SC0 57
+#define CC_CKE_HDMIRX 58
+#define CC_CKE_HSE 59
+#define CC_CKE_UR2 60
+#define CC_CKE_UR1 61
+#define CC_CKE_FAN 62
+#define CC_CKE_SATA_WRAP_SYS 63
+#define CC_CKE_SATA_WRAP_SYSH 64
+#define CC_CKE_SATA_MAC_SYSH 65
+#define CC_CKE_R2RDSC 66
+#define CC_CKE_PCIE1 67
+#define CC_CKE_I2C4 68
+#define CC_CKE_I2C5 69
+#define CC_CKE_EDP 70
+#define CC_CKE_TSIO_TRX 71
+#define CC_CKE_TVE 72
+#define CC_CKE_VO 73
+
+#define CC_CLK_MAX 74
+
+
+#define IC_CKE_CEC0 2
+#define IC_CKE_CBUSRX_SYS 3
+#define IC_CKE_CBUSTX_SYS 4
+#define IC_CKE_CBUS_SYS 5
+#define IC_CKE_CBUS_OSC 6
+#define IC_CKE_IR 7
+#define IC_CKE_UR0 8
+#define IC_CKE_I2C0 9
+#define IC_CKE_I2C1 10
+#define IC_CKE_ETN_250M 11
+#define IC_CKE_ETN_SYS 12
+#define IC_CKE_USB_DRD 13
+#define IC_CKE_USB_HOST 14
+#define IC_CKE_USB_U3_HOST 15
+#define IC_CKE_USB 16
+#define IC_CLK_MAX 17
+
+#endif /* __DT_BINDINGS_RTK_CLOCK_RTD1619_H */
+
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 2/6] dt-bindings: reset: add bindings for rtd1619 reset controls
  2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
  2019-12-03  7:35 ` [PATCH 1/6] dt-bindings: clock: add bindings for RTD1619 clocks James Tai
@ 2019-12-03  7:35 ` James Tai
  2019-12-03  7:35 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: James Tai @ 2019-12-03  7:35 UTC (permalink / raw)
  To: Andreas Färber
  Cc: Mark Rutland, devicetree, cylee12, Palmer Dabbelt, linux-kernel,
	Rob Herring, linux-mediatek, Philipp Zabel, Paul Walmsley,
	Matthias Brugger, linux-riscv, linux-arm-kernel

From: cylee12 <cylee12@realtek.com>

Add devicetree binding for Realtek RTD1619 SoC reset controls.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 include/dt-bindings/reset/rtk,reset-rtd1619.h | 124 ++++++++++++++++++
 1 file changed, 124 insertions(+)
 create mode 100644 include/dt-bindings/reset/rtk,reset-rtd1619.h

diff --git a/include/dt-bindings/reset/rtk,reset-rtd1619.h b/include/dt-bindings/reset/rtk,reset-rtd1619.h
new file mode 100644
index 000000000000..f6fa6359ec1c
--- /dev/null
+++ b/include/dt-bindings/reset/rtk,reset-rtd1619.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __DT_BINDINGS_RTK_RESET_RTD1619_H
+#define __DT_BINDINGS_RTK_RESET_RTD1619_H
+
+#define CC_RSTN_REG_BANK_1       (0x0000)
+#define CC_RSTN_REG_BANK_2       (0x0100)
+#define CC_RSTN_REG_BANK_3       (0x0200)
+#define CC_RSTN_REG_BANK_4       (0x0300)
+#define CC_RSTN_REG_BANK_6       (0x0400)
+#define CC_RSTN_REG_BANK_7       (0x0500)
+
+#define CC_RSTN_MISC             (CC_RSTN_REG_BANK_1 | 0x00)
+#define CC_RSTN_DIP              (CC_RSTN_REG_BANK_1 | 0x02)
+#define CC_RSTN_GSPI             (CC_RSTN_REG_BANK_1 | 0x04)
+#define CC_RSTN_SDS              (CC_RSTN_REG_BANK_1 | 0x06)
+#define CC_RSTN_SDS_REG          (CC_RSTN_REG_BANK_1 | 0x08)
+#define CC_RSTN_SDS_PHY          (CC_RSTN_REG_BANK_1 | 0x0a)
+#define CC_RSTN_VE1              (CC_RSTN_REG_BANK_1 | 0x0c)
+#define CC_RSTN_VE2              (CC_RSTN_REG_BANK_1 | 0x0e)
+#define CC_RSTN_R2RDSC_A00       (CC_RSTN_REG_BANK_1 | 0x10)
+#define CC_RSTN_RSA              (CC_RSTN_REG_BANK_1 | 0x12)
+#define CC_RSTN_GPU              (CC_RSTN_REG_BANK_1 | 0x14)
+#define CC_RSTN_DC_PHY           (CC_RSTN_REG_BANK_1 | 0x16)
+#define CC_RSTN_DCPHY_CRT        (CC_RSTN_REG_BANK_1 | 0x18)
+#define CC_RSTN_LSADC            (CC_RSTN_REG_BANK_1 | 0x1a)
+#define CC_RSTN_SE               (CC_RSTN_REG_BANK_1 | 0x1c)
+#define CC_RSTN_HSE_A00          (CC_RSTN_REG_BANK_1 | 0x1e)
+
+#define CC_RSTN_JPEG             (CC_RSTN_REG_BANK_2 | 0x00)
+#define CC_RSTN_SD               (CC_RSTN_REG_BANK_2 | 0x02)
+#define CC_RSTN_EMMC_A00         (CC_RSTN_REG_BANK_2 | 0x04)
+#define CC_RSTN_SDIO             (CC_RSTN_REG_BANK_2 | 0x06)
+#define CC_RSTN_PCR_CNT          (CC_RSTN_REG_BANK_2 | 0x08)
+#define CC_RSTN_PCIE0_STITCH     (CC_RSTN_REG_BANK_2 | 0x0a)
+#define CC_RSTN_PCIE0_PHY        (CC_RSTN_REG_BANK_2 | 0x0c)
+#define CC_RSTN_PCIE0            (CC_RSTN_REG_BANK_2 | 0x0e)
+#define CC_RSTN_PCIE0_CORE       (CC_RSTN_REG_BANK_2 | 0x10)
+#define CC_RSTN_PCIE0_POWER      (CC_RSTN_REG_BANK_2 | 0x12)
+#define CC_RSTN_PCIE0_NONSTITCH  (CC_RSTN_REG_BANK_2 | 0x14)
+#define CC_RSTN_PCIE0_PHY_MDIO   (CC_RSTN_REG_BANK_2 | 0x16)
+#define CC_RSTN_PCIE0_SGMII_MDIO (CC_RSTN_REG_BANK_2 | 0x18)
+#define CC_RSTN_UR2              (CC_RSTN_REG_BANK_2 | 0x1a)
+#define CC_RSTN_UR1              (CC_RSTN_REG_BANK_2 | 0x1c)
+#define CC_RSTN_MISC_SC0         (CC_RSTN_REG_BANK_2 | 0x1e)
+
+#define CC_RSTN_AE               (CC_RSTN_REG_BANK_3 | 0x00)
+#define CC_RSTN_CABLERX          (CC_RSTN_REG_BANK_3 | 0x02)
+#define CC_RSTN_MD_A00           (CC_RSTN_REG_BANK_3 | 0x04)
+#define CC_RSTN_TP_A00           (CC_RSTN_REG_BANK_3 | 0x06)
+#define CC_RSTN_NF_A00           (CC_RSTN_REG_BANK_3 | 0x08)
+#define CC_RSTN_MISC_SC1         (CC_RSTN_REG_BANK_3 | 0x0a)
+#define CC_RSTN_I2C_3            (CC_RSTN_REG_BANK_3 | 0x0c)
+#define CC_RSTN_FAN              (CC_RSTN_REG_BANK_3 | 0x0e)
+#define CC_RSTN_TVE              (CC_RSTN_REG_BANK_3 | 0x10)
+#define CC_RSTN_AIO              (CC_RSTN_REG_BANK_3 | 0x12)
+#define CC_RSTN_VO               (CC_RSTN_REG_BANK_3 | 0x14)
+#define CC_RSTN_MIPI_A00         (CC_RSTN_REG_BANK_3 | 0x16)
+#define CC_RSTN_HDMIRX           (CC_RSTN_REG_BANK_3 | 0x18)
+#define CC_RSTN_HDMIRX_WRAP      (CC_RSTN_REG_BANK_3 | 0x1a)
+#define CC_RSTN_HDMI             (CC_RSTN_REG_BANK_3 | 0x1c)
+#define CC_RSTN_DISP             (CC_RSTN_REG_BANK_3 | 0x1e)
+
+#define CC_RSTN_SATA_PHY_POW1    (CC_RSTN_REG_BANK_4 | 0x00)
+#define CC_RSTN_SATA_PHY_POW0    (CC_RSTN_REG_BANK_4 | 0x02)
+#define CC_RSTN_SATA_MDIO1       (CC_RSTN_REG_BANK_4 | 0x04)
+#define CC_RSTN_SATA_MDIO0       (CC_RSTN_REG_BANK_4 | 0x06)
+#define CC_RSTN_SATA_WRAP        (CC_RSTN_REG_BANK_4 | 0x08)
+#define CC_RSTN_SATA_MAC_P1      (CC_RSTN_REG_BANK_4 | 0x0a)
+#define CC_RSTN_SATA_MAC_P0      (CC_RSTN_REG_BANK_4 | 0x0c)
+#define CC_RSTN_SATA_MAC_COM     (CC_RSTN_REG_BANK_4 | 0x0e)
+#define CC_RSTN_PCIE1_STITCH     (CC_RSTN_REG_BANK_4 | 0x10)
+#define CC_RSTN_PCIE1_PHY        (CC_RSTN_REG_BANK_4 | 0x12)
+#define CC_RSTN_PCIE1            (CC_RSTN_REG_BANK_4 | 0x14)
+#define CC_RSTN_PCIE1_CORE       (CC_RSTN_REG_BANK_4 | 0x16)
+#define CC_RSTN_PCIE1_POWER      (CC_RSTN_REG_BANK_4 | 0x18)
+#define CC_RSTN_PCIE1_NONSTITCH  (CC_RSTN_REG_BANK_4 | 0x1a)
+#define CC_RSTN_PCIE1_PHY_MDIO   (CC_RSTN_REG_BANK_4 | 0x1c)
+#define CC_RSTN_HDMITOP          (CC_RSTN_REG_BANK_4 | 0x1e)
+
+#define CC_RSTN_HSE              (CC_RSTN_REG_BANK_6 | 0x06)
+#define CC_RSTN_R2RDSC           (CC_RSTN_REG_BANK_6 | 0x08)
+#define CC_RSTN_EMMC             (CC_RSTN_REG_BANK_6 | 0x0a)
+#define CC_RSTN_NF               (CC_RSTN_REG_BANK_6 | 0x0c)
+#define CC_RSTN_MD               (CC_RSTN_REG_BANK_6 | 0x0e)
+#define CC_RSTN_TPB              (CC_RSTN_REG_BANK_6 | 0x18)
+#define CC_RSTN_TP               (CC_RSTN_REG_BANK_6 | 0x1a)
+#define CC_RSTN_MIPI             (CC_RSTN_REG_BANK_6 | 0x1c)
+
+#define CC_RSTN_TPB_A00          (CC_RSTN_REG_BANK_7 | 0x00)
+#define CC_RSTN_I2C_4            (CC_RSTN_REG_BANK_7 | 0x02)
+#define CC_RSTN_I2C_5            (CC_RSTN_REG_BANK_7 | 0x04)
+#define CC_RSTN_TSIO             (CC_RSTN_REG_BANK_7 | 0x06)
+#define CC_RSTN_VE3              (CC_RSTN_REG_BANK_7 | 0x08)
+#define CC_RSTN_EDP              (CC_RSTN_REG_BANK_7 | 0x0a)
+
+/* 0x98007088 */
+#define IC_RSTN_VFD              0x0000
+#define IC_RSTN_IR               0x0001
+#define IC_RSTN_CEC0             0x0002
+#define IC_RSTN_CEC1             0x0003
+#define IC_RSTN_DP               0x0004
+#define IC_RSTN_CBUSTX           0x0005
+#define IC_RSTN_CBUSRX           0x0006
+#define IC_RSTN_EFUSE            0x0007
+#define IC_RSTN_UR0              0x0008
+#define IC_RSTN_GMAC             0x0009
+#define IC_RSTN_GPHY             0x000a
+#define IC_RSTN_I2C_0            0x000b
+#define IC_RSTN_I2C_1            0x000c
+#define IC_RSTN_CBUS             0x000d
+#define IC_RSTN_USB_DRD          0x000e
+#define IC_RSTN_USB_HOST         0x000f
+#define IC_RSTN_USB_PHY_0        0x0010
+#define IC_RSTN_USB_PHY_1        0x0011
+#define IC_RSTN_USB_PHY_2        0x0012
+#define IC_RSTN_USB              0x0013
+#define IC_RSTN_TYPE_C           0x0014
+#define IC_RSTN_USB_U3_HOST      0x0015
+#define IC_RSTN_USB3_PHY0_POW    0x0016
+#define IC_RSTN_USB3_P0_MDIO     0x0017
+#define IC_RSTN_USB3_PHY1_POW    0x0018
+#define IC_RSTN_USB3_P1_MDIO     0x0019
+
+#endif
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs
  2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
  2019-12-03  7:35 ` [PATCH 1/6] dt-bindings: clock: add bindings for RTD1619 clocks James Tai
  2019-12-03  7:35 ` [PATCH 2/6] dt-bindings: reset: add bindings for rtd1619 reset controls James Tai
@ 2019-12-03  7:35 ` James Tai
  2019-12-03  7:35 ` [PATCH 4/6] clk: realtek: add reset controller " James Tai
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: James Tai @ 2019-12-03  7:35 UTC (permalink / raw)
  To: Andreas Färber
  Cc: cylee12, Stephen Boyd, Michael Turquette, Palmer Dabbelt,
	linux-kernel, linux-mediatek, Paul Walmsley, Matthias Brugger,
	linux-riscv, linux-clk, linux-arm-kernel

From: cylee12 <cylee12@realtek.com>

This patch adds common clock support for Realtek SoCs, including PLLs,
Mux clocks and Gate clocks.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 drivers/clk/Kconfig                   |   1 +
 drivers/clk/Makefile                  |   1 +
 drivers/clk/realtek/Kconfig           |  10 +
 drivers/clk/realtek/Makefile          |   9 +
 drivers/clk/realtek/clk-pll-dif.c     |  81 ++++++
 drivers/clk/realtek/clk-pll-psaud.c   | 120 ++++++++
 drivers/clk/realtek/clk-pll.c         | 400 ++++++++++++++++++++++++++
 drivers/clk/realtek/clk-pll.h         | 151 ++++++++++
 drivers/clk/realtek/clk-regmap-gate.c |  89 ++++++
 drivers/clk/realtek/clk-regmap-gate.h |  26 ++
 drivers/clk/realtek/clk-regmap-mux.c  |  63 ++++
 drivers/clk/realtek/clk-regmap-mux.h  |  26 ++
 drivers/clk/realtek/common.c          | 320 +++++++++++++++++++++
 drivers/clk/realtek/common.h          | 123 ++++++++
 14 files changed, 1420 insertions(+)
 create mode 100644 drivers/clk/realtek/Kconfig
 create mode 100644 drivers/clk/realtek/Makefile
 create mode 100644 drivers/clk/realtek/clk-pll-dif.c
 create mode 100644 drivers/clk/realtek/clk-pll-psaud.c
 create mode 100644 drivers/clk/realtek/clk-pll.c
 create mode 100644 drivers/clk/realtek/clk-pll.h
 create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
 create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
 create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
 create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
 create mode 100644 drivers/clk/realtek/common.c
 create mode 100644 drivers/clk/realtek/common.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c44247d0b83e..8e06487440ce 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -317,6 +317,7 @@ source "drivers/clk/mediatek/Kconfig"
 source "drivers/clk/meson/Kconfig"
 source "drivers/clk/mvebu/Kconfig"
 source "drivers/clk/qcom/Kconfig"
+source "drivers/clk/realtek/Kconfig"
 source "drivers/clk/renesas/Kconfig"
 source "drivers/clk/samsung/Kconfig"
 source "drivers/clk/sifive/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 0138fb14e6f8..71ea17f97f7d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
 obj-$(CONFIG_MACH_PISTACHIO)		+= pistachio/
 obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
 obj-$(CONFIG_COMMON_CLK_QCOM)		+= qcom/
+obj-$(CONFIG_COMMON_CLK_REALTEK)	+= realtek/
 obj-y					+= renesas/
 obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
new file mode 100644
index 000000000000..5bca757dddfa
--- /dev/null
+++ b/drivers/clk/realtek/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config COMMON_CLK_REALTEK
+	bool "Clock driver for realtek"
+	select MFD_SYSCON
+
+config CLK_PLL_PSAUD
+	bool
+
+config CLK_PLL_DIF
+	bool
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
new file mode 100644
index 000000000000..050d450db067
--- /dev/null
+++ b/drivers/clk/realtek/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_COMMON_CLK_REALTEK) += clk-rtk.o
+
+clk-rtk-y += common.o
+clk-rtk-y += clk-regmap-mux.o
+clk-rtk-y += clk-regmap-gate.o
+clk-rtk-y += clk-pll.o
+clk-rtk-$(CONFIG_CLK_PLL_PSAUD) += clk-pll-psaud.o
+clk-rtk-$(CONFIG_CLK_PLL_DIF) += clk-pll-dif.o
diff --git a/drivers/clk/realtek/clk-pll-dif.c b/drivers/clk/realtek/clk-pll-dif.c
new file mode 100644
index 000000000000..d19efef2626e
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-dif.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_dif_enable(struct clk_hw *hw)
+{
+	struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+	pr_debug("%pC: %s\n", hw->clk, __func__);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000048);
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00020c00);
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204004ca);
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x00, 0x8000a000);
+	udelay(100);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c00);
+	udelay(50);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c03);
+	udelay(200);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000078);
+	udelay(100);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204084ca);
+
+	/* ssc control */
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000004);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x04, 0x00006800);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x0C, 0x00000000);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x10, 0x00000000);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x08, 0x001e1f98);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000005);
+	pll->status = 1;
+
+	return 0;
+}
+
+static void clk_pll_dif_disable(struct clk_hw *hw)
+{
+	struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+	pr_debug("%pC: %s\n", hw->clk, __func__);
+	clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x04, 0x00080000, 0x0);
+	clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x08, 0x00400C03, 0x0);
+	clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000038, 0x0);
+
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000004);
+	pll->status = 0;
+}
+
+static int clk_pll_dif_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+	return pll->status;
+}
+
+static void clk_pll_dif_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_pll_dif_disable(hw);
+}
+
+const struct clk_ops clk_pll_dif_ops = {
+	.enable           = clk_pll_dif_enable,
+	.disable          = clk_pll_dif_disable,
+	.disable_unused   = clk_pll_dif_disable_unused,
+	.is_enabled       = clk_pll_dif_is_enabled,
+};
+
diff --git a/drivers/clk/realtek/clk-pll-psaud.c b/drivers/clk/realtek/clk-pll-psaud.c
new file mode 100644
index 000000000000..757a3320ebb7
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-psaud.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * clk-pll-psaud.c - PLL_PSAUDXA
+ *
+ * Copyright (c) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_psaud_enable(struct clk_hw *hw)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 mask = 0, val = 0;
+
+	if (pll->id == CLK_PLL_PSAUD1A) {
+		mask = 0x3;
+		val = 0x1;
+	} else {
+		mask = 0xc;
+		val = 0x4;
+	}
+	clk_regmap_update(&pll->clkr, pll->reg + 4, mask, val);
+	return 0;
+}
+
+static void clk_pll_psaud_disable(struct clk_hw *hw)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 mask = 0, val = 0;
+
+	if (pll->id == CLK_PLL_PSAUD1A) {
+		mask = 0x3;
+		val = 0x3;
+	} else {
+		mask = 0xc;
+		val = 0xc;
+	}
+	clk_regmap_update(&pll->clkr, pll->reg + 4, mask, val);
+}
+
+static void clk_pll_psaud_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_pll_psaud_disable(hw);
+}
+
+static int clk_pll_psaud_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 val;
+
+	val = clk_regmap_read(&pll->clkr, pll->reg + 4);
+	if (pll->id == CLK_PLL_PSAUD1A)
+		val &= 0x3;
+	else
+		val >>= 2;
+	return val == 0x1;
+}
+
+static long clk_pll_psaud_round_rate(struct clk_hw *hw,
+				     unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	return 49192000;
+}
+
+static int clk_pll_psaud_set_rate(struct clk_hw *hw,
+				  unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 rsel = 0;
+	u32 mask = 0, val = 0;
+
+	if (WARN_ON_ONCE(rate != 45158400 && rate != 49192000))
+		return -EINVAL;
+
+	if (rate == 45158400)
+		rsel = 1;
+	if (pll->id == CLK_PLL_PSAUD1A) {
+		val  = 0x6a0 | (rsel << 8);
+		mask = 0x7e0;
+	} else {
+		val  = 0x19 | (rsel << 2);
+		mask = 0x1f;
+	}
+	clk_regmap_update(&pll->clkr, pll->reg, mask, val);
+	return 0;
+}
+
+static unsigned long clk_pll_psaud_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 val;
+	u32 rsel = 0;
+
+	val = clk_regmap_read(&pll->clkr, pll->reg);
+
+	if (pll->id == CLK_PLL_PSAUD1A)
+		rsel = !!(val & BIT(8));
+	else
+		rsel = !!(val & BIT(2));
+
+	return rsel ? 45158400 : 49192000;
+}
+
+const struct clk_ops clk_pll_psaud_ops = {
+	.enable         = clk_pll_psaud_enable,
+	.disable        = clk_pll_psaud_disable,
+	.disable_unused = clk_pll_psaud_disable_unused,
+	.is_enabled     = clk_pll_psaud_is_enabled,
+	.set_rate       = clk_pll_psaud_set_rate,
+	.round_rate     = clk_pll_psaud_round_rate,
+	.recalc_rate    = clk_pll_psaud_recalc_rate,
+};
+
diff --git a/drivers/clk/realtek/clk-pll.c b/drivers/clk/realtek/clk-pll.c
new file mode 100644
index 000000000000..dd90bfc011ec
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2018 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_debug_rate_u64_set(void *data, u64 val)
+{
+	struct clk_hw *hw = data;
+
+	clk_set_rate(hw->clk, (unsigned long)(val));
+	return 0;
+}
+
+static int clk_pll_debug_rate_u64_get(void *data, u64 *val)
+{
+	struct clk_hw *hw = data;
+
+	*val = (u64)clk_get_rate(hw->clk);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_pll_debut_rate_op, clk_pll_debug_rate_u64_get,
+			clk_pll_debug_rate_u64_set, "%llu\n");
+
+static void clk_pll_debug_init(struct clk_hw *hw, struct dentry *d)
+{
+	debugfs_create_file("debug_rate", 0644, d, hw, &clk_pll_debut_rate_op);
+}
+
+static const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
+						  unsigned long rate)
+{
+	unsigned long best_rate = 0;
+	const struct freq_table *best = NULL;
+
+	for ( ; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+		if (ftbl->rate == rate)
+			return ftbl;
+
+		if (ftbl->rate > rate)
+			continue;
+
+		if ((rate - best_rate) > (rate - ftbl->rate)) {
+			best_rate = ftbl->rate;
+			best = ftbl;
+		}
+	}
+
+	return best;
+}
+
+static const struct freq_table *ftbl_find_by_val(const struct freq_table *ftbl,
+						 uint32_t value)
+{
+	while (!IS_FREQ_TABLE_END(ftbl)) {
+		if (ftbl->val == (value))
+			return ftbl;
+		ftbl++;
+	}
+	return NULL;
+};
+
+static const struct div_table *dtbl_find_by_rate(const struct div_table *dtbl,
+						 unsigned long rate)
+{
+	while (!IS_DIV_TABLE_END(dtbl)) {
+		if (rate >= dtbl->rate)
+			return dtbl;
+		dtbl++;
+	}
+	return NULL;
+}
+
+static const struct div_table *dtbl_find_by_val(const struct div_table *dtbl,
+						uint32_t val)
+{
+	while (!IS_DIV_TABLE_END(dtbl)) {
+		if (val == dtbl->val)
+			return dtbl;
+		dtbl++;
+	}
+	return NULL;
+}
+
+static void __clk_pll_set_pow_reg(struct clk_pll *clkp, int on)
+{
+	uint32_t pow = (clkp->pow_loc == CLK_PLL_CONF_POW_LOC_CTL3) ? 0x8 : 0x4;
+
+	if (on) {
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + pow, 0x7, 0x3);
+		if (clkp->freq_loc == CLK_PLL_CONF_FREQ_LOC_SSC1) {
+			uint32_t val;
+
+			val = clk_regmap_read(&clkp->clkr, clkp->ssc_ofs + 0x0);
+
+			/*
+			 * For those PLL with SCC used only the default
+			 * freq, the oc_en would nerver to be set.
+			 * Help to set it here.
+			 */
+			if ((val & 0x7) != 0x5)
+				clk_regmap_update(&clkp->clkr,
+					clkp->ssc_ofs + 0x0, 0x7, 0x5);
+		}
+		udelay(200);
+	} else {
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + pow, 0x7, 0x4);
+	}
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+
+	if (clk_pll_has_pow(clkp))
+		__clk_pll_set_pow_reg(clkp, 1);
+	return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+
+	if (clk_pll_has_pow(clkp))
+		__clk_pll_set_pow_reg(clkp, 0);
+}
+
+static void clk_pll_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_pll_disable(hw);
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	uint32_t pow;
+	uint32_t val;
+
+	if (!clk_pll_has_pow(clkp))
+		return -EINVAL;
+
+	pow = (clkp->pow_loc == CLK_PLL_CONF_POW_LOC_CTL3) ? 0x8 : 0x4;
+	val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + pow);
+	return !!(val & 0x1);
+}
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *parent_rate)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	const struct freq_table *ftblv = NULL;
+
+	ftblv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+	return ftblv ? ftblv->rate : 0;
+}
+
+static uint32_t __clk_pll_freq_get(struct clk_pll *clkp)
+{
+	uint32_t val = 0;
+
+	switch (clkp->freq_loc) {
+	case CLK_PLL_CONF_FREQ_LOC_CTL1:
+		val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + 0x0);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_CTL2:
+		val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + 0x4);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_SSC1:
+		val = clk_regmap_read(&clkp->clkr, clkp->ssc_ofs + 0x4);
+		break;
+
+	default:
+		break;
+	}
+	return val & clkp->freq_mask;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	unsigned long flags = 0;
+	const struct freq_table *fv;
+	uint32_t val;
+
+	flags = clk_pll_lock(clkp);
+	val = __clk_pll_freq_get(clkp);
+	clk_pll_unlock(clkp, flags);
+
+	fv = ftbl_find_by_val(clkp->freq_tbl, val);
+	return fv ? fv->rate : 0;
+}
+
+static inline int __clk_pll_freq_set(struct clk_pll *clkp, uint32_t val)
+{
+	struct clk_hw *hw = &clkp->clkr.hw;
+	int ret = 0;
+	uint32_t mask = clkp->freq_mask;
+	uint32_t pollval;
+
+	switch (clkp->freq_loc) {
+	case CLK_PLL_CONF_FREQ_LOC_CTL1:
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs, mask, val);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_CTL2:
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x4, mask, val);
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x8, 0x1, 0x0);
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x8, 0x1, 0x1);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_SSC1:
+		clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x0, 0x7, 0x4);
+		clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x4, mask, val);
+		clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x0, 0x7, 0x5);
+
+		if (clk_pll_has_pow(clkp) && !clk_pll_is_enabled(hw))
+			return 0;
+
+		ret = regmap_read_poll_timeout(clkp->clkr.regmap,
+					       clkp->ssc_ofs + 0x1c,
+					       pollval, pollval & BIT(20),
+					       0, 2000);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	unsigned long flags = 0;
+	const struct freq_table *fv;
+	int ret = 0;
+
+	fv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+	if (!fv)
+		return -EINVAL;
+
+	pr_debug("%pC: %s: rate=%ld, val=0x%08x\n", hw->clk, __func__,
+		 fv->rate, fv->val);
+
+	flags = clk_pll_lock(clkp);
+	ret = __clk_pll_freq_set(clkp, fv->val);
+	clk_pll_unlock(clkp, flags);
+	if (ret)
+		pr_warn("%pC %s: failed to set freq: %d\n", hw->clk, __func__,
+			ret);
+	return ret;
+}
+
+static void __clk_pll_div_set(struct clk_pll_div *clkpd, uint32_t val)
+{
+	uint32_t m = (BIT(clkpd->div_width) - 1) << clkpd->div_shift;
+	uint32_t s = clkpd->div_shift;
+
+	clk_regmap_update(&clkpd->clkp.clkr, clkpd->div_ofs, m, val << s);
+}
+
+static uint32_t __clk_pll_div_get(struct clk_pll_div *clkpd)
+{
+	uint32_t m = (BIT(clkpd->div_width) - 1) << clkpd->div_shift;
+	uint32_t s = clkpd->div_shift;
+
+	return (clk_regmap_read(&clkpd->clkp.clkr, clkpd->div_ofs) & m) >> s;
+}
+
+static long clk_pll_div_round_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long *parent_rate)
+{
+	struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+	const struct div_table *dv;
+
+	/* lookup div in dtbl */
+	dv = dtbl_find_by_rate(clkpd->div_tbl, rate);
+	if (!dv)
+		return 0;
+
+	rate *= dv->div;
+	rate = clk_pll_round_rate(hw, rate, parent_rate);
+	return rate / dv->div;
+}
+
+static unsigned long clk_pll_div_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+	unsigned long rate;
+	const struct div_table *dv;
+	uint32_t val;
+
+	rate = clk_pll_recalc_rate(hw, parent_rate);
+
+	val = __clk_pll_div_get(clkpd);
+	dv = dtbl_find_by_val(clkpd->div_tbl, val);
+	if (!dv)
+		return 0;
+
+	rate /= dv->div;
+	pr_debug("%pC: %s: current rate=%lu, div=%d, reg_val=0x%x\n",
+		 hw->clk, __func__, rate, dv->div, val);
+
+	return rate;
+}
+
+static int clk_pll_div_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct clk *clk = hw->clk;
+	struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+	unsigned long flags;
+	const struct div_table *ndv, *cdv;
+	unsigned long target;
+	uint32_t cur_d;
+	int ret;
+
+	/* find next in the dtbl */
+	ndv = dtbl_find_by_rate(clkpd->div_tbl, rate);
+	if (!ndv)
+		return -EINVAL;
+
+	target = rate * ndv->div;
+
+	/* find current in the dtbl */
+	cur_d = __clk_pll_div_get(clkpd);
+	cdv = dtbl_find_by_val(clkpd->div_tbl, cur_d);
+	if (!cdv)
+		return -EINVAL;
+
+	pr_debug("%pC: rate=%lu, cdv={%d,0x%x}, ndv={%d,0x%x}\n",
+		 clk, rate, cdv->div, cdv->val, ndv->div, ndv->val);
+
+	flags = clk_pll_div_lock(clkpd);
+
+	/* workaround to prevent glitch */
+#ifdef CONFIG_COMMON_CLK_RTD129X
+	if ((&clkpd->clkp.flags & CLK_PLL_DIV_WORKAROUND) &&
+		ndv->val != cdv->val && (ndv->val == 1 || cdv->val == 1)) {
+
+		pr_debug("%pC: apply rate=%u\n", clk, 1000000000);
+		clk_pll_set_rate(hw, 1000000000, parent_rate);
+
+		pr_debug("%pC: apply dv={%d, 0x%x}\n", clk, ndv->div, ndv->val);
+		__clk_pll_div_set(clkpd, ndv->val);
+		cdv = ndv;
+	}
+#endif
+
+	if (ndv->div > cdv->div)
+		__clk_pll_div_set(clkpd, ndv->val);
+	ret = clk_pll_set_rate(hw, target, parent_rate);
+	if (ndv->div < cdv->div)
+		__clk_pll_div_set(clkpd, ndv->val);
+
+	clk_pll_div_unlock(clkpd, flags);
+
+	return ret;
+}
+
+const struct clk_ops clk_pll_ops = {
+	.debug_init       = clk_pll_debug_init,
+	.round_rate       = clk_pll_round_rate,
+	.recalc_rate      = clk_pll_recalc_rate,
+	.set_rate         = clk_pll_set_rate,
+	.enable           = clk_pll_enable,
+	.disable          = clk_pll_disable,
+	.disable_unused   = clk_pll_disable_unused,
+	.is_enabled       = clk_pll_is_enabled,
+};
+
+const struct clk_ops clk_pll_div_ops = {
+	.debug_init       = clk_pll_debug_init,
+	.round_rate       = clk_pll_div_round_rate,
+	.recalc_rate      = clk_pll_div_recalc_rate,
+	.set_rate         = clk_pll_div_set_rate,
+	.enable           = clk_pll_enable,
+	.disable          = clk_pll_disable,
+	.disable_unused   = clk_pll_disable_unused,
+	.is_enabled       = clk_pll_is_enabled,
+};
+
diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
new file mode 100644
index 000000000000..03b4d0391c31
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_PLL_H
+#define __CLK_REALTEK_CLK_PLL_H
+
+#include "common.h"
+
+struct freq_table {
+	uint32_t val;
+	unsigned long rate;
+};
+
+#define FREQ_TABLE_END                  { .rate = 0 }
+#define IS_FREQ_TABLE_END(_f)           ((_f)->rate == 0)
+
+struct div_table {
+	unsigned long rate;
+	uint32_t div;
+	uint32_t val;
+};
+
+#define DIV_TABLE_END                   { .rate = 0 }
+#define IS_DIV_TABLE_END(_d)            ((_d)->rate == 0)
+
+struct clk_pll {
+	struct clk_regmap clkr;
+
+
+	int pll_ofs;
+	int ssc_ofs;
+
+	const struct freq_table *freq_tbl;
+	uint32_t freq_mask;
+	uint32_t freq_loc;
+#define CLK_PLL_CONF_FREQ_LOC_CTL1      1
+#define CLK_PLL_CONF_FREQ_LOC_CTL2      2
+#define CLK_PLL_CONF_FREQ_LOC_SSC1      3
+
+	uint32_t pow_loc;
+#define CLK_PLL_CONF_NO_POW             0
+#define CLK_PLL_CONF_POW_LOC_CTL2       1
+#define CLK_PLL_CONF_POW_LOC_CTL3       2
+
+	spinlock_t *lock;
+
+	uint32_t flags;
+};
+
+#define to_clk_pll(_hw) container_of(to_clk_regmap(_hw), struct clk_pll, clkr)
+#define __clk_pll_hw(_ptr)  __clk_regmap_hw(&(_ptr)->clkr)
+
+/* clk_pll flags */
+#define CLK_PLL_DIV_WORKAROUND          BIT(2)
+
+
+static inline bool clk_pll_has_pow(struct clk_pll *pll)
+{
+	if (pll->pow_loc != CLK_PLL_CONF_NO_POW)
+		return true;
+	return false;
+}
+
+static inline unsigned long clk_pll_lock(struct clk_pll *pll)
+{
+	unsigned long flags = 0;
+
+	if (pll->lock)
+		spin_lock_irqsave(pll->lock, flags);
+	return flags;
+}
+
+static inline void clk_pll_unlock(struct clk_pll *pll, unsigned long flags)
+{
+	if (pll->lock)
+		spin_unlock_irqrestore(pll->lock, flags);
+}
+
+struct clk_pll_div {
+	struct clk_pll clkp;
+	int div_ofs;
+	int div_shift;
+	int div_width;
+	const struct div_table *div_tbl;
+	spinlock_t *lock;
+};
+
+#define to_clk_pll_div(_hw) \
+	container_of(to_clk_pll(_hw), struct clk_pll_div, clkp)
+#define __clk_pll_div_hw(_ptr) __clk_pll_hw(&(_ptr)->clkp)
+
+/* clk_pll_div helper functions */
+static inline unsigned long clk_pll_div_lock(struct clk_pll_div *plld)
+{
+	unsigned long flags = 0;
+
+	if (plld->lock)
+		spin_lock_irqsave(plld->lock, flags);
+	return flags;
+}
+
+static inline void clk_pll_div_unlock(struct clk_pll_div *plld,
+	unsigned long flags)
+{
+	if (plld->lock)
+		spin_unlock_irqrestore(plld->lock, flags);
+}
+
+extern const struct clk_ops clk_pll_ops;
+extern const struct clk_ops clk_pll_div_ops;
+
+#ifdef CONFIG_CLK_PLL_PSAUD
+
+struct clk_pll_psaud {
+	struct clk_regmap clkr;
+	int id;
+	int reg;
+	spinlock_t *lock;
+};
+
+#define to_clk_pll_psaud(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_pll_psaud, clkr)
+#define __clk_pll_psaud_hw(_ptr)  __clk_regmap_hw(&(_ptr)->clkr)
+extern const struct clk_ops clk_pll_psaud_ops;
+
+#define CLK_PLL_PSAUD1A       (0x1)
+#define CLK_PLL_PSAUD2A       (0x2)
+
+#endif /* CONFIG_CLK_PLL_PSAUD */
+
+#ifdef CONFIG_CLK_PLL_DIF
+
+struct clk_pll_dif {
+	struct clk_regmap clkr;
+	int pll_ofs;
+	int ssc_ofs;
+	uint32_t status;
+	spinlock_t *lock;
+};
+#define to_clk_pll_dif(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_pll_dif, clkr)
+#define __clk_pll_dif_hw(_ptr)  __clk_regmap_hw(&(_ptr)->clkr)
+
+extern const struct clk_ops clk_pll_dif_ops;
+#endif /* CONFIG_CLK_PLL_DIF */
+
+
+#endif /* __CLK_REALTEK_CLK_PLL_H */
diff --git a/drivers/clk/realtek/clk-regmap-gate.c b/drivers/clk/realtek/clk-regmap-gate.c
new file mode 100644
index 000000000000..5f4a6a98da94
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include "clk-regmap-gate.h"
+
+static int clk_regmap_gate_enable(struct clk_hw *hw)
+{
+	struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+	unsigned long flags = 0;
+	unsigned int mask;
+	unsigned int val;
+
+	if (clkg->lock)
+		spin_lock_irqsave(clkg->lock, flags);
+
+	mask = BIT(clkg->bit_idx);
+	val  = BIT(clkg->bit_idx);
+
+	if (clkg->write_en) {
+		mask |= BIT(clkg->bit_idx + 1);
+		val  |= BIT(clkg->bit_idx + 1);
+	}
+
+	clk_regmap_update(&clkg->clkr, clkg->gate_ofs, mask, val);
+
+	if (clkg->lock)
+		spin_unlock_irqrestore(clkg->lock, flags);
+
+	return 0;
+}
+
+static void clk_regmap_gate_disable(struct clk_hw *hw)
+{
+	struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+	unsigned long flags = 0;
+	unsigned int mask;
+	unsigned int val;
+
+	if (clkg->lock)
+		spin_lock_irqsave(clkg->lock, flags);
+
+	mask = BIT(clkg->bit_idx);
+	val  = 0;
+
+	if (clkg->write_en) {
+		mask |= BIT(clkg->bit_idx + 1);
+		val  |= BIT(clkg->bit_idx + 1);
+	}
+
+	clk_regmap_update(&clkg->clkr, clkg->gate_ofs, mask, val);
+
+	if (clkg->lock)
+		spin_unlock_irqrestore(clkg->lock, flags);
+}
+
+static void clk_regmap_gate_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_regmap_gate_disable(hw);
+}
+
+static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
+{
+	struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+	int ret;
+	unsigned long flags = 0;
+
+	if (clkg->lock)
+		spin_lock_irqsave(clkg->lock, flags);
+
+	ret = clk_regmap_read(&clkg->clkr, clkg->gate_ofs) & BIT(clkg->bit_idx);
+
+	if (clkg->lock)
+		spin_unlock_irqrestore(clkg->lock, flags);
+
+	return !!ret;
+}
+
+const struct clk_ops clk_regmap_gate_ops = {
+	.enable = clk_regmap_gate_enable,
+	.disable = clk_regmap_gate_disable,
+	.disable_unused = clk_regmap_gate_disable_unused,
+	.is_enabled = clk_regmap_gate_is_enabled,
+};
diff --git a/drivers/clk/realtek/clk-regmap-gate.h b/drivers/clk/realtek/clk-regmap-gate.h
new file mode 100644
index 000000000000..7643e0193177
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_MMIO_GATE_H
+#define __CLK_REALTEK_CLK_MMIO_GATE_H
+
+#include "common.h"
+
+struct clk_regmap_gate {
+	struct clk_regmap clkr;
+	int gate_ofs;
+	uint8_t bit_idx;
+	spinlock_t *lock;
+	int write_en:1;
+};
+
+#define to_clk_regmap_gate(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_regmap_gate, clkr)
+#define __clk_regmap_gate_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+extern const struct clk_ops clk_regmap_gate_ops;
+
+#endif
diff --git a/drivers/clk/realtek/clk-regmap-mux.c b/drivers/clk/realtek/clk-regmap-mux.c
new file mode 100644
index 000000000000..bd7eb706e12a
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk-regmap-mux.h"
+
+static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
+{
+	struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+	int num_parents = clk_hw_get_num_parents(hw);
+	u32 val;
+	unsigned long flags = 0;
+
+	if (clkm->lock)
+		spin_lock_irqsave(clkm->lock, flags);
+
+	val = clk_regmap_read(&clkm->clkr, clkm->mux_ofs) >> clkm->shift;
+	if (clkm->lock)
+		spin_unlock_irqrestore(clkm->lock, flags);
+
+	val &= clkm->mask;
+	if (val >= num_parents)
+		return -EINVAL;
+
+	return val;
+}
+
+static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+	unsigned long flags = 0;
+
+	if (clkm->lock)
+		spin_lock_irqsave(clkm->lock, flags);
+
+	clk_regmap_update(&clkm->clkr, clkm->mux_ofs, clkm->mask << clkm->shift,
+		index << clkm->shift);
+
+	if (clkm->lock)
+		spin_unlock_irqrestore(clkm->lock, flags);
+	return 0;
+}
+
+const struct clk_ops clk_regmap_mux_ops = {
+	.get_parent = clk_regmap_mux_get_parent,
+	.set_parent = clk_regmap_mux_set_parent,
+	.determine_rate = __clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
+
+const struct clk_ops clk_regmap_mux_ro_ops = {
+	.get_parent = clk_regmap_mux_get_parent,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
+
diff --git a/drivers/clk/realtek/clk-regmap-mux.h b/drivers/clk/realtek/clk-regmap-mux.h
new file mode 100644
index 000000000000..36895f03c0f4
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_MMIO_MUX_H
+#define __CLK_REALTEK_CLK_MMIO_MUX_H
+
+#include "common.h"
+
+struct clk_regmap_mux {
+	struct clk_regmap clkr;
+	int mux_ofs;
+	unsigned int mask;
+	unsigned int shift;
+	spinlock_t *lock;
+};
+
+#define to_clk_regmap_mux(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_regmap_mux, clkr)
+#define __clk_regmap_mux_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+extern const struct clk_ops clk_regmap_mux_ops;
+
+#endif
diff --git a/drivers/clk/realtek/common.c b/drivers/clk/realtek/common.c
new file mode 100644
index 000000000000..0c5dc5ce4682
--- /dev/null
+++ b/drivers/clk/realtek/common.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include "common.h"
+#include "clk-pll.h"
+#include "clk-regmap-gate.h"
+#include "clk-regmap-mux.h"
+
+static int rtk_clk_suspend(struct device *dev)
+{
+	struct rtk_clk_data *data = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; i < data->pm_data_num; i++) {
+		struct clk_pm_data *pm_data = &data->pm_data[i];
+
+		regmap_read(data->regmap, pm_data->ofs, &pm_data->val);
+	}
+	return 0;
+}
+
+static int rtk_clk_resume(struct device *dev)
+{
+	struct rtk_clk_data *data = dev_get_drvdata(dev);
+	int i;
+
+	for (i = data->pm_data_num - 1; i >= 0; i--) {
+		struct clk_pm_data *pm_data = &data->pm_data[i];
+		uint32_t val = pm_data->val;
+		uint32_t mask = ~0x0;
+
+		if (pm_data->write_en_bits)
+			val |= pm_data->write_en_bits;
+		if (pm_data->ignore_bits)
+			mask &= ~pm_data->ignore_bits;
+		dev_info(dev, "resuming: ofs=%03x, vs=%08x vr=%08x m=%08x\n",
+			pm_data->ofs, pm_data->val, val, mask);
+		regmap_update_bits(data->regmap, pm_data->ofs, mask, val);
+	}
+	return 0;
+}
+
+const struct dev_pm_ops rtk_clk_pm_ops = {
+	.suspend = rtk_clk_suspend,
+	.resume  = rtk_clk_resume,
+};
+
+struct rtk_clk_data *alloc_rtk_clk_data(int clk_num)
+{
+	struct rtk_clk_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return NULL;
+
+	data->clk_num = clk_num;
+	data->clk_data.clk_num = clk_num;
+	data->clk_data.clks = kcalloc(clk_num, sizeof(*data->clk_data.clks),
+				      GFP_KERNEL);
+	if (!data->clk_data.clks)
+		goto free_data;
+	return data;
+
+free_data:
+	kfree(data->clk_data.clks);
+	kfree(data);
+	return NULL;
+}
+
+void free_rtk_clk_data(struct rtk_clk_data *data)
+{
+	kfree(data->clk_data.clks);
+	kfree(data);
+}
+
+static inline
+int __cell_clk_add(struct clk_onecell_data *clk_data, int i, struct clk *clk)
+{
+	if (clk_data->clks[i]) {
+		pr_err("%s: failed to add %pC, cell%d is used by %pC\n",
+			__func__, clk, i, clk_data->clks[i]);
+		return -EINVAL;
+	}
+	clk_data->clks[i] = clk;
+	return 0;
+}
+
+#define CLK_TYPE_DEFAULT                (0x0)
+#define CLK_TYPE_REGMAP                 (0x8)
+#define CLK_TYPE_REGMAP_PLL             (0x1 | CLK_TYPE_REGMAP)
+#define CLK_TYPE_REGMAP_MUX             (0x2 | CLK_TYPE_REGMAP)
+#define CLK_TYPE_REGMAP_GATE            (0x3 | CLK_TYPE_REGMAP)
+
+static inline int __hw_to_type(struct clk_hw *hw)
+{
+	const struct clk_ops *ops = hw->init->ops;
+
+	if (ops == &clk_pll_ops || ops == &clk_pll_div_ops)
+		return CLK_TYPE_REGMAP_PLL;
+	if (ops == &clk_regmap_mux_ops)
+		return CLK_TYPE_REGMAP_MUX;
+	if (ops == &clk_regmap_gate_ops)
+		return CLK_TYPE_REGMAP_GATE;
+#ifdef CONFIG_CLK_PLL_DIF
+	/*
+	 * clk_pll_dif is not based struct clk_pll, so
+	 * return as CLK_TYPE_REGMAP to setup internal
+	 * clk_reg
+	 */
+	if (ops == &clk_pll_dif_ops)
+		return CLK_TYPE_REGMAP;
+#endif
+#ifdef CONFIG_CLK_PLL_PSAUD
+	/*
+	 * clk_pll_psaud is not based struct clk_pll, so
+	 * return as CLK_TYPE_REGMAP to setup internal
+	 * clk_reg
+	 */
+	if (ops == &clk_pll_psaud_ops)
+		return CLK_TYPE_REGMAP;
+#endif
+
+	return CLK_TYPE_DEFAULT;
+}
+
+static
+struct clk *rtk_clk_register_hw(struct device *dev, struct regmap *regmap,
+				struct clk_hw *hw)
+{
+	int type;
+
+	type = __hw_to_type(hw);
+	if (type & CLK_TYPE_REGMAP) {
+		struct clk_regmap *clkr = to_clk_regmap(hw);
+
+		clkr->regmap = regmap;
+	}
+
+	return clk_register(dev, hw);
+}
+
+int rtk_clk_add_hws(struct device *dev, struct rtk_clk_data *data,
+		    struct clk_hw **hws, int num)
+{
+	struct clk_onecell_data *clk_data = &data->clk_data;
+	struct regmap *regmap = data->regmap;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		struct clk_hw *hw = hws[i];
+		const char *name;
+		struct clk *clk;
+
+		if (IS_ERR(hw))
+			__cell_clk_add(clk_data, i, ERR_CAST(hw));
+		if (IS_ERR_OR_NULL(hw))
+			continue;
+
+		name = hw->init->name;
+		clk = rtk_clk_register_hw(dev, regmap, hw);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to add hw%d(%s): %ld\n", __func__,
+				i, name, PTR_ERR(clk));
+			continue;
+		}
+
+		clk_register_clkdev(clk, name, NULL);
+		__cell_clk_add(clk_data, i, clk);
+	}
+	return 0;
+}
+
+static
+struct clk *rtk_clk_register_composite(struct device *dev,
+				       struct regmap *regmap,
+				       struct clk_composite_data *comp)
+{
+	struct clk_regmap_mux *clkm = NULL;
+	const struct clk_ops *mux_op = NULL;
+	struct clk_hw *mux_hw = NULL;
+	struct clk_regmap_gate *clkg = NULL;
+	const struct clk_ops *gate_op = NULL;
+	struct clk_hw *gate_hw = NULL;
+	struct clk *clk;
+
+	if (comp->mux_ofs != CLK_OFS_INVALID) {
+		clkm = kzalloc(sizeof(*clkm), GFP_KERNEL);
+		if (!clkm) {
+			clk = ERR_PTR(-ENOMEM);
+			goto check_err;
+		}
+
+		clkm->mux_ofs     = comp->mux_ofs;
+		clkm->mask        = BIT(comp->mux_width) - 1;
+		clkm->shift       = comp->mux_shift;
+		clkm->clkr.regmap = regmap;
+
+		mux_op = &clk_regmap_mux_ops;
+		mux_hw = &__clk_regmap_mux_hw(clkm);
+	}
+
+	if (comp->gate_ofs != CLK_OFS_INVALID) {
+		clkg = kzalloc(sizeof(*clkg), GFP_KERNEL);
+		if (!clkg) {
+			clk = ERR_PTR(-ENOMEM);
+			goto check_err;
+		}
+
+		clkg->gate_ofs    = comp->gate_ofs;
+		clkg->bit_idx     = comp->gate_shift;
+		clkg->write_en    = comp->gate_write_en;
+		clkg->clkr.regmap = regmap;
+
+		gate_op = &clk_regmap_gate_ops;
+		gate_hw = &__clk_regmap_gate_hw(clkg);
+	}
+
+	clk = clk_register_composite(NULL, comp->name, comp->parent_names,
+				     comp->num_parents, mux_hw, mux_op,
+				     NULL, NULL, gate_hw, gate_op, comp->flags);
+check_err:
+	if (IS_ERR(clk)) {
+		kfree(clkm);
+		kfree(clkg);
+	}
+	return clk;
+}
+
+int rtk_clk_add_composites(struct device *dev, struct rtk_clk_data *data,
+			   struct clk_composite_data *comps, int num)
+{
+	struct clk_onecell_data *clk_data = &data->clk_data;
+	struct regmap *regmap = data->regmap;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		struct clk_composite_data *comp = &comps[i];
+		const char *name = comp->name;
+		struct clk *clk;
+
+		clk = rtk_clk_register_composite(dev, regmap, comp);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to add composite%d(%s): %ld\n",
+				__func__, i, name, PTR_ERR(clk));
+			continue;
+		}
+
+		clk_register_clkdev(clk, name, NULL);
+		__cell_clk_add(clk_data, comp->id, clk);
+	}
+
+	return 0;
+}
+
+static
+struct clk *rtk_clk_register_gate(struct device *dev, struct regmap *regmap,
+				  struct clk_gate_data *gate)
+{
+	struct clk_regmap_gate *clkg;
+	struct clk_init_data init = { 0 };
+	struct clk_hw *hw;
+
+	clkg = kzalloc(sizeof(*clkg), GFP_KERNEL);
+	if (!clkg)
+		return ERR_PTR(-ENOMEM);
+
+	clkg->gate_ofs    = gate->gate_ofs;
+	clkg->bit_idx     = gate->gate_shift;
+	clkg->write_en    = gate->gate_write_en;
+	clkg->clkr.regmap = regmap;
+
+	init.name         = gate->name;
+	init.ops          = &clk_regmap_gate_ops;
+	init.flags         = gate->flags;
+	if (gate->parent) {
+		init.parent_names = &gate->parent;
+		init.num_parents  = 1;
+	}
+
+	hw = &__clk_regmap_gate_hw(clkg);
+	hw->init = &init;
+	return clk_register(dev, hw);
+}
+
+int rtk_clk_add_gates(struct device *dev, struct rtk_clk_data *data,
+		      struct clk_gate_data *gates, int num)
+{
+	struct clk_onecell_data *clk_data = &data->clk_data;
+	struct regmap *regmap = data->regmap;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		struct clk_gate_data *gate = &gates[i];
+		const char *name = gate->name;
+		struct clk *clk;
+
+		clk = rtk_clk_register_gate(dev, regmap, gate);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to add gate%d(%s): %ld\n", __func__,
+				i, name, PTR_ERR(clk));
+			continue;
+		}
+
+		clk_register_clkdev(clk, name, NULL);
+		__cell_clk_add(clk_data, gate->id, clk);
+	}
+
+	return 0;
+}
+
diff --git a/drivers/clk/realtek/common.h b/drivers/clk/realtek/common.h
new file mode 100644
index 000000000000..c736b254852f
--- /dev/null
+++ b/drivers/clk/realtek/common.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_COMMON_H
+#define __CLK_REALTEK_COMMON_H
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+struct device;
+struct platform_device;
+
+struct clk_regmap {
+	struct clk_hw hw;
+	struct regmap *regmap;
+};
+
+#define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
+#define __clk_regmap_hw(_p) ((_p)->hw)
+
+static inline
+void clk_regmap_write(struct clk_regmap *clkr, uint32_t ofs, uint32_t val)
+{
+	pr_debug("%s: ofs=%03x, val=%08x\n", __func__, ofs, val);
+	regmap_write(clkr->regmap, ofs, val);
+}
+
+static inline
+uint32_t clk_regmap_read(struct clk_regmap *clkr, uint32_t ofs)
+{
+	uint32_t val = 0;
+
+	regmap_read(clkr->regmap, ofs, &val);
+	pr_debug("%s: ofs=%03x, val=%08x\n", __func__, ofs, val);
+	return val;
+}
+
+static inline void clk_regmap_update(struct clk_regmap *clkr, uint32_t ofs,
+				     uint32_t mask, uint32_t val)
+{
+	pr_debug("%s: ofs=%03x, mask=%08x, val=%08x\n", __func__, ofs,
+		 mask, val);
+	regmap_update_bits(clkr->regmap, ofs, mask, val);
+}
+
+/* ofs check */
+#define CLK_OFS_INVALID                 (-1)
+#define CLK_OFS_IS_VALID(_ofs)          ((_ofs) != CLK_OFS_INVALID)
+
+struct clk_composite_data {
+	int id;
+	const char *name;
+	unsigned long flags;
+	struct clk *clk;
+
+	int gate_ofs;
+	int gate_shift;
+	int gate_write_en;
+
+	int mux_ofs;
+	int mux_width;
+	int mux_shift;
+	const char * const *parent_names;
+	int num_parents;
+};
+
+struct clk_gate_data {
+	int id;
+	const char *name;
+	const char *parent;
+	unsigned long flags;
+	struct clk *clk;
+
+	int gate_ofs;
+	int gate_shift;
+	int gate_write_en;
+};
+
+#define CLK_GATE_DATA(_id, _name, _parent, _flags, _ofs, _shift, _write_en) \
+{ \
+	.id = _id, \
+	.name = _name, \
+	.parent = _parent, \
+	.flags = _flags, \
+	.gate_ofs = _ofs, \
+	.gate_shift = _shift, \
+	.gate_write_en = _write_en, \
+}
+
+struct clk_pm_data {
+	int ofs;
+	uint32_t ignore_bits;
+	uint32_t write_en_bits;
+	uint32_t val;
+};
+
+struct rtk_clk_data {
+	int clk_num;
+	struct regmap *regmap;
+	struct clk_onecell_data clk_data;
+	struct clk_pm_data *pm_data;
+	int pm_data_num;
+};
+
+struct rtk_clk_data *alloc_rtk_clk_data(int clk_num);
+void free_rtk_clk_data(struct rtk_clk_data *data);
+int rtk_clk_add_hws(struct device *dev, struct rtk_clk_data *data,
+		    struct clk_hw **hws, int num);
+int rtk_clk_add_composites(struct device *dev, struct rtk_clk_data *data,
+			   struct clk_composite_data *comps, int num);
+int rtk_clk_add_gates(struct device *dev, struct rtk_clk_data *data,
+		      struct clk_gate_data *gates, int num);
+extern const struct dev_pm_ops rtk_clk_pm_ops;
+
+#endif /* __CLK_REALTEK_COMMON_H */
+
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 4/6] clk: realtek: add reset controller support for Realtek SoCs
  2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
                   ` (2 preceding siblings ...)
  2019-12-03  7:35 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
@ 2019-12-03  7:35 ` " James Tai
  2019-12-03  7:35 ` [PATCH 5/6] clk: realtek: add rtd1619 controllers James Tai
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: James Tai @ 2019-12-03  7:35 UTC (permalink / raw)
  To: Andreas Färber
  Cc: cylee12, Stephen Boyd, Michael Turquette, Palmer Dabbelt,
	linux-kernel, linux-mediatek, Paul Walmsley, Matthias Brugger,
	linux-riscv, linux-clk, linux-arm-kernel

From: cylee12 <cylee12@realtek.com>

This patch add reset control support for Realtek SoCs.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 drivers/clk/realtek/Kconfig  |   1 +
 drivers/clk/realtek/Makefile |   1 +
 drivers/clk/realtek/reset.c  | 107 +++++++++++++++++++++++++++++++++++
 drivers/clk/realtek/reset.h  |  37 ++++++++++++
 4 files changed, 146 insertions(+)
 create mode 100644 drivers/clk/realtek/reset.c
 create mode 100644 drivers/clk/realtek/reset.h

diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 5bca757dddfa..8e7e7edf64dd 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config COMMON_CLK_REALTEK
 	bool "Clock driver for realtek"
+	select RESET_CONTROLLER
 	select MFD_SYSCON
 
 config CLK_PLL_PSAUD
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 050d450db067..43f8bd71c0c8 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -7,3 +7,4 @@ clk-rtk-y += clk-regmap-gate.o
 clk-rtk-y += clk-pll.o
 clk-rtk-$(CONFIG_CLK_PLL_PSAUD) += clk-pll-psaud.o
 clk-rtk-$(CONFIG_CLK_PLL_DIF) += clk-pll-dif.o
+clk-rtk-y += reset.o
diff --git a/drivers/clk/realtek/reset.c b/drivers/clk/realtek/reset.c
new file mode 100644
index 000000000000..3f4d1a723b2a
--- /dev/null
+++ b/drivers/clk/realtek/reset.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ */
+
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include "reset.h"
+
+static int rtk_reset_assert(struct reset_controller_dev *rcdev,
+			    unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id   = idx & 0xff;
+	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
+	uint32_t val  = bank->write_en ? (0x2 << id) : 0;
+
+	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
+}
+
+static int rtk_reset_deassert(struct reset_controller_dev *rcdev,
+			      unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id   = idx & 0xff;
+	uint32_t mask = bank->write_en ? (0x3 << id) : BIT(id);
+	uint32_t val  = mask;
+
+	return regmap_update_bits(data->regmap, bank->ofs, mask, val);
+}
+
+static int rtk_reset_reset(struct reset_controller_dev *rcdev,
+			   unsigned long idx)
+{
+	int ret;
+
+	ret = rtk_reset_assert(rcdev, idx);
+	if (ret)
+		return ret;
+
+	return rtk_reset_deassert(rcdev, idx);
+}
+
+static int rtk_reset_status(struct reset_controller_dev *rcdev,
+			    unsigned long idx)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	struct rtk_reset_bank *bank = &data->banks[idx >> 8];
+	uint32_t id = idx & 0xff;
+	uint32_t val;
+
+	regmap_read(data->regmap, bank->ofs, &val);
+	return !((val >> id) & 1);
+}
+
+static struct reset_control_ops rtk_reset_ops = {
+	.assert   = rtk_reset_assert,
+	.deassert = rtk_reset_deassert,
+	.reset    = rtk_reset_reset,
+	.status   = rtk_reset_status,
+};
+
+static int rtk_of_reset_xlate(struct reset_controller_dev *rcdev,
+			      const struct of_phandle_args *reset_spec)
+{
+	struct rtk_reset_data *data = to_rtk_reset_controller(rcdev);
+	int val;
+
+	val = reset_spec->args[0];
+	if (val >= rcdev->nr_resets)
+		return -EINVAL;
+
+	if (data->id_xlate)
+		return data->id_xlate(val);
+	return val;
+}
+
+
+int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
+			     struct rtk_reset_initdata *initdata)
+{
+	struct rtk_reset_data *data;
+	struct device_node *np = dev->of_node;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	data->regmap    = regmap;
+	data->num_banks = initdata->num_banks;
+	data->banks     = initdata->banks;
+	data->id_xlate  = initdata->id_xlate;
+
+	data->rcdev.owner     = THIS_MODULE;
+	data->rcdev.ops       = &rtk_reset_ops;
+	data->rcdev.of_node   = np;
+	data->rcdev.nr_resets = initdata->num_banks * 0x100;
+	data->rcdev.of_xlate  = rtk_of_reset_xlate;
+	data->rcdev.of_reset_n_cells = 1;
+
+	return reset_controller_register(&data->rcdev);
+}
+
diff --git a/drivers/clk/realtek/reset.h b/drivers/clk/realtek/reset.h
new file mode 100644
index 000000000000..f0cc7b1045ee
--- /dev/null
+++ b/drivers/clk/realtek/reset.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_RESET_H
+#define __CLK_REALTEK_RESET_H
+
+#include <linux/reset-controller.h>
+
+struct rtk_reset_bank {
+	uint32_t ofs;
+	uint32_t write_en;
+};
+
+struct rtk_reset_data {
+	struct reset_controller_dev rcdev;
+	struct rtk_reset_bank *banks;
+	uint32_t num_banks;
+	struct regmap *regmap;
+	unsigned long (*id_xlate)(unsigned long id);
+};
+
+#define to_rtk_reset_controller(r) \
+	container_of(r, struct rtk_reset_data, rcdev)
+
+struct rtk_reset_initdata {
+	struct rtk_reset_bank *banks;
+	uint32_t num_banks;
+	unsigned long (*id_xlate)(unsigned long id);
+};
+
+int rtk_reset_controller_add(struct device *dev, struct regmap *regmap,
+			     struct rtk_reset_initdata *initdata);
+
+#endif /* __CLK_REALTEK_RESET_H */
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 5/6] clk: realtek: add rtd1619 controllers
  2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
                   ` (3 preceding siblings ...)
  2019-12-03  7:35 ` [PATCH 4/6] clk: realtek: add reset controller " James Tai
@ 2019-12-03  7:35 ` James Tai
  2019-12-03  7:35 ` [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings James Tai
  2019-12-03  8:15 ` [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers Andreas Färber
  6 siblings, 0 replies; 10+ messages in thread
From: James Tai @ 2019-12-03  7:35 UTC (permalink / raw)
  To: Andreas Färber
  Cc: cylee12, Stephen Boyd, Michael Turquette, Palmer Dabbelt,
	linux-kernel, linux-mediatek, Paul Walmsley, Matthias Brugger,
	linux-riscv, linux-clk, linux-arm-kernel

From: cylee12 <cylee12@realtek.com>

This patch adds CRT controller and ISO controller for RTD1619 SoC.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 drivers/clk/realtek/Kconfig          |  10 +
 drivers/clk/realtek/Makefile         |   2 +
 drivers/clk/realtek/clk-rtd1619-cc.c | 553 +++++++++++++++++++++++++++
 drivers/clk/realtek/clk-rtd1619-ic.c | 112 ++++++
 4 files changed, 677 insertions(+)
 create mode 100644 drivers/clk/realtek/clk-rtd1619-cc.c
 create mode 100644 drivers/clk/realtek/clk-rtd1619-ic.c

diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
index 8e7e7edf64dd..43c55be25eba 100644
--- a/drivers/clk/realtek/Kconfig
+++ b/drivers/clk/realtek/Kconfig
@@ -9,3 +9,13 @@ config CLK_PLL_PSAUD
 
 config CLK_PLL_DIF
 	bool
+
+config COMMON_CLK_RTD1619
+	bool "RTD1619 Clock Controller"
+	depends on ARCH_REALTEK || COMPILE_TEST
+	select COMMON_CLK_REALTEK
+	select CLK_PLL_PSAUD
+	select CLK_PLL_DIF
+	default ARCH_REALTEK
+	---help---
+	  Support for the clock controller on RTD1619
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
index 43f8bd71c0c8..24af3dbe2006 100644
--- a/drivers/clk/realtek/Makefile
+++ b/drivers/clk/realtek/Makefile
@@ -8,3 +8,5 @@ clk-rtk-y += clk-pll.o
 clk-rtk-$(CONFIG_CLK_PLL_PSAUD) += clk-pll-psaud.o
 clk-rtk-$(CONFIG_CLK_PLL_DIF) += clk-pll-dif.o
 clk-rtk-y += reset.o
+clk-rtk-$(CONFIG_COMMON_CLK_RTD1619) += clk-rtd1619-cc.o
+clk-rtk-$(CONFIG_COMMON_CLK_RTD1619) += clk-rtd1619-ic.o
diff --git a/drivers/clk/realtek/clk-rtd1619-cc.c b/drivers/clk/realtek/clk-rtd1619-cc.c
new file mode 100644
index 000000000000..c799ffd4cc7a
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1619-cc.c
@@ -0,0 +1,553 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2018-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include "common.h"
+#include "clk-pll.h"
+#include "clk-regmap-gate.h"
+#include "clk-regmap-mux.h"
+#include "reset.h"
+#include <dt-bindings/clock/rtk,clock-rtd1619.h>
+#include <dt-bindings/reset/rtk,reset-rtd1619.h>
+
+#define DIV_DV(_r, _d, _v)    { .rate = _r, .div = _d, .val = _v, }
+#define FREQ_NF_MASK          (0x7FFFF)
+#define FREQ_NF(_r, _n, _f)   { .rate = _r, .val = ((_n) << 11) | (_f), }
+#define FREQ_MNO_MASK         (0x63FF0)
+#define FREQ_MNO(_r, _m, _n, _o) \
+	{ .rate = _r, .val = ((_m) << 4) | ((_n) << 12) | ((_o) << 17), }
+
+static const char * const default_parent[] = { "osc27m" };
+
+static const struct div_table scpu_div_tbl[] = {
+	DIV_DV(1000000000,  1, 0),
+	DIV_DV(500000000,   2, 0x88),
+	DIV_DV(350000000,   3, 0x8C),
+	DIV_DV(250000000,   4, 0x90),
+	DIV_DV(200000000,   8, 0xA0),
+	DIV_DV(100000000,  10, 0xA8),
+	DIV_TABLE_END
+};
+
+static const struct freq_table scpu_tbl[] = {
+	FREQ_NF(1000000000, 34,   75),
+	FREQ_NF(1100000000, 37, 1517),
+	FREQ_NF(1200000000, 41,  910),
+	FREQ_NF(1300000000, 45,  303),
+	FREQ_NF(1400000000, 48, 1745),
+	FREQ_NF(1500000000, 52, 1137),
+	FREQ_NF(1600000000, 56,  530),
+	FREQ_NF(1700000000, 59, 1972),
+	FREQ_NF(1800000000, 63, 1365),
+	FREQ_NF(1900000000, 67,  758),
+	FREQ_NF(2000000000, 71,  151),
+	/* init-value mapping */
+	FREQ_NF(1000000000, 35,    0),
+	FREQ_NF(1200000000, 41,    0),
+	FREQ_NF(1800000000, 65,    0),
+	FREQ_NF(1800000000, 64,    0),
+	FREQ_TABLE_END
+};
+
+static struct clk_pll_div pll_scpu = {
+	.div_ofs    = 0x030,
+	.div_shift  = 6,
+	.div_width  = 8,
+	.div_tbl    = scpu_div_tbl,
+	.clkp       = {
+		.ssc_ofs   = 0x500,
+		.pll_ofs   = CLK_OFS_INVALID,
+		.freq_loc  = CLK_PLL_CONF_FREQ_LOC_SSC1,
+		.freq_tbl  = scpu_tbl,
+		.freq_mask = FREQ_NF_MASK,
+		.clkr.hw.init = &(struct clk_init_data) {
+			.name         = "pll_scpu",
+			.ops          = &clk_pll_div_ops,
+			.parent_names = default_parent,
+			.num_parents  = 1,
+			.flags        = CLK_IGNORE_UNUSED |
+					CLK_GET_RATE_NOCACHE,
+		},
+	},
+};
+
+static const struct div_table bus_div_tbl[] = {
+	DIV_DV(257000000, 1, 0),
+	DIV_DV(129000000, 2, 2),
+	DIV_DV(65000000,  4, 3),
+	DIV_TABLE_END
+};
+
+static const struct freq_table bus_tbl[] = {
+	FREQ_NF(513000000, 35,    0),
+	FREQ_NF(400000000, 26, 1289),
+	FREQ_TABLE_END
+};
+
+static struct clk_pll_div pll_bus = {
+	.div_ofs    = 0x030,
+	.div_shift  = 0,
+	.div_width  = 2,
+	.div_tbl    = bus_div_tbl,
+	.clkp       = {
+		.ssc_ofs   = 0x520,
+		.pll_ofs   = CLK_OFS_INVALID,
+		.freq_loc  = CLK_PLL_CONF_FREQ_LOC_SSC1,
+		.freq_tbl  = bus_tbl,
+		.freq_mask = FREQ_NF_MASK,
+		.clkr.hw.init = &(struct clk_init_data) {
+			.name         = "pll_bus",
+			.ops          = &clk_pll_div_ops,
+			.parent_names = default_parent,
+			.num_parents  = 1,
+			.flags        = CLK_IGNORE_UNUSED |
+					CLK_GET_RATE_NOCACHE,
+		},
+	},
+};
+
+static struct clk_fixed_factor clk_sys = {
+	.div     = 1,
+	.mult    = 1,
+	.hw.init = &(struct clk_init_data) {
+		.name         = "clk_sys",
+		.ops          = &clk_fixed_factor_ops,
+		.parent_names = (const char *[]){ "pll_bus" },
+		.num_parents  = 1,
+		.flags        = CLK_SET_RATE_PARENT,
+	},
+};
+
+static const struct div_table dcsb_div_tbl[] = {
+	DIV_DV(550000000, 1, 0),
+	DIV_DV(275000000, 2, 2),
+	DIV_DV(1,         4, 3),
+	DIV_TABLE_END
+};
+
+static const struct freq_table dcsb_tbl[] = {
+	FREQ_NF(550000000, 38, 0),
+	FREQ_NF(550000000, 37, 1517),
+	FREQ_TABLE_END
+};
+
+static struct clk_pll_div pll_dcsb = {
+	.div_ofs    = 0x030,
+	.div_shift  = 2,
+	.div_width  = 2,
+	.div_tbl    = dcsb_div_tbl,
+	.clkp       = {
+		.ssc_ofs   = 0x540,
+		.pll_ofs   = CLK_OFS_INVALID,
+		.freq_loc  = CLK_PLL_CONF_FREQ_LOC_SSC1,
+		.freq_tbl  = dcsb_tbl,
+		.freq_mask = FREQ_NF_MASK,
+		.clkr.hw.init = &(struct clk_init_data) {
+			.name         = "pll_dcsb",
+			.ops          = &clk_pll_div_ops,
+			.parent_names = default_parent,
+			.num_parents  = 1,
+			.flags        = CLK_IGNORE_UNUSED |
+					CLK_GET_RATE_NOCACHE,
+		},
+	},
+};
+
+static struct clk_fixed_factor clk_sysh = {
+	.div     = 1,
+	.mult    = 1,
+	.hw.init = &(struct clk_init_data) {
+		.name         = "clk_sysh",
+		.ops          = &clk_fixed_factor_ops,
+		.parent_names = (const char *[]){ "pll_dcsb" },
+		.num_parents  = 1,
+		.flags        = CLK_SET_RATE_PARENT,
+	},
+};
+
+static const struct freq_table ddsx_tbl[] = {
+	FREQ_NF(432000000, 13, 0),
+	FREQ_TABLE_END
+};
+
+static struct clk_pll pll_ddsa = {
+	.ssc_ofs   = 0x560,
+	.pll_ofs   = 0x120,
+	.pow_loc   = CLK_PLL_CONF_POW_LOC_CTL3,
+	.freq_loc  = CLK_PLL_CONF_FREQ_LOC_SSC1,
+	.freq_tbl  = ddsx_tbl,
+	.freq_mask = FREQ_NF_MASK,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name         = "pll_ddsa",
+		.ops          = &clk_pll_ops,
+		.parent_names = default_parent,
+		.num_parents  = 1,
+		.flags        = CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE,
+	},
+};
+
+static const struct freq_table gpu_tbl[] = {
+	FREQ_NF(300000000, 19,  455),
+	FREQ_NF(400000000, 26, 1289),
+	FREQ_NF(500000000, 34,   75),
+	FREQ_NF(600000000, 41,  910),
+	FREQ_NF(650000000, 45,  303),
+	FREQ_NF(700000000, 48, 1745),
+	FREQ_NF(750000000, 52, 1137),
+	FREQ_NF(800000000, 56,  530),
+	FREQ_NF(850000000, 59, 1971),
+	FREQ_TABLE_END
+};
+
+static struct clk_pll pll_gpu = {
+	.ssc_ofs   = 0x5A0,
+	.pll_ofs   = 0x1C0,
+	.pow_loc   = CLK_PLL_CONF_POW_LOC_CTL2,
+	.freq_loc  = CLK_PLL_CONF_FREQ_LOC_SSC1,
+	.freq_tbl  = gpu_tbl,
+	.freq_mask = FREQ_NF_MASK,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name         = "pll_gpu",
+		.ops          = &clk_pll_ops,
+		.parent_names = default_parent,
+		.num_parents  = 1,
+		.flags        = CLK_GET_RATE_NOCACHE,
+	},
+};
+
+static const struct freq_table ve_tbl[] = {
+	FREQ_MNO(189000000, 12, 0, 1),
+	FREQ_MNO(270000000, 18, 0, 1),
+	FREQ_MNO(405000000, 13, 0, 0),
+	FREQ_MNO(432000000, 14, 0, 0),
+	FREQ_MNO(459000000, 15, 0, 0),
+	FREQ_MNO(486000000, 16, 0, 0),
+	FREQ_MNO(513000000, 17, 0, 0),
+	FREQ_MNO(540000000, 18, 0, 0),
+	FREQ_MNO(550000000, 59, 2, 0),
+	FREQ_MNO(567000000, 19, 0, 0),
+	FREQ_MNO(594000000, 20, 0, 0),
+	FREQ_MNO(648000000, 22, 0, 0),
+	FREQ_MNO(675000000, 23, 0, 0),
+	FREQ_MNO(702000000, 24, 0, 0),
+	FREQ_MNO(715000000, 51, 1, 0),
+	FREQ_TABLE_END
+};
+
+static struct clk_pll pll_ve1 = {
+	.ssc_ofs   = CLK_OFS_INVALID,
+	.pll_ofs   = 0x114,
+	.pow_loc   = CLK_PLL_CONF_POW_LOC_CTL2,
+	.freq_loc  = CLK_PLL_CONF_FREQ_LOC_CTL1,
+	.freq_tbl  = ve_tbl,
+	.freq_mask = FREQ_MNO_MASK,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name         = "pll_ve1",
+		.ops          = &clk_pll_ops,
+		.parent_names = default_parent,
+		.num_parents  = 1,
+		.flags        = CLK_GET_RATE_NOCACHE,
+	},
+};
+
+static struct clk_pll pll_ve2 = {
+	.ssc_ofs   = CLK_OFS_INVALID,
+	.pll_ofs   = 0x1D0,
+	.pow_loc   = CLK_PLL_CONF_POW_LOC_CTL2,
+	.freq_loc  = CLK_PLL_CONF_FREQ_LOC_CTL1,
+	.freq_tbl  = ve_tbl,
+	.freq_mask = FREQ_MNO_MASK,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name         = "pll_ve2",
+		.ops          = &clk_pll_ops,
+		.parent_names = default_parent,
+		.num_parents  = 1,
+		.flags        = CLK_GET_RATE_NOCACHE,
+	},
+};
+
+static struct clk_pll_dif pll_dif = {
+	.ssc_ofs   = 0x634,
+	.pll_ofs   = 0x624,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name         = "pll_dif",
+		.ops          = &clk_pll_dif_ops,
+		.parent_names = default_parent,
+		.num_parents  = 1,
+		.flags        = CLK_GET_RATE_NOCACHE,
+	},
+};
+
+static struct clk_pll_psaud pll_psaud1a = {
+	.reg = 0x130,
+	.id  = CLK_PLL_PSAUD1A,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name         = "pll_psaud1a",
+		.ops          = &clk_pll_psaud_ops,
+		.parent_names = default_parent,
+		.num_parents  = 1,
+		.flags        = CLK_IGNORE_UNUSED | CLK_SET_RATE_UNGATE,
+	},
+};
+
+static struct clk_pll_psaud pll_psaud2a = {
+	.reg = 0x130,
+	.id  = CLK_PLL_PSAUD2A,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name         = "pll_psaud2a",
+		.ops          = &clk_pll_psaud_ops,
+		.parent_names = default_parent,
+		.num_parents  = 1,
+		.flags        = CLK_IGNORE_UNUSED | CLK_SET_RATE_UNGATE,
+	},
+};
+
+static struct clk_hw *cc_hws[] = {
+	[CC_PLL_SCPU]    = &__clk_pll_div_hw(&pll_scpu),
+	[CC_PLL_BUS]     = &__clk_pll_div_hw(&pll_bus),
+	[CC_PLL_DCSB]    = &__clk_pll_div_hw(&pll_dcsb),
+	[CC_PLL_DDSA]    = &__clk_pll_hw(&pll_ddsa),
+	[CC_PLL_GPU]     = &__clk_pll_hw(&pll_gpu),
+	[CC_PLL_VE1]     = &__clk_pll_hw(&pll_ve1),
+	[CC_PLL_VE2]     = &__clk_pll_hw(&pll_ve2),
+	[CC_PLL_DIF]     = &__clk_pll_dif_hw(&pll_dif),
+	[CC_CLK_SYS]     = &clk_sys.hw,
+	[CC_CLK_SYSH]    = &clk_sysh.hw,
+	[CC_PLL_PSAUD1A] = &__clk_pll_psaud_hw(&pll_psaud1a),
+	[CC_PLL_PSAUD2A] = &__clk_pll_psaud_hw(&pll_psaud2a),
+};
+
+static const char * const ve_parents[] = {
+	"clk_sys",
+	"clk_sysh",
+	"pll_ve1",
+	"pll_ve2",
+};
+
+static struct clk_composite_data cc_composites[] = {
+	{
+		.id              = CC_CLK_GPU,
+		.mux_ofs         = CLK_OFS_INVALID,
+		.gate_ofs        = 0x050,
+		.gate_shift      = 18,
+		.gate_write_en   = 1,
+		.parent_names    = (const char *[]){ "pll_gpu" },
+		.num_parents     = 1,
+		.name            = "clk_gpu",
+		.flags           = CLK_SET_RATE_PARENT,
+	},
+	{
+		.id              = CC_CLK_VE1,
+		.gate_ofs        = 0x050,
+		.gate_shift      = 20,
+		.gate_write_en   = 1,
+		.mux_ofs         = 0x04C,
+		.mux_width       = 3,
+		.mux_shift       = 0,
+		.parent_names    = ve_parents,
+		.num_parents     = ARRAY_SIZE(ve_parents),
+		.name            = "clk_ve1",
+		.flags           = CLK_SET_RATE_PARENT |
+				   CLK_SET_RATE_NO_REPARENT,
+	},
+	{
+		.id              = CC_CLK_VE2,
+		.gate_ofs        = 0x050,
+		.gate_shift      = 22,
+		.gate_write_en   = 1,
+		.mux_ofs         = 0x04C,
+		.mux_width       = 3,
+		.mux_shift       = 3,
+		.parent_names    = ve_parents,
+		.num_parents     = ARRAY_SIZE(ve_parents),
+		.name            = "clk_ve2",
+		.flags           = CLK_SET_RATE_PARENT |
+				   CLK_SET_RATE_NO_REPARENT,
+	},
+	{
+		.id              = CC_CLK_VE3,
+		.gate_ofs        = 0x05C,
+		.gate_shift      = 26,
+		.gate_write_en   = 1,
+		.mux_ofs         = 0x04C,
+		.mux_width       = 3,
+		.mux_shift       = 6,
+		.parent_names    = ve_parents,
+		.num_parents     = ARRAY_SIZE(ve_parents),
+		.name            = "clk_ve3",
+		.flags           = CLK_SET_RATE_PARENT |
+				   CLK_SET_RATE_NO_REPARENT,
+	},
+	{
+		.id              = CC_CLK_VE2_BPU,
+		.gate_ofs        = CLK_OFS_INVALID,
+		.mux_ofs         = 0x04C,
+		.mux_width       = 3,
+		.mux_shift       = 9,
+		.parent_names    = ve_parents,
+		.num_parents     = ARRAY_SIZE(ve_parents),
+		.name            = "clk_ve2_bpu",
+		.flags           = CLK_SET_RATE_PARENT |
+				   CLK_SET_RATE_NO_REPARENT,
+	},
+};
+
+#define GATE(_id, _name, _parent, _ofs, _shift) \
+	CLK_GATE_DATA(_id, _name, _parent, 0, _ofs, _shift, 1)
+#define GATE_IGNORE(_id, _name, _parent, _ofs, _shift) \
+	CLK_GATE_DATA(_id, _name, _parent, CLK_IGNORE_UNUSED, _ofs, _shift, 1)
+
+static struct clk_gate_data cc_gates[] = {
+	GATE_IGNORE(CC_CKE_MISC, "misc", NULL, 0x50, 0),
+	GATE(CC_CKE_PCIE0, "pcie0", NULL, 0x50,  2),
+	GATE(CC_CKE_GSPI, "gspi", "misc", 0x50,  6),
+	GATE(CC_CKE_SDS, "sds", NULL, 0x50, 12),
+	GATE_IGNORE(CC_CKE_HDMI, "hdmi", NULL, 0x50, 14),
+	GATE_IGNORE(CC_CKE_TVE, "tve", NULL, 0x50, 24),
+	GATE_IGNORE(CC_CKE_VO, "vo", NULL, 0x50, 26),
+	GATE_IGNORE(CC_CKE_LSADC, "lsadc", NULL, 0x50, 28),
+	GATE(CC_CKE_SE, "se", NULL, 0x50, 30),
+	GATE_IGNORE(CC_CKE_CP, "cp", NULL, 0x54,  2),
+	GATE_IGNORE(CC_CKE_MD, "md", NULL, 0x54, 4),
+	GATE_IGNORE(CC_CKE_TP, "tp", NULL, 0x54, 6),
+	GATE(CC_CKE_RSA, "rsa", NULL, 0x54, 8),
+	GATE(CC_CKE_NF, "nf", NULL, 0x54, 10),
+	GATE(CC_CKE_EMMC, "emmc", NULL, 0x54, 12),
+	GATE(CC_CKE_SD, "sd", NULL, 0x54, 14),
+	GATE(CC_CKE_SDIO_IP, "sdio_ip", NULL, 0x54, 16),
+	GATE(CC_CKE_MIPI, "mipi", NULL, 0x54, 18),
+	GATE(CC_CKE_EMMC_IP, "emmc_ip", NULL, 0x54, 20),
+	GATE(CC_CKE_SDIO, "sdio", NULL, 0x54, 22),
+	GATE(CC_CKE_SD_IP, "sd_ip", NULL, 0x54, 24),
+	GATE(CC_CKE_CABLERX, "cablerx", NULL, 0x54, 26),
+	GATE(CC_CKE_TPB, "tpb", NULL, 0x54, 28),
+	GATE(CC_CKE_SC1, "sc1", "misc", 0x54, 30),
+	GATE(CC_CKE_I2C3, "i2c3", "misc", 0x58, 0),
+	GATE(CC_CKE_JPEG, "jpeg", NULL, 0x58, 4),
+	GATE(CC_CKE_SC0, "sc0", "misc", 0x58, 10),
+	GATE(CC_CKE_HDMIRX, "hdmirx", NULL, 0x58, 26),
+	GATE(CC_CKE_HSE, "hse", NULL, 0x58, 28),
+	GATE(CC_CKE_UR2, "ur2", "misc", 0x58, 30),
+	GATE(CC_CKE_UR1, "ur1", "misc", 0x5C, 0),
+	GATE(CC_CKE_FAN, "fan", "misc", 0x5C, 2),
+	GATE(CC_CKE_SATA_WRAP_SYS, "sata_wrap_sys", NULL, 0x5C, 8),
+	GATE(CC_CKE_SATA_WRAP_SYSH, "sata_wrap_sysh", NULL, 0x5C, 10),
+	GATE(CC_CKE_SATA_MAC_SYSH, "sata_mac_sysh", NULL, 0x5C, 12),
+	GATE(CC_CKE_R2RDSC, "r2rdsc", NULL, 0x5C, 14),
+	GATE(CC_CKE_PCIE1, "pcie1", NULL, 0x5C, 18),
+	GATE(CC_CKE_I2C4, "i2c4", "misc", 0x5C, 20),
+	GATE(CC_CKE_I2C5, "i2c5", "misc", 0x5C, 22),
+	GATE(CC_CKE_EDP, "edp", NULL, 0x5C, 28),
+	GATE_IGNORE(CC_CKE_TSIO_TRX, "tsio_trx", NULL, 0x5C, 30),
+};
+
+static struct rtk_reset_bank cc_reset_banks[] = {
+	{ .ofs = 0x00, .write_en = 1, },
+	{ .ofs = 0x04, .write_en = 1, },
+	{ .ofs = 0x08, .write_en = 1, },
+	{ .ofs = 0x0c, .write_en = 1, },
+	{ .ofs = 0x14, .write_en = 1, },
+	{ .ofs = 0x68, .write_en = 1, },
+};
+
+static struct rtk_reset_initdata cc_reset_initdata = {
+	.banks     = cc_reset_banks,
+	.num_banks = ARRAY_SIZE(cc_reset_banks),
+};
+
+static struct clk_pm_data cc_pm_data[] = {
+	/* SOFT_RESET */
+	{ .ofs = 0x00, .write_en_bits = 0xAAAAAAAA, },
+	{ .ofs = 0x04, .write_en_bits = 0xAAAAAAAA, },
+	{ .ofs = 0x08, .write_en_bits = 0x0A80AAAA, },
+	{ .ofs = 0x0C, .write_en_bits = 0x2AAAAAAA, },
+	{ .ofs = 0x14, .write_en_bits = 0xAA82AA82, },
+	{ .ofs = 0x68, .write_en_bits = 0xAAAAAAAA, },
+	/* CLK_EN */
+	{ .ofs = 0x50, .write_en_bits = 0xA0A8288A, },
+	{ .ofs = 0x54, .write_en_bits = 0xAAAAAAA8, },
+	{ .ofs = 0x58, .write_en_bits = 0xA800082A, },
+	{ .ofs = 0x5C, .write_en_bits = 0xAAAAAA0A, },
+	/* PLL_GPU */
+	{ .ofs = 0x1C4, },
+	{ .ofs = 0x5A4, .ignore_bits = ~(0x7FFFF), },
+	/* PLL_VE1 */
+	{ .ofs = 0x118, },
+	{ .ofs = 0x114, .ignore_bits = ~(0x63FF0), },
+	/* PLL_VE2 */
+	{ .ofs = 0x1D4, },
+	{ .ofs = 0x1D0, .ignore_bits = ~(0x63FF0), },
+};
+
+static int rtd1619_cc_probe(struct platform_device *pdev)
+{
+	struct device *parent;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct rtk_clk_data *data;
+	struct regmap *regmap;
+	int ret;
+
+	parent = dev->parent;
+	if (!parent) {
+		dev_err(dev, "no parent crt controller\n");
+		return -ENODEV;
+	}
+
+	regmap = syscon_node_to_regmap(parent->of_node);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "failed to get regmap form %s: %d\n", np->name,
+			ret);
+		return ret;
+	}
+
+	data = alloc_rtk_clk_data(CC_CLK_MAX);
+	if (!data)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, data);
+	data->regmap = regmap;
+	data->pm_data = cc_pm_data;
+	data->pm_data_num = ARRAY_SIZE(cc_pm_data);
+
+	rtk_clk_add_hws(dev, data, cc_hws, ARRAY_SIZE(cc_hws));
+	rtk_clk_add_composites(dev, data, cc_composites,
+					  ARRAY_SIZE(cc_composites));
+	rtk_clk_add_gates(dev, data, cc_gates, ARRAY_SIZE(cc_gates));
+
+	ret = of_clk_add_provider(np, of_clk_src_onecell_get, &data->clk_data);
+	if (ret)
+		dev_err(dev, "failed to add clk provider: %d\n", ret);
+
+	rtk_reset_controller_add(dev, regmap, &cc_reset_initdata);
+
+	return 0;
+}
+
+static const struct of_device_id rtd1619_cc_match[] = {
+	{ .compatible = "realtek,rtd1619-cc", },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver rtd1619_cc_driver = {
+	.probe = rtd1619_cc_probe,
+	.driver = {
+		.name = "rtk-rtd1619-cc",
+		.of_match_table = rtd1619_cc_match,
+		.pm = &rtk_clk_pm_ops,
+	},
+};
+
+static int __init rtd1619_cc_init(void)
+{
+	return platform_driver_register(&rtd1619_cc_driver);
+}
+core_initcall(rtd1619_cc_init);
diff --git a/drivers/clk/realtek/clk-rtd1619-ic.c b/drivers/clk/realtek/clk-rtd1619-ic.c
new file mode 100644
index 000000000000..9651c9aca26d
--- /dev/null
+++ b/drivers/clk/realtek/clk-rtd1619-ic.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include "common.h"
+#include "reset.h"
+#include <dt-bindings/clock/rtk,clock-rtd1619.h>
+
+#define GATE(_id, _name, _parent, _ofs, _shift) \
+	CLK_GATE_DATA(_id, _name, _parent, 0, _ofs, _shift, 0)
+
+static struct clk_gate_data ic_gates[] = {
+	GATE(IC_CKE_CEC0, "cec0", NULL, 0x8c, 2),
+	GATE(IC_CKE_CBUSRX_SYS, "cbusrx_sys", NULL, 0x8c, 3),
+	GATE(IC_CKE_CBUSTX_SYS, "cbustx_sys", NULL, 0x8c, 4),
+	GATE(IC_CKE_CBUS_SYS, "cbus_sys", NULL, 0x8c, 5),
+	GATE(IC_CKE_CBUS_OSC, "cbus_osc", NULL, 0x8c, 6),
+	GATE(IC_CKE_IR, "ir", NULL, 0x8c, 7),
+	GATE(IC_CKE_UR0, "ur0", NULL, 0x8c, 8),
+	GATE(IC_CKE_I2C0, "i2c0", NULL, 0x8c, 9),
+	GATE(IC_CKE_I2C1, "i2c1", NULL, 0x8c, 10),
+	GATE(IC_CKE_ETN_250M, "etn_250m", NULL, 0x8c, 11),
+	GATE(IC_CKE_ETN_SYS, "etn_sys", NULL, 0x8c, 12),
+	GATE(IC_CKE_USB_DRD, "usb_drd", NULL, 0x8c, 13),
+	GATE(IC_CKE_USB_HOST, "usb_host", NULL, 0x8c, 14),
+	GATE(IC_CKE_USB_U3_HOST, "usb_u3_host", NULL, 0x8c, 15),
+	GATE(IC_CKE_USB, "usb", NULL, 0x8c, 16),
+};
+
+static struct rtk_reset_bank ic_reset_banks[] = {
+	{ .ofs = 0x88, },
+};
+
+static struct rtk_reset_initdata ic_reset_initdata = {
+	.banks     = ic_reset_banks,
+	.num_banks = ARRAY_SIZE(ic_reset_banks),
+};
+
+static struct clk_pm_data ic_pm_data[] = {
+	{ .ofs = 0x88, },
+	{ .ofs = 0x8C, },
+};
+
+static int rtd1619_ic_probe(struct platform_device *pdev)
+{
+	struct device *parent;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct rtk_clk_data *data;
+	struct regmap *regmap;
+	int ret;
+
+	parent = dev->parent;
+	if (!parent) {
+		dev_err(dev, "no parent crt controller\n");
+		return -ENODEV;
+	}
+
+	regmap = syscon_node_to_regmap(parent->of_node);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(dev, "failed to get regmap form %s: %d\n", np->name,
+			ret);
+		return ret;
+	}
+
+	data = alloc_rtk_clk_data(IC_CLK_MAX);
+	if (!data)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, data);
+	data->regmap = regmap;
+	data->pm_data = ic_pm_data;
+	data->pm_data_num = ARRAY_SIZE(ic_pm_data);
+
+	rtk_clk_add_gates(dev, data, ic_gates, ARRAY_SIZE(ic_gates));
+
+	ret = of_clk_add_provider(np, of_clk_src_onecell_get, &data->clk_data);
+	if (ret)
+		dev_err(dev, "failed to add clk provider: %d\n", ret);
+
+	rtk_reset_controller_add(dev, regmap, &ic_reset_initdata);
+
+	return 0;
+}
+
+static const struct of_device_id rtd1619_ic_match[] = {
+	{ .compatible = "realtek,rtd1619-ic", },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver rtd1619_ic_driver = {
+	.probe = rtd1619_ic_probe,
+	.driver = {
+		.name = "rtk-rtd1619-ic",
+		.of_match_table = rtd1619_ic_match,
+		.pm = &rtk_clk_pm_ops,
+	},
+};
+
+static int __init rtd1619_ic_init(void)
+{
+	return platform_driver_register(&rtd1619_ic_driver);
+}
+core_initcall(rtd1619_ic_init);
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings
  2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
                   ` (4 preceding siblings ...)
  2019-12-03  7:35 ` [PATCH 5/6] clk: realtek: add rtd1619 controllers James Tai
@ 2019-12-03  7:35 ` James Tai
  2019-12-03  8:15 ` [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers Andreas Färber
  6 siblings, 0 replies; 10+ messages in thread
From: James Tai @ 2019-12-03  7:35 UTC (permalink / raw)
  To: Andreas Färber
  Cc: Mark Rutland, devicetree, cylee12, Stephen Boyd,
	Michael Turquette, Palmer Dabbelt, linux-kernel, Rob Herring,
	linux-mediatek, Paul Walmsley, Matthias Brugger, linux-riscv,
	linux-clk, linux-arm-kernel

From: cylee12 <cylee12@realtek.com>

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 .../bindings/clock/realtek,clocks.txt         | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/realtek,clocks.txt

diff --git a/Documentation/devicetree/bindings/clock/realtek,clocks.txt b/Documentation/devicetree/bindings/clock/realtek,clocks.txt
new file mode 100644
index 000000000000..db101508ac6a
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/realtek,clocks.txt
@@ -0,0 +1,38 @@
+Realtek Clock/Reset Controller
+==============================
+
+Realtek CRT/ISO controller device-tree binding for Realtek Platforms.
+
+This binding uses the common clock binding[1].
+
+The controller node should be the child of a syscon node with the required
+propertise:
+
+- compatible :
+	should contain only one of the following:
+		"realtek,rtd1619-cc" for RTD1619 CRT clock controller,
+		"realtek,rtd1619-ic" for RTD1619 ISO clock controller,
+
+- #clock-cells : should be 1.
+
+- #reset-cells : should be 1.
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Example:
+
+	crt@98000000 {
+		compatible = "realtek,rtd1619-crt", "simple-mfd", "syscon";
+		reg = <0x98000000 0x1000>;
+
+		cc: cc@98000000 {
+			compatible = "realtek,rtd1619-cc";
+			#clock-cells = <1>;
+			#reset-cells = <1>;
+		};
+	};
+
+	consumer {
+		clocks = <&cc CC_CKE_GSPI>;
+	};
+
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers
  2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
                   ` (5 preceding siblings ...)
  2019-12-03  7:35 ` [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings James Tai
@ 2019-12-03  8:15 ` Andreas Färber
  6 siblings, 0 replies; 10+ messages in thread
From: Andreas Färber @ 2019-12-03  8:15 UTC (permalink / raw)
  To: James Tai
  Cc: devicetree, Cheng-Yu Lee, linux-realtek-soc, Palmer Dabbelt,
	linux-kernel, linux-mediatek, Paul Walmsley, Matthias Brugger,
	linux-riscv, linux-arm-kernel

Hi James,

Am 03.12.19 um 08:35 schrieb James Tai:
> This series adds clock and reset controllers for the Realtek RTD1619 SoC.

Thanks - when you resend (in this case I guess because of missing
linux-realtek-soc mailing list), please change the subject prefix to
"PATCH RESEND" (--subject-prefix=) so that people know which one to
reply to.

Let's try to keep the review on the series sent 10 minutes later (:45),
so that everyone receives it.

Thanks,
Andreas

> 
> Cc: Andreas Färber <afaerber@suse.de>
> Cc: Cheng-Yu Lee <cylee12@realtek.com>
> Cc: devicetree@vger.kernel.org
> 
> cylee12 (6):
>   dt-bindings: clock: add bindings for RTD1619 clocks
>   dt-bindings: reset: add bindings for rtd1619 reset controls
>   clk: realtek: add common clock support for Realtek SoCs
>   clk: realtek: add reset controller support for Realtek SoCs
>   clk: realtek: add rtd1619 controllers
>   dt-bindings: clk: realtek: add rtd1619 clock controller bindings
> 
>  .../bindings/clock/realtek,clocks.txt         |  38 ++
>  drivers/clk/Kconfig                           |   1 +
>  drivers/clk/Makefile                          |   1 +
>  drivers/clk/realtek/Kconfig                   |  21 +
>  drivers/clk/realtek/Makefile                  |  12 +
>  drivers/clk/realtek/clk-pll-dif.c             |  81 +++
>  drivers/clk/realtek/clk-pll-psaud.c           | 120 ++++
>  drivers/clk/realtek/clk-pll.c                 | 400 +++++++++++++
>  drivers/clk/realtek/clk-pll.h                 | 151 +++++
>  drivers/clk/realtek/clk-regmap-gate.c         |  89 +++
>  drivers/clk/realtek/clk-regmap-gate.h         |  26 +
>  drivers/clk/realtek/clk-regmap-mux.c          |  63 ++
>  drivers/clk/realtek/clk-regmap-mux.h          |  26 +
>  drivers/clk/realtek/clk-rtd1619-cc.c          | 553 ++++++++++++++++++
>  drivers/clk/realtek/clk-rtd1619-ic.c          | 112 ++++
>  drivers/clk/realtek/common.c                  | 320 ++++++++++
>  drivers/clk/realtek/common.h                  | 123 ++++
>  drivers/clk/realtek/reset.c                   | 107 ++++
>  drivers/clk/realtek/reset.h                   |  37 ++
>  include/dt-bindings/clock/rtk,clock-rtd1619.h |  88 +++
>  include/dt-bindings/reset/rtk,reset-rtd1619.h | 124 ++++
>  21 files changed, 2493 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/realtek,clocks.txt
>  create mode 100644 drivers/clk/realtek/Kconfig
>  create mode 100644 drivers/clk/realtek/Makefile
>  create mode 100644 drivers/clk/realtek/clk-pll-dif.c
>  create mode 100644 drivers/clk/realtek/clk-pll-psaud.c
>  create mode 100644 drivers/clk/realtek/clk-pll.c
>  create mode 100644 drivers/clk/realtek/clk-pll.h
>  create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
>  create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
>  create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
>  create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
>  create mode 100644 drivers/clk/realtek/clk-rtd1619-cc.c
>  create mode 100644 drivers/clk/realtek/clk-rtd1619-ic.c
>  create mode 100644 drivers/clk/realtek/common.c
>  create mode 100644 drivers/clk/realtek/common.h
>  create mode 100644 drivers/clk/realtek/reset.c
>  create mode 100644 drivers/clk/realtek/reset.h
>  create mode 100644 include/dt-bindings/clock/rtk,clock-rtd1619.h
>  create mode 100644 include/dt-bindings/reset/rtk,reset-rtd1619.h
> 


-- 
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer
HRB 36809 (AG Nürnberg)

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs
  2019-12-03  7:45 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
@ 2019-12-03 15:28   ` Andreas Färber
  0 siblings, 0 replies; 10+ messages in thread
From: Andreas Färber @ 2019-12-03 15:28 UTC (permalink / raw)
  To: James Tai
  Cc: cylee12, linux-realtek-soc, Stephen Boyd, Michael Turquette,
	linux-kernel, linux-mediatek, Paul Walmsley, Matthias Brugger,
	linux-riscv, linux-clk, linux-arm-kernel

Hi James,

[dropping Palmer]

Am 03.12.19 um 08:45 schrieb James Tai:
> From: cylee12 <cylee12@realtek.com>

Please fix the author name.

> 
> This patch adds common clock support for Realtek SoCs, including PLLs,
> Mux clocks and Gate clocks.

Can you be more specific here, please? Is this compatible back to
RTD1195 or RTD1295 or just 1619 forward? I only see RTD1619 in 5/6. And
not a single .dtsi patch is included in this series for testing it.

> 
> Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
> Signed-off-by: James Tai <james.tai@realtek.com>
> ---
>  drivers/clk/Kconfig                   |   1 +
>  drivers/clk/Makefile                  |   1 +
>  drivers/clk/realtek/Kconfig           |  10 +
>  drivers/clk/realtek/Makefile          |   9 +
>  drivers/clk/realtek/clk-pll-dif.c     |  81 ++++++
>  drivers/clk/realtek/clk-pll-psaud.c   | 120 ++++++++
>  drivers/clk/realtek/clk-pll.c         | 400 ++++++++++++++++++++++++++
>  drivers/clk/realtek/clk-pll.h         | 151 ++++++++++
>  drivers/clk/realtek/clk-regmap-gate.c |  89 ++++++
>  drivers/clk/realtek/clk-regmap-gate.h |  26 ++
>  drivers/clk/realtek/clk-regmap-mux.c  |  63 ++++
>  drivers/clk/realtek/clk-regmap-mux.h  |  26 ++
>  drivers/clk/realtek/common.c          | 320 +++++++++++++++++++++
>  drivers/clk/realtek/common.h          | 123 ++++++++
>  14 files changed, 1420 insertions(+)

This patch is way too large for me to review. Please split this up
further into logically self-contained, compile-testable patches.

>  create mode 100644 drivers/clk/realtek/Kconfig
>  create mode 100644 drivers/clk/realtek/Makefile
>  create mode 100644 drivers/clk/realtek/clk-pll-dif.c
>  create mode 100644 drivers/clk/realtek/clk-pll-psaud.c
>  create mode 100644 drivers/clk/realtek/clk-pll.c
>  create mode 100644 drivers/clk/realtek/clk-pll.h
>  create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
>  create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
>  create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
>  create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
>  create mode 100644 drivers/clk/realtek/common.c
>  create mode 100644 drivers/clk/realtek/common.h
> 
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index c44247d0b83e..8e06487440ce 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -317,6 +317,7 @@ source "drivers/clk/mediatek/Kconfig"
>  source "drivers/clk/meson/Kconfig"
>  source "drivers/clk/mvebu/Kconfig"
>  source "drivers/clk/qcom/Kconfig"
> +source "drivers/clk/realtek/Kconfig"
>  source "drivers/clk/renesas/Kconfig"
>  source "drivers/clk/samsung/Kconfig"
>  source "drivers/clk/sifive/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 0138fb14e6f8..71ea17f97f7d 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -95,6 +95,7 @@ obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
>  obj-$(CONFIG_MACH_PISTACHIO)		+= pistachio/
>  obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
>  obj-$(CONFIG_COMMON_CLK_QCOM)		+= qcom/
> +obj-$(CONFIG_COMMON_CLK_REALTEK)	+= realtek/

Should we take the Renesas approach here and allow for COMPILE_TEST?

>  obj-y					+= renesas/
>  obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
>  obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
> diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
> new file mode 100644
> index 000000000000..5bca757dddfa
> --- /dev/null
> +++ b/drivers/clk/realtek/Kconfig
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config COMMON_CLK_REALTEK

I would personally think that it's not necessary to prefix with COMMON_
here, even if based on CONFIG_COMMON_CLK framework, since Realtek is no
longer common. Stephen/Michael?

Which brings us to the next aspect, is this really universally common
for Realtek? Are they compatible with old MIPS SoCs and Arm SoCs from
departments other than DHC? (e.g., Smart TV)
If the answer to any of these is no, then please rename the Kconfig
symbol to the oldest SoC for uniqueness, e.g., RTD1195.

> +	bool "Clock driver for realtek"

Spelling.

> +	select MFD_SYSCON

Please add help text and include SoC names.

> +
> +config CLK_PLL_PSAUD

Too generic name.

> +	bool
> +
> +config CLK_PLL_DIF

Ditto.

> +	bool
> diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
> new file mode 100644
> index 000000000000..050d450db067
> --- /dev/null
> +++ b/drivers/clk/realtek/Makefile
> @@ -0,0 +1,9 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_COMMON_CLK_REALTEK) += clk-rtk.o
> +
> +clk-rtk-y += common.o
> +clk-rtk-y += clk-regmap-mux.o
> +clk-rtk-y += clk-regmap-gate.o
> +clk-rtk-y += clk-pll.o
> +clk-rtk-$(CONFIG_CLK_PLL_PSAUD) += clk-pll-psaud.o
> +clk-rtk-$(CONFIG_CLK_PLL_DIF) += clk-pll-dif.o
> diff --git a/drivers/clk/realtek/clk-pll-dif.c b/drivers/clk/realtek/clk-pll-dif.c
> new file mode 100644
> index 000000000000..d19efef2626e
> --- /dev/null
> +++ b/drivers/clk/realtek/clk-pll-dif.c
> @@ -0,0 +1,81 @@
> +// SPDX-License-Identifier: GPL-2.0-only

Can you relicense this code as GPL-2.0-or-later? For Makefile and
Kconfig it doesn't matter, just for low-level code that could become
relevant for debuggers like OpenOCD, which is licensed GPLv2+.

> +/*

Care to elaborate here what "dif" is? :)

> + * Copyright (c) 2019 Realtek Semiconductor Corporation
> + * Author: Cheng-Yu Lee <cylee12@realtek.com>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clk.h>
> +#include <linux/spinlock.h>
> +#include <linux/delay.h>

Insert white line here?

> +#include "common.h"
> +#include "clk-pll.h"
> +
> +static int clk_pll_dif_enable(struct clk_hw *hw)
> +{
> +	struct clk_pll_dif *pll = to_clk_pll_dif(hw);
> +
> +	pr_debug("%pC: %s\n", hw->clk, __func__);

Please review debug and info messages for whether they are still needed
- in particular for info below I assume no.

> +
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000048);
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00020c00);
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204004ca);
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x00, 0x8000a000);

This is way too much dark magic!

Start with giving the offsets symbolic names, please.

Next, construct the value from symbolic constants. You will see me use
BIT() and GENMASK() macros elsewhere; please adopt those conventions.

> +	udelay(100);

Is this from some internal datasheet? Otherwise, from some MCU clocks
that I've previously implemented, I would expect there to be some status
bit indicating readyness that we should poll rather than trusting a
hardcoded delay. I don't see a single read below, nor any explanatory
comment.

> +
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c00);
> +	udelay(50);
> +
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c03);
> +	udelay(200);
> +
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000078);
> +	udelay(100);
> +
> +	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204084ca);
> +
> +	/* ssc control */

This lonely comment seems kind of redundant with ssc_ofs vs. pll_ofs.

> +	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000004);
> +	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x04, 0x00006800);
> +	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x0C, 0x00000000);
> +	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x10, 0x00000000);
> +	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x08, 0x001e1f98);
> +	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000005);
> +	pll->status = 1;
> +
> +	return 0;
> +}
[snip]

I'll stop reviewing here and am waiting for a v2.

Thanks,
Andreas

-- 
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer
HRB 36809 (AG Nürnberg)

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs
  2019-12-03  7:45 James Tai
@ 2019-12-03  7:45 ` James Tai
  2019-12-03 15:28   ` Andreas Färber
  0 siblings, 1 reply; 10+ messages in thread
From: James Tai @ 2019-12-03  7:45 UTC (permalink / raw)
  To: Andreas Färber
  Cc: cylee12, linux-realtek-soc, Stephen Boyd, Michael Turquette,
	Palmer Dabbelt, linux-kernel, linux-mediatek, Paul Walmsley,
	Matthias Brugger, linux-riscv, linux-clk, linux-arm-kernel

From: cylee12 <cylee12@realtek.com>

This patch adds common clock support for Realtek SoCs, including PLLs,
Mux clocks and Gate clocks.

Signed-off-by: Cheng-Yu Lee <cylee12@realtek.com>
Signed-off-by: James Tai <james.tai@realtek.com>
---
 drivers/clk/Kconfig                   |   1 +
 drivers/clk/Makefile                  |   1 +
 drivers/clk/realtek/Kconfig           |  10 +
 drivers/clk/realtek/Makefile          |   9 +
 drivers/clk/realtek/clk-pll-dif.c     |  81 ++++++
 drivers/clk/realtek/clk-pll-psaud.c   | 120 ++++++++
 drivers/clk/realtek/clk-pll.c         | 400 ++++++++++++++++++++++++++
 drivers/clk/realtek/clk-pll.h         | 151 ++++++++++
 drivers/clk/realtek/clk-regmap-gate.c |  89 ++++++
 drivers/clk/realtek/clk-regmap-gate.h |  26 ++
 drivers/clk/realtek/clk-regmap-mux.c  |  63 ++++
 drivers/clk/realtek/clk-regmap-mux.h  |  26 ++
 drivers/clk/realtek/common.c          | 320 +++++++++++++++++++++
 drivers/clk/realtek/common.h          | 123 ++++++++
 14 files changed, 1420 insertions(+)
 create mode 100644 drivers/clk/realtek/Kconfig
 create mode 100644 drivers/clk/realtek/Makefile
 create mode 100644 drivers/clk/realtek/clk-pll-dif.c
 create mode 100644 drivers/clk/realtek/clk-pll-psaud.c
 create mode 100644 drivers/clk/realtek/clk-pll.c
 create mode 100644 drivers/clk/realtek/clk-pll.h
 create mode 100644 drivers/clk/realtek/clk-regmap-gate.c
 create mode 100644 drivers/clk/realtek/clk-regmap-gate.h
 create mode 100644 drivers/clk/realtek/clk-regmap-mux.c
 create mode 100644 drivers/clk/realtek/clk-regmap-mux.h
 create mode 100644 drivers/clk/realtek/common.c
 create mode 100644 drivers/clk/realtek/common.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c44247d0b83e..8e06487440ce 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -317,6 +317,7 @@ source "drivers/clk/mediatek/Kconfig"
 source "drivers/clk/meson/Kconfig"
 source "drivers/clk/mvebu/Kconfig"
 source "drivers/clk/qcom/Kconfig"
+source "drivers/clk/realtek/Kconfig"
 source "drivers/clk/renesas/Kconfig"
 source "drivers/clk/samsung/Kconfig"
 source "drivers/clk/sifive/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 0138fb14e6f8..71ea17f97f7d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
 obj-$(CONFIG_MACH_PISTACHIO)		+= pistachio/
 obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
 obj-$(CONFIG_COMMON_CLK_QCOM)		+= qcom/
+obj-$(CONFIG_COMMON_CLK_REALTEK)	+= realtek/
 obj-y					+= renesas/
 obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
diff --git a/drivers/clk/realtek/Kconfig b/drivers/clk/realtek/Kconfig
new file mode 100644
index 000000000000..5bca757dddfa
--- /dev/null
+++ b/drivers/clk/realtek/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config COMMON_CLK_REALTEK
+	bool "Clock driver for realtek"
+	select MFD_SYSCON
+
+config CLK_PLL_PSAUD
+	bool
+
+config CLK_PLL_DIF
+	bool
diff --git a/drivers/clk/realtek/Makefile b/drivers/clk/realtek/Makefile
new file mode 100644
index 000000000000..050d450db067
--- /dev/null
+++ b/drivers/clk/realtek/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_COMMON_CLK_REALTEK) += clk-rtk.o
+
+clk-rtk-y += common.o
+clk-rtk-y += clk-regmap-mux.o
+clk-rtk-y += clk-regmap-gate.o
+clk-rtk-y += clk-pll.o
+clk-rtk-$(CONFIG_CLK_PLL_PSAUD) += clk-pll-psaud.o
+clk-rtk-$(CONFIG_CLK_PLL_DIF) += clk-pll-dif.o
diff --git a/drivers/clk/realtek/clk-pll-dif.c b/drivers/clk/realtek/clk-pll-dif.c
new file mode 100644
index 000000000000..d19efef2626e
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-dif.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_dif_enable(struct clk_hw *hw)
+{
+	struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+	pr_debug("%pC: %s\n", hw->clk, __func__);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000048);
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00020c00);
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204004ca);
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x00, 0x8000a000);
+	udelay(100);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c00);
+	udelay(50);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x08, 0x00420c03);
+	udelay(200);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000078);
+	udelay(100);
+
+	clk_regmap_write(&pll->clkr, pll->pll_ofs + 0x04, 0x204084ca);
+
+	/* ssc control */
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000004);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x04, 0x00006800);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x0C, 0x00000000);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x10, 0x00000000);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x08, 0x001e1f98);
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000005);
+	pll->status = 1;
+
+	return 0;
+}
+
+static void clk_pll_dif_disable(struct clk_hw *hw)
+{
+	struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+	pr_debug("%pC: %s\n", hw->clk, __func__);
+	clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x04, 0x00080000, 0x0);
+	clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x08, 0x00400C03, 0x0);
+	clk_regmap_update(&pll->clkr, pll->pll_ofs + 0x0C, 0x00000038, 0x0);
+
+	clk_regmap_write(&pll->clkr, pll->ssc_ofs + 0x00, 0x00000004);
+	pll->status = 0;
+}
+
+static int clk_pll_dif_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pll_dif *pll = to_clk_pll_dif(hw);
+
+	return pll->status;
+}
+
+static void clk_pll_dif_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_pll_dif_disable(hw);
+}
+
+const struct clk_ops clk_pll_dif_ops = {
+	.enable           = clk_pll_dif_enable,
+	.disable          = clk_pll_dif_disable,
+	.disable_unused   = clk_pll_dif_disable_unused,
+	.is_enabled       = clk_pll_dif_is_enabled,
+};
+
diff --git a/drivers/clk/realtek/clk-pll-psaud.c b/drivers/clk/realtek/clk-pll-psaud.c
new file mode 100644
index 000000000000..757a3320ebb7
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll-psaud.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * clk-pll-psaud.c - PLL_PSAUDXA
+ *
+ * Copyright (c) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk-provider.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_psaud_enable(struct clk_hw *hw)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 mask = 0, val = 0;
+
+	if (pll->id == CLK_PLL_PSAUD1A) {
+		mask = 0x3;
+		val = 0x1;
+	} else {
+		mask = 0xc;
+		val = 0x4;
+	}
+	clk_regmap_update(&pll->clkr, pll->reg + 4, mask, val);
+	return 0;
+}
+
+static void clk_pll_psaud_disable(struct clk_hw *hw)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 mask = 0, val = 0;
+
+	if (pll->id == CLK_PLL_PSAUD1A) {
+		mask = 0x3;
+		val = 0x3;
+	} else {
+		mask = 0xc;
+		val = 0xc;
+	}
+	clk_regmap_update(&pll->clkr, pll->reg + 4, mask, val);
+}
+
+static void clk_pll_psaud_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_pll_psaud_disable(hw);
+}
+
+static int clk_pll_psaud_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 val;
+
+	val = clk_regmap_read(&pll->clkr, pll->reg + 4);
+	if (pll->id == CLK_PLL_PSAUD1A)
+		val &= 0x3;
+	else
+		val >>= 2;
+	return val == 0x1;
+}
+
+static long clk_pll_psaud_round_rate(struct clk_hw *hw,
+				     unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	return 49192000;
+}
+
+static int clk_pll_psaud_set_rate(struct clk_hw *hw,
+				  unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 rsel = 0;
+	u32 mask = 0, val = 0;
+
+	if (WARN_ON_ONCE(rate != 45158400 && rate != 49192000))
+		return -EINVAL;
+
+	if (rate == 45158400)
+		rsel = 1;
+	if (pll->id == CLK_PLL_PSAUD1A) {
+		val  = 0x6a0 | (rsel << 8);
+		mask = 0x7e0;
+	} else {
+		val  = 0x19 | (rsel << 2);
+		mask = 0x1f;
+	}
+	clk_regmap_update(&pll->clkr, pll->reg, mask, val);
+	return 0;
+}
+
+static unsigned long clk_pll_psaud_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct clk_pll_psaud *pll = to_clk_pll_psaud(hw);
+	u32 val;
+	u32 rsel = 0;
+
+	val = clk_regmap_read(&pll->clkr, pll->reg);
+
+	if (pll->id == CLK_PLL_PSAUD1A)
+		rsel = !!(val & BIT(8));
+	else
+		rsel = !!(val & BIT(2));
+
+	return rsel ? 45158400 : 49192000;
+}
+
+const struct clk_ops clk_pll_psaud_ops = {
+	.enable         = clk_pll_psaud_enable,
+	.disable        = clk_pll_psaud_disable,
+	.disable_unused = clk_pll_psaud_disable_unused,
+	.is_enabled     = clk_pll_psaud_is_enabled,
+	.set_rate       = clk_pll_psaud_set_rate,
+	.round_rate     = clk_pll_psaud_round_rate,
+	.recalc_rate    = clk_pll_psaud_recalc_rate,
+};
+
diff --git a/drivers/clk/realtek/clk-pll.c b/drivers/clk/realtek/clk-pll.c
new file mode 100644
index 000000000000..dd90bfc011ec
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2018 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include "common.h"
+#include "clk-pll.h"
+
+static int clk_pll_debug_rate_u64_set(void *data, u64 val)
+{
+	struct clk_hw *hw = data;
+
+	clk_set_rate(hw->clk, (unsigned long)(val));
+	return 0;
+}
+
+static int clk_pll_debug_rate_u64_get(void *data, u64 *val)
+{
+	struct clk_hw *hw = data;
+
+	*val = (u64)clk_get_rate(hw->clk);
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_pll_debut_rate_op, clk_pll_debug_rate_u64_get,
+			clk_pll_debug_rate_u64_set, "%llu\n");
+
+static void clk_pll_debug_init(struct clk_hw *hw, struct dentry *d)
+{
+	debugfs_create_file("debug_rate", 0644, d, hw, &clk_pll_debut_rate_op);
+}
+
+static const struct freq_table *ftbl_find_by_rate(const struct freq_table *ftbl,
+						  unsigned long rate)
+{
+	unsigned long best_rate = 0;
+	const struct freq_table *best = NULL;
+
+	for ( ; !IS_FREQ_TABLE_END(ftbl); ftbl++) {
+		if (ftbl->rate == rate)
+			return ftbl;
+
+		if (ftbl->rate > rate)
+			continue;
+
+		if ((rate - best_rate) > (rate - ftbl->rate)) {
+			best_rate = ftbl->rate;
+			best = ftbl;
+		}
+	}
+
+	return best;
+}
+
+static const struct freq_table *ftbl_find_by_val(const struct freq_table *ftbl,
+						 uint32_t value)
+{
+	while (!IS_FREQ_TABLE_END(ftbl)) {
+		if (ftbl->val == (value))
+			return ftbl;
+		ftbl++;
+	}
+	return NULL;
+};
+
+static const struct div_table *dtbl_find_by_rate(const struct div_table *dtbl,
+						 unsigned long rate)
+{
+	while (!IS_DIV_TABLE_END(dtbl)) {
+		if (rate >= dtbl->rate)
+			return dtbl;
+		dtbl++;
+	}
+	return NULL;
+}
+
+static const struct div_table *dtbl_find_by_val(const struct div_table *dtbl,
+						uint32_t val)
+{
+	while (!IS_DIV_TABLE_END(dtbl)) {
+		if (val == dtbl->val)
+			return dtbl;
+		dtbl++;
+	}
+	return NULL;
+}
+
+static void __clk_pll_set_pow_reg(struct clk_pll *clkp, int on)
+{
+	uint32_t pow = (clkp->pow_loc == CLK_PLL_CONF_POW_LOC_CTL3) ? 0x8 : 0x4;
+
+	if (on) {
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + pow, 0x7, 0x3);
+		if (clkp->freq_loc == CLK_PLL_CONF_FREQ_LOC_SSC1) {
+			uint32_t val;
+
+			val = clk_regmap_read(&clkp->clkr, clkp->ssc_ofs + 0x0);
+
+			/*
+			 * For those PLL with SCC used only the default
+			 * freq, the oc_en would nerver to be set.
+			 * Help to set it here.
+			 */
+			if ((val & 0x7) != 0x5)
+				clk_regmap_update(&clkp->clkr,
+					clkp->ssc_ofs + 0x0, 0x7, 0x5);
+		}
+		udelay(200);
+	} else {
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + pow, 0x7, 0x4);
+	}
+}
+
+static int clk_pll_enable(struct clk_hw *hw)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+
+	if (clk_pll_has_pow(clkp))
+		__clk_pll_set_pow_reg(clkp, 1);
+	return 0;
+}
+
+static void clk_pll_disable(struct clk_hw *hw)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+
+	if (clk_pll_has_pow(clkp))
+		__clk_pll_set_pow_reg(clkp, 0);
+}
+
+static void clk_pll_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_pll_disable(hw);
+}
+
+static int clk_pll_is_enabled(struct clk_hw *hw)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	uint32_t pow;
+	uint32_t val;
+
+	if (!clk_pll_has_pow(clkp))
+		return -EINVAL;
+
+	pow = (clkp->pow_loc == CLK_PLL_CONF_POW_LOC_CTL3) ? 0x8 : 0x4;
+	val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + pow);
+	return !!(val & 0x1);
+}
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *parent_rate)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	const struct freq_table *ftblv = NULL;
+
+	ftblv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+	return ftblv ? ftblv->rate : 0;
+}
+
+static uint32_t __clk_pll_freq_get(struct clk_pll *clkp)
+{
+	uint32_t val = 0;
+
+	switch (clkp->freq_loc) {
+	case CLK_PLL_CONF_FREQ_LOC_CTL1:
+		val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + 0x0);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_CTL2:
+		val = clk_regmap_read(&clkp->clkr, clkp->pll_ofs + 0x4);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_SSC1:
+		val = clk_regmap_read(&clkp->clkr, clkp->ssc_ofs + 0x4);
+		break;
+
+	default:
+		break;
+	}
+	return val & clkp->freq_mask;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	unsigned long flags = 0;
+	const struct freq_table *fv;
+	uint32_t val;
+
+	flags = clk_pll_lock(clkp);
+	val = __clk_pll_freq_get(clkp);
+	clk_pll_unlock(clkp, flags);
+
+	fv = ftbl_find_by_val(clkp->freq_tbl, val);
+	return fv ? fv->rate : 0;
+}
+
+static inline int __clk_pll_freq_set(struct clk_pll *clkp, uint32_t val)
+{
+	struct clk_hw *hw = &clkp->clkr.hw;
+	int ret = 0;
+	uint32_t mask = clkp->freq_mask;
+	uint32_t pollval;
+
+	switch (clkp->freq_loc) {
+	case CLK_PLL_CONF_FREQ_LOC_CTL1:
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs, mask, val);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_CTL2:
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x4, mask, val);
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x8, 0x1, 0x0);
+		clk_regmap_update(&clkp->clkr, clkp->pll_ofs + 0x8, 0x1, 0x1);
+		break;
+
+	case CLK_PLL_CONF_FREQ_LOC_SSC1:
+		clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x0, 0x7, 0x4);
+		clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x4, mask, val);
+		clk_regmap_update(&clkp->clkr, clkp->ssc_ofs + 0x0, 0x7, 0x5);
+
+		if (clk_pll_has_pow(clkp) && !clk_pll_is_enabled(hw))
+			return 0;
+
+		ret = regmap_read_poll_timeout(clkp->clkr.regmap,
+					       clkp->ssc_ofs + 0x1c,
+					       pollval, pollval & BIT(20),
+					       0, 2000);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	struct clk_pll *clkp = to_clk_pll(hw);
+	unsigned long flags = 0;
+	const struct freq_table *fv;
+	int ret = 0;
+
+	fv = ftbl_find_by_rate(clkp->freq_tbl, rate);
+	if (!fv)
+		return -EINVAL;
+
+	pr_debug("%pC: %s: rate=%ld, val=0x%08x\n", hw->clk, __func__,
+		 fv->rate, fv->val);
+
+	flags = clk_pll_lock(clkp);
+	ret = __clk_pll_freq_set(clkp, fv->val);
+	clk_pll_unlock(clkp, flags);
+	if (ret)
+		pr_warn("%pC %s: failed to set freq: %d\n", hw->clk, __func__,
+			ret);
+	return ret;
+}
+
+static void __clk_pll_div_set(struct clk_pll_div *clkpd, uint32_t val)
+{
+	uint32_t m = (BIT(clkpd->div_width) - 1) << clkpd->div_shift;
+	uint32_t s = clkpd->div_shift;
+
+	clk_regmap_update(&clkpd->clkp.clkr, clkpd->div_ofs, m, val << s);
+}
+
+static uint32_t __clk_pll_div_get(struct clk_pll_div *clkpd)
+{
+	uint32_t m = (BIT(clkpd->div_width) - 1) << clkpd->div_shift;
+	uint32_t s = clkpd->div_shift;
+
+	return (clk_regmap_read(&clkpd->clkp.clkr, clkpd->div_ofs) & m) >> s;
+}
+
+static long clk_pll_div_round_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long *parent_rate)
+{
+	struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+	const struct div_table *dv;
+
+	/* lookup div in dtbl */
+	dv = dtbl_find_by_rate(clkpd->div_tbl, rate);
+	if (!dv)
+		return 0;
+
+	rate *= dv->div;
+	rate = clk_pll_round_rate(hw, rate, parent_rate);
+	return rate / dv->div;
+}
+
+static unsigned long clk_pll_div_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+	unsigned long rate;
+	const struct div_table *dv;
+	uint32_t val;
+
+	rate = clk_pll_recalc_rate(hw, parent_rate);
+
+	val = __clk_pll_div_get(clkpd);
+	dv = dtbl_find_by_val(clkpd->div_tbl, val);
+	if (!dv)
+		return 0;
+
+	rate /= dv->div;
+	pr_debug("%pC: %s: current rate=%lu, div=%d, reg_val=0x%x\n",
+		 hw->clk, __func__, rate, dv->div, val);
+
+	return rate;
+}
+
+static int clk_pll_div_set_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long parent_rate)
+{
+	struct clk *clk = hw->clk;
+	struct clk_pll_div *clkpd = to_clk_pll_div(hw);
+	unsigned long flags;
+	const struct div_table *ndv, *cdv;
+	unsigned long target;
+	uint32_t cur_d;
+	int ret;
+
+	/* find next in the dtbl */
+	ndv = dtbl_find_by_rate(clkpd->div_tbl, rate);
+	if (!ndv)
+		return -EINVAL;
+
+	target = rate * ndv->div;
+
+	/* find current in the dtbl */
+	cur_d = __clk_pll_div_get(clkpd);
+	cdv = dtbl_find_by_val(clkpd->div_tbl, cur_d);
+	if (!cdv)
+		return -EINVAL;
+
+	pr_debug("%pC: rate=%lu, cdv={%d,0x%x}, ndv={%d,0x%x}\n",
+		 clk, rate, cdv->div, cdv->val, ndv->div, ndv->val);
+
+	flags = clk_pll_div_lock(clkpd);
+
+	/* workaround to prevent glitch */
+#ifdef CONFIG_COMMON_CLK_RTD129X
+	if ((&clkpd->clkp.flags & CLK_PLL_DIV_WORKAROUND) &&
+		ndv->val != cdv->val && (ndv->val == 1 || cdv->val == 1)) {
+
+		pr_debug("%pC: apply rate=%u\n", clk, 1000000000);
+		clk_pll_set_rate(hw, 1000000000, parent_rate);
+
+		pr_debug("%pC: apply dv={%d, 0x%x}\n", clk, ndv->div, ndv->val);
+		__clk_pll_div_set(clkpd, ndv->val);
+		cdv = ndv;
+	}
+#endif
+
+	if (ndv->div > cdv->div)
+		__clk_pll_div_set(clkpd, ndv->val);
+	ret = clk_pll_set_rate(hw, target, parent_rate);
+	if (ndv->div < cdv->div)
+		__clk_pll_div_set(clkpd, ndv->val);
+
+	clk_pll_div_unlock(clkpd, flags);
+
+	return ret;
+}
+
+const struct clk_ops clk_pll_ops = {
+	.debug_init       = clk_pll_debug_init,
+	.round_rate       = clk_pll_round_rate,
+	.recalc_rate      = clk_pll_recalc_rate,
+	.set_rate         = clk_pll_set_rate,
+	.enable           = clk_pll_enable,
+	.disable          = clk_pll_disable,
+	.disable_unused   = clk_pll_disable_unused,
+	.is_enabled       = clk_pll_is_enabled,
+};
+
+const struct clk_ops clk_pll_div_ops = {
+	.debug_init       = clk_pll_debug_init,
+	.round_rate       = clk_pll_div_round_rate,
+	.recalc_rate      = clk_pll_div_recalc_rate,
+	.set_rate         = clk_pll_div_set_rate,
+	.enable           = clk_pll_enable,
+	.disable          = clk_pll_disable,
+	.disable_unused   = clk_pll_disable_unused,
+	.is_enabled       = clk_pll_is_enabled,
+};
+
diff --git a/drivers/clk/realtek/clk-pll.h b/drivers/clk/realtek/clk-pll.h
new file mode 100644
index 000000000000..03b4d0391c31
--- /dev/null
+++ b/drivers/clk/realtek/clk-pll.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_PLL_H
+#define __CLK_REALTEK_CLK_PLL_H
+
+#include "common.h"
+
+struct freq_table {
+	uint32_t val;
+	unsigned long rate;
+};
+
+#define FREQ_TABLE_END                  { .rate = 0 }
+#define IS_FREQ_TABLE_END(_f)           ((_f)->rate == 0)
+
+struct div_table {
+	unsigned long rate;
+	uint32_t div;
+	uint32_t val;
+};
+
+#define DIV_TABLE_END                   { .rate = 0 }
+#define IS_DIV_TABLE_END(_d)            ((_d)->rate == 0)
+
+struct clk_pll {
+	struct clk_regmap clkr;
+
+
+	int pll_ofs;
+	int ssc_ofs;
+
+	const struct freq_table *freq_tbl;
+	uint32_t freq_mask;
+	uint32_t freq_loc;
+#define CLK_PLL_CONF_FREQ_LOC_CTL1      1
+#define CLK_PLL_CONF_FREQ_LOC_CTL2      2
+#define CLK_PLL_CONF_FREQ_LOC_SSC1      3
+
+	uint32_t pow_loc;
+#define CLK_PLL_CONF_NO_POW             0
+#define CLK_PLL_CONF_POW_LOC_CTL2       1
+#define CLK_PLL_CONF_POW_LOC_CTL3       2
+
+	spinlock_t *lock;
+
+	uint32_t flags;
+};
+
+#define to_clk_pll(_hw) container_of(to_clk_regmap(_hw), struct clk_pll, clkr)
+#define __clk_pll_hw(_ptr)  __clk_regmap_hw(&(_ptr)->clkr)
+
+/* clk_pll flags */
+#define CLK_PLL_DIV_WORKAROUND          BIT(2)
+
+
+static inline bool clk_pll_has_pow(struct clk_pll *pll)
+{
+	if (pll->pow_loc != CLK_PLL_CONF_NO_POW)
+		return true;
+	return false;
+}
+
+static inline unsigned long clk_pll_lock(struct clk_pll *pll)
+{
+	unsigned long flags = 0;
+
+	if (pll->lock)
+		spin_lock_irqsave(pll->lock, flags);
+	return flags;
+}
+
+static inline void clk_pll_unlock(struct clk_pll *pll, unsigned long flags)
+{
+	if (pll->lock)
+		spin_unlock_irqrestore(pll->lock, flags);
+}
+
+struct clk_pll_div {
+	struct clk_pll clkp;
+	int div_ofs;
+	int div_shift;
+	int div_width;
+	const struct div_table *div_tbl;
+	spinlock_t *lock;
+};
+
+#define to_clk_pll_div(_hw) \
+	container_of(to_clk_pll(_hw), struct clk_pll_div, clkp)
+#define __clk_pll_div_hw(_ptr) __clk_pll_hw(&(_ptr)->clkp)
+
+/* clk_pll_div helper functions */
+static inline unsigned long clk_pll_div_lock(struct clk_pll_div *plld)
+{
+	unsigned long flags = 0;
+
+	if (plld->lock)
+		spin_lock_irqsave(plld->lock, flags);
+	return flags;
+}
+
+static inline void clk_pll_div_unlock(struct clk_pll_div *plld,
+	unsigned long flags)
+{
+	if (plld->lock)
+		spin_unlock_irqrestore(plld->lock, flags);
+}
+
+extern const struct clk_ops clk_pll_ops;
+extern const struct clk_ops clk_pll_div_ops;
+
+#ifdef CONFIG_CLK_PLL_PSAUD
+
+struct clk_pll_psaud {
+	struct clk_regmap clkr;
+	int id;
+	int reg;
+	spinlock_t *lock;
+};
+
+#define to_clk_pll_psaud(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_pll_psaud, clkr)
+#define __clk_pll_psaud_hw(_ptr)  __clk_regmap_hw(&(_ptr)->clkr)
+extern const struct clk_ops clk_pll_psaud_ops;
+
+#define CLK_PLL_PSAUD1A       (0x1)
+#define CLK_PLL_PSAUD2A       (0x2)
+
+#endif /* CONFIG_CLK_PLL_PSAUD */
+
+#ifdef CONFIG_CLK_PLL_DIF
+
+struct clk_pll_dif {
+	struct clk_regmap clkr;
+	int pll_ofs;
+	int ssc_ofs;
+	uint32_t status;
+	spinlock_t *lock;
+};
+#define to_clk_pll_dif(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_pll_dif, clkr)
+#define __clk_pll_dif_hw(_ptr)  __clk_regmap_hw(&(_ptr)->clkr)
+
+extern const struct clk_ops clk_pll_dif_ops;
+#endif /* CONFIG_CLK_PLL_DIF */
+
+
+#endif /* __CLK_REALTEK_CLK_PLL_H */
diff --git a/drivers/clk/realtek/clk-regmap-gate.c b/drivers/clk/realtek/clk-regmap-gate.c
new file mode 100644
index 000000000000..5f4a6a98da94
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include "clk-regmap-gate.h"
+
+static int clk_regmap_gate_enable(struct clk_hw *hw)
+{
+	struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+	unsigned long flags = 0;
+	unsigned int mask;
+	unsigned int val;
+
+	if (clkg->lock)
+		spin_lock_irqsave(clkg->lock, flags);
+
+	mask = BIT(clkg->bit_idx);
+	val  = BIT(clkg->bit_idx);
+
+	if (clkg->write_en) {
+		mask |= BIT(clkg->bit_idx + 1);
+		val  |= BIT(clkg->bit_idx + 1);
+	}
+
+	clk_regmap_update(&clkg->clkr, clkg->gate_ofs, mask, val);
+
+	if (clkg->lock)
+		spin_unlock_irqrestore(clkg->lock, flags);
+
+	return 0;
+}
+
+static void clk_regmap_gate_disable(struct clk_hw *hw)
+{
+	struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+	unsigned long flags = 0;
+	unsigned int mask;
+	unsigned int val;
+
+	if (clkg->lock)
+		spin_lock_irqsave(clkg->lock, flags);
+
+	mask = BIT(clkg->bit_idx);
+	val  = 0;
+
+	if (clkg->write_en) {
+		mask |= BIT(clkg->bit_idx + 1);
+		val  |= BIT(clkg->bit_idx + 1);
+	}
+
+	clk_regmap_update(&clkg->clkr, clkg->gate_ofs, mask, val);
+
+	if (clkg->lock)
+		spin_unlock_irqrestore(clkg->lock, flags);
+}
+
+static void clk_regmap_gate_disable_unused(struct clk_hw *hw)
+{
+	pr_info("%pC: %s\n", hw->clk, __func__);
+	clk_regmap_gate_disable(hw);
+}
+
+static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
+{
+	struct clk_regmap_gate *clkg = to_clk_regmap_gate(hw);
+	int ret;
+	unsigned long flags = 0;
+
+	if (clkg->lock)
+		spin_lock_irqsave(clkg->lock, flags);
+
+	ret = clk_regmap_read(&clkg->clkr, clkg->gate_ofs) & BIT(clkg->bit_idx);
+
+	if (clkg->lock)
+		spin_unlock_irqrestore(clkg->lock, flags);
+
+	return !!ret;
+}
+
+const struct clk_ops clk_regmap_gate_ops = {
+	.enable = clk_regmap_gate_enable,
+	.disable = clk_regmap_gate_disable,
+	.disable_unused = clk_regmap_gate_disable_unused,
+	.is_enabled = clk_regmap_gate_is_enabled,
+};
diff --git a/drivers/clk/realtek/clk-regmap-gate.h b/drivers/clk/realtek/clk-regmap-gate.h
new file mode 100644
index 000000000000..7643e0193177
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-gate.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_MMIO_GATE_H
+#define __CLK_REALTEK_CLK_MMIO_GATE_H
+
+#include "common.h"
+
+struct clk_regmap_gate {
+	struct clk_regmap clkr;
+	int gate_ofs;
+	uint8_t bit_idx;
+	spinlock_t *lock;
+	int write_en:1;
+};
+
+#define to_clk_regmap_gate(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_regmap_gate, clkr)
+#define __clk_regmap_gate_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+extern const struct clk_ops clk_regmap_gate_ops;
+
+#endif
diff --git a/drivers/clk/realtek/clk-regmap-mux.c b/drivers/clk/realtek/clk-regmap-mux.c
new file mode 100644
index 000000000000..bd7eb706e12a
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "clk-regmap-mux.h"
+
+static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
+{
+	struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+	int num_parents = clk_hw_get_num_parents(hw);
+	u32 val;
+	unsigned long flags = 0;
+
+	if (clkm->lock)
+		spin_lock_irqsave(clkm->lock, flags);
+
+	val = clk_regmap_read(&clkm->clkr, clkm->mux_ofs) >> clkm->shift;
+	if (clkm->lock)
+		spin_unlock_irqrestore(clkm->lock, flags);
+
+	val &= clkm->mask;
+	if (val >= num_parents)
+		return -EINVAL;
+
+	return val;
+}
+
+static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct clk_regmap_mux *clkm = to_clk_regmap_mux(hw);
+	unsigned long flags = 0;
+
+	if (clkm->lock)
+		spin_lock_irqsave(clkm->lock, flags);
+
+	clk_regmap_update(&clkm->clkr, clkm->mux_ofs, clkm->mask << clkm->shift,
+		index << clkm->shift);
+
+	if (clkm->lock)
+		spin_unlock_irqrestore(clkm->lock, flags);
+	return 0;
+}
+
+const struct clk_ops clk_regmap_mux_ops = {
+	.get_parent = clk_regmap_mux_get_parent,
+	.set_parent = clk_regmap_mux_set_parent,
+	.determine_rate = __clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
+
+const struct clk_ops clk_regmap_mux_ro_ops = {
+	.get_parent = clk_regmap_mux_get_parent,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
+
diff --git a/drivers/clk/realtek/clk-regmap-mux.h b/drivers/clk/realtek/clk-regmap-mux.h
new file mode 100644
index 000000000000..36895f03c0f4
--- /dev/null
+++ b/drivers/clk/realtek/clk-regmap-mux.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_CLK_MMIO_MUX_H
+#define __CLK_REALTEK_CLK_MMIO_MUX_H
+
+#include "common.h"
+
+struct clk_regmap_mux {
+	struct clk_regmap clkr;
+	int mux_ofs;
+	unsigned int mask;
+	unsigned int shift;
+	spinlock_t *lock;
+};
+
+#define to_clk_regmap_mux(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_regmap_mux, clkr)
+#define __clk_regmap_mux_hw(_p) __clk_regmap_hw(&(_p)->clkr)
+
+extern const struct clk_ops clk_regmap_mux_ops;
+
+#endif
diff --git a/drivers/clk/realtek/common.c b/drivers/clk/realtek/common.c
new file mode 100644
index 000000000000..0c5dc5ce4682
--- /dev/null
+++ b/drivers/clk/realtek/common.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include "common.h"
+#include "clk-pll.h"
+#include "clk-regmap-gate.h"
+#include "clk-regmap-mux.h"
+
+static int rtk_clk_suspend(struct device *dev)
+{
+	struct rtk_clk_data *data = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; i < data->pm_data_num; i++) {
+		struct clk_pm_data *pm_data = &data->pm_data[i];
+
+		regmap_read(data->regmap, pm_data->ofs, &pm_data->val);
+	}
+	return 0;
+}
+
+static int rtk_clk_resume(struct device *dev)
+{
+	struct rtk_clk_data *data = dev_get_drvdata(dev);
+	int i;
+
+	for (i = data->pm_data_num - 1; i >= 0; i--) {
+		struct clk_pm_data *pm_data = &data->pm_data[i];
+		uint32_t val = pm_data->val;
+		uint32_t mask = ~0x0;
+
+		if (pm_data->write_en_bits)
+			val |= pm_data->write_en_bits;
+		if (pm_data->ignore_bits)
+			mask &= ~pm_data->ignore_bits;
+		dev_info(dev, "resuming: ofs=%03x, vs=%08x vr=%08x m=%08x\n",
+			pm_data->ofs, pm_data->val, val, mask);
+		regmap_update_bits(data->regmap, pm_data->ofs, mask, val);
+	}
+	return 0;
+}
+
+const struct dev_pm_ops rtk_clk_pm_ops = {
+	.suspend = rtk_clk_suspend,
+	.resume  = rtk_clk_resume,
+};
+
+struct rtk_clk_data *alloc_rtk_clk_data(int clk_num)
+{
+	struct rtk_clk_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return NULL;
+
+	data->clk_num = clk_num;
+	data->clk_data.clk_num = clk_num;
+	data->clk_data.clks = kcalloc(clk_num, sizeof(*data->clk_data.clks),
+				      GFP_KERNEL);
+	if (!data->clk_data.clks)
+		goto free_data;
+	return data;
+
+free_data:
+	kfree(data->clk_data.clks);
+	kfree(data);
+	return NULL;
+}
+
+void free_rtk_clk_data(struct rtk_clk_data *data)
+{
+	kfree(data->clk_data.clks);
+	kfree(data);
+}
+
+static inline
+int __cell_clk_add(struct clk_onecell_data *clk_data, int i, struct clk *clk)
+{
+	if (clk_data->clks[i]) {
+		pr_err("%s: failed to add %pC, cell%d is used by %pC\n",
+			__func__, clk, i, clk_data->clks[i]);
+		return -EINVAL;
+	}
+	clk_data->clks[i] = clk;
+	return 0;
+}
+
+#define CLK_TYPE_DEFAULT                (0x0)
+#define CLK_TYPE_REGMAP                 (0x8)
+#define CLK_TYPE_REGMAP_PLL             (0x1 | CLK_TYPE_REGMAP)
+#define CLK_TYPE_REGMAP_MUX             (0x2 | CLK_TYPE_REGMAP)
+#define CLK_TYPE_REGMAP_GATE            (0x3 | CLK_TYPE_REGMAP)
+
+static inline int __hw_to_type(struct clk_hw *hw)
+{
+	const struct clk_ops *ops = hw->init->ops;
+
+	if (ops == &clk_pll_ops || ops == &clk_pll_div_ops)
+		return CLK_TYPE_REGMAP_PLL;
+	if (ops == &clk_regmap_mux_ops)
+		return CLK_TYPE_REGMAP_MUX;
+	if (ops == &clk_regmap_gate_ops)
+		return CLK_TYPE_REGMAP_GATE;
+#ifdef CONFIG_CLK_PLL_DIF
+	/*
+	 * clk_pll_dif is not based struct clk_pll, so
+	 * return as CLK_TYPE_REGMAP to setup internal
+	 * clk_reg
+	 */
+	if (ops == &clk_pll_dif_ops)
+		return CLK_TYPE_REGMAP;
+#endif
+#ifdef CONFIG_CLK_PLL_PSAUD
+	/*
+	 * clk_pll_psaud is not based struct clk_pll, so
+	 * return as CLK_TYPE_REGMAP to setup internal
+	 * clk_reg
+	 */
+	if (ops == &clk_pll_psaud_ops)
+		return CLK_TYPE_REGMAP;
+#endif
+
+	return CLK_TYPE_DEFAULT;
+}
+
+static
+struct clk *rtk_clk_register_hw(struct device *dev, struct regmap *regmap,
+				struct clk_hw *hw)
+{
+	int type;
+
+	type = __hw_to_type(hw);
+	if (type & CLK_TYPE_REGMAP) {
+		struct clk_regmap *clkr = to_clk_regmap(hw);
+
+		clkr->regmap = regmap;
+	}
+
+	return clk_register(dev, hw);
+}
+
+int rtk_clk_add_hws(struct device *dev, struct rtk_clk_data *data,
+		    struct clk_hw **hws, int num)
+{
+	struct clk_onecell_data *clk_data = &data->clk_data;
+	struct regmap *regmap = data->regmap;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		struct clk_hw *hw = hws[i];
+		const char *name;
+		struct clk *clk;
+
+		if (IS_ERR(hw))
+			__cell_clk_add(clk_data, i, ERR_CAST(hw));
+		if (IS_ERR_OR_NULL(hw))
+			continue;
+
+		name = hw->init->name;
+		clk = rtk_clk_register_hw(dev, regmap, hw);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to add hw%d(%s): %ld\n", __func__,
+				i, name, PTR_ERR(clk));
+			continue;
+		}
+
+		clk_register_clkdev(clk, name, NULL);
+		__cell_clk_add(clk_data, i, clk);
+	}
+	return 0;
+}
+
+static
+struct clk *rtk_clk_register_composite(struct device *dev,
+				       struct regmap *regmap,
+				       struct clk_composite_data *comp)
+{
+	struct clk_regmap_mux *clkm = NULL;
+	const struct clk_ops *mux_op = NULL;
+	struct clk_hw *mux_hw = NULL;
+	struct clk_regmap_gate *clkg = NULL;
+	const struct clk_ops *gate_op = NULL;
+	struct clk_hw *gate_hw = NULL;
+	struct clk *clk;
+
+	if (comp->mux_ofs != CLK_OFS_INVALID) {
+		clkm = kzalloc(sizeof(*clkm), GFP_KERNEL);
+		if (!clkm) {
+			clk = ERR_PTR(-ENOMEM);
+			goto check_err;
+		}
+
+		clkm->mux_ofs     = comp->mux_ofs;
+		clkm->mask        = BIT(comp->mux_width) - 1;
+		clkm->shift       = comp->mux_shift;
+		clkm->clkr.regmap = regmap;
+
+		mux_op = &clk_regmap_mux_ops;
+		mux_hw = &__clk_regmap_mux_hw(clkm);
+	}
+
+	if (comp->gate_ofs != CLK_OFS_INVALID) {
+		clkg = kzalloc(sizeof(*clkg), GFP_KERNEL);
+		if (!clkg) {
+			clk = ERR_PTR(-ENOMEM);
+			goto check_err;
+		}
+
+		clkg->gate_ofs    = comp->gate_ofs;
+		clkg->bit_idx     = comp->gate_shift;
+		clkg->write_en    = comp->gate_write_en;
+		clkg->clkr.regmap = regmap;
+
+		gate_op = &clk_regmap_gate_ops;
+		gate_hw = &__clk_regmap_gate_hw(clkg);
+	}
+
+	clk = clk_register_composite(NULL, comp->name, comp->parent_names,
+				     comp->num_parents, mux_hw, mux_op,
+				     NULL, NULL, gate_hw, gate_op, comp->flags);
+check_err:
+	if (IS_ERR(clk)) {
+		kfree(clkm);
+		kfree(clkg);
+	}
+	return clk;
+}
+
+int rtk_clk_add_composites(struct device *dev, struct rtk_clk_data *data,
+			   struct clk_composite_data *comps, int num)
+{
+	struct clk_onecell_data *clk_data = &data->clk_data;
+	struct regmap *regmap = data->regmap;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		struct clk_composite_data *comp = &comps[i];
+		const char *name = comp->name;
+		struct clk *clk;
+
+		clk = rtk_clk_register_composite(dev, regmap, comp);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to add composite%d(%s): %ld\n",
+				__func__, i, name, PTR_ERR(clk));
+			continue;
+		}
+
+		clk_register_clkdev(clk, name, NULL);
+		__cell_clk_add(clk_data, comp->id, clk);
+	}
+
+	return 0;
+}
+
+static
+struct clk *rtk_clk_register_gate(struct device *dev, struct regmap *regmap,
+				  struct clk_gate_data *gate)
+{
+	struct clk_regmap_gate *clkg;
+	struct clk_init_data init = { 0 };
+	struct clk_hw *hw;
+
+	clkg = kzalloc(sizeof(*clkg), GFP_KERNEL);
+	if (!clkg)
+		return ERR_PTR(-ENOMEM);
+
+	clkg->gate_ofs    = gate->gate_ofs;
+	clkg->bit_idx     = gate->gate_shift;
+	clkg->write_en    = gate->gate_write_en;
+	clkg->clkr.regmap = regmap;
+
+	init.name         = gate->name;
+	init.ops          = &clk_regmap_gate_ops;
+	init.flags         = gate->flags;
+	if (gate->parent) {
+		init.parent_names = &gate->parent;
+		init.num_parents  = 1;
+	}
+
+	hw = &__clk_regmap_gate_hw(clkg);
+	hw->init = &init;
+	return clk_register(dev, hw);
+}
+
+int rtk_clk_add_gates(struct device *dev, struct rtk_clk_data *data,
+		      struct clk_gate_data *gates, int num)
+{
+	struct clk_onecell_data *clk_data = &data->clk_data;
+	struct regmap *regmap = data->regmap;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		struct clk_gate_data *gate = &gates[i];
+		const char *name = gate->name;
+		struct clk *clk;
+
+		clk = rtk_clk_register_gate(dev, regmap, gate);
+		if (IS_ERR(clk)) {
+			pr_err("%s: failed to add gate%d(%s): %ld\n", __func__,
+				i, name, PTR_ERR(clk));
+			continue;
+		}
+
+		clk_register_clkdev(clk, name, NULL);
+		__cell_clk_add(clk_data, gate->id, clk);
+	}
+
+	return 0;
+}
+
diff --git a/drivers/clk/realtek/common.h b/drivers/clk/realtek/common.h
new file mode 100644
index 000000000000..c736b254852f
--- /dev/null
+++ b/drivers/clk/realtek/common.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016-2019 Realtek Semiconductor Corporation
+ * Author: Cheng-Yu Lee <cylee12@realtek.com>
+ */
+
+#ifndef __CLK_REALTEK_COMMON_H
+#define __CLK_REALTEK_COMMON_H
+
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+struct device;
+struct platform_device;
+
+struct clk_regmap {
+	struct clk_hw hw;
+	struct regmap *regmap;
+};
+
+#define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
+#define __clk_regmap_hw(_p) ((_p)->hw)
+
+static inline
+void clk_regmap_write(struct clk_regmap *clkr, uint32_t ofs, uint32_t val)
+{
+	pr_debug("%s: ofs=%03x, val=%08x\n", __func__, ofs, val);
+	regmap_write(clkr->regmap, ofs, val);
+}
+
+static inline
+uint32_t clk_regmap_read(struct clk_regmap *clkr, uint32_t ofs)
+{
+	uint32_t val = 0;
+
+	regmap_read(clkr->regmap, ofs, &val);
+	pr_debug("%s: ofs=%03x, val=%08x\n", __func__, ofs, val);
+	return val;
+}
+
+static inline void clk_regmap_update(struct clk_regmap *clkr, uint32_t ofs,
+				     uint32_t mask, uint32_t val)
+{
+	pr_debug("%s: ofs=%03x, mask=%08x, val=%08x\n", __func__, ofs,
+		 mask, val);
+	regmap_update_bits(clkr->regmap, ofs, mask, val);
+}
+
+/* ofs check */
+#define CLK_OFS_INVALID                 (-1)
+#define CLK_OFS_IS_VALID(_ofs)          ((_ofs) != CLK_OFS_INVALID)
+
+struct clk_composite_data {
+	int id;
+	const char *name;
+	unsigned long flags;
+	struct clk *clk;
+
+	int gate_ofs;
+	int gate_shift;
+	int gate_write_en;
+
+	int mux_ofs;
+	int mux_width;
+	int mux_shift;
+	const char * const *parent_names;
+	int num_parents;
+};
+
+struct clk_gate_data {
+	int id;
+	const char *name;
+	const char *parent;
+	unsigned long flags;
+	struct clk *clk;
+
+	int gate_ofs;
+	int gate_shift;
+	int gate_write_en;
+};
+
+#define CLK_GATE_DATA(_id, _name, _parent, _flags, _ofs, _shift, _write_en) \
+{ \
+	.id = _id, \
+	.name = _name, \
+	.parent = _parent, \
+	.flags = _flags, \
+	.gate_ofs = _ofs, \
+	.gate_shift = _shift, \
+	.gate_write_en = _write_en, \
+}
+
+struct clk_pm_data {
+	int ofs;
+	uint32_t ignore_bits;
+	uint32_t write_en_bits;
+	uint32_t val;
+};
+
+struct rtk_clk_data {
+	int clk_num;
+	struct regmap *regmap;
+	struct clk_onecell_data clk_data;
+	struct clk_pm_data *pm_data;
+	int pm_data_num;
+};
+
+struct rtk_clk_data *alloc_rtk_clk_data(int clk_num);
+void free_rtk_clk_data(struct rtk_clk_data *data);
+int rtk_clk_add_hws(struct device *dev, struct rtk_clk_data *data,
+		    struct clk_hw **hws, int num);
+int rtk_clk_add_composites(struct device *dev, struct rtk_clk_data *data,
+			   struct clk_composite_data *comps, int num);
+int rtk_clk_add_gates(struct device *dev, struct rtk_clk_data *data,
+		      struct clk_gate_data *gates, int num);
+extern const struct dev_pm_ops rtk_clk_pm_ops;
+
+#endif /* __CLK_REALTEK_COMMON_H */
+
-- 
2.24.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

end of thread, back to index

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-03  7:35 [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers James Tai
2019-12-03  7:35 ` [PATCH 1/6] dt-bindings: clock: add bindings for RTD1619 clocks James Tai
2019-12-03  7:35 ` [PATCH 2/6] dt-bindings: reset: add bindings for rtd1619 reset controls James Tai
2019-12-03  7:35 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
2019-12-03  7:35 ` [PATCH 4/6] clk: realtek: add reset controller " James Tai
2019-12-03  7:35 ` [PATCH 5/6] clk: realtek: add rtd1619 controllers James Tai
2019-12-03  7:35 ` [PATCH 6/6] dt-bindings: clk: realtek: add rtd1619 clock controller bindings James Tai
2019-12-03  8:15 ` [PATCH 0/6] arm64: Realtek RTD1619 clock and reset controllers Andreas Färber
2019-12-03  7:45 James Tai
2019-12-03  7:45 ` [PATCH 3/6] clk: realtek: add common clock support for Realtek SoCs James Tai
2019-12-03 15:28   ` Andreas Färber

Linux-ARM-Kernel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-arm-kernel/0 linux-arm-kernel/git/0.git
	git clone --mirror https://lore.kernel.org/linux-arm-kernel/1 linux-arm-kernel/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-arm-kernel linux-arm-kernel/ https://lore.kernel.org/linux-arm-kernel \
		linux-arm-kernel@lists.infradead.org
	public-inbox-index linux-arm-kernel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.infradead.lists.linux-arm-kernel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git