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