165 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.4 */5 6#include <linux/kernel.h>7#include <linux/err.h>8#include <linux/platform_device.h>9#include <linux/clk-provider.h>10#include <linux/regmap.h>11#include <linux/module.h>12#include <linux/clk.h>13#include <linux/soc/qcom/smem.h>14 15#include <dt-bindings/clock/qcom,apss-ipq.h>16#include <dt-bindings/arm/qcom,ids.h>17 18#include "common.h"19#include "clk-regmap.h"20#include "clk-branch.h"21#include "clk-alpha-pll.h"22#include "clk-rcg.h"23 24enum {25 P_XO,26 P_GPLL0,27 P_APSS_PLL_EARLY,28};29 30static const struct clk_parent_data parents_apcs_alias0_clk_src[] = {31 { .fw_name = "xo" },32 { .fw_name = "gpll0" },33 { .fw_name = "pll" },34};35 36static const struct parent_map parents_apcs_alias0_clk_src_map[] = {37 { P_XO, 0 },38 { P_GPLL0, 4 },39 { P_APSS_PLL_EARLY, 5 },40};41 42static struct clk_rcg2 apcs_alias0_clk_src = {43 .cmd_rcgr = 0x0050,44 .hid_width = 5,45 .parent_map = parents_apcs_alias0_clk_src_map,46 .clkr.hw.init = &(struct clk_init_data){47 .name = "apcs_alias0_clk_src",48 .parent_data = parents_apcs_alias0_clk_src,49 .num_parents = ARRAY_SIZE(parents_apcs_alias0_clk_src),50 .ops = &clk_rcg2_mux_closest_ops,51 .flags = CLK_SET_RATE_PARENT,52 },53};54 55static struct clk_branch apcs_alias0_core_clk = {56 .halt_reg = 0x0058,57 .clkr = {58 .enable_reg = 0x0058,59 .enable_mask = BIT(0),60 .hw.init = &(struct clk_init_data){61 .name = "apcs_alias0_core_clk",62 .parent_hws = (const struct clk_hw *[]){63 &apcs_alias0_clk_src.clkr.hw },64 .num_parents = 1,65 .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,66 .ops = &clk_branch2_ops,67 },68 },69};70 71static const struct regmap_config apss_ipq6018_regmap_config = {72 .reg_bits = 32,73 .reg_stride = 4,74 .val_bits = 32,75 .max_register = 0x1000,76 .fast_io = true,77};78 79static struct clk_regmap *apss_ipq6018_clks[] = {80 [APCS_ALIAS0_CLK_SRC] = &apcs_alias0_clk_src.clkr,81 [APCS_ALIAS0_CORE_CLK] = &apcs_alias0_core_clk.clkr,82};83 84static const struct qcom_cc_desc apss_ipq6018_desc = {85 .config = &apss_ipq6018_regmap_config,86 .clks = apss_ipq6018_clks,87 .num_clks = ARRAY_SIZE(apss_ipq6018_clks),88};89 90static int cpu_clk_notifier_fn(struct notifier_block *nb, unsigned long action,91 void *data)92{93 struct clk_hw *hw;94 u8 index;95 int err;96 97 if (action == PRE_RATE_CHANGE)98 index = P_GPLL0;99 else if (action == POST_RATE_CHANGE || action == ABORT_RATE_CHANGE)100 index = P_APSS_PLL_EARLY;101 else102 return NOTIFY_OK;103 104 hw = &apcs_alias0_clk_src.clkr.hw;105 err = clk_rcg2_mux_closest_ops.set_parent(hw, index);106 107 return notifier_from_errno(err);108}109 110static int apss_ipq6018_probe(struct platform_device *pdev)111{112 struct clk_hw *hw = &apcs_alias0_clk_src.clkr.hw;113 struct notifier_block *cpu_clk_notifier;114 struct regmap *regmap;115 u32 soc_id;116 int ret;117 118 ret = qcom_smem_get_soc_id(&soc_id);119 if (ret)120 return ret;121 122 regmap = dev_get_regmap(pdev->dev.parent, NULL);123 if (!regmap)124 return -ENODEV;125 126 ret = qcom_cc_really_probe(&pdev->dev, &apss_ipq6018_desc, regmap);127 if (ret)128 return ret;129 130 switch (soc_id) {131 /* Only below variants of IPQ53xx support scaling */132 case QCOM_ID_IPQ5332:133 case QCOM_ID_IPQ5322:134 case QCOM_ID_IPQ5300:135 cpu_clk_notifier = devm_kzalloc(&pdev->dev,136 sizeof(*cpu_clk_notifier),137 GFP_KERNEL);138 if (!cpu_clk_notifier)139 return -ENOMEM;140 141 cpu_clk_notifier->notifier_call = cpu_clk_notifier_fn;142 143 ret = devm_clk_notifier_register(&pdev->dev, hw->clk, cpu_clk_notifier);144 if (ret)145 return ret;146 break;147 default:148 break;149 }150 151 return 0;152}153 154static struct platform_driver apss_ipq6018_driver = {155 .probe = apss_ipq6018_probe,156 .driver = {157 .name = "qcom,apss-ipq6018-clk",158 },159};160 161module_platform_driver(apss_ipq6018_driver);162 163MODULE_DESCRIPTION("QCOM APSS IPQ 6018 CLK Driver");164MODULE_LICENSE("GPL v2");165