| 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 | * Copyright (C) 2017 T-Platforms All Rights Reserved.
|
| 9 | *
|
| 10 | * This program is free software; you can redistribute it and/or modify
|
| 11 | * it under the terms of version 2 of the GNU General Public License as
|
| 12 | * published by the Free Software Foundation.
|
| 13 | *
|
| 14 | * This program is distributed in the hope that it will be useful, but
|
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
| 17 | * General Public License for more details.
|
| 18 | *
|
| 19 | * BSD LICENSE
|
| 20 | *
|
| 21 | * Copyright (C) 2015 EMC Corporation. All Rights Reserved.
|
| 22 | * Copyright (C) 2017 T-Platforms All Rights Reserved.
|
| 23 | *
|
| 24 | * Redistribution and use in source and binary forms, with or without
|
| 25 | * modification, are permitted provided that the following conditions
|
| 26 | * are met:
|
| 27 | *
|
| 28 | * * Redistributions of source code must retain the above copyright
|
| 29 | * notice, this list of conditions and the following disclaimer.
|
| 30 | * * Redistributions in binary form must reproduce the above copy
|
| 31 | * notice, this list of conditions and the following disclaimer in
|
| 32 | * the documentation and/or other materials provided with the
|
| 33 | * distribution.
|
| 34 | * * Neither the name of Intel Corporation nor the names of its
|
| 35 | * contributors may be used to endorse or promote products derived
|
| 36 | * from this software without specific prior written permission.
|
| 37 | *
|
| 38 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 39 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 40 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 41 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 42 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 43 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| 44 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 45 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 46 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 47 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| 48 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 49 | *
|
| 50 | * PCIe NTB Debugging Tool Linux driver
|
| 51 |
|
| 52 |
|
| 53 | |
| 54 | * How to use this tool, by example.
|
| 55 | *
|
| 56 | * Assuming $DBG_DIR is something like:
|
| 57 | * '/sys/kernel/debug/ntb_tool/0000:00:03.0'
|
| 58 | * Suppose aside from local device there is at least one remote device
|
| 59 | * connected to NTB with index 0.
|
| 60 | *-----------------------------------------------------------------------------
|
| 61 | * Eg: check local/peer device information.
|
| 62 | *
|
| 63 | * # Get local device port number
|
| 64 | * root@self# cat $DBG_DIR/port
|
| 65 | *
|
| 66 | * # Check local device functionality
|
| 67 | * root@self# ls $DBG_DIR
|
| 68 | * db msg1 msg_sts peer4/ port
|
| 69 | * db_event msg2 peer0/ peer5/ spad0
|
| 70 | * db_mask msg3 peer1/ peer_db spad1
|
| 71 | * link msg_event peer2/ peer_db_mask spad2
|
| 72 | * msg0 msg_mask peer3/ peer_spad spad3
|
| 73 | * # As one can see it supports:
|
| 74 | * # 1) four inbound message registers
|
| 75 | * # 2) four inbound scratchpads
|
| 76 | * # 3) up to six peer devices
|
| 77 | *
|
| 78 | * # Check peer device port number
|
| 79 | * root@self# cat $DBG_DIR/peer0/port
|
| 80 | *
|
| 81 | * # Check peer device(s) functionality to be used
|
| 82 | * root@self# ls $DBG_DIR/peer0
|
| 83 | * link mw_trans0 mw_trans6 port
|
| 84 | * link_event mw_trans1 mw_trans7 spad0
|
| 85 | * msg0 mw_trans2 peer_mw_trans0 spad1
|
| 86 | * msg1 mw_trans3 peer_mw_trans1 spad2
|
| 87 | * msg2 mw_trans4 peer_mw_trans2 spad3
|
| 88 | * msg3 mw_trans5 peer_mw_trans3
|
| 89 | * # As one can see we got:
|
| 90 | * # 1) four outbound message registers
|
| 91 | * # 2) four outbound scratchpads
|
| 92 | * # 3) eight inbound memory windows
|
| 93 | * # 4) four outbound memory windows
|
| 94 | *-----------------------------------------------------------------------------
|
| 95 | * Eg: NTB link tests
|
| 96 | *
|
| 97 | * # Set local link up/down
|
| 98 | * root@self# echo Y > $DBG_DIR/link
|
| 99 | * root@self# echo N > $DBG_DIR/link
|
| 100 | *
|
| 101 | * # Check if link with peer device is up/down:
|
| 102 | * root@self# cat $DBG_DIR/peer0/link
|
| 103 | *
|
| 104 | * # Block until the link is up/down
|
| 105 | * root@self# echo Y > $DBG_DIR/peer0/link_event
|
| 106 | * root@self# echo N > $DBG_DIR/peer0/link_event
|
| 107 | *-----------------------------------------------------------------------------
|
| 108 | * Eg: Doorbell registers tests (some functionality might be absent)
|
| 109 | *
|
| 110 | * # Set/clear/get local doorbell
|
| 111 | * root@self# echo 's 1' > $DBG_DIR/db
|
| 112 | * root@self# echo 'c 1' > $DBG_DIR/db
|
| 113 | * root@self# cat $DBG_DIR/db
|
| 114 | *
|
| 115 | * # Set/clear/get local doorbell mask
|
| 116 | * root@self# echo 's 1' > $DBG_DIR/db_mask
|
| 117 | * root@self# echo 'c 1' > $DBG_DIR/db_mask
|
| 118 | * root@self# cat $DBG_DIR/db_mask
|
| 119 | *
|
| 120 | * # Ring/clear/get peer doorbell
|
| 121 | * root@peer# echo 's 1' > $DBG_DIR/peer_db
|
| 122 | * root@peer# echo 'c 1' > $DBG_DIR/peer_db
|
| 123 | * root@peer# cat $DBG_DIR/peer_db
|
| 124 | *
|
| 125 | * # Set/clear/get peer doorbell mask
|
| 126 | * root@self# echo 's 1' > $DBG_DIR/peer_db_mask
|
| 127 | * root@self# echo 'c 1' > $DBG_DIR/peer_db_mask
|
| 128 | * root@self# cat $DBG_DIR/peer_db_mask
|
| 129 | *
|
| 130 | * # Block until local doorbell is set with specified value
|
| 131 | * root@self# echo 1 > $DBG_DIR/db_event
|
| 132 | *-----------------------------------------------------------------------------
|
| 133 | * Eg: Message registers tests (functionality might be absent)
|
| 134 | *
|
| 135 | * # Set/clear/get in/out message registers status
|
| 136 | * root@self# echo 's 1' > $DBG_DIR/msg_sts
|
| 137 | * root@self# echo 'c 1' > $DBG_DIR/msg_sts
|
| 138 | * root@self# cat $DBG_DIR/msg_sts
|
| 139 | *
|
| 140 | * # Set/clear in/out message registers mask
|
| 141 | * root@self# echo 's 1' > $DBG_DIR/msg_mask
|
| 142 | * root@self# echo 'c 1' > $DBG_DIR/msg_mask
|
| 143 | *
|
| 144 | * # Get inbound message register #0 value and source of port index
|
| 145 | * root@self# cat $DBG_DIR/msg0
|
| 146 | *
|
| 147 | * # Send some data to peer over outbound message register #0
|
| 148 | * root@self# echo 0x01020304 > $DBG_DIR/peer0/msg0
|
| 149 | *-----------------------------------------------------------------------------
|
| 150 | * Eg: Scratchpad registers tests (functionality might be absent)
|
| 151 | *
|
| 152 | * # Write/read to/from local scratchpad register #0
|
| 153 | * root@peer# echo 0x01020304 > $DBG_DIR/spad0
|
| 154 | * root@peer# cat $DBG_DIR/spad0
|
| 155 | *
|
| 156 | * # Write/read to/from peer scratchpad register #0
|
| 157 | * root@peer# echo 0x01020304 > $DBG_DIR/peer0/spad0
|
| 158 | * root@peer# cat $DBG_DIR/peer0/spad0
|
| 159 | *-----------------------------------------------------------------------------
|
| 160 | * Eg: Memory windows tests
|
| 161 | *
|
| 162 | * # Create inbound memory window buffer of specified size/get its base address
|
| 163 | * root@peer# echo 16384 > $DBG_DIR/peer0/mw_trans0
|
| 164 | * root@peer# cat $DBG_DIR/peer0/mw_trans0
|
| 165 | *
|
| 166 | * # Write/read data to/from inbound memory window
|
| 167 | * root@peer# echo Hello > $DBG_DIR/peer0/mw0
|
| 168 | * root@peer# head -c 7 $DBG_DIR/peer0/mw0
|
| 169 | *
|
| 170 | * # Map outbound memory window/check it settings (on peer device)
|
| 171 | * root@peer# echo 0xADD0BA5E:16384 > $DBG_DIR/peer0/peer_mw_trans0
|
| 172 | * root@peer# cat $DBG_DIR/peer0/peer_mw_trans0
|
| 173 | *
|
| 174 | * # Write/read data to/from outbound memory window (on peer device)
|
| 175 | * root@peer# echo olleH > $DBG_DIR/peer0/peer_mw0
|
| 176 | * root@peer# head -c 7 $DBG_DIR/peer0/peer_mw0
|
| 177 |
|
| 178 |
|
| 179 | #include <linux/init.h>
|
| 180 | #include <linux/kernel.h>
|
| 181 | #include <linux/module.h>
|
| 182 |
|
| 183 | #include <linux/debugfs.h>
|
| 184 | #include <linux/dma-mapping.h>
|
| 185 | #include <linux/pci.h>
|
| 186 | #include <linux/slab.h>
|
| 187 | #include <linux/uaccess.h>
|
| 188 |
|
| 189 | #include <linux/ntb.h>
|
| 190 |
|
| 191 | #define DRIVER_NAME "ntb_tool"
|
| 192 | #define DRIVER_VERSION "2.0"
|
| 193 |
|
| 194 | MODULE_LICENSE("Dual BSD/GPL");
|
| 195 | MODULE_VERSION(DRIVER_VERSION);
|
| 196 | MODULE_AUTHOR("Allen Hubbe <[email protected]>");
|
| 197 | MODULE_DESCRIPTION("PCIe NTB Debugging Tool");
|
| 198 |
|
| 199 | |
| 200 | * Inbound and outbound memory windows descriptor. Union members selection
|
| 201 | * depends on the MW type the structure describes. mm_base/dma_base are the
|
| 202 | * virtual and DMA address of an inbound MW. io_base/tr_base are the MMIO
|
| 203 | * mapped virtual and xlat addresses of an outbound MW respectively.
|
| 204 |
|
| 205 | struct tool_mw {
|
| 206 | int widx;
|
| 207 | int pidx;
|
| 208 | struct tool_ctx *tc;
|
| 209 | union {
|
| 210 | u8 *mm_base;
|
| 211 | u8 __iomem *io_base;
|
| 212 | };
|
| 213 | union {
|
| 214 | dma_addr_t dma_base;
|
| 215 | u64 tr_base;
|
| 216 | };
|
| 217 | resource_size_t size;
|
| 218 | struct dentry *dbgfs_file;
|
| 219 | };
|
| 220 |
|
| 221 | |
| 222 | * Wrapper structure is used to distinguish the outbound MW peers reference
|
| 223 | * within the corresponding DebugFS directory IO operation.
|
| 224 |
|
| 225 | struct tool_mw_wrap {
|
| 226 | int pidx;
|
| 227 | struct tool_mw *mw;
|
| 228 | };
|
| 229 |
|
| 230 | struct tool_msg {
|
| 231 | int midx;
|
| 232 | int pidx;
|
| 233 | struct tool_ctx *tc;
|
| 234 | };
|
| 235 |
|
| 236 | struct tool_spad {
|
| 237 | int sidx;
|
| 238 | int pidx;
|
| 239 | struct tool_ctx *tc;
|
| 240 | };
|
| 241 |
|
| 242 | struct tool_peer {
|
| 243 | int pidx;
|
| 244 | struct tool_ctx *tc;
|
| 245 | int inmw_cnt;
|
| 246 | struct tool_mw *inmws;
|
| 247 | int outmw_cnt;
|
| 248 | struct tool_mw_wrap *outmws;
|
| 249 | int outmsg_cnt;
|
| 250 | struct tool_msg *outmsgs;
|
| 251 | int outspad_cnt;
|
| 252 | struct tool_spad *outspads;
|
| 253 | struct dentry *dbgfs_dir;
|
| 254 | };
|
| 255 |
|
| 256 | struct tool_ctx {
|
| 257 | struct ntb_dev *ntb;
|
| 258 | wait_queue_head_t link_wq;
|
| 259 | wait_queue_head_t db_wq;
|
| 260 | wait_queue_head_t msg_wq;
|
| 261 | int outmw_cnt;
|
| 262 | struct tool_mw *outmws;
|
| 263 | int peer_cnt;
|
| 264 | struct tool_peer *peers;
|
| 265 | int inmsg_cnt;
|
| 266 | struct tool_msg *inmsgs;
|
| 267 | int inspad_cnt;
|
| 268 | struct tool_spad *inspads;
|
| 269 | struct dentry *dbgfs_dir;
|
| 270 | };
|
| 271 |
|
| 272 | #define TOOL_FOPS_RDWR(__name, __read, __write) \
|
| 273 | const struct file_operations __name = { \
|
| 274 | .owner = THIS_MODULE, \
|
| 275 | .open = simple_open, \
|
| 276 | .read = __read, \
|
| 277 | .write = __write, \
|
| 278 | }
|
| 279 |
|
| 280 | #define TOOL_BUF_LEN 32
|
| 281 |
|
| 282 | static struct dentry *tool_dbgfs_topdir;
|
| 283 |
|
| 284 | |
| 285 | * NTB events handlers
|
| 286 | *==============================================================================
|
| 287 |
|
| 288 |
|
| 289 | static void tool_link_event(void *ctx)
|
| 290 | {
|
| 291 | struct tool_ctx *tc = ctx;
|
| 292 | enum ntb_speed speed;
|
| 293 | enum ntb_width width;
|
| 294 | int up;
|
| 295 |
|
| 296 | up = ntb_link_is_up(tc->ntb, &speed, &width);
|
| 297 |
|
| 298 | dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n",
|
| 299 | up ? "up" : "down", speed, width);
|
| 300 |
|
| 301 | wake_up(&tc->link_wq);
|
| 302 | }
|
| 303 |
|
| 304 | static void tool_db_event(void *ctx, int vec)
|
| 305 | {
|
| 306 | struct tool_ctx *tc = ctx;
|
| 307 | u64 db_bits, db_mask;
|
| 308 |
|
| 309 | db_mask = ntb_db_vector_mask(tc->ntb, vec);
|
| 310 | db_bits = ntb_db_read(tc->ntb);
|
| 311 |
|
| 312 | dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n",
|
| 313 | vec, db_mask, db_bits);
|
| 314 |
|
| 315 | wake_up(&tc->db_wq);
|
| 316 | }
|
| 317 |
|
| 318 | static void tool_msg_event(void *ctx)
|
| 319 | {
|
| 320 | struct tool_ctx *tc = ctx;
|
| 321 | u64 msg_sts;
|
| 322 |
|
| 323 | msg_sts = ntb_msg_read_sts(tc->ntb);
|
| 324 |
|
| 325 | dev_dbg(&tc->ntb->dev, "message bits %#llx\n", msg_sts);
|
| 326 |
|
| 327 | wake_up(&tc->msg_wq);
|
| 328 | }
|
| 329 |
|
| 330 | static const struct ntb_ctx_ops tool_ops = {
|
| 331 | .link_event = tool_link_event,
|
| 332 | .db_event = tool_db_event,
|
| 333 | .msg_event = tool_msg_event
|
| 334 | };
|
| 335 |
|
| 336 | |
| 337 | * Common read/write methods
|
| 338 | *==============================================================================
|
| 339 |
|
| 340 |
|
| 341 | static ssize_t tool_fn_read(struct tool_ctx *tc, char __user *ubuf,
|
| 342 | size_t size, loff_t *offp,
|
| 343 | u64 (*fn_read)(struct ntb_dev *))
|
| 344 | {
|
| 345 | size_t buf_size;
|
| 346 | char buf[TOOL_BUF_LEN];
|
| 347 | ssize_t pos;
|
| 348 |
|
| 349 | if (!fn_read)
|
| 350 | return -EINVAL;
|
| 351 |
|
| 352 | buf_size = min(size, sizeof(buf));
|
| 353 |
|
| 354 | pos = scnprintf(buf, buf_size, "%#llx\n", fn_read(tc->ntb));
|
| 355 |
|
| 356 | return simple_read_from_buffer(ubuf, size, offp, buf, pos);
|
| 357 | }
|
| 358 |
|
| 359 | static ssize_t tool_fn_write(struct tool_ctx *tc,
|
| 360 | const char __user *ubuf,
|
| 361 | size_t size, loff_t *offp,
|
| 362 | int (*fn_set)(struct ntb_dev *, u64),
|
| 363 | int (*fn_clear)(struct ntb_dev *, u64))
|
| 364 | {
|
| 365 | char *buf, cmd;
|
| 366 | ssize_t ret;
|
| 367 | u64 bits;
|
| 368 | int n;
|
| 369 |
|
| 370 | if (*offp)
|
| 371 | return 0;
|
| 372 |
|
| 373 | buf = memdup_user_nul(ubuf, size);
|
| 374 | if (IS_ERR(buf))
|
| 375 | return PTR_ERR(buf);
|
| 376 |
|
| 377 | n = sscanf(buf, "%c %lli", &cmd, &bits);
|
| 378 |
|
| 379 | kfree(buf);
|
| 380 |
|
| 381 | if (n != 2) {
|
| 382 | ret = -EINVAL;
|
| 383 | } else if (cmd == 's') {
|
| 384 | if (!fn_set)
|
| 385 | ret = -EINVAL;
|
| 386 | else
|
| 387 | ret = fn_set(tc->ntb, bits);
|
| 388 | } else if (cmd == 'c') {
|
| 389 | if (!fn_clear)
|
| 390 | ret = -EINVAL;
|
| 391 | else
|
| 392 | ret = fn_clear(tc->ntb, bits);
|
| 393 | } else {
|
| 394 | ret = -EINVAL;
|
| 395 | }
|
| 396 |
|
| 397 | return ret ? : size;
|
| 398 | }
|
| 399 |
|
| 400 | |
| 401 | * Port read/write methods
|
| 402 | *==============================================================================
|
| 403 |
|
| 404 |
|
| 405 | static ssize_t tool_port_read(struct file *filep, char __user *ubuf,
|
| 406 | size_t size, loff_t *offp)
|
| 407 | {
|
| 408 | struct tool_ctx *tc = filep->private_data;
|
| 409 | char buf[TOOL_BUF_LEN];
|
| 410 | int pos;
|
| 411 |
|
| 412 | pos = scnprintf(buf, sizeof(buf), "%d\n", ntb_port_number(tc->ntb));
|
| 413 |
|
| 414 | return simple_read_from_buffer(ubuf, size, offp, buf, pos);
|
| 415 | }
|
| 416 |
|
| 417 | static TOOL_FOPS_RDWR(tool_port_fops,
|
| 418 | tool_port_read,
|
| 419 | NULL);
|
| 420 |
|
| 421 | static ssize_t tool_peer_port_read(struct file *filep, char __user *ubuf,
|
| 422 | size_t size, loff_t *offp)
|
| 423 | {
|
| 424 | struct tool_peer *peer = filep->private_data;
|
| 425 | struct tool_ctx *tc = peer->tc;
|
| 426 | char buf[TOOL_BUF_LEN];
|
| 427 | int pos;
|
| 428 |
|
| 429 | pos = scnprintf(buf, sizeof(buf), "%d\n",
|
| 430 | ntb_peer_port_number(tc->ntb, peer->pidx));
|
| 431 |
|
| 432 | return simple_read_from_buffer(ubuf, size, offp, buf, pos);
|
| 433 | }
|
| 434 |
|
| 435 | static TOOL_FOPS_RDWR(tool_peer_port_fops,
|
| 436 | tool_peer_port_read,
|
| 437 | NULL);
|
| 438 |
|
| 439 | static int tool_init_peers(struct tool_ctx *tc)
|
| 440 | {
|
| 441 | int pidx;
|
| 442 |
|
| 443 | tc->peer_cnt = ntb_peer_port_count(tc->ntb);
|
| 444 | tc->peers = devm_kcalloc(&tc->ntb->dev, tc->peer_cnt,
|
| 445 | sizeof(*tc->peers), GFP_KERNEL);
|
| 446 | if (tc->peers == NULL)
|
| 447 | return -ENOMEM;
|
| 448 |
|
| 449 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
|
| 450 | tc->peers[pidx].pidx = pidx;
|
| 451 | tc->peers[pidx].tc = tc;
|
| 452 | }
|
| 453 |
|
| 454 | return 0;
|
| 455 | }
|
| 456 |
|
| 457 | |
| 458 | * Link state read/write methods
|
| 459 | *==============================================================================
|
| 460 |
|
| 461 |
|
| 462 | static ssize_t tool_link_write(struct file *filep, const char __user *ubuf,
|
| 463 | size_t size, loff_t *offp)
|
| 464 | {
|
| 465 | struct tool_ctx *tc = filep->private_data;
|
| 466 | bool val;
|
| 467 | int ret;
|
| 468 |
|
| 469 | ret = kstrtobool_from_user(ubuf, size, &val);
|
| 470 | if (ret)
|
| 471 | return ret;
|
| 472 |
|
| 473 | if (val)
|
| 474 | ret = ntb_link_enable(tc->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
|
| 475 | else
|
| 476 | ret = ntb_link_disable(tc->ntb);
|
| 477 |
|
| 478 | if (ret)
|
| 479 | return ret;
|
| 480 |
|
| 481 | return size;
|
| 482 | }
|
| 483 |
|
| 484 | static TOOL_FOPS_RDWR(tool_link_fops,
|
| 485 | NULL,
|
| 486 | tool_link_write);
|
| 487 |
|
| 488 | static ssize_t tool_peer_link_read(struct file *filep, char __user *ubuf,
|
| 489 | size_t size, loff_t *offp)
|
| 490 | {
|
| 491 | struct tool_peer *peer = filep->private_data;
|
| 492 | struct tool_ctx *tc = peer->tc;
|
| 493 | char buf[3];
|
| 494 |
|
| 495 | if (ntb_link_is_up(tc->ntb, NULL, NULL) & BIT(peer->pidx))
|
| 496 | buf[0] = 'Y';
|
| 497 | else
|
| 498 | buf[0] = 'N';
|
| 499 | buf[1] = '\n';
|
| 500 | buf[2] = '\0';
|
| 501 |
|
| 502 | return simple_read_from_buffer(ubuf, size, offp, buf, 2);
|
| 503 | }
|
| 504 |
|
| 505 | static TOOL_FOPS_RDWR(tool_peer_link_fops,
|
| 506 | tool_peer_link_read,
|
| 507 | NULL);
|
| 508 |
|
| 509 | static ssize_t tool_peer_link_event_write(struct file *filep,
|
| 510 | const char __user *ubuf,
|
| 511 | size_t size, loff_t *offp)
|
| 512 | {
|
| 513 | struct tool_peer *peer = filep->private_data;
|
| 514 | struct tool_ctx *tc = peer->tc;
|
| 515 | u64 link_msk;
|
| 516 | bool val;
|
| 517 | int ret;
|
| 518 |
|
| 519 | ret = kstrtobool_from_user(ubuf, size, &val);
|
| 520 | if (ret)
|
| 521 | return ret;
|
| 522 |
|
| 523 | link_msk = BIT_ULL_MASK(peer->pidx);
|
| 524 |
|
| 525 | if (wait_event_interruptible(tc->link_wq,
|
| 526 | !!(ntb_link_is_up(tc->ntb, NULL, NULL) & link_msk) == val))
|
| 527 | return -ERESTART;
|
| 528 |
|
| 529 | return size;
|
| 530 | }
|
| 531 |
|
| 532 | static TOOL_FOPS_RDWR(tool_peer_link_event_fops,
|
| 533 | NULL,
|
| 534 | tool_peer_link_event_write);
|
| 535 |
|
| 536 | |
| 537 | * Memory windows read/write/setting methods
|
| 538 | *==============================================================================
|
| 539 |
|
| 540 |
|
| 541 | static ssize_t tool_mw_read(struct file *filep, char __user *ubuf,
|
| 542 | size_t size, loff_t *offp)
|
| 543 | {
|
| 544 | struct tool_mw *inmw = filep->private_data;
|
| 545 |
|
| 546 | if (inmw->mm_base == NULL)
|
| 547 | return -ENXIO;
|
| 548 |
|
| 549 | return simple_read_from_buffer(ubuf, size, offp,
|
| 550 | inmw->mm_base, inmw->size);
|
| 551 | }
|
| 552 |
|
| 553 | static ssize_t tool_mw_write(struct file *filep, const char __user *ubuf,
|
| 554 | size_t size, loff_t *offp)
|
| 555 | {
|
| 556 | struct tool_mw *inmw = filep->private_data;
|
| 557 |
|
| 558 | if (inmw->mm_base == NULL)
|
| 559 | return -ENXIO;
|
| 560 |
|
| 561 | return simple_write_to_buffer(inmw->mm_base, inmw->size, offp,
|
| 562 | ubuf, size);
|
| 563 | }
|
| 564 |
|
| 565 | static TOOL_FOPS_RDWR(tool_mw_fops,
|
| 566 | tool_mw_read,
|
| 567 | tool_mw_write);
|
| 568 |
|
| 569 | static int tool_setup_mw(struct tool_ctx *tc, int pidx, int widx,
|
| 570 | size_t req_size)
|
| 571 | {
|
| 572 | resource_size_t size, addr_align, size_align;
|
| 573 | struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
|
| 574 | char buf[TOOL_BUF_LEN];
|
| 575 | int ret;
|
| 576 |
|
| 577 | if (inmw->mm_base != NULL)
|
| 578 | return 0;
|
| 579 |
|
| 580 | ret = ntb_mw_get_align(tc->ntb, pidx, widx, &addr_align,
|
| 581 | &size_align, &size);
|
| 582 | if (ret)
|
| 583 | return ret;
|
| 584 |
|
| 585 | inmw->size = min_t(resource_size_t, req_size, size);
|
| 586 | inmw->size = round_up(inmw->size, addr_align);
|
| 587 | inmw->size = round_up(inmw->size, size_align);
|
| 588 | inmw->mm_base = dma_alloc_coherent(&tc->ntb->pdev->dev, inmw->size,
|
| 589 | &inmw->dma_base, GFP_KERNEL);
|
| 590 | if (!inmw->mm_base)
|
| 591 | return -ENOMEM;
|
| 592 |
|
| 593 | if (!IS_ALIGNED(inmw->dma_base, addr_align)) {
|
| 594 | ret = -ENOMEM;
|
| 595 | goto err_free_dma;
|
| 596 | }
|
| 597 |
|
| 598 | ret = ntb_mw_set_trans(tc->ntb, pidx, widx, inmw->dma_base, inmw->size);
|
| 599 | if (ret)
|
| 600 | goto err_free_dma;
|
| 601 |
|
| 602 | snprintf(buf, sizeof(buf), "mw%d", widx);
|
| 603 | inmw->dbgfs_file = debugfs_create_file(buf, 0600,
|
| 604 | tc->peers[pidx].dbgfs_dir, inmw,
|
| 605 | &tool_mw_fops);
|
| 606 |
|
| 607 | return 0;
|
| 608 |
|
| 609 | err_free_dma:
|
| 610 | dma_free_coherent(&tc->ntb->pdev->dev, inmw->size, inmw->mm_base,
|
| 611 | inmw->dma_base);
|
| 612 | inmw->mm_base = NULL;
|
| 613 | inmw->dma_base = 0;
|
| 614 | inmw->size = 0;
|
| 615 |
|
| 616 | return ret;
|
| 617 | }
|
| 618 |
|
| 619 | static void tool_free_mw(struct tool_ctx *tc, int pidx, int widx)
|
| 620 | {
|
| 621 | struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
|
| 622 |
|
| 623 | debugfs_remove(inmw->dbgfs_file);
|
| 624 |
|
| 625 | if (inmw->mm_base != NULL) {
|
| 626 | ntb_mw_clear_trans(tc->ntb, pidx, widx);
|
| 627 | dma_free_coherent(&tc->ntb->pdev->dev, inmw->size,
|
| 628 | inmw->mm_base, inmw->dma_base);
|
| 629 | }
|
| 630 |
|
| 631 | inmw->mm_base = NULL;
|
| 632 | inmw->dma_base = 0;
|
| 633 | inmw->size = 0;
|
| 634 | inmw->dbgfs_file = NULL;
|
| 635 | }
|
| 636 |
|
| 637 | static ssize_t tool_mw_trans_read(struct file *filep, char __user *ubuf,
|
| 638 | size_t size, loff_t *offp)
|
| 639 | {
|
| 640 | struct tool_mw *inmw = filep->private_data;
|
| 641 | resource_size_t addr_align;
|
| 642 | resource_size_t size_align;
|
| 643 | resource_size_t size_max;
|
| 644 | ssize_t ret, off = 0;
|
| 645 | size_t buf_size;
|
| 646 | char *buf;
|
| 647 |
|
| 648 | buf_size = min_t(size_t, size, 512);
|
| 649 |
|
| 650 | buf = kmalloc(buf_size, GFP_KERNEL);
|
| 651 | if (!buf)
|
| 652 | return -ENOMEM;
|
| 653 |
|
| 654 | ret = ntb_mw_get_align(inmw->tc->ntb, inmw->pidx, inmw->widx,
|
| 655 | &addr_align, &size_align, &size_max);
|
| 656 | if (ret)
|
| 657 | goto err;
|
| 658 |
|
| 659 | off += scnprintf(buf + off, buf_size - off,
|
| 660 | "Inbound MW \t%d\n",
|
| 661 | inmw->widx);
|
| 662 |
|
| 663 | off += scnprintf(buf + off, buf_size - off,
|
| 664 | "Port \t%d (%d)\n",
|
| 665 | ntb_peer_port_number(inmw->tc->ntb, inmw->pidx),
|
| 666 | inmw->pidx);
|
| 667 |
|
| 668 | off += scnprintf(buf + off, buf_size - off,
|
| 669 | "Window Address \t0x%pK\n", inmw->mm_base);
|
| 670 |
|
| 671 | off += scnprintf(buf + off, buf_size - off,
|
| 672 | "DMA Address \t%pad\n",
|
| 673 | &inmw->dma_base);
|
| 674 |
|
| 675 | off += scnprintf(buf + off, buf_size - off,
|
| 676 | "Window Size \t%pap\n",
|
| 677 | &inmw->size);
|
| 678 |
|
| 679 | off += scnprintf(buf + off, buf_size - off,
|
| 680 | "Alignment \t%pap\n",
|
| 681 | &addr_align);
|
| 682 |
|
| 683 | off += scnprintf(buf + off, buf_size - off,
|
| 684 | "Size Alignment \t%pap\n",
|
| 685 | &size_align);
|
| 686 |
|
| 687 | off += scnprintf(buf + off, buf_size - off,
|
| 688 | "Size Max \t%pap\n",
|
| 689 | &size_max);
|
| 690 |
|
| 691 | ret = simple_read_from_buffer(ubuf, size, offp, buf, off);
|
| 692 |
|
| 693 | err:
|
| 694 | kfree(buf);
|
| 695 |
|
| 696 | return ret;
|
| 697 | }
|
| 698 |
|
| 699 | static ssize_t tool_mw_trans_write(struct file *filep, const char __user *ubuf,
|
| 700 | size_t size, loff_t *offp)
|
| 701 | {
|
| 702 | struct tool_mw *inmw = filep->private_data;
|
| 703 | unsigned int val;
|
| 704 | int ret;
|
| 705 |
|
| 706 | ret = kstrtouint_from_user(ubuf, size, 0, &val);
|
| 707 | if (ret)
|
| 708 | return ret;
|
| 709 |
|
| 710 | tool_free_mw(inmw->tc, inmw->pidx, inmw->widx);
|
| 711 | if (val) {
|
| 712 | ret = tool_setup_mw(inmw->tc, inmw->pidx, inmw->widx, val);
|
| 713 | if (ret)
|
| 714 | return ret;
|
| 715 | }
|
| 716 |
|
| 717 | return size;
|
| 718 | }
|
| 719 |
|
| 720 | static TOOL_FOPS_RDWR(tool_mw_trans_fops,
|
| 721 | tool_mw_trans_read,
|
| 722 | tool_mw_trans_write);
|
| 723 |
|
| 724 | static ssize_t tool_peer_mw_read(struct file *filep, char __user *ubuf,
|
| 725 | size_t size, loff_t *offp)
|
| 726 | {
|
| 727 | struct tool_mw *outmw = filep->private_data;
|
| 728 | loff_t pos = *offp;
|
| 729 | ssize_t ret;
|
| 730 | void *buf;
|
| 731 |
|
| 732 | if (outmw->io_base == NULL)
|
| 733 | return -EIO;
|
| 734 |
|
| 735 | if (pos >= outmw->size || !size)
|
| 736 | return 0;
|
| 737 |
|
| 738 | if (size > outmw->size - pos)
|
| 739 | size = outmw->size - pos;
|
| 740 |
|
| 741 | buf = kmalloc(size, GFP_KERNEL);
|
| 742 | if (!buf)
|
| 743 | return -ENOMEM;
|
| 744 |
|
| 745 | memcpy_fromio(buf, outmw->io_base + pos, size);
|
| 746 | ret = copy_to_user(ubuf, buf, size);
|
| 747 | if (ret == size) {
|
| 748 | ret = -EFAULT;
|
| 749 | goto err_free;
|
| 750 | }
|
| 751 |
|
| 752 | size -= ret;
|
| 753 | *offp = pos + size;
|
| 754 | ret = size;
|
| 755 |
|
| 756 | err_free:
|
| 757 | kfree(buf);
|
| 758 |
|
| 759 | return ret;
|
| 760 | }
|
| 761 |
|
| 762 | static ssize_t tool_peer_mw_write(struct file *filep, const char __user *ubuf,
|
| 763 | size_t size, loff_t *offp)
|
| 764 | {
|
| 765 | struct tool_mw *outmw = filep->private_data;
|
| 766 | ssize_t ret;
|
| 767 | loff_t pos = *offp;
|
| 768 | void *buf;
|
| 769 |
|
| 770 | if (outmw->io_base == NULL)
|
| 771 | return -EIO;
|
| 772 |
|
| 773 | if (pos >= outmw->size || !size)
|
| 774 | return 0;
|
| 775 | if (size > outmw->size - pos)
|
| 776 | size = outmw->size - pos;
|
| 777 |
|
| 778 | buf = kmalloc(size, GFP_KERNEL);
|
| 779 | if (!buf)
|
| 780 | return -ENOMEM;
|
| 781 |
|
| 782 | ret = copy_from_user(buf, ubuf, size);
|
| 783 | if (ret == size) {
|
| 784 | ret = -EFAULT;
|
| 785 | goto err_free;
|
| 786 | }
|
| 787 |
|
| 788 | size -= ret;
|
| 789 | *offp = pos + size;
|
| 790 | ret = size;
|
| 791 |
|
| 792 | memcpy_toio(outmw->io_base + pos, buf, size);
|
| 793 |
|
| 794 | err_free:
|
| 795 | kfree(buf);
|
| 796 |
|
| 797 | return ret;
|
| 798 | }
|
| 799 |
|
| 800 | static TOOL_FOPS_RDWR(tool_peer_mw_fops,
|
| 801 | tool_peer_mw_read,
|
| 802 | tool_peer_mw_write);
|
| 803 |
|
| 804 | static int tool_setup_peer_mw(struct tool_ctx *tc, int pidx, int widx,
|
| 805 | u64 req_addr, size_t req_size)
|
| 806 | {
|
| 807 | struct tool_mw *outmw = &tc->outmws[widx];
|
| 808 | resource_size_t map_size;
|
| 809 | phys_addr_t map_base;
|
| 810 | char buf[TOOL_BUF_LEN];
|
| 811 | int ret;
|
| 812 |
|
| 813 | if (outmw->io_base != NULL)
|
| 814 | return 0;
|
| 815 |
|
| 816 | ret = ntb_peer_mw_get_addr(tc->ntb, widx, &map_base, &map_size);
|
| 817 | if (ret)
|
| 818 | return ret;
|
| 819 |
|
| 820 | ret = ntb_peer_mw_set_trans(tc->ntb, pidx, widx, req_addr, req_size);
|
| 821 | if (ret)
|
| 822 | return ret;
|
| 823 |
|
| 824 | outmw->io_base = ioremap_wc(map_base, map_size);
|
| 825 | if (outmw->io_base == NULL) {
|
| 826 | ret = -EFAULT;
|
| 827 | goto err_clear_trans;
|
| 828 | }
|
| 829 |
|
| 830 | outmw->tr_base = req_addr;
|
| 831 | outmw->size = req_size;
|
| 832 | outmw->pidx = pidx;
|
| 833 |
|
| 834 | snprintf(buf, sizeof(buf), "peer_mw%d", widx);
|
| 835 | outmw->dbgfs_file = debugfs_create_file(buf, 0600,
|
| 836 | tc->peers[pidx].dbgfs_dir, outmw,
|
| 837 | &tool_peer_mw_fops);
|
| 838 |
|
| 839 | return 0;
|
| 840 |
|
| 841 | err_clear_trans:
|
| 842 | ntb_peer_mw_clear_trans(tc->ntb, pidx, widx);
|
| 843 |
|
| 844 | return ret;
|
| 845 | }
|
| 846 |
|
| 847 | static void tool_free_peer_mw(struct tool_ctx *tc, int widx)
|
| 848 | {
|
| 849 | struct tool_mw *outmw = &tc->outmws[widx];
|
| 850 |
|
| 851 | debugfs_remove(outmw->dbgfs_file);
|
| 852 |
|
| 853 | if (outmw->io_base != NULL) {
|
| 854 | iounmap(tc->outmws[widx].io_base);
|
| 855 | ntb_peer_mw_clear_trans(tc->ntb, outmw->pidx, widx);
|
| 856 | }
|
| 857 |
|
| 858 | outmw->io_base = NULL;
|
| 859 | outmw->tr_base = 0;
|
| 860 | outmw->size = 0;
|
| 861 | outmw->pidx = -1;
|
| 862 | outmw->dbgfs_file = NULL;
|
| 863 | }
|
| 864 |
|
| 865 | static ssize_t tool_peer_mw_trans_read(struct file *filep, char __user *ubuf,
|
| 866 | size_t size, loff_t *offp)
|
| 867 | {
|
| 868 | struct tool_mw_wrap *outmw_wrap = filep->private_data;
|
| 869 | struct tool_mw *outmw = outmw_wrap->mw;
|
| 870 | resource_size_t map_size;
|
| 871 | phys_addr_t map_base;
|
| 872 | ssize_t off = 0;
|
| 873 | size_t buf_size;
|
| 874 | char *buf;
|
| 875 | int ret;
|
| 876 |
|
| 877 | ret = ntb_peer_mw_get_addr(outmw->tc->ntb, outmw->widx,
|
| 878 | &map_base, &map_size);
|
| 879 | if (ret)
|
| 880 | return ret;
|
| 881 |
|
| 882 | buf_size = min_t(size_t, size, 512);
|
| 883 |
|
| 884 | buf = kmalloc(buf_size, GFP_KERNEL);
|
| 885 | if (!buf)
|
| 886 | return -ENOMEM;
|
| 887 |
|
| 888 | off += scnprintf(buf + off, buf_size - off,
|
| 889 | "Outbound MW: \t%d\n", outmw->widx);
|
| 890 |
|
| 891 | if (outmw->io_base != NULL) {
|
| 892 | off += scnprintf(buf + off, buf_size - off,
|
| 893 | "Port attached \t%d (%d)\n",
|
| 894 | ntb_peer_port_number(outmw->tc->ntb, outmw->pidx),
|
| 895 | outmw->pidx);
|
| 896 | } else {
|
| 897 | off += scnprintf(buf + off, buf_size - off,
|
| 898 | "Port attached \t-1 (-1)\n");
|
| 899 | }
|
| 900 |
|
| 901 | off += scnprintf(buf + off, buf_size - off,
|
| 902 | "Virtual address \t0x%pK\n", outmw->io_base);
|
| 903 |
|
| 904 | off += scnprintf(buf + off, buf_size - off,
|
| 905 | "Phys Address \t%pap\n", &map_base);
|
| 906 |
|
| 907 | off += scnprintf(buf + off, buf_size - off,
|
| 908 | "Mapping Size \t%pap\n", &map_size);
|
| 909 |
|
| 910 | off += scnprintf(buf + off, buf_size - off,
|
| 911 | "Translation Address \t0x%016llx\n", outmw->tr_base);
|
| 912 |
|
| 913 | off += scnprintf(buf + off, buf_size - off,
|
| 914 | "Window Size \t%pap\n", &outmw->size);
|
| 915 |
|
| 916 | ret = simple_read_from_buffer(ubuf, size, offp, buf, off);
|
| 917 | kfree(buf);
|
| 918 |
|
| 919 | return ret;
|
| 920 | }
|
| 921 |
|
| 922 | static ssize_t tool_peer_mw_trans_write(struct file *filep,
|
| 923 | const char __user *ubuf,
|
| 924 | size_t size, loff_t *offp)
|
| 925 | {
|
| 926 | struct tool_mw_wrap *outmw_wrap = filep->private_data;
|
| 927 | struct tool_mw *outmw = outmw_wrap->mw;
|
| 928 | size_t buf_size, wsize;
|
| 929 | char buf[TOOL_BUF_LEN];
|
| 930 | int ret, n;
|
| 931 | u64 addr;
|
| 932 |
|
| 933 | buf_size = min(size, (sizeof(buf) - 1));
|
| 934 | if (copy_from_user(buf, ubuf, buf_size))
|
| 935 | return -EFAULT;
|
| 936 |
|
| 937 | buf[buf_size] = '\0';
|
| 938 |
|
| 939 | n = sscanf(buf, "%llu:%zu", &addr, &wsize);
|
| 940 | if (n != 2)
|
| 941 | return -EINVAL;
|
| 942 |
|
| 943 | tool_free_peer_mw(outmw->tc, outmw->widx);
|
| 944 | if (wsize) {
|
| 945 | ret = tool_setup_peer_mw(outmw->tc, outmw_wrap->pidx,
|
| 946 | outmw->widx, addr, wsize);
|
| 947 | if (ret)
|
| 948 | return ret;
|
| 949 | }
|
| 950 |
|
| 951 | return size;
|
| 952 | }
|
| 953 |
|
| 954 | static TOOL_FOPS_RDWR(tool_peer_mw_trans_fops,
|
| 955 | tool_peer_mw_trans_read,
|
| 956 | tool_peer_mw_trans_write);
|
| 957 |
|
| 958 | static int tool_init_mws(struct tool_ctx *tc)
|
| 959 | {
|
| 960 | int widx, pidx;
|
| 961 |
|
| 962 |
|
| 963 | tc->outmw_cnt = ntb_peer_mw_count(tc->ntb);
|
| 964 | tc->outmws = devm_kcalloc(&tc->ntb->dev, tc->outmw_cnt,
|
| 965 | sizeof(*tc->outmws), GFP_KERNEL);
|
| 966 | if (tc->outmws == NULL)
|
| 967 | return -ENOMEM;
|
| 968 |
|
| 969 | for (widx = 0; widx < tc->outmw_cnt; widx++) {
|
| 970 | tc->outmws[widx].widx = widx;
|
| 971 | tc->outmws[widx].pidx = -1;
|
| 972 | tc->outmws[widx].tc = tc;
|
| 973 | }
|
| 974 |
|
| 975 |
|
| 976 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
|
| 977 | tc->peers[pidx].inmw_cnt = ntb_mw_count(tc->ntb, pidx);
|
| 978 | tc->peers[pidx].inmws =
|
| 979 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].inmw_cnt,
|
| 980 | sizeof(*tc->peers[pidx].inmws), GFP_KERNEL);
|
| 981 | if (tc->peers[pidx].inmws == NULL)
|
| 982 | return -ENOMEM;
|
| 983 |
|
| 984 | for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
|
| 985 | tc->peers[pidx].inmws[widx].widx = widx;
|
| 986 | tc->peers[pidx].inmws[widx].pidx = pidx;
|
| 987 | tc->peers[pidx].inmws[widx].tc = tc;
|
| 988 | }
|
| 989 |
|
| 990 | tc->peers[pidx].outmw_cnt = ntb_peer_mw_count(tc->ntb);
|
| 991 | tc->peers[pidx].outmws =
|
| 992 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outmw_cnt,
|
| 993 | sizeof(*tc->peers[pidx].outmws), GFP_KERNEL);
|
| 994 | if (tc->peers[pidx].outmws == NULL)
|
| 995 | return -ENOMEM;
|
| 996 |
|
| 997 | for (widx = 0; widx < tc->peers[pidx].outmw_cnt; widx++) {
|
| 998 | tc->peers[pidx].outmws[widx].pidx = pidx;
|
| 999 | tc->peers[pidx].outmws[widx].mw = &tc->outmws[widx];
|
| 1000 | }
|
| 1001 | }
|
| 1002 |
|
| 1003 | return 0;
|
| 1004 | }
|
| 1005 |
|
| 1006 | static void tool_clear_mws(struct tool_ctx *tc)
|
| 1007 | {
|
| 1008 | int widx, pidx;
|
| 1009 |
|
| 1010 |
|
| 1011 | for (widx = 0; widx < tc->outmw_cnt; widx++)
|
| 1012 | tool_free_peer_mw(tc, widx);
|
| 1013 |
|
| 1014 |
|
| 1015 | for (pidx = 0; pidx < tc->peer_cnt; pidx++)
|
| 1016 | for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++)
|
| 1017 | tool_free_mw(tc, pidx, widx);
|
| 1018 | }
|
| 1019 |
|
| 1020 | |
| 1021 | * Doorbell read/write methods
|
| 1022 | *==============================================================================
|
| 1023 |
|
| 1024 |
|
| 1025 | static ssize_t tool_db_read(struct file *filep, char __user *ubuf,
|
| 1026 | size_t size, loff_t *offp)
|
| 1027 | {
|
| 1028 | struct tool_ctx *tc = filep->private_data;
|
| 1029 |
|
| 1030 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_read);
|
| 1031 | }
|
| 1032 |
|
| 1033 | static ssize_t tool_db_write(struct file *filep, const char __user *ubuf,
|
| 1034 | size_t size, loff_t *offp)
|
| 1035 | {
|
| 1036 | struct tool_ctx *tc = filep->private_data;
|
| 1037 |
|
| 1038 | return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->db_set,
|
| 1039 | tc->ntb->ops->db_clear);
|
| 1040 | }
|
| 1041 |
|
| 1042 | static TOOL_FOPS_RDWR(tool_db_fops,
|
| 1043 | tool_db_read,
|
| 1044 | tool_db_write);
|
| 1045 |
|
| 1046 | static ssize_t tool_db_valid_mask_read(struct file *filep, char __user *ubuf,
|
| 1047 | size_t size, loff_t *offp)
|
| 1048 | {
|
| 1049 | struct tool_ctx *tc = filep->private_data;
|
| 1050 |
|
| 1051 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_valid_mask);
|
| 1052 | }
|
| 1053 |
|
| 1054 | static TOOL_FOPS_RDWR(tool_db_valid_mask_fops,
|
| 1055 | tool_db_valid_mask_read,
|
| 1056 | NULL);
|
| 1057 |
|
| 1058 | static ssize_t tool_db_mask_read(struct file *filep, char __user *ubuf,
|
| 1059 | size_t size, loff_t *offp)
|
| 1060 | {
|
| 1061 | struct tool_ctx *tc = filep->private_data;
|
| 1062 |
|
| 1063 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_read_mask);
|
| 1064 | }
|
| 1065 |
|
| 1066 | static ssize_t tool_db_mask_write(struct file *filep, const char __user *ubuf,
|
| 1067 | size_t size, loff_t *offp)
|
| 1068 | {
|
| 1069 | struct tool_ctx *tc = filep->private_data;
|
| 1070 |
|
| 1071 | return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->db_set_mask,
|
| 1072 | tc->ntb->ops->db_clear_mask);
|
| 1073 | }
|
| 1074 |
|
| 1075 | static TOOL_FOPS_RDWR(tool_db_mask_fops,
|
| 1076 | tool_db_mask_read,
|
| 1077 | tool_db_mask_write);
|
| 1078 |
|
| 1079 | static ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf,
|
| 1080 | size_t size, loff_t *offp)
|
| 1081 | {
|
| 1082 | struct tool_ctx *tc = filep->private_data;
|
| 1083 |
|
| 1084 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->peer_db_read);
|
| 1085 | }
|
| 1086 |
|
| 1087 | static ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf,
|
| 1088 | size_t size, loff_t *offp)
|
| 1089 | {
|
| 1090 | struct tool_ctx *tc = filep->private_data;
|
| 1091 |
|
| 1092 | return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->peer_db_set,
|
| 1093 | tc->ntb->ops->peer_db_clear);
|
| 1094 | }
|
| 1095 |
|
| 1096 | static TOOL_FOPS_RDWR(tool_peer_db_fops,
|
| 1097 | tool_peer_db_read,
|
| 1098 | tool_peer_db_write);
|
| 1099 |
|
| 1100 | static ssize_t tool_peer_db_mask_read(struct file *filep, char __user *ubuf,
|
| 1101 | size_t size, loff_t *offp)
|
| 1102 | {
|
| 1103 | struct tool_ctx *tc = filep->private_data;
|
| 1104 |
|
| 1105 | return tool_fn_read(tc, ubuf, size, offp,
|
| 1106 | tc->ntb->ops->peer_db_read_mask);
|
| 1107 | }
|
| 1108 |
|
| 1109 | static ssize_t tool_peer_db_mask_write(struct file *filep,
|
| 1110 | const char __user *ubuf,
|
| 1111 | size_t size, loff_t *offp)
|
| 1112 | {
|
| 1113 | struct tool_ctx *tc = filep->private_data;
|
| 1114 |
|
| 1115 | return tool_fn_write(tc, ubuf, size, offp,
|
| 1116 | tc->ntb->ops->peer_db_set_mask,
|
| 1117 | tc->ntb->ops->peer_db_clear_mask);
|
| 1118 | }
|
| 1119 |
|
| 1120 | static TOOL_FOPS_RDWR(tool_peer_db_mask_fops,
|
| 1121 | tool_peer_db_mask_read,
|
| 1122 | tool_peer_db_mask_write);
|
| 1123 |
|
| 1124 | static ssize_t tool_db_event_write(struct file *filep,
|
| 1125 | const char __user *ubuf,
|
| 1126 | size_t size, loff_t *offp)
|
| 1127 | {
|
| 1128 | struct tool_ctx *tc = filep->private_data;
|
| 1129 | u64 val;
|
| 1130 | int ret;
|
| 1131 |
|
| 1132 | ret = kstrtou64_from_user(ubuf, size, 0, &val);
|
| 1133 | if (ret)
|
| 1134 | return ret;
|
| 1135 |
|
| 1136 | if (wait_event_interruptible(tc->db_wq, ntb_db_read(tc->ntb) == val))
|
| 1137 | return -ERESTART;
|
| 1138 |
|
| 1139 | return size;
|
| 1140 | }
|
| 1141 |
|
| 1142 | static TOOL_FOPS_RDWR(tool_db_event_fops,
|
| 1143 | NULL,
|
| 1144 | tool_db_event_write);
|
| 1145 |
|
| 1146 | |
| 1147 | * Scratchpads read/write methods
|
| 1148 | *==============================================================================
|
| 1149 |
|
| 1150 |
|
| 1151 | static ssize_t tool_spad_read(struct file *filep, char __user *ubuf,
|
| 1152 | size_t size, loff_t *offp)
|
| 1153 | {
|
| 1154 | struct tool_spad *spad = filep->private_data;
|
| 1155 | char buf[TOOL_BUF_LEN];
|
| 1156 | ssize_t pos;
|
| 1157 |
|
| 1158 | if (!spad->tc->ntb->ops->spad_read)
|
| 1159 | return -EINVAL;
|
| 1160 |
|
| 1161 | pos = scnprintf(buf, sizeof(buf), "%#x\n",
|
| 1162 | ntb_spad_read(spad->tc->ntb, spad->sidx));
|
| 1163 |
|
| 1164 | return simple_read_from_buffer(ubuf, size, offp, buf, pos);
|
| 1165 | }
|
| 1166 |
|
| 1167 | static ssize_t tool_spad_write(struct file *filep, const char __user *ubuf,
|
| 1168 | size_t size, loff_t *offp)
|
| 1169 | {
|
| 1170 | struct tool_spad *spad = filep->private_data;
|
| 1171 | u32 val;
|
| 1172 | int ret;
|
| 1173 |
|
| 1174 | if (!spad->tc->ntb->ops->spad_write) {
|
| 1175 | dev_dbg(&spad->tc->ntb->dev, "no spad write fn\n");
|
| 1176 | return -EINVAL;
|
| 1177 | }
|
| 1178 |
|
| 1179 | ret = kstrtou32_from_user(ubuf, size, 0, &val);
|
| 1180 | if (ret)
|
| 1181 | return ret;
|
| 1182 |
|
| 1183 | ret = ntb_spad_write(spad->tc->ntb, spad->sidx, val);
|
| 1184 |
|
| 1185 | return ret ?: size;
|
| 1186 | }
|
| 1187 |
|
| 1188 | static TOOL_FOPS_RDWR(tool_spad_fops,
|
| 1189 | tool_spad_read,
|
| 1190 | tool_spad_write);
|
| 1191 |
|
| 1192 | static ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf,
|
| 1193 | size_t size, loff_t *offp)
|
| 1194 | {
|
| 1195 | struct tool_spad *spad = filep->private_data;
|
| 1196 | char buf[TOOL_BUF_LEN];
|
| 1197 | ssize_t pos;
|
| 1198 |
|
| 1199 | if (!spad->tc->ntb->ops->peer_spad_read)
|
| 1200 | return -EINVAL;
|
| 1201 |
|
| 1202 | pos = scnprintf(buf, sizeof(buf), "%#x\n",
|
| 1203 | ntb_peer_spad_read(spad->tc->ntb, spad->pidx, spad->sidx));
|
| 1204 |
|
| 1205 | return simple_read_from_buffer(ubuf, size, offp, buf, pos);
|
| 1206 | }
|
| 1207 |
|
| 1208 | static ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf,
|
| 1209 | size_t size, loff_t *offp)
|
| 1210 | {
|
| 1211 | struct tool_spad *spad = filep->private_data;
|
| 1212 | u32 val;
|
| 1213 | int ret;
|
| 1214 |
|
| 1215 | if (!spad->tc->ntb->ops->peer_spad_write) {
|
| 1216 | dev_dbg(&spad->tc->ntb->dev, "no spad write fn\n");
|
| 1217 | return -EINVAL;
|
| 1218 | }
|
| 1219 |
|
| 1220 | ret = kstrtou32_from_user(ubuf, size, 0, &val);
|
| 1221 | if (ret)
|
| 1222 | return ret;
|
| 1223 |
|
| 1224 | ret = ntb_peer_spad_write(spad->tc->ntb, spad->pidx, spad->sidx, val);
|
| 1225 |
|
| 1226 | return ret ?: size;
|
| 1227 | }
|
| 1228 |
|
| 1229 | static TOOL_FOPS_RDWR(tool_peer_spad_fops,
|
| 1230 | tool_peer_spad_read,
|
| 1231 | tool_peer_spad_write);
|
| 1232 |
|
| 1233 | static int tool_init_spads(struct tool_ctx *tc)
|
| 1234 | {
|
| 1235 | int sidx, pidx;
|
| 1236 |
|
| 1237 |
|
| 1238 | tc->inspad_cnt = ntb_spad_count(tc->ntb);
|
| 1239 | tc->inspads = devm_kcalloc(&tc->ntb->dev, tc->inspad_cnt,
|
| 1240 | sizeof(*tc->inspads), GFP_KERNEL);
|
| 1241 | if (tc->inspads == NULL)
|
| 1242 | return -ENOMEM;
|
| 1243 |
|
| 1244 | for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
|
| 1245 | tc->inspads[sidx].sidx = sidx;
|
| 1246 | tc->inspads[sidx].pidx = -1;
|
| 1247 | tc->inspads[sidx].tc = tc;
|
| 1248 | }
|
| 1249 |
|
| 1250 |
|
| 1251 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
|
| 1252 | tc->peers[pidx].outspad_cnt = ntb_spad_count(tc->ntb);
|
| 1253 | tc->peers[pidx].outspads =
|
| 1254 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outspad_cnt,
|
| 1255 | sizeof(*tc->peers[pidx].outspads), GFP_KERNEL);
|
| 1256 | if (tc->peers[pidx].outspads == NULL)
|
| 1257 | return -ENOMEM;
|
| 1258 |
|
| 1259 | for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
|
| 1260 | tc->peers[pidx].outspads[sidx].sidx = sidx;
|
| 1261 | tc->peers[pidx].outspads[sidx].pidx = pidx;
|
| 1262 | tc->peers[pidx].outspads[sidx].tc = tc;
|
| 1263 | }
|
| 1264 | }
|
| 1265 |
|
| 1266 | return 0;
|
| 1267 | }
|
| 1268 |
|
| 1269 | |
| 1270 | * Messages read/write methods
|
| 1271 | *==============================================================================
|
| 1272 |
|
| 1273 |
|
| 1274 | static ssize_t tool_inmsg_read(struct file *filep, char __user *ubuf,
|
| 1275 | size_t size, loff_t *offp)
|
| 1276 | {
|
| 1277 | struct tool_msg *msg = filep->private_data;
|
| 1278 | char buf[TOOL_BUF_LEN];
|
| 1279 | ssize_t pos;
|
| 1280 | u32 data;
|
| 1281 | int pidx;
|
| 1282 |
|
| 1283 | data = ntb_msg_read(msg->tc->ntb, &pidx, msg->midx);
|
| 1284 |
|
| 1285 | pos = scnprintf(buf, sizeof(buf), "0x%08x<-%d\n", data, pidx);
|
| 1286 |
|
| 1287 | return simple_read_from_buffer(ubuf, size, offp, buf, pos);
|
| 1288 | }
|
| 1289 |
|
| 1290 | static TOOL_FOPS_RDWR(tool_inmsg_fops,
|
| 1291 | tool_inmsg_read,
|
| 1292 | NULL);
|
| 1293 |
|
| 1294 | static ssize_t tool_outmsg_write(struct file *filep,
|
| 1295 | const char __user *ubuf,
|
| 1296 | size_t size, loff_t *offp)
|
| 1297 | {
|
| 1298 | struct tool_msg *msg = filep->private_data;
|
| 1299 | u32 val;
|
| 1300 | int ret;
|
| 1301 |
|
| 1302 | ret = kstrtou32_from_user(ubuf, size, 0, &val);
|
| 1303 | if (ret)
|
| 1304 | return ret;
|
| 1305 |
|
| 1306 | ret = ntb_peer_msg_write(msg->tc->ntb, msg->pidx, msg->midx, val);
|
| 1307 |
|
| 1308 | return ret ? : size;
|
| 1309 | }
|
| 1310 |
|
| 1311 | static TOOL_FOPS_RDWR(tool_outmsg_fops,
|
| 1312 | NULL,
|
| 1313 | tool_outmsg_write);
|
| 1314 |
|
| 1315 | static ssize_t tool_msg_sts_read(struct file *filep, char __user *ubuf,
|
| 1316 | size_t size, loff_t *offp)
|
| 1317 | {
|
| 1318 | struct tool_ctx *tc = filep->private_data;
|
| 1319 |
|
| 1320 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_read_sts);
|
| 1321 | }
|
| 1322 |
|
| 1323 | static ssize_t tool_msg_sts_write(struct file *filep, const char __user *ubuf,
|
| 1324 | size_t size, loff_t *offp)
|
| 1325 | {
|
| 1326 | struct tool_ctx *tc = filep->private_data;
|
| 1327 |
|
| 1328 | return tool_fn_write(tc, ubuf, size, offp, NULL,
|
| 1329 | tc->ntb->ops->msg_clear_sts);
|
| 1330 | }
|
| 1331 |
|
| 1332 | static TOOL_FOPS_RDWR(tool_msg_sts_fops,
|
| 1333 | tool_msg_sts_read,
|
| 1334 | tool_msg_sts_write);
|
| 1335 |
|
| 1336 | static ssize_t tool_msg_inbits_read(struct file *filep, char __user *ubuf,
|
| 1337 | size_t size, loff_t *offp)
|
| 1338 | {
|
| 1339 | struct tool_ctx *tc = filep->private_data;
|
| 1340 |
|
| 1341 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_inbits);
|
| 1342 | }
|
| 1343 |
|
| 1344 | static TOOL_FOPS_RDWR(tool_msg_inbits_fops,
|
| 1345 | tool_msg_inbits_read,
|
| 1346 | NULL);
|
| 1347 |
|
| 1348 | static ssize_t tool_msg_outbits_read(struct file *filep, char __user *ubuf,
|
| 1349 | size_t size, loff_t *offp)
|
| 1350 | {
|
| 1351 | struct tool_ctx *tc = filep->private_data;
|
| 1352 |
|
| 1353 | return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_outbits);
|
| 1354 | }
|
| 1355 |
|
| 1356 | static TOOL_FOPS_RDWR(tool_msg_outbits_fops,
|
| 1357 | tool_msg_outbits_read,
|
| 1358 | NULL);
|
| 1359 |
|
| 1360 | static ssize_t tool_msg_mask_write(struct file *filep, const char __user *ubuf,
|
| 1361 | size_t size, loff_t *offp)
|
| 1362 | {
|
| 1363 | struct tool_ctx *tc = filep->private_data;
|
| 1364 |
|
| 1365 | return tool_fn_write(tc, ubuf, size, offp,
|
| 1366 | tc->ntb->ops->msg_set_mask,
|
| 1367 | tc->ntb->ops->msg_clear_mask);
|
| 1368 | }
|
| 1369 |
|
| 1370 | static TOOL_FOPS_RDWR(tool_msg_mask_fops,
|
| 1371 | NULL,
|
| 1372 | tool_msg_mask_write);
|
| 1373 |
|
| 1374 | static ssize_t tool_msg_event_write(struct file *filep,
|
| 1375 | const char __user *ubuf,
|
| 1376 | size_t size, loff_t *offp)
|
| 1377 | {
|
| 1378 | struct tool_ctx *tc = filep->private_data;
|
| 1379 | u64 val;
|
| 1380 | int ret;
|
| 1381 |
|
| 1382 | ret = kstrtou64_from_user(ubuf, size, 0, &val);
|
| 1383 | if (ret)
|
| 1384 | return ret;
|
| 1385 |
|
| 1386 | if (wait_event_interruptible(tc->msg_wq,
|
| 1387 | ntb_msg_read_sts(tc->ntb) == val))
|
| 1388 | return -ERESTART;
|
| 1389 |
|
| 1390 | return size;
|
| 1391 | }
|
| 1392 |
|
| 1393 | static TOOL_FOPS_RDWR(tool_msg_event_fops,
|
| 1394 | NULL,
|
| 1395 | tool_msg_event_write);
|
| 1396 |
|
| 1397 | static int tool_init_msgs(struct tool_ctx *tc)
|
| 1398 | {
|
| 1399 | int midx, pidx;
|
| 1400 |
|
| 1401 |
|
| 1402 | tc->inmsg_cnt = ntb_msg_count(tc->ntb);
|
| 1403 | tc->inmsgs = devm_kcalloc(&tc->ntb->dev, tc->inmsg_cnt,
|
| 1404 | sizeof(*tc->inmsgs), GFP_KERNEL);
|
| 1405 | if (tc->inmsgs == NULL)
|
| 1406 | return -ENOMEM;
|
| 1407 |
|
| 1408 | for (midx = 0; midx < tc->inmsg_cnt; midx++) {
|
| 1409 | tc->inmsgs[midx].midx = midx;
|
| 1410 | tc->inmsgs[midx].pidx = -1;
|
| 1411 | tc->inmsgs[midx].tc = tc;
|
| 1412 | }
|
| 1413 |
|
| 1414 |
|
| 1415 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
|
| 1416 | tc->peers[pidx].outmsg_cnt = ntb_msg_count(tc->ntb);
|
| 1417 | tc->peers[pidx].outmsgs =
|
| 1418 | devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outmsg_cnt,
|
| 1419 | sizeof(*tc->peers[pidx].outmsgs), GFP_KERNEL);
|
| 1420 | if (tc->peers[pidx].outmsgs == NULL)
|
| 1421 | return -ENOMEM;
|
| 1422 |
|
| 1423 | for (midx = 0; midx < tc->peers[pidx].outmsg_cnt; midx++) {
|
| 1424 | tc->peers[pidx].outmsgs[midx].midx = midx;
|
| 1425 | tc->peers[pidx].outmsgs[midx].pidx = pidx;
|
| 1426 | tc->peers[pidx].outmsgs[midx].tc = tc;
|
| 1427 | }
|
| 1428 | }
|
| 1429 |
|
| 1430 | return 0;
|
| 1431 | }
|
| 1432 |
|
| 1433 | |
| 1434 | * Initialization methods
|
| 1435 | *==============================================================================
|
| 1436 |
|
| 1437 |
|
| 1438 | static struct tool_ctx *tool_create_data(struct ntb_dev *ntb)
|
| 1439 | {
|
| 1440 | struct tool_ctx *tc;
|
| 1441 |
|
| 1442 | tc = devm_kzalloc(&ntb->dev, sizeof(*tc), GFP_KERNEL);
|
| 1443 | if (tc == NULL)
|
| 1444 | return ERR_PTR(-ENOMEM);
|
| 1445 |
|
| 1446 | tc->ntb = ntb;
|
| 1447 | init_waitqueue_head(&tc->link_wq);
|
| 1448 | init_waitqueue_head(&tc->db_wq);
|
| 1449 | init_waitqueue_head(&tc->msg_wq);
|
| 1450 |
|
| 1451 | if (ntb_db_is_unsafe(ntb))
|
| 1452 | dev_dbg(&ntb->dev, "doorbell is unsafe\n");
|
| 1453 |
|
| 1454 | if (ntb_spad_is_unsafe(ntb))
|
| 1455 | dev_dbg(&ntb->dev, "scratchpad is unsafe\n");
|
| 1456 |
|
| 1457 | return tc;
|
| 1458 | }
|
| 1459 |
|
| 1460 | static void tool_clear_data(struct tool_ctx *tc)
|
| 1461 | {
|
| 1462 | wake_up(&tc->link_wq);
|
| 1463 | wake_up(&tc->db_wq);
|
| 1464 | wake_up(&tc->msg_wq);
|
| 1465 | }
|
| 1466 |
|
| 1467 | static int tool_init_ntb(struct tool_ctx *tc)
|
| 1468 | {
|
| 1469 | return ntb_set_ctx(tc->ntb, tc, &tool_ops);
|
| 1470 | }
|
| 1471 |
|
| 1472 | static void tool_clear_ntb(struct tool_ctx *tc)
|
| 1473 | {
|
| 1474 | ntb_clear_ctx(tc->ntb);
|
| 1475 | ntb_link_disable(tc->ntb);
|
| 1476 | }
|
| 1477 |
|
| 1478 | static void tool_setup_dbgfs(struct tool_ctx *tc)
|
| 1479 | {
|
| 1480 | int pidx, widx, sidx, midx;
|
| 1481 | char buf[TOOL_BUF_LEN];
|
| 1482 |
|
| 1483 |
|
| 1484 | if (!tool_dbgfs_topdir) {
|
| 1485 | tc->dbgfs_dir = NULL;
|
| 1486 | return;
|
| 1487 | }
|
| 1488 |
|
| 1489 | tc->dbgfs_dir = debugfs_create_dir(dev_name(&tc->ntb->dev),
|
| 1490 | tool_dbgfs_topdir);
|
| 1491 |
|
| 1492 | debugfs_create_file("port", 0600, tc->dbgfs_dir,
|
| 1493 | tc, &tool_port_fops);
|
| 1494 |
|
| 1495 | debugfs_create_file("link", 0600, tc->dbgfs_dir,
|
| 1496 | tc, &tool_link_fops);
|
| 1497 |
|
| 1498 | debugfs_create_file("db", 0600, tc->dbgfs_dir,
|
| 1499 | tc, &tool_db_fops);
|
| 1500 |
|
| 1501 | debugfs_create_file("db_valid_mask", 0600, tc->dbgfs_dir,
|
| 1502 | tc, &tool_db_valid_mask_fops);
|
| 1503 |
|
| 1504 | debugfs_create_file("db_mask", 0600, tc->dbgfs_dir,
|
| 1505 | tc, &tool_db_mask_fops);
|
| 1506 |
|
| 1507 | debugfs_create_file("db_event", 0600, tc->dbgfs_dir,
|
| 1508 | tc, &tool_db_event_fops);
|
| 1509 |
|
| 1510 | debugfs_create_file("peer_db", 0600, tc->dbgfs_dir,
|
| 1511 | tc, &tool_peer_db_fops);
|
| 1512 |
|
| 1513 | debugfs_create_file("peer_db_mask", 0600, tc->dbgfs_dir,
|
| 1514 | tc, &tool_peer_db_mask_fops);
|
| 1515 |
|
| 1516 | if (tc->inspad_cnt != 0) {
|
| 1517 | for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
|
| 1518 | snprintf(buf, sizeof(buf), "spad%d", sidx);
|
| 1519 |
|
| 1520 | debugfs_create_file(buf, 0600, tc->dbgfs_dir,
|
| 1521 | &tc->inspads[sidx], &tool_spad_fops);
|
| 1522 | }
|
| 1523 | }
|
| 1524 |
|
| 1525 | if (tc->inmsg_cnt != 0) {
|
| 1526 | for (midx = 0; midx < tc->inmsg_cnt; midx++) {
|
| 1527 | snprintf(buf, sizeof(buf), "msg%d", midx);
|
| 1528 | debugfs_create_file(buf, 0600, tc->dbgfs_dir,
|
| 1529 | &tc->inmsgs[midx], &tool_inmsg_fops);
|
| 1530 | }
|
| 1531 |
|
| 1532 | debugfs_create_file("msg_sts", 0600, tc->dbgfs_dir,
|
| 1533 | tc, &tool_msg_sts_fops);
|
| 1534 |
|
| 1535 | debugfs_create_file("msg_inbits", 0600, tc->dbgfs_dir,
|
| 1536 | tc, &tool_msg_inbits_fops);
|
| 1537 |
|
| 1538 | debugfs_create_file("msg_outbits", 0600, tc->dbgfs_dir,
|
| 1539 | tc, &tool_msg_outbits_fops);
|
| 1540 |
|
| 1541 | debugfs_create_file("msg_mask", 0600, tc->dbgfs_dir,
|
| 1542 | tc, &tool_msg_mask_fops);
|
| 1543 |
|
| 1544 | debugfs_create_file("msg_event", 0600, tc->dbgfs_dir,
|
| 1545 | tc, &tool_msg_event_fops);
|
| 1546 | }
|
| 1547 |
|
| 1548 | for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
|
| 1549 | snprintf(buf, sizeof(buf), "peer%d", pidx);
|
| 1550 | tc->peers[pidx].dbgfs_dir =
|
| 1551 | debugfs_create_dir(buf, tc->dbgfs_dir);
|
| 1552 |
|
| 1553 | debugfs_create_file("port", 0600,
|
| 1554 | tc->peers[pidx].dbgfs_dir,
|
| 1555 | &tc->peers[pidx], &tool_peer_port_fops);
|
| 1556 |
|
| 1557 | debugfs_create_file("link", 0200,
|
| 1558 | tc->peers[pidx].dbgfs_dir,
|
| 1559 | &tc->peers[pidx], &tool_peer_link_fops);
|
| 1560 |
|
| 1561 | debugfs_create_file("link_event", 0200,
|
| 1562 | tc->peers[pidx].dbgfs_dir,
|
| 1563 | &tc->peers[pidx], &tool_peer_link_event_fops);
|
| 1564 |
|
| 1565 | for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
|
| 1566 | snprintf(buf, sizeof(buf), "mw_trans%d", widx);
|
| 1567 | debugfs_create_file(buf, 0600,
|
| 1568 | tc->peers[pidx].dbgfs_dir,
|
| 1569 | &tc->peers[pidx].inmws[widx],
|
| 1570 | &tool_mw_trans_fops);
|
| 1571 | }
|
| 1572 |
|
| 1573 | for (widx = 0; widx < tc->peers[pidx].outmw_cnt; widx++) {
|
| 1574 | snprintf(buf, sizeof(buf), "peer_mw_trans%d", widx);
|
| 1575 | debugfs_create_file(buf, 0600,
|
| 1576 | tc->peers[pidx].dbgfs_dir,
|
| 1577 | &tc->peers[pidx].outmws[widx],
|
| 1578 | &tool_peer_mw_trans_fops);
|
| 1579 | }
|
| 1580 |
|
| 1581 | for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
|
| 1582 | snprintf(buf, sizeof(buf), "spad%d", sidx);
|
| 1583 |
|
| 1584 | debugfs_create_file(buf, 0600,
|
| 1585 | tc->peers[pidx].dbgfs_dir,
|
| 1586 | &tc->peers[pidx].outspads[sidx],
|
| 1587 | &tool_peer_spad_fops);
|
| 1588 | }
|
| 1589 |
|
| 1590 | for (midx = 0; midx < tc->peers[pidx].outmsg_cnt; midx++) {
|
| 1591 | snprintf(buf, sizeof(buf), "msg%d", midx);
|
| 1592 | debugfs_create_file(buf, 0600,
|
| 1593 | tc->peers[pidx].dbgfs_dir,
|
| 1594 | &tc->peers[pidx].outmsgs[midx],
|
| 1595 | &tool_outmsg_fops);
|
| 1596 | }
|
| 1597 | }
|
| 1598 | }
|
| 1599 |
|
| 1600 | static void tool_clear_dbgfs(struct tool_ctx *tc)
|
| 1601 | {
|
| 1602 | debugfs_remove_recursive(tc->dbgfs_dir);
|
| 1603 | }
|
| 1604 |
|
| 1605 | static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb)
|
| 1606 | {
|
| 1607 | struct tool_ctx *tc;
|
| 1608 | int ret;
|
| 1609 |
|
| 1610 | tc = tool_create_data(ntb);
|
| 1611 | if (IS_ERR(tc))
|
| 1612 | return PTR_ERR(tc);
|
| 1613 |
|
| 1614 | ret = tool_init_peers(tc);
|
| 1615 | if (ret != 0)
|
| 1616 | goto err_clear_data;
|
| 1617 |
|
| 1618 | ret = tool_init_mws(tc);
|
| 1619 | if (ret != 0)
|
| 1620 | goto err_clear_data;
|
| 1621 |
|
| 1622 | ret = tool_init_spads(tc);
|
| 1623 | if (ret != 0)
|
| 1624 | goto err_clear_mws;
|
| 1625 |
|
| 1626 | ret = tool_init_msgs(tc);
|
| 1627 | if (ret != 0)
|
| 1628 | goto err_clear_mws;
|
| 1629 |
|
| 1630 | ret = tool_init_ntb(tc);
|
| 1631 | if (ret != 0)
|
| 1632 | goto err_clear_mws;
|
| 1633 |
|
| 1634 | tool_setup_dbgfs(tc);
|
| 1635 |
|
| 1636 | return 0;
|
| 1637 |
|
| 1638 | err_clear_mws:
|
| 1639 | tool_clear_mws(tc);
|
| 1640 |
|
| 1641 | err_clear_data:
|
| 1642 | tool_clear_data(tc);
|
| 1643 |
|
| 1644 | return ret;
|
| 1645 | }
|
| 1646 |
|
| 1647 | static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb)
|
| 1648 | {
|
| 1649 | struct tool_ctx *tc = ntb->ctx;
|
| 1650 |
|
| 1651 | tool_clear_dbgfs(tc);
|
| 1652 |
|
| 1653 | tool_clear_ntb(tc);
|
| 1654 |
|
| 1655 | tool_clear_mws(tc);
|
| 1656 |
|
| 1657 | tool_clear_data(tc);
|
| 1658 | }
|
| 1659 |
|
| 1660 | static struct ntb_client tool_client = {
|
| 1661 | .ops = {
|
| 1662 | .probe = tool_probe,
|
| 1663 | .remove = tool_remove,
|
| 1664 | }
|
| 1665 | };
|
| 1666 |
|
| 1667 | static int __init tool_init(void)
|
| 1668 | {
|
| 1669 | int ret;
|
| 1670 |
|
| 1671 | if (debugfs_initialized())
|
| 1672 | tool_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
| 1673 |
|
| 1674 | ret = ntb_register_client(&tool_client);
|
| 1675 | if (ret)
|
| 1676 | debugfs_remove_recursive(tool_dbgfs_topdir);
|
| 1677 |
|
| 1678 | return ret;
|
| 1679 | }
|
| 1680 | module_init(tool_init);
|
| 1681 |
|
| 1682 | static void __exit tool_exit(void)
|
| 1683 | {
|
| 1684 | ntb_unregister_client(&tool_client);
|
| 1685 | debugfs_remove_recursive(tool_dbgfs_topdir);
|
| 1686 | }
|
| 1687 | module_exit(tool_exit);
|