1ca574a5dSDmitry Baryshkov // SPDX-License-Identifier: GPL-2.0 2ca574a5dSDmitry Baryshkov /* 3ca574a5dSDmitry Baryshkov * Copyright (c) 2022, 2023 Linaro Ltd. 4ca574a5dSDmitry Baryshkov */ 5ca574a5dSDmitry Baryshkov #include <linux/bitfield.h> 6ca574a5dSDmitry Baryshkov #include <linux/clk.h> 7ca574a5dSDmitry Baryshkov #include <linux/clk-provider.h> 812dc7195SDmitry Baryshkov #include <linux/interconnect-clk.h> 912dc7195SDmitry Baryshkov #include <linux/interconnect-provider.h> 10ca574a5dSDmitry Baryshkov #include <linux/of.h> 11ca574a5dSDmitry Baryshkov #include <linux/module.h> 12ca574a5dSDmitry Baryshkov #include <linux/platform_device.h> 13ca574a5dSDmitry Baryshkov #include <linux/regmap.h> 14ca574a5dSDmitry Baryshkov 1512dc7195SDmitry Baryshkov #include <dt-bindings/interconnect/qcom,msm8996-cbf.h> 1612dc7195SDmitry Baryshkov 17ca574a5dSDmitry Baryshkov #include "clk-alpha-pll.h" 18ca574a5dSDmitry Baryshkov #include "clk-regmap.h" 19ca574a5dSDmitry Baryshkov 20ca574a5dSDmitry Baryshkov /* Need to match the order of clocks in DT binding */ 21ca574a5dSDmitry Baryshkov enum { 22ca574a5dSDmitry Baryshkov DT_XO, 23ca574a5dSDmitry Baryshkov DT_APCS_AUX, 24ca574a5dSDmitry Baryshkov }; 25ca574a5dSDmitry Baryshkov 26ca574a5dSDmitry Baryshkov enum { 27ca574a5dSDmitry Baryshkov CBF_XO_INDEX, 28ca574a5dSDmitry Baryshkov CBF_PLL_INDEX, 29ca574a5dSDmitry Baryshkov CBF_DIV_INDEX, 30ca574a5dSDmitry Baryshkov CBF_APCS_AUX_INDEX, 31ca574a5dSDmitry Baryshkov }; 32ca574a5dSDmitry Baryshkov 33ca574a5dSDmitry Baryshkov #define DIV_THRESHOLD 600000000 34ca574a5dSDmitry Baryshkov 35ca574a5dSDmitry Baryshkov #define CBF_MUX_OFFSET 0x18 36ca574a5dSDmitry Baryshkov #define CBF_MUX_PARENT_MASK GENMASK(1, 0) 37ca574a5dSDmitry Baryshkov #define CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK GENMASK(5, 4) 38ca574a5dSDmitry Baryshkov #define CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL \ 39ca574a5dSDmitry Baryshkov FIELD_PREP(CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 0x03) 40ca574a5dSDmitry Baryshkov #define CBF_MUX_AUTO_CLK_SEL_BIT BIT(6) 41ca574a5dSDmitry Baryshkov 42ca574a5dSDmitry Baryshkov #define CBF_PLL_OFFSET 0xf000 43ca574a5dSDmitry Baryshkov 44bc48641aSYassine Oudjana static struct alpha_pll_config cbfpll_config = { 45ca574a5dSDmitry Baryshkov .l = 72, 46ca574a5dSDmitry Baryshkov .config_ctl_val = 0x200d4828, 47ca574a5dSDmitry Baryshkov .config_ctl_hi_val = 0x006, 48ca574a5dSDmitry Baryshkov .test_ctl_val = 0x1c000000, 49ca574a5dSDmitry Baryshkov .test_ctl_hi_val = 0x00004000, 50ca574a5dSDmitry Baryshkov .pre_div_mask = BIT(12), 51ca574a5dSDmitry Baryshkov .post_div_mask = 0x3 << 8, 52ca574a5dSDmitry Baryshkov .post_div_val = 0x1 << 8, 53ca574a5dSDmitry Baryshkov .main_output_mask = BIT(0), 54ca574a5dSDmitry Baryshkov .early_output_mask = BIT(3), 55ca574a5dSDmitry Baryshkov }; 56ca574a5dSDmitry Baryshkov 57ca574a5dSDmitry Baryshkov static struct clk_alpha_pll cbf_pll = { 58ca574a5dSDmitry Baryshkov .offset = CBF_PLL_OFFSET, 59f3574392SGabor Juhos .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_HUAYRA_APSS], 60ca574a5dSDmitry Baryshkov .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, 61ca574a5dSDmitry Baryshkov .clkr.hw.init = &(struct clk_init_data){ 62ca574a5dSDmitry Baryshkov .name = "cbf_pll", 63ca574a5dSDmitry Baryshkov .parent_data = (const struct clk_parent_data[]) { 64ca574a5dSDmitry Baryshkov { .index = DT_XO, }, 65ca574a5dSDmitry Baryshkov }, 66ca574a5dSDmitry Baryshkov .num_parents = 1, 67ca574a5dSDmitry Baryshkov .ops = &clk_alpha_pll_hwfsm_ops, 68ca574a5dSDmitry Baryshkov }, 69ca574a5dSDmitry Baryshkov }; 70ca574a5dSDmitry Baryshkov 71ca574a5dSDmitry Baryshkov static struct clk_fixed_factor cbf_pll_postdiv = { 72ca574a5dSDmitry Baryshkov .mult = 1, 73ca574a5dSDmitry Baryshkov .div = 2, 74ca574a5dSDmitry Baryshkov .hw.init = &(struct clk_init_data){ 75ca574a5dSDmitry Baryshkov .name = "cbf_pll_postdiv", 76ca574a5dSDmitry Baryshkov .parent_hws = (const struct clk_hw*[]){ 77ca574a5dSDmitry Baryshkov &cbf_pll.clkr.hw 78ca574a5dSDmitry Baryshkov }, 79ca574a5dSDmitry Baryshkov .num_parents = 1, 80ca574a5dSDmitry Baryshkov .ops = &clk_fixed_factor_ops, 81ca574a5dSDmitry Baryshkov .flags = CLK_SET_RATE_PARENT, 82ca574a5dSDmitry Baryshkov }, 83ca574a5dSDmitry Baryshkov }; 84ca574a5dSDmitry Baryshkov 85ca574a5dSDmitry Baryshkov static const struct clk_parent_data cbf_mux_parent_data[] = { 86ca574a5dSDmitry Baryshkov { .index = DT_XO }, 87ca574a5dSDmitry Baryshkov { .hw = &cbf_pll.clkr.hw }, 88ca574a5dSDmitry Baryshkov { .hw = &cbf_pll_postdiv.hw }, 89ca574a5dSDmitry Baryshkov { .index = DT_APCS_AUX }, 90ca574a5dSDmitry Baryshkov }; 91ca574a5dSDmitry Baryshkov 92ca574a5dSDmitry Baryshkov struct clk_cbf_8996_mux { 93ca574a5dSDmitry Baryshkov u32 reg; 94ca574a5dSDmitry Baryshkov struct notifier_block nb; 95ca574a5dSDmitry Baryshkov struct clk_regmap clkr; 96ca574a5dSDmitry Baryshkov }; 97ca574a5dSDmitry Baryshkov 98ca574a5dSDmitry Baryshkov static struct clk_cbf_8996_mux *to_clk_cbf_8996_mux(struct clk_regmap *clkr) 99ca574a5dSDmitry Baryshkov { 100ca574a5dSDmitry Baryshkov return container_of(clkr, struct clk_cbf_8996_mux, clkr); 101ca574a5dSDmitry Baryshkov } 102ca574a5dSDmitry Baryshkov 103ca574a5dSDmitry Baryshkov static int cbf_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 104ca574a5dSDmitry Baryshkov void *data); 105ca574a5dSDmitry Baryshkov 106ca574a5dSDmitry Baryshkov static u8 clk_cbf_8996_mux_get_parent(struct clk_hw *hw) 107ca574a5dSDmitry Baryshkov { 108ca574a5dSDmitry Baryshkov struct clk_regmap *clkr = to_clk_regmap(hw); 109ca574a5dSDmitry Baryshkov struct clk_cbf_8996_mux *mux = to_clk_cbf_8996_mux(clkr); 110ca574a5dSDmitry Baryshkov u32 val; 111ca574a5dSDmitry Baryshkov 112ca574a5dSDmitry Baryshkov regmap_read(clkr->regmap, mux->reg, &val); 113ca574a5dSDmitry Baryshkov 114ca574a5dSDmitry Baryshkov return FIELD_GET(CBF_MUX_PARENT_MASK, val); 115ca574a5dSDmitry Baryshkov } 116ca574a5dSDmitry Baryshkov 117ca574a5dSDmitry Baryshkov static int clk_cbf_8996_mux_set_parent(struct clk_hw *hw, u8 index) 118ca574a5dSDmitry Baryshkov { 119ca574a5dSDmitry Baryshkov struct clk_regmap *clkr = to_clk_regmap(hw); 120ca574a5dSDmitry Baryshkov struct clk_cbf_8996_mux *mux = to_clk_cbf_8996_mux(clkr); 121ca574a5dSDmitry Baryshkov u32 val; 122ca574a5dSDmitry Baryshkov 123ca574a5dSDmitry Baryshkov val = FIELD_PREP(CBF_MUX_PARENT_MASK, index); 124ca574a5dSDmitry Baryshkov 125ca574a5dSDmitry Baryshkov return regmap_update_bits(clkr->regmap, mux->reg, CBF_MUX_PARENT_MASK, val); 126ca574a5dSDmitry Baryshkov } 127ca574a5dSDmitry Baryshkov 128ca574a5dSDmitry Baryshkov static int clk_cbf_8996_mux_determine_rate(struct clk_hw *hw, 129ca574a5dSDmitry Baryshkov struct clk_rate_request *req) 130ca574a5dSDmitry Baryshkov { 131ca574a5dSDmitry Baryshkov struct clk_hw *parent; 132ca574a5dSDmitry Baryshkov 133bc48641aSYassine Oudjana if (req->rate < (DIV_THRESHOLD / cbf_pll_postdiv.div)) 134ca574a5dSDmitry Baryshkov return -EINVAL; 135ca574a5dSDmitry Baryshkov 136ca574a5dSDmitry Baryshkov if (req->rate < DIV_THRESHOLD) 137ca574a5dSDmitry Baryshkov parent = clk_hw_get_parent_by_index(hw, CBF_DIV_INDEX); 138ca574a5dSDmitry Baryshkov else 139ca574a5dSDmitry Baryshkov parent = clk_hw_get_parent_by_index(hw, CBF_PLL_INDEX); 140ca574a5dSDmitry Baryshkov 141ca574a5dSDmitry Baryshkov if (!parent) 142ca574a5dSDmitry Baryshkov return -EINVAL; 143ca574a5dSDmitry Baryshkov 144ca574a5dSDmitry Baryshkov req->best_parent_rate = clk_hw_round_rate(parent, req->rate); 145ca574a5dSDmitry Baryshkov req->best_parent_hw = parent; 146ca574a5dSDmitry Baryshkov 147ca574a5dSDmitry Baryshkov return 0; 148ca574a5dSDmitry Baryshkov } 149ca574a5dSDmitry Baryshkov 150ca574a5dSDmitry Baryshkov static const struct clk_ops clk_cbf_8996_mux_ops = { 151ca574a5dSDmitry Baryshkov .set_parent = clk_cbf_8996_mux_set_parent, 152ca574a5dSDmitry Baryshkov .get_parent = clk_cbf_8996_mux_get_parent, 153ca574a5dSDmitry Baryshkov .determine_rate = clk_cbf_8996_mux_determine_rate, 154ca574a5dSDmitry Baryshkov }; 155ca574a5dSDmitry Baryshkov 156ca574a5dSDmitry Baryshkov static struct clk_cbf_8996_mux cbf_mux = { 157ca574a5dSDmitry Baryshkov .reg = CBF_MUX_OFFSET, 158ca574a5dSDmitry Baryshkov .nb.notifier_call = cbf_clk_notifier_cb, 159ca574a5dSDmitry Baryshkov .clkr.hw.init = &(struct clk_init_data) { 160ca574a5dSDmitry Baryshkov .name = "cbf_mux", 161ca574a5dSDmitry Baryshkov .parent_data = cbf_mux_parent_data, 162ca574a5dSDmitry Baryshkov .num_parents = ARRAY_SIZE(cbf_mux_parent_data), 163ca574a5dSDmitry Baryshkov .ops = &clk_cbf_8996_mux_ops, 164ca574a5dSDmitry Baryshkov /* CPU clock is critical and should never be gated */ 165ca574a5dSDmitry Baryshkov .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 166ca574a5dSDmitry Baryshkov }, 167ca574a5dSDmitry Baryshkov }; 168ca574a5dSDmitry Baryshkov 169ca574a5dSDmitry Baryshkov static int cbf_clk_notifier_cb(struct notifier_block *nb, unsigned long event, 170ca574a5dSDmitry Baryshkov void *data) 171ca574a5dSDmitry Baryshkov { 172ca574a5dSDmitry Baryshkov struct clk_notifier_data *cnd = data; 173ca574a5dSDmitry Baryshkov 174ca574a5dSDmitry Baryshkov switch (event) { 175ca574a5dSDmitry Baryshkov case PRE_RATE_CHANGE: 176ca574a5dSDmitry Baryshkov /* 177ca574a5dSDmitry Baryshkov * Avoid overvolting. clk_core_set_rate_nolock() walks from top 178ca574a5dSDmitry Baryshkov * to bottom, so it will change the rate of the PLL before 179ca574a5dSDmitry Baryshkov * chaging the parent of PMUX. This can result in pmux getting 180ca574a5dSDmitry Baryshkov * clocked twice the expected rate. 181ca574a5dSDmitry Baryshkov * 182ca574a5dSDmitry Baryshkov * Manually switch to PLL/2 here. 183ca574a5dSDmitry Baryshkov */ 184ca574a5dSDmitry Baryshkov if (cnd->old_rate > DIV_THRESHOLD && 185ca574a5dSDmitry Baryshkov cnd->new_rate < DIV_THRESHOLD) 186ca574a5dSDmitry Baryshkov clk_cbf_8996_mux_set_parent(&cbf_mux.clkr.hw, CBF_DIV_INDEX); 187ca574a5dSDmitry Baryshkov break; 188ca574a5dSDmitry Baryshkov case ABORT_RATE_CHANGE: 189ca574a5dSDmitry Baryshkov /* Revert manual change */ 190ca574a5dSDmitry Baryshkov if (cnd->new_rate < DIV_THRESHOLD && 191ca574a5dSDmitry Baryshkov cnd->old_rate > DIV_THRESHOLD) 192ca574a5dSDmitry Baryshkov clk_cbf_8996_mux_set_parent(&cbf_mux.clkr.hw, CBF_PLL_INDEX); 193ca574a5dSDmitry Baryshkov break; 194ca574a5dSDmitry Baryshkov default: 195ca574a5dSDmitry Baryshkov break; 196ca574a5dSDmitry Baryshkov } 197ca574a5dSDmitry Baryshkov 198ca574a5dSDmitry Baryshkov return notifier_from_errno(0); 199ca574a5dSDmitry Baryshkov }; 200ca574a5dSDmitry Baryshkov 201ca574a5dSDmitry Baryshkov static struct clk_hw *cbf_msm8996_hw_clks[] = { 202ca574a5dSDmitry Baryshkov &cbf_pll_postdiv.hw, 203ca574a5dSDmitry Baryshkov }; 204ca574a5dSDmitry Baryshkov 205ca574a5dSDmitry Baryshkov static struct clk_regmap *cbf_msm8996_clks[] = { 206ca574a5dSDmitry Baryshkov &cbf_pll.clkr, 207ca574a5dSDmitry Baryshkov &cbf_mux.clkr, 208ca574a5dSDmitry Baryshkov }; 209ca574a5dSDmitry Baryshkov 210ca574a5dSDmitry Baryshkov static const struct regmap_config cbf_msm8996_regmap_config = { 211ca574a5dSDmitry Baryshkov .reg_bits = 32, 212ca574a5dSDmitry Baryshkov .reg_stride = 4, 213ca574a5dSDmitry Baryshkov .val_bits = 32, 214ca574a5dSDmitry Baryshkov .max_register = 0x10000, 215ca574a5dSDmitry Baryshkov .fast_io = true, 216ca574a5dSDmitry Baryshkov .val_format_endian = REGMAP_ENDIAN_LITTLE, 217ca574a5dSDmitry Baryshkov }; 218ca574a5dSDmitry Baryshkov 21912dc7195SDmitry Baryshkov #ifdef CONFIG_INTERCONNECT 22012dc7195SDmitry Baryshkov 22112dc7195SDmitry Baryshkov /* Random ID that doesn't clash with main qnoc and OSM */ 22212dc7195SDmitry Baryshkov #define CBF_MASTER_NODE 2000 22312dc7195SDmitry Baryshkov 22412dc7195SDmitry Baryshkov static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw) 22512dc7195SDmitry Baryshkov { 22612dc7195SDmitry Baryshkov struct device *dev = &pdev->dev; 22712dc7195SDmitry Baryshkov struct clk *clk = devm_clk_hw_get_clk(dev, cbf_hw, "cbf"); 22812dc7195SDmitry Baryshkov const struct icc_clk_data data[] = { 229f45b94ffSVaradarajan Narayanan { 230f45b94ffSVaradarajan Narayanan .clk = clk, 231f45b94ffSVaradarajan Narayanan .name = "cbf", 232f45b94ffSVaradarajan Narayanan .master_id = MASTER_CBF_M4M, 233f45b94ffSVaradarajan Narayanan .slave_id = SLAVE_CBF_M4M, 234f45b94ffSVaradarajan Narayanan }, 23512dc7195SDmitry Baryshkov }; 23612dc7195SDmitry Baryshkov struct icc_provider *provider; 23712dc7195SDmitry Baryshkov 23812dc7195SDmitry Baryshkov provider = icc_clk_register(dev, CBF_MASTER_NODE, ARRAY_SIZE(data), data); 23912dc7195SDmitry Baryshkov if (IS_ERR(provider)) 24012dc7195SDmitry Baryshkov return PTR_ERR(provider); 24112dc7195SDmitry Baryshkov 24212dc7195SDmitry Baryshkov platform_set_drvdata(pdev, provider); 24312dc7195SDmitry Baryshkov 24412dc7195SDmitry Baryshkov return 0; 24512dc7195SDmitry Baryshkov } 24612dc7195SDmitry Baryshkov 247abaf59c4SUwe Kleine-König static void qcom_msm8996_cbf_icc_remove(struct platform_device *pdev) 24812dc7195SDmitry Baryshkov { 24912dc7195SDmitry Baryshkov struct icc_provider *provider = platform_get_drvdata(pdev); 25012dc7195SDmitry Baryshkov 25112dc7195SDmitry Baryshkov icc_clk_unregister(provider); 25212dc7195SDmitry Baryshkov } 25312dc7195SDmitry Baryshkov #define qcom_msm8996_cbf_icc_sync_state icc_sync_state 25412dc7195SDmitry Baryshkov #else 25512dc7195SDmitry Baryshkov static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw) 25612dc7195SDmitry Baryshkov { 25712dc7195SDmitry Baryshkov dev_warn(&pdev->dev, "CONFIG_INTERCONNECT is disabled, CBF clock is fixed\n"); 25812dc7195SDmitry Baryshkov 25912dc7195SDmitry Baryshkov return 0; 26012dc7195SDmitry Baryshkov } 261abaf59c4SUwe Kleine-König #define qcom_msm8996_cbf_icc_remove(pdev) { } 26212dc7195SDmitry Baryshkov #define qcom_msm8996_cbf_icc_sync_state NULL 26312dc7195SDmitry Baryshkov #endif 26412dc7195SDmitry Baryshkov 265ca574a5dSDmitry Baryshkov static int qcom_msm8996_cbf_probe(struct platform_device *pdev) 266ca574a5dSDmitry Baryshkov { 267ca574a5dSDmitry Baryshkov void __iomem *base; 268ca574a5dSDmitry Baryshkov struct regmap *regmap; 269ca574a5dSDmitry Baryshkov struct device *dev = &pdev->dev; 270ca574a5dSDmitry Baryshkov int i, ret; 271ca574a5dSDmitry Baryshkov 272ca574a5dSDmitry Baryshkov base = devm_platform_ioremap_resource(pdev, 0); 273ca574a5dSDmitry Baryshkov if (IS_ERR(base)) 274ca574a5dSDmitry Baryshkov return PTR_ERR(base); 275ca574a5dSDmitry Baryshkov 276ca574a5dSDmitry Baryshkov regmap = devm_regmap_init_mmio(dev, base, &cbf_msm8996_regmap_config); 277ca574a5dSDmitry Baryshkov if (IS_ERR(regmap)) 278ca574a5dSDmitry Baryshkov return PTR_ERR(regmap); 279ca574a5dSDmitry Baryshkov 280ca574a5dSDmitry Baryshkov /* Select GPLL0 for 300MHz for the CBF clock */ 281ca574a5dSDmitry Baryshkov regmap_write(regmap, CBF_MUX_OFFSET, 0x3); 282ca574a5dSDmitry Baryshkov 283ca574a5dSDmitry Baryshkov /* Ensure write goes through before PLLs are reconfigured */ 284ca574a5dSDmitry Baryshkov udelay(5); 285ca574a5dSDmitry Baryshkov 286ca574a5dSDmitry Baryshkov /* Set the auto clock sel always-on source to GPLL0/2 (300MHz) */ 287ca574a5dSDmitry Baryshkov regmap_update_bits(regmap, CBF_MUX_OFFSET, 288ca574a5dSDmitry Baryshkov CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_MASK, 289ca574a5dSDmitry Baryshkov CBF_MUX_AUTO_CLK_SEL_ALWAYS_ON_GPLL0_SEL); 290ca574a5dSDmitry Baryshkov 291ca574a5dSDmitry Baryshkov clk_alpha_pll_configure(&cbf_pll, regmap, &cbfpll_config); 292ca574a5dSDmitry Baryshkov 293ca574a5dSDmitry Baryshkov /* Wait for PLL(s) to lock */ 294ca574a5dSDmitry Baryshkov udelay(50); 295ca574a5dSDmitry Baryshkov 296ca574a5dSDmitry Baryshkov /* Enable auto clock selection for CBF */ 297ca574a5dSDmitry Baryshkov regmap_update_bits(regmap, CBF_MUX_OFFSET, 298ca574a5dSDmitry Baryshkov CBF_MUX_AUTO_CLK_SEL_BIT, 299ca574a5dSDmitry Baryshkov CBF_MUX_AUTO_CLK_SEL_BIT); 300ca574a5dSDmitry Baryshkov 301ca574a5dSDmitry Baryshkov /* Ensure write goes through before muxes are switched */ 302ca574a5dSDmitry Baryshkov udelay(5); 303ca574a5dSDmitry Baryshkov 304ca574a5dSDmitry Baryshkov /* Switch CBF to use the primary PLL */ 305ca574a5dSDmitry Baryshkov regmap_update_bits(regmap, CBF_MUX_OFFSET, CBF_MUX_PARENT_MASK, 0x1); 306ca574a5dSDmitry Baryshkov 307bc48641aSYassine Oudjana if (of_device_is_compatible(dev->of_node, "qcom,msm8996pro-cbf")) { 308bc48641aSYassine Oudjana cbfpll_config.post_div_val = 0x3 << 8; 309bc48641aSYassine Oudjana cbf_pll_postdiv.div = 4; 310bc48641aSYassine Oudjana } 311bc48641aSYassine Oudjana 312ca574a5dSDmitry Baryshkov for (i = 0; i < ARRAY_SIZE(cbf_msm8996_hw_clks); i++) { 313ca574a5dSDmitry Baryshkov ret = devm_clk_hw_register(dev, cbf_msm8996_hw_clks[i]); 314ca574a5dSDmitry Baryshkov if (ret) 315ca574a5dSDmitry Baryshkov return ret; 316ca574a5dSDmitry Baryshkov } 317ca574a5dSDmitry Baryshkov 318ca574a5dSDmitry Baryshkov for (i = 0; i < ARRAY_SIZE(cbf_msm8996_clks); i++) { 319ca574a5dSDmitry Baryshkov ret = devm_clk_register_regmap(dev, cbf_msm8996_clks[i]); 320ca574a5dSDmitry Baryshkov if (ret) 321ca574a5dSDmitry Baryshkov return ret; 322ca574a5dSDmitry Baryshkov } 323ca574a5dSDmitry Baryshkov 324ca574a5dSDmitry Baryshkov ret = devm_clk_notifier_register(dev, cbf_mux.clkr.hw.clk, &cbf_mux.nb); 325ca574a5dSDmitry Baryshkov if (ret) 326ca574a5dSDmitry Baryshkov return ret; 327ca574a5dSDmitry Baryshkov 32812dc7195SDmitry Baryshkov ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw); 32912dc7195SDmitry Baryshkov if (ret) 33012dc7195SDmitry Baryshkov return ret; 33112dc7195SDmitry Baryshkov 33212dc7195SDmitry Baryshkov return qcom_msm8996_cbf_icc_register(pdev, &cbf_mux.clkr.hw); 33312dc7195SDmitry Baryshkov } 33412dc7195SDmitry Baryshkov 335abaf59c4SUwe Kleine-König static void qcom_msm8996_cbf_remove(struct platform_device *pdev) 33612dc7195SDmitry Baryshkov { 337abaf59c4SUwe Kleine-König qcom_msm8996_cbf_icc_remove(pdev); 338ca574a5dSDmitry Baryshkov } 339ca574a5dSDmitry Baryshkov 340ca574a5dSDmitry Baryshkov static const struct of_device_id qcom_msm8996_cbf_match_table[] = { 341ca574a5dSDmitry Baryshkov { .compatible = "qcom,msm8996-cbf" }, 342bc48641aSYassine Oudjana { .compatible = "qcom,msm8996pro-cbf" }, 343ca574a5dSDmitry Baryshkov { /* sentinel */ }, 344ca574a5dSDmitry Baryshkov }; 345ca574a5dSDmitry Baryshkov MODULE_DEVICE_TABLE(of, qcom_msm8996_cbf_match_table); 346ca574a5dSDmitry Baryshkov 347ca574a5dSDmitry Baryshkov static struct platform_driver qcom_msm8996_cbf_driver = { 348ca574a5dSDmitry Baryshkov .probe = qcom_msm8996_cbf_probe, 349*f00b45dbSUwe Kleine-König .remove = qcom_msm8996_cbf_remove, 350ca574a5dSDmitry Baryshkov .driver = { 351ca574a5dSDmitry Baryshkov .name = "qcom-msm8996-cbf", 352ca574a5dSDmitry Baryshkov .of_match_table = qcom_msm8996_cbf_match_table, 35312dc7195SDmitry Baryshkov .sync_state = qcom_msm8996_cbf_icc_sync_state, 354ca574a5dSDmitry Baryshkov }, 355ca574a5dSDmitry Baryshkov }; 356ca574a5dSDmitry Baryshkov 357ca574a5dSDmitry Baryshkov /* Register early enough to fix the clock to be used for other cores */ 358ca574a5dSDmitry Baryshkov static int __init qcom_msm8996_cbf_init(void) 359ca574a5dSDmitry Baryshkov { 360ca574a5dSDmitry Baryshkov return platform_driver_register(&qcom_msm8996_cbf_driver); 361ca574a5dSDmitry Baryshkov } 362ca574a5dSDmitry Baryshkov postcore_initcall(qcom_msm8996_cbf_init); 363ca574a5dSDmitry Baryshkov 364ca574a5dSDmitry Baryshkov static void __exit qcom_msm8996_cbf_exit(void) 365ca574a5dSDmitry Baryshkov { 366ca574a5dSDmitry Baryshkov platform_driver_unregister(&qcom_msm8996_cbf_driver); 367ca574a5dSDmitry Baryshkov } 368ca574a5dSDmitry Baryshkov module_exit(qcom_msm8996_cbf_exit); 369ca574a5dSDmitry Baryshkov 370ca574a5dSDmitry Baryshkov MODULE_DESCRIPTION("QCOM MSM8996 CPU Bus Fabric Clock Driver"); 371ca574a5dSDmitry Baryshkov MODULE_LICENSE("GPL"); 372