diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-designware.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.c | 96 | 
1 files changed, 93 insertions, 3 deletions
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 7d25102c304c..820488dfeaed 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -14,6 +14,86 @@  #include "pcie-designware.h" +/* + * These interfaces resemble the pci_find_*capability() interfaces, but these + * are for configuring host controllers, which are bridges *to* PCI devices but + * are not PCI devices themselves. + */ +static u8 __dw_pcie_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, +				  u8 cap) +{ +	u8 cap_id, next_cap_ptr; +	u16 reg; + +	if (!cap_ptr) +		return 0; + +	reg = dw_pcie_readw_dbi(pci, cap_ptr); +	cap_id = (reg & 0x00ff); + +	if (cap_id > PCI_CAP_ID_MAX) +		return 0; + +	if (cap_id == cap) +		return cap_ptr; + +	next_cap_ptr = (reg & 0xff00) >> 8; +	return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); +} + +u8 dw_pcie_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); + +	return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); +} +EXPORT_SYMBOL_GPL(dw_pcie_find_capability); + +static u16 dw_pcie_find_next_ext_capability(struct dw_pcie *pci, u16 start, +					    u8 cap) +{ +	u32 header; +	int ttl; +	int pos = PCI_CFG_SPACE_SIZE; + +	/* minimum 8 bytes per capability */ +	ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; + +	if (start) +		pos = start; + +	header = dw_pcie_readl_dbi(pci, pos); +	/* +	 * If we have no capabilities, this is indicated by cap ID, +	 * cap version and next pointer all being 0. +	 */ +	if (header == 0) +		return 0; + +	while (ttl-- > 0) { +		if (PCI_EXT_CAP_ID(header) == cap && pos != start) +			return pos; + +		pos = PCI_EXT_CAP_NEXT(header); +		if (pos < PCI_CFG_SPACE_SIZE) +			break; + +		header = dw_pcie_readl_dbi(pci, pos); +	} + +	return 0; +} + +u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap) +{ +	return dw_pcie_find_next_ext_capability(pci, 0, cap); +} +EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability); +  int dw_pcie_read(void __iomem *addr, int size, u32 *val)  {  	if (!IS_ALIGNED((uintptr_t)addr, size)) { @@ -376,10 +456,11 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)  		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);  	} -	dev_err(pci->dev, "Phy link never came up\n"); +	dev_info(pci->dev, "Phy link never came up\n");  	return -ETIMEDOUT;  } +EXPORT_SYMBOL_GPL(dw_pcie_wait_for_link);  int dw_pcie_link_up(struct dw_pcie *pci)  { @@ -423,8 +504,10 @@ void dw_pcie_setup(struct dw_pcie *pci)  	ret = of_property_read_u32(np, "num-lanes", &lanes); -	if (ret) -		lanes = 0; +	if (ret) { +		dev_dbg(pci->dev, "property num-lanes isn't found\n"); +		return; +	}  	/* Set the number of lanes */  	val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL); @@ -466,4 +549,11 @@ void dw_pcie_setup(struct dw_pcie *pci)  		break;  	}  	dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); + +	if (of_property_read_bool(np, "snps,enable-cdm-check")) { +		val = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS); +		val |= PCIE_PL_CHK_REG_CHK_REG_CONTINUOUS | +		       PCIE_PL_CHK_REG_CHK_REG_START; +		dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val); +	}  }  |