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