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