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