Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / 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 <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
26 static volatile unsigned char *sccc, *sccd;
27 unsigned int TXRDY, RXRDY, DLAB;
28 static int xmon_expect(const char *str, unsigned int timeout);
29
30 static int use_serial;
31 static int use_screen;
32 static int via_modem;
33 static int xmon_use_sccb;
34 static struct device_node *channel_node;
35
36 #define TB_SPEED        25000000
37
38 static inline unsigned int readtb(void)
39 {
40         unsigned int ret;
41
42         asm volatile("mftb %0" : "=r" (ret) :);
43         return ret;
44 }
45
46 void buf_access(void)
47 {
48         if (DLAB)
49                 sccd[3] &= ~DLAB;       /* reset DLAB */
50 }
51
52 extern 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  */
60 static 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
95 static void sysrq_handle_xmon(int key, struct pt_regs *regs,
96                               struct tty_struct *tty)
97 {
98         xmon(regs);
99 }
100
101 static 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
109 void
110 xmon_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
187         } else {
188                 base = (volatile unsigned char *) isa_io_base;
189                 if (_machine == _MACH_chrp)
190                         base = (volatile unsigned char *)
191                                 ioremap(chrp_find_phys_io_base(), 0x1000);
192
193                 sccc = base + 0x3fd;
194                 sccd = base + 0x3f8;
195                 if (xmon_use_sccb) {
196                         sccc -= 0x100;
197                         sccd -= 0x100;
198                 }
199                 TXRDY = 0x20;
200                 RXRDY = 1;
201                 DLAB = 0x80;
202         }
203 #elif defined(CONFIG_GEMINI)
204         /* should already be mapped by the kernel boot */
205         sccc = (volatile unsigned char *) 0xffeffb0d;
206         sccd = (volatile unsigned char *) 0xffeffb08;
207         TXRDY = 0x20;
208         RXRDY = 1;
209         DLAB = 0x80;
210 #elif defined(CONFIG_405GP)
211         sccc = (volatile unsigned char *)0xef600305;
212         sccd = (volatile unsigned char *)0xef600300;
213         TXRDY = 0x20;
214         RXRDY = 1;
215         DLAB = 0x80;
216 #endif /* platform */
217
218         register_sysrq_key('x', &sysrq_xmon_op);
219 }
220
221 static int scc_initialized = 0;
222
223 void xmon_init_scc(void);
224 extern void cuda_poll(void);
225
226 static inline void do_poll_adb(void)
227 {
228 #ifdef CONFIG_ADB_PMU
229         if (sys_ctrler == SYS_CTRLER_PMU)
230                 pmu_poll_adb();
231 #endif /* CONFIG_ADB_PMU */
232 #ifdef CONFIG_ADB_CUDA
233         if (sys_ctrler == SYS_CTRLER_CUDA)
234                 cuda_poll();
235 #endif /* CONFIG_ADB_CUDA */
236 }
237
238 int
239 xmon_write(void *handle, void *ptr, int nb)
240 {
241         char *p = ptr;
242         int i, c, ct;
243
244 #ifdef CONFIG_SMP
245         static unsigned long xmon_write_lock;
246         int lock_wait = 1000000;
247         int locked;
248
249         while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0)
250                 if (--lock_wait == 0)
251                         break;
252 #endif
253
254 #ifdef CONFIG_BOOTX_TEXT
255         if (use_screen) {
256                 /* write it on the screen */
257                 for (i = 0; i < nb; ++i)
258                         btext_drawchar(*p++);
259                 goto out;
260         }
261 #endif
262         if (!scc_initialized)
263                 xmon_init_scc();
264         ct = 0;
265         for (i = 0; i < nb; ++i) {
266                 while ((*sccc & TXRDY) == 0)
267                         do_poll_adb();
268                 c = p[i];
269                 if (c == '\n' && !ct) {
270                         c = '\r';
271                         ct = 1;
272                         --i;
273                 } else {
274                         ct = 0;
275                 }
276                 buf_access();
277                 *sccd = c;
278                 eieio();
279         }
280
281  out:
282 #ifdef CONFIG_SMP
283         if (!locked)
284                 clear_bit(0, &xmon_write_lock);
285 #endif
286         return nb;
287 }
288
289 int xmon_wants_key;
290 int xmon_adb_keycode;
291
292 #ifdef CONFIG_BOOTX_TEXT
293 static int xmon_adb_shiftstate;
294
295 static unsigned char xmon_keytab[128] =
296         "asdfhgzxcv\000bqwer"                           /* 0x00 - 0x0f */
297         "yt123465=97-80]o"                              /* 0x10 - 0x1f */
298         "u[ip\rlj'k;\\,/nm."                            /* 0x20 - 0x2f */
299         "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0"            /* 0x30 - 0x3f */
300         "\0.\0*\0+\0\0\0\0\0/\r\0-\0"                   /* 0x40 - 0x4f */
301         "\0\0000123456789\0\0\0";                       /* 0x50 - 0x5f */
302
303 static unsigned char xmon_shift_keytab[128] =
304         "ASDFHGZXCV\000BQWER"                           /* 0x00 - 0x0f */
305         "YT!@#$^%+(&_*)}O"                              /* 0x10 - 0x1f */
306         "U{IP\rLJ\"K:|<?NM>"                            /* 0x20 - 0x2f */
307         "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0"            /* 0x30 - 0x3f */
308         "\0.\0*\0+\0\0\0\0\0/\r\0-\0"                   /* 0x40 - 0x4f */
309         "\0\0000123456789\0\0\0";                       /* 0x50 - 0x5f */
310
311 static int
312 xmon_get_adb_key(void)
313 {
314         int k, t, on;
315
316         xmon_wants_key = 1;
317         for (;;) {
318                 xmon_adb_keycode = -1;
319                 t = 0;
320                 on = 0;
321                 do {
322                         if (--t < 0) {
323                                 on = 1 - on;
324                                 btext_drawchar(on? 0xdb: 0x20);
325                                 btext_drawchar('\b');
326                                 t = 200000;
327                         }
328                         do_poll_adb();
329                 } while (xmon_adb_keycode == -1);
330                 k = xmon_adb_keycode;
331                 if (on)
332                         btext_drawstring(" \b");
333
334                 /* test for shift keys */
335                 if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
336                         xmon_adb_shiftstate = (k & 0x80) == 0;
337                         continue;
338                 }
339                 if (k >= 0x80)
340                         continue;       /* ignore up transitions */
341                 k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k];
342                 if (k != 0)
343                         break;
344         }
345         xmon_wants_key = 0;
346         return k;
347 }
348 #endif /* CONFIG_BOOTX_TEXT */
349
350 int
351 xmon_read(void *handle, void *ptr, int nb)
352 {
353     char *p = ptr;
354     int i;
355
356 #ifdef CONFIG_BOOTX_TEXT
357     if (use_screen) {
358         for (i = 0; i < nb; ++i)
359             *p++ = xmon_get_adb_key();
360         return i;
361     }
362 #endif
363     if (!scc_initialized)
364         xmon_init_scc();
365     for (i = 0; i < nb; ++i) {
366         while ((*sccc & RXRDY) == 0)
367             do_poll_adb();
368         buf_access();
369         *p++ = *sccd;
370     }
371     return i;
372 }
373
374 int
375 xmon_read_poll(void)
376 {
377         if ((*sccc & RXRDY) == 0) {
378                 do_poll_adb();
379                 return -1;
380         }
381         buf_access();
382         return *sccd;
383 }
384
385 static unsigned char scc_inittab[] = {
386     13, 0,              /* set baud rate divisor */
387     12, 1,
388     14, 1,              /* baud rate gen enable, src=rtxc */
389     11, 0x50,           /* clocks = br gen */
390     5,  0xea,           /* tx 8 bits, assert DTR & RTS */
391     4,  0x46,           /* x16 clock, 1 stop */
392     3,  0xc1,           /* rx enable, 8 bits */
393 };
394
395 void
396 xmon_init_scc(void)
397 {
398         if ( _machine == _MACH_chrp )
399         {
400                 sccd[3] = 0x83; eieio();        /* LCR = 8N1 + DLAB */
401                 sccd[0] = 12; eieio();          /* DLL = 9600 baud */
402                 sccd[1] = 0; eieio();
403                 sccd[2] = 0; eieio();           /* FCR = 0 */
404                 sccd[3] = 3; eieio();           /* LCR = 8N1 */
405                 sccd[1] = 0; eieio();           /* IER = 0 */
406         }
407         else if ( _machine == _MACH_Pmac )
408         {
409                 int i, x;
410
411                 if (channel_node != 0)
412                         pmac_call_feature(
413                                 PMAC_FTR_SCC_ENABLE,
414                                 channel_node,
415                                 PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
416                         printk(KERN_INFO "Serial port locked ON by debugger !\n");
417                 if (via_modem && channel_node != 0) {
418                         unsigned int t0;
419
420                         pmac_call_feature(
421                                 PMAC_FTR_MODEM_ENABLE,
422                                 channel_node, 0, 1);
423                         printk(KERN_INFO "Modem powered up by debugger !\n");
424                         t0 = readtb();
425                         while (readtb() - t0 < 3*TB_SPEED)
426                                 eieio();
427                 }
428                 /* use the B channel if requested */
429                 if (xmon_use_sccb) {
430                         sccc = (volatile unsigned char *)
431                                 ((unsigned long)sccc & ~0x20);
432                         sccd = sccc + 0x10;
433                 }
434                 for (i = 20000; i != 0; --i) {
435                         x = *sccc; eieio();
436                 }
437                 *sccc = 9; eieio();             /* reset A or B side */
438                 *sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio();
439                 for (i = 0; i < sizeof(scc_inittab); ++i) {
440                         *sccc = scc_inittab[i];
441                         eieio();
442                 }
443         }
444         scc_initialized = 1;
445         if (via_modem) {
446                 for (;;) {
447                         xmon_write(NULL, "ATE1V1\r", 7);
448                         if (xmon_expect("OK", 5)) {
449                                 xmon_write(NULL, "ATA\r", 4);
450                                 if (xmon_expect("CONNECT", 40))
451                                         break;
452                         }
453                         xmon_write(NULL, "+++", 3);
454                         xmon_expect("OK", 3);
455                 }
456         }
457 }
458
459 #if 0
460 extern int (*prom_entry)(void *);
461
462 int
463 xmon_exit(void)
464 {
465     struct prom_args {
466         char *service;
467     } args;
468
469     for (;;) {
470         args.service = "exit";
471         (*prom_entry)(&args);
472     }
473 }
474 #endif
475
476 void *xmon_stdin;
477 void *xmon_stdout;
478 void *xmon_stderr;
479
480 void
481 xmon_init(int arg)
482 {
483         xmon_map_scc();
484 }
485
486 int
487 xmon_putc(int c, void *f)
488 {
489     char ch = c;
490
491     if (c == '\n')
492         xmon_putc('\r', f);
493     return xmon_write(f, &ch, 1) == 1? c: -1;
494 }
495
496 int
497 xmon_putchar(int c)
498 {
499     return xmon_putc(c, xmon_stdout);
500 }
501
502 int
503 xmon_fputs(char *str, void *f)
504 {
505     int n = strlen(str);
506
507     return xmon_write(f, str, n) == n? 0: -1;
508 }
509
510 int
511 xmon_readchar(void)
512 {
513     char ch;
514
515     for (;;) {
516         switch (xmon_read(xmon_stdin, &ch, 1)) {
517         case 1:
518             return ch;
519         case -1:
520             xmon_printf("read(stdin) returned -1\r\n", 0, 0);
521             return -1;
522         }
523     }
524 }
525
526 static char line[256];
527 static char *lineptr;
528 static int lineleft;
529
530 int xmon_expect(const char *str, unsigned int timeout)
531 {
532         int c;
533         unsigned int t0;
534
535         timeout *= TB_SPEED;
536         t0 = readtb();
537         do {
538                 lineptr = line;
539                 for (;;) {
540                         c = xmon_read_poll();
541                         if (c == -1) {
542                                 if (readtb() - t0 > timeout)
543                                         return 0;
544                                 continue;
545                         }
546                         if (c == '\n')
547                                 break;
548                         if (c != '\r' && lineptr < &line[sizeof(line) - 1])
549                                 *lineptr++ = c;
550                 }
551                 *lineptr = 0;
552         } while (strstr(line, str) == NULL);
553         return 1;
554 }
555
556 int
557 xmon_getchar(void)
558 {
559     int c;
560
561     if (lineleft == 0) {
562         lineptr = line;
563         for (;;) {
564             c = xmon_readchar();
565             if (c == -1 || c == 4)
566                 break;
567             if (c == '\r' || c == '\n') {
568                 *lineptr++ = '\n';
569                 xmon_putchar('\n');
570                 break;
571             }
572             switch (c) {
573             case 0177:
574             case '\b':
575                 if (lineptr > line) {
576                     xmon_putchar('\b');
577                     xmon_putchar(' ');
578                     xmon_putchar('\b');
579                     --lineptr;
580                 }
581                 break;
582             case 'U' & 0x1F:
583                 while (lineptr > line) {
584                     xmon_putchar('\b');
585                     xmon_putchar(' ');
586                     xmon_putchar('\b');
587                     --lineptr;
588                 }
589                 break;
590             default:
591                 if (lineptr >= &line[sizeof(line) - 1])
592                     xmon_putchar('\a');
593                 else {
594                     xmon_putchar(c);
595                     *lineptr++ = c;
596                 }
597             }
598         }
599         lineleft = lineptr - line;
600         lineptr = line;
601     }
602     if (lineleft == 0)
603         return -1;
604     --lineleft;
605     return *lineptr++;
606 }
607
608 char *
609 xmon_fgets(char *str, int nb, void *f)
610 {
611     char *p;
612     int c;
613
614     for (p = str; p < str + nb - 1; ) {
615         c = xmon_getchar();
616         if (c == -1) {
617             if (p == str)
618                 return NULL;
619             break;
620         }
621         *p++ = c;
622         if (c == '\n')
623             break;
624     }
625     *p = 0;
626     return str;
627 }
628
629 void
630 xmon_enter(void)
631 {
632 #ifdef CONFIG_ADB_PMU
633         if (_machine == _MACH_Pmac) {
634                 pmu_suspend();
635         }
636 #endif
637 }
638
639 void
640 xmon_leave(void)
641 {
642 #ifdef CONFIG_ADB_PMU
643         if (_machine == _MACH_Pmac) {
644                 pmu_resume();
645         }
646 #endif
647 }