xref: /openbmc/linux/drivers/ntb/msi.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
126b3a37bSLogan Gunthorpe // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
226b3a37bSLogan Gunthorpe 
326b3a37bSLogan Gunthorpe #include <linux/irq.h>
426b3a37bSLogan Gunthorpe #include <linux/module.h>
526b3a37bSLogan Gunthorpe #include <linux/ntb.h>
626b3a37bSLogan Gunthorpe #include <linux/msi.h>
726b3a37bSLogan Gunthorpe #include <linux/pci.h>
826b3a37bSLogan Gunthorpe 
926b3a37bSLogan Gunthorpe struct ntb_msi {
1026b3a37bSLogan Gunthorpe 	u64 base_addr;
1126b3a37bSLogan Gunthorpe 	u64 end_addr;
1226b3a37bSLogan Gunthorpe 
1326b3a37bSLogan Gunthorpe 	void (*desc_changed)(void *ctx);
1426b3a37bSLogan Gunthorpe 
1526b3a37bSLogan Gunthorpe 	u32 __iomem *peer_mws[];
1626b3a37bSLogan Gunthorpe };
1726b3a37bSLogan Gunthorpe 
1826b3a37bSLogan Gunthorpe /**
1926b3a37bSLogan Gunthorpe  * ntb_msi_init() - Initialize the MSI context
2026b3a37bSLogan Gunthorpe  * @ntb:	NTB device context
2126b3a37bSLogan Gunthorpe  *
2226b3a37bSLogan Gunthorpe  * This function must be called before any other ntb_msi function.
2326b3a37bSLogan Gunthorpe  * It initializes the context for MSI operations and maps
2426b3a37bSLogan Gunthorpe  * the peer memory windows.
2526b3a37bSLogan Gunthorpe  *
2626b3a37bSLogan Gunthorpe  * This function reserves the last N outbound memory windows (where N
2726b3a37bSLogan Gunthorpe  * is the number of peers).
2826b3a37bSLogan Gunthorpe  *
2926b3a37bSLogan Gunthorpe  * Return: Zero on success, otherwise a negative error number.
3026b3a37bSLogan Gunthorpe  */
ntb_msi_init(struct ntb_dev * ntb,void (* desc_changed)(void * ctx))3126b3a37bSLogan Gunthorpe int ntb_msi_init(struct ntb_dev *ntb,
3226b3a37bSLogan Gunthorpe 		 void (*desc_changed)(void *ctx))
3326b3a37bSLogan Gunthorpe {
3426b3a37bSLogan Gunthorpe 	phys_addr_t mw_phys_addr;
3526b3a37bSLogan Gunthorpe 	resource_size_t mw_size;
3626b3a37bSLogan Gunthorpe 	int peer_widx;
3726b3a37bSLogan Gunthorpe 	int peers;
3826b3a37bSLogan Gunthorpe 	int ret;
3926b3a37bSLogan Gunthorpe 	int i;
4026b3a37bSLogan Gunthorpe 
4126b3a37bSLogan Gunthorpe 	peers = ntb_peer_port_count(ntb);
4226b3a37bSLogan Gunthorpe 	if (peers <= 0)
4326b3a37bSLogan Gunthorpe 		return -EINVAL;
4426b3a37bSLogan Gunthorpe 
45*30532568SGustavo A. R. Silva 	ntb->msi = devm_kzalloc(&ntb->dev, struct_size(ntb->msi, peer_mws, peers),
46*30532568SGustavo A. R. Silva 				GFP_KERNEL);
4726b3a37bSLogan Gunthorpe 	if (!ntb->msi)
4826b3a37bSLogan Gunthorpe 		return -ENOMEM;
4926b3a37bSLogan Gunthorpe 
5026b3a37bSLogan Gunthorpe 	ntb->msi->desc_changed = desc_changed;
5126b3a37bSLogan Gunthorpe 
5226b3a37bSLogan Gunthorpe 	for (i = 0; i < peers; i++) {
5326b3a37bSLogan Gunthorpe 		peer_widx = ntb_peer_mw_count(ntb) - 1 - i;
5426b3a37bSLogan Gunthorpe 
5526b3a37bSLogan Gunthorpe 		ret = ntb_peer_mw_get_addr(ntb, peer_widx, &mw_phys_addr,
5626b3a37bSLogan Gunthorpe 					   &mw_size);
5726b3a37bSLogan Gunthorpe 		if (ret)
5826b3a37bSLogan Gunthorpe 			goto unroll;
5926b3a37bSLogan Gunthorpe 
6026b3a37bSLogan Gunthorpe 		ntb->msi->peer_mws[i] = devm_ioremap(&ntb->dev, mw_phys_addr,
6126b3a37bSLogan Gunthorpe 						     mw_size);
6226b3a37bSLogan Gunthorpe 		if (!ntb->msi->peer_mws[i]) {
6326b3a37bSLogan Gunthorpe 			ret = -EFAULT;
6426b3a37bSLogan Gunthorpe 			goto unroll;
6526b3a37bSLogan Gunthorpe 		}
6626b3a37bSLogan Gunthorpe 	}
6726b3a37bSLogan Gunthorpe 
6826b3a37bSLogan Gunthorpe 	return 0;
6926b3a37bSLogan Gunthorpe 
7026b3a37bSLogan Gunthorpe unroll:
7126b3a37bSLogan Gunthorpe 	for (i = 0; i < peers; i++)
7226b3a37bSLogan Gunthorpe 		if (ntb->msi->peer_mws[i])
7326b3a37bSLogan Gunthorpe 			devm_iounmap(&ntb->dev, ntb->msi->peer_mws[i]);
7426b3a37bSLogan Gunthorpe 
7526b3a37bSLogan Gunthorpe 	devm_kfree(&ntb->dev, ntb->msi);
7626b3a37bSLogan Gunthorpe 	ntb->msi = NULL;
7726b3a37bSLogan Gunthorpe 	return ret;
7826b3a37bSLogan Gunthorpe }
7926b3a37bSLogan Gunthorpe EXPORT_SYMBOL(ntb_msi_init);
8026b3a37bSLogan Gunthorpe 
8126b3a37bSLogan Gunthorpe /**
8226b3a37bSLogan Gunthorpe  * ntb_msi_setup_mws() - Initialize the MSI inbound memory windows
8326b3a37bSLogan Gunthorpe  * @ntb:	NTB device context
8426b3a37bSLogan Gunthorpe  *
8526b3a37bSLogan Gunthorpe  * This function sets up the required inbound memory windows. It should be
8626b3a37bSLogan Gunthorpe  * called from a work function after a link up event.
8726b3a37bSLogan Gunthorpe  *
8826b3a37bSLogan Gunthorpe  * Over the entire network, this function will reserves the last N
8926b3a37bSLogan Gunthorpe  * inbound memory windows for each peer (where N is the number of peers).
9026b3a37bSLogan Gunthorpe  *
9126b3a37bSLogan Gunthorpe  * ntb_msi_init() must be called before this function.
9226b3a37bSLogan Gunthorpe  *
9326b3a37bSLogan Gunthorpe  * Return: Zero on success, otherwise a negative error number.
9426b3a37bSLogan Gunthorpe  */
ntb_msi_setup_mws(struct ntb_dev * ntb)9526b3a37bSLogan Gunthorpe int ntb_msi_setup_mws(struct ntb_dev *ntb)
9626b3a37bSLogan Gunthorpe {
9726b3a37bSLogan Gunthorpe 	struct msi_desc *desc;
9826b3a37bSLogan Gunthorpe 	u64 addr;
9926b3a37bSLogan Gunthorpe 	int peer, peer_widx;
10026b3a37bSLogan Gunthorpe 	resource_size_t addr_align, size_align, size_max;
10126b3a37bSLogan Gunthorpe 	resource_size_t mw_size = SZ_32K;
10226b3a37bSLogan Gunthorpe 	resource_size_t mw_min_size = mw_size;
10326b3a37bSLogan Gunthorpe 	int i;
10426b3a37bSLogan Gunthorpe 	int ret;
10526b3a37bSLogan Gunthorpe 
10626b3a37bSLogan Gunthorpe 	if (!ntb->msi)
10726b3a37bSLogan Gunthorpe 		return -EINVAL;
10826b3a37bSLogan Gunthorpe 
10968e31835SThomas Gleixner 	msi_lock_descs(&ntb->pdev->dev);
11068e31835SThomas Gleixner 	desc = msi_first_desc(&ntb->pdev->dev, MSI_DESC_ASSOCIATED);
11126b3a37bSLogan Gunthorpe 	addr = desc->msg.address_lo + ((uint64_t)desc->msg.address_hi << 32);
11268e31835SThomas Gleixner 	msi_unlock_descs(&ntb->pdev->dev);
11326b3a37bSLogan Gunthorpe 
11426b3a37bSLogan Gunthorpe 	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
11526b3a37bSLogan Gunthorpe 		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
11626b3a37bSLogan Gunthorpe 		if (peer_widx < 0)
11726b3a37bSLogan Gunthorpe 			return peer_widx;
11826b3a37bSLogan Gunthorpe 
11926b3a37bSLogan Gunthorpe 		ret = ntb_mw_get_align(ntb, peer, peer_widx, &addr_align,
12026b3a37bSLogan Gunthorpe 				       NULL, NULL);
12126b3a37bSLogan Gunthorpe 		if (ret)
12226b3a37bSLogan Gunthorpe 			return ret;
12326b3a37bSLogan Gunthorpe 
12426b3a37bSLogan Gunthorpe 		addr &= ~(addr_align - 1);
12526b3a37bSLogan Gunthorpe 	}
12626b3a37bSLogan Gunthorpe 
12726b3a37bSLogan Gunthorpe 	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
12826b3a37bSLogan Gunthorpe 		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
12926b3a37bSLogan Gunthorpe 		if (peer_widx < 0) {
13026b3a37bSLogan Gunthorpe 			ret = peer_widx;
13126b3a37bSLogan Gunthorpe 			goto error_out;
13226b3a37bSLogan Gunthorpe 		}
13326b3a37bSLogan Gunthorpe 
13426b3a37bSLogan Gunthorpe 		ret = ntb_mw_get_align(ntb, peer, peer_widx, NULL,
13526b3a37bSLogan Gunthorpe 				       &size_align, &size_max);
13626b3a37bSLogan Gunthorpe 		if (ret)
13726b3a37bSLogan Gunthorpe 			goto error_out;
13826b3a37bSLogan Gunthorpe 
13926b3a37bSLogan Gunthorpe 		mw_size = round_up(mw_size, size_align);
14026b3a37bSLogan Gunthorpe 		mw_size = max(mw_size, size_max);
14126b3a37bSLogan Gunthorpe 		if (mw_size < mw_min_size)
14226b3a37bSLogan Gunthorpe 			mw_min_size = mw_size;
14326b3a37bSLogan Gunthorpe 
14426b3a37bSLogan Gunthorpe 		ret = ntb_mw_set_trans(ntb, peer, peer_widx,
14526b3a37bSLogan Gunthorpe 				       addr, mw_size);
14626b3a37bSLogan Gunthorpe 		if (ret)
14726b3a37bSLogan Gunthorpe 			goto error_out;
14826b3a37bSLogan Gunthorpe 	}
14926b3a37bSLogan Gunthorpe 
15026b3a37bSLogan Gunthorpe 	ntb->msi->base_addr = addr;
15126b3a37bSLogan Gunthorpe 	ntb->msi->end_addr = addr + mw_min_size;
15226b3a37bSLogan Gunthorpe 
15326b3a37bSLogan Gunthorpe 	return 0;
15426b3a37bSLogan Gunthorpe 
15526b3a37bSLogan Gunthorpe error_out:
15626b3a37bSLogan Gunthorpe 	for (i = 0; i < peer; i++) {
15726b3a37bSLogan Gunthorpe 		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
15826b3a37bSLogan Gunthorpe 		if (peer_widx < 0)
15926b3a37bSLogan Gunthorpe 			continue;
16026b3a37bSLogan Gunthorpe 
16126b3a37bSLogan Gunthorpe 		ntb_mw_clear_trans(ntb, i, peer_widx);
16226b3a37bSLogan Gunthorpe 	}
16326b3a37bSLogan Gunthorpe 
16426b3a37bSLogan Gunthorpe 	return ret;
16526b3a37bSLogan Gunthorpe }
16626b3a37bSLogan Gunthorpe EXPORT_SYMBOL(ntb_msi_setup_mws);
16726b3a37bSLogan Gunthorpe 
16826b3a37bSLogan Gunthorpe /**
16926b3a37bSLogan Gunthorpe  * ntb_msi_clear_mws() - Clear all inbound memory windows
17026b3a37bSLogan Gunthorpe  * @ntb:	NTB device context
17126b3a37bSLogan Gunthorpe  *
17226b3a37bSLogan Gunthorpe  * This function tears down the resources used by ntb_msi_setup_mws().
17326b3a37bSLogan Gunthorpe  */
ntb_msi_clear_mws(struct ntb_dev * ntb)17426b3a37bSLogan Gunthorpe void ntb_msi_clear_mws(struct ntb_dev *ntb)
17526b3a37bSLogan Gunthorpe {
17626b3a37bSLogan Gunthorpe 	int peer;
17726b3a37bSLogan Gunthorpe 	int peer_widx;
17826b3a37bSLogan Gunthorpe 
17926b3a37bSLogan Gunthorpe 	for (peer = 0; peer < ntb_peer_port_count(ntb); peer++) {
18026b3a37bSLogan Gunthorpe 		peer_widx = ntb_peer_highest_mw_idx(ntb, peer);
18126b3a37bSLogan Gunthorpe 		if (peer_widx < 0)
18226b3a37bSLogan Gunthorpe 			continue;
18326b3a37bSLogan Gunthorpe 
18426b3a37bSLogan Gunthorpe 		ntb_mw_clear_trans(ntb, peer, peer_widx);
18526b3a37bSLogan Gunthorpe 	}
18626b3a37bSLogan Gunthorpe }
18726b3a37bSLogan Gunthorpe EXPORT_SYMBOL(ntb_msi_clear_mws);
18826b3a37bSLogan Gunthorpe 
18926b3a37bSLogan Gunthorpe struct ntb_msi_devres {
19026b3a37bSLogan Gunthorpe 	struct ntb_dev *ntb;
19126b3a37bSLogan Gunthorpe 	struct msi_desc *entry;
19226b3a37bSLogan Gunthorpe 	struct ntb_msi_desc *msi_desc;
19326b3a37bSLogan Gunthorpe };
19426b3a37bSLogan Gunthorpe 
ntb_msi_set_desc(struct ntb_dev * ntb,struct msi_desc * entry,struct ntb_msi_desc * msi_desc)19526b3a37bSLogan Gunthorpe static int ntb_msi_set_desc(struct ntb_dev *ntb, struct msi_desc *entry,
19626b3a37bSLogan Gunthorpe 			    struct ntb_msi_desc *msi_desc)
19726b3a37bSLogan Gunthorpe {
19826b3a37bSLogan Gunthorpe 	u64 addr;
19926b3a37bSLogan Gunthorpe 
20026b3a37bSLogan Gunthorpe 	addr = entry->msg.address_lo +
20126b3a37bSLogan Gunthorpe 		((uint64_t)entry->msg.address_hi << 32);
20226b3a37bSLogan Gunthorpe 
20326b3a37bSLogan Gunthorpe 	if (addr < ntb->msi->base_addr || addr >= ntb->msi->end_addr) {
20426b3a37bSLogan Gunthorpe 		dev_warn_once(&ntb->dev,
20526b3a37bSLogan Gunthorpe 			      "IRQ %d: MSI Address not within the memory window (%llx, [%llx %llx])\n",
20626b3a37bSLogan Gunthorpe 			      entry->irq, addr, ntb->msi->base_addr,
20726b3a37bSLogan Gunthorpe 			      ntb->msi->end_addr);
20826b3a37bSLogan Gunthorpe 		return -EFAULT;
20926b3a37bSLogan Gunthorpe 	}
21026b3a37bSLogan Gunthorpe 
21126b3a37bSLogan Gunthorpe 	msi_desc->addr_offset = addr - ntb->msi->base_addr;
21226b3a37bSLogan Gunthorpe 	msi_desc->data = entry->msg.data;
21326b3a37bSLogan Gunthorpe 
21426b3a37bSLogan Gunthorpe 	return 0;
21526b3a37bSLogan Gunthorpe }
21626b3a37bSLogan Gunthorpe 
ntb_msi_write_msg(struct msi_desc * entry,void * data)21726b3a37bSLogan Gunthorpe static void ntb_msi_write_msg(struct msi_desc *entry, void *data)
21826b3a37bSLogan Gunthorpe {
21926b3a37bSLogan Gunthorpe 	struct ntb_msi_devres *dr = data;
22026b3a37bSLogan Gunthorpe 
22126b3a37bSLogan Gunthorpe 	WARN_ON(ntb_msi_set_desc(dr->ntb, entry, dr->msi_desc));
22226b3a37bSLogan Gunthorpe 
22326b3a37bSLogan Gunthorpe 	if (dr->ntb->msi->desc_changed)
22426b3a37bSLogan Gunthorpe 		dr->ntb->msi->desc_changed(dr->ntb->ctx);
22526b3a37bSLogan Gunthorpe }
22626b3a37bSLogan Gunthorpe 
ntbm_msi_callback_release(struct device * dev,void * res)22726b3a37bSLogan Gunthorpe static void ntbm_msi_callback_release(struct device *dev, void *res)
22826b3a37bSLogan Gunthorpe {
22926b3a37bSLogan Gunthorpe 	struct ntb_msi_devres *dr = res;
23026b3a37bSLogan Gunthorpe 
23126b3a37bSLogan Gunthorpe 	dr->entry->write_msi_msg = NULL;
23226b3a37bSLogan Gunthorpe 	dr->entry->write_msi_msg_data = NULL;
23326b3a37bSLogan Gunthorpe }
23426b3a37bSLogan Gunthorpe 
ntbm_msi_setup_callback(struct ntb_dev * ntb,struct msi_desc * entry,struct ntb_msi_desc * msi_desc)23526b3a37bSLogan Gunthorpe static int ntbm_msi_setup_callback(struct ntb_dev *ntb, struct msi_desc *entry,
23626b3a37bSLogan Gunthorpe 				   struct ntb_msi_desc *msi_desc)
23726b3a37bSLogan Gunthorpe {
23826b3a37bSLogan Gunthorpe 	struct ntb_msi_devres *dr;
23926b3a37bSLogan Gunthorpe 
24026b3a37bSLogan Gunthorpe 	dr = devres_alloc(ntbm_msi_callback_release,
24126b3a37bSLogan Gunthorpe 			  sizeof(struct ntb_msi_devres), GFP_KERNEL);
24226b3a37bSLogan Gunthorpe 	if (!dr)
24326b3a37bSLogan Gunthorpe 		return -ENOMEM;
24426b3a37bSLogan Gunthorpe 
24526b3a37bSLogan Gunthorpe 	dr->ntb = ntb;
24626b3a37bSLogan Gunthorpe 	dr->entry = entry;
24726b3a37bSLogan Gunthorpe 	dr->msi_desc = msi_desc;
24826b3a37bSLogan Gunthorpe 
24926b3a37bSLogan Gunthorpe 	devres_add(&ntb->dev, dr);
25026b3a37bSLogan Gunthorpe 
25126b3a37bSLogan Gunthorpe 	dr->entry->write_msi_msg = ntb_msi_write_msg;
25226b3a37bSLogan Gunthorpe 	dr->entry->write_msi_msg_data = dr;
25326b3a37bSLogan Gunthorpe 
25426b3a37bSLogan Gunthorpe 	return 0;
25526b3a37bSLogan Gunthorpe }
25626b3a37bSLogan Gunthorpe 
25726b3a37bSLogan Gunthorpe /**
25826b3a37bSLogan Gunthorpe  * ntbm_msi_request_threaded_irq() - allocate an MSI interrupt
25926b3a37bSLogan Gunthorpe  * @ntb:	NTB device context
26026b3a37bSLogan Gunthorpe  * @handler:	Function to be called when the IRQ occurs
26126b3a37bSLogan Gunthorpe  * @thread_fn:  Function to be called in a threaded interrupt context. NULL
26226b3a37bSLogan Gunthorpe  *              for clients which handle everything in @handler
263e70dc094SYang Li  * @name:    An ascii name for the claiming device, dev_name(dev) if NULL
26426b3a37bSLogan Gunthorpe  * @dev_id:     A cookie passed back to the handler function
265e70dc094SYang Li  * @msi_desc:	MSI descriptor data which triggers the interrupt
26626b3a37bSLogan Gunthorpe  *
26726b3a37bSLogan Gunthorpe  * This function assigns an interrupt handler to an unused
26826b3a37bSLogan Gunthorpe  * MSI interrupt and returns the descriptor used to trigger
26926b3a37bSLogan Gunthorpe  * it. The descriptor can then be sent to a peer to trigger
27026b3a37bSLogan Gunthorpe  * the interrupt.
27126b3a37bSLogan Gunthorpe  *
27226b3a37bSLogan Gunthorpe  * The interrupt resource is managed with devres so it will
27326b3a37bSLogan Gunthorpe  * be automatically freed when the NTB device is torn down.
27426b3a37bSLogan Gunthorpe  *
27526b3a37bSLogan Gunthorpe  * If an IRQ allocated with this function needs to be freed
27626b3a37bSLogan Gunthorpe  * separately, ntbm_free_irq() must be used.
27726b3a37bSLogan Gunthorpe  *
27826b3a37bSLogan Gunthorpe  * Return: IRQ number assigned on success, otherwise a negative error number.
27926b3a37bSLogan Gunthorpe  */
ntbm_msi_request_threaded_irq(struct ntb_dev * ntb,irq_handler_t handler,irq_handler_t thread_fn,const char * name,void * dev_id,struct ntb_msi_desc * msi_desc)28026b3a37bSLogan Gunthorpe int ntbm_msi_request_threaded_irq(struct ntb_dev *ntb, irq_handler_t handler,
28126b3a37bSLogan Gunthorpe 				  irq_handler_t thread_fn,
28226b3a37bSLogan Gunthorpe 				  const char *name, void *dev_id,
28326b3a37bSLogan Gunthorpe 				  struct ntb_msi_desc *msi_desc)
28426b3a37bSLogan Gunthorpe {
28568e31835SThomas Gleixner 	struct device *dev = &ntb->pdev->dev;
28626b3a37bSLogan Gunthorpe 	struct msi_desc *entry;
28726b3a37bSLogan Gunthorpe 	int ret;
28826b3a37bSLogan Gunthorpe 
28926b3a37bSLogan Gunthorpe 	if (!ntb->msi)
29026b3a37bSLogan Gunthorpe 		return -EINVAL;
29126b3a37bSLogan Gunthorpe 
29268e31835SThomas Gleixner 	msi_lock_descs(dev);
29368e31835SThomas Gleixner 	msi_for_each_desc(entry, dev, MSI_DESC_ASSOCIATED) {
2941110918eSThomas Gleixner 		if (irq_has_action(entry->irq))
29526b3a37bSLogan Gunthorpe 			continue;
29626b3a37bSLogan Gunthorpe 
29726b3a37bSLogan Gunthorpe 		ret = devm_request_threaded_irq(&ntb->dev, entry->irq, handler,
29826b3a37bSLogan Gunthorpe 						thread_fn, 0, name, dev_id);
29926b3a37bSLogan Gunthorpe 		if (ret)
30026b3a37bSLogan Gunthorpe 			continue;
30126b3a37bSLogan Gunthorpe 
30226b3a37bSLogan Gunthorpe 		if (ntb_msi_set_desc(ntb, entry, msi_desc)) {
30326b3a37bSLogan Gunthorpe 			devm_free_irq(&ntb->dev, entry->irq, dev_id);
30426b3a37bSLogan Gunthorpe 			continue;
30526b3a37bSLogan Gunthorpe 		}
30626b3a37bSLogan Gunthorpe 
30726b3a37bSLogan Gunthorpe 		ret = ntbm_msi_setup_callback(ntb, entry, msi_desc);
30826b3a37bSLogan Gunthorpe 		if (ret) {
30926b3a37bSLogan Gunthorpe 			devm_free_irq(&ntb->dev, entry->irq, dev_id);
31068e31835SThomas Gleixner 			goto unlock;
31168e31835SThomas Gleixner 		}
31268e31835SThomas Gleixner 
31368e31835SThomas Gleixner 		ret = entry->irq;
31468e31835SThomas Gleixner 		goto unlock;
31568e31835SThomas Gleixner 	}
31668e31835SThomas Gleixner 	ret = -ENODEV;
31768e31835SThomas Gleixner 
31868e31835SThomas Gleixner unlock:
31968e31835SThomas Gleixner 	msi_unlock_descs(dev);
32026b3a37bSLogan Gunthorpe 	return ret;
32126b3a37bSLogan Gunthorpe }
32226b3a37bSLogan Gunthorpe EXPORT_SYMBOL(ntbm_msi_request_threaded_irq);
32326b3a37bSLogan Gunthorpe 
ntbm_msi_callback_match(struct device * dev,void * res,void * data)32426b3a37bSLogan Gunthorpe static int ntbm_msi_callback_match(struct device *dev, void *res, void *data)
32526b3a37bSLogan Gunthorpe {
32626b3a37bSLogan Gunthorpe 	struct ntb_dev *ntb = dev_ntb(dev);
32726b3a37bSLogan Gunthorpe 	struct ntb_msi_devres *dr = res;
32826b3a37bSLogan Gunthorpe 
32926b3a37bSLogan Gunthorpe 	return dr->ntb == ntb && dr->entry == data;
33026b3a37bSLogan Gunthorpe }
33126b3a37bSLogan Gunthorpe 
33226b3a37bSLogan Gunthorpe /**
33326b3a37bSLogan Gunthorpe  * ntbm_msi_free_irq() - free an interrupt
33426b3a37bSLogan Gunthorpe  * @ntb:	NTB device context
33526b3a37bSLogan Gunthorpe  * @irq:	Interrupt line to free
33626b3a37bSLogan Gunthorpe  * @dev_id:	Device identity to free
33726b3a37bSLogan Gunthorpe  *
33826b3a37bSLogan Gunthorpe  * This function should be used to manually free IRQs allocated with
33926b3a37bSLogan Gunthorpe  * ntbm_request_[threaded_]irq().
34026b3a37bSLogan Gunthorpe  */
ntbm_msi_free_irq(struct ntb_dev * ntb,unsigned int irq,void * dev_id)34126b3a37bSLogan Gunthorpe void ntbm_msi_free_irq(struct ntb_dev *ntb, unsigned int irq, void *dev_id)
34226b3a37bSLogan Gunthorpe {
34326b3a37bSLogan Gunthorpe 	struct msi_desc *entry = irq_get_msi_desc(irq);
34426b3a37bSLogan Gunthorpe 
34526b3a37bSLogan Gunthorpe 	entry->write_msi_msg = NULL;
34626b3a37bSLogan Gunthorpe 	entry->write_msi_msg_data = NULL;
34726b3a37bSLogan Gunthorpe 
34826b3a37bSLogan Gunthorpe 	WARN_ON(devres_destroy(&ntb->dev, ntbm_msi_callback_release,
34926b3a37bSLogan Gunthorpe 			       ntbm_msi_callback_match, entry));
35026b3a37bSLogan Gunthorpe 
35126b3a37bSLogan Gunthorpe 	devm_free_irq(&ntb->dev, irq, dev_id);
35226b3a37bSLogan Gunthorpe }
35326b3a37bSLogan Gunthorpe EXPORT_SYMBOL(ntbm_msi_free_irq);
35426b3a37bSLogan Gunthorpe 
35526b3a37bSLogan Gunthorpe /**
35626b3a37bSLogan Gunthorpe  * ntb_msi_peer_trigger() - Trigger an interrupt handler on a peer
35726b3a37bSLogan Gunthorpe  * @ntb:	NTB device context
35826b3a37bSLogan Gunthorpe  * @peer:	Peer index
35926b3a37bSLogan Gunthorpe  * @desc:	MSI descriptor data which triggers the interrupt
36026b3a37bSLogan Gunthorpe  *
36126b3a37bSLogan Gunthorpe  * This function triggers an interrupt on a peer. It requires
36226b3a37bSLogan Gunthorpe  * the descriptor structure to have been passed from that peer
36326b3a37bSLogan Gunthorpe  * by some other means.
36426b3a37bSLogan Gunthorpe  *
36526b3a37bSLogan Gunthorpe  * Return: Zero on success, otherwise a negative error number.
36626b3a37bSLogan Gunthorpe  */
ntb_msi_peer_trigger(struct ntb_dev * ntb,int peer,struct ntb_msi_desc * desc)36726b3a37bSLogan Gunthorpe int ntb_msi_peer_trigger(struct ntb_dev *ntb, int peer,
36826b3a37bSLogan Gunthorpe 			 struct ntb_msi_desc *desc)
36926b3a37bSLogan Gunthorpe {
37026b3a37bSLogan Gunthorpe 	int idx;
37126b3a37bSLogan Gunthorpe 
37226b3a37bSLogan Gunthorpe 	if (!ntb->msi)
37326b3a37bSLogan Gunthorpe 		return -EINVAL;
37426b3a37bSLogan Gunthorpe 
37526b3a37bSLogan Gunthorpe 	idx = desc->addr_offset / sizeof(*ntb->msi->peer_mws[peer]);
37626b3a37bSLogan Gunthorpe 
37726b3a37bSLogan Gunthorpe 	iowrite32(desc->data, &ntb->msi->peer_mws[peer][idx]);
37826b3a37bSLogan Gunthorpe 
37926b3a37bSLogan Gunthorpe 	return 0;
38026b3a37bSLogan Gunthorpe }
38126b3a37bSLogan Gunthorpe EXPORT_SYMBOL(ntb_msi_peer_trigger);
38226b3a37bSLogan Gunthorpe 
38326b3a37bSLogan Gunthorpe /**
38426b3a37bSLogan Gunthorpe  * ntb_msi_peer_addr() - Get the DMA address to trigger a peer's MSI interrupt
38526b3a37bSLogan Gunthorpe  * @ntb:	NTB device context
38626b3a37bSLogan Gunthorpe  * @peer:	Peer index
38726b3a37bSLogan Gunthorpe  * @desc:	MSI descriptor data which triggers the interrupt
38826b3a37bSLogan Gunthorpe  * @msi_addr:   Physical address to trigger the interrupt
38926b3a37bSLogan Gunthorpe  *
39026b3a37bSLogan Gunthorpe  * This function allows using DMA engines to trigger an interrupt
39126b3a37bSLogan Gunthorpe  * (for example, trigger an interrupt to process the data after
39226b3a37bSLogan Gunthorpe  * sending it). To trigger the interrupt, write @desc.data to the address
39326b3a37bSLogan Gunthorpe  * returned in @msi_addr
39426b3a37bSLogan Gunthorpe  *
39526b3a37bSLogan Gunthorpe  * Return: Zero on success, otherwise a negative error number.
39626b3a37bSLogan Gunthorpe  */
ntb_msi_peer_addr(struct ntb_dev * ntb,int peer,struct ntb_msi_desc * desc,phys_addr_t * msi_addr)39726b3a37bSLogan Gunthorpe int ntb_msi_peer_addr(struct ntb_dev *ntb, int peer,
39826b3a37bSLogan Gunthorpe 		      struct ntb_msi_desc *desc,
39926b3a37bSLogan Gunthorpe 		      phys_addr_t *msi_addr)
40026b3a37bSLogan Gunthorpe {
40126b3a37bSLogan Gunthorpe 	int peer_widx = ntb_peer_mw_count(ntb) - 1 - peer;
40226b3a37bSLogan Gunthorpe 	phys_addr_t mw_phys_addr;
40326b3a37bSLogan Gunthorpe 	int ret;
40426b3a37bSLogan Gunthorpe 
40526b3a37bSLogan Gunthorpe 	ret = ntb_peer_mw_get_addr(ntb, peer_widx, &mw_phys_addr, NULL);
40626b3a37bSLogan Gunthorpe 	if (ret)
40726b3a37bSLogan Gunthorpe 		return ret;
40826b3a37bSLogan Gunthorpe 
40926b3a37bSLogan Gunthorpe 	if (msi_addr)
41026b3a37bSLogan Gunthorpe 		*msi_addr = mw_phys_addr + desc->addr_offset;
41126b3a37bSLogan Gunthorpe 
41226b3a37bSLogan Gunthorpe 	return 0;
41326b3a37bSLogan Gunthorpe }
41426b3a37bSLogan Gunthorpe EXPORT_SYMBOL(ntb_msi_peer_addr);
415