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