diff options
author | Yanhong Wang | 2023-03-29 11:42:13 +0800 |
---|---|---|
committer | Leo Yu-Chi Liang | 2023-04-20 16:08:44 +0800 |
commit | c13fe7c034fa2b616cc0223e20fd365afb36574a (patch) | |
tree | 7b87a36c0bd2ff0534fecda7cafa51a2ed836db6 /drivers | |
parent | 58c9c60b64a8d459ab0d204f9d5c468b0ae72e02 (diff) |
clk: starfive: Add StarFive JH7110 clock driver
Add a DM clock driver for StarFive JH7110 SoC.
Signed-off-by: Yanhong Wang <yanhong.wang@starfivetech.com>
Tested-by: Conor Dooley <conor.dooley@microchip.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Kconfig | 1 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/starfive/Kconfig | 17 | ||||
-rw-r--r-- | drivers/clk/starfive/Makefile | 4 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-jh7110-pll.c | 321 | ||||
-rw-r--r-- | drivers/clk/starfive/clk-jh7110.c | 603 | ||||
-rw-r--r-- | drivers/clk/starfive/clk.h | 57 |
7 files changed, 1004 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 42280cbf83a..a2d4f0c5db1 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -235,6 +235,7 @@ source "drivers/clk/owl/Kconfig" source "drivers/clk/renesas/Kconfig" source "drivers/clk/sunxi/Kconfig" source "drivers/clk/sifive/Kconfig" +source "drivers/clk/starfive/Kconfig" source "drivers/clk/stm32/Kconfig" source "drivers/clk/tegra/Kconfig" source "drivers/clk/ti/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c274cda77c6..66f58603567 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o obj-y += analogbits/ obj-y += imx/ +obj-$(CONFIG_CLK_JH7110) += starfive/ obj-y += tegra/ obj-y += ti/ obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/ diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig new file mode 100644 index 00000000000..9399ef6d517 --- /dev/null +++ b/drivers/clk/starfive/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ + +config SPL_CLK_JH7110 + bool "SPL clock support for JH7110" + depends on STARFIVE_JH7110 && SPL + select SPL_CLK + select SPL_CLK_CCF + help + This enables SPL DM support for clock driver in JH7110. + +config CLK_JH7110 + bool "StarFive JH7110 clock support" + depends on STARFIVE_JH7110 + select CLK + select CLK_CCF + help + This enables support clock driver for StarFive JH7110 SoC platform. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile new file mode 100644 index 00000000000..ec0d1570946 --- /dev/null +++ b/drivers/clk/starfive/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += clk-jh7110.o +obj-y += clk-jh7110-pll.o diff --git a/drivers/clk/starfive/clk-jh7110-pll.c b/drivers/clk/starfive/clk-jh7110-pll.c new file mode 100644 index 00000000000..02e6d9000e2 --- /dev/null +++ b/drivers/clk/starfive/clk-jh7110-pll.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022-23 StarFive Technology Co., Ltd. + * + * Author: Yanhong Wang <yanhong.wang@starfivetech.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <div64.h> +#include <dm/device.h> +#include <linux/bitops.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include "clk.h" + +#define UBOOT_DM_CLK_JH7110_PLLX "jh7110_clk_pllx" + +#define PLL_PD_OFF 1 +#define PLL_PD_ON 0 + +#define CLK_DDR_BUS_MASK GENMASK(29, 24) +#define CLK_DDR_BUS_OFFSET 0xAC +#define CLK_DDR_BUS_OSC_DIV2 0 +#define CLK_DDR_BUS_PLL1_DIV2 1 +#define CLK_DDR_BUS_PLL1_DIV4 2 +#define CLK_DDR_BUS_PLL1_DIV8 3 + +struct clk_jh7110_pllx { + struct clk clk; + void __iomem *base; + void __iomem *sysreg; + enum starfive_pll_type type; + const struct starfive_pllx_offset *offset; + const struct starfive_pllx_rate *rate_table; + int rate_count; +}; + +#define getbits_le32(addr, mask) ((in_le32(addr) & (mask)) >> __ffs((mask))) + +#define PLLX_SET(offset, mask, val) do {\ + reg = readl((ulong *)((ulong)pll->base + (offset))); \ + reg &= ~(mask); \ + reg |= (mask) & ((val) << __ffs(mask)); \ + writel(reg, (ulong *)((ulong)pll->base + (offset))); \ + } while (0) + +#define PLLX_RATE(_rate, _pd, _fd) \ + { \ + .rate = (_rate), \ + .prediv = (_pd), \ + .fbdiv = (_fd), \ + } + +#define to_clk_pllx(_clk) container_of(_clk, struct clk_jh7110_pllx, clk) + +static const struct starfive_pllx_rate jh7110_pll0_tbl[] = { + PLLX_RATE(375000000UL, 8, 125), + PLLX_RATE(500000000UL, 6, 125), + PLLX_RATE(625000000UL, 24, 625), + PLLX_RATE(750000000UL, 4, 125), + PLLX_RATE(875000000UL, 24, 875), + PLLX_RATE(1000000000UL, 3, 125), + PLLX_RATE(1250000000UL, 12, 625), + PLLX_RATE(1375000000UL, 24, 1375), + PLLX_RATE(1500000000UL, 2, 125), + PLLX_RATE(1625000000UL, 24, 1625), + PLLX_RATE(1750000000UL, 12, 875), + PLLX_RATE(1800000000UL, 3, 225), +}; + +static const struct starfive_pllx_rate jh7110_pll1_tbl[] = { + PLLX_RATE(1066000000UL, 12, 533), + PLLX_RATE(1200000000UL, 1, 50), + PLLX_RATE(1400000000UL, 6, 350), + PLLX_RATE(1600000000UL, 3, 200), +}; + +static const struct starfive_pllx_rate jh7110_pll2_tbl[] = { + PLLX_RATE(1228800000UL, 15, 768), + PLLX_RATE(1188000000UL, 2, 99), +}; + +static const struct starfive_pllx_offset jh7110_pll0_offset = { + .pd = 0x20, + .prediv = 0x24, + .fbdiv = 0x1c, + .frac = 0x20, + .postdiv1 = 0x20, + .dacpd = 0x18, + .dsmpd = 0x18, + .pd_mask = BIT(27), + .prediv_mask = GENMASK(5, 0), + .fbdiv_mask = GENMASK(11, 0), + .frac_mask = GENMASK(23, 0), + .postdiv1_mask = GENMASK(29, 28), + .dacpd_mask = BIT(24), + .dsmpd_mask = BIT(25) +}; + +static const struct starfive_pllx_offset jh7110_pll1_offset = { + .pd = 0x28, + .prediv = 0x2c, + .fbdiv = 0x24, + .frac = 0x28, + .postdiv1 = 0x28, + .dacpd = 0x24, + .dsmpd = 0x24, + .pd_mask = BIT(27), + .prediv_mask = GENMASK(5, 0), + .fbdiv_mask = GENMASK(28, 17), + .frac_mask = GENMASK(23, 0), + .postdiv1_mask = GENMASK(29, 28), + .dacpd_mask = BIT(15), + .dsmpd_mask = BIT(16) +}; + +static const struct starfive_pllx_offset jh7110_pll2_offset = { + .pd = 0x30, + .prediv = 0x34, + .fbdiv = 0x2c, + .frac = 0x30, + .postdiv1 = 0x30, + .dacpd = 0x2c, + .dsmpd = 0x2c, + .pd_mask = BIT(27), + .prediv_mask = GENMASK(5, 0), + .fbdiv_mask = GENMASK(28, 17), + .frac_mask = GENMASK(23, 0), + .postdiv1_mask = GENMASK(29, 28), + .dacpd_mask = BIT(15), + .dsmpd_mask = BIT(16) +}; + +struct starfive_pllx_clk starfive_jh7110_pll0 __initdata = { + .type = PLL0, + .offset = &jh7110_pll0_offset, + .rate_table = jh7110_pll0_tbl, + .rate_count = ARRAY_SIZE(jh7110_pll0_tbl), +}; + +struct starfive_pllx_clk starfive_jh7110_pll1 __initdata = { + .type = PLL1, + .offset = &jh7110_pll1_offset, + .rate_table = jh7110_pll1_tbl, + .rate_count = ARRAY_SIZE(jh7110_pll1_tbl), +}; + +struct starfive_pllx_clk starfive_jh7110_pll2 __initdata = { + .type = PLL2, + .offset = &jh7110_pll2_offset, + .rate_table = jh7110_pll2_tbl, + .rate_count = ARRAY_SIZE(jh7110_pll2_tbl), +}; + +static const struct starfive_pllx_rate * +jh7110_get_pll_settings(struct clk_jh7110_pllx *pll, unsigned long rate) +{ + for (int i = 0; i < pll->rate_count; i++) + if (rate == pll->rate_table[i].rate) + return &pll->rate_table[i]; + + return NULL; +} + +static void jh7110_pll_set_rate(struct clk_jh7110_pllx *pll, + const struct starfive_pllx_rate *rate) +{ + u32 reg; + bool set = (pll->type == PLL1) ? true : false; + + if (set) { + reg = readl((ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET)); + reg &= ~CLK_DDR_BUS_MASK; + reg |= CLK_DDR_BUS_OSC_DIV2 << __ffs(CLK_DDR_BUS_MASK); + writel(reg, (ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET)); + } + + PLLX_SET(pll->offset->pd, pll->offset->pd_mask, PLL_PD_OFF); + PLLX_SET(pll->offset->dacpd, pll->offset->dacpd_mask, 1); + PLLX_SET(pll->offset->dsmpd, pll->offset->dsmpd_mask, 1); + PLLX_SET(pll->offset->prediv, pll->offset->prediv_mask, rate->prediv); + PLLX_SET(pll->offset->fbdiv, pll->offset->fbdiv_mask, rate->fbdiv); + PLLX_SET(pll->offset->postdiv1, pll->offset->postdiv1, 0); + PLLX_SET(pll->offset->pd, pll->offset->pd_mask, PLL_PD_ON); + + if (set) { + udelay(100); + reg = readl((ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET)); + reg &= ~CLK_DDR_BUS_MASK; + reg |= CLK_DDR_BUS_PLL1_DIV2 << __ffs(CLK_DDR_BUS_MASK); + writel(reg, (ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET)); + } +} + +static ulong jh7110_pllx_recalc_rate(struct clk *clk) +{ + struct clk_jh7110_pllx *pll = to_clk_pllx(dev_get_clk_ptr(clk->dev)); + u64 refclk = clk_get_parent_rate(clk); + u32 dacpd, dsmpd; + u32 prediv, fbdiv, postdiv1; + u64 frac; + + dacpd = getbits_le32((ulong)pll->base + pll->offset->dacpd, + pll->offset->dacpd_mask); + dsmpd = getbits_le32((ulong)pll->base + pll->offset->dsmpd, + pll->offset->dsmpd_mask); + prediv = getbits_le32((ulong)pll->base + pll->offset->prediv, + pll->offset->prediv_mask); + fbdiv = getbits_le32((ulong)pll->base + pll->offset->fbdiv, + pll->offset->fbdiv_mask); + postdiv1 = 1 << getbits_le32((ulong)pll->base + pll->offset->postdiv1, + pll->offset->postdiv1_mask); + frac = (u64)getbits_le32((ulong)pll->base + pll->offset->frac, + pll->offset->frac_mask); + + /* Integer Multiple Mode + * Both dacpd and dsmpd should be set as 1 while integer multiple mode. + * + * The frequency of outputs can be figured out as below. + * + * Fvco = Fref*Nl/M + * NI is integer frequency dividing ratio of feedback divider, set by fbdiv1[11:0] , + * NI = 8, 9, 10, 12.13....4095 + * M is frequency dividing ratio of pre-divider, set by prediv[5:0],M = 1,2...63 + * + * Fclko1 = Fvco/Q1 + * Q1 is frequency dividing ratio of post divider, set by postdiv1[1:0],Q1= 1,2,4,8 + * + * Fraction Multiple Mode + * + * Both dacpd and dsmpd should be set as 0 while integer multiple mode. + * + * Fvco = Fref*(NI+NF)/M + * NI is integer frequency dividing ratio of feedback divider, set by fbdiv[11:0] , + * NI = 8, 9, 10, 12.13....4095 + * NF is fractional frequency dividing ratio, set by frac[23:0], NF =frac[23:0]/2^24= 0~0.99999994 + * M is frequency dividing ratio of pre-divider, set by prediv[5:0],M = 1,2...63 + * + * Fclko1 = Fvco/Q1 + * Q1 is frequency dividing ratio of post divider, set by postdivl[1:0],Q1= 1,2,4,8 + */ + if (dacpd == 1 && dsmpd == 1) + frac = 0; + else if (dacpd == 0 && dsmpd == 0) + do_div(frac, 1 << 24); + else + return -EINVAL; + + refclk *= (fbdiv + frac); + do_div(refclk, prediv * postdiv1); + + return refclk; +} + +static ulong jh7110_pllx_set_rate(struct clk *clk, ulong drate) +{ + struct clk_jh7110_pllx *pll = to_clk_pllx(dev_get_clk_ptr(clk->dev)); + const struct starfive_pllx_rate *rate; + + rate = jh7110_get_pll_settings(pll, drate); + if (!rate) + return -EINVAL; + + jh7110_pll_set_rate(pll, rate); + + return jh7110_pllx_recalc_rate(clk); +} + +static const struct clk_ops clk_jh7110_ops = { + .set_rate = jh7110_pllx_set_rate, + .get_rate = jh7110_pllx_recalc_rate, +}; + +struct clk *starfive_jh7110_pll(const char *name, const char *parent_name, + void __iomem *base, void __iomem *sysreg, + const struct starfive_pllx_clk *pll_clk) +{ + struct clk_jh7110_pllx *pll; + struct clk *clk; + int ret; + + if (!pll_clk || !base || !sysreg) + return ERR_PTR(-EINVAL); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + pll->sysreg = sysreg; + pll->type = pll_clk->type; + pll->offset = pll_clk->offset; + pll->rate_table = pll_clk->rate_table; + pll->rate_count = pll_clk->rate_count; + + clk = &pll->clk; + ret = clk_register(clk, UBOOT_DM_CLK_JH7110_PLLX, name, parent_name); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + if (IS_ENABLED(CONFIG_SPL_BUILD) && pll->type == PLL0) + jh7110_pllx_set_rate(clk, 1000000000); + + if (IS_ENABLED(CONFIG_SPL_BUILD) && pll->type == PLL2) + jh7110_pllx_set_rate(clk, 1188000000); + + return clk; +} + +U_BOOT_DRIVER(jh7110_clk_pllx) = { + .name = UBOOT_DM_CLK_JH7110_PLLX, + .id = UCLASS_CLK, + .ops = &clk_jh7110_ops, +}; diff --git a/drivers/clk/starfive/clk-jh7110.c b/drivers/clk/starfive/clk-jh7110.c new file mode 100644 index 00000000000..a74b70906a6 --- /dev/null +++ b/drivers/clk/starfive/clk-jh7110.c @@ -0,0 +1,603 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022-23 StarFive Technology Co., Ltd. + * + * Author: Yanhong Wang <yanhong.wang@starfivetech.com> + */ + +#include <common.h> +#include <clk.h> +#include <clk-uclass.h> +#include <dm.h> +#include <dm/device.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <dt-bindings/clock/starfive,jh7110-crg.h> +#include <log.h> +#include <linux/clk-provider.h> + +#include "clk.h" + +#define STARFIVE_CLK_ENABLE_SHIFT 31 /* [31] */ +#define STARFIVE_CLK_INVERT_SHIFT 30 /* [30] */ +#define STARFIVE_CLK_MUX_SHIFT 24 /* [29:24] */ +#define STARFIVE_CLK_DIV_SHIFT 0 /* [23:0] */ + +#define OFFSET(id) ((id) * 4) +#define AONOFFSET(id) (((id) - JH7110_SYSCLK_END) * 4) +#define STGOFFSET(id) (((id) - JH7110_AONCLK_END) * 4) + +typedef int (*jh1710_init_fn)(struct udevice *dev); + +struct jh7110_clk_priv { + void __iomem *reg; + jh1710_init_fn init; +}; + +static const char *cpu_root_sels[2] = { + [0] = "oscillator", + [1] = "pll0_out", +}; + +static const char *perh_root_sels[2] = { + [0] = "pll0_out", + [1] = "pll2_out", +}; + +static const char *bus_root_sels[2] = { + [0] = "oscillator", + [1] = "pll2_out", +}; + +static const char *qspi_ref_sels[2] = { + [0] = "oscillator", + [1] = "qspi_ref_src", +}; + +static const char *gmac1_tx_sels[2] = { + [0] = "gmac1_gtxclk", + [1] = "gmac1_rmii_rtx", +}; + +static const char *gmac0_tx_sels[2] = { + [0] = "gmac0_gtxclk", + [1] = "gmac0_rmii_rtx", +}; + +static const char *apb_func_sels[2] = { + [0] = "osc_div4", + [1] = "oscillator", +}; + +static const char *gmac1_rx_sels[2] = { + [0] = "gmac1-rgmii-rxin-clock", + [1] = "gmac1_rmii_rtx", +}; + +static struct clk *starfive_clk_mux(void __iomem *reg, + const char *name, + unsigned int offset, + u8 width, + const char * const *parent_names, + u8 num_parents) +{ + return clk_register_mux(NULL, name, parent_names, num_parents, 0, + reg + offset, STARFIVE_CLK_MUX_SHIFT, + width, 0); +} + +static struct clk *starfive_clk_gate(void __iomem *reg, + const char *name, + const char *parent_name, + unsigned int offset) +{ + return clk_register_gate(NULL, name, parent_name, 0, reg + offset, + STARFIVE_CLK_ENABLE_SHIFT, 0, NULL); +} + +static struct clk *starfive_clk_inv(void __iomem *reg, + const char *name, + const char *parent_name, + unsigned int offset) +{ + return clk_register_gate(NULL, name, parent_name, 0, reg + offset, + STARFIVE_CLK_INVERT_SHIFT, 0, NULL); +} + +static struct clk *starfive_clk_divider(void __iomem *reg, + const char *name, + const char *parent_name, + unsigned int offset, + u8 width) +{ + return clk_register_divider(NULL, name, parent_name, 0, reg + offset, + 0, width, CLK_DIVIDER_ONE_BASED); +} + +static struct clk *starfive_clk_composite(void __iomem *reg, + const char *name, + const char * const *parent_names, + unsigned int num_parents, + unsigned int offset, + unsigned int mux_width, + unsigned int gate_width, + unsigned int div_width) +{ + struct clk *clk = ERR_PTR(-ENOMEM); + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + int mask_arry[4] = {0x1, 0x3, 0x7, 0xF}; + int mask; + + if (mux_width) { + if (mux_width > 4) + goto fail; + else + mask = mask_arry[mux_width - 1]; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto fail; + + mux->reg = reg + offset; + mux->mask = mask; + mux->shift = STARFIVE_CLK_MUX_SHIFT; + mux->num_parents = num_parents; + mux->flags = 0; + mux->parent_names = parent_names; + } + + if (gate_width) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + + if (!gate) + goto fail; + + gate->reg = reg + offset; + gate->bit_idx = STARFIVE_CLK_ENABLE_SHIFT; + gate->flags = 0; + } + + if (div_width) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto fail; + + div->reg = reg + offset; + + if (offset == OFFSET(JH7110_SYSCLK_UART3_CORE) || + offset == OFFSET(JH7110_SYSCLK_UART4_CORE) || + offset == OFFSET(JH7110_SYSCLK_UART5_CORE)) { + div->shift = 8; + div->width = 8; + } else { + div->shift = STARFIVE_CLK_DIV_SHIFT; + div->width = div_width; + } + div->flags = CLK_DIVIDER_ONE_BASED; + div->table = NULL; + } + + clk = clk_register_composite(NULL, name, + parent_names, num_parents, + &mux->clk, &clk_mux_ops, + &div->clk, &clk_divider_ops, + &gate->clk, &clk_gate_ops, 0); + + if (IS_ERR(clk)) + goto fail; + + return clk; + +fail: + kfree(gate); + kfree(div); + kfree(mux); + return ERR_CAST(clk); +} + +static struct clk *starfive_clk_fix_parent_composite(void __iomem *reg, + const char *name, + const char *parent_names, + unsigned int offset, + unsigned int mux_width, + unsigned int gate_width, + unsigned int div_width) +{ + const char * const *parents; + + parents = &parent_names; + + return starfive_clk_composite(reg, name, parents, 1, offset, + mux_width, gate_width, div_width); +} + +static struct clk *starfive_clk_gate_divider(void __iomem *reg, + const char *name, + const char *parent, + unsigned int offset, + unsigned int width) +{ + const char * const *parent_names; + + parent_names = &parent; + + return starfive_clk_composite(reg, name, parent_names, 1, + offset, 0, 1, width); +} + +static int jh7110_syscrg_init(struct udevice *dev) +{ + struct jh7110_clk_priv *priv = dev_get_priv(dev); + struct ofnode_phandle_args args; + fdt_addr_t addr; + struct clk *pclk; + int ret; + + ret = ofnode_parse_phandle_with_args(dev->node_, "starfive,sys-syscon", NULL, 0, 0, &args); + if (ret) + return ret; + + addr = ofnode_get_addr(args.node); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + clk_dm(JH7110_SYSCLK_PLL0_OUT, + starfive_jh7110_pll("pll0_out", "oscillator", (void __iomem *)addr, + priv->reg, &starfive_jh7110_pll0)); + clk_dm(JH7110_SYSCLK_PLL1_OUT, + starfive_jh7110_pll("pll1_out", "oscillator", (void __iomem *)addr, + priv->reg, &starfive_jh7110_pll1)); + clk_dm(JH7110_SYSCLK_PLL2_OUT, + starfive_jh7110_pll("pll2_out", "oscillator", (void __iomem *)addr, + priv->reg, &starfive_jh7110_pll2)); + clk_dm(JH7110_SYSCLK_CPU_ROOT, + starfive_clk_mux(priv->reg, "cpu_root", + OFFSET(JH7110_SYSCLK_CPU_ROOT), 1, + cpu_root_sels, ARRAY_SIZE(cpu_root_sels))); + clk_dm(JH7110_SYSCLK_CPU_CORE, + starfive_clk_divider(priv->reg, + "cpu_core", "cpu_root", + OFFSET(JH7110_SYSCLK_CPU_CORE), 3)); + clk_dm(JH7110_SYSCLK_CPU_BUS, + starfive_clk_divider(priv->reg, + "cpu_bus", "cpu_core", + OFFSET(JH7110_SYSCLK_CPU_BUS), 2)); + clk_dm(JH7110_SYSCLK_PERH_ROOT, + starfive_clk_composite(priv->reg, + "perh_root", + perh_root_sels, ARRAY_SIZE(perh_root_sels), + OFFSET(JH7110_SYSCLK_PERH_ROOT), 1, 0, 2)); + clk_dm(JH7110_SYSCLK_BUS_ROOT, + starfive_clk_mux(priv->reg, "bus_root", + OFFSET(JH7110_SYSCLK_BUS_ROOT), 1, + bus_root_sels, ARRAY_SIZE(bus_root_sels))); + clk_dm(JH7110_SYSCLK_NOCSTG_BUS, + starfive_clk_divider(priv->reg, + "nocstg_bus", "bus_root", + OFFSET(JH7110_SYSCLK_NOCSTG_BUS), 3)); + clk_dm(JH7110_SYSCLK_AXI_CFG0, + starfive_clk_divider(priv->reg, + "axi_cfg0", "bus_root", + OFFSET(JH7110_SYSCLK_AXI_CFG0), 2)); + clk_dm(JH7110_SYSCLK_STG_AXIAHB, + starfive_clk_divider(priv->reg, + "stg_axiahb", "axi_cfg0", + OFFSET(JH7110_SYSCLK_STG_AXIAHB), 2)); + clk_dm(JH7110_SYSCLK_AHB0, + starfive_clk_gate(priv->reg, + "ahb0", "stg_axiahb", + OFFSET(JH7110_SYSCLK_AHB0))); + clk_dm(JH7110_SYSCLK_AHB1, + starfive_clk_gate(priv->reg, + "ahb1", "stg_axiahb", + OFFSET(JH7110_SYSCLK_AHB1))); + clk_dm(JH7110_SYSCLK_APB_BUS, + starfive_clk_divider(priv->reg, + "apb_bus", "stg_axiahb", + OFFSET(JH7110_SYSCLK_APB_BUS), 4)); + clk_dm(JH7110_SYSCLK_APB0, + starfive_clk_gate(priv->reg, + "apb0", "apb_bus", + OFFSET(JH7110_SYSCLK_APB0))); + clk_dm(JH7110_SYSCLK_QSPI_AHB, + starfive_clk_gate(priv->reg, + "qspi_ahb", "ahb1", + OFFSET(JH7110_SYSCLK_QSPI_AHB))); + clk_dm(JH7110_SYSCLK_QSPI_APB, + starfive_clk_gate(priv->reg, + "qspi_apb", "apb_bus", + OFFSET(JH7110_SYSCLK_QSPI_APB))); + clk_dm(JH7110_SYSCLK_QSPI_REF_SRC, + starfive_clk_divider(priv->reg, + "qspi_ref_src", "pll0_out", + OFFSET(JH7110_SYSCLK_QSPI_REF_SRC), 5)); + clk_dm(JH7110_SYSCLK_QSPI_REF, + starfive_clk_composite(priv->reg, + "qspi_ref", + qspi_ref_sels, ARRAY_SIZE(qspi_ref_sels), + OFFSET(JH7110_SYSCLK_QSPI_REF), 1, 1, 0)); + clk_dm(JH7110_SYSCLK_SDIO0_AHB, + starfive_clk_gate(priv->reg, + "sdio0_ahb", "ahb0", + OFFSET(JH7110_SYSCLK_SDIO0_AHB))); + clk_dm(JH7110_SYSCLK_SDIO1_AHB, + starfive_clk_gate(priv->reg, + "sdio1_ahb", "ahb0", + OFFSET(JH7110_SYSCLK_SDIO1_AHB))); + clk_dm(JH7110_SYSCLK_SDIO0_SDCARD, + starfive_clk_fix_parent_composite(priv->reg, + "sdio0_sdcard", "axi_cfg0", + OFFSET(JH7110_SYSCLK_SDIO0_SDCARD), 0, 1, 4)); + clk_dm(JH7110_SYSCLK_SDIO1_SDCARD, + starfive_clk_fix_parent_composite(priv->reg, + "sdio1_sdcard", "axi_cfg0", + OFFSET(JH7110_SYSCLK_SDIO1_SDCARD), 0, 1, 4)); + clk_dm(JH7110_SYSCLK_USB_125M, + starfive_clk_divider(priv->reg, + "usb_125m", "pll0_out", + OFFSET(JH7110_SYSCLK_USB_125M), 4)); + clk_dm(JH7110_SYSCLK_NOC_BUS_STG_AXI, + starfive_clk_gate(priv->reg, + "noc_bus_stg_axi", "nocstg_bus", + OFFSET(JH7110_SYSCLK_NOC_BUS_STG_AXI))); + clk_dm(JH7110_SYSCLK_GMAC1_AHB, + starfive_clk_gate(priv->reg, + "gmac1_ahb", "ahb0", + OFFSET(JH7110_SYSCLK_GMAC1_AHB))); + clk_dm(JH7110_SYSCLK_GMAC1_AXI, + starfive_clk_gate(priv->reg, + "gmac1_axi", "stg_axiahb", + OFFSET(JH7110_SYSCLK_GMAC1_AXI))); + clk_dm(JH7110_SYSCLK_GMAC_SRC, + starfive_clk_divider(priv->reg, + "gmac_src", "pll0_out", + OFFSET(JH7110_SYSCLK_GMAC_SRC), 3)); + clk_dm(JH7110_SYSCLK_GMAC1_GTXCLK, + starfive_clk_divider(priv->reg, + "gmac1_gtxclk", "pll0_out", + OFFSET(JH7110_SYSCLK_GMAC1_GTXCLK), 4)); + clk_dm(JH7110_SYSCLK_GMAC1_GTXC, + starfive_clk_gate(priv->reg, + "gmac1_gtxc", "gmac1_gtxclk", + OFFSET(JH7110_SYSCLK_GMAC1_GTXC))); + clk_dm(JH7110_SYSCLK_GMAC1_RMII_RTX, + starfive_clk_divider(priv->reg, + "gmac1_rmii_rtx", "gmac1-rmii-refin-clock", + OFFSET(JH7110_SYSCLK_GMAC1_RMII_RTX), 5)); + clk_dm(JH7110_SYSCLK_GMAC1_PTP, + starfive_clk_gate_divider(priv->reg, + "gmac1_ptp", "gmac_src", + OFFSET(JH7110_SYSCLK_GMAC1_PTP), 5)); + clk_dm(JH7110_SYSCLK_GMAC1_RX, + starfive_clk_mux(priv->reg, "gmac1_rx", + OFFSET(JH7110_SYSCLK_GMAC1_RX), 1, + gmac1_rx_sels, ARRAY_SIZE(gmac1_rx_sels))); + clk_dm(JH7110_SYSCLK_GMAC1_TX, + starfive_clk_composite(priv->reg, + "gmac1_tx", + gmac1_tx_sels, ARRAY_SIZE(gmac1_tx_sels), + OFFSET(JH7110_SYSCLK_GMAC1_TX), 1, 1, 0)); + clk_dm(JH7110_SYSCLK_GMAC1_TX_INV, + starfive_clk_inv(priv->reg, + "gmac1_tx_inv", "gmac1_tx", + OFFSET(JH7110_SYSCLK_GMAC1_TX_INV))); + clk_dm(JH7110_SYSCLK_GMAC0_GTXCLK, + starfive_clk_gate_divider(priv->reg, + "gmac0_gtxclk", "pll0_out", + OFFSET(JH7110_SYSCLK_GMAC0_GTXCLK), 4)); + clk_dm(JH7110_SYSCLK_GMAC0_PTP, + starfive_clk_gate_divider(priv->reg, + "gmac0_ptp", "gmac_src", + OFFSET(JH7110_SYSCLK_GMAC0_PTP), 5)); + clk_dm(JH7110_SYSCLK_GMAC0_GTXC, + starfive_clk_gate(priv->reg, + "gmac0_gtxc", "gmac0_gtxclk", + OFFSET(JH7110_SYSCLK_GMAC0_GTXC))); + clk_dm(JH7110_SYSCLK_UART0_APB, + starfive_clk_gate(priv->reg, + "uart0_apb", "apb0", + OFFSET(JH7110_SYSCLK_UART0_APB))); + clk_dm(JH7110_SYSCLK_UART0_CORE, + starfive_clk_gate(priv->reg, + "uart0_core", "oscillator", + OFFSET(JH7110_SYSCLK_UART0_CORE))); + clk_dm(JH7110_SYSCLK_UART1_APB, + starfive_clk_gate(priv->reg, + "uart1_apb", "apb0", + OFFSET(JH7110_SYSCLK_UART1_APB))); + clk_dm(JH7110_SYSCLK_UART1_CORE, + starfive_clk_gate(priv->reg, + "uart1_core", "oscillator", + OFFSET(JH7110_SYSCLK_UART1_CORE))); + clk_dm(JH7110_SYSCLK_UART2_APB, + starfive_clk_gate(priv->reg, + "uart2_apb", "apb0", + OFFSET(JH7110_SYSCLK_UART2_APB))); + clk_dm(JH7110_SYSCLK_UART2_CORE, + starfive_clk_gate(priv->reg, + "uart2_core", "oscillator", + OFFSET(JH7110_SYSCLK_UART2_CORE))); + clk_dm(JH7110_SYSCLK_UART3_APB, + starfive_clk_gate(priv->reg, + "uart3_apb", "apb0", + OFFSET(JH7110_SYSCLK_UART3_APB))); + clk_dm(JH7110_SYSCLK_UART3_CORE, + starfive_clk_gate_divider(priv->reg, + "uart3_core", "perh_root", + OFFSET(JH7110_SYSCLK_UART3_CORE), 8)); + clk_dm(JH7110_SYSCLK_UART4_APB, + starfive_clk_gate(priv->reg, + "uart4_apb", "apb0", + OFFSET(JH7110_SYSCLK_UART4_APB))); + clk_dm(JH7110_SYSCLK_UART4_CORE, + starfive_clk_gate_divider(priv->reg, + "uart4_core", "perh_root", + OFFSET(JH7110_SYSCLK_UART4_CORE), 8)); + clk_dm(JH7110_SYSCLK_UART5_APB, + starfive_clk_gate(priv->reg, + "uart5_apb", "apb0", + OFFSET(JH7110_SYSCLK_UART5_APB))); + clk_dm(JH7110_SYSCLK_UART5_CORE, + starfive_clk_gate_divider(priv->reg, + "uart5_core", "perh_root", + OFFSET(JH7110_SYSCLK_UART5_CORE), 8)); + clk_dm(JH7110_SYSCLK_I2C2_APB, + starfive_clk_gate(priv->reg, + "i2c2_apb", "apb0", + OFFSET(JH7110_SYSCLK_I2C2_APB))); + clk_dm(JH7110_SYSCLK_I2C5_APB, + starfive_clk_gate(priv->reg, + "i2c5_apb", "apb0", + OFFSET(JH7110_SYSCLK_I2C5_APB))); + + /* enable noc_bus_stg_axi clock */ + if (!clk_get_by_id(JH7110_SYSCLK_NOC_BUS_STG_AXI, &pclk)) + clk_enable(pclk); + + return 0; +} + +static int jh7110_aoncrg_init(struct udevice *dev) +{ + struct jh7110_clk_priv *priv = dev_get_priv(dev); + + clk_dm(JH7110_AONCLK_OSC_DIV4, + starfive_clk_divider(priv->reg, + "osc_div4", "oscillator", + AONOFFSET(JH7110_AONCLK_OSC_DIV4), 5)); + clk_dm(JH7110_AONCLK_APB_FUNC, + starfive_clk_mux(priv->reg, "apb_func", + AONOFFSET(JH7110_AONCLK_APB_FUNC), 1, + apb_func_sels, ARRAY_SIZE(apb_func_sels))); + clk_dm(JH7110_AONCLK_GMAC0_AHB, + starfive_clk_gate(priv->reg, + "gmac0_ahb", "stg_axiahb", + AONOFFSET(JH7110_AONCLK_GMAC0_AHB))); + clk_dm(JH7110_AONCLK_GMAC0_AXI, + starfive_clk_gate(priv->reg, + "gmac0_axi", "stg_axiahb", + AONOFFSET(JH7110_AONCLK_GMAC0_AXI))); + clk_dm(JH7110_AONCLK_GMAC0_RMII_RTX, + starfive_clk_divider(priv->reg, + "gmac0_rmii_rtx", "gmac0-rmii-refin-clock", + AONOFFSET(JH7110_AONCLK_GMAC0_RMII_RTX), 5)); + clk_dm(JH7110_AONCLK_GMAC0_TX, + starfive_clk_composite(priv->reg, + "gmac0_tx", gmac0_tx_sels, + ARRAY_SIZE(gmac0_tx_sels), + AONOFFSET(JH7110_AONCLK_GMAC0_TX), 1, 1, 0)); + clk_dm(JH7110_AONCLK_GMAC0_TX_INV, + starfive_clk_inv(priv->reg, + "gmac0_tx_inv", "gmac0_tx", + AONOFFSET(JH7110_AONCLK_GMAC0_TX_INV))); + clk_dm(JH7110_AONCLK_OTPC_APB, + starfive_clk_gate(priv->reg, + "otpc_apb", "apb_bus", + AONOFFSET(JH7110_AONCLK_OTPC_APB))); + + return 0; +} + +static int jh7110_stgcrg_init(struct udevice *dev) +{ + struct jh7110_clk_priv *priv = dev_get_priv(dev); + + clk_dm(JH7110_STGCLK_USB_APB, + starfive_clk_gate(priv->reg, + "usb_apb", "apb_bus", + STGOFFSET(JH7110_STGCLK_USB_APB))); + clk_dm(JH7110_STGCLK_USB_UTMI_APB, + starfive_clk_gate(priv->reg, + "usb_utmi_apb", "apb_bus", + STGOFFSET(JH7110_STGCLK_USB_UTMI_APB))); + clk_dm(JH7110_STGCLK_USB_AXI, + starfive_clk_gate(priv->reg, + "usb_axi", "stg_axiahb", + STGOFFSET(JH7110_STGCLK_USB_AXI))); + clk_dm(JH7110_STGCLK_USB_LPM, + starfive_clk_gate_divider(priv->reg, + "usb_lpm", "oscillator", + STGOFFSET(JH7110_STGCLK_USB_LPM), 2)); + clk_dm(JH7110_STGCLK_USB_STB, + starfive_clk_gate_divider(priv->reg, + "usb_stb", "oscillator", + STGOFFSET(JH7110_STGCLK_USB_STB), 3)); + clk_dm(JH7110_STGCLK_USB_APP_125, + starfive_clk_gate(priv->reg, + "usb_app_125", "usb_125m", + STGOFFSET(JH7110_STGCLK_USB_APP_125))); + clk_dm(JH7110_STGCLK_USB_REFCLK, + starfive_clk_divider(priv->reg, "usb_refclk", "oscillator", + STGOFFSET(JH7110_STGCLK_USB_REFCLK), 2)); + clk_dm(JH7110_STGCLK_PCIE0_AXI, + starfive_clk_gate(priv->reg, + "pcie0_axi", "stg_axiahb", + STGOFFSET(JH7110_STGCLK_PCIE0_AXI))); + clk_dm(JH7110_STGCLK_PCIE0_APB, + starfive_clk_gate(priv->reg, + "pcie0_apb", "apb_bus", + STGOFFSET(JH7110_STGCLK_PCIE0_APB))); + clk_dm(JH7110_STGCLK_PCIE0_TL, + starfive_clk_gate(priv->reg, + "pcie0_tl", "stg_axiahb", + STGOFFSET(JH7110_STGCLK_PCIE0_TL))); + clk_dm(JH7110_STGCLK_PCIE1_AXI, + starfive_clk_gate(priv->reg, + "pcie1_axi", "stg_axiahb", + STGOFFSET(JH7110_STGCLK_PCIE1_AXI))); + clk_dm(JH7110_STGCLK_PCIE1_APB, + starfive_clk_gate(priv->reg, + "pcie1_apb", "apb_bus", + STGOFFSET(JH7110_STGCLK_PCIE1_APB))); + clk_dm(JH7110_STGCLK_PCIE1_TL, + starfive_clk_gate(priv->reg, + "pcie1_tl", "stg_axiahb", + STGOFFSET(JH7110_STGCLK_PCIE1_TL))); + + return 0; +} + +static int jh7110_clk_probe(struct udevice *dev) +{ + struct jh7110_clk_priv *priv = dev_get_priv(dev); + + priv->init = (jh1710_init_fn)dev_get_driver_data(dev); + priv->reg = (void __iomem *)dev_read_addr_ptr(dev); + + if (priv->init) + return priv->init(dev); + + return 0; +} + +static int jh7110_clk_bind(struct udevice *dev) +{ + /* The reset driver does not have a device node, so bind it here */ + return device_bind_driver_to_node(dev, "jh7110_reset", dev->name, + dev_ofnode(dev), NULL); +} + +static const struct udevice_id jh7110_clk_of_match[] = { + { .compatible = "starfive,jh7110-syscrg", + .data = (ulong)&jh7110_syscrg_init + }, + { .compatible = "starfive,jh7110-stgcrg", + .data = (ulong)&jh7110_stgcrg_init + }, + { .compatible = "starfive,jh7110-aoncrg", + .data = (ulong)&jh7110_aoncrg_init + }, + { } +}; + +U_BOOT_DRIVER(jh7110_clk) = { + .name = "jh7110_clk", + .id = UCLASS_CLK, + .of_match = jh7110_clk_of_match, + .probe = jh7110_clk_probe, + .ops = &ccf_clk_ops, + .priv_auto = sizeof(struct jh7110_clk_priv), + .bind = jh7110_clk_bind, +}; diff --git a/drivers/clk/starfive/clk.h b/drivers/clk/starfive/clk.h new file mode 100644 index 00000000000..4dee12fe895 --- /dev/null +++ b/drivers/clk/starfive/clk.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2022 Starfive, Inc. + * Author: Yanhong Wang <yanhong.wang@starfivetech.com> + * + */ + +#ifndef __CLK_STARFIVE_H +#define __CLK_STARFIVE_H + +enum starfive_pll_type { + PLL0 = 0, + PLL1, + PLL2, + PLL_MAX = PLL2 +}; + +struct starfive_pllx_rate { + u64 rate; + u32 prediv; + u32 fbdiv; + u32 frac; +}; + +struct starfive_pllx_offset { + u32 pd; + u32 prediv; + u32 fbdiv; + u32 frac; + u32 postdiv1; + u32 dacpd; + u32 dsmpd; + u32 pd_mask; + u32 prediv_mask; + u32 fbdiv_mask; + u32 frac_mask; + u32 postdiv1_mask; + u32 dacpd_mask; + u32 dsmpd_mask; +}; + +struct starfive_pllx_clk { + enum starfive_pll_type type; + const struct starfive_pllx_offset *offset; + const struct starfive_pllx_rate *rate_table; + int rate_count; + int flags; +}; + +extern struct starfive_pllx_clk starfive_jh7110_pll0; +extern struct starfive_pllx_clk starfive_jh7110_pll1; +extern struct starfive_pllx_clk starfive_jh7110_pll2; + +struct clk *starfive_jh7110_pll(const char *name, const char *parent_name, + void __iomem *base, void __iomem *sysreg, + const struct starfive_pllx_clk *pll_clk); +#endif |