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, &regsinfo_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