blob: c80177f8ec04e1f1fa6cc32fed12e90a6c3d26d9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 1996 Paul Mackerras.
3 */
4#include <linux/config.h>
5#include <linux/string.h>
6#include <asm/machdep.h>
7#include <asm/io.h>
8#include <asm/page.h>
9#include <linux/adb.h>
10#include <linux/pmu.h>
11#include <linux/cuda.h>
12#include <linux/kernel.h>
13#include <linux/errno.h>
14#include <linux/sysrq.h>
15#include <linux/bitops.h>
16#include <asm/xmon.h>
17#include <asm/prom.h>
18#include <asm/bootx.h>
19#include <asm/machdep.h>
20#include <asm/errno.h>
21#include <asm/pmac_feature.h>
22#include <asm/processor.h>
23#include <asm/delay.h>
24#include <asm/btext.h>
25
26static volatile unsigned char *sccc, *sccd;
27unsigned int TXRDY, RXRDY, DLAB;
28static int xmon_expect(const char *str, unsigned int timeout);
29
30static int use_serial;
31static int use_screen;
32static int via_modem;
33static int xmon_use_sccb;
34static struct device_node *channel_node;
35
36#define TB_SPEED 25000000
37
38static inline unsigned int readtb(void)
39{
40 unsigned int ret;
41
42 asm volatile("mftb %0" : "=r" (ret) :);
43 return ret;
44}
45
46void buf_access(void)
47{
48 if (DLAB)
49 sccd[3] &= ~DLAB; /* reset DLAB */
50}
51
52extern int adb_init(void);
53
54#ifdef CONFIG_PPC_CHRP
55/*
56 * This looks in the "ranges" property for the primary PCI host bridge
57 * to find the physical address of the start of PCI/ISA I/O space.
58 * It is basically a cut-down version of pci_process_bridge_OF_ranges.
59 */
60static unsigned long chrp_find_phys_io_base(void)
61{
62 struct device_node *node;
63 unsigned int *ranges;
64 unsigned long base = CHRP_ISA_IO_BASE;
65 int rlen = 0;
66 int np;
67
68 node = find_devices("isa");
69 if (node != NULL) {
70 node = node->parent;
71 if (node == NULL || node->type == NULL
72 || strcmp(node->type, "pci") != 0)
73 node = NULL;
74 }
75 if (node == NULL)
76 node = find_devices("pci");
77 if (node == NULL)
78 return base;
79
80 ranges = (unsigned int *) get_property(node, "ranges", &rlen);
81 np = prom_n_addr_cells(node) + 5;
82 while ((rlen -= np * sizeof(unsigned int)) >= 0) {
83 if ((ranges[0] >> 24) == 1 && ranges[2] == 0) {
84 /* I/O space starting at 0, grab the phys base */
85 base = ranges[np - 3];
86 break;
87 }
88 ranges += np;
89 }
90 return base;
91}
92#endif /* CONFIG_PPC_CHRP */
93
94#ifdef CONFIG_MAGIC_SYSRQ
95static void sysrq_handle_xmon(int key, struct pt_regs *regs,
96 struct tty_struct *tty)
97{
98 xmon(regs);
99}
100
101static struct sysrq_key_op sysrq_xmon_op =
102{
103 .handler = sysrq_handle_xmon,
104 .help_msg = "Xmon",
105 .action_msg = "Entering xmon",
106};
107#endif
108
109void
110xmon_map_scc(void)
111{
112#ifdef CONFIG_PPC_MULTIPLATFORM
113 volatile unsigned char *base;
114
115 if (_machine == _MACH_Pmac) {
116 struct device_node *np;
117 unsigned long addr;
118#ifdef CONFIG_BOOTX_TEXT
119 if (!use_screen && !use_serial
120 && !machine_is_compatible("iMac")) {
121 /* see if there is a keyboard in the device tree
122 with a parent of type "adb" */
123 for (np = find_devices("keyboard"); np; np = np->next)
124 if (np->parent && np->parent->type
125 && strcmp(np->parent->type, "adb") == 0)
126 break;
127
128 /* needs to be hacked if xmon_printk is to be used
129 from within find_via_pmu() */
130#ifdef CONFIG_ADB_PMU
131 if (np != NULL && boot_text_mapped && find_via_pmu())
132 use_screen = 1;
133#endif
134#ifdef CONFIG_ADB_CUDA
135 if (np != NULL && boot_text_mapped && find_via_cuda())
136 use_screen = 1;
137#endif
138 }
139 if (!use_screen && (np = find_devices("escc")) != NULL) {
140 /*
141 * look for the device node for the serial port
142 * we're using and see if it says it has a modem
143 */
144 char *name = xmon_use_sccb? "ch-b": "ch-a";
145 char *slots;
146 int l;
147
148 np = np->child;
149 while (np != NULL && strcmp(np->name, name) != 0)
150 np = np->sibling;
151 if (np != NULL) {
152 /* XXX should parse this properly */
153 channel_node = np;
154 slots = get_property(np, "slot-names", &l);
155 if (slots != NULL && l >= 10
156 && strcmp(slots+4, "Modem") == 0)
157 via_modem = 1;
158 }
159 }
160 btext_drawstring("xmon uses ");
161 if (use_screen)
162 btext_drawstring("screen and keyboard\n");
163 else {
164 if (via_modem)
165 btext_drawstring("modem on ");
166 btext_drawstring(xmon_use_sccb? "printer": "modem");
167 btext_drawstring(" port\n");
168 }
169
170#endif /* CONFIG_BOOTX_TEXT */
171
172#ifdef CHRP_ESCC
173 addr = 0xc1013020;
174#else
175 addr = 0xf3013020;
176#endif
177 TXRDY = 4;
178 RXRDY = 1;
179
180 np = find_devices("mac-io");
181 if (np && np->n_addrs)
182 addr = np->addrs[0].address + 0x13020;
183 base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
184 sccc = base + (addr & ~PAGE_MASK);
185 sccd = sccc + 0x10;
186
Benjamin Herrenschmidtd3ed6582005-11-16 13:40:43 +1100187 }
188#ifdef CONFIG_PPC_CHRP
189 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 base = (volatile unsigned char *) isa_io_base;
191 if (_machine == _MACH_chrp)
192 base = (volatile unsigned char *)
193 ioremap(chrp_find_phys_io_base(), 0x1000);
194
195 sccc = base + 0x3fd;
196 sccd = base + 0x3f8;
197 if (xmon_use_sccb) {
198 sccc -= 0x100;
199 sccd -= 0x100;
200 }
201 TXRDY = 0x20;
202 RXRDY = 1;
203 DLAB = 0x80;
204 }
Benjamin Herrenschmidtd3ed6582005-11-16 13:40:43 +1100205#endif /* CONFIG_PPC_CHRP */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206#elif defined(CONFIG_GEMINI)
207 /* should already be mapped by the kernel boot */
208 sccc = (volatile unsigned char *) 0xffeffb0d;
209 sccd = (volatile unsigned char *) 0xffeffb08;
210 TXRDY = 0x20;
211 RXRDY = 1;
212 DLAB = 0x80;
213#elif defined(CONFIG_405GP)
214 sccc = (volatile unsigned char *)0xef600305;
215 sccd = (volatile unsigned char *)0xef600300;
216 TXRDY = 0x20;
217 RXRDY = 1;
218 DLAB = 0x80;
219#endif /* platform */
220
221 register_sysrq_key('x', &sysrq_xmon_op);
222}
223
224static int scc_initialized = 0;
225
226void xmon_init_scc(void);
227extern void cuda_poll(void);
228
229static inline void do_poll_adb(void)
230{
231#ifdef CONFIG_ADB_PMU
232 if (sys_ctrler == SYS_CTRLER_PMU)
233 pmu_poll_adb();
234#endif /* CONFIG_ADB_PMU */
235#ifdef CONFIG_ADB_CUDA
236 if (sys_ctrler == SYS_CTRLER_CUDA)
237 cuda_poll();
238#endif /* CONFIG_ADB_CUDA */
239}
240
241int
242xmon_write(void *handle, void *ptr, int nb)
243{
244 char *p = ptr;
245 int i, c, ct;
246
247#ifdef CONFIG_SMP
248 static unsigned long xmon_write_lock;
249 int lock_wait = 1000000;
250 int locked;
251
252 while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0)
253 if (--lock_wait == 0)
254 break;
255#endif
256
257#ifdef CONFIG_BOOTX_TEXT
258 if (use_screen) {
259 /* write it on the screen */
260 for (i = 0; i < nb; ++i)
261 btext_drawchar(*p++);
262 goto out;
263 }
264#endif
265 if (!scc_initialized)
266 xmon_init_scc();
267 ct = 0;
268 for (i = 0; i < nb; ++i) {
269 while ((*sccc & TXRDY) == 0)
270 do_poll_adb();
271 c = p[i];
272 if (c == '\n' && !ct) {
273 c = '\r';
274 ct = 1;
275 --i;
276 } else {
277 ct = 0;
278 }
279 buf_access();
280 *sccd = c;
281 eieio();
282 }
283
284 out:
285#ifdef CONFIG_SMP
286 if (!locked)
287 clear_bit(0, &xmon_write_lock);
288#endif
289 return nb;
290}
291
292int xmon_wants_key;
293int xmon_adb_keycode;
294
295#ifdef CONFIG_BOOTX_TEXT
296static int xmon_adb_shiftstate;
297
298static unsigned char xmon_keytab[128] =
299 "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */
300 "yt123465=97-80]o" /* 0x10 - 0x1f */
301 "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */
302 "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */
303 "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */
304 "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */
305
306static unsigned char xmon_shift_keytab[128] =
307 "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */
308 "YT!@#$^%+(&_*)}O" /* 0x10 - 0x1f */
309 "U{IP\rLJ\"K:|<?NM>" /* 0x20 - 0x2f */
310 "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */
311 "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */
312 "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */
313
314static int
315xmon_get_adb_key(void)
316{
317 int k, t, on;
318
319 xmon_wants_key = 1;
320 for (;;) {
321 xmon_adb_keycode = -1;
322 t = 0;
323 on = 0;
324 do {
325 if (--t < 0) {
326 on = 1 - on;
327 btext_drawchar(on? 0xdb: 0x20);
328 btext_drawchar('\b');
329 t = 200000;
330 }
331 do_poll_adb();
332 } while (xmon_adb_keycode == -1);
333 k = xmon_adb_keycode;
334 if (on)
335 btext_drawstring(" \b");
336
337 /* test for shift keys */
338 if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
339 xmon_adb_shiftstate = (k & 0x80) == 0;
340 continue;
341 }
342 if (k >= 0x80)
343 continue; /* ignore up transitions */
344 k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k];
345 if (k != 0)
346 break;
347 }
348 xmon_wants_key = 0;
349 return k;
350}
351#endif /* CONFIG_BOOTX_TEXT */
352
353int
354xmon_read(void *handle, void *ptr, int nb)
355{
356 char *p = ptr;
357 int i;
358
359#ifdef CONFIG_BOOTX_TEXT
360 if (use_screen) {
361 for (i = 0; i < nb; ++i)
362 *p++ = xmon_get_adb_key();
363 return i;
364 }
365#endif
366 if (!scc_initialized)
367 xmon_init_scc();
368 for (i = 0; i < nb; ++i) {
369 while ((*sccc & RXRDY) == 0)
370 do_poll_adb();
371 buf_access();
372 *p++ = *sccd;
373 }
374 return i;
375}
376
377int
378xmon_read_poll(void)
379{
380 if ((*sccc & RXRDY) == 0) {
381 do_poll_adb();
382 return -1;
383 }
384 buf_access();
385 return *sccd;
386}
387
388static unsigned char scc_inittab[] = {
389 13, 0, /* set baud rate divisor */
390 12, 1,
391 14, 1, /* baud rate gen enable, src=rtxc */
392 11, 0x50, /* clocks = br gen */
393 5, 0xea, /* tx 8 bits, assert DTR & RTS */
394 4, 0x46, /* x16 clock, 1 stop */
395 3, 0xc1, /* rx enable, 8 bits */
396};
397
398void
399xmon_init_scc(void)
400{
401 if ( _machine == _MACH_chrp )
402 {
403 sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */
404 sccd[0] = 12; eieio(); /* DLL = 9600 baud */
405 sccd[1] = 0; eieio();
406 sccd[2] = 0; eieio(); /* FCR = 0 */
407 sccd[3] = 3; eieio(); /* LCR = 8N1 */
408 sccd[1] = 0; eieio(); /* IER = 0 */
409 }
410 else if ( _machine == _MACH_Pmac )
411 {
412 int i, x;
413
414 if (channel_node != 0)
415 pmac_call_feature(
416 PMAC_FTR_SCC_ENABLE,
417 channel_node,
418 PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
419 printk(KERN_INFO "Serial port locked ON by debugger !\n");
420 if (via_modem && channel_node != 0) {
421 unsigned int t0;
422
423 pmac_call_feature(
424 PMAC_FTR_MODEM_ENABLE,
425 channel_node, 0, 1);
426 printk(KERN_INFO "Modem powered up by debugger !\n");
427 t0 = readtb();
428 while (readtb() - t0 < 3*TB_SPEED)
429 eieio();
430 }
431 /* use the B channel if requested */
432 if (xmon_use_sccb) {
433 sccc = (volatile unsigned char *)
434 ((unsigned long)sccc & ~0x20);
435 sccd = sccc + 0x10;
436 }
437 for (i = 20000; i != 0; --i) {
438 x = *sccc; eieio();
439 }
440 *sccc = 9; eieio(); /* reset A or B side */
441 *sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio();
442 for (i = 0; i < sizeof(scc_inittab); ++i) {
443 *sccc = scc_inittab[i];
444 eieio();
445 }
446 }
447 scc_initialized = 1;
448 if (via_modem) {
449 for (;;) {
450 xmon_write(NULL, "ATE1V1\r", 7);
451 if (xmon_expect("OK", 5)) {
452 xmon_write(NULL, "ATA\r", 4);
453 if (xmon_expect("CONNECT", 40))
454 break;
455 }
456 xmon_write(NULL, "+++", 3);
457 xmon_expect("OK", 3);
458 }
459 }
460}
461
462#if 0
463extern int (*prom_entry)(void *);
464
465int
466xmon_exit(void)
467{
468 struct prom_args {
469 char *service;
470 } args;
471
472 for (;;) {
473 args.service = "exit";
474 (*prom_entry)(&args);
475 }
476}
477#endif
478
479void *xmon_stdin;
480void *xmon_stdout;
481void *xmon_stderr;
482
483void
Paul Mackerrasfd582ec2005-10-11 22:08:12 +1000484xmon_init(int arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
Paul Mackerrasfd582ec2005-10-11 22:08:12 +1000486 xmon_map_scc();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487}
488
489int
490xmon_putc(int c, void *f)
491{
492 char ch = c;
493
494 if (c == '\n')
495 xmon_putc('\r', f);
496 return xmon_write(f, &ch, 1) == 1? c: -1;
497}
498
499int
500xmon_putchar(int c)
501{
502 return xmon_putc(c, xmon_stdout);
503}
504
505int
506xmon_fputs(char *str, void *f)
507{
508 int n = strlen(str);
509
510 return xmon_write(f, str, n) == n? 0: -1;
511}
512
513int
514xmon_readchar(void)
515{
516 char ch;
517
518 for (;;) {
519 switch (xmon_read(xmon_stdin, &ch, 1)) {
520 case 1:
521 return ch;
522 case -1:
523 xmon_printf("read(stdin) returned -1\r\n", 0, 0);
524 return -1;
525 }
526 }
527}
528
529static char line[256];
530static char *lineptr;
531static int lineleft;
532
533int xmon_expect(const char *str, unsigned int timeout)
534{
535 int c;
536 unsigned int t0;
537
538 timeout *= TB_SPEED;
539 t0 = readtb();
540 do {
541 lineptr = line;
542 for (;;) {
543 c = xmon_read_poll();
544 if (c == -1) {
545 if (readtb() - t0 > timeout)
546 return 0;
547 continue;
548 }
549 if (c == '\n')
550 break;
551 if (c != '\r' && lineptr < &line[sizeof(line) - 1])
552 *lineptr++ = c;
553 }
554 *lineptr = 0;
555 } while (strstr(line, str) == NULL);
556 return 1;
557}
558
559int
560xmon_getchar(void)
561{
562 int c;
563
564 if (lineleft == 0) {
565 lineptr = line;
566 for (;;) {
567 c = xmon_readchar();
568 if (c == -1 || c == 4)
569 break;
570 if (c == '\r' || c == '\n') {
571 *lineptr++ = '\n';
572 xmon_putchar('\n');
573 break;
574 }
575 switch (c) {
576 case 0177:
577 case '\b':
578 if (lineptr > line) {
579 xmon_putchar('\b');
580 xmon_putchar(' ');
581 xmon_putchar('\b');
582 --lineptr;
583 }
584 break;
585 case 'U' & 0x1F:
586 while (lineptr > line) {
587 xmon_putchar('\b');
588 xmon_putchar(' ');
589 xmon_putchar('\b');
590 --lineptr;
591 }
592 break;
593 default:
594 if (lineptr >= &line[sizeof(line) - 1])
595 xmon_putchar('\a');
596 else {
597 xmon_putchar(c);
598 *lineptr++ = c;
599 }
600 }
601 }
602 lineleft = lineptr - line;
603 lineptr = line;
604 }
605 if (lineleft == 0)
606 return -1;
607 --lineleft;
608 return *lineptr++;
609}
610
611char *
612xmon_fgets(char *str, int nb, void *f)
613{
614 char *p;
615 int c;
616
617 for (p = str; p < str + nb - 1; ) {
618 c = xmon_getchar();
619 if (c == -1) {
620 if (p == str)
621 return NULL;
622 break;
623 }
624 *p++ = c;
625 if (c == '\n')
626 break;
627 }
628 *p = 0;
629 return str;
630}
631
632void
633xmon_enter(void)
634{
635#ifdef CONFIG_ADB_PMU
636 if (_machine == _MACH_Pmac) {
637 pmu_suspend();
638 }
639#endif
640}
641
642void
643xmon_leave(void)
644{
645#ifdef CONFIG_ADB_PMU
646 if (_machine == _MACH_Pmac) {
647 pmu_resume();
648 }
649#endif
650}