diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
commit | 78c388aed2b7184182c08428db1de6c872d815f5 (patch) | |
tree | 4b2003b1b4ceb241a17faa995da8dd1004bb8e45 /drivers/sbus/char | |
parent | eb7a5bf93aaa4be1d7c6181100ab7639e74d67f7 (diff) |
Merge with Linux 2.1.131 and more MIPS goodies.
(Did I mention that CVS is buggy ...)
Diffstat (limited to 'drivers/sbus/char')
-rw-r--r-- | drivers/sbus/char/Config.in | 4 | ||||
-rw-r--r-- | drivers/sbus/char/Makefile | 8 | ||||
-rw-r--r-- | drivers/sbus/char/bpp.c | 26 | ||||
-rw-r--r-- | drivers/sbus/char/envctrl.c | 54 | ||||
-rw-r--r-- | drivers/sbus/char/flash.c | 2 | ||||
-rw-r--r-- | drivers/sbus/char/pcikbd.c | 214 | ||||
-rw-r--r-- | drivers/sbus/char/rtc.c | 2 | ||||
-rw-r--r-- | drivers/sbus/char/sab82532.c | 244 | ||||
-rw-r--r-- | drivers/sbus/char/su.c | 2529 | ||||
-rw-r--r-- | drivers/sbus/char/sunkbd.c | 6 | ||||
-rw-r--r-- | drivers/sbus/char/sunmouse.c | 6 | ||||
-rw-r--r-- | drivers/sbus/char/sunserial.c | 41 | ||||
-rw-r--r-- | drivers/sbus/char/vfc_dev.c | 4 | ||||
-rw-r--r-- | drivers/sbus/char/zs.c | 51 |
14 files changed, 2801 insertions, 390 deletions
diff --git a/drivers/sbus/char/Config.in b/drivers/sbus/char/Config.in index 6312709e1..6faee5221 100644 --- a/drivers/sbus/char/Config.in +++ b/drivers/sbus/char/Config.in @@ -2,7 +2,9 @@ comment 'Misc Linux/SPARC drivers' tristate '/dev/openprom device support' CONFIG_SUN_OPENPROMIO tristate 'Mostek real time clock support' CONFIG_SUN_MOSTEK_RTC if [ "$ARCH" = "sparc64" ]; then - tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532 + if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Siemens SAB82532 serial support' CONFIG_SAB82532 + fi tristate 'OBP Flash Device support' CONFIG_OBP_FLASH fi diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile index 9ad88d157..a64f823c2 100644 --- a/drivers/sbus/char/Makefile +++ b/drivers/sbus/char/Makefile @@ -47,7 +47,13 @@ else endif endif -endif # eq($(ARCH),sparc64) +else # !eq($(ARCH),sparc64) + +ifeq ($(CONFIG_PCI),y) +O_OBJS += su.o pcikbd.o +endif + +endif # !eq($(ARCH),sparc64) ifeq ($(CONFIG_SUN_OPENPROMIO),y) O_OBJS += openprom.o diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index 846f7cd7b..f445a14b2 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -314,13 +314,6 @@ static inline void bpp_snap(const char *msg, unsigned minor) #endif /* __sparc__ */ -/* - * This is TRUE if the module_init successfully loaded the module. - */ -#if 0 -static int loaded_flag = 0; -#endif - static void bpp_wake_up(unsigned long val) { wake_up(&instances[val].wait_queue); } @@ -946,25 +939,22 @@ static volatile struct bpp_regs *map_bpp(struct linux_sbus_device *dev, int idx) { volatile struct bpp_regs *regs; - /* Apply ranges to here, do not pollute Sbus devices list. */ - struct linux_prom_registers areg; - /* * PROM reports different numbers on Zebra and on DMA2. * We need to figure out when to apply parent ranges. * printk will show this on different machines. */ - areg = dev->reg_addrs[0]; - printk("bpp%d.map_bpp: 0x%x.%p[0x%x] i=%d\n", idx, - areg.which_io, areg.phys_addr, areg.reg_size, - dev->irqs[0]); /* IPC Zebra 1.fa200000[1c] i=2 */ - /** prom_apply_sbus_ranges (&areg, 1); **/ + prom_apply_sbus_ranges(dev->my_bus, &dev->reg_addrs[0], + dev->num_registers, dev); - regs = sparc_alloc_io (areg.phys_addr, 0, - sizeof(struct bpp_regs), "bpp", - areg.which_io, 0x0); + regs = sparc_alloc_io(dev->reg_addrs[0].phys_addr, 0, + dev->reg_addrs[0].reg_size, "bpp", + dev->reg_addrs[0].which_io, 0x0); + printk("bpp%d.map_bpp: 0x%x.%p[0x%x] i=%d\n", idx, + dev->reg_addrs[0].which_io, dev->reg_addrs[0].phys_addr, + dev->reg_addrs[0].reg_size, dev->irqs[0]); return regs; } diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index af8f6ce78..fbc93415d 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -1,4 +1,4 @@ -/* $Id: envctrl.c,v 1.7 1998/06/10 07:25:28 davem Exp $ +/* $Id: envctrl.c,v 1.9 1998/11/06 07:38:20 ecd Exp $ * envctrl.c: Temperature and Fan monitoring on Machines providing it. * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) @@ -115,10 +115,7 @@ i2c_read(unsigned char dev, char *buffer, int len, int user) unsigned char dummy; unsigned char stat; int error = -ENODEV; - int count = -1; - - if (len == 0) - return 0; + int count = 0; i2c->data = (dev << 1) | I2C_READ; @@ -134,21 +131,26 @@ i2c_read(unsigned char dev, char *buffer, int len, int user) if (stat & STATUS_LRB) goto stop; + error = 0; - if (count == (len - 2)) - goto final; + if (len == 0) { + count--; + break; + } - if (++count > 0) { + if (count == (len - 1)) + break; + + if (count++ > 0) { error = PUT_DATA(&i2c->data, buffer++, user); if (error) - goto final; + break; } else dummy = i2c->data; } while (1); -final: i2c->csr = CONTROL_ES0; - if (!error && (++count > 0)) + if (!error && (count++ > 0)) error = PUT_DATA(&i2c->data, buffer++, user); else dummy = i2c->data; @@ -159,14 +161,14 @@ final: stop: i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK; - if (!error && (++count > 0)) + if (!error && (count++ > 0)) error = PUT_DATA(&i2c->data, buffer++, user); else dummy = i2c->data; if (error) return error; - return count; + return count - 1; } static int @@ -189,19 +191,19 @@ i2c_write(unsigned char dev, const char *buffer, int len, int user) udelay(1); if (stat & STATUS_LRB) - goto stop; + break; + error = count; if (count == len) - goto stop; + break; error = GET_DATA(&i2c->data, buffer++, user); if (error) - goto stop; + break; count++; } while (1); -stop: i2c->csr = CONTROL_PIN | CONTROL_ES0 | CONTROL_STO | CONTROL_ACK; return error; } @@ -212,7 +214,7 @@ __initfunc(static int i2c_scan_bus(void)) int count = 0; for (dev = 1; dev < 128; dev++) { - if (i2c_write(dev, 0, 0, 0) == 0) { + if (i2c_read(dev, 0, 0, 0) == 0) { #ifdef DEBUG_BUS_SCAN int i; for (i = 0; i < NR_DEVMAP; i++) @@ -224,7 +226,11 @@ __initfunc(static int i2c_scan_bus(void)) count++; } } - return count ? 0 : -ENODEV; + if (!count) { + printk("%s: no devices found\n", __FUNCTION__); + return -ENODEV; + } + return 0; } static loff_t @@ -317,6 +323,7 @@ __initfunc(int envctrl_init(void)) #ifdef CONFIG_PCI struct linux_ebus *ebus; struct linux_ebus_device *edev = 0; + int err; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { @@ -327,8 +334,10 @@ __initfunc(int envctrl_init(void)) } } ebus_done: - if (!edev) + if (!edev) { + printk("%s: ebus device not found\n", __FUNCTION__); return -ENODEV; + } if (check_region(edev->base_address[0], sizeof(*i2c))) { printk("%s: Can't get region %lx, %d\n", @@ -353,7 +362,10 @@ ebus_done: release_region((unsigned long)i2c, sizeof(*i2c)); } - return i2c_scan_bus(); + err = i2c_scan_bus(); + if (err) + release_region((unsigned long)i2c, sizeof(*i2c)); + return err; #else return -ENODEV; #endif diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index fd91eeefd..cea0c5131 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -1,4 +1,4 @@ -/* $Id: flash.c,v 1.9 1998/05/17 06:33:39 ecd Exp $ +/* $Id: flash.c,v 1.10 1998/08/26 10:29:41 davem Exp $ * flash.c: Allow mmap access to the OBP Flash, for OBP updates. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) diff --git a/drivers/sbus/char/pcikbd.c b/drivers/sbus/char/pcikbd.c index d6be1fca0..297b8f9ae 100644 --- a/drivers/sbus/char/pcikbd.c +++ b/drivers/sbus/char/pcikbd.c @@ -1,7 +1,8 @@ -/* $Id: pcikbd.c,v 1.18 1998/05/29 06:00:23 ecd Exp $ +/* $Id: pcikbd.c,v 1.24 1998/11/08 11:15:24 davem Exp $ * pcikbd.c: Ultra/AX PC keyboard support. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * JavaStation(MrCoffee) support by Pete A. Zaitcev. * * This code is mainly put together from various places in * drivers/char, please refer to these sources for credits @@ -29,15 +30,25 @@ #include <asm/io.h> #include <asm/uaccess.h> +#ifdef __sparc_v9__ +#define PCI_KB_NAME "kb_ps2" +#define PCI_MS_NAME "kdmouse" +#else +#define PCI_KB_NAME "keyboard" +#define PCI_MS_NAME "mouse" +#endif + #include "pcikbd.h" #include "sunserial.h" -static int kbd_node; -static int beep_node; +#ifndef __sparc_v9__ +static int pcikbd_mrcoffee = 0; +#else +#define pcikbd_mrcoffee 0 +#endif static unsigned long pcikbd_iobase = 0; -static unsigned long pcibeep_iobase = 0; -static unsigned int pcikbd_irq; +static unsigned int pcikbd_irq = 0; /* used only by send_data - set by keyboard_interrupt */ static volatile unsigned char reply_expected = 0; @@ -53,6 +64,8 @@ extern int pci_getkeycode(unsigned int); extern void pci_setledstate(struct kbd_struct *, unsigned int); extern unsigned char pci_getledstate(void); +#ifdef __sparc_v9__ + static __inline__ unsigned char pcikbd_inb(unsigned long port) { return inb(port); @@ -63,6 +76,20 @@ static __inline__ void pcikbd_outb(unsigned char val, unsigned long port) outb(val, port); } +#else + +static __inline__ unsigned char pcikbd_inb(unsigned long port) +{ + return *(volatile unsigned char *)port; +} + +static __inline__ void pcikbd_outb(unsigned char val, unsigned long port) +{ + *(volatile unsigned char *)port = val; +} + +#endif + static inline void kb_wait(void) { unsigned long start = jiffies; @@ -372,6 +399,10 @@ __initfunc(static void pcikbd_write(int address, int data)) pcikbd_outb(data, pcikbd_iobase + address); } +#ifdef __sparc_v9__ + +static unsigned long pcibeep_iobase = 0; + /* Timer routine to turn off the beep after the interval expires. */ static void pcikbd_kd_nosound(unsigned long __unused) { @@ -402,6 +433,7 @@ static void pcikbd_kd_mksound(unsigned int hz, unsigned int ticks) outl(0, pcibeep_iobase); restore_flags(flags); } +#endif static void nop_kd_mksound(unsigned int hz, unsigned int ticks) { @@ -411,6 +443,7 @@ extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); __initfunc(static char *do_pcikbd_init_hw(void)) { + while(pcikbd_wait_for_input() != -1) ; @@ -458,40 +491,58 @@ __initfunc(void pcikbd_init_hw(void)) struct linux_ebus_child *child; char *msg; - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if(!strcmp(edev->prom_name, "8042")) { - for_each_edevchild(edev, child) { - if (!strcmp(child->prom_name, "kb_ps2")) - goto found; + if (pcikbd_mrcoffee) { + if ((pcikbd_iobase = (unsigned long) sparc_alloc_io(0x71300060, + 0, 8, "ps2kbd-regs", 0x0, 0)) == 0) { + prom_printf("pcikbd_init_hw: cannot map\n"); + return; + } + pcikbd_irq = 13 | 0x20; + if (request_irq(pcikbd_irq, &pcikbd_interrupt, + SA_SHIRQ, "keyboard", (void *)pcikbd_iobase)) { + printk("8042: cannot register IRQ %x\n", pcikbd_irq); + return; + } + printk("8042(kbd): iobase[%08x] irq[%x]\n", + (unsigned)pcikbd_iobase, pcikbd_irq); + } else { + for_each_ebus(ebus) { + for_each_ebusdev(edev, ebus) { + if(!strcmp(edev->prom_name, "8042")) { + for_each_edevchild(edev, child) { + if (!strcmp(child->prom_name, PCI_KB_NAME)) + goto found; + } } } } - } - printk("pcikbd_probe: no 8042 found\n"); - return; + printk("pcikbd_init_hw: no 8042 found\n"); + return; found: - pcikbd_iobase = child->base_address[0]; - if (check_region(pcikbd_iobase, sizeof(unsigned long))) { - printk("8042: can't get region %lx, %d\n", - pcikbd_iobase, (int)sizeof(unsigned long)); - return; - } - request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller"); + pcikbd_iobase = child->base_address[0]; + if (check_region(pcikbd_iobase, sizeof(unsigned long))) { + printk("8042: can't get region %lx, %d\n", + pcikbd_iobase, (int)sizeof(unsigned long)); + return; + } + request_region(pcikbd_iobase, sizeof(unsigned long), "8042 controller"); + + pcikbd_irq = child->irqs[0]; + if (request_irq(pcikbd_irq, &pcikbd_interrupt, + SA_SHIRQ, "keyboard", (void *)pcikbd_iobase)) { + printk("8042: cannot register IRQ %s\n", + __irq_itoa(pcikbd_irq)); + return; + } - pcikbd_irq = child->irqs[0]; - if (request_irq(pcikbd_irq, &pcikbd_interrupt, - SA_SHIRQ, "keyboard", (void *)pcikbd_iobase)) { - printk("8042: cannot register IRQ %s\n", + printk("8042(kbd) at 0x%lx (irq %s)\n", pcikbd_iobase, __irq_itoa(pcikbd_irq)); - return; } - printk("8042(kbd) at 0x%lx (irq %s)\n", pcikbd_iobase, - __irq_itoa(pcikbd_irq)); - kd_mksound = nop_kd_mksound; + +#ifdef __sparc_v9__ edev = 0; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { @@ -504,6 +555,8 @@ ebus_done: /* * XXX: my 3.1.3 PROM does not give me the beeper node for the audio * auxio register, though I know it is there... (ecd) + * + * Both JE1 & MrCoffe have no beeper. How about Krups? --zaitcev */ if (!edev) pcibeep_iobase = (pcikbd_iobase & ~(0xffffff)) | 0x722000; @@ -519,6 +572,7 @@ ebus_done: printk("8042(speaker): iobase[%016lx]%s\n", pcibeep_iobase, edev ? "" : " (forced)"); } +#endif disable_irq(pcikbd_irq); msg = do_pcikbd_init_hw(); @@ -534,8 +588,6 @@ ebus_done: * Here begins the Mouse Driver. */ -static int ms_node; - static unsigned long pcimouse_iobase = 0; static unsigned int pcimouse_irq; @@ -554,6 +606,8 @@ static int aux_ready = 0; static int aux_count = 0; static int aux_present = 0; +#ifdef __sparc_v9__ + static __inline__ unsigned char pcimouse_inb(unsigned long port) { return inb(port); @@ -564,6 +618,20 @@ static __inline__ void pcimouse_outb(unsigned char val, unsigned long port) outb(val, port); } +#else + +static __inline__ unsigned char pcimouse_inb(unsigned long port) +{ + return *(volatile unsigned char *)port; +} + +static __inline__ void pcimouse_outb(unsigned char val, unsigned long port) +{ + *(volatile unsigned char *)port = val; +} + +#endif + /* * Shared subroutines */ @@ -587,11 +655,11 @@ static inline int queue_empty(void) return queue->head == queue->tail; } -static int aux_fasync(struct file *filp, int on) +static int aux_fasync(int fd, struct file *filp, int on) { int retval; - retval = fasync_helper(filp, on, &queue->fasync); + retval = fasync_helper(fd, filp, on, &queue->fasync); if (retval < 0) return retval; return 0; @@ -623,8 +691,7 @@ static int poll_aux_status(void) == AUX_STAT_OBF) pcimouse_inb(pcimouse_iobase + KBD_DATA_REG); current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + (5*HZ + 99) / 100; - schedule(); + schedule_timeout((5*HZ + 99) / 100); retries++; } return (retries < MAX_RETRIES); @@ -721,7 +788,7 @@ void pcimouse_interrupt(int irq, void *dev_id, struct pt_regs *regs) static int aux_release(struct inode * inode, struct file * file) { - aux_fasync(file, 0); + aux_fasync(-1, file, 0); if (--aux_count) return 0; aux_start_atomic(); @@ -885,29 +952,43 @@ __initfunc(int pcimouse_init(void)) struct linux_ebus_device *edev; struct linux_ebus_child *child; - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if(!strcmp(edev->prom_name, "8042")) { - for_each_edevchild(edev, child) { - if (!strcmp(child->prom_name,"kdmouse")) - goto found; + if (pcikbd_mrcoffee) { + if ((pcimouse_iobase = pcikbd_iobase) == 0) { + printk("pcimouse_init: no 8042 given\n"); + return -ENODEV; + } + pcimouse_irq = pcikbd_irq; + } else { + for_each_ebus(ebus) { + for_each_ebusdev(edev, ebus) { + if(!strcmp(edev->prom_name, "8042")) { + for_each_edevchild(edev, child) { + if (!strcmp(child->prom_name, PCI_MS_NAME)) + goto found; + } } } } - } - printk("pcimouse_init: no 8042 found\n"); - return -ENODEV; + printk("pcimouse_init: no 8042 found\n"); + return -ENODEV; found: - pcimouse_iobase = child->base_address[0]; - /* - * Just in case the iobases for kbd/mouse ever differ... - */ - if (!check_region(pcimouse_iobase, sizeof(unsigned long))) - request_region(pcimouse_iobase, sizeof(unsigned long), - "8042 controller"); + pcimouse_iobase = child->base_address[0]; + /* + * Just in case the iobases for kbd/mouse ever differ... + */ + if (!check_region(pcimouse_iobase, sizeof(unsigned long))) + request_region(pcimouse_iobase, sizeof(unsigned long), + "8042 controller"); + + pcimouse_irq = child->irqs[0]; + } + + queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + memset(queue, 0, sizeof(*queue)); + queue->head = queue->tail = 0; + queue->proc_list = NULL; - pcimouse_irq = child->irqs[0]; if (request_irq(pcimouse_irq, &pcimouse_interrupt, SA_SHIRQ, "mouse", (void *)pcimouse_iobase)) { printk("8042: Cannot register IRQ %s\n", @@ -923,10 +1004,6 @@ found: pckbd_read_mask = AUX_STAT_OBF; misc_register(&psaux_mouse); - queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); - memset(queue, 0, sizeof(*queue)); - queue->head = queue->tail = 0; - queue->proc_list = NULL; aux_start_atomic(); pcimouse_outb(KBD_CCMD_MOUSE_ENABLE, pcimouse_iobase + KBD_CNTL_REG); aux_write_ack(AUX_RESET); @@ -956,8 +1033,22 @@ __initfunc(int ps2kbd_probe(unsigned long *memory_start)) char prop[128]; int len; +#ifndef __sparc_v9__ + /* + * MrCoffee has hardware but has no PROM nodes whatsoever. + */ + len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop)); + if (len < 0) { + printk("ps2kbd_probe: no name of root node\n"); + return -ENODEV; + } + if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) { + pcikbd_mrcoffee = 1; /* Brain damage detected */ + goto found; + } +#endif /* - * Get the nodes for keyboard and mouse from 'aliases'... + * Get the nodes for keyboard and mouse from aliases on normal systems. */ node = prom_getchild(prom_root_node); node = prom_searchsiblings(node, "aliases"); @@ -1020,17 +1111,14 @@ __initfunc(int ps2kbd_probe(unsigned long *memory_start)) * Does it match? */ dnode = prom_getchild(node); - dnode = prom_searchsiblings(dnode, "kb_ps2"); + dnode = prom_searchsiblings(dnode, PCI_KB_NAME); if (dnode == kbnode) { - kbd_node = kbnode; - beep_node = bnode; ++devices; } dnode = prom_getchild(node); - dnode = prom_searchsiblings(dnode, "kdmouse"); + dnode = prom_searchsiblings(dnode, PCI_MS_NAME); if (dnode == msnode) { - ms_node = msnode; ++devices; } diff --git a/drivers/sbus/char/rtc.c b/drivers/sbus/char/rtc.c index 8e001c0bd..1576a4a0b 100644 --- a/drivers/sbus/char/rtc.c +++ b/drivers/sbus/char/rtc.c @@ -1,4 +1,4 @@ -/* $Id: rtc.c,v 1.12 1998/05/08 21:04:35 davem Exp $ +/* $Id: rtc.c,v 1.13 1998/08/26 10:29:44 davem Exp $ * * Linux/SPARC Real Time Clock Driver * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c index 8f3d2a90a..3f0924525 100644 --- a/drivers/sbus/char/sab82532.c +++ b/drivers/sbus/char/sab82532.c @@ -1,4 +1,4 @@ -/* $Id: sab82532.c,v 1.20 1998/05/29 06:00:24 ecd Exp $ +/* $Id: sab82532.c,v 1.27 1998/11/08 11:15:25 davem Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -52,6 +52,13 @@ static int sab82532_refcount; #undef SERIAL_DEBUG_SEND_BREAK #undef SERIAL_DEBUG_INTR +/* Trace things on serial device, useful for console debugging: */ +#undef SERIAL_LOG_DEVICE + +#ifdef SERIAL_LOG_DEVICE +static void dprint_init(int tty); +#endif + static void change_speed(struct sab82532 *info); static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout); @@ -81,6 +88,7 @@ static char *sab82532_version[16] = { "V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)", "V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)" }; +static char serial_version[16]; /* * tmp_buf is used as a temporary buffer by sab82532_write. We need to @@ -498,7 +506,7 @@ check_modem: #endif } if ((info->regs->r.pvr & info->pvr_dsr_bit) ^ info->dsr) { - info->dsr = info->regs->r.pvr & info->pvr_dsr_bit; + info->dsr = (info->regs->r.pvr & info->pvr_dsr_bit) ? 0 : 1; info->icount.dsr++; modem_change++; #if 0 @@ -779,6 +787,12 @@ static int startup(struct sab82532 *info) */ sab82532_init_line(info); + if (info->tty->termios->c_cflag & CBAUD) { + info->regs->rw.mode &= ~(SAB82532_MODE_FRTS); + info->regs->rw.mode |= SAB82532_MODE_RTS; + info->regs->rw.pvr &= ~(info->pvr_dtr_bit); + } + /* * Finally, enable interrupts */ @@ -944,8 +958,9 @@ static void change_speed(struct sab82532 *info) ebrg = ebrg_table[i].n; ebrg |= (ebrg_table[i].m << 6); - if (ebrg_table[i].baud) - info->timeout = (info->xmit_fifo_size * HZ * bits) / ebrg_table[i].baud; + info->baud = ebrg_table[i].baud; + if (info->baud) + info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud; else info->timeout = 0; info->timeout += HZ / 50; /* Add .02 seconds of slop */ @@ -1271,7 +1286,9 @@ static int get_modem_info(struct sab82532 * info, unsigned int *value) { unsigned int result; - result = ((info->regs->r.mode & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS) + result = ((info->regs->r.mode & SAB82532_MODE_RTS) ? + ((info->regs->r.mode & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS) + : TIOCM_RTS) | ((info->regs->r.pvr & info->pvr_dtr_bit) ? 0 : TIOCM_DTR) | ((info->regs->r.vstr & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR) | ((info->regs->r.pvr & info->pvr_dsr_bit) ? 0 : TIOCM_DSR) @@ -1330,57 +1347,34 @@ static int set_modem_info(struct sab82532 * info, unsigned int cmd, /* * This routine sends a break character out the serial port. */ -static void send_break( struct sab82532 * info, int duration) +static void sab82532_break(struct tty_struct *tty, int break_state) { - if (!info->regs) - return; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("sab82532_send_break(%d) jiff=%lu...", duration, jiffies); -#endif - cli(); - info->regs->rw.dafo |= SAB82532_DAFO_XBRK; - schedule(); - info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK); - sti(); -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("done jiffies=%lu\n", jiffies); -#endif -} + struct sab82532 * info = (struct sab82532 *)tty->driver_data; + unsigned long flags; -/* - * This routine sets the break condition on the serial port. - */ -static void begin_break(struct sab82532 * info) -{ - if (!info->regs) + if (serial_paranoia_check(info, tty->device, "sab82532_break")) return; - info->regs->rw.dafo |= SAB82532_DAFO_XBRK; -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("begin_break: jiffies=%lu\n", jiffies); -#endif -} -/* - * This routine clears the break condition on the serial port. - */ -static void end_break(struct sab82532 * info) -{ if (!info->regs) return; - info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK); + #ifdef SERIAL_DEBUG_SEND_BREAK - printk("end_break: jiffies=%lu\n", jiffies); + printk("sab82532_break(%d) jiff=%lu...", break_state, jiffies); #endif + save_flags(flags); cli(); + if (break_state == -1) + info->regs->rw.dafo |= SAB82532_DAFO_XBRK; + else + info->regs->rw.dafo &= ~(SAB82532_DAFO_XBRK); + restore_flags(flags); } + static int sab82532_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { int error; struct sab82532 * info = (struct sab82532 *)tty->driver_data; - int retval; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ @@ -1396,43 +1390,6 @@ static int sab82532_ioctl(struct tty_struct *tty, struct file * file, } switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, HZ/4); /* 1/4 second */ - if (signal_pending(current)) - return -EINTR; - } - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*(HZ/10) : HZ/4); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCSBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - begin_break(info); - return 0; - case TIOCCBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - end_break(info); - return 0; case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); case TIOCSSOFTCAR: @@ -1672,8 +1629,7 @@ static void sab82532_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } @@ -1717,8 +1673,7 @@ static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout) while (info->xmit_cnt || !info->all_sent) { current->state = TASK_INTERRUPTIBLE; current->counter = 0; - current->timeout = jiffies + char_time; - schedule(); + schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && (orig_jiffies + timeout) < jiffies) @@ -2005,9 +1960,11 @@ static int sab82532_open(struct tty_struct *tty, struct file * filp) * /proc fs routines.... */ -static int inline line_info(char *buf, struct tty_struct *tty) +static __inline__ int +line_info(char *buf, struct sab82532 *info) { - struct sab82532 *info = (struct sab82532 *)tty->driver_data; + unsigned long flags; + char stat_buf[30]; int ret; ret = sprintf(buf, "%d: uart:SAB82532 ", info->line); @@ -2025,8 +1982,8 @@ static int inline line_info(char *buf, struct tty_struct *tty) ret += sprintf(buf+ret, "V?.? "); break; } - ret += sprintf(buf+ret, "port:%lX irq:%d", - (unsigned long)info->regs, info->irq); + ret += sprintf(buf+ret, "port:%lX irq:%s", + (unsigned long)info->regs, __irq_itoa(info->irq)); if (!info->regs) { ret += sprintf(buf+ret, "\n"); @@ -2036,20 +1993,57 @@ static int inline line_info(char *buf, struct tty_struct *tty) /* * Figure out the current RS-232 lines */ + stat_buf[0] = 0; + stat_buf[1] = 0; + save_flags(flags); cli(); + if (info->regs->r.mode & SAB82532_MODE_RTS) { + if (!(info->regs->r.mode & SAB82532_MODE_FRTS)) + strcat(stat_buf, "|RTS"); + } else { + strcat(stat_buf, "|RTS"); + } + if (info->regs->r.star & SAB82532_STAR_CTS) + strcat(stat_buf, "|CTS"); + if (!(info->regs->r.pvr & info->pvr_dtr_bit)) + strcat(stat_buf, "|DTR"); + if (!(info->regs->r.pvr & info->pvr_dsr_bit)) + strcat(stat_buf, "|DSR"); + if (!(info->regs->r.vstr & SAB82532_VSTR_CD)) + strcat(stat_buf, "|CD"); + restore_flags(flags); - ret += sprintf(buf+ret, "\n"); + if (info->baud) + ret += sprintf(buf+ret, " baud:%d", info->baud); + + if (info->icount.frame) + ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + + if (info->icount.parity) + ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + + if (info->icount.brk) + ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + + if (info->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + + /* + * Last thing is the RS-232 status lines. + */ + ret += sprintf(buf+ret, " %s\n", stat_buf + 1); return ret; } int sab82532_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - int i, len = 0; - off_t begin = 0; + struct sab82532 *info = sab82532_chain; + off_t begin = 0; + int len = 0; - len += sprintf(page, "serinfo:1.0 driver:%s\n", "$Revision: 1.20 $"); - for (i = 0; i < NR_PORTS && len < 4000; i++) { - len += line_info(page + len, sab82532_table[i]); + len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); + for (info = sab82532_chain; info && len < 4000; info = info->next) { + len += line_info(page + len, info); if (len+begin > off+count) goto done; if (len+begin < off) { @@ -2142,13 +2136,14 @@ sab82532_kgdb_hook(int line)) __initfunc(static inline void show_serial_version(void)) { - char *revision = "$Revision: 1.20 $"; + char *revision = "$Revision: 1.27 $"; char *version, *p; version = strchr(revision, ' '); - p = strchr(++version, ' '); + strcpy(serial_version, ++version); + p = strchr(serial_version, ' '); *p = '\0'; - printk("SAB82532 serial driver version %s\n", version); + printk("SAB82532 serial driver version %s\n", serial_version); } /* @@ -2203,6 +2198,7 @@ __initfunc(int sab82532_init(void)) serial_driver.stop = sab82532_stop; serial_driver.start = sab82532_start; serial_driver.hangup = sab82532_hangup; + serial_driver.break_ctl = sab82532_break; serial_driver.wait_until_sent = sab82532_wait_until_sent; serial_driver.read_proc = sab82532_read_proc; @@ -2274,6 +2270,10 @@ __initfunc(int sab82532_init(void)) info->line, (unsigned long)info->regs, __irq_itoa(info->irq), sab82532_version[info->type]); } + +#ifdef SERIAL_LOG_DEVICE + dprint_init(SERIAL_LOG_DEVICE); +#endif return 0; } @@ -2388,7 +2388,12 @@ sab82532_console_write(struct console *con, const char *s, unsigned n) struct sab82532 *info; int i; - info = sab82532_chain + con->index; + info = sab82532_chain; + for (i = con->index; i; i--) { + info = info->next; + if (!info) + return; + } for (i = 0; i < n; i++) { if (*s == '\n') @@ -2421,7 +2426,12 @@ sab82532_console_setup(struct console *con, char *options) int i, bits; unsigned long flags; - info = sab82532_chain + con->index; + info = sab82532_chain; + for (i = con->index; i; i--) { + info = info->next; + if (!info) + return -ENODEV; + } info->is_console = 1; /* @@ -2487,15 +2497,16 @@ sab82532_console_setup(struct console *con, char *options) if (i & CBAUDEX) { i &= ~(CBAUDEX); if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES)) - info->tty->termios->c_cflag &= ~CBAUDEX; + cflag &= ~CBAUDEX; else i += 15; } ebrg = ebrg_table[i].n; ebrg |= (ebrg_table[i].m << 6); - if (ebrg_table[i].baud) - info->timeout = (info->xmit_fifo_size * HZ * bits) / ebrg_table[i].baud; + info->baud = ebrg_table[i].baud; + if (info->baud) + info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud; else info->timeout = 0; info->timeout += HZ / 50; /* Add .02 seconds of slop */ @@ -2528,6 +2539,7 @@ sab82532_console_setup(struct console *con, char *options) info->regs->rw.mode &= ~(SAB82532_MODE_FRTS); info->regs->rw.mode |= SAB82532_MODE_FCTS; } + info->regs->rw.pvr &= ~(info->pvr_dtr_bit); info->regs->rw.mode |= SAB82532_MODE_RAC; restore_flags(flags); @@ -2565,4 +2577,36 @@ __initfunc(int sab82532_console_init(void)) return 0; } +#ifdef SERIAL_LOG_DEVICE + +static int serial_log_device = 0; + +static void +dprint_init(int tty) +{ + serial_console = tty + 1; + sab82532_console.index = tty; + sab82532_console_setup(&sab82532_console, ""); + serial_console = 0; + serial_log_device = tty + 1; +} + +int +dprintf(const char *fmt, ...) +{ + static char buffer[4096]; + va_list args; + int i; + + if (!serial_log_device) + return 0; + + va_start(args, fmt); + i = vsprintf(buffer, fmt, args); + va_end(args); + sab82532_console.write(&sab82532_console, buffer, i); + return i; +} +#endif /* SERIAL_LOG_DEVICE */ #endif /* CONFIG_SERIAL_CONSOLE */ + diff --git a/drivers/sbus/char/su.c b/drivers/sbus/char/su.c index 8ed6660b7..b05acc314 100644 --- a/drivers/sbus/char/su.c +++ b/drivers/sbus/char/su.c @@ -1,69 +1,164 @@ -/* $Id: su.c,v 1.10 1998/05/29 06:00:26 ecd Exp $ - * su.c: Small serial driver for keyboard/mouse interface on Ultra/AX +/* $Id: su.c,v 1.16 1998/11/14 23:02:54 ecd 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) * - * This is mainly a very stripped down version of drivers/char/serial.c, + * This is mainly a variation of drivers/char/serial.c, * credits go to authors mentioned therein. */ +/* + * Configuration section. + */ +#define SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO /* Unused on sparc */ +#define SERIAL_DO_RESTART + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_THROTTLE + +#define RS_ISR_PASS_LIMIT 256 + +/* + * 0x20 is sun4m thing, Dave Redman heritage. + * See arch/sparc/kernel/irq.c. + */ +#define IRQ_4M(n) ((n)|0x20) + +#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) +#define DBG_CNT(s) \ +do { \ + printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ + kdevname(tty->device), (info->flags), serial_refcount, \ + info->count,tty->count,s); \ +} while (0) +#else +#define DBG_CNT(s) +#endif + +/* + * End of serial driver configuration section. + */ +#include <linux/config.h> +#include <linux/module.h> #include <linux/errno.h> #include <linux/signal.h> +#include <linux/sched.h> #include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/serial_reg.h> #include <linux/string.h> +#include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/ioport.h> +#include <linux/mm.h> #include <linux/malloc.h> #include <linux/init.h> +#include <linux/delay.h> +#ifdef CONFIG_SERIAL_CONSOLE +#include <linux/console.h> +#include <linux/major.h> +#endif +#include <linux/sysrq.h> +#include <asm/system.h> #include <asm/oplib.h> #include <asm/io.h> #include <asm/ebus.h> #include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> #include "sunserial.h" #include "sunkbd.h" #include "sunmouse.h" -static char *serial_name = "kbd/mouse serial driver"; -static char *serial_version = "1.0"; - -/* Set of debugging defines */ -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN - /* We are on a NS PC87303 clocked with 24.0 MHz, which results * in a UART clock of 1.8462 MHz. */ #define BAUD_BASE (1846200 / 16) +#ifdef CONFIG_SERIAL_CONSOLE +extern int serial_console; +static struct console sercons; +int su_serial_console_init(void); +#endif + +/* + * serial.c saves memory when it allocates async_info upon first open. + * We have parts of state structure together because we do call startup + * for keyboard and mouse. + */ struct su_struct { int magic; unsigned long port; int baud_base; - int type; + int type; /* Hardware type: e.g. 16550 */ int irq; int flags; - unsigned char IER; - unsigned char MCR; int line; int cflag; + int kbd_node; int ms_node; + int port_node; + char name[16]; -}; -static struct su_struct su_table[] = { - { 0, 0, BAUD_BASE, PORT_UNKNOWN }, - { 0, 0, BAUD_BASE, PORT_UNKNOWN } + int xmit_fifo_size; + int custom_divisor; + unsigned short close_delay; + unsigned short closing_wait; /* time to wait before closing */ + + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int timeout; + int quot; + int x_char; /* xon/xoff character */ + int IER; /* Interrupt Enable Register */ + int MCR; /* Modem control register */ + unsigned long event; + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct tq_struct tqueue; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct wait_queue *delta_msr_wait; + + int count; + struct async_icount icount; + struct termios normal_termios, callout_termios; + unsigned long last_active; /* For async_struct, to be */ }; -#define NR_PORTS (sizeof(su_table) / sizeof(struct su_struct)) +static char *serial_name = "PCIO serial driver"; +static char serial_version[16]; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 -static void autoconfig(struct su_struct * info); +static void autoconfig(struct su_struct *info); static void change_speed(struct su_struct *info); +static void su_wait_until_sent(struct tty_struct *tty, int timeout); /* * Here we define the default xmit fifo size used for each type of @@ -83,12 +178,52 @@ static struct serial_uart_config uart_config[] = { { 0, 0} }; + +#define NR_PORTS 4 + +static struct su_struct su_table[NR_PORTS]; +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + /* - * This is used to figure out the divisor speeds and the timeouts + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; +static unsigned char *tmp_buf; +static struct semaphore tmp_buf_sem = MUTEX; + +static inline int serial_paranoia_check(struct su_struct *info, + kdev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null su_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +#ifdef __sparc_v9__ static inline unsigned int su_inb(struct su_struct *info, unsigned long offset) @@ -102,19 +237,117 @@ 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 -receive_chars(struct su_struct *info, struct pt_regs *regs) +su_outb(struct su_struct *info, unsigned long offset, int value) +{ + /* + * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are + * connected with a gate then go to SlavIO. When IRQ4 goes tristated + * gate outputs a logical one. Since we use level triggered interrupts + * we have lockup and watchdog reset. We cannot mask IRQ because + * keyboard shares IRQ with us (Word has it as Bob Smelik's design). + * 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 + +#define serial_in(info, off) su_inb(info, off) +#define serial_inp(info, off) su_inb(info, off) +#define serial_out(info, off, val) su_outb(info, off, val) +#define serial_outp(info, off, val) su_outb(info, off, val) + +/* + * ------------------------------------------------------------ + * su_stop() and su_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void su_stop(struct tty_struct *tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "su_stop")) + return; + + save_flags(flags); cli(); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + restore_flags(flags); +} + +static void su_start(struct tty_struct *tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "su_start")) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + restore_flags(flags); +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * su_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static __inline__ void +su_sched_event(struct su_struct *info, int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static __inline__ void +receive_kbd_ms_chars(struct su_struct *info, struct pt_regs *regs) { unsigned char status = 0; unsigned char ch; do { - ch = su_inb(info, UART_RX); - -#ifdef SERIAL_DEBUG_INTR - printk("DR%02x:%02x...", ch, status); -#endif - + ch = serial_inp(info, UART_RX); if (info->kbd_node) { if(ch == SUNKBD_RESET) { l1a_state.kbd_id = 1; @@ -142,64 +375,387 @@ receive_chars(struct su_struct *info, struct pt_regs *regs) } while (status & UART_LSR_DR); } +static __inline__ void +receive_serial_chars(struct su_struct *info, int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + int ignored = 0; + struct async_icount *icount; + + icount = &info->icount; + do { + ch = serial_inp(info, UART_RX); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("D%02x:%02x.", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + */ + if (*status & info->ignore_status_mask) { + if (++ignored > 100) { +#ifdef SERIAL_DEBUG_INTR + printk("ign100.."); +#endif + break; + } + goto ignore_char; + } + *status &= info->read_status_mask; + + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + if (*status & UART_LSR_OE) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } + } + } + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + ignore_char: + *status = serial_inp(info, UART_LSR); + } while (*status & UART_LSR_DR); +#ifdef SERIAL_DEBUG_INTR + printk("E%02x.R%d", *status, tty->flip.count); +#endif + tty_flip_buffer_push(tty); +} + +static __inline__ void +transmit_chars(struct su_struct *info, int *intr_done) +{ + int count; + + if (info->x_char) { + serial_outp(info, UART_TX, info->x_char); + info->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if ((info->xmit_cnt <= 0) || info->tty->stopped || + info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + return; + } + + count = info->xmit_fifo_size; + do { + serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->icount.tx++; + if (--info->xmit_cnt <= 0) + break; + } while (--count > 0); + + if (info->xmit_cnt < WAKEUP_CHARS) + su_sched_event(info, RS_EVENT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("T%d...", info->xmit_cnt); +#endif + if (intr_done) + *intr_done = 0; + + if (info->xmit_cnt <= 0) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } +} + +static __inline__ void +check_modem_status(struct su_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + icount->cts++; + wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SERIAL_DEBUG_OPEN + printk("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + su_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + } + } +} + /* - * This is the serial driver's generic interrupt routine + * This is the kbd/mouse serial driver's interrupt routine */ static void -su_interrupt(int irq, void *dev_id, struct pt_regs * regs) +su_kbd_ms_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct su_struct *info = (struct su_struct *)dev_id; unsigned char status; #ifdef SERIAL_DEBUG_INTR - printk("su_interrupt(%s)...", __irq_itoa(irq)); + printk("su_kbd_ms_interrupt(%s)...", __irq_itoa(irq)); #endif + if (!info) + return; - if (su_inb(info, UART_IIR) & UART_IIR_NO_INT) + if (serial_in(info, UART_IIR) & UART_IIR_NO_INT) return; - status = su_inb(info, UART_LSR); + status = serial_inp(info, UART_LSR); #ifdef SERIAL_DEBUG_INTR printk("status = %x...", status); #endif if (status & UART_LSR_DR) - receive_chars(info, regs); + receive_kbd_ms_chars(info, regs); + +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +/* + * This is the serial driver's generic interrupt routine + */ +static void +su_serial_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + int status; + struct su_struct *info; + int pass_counter = 0; + +#ifdef SERIAL_DEBUG_INTR + printk("su_serial_interrupt(%s)...", __irq_itoa(irq)); +#endif + info = (struct su_struct *)dev_id; + if (!info || !info->tty) { +#ifdef SERIAL_DEBUG_INTR + printk("strain\n"); +#endif + return; + } + + do { + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_serial_chars(info, &status, regs); + check_modem_status(info); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); + + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#ifdef SERIAL_DEBUG_INTR + printk("rs loop break"); +#endif + break; /* Prevent infinite loops */ + } + } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + + info->last_active = jiffies; #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif } +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * su_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using su_sched_event(), and they get done here. + */ +static void do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void do_softint(void *private_) +{ + struct su_struct *info = (struct su_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +/* + * --------------------------------------------------------------- + * Low level utility subroutines for the serial driver: routines to + * figure out the appropriate timeout for an interrupt chain, routines + * to initialize and startup a serial port, and routines to shutdown a + * serial port. Useful stuff like that. + * --------------------------------------------------------------- + */ + static int -startup(struct su_struct * info) +startup(struct su_struct *info) { unsigned long flags; int retval=0; + unsigned long page; - save_flags(flags); cli(); + save_flags(flags); + if (info->tty) { + page = get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; - if (info->flags & ASYNC_INITIALIZED) { - goto errout; - } + cli(); - if (!info->port || !info->type) { - goto errout; + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (info->port == 0 || info->type == PORT_UNKNOWN) { + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; } + cli(); #ifdef SERIAL_DEBUG_OPEN - printk("starting up su%d (irq %s)...", info->line, + printk("starting up ttys%d (irq %s)...", info->line, __irq_itoa(info->irq)); #endif - if (info->type == PORT_16750) - su_outb(info, UART_IER, 0); + if (uart_config[info->type].flags & UART_STARTECH) { + /* Wake up UART */ + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, 0); + serial_outp(info, UART_EFR, 0); + serial_outp(info, UART_LCR, 0); + } + + if (info->type == PORT_16750) { + /* Wake up UART */ + serial_outp(info, UART_IER, 0); + } /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ if (uart_config[info->type].flags & UART_CLEAR_FIFO) - su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR | + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); /* @@ -207,47 +763,84 @@ startup(struct su_struct * info) * if it is, then bail out, because there's likely no UART * here. */ - if (su_inb(info, UART_LSR) == 0xff) { - retval = -ENODEV; + if (serial_inp(info, UART_LSR) == 0xff) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + } else + retval = -ENODEV; goto errout; } - + /* * Allocate the IRQ if necessary */ - retval = request_irq(info->irq, su_interrupt, SA_SHIRQ, - info->name, info); - if (retval) + if (info->kbd_node || info->ms_node) { + retval = request_irq(info->irq, su_kbd_ms_interrupt, + SA_SHIRQ, info->name, info); + } else { + retval = request_irq(info->irq, su_serial_interrupt, + SA_SHIRQ, info->name, info); + } + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + retval = 0; + } goto errout; + } /* * Clear the interrupt registers. */ - su_inb(info, UART_RX); - su_inb(info, UART_IIR); - su_inb(info, UART_MSR); + (void) serial_inp(info, UART_RX); + (void) serial_inp(info, UART_IIR); + (void) serial_inp(info, UART_MSR); /* * Now, initialize the UART */ - su_outb(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ - info->MCR = UART_MCR_OUT2; - su_outb(info, UART_MCR, info->MCR); + info->MCR = 0; + if (info->tty && info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; + if (info->irq != 0) + info->MCR |= UART_MCR_OUT2; + serial_outp(info, UART_MCR, info->MCR); /* * Finally, enable interrupts */ - info->IER = UART_IER_RLSI | UART_IER_RDI; - su_outb(info, UART_IER, info->IER); /* enable interrupts */ - + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); /* enable interrupts */ + /* * And clear the interrupt registers again for luck. */ - su_inb(info, UART_LSR); - su_inb(info, UART_RX); - su_inb(info, UART_IIR); - su_inb(info, UART_MSR); + (void)serial_inp(info, UART_LSR); + (void)serial_inp(info, UART_RX); + (void)serial_inp(info, UART_IIR); + (void)serial_inp(info, UART_MSR); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * Set up the tty->alt_speed kludge + */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } /* * and set the speed of the serial port @@ -257,38 +850,135 @@ startup(struct su_struct * info) info->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0; - + errout: restore_flags(flags); return retval; } /* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void +shutdown(struct su_struct *info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * Free the IRQ, if necessary + */ + free_irq(info->irq, info); + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + info->IER = 0; + serial_outp(info, UART_IER, 0x00); /* disable all intrs */ + info->MCR &= ~UART_MCR_OUT2; + + /* disable break condition */ + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + + /* disable FIFO's */ + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + (void)serial_in(info, UART_RX); /* read data port to reset things */ + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (uart_config[info->type].flags & UART_STARTECH) { + /* Arrange to enter sleep mode */ + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, UART_IERX_SLEEP); + serial_outp(info, UART_LCR, 0); + } + if (info->type == PORT_16750) { + /* Arrange to enter sleep mode */ + serial_outp(info, UART_IER, UART_IERX_SLEEP); + } + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +static __inline__ int +su_get_baud_rate(struct su_struct *info) +{ + static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, + 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 + }; + int i; + + if (info->tty) + return tty_get_baud_rate(info->tty); + + i = info->cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~(CBAUDEX); + if (i < 1 || i > 4) + info->cflag &= ~(CBAUDEX); + else + i += 15; + } + return baud_table[i]; +} + +/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ static void change_speed(struct su_struct *info) { - unsigned char cval, fcr = 0; - int quot = 0; - unsigned long flags; - int i; + int quot = 0, baud; + unsigned int cval, fcr = 0; + int bits; + unsigned long flags; + + if (!info->kbd_node && !info->ms_node) { + if (!info->tty || !info->tty->termios) + return; + if (!info->port) + return; + info->cflag = info->tty->termios->c_cflag; + } /* byte size and parity */ switch (info->cflag & CSIZE) { - case CS5: cval = 0x00; break; - case CS6: cval = 0x01; break; - case CS7: cval = 0x02; break; - case CS8: cval = 0x03; break; + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; /* Never happens, but GCC is too dumb to figure it out */ - default: cval = 0x00; break; + default: cval = 0x00; bits = 7; break; } if (info->cflag & CSTOPB) { cval |= 0x04; + bits++; } if (info->cflag & PARENB) { cval |= UART_LCR_PARITY; + bits++; } if (!(info->cflag & PARODD)) cval |= UART_LCR_EPAR; @@ -298,54 +988,139 @@ change_speed(struct su_struct *info) #endif /* Determine divisor based on baud rate */ - i = info->cflag & CBAUD; - if (i & CBAUDEX) { - i &= ~CBAUDEX; - if (i < 1 || i > 4) - info->cflag &= ~CBAUDEX; - else - i += 15; - } - if (!quot) { - if (baud_table[i] == 134) + baud = su_get_baud_rate(info); + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->custom_divisor; + else { + if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2 * info->baud_base / 269); - else if (baud_table[i]) - quot = info->baud_base / baud_table[i]; - /* If the quotient is ever zero, default to 1200 bps */ - if (!quot) - quot = info->baud_base / 1200; + else if (baud) + quot = info->baud_base / baud; } + /* If the quotient is ever zero, default to 9600 bps */ + if (!quot) + quot = info->baud_base / 9600; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / info->baud_base); + info->timeout += HZ/50; /* Add .02 seconds of slop */ /* Set up FIFO's */ - if (uart_config[info->type].flags & UART_USE_FIFO) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + if (uart_config[info->type].flags & UART_USE_FIFO) { + if ((info->baud_base / quot) < 9600) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (info->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (info->cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (info->cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); + + /* + * Set up parity check flag + */ + if (info->tty) { +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - su_outb(info, UART_IER, info->IER); + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | + UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_OE; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((info->cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; + } save_flags(flags); cli(); - su_outb(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ - su_outb(info, UART_DLL, quot & 0xff); /* LS of divisor */ - su_outb(info, UART_DLM, quot >> 8); /* MS of divisor */ + if (uart_config[info->type].flags & UART_STARTECH) { + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, + (info->cflag & CRTSCTS) ? UART_EFR_CTS : 0); + } + serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */ if (info->type == PORT_16750) - su_outb(info, UART_FCR, fcr); /* set fcr */ - su_outb(info, UART_LCR, cval); /* reset DLAB */ + serial_outp(info, UART_FCR, fcr); /* set fcr */ + serial_outp(info, UART_LCR, cval); /* reset DLAB */ if (info->type != PORT_16750) - su_outb(info, UART_FCR, fcr); /* set fcr */ + serial_outp(info, UART_FCR, fcr); /* set fcr */ restore_flags(flags); + info->quot = quot; } static void -su_put_char(unsigned char c) +su_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "su_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE-1; + info->xmit_cnt++; + restore_flags(flags); +} + +static void su_put_char_kbd(unsigned char c) { struct su_struct *info = su_table; int lsr; if (!info->kbd_node) ++info; + if (!info) + return; do { - lsr = inb(info->port + UART_LSR); + lsr = serial_in(info, UART_LSR); } while (!(lsr & UART_LSR_THRE)); /* Send the character out. */ @@ -385,14 +1160,1046 @@ su_change_mouse_baud(int baud) change_speed(info); } +static void +su_flush_chars(struct tty_struct *tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "su_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + save_flags(flags); cli(); + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + restore_flags(flags); +} + +static int +su_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "su_write")) + return 0; + + if (!tty || !info->xmit_buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + c = MIN(count, + MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + while (1) { + cli(); + c = MIN(count, + MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + restore_flags(flags); + break; + } + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + } + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + return ret; +} + +static int +su_write_room(struct tty_struct *tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + int ret; + + if (serial_paranoia_check(info, tty->device, "su_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + +static int +su_chars_in_buffer(struct tty_struct *tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "su_chars_in_buffer")) + return 0; + return info->xmit_cnt; +} + +static void +su_flush_buffer(struct tty_struct *tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "su_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void +su_send_xchar(struct tty_struct *tty, char ch) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "su_send_char")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } +} + +/* + * ------------------------------------------------------------ + * su_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void +su_throttle(struct tty_struct * tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "su_throttle")) + return; + + if (I_IXOFF(tty)) + su_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void +su_unthrottle(struct tty_struct * tty) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "su_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + su_send_xchar(tty, START_CHAR(tty)); + } + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * su_ioctl() and friends + * ------------------------------------------------------------ + */ + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int +get_lsr_info(struct su_struct * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, UART_LSR); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + return put_user(result,value); +} + + +static int +get_modem_info(struct su_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); + return put_user(result,value); +} + +static int +set_modem_info(struct su_struct * info, unsigned int cmd, unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + + error = get_user(arg, value); + if (error) + return error; + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + return 0; +} + +/* + * su_break() --- routine which turns the break handling on or off + */ +static void +su_break(struct tty_struct *tty, int break_state) +{ + struct su_struct * info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "su_break")) + return; + + if (!info->port) + return; + save_flags(flags); cli(); + if (break_state == -1) + serial_out(info, UART_LCR, + serial_inp(info, UART_LCR) | UART_LCR_SBC); + else + serial_out(info, UART_LCR, + serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + restore_flags(flags); +} + +static int +su_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct su_struct * info = (struct su_struct *)tty->driver_data; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + + if (serial_paranoia_check(info, tty->device, "su_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + +#if 0 + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; +#endif + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + cli(); + /* note the counters on entry */ + cprev = info->icount; + sti(); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + cli(); + cnow = info->icount; /* atomic copy */ + sti(); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + cli(); + cnow = info->icount; + sti(); + p_cuser = (struct serial_icounter_struct *) arg; + error = put_user(cnow.cts, &p_cuser->cts); + if (error) return error; + error = put_user(cnow.dsr, &p_cuser->dsr); + if (error) return error; + error = put_user(cnow.rng, &p_cuser->rng); + if (error) return error; + error = put_user(cnow.dcd, &p_cuser->dcd); + if (error) return error; + return 0; + + default: + return -ENOIOCTLCMD; + } + /* return 0; */ /* Trigger warnings if fall through by a chance. */ +} + +static void +su_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if ( (tty->termios->c_cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(tty->termios->c_cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (tty->termios->c_cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + su_start(tty); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * su_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void +su_close(struct tty_struct *tty, struct file * filp) +{ + struct su_struct *info = (struct su_struct *)tty->driver_data; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "su_close")) + return; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("su_close ttys%d, count = %d\n", info->line, info->count); +#endif + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("su_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("su_close: bad serial port count for ttys%d: %d\n", + info->line, info->count); + info->count = 0; + } + if (info->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + info->read_status_mask &= ~UART_LSR_DR; + if (info->flags & ASYNC_INITIALIZED) { + serial_out(info, UART_IER, info->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + su_wait_until_sent(tty, info->timeout); + } + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; + restore_flags(flags); +} + +/* + * su_wait_until_sent() --- wait until the transmitter is empty + */ +static void +su_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct su_struct * info = (struct su_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "su_wait_until_sent")) + return; + + if (info->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; /* Just in case ... */ + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout) + char_time = MIN(char_time, timeout); +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In su_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + current->state = TASK_RUNNING; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * su_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void +su_hangup(struct tty_struct *tty) +{ + struct su_struct * info = (struct su_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "su_hangup")) + return; + + su_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * su_open() and friends + * ------------------------------------------------------------ + */ +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct su_struct *info) +{ + struct wait_queue wait = { current, NULL }; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * su_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + info->line, info->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + info->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); + restore_flags(flags); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, info->count); +#endif + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + info->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, info->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int +su_open(struct tty_struct *tty, struct file * filp) +{ + struct su_struct *info; + int retval, line; + unsigned long page; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + return -ENODEV; + info = su_table + line; + info->count++; + tty->driver_data = info; + info->tty = tty; + + if (serial_paranoia_check(info, tty->device, "su_open")) { + info->count--; + return -ENODEV; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("su_open %s%d, count = %d\n", tty->driver.name, info->line, + info->count); +#endif + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + if (!tmp_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + MOD_INC_USE_COUNT; + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("su_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + if ((info->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + change_speed(info); + } +#ifdef CONFIG_SERIAL_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("su_open ttys%d successful...", info->line); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ +static __inline__ int +line_info(char *buf, struct su_struct *info) +{ + char stat_buf[30], control, status; + int ret; + unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%X irq:%s", + info->line, uart_config[info->type].name, + (int)info->port, __irq_itoa(info->irq)); + + if (info->port == 0 || info->type == PORT_UNKNOWN) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + /* + * Figure out the current RS-232 lines + */ + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); + + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + info->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + info->icount.tx, info->icount.rx); + + if (info->icount.frame) + ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + + if (info->icount.parity) + ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + + if (info->icount.brk) + ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + + if (info->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +int su_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + len += line_info(page + len, &su_table[i]); + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * su_init() and friends + * + * su_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + /* * This routine prints out the appropriate serial driver version * number, and identifies which options were configured into this * driver. */ -static inline void -show_su_version(void) +__initfunc(static __inline__ void show_su_version(void)) { + char *revision = "$Revision: 1.16 $"; + char *version, *p; + + version = strchr(revision, ' '); + strcpy(serial_version, ++version); + p = strchr(serial_version, ' '); + *p = '\0'; printk(KERN_INFO "%s version %s\n", serial_name, serial_version); } @@ -407,10 +2214,15 @@ static void 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 + struct linux_prom_registers reg0; +#endif unsigned long flags; +#ifdef __sparc_v9__ for_each_ebus(ebus) { for_each_ebusdev(dev, ebus) { if (!strncmp(dev->prom_name, "su", 2)) { @@ -430,9 +2242,29 @@ ebus_done: return; info->irq = dev->irqs[0]; +#else + if (!info->port_node) + return; + + if (prom_getproperty(info->port_node, "reg", + (char *)®0, sizeof(reg0)) == -1) { + prom_printf("su: no \"reg\" property\n"); + return; + } + prom_apply_obio_ranges(®0, 1); + if ((info->port = (unsigned long) sparc_alloc_io(reg0.phys_addr, + 0, reg0.reg_size, "su-regs", reg0.which_io, 0)) == 0) { + prom_printf("su: cannot map\n"); + return; + } + /* + * There is no intr property on MrCoffee, so hardwire it. Krups? + */ + info->irq = IRQ_4M(13); +#endif #ifdef DEBUG_SERIAL_OPEN - printk("Found 'su' at %016lx IRQ %d,%x\n", dev->base_address[0], + printk("Found 'su' at %016lx IRQ %s\n", dev->base_address[0], __irq_itoa(dev->irqs[0])); #endif @@ -449,33 +2281,33 @@ ebus_done: * 0x80 is a non-existent port; which should be safe since * include/asm/io.h also makes this assumption. */ - scratch = su_inb(info, UART_IER); - su_outb(info, UART_IER, 0); - scratch2 = su_inb(info, UART_IER); - su_outb(info, UART_IER, scratch); + scratch = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0); + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); if (scratch2) { restore_flags(flags); return; /* We failed; there's nothing here */ } - scratch = su_inb(info, UART_MCR); - su_outb(info, UART_MCR, UART_MCR_LOOP | scratch); - scratch2 = su_inb(info, UART_MSR); - su_outb(info, UART_MCR, UART_MCR_LOOP | 0x0A); - status1 = su_inb(info, UART_MSR) & 0xF0; - su_outb(info, UART_MCR, scratch); - su_outb(info, UART_MSR, scratch2); +#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) { restore_flags(flags); return; } +#endif - scratch2 = su_inb(info, UART_LCR); - su_outb(info, UART_LCR, 0xBF); /* set up for StarTech test */ - su_outb(info, UART_EFR, 0); /* EFR is the same as FCR */ - su_outb(info, UART_LCR, 0); - su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO); - scratch = su_inb(info, UART_IIR) >> 6; + scratch2 = serial_in(info, UART_LCR); + serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ + serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */ + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 6; switch (scratch) { case 0: info->type = PORT_16450; @@ -492,58 +2324,65 @@ ebus_done: } if (info->type == PORT_16550A) { /* Check for Startech UART's */ - su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB); - if (su_inb(info, UART_EFR) == 0) { + serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB); + if (serial_in(info, UART_EFR) == 0) { info->type = PORT_16650; } else { - su_outb(info, UART_LCR, 0xBF); - if (su_inb(info, UART_EFR) == 0) + serial_outp(info, UART_LCR, 0xBF); + if (serial_in(info, UART_EFR) == 0) info->type = PORT_16650V2; } } if (info->type == PORT_16550A) { /* Check for TI 16750 */ - su_outb(info, UART_LCR, scratch2 | UART_LCR_DLAB); - su_outb(info, UART_FCR, + serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - scratch = su_inb(info, UART_IIR) >> 5; + scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 7) { - su_outb(info, UART_LCR, 0); - su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO); - scratch = su_inb(info, UART_IIR) >> 5; + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 5; if (scratch == 6) info->type = PORT_16750; } - su_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); } - su_outb(info, UART_LCR, scratch2); + serial_outp(info, UART_LCR, scratch2); if (info->type == PORT_16450) { - scratch = su_inb(info, UART_SCR); - su_outb(info, UART_SCR, 0xa5); - status1 = su_inb(info, UART_SCR); - su_outb(info, UART_SCR, 0x5a); - status2 = su_inb(info, UART_SCR); - su_outb(info, UART_SCR, scratch); + scratch = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0xa5); + status1 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, 0x5a); + status2 = serial_in(info, UART_SCR); + serial_outp(info, UART_SCR, scratch); if ((status1 != 0xa5) || (status2 != 0x5a)) info->type = PORT_8250; } + info->xmit_fifo_size = uart_config[info->type].dfl_xmit_fifo_size; if (info->type == PORT_UNKNOWN) { restore_flags(flags); return; } - sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd"); + if (info->kbd_node || info->ms_node) + sprintf(info->name, "su(%s)", info->ms_node ? "mouse" : "kbd"); + else + strcpy(info->name, "su(serial)"); + +#ifdef __sparc_v9__ request_region(info->port, 8, info->name); +#endif /* * Reset the UART. */ - su_outb(info, UART_MCR, 0x00); - su_outb(info, UART_FCR, (UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT)); - su_inb(info, UART_RX); + serial_outp(info, UART_MCR, 0x00); + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT)); + (void)serial_in(info, UART_RX); + serial_outp(info, UART_IER, 0x00); restore_flags(flags); } @@ -551,19 +2390,117 @@ ebus_done: /* * The serial driver boot-time initialization code! */ -__initfunc(int su_init(void)) +__initfunc(int su_serial_init(void)) { int i; struct su_struct *info; - + + init_bh(SERIAL_BH, do_serial_bh); show_su_version(); + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "su"; + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = su_open; + serial_driver.close = su_close; + serial_driver.write = su_write; + serial_driver.put_char = su_put_char; + serial_driver.flush_chars = su_flush_chars; + serial_driver.write_room = su_write_room; + serial_driver.chars_in_buffer = su_chars_in_buffer; + serial_driver.flush_buffer = su_flush_buffer; + serial_driver.ioctl = su_ioctl; + serial_driver.throttle = su_throttle; + serial_driver.unthrottle = su_unthrottle; + serial_driver.send_xchar = su_send_xchar; + serial_driver.set_termios = su_set_termios; + serial_driver.stop = su_stop; + serial_driver.start = su_start; + serial_driver.hangup = su_hangup; + serial_driver.break_ctl = su_break; + serial_driver.wait_until_sent = su_wait_until_sent; + serial_driver.read_proc = su_read_proc; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register regular su\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout su\n"); + for (i = 0, info = su_table; i < NR_PORTS; i++, info++) { info->line = i; + info->type = PORT_UNKNOWN; + info->baud_base = BAUD_BASE; + /* info->flags = 0; */ + info->custom_divisor = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = 30*HZ; + info->callout_termios = callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + info->icount.cts = info->icount.dsr = + info->icount.rng = info->icount.dcd = 0; + info->icount.rx = info->icount.tx = 0; + info->icount.frame = info->icount.parity = 0; + info->icount.overrun = info->icount.brk = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->cflag = serial_driver.init_termios.c_cflag; + + autoconfig(info); + 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), + uart_config[info->type].name); + } + + return 0; +} + +__initfunc(int su_kbd_ms_init(void)) +{ + int i; + struct su_struct *info; + + show_su_version(); + + for (i = 0, info = su_table; i < 2; i++, info++) { + info->line = i; + info->type = PORT_UNKNOWN; + info->baud_base = BAUD_BASE; + if (info->kbd_node) - info->cflag = B1200 | CS8 | CREAD; + info->cflag = B1200 | CS8 | CLOCAL | CREAD; else - info->cflag = B4800 | CS8 | CREAD; + info->cflag = B4800 | CS8 | CLOCAL | CREAD; autoconfig(info); if (info->type == PORT_UNKNOWN) @@ -575,7 +2512,7 @@ __initfunc(int su_init(void)) startup(info); if (info->kbd_node) - keyboard_zsinit(su_put_char); + keyboard_zsinit(su_put_char_kbd); else sun_mouse_zsinit(); } @@ -592,6 +2529,33 @@ __initfunc(int su_probe (unsigned long *memory_start)) 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; + } + } + } + + /* * Get the nodes for keyboard and mouse from 'aliases'... */ node = prom_getchild(prom_root_node); @@ -619,7 +2583,9 @@ __initfunc(int su_probe (unsigned long *memory_start)) * Find matching EBus nodes... */ node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "pci"); + if ((node = prom_searchsiblings(node, "pci")) == 0) { + return -ENODEV; /* Plain sparc */ + } /* * Check for SUNW,sabre on Ultra 5/10/AXi. @@ -668,7 +2634,7 @@ __initfunc(int su_probe (unsigned long *memory_start)) /* * Found everything we need? */ - if (devices == NR_PORTS) + if (devices == 2) goto found; sunode = prom_getsibling(sunode); @@ -687,7 +2653,7 @@ __initfunc(int su_probe (unsigned long *memory_start)) return -ENODEV; found: - sunserial_setinitfunc(memory_start, su_init); + 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; @@ -695,9 +2661,266 @@ found: 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; } + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static __inline__ void +wait_for_xmitr(struct su_struct *info) +{ + int lsr; + unsigned int tmout = 1000000; + + do { + lsr = su_inb(info, UART_LSR); + if (--tmout == 0) + break; + } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +static void +serial_console_write(struct console *co, const char *s, + unsigned count) +{ + struct su_struct *info; + int ier; + unsigned i; + + info = su_table + co->index; + /* + * First save the IER then disable the interrupts + */ + ier = su_inb(info, UART_IER); + su_outb(info, UART_IER, 0x00); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + su_outb(info, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(info); + su_outb(info, UART_TX, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); + su_outb(info, UART_IER, ier); +} + +/* + * Receive character from the serial port + */ +static int +serial_console_wait_key(struct console *co) +{ + struct su_struct *info; + int ier; + int lsr; + int c; + + info = su_table + co->index; + + /* + * First save the IER then disable the interrupts so + * that the real driver for the port does not get the + * character. + */ + ier = su_inb(info, UART_IER); + su_outb(info, UART_IER, 0x00); + + do { + lsr = su_inb(info, UART_LSR); + } while (!(lsr & UART_LSR_DR)); + c = su_inb(info, UART_RX); + + /* + * Restore the interrupts + */ + su_outb(info, UART_IER, ier); + + return c; +} + +static kdev_t +serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity. We do two things here: + * - construct a cflag setting for the first su_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +__initfunc(static int serial_console_setup(struct console *co, char *options)) +{ + struct su_struct *info; + unsigned cval; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* + * Divisor, bytesize and parity + */ + info = su_table + co->index; + quot = BAUD_BASE / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + su_outb(info, UART_IER, 0); + su_outb(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + su_outb(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + su_outb(info, UART_DLL, quot & 0xff); /* LS of divisor */ + su_outb(info, UART_DLM, quot >> 8); /* MS of divisor */ + su_outb(info, UART_LCR, cval); /* reset DLAB */ + info->quot = quot; + + /* + * If we read 0xff from the LSR, there is no UART here. + */ + if (su_inb(info, UART_LSR) == 0xff) + return -1; + + return 0; +} + +static struct console sercons = { + "ttyS", + serial_console_write, + NULL, + serial_console_device, + serial_console_wait_key, + NULL, + serial_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +/* + * Register console. + */ +__initfunc(int su_serial_console_init(void)) +{ + extern int con_is_present(void); + + if (con_is_present()) + return 0; + if (serial_console == 0) + return 0; + if (su_table[0].port == 0 || su_table[0].port_node == 0) + return 0; + sercons.index = 0; + register_console(&sercons); + return 0; +} + +#endif /* CONFIG_SERIAL_CONSOLE */ diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index 59b77a5e4..b720afbec 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -1325,11 +1325,11 @@ kbd_read (struct file *f, char *buffer, size_t count, loff_t *ppos) } /* Needed by X */ -static int kbd_fasync (struct file *filp, int on) +static int kbd_fasync (int fd, struct file *filp, int on) { int retval; - retval = fasync_helper (filp, on, &kb_fasync); + retval = fasync_helper (fd, filp, on, &kb_fasync); if (retval < 0) return retval; return 0; @@ -1479,7 +1479,7 @@ kbd_close (struct inode *i, struct file *f) kbd_redirected = 0; kbd_opened = 0; - kbd_fasync (f, 0); + kbd_fasync (-1, f, 0); return 0; } diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c index 3a6d672fb..1c000b661 100644 --- a/drivers/sbus/char/sunmouse.c +++ b/drivers/sbus/char/sunmouse.c @@ -329,11 +329,11 @@ sun_mouse_open(struct inode * inode, struct file * file) return 0; } -static int sun_mouse_fasync (struct file *filp, int on) +static int sun_mouse_fasync (int fd, struct file *filp, int on) { int retval; - retval = fasync_helper (filp, on, &sunmouse.fasync); + retval = fasync_helper (fd, filp, on, &sunmouse.fasync); if (retval < 0) return retval; return 0; @@ -342,7 +342,7 @@ static int sun_mouse_fasync (struct file *filp, int on) static int sun_mouse_close(struct inode *inode, struct file *file) { - sun_mouse_fasync (file, 0); + sun_mouse_fasync (-1, file, 0); if (--sunmouse.active) return 0; sunmouse.ready = 0; diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c index 5a45c85ae..30ff83e4f 100644 --- a/drivers/sbus/char/sunserial.c +++ b/drivers/sbus/char/sunserial.c @@ -1,4 +1,4 @@ -/* $Id: sunserial.c,v 1.61 1998/07/28 13:59:52 jj Exp $ +/* $Id: sunserial.c,v 1.67 1998/10/25 03:22:46 jj Exp $ * serial.c: Serial port driver infrastructure for the Sparc. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -69,8 +69,9 @@ rs_kgdb_hook(int channel)) rs_ops.rs_kgdb_hook(channel); } -__initfunc(void serial_console_init(void)) +__initfunc(long serial_console_init(long kmem_start, long kmem_end)) { + return kmem_start; } void rs_change_mouse_baud(int baud) @@ -169,7 +170,6 @@ int getkeycode (unsigned int scancode) return kbd_ops.getkeycode(scancode); } - void sunserial_setinitfunc(unsigned long *memory_start, int (*init) (void)) { @@ -343,29 +343,41 @@ sunkbd_install_keymaps(unsigned long *memory_start, } #endif +extern int su_probe(unsigned long *); extern int zs_probe(unsigned long *); #ifdef CONFIG_SAB82532 extern int sab82532_probe(unsigned long *); #endif #ifdef CONFIG_PCI extern int ps2kbd_probe(unsigned long *); -extern int su_probe(unsigned long *); #endif __initfunc(unsigned long sun_serial_setup(unsigned long memory_start)) { - int ret = -ENODEV; - - /* Probe for controllers. */ - ret = zs_probe(&memory_start); - if (!ret) + int ret = 1; + +#if defined(CONFIG_PCI) && !defined(__sparc_v9__) + /* + * Probing sequence on sparc differs from sparc64. + * Keyboard is probed ahead of su because we want su function + * when keyboard is active. su is probed ahead of zs in order to + * get console on MrCoffee with fine but disconnected zs. + */ + if (!serial_console) + ps2kbd_probe(&memory_start); + if (su_probe(&memory_start) == 0) return memory_start; +#endif + if (zs_probe(&memory_start) == 0) + return memory_start; + #ifdef CONFIG_SAB82532 ret = sab82532_probe(&memory_start); #endif -#ifdef CONFIG_PCI + +#if defined(CONFIG_PCI) && defined(__sparc_v9__) /* * Keyboard serial devices. * @@ -384,8 +396,17 @@ sun_serial_setup(unsigned long memory_start)) return memory_start; } #endif + if (!ret) return memory_start; + +#ifdef __sparc_v9__ + ret = prom_finddevice("/ssp-serial"); + if (ret && ret != -1) { + /* Hello, Starfire. Pleased to meet you :) */ + return memory_start; + } +#endif prom_printf("No serial devices found, bailing out.\n"); prom_halt(); diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index b16581679..48661f34b 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -21,7 +21,6 @@ #include <linux/errno.h> #include <linux/sched.h> #include <linux/fs.h> -#include <linux/uaccess.h> #include <asm/openprom.h> #include <asm/oplib.h> @@ -31,6 +30,7 @@ #include <asm/delay.h> #include <asm/page.h> #include <asm/pgtable.h> +#include <asm/uaccess.h> #define VFC_MAJOR (60) @@ -218,7 +218,7 @@ static int vfc_debug(struct vfc_dev *dev, int cmd, unsigned long arg) return -ENOMEM; if(copy_from_user(buffer, inout.buffer, - inout.len*sizeof(char));) { + inout.len*sizeof(char))) { kfree_s(buffer,inout.len*sizeof(char)); return -EFAULT; } diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c index 9bc0b2717..67aa3c574 100644 --- a/drivers/sbus/char/zs.c +++ b/drivers/sbus/char/zs.c @@ -1,9 +1,9 @@ -/* $Id: zs.c,v 1.26 1998/08/03 23:58:14 davem Exp $ +/* $Id: zs.c,v 1.32 1998/11/08 11:15:29 davem Exp $ * zs.c: Zilog serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) - * Fixes by Pete A. Zaitcev <zaitcev@ipmce.su>. + * Fixes by Pete A. Zaitcev <zaitcev@metabyte.com>. */ #include <linux/errno.h> @@ -36,10 +36,10 @@ #include <asm/sbus.h> #ifdef __sparc_v9__ #include <asm/fhc.h> +#endif #ifdef CONFIG_PCI #include <linux/pci.h> #endif -#endif #include "sunserial.h" #include "zs.h" @@ -111,7 +111,7 @@ static unsigned char zscons_regs[16] = { DECLARE_TASK_QUEUE(tq_serial); -struct tty_driver serial_driver, callout_driver; +static struct tty_driver serial_driver, callout_driver; static int serial_refcount; /* serial subtype definitions */ @@ -1354,10 +1354,9 @@ static void send_break( struct sun_serial * info, int duration) if (!info->port) return; current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; cli(); write_zsreg(info->zs_channel, 5, (info->curregs[5] | SND_BRK)); - schedule(); + schedule_timeout(duration); write_zsreg(info->zs_channel, 5, info->curregs[5]); sti(); } @@ -1543,8 +1542,7 @@ static void zs_close(struct tty_struct *tty, struct file * filp) if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); + schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } @@ -1808,7 +1806,7 @@ int zs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - char *revision = "$Revision: 1.26 $"; + char *revision = "$Revision: 1.32 $"; char *version, *p; version = strchr(revision, ' '); @@ -1830,6 +1828,7 @@ __initfunc(static struct sun_zslayout * get_zs(int chip)) { unsigned int vaddr[2] = { 0, 0 }; + unsigned long mapped_addr = 0; int busnode, seen, zsnode, sun4u_ino; static int irq = 0; @@ -1854,7 +1853,30 @@ get_zs(int chip)) int len = prom_getproperty(zsnode, "address", (void *) vaddr, sizeof(vaddr)); - if(len % sizeof(unsigned int)) { + if(len == -1) { + struct linux_sbus *sbus; + struct linux_sbus_device *sdev = NULL; + + /* "address" property is not guarenteed, + * everything in I/O is implicitly mapped + * anyways by our clever TLB miss handling + * scheme, so don't fail here. -DaveM + */ + for_each_sbus(sbus) { + for_each_sbusdev(sdev, sbus) { + if (sdev->prom_node == zsnode) + goto found; + } + } + found: + if (sdev == NULL) + prom_halt(); + prom_apply_sbus_ranges(sbus, sdev->reg_addrs, 1, sdev); + mapped_addr = (unsigned long) + sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0, + PAGE_SIZE, "Zilog Registers", + sdev->reg_addrs[0].which_io, 0x0); + } else if(len % sizeof(unsigned int)) { prom_printf("WHOOPS: proplen for %s " "was %d, need multiple of " "%d\n", "address", len, @@ -1883,9 +1905,12 @@ get_zs(int chip)) } if(!zsnode) panic("get_zs: whee chip not found"); - if(!vaddr[0]) + if(!vaddr[0] && !mapped_addr) panic("get_zs: whee no serial chip mappable"); - return (struct sun_zslayout *)(unsigned long) vaddr[0]; + if (mapped_addr != 0) + return (struct sun_zslayout *) mapped_addr; + else + return (struct sun_zslayout *) (unsigned long) vaddr[0]; } #else /* !(__sparc_v9__) */ __initfunc(static struct sun_zslayout * @@ -2119,7 +2144,7 @@ no_probe: kbd_ops.getledstate = sun_getledstate; kbd_ops.setkeycode = sun_setkeycode; kbd_ops.getkeycode = sun_getkeycode; -#ifdef CONFIG_PCI +#if defined(__sparc_v9__) && defined(CONFIG_PCI) sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count, sun_func_buf, sun_func_table, sun_funcbufsize, sun_funcbufleft, |