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) 2012 Intel Corporation. All rights reserved.
862306a36Sopenharmony_ci *   Copyright (C) 2015 EMC Corporation. 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 *   BSD LICENSE
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *   Copyright(c) 2012 Intel Corporation. All rights reserved.
1762306a36Sopenharmony_ci *   Copyright (C) 2015 EMC Corporation. All Rights Reserved.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *   Redistribution and use in source and binary forms, with or without
2062306a36Sopenharmony_ci *   modification, are permitted provided that the following conditions
2162306a36Sopenharmony_ci *   are met:
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *     * Redistributions of source code must retain the above copyright
2462306a36Sopenharmony_ci *       notice, this list of conditions and the following disclaimer.
2562306a36Sopenharmony_ci *     * Redistributions in binary form must reproduce the above copy
2662306a36Sopenharmony_ci *       notice, this list of conditions and the following disclaimer in
2762306a36Sopenharmony_ci *       the documentation and/or other materials provided with the
2862306a36Sopenharmony_ci *       distribution.
2962306a36Sopenharmony_ci *     * Neither the name of Intel Corporation nor the names of its
3062306a36Sopenharmony_ci *       contributors may be used to endorse or promote products derived
3162306a36Sopenharmony_ci *       from this software without specific prior written permission.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3462306a36Sopenharmony_ci *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3562306a36Sopenharmony_ci *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3662306a36Sopenharmony_ci *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3762306a36Sopenharmony_ci *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3862306a36Sopenharmony_ci *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3962306a36Sopenharmony_ci *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
4062306a36Sopenharmony_ci *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
4162306a36Sopenharmony_ci *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4262306a36Sopenharmony_ci *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
4362306a36Sopenharmony_ci *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * PCIe NTB Transport Linux driver
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * Contact Information:
4862306a36Sopenharmony_ci * Jon Mason <jon.mason@intel.com>
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci#include <linux/debugfs.h>
5162306a36Sopenharmony_ci#include <linux/delay.h>
5262306a36Sopenharmony_ci#include <linux/dmaengine.h>
5362306a36Sopenharmony_ci#include <linux/dma-mapping.h>
5462306a36Sopenharmony_ci#include <linux/errno.h>
5562306a36Sopenharmony_ci#include <linux/export.h>
5662306a36Sopenharmony_ci#include <linux/interrupt.h>
5762306a36Sopenharmony_ci#include <linux/module.h>
5862306a36Sopenharmony_ci#include <linux/pci.h>
5962306a36Sopenharmony_ci#include <linux/slab.h>
6062306a36Sopenharmony_ci#include <linux/types.h>
6162306a36Sopenharmony_ci#include <linux/uaccess.h>
6262306a36Sopenharmony_ci#include "linux/ntb.h"
6362306a36Sopenharmony_ci#include "linux/ntb_transport.h"
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define NTB_TRANSPORT_VERSION	4
6662306a36Sopenharmony_ci#define NTB_TRANSPORT_VER	"4"
6762306a36Sopenharmony_ci#define NTB_TRANSPORT_NAME	"ntb_transport"
6862306a36Sopenharmony_ci#define NTB_TRANSPORT_DESC	"Software Queue-Pair Transport over NTB"
6962306a36Sopenharmony_ci#define NTB_TRANSPORT_MIN_SPADS (MW0_SZ_HIGH + 2)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciMODULE_DESCRIPTION(NTB_TRANSPORT_DESC);
7262306a36Sopenharmony_ciMODULE_VERSION(NTB_TRANSPORT_VER);
7362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
7462306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic unsigned long max_mw_size;
7762306a36Sopenharmony_cimodule_param(max_mw_size, ulong, 0644);
7862306a36Sopenharmony_ciMODULE_PARM_DESC(max_mw_size, "Limit size of large memory windows");
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic unsigned int transport_mtu = 0x10000;
8162306a36Sopenharmony_cimodule_param(transport_mtu, uint, 0644);
8262306a36Sopenharmony_ciMODULE_PARM_DESC(transport_mtu, "Maximum size of NTB transport packets");
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic unsigned char max_num_clients;
8562306a36Sopenharmony_cimodule_param(max_num_clients, byte, 0644);
8662306a36Sopenharmony_ciMODULE_PARM_DESC(max_num_clients, "Maximum number of NTB transport clients");
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic unsigned int copy_bytes = 1024;
8962306a36Sopenharmony_cimodule_param(copy_bytes, uint, 0644);
9062306a36Sopenharmony_ciMODULE_PARM_DESC(copy_bytes, "Threshold under which NTB will use the CPU to copy instead of DMA");
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic bool use_dma;
9362306a36Sopenharmony_cimodule_param(use_dma, bool, 0644);
9462306a36Sopenharmony_ciMODULE_PARM_DESC(use_dma, "Use DMA engine to perform large data copy");
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic bool use_msi;
9762306a36Sopenharmony_ci#ifdef CONFIG_NTB_MSI
9862306a36Sopenharmony_cimodule_param(use_msi, bool, 0644);
9962306a36Sopenharmony_ciMODULE_PARM_DESC(use_msi, "Use MSI interrupts instead of doorbells");
10062306a36Sopenharmony_ci#endif
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct dentry *nt_debugfs_dir;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/* Only two-ports NTB devices are supported */
10562306a36Sopenharmony_ci#define PIDX		NTB_DEF_PEER_IDX
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistruct ntb_queue_entry {
10862306a36Sopenharmony_ci	/* ntb_queue list reference */
10962306a36Sopenharmony_ci	struct list_head entry;
11062306a36Sopenharmony_ci	/* pointers to data to be transferred */
11162306a36Sopenharmony_ci	void *cb_data;
11262306a36Sopenharmony_ci	void *buf;
11362306a36Sopenharmony_ci	unsigned int len;
11462306a36Sopenharmony_ci	unsigned int flags;
11562306a36Sopenharmony_ci	int retries;
11662306a36Sopenharmony_ci	int errors;
11762306a36Sopenharmony_ci	unsigned int tx_index;
11862306a36Sopenharmony_ci	unsigned int rx_index;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	struct ntb_transport_qp *qp;
12162306a36Sopenharmony_ci	union {
12262306a36Sopenharmony_ci		struct ntb_payload_header __iomem *tx_hdr;
12362306a36Sopenharmony_ci		struct ntb_payload_header *rx_hdr;
12462306a36Sopenharmony_ci	};
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistruct ntb_rx_info {
12862306a36Sopenharmony_ci	unsigned int entry;
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistruct ntb_transport_qp {
13262306a36Sopenharmony_ci	struct ntb_transport_ctx *transport;
13362306a36Sopenharmony_ci	struct ntb_dev *ndev;
13462306a36Sopenharmony_ci	void *cb_data;
13562306a36Sopenharmony_ci	struct dma_chan *tx_dma_chan;
13662306a36Sopenharmony_ci	struct dma_chan *rx_dma_chan;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	bool client_ready;
13962306a36Sopenharmony_ci	bool link_is_up;
14062306a36Sopenharmony_ci	bool active;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	u8 qp_num;	/* Only 64 QP's are allowed.  0-63 */
14362306a36Sopenharmony_ci	u64 qp_bit;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	struct ntb_rx_info __iomem *rx_info;
14662306a36Sopenharmony_ci	struct ntb_rx_info *remote_rx_info;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	void (*tx_handler)(struct ntb_transport_qp *qp, void *qp_data,
14962306a36Sopenharmony_ci			   void *data, int len);
15062306a36Sopenharmony_ci	struct list_head tx_free_q;
15162306a36Sopenharmony_ci	spinlock_t ntb_tx_free_q_lock;
15262306a36Sopenharmony_ci	void __iomem *tx_mw;
15362306a36Sopenharmony_ci	phys_addr_t tx_mw_phys;
15462306a36Sopenharmony_ci	size_t tx_mw_size;
15562306a36Sopenharmony_ci	dma_addr_t tx_mw_dma_addr;
15662306a36Sopenharmony_ci	unsigned int tx_index;
15762306a36Sopenharmony_ci	unsigned int tx_max_entry;
15862306a36Sopenharmony_ci	unsigned int tx_max_frame;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	void (*rx_handler)(struct ntb_transport_qp *qp, void *qp_data,
16162306a36Sopenharmony_ci			   void *data, int len);
16262306a36Sopenharmony_ci	struct list_head rx_post_q;
16362306a36Sopenharmony_ci	struct list_head rx_pend_q;
16462306a36Sopenharmony_ci	struct list_head rx_free_q;
16562306a36Sopenharmony_ci	/* ntb_rx_q_lock: synchronize access to rx_XXXX_q */
16662306a36Sopenharmony_ci	spinlock_t ntb_rx_q_lock;
16762306a36Sopenharmony_ci	void *rx_buff;
16862306a36Sopenharmony_ci	unsigned int rx_index;
16962306a36Sopenharmony_ci	unsigned int rx_max_entry;
17062306a36Sopenharmony_ci	unsigned int rx_max_frame;
17162306a36Sopenharmony_ci	unsigned int rx_alloc_entry;
17262306a36Sopenharmony_ci	dma_cookie_t last_cookie;
17362306a36Sopenharmony_ci	struct tasklet_struct rxc_db_work;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	void (*event_handler)(void *data, int status);
17662306a36Sopenharmony_ci	struct delayed_work link_work;
17762306a36Sopenharmony_ci	struct work_struct link_cleanup;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	struct dentry *debugfs_dir;
18062306a36Sopenharmony_ci	struct dentry *debugfs_stats;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Stats */
18362306a36Sopenharmony_ci	u64 rx_bytes;
18462306a36Sopenharmony_ci	u64 rx_pkts;
18562306a36Sopenharmony_ci	u64 rx_ring_empty;
18662306a36Sopenharmony_ci	u64 rx_err_no_buf;
18762306a36Sopenharmony_ci	u64 rx_err_oflow;
18862306a36Sopenharmony_ci	u64 rx_err_ver;
18962306a36Sopenharmony_ci	u64 rx_memcpy;
19062306a36Sopenharmony_ci	u64 rx_async;
19162306a36Sopenharmony_ci	u64 tx_bytes;
19262306a36Sopenharmony_ci	u64 tx_pkts;
19362306a36Sopenharmony_ci	u64 tx_ring_full;
19462306a36Sopenharmony_ci	u64 tx_err_no_buf;
19562306a36Sopenharmony_ci	u64 tx_memcpy;
19662306a36Sopenharmony_ci	u64 tx_async;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	bool use_msi;
19962306a36Sopenharmony_ci	int msi_irq;
20062306a36Sopenharmony_ci	struct ntb_msi_desc msi_desc;
20162306a36Sopenharmony_ci	struct ntb_msi_desc peer_msi_desc;
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistruct ntb_transport_mw {
20562306a36Sopenharmony_ci	phys_addr_t phys_addr;
20662306a36Sopenharmony_ci	resource_size_t phys_size;
20762306a36Sopenharmony_ci	void __iomem *vbase;
20862306a36Sopenharmony_ci	size_t xlat_size;
20962306a36Sopenharmony_ci	size_t buff_size;
21062306a36Sopenharmony_ci	size_t alloc_size;
21162306a36Sopenharmony_ci	void *alloc_addr;
21262306a36Sopenharmony_ci	void *virt_addr;
21362306a36Sopenharmony_ci	dma_addr_t dma_addr;
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistruct ntb_transport_client_dev {
21762306a36Sopenharmony_ci	struct list_head entry;
21862306a36Sopenharmony_ci	struct ntb_transport_ctx *nt;
21962306a36Sopenharmony_ci	struct device dev;
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistruct ntb_transport_ctx {
22362306a36Sopenharmony_ci	struct list_head entry;
22462306a36Sopenharmony_ci	struct list_head client_devs;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	struct ntb_dev *ndev;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	struct ntb_transport_mw *mw_vec;
22962306a36Sopenharmony_ci	struct ntb_transport_qp *qp_vec;
23062306a36Sopenharmony_ci	unsigned int mw_count;
23162306a36Sopenharmony_ci	unsigned int qp_count;
23262306a36Sopenharmony_ci	u64 qp_bitmap;
23362306a36Sopenharmony_ci	u64 qp_bitmap_free;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	bool use_msi;
23662306a36Sopenharmony_ci	unsigned int msi_spad_offset;
23762306a36Sopenharmony_ci	u64 msi_db_mask;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	bool link_is_up;
24062306a36Sopenharmony_ci	struct delayed_work link_work;
24162306a36Sopenharmony_ci	struct work_struct link_cleanup;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	struct dentry *debugfs_node_dir;
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cienum {
24762306a36Sopenharmony_ci	DESC_DONE_FLAG = BIT(0),
24862306a36Sopenharmony_ci	LINK_DOWN_FLAG = BIT(1),
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistruct ntb_payload_header {
25262306a36Sopenharmony_ci	unsigned int ver;
25362306a36Sopenharmony_ci	unsigned int len;
25462306a36Sopenharmony_ci	unsigned int flags;
25562306a36Sopenharmony_ci};
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cienum {
25862306a36Sopenharmony_ci	VERSION = 0,
25962306a36Sopenharmony_ci	QP_LINKS,
26062306a36Sopenharmony_ci	NUM_QPS,
26162306a36Sopenharmony_ci	NUM_MWS,
26262306a36Sopenharmony_ci	MW0_SZ_HIGH,
26362306a36Sopenharmony_ci	MW0_SZ_LOW,
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci#define dev_client_dev(__dev) \
26762306a36Sopenharmony_ci	container_of((__dev), struct ntb_transport_client_dev, dev)
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci#define drv_client(__drv) \
27062306a36Sopenharmony_ci	container_of((__drv), struct ntb_transport_client, driver)
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci#define QP_TO_MW(nt, qp)	((qp) % nt->mw_count)
27362306a36Sopenharmony_ci#define NTB_QP_DEF_NUM_ENTRIES	100
27462306a36Sopenharmony_ci#define NTB_LINK_DOWN_TIMEOUT	10
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic void ntb_transport_rxc_db(unsigned long data);
27762306a36Sopenharmony_cistatic const struct ntb_ctx_ops ntb_transport_ops;
27862306a36Sopenharmony_cistatic struct ntb_client ntb_transport_client;
27962306a36Sopenharmony_cistatic int ntb_async_tx_submit(struct ntb_transport_qp *qp,
28062306a36Sopenharmony_ci			       struct ntb_queue_entry *entry);
28162306a36Sopenharmony_cistatic void ntb_memcpy_tx(struct ntb_queue_entry *entry, void __iomem *offset);
28262306a36Sopenharmony_cistatic int ntb_async_rx_submit(struct ntb_queue_entry *entry, void *offset);
28362306a36Sopenharmony_cistatic void ntb_memcpy_rx(struct ntb_queue_entry *entry, void *offset);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int ntb_transport_bus_match(struct device *dev,
28762306a36Sopenharmony_ci				   struct device_driver *drv)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	return !strncmp(dev_name(dev), drv->name, strlen(drv->name));
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic int ntb_transport_bus_probe(struct device *dev)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	const struct ntb_transport_client *client;
29562306a36Sopenharmony_ci	int rc;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	get_device(dev);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	client = drv_client(dev->driver);
30062306a36Sopenharmony_ci	rc = client->probe(dev);
30162306a36Sopenharmony_ci	if (rc)
30262306a36Sopenharmony_ci		put_device(dev);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return rc;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void ntb_transport_bus_remove(struct device *dev)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	const struct ntb_transport_client *client;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	client = drv_client(dev->driver);
31262306a36Sopenharmony_ci	client->remove(dev);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	put_device(dev);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic struct bus_type ntb_transport_bus = {
31862306a36Sopenharmony_ci	.name = "ntb_transport",
31962306a36Sopenharmony_ci	.match = ntb_transport_bus_match,
32062306a36Sopenharmony_ci	.probe = ntb_transport_bus_probe,
32162306a36Sopenharmony_ci	.remove = ntb_transport_bus_remove,
32262306a36Sopenharmony_ci};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic LIST_HEAD(ntb_transport_list);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int ntb_bus_init(struct ntb_transport_ctx *nt)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	list_add_tail(&nt->entry, &ntb_transport_list);
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic void ntb_bus_remove(struct ntb_transport_ctx *nt)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct ntb_transport_client_dev *client_dev, *cd;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	list_for_each_entry_safe(client_dev, cd, &nt->client_devs, entry) {
33762306a36Sopenharmony_ci		dev_err(client_dev->dev.parent, "%s still attached to bus, removing\n",
33862306a36Sopenharmony_ci			dev_name(&client_dev->dev));
33962306a36Sopenharmony_ci		list_del(&client_dev->entry);
34062306a36Sopenharmony_ci		device_unregister(&client_dev->dev);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	list_del(&nt->entry);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void ntb_transport_client_release(struct device *dev)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct ntb_transport_client_dev *client_dev;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	client_dev = dev_client_dev(dev);
35162306a36Sopenharmony_ci	kfree(client_dev);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/**
35562306a36Sopenharmony_ci * ntb_transport_unregister_client_dev - Unregister NTB client device
35662306a36Sopenharmony_ci * @device_name: Name of NTB client device
35762306a36Sopenharmony_ci *
35862306a36Sopenharmony_ci * Unregister an NTB client device with the NTB transport layer
35962306a36Sopenharmony_ci */
36062306a36Sopenharmony_civoid ntb_transport_unregister_client_dev(char *device_name)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct ntb_transport_client_dev *client, *cd;
36362306a36Sopenharmony_ci	struct ntb_transport_ctx *nt;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	list_for_each_entry(nt, &ntb_transport_list, entry)
36662306a36Sopenharmony_ci		list_for_each_entry_safe(client, cd, &nt->client_devs, entry)
36762306a36Sopenharmony_ci			if (!strncmp(dev_name(&client->dev), device_name,
36862306a36Sopenharmony_ci				     strlen(device_name))) {
36962306a36Sopenharmony_ci				list_del(&client->entry);
37062306a36Sopenharmony_ci				device_unregister(&client->dev);
37162306a36Sopenharmony_ci			}
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_unregister_client_dev);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/**
37662306a36Sopenharmony_ci * ntb_transport_register_client_dev - Register NTB client device
37762306a36Sopenharmony_ci * @device_name: Name of NTB client device
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * Register an NTB client device with the NTB transport layer
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_ciint ntb_transport_register_client_dev(char *device_name)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct ntb_transport_client_dev *client_dev;
38462306a36Sopenharmony_ci	struct ntb_transport_ctx *nt;
38562306a36Sopenharmony_ci	int node;
38662306a36Sopenharmony_ci	int rc, i = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (list_empty(&ntb_transport_list))
38962306a36Sopenharmony_ci		return -ENODEV;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	list_for_each_entry(nt, &ntb_transport_list, entry) {
39262306a36Sopenharmony_ci		struct device *dev;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		node = dev_to_node(&nt->ndev->dev);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		client_dev = kzalloc_node(sizeof(*client_dev),
39762306a36Sopenharmony_ci					  GFP_KERNEL, node);
39862306a36Sopenharmony_ci		if (!client_dev) {
39962306a36Sopenharmony_ci			rc = -ENOMEM;
40062306a36Sopenharmony_ci			goto err;
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		dev = &client_dev->dev;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		/* setup and register client devices */
40662306a36Sopenharmony_ci		dev_set_name(dev, "%s%d", device_name, i);
40762306a36Sopenharmony_ci		dev->bus = &ntb_transport_bus;
40862306a36Sopenharmony_ci		dev->release = ntb_transport_client_release;
40962306a36Sopenharmony_ci		dev->parent = &nt->ndev->dev;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		rc = device_register(dev);
41262306a36Sopenharmony_ci		if (rc) {
41362306a36Sopenharmony_ci			put_device(dev);
41462306a36Sopenharmony_ci			goto err;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		list_add_tail(&client_dev->entry, &nt->client_devs);
41862306a36Sopenharmony_ci		i++;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cierr:
42462306a36Sopenharmony_ci	ntb_transport_unregister_client_dev(device_name);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	return rc;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_register_client_dev);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci/**
43162306a36Sopenharmony_ci * ntb_transport_register_client - Register NTB client driver
43262306a36Sopenharmony_ci * @drv: NTB client driver to be registered
43362306a36Sopenharmony_ci *
43462306a36Sopenharmony_ci * Register an NTB client driver with the NTB transport layer
43562306a36Sopenharmony_ci *
43662306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
43762306a36Sopenharmony_ci */
43862306a36Sopenharmony_ciint ntb_transport_register_client(struct ntb_transport_client *drv)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	drv->driver.bus = &ntb_transport_bus;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (list_empty(&ntb_transport_list))
44362306a36Sopenharmony_ci		return -ENODEV;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return driver_register(&drv->driver);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_register_client);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/**
45062306a36Sopenharmony_ci * ntb_transport_unregister_client - Unregister NTB client driver
45162306a36Sopenharmony_ci * @drv: NTB client driver to be unregistered
45262306a36Sopenharmony_ci *
45362306a36Sopenharmony_ci * Unregister an NTB client driver with the NTB transport layer
45462306a36Sopenharmony_ci *
45562306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
45662306a36Sopenharmony_ci */
45762306a36Sopenharmony_civoid ntb_transport_unregister_client(struct ntb_transport_client *drv)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	driver_unregister(&drv->driver);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_unregister_client);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count,
46462306a36Sopenharmony_ci			    loff_t *offp)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct ntb_transport_qp *qp;
46762306a36Sopenharmony_ci	char *buf;
46862306a36Sopenharmony_ci	ssize_t ret, out_offset, out_count;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	qp = filp->private_data;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (!qp || !qp->link_is_up)
47362306a36Sopenharmony_ci		return 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	out_count = 1000;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	buf = kmalloc(out_count, GFP_KERNEL);
47862306a36Sopenharmony_ci	if (!buf)
47962306a36Sopenharmony_ci		return -ENOMEM;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	out_offset = 0;
48262306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
48362306a36Sopenharmony_ci			       "\nNTB QP stats:\n\n");
48462306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
48562306a36Sopenharmony_ci			       "rx_bytes - \t%llu\n", qp->rx_bytes);
48662306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
48762306a36Sopenharmony_ci			       "rx_pkts - \t%llu\n", qp->rx_pkts);
48862306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
48962306a36Sopenharmony_ci			       "rx_memcpy - \t%llu\n", qp->rx_memcpy);
49062306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
49162306a36Sopenharmony_ci			       "rx_async - \t%llu\n", qp->rx_async);
49262306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
49362306a36Sopenharmony_ci			       "rx_ring_empty - %llu\n", qp->rx_ring_empty);
49462306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
49562306a36Sopenharmony_ci			       "rx_err_no_buf - %llu\n", qp->rx_err_no_buf);
49662306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
49762306a36Sopenharmony_ci			       "rx_err_oflow - \t%llu\n", qp->rx_err_oflow);
49862306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
49962306a36Sopenharmony_ci			       "rx_err_ver - \t%llu\n", qp->rx_err_ver);
50062306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
50162306a36Sopenharmony_ci			       "rx_buff - \t0x%p\n", qp->rx_buff);
50262306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
50362306a36Sopenharmony_ci			       "rx_index - \t%u\n", qp->rx_index);
50462306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
50562306a36Sopenharmony_ci			       "rx_max_entry - \t%u\n", qp->rx_max_entry);
50662306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
50762306a36Sopenharmony_ci			       "rx_alloc_entry - \t%u\n\n", qp->rx_alloc_entry);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
51062306a36Sopenharmony_ci			       "tx_bytes - \t%llu\n", qp->tx_bytes);
51162306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
51262306a36Sopenharmony_ci			       "tx_pkts - \t%llu\n", qp->tx_pkts);
51362306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
51462306a36Sopenharmony_ci			       "tx_memcpy - \t%llu\n", qp->tx_memcpy);
51562306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
51662306a36Sopenharmony_ci			       "tx_async - \t%llu\n", qp->tx_async);
51762306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
51862306a36Sopenharmony_ci			       "tx_ring_full - \t%llu\n", qp->tx_ring_full);
51962306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
52062306a36Sopenharmony_ci			       "tx_err_no_buf - %llu\n", qp->tx_err_no_buf);
52162306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
52262306a36Sopenharmony_ci			       "tx_mw - \t0x%p\n", qp->tx_mw);
52362306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
52462306a36Sopenharmony_ci			       "tx_index (H) - \t%u\n", qp->tx_index);
52562306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
52662306a36Sopenharmony_ci			       "RRI (T) - \t%u\n",
52762306a36Sopenharmony_ci			       qp->remote_rx_info->entry);
52862306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
52962306a36Sopenharmony_ci			       "tx_max_entry - \t%u\n", qp->tx_max_entry);
53062306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
53162306a36Sopenharmony_ci			       "free tx - \t%u\n",
53262306a36Sopenharmony_ci			       ntb_transport_tx_free_entry(qp));
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
53562306a36Sopenharmony_ci			       "\n");
53662306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
53762306a36Sopenharmony_ci			       "Using TX DMA - \t%s\n",
53862306a36Sopenharmony_ci			       qp->tx_dma_chan ? "Yes" : "No");
53962306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
54062306a36Sopenharmony_ci			       "Using RX DMA - \t%s\n",
54162306a36Sopenharmony_ci			       qp->rx_dma_chan ? "Yes" : "No");
54262306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
54362306a36Sopenharmony_ci			       "QP Link - \t%s\n",
54462306a36Sopenharmony_ci			       qp->link_is_up ? "Up" : "Down");
54562306a36Sopenharmony_ci	out_offset += scnprintf(buf + out_offset, out_count - out_offset,
54662306a36Sopenharmony_ci			       "\n");
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (out_offset > out_count)
54962306a36Sopenharmony_ci		out_offset = out_count;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset);
55262306a36Sopenharmony_ci	kfree(buf);
55362306a36Sopenharmony_ci	return ret;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic const struct file_operations ntb_qp_debugfs_stats = {
55762306a36Sopenharmony_ci	.owner = THIS_MODULE,
55862306a36Sopenharmony_ci	.open = simple_open,
55962306a36Sopenharmony_ci	.read = debugfs_read,
56062306a36Sopenharmony_ci};
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic void ntb_list_add(spinlock_t *lock, struct list_head *entry,
56362306a36Sopenharmony_ci			 struct list_head *list)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	unsigned long flags;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	spin_lock_irqsave(lock, flags);
56862306a36Sopenharmony_ci	list_add_tail(entry, list);
56962306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic struct ntb_queue_entry *ntb_list_rm(spinlock_t *lock,
57362306a36Sopenharmony_ci					   struct list_head *list)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
57662306a36Sopenharmony_ci	unsigned long flags;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	spin_lock_irqsave(lock, flags);
57962306a36Sopenharmony_ci	if (list_empty(list)) {
58062306a36Sopenharmony_ci		entry = NULL;
58162306a36Sopenharmony_ci		goto out;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	entry = list_first_entry(list, struct ntb_queue_entry, entry);
58462306a36Sopenharmony_ci	list_del(&entry->entry);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ciout:
58762306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return entry;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic struct ntb_queue_entry *ntb_list_mv(spinlock_t *lock,
59362306a36Sopenharmony_ci					   struct list_head *list,
59462306a36Sopenharmony_ci					   struct list_head *to_list)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
59762306a36Sopenharmony_ci	unsigned long flags;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	spin_lock_irqsave(lock, flags);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (list_empty(list)) {
60262306a36Sopenharmony_ci		entry = NULL;
60362306a36Sopenharmony_ci	} else {
60462306a36Sopenharmony_ci		entry = list_first_entry(list, struct ntb_queue_entry, entry);
60562306a36Sopenharmony_ci		list_move_tail(&entry->entry, to_list);
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return entry;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt,
61462306a36Sopenharmony_ci				     unsigned int qp_num)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct ntb_transport_qp *qp = &nt->qp_vec[qp_num];
61762306a36Sopenharmony_ci	struct ntb_transport_mw *mw;
61862306a36Sopenharmony_ci	struct ntb_dev *ndev = nt->ndev;
61962306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
62062306a36Sopenharmony_ci	unsigned int rx_size, num_qps_mw;
62162306a36Sopenharmony_ci	unsigned int mw_num, mw_count, qp_count;
62262306a36Sopenharmony_ci	unsigned int i;
62362306a36Sopenharmony_ci	int node;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	mw_count = nt->mw_count;
62662306a36Sopenharmony_ci	qp_count = nt->qp_count;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	mw_num = QP_TO_MW(nt, qp_num);
62962306a36Sopenharmony_ci	mw = &nt->mw_vec[mw_num];
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (!mw->virt_addr)
63262306a36Sopenharmony_ci		return -ENOMEM;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if (mw_num < qp_count % mw_count)
63562306a36Sopenharmony_ci		num_qps_mw = qp_count / mw_count + 1;
63662306a36Sopenharmony_ci	else
63762306a36Sopenharmony_ci		num_qps_mw = qp_count / mw_count;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	rx_size = (unsigned int)mw->xlat_size / num_qps_mw;
64062306a36Sopenharmony_ci	qp->rx_buff = mw->virt_addr + rx_size * (qp_num / mw_count);
64162306a36Sopenharmony_ci	rx_size -= sizeof(struct ntb_rx_info);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	qp->remote_rx_info = qp->rx_buff + rx_size;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* Due to housekeeping, there must be atleast 2 buffs */
64662306a36Sopenharmony_ci	qp->rx_max_frame = min(transport_mtu, rx_size / 2);
64762306a36Sopenharmony_ci	qp->rx_max_entry = rx_size / qp->rx_max_frame;
64862306a36Sopenharmony_ci	qp->rx_index = 0;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/*
65162306a36Sopenharmony_ci	 * Checking to see if we have more entries than the default.
65262306a36Sopenharmony_ci	 * We should add additional entries if that is the case so we
65362306a36Sopenharmony_ci	 * can be in sync with the transport frames.
65462306a36Sopenharmony_ci	 */
65562306a36Sopenharmony_ci	node = dev_to_node(&ndev->dev);
65662306a36Sopenharmony_ci	for (i = qp->rx_alloc_entry; i < qp->rx_max_entry; i++) {
65762306a36Sopenharmony_ci		entry = kzalloc_node(sizeof(*entry), GFP_KERNEL, node);
65862306a36Sopenharmony_ci		if (!entry)
65962306a36Sopenharmony_ci			return -ENOMEM;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		entry->qp = qp;
66262306a36Sopenharmony_ci		ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry,
66362306a36Sopenharmony_ci			     &qp->rx_free_q);
66462306a36Sopenharmony_ci		qp->rx_alloc_entry++;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	qp->remote_rx_info->entry = qp->rx_max_entry - 1;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	/* setup the hdr offsets with 0's */
67062306a36Sopenharmony_ci	for (i = 0; i < qp->rx_max_entry; i++) {
67162306a36Sopenharmony_ci		void *offset = (qp->rx_buff + qp->rx_max_frame * (i + 1) -
67262306a36Sopenharmony_ci				sizeof(struct ntb_payload_header));
67362306a36Sopenharmony_ci		memset(offset, 0, sizeof(struct ntb_payload_header));
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	qp->rx_pkts = 0;
67762306a36Sopenharmony_ci	qp->tx_pkts = 0;
67862306a36Sopenharmony_ci	qp->tx_index = 0;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic irqreturn_t ntb_transport_isr(int irq, void *dev)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct ntb_transport_qp *qp = dev;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	tasklet_schedule(&qp->rxc_db_work);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return IRQ_HANDLED;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic void ntb_transport_setup_qp_peer_msi(struct ntb_transport_ctx *nt,
69362306a36Sopenharmony_ci					    unsigned int qp_num)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	struct ntb_transport_qp *qp = &nt->qp_vec[qp_num];
69662306a36Sopenharmony_ci	int spad = qp_num * 2 + nt->msi_spad_offset;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (!nt->use_msi)
69962306a36Sopenharmony_ci		return;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (spad >= ntb_spad_count(nt->ndev))
70262306a36Sopenharmony_ci		return;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	qp->peer_msi_desc.addr_offset =
70562306a36Sopenharmony_ci		ntb_peer_spad_read(qp->ndev, PIDX, spad);
70662306a36Sopenharmony_ci	qp->peer_msi_desc.data =
70762306a36Sopenharmony_ci		ntb_peer_spad_read(qp->ndev, PIDX, spad + 1);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	dev_dbg(&qp->ndev->pdev->dev, "QP%d Peer MSI addr=%x data=%x\n",
71062306a36Sopenharmony_ci		qp_num, qp->peer_msi_desc.addr_offset, qp->peer_msi_desc.data);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (qp->peer_msi_desc.addr_offset) {
71362306a36Sopenharmony_ci		qp->use_msi = true;
71462306a36Sopenharmony_ci		dev_info(&qp->ndev->pdev->dev,
71562306a36Sopenharmony_ci			 "Using MSI interrupts for QP%d\n", qp_num);
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void ntb_transport_setup_qp_msi(struct ntb_transport_ctx *nt,
72062306a36Sopenharmony_ci				       unsigned int qp_num)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct ntb_transport_qp *qp = &nt->qp_vec[qp_num];
72362306a36Sopenharmony_ci	int spad = qp_num * 2 + nt->msi_spad_offset;
72462306a36Sopenharmony_ci	int rc;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (!nt->use_msi)
72762306a36Sopenharmony_ci		return;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (spad >= ntb_spad_count(nt->ndev)) {
73062306a36Sopenharmony_ci		dev_warn_once(&qp->ndev->pdev->dev,
73162306a36Sopenharmony_ci			      "Not enough SPADS to use MSI interrupts\n");
73262306a36Sopenharmony_ci		return;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	ntb_spad_write(qp->ndev, spad, 0);
73662306a36Sopenharmony_ci	ntb_spad_write(qp->ndev, spad + 1, 0);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!qp->msi_irq) {
73962306a36Sopenharmony_ci		qp->msi_irq = ntbm_msi_request_irq(qp->ndev, ntb_transport_isr,
74062306a36Sopenharmony_ci						   KBUILD_MODNAME, qp,
74162306a36Sopenharmony_ci						   &qp->msi_desc);
74262306a36Sopenharmony_ci		if (qp->msi_irq < 0) {
74362306a36Sopenharmony_ci			dev_warn(&qp->ndev->pdev->dev,
74462306a36Sopenharmony_ci				 "Unable to allocate MSI interrupt for qp%d\n",
74562306a36Sopenharmony_ci				 qp_num);
74662306a36Sopenharmony_ci			return;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	rc = ntb_spad_write(qp->ndev, spad, qp->msi_desc.addr_offset);
75162306a36Sopenharmony_ci	if (rc)
75262306a36Sopenharmony_ci		goto err_free_interrupt;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	rc = ntb_spad_write(qp->ndev, spad + 1, qp->msi_desc.data);
75562306a36Sopenharmony_ci	if (rc)
75662306a36Sopenharmony_ci		goto err_free_interrupt;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	dev_dbg(&qp->ndev->pdev->dev, "QP%d MSI %d addr=%x data=%x\n",
75962306a36Sopenharmony_ci		qp_num, qp->msi_irq, qp->msi_desc.addr_offset,
76062306a36Sopenharmony_ci		qp->msi_desc.data);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cierr_free_interrupt:
76562306a36Sopenharmony_ci	devm_free_irq(&nt->ndev->dev, qp->msi_irq, qp);
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void ntb_transport_msi_peer_desc_changed(struct ntb_transport_ctx *nt)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	int i;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	dev_dbg(&nt->ndev->pdev->dev, "Peer MSI descriptors changed");
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	for (i = 0; i < nt->qp_count; i++)
77562306a36Sopenharmony_ci		ntb_transport_setup_qp_peer_msi(nt, i);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic void ntb_transport_msi_desc_changed(void *data)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct ntb_transport_ctx *nt = data;
78162306a36Sopenharmony_ci	int i;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	dev_dbg(&nt->ndev->pdev->dev, "MSI descriptors changed");
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	for (i = 0; i < nt->qp_count; i++)
78662306a36Sopenharmony_ci		ntb_transport_setup_qp_msi(nt, i);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ntb_peer_db_set(nt->ndev, nt->msi_db_mask);
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	struct ntb_transport_mw *mw = &nt->mw_vec[num_mw];
79462306a36Sopenharmony_ci	struct pci_dev *pdev = nt->ndev->pdev;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (!mw->virt_addr)
79762306a36Sopenharmony_ci		return;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	ntb_mw_clear_trans(nt->ndev, PIDX, num_mw);
80062306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, mw->alloc_size,
80162306a36Sopenharmony_ci			  mw->alloc_addr, mw->dma_addr);
80262306a36Sopenharmony_ci	mw->xlat_size = 0;
80362306a36Sopenharmony_ci	mw->buff_size = 0;
80462306a36Sopenharmony_ci	mw->alloc_size = 0;
80562306a36Sopenharmony_ci	mw->alloc_addr = NULL;
80662306a36Sopenharmony_ci	mw->virt_addr = NULL;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic int ntb_alloc_mw_buffer(struct ntb_transport_mw *mw,
81062306a36Sopenharmony_ci			       struct device *dma_dev, size_t align)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	dma_addr_t dma_addr;
81362306a36Sopenharmony_ci	void *alloc_addr, *virt_addr;
81462306a36Sopenharmony_ci	int rc;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	alloc_addr = dma_alloc_coherent(dma_dev, mw->alloc_size,
81762306a36Sopenharmony_ci					&dma_addr, GFP_KERNEL);
81862306a36Sopenharmony_ci	if (!alloc_addr) {
81962306a36Sopenharmony_ci		dev_err(dma_dev, "Unable to alloc MW buff of size %zu\n",
82062306a36Sopenharmony_ci			mw->alloc_size);
82162306a36Sopenharmony_ci		return -ENOMEM;
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci	virt_addr = alloc_addr;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/*
82662306a36Sopenharmony_ci	 * we must ensure that the memory address allocated is BAR size
82762306a36Sopenharmony_ci	 * aligned in order for the XLAT register to take the value. This
82862306a36Sopenharmony_ci	 * is a requirement of the hardware. It is recommended to setup CMA
82962306a36Sopenharmony_ci	 * for BAR sizes equal or greater than 4MB.
83062306a36Sopenharmony_ci	 */
83162306a36Sopenharmony_ci	if (!IS_ALIGNED(dma_addr, align)) {
83262306a36Sopenharmony_ci		if (mw->alloc_size > mw->buff_size) {
83362306a36Sopenharmony_ci			virt_addr = PTR_ALIGN(alloc_addr, align);
83462306a36Sopenharmony_ci			dma_addr = ALIGN(dma_addr, align);
83562306a36Sopenharmony_ci		} else {
83662306a36Sopenharmony_ci			rc = -ENOMEM;
83762306a36Sopenharmony_ci			goto err;
83862306a36Sopenharmony_ci		}
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	mw->alloc_addr = alloc_addr;
84262306a36Sopenharmony_ci	mw->virt_addr = virt_addr;
84362306a36Sopenharmony_ci	mw->dma_addr = dma_addr;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	return 0;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cierr:
84862306a36Sopenharmony_ci	dma_free_coherent(dma_dev, mw->alloc_size, alloc_addr, dma_addr);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	return rc;
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw,
85462306a36Sopenharmony_ci		      resource_size_t size)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	struct ntb_transport_mw *mw = &nt->mw_vec[num_mw];
85762306a36Sopenharmony_ci	struct pci_dev *pdev = nt->ndev->pdev;
85862306a36Sopenharmony_ci	size_t xlat_size, buff_size;
85962306a36Sopenharmony_ci	resource_size_t xlat_align;
86062306a36Sopenharmony_ci	resource_size_t xlat_align_size;
86162306a36Sopenharmony_ci	int rc;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (!size)
86462306a36Sopenharmony_ci		return -EINVAL;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	rc = ntb_mw_get_align(nt->ndev, PIDX, num_mw, &xlat_align,
86762306a36Sopenharmony_ci			      &xlat_align_size, NULL);
86862306a36Sopenharmony_ci	if (rc)
86962306a36Sopenharmony_ci		return rc;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	xlat_size = round_up(size, xlat_align_size);
87262306a36Sopenharmony_ci	buff_size = round_up(size, xlat_align);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* No need to re-setup */
87562306a36Sopenharmony_ci	if (mw->xlat_size == xlat_size)
87662306a36Sopenharmony_ci		return 0;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (mw->buff_size)
87962306a36Sopenharmony_ci		ntb_free_mw(nt, num_mw);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* Alloc memory for receiving data.  Must be aligned */
88262306a36Sopenharmony_ci	mw->xlat_size = xlat_size;
88362306a36Sopenharmony_ci	mw->buff_size = buff_size;
88462306a36Sopenharmony_ci	mw->alloc_size = buff_size;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align);
88762306a36Sopenharmony_ci	if (rc) {
88862306a36Sopenharmony_ci		mw->alloc_size *= 2;
88962306a36Sopenharmony_ci		rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align);
89062306a36Sopenharmony_ci		if (rc) {
89162306a36Sopenharmony_ci			dev_err(&pdev->dev,
89262306a36Sopenharmony_ci				"Unable to alloc aligned MW buff\n");
89362306a36Sopenharmony_ci			mw->xlat_size = 0;
89462306a36Sopenharmony_ci			mw->buff_size = 0;
89562306a36Sopenharmony_ci			mw->alloc_size = 0;
89662306a36Sopenharmony_ci			return rc;
89762306a36Sopenharmony_ci		}
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* Notify HW the memory location of the receive buffer */
90162306a36Sopenharmony_ci	rc = ntb_mw_set_trans(nt->ndev, PIDX, num_mw, mw->dma_addr,
90262306a36Sopenharmony_ci			      mw->xlat_size);
90362306a36Sopenharmony_ci	if (rc) {
90462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to set mw%d translation", num_mw);
90562306a36Sopenharmony_ci		ntb_free_mw(nt, num_mw);
90662306a36Sopenharmony_ci		return -EIO;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	return 0;
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic void ntb_qp_link_context_reset(struct ntb_transport_qp *qp)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	qp->link_is_up = false;
91562306a36Sopenharmony_ci	qp->active = false;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	qp->tx_index = 0;
91862306a36Sopenharmony_ci	qp->rx_index = 0;
91962306a36Sopenharmony_ci	qp->rx_bytes = 0;
92062306a36Sopenharmony_ci	qp->rx_pkts = 0;
92162306a36Sopenharmony_ci	qp->rx_ring_empty = 0;
92262306a36Sopenharmony_ci	qp->rx_err_no_buf = 0;
92362306a36Sopenharmony_ci	qp->rx_err_oflow = 0;
92462306a36Sopenharmony_ci	qp->rx_err_ver = 0;
92562306a36Sopenharmony_ci	qp->rx_memcpy = 0;
92662306a36Sopenharmony_ci	qp->rx_async = 0;
92762306a36Sopenharmony_ci	qp->tx_bytes = 0;
92862306a36Sopenharmony_ci	qp->tx_pkts = 0;
92962306a36Sopenharmony_ci	qp->tx_ring_full = 0;
93062306a36Sopenharmony_ci	qp->tx_err_no_buf = 0;
93162306a36Sopenharmony_ci	qp->tx_memcpy = 0;
93262306a36Sopenharmony_ci	qp->tx_async = 0;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void ntb_qp_link_down_reset(struct ntb_transport_qp *qp)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	ntb_qp_link_context_reset(qp);
93862306a36Sopenharmony_ci	if (qp->remote_rx_info)
93962306a36Sopenharmony_ci		qp->remote_rx_info->entry = qp->rx_max_entry - 1;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic void ntb_qp_link_cleanup(struct ntb_transport_qp *qp)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct ntb_transport_ctx *nt = qp->transport;
94562306a36Sopenharmony_ci	struct pci_dev *pdev = nt->ndev->pdev;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	dev_info(&pdev->dev, "qp %d: Link Cleanup\n", qp->qp_num);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	cancel_delayed_work_sync(&qp->link_work);
95062306a36Sopenharmony_ci	ntb_qp_link_down_reset(qp);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	if (qp->event_handler)
95362306a36Sopenharmony_ci		qp->event_handler(qp->cb_data, qp->link_is_up);
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic void ntb_qp_link_cleanup_work(struct work_struct *work)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct ntb_transport_qp *qp = container_of(work,
95962306a36Sopenharmony_ci						   struct ntb_transport_qp,
96062306a36Sopenharmony_ci						   link_cleanup);
96162306a36Sopenharmony_ci	struct ntb_transport_ctx *nt = qp->transport;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	ntb_qp_link_cleanup(qp);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (nt->link_is_up)
96662306a36Sopenharmony_ci		schedule_delayed_work(&qp->link_work,
96762306a36Sopenharmony_ci				      msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT));
96862306a36Sopenharmony_ci}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_cistatic void ntb_qp_link_down(struct ntb_transport_qp *qp)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	schedule_work(&qp->link_cleanup);
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic void ntb_transport_link_cleanup(struct ntb_transport_ctx *nt)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct ntb_transport_qp *qp;
97862306a36Sopenharmony_ci	u64 qp_bitmap_alloc;
97962306a36Sopenharmony_ci	unsigned int i, count;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	qp_bitmap_alloc = nt->qp_bitmap & ~nt->qp_bitmap_free;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Pass along the info to any clients */
98462306a36Sopenharmony_ci	for (i = 0; i < nt->qp_count; i++)
98562306a36Sopenharmony_ci		if (qp_bitmap_alloc & BIT_ULL(i)) {
98662306a36Sopenharmony_ci			qp = &nt->qp_vec[i];
98762306a36Sopenharmony_ci			ntb_qp_link_cleanup(qp);
98862306a36Sopenharmony_ci			cancel_work_sync(&qp->link_cleanup);
98962306a36Sopenharmony_ci			cancel_delayed_work_sync(&qp->link_work);
99062306a36Sopenharmony_ci		}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (!nt->link_is_up)
99362306a36Sopenharmony_ci		cancel_delayed_work_sync(&nt->link_work);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	for (i = 0; i < nt->mw_count; i++)
99662306a36Sopenharmony_ci		ntb_free_mw(nt, i);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* The scratchpad registers keep the values if the remote side
99962306a36Sopenharmony_ci	 * goes down, blast them now to give them a sane value the next
100062306a36Sopenharmony_ci	 * time they are accessed
100162306a36Sopenharmony_ci	 */
100262306a36Sopenharmony_ci	count = ntb_spad_count(nt->ndev);
100362306a36Sopenharmony_ci	for (i = 0; i < count; i++)
100462306a36Sopenharmony_ci		ntb_spad_write(nt->ndev, i, 0);
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic void ntb_transport_link_cleanup_work(struct work_struct *work)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct ntb_transport_ctx *nt =
101062306a36Sopenharmony_ci		container_of(work, struct ntb_transport_ctx, link_cleanup);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	ntb_transport_link_cleanup(nt);
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic void ntb_transport_event_callback(void *data)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct ntb_transport_ctx *nt = data;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (ntb_link_is_up(nt->ndev, NULL, NULL) == 1)
102062306a36Sopenharmony_ci		schedule_delayed_work(&nt->link_work, 0);
102162306a36Sopenharmony_ci	else
102262306a36Sopenharmony_ci		schedule_work(&nt->link_cleanup);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic void ntb_transport_link_work(struct work_struct *work)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct ntb_transport_ctx *nt =
102862306a36Sopenharmony_ci		container_of(work, struct ntb_transport_ctx, link_work.work);
102962306a36Sopenharmony_ci	struct ntb_dev *ndev = nt->ndev;
103062306a36Sopenharmony_ci	struct pci_dev *pdev = ndev->pdev;
103162306a36Sopenharmony_ci	resource_size_t size;
103262306a36Sopenharmony_ci	u32 val;
103362306a36Sopenharmony_ci	int rc = 0, i, spad;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* send the local info, in the opposite order of the way we read it */
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if (nt->use_msi) {
103862306a36Sopenharmony_ci		rc = ntb_msi_setup_mws(ndev);
103962306a36Sopenharmony_ci		if (rc) {
104062306a36Sopenharmony_ci			dev_warn(&pdev->dev,
104162306a36Sopenharmony_ci				 "Failed to register MSI memory window: %d\n",
104262306a36Sopenharmony_ci				 rc);
104362306a36Sopenharmony_ci			nt->use_msi = false;
104462306a36Sopenharmony_ci		}
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	for (i = 0; i < nt->qp_count; i++)
104862306a36Sopenharmony_ci		ntb_transport_setup_qp_msi(nt, i);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	for (i = 0; i < nt->mw_count; i++) {
105162306a36Sopenharmony_ci		size = nt->mw_vec[i].phys_size;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci		if (max_mw_size && size > max_mw_size)
105462306a36Sopenharmony_ci			size = max_mw_size;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		spad = MW0_SZ_HIGH + (i * 2);
105762306a36Sopenharmony_ci		ntb_peer_spad_write(ndev, PIDX, spad, upper_32_bits(size));
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		spad = MW0_SZ_LOW + (i * 2);
106062306a36Sopenharmony_ci		ntb_peer_spad_write(ndev, PIDX, spad, lower_32_bits(size));
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	ntb_peer_spad_write(ndev, PIDX, NUM_MWS, nt->mw_count);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	ntb_peer_spad_write(ndev, PIDX, NUM_QPS, nt->qp_count);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	ntb_peer_spad_write(ndev, PIDX, VERSION, NTB_TRANSPORT_VERSION);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Query the remote side for its info */
107062306a36Sopenharmony_ci	val = ntb_spad_read(ndev, VERSION);
107162306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Remote version = %d\n", val);
107262306a36Sopenharmony_ci	if (val != NTB_TRANSPORT_VERSION)
107362306a36Sopenharmony_ci		goto out;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	val = ntb_spad_read(ndev, NUM_QPS);
107662306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Remote max number of qps = %d\n", val);
107762306a36Sopenharmony_ci	if (val != nt->qp_count)
107862306a36Sopenharmony_ci		goto out;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	val = ntb_spad_read(ndev, NUM_MWS);
108162306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Remote number of mws = %d\n", val);
108262306a36Sopenharmony_ci	if (val != nt->mw_count)
108362306a36Sopenharmony_ci		goto out;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	for (i = 0; i < nt->mw_count; i++) {
108662306a36Sopenharmony_ci		u64 val64;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		val = ntb_spad_read(ndev, MW0_SZ_HIGH + (i * 2));
108962306a36Sopenharmony_ci		val64 = (u64)val << 32;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		val = ntb_spad_read(ndev, MW0_SZ_LOW + (i * 2));
109262306a36Sopenharmony_ci		val64 |= val;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		dev_dbg(&pdev->dev, "Remote MW%d size = %#llx\n", i, val64);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci		rc = ntb_set_mw(nt, i, val64);
109762306a36Sopenharmony_ci		if (rc)
109862306a36Sopenharmony_ci			goto out1;
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	nt->link_is_up = true;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	for (i = 0; i < nt->qp_count; i++) {
110462306a36Sopenharmony_ci		struct ntb_transport_qp *qp = &nt->qp_vec[i];
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci		ntb_transport_setup_qp_mw(nt, i);
110762306a36Sopenharmony_ci		ntb_transport_setup_qp_peer_msi(nt, i);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci		if (qp->client_ready)
111062306a36Sopenharmony_ci			schedule_delayed_work(&qp->link_work, 0);
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	return;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ciout1:
111662306a36Sopenharmony_ci	for (i = 0; i < nt->mw_count; i++)
111762306a36Sopenharmony_ci		ntb_free_mw(nt, i);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	/* if there's an actual failure, we should just bail */
112062306a36Sopenharmony_ci	if (rc < 0)
112162306a36Sopenharmony_ci		return;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ciout:
112462306a36Sopenharmony_ci	if (ntb_link_is_up(ndev, NULL, NULL) == 1)
112562306a36Sopenharmony_ci		schedule_delayed_work(&nt->link_work,
112662306a36Sopenharmony_ci				      msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT));
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic void ntb_qp_link_work(struct work_struct *work)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	struct ntb_transport_qp *qp = container_of(work,
113262306a36Sopenharmony_ci						   struct ntb_transport_qp,
113362306a36Sopenharmony_ci						   link_work.work);
113462306a36Sopenharmony_ci	struct pci_dev *pdev = qp->ndev->pdev;
113562306a36Sopenharmony_ci	struct ntb_transport_ctx *nt = qp->transport;
113662306a36Sopenharmony_ci	int val;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	WARN_ON(!nt->link_is_up);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	val = ntb_spad_read(nt->ndev, QP_LINKS);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	ntb_peer_spad_write(nt->ndev, PIDX, QP_LINKS, val | BIT(qp->qp_num));
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	/* query remote spad for qp ready bits */
114562306a36Sopenharmony_ci	dev_dbg_ratelimited(&pdev->dev, "Remote QP link status = %x\n", val);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	/* See if the remote side is up */
114862306a36Sopenharmony_ci	if (val & BIT(qp->qp_num)) {
114962306a36Sopenharmony_ci		dev_info(&pdev->dev, "qp %d: Link Up\n", qp->qp_num);
115062306a36Sopenharmony_ci		qp->link_is_up = true;
115162306a36Sopenharmony_ci		qp->active = true;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		if (qp->event_handler)
115462306a36Sopenharmony_ci			qp->event_handler(qp->cb_data, qp->link_is_up);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		if (qp->active)
115762306a36Sopenharmony_ci			tasklet_schedule(&qp->rxc_db_work);
115862306a36Sopenharmony_ci	} else if (nt->link_is_up)
115962306a36Sopenharmony_ci		schedule_delayed_work(&qp->link_work,
116062306a36Sopenharmony_ci				      msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT));
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic int ntb_transport_init_queue(struct ntb_transport_ctx *nt,
116462306a36Sopenharmony_ci				    unsigned int qp_num)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct ntb_transport_qp *qp;
116762306a36Sopenharmony_ci	phys_addr_t mw_base;
116862306a36Sopenharmony_ci	resource_size_t mw_size;
116962306a36Sopenharmony_ci	unsigned int num_qps_mw, tx_size;
117062306a36Sopenharmony_ci	unsigned int mw_num, mw_count, qp_count;
117162306a36Sopenharmony_ci	u64 qp_offset;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	mw_count = nt->mw_count;
117462306a36Sopenharmony_ci	qp_count = nt->qp_count;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	mw_num = QP_TO_MW(nt, qp_num);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	qp = &nt->qp_vec[qp_num];
117962306a36Sopenharmony_ci	qp->qp_num = qp_num;
118062306a36Sopenharmony_ci	qp->transport = nt;
118162306a36Sopenharmony_ci	qp->ndev = nt->ndev;
118262306a36Sopenharmony_ci	qp->client_ready = false;
118362306a36Sopenharmony_ci	qp->event_handler = NULL;
118462306a36Sopenharmony_ci	ntb_qp_link_context_reset(qp);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (mw_num < qp_count % mw_count)
118762306a36Sopenharmony_ci		num_qps_mw = qp_count / mw_count + 1;
118862306a36Sopenharmony_ci	else
118962306a36Sopenharmony_ci		num_qps_mw = qp_count / mw_count;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	mw_base = nt->mw_vec[mw_num].phys_addr;
119262306a36Sopenharmony_ci	mw_size = nt->mw_vec[mw_num].phys_size;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (max_mw_size && mw_size > max_mw_size)
119562306a36Sopenharmony_ci		mw_size = max_mw_size;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	tx_size = (unsigned int)mw_size / num_qps_mw;
119862306a36Sopenharmony_ci	qp_offset = tx_size * (qp_num / mw_count);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	qp->tx_mw_size = tx_size;
120162306a36Sopenharmony_ci	qp->tx_mw = nt->mw_vec[mw_num].vbase + qp_offset;
120262306a36Sopenharmony_ci	if (!qp->tx_mw)
120362306a36Sopenharmony_ci		return -EINVAL;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	qp->tx_mw_phys = mw_base + qp_offset;
120662306a36Sopenharmony_ci	if (!qp->tx_mw_phys)
120762306a36Sopenharmony_ci		return -EINVAL;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	tx_size -= sizeof(struct ntb_rx_info);
121062306a36Sopenharmony_ci	qp->rx_info = qp->tx_mw + tx_size;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	/* Due to housekeeping, there must be atleast 2 buffs */
121362306a36Sopenharmony_ci	qp->tx_max_frame = min(transport_mtu, tx_size / 2);
121462306a36Sopenharmony_ci	qp->tx_max_entry = tx_size / qp->tx_max_frame;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (nt->debugfs_node_dir) {
121762306a36Sopenharmony_ci		char debugfs_name[4];
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		snprintf(debugfs_name, 4, "qp%d", qp_num);
122062306a36Sopenharmony_ci		qp->debugfs_dir = debugfs_create_dir(debugfs_name,
122162306a36Sopenharmony_ci						     nt->debugfs_node_dir);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		qp->debugfs_stats = debugfs_create_file("stats", S_IRUSR,
122462306a36Sopenharmony_ci							qp->debugfs_dir, qp,
122562306a36Sopenharmony_ci							&ntb_qp_debugfs_stats);
122662306a36Sopenharmony_ci	} else {
122762306a36Sopenharmony_ci		qp->debugfs_dir = NULL;
122862306a36Sopenharmony_ci		qp->debugfs_stats = NULL;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&qp->link_work, ntb_qp_link_work);
123262306a36Sopenharmony_ci	INIT_WORK(&qp->link_cleanup, ntb_qp_link_cleanup_work);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	spin_lock_init(&qp->ntb_rx_q_lock);
123562306a36Sopenharmony_ci	spin_lock_init(&qp->ntb_tx_free_q_lock);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	INIT_LIST_HEAD(&qp->rx_post_q);
123862306a36Sopenharmony_ci	INIT_LIST_HEAD(&qp->rx_pend_q);
123962306a36Sopenharmony_ci	INIT_LIST_HEAD(&qp->rx_free_q);
124062306a36Sopenharmony_ci	INIT_LIST_HEAD(&qp->tx_free_q);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	tasklet_init(&qp->rxc_db_work, ntb_transport_rxc_db,
124362306a36Sopenharmony_ci		     (unsigned long)qp);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return 0;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct ntb_transport_ctx *nt;
125162306a36Sopenharmony_ci	struct ntb_transport_mw *mw;
125262306a36Sopenharmony_ci	unsigned int mw_count, qp_count, spad_count, max_mw_count_for_spads;
125362306a36Sopenharmony_ci	u64 qp_bitmap;
125462306a36Sopenharmony_ci	int node;
125562306a36Sopenharmony_ci	int rc, i;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	mw_count = ntb_peer_mw_count(ndev);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	if (!ndev->ops->mw_set_trans) {
126062306a36Sopenharmony_ci		dev_err(&ndev->dev, "Inbound MW based NTB API is required\n");
126162306a36Sopenharmony_ci		return -EINVAL;
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	if (ntb_db_is_unsafe(ndev))
126562306a36Sopenharmony_ci		dev_dbg(&ndev->dev,
126662306a36Sopenharmony_ci			"doorbell is unsafe, proceed anyway...\n");
126762306a36Sopenharmony_ci	if (ntb_spad_is_unsafe(ndev))
126862306a36Sopenharmony_ci		dev_dbg(&ndev->dev,
126962306a36Sopenharmony_ci			"scratchpad is unsafe, proceed anyway...\n");
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (ntb_peer_port_count(ndev) != NTB_DEF_PEER_CNT)
127262306a36Sopenharmony_ci		dev_warn(&ndev->dev, "Multi-port NTB devices unsupported\n");
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	node = dev_to_node(&ndev->dev);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	nt = kzalloc_node(sizeof(*nt), GFP_KERNEL, node);
127762306a36Sopenharmony_ci	if (!nt)
127862306a36Sopenharmony_ci		return -ENOMEM;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	nt->ndev = ndev;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	/*
128362306a36Sopenharmony_ci	 * If we are using MSI, and have at least one extra memory window,
128462306a36Sopenharmony_ci	 * we will reserve the last MW for the MSI window.
128562306a36Sopenharmony_ci	 */
128662306a36Sopenharmony_ci	if (use_msi && mw_count > 1) {
128762306a36Sopenharmony_ci		rc = ntb_msi_init(ndev, ntb_transport_msi_desc_changed);
128862306a36Sopenharmony_ci		if (!rc) {
128962306a36Sopenharmony_ci			mw_count -= 1;
129062306a36Sopenharmony_ci			nt->use_msi = true;
129162306a36Sopenharmony_ci		}
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	spad_count = ntb_spad_count(ndev);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	/* Limit the MW's based on the availability of scratchpads */
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (spad_count < NTB_TRANSPORT_MIN_SPADS) {
129962306a36Sopenharmony_ci		nt->mw_count = 0;
130062306a36Sopenharmony_ci		rc = -EINVAL;
130162306a36Sopenharmony_ci		goto err;
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	max_mw_count_for_spads = (spad_count - MW0_SZ_HIGH) / 2;
130562306a36Sopenharmony_ci	nt->mw_count = min(mw_count, max_mw_count_for_spads);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	nt->msi_spad_offset = nt->mw_count * 2 + MW0_SZ_HIGH;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	nt->mw_vec = kcalloc_node(mw_count, sizeof(*nt->mw_vec),
131062306a36Sopenharmony_ci				  GFP_KERNEL, node);
131162306a36Sopenharmony_ci	if (!nt->mw_vec) {
131262306a36Sopenharmony_ci		rc = -ENOMEM;
131362306a36Sopenharmony_ci		goto err;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	for (i = 0; i < mw_count; i++) {
131762306a36Sopenharmony_ci		mw = &nt->mw_vec[i];
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		rc = ntb_peer_mw_get_addr(ndev, i, &mw->phys_addr,
132062306a36Sopenharmony_ci					  &mw->phys_size);
132162306a36Sopenharmony_ci		if (rc)
132262306a36Sopenharmony_ci			goto err1;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		mw->vbase = ioremap_wc(mw->phys_addr, mw->phys_size);
132562306a36Sopenharmony_ci		if (!mw->vbase) {
132662306a36Sopenharmony_ci			rc = -ENOMEM;
132762306a36Sopenharmony_ci			goto err1;
132862306a36Sopenharmony_ci		}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		mw->buff_size = 0;
133162306a36Sopenharmony_ci		mw->xlat_size = 0;
133262306a36Sopenharmony_ci		mw->virt_addr = NULL;
133362306a36Sopenharmony_ci		mw->dma_addr = 0;
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	qp_bitmap = ntb_db_valid_mask(ndev);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	qp_count = ilog2(qp_bitmap);
133962306a36Sopenharmony_ci	if (nt->use_msi) {
134062306a36Sopenharmony_ci		qp_count -= 1;
134162306a36Sopenharmony_ci		nt->msi_db_mask = 1 << qp_count;
134262306a36Sopenharmony_ci		ntb_db_clear_mask(ndev, nt->msi_db_mask);
134362306a36Sopenharmony_ci	}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (max_num_clients && max_num_clients < qp_count)
134662306a36Sopenharmony_ci		qp_count = max_num_clients;
134762306a36Sopenharmony_ci	else if (nt->mw_count < qp_count)
134862306a36Sopenharmony_ci		qp_count = nt->mw_count;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	qp_bitmap &= BIT_ULL(qp_count) - 1;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	nt->qp_count = qp_count;
135362306a36Sopenharmony_ci	nt->qp_bitmap = qp_bitmap;
135462306a36Sopenharmony_ci	nt->qp_bitmap_free = qp_bitmap;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	nt->qp_vec = kcalloc_node(qp_count, sizeof(*nt->qp_vec),
135762306a36Sopenharmony_ci				  GFP_KERNEL, node);
135862306a36Sopenharmony_ci	if (!nt->qp_vec) {
135962306a36Sopenharmony_ci		rc = -ENOMEM;
136062306a36Sopenharmony_ci		goto err1;
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	if (nt_debugfs_dir) {
136462306a36Sopenharmony_ci		nt->debugfs_node_dir =
136562306a36Sopenharmony_ci			debugfs_create_dir(pci_name(ndev->pdev),
136662306a36Sopenharmony_ci					   nt_debugfs_dir);
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	for (i = 0; i < qp_count; i++) {
137062306a36Sopenharmony_ci		rc = ntb_transport_init_queue(nt, i);
137162306a36Sopenharmony_ci		if (rc)
137262306a36Sopenharmony_ci			goto err2;
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&nt->link_work, ntb_transport_link_work);
137662306a36Sopenharmony_ci	INIT_WORK(&nt->link_cleanup, ntb_transport_link_cleanup_work);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	rc = ntb_set_ctx(ndev, nt, &ntb_transport_ops);
137962306a36Sopenharmony_ci	if (rc)
138062306a36Sopenharmony_ci		goto err2;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	INIT_LIST_HEAD(&nt->client_devs);
138362306a36Sopenharmony_ci	rc = ntb_bus_init(nt);
138462306a36Sopenharmony_ci	if (rc)
138562306a36Sopenharmony_ci		goto err3;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	nt->link_is_up = false;
138862306a36Sopenharmony_ci	ntb_link_enable(ndev, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
138962306a36Sopenharmony_ci	ntb_link_event(ndev);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	return 0;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_cierr3:
139462306a36Sopenharmony_ci	ntb_clear_ctx(ndev);
139562306a36Sopenharmony_cierr2:
139662306a36Sopenharmony_ci	kfree(nt->qp_vec);
139762306a36Sopenharmony_cierr1:
139862306a36Sopenharmony_ci	while (i--) {
139962306a36Sopenharmony_ci		mw = &nt->mw_vec[i];
140062306a36Sopenharmony_ci		iounmap(mw->vbase);
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci	kfree(nt->mw_vec);
140362306a36Sopenharmony_cierr:
140462306a36Sopenharmony_ci	kfree(nt);
140562306a36Sopenharmony_ci	return rc;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_cistatic void ntb_transport_free(struct ntb_client *self, struct ntb_dev *ndev)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct ntb_transport_ctx *nt = ndev->ctx;
141162306a36Sopenharmony_ci	struct ntb_transport_qp *qp;
141262306a36Sopenharmony_ci	u64 qp_bitmap_alloc;
141362306a36Sopenharmony_ci	int i;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	ntb_transport_link_cleanup(nt);
141662306a36Sopenharmony_ci	cancel_work_sync(&nt->link_cleanup);
141762306a36Sopenharmony_ci	cancel_delayed_work_sync(&nt->link_work);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	qp_bitmap_alloc = nt->qp_bitmap & ~nt->qp_bitmap_free;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	/* verify that all the qp's are freed */
142262306a36Sopenharmony_ci	for (i = 0; i < nt->qp_count; i++) {
142362306a36Sopenharmony_ci		qp = &nt->qp_vec[i];
142462306a36Sopenharmony_ci		if (qp_bitmap_alloc & BIT_ULL(i))
142562306a36Sopenharmony_ci			ntb_transport_free_queue(qp);
142662306a36Sopenharmony_ci		debugfs_remove_recursive(qp->debugfs_dir);
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	ntb_link_disable(ndev);
143062306a36Sopenharmony_ci	ntb_clear_ctx(ndev);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	ntb_bus_remove(nt);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	for (i = nt->mw_count; i--; ) {
143562306a36Sopenharmony_ci		ntb_free_mw(nt, i);
143662306a36Sopenharmony_ci		iounmap(nt->mw_vec[i].vbase);
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	kfree(nt->qp_vec);
144062306a36Sopenharmony_ci	kfree(nt->mw_vec);
144162306a36Sopenharmony_ci	kfree(nt);
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic void ntb_complete_rxc(struct ntb_transport_qp *qp)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
144762306a36Sopenharmony_ci	void *cb_data;
144862306a36Sopenharmony_ci	unsigned int len;
144962306a36Sopenharmony_ci	unsigned long irqflags;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	spin_lock_irqsave(&qp->ntb_rx_q_lock, irqflags);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	while (!list_empty(&qp->rx_post_q)) {
145462306a36Sopenharmony_ci		entry = list_first_entry(&qp->rx_post_q,
145562306a36Sopenharmony_ci					 struct ntb_queue_entry, entry);
145662306a36Sopenharmony_ci		if (!(entry->flags & DESC_DONE_FLAG))
145762306a36Sopenharmony_ci			break;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci		entry->rx_hdr->flags = 0;
146062306a36Sopenharmony_ci		iowrite32(entry->rx_index, &qp->rx_info->entry);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci		cb_data = entry->cb_data;
146362306a36Sopenharmony_ci		len = entry->len;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci		list_move_tail(&entry->entry, &qp->rx_free_q);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci		spin_unlock_irqrestore(&qp->ntb_rx_q_lock, irqflags);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci		if (qp->rx_handler && qp->client_ready)
147062306a36Sopenharmony_ci			qp->rx_handler(qp, qp->cb_data, cb_data, len);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci		spin_lock_irqsave(&qp->ntb_rx_q_lock, irqflags);
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	spin_unlock_irqrestore(&qp->ntb_rx_q_lock, irqflags);
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_cistatic void ntb_rx_copy_callback(void *data,
147962306a36Sopenharmony_ci				 const struct dmaengine_result *res)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	struct ntb_queue_entry *entry = data;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	/* we need to check DMA results if we are using DMA */
148462306a36Sopenharmony_ci	if (res) {
148562306a36Sopenharmony_ci		enum dmaengine_tx_result dma_err = res->result;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		switch (dma_err) {
148862306a36Sopenharmony_ci		case DMA_TRANS_READ_FAILED:
148962306a36Sopenharmony_ci		case DMA_TRANS_WRITE_FAILED:
149062306a36Sopenharmony_ci			entry->errors++;
149162306a36Sopenharmony_ci			fallthrough;
149262306a36Sopenharmony_ci		case DMA_TRANS_ABORTED:
149362306a36Sopenharmony_ci		{
149462306a36Sopenharmony_ci			struct ntb_transport_qp *qp = entry->qp;
149562306a36Sopenharmony_ci			void *offset = qp->rx_buff + qp->rx_max_frame *
149662306a36Sopenharmony_ci					qp->rx_index;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci			ntb_memcpy_rx(entry, offset);
149962306a36Sopenharmony_ci			qp->rx_memcpy++;
150062306a36Sopenharmony_ci			return;
150162306a36Sopenharmony_ci		}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci		case DMA_TRANS_NOERROR:
150462306a36Sopenharmony_ci		default:
150562306a36Sopenharmony_ci			break;
150662306a36Sopenharmony_ci		}
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	entry->flags |= DESC_DONE_FLAG;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	ntb_complete_rxc(entry->qp);
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_cistatic void ntb_memcpy_rx(struct ntb_queue_entry *entry, void *offset)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	void *buf = entry->buf;
151762306a36Sopenharmony_ci	size_t len = entry->len;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	memcpy(buf, offset, len);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	/* Ensure that the data is fully copied out before clearing the flag */
152262306a36Sopenharmony_ci	wmb();
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	ntb_rx_copy_callback(entry, NULL);
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_cistatic int ntb_async_rx_submit(struct ntb_queue_entry *entry, void *offset)
152862306a36Sopenharmony_ci{
152962306a36Sopenharmony_ci	struct dma_async_tx_descriptor *txd;
153062306a36Sopenharmony_ci	struct ntb_transport_qp *qp = entry->qp;
153162306a36Sopenharmony_ci	struct dma_chan *chan = qp->rx_dma_chan;
153262306a36Sopenharmony_ci	struct dma_device *device;
153362306a36Sopenharmony_ci	size_t pay_off, buff_off, len;
153462306a36Sopenharmony_ci	struct dmaengine_unmap_data *unmap;
153562306a36Sopenharmony_ci	dma_cookie_t cookie;
153662306a36Sopenharmony_ci	void *buf = entry->buf;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	len = entry->len;
153962306a36Sopenharmony_ci	device = chan->device;
154062306a36Sopenharmony_ci	pay_off = (size_t)offset & ~PAGE_MASK;
154162306a36Sopenharmony_ci	buff_off = (size_t)buf & ~PAGE_MASK;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	if (!is_dma_copy_aligned(device, pay_off, buff_off, len))
154462306a36Sopenharmony_ci		goto err;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT);
154762306a36Sopenharmony_ci	if (!unmap)
154862306a36Sopenharmony_ci		goto err;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	unmap->len = len;
155162306a36Sopenharmony_ci	unmap->addr[0] = dma_map_page(device->dev, virt_to_page(offset),
155262306a36Sopenharmony_ci				      pay_off, len, DMA_TO_DEVICE);
155362306a36Sopenharmony_ci	if (dma_mapping_error(device->dev, unmap->addr[0]))
155462306a36Sopenharmony_ci		goto err_get_unmap;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	unmap->to_cnt = 1;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	unmap->addr[1] = dma_map_page(device->dev, virt_to_page(buf),
155962306a36Sopenharmony_ci				      buff_off, len, DMA_FROM_DEVICE);
156062306a36Sopenharmony_ci	if (dma_mapping_error(device->dev, unmap->addr[1]))
156162306a36Sopenharmony_ci		goto err_get_unmap;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	unmap->from_cnt = 1;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	txd = device->device_prep_dma_memcpy(chan, unmap->addr[1],
156662306a36Sopenharmony_ci					     unmap->addr[0], len,
156762306a36Sopenharmony_ci					     DMA_PREP_INTERRUPT);
156862306a36Sopenharmony_ci	if (!txd)
156962306a36Sopenharmony_ci		goto err_get_unmap;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	txd->callback_result = ntb_rx_copy_callback;
157262306a36Sopenharmony_ci	txd->callback_param = entry;
157362306a36Sopenharmony_ci	dma_set_unmap(txd, unmap);
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	cookie = dmaengine_submit(txd);
157662306a36Sopenharmony_ci	if (dma_submit_error(cookie))
157762306a36Sopenharmony_ci		goto err_set_unmap;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	qp->last_cookie = cookie;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	qp->rx_async++;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	return 0;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_cierr_set_unmap:
158862306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
158962306a36Sopenharmony_cierr_get_unmap:
159062306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
159162306a36Sopenharmony_cierr:
159262306a36Sopenharmony_ci	return -ENXIO;
159362306a36Sopenharmony_ci}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_cistatic void ntb_async_rx(struct ntb_queue_entry *entry, void *offset)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	struct ntb_transport_qp *qp = entry->qp;
159862306a36Sopenharmony_ci	struct dma_chan *chan = qp->rx_dma_chan;
159962306a36Sopenharmony_ci	int res;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (!chan)
160262306a36Sopenharmony_ci		goto err;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (entry->len < copy_bytes)
160562306a36Sopenharmony_ci		goto err;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	res = ntb_async_rx_submit(entry, offset);
160862306a36Sopenharmony_ci	if (res < 0)
160962306a36Sopenharmony_ci		goto err;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	if (!entry->retries)
161262306a36Sopenharmony_ci		qp->rx_async++;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	return;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_cierr:
161762306a36Sopenharmony_ci	ntb_memcpy_rx(entry, offset);
161862306a36Sopenharmony_ci	qp->rx_memcpy++;
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistatic int ntb_process_rxc(struct ntb_transport_qp *qp)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	struct ntb_payload_header *hdr;
162462306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
162562306a36Sopenharmony_ci	void *offset;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	offset = qp->rx_buff + qp->rx_max_frame * qp->rx_index;
162862306a36Sopenharmony_ci	hdr = offset + qp->rx_max_frame - sizeof(struct ntb_payload_header);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	dev_dbg(&qp->ndev->pdev->dev, "qp %d: RX ver %u len %d flags %x\n",
163162306a36Sopenharmony_ci		qp->qp_num, hdr->ver, hdr->len, hdr->flags);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (!(hdr->flags & DESC_DONE_FLAG)) {
163462306a36Sopenharmony_ci		dev_dbg(&qp->ndev->pdev->dev, "done flag not set\n");
163562306a36Sopenharmony_ci		qp->rx_ring_empty++;
163662306a36Sopenharmony_ci		return -EAGAIN;
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	if (hdr->flags & LINK_DOWN_FLAG) {
164062306a36Sopenharmony_ci		dev_dbg(&qp->ndev->pdev->dev, "link down flag set\n");
164162306a36Sopenharmony_ci		ntb_qp_link_down(qp);
164262306a36Sopenharmony_ci		hdr->flags = 0;
164362306a36Sopenharmony_ci		return -EAGAIN;
164462306a36Sopenharmony_ci	}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	if (hdr->ver != (u32)qp->rx_pkts) {
164762306a36Sopenharmony_ci		dev_dbg(&qp->ndev->pdev->dev,
164862306a36Sopenharmony_ci			"version mismatch, expected %llu - got %u\n",
164962306a36Sopenharmony_ci			qp->rx_pkts, hdr->ver);
165062306a36Sopenharmony_ci		qp->rx_err_ver++;
165162306a36Sopenharmony_ci		return -EIO;
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	entry = ntb_list_mv(&qp->ntb_rx_q_lock, &qp->rx_pend_q, &qp->rx_post_q);
165562306a36Sopenharmony_ci	if (!entry) {
165662306a36Sopenharmony_ci		dev_dbg(&qp->ndev->pdev->dev, "no receive buffer\n");
165762306a36Sopenharmony_ci		qp->rx_err_no_buf++;
165862306a36Sopenharmony_ci		return -EAGAIN;
165962306a36Sopenharmony_ci	}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	entry->rx_hdr = hdr;
166262306a36Sopenharmony_ci	entry->rx_index = qp->rx_index;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	if (hdr->len > entry->len) {
166562306a36Sopenharmony_ci		dev_dbg(&qp->ndev->pdev->dev,
166662306a36Sopenharmony_ci			"receive buffer overflow! Wanted %d got %d\n",
166762306a36Sopenharmony_ci			hdr->len, entry->len);
166862306a36Sopenharmony_ci		qp->rx_err_oflow++;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci		entry->len = -EIO;
167162306a36Sopenharmony_ci		entry->flags |= DESC_DONE_FLAG;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci		ntb_complete_rxc(qp);
167462306a36Sopenharmony_ci	} else {
167562306a36Sopenharmony_ci		dev_dbg(&qp->ndev->pdev->dev,
167662306a36Sopenharmony_ci			"RX OK index %u ver %u size %d into buf size %d\n",
167762306a36Sopenharmony_ci			qp->rx_index, hdr->ver, hdr->len, entry->len);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci		qp->rx_bytes += hdr->len;
168062306a36Sopenharmony_ci		qp->rx_pkts++;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		entry->len = hdr->len;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci		ntb_async_rx(entry, offset);
168562306a36Sopenharmony_ci	}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	qp->rx_index++;
168862306a36Sopenharmony_ci	qp->rx_index %= qp->rx_max_entry;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	return 0;
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_cistatic void ntb_transport_rxc_db(unsigned long data)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	struct ntb_transport_qp *qp = (void *)data;
169662306a36Sopenharmony_ci	int rc, i;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	dev_dbg(&qp->ndev->pdev->dev, "%s: doorbell %d received\n",
169962306a36Sopenharmony_ci		__func__, qp->qp_num);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	/* Limit the number of packets processed in a single interrupt to
170262306a36Sopenharmony_ci	 * provide fairness to others
170362306a36Sopenharmony_ci	 */
170462306a36Sopenharmony_ci	for (i = 0; i < qp->rx_max_entry; i++) {
170562306a36Sopenharmony_ci		rc = ntb_process_rxc(qp);
170662306a36Sopenharmony_ci		if (rc)
170762306a36Sopenharmony_ci			break;
170862306a36Sopenharmony_ci	}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	if (i && qp->rx_dma_chan)
171162306a36Sopenharmony_ci		dma_async_issue_pending(qp->rx_dma_chan);
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (i == qp->rx_max_entry) {
171462306a36Sopenharmony_ci		/* there is more work to do */
171562306a36Sopenharmony_ci		if (qp->active)
171662306a36Sopenharmony_ci			tasklet_schedule(&qp->rxc_db_work);
171762306a36Sopenharmony_ci	} else if (ntb_db_read(qp->ndev) & BIT_ULL(qp->qp_num)) {
171862306a36Sopenharmony_ci		/* the doorbell bit is set: clear it */
171962306a36Sopenharmony_ci		ntb_db_clear(qp->ndev, BIT_ULL(qp->qp_num));
172062306a36Sopenharmony_ci		/* ntb_db_read ensures ntb_db_clear write is committed */
172162306a36Sopenharmony_ci		ntb_db_read(qp->ndev);
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci		/* an interrupt may have arrived between finishing
172462306a36Sopenharmony_ci		 * ntb_process_rxc and clearing the doorbell bit:
172562306a36Sopenharmony_ci		 * there might be some more work to do.
172662306a36Sopenharmony_ci		 */
172762306a36Sopenharmony_ci		if (qp->active)
172862306a36Sopenharmony_ci			tasklet_schedule(&qp->rxc_db_work);
172962306a36Sopenharmony_ci	}
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic void ntb_tx_copy_callback(void *data,
173362306a36Sopenharmony_ci				 const struct dmaengine_result *res)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	struct ntb_queue_entry *entry = data;
173662306a36Sopenharmony_ci	struct ntb_transport_qp *qp = entry->qp;
173762306a36Sopenharmony_ci	struct ntb_payload_header __iomem *hdr = entry->tx_hdr;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	/* we need to check DMA results if we are using DMA */
174062306a36Sopenharmony_ci	if (res) {
174162306a36Sopenharmony_ci		enum dmaengine_tx_result dma_err = res->result;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci		switch (dma_err) {
174462306a36Sopenharmony_ci		case DMA_TRANS_READ_FAILED:
174562306a36Sopenharmony_ci		case DMA_TRANS_WRITE_FAILED:
174662306a36Sopenharmony_ci			entry->errors++;
174762306a36Sopenharmony_ci			fallthrough;
174862306a36Sopenharmony_ci		case DMA_TRANS_ABORTED:
174962306a36Sopenharmony_ci		{
175062306a36Sopenharmony_ci			void __iomem *offset =
175162306a36Sopenharmony_ci				qp->tx_mw + qp->tx_max_frame *
175262306a36Sopenharmony_ci				entry->tx_index;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci			/* resubmit via CPU */
175562306a36Sopenharmony_ci			ntb_memcpy_tx(entry, offset);
175662306a36Sopenharmony_ci			qp->tx_memcpy++;
175762306a36Sopenharmony_ci			return;
175862306a36Sopenharmony_ci		}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci		case DMA_TRANS_NOERROR:
176162306a36Sopenharmony_ci		default:
176262306a36Sopenharmony_ci			break;
176362306a36Sopenharmony_ci		}
176462306a36Sopenharmony_ci	}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	iowrite32(entry->flags | DESC_DONE_FLAG, &hdr->flags);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	if (qp->use_msi)
176962306a36Sopenharmony_ci		ntb_msi_peer_trigger(qp->ndev, PIDX, &qp->peer_msi_desc);
177062306a36Sopenharmony_ci	else
177162306a36Sopenharmony_ci		ntb_peer_db_set(qp->ndev, BIT_ULL(qp->qp_num));
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	/* The entry length can only be zero if the packet is intended to be a
177462306a36Sopenharmony_ci	 * "link down" or similar.  Since no payload is being sent in these
177562306a36Sopenharmony_ci	 * cases, there is nothing to add to the completion queue.
177662306a36Sopenharmony_ci	 */
177762306a36Sopenharmony_ci	if (entry->len > 0) {
177862306a36Sopenharmony_ci		qp->tx_bytes += entry->len;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci		if (qp->tx_handler)
178162306a36Sopenharmony_ci			qp->tx_handler(qp, qp->cb_data, entry->cb_data,
178262306a36Sopenharmony_ci				       entry->len);
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q);
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_cistatic void ntb_memcpy_tx(struct ntb_queue_entry *entry, void __iomem *offset)
178962306a36Sopenharmony_ci{
179062306a36Sopenharmony_ci#ifdef ARCH_HAS_NOCACHE_UACCESS
179162306a36Sopenharmony_ci	/*
179262306a36Sopenharmony_ci	 * Using non-temporal mov to improve performance on non-cached
179362306a36Sopenharmony_ci	 * writes, even though we aren't actually copying from user space.
179462306a36Sopenharmony_ci	 */
179562306a36Sopenharmony_ci	__copy_from_user_inatomic_nocache(offset, entry->buf, entry->len);
179662306a36Sopenharmony_ci#else
179762306a36Sopenharmony_ci	memcpy_toio(offset, entry->buf, entry->len);
179862306a36Sopenharmony_ci#endif
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	/* Ensure that the data is fully copied out before setting the flags */
180162306a36Sopenharmony_ci	wmb();
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	ntb_tx_copy_callback(entry, NULL);
180462306a36Sopenharmony_ci}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_cistatic int ntb_async_tx_submit(struct ntb_transport_qp *qp,
180762306a36Sopenharmony_ci			       struct ntb_queue_entry *entry)
180862306a36Sopenharmony_ci{
180962306a36Sopenharmony_ci	struct dma_async_tx_descriptor *txd;
181062306a36Sopenharmony_ci	struct dma_chan *chan = qp->tx_dma_chan;
181162306a36Sopenharmony_ci	struct dma_device *device;
181262306a36Sopenharmony_ci	size_t len = entry->len;
181362306a36Sopenharmony_ci	void *buf = entry->buf;
181462306a36Sopenharmony_ci	size_t dest_off, buff_off;
181562306a36Sopenharmony_ci	struct dmaengine_unmap_data *unmap;
181662306a36Sopenharmony_ci	dma_addr_t dest;
181762306a36Sopenharmony_ci	dma_cookie_t cookie;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	device = chan->device;
182062306a36Sopenharmony_ci	dest = qp->tx_mw_dma_addr + qp->tx_max_frame * entry->tx_index;
182162306a36Sopenharmony_ci	buff_off = (size_t)buf & ~PAGE_MASK;
182262306a36Sopenharmony_ci	dest_off = (size_t)dest & ~PAGE_MASK;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	if (!is_dma_copy_aligned(device, buff_off, dest_off, len))
182562306a36Sopenharmony_ci		goto err;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	unmap = dmaengine_get_unmap_data(device->dev, 1, GFP_NOWAIT);
182862306a36Sopenharmony_ci	if (!unmap)
182962306a36Sopenharmony_ci		goto err;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	unmap->len = len;
183262306a36Sopenharmony_ci	unmap->addr[0] = dma_map_page(device->dev, virt_to_page(buf),
183362306a36Sopenharmony_ci				      buff_off, len, DMA_TO_DEVICE);
183462306a36Sopenharmony_ci	if (dma_mapping_error(device->dev, unmap->addr[0]))
183562306a36Sopenharmony_ci		goto err_get_unmap;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	unmap->to_cnt = 1;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	txd = device->device_prep_dma_memcpy(chan, dest, unmap->addr[0], len,
184062306a36Sopenharmony_ci					     DMA_PREP_INTERRUPT);
184162306a36Sopenharmony_ci	if (!txd)
184262306a36Sopenharmony_ci		goto err_get_unmap;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	txd->callback_result = ntb_tx_copy_callback;
184562306a36Sopenharmony_ci	txd->callback_param = entry;
184662306a36Sopenharmony_ci	dma_set_unmap(txd, unmap);
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	cookie = dmaengine_submit(txd);
184962306a36Sopenharmony_ci	if (dma_submit_error(cookie))
185062306a36Sopenharmony_ci		goto err_set_unmap;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	dma_async_issue_pending(chan);
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	return 0;
185762306a36Sopenharmony_cierr_set_unmap:
185862306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
185962306a36Sopenharmony_cierr_get_unmap:
186062306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
186162306a36Sopenharmony_cierr:
186262306a36Sopenharmony_ci	return -ENXIO;
186362306a36Sopenharmony_ci}
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_cistatic void ntb_async_tx(struct ntb_transport_qp *qp,
186662306a36Sopenharmony_ci			 struct ntb_queue_entry *entry)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	struct ntb_payload_header __iomem *hdr;
186962306a36Sopenharmony_ci	struct dma_chan *chan = qp->tx_dma_chan;
187062306a36Sopenharmony_ci	void __iomem *offset;
187162306a36Sopenharmony_ci	int res;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	entry->tx_index = qp->tx_index;
187462306a36Sopenharmony_ci	offset = qp->tx_mw + qp->tx_max_frame * entry->tx_index;
187562306a36Sopenharmony_ci	hdr = offset + qp->tx_max_frame - sizeof(struct ntb_payload_header);
187662306a36Sopenharmony_ci	entry->tx_hdr = hdr;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	iowrite32(entry->len, &hdr->len);
187962306a36Sopenharmony_ci	iowrite32((u32)qp->tx_pkts, &hdr->ver);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	if (!chan)
188262306a36Sopenharmony_ci		goto err;
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	if (entry->len < copy_bytes)
188562306a36Sopenharmony_ci		goto err;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	res = ntb_async_tx_submit(qp, entry);
188862306a36Sopenharmony_ci	if (res < 0)
188962306a36Sopenharmony_ci		goto err;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	if (!entry->retries)
189262306a36Sopenharmony_ci		qp->tx_async++;
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	return;
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_cierr:
189762306a36Sopenharmony_ci	ntb_memcpy_tx(entry, offset);
189862306a36Sopenharmony_ci	qp->tx_memcpy++;
189962306a36Sopenharmony_ci}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_cistatic int ntb_process_tx(struct ntb_transport_qp *qp,
190262306a36Sopenharmony_ci			  struct ntb_queue_entry *entry)
190362306a36Sopenharmony_ci{
190462306a36Sopenharmony_ci	if (!ntb_transport_tx_free_entry(qp)) {
190562306a36Sopenharmony_ci		qp->tx_ring_full++;
190662306a36Sopenharmony_ci		return -EAGAIN;
190762306a36Sopenharmony_ci	}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) {
191062306a36Sopenharmony_ci		if (qp->tx_handler)
191162306a36Sopenharmony_ci			qp->tx_handler(qp, qp->cb_data, NULL, -EIO);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci		ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
191462306a36Sopenharmony_ci			     &qp->tx_free_q);
191562306a36Sopenharmony_ci		return 0;
191662306a36Sopenharmony_ci	}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	ntb_async_tx(qp, entry);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	qp->tx_index++;
192162306a36Sopenharmony_ci	qp->tx_index %= qp->tx_max_entry;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	qp->tx_pkts++;
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	return 0;
192662306a36Sopenharmony_ci}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_cistatic void ntb_send_link_down(struct ntb_transport_qp *qp)
192962306a36Sopenharmony_ci{
193062306a36Sopenharmony_ci	struct pci_dev *pdev = qp->ndev->pdev;
193162306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
193262306a36Sopenharmony_ci	int i, rc;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	if (!qp->link_is_up)
193562306a36Sopenharmony_ci		return;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	dev_info(&pdev->dev, "qp %d: Send Link Down\n", qp->qp_num);
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	for (i = 0; i < NTB_LINK_DOWN_TIMEOUT; i++) {
194062306a36Sopenharmony_ci		entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
194162306a36Sopenharmony_ci		if (entry)
194262306a36Sopenharmony_ci			break;
194362306a36Sopenharmony_ci		msleep(100);
194462306a36Sopenharmony_ci	}
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	if (!entry)
194762306a36Sopenharmony_ci		return;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	entry->cb_data = NULL;
195062306a36Sopenharmony_ci	entry->buf = NULL;
195162306a36Sopenharmony_ci	entry->len = 0;
195262306a36Sopenharmony_ci	entry->flags = LINK_DOWN_FLAG;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	rc = ntb_process_tx(qp, entry);
195562306a36Sopenharmony_ci	if (rc)
195662306a36Sopenharmony_ci		dev_err(&pdev->dev, "ntb: QP%d unable to send linkdown msg\n",
195762306a36Sopenharmony_ci			qp->qp_num);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	ntb_qp_link_down_reset(qp);
196062306a36Sopenharmony_ci}
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_cistatic bool ntb_dma_filter_fn(struct dma_chan *chan, void *node)
196362306a36Sopenharmony_ci{
196462306a36Sopenharmony_ci	return dev_to_node(&chan->dev->device) == (int)(unsigned long)node;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci/**
196862306a36Sopenharmony_ci * ntb_transport_create_queue - Create a new NTB transport layer queue
196962306a36Sopenharmony_ci * @rx_handler: receive callback function
197062306a36Sopenharmony_ci * @tx_handler: transmit callback function
197162306a36Sopenharmony_ci * @event_handler: event callback function
197262306a36Sopenharmony_ci *
197362306a36Sopenharmony_ci * Create a new NTB transport layer queue and provide the queue with a callback
197462306a36Sopenharmony_ci * routine for both transmit and receive.  The receive callback routine will be
197562306a36Sopenharmony_ci * used to pass up data when the transport has received it on the queue.   The
197662306a36Sopenharmony_ci * transmit callback routine will be called when the transport has completed the
197762306a36Sopenharmony_ci * transmission of the data on the queue and the data is ready to be freed.
197862306a36Sopenharmony_ci *
197962306a36Sopenharmony_ci * RETURNS: pointer to newly created ntb_queue, NULL on error.
198062306a36Sopenharmony_ci */
198162306a36Sopenharmony_cistruct ntb_transport_qp *
198262306a36Sopenharmony_cintb_transport_create_queue(void *data, struct device *client_dev,
198362306a36Sopenharmony_ci			   const struct ntb_queue_handlers *handlers)
198462306a36Sopenharmony_ci{
198562306a36Sopenharmony_ci	struct ntb_dev *ndev;
198662306a36Sopenharmony_ci	struct pci_dev *pdev;
198762306a36Sopenharmony_ci	struct ntb_transport_ctx *nt;
198862306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
198962306a36Sopenharmony_ci	struct ntb_transport_qp *qp;
199062306a36Sopenharmony_ci	u64 qp_bit;
199162306a36Sopenharmony_ci	unsigned int free_queue;
199262306a36Sopenharmony_ci	dma_cap_mask_t dma_mask;
199362306a36Sopenharmony_ci	int node;
199462306a36Sopenharmony_ci	int i;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	ndev = dev_ntb(client_dev->parent);
199762306a36Sopenharmony_ci	pdev = ndev->pdev;
199862306a36Sopenharmony_ci	nt = ndev->ctx;
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	node = dev_to_node(&ndev->dev);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	free_queue = ffs(nt->qp_bitmap_free);
200362306a36Sopenharmony_ci	if (!free_queue)
200462306a36Sopenharmony_ci		goto err;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	/* decrement free_queue to make it zero based */
200762306a36Sopenharmony_ci	free_queue--;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	qp = &nt->qp_vec[free_queue];
201062306a36Sopenharmony_ci	qp_bit = BIT_ULL(qp->qp_num);
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	nt->qp_bitmap_free &= ~qp_bit;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	qp->cb_data = data;
201562306a36Sopenharmony_ci	qp->rx_handler = handlers->rx_handler;
201662306a36Sopenharmony_ci	qp->tx_handler = handlers->tx_handler;
201762306a36Sopenharmony_ci	qp->event_handler = handlers->event_handler;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	dma_cap_zero(dma_mask);
202062306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, dma_mask);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	if (use_dma) {
202362306a36Sopenharmony_ci		qp->tx_dma_chan =
202462306a36Sopenharmony_ci			dma_request_channel(dma_mask, ntb_dma_filter_fn,
202562306a36Sopenharmony_ci					    (void *)(unsigned long)node);
202662306a36Sopenharmony_ci		if (!qp->tx_dma_chan)
202762306a36Sopenharmony_ci			dev_info(&pdev->dev, "Unable to allocate TX DMA channel\n");
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci		qp->rx_dma_chan =
203062306a36Sopenharmony_ci			dma_request_channel(dma_mask, ntb_dma_filter_fn,
203162306a36Sopenharmony_ci					    (void *)(unsigned long)node);
203262306a36Sopenharmony_ci		if (!qp->rx_dma_chan)
203362306a36Sopenharmony_ci			dev_info(&pdev->dev, "Unable to allocate RX DMA channel\n");
203462306a36Sopenharmony_ci	} else {
203562306a36Sopenharmony_ci		qp->tx_dma_chan = NULL;
203662306a36Sopenharmony_ci		qp->rx_dma_chan = NULL;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	qp->tx_mw_dma_addr = 0;
204062306a36Sopenharmony_ci	if (qp->tx_dma_chan) {
204162306a36Sopenharmony_ci		qp->tx_mw_dma_addr =
204262306a36Sopenharmony_ci			dma_map_resource(qp->tx_dma_chan->device->dev,
204362306a36Sopenharmony_ci					 qp->tx_mw_phys, qp->tx_mw_size,
204462306a36Sopenharmony_ci					 DMA_FROM_DEVICE, 0);
204562306a36Sopenharmony_ci		if (dma_mapping_error(qp->tx_dma_chan->device->dev,
204662306a36Sopenharmony_ci				      qp->tx_mw_dma_addr)) {
204762306a36Sopenharmony_ci			qp->tx_mw_dma_addr = 0;
204862306a36Sopenharmony_ci			goto err1;
204962306a36Sopenharmony_ci		}
205062306a36Sopenharmony_ci	}
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Using %s memcpy for TX\n",
205362306a36Sopenharmony_ci		qp->tx_dma_chan ? "DMA" : "CPU");
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Using %s memcpy for RX\n",
205662306a36Sopenharmony_ci		qp->rx_dma_chan ? "DMA" : "CPU");
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
205962306a36Sopenharmony_ci		entry = kzalloc_node(sizeof(*entry), GFP_KERNEL, node);
206062306a36Sopenharmony_ci		if (!entry)
206162306a36Sopenharmony_ci			goto err1;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci		entry->qp = qp;
206462306a36Sopenharmony_ci		ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry,
206562306a36Sopenharmony_ci			     &qp->rx_free_q);
206662306a36Sopenharmony_ci	}
206762306a36Sopenharmony_ci	qp->rx_alloc_entry = NTB_QP_DEF_NUM_ENTRIES;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	for (i = 0; i < qp->tx_max_entry; i++) {
207062306a36Sopenharmony_ci		entry = kzalloc_node(sizeof(*entry), GFP_KERNEL, node);
207162306a36Sopenharmony_ci		if (!entry)
207262306a36Sopenharmony_ci			goto err2;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci		entry->qp = qp;
207562306a36Sopenharmony_ci		ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
207662306a36Sopenharmony_ci			     &qp->tx_free_q);
207762306a36Sopenharmony_ci	}
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	ntb_db_clear(qp->ndev, qp_bit);
208062306a36Sopenharmony_ci	ntb_db_clear_mask(qp->ndev, qp_bit);
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	dev_info(&pdev->dev, "NTB Transport QP %d created\n", qp->qp_num);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	return qp;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_cierr2:
208762306a36Sopenharmony_ci	while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
208862306a36Sopenharmony_ci		kfree(entry);
208962306a36Sopenharmony_cierr1:
209062306a36Sopenharmony_ci	qp->rx_alloc_entry = 0;
209162306a36Sopenharmony_ci	while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q)))
209262306a36Sopenharmony_ci		kfree(entry);
209362306a36Sopenharmony_ci	if (qp->tx_mw_dma_addr)
209462306a36Sopenharmony_ci		dma_unmap_resource(qp->tx_dma_chan->device->dev,
209562306a36Sopenharmony_ci				   qp->tx_mw_dma_addr, qp->tx_mw_size,
209662306a36Sopenharmony_ci				   DMA_FROM_DEVICE, 0);
209762306a36Sopenharmony_ci	if (qp->tx_dma_chan)
209862306a36Sopenharmony_ci		dma_release_channel(qp->tx_dma_chan);
209962306a36Sopenharmony_ci	if (qp->rx_dma_chan)
210062306a36Sopenharmony_ci		dma_release_channel(qp->rx_dma_chan);
210162306a36Sopenharmony_ci	nt->qp_bitmap_free |= qp_bit;
210262306a36Sopenharmony_cierr:
210362306a36Sopenharmony_ci	return NULL;
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_create_queue);
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci/**
210862306a36Sopenharmony_ci * ntb_transport_free_queue - Frees NTB transport queue
210962306a36Sopenharmony_ci * @qp: NTB queue to be freed
211062306a36Sopenharmony_ci *
211162306a36Sopenharmony_ci * Frees NTB transport queue
211262306a36Sopenharmony_ci */
211362306a36Sopenharmony_civoid ntb_transport_free_queue(struct ntb_transport_qp *qp)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	struct pci_dev *pdev;
211662306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
211762306a36Sopenharmony_ci	u64 qp_bit;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	if (!qp)
212062306a36Sopenharmony_ci		return;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	pdev = qp->ndev->pdev;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	qp->active = false;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	if (qp->tx_dma_chan) {
212762306a36Sopenharmony_ci		struct dma_chan *chan = qp->tx_dma_chan;
212862306a36Sopenharmony_ci		/* Putting the dma_chan to NULL will force any new traffic to be
212962306a36Sopenharmony_ci		 * processed by the CPU instead of the DAM engine
213062306a36Sopenharmony_ci		 */
213162306a36Sopenharmony_ci		qp->tx_dma_chan = NULL;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci		/* Try to be nice and wait for any queued DMA engine
213462306a36Sopenharmony_ci		 * transactions to process before smashing it with a rock
213562306a36Sopenharmony_ci		 */
213662306a36Sopenharmony_ci		dma_sync_wait(chan, qp->last_cookie);
213762306a36Sopenharmony_ci		dmaengine_terminate_all(chan);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci		dma_unmap_resource(chan->device->dev,
214062306a36Sopenharmony_ci				   qp->tx_mw_dma_addr, qp->tx_mw_size,
214162306a36Sopenharmony_ci				   DMA_FROM_DEVICE, 0);
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci		dma_release_channel(chan);
214462306a36Sopenharmony_ci	}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	if (qp->rx_dma_chan) {
214762306a36Sopenharmony_ci		struct dma_chan *chan = qp->rx_dma_chan;
214862306a36Sopenharmony_ci		/* Putting the dma_chan to NULL will force any new traffic to be
214962306a36Sopenharmony_ci		 * processed by the CPU instead of the DAM engine
215062306a36Sopenharmony_ci		 */
215162306a36Sopenharmony_ci		qp->rx_dma_chan = NULL;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci		/* Try to be nice and wait for any queued DMA engine
215462306a36Sopenharmony_ci		 * transactions to process before smashing it with a rock
215562306a36Sopenharmony_ci		 */
215662306a36Sopenharmony_ci		dma_sync_wait(chan, qp->last_cookie);
215762306a36Sopenharmony_ci		dmaengine_terminate_all(chan);
215862306a36Sopenharmony_ci		dma_release_channel(chan);
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	qp_bit = BIT_ULL(qp->qp_num);
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	ntb_db_set_mask(qp->ndev, qp_bit);
216462306a36Sopenharmony_ci	tasklet_kill(&qp->rxc_db_work);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	cancel_delayed_work_sync(&qp->link_work);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	qp->cb_data = NULL;
216962306a36Sopenharmony_ci	qp->rx_handler = NULL;
217062306a36Sopenharmony_ci	qp->tx_handler = NULL;
217162306a36Sopenharmony_ci	qp->event_handler = NULL;
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q)))
217462306a36Sopenharmony_ci		kfree(entry);
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_pend_q))) {
217762306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Freeing item from non-empty rx_pend_q\n");
217862306a36Sopenharmony_ci		kfree(entry);
217962306a36Sopenharmony_ci	}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_post_q))) {
218262306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Freeing item from non-empty rx_post_q\n");
218362306a36Sopenharmony_ci		kfree(entry);
218462306a36Sopenharmony_ci	}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q)))
218762306a36Sopenharmony_ci		kfree(entry);
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	qp->transport->qp_bitmap_free |= qp_bit;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	dev_info(&pdev->dev, "NTB Transport QP %d freed\n", qp->qp_num);
219262306a36Sopenharmony_ci}
219362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_free_queue);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci/**
219662306a36Sopenharmony_ci * ntb_transport_rx_remove - Dequeues enqueued rx packet
219762306a36Sopenharmony_ci * @qp: NTB queue to be freed
219862306a36Sopenharmony_ci * @len: pointer to variable to write enqueued buffers length
219962306a36Sopenharmony_ci *
220062306a36Sopenharmony_ci * Dequeues unused buffers from receive queue.  Should only be used during
220162306a36Sopenharmony_ci * shutdown of qp.
220262306a36Sopenharmony_ci *
220362306a36Sopenharmony_ci * RETURNS: NULL error value on error, or void* for success.
220462306a36Sopenharmony_ci */
220562306a36Sopenharmony_civoid *ntb_transport_rx_remove(struct ntb_transport_qp *qp, unsigned int *len)
220662306a36Sopenharmony_ci{
220762306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
220862306a36Sopenharmony_ci	void *buf;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	if (!qp || qp->client_ready)
221162306a36Sopenharmony_ci		return NULL;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_pend_q);
221462306a36Sopenharmony_ci	if (!entry)
221562306a36Sopenharmony_ci		return NULL;
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	buf = entry->cb_data;
221862306a36Sopenharmony_ci	*len = entry->len;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_free_q);
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	return buf;
222362306a36Sopenharmony_ci}
222462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_rx_remove);
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci/**
222762306a36Sopenharmony_ci * ntb_transport_rx_enqueue - Enqueue a new NTB queue entry
222862306a36Sopenharmony_ci * @qp: NTB transport layer queue the entry is to be enqueued on
222962306a36Sopenharmony_ci * @cb: per buffer pointer for callback function to use
223062306a36Sopenharmony_ci * @data: pointer to data buffer that incoming packets will be copied into
223162306a36Sopenharmony_ci * @len: length of the data buffer
223262306a36Sopenharmony_ci *
223362306a36Sopenharmony_ci * Enqueue a new receive buffer onto the transport queue into which a NTB
223462306a36Sopenharmony_ci * payload can be received into.
223562306a36Sopenharmony_ci *
223662306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
223762306a36Sopenharmony_ci */
223862306a36Sopenharmony_ciint ntb_transport_rx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,
223962306a36Sopenharmony_ci			     unsigned int len)
224062306a36Sopenharmony_ci{
224162306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	if (!qp)
224462306a36Sopenharmony_ci		return -EINVAL;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q);
224762306a36Sopenharmony_ci	if (!entry)
224862306a36Sopenharmony_ci		return -ENOMEM;
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	entry->cb_data = cb;
225162306a36Sopenharmony_ci	entry->buf = data;
225262306a36Sopenharmony_ci	entry->len = len;
225362306a36Sopenharmony_ci	entry->flags = 0;
225462306a36Sopenharmony_ci	entry->retries = 0;
225562306a36Sopenharmony_ci	entry->errors = 0;
225662306a36Sopenharmony_ci	entry->rx_index = 0;
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_pend_q);
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	if (qp->active)
226162306a36Sopenharmony_ci		tasklet_schedule(&qp->rxc_db_work);
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	return 0;
226462306a36Sopenharmony_ci}
226562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_rx_enqueue);
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci/**
226862306a36Sopenharmony_ci * ntb_transport_tx_enqueue - Enqueue a new NTB queue entry
226962306a36Sopenharmony_ci * @qp: NTB transport layer queue the entry is to be enqueued on
227062306a36Sopenharmony_ci * @cb: per buffer pointer for callback function to use
227162306a36Sopenharmony_ci * @data: pointer to data buffer that will be sent
227262306a36Sopenharmony_ci * @len: length of the data buffer
227362306a36Sopenharmony_ci *
227462306a36Sopenharmony_ci * Enqueue a new transmit buffer onto the transport queue from which a NTB
227562306a36Sopenharmony_ci * payload will be transmitted.  This assumes that a lock is being held to
227662306a36Sopenharmony_ci * serialize access to the qp.
227762306a36Sopenharmony_ci *
227862306a36Sopenharmony_ci * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
227962306a36Sopenharmony_ci */
228062306a36Sopenharmony_ciint ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,
228162306a36Sopenharmony_ci			     unsigned int len)
228262306a36Sopenharmony_ci{
228362306a36Sopenharmony_ci	struct ntb_queue_entry *entry;
228462306a36Sopenharmony_ci	int rc;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	if (!qp || !len)
228762306a36Sopenharmony_ci		return -EINVAL;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	/* If the qp link is down already, just ignore. */
229062306a36Sopenharmony_ci	if (!qp->link_is_up)
229162306a36Sopenharmony_ci		return 0;
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
229462306a36Sopenharmony_ci	if (!entry) {
229562306a36Sopenharmony_ci		qp->tx_err_no_buf++;
229662306a36Sopenharmony_ci		return -EBUSY;
229762306a36Sopenharmony_ci	}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	entry->cb_data = cb;
230062306a36Sopenharmony_ci	entry->buf = data;
230162306a36Sopenharmony_ci	entry->len = len;
230262306a36Sopenharmony_ci	entry->flags = 0;
230362306a36Sopenharmony_ci	entry->errors = 0;
230462306a36Sopenharmony_ci	entry->retries = 0;
230562306a36Sopenharmony_ci	entry->tx_index = 0;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	rc = ntb_process_tx(qp, entry);
230862306a36Sopenharmony_ci	if (rc)
230962306a36Sopenharmony_ci		ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
231062306a36Sopenharmony_ci			     &qp->tx_free_q);
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	return rc;
231362306a36Sopenharmony_ci}
231462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_tx_enqueue);
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci/**
231762306a36Sopenharmony_ci * ntb_transport_link_up - Notify NTB transport of client readiness to use queue
231862306a36Sopenharmony_ci * @qp: NTB transport layer queue to be enabled
231962306a36Sopenharmony_ci *
232062306a36Sopenharmony_ci * Notify NTB transport layer of client readiness to use queue
232162306a36Sopenharmony_ci */
232262306a36Sopenharmony_civoid ntb_transport_link_up(struct ntb_transport_qp *qp)
232362306a36Sopenharmony_ci{
232462306a36Sopenharmony_ci	if (!qp)
232562306a36Sopenharmony_ci		return;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	qp->client_ready = true;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	if (qp->transport->link_is_up)
233062306a36Sopenharmony_ci		schedule_delayed_work(&qp->link_work, 0);
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_link_up);
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci/**
233562306a36Sopenharmony_ci * ntb_transport_link_down - Notify NTB transport to no longer enqueue data
233662306a36Sopenharmony_ci * @qp: NTB transport layer queue to be disabled
233762306a36Sopenharmony_ci *
233862306a36Sopenharmony_ci * Notify NTB transport layer of client's desire to no longer receive data on
233962306a36Sopenharmony_ci * transport queue specified.  It is the client's responsibility to ensure all
234062306a36Sopenharmony_ci * entries on queue are purged or otherwise handled appropriately.
234162306a36Sopenharmony_ci */
234262306a36Sopenharmony_civoid ntb_transport_link_down(struct ntb_transport_qp *qp)
234362306a36Sopenharmony_ci{
234462306a36Sopenharmony_ci	int val;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	if (!qp)
234762306a36Sopenharmony_ci		return;
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	qp->client_ready = false;
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	val = ntb_spad_read(qp->ndev, QP_LINKS);
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	ntb_peer_spad_write(qp->ndev, PIDX, QP_LINKS, val & ~BIT(qp->qp_num));
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	if (qp->link_is_up)
235662306a36Sopenharmony_ci		ntb_send_link_down(qp);
235762306a36Sopenharmony_ci	else
235862306a36Sopenharmony_ci		cancel_delayed_work_sync(&qp->link_work);
235962306a36Sopenharmony_ci}
236062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_link_down);
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci/**
236362306a36Sopenharmony_ci * ntb_transport_link_query - Query transport link state
236462306a36Sopenharmony_ci * @qp: NTB transport layer queue to be queried
236562306a36Sopenharmony_ci *
236662306a36Sopenharmony_ci * Query connectivity to the remote system of the NTB transport queue
236762306a36Sopenharmony_ci *
236862306a36Sopenharmony_ci * RETURNS: true for link up or false for link down
236962306a36Sopenharmony_ci */
237062306a36Sopenharmony_cibool ntb_transport_link_query(struct ntb_transport_qp *qp)
237162306a36Sopenharmony_ci{
237262306a36Sopenharmony_ci	if (!qp)
237362306a36Sopenharmony_ci		return false;
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	return qp->link_is_up;
237662306a36Sopenharmony_ci}
237762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_link_query);
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci/**
238062306a36Sopenharmony_ci * ntb_transport_qp_num - Query the qp number
238162306a36Sopenharmony_ci * @qp: NTB transport layer queue to be queried
238262306a36Sopenharmony_ci *
238362306a36Sopenharmony_ci * Query qp number of the NTB transport queue
238462306a36Sopenharmony_ci *
238562306a36Sopenharmony_ci * RETURNS: a zero based number specifying the qp number
238662306a36Sopenharmony_ci */
238762306a36Sopenharmony_ciunsigned char ntb_transport_qp_num(struct ntb_transport_qp *qp)
238862306a36Sopenharmony_ci{
238962306a36Sopenharmony_ci	if (!qp)
239062306a36Sopenharmony_ci		return 0;
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	return qp->qp_num;
239362306a36Sopenharmony_ci}
239462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_qp_num);
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci/**
239762306a36Sopenharmony_ci * ntb_transport_max_size - Query the max payload size of a qp
239862306a36Sopenharmony_ci * @qp: NTB transport layer queue to be queried
239962306a36Sopenharmony_ci *
240062306a36Sopenharmony_ci * Query the maximum payload size permissible on the given qp
240162306a36Sopenharmony_ci *
240262306a36Sopenharmony_ci * RETURNS: the max payload size of a qp
240362306a36Sopenharmony_ci */
240462306a36Sopenharmony_ciunsigned int ntb_transport_max_size(struct ntb_transport_qp *qp)
240562306a36Sopenharmony_ci{
240662306a36Sopenharmony_ci	unsigned int max_size;
240762306a36Sopenharmony_ci	unsigned int copy_align;
240862306a36Sopenharmony_ci	struct dma_chan *rx_chan, *tx_chan;
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	if (!qp)
241162306a36Sopenharmony_ci		return 0;
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	rx_chan = qp->rx_dma_chan;
241462306a36Sopenharmony_ci	tx_chan = qp->tx_dma_chan;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	copy_align = max(rx_chan ? rx_chan->device->copy_align : 0,
241762306a36Sopenharmony_ci			 tx_chan ? tx_chan->device->copy_align : 0);
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	/* If DMA engine usage is possible, try to find the max size for that */
242062306a36Sopenharmony_ci	max_size = qp->tx_max_frame - sizeof(struct ntb_payload_header);
242162306a36Sopenharmony_ci	max_size = round_down(max_size, 1 << copy_align);
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	return max_size;
242462306a36Sopenharmony_ci}
242562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_max_size);
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ciunsigned int ntb_transport_tx_free_entry(struct ntb_transport_qp *qp)
242862306a36Sopenharmony_ci{
242962306a36Sopenharmony_ci	unsigned int head = qp->tx_index;
243062306a36Sopenharmony_ci	unsigned int tail = qp->remote_rx_info->entry;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	return tail >= head ? tail - head : qp->tx_max_entry + tail - head;
243362306a36Sopenharmony_ci}
243462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ntb_transport_tx_free_entry);
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_cistatic void ntb_transport_doorbell_callback(void *data, int vector)
243762306a36Sopenharmony_ci{
243862306a36Sopenharmony_ci	struct ntb_transport_ctx *nt = data;
243962306a36Sopenharmony_ci	struct ntb_transport_qp *qp;
244062306a36Sopenharmony_ci	u64 db_bits;
244162306a36Sopenharmony_ci	unsigned int qp_num;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	if (ntb_db_read(nt->ndev) & nt->msi_db_mask) {
244462306a36Sopenharmony_ci		ntb_transport_msi_peer_desc_changed(nt);
244562306a36Sopenharmony_ci		ntb_db_clear(nt->ndev, nt->msi_db_mask);
244662306a36Sopenharmony_ci	}
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	db_bits = (nt->qp_bitmap & ~nt->qp_bitmap_free &
244962306a36Sopenharmony_ci		   ntb_db_vector_mask(nt->ndev, vector));
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	while (db_bits) {
245262306a36Sopenharmony_ci		qp_num = __ffs(db_bits);
245362306a36Sopenharmony_ci		qp = &nt->qp_vec[qp_num];
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci		if (qp->active)
245662306a36Sopenharmony_ci			tasklet_schedule(&qp->rxc_db_work);
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci		db_bits &= ~BIT_ULL(qp_num);
245962306a36Sopenharmony_ci	}
246062306a36Sopenharmony_ci}
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_cistatic const struct ntb_ctx_ops ntb_transport_ops = {
246362306a36Sopenharmony_ci	.link_event = ntb_transport_event_callback,
246462306a36Sopenharmony_ci	.db_event = ntb_transport_doorbell_callback,
246562306a36Sopenharmony_ci};
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_cistatic struct ntb_client ntb_transport_client = {
246862306a36Sopenharmony_ci	.ops = {
246962306a36Sopenharmony_ci		.probe = ntb_transport_probe,
247062306a36Sopenharmony_ci		.remove = ntb_transport_free,
247162306a36Sopenharmony_ci	},
247262306a36Sopenharmony_ci};
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_cistatic int __init ntb_transport_init(void)
247562306a36Sopenharmony_ci{
247662306a36Sopenharmony_ci	int rc;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	pr_info("%s, version %s\n", NTB_TRANSPORT_DESC, NTB_TRANSPORT_VER);
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	if (debugfs_initialized())
248162306a36Sopenharmony_ci		nt_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	rc = bus_register(&ntb_transport_bus);
248462306a36Sopenharmony_ci	if (rc)
248562306a36Sopenharmony_ci		goto err_bus;
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	rc = ntb_register_client(&ntb_transport_client);
248862306a36Sopenharmony_ci	if (rc)
248962306a36Sopenharmony_ci		goto err_client;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	return 0;
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_cierr_client:
249462306a36Sopenharmony_ci	bus_unregister(&ntb_transport_bus);
249562306a36Sopenharmony_cierr_bus:
249662306a36Sopenharmony_ci	debugfs_remove_recursive(nt_debugfs_dir);
249762306a36Sopenharmony_ci	return rc;
249862306a36Sopenharmony_ci}
249962306a36Sopenharmony_cimodule_init(ntb_transport_init);
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_cistatic void __exit ntb_transport_exit(void)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	ntb_unregister_client(&ntb_transport_client);
250462306a36Sopenharmony_ci	bus_unregister(&ntb_transport_bus);
250562306a36Sopenharmony_ci	debugfs_remove_recursive(nt_debugfs_dir);
250662306a36Sopenharmony_ci}
250762306a36Sopenharmony_cimodule_exit(ntb_transport_exit);
2508