xref: /kernel/linux/linux-6.6/drivers/ntb/msi.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/irq.h>
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/ntb.h>
662306a36Sopenharmony_ci#include <linux/msi.h>
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistruct ntb_msi {
1062306a36Sopenharmony_ci	u64 base_addr;
1162306a36Sopenharmony_ci	u64 end_addr;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	void (*desc_changed)(void *ctx);
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	u32 __iomem *peer_mws[];
1662306a36Sopenharmony_ci};
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/**
1962306a36Sopenharmony_ci * ntb_msi_init() - Initialize the MSI context
2062306a36Sopenharmony_ci * @ntb:	NTB device context
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * This function must be called before any other ntb_msi function.
2362306a36Sopenharmony_ci * It initializes the context for MSI operations and maps
2462306a36Sopenharmony_ci * the peer memory windows.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * This function reserves the last N outbound memory windows (where N
2762306a36Sopenharmony_ci * is the number of peers).
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Return: Zero on success, otherwise a negative error number.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ciint ntb_msi_init(struct ntb_dev *ntb,
3262306a36Sopenharmony_ci		 void (*desc_changed)(void *ctx))
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	phys_addr_t mw_phys_addr;
3562306a36Sopenharmony_ci	resource_size_t mw_size;
3662306a36Sopenharmony_ci	int peer_widx;
3762306a36Sopenharmony_ci	int peers;
3862306a36Sopenharmony_ci	int ret;
3962306a36Sopenharmony_ci	int i;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	peers = ntb_peer_port_count(ntb);
4262306a36Sopenharmony_ci	if (peers <= 0)
4362306a36Sopenharmony_ci		return -EINVAL;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ntb->msi = devm_kzalloc(&ntb->dev, struct_size(ntb->msi, peer_mws, peers),
4662306a36Sopenharmony_ci				GFP_KERNEL);
4762306a36Sopenharmony_ci	if (!ntb->msi)
4862306a36Sopenharmony_ci		return -ENOMEM;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	ntb->msi->desc_changed = desc_changed;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	for (i = 0; i < peers; i++) {
5362306a36Sopenharmony_ci		peer_widx = ntb_peer_mw_count(ntb) - 1 - i;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		ret = ntb_peer_mw_get_addr(ntb, peer_widx, &mw_phys_addr,
5662306a36Sopenharmony_ci					   &mw_size);
5762306a36Sopenharmony_ci		if (ret)
5862306a36Sopenharmony_ci			goto unroll;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		ntb->msi->peer_mws[i] = devm_ioremap(&ntb->dev, mw_phys_addr,
6162306a36Sopenharmony_ci						     mw_size);
6262306a36Sopenharmony_ci		if (!ntb->msi->peer_mws[i]) {
6362306a36Sopenharmony_ci			ret = -EFAULT;
6462306a36Sopenharmony_ci			goto unroll;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciunroll:
7162306a36Sopenharmony_ci	for (i = 0; i < peers; i++)
7262306a36Sopenharmony_ci		if (ntb->msi->peer_mws[i])
7362306a36Sopenharmony_ci			devm_iounmap(&ntb->dev, ntb->msi->peer_mws[i]);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	devm_kfree(&ntb->dev, ntb->msi);
7662306a36Sopenharmony_ci	ntb->msi = NULL;
7762306a36Sopenharmony_ci	return ret;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ciEXPORT_SYMBOL(ntb_msi_init);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * ntb_msi_setup_mws() - Initialize the MSI inbound memory windows
8362306a36Sopenharmony_ci * @ntb:	NTB device context
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * This function sets up the required inbound memory windows. It should be
8662306a36Sopenharmony_ci * called from a work function after a link up event.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Over the entire network, this function will reserves the last N
8962306a36Sopenharmony_ci * inbound memory windows for each peer (where N is the number of peers).
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * ntb_msi_init() must be called before this function.
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * Return: Zero on success, otherwise a negative error number.
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_ciint ntb_msi_setup_mws(struct ntb_dev *ntb)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct msi_desc *desc;
9862306a36Sopenharmony_ci	u64 addr;
9962306a36Sopenharmony_ci	int peer, peer_widx;
10062306a36Sopenharmony_ci	resource_size_t addr_align, size_align, size_max;
10162306a36Sopenharmony_ci	resource_size_t mw_size = SZ_32K;
10262306a36Sopenharmony_ci	resource_size_t mw_min_size = mw_size;
10362306a36Sopenharmony_ci	int i;
10462306a36Sopenharmony_ci	int ret;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (!ntb->msi)
10762306a36Sopenharmony_ci		return -EINVAL;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	msi_lock_descs(&ntb->pdev->dev);
11062306a36Sopenharmony_ci	desc = msi_first_desc(&ntb->pdev->dev, MSI_DESC_ASSOCIATED);
11162306a36Sopenharmony_ci	addr = desc->msg.address_lo + ((uint64_t)desc->msg.address_hi << 32);
11262306a36Sopenharmony_ci	msi_unlock_descs(&ntb->pdev->dev);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
11562306a36Sopenharmony_ci		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
11662306a36Sopenharmony_ci		if (peer_widx < 0)
11762306a36Sopenharmony_ci			return peer_widx;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		ret = ntb_mw_get_align(ntb, peer, peer_widx, &addr_align,
12062306a36Sopenharmony_ci				       NULL, NULL);
12162306a36Sopenharmony_ci		if (ret)
12262306a36Sopenharmony_ci			return ret;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		addr &= ~(addr_align - 1);
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
12862306a36Sopenharmony_ci		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
12962306a36Sopenharmony_ci		if (peer_widx < 0) {
13062306a36Sopenharmony_ci			ret = peer_widx;
13162306a36Sopenharmony_ci			goto error_out;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		ret = ntb_mw_get_align(ntb, peer, peer_widx, NULL,
13562306a36Sopenharmony_ci				       &size_align, &size_max);
13662306a36Sopenharmony_ci		if (ret)
13762306a36Sopenharmony_ci			goto error_out;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		mw_size = round_up(mw_size, size_align);
14062306a36Sopenharmony_ci		mw_size = max(mw_size, size_max);
14162306a36Sopenharmony_ci		if (mw_size < mw_min_size)
14262306a36Sopenharmony_ci			mw_min_size = mw_size;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		ret = ntb_mw_set_trans(ntb, peer, peer_widx,
14562306a36Sopenharmony_ci				       addr, mw_size);
14662306a36Sopenharmony_ci		if (ret)
14762306a36Sopenharmony_ci			goto error_out;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ntb->msi->base_addr = addr;
15162306a36Sopenharmony_ci	ntb->msi->end_addr = addr + mw_min_size;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cierror_out:
15662306a36Sopenharmony_ci	for (i = 0; i < peer; i++) {
15762306a36Sopenharmony_ci		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
15862306a36Sopenharmony_ci		if (peer_widx < 0)
15962306a36Sopenharmony_ci			continue;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		ntb_mw_clear_trans(ntb, i, peer_widx);
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return ret;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ciEXPORT_SYMBOL(ntb_msi_setup_mws);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/**
16962306a36Sopenharmony_ci * ntb_msi_clear_mws() - Clear all inbound memory windows
17062306a36Sopenharmony_ci * @ntb:	NTB device context
17162306a36Sopenharmony_ci *
17262306a36Sopenharmony_ci * This function tears down the resources used by ntb_msi_setup_mws().
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_civoid ntb_msi_clear_mws(struct ntb_dev *ntb)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	int peer;
17762306a36Sopenharmony_ci	int peer_widx;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
18062306a36Sopenharmony_ci		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
18162306a36Sopenharmony_ci		if (peer_widx < 0)
18262306a36Sopenharmony_ci			continue;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		ntb_mw_clear_trans(ntb, peer, peer_widx);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ciEXPORT_SYMBOL(ntb_msi_clear_mws);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistruct ntb_msi_devres {
19062306a36Sopenharmony_ci	struct ntb_dev *ntb;
19162306a36Sopenharmony_ci	struct msi_desc *entry;
19262306a36Sopenharmony_ci	struct ntb_msi_desc *msi_desc;
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int ntb_msi_set_desc(struct ntb_dev *ntb, struct msi_desc *entry,
19662306a36Sopenharmony_ci			    struct ntb_msi_desc *msi_desc)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	u64 addr;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	addr = entry->msg.address_lo +
20162306a36Sopenharmony_ci		((uint64_t)entry->msg.address_hi << 32);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (addr < ntb->msi->base_addr || addr >= ntb->msi->end_addr) {
20462306a36Sopenharmony_ci		dev_warn_once(&ntb->dev,
20562306a36Sopenharmony_ci			      "IRQ %d: MSI Address not within the memory window (%llx, [%llx %llx])\n",
20662306a36Sopenharmony_ci			      entry->irq, addr, ntb->msi->base_addr,
20762306a36Sopenharmony_ci			      ntb->msi->end_addr);
20862306a36Sopenharmony_ci		return -EFAULT;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	msi_desc->addr_offset = addr - ntb->msi->base_addr;
21262306a36Sopenharmony_ci	msi_desc->data = entry->msg.data;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void ntb_msi_write_msg(struct msi_desc *entry, void *data)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct ntb_msi_devres *dr = data;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	WARN_ON(ntb_msi_set_desc(dr->ntb, entry, dr->msi_desc));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (dr->ntb->msi->desc_changed)
22462306a36Sopenharmony_ci		dr->ntb->msi->desc_changed(dr->ntb->ctx);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void ntbm_msi_callback_release(struct device *dev, void *res)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct ntb_msi_devres *dr = res;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	dr->entry->write_msi_msg = NULL;
23262306a36Sopenharmony_ci	dr->entry->write_msi_msg_data = NULL;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int ntbm_msi_setup_callback(struct ntb_dev *ntb, struct msi_desc *entry,
23662306a36Sopenharmony_ci				   struct ntb_msi_desc *msi_desc)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct ntb_msi_devres *dr;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	dr = devres_alloc(ntbm_msi_callback_release,
24162306a36Sopenharmony_ci			  sizeof(struct ntb_msi_devres), GFP_KERNEL);
24262306a36Sopenharmony_ci	if (!dr)
24362306a36Sopenharmony_ci		return -ENOMEM;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dr->ntb = ntb;
24662306a36Sopenharmony_ci	dr->entry = entry;
24762306a36Sopenharmony_ci	dr->msi_desc = msi_desc;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	devres_add(&ntb->dev, dr);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	dr->entry->write_msi_msg = ntb_msi_write_msg;
25262306a36Sopenharmony_ci	dr->entry->write_msi_msg_data = dr;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/**
25862306a36Sopenharmony_ci * ntbm_msi_request_threaded_irq() - allocate an MSI interrupt
25962306a36Sopenharmony_ci * @ntb:	NTB device context
26062306a36Sopenharmony_ci * @handler:	Function to be called when the IRQ occurs
26162306a36Sopenharmony_ci * @thread_fn:  Function to be called in a threaded interrupt context. NULL
26262306a36Sopenharmony_ci *              for clients which handle everything in @handler
26362306a36Sopenharmony_ci * @name:    An ascii name for the claiming device, dev_name(dev) if NULL
26462306a36Sopenharmony_ci * @dev_id:     A cookie passed back to the handler function
26562306a36Sopenharmony_ci * @msi_desc:	MSI descriptor data which triggers the interrupt
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * This function assigns an interrupt handler to an unused
26862306a36Sopenharmony_ci * MSI interrupt and returns the descriptor used to trigger
26962306a36Sopenharmony_ci * it. The descriptor can then be sent to a peer to trigger
27062306a36Sopenharmony_ci * the interrupt.
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci * The interrupt resource is managed with devres so it will
27362306a36Sopenharmony_ci * be automatically freed when the NTB device is torn down.
27462306a36Sopenharmony_ci *
27562306a36Sopenharmony_ci * If an IRQ allocated with this function needs to be freed
27662306a36Sopenharmony_ci * separately, ntbm_free_irq() must be used.
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * Return: IRQ number assigned on success, otherwise a negative error number.
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_ciint ntbm_msi_request_threaded_irq(struct ntb_dev *ntb, irq_handler_t handler,
28162306a36Sopenharmony_ci				  irq_handler_t thread_fn,
28262306a36Sopenharmony_ci				  const char *name, void *dev_id,
28362306a36Sopenharmony_ci				  struct ntb_msi_desc *msi_desc)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct device *dev = &ntb->pdev->dev;
28662306a36Sopenharmony_ci	struct msi_desc *entry;
28762306a36Sopenharmony_ci	int ret;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!ntb->msi)
29062306a36Sopenharmony_ci		return -EINVAL;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	msi_lock_descs(dev);
29362306a36Sopenharmony_ci	msi_for_each_desc(entry, dev, MSI_DESC_ASSOCIATED) {
29462306a36Sopenharmony_ci		if (irq_has_action(entry->irq))
29562306a36Sopenharmony_ci			continue;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&ntb->dev, entry->irq, handler,
29862306a36Sopenharmony_ci						thread_fn, 0, name, dev_id);
29962306a36Sopenharmony_ci		if (ret)
30062306a36Sopenharmony_ci			continue;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		if (ntb_msi_set_desc(ntb, entry, msi_desc)) {
30362306a36Sopenharmony_ci			devm_free_irq(&ntb->dev, entry->irq, dev_id);
30462306a36Sopenharmony_ci			continue;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		ret = ntbm_msi_setup_callback(ntb, entry, msi_desc);
30862306a36Sopenharmony_ci		if (ret) {
30962306a36Sopenharmony_ci			devm_free_irq(&ntb->dev, entry->irq, dev_id);
31062306a36Sopenharmony_ci			goto unlock;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		ret = entry->irq;
31462306a36Sopenharmony_ci		goto unlock;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	ret = -ENODEV;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciunlock:
31962306a36Sopenharmony_ci	msi_unlock_descs(dev);
32062306a36Sopenharmony_ci	return ret;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ciEXPORT_SYMBOL(ntbm_msi_request_threaded_irq);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic int ntbm_msi_callback_match(struct device *dev, void *res, void *data)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct ntb_dev *ntb = dev_ntb(dev);
32762306a36Sopenharmony_ci	struct ntb_msi_devres *dr = res;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return dr->ntb == ntb && dr->entry == data;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci/**
33362306a36Sopenharmony_ci * ntbm_msi_free_irq() - free an interrupt
33462306a36Sopenharmony_ci * @ntb:	NTB device context
33562306a36Sopenharmony_ci * @irq:	Interrupt line to free
33662306a36Sopenharmony_ci * @dev_id:	Device identity to free
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * This function should be used to manually free IRQs allocated with
33962306a36Sopenharmony_ci * ntbm_request_[threaded_]irq().
34062306a36Sopenharmony_ci */
34162306a36Sopenharmony_civoid ntbm_msi_free_irq(struct ntb_dev *ntb, unsigned int irq, void *dev_id)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct msi_desc *entry = irq_get_msi_desc(irq);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	entry->write_msi_msg = NULL;
34662306a36Sopenharmony_ci	entry->write_msi_msg_data = NULL;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	WARN_ON(devres_destroy(&ntb->dev, ntbm_msi_callback_release,
34962306a36Sopenharmony_ci			       ntbm_msi_callback_match, entry));
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	devm_free_irq(&ntb->dev, irq, dev_id);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ciEXPORT_SYMBOL(ntbm_msi_free_irq);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/**
35662306a36Sopenharmony_ci * ntb_msi_peer_trigger() - Trigger an interrupt handler on a peer
35762306a36Sopenharmony_ci * @ntb:	NTB device context
35862306a36Sopenharmony_ci * @peer:	Peer index
35962306a36Sopenharmony_ci * @desc:	MSI descriptor data which triggers the interrupt
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * This function triggers an interrupt on a peer. It requires
36262306a36Sopenharmony_ci * the descriptor structure to have been passed from that peer
36362306a36Sopenharmony_ci * by some other means.
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * Return: Zero on success, otherwise a negative error number.
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_ciint ntb_msi_peer_trigger(struct ntb_dev *ntb, int peer,
36862306a36Sopenharmony_ci			 struct ntb_msi_desc *desc)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	int idx;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (!ntb->msi)
37362306a36Sopenharmony_ci		return -EINVAL;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	idx = desc->addr_offset / sizeof(*ntb->msi->peer_mws[peer]);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	iowrite32(desc->data, &ntb->msi->peer_mws[peer][idx]);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ciEXPORT_SYMBOL(ntb_msi_peer_trigger);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/**
38462306a36Sopenharmony_ci * ntb_msi_peer_addr() - Get the DMA address to trigger a peer's MSI interrupt
38562306a36Sopenharmony_ci * @ntb:	NTB device context
38662306a36Sopenharmony_ci * @peer:	Peer index
38762306a36Sopenharmony_ci * @desc:	MSI descriptor data which triggers the interrupt
38862306a36Sopenharmony_ci * @msi_addr:   Physical address to trigger the interrupt
38962306a36Sopenharmony_ci *
39062306a36Sopenharmony_ci * This function allows using DMA engines to trigger an interrupt
39162306a36Sopenharmony_ci * (for example, trigger an interrupt to process the data after
39262306a36Sopenharmony_ci * sending it). To trigger the interrupt, write @desc.data to the address
39362306a36Sopenharmony_ci * returned in @msi_addr
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * Return: Zero on success, otherwise a negative error number.
39662306a36Sopenharmony_ci */
39762306a36Sopenharmony_ciint ntb_msi_peer_addr(struct ntb_dev *ntb, int peer,
39862306a36Sopenharmony_ci		      struct ntb_msi_desc *desc,
39962306a36Sopenharmony_ci		      phys_addr_t *msi_addr)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	int peer_widx = ntb_peer_mw_count(ntb) - 1 - peer;
40262306a36Sopenharmony_ci	phys_addr_t mw_phys_addr;
40362306a36Sopenharmony_ci	int ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = ntb_peer_mw_get_addr(ntb, peer_widx, &mw_phys_addr, NULL);
40662306a36Sopenharmony_ci	if (ret)
40762306a36Sopenharmony_ci		return ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (msi_addr)
41062306a36Sopenharmony_ci		*msi_addr = mw_phys_addr + desc->addr_offset;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return 0;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ciEXPORT_SYMBOL(ntb_msi_peer_addr);
415