1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Driver for the 98626/98644/internal serial interface on hp300/hp400 |
4 | * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) |
5 | * |
6 | * Ported from 2.2 and modified to use the normal 8250 driver |
7 | * by Kars de Jong <jongk@linux-m68k.org>, May 2004. |
8 | */ |
9 | #include <linux/module.h> |
10 | #include <linux/init.h> |
11 | #include <linux/string.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/serial.h> |
14 | #include <linux/serial_8250.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/dio.h> |
17 | #include <linux/console.h> |
18 | #include <linux/slab.h> |
19 | #include <asm/io.h> |
20 | |
21 | #include "8250.h" |
22 | |
23 | #if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) && !defined(CONFIG_COMPILE_TEST) |
24 | #warning CONFIG_SERIAL_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? |
25 | #endif |
26 | |
27 | #ifdef CONFIG_HPAPCI |
28 | struct hp300_port { |
29 | struct hp300_port *next; /* next port */ |
30 | int line; /* line (tty) number */ |
31 | }; |
32 | |
33 | static struct hp300_port *hp300_ports; |
34 | #endif |
35 | |
36 | #ifdef CONFIG_HPDCA |
37 | |
38 | static int hpdca_init_one(struct dio_dev *d, |
39 | const struct dio_device_id *ent); |
40 | static void hpdca_remove_one(struct dio_dev *d); |
41 | |
42 | static struct dio_device_id hpdca_dio_tbl[] = { |
43 | { DIO_ID_DCA0 }, |
44 | { DIO_ID_DCA0REM }, |
45 | { DIO_ID_DCA1 }, |
46 | { DIO_ID_DCA1REM }, |
47 | { 0 } |
48 | }; |
49 | |
50 | static struct dio_driver hpdca_driver = { |
51 | .name = "hpdca" , |
52 | .id_table = hpdca_dio_tbl, |
53 | .probe = hpdca_init_one, |
54 | .remove = hpdca_remove_one, |
55 | }; |
56 | |
57 | #endif |
58 | |
59 | static unsigned int num_ports; |
60 | |
61 | extern int hp300_uart_scode; |
62 | |
63 | /* Offset to UART registers from base of DCA */ |
64 | #define UART_OFFSET 17 |
65 | |
66 | #define DCA_ID 0x01 /* ID (read), reset (write) */ |
67 | #define DCA_IC 0x03 /* Interrupt control */ |
68 | |
69 | /* Interrupt control */ |
70 | #define DCA_IC_IE 0x80 /* Master interrupt enable */ |
71 | |
72 | #define HPDCA_BAUD_BASE 153600 |
73 | |
74 | /* Base address of the Frodo part */ |
75 | #define FRODO_BASE (0x41c000) |
76 | |
77 | /* |
78 | * Where we find the 8250-like APCI ports, and how far apart they are. |
79 | */ |
80 | #define FRODO_APCIBASE 0x0 |
81 | #define FRODO_APCISPACE 0x20 |
82 | #define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE)) |
83 | |
84 | #define HPAPCI_BAUD_BASE 500400 |
85 | |
86 | #ifdef CONFIG_SERIAL_8250_CONSOLE |
87 | /* |
88 | * Parse the bootinfo to find descriptions for headless console and |
89 | * debug serial ports and register them with the 8250 driver. |
90 | */ |
91 | int __init hp300_setup_serial_console(void) |
92 | { |
93 | int scode; |
94 | struct uart_port port; |
95 | |
96 | memset(&port, 0, sizeof(port)); |
97 | |
98 | if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX) |
99 | return 0; |
100 | |
101 | if (DIO_SCINHOLE(hp300_uart_scode)) |
102 | return 0; |
103 | |
104 | scode = hp300_uart_scode; |
105 | |
106 | /* Memory mapped I/O */ |
107 | port.iotype = UPIO_MEM; |
108 | port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; |
109 | port.type = PORT_UNKNOWN; |
110 | |
111 | /* Check for APCI console */ |
112 | if (scode == 256) { |
113 | #ifdef CONFIG_HPAPCI |
114 | pr_info("Serial console is HP APCI 1\n" ); |
115 | |
116 | port.uartclk = HPAPCI_BAUD_BASE * 16; |
117 | port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1)); |
118 | port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); |
119 | port.regshift = 2; |
120 | add_preferred_console("ttyS" , port.line, "9600n8" ); |
121 | #else |
122 | pr_warn("Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n" ); |
123 | return 0; |
124 | #endif |
125 | } else { |
126 | #ifdef CONFIG_HPDCA |
127 | unsigned long pa = dio_scodetophysaddr(scode); |
128 | if (!pa) |
129 | return 0; |
130 | |
131 | pr_info("Serial console is HP DCA at select code %d\n" , scode); |
132 | |
133 | port.uartclk = HPDCA_BAUD_BASE * 16; |
134 | port.mapbase = (pa + UART_OFFSET); |
135 | port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); |
136 | port.regshift = 1; |
137 | port.irq = DIO_IPL(pa + DIO_VIRADDRBASE); |
138 | |
139 | /* Enable board-interrupts */ |
140 | out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); |
141 | |
142 | if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) |
143 | add_preferred_console("ttyS" , port.line, "9600n8" ); |
144 | #else |
145 | pr_warn("Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n" ); |
146 | return 0; |
147 | #endif |
148 | } |
149 | |
150 | if (early_serial_setup(port: &port) < 0) |
151 | pr_warn("%s: early_serial_setup() failed.\n" , __func__); |
152 | return 0; |
153 | } |
154 | #endif /* CONFIG_SERIAL_8250_CONSOLE */ |
155 | |
156 | #ifdef CONFIG_HPDCA |
157 | static int hpdca_init_one(struct dio_dev *d, |
158 | const struct dio_device_id *ent) |
159 | { |
160 | struct uart_8250_port uart; |
161 | int line; |
162 | |
163 | #ifdef CONFIG_SERIAL_8250_CONSOLE |
164 | if (hp300_uart_scode == d->scode) { |
165 | /* Already got it. */ |
166 | return 0; |
167 | } |
168 | #endif |
169 | memset(&uart, 0, sizeof(uart)); |
170 | |
171 | /* Memory mapped I/O */ |
172 | uart.port.iotype = UPIO_MEM; |
173 | uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; |
174 | uart.port.irq = d->ipl; |
175 | uart.port.uartclk = HPDCA_BAUD_BASE * 16; |
176 | uart.port.mapbase = (d->resource.start + UART_OFFSET); |
177 | uart.port.membase = (char *)(uart.port.mapbase + DIO_VIRADDRBASE); |
178 | uart.port.regshift = 1; |
179 | uart.port.dev = &d->dev; |
180 | line = serial8250_register_8250_port(&uart); |
181 | |
182 | if (line < 0) { |
183 | dev_notice(&d->dev, |
184 | "8250_hp300: register_serial() DCA scode %d irq %d failed\n" , |
185 | d->scode, uart.port.irq); |
186 | return -ENOMEM; |
187 | } |
188 | |
189 | /* Enable board-interrupts */ |
190 | out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); |
191 | dio_set_drvdata(d, (void *)line); |
192 | |
193 | /* Reset the DCA */ |
194 | out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff); |
195 | udelay(100); |
196 | |
197 | num_ports++; |
198 | |
199 | return 0; |
200 | } |
201 | #endif |
202 | |
203 | static int __init hp300_8250_init(void) |
204 | { |
205 | static int called; |
206 | #ifdef CONFIG_HPAPCI |
207 | int line; |
208 | unsigned long base; |
209 | struct uart_8250_port uart; |
210 | struct hp300_port *port; |
211 | int i; |
212 | #endif |
213 | if (called) |
214 | return -ENODEV; |
215 | called = 1; |
216 | |
217 | if (!MACH_IS_HP300) |
218 | return -ENODEV; |
219 | |
220 | #ifdef CONFIG_HPDCA |
221 | dio_register_driver(&hpdca_driver); |
222 | #endif |
223 | #ifdef CONFIG_HPAPCI |
224 | if (hp300_model < HP_400) { |
225 | if (!num_ports) |
226 | return -ENODEV; |
227 | return 0; |
228 | } |
229 | /* These models have the Frodo chip. |
230 | * Port 0 is reserved for the Apollo Domain keyboard. |
231 | * Port 1 is either the console or the DCA. |
232 | */ |
233 | for (i = 1; i < 4; i++) { |
234 | /* Port 1 is the console on a 425e, on other machines it's |
235 | * mapped to DCA. |
236 | */ |
237 | #ifdef CONFIG_SERIAL_8250_CONSOLE |
238 | if (i == 1) |
239 | continue; |
240 | #endif |
241 | |
242 | /* Create new serial device */ |
243 | port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL); |
244 | if (!port) |
245 | return -ENOMEM; |
246 | |
247 | memset(&uart, 0, sizeof(uart)); |
248 | |
249 | base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); |
250 | |
251 | /* Memory mapped I/O */ |
252 | uart.port.iotype = UPIO_MEM; |
253 | uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ |
254 | | UPF_BOOT_AUTOCONF; |
255 | /* XXX - no interrupt support yet */ |
256 | uart.port.irq = 0; |
257 | uart.port.uartclk = HPAPCI_BAUD_BASE * 16; |
258 | uart.port.mapbase = base; |
259 | uart.port.membase = (char *)(base + DIO_VIRADDRBASE); |
260 | uart.port.regshift = 2; |
261 | |
262 | line = serial8250_register_8250_port(&uart); |
263 | |
264 | if (line < 0) { |
265 | dev_notice(uart.port.dev, |
266 | "8250_hp300: register_serial() APCI %d irq %d failed\n" , |
267 | i, uart.port.irq); |
268 | kfree(port); |
269 | continue; |
270 | } |
271 | |
272 | port->line = line; |
273 | port->next = hp300_ports; |
274 | hp300_ports = port; |
275 | |
276 | num_ports++; |
277 | } |
278 | #endif |
279 | |
280 | /* Any boards found? */ |
281 | if (!num_ports) |
282 | return -ENODEV; |
283 | |
284 | return 0; |
285 | } |
286 | |
287 | #ifdef CONFIG_HPDCA |
288 | static void hpdca_remove_one(struct dio_dev *d) |
289 | { |
290 | int line; |
291 | |
292 | line = (int) dio_get_drvdata(d); |
293 | if (d->resource.start) { |
294 | /* Disable board-interrupts */ |
295 | out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0); |
296 | } |
297 | serial8250_unregister_port(line); |
298 | } |
299 | #endif |
300 | |
301 | static void __exit hp300_8250_exit(void) |
302 | { |
303 | #ifdef CONFIG_HPAPCI |
304 | struct hp300_port *port, *to_free; |
305 | |
306 | for (port = hp300_ports; port; ) { |
307 | serial8250_unregister_port(port->line); |
308 | to_free = port; |
309 | port = port->next; |
310 | kfree(to_free); |
311 | } |
312 | |
313 | hp300_ports = NULL; |
314 | #endif |
315 | #ifdef CONFIG_HPDCA |
316 | dio_unregister_driver(&hpdca_driver); |
317 | #endif |
318 | } |
319 | |
320 | module_init(hp300_8250_init); |
321 | module_exit(hp300_8250_exit); |
322 | MODULE_DESCRIPTION("HP DCA/APCI serial driver" ); |
323 | MODULE_AUTHOR("Kars de Jong <jongk@linux-m68k.org>" ); |
324 | MODULE_LICENSE("GPL" ); |
325 | |