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