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