162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Microsemi Switchtec(tm) PCIe Management Driver
462306a36Sopenharmony_ci * Copyright (c) 2017, Microsemi Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/interrupt.h>
862306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/kthread.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/ntb.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/switchtec.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciMODULE_DESCRIPTION("Microsemi Switchtec(tm) NTB Driver");
1762306a36Sopenharmony_ciMODULE_VERSION("0.1");
1862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1962306a36Sopenharmony_ciMODULE_AUTHOR("Microsemi Corporation");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic ulong max_mw_size = SZ_2M;
2262306a36Sopenharmony_cimodule_param(max_mw_size, ulong, 0644);
2362306a36Sopenharmony_ciMODULE_PARM_DESC(max_mw_size,
2462306a36Sopenharmony_ci	"Max memory window size reported to the upper layer");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic bool use_lut_mws;
2762306a36Sopenharmony_cimodule_param(use_lut_mws, bool, 0644);
2862306a36Sopenharmony_ciMODULE_PARM_DESC(use_lut_mws,
2962306a36Sopenharmony_ci		 "Enable the use of the LUT based memory windows");
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define SWITCHTEC_NTB_MAGIC 0x45CC0001
3262306a36Sopenharmony_ci#define MAX_MWS     128
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct shared_mw {
3562306a36Sopenharmony_ci	u32 magic;
3662306a36Sopenharmony_ci	u32 link_sta;
3762306a36Sopenharmony_ci	u32 partition_id;
3862306a36Sopenharmony_ci	u64 mw_sizes[MAX_MWS];
3962306a36Sopenharmony_ci	u32 spad[128];
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define MAX_DIRECT_MW ARRAY_SIZE(((struct ntb_ctrl_regs *)(0))->bar_entry)
4362306a36Sopenharmony_ci#define LUT_SIZE SZ_64K
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct switchtec_ntb {
4662306a36Sopenharmony_ci	struct ntb_dev ntb;
4762306a36Sopenharmony_ci	struct switchtec_dev *stdev;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	int self_partition;
5062306a36Sopenharmony_ci	int peer_partition;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	int doorbell_irq;
5362306a36Sopenharmony_ci	int message_irq;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	struct ntb_info_regs __iomem *mmio_ntb;
5662306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *mmio_ctrl;
5762306a36Sopenharmony_ci	struct ntb_dbmsg_regs __iomem *mmio_dbmsg;
5862306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *mmio_self_ctrl;
5962306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *mmio_peer_ctrl;
6062306a36Sopenharmony_ci	struct ntb_dbmsg_regs __iomem *mmio_self_dbmsg;
6162306a36Sopenharmony_ci	struct ntb_dbmsg_regs __iomem *mmio_peer_dbmsg;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	void __iomem *mmio_xlink_win;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	struct shared_mw *self_shared;
6662306a36Sopenharmony_ci	struct shared_mw __iomem *peer_shared;
6762306a36Sopenharmony_ci	dma_addr_t self_shared_dma;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	u64 db_mask;
7062306a36Sopenharmony_ci	u64 db_valid_mask;
7162306a36Sopenharmony_ci	int db_shift;
7262306a36Sopenharmony_ci	int db_peer_shift;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* synchronize rmw access of db_mask and hw reg */
7562306a36Sopenharmony_ci	spinlock_t db_mask_lock;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	int nr_direct_mw;
7862306a36Sopenharmony_ci	int nr_lut_mw;
7962306a36Sopenharmony_ci	int nr_rsvd_luts;
8062306a36Sopenharmony_ci	int direct_mw_to_bar[MAX_DIRECT_MW];
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	int peer_nr_direct_mw;
8362306a36Sopenharmony_ci	int peer_nr_lut_mw;
8462306a36Sopenharmony_ci	int peer_direct_mw_to_bar[MAX_DIRECT_MW];
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	bool link_is_up;
8762306a36Sopenharmony_ci	enum ntb_speed link_speed;
8862306a36Sopenharmony_ci	enum ntb_width link_width;
8962306a36Sopenharmony_ci	struct work_struct check_link_status_work;
9062306a36Sopenharmony_ci	bool link_force_down;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic struct switchtec_ntb *ntb_sndev(struct ntb_dev *ntb)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	return container_of(ntb, struct switchtec_ntb, ntb);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int switchtec_ntb_part_op(struct switchtec_ntb *sndev,
9962306a36Sopenharmony_ci				 struct ntb_ctrl_regs __iomem *ctl,
10062306a36Sopenharmony_ci				 u32 op, int wait_status)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	static const char * const op_text[] = {
10362306a36Sopenharmony_ci		[NTB_CTRL_PART_OP_LOCK] = "lock",
10462306a36Sopenharmony_ci		[NTB_CTRL_PART_OP_CFG] = "configure",
10562306a36Sopenharmony_ci		[NTB_CTRL_PART_OP_RESET] = "reset",
10662306a36Sopenharmony_ci	};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	int i;
10962306a36Sopenharmony_ci	u32 ps;
11062306a36Sopenharmony_ci	int status;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	switch (op) {
11362306a36Sopenharmony_ci	case NTB_CTRL_PART_OP_LOCK:
11462306a36Sopenharmony_ci		status = NTB_CTRL_PART_STATUS_LOCKING;
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case NTB_CTRL_PART_OP_CFG:
11762306a36Sopenharmony_ci		status = NTB_CTRL_PART_STATUS_CONFIGURING;
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	case NTB_CTRL_PART_OP_RESET:
12062306a36Sopenharmony_ci		status = NTB_CTRL_PART_STATUS_RESETTING;
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	default:
12362306a36Sopenharmony_ci		return -EINVAL;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	iowrite32(op, &ctl->partition_op);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	for (i = 0; i < 1000; i++) {
12962306a36Sopenharmony_ci		if (msleep_interruptible(50) != 0) {
13062306a36Sopenharmony_ci			iowrite32(NTB_CTRL_PART_OP_RESET, &ctl->partition_op);
13162306a36Sopenharmony_ci			return -EINTR;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		ps = ioread32(&ctl->partition_status) & 0xFFFF;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		if (ps != status)
13762306a36Sopenharmony_ci			break;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (ps == wait_status)
14162306a36Sopenharmony_ci		return 0;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (ps == status) {
14462306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
14562306a36Sopenharmony_ci			"Timed out while performing %s (%d). (%08x)\n",
14662306a36Sopenharmony_ci			op_text[op], op,
14762306a36Sopenharmony_ci			ioread32(&ctl->partition_status));
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		return -ETIMEDOUT;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return -EIO;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int switchtec_ntb_send_msg(struct switchtec_ntb *sndev, int idx,
15662306a36Sopenharmony_ci				  u32 val)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	if (idx < 0 || idx >= ARRAY_SIZE(sndev->mmio_peer_dbmsg->omsg))
15962306a36Sopenharmony_ci		return -EINVAL;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	iowrite32(val, &sndev->mmio_peer_dbmsg->omsg[idx].msg);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int switchtec_ntb_mw_count(struct ntb_dev *ntb, int pidx)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
16962306a36Sopenharmony_ci	int nr_direct_mw = sndev->peer_nr_direct_mw;
17062306a36Sopenharmony_ci	int nr_lut_mw = sndev->peer_nr_lut_mw - sndev->nr_rsvd_luts;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (pidx != NTB_DEF_PEER_IDX)
17362306a36Sopenharmony_ci		return -EINVAL;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!use_lut_mws)
17662306a36Sopenharmony_ci		nr_lut_mw = 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return nr_direct_mw + nr_lut_mw;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int lut_index(struct switchtec_ntb *sndev, int mw_idx)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	return mw_idx - sndev->nr_direct_mw + sndev->nr_rsvd_luts;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int peer_lut_index(struct switchtec_ntb *sndev, int mw_idx)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	return mw_idx - sndev->peer_nr_direct_mw + sndev->nr_rsvd_luts;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int switchtec_ntb_mw_get_align(struct ntb_dev *ntb, int pidx,
19262306a36Sopenharmony_ci				      int widx, resource_size_t *addr_align,
19362306a36Sopenharmony_ci				      resource_size_t *size_align,
19462306a36Sopenharmony_ci				      resource_size_t *size_max)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
19762306a36Sopenharmony_ci	int lut;
19862306a36Sopenharmony_ci	resource_size_t size;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (pidx != NTB_DEF_PEER_IDX)
20162306a36Sopenharmony_ci		return -EINVAL;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	lut = widx >= sndev->peer_nr_direct_mw;
20462306a36Sopenharmony_ci	size = ioread64(&sndev->peer_shared->mw_sizes[widx]);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (size == 0)
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (addr_align)
21062306a36Sopenharmony_ci		*addr_align = lut ? size : SZ_4K;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (size_align)
21362306a36Sopenharmony_ci		*size_align = lut ? size : SZ_4K;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (size_max)
21662306a36Sopenharmony_ci		*size_max = size;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return 0;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
22462306a36Sopenharmony_ci	int bar = sndev->peer_direct_mw_to_bar[idx];
22562306a36Sopenharmony_ci	u32 ctl_val;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
22862306a36Sopenharmony_ci	ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN;
22962306a36Sopenharmony_ci	iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
23062306a36Sopenharmony_ci	iowrite32(0, &ctl->bar_entry[bar].win_size);
23162306a36Sopenharmony_ci	iowrite32(0, &ctl->bar_ext_entry[bar].win_size);
23262306a36Sopenharmony_ci	iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void switchtec_ntb_mw_clr_lut(struct switchtec_ntb *sndev, int idx)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	iowrite64(0, &ctl->lut_entry[peer_lut_index(sndev, idx)]);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx,
24362306a36Sopenharmony_ci					dma_addr_t addr, resource_size_t size)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	int xlate_pos = ilog2(size);
24662306a36Sopenharmony_ci	int bar = sndev->peer_direct_mw_to_bar[idx];
24762306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
24862306a36Sopenharmony_ci	u32 ctl_val;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
25162306a36Sopenharmony_ci	ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
25462306a36Sopenharmony_ci	iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000),
25562306a36Sopenharmony_ci		  &ctl->bar_entry[bar].win_size);
25662306a36Sopenharmony_ci	iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size);
25762306a36Sopenharmony_ci	iowrite64(sndev->self_partition | addr,
25862306a36Sopenharmony_ci		  &ctl->bar_entry[bar].xlate_addr);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic void switchtec_ntb_mw_set_lut(struct switchtec_ntb *sndev, int idx,
26262306a36Sopenharmony_ci				     dma_addr_t addr, resource_size_t size)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | addr),
26762306a36Sopenharmony_ci		  &ctl->lut_entry[peer_lut_index(sndev, idx)]);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx,
27162306a36Sopenharmony_ci				      dma_addr_t addr, resource_size_t size)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
27462306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
27562306a36Sopenharmony_ci	int xlate_pos = ilog2(size);
27662306a36Sopenharmony_ci	int nr_direct_mw = sndev->peer_nr_direct_mw;
27762306a36Sopenharmony_ci	int rc;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (pidx != NTB_DEF_PEER_IDX)
28062306a36Sopenharmony_ci		return -EINVAL;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "MW %d: part %d addr %pad size %pap\n",
28362306a36Sopenharmony_ci		widx, pidx, &addr, &size);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (widx >= switchtec_ntb_mw_count(ntb, pidx))
28662306a36Sopenharmony_ci		return -EINVAL;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (size != 0 && xlate_pos < 12)
28962306a36Sopenharmony_ci		return -EINVAL;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (!IS_ALIGNED(addr, BIT_ULL(xlate_pos))) {
29262306a36Sopenharmony_ci		/*
29362306a36Sopenharmony_ci		 * In certain circumstances we can get a buffer that is
29462306a36Sopenharmony_ci		 * not aligned to its size. (Most of the time
29562306a36Sopenharmony_ci		 * dma_alloc_coherent ensures this). This can happen when
29662306a36Sopenharmony_ci		 * using large buffers allocated by the CMA
29762306a36Sopenharmony_ci		 * (see CMA_CONFIG_ALIGNMENT)
29862306a36Sopenharmony_ci		 */
29962306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
30062306a36Sopenharmony_ci			"ERROR: Memory window address is not aligned to its size!\n");
30162306a36Sopenharmony_ci		return -EINVAL;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK,
30562306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_LOCKED);
30662306a36Sopenharmony_ci	if (rc)
30762306a36Sopenharmony_ci		return rc;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (size == 0) {
31062306a36Sopenharmony_ci		if (widx < nr_direct_mw)
31162306a36Sopenharmony_ci			switchtec_ntb_mw_clr_direct(sndev, widx);
31262306a36Sopenharmony_ci		else
31362306a36Sopenharmony_ci			switchtec_ntb_mw_clr_lut(sndev, widx);
31462306a36Sopenharmony_ci	} else {
31562306a36Sopenharmony_ci		if (widx < nr_direct_mw)
31662306a36Sopenharmony_ci			switchtec_ntb_mw_set_direct(sndev, widx, addr, size);
31762306a36Sopenharmony_ci		else
31862306a36Sopenharmony_ci			switchtec_ntb_mw_set_lut(sndev, widx, addr, size);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
32262306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_NORMAL);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (rc == -EIO) {
32562306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
32662306a36Sopenharmony_ci			"Hardware reported an error configuring mw %d: %08x\n",
32762306a36Sopenharmony_ci			widx, ioread32(&ctl->bar_error));
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		if (widx < nr_direct_mw)
33062306a36Sopenharmony_ci			switchtec_ntb_mw_clr_direct(sndev, widx);
33162306a36Sopenharmony_ci		else
33262306a36Sopenharmony_ci			switchtec_ntb_mw_clr_lut(sndev, widx);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
33562306a36Sopenharmony_ci				      NTB_CTRL_PART_STATUS_NORMAL);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return rc;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int switchtec_ntb_peer_mw_count(struct ntb_dev *ntb)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
34462306a36Sopenharmony_ci	int nr_lut_mw = sndev->nr_lut_mw - sndev->nr_rsvd_luts;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	return sndev->nr_direct_mw + (use_lut_mws ? nr_lut_mw : 0);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int switchtec_ntb_direct_get_addr(struct switchtec_ntb *sndev,
35062306a36Sopenharmony_ci					 int idx, phys_addr_t *base,
35162306a36Sopenharmony_ci					 resource_size_t *size)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	int bar = sndev->direct_mw_to_bar[idx];
35462306a36Sopenharmony_ci	size_t offset = 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (bar < 0)
35762306a36Sopenharmony_ci		return -EINVAL;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (idx == 0) {
36062306a36Sopenharmony_ci		/*
36162306a36Sopenharmony_ci		 * This is the direct BAR shared with the LUTs
36262306a36Sopenharmony_ci		 * which means the actual window will be offset
36362306a36Sopenharmony_ci		 * by the size of all the LUT entries.
36462306a36Sopenharmony_ci		 */
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		offset = LUT_SIZE * sndev->nr_lut_mw;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (base)
37062306a36Sopenharmony_ci		*base = pci_resource_start(sndev->ntb.pdev, bar) + offset;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (size) {
37362306a36Sopenharmony_ci		*size = pci_resource_len(sndev->ntb.pdev, bar) - offset;
37462306a36Sopenharmony_ci		if (offset && *size > offset)
37562306a36Sopenharmony_ci			*size = offset;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		if (*size > max_mw_size)
37862306a36Sopenharmony_ci			*size = max_mw_size;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int switchtec_ntb_lut_get_addr(struct switchtec_ntb *sndev,
38562306a36Sopenharmony_ci				      int idx, phys_addr_t *base,
38662306a36Sopenharmony_ci				      resource_size_t *size)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	int bar = sndev->direct_mw_to_bar[0];
38962306a36Sopenharmony_ci	int offset;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	offset = LUT_SIZE * lut_index(sndev, idx);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (base)
39462306a36Sopenharmony_ci		*base = pci_resource_start(sndev->ntb.pdev, bar) + offset;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (size)
39762306a36Sopenharmony_ci		*size = LUT_SIZE;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int switchtec_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
40362306a36Sopenharmony_ci					  phys_addr_t *base,
40462306a36Sopenharmony_ci					  resource_size_t *size)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (idx < sndev->nr_direct_mw)
40962306a36Sopenharmony_ci		return switchtec_ntb_direct_get_addr(sndev, idx, base, size);
41062306a36Sopenharmony_ci	else if (idx < switchtec_ntb_peer_mw_count(ntb))
41162306a36Sopenharmony_ci		return switchtec_ntb_lut_get_addr(sndev, idx, base, size);
41262306a36Sopenharmony_ci	else
41362306a36Sopenharmony_ci		return -EINVAL;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic void switchtec_ntb_part_link_speed(struct switchtec_ntb *sndev,
41762306a36Sopenharmony_ci					  int partition,
41862306a36Sopenharmony_ci					  enum ntb_speed *speed,
41962306a36Sopenharmony_ci					  enum ntb_width *width)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct switchtec_dev *stdev = sndev->stdev;
42262306a36Sopenharmony_ci	struct part_cfg_regs __iomem *part_cfg =
42362306a36Sopenharmony_ci		&stdev->mmio_part_cfg_all[partition];
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	u32 pff = ioread32(&part_cfg->vep_pff_inst_id) & 0xFF;
42662306a36Sopenharmony_ci	u32 linksta = ioread32(&stdev->mmio_pff_csr[pff].pci_cap_region[13]);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (speed)
42962306a36Sopenharmony_ci		*speed = (linksta >> 16) & 0xF;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (width)
43262306a36Sopenharmony_ci		*width = (linksta >> 20) & 0x3F;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void switchtec_ntb_set_link_speed(struct switchtec_ntb *sndev)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	enum ntb_speed self_speed, peer_speed;
43862306a36Sopenharmony_ci	enum ntb_width self_width, peer_width;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!sndev->link_is_up) {
44162306a36Sopenharmony_ci		sndev->link_speed = NTB_SPEED_NONE;
44262306a36Sopenharmony_ci		sndev->link_width = NTB_WIDTH_NONE;
44362306a36Sopenharmony_ci		return;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	switchtec_ntb_part_link_speed(sndev, sndev->self_partition,
44762306a36Sopenharmony_ci				      &self_speed, &self_width);
44862306a36Sopenharmony_ci	switchtec_ntb_part_link_speed(sndev, sndev->peer_partition,
44962306a36Sopenharmony_ci				      &peer_speed, &peer_width);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	sndev->link_speed = min(self_speed, peer_speed);
45262306a36Sopenharmony_ci	sndev->link_width = min(self_width, peer_width);
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int crosslink_is_enabled(struct switchtec_ntb *sndev)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct ntb_info_regs __iomem *inf = sndev->mmio_ntb;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return ioread8(&inf->ntp_info[sndev->peer_partition].xlink_enabled);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void crosslink_init_dbmsgs(struct switchtec_ntb *sndev)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int i;
46562306a36Sopenharmony_ci	u32 msg_map = 0;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (!crosslink_is_enabled(sndev))
46862306a36Sopenharmony_ci		return;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sndev->mmio_peer_dbmsg->imsg); i++) {
47162306a36Sopenharmony_ci		int m = i | sndev->self_partition << 2;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		msg_map |= m << i * 8;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	iowrite32(msg_map, &sndev->mmio_peer_dbmsg->msg_map);
47762306a36Sopenharmony_ci	iowrite64(sndev->db_valid_mask << sndev->db_peer_shift,
47862306a36Sopenharmony_ci		  &sndev->mmio_peer_dbmsg->odb_mask);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cienum switchtec_msg {
48262306a36Sopenharmony_ci	LINK_MESSAGE = 0,
48362306a36Sopenharmony_ci	MSG_LINK_UP = 1,
48462306a36Sopenharmony_ci	MSG_LINK_DOWN = 2,
48562306a36Sopenharmony_ci	MSG_CHECK_LINK = 3,
48662306a36Sopenharmony_ci	MSG_LINK_FORCE_DOWN = 4,
48762306a36Sopenharmony_ci};
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int switchtec_ntb_reinit_peer(struct switchtec_ntb *sndev);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic void switchtec_ntb_link_status_update(struct switchtec_ntb *sndev)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int link_sta;
49462306a36Sopenharmony_ci	int old = sndev->link_is_up;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	link_sta = sndev->self_shared->link_sta;
49762306a36Sopenharmony_ci	if (link_sta) {
49862306a36Sopenharmony_ci		u64 peer = ioread64(&sndev->peer_shared->magic);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		if ((peer & 0xFFFFFFFF) == SWITCHTEC_NTB_MAGIC)
50162306a36Sopenharmony_ci			link_sta = peer >> 32;
50262306a36Sopenharmony_ci		else
50362306a36Sopenharmony_ci			link_sta = 0;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	sndev->link_is_up = link_sta;
50762306a36Sopenharmony_ci	switchtec_ntb_set_link_speed(sndev);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (link_sta != old) {
51062306a36Sopenharmony_ci		switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_CHECK_LINK);
51162306a36Sopenharmony_ci		ntb_link_event(&sndev->ntb);
51262306a36Sopenharmony_ci		dev_info(&sndev->stdev->dev, "ntb link %s\n",
51362306a36Sopenharmony_ci			 link_sta ? "up" : "down");
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		if (link_sta)
51662306a36Sopenharmony_ci			crosslink_init_dbmsgs(sndev);
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void check_link_status_work(struct work_struct *work)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct switchtec_ntb *sndev;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	sndev = container_of(work, struct switchtec_ntb,
52562306a36Sopenharmony_ci			     check_link_status_work);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (sndev->link_force_down) {
52862306a36Sopenharmony_ci		sndev->link_force_down = false;
52962306a36Sopenharmony_ci		switchtec_ntb_reinit_peer(sndev);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		if (sndev->link_is_up) {
53262306a36Sopenharmony_ci			sndev->link_is_up = 0;
53362306a36Sopenharmony_ci			ntb_link_event(&sndev->ntb);
53462306a36Sopenharmony_ci			dev_info(&sndev->stdev->dev, "ntb link forced down\n");
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		return;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	switchtec_ntb_link_status_update(sndev);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic void switchtec_ntb_check_link(struct switchtec_ntb *sndev,
54462306a36Sopenharmony_ci				      enum switchtec_msg msg)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	if (msg == MSG_LINK_FORCE_DOWN)
54762306a36Sopenharmony_ci		sndev->link_force_down = true;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	schedule_work(&sndev->check_link_status_work);
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic void switchtec_ntb_link_notification(struct switchtec_dev *stdev)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct switchtec_ntb *sndev = stdev->sndev;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	switchtec_ntb_check_link(sndev, MSG_CHECK_LINK);
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic u64 switchtec_ntb_link_is_up(struct ntb_dev *ntb,
56062306a36Sopenharmony_ci				    enum ntb_speed *speed,
56162306a36Sopenharmony_ci				    enum ntb_width *width)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (speed)
56662306a36Sopenharmony_ci		*speed = sndev->link_speed;
56762306a36Sopenharmony_ci	if (width)
56862306a36Sopenharmony_ci		*width = sndev->link_width;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return sndev->link_is_up;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int switchtec_ntb_link_enable(struct ntb_dev *ntb,
57462306a36Sopenharmony_ci				     enum ntb_speed max_speed,
57562306a36Sopenharmony_ci				     enum ntb_width max_width)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "enabling link\n");
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	sndev->self_shared->link_sta = 1;
58262306a36Sopenharmony_ci	switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	switchtec_ntb_link_status_update(sndev);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int switchtec_ntb_link_disable(struct ntb_dev *ntb)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "disabling link\n");
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	sndev->self_shared->link_sta = 0;
59662306a36Sopenharmony_ci	switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_DOWN);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	switchtec_ntb_link_status_update(sndev);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return 0;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic u64 switchtec_ntb_db_valid_mask(struct ntb_dev *ntb)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return sndev->db_valid_mask;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic int switchtec_ntb_db_vector_count(struct ntb_dev *ntb)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	return 1;
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic u64 switchtec_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (db_vector < 0 || db_vector > 1)
62062306a36Sopenharmony_ci		return 0;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return sndev->db_valid_mask;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic u64 switchtec_ntb_db_read(struct ntb_dev *ntb)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	u64 ret;
62862306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	ret = ioread64(&sndev->mmio_self_dbmsg->idb) >> sndev->db_shift;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return ret & sndev->db_valid_mask;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int switchtec_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	iowrite64(db_bits << sndev->db_shift, &sndev->mmio_self_dbmsg->idb);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return 0;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int switchtec_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	unsigned long irqflags;
64762306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (db_bits & ~sndev->db_valid_mask)
65062306a36Sopenharmony_ci		return -EINVAL;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	spin_lock_irqsave(&sndev->db_mask_lock, irqflags);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	sndev->db_mask |= db_bits << sndev->db_shift;
65562306a36Sopenharmony_ci	iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return 0;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic int switchtec_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	unsigned long irqflags;
66562306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (db_bits & ~sndev->db_valid_mask)
66862306a36Sopenharmony_ci		return -EINVAL;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	spin_lock_irqsave(&sndev->db_mask_lock, irqflags);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	sndev->db_mask &= ~(db_bits << sndev->db_shift);
67362306a36Sopenharmony_ci	iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return 0;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic u64 switchtec_ntb_db_read_mask(struct ntb_dev *ntb)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	return (sndev->db_mask >> sndev->db_shift) & sndev->db_valid_mask;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb,
68862306a36Sopenharmony_ci				      phys_addr_t *db_addr,
68962306a36Sopenharmony_ci				      resource_size_t *db_size,
69062306a36Sopenharmony_ci				      u64 *db_data,
69162306a36Sopenharmony_ci				      int db_bit)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
69462306a36Sopenharmony_ci	unsigned long offset;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (unlikely(db_bit >= BITS_PER_LONG_LONG))
69762306a36Sopenharmony_ci		return -EINVAL;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	offset = (unsigned long)sndev->mmio_peer_dbmsg->odb -
70062306a36Sopenharmony_ci		(unsigned long)sndev->stdev->mmio;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	offset += sndev->db_shift / 8;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (db_addr)
70562306a36Sopenharmony_ci		*db_addr = pci_resource_start(ntb->pdev, 0) + offset;
70662306a36Sopenharmony_ci	if (db_size)
70762306a36Sopenharmony_ci		*db_size = sizeof(u32);
70862306a36Sopenharmony_ci	if (db_data)
70962306a36Sopenharmony_ci		*db_data = BIT_ULL(db_bit) << sndev->db_peer_shift;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	return 0;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic int switchtec_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	iowrite64(db_bits << sndev->db_peer_shift,
71962306a36Sopenharmony_ci		  &sndev->mmio_peer_dbmsg->odb);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	return 0;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int switchtec_ntb_spad_count(struct ntb_dev *ntb)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	return ARRAY_SIZE(sndev->self_shared->spad);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic u32 switchtec_ntb_spad_read(struct ntb_dev *ntb, int idx)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad))
73662306a36Sopenharmony_ci		return 0;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!sndev->self_shared)
73962306a36Sopenharmony_ci		return 0;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	return sndev->self_shared->spad[idx];
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic int switchtec_ntb_spad_write(struct ntb_dev *ntb, int idx, u32 val)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad))
74962306a36Sopenharmony_ci		return -EINVAL;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (!sndev->self_shared)
75262306a36Sopenharmony_ci		return -EIO;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	sndev->self_shared->spad[idx] = val;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return 0;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic u32 switchtec_ntb_peer_spad_read(struct ntb_dev *ntb, int pidx,
76062306a36Sopenharmony_ci					int sidx)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (pidx != NTB_DEF_PEER_IDX)
76562306a36Sopenharmony_ci		return -EINVAL;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad))
76862306a36Sopenharmony_ci		return 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (!sndev->peer_shared)
77162306a36Sopenharmony_ci		return 0;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return ioread32(&sndev->peer_shared->spad[sidx]);
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic int switchtec_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx,
77762306a36Sopenharmony_ci					 int sidx, u32 val)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (pidx != NTB_DEF_PEER_IDX)
78262306a36Sopenharmony_ci		return -EINVAL;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad))
78562306a36Sopenharmony_ci		return -EINVAL;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (!sndev->peer_shared)
78862306a36Sopenharmony_ci		return -EIO;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	iowrite32(val, &sndev->peer_shared->spad[sidx]);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	return 0;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic int switchtec_ntb_peer_spad_addr(struct ntb_dev *ntb, int pidx,
79662306a36Sopenharmony_ci					int sidx, phys_addr_t *spad_addr)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	struct switchtec_ntb *sndev = ntb_sndev(ntb);
79962306a36Sopenharmony_ci	unsigned long offset;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	if (pidx != NTB_DEF_PEER_IDX)
80262306a36Sopenharmony_ci		return -EINVAL;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	offset = (unsigned long)&sndev->peer_shared->spad[sidx] -
80562306a36Sopenharmony_ci		(unsigned long)sndev->stdev->mmio;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (spad_addr)
80862306a36Sopenharmony_ci		*spad_addr = pci_resource_start(ntb->pdev, 0) + offset;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	return 0;
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic const struct ntb_dev_ops switchtec_ntb_ops = {
81462306a36Sopenharmony_ci	.mw_count		= switchtec_ntb_mw_count,
81562306a36Sopenharmony_ci	.mw_get_align		= switchtec_ntb_mw_get_align,
81662306a36Sopenharmony_ci	.mw_set_trans		= switchtec_ntb_mw_set_trans,
81762306a36Sopenharmony_ci	.peer_mw_count		= switchtec_ntb_peer_mw_count,
81862306a36Sopenharmony_ci	.peer_mw_get_addr	= switchtec_ntb_peer_mw_get_addr,
81962306a36Sopenharmony_ci	.link_is_up		= switchtec_ntb_link_is_up,
82062306a36Sopenharmony_ci	.link_enable		= switchtec_ntb_link_enable,
82162306a36Sopenharmony_ci	.link_disable		= switchtec_ntb_link_disable,
82262306a36Sopenharmony_ci	.db_valid_mask		= switchtec_ntb_db_valid_mask,
82362306a36Sopenharmony_ci	.db_vector_count	= switchtec_ntb_db_vector_count,
82462306a36Sopenharmony_ci	.db_vector_mask		= switchtec_ntb_db_vector_mask,
82562306a36Sopenharmony_ci	.db_read		= switchtec_ntb_db_read,
82662306a36Sopenharmony_ci	.db_clear		= switchtec_ntb_db_clear,
82762306a36Sopenharmony_ci	.db_set_mask		= switchtec_ntb_db_set_mask,
82862306a36Sopenharmony_ci	.db_clear_mask		= switchtec_ntb_db_clear_mask,
82962306a36Sopenharmony_ci	.db_read_mask		= switchtec_ntb_db_read_mask,
83062306a36Sopenharmony_ci	.peer_db_addr		= switchtec_ntb_peer_db_addr,
83162306a36Sopenharmony_ci	.peer_db_set		= switchtec_ntb_peer_db_set,
83262306a36Sopenharmony_ci	.spad_count		= switchtec_ntb_spad_count,
83362306a36Sopenharmony_ci	.spad_read		= switchtec_ntb_spad_read,
83462306a36Sopenharmony_ci	.spad_write		= switchtec_ntb_spad_write,
83562306a36Sopenharmony_ci	.peer_spad_read		= switchtec_ntb_peer_spad_read,
83662306a36Sopenharmony_ci	.peer_spad_write	= switchtec_ntb_peer_spad_write,
83762306a36Sopenharmony_ci	.peer_spad_addr		= switchtec_ntb_peer_spad_addr,
83862306a36Sopenharmony_ci};
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic int switchtec_ntb_init_sndev(struct switchtec_ntb *sndev)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	u64 tpart_vec;
84362306a36Sopenharmony_ci	int self;
84462306a36Sopenharmony_ci	u64 part_map;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	sndev->ntb.pdev = sndev->stdev->pdev;
84762306a36Sopenharmony_ci	sndev->ntb.topo = NTB_TOPO_SWITCH;
84862306a36Sopenharmony_ci	sndev->ntb.ops = &switchtec_ntb_ops;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	INIT_WORK(&sndev->check_link_status_work, check_link_status_work);
85162306a36Sopenharmony_ci	sndev->link_force_down = false;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	sndev->self_partition = sndev->stdev->partition;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	sndev->mmio_ntb = sndev->stdev->mmio_ntb;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	self = sndev->self_partition;
85862306a36Sopenharmony_ci	tpart_vec = ioread32(&sndev->mmio_ntb->ntp_info[self].target_part_high);
85962306a36Sopenharmony_ci	tpart_vec <<= 32;
86062306a36Sopenharmony_ci	tpart_vec |= ioread32(&sndev->mmio_ntb->ntp_info[self].target_part_low);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	part_map = ioread64(&sndev->mmio_ntb->ep_map);
86362306a36Sopenharmony_ci	tpart_vec &= part_map;
86462306a36Sopenharmony_ci	part_map &= ~(1 << sndev->self_partition);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (!tpart_vec) {
86762306a36Sopenharmony_ci		if (sndev->stdev->partition_count != 2) {
86862306a36Sopenharmony_ci			dev_err(&sndev->stdev->dev,
86962306a36Sopenharmony_ci				"ntb target partition not defined\n");
87062306a36Sopenharmony_ci			return -ENODEV;
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci		if (!part_map) {
87462306a36Sopenharmony_ci			dev_err(&sndev->stdev->dev,
87562306a36Sopenharmony_ci				"peer partition is not NT partition\n");
87662306a36Sopenharmony_ci			return -ENODEV;
87762306a36Sopenharmony_ci		}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		sndev->peer_partition = __ffs64(part_map);
88062306a36Sopenharmony_ci	} else {
88162306a36Sopenharmony_ci		if (__ffs64(tpart_vec) != (fls64(tpart_vec) - 1)) {
88262306a36Sopenharmony_ci			dev_err(&sndev->stdev->dev,
88362306a36Sopenharmony_ci				"ntb driver only supports 1 pair of 1-1 ntb mapping\n");
88462306a36Sopenharmony_ci			return -ENODEV;
88562306a36Sopenharmony_ci		}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		sndev->peer_partition = __ffs64(tpart_vec);
88862306a36Sopenharmony_ci		if (!(part_map & (1ULL << sndev->peer_partition))) {
88962306a36Sopenharmony_ci			dev_err(&sndev->stdev->dev,
89062306a36Sopenharmony_ci				"ntb target partition is not NT partition\n");
89162306a36Sopenharmony_ci			return -ENODEV;
89262306a36Sopenharmony_ci		}
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "Partition ID %d of %d\n",
89662306a36Sopenharmony_ci		sndev->self_partition, sndev->stdev->partition_count);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	sndev->mmio_ctrl = (void * __iomem)sndev->mmio_ntb +
89962306a36Sopenharmony_ci		SWITCHTEC_NTB_REG_CTRL_OFFSET;
90062306a36Sopenharmony_ci	sndev->mmio_dbmsg = (void * __iomem)sndev->mmio_ntb +
90162306a36Sopenharmony_ci		SWITCHTEC_NTB_REG_DBMSG_OFFSET;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	sndev->mmio_self_ctrl = &sndev->mmio_ctrl[sndev->self_partition];
90462306a36Sopenharmony_ci	sndev->mmio_peer_ctrl = &sndev->mmio_ctrl[sndev->peer_partition];
90562306a36Sopenharmony_ci	sndev->mmio_self_dbmsg = &sndev->mmio_dbmsg[sndev->self_partition];
90662306a36Sopenharmony_ci	sndev->mmio_peer_dbmsg = sndev->mmio_self_dbmsg;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	return 0;
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic int config_rsvd_lut_win(struct switchtec_ntb *sndev,
91262306a36Sopenharmony_ci			       struct ntb_ctrl_regs __iomem *ctl,
91362306a36Sopenharmony_ci			       int lut_idx, int partition, u64 addr)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	int peer_bar = sndev->peer_direct_mw_to_bar[0];
91662306a36Sopenharmony_ci	u32 ctl_val;
91762306a36Sopenharmony_ci	int rc;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK,
92062306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_LOCKED);
92162306a36Sopenharmony_ci	if (rc)
92262306a36Sopenharmony_ci		return rc;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	ctl_val = ioread32(&ctl->bar_entry[peer_bar].ctl);
92562306a36Sopenharmony_ci	ctl_val &= 0xFF;
92662306a36Sopenharmony_ci	ctl_val |= NTB_CTRL_BAR_LUT_WIN_EN;
92762306a36Sopenharmony_ci	ctl_val |= ilog2(LUT_SIZE) << 8;
92862306a36Sopenharmony_ci	ctl_val |= (sndev->nr_lut_mw - 1) << 14;
92962306a36Sopenharmony_ci	iowrite32(ctl_val, &ctl->bar_entry[peer_bar].ctl);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	iowrite64((NTB_CTRL_LUT_EN | (partition << 1) | addr),
93262306a36Sopenharmony_ci		  &ctl->lut_entry[lut_idx]);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
93562306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_NORMAL);
93662306a36Sopenharmony_ci	if (rc) {
93762306a36Sopenharmony_ci		u32 bar_error, lut_error;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		bar_error = ioread32(&ctl->bar_error);
94062306a36Sopenharmony_ci		lut_error = ioread32(&ctl->lut_error);
94162306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
94262306a36Sopenharmony_ci			"Error setting up reserved lut window: %08x / %08x\n",
94362306a36Sopenharmony_ci			bar_error, lut_error);
94462306a36Sopenharmony_ci		return rc;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	return 0;
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic int config_req_id_table(struct switchtec_ntb *sndev,
95162306a36Sopenharmony_ci			       struct ntb_ctrl_regs __iomem *mmio_ctrl,
95262306a36Sopenharmony_ci			       int *req_ids, int count)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	int i, rc = 0;
95562306a36Sopenharmony_ci	u32 error;
95662306a36Sopenharmony_ci	u32 proxy_id;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (ioread16(&mmio_ctrl->req_id_table_size) < count) {
95962306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
96062306a36Sopenharmony_ci			"Not enough requester IDs available.\n");
96162306a36Sopenharmony_ci		return -EFAULT;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, mmio_ctrl,
96562306a36Sopenharmony_ci				   NTB_CTRL_PART_OP_LOCK,
96662306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_LOCKED);
96762306a36Sopenharmony_ci	if (rc)
96862306a36Sopenharmony_ci		return rc;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
97162306a36Sopenharmony_ci		iowrite32(req_ids[i] << 16 | NTB_CTRL_REQ_ID_EN,
97262306a36Sopenharmony_ci			  &mmio_ctrl->req_id_table[i]);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		proxy_id = ioread32(&mmio_ctrl->req_id_table[i]);
97562306a36Sopenharmony_ci		dev_dbg(&sndev->stdev->dev,
97662306a36Sopenharmony_ci			"Requester ID %02X:%02X.%X -> BB:%02X.%X\n",
97762306a36Sopenharmony_ci			req_ids[i] >> 8, (req_ids[i] >> 3) & 0x1F,
97862306a36Sopenharmony_ci			req_ids[i] & 0x7, (proxy_id >> 4) & 0x1F,
97962306a36Sopenharmony_ci			(proxy_id >> 1) & 0x7);
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, mmio_ctrl,
98362306a36Sopenharmony_ci				   NTB_CTRL_PART_OP_CFG,
98462306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_NORMAL);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (rc == -EIO) {
98762306a36Sopenharmony_ci		error = ioread32(&mmio_ctrl->req_id_error);
98862306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
98962306a36Sopenharmony_ci			"Error setting up the requester ID table: %08x\n",
99062306a36Sopenharmony_ci			error);
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int crosslink_setup_mws(struct switchtec_ntb *sndev, int ntb_lut_idx,
99762306a36Sopenharmony_ci			       u64 *mw_addrs, int mw_count)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	int rc, i;
100062306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_self_ctrl;
100162306a36Sopenharmony_ci	u64 addr;
100262306a36Sopenharmony_ci	size_t size, offset;
100362306a36Sopenharmony_ci	int bar;
100462306a36Sopenharmony_ci	int xlate_pos;
100562306a36Sopenharmony_ci	u32 ctl_val;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK,
100862306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_LOCKED);
100962306a36Sopenharmony_ci	if (rc)
101062306a36Sopenharmony_ci		return rc;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	for (i = 0; i < sndev->nr_lut_mw; i++) {
101362306a36Sopenharmony_ci		if (i == ntb_lut_idx)
101462306a36Sopenharmony_ci			continue;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci		addr = mw_addrs[0] + LUT_SIZE * i;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		iowrite64((NTB_CTRL_LUT_EN | (sndev->peer_partition << 1) |
101962306a36Sopenharmony_ci			   addr),
102062306a36Sopenharmony_ci			  &ctl->lut_entry[i]);
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	sndev->nr_direct_mw = min_t(int, sndev->nr_direct_mw, mw_count);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	for (i = 0; i < sndev->nr_direct_mw; i++) {
102662306a36Sopenharmony_ci		bar = sndev->direct_mw_to_bar[i];
102762306a36Sopenharmony_ci		offset = (i == 0) ? LUT_SIZE * sndev->nr_lut_mw : 0;
102862306a36Sopenharmony_ci		addr = mw_addrs[i] + offset;
102962306a36Sopenharmony_ci		size = pci_resource_len(sndev->ntb.pdev, bar) - offset;
103062306a36Sopenharmony_ci		xlate_pos = ilog2(size);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		if (offset && size > offset)
103362306a36Sopenharmony_ci			size = offset;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
103662306a36Sopenharmony_ci		ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
103962306a36Sopenharmony_ci		iowrite32(xlate_pos | (lower_32_bits(size) & 0xFFFFF000),
104062306a36Sopenharmony_ci			  &ctl->bar_entry[bar].win_size);
104162306a36Sopenharmony_ci		iowrite32(upper_32_bits(size), &ctl->bar_ext_entry[bar].win_size);
104262306a36Sopenharmony_ci		iowrite64(sndev->peer_partition | addr,
104362306a36Sopenharmony_ci			  &ctl->bar_entry[bar].xlate_addr);
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
104762306a36Sopenharmony_ci				   NTB_CTRL_PART_STATUS_NORMAL);
104862306a36Sopenharmony_ci	if (rc) {
104962306a36Sopenharmony_ci		u32 bar_error, lut_error;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci		bar_error = ioread32(&ctl->bar_error);
105262306a36Sopenharmony_ci		lut_error = ioread32(&ctl->lut_error);
105362306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
105462306a36Sopenharmony_ci			"Error setting up cross link windows: %08x / %08x\n",
105562306a36Sopenharmony_ci			bar_error, lut_error);
105662306a36Sopenharmony_ci		return rc;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return 0;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic int crosslink_setup_req_ids(struct switchtec_ntb *sndev,
106362306a36Sopenharmony_ci	struct ntb_ctrl_regs __iomem *mmio_ctrl)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	int req_ids[16];
106662306a36Sopenharmony_ci	int i;
106762306a36Sopenharmony_ci	u32 proxy_id;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(req_ids); i++) {
107062306a36Sopenharmony_ci		proxy_id = ioread32(&sndev->mmio_self_ctrl->req_id_table[i]);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci		if (!(proxy_id & NTB_CTRL_REQ_ID_EN))
107362306a36Sopenharmony_ci			break;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci		req_ids[i] = ((proxy_id >> 1) & 0xFF);
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	return config_req_id_table(sndev, mmio_ctrl, req_ids, i);
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci/*
108262306a36Sopenharmony_ci * In crosslink configuration there is a virtual partition in the
108362306a36Sopenharmony_ci * middle of the two switches. The BARs in this partition have to be
108462306a36Sopenharmony_ci * enumerated and assigned addresses.
108562306a36Sopenharmony_ci */
108662306a36Sopenharmony_cistatic int crosslink_enum_partition(struct switchtec_ntb *sndev,
108762306a36Sopenharmony_ci				    u64 *bar_addrs)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct part_cfg_regs __iomem *part_cfg =
109062306a36Sopenharmony_ci		&sndev->stdev->mmio_part_cfg_all[sndev->peer_partition];
109162306a36Sopenharmony_ci	u32 pff = ioread32(&part_cfg->vep_pff_inst_id) & 0xFF;
109262306a36Sopenharmony_ci	struct pff_csr_regs __iomem *mmio_pff =
109362306a36Sopenharmony_ci		&sndev->stdev->mmio_pff_csr[pff];
109462306a36Sopenharmony_ci	const u64 bar_space = 0x1000000000LL;
109562306a36Sopenharmony_ci	u64 bar_addr;
109662306a36Sopenharmony_ci	int bar_cnt = 0;
109762306a36Sopenharmony_ci	int i;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	iowrite16(0x6, &mmio_pff->pcicmd);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mmio_pff->pci_bar64); i++) {
110262306a36Sopenharmony_ci		iowrite64(bar_space * i, &mmio_pff->pci_bar64[i]);
110362306a36Sopenharmony_ci		bar_addr = ioread64(&mmio_pff->pci_bar64[i]);
110462306a36Sopenharmony_ci		bar_addr &= ~0xf;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci		dev_dbg(&sndev->stdev->dev,
110762306a36Sopenharmony_ci			"Crosslink BAR%d addr: %llx\n",
110862306a36Sopenharmony_ci			i*2, bar_addr);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		if (bar_addr != bar_space * i)
111162306a36Sopenharmony_ci			continue;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci		bar_addrs[bar_cnt++] = bar_addr;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	return bar_cnt;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic int switchtec_ntb_init_crosslink(struct switchtec_ntb *sndev)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	int rc;
112262306a36Sopenharmony_ci	int bar = sndev->direct_mw_to_bar[0];
112362306a36Sopenharmony_ci	const int ntb_lut_idx = 1;
112462306a36Sopenharmony_ci	u64 bar_addrs[6];
112562306a36Sopenharmony_ci	u64 addr;
112662306a36Sopenharmony_ci	int offset;
112762306a36Sopenharmony_ci	int bar_cnt;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (!crosslink_is_enabled(sndev))
113062306a36Sopenharmony_ci		return 0;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	dev_info(&sndev->stdev->dev, "Using crosslink configuration\n");
113362306a36Sopenharmony_ci	sndev->ntb.topo = NTB_TOPO_CROSSLINK;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	bar_cnt = crosslink_enum_partition(sndev, bar_addrs);
113662306a36Sopenharmony_ci	if (bar_cnt < sndev->nr_direct_mw + 1) {
113762306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
113862306a36Sopenharmony_ci			"Error enumerating crosslink partition\n");
113962306a36Sopenharmony_ci		return -EINVAL;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	addr = (bar_addrs[0] + SWITCHTEC_GAS_NTB_OFFSET +
114362306a36Sopenharmony_ci		SWITCHTEC_NTB_REG_DBMSG_OFFSET +
114462306a36Sopenharmony_ci		sizeof(struct ntb_dbmsg_regs) * sndev->peer_partition);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	offset = addr & (LUT_SIZE - 1);
114762306a36Sopenharmony_ci	addr -= offset;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	rc = config_rsvd_lut_win(sndev, sndev->mmio_self_ctrl, ntb_lut_idx,
115062306a36Sopenharmony_ci				 sndev->peer_partition, addr);
115162306a36Sopenharmony_ci	if (rc)
115262306a36Sopenharmony_ci		return rc;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	rc = crosslink_setup_mws(sndev, ntb_lut_idx, &bar_addrs[1],
115562306a36Sopenharmony_ci				 bar_cnt - 1);
115662306a36Sopenharmony_ci	if (rc)
115762306a36Sopenharmony_ci		return rc;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	rc = crosslink_setup_req_ids(sndev, sndev->mmio_peer_ctrl);
116062306a36Sopenharmony_ci	if (rc)
116162306a36Sopenharmony_ci		return rc;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	sndev->mmio_xlink_win = pci_iomap_range(sndev->stdev->pdev, bar,
116462306a36Sopenharmony_ci						LUT_SIZE, LUT_SIZE);
116562306a36Sopenharmony_ci	if (!sndev->mmio_xlink_win) {
116662306a36Sopenharmony_ci		rc = -ENOMEM;
116762306a36Sopenharmony_ci		return rc;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	sndev->mmio_peer_dbmsg = sndev->mmio_xlink_win + offset;
117162306a36Sopenharmony_ci	sndev->nr_rsvd_luts++;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	crosslink_init_dbmsgs(sndev);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	return 0;
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic void switchtec_ntb_deinit_crosslink(struct switchtec_ntb *sndev)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	if (sndev->mmio_xlink_win)
118162306a36Sopenharmony_ci		pci_iounmap(sndev->stdev->pdev, sndev->mmio_xlink_win);
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic int map_bars(int *map, struct ntb_ctrl_regs __iomem *ctrl)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	int i;
118762306a36Sopenharmony_ci	int cnt = 0;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ctrl->bar_entry); i++) {
119062306a36Sopenharmony_ci		u32 r = ioread32(&ctrl->bar_entry[i].ctl);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		if (r & NTB_CTRL_BAR_VALID)
119362306a36Sopenharmony_ci			map[cnt++] = i;
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return cnt;
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic void switchtec_ntb_init_mw(struct switchtec_ntb *sndev)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	sndev->nr_direct_mw = map_bars(sndev->direct_mw_to_bar,
120262306a36Sopenharmony_ci				       sndev->mmio_self_ctrl);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	sndev->nr_lut_mw = ioread16(&sndev->mmio_self_ctrl->lut_table_entries);
120562306a36Sopenharmony_ci	sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "MWs: %d direct, %d lut\n",
120862306a36Sopenharmony_ci		sndev->nr_direct_mw, sndev->nr_lut_mw);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	sndev->peer_nr_direct_mw = map_bars(sndev->peer_direct_mw_to_bar,
121162306a36Sopenharmony_ci					    sndev->mmio_peer_ctrl);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	sndev->peer_nr_lut_mw =
121462306a36Sopenharmony_ci		ioread16(&sndev->mmio_peer_ctrl->lut_table_entries);
121562306a36Sopenharmony_ci	sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "Peer MWs: %d direct, %d lut\n",
121862306a36Sopenharmony_ci		sndev->peer_nr_direct_mw, sndev->peer_nr_lut_mw);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci/*
122362306a36Sopenharmony_ci * There are 64 doorbells in the switch hardware but this is
122462306a36Sopenharmony_ci * shared among all partitions. So we must split them in half
122562306a36Sopenharmony_ci * (32 for each partition). However, the message interrupts are
122662306a36Sopenharmony_ci * also shared with the top 4 doorbells so we just limit this to
122762306a36Sopenharmony_ci * 28 doorbells per partition.
122862306a36Sopenharmony_ci *
122962306a36Sopenharmony_ci * In crosslink mode, each side has it's own dbmsg register so
123062306a36Sopenharmony_ci * they can each use all 60 of the available doorbells.
123162306a36Sopenharmony_ci */
123262306a36Sopenharmony_cistatic void switchtec_ntb_init_db(struct switchtec_ntb *sndev)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	sndev->db_mask = 0x0FFFFFFFFFFFFFFFULL;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	if (sndev->mmio_peer_dbmsg != sndev->mmio_self_dbmsg) {
123762306a36Sopenharmony_ci		sndev->db_shift = 0;
123862306a36Sopenharmony_ci		sndev->db_peer_shift = 0;
123962306a36Sopenharmony_ci		sndev->db_valid_mask = sndev->db_mask;
124062306a36Sopenharmony_ci	} else if (sndev->self_partition < sndev->peer_partition) {
124162306a36Sopenharmony_ci		sndev->db_shift = 0;
124262306a36Sopenharmony_ci		sndev->db_peer_shift = 32;
124362306a36Sopenharmony_ci		sndev->db_valid_mask = 0x0FFFFFFF;
124462306a36Sopenharmony_ci	} else {
124562306a36Sopenharmony_ci		sndev->db_shift = 32;
124662306a36Sopenharmony_ci		sndev->db_peer_shift = 0;
124762306a36Sopenharmony_ci		sndev->db_valid_mask = 0x0FFFFFFF;
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask);
125162306a36Sopenharmony_ci	iowrite64(sndev->db_valid_mask << sndev->db_peer_shift,
125262306a36Sopenharmony_ci		  &sndev->mmio_peer_dbmsg->odb_mask);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "dbs: shift %d/%d, mask %016llx\n",
125562306a36Sopenharmony_ci		sndev->db_shift, sndev->db_peer_shift, sndev->db_valid_mask);
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic void switchtec_ntb_init_msgs(struct switchtec_ntb *sndev)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	int i;
126162306a36Sopenharmony_ci	u32 msg_map = 0;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) {
126462306a36Sopenharmony_ci		int m = i | sndev->peer_partition << 2;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci		msg_map |= m << i * 8;
126762306a36Sopenharmony_ci	}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	iowrite32(msg_map, &sndev->mmio_self_dbmsg->msg_map);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++)
127262306a36Sopenharmony_ci		iowrite64(NTB_DBMSG_IMSG_STATUS | NTB_DBMSG_IMSG_MASK,
127362306a36Sopenharmony_ci			  &sndev->mmio_self_dbmsg->imsg[i]);
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic int
127762306a36Sopenharmony_ciswitchtec_ntb_init_req_id_table(struct switchtec_ntb *sndev)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	int req_ids[2];
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	/*
128262306a36Sopenharmony_ci	 * Root Complex Requester ID (which is 0:00.0)
128362306a36Sopenharmony_ci	 */
128462306a36Sopenharmony_ci	req_ids[0] = 0;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/*
128762306a36Sopenharmony_ci	 * Host Bridge Requester ID (as read from the mmap address)
128862306a36Sopenharmony_ci	 */
128962306a36Sopenharmony_ci	req_ids[1] = ioread16(&sndev->mmio_ntb->requester_id);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	return config_req_id_table(sndev, sndev->mmio_self_ctrl, req_ids,
129262306a36Sopenharmony_ci				   ARRAY_SIZE(req_ids));
129362306a36Sopenharmony_ci}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic void switchtec_ntb_init_shared(struct switchtec_ntb *sndev)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	int i;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	memset(sndev->self_shared, 0, LUT_SIZE);
130062306a36Sopenharmony_ci	sndev->self_shared->magic = SWITCHTEC_NTB_MAGIC;
130162306a36Sopenharmony_ci	sndev->self_shared->partition_id = sndev->stdev->partition;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	for (i = 0; i < sndev->nr_direct_mw; i++) {
130462306a36Sopenharmony_ci		int bar = sndev->direct_mw_to_bar[i];
130562306a36Sopenharmony_ci		resource_size_t sz = pci_resource_len(sndev->stdev->pdev, bar);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		if (i == 0)
130862306a36Sopenharmony_ci			sz = min_t(resource_size_t, sz,
130962306a36Sopenharmony_ci				   LUT_SIZE * sndev->nr_lut_mw);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci		sndev->self_shared->mw_sizes[i] = sz;
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	for (i = 0; i < sndev->nr_lut_mw; i++) {
131562306a36Sopenharmony_ci		int idx = sndev->nr_direct_mw + i;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci		sndev->self_shared->mw_sizes[idx] = LUT_SIZE;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic int switchtec_ntb_init_shared_mw(struct switchtec_ntb *sndev)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	int self_bar = sndev->direct_mw_to_bar[0];
132462306a36Sopenharmony_ci	int rc;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	sndev->nr_rsvd_luts++;
132762306a36Sopenharmony_ci	sndev->self_shared = dma_alloc_coherent(&sndev->stdev->pdev->dev,
132862306a36Sopenharmony_ci						LUT_SIZE,
132962306a36Sopenharmony_ci						&sndev->self_shared_dma,
133062306a36Sopenharmony_ci						GFP_KERNEL);
133162306a36Sopenharmony_ci	if (!sndev->self_shared) {
133262306a36Sopenharmony_ci		dev_err(&sndev->stdev->dev,
133362306a36Sopenharmony_ci			"unable to allocate memory for shared mw\n");
133462306a36Sopenharmony_ci		return -ENOMEM;
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	switchtec_ntb_init_shared(sndev);
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	rc = config_rsvd_lut_win(sndev, sndev->mmio_peer_ctrl, 0,
134062306a36Sopenharmony_ci				 sndev->self_partition,
134162306a36Sopenharmony_ci				 sndev->self_shared_dma);
134262306a36Sopenharmony_ci	if (rc)
134362306a36Sopenharmony_ci		goto unalloc_and_exit;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	sndev->peer_shared = pci_iomap(sndev->stdev->pdev, self_bar, LUT_SIZE);
134662306a36Sopenharmony_ci	if (!sndev->peer_shared) {
134762306a36Sopenharmony_ci		rc = -ENOMEM;
134862306a36Sopenharmony_ci		goto unalloc_and_exit;
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "Shared MW Ready\n");
135262306a36Sopenharmony_ci	return 0;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ciunalloc_and_exit:
135562306a36Sopenharmony_ci	dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE,
135662306a36Sopenharmony_ci			  sndev->self_shared, sndev->self_shared_dma);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	return rc;
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic void switchtec_ntb_deinit_shared_mw(struct switchtec_ntb *sndev)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	if (sndev->peer_shared)
136462306a36Sopenharmony_ci		pci_iounmap(sndev->stdev->pdev, sndev->peer_shared);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	if (sndev->self_shared)
136762306a36Sopenharmony_ci		dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE,
136862306a36Sopenharmony_ci				  sndev->self_shared,
136962306a36Sopenharmony_ci				  sndev->self_shared_dma);
137062306a36Sopenharmony_ci	sndev->nr_rsvd_luts--;
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic irqreturn_t switchtec_ntb_doorbell_isr(int irq, void *dev)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	struct switchtec_ntb *sndev = dev;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "doorbell\n");
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	ntb_db_event(&sndev->ntb, 0);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	return IRQ_HANDLED;
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic irqreturn_t switchtec_ntb_message_isr(int irq, void *dev)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	int i;
138762306a36Sopenharmony_ci	struct switchtec_ntb *sndev = dev;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) {
139062306a36Sopenharmony_ci		u64 msg = ioread64(&sndev->mmio_self_dbmsg->imsg[i]);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		if (msg & NTB_DBMSG_IMSG_STATUS) {
139362306a36Sopenharmony_ci			dev_dbg(&sndev->stdev->dev, "message: %d %08x\n",
139462306a36Sopenharmony_ci				i, (u32)msg);
139562306a36Sopenharmony_ci			iowrite8(1, &sndev->mmio_self_dbmsg->imsg[i].status);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci			if (i == LINK_MESSAGE)
139862306a36Sopenharmony_ci				switchtec_ntb_check_link(sndev, msg);
139962306a36Sopenharmony_ci		}
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	return IRQ_HANDLED;
140362306a36Sopenharmony_ci}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic int switchtec_ntb_init_db_msg_irq(struct switchtec_ntb *sndev)
140662306a36Sopenharmony_ci{
140762306a36Sopenharmony_ci	int i;
140862306a36Sopenharmony_ci	int rc;
140962306a36Sopenharmony_ci	int doorbell_irq = 0;
141062306a36Sopenharmony_ci	int message_irq = 0;
141162306a36Sopenharmony_ci	int event_irq;
141262306a36Sopenharmony_ci	int idb_vecs = sizeof(sndev->mmio_self_dbmsg->idb_vec_map);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	event_irq = ioread32(&sndev->stdev->mmio_part_cfg->vep_vector_number);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	while (doorbell_irq == event_irq)
141762306a36Sopenharmony_ci		doorbell_irq++;
141862306a36Sopenharmony_ci	while (message_irq == doorbell_irq ||
141962306a36Sopenharmony_ci	       message_irq == event_irq)
142062306a36Sopenharmony_ci		message_irq++;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	dev_dbg(&sndev->stdev->dev, "irqs - event: %d, db: %d, msgs: %d\n",
142362306a36Sopenharmony_ci		event_irq, doorbell_irq, message_irq);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	for (i = 0; i < idb_vecs - 4; i++)
142662306a36Sopenharmony_ci		iowrite8(doorbell_irq,
142762306a36Sopenharmony_ci			 &sndev->mmio_self_dbmsg->idb_vec_map[i]);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	for (; i < idb_vecs; i++)
143062306a36Sopenharmony_ci		iowrite8(message_irq,
143162306a36Sopenharmony_ci			 &sndev->mmio_self_dbmsg->idb_vec_map[i]);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	sndev->doorbell_irq = pci_irq_vector(sndev->stdev->pdev, doorbell_irq);
143462306a36Sopenharmony_ci	sndev->message_irq = pci_irq_vector(sndev->stdev->pdev, message_irq);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	rc = request_irq(sndev->doorbell_irq,
143762306a36Sopenharmony_ci			 switchtec_ntb_doorbell_isr, 0,
143862306a36Sopenharmony_ci			 "switchtec_ntb_doorbell", sndev);
143962306a36Sopenharmony_ci	if (rc)
144062306a36Sopenharmony_ci		return rc;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	rc = request_irq(sndev->message_irq,
144362306a36Sopenharmony_ci			 switchtec_ntb_message_isr, 0,
144462306a36Sopenharmony_ci			 "switchtec_ntb_message", sndev);
144562306a36Sopenharmony_ci	if (rc) {
144662306a36Sopenharmony_ci		free_irq(sndev->doorbell_irq, sndev);
144762306a36Sopenharmony_ci		return rc;
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	return 0;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic void switchtec_ntb_deinit_db_msg_irq(struct switchtec_ntb *sndev)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	free_irq(sndev->doorbell_irq, sndev);
145662306a36Sopenharmony_ci	free_irq(sndev->message_irq, sndev);
145762306a36Sopenharmony_ci}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_cistatic int switchtec_ntb_reinit_peer(struct switchtec_ntb *sndev)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	int rc;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (crosslink_is_enabled(sndev))
146462306a36Sopenharmony_ci		return 0;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	dev_info(&sndev->stdev->dev, "reinitialize shared memory window\n");
146762306a36Sopenharmony_ci	rc = config_rsvd_lut_win(sndev, sndev->mmio_peer_ctrl, 0,
146862306a36Sopenharmony_ci				 sndev->self_partition,
146962306a36Sopenharmony_ci				 sndev->self_shared_dma);
147062306a36Sopenharmony_ci	return rc;
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic int switchtec_ntb_add(struct device *dev)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	struct switchtec_dev *stdev = to_stdev(dev);
147662306a36Sopenharmony_ci	struct switchtec_ntb *sndev;
147762306a36Sopenharmony_ci	int rc;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	stdev->sndev = NULL;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (stdev->pdev->class != (PCI_CLASS_BRIDGE_OTHER << 8))
148262306a36Sopenharmony_ci		return -ENODEV;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	sndev = kzalloc_node(sizeof(*sndev), GFP_KERNEL, dev_to_node(dev));
148562306a36Sopenharmony_ci	if (!sndev)
148662306a36Sopenharmony_ci		return -ENOMEM;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	sndev->stdev = stdev;
148962306a36Sopenharmony_ci	rc = switchtec_ntb_init_sndev(sndev);
149062306a36Sopenharmony_ci	if (rc)
149162306a36Sopenharmony_ci		goto free_and_exit;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	switchtec_ntb_init_mw(sndev);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	rc = switchtec_ntb_init_req_id_table(sndev);
149662306a36Sopenharmony_ci	if (rc)
149762306a36Sopenharmony_ci		goto free_and_exit;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	rc = switchtec_ntb_init_crosslink(sndev);
150062306a36Sopenharmony_ci	if (rc)
150162306a36Sopenharmony_ci		goto free_and_exit;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	switchtec_ntb_init_db(sndev);
150462306a36Sopenharmony_ci	switchtec_ntb_init_msgs(sndev);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	rc = switchtec_ntb_init_shared_mw(sndev);
150762306a36Sopenharmony_ci	if (rc)
150862306a36Sopenharmony_ci		goto deinit_crosslink;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	rc = switchtec_ntb_init_db_msg_irq(sndev);
151162306a36Sopenharmony_ci	if (rc)
151262306a36Sopenharmony_ci		goto deinit_shared_and_exit;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	/*
151562306a36Sopenharmony_ci	 * If this host crashed, the other host may think the link is
151662306a36Sopenharmony_ci	 * still up. Tell them to force it down (it will go back up
151762306a36Sopenharmony_ci	 * once we register the ntb device).
151862306a36Sopenharmony_ci	 */
151962306a36Sopenharmony_ci	switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_FORCE_DOWN);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	rc = ntb_register_device(&sndev->ntb);
152262306a36Sopenharmony_ci	if (rc)
152362306a36Sopenharmony_ci		goto deinit_and_exit;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	stdev->sndev = sndev;
152662306a36Sopenharmony_ci	stdev->link_notifier = switchtec_ntb_link_notification;
152762306a36Sopenharmony_ci	dev_info(dev, "NTB device registered\n");
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	return 0;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cideinit_and_exit:
153262306a36Sopenharmony_ci	switchtec_ntb_deinit_db_msg_irq(sndev);
153362306a36Sopenharmony_cideinit_shared_and_exit:
153462306a36Sopenharmony_ci	switchtec_ntb_deinit_shared_mw(sndev);
153562306a36Sopenharmony_cideinit_crosslink:
153662306a36Sopenharmony_ci	switchtec_ntb_deinit_crosslink(sndev);
153762306a36Sopenharmony_cifree_and_exit:
153862306a36Sopenharmony_ci	kfree(sndev);
153962306a36Sopenharmony_ci	dev_err(dev, "failed to register ntb device: %d\n", rc);
154062306a36Sopenharmony_ci	return rc;
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_cistatic void switchtec_ntb_remove(struct device *dev)
154462306a36Sopenharmony_ci{
154562306a36Sopenharmony_ci	struct switchtec_dev *stdev = to_stdev(dev);
154662306a36Sopenharmony_ci	struct switchtec_ntb *sndev = stdev->sndev;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (!sndev)
154962306a36Sopenharmony_ci		return;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	stdev->link_notifier = NULL;
155262306a36Sopenharmony_ci	stdev->sndev = NULL;
155362306a36Sopenharmony_ci	ntb_unregister_device(&sndev->ntb);
155462306a36Sopenharmony_ci	switchtec_ntb_deinit_db_msg_irq(sndev);
155562306a36Sopenharmony_ci	switchtec_ntb_deinit_shared_mw(sndev);
155662306a36Sopenharmony_ci	switchtec_ntb_deinit_crosslink(sndev);
155762306a36Sopenharmony_ci	kfree(sndev);
155862306a36Sopenharmony_ci	dev_info(dev, "ntb device unregistered\n");
155962306a36Sopenharmony_ci}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic struct class_interface switchtec_interface  = {
156262306a36Sopenharmony_ci	.add_dev = switchtec_ntb_add,
156362306a36Sopenharmony_ci	.remove_dev = switchtec_ntb_remove,
156462306a36Sopenharmony_ci};
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_cistatic int __init switchtec_ntb_init(void)
156762306a36Sopenharmony_ci{
156862306a36Sopenharmony_ci	switchtec_interface.class = switchtec_class;
156962306a36Sopenharmony_ci	return class_interface_register(&switchtec_interface);
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_cimodule_init(switchtec_ntb_init);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic void __exit switchtec_ntb_exit(void)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	class_interface_unregister(&switchtec_interface);
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_cimodule_exit(switchtec_ntb_exit);
1578