162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Endpoint Function Driver to implement Non-Transparent Bridge functionality
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020 Texas Instruments
662306a36Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci * The PCI NTB function driver configures the SoC with multiple PCIe Endpoint
1162306a36Sopenharmony_ci * (EP) controller instances (see diagram below) in such a way that
1262306a36Sopenharmony_ci * transactions from one EP controller are routed to the other EP controller.
1362306a36Sopenharmony_ci * Once PCI NTB function driver configures the SoC with multiple EP instances,
1462306a36Sopenharmony_ci * HOST1 and HOST2 can communicate with each other using SoC as a bridge.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *    +-------------+                                   +-------------+
1762306a36Sopenharmony_ci *    |             |                                   |             |
1862306a36Sopenharmony_ci *    |    HOST1    |                                   |    HOST2    |
1962306a36Sopenharmony_ci *    |             |                                   |             |
2062306a36Sopenharmony_ci *    +------^------+                                   +------^------+
2162306a36Sopenharmony_ci *           |                                                 |
2262306a36Sopenharmony_ci *           |                                                 |
2362306a36Sopenharmony_ci * +---------|-------------------------------------------------|---------+
2462306a36Sopenharmony_ci * |  +------v------+                                   +------v------+  |
2562306a36Sopenharmony_ci * |  |             |                                   |             |  |
2662306a36Sopenharmony_ci * |  |     EP      |                                   |     EP      |  |
2762306a36Sopenharmony_ci * |  | CONTROLLER1 |                                   | CONTROLLER2 |  |
2862306a36Sopenharmony_ci * |  |             <----------------------------------->             |  |
2962306a36Sopenharmony_ci * |  |             |                                   |             |  |
3062306a36Sopenharmony_ci * |  |             |                                   |             |  |
3162306a36Sopenharmony_ci * |  |             |  SoC With Multiple EP Instances   |             |  |
3262306a36Sopenharmony_ci * |  |             |  (Configured using NTB Function)  |             |  |
3362306a36Sopenharmony_ci * |  +-------------+                                   +-------------+  |
3462306a36Sopenharmony_ci * +---------------------------------------------------------------------+
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <linux/delay.h>
3862306a36Sopenharmony_ci#include <linux/io.h>
3962306a36Sopenharmony_ci#include <linux/module.h>
4062306a36Sopenharmony_ci#include <linux/slab.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include <linux/pci-epc.h>
4362306a36Sopenharmony_ci#include <linux/pci-epf.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct workqueue_struct *kpcintb_workqueue;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define COMMAND_CONFIGURE_DOORBELL	1
4862306a36Sopenharmony_ci#define COMMAND_TEARDOWN_DOORBELL	2
4962306a36Sopenharmony_ci#define COMMAND_CONFIGURE_MW		3
5062306a36Sopenharmony_ci#define COMMAND_TEARDOWN_MW		4
5162306a36Sopenharmony_ci#define COMMAND_LINK_UP			5
5262306a36Sopenharmony_ci#define COMMAND_LINK_DOWN		6
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define COMMAND_STATUS_OK		1
5562306a36Sopenharmony_ci#define COMMAND_STATUS_ERROR		2
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define LINK_STATUS_UP			BIT(0)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define SPAD_COUNT			64
6062306a36Sopenharmony_ci#define DB_COUNT			4
6162306a36Sopenharmony_ci#define NTB_MW_OFFSET			2
6262306a36Sopenharmony_ci#define DB_COUNT_MASK			GENMASK(15, 0)
6362306a36Sopenharmony_ci#define MSIX_ENABLE			BIT(16)
6462306a36Sopenharmony_ci#define MAX_DB_COUNT			32
6562306a36Sopenharmony_ci#define MAX_MW				4
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cienum epf_ntb_bar {
6862306a36Sopenharmony_ci	BAR_CONFIG,
6962306a36Sopenharmony_ci	BAR_PEER_SPAD,
7062306a36Sopenharmony_ci	BAR_DB_MW1,
7162306a36Sopenharmony_ci	BAR_MW2,
7262306a36Sopenharmony_ci	BAR_MW3,
7362306a36Sopenharmony_ci	BAR_MW4,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct epf_ntb {
7762306a36Sopenharmony_ci	u32 num_mws;
7862306a36Sopenharmony_ci	u32 db_count;
7962306a36Sopenharmony_ci	u32 spad_count;
8062306a36Sopenharmony_ci	struct pci_epf *epf;
8162306a36Sopenharmony_ci	u64 mws_size[MAX_MW];
8262306a36Sopenharmony_ci	struct config_group group;
8362306a36Sopenharmony_ci	struct epf_ntb_epc *epc[2];
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group)
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistruct epf_ntb_epc {
8962306a36Sopenharmony_ci	u8 func_no;
9062306a36Sopenharmony_ci	u8 vfunc_no;
9162306a36Sopenharmony_ci	bool linkup;
9262306a36Sopenharmony_ci	bool is_msix;
9362306a36Sopenharmony_ci	int msix_bar;
9462306a36Sopenharmony_ci	u32 spad_size;
9562306a36Sopenharmony_ci	struct pci_epc *epc;
9662306a36Sopenharmony_ci	struct epf_ntb *epf_ntb;
9762306a36Sopenharmony_ci	void __iomem *mw_addr[6];
9862306a36Sopenharmony_ci	size_t msix_table_offset;
9962306a36Sopenharmony_ci	struct epf_ntb_ctrl *reg;
10062306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
10162306a36Sopenharmony_ci	enum pci_barno epf_ntb_bar[6];
10262306a36Sopenharmony_ci	struct delayed_work cmd_handler;
10362306a36Sopenharmony_ci	enum pci_epc_interface_type type;
10462306a36Sopenharmony_ci	const struct pci_epc_features *epc_features;
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistruct epf_ntb_ctrl {
10862306a36Sopenharmony_ci	u32	command;
10962306a36Sopenharmony_ci	u32	argument;
11062306a36Sopenharmony_ci	u16	command_status;
11162306a36Sopenharmony_ci	u16	link_status;
11262306a36Sopenharmony_ci	u32	topology;
11362306a36Sopenharmony_ci	u64	addr;
11462306a36Sopenharmony_ci	u64	size;
11562306a36Sopenharmony_ci	u32	num_mws;
11662306a36Sopenharmony_ci	u32	mw1_offset;
11762306a36Sopenharmony_ci	u32	spad_offset;
11862306a36Sopenharmony_ci	u32	spad_count;
11962306a36Sopenharmony_ci	u32	db_entry_size;
12062306a36Sopenharmony_ci	u32	db_data[MAX_DB_COUNT];
12162306a36Sopenharmony_ci	u32	db_offset[MAX_DB_COUNT];
12262306a36Sopenharmony_ci} __packed;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct pci_epf_header epf_ntb_header = {
12562306a36Sopenharmony_ci	.vendorid	= PCI_ANY_ID,
12662306a36Sopenharmony_ci	.deviceid	= PCI_ANY_ID,
12762306a36Sopenharmony_ci	.baseclass_code	= PCI_BASE_CLASS_MEMORY,
12862306a36Sopenharmony_ci	.interrupt_pin	= PCI_INTERRUPT_INTA,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/**
13262306a36Sopenharmony_ci * epf_ntb_link_up() - Raise link_up interrupt to both the hosts
13362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
13462306a36Sopenharmony_ci * @link_up: true or false indicating Link is UP or Down
13562306a36Sopenharmony_ci *
13662306a36Sopenharmony_ci * Once NTB function in HOST1 and the NTB function in HOST2 invoke
13762306a36Sopenharmony_ci * ntb_link_enable(), this NTB function driver will trigger a link event to
13862306a36Sopenharmony_ci * the NTB client in both the hosts.
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistatic int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	enum pci_epc_interface_type type;
14362306a36Sopenharmony_ci	enum pci_epc_irq_type irq_type;
14462306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
14562306a36Sopenharmony_ci	struct epf_ntb_ctrl *ctrl;
14662306a36Sopenharmony_ci	struct pci_epc *epc;
14762306a36Sopenharmony_ci	u8 func_no, vfunc_no;
14862306a36Sopenharmony_ci	bool is_msix;
14962306a36Sopenharmony_ci	int ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
15262306a36Sopenharmony_ci		ntb_epc = ntb->epc[type];
15362306a36Sopenharmony_ci		epc = ntb_epc->epc;
15462306a36Sopenharmony_ci		func_no = ntb_epc->func_no;
15562306a36Sopenharmony_ci		vfunc_no = ntb_epc->vfunc_no;
15662306a36Sopenharmony_ci		is_msix = ntb_epc->is_msix;
15762306a36Sopenharmony_ci		ctrl = ntb_epc->reg;
15862306a36Sopenharmony_ci		if (link_up)
15962306a36Sopenharmony_ci			ctrl->link_status |= LINK_STATUS_UP;
16062306a36Sopenharmony_ci		else
16162306a36Sopenharmony_ci			ctrl->link_status &= ~LINK_STATUS_UP;
16262306a36Sopenharmony_ci		irq_type = is_msix ? PCI_EPC_IRQ_MSIX : PCI_EPC_IRQ_MSI;
16362306a36Sopenharmony_ci		ret = pci_epc_raise_irq(epc, func_no, vfunc_no, irq_type, 1);
16462306a36Sopenharmony_ci		if (ret) {
16562306a36Sopenharmony_ci			dev_err(&epc->dev,
16662306a36Sopenharmony_ci				"%s intf: Failed to raise Link Up IRQ\n",
16762306a36Sopenharmony_ci				pci_epc_interface_string(type));
16862306a36Sopenharmony_ci			return ret;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * epf_ntb_configure_mw() - Configure the Outbound Address Space for one host
17762306a36Sopenharmony_ci *   to access the memory window of other host
17862306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
17962306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
18062306a36Sopenharmony_ci * @mw: Index of the memory window (either 0, 1, 2 or 3)
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * +-----------------+    +---->+----------------+-----------+-----------------+
18362306a36Sopenharmony_ci * |       BAR0      |    |     |   Doorbell 1   +-----------> MSI|X ADDRESS 1 |
18462306a36Sopenharmony_ci * +-----------------+    |     +----------------+           +-----------------+
18562306a36Sopenharmony_ci * |       BAR1      |    |     |   Doorbell 2   +---------+ |                 |
18662306a36Sopenharmony_ci * +-----------------+----+     +----------------+         | |                 |
18762306a36Sopenharmony_ci * |       BAR2      |          |   Doorbell 3   +-------+ | +-----------------+
18862306a36Sopenharmony_ci * +-----------------+----+     +----------------+       | +-> MSI|X ADDRESS 2 |
18962306a36Sopenharmony_ci * |       BAR3      |    |     |   Doorbell 4   +-----+ |   +-----------------+
19062306a36Sopenharmony_ci * +-----------------+    |     |----------------+     | |   |                 |
19162306a36Sopenharmony_ci * |       BAR4      |    |     |                |     | |   +-----------------+
19262306a36Sopenharmony_ci * +-----------------+    |     |      MW1       +---+ | +-->+ MSI|X ADDRESS 3||
19362306a36Sopenharmony_ci * |       BAR5      |    |     |                |   | |     +-----------------+
19462306a36Sopenharmony_ci * +-----------------+    +---->-----------------+   | |     |                 |
19562306a36Sopenharmony_ci *   EP CONTROLLER 1            |                |   | |     +-----------------+
19662306a36Sopenharmony_ci *                              |                |   | +---->+ MSI|X ADDRESS 4 |
19762306a36Sopenharmony_ci *                              +----------------+   |       +-----------------+
19862306a36Sopenharmony_ci *                      (A)      EP CONTROLLER 2     |       |                 |
19962306a36Sopenharmony_ci *                                 (OB SPACE)        |       |                 |
20062306a36Sopenharmony_ci *                                                   +------->      MW1        |
20162306a36Sopenharmony_ci *                                                           |                 |
20262306a36Sopenharmony_ci *                                                           |                 |
20362306a36Sopenharmony_ci *                                                   (B)     +-----------------+
20462306a36Sopenharmony_ci *                                                           |                 |
20562306a36Sopenharmony_ci *                                                           |                 |
20662306a36Sopenharmony_ci *                                                           |                 |
20762306a36Sopenharmony_ci *                                                           |                 |
20862306a36Sopenharmony_ci *                                                           |                 |
20962306a36Sopenharmony_ci *                                                           +-----------------+
21062306a36Sopenharmony_ci *                                                           PCI Address Space
21162306a36Sopenharmony_ci *                                                           (Managed by HOST2)
21262306a36Sopenharmony_ci *
21362306a36Sopenharmony_ci * This function performs stage (B) in the above diagram (see MW1) i.e., map OB
21462306a36Sopenharmony_ci * address space of memory window to PCI address space.
21562306a36Sopenharmony_ci *
21662306a36Sopenharmony_ci * This operation requires 3 parameters
21762306a36Sopenharmony_ci *  1) Address in the outbound address space
21862306a36Sopenharmony_ci *  2) Address in the PCI Address space
21962306a36Sopenharmony_ci *  3) Size of the address region to be mapped
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * The address in the outbound address space (for MW1, MW2, MW3 and MW4) is
22262306a36Sopenharmony_ci * stored in epf_bar corresponding to BAR_DB_MW1 for MW1 and BAR_MW2, BAR_MW3
22362306a36Sopenharmony_ci * BAR_MW4 for rest of the BARs of epf_ntb_epc that is connected to HOST1. This
22462306a36Sopenharmony_ci * is populated in epf_ntb_alloc_peer_mem() in this driver.
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * The address and size of the PCI address region that has to be mapped would
22762306a36Sopenharmony_ci * be provided by HOST2 in ctrl->addr and ctrl->size of epf_ntb_epc that is
22862306a36Sopenharmony_ci * connected to HOST2.
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Please note Memory window1 (MW1) and Doorbell registers together will be
23162306a36Sopenharmony_ci * mapped to a single BAR (BAR2) above for 32-bit BARs. The exact BAR that's
23262306a36Sopenharmony_ci * used for Memory window (MW) can be obtained from epf_ntb_bar[BAR_DB_MW1],
23362306a36Sopenharmony_ci * epf_ntb_bar[BAR_MW2], epf_ntb_bar[BAR_MW2], epf_ntb_bar[BAR_MW2].
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic int epf_ntb_configure_mw(struct epf_ntb *ntb,
23662306a36Sopenharmony_ci				enum pci_epc_interface_type type, u32 mw)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
23962306a36Sopenharmony_ci	struct pci_epf_bar *peer_epf_bar;
24062306a36Sopenharmony_ci	enum pci_barno peer_barno;
24162306a36Sopenharmony_ci	struct epf_ntb_ctrl *ctrl;
24262306a36Sopenharmony_ci	phys_addr_t phys_addr;
24362306a36Sopenharmony_ci	u8 func_no, vfunc_no;
24462306a36Sopenharmony_ci	struct pci_epc *epc;
24562306a36Sopenharmony_ci	u64 addr, size;
24662306a36Sopenharmony_ci	int ret = 0;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
24962306a36Sopenharmony_ci	epc = ntb_epc->epc;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
25262306a36Sopenharmony_ci	peer_barno = peer_ntb_epc->epf_ntb_bar[mw + NTB_MW_OFFSET];
25362306a36Sopenharmony_ci	peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno];
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	phys_addr = peer_epf_bar->phys_addr;
25662306a36Sopenharmony_ci	ctrl = ntb_epc->reg;
25762306a36Sopenharmony_ci	addr = ctrl->addr;
25862306a36Sopenharmony_ci	size = ctrl->size;
25962306a36Sopenharmony_ci	if (mw + NTB_MW_OFFSET == BAR_DB_MW1)
26062306a36Sopenharmony_ci		phys_addr += ctrl->mw1_offset;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (size > ntb->mws_size[mw]) {
26362306a36Sopenharmony_ci		dev_err(&epc->dev,
26462306a36Sopenharmony_ci			"%s intf: MW: %d Req Sz:%llxx > Supported Sz:%llx\n",
26562306a36Sopenharmony_ci			pci_epc_interface_string(type), mw, size,
26662306a36Sopenharmony_ci			ntb->mws_size[mw]);
26762306a36Sopenharmony_ci		ret = -EINVAL;
26862306a36Sopenharmony_ci		goto err_invalid_size;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
27262306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ret = pci_epc_map_addr(epc, func_no, vfunc_no, phys_addr, addr, size);
27562306a36Sopenharmony_ci	if (ret)
27662306a36Sopenharmony_ci		dev_err(&epc->dev,
27762306a36Sopenharmony_ci			"%s intf: Failed to map memory window %d address\n",
27862306a36Sopenharmony_ci			pci_epc_interface_string(type), mw);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cierr_invalid_size:
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return ret;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/**
28662306a36Sopenharmony_ci * epf_ntb_teardown_mw() - Teardown the configured OB ATU
28762306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
28862306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
28962306a36Sopenharmony_ci * @mw: Index of the memory window (either 0, 1, 2 or 3)
29062306a36Sopenharmony_ci *
29162306a36Sopenharmony_ci * Teardown the configured OB ATU configured in epf_ntb_configure_mw() using
29262306a36Sopenharmony_ci * pci_epc_unmap_addr()
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic void epf_ntb_teardown_mw(struct epf_ntb *ntb,
29562306a36Sopenharmony_ci				enum pci_epc_interface_type type, u32 mw)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
29862306a36Sopenharmony_ci	struct pci_epf_bar *peer_epf_bar;
29962306a36Sopenharmony_ci	enum pci_barno peer_barno;
30062306a36Sopenharmony_ci	struct epf_ntb_ctrl *ctrl;
30162306a36Sopenharmony_ci	phys_addr_t phys_addr;
30262306a36Sopenharmony_ci	u8 func_no, vfunc_no;
30362306a36Sopenharmony_ci	struct pci_epc *epc;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
30662306a36Sopenharmony_ci	epc = ntb_epc->epc;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
30962306a36Sopenharmony_ci	peer_barno = peer_ntb_epc->epf_ntb_bar[mw + NTB_MW_OFFSET];
31062306a36Sopenharmony_ci	peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno];
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	phys_addr = peer_epf_bar->phys_addr;
31362306a36Sopenharmony_ci	ctrl = ntb_epc->reg;
31462306a36Sopenharmony_ci	if (mw + NTB_MW_OFFSET == BAR_DB_MW1)
31562306a36Sopenharmony_ci		phys_addr += ctrl->mw1_offset;
31662306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
31762306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	pci_epc_unmap_addr(epc, func_no, vfunc_no, phys_addr);
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci/**
32362306a36Sopenharmony_ci * epf_ntb_configure_msi() - Map OB address space to MSI address
32462306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
32562306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
32662306a36Sopenharmony_ci * @db_count: Number of doorbell interrupts to map
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci *+-----------------+    +----->+----------------+-----------+-----------------+
32962306a36Sopenharmony_ci *|       BAR0      |    |      |   Doorbell 1   +---+------->   MSI ADDRESS   |
33062306a36Sopenharmony_ci *+-----------------+    |      +----------------+   |       +-----------------+
33162306a36Sopenharmony_ci *|       BAR1      |    |      |   Doorbell 2   +---+       |                 |
33262306a36Sopenharmony_ci *+-----------------+----+      +----------------+   |       |                 |
33362306a36Sopenharmony_ci *|       BAR2      |           |   Doorbell 3   +---+       |                 |
33462306a36Sopenharmony_ci *+-----------------+----+      +----------------+   |       |                 |
33562306a36Sopenharmony_ci *|       BAR3      |    |      |   Doorbell 4   +---+       |                 |
33662306a36Sopenharmony_ci *+-----------------+    |      |----------------+           |                 |
33762306a36Sopenharmony_ci *|       BAR4      |    |      |                |           |                 |
33862306a36Sopenharmony_ci *+-----------------+    |      |      MW1       |           |                 |
33962306a36Sopenharmony_ci *|       BAR5      |    |      |                |           |                 |
34062306a36Sopenharmony_ci *+-----------------+    +----->-----------------+           |                 |
34162306a36Sopenharmony_ci *  EP CONTROLLER 1             |                |           |                 |
34262306a36Sopenharmony_ci *                              |                |           |                 |
34362306a36Sopenharmony_ci *                              +----------------+           +-----------------+
34462306a36Sopenharmony_ci *                     (A)       EP CONTROLLER 2             |                 |
34562306a36Sopenharmony_ci *                                 (OB SPACE)                |                 |
34662306a36Sopenharmony_ci *                                                           |      MW1        |
34762306a36Sopenharmony_ci *                                                           |                 |
34862306a36Sopenharmony_ci *                                                           |                 |
34962306a36Sopenharmony_ci *                                                   (B)     +-----------------+
35062306a36Sopenharmony_ci *                                                           |                 |
35162306a36Sopenharmony_ci *                                                           |                 |
35262306a36Sopenharmony_ci *                                                           |                 |
35362306a36Sopenharmony_ci *                                                           |                 |
35462306a36Sopenharmony_ci *                                                           |                 |
35562306a36Sopenharmony_ci *                                                           +-----------------+
35662306a36Sopenharmony_ci *                                                           PCI Address Space
35762306a36Sopenharmony_ci *                                                           (Managed by HOST2)
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci *
36062306a36Sopenharmony_ci * This function performs stage (B) in the above diagram (see Doorbell 1,
36162306a36Sopenharmony_ci * Doorbell 2, Doorbell 3, Doorbell 4) i.e map OB address space corresponding to
36262306a36Sopenharmony_ci * doorbell to MSI address in PCI address space.
36362306a36Sopenharmony_ci *
36462306a36Sopenharmony_ci * This operation requires 3 parameters
36562306a36Sopenharmony_ci *  1) Address reserved for doorbell in the outbound address space
36662306a36Sopenharmony_ci *  2) MSI-X address in the PCIe Address space
36762306a36Sopenharmony_ci *  3) Number of MSI-X interrupts that has to be configured
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci * The address in the outbound address space (for the Doorbell) is stored in
37062306a36Sopenharmony_ci * epf_bar corresponding to BAR_DB_MW1 of epf_ntb_epc that is connected to
37162306a36Sopenharmony_ci * HOST1. This is populated in epf_ntb_alloc_peer_mem() in this driver along
37262306a36Sopenharmony_ci * with address for MW1.
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci * pci_epc_map_msi_irq() takes the MSI address from MSI capability register
37562306a36Sopenharmony_ci * and maps the OB address (obtained in epf_ntb_alloc_peer_mem()) to the MSI
37662306a36Sopenharmony_ci * address.
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci * epf_ntb_configure_msi() also stores the MSI data to raise each interrupt
37962306a36Sopenharmony_ci * in db_data of the peer's control region. This helps the peer to raise
38062306a36Sopenharmony_ci * doorbell of the other host by writing db_data to the BAR corresponding to
38162306a36Sopenharmony_ci * BAR_DB_MW1.
38262306a36Sopenharmony_ci */
38362306a36Sopenharmony_cistatic int epf_ntb_configure_msi(struct epf_ntb *ntb,
38462306a36Sopenharmony_ci				 enum pci_epc_interface_type type, u16 db_count)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
38762306a36Sopenharmony_ci	u32 db_entry_size, db_data, db_offset;
38862306a36Sopenharmony_ci	struct pci_epf_bar *peer_epf_bar;
38962306a36Sopenharmony_ci	struct epf_ntb_ctrl *peer_ctrl;
39062306a36Sopenharmony_ci	enum pci_barno peer_barno;
39162306a36Sopenharmony_ci	phys_addr_t phys_addr;
39262306a36Sopenharmony_ci	u8 func_no, vfunc_no;
39362306a36Sopenharmony_ci	struct pci_epc *epc;
39462306a36Sopenharmony_ci	int ret, i;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
39762306a36Sopenharmony_ci	epc = ntb_epc->epc;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
40062306a36Sopenharmony_ci	peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_DB_MW1];
40162306a36Sopenharmony_ci	peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno];
40262306a36Sopenharmony_ci	peer_ctrl = peer_ntb_epc->reg;
40362306a36Sopenharmony_ci	db_entry_size = peer_ctrl->db_entry_size;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	phys_addr = peer_epf_bar->phys_addr;
40662306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
40762306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	ret = pci_epc_map_msi_irq(epc, func_no, vfunc_no, phys_addr, db_count,
41062306a36Sopenharmony_ci				  db_entry_size, &db_data, &db_offset);
41162306a36Sopenharmony_ci	if (ret) {
41262306a36Sopenharmony_ci		dev_err(&epc->dev, "%s intf: Failed to map MSI IRQ\n",
41362306a36Sopenharmony_ci			pci_epc_interface_string(type));
41462306a36Sopenharmony_ci		return ret;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	for (i = 0; i < db_count; i++) {
41862306a36Sopenharmony_ci		peer_ctrl->db_data[i] = db_data | i;
41962306a36Sopenharmony_ci		peer_ctrl->db_offset[i] = db_offset;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	return 0;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci/**
42662306a36Sopenharmony_ci * epf_ntb_configure_msix() - Map OB address space to MSI-X address
42762306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
42862306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
42962306a36Sopenharmony_ci * @db_count: Number of doorbell interrupts to map
43062306a36Sopenharmony_ci *
43162306a36Sopenharmony_ci *+-----------------+    +----->+----------------+-----------+-----------------+
43262306a36Sopenharmony_ci *|       BAR0      |    |      |   Doorbell 1   +-----------> MSI-X ADDRESS 1 |
43362306a36Sopenharmony_ci *+-----------------+    |      +----------------+           +-----------------+
43462306a36Sopenharmony_ci *|       BAR1      |    |      |   Doorbell 2   +---------+ |                 |
43562306a36Sopenharmony_ci *+-----------------+----+      +----------------+         | |                 |
43662306a36Sopenharmony_ci *|       BAR2      |           |   Doorbell 3   +-------+ | +-----------------+
43762306a36Sopenharmony_ci *+-----------------+----+      +----------------+       | +-> MSI-X ADDRESS 2 |
43862306a36Sopenharmony_ci *|       BAR3      |    |      |   Doorbell 4   +-----+ |   +-----------------+
43962306a36Sopenharmony_ci *+-----------------+    |      |----------------+     | |   |                 |
44062306a36Sopenharmony_ci *|       BAR4      |    |      |                |     | |   +-----------------+
44162306a36Sopenharmony_ci *+-----------------+    |      |      MW1       +     | +-->+ MSI-X ADDRESS 3||
44262306a36Sopenharmony_ci *|       BAR5      |    |      |                |     |     +-----------------+
44362306a36Sopenharmony_ci *+-----------------+    +----->-----------------+     |     |                 |
44462306a36Sopenharmony_ci *  EP CONTROLLER 1             |                |     |     +-----------------+
44562306a36Sopenharmony_ci *                              |                |     +---->+ MSI-X ADDRESS 4 |
44662306a36Sopenharmony_ci *                              +----------------+           +-----------------+
44762306a36Sopenharmony_ci *                     (A)       EP CONTROLLER 2             |                 |
44862306a36Sopenharmony_ci *                                 (OB SPACE)                |                 |
44962306a36Sopenharmony_ci *                                                           |      MW1        |
45062306a36Sopenharmony_ci *                                                           |                 |
45162306a36Sopenharmony_ci *                                                           |                 |
45262306a36Sopenharmony_ci *                                                   (B)     +-----------------+
45362306a36Sopenharmony_ci *                                                           |                 |
45462306a36Sopenharmony_ci *                                                           |                 |
45562306a36Sopenharmony_ci *                                                           |                 |
45662306a36Sopenharmony_ci *                                                           |                 |
45762306a36Sopenharmony_ci *                                                           |                 |
45862306a36Sopenharmony_ci *                                                           +-----------------+
45962306a36Sopenharmony_ci *                                                           PCI Address Space
46062306a36Sopenharmony_ci *                                                           (Managed by HOST2)
46162306a36Sopenharmony_ci *
46262306a36Sopenharmony_ci * This function performs stage (B) in the above diagram (see Doorbell 1,
46362306a36Sopenharmony_ci * Doorbell 2, Doorbell 3, Doorbell 4) i.e map OB address space corresponding to
46462306a36Sopenharmony_ci * doorbell to MSI-X address in PCI address space.
46562306a36Sopenharmony_ci *
46662306a36Sopenharmony_ci * This operation requires 3 parameters
46762306a36Sopenharmony_ci *  1) Address reserved for doorbell in the outbound address space
46862306a36Sopenharmony_ci *  2) MSI-X address in the PCIe Address space
46962306a36Sopenharmony_ci *  3) Number of MSI-X interrupts that has to be configured
47062306a36Sopenharmony_ci *
47162306a36Sopenharmony_ci * The address in the outbound address space (for the Doorbell) is stored in
47262306a36Sopenharmony_ci * epf_bar corresponding to BAR_DB_MW1 of epf_ntb_epc that is connected to
47362306a36Sopenharmony_ci * HOST1. This is populated in epf_ntb_alloc_peer_mem() in this driver along
47462306a36Sopenharmony_ci * with address for MW1.
47562306a36Sopenharmony_ci *
47662306a36Sopenharmony_ci * The MSI-X address is in the MSI-X table of EP CONTROLLER 2 and
47762306a36Sopenharmony_ci * the count of doorbell is in ctrl->argument of epf_ntb_epc that is connected
47862306a36Sopenharmony_ci * to HOST2. MSI-X table is stored memory mapped to ntb_epc->msix_bar and the
47962306a36Sopenharmony_ci * offset is in ntb_epc->msix_table_offset. From this epf_ntb_configure_msix()
48062306a36Sopenharmony_ci * gets the MSI-X address and data.
48162306a36Sopenharmony_ci *
48262306a36Sopenharmony_ci * epf_ntb_configure_msix() also stores the MSI-X data to raise each interrupt
48362306a36Sopenharmony_ci * in db_data of the peer's control region. This helps the peer to raise
48462306a36Sopenharmony_ci * doorbell of the other host by writing db_data to the BAR corresponding to
48562306a36Sopenharmony_ci * BAR_DB_MW1.
48662306a36Sopenharmony_ci */
48762306a36Sopenharmony_cistatic int epf_ntb_configure_msix(struct epf_ntb *ntb,
48862306a36Sopenharmony_ci				  enum pci_epc_interface_type type,
48962306a36Sopenharmony_ci				  u16 db_count)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	const struct pci_epc_features *epc_features;
49262306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
49362306a36Sopenharmony_ci	struct pci_epf_bar *peer_epf_bar, *epf_bar;
49462306a36Sopenharmony_ci	struct pci_epf_msix_tbl *msix_tbl;
49562306a36Sopenharmony_ci	struct epf_ntb_ctrl *peer_ctrl;
49662306a36Sopenharmony_ci	u32 db_entry_size, msg_data;
49762306a36Sopenharmony_ci	enum pci_barno peer_barno;
49862306a36Sopenharmony_ci	phys_addr_t phys_addr;
49962306a36Sopenharmony_ci	u8 func_no, vfunc_no;
50062306a36Sopenharmony_ci	struct pci_epc *epc;
50162306a36Sopenharmony_ci	size_t align;
50262306a36Sopenharmony_ci	u64 msg_addr;
50362306a36Sopenharmony_ci	int ret, i;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
50662306a36Sopenharmony_ci	epc = ntb_epc->epc;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	epf_bar = &ntb_epc->epf_bar[ntb_epc->msix_bar];
50962306a36Sopenharmony_ci	msix_tbl = epf_bar->addr + ntb_epc->msix_table_offset;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
51262306a36Sopenharmony_ci	peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_DB_MW1];
51362306a36Sopenharmony_ci	peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno];
51462306a36Sopenharmony_ci	phys_addr = peer_epf_bar->phys_addr;
51562306a36Sopenharmony_ci	peer_ctrl = peer_ntb_epc->reg;
51662306a36Sopenharmony_ci	epc_features = ntb_epc->epc_features;
51762306a36Sopenharmony_ci	align = epc_features->align;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
52062306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
52162306a36Sopenharmony_ci	db_entry_size = peer_ctrl->db_entry_size;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	for (i = 0; i < db_count; i++) {
52462306a36Sopenharmony_ci		msg_addr = ALIGN_DOWN(msix_tbl[i].msg_addr, align);
52562306a36Sopenharmony_ci		msg_data = msix_tbl[i].msg_data;
52662306a36Sopenharmony_ci		ret = pci_epc_map_addr(epc, func_no, vfunc_no, phys_addr, msg_addr,
52762306a36Sopenharmony_ci				       db_entry_size);
52862306a36Sopenharmony_ci		if (ret) {
52962306a36Sopenharmony_ci			dev_err(&epc->dev,
53062306a36Sopenharmony_ci				"%s intf: Failed to configure MSI-X IRQ\n",
53162306a36Sopenharmony_ci				pci_epc_interface_string(type));
53262306a36Sopenharmony_ci			return ret;
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci		phys_addr = phys_addr + db_entry_size;
53562306a36Sopenharmony_ci		peer_ctrl->db_data[i] = msg_data;
53662306a36Sopenharmony_ci		peer_ctrl->db_offset[i] = msix_tbl[i].msg_addr & (align - 1);
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci	ntb_epc->is_msix = true;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/**
54462306a36Sopenharmony_ci * epf_ntb_configure_db() - Configure the Outbound Address Space for one host
54562306a36Sopenharmony_ci *   to ring the doorbell of other host
54662306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
54762306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
54862306a36Sopenharmony_ci * @db_count: Count of the number of doorbells that has to be configured
54962306a36Sopenharmony_ci * @msix: Indicates whether MSI-X or MSI should be used
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci * Invokes epf_ntb_configure_msix() or epf_ntb_configure_msi() required for
55262306a36Sopenharmony_ci * one HOST to ring the doorbell of other HOST.
55362306a36Sopenharmony_ci */
55462306a36Sopenharmony_cistatic int epf_ntb_configure_db(struct epf_ntb *ntb,
55562306a36Sopenharmony_ci				enum pci_epc_interface_type type,
55662306a36Sopenharmony_ci				u16 db_count, bool msix)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
55962306a36Sopenharmony_ci	struct pci_epc *epc;
56062306a36Sopenharmony_ci	int ret;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (db_count > MAX_DB_COUNT)
56362306a36Sopenharmony_ci		return -EINVAL;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
56662306a36Sopenharmony_ci	epc = ntb_epc->epc;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (msix)
56962306a36Sopenharmony_ci		ret = epf_ntb_configure_msix(ntb, type, db_count);
57062306a36Sopenharmony_ci	else
57162306a36Sopenharmony_ci		ret = epf_ntb_configure_msi(ntb, type, db_count);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	if (ret)
57462306a36Sopenharmony_ci		dev_err(&epc->dev, "%s intf: Failed to configure DB\n",
57562306a36Sopenharmony_ci			pci_epc_interface_string(type));
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return ret;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci/**
58162306a36Sopenharmony_ci * epf_ntb_teardown_db() - Unmap address in OB address space to MSI/MSI-X
58262306a36Sopenharmony_ci *   address
58362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
58462306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
58562306a36Sopenharmony_ci *
58662306a36Sopenharmony_ci * Invoke pci_epc_unmap_addr() to unmap OB address to MSI/MSI-X address.
58762306a36Sopenharmony_ci */
58862306a36Sopenharmony_cistatic void
58962306a36Sopenharmony_ciepf_ntb_teardown_db(struct epf_ntb *ntb, enum pci_epc_interface_type type)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
59262306a36Sopenharmony_ci	struct pci_epf_bar *peer_epf_bar;
59362306a36Sopenharmony_ci	enum pci_barno peer_barno;
59462306a36Sopenharmony_ci	phys_addr_t phys_addr;
59562306a36Sopenharmony_ci	u8 func_no, vfunc_no;
59662306a36Sopenharmony_ci	struct pci_epc *epc;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
59962306a36Sopenharmony_ci	epc = ntb_epc->epc;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
60262306a36Sopenharmony_ci	peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_DB_MW1];
60362306a36Sopenharmony_ci	peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno];
60462306a36Sopenharmony_ci	phys_addr = peer_epf_bar->phys_addr;
60562306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
60662306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	pci_epc_unmap_addr(epc, func_no, vfunc_no, phys_addr);
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/**
61262306a36Sopenharmony_ci * epf_ntb_cmd_handler() - Handle commands provided by the NTB Host
61362306a36Sopenharmony_ci * @work: work_struct for the two epf_ntb_epc (PRIMARY and SECONDARY)
61462306a36Sopenharmony_ci *
61562306a36Sopenharmony_ci * Workqueue function that gets invoked for the two epf_ntb_epc
61662306a36Sopenharmony_ci * periodically (once every 5ms) to see if it has received any commands
61762306a36Sopenharmony_ci * from NTB host. The host can send commands to configure doorbell or
61862306a36Sopenharmony_ci * configure memory window or to update link status.
61962306a36Sopenharmony_ci */
62062306a36Sopenharmony_cistatic void epf_ntb_cmd_handler(struct work_struct *work)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	enum pci_epc_interface_type type;
62362306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
62462306a36Sopenharmony_ci	struct epf_ntb_ctrl *ctrl;
62562306a36Sopenharmony_ci	u32 command, argument;
62662306a36Sopenharmony_ci	struct epf_ntb *ntb;
62762306a36Sopenharmony_ci	struct device *dev;
62862306a36Sopenharmony_ci	u16 db_count;
62962306a36Sopenharmony_ci	bool is_msix;
63062306a36Sopenharmony_ci	int ret;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	ntb_epc = container_of(work, struct epf_ntb_epc, cmd_handler.work);
63362306a36Sopenharmony_ci	ctrl = ntb_epc->reg;
63462306a36Sopenharmony_ci	command = ctrl->command;
63562306a36Sopenharmony_ci	if (!command)
63662306a36Sopenharmony_ci		goto reset_handler;
63762306a36Sopenharmony_ci	argument = ctrl->argument;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	ctrl->command = 0;
64062306a36Sopenharmony_ci	ctrl->argument = 0;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	ctrl = ntb_epc->reg;
64362306a36Sopenharmony_ci	type = ntb_epc->type;
64462306a36Sopenharmony_ci	ntb = ntb_epc->epf_ntb;
64562306a36Sopenharmony_ci	dev = &ntb->epf->dev;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	switch (command) {
64862306a36Sopenharmony_ci	case COMMAND_CONFIGURE_DOORBELL:
64962306a36Sopenharmony_ci		db_count = argument & DB_COUNT_MASK;
65062306a36Sopenharmony_ci		is_msix = argument & MSIX_ENABLE;
65162306a36Sopenharmony_ci		ret = epf_ntb_configure_db(ntb, type, db_count, is_msix);
65262306a36Sopenharmony_ci		if (ret < 0)
65362306a36Sopenharmony_ci			ctrl->command_status = COMMAND_STATUS_ERROR;
65462306a36Sopenharmony_ci		else
65562306a36Sopenharmony_ci			ctrl->command_status = COMMAND_STATUS_OK;
65662306a36Sopenharmony_ci		break;
65762306a36Sopenharmony_ci	case COMMAND_TEARDOWN_DOORBELL:
65862306a36Sopenharmony_ci		epf_ntb_teardown_db(ntb, type);
65962306a36Sopenharmony_ci		ctrl->command_status = COMMAND_STATUS_OK;
66062306a36Sopenharmony_ci		break;
66162306a36Sopenharmony_ci	case COMMAND_CONFIGURE_MW:
66262306a36Sopenharmony_ci		ret = epf_ntb_configure_mw(ntb, type, argument);
66362306a36Sopenharmony_ci		if (ret < 0)
66462306a36Sopenharmony_ci			ctrl->command_status = COMMAND_STATUS_ERROR;
66562306a36Sopenharmony_ci		else
66662306a36Sopenharmony_ci			ctrl->command_status = COMMAND_STATUS_OK;
66762306a36Sopenharmony_ci		break;
66862306a36Sopenharmony_ci	case COMMAND_TEARDOWN_MW:
66962306a36Sopenharmony_ci		epf_ntb_teardown_mw(ntb, type, argument);
67062306a36Sopenharmony_ci		ctrl->command_status = COMMAND_STATUS_OK;
67162306a36Sopenharmony_ci		break;
67262306a36Sopenharmony_ci	case COMMAND_LINK_UP:
67362306a36Sopenharmony_ci		ntb_epc->linkup = true;
67462306a36Sopenharmony_ci		if (ntb->epc[PRIMARY_INTERFACE]->linkup &&
67562306a36Sopenharmony_ci		    ntb->epc[SECONDARY_INTERFACE]->linkup) {
67662306a36Sopenharmony_ci			ret = epf_ntb_link_up(ntb, true);
67762306a36Sopenharmony_ci			if (ret < 0)
67862306a36Sopenharmony_ci				ctrl->command_status = COMMAND_STATUS_ERROR;
67962306a36Sopenharmony_ci			else
68062306a36Sopenharmony_ci				ctrl->command_status = COMMAND_STATUS_OK;
68162306a36Sopenharmony_ci			goto reset_handler;
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci		ctrl->command_status = COMMAND_STATUS_OK;
68462306a36Sopenharmony_ci		break;
68562306a36Sopenharmony_ci	case COMMAND_LINK_DOWN:
68662306a36Sopenharmony_ci		ntb_epc->linkup = false;
68762306a36Sopenharmony_ci		ret = epf_ntb_link_up(ntb, false);
68862306a36Sopenharmony_ci		if (ret < 0)
68962306a36Sopenharmony_ci			ctrl->command_status = COMMAND_STATUS_ERROR;
69062306a36Sopenharmony_ci		else
69162306a36Sopenharmony_ci			ctrl->command_status = COMMAND_STATUS_OK;
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci	default:
69462306a36Sopenharmony_ci		dev_err(dev, "%s intf UNKNOWN command: %d\n",
69562306a36Sopenharmony_ci			pci_epc_interface_string(type), command);
69662306a36Sopenharmony_ci		break;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cireset_handler:
70062306a36Sopenharmony_ci	queue_delayed_work(kpcintb_workqueue, &ntb_epc->cmd_handler,
70162306a36Sopenharmony_ci			   msecs_to_jiffies(5));
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/**
70562306a36Sopenharmony_ci * epf_ntb_peer_spad_bar_clear() - Clear Peer Scratchpad BAR
70662306a36Sopenharmony_ci * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound
70762306a36Sopenharmony_ci *	     address.
70862306a36Sopenharmony_ci *
70962306a36Sopenharmony_ci *+-----------------+------->+------------------+        +-----------------+
71062306a36Sopenharmony_ci *|       BAR0      |        |  CONFIG REGION   |        |       BAR0      |
71162306a36Sopenharmony_ci *+-----------------+----+   +------------------+<-------+-----------------+
71262306a36Sopenharmony_ci *|       BAR1      |    |   |SCRATCHPAD REGION |        |       BAR1      |
71362306a36Sopenharmony_ci *+-----------------+    +-->+------------------+<-------+-----------------+
71462306a36Sopenharmony_ci *|       BAR2      |            Local Memory            |       BAR2      |
71562306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
71662306a36Sopenharmony_ci *|       BAR3      |                                    |       BAR3      |
71762306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
71862306a36Sopenharmony_ci *|       BAR4      |                                    |       BAR4      |
71962306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
72062306a36Sopenharmony_ci *|       BAR5      |                                    |       BAR5      |
72162306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
72262306a36Sopenharmony_ci *  EP CONTROLLER 1                                        EP CONTROLLER 2
72362306a36Sopenharmony_ci *
72462306a36Sopenharmony_ci * Clear BAR1 of EP CONTROLLER 2 which contains the HOST2's peer scratchpad
72562306a36Sopenharmony_ci * region. While BAR1 is the default peer scratchpad BAR, an NTB could have
72662306a36Sopenharmony_ci * other BARs for peer scratchpad (because of 64-bit BARs or reserved BARs).
72762306a36Sopenharmony_ci * This function can get the exact BAR used for peer scratchpad from
72862306a36Sopenharmony_ci * epf_ntb_bar[BAR_PEER_SPAD].
72962306a36Sopenharmony_ci *
73062306a36Sopenharmony_ci * Since HOST2's peer scratchpad is also HOST1's self scratchpad, this function
73162306a36Sopenharmony_ci * gets the address of peer scratchpad from
73262306a36Sopenharmony_ci * peer_ntb_epc->epf_ntb_bar[BAR_CONFIG].
73362306a36Sopenharmony_ci */
73462306a36Sopenharmony_cistatic void epf_ntb_peer_spad_bar_clear(struct epf_ntb_epc *ntb_epc)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
73762306a36Sopenharmony_ci	enum pci_barno barno;
73862306a36Sopenharmony_ci	u8 func_no, vfunc_no;
73962306a36Sopenharmony_ci	struct pci_epc *epc;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	epc = ntb_epc->epc;
74262306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
74362306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
74462306a36Sopenharmony_ci	barno = ntb_epc->epf_ntb_bar[BAR_PEER_SPAD];
74562306a36Sopenharmony_ci	epf_bar = &ntb_epc->epf_bar[barno];
74662306a36Sopenharmony_ci	pci_epc_clear_bar(epc, func_no, vfunc_no, epf_bar);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci/**
75062306a36Sopenharmony_ci * epf_ntb_peer_spad_bar_set() - Set peer scratchpad BAR
75162306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
75262306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
75362306a36Sopenharmony_ci *
75462306a36Sopenharmony_ci *+-----------------+------->+------------------+        +-----------------+
75562306a36Sopenharmony_ci *|       BAR0      |        |  CONFIG REGION   |        |       BAR0      |
75662306a36Sopenharmony_ci *+-----------------+----+   +------------------+<-------+-----------------+
75762306a36Sopenharmony_ci *|       BAR1      |    |   |SCRATCHPAD REGION |        |       BAR1      |
75862306a36Sopenharmony_ci *+-----------------+    +-->+------------------+<-------+-----------------+
75962306a36Sopenharmony_ci *|       BAR2      |            Local Memory            |       BAR2      |
76062306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
76162306a36Sopenharmony_ci *|       BAR3      |                                    |       BAR3      |
76262306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
76362306a36Sopenharmony_ci *|       BAR4      |                                    |       BAR4      |
76462306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
76562306a36Sopenharmony_ci *|       BAR5      |                                    |       BAR5      |
76662306a36Sopenharmony_ci *+-----------------+                                    +-----------------+
76762306a36Sopenharmony_ci *  EP CONTROLLER 1                                        EP CONTROLLER 2
76862306a36Sopenharmony_ci *
76962306a36Sopenharmony_ci * Set BAR1 of EP CONTROLLER 2 which contains the HOST2's peer scratchpad
77062306a36Sopenharmony_ci * region. While BAR1 is the default peer scratchpad BAR, an NTB could have
77162306a36Sopenharmony_ci * other BARs for peer scratchpad (because of 64-bit BARs or reserved BARs).
77262306a36Sopenharmony_ci * This function can get the exact BAR used for peer scratchpad from
77362306a36Sopenharmony_ci * epf_ntb_bar[BAR_PEER_SPAD].
77462306a36Sopenharmony_ci *
77562306a36Sopenharmony_ci * Since HOST2's peer scratchpad is also HOST1's self scratchpad, this function
77662306a36Sopenharmony_ci * gets the address of peer scratchpad from
77762306a36Sopenharmony_ci * peer_ntb_epc->epf_ntb_bar[BAR_CONFIG].
77862306a36Sopenharmony_ci */
77962306a36Sopenharmony_cistatic int epf_ntb_peer_spad_bar_set(struct epf_ntb *ntb,
78062306a36Sopenharmony_ci				     enum pci_epc_interface_type type)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
78362306a36Sopenharmony_ci	struct pci_epf_bar *peer_epf_bar, *epf_bar;
78462306a36Sopenharmony_ci	enum pci_barno peer_barno, barno;
78562306a36Sopenharmony_ci	u32 peer_spad_offset;
78662306a36Sopenharmony_ci	u8 func_no, vfunc_no;
78762306a36Sopenharmony_ci	struct pci_epc *epc;
78862306a36Sopenharmony_ci	struct device *dev;
78962306a36Sopenharmony_ci	int ret;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	dev = &ntb->epf->dev;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
79462306a36Sopenharmony_ci	peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_CONFIG];
79562306a36Sopenharmony_ci	peer_epf_bar = &peer_ntb_epc->epf_bar[peer_barno];
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
79862306a36Sopenharmony_ci	barno = ntb_epc->epf_ntb_bar[BAR_PEER_SPAD];
79962306a36Sopenharmony_ci	epf_bar = &ntb_epc->epf_bar[barno];
80062306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
80162306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
80262306a36Sopenharmony_ci	epc = ntb_epc->epc;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	peer_spad_offset = peer_ntb_epc->reg->spad_offset;
80562306a36Sopenharmony_ci	epf_bar->phys_addr = peer_epf_bar->phys_addr + peer_spad_offset;
80662306a36Sopenharmony_ci	epf_bar->size = peer_ntb_epc->spad_size;
80762306a36Sopenharmony_ci	epf_bar->barno = barno;
80862306a36Sopenharmony_ci	epf_bar->flags = PCI_BASE_ADDRESS_MEM_TYPE_32;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar);
81162306a36Sopenharmony_ci	if (ret) {
81262306a36Sopenharmony_ci		dev_err(dev, "%s intf: peer SPAD BAR set failed\n",
81362306a36Sopenharmony_ci			pci_epc_interface_string(type));
81462306a36Sopenharmony_ci		return ret;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	return 0;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci/**
82162306a36Sopenharmony_ci * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR
82262306a36Sopenharmony_ci * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound
82362306a36Sopenharmony_ci *	     address.
82462306a36Sopenharmony_ci *
82562306a36Sopenharmony_ci * +-----------------+------->+------------------+        +-----------------+
82662306a36Sopenharmony_ci * |       BAR0      |        |  CONFIG REGION   |        |       BAR0      |
82762306a36Sopenharmony_ci * +-----------------+----+   +------------------+<-------+-----------------+
82862306a36Sopenharmony_ci * |       BAR1      |    |   |SCRATCHPAD REGION |        |       BAR1      |
82962306a36Sopenharmony_ci * +-----------------+    +-->+------------------+<-------+-----------------+
83062306a36Sopenharmony_ci * |       BAR2      |            Local Memory            |       BAR2      |
83162306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
83262306a36Sopenharmony_ci * |       BAR3      |                                    |       BAR3      |
83362306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
83462306a36Sopenharmony_ci * |       BAR4      |                                    |       BAR4      |
83562306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
83662306a36Sopenharmony_ci * |       BAR5      |                                    |       BAR5      |
83762306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
83862306a36Sopenharmony_ci *   EP CONTROLLER 1                                        EP CONTROLLER 2
83962306a36Sopenharmony_ci *
84062306a36Sopenharmony_ci * Clear BAR0 of EP CONTROLLER 1 which contains the HOST1's config and
84162306a36Sopenharmony_ci * self scratchpad region (removes inbound ATU configuration). While BAR0 is
84262306a36Sopenharmony_ci * the default self scratchpad BAR, an NTB could have other BARs for self
84362306a36Sopenharmony_ci * scratchpad (because of reserved BARs). This function can get the exact BAR
84462306a36Sopenharmony_ci * used for self scratchpad from epf_ntb_bar[BAR_CONFIG].
84562306a36Sopenharmony_ci *
84662306a36Sopenharmony_ci * Please note the self scratchpad region and config region is combined to
84762306a36Sopenharmony_ci * a single region and mapped using the same BAR. Also note HOST2's peer
84862306a36Sopenharmony_ci * scratchpad is HOST1's self scratchpad.
84962306a36Sopenharmony_ci */
85062306a36Sopenharmony_cistatic void epf_ntb_config_sspad_bar_clear(struct epf_ntb_epc *ntb_epc)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
85362306a36Sopenharmony_ci	enum pci_barno barno;
85462306a36Sopenharmony_ci	u8 func_no, vfunc_no;
85562306a36Sopenharmony_ci	struct pci_epc *epc;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	epc = ntb_epc->epc;
85862306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
85962306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
86062306a36Sopenharmony_ci	barno = ntb_epc->epf_ntb_bar[BAR_CONFIG];
86162306a36Sopenharmony_ci	epf_bar = &ntb_epc->epf_bar[barno];
86262306a36Sopenharmony_ci	pci_epc_clear_bar(epc, func_no, vfunc_no, epf_bar);
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci/**
86662306a36Sopenharmony_ci * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR
86762306a36Sopenharmony_ci * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound
86862306a36Sopenharmony_ci *	     address.
86962306a36Sopenharmony_ci *
87062306a36Sopenharmony_ci * +-----------------+------->+------------------+        +-----------------+
87162306a36Sopenharmony_ci * |       BAR0      |        |  CONFIG REGION   |        |       BAR0      |
87262306a36Sopenharmony_ci * +-----------------+----+   +------------------+<-------+-----------------+
87362306a36Sopenharmony_ci * |       BAR1      |    |   |SCRATCHPAD REGION |        |       BAR1      |
87462306a36Sopenharmony_ci * +-----------------+    +-->+------------------+<-------+-----------------+
87562306a36Sopenharmony_ci * |       BAR2      |            Local Memory            |       BAR2      |
87662306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
87762306a36Sopenharmony_ci * |       BAR3      |                                    |       BAR3      |
87862306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
87962306a36Sopenharmony_ci * |       BAR4      |                                    |       BAR4      |
88062306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
88162306a36Sopenharmony_ci * |       BAR5      |                                    |       BAR5      |
88262306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
88362306a36Sopenharmony_ci *   EP CONTROLLER 1                                        EP CONTROLLER 2
88462306a36Sopenharmony_ci *
88562306a36Sopenharmony_ci * Map BAR0 of EP CONTROLLER 1 which contains the HOST1's config and
88662306a36Sopenharmony_ci * self scratchpad region. While BAR0 is the default self scratchpad BAR, an
88762306a36Sopenharmony_ci * NTB could have other BARs for self scratchpad (because of reserved BARs).
88862306a36Sopenharmony_ci * This function can get the exact BAR used for self scratchpad from
88962306a36Sopenharmony_ci * epf_ntb_bar[BAR_CONFIG].
89062306a36Sopenharmony_ci *
89162306a36Sopenharmony_ci * Please note the self scratchpad region and config region is combined to
89262306a36Sopenharmony_ci * a single region and mapped using the same BAR. Also note HOST2's peer
89362306a36Sopenharmony_ci * scratchpad is HOST1's self scratchpad.
89462306a36Sopenharmony_ci */
89562306a36Sopenharmony_cistatic int epf_ntb_config_sspad_bar_set(struct epf_ntb_epc *ntb_epc)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
89862306a36Sopenharmony_ci	enum pci_barno barno;
89962306a36Sopenharmony_ci	u8 func_no, vfunc_no;
90062306a36Sopenharmony_ci	struct epf_ntb *ntb;
90162306a36Sopenharmony_ci	struct pci_epc *epc;
90262306a36Sopenharmony_ci	struct device *dev;
90362306a36Sopenharmony_ci	int ret;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	ntb = ntb_epc->epf_ntb;
90662306a36Sopenharmony_ci	dev = &ntb->epf->dev;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	epc = ntb_epc->epc;
90962306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
91062306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
91162306a36Sopenharmony_ci	barno = ntb_epc->epf_ntb_bar[BAR_CONFIG];
91262306a36Sopenharmony_ci	epf_bar = &ntb_epc->epf_bar[barno];
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar);
91562306a36Sopenharmony_ci	if (ret) {
91662306a36Sopenharmony_ci		dev_err(dev, "%s inft: Config/Status/SPAD BAR set failed\n",
91762306a36Sopenharmony_ci			pci_epc_interface_string(ntb_epc->type));
91862306a36Sopenharmony_ci		return ret;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return 0;
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci/**
92562306a36Sopenharmony_ci * epf_ntb_config_spad_bar_free() - Free the physical memory associated with
92662306a36Sopenharmony_ci *   config + scratchpad region
92762306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * +-----------------+------->+------------------+        +-----------------+
93062306a36Sopenharmony_ci * |       BAR0      |        |  CONFIG REGION   |        |       BAR0      |
93162306a36Sopenharmony_ci * +-----------------+----+   +------------------+<-------+-----------------+
93262306a36Sopenharmony_ci * |       BAR1      |    |   |SCRATCHPAD REGION |        |       BAR1      |
93362306a36Sopenharmony_ci * +-----------------+    +-->+------------------+<-------+-----------------+
93462306a36Sopenharmony_ci * |       BAR2      |            Local Memory            |       BAR2      |
93562306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
93662306a36Sopenharmony_ci * |       BAR3      |                                    |       BAR3      |
93762306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
93862306a36Sopenharmony_ci * |       BAR4      |                                    |       BAR4      |
93962306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
94062306a36Sopenharmony_ci * |       BAR5      |                                    |       BAR5      |
94162306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
94262306a36Sopenharmony_ci *   EP CONTROLLER 1                                        EP CONTROLLER 2
94362306a36Sopenharmony_ci *
94462306a36Sopenharmony_ci * Free the Local Memory mentioned in the above diagram. After invoking this
94562306a36Sopenharmony_ci * function, any of config + self scratchpad region of HOST1 or peer scratchpad
94662306a36Sopenharmony_ci * region of HOST2 should not be accessed.
94762306a36Sopenharmony_ci */
94862306a36Sopenharmony_cistatic void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	enum pci_epc_interface_type type;
95162306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
95262306a36Sopenharmony_ci	enum pci_barno barno;
95362306a36Sopenharmony_ci	struct pci_epf *epf;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	epf = ntb->epf;
95662306a36Sopenharmony_ci	for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
95762306a36Sopenharmony_ci		ntb_epc = ntb->epc[type];
95862306a36Sopenharmony_ci		barno = ntb_epc->epf_ntb_bar[BAR_CONFIG];
95962306a36Sopenharmony_ci		if (ntb_epc->reg)
96062306a36Sopenharmony_ci			pci_epf_free_space(epf, ntb_epc->reg, barno, type);
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci/**
96562306a36Sopenharmony_ci * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad
96662306a36Sopenharmony_ci *   region
96762306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
96862306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
96962306a36Sopenharmony_ci *
97062306a36Sopenharmony_ci * +-----------------+------->+------------------+        +-----------------+
97162306a36Sopenharmony_ci * |       BAR0      |        |  CONFIG REGION   |        |       BAR0      |
97262306a36Sopenharmony_ci * +-----------------+----+   +------------------+<-------+-----------------+
97362306a36Sopenharmony_ci * |       BAR1      |    |   |SCRATCHPAD REGION |        |       BAR1      |
97462306a36Sopenharmony_ci * +-----------------+    +-->+------------------+<-------+-----------------+
97562306a36Sopenharmony_ci * |       BAR2      |            Local Memory            |       BAR2      |
97662306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
97762306a36Sopenharmony_ci * |       BAR3      |                                    |       BAR3      |
97862306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
97962306a36Sopenharmony_ci * |       BAR4      |                                    |       BAR4      |
98062306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
98162306a36Sopenharmony_ci * |       BAR5      |                                    |       BAR5      |
98262306a36Sopenharmony_ci * +-----------------+                                    +-----------------+
98362306a36Sopenharmony_ci *   EP CONTROLLER 1                                        EP CONTROLLER 2
98462306a36Sopenharmony_ci *
98562306a36Sopenharmony_ci * Allocate the Local Memory mentioned in the above diagram. The size of
98662306a36Sopenharmony_ci * CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION
98762306a36Sopenharmony_ci * is obtained from "spad-count" configfs entry.
98862306a36Sopenharmony_ci *
98962306a36Sopenharmony_ci * The size of both config region and scratchpad region has to be aligned,
99062306a36Sopenharmony_ci * since the scratchpad region will also be mapped as PEER SCRATCHPAD of
99162306a36Sopenharmony_ci * other host using a separate BAR.
99262306a36Sopenharmony_ci */
99362306a36Sopenharmony_cistatic int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb,
99462306a36Sopenharmony_ci					 enum pci_epc_interface_type type)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	const struct pci_epc_features *peer_epc_features, *epc_features;
99762306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
99862306a36Sopenharmony_ci	size_t msix_table_size, pba_size, align;
99962306a36Sopenharmony_ci	enum pci_barno peer_barno, barno;
100062306a36Sopenharmony_ci	struct epf_ntb_ctrl *ctrl;
100162306a36Sopenharmony_ci	u32 spad_size, ctrl_size;
100262306a36Sopenharmony_ci	u64 size, peer_size;
100362306a36Sopenharmony_ci	struct pci_epf *epf;
100462306a36Sopenharmony_ci	struct device *dev;
100562306a36Sopenharmony_ci	bool msix_capable;
100662306a36Sopenharmony_ci	u32 spad_count;
100762306a36Sopenharmony_ci	void *base;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	epf = ntb->epf;
101062306a36Sopenharmony_ci	dev = &epf->dev;
101162306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	epc_features = ntb_epc->epc_features;
101462306a36Sopenharmony_ci	barno = ntb_epc->epf_ntb_bar[BAR_CONFIG];
101562306a36Sopenharmony_ci	size = epc_features->bar_fixed_size[barno];
101662306a36Sopenharmony_ci	align = epc_features->align;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
101962306a36Sopenharmony_ci	peer_epc_features = peer_ntb_epc->epc_features;
102062306a36Sopenharmony_ci	peer_barno = ntb_epc->epf_ntb_bar[BAR_PEER_SPAD];
102162306a36Sopenharmony_ci	peer_size = peer_epc_features->bar_fixed_size[peer_barno];
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* Check if epc_features is populated incorrectly */
102462306a36Sopenharmony_ci	if ((!IS_ALIGNED(size, align)))
102562306a36Sopenharmony_ci		return -EINVAL;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	spad_count = ntb->spad_count;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	ctrl_size = sizeof(struct epf_ntb_ctrl);
103062306a36Sopenharmony_ci	spad_size = spad_count * 4;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	msix_capable = epc_features->msix_capable;
103362306a36Sopenharmony_ci	if (msix_capable) {
103462306a36Sopenharmony_ci		msix_table_size = PCI_MSIX_ENTRY_SIZE * ntb->db_count;
103562306a36Sopenharmony_ci		ctrl_size = ALIGN(ctrl_size, 8);
103662306a36Sopenharmony_ci		ntb_epc->msix_table_offset = ctrl_size;
103762306a36Sopenharmony_ci		ntb_epc->msix_bar = barno;
103862306a36Sopenharmony_ci		/* Align to QWORD or 8 Bytes */
103962306a36Sopenharmony_ci		pba_size = ALIGN(DIV_ROUND_UP(ntb->db_count, 8), 8);
104062306a36Sopenharmony_ci		ctrl_size = ctrl_size + msix_table_size + pba_size;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (!align) {
104462306a36Sopenharmony_ci		ctrl_size = roundup_pow_of_two(ctrl_size);
104562306a36Sopenharmony_ci		spad_size = roundup_pow_of_two(spad_size);
104662306a36Sopenharmony_ci	} else {
104762306a36Sopenharmony_ci		ctrl_size = ALIGN(ctrl_size, align);
104862306a36Sopenharmony_ci		spad_size = ALIGN(spad_size, align);
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if (peer_size) {
105262306a36Sopenharmony_ci		if (peer_size < spad_size)
105362306a36Sopenharmony_ci			spad_count = peer_size / 4;
105462306a36Sopenharmony_ci		spad_size = peer_size;
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/*
105862306a36Sopenharmony_ci	 * In order to make sure SPAD offset is aligned to its size,
105962306a36Sopenharmony_ci	 * expand control region size to the size of SPAD if SPAD size
106062306a36Sopenharmony_ci	 * is greater than control region size.
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	if (spad_size > ctrl_size)
106362306a36Sopenharmony_ci		ctrl_size = spad_size;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (!size)
106662306a36Sopenharmony_ci		size = ctrl_size + spad_size;
106762306a36Sopenharmony_ci	else if (size < ctrl_size + spad_size)
106862306a36Sopenharmony_ci		return -EINVAL;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	base = pci_epf_alloc_space(epf, size, barno, align, type);
107162306a36Sopenharmony_ci	if (!base) {
107262306a36Sopenharmony_ci		dev_err(dev, "%s intf: Config/Status/SPAD alloc region fail\n",
107362306a36Sopenharmony_ci			pci_epc_interface_string(type));
107462306a36Sopenharmony_ci		return -ENOMEM;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	ntb_epc->reg = base;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	ctrl = ntb_epc->reg;
108062306a36Sopenharmony_ci	ctrl->spad_offset = ctrl_size;
108162306a36Sopenharmony_ci	ctrl->spad_count = spad_count;
108262306a36Sopenharmony_ci	ctrl->num_mws = ntb->num_mws;
108362306a36Sopenharmony_ci	ctrl->db_entry_size = align ? align : 4;
108462306a36Sopenharmony_ci	ntb_epc->spad_size = spad_size;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	return 0;
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci/**
109062306a36Sopenharmony_ci * epf_ntb_config_spad_bar_alloc_interface() - Allocate memory for config +
109162306a36Sopenharmony_ci *   scratchpad region for each of PRIMARY and SECONDARY interface
109262306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
109362306a36Sopenharmony_ci *
109462306a36Sopenharmony_ci * Wrapper for epf_ntb_config_spad_bar_alloc() which allocates memory for
109562306a36Sopenharmony_ci * config + scratchpad region for a specific interface
109662306a36Sopenharmony_ci */
109762306a36Sopenharmony_cistatic int epf_ntb_config_spad_bar_alloc_interface(struct epf_ntb *ntb)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	enum pci_epc_interface_type type;
110062306a36Sopenharmony_ci	struct device *dev;
110162306a36Sopenharmony_ci	int ret;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	dev = &ntb->epf->dev;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
110662306a36Sopenharmony_ci		ret = epf_ntb_config_spad_bar_alloc(ntb, type);
110762306a36Sopenharmony_ci		if (ret) {
110862306a36Sopenharmony_ci			dev_err(dev, "%s intf: Config/SPAD BAR alloc failed\n",
110962306a36Sopenharmony_ci				pci_epc_interface_string(type));
111062306a36Sopenharmony_ci			return ret;
111162306a36Sopenharmony_ci		}
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci/**
111862306a36Sopenharmony_ci * epf_ntb_free_peer_mem() - Free memory allocated in peers outbound address
111962306a36Sopenharmony_ci *   space
112062306a36Sopenharmony_ci * @ntb_epc: EPC associated with one of the HOST which holds peers outbound
112162306a36Sopenharmony_ci *   address regions
112262306a36Sopenharmony_ci *
112362306a36Sopenharmony_ci * +-----------------+    +---->+----------------+-----------+-----------------+
112462306a36Sopenharmony_ci * |       BAR0      |    |     |   Doorbell 1   +-----------> MSI|X ADDRESS 1 |
112562306a36Sopenharmony_ci * +-----------------+    |     +----------------+           +-----------------+
112662306a36Sopenharmony_ci * |       BAR1      |    |     |   Doorbell 2   +---------+ |                 |
112762306a36Sopenharmony_ci * +-----------------+----+     +----------------+         | |                 |
112862306a36Sopenharmony_ci * |       BAR2      |          |   Doorbell 3   +-------+ | +-----------------+
112962306a36Sopenharmony_ci * +-----------------+----+     +----------------+       | +-> MSI|X ADDRESS 2 |
113062306a36Sopenharmony_ci * |       BAR3      |    |     |   Doorbell 4   +-----+ |   +-----------------+
113162306a36Sopenharmony_ci * +-----------------+    |     |----------------+     | |   |                 |
113262306a36Sopenharmony_ci * |       BAR4      |    |     |                |     | |   +-----------------+
113362306a36Sopenharmony_ci * +-----------------+    |     |      MW1       +---+ | +-->+ MSI|X ADDRESS 3||
113462306a36Sopenharmony_ci * |       BAR5      |    |     |                |   | |     +-----------------+
113562306a36Sopenharmony_ci * +-----------------+    +---->-----------------+   | |     |                 |
113662306a36Sopenharmony_ci *   EP CONTROLLER 1            |                |   | |     +-----------------+
113762306a36Sopenharmony_ci *                              |                |   | +---->+ MSI|X ADDRESS 4 |
113862306a36Sopenharmony_ci *                              +----------------+   |       +-----------------+
113962306a36Sopenharmony_ci *                      (A)      EP CONTROLLER 2     |       |                 |
114062306a36Sopenharmony_ci *                                 (OB SPACE)        |       |                 |
114162306a36Sopenharmony_ci *                                                   +------->      MW1        |
114262306a36Sopenharmony_ci *                                                           |                 |
114362306a36Sopenharmony_ci *                                                           |                 |
114462306a36Sopenharmony_ci *                                                   (B)     +-----------------+
114562306a36Sopenharmony_ci *                                                           |                 |
114662306a36Sopenharmony_ci *                                                           |                 |
114762306a36Sopenharmony_ci *                                                           |                 |
114862306a36Sopenharmony_ci *                                                           |                 |
114962306a36Sopenharmony_ci *                                                           |                 |
115062306a36Sopenharmony_ci *                                                           +-----------------+
115162306a36Sopenharmony_ci *                                                           PCI Address Space
115262306a36Sopenharmony_ci *                                                           (Managed by HOST2)
115362306a36Sopenharmony_ci *
115462306a36Sopenharmony_ci * Free memory allocated in EP CONTROLLER 2 (OB SPACE) in the above diagram.
115562306a36Sopenharmony_ci * It'll free Doorbell 1, Doorbell 2, Doorbell 3, Doorbell 4, MW1 (and MW2, MW3,
115662306a36Sopenharmony_ci * MW4).
115762306a36Sopenharmony_ci */
115862306a36Sopenharmony_cistatic void epf_ntb_free_peer_mem(struct epf_ntb_epc *ntb_epc)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
116162306a36Sopenharmony_ci	void __iomem *mw_addr;
116262306a36Sopenharmony_ci	phys_addr_t phys_addr;
116362306a36Sopenharmony_ci	enum epf_ntb_bar bar;
116462306a36Sopenharmony_ci	enum pci_barno barno;
116562306a36Sopenharmony_ci	struct pci_epc *epc;
116662306a36Sopenharmony_ci	size_t size;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	epc = ntb_epc->epc;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	for (bar = BAR_DB_MW1; bar < BAR_MW4; bar++) {
117162306a36Sopenharmony_ci		barno = ntb_epc->epf_ntb_bar[bar];
117262306a36Sopenharmony_ci		mw_addr = ntb_epc->mw_addr[barno];
117362306a36Sopenharmony_ci		epf_bar = &ntb_epc->epf_bar[barno];
117462306a36Sopenharmony_ci		phys_addr = epf_bar->phys_addr;
117562306a36Sopenharmony_ci		size = epf_bar->size;
117662306a36Sopenharmony_ci		if (mw_addr) {
117762306a36Sopenharmony_ci			pci_epc_mem_free_addr(epc, phys_addr, mw_addr, size);
117862306a36Sopenharmony_ci			ntb_epc->mw_addr[barno] = NULL;
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci/**
118462306a36Sopenharmony_ci * epf_ntb_db_mw_bar_clear() - Clear doorbell and memory BAR
118562306a36Sopenharmony_ci * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound
118662306a36Sopenharmony_ci *   address
118762306a36Sopenharmony_ci *
118862306a36Sopenharmony_ci * +-----------------+    +---->+----------------+-----------+-----------------+
118962306a36Sopenharmony_ci * |       BAR0      |    |     |   Doorbell 1   +-----------> MSI|X ADDRESS 1 |
119062306a36Sopenharmony_ci * +-----------------+    |     +----------------+           +-----------------+
119162306a36Sopenharmony_ci * |       BAR1      |    |     |   Doorbell 2   +---------+ |                 |
119262306a36Sopenharmony_ci * +-----------------+----+     +----------------+         | |                 |
119362306a36Sopenharmony_ci * |       BAR2      |          |   Doorbell 3   +-------+ | +-----------------+
119462306a36Sopenharmony_ci * +-----------------+----+     +----------------+       | +-> MSI|X ADDRESS 2 |
119562306a36Sopenharmony_ci * |       BAR3      |    |     |   Doorbell 4   +-----+ |   +-----------------+
119662306a36Sopenharmony_ci * +-----------------+    |     |----------------+     | |   |                 |
119762306a36Sopenharmony_ci * |       BAR4      |    |     |                |     | |   +-----------------+
119862306a36Sopenharmony_ci * +-----------------+    |     |      MW1       +---+ | +-->+ MSI|X ADDRESS 3||
119962306a36Sopenharmony_ci * |       BAR5      |    |     |                |   | |     +-----------------+
120062306a36Sopenharmony_ci * +-----------------+    +---->-----------------+   | |     |                 |
120162306a36Sopenharmony_ci *   EP CONTROLLER 1            |                |   | |     +-----------------+
120262306a36Sopenharmony_ci *                              |                |   | +---->+ MSI|X ADDRESS 4 |
120362306a36Sopenharmony_ci *                              +----------------+   |       +-----------------+
120462306a36Sopenharmony_ci *                      (A)      EP CONTROLLER 2     |       |                 |
120562306a36Sopenharmony_ci *                                 (OB SPACE)        |       |                 |
120662306a36Sopenharmony_ci *                                                   +------->      MW1        |
120762306a36Sopenharmony_ci *                                                           |                 |
120862306a36Sopenharmony_ci *                                                           |                 |
120962306a36Sopenharmony_ci *                                                   (B)     +-----------------+
121062306a36Sopenharmony_ci *                                                           |                 |
121162306a36Sopenharmony_ci *                                                           |                 |
121262306a36Sopenharmony_ci *                                                           |                 |
121362306a36Sopenharmony_ci *                                                           |                 |
121462306a36Sopenharmony_ci *                                                           |                 |
121562306a36Sopenharmony_ci *                                                           +-----------------+
121662306a36Sopenharmony_ci *                                                           PCI Address Space
121762306a36Sopenharmony_ci *                                                           (Managed by HOST2)
121862306a36Sopenharmony_ci *
121962306a36Sopenharmony_ci * Clear doorbell and memory BARs (remove inbound ATU configuration). In the above
122062306a36Sopenharmony_ci * diagram it clears BAR2 TO BAR5 of EP CONTROLLER 1 (Doorbell BAR, MW1 BAR, MW2
122162306a36Sopenharmony_ci * BAR, MW3 BAR and MW4 BAR).
122262306a36Sopenharmony_ci */
122362306a36Sopenharmony_cistatic void epf_ntb_db_mw_bar_clear(struct epf_ntb_epc *ntb_epc)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
122662306a36Sopenharmony_ci	enum epf_ntb_bar bar;
122762306a36Sopenharmony_ci	enum pci_barno barno;
122862306a36Sopenharmony_ci	u8 func_no, vfunc_no;
122962306a36Sopenharmony_ci	struct pci_epc *epc;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	epc = ntb_epc->epc;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
123462306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	for (bar = BAR_DB_MW1; bar < BAR_MW4; bar++) {
123762306a36Sopenharmony_ci		barno = ntb_epc->epf_ntb_bar[bar];
123862306a36Sopenharmony_ci		epf_bar = &ntb_epc->epf_bar[barno];
123962306a36Sopenharmony_ci		pci_epc_clear_bar(epc, func_no, vfunc_no, epf_bar);
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci/**
124462306a36Sopenharmony_ci * epf_ntb_db_mw_bar_cleanup() - Clear doorbell/memory BAR and free memory
124562306a36Sopenharmony_ci *   allocated in peers outbound address space
124662306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
124762306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
124862306a36Sopenharmony_ci *
124962306a36Sopenharmony_ci * Wrapper for epf_ntb_db_mw_bar_clear() to clear HOST1's BAR and
125062306a36Sopenharmony_ci * epf_ntb_free_peer_mem() which frees up HOST2 outbound memory.
125162306a36Sopenharmony_ci */
125262306a36Sopenharmony_cistatic void epf_ntb_db_mw_bar_cleanup(struct epf_ntb *ntb,
125362306a36Sopenharmony_ci				      enum pci_epc_interface_type type)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
125862306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	epf_ntb_db_mw_bar_clear(ntb_epc);
126162306a36Sopenharmony_ci	epf_ntb_free_peer_mem(peer_ntb_epc);
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci/**
126562306a36Sopenharmony_ci * epf_ntb_configure_interrupt() - Configure MSI/MSI-X capability
126662306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
126762306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
126862306a36Sopenharmony_ci *
126962306a36Sopenharmony_ci * Configure MSI/MSI-X capability for each interface with number of
127062306a36Sopenharmony_ci * interrupts equal to "db_count" configfs entry.
127162306a36Sopenharmony_ci */
127262306a36Sopenharmony_cistatic int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
127362306a36Sopenharmony_ci				       enum pci_epc_interface_type type)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	const struct pci_epc_features *epc_features;
127662306a36Sopenharmony_ci	bool msix_capable, msi_capable;
127762306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
127862306a36Sopenharmony_ci	u8 func_no, vfunc_no;
127962306a36Sopenharmony_ci	struct pci_epc *epc;
128062306a36Sopenharmony_ci	struct device *dev;
128162306a36Sopenharmony_ci	u32 db_count;
128262306a36Sopenharmony_ci	int ret;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
128562306a36Sopenharmony_ci	dev = &ntb->epf->dev;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	epc_features = ntb_epc->epc_features;
128862306a36Sopenharmony_ci	msix_capable = epc_features->msix_capable;
128962306a36Sopenharmony_ci	msi_capable = epc_features->msi_capable;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (!(msix_capable || msi_capable)) {
129262306a36Sopenharmony_ci		dev_err(dev, "MSI or MSI-X is required for doorbell\n");
129362306a36Sopenharmony_ci		return -EINVAL;
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
129762306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	db_count = ntb->db_count;
130062306a36Sopenharmony_ci	if (db_count > MAX_DB_COUNT) {
130162306a36Sopenharmony_ci		dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT);
130262306a36Sopenharmony_ci		return -EINVAL;
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	ntb->db_count = db_count;
130662306a36Sopenharmony_ci	epc = ntb_epc->epc;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (msi_capable) {
130962306a36Sopenharmony_ci		ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count);
131062306a36Sopenharmony_ci		if (ret) {
131162306a36Sopenharmony_ci			dev_err(dev, "%s intf: MSI configuration failed\n",
131262306a36Sopenharmony_ci				pci_epc_interface_string(type));
131362306a36Sopenharmony_ci			return ret;
131462306a36Sopenharmony_ci		}
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (msix_capable) {
131862306a36Sopenharmony_ci		ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count,
131962306a36Sopenharmony_ci				       ntb_epc->msix_bar,
132062306a36Sopenharmony_ci				       ntb_epc->msix_table_offset);
132162306a36Sopenharmony_ci		if (ret) {
132262306a36Sopenharmony_ci			dev_err(dev, "MSI configuration failed\n");
132362306a36Sopenharmony_ci			return ret;
132462306a36Sopenharmony_ci		}
132562306a36Sopenharmony_ci	}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	return 0;
132862306a36Sopenharmony_ci}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci/**
133162306a36Sopenharmony_ci * epf_ntb_alloc_peer_mem() - Allocate memory in peer's outbound address space
133262306a36Sopenharmony_ci * @dev: The PCI device.
133362306a36Sopenharmony_ci * @ntb_epc: EPC associated with one of the HOST whose BAR holds peer's outbound
133462306a36Sopenharmony_ci *   address
133562306a36Sopenharmony_ci * @bar: BAR of @ntb_epc in for which memory has to be allocated (could be
133662306a36Sopenharmony_ci *   BAR_DB_MW1, BAR_MW2, BAR_MW3, BAR_MW4)
133762306a36Sopenharmony_ci * @peer_ntb_epc: EPC associated with HOST whose outbound address space is
133862306a36Sopenharmony_ci *   used by @ntb_epc
133962306a36Sopenharmony_ci * @size: Size of the address region that has to be allocated in peers OB SPACE
134062306a36Sopenharmony_ci *
134162306a36Sopenharmony_ci *
134262306a36Sopenharmony_ci * +-----------------+    +---->+----------------+-----------+-----------------+
134362306a36Sopenharmony_ci * |       BAR0      |    |     |   Doorbell 1   +-----------> MSI|X ADDRESS 1 |
134462306a36Sopenharmony_ci * +-----------------+    |     +----------------+           +-----------------+
134562306a36Sopenharmony_ci * |       BAR1      |    |     |   Doorbell 2   +---------+ |                 |
134662306a36Sopenharmony_ci * +-----------------+----+     +----------------+         | |                 |
134762306a36Sopenharmony_ci * |       BAR2      |          |   Doorbell 3   +-------+ | +-----------------+
134862306a36Sopenharmony_ci * +-----------------+----+     +----------------+       | +-> MSI|X ADDRESS 2 |
134962306a36Sopenharmony_ci * |       BAR3      |    |     |   Doorbell 4   +-----+ |   +-----------------+
135062306a36Sopenharmony_ci * +-----------------+    |     |----------------+     | |   |                 |
135162306a36Sopenharmony_ci * |       BAR4      |    |     |                |     | |   +-----------------+
135262306a36Sopenharmony_ci * +-----------------+    |     |      MW1       +---+ | +-->+ MSI|X ADDRESS 3||
135362306a36Sopenharmony_ci * |       BAR5      |    |     |                |   | |     +-----------------+
135462306a36Sopenharmony_ci * +-----------------+    +---->-----------------+   | |     |                 |
135562306a36Sopenharmony_ci *   EP CONTROLLER 1            |                |   | |     +-----------------+
135662306a36Sopenharmony_ci *                              |                |   | +---->+ MSI|X ADDRESS 4 |
135762306a36Sopenharmony_ci *                              +----------------+   |       +-----------------+
135862306a36Sopenharmony_ci *                      (A)      EP CONTROLLER 2     |       |                 |
135962306a36Sopenharmony_ci *                                 (OB SPACE)        |       |                 |
136062306a36Sopenharmony_ci *                                                   +------->      MW1        |
136162306a36Sopenharmony_ci *                                                           |                 |
136262306a36Sopenharmony_ci *                                                           |                 |
136362306a36Sopenharmony_ci *                                                   (B)     +-----------------+
136462306a36Sopenharmony_ci *                                                           |                 |
136562306a36Sopenharmony_ci *                                                           |                 |
136662306a36Sopenharmony_ci *                                                           |                 |
136762306a36Sopenharmony_ci *                                                           |                 |
136862306a36Sopenharmony_ci *                                                           |                 |
136962306a36Sopenharmony_ci *                                                           +-----------------+
137062306a36Sopenharmony_ci *                                                           PCI Address Space
137162306a36Sopenharmony_ci *                                                           (Managed by HOST2)
137262306a36Sopenharmony_ci *
137362306a36Sopenharmony_ci * Allocate memory in OB space of EP CONTROLLER 2 in the above diagram. Allocate
137462306a36Sopenharmony_ci * for Doorbell 1, Doorbell 2, Doorbell 3, Doorbell 4, MW1 (and MW2, MW3, MW4).
137562306a36Sopenharmony_ci */
137662306a36Sopenharmony_cistatic int epf_ntb_alloc_peer_mem(struct device *dev,
137762306a36Sopenharmony_ci				  struct epf_ntb_epc *ntb_epc,
137862306a36Sopenharmony_ci				  enum epf_ntb_bar bar,
137962306a36Sopenharmony_ci				  struct epf_ntb_epc *peer_ntb_epc,
138062306a36Sopenharmony_ci				  size_t size)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	const struct pci_epc_features *epc_features;
138362306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
138462306a36Sopenharmony_ci	struct pci_epc *peer_epc;
138562306a36Sopenharmony_ci	phys_addr_t phys_addr;
138662306a36Sopenharmony_ci	void __iomem *mw_addr;
138762306a36Sopenharmony_ci	enum pci_barno barno;
138862306a36Sopenharmony_ci	size_t align;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	epc_features = ntb_epc->epc_features;
139162306a36Sopenharmony_ci	align = epc_features->align;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	if (size < 128)
139462306a36Sopenharmony_ci		size = 128;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	if (align)
139762306a36Sopenharmony_ci		size = ALIGN(size, align);
139862306a36Sopenharmony_ci	else
139962306a36Sopenharmony_ci		size = roundup_pow_of_two(size);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	peer_epc = peer_ntb_epc->epc;
140262306a36Sopenharmony_ci	mw_addr = pci_epc_mem_alloc_addr(peer_epc, &phys_addr, size);
140362306a36Sopenharmony_ci	if (!mw_addr) {
140462306a36Sopenharmony_ci		dev_err(dev, "%s intf: Failed to allocate OB address\n",
140562306a36Sopenharmony_ci			pci_epc_interface_string(peer_ntb_epc->type));
140662306a36Sopenharmony_ci		return -ENOMEM;
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	barno = ntb_epc->epf_ntb_bar[bar];
141062306a36Sopenharmony_ci	epf_bar = &ntb_epc->epf_bar[barno];
141162306a36Sopenharmony_ci	ntb_epc->mw_addr[barno] = mw_addr;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	epf_bar->phys_addr = phys_addr;
141462306a36Sopenharmony_ci	epf_bar->size = size;
141562306a36Sopenharmony_ci	epf_bar->barno = barno;
141662306a36Sopenharmony_ci	epf_bar->flags = PCI_BASE_ADDRESS_MEM_TYPE_32;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	return 0;
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci/**
142262306a36Sopenharmony_ci * epf_ntb_db_mw_bar_init() - Configure Doorbell and Memory window BARs
142362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
142462306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
142562306a36Sopenharmony_ci *
142662306a36Sopenharmony_ci * Wrapper for epf_ntb_alloc_peer_mem() and pci_epc_set_bar() that allocates
142762306a36Sopenharmony_ci * memory in OB address space of HOST2 and configures BAR of HOST1
142862306a36Sopenharmony_ci */
142962306a36Sopenharmony_cistatic int epf_ntb_db_mw_bar_init(struct epf_ntb *ntb,
143062306a36Sopenharmony_ci				  enum pci_epc_interface_type type)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	const struct pci_epc_features *epc_features;
143362306a36Sopenharmony_ci	struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
143462306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
143562306a36Sopenharmony_ci	struct epf_ntb_ctrl *ctrl;
143662306a36Sopenharmony_ci	u32 num_mws, db_count;
143762306a36Sopenharmony_ci	enum epf_ntb_bar bar;
143862306a36Sopenharmony_ci	enum pci_barno barno;
143962306a36Sopenharmony_ci	u8 func_no, vfunc_no;
144062306a36Sopenharmony_ci	struct pci_epc *epc;
144162306a36Sopenharmony_ci	struct device *dev;
144262306a36Sopenharmony_ci	size_t align;
144362306a36Sopenharmony_ci	int ret, i;
144462306a36Sopenharmony_ci	u64 size;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
144762306a36Sopenharmony_ci	peer_ntb_epc = ntb->epc[!type];
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	dev = &ntb->epf->dev;
145062306a36Sopenharmony_ci	epc_features = ntb_epc->epc_features;
145162306a36Sopenharmony_ci	align = epc_features->align;
145262306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
145362306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
145462306a36Sopenharmony_ci	epc = ntb_epc->epc;
145562306a36Sopenharmony_ci	num_mws = ntb->num_mws;
145662306a36Sopenharmony_ci	db_count = ntb->db_count;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	for (bar = BAR_DB_MW1, i = 0; i < num_mws; bar++, i++) {
145962306a36Sopenharmony_ci		if (bar == BAR_DB_MW1) {
146062306a36Sopenharmony_ci			align = align ? align : 4;
146162306a36Sopenharmony_ci			size = db_count * align;
146262306a36Sopenharmony_ci			size = ALIGN(size, ntb->mws_size[i]);
146362306a36Sopenharmony_ci			ctrl = ntb_epc->reg;
146462306a36Sopenharmony_ci			ctrl->mw1_offset = size;
146562306a36Sopenharmony_ci			size += ntb->mws_size[i];
146662306a36Sopenharmony_ci		} else {
146762306a36Sopenharmony_ci			size = ntb->mws_size[i];
146862306a36Sopenharmony_ci		}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci		ret = epf_ntb_alloc_peer_mem(dev, ntb_epc, bar,
147162306a36Sopenharmony_ci					     peer_ntb_epc, size);
147262306a36Sopenharmony_ci		if (ret) {
147362306a36Sopenharmony_ci			dev_err(dev, "%s intf: DoorBell mem alloc failed\n",
147462306a36Sopenharmony_ci				pci_epc_interface_string(type));
147562306a36Sopenharmony_ci			goto err_alloc_peer_mem;
147662306a36Sopenharmony_ci		}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci		barno = ntb_epc->epf_ntb_bar[bar];
147962306a36Sopenharmony_ci		epf_bar = &ntb_epc->epf_bar[barno];
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci		ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar);
148262306a36Sopenharmony_ci		if (ret) {
148362306a36Sopenharmony_ci			dev_err(dev, "%s intf: DoorBell BAR set failed\n",
148462306a36Sopenharmony_ci				pci_epc_interface_string(type));
148562306a36Sopenharmony_ci			goto err_alloc_peer_mem;
148662306a36Sopenharmony_ci		}
148762306a36Sopenharmony_ci	}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return 0;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_cierr_alloc_peer_mem:
149262306a36Sopenharmony_ci	epf_ntb_db_mw_bar_cleanup(ntb, type);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	return ret;
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci/**
149862306a36Sopenharmony_ci * epf_ntb_epc_destroy_interface() - Cleanup NTB EPC interface
149962306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
150062306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
150162306a36Sopenharmony_ci *
150262306a36Sopenharmony_ci * Unbind NTB function device from EPC and relinquish reference to pci_epc
150362306a36Sopenharmony_ci * for each of the interface.
150462306a36Sopenharmony_ci */
150562306a36Sopenharmony_cistatic void epf_ntb_epc_destroy_interface(struct epf_ntb *ntb,
150662306a36Sopenharmony_ci					  enum pci_epc_interface_type type)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
150962306a36Sopenharmony_ci	struct pci_epc *epc;
151062306a36Sopenharmony_ci	struct pci_epf *epf;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (type < 0)
151362306a36Sopenharmony_ci		return;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	epf = ntb->epf;
151662306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
151762306a36Sopenharmony_ci	if (!ntb_epc)
151862306a36Sopenharmony_ci		return;
151962306a36Sopenharmony_ci	epc = ntb_epc->epc;
152062306a36Sopenharmony_ci	pci_epc_remove_epf(epc, epf, type);
152162306a36Sopenharmony_ci	pci_epc_put(epc);
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci/**
152562306a36Sopenharmony_ci * epf_ntb_epc_destroy() - Cleanup NTB EPC interface
152662306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
152762306a36Sopenharmony_ci *
152862306a36Sopenharmony_ci * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces
152962306a36Sopenharmony_ci */
153062306a36Sopenharmony_cistatic void epf_ntb_epc_destroy(struct epf_ntb *ntb)
153162306a36Sopenharmony_ci{
153262306a36Sopenharmony_ci	enum pci_epc_interface_type type;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++)
153562306a36Sopenharmony_ci		epf_ntb_epc_destroy_interface(ntb, type);
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci/**
153962306a36Sopenharmony_ci * epf_ntb_epc_create_interface() - Create and initialize NTB EPC interface
154062306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
154162306a36Sopenharmony_ci * @epc: struct pci_epc to which a particular NTB interface should be associated
154262306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
154362306a36Sopenharmony_ci *
154462306a36Sopenharmony_ci * Allocate memory for NTB EPC interface and initialize it.
154562306a36Sopenharmony_ci */
154662306a36Sopenharmony_cistatic int epf_ntb_epc_create_interface(struct epf_ntb *ntb,
154762306a36Sopenharmony_ci					struct pci_epc *epc,
154862306a36Sopenharmony_ci					enum pci_epc_interface_type type)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	const struct pci_epc_features *epc_features;
155162306a36Sopenharmony_ci	struct pci_epf_bar *epf_bar;
155262306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
155362306a36Sopenharmony_ci	u8 func_no, vfunc_no;
155462306a36Sopenharmony_ci	struct pci_epf *epf;
155562306a36Sopenharmony_ci	struct device *dev;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	dev = &ntb->epf->dev;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	ntb_epc = devm_kzalloc(dev, sizeof(*ntb_epc), GFP_KERNEL);
156062306a36Sopenharmony_ci	if (!ntb_epc)
156162306a36Sopenharmony_ci		return -ENOMEM;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	epf = ntb->epf;
156462306a36Sopenharmony_ci	vfunc_no = epf->vfunc_no;
156562306a36Sopenharmony_ci	if (type == PRIMARY_INTERFACE) {
156662306a36Sopenharmony_ci		func_no = epf->func_no;
156762306a36Sopenharmony_ci		epf_bar = epf->bar;
156862306a36Sopenharmony_ci	} else {
156962306a36Sopenharmony_ci		func_no = epf->sec_epc_func_no;
157062306a36Sopenharmony_ci		epf_bar = epf->sec_epc_bar;
157162306a36Sopenharmony_ci	}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	ntb_epc->linkup = false;
157462306a36Sopenharmony_ci	ntb_epc->epc = epc;
157562306a36Sopenharmony_ci	ntb_epc->func_no = func_no;
157662306a36Sopenharmony_ci	ntb_epc->vfunc_no = vfunc_no;
157762306a36Sopenharmony_ci	ntb_epc->type = type;
157862306a36Sopenharmony_ci	ntb_epc->epf_bar = epf_bar;
157962306a36Sopenharmony_ci	ntb_epc->epf_ntb = ntb;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	epc_features = pci_epc_get_features(epc, func_no, vfunc_no);
158262306a36Sopenharmony_ci	if (!epc_features)
158362306a36Sopenharmony_ci		return -EINVAL;
158462306a36Sopenharmony_ci	ntb_epc->epc_features = epc_features;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	ntb->epc[type] = ntb_epc;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	return 0;
158962306a36Sopenharmony_ci}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci/**
159262306a36Sopenharmony_ci * epf_ntb_epc_create() - Create and initialize NTB EPC interface
159362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
159462306a36Sopenharmony_ci *
159562306a36Sopenharmony_ci * Get a reference to EPC device and bind NTB function device to that EPC
159662306a36Sopenharmony_ci * for each of the interface. It is also a wrapper to
159762306a36Sopenharmony_ci * epf_ntb_epc_create_interface() to allocate memory for NTB EPC interface
159862306a36Sopenharmony_ci * and initialize it
159962306a36Sopenharmony_ci */
160062306a36Sopenharmony_cistatic int epf_ntb_epc_create(struct epf_ntb *ntb)
160162306a36Sopenharmony_ci{
160262306a36Sopenharmony_ci	struct pci_epf *epf;
160362306a36Sopenharmony_ci	struct device *dev;
160462306a36Sopenharmony_ci	int ret;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	epf = ntb->epf;
160762306a36Sopenharmony_ci	dev = &epf->dev;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	ret = epf_ntb_epc_create_interface(ntb, epf->epc, PRIMARY_INTERFACE);
161062306a36Sopenharmony_ci	if (ret) {
161162306a36Sopenharmony_ci		dev_err(dev, "PRIMARY intf: Fail to create NTB EPC\n");
161262306a36Sopenharmony_ci		return ret;
161362306a36Sopenharmony_ci	}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	ret = epf_ntb_epc_create_interface(ntb, epf->sec_epc,
161662306a36Sopenharmony_ci					   SECONDARY_INTERFACE);
161762306a36Sopenharmony_ci	if (ret) {
161862306a36Sopenharmony_ci		dev_err(dev, "SECONDARY intf: Fail to create NTB EPC\n");
161962306a36Sopenharmony_ci		goto err_epc_create;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	return 0;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_cierr_epc_create:
162562306a36Sopenharmony_ci	epf_ntb_epc_destroy_interface(ntb, PRIMARY_INTERFACE);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	return ret;
162862306a36Sopenharmony_ci}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci/**
163162306a36Sopenharmony_ci * epf_ntb_init_epc_bar_interface() - Identify BARs to be used for each of
163262306a36Sopenharmony_ci *   the NTB constructs (scratchpad region, doorbell, memorywindow)
163362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
163462306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
163562306a36Sopenharmony_ci *
163662306a36Sopenharmony_ci * Identify the free BARs to be used for each of BAR_CONFIG, BAR_PEER_SPAD,
163762306a36Sopenharmony_ci * BAR_DB_MW1, BAR_MW2, BAR_MW3 and BAR_MW4.
163862306a36Sopenharmony_ci */
163962306a36Sopenharmony_cistatic int epf_ntb_init_epc_bar_interface(struct epf_ntb *ntb,
164062306a36Sopenharmony_ci					  enum pci_epc_interface_type type)
164162306a36Sopenharmony_ci{
164262306a36Sopenharmony_ci	const struct pci_epc_features *epc_features;
164362306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
164462306a36Sopenharmony_ci	enum pci_barno barno;
164562306a36Sopenharmony_ci	enum epf_ntb_bar bar;
164662306a36Sopenharmony_ci	struct device *dev;
164762306a36Sopenharmony_ci	u32 num_mws;
164862306a36Sopenharmony_ci	int i;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	barno = BAR_0;
165162306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
165262306a36Sopenharmony_ci	num_mws = ntb->num_mws;
165362306a36Sopenharmony_ci	dev = &ntb->epf->dev;
165462306a36Sopenharmony_ci	epc_features = ntb_epc->epc_features;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	/* These are required BARs which are mandatory for NTB functionality */
165762306a36Sopenharmony_ci	for (bar = BAR_CONFIG; bar <= BAR_DB_MW1; bar++, barno++) {
165862306a36Sopenharmony_ci		barno = pci_epc_get_next_free_bar(epc_features, barno);
165962306a36Sopenharmony_ci		if (barno < 0) {
166062306a36Sopenharmony_ci			dev_err(dev, "%s intf: Fail to get NTB function BAR\n",
166162306a36Sopenharmony_ci				pci_epc_interface_string(type));
166262306a36Sopenharmony_ci			return barno;
166362306a36Sopenharmony_ci		}
166462306a36Sopenharmony_ci		ntb_epc->epf_ntb_bar[bar] = barno;
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	/* These are optional BARs which don't impact NTB functionality */
166862306a36Sopenharmony_ci	for (bar = BAR_MW2, i = 1; i < num_mws; bar++, barno++, i++) {
166962306a36Sopenharmony_ci		barno = pci_epc_get_next_free_bar(epc_features, barno);
167062306a36Sopenharmony_ci		if (barno < 0) {
167162306a36Sopenharmony_ci			ntb->num_mws = i;
167262306a36Sopenharmony_ci			dev_dbg(dev, "BAR not available for > MW%d\n", i + 1);
167362306a36Sopenharmony_ci		}
167462306a36Sopenharmony_ci		ntb_epc->epf_ntb_bar[bar] = barno;
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	return 0;
167862306a36Sopenharmony_ci}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci/**
168162306a36Sopenharmony_ci * epf_ntb_init_epc_bar() - Identify BARs to be used for each of the NTB
168262306a36Sopenharmony_ci * constructs (scratchpad region, doorbell, memorywindow)
168362306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
168462306a36Sopenharmony_ci *
168562306a36Sopenharmony_ci * Wrapper to epf_ntb_init_epc_bar_interface() to identify the free BARs
168662306a36Sopenharmony_ci * to be used for each of BAR_CONFIG, BAR_PEER_SPAD, BAR_DB_MW1, BAR_MW2,
168762306a36Sopenharmony_ci * BAR_MW3 and BAR_MW4 for all the interfaces.
168862306a36Sopenharmony_ci */
168962306a36Sopenharmony_cistatic int epf_ntb_init_epc_bar(struct epf_ntb *ntb)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	enum pci_epc_interface_type type;
169262306a36Sopenharmony_ci	struct device *dev;
169362306a36Sopenharmony_ci	int ret;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	dev = &ntb->epf->dev;
169662306a36Sopenharmony_ci	for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
169762306a36Sopenharmony_ci		ret = epf_ntb_init_epc_bar_interface(ntb, type);
169862306a36Sopenharmony_ci		if (ret) {
169962306a36Sopenharmony_ci			dev_err(dev, "Fail to init EPC bar for %s interface\n",
170062306a36Sopenharmony_ci				pci_epc_interface_string(type));
170162306a36Sopenharmony_ci			return ret;
170262306a36Sopenharmony_ci		}
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	return 0;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci/**
170962306a36Sopenharmony_ci * epf_ntb_epc_init_interface() - Initialize NTB interface
171062306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
171162306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
171262306a36Sopenharmony_ci *
171362306a36Sopenharmony_ci * Wrapper to initialize a particular EPC interface and start the workqueue
171462306a36Sopenharmony_ci * to check for commands from host. This function will write to the
171562306a36Sopenharmony_ci * EP controller HW for configuring it.
171662306a36Sopenharmony_ci */
171762306a36Sopenharmony_cistatic int epf_ntb_epc_init_interface(struct epf_ntb *ntb,
171862306a36Sopenharmony_ci				      enum pci_epc_interface_type type)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
172162306a36Sopenharmony_ci	u8 func_no, vfunc_no;
172262306a36Sopenharmony_ci	struct pci_epc *epc;
172362306a36Sopenharmony_ci	struct pci_epf *epf;
172462306a36Sopenharmony_ci	struct device *dev;
172562306a36Sopenharmony_ci	int ret;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
172862306a36Sopenharmony_ci	epf = ntb->epf;
172962306a36Sopenharmony_ci	dev = &epf->dev;
173062306a36Sopenharmony_ci	epc = ntb_epc->epc;
173162306a36Sopenharmony_ci	func_no = ntb_epc->func_no;
173262306a36Sopenharmony_ci	vfunc_no = ntb_epc->vfunc_no;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	ret = epf_ntb_config_sspad_bar_set(ntb->epc[type]);
173562306a36Sopenharmony_ci	if (ret) {
173662306a36Sopenharmony_ci		dev_err(dev, "%s intf: Config/self SPAD BAR init failed\n",
173762306a36Sopenharmony_ci			pci_epc_interface_string(type));
173862306a36Sopenharmony_ci		return ret;
173962306a36Sopenharmony_ci	}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	ret = epf_ntb_peer_spad_bar_set(ntb, type);
174262306a36Sopenharmony_ci	if (ret) {
174362306a36Sopenharmony_ci		dev_err(dev, "%s intf: Peer SPAD BAR init failed\n",
174462306a36Sopenharmony_ci			pci_epc_interface_string(type));
174562306a36Sopenharmony_ci		goto err_peer_spad_bar_init;
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	ret = epf_ntb_configure_interrupt(ntb, type);
174962306a36Sopenharmony_ci	if (ret) {
175062306a36Sopenharmony_ci		dev_err(dev, "%s intf: Interrupt configuration failed\n",
175162306a36Sopenharmony_ci			pci_epc_interface_string(type));
175262306a36Sopenharmony_ci		goto err_peer_spad_bar_init;
175362306a36Sopenharmony_ci	}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	ret = epf_ntb_db_mw_bar_init(ntb, type);
175662306a36Sopenharmony_ci	if (ret) {
175762306a36Sopenharmony_ci		dev_err(dev, "%s intf: DB/MW BAR init failed\n",
175862306a36Sopenharmony_ci			pci_epc_interface_string(type));
175962306a36Sopenharmony_ci		goto err_db_mw_bar_init;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	if (vfunc_no <= 1) {
176362306a36Sopenharmony_ci		ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header);
176462306a36Sopenharmony_ci		if (ret) {
176562306a36Sopenharmony_ci			dev_err(dev, "%s intf: Configuration header write failed\n",
176662306a36Sopenharmony_ci				pci_epc_interface_string(type));
176762306a36Sopenharmony_ci			goto err_write_header;
176862306a36Sopenharmony_ci		}
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ntb->epc[type]->cmd_handler, epf_ntb_cmd_handler);
177262306a36Sopenharmony_ci	queue_work(kpcintb_workqueue, &ntb->epc[type]->cmd_handler.work);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	return 0;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_cierr_write_header:
177762306a36Sopenharmony_ci	epf_ntb_db_mw_bar_cleanup(ntb, type);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cierr_db_mw_bar_init:
178062306a36Sopenharmony_ci	epf_ntb_peer_spad_bar_clear(ntb->epc[type]);
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cierr_peer_spad_bar_init:
178362306a36Sopenharmony_ci	epf_ntb_config_sspad_bar_clear(ntb->epc[type]);
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	return ret;
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci/**
178962306a36Sopenharmony_ci * epf_ntb_epc_cleanup_interface() - Cleanup NTB interface
179062306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
179162306a36Sopenharmony_ci * @type: PRIMARY interface or SECONDARY interface
179262306a36Sopenharmony_ci *
179362306a36Sopenharmony_ci * Wrapper to cleanup a particular NTB interface.
179462306a36Sopenharmony_ci */
179562306a36Sopenharmony_cistatic void epf_ntb_epc_cleanup_interface(struct epf_ntb *ntb,
179662306a36Sopenharmony_ci					  enum pci_epc_interface_type type)
179762306a36Sopenharmony_ci{
179862306a36Sopenharmony_ci	struct epf_ntb_epc *ntb_epc;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	if (type < 0)
180162306a36Sopenharmony_ci		return;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	ntb_epc = ntb->epc[type];
180462306a36Sopenharmony_ci	cancel_delayed_work(&ntb_epc->cmd_handler);
180562306a36Sopenharmony_ci	epf_ntb_db_mw_bar_cleanup(ntb, type);
180662306a36Sopenharmony_ci	epf_ntb_peer_spad_bar_clear(ntb_epc);
180762306a36Sopenharmony_ci	epf_ntb_config_sspad_bar_clear(ntb_epc);
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci/**
181162306a36Sopenharmony_ci * epf_ntb_epc_cleanup() - Cleanup all NTB interfaces
181262306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
181362306a36Sopenharmony_ci *
181462306a36Sopenharmony_ci * Wrapper to cleanup all NTB interfaces.
181562306a36Sopenharmony_ci */
181662306a36Sopenharmony_cistatic void epf_ntb_epc_cleanup(struct epf_ntb *ntb)
181762306a36Sopenharmony_ci{
181862306a36Sopenharmony_ci	enum pci_epc_interface_type type;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++)
182162306a36Sopenharmony_ci		epf_ntb_epc_cleanup_interface(ntb, type);
182262306a36Sopenharmony_ci}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci/**
182562306a36Sopenharmony_ci * epf_ntb_epc_init() - Initialize all NTB interfaces
182662306a36Sopenharmony_ci * @ntb: NTB device that facilitates communication between HOST1 and HOST2
182762306a36Sopenharmony_ci *
182862306a36Sopenharmony_ci * Wrapper to initialize all NTB interface and start the workqueue
182962306a36Sopenharmony_ci * to check for commands from host.
183062306a36Sopenharmony_ci */
183162306a36Sopenharmony_cistatic int epf_ntb_epc_init(struct epf_ntb *ntb)
183262306a36Sopenharmony_ci{
183362306a36Sopenharmony_ci	enum pci_epc_interface_type type;
183462306a36Sopenharmony_ci	struct device *dev;
183562306a36Sopenharmony_ci	int ret;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	dev = &ntb->epf->dev;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
184062306a36Sopenharmony_ci		ret = epf_ntb_epc_init_interface(ntb, type);
184162306a36Sopenharmony_ci		if (ret) {
184262306a36Sopenharmony_ci			dev_err(dev, "%s intf: Failed to initialize\n",
184362306a36Sopenharmony_ci				pci_epc_interface_string(type));
184462306a36Sopenharmony_ci			goto err_init_type;
184562306a36Sopenharmony_ci		}
184662306a36Sopenharmony_ci	}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	return 0;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_cierr_init_type:
185162306a36Sopenharmony_ci	epf_ntb_epc_cleanup_interface(ntb, type - 1);
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	return ret;
185462306a36Sopenharmony_ci}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci/**
185762306a36Sopenharmony_ci * epf_ntb_bind() - Initialize endpoint controller to provide NTB functionality
185862306a36Sopenharmony_ci * @epf: NTB endpoint function device
185962306a36Sopenharmony_ci *
186062306a36Sopenharmony_ci * Initialize both the endpoint controllers associated with NTB function device.
186162306a36Sopenharmony_ci * Invoked when a primary interface or secondary interface is bound to EPC
186262306a36Sopenharmony_ci * device. This function will succeed only when EPC is bound to both the
186362306a36Sopenharmony_ci * interfaces.
186462306a36Sopenharmony_ci */
186562306a36Sopenharmony_cistatic int epf_ntb_bind(struct pci_epf *epf)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	struct epf_ntb *ntb = epf_get_drvdata(epf);
186862306a36Sopenharmony_ci	struct device *dev = &epf->dev;
186962306a36Sopenharmony_ci	int ret;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	if (!epf->epc) {
187262306a36Sopenharmony_ci		dev_dbg(dev, "PRIMARY EPC interface not yet bound\n");
187362306a36Sopenharmony_ci		return 0;
187462306a36Sopenharmony_ci	}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (!epf->sec_epc) {
187762306a36Sopenharmony_ci		dev_dbg(dev, "SECONDARY EPC interface not yet bound\n");
187862306a36Sopenharmony_ci		return 0;
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	ret = epf_ntb_epc_create(ntb);
188262306a36Sopenharmony_ci	if (ret) {
188362306a36Sopenharmony_ci		dev_err(dev, "Failed to create NTB EPC\n");
188462306a36Sopenharmony_ci		return ret;
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	ret = epf_ntb_init_epc_bar(ntb);
188862306a36Sopenharmony_ci	if (ret) {
188962306a36Sopenharmony_ci		dev_err(dev, "Failed to create NTB EPC\n");
189062306a36Sopenharmony_ci		goto err_bar_init;
189162306a36Sopenharmony_ci	}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	ret = epf_ntb_config_spad_bar_alloc_interface(ntb);
189462306a36Sopenharmony_ci	if (ret) {
189562306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate BAR memory\n");
189662306a36Sopenharmony_ci		goto err_bar_alloc;
189762306a36Sopenharmony_ci	}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	ret = epf_ntb_epc_init(ntb);
190062306a36Sopenharmony_ci	if (ret) {
190162306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize EPC\n");
190262306a36Sopenharmony_ci		goto err_bar_alloc;
190362306a36Sopenharmony_ci	}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	epf_set_drvdata(epf, ntb);
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	return 0;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cierr_bar_alloc:
191062306a36Sopenharmony_ci	epf_ntb_config_spad_bar_free(ntb);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_cierr_bar_init:
191362306a36Sopenharmony_ci	epf_ntb_epc_destroy(ntb);
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	return ret;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci/**
191962306a36Sopenharmony_ci * epf_ntb_unbind() - Cleanup the initialization from epf_ntb_bind()
192062306a36Sopenharmony_ci * @epf: NTB endpoint function device
192162306a36Sopenharmony_ci *
192262306a36Sopenharmony_ci * Cleanup the initialization from epf_ntb_bind()
192362306a36Sopenharmony_ci */
192462306a36Sopenharmony_cistatic void epf_ntb_unbind(struct pci_epf *epf)
192562306a36Sopenharmony_ci{
192662306a36Sopenharmony_ci	struct epf_ntb *ntb = epf_get_drvdata(epf);
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	epf_ntb_epc_cleanup(ntb);
192962306a36Sopenharmony_ci	epf_ntb_config_spad_bar_free(ntb);
193062306a36Sopenharmony_ci	epf_ntb_epc_destroy(ntb);
193162306a36Sopenharmony_ci}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci#define EPF_NTB_R(_name)						\
193462306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_show(struct config_item *item,		\
193562306a36Sopenharmony_ci				      char *page)			\
193662306a36Sopenharmony_ci{									\
193762306a36Sopenharmony_ci	struct config_group *group = to_config_group(item);		\
193862306a36Sopenharmony_ci	struct epf_ntb *ntb = to_epf_ntb(group);			\
193962306a36Sopenharmony_ci									\
194062306a36Sopenharmony_ci	return sysfs_emit(page, "%d\n", ntb->_name);			\
194162306a36Sopenharmony_ci}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci#define EPF_NTB_W(_name)						\
194462306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_store(struct config_item *item,	\
194562306a36Sopenharmony_ci				       const char *page, size_t len)	\
194662306a36Sopenharmony_ci{									\
194762306a36Sopenharmony_ci	struct config_group *group = to_config_group(item);		\
194862306a36Sopenharmony_ci	struct epf_ntb *ntb = to_epf_ntb(group);			\
194962306a36Sopenharmony_ci	u32 val;							\
195062306a36Sopenharmony_ci									\
195162306a36Sopenharmony_ci	if (kstrtou32(page, 0, &val) < 0)				\
195262306a36Sopenharmony_ci		return -EINVAL;						\
195362306a36Sopenharmony_ci									\
195462306a36Sopenharmony_ci	ntb->_name = val;						\
195562306a36Sopenharmony_ci									\
195662306a36Sopenharmony_ci	return len;							\
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci#define EPF_NTB_MW_R(_name)						\
196062306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_show(struct config_item *item,		\
196162306a36Sopenharmony_ci				      char *page)			\
196262306a36Sopenharmony_ci{									\
196362306a36Sopenharmony_ci	struct config_group *group = to_config_group(item);		\
196462306a36Sopenharmony_ci	struct epf_ntb *ntb = to_epf_ntb(group);			\
196562306a36Sopenharmony_ci	int win_no;							\
196662306a36Sopenharmony_ci									\
196762306a36Sopenharmony_ci	sscanf(#_name, "mw%d", &win_no);				\
196862306a36Sopenharmony_ci									\
196962306a36Sopenharmony_ci	return sysfs_emit(page, "%lld\n", ntb->mws_size[win_no - 1]);	\
197062306a36Sopenharmony_ci}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci#define EPF_NTB_MW_W(_name)						\
197362306a36Sopenharmony_cistatic ssize_t epf_ntb_##_name##_store(struct config_item *item,	\
197462306a36Sopenharmony_ci				       const char *page, size_t len)	\
197562306a36Sopenharmony_ci{									\
197662306a36Sopenharmony_ci	struct config_group *group = to_config_group(item);		\
197762306a36Sopenharmony_ci	struct epf_ntb *ntb = to_epf_ntb(group);			\
197862306a36Sopenharmony_ci	struct device *dev = &ntb->epf->dev;				\
197962306a36Sopenharmony_ci	int win_no;							\
198062306a36Sopenharmony_ci	u64 val;							\
198162306a36Sopenharmony_ci									\
198262306a36Sopenharmony_ci	if (kstrtou64(page, 0, &val) < 0)				\
198362306a36Sopenharmony_ci		return -EINVAL;						\
198462306a36Sopenharmony_ci									\
198562306a36Sopenharmony_ci	if (sscanf(#_name, "mw%d", &win_no) != 1)			\
198662306a36Sopenharmony_ci		return -EINVAL;						\
198762306a36Sopenharmony_ci									\
198862306a36Sopenharmony_ci	if (ntb->num_mws < win_no) {					\
198962306a36Sopenharmony_ci		dev_err(dev, "Invalid num_nws: %d value\n", ntb->num_mws); \
199062306a36Sopenharmony_ci		return -EINVAL;						\
199162306a36Sopenharmony_ci	}								\
199262306a36Sopenharmony_ci									\
199362306a36Sopenharmony_ci	ntb->mws_size[win_no - 1] = val;				\
199462306a36Sopenharmony_ci									\
199562306a36Sopenharmony_ci	return len;							\
199662306a36Sopenharmony_ci}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_cistatic ssize_t epf_ntb_num_mws_store(struct config_item *item,
199962306a36Sopenharmony_ci				     const char *page, size_t len)
200062306a36Sopenharmony_ci{
200162306a36Sopenharmony_ci	struct config_group *group = to_config_group(item);
200262306a36Sopenharmony_ci	struct epf_ntb *ntb = to_epf_ntb(group);
200362306a36Sopenharmony_ci	u32 val;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	if (kstrtou32(page, 0, &val) < 0)
200662306a36Sopenharmony_ci		return -EINVAL;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	if (val > MAX_MW)
200962306a36Sopenharmony_ci		return -EINVAL;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	ntb->num_mws = val;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	return len;
201462306a36Sopenharmony_ci}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ciEPF_NTB_R(spad_count)
201762306a36Sopenharmony_ciEPF_NTB_W(spad_count)
201862306a36Sopenharmony_ciEPF_NTB_R(db_count)
201962306a36Sopenharmony_ciEPF_NTB_W(db_count)
202062306a36Sopenharmony_ciEPF_NTB_R(num_mws)
202162306a36Sopenharmony_ciEPF_NTB_MW_R(mw1)
202262306a36Sopenharmony_ciEPF_NTB_MW_W(mw1)
202362306a36Sopenharmony_ciEPF_NTB_MW_R(mw2)
202462306a36Sopenharmony_ciEPF_NTB_MW_W(mw2)
202562306a36Sopenharmony_ciEPF_NTB_MW_R(mw3)
202662306a36Sopenharmony_ciEPF_NTB_MW_W(mw3)
202762306a36Sopenharmony_ciEPF_NTB_MW_R(mw4)
202862306a36Sopenharmony_ciEPF_NTB_MW_W(mw4)
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, spad_count);
203162306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, db_count);
203262306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, num_mws);
203362306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw1);
203462306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw2);
203562306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw3);
203662306a36Sopenharmony_ciCONFIGFS_ATTR(epf_ntb_, mw4);
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cistatic struct configfs_attribute *epf_ntb_attrs[] = {
203962306a36Sopenharmony_ci	&epf_ntb_attr_spad_count,
204062306a36Sopenharmony_ci	&epf_ntb_attr_db_count,
204162306a36Sopenharmony_ci	&epf_ntb_attr_num_mws,
204262306a36Sopenharmony_ci	&epf_ntb_attr_mw1,
204362306a36Sopenharmony_ci	&epf_ntb_attr_mw2,
204462306a36Sopenharmony_ci	&epf_ntb_attr_mw3,
204562306a36Sopenharmony_ci	&epf_ntb_attr_mw4,
204662306a36Sopenharmony_ci	NULL,
204762306a36Sopenharmony_ci};
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_cistatic const struct config_item_type ntb_group_type = {
205062306a36Sopenharmony_ci	.ct_attrs	= epf_ntb_attrs,
205162306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
205262306a36Sopenharmony_ci};
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci/**
205562306a36Sopenharmony_ci * epf_ntb_add_cfs() - Add configfs directory specific to NTB
205662306a36Sopenharmony_ci * @epf: NTB endpoint function device
205762306a36Sopenharmony_ci * @group: A pointer to the config_group structure referencing a group of
205862306a36Sopenharmony_ci *	   config_items of a specific type that belong to a specific sub-system.
205962306a36Sopenharmony_ci *
206062306a36Sopenharmony_ci * Add configfs directory specific to NTB. This directory will hold
206162306a36Sopenharmony_ci * NTB specific properties like db_count, spad_count, num_mws etc.,
206262306a36Sopenharmony_ci */
206362306a36Sopenharmony_cistatic struct config_group *epf_ntb_add_cfs(struct pci_epf *epf,
206462306a36Sopenharmony_ci					    struct config_group *group)
206562306a36Sopenharmony_ci{
206662306a36Sopenharmony_ci	struct epf_ntb *ntb = epf_get_drvdata(epf);
206762306a36Sopenharmony_ci	struct config_group *ntb_group = &ntb->group;
206862306a36Sopenharmony_ci	struct device *dev = &epf->dev;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	config_group_init_type_name(ntb_group, dev_name(dev), &ntb_group_type);
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	return ntb_group;
207362306a36Sopenharmony_ci}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci/**
207662306a36Sopenharmony_ci * epf_ntb_probe() - Probe NTB function driver
207762306a36Sopenharmony_ci * @epf: NTB endpoint function device
207862306a36Sopenharmony_ci * @id: NTB endpoint function device ID
207962306a36Sopenharmony_ci *
208062306a36Sopenharmony_ci * Probe NTB function driver when endpoint function bus detects a NTB
208162306a36Sopenharmony_ci * endpoint function.
208262306a36Sopenharmony_ci */
208362306a36Sopenharmony_cistatic int epf_ntb_probe(struct pci_epf *epf,
208462306a36Sopenharmony_ci			 const struct pci_epf_device_id *id)
208562306a36Sopenharmony_ci{
208662306a36Sopenharmony_ci	struct epf_ntb *ntb;
208762306a36Sopenharmony_ci	struct device *dev;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	dev = &epf->dev;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL);
209262306a36Sopenharmony_ci	if (!ntb)
209362306a36Sopenharmony_ci		return -ENOMEM;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	epf->header = &epf_ntb_header;
209662306a36Sopenharmony_ci	ntb->epf = epf;
209762306a36Sopenharmony_ci	epf_set_drvdata(epf, ntb);
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	return 0;
210062306a36Sopenharmony_ci}
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_cistatic struct pci_epf_ops epf_ntb_ops = {
210362306a36Sopenharmony_ci	.bind	= epf_ntb_bind,
210462306a36Sopenharmony_ci	.unbind	= epf_ntb_unbind,
210562306a36Sopenharmony_ci	.add_cfs = epf_ntb_add_cfs,
210662306a36Sopenharmony_ci};
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_cistatic const struct pci_epf_device_id epf_ntb_ids[] = {
210962306a36Sopenharmony_ci	{
211062306a36Sopenharmony_ci		.name = "pci_epf_ntb",
211162306a36Sopenharmony_ci	},
211262306a36Sopenharmony_ci	{},
211362306a36Sopenharmony_ci};
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_cistatic struct pci_epf_driver epf_ntb_driver = {
211662306a36Sopenharmony_ci	.driver.name	= "pci_epf_ntb",
211762306a36Sopenharmony_ci	.probe		= epf_ntb_probe,
211862306a36Sopenharmony_ci	.id_table	= epf_ntb_ids,
211962306a36Sopenharmony_ci	.ops		= &epf_ntb_ops,
212062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
212162306a36Sopenharmony_ci};
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_cistatic int __init epf_ntb_init(void)
212462306a36Sopenharmony_ci{
212562306a36Sopenharmony_ci	int ret;
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM |
212862306a36Sopenharmony_ci					    WQ_HIGHPRI, 0);
212962306a36Sopenharmony_ci	ret = pci_epf_register_driver(&epf_ntb_driver);
213062306a36Sopenharmony_ci	if (ret) {
213162306a36Sopenharmony_ci		destroy_workqueue(kpcintb_workqueue);
213262306a36Sopenharmony_ci		pr_err("Failed to register pci epf ntb driver --> %d\n", ret);
213362306a36Sopenharmony_ci		return ret;
213462306a36Sopenharmony_ci	}
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	return 0;
213762306a36Sopenharmony_ci}
213862306a36Sopenharmony_cimodule_init(epf_ntb_init);
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_cistatic void __exit epf_ntb_exit(void)
214162306a36Sopenharmony_ci{
214262306a36Sopenharmony_ci	pci_epf_unregister_driver(&epf_ntb_driver);
214362306a36Sopenharmony_ci	destroy_workqueue(kpcintb_workqueue);
214462306a36Sopenharmony_ci}
214562306a36Sopenharmony_cimodule_exit(epf_ntb_exit);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ciMODULE_DESCRIPTION("PCI EPF NTB DRIVER");
214862306a36Sopenharmony_ciMODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
214962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2150