1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2020, Rockchip Electronics Co., Ltd.
4 */
5 #include <linux/module.h>
6 #include <linux/nospec.h>
7 #include <linux/of.h>
8 #include <linux/platform_device.h>
9 #include <linux/proc_fs.h>
10 #include <linux/rockchip/rockchip_sip.h>
11 #include <linux/seq_file.h>
12 #include <linux/string.h>
13 #include <linux/uaccess.h>
14 #include <linux/vmalloc.h>
15
16 #include <soc/rockchip/rockchip_sip.h>
17
18 #include "rockchip_dmc_timing.h"
19
20 /*
21 * DMCDBG share memory request 4KB for delivery parameter
22 */
23 #define DMCDBG_PAGE_NUMS (1)
24 #define DMCDBG_SHARE_MEM_SIZE ((DMCDBG_PAGE_NUMS)*4096)
25
26 #define PROC_DMCDBG_DIR_NAME "dmcdbg"
27 #define PROC_DMCDBG_DRAM_INFO "dmcinfo"
28 #define PROC_DMCDBG_POWERSAVE "powersave"
29 #define PROC_DMCDBG_DRVODT "drvodt"
30 #define PROC_DMCDBG_DESKEW "deskew"
31 #define PROC_DMCDBG_REGS_INFO "regsinfo"
32
33 #define DDRDBG_FUNCGET_VERSION (0x01)
34 #define DDRDBG_FUNC_GET_SUPPORTED (0x02)
35 #define DDRDBG_FUNC_GET_DRAM_INFO (0x03)
36 #define DDRDBG_FUNC_GET_DESKEW_INFO (0x04)
37 #define DDRDBG_FUNC_UPDATE_DESKEW (0x05)
38 #define DDRDBG_FUNC_DATA_TRAINING (0x06)
39 #define DDRDBG_FUNC_UPDATE_DESKEW_TR (0x07)
40 #define DDRDBG_FUNC_GET_POWERSAVE_INFO (0x08)
41 #define DDRDBG_FUNC_UPDATE_POWERSAVE (0x09)
42 #define DDRDBG_FUNC_GET_DRVODT_INFO (0x0a)
43 #define DDRDBG_FUNC_UPDATE_DRVODT (0x0b)
44 #define DDRDBG_FUNC_GET_REGISTERS_INFO (0x0c)
45
46 #define DRV_ODT_UNKNOWN (0xffff)
47 #define DRV_ODT_UNSUSPEND_FIX (0x0)
48 #define DRV_ODT_SUSPEND_FIX (0x1)
49
50 #define REGS_NAME_LEN_MAX (20)
51 #define SKEW_GROUP_NUM_MAX (6)
52 #define SKEW_TIMING_NUM_MAX (50)
53
54 struct rockchip_dmcdbg {
55 struct device *dev;
56 };
57
58 struct proc_dir_entry *proc_dmcdbg_dir;
59
60 struct dram_cap_info {
61 unsigned int rank;
62 unsigned int col;
63 unsigned int bank;
64 unsigned int buswidth;
65 unsigned int die_buswidth;
66 unsigned int row_3_4;
67 unsigned int cs0_row;
68 unsigned int cs1_row;
69 unsigned int cs0_high16bit_row;
70 unsigned int cs1_high16bit_row;
71 unsigned int bankgroup;
72 unsigned int size;
73 };
74
75 struct dram_info {
76 unsigned int version;
77 char dramtype[10];
78 unsigned int dramfreq;
79 unsigned int channel_num;
80 struct dram_cap_info ch[2];
81 };
82
83 static const char *const power_save_msg[] = {
84 "auto power down enable",
85 "auto power down idle cycle",
86 "auto self refresh enable",
87 "auto self refresh idle cycle",
88 "self refresh with clock gate idle cycle",
89 "self refresh and power down lite idle cycle",
90 "standby idle cycle",
91 };
92
93 struct power_save_info {
94 unsigned int pd_en;
95 unsigned int pd_idle;
96 unsigned int sr_en;
97 unsigned int sr_idle;
98 unsigned int sr_mc_gate_idle;
99 unsigned int srpd_lite_idle;
100 unsigned int standby_idle;
101 };
102
103 static const char *const drv_odt_msg[] = {
104 "dram side drv pull-up", "dram side drv pull-down",
105 "dram side dq odt pull-up", "dram side dq odt pull-down",
106 "dram side ca odt pull-up", "dram side ca odt pull-down",
107 "soc side ca drv pull-up", "soc side ca drv pull-down",
108 "soc side ck drv pull-up", "soc side ck drv pull-down",
109 "soc side cs drv pull-up", "soc side cs drv pull-down",
110 "soc side dq drv pull-up", "soc side dq drv pull-down",
111 "soc side odt pull-up", "soc side odt pull-down",
112 "phy vref inner", "phy vref out",
113 };
114
115 struct drv_odt {
116 unsigned int value;
117 unsigned int ohm;
118 unsigned int flag;
119 };
120
121 struct drv_odt_vref {
122 unsigned int value;
123 unsigned int percen;
124 unsigned int flag;
125 };
126
127 struct drv_odt_info {
128 struct drv_odt dram_drv_up;
129 struct drv_odt dram_drv_down;
130 struct drv_odt dram_dq_odt_up;
131 struct drv_odt dram_dq_odt_down;
132 struct drv_odt dram_ca_odt_up;
133 struct drv_odt dram_ca_odt_down;
134 struct drv_odt phy_ca_drv_up;
135 struct drv_odt phy_ca_drv_down;
136 struct drv_odt phy_ck_drv_up;
137 struct drv_odt phy_ck_drv_down;
138 struct drv_odt phy_cs_drv_up;
139 struct drv_odt phy_cs_drv_down;
140 struct drv_odt phy_dq_drv_up;
141 struct drv_odt phy_dq_drv_down;
142 struct drv_odt phy_odt_up;
143 struct drv_odt phy_odt_down;
144 struct drv_odt_vref phy_vref_inner;
145 struct drv_odt_vref phy_vref_out;
146 };
147
148 struct dmc_registers {
149 char regs_name[REGS_NAME_LEN_MAX];
150 unsigned int regs_addr;
151 };
152
153 struct registers_info {
154 unsigned int regs_num;
155 struct dmc_registers regs[];
156 };
157
158 struct skew_group {
159 unsigned int skew_num;
160 unsigned int *p_skew_info;
161 char *p_skew_timing[SKEW_TIMING_NUM_MAX];
162 char *note;
163 };
164
165 struct rockchip_dmcdbg_data {
166 unsigned int inited_flag;
167 void __iomem *share_memory;
168 unsigned int skew_group_num;
169 struct skew_group skew_group[SKEW_GROUP_NUM_MAX];
170 };
171
172 static struct rockchip_dmcdbg_data dmcdbg_data;
173
174 struct skew_info_rv1126 {
175 unsigned int ca_skew[32];
176 unsigned int cs0_a_skew[44];
177 unsigned int cs0_b_skew[44];
178 unsigned int cs1_a_skew[44];
179 unsigned int cs1_b_skew[44];
180 };
181
dmcinfo_proc_show(struct seq_file *m, void *v)182 static int dmcinfo_proc_show(struct seq_file *m, void *v)
183 {
184 struct arm_smccc_res res;
185 struct dram_info *p_dram_info;
186 struct file *fp = NULL;
187 char cur_freq[20] = {0};
188 char governor[20] = {0};
189 loff_t pos;
190 u32 i;
191
192 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_DRAM_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
193 if (res.a0) {
194 seq_printf(m, "rockchip_sip_config_dram_debug error:%lx\n", res.a0);
195 return -ENOMEM;
196 }
197
198 if (!dmcdbg_data.inited_flag) {
199 seq_puts(m, "dmcdbg_data no int\n");
200 return -EPERM;
201 }
202 p_dram_info = (struct dram_info *)dmcdbg_data.share_memory;
203
204 /* dram type information */
205 seq_printf(m, "DramType: %s\n", p_dram_info->dramtype);
206
207 /* dram capacity information */
208 seq_printf(m, "\n"
209 "DramCapacity:\n");
210
211 for (i = 0; i < p_dram_info->channel_num; i++) {
212 if (p_dram_info->channel_num == 0x2) {
213 seq_printf(m, "Channel [%d]:\n", i);
214 }
215
216 seq_printf(m,
217 "CS Count: %d\n"
218 "Bus Width: %d bit\n"
219 "Column: %d\n"
220 "Bank: %d\n"
221 "CS0_Row: %d\n"
222 "CS1_Row: %d\n"
223 "DieBusWidth: %d bit\n"
224 "TotalSize: %d MB\n",
225 p_dram_info->ch[i].rank, p_dram_info->ch[i].buswidth, p_dram_info->ch[i].col,
226 p_dram_info->ch[i].bank, p_dram_info->ch[i].cs0_row, p_dram_info->ch[i].cs1_row,
227 p_dram_info->ch[i].die_buswidth, p_dram_info->ch[i].size);
228 }
229
230 /* check devfreq/dmc device */
231 fp = filp_open("/sys/class/devfreq/dmc/cur_freq", O_RDONLY, 0);
232 if (IS_ERR(fp)) {
233 seq_printf(m,
234 "\n"
235 "devfreq/dmc: Disable\n"
236 "DramFreq: %d\n",
237 p_dram_info->dramfreq);
238 } else {
239 pos = 0;
240 kernel_read(fp, cur_freq, sizeof(cur_freq), &pos);
241 filp_close(fp, NULL);
242
243 fp = filp_open("/sys/class/devfreq/dmc/governor", O_RDONLY, 0);
244 if (IS_ERR(fp)) {
245 fp = NULL;
246 } else {
247 pos = 0;
248 kernel_read(fp, governor, sizeof(governor), &pos);
249 filp_close(fp, NULL);
250 }
251
252 seq_printf(m,
253 "\n"
254 "devfreq/dmc: Enable\n"
255 "governor: %s\n"
256 "cur_freq: %s\n",
257 governor, cur_freq);
258 seq_printf(m, "NOTE:\n"
259 "more information about dmc can get from /sys/class/devfreq/dmc.\n");
260 }
261
262 return 0;
263 }
264
dmcinfo_proc_open(struct inode *inode, struct file *file)265 static int dmcinfo_proc_open(struct inode *inode, struct file *file)
266 {
267 return single_open(file, dmcinfo_proc_show, NULL);
268 }
269
270 static const struct file_operations dmcinfo_proc_fops = {
271 .open = dmcinfo_proc_open,
272 .read = seq_read,
273 .llseek = seq_lseek,
274 .release = single_release,
275 };
276
proc_dmcinfo_init(void)277 static int proc_dmcinfo_init(void)
278 {
279 /* create dmcinfo file */
280 proc_create(PROC_DMCDBG_DRAM_INFO, 0x1a4, proc_dmcdbg_dir, &dmcinfo_proc_fops);
281
282 return 0;
283 }
284
powersave_proc_show(struct seq_file *m, void *v)285 static int powersave_proc_show(struct seq_file *m, void *v)
286 {
287 struct arm_smccc_res res;
288 struct power_save_info *p_power;
289 unsigned int *p_uint;
290 unsigned int i = 0;
291
292 /* get low power information */
293 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_POWERSAVE_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
294 if (res.a0) {
295 seq_printf(m, "rockchip_sip_config_dram_debug error:%lx\n", res.a0);
296 return -ENOMEM;
297 }
298
299 if (!dmcdbg_data.inited_flag) {
300 seq_puts(m, "dmcdbg_data no int\n");
301 return -EPERM;
302 }
303 p_power = (struct power_save_info *)dmcdbg_data.share_memory;
304
305 seq_printf(m, "low power information:\n"
306 "\n"
307 "[number]name: value\n");
308
309 p_uint = (unsigned int *)p_power;
310 for (i = 0; i < ARRAY_SIZE(power_save_msg); i++) {
311 seq_printf(m, "[%d]%s: %d\n", i, power_save_msg[i], *(p_uint + i));
312 }
313
314 seq_printf(m, "\n"
315 "power save setting:\n"
316 "echo number=value > /proc/dmcdbg/powersave\n"
317 "eg: set auto power down enable to 1\n"
318 " echo 0=1 > /proc/dmcdbg/powersave\n"
319 "\n"
320 "Support for setting multiple parameters at the same time.\n"
321 "echo number=value,number=value,... > /proc/dmcdbg/powersave\n"
322 "eg:\n"
323 " echo 0=1,1=32 > /proc/dmcdbg/powersave\n");
324
325 return 0;
326 }
327
powersave_proc_open(struct inode *inode, struct file *file)328 static int powersave_proc_open(struct inode *inode, struct file *file)
329 {
330 return single_open(file, powersave_proc_show, NULL);
331 }
332
powersave_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)333 static ssize_t powersave_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
334 {
335 struct arm_smccc_res res;
336 struct power_save_info *p_power;
337 unsigned int *p_uint;
338 char *buf, *cookie_pot, *p_char;
339 int ret = 0;
340 u32 loop, i, offset, value;
341 long long_val;
342
343 /* get buffer data */
344 buf = vzalloc(count);
345 cookie_pot = buf;
346 if (!cookie_pot) {
347 return -ENOMEM;
348 }
349
350 if (copy_from_user(cookie_pot, buffer, count)) {
351 ret = -EFAULT;
352 goto err;
353 }
354
355 /* get power save setting information */
356 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_POWERSAVE_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
357 if (res.a0) {
358 pr_err("rockchip_sip_config_dram_debug error:%lx\n", res.a0);
359 ret = -ENOMEM;
360 goto err;
361 }
362
363 if (!dmcdbg_data.inited_flag) {
364 pr_err("dmcdbg_data no int\n");
365 ret = -EPERM;
366 goto err;
367 }
368 p_power = (struct power_save_info *)dmcdbg_data.share_memory;
369
370 loop = 0;
371 for (i = 0; i < count; i++) {
372 if (*(cookie_pot + i) == '=') {
373 loop++;
374 }
375 }
376
377 p_uint = (unsigned int *)p_power;
378 for (i = 0; i < loop; i++) {
379 p_char = strsep(&cookie_pot, "=");
380 ret = kstrtol(p_char, 10, &long_val);
381 if (ret) {
382 goto err;
383 }
384 offset = long_val;
385
386 if (i == (loop - 1)) {
387 p_char = strsep(&cookie_pot, "\0");
388 } else {
389 p_char = strsep(&cookie_pot, ",");
390 }
391
392 ret = kstrtol(p_char, 0xa, &long_val);
393 if (ret) {
394 goto err;
395 }
396 value = long_val;
397
398 if (offset >= ARRAY_SIZE(power_save_msg)) {
399 ret = -EINVAL;
400 goto err;
401 }
402 offset = array_index_nospec(offset, ARRAY_SIZE(power_save_msg));
403
404 *(p_uint + offset) = value;
405 }
406
407 /* update power save setting */
408 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_UPDATE_POWERSAVE, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
409 if (res.a0) {
410 pr_err("rockchip_sip_config_dram_debug error:%lx\n", res.a0);
411 ret = -ENOMEM;
412 goto err;
413 }
414
415 ret = count;
416 err:
417 vfree(buf);
418 return ret;
419 }
420
421 static const struct file_operations powersave_proc_fops = {
422 .open = powersave_proc_open,
423 .read = seq_read,
424 .llseek = seq_lseek,
425 .release = single_release,
426 .write = powersave_proc_write,
427 };
428
proc_powersave_init(void)429 static int proc_powersave_init(void)
430 {
431 /* create dmcinfo file */
432 proc_create(PROC_DMCDBG_POWERSAVE, 0x1a4, proc_dmcdbg_dir, &powersave_proc_fops);
433
434 return 0;
435 }
436
drvodt_proc_show(struct seq_file *m, void *v)437 static int drvodt_proc_show(struct seq_file *m, void *v)
438 {
439 struct arm_smccc_res res;
440 struct drv_odt_info *p_drvodt;
441 unsigned int *p_uint;
442 unsigned int i;
443
444 /* get drive strength and odt information */
445 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_DRVODT_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
446 if (res.a0) {
447 seq_printf(m, "rockchip_sip_config_dram_debug error:%lx\n", res.a0);
448 return -ENOMEM;
449 }
450
451 if (!dmcdbg_data.inited_flag) {
452 seq_puts(m, "dmcdbg_data no int\n");
453 return -EPERM;
454 }
455 p_drvodt = (struct drv_odt_info *)dmcdbg_data.share_memory;
456
457 seq_printf(m, "drv and odt information:\n"
458 "\n"
459 "[number]name: value (ohm)\n");
460
461 p_uint = (unsigned int *)p_drvodt;
462 for (i = 0; i < ARRAY_SIZE(drv_odt_msg); i++) {
463 if (*(p_uint + (i * 0x3)) == DRV_ODT_UNKNOWN) {
464 seq_printf(m, "[%2d]%s: NULL (unknown) %c\n", i, drv_odt_msg[i],
465 (*(p_uint + (i * 0x3) + 0x2) == DRV_ODT_SUSPEND_FIX) ? '\0' : '*');
466 } else if (*(p_uint + (i * 0x3) + 0x1) == DRV_ODT_UNKNOWN) {
467 seq_printf(m, "[%2d]%s: %d (unknown) %c\n", i, drv_odt_msg[i], *(p_uint + (i * 0x3)),
468 (*(p_uint + (i * 0x3) + 0x2) == DRV_ODT_SUSPEND_FIX) ? '\0' : '*');
469 } else if (i < (ARRAY_SIZE(drv_odt_msg) - 0x2)) {
470 seq_printf(m, "[%2d]%s: %d (%d ohm) %c\n", i, drv_odt_msg[i], *(p_uint + (i * 0x3)),
471 *(p_uint + (i * 0x3) + 1), (*(p_uint + (i * 0x3) + 0x2) == DRV_ODT_SUSPEND_FIX) ? '\0' : '*');
472 } else {
473 seq_printf(m, "[%2d]%s: %d (%d %%) %c\n", i, drv_odt_msg[i], *(p_uint + (i * 0x3)),
474 *(p_uint + (i * 0x3) + 1), (*(p_uint + (i * 0x3) + 0x2) == DRV_ODT_SUSPEND_FIX) ? '\0' : '*');
475 }
476 }
477
478 seq_printf(m, "\n"
479 "drvodt setting:\n"
480 "echo number=value > /proc/dmcdbg/drvodt\n"
481 "eg: set soc side ca drv up to 20\n"
482 " echo 6=20 > /proc/dmcdbg/drvodt\n"
483 "\n"
484 "Support for setting multiple parameters at the same time.\n"
485 "echo number=value,number=value,... > /proc/dmcdbg/drvodt\n"
486 "eg: set soc side ca drv up and down to 20\n"
487 " echo 6=20,7=20 > /proc/dmcdbg/drvodt\n"
488 "Note: Please update both up and down at the same time.\n"
489 " (*) mean unsupported setting value\n");
490
491 return 0;
492 }
493
drvodt_proc_open(struct inode *inode, struct file *file)494 static int drvodt_proc_open(struct inode *inode, struct file *file)
495 {
496 return single_open(file, drvodt_proc_show, NULL);
497 }
498
drvodt_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)499 static ssize_t drvodt_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
500 {
501 struct arm_smccc_res res;
502 struct drv_odt_info *p_drvodt;
503 unsigned int *p_uint;
504 char *buf, *cookie_pot, *p_char;
505 int ret = 0;
506 u32 loop, i, offset, value;
507 long long_val;
508
509 /* get buffer data */
510 buf = vzalloc(count);
511 cookie_pot = buf;
512 if (!cookie_pot) {
513 return -ENOMEM;
514 }
515
516 if (copy_from_user(cookie_pot, buffer, count)) {
517 ret = -EFAULT;
518 goto err;
519 }
520
521 /* get drv and odt setting */
522 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_DRVODT_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
523 if (res.a0) {
524 pr_err("rockchip_sip_config_dram_debug error:%lx\n", res.a0);
525 ret = -ENOMEM;
526 goto err;
527 }
528
529 if (!dmcdbg_data.inited_flag) {
530 pr_err("dmcdbg_data no int\n");
531 ret = -EPERM;
532 goto err;
533 }
534 p_drvodt = (struct drv_odt_info *)dmcdbg_data.share_memory;
535
536 loop = 0;
537 for (i = 0; i < count; i++) {
538 if (*(cookie_pot + i) == '=') {
539 loop++;
540 }
541 }
542
543 p_uint = (unsigned int *)p_drvodt;
544 for (i = 0; i < loop; i++) {
545 p_char = strsep(&cookie_pot, "=");
546 ret = kstrtol(p_char, 0xa, &long_val);
547 if (ret) {
548 goto err;
549 }
550 offset = long_val;
551
552 if (i == (loop - 1)) {
553 p_char = strsep(&cookie_pot, "\0");
554 } else {
555 p_char = strsep(&cookie_pot, ",");
556 }
557
558 ret = kstrtol(p_char, 0xa, &long_val);
559 if (ret) {
560 goto err;
561 }
562 value = long_val;
563
564 if (offset >= ARRAY_SIZE(drv_odt_msg)) {
565 ret = -EINVAL;
566 goto err;
567 }
568 offset *= 3;
569 offset = array_index_nospec(offset, ARRAY_SIZE(drv_odt_msg) * 3);
570
571 *(p_uint + offset) = value;
572 }
573
574 /* update power save setting */
575 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_UPDATE_DRVODT, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
576 if (res.a0) {
577 pr_err("rockchip_sip_config_dram_debug error:%lx\n", res.a0);
578 ret = -ENOMEM;
579 goto err;
580 }
581
582 ret = count;
583 err:
584 vfree(buf);
585 return ret;
586 }
587
588 static const struct file_operations drvodt_proc_fops = {
589 .open = drvodt_proc_open,
590 .read = seq_read,
591 .llseek = seq_lseek,
592 .release = single_release,
593 .write = drvodt_proc_write,
594 };
595
proc_drvodt_init(void)596 static int proc_drvodt_init(void)
597 {
598 /* create dmcinfo file */
599 proc_create(PROC_DMCDBG_DRVODT, 0x1a4, proc_dmcdbg_dir, &drvodt_proc_fops);
600
601 return 0;
602 }
603
skew_proc_show(struct seq_file *m, void *v)604 static int skew_proc_show(struct seq_file *m, void *v)
605 {
606 struct arm_smccc_res res;
607 unsigned int *p_uint;
608 u32 group, i;
609
610 /* get deskew information */
611 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_DESKEW_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
612 if (res.a0) {
613 seq_printf(m, "rockchip_sip_config_dram_debug error:%lx\n", res.a0);
614 return -ENOMEM;
615 }
616
617 if (!dmcdbg_data.inited_flag) {
618 seq_puts(m, "dmcdbg_data no int\n");
619 return -EPERM;
620 }
621
622 seq_printf(m, "de-skew information:\n"
623 "\n"
624 "[group_number]name: value\n");
625
626 for (group = 0; group < dmcdbg_data.skew_group_num; group++) {
627 if (dmcdbg_data.skew_group[group].note != NULL) {
628 seq_printf(m, "%s\n", dmcdbg_data.skew_group[group].note);
629 }
630 p_uint = (unsigned int *)dmcdbg_data.skew_group[group].p_skew_info;
631 for (i = 0; i < dmcdbg_data.skew_group[group].skew_num; i++) {
632 seq_printf(m, "[%c%d_%d]%s: %d\n", (i < 0xa) ? ' ' : '\0', group, i,
633 dmcdbg_data.skew_group[group].p_skew_timing[i], *(p_uint + i));
634 }
635 }
636
637 seq_printf(m, "\n"
638 "de-skew setting:\n"
639 "echo group_number=value > /proc/dmcdbg/deskew\n"
640 "eg: set a1_ddr3a14_de-skew to 8\n"
641 " echo 0_1=8 > /proc/dmcdbg/deskew\n"
642 "\n"
643 "Support for setting multiple parameters simultaneously.\n"
644 "echo group_number=value,group_number=value,... > /proc/dmcdbg/deskew\n"
645 "eg:\n"
646 " echo 0_1=8,1_2=8 > /proc/dmcdbg/deskew\n");
647
648 return 0;
649 }
650
skew_proc_open(struct inode *inode, struct file *file)651 static int skew_proc_open(struct inode *inode, struct file *file)
652 {
653 return single_open(file, skew_proc_show, NULL);
654 }
655
skew_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)656 static ssize_t skew_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
657 {
658 struct arm_smccc_res res;
659 unsigned int *p_uint;
660 char *buf, *cookie_pot, *p_char;
661 int ret = 0;
662 u32 loop, i, offset_max, group, offset, value;
663 long long_val;
664
665 /* get buffer data */
666 buf = vzalloc(count);
667 cookie_pot = buf;
668 if (!cookie_pot) {
669 return -ENOMEM;
670 }
671
672 if (copy_from_user(cookie_pot, buffer, count)) {
673 ret = -EFAULT;
674 goto err;
675 }
676
677 /* get skew setting */
678 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_DESKEW_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
679 if (res.a0) {
680 pr_err("rockchip_sip_config_dram_debug error:%lx\n", res.a0);
681 ret = -ENOMEM;
682 goto err;
683 }
684
685 if (!dmcdbg_data.inited_flag) {
686 pr_err("dmcdbg_data no int\n");
687 ret = -EPERM;
688 goto err;
689 }
690
691 loop = 0;
692 for (i = 0; i < count; i++) {
693 if (*(cookie_pot + i) == '=') {
694 loop++;
695 }
696 }
697
698 for (i = 0; i < loop; i++) {
699 p_char = strsep(&cookie_pot, "_");
700 ret = kstrtol(p_char, 0xa, &long_val);
701 if (ret) {
702 goto err;
703 }
704 group = long_val;
705
706 p_char = strsep(&cookie_pot, "=");
707 ret = kstrtol(p_char, 0xa, &long_val);
708 if (ret) {
709 goto err;
710 }
711 offset = long_val;
712
713 if (i == (loop - 1)) {
714 p_char = strsep(&cookie_pot, "\0");
715 } else {
716 p_char = strsep(&cookie_pot, ",");
717 }
718
719 ret = kstrtol(p_char, 0xa, &long_val);
720 if (ret) {
721 goto err;
722 }
723 value = long_val;
724
725 if (group >= dmcdbg_data.skew_group_num) {
726 ret = -EINVAL;
727 goto err;
728 }
729 group = array_index_nospec(group, dmcdbg_data.skew_group_num);
730
731 p_uint = (unsigned int *)dmcdbg_data.skew_group[group].p_skew_info;
732 offset_max = dmcdbg_data.skew_group[group].skew_num;
733
734 if (offset >= offset_max) {
735 ret = -EINVAL;
736 goto err;
737 }
738 offset = array_index_nospec(offset, offset_max);
739
740 *(p_uint + offset) = value;
741 }
742
743 /* update power save setting */
744 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_UPDATE_DESKEW, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
745 if (res.a0) {
746 pr_err("rockchip_sip_config_dram_debug error:%lx\n", res.a0);
747 ret = -ENOMEM;
748 goto err;
749 }
750
751 ret = count;
752 err:
753 vfree(buf);
754 return ret;
755 }
756
757 static const struct file_operations skew_proc_fops = {
758 .open = skew_proc_open,
759 .read = seq_read,
760 .llseek = seq_lseek,
761 .release = single_release,
762 .write = skew_proc_write,
763 };
764
proc_skew_init(void)765 static int proc_skew_init(void)
766 {
767 /* create dmcinfo file */
768 proc_create(PROC_DMCDBG_DESKEW, 0x1a4, proc_dmcdbg_dir, &skew_proc_fops);
769
770 return 0;
771 }
772
regsinfo_proc_show(struct seq_file *m, void *v)773 static int regsinfo_proc_show(struct seq_file *m, void *v)
774 {
775 struct arm_smccc_res res;
776 struct registers_info *p_regsinfo;
777 u32 i;
778
779 res = sip_smc_dram(SHARE_PAGE_TYPE_DDRDBG, DDRDBG_FUNC_GET_REGISTERS_INFO, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
780 if (res.a0) {
781 seq_printf(m, "rockchip_sip_config_dram_debug error:%lx\n", res.a0);
782 return -ENOMEM;
783 }
784
785 if (!dmcdbg_data.inited_flag) {
786 seq_puts(m, "dmcdbg_data no int\n");
787 return -EPERM;
788 }
789 p_regsinfo = (struct registers_info *)dmcdbg_data.share_memory;
790
791 seq_printf(m, "registers base address information:\n"
792 "\n");
793
794 for (i = 0; i < p_regsinfo->regs_num; i++) {
795 seq_printf(m, "%s=0x%x\n", p_regsinfo->regs[i].regs_name, p_regsinfo->regs[i].regs_addr);
796 }
797
798 return 0;
799 }
800
regsinfo_proc_open(struct inode *inode, struct file *file)801 static int regsinfo_proc_open(struct inode *inode, struct file *file)
802 {
803 return single_open(file, regsinfo_proc_show, NULL);
804 }
805
806 static const struct file_operations regsinfo_proc_fops = {
807 .open = regsinfo_proc_open,
808 .read = seq_read,
809 .llseek = seq_lseek,
810 .release = single_release,
811 };
812
proc_regsinfo_init(void)813 static int proc_regsinfo_init(void)
814 {
815 /* create dmcinfo file */
816 proc_create(PROC_DMCDBG_REGS_INFO, 0x1a4, proc_dmcdbg_dir, ®sinfo_proc_fops);
817
818 return 0;
819 }
820
rv1126_get_skew_parameter(void)821 static void rv1126_get_skew_parameter(void)
822 {
823 struct skew_info_rv1126 *p_skew;
824 u32 i;
825
826 /* get skew parameters */
827 p_skew = (struct skew_info_rv1126 *)dmcdbg_data.share_memory;
828 dmcdbg_data.skew_group_num = 0x5;
829
830 /* ca_skew parameters */
831 dmcdbg_data.skew_group[0].p_skew_info = (unsigned int *)p_skew->ca_skew;
832 dmcdbg_data.skew_group[0].skew_num = ARRAY_SIZE(rv1126_dts_ca_timing);
833 for (i = 0; i < dmcdbg_data.skew_group[0].skew_num; i++) {
834 dmcdbg_data.skew_group[0].p_skew_timing[i] = (char *)rv1126_dts_ca_timing[i];
835 }
836 dmcdbg_data.skew_group[0].note = "(ca_skew: ddr4(pad_name)_ddr3_lpddr3_lpddr4_de-skew)";
837
838 /* cs0_a_skew parameters */
839 dmcdbg_data.skew_group[1].p_skew_info = (unsigned int *)p_skew->cs0_a_skew;
840 dmcdbg_data.skew_group[1].skew_num = ARRAY_SIZE(rv1126_dts_cs0_a_timing);
841 for (i = 0; i < dmcdbg_data.skew_group[1].skew_num; i++) {
842 dmcdbg_data.skew_group[1].p_skew_timing[i] = (char *)rv1126_dts_cs0_a_timing[i];
843 }
844 dmcdbg_data.skew_group[1].note = "(cs0_a_skew)";
845
846 /* cs0_b_skew parameters */
847 dmcdbg_data.skew_group[0x2].p_skew_info = (unsigned int *)p_skew->cs0_b_skew;
848 dmcdbg_data.skew_group[0x2].skew_num = ARRAY_SIZE(rv1126_dts_cs0_b_timing);
849 for (i = 0; i < dmcdbg_data.skew_group[0x2].skew_num; i++) {
850 dmcdbg_data.skew_group[0x2].p_skew_timing[i] = (char *)rv1126_dts_cs0_b_timing[i];
851 }
852 dmcdbg_data.skew_group[0x2].note = "(cs0_b_skew)";
853
854 /* cs1_a_skew parameters */
855 dmcdbg_data.skew_group[0x3].p_skew_info = (unsigned int *)p_skew->cs1_a_skew;
856 dmcdbg_data.skew_group[0x3].skew_num = ARRAY_SIZE(rv1126_dts_cs1_a_timing);
857 for (i = 0; i < dmcdbg_data.skew_group[0x3].skew_num; i++) {
858 dmcdbg_data.skew_group[0x3].p_skew_timing[i] = (char *)rv1126_dts_cs1_a_timing[i];
859 }
860 dmcdbg_data.skew_group[0x3].note = "(cs1_a_skew)";
861
862 /* cs1_b_skew parameters */
863 dmcdbg_data.skew_group[0x4].p_skew_info = (unsigned int *)p_skew->cs1_b_skew;
864 dmcdbg_data.skew_group[0x4].skew_num = ARRAY_SIZE(rv1126_dts_cs1_b_timing);
865 for (i = 0; i < dmcdbg_data.skew_group[0x3].skew_num; i++) {
866 dmcdbg_data.skew_group[0x4].p_skew_timing[i] = (char *)rv1126_dts_cs1_b_timing[i];
867 }
868 dmcdbg_data.skew_group[0x4].note = "(cs1_b_skew)";
869 }
870
rv1126_dmcdbg_init(struct platform_device *pdev, struct rockchip_dmcdbg *dmcdbg)871 static __maybe_unused int rv1126_dmcdbg_init(struct platform_device *pdev, struct rockchip_dmcdbg *dmcdbg)
872 {
873 struct arm_smccc_res res;
874
875 /* check ddr_debug_func version */
876 res = sip_smc_dram(0, DDRDBG_FUNCGET_VERSION, ROCKCHIP_SIP_CONFIG_DRAM_DEBUG);
877 dev_notice(&pdev->dev, "current ATF ddr_debug_func version 0x%lx.\n", res.a1);
878 /*
879 * [15:8] major version, [7:0] minor version
880 * major version must match both kernel dmcdbg and ATF ddr_debug_func.
881 */
882 if (res.a0 || res.a1 < 0x101 || ((res.a1 & 0xff00) != 0x100)) {
883 dev_err(&pdev->dev, "version invalid,need update,the major version unmatch!\n");
884 return -ENXIO;
885 }
886
887 /* request share memory for pass parameter */
888 res = sip_smc_request_share_mem(DMCDBG_PAGE_NUMS, SHARE_PAGE_TYPE_DDRDBG);
889 if (res.a0 != 0) {
890 dev_err(&pdev->dev, "request share mem error\n");
891 return -ENOMEM;
892 }
893
894 dmcdbg_data.share_memory = (void __iomem *)res.a1;
895 dmcdbg_data.inited_flag = 1;
896
897 rv1126_get_skew_parameter();
898
899 /* create parent dir in /proc */
900 proc_dmcdbg_dir = proc_mkdir(PROC_DMCDBG_DIR_NAME, NULL);
901 if (!proc_dmcdbg_dir) {
902 dev_err(&pdev->dev, "create proc dir error!");
903 return -ENOENT;
904 }
905
906 proc_dmcinfo_init();
907 proc_powersave_init();
908 proc_drvodt_init();
909 proc_skew_init();
910 proc_regsinfo_init();
911 return 0;
912 }
913
914 static const struct of_device_id rockchip_dmcdbg_of_match[] = {
915 {.compatible = "rockchip,rv1126-dmcdbg", .data = rv1126_dmcdbg_init},
916 {},
917 };
918 MODULE_DEVICE_TABLE(of, rockchip_dmcdbg_of_match);
919
rockchip_dmcdbg_probe(struct platform_device *pdev)920 static int rockchip_dmcdbg_probe(struct platform_device *pdev)
921 {
922 struct device *dev = &pdev->dev;
923 struct rockchip_dmcdbg *data;
924 const struct of_device_id *match;
925 int (*init)(struct platform_device * pdev, struct rockchip_dmcdbg * data);
926 int ret = 0;
927
928 data = devm_kzalloc(dev, sizeof(struct rockchip_dmcdbg), GFP_KERNEL);
929 if (!data) {
930 return -ENOMEM;
931 }
932
933 data->dev = dev;
934
935 /* match soc chip init */
936 match = of_match_node(rockchip_dmcdbg_of_match, pdev->dev.of_node);
937 if (match) {
938 init = match->data;
939 if (init) {
940 if (init(pdev, data)) {
941 return -EINVAL;
942 }
943 }
944 }
945
946 return ret;
947 }
948
949 static struct platform_driver rockchip_dmcdbg_driver = {
950 .probe = rockchip_dmcdbg_probe,
951 .driver =
952 {
953 .name = "rockchip,dmcdbg",
954 .of_match_table = rockchip_dmcdbg_of_match,
955 },
956 };
957 module_platform_driver(rockchip_dmcdbg_driver);
958
959 MODULE_LICENSE("GPL v2");
960 MODULE_AUTHOR("YouMin Chen <cym@rock-chips.com>");
961 MODULE_DESCRIPTION("rockchip dmc debug driver with devfreq framework");
962