diff options
Diffstat (limited to 'drivers/clk')
25 files changed, 5455 insertions, 1555 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a0ac661f28..e07c6dd78a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -170,11 +170,23 @@ config CLK_SCMI by a SCMI agent based on SCMI clock protocol communication with a SCMI server. +config CLK_K210 + bool "Clock support for Kendryte K210" + depends on CLK + help + This enables support clock driver for Kendryte K210 platforms. + +config CLK_K210_SET_RATE + bool "Enable setting the Kendryte K210 PLL rate" + depends on CLK_K210 + help + Add functionality to calculate new rates for K210 PLLs. Enabling this + feature adds around 1K to U-Boot's final size. + source "drivers/clk/analogbits/Kconfig" source "drivers/clk/at91/Kconfig" source "drivers/clk/exynos/Kconfig" source "drivers/clk/imx/Kconfig" -source "drivers/clk/kendryte/Kconfig" source "drivers/clk/meson/Kconfig" source "drivers/clk/microchip/Kconfig" source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 4fcc33953a..6e9c2d5485 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -28,7 +28,7 @@ obj-$(CONFIG_CLK_BOSTON) += clk_boston.o obj-$(CONFIG_CLK_EXYNOS) += exynos/ obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/ obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o -obj-$(CONFIG_CLK_K210) += kendryte/ +obj-$(CONFIG_CLK_K210) += clk_kendryte.o obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_MPFS) += microchip/ obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 53e7be764d..14254212ca 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -160,7 +160,7 @@ int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk) int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk) { int i, ret, err, count; - + bulk->count = 0; count = dev_count_phandle_with_args(dev, "clocks", "#clock-cells", 0); @@ -207,7 +207,8 @@ static struct clk *clk_set_default_get_by_id(struct clk *clk) return c; } -static int clk_set_default_parents(struct udevice *dev, int stage) +static int clk_set_default_parents(struct udevice *dev, + enum clk_defaults_stage stage) { struct clk clk, parent_clk, *c, *p; int index; @@ -241,6 +242,15 @@ static int clk_set_default_parents(struct udevice *dev, int stage) ret = clk_get_by_indexed_prop(dev, "assigned-clocks", index, &clk); + /* + * If the clock provider is not ready yet, let it handle + * the re-programming later. + */ + if (ret == -EPROBE_DEFER) { + ret = 0; + continue; + } + if (ret) { debug("%s: could not get assigned clock %d for %s\n", __func__, index, dev_read_name(dev)); @@ -251,10 +261,10 @@ static int clk_set_default_parents(struct udevice *dev, int stage) * It cannot be done right now but need to wait after the * device is probed */ - if (stage == 0 && clk.dev == dev) + if (stage == CLK_DEFAULTS_PRE && clk.dev == dev) continue; - if (stage > 0 && clk.dev != dev) + if (stage != CLK_DEFAULTS_PRE && clk.dev != dev) /* do not setup twice the parent clocks */ continue; @@ -280,7 +290,8 @@ static int clk_set_default_parents(struct udevice *dev, int stage) return 0; } -static int clk_set_default_rates(struct udevice *dev, int stage) +static int clk_set_default_rates(struct udevice *dev, + enum clk_defaults_stage stage) { struct clk clk, *c; int index; @@ -309,6 +320,15 @@ static int clk_set_default_rates(struct udevice *dev, int stage) ret = clk_get_by_indexed_prop(dev, "assigned-clocks", index, &clk); + /* + * If the clock provider is not ready yet, let it handle + * the re-programming later. + */ + if (ret == -EPROBE_DEFER) { + ret = 0; + continue; + } + if (ret) { dev_dbg(dev, "could not get assigned clock %d (err = %d)\n", @@ -320,10 +340,10 @@ static int clk_set_default_rates(struct udevice *dev, int stage) * It cannot be done right now but need to wait after the * device is probed */ - if (stage == 0 && clk.dev == dev) + if (stage == CLK_DEFAULTS_PRE && clk.dev == dev) continue; - if (stage > 0 && clk.dev != dev) + if (stage != CLK_DEFAULTS_PRE && clk.dev != dev) /* do not setup twice the parent clocks */ continue; @@ -346,16 +366,21 @@ fail: return ret; } -int clk_set_defaults(struct udevice *dev, int stage) +int clk_set_defaults(struct udevice *dev, enum clk_defaults_stage stage) { int ret; if (!dev_has_ofnode(dev)) return 0; - /* If this not in SPL and pre-reloc state, don't take any action. */ + /* + * To avoid setting defaults twice, don't set them before relocation. + * However, still set them for SPL. And still set them if explicitly + * asked. + */ if (!(IS_ENABLED(CONFIG_SPL_BUILD) || (gd->flags & GD_FLG_RELOC))) - return 0; + if (stage != CLK_DEFAULTS_POST_FORCE) + return 0; debug("%s(%s)\n", __func__, dev_read_name(dev)); @@ -502,6 +527,8 @@ struct clk *clk_get_parent(struct clk *clk) return NULL; pdev = dev_get_parent(clk->dev); + if (!pdev) + return ERR_PTR(-ENODEV); pclk = dev_get_clk_ptr(pdev); if (!pclk) return ERR_PTR(-ENODEV); @@ -548,6 +575,22 @@ ulong clk_round_rate(struct clk *clk, ulong rate) return ops->round_rate(clk, rate); } +static void clk_clean_rate_cache(struct clk *clk) +{ + struct udevice *child_dev; + struct clk *clkp; + + if (!clk) + return; + + clk->rate = 0; + + list_for_each_entry(child_dev, &clk->dev->child_head, sibling_node) { + clkp = dev_get_clk_ptr(child_dev); + clk_clean_rate_cache(clkp); + } +} + ulong clk_set_rate(struct clk *clk, ulong rate) { const struct clk_ops *ops; @@ -560,6 +603,9 @@ ulong clk_set_rate(struct clk *clk, ulong rate) if (!ops->set_rate) return -ENOSYS; + /* Clean up cached rates for us and all child clocks */ + clk_clean_rate_cache(clk); + return ops->set_rate(clk, rate); } @@ -805,7 +851,7 @@ int clk_uclass_post_probe(struct udevice *dev) * where the DT is used to setup default parents and rates * using assigned-clocks */ - clk_set_defaults(dev, 1); + clk_set_defaults(dev, CLK_DEFAULTS_POST); return 0; } diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c index 09f9ef26a4..325a9b2dcf 100644 --- a/drivers/clk/clk_fixed_rate.c +++ b/drivers/clk/clk_fixed_rate.c @@ -9,6 +9,9 @@ #include <dm/device-internal.h> #include <linux/clk-provider.h> +#define UBOOT_DM_CLK_FIXED_RATE "fixed_rate_clock" +#define UBOOT_DM_CLK_FIXED_RATE_RAW "fixed_rate_raw_clock" + static ulong clk_fixed_rate_get_rate(struct clk *clk) { return to_clk_fixed_rate(clk->dev)->fixed_rate; @@ -40,6 +43,15 @@ void clk_fixed_rate_ofdata_to_plat_(struct udevice *dev, clk->enable_count = 0; } +static ulong clk_fixed_rate_raw_get_rate(struct clk *clk) +{ + return container_of(clk, struct clk_fixed_rate, clk)->fixed_rate; +} + +const struct clk_ops clk_fixed_rate_raw_ops = { + .get_rate = clk_fixed_rate_raw_get_rate, +}; + static int clk_fixed_rate_of_to_plat(struct udevice *dev) { clk_fixed_rate_ofdata_to_plat_(dev, to_clk_fixed_rate(dev)); @@ -47,6 +59,32 @@ static int clk_fixed_rate_of_to_plat(struct udevice *dev) return 0; } +#if CONFIG_IS_ENABLED(CLK_CCF) +struct clk *clk_register_fixed_rate(struct device *dev, const char *name, + ulong rate) +{ + struct clk *clk; + struct clk_fixed_rate *fixed; + int ret; + + fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return ERR_PTR(-ENOMEM); + + fixed->fixed_rate = rate; + + clk = &fixed->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_FIXED_RATE_RAW, name, NULL); + if (ret) { + kfree(fixed); + return ERR_PTR(ret); + } + + return clk; +} +#endif + static const struct udevice_id clk_fixed_rate_match[] = { { .compatible = "fixed-clock", @@ -63,3 +101,10 @@ U_BOOT_DRIVER(fixed_clock) = { .ops = &clk_fixed_rate_ops, .flags = DM_FLAG_PRE_RELOC, }; + +U_BOOT_DRIVER(clk_fixed_rate_raw) = { + .name = UBOOT_DM_CLK_FIXED_RATE_RAW, + .id = UCLASS_CLK, + .ops = &clk_fixed_rate_raw_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/clk_kendryte.c b/drivers/clk/clk_kendryte.c new file mode 100644 index 0000000000..3148756968 --- /dev/null +++ b/drivers/clk/clk_kendryte.c @@ -0,0 +1,1320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com> + */ +#define LOG_CATEGORY UCLASS_CLK + +#include <common.h> +#include <clk.h> +#include <clk-uclass.h> +#include <div64.h> +#include <dm.h> +#include <log.h> +#include <mapmem.h> +#include <serial.h> +#include <dt-bindings/clock/k210-sysctl.h> +#include <dt-bindings/mfd/k210-sysctl.h> +#include <kendryte/pll.h> +#include <linux/bitfield.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct k210_clk_priv - K210 clock driver private data + * @base: The base address of the sysctl device + * @in0: The "in0" external oscillator + */ +struct k210_clk_priv { + void __iomem *base; + struct clk in0; +}; + +/* + * All parameters for different sub-clocks are collected into parameter arrays. + * These parameters are then initialized by the clock which uses them during + * probe. To save space, ids are automatically generated for each sub-clock by + * using an enum. Instead of storing a parameter struct for each clock, even for + * those clocks which don't use a particular type of sub-clock, we can just + * store the parameters for the clocks which need them. + * + * So why do it like this? Arranging all the sub-clocks together makes it very + * easy to find bugs in the code. + */ + +/** + * enum k210_clk_div_type - The type of divider + * @K210_DIV_ONE: freq = parent / (reg + 1) + * @K210_DIV_EVEN: freq = parent / 2 / (reg + 1) + * @K210_DIV_POWER: freq = parent / (2 << reg) + * @K210_DIV_FIXED: freq = parent / factor + */ +enum k210_clk_div_type { + K210_DIV_ONE, + K210_DIV_EVEN, + K210_DIV_POWER, + K210_DIV_FIXED, +}; + +/** + * struct k210_div_params - Parameters for dividing clocks + * @type: An &enum k210_clk_div_type specifying the dividing formula + * @off: The offset of the divider from the sysctl base address + * @shift: The offset of the LSB of the divider + * @width: The number of bits in the divider + * @div: The fixed divisor for this divider + */ +struct k210_div_params { + u8 type; + union { + struct { + u8 off; + u8 shift; + u8 width; + }; + u8 div; + }; +}; + +#define DIV_LIST \ + DIV(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, K210_DIV_POWER) \ + DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3, K210_DIV_ONE) \ + DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3, K210_DIV_ONE) \ + DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3, K210_DIV_ONE) \ + DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4, K210_DIV_ONE) \ + DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4, K210_DIV_ONE) \ + DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4, K210_DIV_ONE) \ + DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4, K210_DIV_ONE) \ + DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4, K210_DIV_ONE) \ + DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8, K210_DIV_EVEN) \ + DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8, K210_DIV_EVEN) \ + DIV_FIXED(K210_CLK_CLINT, 50) \ + +#define _DIVIFY(id) K210_CLK_DIV_##id +#define DIVIFY(id) _DIVIFY(id) + +enum k210_div_id { +#define DIV(id, ...) DIVIFY(id), +#define DIV_FIXED DIV + DIV_LIST +#undef DIV +#undef DIV_FIXED + K210_CLK_DIV_NONE, +}; + +static const struct k210_div_params k210_divs[] = { +#define DIV(id, _off, _shift, _width, _type) \ + [DIVIFY(id)] = { \ + .type = (_type), \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ + }, +#define DIV_FIXED(id, _div) \ + [DIVIFY(id)] = { \ + .type = K210_DIV_FIXED, \ + .div = (_div) \ + }, + DIV_LIST +#undef DIV +#undef DIV_FIXED +}; + +#undef DIV +#undef DIV_LIST + +/** + * struct k210_gate_params - Parameters for gated clocks + * @off: The offset of the gate from the sysctl base address + * @bit_idx: The index of the bit within the register + */ +struct k210_gate_params { + u8 off; + u8 bit_idx; +}; + +#define GATE_LIST \ + GATE(K210_CLK_CPU, K210_SYSCTL_EN_CENT, 0) \ + GATE(K210_CLK_SRAM0, K210_SYSCTL_EN_CENT, 1) \ + GATE(K210_CLK_SRAM1, K210_SYSCTL_EN_CENT, 2) \ + GATE(K210_CLK_APB0, K210_SYSCTL_EN_CENT, 3) \ + GATE(K210_CLK_APB1, K210_SYSCTL_EN_CENT, 4) \ + GATE(K210_CLK_APB2, K210_SYSCTL_EN_CENT, 5) \ + GATE(K210_CLK_ROM, K210_SYSCTL_EN_PERI, 0) \ + GATE(K210_CLK_DMA, K210_SYSCTL_EN_PERI, 1) \ + GATE(K210_CLK_AI, K210_SYSCTL_EN_PERI, 2) \ + GATE(K210_CLK_DVP, K210_SYSCTL_EN_PERI, 3) \ + GATE(K210_CLK_FFT, K210_SYSCTL_EN_PERI, 4) \ + GATE(K210_CLK_GPIO, K210_SYSCTL_EN_PERI, 5) \ + GATE(K210_CLK_SPI0, K210_SYSCTL_EN_PERI, 6) \ + GATE(K210_CLK_SPI1, K210_SYSCTL_EN_PERI, 7) \ + GATE(K210_CLK_SPI2, K210_SYSCTL_EN_PERI, 8) \ + GATE(K210_CLK_SPI3, K210_SYSCTL_EN_PERI, 9) \ + GATE(K210_CLK_I2S0, K210_SYSCTL_EN_PERI, 10) \ + GATE(K210_CLK_I2S1, K210_SYSCTL_EN_PERI, 11) \ + GATE(K210_CLK_I2S2, K210_SYSCTL_EN_PERI, 12) \ + GATE(K210_CLK_I2C0, K210_SYSCTL_EN_PERI, 13) \ + GATE(K210_CLK_I2C1, K210_SYSCTL_EN_PERI, 14) \ + GATE(K210_CLK_I2C2, K210_SYSCTL_EN_PERI, 15) \ + GATE(K210_CLK_UART1, K210_SYSCTL_EN_PERI, 16) \ + GATE(K210_CLK_UART2, K210_SYSCTL_EN_PERI, 17) \ + GATE(K210_CLK_UART3, K210_SYSCTL_EN_PERI, 18) \ + GATE(K210_CLK_AES, K210_SYSCTL_EN_PERI, 19) \ + GATE(K210_CLK_FPIOA, K210_SYSCTL_EN_PERI, 20) \ + GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \ + GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \ + GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \ + GATE(K210_CLK_WDT0, K210_SYSCTL_EN_PERI, 24) \ + GATE(K210_CLK_WDT1, K210_SYSCTL_EN_PERI, 25) \ + GATE(K210_CLK_SHA, K210_SYSCTL_EN_PERI, 26) \ + GATE(K210_CLK_OTP, K210_SYSCTL_EN_PERI, 27) \ + GATE(K210_CLK_RTC, K210_SYSCTL_EN_PERI, 29) + +#define _GATEIFY(id) K210_CLK_GATE_##id +#define GATEIFY(id) _GATEIFY(id) + +enum k210_gate_id { +#define GATE(id, ...) GATEIFY(id), + GATE_LIST +#undef GATE + K210_CLK_GATE_NONE, +}; + +static const struct k210_gate_params k210_gates[] = { +#define GATE(id, _off, _idx) \ + [GATEIFY(id)] = { \ + .off = (_off), \ + .bit_idx = (_idx), \ + }, + GATE_LIST +#undef GATE +}; + +#undef GATE_LIST + +/* The most parents is PLL2 */ +#define K210_CLK_MAX_PARENTS 3 + +/** + * struct k210_mux_params - Parameters for muxed clocks + * @parents: A list of parent clock ids + * @num_parents: The number of parent clocks + * @off: The offset of the mux from the base sysctl address + * @shift: The offset of the LSB of the mux selector + * @width: The number of bits in the mux selector + */ +struct k210_mux_params { + u8 parents[K210_CLK_MAX_PARENTS]; + u8 num_parents; + u8 off; + u8 shift; + u8 width; +}; + +#define MUX(id, reg, shift, width) \ + MUX_PARENTS(id, reg, shift, width, K210_CLK_IN0, K210_CLK_PLL0) +#define MUX_LIST \ + MUX_PARENTS(K210_CLK_PLL2, K210_SYSCTL_PLL2, 26, 2, \ + K210_CLK_IN0, K210_CLK_PLL0, K210_CLK_PLL1) \ + MUX(K210_CLK_ACLK, K210_SYSCTL_SEL0, 0, 1) \ + MUX(K210_CLK_SPI3, K210_SYSCTL_SEL0, 12, 1) \ + MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \ + MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \ + MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1) + +#define _MUXIFY(id) K210_CLK_MUX_##id +#define MUXIFY(id) _MUXIFY(id) + +enum k210_mux_id { +#define MUX_PARENTS(id, ...) MUXIFY(id), + MUX_LIST +#undef MUX_PARENTS + K210_CLK_MUX_NONE, +}; + +static const struct k210_mux_params k210_muxes[] = { +#define MUX_PARENTS(id, _off, _shift, _width, ...) \ + [MUXIFY(id)] = { \ + .parents = { __VA_ARGS__ }, \ + .num_parents = __count_args(__VA_ARGS__), \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ + }, + MUX_LIST +#undef MUX_PARENTS +}; + +#undef MUX +#undef MUX_LIST + +/** + * struct k210_pll_params - K210 PLL parameters + * @off: The offset of the PLL from the base sysctl address + * @shift: The offset of the LSB of the lock status + * @width: The number of bits in the lock status + */ +struct k210_pll_params { + u8 off; + u8 shift; + u8 width; +}; + +static const struct k210_pll_params k210_plls[] = { +#define PLL(_off, _shift, _width) { \ + .off = (_off), \ + .shift = (_shift), \ + .width = (_width), \ +} + [0] = PLL(K210_SYSCTL_PLL0, 0, 2), + [1] = PLL(K210_SYSCTL_PLL1, 8, 1), + [2] = PLL(K210_SYSCTL_PLL2, 16, 1), +#undef PLL +}; + +/** + * enum k210_clk_flags - The type of a K210 clock + * @K210_CLKF_MUX: This clock has a mux and not a static parent + * @K210_CLKF_PLL: This clock is a PLL + */ +enum k210_clk_flags { + K210_CLKF_MUX = BIT(0), + K210_CLKF_PLL = BIT(1), +}; + +/** + * struct k210_clk_params - The parameters defining a K210 clock + * @name: The name of the clock + * @flags: A set of &enum k210_clk_flags defining which fields are valid + * @mux: An &enum k210_mux_id of this clock's mux + * @parent: The clock id of this clock's parent + * @pll: The id of the PLL (if this clock is a PLL) + * @div: An &enum k210_div_id of this clock's divider + * @gate: An &enum k210_gate_id of this clock's gate + */ +struct k210_clk_params { +#if CONFIG_IS_ENABLED(CMD_CLK) + const char *name; +#endif + u8 flags; + union { + u8 parent; + u8 mux; + }; + union { + u8 pll; + struct { + u8 div; + u8 gate; + }; + }; +}; + +static const struct k210_clk_params k210_clks[] = { +#if CONFIG_IS_ENABLED(CMD_CLK) +#define NAME(_name) .name = (_name), +#else +#define NAME(name) +#endif +#define CLK(id, _name, _parent, _div, _gate) \ + [id] = { \ + NAME(_name) \ + .parent = (_parent), \ + .div = (_div), \ + .gate = (_gate), \ + } +#define CLK_MUX(id, _name, _mux, _div, _gate) \ + [id] = { \ + NAME(_name) \ + .flags = K210_CLKF_MUX, \ + .mux = (_mux), \ + .div = (_div), \ + .gate = (_gate), \ + } +#define CLK_PLL(id, _pll, _parent) \ + [id] = { \ + NAME("pll" #_pll) \ + .flags = K210_CLKF_PLL, \ + .parent = (_parent), \ + .pll = (_pll), \ + } +#define CLK_FULL(id, name) \ + CLK_MUX(id, name, MUXIFY(id), DIVIFY(id), GATEIFY(id)) +#define CLK_NOMUX(id, name, parent) \ + CLK(id, name, parent, DIVIFY(id), GATEIFY(id)) +#define CLK_DIV(id, name, parent) \ + CLK(id, name, parent, DIVIFY(id), K210_CLK_GATE_NONE) +#define CLK_GATE(id, name, parent) \ + CLK(id, name, parent, K210_CLK_DIV_NONE, GATEIFY(id)) + CLK_PLL(K210_CLK_PLL0, 0, K210_CLK_IN0), + CLK_PLL(K210_CLK_PLL1, 1, K210_CLK_IN0), + [K210_CLK_PLL2] = { + NAME("pll2") + .flags = K210_CLKF_MUX | K210_CLKF_PLL, + .mux = MUXIFY(K210_CLK_PLL2), + .pll = 2, + }, + CLK_MUX(K210_CLK_ACLK, "aclk", MUXIFY(K210_CLK_ACLK), + DIVIFY(K210_CLK_ACLK), K210_CLK_GATE_NONE), + CLK_FULL(K210_CLK_SPI3, "spi3"), + CLK_FULL(K210_CLK_TIMER0, "timer0"), + CLK_FULL(K210_CLK_TIMER1, "timer1"), + CLK_FULL(K210_CLK_TIMER2, "timer2"), + CLK_NOMUX(K210_CLK_SRAM0, "sram0", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_SRAM1, "sram1", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_ROM, "rom", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_DVP, "dvp", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB0, "apb0", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB1, "apb1", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_APB2, "apb2", K210_CLK_ACLK), + CLK_NOMUX(K210_CLK_AI, "ai", K210_CLK_PLL1), + CLK_NOMUX(K210_CLK_I2S0, "i2s0", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_I2S1, "i2s1", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_I2S2, "i2s2", K210_CLK_PLL2), + CLK_NOMUX(K210_CLK_WDT0, "wdt0", K210_CLK_IN0), + CLK_NOMUX(K210_CLK_WDT1, "wdt1", K210_CLK_IN0), + CLK_NOMUX(K210_CLK_SPI0, "spi0", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_SPI1, "spi1", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_SPI2, "spi2", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C0, "i2c0", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C1, "i2c1", K210_CLK_PLL0), + CLK_NOMUX(K210_CLK_I2C2, "i2c2", K210_CLK_PLL0), + CLK_DIV(K210_CLK_I2S0_M, "i2s0_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_I2S1_M, "i2s1_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_I2S2_M, "i2s2_m", K210_CLK_PLL2), + CLK_DIV(K210_CLK_CLINT, "clint", K210_CLK_ACLK), + CLK_GATE(K210_CLK_CPU, "cpu", K210_CLK_ACLK), + CLK_GATE(K210_CLK_DMA, "dma", K210_CLK_ACLK), + CLK_GATE(K210_CLK_FFT, "fft", K210_CLK_ACLK), + CLK_GATE(K210_CLK_GPIO, "gpio", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART1, "uart1", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART2, "uart2", K210_CLK_APB0), + CLK_GATE(K210_CLK_UART3, "uart3", K210_CLK_APB0), + CLK_GATE(K210_CLK_FPIOA, "fpioa", K210_CLK_APB0), + CLK_GATE(K210_CLK_SHA, "sha", K210_CLK_APB0), + CLK_GATE(K210_CLK_AES, "aes", K210_CLK_APB1), + CLK_GATE(K210_CLK_OTP, "otp", K210_CLK_APB1), + CLK_GATE(K210_CLK_RTC, "rtc", K210_CLK_IN0), +#undef NAME +#undef CLK_PLL +#undef CLK +#undef CLK_FULL +#undef CLK_NOMUX +#undef CLK_DIV +#undef CLK_GATE +#undef CLK_LIST +}; + +#define K210_PLL_CLKR GENMASK(3, 0) +#define K210_PLL_CLKF GENMASK(9, 4) +#define K210_PLL_CLKOD GENMASK(13, 10) /* Output Divider */ +#define K210_PLL_BWADJ GENMASK(19, 14) /* BandWidth Adjust */ +#define K210_PLL_RESET BIT(20) +#define K210_PLL_PWRD BIT(21) /* PoWeReD */ +#define K210_PLL_INTFB BIT(22) /* Internal FeedBack */ +#define K210_PLL_BYPASS BIT(23) +#define K210_PLL_TEST BIT(24) +#define K210_PLL_EN BIT(25) +#define K210_PLL_TEST_EN BIT(26) + +#define K210_PLL_LOCK 0 +#define K210_PLL_CLEAR_SLIP 2 +#define K210_PLL_TEST_OUT 3 + +#ifdef CONFIG_CLK_K210_SET_RATE +static int k210_pll_enable(struct k210_clk_priv *priv, int id); +static int k210_pll_disable(struct k210_clk_priv *priv, int id); +static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, ulong rate_in); + +/* + * The PLL included with the Kendryte K210 appears to be a True Circuits, Inc. + * General-Purpose PLL. The logical layout of the PLL with internal feedback is + * approximately the following: + * + * +---------------+ + * |reference clock| + * +---------------+ + * | + * v + * +--+ + * |/r| + * +--+ + * | + * v + * +-------------+ + * |divided clock| + * +-------------+ + * | + * v + * +--------------+ + * |phase detector|<---+ + * +--------------+ | + * | | + * v +--------------+ + * +---+ |feedback clock| + * |VCO| +--------------+ + * +---+ ^ + * | +--+ | + * +--->|/f|---+ + * | +--+ + * v + * +---+ + * |/od| + * +---+ + * | + * v + * +------+ + * |output| + * +------+ + * + * The k210 PLLs have three factors: r, f, and od. Because of the feedback mode, + * the effect of the division by f is to multiply the input frequency. The + * equation for the output rate is + * rate = (rate_in * f) / (r * od). + * Moving knowns to one side of the equation, we get + * rate / rate_in = f / (r * od) + * Rearranging slightly, + * abs_error = abs((rate / rate_in) - (f / (r * od))). + * To get relative, error, we divide by the expected ratio + * error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in). + * Simplifying, + * error = abs(1 - f / (r * od)) / (rate / rate_in) + * error = abs(1 - (f * rate_in) / (r * od * rate)) + * Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate, + * error = abs((f * inv_ratio) / (r * od) - 1) + * This is the error used in evaluating parameters. + * + * r and od are four bits each, while f is six bits. Because r and od are + * multiplied together, instead of the full 256 values possible if both bits + * were used fully, there are only 97 distinct products. Combined with f, there + * are 6208 theoretical settings for the PLL. However, most of these settings + * can be ruled out immediately because they do not have the correct ratio. + * + * In addition to the constraint of approximating the desired ratio, parameters + * must also keep internal pll frequencies within acceptable ranges. The divided + * clock's minimum and maximum frequencies have a ratio of around 128. This + * leaves fairly substantial room to work with, especially since the only + * affected parameter is r. The VCO's minimum and maximum frequency have a ratio + * of 5, which is considerably more restrictive. + * + * The r and od factors are stored in a table. This is to make it easy to find + * the next-largest product. Some products have multiple factorizations, but + * only when one factor has at least a 2.5x ratio to the factors of the other + * factorization. This is because any smaller ratio would not make a difference + * when ensuring the VCO's frequency is within spec. + * + * Throughout the calculation function, fixed point arithmetic is used. Because + * the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit + * 32.32 fixed-point numbers are used to represent ratios. In general, to + * implement division, the numerator is first multiplied by 2^32. This gives a + * result where the whole number part is in the upper 32 bits, and the fraction + * is in the lower 32 bits. + * + * In general, rounding is done to the closest integer. This helps find the best + * approximation for the ratio. Rounding in one direction (e.g down) could cause + * the function to miss a better ratio with one of the parameters increased by + * one. + */ + +/* + * The factors table was generated with the following python code: + * + * def p(x, y): + * return (1.0*x/y > 2.5) or (1.0*y/x > 2.5) + * + * factors = {} + * for i in range(1, 17): + * for j in range(1, 17): + * fs = factors.get(i*j) or [] + * if fs == [] or all([ + * (p(i, x) and p(i, y)) or (p(j, x) and p(j, y)) + * for (x, y) in fs]): + * fs.append((i, j)) + * factors[i*j] = fs + * + * for k, l in sorted(factors.items()): + * for v in l: + * print("PACK(%s, %s)," % v) + */ +#define PACK(r, od) (((((r) - 1) & 0xF) << 4) | (((od) - 1) & 0xF)) +#define UNPACK_R(val) ((((val) >> 4) & 0xF) + 1) +#define UNPACK_OD(val) (((val) & 0xF) + 1) +static const u8 factors[] = { + PACK(1, 1), + PACK(1, 2), + PACK(1, 3), + PACK(1, 4), + PACK(1, 5), + PACK(1, 6), + PACK(1, 7), + PACK(1, 8), + PACK(1, 9), + PACK(3, 3), + PACK(1, 10), + PACK(1, 11), + PACK(1, 12), + PACK(3, 4), + PACK(1, 13), + PACK(1, 14), + PACK(1, 15), + PACK(3, 5), + PACK(1, 16), + PACK(4, 4), + PACK(2, 9), + PACK(2, 10), + PACK(3, 7), + PACK(2, 11), + PACK(2, 12), + PACK(5, 5), + PACK(2, 13), + PACK(3, 9), + PACK(2, 14), + PACK(2, 15), + PACK(2, 16), + PACK(3, 11), + PACK(5, 7), + PACK(3, 12), + PACK(3, 13), + PACK(4, 10), + PACK(3, 14), + PACK(4, 11), + PACK(3, 15), + PACK(3, 16), + PACK(7, 7), + PACK(5, 10), + PACK(4, 13), + PACK(6, 9), + PACK(5, 11), + PACK(4, 14), + PACK(4, 15), + PACK(7, 9), + PACK(4, 16), + PACK(5, 13), + PACK(6, 11), + PACK(5, 14), + PACK(6, 12), + PACK(5, 15), + PACK(7, 11), + PACK(6, 13), + PACK(5, 16), + PACK(9, 9), + PACK(6, 14), + PACK(8, 11), + PACK(6, 15), + PACK(7, 13), + PACK(6, 16), + PACK(7, 14), + PACK(9, 11), + PACK(10, 10), + PACK(8, 13), + PACK(7, 15), + PACK(9, 12), + PACK(10, 11), + PACK(7, 16), + PACK(9, 13), + PACK(8, 15), + PACK(11, 11), + PACK(9, 14), + PACK(8, 16), + PACK(10, 13), + PACK(11, 12), + PACK(9, 15), + PACK(10, 14), + PACK(11, 13), + PACK(9, 16), + PACK(10, 15), + PACK(11, 14), + PACK(12, 13), + PACK(10, 16), + PACK(11, 15), + PACK(12, 14), + PACK(13, 13), + PACK(11, 16), + PACK(12, 15), + PACK(13, 14), + PACK(12, 16), + PACK(13, 15), + PACK(14, 14), + PACK(13, 16), + PACK(14, 15), + PACK(14, 16), + PACK(15, 15), + PACK(15, 16), + PACK(16, 16), +}; + +TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, + struct k210_pll_config *best) +{ + int i; + s64 error, best_error; + u64 ratio, inv_ratio; /* fixed point 32.32 ratio of the rates */ + u64 max_r; + u64 r, f, od; + + /* + * Can't go over 1.75 GHz or under 21.25 MHz due to limitations on the + * VCO frequency. These are not the same limits as below because od can + * reduce the output frequency by 16. + */ + if (rate > 1750000000 || rate < 21250000) + return -EINVAL; + + /* Similar restrictions on the input rate */ + if (rate_in > 1750000000 || rate_in < 13300000) + return -EINVAL; + + ratio = DIV_ROUND_CLOSEST_ULL((u64)rate << 32, rate_in); + inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate); + /* Can't increase by more than 64 or reduce by more than 256 */ + if (rate > rate_in && ratio > (64ULL << 32)) + return -EINVAL; + else if (rate <= rate_in && inv_ratio > (256ULL << 32)) + return -EINVAL; + + /* + * The divided clock (rate_in / r) must stay between 1.75 GHz and 13.3 + * MHz. There is no minimum, since the only way to get a higher input + * clock than 26 MHz is to use a clock generated by a PLL. Because PLLs + * cannot output frequencies greater than 1.75 GHz, the minimum would + * never be greater than one. + */ + max_r = DIV_ROUND_DOWN_ULL(rate_in, 13300000); + + /* Variables get immediately incremented, so start at -1th iteration */ + i = -1; + f = 0; + r = 0; + od = 0; + best_error = S64_MAX; + error = best_error; + /* do-while here so we always try at least one ratio */ + do { + /* + * Whether we swapped r and od while enforcing frequency limits + */ + bool swapped = false; + u64 last_od = od; + u64 last_r = r; + + /* + * Try the next largest value for f (or r and od) and + * recalculate the other parameters based on that + */ + if (rate > rate_in) { + /* + * Skip factors of the same product if we already tried + * out that product + */ + do { + i++; + r = UNPACK_R(factors[i]); + od = UNPACK_OD(factors[i]); + } while (i + 1 < ARRAY_SIZE(factors) && + r * od == last_r * last_od); + + /* Round close */ + f = (r * od * ratio + BIT(31)) >> 32; + if (f > 64) + f = 64; + } else { + u64 tmp = ++f * inv_ratio; + bool round_up = !!(tmp & BIT(31)); + u32 goal = (tmp >> 32) + round_up; + u32 err, last_err; + + /* Get the next r/od pair in factors */ + while (r * od < goal && i + 1 < ARRAY_SIZE(factors)) { + i++; + r = UNPACK_R(factors[i]); + od = UNPACK_OD(factors[i]); + } + + /* + * This is a case of double rounding. If we rounded up + * above, we need to round down (in cases of ties) here. + * This prevents off-by-one errors resulting from + * choosing X+2 over X when X.Y rounds up to X+1 and + * there is no r * od = X+1. For the converse, when X.Y + * is rounded down to X, we should choose X+1 over X-1. + */ + err = abs(r * od - goal); + last_err = abs(last_r * last_od - goal); + if (last_err < err || (round_up && last_err == err)) { + i--; + r = last_r; + od = last_od; + } + } + + /* + * Enforce limits on internal clock frequencies. If we + * aren't in spec, try swapping r and od. If everything is + * in-spec, calculate the relative error. + */ + while (true) { + /* + * Whether the intermediate frequencies are out-of-spec + */ + bool out_of_spec = false; + + if (r > max_r) { + out_of_spec = true; + } else { + /* + * There is no way to only divide once; we need + * to examine the frequency with and without the + * effect of od. + */ + u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r); + + if (vco > 1750000000 || vco < 340000000) + out_of_spec = true; + } + + if (out_of_spec) { + if (!swapped) { + u64 tmp = r; + + r = od; + od = tmp; + swapped = true; + continue; + } else { + /* + * Try looking ahead to see if there are + * additional factors for the same + * product. + */ + if (i + 1 < ARRAY_SIZE(factors)) { + u64 new_r, new_od; + + i++; + new_r = UNPACK_R(factors[i]); + new_od = UNPACK_OD(factors[i]); + if (r * od == new_r * new_od) { + r = new_r; + od = new_od; + swapped = false; + continue; + } + i--; + } + break; + } + } + + error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, r * od); + /* The lower 16 bits are spurious */ + error = abs((error - BIT(32))) >> 16; + + if (error < best_error) { + best->r = r; + best->f = f; + best->od = od; + best_error = error; + } + break; + } + } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0); + + if (best_error == S64_MAX) + return -EINVAL; + + log_debug("best error %lld\n", best_error); + return 0; +} + +static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, + ulong rate_in) +{ + int err; + const struct k210_pll_params *pll = &k210_plls[id]; + struct k210_pll_config config = {}; + u32 reg; + ulong calc_rate; + + if (rate_in < 0) + return rate_in; + + err = k210_pll_calc_config(rate, rate_in, &config); + if (err) + return err; + log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od); + + /* Don't bother setting the rate if we're already at that rate */ + calc_rate = DIV_ROUND_DOWN_ULL(((u64)rate_in) * config.f, + config.r * config.od); + if (calc_rate == k210_pll_get_rate(priv, id, rate)) + return calc_rate; + + k210_pll_disable(priv, id); + + reg = readl(priv->base + pll->off); + reg &= ~K210_PLL_CLKR + & ~K210_PLL_CLKF + & ~K210_PLL_CLKOD + & ~K210_PLL_BWADJ; + reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1) + | FIELD_PREP(K210_PLL_CLKF, config.f - 1) + | FIELD_PREP(K210_PLL_CLKOD, config.od - 1) + | FIELD_PREP(K210_PLL_BWADJ, config.f - 1); + writel(reg, priv->base + pll->off); + + k210_pll_enable(priv, id); + + serial_setbrg(); + return k210_pll_get_rate(priv, id, rate); +} +#else +static ulong k210_pll_set_rate(struct k210_clk_priv *priv, int id, ulong rate, + ulong rate_in) +{ + return -ENOSYS; +} +#endif /* CONFIG_CLK_K210_SET_RATE */ + +static ulong k210_pll_get_rate(struct k210_clk_priv *priv, int id, + ulong rate_in) +{ + u64 r, f, od; + u32 reg = readl(priv->base + k210_plls[id].off); + + if (rate_in < 0 || (reg & K210_PLL_BYPASS)) + return rate_in; + + if (!(reg & K210_PLL_PWRD)) + return 0; + + r = FIELD_GET(K210_PLL_CLKR, reg) + 1; + f = FIELD_GET(K210_PLL_CLKF, reg) + 1; + od = FIELD_GET(K210_PLL_CLKOD, reg) + 1; + + return DIV_ROUND_DOWN_ULL(((u64)rate_in) * f, r * od); +} + +/* + * Wait for the PLL to be locked. If the PLL is not locked, try clearing the + * slip before retrying + */ +static void k210_pll_waitfor_lock(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 mask = (BIT(pll->width) - 1) << pll->shift; + + while (true) { + u32 reg = readl(priv->base + K210_SYSCTL_PLL_LOCK); + + if ((reg & mask) == mask) + break; + + reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP); + writel(reg, priv->base + K210_SYSCTL_PLL_LOCK); + } +} + +static bool k210_pll_enabled(u32 reg) +{ + return (reg & K210_PLL_PWRD) && (reg & K210_PLL_EN) && + !(reg & K210_PLL_RESET); +} + +/* Adapted from sysctl_pll_enable */ +static int k210_pll_enable(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 reg = readl(priv->base + pll->off); + + if (k210_pll_enabled(reg)) + return 0; + + reg |= K210_PLL_PWRD; + writel(reg, priv->base + pll->off); + + /* Ensure reset is low before asserting it */ + reg &= ~K210_PLL_RESET; + writel(reg, priv->base + pll->off); + reg |= K210_PLL_RESET; + writel(reg, priv->base + pll->off); + nop(); + nop(); + reg &= ~K210_PLL_RESET; + writel(reg, priv->base + pll->off); + + k210_pll_waitfor_lock(priv, id); + + reg &= ~K210_PLL_BYPASS; + reg |= K210_PLL_EN; + writel(reg, priv->base + pll->off); + + return 0; +} + +static int k210_pll_disable(struct k210_clk_priv *priv, int id) +{ + const struct k210_pll_params *pll = &k210_plls[id]; + u32 reg = readl(priv->base + pll->off); + + /* + * Bypassing before powering off is important so child clocks don't stop + * working. This is especially important for pll0, the indirect parent + * of the cpu clock. + */ + reg |= K210_PLL_BYPASS; + writel(reg, priv->base + pll->off); + + reg &= ~K210_PLL_PWRD; + reg &= ~K210_PLL_EN; + writel(reg, priv->base + pll->off); + return 0; +} + +static u32 k210_clk_readl(struct k210_clk_priv *priv, u8 off, u8 shift, + u8 width) +{ + u32 reg = readl(priv->base + off); + + return (reg >> shift) & (BIT(width) - 1); +} + +static void k210_clk_writel(struct k210_clk_priv *priv, u8 off, u8 shift, + u8 width, u32 val) +{ + u32 reg = readl(priv->base + off); + u32 mask = (BIT(width) - 1) << shift; + + reg &= ~mask; + reg |= mask & (val << shift); + writel(reg, priv->base + off); +} + +static int k210_clk_get_parent(struct k210_clk_priv *priv, int id) +{ + u32 sel; + const struct k210_mux_params *mux; + + if (!(k210_clks[id].flags & K210_CLKF_MUX)) + return k210_clks[id].parent; + mux = &k210_muxes[k210_clks[id].mux]; + + sel = k210_clk_readl(priv, mux->off, mux->shift, mux->width); + assert(sel < mux->num_parents); + return mux->parents[sel]; +} + +static ulong do_k210_clk_get_rate(struct k210_clk_priv *priv, int id) +{ + int parent; + u32 val; + ulong parent_rate; + const struct k210_div_params *div; + + if (id == K210_CLK_IN0) + return clk_get_rate(&priv->in0); + + parent = k210_clk_get_parent(priv, id); + parent_rate = do_k210_clk_get_rate(priv, parent); + + if (k210_clks[id].flags & K210_CLKF_PLL) + return k210_pll_get_rate(priv, k210_clks[id].pll, parent_rate); + + if (k210_clks[id].div == K210_CLK_DIV_NONE) + return parent_rate; + div = &k210_divs[k210_clks[id].div]; + + if (div->type == K210_DIV_FIXED) + return parent_rate / div->div; + + val = k210_clk_readl(priv, div->off, div->shift, div->width); + switch (div->type) { + case K210_DIV_ONE: + return parent_rate / (val + 1); + case K210_DIV_EVEN: + return parent_rate / 2 / (val + 1); + case K210_DIV_POWER: + /* This is ACLK, which has no divider on IN0 */ + if (parent == K210_CLK_IN0) + return parent_rate; + return parent_rate / (2 << val); + default: + assert(false); + return -EINVAL; + }; +} + +static ulong k210_clk_get_rate(struct clk *clk) +{ + return do_k210_clk_get_rate(dev_get_priv(clk->dev), clk->id); +} + +static int do_k210_clk_set_parent(struct k210_clk_priv *priv, int id, int new) +{ + int i; + const struct k210_mux_params *mux; + + if (!(k210_clks[id].flags & K210_CLKF_MUX)) + return -ENOSYS; + mux = &k210_muxes[k210_clks[id].mux]; + + for (i = 0; i < mux->num_parents; i++) { + if (mux->parents[i] == new) { + k210_clk_writel(priv, mux->off, mux->shift, mux->width, + i); + return 0; + } + } + return -EINVAL; +} + +static int k210_clk_set_parent(struct clk *clk, struct clk *parent) +{ + return do_k210_clk_set_parent(dev_get_priv(clk->dev), clk->id, + parent->id); +} + +static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate) +{ + int parent, ret, err; + ulong rate_in, val; + const struct k210_div_params *div; + struct k210_clk_priv *priv = dev_get_priv(clk->dev); + + if (clk->id == K210_CLK_IN0) + return clk_set_rate(&priv->in0, rate); + + parent = k210_clk_get_parent(priv, clk->id); + rate_in = do_k210_clk_get_rate(priv, parent); + + log_debug("id=%ld rate=%lu rate_in=%lu\n", clk->id, rate, rate_in); + + if (clk->id == K210_CLK_PLL0) { + /* Bypass ACLK so the CPU keeps going */ + ret = do_k210_clk_set_parent(priv, K210_CLK_ACLK, K210_CLK_IN0); + if (ret) + return ret; + } else if (clk->id == K210_CLK_PLL1 && gd->flags & GD_FLG_RELOC) { + /* + * We can't bypass the AI clock like we can ACLK, and after + * relocation we are using the AI ram. + */ + return -EPERM; + } + + if (k210_clks[clk->id].flags & K210_CLKF_PLL) { + ret = k210_pll_set_rate(priv, k210_clks[clk->id].pll, rate, + rate_in); + if (!IS_ERR_VALUE(ret) && clk->id == K210_CLK_PLL0) { + /* + * This may have the side effect of reparenting ACLK, + * but I don't really want to keep track of what the old + * parent was. + */ + err = do_k210_clk_set_parent(priv, K210_CLK_ACLK, + K210_CLK_PLL0); + if (err) + return err; + } + return ret; + } + + if (k210_clks[clk->id].div == K210_CLK_DIV_NONE) + return -ENOSYS; + div = &k210_divs[k210_clks[clk->id].div]; + + switch (div->type) { + case K210_DIV_ONE: + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); + val = val ? val - 1 : 0; + break; + case K210_DIV_EVEN: + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, 2 * rate); + break; + case K210_DIV_POWER: + /* This is ACLK, which has no divider on IN0 */ + if (parent == K210_CLK_IN0) + return -ENOSYS; + + val = DIV_ROUND_CLOSEST_ULL((u64)rate_in, rate); + val = __ffs(val); + break; + default: + assert(false); + return -EINVAL; + }; + + val = val ? val - 1 : 0; + k210_clk_writel(priv, div->off, div->shift, div->width, val); + return do_k210_clk_get_rate(priv, clk->id); +} + +static int k210_clk_endisable(struct k210_clk_priv *priv, int id, bool enable) +{ + int parent = k210_clk_get_parent(priv, id); + const struct k210_gate_params *gate; + + if (id == K210_CLK_IN0) { + if (enable) + return clk_enable(&priv->in0); + else + return clk_disable(&priv->in0); + } + + /* Only recursively enable clocks since we don't track refcounts */ + if (enable) { + int ret = k210_clk_endisable(priv, parent, true); + + if (ret && ret != -ENOSYS) + return ret; + } + + if (k210_clks[id].flags & K210_CLKF_PLL) { + if (enable) + return k210_pll_enable(priv, k210_clks[id].pll); + else + return k210_pll_disable(priv, k210_clks[id].pll); + } + + if (k210_clks[id].gate == K210_CLK_GATE_NONE) + return -ENOSYS; + gate = &k210_gates[k210_clks[id].gate]; + + k210_clk_writel(priv, gate->off, gate->bit_idx, 1, enable); + return 0; +} + +static int k210_clk_enable(struct clk *clk) +{ + return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, true); +} + +static int k210_clk_disable(struct clk *clk) +{ + return k210_clk_endisable(dev_get_priv(clk->dev), clk->id, false); +} + +static int k210_clk_request(struct clk *clk) +{ + if (clk->id >= ARRAY_SIZE(k210_clks)) + return -EINVAL; + return 0; +} + +static const struct clk_ops k210_clk_ops = { + .request = k210_clk_request, + .set_rate = k210_clk_set_rate, + .get_rate = k210_clk_get_rate, + .set_parent = k210_clk_set_parent, + .enable = k210_clk_enable, + .disable = k210_clk_disable, +}; + +static int k210_clk_probe(struct udevice *dev) +{ + int ret; + struct k210_clk_priv *priv = dev_get_priv(dev); + + priv->base = dev_read_addr_ptr(dev_get_parent(dev)); + if (!priv->base) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &priv->in0); + if (ret) + return ret; + + /* + * Force setting defaults, even before relocation. This is so we can + * set the clock rate for PLL1 before we relocate into aisram. + */ + if (!(gd->flags & GD_FLG_RELOC)) + clk_set_defaults(dev, CLK_DEFAULTS_POST_FORCE); + + return 0; +} + +static const struct udevice_id k210_clk_ids[] = { + { .compatible = "kendryte,k210-clk" }, + { }, +}; + +U_BOOT_DRIVER(k210_clk) = { + .name = "k210_clk", + .id = UCLASS_CLK, + .of_match = k210_clk_ids, + .ops = &k210_clk_ops, + .probe = k210_clk_probe, + .priv_auto = sizeof(struct k210_clk_priv), +}; + +#if CONFIG_IS_ENABLED(CMD_CLK) +static char show_enabled(struct k210_clk_priv *priv, int id) +{ + bool enabled; + + if (k210_clks[id].flags & K210_CLKF_PLL) { + const struct k210_pll_params *pll = + &k210_plls[k210_clks[id].pll]; + + enabled = k210_pll_enabled(readl(priv->base + pll->off)); + } else if (k210_clks[id].gate == K210_CLK_GATE_NONE) { + return '-'; + } else { + const struct k210_gate_params *gate = + &k210_gates[k210_clks[id].gate]; + + enabled = k210_clk_readl(priv, gate->off, gate->bit_idx, 1); + } + + return enabled ? 'y' : 'n'; +} + +static void show_clks(struct k210_clk_priv *priv, int id, int depth) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(k210_clks); i++) { + if (k210_clk_get_parent(priv, i) != id) + continue; + + printf(" %-9lu %-7c %*s%s\n", do_k210_clk_get_rate(priv, i), + show_enabled(priv, i), depth * 4, "", + k210_clks[i].name); + + show_clks(priv, i, depth + 1); + } +} + +int soc_clk_dump(void) +{ + int ret; + struct udevice *dev; + struct k210_clk_priv *priv; + + ret = uclass_get_device_by_driver(UCLASS_CLK, DM_DRIVER_GET(k210_clk), + &dev); + if (ret) + return ret; + priv = dev_get_priv(dev); + + puts(" Rate Enabled Name\n"); + puts("------------------------\n"); + printf(" %-9lu %-7c %*s%s\n", clk_get_rate(&priv->in0), 'y', 0, "", + priv->in0.dev->name); + show_clks(priv, K210_CLK_IN0, 1); + return 0; +} +#endif diff --git a/drivers/clk/kendryte/Kconfig b/drivers/clk/kendryte/Kconfig deleted file mode 100644 index 073fca0781..0000000000 --- a/drivers/clk/kendryte/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config CLK_K210 - bool "Clock support for Kendryte K210" - depends on CLK && CLK_CCF && CLK_COMPOSITE_CCF - help - This enables support clock driver for Kendryte K210 platforms. - -config CLK_K210_SET_RATE - bool "Enable setting the Kendryte K210 PLL rate" - depends on CLK_K210 - help - Add functionality to calculate new rates for K210 PLLs. Enabling this - feature adds around 1K to U-Boot's final size. diff --git a/drivers/clk/kendryte/Makefile b/drivers/clk/kendryte/Makefile deleted file mode 100644 index 6fb68253ae..0000000000 --- a/drivers/clk/kendryte/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-y += bypass.o clk.o pll.o diff --git a/drivers/clk/kendryte/bypass.c b/drivers/clk/kendryte/bypass.c deleted file mode 100644 index bbdbd9a10d..0000000000 --- a/drivers/clk/kendryte/bypass.c +++ /dev/null @@ -1,273 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> - */ - -#define LOG_CATEGORY UCLASS_CLK - -#include <common.h> -#include <clk.h> -#include <clk-uclass.h> -#include <dm.h> -#include <log.h> -#include <kendryte/bypass.h> -#include <linux/clk-provider.h> -#include <linux/err.h> - -#define CLK_K210_BYPASS "k210_clk_bypass" - -/* - * This is a small driver to do a software bypass of a clock if hardware bypass - * is not working. I have tried to write this in a generic fashion, so that it - * could be potentially broken out of the kendryte code at some future date. - * - * Say you have the following clock configuration - * - * +---+ +---+ - * |osc| |pll| - * +---+ +---+ - * ^ - * /| - * / | - * / | - * / | - * / | - * +---+ +---+ - * |clk| |clk| - * +---+ +---+ - * - * But the pll does not have a bypass, so when you configure the pll, the - * configuration needs to change to look like - * - * +---+ +---+ - * |osc| |pll| - * +---+ +---+ - * ^ - * |\ - * | \ - * | \ - * | \ - * | \ - * +---+ +---+ - * |clk| |clk| - * +---+ +---+ - * - * To set this up, create a bypass clock with bypassee=pll and alt=osc. When - * creating the child clocks, set their parent to the bypass clock. After - * creating all the children, call k210_bypass_setchildren(). - */ - -static int k210_bypass_dobypass(struct k210_bypass *bypass) -{ - int ret, i; - - /* - * If we already have saved parents, then the children are already - * bypassed - */ - if (bypass->child_count && bypass->saved_parents[0]) - return 0; - - for (i = 0; i < bypass->child_count; i++) { - struct clk *child = bypass->children[i]; - struct clk *parent = clk_get_parent(child); - - if (IS_ERR(parent)) { - for (; i; i--) - bypass->saved_parents[i] = NULL; - return PTR_ERR(parent); - } - bypass->saved_parents[i] = parent; - } - - for (i = 0; i < bypass->child_count; i++) { - struct clk *child = bypass->children[i]; - - ret = clk_set_parent(child, bypass->alt); - if (ret) { - for (; i; i--) - clk_set_parent(bypass->children[i], - bypass->saved_parents[i]); - for (i = 0; i < bypass->child_count; i++) - bypass->saved_parents[i] = NULL; - return ret; - } - } - - return 0; -} - -static int k210_bypass_unbypass(struct k210_bypass *bypass) -{ - int err, ret, i; - - if (!bypass->child_count && !bypass->saved_parents[0]) { - log_warning("Cannot unbypass children; dobypass not called first\n"); - return 0; - } - - ret = 0; - for (i = 0; i < bypass->child_count; i++) { - err = clk_set_parent(bypass->children[i], - bypass->saved_parents[i]); - if (err) - ret = err; - bypass->saved_parents[i] = NULL; - } - return ret; -} - -static ulong k210_bypass_get_rate(struct clk *clk) -{ - struct k210_bypass *bypass = to_k210_bypass(clk); - const struct clk_ops *ops = bypass->bypassee_ops; - - if (ops->get_rate) - return ops->get_rate(bypass->bypassee); - else - return clk_get_parent_rate(bypass->bypassee); -} - -static ulong k210_bypass_set_rate(struct clk *clk, unsigned long rate) -{ - int ret; - struct k210_bypass *bypass = to_k210_bypass(clk); - const struct clk_ops *ops = bypass->bypassee_ops; - - /* Don't bother bypassing if we aren't going to set the rate */ - if (!ops->set_rate) - return k210_bypass_get_rate(clk); - - ret = k210_bypass_dobypass(bypass); - if (ret) - return ret; - - ret = ops->set_rate(bypass->bypassee, rate); - if (ret < 0) - return ret; - - return k210_bypass_unbypass(bypass); -} - -static int k210_bypass_set_parent(struct clk *clk, struct clk *parent) -{ - struct k210_bypass *bypass = to_k210_bypass(clk); - const struct clk_ops *ops = bypass->bypassee_ops; - - if (ops->set_parent) - return ops->set_parent(bypass->bypassee, parent); - else - return -EINVAL; -} - -/* - * For these next two functions, do the bypassing even if there is no - * en-/-disable function, since the bypassing itself can be observed in between - * calls. - */ -static int k210_bypass_enable(struct clk *clk) -{ - int ret; - struct k210_bypass *bypass = to_k210_bypass(clk); - const struct clk_ops *ops = bypass->bypassee_ops; - - ret = k210_bypass_dobypass(bypass); - if (ret) - return ret; - - if (ops->enable) - ret = ops->enable(bypass->bypassee); - else - ret = 0; - if (ret) - return ret; - - return k210_bypass_unbypass(bypass); -} - -static int k210_bypass_disable(struct clk *clk) -{ - int ret; - struct k210_bypass *bypass = to_k210_bypass(clk); - const struct clk_ops *ops = bypass->bypassee_ops; - - ret = k210_bypass_dobypass(bypass); - if (ret) - return ret; - - if (ops->disable) - return ops->disable(bypass->bypassee); - else - return 0; -} - -static const struct clk_ops k210_bypass_ops = { - .get_rate = k210_bypass_get_rate, - .set_rate = k210_bypass_set_rate, - .set_parent = k210_bypass_set_parent, - .enable = k210_bypass_enable, - .disable = k210_bypass_disable, -}; - -int k210_bypass_set_children(struct clk *clk, struct clk **children, - size_t child_count) -{ - struct k210_bypass *bypass = to_k210_bypass(clk); - - kfree(bypass->saved_parents); - if (child_count) { - bypass->saved_parents = - kcalloc(child_count, sizeof(struct clk *), GFP_KERNEL); - if (!bypass->saved_parents) - return -ENOMEM; - } - bypass->child_count = child_count; - bypass->children = children; - - return 0; -} - -struct clk *k210_register_bypass_struct(const char *name, - const char *parent_name, - struct k210_bypass *bypass) -{ - int ret; - struct clk *clk; - - clk = &bypass->clk; - - ret = clk_register(clk, CLK_K210_BYPASS, name, parent_name); - if (ret) - return ERR_PTR(ret); - - bypass->bypassee->dev = clk->dev; - return clk; -} - -struct clk *k210_register_bypass(const char *name, const char *parent_name, - struct clk *bypassee, - const struct clk_ops *bypassee_ops, - struct clk *alt) -{ - struct clk *clk; - struct k210_bypass *bypass; - - bypass = kzalloc(sizeof(*bypass), GFP_KERNEL); - if (!bypass) - return ERR_PTR(-ENOMEM); - - bypass->bypassee = bypassee; - bypass->bypassee_ops = bypassee_ops; - bypass->alt = alt; - - clk = k210_register_bypass_struct(name, parent_name, bypass); - if (IS_ERR(clk)) - kfree(bypass); - return clk; -} - -U_BOOT_DRIVER(k210_bypass) = { - .name = CLK_K210_BYPASS, - .id = UCLASS_CLK, - .ops = &k210_bypass_ops, -}; diff --git a/drivers/clk/kendryte/clk.c b/drivers/clk/kendryte/clk.c deleted file mode 100644 index 41c712e03f..0000000000 --- a/drivers/clk/kendryte/clk.c +++ /dev/null @@ -1,668 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com> - */ -#include <kendryte/clk.h> - -#include <asm/io.h> -#include <dt-bindings/clock/k210-sysctl.h> -#include <dt-bindings/mfd/k210-sysctl.h> -#include <dm.h> -#include <log.h> -#include <mapmem.h> - -#include <kendryte/bypass.h> -#include <kendryte/pll.h> - -/* All methods are delegated to CCF clocks */ - -static ulong k210_clk_get_rate(struct clk *clk) -{ - struct clk *c; - int err = clk_get_by_id(clk->id, &c); - - if (err) - return err; - return clk_get_rate(c); -} - -static ulong k210_clk_set_rate(struct clk *clk, unsigned long rate) -{ - struct clk *c; - int err = clk_get_by_id(clk->id, &c); - - if (err) - return err; - return clk_set_rate(c, rate); -} - -static int k210_clk_set_parent(struct clk *clk, struct clk *parent) -{ - struct clk *c, *p; - int err = clk_get_by_id(clk->id, &c); - - if (err) - return err; - - err = clk_get_by_id(parent->id, &p); - if (err) - return err; - - return clk_set_parent(c, p); -} - -static int k210_clk_endisable(struct clk *clk, bool enable) -{ - struct clk *c; - int err = clk_get_by_id(clk->id, &c); - - if (err) - return err; - return enable ? clk_enable(c) : clk_disable(c); -} - -static int k210_clk_enable(struct clk *clk) -{ - return k210_clk_endisable(clk, true); -} - -static int k210_clk_disable(struct clk *clk) -{ - return k210_clk_endisable(clk, false); -} - -static const struct clk_ops k210_clk_ops = { - .set_rate = k210_clk_set_rate, - .get_rate = k210_clk_get_rate, - .set_parent = k210_clk_set_parent, - .enable = k210_clk_enable, - .disable = k210_clk_disable, -}; - -/* Parents for muxed clocks */ -static const char * const generic_sels[] = { "in0_half", "pll0_half" }; -/* The first clock is in0, which is filled in by k210_clk_probe */ -static const char *aclk_sels[] = { NULL, "pll0_half" }; -static const char *pll2_sels[] = { NULL, "pll0", "pll1" }; - -/* - * All parameters for different sub-clocks are collected into parameter arrays. - * These parameters are then initialized by the clock which uses them during - * probe. To save space, ids are automatically generated for each sub-clock by - * using an enum. Instead of storing a parameter struct for each clock, even for - * those clocks which don't use a particular type of sub-clock, we can just - * store the parameters for the clocks which need them. - * - * So why do it like this? Arranging all the sub-clocks together makes it very - * easy to find bugs in the code. - */ - -#define DIV(id, off, shift, width) DIV_FLAGS(id, off, shift, width, 0) -#define DIV_LIST \ - DIV_FLAGS(K210_CLK_ACLK, K210_SYSCTL_SEL0, 1, 2, \ - CLK_DIVIDER_POWER_OF_TWO) \ - DIV(K210_CLK_APB0, K210_SYSCTL_SEL0, 3, 3) \ - DIV(K210_CLK_APB1, K210_SYSCTL_SEL0, 6, 3) \ - DIV(K210_CLK_APB2, K210_SYSCTL_SEL0, 9, 3) \ - DIV(K210_CLK_SRAM0, K210_SYSCTL_THR0, 0, 4) \ - DIV(K210_CLK_SRAM1, K210_SYSCTL_THR0, 4, 4) \ - DIV(K210_CLK_AI, K210_SYSCTL_THR0, 8, 4) \ - DIV(K210_CLK_DVP, K210_SYSCTL_THR0, 12, 4) \ - DIV(K210_CLK_ROM, K210_SYSCTL_THR0, 16, 4) \ - DIV(K210_CLK_SPI0, K210_SYSCTL_THR1, 0, 8) \ - DIV(K210_CLK_SPI1, K210_SYSCTL_THR1, 8, 8) \ - DIV(K210_CLK_SPI2, K210_SYSCTL_THR1, 16, 8) \ - DIV(K210_CLK_SPI3, K210_SYSCTL_THR1, 24, 8) \ - DIV(K210_CLK_TIMER0, K210_SYSCTL_THR2, 0, 8) \ - DIV(K210_CLK_TIMER1, K210_SYSCTL_THR2, 8, 8) \ - DIV(K210_CLK_TIMER2, K210_SYSCTL_THR2, 16, 8) \ - DIV(K210_CLK_I2S0, K210_SYSCTL_THR3, 0, 16) \ - DIV(K210_CLK_I2S1, K210_SYSCTL_THR3, 16, 16) \ - DIV(K210_CLK_I2S2, K210_SYSCTL_THR4, 0, 16) \ - DIV(K210_CLK_I2S0_M, K210_SYSCTL_THR4, 16, 8) \ - DIV(K210_CLK_I2S1_M, K210_SYSCTL_THR4, 24, 8) \ - DIV(K210_CLK_I2S2_M, K210_SYSCTL_THR4, 0, 8) \ - DIV(K210_CLK_I2C0, K210_SYSCTL_THR5, 8, 8) \ - DIV(K210_CLK_I2C1, K210_SYSCTL_THR5, 16, 8) \ - DIV(K210_CLK_I2C2, K210_SYSCTL_THR5, 24, 8) \ - DIV(K210_CLK_WDT0, K210_SYSCTL_THR6, 0, 8) \ - DIV(K210_CLK_WDT1, K210_SYSCTL_THR6, 8, 8) - -#define _DIVIFY(id) K210_CLK_DIV_##id -#define DIVIFY(id) _DIVIFY(id) - -enum k210_div_ids { -#define DIV_FLAGS(id, ...) DIVIFY(id), - DIV_LIST -#undef DIV_FLAGS -}; - -struct k210_div_params { - u8 off; - u8 shift; - u8 width; - u8 flags; -}; - -static const struct k210_div_params k210_divs[] = { -#define DIV_FLAGS(id, _off, _shift, _width, _flags) \ - [DIVIFY(id)] = { \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ - .flags = (_flags), \ - }, - DIV_LIST -#undef DIV_FLAGS -}; - -#undef DIV -#undef DIV_LIST - -#define GATE_LIST \ - GATE(K210_CLK_CPU, K210_SYSCTL_EN_CENT, 0) \ - GATE(K210_CLK_SRAM0, K210_SYSCTL_EN_CENT, 1) \ - GATE(K210_CLK_SRAM1, K210_SYSCTL_EN_CENT, 2) \ - GATE(K210_CLK_APB0, K210_SYSCTL_EN_CENT, 3) \ - GATE(K210_CLK_APB1, K210_SYSCTL_EN_CENT, 4) \ - GATE(K210_CLK_APB2, K210_SYSCTL_EN_CENT, 5) \ - GATE(K210_CLK_ROM, K210_SYSCTL_EN_PERI, 0) \ - GATE(K210_CLK_DMA, K210_SYSCTL_EN_PERI, 1) \ - GATE(K210_CLK_AI, K210_SYSCTL_EN_PERI, 2) \ - GATE(K210_CLK_DVP, K210_SYSCTL_EN_PERI, 3) \ - GATE(K210_CLK_FFT, K210_SYSCTL_EN_PERI, 4) \ - GATE(K210_CLK_GPIO, K210_SYSCTL_EN_PERI, 5) \ - GATE(K210_CLK_SPI0, K210_SYSCTL_EN_PERI, 6) \ - GATE(K210_CLK_SPI1, K210_SYSCTL_EN_PERI, 7) \ - GATE(K210_CLK_SPI2, K210_SYSCTL_EN_PERI, 8) \ - GATE(K210_CLK_SPI3, K210_SYSCTL_EN_PERI, 9) \ - GATE(K210_CLK_I2S0, K210_SYSCTL_EN_PERI, 10) \ - GATE(K210_CLK_I2S1, K210_SYSCTL_EN_PERI, 11) \ - GATE(K210_CLK_I2S2, K210_SYSCTL_EN_PERI, 12) \ - GATE(K210_CLK_I2C0, K210_SYSCTL_EN_PERI, 13) \ - GATE(K210_CLK_I2C1, K210_SYSCTL_EN_PERI, 14) \ - GATE(K210_CLK_I2C2, K210_SYSCTL_EN_PERI, 15) \ - GATE(K210_CLK_UART1, K210_SYSCTL_EN_PERI, 16) \ - GATE(K210_CLK_UART2, K210_SYSCTL_EN_PERI, 17) \ - GATE(K210_CLK_UART3, K210_SYSCTL_EN_PERI, 18) \ - GATE(K210_CLK_AES, K210_SYSCTL_EN_PERI, 19) \ - GATE(K210_CLK_FPIOA, K210_SYSCTL_EN_PERI, 20) \ - GATE(K210_CLK_TIMER0, K210_SYSCTL_EN_PERI, 21) \ - GATE(K210_CLK_TIMER1, K210_SYSCTL_EN_PERI, 22) \ - GATE(K210_CLK_TIMER2, K210_SYSCTL_EN_PERI, 23) \ - GATE(K210_CLK_WDT0, K210_SYSCTL_EN_PERI, 24) \ - GATE(K210_CLK_WDT1, K210_SYSCTL_EN_PERI, 25) \ - GATE(K210_CLK_SHA, K210_SYSCTL_EN_PERI, 26) \ - GATE(K210_CLK_OTP, K210_SYSCTL_EN_PERI, 27) \ - GATE(K210_CLK_RTC, K210_SYSCTL_EN_PERI, 29) - -#define _GATEIFY(id) K210_CLK_GATE_##id -#define GATEIFY(id) _GATEIFY(id) - -enum k210_gate_ids { -#define GATE(id, ...) GATEIFY(id), - GATE_LIST -#undef GATE -}; - -struct k210_gate_params { - u8 off; - u8 bit_idx; -}; - -static const struct k210_gate_params k210_gates[] = { -#define GATE(id, _off, _idx) \ - [GATEIFY(id)] = { \ - .off = (_off), \ - .bit_idx = (_idx), \ - }, - GATE_LIST -#undef GATE -}; - -#undef GATE_LIST - -#define MUX(id, reg, shift, width) \ - MUX_PARENTS(id, generic_sels, reg, shift, width) -#define MUX_LIST \ - MUX_PARENTS(K210_CLK_PLL2, pll2_sels, K210_SYSCTL_PLL2, 26, 2) \ - MUX_PARENTS(K210_CLK_ACLK, aclk_sels, K210_SYSCTL_SEL0, 0, 1) \ - MUX(K210_CLK_SPI3, K210_SYSCTL_SEL0, 12, 1) \ - MUX(K210_CLK_TIMER0, K210_SYSCTL_SEL0, 13, 1) \ - MUX(K210_CLK_TIMER1, K210_SYSCTL_SEL0, 14, 1) \ - MUX(K210_CLK_TIMER2, K210_SYSCTL_SEL0, 15, 1) - -#define _MUXIFY(id) K210_CLK_MUX_##id -#define MUXIFY(id) _MUXIFY(id) - -enum k210_mux_ids { -#define MUX_PARENTS(id, ...) MUXIFY(id), - MUX_LIST -#undef MUX_PARENTS - K210_CLK_MUX_NONE, -}; - -struct k210_mux_params { - const char *const *parent_names; - u8 num_parents; - u8 off; - u8 shift; - u8 width; -}; - -static const struct k210_mux_params k210_muxes[] = { -#define MUX_PARENTS(id, parents, _off, _shift, _width) \ - [MUXIFY(id)] = { \ - .parent_names = (const char * const *)(parents), \ - .num_parents = ARRAY_SIZE(parents), \ - .off = (_off), \ - .shift = (_shift), \ - .width = (_width), \ - }, - MUX_LIST -#undef MUX_PARENTS -}; - -#undef MUX -#undef MUX_LIST - -struct k210_pll_params { - u8 off; - u8 lock_off; - u8 shift; - u8 width; -}; - -static const struct k210_pll_params k210_plls[] = { -#define PLL(_off, _shift, _width) { \ - .off = (_off), \ - .lock_off = K210_SYSCTL_PLL_LOCK, \ - .shift = (_shift), \ - .width = (_width), \ -} - [0] = PLL(K210_SYSCTL_PLL0, 0, 2), - [1] = PLL(K210_SYSCTL_PLL1, 8, 1), - [2] = PLL(K210_SYSCTL_PLL2, 16, 1), -#undef PLL -}; - -#define COMP(id) \ - COMP_FULL(id, MUXIFY(id), DIVIFY(id), GATEIFY(id)) -#define COMP_NOMUX(id) \ - COMP_FULL(id, K210_CLK_MUX_NONE, DIVIFY(id), GATEIFY(id)) -#define COMP_LIST \ - COMP(K210_CLK_SPI3) \ - COMP(K210_CLK_TIMER0) \ - COMP(K210_CLK_TIMER1) \ - COMP(K210_CLK_TIMER2) \ - COMP_NOMUX(K210_CLK_SRAM0) \ - COMP_NOMUX(K210_CLK_SRAM1) \ - COMP_NOMUX(K210_CLK_ROM) \ - COMP_NOMUX(K210_CLK_DVP) \ - COMP_NOMUX(K210_CLK_APB0) \ - COMP_NOMUX(K210_CLK_APB1) \ - COMP_NOMUX(K210_CLK_APB2) \ - COMP_NOMUX(K210_CLK_AI) \ - COMP_NOMUX(K210_CLK_I2S0) \ - COMP_NOMUX(K210_CLK_I2S1) \ - COMP_NOMUX(K210_CLK_I2S2) \ - COMP_NOMUX(K210_CLK_WDT0) \ - COMP_NOMUX(K210_CLK_WDT1) \ - COMP_NOMUX(K210_CLK_SPI0) \ - COMP_NOMUX(K210_CLK_SPI1) \ - COMP_NOMUX(K210_CLK_SPI2) \ - COMP_NOMUX(K210_CLK_I2C0) \ - COMP_NOMUX(K210_CLK_I2C1) \ - COMP_NOMUX(K210_CLK_I2C2) - -#define _COMPIFY(id) K210_CLK_COMP_##id -#define COMPIFY(id) _COMPIFY(id) - -enum k210_comp_ids { -#define COMP_FULL(id, ...) COMPIFY(id), - COMP_LIST -#undef COMP_FULL -}; - -struct k210_comp_params { - u8 mux; - u8 div; - u8 gate; -}; - -static const struct k210_comp_params k210_comps[] = { -#define COMP_FULL(id, _mux, _div, _gate) \ - [COMPIFY(id)] = { \ - .mux = (_mux), \ - .div = (_div), \ - .gate = (_gate), \ - }, - COMP_LIST -#undef COMP_FULL -}; - -#undef COMP -#undef COMP_ID -#undef COMP_NOMUX -#undef COMP_NOMUX_ID -#undef COMP_LIST - -static struct clk *k210_bypass_children __section(".data"); - -/* Helper functions to create sub-clocks */ -static struct clk_mux *k210_create_mux(const struct k210_mux_params *params, - void *base) -{ - struct clk_mux *mux = kzalloc(sizeof(*mux), GFP_KERNEL); - - if (!mux) - return mux; - - mux->reg = base + params->off; - mux->mask = BIT(params->width) - 1; - mux->shift = params->shift; - mux->parent_names = params->parent_names; - mux->num_parents = params->num_parents; - - return mux; -} - -static struct clk_divider *k210_create_div(const struct k210_div_params *params, - void *base) -{ - struct clk_divider *div = kzalloc(sizeof(*div), GFP_KERNEL); - - if (!div) - return div; - - div->reg = base + params->off; - div->shift = params->shift; - div->width = params->width; - div->flags = params->flags; - - return div; -} - -static struct clk_gate *k210_create_gate(const struct k210_gate_params *params, - void *base) -{ - struct clk_gate *gate = kzalloc(sizeof(*gate), GFP_KERNEL); - - if (!gate) - return gate; - - gate->reg = base + params->off; - gate->bit_idx = params->bit_idx; - - return gate; -} - -static struct k210_pll *k210_create_pll(const struct k210_pll_params *params, - void *base) -{ - struct k210_pll *pll = kzalloc(sizeof(*pll), GFP_KERNEL); - - if (!pll) - return pll; - - pll->reg = base + params->off; - pll->lock = base + params->lock_off; - pll->shift = params->shift; - pll->width = params->width; - - return pll; -} - -/* Create all sub-clocks, and then register the composite clock */ -static struct clk *k210_register_comp(const struct k210_comp_params *params, - void *base, const char *name, - const char *parent) -{ - const char *const *parent_names; - int num_parents; - struct clk *comp; - const struct clk_ops *mux_ops; - struct clk_mux *mux; - struct clk_divider *div; - struct clk_gate *gate; - - if (params->mux == K210_CLK_MUX_NONE) { - if (!parent) - return ERR_PTR(-EINVAL); - - mux_ops = NULL; - mux = NULL; - parent_names = &parent; - num_parents = 1; - } else { - mux_ops = &clk_mux_ops; - mux = k210_create_mux(&k210_muxes[params->mux], base); - if (!mux) - return ERR_PTR(-ENOMEM); - - parent_names = mux->parent_names; - num_parents = mux->num_parents; - } - - div = k210_create_div(&k210_divs[params->div], base); - if (!div) { - comp = ERR_PTR(-ENOMEM); - goto cleanup_mux; - } - - gate = k210_create_gate(&k210_gates[params->gate], base); - if (!gate) { - comp = ERR_PTR(-ENOMEM); - goto cleanup_div; - } - - comp = clk_register_composite(NULL, name, parent_names, num_parents, - &mux->clk, mux_ops, - &div->clk, &clk_divider_ops, - &gate->clk, &clk_gate_ops, 0); - if (IS_ERR(comp)) - goto cleanup_gate; - return comp; - -cleanup_gate: - free(gate); -cleanup_div: - free(div); -cleanup_mux: - free(mux); - return comp; -} - -static bool __section(".data") probed; - -/* reset probed so we will probe again post-relocation */ -static int k210_clk_bind(struct udevice *dev) -{ - probed = false; - return 0; -} - -static int k210_clk_probe(struct udevice *dev) -{ - int ret; - const char *in0; - struct clk *in0_clk, *bypass; - struct clk_mux *mux; - struct clk_divider *div; - struct k210_pll *pll; - void *base; - - /* - * Only one instance of this driver allowed. This prevents weird bugs - * when the driver fails part-way through probing. Some clocks will - * already have been registered, and re-probing will register them - * again, creating a bunch of duplicates. Better error-handling/cleanup - * could fix this, but it's Probably Not Worth It (TM). - */ - if (probed) - return -EINVAL; - - base = dev_read_addr_ptr(dev_get_parent(dev)); - if (!base) - return -EINVAL; - - in0_clk = kzalloc(sizeof(*in0_clk), GFP_KERNEL); - if (!in0_clk) - return -ENOMEM; - - ret = clk_get_by_index(dev, 0, in0_clk); - if (ret) - return ret; - in0 = in0_clk->dev->name; - - probed = true; - - aclk_sels[0] = in0; - pll2_sels[0] = in0; - - /* - * All PLLs have a broken bypass, but pll0 has the CPU downstream, so we - * need to manually reparent it whenever we configure pll0 - */ - pll = k210_create_pll(&k210_plls[0], base); - if (pll) { - bypass = k210_register_bypass("pll0", in0, &pll->clk, - &k210_pll_ops, in0_clk); - clk_dm(K210_CLK_PLL0, bypass); - } else { - return -ENOMEM; - } - - pll = k210_create_pll(&k210_plls[1], base); - if (pll) - clk_dm(K210_CLK_PLL1, - k210_register_pll_struct("pll1", in0, pll)); - - /* PLL2 is muxed, so set up a composite clock */ - mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_PLL2)], base); - pll = k210_create_pll(&k210_plls[2], base); - if (!mux || !pll) { - free(mux); - free(pll); - } else { - clk_dm(K210_CLK_PLL2, - clk_register_composite(NULL, "pll2", pll2_sels, - ARRAY_SIZE(pll2_sels), - &mux->clk, &clk_mux_ops, - &pll->clk, &k210_pll_ops, - &pll->clk, &k210_pll_ops, 0)); - } - - /* Half-frequency clocks for "even" dividers */ - clk_dm(K210_CLK_IN0_H, k210_clk_half("in0_half", in0)); - clk_dm(K210_CLK_PLL0_H, k210_clk_half("pll0_half", "pll0")); - clk_dm(K210_CLK_PLL2_H, k210_clk_half("pll2_half", "pll2")); - - /* ACLK has no gate */ - mux = k210_create_mux(&k210_muxes[MUXIFY(K210_CLK_ACLK)], base); - div = k210_create_div(&k210_divs[DIVIFY(K210_CLK_ACLK)], base); - if (!mux || !div) { - free(mux); - free(div); - } else { - struct clk *aclk = - clk_register_composite(NULL, "aclk", aclk_sels, - ARRAY_SIZE(aclk_sels), - &mux->clk, &clk_mux_ops, - &div->clk, &clk_divider_ops, - NULL, NULL, 0); - clk_dm(K210_CLK_ACLK, aclk); - if (!IS_ERR(aclk)) { - k210_bypass_children = aclk; - k210_bypass_set_children(bypass, - &k210_bypass_children, 1); - } - } - -#define REGISTER_COMP(id, name) \ - clk_dm(id, \ - k210_register_comp(&k210_comps[COMPIFY(id)], base, name, NULL)) - REGISTER_COMP(K210_CLK_SPI3, "spi3"); - REGISTER_COMP(K210_CLK_TIMER0, "timer0"); - REGISTER_COMP(K210_CLK_TIMER1, "timer1"); - REGISTER_COMP(K210_CLK_TIMER2, "timer2"); -#undef REGISTER_COMP - - /* Dividing clocks, no mux */ -#define REGISTER_COMP_NOMUX(id, name, parent) \ - clk_dm(id, \ - k210_register_comp(&k210_comps[COMPIFY(id)], base, name, parent)) - REGISTER_COMP_NOMUX(K210_CLK_SRAM0, "sram0", "aclk"); - REGISTER_COMP_NOMUX(K210_CLK_SRAM1, "sram1", "aclk"); - REGISTER_COMP_NOMUX(K210_CLK_ROM, "rom", "aclk"); - REGISTER_COMP_NOMUX(K210_CLK_DVP, "dvp", "aclk"); - REGISTER_COMP_NOMUX(K210_CLK_APB0, "apb0", "aclk"); - REGISTER_COMP_NOMUX(K210_CLK_APB1, "apb1", "aclk"); - REGISTER_COMP_NOMUX(K210_CLK_APB2, "apb2", "aclk"); - REGISTER_COMP_NOMUX(K210_CLK_AI, "ai", "pll1"); - REGISTER_COMP_NOMUX(K210_CLK_I2S0, "i2s0", "pll2_half"); - REGISTER_COMP_NOMUX(K210_CLK_I2S1, "i2s1", "pll2_half"); - REGISTER_COMP_NOMUX(K210_CLK_I2S2, "i2s2", "pll2_half"); - REGISTER_COMP_NOMUX(K210_CLK_WDT0, "wdt0", "in0_half"); - REGISTER_COMP_NOMUX(K210_CLK_WDT1, "wdt1", "in0_half"); - REGISTER_COMP_NOMUX(K210_CLK_SPI0, "spi0", "pll0_half"); - REGISTER_COMP_NOMUX(K210_CLK_SPI1, "spi1", "pll0_half"); - REGISTER_COMP_NOMUX(K210_CLK_SPI2, "spi2", "pll0_half"); - REGISTER_COMP_NOMUX(K210_CLK_I2C0, "i2c0", "pll0_half"); - REGISTER_COMP_NOMUX(K210_CLK_I2C1, "i2c1", "pll0_half"); - REGISTER_COMP_NOMUX(K210_CLK_I2C2, "i2c2", "pll0_half"); -#undef REGISTER_COMP_NOMUX - - /* Dividing clocks */ -#define REGISTER_DIV(id, name, parent) do {\ - const struct k210_div_params *params = &k210_divs[DIVIFY(id)]; \ - clk_dm(id, \ - clk_register_divider(NULL, name, parent, 0, base + params->off, \ - params->shift, params->width, 0)); \ -} while (false) - REGISTER_DIV(K210_CLK_I2S0_M, "i2s0_m", "pll2_half"); - REGISTER_DIV(K210_CLK_I2S1_M, "i2s1_m", "pll2_half"); - REGISTER_DIV(K210_CLK_I2S2_M, "i2s2_m", "pll2_half"); -#undef REGISTER_DIV - - /* Gated clocks */ -#define REGISTER_GATE(id, name, parent) do { \ - const struct k210_gate_params *params = &k210_gates[GATEIFY(id)]; \ - clk_dm(id, \ - clk_register_gate(NULL, name, parent, 0, base + params->off, \ - params->bit_idx, 0, NULL)); \ -} while (false) - REGISTER_GATE(K210_CLK_CPU, "cpu", "aclk"); - REGISTER_GATE(K210_CLK_DMA, "dma", "aclk"); - REGISTER_GATE(K210_CLK_FFT, "fft", "aclk"); - REGISTER_GATE(K210_CLK_GPIO, "gpio", "apb0"); - REGISTER_GATE(K210_CLK_UART1, "uart1", "apb0"); - REGISTER_GATE(K210_CLK_UART2, "uart2", "apb0"); - REGISTER_GATE(K210_CLK_UART3, "uart3", "apb0"); - REGISTER_GATE(K210_CLK_FPIOA, "fpioa", "apb0"); - REGISTER_GATE(K210_CLK_SHA, "sha", "apb0"); - REGISTER_GATE(K210_CLK_AES, "aes", "apb1"); - REGISTER_GATE(K210_CLK_OTP, "otp", "apb1"); - REGISTER_GATE(K210_CLK_RTC, "rtc", in0); -#undef REGISTER_GATE - - /* The MTIME register in CLINT runs at one 50th the CPU clock speed */ - clk_dm(K210_CLK_CLINT, - clk_register_fixed_factor(NULL, "clint", "aclk", 0, 1, 50)); - - return 0; -} - -static const struct udevice_id k210_clk_ids[] = { - { .compatible = "kendryte,k210-clk" }, - { }, -}; - -U_BOOT_DRIVER(k210_clk) = { - .name = "k210_clk", - .id = UCLASS_CLK, - .of_match = k210_clk_ids, - .ops = &k210_clk_ops, - .bind = k210_clk_bind, - .probe = k210_clk_probe, -}; diff --git a/drivers/clk/kendryte/pll.c b/drivers/clk/kendryte/pll.c deleted file mode 100644 index 184f37aaf2..0000000000 --- a/drivers/clk/kendryte/pll.c +++ /dev/null @@ -1,585 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com> - */ -#define LOG_CATEGORY UCLASS_CLK - -#include <common.h> -#include <dm.h> -/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */ -#include <div64.h> -#include <log.h> -#include <serial.h> -#include <asm/io.h> -#include <dt-bindings/clock/k210-sysctl.h> -#include <kendryte/pll.h> -#include <linux/bitfield.h> -#include <linux/clk-provider.h> -#include <linux/delay.h> -#include <linux/err.h> - -#define CLK_K210_PLL "k210_clk_pll" - -#ifdef CONFIG_CLK_K210_SET_RATE -static int k210_pll_enable(struct clk *clk); -static int k210_pll_disable(struct clk *clk); - -/* - * The PLL included with the Kendryte K210 appears to be a True Circuits, Inc. - * General-Purpose PLL. The logical layout of the PLL with internal feedback is - * approximately the following: - * - * +---------------+ - * |reference clock| - * +---------------+ - * | - * v - * +--+ - * |/r| - * +--+ - * | - * v - * +-------------+ - * |divided clock| - * +-------------+ - * | - * v - * +--------------+ - * |phase detector|<---+ - * +--------------+ | - * | | - * v +--------------+ - * +---+ |feedback clock| - * |VCO| +--------------+ - * +---+ ^ - * | +--+ | - * +--->|/f|---+ - * | +--+ - * v - * +---+ - * |/od| - * +---+ - * | - * v - * +------+ - * |output| - * +------+ - * - * The k210 PLLs have three factors: r, f, and od. Because of the feedback mode, - * the effect of the division by f is to multiply the input frequency. The - * equation for the output rate is - * rate = (rate_in * f) / (r * od). - * Moving knowns to one side of the equation, we get - * rate / rate_in = f / (r * od) - * Rearranging slightly, - * abs_error = abs((rate / rate_in) - (f / (r * od))). - * To get relative, error, we divide by the expected ratio - * error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in). - * Simplifying, - * error = abs(1 - f / (r * od)) / (rate / rate_in) - * error = abs(1 - (f * rate_in) / (r * od * rate)) - * Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate, - * error = abs((f * inv_ratio) / (r * od) - 1) - * This is the error used in evaluating parameters. - * - * r and od are four bits each, while f is six bits. Because r and od are - * multiplied together, instead of the full 256 values possible if both bits - * were used fully, there are only 97 distinct products. Combined with f, there - * are 6208 theoretical settings for the PLL. However, most of these settings - * can be ruled out immediately because they do not have the correct ratio. - * - * In addition to the constraint of approximating the desired ratio, parameters - * must also keep internal pll frequencies within acceptable ranges. The divided - * clock's minimum and maximum frequencies have a ratio of around 128. This - * leaves fairly substantial room to work with, especially since the only - * affected parameter is r. The VCO's minimum and maximum frequency have a ratio - * of 5, which is considerably more restrictive. - * - * The r and od factors are stored in a table. This is to make it easy to find - * the next-largest product. Some products have multiple factorizations, but - * only when one factor has at least a 2.5x ratio to the factors of the other - * factorization. This is because any smaller ratio would not make a difference - * when ensuring the VCO's frequency is within spec. - * - * Throughout the calculation function, fixed point arithmetic is used. Because - * the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit - * 32.32 fixed-point numbers are used to represent ratios. In general, to - * implement division, the numerator is first multiplied by 2^32. This gives a - * result where the whole number part is in the upper 32 bits, and the fraction - * is in the lower 32 bits. - * - * In general, rounding is done to the closest integer. This helps find the best - * approximation for the ratio. Rounding in one direction (e.g down) could cause - * the function to miss a better ratio with one of the parameters increased by - * one. - */ - -/* - * The factors table was generated with the following python code: - * - * def p(x, y): - * return (1.0*x/y > 2.5) or (1.0*y/x > 2.5) - * - * factors = {} - * for i in range(1, 17): - * for j in range(1, 17): - * fs = factors.get(i*j) or [] - * if fs == [] or all([ - * (p(i, x) and p(i, y)) or (p(j, x) and p(j, y)) - * for (x, y) in fs]): - * fs.append((i, j)) - * factors[i*j] = fs - * - * for k, l in sorted(factors.items()): - * for v in l: - * print("PACK(%s, %s)," % v) - */ -#define PACK(r, od) (((((r) - 1) & 0xF) << 4) | (((od) - 1) & 0xF)) -#define UNPACK_R(val) ((((val) >> 4) & 0xF) + 1) -#define UNPACK_OD(val) (((val) & 0xF) + 1) -static const u8 factors[] = { - PACK(1, 1), - PACK(1, 2), - PACK(1, 3), - PACK(1, 4), - PACK(1, 5), - PACK(1, 6), - PACK(1, 7), - PACK(1, 8), - PACK(1, 9), - PACK(3, 3), - PACK(1, 10), - PACK(1, 11), - PACK(1, 12), - PACK(3, 4), - PACK(1, 13), - PACK(1, 14), - PACK(1, 15), - PACK(3, 5), - PACK(1, 16), - PACK(4, 4), - PACK(2, 9), - PACK(2, 10), - PACK(3, 7), - PACK(2, 11), - PACK(2, 12), - PACK(5, 5), - PACK(2, 13), - PACK(3, 9), - PACK(2, 14), - PACK(2, 15), - PACK(2, 16), - PACK(3, 11), - PACK(5, 7), - PACK(3, 12), - PACK(3, 13), - PACK(4, 10), - PACK(3, 14), - PACK(4, 11), - PACK(3, 15), - PACK(3, 16), - PACK(7, 7), - PACK(5, 10), - PACK(4, 13), - PACK(6, 9), - PACK(5, 11), - PACK(4, 14), - PACK(4, 15), - PACK(7, 9), - PACK(4, 16), - PACK(5, 13), - PACK(6, 11), - PACK(5, 14), - PACK(6, 12), - PACK(5, 15), - PACK(7, 11), - PACK(6, 13), - PACK(5, 16), - PACK(9, 9), - PACK(6, 14), - PACK(8, 11), - PACK(6, 15), - PACK(7, 13), - PACK(6, 16), - PACK(7, 14), - PACK(9, 11), - PACK(10, 10), - PACK(8, 13), - PACK(7, 15), - PACK(9, 12), - PACK(10, 11), - PACK(7, 16), - PACK(9, 13), - PACK(8, 15), - PACK(11, 11), - PACK(9, 14), - PACK(8, 16), - PACK(10, 13), - PACK(11, 12), - PACK(9, 15), - PACK(10, 14), - PACK(11, 13), - PACK(9, 16), - PACK(10, 15), - PACK(11, 14), - PACK(12, 13), - PACK(10, 16), - PACK(11, 15), - PACK(12, 14), - PACK(13, 13), - PACK(11, 16), - PACK(12, 15), - PACK(13, 14), - PACK(12, 16), - PACK(13, 15), - PACK(14, 14), - PACK(13, 16), - PACK(14, 15), - PACK(14, 16), - PACK(15, 15), - PACK(15, 16), - PACK(16, 16), -}; - -TEST_STATIC int k210_pll_calc_config(u32 rate, u32 rate_in, - struct k210_pll_config *best) -{ - int i; - s64 error, best_error; - u64 ratio, inv_ratio; /* fixed point 32.32 ratio of the rates */ - u64 max_r; - u64 r, f, od; - - /* - * Can't go over 1.75 GHz or under 21.25 MHz due to limitations on the - * VCO frequency. These are not the same limits as below because od can - * reduce the output frequency by 16. - */ - if (rate > 1750000000 || rate < 21250000) - return -EINVAL; - - /* Similar restrictions on the input rate */ - if (rate_in > 1750000000 || rate_in < 13300000) - return -EINVAL; - - ratio = DIV_ROUND_CLOSEST_ULL((u64)rate << 32, rate_in); - inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate); - /* Can't increase by more than 64 or reduce by more than 256 */ - if (rate > rate_in && ratio > (64ULL << 32)) - return -EINVAL; - else if (rate <= rate_in && inv_ratio > (256ULL << 32)) - return -EINVAL; - - /* - * The divided clock (rate_in / r) must stay between 1.75 GHz and 13.3 - * MHz. There is no minimum, since the only way to get a higher input - * clock than 26 MHz is to use a clock generated by a PLL. Because PLLs - * cannot output frequencies greater than 1.75 GHz, the minimum would - * never be greater than one. - */ - max_r = DIV_ROUND_DOWN_ULL(rate_in, 13300000); - - /* Variables get immediately incremented, so start at -1th iteration */ - i = -1; - f = 0; - r = 0; - od = 0; - best_error = S64_MAX; - error = best_error; - /* do-while here so we always try at least one ratio */ - do { - /* - * Whether we swapped r and od while enforcing frequency limits - */ - bool swapped = false; - u64 last_od = od; - u64 last_r = r; - - /* - * Try the next largest value for f (or r and od) and - * recalculate the other parameters based on that - */ - if (rate > rate_in) { - /* - * Skip factors of the same product if we already tried - * out that product - */ - do { - i++; - r = UNPACK_R(factors[i]); - od = UNPACK_OD(factors[i]); - } while (i + 1 < ARRAY_SIZE(factors) && - r * od == last_r * last_od); - - /* Round close */ - f = (r * od * ratio + BIT(31)) >> 32; - if (f > 64) - f = 64; - } else { - u64 tmp = ++f * inv_ratio; - bool round_up = !!(tmp & BIT(31)); - u32 goal = (tmp >> 32) + round_up; - u32 err, last_err; - - /* Get the next r/od pair in factors */ - while (r * od < goal && i + 1 < ARRAY_SIZE(factors)) { - i++; - r = UNPACK_R(factors[i]); - od = UNPACK_OD(factors[i]); - } - - /* - * This is a case of double rounding. If we rounded up - * above, we need to round down (in cases of ties) here. - * This prevents off-by-one errors resulting from - * choosing X+2 over X when X.Y rounds up to X+1 and - * there is no r * od = X+1. For the converse, when X.Y - * is rounded down to X, we should choose X+1 over X-1. - */ - err = abs(r * od - goal); - last_err = abs(last_r * last_od - goal); - if (last_err < err || (round_up && last_err == err)) { - i--; - r = last_r; - od = last_od; - } - } - - /* - * Enforce limits on internal clock frequencies. If we - * aren't in spec, try swapping r and od. If everything is - * in-spec, calculate the relative error. - */ - while (true) { - /* - * Whether the intermediate frequencies are out-of-spec - */ - bool out_of_spec = false; - - if (r > max_r) { - out_of_spec = true; - } else { - /* - * There is no way to only divide once; we need - * to examine the frequency with and without the - * effect of od. - */ - u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r); - - if (vco > 1750000000 || vco < 340000000) - out_of_spec = true; - } - - if (out_of_spec) { - if (!swapped) { - u64 tmp = r; - - r = od; - od = tmp; - swapped = true; - continue; - } else { - /* - * Try looking ahead to see if there are - * additional factors for the same - * product. - */ - if (i + 1 < ARRAY_SIZE(factors)) { - u64 new_r, new_od; - - i++; - new_r = UNPACK_R(factors[i]); - new_od = UNPACK_OD(factors[i]); - if (r * od == new_r * new_od) { - r = new_r; - od = new_od; - swapped = false; - continue; - } - i--; - } - break; - } - } - - error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, r * od); - /* The lower 16 bits are spurious */ - error = abs((error - BIT(32))) >> 16; - - if (error < best_error) { - best->r = r; - best->f = f; - best->od = od; - best_error = error; - } - break; - } - } while (f < 64 && i + 1 < ARRAY_SIZE(factors) && error != 0); - - if (best_error == S64_MAX) - return -EINVAL; - - log_debug("best error %lld\n", best_error); - return 0; -} - -static ulong k210_pll_set_rate(struct clk *clk, ulong rate) -{ - int err; - long long rate_in = clk_get_parent_rate(clk); - struct k210_pll_config config = {}; - struct k210_pll *pll = to_k210_pll(clk); - u32 reg; - - if (rate_in < 0) - return rate_in; - - log_debug("Calculating parameters with rate=%lu and rate_in=%lld\n", - rate, rate_in); - err = k210_pll_calc_config(rate, rate_in, &config); - if (err) - return err; - log_debug("Got r=%u f=%u od=%u\n", config.r, config.f, config.od); - - /* - * Don't use clk_disable as it might not actually disable the pll due to - * refcounting - */ - k210_pll_disable(clk); - - reg = readl(pll->reg); - reg &= ~K210_PLL_CLKR - & ~K210_PLL_CLKF - & ~K210_PLL_CLKOD - & ~K210_PLL_BWADJ; - reg |= FIELD_PREP(K210_PLL_CLKR, config.r - 1) - | FIELD_PREP(K210_PLL_CLKF, config.f - 1) - | FIELD_PREP(K210_PLL_CLKOD, config.od - 1) - | FIELD_PREP(K210_PLL_BWADJ, config.f - 1); - writel(reg, pll->reg); - - err = k210_pll_enable(clk); - if (err) - return err; - - serial_setbrg(); - return clk_get_rate(clk); -} -#endif /* CONFIG_CLK_K210_SET_RATE */ - -static ulong k210_pll_get_rate(struct clk *clk) -{ - long long rate_in = clk_get_parent_rate(clk); - struct k210_pll *pll = to_k210_pll(clk); - u64 r, f, od; - u32 reg = readl(pll->reg); - - if (rate_in < 0 || (reg & K210_PLL_BYPASS)) - return rate_in; - - if (!(reg & K210_PLL_PWRD)) - return 0; - - r = FIELD_GET(K210_PLL_CLKR, reg) + 1; - f = FIELD_GET(K210_PLL_CLKF, reg) + 1; - od = FIELD_GET(K210_PLL_CLKOD, reg) + 1; - - return DIV_ROUND_DOWN_ULL(((u64)rate_in) * f, r * od); -} - -/* - * Wait for the PLL to be locked. If the PLL is not locked, try clearing the - * slip before retrying - */ -static void k210_pll_waitfor_lock(struct k210_pll *pll) -{ - u32 mask = GENMASK(pll->width - 1, 0) << pll->shift; - - while (true) { - u32 reg = readl(pll->lock); - - if ((reg & mask) == mask) - break; - - reg |= BIT(pll->shift + K210_PLL_CLEAR_SLIP); - writel(reg, pll->lock); - } -} - -/* Adapted from sysctl_pll_enable */ -static int k210_pll_enable(struct clk *clk) -{ - struct k210_pll *pll = to_k210_pll(clk); - u32 reg = readl(pll->reg); - - if ((reg & K210_PLL_PWRD) && (reg & K210_PLL_EN) && - !(reg & K210_PLL_RESET)) - return 0; - - reg |= K210_PLL_PWRD; - writel(reg, pll->reg); - - /* Ensure reset is low before asserting it */ - reg &= ~K210_PLL_RESET; - writel(reg, pll->reg); - reg |= K210_PLL_RESET; - writel(reg, pll->reg); - nop(); - nop(); - reg &= ~K210_PLL_RESET; - writel(reg, pll->reg); - - k210_pll_waitfor_lock(pll); - - reg &= ~K210_PLL_BYPASS; - reg |= K210_PLL_EN; - writel(reg, pll->reg); - - return 0; -} - -static int k210_pll_disable(struct clk *clk) -{ - struct k210_pll *pll = to_k210_pll(clk); - u32 reg = readl(pll->reg); - - /* - * Bypassing before powering off is important so child clocks don't stop - * working. This is especially important for pll0, the indirect parent - * of the cpu clock. - */ - reg |= K210_PLL_BYPASS; - writel(reg, pll->reg); - - reg &= ~K210_PLL_PWRD; - reg &= ~K210_PLL_EN; - writel(reg, pll->reg); - return 0; -} - -const struct clk_ops k210_pll_ops = { - .get_rate = k210_pll_get_rate, -#ifdef CONFIG_CLK_K210_SET_RATE - .set_rate = k210_pll_set_rate, -#endif - .enable = k210_pll_enable, - .disable = k210_pll_disable, -}; - -struct clk *k210_register_pll_struct(const char *name, const char *parent_name, - struct k210_pll *pll) -{ - int ret; - struct clk *clk = &pll->clk; - - ret = clk_register(clk, CLK_K210_PLL, name, parent_name); - if (ret) - return ERR_PTR(ret); - return clk; -} - -U_BOOT_DRIVER(k210_pll) = { - .name = CLK_K210_PLL, - .id = UCLASS_CLK, - .ops = &k210_pll_ops, -}; diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 0c8b9eb47d..f4d6ef9f93 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -114,3 +114,9 @@ config CLK_R8A77995 depends on CLK_RCAR_GEN3 help Enable this to support the clocks on Renesas R8A77995 SoC. + +config CLK_R8A779A0 + bool "Renesas R8A779A0 clock driver" + depends on CLK_RCAR_GEN3 + help + Enable this to support the clocks on Renesas R8A779A0 SoC. diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index ed1a1252c4..36a5ca65f4 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_CLK_R8A77970) += r8a77970-cpg-mssr.o obj-$(CONFIG_CLK_R8A77980) += r8a77980-cpg-mssr.o obj-$(CONFIG_CLK_R8A77990) += r8a77990-cpg-mssr.o obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o +obj-$(CONFIG_CLK_R8A779A0) += r8a779a0-cpg-mssr.o diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index 7b42e28e83..6cf07fb418 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -253,6 +253,28 @@ static u64 gen3_clk_get_rate64(struct clk *clk) return gen3_clk_get_rate64_pll_mul_reg(priv, &parent, core, CPG_PLL4CR, 0, 0, "PLL4"); + case CLK_TYPE_R8A779A0_MAIN: + return gen3_clk_get_rate64_pll_mul_reg(priv, &parent, core, + 0, 1, pll_config->extal_div, + "V3U_MAIN"); + + case CLK_TYPE_R8A779A0_PLL1: + return gen3_clk_get_rate64_pll_mul_reg(priv, &parent, core, + 0, pll_config->pll1_mult, + pll_config->pll1_div, + "V3U_PLL1"); + + case CLK_TYPE_R8A779A0_PLL2X_3X: + return gen3_clk_get_rate64_pll_mul_reg(priv, &parent, core, + core->offset, 0, 0, + "V3U_PLL2X_3X"); + + case CLK_TYPE_R8A779A0_PLL5: + return gen3_clk_get_rate64_pll_mul_reg(priv, &parent, core, + 0, pll_config->pll5_mult, + pll_config->pll5_div, + "V3U_PLL5"); + case CLK_TYPE_FF: return gen3_clk_get_rate64_pll_mul_reg(priv, &parent, core, 0, core->mult, core->div, @@ -268,6 +290,8 @@ static u64 gen3_clk_get_rate64(struct clk *clk) return rate; case CLK_TYPE_GEN3_SD: /* FIXME */ + fallthrough; + case CLK_TYPE_R8A779A0_SD: value = readl(priv->base + core->offset); value &= CPG_SD_STP_MASK | CPG_SD_FC_MASK; @@ -394,6 +418,11 @@ int gen3_clk_probe(struct udevice *dev) priv->info->control_regs = smstpcr; priv->info->reset_regs = srcr; priv->info->reset_clear_regs = srstclr; + } else if (info->reg_layout == CLK_REG_LAYOUT_RCAR_V3U) { + priv->info->status_regs = mstpsr_for_v3u; + priv->info->control_regs = mstpcr_for_v3u; + priv->info->reset_regs = srcr_for_v3u; + priv->info->reset_clear_regs = srstclr_for_v3u; } else { return -EINVAL; } diff --git a/drivers/clk/renesas/r8a779a0-cpg-mssr.c b/drivers/clk/renesas/r8a779a0-cpg-mssr.c new file mode 100644 index 0000000000..bda6995236 --- /dev/null +++ b/drivers/clk/renesas/r8a779a0-cpg-mssr.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r8a779a0 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2020 Renesas Electronics Corp. + * + * Based on r8a7795-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + * Copyright (C) 2015 Renesas Electronics Corp. + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> + +#include <dt-bindings/clock/r8a779a0-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen3-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A779A0_CLK_OSC, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_EXTALR, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL1, + CLK_PLL20, + CLK_PLL21, + CLK_PLL30, + CLK_PLL31, + CLK_PLL5, + CLK_PLL1_DIV2, + CLK_PLL20_DIV2, + CLK_PLL21_DIV2, + CLK_PLL30_DIV2, + CLK_PLL31_DIV2, + CLK_PLL5_DIV2, + CLK_PLL5_DIV4, + CLK_S1, + CLK_S3, + CLK_SDSRC, + CLK_RPCSRC, + CLK_OCO, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +#define DEF_PLL(_name, _id, _offset) \ + DEF_BASE(_name, _id, CLK_TYPE_R8A779A0_PLL2X_3X, CLK_MAIN, \ + .offset = _offset) + +#define DEF_SD(_name, _id, _parent, _offset) \ + DEF_BASE(_name, _id, CLK_TYPE_R8A779A0_SD, _parent, .offset = _offset) + +#define DEF_MDSEL(_name, _id, _md, _parent0, _div0, _parent1, _div1) \ + DEF_BASE(_name, _id, CLK_TYPE_R8A779A0_MDSEL, \ + (_parent0) << 16 | (_parent1), \ + .div = (_div0) << 16 | (_div1), .offset = _md) + +#define DEF_OSC(_name, _id, _parent, _div) \ + DEF_BASE(_name, _id, CLK_TYPE_R8A779A0_OSC, _parent, .div = _div) + +static const struct cpg_core_clk r8a779a0_core_clks[] = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("extalr", CLK_EXTALR), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_R8A779A0_MAIN, CLK_EXTAL), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_R8A779A0_PLL1, CLK_MAIN), + DEF_BASE(".pll5", CLK_PLL5, CLK_TYPE_R8A779A0_PLL5, CLK_MAIN), + DEF_PLL(".pll20", CLK_PLL20, 0x0834), + DEF_PLL(".pll21", CLK_PLL21, 0x0838), + DEF_PLL(".pll30", CLK_PLL30, 0x083c), + DEF_PLL(".pll31", CLK_PLL31, 0x0840), + + DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1), + DEF_FIXED(".pll20_div2", CLK_PLL20_DIV2, CLK_PLL20, 2, 1), + DEF_FIXED(".pll21_div2", CLK_PLL21_DIV2, CLK_PLL21, 2, 1), + DEF_FIXED(".pll30_div2", CLK_PLL30_DIV2, CLK_PLL30, 2, 1), + DEF_FIXED(".pll31_div2", CLK_PLL31_DIV2, CLK_PLL31, 2, 1), + DEF_FIXED(".pll5_div2", CLK_PLL5_DIV2, CLK_PLL5, 2, 1), + DEF_FIXED(".pll5_div4", CLK_PLL5_DIV4, CLK_PLL5_DIV2, 2, 1), + DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL5_DIV4, 1, 1), + DEF_RATE(".oco", CLK_OCO, 32768), + + /* Core Clock Outputs */ + DEF_FIXED("zx", R8A779A0_CLK_ZX, CLK_PLL20_DIV2, 2, 1), + DEF_FIXED("s1d1", R8A779A0_CLK_S1D1, CLK_S1, 1, 1), + DEF_FIXED("s1d2", R8A779A0_CLK_S1D2, CLK_S1, 2, 1), + DEF_FIXED("s1d4", R8A779A0_CLK_S1D4, CLK_S1, 4, 1), + DEF_FIXED("s1d8", R8A779A0_CLK_S1D8, CLK_S1, 8, 1), + DEF_FIXED("s1d12", R8A779A0_CLK_S1D12, CLK_S1, 12, 1), + DEF_FIXED("s3d1", R8A779A0_CLK_S3D1, CLK_S3, 1, 1), + DEF_FIXED("s3d2", R8A779A0_CLK_S3D2, CLK_S3, 2, 1), + DEF_FIXED("s3d4", R8A779A0_CLK_S3D4, CLK_S3, 4, 1), + DEF_FIXED("zs", R8A779A0_CLK_ZS, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED("zt", R8A779A0_CLK_ZT, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED("ztr", R8A779A0_CLK_ZTR, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED("zr", R8A779A0_CLK_ZR, CLK_PLL1_DIV2, 1, 1), + DEF_FIXED("dsi", R8A779A0_CLK_DSI, CLK_PLL5_DIV4, 1, 1), + DEF_FIXED("cnndsp", R8A779A0_CLK_CNNDSP, CLK_PLL5_DIV4, 1, 1), + DEF_FIXED("vip", R8A779A0_CLK_VIP, CLK_PLL5, 5, 1), + DEF_FIXED("adgh", R8A779A0_CLK_ADGH, CLK_PLL5_DIV4, 1, 1), + DEF_FIXED("icu", R8A779A0_CLK_ICU, CLK_PLL5_DIV4, 2, 1), + DEF_FIXED("icud2", R8A779A0_CLK_ICUD2, CLK_PLL5_DIV4, 4, 1), + DEF_FIXED("vcbus", R8A779A0_CLK_VCBUS, CLK_PLL5_DIV4, 1, 1), + DEF_FIXED("cbfusa", R8A779A0_CLK_CBFUSA, CLK_EXTAL, 2, 1), + DEF_FIXED("cp", R8A779A0_CLK_CP, CLK_EXTAL, 2, 1), + + DEF_SD("sd0", R8A779A0_CLK_SD0, CLK_SDSRC, 0x870), + + DEF_DIV6P1("mso", R8A779A0_CLK_MSO, CLK_PLL5_DIV4, 0x87c), + DEF_DIV6P1("canfd", R8A779A0_CLK_CANFD, CLK_PLL5_DIV4, 0x878), + DEF_DIV6P1("csi0", R8A779A0_CLK_CSI0, CLK_PLL5_DIV4, 0x880), + + DEF_OSC("osc", R8A779A0_CLK_OSC, CLK_EXTAL, 8), + DEF_MDSEL("r", R8A779A0_CLK_R, 29, CLK_EXTALR, 1, CLK_OCO, 1), +}; + +static const struct mssr_mod_clk r8a779a0_mod_clks[] = { + DEF_MOD("avb0", 211, R8A779A0_CLK_S3D2), + DEF_MOD("avb1", 212, R8A779A0_CLK_S3D2), + DEF_MOD("avb2", 213, R8A779A0_CLK_S3D2), + DEF_MOD("avb3", 214, R8A779A0_CLK_S3D2), + DEF_MOD("avb4", 215, R8A779A0_CLK_S3D2), + DEF_MOD("avb5", 216, R8A779A0_CLK_S3D2), + DEF_MOD("csi40", 331, R8A779A0_CLK_CSI0), + DEF_MOD("csi41", 400, R8A779A0_CLK_CSI0), + DEF_MOD("csi42", 401, R8A779A0_CLK_CSI0), + DEF_MOD("csi43", 402, R8A779A0_CLK_CSI0), + DEF_MOD("fcpvd0", 508, R8A779A0_CLK_S3D1), + DEF_MOD("fcpvd1", 509, R8A779A0_CLK_S3D1), + DEF_MOD("hscif0", 514, R8A779A0_CLK_S1D2), + DEF_MOD("hscif1", 515, R8A779A0_CLK_S1D2), + DEF_MOD("hscif2", 516, R8A779A0_CLK_S1D2), + DEF_MOD("hscif3", 517, R8A779A0_CLK_S1D2), + DEF_MOD("i2c0", 518, R8A779A0_CLK_S1D4), + DEF_MOD("i2c1", 519, R8A779A0_CLK_S1D4), + DEF_MOD("i2c2", 520, R8A779A0_CLK_S1D4), + DEF_MOD("i2c3", 521, R8A779A0_CLK_S1D4), + DEF_MOD("i2c4", 522, R8A779A0_CLK_S1D4), + DEF_MOD("i2c5", 523, R8A779A0_CLK_S1D4), + DEF_MOD("i2c6", 524, R8A779A0_CLK_S1D4), + DEF_MOD("msi0", 618, R8A779A0_CLK_MSO), + DEF_MOD("msi1", 619, R8A779A0_CLK_MSO), + DEF_MOD("msi2", 620, R8A779A0_CLK_MSO), + DEF_MOD("msi3", 621, R8A779A0_CLK_MSO), + DEF_MOD("msi4", 622, R8A779A0_CLK_MSO), + DEF_MOD("msi5", 623, R8A779A0_CLK_MSO), + DEF_MOD("scif0", 702, R8A779A0_CLK_S1D8), + DEF_MOD("scif1", 703, R8A779A0_CLK_S1D8), + DEF_MOD("scif3", 704, R8A779A0_CLK_S1D8), + DEF_MOD("scif4", 705, R8A779A0_CLK_S1D8), + DEF_MOD("sdhi0", 706, R8A779A0_CLK_SD0), + DEF_MOD("sydm1", 709, R8A779A0_CLK_S1D2), + DEF_MOD("sydm2", 710, R8A779A0_CLK_S1D2), + DEF_MOD("vin00", 730, R8A779A0_CLK_S1D1), + DEF_MOD("vin01", 731, R8A779A0_CLK_S1D1), + DEF_MOD("vin02", 800, R8A779A0_CLK_S1D1), + DEF_MOD("vin03", 801, R8A779A0_CLK_S1D1), + DEF_MOD("vin04", 802, R8A779A0_CLK_S1D1), + DEF_MOD("vin05", 803, R8A779A0_CLK_S1D1), + DEF_MOD("vin06", 804, R8A779A0_CLK_S1D1), + DEF_MOD("vin07", 805, R8A779A0_CLK_S1D1), + DEF_MOD("vin10", 806, R8A779A0_CLK_S1D1), + DEF_MOD("vin11", 807, R8A779A0_CLK_S1D1), + DEF_MOD("vin12", 808, R8A779A0_CLK_S1D1), + DEF_MOD("vin13", 809, R8A779A0_CLK_S1D1), + DEF_MOD("vin14", 810, R8A779A0_CLK_S1D1), + DEF_MOD("vin15", 811, R8A779A0_CLK_S1D1), + DEF_MOD("vin16", 812, R8A779A0_CLK_S1D1), + DEF_MOD("vin17", 813, R8A779A0_CLK_S1D1), + DEF_MOD("vin20", 814, R8A779A0_CLK_S1D1), + DEF_MOD("vin21", 815, R8A779A0_CLK_S1D1), + DEF_MOD("vin22", 816, R8A779A0_CLK_S1D1), + DEF_MOD("vin23", 817, R8A779A0_CLK_S1D1), + DEF_MOD("vin24", 818, R8A779A0_CLK_S1D1), + DEF_MOD("vin25", 819, R8A779A0_CLK_S1D1), + DEF_MOD("vin26", 820, R8A779A0_CLK_S1D1), + DEF_MOD("vin27", 821, R8A779A0_CLK_S1D1), + DEF_MOD("vin30", 822, R8A779A0_CLK_S1D1), + DEF_MOD("vin31", 823, R8A779A0_CLK_S1D1), + DEF_MOD("vin32", 824, R8A779A0_CLK_S1D1), + DEF_MOD("vin33", 825, R8A779A0_CLK_S1D1), + DEF_MOD("vin34", 826, R8A779A0_CLK_S1D1), + DEF_MOD("vin35", 827, R8A779A0_CLK_S1D1), + DEF_MOD("vin36", 828, R8A779A0_CLK_S1D1), + DEF_MOD("vin37", 829, R8A779A0_CLK_S1D1), + DEF_MOD("vspd0", 830, R8A779A0_CLK_S3D1), + DEF_MOD("vspd1", 831, R8A779A0_CLK_S3D1), + DEF_MOD("rwdt", 907, R8A779A0_CLK_R), + DEF_MOD("pfc0", 915, R8A779A0_CLK_CP), + DEF_MOD("pfc1", 916, R8A779A0_CLK_CP), + DEF_MOD("pfc2", 917, R8A779A0_CLK_CP), + DEF_MOD("pfc3", 918, R8A779A0_CLK_CP), + DEF_MOD("vspx0", 1028, R8A779A0_CLK_S1D1), + DEF_MOD("vspx1", 1029, R8A779A0_CLK_S1D1), + DEF_MOD("vspx2", 1030, R8A779A0_CLK_S1D1), + DEF_MOD("vspx3", 1031, R8A779A0_CLK_S1D1), +}; + +/* + * CPG Clock Data + */ + +/* + * MD EXTAL PLL1 PLL20 PLL30 PLL4 PLL5 OSC + * 14 13 (MHz) 21 31 + * -------------------------------------------------------- + * 0 0 16.66 x 1 x128 x216 x128 x144 x192 /16 + * 0 1 20 x 1 x106 x180 x106 x120 x160 /19 + * 1 0 Prohibited setting + * 1 1 33.33 / 2 x128 x216 x128 x144 x192 /32 + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 13) | \ + (((md) & BIT(13)) >> 13)) + +static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[4] = { + /* EXTAL div PLL1 mult/div Not used OSC prediv PLL5 mult/div */ + { 1, 128, 1, 128, 1, 16, 192, 1, }, + { 1, 106, 1, 106, 1, 19, 160, 1, }, + { 0, 0, 0, 0, 0, 0, 0, 0, }, + { 2, 128, 1, 128, 1, 32, 192, 1, }, +}; + +/* + * Note that the only clock left running before booting Linux are now + * MFIS, INTC-AP, INTC-EX and SCIF0 on V3U + */ +#define MSTPCR7_SCIF0 BIT(2) +#define MSTPCR6_MFIS BIT(17) +#define MSTPCR6_INTC BIT(11) /* No information: INTC-AP, INTC-EX */ + +static const struct mstp_stop_table r8a779a0_mstp_table[] = { + { 0x003f7ffe, 0x0, 0x0, 0x0 }, + { 0x00cb0000, 0x0, 0x0, 0x0 }, + { 0x0001f800, 0x0, 0x0, 0x0 }, + { 0x90000000, 0x0, 0x0, 0x0 }, + { 0x0001c807, 0x0, 0x0, 0x0 }, + { 0x7e03c380, 0x0, 0x0, 0x0 }, + { 0x1f01f001, MSTPCR6_MFIS, 0x0, 0x0 }, + { 0xffffe040, MSTPCR7_SCIF0, 0x0, 0x0 }, + { 0xffffffff, 0x0, 0x0, 0x0 }, + { 0x00003c78, 0x0, 0x0, 0x0 }, + { 0xf0000000, 0x0, 0x0, 0x0 }, + { 0x0000000f, 0x0, 0x0, 0x0 }, + { 0xbe800000, 0x0, 0x0, 0x0 }, + { 0x00000037, 0x0, 0x0, 0x0 }, + { 0x00000000, 0x0, 0x0, 0x0 }, +}; + +static const void *r8a779a0_get_pll_config(const u32 cpg_mode) +{ + return &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; +} + +static const struct cpg_mssr_info r8a779a0_cpg_mssr_info = { + .core_clk = r8a779a0_core_clks, + .core_clk_size = ARRAY_SIZE(r8a779a0_core_clks), + .mod_clk = r8a779a0_mod_clks, + .mod_clk_size = ARRAY_SIZE(r8a779a0_mod_clks), + .mstp_table = r8a779a0_mstp_table, + .mstp_table_size = ARRAY_SIZE(r8a779a0_mstp_table), + .reset_node = "renesas,r8a779a0-rst", + .reset_modemr_offset = 0x00, + .extalr_node = "extalr", + .mod_clk_base = MOD_CLK_BASE, + .clk_extal_id = CLK_EXTAL, + .clk_extalr_id = CLK_EXTALR, + .get_pll_config = r8a779a0_get_pll_config, + .reg_layout = CLK_REG_LAYOUT_RCAR_V3U, +}; + +static const struct udevice_id r8a779a0_clk_ids[] = { + { + .compatible = "renesas,r8a779a0-cpg-mssr", + .data = (ulong)&r8a779a0_cpg_mssr_info + }, + { } +}; + +U_BOOT_DRIVER(clk_r8a779a0) = { + .name = "clk_r8a779a0", + .id = UCLASS_CLK, + .of_match = r8a779a0_clk_ids, + .priv_auto = sizeof(struct gen3_clk_priv), + .ops = &gen3_clk_ops, + .probe = gen3_clk_probe, + .remove = gen3_clk_remove, +}; diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index 4fce0a9946..7bf5701361 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -28,6 +28,14 @@ enum rcar_gen3_clk_types { CLK_TYPE_GEN3_RPC, CLK_TYPE_GEN3_RPCD2, + CLK_TYPE_R8A779A0_MAIN, + CLK_TYPE_R8A779A0_PLL1, + CLK_TYPE_R8A779A0_PLL2X_3X, /* PLL[23][01] */ + CLK_TYPE_R8A779A0_PLL5, + CLK_TYPE_R8A779A0_SD, + CLK_TYPE_R8A779A0_MDSEL, /* Select parent/divider using mode pin */ + CLK_TYPE_R8A779A0_OSC, /* OSC EXTAL predivider and fixed divider */ + /* SoC specific definitions start here */ CLK_TYPE_GEN3_SOC_BASE, }; @@ -69,6 +77,8 @@ struct rcar_gen3_cpg_pll_config { u8 pll3_mult; u8 pll3_div; u8 osc_prediv; + u8 pll5_mult; + u8 pll5_div; }; #define CPG_RST_MODEMR 0x060 diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index b1cf7f599c..e0895d2c2f 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -127,6 +127,10 @@ int renesas_clk_remove(void __iomem *base, struct cpg_mssr_info *info) clrsetbits_le32(base + info->control_regs[i], info->mstp_table[i].sdis, info->mstp_table[i].sen); + + if (info->reg_layout == CLK_REG_LAYOUT_RCAR_V3U) + continue; + clrsetbits_le32(base + RMSTPCR(i), info->mstp_table[i].rdis, info->mstp_table[i].ren); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 92421b15ee..519f88548f 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -17,6 +17,7 @@ enum clk_reg_layout { CLK_REG_LAYOUT_RCAR_GEN2_AND_GEN3 = 0, + CLK_REG_LAYOUT_RCAR_V3U, }; struct cpg_mssr_info { @@ -146,6 +147,11 @@ static const u16 mstpsr[] = { 0x9A0, 0x9A4, 0x9A8, 0x9AC, }; +static const u16 mstpsr_for_v3u[] = { + 0x2E00, 0x2E04, 0x2E08, 0x2E0C, 0x2E10, 0x2E14, 0x2E18, 0x2E1C, + 0x2E20, 0x2E24, 0x2E28, 0x2E2C, 0x2E30, 0x2E34, 0x2E38, +}; + /* * System Module Stop Control Register offsets */ @@ -155,6 +161,11 @@ static const u16 smstpcr[] = { 0x990, 0x994, 0x998, 0x99C, }; +static const u16 mstpcr_for_v3u[] = { + 0x2D00, 0x2D04, 0x2D08, 0x2D0C, 0x2D10, 0x2D14, 0x2D18, 0x2D1C, + 0x2D20, 0x2D24, 0x2D28, 0x2D2C, 0x2D30, 0x2D34, 0x2D38, +}; + /* * Software Reset Register offsets */ @@ -164,6 +175,11 @@ static const u16 srcr[] = { 0x920, 0x924, 0x928, 0x92C, }; +static const u16 srcr_for_v3u[] = { + 0x2C00, 0x2C04, 0x2C08, 0x2C0C, 0x2C10, 0x2C14, 0x2C18, 0x2C1C, + 0x2C20, 0x2C24, 0x2C28, 0x2C2C, 0x2C30, 0x2C34, 0x2C38, +}; + /* Realtime Module Stop Control Register offsets */ #define RMSTPCR(i) ((i) < 8 ? smstpcr[i] - 0x20 : smstpcr[i] - 0x10) @@ -177,4 +193,9 @@ static const u16 srstclr[] = { 0x960, 0x964, 0x968, 0x96C, }; +static const u16 srstclr_for_v3u[] = { + 0x2C80, 0x2C84, 0x2C88, 0x2C8C, 0x2C90, 0x2C94, 0x2C98, 0x2C9C, + 0x2CA0, 0x2CA4, 0x2CA8, 0x2CAC, 0x2CB0, 0x2CB4, 0x2CB8, +}; + #endif /* __DRIVERS_CLK_RENESAS_CPG_MSSR__ */ diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 4cfcf83309..913f611a0f 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += clk_rk3308.o obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o +obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o diff --git a/drivers/clk/rockchip/clk_rk3308.c b/drivers/clk/rockchip/clk_rk3308.c index 5a838b9e9a..5248e59685 100644 --- a/drivers/clk/rockchip/clk_rk3308.c +++ b/drivers/clk/rockchip/clk_rk3308.c @@ -1014,7 +1014,7 @@ static int rk3308_clk_probe(struct udevice *dev) rk3308_clk_init(dev); /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ - ret = clk_set_defaults(dev, 1); + ret = clk_set_defaults(dev, CLK_DEFAULTS_POST); if (ret) debug("%s clk_set_defaults failed %d\n", __func__, ret); diff --git a/drivers/clk/rockchip/clk_rk3568.c b/drivers/clk/rockchip/clk_rk3568.c new file mode 100644 index 0000000000..553c6c0daf --- /dev/null +++ b/drivers/clk/rockchip/clk_rk3568.c @@ -0,0 +1,2959 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Rockchip Electronics Co., Ltd + * Author: Elaine Zhang <zhangqing@rock-chips.com> + */ + +#include <common.h> +#include <bitfield.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <syscon.h> +#include <asm/arch-rockchip/cru_rk3568.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/hardware.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <dt-bindings/clock/rk3568-cru.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +struct rk3568_clk_plat { + struct dtd_rockchip_rk3568_cru dtd; +}; + +struct rk3568_pmuclk_plat { + struct dtd_rockchip_rk3568_pmucru dtd; +}; +#endif + +#define RK3568_CPUCLK_RATE(_rate, _aclk_div, _pclk_div) \ +{ \ + .rate = _rate##U, \ + .aclk_div = _aclk_div, \ + .pclk_div = _pclk_div, \ +} + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +static struct rockchip_cpu_rate_table rk3568_cpu_rates[] = { + RK3568_CPUCLK_RATE(1416000000, 1, 5), + RK3568_CPUCLK_RATE(1296000000, 1, 5), + RK3568_CPUCLK_RATE(1200000000, 1, 3), + RK3568_CPUCLK_RATE(1104000000, 1, 3), + RK3568_CPUCLK_RATE(1008000000, 1, 3), + RK3568_CPUCLK_RATE(912000000, 1, 3), + RK3568_CPUCLK_RATE(816000000, 1, 3), + RK3568_CPUCLK_RATE(600000000, 1, 1), + RK3568_CPUCLK_RATE(408000000, 1, 1), + { /* sentinel */ }, +}; + +static struct rockchip_pll_rate_table rk3568_pll_rates[] = { + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ + RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0), + RK3036_PLL_RATE(1416000000, 1, 118, 2, 1, 1, 0), + RK3036_PLL_RATE(1296000000, 1, 108, 2, 1, 1, 0), + RK3036_PLL_RATE(1200000000, 1, 100, 2, 1, 1, 0), + RK3036_PLL_RATE(1188000000, 1, 99, 2, 1, 1, 0), + RK3036_PLL_RATE(1104000000, 1, 92, 2, 1, 1, 0), + RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), + RK3036_PLL_RATE(1000000000, 3, 250, 2, 1, 1, 0), + RK3036_PLL_RATE(912000000, 1, 76, 2, 1, 1, 0), + RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), + RK3036_PLL_RATE(600000000, 1, 100, 4, 1, 1, 0), + RK3036_PLL_RATE(594000000, 1, 99, 4, 1, 1, 0), + RK3036_PLL_RATE(500000000, 1, 125, 6, 1, 1, 0), + RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0), + RK3036_PLL_RATE(400000000, 1, 100, 6, 1, 1, 0), + RK3036_PLL_RATE(200000000, 1, 100, 6, 2, 1, 0), + RK3036_PLL_RATE(100000000, 1, 150, 6, 6, 1, 0), + { /* sentinel */ }, +}; + +static struct rockchip_pll_clock rk3568_pll_clks[] = { + [APLL] = PLL(pll_rk3328, PLL_APLL, RK3568_PLL_CON(0), + RK3568_MODE_CON, 0, 10, 0, rk3568_pll_rates), + [DPLL] = PLL(pll_rk3328, PLL_DPLL, RK3568_PLL_CON(8), + RK3568_MODE_CON, 2, 10, 0, NULL), + [CPLL] = PLL(pll_rk3328, PLL_CPLL, RK3568_PLL_CON(24), + RK3568_MODE_CON, 4, 10, 0, rk3568_pll_rates), + [GPLL] = PLL(pll_rk3328, PLL_HPLL, RK3568_PLL_CON(16), + RK3568_MODE_CON, 6, 10, 0, rk3568_pll_rates), + [NPLL] = PLL(pll_rk3328, PLL_NPLL, RK3568_PLL_CON(32), + RK3568_MODE_CON, 10, 10, 0, rk3568_pll_rates), + [VPLL] = PLL(pll_rk3328, PLL_VPLL, RK3568_PLL_CON(40), + RK3568_MODE_CON, 12, 10, 0, rk3568_pll_rates), + [PPLL] = PLL(pll_rk3328, PLL_PPLL, RK3568_PMU_PLL_CON(0), + RK3568_PMU_MODE, 0, 10, 0, rk3568_pll_rates), + [HPLL] = PLL(pll_rk3328, PLL_HPLL, RK3568_PMU_PLL_CON(16), + RK3568_PMU_MODE, 2, 10, 0, rk3568_pll_rates), +}; + +#ifndef CONFIG_SPL_BUILD +static ulong +rk3568_pmu_pll_set_rate(struct rk3568_clk_priv *priv, + ulong pll_id, ulong rate) +{ + struct udevice *pmucru_dev; + struct rk3568_pmuclk_priv *pmu_priv; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_CLK, + DM_DRIVER_GET(rockchip_rk3568_pmucru), + &pmucru_dev); + if (ret) { + printf("%s: could not find pmucru device\n", __func__); + return ret; + } + pmu_priv = dev_get_priv(pmucru_dev); + + rockchip_pll_set_rate(&rk3568_pll_clks[pll_id], + pmu_priv->pmucru, pll_id, rate); + + return 0; +} +#endif + +static ulong rk3568_pmu_pll_get_rate(struct rk3568_clk_priv *priv, + ulong pll_id) +{ + struct udevice *pmucru_dev; + struct rk3568_pmuclk_priv *pmu_priv; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_CLK, + DM_DRIVER_GET(rockchip_rk3568_pmucru), + &pmucru_dev); + if (ret) { + printf("%s: could not find pmucru device\n", __func__); + return ret; + } + pmu_priv = dev_get_priv(pmucru_dev); + + return rockchip_pll_get_rate(&rk3568_pll_clks[pll_id], + pmu_priv->pmucru, pll_id); +} + +/* + * + * rational_best_approximation(31415, 10000, + * (1 << 8) - 1, (1 << 5) - 1, &n, &d); + * + * you may look at given_numerator as a fixed point number, + * with the fractional part size described in given_denominator. + * + * for theoretical background, see: + * http://en.wikipedia.org/wiki/Continued_fraction + */ +static void rational_best_approximation(unsigned long given_numerator, + unsigned long given_denominator, + unsigned long max_numerator, + unsigned long max_denominator, + unsigned long *best_numerator, + unsigned long *best_denominator) +{ + unsigned long n, d, n0, d0, n1, d1; + + n = given_numerator; + d = given_denominator; + n0 = 0; + d1 = 0; + n1 = 1; + d0 = 1; + for (;;) { + unsigned long t, a; + + if (n1 > max_numerator || d1 > max_denominator) { + n1 = n0; + d1 = d0; + break; + } + if (d == 0) + break; + t = d; + a = n / d; + d = n % d; + n = t; + t = n0 + a * n1; + n0 = n1; + n1 = t; + t = d0 + a * d1; + d0 = d1; + d1 = t; + } + *best_numerator = n1; + *best_denominator = d1; +} + +static ulong rk3568_rtc32k_get_pmuclk(struct rk3568_pmuclk_priv *priv) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + unsigned long m, n; + u32 fracdiv; + + fracdiv = readl(&pmucru->pmu_clksel_con[1]); + m = fracdiv & RTC32K_FRAC_NUMERATOR_MASK; + m >>= RTC32K_FRAC_NUMERATOR_SHIFT; + n = fracdiv & RTC32K_FRAC_DENOMINATOR_MASK; + n >>= RTC32K_FRAC_DENOMINATOR_SHIFT; + + return OSC_HZ * m / n; +} + +static ulong rk3568_rtc32k_set_pmuclk(struct rk3568_pmuclk_priv *priv, + ulong rate) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + unsigned long m, n, val; + + rk_clrsetreg(&pmucru->pmu_clksel_con[0], RTC32K_SEL_MASK, + RTC32K_SEL_OSC0_DIV32K << RTC32K_SEL_SHIFT); + + rational_best_approximation(rate, OSC_HZ, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), + &m, &n); + val = m << RTC32K_FRAC_NUMERATOR_SHIFT | n; + writel(val, &pmucru->pmu_clksel_con[1]); + + return rk3568_rtc32k_get_pmuclk(priv); +} + +static ulong rk3568_i2c_get_pmuclk(struct rk3568_pmuclk_priv *priv, + ulong clk_id) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + u32 div, con; + + switch (clk_id) { + case CLK_I2C0: + con = readl(&pmucru->pmu_clksel_con[3]); + div = (con & CLK_I2C0_DIV_MASK) >> CLK_I2C0_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(priv->ppll_hz, div); +} + +static ulong rk3568_i2c_set_pmuclk(struct rk3568_pmuclk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->ppll_hz, rate); + assert(src_clk_div - 1 <= 127); + + switch (clk_id) { + case CLK_I2C0: + rk_clrsetreg(&pmucru->pmu_clksel_con[3], CLK_I2C0_DIV_MASK, + (src_clk_div - 1) << CLK_I2C0_DIV_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3568_i2c_get_pmuclk(priv, clk_id); +} + +static ulong rk3568_pwm_get_pmuclk(struct rk3568_pmuclk_priv *priv, + ulong clk_id) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + u32 div, sel, con, parent; + + switch (clk_id) { + case CLK_PWM0: + con = readl(&pmucru->pmu_clksel_con[6]); + sel = (con & CLK_PWM0_SEL_MASK) >> CLK_PWM0_SEL_SHIFT; + div = (con & CLK_PWM0_DIV_MASK) >> CLK_PWM0_DIV_SHIFT; + if (sel == CLK_PWM0_SEL_XIN24M) + parent = OSC_HZ; + else + parent = priv->ppll_hz; + break; + default: + return -ENOENT; + } + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3568_pwm_set_pmuclk(struct rk3568_pmuclk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + int src_clk_div; + + switch (clk_id) { + case CLK_PWM0: + if (rate == OSC_HZ) { + rk_clrsetreg(&pmucru->pmu_clksel_con[6], + CLK_PWM0_SEL_MASK | CLK_PWM0_DIV_MASK, + (CLK_PWM0_SEL_XIN24M << + CLK_PWM0_SEL_SHIFT) | + 0 << CLK_PWM0_SEL_SHIFT); + } else { + src_clk_div = DIV_ROUND_UP(priv->ppll_hz, rate); + assert(src_clk_div - 1 <= 127); + rk_clrsetreg(&pmucru->pmu_clksel_con[6], + CLK_PWM0_DIV_MASK | CLK_PWM0_DIV_MASK, + (CLK_PWM0_SEL_PPLL << CLK_PWM0_SEL_SHIFT) | + (src_clk_div - 1) << CLK_PWM0_DIV_SHIFT); + } + break; + default: + return -ENOENT; + } + + return rk3568_pwm_get_pmuclk(priv, clk_id); +} + +static ulong rk3568_pmu_get_pmuclk(struct rk3568_pmuclk_priv *priv) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + u32 div, con, sel, parent; + + con = readl(&pmucru->pmu_clksel_con[2]); + sel = (con & PCLK_PDPMU_SEL_MASK) >> PCLK_PDPMU_SEL_SHIFT; + div = (con & PCLK_PDPMU_DIV_MASK) >> PCLK_PDPMU_DIV_SHIFT; + if (sel) + parent = GPLL_HZ; + else + parent = priv->ppll_hz; + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3568_pmu_set_pmuclk(struct rk3568_pmuclk_priv *priv, + ulong rate) +{ + struct rk3568_pmucru *pmucru = priv->pmucru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->ppll_hz, rate); + assert(src_clk_div - 1 <= 31); + + rk_clrsetreg(&pmucru->pmu_clksel_con[2], + PCLK_PDPMU_DIV_MASK | PCLK_PDPMU_SEL_MASK, + (PCLK_PDPMU_SEL_PPLL << PCLK_PDPMU_SEL_SHIFT) | + ((src_clk_div - 1) << PCLK_PDPMU_DIV_SHIFT)); + + return rk3568_pmu_get_pmuclk(priv); +} + +static ulong rk3568_pmuclk_get_rate(struct clk *clk) +{ + struct rk3568_pmuclk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + if (!priv->ppll_hz) { + printf("%s ppll=%lu\n", __func__, priv->ppll_hz); + return -ENOENT; + } + + debug("%s %ld\n", __func__, clk->id); + switch (clk->id) { + case PLL_PPLL: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[PPLL], + priv->pmucru, PPLL); + break; + case PLL_HPLL: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[HPLL], + priv->pmucru, HPLL); + break; + case CLK_RTC_32K: + case CLK_RTC32K_FRAC: + rate = rk3568_rtc32k_get_pmuclk(priv); + break; + case CLK_I2C0: + rate = rk3568_i2c_get_pmuclk(priv, clk->id); + break; + case CLK_PWM0: + rate = rk3568_pwm_get_pmuclk(priv, clk->id); + break; + case PCLK_PMU: + rate = rk3568_pmu_get_pmuclk(priv); + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3568_pmuclk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3568_pmuclk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + if (!priv->ppll_hz) { + printf("%s ppll=%lu\n", __func__, priv->ppll_hz); + return -ENOENT; + } + + debug("%s %ld %ld\n", __func__, clk->id, rate); + switch (clk->id) { + case PLL_PPLL: + ret = rockchip_pll_set_rate(&rk3568_pll_clks[PPLL], + priv->pmucru, PPLL, rate); + priv->ppll_hz = rockchip_pll_get_rate(&rk3568_pll_clks[PPLL], + priv->pmucru, PPLL); + break; + case PLL_HPLL: + ret = rockchip_pll_set_rate(&rk3568_pll_clks[HPLL], + priv->pmucru, HPLL, rate); + priv->hpll_hz = rockchip_pll_get_rate(&rk3568_pll_clks[HPLL], + priv->pmucru, HPLL); + break; + case CLK_RTC_32K: + case CLK_RTC32K_FRAC: + ret = rk3568_rtc32k_set_pmuclk(priv, rate); + break; + case CLK_I2C0: + ret = rk3568_i2c_set_pmuclk(priv, clk->id, rate); + break; + case CLK_PWM0: + ret = rk3568_pwm_set_pmuclk(priv, clk->id, rate); + break; + case PCLK_PMU: + ret = rk3568_pmu_set_pmuclk(priv, rate); + break; + default: + return -ENOENT; + } + + return ret; +} + +static int rk3568_rtc32k_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3568_pmuclk_priv *priv = dev_get_priv(clk->dev); + struct rk3568_pmucru *pmucru = priv->pmucru; + + if (parent->id == CLK_RTC32K_FRAC) + rk_clrsetreg(&pmucru->pmu_clksel_con[0], RTC32K_SEL_MASK, + RTC32K_SEL_OSC0_DIV32K << RTC32K_SEL_SHIFT); + else + rk_clrsetreg(&pmucru->pmu_clksel_con[0], RTC32K_SEL_MASK, + RTC32K_SEL_OSC1_32K << RTC32K_SEL_SHIFT); + + return 0; +} + +static int rk3568_pmuclk_set_parent(struct clk *clk, struct clk *parent) +{ + switch (clk->id) { + case CLK_RTC_32K: + return rk3568_rtc32k_set_parent(clk, parent); + default: + return -ENOENT; + } +} + +static struct clk_ops rk3568_pmuclk_ops = { + .get_rate = rk3568_pmuclk_get_rate, + .set_rate = rk3568_pmuclk_set_rate, + .set_parent = rk3568_pmuclk_set_parent, +}; + +static int rk3568_pmuclk_probe(struct udevice *dev) +{ + struct rk3568_pmuclk_priv *priv = dev_get_priv(dev); + int ret = 0; + + if (priv->ppll_hz != PPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3568_pll_clks[PPLL], + priv->pmucru, + PPLL, PPLL_HZ); + if (!ret) + priv->ppll_hz = PPLL_HZ; + } + + /* Ungate PCIe30phy refclk_m and refclk_n */ + rk_clrsetreg(&priv->pmucru->pmu_clkgate_con[2], 0x3 << 13, 0 << 13); + return 0; +} + +static int rk3568_pmuclk_ofdata_to_platdata(struct udevice *dev) +{ + struct rk3568_pmuclk_priv *priv = dev_get_priv(dev); + + priv->pmucru = dev_read_addr_ptr(dev); + + return 0; +} + +static int rk3568_pmuclk_bind(struct udevice *dev) +{ +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + int ret = 0; + + ret = offsetof(struct rk3568_pmucru, pmu_softrst_con[0]); + ret = rockchip_reset_bind(dev, ret, 1); + if (ret) + debug("Warning: pmucru software reset driver bind faile\n"); +#endif + + return 0; +} + +static const struct udevice_id rk3568_pmuclk_ids[] = { + { .compatible = "rockchip,rk3568-pmucru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3568_pmucru) = { + .name = "rockchip_rk3568_pmucru", + .id = UCLASS_CLK, + .of_match = rk3568_pmuclk_ids, + .priv_auto = sizeof(struct rk3568_pmuclk_priv), + .of_to_plat = rk3568_pmuclk_ofdata_to_platdata, + .ops = &rk3568_pmuclk_ops, + .bind = rk3568_pmuclk_bind, + .probe = rk3568_pmuclk_probe, +#if CONFIG_IS_ENABLED(OF_PLATDATA) + .plat_auto = sizeof(struct rk3568_pmuclk_plat), +#endif + +}; + +static int rk3568_armclk_set_clk(struct rk3568_clk_priv *priv, ulong hz) +{ + struct rk3568_cru *cru = priv->cru; + const struct rockchip_cpu_rate_table *rate; + ulong old_rate; + + rate = rockchip_get_cpu_settings(rk3568_cpu_rates, hz); + if (!rate) { + printf("%s unsupported rate\n", __func__); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[0], + CLK_CORE_PRE_SEL_MASK, + (CLK_CORE_PRE_SEL_SRC << CLK_CORE_PRE_SEL_SHIFT)); + rk_clrsetreg(&cru->clksel_con[2], + SCLK_CORE_PRE_SEL_MASK | + SCLK_CORE_SRC_SEL_MASK | + SCLK_CORE_SRC_DIV_MASK, + (SCLK_CORE_PRE_SEL_SRC << + SCLK_CORE_PRE_SEL_SHIFT) | + (SCLK_CORE_SRC_SEL_APLL << + SCLK_CORE_SRC_SEL_SHIFT) | + (1 << SCLK_CORE_SRC_DIV_SHIFT)); + + /* + * set up dependent divisors for DBG and ACLK clocks. + */ + old_rate = rockchip_pll_get_rate(&rk3568_pll_clks[APLL], + priv->cru, APLL); + if (old_rate > hz) { + if (rockchip_pll_set_rate(&rk3568_pll_clks[APLL], + priv->cru, APLL, hz)) + return -EINVAL; + rk_clrsetreg(&cru->clksel_con[3], + GICCLK_CORE_DIV_MASK | ATCLK_CORE_DIV_MASK, + rate->pclk_div << GICCLK_CORE_DIV_SHIFT | + rate->pclk_div << ATCLK_CORE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[4], + PERIPHCLK_CORE_PRE_DIV_MASK | + PCLK_CORE_PRE_DIV_MASK, + rate->pclk_div << PCLK_CORE_PRE_DIV_SHIFT | + rate->pclk_div << PERIPHCLK_CORE_PRE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[5], + ACLK_CORE_NDFT_DIV_MASK, + rate->aclk_div << ACLK_CORE_NDFT_DIV_SHIFT); + } else if (old_rate < hz) { + rk_clrsetreg(&cru->clksel_con[3], + GICCLK_CORE_DIV_MASK | ATCLK_CORE_DIV_MASK, + rate->pclk_div << GICCLK_CORE_DIV_SHIFT | + rate->pclk_div << ATCLK_CORE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[4], + PERIPHCLK_CORE_PRE_DIV_MASK | + PCLK_CORE_PRE_DIV_MASK, + rate->pclk_div << PCLK_CORE_PRE_DIV_SHIFT | + rate->pclk_div << PERIPHCLK_CORE_PRE_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[5], + ACLK_CORE_NDFT_DIV_MASK, + rate->aclk_div << ACLK_CORE_NDFT_DIV_SHIFT); + if (rockchip_pll_set_rate(&rk3568_pll_clks[APLL], + priv->cru, APLL, hz)) + return -EINVAL; + } + + return 0; +} + +static ulong rk3568_cpll_div_get_rate(struct rk3568_clk_priv *priv, + ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + int div, mask, shift, con; + + switch (clk_id) { + case CPLL_500M: + con = 78; + mask = CPLL_500M_DIV_MASK; + shift = CPLL_500M_DIV_SHIFT; + break; + case CPLL_333M: + con = 79; + mask = CPLL_333M_DIV_MASK; + shift = CPLL_333M_DIV_SHIFT; + break; + case CPLL_250M: + con = 79; + mask = CPLL_250M_DIV_MASK; + shift = CPLL_250M_DIV_SHIFT; + break; + case CPLL_125M: + con = 80; + mask = CPLL_125M_DIV_MASK; + shift = CPLL_125M_DIV_SHIFT; + break; + case CPLL_100M: + con = 82; + mask = CPLL_100M_DIV_MASK; + shift = CPLL_100M_DIV_SHIFT; + break; + case CPLL_62P5M: + con = 80; + mask = CPLL_62P5M_DIV_MASK; + shift = CPLL_62P5M_DIV_SHIFT; + break; + case CPLL_50M: + con = 81; + mask = CPLL_50M_DIV_MASK; + shift = CPLL_50M_DIV_SHIFT; + break; + case CPLL_25M: + con = 81; + mask = CPLL_25M_DIV_MASK; + shift = CPLL_25M_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + div = (readl(&cru->clksel_con[con]) & mask) >> shift; + return DIV_TO_RATE(priv->cpll_hz, div); +} + +static ulong rk3568_cpll_div_set_rate(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int div, mask, shift, con; + + switch (clk_id) { + case CPLL_500M: + con = 78; + mask = CPLL_500M_DIV_MASK; + shift = CPLL_500M_DIV_SHIFT; + break; + case CPLL_333M: + con = 79; + mask = CPLL_333M_DIV_MASK; + shift = CPLL_333M_DIV_SHIFT; + break; + case CPLL_250M: + con = 79; + mask = CPLL_250M_DIV_MASK; + shift = CPLL_250M_DIV_SHIFT; + break; + case CPLL_125M: + con = 80; + mask = CPLL_125M_DIV_MASK; + shift = CPLL_125M_DIV_SHIFT; + break; + case CPLL_100M: + con = 82; + mask = CPLL_100M_DIV_MASK; + shift = CPLL_100M_DIV_SHIFT; + break; + case CPLL_62P5M: + con = 80; + mask = CPLL_62P5M_DIV_MASK; + shift = CPLL_62P5M_DIV_SHIFT; + break; + case CPLL_50M: + con = 81; + mask = CPLL_50M_DIV_MASK; + shift = CPLL_50M_DIV_SHIFT; + break; + case CPLL_25M: + con = 81; + mask = CPLL_25M_DIV_MASK; + shift = CPLL_25M_DIV_SHIFT; + break; + default: + return -ENOENT; + } + + div = DIV_ROUND_UP(priv->cpll_hz, rate); + assert(div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[con], + mask, (div - 1) << shift); + return rk3568_cpll_div_get_rate(priv, clk_id); +} + +static ulong rk3568_bus_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 con, sel, rate; + + switch (clk_id) { + case ACLK_BUS: + con = readl(&cru->clksel_con[50]); + sel = (con & ACLK_BUS_SEL_MASK) >> ACLK_BUS_SEL_SHIFT; + if (sel == ACLK_BUS_SEL_200M) + rate = 200 * MHz; + else if (sel == ACLK_BUS_SEL_150M) + rate = 150 * MHz; + else if (sel == ACLK_BUS_SEL_100M) + rate = 100 * MHz; + else + rate = OSC_HZ; + break; + case PCLK_BUS: + case PCLK_WDT_NS: + con = readl(&cru->clksel_con[50]); + sel = (con & PCLK_BUS_SEL_MASK) >> PCLK_BUS_SEL_SHIFT; + if (sel == PCLK_BUS_SEL_100M) + rate = 100 * MHz; + else if (sel == PCLK_BUS_SEL_75M) + rate = 75 * MHz; + else if (sel == PCLK_BUS_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3568_bus_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (clk_id) { + case ACLK_BUS: + if (rate == 200 * MHz) + src_clk = ACLK_BUS_SEL_200M; + else if (rate == 150 * MHz) + src_clk = ACLK_BUS_SEL_150M; + else if (rate == 100 * MHz) + src_clk = ACLK_BUS_SEL_100M; + else + src_clk = ACLK_BUS_SEL_24M; + rk_clrsetreg(&cru->clksel_con[50], + ACLK_BUS_SEL_MASK, + src_clk << ACLK_BUS_SEL_SHIFT); + break; + case PCLK_BUS: + case PCLK_WDT_NS: + if (rate == 100 * MHz) + src_clk = PCLK_BUS_SEL_100M; + else if (rate == 75 * MHz) + src_clk = PCLK_BUS_SEL_75M; + else if (rate == 50 * MHz) + src_clk = PCLK_BUS_SEL_50M; + else + src_clk = PCLK_BUS_SEL_24M; + rk_clrsetreg(&cru->clksel_con[50], + PCLK_BUS_SEL_MASK, + src_clk << PCLK_BUS_SEL_SHIFT); + break; + + default: + printf("do not support this bus freq\n"); + return -EINVAL; + } + + return rk3568_bus_get_clk(priv, clk_id); +} + +static ulong rk3568_perimid_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 con, sel, rate; + + switch (clk_id) { + case ACLK_PERIMID: + con = readl(&cru->clksel_con[10]); + sel = (con & ACLK_PERIMID_SEL_MASK) >> ACLK_PERIMID_SEL_SHIFT; + if (sel == ACLK_PERIMID_SEL_300M) + rate = 300 * MHz; + else if (sel == ACLK_PERIMID_SEL_200M) + rate = 200 * MHz; + else if (sel == ACLK_PERIMID_SEL_100M) + rate = 100 * MHz; + else + rate = OSC_HZ; + break; + case HCLK_PERIMID: + con = readl(&cru->clksel_con[10]); + sel = (con & HCLK_PERIMID_SEL_MASK) >> HCLK_PERIMID_SEL_SHIFT; + if (sel == HCLK_PERIMID_SEL_150M) + rate = 150 * MHz; + else if (sel == HCLK_PERIMID_SEL_100M) + rate = 100 * MHz; + else if (sel == HCLK_PERIMID_SEL_75M) + rate = 75 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3568_perimid_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (clk_id) { + case ACLK_PERIMID: + if (rate == 300 * MHz) + src_clk = ACLK_PERIMID_SEL_300M; + else if (rate == 200 * MHz) + src_clk = ACLK_PERIMID_SEL_200M; + else if (rate == 100 * MHz) + src_clk = ACLK_PERIMID_SEL_100M; + else + src_clk = ACLK_PERIMID_SEL_24M; + rk_clrsetreg(&cru->clksel_con[10], + ACLK_PERIMID_SEL_MASK, + src_clk << ACLK_PERIMID_SEL_SHIFT); + break; + case HCLK_PERIMID: + if (rate == 150 * MHz) + src_clk = HCLK_PERIMID_SEL_150M; + else if (rate == 100 * MHz) + src_clk = HCLK_PERIMID_SEL_100M; + else if (rate == 75 * MHz) + src_clk = HCLK_PERIMID_SEL_75M; + else + src_clk = HCLK_PERIMID_SEL_24M; + rk_clrsetreg(&cru->clksel_con[10], + HCLK_PERIMID_SEL_MASK, + src_clk << HCLK_PERIMID_SEL_SHIFT); + break; + + default: + printf("do not support this permid freq\n"); + return -EINVAL; + } + + return rk3568_perimid_get_clk(priv, clk_id); +} + +static ulong rk3568_top_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 con, sel, rate; + + switch (clk_id) { + case ACLK_TOP_HIGH: + con = readl(&cru->clksel_con[73]); + sel = (con & ACLK_TOP_HIGH_SEL_MASK) >> ACLK_TOP_HIGH_SEL_SHIFT; + if (sel == ACLK_TOP_HIGH_SEL_500M) + rate = 500 * MHz; + else if (sel == ACLK_TOP_HIGH_SEL_400M) + rate = 400 * MHz; + else if (sel == ACLK_TOP_HIGH_SEL_300M) + rate = 300 * MHz; + else + rate = OSC_HZ; + break; + case ACLK_TOP_LOW: + con = readl(&cru->clksel_con[73]); + sel = (con & ACLK_TOP_LOW_SEL_MASK) >> ACLK_TOP_LOW_SEL_SHIFT; + if (sel == ACLK_TOP_LOW_SEL_400M) + rate = 400 * MHz; + else if (sel == ACLK_TOP_LOW_SEL_300M) + rate = 300 * MHz; + else if (sel == ACLK_TOP_LOW_SEL_200M) + rate = 200 * MHz; + else + rate = OSC_HZ; + break; + case HCLK_TOP: + con = readl(&cru->clksel_con[73]); + sel = (con & HCLK_TOP_SEL_MASK) >> HCLK_TOP_SEL_SHIFT; + if (sel == HCLK_TOP_SEL_150M) + rate = 150 * MHz; + else if (sel == HCLK_TOP_SEL_100M) + rate = 100 * MHz; + else if (sel == HCLK_TOP_SEL_75M) + rate = 75 * MHz; + else + rate = OSC_HZ; + break; + case PCLK_TOP: + con = readl(&cru->clksel_con[73]); + sel = (con & PCLK_TOP_SEL_MASK) >> PCLK_TOP_SEL_SHIFT; + if (sel == PCLK_TOP_SEL_100M) + rate = 100 * MHz; + else if (sel == PCLK_TOP_SEL_75M) + rate = 75 * MHz; + else if (sel == PCLK_TOP_SEL_50M) + rate = 50 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3568_top_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (clk_id) { + case ACLK_TOP_HIGH: + if (rate == 500 * MHz) + src_clk = ACLK_TOP_HIGH_SEL_500M; + else if (rate == 400 * MHz) + src_clk = ACLK_TOP_HIGH_SEL_400M; + else if (rate == 300 * MHz) + src_clk = ACLK_TOP_HIGH_SEL_300M; + else + src_clk = ACLK_TOP_HIGH_SEL_24M; + rk_clrsetreg(&cru->clksel_con[73], + ACLK_TOP_HIGH_SEL_MASK, + src_clk << ACLK_TOP_HIGH_SEL_SHIFT); + break; + case ACLK_TOP_LOW: + if (rate == 400 * MHz) + src_clk = ACLK_TOP_LOW_SEL_400M; + else if (rate == 300 * MHz) + src_clk = ACLK_TOP_LOW_SEL_300M; + else if (rate == 200 * MHz) + src_clk = ACLK_TOP_LOW_SEL_200M; + else + src_clk = ACLK_TOP_LOW_SEL_24M; + rk_clrsetreg(&cru->clksel_con[73], + ACLK_TOP_LOW_SEL_MASK, + src_clk << ACLK_TOP_LOW_SEL_SHIFT); + break; + case HCLK_TOP: + if (rate == 150 * MHz) + src_clk = HCLK_TOP_SEL_150M; + else if (rate == 100 * MHz) + src_clk = HCLK_TOP_SEL_100M; + else if (rate == 75 * MHz) + src_clk = HCLK_TOP_SEL_75M; + else + src_clk = HCLK_TOP_SEL_24M; + rk_clrsetreg(&cru->clksel_con[73], + HCLK_TOP_SEL_MASK, + src_clk << HCLK_TOP_SEL_SHIFT); + break; + case PCLK_TOP: + if (rate == 100 * MHz) + src_clk = PCLK_TOP_SEL_100M; + else if (rate == 75 * MHz) + src_clk = PCLK_TOP_SEL_75M; + else if (rate == 50 * MHz) + src_clk = PCLK_TOP_SEL_50M; + else + src_clk = PCLK_TOP_SEL_24M; + rk_clrsetreg(&cru->clksel_con[73], + PCLK_TOP_SEL_MASK, + src_clk << PCLK_TOP_SEL_SHIFT); + break; + + default: + printf("do not support this permid freq\n"); + return -EINVAL; + } + + return rk3568_top_get_clk(priv, clk_id); +} + +static ulong rk3568_i2c_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + ulong rate; + + switch (clk_id) { + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + con = readl(&cru->clksel_con[71]); + sel = (con & CLK_I2C_SEL_MASK) >> CLK_I2C_SEL_SHIFT; + if (sel == CLK_I2C_SEL_200M) + rate = 200 * MHz; + else if (sel == CLK_I2C_SEL_100M) + rate = 100 * MHz; + else if (sel == CLK_I2C_SEL_CPLL_100M) + rate = 100 * MHz; + else + rate = OSC_HZ; + break; + default: + return -ENOENT; + } + + return rate; +} + +static ulong rk3568_i2c_set_clk(struct rk3568_clk_priv *priv, ulong clk_id, + ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + if (rate == 200 * MHz) + src_clk = CLK_I2C_SEL_200M; + else if (rate == 100 * MHz) + src_clk = CLK_I2C_SEL_100M; + else + src_clk = CLK_I2C_SEL_24M; + + switch (clk_id) { + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + rk_clrsetreg(&cru->clksel_con[71], CLK_I2C_SEL_MASK, + src_clk << CLK_I2C_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3568_i2c_get_clk(priv, clk_id); +} + +static ulong rk3568_spi_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[72]); + + switch (clk_id) { + case CLK_SPI0: + sel = (con & CLK_SPI0_SEL_MASK) >> CLK_SPI0_SEL_SHIFT; + break; + case CLK_SPI1: + sel = (con & CLK_SPI1_SEL_MASK) >> CLK_SPI1_SEL_SHIFT; + break; + case CLK_SPI2: + sel = (con & CLK_SPI2_SEL_MASK) >> CLK_SPI2_SEL_SHIFT; + break; + case CLK_SPI3: + sel = (con & CLK_SPI3_SEL_MASK) >> CLK_SPI3_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + switch (sel) { + case CLK_SPI_SEL_200M: + return 200 * MHz; + case CLK_SPI_SEL_24M: + return OSC_HZ; + case CLK_SPI_SEL_CPLL_100M: + return 100 * MHz; + default: + return -ENOENT; + } +} + +static ulong rk3568_spi_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + if (rate == 200 * MHz) + src_clk = CLK_SPI_SEL_200M; + else if (rate == 100 * MHz) + src_clk = CLK_SPI_SEL_CPLL_100M; + else + src_clk = CLK_SPI_SEL_24M; + + switch (clk_id) { + case CLK_SPI0: + rk_clrsetreg(&cru->clksel_con[72], + CLK_SPI0_SEL_MASK, + src_clk << CLK_SPI0_SEL_SHIFT); + break; + case CLK_SPI1: + rk_clrsetreg(&cru->clksel_con[72], + CLK_SPI1_SEL_MASK, + src_clk << CLK_SPI1_SEL_SHIFT); + break; + case CLK_SPI2: + rk_clrsetreg(&cru->clksel_con[72], + CLK_SPI2_SEL_MASK, + src_clk << CLK_SPI2_SEL_SHIFT); + break; + case CLK_SPI3: + rk_clrsetreg(&cru->clksel_con[72], + CLK_SPI3_SEL_MASK, + src_clk << CLK_SPI3_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3568_spi_get_clk(priv, clk_id); +} + +static ulong rk3568_pwm_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[72]); + + switch (clk_id) { + case CLK_PWM1: + sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM3_SEL_SHIFT; + break; + case CLK_PWM2: + sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT; + break; + case CLK_PWM3: + sel = (con & CLK_PWM3_SEL_MASK) >> CLK_PWM3_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + switch (sel) { + case CLK_PWM_SEL_100M: + return 100 * MHz; + case CLK_PWM_SEL_24M: + return OSC_HZ; + case CLK_PWM_SEL_CPLL_100M: + return 100 * MHz; + default: + return -ENOENT; + } +} + +static ulong rk3568_pwm_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + if (rate == 100 * MHz) + src_clk = CLK_PWM_SEL_100M; + else + src_clk = CLK_PWM_SEL_24M; + + switch (clk_id) { + case CLK_PWM1: + rk_clrsetreg(&cru->clksel_con[72], + CLK_PWM1_SEL_MASK, + src_clk << CLK_PWM1_SEL_SHIFT); + break; + case CLK_PWM2: + rk_clrsetreg(&cru->clksel_con[72], + CLK_PWM2_SEL_MASK, + src_clk << CLK_PWM2_SEL_SHIFT); + break; + case CLK_PWM3: + rk_clrsetreg(&cru->clksel_con[72], + CLK_PWM3_SEL_MASK, + src_clk << CLK_PWM3_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3568_pwm_get_clk(priv, clk_id); +} + +static ulong rk3568_adc_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 div, sel, con, prate; + + switch (clk_id) { + case CLK_SARADC: + return OSC_HZ; + case CLK_TSADC_TSEN: + con = readl(&cru->clksel_con[51]); + div = (con & CLK_TSADC_TSEN_DIV_MASK) >> + CLK_TSADC_TSEN_DIV_SHIFT; + sel = (con & CLK_TSADC_TSEN_SEL_MASK) >> + CLK_TSADC_TSEN_SEL_SHIFT; + if (sel == CLK_TSADC_TSEN_SEL_24M) + prate = OSC_HZ; + else + prate = 100 * MHz; + return DIV_TO_RATE(prate, div); + case CLK_TSADC: + con = readl(&cru->clksel_con[51]); + div = (con & CLK_TSADC_DIV_MASK) >> CLK_TSADC_DIV_SHIFT; + prate = rk3568_adc_get_clk(priv, CLK_TSADC_TSEN); + return DIV_TO_RATE(prate, div); + default: + return -ENOENT; + } +} + +static ulong rk3568_adc_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk_div; + ulong prate = 0; + + switch (clk_id) { + case CLK_SARADC: + return OSC_HZ; + case CLK_TSADC_TSEN: + if (!(OSC_HZ % rate)) { + src_clk_div = DIV_ROUND_UP(OSC_HZ, rate); + assert(src_clk_div - 1 <= 7); + rk_clrsetreg(&cru->clksel_con[51], + CLK_TSADC_TSEN_SEL_MASK | + CLK_TSADC_TSEN_DIV_MASK, + (CLK_TSADC_TSEN_SEL_24M << + CLK_TSADC_TSEN_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_TSADC_TSEN_DIV_SHIFT); + } else { + src_clk_div = DIV_ROUND_UP(100 * MHz, rate); + assert(src_clk_div - 1 <= 7); + rk_clrsetreg(&cru->clksel_con[51], + CLK_TSADC_TSEN_SEL_MASK | + CLK_TSADC_TSEN_DIV_MASK, + (CLK_TSADC_TSEN_SEL_100M << + CLK_TSADC_TSEN_SEL_SHIFT) | + (src_clk_div - 1) << + CLK_TSADC_TSEN_DIV_SHIFT); + } + break; + case CLK_TSADC: + prate = rk3568_adc_get_clk(priv, CLK_TSADC_TSEN); + src_clk_div = DIV_ROUND_UP(prate, rate); + assert(src_clk_div - 1 <= 128); + rk_clrsetreg(&cru->clksel_con[51], + CLK_TSADC_DIV_MASK, + (src_clk_div - 1) << CLK_TSADC_DIV_SHIFT); + break; + default: + return -ENOENT; + } + return rk3568_adc_get_clk(priv, clk_id); +} + +static ulong rk3568_crypto_get_rate(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + switch (clk_id) { + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + con = readl(&cru->clksel_con[27]); + sel = (con & ACLK_SECURE_FLASH_SEL_MASK) >> + ACLK_SECURE_FLASH_SEL_SHIFT; + if (sel == ACLK_SECURE_FLASH_SEL_200M) + return 200 * MHz; + else if (sel == ACLK_SECURE_FLASH_SEL_150M) + return 150 * MHz; + else if (sel == ACLK_SECURE_FLASH_SEL_100M) + return 100 * MHz; + else + return 24 * MHz; + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + con = readl(&cru->clksel_con[27]); + sel = (con & HCLK_SECURE_FLASH_SEL_MASK) >> + HCLK_SECURE_FLASH_SEL_SHIFT; + if (sel == HCLK_SECURE_FLASH_SEL_150M) + return 150 * MHz; + else if (sel == HCLK_SECURE_FLASH_SEL_100M) + return 100 * MHz; + else if (sel == HCLK_SECURE_FLASH_SEL_75M) + return 75 * MHz; + else + return 24 * MHz; + case CLK_CRYPTO_NS_CORE: + con = readl(&cru->clksel_con[27]); + sel = (con & CLK_CRYPTO_CORE_SEL_MASK) >> + CLK_CRYPTO_CORE_SEL_SHIFT; + if (sel == CLK_CRYPTO_CORE_SEL_200M) + return 200 * MHz; + else if (sel == CLK_CRYPTO_CORE_SEL_150M) + return 150 * MHz; + else + return 100 * MHz; + case CLK_CRYPTO_NS_PKA: + con = readl(&cru->clksel_con[27]); + sel = (con & CLK_CRYPTO_PKA_SEL_MASK) >> + CLK_CRYPTO_PKA_SEL_SHIFT; + if (sel == CLK_CRYPTO_PKA_SEL_300M) + return 300 * MHz; + else if (sel == CLK_CRYPTO_PKA_SEL_200M) + return 200 * MHz; + else + return 100 * MHz; + default: + return -ENOENT; + } +} + +static ulong rk3568_crypto_set_rate(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + u32 src_clk, mask, shift; + + switch (clk_id) { + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + mask = ACLK_SECURE_FLASH_SEL_MASK; + shift = ACLK_SECURE_FLASH_SEL_SHIFT; + if (rate == 200 * MHz) + src_clk = ACLK_SECURE_FLASH_SEL_200M; + else if (rate == 150 * MHz) + src_clk = ACLK_SECURE_FLASH_SEL_150M; + else if (rate == 100 * MHz) + src_clk = ACLK_SECURE_FLASH_SEL_100M; + else + src_clk = ACLK_SECURE_FLASH_SEL_24M; + break; + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + mask = HCLK_SECURE_FLASH_SEL_MASK; + shift = HCLK_SECURE_FLASH_SEL_SHIFT; + if (rate == 150 * MHz) + src_clk = HCLK_SECURE_FLASH_SEL_150M; + else if (rate == 100 * MHz) + src_clk = HCLK_SECURE_FLASH_SEL_100M; + else if (rate == 75 * MHz) + src_clk = HCLK_SECURE_FLASH_SEL_75M; + else + src_clk = HCLK_SECURE_FLASH_SEL_24M; + break; + case CLK_CRYPTO_NS_CORE: + mask = CLK_CRYPTO_CORE_SEL_MASK; + shift = CLK_CRYPTO_CORE_SEL_SHIFT; + if (rate == 200 * MHz) + src_clk = CLK_CRYPTO_CORE_SEL_200M; + else if (rate == 150 * MHz) + src_clk = CLK_CRYPTO_CORE_SEL_150M; + else + src_clk = CLK_CRYPTO_CORE_SEL_100M; + break; + case CLK_CRYPTO_NS_PKA: + mask = CLK_CRYPTO_PKA_SEL_MASK; + shift = CLK_CRYPTO_PKA_SEL_SHIFT; + if (rate == 300 * MHz) + src_clk = CLK_CRYPTO_PKA_SEL_300M; + else if (rate == 200 * MHz) + src_clk = CLK_CRYPTO_PKA_SEL_200M; + else + src_clk = CLK_CRYPTO_PKA_SEL_100M; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[27], mask, src_clk << shift); + + return rk3568_crypto_get_rate(priv, clk_id); +} + +static ulong rk3568_sdmmc_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + switch (clk_id) { + case HCLK_SDMMC0: + case CLK_SDMMC0: + con = readl(&cru->clksel_con[30]); + sel = (con & CLK_SDMMC0_SEL_MASK) >> CLK_SDMMC0_SEL_SHIFT; + break; + case CLK_SDMMC1: + con = readl(&cru->clksel_con[30]); + sel = (con & CLK_SDMMC1_SEL_MASK) >> CLK_SDMMC1_SEL_SHIFT; + break; + case CLK_SDMMC2: + con = readl(&cru->clksel_con[32]); + sel = (con & CLK_SDMMC2_SEL_MASK) >> CLK_SDMMC2_SEL_SHIFT; + break; + default: + return -ENOENT; + } + + switch (sel) { + case CLK_SDMMC_SEL_24M: + return OSC_HZ; + case CLK_SDMMC_SEL_400M: + return 400 * MHz; + case CLK_SDMMC_SEL_300M: + return 300 * MHz; + case CLK_SDMMC_SEL_100M: + return 100 * MHz; + case CLK_SDMMC_SEL_50M: + return 50 * MHz; + case CLK_SDMMC_SEL_750K: + return 750 * KHz; + default: + return -ENOENT; + } +} + +static ulong rk3568_sdmmc_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case OSC_HZ: + src_clk = CLK_SDMMC_SEL_24M; + break; + case 400 * MHz: + src_clk = CLK_SDMMC_SEL_400M; + break; + case 300 * MHz: + src_clk = CLK_SDMMC_SEL_300M; + break; + case 100 * MHz: + src_clk = CLK_SDMMC_SEL_100M; + break; + case 52 * MHz: + case 50 * MHz: + src_clk = CLK_SDMMC_SEL_50M; + break; + case 750 * KHz: + case 400 * KHz: + src_clk = CLK_SDMMC_SEL_750K; + break; + default: + return -ENOENT; + } + + switch (clk_id) { + case HCLK_SDMMC0: + case CLK_SDMMC0: + rk_clrsetreg(&cru->clksel_con[30], + CLK_SDMMC0_SEL_MASK, + src_clk << CLK_SDMMC0_SEL_SHIFT); + break; + case CLK_SDMMC1: + rk_clrsetreg(&cru->clksel_con[30], + CLK_SDMMC1_SEL_MASK, + src_clk << CLK_SDMMC1_SEL_SHIFT); + break; + case CLK_SDMMC2: + rk_clrsetreg(&cru->clksel_con[32], + CLK_SDMMC2_SEL_MASK, + src_clk << CLK_SDMMC2_SEL_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3568_sdmmc_get_clk(priv, clk_id); +} + +static ulong rk3568_sfc_get_clk(struct rk3568_clk_priv *priv) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[28]); + sel = (con & SCLK_SFC_SEL_MASK) >> SCLK_SFC_SEL_SHIFT; + switch (sel) { + case SCLK_SFC_SEL_24M: + return OSC_HZ; + case SCLK_SFC_SEL_50M: + return 50 * MHz; + case SCLK_SFC_SEL_75M: + return 75 * MHz; + case SCLK_SFC_SEL_100M: + return 100 * MHz; + case SCLK_SFC_SEL_125M: + return 125 * MHz; + case SCLK_SFC_SEL_150M: + return 150 * KHz; + default: + return -ENOENT; + } +} + +static ulong rk3568_sfc_set_clk(struct rk3568_clk_priv *priv, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case OSC_HZ: + src_clk = SCLK_SFC_SEL_24M; + break; + case 50 * MHz: + src_clk = SCLK_SFC_SEL_50M; + break; + case 75 * MHz: + src_clk = SCLK_SFC_SEL_75M; + break; + case 100 * MHz: + src_clk = SCLK_SFC_SEL_100M; + break; + case 125 * MHz: + src_clk = SCLK_SFC_SEL_125M; + break; + case 150 * KHz: + src_clk = SCLK_SFC_SEL_150M; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[28], + SCLK_SFC_SEL_MASK, + src_clk << SCLK_SFC_SEL_SHIFT); + + return rk3568_sfc_get_clk(priv); +} + +static ulong rk3568_nand_get_clk(struct rk3568_clk_priv *priv) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[28]); + sel = (con & NCLK_NANDC_SEL_MASK) >> NCLK_NANDC_SEL_SHIFT; + switch (sel) { + case NCLK_NANDC_SEL_200M: + return 200 * MHz; + case NCLK_NANDC_SEL_150M: + return 150 * MHz; + case NCLK_NANDC_SEL_100M: + return 100 * MHz; + case NCLK_NANDC_SEL_24M: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3568_nand_set_clk(struct rk3568_clk_priv *priv, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case OSC_HZ: + src_clk = NCLK_NANDC_SEL_24M; + break; + case 100 * MHz: + src_clk = NCLK_NANDC_SEL_100M; + break; + case 150 * MHz: + src_clk = NCLK_NANDC_SEL_150M; + break; + case 200 * MHz: + src_clk = NCLK_NANDC_SEL_200M; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[28], + NCLK_NANDC_SEL_MASK, + src_clk << NCLK_NANDC_SEL_SHIFT); + + return rk3568_nand_get_clk(priv); +} + +static ulong rk3568_emmc_get_clk(struct rk3568_clk_priv *priv) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[28]); + sel = (con & CCLK_EMMC_SEL_MASK) >> CCLK_EMMC_SEL_SHIFT; + switch (sel) { + case CCLK_EMMC_SEL_200M: + return 200 * MHz; + case CCLK_EMMC_SEL_150M: + return 150 * MHz; + case CCLK_EMMC_SEL_100M: + return 100 * MHz; + case CCLK_EMMC_SEL_50M: + return 50 * MHz; + case CCLK_EMMC_SEL_375K: + return 375 * KHz; + case CCLK_EMMC_SEL_24M: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3568_emmc_set_clk(struct rk3568_clk_priv *priv, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case OSC_HZ: + src_clk = CCLK_EMMC_SEL_24M; + break; + case 52 * MHz: + case 50 * MHz: + src_clk = CCLK_EMMC_SEL_50M; + break; + case 100 * MHz: + src_clk = CCLK_EMMC_SEL_100M; + break; + case 150 * MHz: + src_clk = CCLK_EMMC_SEL_150M; + break; + case 200 * MHz: + src_clk = CCLK_EMMC_SEL_200M; + break; + case 400 * KHz: + case 375 * KHz: + src_clk = CCLK_EMMC_SEL_375K; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[28], + CCLK_EMMC_SEL_MASK, + src_clk << CCLK_EMMC_SEL_SHIFT); + + return rk3568_emmc_get_clk(priv); +} + +static ulong rk3568_emmc_get_bclk(struct rk3568_clk_priv *priv) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[28]); + sel = (con & BCLK_EMMC_SEL_MASK) >> BCLK_EMMC_SEL_SHIFT; + switch (sel) { + case BCLK_EMMC_SEL_200M: + return 200 * MHz; + case BCLK_EMMC_SEL_150M: + return 150 * MHz; + case BCLK_EMMC_SEL_125M: + return 125 * MHz; + default: + return -ENOENT; + } +} + +static ulong rk3568_emmc_set_bclk(struct rk3568_clk_priv *priv, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case 200 * MHz: + src_clk = BCLK_EMMC_SEL_200M; + break; + case 150 * MHz: + src_clk = BCLK_EMMC_SEL_150M; + break; + case 125 * MHz: + src_clk = BCLK_EMMC_SEL_125M; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[28], + BCLK_EMMC_SEL_MASK, + src_clk << BCLK_EMMC_SEL_SHIFT); + + return rk3568_emmc_get_bclk(priv); +} + +#ifndef CONFIG_SPL_BUILD +static ulong rk3568_aclk_vop_get_clk(struct rk3568_clk_priv *priv) +{ + struct rk3568_cru *cru = priv->cru; + u32 div, sel, con, parent; + + con = readl(&cru->clksel_con[38]); + div = (con & ACLK_VOP_PRE_DIV_MASK) >> ACLK_VOP_PRE_DIV_SHIFT; + sel = (con & ACLK_VOP_PRE_SEL_MASK) >> ACLK_VOP_PRE_SEL_SHIFT; + if (sel == ACLK_VOP_PRE_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == ACLK_VOP_PRE_SEL_CPLL) + parent = priv->cpll_hz; + else if (sel == ACLK_VOP_PRE_SEL_VPLL) + parent = priv->vpll_hz; + else + parent = priv->hpll_hz; + + return DIV_TO_RATE(parent, div); +} + +static ulong rk3568_aclk_vop_set_clk(struct rk3568_clk_priv *priv, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk_div, src_clk_mux; + + if ((priv->cpll_hz % rate) == 0) { + src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate); + src_clk_mux = ACLK_VOP_PRE_SEL_CPLL; + } else { + src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate); + src_clk_mux = ACLK_VOP_PRE_SEL_GPLL; + } + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[38], + ACLK_VOP_PRE_SEL_MASK | ACLK_VOP_PRE_DIV_MASK, + src_clk_mux << ACLK_VOP_PRE_SEL_SHIFT | + (src_clk_div - 1) << ACLK_VOP_PRE_DIV_SHIFT); + + return rk3568_aclk_vop_get_clk(priv); +} + +static ulong rk3568_dclk_vop_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 conid, div, sel, con, parent; + + switch (clk_id) { + case DCLK_VOP0: + conid = 39; + break; + case DCLK_VOP1: + conid = 40; + break; + case DCLK_VOP2: + conid = 41; + break; + default: + return -ENOENT; + } + + con = readl(&cru->clksel_con[conid]); + div = (con & DCLK0_VOP_DIV_MASK) >> DCLK0_VOP_DIV_SHIFT; + sel = (con & DCLK0_VOP_SEL_MASK) >> DCLK0_VOP_SEL_SHIFT; + if (sel == DCLK_VOP_SEL_HPLL) + parent = rk3568_pmu_pll_get_rate(priv, HPLL); + else if (sel == DCLK_VOP_SEL_VPLL) + parent = rockchip_pll_get_rate(&rk3568_pll_clks[VPLL], + priv->cru, VPLL); + else if (sel == DCLK_VOP_SEL_GPLL) + parent = priv->gpll_hz; + else if (sel == DCLK_VOP_SEL_CPLL) + parent = priv->cpll_hz; + else + return -ENOENT; + + return DIV_TO_RATE(parent, div); +} + +#define RK3568_VOP_PLL_LIMIT_FREQ 600000000 + +static ulong rk3568_dclk_vop_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + ulong pll_rate, now, best_rate = 0; + u32 i, conid, con, sel, div, best_div = 0, best_sel = 0; + + switch (clk_id) { + case DCLK_VOP0: + conid = 39; + break; + case DCLK_VOP1: + conid = 40; + break; + case DCLK_VOP2: + conid = 41; + break; + default: + return -ENOENT; + } + + con = readl(&cru->clksel_con[conid]); + sel = (con & DCLK0_VOP_SEL_MASK) >> DCLK0_VOP_SEL_SHIFT; + + if (sel == DCLK_VOP_SEL_HPLL) { + div = 1; + rk_clrsetreg(&cru->clksel_con[conid], + DCLK0_VOP_DIV_MASK | DCLK0_VOP_SEL_MASK, + (DCLK_VOP_SEL_HPLL << DCLK0_VOP_SEL_SHIFT) | + ((div - 1) << DCLK0_VOP_DIV_SHIFT)); + rk3568_pmu_pll_set_rate(priv, HPLL, div * rate); + } else if (sel == DCLK_VOP_SEL_VPLL) { + div = DIV_ROUND_UP(RK3568_VOP_PLL_LIMIT_FREQ, rate); + rk_clrsetreg(&cru->clksel_con[conid], + DCLK0_VOP_DIV_MASK | DCLK0_VOP_SEL_MASK, + (DCLK_VOP_SEL_VPLL << DCLK0_VOP_SEL_SHIFT) | + ((div - 1) << DCLK0_VOP_DIV_SHIFT)); + rockchip_pll_set_rate(&rk3568_pll_clks[VPLL], + priv->cru, VPLL, div * rate); + } else { + for (i = 0; i <= DCLK_VOP_SEL_CPLL; i++) { + switch (i) { + case DCLK_VOP_SEL_GPLL: + pll_rate = priv->gpll_hz; + break; + case DCLK_VOP_SEL_CPLL: + pll_rate = priv->cpll_hz; + break; + default: + printf("do not support this vop pll sel\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP(pll_rate, rate); + if (div > 255) + continue; + now = pll_rate / div; + if (abs(rate - now) < abs(rate - best_rate)) { + best_rate = now; + best_div = div; + best_sel = i; + } + debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n", + pll_rate, best_rate, best_div, best_sel); + } + + if (best_rate) { + rk_clrsetreg(&cru->clksel_con[conid], + DCLK0_VOP_DIV_MASK | DCLK0_VOP_SEL_MASK, + best_sel << DCLK0_VOP_SEL_SHIFT | + (best_div - 1) << DCLK0_VOP_DIV_SHIFT); + } else { + printf("do not support this vop freq %lu\n", rate); + return -EINVAL; + } + } + return rk3568_dclk_vop_get_clk(priv, clk_id); +} + +static ulong rk3568_gmac_src_get_clk(struct rk3568_clk_priv *priv, + ulong mac_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & CLK_MAC0_2TOP_SEL_MASK) >> CLK_MAC0_2TOP_SEL_SHIFT; + + switch (sel) { + case CLK_MAC0_2TOP_SEL_125M: + return 125 * MHz; + case CLK_MAC0_2TOP_SEL_50M: + return 50 * MHz; + case CLK_MAC0_2TOP_SEL_25M: + return 25 * MHz; + case CLK_MAC0_2TOP_SEL_PPLL: + return rk3568_pmu_pll_get_rate(priv, HPLL); + default: + return -ENOENT; + } +} + +static ulong rk3568_gmac_src_set_clk(struct rk3568_clk_priv *priv, + ulong mac_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case 125 * MHz: + src_clk = CLK_MAC0_2TOP_SEL_125M; + break; + case 50 * MHz: + src_clk = CLK_MAC0_2TOP_SEL_50M; + break; + case 25 * MHz: + src_clk = CLK_MAC0_2TOP_SEL_25M; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], + CLK_MAC0_2TOP_SEL_MASK, + src_clk << CLK_MAC0_2TOP_SEL_SHIFT); + + return rk3568_gmac_src_get_clk(priv, mac_id); +} + +static ulong rk3568_gmac_out_get_clk(struct rk3568_clk_priv *priv, + ulong mac_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & CLK_MAC0_OUT_SEL_MASK) >> CLK_MAC0_OUT_SEL_SHIFT; + + switch (sel) { + case CLK_MAC0_OUT_SEL_125M: + return 125 * MHz; + case CLK_MAC0_OUT_SEL_50M: + return 50 * MHz; + case CLK_MAC0_OUT_SEL_25M: + return 25 * MHz; + case CLK_MAC0_OUT_SEL_24M: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3568_gmac_out_set_clk(struct rk3568_clk_priv *priv, + ulong mac_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case 125 * MHz: + src_clk = CLK_MAC0_OUT_SEL_125M; + break; + case 50 * MHz: + src_clk = CLK_MAC0_OUT_SEL_50M; + break; + case 25 * MHz: + src_clk = CLK_MAC0_OUT_SEL_25M; + break; + case 24 * MHz: + src_clk = CLK_MAC0_OUT_SEL_24M; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], + CLK_MAC0_OUT_SEL_MASK, + src_clk << CLK_MAC0_OUT_SEL_SHIFT); + + return rk3568_gmac_out_get_clk(priv, mac_id); +} + +static ulong rk3568_gmac_ptp_ref_get_clk(struct rk3568_clk_priv *priv, + ulong mac_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 sel, con; + + con = readl(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & CLK_GMAC0_PTP_REF_SEL_MASK) >> CLK_GMAC0_PTP_REF_SEL_SHIFT; + + switch (sel) { + case CLK_GMAC0_PTP_REF_SEL_62_5M: + return 62500 * KHz; + case CLK_GMAC0_PTP_REF_SEL_100M: + return 100 * MHz; + case CLK_GMAC0_PTP_REF_SEL_50M: + return 50 * MHz; + case CLK_GMAC0_PTP_REF_SEL_24M: + return OSC_HZ; + default: + return -ENOENT; + } +} + +static ulong rk3568_gmac_ptp_ref_set_clk(struct rk3568_clk_priv *priv, + ulong mac_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk; + + switch (rate) { + case 62500 * KHz: + src_clk = CLK_GMAC0_PTP_REF_SEL_62_5M; + break; + case 100 * MHz: + src_clk = CLK_GMAC0_PTP_REF_SEL_100M; + break; + case 50 * MHz: + src_clk = CLK_GMAC0_PTP_REF_SEL_50M; + break; + case 24 * MHz: + src_clk = CLK_GMAC0_PTP_REF_SEL_24M; + break; + default: + return -ENOENT; + } + + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], + CLK_GMAC0_PTP_REF_SEL_MASK, + src_clk << CLK_GMAC0_PTP_REF_SEL_SHIFT); + + return rk3568_gmac_ptp_ref_get_clk(priv, mac_id); +} + +static ulong rk3568_gmac_tx_rx_set_clk(struct rk3568_clk_priv *priv, + ulong mac_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + u32 con, sel, div_sel; + + con = readl(&cru->clksel_con[31 + mac_id * 2]); + sel = (con & RMII0_MODE_MASK) >> RMII0_MODE_SHIFT; + + if (sel == RMII0_MODE_SEL_RGMII) { + if (rate == 2500000) + div_sel = RGMII0_CLK_SEL_2_5M; + else if (rate == 25000000) + div_sel = RGMII0_CLK_SEL_25M; + else + div_sel = RGMII0_CLK_SEL_125M; + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], + RGMII0_CLK_SEL_MASK, + div_sel << RGMII0_CLK_SEL_SHIFT); + } else if (sel == RMII0_MODE_SEL_RMII) { + if (rate == 2500000) + div_sel = RMII0_CLK_SEL_2_5M; + else + div_sel = RMII0_CLK_SEL_25M; + rk_clrsetreg(&cru->clksel_con[31 + mac_id * 2], + RMII0_CLK_SEL_MASK, + div_sel << RMII0_CLK_SEL_SHIFT); + } + + return 0; +} + +static ulong rk3568_ebc_get_clk(struct rk3568_clk_priv *priv) +{ + struct rk3568_cru *cru = priv->cru; + u32 con, div, p_rate; + + con = readl(&cru->clksel_con[79]); + div = (con & CPLL_333M_DIV_MASK) >> CPLL_333M_DIV_SHIFT; + p_rate = DIV_TO_RATE(priv->cpll_hz, div); + + con = readl(&cru->clksel_con[43]); + div = (con & DCLK_EBC_SEL_MASK) >> DCLK_EBC_SEL_SHIFT; + switch (div) { + case DCLK_EBC_SEL_GPLL_400M: + return 400 * MHz; + case DCLK_EBC_SEL_CPLL_333M: + return p_rate; + case DCLK_EBC_SEL_GPLL_200M: + return 200 * MHz; + default: + return -ENOENT; + } +} + +static ulong rk3568_ebc_set_clk(struct rk3568_clk_priv *priv, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk_div; + + src_clk_div = DIV_ROUND_UP(priv->cpll_hz, rate); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[79], + CPLL_333M_DIV_MASK, + (src_clk_div - 1) << CPLL_333M_DIV_SHIFT); + rk_clrsetreg(&cru->clksel_con[43], + DCLK_EBC_SEL_MASK, + DCLK_EBC_SEL_CPLL_333M << DCLK_EBC_SEL_SHIFT); + + return rk3568_ebc_get_clk(priv); +} + +static ulong rk3568_rkvdec_get_clk(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 con, div, src, p_rate; + + switch (clk_id) { + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + con = readl(&cru->clksel_con[47]); + src = (con & ACLK_RKVDEC_SEL_MASK) >> ACLK_RKVDEC_SEL_SHIFT; + div = (con & ACLK_RKVDEC_DIV_MASK) >> ACLK_RKVDEC_DIV_SHIFT; + if (src == ACLK_RKVDEC_SEL_CPLL) + p_rate = priv->cpll_hz; + else + p_rate = priv->gpll_hz; + return DIV_TO_RATE(p_rate, div); + case CLK_RKVDEC_CORE: + con = readl(&cru->clksel_con[49]); + src = (con & CLK_RKVDEC_CORE_SEL_MASK) + >> CLK_RKVDEC_CORE_SEL_SHIFT; + div = (con & CLK_RKVDEC_CORE_DIV_MASK) + >> CLK_RKVDEC_CORE_DIV_SHIFT; + if (src == CLK_RKVDEC_CORE_SEL_CPLL) + p_rate = priv->cpll_hz; + else if (src == CLK_RKVDEC_CORE_SEL_NPLL) + p_rate = priv->npll_hz; + else if (src == CLK_RKVDEC_CORE_SEL_VPLL) + p_rate = priv->vpll_hz; + else + p_rate = priv->gpll_hz; + return DIV_TO_RATE(p_rate, div); + default: + return -ENOENT; + } +} + +static ulong rk3568_rkvdec_set_clk(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + int src_clk_div, src, p_rate; + + switch (clk_id) { + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + src = (readl(&cru->clksel_con[47]) & ACLK_RKVDEC_SEL_MASK) + >> ACLK_RKVDEC_SEL_SHIFT; + if (src == ACLK_RKVDEC_SEL_CPLL) + p_rate = priv->cpll_hz; + else + p_rate = priv->gpll_hz; + src_clk_div = DIV_ROUND_UP(p_rate, rate); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[47], + ACLK_RKVDEC_SEL_MASK | + ACLK_RKVDEC_DIV_MASK, + (src << ACLK_RKVDEC_SEL_SHIFT) | + (src_clk_div - 1) << ACLK_RKVDEC_DIV_SHIFT); + break; + case CLK_RKVDEC_CORE: + src = (readl(&cru->clksel_con[49]) & CLK_RKVDEC_CORE_SEL_MASK) + >> CLK_RKVDEC_CORE_SEL_SHIFT; + if (src == CLK_RKVDEC_CORE_SEL_CPLL) + p_rate = priv->cpll_hz; + else if (src == CLK_RKVDEC_CORE_SEL_NPLL) + p_rate = priv->npll_hz; + else if (src == CLK_RKVDEC_CORE_SEL_VPLL) + p_rate = priv->vpll_hz; + else + p_rate = priv->gpll_hz; + src_clk_div = DIV_ROUND_UP(p_rate, rate); + assert(src_clk_div - 1 <= 31); + rk_clrsetreg(&cru->clksel_con[49], + CLK_RKVDEC_CORE_SEL_MASK | + CLK_RKVDEC_CORE_DIV_MASK, + (src << CLK_RKVDEC_CORE_SEL_SHIFT) | + (src_clk_div - 1) << CLK_RKVDEC_CORE_DIV_SHIFT); + break; + default: + return -ENOENT; + } + + return rk3568_rkvdec_get_clk(priv, clk_id); +} + +static ulong rk3568_uart_get_rate(struct rk3568_clk_priv *priv, ulong clk_id) +{ + struct rk3568_cru *cru = priv->cru; + u32 reg, con, fracdiv, div, src, p_src, p_rate; + unsigned long m, n; + + switch (clk_id) { + case SCLK_UART1: + reg = 52; + break; + case SCLK_UART2: + reg = 54; + break; + case SCLK_UART3: + reg = 56; + break; + case SCLK_UART4: + reg = 58; + break; + case SCLK_UART5: + reg = 60; + break; + case SCLK_UART6: + reg = 62; + break; + case SCLK_UART7: + reg = 64; + break; + case SCLK_UART8: + reg = 66; + break; + case SCLK_UART9: + reg = 68; + break; + default: + return -ENOENT; + } + con = readl(&cru->clksel_con[reg]); + src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT; + div = (con & CLK_UART_SRC_DIV_MASK) >> CLK_UART_SRC_DIV_SHIFT; + p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT; + if (p_src == CLK_UART_SRC_SEL_GPLL) + p_rate = priv->gpll_hz; + else if (p_src == CLK_UART_SRC_SEL_CPLL) + p_rate = priv->cpll_hz; + else + p_rate = 480000000; + if (src == CLK_UART_SEL_SRC) { + return DIV_TO_RATE(p_rate, div); + } else if (src == CLK_UART_SEL_FRAC) { + fracdiv = readl(&cru->clksel_con[reg + 1]); + n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK; + n >>= CLK_UART_FRAC_NUMERATOR_SHIFT; + m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK; + m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT; + return DIV_TO_RATE(p_rate, div) * n / m; + } else { + return OSC_HZ; + } +} + +static ulong rk3568_uart_set_rate(struct rk3568_clk_priv *priv, + ulong clk_id, ulong rate) +{ + struct rk3568_cru *cru = priv->cru; + u32 reg, clk_src, uart_src, div; + unsigned long m = 0, n = 0, val; + + if (priv->gpll_hz % rate == 0) { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_SRC; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (priv->cpll_hz % rate == 0) { + clk_src = CLK_UART_SRC_SEL_CPLL; + uart_src = CLK_UART_SEL_SRC; + div = DIV_ROUND_UP(priv->gpll_hz, rate); + } else if (rate == OSC_HZ) { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_XIN24M; + div = 2; + } else { + clk_src = CLK_UART_SRC_SEL_GPLL; + uart_src = CLK_UART_SEL_FRAC; + div = 2; + rational_best_approximation(rate, priv->gpll_hz / div, + GENMASK(16 - 1, 0), + GENMASK(16 - 1, 0), + &m, &n); + } + + switch (clk_id) { + case SCLK_UART1: + reg = 52; + break; + case SCLK_UART2: + reg = 54; + break; + case SCLK_UART3: + reg = 56; + break; + case SCLK_UART4: + reg = 58; + break; + case SCLK_UART5: + reg = 60; + break; + case SCLK_UART6: + reg = 62; + break; + case SCLK_UART7: + reg = 64; + break; + case SCLK_UART8: + reg = 66; + break; + case SCLK_UART9: + reg = 68; + break; + default: + return -ENOENT; + } + rk_clrsetreg(&cru->clksel_con[reg], + CLK_UART_SEL_MASK | CLK_UART_SRC_SEL_MASK | + CLK_UART_SRC_DIV_MASK, + (clk_src << CLK_UART_SRC_SEL_SHIFT) | + (uart_src << CLK_UART_SEL_SHIFT) | + ((div - 1) << CLK_UART_SRC_DIV_SHIFT)); + if (m && n) { + val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n; + writel(val, &cru->clksel_con[reg + 1]); + } + + return rk3568_uart_get_rate(priv, clk_id); +} +#endif + +static ulong rk3568_clk_get_rate(struct clk *clk) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + ulong rate = 0; + + if (!priv->gpll_hz) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + switch (clk->id) { + case PLL_APLL: + case ARMCLK: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[APLL], priv->cru, + APLL); + break; + case PLL_CPLL: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[CPLL], priv->cru, + CPLL); + break; + case PLL_GPLL: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[GPLL], priv->cru, + GPLL); + break; + case PLL_NPLL: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[NPLL], priv->cru, + NPLL); + break; + case PLL_VPLL: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[VPLL], priv->cru, + VPLL); + break; + case PLL_DPLL: + rate = rockchip_pll_get_rate(&rk3568_pll_clks[DPLL], priv->cru, + DPLL); + break; + case ACLK_BUS: + case PCLK_BUS: + case PCLK_WDT_NS: + rate = rk3568_bus_get_clk(priv, clk->id); + break; + case ACLK_PERIMID: + case HCLK_PERIMID: + rate = rk3568_perimid_get_clk(priv, clk->id); + break; + case ACLK_TOP_HIGH: + case ACLK_TOP_LOW: + case HCLK_TOP: + case PCLK_TOP: + rate = rk3568_top_get_clk(priv, clk->id); + break; + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + rate = rk3568_i2c_get_clk(priv, clk->id); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + rate = rk3568_spi_get_clk(priv, clk->id); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PWM3: + rate = rk3568_pwm_get_clk(priv, clk->id); + break; + case CLK_SARADC: + case CLK_TSADC_TSEN: + case CLK_TSADC: + rate = rk3568_adc_get_clk(priv, clk->id); + break; + case HCLK_SDMMC0: + case CLK_SDMMC0: + case CLK_SDMMC1: + case CLK_SDMMC2: + rate = rk3568_sdmmc_get_clk(priv, clk->id); + break; + case SCLK_SFC: + rate = rk3568_sfc_get_clk(priv); + break; + case NCLK_NANDC: + rate = rk3568_nand_get_clk(priv); + break; + case CCLK_EMMC: + rate = rk3568_emmc_get_clk(priv); + break; + case BCLK_EMMC: + rate = rk3568_emmc_get_bclk(priv); + break; +#ifndef CONFIG_SPL_BUILD + case ACLK_VOP: + rate = rk3568_aclk_vop_get_clk(priv); + break; + case DCLK_VOP0: + case DCLK_VOP1: + case DCLK_VOP2: + rate = rk3568_dclk_vop_get_clk(priv, clk->id); + break; + case SCLK_GMAC0: + case CLK_MAC0_2TOP: + case CLK_MAC0_REFOUT: + rate = rk3568_gmac_src_get_clk(priv, 0); + break; + case CLK_MAC0_OUT: + rate = rk3568_gmac_out_get_clk(priv, 0); + break; + case CLK_GMAC0_PTP_REF: + rate = rk3568_gmac_ptp_ref_get_clk(priv, 0); + break; + case SCLK_GMAC1: + case CLK_MAC1_2TOP: + case CLK_MAC1_REFOUT: + rate = rk3568_gmac_src_get_clk(priv, 1); + break; + case CLK_MAC1_OUT: + rate = rk3568_gmac_out_get_clk(priv, 1); + break; + case CLK_GMAC1_PTP_REF: + rate = rk3568_gmac_ptp_ref_get_clk(priv, 1); + break; + case DCLK_EBC: + rate = rk3568_ebc_get_clk(priv); + break; + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + case CLK_RKVDEC_CORE: + rate = rk3568_rkvdec_get_clk(priv, clk->id); + break; + case TCLK_WDT_NS: + rate = OSC_HZ; + break; + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + rate = rk3568_uart_get_rate(priv, clk->id); + break; +#endif + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + case CLK_CRYPTO_NS_CORE: + case CLK_CRYPTO_NS_PKA: + rate = rk3568_crypto_get_rate(priv, clk->id); + break; + case CPLL_500M: + case CPLL_333M: + case CPLL_250M: + case CPLL_125M: + case CPLL_100M: + case CPLL_62P5M: + case CPLL_50M: + case CPLL_25M: + rate = rk3568_cpll_div_get_rate(priv, clk->id); + break; + default: + return -ENOENT; + } + + return rate; +}; + +static ulong rk3568_clk_set_rate(struct clk *clk, ulong rate) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + ulong ret = 0; + + if (!priv->gpll_hz) { + printf("%s gpll=%lu\n", __func__, priv->gpll_hz); + return -ENOENT; + } + + switch (clk->id) { + case PLL_APLL: + case ARMCLK: + if (priv->armclk_hz) + rk3568_armclk_set_clk(priv, rate); + priv->armclk_hz = rate; + break; + case PLL_CPLL: + ret = rockchip_pll_set_rate(&rk3568_pll_clks[CPLL], priv->cru, + CPLL, rate); + priv->cpll_hz = rockchip_pll_get_rate(&rk3568_pll_clks[CPLL], + priv->cru, CPLL); + break; + case PLL_GPLL: + ret = rockchip_pll_set_rate(&rk3568_pll_clks[GPLL], priv->cru, + GPLL, rate); + priv->gpll_hz = rockchip_pll_get_rate(&rk3568_pll_clks[GPLL], + priv->cru, GPLL); + break; + case PLL_NPLL: + ret = rockchip_pll_set_rate(&rk3568_pll_clks[NPLL], priv->cru, + NPLL, rate); + break; + case PLL_VPLL: + ret = rockchip_pll_set_rate(&rk3568_pll_clks[VPLL], priv->cru, + VPLL, rate); + priv->vpll_hz = rockchip_pll_get_rate(&rk3568_pll_clks[VPLL], + priv->cru, + VPLL); + break; + case ACLK_BUS: + case PCLK_BUS: + case PCLK_WDT_NS: + ret = rk3568_bus_set_clk(priv, clk->id, rate); + break; + case ACLK_PERIMID: + case HCLK_PERIMID: + ret = rk3568_perimid_set_clk(priv, clk->id, rate); + break; + case ACLK_TOP_HIGH: + case ACLK_TOP_LOW: + case HCLK_TOP: + case PCLK_TOP: + ret = rk3568_top_set_clk(priv, clk->id, rate); + break; + case CLK_I2C1: + case CLK_I2C2: + case CLK_I2C3: + case CLK_I2C4: + case CLK_I2C5: + ret = rk3568_i2c_set_clk(priv, clk->id, rate); + break; + case CLK_SPI0: + case CLK_SPI1: + case CLK_SPI2: + case CLK_SPI3: + ret = rk3568_spi_set_clk(priv, clk->id, rate); + break; + case CLK_PWM1: + case CLK_PWM2: + case CLK_PWM3: + ret = rk3568_pwm_set_clk(priv, clk->id, rate); + break; + case CLK_SARADC: + case CLK_TSADC_TSEN: + case CLK_TSADC: + ret = rk3568_adc_set_clk(priv, clk->id, rate); + break; + case HCLK_SDMMC0: + case CLK_SDMMC0: + case CLK_SDMMC1: + case CLK_SDMMC2: + ret = rk3568_sdmmc_set_clk(priv, clk->id, rate); + break; + case SCLK_SFC: + ret = rk3568_sfc_set_clk(priv, rate); + break; + case NCLK_NANDC: + ret = rk3568_nand_set_clk(priv, rate); + break; + case CCLK_EMMC: + ret = rk3568_emmc_set_clk(priv, rate); + break; + case BCLK_EMMC: + ret = rk3568_emmc_set_bclk(priv, rate); + break; +#ifndef CONFIG_SPL_BUILD + case ACLK_VOP: + ret = rk3568_aclk_vop_set_clk(priv, rate); + break; + case DCLK_VOP0: + case DCLK_VOP1: + case DCLK_VOP2: + ret = rk3568_dclk_vop_set_clk(priv, clk->id, rate); + break; + case SCLK_GMAC0: + case CLK_MAC0_2TOP: + case CLK_MAC0_REFOUT: + ret = rk3568_gmac_src_set_clk(priv, 0, rate); + break; + case CLK_MAC0_OUT: + ret = rk3568_gmac_out_set_clk(priv, 0, rate); + break; + case SCLK_GMAC0_RX_TX: + ret = rk3568_gmac_tx_rx_set_clk(priv, 0, rate); + break; + case CLK_GMAC0_PTP_REF: + ret = rk3568_gmac_ptp_ref_set_clk(priv, 0, rate); + break; + case SCLK_GMAC1: + case CLK_MAC1_2TOP: + case CLK_MAC1_REFOUT: + ret = rk3568_gmac_src_set_clk(priv, 1, rate); + break; + case CLK_MAC1_OUT: + ret = rk3568_gmac_out_set_clk(priv, 1, rate); + break; + case SCLK_GMAC1_RX_TX: + ret = rk3568_gmac_tx_rx_set_clk(priv, 1, rate); + break; + case CLK_GMAC1_PTP_REF: + ret = rk3568_gmac_ptp_ref_set_clk(priv, 1, rate); + break; + case DCLK_EBC: + ret = rk3568_ebc_set_clk(priv, rate); + break; + case ACLK_RKVDEC_PRE: + case ACLK_RKVDEC: + case CLK_RKVDEC_CORE: + ret = rk3568_rkvdec_set_clk(priv, clk->id, rate); + break; + case TCLK_WDT_NS: + ret = OSC_HZ; + break; + case SCLK_UART1: + case SCLK_UART2: + case SCLK_UART3: + case SCLK_UART4: + case SCLK_UART5: + case SCLK_UART6: + case SCLK_UART7: + case SCLK_UART8: + case SCLK_UART9: + ret = rk3568_uart_set_rate(priv, clk->id, rate); + break; +#endif + case ACLK_SECURE_FLASH: + case ACLK_CRYPTO_NS: + case HCLK_SECURE_FLASH: + case HCLK_CRYPTO_NS: + case CLK_CRYPTO_NS_RNG: + case CLK_CRYPTO_NS_CORE: + case CLK_CRYPTO_NS_PKA: + ret = rk3568_crypto_set_rate(priv, clk->id, rate); + break; + case CPLL_500M: + case CPLL_333M: + case CPLL_250M: + case CPLL_125M: + case CPLL_100M: + case CPLL_62P5M: + case CPLL_50M: + case CPLL_25M: + ret = rk3568_cpll_div_set_rate(priv, clk->id, rate); + break; + default: + return -ENOENT; + } + + return ret; +}; + +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA)) +static int rk3568_gmac0_src_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3568_cru *cru = priv->cru; + + if (parent->id == CLK_MAC0_2TOP) + rk_clrsetreg(&cru->clksel_con[31], + RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_MAC0_TOP << + RMII0_EXTCLK_SEL_SHIFT); + else + rk_clrsetreg(&cru->clksel_con[31], + RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_IO << RMII0_EXTCLK_SEL_SHIFT); + return 0; +} + +static int rk3568_gmac1_src_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3568_cru *cru = priv->cru; + + if (parent->id == CLK_MAC1_2TOP) + rk_clrsetreg(&cru->clksel_con[33], + RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_MAC0_TOP << + RMII0_EXTCLK_SEL_SHIFT); + else + rk_clrsetreg(&cru->clksel_con[33], + RMII0_EXTCLK_SEL_MASK, + RMII0_EXTCLK_SEL_IO << RMII0_EXTCLK_SEL_SHIFT); + return 0; +} + +static int rk3568_gmac0_tx_rx_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3568_cru *cru = priv->cru; + + if (parent->id == SCLK_GMAC0_RGMII_SPEED) + rk_clrsetreg(&cru->clksel_con[31], + RMII0_MODE_MASK, + RMII0_MODE_SEL_RGMII << RMII0_MODE_SHIFT); + else if (parent->id == SCLK_GMAC0_RMII_SPEED) + rk_clrsetreg(&cru->clksel_con[31], + RMII0_MODE_MASK, + RMII0_MODE_SEL_RMII << RMII0_MODE_SHIFT); + else + rk_clrsetreg(&cru->clksel_con[31], + RMII0_MODE_MASK, + RMII0_MODE_SEL_GMII << RMII0_MODE_SHIFT); + + return 0; +} + +static int rk3568_gmac1_tx_rx_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3568_cru *cru = priv->cru; + + if (parent->id == SCLK_GMAC1_RGMII_SPEED) + rk_clrsetreg(&cru->clksel_con[33], + RMII0_MODE_MASK, + RMII0_MODE_SEL_RGMII << RMII0_MODE_SHIFT); + else if (parent->id == SCLK_GMAC1_RMII_SPEED) + rk_clrsetreg(&cru->clksel_con[33], + RMII0_MODE_MASK, + RMII0_MODE_SEL_RMII << RMII0_MODE_SHIFT); + else + rk_clrsetreg(&cru->clksel_con[33], + RMII0_MODE_MASK, + RMII0_MODE_SEL_GMII << RMII0_MODE_SHIFT); + + return 0; +} + +static int rk3568_dclk_vop_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3568_cru *cru = priv->cru; + u32 con_id; + + switch (clk->id) { + case DCLK_VOP0: + con_id = 39; + break; + case DCLK_VOP1: + con_id = 40; + break; + case DCLK_VOP2: + con_id = 41; + break; + default: + return -EINVAL; + } + if (parent->id == PLL_VPLL) { + rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, + DCLK_VOP_SEL_VPLL << DCLK0_VOP_SEL_SHIFT); + } else { + rk_clrsetreg(&cru->clksel_con[con_id], DCLK0_VOP_SEL_MASK, + DCLK_VOP_SEL_HPLL << DCLK0_VOP_SEL_SHIFT); + } + + return 0; +} + +static int rk3568_rkvdec_set_parent(struct clk *clk, struct clk *parent) +{ + struct rk3568_clk_priv *priv = dev_get_priv(clk->dev); + struct rk3568_cru *cru = priv->cru; + u32 con_id, mask, shift; + + switch (clk->id) { + case ACLK_RKVDEC_PRE: + con_id = 47; + mask = ACLK_RKVDEC_SEL_MASK; + shift = ACLK_RKVDEC_SEL_SHIFT; + break; + case CLK_RKVDEC_CORE: + con_id = 49; + mask = CLK_RKVDEC_CORE_SEL_MASK; + shift = CLK_RKVDEC_CORE_SEL_SHIFT; + break; + default: + return -EINVAL; + } + if (parent->id == PLL_CPLL) { + rk_clrsetreg(&cru->clksel_con[con_id], mask, + ACLK_RKVDEC_SEL_CPLL << shift); + } else { + rk_clrsetreg(&cru->clksel_con[con_id], mask, + ACLK_RKVDEC_SEL_GPLL << shift); + } + + return 0; +} + +static int rk3568_clk_set_parent(struct clk *clk, struct clk *parent) +{ + switch (clk->id) { + case SCLK_GMAC0: + return rk3568_gmac0_src_set_parent(clk, parent); + case SCLK_GMAC1: + return rk3568_gmac1_src_set_parent(clk, parent); + case SCLK_GMAC0_RX_TX: + return rk3568_gmac0_tx_rx_set_parent(clk, parent); + case SCLK_GMAC1_RX_TX: + return rk3568_gmac1_tx_rx_set_parent(clk, parent); + case DCLK_VOP0: + case DCLK_VOP1: + case DCLK_VOP2: + return rk3568_dclk_vop_set_parent(clk, parent); + case ACLK_RKVDEC_PRE: + case CLK_RKVDEC_CORE: + return rk3568_rkvdec_set_parent(clk, parent); + default: + return -ENOENT; + } + + return 0; +} +#endif + +static struct clk_ops rk3568_clk_ops = { + .get_rate = rk3568_clk_get_rate, + .set_rate = rk3568_clk_set_rate, +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA)) + .set_parent = rk3568_clk_set_parent, +#endif +}; + +static void rk3568_clk_init(struct rk3568_clk_priv *priv) +{ + int ret; + + priv->sync_kernel = false; + if (!priv->armclk_enter_hz) { + priv->armclk_enter_hz = + rockchip_pll_get_rate(&rk3568_pll_clks[APLL], + priv->cru, APLL); + priv->armclk_init_hz = priv->armclk_enter_hz; + } + + if (priv->armclk_init_hz != APLL_HZ) { + ret = rk3568_armclk_set_clk(priv, APLL_HZ); + if (!ret) + priv->armclk_init_hz = APLL_HZ; + } + if (priv->cpll_hz != CPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3568_pll_clks[CPLL], priv->cru, + CPLL, CPLL_HZ); + if (!ret) + priv->cpll_hz = CPLL_HZ; + } + if (priv->gpll_hz != GPLL_HZ) { + ret = rockchip_pll_set_rate(&rk3568_pll_clks[GPLL], priv->cru, + GPLL, GPLL_HZ); + if (!ret) + priv->gpll_hz = GPLL_HZ; + } + +#ifdef CONFIG_SPL_BUILD + ret = rk3568_bus_set_clk(priv, ACLK_BUS, 150000000); + if (ret < 0) + printf("Fail to set the ACLK_BUS clock.\n"); +#endif + + priv->ppll_hz = rk3568_pmu_pll_get_rate(priv, PPLL); + priv->hpll_hz = rk3568_pmu_pll_get_rate(priv, HPLL); +} + +static int rk3568_clk_probe(struct udevice *dev) +{ + struct rk3568_clk_priv *priv = dev_get_priv(dev); + int ret; + + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + if (IS_ERR(priv->grf)) + return PTR_ERR(priv->grf); + + rk3568_clk_init(priv); + + /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ + ret = clk_set_defaults(dev, 1); + if (ret) + debug("%s clk_set_defaults failed %d\n", __func__, ret); + else + priv->sync_kernel = true; + + return 0; +} + +static int rk3568_clk_ofdata_to_platdata(struct udevice *dev) +{ + struct rk3568_clk_priv *priv = dev_get_priv(dev); + + priv->cru = dev_read_addr_ptr(dev); + + return 0; +} + +static int rk3568_clk_bind(struct udevice *dev) +{ + int ret; + struct udevice *sys_child; + struct sysreset_reg *priv; + + /* The reset driver does not have a device node, so bind it here */ + ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", + &sys_child); + if (ret) { + debug("Warning: No sysreset driver: ret=%d\n", ret); + } else { + priv = malloc(sizeof(struct sysreset_reg)); + priv->glb_srst_fst_value = offsetof(struct rk3568_cru, + glb_srst_fst); + priv->glb_srst_snd_value = offsetof(struct rk3568_cru, + glb_srsr_snd); + } + +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP) + ret = offsetof(struct rk3568_cru, softrst_con[0]); + ret = rockchip_reset_bind(dev, ret, 30); + if (ret) + debug("Warning: software reset driver bind faile\n"); +#endif + + return 0; +} + +static const struct udevice_id rk3568_clk_ids[] = { + { .compatible = "rockchip,rk3568-cru" }, + { } +}; + +U_BOOT_DRIVER(rockchip_rk3568_cru) = { + .name = "rockchip_rk3568_cru", + .id = UCLASS_CLK, + .of_match = rk3568_clk_ids, + .priv_auto = sizeof(struct rk3568_clk_priv), + .of_to_plat = rk3568_clk_ofdata_to_platdata, + .ops = &rk3568_clk_ops, + .bind = rk3568_clk_bind, + .probe = rk3568_clk_probe, +#if CONFIG_IS_ENABLED(OF_PLATDATA) + .plat_auto = sizeof(struct rk3568_clk_plat), +#endif +}; diff --git a/drivers/clk/ti/Kconfig b/drivers/clk/ti/Kconfig index 2dc86d44a9..fbcdefd889 100644 --- a/drivers/clk/ti/Kconfig +++ b/drivers/clk/ti/Kconfig @@ -41,3 +41,27 @@ config CLK_TI_SCI This enables the clock driver support over TI System Control Interface available on some new TI's SoCs. If you wish to use clock resources managed by the TI System Controller, say Y here. Otherwise, say N. + +config CLK_K3_PLL + bool "PLL clock support for K3 SoC family of devices" + depends on CLK && LIB_RATIONAL + help + Enables PLL clock support for K3 SoC family of devices. + +config SPL_CLK_K3_PLL + bool "PLL clock support for K3 SoC family of devices" + depends on CLK && LIB_RATIONAL && SPL + help + Enables PLL clock support for K3 SoC family of devices. + +config CLK_K3 + bool "Clock support for K3 SoC family of devices" + depends on CLK + help + Enables the clock translation layer from DT to device clocks. + +config SPL_CLK_K3 + bool "Clock support for K3 SoC family of devices" + depends on CLK && SPL + help + Enables the clock translation layer from DT to device clocks. diff --git a/drivers/clk/ti/Makefile b/drivers/clk/ti/Makefile index 9f56b47736..07aa9a53e0 100644 --- a/drivers/clk/ti/Makefile +++ b/drivers/clk/ti/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_CLK_TI_DIVIDER) += clk-divider.o obj-$(CONFIG_CLK_TI_GATE) += clk-gate.o obj-$(CONFIG_CLK_TI_MUX) += clk-mux.o obj-$(CONFIG_CLK_TI_SCI) += clk-sci.o +obj-$(CONFIG_$(SPL_TPL_)CLK_K3_PLL) += clk-k3-pll.o +obj-$(CONFIG_$(SPL_TPL_)CLK_K3) += clk-k3.o diff --git a/drivers/clk/ti/clk-k3-pll.c b/drivers/clk/ti/clk-k3-pll.c new file mode 100644 index 0000000000..bf2407a020 --- /dev/null +++ b/drivers/clk/ti/clk-k3-pll.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments K3 SoC PLL clock driver + * + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * Tero Kristo <t-kristo@ti.com> + */ + +#include <common.h> +#include <asm/io.h> +#include <dm.h> +#include <div64.h> +#include <errno.h> +#include <clk-uclass.h> +#include <linux/clk-provider.h> +#include "k3-clk.h" +#include <linux/rational.h> + +/* 16FFT register offsets */ +#define PLL_16FFT_CFG 0x08 +#define PLL_KICK0 0x10 +#define PLL_KICK1 0x14 +#define PLL_16FFT_CTRL 0x20 +#define PLL_16FFT_STAT 0x24 +#define PLL_16FFT_FREQ_CTRL0 0x30 +#define PLL_16FFT_FREQ_CTRL1 0x34 +#define PLL_16FFT_DIV_CTRL 0x38 + +/* CTRL register bits */ +#define PLL_16FFT_CTRL_BYPASS_EN BIT(31) +#define PLL_16FFT_CTRL_PLL_EN BIT(15) +#define PLL_16FFT_CTRL_DSM_EN BIT(1) + +/* STAT register bits */ +#define PLL_16FFT_STAT_LOCK BIT(0) + +/* FREQ_CTRL0 bits */ +#define PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK 0xfff + +/* DIV CTRL register bits */ +#define PLL_16FFT_DIV_CTRL_REF_DIV_MASK 0x3f + +#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS 24 +#define PLL_16FFT_HSDIV_CTRL_CLKOUT_EN BIT(15) + +/* KICK register magic values */ +#define PLL_KICK0_VALUE 0x68ef3490 +#define PLL_KICK1_VALUE 0xd172bc5a + +/** + * struct ti_pll_clk - TI PLL clock data info structure + * @clk: core clock structure + * @reg: memory address of the PLL controller + */ +struct ti_pll_clk { + struct clk clk; + void __iomem *reg; +}; + +#define to_clk_pll(_clk) container_of(_clk, struct ti_pll_clk, clk) + +static int ti_pll_wait_for_lock(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u32 stat; + int i; + + for (i = 0; i < 100000; i++) { + stat = readl(pll->reg + PLL_16FFT_STAT); + if (stat & PLL_16FFT_STAT_LOCK) + return 0; + } + + printf("%s: pll (%s) failed to lock\n", __func__, + clk->dev->name); + + return -EBUSY; +} + +static ulong ti_pll_clk_get_rate(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u64 current_freq; + u64 parent_freq = clk_get_parent_rate(clk); + u32 pllm; + u32 plld; + u32 pllfm; + u32 ctrl; + + /* Check if we are in bypass */ + ctrl = readl(pll->reg + PLL_16FFT_CTRL); + if (ctrl & PLL_16FFT_CTRL_BYPASS_EN) + return parent_freq; + + pllm = readl(pll->reg + PLL_16FFT_FREQ_CTRL0); + pllfm = readl(pll->reg + PLL_16FFT_FREQ_CTRL1); + + plld = readl(pll->reg + PLL_16FFT_DIV_CTRL) & + PLL_16FFT_DIV_CTRL_REF_DIV_MASK; + + current_freq = parent_freq * pllm / plld; + + if (pllfm) { + u64 tmp; + + tmp = parent_freq * pllfm; + do_div(tmp, plld); + tmp >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS; + current_freq += tmp; + } + + return current_freq; +} + +static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u64 current_freq; + u64 parent_freq = clk_get_parent_rate(clk); + int ret; + u32 ctrl; + unsigned long pllm; + u32 pllfm = 0; + unsigned long plld; + u32 rem; + int shift; + + debug("%s(clk=%p, rate=%u)\n", __func__, clk, (u32)rate); + + if (ti_pll_clk_get_rate(clk) == rate) + return rate; + + if (rate != parent_freq) + /* + * Attempt with higher max multiplier value first to give + * some space for fractional divider to kick in. + */ + for (shift = 8; shift >= 0; shift -= 8) { + rational_best_approximation(rate, parent_freq, + ((PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK + 1) << shift) - 1, + PLL_16FFT_DIV_CTRL_REF_DIV_MASK, &pllm, &plld); + if (pllm / plld <= PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK) + break; + } + + /* Put PLL to bypass mode */ + ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl |= PLL_16FFT_CTRL_BYPASS_EN; + writel(ctrl, pll->reg + PLL_16FFT_CTRL); + + if (rate == parent_freq) { + debug("%s: put %s to bypass\n", __func__, clk->dev->name); + return rate; + } + + debug("%s: pre-frac-calc: rate=%u, parent_freq=%u, plld=%u, pllm=%u\n", + __func__, (u32)rate, (u32)parent_freq, (u32)plld, (u32)pllm); + + /* Check if we need fractional config */ + if (plld > 1) { + pllfm = pllm % plld; + pllfm <<= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS; + rem = pllfm % plld; + pllfm /= plld; + if (rem) + pllfm++; + pllm /= plld; + plld = 1; + } + + if (pllfm) + ctrl |= PLL_16FFT_CTRL_DSM_EN; + else + ctrl &= ~PLL_16FFT_CTRL_DSM_EN; + + writel(pllm, pll->reg + PLL_16FFT_FREQ_CTRL0); + writel(pllfm, pll->reg + PLL_16FFT_FREQ_CTRL1); + writel(plld, pll->reg + PLL_16FFT_DIV_CTRL); + + ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; + ctrl |= PLL_16FFT_CTRL_PLL_EN; + writel(ctrl, pll->reg + PLL_16FFT_CTRL); + + ret = ti_pll_wait_for_lock(clk); + if (ret) + return ret; + + debug("%s: pllm=%u, plld=%u, pllfm=%u, parent_freq=%u\n", + __func__, (u32)pllm, (u32)plld, (u32)pllfm, (u32)parent_freq); + + current_freq = parent_freq * pllm / plld; + + if (pllfm) { + u64 tmp; + + tmp = parent_freq * pllfm; + do_div(tmp, plld); + tmp >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS; + current_freq += tmp; + } + + return current_freq; +} + +static int ti_pll_clk_enable(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u32 ctrl; + + ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN; + ctrl |= PLL_16FFT_CTRL_PLL_EN; + writel(ctrl, pll->reg + PLL_16FFT_CTRL); + + return ti_pll_wait_for_lock(clk); +} + +static int ti_pll_clk_disable(struct clk *clk) +{ + struct ti_pll_clk *pll = to_clk_pll(clk); + u32 ctrl; + + ctrl = readl(pll->reg + PLL_16FFT_CTRL); + ctrl |= PLL_16FFT_CTRL_BYPASS_EN; + writel(ctrl, pll->reg + PLL_16FFT_CTRL); + + return 0; +} + +static const struct clk_ops ti_pll_clk_ops = { + .get_rate = ti_pll_clk_get_rate, + .set_rate = ti_pll_clk_set_rate, + .enable = ti_pll_clk_enable, + .disable = ti_pll_clk_disable, +}; + +struct clk *clk_register_ti_pll(const char *name, const char *parent_name, + void __iomem *reg) +{ + struct ti_pll_clk *pll; + int ret; + int i; + u32 cfg, ctrl, hsdiv_presence_bit, hsdiv_ctrl_offs; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->reg = reg; + + ret = clk_register(&pll->clk, "ti-pll-clk", name, parent_name); + if (ret) { + printf("%s: failed to register: %d\n", __func__, ret); + kfree(pll); + return ERR_PTR(ret); + } + + /* Unlock the PLL registers */ + writel(PLL_KICK0_VALUE, pll->reg + PLL_KICK0); + writel(PLL_KICK1_VALUE, pll->reg + PLL_KICK1); + + /* Enable all HSDIV outputs */ + cfg = readl(pll->reg + PLL_16FFT_CFG); + for (i = 0; i < 16; i++) { + hsdiv_presence_bit = BIT(16 + i); + hsdiv_ctrl_offs = 0x80 + (i * 4); + /* Enable HSDIV output if present */ + if ((hsdiv_presence_bit & cfg) != 0UL) { + ctrl = readl(pll->reg + hsdiv_ctrl_offs); + ctrl |= PLL_16FFT_HSDIV_CTRL_CLKOUT_EN; + writel(ctrl, pll->reg + hsdiv_ctrl_offs); + } + } + + return &pll->clk; +} + +U_BOOT_DRIVER(ti_pll_clk) = { + .name = "ti-pll-clk", + .id = UCLASS_CLK, + .ops = &ti_pll_clk_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/ti/clk-k3.c b/drivers/clk/ti/clk-k3.c new file mode 100644 index 0000000000..e921894e7a --- /dev/null +++ b/drivers/clk/ti/clk-k3.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments K3 clock driver + * + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * Tero Kristo <t-kristo@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <soc.h> +#include <clk-uclass.h> +#include "k3-clk.h" + +#define PLL_MIN_FREQ 800000000 +#define PLL_MAX_FREQ 3200000000UL +#define PLL_MAX_DIV 127 + +/** + * struct clk_map - mapping from dev/clk id tuples towards physical clocks + * @dev_id: device ID for the clock + * @clk_id: clock ID for the clock + * @clk: pointer to the registered clock entry for the mapping + */ +struct clk_map { + u16 dev_id; + u32 clk_id; + struct clk *clk; +}; + +/** + * struct ti_clk_data - clock controller information structure + * @map: mapping from dev/clk id tuples to physical clock entries + * @size: number of entries in the map + */ +struct ti_clk_data { + struct clk_map *map; + int size; +}; + +static ulong osc_freq; + +static void clk_add_map(struct ti_clk_data *data, struct clk *clk, + u32 dev_id, u32 clk_id) +{ + struct clk_map *map; + + debug("%s: added clk=%p, data=%p, dev=%d, clk=%d\n", __func__, + clk, data, dev_id, clk_id); + if (!clk) + return; + + map = data->map + data->size++; + + map->dev_id = dev_id; + map->clk_id = clk_id; + map->clk = clk; +} + +static const struct soc_attr ti_k3_soc_clk_data[] = { +#if IS_ENABLED(CONFIG_SOC_K3_J721E) + { + .family = "J721E", + .data = &j721e_clk_platdata, + }, + { + .family = "J7200", + .data = &j7200_clk_platdata, + }, +#endif + { /* sentinel */ } +}; + +static int ti_clk_probe(struct udevice *dev) +{ + struct ti_clk_data *data = dev_get_priv(dev); + struct clk *clk; + const char *name; + const struct clk_data *ti_clk_data; + int i, j; + const struct soc_attr *soc_match_data; + const struct ti_k3_clk_platdata *pdata; + + debug("%s(dev=%p)\n", __func__, dev); + + soc_match_data = soc_device_match(ti_k3_soc_clk_data); + if (!soc_match_data) + return -ENODEV; + + pdata = (const struct ti_k3_clk_platdata *)soc_match_data->data; + + data->map = kcalloc(pdata->soc_dev_clk_data_cnt, sizeof(*data->map), + GFP_KERNEL); + data->size = 0; + + for (i = 0; i < pdata->clk_list_cnt; i++) { + ti_clk_data = &pdata->clk_list[i]; + + switch (ti_clk_data->type) { + case CLK_TYPE_FIXED_RATE: + name = ti_clk_data->clk.fixed_rate.name; + clk = clk_register_fixed_rate(NULL, + name, + ti_clk_data->clk.fixed_rate.rate); + break; + case CLK_TYPE_DIV: + name = ti_clk_data->clk.div.name; + clk = clk_register_divider(NULL, name, + ti_clk_data->clk.div.parent, + ti_clk_data->clk.div.flags, + map_physmem(ti_clk_data->clk.div.reg, 0, MAP_NOCACHE), + ti_clk_data->clk.div.shift, + ti_clk_data->clk.div.width, + 0); + break; + case CLK_TYPE_MUX: + name = ti_clk_data->clk.mux.name; + clk = clk_register_mux(NULL, name, + ti_clk_data->clk.mux.parents, + ti_clk_data->clk.mux.num_parents, + ti_clk_data->clk.mux.flags, + map_physmem(ti_clk_data->clk.mux.reg, 0, MAP_NOCACHE), + ti_clk_data->clk.mux.shift, + ti_clk_data->clk.mux.width, + 0); + break; + case CLK_TYPE_PLL: + name = ti_clk_data->clk.pll.name; + clk = clk_register_ti_pll(name, + ti_clk_data->clk.pll.parent, + map_physmem(ti_clk_data->clk.pll.reg, 0, MAP_NOCACHE)); + + if (!osc_freq) + osc_freq = clk_get_rate(clk_get_parent(clk)); + break; + default: + name = NULL; + clk = NULL; + printf("WARNING: %s has encountered unknown clk type %d\n", + __func__, ti_clk_data->type); + } + + if (clk && ti_clk_data->default_freq) + clk_set_rate(clk, ti_clk_data->default_freq); + + if (clk && name) { + for (j = 0; j < pdata->soc_dev_clk_data_cnt; j++) { + if (!strcmp(name, pdata->soc_dev_clk_data[j].clk_name)) { + clk_add_map(data, clk, pdata->soc_dev_clk_data[j].dev_id, + pdata->soc_dev_clk_data[j].clk_id); + } + } + } + } + + return 0; +} + +static int _clk_cmp(u32 dev_id, u32 clk_id, const struct clk_map *map) +{ + if (map->dev_id == dev_id && map->clk_id == clk_id) + return 0; + if (map->dev_id > dev_id || + (map->dev_id == dev_id && map->clk_id > clk_id)) + return -1; + return 1; +} + +static int bsearch(u32 dev_id, u32 clk_id, struct clk_map *map, int num) +{ + int result; + int idx; + + for (idx = 0; idx < num; idx++) { + result = _clk_cmp(dev_id, clk_id, &map[idx]); + + if (result == 0) + return idx; + } + + return -ENOENT; +} + +static int ti_clk_of_xlate(struct clk *clk, + struct ofnode_phandle_args *args) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + int idx; + + debug("%s(clk=%p, args_count=%d [0]=%d [1]=%d)\n", __func__, clk, + args->args_count, args->args[0], args->args[1]); + + if (args->args_count != 2) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (!data->size) + return -EPROBE_DEFER; + + idx = bsearch(args->args[0], args->args[1], data->map, data->size); + if (idx < 0) + return idx; + + clk->id = idx; + + return 0; +} + +static ulong ti_clk_get_rate(struct clk *clk) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + + return clk_get_rate(clkp); +} + +static ulong ti_clk_set_rate(struct clk *clk, ulong rate) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + int div = 1; + ulong child_rate; + const struct clk_ops *ops; + ulong new_rate, rem; + ulong diff, new_diff; + + /* + * We must propagate rate change to parent if current clock type + * does not allow setting it. + */ + while (clkp) { + ops = clkp->dev->driver->ops; + if (ops->set_rate) + break; + + /* + * Store child rate so we can calculate the clock rate + * that must be passed to parent + */ + child_rate = clk_get_rate(clkp); + clkp = clk_get_parent(clkp); + if (clkp) { + debug("%s: propagating rate change to parent %s, rate=%u.\n", + __func__, clkp->dev->name, (u32)rate / div); + div *= clk_get_rate(clkp) / child_rate; + } + } + + if (!clkp) + return -ENOSYS; + + child_rate = clk_get_rate(clkp); + + new_rate = clk_set_rate(clkp, rate / div); + + diff = abs(new_rate - rate / div); + + debug("%s: clk=%s, div=%d, rate=%u, new_rate=%u, diff=%u\n", __func__, + clkp->dev->name, div, (u32)rate, (u32)new_rate, (u32)diff); + + /* + * If the new rate differs by 50% of the target, + * modify parent. This handles typical cases where we have a hsdiv + * following directly a PLL + */ + + if (diff > rate / div / 2) { + ulong pll_tgt; + int pll_div = 0; + + clk = clkp; + + debug("%s: propagating rate change to parent, rate=%u.\n", + __func__, (u32)rate / div); + + clkp = clk_get_parent(clkp); + + if (rate > osc_freq) { + if (rate > PLL_MAX_FREQ / 2 && rate < PLL_MAX_FREQ) { + pll_tgt = rate; + pll_div = 1; + } else { + for (pll_div = 2; pll_div < PLL_MAX_DIV; pll_div++) { + pll_tgt = rate / div * pll_div; + if (pll_tgt >= PLL_MIN_FREQ && pll_tgt <= PLL_MAX_FREQ) + break; + } + } + } else { + pll_tgt = osc_freq; + pll_div = rate / div / osc_freq; + } + + debug("%s: pll_tgt=%u, rate=%u, div=%u\n", __func__, + (u32)pll_tgt, (u32)rate, pll_div); + + clk_set_rate(clkp, pll_tgt); + + return clk_set_rate(clk, rate / div) * div; + } + + /* + * If the new rate differs by at least 5% of the target, + * we must check for rounding error in a divider, so try + * set rate with rate + (parent_freq % rate). + */ + + if (diff > rate / div / 20) { + u64 parent_freq = clk_get_parent_rate(clkp); + + rem = parent_freq % rate; + new_rate = clk_set_rate(clkp, (rate / div) + rem); + new_diff = abs(new_rate - rate / div); + + if (new_diff > diff) { + new_rate = clk_set_rate(clkp, rate / div); + } else { + debug("%s: Using better rate %lu that gives diff %lu\n", + __func__, new_rate, new_diff); + } + } + + return new_rate; +} + +static int ti_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + struct clk *parentp = data->map[parent->id].clk; + + return clk_set_parent(clkp, parentp); +} + +static int ti_clk_enable(struct clk *clk) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + + return clk_enable(clkp); +} + +static int ti_clk_disable(struct clk *clk) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + + return clk_disable(clkp); +} + +static const struct udevice_id ti_clk_of_match[] = { + { .compatible = "ti,k2g-sci-clk" }, + { /* sentinel */ }, +}; + +static const struct clk_ops ti_clk_ops = { + .of_xlate = ti_clk_of_xlate, + .set_rate = ti_clk_set_rate, + .get_rate = ti_clk_get_rate, + .enable = ti_clk_enable, + .disable = ti_clk_disable, + .set_parent = ti_clk_set_parent, +}; + +U_BOOT_DRIVER(ti_clk) = { + .name = "ti-clk", + .id = UCLASS_CLK, + .of_match = ti_clk_of_match, + .probe = ti_clk_probe, + .priv_auto = sizeof(struct ti_clk_data), + .ops = &ti_clk_ops, +}; diff --git a/drivers/clk/ti/clk-sci.c b/drivers/clk/ti/clk-sci.c index 6f0fdaa111..acb9eadf03 100644 --- a/drivers/clk/ti/clk-sci.c +++ b/drivers/clk/ti/clk-sci.c @@ -111,10 +111,12 @@ static ulong ti_sci_clk_set_rate(struct clk *clk, ulong rate) #endif ret = cops->set_freq(sci, clk->id, clk->data, 0, rate, ULONG_MAX); - if (ret) + if (ret) { dev_err(clk->dev, "%s: set_freq failed (%d)\n", __func__, ret); + return ret; + } - return ret; + return rate; } static int ti_sci_clk_set_parent(struct clk *clk, struct clk *parent) |