8250_hp300.c 7.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Driver for the 98626/98644/internal serial interface on hp300/hp400
 * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs)
 *
 * Ported from 2.2 and modified to use the normal 8250 driver
 * by Kars de Jong <jongk@linux-m68k.org>, May 2004.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/serial.h>
13
#include <linux/serial_8250.h>
L
Linus Torvalds 已提交
14 15 16
#include <linux/delay.h>
#include <linux/dio.h>
#include <linux/console.h>
17
#include <linux/slab.h>
L
Linus Torvalds 已提交
18 19
#include <asm/io.h>

20 21
#include "8250.h"

L
Linus Torvalds 已提交
22
#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI)
23
#warning CONFIG_SERIAL_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure?
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37
#endif

#ifdef CONFIG_HPAPCI
struct hp300_port
{
	struct hp300_port *next;	/* next port */
	int line;			/* line (tty) number */
};

static struct hp300_port *hp300_ports;
#endif

#ifdef CONFIG_HPDCA

B
Bill Pemberton 已提交
38
static int hpdca_init_one(struct dio_dev *d,
A
Alan Cox 已提交
39
					const struct dio_device_id *ent);
B
Bill Pemberton 已提交
40
static void hpdca_remove_one(struct dio_dev *d);
L
Linus Torvalds 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53

static struct dio_device_id hpdca_dio_tbl[] = {
	{ DIO_ID_DCA0 },
	{ DIO_ID_DCA0REM },
	{ DIO_ID_DCA1 },
	{ DIO_ID_DCA1REM },
	{ 0 }
};

static struct dio_driver hpdca_driver = {
	.name      = "hpdca",
	.id_table  = hpdca_dio_tbl,
	.probe     = hpdca_init_one,
54
	.remove    = hpdca_remove_one,
L
Linus Torvalds 已提交
55 56 57 58
};

#endif

59 60
static unsigned int num_ports;

L
Linus Torvalds 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
extern int hp300_uart_scode;

/* Offset to UART registers from base of DCA */
#define UART_OFFSET	17

#define DCA_ID		0x01	/* ID (read), reset (write) */
#define DCA_IC		0x03	/* Interrupt control        */

/* Interrupt control */
#define DCA_IC_IE	0x80	/* Master interrupt enable  */

#define HPDCA_BAUD_BASE 153600

/* Base address of the Frodo part */
#define FRODO_BASE	(0x41c000)

/*
 * Where we find the 8250-like APCI ports, and how far apart they are.
 */
#define FRODO_APCIBASE		0x0
#define FRODO_APCISPACE		0x20
#define FRODO_APCI_OFFSET(x)	(FRODO_APCIBASE + ((x) * FRODO_APCISPACE))

#define HPAPCI_BAUD_BASE 500400

#ifdef CONFIG_SERIAL_8250_CONSOLE
/*
A
Alan Cox 已提交
88
 * Parse the bootinfo to find descriptions for headless console and
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
 * debug serial ports and register them with the 8250 driver.
 */
int __init hp300_setup_serial_console(void)
{
	int scode;
	struct uart_port port;

	memset(&port, 0, sizeof(port));

	if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX)
		return 0;

	if (DIO_SCINHOLE(hp300_uart_scode))
		return 0;

	scode = hp300_uart_scode;

	/* Memory mapped I/O */
	port.iotype = UPIO_MEM;
	port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
	port.type = PORT_UNKNOWN;

	/* Check for APCI console */
	if (scode == 256) {
#ifdef CONFIG_HPAPCI
		printk(KERN_INFO "Serial console is HP APCI 1\n");

		port.uartclk = HPAPCI_BAUD_BASE * 16;
		port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1));
		port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
		port.regshift = 2;
		add_preferred_console("ttyS", port.line, "9600n8");
#else
		printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n");
		return 0;
#endif
A
Alan Cox 已提交
125
	} else {
L
Linus Torvalds 已提交
126 127
#ifdef CONFIG_HPDCA
		unsigned long pa = dio_scodetophysaddr(scode);
A
Alan Cox 已提交
128
		if (!pa)
L
Linus Torvalds 已提交
129 130 131 132 133 134 135 136 137 138 139 140 141
			return 0;

		printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode);

		port.uartclk = HPDCA_BAUD_BASE * 16;
		port.mapbase = (pa + UART_OFFSET);
		port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
		port.regshift = 1;
		port.irq = DIO_IPL(pa + DIO_VIRADDRBASE);

		/* Enable board-interrupts */
		out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);

A
Alan Cox 已提交
142
		if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80)
L
Linus Torvalds 已提交
143 144 145 146 147 148 149
			add_preferred_console("ttyS", port.line, "9600n8");
#else
		printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n");
		return 0;
#endif
	}

A
Alan Cox 已提交
150
	if (early_serial_setup(&port) < 0)
L
Linus Torvalds 已提交
151 152 153 154 155 156
		printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n");
	return 0;
}
#endif /* CONFIG_SERIAL_8250_CONSOLE */

#ifdef CONFIG_HPDCA
B
Bill Pemberton 已提交
157
static int hpdca_init_one(struct dio_dev *d,
A
Alan Cox 已提交
158
				const struct dio_device_id *ent)
L
Linus Torvalds 已提交
159
{
160
	struct uart_8250_port uart;
L
Linus Torvalds 已提交
161 162 163 164 165 166 167 168
	int line;

#ifdef CONFIG_SERIAL_8250_CONSOLE
	if (hp300_uart_scode == d->scode) {
		/* Already got it. */
		return 0;
	}
#endif
169
	memset(&uart, 0, sizeof(uart));
L
Linus Torvalds 已提交
170 171

	/* Memory mapped I/O */
172 173 174 175 176 177 178 179
	uart.port.iotype = UPIO_MEM;
	uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
	uart.port.irq = d->ipl;
	uart.port.uartclk = HPDCA_BAUD_BASE * 16;
	uart.port.mapbase = (d->resource.start + UART_OFFSET);
	uart.port.membase = (char *)(uart.port.mapbase + DIO_VIRADDRBASE);
	uart.port.regshift = 1;
	uart.port.dev = &d->dev;
180
	line = serial8250_register_8250_port(&uart);
L
Linus Torvalds 已提交
181 182 183

	if (line < 0) {
		printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
184
		       " irq %d failed\n", d->scode, uart.port.irq);
L
Linus Torvalds 已提交
185 186 187 188 189 190 191 192 193 194 195
		return -ENOMEM;
	}

	/* Enable board-interrupts */
	out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);
	dio_set_drvdata(d, (void *)line);

	/* Reset the DCA */
	out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff);
	udelay(100);

196 197
	num_ports++;

L
Linus Torvalds 已提交
198 199 200 201 202 203
	return 0;
}
#endif

static int __init hp300_8250_init(void)
{
A
Alan Cox 已提交
204
	static int called;
L
Linus Torvalds 已提交
205 206 207
#ifdef CONFIG_HPAPCI
	int line;
	unsigned long base;
208
	struct uart_8250_port uart;
L
Linus Torvalds 已提交
209 210 211 212 213 214 215 216 217 218 219
	struct hp300_port *port;
	int i;
#endif
	if (called)
		return -ENODEV;
	called = 1;

	if (!MACH_IS_HP300)
		return -ENODEV;

#ifdef CONFIG_HPDCA
220
	dio_register_driver(&hpdca_driver);
L
Linus Torvalds 已提交
221 222 223 224 225 226 227 228 229 230 231 232
#endif
#ifdef CONFIG_HPAPCI
	if (hp300_model < HP_400) {
		if (!num_ports)
			return -ENODEV;
		return 0;
	}
	/* These models have the Frodo chip.
	 * Port 0 is reserved for the Apollo Domain keyboard.
	 * Port 1 is either the console or the DCA.
	 */
	for (i = 1; i < 4; i++) {
A
Alan Cox 已提交
233 234
		/* Port 1 is the console on a 425e, on other machines it's
		 * mapped to DCA.
L
Linus Torvalds 已提交
235 236
		 */
#ifdef CONFIG_SERIAL_8250_CONSOLE
A
Alan Cox 已提交
237
		if (i == 1)
L
Linus Torvalds 已提交
238 239 240 241 242 243 244 245
			continue;
#endif

		/* Create new serial device */
		port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL);
		if (!port)
			return -ENOMEM;

246
		memset(&uart, 0, sizeof(uart));
L
Linus Torvalds 已提交
247 248 249 250

		base = (FRODO_BASE + FRODO_APCI_OFFSET(i));

		/* Memory mapped I/O */
251 252
		uart.port.iotype = UPIO_MEM;
		uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
A
Alan Cox 已提交
253
			      | UPF_BOOT_AUTOCONF;
L
Linus Torvalds 已提交
254
		/* XXX - no interrupt support yet */
255 256 257 258 259
		uart.port.irq = 0;
		uart.port.uartclk = HPAPCI_BAUD_BASE * 16;
		uart.port.mapbase = base;
		uart.port.membase = (char *)(base + DIO_VIRADDRBASE);
		uart.port.regshift = 2;
L
Linus Torvalds 已提交
260

261
		line = serial8250_register_8250_port(&uart);
L
Linus Torvalds 已提交
262 263

		if (line < 0) {
A
Alan Cox 已提交
264
			printk(KERN_NOTICE "8250_hp300: register_serial() APCI"
265
			       " %d irq %d failed\n", i, uart.port.irq);
L
Linus Torvalds 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
			kfree(port);
			continue;
		}

		port->line = line;
		port->next = hp300_ports;
		hp300_ports = port;

		num_ports++;
	}
#endif

	/* Any boards found? */
	if (!num_ports)
		return -ENODEV;

	return 0;
}

#ifdef CONFIG_HPDCA
B
Bill Pemberton 已提交
286
static void hpdca_remove_one(struct dio_dev *d)
L
Linus Torvalds 已提交
287 288 289 290 291 292 293 294
{
	int line;

	line = (int) dio_get_drvdata(d);
	if (d->resource.start) {
		/* Disable board-interrupts */
		out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0);
	}
295
	serial8250_unregister_port(line);
L
Linus Torvalds 已提交
296 297 298 299 300 301 302 303 304
}
#endif

static void __exit hp300_8250_exit(void)
{
#ifdef CONFIG_HPAPCI
	struct hp300_port *port, *to_free;

	for (port = hp300_ports; port; ) {
305
		serial8250_unregister_port(port->line);
L
Linus Torvalds 已提交
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
		to_free = port;
		port = port->next;
		kfree(to_free);
	}

	hp300_ports = NULL;
#endif
#ifdef CONFIG_HPDCA
	dio_unregister_driver(&hpdca_driver);
#endif
}

module_init(hp300_8250_init);
module_exit(hp300_8250_exit);
MODULE_DESCRIPTION("HP DCA/APCI serial driver");
MODULE_AUTHOR("Kars de Jong <jongk@linux-m68k.org>");
MODULE_LICENSE("GPL");