forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									d3c70a98d7
								
							
						
					
					
						commit
						beb4641a78
					
				
					 3 changed files with 148 additions and 1 deletions
				
			
		| 
						 | 
					@ -40,6 +40,39 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
 | 
				
			||||||
	__dw_pcie_ep_reset_bar(pci, bar, 0);
 | 
						__dw_pcie_ep_reset_bar(pci, bar, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie *pci, u8 cap_ptr,
 | 
				
			||||||
 | 
								      u8 cap)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u8 cap_id, next_cap_ptr;
 | 
				
			||||||
 | 
						u16 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = dw_pcie_readw_dbi(pci, cap_ptr);
 | 
				
			||||||
 | 
						next_cap_ptr = (reg & 0xff00) >> 8;
 | 
				
			||||||
 | 
						cap_id = (reg & 0x00ff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cap_id == cap)
 | 
				
			||||||
 | 
							return cap_ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static u8 dw_pcie_ep_find_capability(struct dw_pcie *pci, u8 cap)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u8 next_cap_ptr;
 | 
				
			||||||
 | 
						u16 reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
 | 
				
			||||||
 | 
						next_cap_ptr = (reg & 0x00ff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!next_cap_ptr)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return __dw_pcie_ep_find_next_cap(pci, next_cap_ptr, cap);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
 | 
					static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
 | 
				
			||||||
				   struct pci_epf_header *hdr)
 | 
									   struct pci_epf_header *hdr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -241,6 +274,45 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 encode_int)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dw_pcie_ep *ep = epc_get_drvdata(epc);
 | 
				
			||||||
 | 
						struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 | 
				
			||||||
 | 
						u32 val, reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ep->msix_cap)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = ep->msix_cap + PCI_MSIX_FLAGS;
 | 
				
			||||||
 | 
						val = dw_pcie_readw_dbi(pci, reg);
 | 
				
			||||||
 | 
						if (!(val & PCI_MSIX_FLAGS_ENABLE))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						val &= PCI_MSIX_FLAGS_QSIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dw_pcie_ep *ep = epc_get_drvdata(epc);
 | 
				
			||||||
 | 
						struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 | 
				
			||||||
 | 
						u32 val, reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ep->msix_cap)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = ep->msix_cap + PCI_MSIX_FLAGS;
 | 
				
			||||||
 | 
						val = dw_pcie_readw_dbi(pci, reg);
 | 
				
			||||||
 | 
						val &= ~PCI_MSIX_FLAGS_QSIZE;
 | 
				
			||||||
 | 
						val |= interrupts;
 | 
				
			||||||
 | 
						dw_pcie_dbi_ro_wr_en(pci);
 | 
				
			||||||
 | 
						dw_pcie_writew_dbi(pci, reg, val);
 | 
				
			||||||
 | 
						dw_pcie_dbi_ro_wr_dis(pci);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
 | 
					static int dw_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
 | 
				
			||||||
				enum pci_epc_irq_type type, u16 interrupt_num)
 | 
									enum pci_epc_irq_type type, u16 interrupt_num)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -282,6 +354,8 @@ static const struct pci_epc_ops epc_ops = {
 | 
				
			||||||
	.unmap_addr		= dw_pcie_ep_unmap_addr,
 | 
						.unmap_addr		= dw_pcie_ep_unmap_addr,
 | 
				
			||||||
	.set_msi		= dw_pcie_ep_set_msi,
 | 
						.set_msi		= dw_pcie_ep_set_msi,
 | 
				
			||||||
	.get_msi		= dw_pcie_ep_get_msi,
 | 
						.get_msi		= dw_pcie_ep_get_msi,
 | 
				
			||||||
 | 
						.set_msix		= dw_pcie_ep_set_msix,
 | 
				
			||||||
 | 
						.get_msix		= dw_pcie_ep_get_msix,
 | 
				
			||||||
	.raise_irq		= dw_pcie_ep_raise_irq,
 | 
						.raise_irq		= dw_pcie_ep_raise_irq,
 | 
				
			||||||
	.start			= dw_pcie_ep_start,
 | 
						.start			= dw_pcie_ep_start,
 | 
				
			||||||
	.stop			= dw_pcie_ep_stop,
 | 
						.stop			= dw_pcie_ep_stop,
 | 
				
			||||||
| 
						 | 
					@ -322,6 +396,64 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
				
			||||||
 | 
								     u16 interrupt_num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 | 
				
			||||||
 | 
						struct pci_epc *epc = ep->epc;
 | 
				
			||||||
 | 
						u16 tbl_offset, bir;
 | 
				
			||||||
 | 
						u32 bar_addr_upper, bar_addr_lower;
 | 
				
			||||||
 | 
						u32 msg_addr_upper, msg_addr_lower;
 | 
				
			||||||
 | 
						u32 reg, msg_data, vec_ctrl;
 | 
				
			||||||
 | 
						u64 tbl_addr, msg_addr, reg_u64;
 | 
				
			||||||
 | 
						void __iomem *msix_tbl;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = ep->msix_cap + PCI_MSIX_TABLE;
 | 
				
			||||||
 | 
						tbl_offset = dw_pcie_readl_dbi(pci, reg);
 | 
				
			||||||
 | 
						bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
 | 
				
			||||||
 | 
						tbl_offset &= PCI_MSIX_TABLE_OFFSET;
 | 
				
			||||||
 | 
						tbl_offset >>= 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reg = PCI_BASE_ADDRESS_0 + (4 * bir);
 | 
				
			||||||
 | 
						bar_addr_upper = 0;
 | 
				
			||||||
 | 
						bar_addr_lower = dw_pcie_readl_dbi(pci, reg);
 | 
				
			||||||
 | 
						reg_u64 = (bar_addr_lower & PCI_BASE_ADDRESS_MEM_TYPE_MASK);
 | 
				
			||||||
 | 
						if (reg_u64 == PCI_BASE_ADDRESS_MEM_TYPE_64)
 | 
				
			||||||
 | 
							bar_addr_upper = dw_pcie_readl_dbi(pci, reg + 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tbl_addr = ((u64) bar_addr_upper) << 32 | bar_addr_lower;
 | 
				
			||||||
 | 
						tbl_addr += (tbl_offset + ((interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE));
 | 
				
			||||||
 | 
						tbl_addr &= PCI_BASE_ADDRESS_MEM_MASK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msix_tbl = ioremap_nocache(ep->phys_base + tbl_addr,
 | 
				
			||||||
 | 
									   PCI_MSIX_ENTRY_SIZE);
 | 
				
			||||||
 | 
						if (!msix_tbl)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg_addr_lower = readl(msix_tbl + PCI_MSIX_ENTRY_LOWER_ADDR);
 | 
				
			||||||
 | 
						msg_addr_upper = readl(msix_tbl + PCI_MSIX_ENTRY_UPPER_ADDR);
 | 
				
			||||||
 | 
						msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
 | 
				
			||||||
 | 
						msg_data = readl(msix_tbl + PCI_MSIX_ENTRY_DATA);
 | 
				
			||||||
 | 
						vec_ctrl = readl(msix_tbl + PCI_MSIX_ENTRY_VECTOR_CTRL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iounmap(msix_tbl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)
 | 
				
			||||||
 | 
							return -EPERM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr,
 | 
				
			||||||
 | 
									  epc->mem->page_size);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writel(msg_data, ep->msi_mem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dw_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
 | 
					void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pci_epc *epc = ep->epc;
 | 
						struct pci_epc *epc = ep->epc;
 | 
				
			||||||
| 
						 | 
					@ -412,9 +544,12 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
 | 
				
			||||||
	ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
 | 
						ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
 | 
				
			||||||
					     epc->mem->page_size);
 | 
										     epc->mem->page_size);
 | 
				
			||||||
	if (!ep->msi_mem) {
 | 
						if (!ep->msi_mem) {
 | 
				
			||||||
		dev_err(dev, "Failed to reserve memory for MSI\n");
 | 
							dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						ep->msi_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dw_pcie_setup(pci);
 | 
						dw_pcie_setup(pci);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +91,8 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	case PCI_EPC_IRQ_MSI:
 | 
						case PCI_EPC_IRQ_MSI:
 | 
				
			||||||
		return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
 | 
							return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
 | 
				
			||||||
 | 
						case PCI_EPC_IRQ_MSIX:
 | 
				
			||||||
 | 
							return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		dev_err(pci->dev, "UNKNOWN IRQ type\n");
 | 
							dev_err(pci->dev, "UNKNOWN IRQ type\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -208,6 +208,8 @@ struct dw_pcie_ep {
 | 
				
			||||||
	u32			num_ob_windows;
 | 
						u32			num_ob_windows;
 | 
				
			||||||
	void __iomem		*msi_mem;
 | 
						void __iomem		*msi_mem;
 | 
				
			||||||
	phys_addr_t		msi_mem_phys;
 | 
						phys_addr_t		msi_mem_phys;
 | 
				
			||||||
 | 
						u8			msi_cap;	/* MSI capability offset */
 | 
				
			||||||
 | 
						u8			msix_cap;	/* MSI-X capability offset */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct dw_pcie_ops {
 | 
					struct dw_pcie_ops {
 | 
				
			||||||
| 
						 | 
					@ -359,6 +361,8 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep);
 | 
				
			||||||
void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
 | 
					void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
 | 
				
			||||||
int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
					int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
				
			||||||
			     u8 interrupt_num);
 | 
								     u8 interrupt_num);
 | 
				
			||||||
 | 
					int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
				
			||||||
 | 
								     u16 interrupt_num);
 | 
				
			||||||
void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
 | 
					void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
 | 
					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,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
				
			||||||
 | 
										   u16 interrupt_num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
 | 
					static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue