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