Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * C interface for trapping into the standard LinuxSH BIOS. |
3 | * | |
4 | * Copyright (C) 2000 Greg Banks, Mitch Davis | |
776258df PM |
5 | * Copyright (C) 1999, 2000 Niibe Yutaka |
6 | * Copyright (C) 2002 M. R. Brown | |
7 | * Copyright (C) 2004 - 2010 Paul Mundt | |
1da177e4 | 8 | * |
776258df PM |
9 | * This file is subject to the terms and conditions of the GNU General Public |
10 | * License. See the file "COPYING" in the main directory of this archive | |
11 | * for more details. | |
1da177e4 | 12 | */ |
98d877c4 | 13 | #include <linux/module.h> |
776258df PM |
14 | #include <linux/console.h> |
15 | #include <linux/tty.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/io.h> | |
18 | #include <linux/delay.h> | |
1da177e4 LT |
19 | #include <asm/sh_bios.h> |
20 | ||
8e32018b | 21 | #define BIOS_CALL_CONSOLE_WRITE 0 |
1da177e4 LT |
22 | #define BIOS_CALL_ETH_NODE_ADDR 10 |
23 | #define BIOS_CALL_SHUTDOWN 11 | |
8e32018b | 24 | #define BIOS_CALL_GDB_DETACH 0xff |
1da177e4 | 25 | |
94cd0495 PM |
26 | void *gdb_vbr_vector = NULL; |
27 | ||
8e32018b PM |
28 | static inline long sh_bios_call(long func, long arg0, long arg1, long arg2, |
29 | long arg3) | |
1da177e4 | 30 | { |
8e32018b PM |
31 | register long r0 __asm__("r0") = func; |
32 | register long r4 __asm__("r4") = arg0; | |
33 | register long r5 __asm__("r5") = arg1; | |
34 | register long r6 __asm__("r6") = arg2; | |
35 | register long r7 __asm__("r7") = arg3; | |
1da177e4 | 36 | |
94cd0495 PM |
37 | if (!gdb_vbr_vector) |
38 | return -ENOSYS; | |
39 | ||
8e32018b PM |
40 | __asm__ __volatile__("trapa #0x3f":"=z"(r0) |
41 | :"0"(r0), "r"(r4), "r"(r5), "r"(r6), "r"(r7) | |
42 | :"memory"); | |
43 | return r0; | |
44 | } | |
1da177e4 LT |
45 | |
46 | void sh_bios_console_write(const char *buf, unsigned int len) | |
47 | { | |
8e32018b | 48 | sh_bios_call(BIOS_CALL_CONSOLE_WRITE, (long)buf, (long)len, 0, 0); |
1da177e4 LT |
49 | } |
50 | ||
1da177e4 LT |
51 | void sh_bios_gdb_detach(void) |
52 | { | |
8e32018b | 53 | sh_bios_call(BIOS_CALL_GDB_DETACH, 0, 0, 0, 0); |
1da177e4 | 54 | } |
a9df1ed9 | 55 | EXPORT_SYMBOL_GPL(sh_bios_gdb_detach); |
1da177e4 | 56 | |
8e32018b | 57 | void sh_bios_get_node_addr(unsigned char *node_addr) |
1da177e4 | 58 | { |
8e32018b | 59 | sh_bios_call(BIOS_CALL_ETH_NODE_ADDR, 0, (long)node_addr, 0, 0); |
1da177e4 | 60 | } |
a9df1ed9 | 61 | EXPORT_SYMBOL_GPL(sh_bios_get_node_addr); |
1da177e4 LT |
62 | |
63 | void sh_bios_shutdown(unsigned int how) | |
64 | { | |
8e32018b | 65 | sh_bios_call(BIOS_CALL_SHUTDOWN, how, 0, 0, 0); |
1da177e4 | 66 | } |
191d0d24 | 67 | |
191d0d24 PM |
68 | /* |
69 | * Read the old value of the VBR register to initialise the vector | |
70 | * through which debug and BIOS traps are delegated by the Linux trap | |
71 | * handler. | |
72 | */ | |
73 | void sh_bios_vbr_init(void) | |
74 | { | |
75 | unsigned long vbr; | |
76 | ||
77 | if (unlikely(gdb_vbr_vector)) | |
78 | return; | |
79 | ||
80 | __asm__ __volatile__ ("stc vbr, %0" : "=r" (vbr)); | |
81 | ||
94cd0495 PM |
82 | if (vbr) { |
83 | gdb_vbr_vector = (void *)(vbr + 0x100); | |
84 | printk(KERN_NOTICE "Setting GDB trap vector to %p\n", | |
85 | gdb_vbr_vector); | |
86 | } else | |
87 | printk(KERN_NOTICE "SH-BIOS not detected\n"); | |
191d0d24 PM |
88 | } |
89 | ||
90 | /** | |
91 | * sh_bios_vbr_reload - Re-load the system VBR from the BIOS vector. | |
92 | * | |
93 | * This can be used by save/restore code to reinitialize the system VBR | |
94 | * from the fixed BIOS VBR. A no-op if no BIOS VBR is known. | |
95 | */ | |
96 | void sh_bios_vbr_reload(void) | |
97 | { | |
98 | if (gdb_vbr_vector) | |
99 | __asm__ __volatile__ ( | |
100 | "ldc %0, vbr" | |
101 | : | |
102 | : "r" (((unsigned long) gdb_vbr_vector) - 0x100) | |
103 | : "memory" | |
104 | ); | |
105 | } | |
776258df | 106 | |
d0380e6c | 107 | #ifdef CONFIG_EARLY_PRINTK |
776258df PM |
108 | /* |
109 | * Print a string through the BIOS | |
110 | */ | |
111 | static void sh_console_write(struct console *co, const char *s, | |
112 | unsigned count) | |
113 | { | |
114 | sh_bios_console_write(s, count); | |
115 | } | |
116 | ||
117 | /* | |
118 | * Setup initial baud/bits/parity. We do two things here: | |
119 | * - construct a cflag setting for the first rs_open() | |
120 | * - initialize the serial port | |
121 | * Return non-zero if we didn't find a serial port. | |
122 | */ | |
123 | static int __init sh_console_setup(struct console *co, char *options) | |
124 | { | |
125 | int cflag = CREAD | HUPCL | CLOCAL; | |
126 | ||
127 | /* | |
128 | * Now construct a cflag setting. | |
129 | * TODO: this is a totally bogus cflag, as we have | |
130 | * no idea what serial settings the BIOS is using, or | |
131 | * even if its using the serial port at all. | |
132 | */ | |
133 | cflag |= B115200 | CS8 | /*no parity*/0; | |
134 | ||
135 | co->cflag = cflag; | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static struct console bios_console = { | |
141 | .name = "bios", | |
142 | .write = sh_console_write, | |
143 | .setup = sh_console_setup, | |
144 | .flags = CON_PRINTBUFFER, | |
145 | .index = -1, | |
146 | }; | |
147 | ||
776258df PM |
148 | static int __init setup_early_printk(char *buf) |
149 | { | |
150 | int keep_early = 0; | |
151 | ||
152 | if (!buf) | |
153 | return 0; | |
154 | ||
155 | if (strstr(buf, "keep")) | |
156 | keep_early = 1; | |
157 | ||
158 | if (!strncmp(buf, "bios", 4)) | |
159 | early_console = &bios_console; | |
160 | ||
161 | if (likely(early_console)) { | |
162 | if (keep_early) | |
163 | early_console->flags &= ~CON_BOOT; | |
164 | else | |
165 | early_console->flags |= CON_BOOT; | |
166 | register_console(early_console); | |
167 | } | |
168 | ||
169 | return 0; | |
170 | } | |
171 | early_param("earlyprintk", setup_early_printk); | |
d0380e6c | 172 | #endif |