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