| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * arch/v850/kernel/setup.c -- Arch-dependent initialization functions | 
|  | 3 | * | 
| Miles Bader | a268cef | 2006-07-30 03:03:28 -0700 | [diff] [blame] | 4 | *  Copyright (C) 2001,02,03,05,06  NEC Electronics Corporation | 
|  | 5 | *  Copyright (C) 2001,02,03,05,06  Miles Bader <miles@gnu.org> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6 | * | 
|  | 7 | * This file is subject to the terms and conditions of the GNU General | 
|  | 8 | * Public License.  See the file COPYING in the main directory of this | 
|  | 9 | * archive for more details. | 
|  | 10 | * | 
|  | 11 | * Written by Miles Bader <miles@gnu.org> | 
|  | 12 | */ | 
|  | 13 |  | 
|  | 14 | #include <linux/mm.h> | 
|  | 15 | #include <linux/bootmem.h> | 
|  | 16 | #include <linux/swap.h>		/* we don't have swap, but for nr_free_pages */ | 
|  | 17 | #include <linux/irq.h> | 
|  | 18 | #include <linux/reboot.h> | 
|  | 19 | #include <linux/personality.h> | 
|  | 20 | #include <linux/major.h> | 
|  | 21 | #include <linux/root_dev.h> | 
|  | 22 | #include <linux/mtd/mtd.h> | 
|  | 23 | #include <linux/init.h> | 
|  | 24 |  | 
|  | 25 | #include <asm/irq.h> | 
|  | 26 | #include <asm/setup.h> | 
|  | 27 |  | 
|  | 28 | #include "mach.h" | 
|  | 29 |  | 
|  | 30 | /* These symbols are all defined in the linker map to delineate various | 
|  | 31 | statically allocated regions of memory.  */ | 
|  | 32 |  | 
|  | 33 | extern char _intv_start, _intv_end; | 
|  | 34 | /* `kram' is only used if the kernel uses part of normal user RAM.  */ | 
|  | 35 | extern char _kram_start __attribute__ ((__weak__)); | 
|  | 36 | extern char _kram_end __attribute__ ((__weak__)); | 
|  | 37 | extern char _init_start, _init_end; | 
|  | 38 | extern char _bootmap; | 
|  | 39 | extern char _stext, _etext, _sdata, _edata, _sbss, _ebss; | 
|  | 40 | /* Many platforms use an embedded root image.  */ | 
|  | 41 | extern char _root_fs_image_start __attribute__ ((__weak__)); | 
|  | 42 | extern char _root_fs_image_end __attribute__ ((__weak__)); | 
|  | 43 |  | 
|  | 44 |  | 
|  | 45 | char command_line[COMMAND_LINE_SIZE]; | 
|  | 46 |  | 
|  | 47 | /* Memory not used by the kernel.  */ | 
|  | 48 | static unsigned long total_ram_pages; | 
|  | 49 |  | 
|  | 50 | /* System RAM.  */ | 
|  | 51 | static unsigned long ram_start = 0, ram_len = 0; | 
|  | 52 |  | 
|  | 53 |  | 
|  | 54 | #define ADDR_TO_PAGE_UP(x)   ((((unsigned long)x) + PAGE_SIZE-1) >> PAGE_SHIFT) | 
|  | 55 | #define ADDR_TO_PAGE(x)	     (((unsigned long)x) >> PAGE_SHIFT) | 
|  | 56 | #define PAGE_TO_ADDR(x)	     (((unsigned long)x) << PAGE_SHIFT) | 
|  | 57 |  | 
|  | 58 | static void init_mem_alloc (unsigned long ram_start, unsigned long ram_len); | 
|  | 59 |  | 
|  | 60 | void set_mem_root (void *addr, size_t len, char *cmd_line); | 
|  | 61 |  | 
|  | 62 |  | 
|  | 63 | void __init setup_arch (char **cmdline) | 
|  | 64 | { | 
|  | 65 | /* Keep a copy of command line */ | 
|  | 66 | *cmdline = command_line; | 
|  | 67 | memcpy (saved_command_line, command_line, COMMAND_LINE_SIZE); | 
|  | 68 | saved_command_line[COMMAND_LINE_SIZE - 1] = '\0'; | 
|  | 69 |  | 
|  | 70 | console_verbose (); | 
|  | 71 |  | 
|  | 72 | init_mm.start_code = (unsigned long) &_stext; | 
|  | 73 | init_mm.end_code = (unsigned long) &_etext; | 
|  | 74 | init_mm.end_data = (unsigned long) &_edata; | 
|  | 75 | init_mm.brk = (unsigned long) &_kram_end; | 
|  | 76 |  | 
|  | 77 | /* Find out what mem this machine has.  */ | 
|  | 78 | mach_get_physical_ram (&ram_start, &ram_len); | 
|  | 79 | /* ... and tell the kernel about it.  */ | 
|  | 80 | init_mem_alloc (ram_start, ram_len); | 
|  | 81 |  | 
|  | 82 | printk (KERN_INFO "CPU: %s\nPlatform: %s\n", | 
|  | 83 | CPU_MODEL_LONG, PLATFORM_LONG); | 
|  | 84 |  | 
|  | 85 | /* do machine-specific setups.  */ | 
|  | 86 | mach_setup (cmdline); | 
|  | 87 |  | 
|  | 88 | #ifdef CONFIG_MTD | 
|  | 89 | if (!ROOT_DEV && &_root_fs_image_end > &_root_fs_image_start) | 
|  | 90 | set_mem_root (&_root_fs_image_start, | 
|  | 91 | &_root_fs_image_end - &_root_fs_image_start, | 
|  | 92 | *cmdline); | 
|  | 93 | #endif | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 | void __init trap_init (void) | 
|  | 97 | { | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | #ifdef CONFIG_MTD | 
| Miles Bader | 023239c | 2005-09-02 15:13:30 +0900 | [diff] [blame] | 101 |  | 
|  | 102 | /* From drivers/mtd/devices/slram.c */ | 
|  | 103 | #define SLRAM_BLK_SZ 0x4000 | 
|  | 104 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | /* Set the root filesystem to be the given memory region. | 
|  | 106 | Some parameter may be appended to CMD_LINE.  */ | 
|  | 107 | void set_mem_root (void *addr, size_t len, char *cmd_line) | 
|  | 108 | { | 
| Miles Bader | 023239c | 2005-09-02 15:13:30 +0900 | [diff] [blame] | 109 | /* Some sort of idiocy in MTD means we must supply a length that's | 
|  | 110 | a multiple of SLRAM_BLK_SZ.  We just round up the real length, | 
|  | 111 | as the file system shouldn't attempt to access anything beyond | 
|  | 112 | the end of the image anyway.  */ | 
|  | 113 | len = (((len - 1) + SLRAM_BLK_SZ) / SLRAM_BLK_SZ) * SLRAM_BLK_SZ; | 
|  | 114 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | /* The only way to pass info to the MTD slram driver is via | 
|  | 116 | the command line.  */ | 
|  | 117 | if (*cmd_line) { | 
|  | 118 | cmd_line += strlen (cmd_line); | 
|  | 119 | *cmd_line++ = ' '; | 
|  | 120 | } | 
|  | 121 | sprintf (cmd_line, "slram=root,0x%x,+0x%x", (u32)addr, (u32)len); | 
|  | 122 |  | 
|  | 123 | ROOT_DEV = MKDEV (MTD_BLOCK_MAJOR, 0); | 
|  | 124 | } | 
|  | 125 | #endif | 
|  | 126 |  | 
|  | 127 |  | 
|  | 128 | static void irq_nop (unsigned irq) { } | 
|  | 129 | static unsigned irq_zero (unsigned irq) { return 0; } | 
|  | 130 |  | 
|  | 131 | static void nmi_end (unsigned irq) | 
|  | 132 | { | 
|  | 133 | if (irq != IRQ_NMI (0)) { | 
|  | 134 | printk (KERN_CRIT "NMI %d is unrecoverable; restarting...", | 
|  | 135 | irq - IRQ_NMI (0)); | 
|  | 136 | machine_restart (0); | 
|  | 137 | } | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | static struct hw_interrupt_type nmi_irq_type = { | 
| Thomas Gleixner | aecd456 | 2005-09-10 00:26:43 -0700 | [diff] [blame] | 141 | .typename = "NMI", | 
|  | 142 | .startup = irq_zero,		/* startup */ | 
|  | 143 | .shutdown = irq_nop,		/* shutdown */ | 
|  | 144 | .enable = irq_nop,		/* enable */ | 
|  | 145 | .disable = irq_nop,		/* disable */ | 
|  | 146 | .ack = irq_nop,		/* ack */ | 
|  | 147 | .end = nmi_end,		/* end */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 148 | }; | 
|  | 149 |  | 
|  | 150 | void __init init_IRQ (void) | 
|  | 151 | { | 
|  | 152 | init_irq_handlers (0, NUM_MACH_IRQS, 1, 0); | 
|  | 153 | init_irq_handlers (IRQ_NMI (0), NUM_NMIS, 1, &nmi_irq_type); | 
|  | 154 | mach_init_irqs (); | 
|  | 155 | } | 
|  | 156 |  | 
|  | 157 |  | 
|  | 158 | void __init mem_init (void) | 
|  | 159 | { | 
|  | 160 | max_mapnr = MAP_NR (ram_start + ram_len); | 
|  | 161 |  | 
|  | 162 | num_physpages = ADDR_TO_PAGE (ram_len); | 
|  | 163 |  | 
|  | 164 | total_ram_pages = free_all_bootmem (); | 
|  | 165 |  | 
|  | 166 | printk (KERN_INFO | 
|  | 167 | "Memory: %luK/%luK available" | 
|  | 168 | " (%luK kernel code, %luK data)\n", | 
|  | 169 | PAGE_TO_ADDR (nr_free_pages()) / 1024, | 
|  | 170 | ram_len / 1024, | 
|  | 171 | ((unsigned long)&_etext - (unsigned long)&_stext) / 1024, | 
|  | 172 | ((unsigned long)&_ebss - (unsigned long)&_sdata) / 1024); | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | void free_initmem (void) | 
|  | 176 | { | 
|  | 177 | unsigned long ram_end = ram_start + ram_len; | 
|  | 178 | unsigned long start = PAGE_ALIGN ((unsigned long)(&_init_start)); | 
|  | 179 |  | 
|  | 180 | if (start >= ram_start && start < ram_end) { | 
|  | 181 | unsigned long addr; | 
|  | 182 | unsigned long end = PAGE_ALIGN ((unsigned long)(&_init_end)); | 
|  | 183 |  | 
|  | 184 | if (end > ram_end) | 
|  | 185 | end = ram_end; | 
|  | 186 |  | 
|  | 187 | printk("Freeing unused kernel memory: %ldK freed\n", | 
|  | 188 | (end - start) / 1024); | 
|  | 189 |  | 
|  | 190 | for (addr = start; addr < end; addr += PAGE_SIZE) { | 
|  | 191 | struct page *page = virt_to_page (addr); | 
|  | 192 | ClearPageReserved (page); | 
| Miles Bader | a268cef | 2006-07-30 03:03:28 -0700 | [diff] [blame] | 193 | init_page_count (page); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 194 | __free_page (page); | 
|  | 195 | total_ram_pages++; | 
|  | 196 | } | 
|  | 197 | } | 
|  | 198 | } | 
|  | 199 |  | 
|  | 200 |  | 
|  | 201 | /* Initialize the `bootmem allocator'.  RAM_START and RAM_LEN identify | 
|  | 202 | what RAM may be used.  */ | 
|  | 203 | static void __init | 
|  | 204 | init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len) | 
|  | 205 | { | 
|  | 206 | /* The part of the kernel that's in the same managed RAM space | 
|  | 207 | used for general allocation.  */ | 
|  | 208 | unsigned long kram_start = (unsigned long)&_kram_start; | 
|  | 209 | unsigned long kram_end = (unsigned long)&_kram_end; | 
|  | 210 | /* End of the managed RAM space.  */ | 
|  | 211 | unsigned long ram_end = ram_start + ram_len; | 
|  | 212 | /* Address range of the interrupt vector table.  */ | 
|  | 213 | unsigned long intv_start = (unsigned long)&_intv_start; | 
|  | 214 | unsigned long intv_end = (unsigned long)&_intv_end; | 
|  | 215 | /* True if the interrupt vectors are in the managed RAM area.  */ | 
|  | 216 | int intv_in_ram = (intv_end > ram_start && intv_start < ram_end); | 
|  | 217 | /* True if the interrupt vectors are inside the kernel's RAM.  */ | 
|  | 218 | int intv_in_kram = (intv_end > kram_start && intv_start < kram_end); | 
|  | 219 | /* A pointer to an optional function that reserves platform-specific | 
|  | 220 | memory regions.  We declare the pointer `volatile' to avoid gcc | 
|  | 221 | turning the call into a static call (the problem is that since | 
|  | 222 | it's a weak symbol, a static call may end up trying to reference | 
|  | 223 | the location 0x0, which is not always reachable).  */ | 
|  | 224 | void (*volatile mrb) (void) = mach_reserve_bootmem; | 
|  | 225 | /* The bootmem allocator's allocation bitmap.  */ | 
|  | 226 | unsigned long bootmap = (unsigned long)&_bootmap; | 
|  | 227 | unsigned long bootmap_len; | 
|  | 228 |  | 
|  | 229 | /* Round bootmap location up to next page.  */ | 
|  | 230 | bootmap = PAGE_TO_ADDR (ADDR_TO_PAGE_UP (bootmap)); | 
|  | 231 |  | 
|  | 232 | /* Initialize bootmem allocator.  */ | 
|  | 233 | bootmap_len = init_bootmem_node (NODE_DATA (0), | 
|  | 234 | ADDR_TO_PAGE (bootmap), | 
|  | 235 | ADDR_TO_PAGE (PAGE_OFFSET), | 
|  | 236 | ADDR_TO_PAGE (ram_end)); | 
|  | 237 |  | 
|  | 238 | /* Now make the RAM actually allocatable (it starts out `reserved'). */ | 
|  | 239 | free_bootmem (ram_start, ram_len); | 
|  | 240 |  | 
|  | 241 | if (kram_end > kram_start) | 
|  | 242 | /* Reserve the RAM part of the kernel's address space, so it | 
|  | 243 | doesn't get allocated.  */ | 
|  | 244 | reserve_bootmem (kram_start, kram_end - kram_start); | 
|  | 245 |  | 
|  | 246 | if (intv_in_ram && !intv_in_kram) | 
|  | 247 | /* Reserve the interrupt vector space.  */ | 
|  | 248 | reserve_bootmem (intv_start, intv_end - intv_start); | 
|  | 249 |  | 
|  | 250 | if (bootmap >= ram_start && bootmap < ram_end) | 
|  | 251 | /* Reserve the bootmap space.  */ | 
|  | 252 | reserve_bootmem (bootmap, bootmap_len); | 
|  | 253 |  | 
|  | 254 | /* Reserve the memory used by the root filesystem image if it's | 
|  | 255 | in RAM.  */ | 
|  | 256 | if (&_root_fs_image_end > &_root_fs_image_start | 
|  | 257 | && (unsigned long)&_root_fs_image_start >= ram_start | 
|  | 258 | && (unsigned long)&_root_fs_image_start < ram_end) | 
|  | 259 | reserve_bootmem ((unsigned long)&_root_fs_image_start, | 
|  | 260 | &_root_fs_image_end - &_root_fs_image_start); | 
|  | 261 |  | 
|  | 262 | /* Let the platform-dependent code reserve some too.  */ | 
|  | 263 | if (mrb) | 
|  | 264 | (*mrb) (); | 
|  | 265 | } | 
|  | 266 |  | 
|  | 267 | /* Tell the kernel about what RAM it may use for memory allocation.  */ | 
|  | 268 | static void __init | 
|  | 269 | init_mem_alloc (unsigned long ram_start, unsigned long ram_len) | 
|  | 270 | { | 
|  | 271 | unsigned i; | 
|  | 272 | unsigned long zones_size[MAX_NR_ZONES]; | 
|  | 273 |  | 
|  | 274 | init_bootmem_alloc (ram_start, ram_len); | 
|  | 275 |  | 
|  | 276 | for (i = 0; i < MAX_NR_ZONES; i++) | 
|  | 277 | zones_size[i] = 0; | 
|  | 278 |  | 
|  | 279 | /* We stuff all the memory into one area, which includes the | 
|  | 280 | initial gap from PAGE_OFFSET to ram_start.  */ | 
|  | 281 | zones_size[ZONE_DMA] | 
|  | 282 | = ADDR_TO_PAGE (ram_len + (ram_start - PAGE_OFFSET)); | 
|  | 283 |  | 
|  | 284 | /* The allocator is very picky about the address of the first | 
|  | 285 | allocatable page -- it must be at least as aligned as the | 
|  | 286 | maximum allocation -- so try to detect cases where it will get | 
|  | 287 | confused and signal them at compile time (this is a common | 
|  | 288 | problem when porting to a new platform with ).  There is a | 
|  | 289 | similar runtime check in free_area_init_core.  */ | 
|  | 290 | #if ((PAGE_OFFSET >> PAGE_SHIFT) & ((1UL << (MAX_ORDER - 1)) - 1)) | 
|  | 291 | #error MAX_ORDER is too large for given PAGE_OFFSET (use CONFIG_FORCE_MAX_ZONEORDER to change it) | 
|  | 292 | #endif | 
|  | 293 | NODE_DATA(0)->node_mem_map = NULL; | 
|  | 294 | free_area_init_node (0, NODE_DATA(0), zones_size, | 
|  | 295 | ADDR_TO_PAGE (PAGE_OFFSET), 0); | 
|  | 296 | } | 
| Miles Bader | 6a9b28d | 2005-09-02 15:13:31 +0900 | [diff] [blame] | 297 |  | 
|  | 298 |  | 
|  | 299 |  | 
|  | 300 | /* Taken from m68knommu */ | 
|  | 301 | void show_mem(void) | 
|  | 302 | { | 
|  | 303 | unsigned long i; | 
|  | 304 | int free = 0, total = 0, reserved = 0, shared = 0; | 
|  | 305 | int cached = 0; | 
|  | 306 |  | 
|  | 307 | printk(KERN_INFO "\nMem-info:\n"); | 
|  | 308 | show_free_areas(); | 
|  | 309 | i = max_mapnr; | 
|  | 310 | while (i-- > 0) { | 
|  | 311 | total++; | 
|  | 312 | if (PageReserved(mem_map+i)) | 
|  | 313 | reserved++; | 
|  | 314 | else if (PageSwapCache(mem_map+i)) | 
|  | 315 | cached++; | 
|  | 316 | else if (!page_count(mem_map+i)) | 
|  | 317 | free++; | 
|  | 318 | else | 
|  | 319 | shared += page_count(mem_map+i) - 1; | 
|  | 320 | } | 
|  | 321 | printk(KERN_INFO "%d pages of RAM\n",total); | 
|  | 322 | printk(KERN_INFO "%d free pages\n",free); | 
|  | 323 | printk(KERN_INFO "%d reserved pages\n",reserved); | 
|  | 324 | printk(KERN_INFO "%d pages shared\n",shared); | 
|  | 325 | printk(KERN_INFO "%d pages swap cached\n",cached); | 
|  | 326 | } |