Message ID | IA1PR20MB4953139C77167200594CE971BB9FA@IA1PR20MB4953.namprd20.prod.outlook.com (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | riscv: sophgo: add clock support for Sophgo CV1800 SoCs | expand |
On Wed, Dec 27, 2023 at 10:54 AM Inochi Amaoto <inochiama@outlook.com> wrote: > > Add driver for CV1800 series clock controller. > > Signed-off-by: Inochi Amaoto <inochiama@outlook.com> > Link: https://github.com/milkv-duo/duo-files/blob/main/hardware/CV1800B/CV180X-Clock-v1.xlsx > Link: https://github.com/milkv-duo/duo-files/blob/main/hardware/CV1800B/CV1800B-CV1801B-Preliminary-Datasheet-full-en.pdf This link may be invalid. Please check it. > --- > drivers/clk/Kconfig | 1 + > drivers/clk/Makefile | 1 + > drivers/clk/sophgo/Kconfig | 12 + > drivers/clk/sophgo/Makefile | 7 + > drivers/clk/sophgo/clk-cv1800.c | 1530 ++++++++++++++++++++++++ > drivers/clk/sophgo/clk-cv1800.h | 123 ++ > drivers/clk/sophgo/clk-cv18xx-common.c | 66 + > drivers/clk/sophgo/clk-cv18xx-common.h | 81 ++ > drivers/clk/sophgo/clk-cv18xx-ip.c | 905 ++++++++++++++ > drivers/clk/sophgo/clk-cv18xx-ip.h | 265 ++++ > drivers/clk/sophgo/clk-cv18xx-pll.c | 425 +++++++ > drivers/clk/sophgo/clk-cv18xx-pll.h | 119 ++ > 12 files changed, 3535 insertions(+) > create mode 100644 drivers/clk/sophgo/Kconfig > create mode 100644 drivers/clk/sophgo/Makefile > create mode 100644 drivers/clk/sophgo/clk-cv1800.c > create mode 100644 drivers/clk/sophgo/clk-cv1800.h > create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.c > create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.h > create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.c > create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.h > create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.c > create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.h > > diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig > index c30d0d396f7a..514343934fda 100644 > --- a/drivers/clk/Kconfig > +++ b/drivers/clk/Kconfig > @@ -499,6 +499,7 @@ source "drivers/clk/rockchip/Kconfig" > source "drivers/clk/samsung/Kconfig" > source "drivers/clk/sifive/Kconfig" > source "drivers/clk/socfpga/Kconfig" > +source "drivers/clk/sophgo/Kconfig" > source "drivers/clk/sprd/Kconfig" > source "drivers/clk/starfive/Kconfig" > source "drivers/clk/sunxi/Kconfig" > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index ed71f2e0ee36..eeae7ae93f89 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -119,6 +119,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ > obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ > obj-$(CONFIG_CLK_SIFIVE) += sifive/ > obj-y += socfpga/ > +obj-y += sophgo/ > obj-$(CONFIG_PLAT_SPEAR) += spear/ > obj-y += sprd/ > obj-$(CONFIG_ARCH_STI) += st/ > diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig > new file mode 100644 > index 000000000000..d67009fa749f > --- /dev/null > +++ b/drivers/clk/sophgo/Kconfig > @@ -0,0 +1,12 @@ > +# 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" > + default m > + 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 > diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile > new file mode 100644 > index 000000000000..a50320764200 > --- /dev/null > +++ b/drivers/clk/sophgo/Makefile > @@ -0,0 +1,7 @@ > +# 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 > diff --git a/drivers/clk/sophgo/clk-cv1800.c b/drivers/clk/sophgo/clk-cv1800.c > new file mode 100644 > index 000000000000..2bdfdd0b907c > --- /dev/null > +++ b/drivers/clk/sophgo/clk-cv1800.c > @@ -0,0 +1,1530 @@ > +// 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 }, > +}; > + > +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); > + > +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); > + > +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); > + > +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); > + > +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); > + > +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 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; > + } > + } > + > + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, > + desc->clks_data); > + > + return ret; > +} > + > +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_kmalloc(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; > + } > + > + ret = cv1800_clk_init_ctrl(dev, reg, ctrl, desc); > + > + return ret; > +} > + > +static const struct of_device_id cv1800_clk_ids[] = { > + { .compatible = "sophgo,cv1800-clk", .data = &cv1800_desc }, > + { .compatible = "sophgo,cv1810-clk", .data = &cv1810_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_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..aede85eac4ff > --- /dev/null > +++ b/drivers/clk/sophgo/clk-cv18xx-ip.c > @@ -0,0 +1,905 @@ > +// 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); > + unsigned long flags; > + > + spin_lock_irqsave(div->div.common.lock, flags); > + > + if (index) > + cv1800_clk_clearbit(&div->div.common, &div->bypass); > + else > + cv1800_clk_setbit(&div->div.common, &div->bypass); > + > + spin_unlock_irqrestore(div->div.common.lock, flags); > + > + return 0; > +} > + > +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); > + unsigned long flags; > + > + spin_lock_irqsave(mux->mux.common.lock, flags); > + > + if (index == 0) { > + cv1800_clk_setbit(&mux->mux.common, &mux->bypass); > + goto release; > + } > + > + cv1800_clk_clearbit(&mux->mux.common, &mux->bypass); > + _mux_set_parent(&mux->mux, index - 1); > + > +release: > + spin_unlock_irqrestore(mux->mux.common.lock, flags); > + return 0; > +} > + > +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; > + } > + > + unreachable(); > +} > + > +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]; > + > + spin_lock_irqsave(mmux->common.lock, flags); > + > + 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); > + > + 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); > + > +release: > + spin_unlock_irqrestore(mmux->common.lock, flags); > + > + 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..928de0146b08 > --- /dev/null > +++ b/drivers/clk/sophgo/clk-cv18xx-ip.h > @@ -0,0 +1,265 @@ > +/* 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..c687bde48455 > --- /dev/null > +++ b/drivers/clk/sophgo/clk-cv18xx-pll.c > @@ -0,0 +1,425 @@ > +// 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) > +{ > + unsigned long 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 = dividend / factor; > + dividend %= factor; > + > + 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); > + unsigned long flags; > + > + spin_lock_irqsave(pll->common.lock, flags); > + > + if (index) > + cv1800_clk_setbit(&pll->common, &pll->pll_syn->en); > + else > + cv1800_clk_clearbit(&pll->common, &pll->pll_syn->en); > + > + spin_unlock_irqrestore(pll->common.lock, flags); > + > + 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..5f876efe48d5 > --- /dev/null > +++ b/drivers/clk/sophgo/clk-cv18xx-pll.h > @@ -0,0 +1,119 @@ > +/* 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_ > -- > 2.43.0 >
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index c30d0d396f7a..514343934fda 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -499,6 +499,7 @@ source "drivers/clk/rockchip/Kconfig" source "drivers/clk/samsung/Kconfig" source "drivers/clk/sifive/Kconfig" source "drivers/clk/socfpga/Kconfig" +source "drivers/clk/sophgo/Kconfig" source "drivers/clk/sprd/Kconfig" source "drivers/clk/starfive/Kconfig" source "drivers/clk/sunxi/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index ed71f2e0ee36..eeae7ae93f89 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-y += socfpga/ +obj-y += sophgo/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ diff --git a/drivers/clk/sophgo/Kconfig b/drivers/clk/sophgo/Kconfig new file mode 100644 index 000000000000..d67009fa749f --- /dev/null +++ b/drivers/clk/sophgo/Kconfig @@ -0,0 +1,12 @@ +# 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" + default m + 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 diff --git a/drivers/clk/sophgo/Makefile b/drivers/clk/sophgo/Makefile new file mode 100644 index 000000000000..a50320764200 --- /dev/null +++ b/drivers/clk/sophgo/Makefile @@ -0,0 +1,7 @@ +# 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 diff --git a/drivers/clk/sophgo/clk-cv1800.c b/drivers/clk/sophgo/clk-cv1800.c new file mode 100644 index 000000000000..2bdfdd0b907c --- /dev/null +++ b/drivers/clk/sophgo/clk-cv1800.c @@ -0,0 +1,1530 @@ +// 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 }, +}; + +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); + +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); + +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); + +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); + +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); + +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 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; + } + } + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + desc->clks_data); + + return ret; +} + +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_kmalloc(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; + } + + ret = cv1800_clk_init_ctrl(dev, reg, ctrl, desc); + + return ret; +} + +static const struct of_device_id cv1800_clk_ids[] = { + { .compatible = "sophgo,cv1800-clk", .data = &cv1800_desc }, + { .compatible = "sophgo,cv1810-clk", .data = &cv1810_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_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..aede85eac4ff --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-ip.c @@ -0,0 +1,905 @@ +// 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); + unsigned long flags; + + spin_lock_irqsave(div->div.common.lock, flags); + + if (index) + cv1800_clk_clearbit(&div->div.common, &div->bypass); + else + cv1800_clk_setbit(&div->div.common, &div->bypass); + + spin_unlock_irqrestore(div->div.common.lock, flags); + + return 0; +} + +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); + unsigned long flags; + + spin_lock_irqsave(mux->mux.common.lock, flags); + + if (index == 0) { + cv1800_clk_setbit(&mux->mux.common, &mux->bypass); + goto release; + } + + cv1800_clk_clearbit(&mux->mux.common, &mux->bypass); + _mux_set_parent(&mux->mux, index - 1); + +release: + spin_unlock_irqrestore(mux->mux.common.lock, flags); + return 0; +} + +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; + } + + unreachable(); +} + +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]; + + spin_lock_irqsave(mmux->common.lock, flags); + + 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); + + 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); + +release: + spin_unlock_irqrestore(mmux->common.lock, flags); + + 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..928de0146b08 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-ip.h @@ -0,0 +1,265 @@ +/* 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..c687bde48455 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-pll.c @@ -0,0 +1,425 @@ +// 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) +{ + unsigned long 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 = dividend / factor; + dividend %= factor; + + 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); + unsigned long flags; + + spin_lock_irqsave(pll->common.lock, flags); + + if (index) + cv1800_clk_setbit(&pll->common, &pll->pll_syn->en); + else + cv1800_clk_clearbit(&pll->common, &pll->pll_syn->en); + + spin_unlock_irqrestore(pll->common.lock, flags); + + 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..5f876efe48d5 --- /dev/null +++ b/drivers/clk/sophgo/clk-cv18xx-pll.h @@ -0,0 +1,119 @@ +/* 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_
Add driver for CV1800 series clock controller. Signed-off-by: Inochi Amaoto <inochiama@outlook.com> Link: https://github.com/milkv-duo/duo-files/blob/main/hardware/CV1800B/CV180X-Clock-v1.xlsx Link: https://github.com/milkv-duo/duo-files/blob/main/hardware/CV1800B/CV1800B-CV1801B-Preliminary-Datasheet-full-en.pdf --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/sophgo/Kconfig | 12 + drivers/clk/sophgo/Makefile | 7 + drivers/clk/sophgo/clk-cv1800.c | 1530 ++++++++++++++++++++++++ drivers/clk/sophgo/clk-cv1800.h | 123 ++ drivers/clk/sophgo/clk-cv18xx-common.c | 66 + drivers/clk/sophgo/clk-cv18xx-common.h | 81 ++ drivers/clk/sophgo/clk-cv18xx-ip.c | 905 ++++++++++++++ drivers/clk/sophgo/clk-cv18xx-ip.h | 265 ++++ drivers/clk/sophgo/clk-cv18xx-pll.c | 425 +++++++ drivers/clk/sophgo/clk-cv18xx-pll.h | 119 ++ 12 files changed, 3535 insertions(+) create mode 100644 drivers/clk/sophgo/Kconfig create mode 100644 drivers/clk/sophgo/Makefile create mode 100644 drivers/clk/sophgo/clk-cv1800.c create mode 100644 drivers/clk/sophgo/clk-cv1800.h create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.c create mode 100644 drivers/clk/sophgo/clk-cv18xx-common.h create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.c create mode 100644 drivers/clk/sophgo/clk-cv18xx-ip.h create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.c create mode 100644 drivers/clk/sophgo/clk-cv18xx-pll.h -- 2.43.0