• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2020, The Linux Foundation. All rights reserved.
4  * Copyright (c) 2021, Linaro Ltd.
5  */
6 
7 #include <linux/err.h>
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/of_device.h>
11 #include <linux/of.h>
12 #include <linux/regmap.h>
13 
14 #include <dt-bindings/clock/qcom,dispcc-qcm2290.h>
15 
16 #include "clk-alpha-pll.h"
17 #include "clk-branch.h"
18 #include "clk-rcg.h"
19 #include "clk-regmap.h"
20 #include "clk-regmap-divider.h"
21 #include "common.h"
22 #include "gdsc.h"
23 
24 enum {
25 	P_BI_TCXO,
26 	P_BI_TCXO_AO,
27 	P_DISP_CC_PLL0_OUT_MAIN,
28 	P_DSI0_PHY_PLL_OUT_BYTECLK,
29 	P_DSI0_PHY_PLL_OUT_DSICLK,
30 	P_GPLL0_OUT_DIV,
31 	P_GPLL0_OUT_MAIN,
32 	P_SLEEP_CLK,
33 };
34 
35 static const struct pll_vco spark_vco[] = {
36 	{ 500000000, 1000000000, 2 },
37 };
38 
39 /* 768MHz configuration */
40 static const struct alpha_pll_config disp_cc_pll0_config = {
41 	.l = 0x28,
42 	.alpha = 0x0,
43 	.alpha_en_mask = BIT(24),
44 	.vco_val = 0x2 << 20,
45 	.vco_mask = GENMASK(21, 20),
46 	.main_output_mask = BIT(0),
47 	.config_ctl_val = 0x4001055B,
48 };
49 
50 static struct clk_alpha_pll disp_cc_pll0 = {
51 	.offset = 0x0,
52 	.vco_table = spark_vco,
53 	.num_vco = ARRAY_SIZE(spark_vco),
54 	.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT],
55 	.clkr = {
56 		.hw.init = &(struct clk_init_data){
57 			.name = "disp_cc_pll0",
58 			.parent_data = &(const struct clk_parent_data){
59 				.fw_name = "bi_tcxo",
60 			},
61 			.num_parents = 1,
62 			.ops = &clk_alpha_pll_ops,
63 		},
64 	},
65 };
66 
67 static const struct parent_map disp_cc_parent_map_0[] = {
68 	{ P_BI_TCXO, 0 },
69 	{ P_DSI0_PHY_PLL_OUT_BYTECLK, 1 },
70 };
71 
72 static const struct clk_parent_data disp_cc_parent_data_0[] = {
73 	{ .fw_name = "bi_tcxo" },
74 	{ .fw_name = "dsi0_phy_pll_out_byteclk" },
75 };
76 
77 static const struct parent_map disp_cc_parent_map_1[] = {
78 	{ P_BI_TCXO, 0 },
79 };
80 
81 static const struct clk_parent_data disp_cc_parent_data_1[] = {
82 	{ .fw_name = "bi_tcxo" },
83 };
84 
85 static const struct parent_map disp_cc_parent_map_2[] = {
86 	{ P_BI_TCXO_AO, 0 },
87 	{ P_GPLL0_OUT_DIV, 4 },
88 };
89 
90 static const struct clk_parent_data disp_cc_parent_data_2[] = {
91 	{ .fw_name = "bi_tcxo_ao" },
92 	{ .fw_name = "gcc_disp_gpll0_div_clk_src" },
93 };
94 
95 static const struct parent_map disp_cc_parent_map_3[] = {
96 	{ P_BI_TCXO, 0 },
97 	{ P_DISP_CC_PLL0_OUT_MAIN, 1 },
98 	{ P_GPLL0_OUT_MAIN, 4 },
99 };
100 
101 static const struct clk_parent_data disp_cc_parent_data_3[] = {
102 	{ .fw_name = "bi_tcxo" },
103 	{ .hw = &disp_cc_pll0.clkr.hw },
104 	{ .fw_name = "gcc_disp_gpll0_clk_src" },
105 };
106 
107 static const struct parent_map disp_cc_parent_map_4[] = {
108 	{ P_BI_TCXO, 0 },
109 	{ P_DSI0_PHY_PLL_OUT_DSICLK, 1 },
110 };
111 
112 static const struct clk_parent_data disp_cc_parent_data_4[] = {
113 	{ .fw_name = "bi_tcxo" },
114 	{ .fw_name = "dsi0_phy_pll_out_dsiclk" },
115 };
116 
117 static const struct parent_map disp_cc_parent_map_5[] = {
118 	{ P_SLEEP_CLK, 0 },
119 };
120 
121 static const struct clk_parent_data disp_cc_parent_data_5[] = {
122 	{ .fw_name = "sleep_clk" },
123 };
124 
125 static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = {
126 	.cmd_rcgr = 0x20a4,
127 	.mnd_width = 0,
128 	.hid_width = 5,
129 	.parent_map = disp_cc_parent_map_0,
130 	.clkr.hw.init = &(struct clk_init_data){
131 		.name = "disp_cc_mdss_byte0_clk_src",
132 		.parent_data = disp_cc_parent_data_0,
133 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_0),
134 		/* For set_rate and set_parent to succeed, parent(s) must be enabled */
135 		.flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
136 		.ops = &clk_byte2_ops,
137 	},
138 };
139 
140 static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
141 	.reg = 0x20bc,
142 	.shift = 0,
143 	.width = 2,
144 	.clkr.hw.init = &(struct clk_init_data) {
145 		.name = "disp_cc_mdss_byte0_div_clk_src",
146 		.parent_hws = (const struct clk_hw*[]){
147 			&disp_cc_mdss_byte0_clk_src.clkr.hw,
148 		},
149 		.num_parents = 1,
150 		.ops = &clk_regmap_div_ops,
151 	},
152 };
153 
154 static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = {
155 	F(19200000, P_BI_TCXO_AO, 1, 0, 0),
156 	F(37500000, P_GPLL0_OUT_DIV, 8, 0, 0),
157 	F(75000000, P_GPLL0_OUT_DIV, 4, 0, 0),
158 	{ }
159 };
160 
161 static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = {
162 	.cmd_rcgr = 0x2154,
163 	.mnd_width = 0,
164 	.hid_width = 5,
165 	.parent_map = disp_cc_parent_map_2,
166 	.freq_tbl = ftbl_disp_cc_mdss_ahb_clk_src,
167 	.clkr.hw.init = &(struct clk_init_data){
168 		.name = "disp_cc_mdss_ahb_clk_src",
169 		.parent_data = disp_cc_parent_data_2,
170 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_2),
171 		.ops = &clk_rcg2_shared_ops,
172 	},
173 };
174 
175 static const struct freq_tbl ftbl_disp_cc_mdss_esc0_clk_src[] = {
176 	F(19200000, P_BI_TCXO, 1, 0, 0),
177 	{ }
178 };
179 
180 static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = {
181 	.cmd_rcgr = 0x20c0,
182 	.mnd_width = 0,
183 	.hid_width = 5,
184 	.parent_map = disp_cc_parent_map_0,
185 	.freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src,
186 	.clkr.hw.init = &(struct clk_init_data){
187 		.name = "disp_cc_mdss_esc0_clk_src",
188 		.parent_data = disp_cc_parent_data_0,
189 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_0),
190 		.ops = &clk_rcg2_ops,
191 	},
192 };
193 
194 static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = {
195 	F(19200000, P_BI_TCXO, 1, 0, 0),
196 	F(192000000, P_DISP_CC_PLL0_OUT_MAIN, 4, 0, 0),
197 	F(256000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0),
198 	F(307200000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0),
199 	F(384000000, P_DISP_CC_PLL0_OUT_MAIN, 2, 0, 0),
200 	{ }
201 };
202 
203 static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = {
204 	.cmd_rcgr = 0x2074,
205 	.mnd_width = 0,
206 	.hid_width = 5,
207 	.parent_map = disp_cc_parent_map_3,
208 	.freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src,
209 	.clkr.hw.init = &(struct clk_init_data){
210 		.name = "disp_cc_mdss_mdp_clk_src",
211 		.parent_data = disp_cc_parent_data_3,
212 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_3),
213 		.flags = CLK_SET_RATE_PARENT,
214 		.ops = &clk_rcg2_shared_ops,
215 	},
216 };
217 
218 static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = {
219 	.cmd_rcgr = 0x205c,
220 	.mnd_width = 8,
221 	.hid_width = 5,
222 	.parent_map = disp_cc_parent_map_4,
223 	.clkr.hw.init = &(struct clk_init_data){
224 		.name = "disp_cc_mdss_pclk0_clk_src",
225 		.parent_data = disp_cc_parent_data_4,
226 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_4),
227 		/* For set_rate and set_parent to succeed, parent(s) must be enabled */
228 		.flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
229 		.ops = &clk_pixel_ops,
230 	},
231 };
232 
233 static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = {
234 	.cmd_rcgr = 0x208c,
235 	.mnd_width = 0,
236 	.hid_width = 5,
237 	.parent_map = disp_cc_parent_map_1,
238 	.freq_tbl = ftbl_disp_cc_mdss_esc0_clk_src,
239 	.clkr.hw.init = &(struct clk_init_data){
240 		.name = "disp_cc_mdss_vsync_clk_src",
241 		.parent_data = disp_cc_parent_data_1,
242 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_1),
243 		.flags = CLK_SET_RATE_PARENT,
244 		.ops = &clk_rcg2_shared_ops,
245 	},
246 };
247 
248 static const struct freq_tbl ftbl_disp_cc_sleep_clk_src[] = {
249 	F(32764, P_SLEEP_CLK, 1, 0, 0),
250 	{ }
251 };
252 
253 static struct clk_rcg2 disp_cc_sleep_clk_src = {
254 	.cmd_rcgr = 0x6050,
255 	.mnd_width = 0,
256 	.hid_width = 5,
257 	.parent_map = disp_cc_parent_map_5,
258 	.freq_tbl = ftbl_disp_cc_sleep_clk_src,
259 	.clkr.hw.init = &(struct clk_init_data){
260 		.name = "disp_cc_sleep_clk_src",
261 		.parent_data = disp_cc_parent_data_5,
262 		.num_parents = ARRAY_SIZE(disp_cc_parent_data_5),
263 		.ops = &clk_rcg2_ops,
264 	},
265 };
266 
267 static struct clk_branch disp_cc_mdss_ahb_clk = {
268 	.halt_reg = 0x2044,
269 	.halt_check = BRANCH_HALT,
270 	.clkr = {
271 		.enable_reg = 0x2044,
272 		.enable_mask = BIT(0),
273 		.hw.init = &(struct clk_init_data){
274 			.name = "disp_cc_mdss_ahb_clk",
275 			.parent_hws = (const struct clk_hw*[]){
276 				&disp_cc_mdss_ahb_clk_src.clkr.hw,
277 			},
278 			.num_parents = 1,
279 			.flags = CLK_SET_RATE_PARENT,
280 			.ops = &clk_branch2_ops,
281 		},
282 	},
283 };
284 
285 static struct clk_branch disp_cc_mdss_byte0_clk = {
286 	.halt_reg = 0x201c,
287 	.halt_check = BRANCH_HALT,
288 	.clkr = {
289 		.enable_reg = 0x201c,
290 		.enable_mask = BIT(0),
291 		.hw.init = &(struct clk_init_data){
292 			.name = "disp_cc_mdss_byte0_clk",
293 			.parent_hws = (const struct clk_hw*[]){
294 				&disp_cc_mdss_byte0_clk_src.clkr.hw,
295 			},
296 			.num_parents = 1,
297 			.flags = CLK_SET_RATE_PARENT,
298 			.ops = &clk_branch2_ops,
299 		},
300 	},
301 };
302 
303 static struct clk_branch disp_cc_mdss_byte0_intf_clk = {
304 	.halt_reg = 0x2020,
305 	.halt_check = BRANCH_HALT,
306 	.clkr = {
307 		.enable_reg = 0x2020,
308 		.enable_mask = BIT(0),
309 		.hw.init = &(struct clk_init_data){
310 			.name = "disp_cc_mdss_byte0_intf_clk",
311 			.parent_hws = (const struct clk_hw*[]){
312 				&disp_cc_mdss_byte0_div_clk_src.clkr.hw,
313 			},
314 			.num_parents = 1,
315 			.flags = CLK_SET_RATE_PARENT,
316 			.ops = &clk_branch2_ops,
317 		},
318 	},
319 };
320 
321 static struct clk_branch disp_cc_mdss_esc0_clk = {
322 	.halt_reg = 0x2024,
323 	.halt_check = BRANCH_HALT,
324 	.clkr = {
325 		.enable_reg = 0x2024,
326 		.enable_mask = BIT(0),
327 		.hw.init = &(struct clk_init_data){
328 			.name = "disp_cc_mdss_esc0_clk",
329 			.parent_hws = (const struct clk_hw*[]){
330 				&disp_cc_mdss_esc0_clk_src.clkr.hw,
331 			},
332 			.num_parents = 1,
333 			.flags = CLK_SET_RATE_PARENT,
334 			.ops = &clk_branch2_ops,
335 		},
336 	},
337 };
338 
339 static struct clk_branch disp_cc_mdss_mdp_clk = {
340 	.halt_reg = 0x2008,
341 	.halt_check = BRANCH_HALT,
342 	.clkr = {
343 		.enable_reg = 0x2008,
344 		.enable_mask = BIT(0),
345 		.hw.init = &(struct clk_init_data){
346 			.name = "disp_cc_mdss_mdp_clk",
347 			.parent_hws = (const struct clk_hw*[]){
348 				&disp_cc_mdss_mdp_clk_src.clkr.hw,
349 			},
350 			.num_parents = 1,
351 			.flags = CLK_SET_RATE_PARENT,
352 			.ops = &clk_branch2_ops,
353 		},
354 	},
355 };
356 
357 static struct clk_branch disp_cc_mdss_mdp_lut_clk = {
358 	.halt_reg = 0x2010,
359 	.halt_check = BRANCH_HALT_VOTED,
360 	.clkr = {
361 		.enable_reg = 0x2010,
362 		.enable_mask = BIT(0),
363 		.hw.init = &(struct clk_init_data){
364 			.name = "disp_cc_mdss_mdp_lut_clk",
365 			.parent_hws = (const struct clk_hw*[]){
366 				&disp_cc_mdss_mdp_clk_src.clkr.hw,
367 			},
368 			.num_parents = 1,
369 			.flags = CLK_SET_RATE_PARENT,
370 			.ops = &clk_branch2_ops,
371 		},
372 	},
373 };
374 
375 static struct clk_branch disp_cc_mdss_non_gdsc_ahb_clk = {
376 	.halt_reg = 0x4004,
377 	.halt_check = BRANCH_HALT_VOTED,
378 	.clkr = {
379 		.enable_reg = 0x4004,
380 		.enable_mask = BIT(0),
381 		.hw.init = &(struct clk_init_data){
382 			.name = "disp_cc_mdss_non_gdsc_ahb_clk",
383 			.parent_hws = (const struct clk_hw*[]){
384 				&disp_cc_mdss_ahb_clk_src.clkr.hw,
385 			},
386 			.num_parents = 1,
387 			.flags = CLK_SET_RATE_PARENT,
388 			.ops = &clk_branch2_ops,
389 		},
390 	},
391 };
392 
393 static struct clk_branch disp_cc_mdss_pclk0_clk = {
394 	.halt_reg = 0x2004,
395 	.halt_check = BRANCH_HALT,
396 	.clkr = {
397 		.enable_reg = 0x2004,
398 		.enable_mask = BIT(0),
399 		.hw.init = &(struct clk_init_data){
400 			.name = "disp_cc_mdss_pclk0_clk",
401 			.parent_hws = (const struct clk_hw*[]){
402 				&disp_cc_mdss_pclk0_clk_src.clkr.hw,
403 			},
404 			.num_parents = 1,
405 			.flags = CLK_SET_RATE_PARENT,
406 			.ops = &clk_branch2_ops,
407 		},
408 	},
409 };
410 
411 static struct clk_branch disp_cc_mdss_vsync_clk = {
412 	.halt_reg = 0x2018,
413 	.halt_check = BRANCH_HALT,
414 	.clkr = {
415 		.enable_reg = 0x2018,
416 		.enable_mask = BIT(0),
417 		.hw.init = &(struct clk_init_data){
418 			.name = "disp_cc_mdss_vsync_clk",
419 			.parent_hws = (const struct clk_hw*[]){
420 				&disp_cc_mdss_vsync_clk_src.clkr.hw,
421 			},
422 			.num_parents = 1,
423 			.flags = CLK_SET_RATE_PARENT,
424 			.ops = &clk_branch2_ops,
425 		},
426 	},
427 };
428 
429 static struct clk_branch disp_cc_sleep_clk = {
430 	.halt_reg = 0x6068,
431 	.halt_check = BRANCH_HALT,
432 	.clkr = {
433 		.enable_reg = 0x6068,
434 		.enable_mask = BIT(0),
435 		.hw.init = &(struct clk_init_data){
436 			.name = "disp_cc_sleep_clk",
437 			.parent_hws = (const struct clk_hw*[]){
438 				&disp_cc_sleep_clk_src.clkr.hw,
439 			},
440 			.num_parents = 1,
441 			.flags = CLK_SET_RATE_PARENT,
442 			.ops = &clk_branch2_ops,
443 		},
444 	},
445 };
446 
447 static struct gdsc mdss_gdsc = {
448 	.gdscr = 0x3000,
449 	.pd = {
450 		.name = "mdss_gdsc",
451 	},
452 	.pwrsts = PWRSTS_OFF_ON,
453 	.flags = HW_CTRL,
454 };
455 
456 static struct gdsc *disp_cc_qcm2290_gdscs[] = {
457 	[MDSS_GDSC] = &mdss_gdsc,
458 };
459 
460 static struct clk_regmap *disp_cc_qcm2290_clocks[] = {
461 	[DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr,
462 	[DISP_CC_MDSS_AHB_CLK_SRC] = &disp_cc_mdss_ahb_clk_src.clkr,
463 	[DISP_CC_MDSS_BYTE0_CLK] = &disp_cc_mdss_byte0_clk.clkr,
464 	[DISP_CC_MDSS_BYTE0_CLK_SRC] = &disp_cc_mdss_byte0_clk_src.clkr,
465 	[DISP_CC_MDSS_BYTE0_DIV_CLK_SRC] = &disp_cc_mdss_byte0_div_clk_src.clkr,
466 	[DISP_CC_MDSS_BYTE0_INTF_CLK] = &disp_cc_mdss_byte0_intf_clk.clkr,
467 	[DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr,
468 	[DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr,
469 	[DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr,
470 	[DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr,
471 	[DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr,
472 	[DISP_CC_MDSS_NON_GDSC_AHB_CLK] = &disp_cc_mdss_non_gdsc_ahb_clk.clkr,
473 	[DISP_CC_MDSS_PCLK0_CLK] = &disp_cc_mdss_pclk0_clk.clkr,
474 	[DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr,
475 	[DISP_CC_MDSS_VSYNC_CLK] = &disp_cc_mdss_vsync_clk.clkr,
476 	[DISP_CC_MDSS_VSYNC_CLK_SRC] = &disp_cc_mdss_vsync_clk_src.clkr,
477 	[DISP_CC_PLL0] = &disp_cc_pll0.clkr,
478 	[DISP_CC_SLEEP_CLK] = &disp_cc_sleep_clk.clkr,
479 	[DISP_CC_SLEEP_CLK_SRC] = &disp_cc_sleep_clk_src.clkr,
480 };
481 
482 static const struct regmap_config disp_cc_qcm2290_regmap_config = {
483 	.reg_bits = 32,
484 	.reg_stride = 4,
485 	.val_bits = 32,
486 	.max_register = 0x10000,
487 	.fast_io = true,
488 };
489 
490 static const struct qcom_cc_desc disp_cc_qcm2290_desc = {
491 	.config = &disp_cc_qcm2290_regmap_config,
492 	.clks = disp_cc_qcm2290_clocks,
493 	.num_clks = ARRAY_SIZE(disp_cc_qcm2290_clocks),
494 	.gdscs = disp_cc_qcm2290_gdscs,
495 	.num_gdscs = ARRAY_SIZE(disp_cc_qcm2290_gdscs),
496 };
497 
498 static const struct of_device_id disp_cc_qcm2290_match_table[] = {
499 	{ .compatible = "qcom,qcm2290-dispcc" },
500 	{ }
501 };
502 MODULE_DEVICE_TABLE(of, disp_cc_qcm2290_match_table);
503 
disp_cc_qcm2290_probe(struct platform_device * pdev)504 static int disp_cc_qcm2290_probe(struct platform_device *pdev)
505 {
506 	struct regmap *regmap;
507 	int ret;
508 
509 	regmap = qcom_cc_map(pdev, &disp_cc_qcm2290_desc);
510 	if (IS_ERR(regmap))
511 		return PTR_ERR(regmap);
512 
513 	clk_alpha_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config);
514 
515 	/* Keep DISP_CC_XO_CLK always-ON */
516 	regmap_update_bits(regmap, 0x604c, BIT(0), BIT(0));
517 
518 	ret = qcom_cc_really_probe(pdev, &disp_cc_qcm2290_desc, regmap);
519 	if (ret) {
520 		dev_err(&pdev->dev, "Failed to register DISP CC clocks\n");
521 		return ret;
522 	}
523 
524 	return ret;
525 }
526 
527 static struct platform_driver disp_cc_qcm2290_driver = {
528 	.probe = disp_cc_qcm2290_probe,
529 	.driver = {
530 		.name = "dispcc-qcm2290",
531 		.of_match_table = disp_cc_qcm2290_match_table,
532 	},
533 };
534 
disp_cc_qcm2290_init(void)535 static int __init disp_cc_qcm2290_init(void)
536 {
537 	return platform_driver_register(&disp_cc_qcm2290_driver);
538 }
539 subsys_initcall(disp_cc_qcm2290_init);
540 
disp_cc_qcm2290_exit(void)541 static void __exit disp_cc_qcm2290_exit(void)
542 {
543 	platform_driver_unregister(&disp_cc_qcm2290_driver);
544 }
545 module_exit(disp_cc_qcm2290_exit);
546 
547 MODULE_DESCRIPTION("QTI DISP_CC qcm2290 Driver");
548 MODULE_LICENSE("GPL v2");
549