mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 01:24:12 +08:00
PCI: cadence: Add MSI-X support to Endpoint driver
Implement ->set_msix() and ->get_msix() callback functions in order to configure MSIX capability in the PCIe endpoint controller. Add cdns_pcie_ep_send_msix_irq() to send MSIX interrupts to Host. cdns_pcie_ep_send_msix_irq() gets the MSIX table address (virtual address) from "struct cdns_pcie_epf" that gets initialized in ->set_bar() call back function. [kishon@ti.com: Re-implement MSIX support in accordance with the re-designed core MSI-X interfaces] Link: https://lore.kernel.org/r/20200722110317.4744-11-kishon@ti.com Signed-off-by: Alan Douglas <adouglas@cadence.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
This commit is contained in:
parent
e3bca37d15
commit
3ef5d16f50
@ -51,6 +51,7 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn,
|
||||
struct pci_epf_bar *epf_bar)
|
||||
{
|
||||
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct cdns_pcie_epf *epf = &ep->epf[fn];
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
dma_addr_t bar_phys = epf_bar->phys_addr;
|
||||
enum pci_barno bar = epf_bar->barno;
|
||||
@ -111,6 +112,8 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn,
|
||||
CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl));
|
||||
cdns_pcie_writel(pcie, reg, cfg);
|
||||
|
||||
epf->epf_bar[bar] = epf_bar;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -118,6 +121,7 @@ static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn,
|
||||
struct pci_epf_bar *epf_bar)
|
||||
{
|
||||
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct cdns_pcie_epf *epf = &ep->epf[fn];
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
enum pci_barno bar = epf_bar->barno;
|
||||
u32 reg, cfg, b, ctrl;
|
||||
@ -139,6 +143,8 @@ static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn,
|
||||
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0);
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0);
|
||||
|
||||
epf->epf_bar[bar] = NULL;
|
||||
}
|
||||
|
||||
static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr,
|
||||
@ -224,6 +230,50 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn)
|
||||
return mme;
|
||||
}
|
||||
|
||||
static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
|
||||
{
|
||||
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
|
||||
u32 val, reg;
|
||||
|
||||
reg = cap + PCI_MSIX_FLAGS;
|
||||
val = cdns_pcie_ep_fn_readw(pcie, func_no, reg);
|
||||
if (!(val & PCI_MSIX_FLAGS_ENABLE))
|
||||
return -EINVAL;
|
||||
|
||||
val &= PCI_MSIX_FLAGS_QSIZE;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u16 interrupts,
|
||||
enum pci_barno bir, u32 offset)
|
||||
{
|
||||
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
|
||||
u32 val, reg;
|
||||
|
||||
reg = cap + PCI_MSIX_FLAGS;
|
||||
val = cdns_pcie_ep_fn_readw(pcie, fn, reg);
|
||||
val &= ~PCI_MSIX_FLAGS_QSIZE;
|
||||
val |= interrupts;
|
||||
cdns_pcie_ep_fn_writew(pcie, fn, reg, val);
|
||||
|
||||
/* Set MSIX BAR and offset */
|
||||
reg = cap + PCI_MSIX_TABLE;
|
||||
val = offset | bir;
|
||||
cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
|
||||
|
||||
/* Set PBA BAR and offset. BAR must match MSIX BAR */
|
||||
reg = cap + PCI_MSIX_PBA;
|
||||
val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
|
||||
cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn,
|
||||
u8 intx, bool is_asserted)
|
||||
{
|
||||
@ -333,6 +383,51 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn,
|
||||
u16 interrupt_num)
|
||||
{
|
||||
u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
|
||||
u32 tbl_offset, msg_data, reg;
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
struct pci_epf_msix_tbl *msix_tbl;
|
||||
struct cdns_pcie_epf *epf;
|
||||
u64 pci_addr_mask = 0xff;
|
||||
u64 msg_addr;
|
||||
u16 flags;
|
||||
u8 bir;
|
||||
|
||||
/* Check whether the MSI-X feature has been enabled by the PCI host. */
|
||||
flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSIX_FLAGS);
|
||||
if (!(flags & PCI_MSIX_FLAGS_ENABLE))
|
||||
return -EINVAL;
|
||||
|
||||
reg = cap + PCI_MSIX_TABLE;
|
||||
tbl_offset = cdns_pcie_ep_fn_readl(pcie, fn, reg);
|
||||
bir = tbl_offset & PCI_MSIX_TABLE_BIR;
|
||||
tbl_offset &= PCI_MSIX_TABLE_OFFSET;
|
||||
|
||||
epf = &ep->epf[fn];
|
||||
msix_tbl = epf->epf_bar[bir]->addr + tbl_offset;
|
||||
msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
|
||||
msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
|
||||
|
||||
/* Set the outbound region if needed. */
|
||||
if (ep->irq_pci_addr != (msg_addr & ~pci_addr_mask) ||
|
||||
ep->irq_pci_fn != fn) {
|
||||
/* First region was reserved for IRQ writes. */
|
||||
cdns_pcie_set_outbound_region(pcie, fn, 0,
|
||||
false,
|
||||
ep->irq_phys_addr,
|
||||
msg_addr & ~pci_addr_mask,
|
||||
pci_addr_mask + 1);
|
||||
ep->irq_pci_addr = (msg_addr & ~pci_addr_mask);
|
||||
ep->irq_pci_fn = fn;
|
||||
}
|
||||
writel(msg_data, ep->irq_cpu_addr + (msg_addr & pci_addr_mask));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
|
||||
enum pci_epc_irq_type type,
|
||||
u16 interrupt_num)
|
||||
@ -346,6 +441,9 @@ static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
|
||||
case PCI_EPC_IRQ_MSI:
|
||||
return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num);
|
||||
|
||||
case PCI_EPC_IRQ_MSIX:
|
||||
return cdns_pcie_ep_send_msix_irq(ep, fn, interrupt_num);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -383,7 +481,7 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
|
||||
static const struct pci_epc_features cdns_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
.msix_capable = true,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
@ -400,6 +498,8 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
|
||||
.unmap_addr = cdns_pcie_ep_unmap_addr,
|
||||
.set_msi = cdns_pcie_ep_set_msi,
|
||||
.get_msi = cdns_pcie_ep_get_msi,
|
||||
.set_msix = cdns_pcie_ep_set_msix,
|
||||
.get_msix = cdns_pcie_ep_get_msix,
|
||||
.raise_irq = cdns_pcie_ep_raise_irq,
|
||||
.start = cdns_pcie_ep_start,
|
||||
.get_features = cdns_pcie_ep_get_features,
|
||||
@ -458,6 +558,11 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
|
||||
if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0)
|
||||
epc->max_functions = 1;
|
||||
|
||||
ep->epf = devm_kcalloc(dev, epc->max_functions, sizeof(*ep->epf),
|
||||
GFP_KERNEL);
|
||||
if (!ep->epf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = pci_epc_mem_init(epc, pcie->mem_res->start,
|
||||
resource_size(pcie->mem_res), PAGE_SIZE);
|
||||
if (ret < 0) {
|
||||
|
@ -113,6 +113,7 @@
|
||||
#define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12))
|
||||
|
||||
#define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90
|
||||
#define CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET 0xb0
|
||||
|
||||
/*
|
||||
* Root Port Registers (PCI configuration space for the root port function)
|
||||
@ -302,6 +303,14 @@ struct cdns_pcie_rc {
|
||||
bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cdns_pcie_epf - Structure to hold info about endpoint function
|
||||
* @epf_bar: reference to the pci_epf_bar for the six Base Address Registers
|
||||
*/
|
||||
struct cdns_pcie_epf {
|
||||
struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cdns_pcie_ep - private data for this PCIe endpoint controller driver
|
||||
* @pcie: Cadence PCIe controller
|
||||
@ -321,6 +330,7 @@ struct cdns_pcie_rc {
|
||||
* @lock: spin lock to disable interrupts while modifying PCIe controller
|
||||
* registers fields (RMW) accessible by both remote RC and EP to
|
||||
* minimize time between read and write
|
||||
* @epf: Structure to hold info about endpoint function
|
||||
*/
|
||||
struct cdns_pcie_ep {
|
||||
struct cdns_pcie pcie;
|
||||
@ -334,6 +344,7 @@ struct cdns_pcie_ep {
|
||||
u8 irq_pending;
|
||||
/* protect writing to PCI_STATUS while raising legacy interrupts */
|
||||
spinlock_t lock;
|
||||
struct cdns_pcie_epf *epf;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user