--- zzzz-none-000/linux-3.10.107/drivers/bus/mvebu-mbus.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/bus/mvebu-mbus.c 2021-02-04 17:41:59.000000000 +0000 @@ -35,13 +35,9 @@ * * - Provides an API for platform code or device drivers to * dynamically add or remove address decoding windows for the CPU -> - * device accesses. This API is mvebu_mbus_add_window(), - * mvebu_mbus_add_window_remap_flags() and - * mvebu_mbus_del_window(). Since the (target, attribute) values - * differ from one SoC family to another, the API uses a 'const char - * *' string to identify devices, and this driver is responsible for - * knowing the mapping between the name of a device and its - * corresponding (target, attribute) in the current SoC family. + * device accesses. This API is mvebu_mbus_add_window_by_id(), + * mvebu_mbus_add_window_remap_by_id() and + * mvebu_mbus_del_window(). * * - Provides a debugfs interface in /sys/kernel/debug/mvebu-mbus/ to * see the list of CPU -> SDRAM windows and their configuration @@ -49,6 +45,8 @@ * configuration (file 'devices'). */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -58,6 +56,9 @@ #include #include #include +#include +#include +#include /* * DDR target is the same on all platforms. @@ -69,6 +70,8 @@ */ #define WIN_CTRL_OFF 0x0000 #define WIN_CTRL_ENABLE BIT(0) +/* Only on HW I/O coherency capable platforms */ +#define WIN_CTRL_SYNCBARRIER BIT(1) #define WIN_CTRL_TGT_MASK 0xf0 #define WIN_CTRL_TGT_SHIFT 4 #define WIN_CTRL_ATTR_MASK 0xff00 @@ -82,6 +85,9 @@ #define WIN_REMAP_LOW 0xffff0000 #define WIN_REMAP_HI_OFF 0x000c +#define UNIT_SYNC_BARRIER_OFF 0x84 +#define UNIT_SYNC_BARRIER_ALL 0xFFFF + #define ATTR_HW_COHERENCY (0x1 << 4) #define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3)) @@ -95,64 +101,98 @@ #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) -struct mvebu_mbus_mapping { - const char *name; - u8 target; - u8 attr; - u8 attrmask; -}; +/* Relative to mbusbridge_base */ +#define MBUS_BRIDGE_CTRL_OFF 0x0 +#define MBUS_BRIDGE_BASE_OFF 0x4 -/* - * Masks used for the 'attrmask' field of mvebu_mbus_mapping. They - * allow to get the real attribute value, discarding the special bits - * used to select a PCI MEM region or a PCI WA region. This allows the - * debugfs code to reverse-match the name of a device from its - * target/attr values. - * - * For all devices except PCI, all bits of 'attr' must be - * considered. For most SoCs, only bit 3 should be ignored (it allows - * to select between PCI MEM and PCI I/O). On Orion5x however, there - * is the special bit 5 to select a PCI WA region. - */ -#define MAPDEF_NOMASK 0xff -#define MAPDEF_PCIMASK 0xf7 -#define MAPDEF_ORIONPCIMASK 0xd7 - -/* Macro used to define one mvebu_mbus_mapping entry */ -#define MAPDEF(__n, __t, __a, __m) \ - { .name = __n, .target = __t, .attr = __a, .attrmask = __m } +/* Maximum number of windows, for all known platforms */ +#define MBUS_WINS_MAX 20 struct mvebu_mbus_state; struct mvebu_mbus_soc_data { unsigned int num_wins; - unsigned int num_remappable_wins; + bool has_mbus_bridge; unsigned int (*win_cfg_offset)(const int win); + unsigned int (*win_remap_offset)(const int win); void (*setup_cpu_target)(struct mvebu_mbus_state *s); + int (*save_cpu_target)(struct mvebu_mbus_state *s, + u32 *store_addr); int (*show_cpu_target)(struct mvebu_mbus_state *s, struct seq_file *seq, void *v); - const struct mvebu_mbus_mapping *map; +}; + +/* + * Used to store the state of one MBus window accross suspend/resume. + */ +struct mvebu_mbus_win_data { + u32 ctrl; + u32 base; + u32 remap_lo; + u32 remap_hi; }; struct mvebu_mbus_state { void __iomem *mbuswins_base; void __iomem *sdramwins_base; + void __iomem *mbusbridge_base; + phys_addr_t sdramwins_phys_base; struct dentry *debugfs_root; struct dentry *debugfs_sdram; struct dentry *debugfs_devs; + struct resource pcie_mem_aperture; + struct resource pcie_io_aperture; const struct mvebu_mbus_soc_data *soc; int hw_io_coherency; + + /* Used during suspend/resume */ + u32 mbus_bridge_ctrl; + u32 mbus_bridge_base; + struct mvebu_mbus_win_data wins[MBUS_WINS_MAX]; }; static struct mvebu_mbus_state mbus_state; +/* + * We provide two variants of the mv_mbus_dram_info() function: + * + * - The normal one, where the described DRAM ranges may overlap with + * the I/O windows, but for which the DRAM ranges are guaranteed to + * have a power of two size. Such ranges are suitable for the DMA + * masters that only DMA between the RAM and the device, which is + * actually all devices except the crypto engines. + * + * - The 'nooverlap' one, where the described DRAM ranges are + * guaranteed to not overlap with the I/O windows, but for which the + * DRAM ranges will not have power of two sizes. They will only be + * aligned on a 64 KB boundary, and have a size multiple of 64 + * KB. Such ranges are suitable for the DMA masters that DMA between + * the crypto SRAM (which is mapped through an I/O window) and a + * device. This is the case for the crypto engines. + */ + static struct mbus_dram_target_info mvebu_mbus_dram_info; +static struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap; + const struct mbus_dram_target_info *mv_mbus_dram_info(void) { return &mvebu_mbus_dram_info; } EXPORT_SYMBOL_GPL(mv_mbus_dram_info); +const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void) +{ + return &mvebu_mbus_dram_info_nooverlap; +} +EXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap); + +/* Checks whether the given window has remap capability */ +static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus, + const int win) +{ + return mbus->soc->win_remap_offset(win) != MVEBU_MBUS_NO_REMAP; +} + /* * Functions to manipulate the address decoding windows */ @@ -184,9 +224,12 @@ *attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT; if (remap) { - if (win < mbus->soc->num_remappable_wins) { - u32 remap_low = readl(addr + WIN_REMAP_LO_OFF); - u32 remap_hi = readl(addr + WIN_REMAP_HI_OFF); + if (mvebu_mbus_window_is_remappable(mbus, win)) { + u32 remap_low, remap_hi; + void __iomem *addr_rmp = mbus->mbuswins_base + + mbus->soc->win_remap_offset(win); + remap_low = readl(addr_rmp + WIN_REMAP_LO_OFF); + remap_hi = readl(addr_rmp + WIN_REMAP_HI_OFF); *remap = ((u64)remap_hi << 32) | remap_low; } else *remap = 0; @@ -199,10 +242,11 @@ void __iomem *addr; addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win); - writel(0, addr + WIN_BASE_OFF); writel(0, addr + WIN_CTRL_OFF); - if (win < mbus->soc->num_remappable_wins) { + + if (mvebu_mbus_window_is_remappable(mbus, win)) { + addr = mbus->mbuswins_base + mbus->soc->win_remap_offset(win); writel(0, addr + WIN_REMAP_LO_OFF); writel(0, addr + WIN_REMAP_HI_OFF); } @@ -210,14 +254,6 @@ /* Checks whether the given window number is available */ -/* On Armada XP, 375 and 38x the MBus window 13 has the remap - * capability, like windows 0 to 7. However, the mvebu-mbus driver - * isn't currently taking into account this special case, which means - * that when window 13 is actually used, the remap registers are left - * to 0, making the device using this MBus window unavailable. The - * quick fix for stable is to not use window 13. A follow up patch - * will correctly handle this window. -*/ static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus, const int win) { @@ -225,9 +261,6 @@ mbus->soc->win_cfg_offset(win); u32 ctrl = readl(addr + WIN_CTRL_OFF); - if (win == 13) - return false; - return !(ctrl & WIN_CTRL_ENABLE); } @@ -301,20 +334,37 @@ mbus->soc->win_cfg_offset(win); u32 ctrl, remap_addr; + if (!is_power_of_2(size)) { + WARN(true, "Invalid MBus window size: 0x%zx\n", size); + return -EINVAL; + } + + if ((base & (phys_addr_t)(size - 1)) != 0) { + WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base, + size); + return -EINVAL; + } + ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | (attr << WIN_CTRL_ATTR_SHIFT) | (target << WIN_CTRL_TGT_SHIFT) | WIN_CTRL_ENABLE; + if (mbus->hw_io_coherency) + ctrl |= WIN_CTRL_SYNCBARRIER; writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF); writel(ctrl, addr + WIN_CTRL_OFF); - if (win < mbus->soc->num_remappable_wins) { + + if (mvebu_mbus_window_is_remappable(mbus, win)) { + void __iomem *addr_rmp = mbus->mbuswins_base + + mbus->soc->win_remap_offset(win); + if (remap == MVEBU_MBUS_NO_REMAP) remap_addr = base; else remap_addr = remap; - writel(remap_addr & WIN_REMAP_LOW, addr + WIN_REMAP_LO_OFF); - writel(0, addr + WIN_REMAP_HI_OFF); + writel(remap_addr & WIN_REMAP_LOW, addr_rmp + WIN_REMAP_LO_OFF); + writel(0, addr_rmp + WIN_REMAP_HI_OFF); } return 0; @@ -328,19 +378,27 @@ int win; if (remap == MVEBU_MBUS_NO_REMAP) { - for (win = mbus->soc->num_remappable_wins; - win < mbus->soc->num_wins; win++) + for (win = 0; win < mbus->soc->num_wins; win++) { + if (mvebu_mbus_window_is_remappable(mbus, win)) + continue; + if (mvebu_mbus_window_is_free(mbus, win)) return mvebu_mbus_setup_window(mbus, win, base, size, remap, target, attr); + } } + for (win = 0; win < mbus->soc->num_wins; win++) { + /* Skip window if need remap but is not supported */ + if ((remap != MVEBU_MBUS_NO_REMAP) && + !mvebu_mbus_window_is_remappable(mbus, win)) + continue; - for (win = 0; win < mbus->soc->num_wins; win++) if (mvebu_mbus_window_is_free(mbus, win)) return mvebu_mbus_setup_window(mbus, win, base, size, remap, target, attr); + } return -ENOMEM; } @@ -433,8 +491,7 @@ u64 wbase, wremap; u32 wsize; u8 wtarget, wattr; - int enabled, i; - const char *name; + int enabled; mvebu_mbus_read_window(mbus, win, &enabled, &wbase, &wsize, @@ -445,20 +502,15 @@ continue; } - - for (i = 0; mbus->soc->map[i].name; i++) - if (mbus->soc->map[i].target == wtarget && - mbus->soc->map[i].attr == - (wattr & mbus->soc->map[i].attrmask)) - break; - - name = mbus->soc->map[i].name ?: "unknown"; - - seq_printf(seq, "[%02d] %016llx - %016llx : %s", + seq_printf(seq, "[%02d] %016llx - %016llx : %04x:%04x", win, (unsigned long long)wbase, - (unsigned long long)(wbase + wsize), name); + (unsigned long long)(wbase + wsize), wtarget, wattr); - if (win < mbus->soc->num_remappable_wins) { + if (!is_power_of_2(wsize) || + ((wbase & (u64)(wsize - 1)) != 0)) + seq_puts(seq, " (Invalid base/size!!)"); + + if (mvebu_mbus_window_is_remappable(mbus, win)) { seq_printf(seq, " (remap %016llx)\n", (unsigned long long)wremap); } else @@ -484,12 +536,12 @@ * SoC-specific functions and definitions */ -static unsigned int orion_mbus_win_offset(int win) +static unsigned int generic_mbus_win_cfg_offset(int win) { return win << 4; } -static unsigned int armada_370_xp_mbus_win_offset(int win) +static unsigned int armada_370_xp_mbus_win_cfg_offset(int win) { /* The register layout is a bit annoying and the below code * tries to cope with it. @@ -509,7 +561,7 @@ return 0x90 + ((win - 8) << 3); } -static unsigned int mv78xx0_mbus_win_offset(int win) +static unsigned int mv78xx0_mbus_win_cfg_offset(int win) { if (win < 8) return win << 4; @@ -517,6 +569,129 @@ return 0x900 + ((win - 8) << 4); } +static unsigned int generic_mbus_win_remap_2_offset(int win) +{ + if (win < 2) + return generic_mbus_win_cfg_offset(win); + else + return MVEBU_MBUS_NO_REMAP; +} + +static unsigned int generic_mbus_win_remap_4_offset(int win) +{ + if (win < 4) + return generic_mbus_win_cfg_offset(win); + else + return MVEBU_MBUS_NO_REMAP; +} + +static unsigned int generic_mbus_win_remap_8_offset(int win) +{ + if (win < 8) + return generic_mbus_win_cfg_offset(win); + else + return MVEBU_MBUS_NO_REMAP; +} + +static unsigned int armada_xp_mbus_win_remap_offset(int win) +{ + if (win < 8) + return generic_mbus_win_cfg_offset(win); + else if (win == 13) + return 0xF0 - WIN_REMAP_LO_OFF; + else + return MVEBU_MBUS_NO_REMAP; +} + +/* + * Use the memblock information to find the MBus bridge hole in the + * physical address space. + */ +static void __init +mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end) +{ + struct memblock_region *r; + uint64_t s = 0; + + for_each_memblock(memory, r) { + /* + * This part of the memory is above 4 GB, so we don't + * care for the MBus bridge hole. + */ + if (r->base >= 0x100000000ULL) + continue; + + /* + * The MBus bridge hole is at the end of the RAM under + * the 4 GB limit. + */ + if (r->base + r->size > s) + s = r->base + r->size; + } + + *start = s; + *end = 0x100000000ULL; +} + +/* + * This function fills in the mvebu_mbus_dram_info_nooverlap data + * structure, by looking at the mvebu_mbus_dram_info data, and + * removing the parts of it that overlap with I/O windows. + */ +static void __init +mvebu_mbus_setup_cpu_target_nooverlap(struct mvebu_mbus_state *mbus) +{ + uint64_t mbus_bridge_base, mbus_bridge_end; + int cs_nooverlap = 0; + int i; + + mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end); + + for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) { + struct mbus_dram_window *w; + u64 base, size, end; + + w = &mvebu_mbus_dram_info.cs[i]; + base = w->base; + size = w->size; + end = base + size; + + /* + * The CS is fully enclosed inside the MBus bridge + * area, so ignore it. + */ + if (base >= mbus_bridge_base && end <= mbus_bridge_end) + continue; + + /* + * Beginning of CS overlaps with end of MBus, raise CS + * base address, and shrink its size. + */ + if (base >= mbus_bridge_base && end > mbus_bridge_end) { + size -= mbus_bridge_end - base; + base = mbus_bridge_end; + } + + /* + * End of CS overlaps with beginning of MBus, shrink + * CS size. + */ + if (base < mbus_bridge_base && end > mbus_bridge_base) + size -= end - mbus_bridge_base; + + w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++]; + w->cs_index = i; + w->mbus_attr = 0xf & ~(1 << i); + if (mbus->hw_io_coherency) + w->mbus_attr |= ATTR_HW_COHERENCY; + w->base = base; + w->size = size; + } + + mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR; + mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap; +} + static void __init mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) { @@ -551,6 +726,28 @@ mvebu_mbus_dram_info.num_cs = cs; } +static int +mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus, + u32 *store_addr) +{ + int i; + + for (i = 0; i < 4; i++) { + u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); + u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); + + writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i), + store_addr++); + writel(base, store_addr++); + writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i), + store_addr++); + writel(size, store_addr++); + } + + /* We've written 16 words to the store address */ + return 16; +} + static void __init mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) { @@ -581,100 +778,65 @@ mvebu_mbus_dram_info.num_cs = cs; } -static const struct mvebu_mbus_mapping armada_370_map[] = { - MAPDEF("bootrom", 1, 0xe0, MAPDEF_NOMASK), - MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK), - MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK), - MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK), - MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK), - MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK), - MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK), - MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK), - {}, -}; +static int +mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus, + u32 *store_addr) +{ + int i; + + for (i = 0; i < 2; i++) { + u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); + + writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i), + store_addr++); + writel(map, store_addr++); + } + + /* We've written 4 words to the store address */ + return 4; +} + +int mvebu_mbus_save_cpu_target(u32 *store_addr) +{ + return mbus_state.soc->save_cpu_target(&mbus_state, store_addr); +} static const struct mvebu_mbus_soc_data armada_370_mbus_data = { .num_wins = 20, - .num_remappable_wins = 8, - .win_cfg_offset = armada_370_xp_mbus_win_offset, + .has_mbus_bridge = true, + .win_cfg_offset = armada_370_xp_mbus_win_cfg_offset, + .win_remap_offset = generic_mbus_win_remap_8_offset, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, - .map = armada_370_map, -}; - -static const struct mvebu_mbus_mapping armada_xp_map[] = { - MAPDEF("bootrom", 1, 0x1d, MAPDEF_NOMASK), - MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK), - MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK), - MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK), - MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK), - MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK), - MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK), - MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK), - MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK), - MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK), - MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK), - MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK), - MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK), - MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK), - MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK), - MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK), - {}, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, }; static const struct mvebu_mbus_soc_data armada_xp_mbus_data = { .num_wins = 20, - .num_remappable_wins = 8, - .win_cfg_offset = armada_370_xp_mbus_win_offset, + .has_mbus_bridge = true, + .win_cfg_offset = armada_370_xp_mbus_win_cfg_offset, + .win_remap_offset = armada_xp_mbus_win_remap_offset, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, - .map = armada_xp_map, -}; - -static const struct mvebu_mbus_mapping kirkwood_map[] = { - MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK), - MAPDEF("pcie1.0", 4, 0xd0, MAPDEF_PCIMASK), - MAPDEF("sram", 3, 0x01, MAPDEF_NOMASK), - MAPDEF("nand", 1, 0x2f, MAPDEF_NOMASK), - {}, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, }; static const struct mvebu_mbus_soc_data kirkwood_mbus_data = { .num_wins = 8, - .num_remappable_wins = 4, - .win_cfg_offset = orion_mbus_win_offset, + .win_cfg_offset = generic_mbus_win_cfg_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, + .win_remap_offset = generic_mbus_win_remap_4_offset, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, - .map = kirkwood_map, -}; - -static const struct mvebu_mbus_mapping dove_map[] = { - MAPDEF("pcie0.0", 0x4, 0xe0, MAPDEF_PCIMASK), - MAPDEF("pcie1.0", 0x8, 0xe0, MAPDEF_PCIMASK), - MAPDEF("cesa", 0x3, 0x01, MAPDEF_NOMASK), - MAPDEF("bootrom", 0x1, 0xfd, MAPDEF_NOMASK), - MAPDEF("scratchpad", 0xd, 0x0, MAPDEF_NOMASK), - {}, }; static const struct mvebu_mbus_soc_data dove_mbus_data = { .num_wins = 8, - .num_remappable_wins = 4, - .win_cfg_offset = orion_mbus_win_offset, + .win_cfg_offset = generic_mbus_win_cfg_offset, + .save_cpu_target = mvebu_mbus_dove_save_cpu_target, + .win_remap_offset = generic_mbus_win_remap_4_offset, .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_dove, - .map = dove_map, -}; - -static const struct mvebu_mbus_mapping orion5x_map[] = { - MAPDEF("pcie0.0", 4, 0x51, MAPDEF_ORIONPCIMASK), - MAPDEF("pci0.0", 3, 0x51, MAPDEF_ORIONPCIMASK), - MAPDEF("devbus-boot", 1, 0x0f, MAPDEF_NOMASK), - MAPDEF("devbus-cs0", 1, 0x1e, MAPDEF_NOMASK), - MAPDEF("devbus-cs1", 1, 0x1d, MAPDEF_NOMASK), - MAPDEF("devbus-cs2", 1, 0x1b, MAPDEF_NOMASK), - MAPDEF("sram", 0, 0x00, MAPDEF_NOMASK), - {}, }; /* @@ -683,54 +845,38 @@ */ static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = { .num_wins = 8, - .num_remappable_wins = 4, - .win_cfg_offset = orion_mbus_win_offset, + .win_cfg_offset = generic_mbus_win_cfg_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, + .win_remap_offset = generic_mbus_win_remap_4_offset, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, - .map = orion5x_map, }; static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = { .num_wins = 8, - .num_remappable_wins = 2, - .win_cfg_offset = orion_mbus_win_offset, + .win_cfg_offset = generic_mbus_win_cfg_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, + .win_remap_offset = generic_mbus_win_remap_2_offset, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, - .map = orion5x_map, -}; - -static const struct mvebu_mbus_mapping mv78xx0_map[] = { - MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK), - MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK), - MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK), - MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK), - MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK), - MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK), - MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK), - MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK), - MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK), - MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK), - {}, }; static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = { .num_wins = 14, - .num_remappable_wins = 8, - .win_cfg_offset = mv78xx0_mbus_win_offset, + .win_cfg_offset = mv78xx0_mbus_win_cfg_offset, + .save_cpu_target = mvebu_mbus_default_save_cpu_target, + .win_remap_offset = generic_mbus_win_remap_8_offset, .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, .show_cpu_target = mvebu_sdram_debug_show_orion, - .map = mv78xx0_map, }; -/* - * The driver doesn't yet have a DT binding because the details of - * this DT binding still need to be sorted out. However, as a - * preparation, we already use of_device_id to match a SoC description - * string against the SoC specific details of this driver. - */ static const struct of_device_id of_mvebu_mbus_ids[] = { { .compatible = "marvell,armada370-mbus", .data = &armada_370_mbus_data, }, + { .compatible = "marvell,armada375-mbus", + .data = &armada_xp_mbus_data, }, + { .compatible = "marvell,armada380-mbus", + .data = &armada_xp_mbus_data, }, { .compatible = "marvell,armadaxp-mbus", .data = &armada_xp_mbus_data, }, { .compatible = "marvell,kirkwood-mbus", @@ -753,48 +899,27 @@ /* * Public API of the driver */ -int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base, - size_t size, phys_addr_t remap, - unsigned int flags) +int mvebu_mbus_add_window_remap_by_id(unsigned int target, + unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap) { struct mvebu_mbus_state *s = &mbus_state; - u8 target, attr; - int i; - if (!s->soc->map) - return -ENODEV; - - for (i = 0; s->soc->map[i].name; i++) - if (!strcmp(s->soc->map[i].name, devname)) - break; - - if (!s->soc->map[i].name) { - pr_err("mvebu-mbus: unknown device '%s'\n", devname); - return -ENODEV; - } - - target = s->soc->map[i].target; - attr = s->soc->map[i].attr; - - if (flags == MVEBU_MBUS_PCI_MEM) - attr |= 0x8; - else if (flags == MVEBU_MBUS_PCI_WA) - attr |= 0x28; - - if (!mvebu_mbus_window_conflicts(s, base, size, target, attr)) { - pr_err("mvebu-mbus: cannot add window '%s', conflicts with another window\n", - devname); + if (!mvebu_mbus_window_conflicts(s, base, size, target, attribute)) { + pr_err("cannot add window '%x:%x', conflicts with another window\n", + target, attribute); return -EINVAL; } - return mvebu_mbus_alloc_window(s, base, size, remap, target, attr); - + return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute); } -int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size) +int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size) { - return mvebu_mbus_add_window_remap_flags(devname, base, size, - MVEBU_MBUS_NO_REMAP, 0); + return mvebu_mbus_add_window_remap_by_id(target, attribute, base, + size, MVEBU_MBUS_NO_REMAP); } int mvebu_mbus_del_window(phys_addr_t base, size_t size) @@ -809,6 +934,20 @@ return 0; } +void mvebu_mbus_get_pcie_mem_aperture(struct resource *res) +{ + if (!res) + return; + *res = mbus_state.pcie_mem_aperture; +} + +void mvebu_mbus_get_pcie_io_aperture(struct resource *res) +{ + if (!res) + return; + *res = mbus_state.pcie_io_aperture; +} + static __init int mvebu_mbus_debugfs_init(void) { struct mvebu_mbus_state *s = &mbus_state; @@ -835,25 +974,84 @@ } fs_initcall(mvebu_mbus_debugfs_init); -int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, - size_t mbuswins_size, - phys_addr_t sdramwins_phys_base, - size_t sdramwins_size, int is_coherent) +static int mvebu_mbus_suspend(void) { - struct mvebu_mbus_state *mbus = &mbus_state; - const struct of_device_id *of_id; + struct mvebu_mbus_state *s = &mbus_state; int win; - for (of_id = of_mvebu_mbus_ids; of_id->compatible; of_id++) - if (!strcmp(of_id->compatible, soc)) - break; - - if (!of_id->compatible) { - pr_err("mvebu-mbus: could not find a matching SoC family\n"); + if (!s->mbusbridge_base) return -ENODEV; + + for (win = 0; win < s->soc->num_wins; win++) { + void __iomem *addr = s->mbuswins_base + + s->soc->win_cfg_offset(win); + void __iomem *addr_rmp; + + s->wins[win].base = readl(addr + WIN_BASE_OFF); + s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF); + + if (!mvebu_mbus_window_is_remappable(s, win)) + continue; + + addr_rmp = s->mbuswins_base + + s->soc->win_remap_offset(win); + + s->wins[win].remap_lo = readl(addr_rmp + WIN_REMAP_LO_OFF); + s->wins[win].remap_hi = readl(addr_rmp + WIN_REMAP_HI_OFF); + } + + s->mbus_bridge_ctrl = readl(s->mbusbridge_base + + MBUS_BRIDGE_CTRL_OFF); + s->mbus_bridge_base = readl(s->mbusbridge_base + + MBUS_BRIDGE_BASE_OFF); + + return 0; +} + +static void mvebu_mbus_resume(void) +{ + struct mvebu_mbus_state *s = &mbus_state; + int win; + + writel(s->mbus_bridge_ctrl, + s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF); + writel(s->mbus_bridge_base, + s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF); + + for (win = 0; win < s->soc->num_wins; win++) { + void __iomem *addr = s->mbuswins_base + + s->soc->win_cfg_offset(win); + void __iomem *addr_rmp; + + writel(s->wins[win].base, addr + WIN_BASE_OFF); + writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF); + + if (!mvebu_mbus_window_is_remappable(s, win)) + continue; + + addr_rmp = s->mbuswins_base + + s->soc->win_remap_offset(win); + + writel(s->wins[win].remap_lo, addr_rmp + WIN_REMAP_LO_OFF); + writel(s->wins[win].remap_hi, addr_rmp + WIN_REMAP_HI_OFF); } +} - mbus->soc = of_id->data; +struct syscore_ops mvebu_mbus_syscore_ops = { + .suspend = mvebu_mbus_suspend, + .resume = mvebu_mbus_resume, +}; + +static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, + phys_addr_t mbuswins_phys_base, + size_t mbuswins_size, + phys_addr_t sdramwins_phys_base, + size_t sdramwins_size, + phys_addr_t mbusbridge_phys_base, + size_t mbusbridge_size, + bool is_coherent) +{ + int win; mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size); if (!mbus->mbuswins_base) @@ -865,12 +1063,264 @@ return -ENOMEM; } - mbus->hw_io_coherency = is_coherent; + mbus->sdramwins_phys_base = sdramwins_phys_base; + + if (mbusbridge_phys_base) { + mbus->mbusbridge_base = ioremap(mbusbridge_phys_base, + mbusbridge_size); + if (!mbus->mbusbridge_base) { + iounmap(mbus->sdramwins_base); + iounmap(mbus->mbuswins_base); + return -ENOMEM; + } + } else + mbus->mbusbridge_base = NULL; for (win = 0; win < mbus->soc->num_wins; win++) mvebu_mbus_disable_window(mbus, win); mbus->soc->setup_cpu_target(mbus); + mvebu_mbus_setup_cpu_target_nooverlap(mbus); + + if (is_coherent) + writel(UNIT_SYNC_BARRIER_ALL, + mbus->mbuswins_base + UNIT_SYNC_BARRIER_OFF); + + register_syscore_ops(&mvebu_mbus_syscore_ops); + + return 0; +} + +int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, + size_t mbuswins_size, + phys_addr_t sdramwins_phys_base, + size_t sdramwins_size) +{ + const struct of_device_id *of_id; + + for (of_id = of_mvebu_mbus_ids; of_id->compatible[0]; of_id++) + if (!strcmp(of_id->compatible, soc)) + break; + + if (!of_id->compatible[0]) { + pr_err("could not find a matching SoC family\n"); + return -ENODEV; + } + + mbus_state.soc = of_id->data; + return mvebu_mbus_common_init(&mbus_state, + mbuswins_phys_base, + mbuswins_size, + sdramwins_phys_base, + sdramwins_size, 0, 0, false); +} + +#ifdef CONFIG_OF +/* + * The window IDs in the ranges DT property have the following format: + * - bits 28 to 31: MBus custom field + * - bits 24 to 27: window target ID + * - bits 16 to 23: window attribute ID + * - bits 0 to 15: unused + */ +#define CUSTOM(id) (((id) & 0xF0000000) >> 24) +#define TARGET(id) (((id) & 0x0F000000) >> 24) +#define ATTR(id) (((id) & 0x00FF0000) >> 16) + +static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus, + u32 base, u32 size, + u8 target, u8 attr) +{ + if (!mvebu_mbus_window_conflicts(mbus, base, size, target, attr)) { + pr_err("cannot add window '%04x:%04x', conflicts with another window\n", + target, attr); + return -EBUSY; + } + + if (mvebu_mbus_alloc_window(mbus, base, size, MVEBU_MBUS_NO_REMAP, + target, attr)) { + pr_err("cannot add window '%04x:%04x', too many windows\n", + target, attr); + return -ENOMEM; + } return 0; } + +static int __init +mbus_parse_ranges(struct device_node *node, + int *addr_cells, int *c_addr_cells, int *c_size_cells, + int *cell_count, const __be32 **ranges_start, + const __be32 **ranges_end) +{ + const __be32 *prop; + int ranges_len, tuple_len; + + /* Allow a node with no 'ranges' property */ + *ranges_start = of_get_property(node, "ranges", &ranges_len); + if (*ranges_start == NULL) { + *addr_cells = *c_addr_cells = *c_size_cells = *cell_count = 0; + *ranges_start = *ranges_end = NULL; + return 0; + } + *ranges_end = *ranges_start + ranges_len / sizeof(__be32); + + *addr_cells = of_n_addr_cells(node); + + prop = of_get_property(node, "#address-cells", NULL); + *c_addr_cells = be32_to_cpup(prop); + + prop = of_get_property(node, "#size-cells", NULL); + *c_size_cells = be32_to_cpup(prop); + + *cell_count = *addr_cells + *c_addr_cells + *c_size_cells; + tuple_len = (*cell_count) * sizeof(__be32); + + if (ranges_len % tuple_len) { + pr_warn("malformed ranges entry '%s'\n", node->name); + return -EINVAL; + } + return 0; +} + +static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus, + struct device_node *np) +{ + int addr_cells, c_addr_cells, c_size_cells; + int i, ret, cell_count; + const __be32 *r, *ranges_start, *ranges_end; + + ret = mbus_parse_ranges(np, &addr_cells, &c_addr_cells, + &c_size_cells, &cell_count, + &ranges_start, &ranges_end); + if (ret < 0) + return ret; + + for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) { + u32 windowid, base, size; + u8 target, attr; + + /* + * An entry with a non-zero custom field do not + * correspond to a static window, so skip it. + */ + windowid = of_read_number(r, 1); + if (CUSTOM(windowid)) + continue; + + target = TARGET(windowid); + attr = ATTR(windowid); + + base = of_read_number(r + c_addr_cells, addr_cells); + size = of_read_number(r + c_addr_cells + addr_cells, + c_size_cells); + ret = mbus_dt_setup_win(mbus, base, size, target, attr); + if (ret < 0) + return ret; + } + return 0; +} + +static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, + struct resource *mem, + struct resource *io) +{ + u32 reg[2]; + int ret; + + /* + * These are optional, so we make sure that resource_size(x) will + * return 0. + */ + memset(mem, 0, sizeof(struct resource)); + mem->end = -1; + memset(io, 0, sizeof(struct resource)); + io->end = -1; + + ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg)); + if (!ret) { + mem->start = reg[0]; + mem->end = mem->start + reg[1] - 1; + mem->flags = IORESOURCE_MEM; + } + + ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg)); + if (!ret) { + io->start = reg[0]; + io->end = io->start + reg[1] - 1; + io->flags = IORESOURCE_IO; + } +} + +int __init mvebu_mbus_dt_init(bool is_coherent) +{ + struct resource mbuswins_res, sdramwins_res, mbusbridge_res; + struct device_node *np, *controller; + const struct of_device_id *of_id; + const __be32 *prop; + int ret; + + np = of_find_matching_node_and_match(NULL, of_mvebu_mbus_ids, &of_id); + if (!np) { + pr_err("could not find a matching SoC family\n"); + return -ENODEV; + } + + mbus_state.soc = of_id->data; + + prop = of_get_property(np, "controller", NULL); + if (!prop) { + pr_err("required 'controller' property missing\n"); + return -EINVAL; + } + + controller = of_find_node_by_phandle(be32_to_cpup(prop)); + if (!controller) { + pr_err("could not find an 'mbus-controller' node\n"); + return -ENODEV; + } + + if (of_address_to_resource(controller, 0, &mbuswins_res)) { + pr_err("cannot get MBUS register address\n"); + return -EINVAL; + } + + if (of_address_to_resource(controller, 1, &sdramwins_res)) { + pr_err("cannot get SDRAM register address\n"); + return -EINVAL; + } + + /* + * Set the resource to 0 so that it can be left unmapped by + * mvebu_mbus_common_init() if the DT doesn't carry the + * necessary information. This is needed to preserve backward + * compatibility. + */ + memset(&mbusbridge_res, 0, sizeof(mbusbridge_res)); + + if (mbus_state.soc->has_mbus_bridge) { + if (of_address_to_resource(controller, 2, &mbusbridge_res)) + pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n"); + } + + mbus_state.hw_io_coherency = is_coherent; + + /* Get optional pcie-{mem,io}-aperture properties */ + mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, + &mbus_state.pcie_io_aperture); + + ret = mvebu_mbus_common_init(&mbus_state, + mbuswins_res.start, + resource_size(&mbuswins_res), + sdramwins_res.start, + resource_size(&sdramwins_res), + mbusbridge_res.start, + resource_size(&mbusbridge_res), + is_coherent); + if (ret) + return ret; + + /* Setup statically declared windows in the DT */ + return mbus_dt_setup(&mbus_state, np); +} +#endif