1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2020 Intel Corporation |
3 | |
4 | #include <linux/debugfs.h> |
5 | |
6 | #include "ufs-debugfs.h" |
7 | #include <ufs/ufshcd.h> |
8 | #include "ufshcd-priv.h" |
9 | |
10 | static struct dentry *ufs_debugfs_root; |
11 | |
12 | struct ufs_debugfs_attr { |
13 | const char *name; |
14 | mode_t mode; |
15 | const struct file_operations *fops; |
16 | }; |
17 | |
18 | /* @file corresponds to a debugfs attribute in directory hba->debugfs_root. */ |
19 | static inline struct ufs_hba *hba_from_file(const struct file *file) |
20 | { |
21 | return d_inode(dentry: file->f_path.dentry->d_parent)->i_private; |
22 | } |
23 | |
24 | void __init ufs_debugfs_init(void) |
25 | { |
26 | ufs_debugfs_root = debugfs_create_dir(name: "ufshcd" , NULL); |
27 | } |
28 | |
29 | void ufs_debugfs_exit(void) |
30 | { |
31 | debugfs_remove_recursive(dentry: ufs_debugfs_root); |
32 | } |
33 | |
34 | static int ufs_debugfs_stats_show(struct seq_file *s, void *data) |
35 | { |
36 | struct ufs_hba *hba = hba_from_file(file: s->file); |
37 | struct ufs_event_hist *e = hba->ufs_stats.event; |
38 | |
39 | #define PRT(fmt, typ) \ |
40 | seq_printf(s, fmt, e[UFS_EVT_ ## typ].cnt) |
41 | |
42 | PRT("PHY Adapter Layer errors (except LINERESET): %llu\n" , PA_ERR); |
43 | PRT("Data Link Layer errors: %llu\n" , DL_ERR); |
44 | PRT("Network Layer errors: %llu\n" , NL_ERR); |
45 | PRT("Transport Layer errors: %llu\n" , TL_ERR); |
46 | PRT("Generic DME errors: %llu\n" , DME_ERR); |
47 | PRT("Auto-hibernate errors: %llu\n" , AUTO_HIBERN8_ERR); |
48 | PRT("IS Fatal errors (CEFES, SBFES, HCFES, DFES): %llu\n" , FATAL_ERR); |
49 | PRT("DME Link Startup errors: %llu\n" , LINK_STARTUP_FAIL); |
50 | PRT("PM Resume errors: %llu\n" , RESUME_ERR); |
51 | PRT("PM Suspend errors : %llu\n" , SUSPEND_ERR); |
52 | PRT("Logical Unit Resets: %llu\n" , DEV_RESET); |
53 | PRT("Host Resets: %llu\n" , HOST_RESET); |
54 | PRT("SCSI command aborts: %llu\n" , ABORT); |
55 | #undef PRT |
56 | return 0; |
57 | } |
58 | DEFINE_SHOW_ATTRIBUTE(ufs_debugfs_stats); |
59 | |
60 | static int ee_usr_mask_get(void *data, u64 *val) |
61 | { |
62 | struct ufs_hba *hba = data; |
63 | |
64 | *val = hba->ee_usr_mask; |
65 | return 0; |
66 | } |
67 | |
68 | static int ufs_debugfs_get_user_access(struct ufs_hba *hba) |
69 | __acquires(&hba->host_sem) |
70 | { |
71 | down(sem: &hba->host_sem); |
72 | if (!ufshcd_is_user_access_allowed(hba)) { |
73 | up(sem: &hba->host_sem); |
74 | return -EBUSY; |
75 | } |
76 | ufshcd_rpm_get_sync(hba); |
77 | return 0; |
78 | } |
79 | |
80 | static void ufs_debugfs_put_user_access(struct ufs_hba *hba) |
81 | __releases(&hba->host_sem) |
82 | { |
83 | ufshcd_rpm_put_sync(hba); |
84 | up(sem: &hba->host_sem); |
85 | } |
86 | |
87 | static int ee_usr_mask_set(void *data, u64 val) |
88 | { |
89 | struct ufs_hba *hba = data; |
90 | int err; |
91 | |
92 | if (val & ~(u64)MASK_EE_STATUS) |
93 | return -EINVAL; |
94 | err = ufs_debugfs_get_user_access(hba); |
95 | if (err) |
96 | return err; |
97 | err = ufshcd_update_ee_usr_mask(hba, set: val, clr: MASK_EE_STATUS); |
98 | ufs_debugfs_put_user_access(hba); |
99 | return err; |
100 | } |
101 | |
102 | DEFINE_DEBUGFS_ATTRIBUTE(ee_usr_mask_fops, ee_usr_mask_get, ee_usr_mask_set, "%#llx\n" ); |
103 | |
104 | void ufs_debugfs_exception_event(struct ufs_hba *hba, u16 status) |
105 | { |
106 | bool chgd = false; |
107 | u16 ee_ctrl_mask; |
108 | int err = 0; |
109 | |
110 | if (!hba->debugfs_ee_rate_limit_ms || !status) |
111 | return; |
112 | |
113 | mutex_lock(&hba->ee_ctrl_mutex); |
114 | ee_ctrl_mask = hba->ee_drv_mask | (hba->ee_usr_mask & ~status); |
115 | chgd = ee_ctrl_mask != hba->ee_ctrl_mask; |
116 | if (chgd) { |
117 | err = __ufshcd_write_ee_control(hba, ee_ctrl_mask); |
118 | if (err) |
119 | dev_err(hba->dev, "%s: failed to write ee control %d\n" , |
120 | __func__, err); |
121 | } |
122 | mutex_unlock(lock: &hba->ee_ctrl_mutex); |
123 | |
124 | if (chgd && !err) { |
125 | unsigned long delay = msecs_to_jiffies(m: hba->debugfs_ee_rate_limit_ms); |
126 | |
127 | queue_delayed_work(wq: system_freezable_wq, dwork: &hba->debugfs_ee_work, delay); |
128 | } |
129 | } |
130 | |
131 | static void ufs_debugfs_restart_ee(struct work_struct *work) |
132 | { |
133 | struct ufs_hba *hba = container_of(work, struct ufs_hba, debugfs_ee_work.work); |
134 | |
135 | if (!hba->ee_usr_mask || pm_runtime_suspended(dev: hba->dev) || |
136 | ufs_debugfs_get_user_access(hba)) |
137 | return; |
138 | ufshcd_write_ee_control(hba); |
139 | ufs_debugfs_put_user_access(hba); |
140 | } |
141 | |
142 | static int ufs_saved_err_show(struct seq_file *s, void *data) |
143 | { |
144 | struct ufs_debugfs_attr *attr = s->private; |
145 | struct ufs_hba *hba = hba_from_file(file: s->file); |
146 | const int *p; |
147 | |
148 | if (strcmp(attr->name, "saved_err" ) == 0) { |
149 | p = &hba->saved_err; |
150 | } else if (strcmp(attr->name, "saved_uic_err" ) == 0) { |
151 | p = &hba->saved_uic_err; |
152 | } else { |
153 | return -ENOENT; |
154 | } |
155 | |
156 | seq_printf(m: s, fmt: "%d\n" , *p); |
157 | return 0; |
158 | } |
159 | |
160 | static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf, |
161 | size_t count, loff_t *ppos) |
162 | { |
163 | struct ufs_debugfs_attr *attr = file->f_inode->i_private; |
164 | struct ufs_hba *hba = hba_from_file(file); |
165 | char val_str[16] = { }; |
166 | int val, ret; |
167 | |
168 | if (count > sizeof(val_str)) |
169 | return -EINVAL; |
170 | if (copy_from_user(to: val_str, from: buf, n: count)) |
171 | return -EFAULT; |
172 | ret = kstrtoint(s: val_str, base: 0, res: &val); |
173 | if (ret < 0) |
174 | return ret; |
175 | |
176 | spin_lock_irq(lock: hba->host->host_lock); |
177 | if (strcmp(attr->name, "saved_err" ) == 0) { |
178 | hba->saved_err = val; |
179 | } else if (strcmp(attr->name, "saved_uic_err" ) == 0) { |
180 | hba->saved_uic_err = val; |
181 | } else { |
182 | ret = -ENOENT; |
183 | } |
184 | if (ret == 0) |
185 | ufshcd_schedule_eh_work(hba); |
186 | spin_unlock_irq(lock: hba->host->host_lock); |
187 | |
188 | return ret < 0 ? ret : count; |
189 | } |
190 | |
191 | static int ufs_saved_err_open(struct inode *inode, struct file *file) |
192 | { |
193 | return single_open(file, ufs_saved_err_show, inode->i_private); |
194 | } |
195 | |
196 | static const struct file_operations ufs_saved_err_fops = { |
197 | .owner = THIS_MODULE, |
198 | .open = ufs_saved_err_open, |
199 | .read = seq_read, |
200 | .write = ufs_saved_err_write, |
201 | .llseek = seq_lseek, |
202 | .release = single_release, |
203 | }; |
204 | |
205 | static const struct ufs_debugfs_attr ufs_attrs[] = { |
206 | { "stats" , 0400, &ufs_debugfs_stats_fops }, |
207 | { "saved_err" , 0600, &ufs_saved_err_fops }, |
208 | { "saved_uic_err" , 0600, &ufs_saved_err_fops }, |
209 | { } |
210 | }; |
211 | |
212 | void ufs_debugfs_hba_init(struct ufs_hba *hba) |
213 | { |
214 | const struct ufs_debugfs_attr *attr; |
215 | struct dentry *root; |
216 | |
217 | /* Set default exception event rate limit period to 20ms */ |
218 | hba->debugfs_ee_rate_limit_ms = 20; |
219 | INIT_DELAYED_WORK(&hba->debugfs_ee_work, ufs_debugfs_restart_ee); |
220 | |
221 | root = debugfs_create_dir(name: dev_name(dev: hba->dev), parent: ufs_debugfs_root); |
222 | if (IS_ERR_OR_NULL(ptr: root)) |
223 | return; |
224 | hba->debugfs_root = root; |
225 | d_inode(dentry: root)->i_private = hba; |
226 | for (attr = ufs_attrs; attr->name; attr++) |
227 | debugfs_create_file(name: attr->name, mode: attr->mode, parent: root, data: (void *)attr, |
228 | fops: attr->fops); |
229 | debugfs_create_file(name: "exception_event_mask" , mode: 0600, parent: hba->debugfs_root, |
230 | data: hba, fops: &ee_usr_mask_fops); |
231 | debugfs_create_u32(name: "exception_event_rate_limit_ms" , mode: 0600, parent: hba->debugfs_root, |
232 | value: &hba->debugfs_ee_rate_limit_ms); |
233 | } |
234 | |
235 | void ufs_debugfs_hba_exit(struct ufs_hba *hba) |
236 | { |
237 | debugfs_remove_recursive(dentry: hba->debugfs_root); |
238 | cancel_delayed_work_sync(dwork: &hba->debugfs_ee_work); |
239 | } |
240 | |