blob: c2464df4217eac408c51e4b2b18ca47fb9e91a54 [file] [log] [blame]
Paul Mackerrasf78541d2005-10-28 22:53:37 +10001/*
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>
Paul Mackerrasf78541d2005-10-28 22:53:37 +100014#include <linux/bitops.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>
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +110024#include <asm/time.h>
25#include "nonstdio.h"
Paul Mackerrasf78541d2005-10-28 22:53:37 +100026
27static volatile unsigned char __iomem *sccc, *sccd;
28unsigned int TXRDY, RXRDY, DLAB;
Paul Mackerrasf78541d2005-10-28 22:53:37 +100029
30static int use_serial;
31static int use_screen;
32static int via_modem;
33static int xmon_use_sccb;
34static struct device_node *channel_node;
35
Paul Mackerrasf78541d2005-10-28 22:53:37 +100036void buf_access(void)
37{
38 if (DLAB)
39 sccd[3] &= ~DLAB; /* reset DLAB */
40}
41
42extern int adb_init(void);
43
44#ifdef CONFIG_PPC_CHRP
45/*
46 * This looks in the "ranges" property for the primary PCI host bridge
47 * to find the physical address of the start of PCI/ISA I/O space.
48 * It is basically a cut-down version of pci_process_bridge_OF_ranges.
49 */
50static unsigned long chrp_find_phys_io_base(void)
51{
52 struct device_node *node;
53 unsigned int *ranges;
54 unsigned long base = CHRP_ISA_IO_BASE;
55 int rlen = 0;
56 int np;
57
58 node = find_devices("isa");
59 if (node != NULL) {
60 node = node->parent;
61 if (node == NULL || node->type == NULL
62 || strcmp(node->type, "pci") != 0)
63 node = NULL;
64 }
65 if (node == NULL)
66 node = find_devices("pci");
67 if (node == NULL)
68 return base;
69
70 ranges = (unsigned int *) get_property(node, "ranges", &rlen);
71 np = prom_n_addr_cells(node) + 5;
72 while ((rlen -= np * sizeof(unsigned int)) >= 0) {
73 if ((ranges[0] >> 24) == 1 && ranges[2] == 0) {
74 /* I/O space starting at 0, grab the phys base */
75 base = ranges[np - 3];
76 break;
77 }
78 ranges += np;
79 }
80 return base;
81}
82#endif /* CONFIG_PPC_CHRP */
83
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +110084void xmon_map_scc(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +100085{
86#ifdef CONFIG_PPC_MULTIPLATFORM
87 volatile unsigned char __iomem *base;
88
89 if (_machine == _MACH_Pmac) {
90 struct device_node *np;
91 unsigned long addr;
92#ifdef CONFIG_BOOTX_TEXT
93 if (!use_screen && !use_serial
94 && !machine_is_compatible("iMac")) {
95 /* see if there is a keyboard in the device tree
96 with a parent of type "adb" */
97 for (np = find_devices("keyboard"); np; np = np->next)
98 if (np->parent && np->parent->type
99 && strcmp(np->parent->type, "adb") == 0)
100 break;
101
102 /* needs to be hacked if xmon_printk is to be used
103 from within find_via_pmu() */
104#ifdef CONFIG_ADB_PMU
105 if (np != NULL && boot_text_mapped && find_via_pmu())
106 use_screen = 1;
107#endif
108#ifdef CONFIG_ADB_CUDA
109 if (np != NULL && boot_text_mapped && find_via_cuda())
110 use_screen = 1;
111#endif
112 }
113 if (!use_screen && (np = find_devices("escc")) != NULL) {
114 /*
115 * look for the device node for the serial port
116 * we're using and see if it says it has a modem
117 */
118 char *name = xmon_use_sccb? "ch-b": "ch-a";
119 char *slots;
120 int l;
121
122 np = np->child;
123 while (np != NULL && strcmp(np->name, name) != 0)
124 np = np->sibling;
125 if (np != NULL) {
126 /* XXX should parse this properly */
127 channel_node = np;
128 slots = get_property(np, "slot-names", &l);
129 if (slots != NULL && l >= 10
130 && strcmp(slots+4, "Modem") == 0)
131 via_modem = 1;
132 }
133 }
134 btext_drawstring("xmon uses ");
135 if (use_screen)
136 btext_drawstring("screen and keyboard\n");
137 else {
138 if (via_modem)
139 btext_drawstring("modem on ");
140 btext_drawstring(xmon_use_sccb? "printer": "modem");
141 btext_drawstring(" port\n");
142 }
143
144#endif /* CONFIG_BOOTX_TEXT */
145
146#ifdef CHRP_ESCC
147 addr = 0xc1013020;
148#else
149 addr = 0xf3013020;
150#endif
151 TXRDY = 4;
152 RXRDY = 1;
153
154 np = find_devices("mac-io");
155 if (np && np->n_addrs)
156 addr = np->addrs[0].address + 0x13020;
157 base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
158 sccc = base + (addr & ~PAGE_MASK);
159 sccd = sccc + 0x10;
160
161 } else {
162 base = (volatile unsigned char *) isa_io_base;
163
164#ifdef CONFIG_PPC_CHRP
165 if (_machine == _MACH_chrp)
166 base = (volatile unsigned char __iomem *)
167 ioremap(chrp_find_phys_io_base(), 0x1000);
168#endif
169
170 sccc = base + 0x3fd;
171 sccd = base + 0x3f8;
172 if (xmon_use_sccb) {
173 sccc -= 0x100;
174 sccd -= 0x100;
175 }
176 TXRDY = 0x20;
177 RXRDY = 1;
178 DLAB = 0x80;
179 }
180#elif defined(CONFIG_GEMINI)
181 /* should already be mapped by the kernel boot */
182 sccc = (volatile unsigned char __iomem *) 0xffeffb0d;
183 sccd = (volatile unsigned char __iomem *) 0xffeffb08;
184 TXRDY = 0x20;
185 RXRDY = 1;
186 DLAB = 0x80;
187#elif defined(CONFIG_405GP)
188 sccc = (volatile unsigned char __iomem *)0xef600305;
189 sccd = (volatile unsigned char __iomem *)0xef600300;
190 TXRDY = 0x20;
191 RXRDY = 1;
192 DLAB = 0x80;
193#endif /* platform */
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000194}
195
196static int scc_initialized = 0;
197
198void xmon_init_scc(void);
199extern void cuda_poll(void);
200
201static inline void do_poll_adb(void)
202{
203#ifdef CONFIG_ADB_PMU
204 if (sys_ctrler == SYS_CTRLER_PMU)
205 pmu_poll_adb();
206#endif /* CONFIG_ADB_PMU */
207#ifdef CONFIG_ADB_CUDA
208 if (sys_ctrler == SYS_CTRLER_CUDA)
209 cuda_poll();
210#endif /* CONFIG_ADB_CUDA */
211}
212
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100213int xmon_write(void *ptr, int nb)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000214{
215 char *p = ptr;
216 int i, c, ct;
217
218#ifdef CONFIG_SMP
219 static unsigned long xmon_write_lock;
220 int lock_wait = 1000000;
221 int locked;
222
223 while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0)
224 if (--lock_wait == 0)
225 break;
226#endif
227
228#ifdef CONFIG_BOOTX_TEXT
229 if (use_screen) {
230 /* write it on the screen */
231 for (i = 0; i < nb; ++i)
232 btext_drawchar(*p++);
233 goto out;
234 }
235#endif
236 if (!scc_initialized)
237 xmon_init_scc();
238 ct = 0;
239 for (i = 0; i < nb; ++i) {
240 while ((*sccc & TXRDY) == 0)
241 do_poll_adb();
242 c = p[i];
243 if (c == '\n' && !ct) {
244 c = '\r';
245 ct = 1;
246 --i;
247 } else {
248 ct = 0;
249 }
250 buf_access();
251 *sccd = c;
252 eieio();
253 }
254
255 out:
256#ifdef CONFIG_SMP
257 if (!locked)
258 clear_bit(0, &xmon_write_lock);
259#endif
260 return nb;
261}
262
263int xmon_wants_key;
264int xmon_adb_keycode;
265
266#ifdef CONFIG_BOOTX_TEXT
267static int xmon_adb_shiftstate;
268
269static unsigned char xmon_keytab[128] =
270 "asdfhgzxcv\000bqwer" /* 0x00 - 0x0f */
271 "yt123465=97-80]o" /* 0x10 - 0x1f */
272 "u[ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */
273 "\t `\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */
274 "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */
275 "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */
276
277static unsigned char xmon_shift_keytab[128] =
278 "ASDFHGZXCV\000BQWER" /* 0x00 - 0x0f */
279 "YT!@#$^%+(&_*)}O" /* 0x10 - 0x1f */
280 "U{IP\rLJ\"K:|<?NM>" /* 0x20 - 0x2f */
281 "\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0" /* 0x30 - 0x3f */
282 "\0.\0*\0+\0\0\0\0\0/\r\0-\0" /* 0x40 - 0x4f */
283 "\0\0000123456789\0\0\0"; /* 0x50 - 0x5f */
284
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100285static int xmon_get_adb_key(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000286{
287 int k, t, on;
288
289 xmon_wants_key = 1;
290 for (;;) {
291 xmon_adb_keycode = -1;
292 t = 0;
293 on = 0;
294 do {
295 if (--t < 0) {
296 on = 1 - on;
297 btext_drawchar(on? 0xdb: 0x20);
298 btext_drawchar('\b');
299 t = 200000;
300 }
301 do_poll_adb();
302 } while (xmon_adb_keycode == -1);
303 k = xmon_adb_keycode;
304 if (on)
305 btext_drawstring(" \b");
306
307 /* test for shift keys */
308 if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
309 xmon_adb_shiftstate = (k & 0x80) == 0;
310 continue;
311 }
312 if (k >= 0x80)
313 continue; /* ignore up transitions */
314 k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k];
315 if (k != 0)
316 break;
317 }
318 xmon_wants_key = 0;
319 return k;
320}
321#endif /* CONFIG_BOOTX_TEXT */
322
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100323int xmon_readchar(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000324{
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000325#ifdef CONFIG_BOOTX_TEXT
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100326 if (use_screen)
327 return xmon_get_adb_key();
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000328#endif
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100329 if (!scc_initialized)
330 xmon_init_scc();
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000331 while ((*sccc & RXRDY) == 0)
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100332 do_poll_adb();
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000333 buf_access();
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100334 return *sccd;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000335}
336
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100337int xmon_read_poll(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000338{
339 if ((*sccc & RXRDY) == 0) {
340 do_poll_adb();
341 return -1;
342 }
343 buf_access();
344 return *sccd;
345}
346
347static unsigned char scc_inittab[] = {
348 13, 0, /* set baud rate divisor */
349 12, 1,
350 14, 1, /* baud rate gen enable, src=rtxc */
351 11, 0x50, /* clocks = br gen */
352 5, 0xea, /* tx 8 bits, assert DTR & RTS */
353 4, 0x46, /* x16 clock, 1 stop */
354 3, 0xc1, /* rx enable, 8 bits */
355};
356
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100357void xmon_init_scc(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000358{
359 if ( _machine == _MACH_chrp )
360 {
361 sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */
362 sccd[0] = 12; eieio(); /* DLL = 9600 baud */
363 sccd[1] = 0; eieio();
364 sccd[2] = 0; eieio(); /* FCR = 0 */
365 sccd[3] = 3; eieio(); /* LCR = 8N1 */
366 sccd[1] = 0; eieio(); /* IER = 0 */
367 }
368 else if ( _machine == _MACH_Pmac )
369 {
370 int i, x;
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100371 unsigned long timeout;
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000372
373 if (channel_node != 0)
374 pmac_call_feature(
375 PMAC_FTR_SCC_ENABLE,
376 channel_node,
377 PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
378 printk(KERN_INFO "Serial port locked ON by debugger !\n");
379 if (via_modem && channel_node != 0) {
380 unsigned int t0;
381
382 pmac_call_feature(
383 PMAC_FTR_MODEM_ENABLE,
384 channel_node, 0, 1);
385 printk(KERN_INFO "Modem powered up by debugger !\n");
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100386 t0 = get_tbl();
387 timeout = 3 * tb_ticks_per_sec;
388 if (timeout == 0)
389 /* assume 25MHz if tb_ticks_per_sec not set */
390 timeout = 75000000;
391 while (get_tbl() - t0 < timeout)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000392 eieio();
393 }
394 /* use the B channel if requested */
395 if (xmon_use_sccb) {
396 sccc = (volatile unsigned char *)
397 ((unsigned long)sccc & ~0x20);
398 sccd = sccc + 0x10;
399 }
400 for (i = 20000; i != 0; --i) {
401 x = *sccc; eieio();
402 }
403 *sccc = 9; eieio(); /* reset A or B side */
404 *sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio();
405 for (i = 0; i < sizeof(scc_inittab); ++i) {
406 *sccc = scc_inittab[i];
407 eieio();
408 }
409 }
410 scc_initialized = 1;
411 if (via_modem) {
412 for (;;) {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100413 xmon_write("ATE1V1\r", 7);
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000414 if (xmon_expect("OK", 5)) {
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100415 xmon_write("ATA\r", 4);
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000416 if (xmon_expect("CONNECT", 40))
417 break;
418 }
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100419 xmon_write("+++", 3);
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000420 xmon_expect("OK", 3);
421 }
422 }
423}
424
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100425void xmon_enter(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000426{
427#ifdef CONFIG_ADB_PMU
428 if (_machine == _MACH_Pmac) {
429 pmu_suspend();
430 }
431#endif
432}
433
Paul Mackerrasfca5dcd2005-11-08 22:55:08 +1100434void xmon_leave(void)
Paul Mackerrasf78541d2005-10-28 22:53:37 +1000435{
436#ifdef CONFIG_ADB_PMU
437 if (_machine == _MACH_Pmac) {
438 pmu_resume();
439 }
440#endif
441}