| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/clk-provider.h> |
| #include <linux/of_address.h> |
| #include <linux/platform_device.h> |
| #include <linux/init.h> |
| #include <linux/slab.h> |
| #include <linux/regmap.h> |
| #include <linux/mfd/syscon.h> |
| #include <dt-bindings/clock/sm1-clkc.h> |
| |
| #include "clkc.h" |
| #include "sm1.h" |
| |
| #ifdef CONFIG_ARM |
| static const struct pll_params_table sm1_gp1_pll_params_table[] = { |
| /* set od 0 default */ |
| PLL_PARAMS(128, 1, 7), /*DCO=3072M OD=24M*/ |
| PLL_PARAMS(128, 1, 6), /*DCO=3072M OD=48M*/ |
| PLL_PARAMS(128, 1, 5), /*DCO=3072M OD=96M*/ |
| PLL_PARAMS(128, 1, 4), /*DCO=3072M OD=192M*/ |
| PLL_PARAMS(208, 1, 4), /*DCO=4992M OD=312M*/ |
| PLL_PARAMS(136, 1, 3), /*DCO=3264M OD=408M*/ |
| PLL_PARAMS(200, 1, 3), /*DCO=4800M OD=600M*/ |
| PLL_PARAMS(232, 1, 3), /*DCO=5568M OD=696M*/ |
| PLL_PARAMS(132, 1, 2), /*DCO=3168M OD=792M*/ |
| PLL_PARAMS(141, 1, 2), /*DCO=3384M OD=846M*/ |
| PLL_PARAMS(142, 1, 2), /*DCO=3408M OD=852M*/ |
| PLL_PARAMS(149, 1, 2), /*DCO=3576M OD=894M*/ |
| PLL_PARAMS(152, 1, 2), /*DCO=3648M OD=912M*/ |
| PLL_PARAMS(168, 1, 2), /*DCO=4032M OD=1008M*/ |
| PLL_PARAMS(184, 1, 2), /*DCO=4416M OD=1104M*/ |
| PLL_PARAMS(200, 1, 2), /*DCO=4800M OD=1200M*/ |
| PLL_PARAMS(216, 1, 2), /*DCO=5184M OD=1296M*/ |
| PLL_PARAMS(233, 1, 2), /*DCO=5592M OD=1398M*/ |
| PLL_PARAMS(249, 1, 2), /*DCO=5976M OD=1494M*/ |
| PLL_PARAMS(126, 1, 1), /*DCO=3024M OD=1512M*/ |
| { /* sentinel */ }, |
| }; |
| #else |
| static const struct pll_params_table sm1_gp1_pll_params_table[] = { |
| PLL_PARAMS(128, 1), /*DCO=3072M */ |
| PLL_PARAMS(128, 1), /*DCO=3072M */ |
| PLL_PARAMS(128, 1), /*DCO=3072M */ |
| PLL_PARAMS(128, 1), /*DCO=3072M */ |
| PLL_PARAMS(208, 1), /*DCO=4992M */ |
| PLL_PARAMS(136, 1), /*DCO=3264M */ |
| PLL_PARAMS(200, 1), /*DCO=4800M */ |
| PLL_PARAMS(232, 1), /*DCO=5568M */ |
| PLL_PARAMS(132, 1), /*DCO=3168M */ |
| PLL_PARAMS(141, 1), /*DCO=3384M */ |
| PLL_PARAMS(142, 1), /*DCO=3408M */ |
| PLL_PARAMS(149, 1), /*DCO=3576M */ |
| PLL_PARAMS(152, 1), /*DCO=3648M */ |
| PLL_PARAMS(168, 1), /*DCO=4032M */ |
| PLL_PARAMS(184, 1), /*DCO=4416M */ |
| PLL_PARAMS(200, 1), /*DCO=4800M */ |
| PLL_PARAMS(216, 1), /*DCO=5184M */ |
| PLL_PARAMS(233, 1), /*DCO=5592M */ |
| PLL_PARAMS(249, 1), /*DCO=5976M */ |
| PLL_PARAMS(126, 1), /*DCO=3024M */ |
| PLL_PARAMS(134, 1), /*DCO=3216M */ |
| PLL_PARAMS(142, 1), /*DCO=3408M */ |
| PLL_PARAMS(150, 1), /*DCO=3600M */ |
| PLL_PARAMS(158, 1), /*DCO=3792M */ |
| PLL_PARAMS(159, 1), /*DCO=3816M */ |
| PLL_PARAMS(160, 1), /*DCO=3840M */ |
| PLL_PARAMS(168, 1), /*DCO=4032M */ |
| { /* sentinel */ }, |
| }; |
| #endif |
| |
| /* |
| * Internal gp1 pll emulation configuration parameters |
| */ |
| static const struct reg_sequence sm1_gp1_init_regs[] = { |
| { .reg = HHI_GP1_PLL_CNTL1, .def = 0x00000000 }, |
| { .reg = HHI_GP1_PLL_CNTL2, .def = 0x00000000 }, |
| { .reg = HHI_GP1_PLL_CNTL3, .def = 0x48681c00 }, |
| { .reg = HHI_GP1_PLL_CNTL4, .def = 0x33771290 }, |
| { .reg = HHI_GP1_PLL_CNTL5, .def = 0x39272000 }, |
| { .reg = HHI_GP1_PLL_CNTL6, .def = 0x56540000, .delay_us = 10 }, |
| }; |
| |
| static struct clk_regmap sm1_gp1_pll_dco = { |
| .data = &(struct meson_clk_pll_data){ |
| .en = { |
| .reg_off = HHI_GP1_PLL_CNTL0, |
| .shift = 28, |
| .width = 1, |
| }, |
| .m = { |
| .reg_off = HHI_GP1_PLL_CNTL0, |
| .shift = 0, |
| .width = 8, |
| }, |
| .n = { |
| .reg_off = HHI_GP1_PLL_CNTL0, |
| .shift = 10, |
| .width = 5, |
| }, |
| /* for 32bit */ |
| .od = { |
| .reg_off = HHI_GP1_PLL_CNTL0, |
| .shift = 16, |
| .width = 3, |
| }, |
| .frac = { |
| .reg_off = HHI_GP1_PLL_CNTL1, |
| .shift = 0, |
| .width = 19, |
| }, |
| .l = { |
| .reg_off = HHI_GP1_PLL_CNTL0, |
| .shift = 31, |
| .width = 1, |
| }, |
| .rst = { |
| .reg_off = HHI_GP1_PLL_CNTL0, |
| .shift = 29, |
| .width = 1, |
| }, |
| .init_en = 0, |
| .table = sm1_gp1_pll_params_table, |
| .init_regs = sm1_gp1_init_regs, |
| .init_count = ARRAY_SIZE(sm1_gp1_init_regs), |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "sm1_gp1_pll_dco", |
| .ops = &meson_clk_pll_ops, |
| .parent_names = (const char *[]){ "sm1_ee_core" }, |
| .num_parents = 1, |
| .flags = CLK_IGNORE_UNUSED, |
| }, |
| }; |
| |
| #ifdef CONFIG_ARM |
| static struct clk_regmap sm1_gp1_pll = { |
| .hw.init = &(struct clk_init_data){ |
| .name = "gp1_pll", |
| .ops = &meson_pll_clk_no_ops, |
| .parent_names = (const char *[]){ "gp1_pll_dco" }, |
| .num_parents = 1, |
| .flags = CLK_SET_RATE_PARENT, |
| }, |
| }; |
| #else |
| static struct clk_regmap sm1_gp1_pll = { |
| .data = &(struct clk_regmap_div_data){ |
| .offset = HHI_GP1_PLL_CNTL0, |
| .shift = 16, |
| .width = 3, |
| .flags = CLK_DIVIDER_POWER_OF_TWO, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "gp1_pll", |
| .ops = &clk_regmap_divider_ops, |
| .parent_names = (const char *[]){ "gp1_pll_dco" }, |
| .num_parents = 1, |
| .flags = CLK_SET_RATE_PARENT, |
| }, |
| }; |
| #endif |
| |
| static const char * const media_parent_names[] = { "sm1_ee_core", |
| "gp0_pll", "hifi_pll", "fclk_div2p5", "fclk_div3", "fclk_div4", |
| "fclk_div5", "fclk_div7"}; |
| |
| static struct clk_regmap cts_vipnanoq_core_clk_mux = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_VIPNANOQ_CLK_CNTL, |
| .mask = 0x7, |
| .shift = 9, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "cts_vipnanoq_core_clk_mux", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = media_parent_names, |
| .num_parents = 8, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_vipnanoq_core_clk_div = { |
| .data = &(struct clk_regmap_div_data) { |
| .offset = HHI_VIPNANOQ_CLK_CNTL, |
| .shift = 0, |
| .width = 7, |
| }, |
| .hw.init = &(struct clk_init_data) { |
| .name = "cts_vipnanoq_core_clk_div", |
| .ops = &clk_regmap_divider_ops, |
| .parent_names = (const char *[]){ "cts_vipnanoq_core_clk_mux" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_vipnanoq_core_clk_gate = { |
| .data = &(struct clk_regmap_gate_data) { |
| .offset = HHI_VIPNANOQ_CLK_CNTL, |
| .bit_idx = 8, |
| }, |
| .hw.init = &(struct clk_init_data) { |
| .name = "cts_vipnanoq_core_clk_gate", |
| .ops = &clk_regmap_gate_ops, |
| .parent_names = (const char *[]){ "cts_vipnanoq_core_clk_div" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_vipnanoq_axi_clk_mux = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_VIPNANOQ_CLK_CNTL, |
| .mask = 0x7, |
| .shift = 25, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "cts_vipnanoq_axi_clk_mux", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = media_parent_names, |
| .num_parents = 8, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_vipnanoq_axi_clk_div = { |
| .data = &(struct clk_regmap_div_data) { |
| .offset = HHI_VIPNANOQ_CLK_CNTL, |
| .shift = 16, |
| .width = 7, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "cts_vipnanoq_axi_clk_div", |
| .ops = &clk_regmap_divider_ops, |
| .parent_names = (const char *[]){ "cts_vipnanoq_axi_clk_mux" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_vipnanoq_axi_clk_gate = { |
| .data = &(struct clk_regmap_gate_data) { |
| .offset = HHI_VIPNANOQ_CLK_CNTL, |
| .bit_idx = 24, |
| }, |
| .hw.init = &(struct clk_init_data) { |
| .name = "cts_vipnanoq_axi_clk_gate", |
| .ops = &clk_regmap_gate_ops, |
| .parent_names = (const char *[]){ "cts_vipnanoq_axi_clk_div" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static const char * const media_parent_names_mipi[] = { "sm1_ee_core", |
| "gp0_pll", "mpll1", "mpll2", "fclk_div3", "fclk_div4", |
| "fclk_div5", "fclk_div7" |
| }; |
| |
| static struct clk_regmap cts_mipi_csi_phy_clk_mux = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_MIPI_CSI_PHY_CLK_CNTL, |
| .mask = 3, |
| .shift = 9, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "cts_mipi_csi_phy_clk_mux", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = media_parent_names_mipi, |
| .num_parents = 8, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_mipi_csi_phy_clk_div = { |
| .data = &(struct clk_regmap_div_data) { |
| .offset = HHI_MIPI_CSI_PHY_CLK_CNTL, |
| .shift = 0, |
| .width = 7, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "cts_mipi_csi_phy_clk_div", |
| .ops = &clk_regmap_divider_ops, |
| .parent_names = (const char *[]){ "cts_mipi_csi_phy_clk_mux" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_mipi_csi_phy_clk_gate = { |
| .data = &(struct clk_regmap_gate_data) { |
| .offset = HHI_MIPI_CSI_PHY_CLK_CNTL, |
| .bit_idx = 8, |
| }, |
| .hw.init = &(struct clk_init_data) { |
| .name = "cts_mipi_csi_phy_clk_gate", |
| .ops = &clk_regmap_gate_ops, |
| .parent_names = (const char *[]){ "cts_mipi_csi_phy_clk_div" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static const char * const media_parent_names_adapt[] = { "sm1_ee_core", |
| "fclk_div4", "fclk_div3", "fclk_div5", "fclk_div7", "mpll2", |
| "mpll3", "gp0_pll" |
| }; |
| |
| static struct clk_regmap cts_csi_adapt_clk_mux = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_CSI2_ADAPT_CLK_CNTL, |
| .mask = 0x7, |
| .shift = 9, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "cts_csi_adapt_clk_mux", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = media_parent_names_adapt, |
| .num_parents = 8, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_csi_adapt_clk_div = { |
| .data = &(struct clk_regmap_div_data) { |
| .offset = HHI_CSI2_ADAPT_CLK_CNTL, |
| .shift = 0, |
| .width = 7, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "cts_csi_adapt_clk_div", |
| .ops = &clk_regmap_divider_ops, |
| .parent_names = (const char *[]){ "cts_csi_adapt_clk_mux" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap cts_csi_adapt_clk_gate = { |
| .data = &(struct clk_regmap_gate_data) { |
| .offset = HHI_CSI2_ADAPT_CLK_CNTL, |
| .bit_idx = 8, |
| }, |
| .hw.init = &(struct clk_init_data) { |
| .name = "cts_csi_adapt_clk_gate", |
| .ops = &clk_regmap_gate_ops, |
| .parent_names = (const char *[]){ "cts_csi_adapt_clk_div" }, |
| .num_parents = 1, |
| .flags = CLK_GET_RATE_NOCACHE, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_pre_src_clk_mux0 = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .mask = 0x3, |
| .shift = 0, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_pre_src0", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = (const char *[]){ "sm1_ee_core", "fclk_div2", |
| "fclk_div3", "gp1_pll" }, |
| .num_parents = 4, |
| .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_pre_src_clk_mux1 = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .mask = 0x3, |
| .shift = 16, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_pre_src1", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = (const char *[]){ "sm1_ee_core", "fclk_div2", |
| "fclk_div3", "gp1_pll" }, |
| .num_parents = 4, |
| .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_clk_div0 = { |
| .data = &(struct clk_regmap_div_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .shift = 4, |
| .width = 6, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_clk_div0", |
| .ops = &clk_regmap_divider_ops, |
| .parent_names = (const char *[]){ "dsu_pre_src0" }, |
| .num_parents = 1, |
| .flags = CLK_SET_RATE_PARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_clk_div1 = { |
| .data = &(struct clk_regmap_div_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .shift = 20, |
| .width = 6, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_clk_div1", |
| .ops = &clk_regmap_divider_ops, |
| .parent_names = (const char *[]){ "dsu_pre_src1" }, |
| .num_parents = 1, |
| .flags = CLK_SET_RATE_PARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_pre_clk_mux0 = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .mask = 0x1, |
| .shift = 2, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_pre0", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = (const char *[]){ "dsu_pre_src0", |
| "dsu_clk_div0",}, |
| .num_parents = 2, |
| .flags = CLK_SET_RATE_PARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_pre_clk_mux1 = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .mask = 0x1, |
| .shift = 18, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_pre1", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = (const char *[]){ "dsu_pre_src1", |
| "dsu_clk_div1",}, |
| .num_parents = 2, |
| .flags = CLK_SET_RATE_PARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_pre_post_clk_mux = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .mask = 0x1, |
| .shift = 10, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_pre_post", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = (const char *[]){ "dsu_pre0", |
| "dsu_pre1",}, |
| .num_parents = 2, |
| .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_pre_clk = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL5, |
| .mask = 0x1, |
| .shift = 11, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_pre_clk", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = (const char *[]){ "dsu_pre_post", |
| "sys_pll",}, |
| .num_parents = 2, |
| .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, |
| }, |
| }; |
| |
| static struct clk_regmap sm1_dsu_clk = { |
| .data = &(struct clk_regmap_mux_data) { |
| .offset = HHI_SYS_CPU_CLK_CNTL6, |
| .mask = 0x1, |
| .shift = 27, |
| }, |
| .hw.init = &(struct clk_init_data){ |
| .name = "dsu_clk", |
| .ops = &clk_regmap_mux_ops, |
| .parent_names = (const char *[]){ "cpu_clk", |
| "dsu_pre_clk",}, |
| .num_parents = 2, |
| .flags = CLK_SET_RATE_PARENT, |
| }, |
| }; |
| |
| static MESON_GATE(sm1_csi_dig, HHI_GCLK_MPEG1, 18); |
| static MESON_GATE(sm1_nna, HHI_GCLK_MPEG1, 19); |
| static MESON_GATE(sm1_parser1, HHI_GCLK_MPEG1, 28); |
| static MESON_GATE(sm1_csi_host, HHI_GCLK_MPEG2, 16); |
| static MESON_GATE(sm1_csi_adpat, HHI_GCLK_MPEG2, 17); |
| static MESON_GATE(sm1_temp_sensor, HHI_GCLK_MPEG2, 22); |
| static MESON_GATE(sm1_csi_phy, HHI_GCLK_MPEG2, 29); |
| |
| static struct clk_regmap *const sm1_clk_regmaps[] = { |
| &sm1_gp1_pll_dco, |
| &sm1_gp1_pll, |
| &sm1_dsu_pre_src_clk_mux0, |
| &sm1_dsu_pre_src_clk_mux1, |
| &sm1_dsu_clk_div0, |
| &sm1_dsu_clk_div1, |
| &sm1_dsu_pre_clk_mux0, |
| &sm1_dsu_pre_clk_mux1, |
| &sm1_dsu_pre_post_clk_mux, |
| &sm1_dsu_pre_clk, |
| &sm1_dsu_clk, |
| &sm1_csi_dig, |
| &sm1_nna, |
| &sm1_parser1, |
| &sm1_csi_host, |
| &sm1_csi_adpat, |
| &sm1_temp_sensor, |
| &sm1_csi_phy, |
| &cts_vipnanoq_core_clk_mux, |
| &cts_vipnanoq_core_clk_div, |
| &cts_vipnanoq_core_clk_gate, |
| &cts_vipnanoq_axi_clk_mux, |
| &cts_vipnanoq_axi_clk_div, |
| &cts_vipnanoq_axi_clk_gate, |
| &cts_mipi_csi_phy_clk_mux, |
| &cts_mipi_csi_phy_clk_div, |
| &cts_mipi_csi_phy_clk_gate, |
| &cts_csi_adapt_clk_mux, |
| &cts_csi_adapt_clk_div, |
| &cts_csi_adapt_clk_gate, |
| }; |
| |
| static struct clk_hw_onecell_data sm1_hw_onecell_data = { |
| .hws = { |
| [CLKID_GP1_PLL_DCO] = &sm1_gp1_pll_dco.hw, |
| [CLKID_GP1_PLL] = &sm1_gp1_pll.hw, |
| [CLKID_DSU_PRE_SRC0] = &sm1_dsu_pre_src_clk_mux0.hw, |
| [CLKID_DSU_PRE_SRC1] = &sm1_dsu_pre_src_clk_mux1.hw, |
| [CLKID_DSU_CLK_DIV0] = &sm1_dsu_clk_div0.hw, |
| [CLKID_DSU_CLK_DIV1] = &sm1_dsu_clk_div1.hw, |
| [CLKID_DSU_PRE_MUX0] = &sm1_dsu_pre_clk_mux0.hw, |
| [CLKID_DSU_PRE_MUX1] = &sm1_dsu_pre_clk_mux1.hw, |
| [CLKID_DSU_PRE_POST_MUX] = &sm1_dsu_pre_post_clk_mux.hw, |
| [CLKID_DSU_PRE_CLK] = &sm1_dsu_pre_clk.hw, |
| [CLKID_DSU_CLK] = &sm1_dsu_clk.hw, |
| [CLKID_CSI_DIG_CLK] = &sm1_csi_dig.hw, |
| [CLKID_NNA_CLK] = &sm1_nna.hw, |
| [CLKID_PARSER1_CLK] = &sm1_parser1.hw, |
| [CLKID_CSI_HOST_CLK] = &sm1_csi_host.hw, |
| [CLKID_CSI_ADPAT_CLK] = &sm1_csi_adpat.hw, |
| [CLKID_TEMP_SENSOR_CLK] = &sm1_temp_sensor.hw, |
| [CLKID_CSI_PHY_CLK] = &sm1_csi_phy.hw, |
| [CLKID_VNANOQ_CORE_MUX_CLK] = &cts_vipnanoq_core_clk_mux.hw, |
| [CLKID_VNANOQ_CORE_DIV_CLK] = &cts_vipnanoq_core_clk_div.hw, |
| [CLKID_VNANOQ_CORE_CLK_COMP] = &cts_vipnanoq_core_clk_gate.hw, |
| [CLKID_VNANOQ_AXI_MUX_CLK] = &cts_vipnanoq_axi_clk_mux.hw, |
| [CLKID_VNANOQ_AXI_DIV_CLK] = &cts_vipnanoq_axi_clk_div.hw, |
| [CLKID_VNANOQ_AXI_CLK_COMP] = &cts_vipnanoq_axi_clk_gate.hw, |
| [CLKID_MIPI_CSI_PHY_MUX_CLK] = &cts_mipi_csi_phy_clk_mux.hw, |
| [CLKID_MIPI_CSI_PHY_DIV_CLK] = &cts_mipi_csi_phy_clk_div.hw, |
| [CLKID_MIPI_CSI_PHY_CLK_COMP] = &cts_mipi_csi_phy_clk_gate.hw, |
| [CLKID_CSI_ADAPT_MUX_CLK] = &cts_csi_adapt_clk_mux.hw, |
| [CLKID_CSI_ADAPT_DIV_CLK] = &cts_csi_adapt_clk_div.hw, |
| [CLKID_CSI_ADAPT_CLK_COMP] = &cts_csi_adapt_clk_gate.hw, |
| }, |
| .num = SM1_NR_CLKS, |
| }; |
| |
| static const struct of_device_id clkc_match_table[] = { |
| { .compatible = "amlogic,sm1-clkc-1" }, |
| {} |
| }; |
| |
| struct sm1_nb_data { |
| struct notifier_block nb; |
| }; |
| |
| static int sm1_dsu_mux_clk_notifier_cb(struct notifier_block *nb, |
| unsigned long event, void *data) |
| { |
| struct clk *dsu_pre_clk, *parent_clk; |
| int ret; |
| |
| switch (event) { |
| case PRE_RATE_CHANGE: |
| parent_clk = sm1_dsu_pre_clk_mux1.hw.clk; |
| break; |
| case POST_RATE_CHANGE: |
| parent_clk = sm1_dsu_pre_clk_mux0.hw.clk; |
| break; |
| default: |
| return NOTIFY_DONE; |
| } |
| |
| dsu_pre_clk = sm1_dsu_pre_post_clk_mux.hw.clk; |
| |
| ret = clk_set_parent(dsu_pre_clk, parent_clk); |
| if (ret) |
| return notifier_from_errno(ret); |
| |
| usleep_range(80, 120); |
| |
| return NOTIFY_OK; |
| } |
| |
| static struct sm1_nb_data sm1_dsu_nb_data = { |
| .nb.notifier_call = sm1_dsu_mux_clk_notifier_cb, |
| }; |
| |
| static int sm1_clkc_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct regmap *map; |
| struct clk_hw *hw; |
| int ret, i; |
| struct clk *fclk_div2; |
| |
| /* Get the hhi system controller node */ |
| map = syscon_node_to_regmap(of_get_parent(dev->of_node)); |
| if (IS_ERR(map)) { |
| dev_err(dev, "failed to get HHI regmap\n"); |
| return PTR_ERR(map); |
| } |
| hw = meson_clk_hw_register_input(dev, "core", "sm1_ee_core", 0); |
| if (IS_ERR(hw)) |
| return PTR_ERR(hw); |
| sm1_hw_onecell_data.hws[CLKID_SM1_EE_CLK] = hw; |
| |
| /* Populate regmap for the regmap backed clocks */ |
| for (i = 0; i < ARRAY_SIZE(sm1_clk_regmaps); i++) |
| sm1_clk_regmaps[i]->map = map; |
| |
| for (i = 1; i < sm1_hw_onecell_data.num; i++) { |
| /* array might be sparse */ |
| if (!sm1_hw_onecell_data.hws[i]) |
| continue; |
| ret = devm_clk_hw_register(dev, sm1_hw_onecell_data.hws[i]); |
| if (ret) { |
| dev_err(dev, "Clock registration failed %d\n", i); |
| return ret; |
| } |
| } |
| |
| /* set sm1_dsu_pre_src_clk_mux1 parent to fclk_div2 1G */ |
| fclk_div2 = of_clk_get_by_name(dev->of_node, "clkin0"); |
| if (IS_ERR(fclk_div2)) { |
| pr_err("faied to get clkin0\n"); |
| return PTR_ERR(fclk_div2); |
| } |
| ret = clk_set_parent(sm1_dsu_pre_src_clk_mux1.hw.clk, fclk_div2); |
| if (ret < 0) { |
| pr_err("%s: failed to set parent for dsu_pre_src_clk_mux1\n", |
| __func__); |
| return ret; |
| } |
| |
| /* |
| * when change sm1_dsu_pre_clk_mux0, switch to |
| * sm1_dsu_pre_clk_mux1 to avoid crash |
| */ |
| ret = clk_notifier_register(sm1_dsu_pre_clk_mux0.hw.clk, |
| &sm1_dsu_nb_data.nb); |
| if (ret) { |
| pr_err("%s:failed to register clock notifier for dsu\n", |
| __func__); |
| return ret; |
| } |
| |
| return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, |
| &sm1_hw_onecell_data); |
| } |
| |
| static struct platform_driver sm1_clk_driver = { |
| .probe = sm1_clkc_probe, |
| .driver = { |
| .name = "sm1-clkc", |
| .of_match_table = clkc_match_table, |
| }, |
| }; |
| |
| static int sm1_clkc_init(void) |
| { |
| return platform_driver_register(&sm1_clk_driver); |
| } |
| arch_initcall_sync(sm1_clkc_init); |