Skip to content

Commit beb4641

Browse files
gustavoSNPSLorenzo Pieralisi
authored andcommitted
PCI: dwc: Add MSI-X callbacks handler
Add PCIe config space capability search function. Add sysfs set/get interface to allow the change of EP MSI-X maximum number. Add EP MSI-X callback for triggering interruptions. Signed-off-by: Gustavo Pimentel <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Acked-by: Kishon Vijay Abraham I <[email protected]>
1 parent d3c70a9 commit beb4641

File tree

3 files changed

+148
-1
lines changed

3 files changed

+148
-1
lines changed

drivers/pci/controller/dwc/pcie-designware-ep.c

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,39 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
4040
__dw_pcie_ep_reset_bar(pci, bar, 0);
4141
}
4242

43+
static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
44+
u8 cap)
45+
{
46+
u8 cap_id, next_cap_ptr;
47+
u16 reg;
48+
49+
reg = dw_pcie_readw_dbi(pci, cap_ptr);
50+
next_cap_ptr = (reg & 0xff00) >> 8;
51+
cap_id = (reg & 0x00ff);
52+
53+
if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
54+
return 0;
55+
56+
if (cap_id == cap)
57+
return cap_ptr;
58+
59+
return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
60+
}
61+
62+
static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
63+
{
64+
u8 next_cap_ptr;
65+
u16 reg;
66+
67+
reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
68+
next_cap_ptr = (reg & 0x00ff);
69+
70+
if (!next_cap_ptr)
71+
return 0;
72+
73+
return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
74+
}
75+
4376
static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
4477
struct pci_epf_header *hdr)
4578
{
@@ -241,6 +274,45 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int)
241274
return 0;
242275
}
243276

277+
static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
278+
{
279+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
280+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
281+
u32 val, reg;
282+
283+
if (!ep->msix_cap)
284+
return -EINVAL;
285+
286+
reg = ep->msix_cap + PCI_MSIX_FLAGS;
287+
val = dw_pcie_readw_dbi(pci, reg);
288+
if (!(val & PCI_MSIX_FLAGS_ENABLE))
289+
return -EINVAL;
290+
291+
val &= PCI_MSIX_FLAGS_QSIZE;
292+
293+
return val;
294+
}
295+
296+
static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
297+
{
298+
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
299+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
300+
u32 val, reg;
301+
302+
if (!ep->msix_cap)
303+
return -EINVAL;
304+
305+
reg = ep->msix_cap + PCI_MSIX_FLAGS;
306+
val = dw_pcie_readw_dbi(pci, reg);
307+
val &= ~PCI_MSIX_FLAGS_QSIZE;
308+
val |= interrupts;
309+
dw_pcie_dbi_ro_wr_en(pci);
310+
dw_pcie_writew_dbi(pci, reg, val);
311+
dw_pcie_dbi_ro_wr_dis(pci);
312+
313+
return 0;
314+
}
315+
244316
static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
245317
enum pci_epc_irq_type type, u16 interrupt_num)
246318
{
@@ -282,6 +354,8 @@ static const struct pci_epc_ops epc_ops = {
282354
.unmap_addr = dw_pcie_ep_unmap_addr,
283355
.set_msi = dw_pcie_ep_set_msi,
284356
.get_msi = dw_pcie_ep_get_msi,
357+
.set_msix = dw_pcie_ep_set_msix,
358+
.get_msix = dw_pcie_ep_get_msix,
285359
.raise_irq = dw_pcie_ep_raise_irq,
286360
.start = dw_pcie_ep_start,
287361
.stop = dw_pcie_ep_stop,
@@ -322,6 +396,64 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
322396
return 0;
323397
}
324398

399+
int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
400+
u16 interrupt_num)
401+
{
402+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
403+
struct pci_epc *epc = ep->epc;
404+
u16 tbl_offset, bir;
405+
u32 bar_addr_upper, bar_addr_lower;
406+
u32 msg_addr_upper, msg_addr_lower;
407+
u32 reg, msg_data, vec_ctrl;
408+
u64 tbl_addr, msg_addr, reg_u64;
409+
void __iomem *msix_tbl;
410+
int ret;
411+
412+
reg = ep->msix_cap + PCI_MSIX_TABLE;
413+
tbl_offset = dw_pcie_readl_dbi(pci, reg);
414+
bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
415+
tbl_offset &= PCI_MSIX_TABLE_OFFSET;
416+
tbl_offset >>= 3;
417+
418+
reg = PCI_BASE_ADDRESS_0 + (4 * bir);
419+
bar_addr_upper = 0;
420+
bar_addr_lower = dw_pcie_readl_dbi(pci, reg);
421+
reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK);
422+
if (reg_u64 == PCI_BASE_ADDRESS_MEM_TYPE_64)
423+
bar_addr_upper = dw_pcie_readl_dbi(pci, reg + 4);
424+
425+
tbl_addr = ((u64) bar_addr_upper) << 32 | bar_addr_lower;
426+
tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE));
427+
tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK;
428+
429+
msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr,
430+
PCI_MSIX_ENTRY_SIZE);
431+
if (!msix_tbl)
432+
return -EINVAL;
433+
434+
msg_addr_lower = readl(msix_tbl + PCI_MSIX_ENTRY_LOWER_ADDR);
435+
msg_addr_upper = readl(msix_tbl + PCI_MSIX_ENTRY_UPPER_ADDR);
436+
msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
437+
msg_data = readl(msix_tbl + PCI_MSIX_ENTRY_DATA);
438+
vec_ctrl = readl(msix_tbl + PCI_MSIX_ENTRY_VECTOR_CTRL);
439+
440+
iounmap(msix_tbl);
441+
442+
if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)
443+
return -EPERM;
444+
445+
ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
446+
epc->mem->page_size);
447+
if (ret)
448+
return ret;
449+
450+
writel(msg_data, ep->msi_mem);
451+
452+
dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
453+
454+
return 0;
455+
}
456+
325457
void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
326458
{
327459
struct pci_epc *epc = ep->epc;
@@ -412,9 +544,12 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
412544
ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
413545
epc->mem->page_size);
414546
if (!ep->msi_mem) {
415-
dev_err(dev, "Failed to reserve memory for MSI\n");
547+
dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
416548
return -ENOMEM;
417549
}
550+
ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
551+
552+
ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
418553

419554
dw_pcie_setup(pci);
420555

drivers/pci/controller/dwc/pcie-designware-plat.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
9191
return -EINVAL;
9292
case PCI_EPC_IRQ_MSI:
9393
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
94+
case PCI_EPC_IRQ_MSIX:
95+
return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
9496
default:
9597
dev_err(pci->dev, "UNKNOWN IRQ type\n");
9698
}

drivers/pci/controller/dwc/pcie-designware.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ struct dw_pcie_ep {
208208
u32 num_ob_windows;
209209
void __iomem *msi_mem;
210210
phys_addr_t msi_mem_phys;
211+
u8 msi_cap; /* MSI capability offset */
212+
u8 msix_cap; /* MSI-X capability offset */
211213
};
212214

213215
struct dw_pcie_ops {
@@ -359,6 +361,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep);
359361
void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
360362
int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
361363
u8 interrupt_num);
364+
int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
365+
u16 interrupt_num);
362366
void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
363367
#else
364368
static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
@@ -380,6 +384,12 @@ static inline int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
380384
return 0;
381385
}
382386

387+
static inline int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
388+
u16 interrupt_num)
389+
{
390+
return 0;
391+
}
392+
383393
static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
384394
{
385395
}

0 commit comments

Comments
 (0)