162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 362306a36Sopenharmony_ci * redistributing this file, you may do so under either license. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * GPL LICENSE SUMMARY 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 862306a36Sopenharmony_ci * Copyright (C) 2017 T-Platforms All Rights Reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1162306a36Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 1262306a36Sopenharmony_ci * published by the Free Software Foundation. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1562306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1662306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1762306a36Sopenharmony_ci * General Public License for more details. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * BSD LICENSE 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 2262306a36Sopenharmony_ci * Copyright (C) 2017 T-Platforms All Rights Reserved. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 2562306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 2662306a36Sopenharmony_ci * are met: 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright 2962306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 3062306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above copy 3162306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 3262306a36Sopenharmony_ci * the documentation and/or other materials provided with the 3362306a36Sopenharmony_ci * distribution. 3462306a36Sopenharmony_ci * * Neither the name of Intel Corporation nor the names of its 3562306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived 3662306a36Sopenharmony_ci * from this software without specific prior written permission. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3962306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 4062306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 4162306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 4262306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 4362306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 4462306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 4562306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 4662306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 4762306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 4862306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * PCIe NTB Debugging Tool Linux driver 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * How to use this tool, by example. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * Assuming $DBG_DIR is something like: 5762306a36Sopenharmony_ci * '/sys/kernel/debug/ntb_tool/0000:00:03.0' 5862306a36Sopenharmony_ci * Suppose aside from local device there is at least one remote device 5962306a36Sopenharmony_ci * connected to NTB with index 0. 6062306a36Sopenharmony_ci *----------------------------------------------------------------------------- 6162306a36Sopenharmony_ci * Eg: check local/peer device information. 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * # Get local device port number 6462306a36Sopenharmony_ci * root@self# cat $DBG_DIR/port 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * # Check local device functionality 6762306a36Sopenharmony_ci * root@self# ls $DBG_DIR 6862306a36Sopenharmony_ci * db msg1 msg_sts peer4/ port 6962306a36Sopenharmony_ci * db_event msg2 peer0/ peer5/ spad0 7062306a36Sopenharmony_ci * db_mask msg3 peer1/ peer_db spad1 7162306a36Sopenharmony_ci * link msg_event peer2/ peer_db_mask spad2 7262306a36Sopenharmony_ci * msg0 msg_mask peer3/ peer_spad spad3 7362306a36Sopenharmony_ci * # As one can see it supports: 7462306a36Sopenharmony_ci * # 1) four inbound message registers 7562306a36Sopenharmony_ci * # 2) four inbound scratchpads 7662306a36Sopenharmony_ci * # 3) up to six peer devices 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * # Check peer device port number 7962306a36Sopenharmony_ci * root@self# cat $DBG_DIR/peer0/port 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * # Check peer device(s) functionality to be used 8262306a36Sopenharmony_ci * root@self# ls $DBG_DIR/peer0 8362306a36Sopenharmony_ci * link mw_trans0 mw_trans6 port 8462306a36Sopenharmony_ci * link_event mw_trans1 mw_trans7 spad0 8562306a36Sopenharmony_ci * msg0 mw_trans2 peer_mw_trans0 spad1 8662306a36Sopenharmony_ci * msg1 mw_trans3 peer_mw_trans1 spad2 8762306a36Sopenharmony_ci * msg2 mw_trans4 peer_mw_trans2 spad3 8862306a36Sopenharmony_ci * msg3 mw_trans5 peer_mw_trans3 8962306a36Sopenharmony_ci * # As one can see we got: 9062306a36Sopenharmony_ci * # 1) four outbound message registers 9162306a36Sopenharmony_ci * # 2) four outbound scratchpads 9262306a36Sopenharmony_ci * # 3) eight inbound memory windows 9362306a36Sopenharmony_ci * # 4) four outbound memory windows 9462306a36Sopenharmony_ci *----------------------------------------------------------------------------- 9562306a36Sopenharmony_ci * Eg: NTB link tests 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * # Set local link up/down 9862306a36Sopenharmony_ci * root@self# echo Y > $DBG_DIR/link 9962306a36Sopenharmony_ci * root@self# echo N > $DBG_DIR/link 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * # Check if link with peer device is up/down: 10262306a36Sopenharmony_ci * root@self# cat $DBG_DIR/peer0/link 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * # Block until the link is up/down 10562306a36Sopenharmony_ci * root@self# echo Y > $DBG_DIR/peer0/link_event 10662306a36Sopenharmony_ci * root@self# echo N > $DBG_DIR/peer0/link_event 10762306a36Sopenharmony_ci *----------------------------------------------------------------------------- 10862306a36Sopenharmony_ci * Eg: Doorbell registers tests (some functionality might be absent) 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * # Set/clear/get local doorbell 11162306a36Sopenharmony_ci * root@self# echo 's 1' > $DBG_DIR/db 11262306a36Sopenharmony_ci * root@self# echo 'c 1' > $DBG_DIR/db 11362306a36Sopenharmony_ci * root@self# cat $DBG_DIR/db 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * # Set/clear/get local doorbell mask 11662306a36Sopenharmony_ci * root@self# echo 's 1' > $DBG_DIR/db_mask 11762306a36Sopenharmony_ci * root@self# echo 'c 1' > $DBG_DIR/db_mask 11862306a36Sopenharmony_ci * root@self# cat $DBG_DIR/db_mask 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * # Ring/clear/get peer doorbell 12162306a36Sopenharmony_ci * root@peer# echo 's 1' > $DBG_DIR/peer_db 12262306a36Sopenharmony_ci * root@peer# echo 'c 1' > $DBG_DIR/peer_db 12362306a36Sopenharmony_ci * root@peer# cat $DBG_DIR/peer_db 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * # Set/clear/get peer doorbell mask 12662306a36Sopenharmony_ci * root@self# echo 's 1' > $DBG_DIR/peer_db_mask 12762306a36Sopenharmony_ci * root@self# echo 'c 1' > $DBG_DIR/peer_db_mask 12862306a36Sopenharmony_ci * root@self# cat $DBG_DIR/peer_db_mask 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * # Block until local doorbell is set with specified value 13162306a36Sopenharmony_ci * root@self# echo 1 > $DBG_DIR/db_event 13262306a36Sopenharmony_ci *----------------------------------------------------------------------------- 13362306a36Sopenharmony_ci * Eg: Message registers tests (functionality might be absent) 13462306a36Sopenharmony_ci * 13562306a36Sopenharmony_ci * # Set/clear/get in/out message registers status 13662306a36Sopenharmony_ci * root@self# echo 's 1' > $DBG_DIR/msg_sts 13762306a36Sopenharmony_ci * root@self# echo 'c 1' > $DBG_DIR/msg_sts 13862306a36Sopenharmony_ci * root@self# cat $DBG_DIR/msg_sts 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * # Set/clear in/out message registers mask 14162306a36Sopenharmony_ci * root@self# echo 's 1' > $DBG_DIR/msg_mask 14262306a36Sopenharmony_ci * root@self# echo 'c 1' > $DBG_DIR/msg_mask 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * # Get inbound message register #0 value and source of port index 14562306a36Sopenharmony_ci * root@self# cat $DBG_DIR/msg0 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * # Send some data to peer over outbound message register #0 14862306a36Sopenharmony_ci * root@self# echo 0x01020304 > $DBG_DIR/peer0/msg0 14962306a36Sopenharmony_ci *----------------------------------------------------------------------------- 15062306a36Sopenharmony_ci * Eg: Scratchpad registers tests (functionality might be absent) 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * # Write/read to/from local scratchpad register #0 15362306a36Sopenharmony_ci * root@peer# echo 0x01020304 > $DBG_DIR/spad0 15462306a36Sopenharmony_ci * root@peer# cat $DBG_DIR/spad0 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci * # Write/read to/from peer scratchpad register #0 15762306a36Sopenharmony_ci * root@peer# echo 0x01020304 > $DBG_DIR/peer0/spad0 15862306a36Sopenharmony_ci * root@peer# cat $DBG_DIR/peer0/spad0 15962306a36Sopenharmony_ci *----------------------------------------------------------------------------- 16062306a36Sopenharmony_ci * Eg: Memory windows tests 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * # Create inbound memory window buffer of specified size/get its base address 16362306a36Sopenharmony_ci * root@peer# echo 16384 > $DBG_DIR/peer0/mw_trans0 16462306a36Sopenharmony_ci * root@peer# cat $DBG_DIR/peer0/mw_trans0 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * # Write/read data to/from inbound memory window 16762306a36Sopenharmony_ci * root@peer# echo Hello > $DBG_DIR/peer0/mw0 16862306a36Sopenharmony_ci * root@peer# head -c 7 $DBG_DIR/peer0/mw0 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * # Map outbound memory window/check it settings (on peer device) 17162306a36Sopenharmony_ci * root@peer# echo 0xADD0BA5E:16384 > $DBG_DIR/peer0/peer_mw_trans0 17262306a36Sopenharmony_ci * root@peer# cat $DBG_DIR/peer0/peer_mw_trans0 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * # Write/read data to/from outbound memory window (on peer device) 17562306a36Sopenharmony_ci * root@peer# echo olleH > $DBG_DIR/peer0/peer_mw0 17662306a36Sopenharmony_ci * root@peer# head -c 7 $DBG_DIR/peer0/peer_mw0 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#include <linux/init.h> 18062306a36Sopenharmony_ci#include <linux/kernel.h> 18162306a36Sopenharmony_ci#include <linux/module.h> 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci#include <linux/debugfs.h> 18462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 18562306a36Sopenharmony_ci#include <linux/pci.h> 18662306a36Sopenharmony_ci#include <linux/slab.h> 18762306a36Sopenharmony_ci#include <linux/uaccess.h> 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci#include <linux/ntb.h> 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#define DRIVER_NAME "ntb_tool" 19262306a36Sopenharmony_ci#define DRIVER_VERSION "2.0" 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 19562306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 19662306a36Sopenharmony_ciMODULE_AUTHOR("Allen Hubbe <Allen.Hubbe@emc.com>"); 19762306a36Sopenharmony_ciMODULE_DESCRIPTION("PCIe NTB Debugging Tool"); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* 20062306a36Sopenharmony_ci * Inbound and outbound memory windows descriptor. Union members selection 20162306a36Sopenharmony_ci * depends on the MW type the structure describes. mm_base/dma_base are the 20262306a36Sopenharmony_ci * virtual and DMA address of an inbound MW. io_base/tr_base are the MMIO 20362306a36Sopenharmony_ci * mapped virtual and xlat addresses of an outbound MW respectively. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistruct tool_mw { 20662306a36Sopenharmony_ci int widx; 20762306a36Sopenharmony_ci int pidx; 20862306a36Sopenharmony_ci struct tool_ctx *tc; 20962306a36Sopenharmony_ci union { 21062306a36Sopenharmony_ci u8 *mm_base; 21162306a36Sopenharmony_ci u8 __iomem *io_base; 21262306a36Sopenharmony_ci }; 21362306a36Sopenharmony_ci union { 21462306a36Sopenharmony_ci dma_addr_t dma_base; 21562306a36Sopenharmony_ci u64 tr_base; 21662306a36Sopenharmony_ci }; 21762306a36Sopenharmony_ci resource_size_t size; 21862306a36Sopenharmony_ci struct dentry *dbgfs_file; 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* 22262306a36Sopenharmony_ci * Wrapper structure is used to distinguish the outbound MW peers reference 22362306a36Sopenharmony_ci * within the corresponding DebugFS directory IO operation. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_cistruct tool_mw_wrap { 22662306a36Sopenharmony_ci int pidx; 22762306a36Sopenharmony_ci struct tool_mw *mw; 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistruct tool_msg { 23162306a36Sopenharmony_ci int midx; 23262306a36Sopenharmony_ci int pidx; 23362306a36Sopenharmony_ci struct tool_ctx *tc; 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistruct tool_spad { 23762306a36Sopenharmony_ci int sidx; 23862306a36Sopenharmony_ci int pidx; 23962306a36Sopenharmony_ci struct tool_ctx *tc; 24062306a36Sopenharmony_ci}; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistruct tool_peer { 24362306a36Sopenharmony_ci int pidx; 24462306a36Sopenharmony_ci struct tool_ctx *tc; 24562306a36Sopenharmony_ci int inmw_cnt; 24662306a36Sopenharmony_ci struct tool_mw *inmws; 24762306a36Sopenharmony_ci int outmw_cnt; 24862306a36Sopenharmony_ci struct tool_mw_wrap *outmws; 24962306a36Sopenharmony_ci int outmsg_cnt; 25062306a36Sopenharmony_ci struct tool_msg *outmsgs; 25162306a36Sopenharmony_ci int outspad_cnt; 25262306a36Sopenharmony_ci struct tool_spad *outspads; 25362306a36Sopenharmony_ci struct dentry *dbgfs_dir; 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistruct tool_ctx { 25762306a36Sopenharmony_ci struct ntb_dev *ntb; 25862306a36Sopenharmony_ci wait_queue_head_t link_wq; 25962306a36Sopenharmony_ci wait_queue_head_t db_wq; 26062306a36Sopenharmony_ci wait_queue_head_t msg_wq; 26162306a36Sopenharmony_ci int outmw_cnt; 26262306a36Sopenharmony_ci struct tool_mw *outmws; 26362306a36Sopenharmony_ci int peer_cnt; 26462306a36Sopenharmony_ci struct tool_peer *peers; 26562306a36Sopenharmony_ci int inmsg_cnt; 26662306a36Sopenharmony_ci struct tool_msg *inmsgs; 26762306a36Sopenharmony_ci int inspad_cnt; 26862306a36Sopenharmony_ci struct tool_spad *inspads; 26962306a36Sopenharmony_ci struct dentry *dbgfs_dir; 27062306a36Sopenharmony_ci}; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci#define TOOL_FOPS_RDWR(__name, __read, __write) \ 27362306a36Sopenharmony_ci const struct file_operations __name = { \ 27462306a36Sopenharmony_ci .owner = THIS_MODULE, \ 27562306a36Sopenharmony_ci .open = simple_open, \ 27662306a36Sopenharmony_ci .read = __read, \ 27762306a36Sopenharmony_ci .write = __write, \ 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci#define TOOL_BUF_LEN 32 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic struct dentry *tool_dbgfs_topdir; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/*============================================================================== 28562306a36Sopenharmony_ci * NTB events handlers 28662306a36Sopenharmony_ci *============================================================================== 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void tool_link_event(void *ctx) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct tool_ctx *tc = ctx; 29262306a36Sopenharmony_ci enum ntb_speed speed; 29362306a36Sopenharmony_ci enum ntb_width width; 29462306a36Sopenharmony_ci int up; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci up = ntb_link_is_up(tc->ntb, &speed, &width); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n", 29962306a36Sopenharmony_ci up ? "up" : "down", speed, width); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci wake_up(&tc->link_wq); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void tool_db_event(void *ctx, int vec) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct tool_ctx *tc = ctx; 30762306a36Sopenharmony_ci u64 db_bits, db_mask; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci db_mask = ntb_db_vector_mask(tc->ntb, vec); 31062306a36Sopenharmony_ci db_bits = ntb_db_read(tc->ntb); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n", 31362306a36Sopenharmony_ci vec, db_mask, db_bits); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci wake_up(&tc->db_wq); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void tool_msg_event(void *ctx) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct tool_ctx *tc = ctx; 32162306a36Sopenharmony_ci u64 msg_sts; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci msg_sts = ntb_msg_read_sts(tc->ntb); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci dev_dbg(&tc->ntb->dev, "message bits %#llx\n", msg_sts); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci wake_up(&tc->msg_wq); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const struct ntb_ctx_ops tool_ops = { 33162306a36Sopenharmony_ci .link_event = tool_link_event, 33262306a36Sopenharmony_ci .db_event = tool_db_event, 33362306a36Sopenharmony_ci .msg_event = tool_msg_event 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/*============================================================================== 33762306a36Sopenharmony_ci * Common read/write methods 33862306a36Sopenharmony_ci *============================================================================== 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic ssize_t tool_fn_read(struct tool_ctx *tc, char __user *ubuf, 34262306a36Sopenharmony_ci size_t size, loff_t *offp, 34362306a36Sopenharmony_ci u64 (*fn_read)(struct ntb_dev *)) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci size_t buf_size; 34662306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 34762306a36Sopenharmony_ci ssize_t pos; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!fn_read) 35062306a36Sopenharmony_ci return -EINVAL; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci buf_size = min(size, sizeof(buf)); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci pos = scnprintf(buf, buf_size, "%#llx\n", fn_read(tc->ntb)); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, pos); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic ssize_t tool_fn_write(struct tool_ctx *tc, 36062306a36Sopenharmony_ci const char __user *ubuf, 36162306a36Sopenharmony_ci size_t size, loff_t *offp, 36262306a36Sopenharmony_ci int (*fn_set)(struct ntb_dev *, u64), 36362306a36Sopenharmony_ci int (*fn_clear)(struct ntb_dev *, u64)) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci char *buf, cmd; 36662306a36Sopenharmony_ci ssize_t ret; 36762306a36Sopenharmony_ci u64 bits; 36862306a36Sopenharmony_ci int n; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (*offp) 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci buf = memdup_user_nul(ubuf, size); 37462306a36Sopenharmony_ci if (IS_ERR(buf)) 37562306a36Sopenharmony_ci return PTR_ERR(buf); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci n = sscanf(buf, "%c %lli", &cmd, &bits); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci kfree(buf); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (n != 2) { 38262306a36Sopenharmony_ci ret = -EINVAL; 38362306a36Sopenharmony_ci } else if (cmd == 's') { 38462306a36Sopenharmony_ci if (!fn_set) 38562306a36Sopenharmony_ci ret = -EINVAL; 38662306a36Sopenharmony_ci else 38762306a36Sopenharmony_ci ret = fn_set(tc->ntb, bits); 38862306a36Sopenharmony_ci } else if (cmd == 'c') { 38962306a36Sopenharmony_ci if (!fn_clear) 39062306a36Sopenharmony_ci ret = -EINVAL; 39162306a36Sopenharmony_ci else 39262306a36Sopenharmony_ci ret = fn_clear(tc->ntb, bits); 39362306a36Sopenharmony_ci } else { 39462306a36Sopenharmony_ci ret = -EINVAL; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return ret ? : size; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/*============================================================================== 40162306a36Sopenharmony_ci * Port read/write methods 40262306a36Sopenharmony_ci *============================================================================== 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic ssize_t tool_port_read(struct file *filep, char __user *ubuf, 40662306a36Sopenharmony_ci size_t size, loff_t *offp) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 40962306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 41062306a36Sopenharmony_ci int pos; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "%d\n", ntb_port_number(tc->ntb)); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, pos); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_port_fops, 41862306a36Sopenharmony_ci tool_port_read, 41962306a36Sopenharmony_ci NULL); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic ssize_t tool_peer_port_read(struct file *filep, char __user *ubuf, 42262306a36Sopenharmony_ci size_t size, loff_t *offp) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct tool_peer *peer = filep->private_data; 42562306a36Sopenharmony_ci struct tool_ctx *tc = peer->tc; 42662306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 42762306a36Sopenharmony_ci int pos; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "%d\n", 43062306a36Sopenharmony_ci ntb_peer_port_number(tc->ntb, peer->pidx)); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, pos); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_port_fops, 43662306a36Sopenharmony_ci tool_peer_port_read, 43762306a36Sopenharmony_ci NULL); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int tool_init_peers(struct tool_ctx *tc) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int pidx; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci tc->peer_cnt = ntb_peer_port_count(tc->ntb); 44462306a36Sopenharmony_ci tc->peers = devm_kcalloc(&tc->ntb->dev, tc->peer_cnt, 44562306a36Sopenharmony_ci sizeof(*tc->peers), GFP_KERNEL); 44662306a36Sopenharmony_ci if (tc->peers == NULL) 44762306a36Sopenharmony_ci return -ENOMEM; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci for (pidx = 0; pidx < tc->peer_cnt; pidx++) { 45062306a36Sopenharmony_ci tc->peers[pidx].pidx = pidx; 45162306a36Sopenharmony_ci tc->peers[pidx].tc = tc; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/*============================================================================== 45862306a36Sopenharmony_ci * Link state read/write methods 45962306a36Sopenharmony_ci *============================================================================== 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic ssize_t tool_link_write(struct file *filep, const char __user *ubuf, 46362306a36Sopenharmony_ci size_t size, loff_t *offp) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 46662306a36Sopenharmony_ci bool val; 46762306a36Sopenharmony_ci int ret; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = kstrtobool_from_user(ubuf, size, &val); 47062306a36Sopenharmony_ci if (ret) 47162306a36Sopenharmony_ci return ret; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (val) 47462306a36Sopenharmony_ci ret = ntb_link_enable(tc->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci ret = ntb_link_disable(tc->ntb); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (ret) 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return size; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_link_fops, 48562306a36Sopenharmony_ci NULL, 48662306a36Sopenharmony_ci tool_link_write); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic ssize_t tool_peer_link_read(struct file *filep, char __user *ubuf, 48962306a36Sopenharmony_ci size_t size, loff_t *offp) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct tool_peer *peer = filep->private_data; 49262306a36Sopenharmony_ci struct tool_ctx *tc = peer->tc; 49362306a36Sopenharmony_ci char buf[3]; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (ntb_link_is_up(tc->ntb, NULL, NULL) & BIT(peer->pidx)) 49662306a36Sopenharmony_ci buf[0] = 'Y'; 49762306a36Sopenharmony_ci else 49862306a36Sopenharmony_ci buf[0] = 'N'; 49962306a36Sopenharmony_ci buf[1] = '\n'; 50062306a36Sopenharmony_ci buf[2] = '\0'; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, 2); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_link_fops, 50662306a36Sopenharmony_ci tool_peer_link_read, 50762306a36Sopenharmony_ci NULL); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic ssize_t tool_peer_link_event_write(struct file *filep, 51062306a36Sopenharmony_ci const char __user *ubuf, 51162306a36Sopenharmony_ci size_t size, loff_t *offp) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct tool_peer *peer = filep->private_data; 51462306a36Sopenharmony_ci struct tool_ctx *tc = peer->tc; 51562306a36Sopenharmony_ci u64 link_msk; 51662306a36Sopenharmony_ci bool val; 51762306a36Sopenharmony_ci int ret; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ret = kstrtobool_from_user(ubuf, size, &val); 52062306a36Sopenharmony_ci if (ret) 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci link_msk = BIT_ULL_MASK(peer->pidx); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (wait_event_interruptible(tc->link_wq, 52662306a36Sopenharmony_ci !!(ntb_link_is_up(tc->ntb, NULL, NULL) & link_msk) == val)) 52762306a36Sopenharmony_ci return -ERESTART; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return size; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_link_event_fops, 53362306a36Sopenharmony_ci NULL, 53462306a36Sopenharmony_ci tool_peer_link_event_write); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/*============================================================================== 53762306a36Sopenharmony_ci * Memory windows read/write/setting methods 53862306a36Sopenharmony_ci *============================================================================== 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic ssize_t tool_mw_read(struct file *filep, char __user *ubuf, 54262306a36Sopenharmony_ci size_t size, loff_t *offp) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct tool_mw *inmw = filep->private_data; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (inmw->mm_base == NULL) 54762306a36Sopenharmony_ci return -ENXIO; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, 55062306a36Sopenharmony_ci inmw->mm_base, inmw->size); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic ssize_t tool_mw_write(struct file *filep, const char __user *ubuf, 55462306a36Sopenharmony_ci size_t size, loff_t *offp) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct tool_mw *inmw = filep->private_data; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (inmw->mm_base == NULL) 55962306a36Sopenharmony_ci return -ENXIO; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return simple_write_to_buffer(inmw->mm_base, inmw->size, offp, 56262306a36Sopenharmony_ci ubuf, size); 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_mw_fops, 56662306a36Sopenharmony_ci tool_mw_read, 56762306a36Sopenharmony_ci tool_mw_write); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int tool_setup_mw(struct tool_ctx *tc, int pidx, int widx, 57062306a36Sopenharmony_ci size_t req_size) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci resource_size_t size, addr_align, size_align; 57362306a36Sopenharmony_ci struct tool_mw *inmw = &tc->peers[pidx].inmws[widx]; 57462306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 57562306a36Sopenharmony_ci int ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (inmw->mm_base != NULL) 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = ntb_mw_get_align(tc->ntb, pidx, widx, &addr_align, 58162306a36Sopenharmony_ci &size_align, &size); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci return ret; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci inmw->size = min_t(resource_size_t, req_size, size); 58662306a36Sopenharmony_ci inmw->size = round_up(inmw->size, addr_align); 58762306a36Sopenharmony_ci inmw->size = round_up(inmw->size, size_align); 58862306a36Sopenharmony_ci inmw->mm_base = dma_alloc_coherent(&tc->ntb->pdev->dev, inmw->size, 58962306a36Sopenharmony_ci &inmw->dma_base, GFP_KERNEL); 59062306a36Sopenharmony_ci if (!inmw->mm_base) 59162306a36Sopenharmony_ci return -ENOMEM; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!IS_ALIGNED(inmw->dma_base, addr_align)) { 59462306a36Sopenharmony_ci ret = -ENOMEM; 59562306a36Sopenharmony_ci goto err_free_dma; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ret = ntb_mw_set_trans(tc->ntb, pidx, widx, inmw->dma_base, inmw->size); 59962306a36Sopenharmony_ci if (ret) 60062306a36Sopenharmony_ci goto err_free_dma; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "mw%d", widx); 60362306a36Sopenharmony_ci inmw->dbgfs_file = debugfs_create_file(buf, 0600, 60462306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, inmw, 60562306a36Sopenharmony_ci &tool_mw_fops); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cierr_free_dma: 61062306a36Sopenharmony_ci dma_free_coherent(&tc->ntb->pdev->dev, inmw->size, inmw->mm_base, 61162306a36Sopenharmony_ci inmw->dma_base); 61262306a36Sopenharmony_ci inmw->mm_base = NULL; 61362306a36Sopenharmony_ci inmw->dma_base = 0; 61462306a36Sopenharmony_ci inmw->size = 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return ret; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic void tool_free_mw(struct tool_ctx *tc, int pidx, int widx) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct tool_mw *inmw = &tc->peers[pidx].inmws[widx]; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci debugfs_remove(inmw->dbgfs_file); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (inmw->mm_base != NULL) { 62662306a36Sopenharmony_ci ntb_mw_clear_trans(tc->ntb, pidx, widx); 62762306a36Sopenharmony_ci dma_free_coherent(&tc->ntb->pdev->dev, inmw->size, 62862306a36Sopenharmony_ci inmw->mm_base, inmw->dma_base); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci inmw->mm_base = NULL; 63262306a36Sopenharmony_ci inmw->dma_base = 0; 63362306a36Sopenharmony_ci inmw->size = 0; 63462306a36Sopenharmony_ci inmw->dbgfs_file = NULL; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic ssize_t tool_mw_trans_read(struct file *filep, char __user *ubuf, 63862306a36Sopenharmony_ci size_t size, loff_t *offp) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct tool_mw *inmw = filep->private_data; 64162306a36Sopenharmony_ci resource_size_t addr_align; 64262306a36Sopenharmony_ci resource_size_t size_align; 64362306a36Sopenharmony_ci resource_size_t size_max; 64462306a36Sopenharmony_ci ssize_t ret, off = 0; 64562306a36Sopenharmony_ci size_t buf_size; 64662306a36Sopenharmony_ci char *buf; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci buf_size = min_t(size_t, size, 512); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci buf = kmalloc(buf_size, GFP_KERNEL); 65162306a36Sopenharmony_ci if (!buf) 65262306a36Sopenharmony_ci return -ENOMEM; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ret = ntb_mw_get_align(inmw->tc->ntb, inmw->pidx, inmw->widx, 65562306a36Sopenharmony_ci &addr_align, &size_align, &size_max); 65662306a36Sopenharmony_ci if (ret) 65762306a36Sopenharmony_ci goto err; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 66062306a36Sopenharmony_ci "Inbound MW \t%d\n", 66162306a36Sopenharmony_ci inmw->widx); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 66462306a36Sopenharmony_ci "Port \t%d (%d)\n", 66562306a36Sopenharmony_ci ntb_peer_port_number(inmw->tc->ntb, inmw->pidx), 66662306a36Sopenharmony_ci inmw->pidx); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 66962306a36Sopenharmony_ci "Window Address \t0x%pK\n", inmw->mm_base); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 67262306a36Sopenharmony_ci "DMA Address \t%pad\n", 67362306a36Sopenharmony_ci &inmw->dma_base); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 67662306a36Sopenharmony_ci "Window Size \t%pap\n", 67762306a36Sopenharmony_ci &inmw->size); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 68062306a36Sopenharmony_ci "Alignment \t%pap\n", 68162306a36Sopenharmony_ci &addr_align); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 68462306a36Sopenharmony_ci "Size Alignment \t%pap\n", 68562306a36Sopenharmony_ci &size_align); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 68862306a36Sopenharmony_ci "Size Max \t%pap\n", 68962306a36Sopenharmony_ci &size_max); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci ret = simple_read_from_buffer(ubuf, size, offp, buf, off); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cierr: 69462306a36Sopenharmony_ci kfree(buf); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return ret; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic ssize_t tool_mw_trans_write(struct file *filep, const char __user *ubuf, 70062306a36Sopenharmony_ci size_t size, loff_t *offp) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct tool_mw *inmw = filep->private_data; 70362306a36Sopenharmony_ci unsigned int val; 70462306a36Sopenharmony_ci int ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci ret = kstrtouint_from_user(ubuf, size, 0, &val); 70762306a36Sopenharmony_ci if (ret) 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci tool_free_mw(inmw->tc, inmw->pidx, inmw->widx); 71162306a36Sopenharmony_ci if (val) { 71262306a36Sopenharmony_ci ret = tool_setup_mw(inmw->tc, inmw->pidx, inmw->widx, val); 71362306a36Sopenharmony_ci if (ret) 71462306a36Sopenharmony_ci return ret; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return size; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_mw_trans_fops, 72162306a36Sopenharmony_ci tool_mw_trans_read, 72262306a36Sopenharmony_ci tool_mw_trans_write); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic ssize_t tool_peer_mw_read(struct file *filep, char __user *ubuf, 72562306a36Sopenharmony_ci size_t size, loff_t *offp) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct tool_mw *outmw = filep->private_data; 72862306a36Sopenharmony_ci loff_t pos = *offp; 72962306a36Sopenharmony_ci ssize_t ret; 73062306a36Sopenharmony_ci void *buf; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (outmw->io_base == NULL) 73362306a36Sopenharmony_ci return -EIO; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (pos >= outmw->size || !size) 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (size > outmw->size - pos) 73962306a36Sopenharmony_ci size = outmw->size - pos; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci buf = kmalloc(size, GFP_KERNEL); 74262306a36Sopenharmony_ci if (!buf) 74362306a36Sopenharmony_ci return -ENOMEM; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci memcpy_fromio(buf, outmw->io_base + pos, size); 74662306a36Sopenharmony_ci ret = copy_to_user(ubuf, buf, size); 74762306a36Sopenharmony_ci if (ret == size) { 74862306a36Sopenharmony_ci ret = -EFAULT; 74962306a36Sopenharmony_ci goto err_free; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci size -= ret; 75362306a36Sopenharmony_ci *offp = pos + size; 75462306a36Sopenharmony_ci ret = size; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cierr_free: 75762306a36Sopenharmony_ci kfree(buf); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic ssize_t tool_peer_mw_write(struct file *filep, const char __user *ubuf, 76362306a36Sopenharmony_ci size_t size, loff_t *offp) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct tool_mw *outmw = filep->private_data; 76662306a36Sopenharmony_ci ssize_t ret; 76762306a36Sopenharmony_ci loff_t pos = *offp; 76862306a36Sopenharmony_ci void *buf; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (outmw->io_base == NULL) 77162306a36Sopenharmony_ci return -EIO; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (pos >= outmw->size || !size) 77462306a36Sopenharmony_ci return 0; 77562306a36Sopenharmony_ci if (size > outmw->size - pos) 77662306a36Sopenharmony_ci size = outmw->size - pos; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci buf = kmalloc(size, GFP_KERNEL); 77962306a36Sopenharmony_ci if (!buf) 78062306a36Sopenharmony_ci return -ENOMEM; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci ret = copy_from_user(buf, ubuf, size); 78362306a36Sopenharmony_ci if (ret == size) { 78462306a36Sopenharmony_ci ret = -EFAULT; 78562306a36Sopenharmony_ci goto err_free; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci size -= ret; 78962306a36Sopenharmony_ci *offp = pos + size; 79062306a36Sopenharmony_ci ret = size; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci memcpy_toio(outmw->io_base + pos, buf, size); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cierr_free: 79562306a36Sopenharmony_ci kfree(buf); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci return ret; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_mw_fops, 80162306a36Sopenharmony_ci tool_peer_mw_read, 80262306a36Sopenharmony_ci tool_peer_mw_write); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int tool_setup_peer_mw(struct tool_ctx *tc, int pidx, int widx, 80562306a36Sopenharmony_ci u64 req_addr, size_t req_size) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci struct tool_mw *outmw = &tc->outmws[widx]; 80862306a36Sopenharmony_ci resource_size_t map_size; 80962306a36Sopenharmony_ci phys_addr_t map_base; 81062306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 81162306a36Sopenharmony_ci int ret; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (outmw->io_base != NULL) 81462306a36Sopenharmony_ci return 0; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = ntb_peer_mw_get_addr(tc->ntb, widx, &map_base, &map_size); 81762306a36Sopenharmony_ci if (ret) 81862306a36Sopenharmony_ci return ret; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci ret = ntb_peer_mw_set_trans(tc->ntb, pidx, widx, req_addr, req_size); 82162306a36Sopenharmony_ci if (ret) 82262306a36Sopenharmony_ci return ret; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci outmw->io_base = ioremap_wc(map_base, map_size); 82562306a36Sopenharmony_ci if (outmw->io_base == NULL) { 82662306a36Sopenharmony_ci ret = -EFAULT; 82762306a36Sopenharmony_ci goto err_clear_trans; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci outmw->tr_base = req_addr; 83162306a36Sopenharmony_ci outmw->size = req_size; 83262306a36Sopenharmony_ci outmw->pidx = pidx; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "peer_mw%d", widx); 83562306a36Sopenharmony_ci outmw->dbgfs_file = debugfs_create_file(buf, 0600, 83662306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, outmw, 83762306a36Sopenharmony_ci &tool_peer_mw_fops); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cierr_clear_trans: 84262306a36Sopenharmony_ci ntb_peer_mw_clear_trans(tc->ntb, pidx, widx); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return ret; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic void tool_free_peer_mw(struct tool_ctx *tc, int widx) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct tool_mw *outmw = &tc->outmws[widx]; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci debugfs_remove(outmw->dbgfs_file); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (outmw->io_base != NULL) { 85462306a36Sopenharmony_ci iounmap(tc->outmws[widx].io_base); 85562306a36Sopenharmony_ci ntb_peer_mw_clear_trans(tc->ntb, outmw->pidx, widx); 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci outmw->io_base = NULL; 85962306a36Sopenharmony_ci outmw->tr_base = 0; 86062306a36Sopenharmony_ci outmw->size = 0; 86162306a36Sopenharmony_ci outmw->pidx = -1; 86262306a36Sopenharmony_ci outmw->dbgfs_file = NULL; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic ssize_t tool_peer_mw_trans_read(struct file *filep, char __user *ubuf, 86662306a36Sopenharmony_ci size_t size, loff_t *offp) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci struct tool_mw_wrap *outmw_wrap = filep->private_data; 86962306a36Sopenharmony_ci struct tool_mw *outmw = outmw_wrap->mw; 87062306a36Sopenharmony_ci resource_size_t map_size; 87162306a36Sopenharmony_ci phys_addr_t map_base; 87262306a36Sopenharmony_ci ssize_t off = 0; 87362306a36Sopenharmony_ci size_t buf_size; 87462306a36Sopenharmony_ci char *buf; 87562306a36Sopenharmony_ci int ret; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci ret = ntb_peer_mw_get_addr(outmw->tc->ntb, outmw->widx, 87862306a36Sopenharmony_ci &map_base, &map_size); 87962306a36Sopenharmony_ci if (ret) 88062306a36Sopenharmony_ci return ret; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci buf_size = min_t(size_t, size, 512); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci buf = kmalloc(buf_size, GFP_KERNEL); 88562306a36Sopenharmony_ci if (!buf) 88662306a36Sopenharmony_ci return -ENOMEM; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 88962306a36Sopenharmony_ci "Outbound MW: \t%d\n", outmw->widx); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (outmw->io_base != NULL) { 89262306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 89362306a36Sopenharmony_ci "Port attached \t%d (%d)\n", 89462306a36Sopenharmony_ci ntb_peer_port_number(outmw->tc->ntb, outmw->pidx), 89562306a36Sopenharmony_ci outmw->pidx); 89662306a36Sopenharmony_ci } else { 89762306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 89862306a36Sopenharmony_ci "Port attached \t-1 (-1)\n"); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 90262306a36Sopenharmony_ci "Virtual address \t0x%pK\n", outmw->io_base); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 90562306a36Sopenharmony_ci "Phys Address \t%pap\n", &map_base); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 90862306a36Sopenharmony_ci "Mapping Size \t%pap\n", &map_size); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 91162306a36Sopenharmony_ci "Translation Address \t0x%016llx\n", outmw->tr_base); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 91462306a36Sopenharmony_ci "Window Size \t%pap\n", &outmw->size); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci ret = simple_read_from_buffer(ubuf, size, offp, buf, off); 91762306a36Sopenharmony_ci kfree(buf); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return ret; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic ssize_t tool_peer_mw_trans_write(struct file *filep, 92362306a36Sopenharmony_ci const char __user *ubuf, 92462306a36Sopenharmony_ci size_t size, loff_t *offp) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct tool_mw_wrap *outmw_wrap = filep->private_data; 92762306a36Sopenharmony_ci struct tool_mw *outmw = outmw_wrap->mw; 92862306a36Sopenharmony_ci size_t buf_size, wsize; 92962306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 93062306a36Sopenharmony_ci int ret, n; 93162306a36Sopenharmony_ci u64 addr; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci buf_size = min(size, (sizeof(buf) - 1)); 93462306a36Sopenharmony_ci if (copy_from_user(buf, ubuf, buf_size)) 93562306a36Sopenharmony_ci return -EFAULT; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci buf[buf_size] = '\0'; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci n = sscanf(buf, "%lli:%zi", &addr, &wsize); 94062306a36Sopenharmony_ci if (n != 2) 94162306a36Sopenharmony_ci return -EINVAL; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci tool_free_peer_mw(outmw->tc, outmw->widx); 94462306a36Sopenharmony_ci if (wsize) { 94562306a36Sopenharmony_ci ret = tool_setup_peer_mw(outmw->tc, outmw_wrap->pidx, 94662306a36Sopenharmony_ci outmw->widx, addr, wsize); 94762306a36Sopenharmony_ci if (ret) 94862306a36Sopenharmony_ci return ret; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return size; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_mw_trans_fops, 95562306a36Sopenharmony_ci tool_peer_mw_trans_read, 95662306a36Sopenharmony_ci tool_peer_mw_trans_write); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cistatic int tool_init_mws(struct tool_ctx *tc) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci int widx, pidx; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Initialize outbound memory windows */ 96362306a36Sopenharmony_ci tc->outmw_cnt = ntb_peer_mw_count(tc->ntb); 96462306a36Sopenharmony_ci tc->outmws = devm_kcalloc(&tc->ntb->dev, tc->outmw_cnt, 96562306a36Sopenharmony_ci sizeof(*tc->outmws), GFP_KERNEL); 96662306a36Sopenharmony_ci if (tc->outmws == NULL) 96762306a36Sopenharmony_ci return -ENOMEM; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci for (widx = 0; widx < tc->outmw_cnt; widx++) { 97062306a36Sopenharmony_ci tc->outmws[widx].widx = widx; 97162306a36Sopenharmony_ci tc->outmws[widx].pidx = -1; 97262306a36Sopenharmony_ci tc->outmws[widx].tc = tc; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* Initialize inbound memory windows and outbound MWs wrapper */ 97662306a36Sopenharmony_ci for (pidx = 0; pidx < tc->peer_cnt; pidx++) { 97762306a36Sopenharmony_ci tc->peers[pidx].inmw_cnt = ntb_mw_count(tc->ntb, pidx); 97862306a36Sopenharmony_ci tc->peers[pidx].inmws = 97962306a36Sopenharmony_ci devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].inmw_cnt, 98062306a36Sopenharmony_ci sizeof(*tc->peers[pidx].inmws), GFP_KERNEL); 98162306a36Sopenharmony_ci if (tc->peers[pidx].inmws == NULL) 98262306a36Sopenharmony_ci return -ENOMEM; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) { 98562306a36Sopenharmony_ci tc->peers[pidx].inmws[widx].widx = widx; 98662306a36Sopenharmony_ci tc->peers[pidx].inmws[widx].pidx = pidx; 98762306a36Sopenharmony_ci tc->peers[pidx].inmws[widx].tc = tc; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci tc->peers[pidx].outmw_cnt = ntb_peer_mw_count(tc->ntb); 99162306a36Sopenharmony_ci tc->peers[pidx].outmws = 99262306a36Sopenharmony_ci devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outmw_cnt, 99362306a36Sopenharmony_ci sizeof(*tc->peers[pidx].outmws), GFP_KERNEL); 99462306a36Sopenharmony_ci if (tc->peers[pidx].outmws == NULL) 99562306a36Sopenharmony_ci return -ENOMEM; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci for (widx = 0; widx < tc->peers[pidx].outmw_cnt; widx++) { 99862306a36Sopenharmony_ci tc->peers[pidx].outmws[widx].pidx = pidx; 99962306a36Sopenharmony_ci tc->peers[pidx].outmws[widx].mw = &tc->outmws[widx]; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void tool_clear_mws(struct tool_ctx *tc) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci int widx, pidx; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* Free outbound memory windows */ 101162306a36Sopenharmony_ci for (widx = 0; widx < tc->outmw_cnt; widx++) 101262306a36Sopenharmony_ci tool_free_peer_mw(tc, widx); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* Free outbound memory windows */ 101562306a36Sopenharmony_ci for (pidx = 0; pidx < tc->peer_cnt; pidx++) 101662306a36Sopenharmony_ci for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) 101762306a36Sopenharmony_ci tool_free_mw(tc, pidx, widx); 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci/*============================================================================== 102162306a36Sopenharmony_ci * Doorbell read/write methods 102262306a36Sopenharmony_ci *============================================================================== 102362306a36Sopenharmony_ci */ 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic ssize_t tool_db_read(struct file *filep, char __user *ubuf, 102662306a36Sopenharmony_ci size_t size, loff_t *offp) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_read); 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic ssize_t tool_db_write(struct file *filep, const char __user *ubuf, 103462306a36Sopenharmony_ci size_t size, loff_t *offp) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->db_set, 103962306a36Sopenharmony_ci tc->ntb->ops->db_clear); 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_db_fops, 104362306a36Sopenharmony_ci tool_db_read, 104462306a36Sopenharmony_ci tool_db_write); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic ssize_t tool_db_valid_mask_read(struct file *filep, char __user *ubuf, 104762306a36Sopenharmony_ci size_t size, loff_t *offp) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_valid_mask); 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_db_valid_mask_fops, 105562306a36Sopenharmony_ci tool_db_valid_mask_read, 105662306a36Sopenharmony_ci NULL); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic ssize_t tool_db_mask_read(struct file *filep, char __user *ubuf, 105962306a36Sopenharmony_ci size_t size, loff_t *offp) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->db_read_mask); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic ssize_t tool_db_mask_write(struct file *filep, const char __user *ubuf, 106762306a36Sopenharmony_ci size_t size, loff_t *offp) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->db_set_mask, 107262306a36Sopenharmony_ci tc->ntb->ops->db_clear_mask); 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_db_mask_fops, 107662306a36Sopenharmony_ci tool_db_mask_read, 107762306a36Sopenharmony_ci tool_db_mask_write); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf, 108062306a36Sopenharmony_ci size_t size, loff_t *offp) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->peer_db_read); 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf, 108862306a36Sopenharmony_ci size_t size, loff_t *offp) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return tool_fn_write(tc, ubuf, size, offp, tc->ntb->ops->peer_db_set, 109362306a36Sopenharmony_ci tc->ntb->ops->peer_db_clear); 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_db_fops, 109762306a36Sopenharmony_ci tool_peer_db_read, 109862306a36Sopenharmony_ci tool_peer_db_write); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_cistatic ssize_t tool_peer_db_mask_read(struct file *filep, char __user *ubuf, 110162306a36Sopenharmony_ci size_t size, loff_t *offp) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, 110662306a36Sopenharmony_ci tc->ntb->ops->peer_db_read_mask); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic ssize_t tool_peer_db_mask_write(struct file *filep, 111062306a36Sopenharmony_ci const char __user *ubuf, 111162306a36Sopenharmony_ci size_t size, loff_t *offp) 111262306a36Sopenharmony_ci{ 111362306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci return tool_fn_write(tc, ubuf, size, offp, 111662306a36Sopenharmony_ci tc->ntb->ops->peer_db_set_mask, 111762306a36Sopenharmony_ci tc->ntb->ops->peer_db_clear_mask); 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_db_mask_fops, 112162306a36Sopenharmony_ci tool_peer_db_mask_read, 112262306a36Sopenharmony_ci tool_peer_db_mask_write); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic ssize_t tool_db_event_write(struct file *filep, 112562306a36Sopenharmony_ci const char __user *ubuf, 112662306a36Sopenharmony_ci size_t size, loff_t *offp) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 112962306a36Sopenharmony_ci u64 val; 113062306a36Sopenharmony_ci int ret; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci ret = kstrtou64_from_user(ubuf, size, 0, &val); 113362306a36Sopenharmony_ci if (ret) 113462306a36Sopenharmony_ci return ret; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (wait_event_interruptible(tc->db_wq, ntb_db_read(tc->ntb) == val)) 113762306a36Sopenharmony_ci return -ERESTART; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci return size; 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_db_event_fops, 114362306a36Sopenharmony_ci NULL, 114462306a36Sopenharmony_ci tool_db_event_write); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci/*============================================================================== 114762306a36Sopenharmony_ci * Scratchpads read/write methods 114862306a36Sopenharmony_ci *============================================================================== 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_cistatic ssize_t tool_spad_read(struct file *filep, char __user *ubuf, 115262306a36Sopenharmony_ci size_t size, loff_t *offp) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct tool_spad *spad = filep->private_data; 115562306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 115662306a36Sopenharmony_ci ssize_t pos; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (!spad->tc->ntb->ops->spad_read) 115962306a36Sopenharmony_ci return -EINVAL; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "%#x\n", 116262306a36Sopenharmony_ci ntb_spad_read(spad->tc->ntb, spad->sidx)); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, pos); 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic ssize_t tool_spad_write(struct file *filep, const char __user *ubuf, 116862306a36Sopenharmony_ci size_t size, loff_t *offp) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct tool_spad *spad = filep->private_data; 117162306a36Sopenharmony_ci u32 val; 117262306a36Sopenharmony_ci int ret; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (!spad->tc->ntb->ops->spad_write) { 117562306a36Sopenharmony_ci dev_dbg(&spad->tc->ntb->dev, "no spad write fn\n"); 117662306a36Sopenharmony_ci return -EINVAL; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci ret = kstrtou32_from_user(ubuf, size, 0, &val); 118062306a36Sopenharmony_ci if (ret) 118162306a36Sopenharmony_ci return ret; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci ret = ntb_spad_write(spad->tc->ntb, spad->sidx, val); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci return ret ?: size; 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_spad_fops, 118962306a36Sopenharmony_ci tool_spad_read, 119062306a36Sopenharmony_ci tool_spad_write); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf, 119362306a36Sopenharmony_ci size_t size, loff_t *offp) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci struct tool_spad *spad = filep->private_data; 119662306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 119762306a36Sopenharmony_ci ssize_t pos; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (!spad->tc->ntb->ops->peer_spad_read) 120062306a36Sopenharmony_ci return -EINVAL; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "%#x\n", 120362306a36Sopenharmony_ci ntb_peer_spad_read(spad->tc->ntb, spad->pidx, spad->sidx)); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, pos); 120662306a36Sopenharmony_ci} 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf, 120962306a36Sopenharmony_ci size_t size, loff_t *offp) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci struct tool_spad *spad = filep->private_data; 121262306a36Sopenharmony_ci u32 val; 121362306a36Sopenharmony_ci int ret; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (!spad->tc->ntb->ops->peer_spad_write) { 121662306a36Sopenharmony_ci dev_dbg(&spad->tc->ntb->dev, "no spad write fn\n"); 121762306a36Sopenharmony_ci return -EINVAL; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci ret = kstrtou32_from_user(ubuf, size, 0, &val); 122162306a36Sopenharmony_ci if (ret) 122262306a36Sopenharmony_ci return ret; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci ret = ntb_peer_spad_write(spad->tc->ntb, spad->pidx, spad->sidx, val); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci return ret ?: size; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_peer_spad_fops, 123062306a36Sopenharmony_ci tool_peer_spad_read, 123162306a36Sopenharmony_ci tool_peer_spad_write); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic int tool_init_spads(struct tool_ctx *tc) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci int sidx, pidx; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* Initialize inbound scratchpad structures */ 123862306a36Sopenharmony_ci tc->inspad_cnt = ntb_spad_count(tc->ntb); 123962306a36Sopenharmony_ci tc->inspads = devm_kcalloc(&tc->ntb->dev, tc->inspad_cnt, 124062306a36Sopenharmony_ci sizeof(*tc->inspads), GFP_KERNEL); 124162306a36Sopenharmony_ci if (tc->inspads == NULL) 124262306a36Sopenharmony_ci return -ENOMEM; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci for (sidx = 0; sidx < tc->inspad_cnt; sidx++) { 124562306a36Sopenharmony_ci tc->inspads[sidx].sidx = sidx; 124662306a36Sopenharmony_ci tc->inspads[sidx].pidx = -1; 124762306a36Sopenharmony_ci tc->inspads[sidx].tc = tc; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* Initialize outbound scratchpad structures */ 125162306a36Sopenharmony_ci for (pidx = 0; pidx < tc->peer_cnt; pidx++) { 125262306a36Sopenharmony_ci tc->peers[pidx].outspad_cnt = ntb_spad_count(tc->ntb); 125362306a36Sopenharmony_ci tc->peers[pidx].outspads = 125462306a36Sopenharmony_ci devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outspad_cnt, 125562306a36Sopenharmony_ci sizeof(*tc->peers[pidx].outspads), GFP_KERNEL); 125662306a36Sopenharmony_ci if (tc->peers[pidx].outspads == NULL) 125762306a36Sopenharmony_ci return -ENOMEM; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) { 126062306a36Sopenharmony_ci tc->peers[pidx].outspads[sidx].sidx = sidx; 126162306a36Sopenharmony_ci tc->peers[pidx].outspads[sidx].pidx = pidx; 126262306a36Sopenharmony_ci tc->peers[pidx].outspads[sidx].tc = tc; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return 0; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci/*============================================================================== 127062306a36Sopenharmony_ci * Messages read/write methods 127162306a36Sopenharmony_ci *============================================================================== 127262306a36Sopenharmony_ci */ 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic ssize_t tool_inmsg_read(struct file *filep, char __user *ubuf, 127562306a36Sopenharmony_ci size_t size, loff_t *offp) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct tool_msg *msg = filep->private_data; 127862306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 127962306a36Sopenharmony_ci ssize_t pos; 128062306a36Sopenharmony_ci u32 data; 128162306a36Sopenharmony_ci int pidx; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci data = ntb_msg_read(msg->tc->ntb, &pidx, msg->midx); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci pos = scnprintf(buf, sizeof(buf), "0x%08x<-%d\n", data, pidx); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci return simple_read_from_buffer(ubuf, size, offp, buf, pos); 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_inmsg_fops, 129162306a36Sopenharmony_ci tool_inmsg_read, 129262306a36Sopenharmony_ci NULL); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic ssize_t tool_outmsg_write(struct file *filep, 129562306a36Sopenharmony_ci const char __user *ubuf, 129662306a36Sopenharmony_ci size_t size, loff_t *offp) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct tool_msg *msg = filep->private_data; 129962306a36Sopenharmony_ci u32 val; 130062306a36Sopenharmony_ci int ret; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci ret = kstrtou32_from_user(ubuf, size, 0, &val); 130362306a36Sopenharmony_ci if (ret) 130462306a36Sopenharmony_ci return ret; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci ret = ntb_peer_msg_write(msg->tc->ntb, msg->pidx, msg->midx, val); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci return ret ? : size; 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_outmsg_fops, 131262306a36Sopenharmony_ci NULL, 131362306a36Sopenharmony_ci tool_outmsg_write); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic ssize_t tool_msg_sts_read(struct file *filep, char __user *ubuf, 131662306a36Sopenharmony_ci size_t size, loff_t *offp) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_read_sts); 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistatic ssize_t tool_msg_sts_write(struct file *filep, const char __user *ubuf, 132462306a36Sopenharmony_ci size_t size, loff_t *offp) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci return tool_fn_write(tc, ubuf, size, offp, NULL, 132962306a36Sopenharmony_ci tc->ntb->ops->msg_clear_sts); 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_msg_sts_fops, 133362306a36Sopenharmony_ci tool_msg_sts_read, 133462306a36Sopenharmony_ci tool_msg_sts_write); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic ssize_t tool_msg_inbits_read(struct file *filep, char __user *ubuf, 133762306a36Sopenharmony_ci size_t size, loff_t *offp) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_inbits); 134262306a36Sopenharmony_ci} 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_msg_inbits_fops, 134562306a36Sopenharmony_ci tool_msg_inbits_read, 134662306a36Sopenharmony_ci NULL); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic ssize_t tool_msg_outbits_read(struct file *filep, char __user *ubuf, 134962306a36Sopenharmony_ci size_t size, loff_t *offp) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return tool_fn_read(tc, ubuf, size, offp, tc->ntb->ops->msg_outbits); 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_msg_outbits_fops, 135762306a36Sopenharmony_ci tool_msg_outbits_read, 135862306a36Sopenharmony_ci NULL); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic ssize_t tool_msg_mask_write(struct file *filep, const char __user *ubuf, 136162306a36Sopenharmony_ci size_t size, loff_t *offp) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci return tool_fn_write(tc, ubuf, size, offp, 136662306a36Sopenharmony_ci tc->ntb->ops->msg_set_mask, 136762306a36Sopenharmony_ci tc->ntb->ops->msg_clear_mask); 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_msg_mask_fops, 137162306a36Sopenharmony_ci NULL, 137262306a36Sopenharmony_ci tool_msg_mask_write); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_cistatic ssize_t tool_msg_event_write(struct file *filep, 137562306a36Sopenharmony_ci const char __user *ubuf, 137662306a36Sopenharmony_ci size_t size, loff_t *offp) 137762306a36Sopenharmony_ci{ 137862306a36Sopenharmony_ci struct tool_ctx *tc = filep->private_data; 137962306a36Sopenharmony_ci u64 val; 138062306a36Sopenharmony_ci int ret; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci ret = kstrtou64_from_user(ubuf, size, 0, &val); 138362306a36Sopenharmony_ci if (ret) 138462306a36Sopenharmony_ci return ret; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (wait_event_interruptible(tc->msg_wq, 138762306a36Sopenharmony_ci ntb_msg_read_sts(tc->ntb) == val)) 138862306a36Sopenharmony_ci return -ERESTART; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return size; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic TOOL_FOPS_RDWR(tool_msg_event_fops, 139462306a36Sopenharmony_ci NULL, 139562306a36Sopenharmony_ci tool_msg_event_write); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic int tool_init_msgs(struct tool_ctx *tc) 139862306a36Sopenharmony_ci{ 139962306a36Sopenharmony_ci int midx, pidx; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci /* Initialize inbound message structures */ 140262306a36Sopenharmony_ci tc->inmsg_cnt = ntb_msg_count(tc->ntb); 140362306a36Sopenharmony_ci tc->inmsgs = devm_kcalloc(&tc->ntb->dev, tc->inmsg_cnt, 140462306a36Sopenharmony_ci sizeof(*tc->inmsgs), GFP_KERNEL); 140562306a36Sopenharmony_ci if (tc->inmsgs == NULL) 140662306a36Sopenharmony_ci return -ENOMEM; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci for (midx = 0; midx < tc->inmsg_cnt; midx++) { 140962306a36Sopenharmony_ci tc->inmsgs[midx].midx = midx; 141062306a36Sopenharmony_ci tc->inmsgs[midx].pidx = -1; 141162306a36Sopenharmony_ci tc->inmsgs[midx].tc = tc; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* Initialize outbound message structures */ 141562306a36Sopenharmony_ci for (pidx = 0; pidx < tc->peer_cnt; pidx++) { 141662306a36Sopenharmony_ci tc->peers[pidx].outmsg_cnt = ntb_msg_count(tc->ntb); 141762306a36Sopenharmony_ci tc->peers[pidx].outmsgs = 141862306a36Sopenharmony_ci devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outmsg_cnt, 141962306a36Sopenharmony_ci sizeof(*tc->peers[pidx].outmsgs), GFP_KERNEL); 142062306a36Sopenharmony_ci if (tc->peers[pidx].outmsgs == NULL) 142162306a36Sopenharmony_ci return -ENOMEM; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci for (midx = 0; midx < tc->peers[pidx].outmsg_cnt; midx++) { 142462306a36Sopenharmony_ci tc->peers[pidx].outmsgs[midx].midx = midx; 142562306a36Sopenharmony_ci tc->peers[pidx].outmsgs[midx].pidx = pidx; 142662306a36Sopenharmony_ci tc->peers[pidx].outmsgs[midx].tc = tc; 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci return 0; 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci/*============================================================================== 143462306a36Sopenharmony_ci * Initialization methods 143562306a36Sopenharmony_ci *============================================================================== 143662306a36Sopenharmony_ci */ 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic struct tool_ctx *tool_create_data(struct ntb_dev *ntb) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci struct tool_ctx *tc; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci tc = devm_kzalloc(&ntb->dev, sizeof(*tc), GFP_KERNEL); 144362306a36Sopenharmony_ci if (tc == NULL) 144462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci tc->ntb = ntb; 144762306a36Sopenharmony_ci init_waitqueue_head(&tc->link_wq); 144862306a36Sopenharmony_ci init_waitqueue_head(&tc->db_wq); 144962306a36Sopenharmony_ci init_waitqueue_head(&tc->msg_wq); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (ntb_db_is_unsafe(ntb)) 145262306a36Sopenharmony_ci dev_dbg(&ntb->dev, "doorbell is unsafe\n"); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci if (ntb_spad_is_unsafe(ntb)) 145562306a36Sopenharmony_ci dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci return tc; 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cistatic void tool_clear_data(struct tool_ctx *tc) 146162306a36Sopenharmony_ci{ 146262306a36Sopenharmony_ci wake_up(&tc->link_wq); 146362306a36Sopenharmony_ci wake_up(&tc->db_wq); 146462306a36Sopenharmony_ci wake_up(&tc->msg_wq); 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic int tool_init_ntb(struct tool_ctx *tc) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci return ntb_set_ctx(tc->ntb, tc, &tool_ops); 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cistatic void tool_clear_ntb(struct tool_ctx *tc) 147362306a36Sopenharmony_ci{ 147462306a36Sopenharmony_ci ntb_clear_ctx(tc->ntb); 147562306a36Sopenharmony_ci ntb_link_disable(tc->ntb); 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic void tool_setup_dbgfs(struct tool_ctx *tc) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci int pidx, widx, sidx, midx; 148162306a36Sopenharmony_ci char buf[TOOL_BUF_LEN]; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* This modules is useless without dbgfs... */ 148462306a36Sopenharmony_ci if (!tool_dbgfs_topdir) { 148562306a36Sopenharmony_ci tc->dbgfs_dir = NULL; 148662306a36Sopenharmony_ci return; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci tc->dbgfs_dir = debugfs_create_dir(dev_name(&tc->ntb->dev), 149062306a36Sopenharmony_ci tool_dbgfs_topdir); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci debugfs_create_file("port", 0600, tc->dbgfs_dir, 149362306a36Sopenharmony_ci tc, &tool_port_fops); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci debugfs_create_file("link", 0600, tc->dbgfs_dir, 149662306a36Sopenharmony_ci tc, &tool_link_fops); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci debugfs_create_file("db", 0600, tc->dbgfs_dir, 149962306a36Sopenharmony_ci tc, &tool_db_fops); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci debugfs_create_file("db_valid_mask", 0600, tc->dbgfs_dir, 150262306a36Sopenharmony_ci tc, &tool_db_valid_mask_fops); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci debugfs_create_file("db_mask", 0600, tc->dbgfs_dir, 150562306a36Sopenharmony_ci tc, &tool_db_mask_fops); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci debugfs_create_file("db_event", 0600, tc->dbgfs_dir, 150862306a36Sopenharmony_ci tc, &tool_db_event_fops); 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci debugfs_create_file("peer_db", 0600, tc->dbgfs_dir, 151162306a36Sopenharmony_ci tc, &tool_peer_db_fops); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci debugfs_create_file("peer_db_mask", 0600, tc->dbgfs_dir, 151462306a36Sopenharmony_ci tc, &tool_peer_db_mask_fops); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (tc->inspad_cnt != 0) { 151762306a36Sopenharmony_ci for (sidx = 0; sidx < tc->inspad_cnt; sidx++) { 151862306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "spad%d", sidx); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci debugfs_create_file(buf, 0600, tc->dbgfs_dir, 152162306a36Sopenharmony_ci &tc->inspads[sidx], &tool_spad_fops); 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (tc->inmsg_cnt != 0) { 152662306a36Sopenharmony_ci for (midx = 0; midx < tc->inmsg_cnt; midx++) { 152762306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "msg%d", midx); 152862306a36Sopenharmony_ci debugfs_create_file(buf, 0600, tc->dbgfs_dir, 152962306a36Sopenharmony_ci &tc->inmsgs[midx], &tool_inmsg_fops); 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci debugfs_create_file("msg_sts", 0600, tc->dbgfs_dir, 153362306a36Sopenharmony_ci tc, &tool_msg_sts_fops); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci debugfs_create_file("msg_inbits", 0600, tc->dbgfs_dir, 153662306a36Sopenharmony_ci tc, &tool_msg_inbits_fops); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci debugfs_create_file("msg_outbits", 0600, tc->dbgfs_dir, 153962306a36Sopenharmony_ci tc, &tool_msg_outbits_fops); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci debugfs_create_file("msg_mask", 0600, tc->dbgfs_dir, 154262306a36Sopenharmony_ci tc, &tool_msg_mask_fops); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci debugfs_create_file("msg_event", 0600, tc->dbgfs_dir, 154562306a36Sopenharmony_ci tc, &tool_msg_event_fops); 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci for (pidx = 0; pidx < tc->peer_cnt; pidx++) { 154962306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "peer%d", pidx); 155062306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir = 155162306a36Sopenharmony_ci debugfs_create_dir(buf, tc->dbgfs_dir); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci debugfs_create_file("port", 0600, 155462306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, 155562306a36Sopenharmony_ci &tc->peers[pidx], &tool_peer_port_fops); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci debugfs_create_file("link", 0200, 155862306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, 155962306a36Sopenharmony_ci &tc->peers[pidx], &tool_peer_link_fops); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci debugfs_create_file("link_event", 0200, 156262306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, 156362306a36Sopenharmony_ci &tc->peers[pidx], &tool_peer_link_event_fops); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) { 156662306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "mw_trans%d", widx); 156762306a36Sopenharmony_ci debugfs_create_file(buf, 0600, 156862306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, 156962306a36Sopenharmony_ci &tc->peers[pidx].inmws[widx], 157062306a36Sopenharmony_ci &tool_mw_trans_fops); 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci for (widx = 0; widx < tc->peers[pidx].outmw_cnt; widx++) { 157462306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "peer_mw_trans%d", widx); 157562306a36Sopenharmony_ci debugfs_create_file(buf, 0600, 157662306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, 157762306a36Sopenharmony_ci &tc->peers[pidx].outmws[widx], 157862306a36Sopenharmony_ci &tool_peer_mw_trans_fops); 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) { 158262306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "spad%d", sidx); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci debugfs_create_file(buf, 0600, 158562306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, 158662306a36Sopenharmony_ci &tc->peers[pidx].outspads[sidx], 158762306a36Sopenharmony_ci &tool_peer_spad_fops); 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci for (midx = 0; midx < tc->peers[pidx].outmsg_cnt; midx++) { 159162306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "msg%d", midx); 159262306a36Sopenharmony_ci debugfs_create_file(buf, 0600, 159362306a36Sopenharmony_ci tc->peers[pidx].dbgfs_dir, 159462306a36Sopenharmony_ci &tc->peers[pidx].outmsgs[midx], 159562306a36Sopenharmony_ci &tool_outmsg_fops); 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic void tool_clear_dbgfs(struct tool_ctx *tc) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci debugfs_remove_recursive(tc->dbgfs_dir); 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci struct tool_ctx *tc; 160862306a36Sopenharmony_ci int ret; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci tc = tool_create_data(ntb); 161162306a36Sopenharmony_ci if (IS_ERR(tc)) 161262306a36Sopenharmony_ci return PTR_ERR(tc); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci ret = tool_init_peers(tc); 161562306a36Sopenharmony_ci if (ret != 0) 161662306a36Sopenharmony_ci goto err_clear_data; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci ret = tool_init_mws(tc); 161962306a36Sopenharmony_ci if (ret != 0) 162062306a36Sopenharmony_ci goto err_clear_data; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci ret = tool_init_spads(tc); 162362306a36Sopenharmony_ci if (ret != 0) 162462306a36Sopenharmony_ci goto err_clear_mws; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci ret = tool_init_msgs(tc); 162762306a36Sopenharmony_ci if (ret != 0) 162862306a36Sopenharmony_ci goto err_clear_mws; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci ret = tool_init_ntb(tc); 163162306a36Sopenharmony_ci if (ret != 0) 163262306a36Sopenharmony_ci goto err_clear_mws; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci tool_setup_dbgfs(tc); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return 0; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cierr_clear_mws: 163962306a36Sopenharmony_ci tool_clear_mws(tc); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cierr_clear_data: 164262306a36Sopenharmony_ci tool_clear_data(tc); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci return ret; 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_cistatic void tool_remove(struct ntb_client *self, struct ntb_dev *ntb) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci struct tool_ctx *tc = ntb->ctx; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci tool_clear_dbgfs(tc); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci tool_clear_ntb(tc); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci tool_clear_mws(tc); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci tool_clear_data(tc); 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic struct ntb_client tool_client = { 166162306a36Sopenharmony_ci .ops = { 166262306a36Sopenharmony_ci .probe = tool_probe, 166362306a36Sopenharmony_ci .remove = tool_remove, 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci}; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_cistatic int __init tool_init(void) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci int ret; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (debugfs_initialized()) 167262306a36Sopenharmony_ci tool_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci ret = ntb_register_client(&tool_client); 167562306a36Sopenharmony_ci if (ret) 167662306a36Sopenharmony_ci debugfs_remove_recursive(tool_dbgfs_topdir); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci return ret; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_cimodule_init(tool_init); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_cistatic void __exit tool_exit(void) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci ntb_unregister_client(&tool_client); 168562306a36Sopenharmony_ci debugfs_remove_recursive(tool_dbgfs_topdir); 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_cimodule_exit(tool_exit); 1688