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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
				   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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
				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,
 | 
			
		||||
	.set_msi		= dw_pcie_ep_set_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,
 | 
			
		||||
	.start			= dw_pcie_ep_start,
 | 
			
		||||
	.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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	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,
 | 
			
		||||
					     epc->mem->page_size);
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,8 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
			
		|||
		return -EINVAL;
 | 
			
		||||
	case PCI_EPC_IRQ_MSI:
 | 
			
		||||
		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:
 | 
			
		||||
		dev_err(pci->dev, "UNKNOWN IRQ type\n");
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -208,6 +208,8 @@ struct dw_pcie_ep {
 | 
			
		|||
	u32			num_ob_windows;
 | 
			
		||||
	void __iomem		*msi_mem;
 | 
			
		||||
	phys_addr_t		msi_mem_phys;
 | 
			
		||||
	u8			msi_cap;	/* MSI capability offset */
 | 
			
		||||
	u8			msix_cap;	/* MSI-X capability offset */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
 | 
			
		||||
			     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);
 | 
			
		||||
#else
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue