1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Address map functions for Marvell EBU SoCs (Kirkwood, Armada 4 * 370/XP, Dove, Orion5x and MV78xx0) 5 * 6 * Ported from the Barebox version to U-Boot by: 7 * Stefan Roese <sr@denx.de> 8 * 9 * The Barebox version is: 10 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 11 * 12 * based on mbus driver from Linux 13 * (C) Copyright 2008 Marvell Semiconductor 14 * 15 * The Marvell EBU SoCs have a configurable physical address space: 16 * the physical address at which certain devices (PCIe, NOR, NAND, 17 * etc.) sit can be configured. The configuration takes place through 18 * two sets of registers: 19 * 20 * - One to configure the access of the CPU to the devices. Depending 21 * on the families, there are between 8 and 20 configurable windows, 22 * each can be use to create a physical memory window that maps to a 23 * specific device. Devices are identified by a tuple (target, 24 * attribute). 25 * 26 * - One to configure the access to the CPU to the SDRAM. There are 27 * either 2 (for Dove) or 4 (for other families) windows to map the 28 * SDRAM into the physical address space. 29 * 30 * This driver: 31 * 32 * - Reads out the SDRAM address decoding windows at initialization 33 * time, and fills the mbus_dram_info structure with these 34 * informations. The exported function mv_mbus_dram_info() allow 35 * device drivers to get those informations related to the SDRAM 36 * address decoding windows. This is because devices also have their 37 * own windows (configured through registers that are part of each 38 * device register space), and therefore the drivers for Marvell 39 * devices have to configure those device -> SDRAM windows to ensure 40 * that DMA works properly. 41 * 42 * - Provides an API for platform code or device drivers to 43 * dynamically add or remove address decoding windows for the CPU -> 44 * device accesses. This API is mvebu_mbus_add_window_by_id(), 45 * mvebu_mbus_add_window_remap_by_id() and 46 * mvebu_mbus_del_window(). 47 */ 48 49#include <common.h> 50#include <malloc.h> 51#include <linux/bitops.h> 52#include <linux/errno.h> 53#include <asm/io.h> 54#include <asm/arch/cpu.h> 55#include <asm/arch/soc.h> 56#include <linux/log2.h> 57#include <linux/mbus.h> 58 59/* DDR target is the same on all platforms */ 60#define TARGET_DDR 0 61 62/* CPU Address Decode Windows registers */ 63#define WIN_CTRL_OFF 0x0000 64#define WIN_CTRL_ENABLE BIT(0) 65#define WIN_CTRL_TGT_MASK 0xf0 66#define WIN_CTRL_TGT_SHIFT 4 67#define WIN_CTRL_ATTR_MASK 0xff00 68#define WIN_CTRL_ATTR_SHIFT 8 69#define WIN_CTRL_SIZE_MASK 0xffff0000 70#define WIN_CTRL_SIZE_SHIFT 16 71#define WIN_BASE_OFF 0x0004 72#define WIN_BASE_LOW 0xffff0000 73#define WIN_BASE_HIGH 0xf 74#define WIN_REMAP_LO_OFF 0x0008 75#define WIN_REMAP_LOW 0xffff0000 76#define WIN_REMAP_HI_OFF 0x000c 77 78#define ATTR_HW_COHERENCY (0x1 << 4) 79 80#define DDR_BASE_CS_OFF(n) (0x0000 + ((n) << 3)) 81#define DDR_BASE_CS_HIGH_MASK 0xf 82#define DDR_BASE_CS_LOW_MASK 0xff000000 83#define DDR_SIZE_CS_OFF(n) (0x0004 + ((n) << 3)) 84#define DDR_SIZE_ENABLED BIT(0) 85#define DDR_SIZE_CS_MASK 0x1c 86#define DDR_SIZE_CS_SHIFT 2 87#define DDR_SIZE_MASK 0xff000000 88 89#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) 90 91static struct mbus_dram_target_info mbus_dram_info 92 __section(".data"); 93 94#if defined(CONFIG_ARCH_MVEBU) 95 #define MVEBU_MBUS_NUM_WINS 20 96 #define MVEBU_MBUS_NUM_REMAPPABLE_WINS 8 97 #define MVEBU_MBUS_WIN_CFG_OFFSET(win) armada_370_xp_mbus_win_offset(win) 98#elif defined(CONFIG_ARCH_KIRKWOOD) 99 #define MVEBU_MBUS_NUM_WINS 8 100 #define MVEBU_MBUS_NUM_REMAPPABLE_WINS 4 101 #define MVEBU_MBUS_WIN_CFG_OFFSET(win) orion5x_mbus_win_offset(win) 102#else 103 #error "No supported architecture" 104#endif 105 106static unsigned int armada_370_xp_mbus_win_offset(int win); 107static unsigned int orion5x_mbus_win_offset(int win); 108 109/* 110 * Functions to manipulate the address decoding windows 111 */ 112 113static void mvebu_mbus_read_window(int win, int *enabled, u64 *base, 114 u32 *size, u8 *target, u8 *attr, 115 u64 *remap) 116{ 117 void __iomem *addr = (void __iomem *)MVEBU_CPU_WIN_BASE + 118 MVEBU_MBUS_WIN_CFG_OFFSET(win); 119 u32 basereg = readl(addr + WIN_BASE_OFF); 120 u32 ctrlreg = readl(addr + WIN_CTRL_OFF); 121 122 if (!(ctrlreg & WIN_CTRL_ENABLE)) { 123 *enabled = 0; 124 return; 125 } 126 127 *enabled = 1; 128 *base = ((u64)basereg & WIN_BASE_HIGH) << 32; 129 *base |= (basereg & WIN_BASE_LOW); 130 *size = (ctrlreg | ~WIN_CTRL_SIZE_MASK) + 1; 131 132 if (target) 133 *target = (ctrlreg & WIN_CTRL_TGT_MASK) >> WIN_CTRL_TGT_SHIFT; 134 135 if (attr) 136 *attr = (ctrlreg & WIN_CTRL_ATTR_MASK) >> WIN_CTRL_ATTR_SHIFT; 137 138 if (remap) { 139 if (win < MVEBU_MBUS_NUM_REMAPPABLE_WINS) { 140 u32 remap_low = readl(addr + WIN_REMAP_LO_OFF); 141 u32 remap_hi = readl(addr + WIN_REMAP_HI_OFF); 142 *remap = ((u64)remap_hi << 32) | remap_low; 143 } else { 144 *remap = 0; 145 } 146 } 147} 148 149static void mvebu_mbus_disable_window(int win) 150{ 151 void __iomem *addr; 152 153 addr = (void __iomem *)MVEBU_CPU_WIN_BASE + MVEBU_MBUS_WIN_CFG_OFFSET(win); 154 155 writel(0, addr + WIN_BASE_OFF); 156 writel(0, addr + WIN_CTRL_OFF); 157 if (win < MVEBU_MBUS_NUM_REMAPPABLE_WINS) { 158 writel(0, addr + WIN_REMAP_LO_OFF); 159 writel(0, addr + WIN_REMAP_HI_OFF); 160 } 161} 162 163/* Checks whether the given window number is available */ 164static int mvebu_mbus_window_is_free(const int win) 165{ 166 void __iomem *addr = (void __iomem *)MVEBU_CPU_WIN_BASE + 167 MVEBU_MBUS_WIN_CFG_OFFSET(win); 168 u32 ctrl = readl(addr + WIN_CTRL_OFF); 169 return !(ctrl & WIN_CTRL_ENABLE); 170} 171 172/* 173 * Checks whether the given (base, base+size) area doesn't overlap an 174 * existing region 175 */ 176static int mvebu_mbus_window_conflicts(phys_addr_t base, size_t size, 177 u8 target, u8 attr) 178{ 179 u64 end = (u64)base + size; 180 int win; 181 182 for (win = 0; win < MVEBU_MBUS_NUM_WINS; win++) { 183 u64 wbase, wend; 184 u32 wsize; 185 u8 wtarget, wattr; 186 int enabled; 187 188 mvebu_mbus_read_window(win, 189 &enabled, &wbase, &wsize, 190 &wtarget, &wattr, NULL); 191 192 if (!enabled) 193 continue; 194 195 wend = wbase + wsize; 196 197 /* 198 * Check if the current window overlaps with the 199 * proposed physical range 200 */ 201 if ((u64)base < wend && end > wbase) 202 return 0; 203 204 /* 205 * Check if target/attribute conflicts 206 */ 207 if (target == wtarget && attr == wattr) 208 return 0; 209 } 210 211 return 1; 212} 213 214static int mvebu_mbus_find_window(phys_addr_t base, size_t size) 215{ 216 int win; 217 218 for (win = 0; win < MVEBU_MBUS_NUM_WINS; win++) { 219 u64 wbase; 220 u32 wsize; 221 int enabled; 222 223 mvebu_mbus_read_window(win, 224 &enabled, &wbase, &wsize, 225 NULL, NULL, NULL); 226 227 if (!enabled) 228 continue; 229 230 if (base == wbase && size == wsize) 231 return win; 232 } 233 234 return -ENODEV; 235} 236 237static int mvebu_mbus_setup_window(int win, phys_addr_t base, size_t size, 238 phys_addr_t remap, u8 target, 239 u8 attr) 240{ 241 void __iomem *addr = (void __iomem *)MVEBU_CPU_WIN_BASE + 242 MVEBU_MBUS_WIN_CFG_OFFSET(win); 243 u32 ctrl, remap_addr; 244 245 ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | 246 (attr << WIN_CTRL_ATTR_SHIFT) | 247 (target << WIN_CTRL_TGT_SHIFT) | 248 WIN_CTRL_ENABLE; 249 250 writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF); 251 writel(ctrl, addr + WIN_CTRL_OFF); 252 if (win < MVEBU_MBUS_NUM_REMAPPABLE_WINS) { 253 if (remap == MVEBU_MBUS_NO_REMAP) 254 remap_addr = base; 255 else 256 remap_addr = remap; 257 writel(remap_addr & WIN_REMAP_LOW, addr + WIN_REMAP_LO_OFF); 258 writel(0, addr + WIN_REMAP_HI_OFF); 259 } 260 261 return 0; 262} 263 264static int mvebu_mbus_alloc_window(phys_addr_t base, size_t size, 265 phys_addr_t remap, u8 target, 266 u8 attr) 267{ 268 int win; 269 270 if (remap == MVEBU_MBUS_NO_REMAP) { 271 for (win = MVEBU_MBUS_NUM_REMAPPABLE_WINS; 272 win < MVEBU_MBUS_NUM_WINS; win++) 273 if (mvebu_mbus_window_is_free(win)) 274 return mvebu_mbus_setup_window(win, base, 275 size, remap, 276 target, attr); 277 } 278 279 280 for (win = 0; win < MVEBU_MBUS_NUM_WINS; win++) 281 if (mvebu_mbus_window_is_free(win)) 282 return mvebu_mbus_setup_window(win, base, size, 283 remap, target, attr); 284 285 return -ENOMEM; 286} 287 288/* 289 * SoC-specific functions and definitions 290 */ 291 292static unsigned int __maybe_unused armada_370_xp_mbus_win_offset(int win) 293{ 294 /* The register layout is a bit annoying and the below code 295 * tries to cope with it. 296 * - At offset 0x0, there are the registers for the first 8 297 * windows, with 4 registers of 32 bits per window (ctrl, 298 * base, remap low, remap high) 299 * - Then at offset 0x80, there is a hole of 0x10 bytes for 300 * the internal registers base address and internal units 301 * sync barrier register. 302 * - Then at offset 0x90, there the registers for 12 303 * windows, with only 2 registers of 32 bits per window 304 * (ctrl, base). 305 */ 306 if (win < 8) 307 return win << 4; 308 else 309 return 0x90 + ((win - 8) << 3); 310} 311 312static unsigned int __maybe_unused orion5x_mbus_win_offset(int win) 313{ 314 return win << 4; 315} 316 317static void mvebu_mbus_default_setup_cpu_target(void) 318{ 319 int i; 320 int cs; 321 322 mbus_dram_info.mbus_dram_target_id = TARGET_DDR; 323 324 for (i = 0, cs = 0; i < 4; i++) { 325 u32 base = readl((void __iomem *)MVEBU_SDRAM_BASE + DDR_BASE_CS_OFF(i)); 326 u32 size = readl((void __iomem *)MVEBU_SDRAM_BASE + DDR_SIZE_CS_OFF(i)); 327 328 /* 329 * We only take care of entries for which the chip 330 * select is enabled, and that don't have high base 331 * address bits set (devices can only access the first 332 * 32 bits of the memory). 333 */ 334 if ((size & DDR_SIZE_ENABLED) && 335 !(base & DDR_BASE_CS_HIGH_MASK)) { 336 struct mbus_dram_window *w; 337 338 w = &mbus_dram_info.cs[cs++]; 339 w->cs_index = i; 340 w->mbus_attr = 0xf & ~(1 << i); 341 w->base = base & DDR_BASE_CS_LOW_MASK; 342 w->size = (size | ~DDR_SIZE_MASK) + 1; 343 } 344 } 345 mbus_dram_info.num_cs = cs; 346 347#if defined(CONFIG_ARMADA_MSYS) 348 /* Disable MBUS Err Prop - in order to avoid data aborts */ 349 clrbits_le32((void __iomem *)MVEBU_CPU_WIN_BASE + 0x200, BIT(8)); 350#endif 351} 352 353/* 354 * Public API of the driver 355 */ 356const struct mbus_dram_target_info *mvebu_mbus_dram_info(void) 357{ 358 return &mbus_dram_info; 359} 360 361int mvebu_mbus_add_window_remap_by_id(unsigned int target, 362 unsigned int attribute, 363 phys_addr_t base, size_t size, 364 phys_addr_t remap) 365{ 366 if (!mvebu_mbus_window_conflicts(base, size, target, attribute)) { 367 printf("Cannot add window '%x:%x', conflicts with another window\n", 368 target, attribute); 369 return -EINVAL; 370 } 371 372 return mvebu_mbus_alloc_window(base, size, remap, target, attribute); 373} 374 375int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, 376 phys_addr_t base, size_t size) 377{ 378 return mvebu_mbus_add_window_remap_by_id(target, attribute, base, 379 size, MVEBU_MBUS_NO_REMAP); 380} 381 382int mvebu_mbus_del_window(phys_addr_t base, size_t size) 383{ 384 int win; 385 386 win = mvebu_mbus_find_window(base, size); 387 if (win < 0) 388 return win; 389 390 mvebu_mbus_disable_window(win); 391 return 0; 392} 393 394#ifndef CONFIG_ARCH_KIRKWOOD 395static void mvebu_mbus_get_lowest_base(phys_addr_t *base) 396{ 397 int win; 398 *base = 0xffffffff; 399 400 for (win = 0; win < MVEBU_MBUS_NUM_WINS; win++) { 401 u64 wbase; 402 u32 wsize; 403 u8 wtarget, wattr; 404 int enabled; 405 406 mvebu_mbus_read_window(win, 407 &enabled, &wbase, &wsize, 408 &wtarget, &wattr, NULL); 409 410 if (!enabled) 411 continue; 412 413 if (wbase < *base) 414 *base = wbase; 415 } 416} 417 418static void mvebu_config_mbus_bridge(void) 419{ 420 phys_addr_t base; 421 u32 val; 422 u32 size; 423 424 /* Set MBUS bridge base/ctrl */ 425 mvebu_mbus_get_lowest_base(&base); 426 427 size = 0xffffffff - base + 1; 428 if (!is_power_of_2(size)) { 429 /* Round up to next power of 2 */ 430 size = 1 << (ffs(base) + 1); 431 base = 0xffffffff - size + 1; 432 } 433 434 /* Now write base and size */ 435 writel(base, MBUS_BRIDGE_WIN_BASE_REG); 436 /* Align window size to 64KiB */ 437 val = (size / (64 << 10)) - 1; 438 writel((val << 16) | 0x1, MBUS_BRIDGE_WIN_CTRL_REG); 439} 440#endif 441 442int mbus_dt_setup_win(u32 base, u32 size, u8 target, u8 attr) 443{ 444 if (!mvebu_mbus_window_conflicts(base, size, target, attr)) { 445 printf("Cannot add window '%04x:%04x', conflicts with another window\n", 446 target, attr); 447 return -EBUSY; 448 } 449 450 /* 451 * In U-Boot we first try to add the mbus window to the remap windows. 452 * If this fails, lets try to add the windows to the non-remap windows. 453 */ 454 if (mvebu_mbus_alloc_window(base, size, base, target, attr)) { 455 if (mvebu_mbus_alloc_window(base, size, 456 MVEBU_MBUS_NO_REMAP, target, attr)) 457 return -ENOMEM; 458 } 459 460#ifndef CONFIG_ARCH_KIRKWOOD 461 /* 462 * Re-configure the mbus bridge registers each time this function 463 * is called. Since it may get called from the board code in 464 * later boot stages as well. 465 */ 466 mvebu_config_mbus_bridge(); 467#endif 468 469 return 0; 470} 471 472int mvebu_mbus_probe(const struct mbus_win windows[], int count) 473{ 474 int win; 475 int ret; 476 int i; 477 478 for (win = 0; win < MVEBU_MBUS_NUM_WINS; win++) 479 mvebu_mbus_disable_window(win); 480 481 mvebu_mbus_default_setup_cpu_target(); 482 483 /* Setup statically declared windows in the DT */ 484 for (i = 0; i < count; i++) { 485 u32 base, size; 486 u8 target, attr; 487 488 target = windows[i].target; 489 attr = windows[i].attr; 490 base = windows[i].base; 491 size = windows[i].size; 492 ret = mbus_dt_setup_win(base, size, target, attr); 493 if (ret < 0) 494 return ret; 495 } 496 497 return 0; 498} 499