@@ -81,3 +81,11 @@ config CLK_STARFIVE_JH8100_SYS
default ARCH_STARFIVE
help
Say yes here to support the System clock controller on the StarFive JH8100 SoC.
+
+config CLK_STARFIVE_JH8100_SYS_NW
+ bool "StarFive JH8100 System-North-West clock support"
+ depends on CLK_STARFIVE_JH8100_SYS
+ default ARCH_STARFIVE
+ help
+ Say yes here to support the System-North-West clock controller on the StarFive JH8100
+ SoC.
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
# StarFive JH8100 Clock
obj-$(CONFIG_CLK_STARFIVE_JH8100_SYS) += clk-sys.o
+obj-$(CONFIG_CLK_STARFIVE_JH8100_SYS_NW) += clk-sys-nw.o
new file mode 100644
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * StarFive JH8100 System Clock Driver
+ *
+ * Copyright (C) 2023 StarFive Technology Co., Ltd.
+ *
+ * Author: Jee Heng Sia <jeeheng.sia@starfivetech.com>
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/starfive,jh8100-crg.h>
+
+#include "clk-starfive-jh8100.h"
+
+/* external clocks */
+#define SYSCRG_NW_CLK_OSC (SYSCRG_NW_CLK_END + 0)
+#define SYSCRG_NW_CLK_APB_BUS (SYSCRG_NW_CLK_END + 1)
+#define SYSCRG_NW_CLK_APB_BUS_PER4 (SYSCRG_NW_CLK_END + 2)
+#define SYSCRG_NW_CLK_SPI_CORE_100 (SYSCRG_NW_CLK_END + 3)
+#define SYSCRG_NW_CLK_ISP_2X (SYSCRG_NW_CLK_END + 4)
+#define SYSCRG_NW_CLK_ISP__AXI (SYSCRG_NW_CLK_END + 5)
+#define SYSCRG_NW_CLK_VOUT_ROOT0 (SYSCRG_NW_CLK_END + 6)
+#define SYSCRG_NW_CLK_VOUT_ROOT1 (SYSCRG_NW_CLK_END + 7)
+#define SYSCRG_NW_CLK_VOUT_SCAN__ATS (SYSCRG_NW_CLK_END + 8)
+#define SYSCRG_NW_CLK_VOUT_DC__CORE (SYSCRG_NW_CLK_END + 9)
+#define SYSCRG_NW_CLK_VOUT__AXI (SYSCRG_NW_CLK_END + 10)
+#define SYSCRG_NW_CLK_AXI_400 (SYSCRG_NW_CLK_END + 11)
+#define SYSCRG_NW_CLK_DVP_EXT (SYSCRG_NW_CLK_END + 12)
+#define SYSCRG_NW_CLK_ISP_DPHY_TAP_TCK_EXT (SYSCRG_NW_CLK_END + 13)
+#define SYSCRG_NW_CLK_GLB_EXT (SYSCRG_NW_CLK_END + 14)
+#define SYSCRG_NW_CLK_VOUT_MIPI_DPHY_TAP_TCK_EXT (SYSCRG_NW_CLK_END + 15)
+#define SYSCRG_NW_CLK_VOUT_EDP_TAP_TCK_EXT (SYSCRG_NW_CLK_END + 16)
+#define SYSCRG_NW_CLK_SPI_IN2_EXT (SYSCRG_NW_CLK_END + 17)
+#define SYSCRG_NW_CLK_PERH_ROOT_PREOSC (SYSCRG_NW_CLK_END + 18)
+#define SYSCRG_NW_CLK_AHB_VOUT (SYSCRG_NW_CLK_END + 19)
+#define SYSCRG_NW_CLK_PLL5_OUT (SYSCRG_NW_CLK_END + 20)
+
+static const struct starfive_clk_data jh8100_syscrg_nw_clk_data[] = {
+ /* root */
+ STARFIVE__DIV(SYSCRG_NW_CLK_PLL5_DIV2, "sys_nw_clk_pll5_div2", 2,
+ SYSCRG_NW_CLK_PLL5_OUT),
+ STARFIVE_GDIV(SYSCRG_NW_CLK_GCLK5, "sys_nw_clk_gclk5", CLK_IS_CRITICAL, 120,
+ SYSCRG_NW_CLK_PLL5_DIV2),
+ /* gpio */
+ STARFIVE_GATE(SYSCRG_NW_CLK_GPIO_100, "sys_nw_clk_gpio_100",
+ CLK_IS_CRITICAL, SYSCRG_NW_CLK_PLL5_OUT),
+ STARFIVE_GATE(SYSCRG_NW_CLK_GPIO_50, "sys_nw_clk_gpio_50",
+ CLK_IS_CRITICAL, SYSCRG_NW_CLK_PLL5_OUT),
+ STARFIVE_GATE(SYSCRG_NW_CLK_GPIO_150, "sys_nw_clk_gpio_150",
+ CLK_IS_CRITICAL, SYSCRG_NW_CLK_PLL5_OUT),
+ STARFIVE_GDIV(SYSCRG_NW_CLK_GPIO_60, "sys_nw_clk_gpio_60", CLK_IS_CRITICAL, 30,
+ SYSCRG_NW_CLK_PLL5_OUT),
+ /* iomux */
+ STARFIVE_GATE(SYSCRG_NW_CLK_IOMUX_WEST_PCLK, "sys_nw_clk_iomux_west_pclk", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ /* i2c */
+ STARFIVE_GATE(SYSCRG_NW_CLK_I2C6_APB, "sys_nw_clk_i2c6_apb", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GATE(SYSCRG_NW_CLK_I2C7_APB, "sys_nw_clk_i2c7_apb", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ /* spi */
+ STARFIVE_GATE(SYSCRG_NW_CLK_SPI2_APB, "sys_nw_clk_spi2_apb", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GATE(SYSCRG_NW_CLK_SPI2_CORE, "sys_nw_clk_spi2_core", 0,
+ SYSCRG_NW_CLK_SPI_CORE_100),
+ STARFIVE__MUX(SYSCRG_NW_CLK_SPI2_SCLK_IN, "sys_nw_clk_spi2_sclk_in", 2,
+ SYSCRG_NW_CLK_SPI_IN2_EXT, SYSCRG_NW_CLK_GPIO_100),
+ /* smbus */
+ STARFIVE_GATE(SYSCRG_NW_CLK_SMBUS1_APB, "sys_nw_clk_smbus1_apb", CLK_IGNORE_UNUSED,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GDIV(SYSCRG_NW_CLK_SMBUS1_CORE, "sys_nw_clk_smbus1_core", CLK_IGNORE_UNUSED, 120,
+ SYSCRG_NW_CLK_PERH_ROOT_PREOSC),
+ /* isp */
+ STARFIVE__MUX(SYSCRG_NW_CLK_ISP_DVP, "sys_nw_clk_isp_dvp", 2,
+ SYSCRG_NW_CLK_DVP_EXT, SYSCRG_NW_CLK_GPIO_150),
+ STARFIVE_GATE(SYSCRG_NW_CLK_ISP_CORE_2X, "sys_nw_clk_isp_core_2x", 0,
+ SYSCRG_NW_CLK_ISP_2X),
+ STARFIVE_GATE(SYSCRG_NW_CLK_ISP_AXI, "sys_nw_clk_isp_axi", 0,
+ SYSCRG_NW_CLK_ISP__AXI),
+ STARFIVE__MUX(SYSCRG_NW_CLK_ISP_DPHY_TAP_TCK, "sys_nw_clk_isp_dphy_tap_tck", 2,
+ SYSCRG_NW_CLK_ISP_DPHY_TAP_TCK_EXT, SYSCRG_NW_CLK_GLB_EXT),
+ STARFIVE_GATE(SYSCRG_NW_CLK_FLEXNOC_ISPSLV, "sys_nw_clk_flexnoc_ispslv", 0,
+ SYSCRG_NW_CLK_ISP__AXI),
+ /* vout */
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_PIX0, "sys_nw_clk_vout_pix0", CLK_IGNORE_UNUSED,
+ SYSCRG_NW_CLK_VOUT_ROOT0),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_PIX1, "sys_nw_clk_vout_pix1", CLK_IGNORE_UNUSED,
+ SYSCRG_NW_CLK_VOUT_ROOT1),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_SCAN_ATS, "sys_nw_clk_vout_scan_ats",
+ CLK_IGNORE_UNUSED, SYSCRG_NW_CLK_VOUT_SCAN__ATS),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_DC_CORE, "sys_nw_clk_vout_dc_core",
+ CLK_IGNORE_UNUSED, SYSCRG_NW_CLK_VOUT_DC__CORE),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_APB, "sys_nw_clk_vout_apb", CLK_IGNORE_UNUSED,
+ SYSCRG_NW_CLK_APB_BUS),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_DSI, "sys_nw_clk_vout_dsi", CLK_IGNORE_UNUSED,
+ SYSCRG_NW_CLK_AXI_400),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_AHB, "sys_nw_clk_vout_ahb", CLK_IGNORE_UNUSED,
+ SYSCRG_NW_CLK_AHB_VOUT),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_AXI, "sys_nw_clk_vout_axi", CLK_IGNORE_UNUSED,
+ SYSCRG_NW_CLK_VOUT__AXI),
+ STARFIVE__MUX(SYSCRG_NW_CLK_VOUT_MIPI_DPHY_TAP_TCK,
+ "sys_nw_clk_vout_mipi_dphy_tap_tck", 2,
+ SYSCRG_NW_CLK_VOUT_MIPI_DPHY_TAP_TCK_EXT,
+ SYSCRG_NW_CLK_GLB_EXT),
+ STARFIVE__MUX(SYSCRG_NW_CLK_VOUT_EDP_PHY_TAP_TCK,
+ "sys_nw_clk_vout_edp_phy_tap_tck", 2,
+ SYSCRG_NW_CLK_VOUT_EDP_TAP_TCK_EXT, SYSCRG_NW_CLK_GLB_EXT),
+ /* uart */
+ STARFIVE__DIV(SYSCRG_NW_CLK_UART5_CORE_PREOSC, "sys_nw_clk_uart5_core_preosc",
+ 131071, SYSCRG_NW_CLK_PERH_ROOT_PREOSC),
+ STARFIVE_GATE(SYSCRG_NW_CLK_UART5_APB, "sys_nw_clk_uart5_apb", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GMUX(SYSCRG_NW_CLK_UART5_CORE, "sys_nw_clk_uart5_core", 0, 2,
+ SYSCRG_NW_CLK_OSC, SYSCRG_NW_CLK_UART5_CORE_PREOSC),
+ STARFIVE__DIV(SYSCRG_NW_CLK_UART6_CORE_PREOSC, "sys_nw_clk_uart6_core_preosc",
+ 131071, SYSCRG_NW_CLK_PERH_ROOT_PREOSC),
+ STARFIVE_GATE(SYSCRG_NW_CLK_UART6_APB, "sys_nw_clk_uart6_apb", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GMUX(SYSCRG_NW_CLK_UART6_CORE, "sys_nw_clk_uart6_core", 0, 2,
+ SYSCRG_NW_CLK_OSC, SYSCRG_NW_CLK_UART6_CORE_PREOSC),
+ /* icg_en */
+ STARFIVE_GATE(SYSCRG_NW_CLK_SPI2_ICG_EN, "sys_nw_clk_spi2_en", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GATE(SYSCRG_NW_CLK_SMBUS1_ICG_EN, "sys_nw_clk_smbus1_en", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GATE(SYSCRG_NW_CLK_ISP_ICG_EN, "sys_nw_clk_isp_en", 0,
+ SYSCRG_NW_CLK_ISP__AXI),
+ STARFIVE_GATE(SYSCRG_NW_CLK_VOUT_ICG_EN, "sys_nw_clk_vout_en", 0,
+ SYSCRG_NW_CLK_VOUT_ROOT0),
+ STARFIVE_GATE(SYSCRG_NW_CLK_UART5_ICG_EN, "sys_nw_clk_uart5_en", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+ STARFIVE_GATE(SYSCRG_NW_CLK_UART6_ICG_EN, "sys_nw_clk_uart6_en", 0,
+ SYSCRG_NW_CLK_APB_BUS_PER4),
+};
+
+struct clk_hw *jh8100_syscrg_nw_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct starfive_clk_priv *priv = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx < SYSCRG_NW_CLK_END)
+ return &priv->reg[idx].hw;
+
+ return ERR_PTR(-EINVAL);
+}
+
+static int jh8100_syscrg_nw_probe(struct platform_device *pdev)
+{
+ struct starfive_clk_priv *priv;
+ unsigned int idx;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev,
+ struct_size(priv, reg, SYSCRG_NW_CLK_END),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->rmw_lock);
+ priv->dev = &pdev->dev;
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ /* 24MHz -> 1500.0MHz */
+ priv->pll[0] = devm_clk_hw_register_fixed_factor(priv->dev, "clk_pll5_out",
+ "clk_osc", 0, 125, 2);
+ if (IS_ERR(priv->pll[0]))
+ return PTR_ERR(priv->pll[0]);
+
+ for (idx = 0; idx < SYSCRG_NW_CLK_END; idx++) {
+ u32 max = jh8100_syscrg_nw_clk_data[idx].max;
+ struct clk_parent_data parents[4] = {};
+ struct clk_init_data init = {
+ .name = jh8100_syscrg_nw_clk_data[idx].name,
+ .ops = starfive_clk_ops(max),
+ .parent_data = parents,
+ .num_parents =
+ ((max & STARFIVE_CLK_MUX_MASK) >> STARFIVE_CLK_MUX_SHIFT) + 1,
+ .flags = jh8100_syscrg_nw_clk_data[idx].flags,
+ };
+ struct starfive_clk *clk = &priv->reg[idx];
+ unsigned int i;
+
+ for (i = 0; i < init.num_parents; i++) {
+ unsigned int pidx = jh8100_syscrg_nw_clk_data[idx].parents[i];
+
+ if (pidx < SYSCRG_NW_CLK_END)
+ parents[i].hw = &priv->reg[pidx].hw;
+ else if (pidx == SYSCRG_NW_CLK_OSC)
+ parents[i].fw_name = "clk_osc";
+ else if (pidx == SYSCRG_NW_CLK_APB_BUS)
+ parents[i].fw_name = "sys_clk_apb_bus";
+ else if (pidx == SYSCRG_NW_CLK_APB_BUS_PER4)
+ parents[i].fw_name = "sys_clk_apb_bus_per4";
+ else if (pidx == SYSCRG_NW_CLK_SPI_CORE_100)
+ parents[i].fw_name = "sys_clk_spi_core_100";
+ else if (pidx == SYSCRG_NW_CLK_ISP_2X)
+ parents[i].fw_name = "sys_clk_isp_2x";
+ else if (pidx == SYSCRG_NW_CLK_ISP__AXI)
+ parents[i].fw_name = "sys_clk_isp_axi";
+ else if (pidx == SYSCRG_NW_CLK_VOUT_ROOT0)
+ parents[i].fw_name = "sys_clk_vout_root0";
+ else if (pidx == SYSCRG_NW_CLK_VOUT_ROOT1)
+ parents[i].fw_name = "sys_clk_vout_root1";
+ else if (pidx == SYSCRG_NW_CLK_VOUT_SCAN__ATS)
+ parents[i].fw_name = "sys_clk_vout_scan_ats";
+ else if (pidx == SYSCRG_NW_CLK_VOUT_DC__CORE)
+ parents[i].fw_name = "sys_clk_vout_dc_core";
+ else if (pidx == SYSCRG_NW_CLK_VOUT__AXI)
+ parents[i].fw_name = "sys_clk_vout_axi";
+ else if (pidx == SYSCRG_NW_CLK_AXI_400)
+ parents[i].fw_name = "sys_clk_axi_400";
+ else if (pidx == SYSCRG_NW_CLK_DVP_EXT)
+ parents[i].fw_name = "clk_dvp_ext";
+ else if (pidx == SYSCRG_NW_CLK_ISP_DPHY_TAP_TCK_EXT)
+ parents[i].fw_name = "clk_isp_dphy_tap_tck_ext";
+ else if (pidx == SYSCRG_NW_CLK_GLB_EXT)
+ parents[i].fw_name = "clk_glb_ext_clk";
+ else if (pidx == SYSCRG_NW_CLK_VOUT_MIPI_DPHY_TAP_TCK_EXT)
+ parents[i].fw_name = "clk_vout_mipi_dphy_tap_tck_ext";
+ else if (pidx == SYSCRG_NW_CLK_VOUT_EDP_TAP_TCK_EXT)
+ parents[i].fw_name = "clk_vout_edp_tap_tck_ext";
+ else if (pidx == SYSCRG_NW_CLK_SPI_IN2_EXT)
+ parents[i].fw_name = "clk_spi_in2_ext";
+ else if (pidx == SYSCRG_NW_CLK_PERH_ROOT_PREOSC)
+ parents[i].fw_name = "sys_clk_perh_root_preosc";
+ else if (pidx == SYSCRG_NW_CLK_AHB_VOUT)
+ parents[i].fw_name = "sys_clk_ahb_vout";
+ else
+ parents[i].hw = priv->pll[pidx - SYSCRG_NW_CLK_PLL5_OUT];
+ }
+
+ clk->hw.init = &init;
+ clk->idx = idx;
+ clk->max_div = max & STARFIVE_CLK_DIV_MASK;
+
+ ret = devm_clk_hw_register(&pdev->dev, &clk->hw);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev, jh8100_syscrg_nw_clk_get, priv);
+ if (ret)
+ return ret;
+
+ return jh8100_reset_controller_register(priv, "rst-sys-nw", 1);
+}
+
+static const struct of_device_id jh8100_syscrg_nw_match[] = {
+ { .compatible = "starfive,jh8100-syscrg-nw" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver jh8100_syscrg_nw_driver = {
+ .driver = {
+ .name = "clk-starfive-jh8100-sys-nw",
+ .of_match_table = jh8100_syscrg_nw_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver_probe(jh8100_syscrg_nw_driver, jh8100_syscrg_nw_probe);