162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Address map functions for Marvell EBU SoCs (Kirkwood, Armada 462306a36Sopenharmony_ci * 370/XP, Dove, Orion5x and MV78xx0) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * The Marvell EBU SoCs have a configurable physical address space: 762306a36Sopenharmony_ci * the physical address at which certain devices (PCIe, NOR, NAND, 862306a36Sopenharmony_ci * etc.) sit can be configured. The configuration takes place through 962306a36Sopenharmony_ci * two sets of registers: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * - One to configure the access of the CPU to the devices. Depending 1262306a36Sopenharmony_ci * on the families, there are between 8 and 20 configurable windows, 1362306a36Sopenharmony_ci * each can be use to create a physical memory window that maps to a 1462306a36Sopenharmony_ci * specific device. Devices are identified by a tuple (target, 1562306a36Sopenharmony_ci * attribute). 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * - One to configure the access to the CPU to the SDRAM. There are 1862306a36Sopenharmony_ci * either 2 (for Dove) or 4 (for other families) windows to map the 1962306a36Sopenharmony_ci * SDRAM into the physical address space. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * This driver: 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * - Reads out the SDRAM address decoding windows at initialization 2462306a36Sopenharmony_ci * time, and fills the mvebu_mbus_dram_info structure with these 2562306a36Sopenharmony_ci * information. The exported function mv_mbus_dram_info() allow 2662306a36Sopenharmony_ci * device drivers to get those information related to the SDRAM 2762306a36Sopenharmony_ci * address decoding windows. This is because devices also have their 2862306a36Sopenharmony_ci * own windows (configured through registers that are part of each 2962306a36Sopenharmony_ci * device register space), and therefore the drivers for Marvell 3062306a36Sopenharmony_ci * devices have to configure those device -> SDRAM windows to ensure 3162306a36Sopenharmony_ci * that DMA works properly. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * - Provides an API for platform code or device drivers to 3462306a36Sopenharmony_ci * dynamically add or remove address decoding windows for the CPU -> 3562306a36Sopenharmony_ci * device accesses. This API is mvebu_mbus_add_window_by_id(), 3662306a36Sopenharmony_ci * mvebu_mbus_add_window_remap_by_id() and 3762306a36Sopenharmony_ci * mvebu_mbus_del_window(). 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * - Provides a debugfs interface in /sys/kernel/debug/mvebu-mbus/ to 4062306a36Sopenharmony_ci * see the list of CPU -> SDRAM windows and their configuration 4162306a36Sopenharmony_ci * (file 'sdram') and the list of CPU -> devices windows and their 4262306a36Sopenharmony_ci * configuration (file 'devices'). 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <linux/kernel.h> 4862306a36Sopenharmony_ci#include <linux/module.h> 4962306a36Sopenharmony_ci#include <linux/init.h> 5062306a36Sopenharmony_ci#include <linux/mbus.h> 5162306a36Sopenharmony_ci#include <linux/io.h> 5262306a36Sopenharmony_ci#include <linux/ioport.h> 5362306a36Sopenharmony_ci#include <linux/of.h> 5462306a36Sopenharmony_ci#include <linux/of_address.h> 5562306a36Sopenharmony_ci#include <linux/debugfs.h> 5662306a36Sopenharmony_ci#include <linux/log2.h> 5762306a36Sopenharmony_ci#include <linux/memblock.h> 5862306a36Sopenharmony_ci#include <linux/syscore_ops.h> 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * DDR target is the same on all platforms. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci#define TARGET_DDR 0 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * CPU Address Decode Windows registers 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci#define WIN_CTRL_OFF 0x0000 6962306a36Sopenharmony_ci#define WIN_CTRL_ENABLE BIT(0) 7062306a36Sopenharmony_ci/* Only on HW I/O coherency capable platforms */ 7162306a36Sopenharmony_ci#define WIN_CTRL_SYNCBARRIER BIT(1) 7262306a36Sopenharmony_ci#define WIN_CTRL_TGT_MASK 0xf0 7362306a36Sopenharmony_ci#define WIN_CTRL_TGT_SHIFT 4 7462306a36Sopenharmony_ci#define WIN_CTRL_ATTR_MASK 0xff00 7562306a36Sopenharmony_ci#define WIN_CTRL_ATTR_SHIFT 8 7662306a36Sopenharmony_ci#define WIN_CTRL_SIZE_MASK 0xffff0000 7762306a36Sopenharmony_ci#define WIN_CTRL_SIZE_SHIFT 16 7862306a36Sopenharmony_ci#define WIN_BASE_OFF 0x0004 7962306a36Sopenharmony_ci#define WIN_BASE_LOW 0xffff0000 8062306a36Sopenharmony_ci#define WIN_BASE_HIGH 0xf 8162306a36Sopenharmony_ci#define WIN_REMAP_LO_OFF 0x0008 8262306a36Sopenharmony_ci#define WIN_REMAP_LOW 0xffff0000 8362306a36Sopenharmony_ci#define WIN_REMAP_HI_OFF 0x000c 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define UNIT_SYNC_BARRIER_OFF 0x84 8662306a36Sopenharmony_ci#define UNIT_SYNC_BARRIER_ALL 0xFFFF 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define ATTR_HW_COHERENCY (0x1 << 4) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3)) 9162306a36Sopenharmony_ci#define DDR_BASE_CS_HIGH_MASK 0xf 9262306a36Sopenharmony_ci#define DDR_BASE_CS_LOW_MASK 0xff000000 9362306a36Sopenharmony_ci#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3)) 9462306a36Sopenharmony_ci#define DDR_SIZE_ENABLED BIT(0) 9562306a36Sopenharmony_ci#define DDR_SIZE_CS_MASK 0x1c 9662306a36Sopenharmony_ci#define DDR_SIZE_CS_SHIFT 2 9762306a36Sopenharmony_ci#define DDR_SIZE_MASK 0xff000000 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Relative to mbusbridge_base */ 10262306a36Sopenharmony_ci#define MBUS_BRIDGE_CTRL_OFF 0x0 10362306a36Sopenharmony_ci#define MBUS_BRIDGE_BASE_OFF 0x4 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Maximum number of windows, for all known platforms */ 10662306a36Sopenharmony_ci#define MBUS_WINS_MAX 20 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistruct mvebu_mbus_state; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct mvebu_mbus_soc_data { 11162306a36Sopenharmony_ci unsigned int num_wins; 11262306a36Sopenharmony_ci bool has_mbus_bridge; 11362306a36Sopenharmony_ci unsigned int (*win_cfg_offset)(const int win); 11462306a36Sopenharmony_ci unsigned int (*win_remap_offset)(const int win); 11562306a36Sopenharmony_ci void (*setup_cpu_target)(struct mvebu_mbus_state *s); 11662306a36Sopenharmony_ci int (*save_cpu_target)(struct mvebu_mbus_state *s, 11762306a36Sopenharmony_ci u32 __iomem *store_addr); 11862306a36Sopenharmony_ci int (*show_cpu_target)(struct mvebu_mbus_state *s, 11962306a36Sopenharmony_ci struct seq_file *seq, void *v); 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci * Used to store the state of one MBus window across suspend/resume. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistruct mvebu_mbus_win_data { 12662306a36Sopenharmony_ci u32 ctrl; 12762306a36Sopenharmony_ci u32 base; 12862306a36Sopenharmony_ci u32 remap_lo; 12962306a36Sopenharmony_ci u32 remap_hi; 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct mvebu_mbus_state { 13362306a36Sopenharmony_ci void __iomem *mbuswins_base; 13462306a36Sopenharmony_ci void __iomem *sdramwins_base; 13562306a36Sopenharmony_ci void __iomem *mbusbridge_base; 13662306a36Sopenharmony_ci phys_addr_t sdramwins_phys_base; 13762306a36Sopenharmony_ci struct dentry *debugfs_root; 13862306a36Sopenharmony_ci struct dentry *debugfs_sdram; 13962306a36Sopenharmony_ci struct dentry *debugfs_devs; 14062306a36Sopenharmony_ci struct resource pcie_mem_aperture; 14162306a36Sopenharmony_ci struct resource pcie_io_aperture; 14262306a36Sopenharmony_ci const struct mvebu_mbus_soc_data *soc; 14362306a36Sopenharmony_ci int hw_io_coherency; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Used during suspend/resume */ 14662306a36Sopenharmony_ci u32 mbus_bridge_ctrl; 14762306a36Sopenharmony_ci u32 mbus_bridge_base; 14862306a36Sopenharmony_ci struct mvebu_mbus_win_data wins[MBUS_WINS_MAX]; 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct mvebu_mbus_state mbus_state; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * We provide two variants of the mv_mbus_dram_info() function: 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci * - The normal one, where the described DRAM ranges may overlap with 15762306a36Sopenharmony_ci * the I/O windows, but for which the DRAM ranges are guaranteed to 15862306a36Sopenharmony_ci * have a power of two size. Such ranges are suitable for the DMA 15962306a36Sopenharmony_ci * masters that only DMA between the RAM and the device, which is 16062306a36Sopenharmony_ci * actually all devices except the crypto engines. 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * - The 'nooverlap' one, where the described DRAM ranges are 16362306a36Sopenharmony_ci * guaranteed to not overlap with the I/O windows, but for which the 16462306a36Sopenharmony_ci * DRAM ranges will not have power of two sizes. They will only be 16562306a36Sopenharmony_ci * aligned on a 64 KB boundary, and have a size multiple of 64 16662306a36Sopenharmony_ci * KB. Such ranges are suitable for the DMA masters that DMA between 16762306a36Sopenharmony_ci * the crypto SRAM (which is mapped through an I/O window) and a 16862306a36Sopenharmony_ci * device. This is the case for the crypto engines. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct mbus_dram_target_info mvebu_mbus_dram_info; 17262306a36Sopenharmony_cistatic struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciconst struct mbus_dram_target_info *mv_mbus_dram_info(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci return &mvebu_mbus_dram_info; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mv_mbus_dram_info); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciconst struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci return &mvebu_mbus_dram_info_nooverlap; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* Checks whether the given window has remap capability */ 18762306a36Sopenharmony_cistatic bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus, 18862306a36Sopenharmony_ci const int win) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci return mbus->soc->win_remap_offset(win) != MVEBU_MBUS_NO_REMAP; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* 19462306a36Sopenharmony_ci * Functions to manipulate the address decoding windows 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void mvebu_mbus_read_window(struct mvebu_mbus_state *mbus, 19862306a36Sopenharmony_ci int win, int *enabled, u64 *base, 19962306a36Sopenharmony_ci u32 *size, u8 *target, u8 *attr, 20062306a36Sopenharmony_ci u64 *remap) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci void __iomem *addr = mbus->mbuswins_base + 20362306a36Sopenharmony_ci mbus->soc->win_cfg_offset(win); 20462306a36Sopenharmony_ci u32 basereg = readl(addr + WIN_BASE_OFF); 20562306a36Sopenharmony_ci u32 ctrlreg = readl(addr + WIN_CTRL_OFF); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!(ctrlreg & WIN_CTRL_ENABLE)) { 20862306a36Sopenharmony_ci *enabled = 0; 20962306a36Sopenharmony_ci return; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci *enabled = 1; 21362306a36Sopenharmony_ci *base = ((u64)basereg & WIN_BASE_HIGH) << 32; 21462306a36Sopenharmony_ci *base |= (basereg & WIN_BASE_LOW); 21562306a36Sopenharmony_ci *size = (ctrlreg | ~WIN_CTRL_SIZE_MASK) + 1; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (target) 21862306a36Sopenharmony_ci *target = (ctrlreg & WIN_CTRL_TGT_MASK) >> WIN_CTRL_TGT_SHIFT; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (attr) 22162306a36Sopenharmony_ci *attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (remap) { 22462306a36Sopenharmony_ci if (mvebu_mbus_window_is_remappable(mbus, win)) { 22562306a36Sopenharmony_ci u32 remap_low, remap_hi; 22662306a36Sopenharmony_ci void __iomem *addr_rmp = mbus->mbuswins_base + 22762306a36Sopenharmony_ci mbus->soc->win_remap_offset(win); 22862306a36Sopenharmony_ci remap_low = readl(addr_rmp + WIN_REMAP_LO_OFF); 22962306a36Sopenharmony_ci remap_hi = readl(addr_rmp + WIN_REMAP_HI_OFF); 23062306a36Sopenharmony_ci *remap = ((u64)remap_hi << 32) | remap_low; 23162306a36Sopenharmony_ci } else 23262306a36Sopenharmony_ci *remap = 0; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus, 23762306a36Sopenharmony_ci int win) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci void __iomem *addr; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win); 24262306a36Sopenharmony_ci writel(0, addr + WIN_BASE_OFF); 24362306a36Sopenharmony_ci writel(0, addr + WIN_CTRL_OFF); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (mvebu_mbus_window_is_remappable(mbus, win)) { 24662306a36Sopenharmony_ci addr = mbus->mbuswins_base + mbus->soc->win_remap_offset(win); 24762306a36Sopenharmony_ci writel(0, addr + WIN_REMAP_LO_OFF); 24862306a36Sopenharmony_ci writel(0, addr + WIN_REMAP_HI_OFF); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* Checks whether the given window number is available */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus, 25562306a36Sopenharmony_ci const int win) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci void __iomem *addr = mbus->mbuswins_base + 25862306a36Sopenharmony_ci mbus->soc->win_cfg_offset(win); 25962306a36Sopenharmony_ci u32 ctrl = readl(addr + WIN_CTRL_OFF); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return !(ctrl & WIN_CTRL_ENABLE); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* 26562306a36Sopenharmony_ci * Checks whether the given (base, base+size) area doesn't overlap an 26662306a36Sopenharmony_ci * existing region 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_cistatic int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus, 26962306a36Sopenharmony_ci phys_addr_t base, size_t size, 27062306a36Sopenharmony_ci u8 target, u8 attr) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci u64 end = (u64)base + size; 27362306a36Sopenharmony_ci int win; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci for (win = 0; win < mbus->soc->num_wins; win++) { 27662306a36Sopenharmony_ci u64 wbase, wend; 27762306a36Sopenharmony_ci u32 wsize; 27862306a36Sopenharmony_ci u8 wtarget, wattr; 27962306a36Sopenharmony_ci int enabled; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci mvebu_mbus_read_window(mbus, win, 28262306a36Sopenharmony_ci &enabled, &wbase, &wsize, 28362306a36Sopenharmony_ci &wtarget, &wattr, NULL); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (!enabled) 28662306a36Sopenharmony_ci continue; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci wend = wbase + wsize; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Check if the current window overlaps with the 29262306a36Sopenharmony_ci * proposed physical range 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci if ((u64)base < wend && end > wbase) 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 1; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int mvebu_mbus_find_window(struct mvebu_mbus_state *mbus, 30262306a36Sopenharmony_ci phys_addr_t base, size_t size) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int win; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (win = 0; win < mbus->soc->num_wins; win++) { 30762306a36Sopenharmony_ci u64 wbase; 30862306a36Sopenharmony_ci u32 wsize; 30962306a36Sopenharmony_ci int enabled; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci mvebu_mbus_read_window(mbus, win, 31262306a36Sopenharmony_ci &enabled, &wbase, &wsize, 31362306a36Sopenharmony_ci NULL, NULL, NULL); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!enabled) 31662306a36Sopenharmony_ci continue; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (base == wbase && size == wsize) 31962306a36Sopenharmony_ci return win; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return -ENODEV; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus, 32662306a36Sopenharmony_ci int win, phys_addr_t base, size_t size, 32762306a36Sopenharmony_ci phys_addr_t remap, u8 target, 32862306a36Sopenharmony_ci u8 attr) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci void __iomem *addr = mbus->mbuswins_base + 33162306a36Sopenharmony_ci mbus->soc->win_cfg_offset(win); 33262306a36Sopenharmony_ci u32 ctrl, remap_addr; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!is_power_of_2(size)) { 33562306a36Sopenharmony_ci WARN(true, "Invalid MBus window size: 0x%zx\n", size); 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if ((base & (phys_addr_t)(size - 1)) != 0) { 34062306a36Sopenharmony_ci WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base, 34162306a36Sopenharmony_ci size); 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | 34662306a36Sopenharmony_ci (attr << WIN_CTRL_ATTR_SHIFT) | 34762306a36Sopenharmony_ci (target << WIN_CTRL_TGT_SHIFT) | 34862306a36Sopenharmony_ci WIN_CTRL_ENABLE; 34962306a36Sopenharmony_ci if (mbus->hw_io_coherency) 35062306a36Sopenharmony_ci ctrl |= WIN_CTRL_SYNCBARRIER; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF); 35362306a36Sopenharmony_ci writel(ctrl, addr + WIN_CTRL_OFF); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (mvebu_mbus_window_is_remappable(mbus, win)) { 35662306a36Sopenharmony_ci void __iomem *addr_rmp = mbus->mbuswins_base + 35762306a36Sopenharmony_ci mbus->soc->win_remap_offset(win); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (remap == MVEBU_MBUS_NO_REMAP) 36062306a36Sopenharmony_ci remap_addr = base; 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci remap_addr = remap; 36362306a36Sopenharmony_ci writel(remap_addr & WIN_REMAP_LOW, addr_rmp + WIN_REMAP_LO_OFF); 36462306a36Sopenharmony_ci writel(0, addr_rmp + WIN_REMAP_HI_OFF); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int mvebu_mbus_alloc_window(struct mvebu_mbus_state *mbus, 37162306a36Sopenharmony_ci phys_addr_t base, size_t size, 37262306a36Sopenharmony_ci phys_addr_t remap, u8 target, 37362306a36Sopenharmony_ci u8 attr) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci int win; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (remap == MVEBU_MBUS_NO_REMAP) { 37862306a36Sopenharmony_ci for (win = 0; win < mbus->soc->num_wins; win++) { 37962306a36Sopenharmony_ci if (mvebu_mbus_window_is_remappable(mbus, win)) 38062306a36Sopenharmony_ci continue; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (mvebu_mbus_window_is_free(mbus, win)) 38362306a36Sopenharmony_ci return mvebu_mbus_setup_window(mbus, win, base, 38462306a36Sopenharmony_ci size, remap, 38562306a36Sopenharmony_ci target, attr); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci for (win = 0; win < mbus->soc->num_wins; win++) { 39062306a36Sopenharmony_ci /* Skip window if need remap but is not supported */ 39162306a36Sopenharmony_ci if ((remap != MVEBU_MBUS_NO_REMAP) && 39262306a36Sopenharmony_ci !mvebu_mbus_window_is_remappable(mbus, win)) 39362306a36Sopenharmony_ci continue; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (mvebu_mbus_window_is_free(mbus, win)) 39662306a36Sopenharmony_ci return mvebu_mbus_setup_window(mbus, win, base, size, 39762306a36Sopenharmony_ci remap, target, attr); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return -ENOMEM; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * Debugfs debugging 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* Common function used for Dove, Kirkwood, Armada 370/XP and Orion 5x */ 40862306a36Sopenharmony_cistatic int mvebu_sdram_debug_show_orion(struct mvebu_mbus_state *mbus, 40962306a36Sopenharmony_ci struct seq_file *seq, void *v) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci int i; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 41462306a36Sopenharmony_ci u32 basereg = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); 41562306a36Sopenharmony_ci u32 sizereg = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); 41662306a36Sopenharmony_ci u64 base; 41762306a36Sopenharmony_ci u32 size; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!(sizereg & DDR_SIZE_ENABLED)) { 42062306a36Sopenharmony_ci seq_printf(seq, "[%d] disabled\n", i); 42162306a36Sopenharmony_ci continue; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci base = ((u64)basereg & DDR_BASE_CS_HIGH_MASK) << 32; 42562306a36Sopenharmony_ci base |= basereg & DDR_BASE_CS_LOW_MASK; 42662306a36Sopenharmony_ci size = (sizereg | ~DDR_SIZE_MASK); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n", 42962306a36Sopenharmony_ci i, (unsigned long long)base, 43062306a36Sopenharmony_ci (unsigned long long)base + size + 1, 43162306a36Sopenharmony_ci (sizereg & DDR_SIZE_CS_MASK) >> DDR_SIZE_CS_SHIFT); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/* Special function for Dove */ 43862306a36Sopenharmony_cistatic int mvebu_sdram_debug_show_dove(struct mvebu_mbus_state *mbus, 43962306a36Sopenharmony_ci struct seq_file *seq, void *v) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int i; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 44462306a36Sopenharmony_ci u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); 44562306a36Sopenharmony_ci u64 base; 44662306a36Sopenharmony_ci u32 size; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!(map & 1)) { 44962306a36Sopenharmony_ci seq_printf(seq, "[%d] disabled\n", i); 45062306a36Sopenharmony_ci continue; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci base = map & 0xff800000; 45462306a36Sopenharmony_ci size = 0x100000 << (((map & 0x000f0000) >> 16) - 4); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci seq_printf(seq, "[%d] %016llx - %016llx : cs%d\n", 45762306a36Sopenharmony_ci i, (unsigned long long)base, 45862306a36Sopenharmony_ci (unsigned long long)base + size, i); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int mvebu_sdram_debug_show(struct seq_file *seq, void *v) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct mvebu_mbus_state *mbus = &mbus_state; 46762306a36Sopenharmony_ci return mbus->soc->show_cpu_target(mbus, seq, v); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mvebu_sdram_debug); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int mvebu_devs_debug_show(struct seq_file *seq, void *v) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct mvebu_mbus_state *mbus = &mbus_state; 47462306a36Sopenharmony_ci int win; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci for (win = 0; win < mbus->soc->num_wins; win++) { 47762306a36Sopenharmony_ci u64 wbase, wremap; 47862306a36Sopenharmony_ci u32 wsize; 47962306a36Sopenharmony_ci u8 wtarget, wattr; 48062306a36Sopenharmony_ci int enabled; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mvebu_mbus_read_window(mbus, win, 48362306a36Sopenharmony_ci &enabled, &wbase, &wsize, 48462306a36Sopenharmony_ci &wtarget, &wattr, &wremap); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (!enabled) { 48762306a36Sopenharmony_ci seq_printf(seq, "[%02d] disabled\n", win); 48862306a36Sopenharmony_ci continue; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci seq_printf(seq, "[%02d] %016llx - %016llx : %04x:%04x", 49262306a36Sopenharmony_ci win, (unsigned long long)wbase, 49362306a36Sopenharmony_ci (unsigned long long)(wbase + wsize), wtarget, wattr); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (!is_power_of_2(wsize) || 49662306a36Sopenharmony_ci ((wbase & (u64)(wsize - 1)) != 0)) 49762306a36Sopenharmony_ci seq_puts(seq, " (Invalid base/size!!)"); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (mvebu_mbus_window_is_remappable(mbus, win)) { 50062306a36Sopenharmony_ci seq_printf(seq, " (remap %016llx)\n", 50162306a36Sopenharmony_ci (unsigned long long)wremap); 50262306a36Sopenharmony_ci } else 50362306a36Sopenharmony_ci seq_printf(seq, "\n"); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mvebu_devs_debug); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci/* 51162306a36Sopenharmony_ci * SoC-specific functions and definitions 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic unsigned int generic_mbus_win_cfg_offset(int win) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci return win << 4; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic unsigned int armada_370_xp_mbus_win_cfg_offset(int win) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci /* The register layout is a bit annoying and the below code 52262306a36Sopenharmony_ci * tries to cope with it. 52362306a36Sopenharmony_ci * - At offset 0x0, there are the registers for the first 8 52462306a36Sopenharmony_ci * windows, with 4 registers of 32 bits per window (ctrl, 52562306a36Sopenharmony_ci * base, remap low, remap high) 52662306a36Sopenharmony_ci * - Then at offset 0x80, there is a hole of 0x10 bytes for 52762306a36Sopenharmony_ci * the internal registers base address and internal units 52862306a36Sopenharmony_ci * sync barrier register. 52962306a36Sopenharmony_ci * - Then at offset 0x90, there the registers for 12 53062306a36Sopenharmony_ci * windows, with only 2 registers of 32 bits per window 53162306a36Sopenharmony_ci * (ctrl, base). 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci if (win < 8) 53462306a36Sopenharmony_ci return win << 4; 53562306a36Sopenharmony_ci else 53662306a36Sopenharmony_ci return 0x90 + ((win - 8) << 3); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic unsigned int mv78xx0_mbus_win_cfg_offset(int win) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci if (win < 8) 54262306a36Sopenharmony_ci return win << 4; 54362306a36Sopenharmony_ci else 54462306a36Sopenharmony_ci return 0x900 + ((win - 8) << 4); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic unsigned int generic_mbus_win_remap_2_offset(int win) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci if (win < 2) 55062306a36Sopenharmony_ci return generic_mbus_win_cfg_offset(win); 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci return MVEBU_MBUS_NO_REMAP; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic unsigned int generic_mbus_win_remap_4_offset(int win) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci if (win < 4) 55862306a36Sopenharmony_ci return generic_mbus_win_cfg_offset(win); 55962306a36Sopenharmony_ci else 56062306a36Sopenharmony_ci return MVEBU_MBUS_NO_REMAP; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic unsigned int generic_mbus_win_remap_8_offset(int win) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci if (win < 8) 56662306a36Sopenharmony_ci return generic_mbus_win_cfg_offset(win); 56762306a36Sopenharmony_ci else 56862306a36Sopenharmony_ci return MVEBU_MBUS_NO_REMAP; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic unsigned int armada_xp_mbus_win_remap_offset(int win) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci if (win < 8) 57462306a36Sopenharmony_ci return generic_mbus_win_cfg_offset(win); 57562306a36Sopenharmony_ci else if (win == 13) 57662306a36Sopenharmony_ci return 0xF0 - WIN_REMAP_LO_OFF; 57762306a36Sopenharmony_ci else 57862306a36Sopenharmony_ci return MVEBU_MBUS_NO_REMAP; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/* 58262306a36Sopenharmony_ci * Use the memblock information to find the MBus bridge hole in the 58362306a36Sopenharmony_ci * physical address space. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_cistatic void __init 58662306a36Sopenharmony_cimvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci phys_addr_t reg_start, reg_end; 58962306a36Sopenharmony_ci uint64_t i, s = 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci for_each_mem_range(i, ®_start, ®_end) { 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * This part of the memory is above 4 GB, so we don't 59462306a36Sopenharmony_ci * care for the MBus bridge hole. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci if ((u64)reg_start >= 0x100000000ULL) 59762306a36Sopenharmony_ci continue; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* 60062306a36Sopenharmony_ci * The MBus bridge hole is at the end of the RAM under 60162306a36Sopenharmony_ci * the 4 GB limit. 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_ci if (reg_end > s) 60462306a36Sopenharmony_ci s = reg_end; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci *start = s; 60862306a36Sopenharmony_ci *end = 0x100000000ULL; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/* 61262306a36Sopenharmony_ci * This function fills in the mvebu_mbus_dram_info_nooverlap data 61362306a36Sopenharmony_ci * structure, by looking at the mvebu_mbus_dram_info data, and 61462306a36Sopenharmony_ci * removing the parts of it that overlap with I/O windows. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_cistatic void __init 61762306a36Sopenharmony_cimvebu_mbus_setup_cpu_target_nooverlap(struct mvebu_mbus_state *mbus) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci uint64_t mbus_bridge_base, mbus_bridge_end; 62062306a36Sopenharmony_ci int cs_nooverlap = 0; 62162306a36Sopenharmony_ci int i; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) { 62662306a36Sopenharmony_ci struct mbus_dram_window *w; 62762306a36Sopenharmony_ci u64 base, size, end; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci w = &mvebu_mbus_dram_info.cs[i]; 63062306a36Sopenharmony_ci base = w->base; 63162306a36Sopenharmony_ci size = w->size; 63262306a36Sopenharmony_ci end = base + size; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * The CS is fully enclosed inside the MBus bridge 63662306a36Sopenharmony_ci * area, so ignore it. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci if (base >= mbus_bridge_base && end <= mbus_bridge_end) 63962306a36Sopenharmony_ci continue; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* 64262306a36Sopenharmony_ci * Beginning of CS overlaps with end of MBus, raise CS 64362306a36Sopenharmony_ci * base address, and shrink its size. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ci if (base >= mbus_bridge_base && end > mbus_bridge_end) { 64662306a36Sopenharmony_ci size -= mbus_bridge_end - base; 64762306a36Sopenharmony_ci base = mbus_bridge_end; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* 65162306a36Sopenharmony_ci * End of CS overlaps with beginning of MBus, shrink 65262306a36Sopenharmony_ci * CS size. 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_ci if (base < mbus_bridge_base && end > mbus_bridge_base) 65562306a36Sopenharmony_ci size -= end - mbus_bridge_base; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++]; 65862306a36Sopenharmony_ci w->cs_index = i; 65962306a36Sopenharmony_ci w->mbus_attr = 0xf & ~(1 << i); 66062306a36Sopenharmony_ci if (mbus->hw_io_coherency) 66162306a36Sopenharmony_ci w->mbus_attr |= ATTR_HW_COHERENCY; 66262306a36Sopenharmony_ci w->base = base; 66362306a36Sopenharmony_ci w->size = size; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR; 66762306a36Sopenharmony_ci mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic void __init 67162306a36Sopenharmony_cimvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci int i; 67462306a36Sopenharmony_ci int cs; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci for (i = 0, cs = 0; i < 4; i++) { 67962306a36Sopenharmony_ci u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); 68062306a36Sopenharmony_ci u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* 68362306a36Sopenharmony_ci * We only take care of entries for which the chip 68462306a36Sopenharmony_ci * select is enabled, and that don't have high base 68562306a36Sopenharmony_ci * address bits set (devices can only access the first 68662306a36Sopenharmony_ci * 32 bits of the memory). 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci if ((size & DDR_SIZE_ENABLED) && 68962306a36Sopenharmony_ci !(base & DDR_BASE_CS_HIGH_MASK)) { 69062306a36Sopenharmony_ci struct mbus_dram_window *w; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci w = &mvebu_mbus_dram_info.cs[cs++]; 69362306a36Sopenharmony_ci w->cs_index = i; 69462306a36Sopenharmony_ci w->mbus_attr = 0xf & ~(1 << i); 69562306a36Sopenharmony_ci if (mbus->hw_io_coherency) 69662306a36Sopenharmony_ci w->mbus_attr |= ATTR_HW_COHERENCY; 69762306a36Sopenharmony_ci w->base = base & DDR_BASE_CS_LOW_MASK; 69862306a36Sopenharmony_ci w->size = (u64)(size | ~DDR_SIZE_MASK) + 1; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci mvebu_mbus_dram_info.num_cs = cs; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int 70562306a36Sopenharmony_cimvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus, 70662306a36Sopenharmony_ci u32 __iomem *store_addr) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci int i; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 71162306a36Sopenharmony_ci u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); 71262306a36Sopenharmony_ci u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i), 71562306a36Sopenharmony_ci store_addr++); 71662306a36Sopenharmony_ci writel(base, store_addr++); 71762306a36Sopenharmony_ci writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i), 71862306a36Sopenharmony_ci store_addr++); 71962306a36Sopenharmony_ci writel(size, store_addr++); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* We've written 16 words to the store address */ 72362306a36Sopenharmony_ci return 16; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void __init 72762306a36Sopenharmony_cimvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci int i; 73062306a36Sopenharmony_ci int cs; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci for (i = 0, cs = 0; i < 2; i++) { 73562306a36Sopenharmony_ci u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* 73862306a36Sopenharmony_ci * Chip select enabled? 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ci if (map & 1) { 74162306a36Sopenharmony_ci struct mbus_dram_window *w; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci w = &mvebu_mbus_dram_info.cs[cs++]; 74462306a36Sopenharmony_ci w->cs_index = i; 74562306a36Sopenharmony_ci w->mbus_attr = 0; /* CS address decoding done inside */ 74662306a36Sopenharmony_ci /* the DDR controller, no need to */ 74762306a36Sopenharmony_ci /* provide attributes */ 74862306a36Sopenharmony_ci w->base = map & 0xff800000; 74962306a36Sopenharmony_ci w->size = 0x100000 << (((map & 0x000f0000) >> 16) - 4); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci mvebu_mbus_dram_info.num_cs = cs; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int 75762306a36Sopenharmony_cimvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus, 75862306a36Sopenharmony_ci u32 __iomem *store_addr) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci int i; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 76362306a36Sopenharmony_ci u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i), 76662306a36Sopenharmony_ci store_addr++); 76762306a36Sopenharmony_ci writel(map, store_addr++); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* We've written 4 words to the store address */ 77162306a36Sopenharmony_ci return 4; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ciint mvebu_mbus_save_cpu_target(u32 __iomem *store_addr) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci return mbus_state.soc->save_cpu_target(&mbus_state, store_addr); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic const struct mvebu_mbus_soc_data armada_370_mbus_data = { 78062306a36Sopenharmony_ci .num_wins = 20, 78162306a36Sopenharmony_ci .has_mbus_bridge = true, 78262306a36Sopenharmony_ci .win_cfg_offset = armada_370_xp_mbus_win_cfg_offset, 78362306a36Sopenharmony_ci .win_remap_offset = generic_mbus_win_remap_8_offset, 78462306a36Sopenharmony_ci .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, 78562306a36Sopenharmony_ci .show_cpu_target = mvebu_sdram_debug_show_orion, 78662306a36Sopenharmony_ci .save_cpu_target = mvebu_mbus_default_save_cpu_target, 78762306a36Sopenharmony_ci}; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic const struct mvebu_mbus_soc_data armada_xp_mbus_data = { 79062306a36Sopenharmony_ci .num_wins = 20, 79162306a36Sopenharmony_ci .has_mbus_bridge = true, 79262306a36Sopenharmony_ci .win_cfg_offset = armada_370_xp_mbus_win_cfg_offset, 79362306a36Sopenharmony_ci .win_remap_offset = armada_xp_mbus_win_remap_offset, 79462306a36Sopenharmony_ci .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, 79562306a36Sopenharmony_ci .show_cpu_target = mvebu_sdram_debug_show_orion, 79662306a36Sopenharmony_ci .save_cpu_target = mvebu_mbus_default_save_cpu_target, 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic const struct mvebu_mbus_soc_data kirkwood_mbus_data = { 80062306a36Sopenharmony_ci .num_wins = 8, 80162306a36Sopenharmony_ci .win_cfg_offset = generic_mbus_win_cfg_offset, 80262306a36Sopenharmony_ci .save_cpu_target = mvebu_mbus_default_save_cpu_target, 80362306a36Sopenharmony_ci .win_remap_offset = generic_mbus_win_remap_4_offset, 80462306a36Sopenharmony_ci .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, 80562306a36Sopenharmony_ci .show_cpu_target = mvebu_sdram_debug_show_orion, 80662306a36Sopenharmony_ci}; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic const struct mvebu_mbus_soc_data dove_mbus_data = { 80962306a36Sopenharmony_ci .num_wins = 8, 81062306a36Sopenharmony_ci .win_cfg_offset = generic_mbus_win_cfg_offset, 81162306a36Sopenharmony_ci .save_cpu_target = mvebu_mbus_dove_save_cpu_target, 81262306a36Sopenharmony_ci .win_remap_offset = generic_mbus_win_remap_4_offset, 81362306a36Sopenharmony_ci .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, 81462306a36Sopenharmony_ci .show_cpu_target = mvebu_sdram_debug_show_dove, 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci/* 81862306a36Sopenharmony_ci * Some variants of Orion5x have 4 remappable windows, some other have 81962306a36Sopenharmony_ci * only two of them. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_cistatic const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = { 82262306a36Sopenharmony_ci .num_wins = 8, 82362306a36Sopenharmony_ci .win_cfg_offset = generic_mbus_win_cfg_offset, 82462306a36Sopenharmony_ci .save_cpu_target = mvebu_mbus_default_save_cpu_target, 82562306a36Sopenharmony_ci .win_remap_offset = generic_mbus_win_remap_4_offset, 82662306a36Sopenharmony_ci .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, 82762306a36Sopenharmony_ci .show_cpu_target = mvebu_sdram_debug_show_orion, 82862306a36Sopenharmony_ci}; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = { 83162306a36Sopenharmony_ci .num_wins = 8, 83262306a36Sopenharmony_ci .win_cfg_offset = generic_mbus_win_cfg_offset, 83362306a36Sopenharmony_ci .save_cpu_target = mvebu_mbus_default_save_cpu_target, 83462306a36Sopenharmony_ci .win_remap_offset = generic_mbus_win_remap_2_offset, 83562306a36Sopenharmony_ci .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, 83662306a36Sopenharmony_ci .show_cpu_target = mvebu_sdram_debug_show_orion, 83762306a36Sopenharmony_ci}; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic const struct mvebu_mbus_soc_data mv78xx0_mbus_data = { 84062306a36Sopenharmony_ci .num_wins = 14, 84162306a36Sopenharmony_ci .win_cfg_offset = mv78xx0_mbus_win_cfg_offset, 84262306a36Sopenharmony_ci .save_cpu_target = mvebu_mbus_default_save_cpu_target, 84362306a36Sopenharmony_ci .win_remap_offset = generic_mbus_win_remap_8_offset, 84462306a36Sopenharmony_ci .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, 84562306a36Sopenharmony_ci .show_cpu_target = mvebu_sdram_debug_show_orion, 84662306a36Sopenharmony_ci}; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic const struct of_device_id of_mvebu_mbus_ids[] = { 84962306a36Sopenharmony_ci { .compatible = "marvell,armada370-mbus", 85062306a36Sopenharmony_ci .data = &armada_370_mbus_data, }, 85162306a36Sopenharmony_ci { .compatible = "marvell,armada375-mbus", 85262306a36Sopenharmony_ci .data = &armada_xp_mbus_data, }, 85362306a36Sopenharmony_ci { .compatible = "marvell,armada380-mbus", 85462306a36Sopenharmony_ci .data = &armada_xp_mbus_data, }, 85562306a36Sopenharmony_ci { .compatible = "marvell,armadaxp-mbus", 85662306a36Sopenharmony_ci .data = &armada_xp_mbus_data, }, 85762306a36Sopenharmony_ci { .compatible = "marvell,kirkwood-mbus", 85862306a36Sopenharmony_ci .data = &kirkwood_mbus_data, }, 85962306a36Sopenharmony_ci { .compatible = "marvell,dove-mbus", 86062306a36Sopenharmony_ci .data = &dove_mbus_data, }, 86162306a36Sopenharmony_ci { .compatible = "marvell,orion5x-88f5281-mbus", 86262306a36Sopenharmony_ci .data = &orion5x_4win_mbus_data, }, 86362306a36Sopenharmony_ci { .compatible = "marvell,orion5x-88f5182-mbus", 86462306a36Sopenharmony_ci .data = &orion5x_2win_mbus_data, }, 86562306a36Sopenharmony_ci { .compatible = "marvell,orion5x-88f5181-mbus", 86662306a36Sopenharmony_ci .data = &orion5x_2win_mbus_data, }, 86762306a36Sopenharmony_ci { .compatible = "marvell,orion5x-88f6183-mbus", 86862306a36Sopenharmony_ci .data = &orion5x_4win_mbus_data, }, 86962306a36Sopenharmony_ci { .compatible = "marvell,mv78xx0-mbus", 87062306a36Sopenharmony_ci .data = &mv78xx0_mbus_data, }, 87162306a36Sopenharmony_ci { }, 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci/* 87562306a36Sopenharmony_ci * Public API of the driver 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_ciint mvebu_mbus_add_window_remap_by_id(unsigned int target, 87862306a36Sopenharmony_ci unsigned int attribute, 87962306a36Sopenharmony_ci phys_addr_t base, size_t size, 88062306a36Sopenharmony_ci phys_addr_t remap) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct mvebu_mbus_state *s = &mbus_state; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (!mvebu_mbus_window_conflicts(s, base, size, target, attribute)) { 88562306a36Sopenharmony_ci pr_err("cannot add window '%x:%x', conflicts with another window\n", 88662306a36Sopenharmony_ci target, attribute); 88762306a36Sopenharmony_ci return -EINVAL; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mvebu_mbus_add_window_remap_by_id); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ciint mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, 89562306a36Sopenharmony_ci phys_addr_t base, size_t size) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci return mvebu_mbus_add_window_remap_by_id(target, attribute, base, 89862306a36Sopenharmony_ci size, MVEBU_MBUS_NO_REMAP); 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mvebu_mbus_add_window_by_id); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ciint mvebu_mbus_del_window(phys_addr_t base, size_t size) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci int win; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci win = mvebu_mbus_find_window(&mbus_state, base, size); 90762306a36Sopenharmony_ci if (win < 0) 90862306a36Sopenharmony_ci return win; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci mvebu_mbus_disable_window(&mbus_state, win); 91162306a36Sopenharmony_ci return 0; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mvebu_mbus_del_window); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_civoid mvebu_mbus_get_pcie_mem_aperture(struct resource *res) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci if (!res) 91862306a36Sopenharmony_ci return; 91962306a36Sopenharmony_ci *res = mbus_state.pcie_mem_aperture; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_mem_aperture); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_civoid mvebu_mbus_get_pcie_io_aperture(struct resource *res) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci if (!res) 92662306a36Sopenharmony_ci return; 92762306a36Sopenharmony_ci *res = mbus_state.pcie_io_aperture; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_io_aperture); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ciint mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci const struct mbus_dram_target_info *dram; 93462306a36Sopenharmony_ci int i; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* Get dram info */ 93762306a36Sopenharmony_ci dram = mv_mbus_dram_info(); 93862306a36Sopenharmony_ci if (!dram) { 93962306a36Sopenharmony_ci pr_err("missing DRAM information\n"); 94062306a36Sopenharmony_ci return -ENODEV; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* Try to find matching DRAM window for phyaddr */ 94462306a36Sopenharmony_ci for (i = 0; i < dram->num_cs; i++) { 94562306a36Sopenharmony_ci const struct mbus_dram_window *cs = dram->cs + i; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (cs->base <= phyaddr && 94862306a36Sopenharmony_ci phyaddr <= (cs->base + cs->size - 1)) { 94962306a36Sopenharmony_ci *target = dram->mbus_dram_target_id; 95062306a36Sopenharmony_ci *attr = cs->mbus_attr; 95162306a36Sopenharmony_ci return 0; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci pr_err("invalid dram address %pa\n", &phyaddr); 95662306a36Sopenharmony_ci return -EINVAL; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mvebu_mbus_get_dram_win_info); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ciint mvebu_mbus_get_io_win_info(phys_addr_t phyaddr, u32 *size, u8 *target, 96162306a36Sopenharmony_ci u8 *attr) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci int win; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci for (win = 0; win < mbus_state.soc->num_wins; win++) { 96662306a36Sopenharmony_ci u64 wbase; 96762306a36Sopenharmony_ci int enabled; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci mvebu_mbus_read_window(&mbus_state, win, &enabled, &wbase, 97062306a36Sopenharmony_ci size, target, attr, NULL); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (!enabled) 97362306a36Sopenharmony_ci continue; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (wbase <= phyaddr && phyaddr <= wbase + *size) 97662306a36Sopenharmony_ci return win; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return -EINVAL; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mvebu_mbus_get_io_win_info); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic __init int mvebu_mbus_debugfs_init(void) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci struct mvebu_mbus_state *s = &mbus_state; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* 98862306a36Sopenharmony_ci * If no base has been initialized, doesn't make sense to 98962306a36Sopenharmony_ci * register the debugfs entries. We may be on a multiplatform 99062306a36Sopenharmony_ci * kernel that isn't running a Marvell EBU SoC. 99162306a36Sopenharmony_ci */ 99262306a36Sopenharmony_ci if (!s->mbuswins_base) 99362306a36Sopenharmony_ci return 0; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci s->debugfs_root = debugfs_create_dir("mvebu-mbus", NULL); 99662306a36Sopenharmony_ci if (s->debugfs_root) { 99762306a36Sopenharmony_ci s->debugfs_sdram = debugfs_create_file("sdram", S_IRUGO, 99862306a36Sopenharmony_ci s->debugfs_root, NULL, 99962306a36Sopenharmony_ci &mvebu_sdram_debug_fops); 100062306a36Sopenharmony_ci s->debugfs_devs = debugfs_create_file("devices", S_IRUGO, 100162306a36Sopenharmony_ci s->debugfs_root, NULL, 100262306a36Sopenharmony_ci &mvebu_devs_debug_fops); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_cifs_initcall(mvebu_mbus_debugfs_init); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cistatic int mvebu_mbus_suspend(void) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct mvebu_mbus_state *s = &mbus_state; 101262306a36Sopenharmony_ci int win; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (!s->mbusbridge_base) 101562306a36Sopenharmony_ci return -ENODEV; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci for (win = 0; win < s->soc->num_wins; win++) { 101862306a36Sopenharmony_ci void __iomem *addr = s->mbuswins_base + 101962306a36Sopenharmony_ci s->soc->win_cfg_offset(win); 102062306a36Sopenharmony_ci void __iomem *addr_rmp; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci s->wins[win].base = readl(addr + WIN_BASE_OFF); 102362306a36Sopenharmony_ci s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (!mvebu_mbus_window_is_remappable(s, win)) 102662306a36Sopenharmony_ci continue; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci addr_rmp = s->mbuswins_base + 102962306a36Sopenharmony_ci s->soc->win_remap_offset(win); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci s->wins[win].remap_lo = readl(addr_rmp + WIN_REMAP_LO_OFF); 103262306a36Sopenharmony_ci s->wins[win].remap_hi = readl(addr_rmp + WIN_REMAP_HI_OFF); 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci s->mbus_bridge_ctrl = readl(s->mbusbridge_base + 103662306a36Sopenharmony_ci MBUS_BRIDGE_CTRL_OFF); 103762306a36Sopenharmony_ci s->mbus_bridge_base = readl(s->mbusbridge_base + 103862306a36Sopenharmony_ci MBUS_BRIDGE_BASE_OFF); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci return 0; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic void mvebu_mbus_resume(void) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci struct mvebu_mbus_state *s = &mbus_state; 104662306a36Sopenharmony_ci int win; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci writel(s->mbus_bridge_ctrl, 104962306a36Sopenharmony_ci s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF); 105062306a36Sopenharmony_ci writel(s->mbus_bridge_base, 105162306a36Sopenharmony_ci s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci for (win = 0; win < s->soc->num_wins; win++) { 105462306a36Sopenharmony_ci void __iomem *addr = s->mbuswins_base + 105562306a36Sopenharmony_ci s->soc->win_cfg_offset(win); 105662306a36Sopenharmony_ci void __iomem *addr_rmp; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci writel(s->wins[win].base, addr + WIN_BASE_OFF); 105962306a36Sopenharmony_ci writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (!mvebu_mbus_window_is_remappable(s, win)) 106262306a36Sopenharmony_ci continue; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci addr_rmp = s->mbuswins_base + 106562306a36Sopenharmony_ci s->soc->win_remap_offset(win); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci writel(s->wins[win].remap_lo, addr_rmp + WIN_REMAP_LO_OFF); 106862306a36Sopenharmony_ci writel(s->wins[win].remap_hi, addr_rmp + WIN_REMAP_HI_OFF); 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic struct syscore_ops mvebu_mbus_syscore_ops = { 107362306a36Sopenharmony_ci .suspend = mvebu_mbus_suspend, 107462306a36Sopenharmony_ci .resume = mvebu_mbus_resume, 107562306a36Sopenharmony_ci}; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, 107862306a36Sopenharmony_ci phys_addr_t mbuswins_phys_base, 107962306a36Sopenharmony_ci size_t mbuswins_size, 108062306a36Sopenharmony_ci phys_addr_t sdramwins_phys_base, 108162306a36Sopenharmony_ci size_t sdramwins_size, 108262306a36Sopenharmony_ci phys_addr_t mbusbridge_phys_base, 108362306a36Sopenharmony_ci size_t mbusbridge_size, 108462306a36Sopenharmony_ci bool is_coherent) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci int win; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size); 108962306a36Sopenharmony_ci if (!mbus->mbuswins_base) 109062306a36Sopenharmony_ci return -ENOMEM; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci mbus->sdramwins_base = ioremap(sdramwins_phys_base, sdramwins_size); 109362306a36Sopenharmony_ci if (!mbus->sdramwins_base) { 109462306a36Sopenharmony_ci iounmap(mbus->mbuswins_base); 109562306a36Sopenharmony_ci return -ENOMEM; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci mbus->sdramwins_phys_base = sdramwins_phys_base; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (mbusbridge_phys_base) { 110162306a36Sopenharmony_ci mbus->mbusbridge_base = ioremap(mbusbridge_phys_base, 110262306a36Sopenharmony_ci mbusbridge_size); 110362306a36Sopenharmony_ci if (!mbus->mbusbridge_base) { 110462306a36Sopenharmony_ci iounmap(mbus->sdramwins_base); 110562306a36Sopenharmony_ci iounmap(mbus->mbuswins_base); 110662306a36Sopenharmony_ci return -ENOMEM; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci } else 110962306a36Sopenharmony_ci mbus->mbusbridge_base = NULL; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci for (win = 0; win < mbus->soc->num_wins; win++) 111262306a36Sopenharmony_ci mvebu_mbus_disable_window(mbus, win); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci mbus->soc->setup_cpu_target(mbus); 111562306a36Sopenharmony_ci mvebu_mbus_setup_cpu_target_nooverlap(mbus); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (is_coherent) 111862306a36Sopenharmony_ci writel(UNIT_SYNC_BARRIER_ALL, 111962306a36Sopenharmony_ci mbus->mbuswins_base + UNIT_SYNC_BARRIER_OFF); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci register_syscore_ops(&mvebu_mbus_syscore_ops); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return 0; 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ciint __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, 112762306a36Sopenharmony_ci size_t mbuswins_size, 112862306a36Sopenharmony_ci phys_addr_t sdramwins_phys_base, 112962306a36Sopenharmony_ci size_t sdramwins_size) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci const struct of_device_id *of_id; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci for (of_id = of_mvebu_mbus_ids; of_id->compatible[0]; of_id++) 113462306a36Sopenharmony_ci if (!strcmp(of_id->compatible, soc)) 113562306a36Sopenharmony_ci break; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (!of_id->compatible[0]) { 113862306a36Sopenharmony_ci pr_err("could not find a matching SoC family\n"); 113962306a36Sopenharmony_ci return -ENODEV; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci mbus_state.soc = of_id->data; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci return mvebu_mbus_common_init(&mbus_state, 114562306a36Sopenharmony_ci mbuswins_phys_base, 114662306a36Sopenharmony_ci mbuswins_size, 114762306a36Sopenharmony_ci sdramwins_phys_base, 114862306a36Sopenharmony_ci sdramwins_size, 0, 0, false); 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci#ifdef CONFIG_OF 115262306a36Sopenharmony_ci/* 115362306a36Sopenharmony_ci * The window IDs in the ranges DT property have the following format: 115462306a36Sopenharmony_ci * - bits 28 to 31: MBus custom field 115562306a36Sopenharmony_ci * - bits 24 to 27: window target ID 115662306a36Sopenharmony_ci * - bits 16 to 23: window attribute ID 115762306a36Sopenharmony_ci * - bits 0 to 15: unused 115862306a36Sopenharmony_ci */ 115962306a36Sopenharmony_ci#define CUSTOM(id) (((id) & 0xF0000000) >> 24) 116062306a36Sopenharmony_ci#define TARGET(id) (((id) & 0x0F000000) >> 24) 116162306a36Sopenharmony_ci#define ATTR(id) (((id) & 0x00FF0000) >> 16) 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus, 116462306a36Sopenharmony_ci u32 base, u32 size, 116562306a36Sopenharmony_ci u8 target, u8 attr) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci if (!mvebu_mbus_window_conflicts(mbus, base, size, target, attr)) { 116862306a36Sopenharmony_ci pr_err("cannot add window '%04x:%04x', conflicts with another window\n", 116962306a36Sopenharmony_ci target, attr); 117062306a36Sopenharmony_ci return -EBUSY; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (mvebu_mbus_alloc_window(mbus, base, size, MVEBU_MBUS_NO_REMAP, 117462306a36Sopenharmony_ci target, attr)) { 117562306a36Sopenharmony_ci pr_err("cannot add window '%04x:%04x', too many windows\n", 117662306a36Sopenharmony_ci target, attr); 117762306a36Sopenharmony_ci return -ENOMEM; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci return 0; 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic int __init mbus_dt_setup(struct mvebu_mbus_state *mbus, 118362306a36Sopenharmony_ci struct device_node *np) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci int ret; 118662306a36Sopenharmony_ci struct of_range_parser parser; 118762306a36Sopenharmony_ci struct of_range range; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci ret = of_range_parser_init(&parser, np); 119062306a36Sopenharmony_ci if (ret < 0) 119162306a36Sopenharmony_ci return 0; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci for_each_of_range(&parser, &range) { 119462306a36Sopenharmony_ci u32 windowid = upper_32_bits(range.bus_addr); 119562306a36Sopenharmony_ci u8 target, attr; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* 119862306a36Sopenharmony_ci * An entry with a non-zero custom field do not 119962306a36Sopenharmony_ci * correspond to a static window, so skip it. 120062306a36Sopenharmony_ci */ 120162306a36Sopenharmony_ci if (CUSTOM(windowid)) 120262306a36Sopenharmony_ci continue; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci target = TARGET(windowid); 120562306a36Sopenharmony_ci attr = ATTR(windowid); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci ret = mbus_dt_setup_win(mbus, range.cpu_addr, range.size, target, attr); 120862306a36Sopenharmony_ci if (ret < 0) 120962306a36Sopenharmony_ci return ret; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci return 0; 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic void __init mvebu_mbus_get_pcie_resources(struct device_node *np, 121562306a36Sopenharmony_ci struct resource *mem, 121662306a36Sopenharmony_ci struct resource *io) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci u32 reg[2]; 121962306a36Sopenharmony_ci int ret; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci /* 122262306a36Sopenharmony_ci * These are optional, so we make sure that resource_size(x) will 122362306a36Sopenharmony_ci * return 0. 122462306a36Sopenharmony_ci */ 122562306a36Sopenharmony_ci memset(mem, 0, sizeof(struct resource)); 122662306a36Sopenharmony_ci mem->end = -1; 122762306a36Sopenharmony_ci memset(io, 0, sizeof(struct resource)); 122862306a36Sopenharmony_ci io->end = -1; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg)); 123162306a36Sopenharmony_ci if (!ret) { 123262306a36Sopenharmony_ci mem->start = reg[0]; 123362306a36Sopenharmony_ci mem->end = mem->start + reg[1] - 1; 123462306a36Sopenharmony_ci mem->flags = IORESOURCE_MEM; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg)); 123862306a36Sopenharmony_ci if (!ret) { 123962306a36Sopenharmony_ci io->start = reg[0]; 124062306a36Sopenharmony_ci io->end = io->start + reg[1] - 1; 124162306a36Sopenharmony_ci io->flags = IORESOURCE_IO; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ciint __init mvebu_mbus_dt_init(bool is_coherent) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct resource mbuswins_res, sdramwins_res, mbusbridge_res; 124862306a36Sopenharmony_ci struct device_node *np, *controller; 124962306a36Sopenharmony_ci const struct of_device_id *of_id; 125062306a36Sopenharmony_ci const __be32 *prop; 125162306a36Sopenharmony_ci int ret; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci np = of_find_matching_node_and_match(NULL, of_mvebu_mbus_ids, &of_id); 125462306a36Sopenharmony_ci if (!np) { 125562306a36Sopenharmony_ci pr_err("could not find a matching SoC family\n"); 125662306a36Sopenharmony_ci return -ENODEV; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci mbus_state.soc = of_id->data; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci prop = of_get_property(np, "controller", NULL); 126262306a36Sopenharmony_ci if (!prop) { 126362306a36Sopenharmony_ci pr_err("required 'controller' property missing\n"); 126462306a36Sopenharmony_ci return -EINVAL; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci controller = of_find_node_by_phandle(be32_to_cpup(prop)); 126862306a36Sopenharmony_ci if (!controller) { 126962306a36Sopenharmony_ci pr_err("could not find an 'mbus-controller' node\n"); 127062306a36Sopenharmony_ci return -ENODEV; 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci if (of_address_to_resource(controller, 0, &mbuswins_res)) { 127462306a36Sopenharmony_ci pr_err("cannot get MBUS register address\n"); 127562306a36Sopenharmony_ci return -EINVAL; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (of_address_to_resource(controller, 1, &sdramwins_res)) { 127962306a36Sopenharmony_ci pr_err("cannot get SDRAM register address\n"); 128062306a36Sopenharmony_ci return -EINVAL; 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* 128462306a36Sopenharmony_ci * Set the resource to 0 so that it can be left unmapped by 128562306a36Sopenharmony_ci * mvebu_mbus_common_init() if the DT doesn't carry the 128662306a36Sopenharmony_ci * necessary information. This is needed to preserve backward 128762306a36Sopenharmony_ci * compatibility. 128862306a36Sopenharmony_ci */ 128962306a36Sopenharmony_ci memset(&mbusbridge_res, 0, sizeof(mbusbridge_res)); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (mbus_state.soc->has_mbus_bridge) { 129262306a36Sopenharmony_ci if (of_address_to_resource(controller, 2, &mbusbridge_res)) 129362306a36Sopenharmony_ci pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n"); 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci mbus_state.hw_io_coherency = is_coherent; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* Get optional pcie-{mem,io}-aperture properties */ 129962306a36Sopenharmony_ci mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, 130062306a36Sopenharmony_ci &mbus_state.pcie_io_aperture); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci ret = mvebu_mbus_common_init(&mbus_state, 130362306a36Sopenharmony_ci mbuswins_res.start, 130462306a36Sopenharmony_ci resource_size(&mbuswins_res), 130562306a36Sopenharmony_ci sdramwins_res.start, 130662306a36Sopenharmony_ci resource_size(&sdramwins_res), 130762306a36Sopenharmony_ci mbusbridge_res.start, 130862306a36Sopenharmony_ci resource_size(&mbusbridge_res), 130962306a36Sopenharmony_ci is_coherent); 131062306a36Sopenharmony_ci if (ret) 131162306a36Sopenharmony_ci return ret; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* Setup statically declared windows in the DT */ 131462306a36Sopenharmony_ci return mbus_dt_setup(&mbus_state, np); 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci#endif 1317