diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-designware-ep.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-ep.c | 210 | 
1 files changed, 185 insertions, 25 deletions
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 8650416f6f9e..1e7b02221eac 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)  	__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)  { @@ -213,36 +246,84 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,  static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)  { -	int val;  	struct dw_pcie_ep *ep = epc_get_drvdata(epc);  	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	u32 val, reg; + +	if (!ep->msi_cap) +		return -EINVAL; + +	reg = ep->msi_cap + PCI_MSI_FLAGS; +	val = dw_pcie_readw_dbi(pci, reg); +	if (!(val & PCI_MSI_FLAGS_ENABLE)) +		return -EINVAL; + +	val = (val & PCI_MSI_FLAGS_QSIZE) >> 4; + +	return val; +} + +static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 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->msi_cap) +		return -EINVAL; + +	reg = ep->msi_cap + PCI_MSI_FLAGS; +	val = dw_pcie_readw_dbi(pci, reg); +	val &= ~PCI_MSI_FLAGS_QMASK; +	val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK; +	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_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; -	val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); -	if (!(val & MSI_CAP_MSI_EN_MASK)) +	if (!ep->msix_cap)  		return -EINVAL; -	val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT; +	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_msi(struct pci_epc *epc, u8 func_no, u8 encode_int) +static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts)  { -	int val;  	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; -	val = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); -	val &= ~MSI_CAP_MMC_MASK; -	val |= (encode_int << MSI_CAP_MMC_SHIFT) & MSI_CAP_MMC_MASK; +	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, MSI_MESSAGE_CONTROL, val); +	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, u8 interrupt_num) +				enum pci_epc_irq_type type, u16 interrupt_num)  {  	struct dw_pcie_ep *ep = epc_get_drvdata(epc); @@ -282,32 +363,52 @@ 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,  }; +int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no) +{ +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	struct device *dev = pci->dev; + +	dev_err(dev, "EP cannot trigger legacy IRQs\n"); + +	return -EINVAL; +} +  int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,  			     u8 interrupt_num)  {  	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);  	struct pci_epc *epc = ep->epc;  	u16 msg_ctrl, msg_data; -	u32 msg_addr_lower, msg_addr_upper; +	u32 msg_addr_lower, msg_addr_upper, reg;  	u64 msg_addr;  	bool has_upper;  	int ret; +	if (!ep->msi_cap) +		return -EINVAL; +  	/* Raise MSI per the PCI Local Bus Specification Revision 3.0, 6.8.1. */ -	msg_ctrl = dw_pcie_readw_dbi(pci, MSI_MESSAGE_CONTROL); +	reg = ep->msi_cap + PCI_MSI_FLAGS; +	msg_ctrl = dw_pcie_readw_dbi(pci, reg);  	has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); -	msg_addr_lower = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32); +	reg = ep->msi_cap + PCI_MSI_ADDRESS_LO; +	msg_addr_lower = dw_pcie_readl_dbi(pci, reg);  	if (has_upper) { -		msg_addr_upper = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32); -		msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_64); +		reg = ep->msi_cap + PCI_MSI_ADDRESS_HI; +		msg_addr_upper = dw_pcie_readl_dbi(pci, reg); +		reg = ep->msi_cap + PCI_MSI_DATA_64; +		msg_data = dw_pcie_readw_dbi(pci, reg);  	} else {  		msg_addr_upper = 0; -		msg_data = dw_pcie_readw_dbi(pci, MSI_MESSAGE_DATA_32); +		reg = ep->msi_cap + PCI_MSI_DATA_32; +		msg_data = dw_pcie_readw_dbi(pci, reg);  	}  	msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;  	ret = dw_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, msg_addr, @@ -322,6 +423,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; @@ -386,15 +545,18 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)  		return -ENOMEM;  	ep->outbound_addr = addr; -	if (ep->ops->ep_init) -		ep->ops->ep_init(ep); -  	epc = devm_pci_epc_create(dev, &epc_ops);  	if (IS_ERR(epc)) {  		dev_err(dev, "Failed to create epc device\n");  		return PTR_ERR(epc);  	} +	ep->epc = epc; +	epc_set_drvdata(epc, ep); + +	if (ep->ops->ep_init) +		ep->ops->ep_init(ep); +  	ret = of_property_read_u8(np, "max-functions", &epc->max_functions);  	if (ret < 0)  		epc->max_functions = 1; @@ -409,15 +571,13 @@ 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); -	epc->features = EPC_FEATURE_NO_LINKUP_NOTIFIER; -	EPC_FEATURE_SET_BAR(epc->features, BAR_0); +	ep->msix_cap = dw_pcie_ep_find_capability(pci, PCI_CAP_ID_MSIX); -	ep->epc = epc; -	epc_set_drvdata(epc, ep);  	dw_pcie_setup(pci);  	return 0;  |