diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-designware-ep.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-ep.c | 157 | 
1 files changed, 98 insertions, 59 deletions
| diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 47391d7d3a73..43ba5c6738df 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -16,30 +16,6 @@  #include <linux/pci-epf.h>  /** - * dw_pcie_ep_linkup - Notify EPF drivers about Link Up event - * @ep: DWC EP device - */ -void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) -{ -	struct pci_epc *epc = ep->epc; - -	pci_epc_linkup(epc); -} -EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup); - -/** - * dw_pcie_ep_init_notify - Notify EPF drivers about EPC initialization complete - * @ep: DWC EP device - */ -void dw_pcie_ep_init_notify(struct dw_pcie_ep *ep) -{ -	struct pci_epc *epc = ep->epc; - -	pci_epc_init_notify(epc); -} -EXPORT_SYMBOL_GPL(dw_pcie_ep_init_notify); - -/**   * dw_pcie_ep_get_func_from_ep - Get the struct dw_pcie_ep_func corresponding to   *				 the endpoint function   * @ep: DWC EP device @@ -161,7 +137,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,  	if (!ep->bar_to_atu[bar])  		free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows);  	else -		free_win = ep->bar_to_atu[bar]; +		free_win = ep->bar_to_atu[bar] - 1;  	if (free_win >= pci->num_ib_windows) {  		dev_err(pci->dev, "No free inbound window\n"); @@ -175,15 +151,18 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,  		return ret;  	} -	ep->bar_to_atu[bar] = free_win; +	/* +	 * Always increment free_win before assignment, since value 0 is used to identify +	 * unallocated mapping. +	 */ +	ep->bar_to_atu[bar] = free_win + 1;  	set_bit(free_win, ep->ib_window_map);  	return 0;  } -static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no, -				   phys_addr_t phys_addr, -				   u64 pci_addr, size_t size) +static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, +				   struct dw_pcie_ob_atu_cfg *atu)  {  	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);  	u32 free_win; @@ -195,13 +174,13 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, u8 func_no,  		return -EINVAL;  	} -	ret = dw_pcie_prog_ep_outbound_atu(pci, func_no, free_win, PCIE_ATU_TYPE_MEM, -					   phys_addr, pci_addr, size); +	atu->index = free_win; +	ret = dw_pcie_prog_outbound_atu(pci, atu);  	if (ret)  		return ret;  	set_bit(free_win, ep->ob_window_map); -	ep->outbound_addr[free_win] = phys_addr; +	ep->outbound_addr[free_win] = atu->cpu_addr;  	return 0;  } @@ -212,7 +191,10 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,  	struct dw_pcie_ep *ep = epc_get_drvdata(epc);  	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);  	enum pci_barno bar = epf_bar->barno; -	u32 atu_index = ep->bar_to_atu[bar]; +	u32 atu_index = ep->bar_to_atu[bar] - 1; + +	if (!ep->bar_to_atu[bar]) +		return;  	__dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags); @@ -233,6 +215,13 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,  	int ret, type;  	u32 reg; +	/* +	 * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs +	 * 1 and 2 to form a 64-bit BAR. +	 */ +	if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1)) +		return -EINVAL; +  	reg = PCI_BASE_ADDRESS_0 + (4 * bar);  	if (!(flags & PCI_BASE_ADDRESS_SPACE)) @@ -301,8 +290,14 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,  	int ret;  	struct dw_pcie_ep *ep = epc_get_drvdata(epc);  	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); - -	ret = dw_pcie_ep_outbound_atu(ep, func_no, addr, pci_addr, size); +	struct dw_pcie_ob_atu_cfg atu = { 0 }; + +	atu.func_no = func_no; +	atu.type = PCIE_ATU_TYPE_MEM; +	atu.cpu_addr = addr; +	atu.pci_addr = pci_addr; +	atu.size = size; +	ret = dw_pcie_ep_outbound_atu(ep, &atu);  	if (ret) {  		dev_err(pci->dev, "Failed to enable address\n");  		return ret; @@ -632,7 +627,6 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)  	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);  	dw_pcie_edma_remove(pci); -	ep->epc->init_complete = false;  }  EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup); @@ -674,6 +668,34 @@ static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)  	return 0;  } +static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) +{ +	unsigned int offset; +	unsigned int nbars; +	u32 reg, i; + +	offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + +	dw_pcie_dbi_ro_wr_en(pci); + +	if (offset) { +		reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); +		nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >> +			PCI_REBAR_CTRL_NBAR_SHIFT; + +		/* +		 * PCIe r6.0, sec 7.8.6.2 require us to support at least one +		 * size in the range from 1 MB to 512 GB. Advertise support +		 * for 1 MB BAR size only. +		 */ +		for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) +			dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, 0x0); +	} + +	dw_pcie_setup(pci); +	dw_pcie_dbi_ro_wr_dis(pci); +} +  /**   * dw_pcie_ep_init_registers - Initialize DWC EP specific registers   * @ep: DWC EP device @@ -688,13 +710,11 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)  	struct dw_pcie_ep_func *ep_func;  	struct device *dev = pci->dev;  	struct pci_epc *epc = ep->epc; -	unsigned int offset, ptm_cap_base; -	unsigned int nbars; +	u32 ptm_cap_base, reg;  	u8 hdr_type;  	u8 func_no; -	int i, ret;  	void *addr; -	u32 reg; +	int ret;  	hdr_type = dw_pcie_readb_dbi(pci, PCI_HEADER_TYPE) &  		   PCI_HEADER_TYPE_MASK; @@ -757,25 +777,8 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)  	if (ep->ops->init)  		ep->ops->init(ep); -	offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);  	ptm_cap_base = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM); -	dw_pcie_dbi_ro_wr_en(pci); - -	if (offset) { -		reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); -		nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >> -			PCI_REBAR_CTRL_NBAR_SHIFT; - -		/* -		 * PCIe r6.0, sec 7.8.6.2 require us to support at least one -		 * size in the range from 1 MB to 512 GB. Advertise support -		 * for 1 MB BAR size only. -		 */ -		for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) -			dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4)); -	} -  	/*  	 * PTM responder capability can be disabled only after disabling  	 * PTM root capability. @@ -792,8 +795,7 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)  		dw_pcie_dbi_ro_wr_dis(pci);  	} -	dw_pcie_setup(pci); -	dw_pcie_dbi_ro_wr_dis(pci); +	dw_pcie_ep_init_non_sticky_registers(pci);  	return 0; @@ -805,6 +807,43 @@ err_remove_edma:  EXPORT_SYMBOL_GPL(dw_pcie_ep_init_registers);  /** + * dw_pcie_ep_linkup - Notify EPF drivers about Link Up event + * @ep: DWC EP device + */ +void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) +{ +	struct pci_epc *epc = ep->epc; + +	pci_epc_linkup(epc); +} +EXPORT_SYMBOL_GPL(dw_pcie_ep_linkup); + +/** + * dw_pcie_ep_linkdown - Notify EPF drivers about Link Down event + * @ep: DWC EP device + * + * Non-sticky registers are also initialized before sending the notification to + * the EPF drivers. This is needed since the registers need to be initialized + * before the link comes back again. + */ +void dw_pcie_ep_linkdown(struct dw_pcie_ep *ep) +{ +	struct dw_pcie *pci = to_dw_pcie_from_ep(ep); +	struct pci_epc *epc = ep->epc; + +	/* +	 * Initialize the non-sticky DWC registers as they would've reset post +	 * Link Down. This is specifically needed for drivers not supporting +	 * PERST# as they have no way to reinitialize the registers before the +	 * link comes back again. +	 */ +	dw_pcie_ep_init_non_sticky_registers(pci); + +	pci_epc_linkdown(epc); +} +EXPORT_SYMBOL_GPL(dw_pcie_ep_linkdown); + +/**   * dw_pcie_ep_init - Initialize the endpoint device   * @ep: DWC EP device   * |