1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2019, Jeffrey Hugo |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/bitops.h> |
8 | #include <linux/err.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/clk-provider.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/reset-controller.h> |
15 | |
16 | #include <dt-bindings/clock/qcom,gpucc-msm8998.h> |
17 | |
18 | #include "common.h" |
19 | #include "clk-regmap.h" |
20 | #include "clk-regmap-divider.h" |
21 | #include "clk-alpha-pll.h" |
22 | #include "clk-rcg.h" |
23 | #include "clk-branch.h" |
24 | #include "reset.h" |
25 | #include "gdsc.h" |
26 | |
27 | enum { |
28 | P_XO, |
29 | P_GPLL0, |
30 | P_GPUPLL0_OUT_EVEN, |
31 | }; |
32 | |
33 | /* Instead of going directly to the block, XO is routed through this branch */ |
34 | static struct clk_branch gpucc_cxo_clk = { |
35 | .halt_reg = 0x1020, |
36 | .clkr = { |
37 | .enable_reg = 0x1020, |
38 | .enable_mask = BIT(0), |
39 | .hw.init = &(struct clk_init_data){ |
40 | .name = "gpucc_cxo_clk" , |
41 | .parent_data = &(const struct clk_parent_data){ |
42 | .fw_name = "xo" |
43 | }, |
44 | .num_parents = 1, |
45 | .ops = &clk_branch2_ops, |
46 | .flags = CLK_IS_CRITICAL, |
47 | }, |
48 | }, |
49 | }; |
50 | |
51 | static struct pll_vco fabia_vco[] = { |
52 | { 249600000, 2000000000, 0 }, |
53 | { 125000000, 1000000000, 1 }, |
54 | }; |
55 | |
56 | static const struct clk_div_table post_div_table_fabia_even[] = { |
57 | { 0x0, 1 }, |
58 | { 0x1, 2 }, |
59 | { 0x3, 4 }, |
60 | { 0x7, 8 }, |
61 | { } |
62 | }; |
63 | |
64 | static struct clk_alpha_pll gpupll0 = { |
65 | .offset = 0x0, |
66 | .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], |
67 | .vco_table = fabia_vco, |
68 | .num_vco = ARRAY_SIZE(fabia_vco), |
69 | .clkr.hw.init = &(struct clk_init_data){ |
70 | .name = "gpupll0" , |
71 | .parent_hws = (const struct clk_hw *[]){ &gpucc_cxo_clk.clkr.hw }, |
72 | .num_parents = 1, |
73 | .ops = &clk_alpha_pll_fabia_ops, |
74 | }, |
75 | }; |
76 | |
77 | static struct clk_alpha_pll_postdiv gpupll0_out_even = { |
78 | .offset = 0x0, |
79 | .post_div_shift = 8, |
80 | .post_div_table = post_div_table_fabia_even, |
81 | .num_post_div = ARRAY_SIZE(post_div_table_fabia_even), |
82 | .width = 4, |
83 | .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], |
84 | .clkr.hw.init = &(struct clk_init_data){ |
85 | .name = "gpupll0_out_even" , |
86 | .parent_hws = (const struct clk_hw *[]){ &gpupll0.clkr.hw }, |
87 | .num_parents = 1, |
88 | .flags = CLK_SET_RATE_PARENT, |
89 | .ops = &clk_alpha_pll_postdiv_fabia_ops, |
90 | }, |
91 | }; |
92 | |
93 | static const struct parent_map gpu_xo_gpll0_map[] = { |
94 | { P_XO, 0 }, |
95 | { P_GPLL0, 5 }, |
96 | }; |
97 | |
98 | static const struct clk_parent_data gpu_xo_gpll0[] = { |
99 | { .hw = &gpucc_cxo_clk.clkr.hw }, |
100 | { .fw_name = "gpll0" , .name = "gcc_gpu_gpll0_clk" }, |
101 | }; |
102 | |
103 | static const struct parent_map gpu_xo_gpupll0_map[] = { |
104 | { P_XO, 0 }, |
105 | { P_GPUPLL0_OUT_EVEN, 1 }, |
106 | }; |
107 | |
108 | static const struct clk_hw *gpu_xo_gpupll0[] = { |
109 | &gpucc_cxo_clk.clkr.hw, |
110 | &gpupll0_out_even.clkr.hw, |
111 | }; |
112 | |
113 | static const struct freq_tbl ftbl_rbcpr_clk_src[] = { |
114 | F(19200000, P_XO, 1, 0, 0), |
115 | F(50000000, P_GPLL0, 12, 0, 0), |
116 | { } |
117 | }; |
118 | |
119 | static struct clk_rcg2 rbcpr_clk_src = { |
120 | .cmd_rcgr = 0x1030, |
121 | .hid_width = 5, |
122 | .parent_map = gpu_xo_gpll0_map, |
123 | .freq_tbl = ftbl_rbcpr_clk_src, |
124 | .clkr.hw.init = &(struct clk_init_data){ |
125 | .name = "rbcpr_clk_src" , |
126 | .parent_data = gpu_xo_gpll0, |
127 | .num_parents = ARRAY_SIZE(gpu_xo_gpll0), |
128 | .ops = &clk_rcg2_ops, |
129 | }, |
130 | }; |
131 | |
132 | static const struct freq_tbl ftbl_gfx3d_clk_src[] = { |
133 | { .src = P_GPUPLL0_OUT_EVEN, .pre_div = 3 }, |
134 | { } |
135 | }; |
136 | |
137 | static struct clk_rcg2 gfx3d_clk_src = { |
138 | .cmd_rcgr = 0x1070, |
139 | .hid_width = 5, |
140 | .parent_map = gpu_xo_gpupll0_map, |
141 | .freq_tbl = ftbl_gfx3d_clk_src, |
142 | .clkr.hw.init = &(struct clk_init_data){ |
143 | .name = "gfx3d_clk_src" , |
144 | .parent_hws = gpu_xo_gpupll0, |
145 | .num_parents = ARRAY_SIZE(gpu_xo_gpupll0), |
146 | .ops = &clk_rcg2_ops, |
147 | .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, |
148 | }, |
149 | }; |
150 | |
151 | static const struct freq_tbl ftbl_rbbmtimer_clk_src[] = { |
152 | F(19200000, P_XO, 1, 0, 0), |
153 | { } |
154 | }; |
155 | |
156 | static struct clk_rcg2 rbbmtimer_clk_src = { |
157 | .cmd_rcgr = 0x10b0, |
158 | .hid_width = 5, |
159 | .parent_map = gpu_xo_gpll0_map, |
160 | .freq_tbl = ftbl_rbbmtimer_clk_src, |
161 | .clkr.hw.init = &(struct clk_init_data){ |
162 | .name = "rbbmtimer_clk_src" , |
163 | .parent_data = gpu_xo_gpll0, |
164 | .num_parents = ARRAY_SIZE(gpu_xo_gpll0), |
165 | .ops = &clk_rcg2_ops, |
166 | }, |
167 | }; |
168 | |
169 | static const struct freq_tbl ftbl_gfx3d_isense_clk_src[] = { |
170 | F(19200000, P_XO, 1, 0, 0), |
171 | F(40000000, P_GPLL0, 15, 0, 0), |
172 | F(200000000, P_GPLL0, 3, 0, 0), |
173 | F(300000000, P_GPLL0, 2, 0, 0), |
174 | { } |
175 | }; |
176 | |
177 | static struct clk_rcg2 gfx3d_isense_clk_src = { |
178 | .cmd_rcgr = 0x1100, |
179 | .hid_width = 5, |
180 | .parent_map = gpu_xo_gpll0_map, |
181 | .freq_tbl = ftbl_gfx3d_isense_clk_src, |
182 | .clkr.hw.init = &(struct clk_init_data){ |
183 | .name = "gfx3d_isense_clk_src" , |
184 | .parent_data = gpu_xo_gpll0, |
185 | .num_parents = ARRAY_SIZE(gpu_xo_gpll0), |
186 | .ops = &clk_rcg2_ops, |
187 | }, |
188 | }; |
189 | |
190 | static struct clk_branch rbcpr_clk = { |
191 | .halt_reg = 0x1054, |
192 | .clkr = { |
193 | .enable_reg = 0x1054, |
194 | .enable_mask = BIT(0), |
195 | .hw.init = &(struct clk_init_data){ |
196 | .name = "rbcpr_clk" , |
197 | .parent_hws = (const struct clk_hw *[]){ &rbcpr_clk_src.clkr.hw }, |
198 | .num_parents = 1, |
199 | .ops = &clk_branch2_ops, |
200 | .flags = CLK_SET_RATE_PARENT, |
201 | }, |
202 | }, |
203 | }; |
204 | |
205 | static struct clk_branch gfx3d_clk = { |
206 | .halt_reg = 0x1098, |
207 | .clkr = { |
208 | .enable_reg = 0x1098, |
209 | .enable_mask = BIT(0), |
210 | .hw.init = &(struct clk_init_data){ |
211 | .name = "gfx3d_clk" , |
212 | .parent_hws = (const struct clk_hw *[]){ &gfx3d_clk_src.clkr.hw }, |
213 | .num_parents = 1, |
214 | .ops = &clk_branch2_ops, |
215 | .flags = CLK_SET_RATE_PARENT, |
216 | }, |
217 | }, |
218 | }; |
219 | |
220 | static struct clk_branch rbbmtimer_clk = { |
221 | .halt_reg = 0x10d0, |
222 | .clkr = { |
223 | .enable_reg = 0x10d0, |
224 | .enable_mask = BIT(0), |
225 | .hw.init = &(struct clk_init_data){ |
226 | .name = "rbbmtimer_clk" , |
227 | .parent_hws = (const struct clk_hw *[]){ &rbbmtimer_clk_src.clkr.hw }, |
228 | .num_parents = 1, |
229 | .ops = &clk_branch2_ops, |
230 | .flags = CLK_SET_RATE_PARENT, |
231 | }, |
232 | }, |
233 | }; |
234 | |
235 | static struct clk_branch gfx3d_isense_clk = { |
236 | .halt_reg = 0x1124, |
237 | .clkr = { |
238 | .enable_reg = 0x1124, |
239 | .enable_mask = BIT(0), |
240 | .hw.init = &(struct clk_init_data){ |
241 | .name = "gfx3d_isense_clk" , |
242 | .parent_hws = (const struct clk_hw *[]){ &gfx3d_isense_clk_src.clkr.hw }, |
243 | .num_parents = 1, |
244 | .ops = &clk_branch2_ops, |
245 | }, |
246 | }, |
247 | }; |
248 | |
249 | static struct gdsc gpu_cx_gdsc = { |
250 | .gdscr = 0x1004, |
251 | .gds_hw_ctrl = 0x1008, |
252 | .pd = { |
253 | .name = "gpu_cx" , |
254 | }, |
255 | .pwrsts = PWRSTS_OFF_ON, |
256 | .flags = VOTABLE, |
257 | }; |
258 | |
259 | static struct gdsc gpu_gx_gdsc = { |
260 | .gdscr = 0x1094, |
261 | .clamp_io_ctrl = 0x130, |
262 | .resets = (unsigned int []){ GPU_GX_BCR }, |
263 | .reset_count = 1, |
264 | .cxcs = (unsigned int []){ 0x1098 }, |
265 | .cxc_count = 1, |
266 | .pd = { |
267 | .name = "gpu_gx" , |
268 | }, |
269 | .parent = &gpu_cx_gdsc.pd, |
270 | .pwrsts = PWRSTS_OFF_ON | PWRSTS_RET, |
271 | .flags = CLAMP_IO | SW_RESET | AON_RESET | NO_RET_PERIPH, |
272 | }; |
273 | |
274 | static struct clk_regmap *gpucc_msm8998_clocks[] = { |
275 | [GPUPLL0] = &gpupll0.clkr, |
276 | [GPUPLL0_OUT_EVEN] = &gpupll0_out_even.clkr, |
277 | [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, |
278 | [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr, |
279 | [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr, |
280 | [GFX3D_ISENSE_CLK_SRC] = &gfx3d_isense_clk_src.clkr, |
281 | [RBCPR_CLK] = &rbcpr_clk.clkr, |
282 | [GFX3D_CLK] = &gfx3d_clk.clkr, |
283 | [RBBMTIMER_CLK] = &rbbmtimer_clk.clkr, |
284 | [GFX3D_ISENSE_CLK] = &gfx3d_isense_clk.clkr, |
285 | [GPUCC_CXO_CLK] = &gpucc_cxo_clk.clkr, |
286 | }; |
287 | |
288 | static struct gdsc *gpucc_msm8998_gdscs[] = { |
289 | [GPU_CX_GDSC] = &gpu_cx_gdsc, |
290 | [GPU_GX_GDSC] = &gpu_gx_gdsc, |
291 | }; |
292 | |
293 | static const struct qcom_reset_map gpucc_msm8998_resets[] = { |
294 | [GPU_CX_BCR] = { 0x1000 }, |
295 | [RBCPR_BCR] = { 0x1050 }, |
296 | [GPU_GX_BCR] = { 0x1090 }, |
297 | [GPU_ISENSE_BCR] = { 0x1120 }, |
298 | }; |
299 | |
300 | static const struct regmap_config gpucc_msm8998_regmap_config = { |
301 | .reg_bits = 32, |
302 | .reg_stride = 4, |
303 | .val_bits = 32, |
304 | .max_register = 0x9000, |
305 | .fast_io = true, |
306 | }; |
307 | |
308 | static const struct qcom_cc_desc gpucc_msm8998_desc = { |
309 | .config = &gpucc_msm8998_regmap_config, |
310 | .clks = gpucc_msm8998_clocks, |
311 | .num_clks = ARRAY_SIZE(gpucc_msm8998_clocks), |
312 | .resets = gpucc_msm8998_resets, |
313 | .num_resets = ARRAY_SIZE(gpucc_msm8998_resets), |
314 | .gdscs = gpucc_msm8998_gdscs, |
315 | .num_gdscs = ARRAY_SIZE(gpucc_msm8998_gdscs), |
316 | }; |
317 | |
318 | static const struct of_device_id gpucc_msm8998_match_table[] = { |
319 | { .compatible = "qcom,msm8998-gpucc" }, |
320 | { } |
321 | }; |
322 | MODULE_DEVICE_TABLE(of, gpucc_msm8998_match_table); |
323 | |
324 | static int gpucc_msm8998_probe(struct platform_device *pdev) |
325 | { |
326 | struct regmap *regmap; |
327 | |
328 | regmap = qcom_cc_map(pdev, desc: &gpucc_msm8998_desc); |
329 | if (IS_ERR(ptr: regmap)) |
330 | return PTR_ERR(ptr: regmap); |
331 | |
332 | /* force periph logic on to avoid perf counter corruption */ |
333 | regmap_write_bits(map: regmap, reg: gfx3d_clk.clkr.enable_reg, BIT(13), BIT(13)); |
334 | /* tweak droop detector (GPUCC_GPU_DD_WRAP_CTRL) to reduce leakage */ |
335 | regmap_write_bits(map: regmap, reg: gfx3d_clk.clkr.enable_reg, BIT(0), BIT(0)); |
336 | |
337 | return qcom_cc_really_probe(pdev, desc: &gpucc_msm8998_desc, regmap); |
338 | } |
339 | |
340 | static struct platform_driver gpucc_msm8998_driver = { |
341 | .probe = gpucc_msm8998_probe, |
342 | .driver = { |
343 | .name = "gpucc-msm8998" , |
344 | .of_match_table = gpucc_msm8998_match_table, |
345 | }, |
346 | }; |
347 | module_platform_driver(gpucc_msm8998_driver); |
348 | |
349 | MODULE_DESCRIPTION("QCOM GPUCC MSM8998 Driver" ); |
350 | MODULE_LICENSE("GPL v2" ); |
351 | |