// SPDX-License-Identifier: GPL-2.0 /* * Endpoint Function Driver to implement Non-Transparent Bridge functionality * * Copyright (C) 2020 Texas Instruments * Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
/* * The PCI NTB function driver configures the SoC with multiple PCIe Endpoint * (EP) controller instances (see diagram below) in such a way that * transactions from one EP controller are routed to the other EP controller. * Once PCI NTB function driver configures the SoC with multiple EP instances, * HOST1 and HOST2 can communicate with each other using SoC as a bridge. * * +-------------+ +-------------+ * | | | | * | HOST1 | | HOST2 | * | | | | * +------^------+ +------^------+ * | | * | | * +---------|-------------------------------------------------|---------+ * | +------v------+ +------v------+ | * | | | | | | * | | EP | | EP | | * | | CONTROLLER1 | | CONTROLLER2 | | * | | <-----------------------------------> | | * | | | | | | * | | | | | | * | | | SoC With Multiple EP Instances | | | * | | | (Configured using NTB Function) | | | * | +-------------+ +-------------+ | * +---------------------------------------------------------------------+
*/
/** * epf_ntb_link_up() - Raise link_up interrupt to both the hosts * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @link_up: true or false indicating Link is UP or Down * * Once NTB function in HOST1 and the NTB function in HOST2 invoke * ntb_link_enable(), this NTB function driver will trigger a link event to * the NTB client in both the hosts.
*/ staticint epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
{ enum pci_epc_interface_type type; struct epf_ntb_epc *ntb_epc; struct epf_ntb_ctrl *ctrl; unsignedint irq_type; struct pci_epc *epc;
u8 func_no, vfunc_no; bool is_msix; int ret;
for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
ntb_epc = ntb->epc[type];
epc = ntb_epc->epc;
func_no = ntb_epc->func_no;
vfunc_no = ntb_epc->vfunc_no;
is_msix = ntb_epc->is_msix;
ctrl = ntb_epc->reg; if (link_up)
ctrl->link_status |= LINK_STATUS_UP; else
ctrl->link_status &= ~LINK_STATUS_UP;
irq_type = is_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
ret = pci_epc_raise_irq(epc, func_no, vfunc_no, irq_type, 1); if (ret) {
dev_err(&epc->dev, "%s intf: Failed to raise Link Up IRQ\n",
pci_epc_interface_string(type)); return ret;
}
}
return 0;
}
/** * epf_ntb_configure_mw() - Configure the Outbound Address Space for one host * to access the memory window of other host * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * @mw: Index of the memory window (either 0, 1, 2 or 3) * * +-----------------+ +---->+----------------+-----------+-----------------+ * | BAR0 | | | Doorbell 1 +-----------> MSI|X ADDRESS 1 | * +-----------------+ | +----------------+ +-----------------+ * | BAR1 | | | Doorbell 2 +---------+ | | * +-----------------+----+ +----------------+ | | | * | BAR2 | | Doorbell 3 +-------+ | +-----------------+ * +-----------------+----+ +----------------+ | +-> MSI|X ADDRESS 2 | * | BAR3 | | | Doorbell 4 +-----+ | +-----------------+ * +-----------------+ | |----------------+ | | | | * | BAR4 | | | | | | +-----------------+ * +-----------------+ | | MW1 +---+ | +-->+ MSI|X ADDRESS 3|| * | BAR5 | | | | | | +-----------------+ * +-----------------+ +---->-----------------+ | | | | * EP CONTROLLER 1 | | | | +-----------------+ * | | | +---->+ MSI|X ADDRESS 4 | * +----------------+ | +-----------------+ * (A) EP CONTROLLER 2 | | | * (OB SPACE) | | | * +-------> MW1 | * | | * | | * (B) +-----------------+ * | | * | | * | | * | | * | | * +-----------------+ * PCI Address Space * (Managed by HOST2) * * This function performs stage (B) in the above diagram (see MW1) i.e., map OB * address space of memory window to PCI address space. * * This operation requires 3 parameters * 1) Address in the outbound address space * 2) Address in the PCI Address space * 3) Size of the address region to be mapped * * The address in the outbound address space (for MW1, MW2, MW3 and MW4) is * stored in epf_bar corresponding to BAR_DB_MW1 for MW1 and BAR_MW2, BAR_MW3 * BAR_MW4 for rest of the BARs of epf_ntb_epc that is connected to HOST1. This * is populated in epf_ntb_alloc_peer_mem() in this driver. * * The address and size of the PCI address region that has to be mapped would * be provided by HOST2 in ctrl->addr and ctrl->size of epf_ntb_epc that is * connected to HOST2. * * Please note Memory window1 (MW1) and Doorbell registers together will be * mapped to a single BAR (BAR2) above for 32-bit BARs. The exact BAR that's * used for Memory window (MW) can be obtained from epf_ntb_bar[BAR_DB_MW1], * epf_ntb_bar[BAR_MW2], epf_ntb_bar[BAR_MW2], epf_ntb_bar[BAR_MW2].
*/ staticint epf_ntb_configure_mw(struct epf_ntb *ntb, enum pci_epc_interface_type type, u32 mw)
{ struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; struct pci_epf_bar *peer_epf_bar; enum pci_barno peer_barno; struct epf_ntb_ctrl *ctrl;
phys_addr_t phys_addr;
u8 func_no, vfunc_no; struct pci_epc *epc;
u64 addr, size; int ret = 0;
/** * epf_ntb_configure_msi() - Map OB address space to MSI address * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * @db_count: Number of doorbell interrupts to map * *+-----------------+ +----->+----------------+-----------+-----------------+ *| BAR0 | | | Doorbell 1 +---+-------> MSI ADDRESS | *+-----------------+ | +----------------+ | +-----------------+ *| BAR1 | | | Doorbell 2 +---+ | | *+-----------------+----+ +----------------+ | | | *| BAR2 | | Doorbell 3 +---+ | | *+-----------------+----+ +----------------+ | | | *| BAR3 | | | Doorbell 4 +---+ | | *+-----------------+ | |----------------+ | | *| BAR4 | | | | | | *+-----------------+ | | MW1 | | | *| BAR5 | | | | | | *+-----------------+ +----->-----------------+ | | * EP CONTROLLER 1 | | | | * | | | | * +----------------+ +-----------------+ * (A) EP CONTROLLER 2 | | * (OB SPACE) | | * | MW1 | * | | * | | * (B) +-----------------+ * | | * | | * | | * | | * | | * +-----------------+ * PCI Address Space * (Managed by HOST2) * * * This function performs stage (B) in the above diagram (see Doorbell 1, * Doorbell 2, Doorbell 3, Doorbell 4) i.e map OB address space corresponding to * doorbell to MSI address in PCI address space. * * This operation requires 3 parameters * 1) Address reserved for doorbell in the outbound address space * 2) MSI-X address in the PCIe Address space * 3) Number of MSI-X interrupts that has to be configured * * The address in the outbound address space (for the Doorbell) is stored in * epf_bar corresponding to BAR_DB_MW1 of epf_ntb_epc that is connected to * HOST1. This is populated in epf_ntb_alloc_peer_mem() in this driver along * with address for MW1. * * pci_epc_map_msi_irq() takes the MSI address from MSI capability register * and maps the OB address (obtained in epf_ntb_alloc_peer_mem()) to the MSI * address. * * epf_ntb_configure_msi() also stores the MSI data to raise each interrupt * in db_data of the peer's control region. This helps the peer to raise * doorbell of the other host by writing db_data to the BAR corresponding to * BAR_DB_MW1.
*/ staticint epf_ntb_configure_msi(struct epf_ntb *ntb, enum pci_epc_interface_type type, u16 db_count)
{ struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
u32 db_entry_size, db_data, db_offset; struct pci_epf_bar *peer_epf_bar; struct epf_ntb_ctrl *peer_ctrl; enum pci_barno peer_barno;
phys_addr_t phys_addr;
u8 func_no, vfunc_no; struct pci_epc *epc; int ret, i;
ret = pci_epc_map_msi_irq(epc, func_no, vfunc_no, phys_addr, db_count,
db_entry_size, &db_data, &db_offset); if (ret) {
dev_err(&epc->dev, "%s intf: Failed to map MSI IRQ\n",
pci_epc_interface_string(type)); return ret;
}
for (i = 0; i < db_count; i++) {
peer_ctrl->db_data[i] = db_data | i;
peer_ctrl->db_offset[i] = db_offset;
}
return 0;
}
/** * epf_ntb_configure_msix() - Map OB address space to MSI-X address * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * @db_count: Number of doorbell interrupts to map * *+-----------------+ +----->+----------------+-----------+-----------------+ *| BAR0 | | | Doorbell 1 +-----------> MSI-X ADDRESS 1 | *+-----------------+ | +----------------+ +-----------------+ *| BAR1 | | | Doorbell 2 +---------+ | | *+-----------------+----+ +----------------+ | | | *| BAR2 | | Doorbell 3 +-------+ | +-----------------+ *+-----------------+----+ +----------------+ | +-> MSI-X ADDRESS 2 | *| BAR3 | | | Doorbell 4 +-----+ | +-----------------+ *+-----------------+ | |----------------+ | | | | *| BAR4 | | | | | | +-----------------+ *+-----------------+ | | MW1 + | +-->+ MSI-X ADDRESS 3|| *| BAR5 | | | | | +-----------------+ *+-----------------+ +----->-----------------+ | | | * EP CONTROLLER 1 | | | +-----------------+ * | | +---->+ MSI-X ADDRESS 4 | * +----------------+ +-----------------+ * (A) EP CONTROLLER 2 | | * (OB SPACE) | | * | MW1 | * | | * | | * (B) +-----------------+ * | | * | | * | | * | | * | | * +-----------------+ * PCI Address Space * (Managed by HOST2) * * This function performs stage (B) in the above diagram (see Doorbell 1, * Doorbell 2, Doorbell 3, Doorbell 4) i.e map OB address space corresponding to * doorbell to MSI-X address in PCI address space. * * This operation requires 3 parameters * 1) Address reserved for doorbell in the outbound address space * 2) MSI-X address in the PCIe Address space * 3) Number of MSI-X interrupts that has to be configured * * The address in the outbound address space (for the Doorbell) is stored in * epf_bar corresponding to BAR_DB_MW1 of epf_ntb_epc that is connected to * HOST1. This is populated in epf_ntb_alloc_peer_mem() in this driver along * with address for MW1. * * The MSI-X address is in the MSI-X table of EP CONTROLLER 2 and * the count of doorbell is in ctrl->argument of epf_ntb_epc that is connected * to HOST2. MSI-X table is stored memory mapped to ntb_epc->msix_bar and the * offset is in ntb_epc->msix_table_offset. From this epf_ntb_configure_msix() * gets the MSI-X address and data. * * epf_ntb_configure_msix() also stores the MSI-X data to raise each interrupt * in db_data of the peer's control region. This helps the peer to raise * doorbell of the other host by writing db_data to the BAR corresponding to * BAR_DB_MW1.
*/ staticint epf_ntb_configure_msix(struct epf_ntb *ntb, enum pci_epc_interface_type type,
u16 db_count)
{ conststruct pci_epc_features *epc_features; struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; struct pci_epf_bar *peer_epf_bar, *epf_bar; struct pci_epf_msix_tbl *msix_tbl; struct epf_ntb_ctrl *peer_ctrl;
u32 db_entry_size, msg_data; enum pci_barno peer_barno;
phys_addr_t phys_addr;
u8 func_no, vfunc_no; struct pci_epc *epc;
size_t align;
u64 msg_addr; int ret, i;
for (i = 0; i < db_count; i++) {
msg_addr = ALIGN_DOWN(msix_tbl[i].msg_addr, align);
msg_data = msix_tbl[i].msg_data;
ret = pci_epc_map_addr(epc, func_no, vfunc_no, phys_addr, msg_addr,
db_entry_size); if (ret) {
dev_err(&epc->dev, "%s intf: Failed to configure MSI-X IRQ\n",
pci_epc_interface_string(type)); return ret;
}
phys_addr = phys_addr + db_entry_size;
peer_ctrl->db_data[i] = msg_data;
peer_ctrl->db_offset[i] = msix_tbl[i].msg_addr & (align - 1);
}
ntb_epc->is_msix = true;
return 0;
}
/** * epf_ntb_configure_db() - Configure the Outbound Address Space for one host * to ring the doorbell of other host * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * @db_count: Count of the number of doorbells that has to be configured * @msix: Indicates whether MSI-X or MSI should be used * * Invokes epf_ntb_configure_msix() or epf_ntb_configure_msi() required for * one HOST to ring the doorbell of other HOST.
*/ staticint epf_ntb_configure_db(struct epf_ntb *ntb, enum pci_epc_interface_type type,
u16 db_count, bool msix)
{ struct epf_ntb_epc *ntb_epc; struct pci_epc *epc; int ret;
if (db_count > MAX_DB_COUNT) return -EINVAL;
ntb_epc = ntb->epc[type];
epc = ntb_epc->epc;
if (msix)
ret = epf_ntb_configure_msix(ntb, type, db_count); else
ret = epf_ntb_configure_msi(ntb, type, db_count);
if (ret)
dev_err(&epc->dev, "%s intf: Failed to configure DB\n",
pci_epc_interface_string(type));
return ret;
}
/** * epf_ntb_teardown_db() - Unmap address in OB address space to MSI/MSI-X * address * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * * Invoke pci_epc_unmap_addr() to unmap OB address to MSI/MSI-X address.
*/ staticvoid
epf_ntb_teardown_db(struct epf_ntb *ntb, enum pci_epc_interface_type type)
{ struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; struct pci_epf_bar *peer_epf_bar; enum pci_barno peer_barno;
phys_addr_t phys_addr;
u8 func_no, vfunc_no; struct pci_epc *epc;
/** * epf_ntb_cmd_handler() - Handle commands provided by the NTB Host * @work: work_struct for the two epf_ntb_epc (PRIMARY and SECONDARY) * * Workqueue function that gets invoked for the two epf_ntb_epc * periodically (once every 5ms) to see if it has received any commands * from NTB host. The host can send commands to configure doorbell or * configure memory window or to update link status.
*/ staticvoid epf_ntb_cmd_handler(struct work_struct *work)
{ enum pci_epc_interface_type type; struct epf_ntb_epc *ntb_epc; struct epf_ntb_ctrl *ctrl;
u32 command, argument; struct epf_ntb *ntb; struct device *dev;
u16 db_count; bool is_msix; int ret;
/** * epf_ntb_peer_spad_bar_clear() - Clear Peer Scratchpad BAR * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound * address. * *+-----------------+------->+------------------+ +-----------------+ *| BAR0 | | CONFIG REGION | | BAR0 | *+-----------------+----+ +------------------+<-------+-----------------+ *| BAR1 | | |SCRATCHPAD REGION | | BAR1 | *+-----------------+ +-->+------------------+<-------+-----------------+ *| BAR2 | Local Memory | BAR2 | *+-----------------+ +-----------------+ *| BAR3 | | BAR3 | *+-----------------+ +-----------------+ *| BAR4 | | BAR4 | *+-----------------+ +-----------------+ *| BAR5 | | BAR5 | *+-----------------+ +-----------------+ * EP CONTROLLER 1 EP CONTROLLER 2 * * Clear BAR1 of EP CONTROLLER 2 which contains the HOST2's peer scratchpad * region. While BAR1 is the default peer scratchpad BAR, an NTB could have * other BARs for peer scratchpad (because of 64-bit BARs or reserved BARs). * This function can get the exact BAR used for peer scratchpad from * epf_ntb_bar[BAR_PEER_SPAD]. * * Since HOST2's peer scratchpad is also HOST1's self scratchpad, this function * gets the address of peer scratchpad from * peer_ntb_epc->epf_ntb_bar[BAR_CONFIG].
*/ staticvoid epf_ntb_peer_spad_bar_clear(struct epf_ntb_epc *ntb_epc)
{ struct pci_epf_bar *epf_bar; enum pci_barno barno;
u8 func_no, vfunc_no; struct pci_epc *epc;
/** * epf_ntb_peer_spad_bar_set() - Set peer scratchpad BAR * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * *+-----------------+------->+------------------+ +-----------------+ *| BAR0 | | CONFIG REGION | | BAR0 | *+-----------------+----+ +------------------+<-------+-----------------+ *| BAR1 | | |SCRATCHPAD REGION | | BAR1 | *+-----------------+ +-->+------------------+<-------+-----------------+ *| BAR2 | Local Memory | BAR2 | *+-----------------+ +-----------------+ *| BAR3 | | BAR3 | *+-----------------+ +-----------------+ *| BAR4 | | BAR4 | *+-----------------+ +-----------------+ *| BAR5 | | BAR5 | *+-----------------+ +-----------------+ * EP CONTROLLER 1 EP CONTROLLER 2 * * Set BAR1 of EP CONTROLLER 2 which contains the HOST2's peer scratchpad * region. While BAR1 is the default peer scratchpad BAR, an NTB could have * other BARs for peer scratchpad (because of 64-bit BARs or reserved BARs). * This function can get the exact BAR used for peer scratchpad from * epf_ntb_bar[BAR_PEER_SPAD]. * * Since HOST2's peer scratchpad is also HOST1's self scratchpad, this function * gets the address of peer scratchpad from * peer_ntb_epc->epf_ntb_bar[BAR_CONFIG].
*/ staticint epf_ntb_peer_spad_bar_set(struct epf_ntb *ntb, enum pci_epc_interface_type type)
{ struct epf_ntb_epc *peer_ntb_epc, *ntb_epc; struct pci_epf_bar *peer_epf_bar, *epf_bar; enum pci_barno peer_barno, barno;
u32 peer_spad_offset;
u8 func_no, vfunc_no; struct pci_epc *epc; struct device *dev; int ret;
ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar); if (ret) {
dev_err(dev, "%s intf: peer SPAD BAR set failed\n",
pci_epc_interface_string(type)); return ret;
}
return 0;
}
/** * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound * address. * * +-----------------+------->+------------------+ +-----------------+ * | BAR0 | | CONFIG REGION | | BAR0 | * +-----------------+----+ +------------------+<-------+-----------------+ * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | * +-----------------+ +-->+------------------+<-------+-----------------+ * | BAR2 | Local Memory | BAR2 | * +-----------------+ +-----------------+ * | BAR3 | | BAR3 | * +-----------------+ +-----------------+ * | BAR4 | | BAR4 | * +-----------------+ +-----------------+ * | BAR5 | | BAR5 | * +-----------------+ +-----------------+ * EP CONTROLLER 1 EP CONTROLLER 2 * * Clear BAR0 of EP CONTROLLER 1 which contains the HOST1's config and * self scratchpad region (removes inbound ATU configuration). While BAR0 is * the default self scratchpad BAR, an NTB could have other BARs for self * scratchpad (because of reserved BARs). This function can get the exact BAR * used for self scratchpad from epf_ntb_bar[BAR_CONFIG]. * * Please note the self scratchpad region and config region is combined to * a single region and mapped using the same BAR. Also note HOST2's peer * scratchpad is HOST1's self scratchpad.
*/ staticvoid epf_ntb_config_sspad_bar_clear(struct epf_ntb_epc *ntb_epc)
{ struct pci_epf_bar *epf_bar; enum pci_barno barno;
u8 func_no, vfunc_no; struct pci_epc *epc;
/** * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR * @ntb_epc: EPC associated with one of the HOST which holds peer's outbound * address. * * +-----------------+------->+------------------+ +-----------------+ * | BAR0 | | CONFIG REGION | | BAR0 | * +-----------------+----+ +------------------+<-------+-----------------+ * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | * +-----------------+ +-->+------------------+<-------+-----------------+ * | BAR2 | Local Memory | BAR2 | * +-----------------+ +-----------------+ * | BAR3 | | BAR3 | * +-----------------+ +-----------------+ * | BAR4 | | BAR4 | * +-----------------+ +-----------------+ * | BAR5 | | BAR5 | * +-----------------+ +-----------------+ * EP CONTROLLER 1 EP CONTROLLER 2 * * Map BAR0 of EP CONTROLLER 1 which contains the HOST1's config and * self scratchpad region. While BAR0 is the default self scratchpad BAR, an * NTB could have other BARs for self scratchpad (because of reserved BARs). * This function can get the exact BAR used for self scratchpad from * epf_ntb_bar[BAR_CONFIG]. * * Please note the self scratchpad region and config region is combined to * a single region and mapped using the same BAR. Also note HOST2's peer * scratchpad is HOST1's self scratchpad.
*/ staticint epf_ntb_config_sspad_bar_set(struct epf_ntb_epc *ntb_epc)
{ struct pci_epf_bar *epf_bar; enum pci_barno barno;
u8 func_no, vfunc_no; struct epf_ntb *ntb; struct pci_epc *epc; struct device *dev; int ret;
ret = pci_epc_set_bar(epc, func_no, vfunc_no, epf_bar); if (ret) {
dev_err(dev, "%s inft: Config/Status/SPAD BAR set failed\n",
pci_epc_interface_string(ntb_epc->type)); return ret;
}
return 0;
}
/** * epf_ntb_config_spad_bar_free() - Free the physical memory associated with * config + scratchpad region * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * * +-----------------+------->+------------------+ +-----------------+ * | BAR0 | | CONFIG REGION | | BAR0 | * +-----------------+----+ +------------------+<-------+-----------------+ * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | * +-----------------+ +-->+------------------+<-------+-----------------+ * | BAR2 | Local Memory | BAR2 | * +-----------------+ +-----------------+ * | BAR3 | | BAR3 | * +-----------------+ +-----------------+ * | BAR4 | | BAR4 | * +-----------------+ +-----------------+ * | BAR5 | | BAR5 | * +-----------------+ +-----------------+ * EP CONTROLLER 1 EP CONTROLLER 2 * * Free the Local Memory mentioned in the above diagram. After invoking this * function, any of config + self scratchpad region of HOST1 or peer scratchpad * region of HOST2 should not be accessed.
*/ staticvoid epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
{ enum pci_epc_interface_type type; struct epf_ntb_epc *ntb_epc; enum pci_barno barno; struct pci_epf *epf;
epf = ntb->epf; for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
ntb_epc = ntb->epc[type];
barno = ntb_epc->epf_ntb_bar[BAR_CONFIG]; if (ntb_epc->reg)
pci_epf_free_space(epf, ntb_epc->reg, barno, type);
}
}
/** * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad * region * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * * +-----------------+------->+------------------+ +-----------------+ * | BAR0 | | CONFIG REGION | | BAR0 | * +-----------------+----+ +------------------+<-------+-----------------+ * | BAR1 | | |SCRATCHPAD REGION | | BAR1 | * +-----------------+ +-->+------------------+<-------+-----------------+ * | BAR2 | Local Memory | BAR2 | * +-----------------+ +-----------------+ * | BAR3 | | BAR3 | * +-----------------+ +-----------------+ * | BAR4 | | BAR4 | * +-----------------+ +-----------------+ * | BAR5 | | BAR5 | * +-----------------+ +-----------------+ * EP CONTROLLER 1 EP CONTROLLER 2 * * Allocate the Local Memory mentioned in the above diagram. The size of * CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION * is obtained from "spad-count" configfs entry. * * The size of both config region and scratchpad region has to be aligned, * since the scratchpad region will also be mapped as PEER SCRATCHPAD of * other host using a separate BAR.
*/ staticint epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb, enum pci_epc_interface_type type)
{ conststruct pci_epc_features *peer_epc_features, *epc_features; struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
size_t msix_table_size, pba_size, align; enum pci_barno peer_barno, barno; struct epf_ntb_ctrl *ctrl;
u32 spad_size, ctrl_size;
u64 size, peer_size; struct pci_epf *epf; struct device *dev; bool msix_capable;
u32 spad_count; void *base;
epf = ntb->epf;
dev = &epf->dev;
ntb_epc = ntb->epc[type];
if (peer_size) { if (peer_size < spad_size)
spad_count = peer_size / 4;
spad_size = peer_size;
}
/* * In order to make sure SPAD offset is aligned to its size, * expand control region size to the size of SPAD if SPAD size * is greater than control region size.
*/ if (spad_size > ctrl_size)
ctrl_size = spad_size;
/** * epf_ntb_config_spad_bar_alloc_interface() - Allocate memory for config + * scratchpad region for each of PRIMARY and SECONDARY interface * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * * Wrapper for epf_ntb_config_spad_bar_alloc() which allocates memory for * config + scratchpad region for a specific interface
*/ staticint epf_ntb_config_spad_bar_alloc_interface(struct epf_ntb *ntb)
{ enum pci_epc_interface_type type; struct device *dev; int ret;
dev = &ntb->epf->dev;
for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
ret = epf_ntb_config_spad_bar_alloc(ntb, type); if (ret) {
dev_err(dev, "%s intf: Config/SPAD BAR alloc failed\n",
pci_epc_interface_string(type)); return ret;
}
}
/** * epf_ntb_db_mw_bar_cleanup() - Clear doorbell/memory BAR and free memory * allocated in peers outbound address space * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * * Wrapper for epf_ntb_db_mw_bar_clear() to clear HOST1's BAR and * epf_ntb_free_peer_mem() which frees up HOST2 outbound memory.
*/ staticvoid epf_ntb_db_mw_bar_cleanup(struct epf_ntb *ntb, enum pci_epc_interface_type type)
{ struct epf_ntb_epc *peer_ntb_epc, *ntb_epc;
/** * epf_ntb_epc_destroy_interface() - Cleanup NTB EPC interface * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * * Unbind NTB function device from EPC and relinquish reference to pci_epc * for each of the interface.
*/ staticvoid epf_ntb_epc_destroy_interface(struct epf_ntb *ntb, enum pci_epc_interface_type type)
{ struct epf_ntb_epc *ntb_epc; struct pci_epc *epc; struct pci_epf *epf;
/** * epf_ntb_epc_destroy() - Cleanup NTB EPC interface * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces
*/ staticvoid epf_ntb_epc_destroy(struct epf_ntb *ntb)
{ enum pci_epc_interface_type type;
for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++)
epf_ntb_epc_destroy_interface(ntb, type);
}
/** * epf_ntb_epc_create_interface() - Create and initialize NTB EPC interface * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @epc: struct pci_epc to which a particular NTB interface should be associated * @type: PRIMARY interface or SECONDARY interface * * Allocate memory for NTB EPC interface and initialize it.
*/ staticint epf_ntb_epc_create_interface(struct epf_ntb *ntb, struct pci_epc *epc, enum pci_epc_interface_type type)
{ conststruct pci_epc_features *epc_features; struct pci_epf_bar *epf_bar; struct epf_ntb_epc *ntb_epc;
u8 func_no, vfunc_no; struct pci_epf *epf; struct device *dev;
dev = &ntb->epf->dev;
ntb_epc = devm_kzalloc(dev, sizeof(*ntb_epc), GFP_KERNEL); if (!ntb_epc) return -ENOMEM;
/** * epf_ntb_epc_create() - Create and initialize NTB EPC interface * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * * Get a reference to EPC device and bind NTB function device to that EPC * for each of the interface. It is also a wrapper to * epf_ntb_epc_create_interface() to allocate memory for NTB EPC interface * and initialize it
*/ staticint epf_ntb_epc_create(struct epf_ntb *ntb)
{ struct pci_epf *epf; struct device *dev; int ret;
epf = ntb->epf;
dev = &epf->dev;
ret = epf_ntb_epc_create_interface(ntb, epf->epc, PRIMARY_INTERFACE); if (ret) {
dev_err(dev, "PRIMARY intf: Fail to create NTB EPC\n"); return ret;
}
ret = epf_ntb_epc_create_interface(ntb, epf->sec_epc,
SECONDARY_INTERFACE); if (ret) {
dev_err(dev, "SECONDARY intf: Fail to create NTB EPC\n"); goto err_epc_create;
}
/** * epf_ntb_init_epc_bar_interface() - Identify BARs to be used for each of * the NTB constructs (scratchpad region, doorbell, memorywindow) * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * * Identify the free BARs to be used for each of BAR_CONFIG, BAR_PEER_SPAD, * BAR_DB_MW1, BAR_MW2, BAR_MW3 and BAR_MW4.
*/ staticint epf_ntb_init_epc_bar_interface(struct epf_ntb *ntb, enum pci_epc_interface_type type)
{ conststruct pci_epc_features *epc_features; struct epf_ntb_epc *ntb_epc; enum pci_barno barno; enum epf_ntb_bar bar; struct device *dev;
u32 num_mws; int i;
/* These are required BARs which are mandatory for NTB functionality */ for (bar = BAR_CONFIG; bar <= BAR_DB_MW1; bar++, barno++) {
barno = pci_epc_get_next_free_bar(epc_features, barno); if (barno < 0) {
dev_err(dev, "%s intf: Fail to get NTB function BAR\n",
pci_epc_interface_string(type)); return barno;
}
ntb_epc->epf_ntb_bar[bar] = barno;
}
/* These are optional BARs which don't impact NTB functionality */ for (bar = BAR_MW2, i = 1; i < num_mws; bar++, barno++, i++) {
barno = pci_epc_get_next_free_bar(epc_features, barno); if (barno < 0) {
ntb->num_mws = i;
dev_dbg(dev, "BAR not available for > MW%d\n", i + 1);
}
ntb_epc->epf_ntb_bar[bar] = barno;
}
return 0;
}
/** * epf_ntb_init_epc_bar() - Identify BARs to be used for each of the NTB * constructs (scratchpad region, doorbell, memorywindow) * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * * Wrapper to epf_ntb_init_epc_bar_interface() to identify the free BARs * to be used for each of BAR_CONFIG, BAR_PEER_SPAD, BAR_DB_MW1, BAR_MW2, * BAR_MW3 and BAR_MW4 for all the interfaces.
*/ staticint epf_ntb_init_epc_bar(struct epf_ntb *ntb)
{ enum pci_epc_interface_type type; struct device *dev; int ret;
dev = &ntb->epf->dev; for (type = PRIMARY_INTERFACE; type <= SECONDARY_INTERFACE; type++) {
ret = epf_ntb_init_epc_bar_interface(ntb, type); if (ret) {
dev_err(dev, "Fail to init EPC bar for %s interface\n",
pci_epc_interface_string(type)); return ret;
}
}
return 0;
}
/** * epf_ntb_epc_init_interface() - Initialize NTB interface * @ntb: NTB device that facilitates communication between HOST1 and HOST2 * @type: PRIMARY interface or SECONDARY interface * * Wrapper to initialize a particular EPC interface and start the workqueue * to check for commands from host. This function will write to the * EP controller HW for configuring it.
*/ staticint epf_ntb_epc_init_interface(struct epf_ntb *ntb, enum pci_epc_interface_type type)
{ struct epf_ntb_epc *ntb_epc;
u8 func_no, vfunc_no; struct pci_epc *epc; struct pci_epf *epf; struct device *dev; int ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.