| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 1 | /* | 
| Robin Getz | 96f1050 | 2009-09-24 14:11:24 +0000 | [diff] [blame] | 2 | * allow a console to be used for early printk | 
|  | 3 | * derived from arch/x86/kernel/early_printk.c | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 4 | * | 
| Robin Getz | 96f1050 | 2009-09-24 14:11:24 +0000 | [diff] [blame] | 5 | * Copyright 2007-2009 Analog Devices Inc. | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 6 | * | 
| Robin Getz | 96f1050 | 2009-09-24 14:11:24 +0000 | [diff] [blame] | 7 | * Licensed under the GPL-2 | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 8 | */ | 
|  | 9 |  | 
|  | 10 | #include <linux/kernel.h> | 
|  | 11 | #include <linux/init.h> | 
|  | 12 | #include <linux/serial_core.h> | 
|  | 13 | #include <linux/console.h> | 
|  | 14 | #include <linux/string.h> | 
| Robin Getz | 837ec2d | 2009-07-07 20:17:09 +0000 | [diff] [blame] | 15 | #include <linux/reboot.h> | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 16 | #include <asm/blackfin.h> | 
|  | 17 | #include <asm/irq_handler.h> | 
|  | 18 | #include <asm/early_printk.h> | 
|  | 19 |  | 
|  | 20 | #ifdef CONFIG_SERIAL_BFIN | 
|  | 21 | extern struct console *bfin_earlyserial_init(unsigned int port, | 
|  | 22 | unsigned int cflag); | 
|  | 23 | #endif | 
| Mike Frysinger | a88c71e | 2008-10-10 17:37:52 +0800 | [diff] [blame] | 24 | #ifdef CONFIG_BFIN_JTAG_COMM | 
|  | 25 | extern struct console *bfin_jc_early_init(void); | 
|  | 26 | #endif | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 27 |  | 
|  | 28 | static struct console *early_console; | 
|  | 29 |  | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 30 | /* Default console */ | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 31 | #define DEFAULT_PORT 0 | 
|  | 32 | #define DEFAULT_CFLAG CS8|B57600 | 
|  | 33 |  | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 34 | /* Default console for early crashes */ | 
|  | 35 | #define DEFAULT_EARLY_PORT "serial,uart0,57600" | 
|  | 36 |  | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 37 | #ifdef CONFIG_SERIAL_CORE | 
|  | 38 | /* What should get here is "0,57600" */ | 
|  | 39 | static struct console * __init earlyserial_init(char *buf) | 
|  | 40 | { | 
|  | 41 | int baud, bit; | 
|  | 42 | char parity; | 
|  | 43 | unsigned int serial_port = DEFAULT_PORT; | 
|  | 44 | unsigned int cflag = DEFAULT_CFLAG; | 
|  | 45 |  | 
|  | 46 | serial_port = simple_strtoul(buf, &buf, 10); | 
|  | 47 | buf++; | 
|  | 48 |  | 
|  | 49 | cflag = 0; | 
|  | 50 | baud = simple_strtoul(buf, &buf, 10); | 
|  | 51 | switch (baud) { | 
|  | 52 | case 1200: | 
|  | 53 | cflag |= B1200; | 
|  | 54 | break; | 
|  | 55 | case 2400: | 
|  | 56 | cflag |= B2400; | 
|  | 57 | break; | 
|  | 58 | case 4800: | 
|  | 59 | cflag |= B4800; | 
|  | 60 | break; | 
|  | 61 | case 9600: | 
|  | 62 | cflag |= B9600; | 
|  | 63 | break; | 
|  | 64 | case 19200: | 
|  | 65 | cflag |= B19200; | 
|  | 66 | break; | 
|  | 67 | case 38400: | 
|  | 68 | cflag |= B38400; | 
|  | 69 | break; | 
|  | 70 | case 115200: | 
|  | 71 | cflag |= B115200; | 
|  | 72 | break; | 
|  | 73 | default: | 
|  | 74 | cflag |= B57600; | 
|  | 75 | } | 
|  | 76 |  | 
|  | 77 | parity = buf[0]; | 
|  | 78 | buf++; | 
|  | 79 | switch (parity) { | 
|  | 80 | case 'e': | 
|  | 81 | cflag |= PARENB; | 
|  | 82 | break; | 
|  | 83 | case 'o': | 
|  | 84 | cflag |= PARODD; | 
|  | 85 | break; | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | bit = simple_strtoul(buf, &buf, 10); | 
|  | 89 | switch (bit) { | 
|  | 90 | case 5: | 
|  | 91 | cflag |= CS5; | 
|  | 92 | break; | 
|  | 93 | case 6: | 
| Mike Frysinger | 09b7f4a | 2009-01-07 23:14:39 +0800 | [diff] [blame] | 94 | cflag |= CS6; | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 95 | break; | 
|  | 96 | case 7: | 
| Mike Frysinger | 09b7f4a | 2009-01-07 23:14:39 +0800 | [diff] [blame] | 97 | cflag |= CS7; | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 98 | break; | 
|  | 99 | default: | 
|  | 100 | cflag |= CS8; | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | #ifdef CONFIG_SERIAL_BFIN | 
|  | 104 | return bfin_earlyserial_init(serial_port, cflag); | 
|  | 105 | #else | 
|  | 106 | return NULL; | 
|  | 107 | #endif | 
|  | 108 |  | 
|  | 109 | } | 
|  | 110 | #endif | 
|  | 111 |  | 
|  | 112 | int __init setup_early_printk(char *buf) | 
|  | 113 | { | 
|  | 114 |  | 
|  | 115 | /* Crashing in here would be really bad, so check both the var | 
|  | 116 | and the pointer before we start using it | 
|  | 117 | */ | 
|  | 118 | if (!buf) | 
|  | 119 | return 0; | 
|  | 120 |  | 
|  | 121 | if (!*buf) | 
|  | 122 | return 0; | 
|  | 123 |  | 
|  | 124 | if (early_console != NULL) | 
|  | 125 | return 0; | 
|  | 126 |  | 
|  | 127 | #ifdef CONFIG_SERIAL_BFIN | 
|  | 128 | /* Check for Blackfin Serial */ | 
|  | 129 | if (!strncmp(buf, "serial,uart", 11)) { | 
|  | 130 | buf += 11; | 
|  | 131 | early_console = earlyserial_init(buf); | 
|  | 132 | } | 
|  | 133 | #endif | 
| Mike Frysinger | a88c71e | 2008-10-10 17:37:52 +0800 | [diff] [blame] | 134 |  | 
|  | 135 | #ifdef CONFIG_BFIN_JTAG_COMM | 
|  | 136 | /* Check for Blackfin JTAG */ | 
|  | 137 | if (!strncmp(buf, "jtag", 4)) { | 
|  | 138 | buf += 4; | 
|  | 139 | early_console = bfin_jc_early_init(); | 
|  | 140 | } | 
|  | 141 | #endif | 
|  | 142 |  | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 143 | #ifdef CONFIG_FB | 
|  | 144 | /* TODO: add framebuffer console support */ | 
|  | 145 | #endif | 
|  | 146 |  | 
|  | 147 | if (likely(early_console)) { | 
|  | 148 | early_console->flags |= CON_BOOT; | 
|  | 149 |  | 
|  | 150 | register_console(early_console); | 
|  | 151 | printk(KERN_INFO "early printk enabled on %s%d\n", | 
|  | 152 | early_console->name, | 
|  | 153 | early_console->index); | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | return 0; | 
|  | 157 | } | 
|  | 158 |  | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 159 | /* | 
|  | 160 | * Set up a temporary Event Vector Table, so if something bad happens before | 
|  | 161 | * the kernel is fully started, it doesn't vector off into somewhere we don't | 
|  | 162 | * know | 
|  | 163 | */ | 
|  | 164 |  | 
|  | 165 | asmlinkage void __init init_early_exception_vectors(void) | 
|  | 166 | { | 
| Mike Frysinger | 685a694 | 2009-06-03 00:33:31 +0000 | [diff] [blame] | 167 | u32 evt; | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 168 | SSYNC(); | 
|  | 169 |  | 
| Robin Getz | 837ec2d | 2009-07-07 20:17:09 +0000 | [diff] [blame] | 170 | /* | 
|  | 171 | * This starts up the shadow buffer, incase anything crashes before | 
|  | 172 | * setup arch | 
|  | 173 | */ | 
|  | 174 | mark_shadow_error(); | 
|  | 175 | early_shadow_puts(linux_banner); | 
|  | 176 | early_shadow_stamp(); | 
|  | 177 |  | 
|  | 178 | if (CPUID != bfin_cpuid()) { | 
|  | 179 | early_shadow_puts("Running on wrong machine type, expected"); | 
|  | 180 | early_shadow_reg(CPUID, 16); | 
|  | 181 | early_shadow_puts(", but running on"); | 
|  | 182 | early_shadow_reg(bfin_cpuid(), 16); | 
|  | 183 | early_shadow_puts("\n"); | 
|  | 184 | } | 
|  | 185 |  | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 186 | /* cannot program in software: | 
|  | 187 | * evt0 - emulation (jtag) | 
|  | 188 | * evt1 - reset | 
|  | 189 | */ | 
| Mike Frysinger | 685a694 | 2009-06-03 00:33:31 +0000 | [diff] [blame] | 190 | for (evt = EVT2; evt <= EVT15; evt += 4) | 
|  | 191 | bfin_write32(evt, early_trap); | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 192 | CSYNC(); | 
|  | 193 |  | 
| Joe Perches | 79f1ec8 | 2007-12-24 20:03:51 +0800 | [diff] [blame] | 194 | /* Set all the return from interrupt, exception, NMI to a known place | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 195 | * so if we do a RETI, RETX or RETN by mistake - we go somewhere known | 
|  | 196 | * Note - don't change RETS - we are in a subroutine, or | 
|  | 197 | * RETE - since it might screw up if emulator is attached | 
|  | 198 | */ | 
|  | 199 | asm("\tRETI = %0; RETX = %0; RETN = %0;\n" | 
|  | 200 | : : "p"(early_trap)); | 
|  | 201 |  | 
|  | 202 | } | 
|  | 203 |  | 
| Robin Getz | 837ec2d | 2009-07-07 20:17:09 +0000 | [diff] [blame] | 204 | __attribute__((__noreturn__)) | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 205 | asmlinkage void __init early_trap_c(struct pt_regs *fp, void *retaddr) | 
|  | 206 | { | 
|  | 207 | /* This can happen before the uart is initialized, so initialize | 
| Robin Getz | 54ebae7 | 2009-06-10 06:11:21 +0000 | [diff] [blame] | 208 | * the UART now (but only if we are running on the processor we think | 
|  | 209 | * we are compiled for - otherwise we write to MMRs that don't exist, | 
|  | 210 | * and cause other problems. Nothing comes out the UART, but it does | 
|  | 211 | * end up in the __buf_log. | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 212 | */ | 
| Robin Getz | 54ebae7 | 2009-06-10 06:11:21 +0000 | [diff] [blame] | 213 | if (likely(early_console == NULL) && CPUID == bfin_cpuid()) | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 214 | setup_early_printk(DEFAULT_EARLY_PORT); | 
|  | 215 |  | 
| Robin Getz | 837ec2d | 2009-07-07 20:17:09 +0000 | [diff] [blame] | 216 | if (!shadow_console_enabled()) { | 
|  | 217 | /* crap - we crashed before setup_arch() */ | 
|  | 218 | early_shadow_puts("panic before setup_arch\n"); | 
|  | 219 | early_shadow_puts("IPEND:"); | 
|  | 220 | early_shadow_reg(fp->ipend, 16); | 
|  | 221 | if (fp->seqstat & SEQSTAT_EXCAUSE) { | 
|  | 222 | early_shadow_puts("\nEXCAUSE:"); | 
|  | 223 | early_shadow_reg(fp->seqstat & SEQSTAT_EXCAUSE, 8); | 
|  | 224 | } | 
|  | 225 | if (fp->seqstat & SEQSTAT_HWERRCAUSE) { | 
|  | 226 | early_shadow_puts("\nHWERRCAUSE:"); | 
|  | 227 | early_shadow_reg( | 
|  | 228 | (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14, 8); | 
|  | 229 | } | 
|  | 230 | early_shadow_puts("\nErr @"); | 
|  | 231 | if (fp->ipend & EVT_EVX) | 
|  | 232 | early_shadow_reg(fp->retx, 32); | 
|  | 233 | else | 
|  | 234 | early_shadow_reg(fp->pc, 32); | 
|  | 235 | #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON | 
|  | 236 | early_shadow_puts("\nTrace:"); | 
|  | 237 | if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) { | 
|  | 238 | while (bfin_read_TBUFSTAT() & TBUFCNT) { | 
|  | 239 | early_shadow_puts("\nT  :"); | 
|  | 240 | early_shadow_reg(bfin_read_TBUF(), 32); | 
|  | 241 | early_shadow_puts("\n S :"); | 
|  | 242 | early_shadow_reg(bfin_read_TBUF(), 32); | 
|  | 243 | } | 
|  | 244 | } | 
|  | 245 | #endif | 
|  | 246 | early_shadow_puts("\nUse bfin-elf-addr2line to determine " | 
|  | 247 | "function names\n"); | 
|  | 248 | /* | 
|  | 249 | * We should panic(), but we can't - since panic calls printk, | 
|  | 250 | * and printk uses memcpy. | 
|  | 251 | * we want to reboot, but if the machine type is different, | 
|  | 252 | * can't due to machine specific reboot sequences | 
|  | 253 | */ | 
|  | 254 | if (CPUID == bfin_cpuid()) { | 
|  | 255 | early_shadow_puts("Trying to restart\n"); | 
|  | 256 | machine_restart(""); | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | early_shadow_puts("Halting, since it is not safe to restart\n"); | 
|  | 260 | while (1) | 
|  | 261 | asm volatile ("EMUEXCPT; IDLE;\n"); | 
|  | 262 |  | 
|  | 263 | } else { | 
|  | 264 | printk(KERN_EMERG "Early panic\n"); | 
|  | 265 | show_regs(fp); | 
|  | 266 | dump_bfin_trace_buffer(); | 
|  | 267 | } | 
| Robin Getz | 337d390 | 2007-10-09 17:31:46 +0800 | [diff] [blame] | 268 |  | 
|  | 269 | panic("Died early"); | 
|  | 270 | } | 
|  | 271 |  | 
| Robin Getz | 0ae5364 | 2007-10-09 17:24:49 +0800 | [diff] [blame] | 272 | early_param("earlyprintk", setup_early_printk); |