diff options
Diffstat (limited to 'drivers/pci/controller/pcie-iproc.c')
| -rw-r--r-- | drivers/pci/controller/pcie-iproc.c | 98 | 
1 files changed, 90 insertions, 8 deletions
| diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index c20fd6bd68fd..e3ca46497470 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -60,6 +60,10 @@  #define APB_ERR_EN_SHIFT		0  #define APB_ERR_EN			BIT(APB_ERR_EN_SHIFT) +#define CFG_RD_SUCCESS			0 +#define CFG_RD_UR			1 +#define CFG_RD_CRS			2 +#define CFG_RD_CA			3  #define CFG_RETRY_STATUS		0xffff0001  #define CFG_RETRY_STATUS_TIMEOUT_US	500000 /* 500 milliseconds */ @@ -289,6 +293,9 @@ enum iproc_pcie_reg {  	IPROC_PCIE_IARR4,  	IPROC_PCIE_IMAP4, +	/* config read status */ +	IPROC_PCIE_CFG_RD_STATUS, +  	/* link status */  	IPROC_PCIE_LINK_STATUS, @@ -350,6 +357,7 @@ static const u16 iproc_pcie_reg_paxb_v2[] = {  	[IPROC_PCIE_IMAP3]		= 0xe08,  	[IPROC_PCIE_IARR4]		= 0xe68,  	[IPROC_PCIE_IMAP4]		= 0xe70, +	[IPROC_PCIE_CFG_RD_STATUS]	= 0xee0,  	[IPROC_PCIE_LINK_STATUS]	= 0xf0c,  	[IPROC_PCIE_APB_ERR_EN]		= 0xf40,  }; @@ -474,10 +482,12 @@ static void __iomem *iproc_pcie_map_ep_cfg_reg(struct iproc_pcie *pcie,  	return (pcie->base + offset);  } -static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p) +static unsigned int iproc_pcie_cfg_retry(struct iproc_pcie *pcie, +					 void __iomem *cfg_data_p)  {  	int timeout = CFG_RETRY_STATUS_TIMEOUT_US;  	unsigned int data; +	u32 status;  	/*  	 * As per PCIe spec r3.1, sec 2.3.2, CRS Software Visibility only @@ -498,6 +508,15 @@ static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p)  	 */  	data = readl(cfg_data_p);  	while (data == CFG_RETRY_STATUS && timeout--) { +		/* +		 * CRS state is set in CFG_RD status register +		 * This will handle the case where CFG_RETRY_STATUS is +		 * valid config data. +		 */ +		status = iproc_pcie_read_reg(pcie, IPROC_PCIE_CFG_RD_STATUS); +		if (status != CFG_RD_CRS) +			return data; +  		udelay(1);  		data = readl(cfg_data_p);  	} @@ -576,7 +595,7 @@ static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,  	if (!cfg_data_p)  		return PCIBIOS_DEVICE_NOT_FOUND; -	data = iproc_pcie_cfg_retry(cfg_data_p); +	data = iproc_pcie_cfg_retry(pcie, cfg_data_p);  	*val = data;  	if (size <= 2) @@ -936,8 +955,25 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,  			resource_size_t window_size =  				ob_map->window_sizes[size_idx] * SZ_1M; -			if (size < window_size) -				continue; +			/* +			 * Keep iterating until we reach the last window and +			 * with the minimal window size at index zero. In this +			 * case, we take a compromise by mapping it using the +			 * minimum window size that can be supported +			 */ +			if (size < window_size) { +				if (size_idx > 0 || window_idx > 0) +					continue; + +				/* +				 * For the corner case of reaching the minimal +				 * window size that can be supported on the +				 * last window +				 */ +				axi_addr = ALIGN_DOWN(axi_addr, window_size); +				pci_addr = ALIGN_DOWN(pci_addr, window_size); +				size = window_size; +			}  			if (!IS_ALIGNED(axi_addr, window_size) ||  			    !IS_ALIGNED(pci_addr, window_size)) { @@ -1146,11 +1182,43 @@ err_ib:  	return ret;  } +static int iproc_pcie_add_dma_range(struct device *dev, +				    struct list_head *resources, +				    struct of_pci_range *range) +{ +	struct resource *res; +	struct resource_entry *entry, *tmp; +	struct list_head *head = resources; + +	res = devm_kzalloc(dev, sizeof(struct resource), GFP_KERNEL); +	if (!res) +		return -ENOMEM; + +	resource_list_for_each_entry(tmp, resources) { +		if (tmp->res->start < range->cpu_addr) +			head = &tmp->node; +	} + +	res->start = range->cpu_addr; +	res->end = res->start + range->size - 1; + +	entry = resource_list_create_entry(res, 0); +	if (!entry) +		return -ENOMEM; + +	entry->offset = res->start - range->cpu_addr; +	resource_list_add(entry, head); + +	return 0; +} +  static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie)  { +	struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);  	struct of_pci_range range;  	struct of_pci_range_parser parser;  	int ret; +	LIST_HEAD(resources);  	/* Get the dma-ranges from DT */  	ret = of_pci_dma_range_parser_init(&parser, pcie->dev->of_node); @@ -1158,13 +1226,23 @@ static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie)  		return ret;  	for_each_of_pci_range(&parser, &range) { +		ret = iproc_pcie_add_dma_range(pcie->dev, +					       &resources, +					       &range); +		if (ret) +			goto out;  		/* Each range entry corresponds to an inbound mapping region */  		ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_MEM);  		if (ret) -			return ret; +			goto out;  	} +	list_splice_init(&resources, &host->dma_ranges); +  	return 0; +out: +	pci_free_resource_list(&resources); +	return ret;  }  static int iproce_pcie_get_msi(struct iproc_pcie *pcie, @@ -1320,14 +1398,18 @@ static int iproc_pcie_msi_enable(struct iproc_pcie *pcie)  	if (pcie->need_msi_steer) {  		ret = iproc_pcie_msi_steer(pcie, msi_node);  		if (ret) -			return ret; +			goto out_put_node;  	}  	/*  	 * If another MSI controller is being used, the call below should fail  	 * but that is okay  	 */ -	return iproc_msi_init(pcie, msi_node); +	ret = iproc_msi_init(pcie, msi_node); + +out_put_node: +	of_node_put(msi_node); +	return ret;  }  static void iproc_pcie_msi_disable(struct iproc_pcie *pcie) @@ -1347,7 +1429,6 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)  		break;  	case IPROC_PCIE_PAXB:  		regs = iproc_pcie_reg_paxb; -		pcie->iproc_cfg_read = true;  		pcie->has_apb_err_disable = true;  		if (pcie->need_ob_cfg) {  			pcie->ob_map = paxb_ob_map; @@ -1356,6 +1437,7 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)  		break;  	case IPROC_PCIE_PAXB_V2:  		regs = iproc_pcie_reg_paxb_v2; +		pcie->iproc_cfg_read = true;  		pcie->has_apb_err_disable = true;  		if (pcie->need_ob_cfg) {  			pcie->ob_map = paxb_v2_ob_map; |