| 1 | /* |
| 2 | * linux/arch/m68k/coldfire/config.c |
| 3 | * |
| 4 | * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved. |
| 5 | * Kurt Mahan kmahan@freescale.com |
| 6 | * Matt Waddel Matt.Waddel@freescale.com |
| 7 | * Shrek Wu b16972@freescale.com |
| 8 | * |
| 9 | * This program is free software; you can redistribute it and/or modify |
| 10 | * it under the terms of the GNU General Public License as published by |
| 11 | * the Free Software Foundation; either version 2 of the License, or |
| 12 | * (at your option) any later version. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/module.h> |
| 16 | #include <linux/init.h> |
| 17 | #include <linux/string.h> |
| 18 | #include <linux/kernel.h> |
| 19 | #include <linux/console.h> |
| 20 | #include <linux/bootmem.h> |
| 21 | #include <linux/mm.h> |
| 22 | #include <linux/clockchips.h> |
| 23 | #include <asm/bootinfo.h> |
| 24 | #include <asm/machdep.h> |
| 25 | #include <asm/coldfire.h> |
| 26 | #include <asm/cfcache.h> |
| 27 | #include <asm/cacheflush.h> |
| 28 | #include <linux/io.h> |
| 29 | #include <asm/cfmmu.h> |
| 30 | #include <asm/setup.h> |
| 31 | #include <asm/irq.h> |
| 32 | #include <asm/traps.h> |
| 33 | #include <asm/movs.h> |
| 34 | #include <asm/movs.h> |
| 35 | #include <asm/page.h> |
| 36 | #include <asm/pgalloc.h> |
| 37 | |
| 38 | #include <asm/mcfsim.h> |
| 39 | |
| 40 | #define UBOOT_PCI |
| 41 | #include <asm/bootinfo.h> |
| 42 | #include <asm/m5485gpt.h> |
| 43 | |
| 44 | extern int get_irq_list(struct seq_file *p, void *v); |
| 45 | extern char _text, _end; |
| 46 | extern char _etext, _edata, __init_begin, __init_end; |
| 47 | extern struct console mcfrs_console; |
| 48 | extern char m68k_command_line[CL_SIZE]; |
| 49 | extern unsigned long availmem; |
| 50 | |
| 51 | static int irq_enable[NR_IRQS]; |
| 52 | unsigned long num_pages; |
| 53 | |
| 54 | /* cf dma physical addresses */ |
| 55 | unsigned long cf_dma_base; |
| 56 | unsigned long cf_dma_end; |
| 57 | unsigned long cf_dma_size; |
| 58 | EXPORT_SYMBOL(cf_dma_base); |
| 59 | EXPORT_SYMBOL(cf_dma_end); |
| 60 | EXPORT_SYMBOL(cf_dma_size); |
| 61 | |
| 62 | /* ethernet mac addresses from uboot */ |
| 63 | unsigned char uboot_enet0[6]; |
| 64 | unsigned char uboot_enet1[6]; |
| 65 | |
| 66 | void coldfire_sort_memrec(void) |
| 67 | { |
| 68 | int i, j; |
| 69 | |
| 70 | /* Sort the m68k_memory records by address */ |
| 71 | for (i = 0; i < m68k_num_memory; ++i) { |
| 72 | for (j = i + 1; j < m68k_num_memory; ++j) { |
| 73 | if (m68k_memory[i].addr > m68k_memory[j].addr) { |
| 74 | struct mem_info tmp; |
| 75 | tmp = m68k_memory[i]; |
| 76 | m68k_memory[i] = m68k_memory[j]; |
| 77 | m68k_memory[j] = tmp; |
| 78 | } |
| 79 | } |
| 80 | } |
| 81 | /* Trim off discontiguous bits */ |
| 82 | for (i = 1; i < m68k_num_memory; ++i) { |
| 83 | if ((m68k_memory[i-1].addr + m68k_memory[i-1].size) != |
| 84 | m68k_memory[i].addr) { |
| 85 | printk(KERN_DEBUG "m68k_parse_bootinfo: " |
| 86 | "addr gap between 0x%lx & 0x%lx\n", |
| 87 | m68k_memory[i-1].addr+m68k_memory[i-1].size, |
| 88 | m68k_memory[i].addr); |
| 89 | m68k_num_memory = i; |
| 90 | break; |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | * UBoot Handler |
| 97 | */ |
| 98 | int __init uboot_commandline(char *bootargs) |
| 99 | { |
| 100 | int len = 0, cmd_line_len; |
| 101 | static struct uboot_record uboot_info; |
| 102 | u32 offset = PAGE_OFFSET_RAW - PHYS_OFFSET; |
| 103 | |
| 104 | extern unsigned long uboot_info_stk; |
| 105 | |
| 106 | /* validate address */ |
| 107 | if ((uboot_info_stk < PAGE_OFFSET_RAW) || |
| 108 | (uboot_info_stk >= (PAGE_OFFSET_RAW + CONFIG_SDRAM_SIZE))) |
| 109 | return 0; |
| 110 | |
| 111 | /* Add offset to get post-remapped kernel memory location */ |
| 112 | uboot_info.bdi = (struct bd_info *)((*(u32 *)(uboot_info_stk)) |
| 113 | + offset); |
| 114 | uboot_info.initrd_start = (*(u32 *)(uboot_info_stk+4)) + offset; |
| 115 | uboot_info.initrd_end = (*(u32 *)(uboot_info_stk+8)) + offset; |
| 116 | uboot_info.cmd_line_start = (*(u32 *)(uboot_info_stk+12)) + offset; |
| 117 | uboot_info.cmd_line_stop = (*(u32 *)(uboot_info_stk+16)) + offset; |
| 118 | |
| 119 | /* copy over mac addresses */ |
| 120 | memcpy(uboot_enet0, uboot_info.bdi->bi_enet0addr, 6); |
| 121 | memcpy(uboot_enet1, uboot_info.bdi->bi_enet1addr, 6); |
| 122 | |
| 123 | /* copy command line */ |
| 124 | cmd_line_len = uboot_info.cmd_line_stop - uboot_info.cmd_line_start; |
| 125 | if ((cmd_line_len > 0) && (cmd_line_len < CL_SIZE-1)) |
| 126 | len = (int)strncpy(bootargs, (char *)uboot_info.cmd_line_start,\ |
| 127 | cmd_line_len); |
| 128 | |
| 129 | return len; |
| 130 | } |
| 131 | |
| 132 | /* |
| 133 | * This routine does things not done in the bootloader. |
| 134 | */ |
| 135 | #define DEFAULT_COMMAND_LINE \ |
| 136 | "debug root=/dev/nfs rw \ |
| 137 | nfsroot=172.27.155.1:/tftpboot/rigo/rootfs/ \ |
| 138 | ip=172.27.155.75:172.27.155.1" |
| 139 | |
| 140 | asmlinkage void __init cf_early_init(void) |
| 141 | { |
| 142 | struct bi_record *record = (struct bi_record *) &_end; |
| 143 | |
| 144 | extern char _end; |
| 145 | |
| 146 | SET_VBR((void *)MCF_RAMBAR0); |
| 147 | |
| 148 | /* Mask all interrupts */ |
| 149 | MCF_IMRL = 0xFFFFFFFF; |
| 150 | MCF_IMRH = 0xFFFFFFFF; |
| 151 | |
| 152 | m68k_machtype = MACH_CFMMU; |
| 153 | m68k_fputype = FPU_CFV4E; |
| 154 | m68k_mmutype = MMU_CFV4E; |
| 155 | m68k_cputype = CPU_CFV4E; |
| 156 | |
| 157 | m68k_num_memory = 0; |
| 158 | m68k_memory[m68k_num_memory].addr = CONFIG_SDRAM_BASE; |
| 159 | m68k_memory[m68k_num_memory++].size = CONFIG_SDRAM_SIZE; |
| 160 | |
| 161 | if (!uboot_commandline(m68k_command_line)) { |
| 162 | #if defined(CONFIG_BOOTPARAM) |
| 163 | strncpy(m68k_command_line, CONFIG_BOOTPARAM_STRING, CL_SIZE-1); |
| 164 | #else |
| 165 | strcpy(m68k_command_line, DEFAULT_COMMAND_LINE); |
| 166 | #endif |
| 167 | } |
| 168 | |
| 169 | #if defined(CONFIG_BLK_DEV_INITRD) |
| 170 | /* add initrd image */ |
| 171 | record = (struct bi_record *) ((void *)record + record->size); |
| 172 | record->tag = BI_RAMDISK; |
| 173 | record->size = sizeof(record->tag) + sizeof(record->size) |
| 174 | + sizeof(record->data[0]) + sizeof(record->data[1]); |
| 175 | #endif |
| 176 | |
| 177 | /* Mark end of tags. */ |
| 178 | record = (struct bi_record *) ((void *) record + record->size); |
| 179 | record->tag = 0; |
| 180 | record->data[0] = 0; |
| 181 | record->data[1] = 0; |
| 182 | record->size = sizeof(record->tag) + sizeof(record->size) |
| 183 | + sizeof(record->data[0]) + sizeof(record->data[1]); |
| 184 | |
| 185 | /* Invalidate caches via CACR */ |
| 186 | flush_bcache(); |
| 187 | cacr_set(CACHE_DISABLE_MODE); |
| 188 | |
| 189 | /* Turn on caches via CACR, enable EUSP */ |
| 190 | cacr_set(CACHE_INITIAL_MODE); |
| 191 | |
| 192 | } |
| 193 | |
| 194 | /* Assembler routines */ |
| 195 | asmlinkage void buserr(void); |
| 196 | asmlinkage void trap(void); |
| 197 | asmlinkage void system_call(void); |
| 198 | asmlinkage void inthandler(void); |
| 199 | |
| 200 | void __init coldfire_trap_init(void) |
| 201 | { |
| 202 | int i = 0; |
| 203 | e_vector *vectors; |
| 204 | |
| 205 | vectors = (e_vector *)MCF_RAMBAR0; |
| 206 | /* |
| 207 | * There is a common trap handler and common interrupt |
| 208 | * handler that handle almost every vector. We treat |
| 209 | * the system call and bus error special, they get their |
| 210 | * own first level handlers. |
| 211 | */ |
| 212 | for (i = 3; (i <= 23); i++) |
| 213 | vectors[i] = trap; |
| 214 | for (i = 33; (i <= 63); i++) |
| 215 | vectors[i] = trap; |
| 216 | for (i = 24; (i <= 31); i++) |
| 217 | vectors[i] = inthandler; |
| 218 | for (i = 64; (i < 255); i++) |
| 219 | vectors[i] = inthandler; |
| 220 | |
| 221 | vectors[255] = 0; |
| 222 | vectors[2] = buserr; |
| 223 | vectors[32] = system_call; |
| 224 | } |
| 225 | |
| 226 | #ifndef CONFIG_GENERIC_CLOCKEVENTS |
| 227 | void coldfire_tick(void) |
| 228 | { |
| 229 | /* Reset the ColdFire timer */ |
| 230 | MCF_SSR(0) = MCF_SSR_ST; |
| 231 | } |
| 232 | |
| 233 | void __init coldfire_sched_init(irq_handler_t handler) |
| 234 | { |
| 235 | int irq = ISC_SLTn(0); |
| 236 | |
| 237 | MCF_SCR(0) = 0; |
| 238 | MCF_ICR(irq) = ILP_SLT0; |
| 239 | request_irq(64 + irq, handler, IRQF_DISABLED, "ColdFire Timer 0", NULL); |
| 240 | MCF_SLTCNT(0) = MCF_BUSCLK / HZ; |
| 241 | MCF_SCR(0) |= MCF_SCR_TEN | MCF_SCR_IEN | MCF_SCR_RUN; |
| 242 | } |
| 243 | |
| 244 | unsigned long coldfire_gettimeoffset(void) |
| 245 | { |
| 246 | volatile unsigned long trr, tcn, offset; |
| 247 | trr = MCF_SLTCNT(0); |
| 248 | tcn = MCF_SCNT(0); |
| 249 | |
| 250 | offset = (trr - tcn) * ((1000000 >> 3) / HZ) / (trr >> 3); |
| 251 | if (MCF_SSR(0) & MCF_SSR_ST) |
| 252 | offset += 1000000 / HZ; |
| 253 | |
| 254 | return offset; |
| 255 | } |
| 256 | #else |
| 257 | static unsigned long long sched_dtim_clk_val; |
| 258 | |
| 259 | unsigned long long sched_clock(void) |
| 260 | { |
| 261 | unsigned long flags; |
| 262 | unsigned long long cycles; |
| 263 | volatile unsigned long trr, tcn, offset; |
| 264 | |
| 265 | local_irq_save(flags); |
| 266 | trr = MCF_SLTCNT(0); |
| 267 | tcn = MCF_SCNT(0); |
| 268 | offset = (trr - tcn); |
| 269 | cycles = sched_dtim_clk_val; |
| 270 | local_irq_restore(flags); |
| 271 | |
| 272 | return cycles + offset; |
| 273 | } |
| 274 | |
| 275 | unsigned long long sys_dtim2_read(void) |
| 276 | { |
| 277 | unsigned long flags; |
| 278 | unsigned long long cycles; |
| 279 | volatile unsigned long trr, tcn, offset; |
| 280 | |
| 281 | local_irq_save(flags); |
| 282 | trr = MCF_SLTCNT(0); |
| 283 | tcn = MCF_SCNT(0); |
| 284 | offset = (trr - tcn); |
| 285 | cycles = sched_dtim_clk_val; |
| 286 | local_irq_restore(flags); |
| 287 | |
| 288 | return cycles + offset; |
| 289 | } |
| 290 | |
| 291 | static irqreturn_t coldfire_dtim_clk_irq(int irq, void *dev) |
| 292 | { |
| 293 | struct clock_event_device *evt = |
| 294 | (struct clock_event_device *)dev; |
| 295 | |
| 296 | MCF_SSR(0) = MCF_SSR_ST; |
| 297 | sched_dtim_clk_val += (MCF_BUSCLK) / HZ;; |
| 298 | evt->event_handler(evt); |
| 299 | return IRQ_HANDLED; |
| 300 | } |
| 301 | |
| 302 | void sys_dtim2_init(struct clock_event_device *evt) |
| 303 | { |
| 304 | int irq = ISC_SLTn(0); |
| 305 | |
| 306 | sched_dtim_clk_val = 0; |
| 307 | MCF_SCR(0) = 0; |
| 308 | MCF_ICR(irq) = ILP_SLT0; |
| 309 | request_irq(64 + irq, coldfire_dtim_clk_irq, IRQF_DISABLED, |
| 310 | "ColdFire Timer 0", (void *)evt); |
| 311 | MCF_SLTCNT(0) = MCF_BUSCLK / HZ; |
| 312 | MCF_SCR(0) |= MCF_SCR_TEN | MCF_SCR_IEN | MCF_SCR_RUN; |
| 313 | } |
| 314 | #endif |
| 315 | |
| 316 | void coldfire_reboot(void) |
| 317 | { |
| 318 | /* disable interrupts and enable the watchdog */ |
| 319 | printk(KERN_INFO "Rebooting\n"); |
| 320 | asm("movew #0x2700, %sr\n"); |
| 321 | MCF_GPT_GMS0 = MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE | MCF_GPT_GMS_TMS(4); |
| 322 | } |
| 323 | |
| 324 | static void coldfire_get_model(char *model) |
| 325 | { |
| 326 | sprintf(model, "Version 4 ColdFire"); |
| 327 | } |
| 328 | |
| 329 | static void __init |
| 330 | coldfire_bootmem_alloc(unsigned long memory_start, unsigned long memory_end) |
| 331 | { |
| 332 | unsigned long base_pfn; |
| 333 | |
| 334 | /* compute total pages in system */ |
| 335 | num_pages = PAGE_ALIGN(memory_end - PAGE_OFFSET) >> PAGE_SHIFT; |
| 336 | |
| 337 | /* align start/end to page boundries */ |
| 338 | memory_start = PAGE_ALIGN(memory_start); |
| 339 | memory_end = memory_end & PAGE_MASK; |
| 340 | |
| 341 | /* page numbers */ |
| 342 | base_pfn = __pa(PAGE_OFFSET) >> PAGE_SHIFT; |
| 343 | min_low_pfn = __pa(memory_start) >> PAGE_SHIFT; |
| 344 | max_low_pfn = __pa(memory_end) >> PAGE_SHIFT; |
| 345 | |
| 346 | high_memory = (void *)memory_end; |
| 347 | availmem = memory_start; |
| 348 | |
| 349 | /* setup bootmem data */ |
| 350 | m68k_setup_node(0); |
| 351 | availmem += init_bootmem_node(NODE_DATA(0), min_low_pfn, |
| 352 | base_pfn, max_low_pfn); |
| 353 | availmem = PAGE_ALIGN(availmem); |
| 354 | |
| 355 | printk(KERN_INFO "** availmem=0x%lx pa(am)=0x%lx\n", |
| 356 | availmem, __pa(availmem)); |
| 357 | printk(KERN_INFO "** mstart=0x%lx mend=0x%lx\n", |
| 358 | memory_start, memory_end); |
| 359 | printk(KERN_INFO "bpfn=0x%lx minpfn=0x%lx maxpfn=0x%lx\n", |
| 360 | base_pfn, min_low_pfn, max_low_pfn); |
| 361 | |
| 362 | /* turn over physram */ |
| 363 | free_bootmem(__pa(availmem), memory_end - (availmem)); |
| 364 | |
| 365 | /* configure physical dma area */ |
| 366 | cf_dma_base = __pa(PAGE_ALIGN(memory_start)); |
| 367 | cf_dma_size = CONFIG_DMA_SIZE; |
| 368 | cf_dma_end = CONFIG_SDRAM_BASE + cf_dma_size - 1; |
| 369 | |
| 370 | printk(KERN_INFO "dma: phys base=0x%lx phys end=0x%lx virt base=0x%x\n", |
| 371 | cf_dma_base, cf_dma_end, CONFIG_DMA_BASE); |
| 372 | |
| 373 | printk(KERN_INFO "mdma=0x%x pa(mdma)=0x%lx\n", |
| 374 | MAX_DMA_ADDRESS, __pa(MAX_DMA_ADDRESS)); |
| 375 | } |
| 376 | |
| 377 | void __init config_coldfire(void) |
| 378 | { |
| 379 | unsigned long endmem, startmem; |
| 380 | int i; |
| 381 | |
| 382 | /* |
| 383 | * Calculate endmem from m68k_memory, assume all are contiguous |
| 384 | */ |
| 385 | startmem = ((((int) &_end) + (PAGE_SIZE - 1)) & PAGE_MASK); |
| 386 | endmem = PAGE_OFFSET; |
| 387 | for (i = 0; i < m68k_num_memory; ++i) |
| 388 | endmem += m68k_memory[i].size; |
| 389 | |
| 390 | printk(KERN_INFO "starting up linux startmem 0x%lx, endmem 0x%lx, \ |
| 391 | size %luMB\n", startmem, endmem, (endmem - startmem) >> 20); |
| 392 | |
| 393 | memset(irq_enable, 0, sizeof(irq_enable)); |
| 394 | |
| 395 | /* |
| 396 | * Setup coldfire mach-specific handlers |
| 397 | */ |
| 398 | mach_max_dma_address = 0xffffffff; |
| 399 | #ifndef CONFIG_GENERIC_CLOCKEVENTS |
| 400 | mach_sched_init = coldfire_sched_init; |
| 401 | mach_tick = coldfire_tick; |
| 402 | mach_gettimeoffset = coldfire_gettimeoffset; |
| 403 | #endif |
| 404 | mach_reset = coldfire_reboot; |
| 405 | /* mach_hwclk = coldfire_hwclk; to be done */ |
| 406 | mach_get_model = coldfire_get_model; |
| 407 | |
| 408 | coldfire_bootmem_alloc(startmem, endmem-1); |
| 409 | |
| 410 | /* |
| 411 | * initrd setup |
| 412 | */ |
| 413 | /* #ifdef CONFIG_BLK_DEV_INITRD |
| 414 | if (m68k_ramdisk.size) { |
| 415 | reserve_bootmem (__pa(m68k_ramdisk.addr), m68k_ramdisk.size); |
| 416 | initrd_start = (unsigned long) m68k_ramdisk.addr; |
| 417 | initrd_end = initrd_start + m68k_ramdisk.size; |
| 418 | printk (KERN_DEBUG "initrd: %08lx - %08lx\n", initrd_start, |
| 419 | initrd_end); |
| 420 | } |
| 421 | #endif */ |
| 422 | |
| 423 | #if defined(CONFIG_DUMMY_CONSOLE) || defined(CONFIG_FRAMEBUFFER_CONSOLE) |
| 424 | conswitchp = &dummy_con; |
| 425 | #endif |
| 426 | |
| 427 | #if defined(CONFIG_SERIAL_COLDFIRE) |
| 428 | /* |
| 429 | * This causes trouble when it is re-registered later. |
| 430 | * Currently this is fixed by conditionally commenting |
| 431 | * out the register_console in mcf_serial.c |
| 432 | */ |
| 433 | register_console(&mcfrs_console); |
| 434 | #endif |
| 435 | } |
| 436 | |