diff options
Diffstat (limited to 'drivers/clk/sophgo')
-rw-r--r-- | drivers/clk/sophgo/Kconfig | 58 | ||||
-rw-r--r-- | drivers/clk/sophgo/Makefile | 13 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv1800.c | 1540 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv1800.h | 123 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv18xx-common.c | 66 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv18xx-common.h | 81 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv18xx-ip.c | 887 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv18xx-ip.h | 261 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv18xx-pll.c | 419 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-cv18xx-pll.h | 118 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-sg2042-clkgen.c | 1152 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-sg2042-pll.c | 567 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-sg2042-rpgate.c | 291 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-sg2042.h | 18 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-sg2044-pll.c | 628 | ||||
-rw-r--r-- | drivers/clk/sophgo/clk-sg2044.c | 1812 |
16 files changed, 8034 insertions, 0 deletions
diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig new file mode 100644 index 000000000000..e14e802f28bf --- /dev/null +++ b/drivers/clk/sophgo/Kconfig @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0 +# common clock support for SOPHGO SoC family. + +config CLK_SOPHGO_CV1800 + tristate "Support for the Sophgo CV1800 series SoCs clock controller" + depends on ARCH_SOPHGO || COMPILE_TEST + help + This driver supports clock controller of Sophgo CV18XX series SoC. + The driver require a 25MHz Oscillator to function generate clock. + It includes PLLs, common clock function and some vendor clock for + IPs of CV18XX series SoC + +config CLK_SOPHGO_SG2042_PLL + tristate "Sophgo SG2042 PLL clock support" + depends on ARCH_SOPHGO || COMPILE_TEST + help + This driver supports the PLL clock controller on the + Sophgo SG2042 SoC. This clock IP uses three oscillators with + frequency of 25 MHz as input, which are used for Main/Fixed + PLL, DDR PLL 0 and DDR PLL 1 respectively. + +config CLK_SOPHGO_SG2042_CLKGEN + tristate "Sophgo SG2042 Clock Generator support" + depends on CLK_SOPHGO_SG2042_PLL + help + This driver supports the Clock Generator on the + Sophgo SG2042 SoC. This clock IP depends on SG2042 PLL clock + because it uses PLL clocks as input. + This driver provides clock function such as DIV/Mux/Gate. + +config CLK_SOPHGO_SG2042_RPGATE + tristate "Sophgo SG2042 RP subsystem clock controller support" + depends on CLK_SOPHGO_SG2042_CLKGEN + help + This driver supports the RP((Riscv Processors)) subsystem clock + controller on the Sophgo SG2042 SoC. + This clock IP depends on SG2042 Clock Generator because it uses + clock from Clock Generator IP as input. + This driver provides Gate function for RP. + +config CLK_SOPHGO_SG2044 + tristate "Sophgo SG2044 clock controller support" + depends on ARCH_SOPHGO || COMPILE_TEST + help + This driver supports the clock controller on the Sophgo SG2044 + SoC. This controller requires mulitple PLL clock as input. + This clock control provides PLL clocks and common clock function + for various IPs on the SoC. + +config CLK_SOPHGO_SG2044_PLL + tristate "Sophgo SG2044 PLL clock controller support" + depends on ARCH_SOPHGO || COMPILE_TEST + select MFD_SYSCON + select REGMAP_MMIO + help + This driver supports the PLL clock controller on the Sophgo + SG2044 SoC. This controller requires 25M oscillator as input. + This clock control provides PLL clocks on the SoC. diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile new file mode 100644 index 000000000000..26b2fd121582 --- /dev/null +++ b/drivers/clk/sophgo/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_CLK_SOPHGO_CV1800) += clk-sophgo-cv1800.o + +clk-sophgo-cv1800-y += clk-cv1800.o +clk-sophgo-cv1800-y += clk-cv18xx-common.o +clk-sophgo-cv1800-y += clk-cv18xx-ip.o +clk-sophgo-cv1800-y += clk-cv18xx-pll.o + +obj-$(CONFIG_CLK_SOPHGO_SG2042_CLKGEN) += clk-sg2042-clkgen.o +obj-$(CONFIG_CLK_SOPHGO_SG2042_PLL) += clk-sg2042-pll.o +obj-$(CONFIG_CLK_SOPHGO_SG2042_RPGATE) += clk-sg2042-rpgate.o +obj-$(CONFIG_CLK_SOPHGO_SG2044) += clk-sg2044.o +obj-$(CONFIG_CLK_SOPHGO_SG2044_PLL) += clk-sg2044-pll.o diff --git a/drivers/clk/sophgo/clk-cv1800.c b/drivers/clk/sophgo/clk-cv1800.c new file mode 100644 index 000000000000..a4116ac1adcb --- /dev/null +++ b/drivers/clk/sophgo/clk-cv1800.c @@ -0,0 +1,1540 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#include <linux/module.h> +#include <linux/clk-provider.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/spinlock.h> + +#include "clk-cv1800.h" + +#include "clk-cv18xx-common.h" +#include "clk-cv18xx-ip.h" +#include "clk-cv18xx-pll.h" + +struct cv1800_clk_ctrl; + +struct cv1800_clk_desc { + struct clk_hw_onecell_data *clks_data; + + int (*pre_init)(struct device *dev, void __iomem *base, + struct cv1800_clk_ctrl *ctrl, + const struct cv1800_clk_desc *desc); +}; + +struct cv1800_clk_ctrl { + const struct cv1800_clk_desc *desc; + spinlock_t lock; +}; + +#define CV1800_DIV_FLAG \ + (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST) +static const struct clk_parent_data osc_parents[] = { + { .index = 0 }, +}; + +static const struct cv1800_clk_pll_limit pll_limits[] = { + { + .pre_div = _CV1800_PLL_LIMIT(1, 127), + .div = _CV1800_PLL_LIMIT(6, 127), + .post_div = _CV1800_PLL_LIMIT(1, 127), + .ictrl = _CV1800_PLL_LIMIT(0, 7), + .mode = _CV1800_PLL_LIMIT(0, 3), + }, + { + .pre_div = _CV1800_PLL_LIMIT(1, 127), + .div = _CV1800_PLL_LIMIT(6, 127), + .post_div = _CV1800_PLL_LIMIT(1, 127), + .ictrl = _CV1800_PLL_LIMIT(0, 7), + .mode = _CV1800_PLL_LIMIT(0, 3), + }, +}; + +static CV1800_INTEGRAL_PLL(clk_fpll, osc_parents, + REG_FPLL_CSR, + REG_PLL_G6_CTRL, 8, + REG_PLL_G6_STATUS, 2, + pll_limits, + CLK_IS_CRITICAL); + +static CV1800_INTEGRAL_PLL(clk_mipimpll, osc_parents, + REG_MIPIMPLL_CSR, + REG_PLL_G2_CTRL, 0, + REG_PLL_G2_STATUS, 0, + pll_limits, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_mipimpll_parents[] = { + { .hw = &clk_mipimpll.common.hw }, +}; +static const struct clk_parent_data clk_bypass_mipimpll_parents[] = { + { .index = 0 }, + { .hw = &clk_mipimpll.common.hw }, +}; +static const struct clk_parent_data clk_bypass_fpll_parents[] = { + { .index = 0 }, + { .hw = &clk_fpll.common.hw }, +}; + +static struct cv1800_clk_pll_synthesizer clk_mpll_synthesizer = { + .en = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 2), + .clk_half = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 0), + .ctrl = REG_MPLL_SSC_SYN_CTRL, + .set = REG_MPLL_SSC_SYN_SET, +}; +static CV1800_FACTIONAL_PLL(clk_mpll, clk_bypass_mipimpll_parents, + REG_MPLL_CSR, + REG_PLL_G6_CTRL, 0, + REG_PLL_G6_STATUS, 0, + pll_limits, + &clk_mpll_synthesizer, + CLK_IS_CRITICAL); + +static struct cv1800_clk_pll_synthesizer clk_tpll_synthesizer = { + .en = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 3), + .clk_half = CV1800_CLK_BIT(REG_PLL_G6_SSC_SYN_CTRL, 0), + .ctrl = REG_TPLL_SSC_SYN_CTRL, + .set = REG_TPLL_SSC_SYN_SET, +}; +static CV1800_FACTIONAL_PLL(clk_tpll, clk_bypass_mipimpll_parents, + REG_TPLL_CSR, + REG_PLL_G6_CTRL, 4, + REG_PLL_G6_STATUS, 1, + pll_limits, + &clk_tpll_synthesizer, + CLK_IS_CRITICAL); + +static struct cv1800_clk_pll_synthesizer clk_a0pll_synthesizer = { + .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 2), + .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0), + .ctrl = REG_A0PLL_SSC_SYN_CTRL, + .set = REG_A0PLL_SSC_SYN_SET, +}; +static CV1800_FACTIONAL_PLL(clk_a0pll, clk_bypass_mipimpll_parents, + REG_A0PLL_CSR, + REG_PLL_G2_CTRL, 4, + REG_PLL_G2_STATUS, 1, + pll_limits, + &clk_a0pll_synthesizer, + CLK_IS_CRITICAL); + +static struct cv1800_clk_pll_synthesizer clk_disppll_synthesizer = { + .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 3), + .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0), + .ctrl = REG_DISPPLL_SSC_SYN_CTRL, + .set = REG_DISPPLL_SSC_SYN_SET, +}; +static CV1800_FACTIONAL_PLL(clk_disppll, clk_bypass_mipimpll_parents, + REG_DISPPLL_CSR, + REG_PLL_G2_CTRL, 8, + REG_PLL_G2_STATUS, 2, + pll_limits, + &clk_disppll_synthesizer, + CLK_IS_CRITICAL); + +static struct cv1800_clk_pll_synthesizer clk_cam0pll_synthesizer = { + .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 4), + .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0), + .ctrl = REG_CAM0PLL_SSC_SYN_CTRL, + .set = REG_CAM0PLL_SSC_SYN_SET, +}; +static CV1800_FACTIONAL_PLL(clk_cam0pll, clk_bypass_mipimpll_parents, + REG_CAM0PLL_CSR, + REG_PLL_G2_CTRL, 12, + REG_PLL_G2_STATUS, 3, + pll_limits, + &clk_cam0pll_synthesizer, + CLK_IGNORE_UNUSED); + +static struct cv1800_clk_pll_synthesizer clk_cam1pll_synthesizer = { + .en = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 5), + .clk_half = CV1800_CLK_BIT(REG_PLL_G2_SSC_SYN_CTRL, 0), + .ctrl = REG_CAM1PLL_SSC_SYN_CTRL, + .set = REG_CAM1PLL_SSC_SYN_SET, +}; +static CV1800_FACTIONAL_PLL(clk_cam1pll, clk_bypass_mipimpll_parents, + REG_CAM1PLL_CSR, + REG_PLL_G2_CTRL, 16, + REG_PLL_G2_STATUS, 4, + pll_limits, + &clk_cam1pll_synthesizer, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_cam0pll_parents[] = { + { .hw = &clk_cam0pll.common.hw }, +}; + +/* G2D */ +static CV1800_FIXED_DIV(clk_cam0pll_d2, clk_cam0pll_parents, + REG_CAM0PLL_CLK_CSR, 1, + 2, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED); +static CV1800_FIXED_DIV(clk_cam0pll_d3, clk_cam0pll_parents, + REG_CAM0PLL_CLK_CSR, 2, + 3, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED); +static CV1800_FIXED_DIV(clk_mipimpll_d3, clk_mipimpll_parents, + REG_MIPIMPLL_CLK_CSR, 2, + 3, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED); + +/* TPU */ +static const struct clk_parent_data clk_tpu_parents[] = { + { .index = 0 }, + { .hw = &clk_tpll.common.hw }, + { .hw = &clk_a0pll.common.hw }, + { .hw = &clk_mipimpll.common.hw }, + { .hw = &clk_fpll.common.hw }, +}; + +static CV1800_BYPASS_MUX(clk_tpu, clk_tpu_parents, + REG_CLK_EN_0, 4, + REG_DIV_CLK_TPU, 16, 4, 3, CV1800_DIV_FLAG, + REG_DIV_CLK_TPU, 8, 2, + REG_CLK_BYP_0, 3, + 0); +static CV1800_GATE(clk_tpu_fab, clk_mipimpll_parents, + REG_CLK_EN_0, 5, + 0); + +/* FABRIC_AXI6 */ +static CV1800_BYPASS_DIV(clk_axi6, clk_bypass_fpll_parents, + REG_CLK_EN_2, 2, + REG_DIV_CLK_AXI6, 16, 4, 15, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 20, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_axi6_bus_parents[] = { + { .hw = &clk_axi6.div.common.hw }, +}; +static const struct clk_parent_data clk_bypass_axi6_bus_parents[] = { + { .index = 0 }, + { .hw = &clk_axi6.div.common.hw }, +}; + +/* FABRIC_AXI4 */ +static const struct clk_parent_data clk_axi4_parents[] = { + { .index = 0 }, + { .hw = &clk_fpll.common.hw }, + { .hw = &clk_disppll.common.hw }, +}; + +static CV1800_BYPASS_MUX(clk_axi4, clk_axi4_parents, + REG_CLK_EN_2, 1, + REG_DIV_CLK_AXI4, 16, 4, 5, CV1800_DIV_FLAG, + REG_DIV_CLK_AXI4, 8, 2, + REG_CLK_BYP_0, 19, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_axi4_bus_parents[] = { + { .hw = &clk_axi4.mux.common.hw }, +}; + +/* XTAL_MISC */ +static CV1800_GATE(clk_xtal_misc, osc_parents, + REG_CLK_EN_0, 14, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_timer_parents[] = { + { .hw = &clk_xtal_misc.common.hw }, +}; + +/* TOP */ +static const struct clk_parent_data clk_cam0_200_parents[] = { + { .index = 0 }, + { .index = 0 }, + { .hw = &clk_disppll.common.hw }, +}; + +static CV1800_BYPASS_MUX(clk_cam0_200, clk_cam0_200_parents, + REG_CLK_EN_1, 13, + REG_DIV_CLK_CAM0_200, 16, 4, 1, CV1800_DIV_FLAG, + REG_DIV_CLK_CAM0_200, 8, 2, + REG_CLK_BYP_0, 16, + CLK_IS_CRITICAL); +static CV1800_DIV(clk_1m, osc_parents, + REG_CLK_EN_3, 5, + REG_DIV_CLK_1M, 16, 6, 25, CV1800_DIV_FLAG, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_pm, clk_axi6_bus_parents, + REG_CLK_EN_3, 8, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer0, clk_timer_parents, + REG_CLK_EN_3, 9, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer1, clk_timer_parents, + REG_CLK_EN_3, 10, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer2, clk_timer_parents, + REG_CLK_EN_3, 11, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer3, clk_timer_parents, + REG_CLK_EN_3, 12, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer4, clk_timer_parents, + REG_CLK_EN_3, 13, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer5, clk_timer_parents, + REG_CLK_EN_3, 14, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer6, clk_timer_parents, + REG_CLK_EN_3, 15, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_timer7, clk_timer_parents, + REG_CLK_EN_3, 16, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_parents_1m[] = { + { .hw = &clk_1m.common.hw }, +}; +static const struct clk_parent_data clk_uart_parents[] = { + { .hw = &clk_cam0_200.mux.common.hw }, +}; + +/* AHB ROM */ +static CV1800_GATE(clk_ahb_rom, clk_axi4_bus_parents, + REG_CLK_EN_0, 6, + 0); + +/* RTC */ +static CV1800_GATE(clk_rtc_25m, osc_parents, + REG_CLK_EN_0, 8, + CLK_IS_CRITICAL); +static CV1800_BYPASS_DIV(clk_src_rtc_sys_0, clk_bypass_fpll_parents, + REG_CLK_EN_4, 6, + REG_DIV_CLK_RTCSYS_SRC_0, 16, 4, 5, CV1800_DIV_FLAG, + REG_CLK_BYP_1, 5, + CLK_IS_CRITICAL); + +/* TEMPSEN */ +static CV1800_GATE(clk_tempsen, osc_parents, + REG_CLK_EN_0, 9, + 0); + +/* SARADC */ +static CV1800_GATE(clk_saradc, osc_parents, + REG_CLK_EN_0, 10, + 0); + +/* EFUSE */ +static CV1800_GATE(clk_efuse, osc_parents, + REG_CLK_EN_0, 11, + 0); +static CV1800_GATE(clk_apb_efuse, osc_parents, + REG_CLK_EN_0, 12, + 0); + +/* WDT */ +static CV1800_GATE(clk_apb_wdt, osc_parents, + REG_CLK_EN_1, 7, + CLK_IS_CRITICAL); + +/* WGN */ +static CV1800_GATE(clk_wgn, osc_parents, + REG_CLK_EN_3, 22, + 0); +static CV1800_GATE(clk_wgn0, osc_parents, + REG_CLK_EN_3, 23, + 0); +static CV1800_GATE(clk_wgn1, osc_parents, + REG_CLK_EN_3, 24, + 0); +static CV1800_GATE(clk_wgn2, osc_parents, + REG_CLK_EN_3, 25, + 0); + +/* KEYSCAN */ +static CV1800_GATE(clk_keyscan, osc_parents, + REG_CLK_EN_3, 26, + 0); + +/* EMMC */ +static CV1800_GATE(clk_axi4_emmc, clk_axi4_bus_parents, + REG_CLK_EN_0, 15, + 0); +static CV1800_BYPASS_MUX(clk_emmc, clk_axi4_parents, + REG_CLK_EN_0, 16, + REG_DIV_CLK_EMMC, 16, 5, 15, CV1800_DIV_FLAG, + REG_DIV_CLK_EMMC, 8, 2, + REG_CLK_BYP_0, 5, + 0); +static CV1800_DIV(clk_emmc_100k, clk_parents_1m, + REG_CLK_EN_0, 17, + REG_DIV_CLK_EMMC_100K, 16, 8, 10, CV1800_DIV_FLAG, + 0); + +/* SD */ +static CV1800_GATE(clk_axi4_sd0, clk_axi4_bus_parents, + REG_CLK_EN_0, 18, + 0); +static CV1800_BYPASS_MUX(clk_sd0, clk_axi4_parents, + REG_CLK_EN_0, 19, + REG_DIV_CLK_SD0, 16, 5, 15, CV1800_DIV_FLAG, + REG_DIV_CLK_SD0, 8, 2, + REG_CLK_BYP_0, 6, + 0); +static CV1800_DIV(clk_sd0_100k, clk_parents_1m, + REG_CLK_EN_0, 20, + REG_DIV_CLK_SD0_100K, 16, 8, 10, CV1800_DIV_FLAG, + 0); +static CV1800_GATE(clk_axi4_sd1, clk_axi4_bus_parents, + REG_CLK_EN_0, 21, + 0); +static CV1800_BYPASS_MUX(clk_sd1, clk_axi4_parents, + REG_CLK_EN_0, 22, + REG_DIV_CLK_SD1, 16, 5, 15, CV1800_DIV_FLAG, + REG_DIV_CLK_SD1, 8, 2, + REG_CLK_BYP_0, 7, + 0); +static CV1800_DIV(clk_sd1_100k, clk_parents_1m, + REG_CLK_EN_0, 23, + REG_DIV_CLK_SD1_100K, 16, 8, 10, CV1800_DIV_FLAG, + 0); + +/* SPI NAND */ +static CV1800_BYPASS_MUX(clk_spi_nand, clk_axi4_parents, + REG_CLK_EN_0, 24, + REG_DIV_CLK_SPI_NAND, 16, 5, 8, CV1800_DIV_FLAG, + REG_DIV_CLK_SPI_NAND, 8, 2, + REG_CLK_BYP_0, 8, + 0); + +/* GPIO */ +static CV1800_DIV(clk_gpio_db, clk_parents_1m, + REG_CLK_EN_0, 31, + REG_DIV_CLK_GPIO_DB, 16, 16, 10, CV1800_DIV_FLAG, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_apb_gpio, clk_axi6_bus_parents, + REG_CLK_EN_0, 29, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_apb_gpio_intr, clk_axi6_bus_parents, + REG_CLK_EN_0, 30, + CLK_IS_CRITICAL); + +/* ETH */ +static CV1800_BYPASS_DIV(clk_eth0_500m, clk_bypass_fpll_parents, + REG_CLK_EN_0, 25, + REG_DIV_CLK_GPIO_DB, 16, 4, 3, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 9, + 0); +static CV1800_GATE(clk_axi4_eth0, clk_axi4_bus_parents, + REG_CLK_EN_0, 26, + 0); +static CV1800_BYPASS_DIV(clk_eth1_500m, clk_bypass_fpll_parents, + REG_CLK_EN_0, 27, + REG_DIV_CLK_GPIO_DB, 16, 4, 3, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 10, + 0); +static CV1800_GATE(clk_axi4_eth1, clk_axi4_bus_parents, + REG_CLK_EN_0, 28, + 0); + +/* SF */ +static CV1800_GATE(clk_ahb_sf, clk_axi4_bus_parents, + REG_CLK_EN_1, 0, + 0); +static CV1800_GATE(clk_ahb_sf1, clk_axi4_bus_parents, + REG_CLK_EN_3, 27, + 0); + +/* AUDSRC */ +static CV1800_ACLK(clk_a24m, clk_mipimpll_parents, + REG_APLL_FRAC_DIV_CTRL, 0, + REG_APLL_FRAC_DIV_CTRL, 3, + REG_APLL_FRAC_DIV_CTRL, 1, + REG_APLL_FRAC_DIV_CTRL, 2, + REG_APLL_FRAC_DIV_M, 0, 22, CV1800_DIV_FLAG, + REG_APLL_FRAC_DIV_N, 0, 22, CV1800_DIV_FLAG, + 24576000, + 0); + +static const struct clk_parent_data clk_aud_parents[] = { + { .index = 0 }, + { .hw = &clk_a0pll.common.hw }, + { .hw = &clk_a24m.common.hw }, +}; + +static CV1800_BYPASS_MUX(clk_audsrc, clk_aud_parents, + REG_CLK_EN_4, 1, + REG_DIV_CLK_AUDSRC, 16, 8, 18, CV1800_DIV_FLAG, + REG_DIV_CLK_AUDSRC, 8, 2, + REG_CLK_BYP_1, 2, + 0); +static CV1800_GATE(clk_apb_audsrc, clk_axi4_bus_parents, + REG_CLK_EN_4, 2, + 0); + +/* SDMA */ +static CV1800_GATE(clk_sdma_axi, clk_axi4_bus_parents, + REG_CLK_EN_1, 1, + 0); +static CV1800_BYPASS_MUX(clk_sdma_aud0, clk_aud_parents, + REG_CLK_EN_1, 2, + REG_DIV_CLK_SDMA_AUD0, 16, 8, 18, CV1800_DIV_FLAG, + REG_DIV_CLK_SDMA_AUD0, 8, 2, + REG_CLK_BYP_0, 11, + 0); +static CV1800_BYPASS_MUX(clk_sdma_aud1, clk_aud_parents, + REG_CLK_EN_1, 3, + REG_DIV_CLK_SDMA_AUD1, 16, 8, 18, CV1800_DIV_FLAG, + REG_DIV_CLK_SDMA_AUD1, 8, 2, + REG_CLK_BYP_0, 12, + 0); +static CV1800_BYPASS_MUX(clk_sdma_aud2, clk_aud_parents, + REG_CLK_EN_1, 3, + REG_DIV_CLK_SDMA_AUD2, 16, 8, 18, CV1800_DIV_FLAG, + REG_DIV_CLK_SDMA_AUD2, 8, 2, + REG_CLK_BYP_0, 13, + 0); +static CV1800_BYPASS_MUX(clk_sdma_aud3, clk_aud_parents, + REG_CLK_EN_1, 3, + REG_DIV_CLK_SDMA_AUD3, 16, 8, 18, CV1800_DIV_FLAG, + REG_DIV_CLK_SDMA_AUD3, 8, 2, + REG_CLK_BYP_0, 14, + 0); + +/* SPI */ +static CV1800_GATE(clk_apb_spi0, clk_axi4_bus_parents, + REG_CLK_EN_1, 9, + 0); +static CV1800_GATE(clk_apb_spi1, clk_axi4_bus_parents, + REG_CLK_EN_1, 10, + 0); +static CV1800_GATE(clk_apb_spi2, clk_axi4_bus_parents, + REG_CLK_EN_1, 11, + 0); +static CV1800_GATE(clk_apb_spi3, clk_axi4_bus_parents, + REG_CLK_EN_1, 12, + 0); +static CV1800_BYPASS_DIV(clk_spi, clk_bypass_fpll_parents, + REG_CLK_EN_3, 6, + REG_DIV_CLK_SPI, 16, 6, 8, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 30, + 0); + +/* UART */ +static CV1800_GATE(clk_uart0, clk_uart_parents, + REG_CLK_EN_1, 14, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_apb_uart0, clk_axi4_bus_parents, + REG_CLK_EN_1, 15, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_uart1, clk_uart_parents, + REG_CLK_EN_1, 16, + 0); +static CV1800_GATE(clk_apb_uart1, clk_axi4_bus_parents, + REG_CLK_EN_1, 17, + 0); +static CV1800_GATE(clk_uart2, clk_uart_parents, + REG_CLK_EN_1, 18, + 0); +static CV1800_GATE(clk_apb_uart2, clk_axi4_bus_parents, + REG_CLK_EN_1, 19, + 0); +static CV1800_GATE(clk_uart3, clk_uart_parents, + REG_CLK_EN_1, 20, + 0); +static CV1800_GATE(clk_apb_uart3, clk_axi4_bus_parents, + REG_CLK_EN_1, 21, + 0); +static CV1800_GATE(clk_uart4, clk_uart_parents, + REG_CLK_EN_1, 22, + 0); +static CV1800_GATE(clk_apb_uart4, clk_axi4_bus_parents, + REG_CLK_EN_1, 23, + 0); + +/* I2S */ +static CV1800_GATE(clk_apb_i2s0, clk_axi4_bus_parents, + REG_CLK_EN_1, 24, + 0); +static CV1800_GATE(clk_apb_i2s1, clk_axi4_bus_parents, + REG_CLK_EN_1, 25, + 0); +static CV1800_GATE(clk_apb_i2s2, clk_axi4_bus_parents, + REG_CLK_EN_1, 26, + 0); +static CV1800_GATE(clk_apb_i2s3, clk_axi4_bus_parents, + REG_CLK_EN_1, 27, + 0); + +/* DEBUG */ +static CV1800_GATE(clk_debug, osc_parents, + REG_CLK_EN_0, 13, + CLK_IS_CRITICAL); +static CV1800_BYPASS_DIV(clk_ap_debug, clk_bypass_fpll_parents, + REG_CLK_EN_4, 5, + REG_DIV_CLK_AP_DEBUG, 16, 4, 5, CV1800_DIV_FLAG, + REG_CLK_BYP_1, 4, + CLK_IS_CRITICAL); + +/* DDR */ +static CV1800_GATE(clk_ddr_axi_reg, clk_axi6_bus_parents, + REG_CLK_EN_0, 7, + CLK_IS_CRITICAL); + +/* I2C */ +static CV1800_GATE(clk_apb_i2c, clk_axi4_bus_parents, + REG_CLK_EN_1, 6, + 0); +static CV1800_BYPASS_DIV(clk_i2c, clk_bypass_axi6_bus_parents, + REG_CLK_EN_3, 7, + REG_DIV_CLK_I2C, 16, 4, 1, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 31, + 0); +static CV1800_GATE(clk_apb_i2c0, clk_axi4_bus_parents, + REG_CLK_EN_3, 17, + 0); +static CV1800_GATE(clk_apb_i2c1, clk_axi4_bus_parents, + REG_CLK_EN_3, 18, + 0); +static CV1800_GATE(clk_apb_i2c2, clk_axi4_bus_parents, + REG_CLK_EN_3, 19, + 0); +static CV1800_GATE(clk_apb_i2c3, clk_axi4_bus_parents, + REG_CLK_EN_3, 20, + 0); +static CV1800_GATE(clk_apb_i2c4, clk_axi4_bus_parents, + REG_CLK_EN_3, 21, + 0); + +/* USB */ +static CV1800_GATE(clk_axi4_usb, clk_axi4_bus_parents, + REG_CLK_EN_1, 28, + 0); +static CV1800_GATE(clk_apb_usb, clk_axi4_bus_parents, + REG_CLK_EN_1, 29, + 0); +static CV1800_BYPASS_FIXED_DIV(clk_usb_125m, clk_bypass_fpll_parents, + REG_CLK_EN_1, 30, + 12, + REG_CLK_BYP_0, 17, + CLK_SET_RATE_PARENT); +static CV1800_FIXED_DIV(clk_usb_33k, clk_parents_1m, + REG_CLK_EN_1, 31, + 3, + 0); +static CV1800_BYPASS_FIXED_DIV(clk_usb_12m, clk_bypass_fpll_parents, + REG_CLK_EN_2, 0, + 125, + REG_CLK_BYP_0, 18, + CLK_SET_RATE_PARENT); + +/* VIP SYS */ +static const struct clk_parent_data clk_vip_sys_parents[] = { + { .index = 0 }, + { .hw = &clk_mipimpll.common.hw }, + { .hw = &clk_cam0pll.common.hw }, + { .hw = &clk_disppll.common.hw }, + { .hw = &clk_fpll.common.hw }, +}; +static const struct clk_parent_data clk_disp_vip_parents[] = { + { .index = 0 }, + { .hw = &clk_disppll.common.hw }, +}; + +static CV1800_BYPASS_DIV(clk_dsi_esc, clk_bypass_axi6_bus_parents, + REG_CLK_EN_2, 3, + REG_DIV_CLK_DSI_ESC, 16, 4, 5, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 21, + 0); +static CV1800_BYPASS_MUX(clk_axi_vip, clk_vip_sys_parents, + REG_CLK_EN_2, 4, + REG_DIV_CLK_AXI_VIP, 16, 4, 3, CV1800_DIV_FLAG, + REG_DIV_CLK_AXI_VIP, 8, 2, + REG_CLK_BYP_0, 22, + 0); + +static const struct clk_parent_data clk_axi_vip_bus_parents[] = { + { .hw = &clk_axi_vip.mux.common.hw }, +}; + +static CV1800_BYPASS_MUX(clk_src_vip_sys_0, clk_vip_sys_parents, + REG_CLK_EN_2, 5, + REG_DIV_CLK_SRC_VIP_SYS_0, 16, 4, 6, CV1800_DIV_FLAG, + REG_DIV_CLK_SRC_VIP_SYS_0, 8, 2, + REG_CLK_BYP_0, 23, + 0); +static CV1800_BYPASS_MUX(clk_src_vip_sys_1, clk_vip_sys_parents, + REG_CLK_EN_2, 6, + REG_DIV_CLK_SRC_VIP_SYS_1, 16, 4, 6, CV1800_DIV_FLAG, + REG_DIV_CLK_SRC_VIP_SYS_1, 8, 2, + REG_CLK_BYP_0, 24, + 0); +static CV1800_BYPASS_DIV(clk_disp_src_vip, clk_disp_vip_parents, + REG_CLK_EN_2, 7, + REG_DIV_CLK_DISP_SRC_VIP, 16, 4, 8, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 25, + 0); +static CV1800_BYPASS_MUX(clk_src_vip_sys_2, clk_vip_sys_parents, + REG_CLK_EN_3, 29, + REG_DIV_CLK_SRC_VIP_SYS_2, 16, 4, 2, CV1800_DIV_FLAG, + REG_DIV_CLK_SRC_VIP_SYS_2, 8, 2, + REG_CLK_BYP_1, 1, + 0); +static CV1800_GATE(clk_csi_mac0_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 18, + 0); +static CV1800_GATE(clk_csi_mac1_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 19, + 0); +static CV1800_GATE(clk_isp_top_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 20, + 0); +static CV1800_GATE(clk_img_d_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 21, + 0); +static CV1800_GATE(clk_img_v_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 22, + 0); +static CV1800_GATE(clk_sc_top_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 23, + 0); +static CV1800_GATE(clk_sc_d_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 24, + 0); +static CV1800_GATE(clk_sc_v1_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 25, + 0); +static CV1800_GATE(clk_sc_v2_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 26, + 0); +static CV1800_GATE(clk_sc_v3_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 27, + 0); +static CV1800_GATE(clk_dwa_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 28, + 0); +static CV1800_GATE(clk_bt_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 29, + 0); +static CV1800_GATE(clk_disp_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 30, + 0); +static CV1800_GATE(clk_dsi_mac_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_2, 31, + 0); +static CV1800_GATE(clk_lvds0_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_3, 0, + 0); +static CV1800_GATE(clk_lvds1_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_3, 1, + 0); +static CV1800_GATE(clk_csi0_rx_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_3, 2, + 0); +static CV1800_GATE(clk_csi1_rx_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_3, 3, + 0); +static CV1800_GATE(clk_pad_vi_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_3, 4, + 0); +static CV1800_GATE(clk_pad_vi1_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_3, 30, + 0); +static CV1800_GATE(clk_cfg_reg_vip, clk_axi6_bus_parents, + REG_CLK_EN_3, 31, + 0); +static CV1800_GATE(clk_pad_vi2_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 7, + 0); +static CV1800_GATE(clk_csi_be_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 8, + 0); +static CV1800_GATE(clk_vip_ip0, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 9, + 0); +static CV1800_GATE(clk_vip_ip1, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 10, + 0); +static CV1800_GATE(clk_vip_ip2, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 11, + 0); +static CV1800_GATE(clk_vip_ip3, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 12, + 0); +static CV1800_BYPASS_MUX(clk_src_vip_sys_3, clk_vip_sys_parents, + REG_CLK_EN_4, 15, + REG_DIV_CLK_SRC_VIP_SYS_3, 16, 4, 2, CV1800_DIV_FLAG, + REG_DIV_CLK_SRC_VIP_SYS_3, 8, 2, + REG_CLK_BYP_1, 8, + 0); +static CV1800_BYPASS_MUX(clk_src_vip_sys_4, clk_vip_sys_parents, + REG_CLK_EN_4, 16, + REG_DIV_CLK_SRC_VIP_SYS_4, 16, 4, 3, CV1800_DIV_FLAG, + REG_DIV_CLK_SRC_VIP_SYS_4, 8, 2, + REG_CLK_BYP_1, 9, + 0); +static CV1800_GATE(clk_ive_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 17, + 0); +static CV1800_GATE(clk_raw_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 18, + 0); +static CV1800_GATE(clk_osdc_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 19, + 0); +static CV1800_GATE(clk_csi_mac2_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 20, + 0); +static CV1800_GATE(clk_cam0_vip, clk_axi_vip_bus_parents, + REG_CLK_EN_4, 21, + 0); + +/* CAM OUT */ +static const struct clk_parent_data clk_cam_parents[] = { + { .hw = &clk_cam0pll.common.hw }, + { .hw = &clk_cam0pll_d2.common.hw }, + { .hw = &clk_cam0pll_d3.common.hw }, + { .hw = &clk_mipimpll_d3.common.hw }, +}; + +static CV1800_MUX(clk_cam0, clk_cam_parents, + REG_CLK_EN_2, 16, + REG_CLK_CAM0_SRC_DIV, 16, 6, 0, CV1800_DIV_FLAG, + REG_CLK_CAM0_SRC_DIV, 8, 2, + CLK_IGNORE_UNUSED); +static CV1800_MUX(clk_cam1, clk_cam_parents, + REG_CLK_EN_2, 17, + REG_CLK_CAM1_SRC_DIV, 16, 6, 0, CV1800_DIV_FLAG, + REG_CLK_CAM1_SRC_DIV, 8, 2, + CLK_IGNORE_UNUSED); + +/* VIDEO SUBSYS */ +static const struct clk_parent_data clk_axi_video_codec_parents[] = { + { .index = 0 }, + { .hw = &clk_a0pll.common.hw }, + { .hw = &clk_mipimpll.common.hw }, + { .hw = &clk_cam1pll.common.hw }, + { .hw = &clk_fpll.common.hw }, +}; +static const struct clk_parent_data clk_vc_src0_parents[] = { + { .index = 0 }, + { .hw = &clk_disppll.common.hw }, + { .hw = &clk_mipimpll.common.hw }, + { .hw = &clk_cam1pll.common.hw }, + { .hw = &clk_fpll.common.hw }, +}; +static const struct clk_parent_data clk_vc_src1_parents[] = { + { .index = 0 }, + { .hw = &clk_cam1pll.common.hw }, +}; + +static CV1800_BYPASS_MUX(clk_axi_video_codec, clk_axi_video_codec_parents, + REG_CLK_EN_2, 8, + REG_DIV_CLK_AXI_VIDEO_CODEC, 16, 4, 2, CV1800_DIV_FLAG, + REG_DIV_CLK_AXI_VIDEO_CODEC, 8, 2, + REG_CLK_BYP_0, 26, + 0); + +static const struct clk_parent_data clk_axi_video_codec_bus_parents[] = { + { .hw = &clk_axi_video_codec.mux.common.hw }, +}; + +static CV1800_BYPASS_MUX(clk_vc_src0, clk_vc_src0_parents, + REG_CLK_EN_2, 9, + REG_DIV_CLK_VC_SRC0, 16, 4, 2, CV1800_DIV_FLAG, + REG_DIV_CLK_VC_SRC0, 8, 2, + REG_CLK_BYP_0, 27, + 0); + +static CV1800_GATE(clk_h264c, clk_axi_video_codec_bus_parents, + REG_CLK_EN_2, 10, + 0); +static CV1800_GATE(clk_h265c, clk_axi_video_codec_bus_parents, + REG_CLK_EN_2, 11, + 0); +static CV1800_GATE(clk_jpeg, clk_axi_video_codec_bus_parents, + REG_CLK_EN_2, 12, + CLK_IGNORE_UNUSED); +static CV1800_GATE(clk_apb_jpeg, clk_axi6_bus_parents, + REG_CLK_EN_2, 13, + CLK_IGNORE_UNUSED); +static CV1800_GATE(clk_apb_h264c, clk_axi6_bus_parents, + REG_CLK_EN_2, 14, + 0); +static CV1800_GATE(clk_apb_h265c, clk_axi6_bus_parents, + REG_CLK_EN_2, 15, + 0); +static CV1800_BYPASS_FIXED_DIV(clk_vc_src1, clk_vc_src1_parents, + REG_CLK_EN_3, 28, + 2, + REG_CLK_BYP_1, 0, + CLK_SET_RATE_PARENT); +static CV1800_BYPASS_FIXED_DIV(clk_vc_src2, clk_bypass_fpll_parents, + REG_CLK_EN_4, 3, + 3, + REG_CLK_BYP_1, 3, + CLK_SET_RATE_PARENT); + +/* VC SYS */ +static CV1800_GATE(clk_cfg_reg_vc, clk_axi6_bus_parents, + REG_CLK_EN_4, 0, + CLK_IGNORE_UNUSED); + +/* PWM */ +static CV1800_BYPASS_MUX(clk_pwm_src, clk_axi4_parents, + REG_CLK_EN_4, 4, + REG_DIV_CLK_PWM_SRC_0, 16, 6, 10, CV1800_DIV_FLAG, + REG_DIV_CLK_PWM_SRC_0, 8, 2, + REG_CLK_BYP_0, 15, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_pwm_parents[] = { + { .hw = &clk_pwm_src.mux.common.hw }, +}; + +static CV1800_GATE(clk_pwm, clk_pwm_parents, + REG_CLK_EN_1, 8, + CLK_IS_CRITICAL); + +/* C906 */ +static const struct clk_parent_data clk_c906_0_parents[] = { + { .index = 0 }, + { .hw = &clk_tpll.common.hw }, + { .hw = &clk_a0pll.common.hw }, + { .hw = &clk_mipimpll.common.hw }, + { .hw = &clk_mpll.common.hw }, + { .hw = &clk_fpll.common.hw }, +}; +static const struct clk_parent_data clk_c906_1_parents[] = { + { .index = 0 }, + { .hw = &clk_tpll.common.hw }, + { .hw = &clk_a0pll.common.hw }, + { .hw = &clk_disppll.common.hw }, + { .hw = &clk_mpll.common.hw }, + { .hw = &clk_fpll.common.hw }, +}; + +static const s8 clk_c906_parent2sel[] = { + -1, /* osc */ + 0, /* mux 0: clk_tpll(c906_0), clk_tpll(c906_1) */ + 0, /* mux 0: clk_a0pll(c906_0), clk_a0pll(c906_1) */ + 0, /* mux 0: clk_mipimpll(c906_0), clk_disppll(c906_1) */ + 0, /* mux 0: clk_mpll(c906_0), clk_mpll(c906_1) */ + 1 /* mux 1: clk_fpll(c906_0), clk_fpll(c906_1) */ +}; + +static const u8 clk_c906_sel2parent[2][4] = { + [0] = { + 1, + 2, + 3, + 4 + }, + [1] = { + 5, + 5, + 5, + 5 + }, +}; + +static CV1800_MMUX(clk_c906_0, clk_c906_0_parents, + REG_CLK_EN_4, 13, + REG_DIV_CLK_C906_0_0, 16, 4, 1, CV1800_DIV_FLAG, + REG_DIV_CLK_C906_0_1, 16, 4, 2, CV1800_DIV_FLAG, + REG_DIV_CLK_C906_0_0, 8, 2, + REG_DIV_CLK_C906_0_1, 8, 2, + REG_CLK_BYP_1, 6, + REG_CLK_SEL_0, 23, + clk_c906_parent2sel, + clk_c906_sel2parent[0], clk_c906_sel2parent[1], + CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); +static CV1800_MMUX(clk_c906_1, clk_c906_1_parents, + REG_CLK_EN_4, 14, + REG_DIV_CLK_C906_1_0, 16, 4, 2, CV1800_DIV_FLAG, + REG_DIV_CLK_C906_1_1, 16, 4, 3, CV1800_DIV_FLAG, + REG_DIV_CLK_C906_1_0, 8, 2, + REG_DIV_CLK_C906_1_1, 8, 2, + REG_CLK_BYP_1, 7, + REG_CLK_SEL_0, 24, + clk_c906_parent2sel, + clk_c906_sel2parent[0], clk_c906_sel2parent[1], + CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); + +/* A53 */ +static CV1800_BYPASS_DIV(clk_cpu_axi0, clk_axi4_parents, + REG_CLK_EN_0, 1, + REG_DIV_CLK_CPU_AXI0, 16, 4, 3, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 1, + CLK_IS_CRITICAL); +static CV1800_BYPASS_DIV(clk_cpu_gic, clk_bypass_fpll_parents, + REG_CLK_EN_0, 2, + REG_DIV_CLK_CPU_GIC, 16, 4, 5, CV1800_DIV_FLAG, + REG_CLK_BYP_0, 2, + CLK_IS_CRITICAL); +static CV1800_GATE(clk_xtal_ap, osc_parents, + REG_CLK_EN_0, 3, + CLK_IS_CRITICAL); + +static const struct clk_parent_data clk_a53_parents[] = { + { .index = 0 }, + { .hw = &clk_tpll.common.hw }, + { .hw = &clk_a0pll.common.hw }, + { .hw = &clk_mipimpll.common.hw }, + { .hw = &clk_mpll.common.hw }, + { .hw = &clk_fpll.common.hw }, +}; + +static const s8 clk_a53_parent2sel[] = { + -1, /* osc */ + 0, /* mux 0: clk_tpll */ + 0, /* mux 0: clk_a0pll */ + 0, /* mux 0: clk_mipimpll */ + 0, /* mux 0: clk_mpll */ + 1 /* mux 1: clk_fpll */ +}; + +static const u8 clk_a53_sel2parent[2][4] = { + [0] = { + 1, + 2, + 3, + 4 + }, + [1] = { + 5, + 5, + 5, + 5 + }, +}; + +/* + * Clock for A53 cpu in the CV18XX/SG200X series. + * For CV180X and CV181X series, this clock is not used, but can not + * be set to bypass mode, or the SoC will hang. + */ +static CV1800_MMUX(clk_a53, clk_a53_parents, + REG_CLK_EN_0, 0, + REG_DIV_CLK_A53_0, 16, 4, 1, CV1800_DIV_FLAG, + REG_DIV_CLK_A53_1, 16, 4, 2, CV1800_DIV_FLAG, + REG_DIV_CLK_A53_0, 8, 2, + REG_DIV_CLK_A53_1, 8, 2, + REG_CLK_BYP_0, 0, + REG_CLK_SEL_0, 0, + clk_a53_parent2sel, + clk_a53_sel2parent[0], clk_a53_sel2parent[1], + CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); + +static struct clk_hw_onecell_data cv1800_hw_clks = { + .num = CV1800_CLK_MAX, + .hws = { + [CLK_MPLL] = &clk_mpll.common.hw, + [CLK_TPLL] = &clk_tpll.common.hw, + [CLK_FPLL] = &clk_fpll.common.hw, + [CLK_MIPIMPLL] = &clk_mipimpll.common.hw, + [CLK_A0PLL] = &clk_a0pll.common.hw, + [CLK_DISPPLL] = &clk_disppll.common.hw, + [CLK_CAM0PLL] = &clk_cam0pll.common.hw, + [CLK_CAM1PLL] = &clk_cam1pll.common.hw, + + [CLK_MIPIMPLL_D3] = &clk_mipimpll_d3.common.hw, + [CLK_CAM0PLL_D2] = &clk_cam0pll_d2.common.hw, + [CLK_CAM0PLL_D3] = &clk_cam0pll_d3.common.hw, + + [CLK_TPU] = &clk_tpu.mux.common.hw, + [CLK_TPU_FAB] = &clk_tpu_fab.common.hw, + [CLK_AHB_ROM] = &clk_ahb_rom.common.hw, + [CLK_DDR_AXI_REG] = &clk_ddr_axi_reg.common.hw, + [CLK_RTC_25M] = &clk_rtc_25m.common.hw, + [CLK_SRC_RTC_SYS_0] = &clk_src_rtc_sys_0.div.common.hw, + [CLK_TEMPSEN] = &clk_tempsen.common.hw, + [CLK_SARADC] = &clk_saradc.common.hw, + [CLK_EFUSE] = &clk_efuse.common.hw, + [CLK_APB_EFUSE] = &clk_apb_efuse.common.hw, + [CLK_DEBUG] = &clk_debug.common.hw, + [CLK_AP_DEBUG] = &clk_ap_debug.div.common.hw, + [CLK_XTAL_MISC] = &clk_xtal_misc.common.hw, + [CLK_AXI4_EMMC] = &clk_axi4_emmc.common.hw, + [CLK_EMMC] = &clk_emmc.mux.common.hw, + [CLK_EMMC_100K] = &clk_emmc_100k.common.hw, + [CLK_AXI4_SD0] = &clk_axi4_sd0.common.hw, + [CLK_SD0] = &clk_sd0.mux.common.hw, + [CLK_SD0_100K] = &clk_sd0_100k.common.hw, + [CLK_AXI4_SD1] = &clk_axi4_sd1.common.hw, + [CLK_SD1] = &clk_sd1.mux.common.hw, + [CLK_SD1_100K] = &clk_sd1_100k.common.hw, + [CLK_SPI_NAND] = &clk_spi_nand.mux.common.hw, + [CLK_ETH0_500M] = &clk_eth0_500m.div.common.hw, + [CLK_AXI4_ETH0] = &clk_axi4_eth0.common.hw, + [CLK_ETH1_500M] = &clk_eth1_500m.div.common.hw, + [CLK_AXI4_ETH1] = &clk_axi4_eth1.common.hw, + [CLK_APB_GPIO] = &clk_apb_gpio.common.hw, + [CLK_APB_GPIO_INTR] = &clk_apb_gpio_intr.common.hw, + [CLK_GPIO_DB] = &clk_gpio_db.common.hw, + [CLK_AHB_SF] = &clk_ahb_sf.common.hw, + [CLK_AHB_SF1] = &clk_ahb_sf1.common.hw, + [CLK_A24M] = &clk_a24m.common.hw, + [CLK_AUDSRC] = &clk_audsrc.mux.common.hw, + [CLK_APB_AUDSRC] = &clk_apb_audsrc.common.hw, + [CLK_SDMA_AXI] = &clk_sdma_axi.common.hw, + [CLK_SDMA_AUD0] = &clk_sdma_aud0.mux.common.hw, + [CLK_SDMA_AUD1] = &clk_sdma_aud1.mux.common.hw, + [CLK_SDMA_AUD2] = &clk_sdma_aud2.mux.common.hw, + [CLK_SDMA_AUD3] = &clk_sdma_aud3.mux.common.hw, + [CLK_I2C] = &clk_i2c.div.common.hw, + [CLK_APB_I2C] = &clk_apb_i2c.common.hw, + [CLK_APB_I2C0] = &clk_apb_i2c0.common.hw, + [CLK_APB_I2C1] = &clk_apb_i2c1.common.hw, + [CLK_APB_I2C2] = &clk_apb_i2c2.common.hw, + [CLK_APB_I2C3] = &clk_apb_i2c3.common.hw, + [CLK_APB_I2C4] = &clk_apb_i2c4.common.hw, + [CLK_APB_WDT] = &clk_apb_wdt.common.hw, + [CLK_PWM_SRC] = &clk_pwm_src.mux.common.hw, + [CLK_PWM] = &clk_pwm.common.hw, + [CLK_SPI] = &clk_spi.div.common.hw, + [CLK_APB_SPI0] = &clk_apb_spi0.common.hw, + [CLK_APB_SPI1] = &clk_apb_spi1.common.hw, + [CLK_APB_SPI2] = &clk_apb_spi2.common.hw, + [CLK_APB_SPI3] = &clk_apb_spi3.common.hw, + [CLK_1M] = &clk_1m.common.hw, + [CLK_CAM0_200] = &clk_cam0_200.mux.common.hw, + [CLK_PM] = &clk_pm.common.hw, + [CLK_TIMER0] = &clk_timer0.common.hw, + [CLK_TIMER1] = &clk_timer1.common.hw, + [CLK_TIMER2] = &clk_timer2.common.hw, + [CLK_TIMER3] = &clk_timer3.common.hw, + [CLK_TIMER4] = &clk_timer4.common.hw, + [CLK_TIMER5] = &clk_timer5.common.hw, + [CLK_TIMER6] = &clk_timer6.common.hw, + [CLK_TIMER7] = &clk_timer7.common.hw, + [CLK_UART0] = &clk_uart0.common.hw, + [CLK_APB_UART0] = &clk_apb_uart0.common.hw, + [CLK_UART1] = &clk_uart1.common.hw, + [CLK_APB_UART1] = &clk_apb_uart1.common.hw, + [CLK_UART2] = &clk_uart2.common.hw, + [CLK_APB_UART2] = &clk_apb_uart2.common.hw, + [CLK_UART3] = &clk_uart3.common.hw, + [CLK_APB_UART3] = &clk_apb_uart3.common.hw, + [CLK_UART4] = &clk_uart4.common.hw, + [CLK_APB_UART4] = &clk_apb_uart4.common.hw, + [CLK_APB_I2S0] = &clk_apb_i2s0.common.hw, + [CLK_APB_I2S1] = &clk_apb_i2s1.common.hw, + [CLK_APB_I2S2] = &clk_apb_i2s2.common.hw, + [CLK_APB_I2S3] = &clk_apb_i2s3.common.hw, + [CLK_AXI4_USB] = &clk_axi4_usb.common.hw, + [CLK_APB_USB] = &clk_apb_usb.common.hw, + [CLK_USB_125M] = &clk_usb_125m.div.common.hw, + [CLK_USB_33K] = &clk_usb_33k.common.hw, + [CLK_USB_12M] = &clk_usb_12m.div.common.hw, + [CLK_AXI4] = &clk_axi4.mux.common.hw, + [CLK_AXI6] = &clk_axi6.div.common.hw, + [CLK_DSI_ESC] = &clk_dsi_esc.div.common.hw, + [CLK_AXI_VIP] = &clk_axi_vip.mux.common.hw, + [CLK_SRC_VIP_SYS_0] = &clk_src_vip_sys_0.mux.common.hw, + [CLK_SRC_VIP_SYS_1] = &clk_src_vip_sys_1.mux.common.hw, + [CLK_SRC_VIP_SYS_2] = &clk_src_vip_sys_2.mux.common.hw, + [CLK_SRC_VIP_SYS_3] = &clk_src_vip_sys_3.mux.common.hw, + [CLK_SRC_VIP_SYS_4] = &clk_src_vip_sys_4.mux.common.hw, + [CLK_CSI_BE_VIP] = &clk_csi_be_vip.common.hw, + [CLK_CSI_MAC0_VIP] = &clk_csi_mac0_vip.common.hw, + [CLK_CSI_MAC1_VIP] = &clk_csi_mac1_vip.common.hw, + [CLK_CSI_MAC2_VIP] = &clk_csi_mac2_vip.common.hw, + [CLK_CSI0_RX_VIP] = &clk_csi0_rx_vip.common.hw, + [CLK_CSI1_RX_VIP] = &clk_csi1_rx_vip.common.hw, + [CLK_ISP_TOP_VIP] = &clk_isp_top_vip.common.hw, + [CLK_IMG_D_VIP] = &clk_img_d_vip.common.hw, + [CLK_IMG_V_VIP] = &clk_img_v_vip.common.hw, + [CLK_SC_TOP_VIP] = &clk_sc_top_vip.common.hw, + [CLK_SC_D_VIP] = &clk_sc_d_vip.common.hw, + [CLK_SC_V1_VIP] = &clk_sc_v1_vip.common.hw, + [CLK_SC_V2_VIP] = &clk_sc_v2_vip.common.hw, + [CLK_SC_V3_VIP] = &clk_sc_v3_vip.common.hw, + [CLK_DWA_VIP] = &clk_dwa_vip.common.hw, + [CLK_BT_VIP] = &clk_bt_vip.common.hw, + [CLK_DISP_VIP] = &clk_disp_vip.common.hw, + [CLK_DSI_MAC_VIP] = &clk_dsi_mac_vip.common.hw, + [CLK_LVDS0_VIP] = &clk_lvds0_vip.common.hw, + [CLK_LVDS1_VIP] = &clk_lvds1_vip.common.hw, + [CLK_PAD_VI_VIP] = &clk_pad_vi_vip.common.hw, + [CLK_PAD_VI1_VIP] = &clk_pad_vi1_vip.common.hw, + [CLK_PAD_VI2_VIP] = &clk_pad_vi2_vip.common.hw, + [CLK_CFG_REG_VIP] = &clk_cfg_reg_vip.common.hw, + [CLK_VIP_IP0] = &clk_vip_ip0.common.hw, + [CLK_VIP_IP1] = &clk_vip_ip1.common.hw, + [CLK_VIP_IP2] = &clk_vip_ip2.common.hw, + [CLK_VIP_IP3] = &clk_vip_ip3.common.hw, + [CLK_IVE_VIP] = &clk_ive_vip.common.hw, + [CLK_RAW_VIP] = &clk_raw_vip.common.hw, + [CLK_OSDC_VIP] = &clk_osdc_vip.common.hw, + [CLK_CAM0_VIP] = &clk_cam0_vip.common.hw, + [CLK_AXI_VIDEO_CODEC] = &clk_axi_video_codec.mux.common.hw, + [CLK_VC_SRC0] = &clk_vc_src0.mux.common.hw, + [CLK_VC_SRC1] = &clk_vc_src1.div.common.hw, + [CLK_VC_SRC2] = &clk_vc_src2.div.common.hw, + [CLK_H264C] = &clk_h264c.common.hw, + [CLK_APB_H264C] = &clk_apb_h264c.common.hw, + [CLK_H265C] = &clk_h265c.common.hw, + [CLK_APB_H265C] = &clk_apb_h265c.common.hw, + [CLK_JPEG] = &clk_jpeg.common.hw, + [CLK_APB_JPEG] = &clk_apb_jpeg.common.hw, + [CLK_CAM0] = &clk_cam0.common.hw, + [CLK_CAM1] = &clk_cam1.common.hw, + [CLK_WGN] = &clk_wgn.common.hw, + [CLK_WGN0] = &clk_wgn0.common.hw, + [CLK_WGN1] = &clk_wgn1.common.hw, + [CLK_WGN2] = &clk_wgn2.common.hw, + [CLK_KEYSCAN] = &clk_keyscan.common.hw, + [CLK_CFG_REG_VC] = &clk_cfg_reg_vc.common.hw, + [CLK_C906_0] = &clk_c906_0.common.hw, + [CLK_C906_1] = &clk_c906_1.common.hw, + [CLK_A53] = &clk_a53.common.hw, + [CLK_CPU_AXI0] = &clk_cpu_axi0.div.common.hw, + [CLK_CPU_GIC] = &clk_cpu_gic.div.common.hw, + [CLK_XTAL_AP] = &clk_xtal_ap.common.hw, + }, +}; + +static void cv18xx_clk_disable_auto_pd(void __iomem *base) +{ + static const u16 CV1800_PD_CLK[] = { + REG_MIPIMPLL_CLK_CSR, + REG_A0PLL_CLK_CSR, + REG_DISPPLL_CLK_CSR, + REG_CAM0PLL_CLK_CSR, + REG_CAM1PLL_CLK_CSR, + }; + + u32 val; + int i; + + /* disable auto power down */ + for (i = 0; i < ARRAY_SIZE(CV1800_PD_CLK); i++) { + u32 reg = CV1800_PD_CLK[i]; + + val = readl(base + reg); + val |= GENMASK(12, 9); + val &= ~BIT(8); + writel(val, base + reg); + } +} + +static void cv18xx_clk_disable_a53(void __iomem *base) +{ + u32 val = readl(base + REG_CLK_BYP_0); + + /* Set bypass clock for clk_a53 */ + val |= BIT(0); + + /* Set bypass clock for clk_cpu_axi0 */ + val |= BIT(1); + + /* Set bypass clock for clk_cpu_gic */ + val |= BIT(2); + + writel(val, base + REG_CLK_BYP_0); +} + +static int cv1800_pre_init(struct device *dev, void __iomem *base, + struct cv1800_clk_ctrl *ctrl, + const struct cv1800_clk_desc *desc) +{ + u32 val = readl(base + REG_CLK_EN_2); + + /* disable unsupported clk_disp_src_vip */ + val &= ~BIT(7); + + writel(val, base + REG_CLK_EN_2); + + cv18xx_clk_disable_a53(base); + cv18xx_clk_disable_auto_pd(base); + + return 0; +} + +static const struct cv1800_clk_desc cv1800_desc = { + .clks_data = &cv1800_hw_clks, + .pre_init = cv1800_pre_init, +}; + +static struct clk_hw_onecell_data cv1810_hw_clks = { + .num = CV1810_CLK_MAX, + .hws = { + [CLK_MPLL] = &clk_mpll.common.hw, + [CLK_TPLL] = &clk_tpll.common.hw, + [CLK_FPLL] = &clk_fpll.common.hw, + [CLK_MIPIMPLL] = &clk_mipimpll.common.hw, + [CLK_A0PLL] = &clk_a0pll.common.hw, + [CLK_DISPPLL] = &clk_disppll.common.hw, + [CLK_CAM0PLL] = &clk_cam0pll.common.hw, + [CLK_CAM1PLL] = &clk_cam1pll.common.hw, + + [CLK_MIPIMPLL_D3] = &clk_mipimpll_d3.common.hw, + [CLK_CAM0PLL_D2] = &clk_cam0pll_d2.common.hw, + [CLK_CAM0PLL_D3] = &clk_cam0pll_d3.common.hw, + + [CLK_TPU] = &clk_tpu.mux.common.hw, + [CLK_TPU_FAB] = &clk_tpu_fab.common.hw, + [CLK_AHB_ROM] = &clk_ahb_rom.common.hw, + [CLK_DDR_AXI_REG] = &clk_ddr_axi_reg.common.hw, + [CLK_RTC_25M] = &clk_rtc_25m.common.hw, + [CLK_SRC_RTC_SYS_0] = &clk_src_rtc_sys_0.div.common.hw, + [CLK_TEMPSEN] = &clk_tempsen.common.hw, + [CLK_SARADC] = &clk_saradc.common.hw, + [CLK_EFUSE] = &clk_efuse.common.hw, + [CLK_APB_EFUSE] = &clk_apb_efuse.common.hw, + [CLK_DEBUG] = &clk_debug.common.hw, + [CLK_AP_DEBUG] = &clk_ap_debug.div.common.hw, + [CLK_XTAL_MISC] = &clk_xtal_misc.common.hw, + [CLK_AXI4_EMMC] = &clk_axi4_emmc.common.hw, + [CLK_EMMC] = &clk_emmc.mux.common.hw, + [CLK_EMMC_100K] = &clk_emmc_100k.common.hw, + [CLK_AXI4_SD0] = &clk_axi4_sd0.common.hw, + [CLK_SD0] = &clk_sd0.mux.common.hw, + [CLK_SD0_100K] = &clk_sd0_100k.common.hw, + [CLK_AXI4_SD1] = &clk_axi4_sd1.common.hw, + [CLK_SD1] = &clk_sd1.mux.common.hw, + [CLK_SD1_100K] = &clk_sd1_100k.common.hw, + [CLK_SPI_NAND] = &clk_spi_nand.mux.common.hw, + [CLK_ETH0_500M] = &clk_eth0_500m.div.common.hw, + [CLK_AXI4_ETH0] = &clk_axi4_eth0.common.hw, + [CLK_ETH1_500M] = &clk_eth1_500m.div.common.hw, + [CLK_AXI4_ETH1] = &clk_axi4_eth1.common.hw, + [CLK_APB_GPIO] = &clk_apb_gpio.common.hw, + [CLK_APB_GPIO_INTR] = &clk_apb_gpio_intr.common.hw, + [CLK_GPIO_DB] = &clk_gpio_db.common.hw, + [CLK_AHB_SF] = &clk_ahb_sf.common.hw, + [CLK_AHB_SF1] = &clk_ahb_sf1.common.hw, + [CLK_A24M] = &clk_a24m.common.hw, + [CLK_AUDSRC] = &clk_audsrc.mux.common.hw, + [CLK_APB_AUDSRC] = &clk_apb_audsrc.common.hw, + [CLK_SDMA_AXI] = &clk_sdma_axi.common.hw, + [CLK_SDMA_AUD0] = &clk_sdma_aud0.mux.common.hw, + [CLK_SDMA_AUD1] = &clk_sdma_aud1.mux.common.hw, + [CLK_SDMA_AUD2] = &clk_sdma_aud2.mux.common.hw, + [CLK_SDMA_AUD3] = &clk_sdma_aud3.mux.common.hw, + [CLK_I2C] = &clk_i2c.div.common.hw, + [CLK_APB_I2C] = &clk_apb_i2c.common.hw, + [CLK_APB_I2C0] = &clk_apb_i2c0.common.hw, + [CLK_APB_I2C1] = &clk_apb_i2c1.common.hw, + [CLK_APB_I2C2] = &clk_apb_i2c2.common.hw, + [CLK_APB_I2C3] = &clk_apb_i2c3.common.hw, + [CLK_APB_I2C4] = &clk_apb_i2c4.common.hw, + [CLK_APB_WDT] = &clk_apb_wdt.common.hw, + [CLK_PWM_SRC] = &clk_pwm_src.mux.common.hw, + [CLK_PWM] = &clk_pwm.common.hw, + [CLK_SPI] = &clk_spi.div.common.hw, + [CLK_APB_SPI0] = &clk_apb_spi0.common.hw, + [CLK_APB_SPI1] = &clk_apb_spi1.common.hw, + [CLK_APB_SPI2] = &clk_apb_spi2.common.hw, + [CLK_APB_SPI3] = &clk_apb_spi3.common.hw, + [CLK_1M] = &clk_1m.common.hw, + [CLK_CAM0_200] = &clk_cam0_200.mux.common.hw, + [CLK_PM] = &clk_pm.common.hw, + [CLK_TIMER0] = &clk_timer0.common.hw, + [CLK_TIMER1] = &clk_timer1.common.hw, + [CLK_TIMER2] = &clk_timer2.common.hw, + [CLK_TIMER3] = &clk_timer3.common.hw, + [CLK_TIMER4] = &clk_timer4.common.hw, + [CLK_TIMER5] = &clk_timer5.common.hw, + [CLK_TIMER6] = &clk_timer6.common.hw, + [CLK_TIMER7] = &clk_timer7.common.hw, + [CLK_UART0] = &clk_uart0.common.hw, + [CLK_APB_UART0] = &clk_apb_uart0.common.hw, + [CLK_UART1] = &clk_uart1.common.hw, + [CLK_APB_UART1] = &clk_apb_uart1.common.hw, + [CLK_UART2] = &clk_uart2.common.hw, + [CLK_APB_UART2] = &clk_apb_uart2.common.hw, + [CLK_UART3] = &clk_uart3.common.hw, + [CLK_APB_UART3] = &clk_apb_uart3.common.hw, + [CLK_UART4] = &clk_uart4.common.hw, + [CLK_APB_UART4] = &clk_apb_uart4.common.hw, + [CLK_APB_I2S0] = &clk_apb_i2s0.common.hw, + [CLK_APB_I2S1] = &clk_apb_i2s1.common.hw, + [CLK_APB_I2S2] = &clk_apb_i2s2.common.hw, + [CLK_APB_I2S3] = &clk_apb_i2s3.common.hw, + [CLK_AXI4_USB] = &clk_axi4_usb.common.hw, + [CLK_APB_USB] = &clk_apb_usb.common.hw, + [CLK_USB_125M] = &clk_usb_125m.div.common.hw, + [CLK_USB_33K] = &clk_usb_33k.common.hw, + [CLK_USB_12M] = &clk_usb_12m.div.common.hw, + [CLK_AXI4] = &clk_axi4.mux.common.hw, + [CLK_AXI6] = &clk_axi6.div.common.hw, + [CLK_DSI_ESC] = &clk_dsi_esc.div.common.hw, + [CLK_AXI_VIP] = &clk_axi_vip.mux.common.hw, + [CLK_SRC_VIP_SYS_0] = &clk_src_vip_sys_0.mux.common.hw, + [CLK_SRC_VIP_SYS_1] = &clk_src_vip_sys_1.mux.common.hw, + [CLK_SRC_VIP_SYS_2] = &clk_src_vip_sys_2.mux.common.hw, + [CLK_SRC_VIP_SYS_3] = &clk_src_vip_sys_3.mux.common.hw, + [CLK_SRC_VIP_SYS_4] = &clk_src_vip_sys_4.mux.common.hw, + [CLK_CSI_BE_VIP] = &clk_csi_be_vip.common.hw, + [CLK_CSI_MAC0_VIP] = &clk_csi_mac0_vip.common.hw, + [CLK_CSI_MAC1_VIP] = &clk_csi_mac1_vip.common.hw, + [CLK_CSI_MAC2_VIP] = &clk_csi_mac2_vip.common.hw, + [CLK_CSI0_RX_VIP] = &clk_csi0_rx_vip.common.hw, + [CLK_CSI1_RX_VIP] = &clk_csi1_rx_vip.common.hw, + [CLK_ISP_TOP_VIP] = &clk_isp_top_vip.common.hw, + [CLK_IMG_D_VIP] = &clk_img_d_vip.common.hw, + [CLK_IMG_V_VIP] = &clk_img_v_vip.common.hw, + [CLK_SC_TOP_VIP] = &clk_sc_top_vip.common.hw, + [CLK_SC_D_VIP] = &clk_sc_d_vip.common.hw, + [CLK_SC_V1_VIP] = &clk_sc_v1_vip.common.hw, + [CLK_SC_V2_VIP] = &clk_sc_v2_vip.common.hw, + [CLK_SC_V3_VIP] = &clk_sc_v3_vip.common.hw, + [CLK_DWA_VIP] = &clk_dwa_vip.common.hw, + [CLK_BT_VIP] = &clk_bt_vip.common.hw, + [CLK_DISP_VIP] = &clk_disp_vip.common.hw, + [CLK_DSI_MAC_VIP] = &clk_dsi_mac_vip.common.hw, + [CLK_LVDS0_VIP] = &clk_lvds0_vip.common.hw, + [CLK_LVDS1_VIP] = &clk_lvds1_vip.common.hw, + [CLK_PAD_VI_VIP] = &clk_pad_vi_vip.common.hw, + [CLK_PAD_VI1_VIP] = &clk_pad_vi1_vip.common.hw, + [CLK_PAD_VI2_VIP] = &clk_pad_vi2_vip.common.hw, + [CLK_CFG_REG_VIP] = &clk_cfg_reg_vip.common.hw, + [CLK_VIP_IP0] = &clk_vip_ip0.common.hw, + [CLK_VIP_IP1] = &clk_vip_ip1.common.hw, + [CLK_VIP_IP2] = &clk_vip_ip2.common.hw, + [CLK_VIP_IP3] = &clk_vip_ip3.common.hw, + [CLK_IVE_VIP] = &clk_ive_vip.common.hw, + [CLK_RAW_VIP] = &clk_raw_vip.common.hw, + [CLK_OSDC_VIP] = &clk_osdc_vip.common.hw, + [CLK_CAM0_VIP] = &clk_cam0_vip.common.hw, + [CLK_AXI_VIDEO_CODEC] = &clk_axi_video_codec.mux.common.hw, + [CLK_VC_SRC0] = &clk_vc_src0.mux.common.hw, + [CLK_VC_SRC1] = &clk_vc_src1.div.common.hw, + [CLK_VC_SRC2] = &clk_vc_src2.div.common.hw, + [CLK_H264C] = &clk_h264c.common.hw, + [CLK_APB_H264C] = &clk_apb_h264c.common.hw, + [CLK_H265C] = &clk_h265c.common.hw, + [CLK_APB_H265C] = &clk_apb_h265c.common.hw, + [CLK_JPEG] = &clk_jpeg.common.hw, + [CLK_APB_JPEG] = &clk_apb_jpeg.common.hw, + [CLK_CAM0] = &clk_cam0.common.hw, + [CLK_CAM1] = &clk_cam1.common.hw, + [CLK_WGN] = &clk_wgn.common.hw, + [CLK_WGN0] = &clk_wgn0.common.hw, + [CLK_WGN1] = &clk_wgn1.common.hw, + [CLK_WGN2] = &clk_wgn2.common.hw, + [CLK_KEYSCAN] = &clk_keyscan.common.hw, + [CLK_CFG_REG_VC] = &clk_cfg_reg_vc.common.hw, + [CLK_C906_0] = &clk_c906_0.common.hw, + [CLK_C906_1] = &clk_c906_1.common.hw, + [CLK_A53] = &clk_a53.common.hw, + [CLK_CPU_AXI0] = &clk_cpu_axi0.div.common.hw, + [CLK_CPU_GIC] = &clk_cpu_gic.div.common.hw, + [CLK_XTAL_AP] = &clk_xtal_ap.common.hw, + [CLK_DISP_SRC_VIP] = &clk_disp_src_vip.div.common.hw, + }, +}; + +static int cv1810_pre_init(struct device *dev, void __iomem *base, + struct cv1800_clk_ctrl *ctrl, + const struct cv1800_clk_desc *desc) +{ + cv18xx_clk_disable_a53(base); + cv18xx_clk_disable_auto_pd(base); + + return 0; +} + +static const struct cv1800_clk_desc cv1810_desc = { + .clks_data = &cv1810_hw_clks, + .pre_init = cv1810_pre_init, +}; + +static int sg2000_pre_init(struct device *dev, void __iomem *base, + struct cv1800_clk_ctrl *ctrl, + const struct cv1800_clk_desc *desc) +{ + cv18xx_clk_disable_auto_pd(base); + + return 0; +} + +static const struct cv1800_clk_desc sg2000_desc = { + .clks_data = &cv1810_hw_clks, + .pre_init = sg2000_pre_init, +}; + +static int cv1800_clk_init_ctrl(struct device *dev, void __iomem *reg, + struct cv1800_clk_ctrl *ctrl, + const struct cv1800_clk_desc *desc) +{ + int i, ret; + + ctrl->desc = desc; + spin_lock_init(&ctrl->lock); + + for (i = 0; i < desc->clks_data->num; i++) { + struct clk_hw *hw = desc->clks_data->hws[i]; + struct cv1800_clk_common *common; + const char *name; + + if (!hw) + continue; + + name = hw->init->name; + + common = hw_to_cv1800_clk_common(hw); + common->base = reg; + common->lock = &ctrl->lock; + + ret = devm_clk_hw_register(dev, hw); + if (ret) { + dev_err(dev, "Couldn't register clock %d - %s\n", + i, name); + return ret; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + desc->clks_data); +} + +static int cv1800_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + void __iomem *reg; + int ret; + const struct cv1800_clk_desc *desc; + struct cv1800_clk_ctrl *ctrl; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + desc = device_get_match_data(dev); + if (!desc) { + dev_err(dev, "no match data for platform\n"); + return -EINVAL; + } + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + if (desc->pre_init) { + ret = desc->pre_init(dev, reg, ctrl, desc); + if (ret) + return ret; + } + + return cv1800_clk_init_ctrl(dev, reg, ctrl, desc); +} + +static const struct of_device_id cv1800_clk_ids[] = { + { .compatible = "sophgo,cv1800-clk", .data = &cv1800_desc }, + { .compatible = "sophgo,cv1800b-clk", .data = &cv1800_desc }, + { .compatible = "sophgo,cv1810-clk", .data = &cv1810_desc }, + { .compatible = "sophgo,cv1812h-clk", .data = &cv1810_desc }, + { .compatible = "sophgo,sg2000-clk", .data = &sg2000_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, cv1800_clk_ids); + +static struct platform_driver cv1800_clk_driver = { + .probe = cv1800_clk_probe, + .driver = { + .name = "cv1800-clk", + .suppress_bind_attrs = true, + .of_match_table = cv1800_clk_ids, + }, +}; +module_platform_driver(cv1800_clk_driver); +MODULE_DESCRIPTION("Sophgo CV1800 series SoCs clock controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/sophgo/clk-cv1800.h b/drivers/clk/sophgo/clk-cv1800.h new file mode 100644 index 000000000000..1e7107b5d05e --- /dev/null +++ b/drivers/clk/sophgo/clk-cv1800.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#ifndef _CLK_SOPHGO_CV1800_H_ +#define _CLK_SOPHGO_CV1800_H_ + +#include <dt-bindings/clock/sophgo,cv1800.h> + +#define CV1800_CLK_MAX (CLK_XTAL_AP + 1) +#define CV1810_CLK_MAX (CLK_DISP_SRC_VIP + 1) + +#define REG_PLL_G2_CTRL 0x800 +#define REG_PLL_G2_STATUS 0x804 +#define REG_MIPIMPLL_CSR 0x808 +#define REG_A0PLL_CSR 0x80C +#define REG_DISPPLL_CSR 0x810 +#define REG_CAM0PLL_CSR 0x814 +#define REG_CAM1PLL_CSR 0x818 +#define REG_PLL_G2_SSC_SYN_CTRL 0x840 +#define REG_A0PLL_SSC_SYN_CTRL 0x850 +#define REG_A0PLL_SSC_SYN_SET 0x854 +#define REG_A0PLL_SSC_SYN_SPAN 0x858 +#define REG_A0PLL_SSC_SYN_STEP 0x85C +#define REG_DISPPLL_SSC_SYN_CTRL 0x860 +#define REG_DISPPLL_SSC_SYN_SET 0x864 +#define REG_DISPPLL_SSC_SYN_SPAN 0x868 +#define REG_DISPPLL_SSC_SYN_STEP 0x86C +#define REG_CAM0PLL_SSC_SYN_CTRL 0x870 +#define REG_CAM0PLL_SSC_SYN_SET 0x874 +#define REG_CAM0PLL_SSC_SYN_SPAN 0x878 +#define REG_CAM0PLL_SSC_SYN_STEP 0x87C +#define REG_CAM1PLL_SSC_SYN_CTRL 0x880 +#define REG_CAM1PLL_SSC_SYN_SET 0x884 +#define REG_CAM1PLL_SSC_SYN_SPAN 0x888 +#define REG_CAM1PLL_SSC_SYN_STEP 0x88C +#define REG_APLL_FRAC_DIV_CTRL 0x890 +#define REG_APLL_FRAC_DIV_M 0x894 +#define REG_APLL_FRAC_DIV_N 0x898 +#define REG_MIPIMPLL_CLK_CSR 0x8A0 +#define REG_A0PLL_CLK_CSR 0x8A4 +#define REG_DISPPLL_CLK_CSR 0x8A8 +#define REG_CAM0PLL_CLK_CSR 0x8AC +#define REG_CAM1PLL_CLK_CSR 0x8B0 +#define REG_CLK_CAM0_SRC_DIV 0x8C0 +#define REG_CLK_CAM1_SRC_DIV 0x8C4 + +/* top_pll_g6 */ +#define REG_PLL_G6_CTRL 0x900 +#define REG_PLL_G6_STATUS 0x904 +#define REG_MPLL_CSR 0x908 +#define REG_TPLL_CSR 0x90C +#define REG_FPLL_CSR 0x910 +#define REG_PLL_G6_SSC_SYN_CTRL 0x940 +#define REG_DPLL_SSC_SYN_CTRL 0x950 +#define REG_DPLL_SSC_SYN_SET 0x954 +#define REG_DPLL_SSC_SYN_SPAN 0x958 +#define REG_DPLL_SSC_SYN_STEP 0x95C +#define REG_MPLL_SSC_SYN_CTRL 0x960 +#define REG_MPLL_SSC_SYN_SET 0x964 +#define REG_MPLL_SSC_SYN_SPAN 0x968 +#define REG_MPLL_SSC_SYN_STEP 0x96C +#define REG_TPLL_SSC_SYN_CTRL 0x970 +#define REG_TPLL_SSC_SYN_SET 0x974 +#define REG_TPLL_SSC_SYN_SPAN 0x978 +#define REG_TPLL_SSC_SYN_STEP 0x97C + +/* clkgen */ +#define REG_CLK_EN_0 0x000 +#define REG_CLK_EN_1 0x004 +#define REG_CLK_EN_2 0x008 +#define REG_CLK_EN_3 0x00C +#define REG_CLK_EN_4 0x010 +#define REG_CLK_SEL_0 0x020 +#define REG_CLK_BYP_0 0x030 +#define REG_CLK_BYP_1 0x034 + +#define REG_DIV_CLK_A53_0 0x040 +#define REG_DIV_CLK_A53_1 0x044 +#define REG_DIV_CLK_CPU_AXI0 0x048 +#define REG_DIV_CLK_CPU_GIC 0x050 +#define REG_DIV_CLK_TPU 0x054 +#define REG_DIV_CLK_EMMC 0x064 +#define REG_DIV_CLK_EMMC_100K 0x06C +#define REG_DIV_CLK_SD0 0x070 +#define REG_DIV_CLK_SD0_100K 0x078 +#define REG_DIV_CLK_SD1 0x07C +#define REG_DIV_CLK_SD1_100K 0x084 +#define REG_DIV_CLK_SPI_NAND 0x088 +#define REG_DIV_CLK_ETH0_500M 0x08C +#define REG_DIV_CLK_ETH1_500M 0x090 +#define REG_DIV_CLK_GPIO_DB 0x094 +#define REG_DIV_CLK_SDMA_AUD0 0x098 +#define REG_DIV_CLK_SDMA_AUD1 0x09C +#define REG_DIV_CLK_SDMA_AUD2 0x0A0 +#define REG_DIV_CLK_SDMA_AUD3 0x0A4 +#define REG_DIV_CLK_CAM0_200 0x0A8 +#define REG_DIV_CLK_AXI4 0x0B8 +#define REG_DIV_CLK_AXI6 0x0BC +#define REG_DIV_CLK_DSI_ESC 0x0C4 +#define REG_DIV_CLK_AXI_VIP 0x0C8 +#define REG_DIV_CLK_SRC_VIP_SYS_0 0x0D0 +#define REG_DIV_CLK_SRC_VIP_SYS_1 0x0D8 +#define REG_DIV_CLK_DISP_SRC_VIP 0x0E0 +#define REG_DIV_CLK_AXI_VIDEO_CODEC 0x0E4 +#define REG_DIV_CLK_VC_SRC0 0x0EC +#define REG_DIV_CLK_1M 0x0FC +#define REG_DIV_CLK_SPI 0x100 +#define REG_DIV_CLK_I2C 0x104 +#define REG_DIV_CLK_SRC_VIP_SYS_2 0x110 +#define REG_DIV_CLK_AUDSRC 0x118 +#define REG_DIV_CLK_PWM_SRC_0 0x120 +#define REG_DIV_CLK_AP_DEBUG 0x128 +#define REG_DIV_CLK_RTCSYS_SRC_0 0x12C +#define REG_DIV_CLK_C906_0_0 0x130 +#define REG_DIV_CLK_C906_0_1 0x134 +#define REG_DIV_CLK_C906_1_0 0x138 +#define REG_DIV_CLK_C906_1_1 0x13C +#define REG_DIV_CLK_SRC_VIP_SYS_3 0x140 +#define REG_DIV_CLK_SRC_VIP_SYS_4 0x144 + +#endif /* _CLK_SOPHGO_CV1800_H_ */ diff --git a/drivers/clk/sophgo/clk-cv18xx-common.c b/drivers/clk/sophgo/clk-cv18xx-common.c new file mode 100644 index 000000000000..cbcdd88f0e23 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-common.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/spinlock.h> +#include <linux/bug.h> + +#include "clk-cv18xx-common.h" + +int cv1800_clk_setbit(struct cv1800_clk_common *common, + struct cv1800_clk_regbit *field) +{ + u32 mask = BIT(field->shift); + u32 value; + unsigned long flags; + + spin_lock_irqsave(common->lock, flags); + + value = readl(common->base + field->reg); + writel(value | mask, common->base + field->reg); + + spin_unlock_irqrestore(common->lock, flags); + + return 0; +} + +int cv1800_clk_clearbit(struct cv1800_clk_common *common, + struct cv1800_clk_regbit *field) +{ + u32 mask = BIT(field->shift); + u32 value; + unsigned long flags; + + spin_lock_irqsave(common->lock, flags); + + value = readl(common->base + field->reg); + writel(value & ~mask, common->base + field->reg); + + spin_unlock_irqrestore(common->lock, flags); + + return 0; +} + +int cv1800_clk_checkbit(struct cv1800_clk_common *common, + struct cv1800_clk_regbit *field) +{ + return readl(common->base + field->reg) & BIT(field->shift); +} + +#define PLL_LOCK_TIMEOUT_US (200 * 1000) + +void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common, + u32 reg, u32 lock) +{ + void __iomem *addr = common->base + reg; + u32 regval; + + if (!lock) + return; + + WARN_ON(readl_relaxed_poll_timeout(addr, regval, regval & lock, + 100, PLL_LOCK_TIMEOUT_US)); +} diff --git a/drivers/clk/sophgo/clk-cv18xx-common.h b/drivers/clk/sophgo/clk-cv18xx-common.h new file mode 100644 index 000000000000..2bfda02b2064 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-common.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#ifndef _CLK_SOPHGO_CV18XX_IP_H_ +#define _CLK_SOPHGO_CV18XX_IP_H_ + +#include <linux/compiler.h> +#include <linux/clk-provider.h> +#include <linux/bitfield.h> + +struct cv1800_clk_common { + void __iomem *base; + spinlock_t *lock; + struct clk_hw hw; + unsigned long features; +}; + +#define CV1800_CLK_COMMON(_name, _parents, _op, _flags) \ + { \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, \ + _op, _flags), \ + } + +static inline struct cv1800_clk_common * +hw_to_cv1800_clk_common(struct clk_hw *hw) +{ + return container_of(hw, struct cv1800_clk_common, hw); +} + +struct cv1800_clk_regbit { + u16 reg; + s8 shift; +}; + +struct cv1800_clk_regfield { + u16 reg; + u8 shift; + u8 width; + s16 initval; + unsigned long flags; +}; + +#define CV1800_CLK_BIT(_reg, _shift) \ + { \ + .reg = _reg, \ + .shift = _shift, \ + } + +#define CV1800_CLK_REG(_reg, _shift, _width, _initval, _flags) \ + { \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .initval = _initval, \ + .flags = _flags, \ + } + +#define cv1800_clk_regfield_genmask(_reg) \ + GENMASK((_reg)->shift + (_reg)->width - 1, (_reg)->shift) +#define cv1800_clk_regfield_get(_val, _reg) \ + (((_val) >> (_reg)->shift) & GENMASK((_reg)->width - 1, 0)) +#define cv1800_clk_regfield_set(_val, _new, _reg) \ + (((_val) & ~cv1800_clk_regfield_genmask((_reg))) | \ + (((_new) & GENMASK((_reg)->width - 1, 0)) << (_reg)->shift)) + +#define _CV1800_SET_FIELD(_reg, _val, _field) \ + (((_reg) & ~(_field)) | FIELD_PREP((_field), (_val))) + +int cv1800_clk_setbit(struct cv1800_clk_common *common, + struct cv1800_clk_regbit *field); +int cv1800_clk_clearbit(struct cv1800_clk_common *common, + struct cv1800_clk_regbit *field); +int cv1800_clk_checkbit(struct cv1800_clk_common *common, + struct cv1800_clk_regbit *field); + +void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common, + u32 reg, u32 lock); + +#endif // _CLK_SOPHGO_CV18XX_IP_H_ diff --git a/drivers/clk/sophgo/clk-cv18xx-ip.c b/drivers/clk/sophgo/clk-cv18xx-ip.c new file mode 100644 index 000000000000..b186e64d4813 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-ip.c @@ -0,0 +1,887 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/gcd.h> +#include <linux/spinlock.h> + +#include "clk-cv18xx-ip.h" + +/* GATE */ +static inline struct cv1800_clk_gate *hw_to_cv1800_clk_gate(struct clk_hw *hw) +{ + struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); + + return container_of(common, struct cv1800_clk_gate, common); +} + +static int gate_enable(struct clk_hw *hw) +{ + struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw); + + return cv1800_clk_setbit(&gate->common, &gate->gate); +} + +static void gate_disable(struct clk_hw *hw) +{ + struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw); + + cv1800_clk_clearbit(&gate->common, &gate->gate); +} + +static int gate_is_enabled(struct clk_hw *hw) +{ + struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw); + + return cv1800_clk_checkbit(&gate->common, &gate->gate); +} + +static unsigned long gate_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate; +} + +static long gate_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return *parent_rate; +} + +static int gate_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + return 0; +} + +const struct clk_ops cv1800_clk_gate_ops = { + .disable = gate_disable, + .enable = gate_enable, + .is_enabled = gate_is_enabled, + + .recalc_rate = gate_recalc_rate, + .round_rate = gate_round_rate, + .set_rate = gate_set_rate, +}; + +/* DIV */ +#define _DIV_EN_CLK_DIV_FACTOR_FIELD BIT(3) + +#define DIV_GET_EN_CLK_DIV_FACTOR(_reg) \ + FIELD_GET(_DIV_EN_CLK_DIV_FACTOR_FIELD, _reg) + +#define DIV_SET_EN_DIV_FACTOR(_reg) \ + _CV1800_SET_FIELD(_reg, 1, _DIV_EN_CLK_DIV_FACTOR_FIELD) + +static inline struct cv1800_clk_div *hw_to_cv1800_clk_div(struct clk_hw *hw) +{ + struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); + + return container_of(common, struct cv1800_clk_div, common); +} + +static int div_enable(struct clk_hw *hw) +{ + struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); + + return cv1800_clk_setbit(&div->common, &div->gate); +} + +static void div_disable(struct clk_hw *hw) +{ + struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); + + cv1800_clk_clearbit(&div->common, &div->gate); +} + +static int div_is_enabled(struct clk_hw *hw) +{ + struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); + + return cv1800_clk_checkbit(&div->common, &div->gate); +} + +static int div_helper_set_rate(struct cv1800_clk_common *common, + struct cv1800_clk_regfield *div, + unsigned long val) +{ + unsigned long flags; + u32 reg; + + if (div->width == 0) + return 0; + + spin_lock_irqsave(common->lock, flags); + + reg = readl(common->base + div->reg); + reg = cv1800_clk_regfield_set(reg, val, div); + if (div->initval > 0) + reg = DIV_SET_EN_DIV_FACTOR(reg); + + writel(reg, common->base + div->reg); + + spin_unlock_irqrestore(common->lock, flags); + + return 0; +} + +static u32 div_helper_get_clockdiv(struct cv1800_clk_common *common, + struct cv1800_clk_regfield *div) +{ + u32 clockdiv = 1; + u32 reg; + + if (!div || div->initval < 0 || (div->width == 0 && div->initval <= 0)) + return 1; + + if (div->width == 0 && div->initval > 0) + return div->initval; + + reg = readl(common->base + div->reg); + + if (div->initval == 0 || DIV_GET_EN_CLK_DIV_FACTOR(reg)) + clockdiv = cv1800_clk_regfield_get(reg, div); + else if (div->initval > 0) + clockdiv = div->initval; + + return clockdiv; +} + +static u32 div_helper_round_rate(struct cv1800_clk_regfield *div, + struct clk_hw *hw, struct clk_hw *parent, + unsigned long rate, unsigned long *prate) +{ + if (div->width == 0) { + if (div->initval <= 0) + return DIV_ROUND_UP_ULL(*prate, 1); + else + return DIV_ROUND_UP_ULL(*prate, div->initval); + } + + return divider_round_rate_parent(hw, parent, rate, prate, NULL, + div->width, div->flags); +} + +static long div_round_rate(struct clk_hw *parent, unsigned long *parent_rate, + unsigned long rate, int id, void *data) +{ + struct cv1800_clk_div *div = data; + + return div_helper_round_rate(&div->div, &div->common.hw, parent, + rate, parent_rate); +} + +static bool div_is_better_rate(struct cv1800_clk_common *common, + unsigned long target, unsigned long now, + unsigned long best) +{ + if (common->features & CLK_DIVIDER_ROUND_CLOSEST) + return abs_diff(target, now) < abs_diff(target, best); + + return now <= target && now > best; +} + +static int mux_helper_determine_rate(struct cv1800_clk_common *common, + struct clk_rate_request *req, + long (*round)(struct clk_hw *, + unsigned long *, + unsigned long, + int, + void *), + void *data) +{ + unsigned long best_parent_rate = 0, best_rate = 0; + struct clk_hw *best_parent, *hw = &common->hw; + unsigned int i; + + if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) { + unsigned long adj_parent_rate; + + best_parent = clk_hw_get_parent(hw); + best_parent_rate = clk_hw_get_rate(best_parent); + + best_rate = round(best_parent, &adj_parent_rate, + req->rate, -1, data); + + goto find; + } + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + unsigned long tmp_rate, parent_rate; + struct clk_hw *parent; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + + tmp_rate = round(parent, &parent_rate, req->rate, i, data); + + if (tmp_rate == req->rate) { + best_parent = parent; + best_parent_rate = parent_rate; + best_rate = tmp_rate; + goto find; + } + + if (div_is_better_rate(common, req->rate, + tmp_rate, best_rate)) { + best_parent = parent; + best_parent_rate = parent_rate; + best_rate = tmp_rate; + } + } + + if (best_rate == 0) + return -EINVAL; + +find: + req->best_parent_hw = best_parent; + req->best_parent_rate = best_parent_rate; + req->rate = best_rate; + return 0; +} + +static int div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); + + return mux_helper_determine_rate(&div->common, req, + div_round_rate, div); +} + +static unsigned long div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); + unsigned long val; + + val = div_helper_get_clockdiv(&div->common, &div->div); + if (val == 0) + return 0; + + return divider_recalc_rate(hw, parent_rate, val, NULL, + div->div.flags, div->div.width); +} + +static int div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); + unsigned long val; + + val = divider_get_val(rate, parent_rate, NULL, + div->div.width, div->div.flags); + + return div_helper_set_rate(&div->common, &div->div, val); +} + +const struct clk_ops cv1800_clk_div_ops = { + .disable = div_disable, + .enable = div_enable, + .is_enabled = div_is_enabled, + + .determine_rate = div_determine_rate, + .recalc_rate = div_recalc_rate, + .set_rate = div_set_rate, +}; + +static inline struct cv1800_clk_bypass_div * +hw_to_cv1800_clk_bypass_div(struct clk_hw *hw) +{ + struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); + + return container_of(div, struct cv1800_clk_bypass_div, div); +} + +static long bypass_div_round_rate(struct clk_hw *parent, + unsigned long *parent_rate, + unsigned long rate, int id, void *data) +{ + struct cv1800_clk_bypass_div *div = data; + + if (id == -1) { + if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) + return *parent_rate; + else + return div_round_rate(parent, parent_rate, rate, + -1, &div->div); + } + + if (id == 0) + return *parent_rate; + + return div_round_rate(parent, parent_rate, rate, id - 1, &div->div); +} + +static int bypass_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); + + return mux_helper_determine_rate(&div->div.common, req, + bypass_div_round_rate, div); +} + +static unsigned long bypass_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); + + if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) + return parent_rate; + + return div_recalc_rate(hw, parent_rate); +} + +static int bypass_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); + + if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) + return 0; + + return div_set_rate(hw, rate, parent_rate); +} + +static u8 bypass_div_get_parent(struct clk_hw *hw) +{ + struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); + + if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) + return 0; + + return 1; +} + +static int bypass_div_set_parent(struct clk_hw *hw, u8 index) +{ + struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); + + if (index) + return cv1800_clk_clearbit(&div->div.common, &div->bypass); + + return cv1800_clk_setbit(&div->div.common, &div->bypass); +} + +const struct clk_ops cv1800_clk_bypass_div_ops = { + .disable = div_disable, + .enable = div_enable, + .is_enabled = div_is_enabled, + + .determine_rate = bypass_div_determine_rate, + .recalc_rate = bypass_div_recalc_rate, + .set_rate = bypass_div_set_rate, + + .set_parent = bypass_div_set_parent, + .get_parent = bypass_div_get_parent, +}; + +/* MUX */ +static inline struct cv1800_clk_mux *hw_to_cv1800_clk_mux(struct clk_hw *hw) +{ + struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); + + return container_of(common, struct cv1800_clk_mux, common); +} + +static int mux_enable(struct clk_hw *hw) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + + return cv1800_clk_setbit(&mux->common, &mux->gate); +} + +static void mux_disable(struct clk_hw *hw) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + + cv1800_clk_clearbit(&mux->common, &mux->gate); +} + +static int mux_is_enabled(struct clk_hw *hw) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + + return cv1800_clk_checkbit(&mux->common, &mux->gate); +} + +static long mux_round_rate(struct clk_hw *parent, unsigned long *parent_rate, + unsigned long rate, int id, void *data) +{ + struct cv1800_clk_mux *mux = data; + + return div_helper_round_rate(&mux->div, &mux->common.hw, parent, + rate, parent_rate); +} + +static int mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + + return mux_helper_determine_rate(&mux->common, req, + mux_round_rate, mux); +} + +static unsigned long mux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + unsigned long val; + + val = div_helper_get_clockdiv(&mux->common, &mux->div); + if (val == 0) + return 0; + + return divider_recalc_rate(hw, parent_rate, val, NULL, + mux->div.flags, mux->div.width); +} + +static int mux_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + unsigned long val; + + val = divider_get_val(rate, parent_rate, NULL, + mux->div.width, mux->div.flags); + + return div_helper_set_rate(&mux->common, &mux->div, val); +} + +static u8 mux_get_parent(struct clk_hw *hw) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + u32 reg = readl(mux->common.base + mux->mux.reg); + + return cv1800_clk_regfield_get(reg, &mux->mux); +} + +static int _mux_set_parent(struct cv1800_clk_mux *mux, u8 index) +{ + u32 reg; + + reg = readl(mux->common.base + mux->mux.reg); + reg = cv1800_clk_regfield_set(reg, index, &mux->mux); + writel(reg, mux->common.base + mux->mux.reg); + + return 0; +} + +static int mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + unsigned long flags; + + spin_lock_irqsave(mux->common.lock, flags); + + _mux_set_parent(mux, index); + + spin_unlock_irqrestore(mux->common.lock, flags); + + return 0; +} + +const struct clk_ops cv1800_clk_mux_ops = { + .disable = mux_disable, + .enable = mux_enable, + .is_enabled = mux_is_enabled, + + .determine_rate = mux_determine_rate, + .recalc_rate = mux_recalc_rate, + .set_rate = mux_set_rate, + + .set_parent = mux_set_parent, + .get_parent = mux_get_parent, +}; + +static inline struct cv1800_clk_bypass_mux * +hw_to_cv1800_clk_bypass_mux(struct clk_hw *hw) +{ + struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); + + return container_of(mux, struct cv1800_clk_bypass_mux, mux); +} + +static long bypass_mux_round_rate(struct clk_hw *parent, + unsigned long *parent_rate, + unsigned long rate, int id, void *data) +{ + struct cv1800_clk_bypass_mux *mux = data; + + if (id == -1) { + if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) + return *parent_rate; + else + return mux_round_rate(parent, parent_rate, rate, + -1, &mux->mux); + } + + if (id == 0) + return *parent_rate; + + return mux_round_rate(parent, parent_rate, rate, id - 1, &mux->mux); +} + +static int bypass_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); + + return mux_helper_determine_rate(&mux->mux.common, req, + bypass_mux_round_rate, mux); +} + +static unsigned long bypass_mux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); + + if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) + return parent_rate; + + return mux_recalc_rate(hw, parent_rate); +} + +static int bypass_mux_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); + + if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) + return 0; + + return mux_set_rate(hw, rate, parent_rate); +} + +static u8 bypass_mux_get_parent(struct clk_hw *hw) +{ + struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); + + if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) + return 0; + + return mux_get_parent(hw) + 1; +} + +static int bypass_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); + + if (index == 0) + return cv1800_clk_setbit(&mux->mux.common, &mux->bypass); + + return cv1800_clk_clearbit(&mux->mux.common, &mux->bypass); +} + +const struct clk_ops cv1800_clk_bypass_mux_ops = { + .disable = mux_disable, + .enable = mux_enable, + .is_enabled = mux_is_enabled, + + .determine_rate = bypass_mux_determine_rate, + .recalc_rate = bypass_mux_recalc_rate, + .set_rate = bypass_mux_set_rate, + + .set_parent = bypass_mux_set_parent, + .get_parent = bypass_mux_get_parent, +}; + +/* MMUX */ +static inline struct cv1800_clk_mmux *hw_to_cv1800_clk_mmux(struct clk_hw *hw) +{ + struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); + + return container_of(common, struct cv1800_clk_mmux, common); +} + +static u8 mmux_get_parent_id(struct cv1800_clk_mmux *mmux) +{ + struct clk_hw *hw = &mmux->common.hw; + struct clk_hw *parent = clk_hw_get_parent(hw); + unsigned int i; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + if (parent == clk_hw_get_parent_by_index(hw, i)) + return i; + } + + BUG(); +} + +static int mmux_enable(struct clk_hw *hw) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + + return cv1800_clk_setbit(&mmux->common, &mmux->gate); +} + +static void mmux_disable(struct clk_hw *hw) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + + cv1800_clk_clearbit(&mmux->common, &mmux->gate); +} + +static int mmux_is_enabled(struct clk_hw *hw) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + + return cv1800_clk_checkbit(&mmux->common, &mmux->gate); +} + +static long mmux_round_rate(struct clk_hw *parent, unsigned long *parent_rate, + unsigned long rate, int id, void *data) +{ + struct cv1800_clk_mmux *mmux = data; + s8 div_id; + + if (id == -1) { + if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) + return *parent_rate; + + id = mmux_get_parent_id(mmux); + } + + div_id = mmux->parent2sel[id]; + + if (div_id < 0) + return *parent_rate; + + return div_helper_round_rate(&mmux->div[div_id], + &mmux->common.hw, parent, + rate, parent_rate); +} + +static int mmux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + + return mux_helper_determine_rate(&mmux->common, req, + mmux_round_rate, mmux); +} + +static unsigned long mmux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + unsigned long val; + struct cv1800_clk_regfield *div; + + if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) + return parent_rate; + + if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel)) + div = &mmux->div[0]; + else + div = &mmux->div[1]; + + val = div_helper_get_clockdiv(&mmux->common, div); + if (val == 0) + return 0; + + return divider_recalc_rate(hw, parent_rate, val, NULL, + div->flags, div->width); +} + +static int mmux_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + struct cv1800_clk_regfield *div; + unsigned long val; + + if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) + return parent_rate; + + if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel)) + div = &mmux->div[0]; + else + div = &mmux->div[1]; + + val = divider_get_val(rate, parent_rate, NULL, + div->width, div->flags); + + return div_helper_set_rate(&mmux->common, div, val); +} + +static u8 mmux_get_parent(struct clk_hw *hw) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + struct cv1800_clk_regfield *mux; + u32 reg; + s8 clk_sel; + + if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) + return 0; + + if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel)) + clk_sel = 0; + else + clk_sel = 1; + mux = &mmux->mux[clk_sel]; + + reg = readl(mmux->common.base + mux->reg); + + return mmux->sel2parent[clk_sel][cv1800_clk_regfield_get(reg, mux)]; +} + +static int mmux_set_parent(struct clk_hw *hw, u8 index) +{ + struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); + struct cv1800_clk_regfield *mux; + unsigned long flags; + u32 reg; + s8 clk_sel = mmux->parent2sel[index]; + + if (index == 0 || clk_sel == -1) { + cv1800_clk_setbit(&mmux->common, &mmux->bypass); + goto release; + } + + cv1800_clk_clearbit(&mmux->common, &mmux->bypass); + + if (clk_sel) + cv1800_clk_clearbit(&mmux->common, &mmux->clk_sel); + else + cv1800_clk_setbit(&mmux->common, &mmux->clk_sel); + + spin_lock_irqsave(mmux->common.lock, flags); + + mux = &mmux->mux[clk_sel]; + reg = readl(mmux->common.base + mux->reg); + reg = cv1800_clk_regfield_set(reg, index, mux); + + writel(reg, mmux->common.base + mux->reg); + + spin_unlock_irqrestore(mmux->common.lock, flags); + +release: + return 0; +} + +const struct clk_ops cv1800_clk_mmux_ops = { + .disable = mmux_disable, + .enable = mmux_enable, + .is_enabled = mmux_is_enabled, + + .determine_rate = mmux_determine_rate, + .recalc_rate = mmux_recalc_rate, + .set_rate = mmux_set_rate, + + .set_parent = mmux_set_parent, + .get_parent = mmux_get_parent, +}; + +/* AUDIO CLK */ +static inline struct cv1800_clk_audio * +hw_to_cv1800_clk_audio(struct clk_hw *hw) +{ + struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); + + return container_of(common, struct cv1800_clk_audio, common); +} + +static int aclk_enable(struct clk_hw *hw) +{ + struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); + + cv1800_clk_setbit(&aclk->common, &aclk->src_en); + return cv1800_clk_setbit(&aclk->common, &aclk->output_en); +} + +static void aclk_disable(struct clk_hw *hw) +{ + struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); + + cv1800_clk_clearbit(&aclk->common, &aclk->output_en); + cv1800_clk_clearbit(&aclk->common, &aclk->src_en); +} + +static int aclk_is_enabled(struct clk_hw *hw) +{ + struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); + + return cv1800_clk_checkbit(&aclk->common, &aclk->output_en); +} + +static int aclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); + + req->rate = aclk->target_rate; + + return 0; +} + +static unsigned long aclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); + u64 rate = parent_rate; + u64 factor = 2; + u32 regval; + + if (!cv1800_clk_checkbit(&aclk->common, &aclk->div_en)) + return 0; + + regval = readl(aclk->common.base + aclk->m.reg); + factor *= cv1800_clk_regfield_get(regval, &aclk->m); + + regval = readl(aclk->common.base + aclk->n.reg); + rate *= cv1800_clk_regfield_get(regval, &aclk->n); + + return DIV64_U64_ROUND_UP(rate, factor); +} + +static void aclk_determine_mn(unsigned long parent_rate, unsigned long rate, + u32 *m, u32 *n) +{ + u32 tm = parent_rate / 2; + u32 tn = rate; + u32 tcommon = gcd(tm, tn); + *m = tm / tcommon; + *n = tn / tcommon; +} + +static int aclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); + unsigned long flags; + u32 m, n; + + aclk_determine_mn(parent_rate, rate, + &m, &n); + + spin_lock_irqsave(aclk->common.lock, flags); + + writel(m, aclk->common.base + aclk->m.reg); + writel(n, aclk->common.base + aclk->n.reg); + + cv1800_clk_setbit(&aclk->common, &aclk->div_en); + cv1800_clk_setbit(&aclk->common, &aclk->div_up); + + spin_unlock_irqrestore(aclk->common.lock, flags); + + return 0; +} + +const struct clk_ops cv1800_clk_audio_ops = { + .disable = aclk_disable, + .enable = aclk_enable, + .is_enabled = aclk_is_enabled, + + .determine_rate = aclk_determine_rate, + .recalc_rate = aclk_recalc_rate, + .set_rate = aclk_set_rate, +}; diff --git a/drivers/clk/sophgo/clk-cv18xx-ip.h b/drivers/clk/sophgo/clk-cv18xx-ip.h new file mode 100644 index 000000000000..b37ba42bfde3 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-ip.h @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#ifndef _CLK_SOPHGO_CV1800_IP_H_ +#define _CLK_SOPHGO_CV1800_IP_H_ + +#include "clk-cv18xx-common.h" + +struct cv1800_clk_gate { + struct cv1800_clk_common common; + struct cv1800_clk_regbit gate; +}; + +struct cv1800_clk_div_data { + u32 reg; + u32 mask; + u32 width; + u32 init; + u32 flags; +}; + +struct cv1800_clk_div { + struct cv1800_clk_common common; + struct cv1800_clk_regbit gate; + struct cv1800_clk_regfield div; +}; + +struct cv1800_clk_bypass_div { + struct cv1800_clk_div div; + struct cv1800_clk_regbit bypass; +}; + +struct cv1800_clk_mux { + struct cv1800_clk_common common; + struct cv1800_clk_regbit gate; + struct cv1800_clk_regfield div; + struct cv1800_clk_regfield mux; +}; + +struct cv1800_clk_bypass_mux { + struct cv1800_clk_mux mux; + struct cv1800_clk_regbit bypass; +}; + +struct cv1800_clk_mmux { + struct cv1800_clk_common common; + struct cv1800_clk_regbit gate; + struct cv1800_clk_regfield div[2]; + struct cv1800_clk_regfield mux[2]; + struct cv1800_clk_regbit bypass; + struct cv1800_clk_regbit clk_sel; + const s8 *parent2sel; + const u8 *sel2parent[2]; +}; + +struct cv1800_clk_audio { + struct cv1800_clk_common common; + struct cv1800_clk_regbit src_en; + struct cv1800_clk_regbit output_en; + struct cv1800_clk_regbit div_en; + struct cv1800_clk_regbit div_up; + struct cv1800_clk_regfield m; + struct cv1800_clk_regfield n; + u32 target_rate; +}; + +#define CV1800_GATE(_name, _parent, _gate_reg, _gate_shift, _flags) \ + struct cv1800_clk_gate _name = { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + &cv1800_clk_gate_ops, \ + _flags), \ + .gate = CV1800_CLK_BIT(_gate_reg, _gate_shift), \ + } + +#define _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init, \ + _div_flag, _ops, _flags) \ + { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + _ops, _flags), \ + .gate = CV1800_CLK_BIT(_gate_reg, \ + _gate_shift), \ + .div = CV1800_CLK_REG(_div_reg, _div_shift, \ + _div_width, _div_init, \ + _div_flag), \ + } + +#define _CV1800_FIXED_DIV_FLAG \ + (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST) + +#define _CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \ + _fix_div, _ops, _flags) \ + { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + _ops, _flags), \ + .gate = CV1800_CLK_BIT(_gate_reg, \ + _gate_shift), \ + .div = CV1800_CLK_REG(0, 0, 0, \ + _fix_div, \ + _CV1800_FIXED_DIV_FLAG),\ + } + +#define CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init, \ + _div_flag, _flags) \ + struct cv1800_clk_div _name = \ + _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init,\ + _div_flag, &cv1800_clk_div_ops, _flags) + +#define CV1800_BYPASS_DIV(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init, \ + _div_flag, _bypass_reg, _bypass_shift, _flags)\ + struct cv1800_clk_bypass_div _name = { \ + .div = _CV1800_DIV(_name, _parent, \ + _gate_reg, _gate_shift, \ + _div_reg, _div_shift, \ + _div_width, _div_init, _div_flag, \ + &cv1800_clk_bypass_div_ops, \ + _flags), \ + .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \ + } + +#define CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \ + _fix_div, _flags) \ + struct cv1800_clk_div _name = \ + _CV1800_FIXED_DIV(_name, _parent, \ + _gate_reg, _gate_shift, \ + _fix_div, \ + &cv1800_clk_div_ops, _flags) \ + +#define CV1800_BYPASS_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \ + _fix_div, _bypass_reg, _bypass_shift, \ + _flags) \ + struct cv1800_clk_bypass_div _name = { \ + .div = _CV1800_FIXED_DIV(_name, _parent, \ + _gate_reg, _gate_shift, \ + _fix_div, \ + &cv1800_clk_bypass_div_ops, \ + _flags), \ + .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \ + } + +#define _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init, \ + _div_flag, \ + _mux_reg, _mux_shift, _mux_width, \ + _ops, _flags) \ + { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + _ops, _flags), \ + .gate = CV1800_CLK_BIT(_gate_reg, \ + _gate_shift), \ + .div = CV1800_CLK_REG(_div_reg, _div_shift, \ + _div_width, _div_init, \ + _div_flag), \ + .mux = CV1800_CLK_REG(_mux_reg, _mux_shift, \ + _mux_width, 0, 0), \ + } + +#define CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init, \ + _div_flag, \ + _mux_reg, _mux_shift, _mux_width, _flags) \ + struct cv1800_clk_mux _name = \ + _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init,\ + _div_flag, _mux_reg, _mux_shift, _mux_width,\ + &cv1800_clk_mux_ops, _flags) + +#define CV1800_BYPASS_MUX(_name, _parent, _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, _div_init, \ + _div_flag, \ + _mux_reg, _mux_shift, _mux_width, \ + _bypass_reg, _bypass_shift, _flags) \ + struct cv1800_clk_bypass_mux _name = { \ + .mux = _CV1800_MUX(_name, _parent, \ + _gate_reg, _gate_shift, \ + _div_reg, _div_shift, _div_width, \ + _div_init, _div_flag, \ + _mux_reg, _mux_shift, _mux_width, \ + &cv1800_clk_bypass_mux_ops, \ + _flags), \ + .bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \ + } + +#define CV1800_MMUX(_name, _parent, _gate_reg, _gate_shift, \ + _div0_reg, _div0_shift, _div0_width, _div0_init, \ + _div0_flag, \ + _div1_reg, _div1_shift, _div1_width, _div1_init, \ + _div1_flag, \ + _mux0_reg, _mux0_shift, _mux0_width, \ + _mux1_reg, _mux1_shift, _mux1_width, \ + _bypass_reg, _bypass_shift, \ + _clk_sel_reg, _clk_sel_shift, \ + _parent2sel, _sel2parent0, _sel2parent1, _flags) \ + struct cv1800_clk_mmux _name = { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + &cv1800_clk_mmux_ops,\ + _flags), \ + .gate = CV1800_CLK_BIT(_gate_reg, _gate_shift),\ + .div = { \ + CV1800_CLK_REG(_div0_reg, _div0_shift, \ + _div0_width, _div0_init, \ + _div0_flag), \ + CV1800_CLK_REG(_div1_reg, _div1_shift, \ + _div1_width, _div1_init, \ + _div1_flag), \ + }, \ + .mux = { \ + CV1800_CLK_REG(_mux0_reg, _mux0_shift, \ + _mux0_width, 0, 0), \ + CV1800_CLK_REG(_mux1_reg, _mux1_shift, \ + _mux1_width, 0, 0), \ + }, \ + .bypass = CV1800_CLK_BIT(_bypass_reg, \ + _bypass_shift), \ + .clk_sel = CV1800_CLK_BIT(_clk_sel_reg, \ + _clk_sel_shift), \ + .parent2sel = _parent2sel, \ + .sel2parent = { _sel2parent0, _sel2parent1 }, \ + } + +#define CV1800_ACLK(_name, _parent, \ + _src_en_reg, _src_en_reg_shift, \ + _output_en_reg, _output_en_shift, \ + _div_en_reg, _div_en_reg_shift, \ + _div_up_reg, _div_up_reg_shift, \ + _m_reg, _m_shift, _m_width, _m_flag, \ + _n_reg, _n_shift, _n_width, _n_flag, \ + _target_rate, _flags) \ + struct cv1800_clk_audio _name = { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + &cv1800_clk_audio_ops,\ + _flags), \ + .src_en = CV1800_CLK_BIT(_src_en_reg, \ + _src_en_reg_shift), \ + .output_en = CV1800_CLK_BIT(_output_en_reg, \ + _output_en_shift), \ + .div_en = CV1800_CLK_BIT(_div_en_reg, \ + _div_en_reg_shift), \ + .div_up = CV1800_CLK_BIT(_div_up_reg, \ + _div_up_reg_shift), \ + .m = CV1800_CLK_REG(_m_reg, _m_shift, \ + _m_width, 0, _m_flag), \ + .n = CV1800_CLK_REG(_n_reg, _n_shift, \ + _n_width, 0, _n_flag), \ + .target_rate = _target_rate, \ + } + +extern const struct clk_ops cv1800_clk_gate_ops; +extern const struct clk_ops cv1800_clk_div_ops; +extern const struct clk_ops cv1800_clk_bypass_div_ops; +extern const struct clk_ops cv1800_clk_mux_ops; +extern const struct clk_ops cv1800_clk_bypass_mux_ops; +extern const struct clk_ops cv1800_clk_mmux_ops; +extern const struct clk_ops cv1800_clk_audio_ops; + +#endif // _CLK_SOPHGO_CV1800_IP_H_ diff --git a/drivers/clk/sophgo/clk-cv18xx-pll.c b/drivers/clk/sophgo/clk-cv18xx-pll.c new file mode 100644 index 000000000000..29e24098bf5f --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-pll.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/limits.h> +#include <linux/spinlock.h> + +#include "clk-cv18xx-pll.h" + +static inline struct cv1800_clk_pll *hw_to_cv1800_clk_pll(struct clk_hw *hw) +{ + struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); + + return container_of(common, struct cv1800_clk_pll, common); +} + +static unsigned long ipll_calc_rate(unsigned long parent_rate, + unsigned long pre_div_sel, + unsigned long div_sel, + unsigned long post_div_sel) +{ + uint64_t rate = parent_rate; + + rate *= div_sel; + do_div(rate, pre_div_sel * post_div_sel); + + return rate; +} + +static unsigned long ipll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + u32 value; + + value = readl(pll->common.base + pll->pll_reg); + + return ipll_calc_rate(parent_rate, + PLL_GET_PRE_DIV_SEL(value), + PLL_GET_DIV_SEL(value), + PLL_GET_POST_DIV_SEL(value)); +} + +static int ipll_find_rate(const struct cv1800_clk_pll_limit *limit, + unsigned long prate, unsigned long *rate, + u32 *value) +{ + unsigned long best_rate = 0; + unsigned long trate = *rate; + unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0; + unsigned long pre, div, post; + u32 detected = *value; + unsigned long tmp; + + for_each_pll_limit_range(pre, &limit->pre_div) { + for_each_pll_limit_range(div, &limit->div) { + for_each_pll_limit_range(post, &limit->post_div) { + tmp = ipll_calc_rate(prate, pre, div, post); + + if (tmp > trate) + continue; + + if ((trate - tmp) < (trate - best_rate)) { + best_rate = tmp; + pre_div_sel = pre; + div_sel = div; + post_div_sel = post; + } + } + } + } + + if (best_rate) { + detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel); + detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel); + detected = PLL_SET_DIV_SEL(detected, div_sel); + *value = detected; + *rate = best_rate; + return 0; + } + + return -EINVAL; +} + +static int ipll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + u32 val; + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + return ipll_find_rate(pll->pll_limit, req->best_parent_rate, + &req->rate, &val); +} + +static void pll_get_mode_ctrl(unsigned long div_sel, + bool (*mode_ctrl_check)(unsigned long, + unsigned long, + unsigned long), + const struct cv1800_clk_pll_limit *limit, + u32 *value) +{ + unsigned long ictrl = 0, mode = 0; + u32 detected = *value; + + for_each_pll_limit_range(mode, &limit->mode) { + for_each_pll_limit_range(ictrl, &limit->ictrl) { + if (mode_ctrl_check(div_sel, ictrl, mode)) { + detected = PLL_SET_SEL_MODE(detected, mode); + detected = PLL_SET_ICTRL(detected, ictrl); + *value = detected; + return; + } + } + } +} + +static bool ipll_check_mode_ctrl_restrict(unsigned long div_sel, + unsigned long ictrl, + unsigned long mode) +{ + unsigned long left_rest = 20 * div_sel; + unsigned long right_rest = 35 * div_sel; + unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2; + + return test > left_rest && test <= right_rest; +} + +static int ipll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 regval, detected = 0; + unsigned long flags; + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + ipll_find_rate(pll->pll_limit, parent_rate, &rate, &detected); + pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected), + ipll_check_mode_ctrl_restrict, + pll->pll_limit, &detected); + + spin_lock_irqsave(pll->common.lock, flags); + + regval = readl(pll->common.base + pll->pll_reg); + regval = PLL_COPY_REG(regval, detected); + + writel(regval, pll->common.base + pll->pll_reg); + + spin_unlock_irqrestore(pll->common.lock, flags); + + cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg, + BIT(pll->pll_status.shift)); + + return 0; +} + +static int pll_enable(struct clk_hw *hw) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + return cv1800_clk_clearbit(&pll->common, &pll->pll_pwd); +} + +static void pll_disable(struct clk_hw *hw) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + cv1800_clk_setbit(&pll->common, &pll->pll_pwd); +} + +static int pll_is_enable(struct clk_hw *hw) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + return cv1800_clk_checkbit(&pll->common, &pll->pll_pwd) == 0; +} + +const struct clk_ops cv1800_clk_ipll_ops = { + .disable = pll_disable, + .enable = pll_enable, + .is_enabled = pll_is_enable, + + .recalc_rate = ipll_recalc_rate, + .determine_rate = ipll_determine_rate, + .set_rate = ipll_set_rate, +}; + +#define PLL_SYN_FACTOR_DOT_POS 26 +#define PLL_SYN_FACTOR_MINIMUM ((4 << PLL_SYN_FACTOR_DOT_POS) + 1) + +static bool fpll_is_factional_mode(struct cv1800_clk_pll *pll) +{ + return cv1800_clk_checkbit(&pll->common, &pll->pll_syn->en); +} + +static unsigned long fpll_calc_rate(unsigned long parent_rate, + unsigned long pre_div_sel, + unsigned long div_sel, + unsigned long post_div_sel, + unsigned long ssc_syn_set, + bool is_full_parent) +{ + u64 dividend = parent_rate * div_sel; + u64 factor = ssc_syn_set * pre_div_sel * post_div_sel; + unsigned long rate; + + dividend <<= PLL_SYN_FACTOR_DOT_POS - 1; + rate = div64_u64_rem(dividend, factor, ÷nd); + + if (is_full_parent) { + dividend <<= 1; + rate <<= 1; + } + + rate += DIV64_U64_ROUND_CLOSEST(dividend, factor); + + return rate; +} + +static unsigned long fpll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + u32 value; + bool clk_full; + u32 syn_set; + + if (!fpll_is_factional_mode(pll)) + return ipll_recalc_rate(hw, parent_rate); + + syn_set = readl(pll->common.base + pll->pll_syn->set); + + if (syn_set == 0) + return 0; + + clk_full = cv1800_clk_checkbit(&pll->common, + &pll->pll_syn->clk_half); + + value = readl(pll->common.base + pll->pll_reg); + + return fpll_calc_rate(parent_rate, + PLL_GET_PRE_DIV_SEL(value), + PLL_GET_DIV_SEL(value), + PLL_GET_POST_DIV_SEL(value), + syn_set, clk_full); +} + +static unsigned long fpll_find_synthesizer(unsigned long parent, + unsigned long rate, + unsigned long pre_div, + unsigned long div, + unsigned long post_div, + bool is_full_parent, + u32 *ssc_syn_set) +{ + u32 test_max = U32_MAX, test_min = PLL_SYN_FACTOR_MINIMUM; + unsigned long trate; + + while (test_min < test_max) { + u32 tssc = (test_max + test_min) / 2; + + trate = fpll_calc_rate(parent, pre_div, div, post_div, + tssc, is_full_parent); + + if (trate == rate) { + test_min = tssc; + break; + } + + if (trate > rate) + test_min = tssc + 1; + else + test_max = tssc - 1; + } + + if (trate != 0) + *ssc_syn_set = test_min; + + return trate; +} + +static int fpll_find_rate(struct cv1800_clk_pll *pll, + const struct cv1800_clk_pll_limit *limit, + unsigned long prate, + unsigned long *rate, + u32 *value, u32 *ssc_syn_set) +{ + unsigned long best_rate = 0; + unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0; + unsigned long pre, div, post; + unsigned long trate = *rate; + u32 detected = *value; + unsigned long tmp; + bool clk_full = cv1800_clk_checkbit(&pll->common, + &pll->pll_syn->clk_half); + + for_each_pll_limit_range(pre, &limit->pre_div) { + for_each_pll_limit_range(post, &limit->post_div) { + for_each_pll_limit_range(div, &limit->div) { + tmp = fpll_find_synthesizer(prate, trate, + pre, div, post, + clk_full, + ssc_syn_set); + + if ((trate - tmp) < (trate - best_rate)) { + best_rate = tmp; + pre_div_sel = pre; + div_sel = div; + post_div_sel = post; + } + } + } + } + + if (best_rate) { + detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel); + detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel); + detected = PLL_SET_DIV_SEL(detected, div_sel); + *value = detected; + *rate = best_rate; + return 0; + } + + return -EINVAL; +} + +static int fpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + u32 val, ssc_syn_set; + + if (!fpll_is_factional_mode(pll)) + return ipll_determine_rate(hw, req); + + fpll_find_rate(pll, &pll->pll_limit[2], req->best_parent_rate, + &req->rate, &val, &ssc_syn_set); + + return 0; +} + +static bool fpll_check_mode_ctrl_restrict(unsigned long div_sel, + unsigned long ictrl, + unsigned long mode) +{ + unsigned long left_rest = 10 * div_sel; + unsigned long right_rest = 24 * div_sel; + unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2; + + return test > left_rest && test <= right_rest; +} + +static int fpll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 regval; + u32 detected = 0, detected_ssc = 0; + unsigned long flags; + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + if (!fpll_is_factional_mode(pll)) + return ipll_set_rate(hw, rate, parent_rate); + + fpll_find_rate(pll, &pll->pll_limit[2], parent_rate, + &rate, &detected, &detected_ssc); + pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected), + fpll_check_mode_ctrl_restrict, + pll->pll_limit, &detected); + + spin_lock_irqsave(pll->common.lock, flags); + + writel(detected_ssc, pll->common.base + pll->pll_syn->set); + + regval = readl(pll->common.base + pll->pll_reg); + regval = PLL_COPY_REG(regval, detected); + + writel(regval, pll->common.base + pll->pll_reg); + + spin_unlock_irqrestore(pll->common.lock, flags); + + cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg, + BIT(pll->pll_status.shift)); + + return 0; +} + +static u8 fpll_get_parent(struct clk_hw *hw) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + if (fpll_is_factional_mode(pll)) + return 1; + + return 0; +} + +static int fpll_set_parent(struct clk_hw *hw, u8 index) +{ + struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw); + + if (index) + cv1800_clk_setbit(&pll->common, &pll->pll_syn->en); + else + cv1800_clk_clearbit(&pll->common, &pll->pll_syn->en); + + return 0; +} + +const struct clk_ops cv1800_clk_fpll_ops = { + .disable = pll_disable, + .enable = pll_enable, + .is_enabled = pll_is_enable, + + .recalc_rate = fpll_recalc_rate, + .determine_rate = fpll_determine_rate, + .set_rate = fpll_set_rate, + + .set_parent = fpll_set_parent, + .get_parent = fpll_get_parent, +}; diff --git a/drivers/clk/sophgo/clk-cv18xx-pll.h b/drivers/clk/sophgo/clk-cv18xx-pll.h new file mode 100644 index 000000000000..7a33f3da2d64 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-pll.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> + */ + +#ifndef _CLK_SOPHGO_CV1800_PLL_H_ +#define _CLK_SOPHGO_CV1800_PLL_H_ + +#include "clk-cv18xx-common.h" + +struct cv1800_clk_pll_limit { + struct { + u8 min; + u8 max; + } pre_div, div, post_div, ictrl, mode; +}; + +#define _CV1800_PLL_LIMIT(_min, _max) \ + { \ + .min = _min, \ + .max = _max, \ + } \ + +#define for_each_pll_limit_range(_var, _restrict) \ + for (_var = (_restrict)->min; _var <= (_restrict)->max; _var++) + +struct cv1800_clk_pll_synthesizer { + struct cv1800_clk_regbit en; + struct cv1800_clk_regbit clk_half; + u32 ctrl; + u32 set; +}; + +#define _PLL_PRE_DIV_SEL_FIELD GENMASK(6, 0) +#define _PLL_POST_DIV_SEL_FIELD GENMASK(14, 8) +#define _PLL_SEL_MODE_FIELD GENMASK(16, 15) +#define _PLL_DIV_SEL_FIELD GENMASK(23, 17) +#define _PLL_ICTRL_FIELD GENMASK(26, 24) + +#define _PLL_ALL_FIELD_MASK \ + (_PLL_PRE_DIV_SEL_FIELD | \ + _PLL_POST_DIV_SEL_FIELD | \ + _PLL_SEL_MODE_FIELD | \ + _PLL_DIV_SEL_FIELD | \ + _PLL_ICTRL_FIELD) + +#define PLL_COPY_REG(_dest, _src) \ + (((_dest) & (~_PLL_ALL_FIELD_MASK)) | ((_src) & _PLL_ALL_FIELD_MASK)) + +#define PLL_GET_PRE_DIV_SEL(_reg) \ + FIELD_GET(_PLL_PRE_DIV_SEL_FIELD, (_reg)) +#define PLL_GET_POST_DIV_SEL(_reg) \ + FIELD_GET(_PLL_POST_DIV_SEL_FIELD, (_reg)) +#define PLL_GET_SEL_MODE(_reg) \ + FIELD_GET(_PLL_SEL_MODE_FIELD, (_reg)) +#define PLL_GET_DIV_SEL(_reg) \ + FIELD_GET(_PLL_DIV_SEL_FIELD, (_reg)) +#define PLL_GET_ICTRL(_reg) \ + FIELD_GET(_PLL_ICTRL_FIELD, (_reg)) + +#define PLL_SET_PRE_DIV_SEL(_reg, _val) \ + _CV1800_SET_FIELD((_reg), (_val), _PLL_PRE_DIV_SEL_FIELD) +#define PLL_SET_POST_DIV_SEL(_reg, _val) \ + _CV1800_SET_FIELD((_reg), (_val), _PLL_POST_DIV_SEL_FIELD) +#define PLL_SET_SEL_MODE(_reg, _val) \ + _CV1800_SET_FIELD((_reg), (_val), _PLL_SEL_MODE_FIELD) +#define PLL_SET_DIV_SEL(_reg, _val) \ + _CV1800_SET_FIELD((_reg), (_val), _PLL_DIV_SEL_FIELD) +#define PLL_SET_ICTRL(_reg, _val) \ + _CV1800_SET_FIELD((_reg), (_val), _PLL_ICTRL_FIELD) + +struct cv1800_clk_pll { + struct cv1800_clk_common common; + u32 pll_reg; + struct cv1800_clk_regbit pll_pwd; + struct cv1800_clk_regbit pll_status; + const struct cv1800_clk_pll_limit *pll_limit; + struct cv1800_clk_pll_synthesizer *pll_syn; +}; + +#define CV1800_INTEGRAL_PLL(_name, _parent, _pll_reg, \ + _pll_pwd_reg, _pll_pwd_shift, \ + _pll_status_reg, _pll_status_shift, \ + _pll_limit, _flags) \ + struct cv1800_clk_pll _name = { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + &cv1800_clk_ipll_ops,\ + _flags), \ + .pll_reg = _pll_reg, \ + .pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \ + _pll_pwd_shift), \ + .pll_status = CV1800_CLK_BIT(_pll_status_reg, \ + _pll_status_shift), \ + .pll_limit = _pll_limit, \ + .pll_syn = NULL, \ + } + +#define CV1800_FACTIONAL_PLL(_name, _parent, _pll_reg, \ + _pll_pwd_reg, _pll_pwd_shift, \ + _pll_status_reg, _pll_status_shift, \ + _pll_limit, _pll_syn, _flags) \ + struct cv1800_clk_pll _name = { \ + .common = CV1800_CLK_COMMON(#_name, _parent, \ + &cv1800_clk_fpll_ops,\ + _flags), \ + .pll_reg = _pll_reg, \ + .pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \ + _pll_pwd_shift), \ + .pll_status = CV1800_CLK_BIT(_pll_status_reg, \ + _pll_status_shift), \ + .pll_limit = _pll_limit, \ + .pll_syn = _pll_syn, \ + } + +extern const struct clk_ops cv1800_clk_ipll_ops; +extern const struct clk_ops cv1800_clk_fpll_ops; + +#endif // _CLK_SOPHGO_CV1800_PLL_H_ diff --git a/drivers/clk/sophgo/clk-sg2042-clkgen.c b/drivers/clk/sophgo/clk-sg2042-clkgen.c new file mode 100644 index 000000000000..a334963e83ce --- /dev/null +++ b/drivers/clk/sophgo/clk-sg2042-clkgen.c @@ -0,0 +1,1152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sophgo SG2042 Clock Generator Driver + * + * Copyright (C) 2024 Sophgo Technology Inc. + * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com> + */ + +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <asm/div64.h> + +#include <dt-bindings/clock/sophgo,sg2042-clkgen.h> + +#include "clk-sg2042.h" + +/* Registers defined in SYS_CTRL */ +#define R_PLL_BEGIN 0xC0 +#define R_PLL_STAT (0xC0 - R_PLL_BEGIN) +#define R_PLL_CLKEN_CONTROL (0xC4 - R_PLL_BEGIN) +#define R_MPLL_CONTROL (0xE8 - R_PLL_BEGIN) +#define R_FPLL_CONTROL (0xF4 - R_PLL_BEGIN) +#define R_DPLL0_CONTROL (0xF8 - R_PLL_BEGIN) +#define R_DPLL1_CONTROL (0xFC - R_PLL_BEGIN) + +/* Registers defined in CLOCK */ +#define R_CLKENREG0 0x00 +#define R_CLKENREG1 0x04 +#define R_CLKSELREG0 0x20 +#define R_CLKDIVREG0 0x40 +#define R_CLKDIVREG1 0x44 +#define R_CLKDIVREG2 0x48 +#define R_CLKDIVREG3 0x4C +#define R_CLKDIVREG4 0x50 +#define R_CLKDIVREG5 0x54 +#define R_CLKDIVREG6 0x58 +#define R_CLKDIVREG7 0x5C +#define R_CLKDIVREG8 0x60 +#define R_CLKDIVREG9 0x64 +#define R_CLKDIVREG10 0x68 +#define R_CLKDIVREG11 0x6C +#define R_CLKDIVREG12 0x70 +#define R_CLKDIVREG13 0x74 +#define R_CLKDIVREG14 0x78 +#define R_CLKDIVREG15 0x7C +#define R_CLKDIVREG16 0x80 +#define R_CLKDIVREG17 0x84 +#define R_CLKDIVREG18 0x88 +#define R_CLKDIVREG19 0x8C +#define R_CLKDIVREG20 0x90 +#define R_CLKDIVREG21 0x94 +#define R_CLKDIVREG22 0x98 +#define R_CLKDIVREG23 0x9C +#define R_CLKDIVREG24 0xA0 +#define R_CLKDIVREG25 0xA4 +#define R_CLKDIVREG26 0xA8 +#define R_CLKDIVREG27 0xAC +#define R_CLKDIVREG28 0xB0 +#define R_CLKDIVREG29 0xB4 +#define R_CLKDIVREG30 0xB8 + +/* All following shift value are the same for all DIV registers */ +#define SHIFT_DIV_RESET_CTRL 0 +#define SHIFT_DIV_FACTOR_SEL 3 +#define SHIFT_DIV_FACTOR 16 + +/** + * struct sg2042_divider_clock - Divider clock + * @hw: clk_hw for initialization + * @id: used to map clk_onecell_data + * @reg: used for readl/writel. + * **NOTE**: DIV registers are ALL in CLOCK! + * @lock: spinlock to protect register access, modification of + * frequency can only be served one at the time + * @offset_ctrl: offset of divider control registers + * @shift: shift of "Clock Divider Factor" in divider control register + * @width: width of "Clock Divider Factor" in divider control register + * @div_flags: private flags for this clock, not for framework-specific + * @initval: In the divider control register, we can configure whether + * to use the value of "Clock Divider Factor" or just use + * the initial value pre-configured by IC. BIT[3] controls + * this and by default (value is 0), means initial value + * is used. + * **NOTE** that we cannot read the initial value (default + * value when poweron) and default value of "Clock Divider + * Factor" is zero, which I think is a hardware design flaw + * and should be sync-ed with the initial value. So in + * software we have to add a configuration item (initval) + * to manually configure this value and use it when BIT[3] + * is zero. + */ +struct sg2042_divider_clock { + struct clk_hw hw; + + unsigned int id; + + void __iomem *reg; + /* protect register access */ + spinlock_t *lock; + + u32 offset_ctrl; + u8 shift; + u8 width; + u8 div_flags; + u32 initval; +}; + +#define to_sg2042_clk_divider(_hw) \ + container_of(_hw, struct sg2042_divider_clock, hw) + +/** + * struct sg2042_gate_clock - Gate clock + * @hw: clk_hw for initialization + * @id: used to map clk_onecell_data + * @offset_enable: offset of gate enable registers + * @bit_idx: which bit in the register controls gating of this clock + */ +struct sg2042_gate_clock { + struct clk_hw hw; + + unsigned int id; + + u32 offset_enable; + u8 bit_idx; +}; + +/** + * struct sg2042_mux_clock - Mux clock + * @hw: clk_hw for initialization + * @id: used to map clk_onecell_data + * @offset_select: offset of mux selection registers + * **NOTE**: MUX registers are ALL in CLOCK! + * @shift: shift of "Clock Select" in mux selection register + * @width: width of "Clock Select" in mux selection register + * @clk_nb: used for notification + * @original_index: set by notifier callback + */ +struct sg2042_mux_clock { + struct clk_hw hw; + + unsigned int id; + + u32 offset_select; + u8 shift; + u8 width; + + struct notifier_block clk_nb; + u8 original_index; +}; + +#define to_sg2042_mux_nb(_nb) container_of(_nb, struct sg2042_mux_clock, clk_nb) + +static unsigned long sg2042_clk_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw); + unsigned long ret_rate; + u32 val; + + if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) { + val = divider->initval; + } else { + val = readl(divider->reg) >> divider->shift; + val &= clk_div_mask(divider->width); + } + + ret_rate = divider_recalc_rate(hw, parent_rate, val, NULL, + divider->div_flags, divider->width); + + pr_debug("--> %s: divider_recalc_rate: ret_rate = %ld\n", + clk_hw_get_name(hw), ret_rate); + return ret_rate; +} + +static long sg2042_clk_divider_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw); + unsigned long ret_rate; + u32 bestdiv; + + /* if read only, just return current value */ + if (divider->div_flags & CLK_DIVIDER_READ_ONLY) { + if (!(readl(divider->reg) & BIT(SHIFT_DIV_FACTOR_SEL))) { + bestdiv = divider->initval; + } else { + bestdiv = readl(divider->reg) >> divider->shift; + bestdiv &= clk_div_mask(divider->width); + } + ret_rate = DIV_ROUND_UP_ULL((u64)*prate, bestdiv); + } else { + ret_rate = divider_round_rate(hw, rate, prate, NULL, + divider->width, divider->div_flags); + } + + pr_debug("--> %s: divider_round_rate: val = %ld\n", + clk_hw_get_name(hw), ret_rate); + return ret_rate; +} + +static int sg2042_clk_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct sg2042_divider_clock *divider = to_sg2042_clk_divider(hw); + unsigned long flags = 0; + u32 val, val2, value; + + value = divider_get_val(rate, parent_rate, NULL, + divider->width, divider->div_flags); + + if (divider->lock) + spin_lock_irqsave(divider->lock, flags); + else + __acquire(divider->lock); + + /* + * The sequence of clock frequency modification is: + * Assert to reset divider. + * Modify the value of Clock Divide Factor (and High Wide if needed). + * De-assert to restore divided clock with new frequency. + */ + val = readl(divider->reg); + + /* assert */ + val &= ~BIT(SHIFT_DIV_RESET_CTRL); + writel(val, divider->reg); + + if (divider->div_flags & CLK_DIVIDER_HIWORD_MASK) { + val = clk_div_mask(divider->width) << (divider->shift + 16); + } else { + val = readl(divider->reg); + val &= ~(clk_div_mask(divider->width) << divider->shift); + } + val |= value << divider->shift; + val |= BIT(SHIFT_DIV_FACTOR_SEL); + writel(val, divider->reg); + val2 = val; + + /* de-assert */ + val |= BIT(SHIFT_DIV_RESET_CTRL); + writel(val, divider->reg); + + if (divider->lock) + spin_unlock_irqrestore(divider->lock, flags); + else + __release(divider->lock); + + pr_debug("--> %s: divider_set_rate: register val = 0x%x\n", + clk_hw_get_name(hw), val2); + return 0; +} + +static const struct clk_ops sg2042_clk_divider_ops = { + .recalc_rate = sg2042_clk_divider_recalc_rate, + .round_rate = sg2042_clk_divider_round_rate, + .set_rate = sg2042_clk_divider_set_rate, +}; + +static const struct clk_ops sg2042_clk_divider_ro_ops = { + .recalc_rate = sg2042_clk_divider_recalc_rate, + .round_rate = sg2042_clk_divider_round_rate, +}; + +/* + * Clock initialization macro naming rules: + * FW: use CLK_HW_INIT_FW_NAME + * HW: use CLK_HW_INIT_HW + * HWS: use CLK_HW_INIT_HWS + * RO: means Read-Only + */ +#define SG2042_DIV_FW(_id, _name, _parent, \ + _r_ctrl, _shift, _width, \ + _div_flag, _initval) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_FW_NAME( \ + _name, \ + _parent, \ + &sg2042_clk_divider_ops, \ + 0), \ + .offset_ctrl = _r_ctrl, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = _div_flag, \ + .initval = _initval, \ + } + +#define SG2042_DIV_FW_RO(_id, _name, _parent, \ + _r_ctrl, _shift, _width, \ + _div_flag, _initval) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_FW_NAME( \ + _name, \ + _parent, \ + &sg2042_clk_divider_ro_ops, \ + 0), \ + .offset_ctrl = _r_ctrl, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY, \ + .initval = _initval, \ + } + +#define SG2042_DIV_HW(_id, _name, _parent, \ + _r_ctrl, _shift, _width, \ + _div_flag, _initval) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_HW( \ + _name, \ + _parent, \ + &sg2042_clk_divider_ops, \ + 0), \ + .offset_ctrl = _r_ctrl, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = _div_flag, \ + .initval = _initval, \ + } + +#define SG2042_DIV_HW_RO(_id, _name, _parent, \ + _r_ctrl, _shift, _width, \ + _div_flag, _initval) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_HW( \ + _name, \ + _parent, \ + &sg2042_clk_divider_ro_ops, \ + 0), \ + .offset_ctrl = _r_ctrl, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY, \ + .initval = _initval, \ + } + +#define SG2042_DIV_HWS(_id, _name, _parent, \ + _r_ctrl, _shift, _width, \ + _div_flag, _initval) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_HWS( \ + _name, \ + _parent, \ + &sg2042_clk_divider_ops, \ + 0), \ + .offset_ctrl = _r_ctrl, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = _div_flag, \ + .initval = _initval, \ + } + +#define SG2042_DIV_HWS_RO(_id, _name, _parent, \ + _r_ctrl, _shift, _width, \ + _div_flag, _initval) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_HWS( \ + _name, \ + _parent, \ + &sg2042_clk_divider_ro_ops, \ + 0), \ + .offset_ctrl = _r_ctrl, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = (_div_flag) | CLK_DIVIDER_READ_ONLY, \ + .initval = _initval, \ + } + +#define SG2042_GATE_HWS(_id, _name, _parent, _flags, \ + _r_enable, _bit_idx) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_HWS( \ + _name, \ + _parent, \ + NULL, \ + _flags), \ + .offset_enable = _r_enable, \ + .bit_idx = _bit_idx, \ + } + +#define SG2042_GATE_HW(_id, _name, _parent, _flags, \ + _r_enable, _bit_idx) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_HW( \ + _name, \ + _parent, \ + NULL, \ + _flags), \ + .offset_enable = _r_enable, \ + .bit_idx = _bit_idx, \ + } + +#define SG2042_GATE_FW(_id, _name, _parent, _flags, \ + _r_enable, _bit_idx) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_FW_NAME( \ + _name, \ + _parent, \ + NULL, \ + _flags), \ + .offset_enable = _r_enable, \ + .bit_idx = _bit_idx, \ + } + +#define SG2042_MUX(_id, _name, _parents, _flags, _r_select, _shift, _width) { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_PARENTS_HW( \ + _name, \ + _parents, \ + NULL, \ + _flags), \ + .offset_select = _r_select, \ + .shift = _shift, \ + .width = _width, \ + } + +/* + * Clock items in the array are sorted according to the clock-tree diagram, + * from top to bottom, from upstream to downstream. Read TRM for details. + */ + +/* updated during probe/registration */ +static const struct clk_hw *clk_gate_ddr01_div0[] = { NULL }; +static const struct clk_hw *clk_gate_ddr01_div1[] = { NULL }; +static const struct clk_hw *clk_gate_ddr23_div0[] = { NULL }; +static const struct clk_hw *clk_gate_ddr23_div1[] = { NULL }; +static const struct clk_hw *clk_gate_rp_cpu_normal_div0[] = { NULL }; +static const struct clk_hw *clk_gate_rp_cpu_normal_div1[] = { NULL }; +static const struct clk_hw *clk_gate_axi_ddr_div0[] = { NULL }; +static const struct clk_hw *clk_gate_axi_ddr_div1[] = { NULL }; + +static const struct sg2042_gate_clock sg2042_gate_clks_level_1[] = { + SG2042_GATE_FW(GATE_CLK_DDR01_DIV0, "clk_gate_ddr01_div0", "dpll0", + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + R_CLKDIVREG27, 4), + SG2042_GATE_FW(GATE_CLK_DDR01_DIV1, "clk_gate_ddr01_div1", "fpll", + CLK_IS_CRITICAL, + R_CLKDIVREG28, 4), + + SG2042_GATE_FW(GATE_CLK_DDR23_DIV0, "clk_gate_ddr23_div0", "dpll1", + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + R_CLKDIVREG29, 4), + SG2042_GATE_FW(GATE_CLK_DDR23_DIV1, "clk_gate_ddr23_div1", "fpll", + CLK_IS_CRITICAL, + R_CLKDIVREG30, 4), + + SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV0, + "clk_gate_rp_cpu_normal_div0", "mpll", + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKDIVREG0, 4), + SG2042_GATE_FW(GATE_CLK_RP_CPU_NORMAL_DIV1, + "clk_gate_rp_cpu_normal_div1", "fpll", + CLK_IS_CRITICAL, + R_CLKDIVREG1, 4), + + SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV0, "clk_gate_axi_ddr_div0", "mpll", + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKDIVREG25, 4), + SG2042_GATE_FW(GATE_CLK_AXI_DDR_DIV1, "clk_gate_axi_ddr_div1", "fpll", + CLK_IS_CRITICAL, + R_CLKDIVREG26, 4), +}; + +#define DEF_DIVFLAG (CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO) + +static struct sg2042_divider_clock sg2042_div_clks_level_1[] = { + SG2042_DIV_HWS_RO(DIV_CLK_DPLL0_DDR01_0, + "clk_div_ddr01_0", clk_gate_ddr01_div0, + R_CLKDIVREG27, 16, 5, DEF_DIVFLAG, 1), + SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR01_1, + "clk_div_ddr01_1", clk_gate_ddr01_div1, + R_CLKDIVREG28, 16, 5, DEF_DIVFLAG, 1), + + SG2042_DIV_HWS_RO(DIV_CLK_DPLL1_DDR23_0, + "clk_div_ddr23_0", clk_gate_ddr23_div0, + R_CLKDIVREG29, 16, 5, DEF_DIVFLAG, 1), + SG2042_DIV_HWS_RO(DIV_CLK_FPLL_DDR23_1, + "clk_div_ddr23_1", clk_gate_ddr23_div1, + R_CLKDIVREG30, 16, 5, DEF_DIVFLAG, 1), + + SG2042_DIV_HWS(DIV_CLK_MPLL_RP_CPU_NORMAL_0, + "clk_div_rp_cpu_normal_0", clk_gate_rp_cpu_normal_div0, + R_CLKDIVREG0, 16, 5, DEF_DIVFLAG, 1), + SG2042_DIV_HWS(DIV_CLK_FPLL_RP_CPU_NORMAL_1, + "clk_div_rp_cpu_normal_1", clk_gate_rp_cpu_normal_div1, + R_CLKDIVREG1, 16, 5, DEF_DIVFLAG, 1), + + SG2042_DIV_HWS(DIV_CLK_MPLL_AXI_DDR_0, + "clk_div_axi_ddr_0", clk_gate_axi_ddr_div0, + R_CLKDIVREG25, 16, 5, DEF_DIVFLAG, 2), + SG2042_DIV_HWS(DIV_CLK_FPLL_AXI_DDR_1, + "clk_div_axi_ddr_1", clk_gate_axi_ddr_div1, + R_CLKDIVREG26, 16, 5, DEF_DIVFLAG, 1), +}; + +/* + * Note: regarding names for mux clock, "0/1" or "div0/div1" means the + * first/second parent input source, not the register value. + * For example: + * "clk_div_ddr01_0" is the name of Clock divider 0 control of DDR01, and + * "clk_gate_ddr01_div0" is the gate clock in front of the "clk_div_ddr01_0", + * they are both controlled by register CLKDIVREG27; + * "clk_div_ddr01_1" is the name of Clock divider 1 control of DDR01, and + * "clk_gate_ddr01_div1" is the gate clock in front of the "clk_div_ddr01_1", + * they are both controlled by register CLKDIVREG28; + * While for register value of mux selection, use Clock Select for DDR01’s clock + * as example, see CLKSELREG0, bit[2]. + * 1: Select in_dpll0_clk as clock source, correspondng to the parent input + * source from "clk_div_ddr01_0". + * 0: Select in_fpll_clk as clock source, corresponding to the parent input + * source from "clk_div_ddr01_1". + * So we need a table to define the array of register values corresponding to + * the parent index and tell CCF about this when registering mux clock. + */ +static const u32 sg2042_mux_table[] = {1, 0}; + +/* Aliases just for easy reading */ +#define clk_div_ddr01_0 (&sg2042_div_clks_level_1[0].hw) +#define clk_div_ddr01_1 (&sg2042_div_clks_level_1[1].hw) +#define clk_div_ddr23_0 (&sg2042_div_clks_level_1[2].hw) +#define clk_div_ddr23_1 (&sg2042_div_clks_level_1[3].hw) +#define clk_div_rp_cpu_normal_0 (&sg2042_div_clks_level_1[4].hw) +#define clk_div_rp_cpu_normal_1 (&sg2042_div_clks_level_1[5].hw) +#define clk_div_axi_ddr_0 (&sg2042_div_clks_level_1[6].hw) +#define clk_div_axi_ddr_1 (&sg2042_div_clks_level_1[7].hw) + +static const struct clk_hw *clk_mux_ddr01_p[] = { + clk_div_ddr01_0, + clk_div_ddr01_1, +}; + +static const struct clk_hw *clk_mux_ddr23_p[] = { + clk_div_ddr23_0, + clk_div_ddr23_1, +}; + +static const struct clk_hw *clk_mux_rp_cpu_normal_p[] = { + clk_div_rp_cpu_normal_0, + clk_div_rp_cpu_normal_1, +}; + +static const struct clk_hw *clk_mux_axi_ddr_p[] = { + clk_div_axi_ddr_0, + clk_div_axi_ddr_1, +}; + +/* Mux clocks to be updated during probe/registration */ +static const struct clk_hw *clk_mux_ddr01[] = { NULL }; +static const struct clk_hw *clk_mux_ddr23[] = { NULL }; +static const struct clk_hw *clk_mux_rp_cpu_normal[] = { NULL }; +static const struct clk_hw *clk_mux_axi_ddr[] = { NULL }; + +static struct sg2042_mux_clock sg2042_mux_clks[] = { + SG2042_MUX(MUX_CLK_DDR01, "clk_mux_ddr01", clk_mux_ddr01_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY, + R_CLKSELREG0, 2, 1), + SG2042_MUX(MUX_CLK_DDR23, "clk_mux_ddr23", clk_mux_ddr23_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT | CLK_MUX_READ_ONLY, + R_CLKSELREG0, 3, 1), + SG2042_MUX(MUX_CLK_RP_CPU_NORMAL, "clk_mux_rp_cpu_normal", clk_mux_rp_cpu_normal_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + R_CLKSELREG0, 0, 1), + SG2042_MUX(MUX_CLK_AXI_DDR, "clk_mux_axi_ddr", clk_mux_axi_ddr_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + R_CLKSELREG0, 1, 1), +}; + +/* Aliases just for easy reading */ +#define clk_div_top_rp_cmn_div2 (&sg2042_div_clks_level_2[0].hw) +#define clk_div_50m_a53 (&sg2042_div_clks_level_2[1].hw) +#define clk_div_timer1 (&sg2042_div_clks_level_2[2].hw) +#define clk_div_timer2 (&sg2042_div_clks_level_2[3].hw) +#define clk_div_timer3 (&sg2042_div_clks_level_2[4].hw) +#define clk_div_timer4 (&sg2042_div_clks_level_2[5].hw) +#define clk_div_timer5 (&sg2042_div_clks_level_2[6].hw) +#define clk_div_timer6 (&sg2042_div_clks_level_2[7].hw) +#define clk_div_timer7 (&sg2042_div_clks_level_2[8].hw) +#define clk_div_timer8 (&sg2042_div_clks_level_2[9].hw) +#define clk_div_uart_500m (&sg2042_div_clks_level_2[10].hw) +#define clk_div_ahb_lpc (&sg2042_div_clks_level_2[11].hw) +#define clk_div_efuse (&sg2042_div_clks_level_2[12].hw) +#define clk_div_tx_eth0 (&sg2042_div_clks_level_2[13].hw) +#define clk_div_ptp_ref_i_eth0 (&sg2042_div_clks_level_2[14].hw) +#define clk_div_ref_eth0 (&sg2042_div_clks_level_2[15].hw) +#define clk_div_emmc (&sg2042_div_clks_level_2[16].hw) +#define clk_div_sd (&sg2042_div_clks_level_2[17].hw) +#define clk_div_top_axi0 (&sg2042_div_clks_level_2[18].hw) +#define clk_div_100k_emmc (&sg2042_div_clks_level_2[19].hw) +#define clk_div_100k_sd (&sg2042_div_clks_level_2[20].hw) +#define clk_div_gpio_db (&sg2042_div_clks_level_2[21].hw) +#define clk_div_top_axi_hsperi (&sg2042_div_clks_level_2[22].hw) + +static struct sg2042_divider_clock sg2042_div_clks_level_2[] = { + SG2042_DIV_HWS(DIV_CLK_FPLL_TOP_RP_CMN_DIV2, + "clk_div_top_rp_cmn_div2", clk_mux_rp_cpu_normal, + R_CLKDIVREG3, 16, 16, DEF_DIVFLAG, 2), + + SG2042_DIV_FW(DIV_CLK_FPLL_50M_A53, "clk_div_50m_a53", "fpll", + R_CLKDIVREG2, 16, 8, DEF_DIVFLAG, 20), + /* downstream of div_50m_a53 */ + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER1, "clk_div_timer1", clk_div_50m_a53, + R_CLKDIVREG6, 16, 16, DEF_DIVFLAG, 1), + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER2, "clk_div_timer2", clk_div_50m_a53, + R_CLKDIVREG7, 16, 16, DEF_DIVFLAG, 1), + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER3, "clk_div_timer3", clk_div_50m_a53, + R_CLKDIVREG8, 16, 16, DEF_DIVFLAG, 1), + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER4, "clk_div_timer4", clk_div_50m_a53, + R_CLKDIVREG9, 16, 16, DEF_DIVFLAG, 1), + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER5, "clk_div_timer5", clk_div_50m_a53, + R_CLKDIVREG10, 16, 16, DEF_DIVFLAG, 1), + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER6, "clk_div_timer6", clk_div_50m_a53, + R_CLKDIVREG11, 16, 16, DEF_DIVFLAG, 1), + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER7, "clk_div_timer7", clk_div_50m_a53, + R_CLKDIVREG12, 16, 16, DEF_DIVFLAG, 1), + SG2042_DIV_HW(DIV_CLK_FPLL_DIV_TIMER8, "clk_div_timer8", clk_div_50m_a53, + R_CLKDIVREG13, 16, 16, DEF_DIVFLAG, 1), + + /* + * Set clk_div_uart_500m as RO, because the width of CLKDIVREG4 is too + * narrow for us to produce 115200. Use UART internal divider directly. + */ + SG2042_DIV_FW_RO(DIV_CLK_FPLL_UART_500M, "clk_div_uart_500m", "fpll", + R_CLKDIVREG4, 16, 7, DEF_DIVFLAG, 2), + SG2042_DIV_FW(DIV_CLK_FPLL_AHB_LPC, "clk_div_ahb_lpc", "fpll", + R_CLKDIVREG5, 16, 16, DEF_DIVFLAG, 5), + SG2042_DIV_FW(DIV_CLK_FPLL_EFUSE, "clk_div_efuse", "fpll", + R_CLKDIVREG14, 16, 7, DEF_DIVFLAG, 40), + SG2042_DIV_FW(DIV_CLK_FPLL_TX_ETH0, "clk_div_tx_eth0", "fpll", + R_CLKDIVREG16, 16, 11, DEF_DIVFLAG, 8), + SG2042_DIV_FW(DIV_CLK_FPLL_PTP_REF_I_ETH0, + "clk_div_ptp_ref_i_eth0", "fpll", + R_CLKDIVREG17, 16, 8, DEF_DIVFLAG, 20), + SG2042_DIV_FW(DIV_CLK_FPLL_REF_ETH0, "clk_div_ref_eth0", "fpll", + R_CLKDIVREG18, 16, 8, DEF_DIVFLAG, 40), + SG2042_DIV_FW(DIV_CLK_FPLL_EMMC, "clk_div_emmc", "fpll", + R_CLKDIVREG19, 16, 5, DEF_DIVFLAG, 10), + SG2042_DIV_FW(DIV_CLK_FPLL_SD, "clk_div_sd", "fpll", + R_CLKDIVREG21, 16, 5, DEF_DIVFLAG, 10), + + SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI0, "clk_div_top_axi0", "fpll", + R_CLKDIVREG23, 16, 5, DEF_DIVFLAG, 10), + /* downstream of div_top_axi0 */ + SG2042_DIV_HW(DIV_CLK_FPLL_100K_EMMC, "clk_div_100k_emmc", clk_div_top_axi0, + R_CLKDIVREG20, 16, 16, DEF_DIVFLAG, 1000), + SG2042_DIV_HW(DIV_CLK_FPLL_100K_SD, "clk_div_100k_sd", clk_div_top_axi0, + R_CLKDIVREG22, 16, 16, DEF_DIVFLAG, 1000), + SG2042_DIV_HW(DIV_CLK_FPLL_GPIO_DB, "clk_div_gpio_db", clk_div_top_axi0, + R_CLKDIVREG15, 16, 16, DEF_DIVFLAG, 1000), + + SG2042_DIV_FW(DIV_CLK_FPLL_TOP_AXI_HSPERI, + "clk_div_top_axi_hsperi", "fpll", + R_CLKDIVREG24, 16, 5, DEF_DIVFLAG, 4), +}; + +/* Gate clocks to be updated during probe/registration */ +static const struct clk_hw *clk_gate_rp_cpu_normal[] = { NULL }; +static const struct clk_hw *clk_gate_top_rp_cmn_div2[] = { NULL }; + +static const struct sg2042_gate_clock sg2042_gate_clks_level_2[] = { + SG2042_GATE_HWS(GATE_CLK_DDR01, "clk_gate_ddr01", clk_mux_ddr01, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKENREG1, 14), + + SG2042_GATE_HWS(GATE_CLK_DDR23, "clk_gate_ddr23", clk_mux_ddr23, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKENREG1, 15), + + SG2042_GATE_HWS(GATE_CLK_RP_CPU_NORMAL, + "clk_gate_rp_cpu_normal", clk_mux_rp_cpu_normal, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKENREG0, 0), + + SG2042_GATE_HWS(GATE_CLK_AXI_DDR, "clk_gate_axi_ddr", clk_mux_axi_ddr, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKENREG1, 13), + + /* upon are gate clocks directly downstream of muxes */ + + /* downstream of clk_div_top_rp_cmn_div2 */ + SG2042_GATE_HW(GATE_CLK_TOP_RP_CMN_DIV2, + "clk_gate_top_rp_cmn_div2", clk_div_top_rp_cmn_div2, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 2), + SG2042_GATE_HWS(GATE_CLK_HSDMA, "clk_gate_hsdma", clk_gate_top_rp_cmn_div2, + CLK_SET_RATE_PARENT, R_CLKENREG1, 10), + + /* + * downstream of clk_gate_rp_cpu_normal + * + * FIXME: there should be one 1/2 DIV between clk_gate_rp_cpu_normal + * and clk_gate_axi_pcie0/clk_gate_axi_pcie1. + * But the 1/2 DIV is fixed and no configurable register exported, so + * when reading from these two clocks, the rate value are still the + * same as that of clk_gate_rp_cpu_normal, it's not correct. + * This just affects the value read. + */ + SG2042_GATE_HWS(GATE_CLK_AXI_PCIE0, + "clk_gate_axi_pcie0", clk_gate_rp_cpu_normal, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 8), + SG2042_GATE_HWS(GATE_CLK_AXI_PCIE1, + "clk_gate_axi_pcie1", clk_gate_rp_cpu_normal, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG1, 9), + + /* downstream of div_50m_a53 */ + SG2042_GATE_HW(GATE_CLK_A53_50M, "clk_gate_a53_50m", clk_div_50m_a53, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 1), + SG2042_GATE_HW(GATE_CLK_TIMER1, "clk_gate_timer1", clk_div_timer1, + CLK_SET_RATE_PARENT, R_CLKENREG0, 12), + SG2042_GATE_HW(GATE_CLK_TIMER2, "clk_gate_timer2", clk_div_timer2, + CLK_SET_RATE_PARENT, R_CLKENREG0, 13), + SG2042_GATE_HW(GATE_CLK_TIMER3, "clk_gate_timer3", clk_div_timer3, + CLK_SET_RATE_PARENT, R_CLKENREG0, 14), + SG2042_GATE_HW(GATE_CLK_TIMER4, "clk_gate_timer4", clk_div_timer4, + CLK_SET_RATE_PARENT, R_CLKENREG0, 15), + SG2042_GATE_HW(GATE_CLK_TIMER5, "clk_gate_timer5", clk_div_timer5, + CLK_SET_RATE_PARENT, R_CLKENREG0, 16), + SG2042_GATE_HW(GATE_CLK_TIMER6, "clk_gate_timer6", clk_div_timer6, + CLK_SET_RATE_PARENT, R_CLKENREG0, 17), + SG2042_GATE_HW(GATE_CLK_TIMER7, "clk_gate_timer7", clk_div_timer7, + CLK_SET_RATE_PARENT, R_CLKENREG0, 18), + SG2042_GATE_HW(GATE_CLK_TIMER8, "clk_gate_timer8", clk_div_timer8, + CLK_SET_RATE_PARENT, R_CLKENREG0, 19), + + /* gate clocks downstream from div clocks one-to-one */ + SG2042_GATE_HW(GATE_CLK_UART_500M, "clk_gate_uart_500m", clk_div_uart_500m, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, R_CLKENREG0, 4), + SG2042_GATE_HW(GATE_CLK_AHB_LPC, "clk_gate_ahb_lpc", clk_div_ahb_lpc, + CLK_SET_RATE_PARENT, R_CLKENREG0, 7), + SG2042_GATE_HW(GATE_CLK_EFUSE, "clk_gate_efuse", clk_div_efuse, + CLK_SET_RATE_PARENT, R_CLKENREG0, 20), + SG2042_GATE_HW(GATE_CLK_TX_ETH0, "clk_gate_tx_eth0", clk_div_tx_eth0, + CLK_SET_RATE_PARENT, R_CLKENREG0, 30), + SG2042_GATE_HW(GATE_CLK_PTP_REF_I_ETH0, + "clk_gate_ptp_ref_i_eth0", clk_div_ptp_ref_i_eth0, + CLK_SET_RATE_PARENT, R_CLKENREG1, 0), + SG2042_GATE_HW(GATE_CLK_REF_ETH0, "clk_gate_ref_eth0", clk_div_ref_eth0, + CLK_SET_RATE_PARENT, R_CLKENREG1, 1), + SG2042_GATE_HW(GATE_CLK_EMMC_100M, "clk_gate_emmc", clk_div_emmc, + CLK_SET_RATE_PARENT, R_CLKENREG1, 3), + SG2042_GATE_HW(GATE_CLK_SD_100M, "clk_gate_sd", clk_div_sd, + CLK_SET_RATE_PARENT, R_CLKENREG1, 6), + + /* downstream of clk_div_top_axi0 */ + SG2042_GATE_HW(GATE_CLK_AHB_ROM, "clk_gate_ahb_rom", clk_div_top_axi0, + 0, R_CLKENREG0, 8), + SG2042_GATE_HW(GATE_CLK_AHB_SF, "clk_gate_ahb_sf", clk_div_top_axi0, + 0, R_CLKENREG0, 9), + SG2042_GATE_HW(GATE_CLK_AXI_SRAM, "clk_gate_axi_sram", clk_div_top_axi0, + CLK_IGNORE_UNUSED, R_CLKENREG0, 10), + SG2042_GATE_HW(GATE_CLK_APB_TIMER, "clk_gate_apb_timer", clk_div_top_axi0, + CLK_IGNORE_UNUSED, R_CLKENREG0, 11), + SG2042_GATE_HW(GATE_CLK_APB_EFUSE, "clk_gate_apb_efuse", clk_div_top_axi0, + 0, R_CLKENREG0, 21), + SG2042_GATE_HW(GATE_CLK_APB_GPIO, "clk_gate_apb_gpio", clk_div_top_axi0, + 0, R_CLKENREG0, 22), + SG2042_GATE_HW(GATE_CLK_APB_GPIO_INTR, + "clk_gate_apb_gpio_intr", clk_div_top_axi0, + CLK_IS_CRITICAL, R_CLKENREG0, 23), + SG2042_GATE_HW(GATE_CLK_APB_I2C, "clk_gate_apb_i2c", clk_div_top_axi0, + 0, R_CLKENREG0, 26), + SG2042_GATE_HW(GATE_CLK_APB_WDT, "clk_gate_apb_wdt", clk_div_top_axi0, + 0, R_CLKENREG0, 27), + SG2042_GATE_HW(GATE_CLK_APB_PWM, "clk_gate_apb_pwm", clk_div_top_axi0, + 0, R_CLKENREG0, 28), + SG2042_GATE_HW(GATE_CLK_APB_RTC, "clk_gate_apb_rtc", clk_div_top_axi0, + 0, R_CLKENREG0, 29), + SG2042_GATE_HW(GATE_CLK_TOP_AXI0, "clk_gate_top_axi0", clk_div_top_axi0, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKENREG1, 11), + /* downstream of DIV clocks which are sourced from clk_div_top_axi0 */ + SG2042_GATE_HW(GATE_CLK_GPIO_DB, "clk_gate_gpio_db", clk_div_gpio_db, + CLK_SET_RATE_PARENT, R_CLKENREG0, 24), + SG2042_GATE_HW(GATE_CLK_100K_EMMC, "clk_gate_100k_emmc", clk_div_100k_emmc, + CLK_SET_RATE_PARENT, R_CLKENREG1, 4), + SG2042_GATE_HW(GATE_CLK_100K_SD, "clk_gate_100k_sd", clk_div_100k_sd, + CLK_SET_RATE_PARENT, R_CLKENREG1, 7), + + /* downstream of clk_div_top_axi_hsperi */ + SG2042_GATE_HW(GATE_CLK_SYSDMA_AXI, + "clk_gate_sysdma_axi", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT, R_CLKENREG0, 3), + SG2042_GATE_HW(GATE_CLK_APB_UART, + "clk_gate_apb_uart", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT, R_CLKENREG0, 5), + SG2042_GATE_HW(GATE_CLK_AXI_DBG_I2C, + "clk_gate_axi_dbg_i2c", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT, R_CLKENREG0, 6), + SG2042_GATE_HW(GATE_CLK_APB_SPI, + "clk_gate_apb_spi", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT, R_CLKENREG0, 25), + SG2042_GATE_HW(GATE_CLK_AXI_ETH0, + "clk_gate_axi_eth0", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT, R_CLKENREG0, 31), + SG2042_GATE_HW(GATE_CLK_AXI_EMMC, + "clk_gate_axi_emmc", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT, R_CLKENREG1, 2), + SG2042_GATE_HW(GATE_CLK_AXI_SD, + "clk_gate_axi_sd", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT, R_CLKENREG1, 5), + SG2042_GATE_HW(GATE_CLK_TOP_AXI_HSPERI, + "clk_gate_top_axi_hsperi", clk_div_top_axi_hsperi, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + R_CLKENREG1, 12), +}; + +static DEFINE_SPINLOCK(sg2042_clk_lock); + +static int sg2042_clk_register_divs(struct device *dev, + struct sg2042_clk_data *clk_data, + struct sg2042_divider_clock div_clks[], + int num_div_clks) +{ + struct sg2042_divider_clock *div; + struct clk_hw *hw; + int i, ret = 0; + + for (i = 0; i < num_div_clks; i++) { + div = &div_clks[i]; + + if (div->div_flags & CLK_DIVIDER_HIWORD_MASK) { + if (div->width + div->shift > 16) { + pr_warn("divider value exceeds LOWORD field\n"); + ret = -EINVAL; + break; + } + } + + div->reg = clk_data->iobase + div->offset_ctrl; + div->lock = &sg2042_clk_lock; + + hw = &div->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) { + pr_err("failed to register clock %s\n", div->hw.init->name); + break; + } + + clk_data->onecell_data.hws[div->id] = hw; + } + + return ret; +} + +static int sg2042_clk_register_gates(struct device *dev, + struct sg2042_clk_data *clk_data, + const struct sg2042_gate_clock gate_clks[], + int num_gate_clks) +{ + const struct sg2042_gate_clock *gate; + struct clk_hw *hw; + int i, ret = 0; + + for (i = 0; i < num_gate_clks; i++) { + gate = &gate_clks[i]; + hw = __devm_clk_hw_register_gate + (dev, + NULL, + gate->hw.init->name, + NULL, + gate->hw.init->parent_hws[0], + NULL, + gate->hw.init->flags, + clk_data->iobase + gate->offset_enable, + gate->bit_idx, + 0, + &sg2042_clk_lock); + if (IS_ERR(hw)) { + pr_err("failed to register clock %s\n", gate->hw.init->name); + ret = PTR_ERR(hw); + break; + } + + clk_data->onecell_data.hws[gate->id] = hw; + + /* Updated some clocks which take the role of parent */ + switch (gate->id) { + case GATE_CLK_RP_CPU_NORMAL: + *clk_gate_rp_cpu_normal = hw; + break; + case GATE_CLK_TOP_RP_CMN_DIV2: + *clk_gate_top_rp_cmn_div2 = hw; + break; + } + } + + return ret; +} + +static int sg2042_clk_register_gates_fw(struct device *dev, + struct sg2042_clk_data *clk_data, + const struct sg2042_gate_clock gate_clks[], + int num_gate_clks) +{ + const struct sg2042_gate_clock *gate; + struct clk_hw *hw; + int i, ret = 0; + + for (i = 0; i < num_gate_clks; i++) { + gate = &gate_clks[i]; + hw = devm_clk_hw_register_gate_parent_data + (dev, + gate->hw.init->name, + gate->hw.init->parent_data, + gate->hw.init->flags, + clk_data->iobase + gate->offset_enable, + gate->bit_idx, + 0, + &sg2042_clk_lock); + if (IS_ERR(hw)) { + pr_err("failed to register clock %s\n", gate->hw.init->name); + ret = PTR_ERR(hw); + break; + } + + clk_data->onecell_data.hws[gate->id] = hw; + + /* Updated some clocks which take the role of parent */ + switch (gate->id) { + case GATE_CLK_DDR01_DIV0: + *clk_gate_ddr01_div0 = hw; + break; + case GATE_CLK_DDR01_DIV1: + *clk_gate_ddr01_div1 = hw; + break; + case GATE_CLK_DDR23_DIV0: + *clk_gate_ddr23_div0 = hw; + break; + case GATE_CLK_DDR23_DIV1: + *clk_gate_ddr23_div1 = hw; + break; + case GATE_CLK_RP_CPU_NORMAL_DIV0: + *clk_gate_rp_cpu_normal_div0 = hw; + break; + case GATE_CLK_RP_CPU_NORMAL_DIV1: + *clk_gate_rp_cpu_normal_div1 = hw; + break; + case GATE_CLK_AXI_DDR_DIV0: + *clk_gate_axi_ddr_div0 = hw; + break; + case GATE_CLK_AXI_DDR_DIV1: + *clk_gate_axi_ddr_div1 = hw; + break; + } + } + + return ret; +} + +static int sg2042_mux_notifier_cb(struct notifier_block *nb, + unsigned long event, + void *data) +{ + struct sg2042_mux_clock *mux = to_sg2042_mux_nb(nb); + const struct clk_ops *ops = &clk_mux_ops; + struct clk_notifier_data *ndata = data; + struct clk_hw *hw; + int ret = 0; + + hw = __clk_get_hw(ndata->clk); + + /* To switch to fpll before changing rate and restore after that */ + if (event == PRE_RATE_CHANGE) { + mux->original_index = ops->get_parent(hw); + + /* + * "1" is the array index of the second parent input source of + * mux. For SG2042, it's fpll for all mux clocks. + * "0" is the array index of the frist parent input source of + * mux, For SG2042, it's mpll. + * FIXME, any good idea to avoid magic number? + */ + if (mux->original_index == 0) + ret = ops->set_parent(hw, 1); + } else if (event == POST_RATE_CHANGE) { + ret = ops->set_parent(hw, mux->original_index); + } + + return notifier_from_errno(ret); +} + +static int sg2042_clk_register_muxs(struct device *dev, + struct sg2042_clk_data *clk_data, + struct sg2042_mux_clock mux_clks[], + int num_mux_clks) +{ + struct sg2042_mux_clock *mux; + struct clk_hw *hw; + int i, ret = 0; + + for (i = 0; i < num_mux_clks; i++) { + mux = &mux_clks[i]; + + hw = __devm_clk_hw_register_mux + (dev, + NULL, + mux->hw.init->name, + mux->hw.init->num_parents, + NULL, + mux->hw.init->parent_hws, + NULL, + mux->hw.init->flags, + clk_data->iobase + mux->offset_select, + mux->shift, + BIT(mux->width) - 1, + 0, + sg2042_mux_table, + &sg2042_clk_lock); + if (IS_ERR(hw)) { + pr_err("failed to register clock %s\n", mux->hw.init->name); + ret = PTR_ERR(hw); + break; + } + + clk_data->onecell_data.hws[mux->id] = hw; + + /* Updated some clocks which takes the role of parent */ + switch (mux->id) { + case MUX_CLK_DDR01: + *clk_mux_ddr01 = hw; + break; + case MUX_CLK_DDR23: + *clk_mux_ddr23 = hw; + break; + case MUX_CLK_RP_CPU_NORMAL: + *clk_mux_rp_cpu_normal = hw; + break; + case MUX_CLK_AXI_DDR: + *clk_mux_axi_ddr = hw; + break; + } + + /* + * FIXME: Theoretically, we should set parent for the + * mux, but seems hardware has done this for us with + * default value, so we don't set parent again here. + */ + + if (!(mux->hw.init->flags & CLK_MUX_READ_ONLY)) { + mux->clk_nb.notifier_call = sg2042_mux_notifier_cb; + ret = devm_clk_notifier_register(dev, hw->clk, &mux->clk_nb); + if (ret) { + pr_err("failed to register clock notifier for %s\n", + mux->hw.init->name); + break; + } + } + } + + return ret; +} + +static int sg2042_init_clkdata(struct platform_device *pdev, + int num_clks, + struct sg2042_clk_data **pp_clk_data) +{ + struct sg2042_clk_data *clk_data = NULL; + + clk_data = devm_kzalloc(&pdev->dev, + struct_size(clk_data, onecell_data.hws, num_clks), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->iobase = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(clk_data->iobase))) + return PTR_ERR(clk_data->iobase); + + clk_data->onecell_data.num = num_clks; + + *pp_clk_data = clk_data; + + return 0; +} + +static int sg2042_clkgen_probe(struct platform_device *pdev) +{ + struct sg2042_clk_data *clk_data = NULL; + int num_clks; + int ret; + + num_clks = ARRAY_SIZE(sg2042_div_clks_level_1) + + ARRAY_SIZE(sg2042_div_clks_level_2) + + ARRAY_SIZE(sg2042_gate_clks_level_1) + + ARRAY_SIZE(sg2042_gate_clks_level_2) + + ARRAY_SIZE(sg2042_mux_clks); + + ret = sg2042_init_clkdata(pdev, num_clks, &clk_data); + if (ret) + goto error_out; + + /* level-1 gates */ + ret = sg2042_clk_register_gates_fw(&pdev->dev, clk_data, + sg2042_gate_clks_level_1, + ARRAY_SIZE(sg2042_gate_clks_level_1)); + if (ret) + goto error_out; + + /* level-1 div */ + ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_1, + ARRAY_SIZE(sg2042_div_clks_level_1)); + if (ret) + goto error_out; + + /* mux */ + ret = sg2042_clk_register_muxs(&pdev->dev, clk_data, sg2042_mux_clks, + ARRAY_SIZE(sg2042_mux_clks)); + if (ret) + goto error_out; + + /* level 2 div */ + ret = sg2042_clk_register_divs(&pdev->dev, clk_data, sg2042_div_clks_level_2, + ARRAY_SIZE(sg2042_div_clks_level_2)); + if (ret) + goto error_out; + + /* level 2 gate */ + ret = sg2042_clk_register_gates(&pdev->dev, clk_data, sg2042_gate_clks_level_2, + ARRAY_SIZE(sg2042_gate_clks_level_2)); + if (ret) + goto error_out; + + return devm_of_clk_add_hw_provider(&pdev->dev, + of_clk_hw_onecell_get, + &clk_data->onecell_data); + +error_out: + pr_err("%s failed error number %d\n", __func__, ret); + return ret; +} + +static const struct of_device_id sg2042_clkgen_match[] = { + { .compatible = "sophgo,sg2042-clkgen" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sg2042_clkgen_match); + +static struct platform_driver sg2042_clkgen_driver = { + .probe = sg2042_clkgen_probe, + .driver = { + .name = "clk-sophgo-sg2042-clkgen", + .of_match_table = sg2042_clkgen_match, + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(sg2042_clkgen_driver); + +MODULE_AUTHOR("Chen Wang"); +MODULE_DESCRIPTION("Sophgo SG2042 clock generator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/sophgo/clk-sg2042-pll.c b/drivers/clk/sophgo/clk-sg2042-pll.c new file mode 100644 index 000000000000..1537f4f05860 --- /dev/null +++ b/drivers/clk/sophgo/clk-sg2042-pll.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sophgo SG2042 PLL clock Driver + * + * Copyright (C) 2024 Sophgo Technology Inc. + * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/platform_device.h> +#include <asm/div64.h> + +#include <dt-bindings/clock/sophgo,sg2042-pll.h> + +#include "clk-sg2042.h" + +/* Registers defined in SYS_CTRL */ +#define R_PLL_BEGIN 0xC0 +#define R_PLL_STAT (0xC0 - R_PLL_BEGIN) +#define R_PLL_CLKEN_CONTROL (0xC4 - R_PLL_BEGIN) +#define R_MPLL_CONTROL (0xE8 - R_PLL_BEGIN) +#define R_FPLL_CONTROL (0xF4 - R_PLL_BEGIN) +#define R_DPLL0_CONTROL (0xF8 - R_PLL_BEGIN) +#define R_DPLL1_CONTROL (0xFC - R_PLL_BEGIN) + +/** + * struct sg2042_pll_clock - PLL clock + * @hw: clk_hw for initialization + * @id: used to map clk_onecell_data + * @base: used for readl/writel. + * **NOTE**: PLL registers are all in SYS_CTRL! + * @lock: spinlock to protect register access, modification + * of frequency can only be served one at the time. + * @offset_ctrl: offset of pll control registers + * @shift_status_lock: shift of XXX_LOCK in pll status register + * @shift_status_updating: shift of UPDATING_XXX in pll status register + * @shift_enable: shift of XXX_CLK_EN in pll enable register + */ +struct sg2042_pll_clock { + struct clk_hw hw; + + unsigned int id; + void __iomem *base; + /* protect register access */ + spinlock_t *lock; + + u32 offset_ctrl; + u8 shift_status_lock; + u8 shift_status_updating; + u8 shift_enable; +}; + +#define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw) + +#define KHZ 1000UL +#define MHZ (KHZ * KHZ) + +#define REFDIV_MIN 1 +#define REFDIV_MAX 63 +#define FBDIV_MIN 16 +#define FBDIV_MAX 320 + +#define PLL_FREF_SG2042 (25 * MHZ) + +#define PLL_FOUTPOSTDIV_MIN (16 * MHZ) +#define PLL_FOUTPOSTDIV_MAX (3200 * MHZ) + +#define PLL_FOUTVCO_MIN (800 * MHZ) +#define PLL_FOUTVCO_MAX (3200 * MHZ) + +struct sg2042_pll_ctrl { + unsigned long freq; + unsigned int fbdiv; + unsigned int postdiv1; + unsigned int postdiv2; + unsigned int refdiv; +}; + +#define PLLCTRL_FBDIV_MASK GENMASK(27, 16) +#define PLLCTRL_POSTDIV2_MASK GENMASK(14, 12) +#define PLLCTRL_POSTDIV1_MASK GENMASK(10, 8) +#define PLLCTRL_REFDIV_MASK GENMASK(5, 0) + +static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl) +{ + return FIELD_PREP(PLLCTRL_FBDIV_MASK, ctrl->fbdiv) | + FIELD_PREP(PLLCTRL_POSTDIV2_MASK, ctrl->postdiv2) | + FIELD_PREP(PLLCTRL_POSTDIV1_MASK, ctrl->postdiv1) | + FIELD_PREP(PLLCTRL_REFDIV_MASK, ctrl->refdiv); +} + +static inline void sg2042_pll_ctrl_decode(unsigned int reg_value, + struct sg2042_pll_ctrl *ctrl) +{ + ctrl->fbdiv = FIELD_GET(PLLCTRL_FBDIV_MASK, reg_value); + ctrl->refdiv = FIELD_GET(PLLCTRL_REFDIV_MASK, reg_value); + ctrl->postdiv1 = FIELD_GET(PLLCTRL_POSTDIV1_MASK, reg_value); + ctrl->postdiv2 = FIELD_GET(PLLCTRL_POSTDIV2_MASK, reg_value); +} + +static inline void sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en) +{ + u32 value; + + if (en) { + /* wait pll lock */ + if (readl_poll_timeout_atomic(pll->base + R_PLL_STAT, + value, + ((value >> pll->shift_status_lock) & 0x1), + 0, + 100000)) + pr_warn("%s not locked\n", pll->hw.init->name); + + /* wait pll updating */ + if (readl_poll_timeout_atomic(pll->base + R_PLL_STAT, + value, + !((value >> pll->shift_status_updating) & 0x1), + 0, + 100000)) + pr_warn("%s still updating\n", pll->hw.init->name); + + /* enable pll */ + value = readl(pll->base + R_PLL_CLKEN_CONTROL); + writel(value | (1 << pll->shift_enable), pll->base + R_PLL_CLKEN_CONTROL); + } else { + /* disable pll */ + value = readl(pll->base + R_PLL_CLKEN_CONTROL); + writel(value & (~(1 << pll->shift_enable)), pll->base + R_PLL_CLKEN_CONTROL); + } +} + +/** + * sg2042_pll_recalc_rate() - Calculate rate for plls + * @reg_value: current register value + * @parent_rate: parent frequency + * + * This function is used to calculate below "rate" in equation + * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2 + * = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2) + * + * Return: The rate calculated. + */ +static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value, + unsigned long parent_rate) +{ + struct sg2042_pll_ctrl ctrl_table; + u64 numerator, denominator; + + sg2042_pll_ctrl_decode(reg_value, &ctrl_table); + + numerator = (u64)parent_rate * ctrl_table.fbdiv; + denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2; + do_div(numerator, denominator); + return numerator; +} + +/** + * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv, + * look up the postdiv1_2 table to get the closest postdiiv combination. + * @rate: FOUTPOSTDIV + * @prate: parent rate, i.e. FREF + * @fbdiv: FBDIV + * @refdiv: REFDIV + * @postdiv1: POSTDIV1, output + * @postdiv2: POSTDIV2, output + * + * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2 + * for example: + * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8 + * + * See TRM: + * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2) + * So we get following formula to get POSTDIV1 and POSTDIV2: + * POSTDIV = (prate/REFDIV) x FBDIV/rate + * above POSTDIV = POSTDIV1*POSTDIV2 + * + * Return: + * %0 - OK + * %-EINVAL - invalid argument, which means Failed to get the postdivs. + */ +static int sg2042_pll_get_postdiv_1_2(unsigned long rate, + unsigned long prate, + unsigned int fbdiv, + unsigned int refdiv, + unsigned int *postdiv1, + unsigned int *postdiv2) +{ + int index; + u64 tmp0; + + /* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */ + #define POSTDIV_RESULT_INDEX 2 + + static const int postdiv1_2[][3] = { + {2, 4, 8}, {3, 3, 9}, {2, 5, 10}, {2, 6, 12}, + {2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18}, + {4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25}, + {4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36}, + {6, 7, 42}, {7, 7, 49} + }; + + /* prate/REFDIV and result save to tmp0 */ + tmp0 = prate; + do_div(tmp0, refdiv); + + /* ((prate/REFDIV) x FBDIV) and result save to tmp0 */ + tmp0 *= fbdiv; + + /* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */ + do_div(tmp0, rate); + + /* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */ + if (tmp0 <= 7) { + /* (div1 * div2) <= 7, no need to use array search */ + *postdiv1 = tmp0; + *postdiv2 = 1; + return 0; + } + + /* (div1 * div2) > 7, use array search */ + for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) { + if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) { + continue; + } else { + /* found it */ + *postdiv1 = postdiv1_2[index][1]; + *postdiv2 = postdiv1_2[index][0]; + return 0; + } + } + pr_warn("%s can not find in postdiv array!\n", __func__); + return -EINVAL; +} + +/** + * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input + * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl + * register. + * @req_rate: expected output clock rate, i.e. FOUTPISTDIV + * @parent_rate: input parent clock rate, i.e. FREF + * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2 + * + * Return: + * %0 - OK + * %-EINVAL - invalid argument + */ +static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best, + unsigned long req_rate, + unsigned long parent_rate) +{ + unsigned int fbdiv, refdiv, postdiv1, postdiv2; + unsigned long foutpostdiv; + u64 foutvco; + int ret; + u64 tmp; + + if (parent_rate != PLL_FREF_SG2042) { + pr_err("INVALID FREF: %ld\n", parent_rate); + return -EINVAL; + } + + if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) { + pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate); + return -EINVAL; + } + + memset(best, 0, sizeof(struct sg2042_pll_ctrl)); + + for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) { + /* required by hardware: FREF/REFDIV must > 10 */ + tmp = parent_rate; + do_div(tmp, refdiv); + if (tmp <= 10) + continue; + + for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) { + /* + * FOUTVCO = FREF*FBDIV/REFDIV validation + * required by hardware, FOUTVCO must [800MHz, 3200MHz] + */ + foutvco = parent_rate * fbdiv; + do_div(foutvco, refdiv); + if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX) + continue; + + ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate, + fbdiv, refdiv, + &postdiv1, &postdiv2); + if (ret) + continue; + + /* + * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2) + * = FOUTVCO/(POSTDIV1*POSTDIV2) + */ + tmp = foutvco; + do_div(tmp, (postdiv1 * postdiv2)); + foutpostdiv = (unsigned long)tmp; + /* Iterative to approach the expected value */ + if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) { + best->freq = foutpostdiv; + best->refdiv = refdiv; + best->fbdiv = fbdiv; + best->postdiv1 = postdiv1; + best->postdiv2 = postdiv2; + if (foutpostdiv == req_rate) + return 0; + } + continue; + } + } + + if (best->freq == 0) + return -EINVAL; + else + return 0; +} + +/** + * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks + * @hw: ccf use to hook get sg2042_pll_clock + * @parent_rate: parent rate + * + * The is function will be called through clk_get_rate + * and return current rate after decoding reg value + * + * Return: Current rate recalculated. + */ +static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw); + unsigned long rate; + u32 value; + + value = readl(pll->base + pll->offset_ctrl); + rate = sg2042_pll_recalc_rate(value, parent_rate); + + pr_debug("--> %s: pll_recalc_rate: val = %ld\n", + clk_hw_get_name(hw), rate); + return rate; +} + +static long sg2042_clk_pll_round_rate(struct clk_hw *hw, + unsigned long req_rate, + unsigned long *prate) +{ + struct sg2042_pll_ctrl pctrl_table; + unsigned int value; + long proper_rate; + int ret; + + ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate); + if (ret) { + proper_rate = 0; + goto out; + } + + value = sg2042_pll_ctrl_encode(&pctrl_table); + proper_rate = (long)sg2042_pll_recalc_rate(value, *prate); + +out: + pr_debug("--> %s: pll_round_rate: val = %ld\n", + clk_hw_get_name(hw), proper_rate); + return proper_rate; +} + +static int sg2042_clk_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate), + &req->best_parent_rate); + pr_debug("--> %s: pll_determine_rate: val = %ld\n", + clk_hw_get_name(hw), req->rate); + return 0; +} + +static int sg2042_clk_pll_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw); + struct sg2042_pll_ctrl pctrl_table; + unsigned long flags; + u32 value = 0; + int ret; + + spin_lock_irqsave(pll->lock, flags); + + sg2042_pll_enable(pll, 0); + + ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate); + if (ret) { + pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name); + goto out; + } + + value = sg2042_pll_ctrl_encode(&pctrl_table); + + /* write the value to top register */ + writel(value, pll->base + pll->offset_ctrl); + +out: + sg2042_pll_enable(pll, 1); + + spin_unlock_irqrestore(pll->lock, flags); + + pr_debug("--> %s: pll_set_rate: val = 0x%x\n", + clk_hw_get_name(hw), value); + return ret; +} + +static const struct clk_ops sg2042_clk_pll_ops = { + .recalc_rate = sg2042_clk_pll_recalc_rate, + .round_rate = sg2042_clk_pll_round_rate, + .determine_rate = sg2042_clk_pll_determine_rate, + .set_rate = sg2042_clk_pll_set_rate, +}; + +static const struct clk_ops sg2042_clk_pll_ro_ops = { + .recalc_rate = sg2042_clk_pll_recalc_rate, + .round_rate = sg2042_clk_pll_round_rate, +}; + +/* + * Clock initialization macro naming rules: + * FW: use CLK_HW_INIT_FW_NAME + * RO: means Read-Only + */ +#define SG2042_PLL_FW(_id, _name, _parent, _r_ctrl, _shift) \ + { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_FW_NAME( \ + _name, \ + _parent, \ + &sg2042_clk_pll_ops, \ + CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\ + .offset_ctrl = _r_ctrl, \ + .shift_status_lock = 8 + (_shift), \ + .shift_status_updating = _shift, \ + .shift_enable = _shift, \ + } + +#define SG2042_PLL_FW_RO(_id, _name, _parent, _r_ctrl, _shift) \ + { \ + .id = _id, \ + .hw.init = CLK_HW_INIT_FW_NAME( \ + _name, \ + _parent, \ + &sg2042_clk_pll_ro_ops, \ + CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\ + .offset_ctrl = _r_ctrl, \ + .shift_status_lock = 8 + (_shift), \ + .shift_status_updating = _shift, \ + .shift_enable = _shift, \ + } + +static struct sg2042_pll_clock sg2042_pll_clks[] = { + SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main", R_MPLL_CONTROL, 0), + SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main", R_FPLL_CONTROL, 3), + SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0", R_DPLL0_CONTROL, 4), + SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1", R_DPLL1_CONTROL, 5), +}; + +static DEFINE_SPINLOCK(sg2042_clk_lock); + +static int sg2042_clk_register_plls(struct device *dev, + struct sg2042_clk_data *clk_data, + struct sg2042_pll_clock pll_clks[], + int num_pll_clks) +{ + struct sg2042_pll_clock *pll; + struct clk_hw *hw; + int i, ret = 0; + + for (i = 0; i < num_pll_clks; i++) { + pll = &pll_clks[i]; + /* assign these for ops usage during registration */ + pll->base = clk_data->iobase; + pll->lock = &sg2042_clk_lock; + + hw = &pll->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) { + pr_err("failed to register clock %s\n", pll->hw.init->name); + break; + } + + clk_data->onecell_data.hws[pll->id] = hw; + } + + return ret; +} + +static int sg2042_init_clkdata(struct platform_device *pdev, + int num_clks, + struct sg2042_clk_data **pp_clk_data) +{ + struct sg2042_clk_data *clk_data; + + clk_data = devm_kzalloc(&pdev->dev, + struct_size(clk_data, onecell_data.hws, num_clks), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->iobase = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(clk_data->iobase))) + return PTR_ERR(clk_data->iobase); + + clk_data->onecell_data.num = num_clks; + + *pp_clk_data = clk_data; + + return 0; +} + +static int sg2042_pll_probe(struct platform_device *pdev) +{ + struct sg2042_clk_data *clk_data = NULL; + int num_clks; + int ret; + + num_clks = ARRAY_SIZE(sg2042_pll_clks); + + ret = sg2042_init_clkdata(pdev, num_clks, &clk_data); + if (ret) + goto error_out; + + ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks, + num_clks); + if (ret) + goto error_out; + + return devm_of_clk_add_hw_provider(&pdev->dev, + of_clk_hw_onecell_get, + &clk_data->onecell_data); + +error_out: + pr_err("%s failed error number %d\n", __func__, ret); + return ret; +} + +static const struct of_device_id sg2042_pll_match[] = { + { .compatible = "sophgo,sg2042-pll" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sg2042_pll_match); + +static struct platform_driver sg2042_pll_driver = { + .probe = sg2042_pll_probe, + .driver = { + .name = "clk-sophgo-sg2042-pll", + .of_match_table = sg2042_pll_match, + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(sg2042_pll_driver); + +MODULE_AUTHOR("Chen Wang"); +MODULE_DESCRIPTION("Sophgo SG2042 pll clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/sophgo/clk-sg2042-rpgate.c b/drivers/clk/sophgo/clk-sg2042-rpgate.c new file mode 100644 index 000000000000..5b38d4f15525 --- /dev/null +++ b/drivers/clk/sophgo/clk-sg2042-rpgate.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sophgo SG2042 RP clock Driver + * + * Copyright (C) 2024 Sophgo Technology Inc. + * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com> + */ + +#include <linux/array_size.h> +#include <linux/clk-provider.h> +#include <linux/platform_device.h> + +#include <dt-bindings/clock/sophgo,sg2042-rpgate.h> + +#include "clk-sg2042.h" + +#define R_SYSGATE_BEGIN 0x0368 +#define R_RP_RXU_CLK_ENABLE (0x0368 - R_SYSGATE_BEGIN) +#define R_MP0_STATUS_REG (0x0380 - R_SYSGATE_BEGIN) +#define R_MP0_CONTROL_REG (0x0384 - R_SYSGATE_BEGIN) +#define R_MP1_STATUS_REG (0x0388 - R_SYSGATE_BEGIN) +#define R_MP1_CONTROL_REG (0x038C - R_SYSGATE_BEGIN) +#define R_MP2_STATUS_REG (0x0390 - R_SYSGATE_BEGIN) +#define R_MP2_CONTROL_REG (0x0394 - R_SYSGATE_BEGIN) +#define R_MP3_STATUS_REG (0x0398 - R_SYSGATE_BEGIN) +#define R_MP3_CONTROL_REG (0x039C - R_SYSGATE_BEGIN) +#define R_MP4_STATUS_REG (0x03A0 - R_SYSGATE_BEGIN) +#define R_MP4_CONTROL_REG (0x03A4 - R_SYSGATE_BEGIN) +#define R_MP5_STATUS_REG (0x03A8 - R_SYSGATE_BEGIN) +#define R_MP5_CONTROL_REG (0x03AC - R_SYSGATE_BEGIN) +#define R_MP6_STATUS_REG (0x03B0 - R_SYSGATE_BEGIN) +#define R_MP6_CONTROL_REG (0x03B4 - R_SYSGATE_BEGIN) +#define R_MP7_STATUS_REG (0x03B8 - R_SYSGATE_BEGIN) +#define R_MP7_CONTROL_REG (0x03BC - R_SYSGATE_BEGIN) +#define R_MP8_STATUS_REG (0x03C0 - R_SYSGATE_BEGIN) +#define R_MP8_CONTROL_REG (0x03C4 - R_SYSGATE_BEGIN) +#define R_MP9_STATUS_REG (0x03C8 - R_SYSGATE_BEGIN) +#define R_MP9_CONTROL_REG (0x03CC - R_SYSGATE_BEGIN) +#define R_MP10_STATUS_REG (0x03D0 - R_SYSGATE_BEGIN) +#define R_MP10_CONTROL_REG (0x03D4 - R_SYSGATE_BEGIN) +#define R_MP11_STATUS_REG (0x03D8 - R_SYSGATE_BEGIN) +#define R_MP11_CONTROL_REG (0x03DC - R_SYSGATE_BEGIN) +#define R_MP12_STATUS_REG (0x03E0 - R_SYSGATE_BEGIN) +#define R_MP12_CONTROL_REG (0x03E4 - R_SYSGATE_BEGIN) +#define R_MP13_STATUS_REG (0x03E8 - R_SYSGATE_BEGIN) +#define R_MP13_CONTROL_REG (0x03EC - R_SYSGATE_BEGIN) +#define R_MP14_STATUS_REG (0x03F0 - R_SYSGATE_BEGIN) +#define R_MP14_CONTROL_REG (0x03F4 - R_SYSGATE_BEGIN) +#define R_MP15_STATUS_REG (0x03F8 - R_SYSGATE_BEGIN) +#define R_MP15_CONTROL_REG (0x03FC - R_SYSGATE_BEGIN) + +/** + * struct sg2042_rpgate_clock - Gate clock for RP(riscv processors) subsystem + * @hw: clk_hw for initialization + * @id: used to map clk_onecell_data + * @offset_enable: offset of gate enable registers + * @bit_idx: which bit in the register controls gating of this clock + */ +struct sg2042_rpgate_clock { + struct clk_hw hw; + + unsigned int id; + + u32 offset_enable; + u8 bit_idx; +}; + +/* + * Clock initialization macro naming rules: + * FW: use CLK_HW_INIT_FW_NAME + */ +#define SG2042_GATE_FW(_id, _name, _parent, _flags, \ + _r_enable, _bit_idx) { \ + .hw.init = CLK_HW_INIT_FW_NAME( \ + _name, \ + _parent, \ + NULL, \ + _flags), \ + .id = _id, \ + .offset_enable = _r_enable, \ + .bit_idx = _bit_idx, \ + } + +/* + * Gate clocks for RP subsystem (including the MP subsystem), which control + * registers are defined in SYS_CTRL. + */ +static const struct sg2042_rpgate_clock sg2042_gate_rp[] = { + /* downstream of clk_gate_rp_cpu_normal about rxu */ + SG2042_GATE_FW(GATE_CLK_RXU0, "clk_gate_rxu0", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 0), + SG2042_GATE_FW(GATE_CLK_RXU1, "clk_gate_rxu1", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 1), + SG2042_GATE_FW(GATE_CLK_RXU2, "clk_gate_rxu2", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 2), + SG2042_GATE_FW(GATE_CLK_RXU3, "clk_gate_rxu3", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 3), + SG2042_GATE_FW(GATE_CLK_RXU4, "clk_gate_rxu4", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 4), + SG2042_GATE_FW(GATE_CLK_RXU5, "clk_gate_rxu5", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 5), + SG2042_GATE_FW(GATE_CLK_RXU6, "clk_gate_rxu6", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 6), + SG2042_GATE_FW(GATE_CLK_RXU7, "clk_gate_rxu7", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 7), + SG2042_GATE_FW(GATE_CLK_RXU8, "clk_gate_rxu8", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 8), + SG2042_GATE_FW(GATE_CLK_RXU9, "clk_gate_rxu9", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 9), + SG2042_GATE_FW(GATE_CLK_RXU10, "clk_gate_rxu10", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 10), + SG2042_GATE_FW(GATE_CLK_RXU11, "clk_gate_rxu11", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 11), + SG2042_GATE_FW(GATE_CLK_RXU12, "clk_gate_rxu12", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 12), + SG2042_GATE_FW(GATE_CLK_RXU13, "clk_gate_rxu13", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 13), + SG2042_GATE_FW(GATE_CLK_RXU14, "clk_gate_rxu14", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 14), + SG2042_GATE_FW(GATE_CLK_RXU15, "clk_gate_rxu15", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 15), + SG2042_GATE_FW(GATE_CLK_RXU16, "clk_gate_rxu16", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 16), + SG2042_GATE_FW(GATE_CLK_RXU17, "clk_gate_rxu17", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 17), + SG2042_GATE_FW(GATE_CLK_RXU18, "clk_gate_rxu18", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 18), + SG2042_GATE_FW(GATE_CLK_RXU19, "clk_gate_rxu19", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 19), + SG2042_GATE_FW(GATE_CLK_RXU20, "clk_gate_rxu20", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 20), + SG2042_GATE_FW(GATE_CLK_RXU21, "clk_gate_rxu21", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 21), + SG2042_GATE_FW(GATE_CLK_RXU22, "clk_gate_rxu22", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 22), + SG2042_GATE_FW(GATE_CLK_RXU23, "clk_gate_rxu23", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 23), + SG2042_GATE_FW(GATE_CLK_RXU24, "clk_gate_rxu24", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 24), + SG2042_GATE_FW(GATE_CLK_RXU25, "clk_gate_rxu25", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 25), + SG2042_GATE_FW(GATE_CLK_RXU26, "clk_gate_rxu26", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 26), + SG2042_GATE_FW(GATE_CLK_RXU27, "clk_gate_rxu27", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 27), + SG2042_GATE_FW(GATE_CLK_RXU28, "clk_gate_rxu28", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 28), + SG2042_GATE_FW(GATE_CLK_RXU29, "clk_gate_rxu29", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 29), + SG2042_GATE_FW(GATE_CLK_RXU30, "clk_gate_rxu30", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 30), + SG2042_GATE_FW(GATE_CLK_RXU31, "clk_gate_rxu31", "rpgate", + 0, R_RP_RXU_CLK_ENABLE, 31), + + /* downstream of clk_gate_rp_cpu_normal about mp */ + SG2042_GATE_FW(GATE_CLK_MP0, "clk_gate_mp0", "rpgate", + CLK_IS_CRITICAL, R_MP0_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP1, "clk_gate_mp1", "rpgate", + CLK_IS_CRITICAL, R_MP1_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP2, "clk_gate_mp2", "rpgate", + CLK_IS_CRITICAL, R_MP2_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP3, "clk_gate_mp3", "rpgate", + CLK_IS_CRITICAL, R_MP3_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP4, "clk_gate_mp4", "rpgate", + CLK_IS_CRITICAL, R_MP4_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP5, "clk_gate_mp5", "rpgate", + CLK_IS_CRITICAL, R_MP5_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP6, "clk_gate_mp6", "rpgate", + CLK_IS_CRITICAL, R_MP6_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP7, "clk_gate_mp7", "rpgate", + CLK_IS_CRITICAL, R_MP7_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP8, "clk_gate_mp8", "rpgate", + CLK_IS_CRITICAL, R_MP8_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP9, "clk_gate_mp9", "rpgate", + CLK_IS_CRITICAL, R_MP9_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP10, "clk_gate_mp10", "rpgate", + CLK_IS_CRITICAL, R_MP10_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP11, "clk_gate_mp11", "rpgate", + CLK_IS_CRITICAL, R_MP11_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP12, "clk_gate_mp12", "rpgate", + CLK_IS_CRITICAL, R_MP12_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP13, "clk_gate_mp13", "rpgate", + CLK_IS_CRITICAL, R_MP13_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP14, "clk_gate_mp14", "rpgate", + CLK_IS_CRITICAL, R_MP14_CONTROL_REG, 0), + SG2042_GATE_FW(GATE_CLK_MP15, "clk_gate_mp15", "rpgate", + CLK_IS_CRITICAL, R_MP15_CONTROL_REG, 0), +}; + +static DEFINE_SPINLOCK(sg2042_clk_lock); + +static int sg2042_clk_register_rpgates(struct device *dev, + struct sg2042_clk_data *clk_data, + const struct sg2042_rpgate_clock gate_clks[], + int num_gate_clks) +{ + const struct sg2042_rpgate_clock *gate; + struct clk_hw *hw; + int i, ret = 0; + + for (i = 0; i < num_gate_clks; i++) { + gate = &gate_clks[i]; + hw = devm_clk_hw_register_gate_parent_data + (dev, + gate->hw.init->name, + gate->hw.init->parent_data, + gate->hw.init->flags, + clk_data->iobase + gate->offset_enable, + gate->bit_idx, + 0, + &sg2042_clk_lock); + if (IS_ERR(hw)) { + pr_err("failed to register clock %s\n", gate->hw.init->name); + ret = PTR_ERR(hw); + break; + } + + clk_data->onecell_data.hws[gate->id] = hw; + } + + return ret; +} + +static int sg2042_init_clkdata(struct platform_device *pdev, + int num_clks, + struct sg2042_clk_data **pp_clk_data) +{ + struct sg2042_clk_data *clk_data; + + clk_data = devm_kzalloc(&pdev->dev, + struct_size(clk_data, onecell_data.hws, num_clks), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->iobase = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(clk_data->iobase))) + return PTR_ERR(clk_data->iobase); + + clk_data->onecell_data.num = num_clks; + + *pp_clk_data = clk_data; + + return 0; +} + +static int sg2042_rpgate_probe(struct platform_device *pdev) +{ + struct sg2042_clk_data *clk_data = NULL; + int num_clks; + int ret; + + num_clks = ARRAY_SIZE(sg2042_gate_rp); + + ret = sg2042_init_clkdata(pdev, num_clks, &clk_data); + if (ret) + goto error_out; + + ret = sg2042_clk_register_rpgates(&pdev->dev, clk_data, sg2042_gate_rp, + num_clks); + if (ret) + goto error_out; + + return devm_of_clk_add_hw_provider(&pdev->dev, + of_clk_hw_onecell_get, + &clk_data->onecell_data); + +error_out: + pr_err("%s failed error number %d\n", __func__, ret); + return ret; +} + +static const struct of_device_id sg2042_rpgate_match[] = { + { .compatible = "sophgo,sg2042-rpgate" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sg2042_rpgate_match); + +static struct platform_driver sg2042_rpgate_driver = { + .probe = sg2042_rpgate_probe, + .driver = { + .name = "clk-sophgo-sg2042-rpgate", + .of_match_table = sg2042_rpgate_match, + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(sg2042_rpgate_driver); + +MODULE_AUTHOR("Chen Wang"); +MODULE_DESCRIPTION("Sophgo SG2042 rp subsystem clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/sophgo/clk-sg2042.h b/drivers/clk/sophgo/clk-sg2042.h new file mode 100644 index 000000000000..f13fed399c6e --- /dev/null +++ b/drivers/clk/sophgo/clk-sg2042.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _CLK_SOPHGO_SG2042_H_ +#define _CLK_SOPHGO_SG2042_H_ + +#include <linux/io.h> +#include <linux/clk-provider.h> + +/** + * struct sg2042_clk_data - Common data of clock-controller + * @iobase: base address of clock-controller + * @onecell_data: used for adding providers. + */ +struct sg2042_clk_data { + void __iomem *iobase; + struct clk_hw_onecell_data onecell_data; +}; + +#endif /* _CLK_SOPHGO_SG2042_H_ */ diff --git a/drivers/clk/sophgo/clk-sg2044-pll.c b/drivers/clk/sophgo/clk-sg2044-pll.c new file mode 100644 index 000000000000..94c0f519ba6d --- /dev/null +++ b/drivers/clk/sophgo/clk-sg2044-pll.c @@ -0,0 +1,628 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sophgo SG2044 PLL clock controller driver + * + * Copyright (C) 2025 Inochi Amaoto <inochiama@gmail.com> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/math64.h> +#include <linux/mfd/syscon.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spinlock.h> + +#include <dt-bindings/clock/sophgo,sg2044-pll.h> + +/* Low Control part */ +#define PLL_VCOSEL_MASK GENMASK(17, 16) + +/* High Control part */ +#define PLL_FBDIV_MASK GENMASK(11, 0) +#define PLL_REFDIV_MASK GENMASK(17, 12) +#define PLL_POSTDIV1_MASK GENMASK(20, 18) +#define PLL_POSTDIV2_MASK GENMASK(23, 21) + +#define PLL_CALIBRATE_EN BIT(24) +#define PLL_CALIBRATE_MASK GENMASK(29, 27) +#define PLL_CALIBRATE_DEFAULT FIELD_PREP(PLL_CALIBRATE_MASK, 2) +#define PLL_UPDATE_EN BIT(30) + +#define PLL_HIGH_CTRL_MASK \ + (PLL_FBDIV_MASK | PLL_REFDIV_MASK | \ + PLL_POSTDIV1_MASK | PLL_POSTDIV2_MASK | \ + PLL_CALIBRATE_EN | PLL_CALIBRATE_MASK | \ + PLL_UPDATE_EN) + +#define PLL_HIGH_CTRL_OFFSET 4 + +#define PLL_VCOSEL_1G6 0x2 +#define PLL_VCOSEL_2G4 0x3 + +#define PLL_LIMIT_FOUTVCO 0 +#define PLL_LIMIT_FOUT 1 +#define PLL_LIMIT_REFDIV 2 +#define PLL_LIMIT_FBDIV 3 +#define PLL_LIMIT_POSTDIV1 4 +#define PLL_LIMIT_POSTDIV2 5 + +#define for_each_pll_limit_range(_var, _limit) \ + for (_var = (_limit)->min; _var <= (_limit)->max; _var++) + +struct sg2044_pll_limit { + u64 min; + u64 max; +}; + +struct sg2044_pll_internal { + u32 ctrl_offset; + u32 status_offset; + u32 enable_offset; + + u8 status_lock_bit; + u8 status_updating_bit; + u8 enable_bit; + + const struct sg2044_pll_limit *limits; +}; + +struct sg2044_clk_common { + struct clk_hw hw; + struct regmap *regmap; + spinlock_t *lock; + unsigned int id; +}; + +struct sg2044_pll { + struct sg2044_clk_common common; + struct sg2044_pll_internal pll; + unsigned int syscon_offset; +}; + +struct sg2044_pll_desc_data { + struct sg2044_clk_common * const *pll; + u16 num_pll; +}; + +#define SG2044_SYSCON_PLL_OFFSET 0x98 + +struct sg2044_pll_ctrl { + spinlock_t lock; + struct clk_hw_onecell_data data; +}; + +#define hw_to_sg2044_clk_common(_hw) \ + container_of((_hw), struct sg2044_clk_common, hw) + +static inline bool sg2044_clk_fit_limit(u64 value, + const struct sg2044_pll_limit *limit) +{ + return value >= limit->min && value <= limit->max; +} + +static inline struct sg2044_pll *hw_to_sg2044_pll(struct clk_hw *hw) +{ + return container_of(hw_to_sg2044_clk_common(hw), + struct sg2044_pll, common); +} + +static unsigned long sg2044_pll_calc_vco_rate(unsigned long parent_rate, + unsigned long refdiv, + unsigned long fbdiv) +{ + u64 numerator = parent_rate * fbdiv; + + return div64_ul(numerator, refdiv); +} + +static unsigned long sg2044_pll_calc_rate(unsigned long parent_rate, + unsigned long refdiv, + unsigned long fbdiv, + unsigned long postdiv1, + unsigned long postdiv2) +{ + u64 numerator, denominator; + + numerator = parent_rate * fbdiv; + denominator = refdiv * (postdiv1 + 1) * (postdiv2 + 1); + + return div64_u64(numerator, denominator); +} + +static unsigned long sg2044_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sg2044_pll *pll = hw_to_sg2044_pll(hw); + u32 value; + int ret; + + ret = regmap_read(pll->common.regmap, + pll->syscon_offset + pll->pll.ctrl_offset + PLL_HIGH_CTRL_OFFSET, + &value); + if (ret < 0) + return 0; + + return sg2044_pll_calc_rate(parent_rate, + FIELD_GET(PLL_REFDIV_MASK, value), + FIELD_GET(PLL_FBDIV_MASK, value), + FIELD_GET(PLL_POSTDIV1_MASK, value), + FIELD_GET(PLL_POSTDIV2_MASK, value)); +} + +static bool pll_is_better_rate(unsigned long target, unsigned long now, + unsigned long best) +{ + return abs_diff(target, now) < abs_diff(target, best); +} + +static int sg2042_pll_compute_postdiv(const struct sg2044_pll_limit *limits, + unsigned long target, + unsigned long parent_rate, + unsigned int refdiv, + unsigned int fbdiv, + unsigned int *postdiv1, + unsigned int *postdiv2) +{ + unsigned int div1, div2; + unsigned long tmp, best_rate = 0; + unsigned int best_div1 = 0, best_div2 = 0; + + for_each_pll_limit_range(div2, &limits[PLL_LIMIT_POSTDIV2]) { + for_each_pll_limit_range(div1, &limits[PLL_LIMIT_POSTDIV1]) { + tmp = sg2044_pll_calc_rate(parent_rate, + refdiv, fbdiv, + div1, div2); + + if (tmp > target) + continue; + + if (pll_is_better_rate(target, tmp, best_rate)) { + best_div1 = div1; + best_div2 = div2; + best_rate = tmp; + + if (tmp == target) + goto find; + } + } + } + +find: + if (best_rate) { + *postdiv1 = best_div1; + *postdiv2 = best_div2; + return 0; + } + + return -EINVAL; +} + +static int sg2044_compute_pll_setting(const struct sg2044_pll_limit *limits, + unsigned long req_rate, + unsigned long parent_rate, + unsigned int *value) +{ + unsigned int refdiv, fbdiv, postdiv1, postdiv2; + unsigned int best_refdiv, best_fbdiv, best_postdiv1, best_postdiv2; + unsigned long tmp, best_rate = 0; + int ret; + + for_each_pll_limit_range(fbdiv, &limits[PLL_LIMIT_FBDIV]) { + for_each_pll_limit_range(refdiv, &limits[PLL_LIMIT_REFDIV]) { + u64 vco = sg2044_pll_calc_vco_rate(parent_rate, + refdiv, fbdiv); + if (!sg2044_clk_fit_limit(vco, &limits[PLL_LIMIT_FOUTVCO])) + continue; + + ret = sg2042_pll_compute_postdiv(limits, + req_rate, parent_rate, + refdiv, fbdiv, + &postdiv1, &postdiv2); + if (ret) + continue; + + tmp = sg2044_pll_calc_rate(parent_rate, + refdiv, fbdiv, + postdiv1, postdiv2); + + if (pll_is_better_rate(req_rate, tmp, best_rate)) { + best_refdiv = refdiv; + best_fbdiv = fbdiv; + best_postdiv1 = postdiv1; + best_postdiv2 = postdiv2; + best_rate = tmp; + + if (tmp == req_rate) + goto find; + } + } + } + +find: + if (best_rate) { + *value = FIELD_PREP(PLL_REFDIV_MASK, best_refdiv) | + FIELD_PREP(PLL_FBDIV_MASK, best_fbdiv) | + FIELD_PREP(PLL_POSTDIV1_MASK, best_postdiv1) | + FIELD_PREP(PLL_POSTDIV2_MASK, best_postdiv2); + return 0; + } + + return -EINVAL; +} + +static int sg2044_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct sg2044_pll *pll = hw_to_sg2044_pll(hw); + unsigned int value; + u64 target; + int ret; + + target = clamp(req->rate, pll->pll.limits[PLL_LIMIT_FOUT].min, + pll->pll.limits[PLL_LIMIT_FOUT].max); + + ret = sg2044_compute_pll_setting(pll->pll.limits, target, + req->best_parent_rate, &value); + if (ret < 0) + return ret; + + req->rate = sg2044_pll_calc_rate(req->best_parent_rate, + FIELD_GET(PLL_REFDIV_MASK, value), + FIELD_GET(PLL_FBDIV_MASK, value), + FIELD_GET(PLL_POSTDIV1_MASK, value), + FIELD_GET(PLL_POSTDIV2_MASK, value)); + + return 0; +} + +static int sg2044_pll_poll_update(struct sg2044_pll *pll) +{ + int ret; + unsigned int value; + + ret = regmap_read_poll_timeout_atomic(pll->common.regmap, + pll->syscon_offset + pll->pll.status_offset, + value, + (value & BIT(pll->pll.status_lock_bit)), + 1, 100000); + if (ret) + return ret; + + return regmap_read_poll_timeout_atomic(pll->common.regmap, + pll->syscon_offset + pll->pll.status_offset, + value, + (!(value & BIT(pll->pll.status_updating_bit))), + 1, 100000); +} + +static int sg2044_pll_enable(struct sg2044_pll *pll, bool en) +{ + if (en) { + if (sg2044_pll_poll_update(pll) < 0) + pr_warn("%s: fail to lock pll\n", clk_hw_get_name(&pll->common.hw)); + + return regmap_set_bits(pll->common.regmap, + pll->syscon_offset + pll->pll.enable_offset, + BIT(pll->pll.enable_bit)); + } + + return regmap_clear_bits(pll->common.regmap, + pll->syscon_offset + pll->pll.enable_offset, + BIT(pll->pll.enable_bit)); +} + +static int sg2044_pll_update_vcosel(struct sg2044_pll *pll, u64 rate) +{ + unsigned int sel; + + if (rate < U64_C(2400000000)) + sel = PLL_VCOSEL_1G6; + else + sel = PLL_VCOSEL_2G4; + + return regmap_write_bits(pll->common.regmap, + pll->syscon_offset + pll->pll.ctrl_offset, + PLL_VCOSEL_MASK, + FIELD_PREP(PLL_VCOSEL_MASK, sel)); +} + +static int sg2044_pll_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct sg2044_pll *pll = hw_to_sg2044_pll(hw); + unsigned int value; + u64 vco; + int ret; + + ret = sg2044_compute_pll_setting(pll->pll.limits, rate, + parent_rate, &value); + if (ret < 0) + return ret; + + vco = sg2044_pll_calc_vco_rate(parent_rate, + FIELD_GET(PLL_REFDIV_MASK, value), + FIELD_GET(PLL_FBDIV_MASK, value)); + + value |= PLL_CALIBRATE_EN; + value |= PLL_CALIBRATE_DEFAULT; + value |= PLL_UPDATE_EN; + + guard(spinlock_irqsave)(pll->common.lock); + + ret = sg2044_pll_enable(pll, false); + if (ret) + return ret; + + sg2044_pll_update_vcosel(pll, vco); + + regmap_write_bits(pll->common.regmap, + pll->syscon_offset + pll->pll.ctrl_offset + + PLL_HIGH_CTRL_OFFSET, + PLL_HIGH_CTRL_MASK, value); + + sg2044_pll_enable(pll, true); + + return ret; +} + +static const struct clk_ops sg2044_pll_ops = { + .recalc_rate = sg2044_pll_recalc_rate, + .determine_rate = sg2044_pll_determine_rate, + .set_rate = sg2044_pll_set_rate, +}; + +static const struct clk_ops sg2044_pll_ro_ops = { + .recalc_rate = sg2044_pll_recalc_rate, +}; + +#define SG2044_CLK_COMMON_PDATA(_id, _name, _parents, _op, _flags) \ + { \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, \ + _op, (_flags)), \ + .id = (_id), \ + } + +#define DEFINE_SG2044_PLL(_id, _name, _parent, _flags, \ + _ctrl_offset, \ + _status_offset, _status_lock_bit, \ + _status_updating_bit, \ + _enable_offset, _enable_bit, \ + _limits) \ + struct sg2044_pll _name = { \ + .common = SG2044_CLK_COMMON_PDATA(_id, #_name, _parent, \ + &sg2044_pll_ops, \ + (_flags)), \ + .pll = { \ + .ctrl_offset = (_ctrl_offset), \ + .status_offset = (_status_offset), \ + .enable_offset = (_enable_offset), \ + .status_lock_bit = (_status_lock_bit), \ + .status_updating_bit = (_status_updating_bit), \ + .enable_bit = (_enable_bit), \ + .limits = (_limits), \ + }, \ + } + +#define DEFINE_SG2044_PLL_RO(_id, _name, _parent, _flags, \ + _ctrl_offset, \ + _status_offset, _status_lock_bit, \ + _status_updating_bit, \ + _enable_offset, _enable_bit, \ + _limits) \ + struct sg2044_pll _name = { \ + .common = SG2044_CLK_COMMON_PDATA(_id, #_name, _parent, \ + &sg2044_pll_ro_ops, \ + (_flags)), \ + .pll = { \ + .ctrl_offset = (_ctrl_offset), \ + .status_offset = (_status_offset), \ + .enable_offset = (_enable_offset), \ + .status_lock_bit = (_status_lock_bit), \ + .status_updating_bit = (_status_updating_bit), \ + .enable_bit = (_enable_bit), \ + .limits = (_limits), \ + }, \ + } + +static const struct clk_parent_data osc_parents[] = { + { .index = 0 }, +}; + +static const struct sg2044_pll_limit pll_limits[] = { + [PLL_LIMIT_FOUTVCO] = { + .min = U64_C(1600000000), + .max = U64_C(3200000000), + }, + [PLL_LIMIT_FOUT] = { + .min = U64_C(25000), + .max = U64_C(3200000000), + }, + [PLL_LIMIT_REFDIV] = { + .min = U64_C(1), + .max = U64_C(63), + }, + [PLL_LIMIT_FBDIV] = { + .min = U64_C(8), + .max = U64_C(1066), + }, + [PLL_LIMIT_POSTDIV1] = { + .min = U64_C(0), + .max = U64_C(7), + }, + [PLL_LIMIT_POSTDIV2] = { + .min = U64_C(0), + .max = U64_C(7), + }, +}; + +static DEFINE_SG2044_PLL_RO(CLK_FPLL0, clk_fpll0, osc_parents, CLK_IS_CRITICAL, + 0x58, 0x00, 22, 6, + 0x04, 6, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_FPLL1, clk_fpll1, osc_parents, CLK_IS_CRITICAL, + 0x60, 0x00, 23, 7, + 0x04, 7, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_FPLL2, clk_fpll2, osc_parents, CLK_IS_CRITICAL, + 0x20, 0x08, 16, 0, + 0x0c, 0, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL0, clk_dpll0, osc_parents, CLK_IS_CRITICAL, + 0x68, 0x00, 24, 8, + 0x04, 8, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL1, clk_dpll1, osc_parents, CLK_IS_CRITICAL, + 0x70, 0x00, 25, 9, + 0x04, 9, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL2, clk_dpll2, osc_parents, CLK_IS_CRITICAL, + 0x78, 0x00, 26, 10, + 0x04, 10, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL3, clk_dpll3, osc_parents, CLK_IS_CRITICAL, + 0x80, 0x00, 27, 11, + 0x04, 11, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL4, clk_dpll4, osc_parents, CLK_IS_CRITICAL, + 0x88, 0x00, 28, 12, + 0x04, 12, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL5, clk_dpll5, osc_parents, CLK_IS_CRITICAL, + 0x90, 0x00, 29, 13, + 0x04, 13, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL6, clk_dpll6, osc_parents, CLK_IS_CRITICAL, + 0x98, 0x00, 30, 14, + 0x04, 14, pll_limits); + +static DEFINE_SG2044_PLL_RO(CLK_DPLL7, clk_dpll7, osc_parents, CLK_IS_CRITICAL, + 0xa0, 0x00, 31, 15, + 0x04, 15, pll_limits); + +static DEFINE_SG2044_PLL(CLK_MPLL0, clk_mpll0, osc_parents, CLK_IS_CRITICAL, + 0x28, 0x00, 16, 0, + 0x04, 0, pll_limits); + +static DEFINE_SG2044_PLL(CLK_MPLL1, clk_mpll1, osc_parents, CLK_IS_CRITICAL, + 0x30, 0x00, 17, 1, + 0x04, 1, pll_limits); + +static DEFINE_SG2044_PLL(CLK_MPLL2, clk_mpll2, osc_parents, CLK_IS_CRITICAL, + 0x38, 0x00, 18, 2, + 0x04, 2, pll_limits); + +static DEFINE_SG2044_PLL(CLK_MPLL3, clk_mpll3, osc_parents, CLK_IS_CRITICAL, + 0x40, 0x00, 19, 3, + 0x04, 3, pll_limits); + +static DEFINE_SG2044_PLL(CLK_MPLL4, clk_mpll4, osc_parents, CLK_IS_CRITICAL, + 0x48, 0x00, 20, 4, + 0x04, 4, pll_limits); + +static DEFINE_SG2044_PLL(CLK_MPLL5, clk_mpll5, osc_parents, CLK_IS_CRITICAL, + 0x50, 0x00, 21, 5, + 0x04, 5, pll_limits); + +static struct sg2044_clk_common * const sg2044_pll_commons[] = { + &clk_fpll0.common, + &clk_fpll1.common, + &clk_fpll2.common, + &clk_dpll0.common, + &clk_dpll1.common, + &clk_dpll2.common, + &clk_dpll3.common, + &clk_dpll4.common, + &clk_dpll5.common, + &clk_dpll6.common, + &clk_dpll7.common, + &clk_mpll0.common, + &clk_mpll1.common, + &clk_mpll2.common, + &clk_mpll3.common, + &clk_mpll4.common, + &clk_mpll5.common, +}; + +static int sg2044_pll_init_ctrl(struct device *dev, struct regmap *regmap, + struct sg2044_pll_ctrl *ctrl, + const struct sg2044_pll_desc_data *desc) +{ + int ret, i; + + spin_lock_init(&ctrl->lock); + + for (i = 0; i < desc->num_pll; i++) { + struct sg2044_clk_common *common = desc->pll[i]; + struct sg2044_pll *pll = hw_to_sg2044_pll(&common->hw); + + common->lock = &ctrl->lock; + common->regmap = regmap; + pll->syscon_offset = SG2044_SYSCON_PLL_OFFSET; + + ret = devm_clk_hw_register(dev, &common->hw); + if (ret) + return ret; + + ctrl->data.hws[common->id] = &common->hw; + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &ctrl->data); +} + +static int sg2044_pll_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sg2044_pll_ctrl *ctrl; + const struct sg2044_pll_desc_data *desc; + struct regmap *regmap; + + regmap = device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "fail to get the regmap for PLL\n"); + + desc = (const struct sg2044_pll_desc_data *)platform_get_device_id(pdev)->driver_data; + if (!desc) + return dev_err_probe(dev, -EINVAL, "no match data for platform\n"); + + ctrl = devm_kzalloc(dev, struct_size(ctrl, data.hws, desc->num_pll), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->data.num = desc->num_pll; + + return sg2044_pll_init_ctrl(dev, regmap, ctrl, desc); +} + +static const struct sg2044_pll_desc_data sg2044_pll_desc_data = { + .pll = sg2044_pll_commons, + .num_pll = ARRAY_SIZE(sg2044_pll_commons), +}; + +static const struct platform_device_id sg2044_pll_match[] = { + { .name = "sg2044-pll", + .driver_data = (unsigned long)&sg2044_pll_desc_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, sg2044_pll_match); + +static struct platform_driver sg2044_clk_driver = { + .probe = sg2044_pll_probe, + .driver = { + .name = "sg2044-pll", + }, + .id_table = sg2044_pll_match, +}; +module_platform_driver(sg2044_clk_driver); + +MODULE_AUTHOR("Inochi Amaoto <inochiama@gmail.com>"); +MODULE_DESCRIPTION("Sophgo SG2044 pll clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/sophgo/clk-sg2044.c b/drivers/clk/sophgo/clk-sg2044.c new file mode 100644 index 000000000000..f67f99c926b6 --- /dev/null +++ b/drivers/clk/sophgo/clk-sg2044.c @@ -0,0 +1,1812 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sophgo SG2044 clock controller driver + * + * Copyright (C) 2025 Inochi Amaoto <inochiama@gmail.com> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/math64.h> +#include <linux/mfd/syscon.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spinlock.h> + +#include <dt-bindings/clock/sophgo,sg2044-clk.h> + +#define DIV_ASSERT BIT(0) +#define DIV_FACTOR_REG_SOURCE BIT(3) +#define DIV_BRANCH_EN BIT(4) + +#define DIV_ASSERT_TIME 2 + +struct sg2044_div_internal { + u32 offset; + u32 initval; + u8 shift; + u8 width; + u16 flags; +}; + +struct sg2044_mux_internal { + const u32 *table; + u32 offset; + u16 shift; + u16 flags; +}; + +struct sg2044_gate_internal { + u32 offset; + u16 shift; + u16 flags; +}; + +struct sg2044_clk_common { + struct clk_hw hw; + void __iomem *base; + spinlock_t *lock; + unsigned int id; +}; + +struct sg2044_div { + struct sg2044_clk_common common; + struct sg2044_div_internal div; +}; + +struct sg2044_mux { + struct sg2044_clk_common common; + struct sg2044_mux_internal mux; + struct notifier_block nb; + u8 saved_parent; +}; + +struct sg2044_gate { + struct sg2044_clk_common common; + struct sg2044_gate_internal gate; +}; + +struct sg2044_clk_ctrl { + spinlock_t lock; + struct clk_hw_onecell_data data; +}; + +struct sg2044_clk_desc_data { + struct sg2044_clk_common * const *pll; + struct sg2044_clk_common * const *div; + struct sg2044_clk_common * const *mux; + struct sg2044_clk_common * const *gate; + u16 num_pll; + u16 num_div; + u16 num_mux; + u16 num_gate; +}; + +#define hw_to_sg2044_clk_common(_hw) \ + container_of((_hw), struct sg2044_clk_common, hw) + +static inline struct sg2044_div *hw_to_sg2044_div(struct clk_hw *hw) +{ + return container_of(hw_to_sg2044_clk_common(hw), + struct sg2044_div, common); +} + +static u32 sg2044_div_get_reg_div(u32 reg, struct sg2044_div_internal *div) +{ + if ((reg & DIV_FACTOR_REG_SOURCE)) + return (reg >> div->shift) & clk_div_mask(div->width); + + return div->initval == 0 ? 1 : div->initval; +} + +static unsigned long _sg2044_div_recalc_rate(struct sg2044_clk_common *common, + struct sg2044_div_internal *div, + unsigned long parent_rate) +{ + u32 reg = readl(common->base + div->offset); + u32 val = sg2044_div_get_reg_div(reg, div); + + return divider_recalc_rate(&common->hw, parent_rate, val, NULL, + div->flags, div->width); +} + +static unsigned long sg2044_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sg2044_div *div = hw_to_sg2044_div(hw); + + return _sg2044_div_recalc_rate(&div->common, &div->div, + parent_rate); +} + +static int _sg2044_div_determine_rate(struct sg2044_clk_common *common, + struct sg2044_div_internal *div, + struct clk_rate_request *req) +{ + if (div->flags & CLK_DIVIDER_READ_ONLY) { + u32 reg = readl(common->base + div->offset); + u32 val = sg2044_div_get_reg_div(reg, div); + + return divider_ro_determine_rate(&common->hw, req, NULL, + div->width, div->flags, + val); + } + + return divider_determine_rate(&common->hw, req, NULL, + div->width, div->flags); +} + +static int sg2044_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct sg2044_div *div = hw_to_sg2044_div(hw); + + return _sg2044_div_determine_rate(&div->common, &div->div, req); +} + +static void sg2044_div_set_reg_div(struct sg2044_clk_common *common, + struct sg2044_div_internal *div, + u32 value) +{ + void __iomem *addr = common->base + div->offset; + u32 reg; + + reg = readl(addr); + + /* assert */ + reg &= ~DIV_ASSERT; + writel(reg, addr); + + /* set value */ + reg = readl(addr); + reg &= ~(clk_div_mask(div->width) << div->shift); + reg |= (value << div->shift) | DIV_FACTOR_REG_SOURCE; + writel(reg, addr); + + /* de-assert */ + reg |= DIV_ASSERT; + writel(reg, addr); +} + +static int sg2044_div_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct sg2044_div *div = hw_to_sg2044_div(hw); + u32 value; + + value = divider_get_val(rate, parent_rate, NULL, + div->div.width, div->div.flags); + + guard(spinlock_irqsave)(div->common.lock); + + sg2044_div_set_reg_div(&div->common, &div->div, value); + + return 0; +} + +static int sg2044_div_enable(struct clk_hw *hw) +{ + struct sg2044_div *div = hw_to_sg2044_div(hw); + void __iomem *addr = div->common.base + div->div.offset; + u32 value; + + guard(spinlock_irqsave)(div->common.lock); + + value = readl(addr); + value |= DIV_BRANCH_EN; + writel(value, addr); + + return 0; +} + +static void sg2044_div_disable(struct clk_hw *hw) +{ + struct sg2044_div *div = hw_to_sg2044_div(hw); + void __iomem *addr = div->common.base + div->div.offset; + u32 value; + + guard(spinlock_irqsave)(div->common.lock); + + value = readl(addr); + value &= ~DIV_BRANCH_EN; + writel(value, addr); +} + +static int sg2044_div_is_enabled(struct clk_hw *hw) +{ + struct sg2044_div *div = hw_to_sg2044_div(hw); + + return readl(div->common.base + div->div.offset) & DIV_BRANCH_EN; +} + +static const struct clk_ops sg2044_gateable_div_ops = { + .enable = sg2044_div_enable, + .disable = sg2044_div_disable, + .is_enabled = sg2044_div_is_enabled, + .recalc_rate = sg2044_div_recalc_rate, + .determine_rate = sg2044_div_determine_rate, + .set_rate = sg2044_div_set_rate, +}; + +static const struct clk_ops sg2044_div_ops = { + .recalc_rate = sg2044_div_recalc_rate, + .determine_rate = sg2044_div_determine_rate, + .set_rate = sg2044_div_set_rate, +}; + +static const struct clk_ops sg2044_div_ro_ops = { + .recalc_rate = sg2044_div_recalc_rate, + .determine_rate = sg2044_div_determine_rate, +}; + +static inline struct sg2044_mux *hw_to_sg2044_mux(struct clk_hw *hw) +{ + return container_of(hw_to_sg2044_clk_common(hw), + struct sg2044_mux, common); +} + +static inline struct sg2044_mux *nb_to_sg2044_mux(struct notifier_block *nb) +{ + return container_of(nb, struct sg2044_mux, nb); +} + +static const u32 sg2044_mux_table[] = {0, 1}; + +static int sg2044_mux_notifier_cb(struct notifier_block *nb, + unsigned long event, + void *data) +{ + struct sg2044_mux *mux = nb_to_sg2044_mux(nb); + const struct clk_ops *ops = &clk_mux_ops; + struct clk_notifier_data *ndata = data; + struct clk_hw *hw = __clk_get_hw(ndata->clk); + int ret = 0; + + if (event == PRE_RATE_CHANGE) { + mux->saved_parent = ops->get_parent(hw); + if (mux->saved_parent) + ret = ops->set_parent(hw, 0); + } else if (event == POST_RATE_CHANGE) { + ret = ops->set_parent(hw, mux->saved_parent); + } + + return notifier_from_errno(ret); +} + +static inline struct sg2044_gate *hw_to_sg2044_gate(struct clk_hw *hw) +{ + return container_of(hw_to_sg2044_clk_common(hw), + struct sg2044_gate, common); +} + +#define SG2044_CLK_COMMON_PDATA(_id, _name, _parents, _op, _flags) \ + { \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, \ + _op, (_flags)), \ + .id = (_id), \ + } + +#define SG2044_CLK_COMMON_PHWS(_id, _name, _parents, _op, _flags) \ + { \ + .hw.init = CLK_HW_INIT_PARENTS_HW(_name, _parents, \ + _op, (_flags)), \ + .id = (_id), \ + } + +#define DEFINE_SG2044_GATEABLE_DIV(_id, _name, _parent, _flags, \ + _div_offset, _div_shift, _div_width, \ + _div_flags, _div_initval) \ + struct sg2044_div _name = { \ + .common = SG2044_CLK_COMMON_PDATA(_id, #_name, _parent, \ + &sg2044_gateable_div_ops,\ + (_flags)), \ + .div = { \ + .offset = (_div_offset), \ + .initval = (_div_initval), \ + .shift = (_div_shift), \ + .width = (_div_width), \ + .flags = (_div_flags), \ + }, \ + } + +#define DEFINE_SG2044_DIV(_id, _name, _parent, _flags, \ + _div_offset, _div_shift, _div_width, \ + _div_flags, _div_initval) \ + struct sg2044_div _name = { \ + .common = SG2044_CLK_COMMON_PHWS(_id, #_name, _parent, \ + &sg2044_div_ops, \ + (_flags)), \ + .div = { \ + .offset = (_div_offset), \ + .initval = (_div_initval), \ + .shift = (_div_shift), \ + .width = (_div_width), \ + .flags = (_div_flags), \ + }, \ + } + +#define DEFINE_SG2044_DIV_PDATA(_id, _name, _parent, _flags, \ + _div_offset, _div_shift, _div_width, \ + _div_flags, _div_initval) \ + struct sg2044_div _name = { \ + .common = SG2044_CLK_COMMON_PDATA(_id, #_name, _parent, \ + &sg2044_div_ops, \ + (_flags)), \ + .div = { \ + .offset = (_div_offset), \ + .initval = (_div_initval), \ + .shift = (_div_shift), \ + .width = (_div_width), \ + .flags = (_div_flags), \ + }, \ + } + +#define DEFINE_SG2044_DIV_RO(_id, _name, _parent, _flags, \ + _div_offset, _div_shift, _div_width, \ + _div_flags, _div_initval) \ + struct sg2044_div _name = { \ + .common = SG2044_CLK_COMMON_PDATA(_id, #_name, _parent, \ + &sg2044_div_ro_ops, \ + (_flags)), \ + .div = { \ + .offset = (_div_offset), \ + .initval = (_div_initval), \ + .shift = (_div_shift), \ + .width = (_div_width), \ + .flags = (_div_flags) | CLK_DIVIDER_READ_ONLY,\ + }, \ + } + +#define DEFINE_SG2044_MUX(_id, _name, _parent, _flags, \ + _mux_offset, _mux_shift, \ + _mux_table, _mux_flags) \ + struct sg2044_mux _name = { \ + .common = SG2044_CLK_COMMON_PDATA(_id, #_name, _parent, \ + &clk_mux_ops, (_flags)),\ + .mux = { \ + .table = (_mux_table), \ + .offset = (_mux_offset), \ + .shift = (_mux_shift), \ + .flags = (_mux_flags), \ + }, \ + } + +#define DEFINE_SG2044_GATE(_id, _name, _parent, _flags, \ + _gate_offset, _gate_shift, _gate_flags) \ + struct sg2044_gate _name = { \ + .common = SG2044_CLK_COMMON_PHWS(_id, #_name, _parent, \ + &clk_gate_ops, (_flags)),\ + .gate = { \ + .offset = (_gate_offset), \ + .shift = (_gate_shift), \ + .flags = (_gate_flags), \ + }, \ + } + +static const struct clk_parent_data clk_fpll0_parent[] = { + { .fw_name = "fpll0" }, +}; + +static const struct clk_parent_data clk_fpll1_parent[] = { + { .fw_name = "fpll1" }, +}; + +static const struct clk_parent_data clk_fpll2_parent[] = { + { .fw_name = "fpll2" }, +}; + +static const struct clk_parent_data clk_dpll0_parent[] = { + { .fw_name = "dpll0" }, +}; + +static const struct clk_parent_data clk_dpll1_parent[] = { + { .fw_name = "dpll1" }, +}; + +static const struct clk_parent_data clk_dpll2_parent[] = { + { .fw_name = "dpll2" }, +}; + +static const struct clk_parent_data clk_dpll3_parent[] = { + { .fw_name = "dpll3" }, +}; + +static const struct clk_parent_data clk_dpll4_parent[] = { + { .fw_name = "dpll4" }, +}; + +static const struct clk_parent_data clk_dpll5_parent[] = { + { .fw_name = "dpll5" }, +}; + +static const struct clk_parent_data clk_dpll6_parent[] = { + { .fw_name = "dpll6" }, +}; + +static const struct clk_parent_data clk_dpll7_parent[] = { + { .fw_name = "dpll7" }, +}; + +static const struct clk_parent_data clk_mpll0_parent[] = { + { .fw_name = "mpll0" }, +}; + +static const struct clk_parent_data clk_mpll1_parent[] = { + { .fw_name = "mpll1" }, +}; + +static const struct clk_parent_data clk_mpll2_parent[] = { + { .fw_name = "mpll2" }, +}; + +static const struct clk_parent_data clk_mpll3_parent[] = { + { .fw_name = "mpll3" }, +}; + +static const struct clk_parent_data clk_mpll4_parent[] = { + { .fw_name = "mpll4" }, +}; + +static const struct clk_parent_data clk_mpll5_parent[] = { + { .fw_name = "mpll5" }, +}; + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_AP_SYS_FIXED, clk_div_ap_sys_fixed, + clk_fpll0_parent, 0, + 0x044, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_AP_SYS_MAIN, clk_div_ap_sys_main, + clk_mpll0_parent, 0, + 0x040, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_RP_SYS_FIXED, clk_div_rp_sys_fixed, + clk_fpll0_parent, 0, + 0x050, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_RP_SYS_MAIN, clk_div_rp_sys_main, + clk_mpll1_parent, 0, + 0x04c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_TPU_SYS_FIXED, clk_div_tpu_sys_fixed, + clk_fpll0_parent, 0, + 0x058, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 2); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_TPU_SYS_MAIN, clk_div_tpu_sys_main, + clk_mpll2_parent, 0, + 0x054, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_NOC_SYS_FIXED, clk_div_noc_sys_fixed, + clk_fpll0_parent, 0, + 0x070, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_NOC_SYS_MAIN, clk_div_noc_sys_main, + clk_mpll3_parent, 0, + 0x06c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_VC_SRC0_FIXED, clk_div_vc_src0_fixed, + clk_fpll0_parent, 0, + 0x078, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 2); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_VC_SRC0_MAIN, clk_div_vc_src0_main, + clk_mpll4_parent, 0, + 0x074, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_VC_SRC1_FIXED, clk_div_vc_src1_fixed, + clk_fpll0_parent, 0, + 0x080, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 3); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_VC_SRC1_MAIN, clk_div_vc_src1_main, + clk_mpll5_parent, 0, + 0x07c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_CXP_MAC_FIXED, clk_div_cxp_mac_fixed, + clk_fpll0_parent, 0, + 0x088, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 2); + +static DEFINE_SG2044_GATEABLE_DIV(CLK_DIV_CXP_MAC_MAIN, clk_div_cxp_mac_main, + clk_fpll1_parent, 0, + 0x084, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_IS_CRITICAL, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR0_FIXED, clk_div_ddr0_fixed, + clk_fpll0_parent, 0, + 0x124, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR0_MAIN, clk_div_ddr0_main, + clk_dpll0_parent, 0, + 0x120, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR1_FIXED, clk_div_ddr1_fixed, + clk_fpll0_parent, 0, + 0x12c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR1_MAIN, clk_div_ddr1_main, + clk_dpll1_parent, 0, + 0x128, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR2_FIXED, clk_div_ddr2_fixed, + clk_fpll0_parent, 0, + 0x134, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR2_MAIN, clk_div_ddr2_main, + clk_dpll2_parent, 0, + 0x130, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR3_FIXED, clk_div_ddr3_fixed, + clk_fpll0_parent, 0, + 0x13c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR3_MAIN, clk_div_ddr3_main, + clk_dpll3_parent, 0, + 0x138, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR4_FIXED, clk_div_ddr4_fixed, + clk_fpll0_parent, 0, + 0x144, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR4_MAIN, clk_div_ddr4_main, + clk_dpll4_parent, 0, + 0x140, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR5_FIXED, clk_div_ddr5_fixed, + clk_fpll0_parent, 0, + 0x14c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR5_MAIN, clk_div_ddr5_main, + clk_dpll5_parent, 0, + 0x148, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR6_FIXED, clk_div_ddr6_fixed, + clk_fpll0_parent, 0, + 0x154, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR6_MAIN, clk_div_ddr6_main, + clk_dpll6_parent, 0, + 0x150, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR7_FIXED, clk_div_ddr7_fixed, + clk_fpll0_parent, 0, + 0x15c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static DEFINE_SG2044_DIV_RO(CLK_DIV_DDR7_MAIN, clk_div_ddr7_main, + clk_dpll7_parent, 0, + 0x158, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_TOP_50M, clk_div_top_50m, + clk_fpll0_parent, 0, + 0x048, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 40); + +static const struct clk_hw *clk_div_top_50m_parent[] = { + &clk_div_top_50m.common.hw, +}; + +static DEFINE_SG2044_DIV_RO(CLK_DIV_TOP_AXI0, clk_div_top_axi0, + clk_fpll0_parent, 0, + 0x118, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 20); + +static const struct clk_hw *clk_div_top_axi0_parent[] = { + &clk_div_top_axi0.common.hw, +}; + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_TOP_AXI_HSPERI, clk_div_top_axi_hsperi, + clk_fpll0_parent, 0, + 0x11c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 8); + +static const struct clk_hw *clk_div_top_axi_hsperi_parent[] = { + &clk_div_top_axi_hsperi.common.hw, +}; + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER0, clk_div_timer0, + clk_div_top_50m_parent, 0, + 0x0d0, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER1, clk_div_timer1, + clk_div_top_50m_parent, 0, + 0x0d4, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER2, clk_div_timer2, + clk_div_top_50m_parent, 0, + 0x0d8, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER3, clk_div_timer3, + clk_div_top_50m_parent, 0, + 0x0dc, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER4, clk_div_timer4, + clk_div_top_50m_parent, 0, + 0x0e0, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER5, clk_div_timer5, + clk_div_top_50m_parent, 0, + 0x0e4, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER6, clk_div_timer6, + clk_div_top_50m_parent, 0, + 0x0e8, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV(CLK_DIV_TIMER7, clk_div_timer7, + clk_div_top_50m_parent, 0, + 0x0ec, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_CXP_TEST_PHY, clk_div_cxp_test_phy, + clk_fpll0_parent, 0, + 0x064, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_CXP_TEST_ETH_PHY, clk_div_cxp_test_eth_phy, + clk_fpll2_parent, 0, + 0x068, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_C2C0_TEST_PHY, clk_div_c2c0_test_phy, + clk_fpll0_parent, 0, + 0x05c, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_C2C1_TEST_PHY, clk_div_c2c1_test_phy, + clk_fpll0_parent, 0, + 0x060, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_PCIE_1G, clk_div_pcie_1g, + clk_fpll1_parent, 0, + 0x160, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_UART_500M, clk_div_uart_500m, + clk_fpll0_parent, 0, + 0x0cc, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 4); + +static DEFINE_SG2044_DIV(CLK_DIV_GPIO_DB, clk_div_gpio_db, + clk_div_top_axi0_parent, 0, + 0x0f8, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1000); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_SD, clk_div_sd, + clk_fpll0_parent, 0, + 0x110, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 5); + +static DEFINE_SG2044_DIV(CLK_DIV_SD_100K, clk_div_sd_100k, + clk_div_top_axi0_parent, 0, + 0x114, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1000); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_EMMC, clk_div_emmc, + clk_fpll0_parent, 0, + 0x108, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 5); + +static DEFINE_SG2044_DIV(CLK_DIV_EMMC_100K, clk_div_emmc_100k, + clk_div_top_axi0_parent, 0, + 0x10c, 16, 16, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 1000); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_EFUSE, clk_div_efuse, + clk_fpll0_parent, 0, + 0x0f4, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 80); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_TX_ETH0, clk_div_tx_eth0, + clk_fpll0_parent, 0, + 0x0fc, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 16); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_PTP_REF_I_ETH0, clk_div_ptp_ref_i_eth0, + clk_fpll0_parent, 0, + 0x100, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 40); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_REF_ETH0, clk_div_ref_eth0, + clk_fpll0_parent, 0, + 0x104, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 80); + +static DEFINE_SG2044_DIV_PDATA(CLK_DIV_PKA, clk_div_pka, + clk_fpll0_parent, 0, + 0x0f0, 16, 8, + CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + 2); + +static const struct clk_parent_data clk_mux_ddr0_parents[] = { + { .hw = &clk_div_ddr0_fixed.common.hw }, + { .hw = &clk_div_ddr0_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR0, clk_mux_ddr0, + clk_mux_ddr0_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 7, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_ddr1_parents[] = { + { .hw = &clk_div_ddr1_fixed.common.hw }, + { .hw = &clk_div_ddr1_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR1, clk_mux_ddr1, + clk_mux_ddr1_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 8, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_ddr2_parents[] = { + { .hw = &clk_div_ddr2_fixed.common.hw }, + { .hw = &clk_div_ddr2_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR2, clk_mux_ddr2, + clk_mux_ddr2_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 9, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_ddr3_parents[] = { + { .hw = &clk_div_ddr3_fixed.common.hw }, + { .hw = &clk_div_ddr3_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR3, clk_mux_ddr3, + clk_mux_ddr3_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 10, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_ddr4_parents[] = { + { .hw = &clk_div_ddr4_fixed.common.hw }, + { .hw = &clk_div_ddr4_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR4, clk_mux_ddr4, + clk_mux_ddr4_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 11, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_ddr5_parents[] = { + { .hw = &clk_div_ddr5_fixed.common.hw }, + { .hw = &clk_div_ddr5_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR5, clk_mux_ddr5, + clk_mux_ddr5_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 12, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_ddr6_parents[] = { + { .hw = &clk_div_ddr6_fixed.common.hw }, + { .hw = &clk_div_ddr6_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR6, clk_mux_ddr6, + clk_mux_ddr6_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 13, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_ddr7_parents[] = { + { .hw = &clk_div_ddr7_fixed.common.hw }, + { .hw = &clk_div_ddr7_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_DDR7, clk_mux_ddr7, + clk_mux_ddr7_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 14, sg2044_mux_table, CLK_MUX_READ_ONLY); + +static const struct clk_parent_data clk_mux_noc_sys_parents[] = { + { .hw = &clk_div_noc_sys_fixed.common.hw }, + { .hw = &clk_div_noc_sys_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_NOC_SYS, clk_mux_noc_sys, + clk_mux_noc_sys_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 3, sg2044_mux_table, 0); + +static const struct clk_parent_data clk_mux_tpu_sys_parents[] = { + { .hw = &clk_div_tpu_sys_fixed.common.hw }, + { .hw = &clk_div_tpu_sys_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_TPU_SYS, clk_mux_tpu_sys, + clk_mux_tpu_sys_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 2, sg2044_mux_table, 0); + +static const struct clk_parent_data clk_mux_rp_sys_parents[] = { + { .hw = &clk_div_rp_sys_fixed.common.hw }, + { .hw = &clk_div_rp_sys_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_RP_SYS, clk_mux_rp_sys, + clk_mux_rp_sys_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 1, sg2044_mux_table, 0); + +static const struct clk_parent_data clk_mux_ap_sys_parents[] = { + { .hw = &clk_div_ap_sys_fixed.common.hw }, + { .hw = &clk_div_ap_sys_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_AP_SYS, clk_mux_ap_sys, + clk_mux_ap_sys_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 0, sg2044_mux_table, 0); + +static const struct clk_parent_data clk_mux_vc_src0_parents[] = { + { .hw = &clk_div_vc_src0_fixed.common.hw }, + { .hw = &clk_div_vc_src0_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_VC_SRC0, clk_mux_vc_src0, + clk_mux_vc_src0_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 4, sg2044_mux_table, 0); + +static const struct clk_parent_data clk_mux_vc_src1_parents[] = { + { .hw = &clk_div_vc_src1_fixed.common.hw }, + { .hw = &clk_div_vc_src1_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_VC_SRC1, clk_mux_vc_src1, + clk_mux_vc_src1_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 5, sg2044_mux_table, 0); + +static const struct clk_parent_data clk_mux_cxp_mac_parents[] = { + { .hw = &clk_div_cxp_mac_fixed.common.hw }, + { .hw = &clk_div_cxp_mac_main.common.hw }, +}; + +static DEFINE_SG2044_MUX(CLK_MUX_CXP_MAC, clk_mux_cxp_mac, + clk_mux_cxp_mac_parents, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + 0x020, 6, sg2044_mux_table, 0); + +static const struct clk_hw *clk_gate_ap_sys_parent[] = { + &clk_mux_ap_sys.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_AP_SYS, clk_gate_ap_sys, + clk_gate_ap_sys_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 0, 0); + +static const struct clk_hw *clk_gate_rp_sys_parent[] = { + &clk_mux_rp_sys.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_RP_SYS, clk_gate_rp_sys, + clk_gate_rp_sys_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 2, 0); + +static const struct clk_hw *clk_gate_tpu_sys_parent[] = { + &clk_mux_tpu_sys.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TPU_SYS, clk_gate_tpu_sys, + clk_gate_tpu_sys_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 3, 0); + +static const struct clk_hw *clk_gate_noc_sys_parent[] = { + &clk_mux_noc_sys.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_NOC_SYS, clk_gate_noc_sys, + clk_gate_noc_sys_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 8, 0); + +static const struct clk_hw *clk_gate_vc_src0_parent[] = { + &clk_mux_vc_src0.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_VC_SRC0, clk_gate_vc_src0, + clk_gate_vc_src0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 9, 0); + +static const struct clk_hw *clk_gate_vc_src1_parent[] = { + &clk_mux_vc_src1.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_VC_SRC1, clk_gate_vc_src1, + clk_gate_vc_src1_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 10, 0); + +static const struct clk_hw *clk_gate_ddr0_parent[] = { + &clk_mux_ddr0.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR0, clk_gate_ddr0, + clk_gate_ddr0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 7, 0); + +static const struct clk_hw *clk_gate_ddr1_parent[] = { + &clk_mux_ddr1.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR1, clk_gate_ddr1, + clk_gate_ddr1_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 8, 0); + +static const struct clk_hw *clk_gate_ddr2_parent[] = { + &clk_mux_ddr2.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR2, clk_gate_ddr2, + clk_gate_ddr2_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 9, 0); + +static const struct clk_hw *clk_gate_ddr3_parent[] = { + &clk_mux_ddr3.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR3, clk_gate_ddr3, + clk_gate_ddr3_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 10, 0); + +static const struct clk_hw *clk_gate_ddr4_parent[] = { + &clk_mux_ddr4.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR4, clk_gate_ddr4, + clk_gate_ddr4_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 11, 0); + +static const struct clk_hw *clk_gate_ddr5_parent[] = { + &clk_mux_ddr5.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR5, clk_gate_ddr5, + clk_gate_ddr5_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 12, 0); + +static const struct clk_hw *clk_gate_ddr6_parent[] = { + &clk_mux_ddr6.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR6, clk_gate_ddr6, + clk_gate_ddr6_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 13, 0); + +static const struct clk_hw *clk_gate_ddr7_parent[] = { + &clk_mux_ddr7.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_DDR7, clk_gate_ddr7, + clk_gate_ddr7_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 14, 0); + +static const struct clk_hw *clk_gate_top_50m_parent[] = { + &clk_div_top_50m.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TOP_50M, clk_gate_top_50m, + clk_gate_top_50m_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 1, 0); + +static const struct clk_hw *clk_gate_sc_rx_parent[] = { + &clk_div_top_50m.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_SC_RX, clk_gate_sc_rx, + clk_gate_sc_rx_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 12, 0); + +static const struct clk_hw *clk_gate_sc_rx_x0y1_parent[] = { + &clk_div_top_50m.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_SC_RX_X0Y1, clk_gate_sc_rx_x0y1, + clk_gate_sc_rx_x0y1_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 13, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_TOP_AXI0, clk_gate_top_axi0, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 5, 0); + +static const struct clk_hw *clk_gate_mailbox_intc_parent[] = { + &clk_gate_top_axi0.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_INTC0, clk_gate_intc0, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 20, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_INTC1, clk_gate_intc1, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 21, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_INTC2, clk_gate_intc2, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 22, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_INTC3, clk_gate_intc3, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 23, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_MAILBOX0, clk_gate_mailbox0, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 16, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_MAILBOX1, clk_gate_mailbox1, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 17, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_MAILBOX2, clk_gate_mailbox2, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 18, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_MAILBOX3, clk_gate_mailbox3, + clk_gate_mailbox_intc_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x020, 19, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_TOP_AXI_HSPERI, clk_gate_top_axi_hsperi, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x008, 6, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_TIMER, clk_gate_apb_timer, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT, + 0x004, 7, 0); + +static const struct clk_hw *clk_gate_timer0_parent[] = { + &clk_div_timer0.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER0, clk_gate_timer0, + clk_gate_timer0_parent, + CLK_SET_RATE_PARENT, + 0x004, 8, 0); + +static const struct clk_hw *clk_gate_timer1_parent[] = { + &clk_div_timer1.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER1, clk_gate_timer1, + clk_gate_timer1_parent, + CLK_SET_RATE_PARENT, + 0x004, 9, 0); + +static const struct clk_hw *clk_gate_timer2_parent[] = { + &clk_div_timer2.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER2, clk_gate_timer2, + clk_gate_timer2_parent, + CLK_SET_RATE_PARENT, + 0x004, 10, 0); + +static const struct clk_hw *clk_gate_timer3_parent[] = { + &clk_div_timer3.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER3, clk_gate_timer3, + clk_gate_timer3_parent, + CLK_SET_RATE_PARENT, + 0x004, 11, 0); + +static const struct clk_hw *clk_gate_timer4_parent[] = { + &clk_div_timer4.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER4, clk_gate_timer4, + clk_gate_timer4_parent, + CLK_SET_RATE_PARENT, + 0x004, 12, 0); + +static const struct clk_hw *clk_gate_timer5_parent[] = { + &clk_div_timer5.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER5, clk_gate_timer5, + clk_gate_timer5_parent, + CLK_SET_RATE_PARENT, + 0x004, 13, 0); + +static const struct clk_hw *clk_gate_timer6_parent[] = { + &clk_div_timer6.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER6, clk_gate_timer6, + clk_gate_timer6_parent, + CLK_SET_RATE_PARENT, + 0x004, 14, 0); + +static const struct clk_hw *clk_gate_timer7_parent[] = { + &clk_div_timer7.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TIMER7, clk_gate_timer7, + clk_gate_timer7_parent, + CLK_SET_RATE_PARENT, + 0x004, 15, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_CXP_CFG, clk_gate_cxp_cfg, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 15, 0); + +static const struct clk_hw *clk_gate_cxp_mac_parent[] = { + &clk_mux_cxp_mac.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_CXP_MAC, clk_gate_cxp_mac, + clk_gate_cxp_mac_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x000, 14, 0); + +static const struct clk_hw *clk_gate_cxp_test_phy_parent[] = { + &clk_div_cxp_test_phy.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_CXP_TEST_PHY, clk_gate_cxp_test_phy, + clk_gate_cxp_test_phy_parent, + CLK_SET_RATE_PARENT, + 0x000, 6, 0); + +static const struct clk_hw *clk_gate_cxp_test_eth_phy_parent[] = { + &clk_div_cxp_test_eth_phy.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_CXP_TEST_ETH_PHY, clk_gate_cxp_test_eth_phy, + clk_gate_cxp_test_eth_phy_parent, + CLK_SET_RATE_PARENT, + 0x000, 7, 0); + +static const struct clk_hw *clk_gate_pcie_1g_parent[] = { + &clk_div_pcie_1g.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_PCIE_1G, clk_gate_pcie_1g, + clk_gate_pcie_1g_parent, + CLK_SET_RATE_PARENT, + 0x008, 15, 0); + +static const struct clk_hw *clk_gate_c2c0_test_phy_parent[] = { + &clk_div_c2c0_test_phy.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_C2C0_TEST_PHY, clk_gate_c2c0_test_phy, + clk_gate_c2c0_test_phy_parent, + CLK_SET_RATE_PARENT, + 0x000, 4, 0); + +static const struct clk_hw *clk_gate_c2c1_test_phy_parent[] = { + &clk_div_c2c1_test_phy.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_C2C1_TEST_PHY, clk_gate_c2c1_test_phy, + clk_gate_c2c1_test_phy_parent, + CLK_SET_RATE_PARENT, + 0x000, 5, 0); + +static const struct clk_hw *clk_gate_uart_500m_parent[] = { + &clk_div_uart_500m.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_UART_500M, clk_gate_uart_500m, + clk_gate_uart_500m_parent, + CLK_SET_RATE_PARENT, + 0x004, 1, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_UART, clk_gate_apb_uart, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT, + 0x004, 2, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_SPI, clk_gate_apb_spi, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT, + 0x004, 22, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_AHB_SPIFMC, clk_gate_ahb_spifmc, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT, + 0x004, 5, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_I2C, clk_gate_apb_i2c, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x004, 23, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_AXI_DBG_I2C, clk_gate_axi_dbg_i2c, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT, + 0x004, 3, 0); + +static const struct clk_hw *clk_gate_gpio_db_parent[] = { + &clk_div_gpio_db.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_GPIO_DB, clk_gate_gpio_db, + clk_gate_gpio_db_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x004, 21, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_GPIO_INTR, clk_gate_apb_gpio_intr, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x004, 20, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_GPIO, clk_gate_apb_gpio, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x004, 19, 0); + +static const struct clk_hw *clk_gate_sd_parent[] = { + &clk_div_sd.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_SD, clk_gate_sd, + clk_gate_sd_parent, + CLK_SET_RATE_PARENT, + 0x008, 3, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_AXI_SD, clk_gate_axi_sd, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT, + 0x008, 2, 0); + +static const struct clk_hw *clk_gate_sd_100k_parent[] = { + &clk_div_sd_100k.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_SD_100K, clk_gate_sd_100k, + clk_gate_sd_100k_parent, + CLK_SET_RATE_PARENT, + 0x008, 4, 0); + +static const struct clk_hw *clk_gate_emmc_parent[] = { + &clk_div_emmc.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_EMMC, clk_gate_emmc, + clk_gate_emmc_parent, + CLK_SET_RATE_PARENT, + 0x008, 0, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_AXI_EMMC, clk_gate_axi_emmc, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT, + 0x004, 31, 0); + +static const struct clk_hw *clk_gate_emmc_100k_parent[] = { + &clk_div_emmc_100k.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_EMMC_100K, clk_gate_emmc_100k, + clk_gate_emmc_100k_parent, + CLK_SET_RATE_PARENT, + 0x008, 1, 0); + +static const struct clk_hw *clk_gate_efuse_parent[] = { + &clk_div_efuse.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_EFUSE, clk_gate_efuse, + clk_gate_efuse_parent, + CLK_SET_RATE_PARENT, + 0x004, 17, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_EFUSE, clk_gate_apb_efuse, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT, + 0x004, 18, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_SYSDMA_AXI, clk_gate_sysdma_axi, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT, + 0x004, 0, 0); + +static const struct clk_hw *clk_gate_tx_eth0_parent[] = { + &clk_div_tx_eth0.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_TX_ETH0, clk_gate_tx_eth0, + clk_gate_tx_eth0_parent, + CLK_SET_RATE_PARENT, + 0x004, 27, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_AXI_ETH0, clk_gate_axi_eth0, + clk_div_top_axi_hsperi_parent, + CLK_SET_RATE_PARENT, + 0x004, 28, 0); + +static const struct clk_hw *clk_gate_ptp_ref_i_eth0_parent[] = { + &clk_div_ptp_ref_i_eth0.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_PTP_REF_I_ETH0, clk_gate_ptp_ref_i_eth0, + clk_gate_ptp_ref_i_eth0_parent, + CLK_SET_RATE_PARENT, + 0x004, 29, 0); + +static const struct clk_hw *clk_gate_ref_eth0_parent[] = { + &clk_div_ref_eth0.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_REF_ETH0, clk_gate_ref_eth0, + clk_gate_ref_eth0_parent, + CLK_SET_RATE_PARENT, + 0x004, 30, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_RTC, clk_gate_apb_rtc, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x004, 26, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_PWM, clk_gate_apb_pwm, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT, + 0x004, 25, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_APB_WDT, clk_gate_apb_wdt, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT, + 0x004, 24, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_AXI_SRAM, clk_gate_axi_sram, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x004, 6, 0); + +static DEFINE_SG2044_GATE(CLK_GATE_AHB_ROM, clk_gate_ahb_rom, + clk_div_top_axi0_parent, + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + 0x004, 4, 0); + +static const struct clk_hw *clk_gate_pka_parent[] = { + &clk_div_pka.common.hw, +}; + +static DEFINE_SG2044_GATE(CLK_GATE_PKA, clk_gate_pka, + clk_gate_pka_parent, + CLK_SET_RATE_PARENT, + 0x004, 16, 0); + +static struct sg2044_clk_common * const sg2044_div_commons[] = { + &clk_div_ap_sys_fixed.common, + &clk_div_ap_sys_main.common, + &clk_div_rp_sys_fixed.common, + &clk_div_rp_sys_main.common, + &clk_div_tpu_sys_fixed.common, + &clk_div_tpu_sys_main.common, + &clk_div_noc_sys_fixed.common, + &clk_div_noc_sys_main.common, + &clk_div_vc_src0_fixed.common, + &clk_div_vc_src0_main.common, + &clk_div_vc_src1_fixed.common, + &clk_div_vc_src1_main.common, + &clk_div_cxp_mac_fixed.common, + &clk_div_cxp_mac_main.common, + &clk_div_ddr0_fixed.common, + &clk_div_ddr0_main.common, + &clk_div_ddr1_fixed.common, + &clk_div_ddr1_main.common, + &clk_div_ddr2_fixed.common, + &clk_div_ddr2_main.common, + &clk_div_ddr3_fixed.common, + &clk_div_ddr3_main.common, + &clk_div_ddr4_fixed.common, + &clk_div_ddr4_main.common, + &clk_div_ddr5_fixed.common, + &clk_div_ddr5_main.common, + &clk_div_ddr6_fixed.common, + &clk_div_ddr6_main.common, + &clk_div_ddr7_fixed.common, + &clk_div_ddr7_main.common, + &clk_div_top_50m.common, + &clk_div_top_axi0.common, + &clk_div_top_axi_hsperi.common, + &clk_div_timer0.common, + &clk_div_timer1.common, + &clk_div_timer2.common, + &clk_div_timer3.common, + &clk_div_timer4.common, + &clk_div_timer5.common, + &clk_div_timer6.common, + &clk_div_timer7.common, + &clk_div_cxp_test_phy.common, + &clk_div_cxp_test_eth_phy.common, + &clk_div_c2c0_test_phy.common, + &clk_div_c2c1_test_phy.common, + &clk_div_pcie_1g.common, + &clk_div_uart_500m.common, + &clk_div_gpio_db.common, + &clk_div_sd.common, + &clk_div_sd_100k.common, + &clk_div_emmc.common, + &clk_div_emmc_100k.common, + &clk_div_efuse.common, + &clk_div_tx_eth0.common, + &clk_div_ptp_ref_i_eth0.common, + &clk_div_ref_eth0.common, + &clk_div_pka.common, +}; + +static struct sg2044_clk_common * const sg2044_mux_commons[] = { + &clk_mux_ddr0.common, + &clk_mux_ddr1.common, + &clk_mux_ddr2.common, + &clk_mux_ddr3.common, + &clk_mux_ddr4.common, + &clk_mux_ddr5.common, + &clk_mux_ddr6.common, + &clk_mux_ddr7.common, + &clk_mux_noc_sys.common, + &clk_mux_tpu_sys.common, + &clk_mux_rp_sys.common, + &clk_mux_ap_sys.common, + &clk_mux_vc_src0.common, + &clk_mux_vc_src1.common, + &clk_mux_cxp_mac.common, +}; + +static struct sg2044_clk_common * const sg2044_gate_commons[] = { + &clk_gate_ap_sys.common, + &clk_gate_rp_sys.common, + &clk_gate_tpu_sys.common, + &clk_gate_noc_sys.common, + &clk_gate_vc_src0.common, + &clk_gate_vc_src1.common, + &clk_gate_ddr0.common, + &clk_gate_ddr1.common, + &clk_gate_ddr2.common, + &clk_gate_ddr3.common, + &clk_gate_ddr4.common, + &clk_gate_ddr5.common, + &clk_gate_ddr6.common, + &clk_gate_ddr7.common, + &clk_gate_top_50m.common, + &clk_gate_sc_rx.common, + &clk_gate_sc_rx_x0y1.common, + &clk_gate_top_axi0.common, + &clk_gate_intc0.common, + &clk_gate_intc1.common, + &clk_gate_intc2.common, + &clk_gate_intc3.common, + &clk_gate_mailbox0.common, + &clk_gate_mailbox1.common, + &clk_gate_mailbox2.common, + &clk_gate_mailbox3.common, + &clk_gate_top_axi_hsperi.common, + &clk_gate_apb_timer.common, + &clk_gate_timer0.common, + &clk_gate_timer1.common, + &clk_gate_timer2.common, + &clk_gate_timer3.common, + &clk_gate_timer4.common, + &clk_gate_timer5.common, + &clk_gate_timer6.common, + &clk_gate_timer7.common, + &clk_gate_cxp_cfg.common, + &clk_gate_cxp_mac.common, + &clk_gate_cxp_test_phy.common, + &clk_gate_cxp_test_eth_phy.common, + &clk_gate_pcie_1g.common, + &clk_gate_c2c0_test_phy.common, + &clk_gate_c2c1_test_phy.common, + &clk_gate_uart_500m.common, + &clk_gate_apb_uart.common, + &clk_gate_apb_spi.common, + &clk_gate_ahb_spifmc.common, + &clk_gate_apb_i2c.common, + &clk_gate_axi_dbg_i2c.common, + &clk_gate_gpio_db.common, + &clk_gate_apb_gpio_intr.common, + &clk_gate_apb_gpio.common, + &clk_gate_sd.common, + &clk_gate_axi_sd.common, + &clk_gate_sd_100k.common, + &clk_gate_emmc.common, + &clk_gate_axi_emmc.common, + &clk_gate_emmc_100k.common, + &clk_gate_efuse.common, + &clk_gate_apb_efuse.common, + &clk_gate_sysdma_axi.common, + &clk_gate_tx_eth0.common, + &clk_gate_axi_eth0.common, + &clk_gate_ptp_ref_i_eth0.common, + &clk_gate_ref_eth0.common, + &clk_gate_apb_rtc.common, + &clk_gate_apb_pwm.common, + &clk_gate_apb_wdt.common, + &clk_gate_axi_sram.common, + &clk_gate_ahb_rom.common, + &clk_gate_pka.common, +}; + +static void sg2044_clk_fix_init_parent(struct clk_hw **pdata, + const struct clk_init_data *init, + struct clk_hw_onecell_data *data) +{ + u8 i; + const struct clk_hw *hw; + const struct sg2044_clk_common *common; + + for (i = 0; i < init->num_parents; i++) { + hw = init->parent_hws[i]; + common = hw_to_sg2044_clk_common(hw); + + WARN(!data->hws[common->id], "clk %u is not register\n", + common->id); + pdata[i] = data->hws[common->id]; + } +} + +static int sg2044_clk_init_ctrl(struct device *dev, void __iomem *reg, + struct sg2044_clk_ctrl *ctrl, + const struct sg2044_clk_desc_data *desc) +{ + int ret, i; + struct clk_hw *hw; + + spin_lock_init(&ctrl->lock); + + for (i = 0; i < desc->num_div; i++) { + struct sg2044_clk_common *common = desc->div[i]; + + common->lock = &ctrl->lock; + common->base = reg; + + ret = devm_clk_hw_register(dev, &common->hw); + if (ret) + return ret; + + ctrl->data.hws[common->id] = &common->hw; + } + + for (i = 0; i < desc->num_mux; i++) { + struct sg2044_clk_common *common = desc->mux[i]; + struct sg2044_mux *mux = hw_to_sg2044_mux(&common->hw); + const struct clk_init_data *init = common->hw.init; + + common->lock = &ctrl->lock; + common->base = reg; + + hw = devm_clk_hw_register_mux_parent_data_table(dev, + init->name, + init->parent_data, + init->num_parents, + init->flags, + reg + mux->mux.offset, + mux->mux.shift, + 1, + mux->mux.flags, + mux->mux.table, + &ctrl->lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + if (!(mux->mux.flags & CLK_MUX_READ_ONLY)) { + mux->nb.notifier_call = sg2044_mux_notifier_cb; + ret = devm_clk_notifier_register(dev, hw->clk, + &mux->nb); + if (ret < 0) + return dev_err_probe(dev, ret, + "%s: failed to register notifier\n", + clk_hw_get_name(hw)); + } + + ctrl->data.hws[common->id] = hw; + } + + for (i = 0; i < desc->num_gate; i++) { + struct sg2044_clk_common *common = desc->gate[i]; + struct sg2044_gate *gate = hw_to_sg2044_gate(&common->hw); + const struct clk_init_data *init = common->hw.init; + struct clk_hw *parent_hws[1] = { }; + + sg2044_clk_fix_init_parent(parent_hws, init, &ctrl->data); + common->lock = &ctrl->lock; + common->base = reg; + + hw = devm_clk_hw_register_gate_parent_hw(dev, init->name, + parent_hws[0], + init->flags, + reg + gate->gate.offset, + gate->gate.shift, + gate->gate.flags, + &ctrl->lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + ctrl->data.hws[common->id] = hw; + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &ctrl->data); +} + +static int sg2044_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sg2044_clk_ctrl *ctrl; + const struct sg2044_clk_desc_data *desc; + void __iomem *reg; + u32 num_clks; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + desc = device_get_match_data(dev); + if (!desc) + return dev_err_probe(dev, -EINVAL, "no match data for platform\n"); + + num_clks = desc->num_div + desc->num_gate + desc->num_mux; + + ctrl = devm_kzalloc(dev, struct_size(ctrl, data.hws, num_clks), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->data.num = num_clks; + + return sg2044_clk_init_ctrl(dev, reg, ctrl, desc); +} + +static const struct sg2044_clk_desc_data sg2044_clk_desc_data = { + .div = sg2044_div_commons, + .mux = sg2044_mux_commons, + .gate = sg2044_gate_commons, + .num_div = ARRAY_SIZE(sg2044_div_commons), + .num_mux = ARRAY_SIZE(sg2044_mux_commons), + .num_gate = ARRAY_SIZE(sg2044_gate_commons), +}; + +static const struct of_device_id sg2044_clk_match[] = { + { .compatible = "sophgo,sg2044-clk", .data = &sg2044_clk_desc_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sg2044_clk_match); + +static struct platform_driver sg2044_clk_driver = { + .probe = sg2044_clk_probe, + .driver = { + .name = "sg2044-clk", + .of_match_table = sg2044_clk_match, + }, +}; +module_platform_driver(sg2044_clk_driver); + +MODULE_AUTHOR("Inochi Amaoto <inochiama@gmail.com>"); +MODULE_DESCRIPTION("Sophgo SG2044 clock driver"); +MODULE_LICENSE("GPL"); |