brintos

brintos / linux-shallow public Read only

0
0
Text · 7.4 KiB · 3012ea0 Raw
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