patch-2.2.14 linux/drivers/sbus/char/su.c
Next file: linux/drivers/sbus/char/sunkeymap.c
Previous file: linux/drivers/sbus/char/sab82532.c
Back to the patch index
Back to the overall index
- Lines: 717
- Date:
Tue Jan 4 10:12:19 2000
- Orig file:
v2.2.13/linux/drivers/sbus/char/su.c
- Orig date:
Mon Mar 15 16:11:30 1999
diff -u --recursive --new-file v2.2.13/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c
@@ -1,8 +1,8 @@
-/* $Id: su.c,v 1.18 1999/01/02 16:47:37 davem Exp $
+/* $Id: su.c,v 1.18.2.5 1999/10/14 08:44:35 davem Exp $
* su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
- * Coypright (C) 1998 Pete Zaitcev (zaitcev@metabyte.com)
+ * Copyright (C) 1998-1999 Pete Zaitcev (zaitcev@metabyte.com)
*
* This is mainly a variation of drivers/char/serial.c,
* credits go to authors mentioned therein.
@@ -92,6 +92,11 @@
int su_serial_console_init(void);
#endif
+enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT };
+static char *su_typev[] = { "???", "mouse", "kbd", "serial" };
+
+#define SU_PROPSIZE 128
+
/*
* serial.c saves memory when it allocates async_info upon first open.
* We have parts of state structure together because we do call startup
@@ -107,8 +112,8 @@
int line;
int cflag;
- int kbd_node;
- int ms_node;
+ enum su_type port_type; /* Hookup type: e.g. mouse */
+ int is_console;
int port_node;
char name[16];
@@ -145,6 +150,18 @@
unsigned long last_active; /* For async_struct, to be */
};
+/*
+ * Scan status structure.
+ * "prop" is a local variable but it eats stack to keep it in each
+ * stack frame of a recursive procedure.
+ */
+struct su_probe_scan {
+ int msnode, kbnode; /* PROM nodes for mouse and keyboard */
+ int msx, kbx; /* minors for mouse and keyboard */
+ int devices; /* scan index */
+ char prop[SU_PROPSIZE];
+};
+
static char *serial_name = "PCIO serial driver";
static char serial_version[16];
@@ -223,8 +240,6 @@
return 0;
}
-#ifdef __sparc_v9__
-
static inline
unsigned int su_inb(struct su_struct *info, unsigned long offset)
{
@@ -234,20 +249,7 @@
static inline void
su_outb(struct su_struct *info, unsigned long offset, int value)
{
- outb(value, info->port + offset);
-}
-
-#else
-
-static inline
-unsigned int su_inb(struct su_struct *info, unsigned long offset)
-{
- return (unsigned int)(*(volatile unsigned char *)(info->port + offset));
-}
-
-static inline void
-su_outb(struct su_struct *info, unsigned long offset, int value)
-{
+#ifndef __sparc_v9__
/*
* MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are
* connected with a gate then go to SlavIO. When IRQ4 goes tristated
@@ -257,10 +259,9 @@
* This problem is similar to what Alpha people suffer, see serial.c.
*/
if (offset == UART_MCR) value |= UART_MCR_OUT2;
- *(volatile unsigned char *)(info->port + offset) = value;
-}
-
#endif
+ outb(value, info->port + offset);
+}
#define serial_in(info, off) su_inb(info, off)
#define serial_inp(info, off) su_inb(info, off)
@@ -341,14 +342,14 @@
}
static __inline__ void
-receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs)
+receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs, int is_brk)
{
unsigned char status = 0;
unsigned char ch;
do {
ch = serial_inp(info, UART_RX);
- if (info->kbd_node) {
+ if (info->port_type == SU_PORT_KBD) {
if(ch == SUNKBD_RESET) {
l1a_state.kbd_id = 1;
l1a_state.l1_down = 0;
@@ -368,7 +369,7 @@
}
sunkbd_inchar(ch, regs);
} else {
- sun_mouse_inbyte(ch);
+ sun_mouse_inbyte(ch, is_brk);
}
status = su_inb(info, UART_LSR);
@@ -380,12 +381,15 @@
{
struct tty_struct *tty = info->tty;
unsigned char ch;
- int ignored = 0;
+ int ignored = 0, saw_console_brk = 0;
struct async_icount *icount;
icount = &info->icount;
do {
ch = serial_inp(info, UART_RX);
+ if (info->is_console &&
+ (ch == 0 || (*status &UART_LSR_BI)))
+ saw_console_brk = 1;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
*tty->flip.char_buf_ptr = ch;
@@ -461,6 +465,8 @@
printk("E%02x.R%d", *status, tty->flip.count);
#endif
tty_flip_buffer_push(tty);
+ if (saw_console_brk != 0)
+ batten_down_hatches();
}
static __inline__ void
@@ -529,7 +535,7 @@
(status & UART_MSR_DCD))
hardpps();
#endif
- }
+ }
if (status & UART_MSR_DCTS)
icount->cts++;
wake_up_interruptible(&info->delta_msr_wait);
@@ -598,8 +604,9 @@
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
- if (status & UART_LSR_DR)
- receive_kbd_ms_chars(info, regs);
+ if ((status & UART_LSR_DR) || (status & UART_LSR_BI))
+ receive_kbd_ms_chars(info, regs,
+ (status & UART_LSR_BI) != 0);
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
@@ -775,7 +782,7 @@
/*
* Allocate the IRQ if necessary
*/
- if (info->kbd_node || info->ms_node) {
+ if (info->port_type != SU_PORT_PORT) {
retval = request_irq(info->irq, su_kbd_ms_interrupt,
SA_SHIRQ, info->name, info);
} else {
@@ -956,7 +963,7 @@
int bits;
unsigned long flags;
- if (!info->kbd_node && !info->ms_node) {
+ if (info->port_type == SU_PORT_PORT) {
if (!info->tty || !info->tty->termios)
return;
if (!info->port)
@@ -1133,9 +1140,9 @@
struct su_struct *info = su_table;
int lsr;
- if (!info->kbd_node)
+ if (info->port_type != SU_PORT_KBD)
++info;
- if (!info)
+ if (info->port_type != SU_PORT_KBD)
return;
do {
@@ -1151,9 +1158,9 @@
{
struct su_struct *info = su_table;
- if (!info->ms_node)
+ if (info->port_type != SU_PORT_MS)
++info;
- if (!info)
+ if (info->port_type != SU_PORT_MS)
return;
info->cflag &= ~(CBAUDEX | CBAUD);
@@ -2026,6 +2033,8 @@
if ((line < 0) || (line >= NR_PORTS))
return -ENODEV;
info = su_table + line;
+ if (info->type == PORT_UNKNOWN)
+ return -ENODEV;
info->count++;
tty->driver_data = info;
info->tty = tty;
@@ -2202,9 +2211,9 @@
/*
* ---------------------------------------------------------------------
- * su_init() and friends
+ * su_XXX_init() and friends
*
- * su_init() is called at boot-time to initialize the serial driver.
+ * su_XXX_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
@@ -2215,7 +2224,7 @@
*/
__initfunc(static __inline__ void show_su_version(void))
{
- char *revision = "$Revision: 1.18 $";
+ char *revision = "$Revision: 1.18.2.5 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2226,8 +2235,8 @@
}
/*
- * This routine is called by su_init() to initialize a specific serial
- * port. It determines what type of UART chip this serial port is
+ * This routine is called by su_{serial|kbd_ms}_init() to initialize a specific
+ * serial port. It determines what type of UART chip this serial port is
* using: 8250, 16450, 16550, 16550A. The important question is
* whether or not this UART is a 16550A, since this will determine
* whether or not we can use its FIFO features.
@@ -2236,38 +2245,38 @@
autoconfig(struct su_struct *info)
{
unsigned char status1, status2, scratch, scratch2;
-#ifdef __sparc_v9__
struct linux_ebus_device *dev = 0;
struct linux_ebus *ebus;
-#else
+#ifndef __sparc_v9__
struct linux_prom_registers reg0;
#endif
unsigned long flags;
-#ifdef __sparc_v9__
+ if (!info->port_node || !info->port_type)
+ return;
+
+ /*
+ * First we look for Ebus-bases su's
+ */
for_each_ebus(ebus) {
for_each_ebusdev(dev, ebus) {
- if (!strncmp(dev->prom_name, "su", 2)) {
- if (dev->prom_node == info->kbd_node)
- goto ebus_done;
- if (dev->prom_node == info->ms_node)
- goto ebus_done;
+ if (dev->prom_node == info->port_node) {
+ info->port = dev->base_address[0];
+ info->irq = dev->irqs[0];
+ goto ebus_done;
}
}
}
-ebus_done:
- if (!dev)
- return;
-
- info->port = dev->base_address[0];
- if (check_region(info->port, 8))
- return;
- info->irq = dev->irqs[0];
+#ifdef __sparc_v9__
+ /*
+ * Not on Ebus, bailing.
+ */
+ return;
#else
- if (!info->port_node)
- return;
-
+ /*
+ * Not on Ebus, must be OBIO.
+ */
if (prom_getproperty(info->port_node, "reg",
(char *)®0, sizeof(reg0)) == -1) {
prom_printf("su: no \"reg\" property\n");
@@ -2279,21 +2288,24 @@
prom_printf("su: cannot map\n");
return;
}
+
/*
- * There is no intr property on MrCoffee, so hardwire it. Krups?
+ * There is no intr property on MrCoffee, so hardwire it.
*/
info->irq = IRQ_4M(13);
#endif
-#ifdef DEBUG_SERIAL_OPEN
- printk("Found 'su' at %016lx IRQ %s\n", dev->base_address[0],
- __irq_itoa(dev->irqs[0]));
+ebus_done:
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Found 'su' at %016lx IRQ %s\n", info->port,
+ __irq_itoa(info->irq));
#endif
info->magic = SERIAL_MAGIC;
save_flags(flags); cli();
-
+
/*
* Do a simple existence test first; if we fail this, there's
* no point trying anything else.
@@ -2312,17 +2324,20 @@
return; /* We failed; there's nothing here */
}
-#if 0 /* P3: This does not work on MrCoffee. OUT2 is 0x80 - should work... */
scratch = serial_inp(info, UART_MCR);
serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
status1 = serial_inp(info, UART_MSR) & 0xF0;
serial_outp(info, UART_MCR, scratch);
if (status1 != 0x90) {
+ /*
+ * This code fragment used to fail, now it fixed itself.
+ * We keep the printout for a case.
+ */
+ printk("su: loopback returned status 0x%02x\n", status1);
restore_flags(flags);
return;
}
-#endif
scratch2 = serial_in(info, UART_LCR);
serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */
@@ -2389,11 +2404,7 @@
return;
}
- if (info->kbd_node || info->ms_node)
- sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd");
- else
- strcpy(info->name, "su(serial)");
-
+ sprintf(info->name, "su(%s)", su_typev[info->port_type]);
#ifdef __sparc_v9__
request_region(info->port, 8, info->name);
#endif
@@ -2409,6 +2420,11 @@
restore_flags(flags);
}
+/* This is used by the SAB driver to adjust where its minor
+ * numbers start, we always are probed for first.
+ */
+int su_num_ports = 0;
+
/*
* The serial driver boot-time initialization code!
*/
@@ -2499,11 +2515,18 @@
if (info->type == PORT_UNKNOWN)
continue;
- printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n",
- info->name, info->port, __irq_itoa(info->irq),
+ printk(KERN_INFO "%s at 0x%lx (tty %d irq %s) is a %s\n",
+ info->name, (long)info->port, i, __irq_itoa(info->irq),
uart_config[info->type].name);
}
+ for (i = 0, info = su_table; i < NR_PORTS; i++, info++)
+ if (info->type == PORT_UNKNOWN)
+ break;
+
+ su_num_ports = i;
+ serial_driver.num = callout_driver.num = i;
+
return 0;
}
@@ -2519,7 +2542,7 @@
info->type = PORT_UNKNOWN;
info->baud_base = BAUD_BASE;
- if (info->kbd_node)
+ if (info->port_type == SU_PORT_KBD)
info->cflag = B1200 | CS8 | CLOCAL | CREAD;
else
info->cflag = B4800 | CS8 | CLOCAL | CREAD;
@@ -2528,12 +2551,12 @@
if (info->type == PORT_UNKNOWN)
continue;
- printk(KERN_INFO "%s at %16lx (irq = %s) is a %s\n",
+ printk(KERN_INFO "%s at 0x%lx (irq = %s) is a %s\n",
info->name, info->port, __irq_itoa(info->irq),
uart_config[info->type].name);
startup(info);
- if (info->kbd_node)
+ if (info->port_type == SU_PORT_KBD)
keyboard_zsinit(su_put_char_kbd);
else
sun_mouse_zsinit();
@@ -2541,154 +2564,131 @@
return 0;
}
-__initfunc(int su_probe (unsigned long *memory_start))
+/*
+ * We got several platforms which present 'su' in different parts
+ * of device tree. 'su' may be found under obio, ebus, isa and pci.
+ * We walk over the tree and find them wherever PROM hides them.
+ */
+__initfunc(void su_probe_any(struct su_probe_scan *t, int sunode))
{
- struct su_struct *info = su_table;
- int node, enode, tnode, sunode;
- int kbnode = 0, msnode = 0;
- int devices = 0;
- char prop[128];
+ struct su_struct *info;
int len;
- /*
- * Find su on MrCoffee. We return OK code if find any.
- * Then su_init finds every one and initializes them.
- * We do this early because MrCoffee got no aliases.
- */
- node = prom_getchild(prom_root_node);
- if ((node = prom_searchsiblings(node, "obio")) != 0) {
- if ((sunode = prom_getchild(node)) != 0) {
- if ((sunode = prom_searchsiblings(sunode, "su")) != 0) {
- info->port_node = sunode;
-#ifdef CONFIG_SERIAL_CONSOLE
- /*
- * Console must be initiated after the generic
- * initialization.
- * sunserial_setinitfunc inverts order, so
- * call this before next one.
- */
- sunserial_setinitfunc(memory_start,
- su_serial_console_init);
-#endif
- sunserial_setinitfunc(memory_start,
- su_serial_init);
- return 0;
+ if (t->devices >= NR_PORTS)
+ return;
+
+ for (; sunode != 0; sunode = prom_getsibling(sunode)) {
+ len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE);
+ if (len <= 1)
+ continue; /* Broken PROM node */
+ if (strncmp(t->prop, "su", len) == 0 ||
+ strncmp(t->prop, "serial", len) == 0 ||
+ strncmp(t->prop, "su_pnp", len) == 0) {
+ info = &su_table[t->devices];
+ if (t->kbnode != 0 && sunode == t->kbnode) {
+ t->kbx = t->devices;
+ info->port_type = SU_PORT_KBD;
+ } else if (t->msnode != 0 && sunode == t->msnode) {
+ t->msx = t->devices;
+ info->port_type = SU_PORT_MS;
+ } else {
+ info->port_type = SU_PORT_PORT;
}
+ info->is_console = 0;
+ info->port_node = sunode;
+ ++t->devices;
+ } else {
+ su_probe_any(t, prom_getchild(sunode));
}
}
+}
+
+__initfunc(int su_probe (unsigned long *memory_start))
+{
+ int node;
+ int len;
+ struct su_probe_scan scan;
+
+ /*
+ * First, we scan the tree.
+ */
+ scan.devices = 0;
+ scan.msx = -1;
+ scan.kbx = -1;
+ scan.kbnode = 0;
+ scan.msnode = 0;
/*
* Get the nodes for keyboard and mouse from 'aliases'...
*/
node = prom_getchild(prom_root_node);
node = prom_searchsiblings(node, "aliases");
- if (!node)
- return -ENODEV;
+ if (node != 0) {
- len = prom_getproperty(node, "keyboard", prop, sizeof(prop));
- if (len > 0) {
- prop[len] = 0;
- kbnode = prom_finddevice(prop);
- }
- if (!kbnode)
- return -ENODEV;
+ len = prom_getproperty(node, "keyboard", scan.prop,SU_PROPSIZE);
+ if (len > 0) {
+ scan.prop[len] = 0;
+ scan.kbnode = prom_finddevice(scan.prop);
+ }
- len = prom_getproperty(node, "mouse", prop, sizeof(prop));
- if (len > 0) {
- prop[len] = 0;
- msnode = prom_finddevice(prop);
+ len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE);
+ if (len > 0) {
+ scan.prop[len] = 0;
+ scan.msnode = prom_finddevice(scan.prop);
+ }
}
- if (!msnode)
- return -ENODEV;
- /*
- * Find matching EBus nodes...
- */
- node = prom_getchild(prom_root_node);
- if ((node = prom_searchsiblings(node, "pci")) == 0) {
- return -ENODEV; /* Plain sparc */
- }
+ su_probe_any(&scan, prom_getchild(prom_root_node));
/*
- * Check for SUNW,sabre on Ultra 5/10/AXi.
- */
- len = prom_getproperty(node, "model", prop, sizeof(prop));
- if ((len > 0) && !strncmp(prop, "SUNW,sabre", len)) {
- node = prom_getchild(node);
- node = prom_searchsiblings(node, "pci");
+ * Second, we process the special case of keyboard and mouse.
+ *
+ * Currently if we got keyboard and mouse hooked to "su" ports
+ * we do not use any possible remaining "su" as a serial port.
+ * Thus, we ignore values of .msx and .kbx, then compact ports.
+ * Those who want to address this issue need to merge
+ * su_serial_init() and su_ms_kbd_init().
+ */
+ if (scan.msx != -1 && scan.kbx != -1) {
+ su_table[0].port_type = SU_PORT_MS;
+ su_table[0].is_console = 0;
+ su_table[0].port_node = scan.msnode;
+ su_table[1].port_type = SU_PORT_KBD;
+ su_table[1].is_console = 0;
+ su_table[1].port_node = scan.kbnode;
+
+ sunserial_setinitfunc(memory_start, su_kbd_ms_init);
+ rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
+ sunkbd_setinitfunc(memory_start, sun_kbd_init);
+ kbd_ops.compute_shiftstate = sun_compute_shiftstate;
+ kbd_ops.setledstate = sun_setledstate;
+ kbd_ops.getledstate = sun_getledstate;
+ kbd_ops.setkeycode = sun_setkeycode;
+ kbd_ops.getkeycode = sun_getkeycode;
+#ifdef CONFIG_PCI
+ sunkbd_install_keymaps(memory_start, sun_key_maps,
+ sun_keymap_count, sun_func_buf, sun_func_table,
+ sun_funcbufsize, sun_funcbufleft,
+ sun_accent_table, sun_accent_table_size);
+#endif
+ return 0;
+ }
+ if (scan.msx != -1 || scan.kbx != -1) {
+ printk("su_probe: cannot match keyboard and mouse, confused\n");
+ return -ENODEV;
}
+ if (scan.devices == 0)
+ return -ENODEV;
+
+#ifdef CONFIG_SERIAL_CONSOLE
/*
- * For each PCI bus...
+ * Console must be initiated after the generic initialization.
+ * sunserial_setinitfunc inverts order, so call this before next one.
*/
- while (node) {
- enode = prom_getchild(node);
- enode = prom_searchsiblings(enode, "ebus");
-
- /*
- * For each EBus on this PCI...
- */
- while (enode) {
- sunode = prom_getchild(enode);
- tnode = prom_searchsiblings(sunode, "su");
- if (!tnode)
- tnode = prom_searchsiblings(sunode, "su_pnp");
- sunode = tnode;
-
- /*
- * For each 'su' on this EBus...
- */
- while (sunode) {
- /*
- * Does it match?
- */
- if (sunode == kbnode) {
- info->kbd_node = sunode;
- ++info;
- ++devices;
- }
- if (sunode == msnode) {
- info->ms_node = sunode;
- ++info;
- ++devices;
- }
-
- /*
- * Found everything we need?
- */
- if (devices == 2)
- goto found;
-
- sunode = prom_getsibling(sunode);
- tnode = prom_searchsiblings(sunode, "su");
- if (!tnode)
- tnode = prom_searchsiblings(sunode,
- "su_pnp");
- sunode = tnode;
- }
- enode = prom_getsibling(enode);
- enode = prom_searchsiblings(enode, "ebus");
- }
- node = prom_getsibling(node);
- node = prom_searchsiblings(node, "pci");
- }
- return -ENODEV;
-
-found:
- sunserial_setinitfunc(memory_start, su_kbd_ms_init);
- rs_ops.rs_change_mouse_baud = su_change_mouse_baud;
- sunkbd_setinitfunc(memory_start, sun_kbd_init);
- kbd_ops.compute_shiftstate = sun_compute_shiftstate;
- kbd_ops.setledstate = sun_setledstate;
- kbd_ops.getledstate = sun_getledstate;
- kbd_ops.setkeycode = sun_setkeycode;
- kbd_ops.getkeycode = sun_getkeycode;
-#ifdef CONFIG_PCI
- sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count,
- sun_func_buf, sun_func_table,
- sun_funcbufsize, sun_funcbufleft,
- sun_accent_table, sun_accent_table_size);
+ sunserial_setinitfunc(memory_start, su_serial_console_init);
#endif
+ sunserial_setinitfunc(memory_start, su_serial_init);
return 0;
}
@@ -2910,6 +2910,8 @@
if (su_inb(info, UART_LSR) == 0xff)
return -1;
+ info->is_console = 1;
+
return 0;
}
@@ -2927,6 +2929,8 @@
NULL
};
+int su_console_registered = 0;
+
/*
* Register console.
*/
@@ -2942,6 +2946,7 @@
return 0;
sercons.index = 0;
register_console(&sercons);
+ su_console_registered = 1;
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)