/** * dw_pcie_ep_get_func_from_ep - Get the struct dw_pcie_ep_func corresponding to * the endpoint function * @ep: DWC EP device * @func_no: Function number of the endpoint device * * Return: struct dw_pcie_ep_func if success, NULL otherwise.
*/ struct dw_pcie_ep_func *
dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no)
{ struct dw_pcie_ep_func *ep_func;
/** * dw_pcie_ep_hide_ext_capability - Hide a capability from the linked list * @pci: DWC PCI device * @prev_cap: Capability preceding the capability that should be hidden * @cap: Capability that should be hidden * * Return: 0 if success, errno otherwise.
*/ int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap)
{
u16 prev_cap_offset, cap_offset;
u32 prev_cap_header, cap_header;
prev_cap_offset = dw_pcie_find_ext_capability(pci, prev_cap); if (!prev_cap_offset) return -EINVAL;
if (free_win >= pci->num_ib_windows) {
dev_err(pci->dev, "No free inbound window\n"); return -EINVAL;
}
ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
parent_bus_addr, bar, size); if (ret < 0) {
dev_err(pci->dev, "Failed to program IB window\n"); return ret;
}
/* * Always increment free_win before assignment, since value 0 is used to identify * unallocated mapping.
*/
ep->bar_to_atu[bar] = free_win + 1;
set_bit(free_win, ep->ib_window_map);
rebar_offset = dw_pcie_ep_get_rebar_offset(pci, bar); if (!rebar_offset) return -EINVAL;
ret = pci_epc_bar_size_to_rebar_cap(size, &rebar_cap); if (ret) return ret;
dw_pcie_dbi_ro_wr_en(pci);
/* * A BAR mask should not be written for a resizable BAR. The BAR mask * is automatically derived by the controller every time the "selected * size" bits are updated, see "Figure 3-26 Resizable BAR Example for * 32-bit Memory BAR0" in DWC EP databook 5.96a. We simply need to write * BIT(0) to set the BAR enable bit.
*/
dw_pcie_ep_writel_dbi2(ep, func_no, reg, BIT(0));
dw_pcie_ep_writel_dbi(ep, func_no, reg, flags);
/* * Bits 31:0 in PCI_REBAR_CAP define "supported sizes" bits for sizes * 1 MB to 128 TB. Bits 31:16 in PCI_REBAR_CTRL define "supported sizes" * bits for sizes 256 TB to 8 EB. Disallow sizes 256 TB to 8 EB.
*/
rebar_ctrl = dw_pcie_readl_dbi(pci, rebar_offset + PCI_REBAR_CTRL);
rebar_ctrl &= ~GENMASK(31, 16);
dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl);
/* * The "selected size" (bits 13:8) in PCI_REBAR_CTRL are automatically * updated when writing PCI_REBAR_CAP, see "Figure 3-26 Resizable BAR * Example for 32-bit Memory BAR0" in DWC EP databook 5.96a.
*/
dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CAP, rebar_cap);
/* * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs * 1 and 2 to form a 64-bit BAR.
*/ if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1)) return -EINVAL;
/* * Certain EPF drivers dynamically change the physical address of a BAR * (i.e. they call set_bar() twice, without ever calling clear_bar(), as * calling clear_bar() would clear the BAR's PCI address assigned by the * host).
*/ if (ep->epf_bar[bar]) { /* * We can only dynamically change a BAR if the new BAR size and * BAR flags do not differ from the existing configuration.
*/ if (ep->epf_bar[bar]->barno != bar ||
ep->epf_bar[bar]->size != size ||
ep->epf_bar[bar]->flags != flags) return -EINVAL;
/* * When dynamically changing a BAR, skip writing the BAR reg, as * that would clear the BAR's PCI address assigned by the host.
*/ goto config_atu;
}
bar_type = dw_pcie_ep_get_bar_type(ep, bar); switch (bar_type) { case BAR_FIXED: /* * There is no need to write a BAR mask for a fixed BAR (except * to write 1 to the LSB of the BAR mask register, to enable the * BAR). Write the BAR mask regardless. (The fixed bits in the * BAR mask register will be read-only anyway.)
*/
fallthrough; case BAR_PROGRAMMABLE:
ret = dw_pcie_ep_set_bar_programmable(ep, func_no, epf_bar); break; case BAR_RESIZABLE:
ret = dw_pcie_ep_set_bar_resizable(ep, func_no, epf_bar); break; default:
ret = -EINVAL;
dev_err(pci->dev, "Invalid BAR type\n"); break;
}
if (ret) return ret;
config_atu: if (!(flags & PCI_BASE_ADDRESS_SPACE))
type = PCIE_ATU_TYPE_MEM; else
type = PCIE_ATU_TYPE_IO;
ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar,
size); if (ret) return ret;
/** * dw_pcie_ep_raise_msix_irq_doorbell - Raise MSI-X to the host using Doorbell * method * @ep: DWC EP device * @func_no: Function number of the endpoint device * @interrupt_num: Interrupt number to be raised * * Return: 0 if success, errno otherwise.
*/ int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no,
u16 interrupt_num)
{ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie_ep_func *ep_func;
u32 msg_data;
ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (!ep_func || !ep_func->msix_cap) return -EINVAL;
/** * dw_pcie_ep_cleanup - Cleanup DWC EP resources after fundamental reset * @ep: DWC EP device * * Cleans up the DWC EP specific resources like eDMA etc... after fundamental * reset like PERST#. Note that this API is only applicable for drivers * supporting PERST# or any other methods of fundamental reset.
*/ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
{ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
/** * dw_pcie_ep_deinit - Deinitialize the endpoint device * @ep: DWC EP device * * Deinitialize the endpoint device. EPC device is not destroyed since that will * be taken care by Devres.
*/ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep)
{ struct pci_epc *epc = ep->epc;
/* * PCIe r6.0, sec 7.8.6.2 require us to support at least one * size in the range from 1 MB to 512 GB. Advertise support * for 1 MB BAR size only. * * For a BAR that has been configured via dw_pcie_ep_set_bar(), * advertise support for only that size instead.
*/ for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) { /* * While the RESBAR_CAP_REG_* fields are sticky, the * RESBAR_CTRL_REG_BAR_SIZE field is non-sticky (it is * sticky in certain versions of DWC PCIe, but not all). * * RESBAR_CTRL_REG_BAR_SIZE is updated automatically by * the controller when RESBAR_CAP_REG is written, which * is why RESBAR_CAP_REG is written here.
*/
val = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
bar = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, val); if (ep->epf_bar[bar])
pci_epc_bar_size_to_rebar_cap(ep->epf_bar[bar]->size, &val); else
val = BIT(4);
/** * dw_pcie_ep_init_registers - Initialize DWC EP specific registers * @ep: DWC EP device * * Initialize the registers (CSRs) specific to DWC EP. This API should be called * only when the endpoint receives an active refclk (either from host or * generated locally).
*/ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
{ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie_ep_func *ep_func; struct device *dev = pci->dev; struct pci_epc *epc = ep->epc;
u32 ptm_cap_base, reg;
u8 hdr_type;
u8 func_no; void *addr; int ret;
hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &
PCI_HEADER_TYPE_MASK; if (hdr_type != PCI_HEADER_TYPE_NORMAL) {
dev_err(pci->dev, "PCIe controller is not set to EP mode (hdr_type:0x%x)!\n",
hdr_type); return -EIO;
}
dw_pcie_version_detect(pci);
dw_pcie_iatu_detect(pci);
ret = dw_pcie_edma_detect(pci); if (ret) return ret;
ret = -ENOMEM; if (!ep->ib_window_map) {
ep->ib_window_map = devm_bitmap_zalloc(dev, pci->num_ib_windows,
GFP_KERNEL); if (!ep->ib_window_map) goto err_remove_edma;
}
if (!ep->ob_window_map) {
ep->ob_window_map = devm_bitmap_zalloc(dev, pci->num_ob_windows,
GFP_KERNEL); if (!ep->ob_window_map) goto err_remove_edma;
}
if (!ep->outbound_addr) {
addr = devm_kcalloc(dev, pci->num_ob_windows, sizeof(phys_addr_t),
GFP_KERNEL); if (!addr) goto err_remove_edma;
ep->outbound_addr = addr;
}
for (func_no = 0; func_no < epc->max_functions; func_no++) {
ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); if (ep_func) continue;
ep_func = devm_kzalloc(dev, sizeof(*ep_func), GFP_KERNEL); if (!ep_func) goto err_remove_edma;
/** * dw_pcie_ep_linkdown - Notify EPF drivers about Link Down event * @ep: DWC EP device * * Non-sticky registers are also initialized before sending the notification to * the EPF drivers. This is needed since the registers need to be initialized * before the link comes back again.
*/ void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep)
{ struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct pci_epc *epc = ep->epc;
/* * Initialize the non-sticky DWC registers as they would've reset post * Link Down. This is specifically needed for drivers not supporting * PERST# as they have no way to reinitialize the registers before the * link comes back again.
*/
dw_pcie_ep_init_non_sticky_registers(pci);
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.