diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/pci-aardvark.c | 169 |
1 files changed, 163 insertions, 6 deletions
diff --git a/drivers/pci/pci-aardvark.c b/drivers/pci/pci-aardvark.c index c43d4f309b1..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,10 +737,33 @@ static int pcie_advk_setup_hw(struct pcie_advk *pcie) reg |= PIO_CTRL_ADDR_WIN_DISABLE; advk_writel(pcie, reg, PIO_CTRL); - /* Start link training */ - reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); - reg |= PCIE_CORE_LINK_TRAINING; - advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); + /* + * 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)) @@ -679,6 +826,16 @@ 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 | + PCIE_CORE_CMD_IO_ACCESS_EN | + PCIE_CORE_CMD_MEM_IO_REQ_EN); + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); reg &= ~LINK_TRAINING_EN; @@ -716,7 +873,7 @@ static const struct dm_pci_ops pcie_advk_ops = { }; static const struct udevice_id pcie_advk_ids[] = { - { .compatible = "marvell,armada-37xx-pcie" }, + { .compatible = "marvell,armada-3700-pcie" }, { } }; |