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