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