2024-03-09 09:02:36

by Inochi Amaoto

[permalink] [raw]
Subject: [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

Add clock controller support for the Sophgo CV1800B, CV1812H and SG2000.

Changed from v8:
1. improve code.
2. remove default config in Kconfig.
3. merge patch 2-4 of v8 into one.

Changed from v7:
1. fix unused variables warnings in patch 3 of v7
2. fix wrong pointer type in patch 3 of v7
3. move the clk_disp_vip_parents variable to the patch 5 to avoid warning

Changed from v6:
1. fix dead lock when setting rate.
2. split the driver patch into several patch for easy reading.

Changed from v5:
1. rebased to mainline master tree
2. add SG2000 clock support.
3. fix document link

Changed from v4:
1. improve code for patch 2
2. remove the already applied bindings
https://lore.kernel.org/all/IA1PR20MB49535E448097F6FFC1218C39BB90A@IA1PR20MB4953.namprd20.prod.outlook.com/

Changed from v3:
1. improve comment of patch 3
2. cleanup the include of patch 2

Changed from v2:
1. remove clock-names from bindings.
2. remove clock-frequency node of DT from previous patch.
3. change some unused clock to bypass mode to avoid unlockable PLL.

Changed from v1:
1. fix license issues.

Inochi Amaoto (6):
dt-bindings: clock: sophgo: Add clock controller of SG2000 series SoC
clk: sophgo: Add clock support for CV1800 SoC
clk: sophgo: Add clock support for CV1810 SoC
clk: sophgo: Add clock support for SG2000 SoC
riscv: dts: sophgo: add clock generator for Sophgo CV1800 series SoC
riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC

.../bindings/clock/sophgo,cv1800-clk.yaml | 3 +-
arch/riscv/boot/dts/sophgo/cv1800b.dtsi | 4 +
arch/riscv/boot/dts/sophgo/cv1812h.dtsi | 4 +
arch/riscv/boot/dts/sophgo/cv18xx.dtsi | 22 +-
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/sophgo/Kconfig | 11 +
drivers/clk/sophgo/Makefile | 7 +
drivers/clk/sophgo/clk-cv1800.c | 1537 +++++++++++++++++
drivers/clk/sophgo/clk-cv1800.h | 123 ++
drivers/clk/sophgo/clk-cv18xx-common.c | 66 +
drivers/clk/sophgo/clk-cv18xx-common.h | 81 +
drivers/clk/sophgo/clk-cv18xx-ip.c | 887 ++++++++++
drivers/clk/sophgo/clk-cv18xx-ip.h | 261 +++
drivers/clk/sophgo/clk-cv18xx-pll.c | 420 +++++
drivers/clk/sophgo/clk-cv18xx-pll.h | 118 ++
16 files changed, 3540 insertions(+), 6 deletions(-)
create mode 100644 drivers/clk/sophgo/Kconfig
create mode 100644 drivers/clk/sophgo/Makefile
create mode 100644 drivers/clk/sophgo/clk-cv1800.c
create mode 100644 drivers/clk/sophgo/clk-cv1800.h
create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.c
create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.h
create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.c
create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.h
create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.c
create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.h

--
2.44.0



2024-03-09 09:03:36

by Inochi Amaoto

[permalink] [raw]
Subject: [PATCH v9 1/6] dt-bindings: clock: sophgo: Add clock controller of SG2000 series SoC

SG2000 series SoC has the same clock as CV1810 series, but the clock
related to A53 is functional in SG2000 series. So a new compatible
string is needed for the new SoC.

Add definition for the clock controller of the SG2000 series SoC.

Link: https://github.com/sophgo/sophgo-doc/releases/tag/sg2000-datasheet-v1.0-alpha
Acked-by: Krzysztof Kozlowski <[email protected]>
Signed-off-by: Inochi Amaoto <[email protected]>
---
Documentation/devicetree/bindings/clock/sophgo,cv1800-clk.yaml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/clock/sophgo,cv1800-clk.yaml b/Documentation/devicetree/bindings/clock/sophgo,cv1800-clk.yaml
index c1dc24673c0d..59ef41adb539 100644
--- a/Documentation/devicetree/bindings/clock/sophgo,cv1800-clk.yaml
+++ b/Documentation/devicetree/bindings/clock/sophgo,cv1800-clk.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/clock/sophgo,cv1800-clk.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

-title: Sophgo CV1800 Series Clock Controller
+title: Sophgo CV1800/SG2000 Series Clock Controller

maintainers:
- Inochi Amaoto <[email protected]>
@@ -14,6 +14,7 @@ properties:
enum:
- sophgo,cv1800-clk
- sophgo,cv1810-clk
+ - sophgo,sg2000-clk

reg:
maxItems: 1
--
2.44.0


2024-03-09 09:03:56

by Inochi Amaoto

[permalink] [raw]
Subject: [PATCH v9 3/6] clk: sophgo: Add clock support for CV1810 SoC

Add clock definition and init code for CV1810 SoC.

Signed-off-by: Inochi Amaoto <[email protected]>
Link: https://github.com/milkv-duo/duo-files/blob/6f4e9b8ecb459e017cca1a8df248a19ca70837a3/duo/datasheet/CV180X-Clock-v1.xlsx
---
drivers/clk/sophgo/clk-cv1800.c | 195 ++++++++++++++++++++++++++++++++
drivers/clk/sophgo/clk-cv1800.h | 1 +
2 files changed, 196 insertions(+)

diff --git a/drivers/clk/sophgo/clk-cv1800.c b/drivers/clk/sophgo/clk-cv1800.c
index 6606a2701b12..c7d4aa6c8343 100644
--- a/drivers/clk/sophgo/clk-cv1800.c
+++ b/drivers/clk/sophgo/clk-cv1800.c
@@ -631,6 +631,10 @@ static const struct clk_parent_data clk_vip_sys_parents[] = {
{ .hw = &clk_disppll.common.hw },
{ .hw = &clk_fpll.common.hw },
};
+static const struct clk_parent_data clk_disp_vip_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_disppll.common.hw },
+};

static CV1800_BYPASS_DIV(clk_dsi_esc, clk_bypass_axi6_bus_parents,
REG_CLK_EN_2, 3,
@@ -660,6 +664,11 @@ static CV1800_BYPASS_MUX(clk_src_vip_sys_1, clk_vip_sys_parents,
REG_DIV_CLK_SRC_VIP_SYS_1, 8, 2,
REG_CLK_BYP_0, 24,
0);
+static CV1800_BYPASS_DIV(clk_disp_src_vip, clk_disp_vip_parents,
+ REG_CLK_EN_2, 7,
+ REG_DIV_CLK_DISP_SRC_VIP, 16, 4, 8, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 25,
+ 0);
static CV1800_BYPASS_MUX(clk_src_vip_sys_2, clk_vip_sys_parents,
REG_CLK_EN_3, 29,
REG_DIV_CLK_SRC_VIP_SYS_2, 16, 4, 2, CV1800_DIV_FLAG,
@@ -1243,6 +1252,191 @@ static const struct cv1800_clk_desc cv1800_desc = {
.pre_init = cv1800_pre_init,
};

+static struct clk_hw_onecell_data cv1810_hw_clks = {
+ .num = CV1810_CLK_MAX,
+ .hws = {
+ [CLK_MPLL] = &clk_mpll.common.hw,
+ [CLK_TPLL] = &clk_tpll.common.hw,
+ [CLK_FPLL] = &clk_fpll.common.hw,
+ [CLK_MIPIMPLL] = &clk_mipimpll.common.hw,
+ [CLK_A0PLL] = &clk_a0pll.common.hw,
+ [CLK_DISPPLL] = &clk_disppll.common.hw,
+ [CLK_CAM0PLL] = &clk_cam0pll.common.hw,
+ [CLK_CAM1PLL] = &clk_cam1pll.common.hw,
+
+ [CLK_MIPIMPLL_D3] = &clk_mipimpll_d3.common.hw,
+ [CLK_CAM0PLL_D2] = &clk_cam0pll_d2.common.hw,
+ [CLK_CAM0PLL_D3] = &clk_cam0pll_d3.common.hw,
+
+ [CLK_TPU] = &clk_tpu.mux.common.hw,
+ [CLK_TPU_FAB] = &clk_tpu_fab.common.hw,
+ [CLK_AHB_ROM] = &clk_ahb_rom.common.hw,
+ [CLK_DDR_AXI_REG] = &clk_ddr_axi_reg.common.hw,
+ [CLK_RTC_25M] = &clk_rtc_25m.common.hw,
+ [CLK_SRC_RTC_SYS_0] = &clk_src_rtc_sys_0.div.common.hw,
+ [CLK_TEMPSEN] = &clk_tempsen.common.hw,
+ [CLK_SARADC] = &clk_saradc.common.hw,
+ [CLK_EFUSE] = &clk_efuse.common.hw,
+ [CLK_APB_EFUSE] = &clk_apb_efuse.common.hw,
+ [CLK_DEBUG] = &clk_debug.common.hw,
+ [CLK_AP_DEBUG] = &clk_ap_debug.div.common.hw,
+ [CLK_XTAL_MISC] = &clk_xtal_misc.common.hw,
+ [CLK_AXI4_EMMC] = &clk_axi4_emmc.common.hw,
+ [CLK_EMMC] = &clk_emmc.mux.common.hw,
+ [CLK_EMMC_100K] = &clk_emmc_100k.common.hw,
+ [CLK_AXI4_SD0] = &clk_axi4_sd0.common.hw,
+ [CLK_SD0] = &clk_sd0.mux.common.hw,
+ [CLK_SD0_100K] = &clk_sd0_100k.common.hw,
+ [CLK_AXI4_SD1] = &clk_axi4_sd1.common.hw,
+ [CLK_SD1] = &clk_sd1.mux.common.hw,
+ [CLK_SD1_100K] = &clk_sd1_100k.common.hw,
+ [CLK_SPI_NAND] = &clk_spi_nand.mux.common.hw,
+ [CLK_ETH0_500M] = &clk_eth0_500m.div.common.hw,
+ [CLK_AXI4_ETH0] = &clk_axi4_eth0.common.hw,
+ [CLK_ETH1_500M] = &clk_eth1_500m.div.common.hw,
+ [CLK_AXI4_ETH1] = &clk_axi4_eth1.common.hw,
+ [CLK_APB_GPIO] = &clk_apb_gpio.common.hw,
+ [CLK_APB_GPIO_INTR] = &clk_apb_gpio_intr.common.hw,
+ [CLK_GPIO_DB] = &clk_gpio_db.common.hw,
+ [CLK_AHB_SF] = &clk_ahb_sf.common.hw,
+ [CLK_AHB_SF1] = &clk_ahb_sf1.common.hw,
+ [CLK_A24M] = &clk_a24m.common.hw,
+ [CLK_AUDSRC] = &clk_audsrc.mux.common.hw,
+ [CLK_APB_AUDSRC] = &clk_apb_audsrc.common.hw,
+ [CLK_SDMA_AXI] = &clk_sdma_axi.common.hw,
+ [CLK_SDMA_AUD0] = &clk_sdma_aud0.mux.common.hw,
+ [CLK_SDMA_AUD1] = &clk_sdma_aud1.mux.common.hw,
+ [CLK_SDMA_AUD2] = &clk_sdma_aud2.mux.common.hw,
+ [CLK_SDMA_AUD3] = &clk_sdma_aud3.mux.common.hw,
+ [CLK_I2C] = &clk_i2c.div.common.hw,
+ [CLK_APB_I2C] = &clk_apb_i2c.common.hw,
+ [CLK_APB_I2C0] = &clk_apb_i2c0.common.hw,
+ [CLK_APB_I2C1] = &clk_apb_i2c1.common.hw,
+ [CLK_APB_I2C2] = &clk_apb_i2c2.common.hw,
+ [CLK_APB_I2C3] = &clk_apb_i2c3.common.hw,
+ [CLK_APB_I2C4] = &clk_apb_i2c4.common.hw,
+ [CLK_APB_WDT] = &clk_apb_wdt.common.hw,
+ [CLK_PWM_SRC] = &clk_pwm_src.mux.common.hw,
+ [CLK_PWM] = &clk_pwm.common.hw,
+ [CLK_SPI] = &clk_spi.div.common.hw,
+ [CLK_APB_SPI0] = &clk_apb_spi0.common.hw,
+ [CLK_APB_SPI1] = &clk_apb_spi1.common.hw,
+ [CLK_APB_SPI2] = &clk_apb_spi2.common.hw,
+ [CLK_APB_SPI3] = &clk_apb_spi3.common.hw,
+ [CLK_1M] = &clk_1m.common.hw,
+ [CLK_CAM0_200] = &clk_cam0_200.mux.common.hw,
+ [CLK_PM] = &clk_pm.common.hw,
+ [CLK_TIMER0] = &clk_timer0.common.hw,
+ [CLK_TIMER1] = &clk_timer1.common.hw,
+ [CLK_TIMER2] = &clk_timer2.common.hw,
+ [CLK_TIMER3] = &clk_timer3.common.hw,
+ [CLK_TIMER4] = &clk_timer4.common.hw,
+ [CLK_TIMER5] = &clk_timer5.common.hw,
+ [CLK_TIMER6] = &clk_timer6.common.hw,
+ [CLK_TIMER7] = &clk_timer7.common.hw,
+ [CLK_UART0] = &clk_uart0.common.hw,
+ [CLK_APB_UART0] = &clk_apb_uart0.common.hw,
+ [CLK_UART1] = &clk_uart1.common.hw,
+ [CLK_APB_UART1] = &clk_apb_uart1.common.hw,
+ [CLK_UART2] = &clk_uart2.common.hw,
+ [CLK_APB_UART2] = &clk_apb_uart2.common.hw,
+ [CLK_UART3] = &clk_uart3.common.hw,
+ [CLK_APB_UART3] = &clk_apb_uart3.common.hw,
+ [CLK_UART4] = &clk_uart4.common.hw,
+ [CLK_APB_UART4] = &clk_apb_uart4.common.hw,
+ [CLK_APB_I2S0] = &clk_apb_i2s0.common.hw,
+ [CLK_APB_I2S1] = &clk_apb_i2s1.common.hw,
+ [CLK_APB_I2S2] = &clk_apb_i2s2.common.hw,
+ [CLK_APB_I2S3] = &clk_apb_i2s3.common.hw,
+ [CLK_AXI4_USB] = &clk_axi4_usb.common.hw,
+ [CLK_APB_USB] = &clk_apb_usb.common.hw,
+ [CLK_USB_125M] = &clk_usb_125m.div.common.hw,
+ [CLK_USB_33K] = &clk_usb_33k.common.hw,
+ [CLK_USB_12M] = &clk_usb_12m.div.common.hw,
+ [CLK_AXI4] = &clk_axi4.mux.common.hw,
+ [CLK_AXI6] = &clk_axi6.div.common.hw,
+ [CLK_DSI_ESC] = &clk_dsi_esc.div.common.hw,
+ [CLK_AXI_VIP] = &clk_axi_vip.mux.common.hw,
+ [CLK_SRC_VIP_SYS_0] = &clk_src_vip_sys_0.mux.common.hw,
+ [CLK_SRC_VIP_SYS_1] = &clk_src_vip_sys_1.mux.common.hw,
+ [CLK_SRC_VIP_SYS_2] = &clk_src_vip_sys_2.mux.common.hw,
+ [CLK_SRC_VIP_SYS_3] = &clk_src_vip_sys_3.mux.common.hw,
+ [CLK_SRC_VIP_SYS_4] = &clk_src_vip_sys_4.mux.common.hw,
+ [CLK_CSI_BE_VIP] = &clk_csi_be_vip.common.hw,
+ [CLK_CSI_MAC0_VIP] = &clk_csi_mac0_vip.common.hw,
+ [CLK_CSI_MAC1_VIP] = &clk_csi_mac1_vip.common.hw,
+ [CLK_CSI_MAC2_VIP] = &clk_csi_mac2_vip.common.hw,
+ [CLK_CSI0_RX_VIP] = &clk_csi0_rx_vip.common.hw,
+ [CLK_CSI1_RX_VIP] = &clk_csi1_rx_vip.common.hw,
+ [CLK_ISP_TOP_VIP] = &clk_isp_top_vip.common.hw,
+ [CLK_IMG_D_VIP] = &clk_img_d_vip.common.hw,
+ [CLK_IMG_V_VIP] = &clk_img_v_vip.common.hw,
+ [CLK_SC_TOP_VIP] = &clk_sc_top_vip.common.hw,
+ [CLK_SC_D_VIP] = &clk_sc_d_vip.common.hw,
+ [CLK_SC_V1_VIP] = &clk_sc_v1_vip.common.hw,
+ [CLK_SC_V2_VIP] = &clk_sc_v2_vip.common.hw,
+ [CLK_SC_V3_VIP] = &clk_sc_v3_vip.common.hw,
+ [CLK_DWA_VIP] = &clk_dwa_vip.common.hw,
+ [CLK_BT_VIP] = &clk_bt_vip.common.hw,
+ [CLK_DISP_VIP] = &clk_disp_vip.common.hw,
+ [CLK_DSI_MAC_VIP] = &clk_dsi_mac_vip.common.hw,
+ [CLK_LVDS0_VIP] = &clk_lvds0_vip.common.hw,
+ [CLK_LVDS1_VIP] = &clk_lvds1_vip.common.hw,
+ [CLK_PAD_VI_VIP] = &clk_pad_vi_vip.common.hw,
+ [CLK_PAD_VI1_VIP] = &clk_pad_vi1_vip.common.hw,
+ [CLK_PAD_VI2_VIP] = &clk_pad_vi2_vip.common.hw,
+ [CLK_CFG_REG_VIP] = &clk_cfg_reg_vip.common.hw,
+ [CLK_VIP_IP0] = &clk_vip_ip0.common.hw,
+ [CLK_VIP_IP1] = &clk_vip_ip1.common.hw,
+ [CLK_VIP_IP2] = &clk_vip_ip2.common.hw,
+ [CLK_VIP_IP3] = &clk_vip_ip3.common.hw,
+ [CLK_IVE_VIP] = &clk_ive_vip.common.hw,
+ [CLK_RAW_VIP] = &clk_raw_vip.common.hw,
+ [CLK_OSDC_VIP] = &clk_osdc_vip.common.hw,
+ [CLK_CAM0_VIP] = &clk_cam0_vip.common.hw,
+ [CLK_AXI_VIDEO_CODEC] = &clk_axi_video_codec.mux.common.hw,
+ [CLK_VC_SRC0] = &clk_vc_src0.mux.common.hw,
+ [CLK_VC_SRC1] = &clk_vc_src1.div.common.hw,
+ [CLK_VC_SRC2] = &clk_vc_src2.div.common.hw,
+ [CLK_H264C] = &clk_h264c.common.hw,
+ [CLK_APB_H264C] = &clk_apb_h264c.common.hw,
+ [CLK_H265C] = &clk_h265c.common.hw,
+ [CLK_APB_H265C] = &clk_apb_h265c.common.hw,
+ [CLK_JPEG] = &clk_jpeg.common.hw,
+ [CLK_APB_JPEG] = &clk_apb_jpeg.common.hw,
+ [CLK_CAM0] = &clk_cam0.common.hw,
+ [CLK_CAM1] = &clk_cam1.common.hw,
+ [CLK_WGN] = &clk_wgn.common.hw,
+ [CLK_WGN0] = &clk_wgn0.common.hw,
+ [CLK_WGN1] = &clk_wgn1.common.hw,
+ [CLK_WGN2] = &clk_wgn2.common.hw,
+ [CLK_KEYSCAN] = &clk_keyscan.common.hw,
+ [CLK_CFG_REG_VC] = &clk_cfg_reg_vc.common.hw,
+ [CLK_C906_0] = &clk_c906_0.common.hw,
+ [CLK_C906_1] = &clk_c906_1.common.hw,
+ [CLK_A53] = &clk_a53.common.hw,
+ [CLK_CPU_AXI0] = &clk_cpu_axi0.div.common.hw,
+ [CLK_CPU_GIC] = &clk_cpu_gic.div.common.hw,
+ [CLK_XTAL_AP] = &clk_xtal_ap.common.hw,
+ [CLK_DISP_SRC_VIP] = &clk_disp_src_vip.div.common.hw,
+ },
+};
+
+static int cv1810_pre_init(struct device *dev, void __iomem *base,
+ struct cv1800_clk_ctrl *ctrl,
+ const struct cv1800_clk_desc *desc)
+{
+ cv18xx_clk_disable_a53(base);
+ cv18xx_clk_disable_auto_pd(base);
+
+ return 0;
+}
+
+static const struct cv1800_clk_desc cv1810_desc = {
+ .clks_data = &cv1810_hw_clks,
+ .pre_init = cv1810_pre_init,
+};
+
static int cv1800_clk_init_ctrl(struct device *dev, void __iomem *reg,
struct cv1800_clk_ctrl *ctrl,
const struct cv1800_clk_desc *desc)
@@ -1311,6 +1505,7 @@ static int cv1800_clk_probe(struct platform_device *pdev)

static const struct of_device_id cv1800_clk_ids[] = {
{ .compatible = "sophgo,cv1800-clk", .data = &cv1800_desc },
+ { .compatible = "sophgo,cv1810-clk", .data = &cv1810_desc },
{ }
};
MODULE_DEVICE_TABLE(of, cv1800_clk_ids);
diff --git a/drivers/clk/sophgo/clk-cv1800.h b/drivers/clk/sophgo/clk-cv1800.h
index 1b9c04b5ac3a..1e7107b5d05e 100644
--- a/drivers/clk/sophgo/clk-cv1800.h
+++ b/drivers/clk/sophgo/clk-cv1800.h
@@ -9,6 +9,7 @@
#include <dt-bindings/clock/sophgo,cv1800.h>

#define CV1800_CLK_MAX (CLK_XTAL_AP + 1)
+#define CV1810_CLK_MAX (CLK_DISP_SRC_VIP + 1)

#define REG_PLL_G2_CTRL 0x800
#define REG_PLL_G2_STATUS 0x804
--
2.44.0


2024-03-09 09:04:30

by Inochi Amaoto

[permalink] [raw]
Subject: [PATCH v9 5/6] riscv: dts: sophgo: add clock generator for Sophgo CV1800 series SoC

Add clock generator node for CV1800B and CV1812H.

Until now, It uses DT override to minimize duplication. This may
change in the future. See the last link for the discussion on
maintaining DT of CV1800 series.

Link: https://github.com/milkv-duo/duo-files/blob/6f4e9b8ecb459e017cca1a8df248a19ca70837a3/duo/datasheet/CV1800B-CV1801B-Preliminary-Datasheet-full-en.pdf
Link: https://lore.kernel.org/all/IA1PR20MB495373158F3B690EF3BF2901BB8BA@IA1PR20MB4953.namprd20.prod.outlook.com/
Reviewed-by: Chen Wang <[email protected]>
Signed-off-by: Inochi Amaoto <[email protected]>
---
arch/riscv/boot/dts/sophgo/cv1800b.dtsi | 4 ++++
arch/riscv/boot/dts/sophgo/cv1812h.dtsi | 4 ++++
arch/riscv/boot/dts/sophgo/cv18xx.dtsi | 6 ++++++
3 files changed, 14 insertions(+)

diff --git a/arch/riscv/boot/dts/sophgo/cv1800b.dtsi b/arch/riscv/boot/dts/sophgo/cv1800b.dtsi
index 165e9e320a8c..baf641829e72 100644
--- a/arch/riscv/boot/dts/sophgo/cv1800b.dtsi
+++ b/arch/riscv/boot/dts/sophgo/cv1800b.dtsi
@@ -16,3 +16,7 @@ &plic {
&clint {
compatible = "sophgo,cv1800b-clint", "thead,c900-clint";
};
+
+&clk {
+ compatible = "sophgo,cv1800-clk";
+};
diff --git a/arch/riscv/boot/dts/sophgo/cv1812h.dtsi b/arch/riscv/boot/dts/sophgo/cv1812h.dtsi
index 3e7a942f5c1a..7fa4c1e2d1da 100644
--- a/arch/riscv/boot/dts/sophgo/cv1812h.dtsi
+++ b/arch/riscv/boot/dts/sophgo/cv1812h.dtsi
@@ -22,3 +22,7 @@ &plic {
&clint {
compatible = "sophgo,cv1812h-clint", "thead,c900-clint";
};
+
+&clk {
+ compatible = "sophgo,cv1810-clk";
+};
diff --git a/arch/riscv/boot/dts/sophgo/cv18xx.dtsi b/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
index 2d6f4a4b1e58..6ea1b2784db9 100644
--- a/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
+++ b/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
@@ -53,6 +53,12 @@ soc {
dma-noncoherent;
ranges;

+ clk: clock-controller@3002000 {
+ reg = <0x03002000 0x1000>;
+ clocks = <&osc>;
+ #clock-cells = <1>;
+ };
+
gpio0: gpio@3020000 {
compatible = "snps,dw-apb-gpio";
reg = <0x3020000 0x1000>;
--
2.44.0


2024-03-09 09:04:26

by Inochi Amaoto

[permalink] [raw]
Subject: [PATCH v9 2/6] clk: sophgo: Add clock support for CV1800 SoC

Add clock definition and driver code for CV1800 SoC.

Signed-off-by: Inochi Amaoto <[email protected]>
Link: https://github.com/milkv-duo/duo-files/blob/6f4e9b8ecb459e017cca1a8df248a19ca70837a3/duo/datasheet/CV180X-Clock-v1.xlsx
Link: https://github.com/milkv-duo/duo-files/blob/6f4e9b8ecb459e017cca1a8df248a19ca70837a3/duo/datasheet/CV1800B-CV1801B-Preliminary-Datasheet-full-en.pdf
---
drivers/clk/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/sophgo/Kconfig | 11 +
drivers/clk/sophgo/Makefile | 7 +
drivers/clk/sophgo/clk-cv1800.c | 1327 ++++++++++++++++++++++++
drivers/clk/sophgo/clk-cv1800.h | 122 +++
drivers/clk/sophgo/clk-cv18xx-common.c | 66 ++
drivers/clk/sophgo/clk-cv18xx-common.h | 81 ++
drivers/clk/sophgo/clk-cv18xx-ip.c | 887 ++++++++++++++++
drivers/clk/sophgo/clk-cv18xx-ip.h | 261 +++++
drivers/clk/sophgo/clk-cv18xx-pll.c | 420 ++++++++
drivers/clk/sophgo/clk-cv18xx-pll.h | 118 +++
12 files changed, 3302 insertions(+)
create mode 100644 drivers/clk/sophgo/Kconfig
create mode 100644 drivers/clk/sophgo/Makefile
create mode 100644 drivers/clk/sophgo/clk-cv1800.c
create mode 100644 drivers/clk/sophgo/clk-cv1800.h
create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.c
create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.h
create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.c
create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.h
create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.c
create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.h

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 50af5fc7f570..bc28502ec3c9 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
source "drivers/clk/samsung/Kconfig"
source "drivers/clk/sifive/Kconfig"
source "drivers/clk/socfpga/Kconfig"
+source "drivers/clk/sophgo/Kconfig"
source "drivers/clk/sprd/Kconfig"
source "drivers/clk/starfive/Kconfig"
source "drivers/clk/sunxi/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 14fa8d4ecc1f..4abe16c8ccdf 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-y += socfpga/
+obj-y += sophgo/
obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-y += sprd/
obj-$(CONFIG_ARCH_STI) += st/
diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig
new file mode 100644
index 000000000000..1cc49be71bdb
--- /dev/null
+++ b/drivers/clk/sophgo/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+# common clock support for SOPHGO SoC family.
+
+config CLK_SOPHGO_CV1800
+ tristate "Support for the Sophgo CV1800 series SoCs clock controller"
+ depends on ARCH_SOPHGO || COMPILE_TEST
+ help
+ This driver supports clock controller of Sophgo CV18XX series SoC.
+ The driver require a 25MHz Oscillator to function generate clock.
+ It includes PLLs, common clock function and some vendor clock for
+ IPs of CV18XX series SoC
diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile
new file mode 100644
index 000000000000..a50320764200
--- /dev/null
+++ b/drivers/clk/sophgo/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_CLK_SOPHGO_CV1800) += clk-sophgo-cv1800.o
+
+clk-sophgo-cv1800-y += clk-cv1800.o
+clk-sophgo-cv1800-y += clk-cv18xx-common.o
+clk-sophgo-cv1800-y += clk-cv18xx-ip.o
+clk-sophgo-cv1800-y += clk-cv18xx-pll.o
diff --git a/drivers/clk/sophgo/clk-cv1800.c b/drivers/clk/sophgo/clk-cv1800.c
new file mode 100644
index 000000000000..6606a2701b12
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv1800.c
@@ -0,0 +1,1327 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+#include "clk-cv1800.h"
+
+#include "clk-cv18xx-common.h"
+#include "clk-cv18xx-ip.h"
+#include "clk-cv18xx-pll.h"
+
+struct cv1800_clk_ctrl;
+
+struct cv1800_clk_desc {
+ struct clk_hw_onecell_data *clks_data;
+
+ int (*pre_init)(struct device *dev, void __iomem *base,
+ struct cv1800_clk_ctrl *ctrl,
+ const struct cv1800_clk_desc *desc);
+};
+
+struct cv1800_clk_ctrl {
+ const struct cv1800_clk_desc *desc;
+ spinlock_t lock;
+};
+
+#define CV1800_DIV_FLAG \
+ (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST)
+static const struct clk_parent_data osc_parents[] = {
+ { .index = 0 },
+};
+
+static const struct cv1800_clk_pll_limit pll_limits[] = {
+ {
+ .pre_div = _CV1800_PLL_LIMIT(1, 127),
+ .div = _CV1800_PLL_LIMIT(6, 127),
+ .post_div = _CV1800_PLL_LIMIT(1, 127),
+ .ictrl = _CV1800_PLL_LIMIT(0, 7),
+ .mode = _CV1800_PLL_LIMIT(0, 3),
+ },
+ {
+ .pre_div = _CV1800_PLL_LIMIT(1, 127),
+ .div = _CV1800_PLL_LIMIT(6, 127),
+ .post_div = _CV1800_PLL_LIMIT(1, 127),
+ .ictrl = _CV1800_PLL_LIMIT(0, 7),
+ .mode = _CV1800_PLL_LIMIT(0, 3),
+ },
+};
+
+static CV1800_INTEGRAL_PLL(clk_fpll, osc_parents,
+ REG_FPLL_CSR,
+ REG_PLL_G6_CTRL, 8,
+ REG_PLL_G6_STATUS, 2,
+ pll_limits,
+ CLK_IS_CRITICAL);
+
+static CV1800_INTEGRAL_PLL(clk_mipimpll, osc_parents,
+ REG_MIPIMPLL_CSR,
+ REG_PLL_G2_CTRL, 0,
+ REG_PLL_G2_STATUS, 0,
+ pll_limits,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_mipimpll_parents[] = {
+ { .hw = &clk_mipimpll.common.hw },
+};
+static const struct clk_parent_data clk_bypass_mipimpll_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_mipimpll.common.hw },
+};
+static const struct clk_parent_data clk_bypass_fpll_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_fpll.common.hw },
+};
+
+struct cv1800_clk_pll_synthesizer clk_mpll_synthesizer = {
+ .en = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 2),
+ .clk_half = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 0),
+ .ctrl = REG_MPLL_SSC_SYN_CTRL,
+ .set = REG_MPLL_SSC_SYN_SET,
+};
+static CV1800_FACTIONAL_PLL(clk_mpll, clk_bypass_mipimpll_parents,
+ REG_MPLL_CSR,
+ REG_PLL_G6_CTRL, 0,
+ REG_PLL_G6_STATUS, 0,
+ pll_limits,
+ &clk_mpll_synthesizer,
+ CLK_IS_CRITICAL);
+
+struct cv1800_clk_pll_synthesizer clk_tpll_synthesizer = {
+ .en = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 3),
+ .clk_half = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 0),
+ .ctrl = REG_TPLL_SSC_SYN_CTRL,
+ .set = REG_TPLL_SSC_SYN_SET,
+};
+static CV1800_FACTIONAL_PLL(clk_tpll, clk_bypass_mipimpll_parents,
+ REG_TPLL_CSR,
+ REG_PLL_G6_CTRL, 4,
+ REG_PLL_G6_STATUS, 1,
+ pll_limits,
+ &clk_tpll_synthesizer,
+ CLK_IS_CRITICAL);
+
+struct cv1800_clk_pll_synthesizer clk_a0pll_synthesizer = {
+ .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 2),
+ .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0),
+ .ctrl = REG_A0PLL_SSC_SYN_CTRL,
+ .set = REG_A0PLL_SSC_SYN_SET,
+};
+static CV1800_FACTIONAL_PLL(clk_a0pll, clk_bypass_mipimpll_parents,
+ REG_A0PLL_CSR,
+ REG_PLL_G2_CTRL, 4,
+ REG_PLL_G2_STATUS, 1,
+ pll_limits,
+ &clk_a0pll_synthesizer,
+ CLK_IS_CRITICAL);
+
+struct cv1800_clk_pll_synthesizer clk_disppll_synthesizer = {
+ .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 3),
+ .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0),
+ .ctrl = REG_DISPPLL_SSC_SYN_CTRL,
+ .set = REG_DISPPLL_SSC_SYN_SET,
+};
+static CV1800_FACTIONAL_PLL(clk_disppll, clk_bypass_mipimpll_parents,
+ REG_DISPPLL_CSR,
+ REG_PLL_G2_CTRL, 8,
+ REG_PLL_G2_STATUS, 2,
+ pll_limits,
+ &clk_disppll_synthesizer,
+ CLK_IS_CRITICAL);
+
+struct cv1800_clk_pll_synthesizer clk_cam0pll_synthesizer = {
+ .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 4),
+ .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0),
+ .ctrl = REG_CAM0PLL_SSC_SYN_CTRL,
+ .set = REG_CAM0PLL_SSC_SYN_SET,
+};
+static CV1800_FACTIONAL_PLL(clk_cam0pll, clk_bypass_mipimpll_parents,
+ REG_CAM0PLL_CSR,
+ REG_PLL_G2_CTRL, 12,
+ REG_PLL_G2_STATUS, 3,
+ pll_limits,
+ &clk_cam0pll_synthesizer,
+ CLK_IGNORE_UNUSED);
+
+struct cv1800_clk_pll_synthesizer clk_cam1pll_synthesizer = {
+ .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 5),
+ .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0),
+ .ctrl = REG_CAM1PLL_SSC_SYN_CTRL,
+ .set = REG_CAM1PLL_SSC_SYN_SET,
+};
+static CV1800_FACTIONAL_PLL(clk_cam1pll, clk_bypass_mipimpll_parents,
+ REG_CAM1PLL_CSR,
+ REG_PLL_G2_CTRL, 16,
+ REG_PLL_G2_STATUS, 4,
+ pll_limits,
+ &clk_cam1pll_synthesizer,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_cam0pll_parents[] = {
+ { .hw = &clk_cam0pll.common.hw },
+};
+
+/* G2D */
+static CV1800_FIXED_DIV(clk_cam0pll_d2, clk_cam0pll_parents,
+ REG_CAM0PLL_CLK_CSR, 1,
+ 2,
+ CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED);
+static CV1800_FIXED_DIV(clk_cam0pll_d3, clk_cam0pll_parents,
+ REG_CAM0PLL_CLK_CSR, 2,
+ 3,
+ CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED);
+static CV1800_FIXED_DIV(clk_mipimpll_d3, clk_mipimpll_parents,
+ REG_MIPIMPLL_CLK_CSR, 2,
+ 3,
+ CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED);
+
+/* TPU */
+static const struct clk_parent_data clk_tpu_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_tpll.common.hw },
+ { .hw = &clk_a0pll.common.hw },
+ { .hw = &clk_mipimpll.common.hw },
+ { .hw = &clk_fpll.common.hw },
+};
+
+static CV1800_BYPASS_MUX(clk_tpu, clk_tpu_parents,
+ REG_CLK_EN_0, 4,
+ REG_DIV_CLK_TPU, 16, 4, 3, CV1800_DIV_FLAG,
+ REG_DIV_CLK_TPU, 8, 2,
+ REG_CLK_BYP_0, 3,
+ 0);
+static CV1800_GATE(clk_tpu_fab, clk_mipimpll_parents,
+ REG_CLK_EN_0, 5,
+ 0);
+
+/* FABRIC_AXI6 */
+static CV1800_BYPASS_DIV(clk_axi6, clk_bypass_fpll_parents,
+ REG_CLK_EN_2, 2,
+ REG_DIV_CLK_AXI6, 16, 4, 15, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 20,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_axi6_bus_parents[] = {
+ { .hw = &clk_axi6.div.common.hw },
+};
+static const struct clk_parent_data clk_bypass_axi6_bus_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_axi6.div.common.hw },
+};
+
+/* FABRIC_AXI4 */
+static const struct clk_parent_data clk_axi4_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_fpll.common.hw },
+ { .hw = &clk_disppll.common.hw },
+};
+
+static CV1800_BYPASS_MUX(clk_axi4, clk_axi4_parents,
+ REG_CLK_EN_2, 1,
+ REG_DIV_CLK_AXI4, 16, 4, 5, CV1800_DIV_FLAG,
+ REG_DIV_CLK_AXI4, 8, 2,
+ REG_CLK_BYP_0, 19,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_axi4_bus_parents[] = {
+ { .hw = &clk_axi4.mux.common.hw },
+};
+
+/* XTAL_MISC */
+static CV1800_GATE(clk_xtal_misc, osc_parents,
+ REG_CLK_EN_0, 14,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_timer_parents[] = {
+ { .hw = &clk_xtal_misc.common.hw },
+};
+
+/* TOP */
+static const struct clk_parent_data clk_cam0_200_parents[] = {
+ { .index = 0 },
+ { .index = 0 },
+ { .hw = &clk_disppll.common.hw },
+};
+
+static CV1800_BYPASS_MUX(clk_cam0_200, clk_cam0_200_parents,
+ REG_CLK_EN_1, 13,
+ REG_DIV_CLK_CAM0_200, 16, 4, 1, CV1800_DIV_FLAG,
+ REG_DIV_CLK_CAM0_200, 8, 2,
+ REG_CLK_BYP_0, 16,
+ CLK_IS_CRITICAL);
+static CV1800_DIV(clk_1m, osc_parents,
+ REG_CLK_EN_3, 5,
+ REG_DIV_CLK_1M, 16, 6, 25, CV1800_DIV_FLAG,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_pm, clk_axi6_bus_parents,
+ REG_CLK_EN_3, 8,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer0, clk_timer_parents,
+ REG_CLK_EN_3, 9,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer1, clk_timer_parents,
+ REG_CLK_EN_3, 10,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer2, clk_timer_parents,
+ REG_CLK_EN_3, 11,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer3, clk_timer_parents,
+ REG_CLK_EN_3, 12,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer4, clk_timer_parents,
+ REG_CLK_EN_3, 13,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer5, clk_timer_parents,
+ REG_CLK_EN_3, 14,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer6, clk_timer_parents,
+ REG_CLK_EN_3, 15,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_timer7, clk_timer_parents,
+ REG_CLK_EN_3, 16,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_parents_1m[] = {
+ { .hw = &clk_1m.common.hw },
+};
+static const struct clk_parent_data clk_uart_parents[] = {
+ { .hw = &clk_cam0_200.mux.common.hw },
+};
+
+/* AHB ROM */
+static CV1800_GATE(clk_ahb_rom, clk_axi4_bus_parents,
+ REG_CLK_EN_0, 6,
+ 0);
+
+/* RTC */
+static CV1800_GATE(clk_rtc_25m, osc_parents,
+ REG_CLK_EN_0, 8,
+ CLK_IS_CRITICAL);
+static CV1800_BYPASS_DIV(clk_src_rtc_sys_0, clk_bypass_fpll_parents,
+ REG_CLK_EN_4, 6,
+ REG_DIV_CLK_RTCSYS_SRC_0, 16, 4, 5, CV1800_DIV_FLAG,
+ REG_CLK_BYP_1, 5,
+ CLK_IS_CRITICAL);
+
+/* TEMPSEN */
+static CV1800_GATE(clk_tempsen, osc_parents,
+ REG_CLK_EN_0, 9,
+ 0);
+
+/* SARADC */
+static CV1800_GATE(clk_saradc, osc_parents,
+ REG_CLK_EN_0, 10,
+ 0);
+
+/* EFUSE */
+static CV1800_GATE(clk_efuse, osc_parents,
+ REG_CLK_EN_0, 11,
+ 0);
+static CV1800_GATE(clk_apb_efuse, osc_parents,
+ REG_CLK_EN_0, 12,
+ 0);
+
+/* WDT */
+static CV1800_GATE(clk_apb_wdt, osc_parents,
+ REG_CLK_EN_1, 7,
+ CLK_IS_CRITICAL);
+
+/* WGN */
+static CV1800_GATE(clk_wgn, osc_parents,
+ REG_CLK_EN_3, 22,
+ 0);
+static CV1800_GATE(clk_wgn0, osc_parents,
+ REG_CLK_EN_3, 23,
+ 0);
+static CV1800_GATE(clk_wgn1, osc_parents,
+ REG_CLK_EN_3, 24,
+ 0);
+static CV1800_GATE(clk_wgn2, osc_parents,
+ REG_CLK_EN_3, 25,
+ 0);
+
+/* KEYSCAN */
+static CV1800_GATE(clk_keyscan, osc_parents,
+ REG_CLK_EN_3, 26,
+ 0);
+
+/* EMMC */
+static CV1800_GATE(clk_axi4_emmc, clk_axi4_bus_parents,
+ REG_CLK_EN_0, 15,
+ 0);
+static CV1800_BYPASS_MUX(clk_emmc, clk_axi4_parents,
+ REG_CLK_EN_0, 16,
+ REG_DIV_CLK_EMMC, 16, 5, 15, CV1800_DIV_FLAG,
+ REG_DIV_CLK_EMMC, 8, 2,
+ REG_CLK_BYP_0, 5,
+ 0);
+static CV1800_DIV(clk_emmc_100k, clk_parents_1m,
+ REG_CLK_EN_0, 17,
+ REG_DIV_CLK_EMMC_100K, 16, 8, 10, CV1800_DIV_FLAG,
+ 0);
+
+/* SD */
+static CV1800_GATE(clk_axi4_sd0, clk_axi4_bus_parents,
+ REG_CLK_EN_0, 18,
+ 0);
+static CV1800_BYPASS_MUX(clk_sd0, clk_axi4_parents,
+ REG_CLK_EN_0, 19,
+ REG_DIV_CLK_SD0, 16, 5, 15, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SD0, 8, 2,
+ REG_CLK_BYP_0, 6,
+ 0);
+static CV1800_DIV(clk_sd0_100k, clk_parents_1m,
+ REG_CLK_EN_0, 20,
+ REG_DIV_CLK_SD0_100K, 16, 8, 10, CV1800_DIV_FLAG,
+ 0);
+static CV1800_GATE(clk_axi4_sd1, clk_axi4_bus_parents,
+ REG_CLK_EN_0, 21,
+ 0);
+static CV1800_BYPASS_MUX(clk_sd1, clk_axi4_parents,
+ REG_CLK_EN_0, 22,
+ REG_DIV_CLK_SD1, 16, 5, 15, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SD1, 8, 2,
+ REG_CLK_BYP_0, 7,
+ 0);
+static CV1800_DIV(clk_sd1_100k, clk_parents_1m,
+ REG_CLK_EN_0, 23,
+ REG_DIV_CLK_SD1_100K, 16, 8, 10, CV1800_DIV_FLAG,
+ 0);
+
+/* SPI NAND */
+static CV1800_BYPASS_MUX(clk_spi_nand, clk_axi4_parents,
+ REG_CLK_EN_0, 24,
+ REG_DIV_CLK_SPI_NAND, 16, 5, 8, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SPI_NAND, 8, 2,
+ REG_CLK_BYP_0, 8,
+ 0);
+
+/* GPIO */
+static CV1800_DIV(clk_gpio_db, clk_parents_1m,
+ REG_CLK_EN_0, 31,
+ REG_DIV_CLK_GPIO_DB, 16, 16, 10, CV1800_DIV_FLAG,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_apb_gpio, clk_axi6_bus_parents,
+ REG_CLK_EN_0, 29,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_apb_gpio_intr, clk_axi6_bus_parents,
+ REG_CLK_EN_0, 30,
+ CLK_IS_CRITICAL);
+
+/* ETH */
+static CV1800_BYPASS_DIV(clk_eth0_500m, clk_bypass_fpll_parents,
+ REG_CLK_EN_0, 25,
+ REG_DIV_CLK_GPIO_DB, 16, 4, 3, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 9,
+ 0);
+static CV1800_GATE(clk_axi4_eth0, clk_axi4_bus_parents,
+ REG_CLK_EN_0, 26,
+ 0);
+static CV1800_BYPASS_DIV(clk_eth1_500m, clk_bypass_fpll_parents,
+ REG_CLK_EN_0, 27,
+ REG_DIV_CLK_GPIO_DB, 16, 4, 3, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 10,
+ 0);
+static CV1800_GATE(clk_axi4_eth1, clk_axi4_bus_parents,
+ REG_CLK_EN_0, 28,
+ 0);
+
+/* SF */
+static CV1800_GATE(clk_ahb_sf, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 0,
+ 0);
+static CV1800_GATE(clk_ahb_sf1, clk_axi4_bus_parents,
+ REG_CLK_EN_3, 27,
+ 0);
+
+/* AUDSRC */
+static CV1800_ACLK(clk_a24m, clk_mipimpll_parents,
+ REG_APLL_FRAC_DIV_CTRL, 0,
+ REG_APLL_FRAC_DIV_CTRL, 3,
+ REG_APLL_FRAC_DIV_CTRL, 1,
+ REG_APLL_FRAC_DIV_CTRL, 2,
+ REG_APLL_FRAC_DIV_M, 0, 22, CV1800_DIV_FLAG,
+ REG_APLL_FRAC_DIV_N, 0, 22, CV1800_DIV_FLAG,
+ 24576000,
+ 0);
+
+static const struct clk_parent_data clk_aud_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_a0pll.common.hw },
+ { .hw = &clk_a24m.common.hw },
+};
+
+static CV1800_BYPASS_MUX(clk_audsrc, clk_aud_parents,
+ REG_CLK_EN_4, 1,
+ REG_DIV_CLK_AUDSRC, 16, 8, 18, CV1800_DIV_FLAG,
+ REG_DIV_CLK_AUDSRC, 8, 2,
+ REG_CLK_BYP_1, 2,
+ 0);
+static CV1800_GATE(clk_apb_audsrc, clk_axi4_bus_parents,
+ REG_CLK_EN_4, 2,
+ 0);
+
+/* SDMA */
+static CV1800_GATE(clk_sdma_axi, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 1,
+ 0);
+static CV1800_BYPASS_MUX(clk_sdma_aud0, clk_aud_parents,
+ REG_CLK_EN_1, 2,
+ REG_DIV_CLK_SDMA_AUD0, 16, 8, 18, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SDMA_AUD0, 8, 2,
+ REG_CLK_BYP_0, 11,
+ 0);
+static CV1800_BYPASS_MUX(clk_sdma_aud1, clk_aud_parents,
+ REG_CLK_EN_1, 3,
+ REG_DIV_CLK_SDMA_AUD1, 16, 8, 18, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SDMA_AUD1, 8, 2,
+ REG_CLK_BYP_0, 12,
+ 0);
+static CV1800_BYPASS_MUX(clk_sdma_aud2, clk_aud_parents,
+ REG_CLK_EN_1, 3,
+ REG_DIV_CLK_SDMA_AUD2, 16, 8, 18, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SDMA_AUD2, 8, 2,
+ REG_CLK_BYP_0, 13,
+ 0);
+static CV1800_BYPASS_MUX(clk_sdma_aud3, clk_aud_parents,
+ REG_CLK_EN_1, 3,
+ REG_DIV_CLK_SDMA_AUD3, 16, 8, 18, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SDMA_AUD3, 8, 2,
+ REG_CLK_BYP_0, 14,
+ 0);
+
+/* SPI */
+static CV1800_GATE(clk_apb_spi0, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 9,
+ 0);
+static CV1800_GATE(clk_apb_spi1, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 10,
+ 0);
+static CV1800_GATE(clk_apb_spi2, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 11,
+ 0);
+static CV1800_GATE(clk_apb_spi3, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 12,
+ 0);
+static CV1800_BYPASS_DIV(clk_spi, clk_bypass_fpll_parents,
+ REG_CLK_EN_3, 6,
+ REG_DIV_CLK_SPI, 16, 6, 8, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 30,
+ 0);
+
+/* UART */
+static CV1800_GATE(clk_uart0, clk_uart_parents,
+ REG_CLK_EN_1, 14,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_apb_uart0, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 15,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_uart1, clk_uart_parents,
+ REG_CLK_EN_1, 16,
+ 0);
+static CV1800_GATE(clk_apb_uart1, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 17,
+ 0);
+static CV1800_GATE(clk_uart2, clk_uart_parents,
+ REG_CLK_EN_1, 18,
+ 0);
+static CV1800_GATE(clk_apb_uart2, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 19,
+ 0);
+static CV1800_GATE(clk_uart3, clk_uart_parents,
+ REG_CLK_EN_1, 20,
+ 0);
+static CV1800_GATE(clk_apb_uart3, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 21,
+ 0);
+static CV1800_GATE(clk_uart4, clk_uart_parents,
+ REG_CLK_EN_1, 22,
+ 0);
+static CV1800_GATE(clk_apb_uart4, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 23,
+ 0);
+
+/* I2S */
+static CV1800_GATE(clk_apb_i2s0, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 24,
+ 0);
+static CV1800_GATE(clk_apb_i2s1, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 25,
+ 0);
+static CV1800_GATE(clk_apb_i2s2, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 26,
+ 0);
+static CV1800_GATE(clk_apb_i2s3, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 27,
+ 0);
+
+/* DEBUG */
+static CV1800_GATE(clk_debug, osc_parents,
+ REG_CLK_EN_0, 13,
+ CLK_IS_CRITICAL);
+static CV1800_BYPASS_DIV(clk_ap_debug, clk_bypass_fpll_parents,
+ REG_CLK_EN_4, 5,
+ REG_DIV_CLK_AP_DEBUG, 16, 4, 5, CV1800_DIV_FLAG,
+ REG_CLK_BYP_1, 4,
+ CLK_IS_CRITICAL);
+
+/* DDR */
+static CV1800_GATE(clk_ddr_axi_reg, clk_axi6_bus_parents,
+ REG_CLK_EN_0, 7,
+ CLK_IS_CRITICAL);
+
+/* I2C */
+static CV1800_GATE(clk_apb_i2c, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 6,
+ 0);
+static CV1800_BYPASS_DIV(clk_i2c, clk_bypass_axi6_bus_parents,
+ REG_CLK_EN_3, 7,
+ REG_DIV_CLK_I2C, 16, 4, 1, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 31,
+ 0);
+static CV1800_GATE(clk_apb_i2c0, clk_axi4_bus_parents,
+ REG_CLK_EN_3, 17,
+ 0);
+static CV1800_GATE(clk_apb_i2c1, clk_axi4_bus_parents,
+ REG_CLK_EN_3, 18,
+ 0);
+static CV1800_GATE(clk_apb_i2c2, clk_axi4_bus_parents,
+ REG_CLK_EN_3, 19,
+ 0);
+static CV1800_GATE(clk_apb_i2c3, clk_axi4_bus_parents,
+ REG_CLK_EN_3, 20,
+ 0);
+static CV1800_GATE(clk_apb_i2c4, clk_axi4_bus_parents,
+ REG_CLK_EN_3, 21,
+ 0);
+
+/* USB */
+static CV1800_GATE(clk_axi4_usb, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 28,
+ 0);
+static CV1800_GATE(clk_apb_usb, clk_axi4_bus_parents,
+ REG_CLK_EN_1, 29,
+ 0);
+static CV1800_BYPASS_FIXED_DIV(clk_usb_125m, clk_bypass_fpll_parents,
+ REG_CLK_EN_1, 30,
+ 12,
+ REG_CLK_BYP_0, 17,
+ CLK_SET_RATE_PARENT);
+static CV1800_FIXED_DIV(clk_usb_33k, clk_parents_1m,
+ REG_CLK_EN_1, 31,
+ 3,
+ 0);
+static CV1800_BYPASS_FIXED_DIV(clk_usb_12m, clk_bypass_fpll_parents,
+ REG_CLK_EN_2, 0,
+ 125,
+ REG_CLK_BYP_0, 18,
+ CLK_SET_RATE_PARENT);
+
+/* VIP SYS */
+static const struct clk_parent_data clk_vip_sys_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_mipimpll.common.hw },
+ { .hw = &clk_cam0pll.common.hw },
+ { .hw = &clk_disppll.common.hw },
+ { .hw = &clk_fpll.common.hw },
+};
+
+static CV1800_BYPASS_DIV(clk_dsi_esc, clk_bypass_axi6_bus_parents,
+ REG_CLK_EN_2, 3,
+ REG_DIV_CLK_DSI_ESC, 16, 4, 5, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 21,
+ 0);
+static CV1800_BYPASS_MUX(clk_axi_vip, clk_vip_sys_parents,
+ REG_CLK_EN_2, 4,
+ REG_DIV_CLK_AXI_VIP, 16, 4, 3, CV1800_DIV_FLAG,
+ REG_DIV_CLK_AXI_VIP, 8, 2,
+ REG_CLK_BYP_0, 22,
+ 0);
+
+static const struct clk_parent_data clk_axi_vip_bus_parents[] = {
+ { .hw = &clk_axi_vip.mux.common.hw },
+};
+
+static CV1800_BYPASS_MUX(clk_src_vip_sys_0, clk_vip_sys_parents,
+ REG_CLK_EN_2, 5,
+ REG_DIV_CLK_SRC_VIP_SYS_0, 16, 4, 6, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SRC_VIP_SYS_0, 8, 2,
+ REG_CLK_BYP_0, 23,
+ 0);
+static CV1800_BYPASS_MUX(clk_src_vip_sys_1, clk_vip_sys_parents,
+ REG_CLK_EN_2, 6,
+ REG_DIV_CLK_SRC_VIP_SYS_1, 16, 4, 6, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SRC_VIP_SYS_1, 8, 2,
+ REG_CLK_BYP_0, 24,
+ 0);
+static CV1800_BYPASS_MUX(clk_src_vip_sys_2, clk_vip_sys_parents,
+ REG_CLK_EN_3, 29,
+ REG_DIV_CLK_SRC_VIP_SYS_2, 16, 4, 2, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SRC_VIP_SYS_2, 8, 2,
+ REG_CLK_BYP_1, 1,
+ 0);
+static CV1800_GATE(clk_csi_mac0_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 18,
+ 0);
+static CV1800_GATE(clk_csi_mac1_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 19,
+ 0);
+static CV1800_GATE(clk_isp_top_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 20,
+ 0);
+static CV1800_GATE(clk_img_d_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 21,
+ 0);
+static CV1800_GATE(clk_img_v_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 22,
+ 0);
+static CV1800_GATE(clk_sc_top_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 23,
+ 0);
+static CV1800_GATE(clk_sc_d_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 24,
+ 0);
+static CV1800_GATE(clk_sc_v1_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 25,
+ 0);
+static CV1800_GATE(clk_sc_v2_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 26,
+ 0);
+static CV1800_GATE(clk_sc_v3_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 27,
+ 0);
+static CV1800_GATE(clk_dwa_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 28,
+ 0);
+static CV1800_GATE(clk_bt_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 29,
+ 0);
+static CV1800_GATE(clk_disp_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 30,
+ 0);
+static CV1800_GATE(clk_dsi_mac_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_2, 31,
+ 0);
+static CV1800_GATE(clk_lvds0_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_3, 0,
+ 0);
+static CV1800_GATE(clk_lvds1_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_3, 1,
+ 0);
+static CV1800_GATE(clk_csi0_rx_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_3, 2,
+ 0);
+static CV1800_GATE(clk_csi1_rx_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_3, 3,
+ 0);
+static CV1800_GATE(clk_pad_vi_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_3, 4,
+ 0);
+static CV1800_GATE(clk_pad_vi1_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_3, 30,
+ 0);
+static CV1800_GATE(clk_cfg_reg_vip, clk_axi6_bus_parents,
+ REG_CLK_EN_3, 31,
+ 0);
+static CV1800_GATE(clk_pad_vi2_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 7,
+ 0);
+static CV1800_GATE(clk_csi_be_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 8,
+ 0);
+static CV1800_GATE(clk_vip_ip0, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 9,
+ 0);
+static CV1800_GATE(clk_vip_ip1, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 10,
+ 0);
+static CV1800_GATE(clk_vip_ip2, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 11,
+ 0);
+static CV1800_GATE(clk_vip_ip3, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 12,
+ 0);
+static CV1800_BYPASS_MUX(clk_src_vip_sys_3, clk_vip_sys_parents,
+ REG_CLK_EN_4, 15,
+ REG_DIV_CLK_SRC_VIP_SYS_3, 16, 4, 2, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SRC_VIP_SYS_3, 8, 2,
+ REG_CLK_BYP_1, 8,
+ 0);
+static CV1800_BYPASS_MUX(clk_src_vip_sys_4, clk_vip_sys_parents,
+ REG_CLK_EN_4, 16,
+ REG_DIV_CLK_SRC_VIP_SYS_4, 16, 4, 3, CV1800_DIV_FLAG,
+ REG_DIV_CLK_SRC_VIP_SYS_4, 8, 2,
+ REG_CLK_BYP_1, 9,
+ 0);
+static CV1800_GATE(clk_ive_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 17,
+ 0);
+static CV1800_GATE(clk_raw_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 18,
+ 0);
+static CV1800_GATE(clk_osdc_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 19,
+ 0);
+static CV1800_GATE(clk_csi_mac2_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 20,
+ 0);
+static CV1800_GATE(clk_cam0_vip, clk_axi_vip_bus_parents,
+ REG_CLK_EN_4, 21,
+ 0);
+
+/* CAM OUT */
+static const struct clk_parent_data clk_cam_parents[] = {
+ { .hw = &clk_cam0pll.common.hw },
+ { .hw = &clk_cam0pll_d2.common.hw },
+ { .hw = &clk_cam0pll_d3.common.hw },
+ { .hw = &clk_mipimpll_d3.common.hw },
+};
+
+static CV1800_MUX(clk_cam0, clk_cam_parents,
+ REG_CLK_EN_2, 16,
+ REG_CLK_CAM0_SRC_DIV, 16, 6, 0, CV1800_DIV_FLAG,
+ REG_CLK_CAM0_SRC_DIV, 8, 2,
+ CLK_IGNORE_UNUSED);
+static CV1800_MUX(clk_cam1, clk_cam_parents,
+ REG_CLK_EN_2, 17,
+ REG_CLK_CAM1_SRC_DIV, 16, 6, 0, CV1800_DIV_FLAG,
+ REG_CLK_CAM1_SRC_DIV, 8, 2,
+ CLK_IGNORE_UNUSED);
+
+/* VIDEO SUBSYS */
+static const struct clk_parent_data clk_axi_video_codec_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_a0pll.common.hw },
+ { .hw = &clk_mipimpll.common.hw },
+ { .hw = &clk_cam1pll.common.hw },
+ { .hw = &clk_fpll.common.hw },
+};
+static const struct clk_parent_data clk_vc_src0_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_disppll.common.hw },
+ { .hw = &clk_mipimpll.common.hw },
+ { .hw = &clk_cam1pll.common.hw },
+ { .hw = &clk_fpll.common.hw },
+};
+static const struct clk_parent_data clk_vc_src1_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_cam1pll.common.hw },
+};
+
+static CV1800_BYPASS_MUX(clk_axi_video_codec, clk_axi_video_codec_parents,
+ REG_CLK_EN_2, 8,
+ REG_DIV_CLK_AXI_VIDEO_CODEC, 16, 4, 2, CV1800_DIV_FLAG,
+ REG_DIV_CLK_AXI_VIDEO_CODEC, 8, 2,
+ REG_CLK_BYP_0, 26,
+ 0);
+
+static const struct clk_parent_data clk_axi_video_codec_bus_parents[] = {
+ { .hw = &clk_axi_video_codec.mux.common.hw },
+};
+
+static CV1800_BYPASS_MUX(clk_vc_src0, clk_vc_src0_parents,
+ REG_CLK_EN_2, 9,
+ REG_DIV_CLK_VC_SRC0, 16, 4, 2, CV1800_DIV_FLAG,
+ REG_DIV_CLK_VC_SRC0, 8, 2,
+ REG_CLK_BYP_0, 27,
+ 0);
+
+static CV1800_GATE(clk_h264c, clk_axi_video_codec_bus_parents,
+ REG_CLK_EN_2, 10,
+ 0);
+static CV1800_GATE(clk_h265c, clk_axi_video_codec_bus_parents,
+ REG_CLK_EN_2, 11,
+ 0);
+static CV1800_GATE(clk_jpeg, clk_axi_video_codec_bus_parents,
+ REG_CLK_EN_2, 12,
+ CLK_IGNORE_UNUSED);
+static CV1800_GATE(clk_apb_jpeg, clk_axi6_bus_parents,
+ REG_CLK_EN_2, 13,
+ CLK_IGNORE_UNUSED);
+static CV1800_GATE(clk_apb_h264c, clk_axi6_bus_parents,
+ REG_CLK_EN_2, 14,
+ 0);
+static CV1800_GATE(clk_apb_h265c, clk_axi6_bus_parents,
+ REG_CLK_EN_2, 15,
+ 0);
+static CV1800_BYPASS_FIXED_DIV(clk_vc_src1, clk_vc_src1_parents,
+ REG_CLK_EN_3, 28,
+ 2,
+ REG_CLK_BYP_1, 0,
+ CLK_SET_RATE_PARENT);
+static CV1800_BYPASS_FIXED_DIV(clk_vc_src2, clk_bypass_fpll_parents,
+ REG_CLK_EN_4, 3,
+ 3,
+ REG_CLK_BYP_1, 3,
+ CLK_SET_RATE_PARENT);
+
+/* VC SYS */
+static CV1800_GATE(clk_cfg_reg_vc, clk_axi6_bus_parents,
+ REG_CLK_EN_4, 0,
+ CLK_IGNORE_UNUSED);
+
+/* PWM */
+static CV1800_BYPASS_MUX(clk_pwm_src, clk_axi4_parents,
+ REG_CLK_EN_4, 4,
+ REG_DIV_CLK_PWM_SRC_0, 16, 6, 10, CV1800_DIV_FLAG,
+ REG_DIV_CLK_PWM_SRC_0, 8, 2,
+ REG_CLK_BYP_0, 15,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_pwm_parents[] = {
+ { .hw = &clk_pwm_src.mux.common.hw },
+};
+
+static CV1800_GATE(clk_pwm, clk_pwm_parents,
+ REG_CLK_EN_1, 8,
+ CLK_IS_CRITICAL);
+
+/* C906 */
+static const struct clk_parent_data clk_c906_0_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_tpll.common.hw },
+ { .hw = &clk_a0pll.common.hw },
+ { .hw = &clk_mipimpll.common.hw },
+ { .hw = &clk_mpll.common.hw },
+ { .hw = &clk_fpll.common.hw },
+};
+static const struct clk_parent_data clk_c906_1_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_tpll.common.hw },
+ { .hw = &clk_a0pll.common.hw },
+ { .hw = &clk_disppll.common.hw },
+ { .hw = &clk_mpll.common.hw },
+ { .hw = &clk_fpll.common.hw },
+};
+
+static const s8 clk_c906_parent2sel[] = {
+ -1, /* osc */
+ 0, /* mux 0: clk_tpll(c906_0), clk_tpll(c906_1) */
+ 0, /* mux 0: clk_a0pll(c906_0), clk_a0pll(c906_1) */
+ 0, /* mux 0: clk_mipimpll(c906_0), clk_disppll(c906_1) */
+ 0, /* mux 0: clk_mpll(c906_0), clk_mpll(c906_1) */
+ 1 /* mux 1: clk_fpll(c906_0), clk_fpll(c906_1) */
+};
+
+static const u8 clk_c906_sel2parent[2][4] = {
+ [0] = {
+ 1,
+ 2,
+ 3,
+ 4
+ },
+ [1] = {
+ 5,
+ 5,
+ 5,
+ 5
+ },
+};
+
+static CV1800_MMUX(clk_c906_0, clk_c906_0_parents,
+ REG_CLK_EN_4, 13,
+ REG_DIV_CLK_C906_0_0, 16, 4, 1, CV1800_DIV_FLAG,
+ REG_DIV_CLK_C906_0_1, 16, 4, 2, CV1800_DIV_FLAG,
+ REG_DIV_CLK_C906_0_0, 8, 2,
+ REG_DIV_CLK_C906_0_1, 8, 2,
+ REG_CLK_BYP_1, 6,
+ REG_CLK_SEL_0, 23,
+ clk_c906_parent2sel,
+ clk_c906_sel2parent[0], clk_c906_sel2parent[1],
+ CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE);
+static CV1800_MMUX(clk_c906_1, clk_c906_1_parents,
+ REG_CLK_EN_4, 14,
+ REG_DIV_CLK_C906_1_0, 16, 4, 2, CV1800_DIV_FLAG,
+ REG_DIV_CLK_C906_1_1, 16, 4, 3, CV1800_DIV_FLAG,
+ REG_DIV_CLK_C906_1_0, 8, 2,
+ REG_DIV_CLK_C906_1_1, 8, 2,
+ REG_CLK_BYP_1, 7,
+ REG_CLK_SEL_0, 24,
+ clk_c906_parent2sel,
+ clk_c906_sel2parent[0], clk_c906_sel2parent[1],
+ CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE);
+
+/* A53 */
+static CV1800_BYPASS_DIV(clk_cpu_axi0, clk_axi4_parents,
+ REG_CLK_EN_0, 1,
+ REG_DIV_CLK_CPU_AXI0, 16, 4, 3, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 1,
+ CLK_IS_CRITICAL);
+static CV1800_BYPASS_DIV(clk_cpu_gic, clk_bypass_fpll_parents,
+ REG_CLK_EN_0, 2,
+ REG_DIV_CLK_CPU_GIC, 16, 4, 5, CV1800_DIV_FLAG,
+ REG_CLK_BYP_0, 2,
+ CLK_IS_CRITICAL);
+static CV1800_GATE(clk_xtal_ap, osc_parents,
+ REG_CLK_EN_0, 3,
+ CLK_IS_CRITICAL);
+
+static const struct clk_parent_data clk_a53_parents[] = {
+ { .index = 0 },
+ { .hw = &clk_tpll.common.hw },
+ { .hw = &clk_a0pll.common.hw },
+ { .hw = &clk_mipimpll.common.hw },
+ { .hw = &clk_mpll.common.hw },
+ { .hw = &clk_fpll.common.hw },
+};
+
+static const s8 clk_a53_parent2sel[] = {
+ -1, /* osc */
+ 0, /* mux 0: clk_tpll */
+ 0, /* mux 0: clk_a0pll */
+ 0, /* mux 0: clk_mipimpll */
+ 0, /* mux 0: clk_mpll */
+ 1 /* mux 1: clk_fpll */
+};
+
+static const u8 clk_a53_sel2parent[2][4] = {
+ [0] = {
+ 1,
+ 2,
+ 3,
+ 4
+ },
+ [1] = {
+ 5,
+ 5,
+ 5,
+ 5
+ },
+};
+
+/*
+ * Clock for A53 cpu in the CV18XX/SG200X series.
+ * For CV180X and CV181X series, this clock is not used, but can not
+ * be set to bypass mode, or the SoC will hang.
+ */
+static CV1800_MMUX(clk_a53, clk_a53_parents,
+ REG_CLK_EN_0, 0,
+ REG_DIV_CLK_A53_0, 16, 4, 1, CV1800_DIV_FLAG,
+ REG_DIV_CLK_A53_1, 16, 4, 2, CV1800_DIV_FLAG,
+ REG_DIV_CLK_A53_0, 8, 2,
+ REG_DIV_CLK_A53_1, 8, 2,
+ REG_CLK_BYP_0, 0,
+ REG_CLK_SEL_0, 0,
+ clk_a53_parent2sel,
+ clk_a53_sel2parent[0], clk_a53_sel2parent[1],
+ CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE);
+
+static struct clk_hw_onecell_data cv1800_hw_clks = {
+ .num = CV1800_CLK_MAX,
+ .hws = {
+ [CLK_MPLL] = &clk_mpll.common.hw,
+ [CLK_TPLL] = &clk_tpll.common.hw,
+ [CLK_FPLL] = &clk_fpll.common.hw,
+ [CLK_MIPIMPLL] = &clk_mipimpll.common.hw,
+ [CLK_A0PLL] = &clk_a0pll.common.hw,
+ [CLK_DISPPLL] = &clk_disppll.common.hw,
+ [CLK_CAM0PLL] = &clk_cam0pll.common.hw,
+ [CLK_CAM1PLL] = &clk_cam1pll.common.hw,
+
+ [CLK_MIPIMPLL_D3] = &clk_mipimpll_d3.common.hw,
+ [CLK_CAM0PLL_D2] = &clk_cam0pll_d2.common.hw,
+ [CLK_CAM0PLL_D3] = &clk_cam0pll_d3.common.hw,
+
+ [CLK_TPU] = &clk_tpu.mux.common.hw,
+ [CLK_TPU_FAB] = &clk_tpu_fab.common.hw,
+ [CLK_AHB_ROM] = &clk_ahb_rom.common.hw,
+ [CLK_DDR_AXI_REG] = &clk_ddr_axi_reg.common.hw,
+ [CLK_RTC_25M] = &clk_rtc_25m.common.hw,
+ [CLK_SRC_RTC_SYS_0] = &clk_src_rtc_sys_0.div.common.hw,
+ [CLK_TEMPSEN] = &clk_tempsen.common.hw,
+ [CLK_SARADC] = &clk_saradc.common.hw,
+ [CLK_EFUSE] = &clk_efuse.common.hw,
+ [CLK_APB_EFUSE] = &clk_apb_efuse.common.hw,
+ [CLK_DEBUG] = &clk_debug.common.hw,
+ [CLK_AP_DEBUG] = &clk_ap_debug.div.common.hw,
+ [CLK_XTAL_MISC] = &clk_xtal_misc.common.hw,
+ [CLK_AXI4_EMMC] = &clk_axi4_emmc.common.hw,
+ [CLK_EMMC] = &clk_emmc.mux.common.hw,
+ [CLK_EMMC_100K] = &clk_emmc_100k.common.hw,
+ [CLK_AXI4_SD0] = &clk_axi4_sd0.common.hw,
+ [CLK_SD0] = &clk_sd0.mux.common.hw,
+ [CLK_SD0_100K] = &clk_sd0_100k.common.hw,
+ [CLK_AXI4_SD1] = &clk_axi4_sd1.common.hw,
+ [CLK_SD1] = &clk_sd1.mux.common.hw,
+ [CLK_SD1_100K] = &clk_sd1_100k.common.hw,
+ [CLK_SPI_NAND] = &clk_spi_nand.mux.common.hw,
+ [CLK_ETH0_500M] = &clk_eth0_500m.div.common.hw,
+ [CLK_AXI4_ETH0] = &clk_axi4_eth0.common.hw,
+ [CLK_ETH1_500M] = &clk_eth1_500m.div.common.hw,
+ [CLK_AXI4_ETH1] = &clk_axi4_eth1.common.hw,
+ [CLK_APB_GPIO] = &clk_apb_gpio.common.hw,
+ [CLK_APB_GPIO_INTR] = &clk_apb_gpio_intr.common.hw,
+ [CLK_GPIO_DB] = &clk_gpio_db.common.hw,
+ [CLK_AHB_SF] = &clk_ahb_sf.common.hw,
+ [CLK_AHB_SF1] = &clk_ahb_sf1.common.hw,
+ [CLK_A24M] = &clk_a24m.common.hw,
+ [CLK_AUDSRC] = &clk_audsrc.mux.common.hw,
+ [CLK_APB_AUDSRC] = &clk_apb_audsrc.common.hw,
+ [CLK_SDMA_AXI] = &clk_sdma_axi.common.hw,
+ [CLK_SDMA_AUD0] = &clk_sdma_aud0.mux.common.hw,
+ [CLK_SDMA_AUD1] = &clk_sdma_aud1.mux.common.hw,
+ [CLK_SDMA_AUD2] = &clk_sdma_aud2.mux.common.hw,
+ [CLK_SDMA_AUD3] = &clk_sdma_aud3.mux.common.hw,
+ [CLK_I2C] = &clk_i2c.div.common.hw,
+ [CLK_APB_I2C] = &clk_apb_i2c.common.hw,
+ [CLK_APB_I2C0] = &clk_apb_i2c0.common.hw,
+ [CLK_APB_I2C1] = &clk_apb_i2c1.common.hw,
+ [CLK_APB_I2C2] = &clk_apb_i2c2.common.hw,
+ [CLK_APB_I2C3] = &clk_apb_i2c3.common.hw,
+ [CLK_APB_I2C4] = &clk_apb_i2c4.common.hw,
+ [CLK_APB_WDT] = &clk_apb_wdt.common.hw,
+ [CLK_PWM_SRC] = &clk_pwm_src.mux.common.hw,
+ [CLK_PWM] = &clk_pwm.common.hw,
+ [CLK_SPI] = &clk_spi.div.common.hw,
+ [CLK_APB_SPI0] = &clk_apb_spi0.common.hw,
+ [CLK_APB_SPI1] = &clk_apb_spi1.common.hw,
+ [CLK_APB_SPI2] = &clk_apb_spi2.common.hw,
+ [CLK_APB_SPI3] = &clk_apb_spi3.common.hw,
+ [CLK_1M] = &clk_1m.common.hw,
+ [CLK_CAM0_200] = &clk_cam0_200.mux.common.hw,
+ [CLK_PM] = &clk_pm.common.hw,
+ [CLK_TIMER0] = &clk_timer0.common.hw,
+ [CLK_TIMER1] = &clk_timer1.common.hw,
+ [CLK_TIMER2] = &clk_timer2.common.hw,
+ [CLK_TIMER3] = &clk_timer3.common.hw,
+ [CLK_TIMER4] = &clk_timer4.common.hw,
+ [CLK_TIMER5] = &clk_timer5.common.hw,
+ [CLK_TIMER6] = &clk_timer6.common.hw,
+ [CLK_TIMER7] = &clk_timer7.common.hw,
+ [CLK_UART0] = &clk_uart0.common.hw,
+ [CLK_APB_UART0] = &clk_apb_uart0.common.hw,
+ [CLK_UART1] = &clk_uart1.common.hw,
+ [CLK_APB_UART1] = &clk_apb_uart1.common.hw,
+ [CLK_UART2] = &clk_uart2.common.hw,
+ [CLK_APB_UART2] = &clk_apb_uart2.common.hw,
+ [CLK_UART3] = &clk_uart3.common.hw,
+ [CLK_APB_UART3] = &clk_apb_uart3.common.hw,
+ [CLK_UART4] = &clk_uart4.common.hw,
+ [CLK_APB_UART4] = &clk_apb_uart4.common.hw,
+ [CLK_APB_I2S0] = &clk_apb_i2s0.common.hw,
+ [CLK_APB_I2S1] = &clk_apb_i2s1.common.hw,
+ [CLK_APB_I2S2] = &clk_apb_i2s2.common.hw,
+ [CLK_APB_I2S3] = &clk_apb_i2s3.common.hw,
+ [CLK_AXI4_USB] = &clk_axi4_usb.common.hw,
+ [CLK_APB_USB] = &clk_apb_usb.common.hw,
+ [CLK_USB_125M] = &clk_usb_125m.div.common.hw,
+ [CLK_USB_33K] = &clk_usb_33k.common.hw,
+ [CLK_USB_12M] = &clk_usb_12m.div.common.hw,
+ [CLK_AXI4] = &clk_axi4.mux.common.hw,
+ [CLK_AXI6] = &clk_axi6.div.common.hw,
+ [CLK_DSI_ESC] = &clk_dsi_esc.div.common.hw,
+ [CLK_AXI_VIP] = &clk_axi_vip.mux.common.hw,
+ [CLK_SRC_VIP_SYS_0] = &clk_src_vip_sys_0.mux.common.hw,
+ [CLK_SRC_VIP_SYS_1] = &clk_src_vip_sys_1.mux.common.hw,
+ [CLK_SRC_VIP_SYS_2] = &clk_src_vip_sys_2.mux.common.hw,
+ [CLK_SRC_VIP_SYS_3] = &clk_src_vip_sys_3.mux.common.hw,
+ [CLK_SRC_VIP_SYS_4] = &clk_src_vip_sys_4.mux.common.hw,
+ [CLK_CSI_BE_VIP] = &clk_csi_be_vip.common.hw,
+ [CLK_CSI_MAC0_VIP] = &clk_csi_mac0_vip.common.hw,
+ [CLK_CSI_MAC1_VIP] = &clk_csi_mac1_vip.common.hw,
+ [CLK_CSI_MAC2_VIP] = &clk_csi_mac2_vip.common.hw,
+ [CLK_CSI0_RX_VIP] = &clk_csi0_rx_vip.common.hw,
+ [CLK_CSI1_RX_VIP] = &clk_csi1_rx_vip.common.hw,
+ [CLK_ISP_TOP_VIP] = &clk_isp_top_vip.common.hw,
+ [CLK_IMG_D_VIP] = &clk_img_d_vip.common.hw,
+ [CLK_IMG_V_VIP] = &clk_img_v_vip.common.hw,
+ [CLK_SC_TOP_VIP] = &clk_sc_top_vip.common.hw,
+ [CLK_SC_D_VIP] = &clk_sc_d_vip.common.hw,
+ [CLK_SC_V1_VIP] = &clk_sc_v1_vip.common.hw,
+ [CLK_SC_V2_VIP] = &clk_sc_v2_vip.common.hw,
+ [CLK_SC_V3_VIP] = &clk_sc_v3_vip.common.hw,
+ [CLK_DWA_VIP] = &clk_dwa_vip.common.hw,
+ [CLK_BT_VIP] = &clk_bt_vip.common.hw,
+ [CLK_DISP_VIP] = &clk_disp_vip.common.hw,
+ [CLK_DSI_MAC_VIP] = &clk_dsi_mac_vip.common.hw,
+ [CLK_LVDS0_VIP] = &clk_lvds0_vip.common.hw,
+ [CLK_LVDS1_VIP] = &clk_lvds1_vip.common.hw,
+ [CLK_PAD_VI_VIP] = &clk_pad_vi_vip.common.hw,
+ [CLK_PAD_VI1_VIP] = &clk_pad_vi1_vip.common.hw,
+ [CLK_PAD_VI2_VIP] = &clk_pad_vi2_vip.common.hw,
+ [CLK_CFG_REG_VIP] = &clk_cfg_reg_vip.common.hw,
+ [CLK_VIP_IP0] = &clk_vip_ip0.common.hw,
+ [CLK_VIP_IP1] = &clk_vip_ip1.common.hw,
+ [CLK_VIP_IP2] = &clk_vip_ip2.common.hw,
+ [CLK_VIP_IP3] = &clk_vip_ip3.common.hw,
+ [CLK_IVE_VIP] = &clk_ive_vip.common.hw,
+ [CLK_RAW_VIP] = &clk_raw_vip.common.hw,
+ [CLK_OSDC_VIP] = &clk_osdc_vip.common.hw,
+ [CLK_CAM0_VIP] = &clk_cam0_vip.common.hw,
+ [CLK_AXI_VIDEO_CODEC] = &clk_axi_video_codec.mux.common.hw,
+ [CLK_VC_SRC0] = &clk_vc_src0.mux.common.hw,
+ [CLK_VC_SRC1] = &clk_vc_src1.div.common.hw,
+ [CLK_VC_SRC2] = &clk_vc_src2.div.common.hw,
+ [CLK_H264C] = &clk_h264c.common.hw,
+ [CLK_APB_H264C] = &clk_apb_h264c.common.hw,
+ [CLK_H265C] = &clk_h265c.common.hw,
+ [CLK_APB_H265C] = &clk_apb_h265c.common.hw,
+ [CLK_JPEG] = &clk_jpeg.common.hw,
+ [CLK_APB_JPEG] = &clk_apb_jpeg.common.hw,
+ [CLK_CAM0] = &clk_cam0.common.hw,
+ [CLK_CAM1] = &clk_cam1.common.hw,
+ [CLK_WGN] = &clk_wgn.common.hw,
+ [CLK_WGN0] = &clk_wgn0.common.hw,
+ [CLK_WGN1] = &clk_wgn1.common.hw,
+ [CLK_WGN2] = &clk_wgn2.common.hw,
+ [CLK_KEYSCAN] = &clk_keyscan.common.hw,
+ [CLK_CFG_REG_VC] = &clk_cfg_reg_vc.common.hw,
+ [CLK_C906_0] = &clk_c906_0.common.hw,
+ [CLK_C906_1] = &clk_c906_1.common.hw,
+ [CLK_A53] = &clk_a53.common.hw,
+ [CLK_CPU_AXI0] = &clk_cpu_axi0.div.common.hw,
+ [CLK_CPU_GIC] = &clk_cpu_gic.div.common.hw,
+ [CLK_XTAL_AP] = &clk_xtal_ap.common.hw,
+ },
+};
+
+static void cv18xx_clk_disable_auto_pd(void __iomem *base)
+{
+ static const u16 CV1800_PD_CLK[] = {
+ REG_MIPIMPLL_CLK_CSR,
+ REG_A0PLL_CLK_CSR,
+ REG_DISPPLL_CLK_CSR,
+ REG_CAM0PLL_CLK_CSR,
+ REG_CAM1PLL_CLK_CSR,
+ };
+
+ u32 val;
+ int i;
+
+ /* disable auto power down */
+ for (i = 0; i < ARRAY_SIZE(CV1800_PD_CLK); i++) {
+ u32 reg = CV1800_PD_CLK[i];
+
+ val = readl(base + reg);
+ val |= GENMASK(12, 9);
+ val &= ~BIT(8);
+ writel(val, base + reg);
+ }
+}
+
+static void cv18xx_clk_disable_a53(void __iomem *base)
+{
+ u32 val = readl(base + REG_CLK_BYP_0);
+
+ /* Set bypass clock for clk_a53 */
+ val |= BIT(0);
+
+ /* Set bypass clock for clk_cpu_axi0 */
+ val |= BIT(1);
+
+ /* Set bypass clock for clk_cpu_gic */
+ val |= BIT(2);
+
+ writel(val, base + REG_CLK_BYP_0);
+}
+
+static int cv1800_pre_init(struct device *dev, void __iomem *base,
+ struct cv1800_clk_ctrl *ctrl,
+ const struct cv1800_clk_desc *desc)
+{
+ u32 val = readl(base + REG_CLK_EN_2);
+
+ /* disable unsupported clk_disp_src_vip */
+ val &= ~BIT(7);
+
+ writel(val, base + REG_CLK_EN_2);
+
+ cv18xx_clk_disable_a53(base);
+ cv18xx_clk_disable_auto_pd(base);
+
+ return 0;
+}
+
+static const struct cv1800_clk_desc cv1800_desc = {
+ .clks_data = &cv1800_hw_clks,
+ .pre_init = cv1800_pre_init,
+};
+
+static int cv1800_clk_init_ctrl(struct device *dev, void __iomem *reg,
+ struct cv1800_clk_ctrl *ctrl,
+ const struct cv1800_clk_desc *desc)
+{
+ int i, ret;
+
+ ctrl->desc = desc;
+ spin_lock_init(&ctrl->lock);
+
+ for (i = 0; i < desc->clks_data->num; i++) {
+ struct clk_hw *hw = desc->clks_data->hws[i];
+ struct cv1800_clk_common *common;
+ const char *name;
+
+ if (!hw)
+ continue;
+
+ name = hw->init->name;
+
+ common = hw_to_cv1800_clk_common(hw);
+ common->base = reg;
+ common->lock = &ctrl->lock;
+
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret) {
+ dev_err(dev, "Couldn't register clock %d - %s\n",
+ i, name);
+ return ret;
+ }
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ desc->clks_data);
+}
+
+static int cv1800_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *reg;
+ int ret;
+ const struct cv1800_clk_desc *desc;
+ struct cv1800_clk_ctrl *ctrl;
+
+ reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ desc = device_get_match_data(dev);
+ if (!desc) {
+ dev_err(dev, "no match data for platform\n");
+ return -EINVAL;
+ }
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ if (desc->pre_init) {
+ ret = desc->pre_init(dev, reg, ctrl, desc);
+ if (ret)
+ return ret;
+ }
+
+ return cv1800_clk_init_ctrl(dev, reg, ctrl, desc);
+}
+
+static const struct of_device_id cv1800_clk_ids[] = {
+ { .compatible = "sophgo,cv1800-clk", .data = &cv1800_desc },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cv1800_clk_ids);
+
+static struct platform_driver cv1800_clk_driver = {
+ .probe = cv1800_clk_probe,
+ .driver = {
+ .name = "cv1800-clk",
+ .suppress_bind_attrs = true,
+ .of_match_table = cv1800_clk_ids,
+ },
+};
+module_platform_driver(cv1800_clk_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/sophgo/clk-cv1800.h b/drivers/clk/sophgo/clk-cv1800.h
new file mode 100644
index 000000000000..1b9c04b5ac3a
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv1800.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#ifndef _CLK_SOPHGO_CV1800_H_
+#define _CLK_SOPHGO_CV1800_H_
+
+#include <dt-bindings/clock/sophgo,cv1800.h>
+
+#define CV1800_CLK_MAX (CLK_XTAL_AP + 1)
+
+#define REG_PLL_G2_CTRL 0x800
+#define REG_PLL_G2_STATUS 0x804
+#define REG_MIPIMPLL_CSR 0x808
+#define REG_A0PLL_CSR 0x80C
+#define REG_DISPPLL_CSR 0x810
+#define REG_CAM0PLL_CSR 0x814
+#define REG_CAM1PLL_CSR 0x818
+#define REG_PLL_G2_SSC_SYN_CTRL 0x840
+#define REG_A0PLL_SSC_SYN_CTRL 0x850
+#define REG_A0PLL_SSC_SYN_SET 0x854
+#define REG_A0PLL_SSC_SYN_SPAN 0x858
+#define REG_A0PLL_SSC_SYN_STEP 0x85C
+#define REG_DISPPLL_SSC_SYN_CTRL 0x860
+#define REG_DISPPLL_SSC_SYN_SET 0x864
+#define REG_DISPPLL_SSC_SYN_SPAN 0x868
+#define REG_DISPPLL_SSC_SYN_STEP 0x86C
+#define REG_CAM0PLL_SSC_SYN_CTRL 0x870
+#define REG_CAM0PLL_SSC_SYN_SET 0x874
+#define REG_CAM0PLL_SSC_SYN_SPAN 0x878
+#define REG_CAM0PLL_SSC_SYN_STEP 0x87C
+#define REG_CAM1PLL_SSC_SYN_CTRL 0x880
+#define REG_CAM1PLL_SSC_SYN_SET 0x884
+#define REG_CAM1PLL_SSC_SYN_SPAN 0x888
+#define REG_CAM1PLL_SSC_SYN_STEP 0x88C
+#define REG_APLL_FRAC_DIV_CTRL 0x890
+#define REG_APLL_FRAC_DIV_M 0x894
+#define REG_APLL_FRAC_DIV_N 0x898
+#define REG_MIPIMPLL_CLK_CSR 0x8A0
+#define REG_A0PLL_CLK_CSR 0x8A4
+#define REG_DISPPLL_CLK_CSR 0x8A8
+#define REG_CAM0PLL_CLK_CSR 0x8AC
+#define REG_CAM1PLL_CLK_CSR 0x8B0
+#define REG_CLK_CAM0_SRC_DIV 0x8C0
+#define REG_CLK_CAM1_SRC_DIV 0x8C4
+
+/* top_pll_g6 */
+#define REG_PLL_G6_CTRL 0x900
+#define REG_PLL_G6_STATUS 0x904
+#define REG_MPLL_CSR 0x908
+#define REG_TPLL_CSR 0x90C
+#define REG_FPLL_CSR 0x910
+#define REG_PLL_G6_SSC_SYN_CTRL 0x940
+#define REG_DPLL_SSC_SYN_CTRL 0x950
+#define REG_DPLL_SSC_SYN_SET 0x954
+#define REG_DPLL_SSC_SYN_SPAN 0x958
+#define REG_DPLL_SSC_SYN_STEP 0x95C
+#define REG_MPLL_SSC_SYN_CTRL 0x960
+#define REG_MPLL_SSC_SYN_SET 0x964
+#define REG_MPLL_SSC_SYN_SPAN 0x968
+#define REG_MPLL_SSC_SYN_STEP 0x96C
+#define REG_TPLL_SSC_SYN_CTRL 0x970
+#define REG_TPLL_SSC_SYN_SET 0x974
+#define REG_TPLL_SSC_SYN_SPAN 0x978
+#define REG_TPLL_SSC_SYN_STEP 0x97C
+
+/* clkgen */
+#define REG_CLK_EN_0 0x000
+#define REG_CLK_EN_1 0x004
+#define REG_CLK_EN_2 0x008
+#define REG_CLK_EN_3 0x00C
+#define REG_CLK_EN_4 0x010
+#define REG_CLK_SEL_0 0x020
+#define REG_CLK_BYP_0 0x030
+#define REG_CLK_BYP_1 0x034
+
+#define REG_DIV_CLK_A53_0 0x040
+#define REG_DIV_CLK_A53_1 0x044
+#define REG_DIV_CLK_CPU_AXI0 0x048
+#define REG_DIV_CLK_CPU_GIC 0x050
+#define REG_DIV_CLK_TPU 0x054
+#define REG_DIV_CLK_EMMC 0x064
+#define REG_DIV_CLK_EMMC_100K 0x06C
+#define REG_DIV_CLK_SD0 0x070
+#define REG_DIV_CLK_SD0_100K 0x078
+#define REG_DIV_CLK_SD1 0x07C
+#define REG_DIV_CLK_SD1_100K 0x084
+#define REG_DIV_CLK_SPI_NAND 0x088
+#define REG_DIV_CLK_ETH0_500M 0x08C
+#define REG_DIV_CLK_ETH1_500M 0x090
+#define REG_DIV_CLK_GPIO_DB 0x094
+#define REG_DIV_CLK_SDMA_AUD0 0x098
+#define REG_DIV_CLK_SDMA_AUD1 0x09C
+#define REG_DIV_CLK_SDMA_AUD2 0x0A0
+#define REG_DIV_CLK_SDMA_AUD3 0x0A4
+#define REG_DIV_CLK_CAM0_200 0x0A8
+#define REG_DIV_CLK_AXI4 0x0B8
+#define REG_DIV_CLK_AXI6 0x0BC
+#define REG_DIV_CLK_DSI_ESC 0x0C4
+#define REG_DIV_CLK_AXI_VIP 0x0C8
+#define REG_DIV_CLK_SRC_VIP_SYS_0 0x0D0
+#define REG_DIV_CLK_SRC_VIP_SYS_1 0x0D8
+#define REG_DIV_CLK_DISP_SRC_VIP 0x0E0
+#define REG_DIV_CLK_AXI_VIDEO_CODEC 0x0E4
+#define REG_DIV_CLK_VC_SRC0 0x0EC
+#define REG_DIV_CLK_1M 0x0FC
+#define REG_DIV_CLK_SPI 0x100
+#define REG_DIV_CLK_I2C 0x104
+#define REG_DIV_CLK_SRC_VIP_SYS_2 0x110
+#define REG_DIV_CLK_AUDSRC 0x118
+#define REG_DIV_CLK_PWM_SRC_0 0x120
+#define REG_DIV_CLK_AP_DEBUG 0x128
+#define REG_DIV_CLK_RTCSYS_SRC_0 0x12C
+#define REG_DIV_CLK_C906_0_0 0x130
+#define REG_DIV_CLK_C906_0_1 0x134
+#define REG_DIV_CLK_C906_1_0 0x138
+#define REG_DIV_CLK_C906_1_1 0x13C
+#define REG_DIV_CLK_SRC_VIP_SYS_3 0x140
+#define REG_DIV_CLK_SRC_VIP_SYS_4 0x144
+
+#endif /* _CLK_SOPHGO_CV1800_H_ */
diff --git a/drivers/clk/sophgo/clk-cv18xx-common.c b/drivers/clk/sophgo/clk-cv18xx-common.c
new file mode 100644
index 000000000000..cbcdd88f0e23
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv18xx-common.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/spinlock.h>
+#include <linux/bug.h>
+
+#include "clk-cv18xx-common.h"
+
+int cv1800_clk_setbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field)
+{
+ u32 mask = BIT(field->shift);
+ u32 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(common->lock, flags);
+
+ value = readl(common->base + field->reg);
+ writel(value | mask, common->base + field->reg);
+
+ spin_unlock_irqrestore(common->lock, flags);
+
+ return 0;
+}
+
+int cv1800_clk_clearbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field)
+{
+ u32 mask = BIT(field->shift);
+ u32 value;
+ unsigned long flags;
+
+ spin_lock_irqsave(common->lock, flags);
+
+ value = readl(common->base + field->reg);
+ writel(value & ~mask, common->base + field->reg);
+
+ spin_unlock_irqrestore(common->lock, flags);
+
+ return 0;
+}
+
+int cv1800_clk_checkbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field)
+{
+ return readl(common->base + field->reg) & BIT(field->shift);
+}
+
+#define PLL_LOCK_TIMEOUT_US (200 * 1000)
+
+void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
+ u32 reg, u32 lock)
+{
+ void __iomem *addr = common->base + reg;
+ u32 regval;
+
+ if (!lock)
+ return;
+
+ WARN_ON(readl_relaxed_poll_timeout(addr, regval, regval & lock,
+ 100, PLL_LOCK_TIMEOUT_US));
+}
diff --git a/drivers/clk/sophgo/clk-cv18xx-common.h b/drivers/clk/sophgo/clk-cv18xx-common.h
new file mode 100644
index 000000000000..2bfda02b2064
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv18xx-common.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#ifndef _CLK_SOPHGO_CV18XX_IP_H_
+#define _CLK_SOPHGO_CV18XX_IP_H_
+
+#include <linux/compiler.h>
+#include <linux/clk-provider.h>
+#include <linux/bitfield.h>
+
+struct cv1800_clk_common {
+ void __iomem *base;
+ spinlock_t *lock;
+ struct clk_hw hw;
+ unsigned long features;
+};
+
+#define CV1800_CLK_COMMON(_name, _parents, _op, _flags) \
+ { \
+ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, \
+ _op, _flags), \
+ }
+
+static inline struct cv1800_clk_common *
+hw_to_cv1800_clk_common(struct clk_hw *hw)
+{
+ return container_of(hw, struct cv1800_clk_common, hw);
+}
+
+struct cv1800_clk_regbit {
+ u16 reg;
+ s8 shift;
+};
+
+struct cv1800_clk_regfield {
+ u16 reg;
+ u8 shift;
+ u8 width;
+ s16 initval;
+ unsigned long flags;
+};
+
+#define CV1800_CLK_BIT(_reg, _shift) \
+ { \
+ .reg = _reg, \
+ .shift = _shift, \
+ }
+
+#define CV1800_CLK_REG(_reg, _shift, _width, _initval, _flags) \
+ { \
+ .reg = _reg, \
+ .shift = _shift, \
+ .width = _width, \
+ .initval = _initval, \
+ .flags = _flags, \
+ }
+
+#define cv1800_clk_regfield_genmask(_reg) \
+ GENMASK((_reg)->shift + (_reg)->width - 1, (_reg)->shift)
+#define cv1800_clk_regfield_get(_val, _reg) \
+ (((_val) >> (_reg)->shift) & GENMASK((_reg)->width - 1, 0))
+#define cv1800_clk_regfield_set(_val, _new, _reg) \
+ (((_val) & ~cv1800_clk_regfield_genmask((_reg))) | \
+ (((_new) & GENMASK((_reg)->width - 1, 0)) << (_reg)->shift))
+
+#define _CV1800_SET_FIELD(_reg, _val, _field) \
+ (((_reg) & ~(_field)) | FIELD_PREP((_field), (_val)))
+
+int cv1800_clk_setbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field);
+int cv1800_clk_clearbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field);
+int cv1800_clk_checkbit(struct cv1800_clk_common *common,
+ struct cv1800_clk_regbit *field);
+
+void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
+ u32 reg, u32 lock);
+
+#endif // _CLK_SOPHGO_CV18XX_IP_H_
diff --git a/drivers/clk/sophgo/clk-cv18xx-ip.c b/drivers/clk/sophgo/clk-cv18xx-ip.c
new file mode 100644
index 000000000000..805f561725ae
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv18xx-ip.c
@@ -0,0 +1,887 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/gcd.h>
+#include <linux/spinlock.h>
+
+#include "clk-cv18xx-ip.h"
+
+/* GATE */
+static inline struct cv1800_clk_gate *hw_to_cv1800_clk_gate(struct clk_hw *hw)
+{
+ struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
+
+ return container_of(common, struct cv1800_clk_gate, common);
+}
+
+static int gate_enable(struct clk_hw *hw)
+{
+ struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);
+
+ return cv1800_clk_setbit(&gate->common, &gate->gate);
+}
+
+static void gate_disable(struct clk_hw *hw)
+{
+ struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);
+
+ cv1800_clk_clearbit(&gate->common, &gate->gate);
+}
+
+static int gate_is_enabled(struct clk_hw *hw)
+{
+ struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);
+
+ return cv1800_clk_checkbit(&gate->common, &gate->gate);
+}
+
+static unsigned long gate_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return parent_rate;
+}
+
+static long gate_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return *parent_rate;
+}
+
+static int gate_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ return 0;
+}
+
+const struct clk_ops cv1800_clk_gate_ops = {
+ .disable = gate_disable,
+ .enable = gate_enable,
+ .is_enabled = gate_is_enabled,
+
+ .recalc_rate = gate_recalc_rate,
+ .round_rate = gate_round_rate,
+ .set_rate = gate_set_rate,
+};
+
+/* DIV */
+#define _DIV_EN_CLK_DIV_FACTOR_FIELD BIT(3)
+
+#define DIV_GET_EN_CLK_DIV_FACTOR(_reg) \
+ FIELD_GET(_DIV_EN_CLK_DIV_FACTOR_FIELD, _reg)
+
+#define DIV_SET_EN_DIV_FACTOR(_reg) \
+ _CV1800_SET_FIELD(_reg, 1, _DIV_EN_CLK_DIV_FACTOR_FIELD)
+
+static inline struct cv1800_clk_div *hw_to_cv1800_clk_div(struct clk_hw *hw)
+{
+ struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
+
+ return container_of(common, struct cv1800_clk_div, common);
+}
+
+static int div_enable(struct clk_hw *hw)
+{
+ struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
+
+ return cv1800_clk_setbit(&div->common, &div->gate);
+}
+
+static void div_disable(struct clk_hw *hw)
+{
+ struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
+
+ cv1800_clk_clearbit(&div->common, &div->gate);
+}
+
+static int div_is_enabled(struct clk_hw *hw)
+{
+ struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
+
+ return cv1800_clk_checkbit(&div->common, &div->gate);
+}
+
+static int div_helper_set_rate(struct cv1800_clk_common *common,
+ struct cv1800_clk_regfield *div,
+ unsigned long val)
+{
+ unsigned long flags;
+ u32 reg;
+
+ if (div->width == 0)
+ return 0;
+
+ spin_lock_irqsave(common->lock, flags);
+
+ reg = readl(common->base + div->reg);
+ reg = cv1800_clk_regfield_set(reg, val, div);
+ if (div->initval > 0)
+ reg = DIV_SET_EN_DIV_FACTOR(reg);
+
+ writel(reg, common->base + div->reg);
+
+ spin_unlock_irqrestore(common->lock, flags);
+
+ return 0;
+}
+
+static u32 div_helper_get_clockdiv(struct cv1800_clk_common *common,
+ struct cv1800_clk_regfield *div)
+{
+ u32 clockdiv = 1;
+ u32 reg;
+
+ if (!div || div->initval < 0 || (div->width == 0 && div->initval <= 0))
+ return 1;
+
+ if (div->width == 0 && div->initval > 0)
+ return div->initval;
+
+ reg = readl(common->base + div->reg);
+
+ if (div->initval == 0 || DIV_GET_EN_CLK_DIV_FACTOR(reg))
+ clockdiv = cv1800_clk_regfield_get(reg, div);
+ else if (div->initval > 0)
+ clockdiv = div->initval;
+
+ return clockdiv;
+}
+
+static u32 div_helper_round_rate(struct cv1800_clk_regfield *div,
+ struct clk_hw *hw, struct clk_hw *parent,
+ unsigned long rate, unsigned long *prate)
+{
+ if (div->width == 0) {
+ if (div->initval <= 0)
+ return DIV_ROUND_UP_ULL(*prate, 1);
+ else
+ return DIV_ROUND_UP_ULL(*prate, div->initval);
+ }
+
+ return divider_round_rate_parent(hw, parent, rate, prate, NULL,
+ div->width, div->flags);
+}
+
+static long div_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
+ unsigned long rate, int id, void *data)
+{
+ struct cv1800_clk_div *div = data;
+
+ return div_helper_round_rate(&div->div, &div->common.hw, parent,
+ rate, parent_rate);
+}
+
+static bool div_is_better_rate(struct cv1800_clk_common *common,
+ unsigned long target, unsigned long now,
+ unsigned long best)
+{
+ if (common->features & CLK_DIVIDER_ROUND_CLOSEST)
+ return abs_diff(target, now) < abs_diff(target, best);
+
+ return now <= target && now > best;
+}
+
+static int mux_helper_determine_rate(struct cv1800_clk_common *common,
+ struct clk_rate_request *req,
+ long (*round)(struct clk_hw *,
+ unsigned long *,
+ unsigned long,
+ int,
+ void *),
+ void *data)
+{
+ unsigned long best_parent_rate = 0, best_rate = 0;
+ struct clk_hw *best_parent, *hw = &common->hw;
+ unsigned int i;
+
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
+ unsigned long adj_parent_rate;
+
+ best_parent = clk_hw_get_parent(hw);
+ best_parent_rate = clk_hw_get_rate(best_parent);
+
+ best_rate = round(best_parent, &adj_parent_rate,
+ req->rate, -1, data);
+
+ goto find;
+ }
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ unsigned long tmp_rate, parent_rate;
+ struct clk_hw *parent;
+
+ parent = clk_hw_get_parent_by_index(hw, i);
+ if (!parent)
+ continue;
+
+ parent_rate = clk_hw_get_rate(parent);
+
+ tmp_rate = round(parent, &parent_rate, req->rate, i, data);
+
+ if (tmp_rate == req->rate) {
+ best_parent = parent;
+ best_parent_rate = parent_rate;
+ best_rate = tmp_rate;
+ goto find;
+ }
+
+ if (div_is_better_rate(common, req->rate,
+ tmp_rate, best_rate)) {
+ best_parent = parent;
+ best_parent_rate = parent_rate;
+ best_rate = tmp_rate;
+ }
+ }
+
+ if (best_rate == 0)
+ return -EINVAL;
+
+find:
+ req->best_parent_hw = best_parent;
+ req->best_parent_rate = best_parent_rate;
+ req->rate = best_rate;
+ return 0;
+}
+
+static int div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
+
+ return mux_helper_determine_rate(&div->common, req,
+ div_round_rate, div);
+}
+
+static unsigned long div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
+ unsigned long val;
+
+ val = div_helper_get_clockdiv(&div->common, &div->div);
+ if (val == 0)
+ return 0;
+
+ return divider_recalc_rate(hw, parent_rate, val, NULL,
+ div->div.flags, div->div.width);
+}
+
+static int div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
+ unsigned long val;
+
+ val = divider_get_val(rate, parent_rate, NULL,
+ div->div.width, div->div.flags);
+
+ return div_helper_set_rate(&div->common, &div->div, val);
+}
+
+const struct clk_ops cv1800_clk_div_ops = {
+ .disable = div_disable,
+ .enable = div_enable,
+ .is_enabled = div_is_enabled,
+
+ .determine_rate = div_determine_rate,
+ .recalc_rate = div_recalc_rate,
+ .set_rate = div_set_rate,
+};
+
+static inline struct cv1800_clk_bypass_div *
+hw_to_cv1800_clk_bypass_div(struct clk_hw *hw)
+{
+ struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
+
+ return container_of(div, struct cv1800_clk_bypass_div, div);
+}
+
+static long bypass_div_round_rate(struct clk_hw *parent,
+ unsigned long *parent_rate,
+ unsigned long rate, int id, void *data)
+{
+ struct cv1800_clk_bypass_div *div = data;
+
+ if (id == -1) {
+ if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
+ return *parent_rate;
+ else
+ return div_round_rate(parent, parent_rate, rate,
+ -1, &div->div);
+ }
+
+ if (id == 0)
+ return *parent_rate;
+
+ return div_round_rate(parent, parent_rate, rate, id - 1, &div->div);
+}
+
+static int bypass_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
+
+ return mux_helper_determine_rate(&div->div.common, req,
+ bypass_div_round_rate, div);
+}
+
+static unsigned long bypass_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
+
+ if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
+ return parent_rate;
+
+ return div_recalc_rate(hw, parent_rate);
+}
+
+static int bypass_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
+
+ if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
+ return 0;
+
+ return div_set_rate(hw, rate, parent_rate);
+}
+
+static u8 bypass_div_get_parent(struct clk_hw *hw)
+{
+ struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
+
+ if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
+ return 0;
+
+ return 1;
+}
+
+static int bypass_div_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
+
+ if (index)
+ return cv1800_clk_clearbit(&div->div.common, &div->bypass);
+
+ return cv1800_clk_setbit(&div->div.common, &div->bypass);
+}
+
+const struct clk_ops cv1800_clk_bypass_div_ops = {
+ .disable = div_disable,
+ .enable = div_enable,
+ .is_enabled = div_is_enabled,
+
+ .determine_rate = bypass_div_determine_rate,
+ .recalc_rate = bypass_div_recalc_rate,
+ .set_rate = bypass_div_set_rate,
+
+ .set_parent = bypass_div_set_parent,
+ .get_parent = bypass_div_get_parent,
+};
+
+/* MUX */
+static inline struct cv1800_clk_mux *hw_to_cv1800_clk_mux(struct clk_hw *hw)
+{
+ struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
+
+ return container_of(common, struct cv1800_clk_mux, common);
+}
+
+static int mux_enable(struct clk_hw *hw)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+
+ return cv1800_clk_setbit(&mux->common, &mux->gate);
+}
+
+static void mux_disable(struct clk_hw *hw)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+
+ cv1800_clk_clearbit(&mux->common, &mux->gate);
+}
+
+static int mux_is_enabled(struct clk_hw *hw)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+
+ return cv1800_clk_checkbit(&mux->common, &mux->gate);
+}
+
+static long mux_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
+ unsigned long rate, int id, void *data)
+{
+ struct cv1800_clk_mux *mux = data;
+
+ return div_helper_round_rate(&mux->div, &mux->common.hw, parent,
+ rate, parent_rate);
+}
+
+static int mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+
+ return mux_helper_determine_rate(&mux->common, req,
+ mux_round_rate, mux);
+}
+
+static unsigned long mux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+ unsigned long val;
+
+ val = div_helper_get_clockdiv(&mux->common, &mux->div);
+ if (val == 0)
+ return 0;
+
+ return divider_recalc_rate(hw, parent_rate, val, NULL,
+ mux->div.flags, mux->div.width);
+}
+
+static int mux_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+ unsigned long val;
+
+ val = divider_get_val(rate, parent_rate, NULL,
+ mux->div.width, mux->div.flags);
+
+ return div_helper_set_rate(&mux->common, &mux->div, val);
+}
+
+static u8 mux_get_parent(struct clk_hw *hw)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+ u32 reg = readl(mux->common.base + mux->mux.reg);
+
+ return cv1800_clk_regfield_get(reg, &mux->mux);
+}
+
+static int _mux_set_parent(struct cv1800_clk_mux *mux, u8 index)
+{
+ u32 reg;
+
+ reg = readl(mux->common.base + mux->mux.reg);
+ reg = cv1800_clk_regfield_set(reg, index, &mux->mux);
+ writel(reg, mux->common.base + mux->mux.reg);
+
+ return 0;
+}
+
+static int mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(mux->common.lock, flags);
+
+ _mux_set_parent(mux, index);
+
+ spin_unlock_irqrestore(mux->common.lock, flags);
+
+ return 0;
+}
+
+const struct clk_ops cv1800_clk_mux_ops = {
+ .disable = mux_disable,
+ .enable = mux_enable,
+ .is_enabled = mux_is_enabled,
+
+ .determine_rate = mux_determine_rate,
+ .recalc_rate = mux_recalc_rate,
+ .set_rate = mux_set_rate,
+
+ .set_parent = mux_set_parent,
+ .get_parent = mux_get_parent,
+};
+
+static inline struct cv1800_clk_bypass_mux *
+hw_to_cv1800_clk_bypass_mux(struct clk_hw *hw)
+{
+ struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
+
+ return container_of(mux, struct cv1800_clk_bypass_mux, mux);
+}
+
+static long bypass_mux_round_rate(struct clk_hw *parent,
+ unsigned long *parent_rate,
+ unsigned long rate, int id, void *data)
+{
+ struct cv1800_clk_bypass_mux *mux = data;
+
+ if (id == -1) {
+ if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
+ return *parent_rate;
+ else
+ return mux_round_rate(parent, parent_rate, rate,
+ -1, &mux->mux);
+ }
+
+ if (id == 0)
+ return *parent_rate;
+
+ return mux_round_rate(parent, parent_rate, rate, id - 1, &mux->mux);
+}
+
+static int bypass_mux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
+
+ return mux_helper_determine_rate(&mux->mux.common, req,
+ bypass_mux_round_rate, mux);
+}
+
+static unsigned long bypass_mux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
+
+ if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
+ return parent_rate;
+
+ return mux_recalc_rate(hw, parent_rate);
+}
+
+static int bypass_mux_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
+
+ if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
+ return 0;
+
+ return mux_set_rate(hw, rate, parent_rate);
+}
+
+static u8 bypass_mux_get_parent(struct clk_hw *hw)
+{
+ struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
+
+ if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
+ return 0;
+
+ return mux_get_parent(hw) + 1;
+}
+
+static int bypass_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
+
+ if (index == 0)
+ return cv1800_clk_setbit(&mux->mux.common, &mux->bypass);
+
+ return cv1800_clk_clearbit(&mux->mux.common, &mux->bypass);
+}
+
+const struct clk_ops cv1800_clk_bypass_mux_ops = {
+ .disable = mux_disable,
+ .enable = mux_enable,
+ .is_enabled = mux_is_enabled,
+
+ .determine_rate = bypass_mux_determine_rate,
+ .recalc_rate = bypass_mux_recalc_rate,
+ .set_rate = bypass_mux_set_rate,
+
+ .set_parent = bypass_mux_set_parent,
+ .get_parent = bypass_mux_get_parent,
+};
+
+/* MMUX */
+static inline struct cv1800_clk_mmux *hw_to_cv1800_clk_mmux(struct clk_hw *hw)
+{
+ struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
+
+ return container_of(common, struct cv1800_clk_mmux, common);
+}
+
+static u8 mmux_get_parent_id(struct cv1800_clk_mmux *mmux)
+{
+ struct clk_hw *hw = &mmux->common.hw;
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ unsigned int i;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ if (parent == clk_hw_get_parent_by_index(hw, i))
+ return i;
+ }
+
+ unreachable();
+}
+
+static int mmux_enable(struct clk_hw *hw)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+
+ return cv1800_clk_setbit(&mmux->common, &mmux->gate);
+}
+
+static void mmux_disable(struct clk_hw *hw)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+
+ cv1800_clk_clearbit(&mmux->common, &mmux->gate);
+}
+
+static int mmux_is_enabled(struct clk_hw *hw)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+
+ return cv1800_clk_checkbit(&mmux->common, &mmux->gate);
+}
+
+static long mmux_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
+ unsigned long rate, int id, void *data)
+{
+ struct cv1800_clk_mmux *mmux = data;
+ s8 div_id;
+
+ if (id == -1) {
+ if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
+ return *parent_rate;
+
+ id = mmux_get_parent_id(mmux);
+ }
+
+ div_id = mmux->parent2sel[id];
+
+ if (div_id < 0)
+ return *parent_rate;
+
+ return div_helper_round_rate(&mmux->div[div_id],
+ &mmux->common.hw, parent,
+ rate, parent_rate);
+}
+
+static int mmux_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+
+ return mux_helper_determine_rate(&mmux->common, req,
+ mmux_round_rate, mmux);
+}
+
+static unsigned long mmux_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+ unsigned long val;
+ struct cv1800_clk_regfield *div;
+
+ if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
+ return parent_rate;
+
+ if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
+ div = &mmux->div[0];
+ else
+ div = &mmux->div[1];
+
+ val = div_helper_get_clockdiv(&mmux->common, div);
+ if (val == 0)
+ return 0;
+
+ return divider_recalc_rate(hw, parent_rate, val, NULL,
+ div->flags, div->width);
+}
+
+static int mmux_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+ struct cv1800_clk_regfield *div;
+ unsigned long val;
+
+ if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
+ return parent_rate;
+
+ if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
+ div = &mmux->div[0];
+ else
+ div = &mmux->div[1];
+
+ val = divider_get_val(rate, parent_rate, NULL,
+ div->width, div->flags);
+
+ return div_helper_set_rate(&mmux->common, div, val);
+}
+
+static u8 mmux_get_parent(struct clk_hw *hw)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+ struct cv1800_clk_regfield *mux;
+ u32 reg;
+ s8 clk_sel;
+
+ if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
+ return 0;
+
+ if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
+ clk_sel = 0;
+ else
+ clk_sel = 1;
+ mux = &mmux->mux[clk_sel];
+
+ reg = readl(mmux->common.base + mux->reg);
+
+ return mmux->sel2parent[clk_sel][cv1800_clk_regfield_get(reg, mux)];
+}
+
+static int mmux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
+ struct cv1800_clk_regfield *mux;
+ unsigned long flags;
+ u32 reg;
+ s8 clk_sel = mmux->parent2sel[index];
+
+ if (index == 0 || clk_sel == -1) {
+ cv1800_clk_setbit(&mmux->common, &mmux->bypass);
+ goto release;
+ }
+
+ cv1800_clk_clearbit(&mmux->common, &mmux->bypass);
+
+ if (clk_sel)
+ cv1800_clk_clearbit(&mmux->common, &mmux->clk_sel);
+ else
+ cv1800_clk_setbit(&mmux->common, &mmux->clk_sel);
+
+ spin_lock_irqsave(mmux->common.lock, flags);
+
+ mux = &mmux->mux[clk_sel];
+ reg = readl(mmux->common.base + mux->reg);
+ reg = cv1800_clk_regfield_set(reg, index, mux);
+
+ writel(reg, mmux->common.base + mux->reg);
+
+ spin_unlock_irqrestore(mmux->common.lock, flags);
+
+release:
+ return 0;
+}
+
+const struct clk_ops cv1800_clk_mmux_ops = {
+ .disable = mmux_disable,
+ .enable = mmux_enable,
+ .is_enabled = mmux_is_enabled,
+
+ .determine_rate = mmux_determine_rate,
+ .recalc_rate = mmux_recalc_rate,
+ .set_rate = mmux_set_rate,
+
+ .set_parent = mmux_set_parent,
+ .get_parent = mmux_get_parent,
+};
+
+/* AUDIO CLK */
+static inline struct cv1800_clk_audio *
+hw_to_cv1800_clk_audio(struct clk_hw *hw)
+{
+ struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
+
+ return container_of(common, struct cv1800_clk_audio, common);
+}
+
+static int aclk_enable(struct clk_hw *hw)
+{
+ struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
+
+ cv1800_clk_setbit(&aclk->common, &aclk->src_en);
+ return cv1800_clk_setbit(&aclk->common, &aclk->output_en);
+}
+
+static void aclk_disable(struct clk_hw *hw)
+{
+ struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
+
+ cv1800_clk_clearbit(&aclk->common, &aclk->output_en);
+ cv1800_clk_clearbit(&aclk->common, &aclk->src_en);
+}
+
+static int aclk_is_enabled(struct clk_hw *hw)
+{
+ struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
+
+ return cv1800_clk_checkbit(&aclk->common, &aclk->output_en);
+}
+
+static int aclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
+
+ req->rate = aclk->target_rate;
+
+ return 0;
+}
+
+static unsigned long aclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
+ u64 rate = parent_rate;
+ u64 factor = 2;
+ u32 regval;
+
+ if (!cv1800_clk_checkbit(&aclk->common, &aclk->div_en))
+ return 0;
+
+ regval = readl(aclk->common.base + aclk->m.reg);
+ factor *= cv1800_clk_regfield_get(regval, &aclk->m);
+
+ regval = readl(aclk->common.base + aclk->n.reg);
+ rate *= cv1800_clk_regfield_get(regval, &aclk->n);
+
+ return DIV64_U64_ROUND_UP(rate, factor);
+}
+
+static void aclk_determine_mn(unsigned long parent_rate, unsigned long rate,
+ u32 *m, u32 *n)
+{
+ u32 tm = parent_rate / 2;
+ u32 tn = rate;
+ u32 tcommon = gcd(tm, tn);
+ *m = tm / tcommon;
+ *n = tn / tcommon;
+}
+
+static int aclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
+ unsigned long flags;
+ u32 m, n;
+
+ aclk_determine_mn(parent_rate, rate,
+ &m, &n);
+
+ spin_lock_irqsave(aclk->common.lock, flags);
+
+ writel(m, aclk->common.base + aclk->m.reg);
+ writel(n, aclk->common.base + aclk->n.reg);
+
+ cv1800_clk_setbit(&aclk->common, &aclk->div_en);
+ cv1800_clk_setbit(&aclk->common, &aclk->div_up);
+
+ spin_unlock_irqrestore(aclk->common.lock, flags);
+
+ return 0;
+}
+
+const struct clk_ops cv1800_clk_audio_ops = {
+ .disable = aclk_disable,
+ .enable = aclk_enable,
+ .is_enabled = aclk_is_enabled,
+
+ .determine_rate = aclk_determine_rate,
+ .recalc_rate = aclk_recalc_rate,
+ .set_rate = aclk_set_rate,
+};
diff --git a/drivers/clk/sophgo/clk-cv18xx-ip.h b/drivers/clk/sophgo/clk-cv18xx-ip.h
new file mode 100644
index 000000000000..b37ba42bfde3
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv18xx-ip.h
@@ -0,0 +1,261 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#ifndef _CLK_SOPHGO_CV1800_IP_H_
+#define _CLK_SOPHGO_CV1800_IP_H_
+
+#include "clk-cv18xx-common.h"
+
+struct cv1800_clk_gate {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+};
+
+struct cv1800_clk_div_data {
+ u32 reg;
+ u32 mask;
+ u32 width;
+ u32 init;
+ u32 flags;
+};
+
+struct cv1800_clk_div {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+ struct cv1800_clk_regfield div;
+};
+
+struct cv1800_clk_bypass_div {
+ struct cv1800_clk_div div;
+ struct cv1800_clk_regbit bypass;
+};
+
+struct cv1800_clk_mux {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+ struct cv1800_clk_regfield div;
+ struct cv1800_clk_regfield mux;
+};
+
+struct cv1800_clk_bypass_mux {
+ struct cv1800_clk_mux mux;
+ struct cv1800_clk_regbit bypass;
+};
+
+struct cv1800_clk_mmux {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit gate;
+ struct cv1800_clk_regfield div[2];
+ struct cv1800_clk_regfield mux[2];
+ struct cv1800_clk_regbit bypass;
+ struct cv1800_clk_regbit clk_sel;
+ const s8 *parent2sel;
+ const u8 *sel2parent[2];
+};
+
+struct cv1800_clk_audio {
+ struct cv1800_clk_common common;
+ struct cv1800_clk_regbit src_en;
+ struct cv1800_clk_regbit output_en;
+ struct cv1800_clk_regbit div_en;
+ struct cv1800_clk_regbit div_up;
+ struct cv1800_clk_regfield m;
+ struct cv1800_clk_regfield n;
+ u32 target_rate;
+};
+
+#define CV1800_GATE(_name, _parent, _gate_reg, _gate_shift, _flags) \
+ struct cv1800_clk_gate _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_gate_ops, \
+ _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, _gate_shift), \
+ }
+
+#define _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, _ops, _flags) \
+ { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ _ops, _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, \
+ _gate_shift), \
+ .div = CV1800_CLK_REG(_div_reg, _div_shift, \
+ _div_width, _div_init, \
+ _div_flag), \
+ }
+
+#define _CV1800_FIXED_DIV_FLAG \
+ (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST)
+
+#define _CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _fix_div, _ops, _flags) \
+ { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ _ops, _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, \
+ _gate_shift), \
+ .div = CV1800_CLK_REG(0, 0, 0, \
+ _fix_div, \
+ _CV1800_FIXED_DIV_FLAG),\
+ }
+
+#define CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, _flags) \
+ struct cv1800_clk_div _name = \
+ _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init,\
+ _div_flag, &cv1800_clk_div_ops, _flags)
+
+#define CV1800_BYPASS_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, _bypass_reg, _bypass_shift, _flags)\
+ struct cv1800_clk_bypass_div _name = { \
+ .div = _CV1800_DIV(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, \
+ _div_width, _div_init, _div_flag, \
+ &cv1800_clk_bypass_div_ops, \
+ _flags), \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
+ }
+
+#define CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _fix_div, _flags) \
+ struct cv1800_clk_div _name = \
+ _CV1800_FIXED_DIV(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _fix_div, \
+ &cv1800_clk_div_ops, _flags) \
+
+#define CV1800_BYPASS_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
+ _fix_div, _bypass_reg, _bypass_shift, \
+ _flags) \
+ struct cv1800_clk_bypass_div _name = { \
+ .div = _CV1800_FIXED_DIV(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _fix_div, \
+ &cv1800_clk_bypass_div_ops, \
+ _flags), \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
+ }
+
+#define _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, \
+ _ops, _flags) \
+ { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ _ops, _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, \
+ _gate_shift), \
+ .div = CV1800_CLK_REG(_div_reg, _div_shift, \
+ _div_width, _div_init, \
+ _div_flag), \
+ .mux = CV1800_CLK_REG(_mux_reg, _mux_shift, \
+ _mux_width, 0, 0), \
+ }
+
+#define CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, _flags) \
+ struct cv1800_clk_mux _name = \
+ _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init,\
+ _div_flag, _mux_reg, _mux_shift, _mux_width,\
+ &cv1800_clk_mux_ops, _flags)
+
+#define CV1800_BYPASS_MUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, _div_init, \
+ _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, \
+ _bypass_reg, _bypass_shift, _flags) \
+ struct cv1800_clk_bypass_mux _name = { \
+ .mux = _CV1800_MUX(_name, _parent, \
+ _gate_reg, _gate_shift, \
+ _div_reg, _div_shift, _div_width, \
+ _div_init, _div_flag, \
+ _mux_reg, _mux_shift, _mux_width, \
+ &cv1800_clk_bypass_mux_ops, \
+ _flags), \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
+ }
+
+#define CV1800_MMUX(_name, _parent, _gate_reg, _gate_shift, \
+ _div0_reg, _div0_shift, _div0_width, _div0_init, \
+ _div0_flag, \
+ _div1_reg, _div1_shift, _div1_width, _div1_init, \
+ _div1_flag, \
+ _mux0_reg, _mux0_shift, _mux0_width, \
+ _mux1_reg, _mux1_shift, _mux1_width, \
+ _bypass_reg, _bypass_shift, \
+ _clk_sel_reg, _clk_sel_shift, \
+ _parent2sel, _sel2parent0, _sel2parent1, _flags) \
+ struct cv1800_clk_mmux _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_mmux_ops,\
+ _flags), \
+ .gate = CV1800_CLK_BIT(_gate_reg, _gate_shift),\
+ .div = { \
+ CV1800_CLK_REG(_div0_reg, _div0_shift, \
+ _div0_width, _div0_init, \
+ _div0_flag), \
+ CV1800_CLK_REG(_div1_reg, _div1_shift, \
+ _div1_width, _div1_init, \
+ _div1_flag), \
+ }, \
+ .mux = { \
+ CV1800_CLK_REG(_mux0_reg, _mux0_shift, \
+ _mux0_width, 0, 0), \
+ CV1800_CLK_REG(_mux1_reg, _mux1_shift, \
+ _mux1_width, 0, 0), \
+ }, \
+ .bypass = CV1800_CLK_BIT(_bypass_reg, \
+ _bypass_shift), \
+ .clk_sel = CV1800_CLK_BIT(_clk_sel_reg, \
+ _clk_sel_shift), \
+ .parent2sel = _parent2sel, \
+ .sel2parent = { _sel2parent0, _sel2parent1 }, \
+ }
+
+#define CV1800_ACLK(_name, _parent, \
+ _src_en_reg, _src_en_reg_shift, \
+ _output_en_reg, _output_en_shift, \
+ _div_en_reg, _div_en_reg_shift, \
+ _div_up_reg, _div_up_reg_shift, \
+ _m_reg, _m_shift, _m_width, _m_flag, \
+ _n_reg, _n_shift, _n_width, _n_flag, \
+ _target_rate, _flags) \
+ struct cv1800_clk_audio _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_audio_ops,\
+ _flags), \
+ .src_en = CV1800_CLK_BIT(_src_en_reg, \
+ _src_en_reg_shift), \
+ .output_en = CV1800_CLK_BIT(_output_en_reg, \
+ _output_en_shift), \
+ .div_en = CV1800_CLK_BIT(_div_en_reg, \
+ _div_en_reg_shift), \
+ .div_up = CV1800_CLK_BIT(_div_up_reg, \
+ _div_up_reg_shift), \
+ .m = CV1800_CLK_REG(_m_reg, _m_shift, \
+ _m_width, 0, _m_flag), \
+ .n = CV1800_CLK_REG(_n_reg, _n_shift, \
+ _n_width, 0, _n_flag), \
+ .target_rate = _target_rate, \
+ }
+
+extern const struct clk_ops cv1800_clk_gate_ops;
+extern const struct clk_ops cv1800_clk_div_ops;
+extern const struct clk_ops cv1800_clk_bypass_div_ops;
+extern const struct clk_ops cv1800_clk_mux_ops;
+extern const struct clk_ops cv1800_clk_bypass_mux_ops;
+extern const struct clk_ops cv1800_clk_mmux_ops;
+extern const struct clk_ops cv1800_clk_audio_ops;
+
+#endif // _CLK_SOPHGO_CV1800_IP_H_
diff --git a/drivers/clk/sophgo/clk-cv18xx-pll.c b/drivers/clk/sophgo/clk-cv18xx-pll.c
new file mode 100644
index 000000000000..c546dad1791c
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv18xx-pll.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/spinlock.h>
+
+#include "clk-cv18xx-pll.h"
+
+static inline struct cv1800_clk_pll *hw_to_cv1800_clk_pll(struct clk_hw *hw)
+{
+ struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
+
+ return container_of(common, struct cv1800_clk_pll, common);
+}
+
+static unsigned long ipll_calc_rate(unsigned long parent_rate,
+ unsigned long pre_div_sel,
+ unsigned long div_sel,
+ unsigned long post_div_sel)
+{
+ uint64_t rate = parent_rate;
+
+ rate *= div_sel;
+ do_div(rate, pre_div_sel * post_div_sel);
+
+ return rate;
+}
+
+static unsigned long ipll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+ u32 value;
+
+ value = readl(pll->common.base + pll->pll_reg);
+
+ return ipll_calc_rate(parent_rate,
+ PLL_GET_PRE_DIV_SEL(value),
+ PLL_GET_DIV_SEL(value),
+ PLL_GET_POST_DIV_SEL(value));
+}
+
+static int ipll_find_rate(const struct cv1800_clk_pll_limit *limit,
+ unsigned long prate, unsigned long *rate,
+ u32 *value)
+{
+ unsigned long best_rate = 0;
+ unsigned long trate = *rate;
+ unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
+ unsigned long pre, div, post;
+ u32 detected = *value;
+ unsigned long tmp;
+
+ for_each_pll_limit_range(pre, &limit->pre_div) {
+ for_each_pll_limit_range(div, &limit->div) {
+ for_each_pll_limit_range(post, &limit->post_div) {
+ tmp = ipll_calc_rate(prate, pre, div, post);
+
+ if (tmp > trate)
+ continue;
+
+ if ((trate - tmp) < (trate - best_rate)) {
+ best_rate = tmp;
+ pre_div_sel = pre;
+ div_sel = div;
+ post_div_sel = post;
+ }
+ }
+ }
+ }
+
+ if (best_rate) {
+ detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
+ detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
+ detected = PLL_SET_DIV_SEL(detected, div_sel);
+ *value = detected;
+ *rate = best_rate;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ipll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ u32 val;
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ return ipll_find_rate(pll->pll_limit, req->best_parent_rate,
+ &req->rate, &val);
+}
+
+static void pll_get_mode_ctrl(unsigned long div_sel,
+ bool (*mode_ctrl_check)(unsigned long,
+ unsigned long,
+ unsigned long),
+ const struct cv1800_clk_pll_limit *limit,
+ u32 *value)
+{
+ unsigned long ictrl = 0, mode = 0;
+ u32 detected = *value;
+
+ for_each_pll_limit_range(mode, &limit->mode) {
+ for_each_pll_limit_range(ictrl, &limit->ictrl) {
+ if (mode_ctrl_check(div_sel, ictrl, mode)) {
+ detected = PLL_SET_SEL_MODE(detected, mode);
+ detected = PLL_SET_ICTRL(detected, ictrl);
+ *value = detected;
+ return;
+ }
+ }
+ }
+}
+
+static bool ipll_check_mode_ctrl_restrict(unsigned long div_sel,
+ unsigned long ictrl,
+ unsigned long mode)
+{
+ unsigned long left_rest = 20 * div_sel;
+ unsigned long right_rest = 35 * div_sel;
+ unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
+
+ return test > left_rest && test <= right_rest;
+}
+
+static int ipll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 regval, detected = 0;
+ unsigned long flags;
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ ipll_find_rate(pll->pll_limit, parent_rate, &rate, &detected);
+ pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
+ ipll_check_mode_ctrl_restrict,
+ pll->pll_limit, &detected);
+
+ spin_lock_irqsave(pll->common.lock, flags);
+
+ regval = readl(pll->common.base + pll->pll_reg);
+ regval = PLL_COPY_REG(regval, detected);
+
+ writel(regval, pll->common.base + pll->pll_reg);
+
+ spin_unlock_irqrestore(pll->common.lock, flags);
+
+ cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
+ BIT(pll->pll_status.shift));
+
+ return 0;
+}
+
+static int pll_enable(struct clk_hw *hw)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ return cv1800_clk_clearbit(&pll->common, &pll->pll_pwd);
+}
+
+static void pll_disable(struct clk_hw *hw)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ cv1800_clk_setbit(&pll->common, &pll->pll_pwd);
+}
+
+static int pll_is_enable(struct clk_hw *hw)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ return cv1800_clk_checkbit(&pll->common, &pll->pll_pwd) == 0;
+}
+
+const struct clk_ops cv1800_clk_ipll_ops = {
+ .disable = pll_disable,
+ .enable = pll_enable,
+ .is_enabled = pll_is_enable,
+
+ .recalc_rate = ipll_recalc_rate,
+ .determine_rate = ipll_determine_rate,
+ .set_rate = ipll_set_rate,
+};
+
+#define PLL_SYN_FACTOR_DOT_POS 26
+#define PLL_SYN_FACTOR_MINIMUM ((4 << PLL_SYN_FACTOR_DOT_POS) + 1)
+
+static bool fpll_is_factional_mode(struct cv1800_clk_pll *pll)
+{
+ return cv1800_clk_checkbit(&pll->common, &pll->pll_syn->en);
+}
+
+static unsigned long fpll_calc_rate(unsigned long parent_rate,
+ unsigned long pre_div_sel,
+ unsigned long div_sel,
+ unsigned long post_div_sel,
+ unsigned long ssc_syn_set,
+ bool is_full_parent)
+{
+ u64 dividend = parent_rate * div_sel;
+ u64 factor = ssc_syn_set * pre_div_sel * post_div_sel;
+ unsigned long rate;
+
+ dividend <<= PLL_SYN_FACTOR_DOT_POS - 1;
+ rate = dividend / factor;
+ dividend %= factor;
+
+ if (is_full_parent) {
+ dividend <<= 1;
+ rate <<= 1;
+ }
+
+ rate += DIV64_U64_ROUND_CLOSEST(dividend, factor);
+
+ return rate;
+}
+
+static unsigned long fpll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+ u32 value;
+ bool clk_full;
+ u32 syn_set;
+
+ if (!fpll_is_factional_mode(pll))
+ return ipll_recalc_rate(hw, parent_rate);
+
+ syn_set = readl(pll->common.base + pll->pll_syn->set);
+
+ if (syn_set == 0)
+ return 0;
+
+ clk_full = cv1800_clk_checkbit(&pll->common,
+ &pll->pll_syn->clk_half);
+
+ value = readl(pll->common.base + pll->pll_reg);
+
+ return fpll_calc_rate(parent_rate,
+ PLL_GET_PRE_DIV_SEL(value),
+ PLL_GET_DIV_SEL(value),
+ PLL_GET_POST_DIV_SEL(value),
+ syn_set, clk_full);
+}
+
+static unsigned long fpll_find_synthesizer(unsigned long parent,
+ unsigned long rate,
+ unsigned long pre_div,
+ unsigned long div,
+ unsigned long post_div,
+ bool is_full_parent,
+ u32 *ssc_syn_set)
+{
+ u32 test_max = U32_MAX, test_min = PLL_SYN_FACTOR_MINIMUM;
+ unsigned long trate;
+
+ while (test_min < test_max) {
+ u32 tssc = (test_max + test_min) / 2;
+
+ trate = fpll_calc_rate(parent, pre_div, div, post_div,
+ tssc, is_full_parent);
+
+ if (trate == rate) {
+ test_min = tssc;
+ break;
+ }
+
+ if (trate > rate)
+ test_min = tssc + 1;
+ else
+ test_max = tssc - 1;
+ }
+
+ if (trate != 0)
+ *ssc_syn_set = test_min;
+
+ return trate;
+}
+
+static int fpll_find_rate(struct cv1800_clk_pll *pll,
+ const struct cv1800_clk_pll_limit *limit,
+ unsigned long prate,
+ unsigned long *rate,
+ u32 *value, u32 *ssc_syn_set)
+{
+ unsigned long best_rate = 0;
+ unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
+ unsigned long pre, div, post;
+ unsigned long trate = *rate;
+ u32 detected = *value;
+ unsigned long tmp;
+ bool clk_full = cv1800_clk_checkbit(&pll->common,
+ &pll->pll_syn->clk_half);
+
+ for_each_pll_limit_range(pre, &limit->pre_div) {
+ for_each_pll_limit_range(post, &limit->post_div) {
+ for_each_pll_limit_range(div, &limit->div) {
+ tmp = fpll_find_synthesizer(prate, trate,
+ pre, div, post,
+ clk_full,
+ ssc_syn_set);
+
+ if ((trate - tmp) < (trate - best_rate)) {
+ best_rate = tmp;
+ pre_div_sel = pre;
+ div_sel = div;
+ post_div_sel = post;
+ }
+ }
+ }
+ }
+
+ if (best_rate) {
+ detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
+ detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
+ detected = PLL_SET_DIV_SEL(detected, div_sel);
+ *value = detected;
+ *rate = best_rate;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int fpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+ u32 val, ssc_syn_set;
+
+ if (!fpll_is_factional_mode(pll))
+ return ipll_determine_rate(hw, req);
+
+ fpll_find_rate(pll, &pll->pll_limit[2], req->best_parent_rate,
+ &req->rate, &val, &ssc_syn_set);
+
+ return 0;
+}
+
+static bool fpll_check_mode_ctrl_restrict(unsigned long div_sel,
+ unsigned long ictrl,
+ unsigned long mode)
+{
+ unsigned long left_rest = 10 * div_sel;
+ unsigned long right_rest = 24 * div_sel;
+ unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
+
+ return test > left_rest && test <= right_rest;
+}
+
+static int fpll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ u32 regval;
+ u32 detected = 0, detected_ssc = 0;
+ unsigned long flags;
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ if (!fpll_is_factional_mode(pll))
+ return ipll_set_rate(hw, rate, parent_rate);
+
+ fpll_find_rate(pll, &pll->pll_limit[2], parent_rate,
+ &rate, &detected, &detected_ssc);
+ pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
+ fpll_check_mode_ctrl_restrict,
+ pll->pll_limit, &detected);
+
+ spin_lock_irqsave(pll->common.lock, flags);
+
+ writel(detected_ssc, pll->common.base + pll->pll_syn->set);
+
+ regval = readl(pll->common.base + pll->pll_reg);
+ regval = PLL_COPY_REG(regval, detected);
+
+ writel(regval, pll->common.base + pll->pll_reg);
+
+ spin_unlock_irqrestore(pll->common.lock, flags);
+
+ cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
+ BIT(pll->pll_status.shift));
+
+ return 0;
+}
+
+static u8 fpll_get_parent(struct clk_hw *hw)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ if (fpll_is_factional_mode(pll))
+ return 1;
+
+ return 0;
+}
+
+static int fpll_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
+
+ if (index)
+ cv1800_clk_setbit(&pll->common, &pll->pll_syn->en);
+ else
+ cv1800_clk_clearbit(&pll->common, &pll->pll_syn->en);
+
+ return 0;
+}
+
+const struct clk_ops cv1800_clk_fpll_ops = {
+ .disable = pll_disable,
+ .enable = pll_enable,
+ .is_enabled = pll_is_enable,
+
+ .recalc_rate = fpll_recalc_rate,
+ .determine_rate = fpll_determine_rate,
+ .set_rate = fpll_set_rate,
+
+ .set_parent = fpll_set_parent,
+ .get_parent = fpll_get_parent,
+};
diff --git a/drivers/clk/sophgo/clk-cv18xx-pll.h b/drivers/clk/sophgo/clk-cv18xx-pll.h
new file mode 100644
index 000000000000..7a33f3da2d64
--- /dev/null
+++ b/drivers/clk/sophgo/clk-cv18xx-pll.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Inochi Amaoto <[email protected]>
+ */
+
+#ifndef _CLK_SOPHGO_CV1800_PLL_H_
+#define _CLK_SOPHGO_CV1800_PLL_H_
+
+#include "clk-cv18xx-common.h"
+
+struct cv1800_clk_pll_limit {
+ struct {
+ u8 min;
+ u8 max;
+ } pre_div, div, post_div, ictrl, mode;
+};
+
+#define _CV1800_PLL_LIMIT(_min, _max) \
+ { \
+ .min = _min, \
+ .max = _max, \
+ } \
+
+#define for_each_pll_limit_range(_var, _restrict) \
+ for (_var = (_restrict)->min; _var <= (_restrict)->max; _var++)
+
+struct cv1800_clk_pll_synthesizer {
+ struct cv1800_clk_regbit en;
+ struct cv1800_clk_regbit clk_half;
+ u32 ctrl;
+ u32 set;
+};
+
+#define _PLL_PRE_DIV_SEL_FIELD GENMASK(6, 0)
+#define _PLL_POST_DIV_SEL_FIELD GENMASK(14, 8)
+#define _PLL_SEL_MODE_FIELD GENMASK(16, 15)
+#define _PLL_DIV_SEL_FIELD GENMASK(23, 17)
+#define _PLL_ICTRL_FIELD GENMASK(26, 24)
+
+#define _PLL_ALL_FIELD_MASK \
+ (_PLL_PRE_DIV_SEL_FIELD | \
+ _PLL_POST_DIV_SEL_FIELD | \
+ _PLL_SEL_MODE_FIELD | \
+ _PLL_DIV_SEL_FIELD | \
+ _PLL_ICTRL_FIELD)
+
+#define PLL_COPY_REG(_dest, _src) \
+ (((_dest) & (~_PLL_ALL_FIELD_MASK)) | ((_src) & _PLL_ALL_FIELD_MASK))
+
+#define PLL_GET_PRE_DIV_SEL(_reg) \
+ FIELD_GET(_PLL_PRE_DIV_SEL_FIELD, (_reg))
+#define PLL_GET_POST_DIV_SEL(_reg) \
+ FIELD_GET(_PLL_POST_DIV_SEL_FIELD, (_reg))
+#define PLL_GET_SEL_MODE(_reg) \
+ FIELD_GET(_PLL_SEL_MODE_FIELD, (_reg))
+#define PLL_GET_DIV_SEL(_reg) \
+ FIELD_GET(_PLL_DIV_SEL_FIELD, (_reg))
+#define PLL_GET_ICTRL(_reg) \
+ FIELD_GET(_PLL_ICTRL_FIELD, (_reg))
+
+#define PLL_SET_PRE_DIV_SEL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_PRE_DIV_SEL_FIELD)
+#define PLL_SET_POST_DIV_SEL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_POST_DIV_SEL_FIELD)
+#define PLL_SET_SEL_MODE(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_SEL_MODE_FIELD)
+#define PLL_SET_DIV_SEL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_DIV_SEL_FIELD)
+#define PLL_SET_ICTRL(_reg, _val) \
+ _CV1800_SET_FIELD((_reg), (_val), _PLL_ICTRL_FIELD)
+
+struct cv1800_clk_pll {
+ struct cv1800_clk_common common;
+ u32 pll_reg;
+ struct cv1800_clk_regbit pll_pwd;
+ struct cv1800_clk_regbit pll_status;
+ const struct cv1800_clk_pll_limit *pll_limit;
+ struct cv1800_clk_pll_synthesizer *pll_syn;
+};
+
+#define CV1800_INTEGRAL_PLL(_name, _parent, _pll_reg, \
+ _pll_pwd_reg, _pll_pwd_shift, \
+ _pll_status_reg, _pll_status_shift, \
+ _pll_limit, _flags) \
+ struct cv1800_clk_pll _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_ipll_ops,\
+ _flags), \
+ .pll_reg = _pll_reg, \
+ .pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
+ _pll_pwd_shift), \
+ .pll_status = CV1800_CLK_BIT(_pll_status_reg, \
+ _pll_status_shift), \
+ .pll_limit = _pll_limit, \
+ .pll_syn = NULL, \
+ }
+
+#define CV1800_FACTIONAL_PLL(_name, _parent, _pll_reg, \
+ _pll_pwd_reg, _pll_pwd_shift, \
+ _pll_status_reg, _pll_status_shift, \
+ _pll_limit, _pll_syn, _flags) \
+ struct cv1800_clk_pll _name = { \
+ .common = CV1800_CLK_COMMON(#_name, _parent, \
+ &cv1800_clk_fpll_ops,\
+ _flags), \
+ .pll_reg = _pll_reg, \
+ .pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
+ _pll_pwd_shift), \
+ .pll_status = CV1800_CLK_BIT(_pll_status_reg, \
+ _pll_status_shift), \
+ .pll_limit = _pll_limit, \
+ .pll_syn = _pll_syn, \
+ }
+
+extern const struct clk_ops cv1800_clk_ipll_ops;
+extern const struct clk_ops cv1800_clk_fpll_ops;
+
+#endif // _CLK_SOPHGO_CV1800_PLL_H_
--
2.44.0


2024-03-09 09:04:48

by Inochi Amaoto

[permalink] [raw]
Subject: [PATCH v9 6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC

Add missing clocks of uart node for CV1800B and CV1812H.

Reviewed-by: Chen Wang <[email protected]>
Signed-off-by: Inochi Amaoto <[email protected]>
---
arch/riscv/boot/dts/sophgo/cv18xx.dtsi | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/arch/riscv/boot/dts/sophgo/cv18xx.dtsi b/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
index 6ea1b2784db9..7c88cbe8e91d 100644
--- a/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
+++ b/arch/riscv/boot/dts/sophgo/cv18xx.dtsi
@@ -5,6 +5,7 @@
*/

#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/clock/sophgo,cv1800.h>

/ {
#address-cells = <1>;
@@ -135,7 +136,8 @@ uart0: serial@4140000 {
compatible = "snps,dw-apb-uart";
reg = <0x04140000 0x100>;
interrupts = <44 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&osc>;
+ clocks = <&clk CLK_UART0>, <&clk CLK_APB_UART0>;
+ clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
@@ -145,7 +147,8 @@ uart1: serial@4150000 {
compatible = "snps,dw-apb-uart";
reg = <0x04150000 0x100>;
interrupts = <45 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&osc>;
+ clocks = <&clk CLK_UART1>, <&clk CLK_APB_UART1>;
+ clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
@@ -155,7 +158,8 @@ uart2: serial@4160000 {
compatible = "snps,dw-apb-uart";
reg = <0x04160000 0x100>;
interrupts = <46 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&osc>;
+ clocks = <&clk CLK_UART2>, <&clk CLK_APB_UART2>;
+ clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
@@ -165,7 +169,8 @@ uart3: serial@4170000 {
compatible = "snps,dw-apb-uart";
reg = <0x04170000 0x100>;
interrupts = <47 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&osc>;
+ clocks = <&clk CLK_UART3>, <&clk CLK_APB_UART3>;
+ clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
@@ -175,7 +180,8 @@ uart4: serial@41c0000 {
compatible = "snps,dw-apb-uart";
reg = <0x041c0000 0x100>;
interrupts = <48 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&osc>;
+ clocks = <&clk CLK_UART4>, <&clk CLK_APB_UART4>;
+ clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
status = "disabled";
--
2.44.0


2024-03-09 09:04:04

by Inochi Amaoto

[permalink] [raw]
Subject: [PATCH v9 4/6] clk: sophgo: Add clock support for SG2000 SoC

Add init code for SG2000 SoC.

Signed-off-by: Inochi Amaoto <[email protected]>
Link: https://github.com/sophgo/sophgo-doc/releases/tag/sg2000-datasheet-v1.0-alpha
---
drivers/clk/sophgo/clk-cv1800.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/clk/sophgo/clk-cv1800.c b/drivers/clk/sophgo/clk-cv1800.c
index c7d4aa6c8343..956de5b21a80 100644
--- a/drivers/clk/sophgo/clk-cv1800.c
+++ b/drivers/clk/sophgo/clk-cv1800.c
@@ -1437,6 +1437,20 @@ static const struct cv1800_clk_desc cv1810_desc = {
.pre_init = cv1810_pre_init,
};

+static int sg2000_pre_init(struct device *dev, void __iomem *base,
+ struct cv1800_clk_ctrl *ctrl,
+ const struct cv1800_clk_desc *desc)
+{
+ cv18xx_clk_disable_auto_pd(base);
+
+ return 0;
+}
+
+static const struct cv1800_clk_desc sg2000_desc = {
+ .clks_data = &cv1810_hw_clks,
+ .pre_init = sg2000_pre_init,
+};
+
static int cv1800_clk_init_ctrl(struct device *dev, void __iomem *reg,
struct cv1800_clk_ctrl *ctrl,
const struct cv1800_clk_desc *desc)
@@ -1506,6 +1520,7 @@ static int cv1800_clk_probe(struct platform_device *pdev)
static const struct of_device_id cv1800_clk_ids[] = {
{ .compatible = "sophgo,cv1800-clk", .data = &cv1800_desc },
{ .compatible = "sophgo,cv1810-clk", .data = &cv1810_desc },
+ { .compatible = "sophgo,sg2000-clk", .data = &sg2000_desc },
{ }
};
MODULE_DEVICE_TABLE(of, cv1800_clk_ids);
--
2.44.0


2024-03-15 06:20:34

by Inochi Amaoto

[permalink] [raw]
Subject: Re: [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

Hi, Stephen,

Ping.

Do you still have some suggestion on this patch series?

On Sat, Mar 09, 2024 at 05:01:21PM +0800, Inochi Amaoto wrote:
> Add clock controller support for the Sophgo CV1800B, CV1812H and SG2000.
>
> Changed from v8:
> 1. improve code.
> 2. remove default config in Kconfig.
> 3. merge patch 2-4 of v8 into one.
>
> Changed from v7:
> 1. fix unused variables warnings in patch 3 of v7
> 2. fix wrong pointer type in patch 3 of v7
> 3. move the clk_disp_vip_parents variable to the patch 5 to avoid warning
>
> Changed from v6:
> 1. fix dead lock when setting rate.
> 2. split the driver patch into several patch for easy reading.
>
> Changed from v5:
> 1. rebased to mainline master tree
> 2. add SG2000 clock support.
> 3. fix document link
>
> Changed from v4:
> 1. improve code for patch 2
> 2. remove the already applied bindings
> https://lore.kernel.org/all/IA1PR20MB49535E448097F6FFC1218C39BB90A@IA1PR20MB4953.namprd20.prod.outlook.com/
>
> Changed from v3:
> 1. improve comment of patch 3
> 2. cleanup the include of patch 2
>
> Changed from v2:
> 1. remove clock-names from bindings.
> 2. remove clock-frequency node of DT from previous patch.
> 3. change some unused clock to bypass mode to avoid unlockable PLL.
>
> Changed from v1:
> 1. fix license issues.
>
> Inochi Amaoto (6):
> dt-bindings: clock: sophgo: Add clock controller of SG2000 series SoC
> clk: sophgo: Add clock support for CV1800 SoC
> clk: sophgo: Add clock support for CV1810 SoC
> clk: sophgo: Add clock support for SG2000 SoC
> riscv: dts: sophgo: add clock generator for Sophgo CV1800 series SoC
> riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC
>
> .../bindings/clock/sophgo,cv1800-clk.yaml | 3 +-
> arch/riscv/boot/dts/sophgo/cv1800b.dtsi | 4 +
> arch/riscv/boot/dts/sophgo/cv1812h.dtsi | 4 +
> arch/riscv/boot/dts/sophgo/cv18xx.dtsi | 22 +-
> drivers/clk/Kconfig | 1 +
> drivers/clk/Makefile | 1 +
> drivers/clk/sophgo/Kconfig | 11 +
> drivers/clk/sophgo/Makefile | 7 +
> drivers/clk/sophgo/clk-cv1800.c | 1537 +++++++++++++++++
> drivers/clk/sophgo/clk-cv1800.h | 123 ++
> drivers/clk/sophgo/clk-cv18xx-common.c | 66 +
> drivers/clk/sophgo/clk-cv18xx-common.h | 81 +
> drivers/clk/sophgo/clk-cv18xx-ip.c | 887 ++++++++++
> drivers/clk/sophgo/clk-cv18xx-ip.h | 261 +++
> drivers/clk/sophgo/clk-cv18xx-pll.c | 420 +++++
> drivers/clk/sophgo/clk-cv18xx-pll.h | 118 ++
> 16 files changed, 3540 insertions(+), 6 deletions(-)
> create mode 100644 drivers/clk/sophgo/Kconfig
> create mode 100644 drivers/clk/sophgo/Makefile
> create mode 100644 drivers/clk/sophgo/clk-cv1800.c
> create mode 100644 drivers/clk/sophgo/clk-cv1800.h
> create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.c
> create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.h
> create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.c
> create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.h
> create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.c
> create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.h
>
> --
> 2.44.0
>

2024-04-11 07:10:13

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v9 1/6] dt-bindings: clock: sophgo: Add clock controller of SG2000 series SoC

Quoting Inochi Amaoto (2024-03-09 01:02:51)
> SG2000 series SoC has the same clock as CV1810 series, but the clock
> related to A53 is functional in SG2000 series. So a new compatible
> string is needed for the new SoC.
>
> Add definition for the clock controller of the SG2000 series SoC.
>
> Link: https://github.com/sophgo/sophgo-doc/releases/tag/sg2000-datasheet-v1.0-alpha
> Acked-by: Krzysztof Kozlowski <[email protected]>
> Signed-off-by: Inochi Amaoto <[email protected]>
> ---

Applied to clk-next

2024-04-11 07:10:41

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v9 3/6] clk: sophgo: Add clock support for CV1810 SoC

Quoting Inochi Amaoto (2024-03-09 01:02:53)
> Add clock definition and init code for CV1810 SoC.
>
> Signed-off-by: Inochi Amaoto <[email protected]>
> Link: https://github.com/milkv-duo/duo-files/blob/6f4e9b8ecb459e017cca1a8df248a19ca70837a3/duo/datasheet/CV180X-Clock-v1.xlsx
> ---

Applied to clk-next

2024-04-11 07:10:54

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v9 4/6] clk: sophgo: Add clock support for SG2000 SoC

Quoting Inochi Amaoto (2024-03-09 01:02:54)
> Add init code for SG2000 SoC.
>
> Signed-off-by: Inochi Amaoto <[email protected]>
> Link: https://github.com/sophgo/sophgo-doc/releases/tag/sg2000-datasheet-v1.0-alpha
> ---

Applied to clk-next

2024-04-11 07:40:13

by Inochi Amaoto

[permalink] [raw]
Subject: Re: (subset) [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

On Sat, 9 Mar 2024 17:01:21 +0800, Inochi Amaoto wrote:
> Add clock controller support for the Sophgo CV1800B, CV1812H and SG2000.
>
> Changed from v8:
> 1. improve code.
> 2. remove default config in Kconfig.
> 3. merge patch 2-4 of v8 into one.
>
> [...]

Applied to sophgo/for-next, thanks!

[5/6] riscv: dts: sophgo: add clock generator for Sophgo CV1800 series SoC
https://github.com/sophgo/linux/commit/bb7b3419627eb34f3466022d1f4b3c942c09712d
[6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC
https://github.com/sophgo/linux/commit/18e8c6d2cced6c57d62813f49b57eeb8ee02f984

Thanks,
Inochi


2024-04-14 05:57:00

by Michael Opdenacker

[permalink] [raw]
Subject: Re: (subset) [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

Hi Inochi

On 4/11/24 at 09:38, Inochi Amaoto wrote:
> On Sat, 9 Mar 2024 17:01:21 +0800, Inochi Amaoto wrote:
>> Add clock controller support for the Sophgo CV1800B, CV1812H and SG2000.
>>
>> Changed from v8:
>> 1. improve code.
>> 2. remove default config in Kconfig.
>> 3. merge patch 2-4 of v8 into one.
>>
>> [...]
>
> Applied to sophgo/for-next, thanks!
>
> [5/6] riscv: dts: sophgo: add clock generator for Sophgo CV1800 series SoC
> https://github.com/sophgo/linux/commit/bb7b3419627eb34f3466022d1f4b3c942c09712d
> [6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC
> https://github.com/sophgo/linux/commit/18e8c6d2cced6c57d62813f49b57eeb8ee02f984

Oops, for your information, this last change
([6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC)
breaks my Milk-V Duo S board when I boot it with cv1812h-huashan-pi.dtb
as I believe you suggested.

I don't know whether the board actually boots, but at least I don't get
any more output in the console.

Has someone tested this on the real Huashan Pi board?

Cheers
Michael.


2024-04-14 06:41:15

by Inochi Amaoto

[permalink] [raw]
Subject: Re: (subset) [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

On Sun, Apr 14, 2024 at 07:56:42AM +0200, Michael Opdenacker wrote:
> Hi Inochi
>
> On 4/11/24 at 09:38, Inochi Amaoto wrote:
> > On Sat, 9 Mar 2024 17:01:21 +0800, Inochi Amaoto wrote:
> > > Add clock controller support for the Sophgo CV1800B, CV1812H and SG2000.
> > >
> > > Changed from v8:
> > > 1. improve code.
> > > 2. remove default config in Kconfig.
> > > 3. merge patch 2-4 of v8 into one.
> > >
> > > [...]
> >
> > Applied to sophgo/for-next, thanks!
> >
> > [5/6] riscv: dts: sophgo: add clock generator for Sophgo CV1800 series SoC
> > https://github.com/sophgo/linux/commit/bb7b3419627eb34f3466022d1f4b3c942c09712d
> > [6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC
> > https://github.com/sophgo/linux/commit/18e8c6d2cced6c57d62813f49b57eeb8ee02f984
>
> Oops, for your information, this last change
> ([6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC)
> breaks my Milk-V Duo S board when I boot it with cv1812h-huashan-pi.dtb as I
> believe you suggested.
>
> I don't know whether the board actually boots, but at least I don't get any
> more output in the console.
>
> Has someone tested this on the real Huashan Pi board?
>
> Cheers
> Michael.
>

Hi Michael,

I think you boot with the `defconfig` config. This config does
not enable the CV1800 clk support. I think you may need to apply
[1], which enable the clk driver. Also if you want to test sdhci
with real clk, you can try [2]. I test them on the Milk-V Duo S.

Regards,
Inochi

[1] https://lore.kernel.org/all/IA1PR20MB4953CA5D46EA8913B130D502BB052@IA1PR20MB4953.namprd20.prod.outlook.com/
[2] https://lore.kernel.org/all/IA1PR20MB4953CA5D46EA8913B130D502BB052@IA1PR20MB4953.namprd20.prod.outlook.com/

2024-04-14 06:52:13

by Inochi Amaoto

[permalink] [raw]
Subject: Re: (subset) [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

On Sun, Apr 14, 2024 at 02:41:21PM +0800, Inochi Amaoto wrote:
> On Sun, Apr 14, 2024 at 07:56:42AM +0200, Michael Opdenacker wrote:
> > Hi Inochi
> >
> > On 4/11/24 at 09:38, Inochi Amaoto wrote:
> > > On Sat, 9 Mar 2024 17:01:21 +0800, Inochi Amaoto wrote:
> > > > Add clock controller support for the Sophgo CV1800B, CV1812H and SG2000.
> > > >
> > > > Changed from v8:
> > > > 1. improve code.
> > > > 2. remove default config in Kconfig.
> > > > 3. merge patch 2-4 of v8 into one.
> > > >
> > > > [...]
> > >
> > > Applied to sophgo/for-next, thanks!
> > >
> > > [5/6] riscv: dts: sophgo: add clock generator for Sophgo CV1800 series SoC
> > > https://github.com/sophgo/linux/commit/bb7b3419627eb34f3466022d1f4b3c942c09712d
> > > [6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC
> > > https://github.com/sophgo/linux/commit/18e8c6d2cced6c57d62813f49b57eeb8ee02f984
> >
> > Oops, for your information, this last change
> > ([6/6] riscv: dts: sophgo: add uart clock for Sophgo CV1800 series SoC)
> > breaks my Milk-V Duo S board when I boot it with cv1812h-huashan-pi.dtb as I
> > believe you suggested.
> >
> > I don't know whether the board actually boots, but at least I don't get any
> > more output in the console.
> >
> > Has someone tested this on the real Huashan Pi board?
> >
> > Cheers
> > Michael.
> >
>
> Hi Michael,
>
> I think you boot with the `defconfig` config. This config does
> not enable the CV1800 clk support. I think you may need to apply
> [1], which enable the clk driver. Also if you want to test sdhci
> with real clk, you can try [2]. I test them on the Milk-V Duo S.
>
> Regards,
> Inochi
>
> [1] https://lore.kernel.org/all/IA1PR20MB4953CA5D46EA8913B130D502BB052@IA1PR20MB4953.namprd20.prod.outlook.com/

I make a mistake when reordering the url.
[1] should be https://lore.kernel.org/all/IA1PR20MB49537E8B2D1FAAA7D5B8BDA2BB052@IA1PR20MB4953.namprd20.prod.outlook.com/

> [2] https://lore.kernel.org/all/IA1PR20MB4953CA5D46EA8913B130D502BB052@IA1PR20MB4953.namprd20.prod.outlook.com/

2024-04-14 09:11:47

by Michael Opdenacker

[permalink] [raw]
Subject: Re: (subset) [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

Hi Inochi

Thanks for your help!

On 4/14/24 at 08:43, Inochi Amaoto wrote:
>> Hi Michael,
>>
>> I think you boot with the `defconfig` config. This config does
>> not enable the CV1800 clk support. I think you may need to apply
>> [1], which enable the clk driver. Also if you want to test sdhci
>> with real clk, you can try [2]. I test them on the Milk-V Duo S.
>>
>> Regards,
>> Inochi
>>
>> [1] https://lore.kernel.org/all/IA1PR20MB4953CA5D46EA8913B130D502BB052@IA1PR20MB4953.namprd20.prod.outlook.com/
> I make a mistake when reordering the url.
> [1] should be https://lore.kernel.org/all/IA1PR20MB49537E8B2D1FAAA7D5B8BDA2BB052@IA1PR20MB4953.namprd20.prod.outlook.com/


Indeed, I was using the "defconfig" config. Too used to having
everything working with this configuration on RISC-V :-/

Gotcha for the CONFIG_CLK_SOPHGO_CV1800 setting. However, I'm confused.
Why is this setting available in linux-next, but neither in
git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git ("master" and
"for-next") nor in github.com/sophgo/linux.git ("master" and "for-next") ?

What's the tree used to push to linux-next?

Cheers
Michael.

--
Michael Opdenacker, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


2024-04-14 09:41:13

by Inochi Amaoto

[permalink] [raw]
Subject: Re: (subset) [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

On Sun, Apr 14, 2024 at 11:11:22AM +0200, Michael Opdenacker wrote:
> Hi Inochi
>
> Thanks for your help!
>
> On 4/14/24 at 08:43, Inochi Amaoto wrote:
> > > Hi Michael,
> > >
> > > I think you boot with the `defconfig` config. This config does
> > > not enable the CV1800 clk support. I think you may need to apply
> > > [1], which enable the clk driver. Also if you want to test sdhci
> > > with real clk, you can try [2]. I test them on the Milk-V Duo S.
> > >
> > > Regards,
> > > Inochi
> > >
> > > [1] https://lore.kernel.org/all/IA1PR20MB4953CA5D46EA8913B130D502BB052@IA1PR20MB4953.namprd20.prod.outlook.com/
> > I make a mistake when reordering the url.
> > [1] should be https://lore.kernel.org/all/IA1PR20MB49537E8B2D1FAAA7D5B8BDA2BB052@IA1PR20MB4953.namprd20.prod.outlook.com/
>
>
> Indeed, I was using the "defconfig" config. Too used to having everything
> working with this configuration on RISC-V :-/
>

I apologize for this mistake. I just forgot this after removing
default value in the Kconfig.

> Gotcha for the CONFIG_CLK_SOPHGO_CV1800 setting. However, I'm confused. Why
> is this setting available in linux-next, but neither in
> git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git ("master" and
> "for-next") nor in github.com/sophgo/linux.git ("master" and "for-next") ?

At now, we only maintain the dts related change, but not the driver
code. The driver code is usually applied by the subsystem maintainer.
This is why you only see the clk changes in the linux-next.

>
> What's the tree used to push to linux-next?
>

As you see, the driver code applied to the clk/for-next. That is the
tree used to push.

2024-04-15 07:44:50

by Michael Opdenacker

[permalink] [raw]
Subject: Re: (subset) [PATCH v9 0/6] riscv: sophgo: add clock support for Sophgo CV1800/SG2000 SoCs

Hi Inochi

On 4/14/24 at 11:41, Inochi Amaoto wrote:
>> Gotcha for the CONFIG_CLK_SOPHGO_CV1800 setting. However, I'm confused. Why
>> is this setting available in linux-next, but neither in
>> git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git ("master" and
>> "for-next") nor in github.com/sophgo/linux.git ("master" and "for-next") ?
> At now, we only maintain the dts related change, but not the driver
> code. The driver code is usually applied by the subsystem maintainer.
> This is why you only see the clk changes in the linux-next.
>
>> What's the tree used to push to linux-next?
>>
> As you see, the driver code applied to the clk/for-next. That is the
> tree used to push.

It makes perfect sense, thanks!

So, I tried to boot my Milk-V Duo S board with the latest linux-next
kernel (with CONFIG_CLK_SOPHGO_CV1800=y). Here's what I'm getting:

[ 1.933301] mmc0: SDHCI controller on 4310000.mmc [4310000.mmc] using ADMA 64-bit
[ 1.947274] Legacy PMU implementation is available
[ 1.954001] clk: Disabling unused clocks
[ 1.959283] PM: genpd: Disabling unused power domains
[ 1.964840] ALSA device list:
[ 1.968165] No soundcards found.
[ 1.975652] dw-apb-uart 4140000.serial: forbid DMA for kernel console
[ 2.133075] mmc0: Internal clock never stabilised.
[ 2.138256] mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
[ 2.145082] mmc0: sdhci: Sys addr: 0x00000000 | Version: 0x00000000
[ 2.151906] mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
[ 2.158725] mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
[ 2.165543] mmc0: sdhci: Present: 0x00000000 | Host ctl: 0x00000000
[ 2.172362] mmc0: sdhci: Power: 0x00000000 | Blk gap: 0x00000000
[ 2.179220] mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x00000000
[ 2.186056] mmc0: sdhci: Timeout: 0x00000000 | Int stat: 0x00000000
[ 2.192878] mmc0: sdhci: Int enab: 0x00000000 | Sig enab: 0x00000000
[ 2.199699] mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
[ 2.206520] mmc0: sdhci: Caps: 0x00000000 | Caps_1: 0x00000000
[ 2.213342] mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x00000000
[ 2.220162] mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
[ 2.226983] mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
[ 2.233798] mmc0: sdhci: Host ctl2: 0x00000000
[ 2.238558] mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x0000000000000000
[ 2.246189] mmc0: sdhci: ============================================
[ 2.256290] /dev/root: Can't open blockdev
[ 2.261025] VFS: Cannot open root device "/dev/mmcblk0p2" or unknown-block(0,0): error -6

Here's my device tree (compiled with the sophgo for-next) branch:

/dts-v1/;

#include "cv1812h.dtsi"

/ {
model = "Milk-V Duo S";
compatible = "milkv,duos", "sophgo,cv1812h";

aliases {
serial0 = &uart0;
};

chosen {
stdout-path = "serial0:115200n8";
};

memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
};

&osc {
clock-frequency = <25000000>;
};

&sdhci0 {
status = "okay";
bus-width = <4>;
no-mmc;
no-sdio;
disable-wp;
};

&uart0 {
status = "okay";
};


What's frustrating is that I got the mmc working a few days back :-)
Hoping this helps...
Cheers
Michael.

--
Michael Opdenacker, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com