diff options
author | Gustavo Pimentel <gustavo.pimentel@synopsys.com> | 2018-07-19 04:32:14 -0400 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-07-19 06:37:27 -0400 |
commit | beb4641a787df79a1423a8789d185b6b78fcbfea (patch) | |
tree | 3ba6f2780dc71ce4b623e6ea094e3681ad5deed0 /drivers/pci/controller/dwc | |
parent | d3c70a98d7d63cae02d50ebfafea04264a767401 (diff) |
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 <gustavo.pimentel@synopsys.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/pci/controller/dwc')
-rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-ep.c | 137 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-plat.c | 2 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.h | 10 |
3 files changed, 148 insertions, 1 deletions
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 69d039de2af6..23be2c0249ee 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c | |||
@@ -40,6 +40,39 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) | |||
40 | __dw_pcie_ep_reset_bar(pci, bar, 0); | 40 | __dw_pcie_ep_reset_bar(pci, bar, 0); |
41 | } | 41 | } |
42 | 42 | ||
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 | |||
43 | static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, | 76 | static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, |
44 | struct pci_epf_header *hdr) | 77 | struct pci_epf_header *hdr) |
45 | { | 78 | { |
@@ -241,6 +274,45 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) | |||
241 | return 0; | 274 | return 0; |
242 | } | 275 | } |
243 | 276 | ||
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 | |||
244 | static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, | 316 | static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no, |
245 | enum pci_epc_irq_type type, u16 interrupt_num) | 317 | enum pci_epc_irq_type type, u16 interrupt_num) |
246 | { | 318 | { |
@@ -282,6 +354,8 @@ static const struct pci_epc_ops epc_ops = { | |||
282 | .unmap_addr = dw_pcie_ep_unmap_addr, | 354 | .unmap_addr = dw_pcie_ep_unmap_addr, |
283 | .set_msi = dw_pcie_ep_set_msi, | 355 | .set_msi = dw_pcie_ep_set_msi, |
284 | .get_msi = dw_pcie_ep_get_msi, | 356 | .get_msi = dw_pcie_ep_get_msi, |
357 | .set_msix = dw_pcie_ep_set_msix, | ||
358 | .get_msix = dw_pcie_ep_get_msix, | ||
285 | .raise_irq = dw_pcie_ep_raise_irq, | 359 | .raise_irq = dw_pcie_ep_raise_irq, |
286 | .start = dw_pcie_ep_start, | 360 | .start = dw_pcie_ep_start, |
287 | .stop = dw_pcie_ep_stop, | 361 | .stop = dw_pcie_ep_stop, |
@@ -322,6 +396,64 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, | |||
322 | return 0; | 396 | return 0; |
323 | } | 397 | } |
324 | 398 | ||
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 | |||
325 | void dw_pcie_ep_exit(struct dw_pcie_ep *ep) | 457 | void dw_pcie_ep_exit(struct dw_pcie_ep *ep) |
326 | { | 458 | { |
327 | struct pci_epc *epc = ep->epc; | 459 | struct pci_epc *epc = ep->epc; |
@@ -412,9 +544,12 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) | |||
412 | ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, | 544 | ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys, |
413 | epc->mem->page_size); | 545 | epc->mem->page_size); |
414 | if (!ep->msi_mem) { | 546 | 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"); |
416 | return -ENOMEM; | 548 | return -ENOMEM; |
417 | } | 549 | } |
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); | ||
418 | 553 | ||
419 | dw_pcie_setup(pci); | 554 | dw_pcie_setup(pci); |
420 | 555 | ||
diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index cca69ac63319..35d2291c6ba5 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c | |||
@@ -91,6 +91,8 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, | |||
91 | return -EINVAL; | 91 | return -EINVAL; |
92 | case PCI_EPC_IRQ_MSI: | 92 | case PCI_EPC_IRQ_MSI: |
93 | return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); | 93 | 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); | ||
94 | default: | 96 | default: |
95 | dev_err(pci->dev, "UNKNOWN IRQ type\n"); | 97 | dev_err(pci->dev, "UNKNOWN IRQ type\n"); |
96 | } | 98 | } |
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 9d581c077329..00ac4197c457 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h | |||
@@ -208,6 +208,8 @@ struct dw_pcie_ep { | |||
208 | u32 num_ob_windows; | 208 | u32 num_ob_windows; |
209 | void __iomem *msi_mem; | 209 | void __iomem *msi_mem; |
210 | phys_addr_t msi_mem_phys; | 210 | phys_addr_t msi_mem_phys; |
211 | u8 msi_cap; /* MSI capability offset */ | ||
212 | u8 msix_cap; /* MSI-X capability offset */ | ||
211 | }; | 213 | }; |
212 | 214 | ||
213 | struct dw_pcie_ops { | 215 | struct dw_pcie_ops { |
@@ -359,6 +361,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep); | |||
359 | void dw_pcie_ep_exit(struct dw_pcie_ep *ep); | 361 | void dw_pcie_ep_exit(struct dw_pcie_ep *ep); |
360 | int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, | 362 | int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, |
361 | u8 interrupt_num); | 363 | u8 interrupt_num); |
364 | int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, | ||
365 | u16 interrupt_num); | ||
362 | void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); | 366 | void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); |
363 | #else | 367 | #else |
364 | static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) | 368 | 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, | |||
380 | return 0; | 384 | return 0; |
381 | } | 385 | } |
382 | 386 | ||
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 | |||
383 | static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) | 393 | static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) |
384 | { | 394 | { |
385 | } | 395 | } |