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 Pingpong 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_perf/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: install driver with specified delay between doorbell event and response
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * root@self# insmod ntb_pingpong.ko delay_ms=1000
6462306a36Sopenharmony_ci *-----------------------------------------------------------------------------
6562306a36Sopenharmony_ci * Eg: get number of ping-pong cycles performed
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * root@self# cat $DBG_DIR/count
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#include <linux/init.h>
7162306a36Sopenharmony_ci#include <linux/kernel.h>
7262306a36Sopenharmony_ci#include <linux/module.h>
7362306a36Sopenharmony_ci#include <linux/device.h>
7462306a36Sopenharmony_ci#include <linux/bitops.h>
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#include <linux/pci.h>
7762306a36Sopenharmony_ci#include <linux/slab.h>
7862306a36Sopenharmony_ci#include <linux/hrtimer.h>
7962306a36Sopenharmony_ci#include <linux/debugfs.h>
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#include <linux/ntb.h>
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define DRIVER_NAME		"ntb_pingpong"
8462306a36Sopenharmony_ci#define DRIVER_VERSION		"2.0"
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
8762306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
8862306a36Sopenharmony_ciMODULE_AUTHOR("Allen Hubbe <Allen.Hubbe@emc.com>");
8962306a36Sopenharmony_ciMODULE_DESCRIPTION("PCIe NTB Simple Pingpong Client");
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic unsigned int unsafe;
9262306a36Sopenharmony_cimodule_param(unsafe, uint, 0644);
9362306a36Sopenharmony_ciMODULE_PARM_DESC(unsafe, "Run even though ntb operations may be unsafe");
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic unsigned int delay_ms = 1000;
9662306a36Sopenharmony_cimodule_param(delay_ms, uint, 0644);
9762306a36Sopenharmony_ciMODULE_PARM_DESC(delay_ms, "Milliseconds to delay the response to peer");
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct pp_ctx {
10062306a36Sopenharmony_ci	struct ntb_dev *ntb;
10162306a36Sopenharmony_ci	struct hrtimer timer;
10262306a36Sopenharmony_ci	u64 in_db;
10362306a36Sopenharmony_ci	u64 out_db;
10462306a36Sopenharmony_ci	int out_pidx;
10562306a36Sopenharmony_ci	u64 nmask;
10662306a36Sopenharmony_ci	u64 pmask;
10762306a36Sopenharmony_ci	atomic_t count;
10862306a36Sopenharmony_ci	spinlock_t lock;
10962306a36Sopenharmony_ci	struct dentry *dbgfs_dir;
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci#define to_pp_timer(__timer) \
11262306a36Sopenharmony_ci	container_of(__timer, struct pp_ctx, timer)
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic struct dentry *pp_dbgfs_topdir;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int pp_find_next_peer(struct pp_ctx *pp)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	u64 link, out_db;
11962306a36Sopenharmony_ci	int pidx;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	link = ntb_link_is_up(pp->ntb, NULL, NULL);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Find next available peer */
12462306a36Sopenharmony_ci	if (link & pp->nmask)
12562306a36Sopenharmony_ci		pidx = __ffs64(link & pp->nmask);
12662306a36Sopenharmony_ci	else if (link & pp->pmask)
12762306a36Sopenharmony_ci		pidx = __ffs64(link & pp->pmask);
12862306a36Sopenharmony_ci	else
12962306a36Sopenharmony_ci		return -ENODEV;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	out_db = BIT_ULL(ntb_peer_port_number(pp->ntb, pidx));
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	spin_lock(&pp->lock);
13462306a36Sopenharmony_ci	pp->out_pidx = pidx;
13562306a36Sopenharmony_ci	pp->out_db = out_db;
13662306a36Sopenharmony_ci	spin_unlock(&pp->lock);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void pp_setup(struct pp_ctx *pp)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	int ret;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	ntb_db_set_mask(pp->ntb, pp->in_db);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	hrtimer_cancel(&pp->timer);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ret = pp_find_next_peer(pp);
15062306a36Sopenharmony_ci	if (ret == -ENODEV) {
15162306a36Sopenharmony_ci		dev_dbg(&pp->ntb->dev, "Got no peers, so cancel\n");
15262306a36Sopenharmony_ci		return;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	dev_dbg(&pp->ntb->dev, "Ping-pong started with port %d, db %#llx\n",
15662306a36Sopenharmony_ci		ntb_peer_port_number(pp->ntb, pp->out_pidx), pp->out_db);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	hrtimer_start(&pp->timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic void pp_clear(struct pp_ctx *pp)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	hrtimer_cancel(&pp->timer);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	ntb_db_set_mask(pp->ntb, pp->in_db);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	dev_dbg(&pp->ntb->dev, "Ping-pong cancelled\n");
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void pp_ping(struct pp_ctx *pp)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	u32 count;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	count = atomic_read(&pp->count);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	spin_lock(&pp->lock);
17762306a36Sopenharmony_ci	ntb_peer_spad_write(pp->ntb, pp->out_pidx, 0, count);
17862306a36Sopenharmony_ci	ntb_peer_msg_write(pp->ntb, pp->out_pidx, 0, count);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	dev_dbg(&pp->ntb->dev, "Ping port %d spad %#x, msg %#x\n",
18162306a36Sopenharmony_ci		ntb_peer_port_number(pp->ntb, pp->out_pidx), count, count);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ntb_peer_db_set(pp->ntb, pp->out_db);
18462306a36Sopenharmony_ci	ntb_db_clear_mask(pp->ntb, pp->in_db);
18562306a36Sopenharmony_ci	spin_unlock(&pp->lock);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void pp_pong(struct pp_ctx *pp)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	u32 msg_data, spad_data;
19162306a36Sopenharmony_ci	int pidx = 0;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* Read pong data */
19462306a36Sopenharmony_ci	spad_data = ntb_spad_read(pp->ntb, 0);
19562306a36Sopenharmony_ci	msg_data = ntb_msg_read(pp->ntb, &pidx, 0);
19662306a36Sopenharmony_ci	ntb_msg_clear_sts(pp->ntb, -1);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/*
19962306a36Sopenharmony_ci	 * Scratchpad and message data may differ, since message register can't
20062306a36Sopenharmony_ci	 * be rewritten unless status is cleared. Additionally either of them
20162306a36Sopenharmony_ci	 * might be unsupported
20262306a36Sopenharmony_ci	 */
20362306a36Sopenharmony_ci	dev_dbg(&pp->ntb->dev, "Pong spad %#x, msg %#x (port %d)\n",
20462306a36Sopenharmony_ci		spad_data, msg_data, ntb_peer_port_number(pp->ntb, pidx));
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	atomic_inc(&pp->count);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ntb_db_set_mask(pp->ntb, pp->in_db);
20962306a36Sopenharmony_ci	ntb_db_clear(pp->ntb, pp->in_db);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	hrtimer_start(&pp->timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic enum hrtimer_restart pp_timer_func(struct hrtimer *t)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct pp_ctx *pp = to_pp_timer(t);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	pp_ping(pp);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return HRTIMER_NORESTART;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void pp_link_event(void *ctx)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct pp_ctx *pp = ctx;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	pp_setup(pp);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void pp_db_event(void *ctx, int vec)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct pp_ctx *pp = ctx;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	pp_pong(pp);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic const struct ntb_ctx_ops pp_ops = {
23862306a36Sopenharmony_ci	.link_event = pp_link_event,
23962306a36Sopenharmony_ci	.db_event = pp_db_event
24062306a36Sopenharmony_ci};
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int pp_check_ntb(struct ntb_dev *ntb)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	u64 pmask;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (ntb_db_is_unsafe(ntb)) {
24762306a36Sopenharmony_ci		dev_dbg(&ntb->dev, "Doorbell is unsafe\n");
24862306a36Sopenharmony_ci		if (!unsafe)
24962306a36Sopenharmony_ci			return -EINVAL;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (ntb_spad_is_unsafe(ntb)) {
25362306a36Sopenharmony_ci		dev_dbg(&ntb->dev, "Scratchpad is unsafe\n");
25462306a36Sopenharmony_ci		if (!unsafe)
25562306a36Sopenharmony_ci			return -EINVAL;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	pmask = GENMASK_ULL(ntb_peer_port_count(ntb), 0);
25962306a36Sopenharmony_ci	if ((ntb_db_valid_mask(ntb) & pmask) != pmask) {
26062306a36Sopenharmony_ci		dev_err(&ntb->dev, "Unsupported DB configuration\n");
26162306a36Sopenharmony_ci		return -EINVAL;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (ntb_spad_count(ntb) < 1 && ntb_msg_count(ntb) < 1) {
26562306a36Sopenharmony_ci		dev_err(&ntb->dev, "Scratchpads and messages unsupported\n");
26662306a36Sopenharmony_ci		return -EINVAL;
26762306a36Sopenharmony_ci	} else if (ntb_spad_count(ntb) < 1) {
26862306a36Sopenharmony_ci		dev_dbg(&ntb->dev, "Scratchpads unsupported\n");
26962306a36Sopenharmony_ci	} else if (ntb_msg_count(ntb) < 1) {
27062306a36Sopenharmony_ci		dev_dbg(&ntb->dev, "Messages unsupported\n");
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic struct pp_ctx *pp_create_data(struct ntb_dev *ntb)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct pp_ctx *pp;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	pp = devm_kzalloc(&ntb->dev, sizeof(*pp), GFP_KERNEL);
28162306a36Sopenharmony_ci	if (!pp)
28262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	pp->ntb = ntb;
28562306a36Sopenharmony_ci	atomic_set(&pp->count, 0);
28662306a36Sopenharmony_ci	spin_lock_init(&pp->lock);
28762306a36Sopenharmony_ci	hrtimer_init(&pp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
28862306a36Sopenharmony_ci	pp->timer.function = pp_timer_func;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return pp;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void pp_init_flds(struct pp_ctx *pp)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int pidx, lport, pcnt;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* Find global port index */
29862306a36Sopenharmony_ci	lport = ntb_port_number(pp->ntb);
29962306a36Sopenharmony_ci	pcnt = ntb_peer_port_count(pp->ntb);
30062306a36Sopenharmony_ci	for (pidx = 0; pidx < pcnt; pidx++) {
30162306a36Sopenharmony_ci		if (lport < ntb_peer_port_number(pp->ntb, pidx))
30262306a36Sopenharmony_ci			break;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	pp->in_db = BIT_ULL(lport);
30662306a36Sopenharmony_ci	pp->pmask = GENMASK_ULL(pidx, 0) >> 1;
30762306a36Sopenharmony_ci	pp->nmask = GENMASK_ULL(pcnt - 1, pidx);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	dev_dbg(&pp->ntb->dev, "Inbound db %#llx, prev %#llx, next %#llx\n",
31062306a36Sopenharmony_ci		pp->in_db, pp->pmask, pp->nmask);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int pp_mask_events(struct pp_ctx *pp)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	u64 db_mask, msg_mask;
31662306a36Sopenharmony_ci	int ret;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	db_mask = ntb_db_valid_mask(pp->ntb);
31962306a36Sopenharmony_ci	ret = ntb_db_set_mask(pp->ntb, db_mask);
32062306a36Sopenharmony_ci	if (ret)
32162306a36Sopenharmony_ci		return ret;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Skip message events masking if unsupported */
32462306a36Sopenharmony_ci	if (ntb_msg_count(pp->ntb) < 1)
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	msg_mask = ntb_msg_outbits(pp->ntb) | ntb_msg_inbits(pp->ntb);
32862306a36Sopenharmony_ci	return ntb_msg_set_mask(pp->ntb, msg_mask);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int pp_setup_ctx(struct pp_ctx *pp)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	int ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	ret = ntb_set_ctx(pp->ntb, pp, &pp_ops);
33662306a36Sopenharmony_ci	if (ret)
33762306a36Sopenharmony_ci		return ret;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ntb_link_enable(pp->ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
34062306a36Sopenharmony_ci	/* Might be not necessary */
34162306a36Sopenharmony_ci	ntb_link_event(pp->ntb);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void pp_clear_ctx(struct pp_ctx *pp)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	ntb_link_disable(pp->ntb);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	ntb_clear_ctx(pp->ntb);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void pp_setup_dbgfs(struct pp_ctx *pp)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct pci_dev *pdev = pp->ntb->pdev;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	pp->dbgfs_dir = debugfs_create_dir(pci_name(pdev), pp_dbgfs_topdir);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	debugfs_create_atomic_t("count", 0600, pp->dbgfs_dir, &pp->count);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void pp_clear_dbgfs(struct pp_ctx *pp)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	debugfs_remove_recursive(pp->dbgfs_dir);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int pp_probe(struct ntb_client *client, struct ntb_dev *ntb)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct pp_ctx *pp;
37062306a36Sopenharmony_ci	int ret;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ret = pp_check_ntb(ntb);
37362306a36Sopenharmony_ci	if (ret)
37462306a36Sopenharmony_ci		return ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	pp = pp_create_data(ntb);
37762306a36Sopenharmony_ci	if (IS_ERR(pp))
37862306a36Sopenharmony_ci		return PTR_ERR(pp);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	pp_init_flds(pp);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ret = pp_mask_events(pp);
38362306a36Sopenharmony_ci	if (ret)
38462306a36Sopenharmony_ci		return ret;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ret = pp_setup_ctx(pp);
38762306a36Sopenharmony_ci	if (ret)
38862306a36Sopenharmony_ci		return ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	pp_setup_dbgfs(pp);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void pp_remove(struct ntb_client *client, struct ntb_dev *ntb)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct pp_ctx *pp = ntb->ctx;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	pp_clear_dbgfs(pp);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	pp_clear_ctx(pp);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	pp_clear(pp);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic struct ntb_client pp_client = {
40762306a36Sopenharmony_ci	.ops = {
40862306a36Sopenharmony_ci		.probe = pp_probe,
40962306a36Sopenharmony_ci		.remove = pp_remove
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci};
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int __init pp_init(void)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	int ret;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (debugfs_initialized())
41862306a36Sopenharmony_ci		pp_dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	ret = ntb_register_client(&pp_client);
42162306a36Sopenharmony_ci	if (ret)
42262306a36Sopenharmony_ci		debugfs_remove_recursive(pp_dbgfs_topdir);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return ret;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_cimodule_init(pp_init);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void __exit pp_exit(void)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	ntb_unregister_client(&pp_client);
43162306a36Sopenharmony_ci	debugfs_remove_recursive(pp_dbgfs_topdir);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_cimodule_exit(pp_exit);
434