325 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the 98626/98644/internal serial interface on hp300/hp4004 * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs)5 *6 * Ported from 2.2 and modified to use the normal 8250 driver7 * 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#endif26 27#ifdef CONFIG_HPAPCI28struct hp300_port {29 struct hp300_port *next; /* next port */30 int line; /* line (tty) number */31};32 33static struct hp300_port *hp300_ports;34#endif35 36#ifdef CONFIG_HPDCA37 38static int hpdca_init_one(struct dio_dev *d,39 const struct dio_device_id *ent);40static void hpdca_remove_one(struct dio_dev *d);41 42static 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 50static 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#endif58 59static unsigned int num_ports;60 61extern int hp300_uart_scode;62 63/* Offset to UART registers from base of DCA */64#define UART_OFFSET 1765 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 15360073 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 0x081#define FRODO_APCISPACE 0x2082#define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE))83 84#define HPAPCI_BAUD_BASE 50040085 86#ifdef CONFIG_SERIAL_8250_CONSOLE87/*88 * Parse the bootinfo to find descriptions for headless console and89 * debug serial ports and register them with the 8250 driver.90 */91int __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_HPAPCI114 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#else122 pr_warn("Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n");123 return 0;124#endif125 } else {126#ifdef CONFIG_HPDCA127 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#else145 pr_warn("Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n");146 return 0;147#endif148 }149 150 if (early_serial_setup(&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_HPDCA157static 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_CONSOLE164 if (hp300_uart_scode == d->scode) {165 /* Already got it. */166 return 0;167 }168#endif169 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#endif202 203static int __init hp300_8250_init(void)204{205 static int called;206#ifdef CONFIG_HPAPCI207 int line;208 unsigned long base;209 struct uart_8250_port uart;210 struct hp300_port *port;211 int i;212#endif213 if (called)214 return -ENODEV;215 called = 1;216 217 if (!MACH_IS_HP300)218 return -ENODEV;219 220#ifdef CONFIG_HPDCA221 dio_register_driver(&hpdca_driver);222#endif223#ifdef CONFIG_HPAPCI224 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's235 * mapped to DCA.236 */237#ifdef CONFIG_SERIAL_8250_CONSOLE238 if (i == 1)239 continue;240#endif241 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_IRQ254 | 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#endif279 280 /* Any boards found? */281 if (!num_ports)282 return -ENODEV;283 284 return 0;285}286 287#ifdef CONFIG_HPDCA288static 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#endif300 301static void __exit hp300_8250_exit(void)302{303#ifdef CONFIG_HPAPCI304 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#endif315#ifdef CONFIG_HPDCA316 dio_unregister_driver(&hpdca_driver);317#endif318}319 320module_init(hp300_8250_init);321module_exit(hp300_8250_exit);322MODULE_DESCRIPTION("HP DCA/APCI serial driver");323MODULE_AUTHOR("Kars de Jong <jongk@linux-m68k.org>");324MODULE_LICENSE("GPL");325