// SPDX-License-Identifier: GPL-2.0 /* * Endpoint Function Driver to implement Non-Transparent Bridge functionality * Between PCI RC and EP * * Copyright (C) 2020 Texas Instruments * Copyright (C) 2022 NXP * * Based on pci-epf-ntb.c * Author: Frank Li <Frank.Li@nxp.com> * Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
/** * epf_ntb_link_up() - Raise link_up interrupt to Virtual Host (VHOST) * @ntb: NTB device that facilitates communication between HOST and VHOST * @link_up: true or false indicating Link is UP or Down * * Once NTB function in HOST invoke ntb_link_enable(), * this NTB function driver will trigger a link event to VHOST. * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
{ if (link_up)
ntb->reg->link_status |= LINK_STATUS_UP; else
ntb->reg->link_status &= ~LINK_STATUS_UP;
ntb_link_event(&ntb->ntb); return 0;
}
/** * epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST * to access the memory window of HOST * @ntb: NTB device that facilitates communication between HOST and VHOST * @mw: Index of the memory window (either 0, 1, 2 or 3) * * EP Outbound Window * +--------+ +-----------+ * | | | | * | | | | * | | | | * | | | | * | | +-----------+ * | Virtual| | Memory Win| * | NTB | -----------> | | * | Driver | | | * | | +-----------+ * | | | | * | | | | * +--------+ +-----------+ * VHOST PCI EP * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_configure_mw(struct epf_ntb *ntb, u32 mw)
{
phys_addr_t phys_addr;
u8 func_no, vfunc_no;
u64 addr, size; int ret = 0;
ret = pci_epc_map_addr(ntb->epf->epc, func_no, vfunc_no, phys_addr, addr, size); if (ret)
dev_err(&ntb->epf->epc->dev, "Failed to map memory window %d address\n", mw); return ret;
}
/** * epf_ntb_teardown_mw() - Teardown the configured OB ATU * @ntb: NTB device that facilitates communication between HOST and VHOST * @mw: Index of the memory window (either 0, 1, 2 or 3) * * Teardown the configured OB ATU configured in epf_ntb_configure_mw() using * pci_epc_unmap_addr()
*/ staticvoid epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw)
{
pci_epc_unmap_addr(ntb->epf->epc,
ntb->epf->func_no,
ntb->epf->vfunc_no,
ntb->vpci_mw_phy[mw]);
}
/** * epf_ntb_cmd_handler() - Handle commands provided by the NTB HOST * @work: work_struct for the epf_ntb_epc * * 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)
{ struct epf_ntb_ctrl *ctrl;
u32 command, argument; struct epf_ntb *ntb; struct device *dev; int ret; int i;
/** * epf_ntb_config_sspad_bar_clear() - Clear Config + Self scratchpad BAR * @ntb: EPC associated with one of the HOST which holds peer's outbound * address. * * 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 VHOST's peer * scratchpad is HOST's self scratchpad. * * Returns: void
*/ staticvoid epf_ntb_config_sspad_bar_clear(struct epf_ntb *ntb)
{ struct pci_epf_bar *epf_bar; enum pci_barno barno;
/** * epf_ntb_config_sspad_bar_set() - Set Config + Self scratchpad BAR * @ntb: NTB device that facilitates communication between HOST and VHOST * * Map BAR0 of EP CONTROLLER which contains the VHOST's config and * self scratchpad region. * * Please note the self scratchpad region and config region is combined to * a single region and mapped using the same BAR. * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_config_sspad_bar_set(struct epf_ntb *ntb)
{ struct pci_epf_bar *epf_bar; enum pci_barno barno;
u8 func_no, vfunc_no; struct device *dev; int ret;
ret = pci_epc_set_bar(ntb->epf->epc, func_no, vfunc_no, epf_bar); if (ret) {
dev_err(dev, "inft: Config/Status/SPAD BAR set failed\n"); 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 HOST and VHOST
*/ staticvoid epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
{ enum pci_barno barno;
/** * epf_ntb_config_spad_bar_alloc() - Allocate memory for config + scratchpad * region * @ntb: NTB device that facilitates communication between HOST and VHOST * * 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. * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
{ enum pci_barno barno; struct epf_ntb_ctrl *ctrl;
u32 spad_size, ctrl_size; struct pci_epf *epf = ntb->epf; struct device *dev = &epf->dev;
u32 spad_count; void *base; int i; conststruct pci_epc_features *epc_features = pci_epc_get_features(epf->epc,
epf->func_no,
epf->vfunc_no);
barno = ntb->epf_ntb_bar[BAR_CONFIG];
spad_count = ntb->spad_count;
for (i = 0; i < ntb->db_count; i++) {
ntb->reg->db_data[i] = 1 + i;
ntb->reg->db_offset[i] = 0;
}
return 0;
}
/** * epf_ntb_configure_interrupt() - Configure MSI/MSI-X capability * @ntb: NTB device that facilitates communication between HOST and VHOST * * Configure MSI/MSI-X capability for each interface with number of * interrupts equal to "db_count" configfs entry. * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_configure_interrupt(struct epf_ntb *ntb)
{ conststruct pci_epc_features *epc_features; struct device *dev;
u32 db_count; int ret;
mw_addr = pci_epf_alloc_space(ntb->epf, size, barno, epc_features, 0); if (!mw_addr) {
dev_err(dev, "Failed to allocate OB address\n"); return -ENOMEM;
}
ntb->epf_db = mw_addr;
epf_bar = &ntb->epf->bar[barno];
ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no, epf_bar); if (ret) {
dev_err(dev, "Doorbell BAR set failed\n"); goto err_alloc_peer_mem;
} return ret;
staticvoid epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws);
/** * epf_ntb_db_bar_clear() - Clear doorbell BAR and free memory * allocated in peer's outbound address space * @ntb: NTB device that facilitates communication between HOST and VHOST
*/ staticvoid epf_ntb_db_bar_clear(struct epf_ntb *ntb)
{ enum pci_barno barno;
/** * epf_ntb_mw_bar_init() - Configure Memory window BARs * @ntb: NTB device that facilitates communication between HOST and VHOST * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_mw_bar_init(struct epf_ntb *ntb)
{ int ret = 0; int i;
u64 size; enum pci_barno barno; struct device *dev = &ntb->epf->dev;
for (i = 0; i < ntb->num_mws; i++) {
size = ntb->mws_size[i];
barno = ntb->epf_ntb_bar[BAR_MW1 + i];
/** * epf_ntb_mw_bar_clear() - Clear Memory window BARs * @ntb: NTB device that facilitates communication between HOST and VHOST * @num_mws: the number of Memory window BARs that to be cleared
*/ staticvoid epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws)
{ enum pci_barno barno; int i;
for (i = 0; i < num_mws; i++) {
barno = ntb->epf_ntb_bar[BAR_MW1 + i];
pci_epc_clear_bar(ntb->epf->epc,
ntb->epf->func_no,
ntb->epf->vfunc_no,
&ntb->epf->bar[barno]);
/** * epf_ntb_epc_destroy() - Cleanup NTB EPC interface * @ntb: NTB device that facilitates communication between HOST and VHOST * * Wrapper for epf_ntb_epc_destroy_interface() to cleanup all the NTB interfaces
*/ staticvoid epf_ntb_epc_destroy(struct epf_ntb *ntb)
{
pci_epc_remove_epf(ntb->epf->epc, ntb->epf, 0);
pci_epc_put(ntb->epf->epc);
}
/** * epf_ntb_is_bar_used() - Check if a bar is used in the ntb configuration * @ntb: NTB device that facilitates communication between HOST and VHOST * @barno: Checked bar number * * Returns: true if used, false if free.
*/ staticbool epf_ntb_is_bar_used(struct epf_ntb *ntb, enum pci_barno barno)
{ int i;
for (i = 0; i < VNTB_BAR_NUM; i++) { if (ntb->epf_ntb_bar[i] == barno) returntrue;
}
returnfalse;
}
/** * epf_ntb_find_bar() - Assign BAR number when no configuration is provided * @ntb: NTB device that facilitates communication between HOST and VHOST * @epc_features: The features provided by the EPC specific to this EPF * @bar: NTB BAR index * @barno: Bar start index * * When the BAR configuration was not provided through the userspace * configuration, automatically assign BAR as it has been historically * done by this endpoint function. * * Returns: the BAR number found, if any. -1 otherwise
*/ staticint epf_ntb_find_bar(struct epf_ntb *ntb, conststruct pci_epc_features *epc_features, enum epf_ntb_bar bar, enum pci_barno barno)
{ while (ntb->epf_ntb_bar[bar] < 0) {
barno = pci_epc_get_next_free_bar(epc_features, barno); if (barno < 0) break; /* No more BAR available */
/* * Verify if the BAR found is not already assigned * through the provided configuration
*/ if (!epf_ntb_is_bar_used(ntb, barno))
ntb->epf_ntb_bar[bar] = barno;
barno += 1;
}
return barno;
}
/** * 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 HOST and VHOST * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_init_epc_bar(struct epf_ntb *ntb)
{ conststruct pci_epc_features *epc_features; 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_MW1; bar++) {
barno = epf_ntb_find_bar(ntb, epc_features, bar, barno); if (barno < 0) {
dev_err(dev, "Fail to get NTB function BAR\n"); return -ENOENT;
}
}
/* These are optional BARs which don't impact NTB functionality */ for (bar = BAR_MW1, i = 1; i < num_mws; bar++, i++) {
barno = epf_ntb_find_bar(ntb, epc_features, bar, barno); if (barno < 0) {
ntb->num_mws = i;
dev_dbg(dev, "BAR not available for > MW%d\n", i + 1);
}
}
return 0;
}
/** * epf_ntb_epc_init() - Initialize NTB interface * @ntb: NTB device that facilitates communication between HOST and VHOST * * 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. * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_epc_init(struct epf_ntb *ntb)
{
u8 func_no, vfunc_no; struct pci_epc *epc; struct pci_epf *epf; struct device *dev; int ret;
/** * epf_ntb_add_cfs() - Add configfs directory specific to NTB * @epf: NTB endpoint function device * @group: A pointer to the config_group structure referencing a group of * config_items of a specific type that belong to a specific sub-system. * * Add configfs directory specific to NTB. This directory will hold * NTB specific properties like db_count, spad_count, num_mws etc., * * Returns: Pointer to config_group
*/ staticstruct config_group *epf_ntb_add_cfs(struct pci_epf *epf, struct config_group *group)
{ struct epf_ntb *ntb = epf_get_drvdata(epf); struct config_group *ntb_group = &ntb->group; struct device *dev = &epf->dev;
/** * epf_ntb_bind() - Initialize endpoint controller to provide NTB functionality * @epf: NTB endpoint function device * * Initialize both the endpoint controllers associated with NTB function device. * Invoked when a primary interface or secondary interface is bound to EPC * device. This function will succeed only when EPC is bound to both the * interfaces. * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_bind(struct pci_epf *epf)
{ struct epf_ntb *ntb = epf_get_drvdata(epf); struct device *dev = &epf->dev; int ret;
if (!epf->epc) {
dev_dbg(dev, "PRIMARY EPC interface not yet bound\n"); return 0;
}
ret = epf_ntb_init_epc_bar(ntb); if (ret) {
dev_err(dev, "Failed to create NTB EPC\n"); goto err_bar_init;
}
ret = epf_ntb_config_spad_bar_alloc(ntb); if (ret) {
dev_err(dev, "Failed to allocate BAR memory\n"); goto err_bar_alloc;
}
ret = epf_ntb_epc_init(ntb); if (ret) {
dev_err(dev, "Failed to initialize EPC\n"); goto err_bar_alloc;
}
/** * epf_ntb_probe() - Probe NTB function driver * @epf: NTB endpoint function device * @id: NTB endpoint function device ID * * Probe NTB function driver when endpoint function bus detects a NTB * endpoint function. * * Returns: Zero for success, or an error code in case of failure
*/ staticint epf_ntb_probe(struct pci_epf *epf, conststruct pci_epf_device_id *id)
{ struct epf_ntb *ntb; struct device *dev; int i;
dev = &epf->dev;
ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL); if (!ntb) return -ENOMEM;
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.