xref: /linux/drivers/clk/qcom/apss-ipq6018.c (revision a3a02a52bcfcbcc4a637d4b68bf1bc391c9fad02)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
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 
24 enum {
25 	P_XO,
26 	P_GPLL0,
27 	P_APSS_PLL_EARLY,
28 };
29 
30 static const struct clk_parent_data parents_apcs_alias0_clk_src[] = {
31 	{ .fw_name = "xo" },
32 	{ .fw_name = "gpll0" },
33 	{ .fw_name = "pll" },
34 };
35 
36 static 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 
42 static 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 
55 static 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 
71 static 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 
79 static 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 
84 static 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 
90 static 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 	else
102 		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 
110 static 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 
154 static struct platform_driver apss_ipq6018_driver = {
155 	.probe = apss_ipq6018_probe,
156 	.driver = {
157 		.name   = "qcom,apss-ipq6018-clk",
158 	},
159 };
160 
161 module_platform_driver(apss_ipq6018_driver);
162 
163 MODULE_DESCRIPTION("QCOM APSS IPQ 6018 CLK Driver");
164 MODULE_LICENSE("GPL v2");
165