Skip to content

Commit 3ef5d16

Browse files
Alan DouglasLorenzo Pieralisi
authored andcommitted
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. [[email protected]: Re-implement MSIX support in accordance with the re-designed core MSI-X interfaces] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alan Douglas <[email protected]> Signed-off-by: Kishon Vijay Abraham I <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]>
1 parent e3bca37 commit 3ef5d16

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

drivers/pci/controller/cadence/pcie-cadence-ep.c

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn,
5151
struct pci_epf_bar *epf_bar)
5252
{
5353
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
54+
struct cdns_pcie_epf *epf = &ep->epf[fn];
5455
struct cdns_pcie *pcie = &ep->pcie;
5556
dma_addr_t bar_phys = epf_bar->phys_addr;
5657
enum pci_barno bar = epf_bar->barno;
@@ -111,13 +112,16 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn,
111112
CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl));
112113
cdns_pcie_writel(pcie, reg, cfg);
113114

115+
epf->epf_bar[bar] = epf_bar;
116+
114117
return 0;
115118
}
116119

117120
static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn,
118121
struct pci_epf_bar *epf_bar)
119122
{
120123
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
124+
struct cdns_pcie_epf *epf = &ep->epf[fn];
121125
struct cdns_pcie *pcie = &ep->pcie;
122126
enum pci_barno bar = epf_bar->barno;
123127
u32 reg, cfg, b, ctrl;
@@ -139,6 +143,8 @@ static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn,
139143

140144
cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar), 0);
141145
cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar), 0);
146+
147+
epf->epf_bar[bar] = NULL;
142148
}
143149

144150
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)
224230
return mme;
225231
}
226232

233+
static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
234+
{
235+
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
236+
struct cdns_pcie *pcie = &ep->pcie;
237+
u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
238+
u32 val, reg;
239+
240+
reg = cap + PCI_MSIX_FLAGS;
241+
val = cdns_pcie_ep_fn_readw(pcie, func_no, reg);
242+
if (!(val & PCI_MSIX_FLAGS_ENABLE))
243+
return -EINVAL;
244+
245+
val &= PCI_MSIX_FLAGS_QSIZE;
246+
247+
return val;
248+
}
249+
250+
static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u16 interrupts,
251+
enum pci_barno bir, u32 offset)
252+
{
253+
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
254+
struct cdns_pcie *pcie = &ep->pcie;
255+
u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
256+
u32 val, reg;
257+
258+
reg = cap + PCI_MSIX_FLAGS;
259+
val = cdns_pcie_ep_fn_readw(pcie, fn, reg);
260+
val &= ~PCI_MSIX_FLAGS_QSIZE;
261+
val |= interrupts;
262+
cdns_pcie_ep_fn_writew(pcie, fn, reg, val);
263+
264+
/* Set MSIX BAR and offset */
265+
reg = cap + PCI_MSIX_TABLE;
266+
val = offset | bir;
267+
cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
268+
269+
/* Set PBA BAR and offset. BAR must match MSIX BAR */
270+
reg = cap + PCI_MSIX_PBA;
271+
val = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | bir;
272+
cdns_pcie_ep_fn_writel(pcie, fn, reg, val);
273+
274+
return 0;
275+
}
276+
227277
static void cdns_pcie_ep_assert_intx(struct cdns_pcie_ep *ep, u8 fn,
228278
u8 intx, bool is_asserted)
229279
{
@@ -333,6 +383,51 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
333383
return 0;
334384
}
335385

386+
static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn,
387+
u16 interrupt_num)
388+
{
389+
u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET;
390+
u32 tbl_offset, msg_data, reg;
391+
struct cdns_pcie *pcie = &ep->pcie;
392+
struct pci_epf_msix_tbl *msix_tbl;
393+
struct cdns_pcie_epf *epf;
394+
u64 pci_addr_mask = 0xff;
395+
u64 msg_addr;
396+
u16 flags;
397+
u8 bir;
398+
399+
/* Check whether the MSI-X feature has been enabled by the PCI host. */
400+
flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSIX_FLAGS);
401+
if (!(flags & PCI_MSIX_FLAGS_ENABLE))
402+
return -EINVAL;
403+
404+
reg = cap + PCI_MSIX_TABLE;
405+
tbl_offset = cdns_pcie_ep_fn_readl(pcie, fn, reg);
406+
bir = tbl_offset & PCI_MSIX_TABLE_BIR;
407+
tbl_offset &= PCI_MSIX_TABLE_OFFSET;
408+
409+
epf = &ep->epf[fn];
410+
msix_tbl = epf->epf_bar[bir]->addr + tbl_offset;
411+
msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
412+
msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
413+
414+
/* Set the outbound region if needed. */
415+
if (ep->irq_pci_addr != (msg_addr & ~pci_addr_mask) ||
416+
ep->irq_pci_fn != fn) {
417+
/* First region was reserved for IRQ writes. */
418+
cdns_pcie_set_outbound_region(pcie, fn, 0,
419+
false,
420+
ep->irq_phys_addr,
421+
msg_addr & ~pci_addr_mask,
422+
pci_addr_mask + 1);
423+
ep->irq_pci_addr = (msg_addr & ~pci_addr_mask);
424+
ep->irq_pci_fn = fn;
425+
}
426+
writel(msg_data, ep->irq_cpu_addr + (msg_addr & pci_addr_mask));
427+
428+
return 0;
429+
}
430+
336431
static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
337432
enum pci_epc_irq_type type,
338433
u16 interrupt_num)
@@ -346,6 +441,9 @@ static int cdns_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn,
346441
case PCI_EPC_IRQ_MSI:
347442
return cdns_pcie_ep_send_msi_irq(ep, fn, interrupt_num);
348443

444+
case PCI_EPC_IRQ_MSIX:
445+
return cdns_pcie_ep_send_msix_irq(ep, fn, interrupt_num);
446+
349447
default:
350448
break;
351449
}
@@ -383,7 +481,7 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
383481
static const struct pci_epc_features cdns_pcie_epc_features = {
384482
.linkup_notifier = false,
385483
.msi_capable = true,
386-
.msix_capable = false,
484+
.msix_capable = true,
387485
};
388486

389487
static const struct pci_epc_features*
@@ -400,6 +498,8 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
400498
.unmap_addr = cdns_pcie_ep_unmap_addr,
401499
.set_msi = cdns_pcie_ep_set_msi,
402500
.get_msi = cdns_pcie_ep_get_msi,
501+
.set_msix = cdns_pcie_ep_set_msix,
502+
.get_msix = cdns_pcie_ep_get_msix,
403503
.raise_irq = cdns_pcie_ep_raise_irq,
404504
.start = cdns_pcie_ep_start,
405505
.get_features = cdns_pcie_ep_get_features,
@@ -458,6 +558,11 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
458558
if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0)
459559
epc->max_functions = 1;
460560

561+
ep->epf = devm_kcalloc(dev, epc->max_functions, sizeof(*ep->epf),
562+
GFP_KERNEL);
563+
if (!ep->epf)
564+
return -ENOMEM;
565+
461566
ret = pci_epc_mem_init(epc, pcie->mem_res->start,
462567
resource_size(pcie->mem_res), PAGE_SIZE);
463568
if (ret < 0) {

drivers/pci/controller/cadence/pcie-cadence.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
#define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12))
114114

115115
#define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90
116+
#define CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET 0xb0
116117

117118
/*
118119
* Root Port Registers (PCI configuration space for the root port function)
@@ -302,6 +303,14 @@ struct cdns_pcie_rc {
302303
bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
303304
};
304305

306+
/**
307+
* struct cdns_pcie_epf - Structure to hold info about endpoint function
308+
* @epf_bar: reference to the pci_epf_bar for the six Base Address Registers
309+
*/
310+
struct cdns_pcie_epf {
311+
struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS];
312+
};
313+
305314
/**
306315
* struct cdns_pcie_ep - private data for this PCIe endpoint controller driver
307316
* @pcie: Cadence PCIe controller
@@ -321,6 +330,7 @@ struct cdns_pcie_rc {
321330
* @lock: spin lock to disable interrupts while modifying PCIe controller
322331
* registers fields (RMW) accessible by both remote RC and EP to
323332
* minimize time between read and write
333+
* @epf: Structure to hold info about endpoint function
324334
*/
325335
struct cdns_pcie_ep {
326336
struct cdns_pcie pcie;
@@ -334,6 +344,7 @@ struct cdns_pcie_ep {
334344
u8 irq_pending;
335345
/* protect writing to PCI_STATUS while raising legacy interrupts */
336346
spinlock_t lock;
347+
struct cdns_pcie_epf *epf;
337348
};
338349

339350

0 commit comments

Comments
 (0)