1 | /* |
2 | * AMD 10Gb Ethernet driver |
3 | * |
4 | * This file is available to you under your choice of the following two |
5 | * licenses: |
6 | * |
7 | * License 1: GPLv2 |
8 | * |
9 | * Copyright (c) 2014 Advanced Micro Devices, Inc. |
10 | * |
11 | * This file is free software; you may copy, redistribute and/or modify |
12 | * it under the terms of the GNU General Public License as published by |
13 | * the Free Software Foundation, either version 2 of the License, or (at |
14 | * your option) any later version. |
15 | * |
16 | * This file is distributed in the hope that it will be useful, but |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
19 | * General Public License for more details. |
20 | * |
21 | * You should have received a copy of the GNU General Public License |
22 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
23 | * |
24 | * This file incorporates work covered by the following copyright and |
25 | * permission notice: |
26 | * The Synopsys DWC ETHER XGMAC Software Driver and documentation |
27 | * (hereinafter "Software") is an unsupported proprietary work of Synopsys, |
28 | * Inc. unless otherwise expressly agreed to in writing between Synopsys |
29 | * and you. |
30 | * |
31 | * The Software IS NOT an item of Licensed Software or Licensed Product |
32 | * under any End User Software License Agreement or Agreement for Licensed |
33 | * Product with Synopsys or any supplement thereto. Permission is hereby |
34 | * granted, free of charge, to any person obtaining a copy of this software |
35 | * annotated with this license and the Software, to deal in the Software |
36 | * without restriction, including without limitation the rights to use, |
37 | * copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
38 | * of the Software, and to permit persons to whom the Software is furnished |
39 | * to do so, subject to the following conditions: |
40 | * |
41 | * The above copyright notice and this permission notice shall be included |
42 | * in all copies or substantial portions of the Software. |
43 | * |
44 | * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" |
45 | * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
46 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
47 | * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS |
48 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
49 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
50 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
51 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
52 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
53 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
54 | * THE POSSIBILITY OF SUCH DAMAGE. |
55 | * |
56 | * |
57 | * License 2: Modified BSD |
58 | * |
59 | * Copyright (c) 2014 Advanced Micro Devices, Inc. |
60 | * All rights reserved. |
61 | * |
62 | * Redistribution and use in source and binary forms, with or without |
63 | * modification, are permitted provided that the following conditions are met: |
64 | * * Redistributions of source code must retain the above copyright |
65 | * notice, this list of conditions and the following disclaimer. |
66 | * * Redistributions in binary form must reproduce the above copyright |
67 | * notice, this list of conditions and the following disclaimer in the |
68 | * documentation and/or other materials provided with the distribution. |
69 | * * Neither the name of Advanced Micro Devices, Inc. nor the |
70 | * names of its contributors may be used to endorse or promote products |
71 | * derived from this software without specific prior written permission. |
72 | * |
73 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
74 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
75 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
76 | * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY |
77 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
78 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
79 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
80 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
81 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
82 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
83 | * |
84 | * This file incorporates work covered by the following copyright and |
85 | * permission notice: |
86 | * The Synopsys DWC ETHER XGMAC Software Driver and documentation |
87 | * (hereinafter "Software") is an unsupported proprietary work of Synopsys, |
88 | * Inc. unless otherwise expressly agreed to in writing between Synopsys |
89 | * and you. |
90 | * |
91 | * The Software IS NOT an item of Licensed Software or Licensed Product |
92 | * under any End User Software License Agreement or Agreement for Licensed |
93 | * Product with Synopsys or any supplement thereto. Permission is hereby |
94 | * granted, free of charge, to any person obtaining a copy of this software |
95 | * annotated with this license and the Software, to deal in the Software |
96 | * without restriction, including without limitation the rights to use, |
97 | * copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
98 | * of the Software, and to permit persons to whom the Software is furnished |
99 | * to do so, subject to the following conditions: |
100 | * |
101 | * The above copyright notice and this permission notice shall be included |
102 | * in all copies or substantial portions of the Software. |
103 | * |
104 | * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" |
105 | * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
106 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
107 | * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS |
108 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
109 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
110 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
111 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
112 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
113 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
114 | * THE POSSIBILITY OF SUCH DAMAGE. |
115 | */ |
116 | |
117 | #include <linux/debugfs.h> |
118 | #include <linux/module.h> |
119 | #include <linux/slab.h> |
120 | |
121 | #include "xgbe.h" |
122 | #include "xgbe-common.h" |
123 | |
124 | static ssize_t xgbe_common_read(char __user *buffer, size_t count, |
125 | loff_t *ppos, unsigned int value) |
126 | { |
127 | char *buf; |
128 | ssize_t len; |
129 | |
130 | if (*ppos != 0) |
131 | return 0; |
132 | |
133 | buf = kasprintf(GFP_KERNEL, fmt: "0x%08x\n" , value); |
134 | if (!buf) |
135 | return -ENOMEM; |
136 | |
137 | if (count < strlen(buf)) { |
138 | kfree(objp: buf); |
139 | return -ENOSPC; |
140 | } |
141 | |
142 | len = simple_read_from_buffer(to: buffer, count, ppos, from: buf, strlen(buf)); |
143 | kfree(objp: buf); |
144 | |
145 | return len; |
146 | } |
147 | |
148 | static ssize_t xgbe_common_write(const char __user *buffer, size_t count, |
149 | loff_t *ppos, unsigned int *value) |
150 | { |
151 | char workarea[32]; |
152 | ssize_t len; |
153 | int ret; |
154 | |
155 | if (*ppos != 0) |
156 | return -EINVAL; |
157 | |
158 | if (count >= sizeof(workarea)) |
159 | return -ENOSPC; |
160 | |
161 | len = simple_write_to_buffer(to: workarea, available: sizeof(workarea) - 1, ppos, |
162 | from: buffer, count); |
163 | if (len < 0) |
164 | return len; |
165 | |
166 | workarea[len] = '\0'; |
167 | ret = kstrtouint(s: workarea, base: 16, res: value); |
168 | if (ret) |
169 | return -EIO; |
170 | |
171 | return len; |
172 | } |
173 | |
174 | static ssize_t xgmac_reg_addr_read(struct file *filp, char __user *buffer, |
175 | size_t count, loff_t *ppos) |
176 | { |
177 | struct xgbe_prv_data *pdata = filp->private_data; |
178 | |
179 | return xgbe_common_read(buffer, count, ppos, value: pdata->debugfs_xgmac_reg); |
180 | } |
181 | |
182 | static ssize_t xgmac_reg_addr_write(struct file *filp, |
183 | const char __user *buffer, |
184 | size_t count, loff_t *ppos) |
185 | { |
186 | struct xgbe_prv_data *pdata = filp->private_data; |
187 | |
188 | return xgbe_common_write(buffer, count, ppos, |
189 | value: &pdata->debugfs_xgmac_reg); |
190 | } |
191 | |
192 | static ssize_t xgmac_reg_value_read(struct file *filp, char __user *buffer, |
193 | size_t count, loff_t *ppos) |
194 | { |
195 | struct xgbe_prv_data *pdata = filp->private_data; |
196 | unsigned int value; |
197 | |
198 | value = XGMAC_IOREAD(pdata, pdata->debugfs_xgmac_reg); |
199 | |
200 | return xgbe_common_read(buffer, count, ppos, value); |
201 | } |
202 | |
203 | static ssize_t xgmac_reg_value_write(struct file *filp, |
204 | const char __user *buffer, |
205 | size_t count, loff_t *ppos) |
206 | { |
207 | struct xgbe_prv_data *pdata = filp->private_data; |
208 | unsigned int value; |
209 | ssize_t len; |
210 | |
211 | len = xgbe_common_write(buffer, count, ppos, value: &value); |
212 | if (len < 0) |
213 | return len; |
214 | |
215 | XGMAC_IOWRITE(pdata, pdata->debugfs_xgmac_reg, value); |
216 | |
217 | return len; |
218 | } |
219 | |
220 | static const struct file_operations xgmac_reg_addr_fops = { |
221 | .owner = THIS_MODULE, |
222 | .open = simple_open, |
223 | .read = xgmac_reg_addr_read, |
224 | .write = xgmac_reg_addr_write, |
225 | }; |
226 | |
227 | static const struct file_operations xgmac_reg_value_fops = { |
228 | .owner = THIS_MODULE, |
229 | .open = simple_open, |
230 | .read = xgmac_reg_value_read, |
231 | .write = xgmac_reg_value_write, |
232 | }; |
233 | |
234 | static ssize_t xpcs_mmd_read(struct file *filp, char __user *buffer, |
235 | size_t count, loff_t *ppos) |
236 | { |
237 | struct xgbe_prv_data *pdata = filp->private_data; |
238 | |
239 | return xgbe_common_read(buffer, count, ppos, value: pdata->debugfs_xpcs_mmd); |
240 | } |
241 | |
242 | static ssize_t xpcs_mmd_write(struct file *filp, const char __user *buffer, |
243 | size_t count, loff_t *ppos) |
244 | { |
245 | struct xgbe_prv_data *pdata = filp->private_data; |
246 | |
247 | return xgbe_common_write(buffer, count, ppos, |
248 | value: &pdata->debugfs_xpcs_mmd); |
249 | } |
250 | |
251 | static ssize_t xpcs_reg_addr_read(struct file *filp, char __user *buffer, |
252 | size_t count, loff_t *ppos) |
253 | { |
254 | struct xgbe_prv_data *pdata = filp->private_data; |
255 | |
256 | return xgbe_common_read(buffer, count, ppos, value: pdata->debugfs_xpcs_reg); |
257 | } |
258 | |
259 | static ssize_t xpcs_reg_addr_write(struct file *filp, const char __user *buffer, |
260 | size_t count, loff_t *ppos) |
261 | { |
262 | struct xgbe_prv_data *pdata = filp->private_data; |
263 | |
264 | return xgbe_common_write(buffer, count, ppos, |
265 | value: &pdata->debugfs_xpcs_reg); |
266 | } |
267 | |
268 | static ssize_t xpcs_reg_value_read(struct file *filp, char __user *buffer, |
269 | size_t count, loff_t *ppos) |
270 | { |
271 | struct xgbe_prv_data *pdata = filp->private_data; |
272 | unsigned int value; |
273 | |
274 | value = XMDIO_READ(pdata, pdata->debugfs_xpcs_mmd, |
275 | pdata->debugfs_xpcs_reg); |
276 | |
277 | return xgbe_common_read(buffer, count, ppos, value); |
278 | } |
279 | |
280 | static ssize_t xpcs_reg_value_write(struct file *filp, |
281 | const char __user *buffer, |
282 | size_t count, loff_t *ppos) |
283 | { |
284 | struct xgbe_prv_data *pdata = filp->private_data; |
285 | unsigned int value; |
286 | ssize_t len; |
287 | |
288 | len = xgbe_common_write(buffer, count, ppos, value: &value); |
289 | if (len < 0) |
290 | return len; |
291 | |
292 | XMDIO_WRITE(pdata, pdata->debugfs_xpcs_mmd, pdata->debugfs_xpcs_reg, |
293 | value); |
294 | |
295 | return len; |
296 | } |
297 | |
298 | static const struct file_operations xpcs_mmd_fops = { |
299 | .owner = THIS_MODULE, |
300 | .open = simple_open, |
301 | .read = xpcs_mmd_read, |
302 | .write = xpcs_mmd_write, |
303 | }; |
304 | |
305 | static const struct file_operations xpcs_reg_addr_fops = { |
306 | .owner = THIS_MODULE, |
307 | .open = simple_open, |
308 | .read = xpcs_reg_addr_read, |
309 | .write = xpcs_reg_addr_write, |
310 | }; |
311 | |
312 | static const struct file_operations xpcs_reg_value_fops = { |
313 | .owner = THIS_MODULE, |
314 | .open = simple_open, |
315 | .read = xpcs_reg_value_read, |
316 | .write = xpcs_reg_value_write, |
317 | }; |
318 | |
319 | static ssize_t xprop_reg_addr_read(struct file *filp, char __user *buffer, |
320 | size_t count, loff_t *ppos) |
321 | { |
322 | struct xgbe_prv_data *pdata = filp->private_data; |
323 | |
324 | return xgbe_common_read(buffer, count, ppos, value: pdata->debugfs_xprop_reg); |
325 | } |
326 | |
327 | static ssize_t xprop_reg_addr_write(struct file *filp, |
328 | const char __user *buffer, |
329 | size_t count, loff_t *ppos) |
330 | { |
331 | struct xgbe_prv_data *pdata = filp->private_data; |
332 | |
333 | return xgbe_common_write(buffer, count, ppos, |
334 | value: &pdata->debugfs_xprop_reg); |
335 | } |
336 | |
337 | static ssize_t xprop_reg_value_read(struct file *filp, char __user *buffer, |
338 | size_t count, loff_t *ppos) |
339 | { |
340 | struct xgbe_prv_data *pdata = filp->private_data; |
341 | unsigned int value; |
342 | |
343 | value = XP_IOREAD(pdata, pdata->debugfs_xprop_reg); |
344 | |
345 | return xgbe_common_read(buffer, count, ppos, value); |
346 | } |
347 | |
348 | static ssize_t xprop_reg_value_write(struct file *filp, |
349 | const char __user *buffer, |
350 | size_t count, loff_t *ppos) |
351 | { |
352 | struct xgbe_prv_data *pdata = filp->private_data; |
353 | unsigned int value; |
354 | ssize_t len; |
355 | |
356 | len = xgbe_common_write(buffer, count, ppos, value: &value); |
357 | if (len < 0) |
358 | return len; |
359 | |
360 | XP_IOWRITE(pdata, pdata->debugfs_xprop_reg, value); |
361 | |
362 | return len; |
363 | } |
364 | |
365 | static const struct file_operations xprop_reg_addr_fops = { |
366 | .owner = THIS_MODULE, |
367 | .open = simple_open, |
368 | .read = xprop_reg_addr_read, |
369 | .write = xprop_reg_addr_write, |
370 | }; |
371 | |
372 | static const struct file_operations xprop_reg_value_fops = { |
373 | .owner = THIS_MODULE, |
374 | .open = simple_open, |
375 | .read = xprop_reg_value_read, |
376 | .write = xprop_reg_value_write, |
377 | }; |
378 | |
379 | static ssize_t xi2c_reg_addr_read(struct file *filp, char __user *buffer, |
380 | size_t count, loff_t *ppos) |
381 | { |
382 | struct xgbe_prv_data *pdata = filp->private_data; |
383 | |
384 | return xgbe_common_read(buffer, count, ppos, value: pdata->debugfs_xi2c_reg); |
385 | } |
386 | |
387 | static ssize_t xi2c_reg_addr_write(struct file *filp, |
388 | const char __user *buffer, |
389 | size_t count, loff_t *ppos) |
390 | { |
391 | struct xgbe_prv_data *pdata = filp->private_data; |
392 | |
393 | return xgbe_common_write(buffer, count, ppos, |
394 | value: &pdata->debugfs_xi2c_reg); |
395 | } |
396 | |
397 | static ssize_t xi2c_reg_value_read(struct file *filp, char __user *buffer, |
398 | size_t count, loff_t *ppos) |
399 | { |
400 | struct xgbe_prv_data *pdata = filp->private_data; |
401 | unsigned int value; |
402 | |
403 | value = XI2C_IOREAD(pdata, pdata->debugfs_xi2c_reg); |
404 | |
405 | return xgbe_common_read(buffer, count, ppos, value); |
406 | } |
407 | |
408 | static ssize_t xi2c_reg_value_write(struct file *filp, |
409 | const char __user *buffer, |
410 | size_t count, loff_t *ppos) |
411 | { |
412 | struct xgbe_prv_data *pdata = filp->private_data; |
413 | unsigned int value; |
414 | ssize_t len; |
415 | |
416 | len = xgbe_common_write(buffer, count, ppos, value: &value); |
417 | if (len < 0) |
418 | return len; |
419 | |
420 | XI2C_IOWRITE(pdata, pdata->debugfs_xi2c_reg, value); |
421 | |
422 | return len; |
423 | } |
424 | |
425 | static const struct file_operations xi2c_reg_addr_fops = { |
426 | .owner = THIS_MODULE, |
427 | .open = simple_open, |
428 | .read = xi2c_reg_addr_read, |
429 | .write = xi2c_reg_addr_write, |
430 | }; |
431 | |
432 | static const struct file_operations xi2c_reg_value_fops = { |
433 | .owner = THIS_MODULE, |
434 | .open = simple_open, |
435 | .read = xi2c_reg_value_read, |
436 | .write = xi2c_reg_value_write, |
437 | }; |
438 | |
439 | void xgbe_debugfs_init(struct xgbe_prv_data *pdata) |
440 | { |
441 | char *buf; |
442 | |
443 | /* Set defaults */ |
444 | pdata->debugfs_xgmac_reg = 0; |
445 | pdata->debugfs_xpcs_mmd = 1; |
446 | pdata->debugfs_xpcs_reg = 0; |
447 | |
448 | buf = kasprintf(GFP_KERNEL, fmt: "amd-xgbe-%s" , pdata->netdev->name); |
449 | if (!buf) |
450 | return; |
451 | |
452 | pdata->xgbe_debugfs = debugfs_create_dir(name: buf, NULL); |
453 | |
454 | debugfs_create_file(name: "xgmac_register" , mode: 0600, parent: pdata->xgbe_debugfs, data: pdata, |
455 | fops: &xgmac_reg_addr_fops); |
456 | |
457 | debugfs_create_file(name: "xgmac_register_value" , mode: 0600, parent: pdata->xgbe_debugfs, |
458 | data: pdata, fops: &xgmac_reg_value_fops); |
459 | |
460 | debugfs_create_file(name: "xpcs_mmd" , mode: 0600, parent: pdata->xgbe_debugfs, data: pdata, |
461 | fops: &xpcs_mmd_fops); |
462 | |
463 | debugfs_create_file(name: "xpcs_register" , mode: 0600, parent: pdata->xgbe_debugfs, data: pdata, |
464 | fops: &xpcs_reg_addr_fops); |
465 | |
466 | debugfs_create_file(name: "xpcs_register_value" , mode: 0600, parent: pdata->xgbe_debugfs, |
467 | data: pdata, fops: &xpcs_reg_value_fops); |
468 | |
469 | if (pdata->xprop_regs) { |
470 | debugfs_create_file(name: "xprop_register" , mode: 0600, parent: pdata->xgbe_debugfs, |
471 | data: pdata, fops: &xprop_reg_addr_fops); |
472 | |
473 | debugfs_create_file(name: "xprop_register_value" , mode: 0600, |
474 | parent: pdata->xgbe_debugfs, data: pdata, |
475 | fops: &xprop_reg_value_fops); |
476 | } |
477 | |
478 | if (pdata->xi2c_regs) { |
479 | debugfs_create_file(name: "xi2c_register" , mode: 0600, parent: pdata->xgbe_debugfs, |
480 | data: pdata, fops: &xi2c_reg_addr_fops); |
481 | |
482 | debugfs_create_file(name: "xi2c_register_value" , mode: 0600, |
483 | parent: pdata->xgbe_debugfs, data: pdata, |
484 | fops: &xi2c_reg_value_fops); |
485 | } |
486 | |
487 | if (pdata->vdata->an_cdr_workaround) { |
488 | debugfs_create_bool(name: "an_cdr_workaround" , mode: 0600, |
489 | parent: pdata->xgbe_debugfs, |
490 | value: &pdata->debugfs_an_cdr_workaround); |
491 | |
492 | debugfs_create_bool(name: "an_cdr_track_early" , mode: 0600, |
493 | parent: pdata->xgbe_debugfs, |
494 | value: &pdata->debugfs_an_cdr_track_early); |
495 | } |
496 | |
497 | kfree(objp: buf); |
498 | } |
499 | |
500 | void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) |
501 | { |
502 | debugfs_remove_recursive(dentry: pdata->xgbe_debugfs); |
503 | pdata->xgbe_debugfs = NULL; |
504 | } |
505 | |
506 | void xgbe_debugfs_rename(struct xgbe_prv_data *pdata) |
507 | { |
508 | char *buf; |
509 | |
510 | if (!pdata->xgbe_debugfs) |
511 | return; |
512 | |
513 | buf = kasprintf(GFP_KERNEL, fmt: "amd-xgbe-%s" , pdata->netdev->name); |
514 | if (!buf) |
515 | return; |
516 | |
517 | if (!strcmp(pdata->xgbe_debugfs->d_name.name, buf)) |
518 | goto out; |
519 | |
520 | debugfs_rename(old_dir: pdata->xgbe_debugfs->d_parent, old_dentry: pdata->xgbe_debugfs, |
521 | new_dir: pdata->xgbe_debugfs->d_parent, new_name: buf); |
522 | |
523 | out: |
524 | kfree(objp: buf); |
525 | } |
526 | |