mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 14:04:26 -04:00 
			
		
		
		
	This updates the iProc PCIe driver to the version currently submitted for kernel 4.5. Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> SVN-Revision: 47688
		
			
				
	
	
		
			237 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From e99a187b5c5f60fe55ca586f82ac1a3557fb166a Mon Sep 17 00:00:00 2001
 | |
| From: Ray Jui <rjui@broadcom.com>
 | |
| Date: Fri, 16 Oct 2015 08:18:24 -0500
 | |
| Subject: [PATCH 146/147] PCI: iproc: Add outbound mapping support
 | |
| 
 | |
| Certain SoCs require the PCIe outbound mapping to be configured in
 | |
| software.  Add support for those chips.
 | |
| 
 | |
| [jonmason: Use %pap format when printing size_t to avoid warnings in 32-bit
 | |
| build.]
 | |
| [arnd: Use div64_u64() instead of "%" to avoid __aeabi_uldivmod link error
 | |
| in 32-bit build.]
 | |
| Signed-off-by: Ray Jui <rjui@broadcom.com>
 | |
| Signed-off-by: Jon Mason <jonmason@broadcom.com>
 | |
| Signed-off-by: Arnd Bergmann <arnd@arndb.de>
 | |
| Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
 | |
| ---
 | |
|  drivers/pci/host/pcie-iproc-platform.c |  27 ++++++++
 | |
|  drivers/pci/host/pcie-iproc.c          | 115 +++++++++++++++++++++++++++++++++
 | |
|  drivers/pci/host/pcie-iproc.h          |  17 +++++
 | |
|  3 files changed, 159 insertions(+)
 | |
| 
 | |
| --- a/drivers/pci/host/pcie-iproc-platform.c
 | |
| +++ b/drivers/pci/host/pcie-iproc-platform.c
 | |
| @@ -54,6 +54,33 @@ static int iproc_pcie_pltfm_probe(struct
 | |
|  		return -ENOMEM;
 | |
|  	}
 | |
|  
 | |
| +	if (of_property_read_bool(np, "brcm,pcie-ob")) {
 | |
| +		u32 val;
 | |
| +
 | |
| +		ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
 | |
| +					   &val);
 | |
| +		if (ret) {
 | |
| +			dev_err(pcie->dev,
 | |
| +				"missing brcm,pcie-ob-axi-offset property\n");
 | |
| +			return ret;
 | |
| +		}
 | |
| +		pcie->ob.axi_offset = val;
 | |
| +
 | |
| +		ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
 | |
| +					   &val);
 | |
| +		if (ret) {
 | |
| +			dev_err(pcie->dev,
 | |
| +				"missing brcm,pcie-ob-window-size property\n");
 | |
| +			return ret;
 | |
| +		}
 | |
| +		pcie->ob.window_size = (resource_size_t)val * SZ_1M;
 | |
| +
 | |
| +		if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
 | |
| +			pcie->ob.set_oarr_size = true;
 | |
| +
 | |
| +		pcie->need_ob_cfg = true;
 | |
| +	}
 | |
| +
 | |
|  	/* PHY use is optional */
 | |
|  	pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
 | |
|  	if (IS_ERR(pcie->phy)) {
 | |
| --- a/drivers/pci/host/pcie-iproc.c
 | |
| +++ b/drivers/pci/host/pcie-iproc.c
 | |
| @@ -66,6 +66,18 @@
 | |
|  #define PCIE_DL_ACTIVE_SHIFT         2
 | |
|  #define PCIE_DL_ACTIVE               BIT(PCIE_DL_ACTIVE_SHIFT)
 | |
|  
 | |
| +#define OARR_VALID_SHIFT             0
 | |
| +#define OARR_VALID                   BIT(OARR_VALID_SHIFT)
 | |
| +#define OARR_SIZE_CFG_SHIFT          1
 | |
| +#define OARR_SIZE_CFG                BIT(OARR_SIZE_CFG_SHIFT)
 | |
| +
 | |
| +#define OARR_LO(window)              (0xd20 + (window) * 8)
 | |
| +#define OARR_HI(window)              (0xd24 + (window) * 8)
 | |
| +#define OMAP_LO(window)              (0xd40 + (window) * 8)
 | |
| +#define OMAP_HI(window)              (0xd44 + (window) * 8)
 | |
| +
 | |
| +#define MAX_NUM_OB_WINDOWS           2
 | |
| +
 | |
|  static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
 | |
|  {
 | |
|  	struct iproc_pcie *pcie;
 | |
| @@ -212,6 +224,101 @@ static void iproc_pcie_enable(struct ipr
 | |
|  	writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
 | |
|  }
 | |
|  
 | |
| +/**
 | |
| + * Some iProc SoCs require the SW to configure the outbound address mapping
 | |
| + *
 | |
| + * Outbound address translation:
 | |
| + *
 | |
| + * iproc_pcie_address = axi_address - axi_offset
 | |
| + * OARR = iproc_pcie_address
 | |
| + * OMAP = pci_addr
 | |
| + *
 | |
| + * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
 | |
| + */
 | |
| +static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
 | |
| +			       u64 pci_addr, resource_size_t size)
 | |
| +{
 | |
| +	struct iproc_pcie_ob *ob = &pcie->ob;
 | |
| +	unsigned i;
 | |
| +	u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
 | |
| +	u64 remainder;
 | |
| +
 | |
| +	if (size > max_size) {
 | |
| +		dev_err(pcie->dev,
 | |
| +			"res size 0x%pap exceeds max supported size 0x%llx\n",
 | |
| +			&size, max_size);
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
| +
 | |
| +	div64_u64_rem(size, ob->window_size, &remainder);
 | |
| +	if (remainder) {
 | |
| +		dev_err(pcie->dev,
 | |
| +			"res size %pap needs to be multiple of window size %pap\n",
 | |
| +			&size, &ob->window_size);
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
| +
 | |
| +	if (axi_addr < ob->axi_offset) {
 | |
| +		dev_err(pcie->dev,
 | |
| +			"axi address %pap less than offset %pap\n",
 | |
| +			&axi_addr, &ob->axi_offset);
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
| +
 | |
| +	/*
 | |
| +	 * Translate the AXI address to the internal address used by the iProc
 | |
| +	 * PCIe core before programming the OARR
 | |
| +	 */
 | |
| +	axi_addr -= ob->axi_offset;
 | |
| +
 | |
| +	for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
 | |
| +		writel(lower_32_bits(axi_addr) | OARR_VALID |
 | |
| +		       (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
 | |
| +		writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
 | |
| +		writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
 | |
| +		writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
 | |
| +
 | |
| +		size -= ob->window_size;
 | |
| +		if (size == 0)
 | |
| +			break;
 | |
| +
 | |
| +		axi_addr += ob->window_size;
 | |
| +		pci_addr += ob->window_size;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
 | |
| +				 struct list_head *resources)
 | |
| +{
 | |
| +	struct resource_entry *window;
 | |
| +	int ret;
 | |
| +
 | |
| +	resource_list_for_each_entry(window, resources) {
 | |
| +		struct resource *res = window->res;
 | |
| +		u64 res_type = resource_type(res);
 | |
| +
 | |
| +		switch (res_type) {
 | |
| +		case IORESOURCE_IO:
 | |
| +		case IORESOURCE_BUS:
 | |
| +			break;
 | |
| +		case IORESOURCE_MEM:
 | |
| +			ret = iproc_pcie_setup_ob(pcie, res->start,
 | |
| +						  res->start - window->offset,
 | |
| +						  resource_size(res));
 | |
| +			if (ret)
 | |
| +				return ret;
 | |
| +			break;
 | |
| +		default:
 | |
| +			dev_err(pcie->dev, "invalid resource %pR\n", res);
 | |
| +			return -EINVAL;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
|  int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 | |
|  {
 | |
|  	int ret;
 | |
| @@ -235,6 +342,14 @@ int iproc_pcie_setup(struct iproc_pcie *
 | |
|  
 | |
|  	iproc_pcie_reset(pcie);
 | |
|  
 | |
| +	if (pcie->need_ob_cfg) {
 | |
| +		ret = iproc_pcie_map_ranges(pcie, res);
 | |
| +		if (ret) {
 | |
| +			dev_err(pcie->dev, "map failed\n");
 | |
| +			goto err_power_off_phy;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
|  #ifdef CONFIG_ARM
 | |
|  	pcie->sysdata.private_data = pcie;
 | |
|  	sysdata = &pcie->sysdata;
 | |
| --- a/drivers/pci/host/pcie-iproc.h
 | |
| +++ b/drivers/pci/host/pcie-iproc.h
 | |
| @@ -15,6 +15,19 @@
 | |
|  #define _PCIE_IPROC_H
 | |
|  
 | |
|  /**
 | |
| + * iProc PCIe outbound mapping
 | |
| + * @set_oarr_size: indicates the OARR size bit needs to be set
 | |
| + * @axi_offset: offset from the AXI address to the internal address used by
 | |
| + * the iProc PCIe core
 | |
| + * @window_size: outbound window size
 | |
| + */
 | |
| +struct iproc_pcie_ob {
 | |
| +	bool set_oarr_size;
 | |
| +	resource_size_t axi_offset;
 | |
| +	resource_size_t window_size;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
|   * iProc PCIe device
 | |
|   * @dev: pointer to device data structure
 | |
|   * @base: PCIe host controller I/O register base
 | |
| @@ -23,6 +36,8 @@
 | |
|   * @phy: optional PHY device that controls the Serdes
 | |
|   * @irqs: interrupt IDs
 | |
|   * @map_irq: function callback to map interrupts
 | |
| + * @need_ob_cfg: indidates SW needs to configure the outbound mapping window
 | |
| + * @ob: outbound mapping parameters
 | |
|   */
 | |
|  struct iproc_pcie {
 | |
|  	struct device *dev;
 | |
| @@ -33,6 +48,8 @@ struct iproc_pcie {
 | |
|  	struct pci_bus *root_bus;
 | |
|  	struct phy *phy;
 | |
|  	int (*map_irq)(const struct pci_dev *, u8, u8);
 | |
| +	bool need_ob_cfg;
 | |
| +	struct iproc_pcie_ob ob;
 | |
|  };
 | |
|  
 | |
|  int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
 |