at v2.6.30-rc7 365 lines 8.9 kB view raw
1/* 2 * linux/arch/m68k/mac/debug.c 3 * 4 * Shamelessly stolen (SCC code and general framework) from: 5 * 6 * linux/arch/m68k/atari/debug.c 7 * 8 * Atari debugging and serial console stuff 9 * 10 * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek 11 * 12 * This file is subject to the terms and conditions of the GNU General Public 13 * License. See the file COPYING in the main directory of this archive 14 * for more details. 15 */ 16 17#include <linux/types.h> 18#include <linux/sched.h> 19#include <linux/tty.h> 20#include <linux/console.h> 21#include <linux/init.h> 22#include <linux/delay.h> 23 24#define BOOTINFO_COMPAT_1_0 25#include <asm/setup.h> 26#include <asm/bootinfo.h> 27#include <asm/macints.h> 28 29extern unsigned long mac_videobase; 30extern unsigned long mac_rowbytes; 31 32extern void mac_serial_print(const char *); 33 34#define DEBUG_HEADS 35#undef DEBUG_SCREEN 36#define DEBUG_SERIAL 37 38/* 39 * These two auxiliary debug functions should go away ASAP. Only usage: 40 * before the console output is up (after head.S come some other crucial 41 * setup routines :-) it permits writing 'data' to the screen as bit patterns 42 * (good luck reading those). Helped to figure that the bootinfo contained 43 * garbage data on the amount and size of memory chunks ... 44 * 45 * The 'pos' argument now simply means 'linefeed after print' ... 46 */ 47 48#ifdef DEBUG_SCREEN 49static int peng, line; 50#endif 51 52#if 0 53 54void mac_debugging_short(int pos, short num) 55{ 56#ifdef DEBUG_SCREEN 57 unsigned char *pengoffset; 58 unsigned char *pptr; 59 int i; 60#endif 61 62#ifdef DEBUG_SERIAL 63 printk("debug: %d !\n", num); 64#endif 65 66#ifdef DEBUG_SCREEN 67 if (!MACH_IS_MAC) { 68 /* printk("debug: %d !\n", num); */ 69 return; 70 } 71 72 /* calculate current offset */ 73 pengoffset = (unsigned char *)mac_videobase + 74 (150+line*2) * mac_rowbytes + 80 * peng; 75 76 pptr = pengoffset; 77 78 for (i = 0; i < 8 * sizeof(short); i++) { /* # of bits */ 79 /* value mask for bit i, reverse order */ 80 *pptr++ = (num & (1 << (8*sizeof(short)-i-1)) ? 0xFF : 0x00); 81 } 82 83 peng++; 84 85 if (pos) { 86 line++; 87 peng = 0; 88 } 89#endif 90} 91 92void mac_debugging_long(int pos, long addr) 93{ 94#ifdef DEBUG_SCREEN 95 unsigned char *pengoffset; 96 unsigned char *pptr; 97 int i; 98#endif 99 100#ifdef DEBUG_SERIAL 101 printk("debug: #%ld !\n", addr); 102#endif 103 104#ifdef DEBUG_SCREEN 105 if (!MACH_IS_MAC) { 106 /* printk("debug: #%ld !\n", addr); */ 107 return; 108 } 109 110 pengoffset=(unsigned char *)(mac_videobase+(150+line*2)*mac_rowbytes) 111 +80*peng; 112 113 pptr = pengoffset; 114 115 for (i = 0; i < 8 * sizeof(long); i++) { /* # of bits */ 116 *pptr++ = (addr & (1 << (8*sizeof(long)-i-1)) ? 0xFF : 0x00); 117 } 118 119 peng++; 120 121 if (pos) { 122 line++; 123 peng = 0; 124 } 125#endif 126} 127 128#endif /* 0 */ 129 130#ifdef DEBUG_SERIAL 131/* 132 * TODO: serial debug code 133 */ 134 135struct mac_SCC { 136 u_char cha_b_ctrl; 137 u_char char_dummy1; 138 u_char cha_a_ctrl; 139 u_char char_dummy2; 140 u_char cha_b_data; 141 u_char char_dummy3; 142 u_char cha_a_data; 143}; 144 145# define scc (*((volatile struct mac_SCC*)mac_bi_data.sccbase)) 146 147static int scc_port = -1; 148 149static struct console mac_console_driver = { 150 .name = "debug", 151 .flags = CON_PRINTBUFFER, 152 .index = -1, 153}; 154 155/* 156 * Crude hack to get console output to the screen before the framebuffer 157 * is initialized (happens a lot later in 2.1!). 158 * We just use the console routines declared in head.S, this will interfere 159 * with regular framebuffer console output and should be used exclusively 160 * to debug kernel problems manifesting before framebuffer init (aka WSOD) 161 * 162 * To keep this hack from interfering with the regular console driver, either 163 * deregister this driver before/on framebuffer console init, or silence this 164 * function after the fbcon driver is running (will lose console messages!?). 165 * To debug real early bugs, need to write a 'mac_register_console_hack()' 166 * that is called from start_kernel() before setup_arch() and just registers 167 * this driver if Mac. 168 */ 169 170static void mac_debug_console_write(struct console *co, const char *str, 171 unsigned int count) 172{ 173 mac_serial_print(str); 174} 175 176 177 178/* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/ 179 180#define uSEC 1 181 182static inline void mac_sccb_out(char c) 183{ 184 int i; 185 186 do { 187 for (i = uSEC; i > 0; --i) 188 barrier(); 189 } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ 190 for (i = uSEC; i > 0; --i) 191 barrier(); 192 scc.cha_b_data = c; 193} 194 195static inline void mac_scca_out(char c) 196{ 197 int i; 198 199 do { 200 for (i = uSEC; i > 0; --i) 201 barrier(); 202 } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */ 203 for (i = uSEC; i > 0; --i) 204 barrier(); 205 scc.cha_a_data = c; 206} 207 208static void mac_sccb_console_write(struct console *co, const char *str, 209 unsigned int count) 210{ 211 while (count--) { 212 if (*str == '\n') 213 mac_sccb_out('\r'); 214 mac_sccb_out(*str++); 215 } 216} 217 218static void mac_scca_console_write(struct console *co, const char *str, 219 unsigned int count) 220{ 221 while (count--) { 222 if (*str == '\n') 223 mac_scca_out('\r'); 224 mac_scca_out(*str++); 225 } 226} 227 228 229/* The following two functions do a quick'n'dirty initialization of the MFP or 230 * SCC serial ports. They're used by the debugging interface, kgdb, and the 231 * serial console code. */ 232#define SCCB_WRITE(reg,val) \ 233 do { \ 234 int i; \ 235 scc.cha_b_ctrl = (reg); \ 236 for (i = uSEC; i > 0; --i) \ 237 barrier(); \ 238 scc.cha_b_ctrl = (val); \ 239 for (i = uSEC; i > 0; --i) \ 240 barrier(); \ 241 } while(0) 242 243#define SCCA_WRITE(reg,val) \ 244 do { \ 245 int i; \ 246 scc.cha_a_ctrl = (reg); \ 247 for (i = uSEC; i > 0; --i) \ 248 barrier(); \ 249 scc.cha_a_ctrl = (val); \ 250 for (i = uSEC; i > 0; --i) \ 251 barrier(); \ 252 } while(0) 253 254/* loops_per_jiffy isn't initialized yet, so we can't use udelay(). This does a 255 * delay of ~ 60us. */ 256/* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/ 257#define LONG_DELAY() \ 258 do { \ 259 int i; \ 260 for (i = 60*uSEC; i > 0; --i) \ 261 barrier(); \ 262 } while(0) 263 264static void __init mac_init_scc_port(int cflag, int port) 265{ 266 /* 267 * baud rates: 1200, 1800, 2400, 4800, 9600, 19.2k, 38.4k, 57.6k, 115.2k 268 */ 269 270 static int clksrc_table[9] = 271 /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */ 272 { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 }; 273 static int clkmode_table[9] = 274 /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */ 275 { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 }; 276 static int div_table[9] = 277 /* reg12 (BRG low) */ 278 { 94, 62, 46, 22, 10, 4, 1, 0, 0 }; 279 280 int baud = cflag & CBAUD; 281 int clksrc, clkmode, div, reg3, reg5; 282 283 if (cflag & CBAUDEX) 284 baud += B38400; 285 if (baud < B1200 || baud > B38400+2) 286 baud = B9600; /* use default 9600bps for non-implemented rates */ 287 baud -= B1200; /* tables starts at 1200bps */ 288 289 clksrc = clksrc_table[baud]; 290 clkmode = clkmode_table[baud]; 291 div = div_table[baud]; 292 293 reg3 = (((cflag & CSIZE) == CS8) ? 0xc0 : 0x40); 294 reg5 = (((cflag & CSIZE) == CS8) ? 0x60 : 0x20) | 0x82 /* assert DTR/RTS */; 295 296 if (port == 1) { 297 (void)scc.cha_b_ctrl; /* reset reg pointer */ 298 SCCB_WRITE(9, 0xc0); /* reset */ 299 LONG_DELAY(); /* extra delay after WR9 access */ 300 SCCB_WRITE(4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | 301 0x04 /* 1 stopbit */ | 302 clkmode); 303 SCCB_WRITE(3, reg3); 304 SCCB_WRITE(5, reg5); 305 SCCB_WRITE(9, 0); /* no interrupts */ 306 LONG_DELAY(); /* extra delay after WR9 access */ 307 SCCB_WRITE(10, 0); /* NRZ mode */ 308 SCCB_WRITE(11, clksrc); /* main clock source */ 309 SCCB_WRITE(12, div); /* BRG value */ 310 SCCB_WRITE(13, 0); /* BRG high byte */ 311 SCCB_WRITE(14, 1); 312 SCCB_WRITE(3, reg3 | 1); 313 SCCB_WRITE(5, reg5 | 8); 314 } else if (port == 0) { 315 (void)scc.cha_a_ctrl; /* reset reg pointer */ 316 SCCA_WRITE(9, 0xc0); /* reset */ 317 LONG_DELAY(); /* extra delay after WR9 access */ 318 SCCA_WRITE(4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 | 319 0x04 /* 1 stopbit */ | 320 clkmode); 321 SCCA_WRITE(3, reg3); 322 SCCA_WRITE(5, reg5); 323 SCCA_WRITE(9, 0); /* no interrupts */ 324 LONG_DELAY(); /* extra delay after WR9 access */ 325 SCCA_WRITE(10, 0); /* NRZ mode */ 326 SCCA_WRITE(11, clksrc); /* main clock source */ 327 SCCA_WRITE(12, div); /* BRG value */ 328 SCCA_WRITE(13, 0); /* BRG high byte */ 329 SCCA_WRITE(14, 1); 330 SCCA_WRITE(3, reg3 | 1); 331 SCCA_WRITE(5, reg5 | 8); 332 } 333} 334#endif /* DEBUG_SERIAL */ 335 336static int __init mac_debug_setup(char *arg) 337{ 338 if (!MACH_IS_MAC) 339 return 0; 340 341#ifdef DEBUG_SERIAL 342 if (!strcmp(arg, "ser") || !strcmp(arg, "ser1")) { 343 /* Mac modem port */ 344 mac_init_scc_port(B9600|CS8, 0); 345 mac_console_driver.write = mac_scca_console_write; 346 scc_port = 0; 347 } else if (!strcmp(arg, "ser2")) { 348 /* Mac printer port */ 349 mac_init_scc_port(B9600|CS8, 1); 350 mac_console_driver.write = mac_sccb_console_write; 351 scc_port = 1; 352 } 353#endif 354#ifdef DEBUG_HEADS 355 if (!strcmp(arg, "scn") || !strcmp(arg, "con")) { 356 /* display, using head.S console routines */ 357 mac_console_driver.write = mac_debug_console_write; 358 } 359#endif 360 if (mac_console_driver.write) 361 register_console(&mac_console_driver); 362 return 0; 363} 364 365early_param("debug", mac_debug_setup);