1 /* 2 * This file is provided under a dual BSD/GPLv2 license. When using or 3 * redistributing this file, you may do so under either license. 4 * 5 * GPL LICENSE SUMMARY 6 * 7 * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of version 2 of the GNU General Public License as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * BSD LICENSE 19 * 20 * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 21 * 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that the following conditions 24 * are met: 25 * 26 * * Redistributions of source code must retain the above copyright 27 * notice, this list of conditions and the following disclaimer. 28 * * Redistributions in binary form must reproduce the above copy 29 * notice, this list of conditions and the following disclaimer in 30 * the documentation and/or other materials provided with the 31 * distribution. 32 * * Neither the name of Intel Corporation nor the names of its 33 * contributors may be used to endorse or promote products derived 34 * from this software without specific prior written permission. 35 * 36 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 37 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 38 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 39 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 40 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 41 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 42 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 43 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 44 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 45 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 * 48 * PCIe NTB Debugging Tool Linux driver 49 * 50 * Contact Information: 51 * Allen Hubbe <Allen.Hubbe@emc.com> 52 */ 53 54 /* 55 * How to use this tool, by example. 56 * 57 * Assuming $DBG_DIR is something like: 58 * '/sys/kernel/debug/ntb_tool/0000:00:03.0' 59 * 60 * Eg: check if clearing the doorbell mask generates an interrupt. 61 * 62 * # Check the link status 63 * root@self# cat $DBG_DIR/link 64 * 65 * # Block until the link is up 66 * root@self# echo Y > $DBG_DIR/link_event 67 * 68 * # Set the doorbell mask 69 * root@self# echo 's 1' > $DBG_DIR/mask 70 * 71 * # Ring the doorbell from the peer 72 * root@peer# echo 's 1' > $DBG_DIR/peer_db 73 * 74 * # Clear the doorbell mask 75 * root@self# echo 'c 1' > $DBG_DIR/mask 76 * 77 * Observe debugging output in dmesg or your console. You should see a 78 * doorbell event triggered by clearing the mask. If not, this may indicate an 79 * issue with the hardware that needs to be worked around in the driver. 80 * 81 * Eg: read and write scratchpad registers 82 * 83 * root@peer# echo '0 0x01010101 1 0x7f7f7f7f' > $DBG_DIR/peer_spad 84 * 85 * root@self# cat $DBG_DIR/spad 86 * 87 * Observe that spad 0 and 1 have the values set by the peer. 88 * 89 * # Check the memory window translation info 90 * cat $DBG_DIR/peer_trans0 91 * 92 * # Setup a 16k memory window buffer 93 * echo 16384 > $DBG_DIR/peer_trans0 94 * 95 */ 96 97 #include <linux/init.h> 98 #include <linux/kernel.h> 99 #include <linux/module.h> 100 101 #include <linux/debugfs.h> 102 #include <linux/dma-mapping.h> 103 #include <linux/pci.h> 104 #include <linux/slab.h> 105 #include <linux/uaccess.h> 106 107 #include <linux/ntb.h> 108 109 #define DRIVER_NAME "ntb_tool" 110 #define DRIVER_DESCRIPTION "PCIe NTB Debugging Tool" 111 112 #define DRIVER_LICENSE "Dual BSD/GPL" 113 #define DRIVER_VERSION "1.0" 114 #define DRIVER_RELDATE "22 April 2015" 115 #define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>" 116 117 MODULE_LICENSE(DRIVER_LICENSE); 118 MODULE_VERSION(DRIVER_VERSION); 119 MODULE_AUTHOR(DRIVER_AUTHOR); 120 MODULE_DESCRIPTION(DRIVER_DESCRIPTION); 121 122 #define MAX_MWS 16 123 124 static struct dentry *tool_dbgfs; 125 126 struct tool_mw { 127 int idx; 128 struct tool_ctx *tc; 129 resource_size_t win_size; 130 resource_size_t size; 131 u8 __iomem *local; 132 u8 *peer; 133 dma_addr_t peer_dma; 134 struct dentry *peer_dbg_file; 135 }; 136 137 struct tool_ctx { 138 struct ntb_dev *ntb; 139 struct dentry *dbgfs; 140 wait_queue_head_t link_wq; 141 int mw_count; 142 struct tool_mw mws[MAX_MWS]; 143 }; 144 145 #define SPAD_FNAME_SIZE 0x10 146 #define INT_PTR(x) ((void *)(unsigned long)x) 147 #define PTR_INT(x) ((int)(unsigned long)x) 148 149 #define TOOL_FOPS_RDWR(__name, __read, __write) \ 150 const struct file_operations __name = { \ 151 .owner = THIS_MODULE, \ 152 .open = simple_open, \ 153 .read = __read, \ 154 .write = __write, \ 155 } 156 157 static void tool_link_event(void *ctx) 158 { 159 struct tool_ctx *tc = ctx; 160 enum ntb_speed speed; 161 enum ntb_width width; 162 int up; 163 164 up = ntb_link_is_up(tc->ntb, &speed, &width); 165 166 dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n", 167 up ? "up" : "down", speed, width); 168 169 wake_up(&tc->link_wq); 170 } 171 172 static void tool_db_event(void *ctx, int vec) 173 { 174 struct tool_ctx *tc = ctx; 175 u64 db_bits, db_mask; 176 177 db_mask = ntb_db_vector_mask(tc->ntb, vec); 178 db_bits = ntb_db_read(tc->ntb); 179 180 dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n", 181 vec, db_mask, db_bits); 182 } 183 184 static const struct ntb_ctx_ops tool_ops = { 185 .link_event = tool_link_event, 186 .db_event = tool_db_event, 187 }; 188 189 static ssize_t tool_dbfn_read(struct tool_ctx *tc, char __user *ubuf, 190 size_t size, loff_t *offp, 191 u64 (*db_read_fn)(struct ntb_dev *)) 192 { 193 size_t buf_size; 194 char *buf; 195 ssize_t pos, rc; 196 197 if (!db_read_fn) 198 return -EINVAL; 199 200 buf_size = min_t(size_t, size, 0x20); 201 202 buf = kmalloc(buf_size, GFP_KERNEL); 203 if (!buf) 204 return -ENOMEM; 205 206 pos = scnprintf(buf, buf_size, "%#llx\n", 207 db_read_fn(tc->ntb)); 208 209 rc = simple_read_from_buffer(ubuf, size, offp, buf, pos); 210 211 kfree(buf); 212 213 return rc; 214 } 215 216 static ssize_t tool_dbfn_write(struct tool_ctx *tc, 217 const char __user *ubuf, 218 size_t size, loff_t *offp, 219 int (*db_set_fn)(struct ntb_dev *, u64), 220 int (*db_clear_fn)(struct ntb_dev *, u64)) 221 { 222 u64 db_bits; 223 char *buf, cmd; 224 ssize_t rc; 225 int n; 226 227 buf = kmalloc(size + 1, GFP_KERNEL); 228 if (!buf) 229 return -ENOMEM; 230 231 rc = simple_write_to_buffer(buf, size, offp, ubuf, size); 232 if (rc < 0) { 233 kfree(buf); 234 return rc; 235 } 236 237 buf[size] = 0; 238 239 n = sscanf(buf, "%c %lli", &cmd, &db_bits); 240 241 kfree(buf); 242 243 if (n != 2) { 244 rc = -EINVAL; 245 } else if (cmd == 's') { 246 if (!db_set_fn) 247 rc = -EINVAL; 248 else 249 rc = db_set_fn(tc->ntb, db_bits); 250 } else if (cmd == 'c') { 251 if (!db_clear_fn) 252 rc = -EINVAL; 253 else 254 rc = db_clear_fn(tc->ntb, db_bits); 255 } else { 256 rc = -EINVAL; 257 } 258 259 return rc ? : size; 260 } 261 262 static ssize_t tool_spadfn_read(struct tool_ctx *tc, char __user *ubuf, 263 size_t size, loff_t *offp, 264 u32 (*spad_read_fn)(struct ntb_dev *, int)) 265 { 266 size_t buf_size; 267 char *buf; 268 ssize_t pos, rc; 269 int i, spad_count; 270 271 if (!spad_read_fn) 272 return -EINVAL; 273 274 spad_count = ntb_spad_count(tc->ntb); 275 276 /* 277 * We multiply the number of spads by 15 to get the buffer size 278 * this is from 3 for the %d, 10 for the largest hex value 279 * (0x00000000) and 2 for the tab and line feed. 280 */ 281 buf_size = min_t(size_t, size, spad_count * 15); 282 283 buf = kmalloc(buf_size, GFP_KERNEL); 284 if (!buf) 285 return -ENOMEM; 286 287 pos = 0; 288 289 for (i = 0; i < spad_count; ++i) { 290 pos += scnprintf(buf + pos, buf_size - pos, "%d\t%#x\n", 291 i, spad_read_fn(tc->ntb, i)); 292 } 293 294 rc = simple_read_from_buffer(ubuf, size, offp, buf, pos); 295 296 kfree(buf); 297 298 return rc; 299 } 300 301 static ssize_t tool_spadfn_write(struct tool_ctx *tc, 302 const char __user *ubuf, 303 size_t size, loff_t *offp, 304 int (*spad_write_fn)(struct ntb_dev *, 305 int, u32)) 306 { 307 int spad_idx; 308 u32 spad_val; 309 char *buf, *buf_ptr; 310 int pos, n; 311 ssize_t rc; 312 313 if (!spad_write_fn) { 314 dev_dbg(&tc->ntb->dev, "no spad write fn\n"); 315 return -EINVAL; 316 } 317 318 buf = kmalloc(size + 1, GFP_KERNEL); 319 if (!buf) 320 return -ENOMEM; 321 322 rc = simple_write_to_buffer(buf, size, offp, ubuf, size); 323 if (rc < 0) { 324 kfree(buf); 325 return rc; 326 } 327 328 buf[size] = 0; 329 buf_ptr = buf; 330 n = sscanf(buf_ptr, "%d %i%n", &spad_idx, &spad_val, &pos); 331 while (n == 2) { 332 buf_ptr += pos; 333 rc = spad_write_fn(tc->ntb, spad_idx, spad_val); 334 if (rc) 335 break; 336 337 n = sscanf(buf_ptr, "%d %i%n", &spad_idx, &spad_val, &pos); 338 } 339 340 if (n < 0) 341 rc = n; 342 343 kfree(buf); 344 345 return rc ? : size; 346 } 347 348 static ssize_t tool_db_read(struct file *filep, char __user *ubuf, 349 size_t size, loff_t *offp) 350 { 351 struct tool_ctx *tc = filep->private_data; 352 353 return tool_dbfn_read(tc, ubuf, size, offp, 354 tc->ntb->ops->db_read); 355 } 356 357 static ssize_t tool_db_write(struct file *filep, const char __user *ubuf, 358 size_t size, loff_t *offp) 359 { 360 struct tool_ctx *tc = filep->private_data; 361 362 return tool_dbfn_write(tc, ubuf, size, offp, 363 tc->ntb->ops->db_set, 364 tc->ntb->ops->db_clear); 365 } 366 367 static TOOL_FOPS_RDWR(tool_db_fops, 368 tool_db_read, 369 tool_db_write); 370 371 static ssize_t tool_mask_read(struct file *filep, char __user *ubuf, 372 size_t size, loff_t *offp) 373 { 374 struct tool_ctx *tc = filep->private_data; 375 376 return tool_dbfn_read(tc, ubuf, size, offp, 377 tc->ntb->ops->db_read_mask); 378 } 379 380 static ssize_t tool_mask_write(struct file *filep, const char __user *ubuf, 381 size_t size, loff_t *offp) 382 { 383 struct tool_ctx *tc = filep->private_data; 384 385 return tool_dbfn_write(tc, ubuf, size, offp, 386 tc->ntb->ops->db_set_mask, 387 tc->ntb->ops->db_clear_mask); 388 } 389 390 static TOOL_FOPS_RDWR(tool_mask_fops, 391 tool_mask_read, 392 tool_mask_write); 393 394 static ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf, 395 size_t size, loff_t *offp) 396 { 397 struct tool_ctx *tc = filep->private_data; 398 399 return tool_dbfn_read(tc, ubuf, size, offp, 400 tc->ntb->ops->peer_db_read); 401 } 402 403 static ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf, 404 size_t size, loff_t *offp) 405 { 406 struct tool_ctx *tc = filep->private_data; 407 408 return tool_dbfn_write(tc, ubuf, size, offp, 409 tc->ntb->ops->peer_db_set, 410 tc->ntb->ops->peer_db_clear); 411 } 412 413 static TOOL_FOPS_RDWR(tool_peer_db_fops, 414 tool_peer_db_read, 415 tool_peer_db_write); 416 417 static ssize_t tool_peer_mask_read(struct file *filep, char __user *ubuf, 418 size_t size, loff_t *offp) 419 { 420 struct tool_ctx *tc = filep->private_data; 421 422 return tool_dbfn_read(tc, ubuf, size, offp, 423 tc->ntb->ops->peer_db_read_mask); 424 } 425 426 static ssize_t tool_peer_mask_write(struct file *filep, const char __user *ubuf, 427 size_t size, loff_t *offp) 428 { 429 struct tool_ctx *tc = filep->private_data; 430 431 return tool_dbfn_write(tc, ubuf, size, offp, 432 tc->ntb->ops->peer_db_set_mask, 433 tc->ntb->ops->peer_db_clear_mask); 434 } 435 436 static TOOL_FOPS_RDWR(tool_peer_mask_fops, 437 tool_peer_mask_read, 438 tool_peer_mask_write); 439 440 static ssize_t tool_spad_read(struct file *filep, char __user *ubuf, 441 size_t size, loff_t *offp) 442 { 443 struct tool_ctx *tc = filep->private_data; 444 445 return tool_spadfn_read(tc, ubuf, size, offp, 446 tc->ntb->ops->spad_read); 447 } 448 449 static ssize_t tool_spad_write(struct file *filep, const char __user *ubuf, 450 size_t size, loff_t *offp) 451 { 452 struct tool_ctx *tc = filep->private_data; 453 454 return tool_spadfn_write(tc, ubuf, size, offp, 455 tc->ntb->ops->spad_write); 456 } 457 458 static TOOL_FOPS_RDWR(tool_spad_fops, 459 tool_spad_read, 460 tool_spad_write); 461 462 static ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf, 463 size_t size, loff_t *offp) 464 { 465 struct tool_ctx *tc = filep->private_data; 466 467 return tool_spadfn_read(tc, ubuf, size, offp, 468 tc->ntb->ops->peer_spad_read); 469 } 470 471 static ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf, 472 size_t size, loff_t *offp) 473 { 474 struct tool_ctx *tc = filep->private_data; 475 476 return tool_spadfn_write(tc, ubuf, size, offp, 477 tc->ntb->ops->peer_spad_write); 478 } 479 480 static TOOL_FOPS_RDWR(tool_peer_spad_fops, 481 tool_peer_spad_read, 482 tool_peer_spad_write); 483 484 static ssize_t tool_link_read(struct file *filep, char __user *ubuf, 485 size_t size, loff_t *offp) 486 { 487 struct tool_ctx *tc = filep->private_data; 488 char buf[3]; 489 490 buf[0] = ntb_link_is_up(tc->ntb, NULL, NULL) ? 'Y' : 'N'; 491 buf[1] = '\n'; 492 buf[2] = '\0'; 493 494 return simple_read_from_buffer(ubuf, size, offp, buf, 2); 495 } 496 497 static ssize_t tool_link_write(struct file *filep, const char __user *ubuf, 498 size_t size, loff_t *offp) 499 { 500 struct tool_ctx *tc = filep->private_data; 501 char buf[32]; 502 size_t buf_size; 503 bool val; 504 int rc; 505 506 buf_size = min(size, (sizeof(buf) - 1)); 507 if (copy_from_user(buf, ubuf, buf_size)) 508 return -EFAULT; 509 510 buf[buf_size] = '\0'; 511 512 rc = strtobool(buf, &val); 513 if (rc) 514 return rc; 515 516 if (val) 517 rc = ntb_link_enable(tc->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); 518 else 519 rc = ntb_link_disable(tc->ntb); 520 521 if (rc) 522 return rc; 523 524 return size; 525 } 526 527 static TOOL_FOPS_RDWR(tool_link_fops, 528 tool_link_read, 529 tool_link_write); 530 531 static ssize_t tool_link_event_write(struct file *filep, 532 const char __user *ubuf, 533 size_t size, loff_t *offp) 534 { 535 struct tool_ctx *tc = filep->private_data; 536 char buf[32]; 537 size_t buf_size; 538 bool val; 539 int rc; 540 541 buf_size = min(size, (sizeof(buf) - 1)); 542 if (copy_from_user(buf, ubuf, buf_size)) 543 return -EFAULT; 544 545 buf[buf_size] = '\0'; 546 547 rc = strtobool(buf, &val); 548 if (rc) 549 return rc; 550 551 if (wait_event_interruptible(tc->link_wq, 552 ntb_link_is_up(tc->ntb, NULL, NULL) == val)) 553 return -ERESTART; 554 555 return size; 556 } 557 558 static TOOL_FOPS_RDWR(tool_link_event_fops, 559 NULL, 560 tool_link_event_write); 561 562 static ssize_t tool_mw_read(struct file *filep, char __user *ubuf, 563 size_t size, loff_t *offp) 564 { 565 struct tool_mw *mw = filep->private_data; 566 ssize_t rc; 567 loff_t pos = *offp; 568 void *buf; 569 570 if (mw->local == NULL) 571 return -EIO; 572 if (pos < 0) 573 return -EINVAL; 574 if (pos >= mw->win_size || !size) 575 return 0; 576 if (size > mw->win_size - pos) 577 size = mw->win_size - pos; 578 579 buf = kmalloc(size, GFP_KERNEL); 580 if (!buf) 581 return -ENOMEM; 582 583 memcpy_fromio(buf, mw->local + pos, size); 584 rc = copy_to_user(ubuf, buf, size); 585 if (rc == size) { 586 rc = -EFAULT; 587 goto err_free; 588 } 589 590 size -= rc; 591 *offp = pos + size; 592 rc = size; 593 594 err_free: 595 kfree(buf); 596 597 return rc; 598 } 599 600 static ssize_t tool_mw_write(struct file *filep, const char __user *ubuf, 601 size_t size, loff_t *offp) 602 { 603 struct tool_mw *mw = filep->private_data; 604 ssize_t rc; 605 loff_t pos = *offp; 606 void *buf; 607 608 if (pos < 0) 609 return -EINVAL; 610 if (pos >= mw->win_size || !size) 611 return 0; 612 if (size > mw->win_size - pos) 613 size = mw->win_size - pos; 614 615 buf = kmalloc(size, GFP_KERNEL); 616 if (!buf) 617 return -ENOMEM; 618 619 rc = copy_from_user(buf, ubuf, size); 620 if (rc == size) { 621 rc = -EFAULT; 622 goto err_free; 623 } 624 625 size -= rc; 626 *offp = pos + size; 627 rc = size; 628 629 memcpy_toio(mw->local + pos, buf, size); 630 631 err_free: 632 kfree(buf); 633 634 return rc; 635 } 636 637 static TOOL_FOPS_RDWR(tool_mw_fops, 638 tool_mw_read, 639 tool_mw_write); 640 641 static ssize_t tool_peer_mw_read(struct file *filep, char __user *ubuf, 642 size_t size, loff_t *offp) 643 { 644 struct tool_mw *mw = filep->private_data; 645 646 if (!mw->peer) 647 return -ENXIO; 648 649 return simple_read_from_buffer(ubuf, size, offp, mw->peer, mw->size); 650 } 651 652 static ssize_t tool_peer_mw_write(struct file *filep, const char __user *ubuf, 653 size_t size, loff_t *offp) 654 { 655 struct tool_mw *mw = filep->private_data; 656 657 if (!mw->peer) 658 return -ENXIO; 659 660 return simple_write_to_buffer(mw->peer, mw->size, offp, ubuf, size); 661 } 662 663 static TOOL_FOPS_RDWR(tool_peer_mw_fops, 664 tool_peer_mw_read, 665 tool_peer_mw_write); 666 667 static int tool_setup_mw(struct tool_ctx *tc, int idx, size_t req_size) 668 { 669 int rc; 670 struct tool_mw *mw = &tc->mws[idx]; 671 phys_addr_t base; 672 resource_size_t size, align, align_size; 673 char buf[16]; 674 675 if (mw->peer) 676 return 0; 677 678 rc = ntb_mw_get_range(tc->ntb, idx, &base, &size, &align, 679 &align_size); 680 if (rc) 681 return rc; 682 683 mw->size = min_t(resource_size_t, req_size, size); 684 mw->size = round_up(mw->size, align); 685 mw->size = round_up(mw->size, align_size); 686 mw->peer = dma_alloc_coherent(&tc->ntb->pdev->dev, mw->size, 687 &mw->peer_dma, GFP_KERNEL); 688 689 if (!mw->peer) 690 return -ENOMEM; 691 692 rc = ntb_mw_set_trans(tc->ntb, idx, mw->peer_dma, mw->size); 693 if (rc) 694 goto err_free_dma; 695 696 snprintf(buf, sizeof(buf), "peer_mw%d", idx); 697 mw->peer_dbg_file = debugfs_create_file(buf, S_IRUSR | S_IWUSR, 698 mw->tc->dbgfs, mw, 699 &tool_peer_mw_fops); 700 701 return 0; 702 703 err_free_dma: 704 dma_free_coherent(&tc->ntb->pdev->dev, mw->size, 705 mw->peer, 706 mw->peer_dma); 707 mw->peer = NULL; 708 mw->peer_dma = 0; 709 mw->size = 0; 710 711 return rc; 712 } 713 714 static void tool_free_mw(struct tool_ctx *tc, int idx) 715 { 716 struct tool_mw *mw = &tc->mws[idx]; 717 718 if (mw->peer) { 719 ntb_mw_clear_trans(tc->ntb, idx); 720 dma_free_coherent(&tc->ntb->pdev->dev, mw->size, 721 mw->peer, 722 mw->peer_dma); 723 } 724 725 mw->peer = NULL; 726 mw->peer_dma = 0; 727 728 debugfs_remove(mw->peer_dbg_file); 729 730 mw->peer_dbg_file = NULL; 731 } 732 733 static ssize_t tool_peer_mw_trans_read(struct file *filep, 734 char __user *ubuf, 735 size_t size, loff_t *offp) 736 { 737 struct tool_mw *mw = filep->private_data; 738 739 char *buf; 740 size_t buf_size; 741 ssize_t ret, off = 0; 742 743 phys_addr_t base; 744 resource_size_t mw_size; 745 resource_size_t align; 746 resource_size_t align_size; 747 748 buf_size = min_t(size_t, size, 512); 749 750 buf = kmalloc(buf_size, GFP_KERNEL); 751 if (!buf) 752 return -ENOMEM; 753 754 ntb_mw_get_range(mw->tc->ntb, mw->idx, 755 &base, &mw_size, &align, &align_size); 756 757 off += scnprintf(buf + off, buf_size - off, 758 "Peer MW %d Information:\n", mw->idx); 759 760 off += scnprintf(buf + off, buf_size - off, 761 "Physical Address \t%pa[p]\n", 762 &base); 763 764 off += scnprintf(buf + off, buf_size - off, 765 "Window Size \t%lld\n", 766 (unsigned long long)mw_size); 767 768 off += scnprintf(buf + off, buf_size - off, 769 "Alignment \t%lld\n", 770 (unsigned long long)align); 771 772 off += scnprintf(buf + off, buf_size - off, 773 "Size Alignment \t%lld\n", 774 (unsigned long long)align_size); 775 776 off += scnprintf(buf + off, buf_size - off, 777 "Ready \t%c\n", 778 (mw->peer) ? 'Y' : 'N'); 779 780 off += scnprintf(buf + off, buf_size - off, 781 "Allocated Size \t%zd\n", 782 (mw->peer) ? (size_t)mw->size : 0); 783 784 ret = simple_read_from_buffer(ubuf, size, offp, buf, off); 785 kfree(buf); 786 return ret; 787 } 788 789 static ssize_t tool_peer_mw_trans_write(struct file *filep, 790 const char __user *ubuf, 791 size_t size, loff_t *offp) 792 { 793 struct tool_mw *mw = filep->private_data; 794 795 char buf[32]; 796 size_t buf_size; 797 unsigned long long val; 798 int rc; 799 800 buf_size = min(size, (sizeof(buf) - 1)); 801 if (copy_from_user(buf, ubuf, buf_size)) 802 return -EFAULT; 803 804 buf[buf_size] = '\0'; 805 806 rc = kstrtoull(buf, 0, &val); 807 if (rc) 808 return rc; 809 810 tool_free_mw(mw->tc, mw->idx); 811 if (val) 812 rc = tool_setup_mw(mw->tc, mw->idx, val); 813 814 if (rc) 815 return rc; 816 817 return size; 818 } 819 820 static TOOL_FOPS_RDWR(tool_peer_mw_trans_fops, 821 tool_peer_mw_trans_read, 822 tool_peer_mw_trans_write); 823 824 static int tool_init_mw(struct tool_ctx *tc, int idx) 825 { 826 struct tool_mw *mw = &tc->mws[idx]; 827 phys_addr_t base; 828 int rc; 829 830 rc = ntb_mw_get_range(tc->ntb, idx, &base, &mw->win_size, 831 NULL, NULL); 832 if (rc) 833 return rc; 834 835 mw->tc = tc; 836 mw->idx = idx; 837 mw->local = ioremap_wc(base, mw->win_size); 838 if (!mw->local) 839 return -EFAULT; 840 841 return 0; 842 } 843 844 static void tool_free_mws(struct tool_ctx *tc) 845 { 846 int i; 847 848 for (i = 0; i < tc->mw_count; i++) { 849 tool_free_mw(tc, i); 850 851 if (tc->mws[i].local) 852 iounmap(tc->mws[i].local); 853 854 tc->mws[i].local = NULL; 855 } 856 } 857 858 static void tool_setup_dbgfs(struct tool_ctx *tc) 859 { 860 int i; 861 862 /* This modules is useless without dbgfs... */ 863 if (!tool_dbgfs) { 864 tc->dbgfs = NULL; 865 return; 866 } 867 868 tc->dbgfs = debugfs_create_dir(dev_name(&tc->ntb->dev), 869 tool_dbgfs); 870 if (!tc->dbgfs) 871 return; 872 873 debugfs_create_file("db", S_IRUSR | S_IWUSR, tc->dbgfs, 874 tc, &tool_db_fops); 875 876 debugfs_create_file("mask", S_IRUSR | S_IWUSR, tc->dbgfs, 877 tc, &tool_mask_fops); 878 879 debugfs_create_file("peer_db", S_IRUSR | S_IWUSR, tc->dbgfs, 880 tc, &tool_peer_db_fops); 881 882 debugfs_create_file("peer_mask", S_IRUSR | S_IWUSR, tc->dbgfs, 883 tc, &tool_peer_mask_fops); 884 885 debugfs_create_file("spad", S_IRUSR | S_IWUSR, tc->dbgfs, 886 tc, &tool_spad_fops); 887 888 debugfs_create_file("peer_spad", S_IRUSR | S_IWUSR, tc->dbgfs, 889 tc, &tool_peer_spad_fops); 890 891 debugfs_create_file("link", S_IRUSR | S_IWUSR, tc->dbgfs, 892 tc, &tool_link_fops); 893 894 debugfs_create_file("link_event", S_IWUSR, tc->dbgfs, 895 tc, &tool_link_event_fops); 896 897 for (i = 0; i < tc->mw_count; i++) { 898 char buf[30]; 899 900 snprintf(buf, sizeof(buf), "mw%d", i); 901 debugfs_create_file(buf, S_IRUSR | S_IWUSR, tc->dbgfs, 902 &tc->mws[i], &tool_mw_fops); 903 904 snprintf(buf, sizeof(buf), "peer_trans%d", i); 905 debugfs_create_file(buf, S_IRUSR | S_IWUSR, tc->dbgfs, 906 &tc->mws[i], &tool_peer_mw_trans_fops); 907 } 908 } 909 910 static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) 911 { 912 struct tool_ctx *tc; 913 int rc; 914 int i; 915 916 if (ntb_db_is_unsafe(ntb)) 917 dev_dbg(&ntb->dev, "doorbell is unsafe\n"); 918 919 if (ntb_spad_is_unsafe(ntb)) 920 dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); 921 922 tc = kzalloc(sizeof(*tc), GFP_KERNEL); 923 if (!tc) { 924 rc = -ENOMEM; 925 goto err_tc; 926 } 927 928 tc->ntb = ntb; 929 init_waitqueue_head(&tc->link_wq); 930 931 tc->mw_count = min(ntb_mw_count(tc->ntb), MAX_MWS); 932 for (i = 0; i < tc->mw_count; i++) { 933 rc = tool_init_mw(tc, i); 934 if (rc) 935 goto err_ctx; 936 } 937 938 tool_setup_dbgfs(tc); 939 940 rc = ntb_set_ctx(ntb, tc, &tool_ops); 941 if (rc) 942 goto err_ctx; 943 944 ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); 945 ntb_link_event(ntb); 946 947 return 0; 948 949 err_ctx: 950 tool_free_mws(tc); 951 debugfs_remove_recursive(tc->dbgfs); 952 kfree(tc); 953 err_tc: 954 return rc; 955 } 956 957 static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb) 958 { 959 struct tool_ctx *tc = ntb->ctx; 960 961 tool_free_mws(tc); 962 963 ntb_clear_ctx(ntb); 964 ntb_link_disable(ntb); 965 966 debugfs_remove_recursive(tc->dbgfs); 967 kfree(tc); 968 } 969 970 static struct ntb_client tool_client = { 971 .ops = { 972 .probe = tool_probe, 973 .remove = tool_remove, 974 }, 975 }; 976 977 static int __init tool_init(void) 978 { 979 int rc; 980 981 if (debugfs_initialized()) 982 tool_dbgfs = debugfs_create_dir(KBUILD_MODNAME, NULL); 983 984 rc = ntb_register_client(&tool_client); 985 if (rc) 986 goto err_client; 987 988 return 0; 989 990 err_client: 991 debugfs_remove_recursive(tool_dbgfs); 992 return rc; 993 } 994 module_init(tool_init); 995 996 static void __exit tool_exit(void) 997 { 998 ntb_unregister_client(&tool_client); 999 debugfs_remove_recursive(tool_dbgfs); 1000 } 1001 module_exit(tool_exit); 1002