/* * 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/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); } }