| /* |
| * Carsten Langgaard, carstenl@mips.com |
| * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. |
| * Portions copyright (C) 2009 Cisco Systems, Inc. |
| * |
| * This program is free software; you can distribute it and/or modify it |
| * under the terms of the GNU General Public License (Version 2) as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. |
| * |
| * Apparently originally from arch/mips/malta-memory.c. Modified to work |
| * with the PowerTV bootloader. |
| */ |
| #include <linux/init.h> |
| #include <linux/mm.h> |
| #include <linux/bootmem.h> |
| #include <linux/pfn.h> |
| #include <linux/string.h> |
| |
| #include <asm/bootinfo.h> |
| #include <asm/page.h> |
| #include <asm/sections.h> |
| |
| #include <asm/mips-boards/prom.h> |
| #include <asm/mach-powertv/asic.h> |
| #include <asm/mach-powertv/ioremap.h> |
| |
| #include "init.h" |
| |
| /* Memory constants */ |
| #define KIBIBYTE(n) ((n) * 1024) /* Number of kibibytes */ |
| #define MEBIBYTE(n) ((n) * KIBIBYTE(1024)) /* Number of mebibytes */ |
| #define DEFAULT_MEMSIZE MEBIBYTE(128) /* If no memsize provided */ |
| |
| #define BLDR_SIZE KIBIBYTE(256) /* Memory reserved for bldr */ |
| #define RV_SIZE MEBIBYTE(4) /* Size of reset vector */ |
| |
| #define LOW_MEM_END 0x20000000 /* Highest low memory address */ |
| #define BLDR_ALIAS 0x10000000 /* Bootloader address */ |
| #define RV_PHYS 0x1fc00000 /* Reset vector address */ |
| #define LOW_RAM_END RV_PHYS /* End of real RAM in low mem */ |
| |
| /* |
| * Very low-level conversion from processor physical address to device |
| * DMA address for the first bank of memory. |
| */ |
| #define PHYS_TO_DMA(paddr) ((paddr) + (CONFIG_LOW_RAM_DMA - LOW_RAM_ALIAS)) |
| |
| unsigned long ptv_memsize; |
| |
| /* |
| * struct low_mem_reserved - Items in low memory that are reserved |
| * @start: Physical address of item |
| * @size: Size, in bytes, of this item |
| * @is_aliased: True if this is RAM aliased from another location. If false, |
| * it is something other than aliased RAM and the RAM in the |
| * unaliased address is still visible outside of low memory. |
| */ |
| struct low_mem_reserved { |
| phys_addr_t start; |
| phys_addr_t size; |
| bool is_aliased; |
| }; |
| |
| /* |
| * Must be in ascending address order |
| */ |
| struct low_mem_reserved low_mem_reserved[] = { |
| {BLDR_ALIAS, BLDR_SIZE, true}, /* Bootloader RAM */ |
| {RV_PHYS, RV_SIZE, false}, /* Reset vector */ |
| }; |
| |
| /* |
| * struct mem_layout - layout of a piece of the system RAM |
| * @phys: Physical address of the start of this piece of RAM. This is the |
| * address at which both the processor and I/O devices see the |
| * RAM. |
| * @alias: Alias of this piece of memory in order to make it appear in |
| * the low memory part of the processor's address space. I/O |
| * devices don't see anything here. |
| * @size: Size, in bytes, of this piece of RAM |
| */ |
| struct mem_layout { |
| phys_addr_t phys; |
| phys_addr_t alias; |
| phys_addr_t size; |
| }; |
| |
| /* |
| * struct mem_layout_list - list descriptor for layouts of system RAM pieces |
| * @family: Specifies the family being described |
| * @n: Number of &struct mem_layout elements |
| * @layout: Pointer to the list of &mem_layout structures |
| */ |
| struct mem_layout_list { |
| enum family_type family; |
| size_t n; |
| struct mem_layout *layout; |
| }; |
| |
| static struct mem_layout f1500_layout[] = { |
| {0x20000000, 0x10000000, MEBIBYTE(256)}, |
| }; |
| |
| static struct mem_layout f4500_layout[] = { |
| {0x40000000, 0x10000000, MEBIBYTE(256)}, |
| {0x20000000, 0x20000000, MEBIBYTE(32)}, |
| }; |
| |
| static struct mem_layout f8500_layout[] = { |
| {0x40000000, 0x10000000, MEBIBYTE(256)}, |
| {0x20000000, 0x20000000, MEBIBYTE(32)}, |
| {0x30000000, 0x30000000, MEBIBYTE(32)}, |
| }; |
| |
| static struct mem_layout fx600_layout[] = { |
| {0x20000000, 0x10000000, MEBIBYTE(256)}, |
| {0x60000000, 0x60000000, MEBIBYTE(128)}, |
| }; |
| |
| static struct mem_layout_list layout_list[] = { |
| {FAMILY_1500, ARRAY_SIZE(f1500_layout), f1500_layout}, |
| {FAMILY_1500VZE, ARRAY_SIZE(f1500_layout), f1500_layout}, |
| {FAMILY_1500VZF, ARRAY_SIZE(f1500_layout), f1500_layout}, |
| {FAMILY_4500, ARRAY_SIZE(f4500_layout), f4500_layout}, |
| {FAMILY_8500, ARRAY_SIZE(f8500_layout), f8500_layout}, |
| {FAMILY_8500RNG, ARRAY_SIZE(f8500_layout), f8500_layout}, |
| {FAMILY_4600, ARRAY_SIZE(fx600_layout), fx600_layout}, |
| {FAMILY_4600VZA, ARRAY_SIZE(fx600_layout), fx600_layout}, |
| {FAMILY_8600, ARRAY_SIZE(fx600_layout), fx600_layout}, |
| {FAMILY_8600VZB, ARRAY_SIZE(fx600_layout), fx600_layout}, |
| }; |
| |
| /* If we can't determine the layout, use this */ |
| static struct mem_layout default_layout[] = { |
| {0x20000000, 0x10000000, MEBIBYTE(128)}, |
| }; |
| |
| /** |
| * register_non_ram - register low memory not available for RAM usage |
| */ |
| static __init void register_non_ram(void) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(low_mem_reserved); i++) |
| add_memory_region(low_mem_reserved[i].start, |
| low_mem_reserved[i].size, BOOT_MEM_RESERVED); |
| } |
| |
| /** |
| * get_memsize - get the size of memory as a single bank |
| */ |
| static phys_addr_t get_memsize(void) |
| { |
| static char cmdline[COMMAND_LINE_SIZE] __initdata; |
| phys_addr_t memsize = 0; |
| char *memsize_str; |
| char *ptr; |
| |
| /* Check the command line first for a memsize directive */ |
| strcpy(cmdline, arcs_cmdline); |
| ptr = strstr(cmdline, "memsize="); |
| if (ptr && (ptr != cmdline) && (*(ptr - 1) != ' ')) |
| ptr = strstr(ptr, " memsize="); |
| |
| if (ptr) { |
| memsize = memparse(ptr + 8, &ptr); |
| } else { |
| /* otherwise look in the environment */ |
| memsize_str = prom_getenv("memsize"); |
| |
| if (memsize_str != NULL) { |
| pr_info("prom memsize = %s\n", memsize_str); |
| memsize = simple_strtol(memsize_str, NULL, 0); |
| } |
| |
| if (memsize == 0) { |
| if (_prom_memsize != 0) { |
| memsize = _prom_memsize; |
| pr_info("_prom_memsize = 0x%x\n", memsize); |
| /* add in memory that the bootloader doesn't |
| * report */ |
| memsize += BLDR_SIZE; |
| } else { |
| memsize = DEFAULT_MEMSIZE; |
| pr_info("Memsize not passed by bootloader, " |
| "defaulting to 0x%x\n", memsize); |
| } |
| } |
| } |
| |
| return memsize; |
| } |
| |
| /** |
| * register_low_ram - register an aliased section of RAM |
| * @p: Alias address of memory |
| * @n: Number of bytes in this section of memory |
| * |
| * Returns the number of bytes registered |
| * |
| */ |
| static __init phys_addr_t register_low_ram(phys_addr_t p, phys_addr_t n) |
| { |
| phys_addr_t s; |
| int i; |
| phys_addr_t orig_n; |
| |
| orig_n = n; |
| |
| BUG_ON(p + n > RV_PHYS); |
| |
| for (i = 0; n != 0 && i < ARRAY_SIZE(low_mem_reserved); i++) { |
| phys_addr_t start; |
| phys_addr_t size; |
| |
| start = low_mem_reserved[i].start; |
| size = low_mem_reserved[i].size; |
| |
| /* Handle memory before this low memory section */ |
| if (p < start) { |
| phys_addr_t s; |
| s = min(n, start - p); |
| add_memory_region(p, s, BOOT_MEM_RAM); |
| p += s; |
| n -= s; |
| } |
| |
| /* Handle the low memory section itself. If it's aliased, |
| * we reduce the number of byes left, but if not, the RAM |
| * is available elsewhere and we don't reduce the number of |
| * bytes remaining. */ |
| if (p == start) { |
| if (low_mem_reserved[i].is_aliased) { |
| s = min(n, size); |
| n -= s; |
| p += s; |
| } else |
| p += n; |
| } |
| } |
| |
| return orig_n - n; |
| } |
| |
| /* |
| * register_ram - register real RAM |
| * @p: Address of memory as seen by devices |
| * @alias: If the memory is seen at an additional address by the processor, |
| * this will be the address, otherwise it is the same as @p. |
| * @n: Number of bytes in this section of memory |
| */ |
| static __init void register_ram(phys_addr_t p, phys_addr_t alias, |
| phys_addr_t n) |
| { |
| /* |
| * If some or all of this memory has an alias, break it into the |
| * aliased and non-aliased portion. |
| */ |
| if (p != alias) { |
| phys_addr_t alias_size; |
| phys_addr_t registered; |
| |
| alias_size = min(n, LOW_RAM_END - alias); |
| registered = register_low_ram(alias, alias_size); |
| ioremap_add_map(alias, p, n); |
| n -= registered; |
| p += registered; |
| } |
| |
| #ifdef CONFIG_HIGHMEM |
| if (n != 0) { |
| add_memory_region(p, n, BOOT_MEM_RAM); |
| ioremap_add_map(p, p, n); |
| } |
| #endif |
| } |
| |
| /** |
| * register_address_space - register things in the address space |
| * @memsize: Number of bytes of RAM installed |
| * |
| * Takes the given number of bytes of RAM and registers as many of the regions, |
| * or partial regions, as it can. So, the default configuration might have |
| * two regions with 256 MiB each. If the memsize passed in on the command line |
| * is 384 MiB, it will register the first region with 256 MiB and the second |
| * with 128 MiB. |
| */ |
| static __init void register_address_space(phys_addr_t memsize) |
| { |
| int i; |
| phys_addr_t size; |
| size_t n; |
| struct mem_layout *layout; |
| enum family_type family; |
| |
| /* |
| * Register all of the things that aren't available to the kernel as |
| * memory. |
| */ |
| register_non_ram(); |
| |
| /* Find the appropriate memory description */ |
| family = platform_get_family(); |
| |
| for (i = 0; i < ARRAY_SIZE(layout_list); i++) { |
| if (layout_list[i].family == family) |
| break; |
| } |
| |
| if (i == ARRAY_SIZE(layout_list)) { |
| n = ARRAY_SIZE(default_layout); |
| layout = default_layout; |
| } else { |
| n = layout_list[i].n; |
| layout = layout_list[i].layout; |
| } |
| |
| for (i = 0; memsize != 0 && i < n; i++) { |
| size = min(memsize, layout[i].size); |
| register_ram(layout[i].phys, layout[i].alias, size); |
| memsize -= size; |
| } |
| } |
| |
| void __init prom_meminit(void) |
| { |
| ptv_memsize = get_memsize(); |
| register_address_space(ptv_memsize); |
| } |
| |
| void __init prom_free_prom_memory(void) |
| { |
| unsigned long addr; |
| int i; |
| |
| for (i = 0; i < boot_mem_map.nr_map; i++) { |
| if (boot_mem_map.map[i].type != BOOT_MEM_ROM_DATA) |
| continue; |
| |
| addr = boot_mem_map.map[i].addr; |
| free_init_pages("prom memory", |
| addr, addr + boot_mem_map.map[i].size); |
| } |
| } |