diff options
Diffstat (limited to 'drivers/pci/pci-aardvark.c')
| -rw-r--r-- | drivers/pci/pci-aardvark.c | 158 | 
1 files changed, 157 insertions, 1 deletions
| diff --git a/drivers/pci/pci-aardvark.c b/drivers/pci/pci-aardvark.c index ae1a20551fe..96aa039bdc2 100644 --- a/drivers/pci/pci-aardvark.c +++ b/drivers/pci/pci-aardvark.c @@ -99,6 +99,46 @@  #define     PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE	BIT(5)  #define     PCIE_CORE_CTRL2_ADDRWIN_MAP_ENABLE	BIT(6) +/* PCIe window configuration */ +#define OB_WIN_BASE_ADDR			0x4c00 +#define OB_WIN_BLOCK_SIZE			0x20 +#define OB_WIN_COUNT				8 +#define OB_WIN_REG_ADDR(win, offset)		(OB_WIN_BASE_ADDR + \ +						 OB_WIN_BLOCK_SIZE * (win) + \ +						 (offset)) +#define OB_WIN_MATCH_LS(win)			OB_WIN_REG_ADDR(win, 0x00) +#define     OB_WIN_ENABLE			BIT(0) +#define OB_WIN_MATCH_MS(win)			OB_WIN_REG_ADDR(win, 0x04) +#define OB_WIN_REMAP_LS(win)			OB_WIN_REG_ADDR(win, 0x08) +#define OB_WIN_REMAP_MS(win)			OB_WIN_REG_ADDR(win, 0x0c) +#define OB_WIN_MASK_LS(win)			OB_WIN_REG_ADDR(win, 0x10) +#define OB_WIN_MASK_MS(win)			OB_WIN_REG_ADDR(win, 0x14) +#define OB_WIN_ACTIONS(win)			OB_WIN_REG_ADDR(win, 0x18) +#define OB_WIN_DEFAULT_ACTIONS			(OB_WIN_ACTIONS(OB_WIN_COUNT-1) + 0x4) +#define     OB_WIN_FUNC_NUM_MASK		GENMASK(31, 24) +#define     OB_WIN_FUNC_NUM_SHIFT		24 +#define     OB_WIN_FUNC_NUM_ENABLE		BIT(23) +#define     OB_WIN_BUS_NUM_BITS_MASK		GENMASK(22, 20) +#define     OB_WIN_BUS_NUM_BITS_SHIFT		20 +#define     OB_WIN_MSG_CODE_ENABLE		BIT(22) +#define     OB_WIN_MSG_CODE_MASK		GENMASK(21, 14) +#define     OB_WIN_MSG_CODE_SHIFT		14 +#define     OB_WIN_MSG_PAYLOAD_LEN		BIT(12) +#define     OB_WIN_ATTR_ENABLE			BIT(11) +#define     OB_WIN_ATTR_TC_MASK			GENMASK(10, 8) +#define     OB_WIN_ATTR_TC_SHIFT		8 +#define     OB_WIN_ATTR_RELAXED			BIT(7) +#define     OB_WIN_ATTR_NOSNOOP			BIT(6) +#define     OB_WIN_ATTR_POISON			BIT(5) +#define     OB_WIN_ATTR_IDO			BIT(4) +#define     OB_WIN_TYPE_MASK			GENMASK(3, 0) +#define     OB_WIN_TYPE_SHIFT			0 +#define     OB_WIN_TYPE_MEM			0x0 +#define     OB_WIN_TYPE_IO			0x4 +#define     OB_WIN_TYPE_CONFIG_TYPE0		0x8 +#define     OB_WIN_TYPE_CONFIG_TYPE1		0x9 +#define     OB_WIN_TYPE_MSG			0xc +  /* LMI registers base address and register offsets */  #define LMI_BASE_ADDR				0x6000  #define CFG_REG					(LMI_BASE_ADDR + 0x0) @@ -522,6 +562,86 @@ static int pcie_advk_wait_for_link(struct pcie_advk *pcie)  	return -ETIMEDOUT;  } +/* + * Set PCIe address window register which could be used for memory + * mapping. + */ +static void pcie_advk_set_ob_win(struct pcie_advk *pcie, u8 win_num, +				 phys_addr_t match, phys_addr_t remap, +				 phys_addr_t mask, u32 actions) +{ +	advk_writel(pcie, OB_WIN_ENABLE | +			  lower_32_bits(match), OB_WIN_MATCH_LS(win_num)); +	advk_writel(pcie, upper_32_bits(match), OB_WIN_MATCH_MS(win_num)); +	advk_writel(pcie, lower_32_bits(remap), OB_WIN_REMAP_LS(win_num)); +	advk_writel(pcie, upper_32_bits(remap), OB_WIN_REMAP_MS(win_num)); +	advk_writel(pcie, lower_32_bits(mask), OB_WIN_MASK_LS(win_num)); +	advk_writel(pcie, upper_32_bits(mask), OB_WIN_MASK_MS(win_num)); +	advk_writel(pcie, actions, OB_WIN_ACTIONS(win_num)); +} + +static void pcie_advk_disable_ob_win(struct pcie_advk *pcie, u8 win_num) +{ +	advk_writel(pcie, 0, OB_WIN_MATCH_LS(win_num)); +	advk_writel(pcie, 0, OB_WIN_MATCH_MS(win_num)); +	advk_writel(pcie, 0, OB_WIN_REMAP_LS(win_num)); +	advk_writel(pcie, 0, OB_WIN_REMAP_MS(win_num)); +	advk_writel(pcie, 0, OB_WIN_MASK_LS(win_num)); +	advk_writel(pcie, 0, OB_WIN_MASK_MS(win_num)); +	advk_writel(pcie, 0, OB_WIN_ACTIONS(win_num)); +} + +static void pcie_advk_set_ob_region(struct pcie_advk *pcie, int *wins, +				    struct pci_region *region, u32 actions) +{ +	phys_addr_t phys_start = region->phys_start; +	pci_addr_t bus_start = region->bus_start; +	pci_size_t size = region->size; +	phys_addr_t win_mask; +	u64 win_size; + +	if (*wins == -1) +		return; + +	/* +	 * The n-th PCIe window is configured by tuple (match, remap, mask) +	 * and an access to address A uses this window it if A matches the +	 * match with given mask. +	 * So every PCIe window size must be a power of two and every start +	 * address must be aligned to window size. Minimal size is 64 KiB +	 * because lower 16 bits of mask must be zero. +	 */ +	while (*wins < OB_WIN_COUNT && size > 0) { +		/* Calculate the largest aligned window size */ +		win_size = (1ULL << (fls64(size) - 1)) | +			   (phys_start ? (1ULL << __ffs64(phys_start)) : 0); +		win_size = 1ULL << __ffs64(win_size); +		if (win_size < 0x10000) +			break; + +		dev_dbg(pcie->dev, +			"Configuring PCIe window %d: [0x%llx-0x%llx] as 0x%x\n", +			*wins, (u64)phys_start, (u64)phys_start + win_size, +			actions); +		win_mask = ~(win_size - 1) & ~0xffff; +		pcie_advk_set_ob_win(pcie, *wins, phys_start, bus_start, +				     win_mask, actions); + +		phys_start += win_size; +		bus_start += win_size; +		size -= win_size; +		(*wins)++; +	} + +	if (size > 0) { +		*wins = -1; +		dev_err(pcie->dev, +			"Invalid PCIe region [0x%llx-0x%llx]\n", +			(u64)region->phys_start, +			(u64)region->phys_start + region->size); +	} +} +  /**   * pcie_advk_setup_hw() - PCIe initailzation   * @@ -531,6 +651,8 @@ static int pcie_advk_wait_for_link(struct pcie_advk *pcie)   */  static int pcie_advk_setup_hw(struct pcie_advk *pcie)  { +	struct pci_region *io, *mem, *pref; +	int i, wins;  	u32 reg;  	/* Set to Direct mode */ @@ -597,7 +719,9 @@ static int pcie_advk_setup_hw(struct pcie_advk *pcie)  	 * configurations (Default User Field: 0xD0074CFC)  	 * are used to transparent address translation for  	 * the outbound transactions. Thus, PCIe address -	 * windows are not required. +	 * windows are not required for transparent memory +	 * access when default outbound window configuration +	 * is set for memory access.  	 */  	reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);  	reg |= PCIE_CORE_CTRL2_ADDRWIN_MAP_ENABLE; @@ -613,6 +737,34 @@ static int pcie_advk_setup_hw(struct pcie_advk *pcie)  	reg |= PIO_CTRL_ADDR_WIN_DISABLE;  	advk_writel(pcie, reg, PIO_CTRL); +	/* +	 * Set memory access in Default User Field so it +	 * is not required to configure PCIe address for +	 * transparent memory access. +	 */ +	advk_writel(pcie, OB_WIN_TYPE_MEM, OB_WIN_DEFAULT_ACTIONS); + +	/* +	 * Configure PCIe address windows for non-memory or +	 * non-transparent access as by default PCIe uses +	 * transparent memory access. +	 */ +	wins = 0; +	pci_get_regions(pcie->dev, &io, &mem, &pref); +	if (io) +		pcie_advk_set_ob_region(pcie, &wins, io, OB_WIN_TYPE_IO); +	if (mem && mem->phys_start != mem->bus_start) +		pcie_advk_set_ob_region(pcie, &wins, mem, OB_WIN_TYPE_MEM); +	if (pref && pref->phys_start != pref->bus_start) +		pcie_advk_set_ob_region(pcie, &wins, pref, OB_WIN_TYPE_MEM); + +	/* Disable remaining PCIe outbound windows */ +	for (i = ((wins >= 0) ? wins : 0); i < OB_WIN_COUNT; i++) +		pcie_advk_disable_ob_win(pcie, i); + +	if (wins == -1) +		return -EINVAL; +  	/* Wait for PCIe link up */  	if (pcie_advk_wait_for_link(pcie))  		return -ENXIO; @@ -674,6 +826,10 @@ static int pcie_advk_remove(struct udevice *dev)  {  	struct pcie_advk *pcie = dev_get_priv(dev);  	u32 reg; +	int i; + +	for (i = 0; i < OB_WIN_COUNT; i++) +		pcie_advk_disable_ob_win(pcie, i);  	reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG);  	reg &= ~(PCIE_CORE_CMD_MEM_ACCESS_EN | | 
