blob: 4344cbe9b5c5238c98dd66818e9ac6e5d8832a5a [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/processor.h>
22#include <asm/delay.h>
23#include <asm/btext.h>
24
25static volatile unsigned char *sccc, *sccd;
26unsigned int TXRDY, RXRDY, DLAB;
27static int xmon_expect(const char *str, unsigned int timeout);
28
Linus Torvalds1da177e2005-04-16 15:20:36 -070029static int use_screen;
30static int via_modem;
31static int xmon_use_sccb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#define TB_SPEED 25000000
34
35static inline unsigned int readtb(void)
36{
37 unsigned int ret;
38
39 asm volatile("mftb %0" : "=r" (ret) :);
40 return ret;
41}
42
43void buf_access(void)
44{
45 if (DLAB)
46 sccd[3] &= ~DLAB; /* reset DLAB */
47}
48
49extern int adb_init(void);
50
51#ifdef CONFIG_PPC_CHRP
52/*
53 * This looks in the "ranges" property for the primary PCI host bridge
54 * to find the physical address of the start of PCI/ISA I/O space.
55 * It is basically a cut-down version of pci_process_bridge_OF_ranges.
56 */
57static unsigned long chrp_find_phys_io_base(void)
58{
59 struct device_node *node;
60 unsigned int *ranges;
61 unsigned long base = CHRP_ISA_IO_BASE;
62 int rlen = 0;
63 int np;
64
65 node = find_devices("isa");
66 if (node != NULL) {
67 node = node->parent;
68 if (node == NULL || node->type == NULL
69 || strcmp(node->type, "pci") != 0)
70 node = NULL;
71 }
72 if (node == NULL)
73 node = find_devices("pci");
74 if (node == NULL)
75 return base;
76
77 ranges = (unsigned int *) get_property(node, "ranges", &rlen);
78 np = prom_n_addr_cells(node) + 5;
79 while ((rlen -= np * sizeof(unsigned int)) >= 0) {
80 if ((ranges[0] >> 24) == 1 && ranges[2] == 0) {
81 /* I/O space starting at 0, grab the phys base */
82 base = ranges[np - 3];
83 break;
84 }
85 ranges += np;
86 }
87 return base;
88}
89#endif /* CONFIG_PPC_CHRP */
90
91#ifdef CONFIG_MAGIC_SYSRQ
92static void sysrq_handle_xmon(int key, struct pt_regs *regs,
93 struct tty_struct *tty)
94{
95 xmon(regs);
96}
97
98static struct sysrq_key_op sysrq_xmon_op =
99{
100 .handler = sysrq_handle_xmon,
101 .help_msg = "Xmon",
102 .action_msg = "Entering xmon",
103};
104#endif
105
106void
107xmon_map_scc(void)
108{
109#ifdef CONFIG_PPC_MULTIPLATFORM
110 volatile unsigned char *base;
111
Benjamin Herrenschmidtd3ed6582005-11-16 13:40:43 +1100112#ifdef CONFIG_PPC_CHRP
Paul Mackerrasa7fdd902006-01-15 17:30:44 +1100113 base = (volatile unsigned char *) isa_io_base;
114 if (_machine == _MACH_chrp)
115 base = (volatile unsigned char *)
116 ioremap(chrp_find_phys_io_base(), 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Paul Mackerrasa7fdd902006-01-15 17:30:44 +1100118 sccc = base + 0x3fd;
119 sccd = base + 0x3f8;
120 if (xmon_use_sccb) {
121 sccc -= 0x100;
122 sccd -= 0x100;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
Paul Mackerrasa7fdd902006-01-15 17:30:44 +1100124 TXRDY = 0x20;
125 RXRDY = 1;
126 DLAB = 0x80;
Benjamin Herrenschmidtd3ed6582005-11-16 13:40:43 +1100127#endif /* CONFIG_PPC_CHRP */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128#elif defined(CONFIG_GEMINI)
129 /* should already be mapped by the kernel boot */
130 sccc = (volatile unsigned char *) 0xffeffb0d;
131 sccd = (volatile unsigned char *) 0xffeffb08;
132 TXRDY = 0x20;
133 RXRDY = 1;
134 DLAB = 0x80;
135#elif defined(CONFIG_405GP)
136 sccc = (volatile unsigned char *)0xef600305;
137 sccd = (volatile unsigned char *)0xef600300;
138 TXRDY = 0x20;
139 RXRDY = 1;
140 DLAB = 0x80;
141#endif /* platform */
142
143 register_sysrq_key('x', &sysrq_xmon_op);
144}
145
146static int scc_initialized = 0;
147
148void xmon_init_scc(void);
149extern void cuda_poll(void);
150
151static inline void do_poll_adb(void)
152{
153#ifdef CONFIG_ADB_PMU
154 if (sys_ctrler == SYS_CTRLER_PMU)
155 pmu_poll_adb();
156#endif /* CONFIG_ADB_PMU */
157#ifdef CONFIG_ADB_CUDA
158 if (sys_ctrler == SYS_CTRLER_CUDA)
159 cuda_poll();
160#endif /* CONFIG_ADB_CUDA */
161}
162
163int
164xmon_write(void *handle, void *ptr, int nb)
165{
166 char *p = ptr;
167 int i, c, ct;
168
169#ifdef CONFIG_SMP
170 static unsigned long xmon_write_lock;
171 int lock_wait = 1000000;
172 int locked;
173
174 while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0)
175 if (--lock_wait == 0)
176 break;
177#endif
178
179#ifdef CONFIG_BOOTX_TEXT
180 if (use_screen) {
181 /* write it on the screen */
182 for (i = 0; i < nb; ++i)
183 btext_drawchar(*p++);
184 goto out;
185 }
186#endif
187 if (!scc_initialized)
188 xmon_init_scc();
189 ct = 0;
190 for (i = 0; i < nb; ++i) {
191 while ((*sccc & TXRDY) == 0)
192 do_poll_adb();
193 c = p[i];
194 if (c == '\n' && !ct) {
195 c = '\r';
196 ct = 1;
197 --i;
198 } else {
199 ct = 0;
200 }
201 buf_access();
202 *sccd = c;
203 eieio();
204 }
205
206 out:
207#ifdef CONFIG_SMP
208 if (!locked)
209 clear_bit(0, &xmon_write_lock);
210#endif
211 return nb;
212}
213
214int xmon_wants_key;
215int xmon_adb_keycode;
216
217#ifdef CONFIG_BOOTX_TEXT
218static int xmon_adb_shiftstate;
219
220static unsigned char xmon_keytab[128] =
221 "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */
222 "yt123465=97-80]o" /* 0x10 - 0x1f */
223 "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */
224 "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */
225 "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */
226 "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */
227
228static unsigned char xmon_shift_keytab[128] =
229 "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */
230 "YT!@#$^%+(&_*)}O" /* 0x10 - 0x1f */
231 "U{IP\rLJ\"K:|<?NM>" /* 0x20 - 0x2f */
232 "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */
233 "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */
234 "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */
235
236static int
237xmon_get_adb_key(void)
238{
239 int k, t, on;
240
241 xmon_wants_key = 1;
242 for (;;) {
243 xmon_adb_keycode = -1;
244 t = 0;
245 on = 0;
246 do {
247 if (--t < 0) {
248 on = 1 - on;
249 btext_drawchar(on? 0xdb: 0x20);
250 btext_drawchar('\b');
251 t = 200000;
252 }
253 do_poll_adb();
254 } while (xmon_adb_keycode == -1);
255 k = xmon_adb_keycode;
256 if (on)
257 btext_drawstring(" \b");
258
259 /* test for shift keys */
260 if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
261 xmon_adb_shiftstate = (k & 0x80) == 0;
262 continue;
263 }
264 if (k >= 0x80)
265 continue; /* ignore up transitions */
266 k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k];
267 if (k != 0)
268 break;
269 }
270 xmon_wants_key = 0;
271 return k;
272}
273#endif /* CONFIG_BOOTX_TEXT */
274
275int
276xmon_read(void *handle, void *ptr, int nb)
277{
278 char *p = ptr;
279 int i;
280
281#ifdef CONFIG_BOOTX_TEXT
282 if (use_screen) {
283 for (i = 0; i < nb; ++i)
284 *p++ = xmon_get_adb_key();
285 return i;
286 }
287#endif
288 if (!scc_initialized)
289 xmon_init_scc();
290 for (i = 0; i < nb; ++i) {
291 while ((*sccc & RXRDY) == 0)
292 do_poll_adb();
293 buf_access();
294 *p++ = *sccd;
295 }
296 return i;
297}
298
299int
300xmon_read_poll(void)
301{
302 if ((*sccc & RXRDY) == 0) {
303 do_poll_adb();
304 return -1;
305 }
306 buf_access();
307 return *sccd;
308}
309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310void
311xmon_init_scc(void)
312{
313 if ( _machine == _MACH_chrp )
314 {
315 sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */
316 sccd[0] = 12; eieio(); /* DLL = 9600 baud */
317 sccd[1] = 0; eieio();
318 sccd[2] = 0; eieio(); /* FCR = 0 */
319 sccd[3] = 3; eieio(); /* LCR = 8N1 */
320 sccd[1] = 0; eieio(); /* IER = 0 */
321 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 scc_initialized = 1;
323 if (via_modem) {
324 for (;;) {
325 xmon_write(NULL, "ATE1V1\r", 7);
326 if (xmon_expect("OK", 5)) {
327 xmon_write(NULL, "ATA\r", 4);
328 if (xmon_expect("CONNECT", 40))
329 break;
330 }
331 xmon_write(NULL, "+++", 3);
332 xmon_expect("OK", 3);
333 }
334 }
335}
336
337#if 0
338extern int (*prom_entry)(void *);
339
340int
341xmon_exit(void)
342{
343 struct prom_args {
344 char *service;
345 } args;
346
347 for (;;) {
348 args.service = "exit";
349 (*prom_entry)(&args);
350 }
351}
352#endif
353
354void *xmon_stdin;
355void *xmon_stdout;
356void *xmon_stderr;
357
358void
Paul Mackerrasfd582ec2005-10-11 22:08:12 +1000359xmon_init(int arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360{
Paul Mackerrasfd582ec2005-10-11 22:08:12 +1000361 xmon_map_scc();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362}
363
364int
365xmon_putc(int c, void *f)
366{
367 char ch = c;
368
369 if (c == '\n')
370 xmon_putc('\r', f);
371 return xmon_write(f, &ch, 1) == 1? c: -1;
372}
373
374int
375xmon_putchar(int c)
376{
377 return xmon_putc(c, xmon_stdout);
378}
379
380int
381xmon_fputs(char *str, void *f)
382{
383 int n = strlen(str);
384
385 return xmon_write(f, str, n) == n? 0: -1;
386}
387
388int
389xmon_readchar(void)
390{
391 char ch;
392
393 for (;;) {
394 switch (xmon_read(xmon_stdin, &ch, 1)) {
395 case 1:
396 return ch;
397 case -1:
398 xmon_printf("read(stdin) returned -1\r\n", 0, 0);
399 return -1;
400 }
401 }
402}
403
404static char line[256];
405static char *lineptr;
406static int lineleft;
407
408int xmon_expect(const char *str, unsigned int timeout)
409{
410 int c;
411 unsigned int t0;
412
413 timeout *= TB_SPEED;
414 t0 = readtb();
415 do {
416 lineptr = line;
417 for (;;) {
418 c = xmon_read_poll();
419 if (c == -1) {
420 if (readtb() - t0 > timeout)
421 return 0;
422 continue;
423 }
424 if (c == '\n')
425 break;
426 if (c != '\r' && lineptr < &line[sizeof(line) - 1])
427 *lineptr++ = c;
428 }
429 *lineptr = 0;
430 } while (strstr(line, str) == NULL);
431 return 1;
432}
433
434int
435xmon_getchar(void)
436{
437 int c;
438
439 if (lineleft == 0) {
440 lineptr = line;
441 for (;;) {
442 c = xmon_readchar();
443 if (c == -1 || c == 4)
444 break;
445 if (c == '\r' || c == '\n') {
446 *lineptr++ = '\n';
447 xmon_putchar('\n');
448 break;
449 }
450 switch (c) {
451 case 0177:
452 case '\b':
453 if (lineptr > line) {
454 xmon_putchar('\b');
455 xmon_putchar(' ');
456 xmon_putchar('\b');
457 --lineptr;
458 }
459 break;
460 case 'U' & 0x1F:
461 while (lineptr > line) {
462 xmon_putchar('\b');
463 xmon_putchar(' ');
464 xmon_putchar('\b');
465 --lineptr;
466 }
467 break;
468 default:
469 if (lineptr >= &line[sizeof(line) - 1])
470 xmon_putchar('\a');
471 else {
472 xmon_putchar(c);
473 *lineptr++ = c;
474 }
475 }
476 }
477 lineleft = lineptr - line;
478 lineptr = line;
479 }
480 if (lineleft == 0)
481 return -1;
482 --lineleft;
483 return *lineptr++;
484}
485
486char *
487xmon_fgets(char *str, int nb, void *f)
488{
489 char *p;
490 int c;
491
492 for (p = str; p < str + nb - 1; ) {
493 c = xmon_getchar();
494 if (c == -1) {
495 if (p == str)
496 return NULL;
497 break;
498 }
499 *p++ = c;
500 if (c == '\n')
501 break;
502 }
503 *p = 0;
504 return str;
505}
506
507void
508xmon_enter(void)
509{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510}
511
512void
513xmon_leave(void)
514{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515}