diff options
author | Geert Uytterhoeven <geert@linux-m68k.org> | 2021-06-01 16:57:52 +0300 |
---|---|---|
committer | Emil Renner Berthing <kernel@esmil.dk> | 2021-09-12 15:35:01 +0300 |
commit | e83860029e5950115a3975eb189a37d2160df0a0 (patch) | |
tree | 69bb1ff7d5f623da27eff3d6935fa0bff474c3f7 | |
parent | 1af86dce73f9e38a340a1cffb8a7d7345c36d819 (diff) | |
download | linux-e83860029e5950115a3975eb189a37d2160df0a0.tar.xz |
[WIP] clk: starfive: Add preliminary JH7100 Clock Generator Driver
Add a preliminary driver for the StarFive JH7100 Clock Generator,
based on work by Ahmad Fatoum for Barebox.
Functional differences compared to the Barebox driver:
- Addition of starfive_clk_pll_mult(), to make the PLL outputs
reasonable,
- Add temporary overrides for critical clocks (uart, i2c, spi) until
we get the clock tree right, so we don't have to keep dummy clocks
like uartclk and hs_uartclk in the DTS.
This driver sets clk_ignore_unused, else the system locks up when all
clocks deemed unused are disabled.
To be updated when the documentation becomes available.
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
-rw-r--r-- | drivers/clk/Kconfig | 1 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/starfive/Kconfig | 9 | ||||
-rw-r--r-- | drivers/clk/starfive/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-starfive-jh7100.c | 583 |
5 files changed, 597 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index e80918be8e9c..61b243a14f42 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -397,6 +397,7 @@ source "drivers/clk/samsung/Kconfig" source "drivers/clk/sifive/Kconfig" source "drivers/clk/socfpga/Kconfig" source "drivers/clk/sprd/Kconfig" +source "drivers/clk/starfive/Kconfig" source "drivers/clk/sunxi/Kconfig" source "drivers/clk/sunxi-ng/Kconfig" source "drivers/clk/tegra/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5f06879d7fe9..c154596d1ab5 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -109,6 +109,7 @@ obj-y += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ +obj-$(CONFIG_SOC_STARFIVE_VIC7100) += starfive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_SUNXI_CCU) += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig new file mode 100644 index 000000000000..0e23c9a8a663 --- /dev/null +++ b/drivers/clk/starfive/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +config CLK_STARFIVE_JH7100 + bool "StarFive JH7100 clock support" + depends on SOC_STARFIVE_VIC7100 || COMPILE_TEST + default y if SOC_STARFIVE_VIC7100 + help + Say yes here to support the clock controller on the StarFive JH7100 + SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile new file mode 100644 index 000000000000..09759cc73530 --- /dev/null +++ b/drivers/clk/starfive/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# StarFive Clock +obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c new file mode 100644 index 000000000000..dd268982474a --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -0,0 +1,583 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7100 Clock Generator Driver + * + * Copyright 2021 Ahmad Fatoum, Pengutronix + * Copyright (C) 2021 Glider bv + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> + +#include <dt-bindings/clock/starfive-jh7100.h> + +#define STARFIVE_CLK_ENABLE_SHIFT 31 +#define STARFIVE_CLK_INVERT_SHIFT 30 +#define STARFIVE_CLK_MUX_SHIFT 24 + + +static const char *cpundbus_root_sels[4] = { + [0] = "osc_sys", + [1] = "pll0_out", + [2] = "pll1_out", + [3] = "pll2_out", +}; + +static const char *dla_root_sels[4] = { + [0] = "osc_sys", + [1] = "pll1_out", + [2] = "pll2_out", + [3] = "dummy", +}; + +static const char *dsp_root_sels[4] = { + [0] = "osc_sys", + [1] = "pll0_out", + [2] = "pll1_out", + [3] = "pll2_out", +}; + +static const char *gmacusb_root_sels[4] = { + [0] = "osc_sys", + [1] = "pll0_out", + [2] = "pll2_out", + [3] = "dummy", +}; + +static const char *perh0_root_sels[2] = { + [0] = "osc_sys", + [1] = "pll0_out", +}; + +static const char *perh1_root_sels[2] = { + [0] = "osc_sys", + [1] = "pll2_out", +}; + +static const char *vin_root_sels[4] = { + [0] = "osc_sys", + [1] = "pll1_out", + [2] = "pll2_out", + [3] = "dummy", +}; + +static const char *vout_root_sels[4] = { + [0] = "osc_aud", + [1] = "pll0_out", + [2] = "pll2_out", + [3] = "dummy", +}; + +static const char *cdechifi4_root_sels[4] = { + [0] = "osc_sys", + [1] = "pll1_out", + [2] = "pll2_out", + [3] = "dummy", +}; + +static const char *cdec_root_sels[4] = { + [0] = "osc_sys", + [1] = "pll0_out", + [2] = "pll1_out", + [3] = "dummy", +}; + +static const char *voutbus_root_sels[4] = { + [0] = "osc_aud", + [1] = "pll0_out", + [2] = "pll2_out", + [3] = "dummy", +}; + +static const char *pll2_refclk_sels[2] = { + [0] = "osc_sys", + [1] = "osc_aud", +}; + +static const char *ddrc0_sels[4] = { + [0] = "ddrosc_div2", + [1] = "ddrpll_div2", + [2] = "ddrpll_div4", + [3] = "ddrpll_div8", +}; + +static const char *ddrc1_sels[4] = { + [0] = "ddrosc_div2", + [1] = "ddrpll_div2", + [2] = "ddrpll_div4", + [3] = "ddrpll_div8", +}; + +static const char *nne_bus_sels[2] = { + [0] = "cpu_axi", + [1] = "nnebus_src1", +}; + +static const char *usbphy_25m_sels[2] = { + [0] = "osc_sys", + [1] = "usbphy_plldiv25m", +}; + +static const char *gmac_tx_sels[4] = { + [0] = "gmac_gtxclk", + [1] = "gmac_mii_txclk", + [2] = "gmac_rmii_txclk", + [3] = "dummy", +}; + +static const char *gmac_rx_pre_sels[2] = { + [0] = "gmac_gr_mii_rxclk", + [1] = "gmac_rmii_rxclk", +}; + +struct clk_starfive_jh7100_priv { + spinlock_t rmw_lock; + struct device *dev; + void __iomem *base; + struct clk_hw_onecell_data clk_hws; +}; + +/* assume osc_sys as direct parent for clocks of yet unknown lineage */ +#define UNKNOWN "osc_sys" + +static struct clk_hw * __init starfive_clk_underspecifid(struct clk_starfive_jh7100_priv *priv, + const char *name, + const char *parent) +{ + /* + * TODO With documentation available, all users of this functions can be + * migrated to one of the above or to a clk_fixed_factor with + * appropriate factor + */ + return devm_clk_hw_register_fixed_factor(priv->dev, name, parent, 0, 1, + 1); +} + +static struct clk_hw * __init starfive_clk_pll_mult(struct clk_starfive_jh7100_priv *priv, + const char *name, + const char *parent, + unsigned int mult) +{ + /* + * TODO With documentation available, all users of this functions can be + * migrated to one of the above or to a clk_fixed_factor with + * appropriate factor + */ + return devm_clk_hw_register_fixed_factor(priv->dev, name, parent, 0, + mult, 1); +} + +static struct clk_hw * __init starfive_clk_divider(struct clk_starfive_jh7100_priv *priv, + const char *name, + const char *parent, + unsigned int offset, + unsigned int width) +{ + return starfive_clk_underspecifid(priv, name, parent); +} + +static struct clk_hw * __init starfive_clk_gate(struct clk_starfive_jh7100_priv *priv, + const char *name, + const char *parent, + unsigned int offset) +{ + // FIXME devm + return clk_hw_register_gate(priv->dev, name, parent, + CLK_SET_RATE_PARENT, priv->base + offset, + STARFIVE_CLK_ENABLE_SHIFT, 0, + &priv->rmw_lock); +} + +static struct clk_hw * __init starfive_clk_gated_divider(struct clk_starfive_jh7100_priv *priv, + const char *name, + const char *parent, + unsigned int offset, + unsigned int width) +{ + /* TODO divider part */ + return starfive_clk_gate(priv, name, parent, offset); +} + +static struct clk_hw * __init starfive_clk_gate_dis(struct clk_starfive_jh7100_priv *priv, + const char *name, + const char *parent, + unsigned int offset) +{ + // FIXME devm + return clk_hw_register_gate(priv->dev, name, parent, + CLK_SET_RATE_PARENT, priv->base + offset, + STARFIVE_CLK_INVERT_SHIFT, + CLK_GATE_SET_TO_DISABLE, &priv->rmw_lock); +} + +static struct clk_hw * __init starfive_clk_mux(struct clk_starfive_jh7100_priv *priv, + const char *name, + unsigned int offset, + unsigned int width, + const char * const *parents, + unsigned int num_parents) +{ + return devm_clk_hw_register_mux(priv->dev, name, parents, num_parents, + 0, priv->base + offset, + STARFIVE_CLK_MUX_SHIFT, width, 0, + &priv->rmw_lock); +} + +static int __init starfive_clkgen_init(struct clk_starfive_jh7100_priv *priv) +{ + struct clk_hw **hws = priv->clk_hws.hws; + struct clk *osc_sys, *osc_aud; + + osc_sys = devm_clk_get(priv->dev, "osc_sys"); + if (IS_ERR(osc_sys)) + return PTR_ERR(osc_sys); + + osc_aud = devm_clk_get(priv->dev, "osc_aud"); + if (IS_ERR(osc_aud)) + return PTR_ERR(osc_aud); + + hws[JH7100_CLK_OSC_SYS] = __clk_get_hw(osc_sys); + hws[JH7100_CLK_OSC_AUD] = __clk_get_hw(osc_aud); + + hws[JH7100_CLK_PLL0_OUT] = starfive_clk_pll_mult(priv, "pll0_out", "osc_sys", 40); + hws[JH7100_CLK_PLL1_OUT] = starfive_clk_pll_mult(priv, "pll1_out", "osc_sys", 64); + hws[JH7100_CLK_PLL2_OUT] = starfive_clk_pll_mult(priv, "pll2_out", "pll2_refclk", 55); + hws[JH7100_CLK_CPUNDBUS_ROOT] = starfive_clk_mux(priv, "cpundbus_root", 0x0, 2, cpundbus_root_sels, ARRAY_SIZE(cpundbus_root_sels)); + hws[JH7100_CLK_DLA_ROOT] = starfive_clk_mux(priv, "dla_root", 0x4, 2, dla_root_sels, ARRAY_SIZE(dla_root_sels)); + hws[JH7100_CLK_DSP_ROOT] = starfive_clk_mux(priv, "dsp_root", 0x8, 2, dsp_root_sels, ARRAY_SIZE(dsp_root_sels)); + hws[JH7100_CLK_GMACUSB_ROOT] = starfive_clk_mux(priv, "gmacusb_root", 0xc, 2, gmacusb_root_sels, ARRAY_SIZE(gmacusb_root_sels)); + hws[JH7100_CLK_PERH0_ROOT] = starfive_clk_mux(priv, "perh0_root", 0x10, 1, perh0_root_sels, ARRAY_SIZE(perh0_root_sels)); + hws[JH7100_CLK_PERH1_ROOT] = starfive_clk_mux(priv, "perh1_root", 0x14, 1, perh1_root_sels, ARRAY_SIZE(perh1_root_sels)); + hws[JH7100_CLK_VIN_ROOT] = starfive_clk_mux(priv, "vin_root", 0x18, 2, vin_root_sels, ARRAY_SIZE(vin_root_sels)); + hws[JH7100_CLK_VOUT_ROOT] = starfive_clk_mux(priv, "vout_root", 0x1c, 2, vout_root_sels, ARRAY_SIZE(vout_root_sels)); + hws[JH7100_CLK_AUDIO_ROOT] = starfive_clk_gated_divider(priv, "audio_root", UNKNOWN, 0x20, 4); + hws[JH7100_CLK_CDECHIFI4_ROOT] = starfive_clk_mux(priv, "cdechifi4_root", 0x24, 2, cdechifi4_root_sels, ARRAY_SIZE(cdechifi4_root_sels)); + hws[JH7100_CLK_CDEC_ROOT] = starfive_clk_mux(priv, "cdec_root", 0x28, 2, cdec_root_sels, ARRAY_SIZE(cdec_root_sels)); + hws[JH7100_CLK_VOUTBUS_ROOT] = starfive_clk_mux(priv, "voutbus_root", 0x2c, 2, voutbus_root_sels, ARRAY_SIZE(voutbus_root_sels)); + hws[JH7100_CLK_CPUNBUS_ROOT_DIV] = starfive_clk_divider(priv, "cpunbus_root_div", "cpunbus_root", 0x30, 2); + hws[JH7100_CLK_DSP_ROOT_DIV] = starfive_clk_divider(priv, "dsp_root_div", "dsp_root", 0x34, 3); + hws[JH7100_CLK_PERH0_SRC] = starfive_clk_divider(priv, "perh0_src", "perh0_root", 0x38, 3); + hws[JH7100_CLK_PERH1_SRC] = starfive_clk_divider(priv, "perh1_src", "perh1_root", 0x3c, 3); + hws[JH7100_CLK_PLL0_TESTOUT] = starfive_clk_gated_divider(priv, "pll0_testout", "pll0_out", 0x40, 5); + hws[JH7100_CLK_PLL1_TESTOUT] = starfive_clk_gated_divider(priv, "pll1_testout", "pll1_out", 0x44, 5); + hws[JH7100_CLK_PLL2_TESTOUT] = starfive_clk_gated_divider(priv, "pll2_testout", "pll2_out", 0x48, 5); + hws[JH7100_CLK_PLL2_REF] = starfive_clk_mux(priv, "pll2_refclk", 0x4c, 1, pll2_refclk_sels, ARRAY_SIZE(pll2_refclk_sels)); + hws[JH7100_CLK_CPU_CORE] = starfive_clk_divider(priv, "cpu_core", UNKNOWN, 0x50, 4); + hws[JH7100_CLK_CPU_AXI] = starfive_clk_divider(priv, "cpu_axi", UNKNOWN, 0x54, 4); + hws[JH7100_CLK_AHB_BUS] = starfive_clk_divider(priv, "ahb_bus", UNKNOWN, 0x58, 4); + hws[JH7100_CLK_APB1_BUS] = starfive_clk_divider(priv, "apb1_bus", UNKNOWN, 0x5c, 4); + hws[JH7100_CLK_APB2_BUS] = starfive_clk_divider(priv, "apb2_bus", UNKNOWN, 0x60, 4); + hws[JH7100_CLK_DOM3AHB_BUS] = starfive_clk_gate(priv, "dom3ahb_bus", UNKNOWN, 0x64); + hws[JH7100_CLK_DOM7AHB_BUS] = starfive_clk_gate(priv, "dom7ahb_bus", UNKNOWN, 0x68); + hws[JH7100_CLK_U74_CORE0] = starfive_clk_gate(priv, "u74_core0", UNKNOWN, 0x6c); + hws[JH7100_CLK_U74_CORE1] = starfive_clk_gated_divider(priv, "u74_core1", UNKNOWN, 0x70, 4); + hws[JH7100_CLK_U74_AXI] = starfive_clk_gate(priv, "u74_axi", UNKNOWN, 0x74); + hws[JH7100_CLK_U74RTC_TOGGLE] = starfive_clk_gate(priv, "u74rtc_toggle", UNKNOWN, 0x78); + hws[JH7100_CLK_SGDMA2P_AXI] = starfive_clk_gate(priv, "sgdma2p_axi", UNKNOWN, 0x7c); + hws[JH7100_CLK_DMA2PNOC_AXI] = starfive_clk_gate(priv, "dma2pnoc_axi", UNKNOWN, 0x80); + hws[JH7100_CLK_SGDMA2P_AHB] = starfive_clk_gate(priv, "sgdma2p_ahb", UNKNOWN, 0x84); + hws[JH7100_CLK_DLA_BUS] = starfive_clk_divider(priv, "dla_bus", UNKNOWN, 0x88, 3); + hws[JH7100_CLK_DLA_AXI] = starfive_clk_gate(priv, "dla_axi", UNKNOWN, 0x8c); + hws[JH7100_CLK_DLANOC_AXI] = starfive_clk_gate(priv, "dlanoc_axi", UNKNOWN, 0x90); + hws[JH7100_CLK_DLA_APB] = starfive_clk_gate(priv, "dla_apb", UNKNOWN, 0x94); + hws[JH7100_CLK_VP6_CORE] = starfive_clk_gated_divider(priv, "vp6_core", UNKNOWN, 0x98, 3); + hws[JH7100_CLK_VP6BUS_SRC] = starfive_clk_divider(priv, "vp6bus_src", UNKNOWN, 0x9c, 3); + hws[JH7100_CLK_VP6_AXI] = starfive_clk_gated_divider(priv, "vp6_axi", UNKNOWN, 0xa0, 3); + hws[JH7100_CLK_VCDECBUS_SRC] = starfive_clk_divider(priv, "vcdecbus_src", UNKNOWN, 0xa4, 3); + hws[JH7100_CLK_VDEC_BUS] = starfive_clk_divider(priv, "vdec_bus", UNKNOWN, 0xa8, 4); + hws[JH7100_CLK_VDEC_AXI] = starfive_clk_gate(priv, "vdec_axi", UNKNOWN, 0xac); + hws[JH7100_CLK_VDECBRG_MAIN] = starfive_clk_gate(priv, "vdecbrg_mainclk", UNKNOWN, 0xb0); + hws[JH7100_CLK_VDEC_BCLK] = starfive_clk_gated_divider(priv, "vdec_bclk", UNKNOWN, 0xb4, 4); + hws[JH7100_CLK_VDEC_CCLK] = starfive_clk_gated_divider(priv, "vdec_cclk", UNKNOWN, 0xb8, 4); + hws[JH7100_CLK_VDEC_APB] = starfive_clk_gate(priv, "vdec_apb", UNKNOWN, 0xbc); + hws[JH7100_CLK_JPEG_AXI] = starfive_clk_gated_divider(priv, "jpeg_axi", UNKNOWN, 0xc0, 4); + hws[JH7100_CLK_JPEG_CCLK] = starfive_clk_gated_divider(priv, "jpeg_cclk", UNKNOWN, 0xc4, 4); + hws[JH7100_CLK_JPEG_APB] = starfive_clk_gate(priv, "jpeg_apb", UNKNOWN, 0xc8); + hws[JH7100_CLK_GC300_2X] = starfive_clk_gated_divider(priv, "gc300_2x", UNKNOWN, 0xcc, 4); + hws[JH7100_CLK_GC300_AHB] = starfive_clk_gate(priv, "gc300_ahb", UNKNOWN, 0xd0); + hws[JH7100_CLK_JPCGC300_AXIBUS] = starfive_clk_divider(priv, "jpcgc300_axibus", UNKNOWN, 0xd4, 4); + hws[JH7100_CLK_GC300_AXI] = starfive_clk_gate(priv, "gc300_axi", UNKNOWN, 0xd8); + hws[JH7100_CLK_JPCGC300_MAIN] = starfive_clk_gate(priv, "jpcgc300_mainclk", UNKNOWN, 0xdc); + hws[JH7100_CLK_VENC_BUS] = starfive_clk_divider(priv, "venc_bus", UNKNOWN, 0xe0, 4); + hws[JH7100_CLK_VENC_AXI] = starfive_clk_gate(priv, "venc_axi", UNKNOWN, 0xe4); + hws[JH7100_CLK_VENCBRG_MAIN] = starfive_clk_gate(priv, "vencbrg_mainclk", UNKNOWN, 0xe8); + hws[JH7100_CLK_VENC_BCLK] = starfive_clk_gated_divider(priv, "venc_bclk", UNKNOWN, 0xec, 4); + hws[JH7100_CLK_VENC_CCLK] = starfive_clk_gated_divider(priv, "venc_cclk", UNKNOWN, 0xf0, 4); + hws[JH7100_CLK_VENC_APB] = starfive_clk_gate(priv, "venc_apb", UNKNOWN, 0xf4); + hws[JH7100_CLK_DDRPLL_DIV2] = starfive_clk_gated_divider(priv, "ddrpll_div2", UNKNOWN, 0xf8, 2); + hws[JH7100_CLK_DDRPLL_DIV4] = starfive_clk_gated_divider(priv, "ddrpll_div4", UNKNOWN, 0xfc, 2); + hws[JH7100_CLK_DDRPLL_DIV8] = starfive_clk_gated_divider(priv, "ddrpll_div8", UNKNOWN, 0x100, 2); + hws[JH7100_CLK_DDROSC_DIV2] = starfive_clk_gated_divider(priv, "ddrosc_div2", UNKNOWN, 0x104, 2); + hws[JH7100_CLK_DDRC0] = starfive_clk_mux(priv, "ddrc0", 0x108, 2, ddrc0_sels, ARRAY_SIZE(ddrc0_sels)); + hws[JH7100_CLK_DDRC1] = starfive_clk_mux(priv, "ddrc1", 0x10c, 2, ddrc1_sels, ARRAY_SIZE(ddrc1_sels)); + hws[JH7100_CLK_DDRPHY_APB] = starfive_clk_gate(priv, "ddrphy_apb", UNKNOWN, 0x110); + hws[JH7100_CLK_NOC_ROB] = starfive_clk_divider(priv, "noc_rob", UNKNOWN, 0x114, 4); + hws[JH7100_CLK_NOC_COG] = starfive_clk_divider(priv, "noc_cog", UNKNOWN, 0x118, 4); + hws[JH7100_CLK_NNE_AHB] = starfive_clk_gate(priv, "nne_ahb", UNKNOWN, 0x11c); + hws[JH7100_CLK_NNEBUS_SRC1] = starfive_clk_divider(priv, "nnebus_src1", UNKNOWN, 0x120, 3); + hws[JH7100_CLK_NNE_BUS] = starfive_clk_mux(priv, "nne_bus", 0x124, 2, nne_bus_sels, ARRAY_SIZE(nne_bus_sels)); + hws[JH7100_CLK_NNE_AXI] = starfive_clk_gate(priv, "nne_axi", UNKNOWN, 0x128); + hws[JH7100_CLK_NNENOC_AXI] = starfive_clk_gate(priv, "nnenoc_axi", UNKNOWN, 0x12c); + hws[JH7100_CLK_DLASLV_AXI] = starfive_clk_gate(priv, "dlaslv_axi", UNKNOWN, 0x130); + hws[JH7100_CLK_DSPX2C_AXI] = starfive_clk_gate(priv, "dspx2c_axi", UNKNOWN, 0x134); + hws[JH7100_CLK_HIFI4_SRC] = starfive_clk_divider(priv, "hifi4_src", UNKNOWN, 0x138, 3); + hws[JH7100_CLK_HIFI4_COREFREE] = starfive_clk_divider(priv, "hifi4_corefree", UNKNOWN, 0x13c, 4); + hws[JH7100_CLK_HIFI4_CORE] = starfive_clk_gate(priv, "hifi4_core", UNKNOWN, 0x140); + hws[JH7100_CLK_HIFI4_BUS] = starfive_clk_divider(priv, "hifi4_bus", UNKNOWN, 0x144, 4); + hws[JH7100_CLK_HIFI4_AXI] = starfive_clk_gate(priv, "hifi4_axi", UNKNOWN, 0x148); + hws[JH7100_CLK_HIFI4NOC_AXI] = starfive_clk_gate(priv, "hifi4noc_axi", UNKNOWN, 0x14c); + hws[JH7100_CLK_SGDMA1P_BUS] = starfive_clk_divider(priv, "sgdma1p_bus", UNKNOWN, 0x150, 4); + hws[JH7100_CLK_SGDMA1P_AXI] = starfive_clk_gate(priv, "sgdma1p_axi", UNKNOWN, 0x154); + hws[JH7100_CLK_DMA1P_AXI] = starfive_clk_gate(priv, "dma1p_axi", UNKNOWN, 0x158); + hws[JH7100_CLK_X2C_AXI] = starfive_clk_gated_divider(priv, "x2c_axi", UNKNOWN, 0x15c, 4); + hws[JH7100_CLK_USB_BUS] = starfive_clk_divider(priv, "usb_bus", UNKNOWN, 0x160, 4); + hws[JH7100_CLK_USB_AXI] = starfive_clk_gate(priv, "usb_axi", UNKNOWN, 0x164); + hws[JH7100_CLK_USBNOC_AXI] = starfive_clk_gate(priv, "usbnoc_axi", UNKNOWN, 0x168); + hws[JH7100_CLK_USBPHY_ROOTDIV] = starfive_clk_divider(priv, "usbphy_rootdiv", UNKNOWN, 0x16c, 3); + hws[JH7100_CLK_USBPHY_125M] = starfive_clk_gated_divider(priv, "usbphy_125m", UNKNOWN, 0x170, 4); + hws[JH7100_CLK_USBPHY_PLLDIV25M] = starfive_clk_gated_divider(priv, "usbphy_plldiv25m", UNKNOWN, 0x174, 6); + hws[JH7100_CLK_USBPHY_25M] = starfive_clk_mux(priv, "usbphy_25m", 0x178, 1, usbphy_25m_sels, ARRAY_SIZE(usbphy_25m_sels)); + hws[JH7100_CLK_AUDIO_DIV] = starfive_clk_divider(priv, "audio_div", UNKNOWN, 0x17c, 18); + hws[JH7100_CLK_AUDIO_SRC] = starfive_clk_gate(priv, "audio_src", UNKNOWN, 0x180); + hws[JH7100_CLK_AUDIO_12288] = starfive_clk_gate(priv, "audio_12288", UNKNOWN, 0x184); + hws[JH7100_CLK_VIN_SRC] = starfive_clk_gated_divider(priv, "vin_src", UNKNOWN, 0x188, 3); + hws[JH7100_CLK_ISP0_BUS] = starfive_clk_divider(priv, "isp0_bus", UNKNOWN, 0x18c, 4); + hws[JH7100_CLK_ISP0_AXI] = starfive_clk_gate(priv, "isp0_axi", UNKNOWN, 0x190); + hws[JH7100_CLK_ISP0NOC_AXI] = starfive_clk_gate(priv, "isp0noc_axi", UNKNOWN, 0x194); + hws[JH7100_CLK_ISPSLV_AXI] = starfive_clk_gate(priv, "ispslv_axi", UNKNOWN, 0x198); + hws[JH7100_CLK_ISP1_BUS] = starfive_clk_divider(priv, "isp1_bus", UNKNOWN, 0x19c, 4); + hws[JH7100_CLK_ISP1_AXI] = starfive_clk_gate(priv, "isp1_axi", UNKNOWN, 0x1a0); + hws[JH7100_CLK_ISP1NOC_AXI] = starfive_clk_gate(priv, "isp1noc_axi", UNKNOWN, 0x1a4); + hws[JH7100_CLK_VIN_BUS] = starfive_clk_divider(priv, "vin_bus", UNKNOWN, 0x1a8, 4); + hws[JH7100_CLK_VIN_AXI] = starfive_clk_gate(priv, "vin_axi", UNKNOWN, 0x1ac); + hws[JH7100_CLK_VINNOC_AXI] = starfive_clk_gate(priv, "vinnoc_axi", UNKNOWN, 0x1b0); + hws[JH7100_CLK_VOUT_SRC] = starfive_clk_gated_divider(priv, "vout_src", UNKNOWN, 0x1b4, 3); + hws[JH7100_CLK_DISPBUS_SRC] = starfive_clk_divider(priv, "dispbus_src", UNKNOWN, 0x1b8, 3); + hws[JH7100_CLK_DISP_BUS] = starfive_clk_divider(priv, "disp_bus", UNKNOWN, 0x1bc, 3); + hws[JH7100_CLK_DISP_AXI] = starfive_clk_gate(priv, "disp_axi", UNKNOWN, 0x1c0); + hws[JH7100_CLK_DISPNOC_AXI] = starfive_clk_gate(priv, "dispnoc_axi", UNKNOWN, 0x1c4); + hws[JH7100_CLK_SDIO0_AHB] = starfive_clk_gate(priv, "sdio0_ahb", UNKNOWN, 0x1c8); + hws[JH7100_CLK_SDIO0_CCLKINT] = starfive_clk_gated_divider(priv, "sdio0_cclkint", UNKNOWN, 0x1cc, 5); + hws[JH7100_CLK_SDIO0_CCLKINT_INV] = starfive_clk_gate_dis(priv, "sdio0_cclkint_inv", UNKNOWN, 0x1d0); + hws[JH7100_CLK_SDIO1_AHB] = starfive_clk_gate(priv, "sdio1_ahb", UNKNOWN, 0x1d4); + hws[JH7100_CLK_SDIO1_CCLKINT] = starfive_clk_gated_divider(priv, "sdio1_cclkint", UNKNOWN, 0x1d8, 5); + hws[JH7100_CLK_SDIO1_CCLKINT_INV] = starfive_clk_gate_dis(priv, "sdio1_cclkint_inv", UNKNOWN, 0x1dc); + hws[JH7100_CLK_GMAC_AHB] = starfive_clk_gate(priv, "gmac_ahb", UNKNOWN, 0x1e0); + hws[JH7100_CLK_GMAC_ROOT_DIV] = starfive_clk_divider(priv, "gmac_root_div", UNKNOWN, 0x1e4, 4); + hws[JH7100_CLK_GMAC_PTP_REF] = starfive_clk_gated_divider(priv, "gmac_ptp_refclk", UNKNOWN, 0x1e8, 5); + hws[JH7100_CLK_GMAC_GTX] = starfive_clk_gated_divider(priv, "gmac_gtxclk", UNKNOWN, 0x1ec, 8); + hws[JH7100_CLK_GMAC_RMII_TX] = starfive_clk_gated_divider(priv, "gmac_rmii_txclk", UNKNOWN, 0x1f0, 4); + hws[JH7100_CLK_GMAC_RMII_RX] = starfive_clk_gated_divider(priv, "gmac_rmii_rxclk", UNKNOWN, 0x1f4, 4); + hws[JH7100_CLK_GMAC_TX] = starfive_clk_mux(priv, "gmac_tx", 0x1f8, 2, gmac_tx_sels, ARRAY_SIZE(gmac_tx_sels)); + hws[JH7100_CLK_GMAC_TX_INV] = starfive_clk_gate_dis(priv, "gmac_tx_inv", UNKNOWN, 0x1fc); + hws[JH7100_CLK_GMAC_RX_PRE] = starfive_clk_mux(priv, "gmac_rx_pre", 0x200, 1, gmac_rx_pre_sels, ARRAY_SIZE(gmac_rx_pre_sels)); + hws[JH7100_CLK_GMAC_RX_INV] = starfive_clk_gate_dis(priv, "gmac_rx_inv", UNKNOWN, 0x204); + hws[JH7100_CLK_GMAC_RMII] = starfive_clk_gate(priv, "gmac_rmii", UNKNOWN, 0x208); + hws[JH7100_CLK_GMAC_TOPHYREF] = starfive_clk_gated_divider(priv, "gmac_tophyref", UNKNOWN, 0x20c, 7); + hws[JH7100_CLK_SPI2AHB_AHB] = starfive_clk_gate(priv, "spi2ahb_ahb", UNKNOWN, 0x210); + hws[JH7100_CLK_SPI2AHB_CORE] = starfive_clk_gated_divider(priv, "spi2ahb_core", UNKNOWN, 0x214, 5); + hws[JH7100_CLK_EZMASTER_AHB] = starfive_clk_gate(priv, "ezmaster_ahb", UNKNOWN, 0x218); + hws[JH7100_CLK_E24_AHB] = starfive_clk_gate(priv, "e24_ahb", UNKNOWN, 0x21c); + hws[JH7100_CLK_E24RTC_TOGGLE] = starfive_clk_gate(priv, "e24rtc_toggle", UNKNOWN, 0x220); + hws[JH7100_CLK_QSPI_AHB] = starfive_clk_gate(priv, "qspi_ahb", UNKNOWN, 0x224); + hws[JH7100_CLK_QSPI_APB] = starfive_clk_gate(priv, "qspi_apb", UNKNOWN, 0x228); + hws[JH7100_CLK_QSPI_REF] = starfive_clk_gated_divider(priv, "qspi_refclk", UNKNOWN, 0x22c, 5); + hws[JH7100_CLK_SEC_AHB] = starfive_clk_gate(priv, "sec_ahb", UNKNOWN, 0x230); + hws[JH7100_CLK_AES] = starfive_clk_gate(priv, "aes_clk", UNKNOWN, 0x234); + hws[JH7100_CLK_SHA] = starfive_clk_gate(priv, "sha_clk", UNKNOWN, 0x238); + hws[JH7100_CLK_PKA] = starfive_clk_gate(priv, "pka_clk", UNKNOWN, 0x23c); + hws[JH7100_CLK_TRNG_APB] = starfive_clk_gate(priv, "trng_apb", UNKNOWN, 0x240); + hws[JH7100_CLK_OTP_APB] = starfive_clk_gate(priv, "otp_apb", UNKNOWN, 0x244); + hws[JH7100_CLK_UART0_APB] = starfive_clk_gate(priv, "uart0_apb", UNKNOWN, 0x248); + hws[JH7100_CLK_UART0_CORE] = starfive_clk_gated_divider(priv, "uart0_core", UNKNOWN, 0x24c, 6); + hws[JH7100_CLK_UART1_APB] = starfive_clk_gate(priv, "uart1_apb", UNKNOWN, 0x250); + hws[JH7100_CLK_UART1_CORE] = starfive_clk_gated_divider(priv, "uart1_core", UNKNOWN, 0x254, 6); + hws[JH7100_CLK_SPI0_APB] = starfive_clk_gate(priv, "spi0_apb", UNKNOWN, 0x258); + hws[JH7100_CLK_SPI0_CORE] = starfive_clk_gated_divider(priv, "spi0_core", UNKNOWN, 0x25c, 6); + hws[JH7100_CLK_SPI1_APB] = starfive_clk_gate(priv, "spi1_apb", UNKNOWN, 0x260); + hws[JH7100_CLK_SPI1_CORE] = starfive_clk_gated_divider(priv, "spi1_core", UNKNOWN, 0x264, 6); + hws[JH7100_CLK_I2C0_APB] = starfive_clk_gate(priv, "i2c0_apb", UNKNOWN, 0x268); + hws[JH7100_CLK_I2C0_CORE] = starfive_clk_gated_divider(priv, "i2c0_core", UNKNOWN, 0x26c, 6); + hws[JH7100_CLK_I2C1_APB] = starfive_clk_gate(priv, "i2c1_apb", UNKNOWN, 0x270); + hws[JH7100_CLK_I2C1_CORE] = starfive_clk_gated_divider(priv, "i2c1_core", UNKNOWN, 0x274, 6); + hws[JH7100_CLK_GPIO_APB] = starfive_clk_gate(priv, "gpio_apb", UNKNOWN, 0x278); + hws[JH7100_CLK_UART2_APB] = starfive_clk_gate(priv, "uart2_apb", UNKNOWN, 0x27c); + hws[JH7100_CLK_UART2_CORE] = starfive_clk_gated_divider(priv, "uart2_core", UNKNOWN, 0x280, 6); + hws[JH7100_CLK_UART3_APB] = starfive_clk_gate(priv, "uart3_apb", UNKNOWN, 0x284); + hws[JH7100_CLK_UART3_CORE] = starfive_clk_gated_divider(priv, "uart3_core", UNKNOWN, 0x288, 6); + hws[JH7100_CLK_SPI2_APB] = starfive_clk_gate(priv, "spi2_apb", UNKNOWN, 0x28c); + hws[JH7100_CLK_SPI2_CORE] = starfive_clk_gated_divider(priv, "spi2_core", UNKNOWN, 0x290, 6); + hws[JH7100_CLK_SPI3_APB] = starfive_clk_gate(priv, "spi3_apb", UNKNOWN, 0x294); + hws[JH7100_CLK_SPI3_CORE] = starfive_clk_gated_divider(priv, "spi3_core", UNKNOWN, 0x298, 6); + hws[JH7100_CLK_I2C2_APB] = starfive_clk_gate(priv, "i2c2_apb", UNKNOWN, 0x29c); + hws[JH7100_CLK_I2C2_CORE] = starfive_clk_gated_divider(priv, "i2c2_core", UNKNOWN, 0x2a0, 6); + hws[JH7100_CLK_I2C3_APB] = starfive_clk_gate(priv, "i2c3_apb", UNKNOWN, 0x2a4); + hws[JH7100_CLK_I2C3_CORE] = starfive_clk_gated_divider(priv, "i2c3_core", UNKNOWN, 0x2a8, 6); + hws[JH7100_CLK_WDTIMER_APB] = starfive_clk_gate(priv, "wdtimer_apb", UNKNOWN, 0x2ac); + hws[JH7100_CLK_WDT_CORE] = starfive_clk_gated_divider(priv, "wdt_coreclk", UNKNOWN, 0x2b0, 6); + hws[JH7100_CLK_TIMER0_CORE] = starfive_clk_gated_divider(priv, "timer0_coreclk", UNKNOWN, 0x2b4, 6); + hws[JH7100_CLK_TIMER1_CORE] = starfive_clk_gated_divider(priv, "timer1_coreclk", UNKNOWN, 0x2b8, 6); + hws[JH7100_CLK_TIMER2_CORE] = starfive_clk_gated_divider(priv, "timer2_coreclk", UNKNOWN, 0x2bc, 6); + hws[JH7100_CLK_TIMER3_CORE] = starfive_clk_gated_divider(priv, "timer3_coreclk", UNKNOWN, 0x2c0, 6); + hws[JH7100_CLK_TIMER4_CORE] = starfive_clk_gated_divider(priv, "timer4_coreclk", UNKNOWN, 0x2c4, 6); + hws[JH7100_CLK_TIMER5_CORE] = starfive_clk_gated_divider(priv, "timer5_coreclk", UNKNOWN, 0x2c8, 6); + hws[JH7100_CLK_TIMER6_CORE] = starfive_clk_gated_divider(priv, "timer6_coreclk", UNKNOWN, 0x2cc, 6); + hws[JH7100_CLK_VP6INTC_APB] = starfive_clk_gate(priv, "vp6intc_apb", UNKNOWN, 0x2d0); + hws[JH7100_CLK_PWM_APB] = starfive_clk_gate(priv, "pwm_apb", UNKNOWN, 0x2d4); + hws[JH7100_CLK_MSI_APB] = starfive_clk_gate(priv, "msi_apb", UNKNOWN, 0x2d8); + hws[JH7100_CLK_TEMP_APB] = starfive_clk_gate(priv, "temp_apb", UNKNOWN, 0x2dc); + hws[JH7100_CLK_TEMP_SENSE] = starfive_clk_gated_divider(priv, "temp_sense", UNKNOWN, 0x2e0, 5); + hws[JH7100_CLK_SYSERR_APB] = starfive_clk_gate(priv, "syserr_apb", UNKNOWN, 0x2e4); + +{ + // FIXME Temporary overrides until we get the clock tree right + #define CLK_AXI 0 + #define CLK_AHB0 1 + #define CLK_AHB2 2 + #define CLK_APB1 3 + #define CLK_APB2 4 + #define CLK_VPU 5 + #define CLK_JPU 6 + #define CLK_PWM 7 + #define CLK_DWMMC_BIU 8 + #define CLK_DWMMC_CIU 9 + #define CLK_UART 10 + #define CLK_HS_UART 11 + #define CLK_I2C0 12 + #define CLK_I2C2 13 + #define CLK_QSPI 14 + #define CLK_SPI 15 + #define CLK_GMAC 16 + #define CLK_HF 17 + #define CLK_RTC 18 + + struct clk_hw **hws = priv->clk_hws.hws; + extern bool clk_ignore_unused; + static struct tmp_clk { + const char *name; + unsigned int mult; + unsigned int div; + struct clk_hw *hw; + } tmp_clks[] = { + [CLK_AXI] = { "axi", .mult = 20, .div = 1 }, + [CLK_AHB0] = { "ahb0", .mult = 10, .div = 1 }, + [CLK_AHB2] = { "ahb2", .mult = 5, .div = 1 }, + [CLK_APB1] = { "apb1", .mult = 5, .div = 1 }, + [CLK_APB2] = { "apb2", .mult = 5, .div = 1 }, + [CLK_VPU] = { "vpu", .mult = 16, .div = 1 }, + [CLK_JPU] = { "jpu", .mult = 40, .div = 3 }, + [CLK_PWM] = { "pwm", .mult = 5, .div = 1 }, + [CLK_DWMMC_BIU] = { "dwmmc-biu", .mult = 4, .div = 1 }, + [CLK_DWMMC_CIU] = { "dwmmc-ciu", .mult = 4, .div = 1 }, + [CLK_UART] = { "uart", .mult = 4, .div = 1 }, + [CLK_HS_UART] = { "hs_uart", .mult = 297, .div = 100 }, + [CLK_I2C0] = { "i2c0", .mult = 99, .div = 50 }, + [CLK_I2C2] = { "i2c2", .mult = 2, .div = 1 }, + [CLK_QSPI] = { "qspi", .mult = 2, .div = 1 }, + [CLK_SPI] = { "spi", .mult = 2, .div = 1 }, + [CLK_GMAC] = { "gmac", .mult = 1, .div = 1 }, + [CLK_HF] = { "hf", .mult = 1, .div = 1 }, + [CLK_RTC] = { "rtc", .mult = 1, .div = 4 } + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tmp_clks); i++) { + struct tmp_clk *clk = &tmp_clks[i]; + clk->hw = devm_clk_hw_register_fixed_factor(priv->dev, + clk->name, "osc_sys", 0, clk->mult, clk->div); + } + + hws[JH7100_CLK_UART0_APB] = tmp_clks[CLK_APB2].hw; + hws[JH7100_CLK_UART0_CORE] = tmp_clks[CLK_HS_UART].hw; +// hws[JH7100_CLK_UART1_APB] = tmp_clks[CLK_APB2].hw; +// hws[JH7100_CLK_UART1_CORE] = tmp_clks[CLK_HS_UART].hw; +// hws[JH7100_CLK_UART2_APB] = tmp_clks[CLK_APB2].hw; +// hws[JH7100_CLK_UART2_CORE] = tmp_clks[CLK_UART].hw; + hws[JH7100_CLK_UART3_APB] = tmp_clks[CLK_APB2].hw; + hws[JH7100_CLK_UART3_CORE] = tmp_clks[CLK_UART].hw; + + hws[JH7100_CLK_I2C0_APB] = tmp_clks[CLK_APB2].hw; + hws[JH7100_CLK_I2C0_CORE] = tmp_clks[CLK_I2C0].hw; + hws[JH7100_CLK_I2C1_APB] = tmp_clks[CLK_APB2].hw; + hws[JH7100_CLK_I2C1_CORE] = tmp_clks[CLK_I2C0].hw; + hws[JH7100_CLK_I2C2_APB] = tmp_clks[CLK_APB2].hw; + hws[JH7100_CLK_I2C2_CORE] = tmp_clks[CLK_I2C2].hw; +// hws[JH7100_CLK_I2C3_APB] = tmp_clks[CLK_APB2].hw; +// hws[JH7100_CLK_I2C3_CORE] = tmp_clks[CLK_I2C2].hw; + +// hws[JH7100_CLK_SPI0_CORE] = tmp_clks[CLK_SPI].hw; +// hws[JH7100_CLK_SPI1_CORE] = tmp_clks[CLK_SPI].hw; + hws[JH7100_CLK_SPI2_CORE] = tmp_clks[CLK_SPI].hw; +// hws[JH7100_CLK_SPI3_CORE] = tmp_clks[CLK_SPI].hw; + + clk_ignore_unused = true; +} + + return 0; +} + +static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_starfive_jh7100_priv *priv; + int error; + + priv = devm_kzalloc(dev, struct_size(priv, clk_hws.hws, JH7100_CLK_END), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->clk_hws.num = JH7100_CLK_END; + spin_lock_init(&priv->rmw_lock); + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + error = starfive_clkgen_init(priv); + if (error) + goto cleanup; + + error = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &priv->clk_hws); + if (error) + goto cleanup; + + return 0; + +cleanup: + // FIXME unregister gate clocks on failure + return error; +} + +static const struct of_device_id clk_starfive_jh7100_match[] = { + { + .compatible = "starfive,jh7100-clkgen", + }, + { /* sentinel */ } +}; + +static struct platform_driver clk_starfive_jh7100_driver = { + .driver = { + .name = "clk-starfive-jh7100", + .of_match_table = clk_starfive_jh7100_match, + }, +}; + +static int __init clk_starfive_jh7100_init(void) +{ + return platform_driver_probe(&clk_starfive_jh7100_driver, + clk_starfive_jh7100_probe); +} + +subsys_initcall(clk_starfive_jh7100_init); + +MODULE_DESCRIPTION("StarFive JH7100 Clock Generator Driver"); +MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>"); +MODULE_LICENSE("GPL v2"); |