• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 		    &regsinfo_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