diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2003-03-15 01:15:11 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-03-15 01:15:11 -0800 |
| commit | 0deb87aa87368dd4ef75772aa5be81f1f8e7823e (patch) | |
| tree | fa0d1c2c4efe8f6fe1ce50d6d6ff7aa339039147 | |
| parent | eb9d0aa48728e1587514ae1dd83d9493497efc71 (diff) | |
| parent | c4ab9d7892e7f3bf3698f514c8db8f5e719f2b66 (diff) | |
Merge bk://bk.arm.linux.org.uk
into home.transmeta.com:/home/torvalds/v2.5/linux
| -rw-r--r-- | drivers/char/tty_io.c | 9 | ||||
| -rw-r--r-- | drivers/serial/8250.c | 28 | ||||
| -rw-r--r-- | drivers/serial/8250.h | 2 | ||||
| -rw-r--r-- | drivers/serial/8250_acorn.c | 76 | ||||
| -rw-r--r-- | drivers/serial/8250_pci.c | 1773 | ||||
| -rw-r--r-- | drivers/serial/core.c | 946 | ||||
| -rw-r--r-- | drivers/serial/sa1100.c | 43 | ||||
| -rw-r--r-- | include/linux/pci_ids.h | 1 | ||||
| -rw-r--r-- | include/linux/serial_core.h | 14 |
9 files changed, 1866 insertions, 1026 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 32488d71037f..86e3216d0547 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2235,14 +2235,19 @@ struct device_class tty_devclass = { }; EXPORT_SYMBOL(tty_devclass); +static int __init tty_devclass_init(void) +{ + return devclass_register(&tty_devclass); +} + +postcore_initcall(tty_devclass_init); + /* * Ok, now we can initialize the rest of the tty devices and can count * on memory allocations, interrupts etc.. */ void __init tty_init(void) { - devclass_register(&tty_devclass); - /* * dev_tty_driver and dev_console_driver are actually magic * devices which get redirected at open time. Nevertheless, diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index d0949c6b6a1e..09e0165f45a9 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -93,13 +93,15 @@ unsigned int share_irqs = SERIAL8250_SHARE_IRQS; #ifdef CONFIG_SERIAL_8250_MULTIPORT #define CONFIG_SERIAL_MULTIPORT 1 #endif +#ifdef CONFIG_SERIAL_8250_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif /* * HUB6 is always on. This will be removed once the header * files have been cleaned. */ #define CONFIG_HUB6 1 -#define CONFIG_SERIAL_MANY_PORTS 1 #include <asm/serial.h> @@ -2095,6 +2097,28 @@ void serial8250_get_irq_map(unsigned int *map) } } +/** + * serial8250_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void serial8250_suspend_port(int line, u32 level) +{ + uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port, level); +} + +/** + * serial8250_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void serial8250_resume_port(int line, u32 level) +{ + uart_resume_port(&serial8250_reg, &serial8250_ports[line].port, level); +} + static int __init serial8250_init(void) { int ret, i; @@ -2128,6 +2152,8 @@ module_exit(serial8250_exit); EXPORT_SYMBOL(register_serial); EXPORT_SYMBOL(unregister_serial); EXPORT_SYMBOL(serial8250_get_irq_map); +EXPORT_SYMBOL(serial8250_suspend_port); +EXPORT_SYMBOL(serial8250_resume_port); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $"); diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index 030116e6ebf6..cc206f9476ac 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -27,6 +27,8 @@ struct serial8250_probe { int serial8250_register_probe(struct serial8250_probe *probe); void serial8250_unregister_probe(struct serial8250_probe *probe); void serial8250_get_irq_map(unsigned int *map); +void serial8250_suspend_port(int line, u32 level); +void serial8250_resume_port(int line, u32 level); struct old_serial_port { unsigned int uart; diff --git a/drivers/serial/8250_acorn.c b/drivers/serial/8250_acorn.c index 0065a31a418a..db4666f894e5 100644 --- a/drivers/serial/8250_acorn.c +++ b/drivers/serial/8250_acorn.c @@ -1,7 +1,7 @@ /* * linux/drivers/serial/acorn.c * - * Copyright (C) 1996-2002 Russell King. + * Copyright (C) 1996-2003 Russell King. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,6 +9,8 @@ */ #include <linux/module.h> #include <linux/types.h> +#include <linux/tty.h> +#include <linux/serial_core.h> #include <linux/serial.h> #include <linux/errno.h> #include <linux/ioport.h> @@ -16,6 +18,7 @@ #include <linux/device.h> #include <linux/init.h> +#include <asm/io.h> #include <asm/ecard.h> #include <asm/string.h> @@ -24,36 +27,41 @@ struct serial_card_type { unsigned int num_ports; unsigned int baud_base; - int type; - int speed; - int offset[MAX_PORTS]; + unsigned int type; + unsigned int offset[MAX_PORTS]; }; struct serial_card_info { unsigned int num_ports; int ports[MAX_PORTS]; - unsigned long base[MAX_PORTS]; }; -static inline int serial_register_onedev(unsigned long port, int irq, unsigned int baud_base) +static inline int +serial_register_onedev(unsigned long baddr, void *vaddr, int irq, unsigned int baud_base) { struct serial_struct req; memset(&req, 0, sizeof(req)); - req.baud_base = baud_base; - req.irq = irq; - req.port = port; - req.flags = 0; + req.irq = irq; + req.flags = UPF_AUTOPROBE | UPF_RESOURCES | + UPF_SHARE_IRQ; + req.baud_base = baud_base; + req.io_type = UPIO_MEM; + req.iomem_base = vaddr; + req.iomem_reg_shift = 2; + req.iomap_base = baddr; return register_serial(&req); } -static int __devinit serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) +static int __devinit +serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) { struct serial_card_info *info; struct serial_card_type *type = id->data; - unsigned long cardaddr, address; - int port; + unsigned long bus_addr; + unsigned char *virt_addr; + unsigned int port; info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL); if (!info) @@ -64,20 +72,19 @@ static int __devinit serial_card_probe(struct expansion_card *ec, const struct e ecard_set_drvdata(ec, info); - cardaddr = ecard_address(ec, type->type, type->speed); + bus_addr = ec->resource[type->type].start; + virt_addr = ioremap(bus_addr, ec->resource[type->type].end - bus_addr + 1); + if (!virt_addr) { + kfree(info); + return -ENOMEM; + } for (port = 0; port < info->num_ports; port ++) { - address = cardaddr + type->offset[port]; - - info->ports[port] = -1; - info->base[port] = address; + unsigned long baddr = bus_addr + type->offset[port]; + unsigned char *vaddr = virt_addr + type->offset[port]; - if (!request_region(address, 8, "acornserial")) - continue; - - info->ports[port] = serial_register_onedev(address, ec->irq, type->baud_base); - if (info->ports[port] < 0) - break; + info->ports[port] = serial_register_onedev(baddr, vaddr, + ec->irq, type->baud_base); } return 0; @@ -90,12 +97,9 @@ static void __devexit serial_card_remove(struct expansion_card *ec) ecard_set_drvdata(ec, NULL); - for (i = 0; i < info->num_ports; i++) { - if (info->ports[i] > 0) { + for (i = 0; i < info->num_ports; i++) + if (info->ports[i] > 0) unregister_serial(info->ports[i]); - release_region(info->base[i], 8); - } - } kfree(info); } @@ -103,17 +107,15 @@ static void __devexit serial_card_remove(struct expansion_card *ec) static struct serial_card_type atomwide_type = { .num_ports = 3, .baud_base = 7372800 / 16, - .type = ECARD_IOC, - .speed = ECARD_SLOW, - .offset = { 0xa00, 0x900, 0x800 }, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2800, 0x2400, 0x2000 }, }; static struct serial_card_type serport_type = { .num_ports = 2, .baud_base = 3686400 / 16, - .type = ECARD_IOC, - .speed = ECARD_SLOW, - .offset = { 0x800, 0x808 }, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2000, 0x2020 }, }; static const struct ecard_id serial_cids[] = { @@ -127,7 +129,8 @@ static struct ecard_driver serial_card_driver = { .remove = __devexit_p(serial_card_remove), .id_table = serial_cids, .drv = { - .name = "acornserial", + .devclass = &tty_devclass, + .name = "8250_acorn", }, }; @@ -142,6 +145,7 @@ static void __exit serial_card_exit(void) } MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); MODULE_LICENSE("GPL"); module_init(serial_card_init); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 240b79df5794..1123ebd9fe30 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -20,8 +20,9 @@ #include <linux/string.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/tty.h> #include <linux/serial.h> -#include <linux/serialP.h> +#include <linux/serial_core.h> #include <asm/bitops.h> #include <asm/byteorder.h> @@ -30,174 +31,213 @@ #include "8250.h" +/* + * Definitions for PCI support. + */ +#define FL_BASE_MASK 0x0007 +#define FL_BASE0 0x0000 +#define FL_BASE1 0x0001 +#define FL_BASE2 0x0002 +#define FL_BASE3 0x0003 +#define FL_BASE4 0x0004 +#define FL_GET_BASE(x) (x & FL_BASE_MASK) + +#define FL_IRQ_MASK (0x0007 << 4) +#define FL_IRQBASE0 (0x0000 << 4) +#define FL_IRQBASE1 (0x0001 << 4) +#define FL_IRQBASE2 (0x0002 << 4) +#define FL_IRQBASE3 (0x0003 << 4) +#define FL_IRQBASE4 (0x0004 << 4) +#define FL_GET_IRQBASE(x) ((x & FL_IRQ_MASK) >> 4) + +/* Use successive BARs (PCI base address registers), + else use offset into some specified BAR */ +#define FL_BASE_BARS 0x0008 + +/* Use the irq resource table instead of dev->irq */ +#define FL_IRQRESOURCE 0x0080 + +/* Use the Base address register size to cap number of ports */ +#define FL_REGION_SZ_CAP 0x0100 -#ifndef IS_PCI_REGION_IOPORT -#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_IO) -#endif -#ifndef IS_PCI_REGION_IOMEM -#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_MEM) -#endif -#ifndef PCI_IRQ_RESOURCE -#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) -#endif - -#ifndef pci_get_subvendor -#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) -#define pci_get_subdevice(dev) ((dev)->subsystem_device) -#endif - -struct serial_private { - unsigned int nr; - struct pci_board *board; - int line[0]; +struct pci_board { + unsigned int flags; + unsigned int num_ports; + unsigned int base_baud; + unsigned int uart_offset; + unsigned int reg_shift; + unsigned int first_offset; }; /* - * init_fn returns: + * init function returns: * > 0 - number of ports * = 0 - use board->num_ports * < 0 - error */ -struct pci_board { - int flags; - int num_ports; - int base_baud; - int uart_offset; - int reg_shift; - int (*init_fn)(struct pci_dev *dev, int enable); - int first_uart_offset; +struct pci_serial_quirk { + u32 vendor; + u32 device; + u32 subvendor; + u32 subdevice; + int (*init)(struct pci_dev *dev); + int (*setup)(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx); + void (*exit)(struct pci_dev *dev); +}; + +#define PCI_NUM_BAR_RESOURCES 6 + +struct serial_private { + unsigned int nr; + void *remapped_bar[PCI_NUM_BAR_RESOURCES]; + struct pci_serial_quirk *quirk; + int line[0]; }; +static void moan_device(const char *str, struct pci_dev *dev) +{ + printk(KERN_WARNING "%s: %s\n" + KERN_WARNING "Please send the output of lspci -vv, this\n" + KERN_WARNING "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + KERN_WARNING "manufacturer and name of serial board or\n" + KERN_WARNING "modem board to rmk+serial@arm.linux.org.uk.\n", + dev->slot_name, str, dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device); +} + static int -get_pci_port(struct pci_dev *dev, struct pci_board *board, - struct serial_struct *req, int idx) +setup_port(struct pci_dev *dev, struct serial_struct *req, + int bar, int offset, int regshift) { - unsigned long port; - int base_idx; - int max_port; - int offset; + struct serial_private *priv = pci_get_drvdata(dev); + unsigned long port, len; - base_idx = SPCI_FL_GET_BASE(board->flags); - if (board->flags & SPCI_FL_BASE_TABLE) - base_idx += idx; + if (bar >= PCI_NUM_BAR_RESOURCES) + return -EINVAL; - if (board->flags & SPCI_FL_REGION_SZ_CAP) { - max_port = pci_resource_len(dev, base_idx) / 8; - if (idx >= max_port) - return 1; - } - - offset = board->first_uart_offset; + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + port = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); - /* - * Timedia/SUNIX uses a mixture of BARs and offsets - * Ugh, this is ugly as all hell --- TYT - */ - if (dev->vendor == PCI_VENDOR_ID_TIMEDIA) - switch(idx) { - case 0: - base_idx = 0; - break; - case 1: - base_idx = 0; - offset = 8; - break; - case 2: - base_idx = 1; - break; - case 3: - base_idx = 1; - offset = 8; - break; - case 4: /* BAR 2 */ - case 5: /* BAR 3 */ - case 6: /* BAR 4 */ - case 7: /* BAR 5 */ - base_idx = idx - 2; - } + if (!priv->remapped_bar[bar]) + priv->remapped_bar[bar] = ioremap(port, len); + if (!priv->remapped_bar[bar]) + return -ENOMEM; - /* AFAVLAB uses a different mixture of BARs and offsets */ - /* Not that ugly ;) -- HW */ - if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) { - base_idx = 4; - offset = (idx - 4) * 8; + req->io_type = UPIO_MEM; + req->iomap_base = port; + req->iomem_base = priv->remapped_bar[bar] + offset; + req->iomem_reg_shift = regshift; + } else { + port = pci_resource_start(dev, bar) + offset; + req->io_type = UPIO_PORT; + req->port = port; + if (HIGH_BITS_OFFSET) + req->port_high = port >> HIGH_BITS_OFFSET; } + return 0; +} - /* Some Titan cards are also a little weird */ - if (dev->vendor == PCI_VENDOR_ID_TITAN && - (dev->device == PCI_DEVICE_ID_TITAN_400L || - dev->device == PCI_DEVICE_ID_TITAN_800L)) { - switch (idx) { - case 0: base_idx = 1; - break; - case 1: base_idx = 2; - break; - default: - base_idx = 4; - offset = 8 * (idx - 2); - } - } +/* + * AFAVLAB uses a different mixture of BARs and offsets + * Not that ugly ;) -- HW + */ +static int +afavlab_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = FL_GET_BASE(board->flags); + if (idx < 4) + bar += idx; + else + offset += (idx - 4) * board->uart_offset; - /* HP's Diva chip puts the 4th/5th serial port further out, and - * some serial ports are supposed to be hidden on certain models. - */ - if (dev->vendor == PCI_VENDOR_ID_HP && - dev->device == PCI_DEVICE_ID_HP_DIVA) { - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - if (idx == 3) - idx++; - break; - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - if (idx > 0) - idx++; - if (idx > 2) - idx++; - break; - } - if (idx > 2) { - offset = 0x18; - } + return setup_port(dev, req, bar, offset, board->reg_shift); +} + +/* + * HP's Remote Management Console. The Diva chip came in several + * different versions. N-class, L2000 and A500 have two Diva chips, each + * with 3 UARTs (the third UART on the second chip is unused). Superdome + * and Keystone have one Diva chip with 3 UARTs. Some later machines have + * one Diva chip, but it has been expanded to 5 UARTs. + */ +static int __devinit pci_hp_diva_init(struct pci_dev *dev) +{ + int rc = 0; + + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_TOSCA1: + case PCI_DEVICE_ID_HP_DIVA_HALFDOME: + case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + rc = 3; + break; + case PCI_DEVICE_ID_HP_DIVA_TOSCA2: + rc = 2; + break; + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + rc = 4; + break; + case PCI_DEVICE_ID_HP_DIVA_POWERBAR: + rc = 1; + break; } - port = pci_resource_start(dev, base_idx) + offset; + return rc; +} - if ((board->flags & SPCI_FL_BASE_TABLE) == 0) - port += idx * (board->uart_offset ? board->uart_offset : 8); +/* + * HP's Diva chip puts the 4th/5th serial port further out, and + * some serial ports are supposed to be hidden on certain models. + */ +static int +pci_hp_diva_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int offset = board->first_offset; + unsigned int bar = FL_GET_BASE(board->flags); - if (IS_PCI_REGION_IOPORT(dev, base_idx)) { - req->port = port; - if (HIGH_BITS_OFFSET) - req->port_high = port >> HIGH_BITS_OFFSET; - else - req->port_high = 0; - return 0; + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + if (idx == 3) + idx++; + break; + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + if (idx > 0) + idx++; + if (idx > 2) + idx++; + break; } - req->io_type = SERIAL_IO_MEM; - req->iomap_base = port; - req->iomem_base = ioremap(port, board->uart_offset); - if (req->iomem_base == NULL) - return -ENOMEM; - req->iomem_reg_shift = board->reg_shift; - req->port = 0; - return 0; + if (idx > 2) + offset = 0x18; + + offset += idx * board->uart_offset; + + return setup_port(dev, req, bar, offset, board->reg_shift); } -static _INLINE_ int -get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +/* + * Added for EKF Intel i960 serial boards + */ +static int __devinit pci_inteli960ni_init(struct pci_dev *dev) { - int base_idx; + unsigned long oldval; - if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) - return dev->irq; + if (!(dev->subsystem_device & 0x1000)) + return -ENODEV; - base_idx = SPCI_FL_GET_IRQBASE(board->flags); - if (board->flags & SPCI_FL_IRQ_TABLE) - base_idx += idx; - - return PCI_IRQ_RESOURCE(dev, base_idx); + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void*) &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return -ENODEV; + } + return 0; } /* @@ -206,26 +246,29 @@ get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) * seems to be mainly needed on card using the PLX which also use I/O * mapped memory. */ -static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) +static int __devinit pci_plx9050_init(struct pci_dev *dev) { - u8 *p, irq_config = 0; - - if (enable) { - irq_config = 0x41; - if (dev->vendor == PCI_VENDOR_ID_PANACOM) - irq_config = 0x43; - if ((dev->vendor == PCI_VENDOR_ID_PLX) && - (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { - /* - * As the megawolf cards have the int pins active - * high, and have 2 UART chips, both ints must be - * enabled on the 9050. Also, the UARTS are set in - * 16450 mode by default, so we have to enable the - * 16C950 'enhanced' mode so that we can use the - * deep FIFOs - */ - irq_config = 0x5b; - } + u8 *p, irq_config; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar 0", dev); + return 0; + } + + irq_config = 0x41; + if (dev->vendor == PCI_VENDOR_ID_PANACOM) + irq_config = 0x43; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the + * deep FIFOs + */ + irq_config = 0x5b; } /* @@ -245,6 +288,27 @@ static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) return 0; } +static void __devexit pci_plx9050_exit(struct pci_dev *dev) +{ + u8 *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) + return; + + /* + * disable interrupts + */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + if (p != NULL) { + writel(0, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + } +} /* * SIIG serial cards have an PCI interface chip which also controls @@ -270,23 +334,20 @@ static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) #define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) #define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) -int pci_siig10x_fn(struct pci_dev *dev, int enable) +static int pci_siig10x_init(struct pci_dev *dev) { u16 data, *p; - if (!enable) - return 0; - switch (dev->device & 0xfff8) { - case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ - data = 0xffdf; - break; - case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ - data = 0xf7ff; - break; - default: /* 1S1P, 4S */ - data = 0xfffb; - break; + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; } p = ioremap(pci_resource_start(dev, 0), 0x80); @@ -294,22 +355,18 @@ int pci_siig10x_fn(struct pci_dev *dev, int enable) return -ENOMEM; writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); + readw((unsigned long)p + 0x28); iounmap(p); return 0; } -EXPORT_SYMBOL(pci_siig10x_fn); - #define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) #define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) -int pci_siig20x_fn(struct pci_dev *dev, int enable) +static int pci_siig20x_init(struct pci_dev *dev) { u8 data; - if (!enable) - return 0; - /* Change clock frequency for the first UART. */ pci_read_config_byte(dev, 0x6f, &data); pci_write_config_byte(dev, 0x6f, data & 0xef); @@ -323,28 +380,25 @@ int pci_siig20x_fn(struct pci_dev *dev, int enable) return 0; } -EXPORT_SYMBOL(pci_siig20x_fn); - -/* Added for EKF Intel i960 serial boards */ -static int __devinit pci_inteli960ni_fn(struct pci_dev *dev, int enable) +int pci_siig10x_fn(struct pci_dev *dev, int enable) { - unsigned long oldval; - - if (!(pci_get_subdevice(dev) & 0x1000)) - return -ENODEV; + int ret = 0; + if (enable) + ret = pci_siig10x_init(dev); + return ret; +} - if (!enable) /* is there something to deinit? */ - return 0; - - /* is firmware started? */ - pci_read_config_dword(dev, 0x44, (void*) &oldval); - if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); - return -ENODEV; - } - return 0; +int pci_siig20x_fn(struct pci_dev *dev, int enable) +{ + int ret = 0; + if (enable) + ret = pci_siig20x_init(dev); + return ret; } +EXPORT_SYMBOL(pci_siig10x_fn); +EXPORT_SYMBOL(pci_siig20x_fn); + /* * Timedia has an explosion of boards, and to avoid the PCI table from * growing *huge*, we use this function to collapse some 70 entries @@ -385,78 +439,453 @@ static struct timedia_struct { { 0, 0 } }; -static int __devinit pci_timedia_fn(struct pci_dev *dev, int enable) +static int __devinit pci_timedia_init(struct pci_dev *dev) { - int i, j; unsigned short *ids; - - if (!enable) - return 0; + int i, j; for (i = 0; timedia_data[i].num; i++) { ids = timedia_data[i].ids; for (j = 0; ids[j]; j++) - if (pci_get_subdevice(dev) == ids[j]) + if (dev->subsystem_device == ids[j]) return timedia_data[i].num; } return 0; } /* - * HP's Remote Management Console. The Diva chip came in several - * different versions. N-class, L2000 and A500 have two Diva chips, each - * with 3 UARTs (the third UART on the second chip is unused). Superdome - * and Keystone have one Diva chip with 3 UARTs. Some later machines have - * one Diva chip, but it has been expanded to 5 UARTs. + * Timedia/SUNIX uses a mixture of BARs and offsets + * Ugh, this is ugly as all hell --- TYT */ -static int __devinit pci_hp_diva(struct pci_dev *dev, int enable) +static int +pci_timedia_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) { - int rc = 0; - - if (!enable) - return 0; + unsigned int bar = 0, offset = board->first_offset; - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_TOSCA1: - case PCI_DEVICE_ID_HP_DIVA_HALFDOME: - case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - rc = 3; + switch (idx) { + case 0: + bar = 0; break; - case PCI_DEVICE_ID_HP_DIVA_TOSCA2: - rc = 2; + case 1: + offset = board->uart_offset; + bar = 0; break; - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - rc = 4; - break; - case PCI_DEVICE_ID_HP_DIVA_POWERBAR: - rc = 1; + case 2: + bar = 1; break; + case 3: + offset = board->uart_offset; + bar = 1; + case 4: /* BAR 2 */ + case 5: /* BAR 3 */ + case 6: /* BAR 4 */ + case 7: /* BAR 5 */ + bar = idx - 2; } - return rc; + return setup_port(dev, req, bar, offset, board->reg_shift); } +/* + * Some Titan cards are also a little weird + */ +static int +titan_400l_800l_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 1; + break; + case 1: + bar = 2; + break; + default: + bar = 4; + offset = (idx - 2) * board->uart_offset; + } + + return setup_port(dev, req, bar, offset, board->reg_shift); +} -static int __devinit pci_xircom_fn(struct pci_dev *dev, int enable) +static int __devinit pci_xircom_init(struct pci_dev *dev) { __set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/10); return 0; } +static int +pci_default_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + maxnr = (pci_resource_len(dev, bar) - board->uart_offset) / + (8 << board->reg_shift); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + return setup_port(dev, req, bar, offset, board->reg_shift); +} + +/* + * Master list of serial port init/setup/exit quirks. + * This does not describe the general nature of the port. + * (ie, baud base, number and location of ports, etc) + * + * This list is ordered alphabetically by vendor then device. + * Specific entries must come before more generic entries. + */ +static struct pci_serial_quirk pci_serial_quirks[] = { + /* + * AFAVLAB cards. + * It is not clear whether this applies to all products. + */ + { + .vendor = PCI_VENDOR_ID_AFAVLAB, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = afavlab_setup, + }, + /* + * HP Diva + */ + { + .vendor = PCI_VENDOR_ID_HP, + .device = PCI_DEVICE_ID_HP_DIVA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_hp_diva_init, + .setup = pci_hp_diva_setup, + }, + /* + * Intel + */ + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_80960_RP, + .subvendor = 0xe4bf, + .subdevice = PCI_ANY_ID, + .init = pci_inteli960ni_init, + .setup = pci_default_setup, + }, + /* + * Panacom + */ + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * PLX + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, + .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_ROMULUS, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * SIIG cards. + * It is not clear whether these could be collapsed. + */ + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + /* + * Titan cards + */ + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_400L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_800L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + /* + * Timedia cards + */ + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_DEVICE_ID_TIMEDIA_1889, + .subvendor = PCI_VENDOR_ID_TIMEDIA, + .subdevice = PCI_ANY_ID, + .init = pci_timedia_init, + .setup = pci_timedia_setup, + }, + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_timedia_setup, + }, + /* + * Xircom cards + */ + { + .vendor = PCI_VENDOR_ID_XIRCOM, + .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_xircom_init, + .setup = pci_default_setup, + }, + /* + * Default "match everything" terminator entry + */ + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + } +}; + +static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) +{ + return quirk_id == PCI_ANY_ID || quirk_id == dev_id; +} + +static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) +{ + struct pci_serial_quirk *quirk; + + for (quirk = pci_serial_quirks; ; quirk++) + if (quirk_id_matches(quirk->vendor, dev->vendor) && + quirk_id_matches(quirk->device, dev->device) && + quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && + quirk_id_matches(quirk->subdevice, dev->subsystem_device)) + break; + return quirk; +} + +static _INLINE_ int +get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +{ + int base_idx; + + if ((board->flags & FL_IRQRESOURCE) == 0) + return dev->irq; + + base_idx = FL_GET_IRQBASE(board->flags); + + if (base_idx > DEVICE_COUNT_IRQ) + return 0; + + return dev->irq_resource[base_idx].start; +} + /* * This is the configuration table for all of the PCI serial boards * which we support. It is directly indexed by the pci_board_num_t enum * value, which is encoded in the pci_device_id PCI probe table's * driver_data member. + * + * The makeup of these names are: + * pbn_bn{_bt}_n_baud + * + * bn = PCI BAR number + * bt = Index using PCI BARs + * n = number of serial ports + * baud = baud rate + * + * Please note: in theory if n = 1, _bt infix should make no difference. + * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 */ enum pci_board_num_t { - pbn_b0_1_115200, pbn_default = 0, + pbn_b0_1_115200, pbn_b0_2_115200, pbn_b0_4_115200, + pbn_b0_5_115200, pbn_b0_1_921600, pbn_b0_2_921600, @@ -465,171 +894,465 @@ enum pci_board_num_t { pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, pbn_b0_bt_8_115200, + pbn_b0_bt_1_460800, pbn_b0_bt_2_460800, + pbn_b0_bt_1_921600, + pbn_b0_bt_2_921600, + pbn_b0_bt_4_921600, + pbn_b0_bt_8_921600, + pbn_b1_1_115200, pbn_b1_2_115200, pbn_b1_4_115200, pbn_b1_8_115200, + pbn_b1_1_921600, pbn_b1_2_921600, pbn_b1_4_921600, pbn_b1_8_921600, + pbn_b1_bt_2_921600, + pbn_b1_2_1382400, pbn_b1_4_1382400, pbn_b1_8_1382400, pbn_b2_1_115200, pbn_b2_8_115200, + + pbn_b2_1_460800, pbn_b2_4_460800, pbn_b2_8_460800, pbn_b2_16_460800, + + pbn_b2_1_921600, pbn_b2_4_921600, pbn_b2_8_921600, pbn_b2_bt_1_115200, pbn_b2_bt_2_115200, pbn_b2_bt_4_115200, + pbn_b2_bt_2_921600, + pbn_b2_bt_4_921600, + + pbn_b3_4_115200, + pbn_b3_8_115200, + /* + * Board-specific versions. + */ pbn_panacom, pbn_panacom2, pbn_panacom4, pbn_plx_romulus, pbn_oxsemi, - pbn_timedia, pbn_intel_i960, pbn_sgi_ioc3, - pbn_hp_diva, pbn_nec_nile4, - - pbn_dci_pccom4, - pbn_dci_pccom8, - - pbn_xircom_combo, - - pbn_siig10x_0, - pbn_siig10x_1, - pbn_siig10x_2, - pbn_siig10x_4, - pbn_siig20x_0, - pbn_siig20x_2, - pbn_siig20x_4, - pbn_computone_4, pbn_computone_6, pbn_computone_8, }; static struct pci_board pci_boards[] __devinitdata = { + [pbn_default] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_2_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_4_115200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_5_115200] = { + .flags = FL_BASE0, + .num_ports = 5, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_1_921600] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_2_921600] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_4_921600] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_2_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_8_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_2_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_2_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_4_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_8_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_115200] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_2_115200] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_4_115200] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_8_115200] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_1_921600] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_921600] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_4_921600] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_8_921600] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_bt_2_921600] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_2_1382400] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_4_1382400] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_8_1382400] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 1382400, + .uart_offset = 8, + }, + + [pbn_b2_1_115200] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_8_115200] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_1_460800] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_4_460800] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_8_460800] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_16_460800] = { + .flags = FL_BASE2, + .num_ports = 16, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b2_1_921600] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_4_921600] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_8_921600] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b2_bt_1_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_2_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_4_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_bt_2_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_bt_4_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b3_4_115200] = { + .flags = FL_BASE3, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_8_115200] = { + .flags = FL_BASE3, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + /* - * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers, - * Register shift to use for memory-mapped I/O, - * Initialization function, first UART offset + * Entries following this are board-specific. */ - /* Generic serial board, pbn_b0_1_115200, pbn_default */ - { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, - pbn_default */ - - { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ - { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ - - { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ - { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ - { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ - - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ - - { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ - { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ - { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ - { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ - - { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ - { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ - { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ - - { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ - { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ - { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ - - { SPCI_FL_BASE2, 1, 115200 }, /* pbn_b2_1_115200 */ - { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ - { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ - { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ - { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ - { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ - { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ - - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ - - { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ - 0x20, 2, pci_plx9050_fn, 0x03 }, + /* + * Panacom - IOMEM + */ + [pbn_panacom] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom2] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom4] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + + /* I think this entry is broken - the first_offset looks wrong --rmk */ + [pbn_plx_romulus] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x03, + }, /* * This board uses the size of PCI Base region 0 to * signal now many ports are available */ - { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ - { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ - 0, 0, pci_timedia_fn }, - /* EKF addition for i960 Boards form EKF with serial port */ - { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ - 8<<2, 2, pci_inteli960ni_fn, 0x10000}, - { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ - 1, 458333, 0, 0, 0, 0x20178 }, - { SPCI_FL_BASE0, 5, 115200, 8, 0, pci_hp_diva, 0 },/* pbn_hp_diva */ + [pbn_oxsemi] = { + .flags = FL_BASE0|FL_REGION_SZ_CAP, + .num_ports = 32, + .base_baud = 115200, + .uart_offset = 8, + }, + + /* + * EKF addition for i960 Boards form EKF with serial port. + * Max 256 ports. + */ + [pbn_intel_i960] = { + .flags = FL_BASE0, + .num_ports = 32, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x10000, + }, + [pbn_sgi_ioc3] = { + .flags = FL_BASE0|FL_IRQRESOURCE, + .num_ports = 1, + .base_baud = 458333, + .uart_offset = 8, + .reg_shift = 0, + .first_offset = 0x20178, + }, /* * NEC Vrc-5074 (Nile 4) builtin UART. */ - { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ - 64, 3, NULL, 0x300 }, - - { SPCI_FL_BASE3, 4, 115200, 8 }, /* pbn_dci_pccom4 */ - { SPCI_FL_BASE3, 8, 115200, 8 }, /* pbn_dci_pccom8 */ - - { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ - 0, 0, pci_xircom_fn }, - - { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ - 0, 0, pci_siig20x_fn }, - - { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ - 0x40, 2, NULL, 0x200 }, + [pbn_nec_nile4] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 520833, + .uart_offset = 8 << 3, + .reg_shift = 3, + .first_offset = 0x300, + }, + + /* + * Computone - uses IOMEM. + */ + [pbn_computone_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_6] = { + .flags = FL_BASE0, + .num_ports = 6, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, }; /* @@ -640,8 +1363,7 @@ static struct pci_board pci_boards[] __devinitdata = { static int __devinit serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) { - int num_iomem = 0, num_port = 0, first_port = -1; - int i; + int num_iomem, num_port, first_port = -1, i; /* * If it is not a communications device or the programming @@ -655,36 +1377,63 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) (dev->class & 0xff) > 6) return -ENODEV; - for (i = 0; i < 6; i++) { - if (IS_PCI_REGION_IOPORT(dev, i)) { + num_iomem = num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO) { num_port++; if (first_port == -1) first_port = i; } - if (IS_PCI_REGION_IOMEM(dev, i)) + if (pci_resource_flags(dev, i) & IORESOURCE_MEM) num_iomem++; } /* - * If there is 1 or 0 iomem regions, and exactly one port, use - * it. + * If there is 1 or 0 iomem regions, and exactly one port, + * use it. We guess the number of ports based on the IO + * region size. */ if (num_iomem <= 1 && num_port == 1) { board->flags = first_port; + board->num_ports = pci_resource_len(dev, first_port) / 8; return 0; } + + /* + * Now guess if we've got a board which indexes by BARs. + * Each IO BAR should be 8 bytes, and they should follow + * consecutively. + */ + first_port = -1; + num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO && + pci_resource_len(dev, i) == 8 && + (first_port == -1 || (first_port + num_port) == i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + } + + if (num_port > 1) { + board->flags = first_port | FL_BASE_BARS; + board->num_ports = num_port; + return 0; + } + return -ENODEV; } static inline int -serial_pci_matches(struct pci_board *board, int index) +serial_pci_matches(struct pci_board *board, struct pci_board *guessed) { return - board->base_baud == pci_boards[index].base_baud && - board->num_ports == pci_boards[index].num_ports && - board->uart_offset == pci_boards[index].uart_offset && - board->reg_shift == pci_boards[index].reg_shift && - board->first_uart_offset == pci_boards[index].first_uart_offset; + board->num_ports == guessed->num_ports && + board->base_baud == guessed->base_baud && + board->uart_offset == guessed->uart_offset && + board->reg_shift == guessed->reg_shift && + board->first_offset == guessed->first_offset; } /* @@ -692,10 +1441,11 @@ serial_pci_matches(struct pci_board *board, int index) * to the arrangement of serial ports on a PCI card. */ static int __devinit -pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { struct serial_private *priv; struct pci_board *board, tmp; + struct pci_serial_quirk *quirk; struct serial_struct serial_req; int base_baud, rc, nr_ports, i; @@ -732,92 +1482,109 @@ pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) * detect this boards settings with our heuristic, * then we no longer need this entry. */ + memcpy(&tmp, &pci_boards[pbn_default], sizeof(struct pci_board)); rc = serial_pci_guess_board(dev, &tmp); - if (rc == 0 && serial_pci_matches(board, pbn_default)) { - printk(KERN_INFO - "Redundant entry in serial pci_table. Please send the output\n" - "of lspci -vv, this message (0x%04x,0x%04x,0x%04x,0x%04x),\n" - "the manufacturer and name of serial board or modem board to\n" - "rmk@arm.linux.org.uk.\n", - dev->vendor, dev->device, - pci_get_subvendor(dev), pci_get_subdevice(dev)); - } + if (rc == 0 && serial_pci_matches(board, &tmp)) + moan_device("Redundant entry in serial pci_table.", + dev); } nr_ports = board->num_ports; /* - * Run the initialization function, if any. The initialization - * function returns: + * Find an init and setup quirks. + */ + quirk = find_quirk(dev); + + /* + * Run the new-style initialization function. + * The initialization function returns: * <0 - error * 0 - use board->num_ports * >0 - number of ports */ - if (board->init_fn) { - rc = board->init_fn(dev, 1); + if (quirk->init) { + rc = quirk->init(dev); if (rc < 0) goto disable; - if (rc) nr_ports = rc; } priv = kmalloc(sizeof(struct serial_private) + - sizeof(unsigned int) * nr_ports, - GFP_KERNEL); + sizeof(unsigned int) * nr_ports, + GFP_KERNEL); if (!priv) { rc = -ENOMEM; goto deinit; } + memset(priv, 0, sizeof(struct serial_private) + + sizeof(unsigned int) * nr_ports); + + priv->quirk = quirk; + pci_set_drvdata(dev, priv); + base_baud = board->base_baud; - if (!base_baud) + if (!base_baud) { + moan_device("Board entry does not specify baud rate.", dev); base_baud = BASE_BAUD; - memset(&serial_req, 0, sizeof(serial_req)); + } for (i = 0; i < nr_ports; i++) { + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.flags = UPF_SKIP_TEST | UPF_AUTOPROBE | + UPF_RESOURCES | UPF_SHARE_IRQ; + serial_req.baud_base = base_baud; serial_req.irq = get_pci_irq(dev, board, i); - if (get_pci_port(dev, board, &serial_req, i)) + if (quirk->setup(dev, board, &serial_req, i)) break; #ifdef SERIAL_DEBUG_PCI printk("Setup PCI port: port %x, irq %d, type %d\n", serial_req.port, serial_req.irq, serial_req.io_type); #endif - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; - serial_req.baud_base = base_baud; priv->line[i] = register_serial(&serial_req); if (priv->line[i] < 0) break; } - priv->board = board; priv->nr = i; - pci_set_drvdata(dev, priv); - return 0; deinit: - if (board->init_fn) - board->init_fn(dev, 0); + if (quirk->exit) + quirk->exit(dev); disable: pci_disable_device(dev); return rc; } -static void __devexit pci_remove_one(struct pci_dev *dev) +static void __devexit pciserial_remove_one(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); - int i; pci_set_drvdata(dev, NULL); if (priv) { + struct pci_serial_quirk *quirk; + int i; + for (i = 0; i < priv->nr; i++) unregister_serial(priv->line[i]); - if (priv->board->init_fn) - priv->board->init_fn(dev, 0); + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (priv->remapped_bar[i]) + iounmap(priv->remapped_bar[i]); + priv->remapped_bar[i] = NULL; + } + + /* + * Find the exit quirks. + */ + quirk = find_quirk(dev); + if (quirk->exit) + quirk->exit(dev); pci_disable_device(dev); @@ -825,6 +1592,53 @@ static void __devexit pci_remove_one(struct pci_dev *dev) } } +static int pciserial_save_state_one(struct pci_dev *dev, u32 state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_suspend_port(priv->line[i], SUSPEND_SAVE_STATE); + } + return 0; +} + +static int pciserial_suspend_one(struct pci_dev *dev, u32 state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_suspend_port(priv->line[i], SUSPEND_POWER_DOWN); + } + return 0; +} + +static int pciserial_resume_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + /* + * Ensure that the board is correctly configured. + */ + if (priv->quirk->init) + priv->quirk->init(dev); + + for (i = 0; i < priv->nr; i++) { + serial8250_resume_port(priv->line[i], RESUME_POWER_ON); + serial8250_resume_port(priv->line[i], RESUME_RESTORE_STATE); + } + } + return 0; +} + static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, @@ -908,7 +1722,9 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_921600 }, - /* VScom SPCOM800, from sl@s.pl */ + /* + * VScom SPCOM800, from sl@s.pl + */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_8_921600 }, @@ -949,8 +1765,10 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, pbn_b2_8_460800 }, - /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ - /* (Exoray@isys.ca) */ + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, 0x10b5, 0x106a, 0, 0, pbn_plx_romulus }, @@ -976,16 +1794,24 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_2_115200 }, - /* Digitan DS560-558, from jimd@esoft.com */ + /* + * Digitan DS560-558, from jimd@esoft.com + */ { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b1_1_115200 }, - /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ + /* + * 3Com US Robotics 56k Voice Internal PCI model 5610 + */ { PCI_VENDOR_ID_USR, 0x1008, - PCI_ANY_ID, PCI_ANY_ID, }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, - /* Titan Electronic cards */ + /* + * Titan Electronic cards + * The 400L and 800L have a custom setup quirk. + */ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_1_921600 }, @@ -999,120 +1825,76 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_4_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 1, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, - /* The 400L and 800L have a custom hack in get_pci_port */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 4, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 8, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, - /* Computone devices submitted by Doug McNash dmcnash@computone.com */ + /* + * Computone devices submitted by Doug McNash dmcnash@computone.com + */ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, 0, 0, pbn_computone_4 }, @@ -1124,18 +1906,22 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { 0, 0, pbn_computone_6 }, { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi }, { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_921600 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - /* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> */ + /* + * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> + */ { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_8_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, @@ -1158,26 +1944,40 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_1_460800 }, - /* RAStel 2 port modem, gerg@moreton.com.au */ + /* + * RAStel 2 port modem, gerg@moreton.com.au + */ { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_115200 }, - /* EKF addition for i960 Boards form EKF with serial port */ - { PCI_VENDOR_ID_INTEL, 0x1960, + /* + * EKF addition for i960 Boards form EKF with serial port + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, 0xE4BF, PCI_ANY_ID, 0, 0, pbn_intel_i960 }, - /* Xircom Cardbus/Ethernet combos */ + /* + * Xircom Cardbus/Ethernet combos + */ { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_xircom_combo }, + pbn_b0_1_115200 }, + /* + * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, /* * Untested PCI modems, sent in from various folks... */ - /* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */ + /* + * Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> + */ { PCI_VENDOR_ID_ROCKWELL, 0x1004, 0x1048, 0x1500, 0, 0, pbn_b1_1_115200 }, @@ -1186,10 +1986,12 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { 0xFF00, 0, 0, 0, pbn_sgi_ioc3 }, - /* HP Diva card */ + /* + * HP Diva card + */ { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_hp_diva }, + pbn_b0_5_115200 }, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_1_115200 }, @@ -1203,15 +2005,14 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom4 }, + pbn_b3_4_115200 }, { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom8 }, + pbn_b3_8_115200 }, /* - * These entries match devices with class - * COMMUNICATION_SERIAL, COMMUNICATION_MODEM - * or COMMUNICATION_MULTISERIAL + * These entries match devices with class COMMUNICATION_SERIAL, + * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, @@ -1230,9 +2031,15 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { static struct pci_driver serial_pci_driver = { .name = "serial", - .probe = pci_init_one, - .remove = __devexit_p(pci_remove_one), + .probe = pciserial_init_one, + .remove = __devexit_p(pciserial_remove_one), + .save_state = pciserial_save_state_one, + .suspend = pciserial_suspend_one, + .resume = pciserial_resume_one, .id_table = serial_pci_tbl, + .driver = { + .devclass = &tty_devclass, + }, }; static int __init serial8250_pci_init(void) diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 5b394b10cf2e..cd71c9e65fb0 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -21,9 +21,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id: core.c,v 1.100 2002/07/28 10:03:28 rmk Exp $ - * */ #include <linux/config.h> #include <linux/module.h> @@ -31,9 +28,9 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/console.h> -#include <linux/pm.h> #include <linux/serial_core.h> #include <linux/smp_lock.h> +#include <linux/device.h> #include <linux/serial.h> /* for serial_state and serial_icounter_struct */ #include <asm/irq.h> @@ -46,11 +43,6 @@ #define DPRINTK(x...) do { } while (0) #endif -#ifndef CONFIG_PM -#define pm_access(pm) do { } while (0) -#define pm_unregister(pm) do { } while (0) -#endif - /* * This is used to lock changes in serial line configuration. */ @@ -58,8 +50,17 @@ static DECLARE_MUTEX(port_sem); #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -static void uart_change_speed(struct uart_info *info, struct termios *old_termios); +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +static void uart_change_speed(struct uart_state *state, struct termios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); +static void uart_change_pm(struct uart_state *state, int pm_state); /* * This routine is used by the interrupt handler to schedule processing in @@ -73,8 +74,8 @@ void uart_write_wakeup(struct uart_port *port) static void uart_stop(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); @@ -84,32 +85,31 @@ static void uart_stop(struct tty_struct *tty) static void __uart_start(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; - if (!uart_circ_empty(&info->xmit) && info->xmit.buf && + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port, 1); } static void uart_start(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; - pm_access(info->state->pm); - - spin_lock_irqsave(&info->port->lock, flags); + spin_lock_irqsave(&port->lock, flags); __uart_start(tty); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); } static void uart_tasklet_action(unsigned long data) { - struct uart_info *info = (struct uart_info *)data; + struct uart_state *state = (struct uart_state *)data; struct tty_struct *tty; - tty = info->tty; + tty = state->info->tty; if (tty) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -137,11 +137,12 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) /* * Startup the port. This will be called once per open. All calls - * will be serialised by the global port semaphore. + * will be serialised by the per-port semaphore. */ -static int uart_startup(struct uart_info *info, int init_hw) +static int uart_startup(struct uart_state *state, int init_hw) { - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; unsigned long page; int retval = 0; @@ -182,7 +183,7 @@ static int uart_startup(struct uart_info *info, int init_hw) /* * Initialise the hardware port settings. */ - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); /* * Setup the RTS and DTR signals once the @@ -194,8 +195,7 @@ static int uart_startup(struct uart_info *info, int init_hw) info->flags |= UIF_INITIALIZED; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + clear_bit(TTY_IO_ERROR, &info->tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) @@ -207,11 +207,12 @@ static int uart_startup(struct uart_info *info, int init_hw) /* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. Calls to - * uart_shutdown are serialised by port_sem. + * uart_shutdown are serialised by the per-port semaphore. */ -static void uart_shutdown(struct uart_info *info) +static void uart_shutdown(struct uart_state *state) { - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; if (!(info->flags & UIF_INITIALIZED)) return; @@ -220,7 +221,7 @@ static void uart_shutdown(struct uart_info *info) * Turn off DTR and RTS early. */ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - uart_clear_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* * clear delta_msr_wait queue to avoid mem leaks: we may free @@ -412,10 +413,10 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) EXPORT_SYMBOL(uart_get_divisor); static void -uart_change_speed(struct uart_info *info, struct termios *old_termios) +uart_change_speed(struct uart_state *state, struct termios *old_termios) { - struct tty_struct *tty = info->tty; - struct uart_port *port = info->port; + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; struct termios *termios; /* @@ -431,14 +432,14 @@ uart_change_speed(struct uart_info *info, struct termios *old_termios) * Set flags based on termios cflag */ if (termios->c_cflag & CRTSCTS) - info->flags |= UIF_CTS_FLOW; + state->info->flags |= UIF_CTS_FLOW; else - info->flags &= ~UIF_CTS_FLOW; + state->info->flags &= ~UIF_CTS_FLOW; if (termios->c_cflag & CLOCAL) - info->flags &= ~UIF_CHECK_CD; + state->info->flags &= ~UIF_CHECK_CD; else - info->flags |= UIF_CHECK_CD; + state->info->flags |= UIF_CHECK_CD; port->ops->set_termios(port, termios, old_termios); } @@ -526,10 +527,10 @@ __uart_kern_write(struct uart_port *port, struct circ_buf *circ, static void uart_put_char(struct tty_struct *tty, unsigned char ch) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; if (tty) - __uart_put_char(info->port, &info->xmit, ch); + __uart_put_char(state->port, &state->info->xmit, ch); } static void uart_flush_chars(struct tty_struct *tty) @@ -541,16 +542,16 @@ static int uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, int count) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; int ret; - if (!tty || !info->xmit.buf) + if (!tty || !state->info->xmit.buf) return 0; if (from_user) - ret = __uart_user_write(info->port, &info->xmit, buf, count); + ret = __uart_user_write(state->port, &state->info->xmit, buf, count); else - ret = __uart_kern_write(info->port, &info->xmit, buf, count); + ret = __uart_kern_write(state->port, &state->info->xmit, buf, count); uart_start(tty); return ret; @@ -558,29 +559,30 @@ uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, static int uart_write_room(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; - return uart_circ_chars_free(&info->xmit); + return uart_circ_chars_free(&state->info->xmit); } static int uart_chars_in_buffer(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; - return uart_circ_chars_pending(&info->xmit); + return uart_circ_chars_pending(&state->info->xmit); } static void uart_flush_buffer(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; DPRINTK("uart_flush_buffer(%d) called\n", minor(tty->device) - tty->driver.minor_start); - spin_lock_irqsave(&info->port->lock, flags); - uart_circ_clear(&info->xmit); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -593,8 +595,8 @@ static void uart_flush_buffer(struct tty_struct *tty) */ static void uart_send_xchar(struct tty_struct *tty, char ch) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; if (port->ops->send_xchar) @@ -611,19 +613,19 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) static void uart_throttle(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; if (I_IXOFF(tty)) uart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) - uart_clear_mctrl(info->port, TIOCM_RTS); + uart_clear_mctrl(state->port, TIOCM_RTS); } static void uart_unthrottle(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; if (I_IXOFF(tty)) { if (port->x_char) @@ -636,10 +638,9 @@ static void uart_unthrottle(struct tty_struct *tty) uart_set_mctrl(port, TIOCM_RTS); } -static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) +static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo) { - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_port *port = state->port; struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); @@ -649,7 +650,7 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) if (HIGH_BITS_OFFSET) tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; tmp.irq = port->irq; - tmp.flags = port->flags | info->flags; + tmp.flags = port->flags; tmp.xmit_fifo_size = port->fifosize; tmp.baud_base = port->uartclk / 16; tmp.close_delay = state->close_delay; @@ -666,11 +667,10 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) } static int -uart_set_info(struct uart_info *info, struct serial_struct *newinfo) +uart_set_info(struct uart_state *state, struct serial_struct *newinfo) { struct serial_struct new_serial; - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_port *port = state->port; unsigned long new_port; unsigned int change_irq, change_port, old_flags; unsigned int old_custom_divisor; @@ -692,7 +692,7 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) * module insertion/removal doesn't change anything * under us. */ - down(&port_sem); + down(&state->sem); change_irq = new_serial.irq != port->irq; @@ -745,14 +745,14 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) /* * Make sure that we are the sole user of this port. */ - if (state->count > 1 || info->blocked_open != 0) + if (uart_users(state) > 1) goto exit; /* * We need to shutdown the serial port at the old * port/type/irq combination. */ - uart_shutdown(info); + uart_shutdown(state); } if (change_port) { @@ -813,18 +813,21 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) port->irq = new_serial.irq; port->uartclk = new_serial.baud_base * 16; - port->flags = new_serial.flags & UPF_CHANGE_MASK; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_serial.flags & UPF_CHANGE_MASK); port->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100; port->fifosize = new_serial.xmit_fifo_size; - info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + if (state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; if (port->type == PORT_UNKNOWN) goto exit; - if (info->flags & UIF_INITIALIZED) { + if (state->info->flags & UIF_INITIALIZED) { if (((old_flags ^ port->flags) & UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor) { /* If they're setting up a custom divisor or speed, @@ -832,25 +835,26 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) * need to rate-limit; it's CAP_SYS_ADMIN only. */ if (port->flags & UPF_SPD_MASK) { printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n", - current->comm, info->tty->driver.name, - info->port->line); + current->comm, state->info->tty->driver.name, + state->port->line); } - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); } } else - retval = uart_startup(info, 1); + retval = uart_startup(state, 1); exit: - up(&port_sem); + up(&state->sem); return retval; } /* - * uart_get_lsr_info - get line status register info + * uart_get_lsr_info - get line status register info. + * Note: uart_ioctl protects us against hangups. */ -static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) +static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; unsigned int result; result = port->ops->tx_empty(port); @@ -861,9 +865,9 @@ static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) * avoid a race condition (depending on when the transmit * interrupt happens). */ - if (info->port->x_char || - ((uart_circ_chars_pending(&info->xmit) > 0) && - !info->tty->stopped && !info->tty->hw_stopped)) + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -888,6 +892,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, if (get_user(arg, value)) return -EFAULT; + arg &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2; + set = clear = 0; switch (cmd) { case TIOCMBIS: @@ -911,8 +917,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, static void uart_break_ctl(struct tty_struct *tty, int break_state) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; BUG_ON(!kernel_locked()); @@ -920,25 +926,25 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) port->ops->break_ctl(port, break_state); } -static int uart_do_autoconfig(struct uart_info *info) +static int uart_do_autoconfig(struct uart_state *state) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; int flags, ret; if (!capable(CAP_SYS_ADMIN)) return -EPERM; /* - * Take the 'count' lock. This prevents count - * from incrementing, and hence any extra opens - * of the port while we're auto-configging. + * Take the per-port semaphore. This prevents count from + * changing, and hence any extra opens of the port while + * we're auto-configuring. */ - if (down_interruptible(&port_sem)) + if (down_interruptible(&state->sem)) return -ERESTARTSYS; ret = -EBUSY; - if (info->state->count == 1 && info->blocked_open == 0) { - uart_shutdown(info); + if (uart_users(state) == 1) { + uart_shutdown(state); /* * If we already have a port type configured, @@ -957,16 +963,22 @@ static int uart_do_autoconfig(struct uart_info *info) */ port->ops->config_port(port, flags); - ret = uart_startup(info, 1); + ret = uart_startup(state, 1); } - up(&port_sem); + up(&state->sem); return ret; } +/* + * 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 + */ static int -uart_wait_modem_status(struct uart_info *info, unsigned long arg) +uart_wait_modem_status(struct uart_state *state, unsigned long arg) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; int ret; @@ -983,7 +995,7 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) port->ops->enable_ms(port); spin_unlock_irq(&port->lock); - add_wait_queue(&info->delta_msr_wait, &wait); + add_wait_queue(&state->info->delta_msr_wait, &wait); for (;;) { spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); @@ -1011,117 +1023,144 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) } current->state = TASK_RUNNING; - remove_wait_queue(&info->delta_msr_wait, &wait); + remove_wait_queue(&state->info->delta_msr_wait, &wait); return ret; } /* - * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + * 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. */ static int -uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, - unsigned long arg) +uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt) { - struct uart_info *info = tty->driver_data; struct serial_icounter_struct icount; struct uart_icount cnow; + struct uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + +/* + * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. + */ +static int +uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct uart_state *state = tty->driver_data; int ret = -ENOIOCTLCMD; BUG_ON(!kernel_locked()); - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; + /* + * These ioctls don't rely on the hardware to be present. + */ + switch (cmd) { + case TIOCGSERIAL: + ret = uart_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; } - switch (cmd) { - case TIOCMGET: - ret = uart_get_modem_info(info->port, - (unsigned int *)arg); - break; + if (ret != -ENOIOCTLCMD) + goto out; - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - ret = uart_set_modem_info(info->port, cmd, - (unsigned int *)arg); - break; + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } - case TIOCGSERIAL: - ret = uart_get_info(info, (struct serial_struct *)arg); - break; + /* + * The following should only be used when hardware is present. + */ + switch (cmd) { + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; - case TIOCSSERIAL: - ret = uart_set_info(info, (struct serial_struct *)arg); - break; + case TIOCGICOUNT: + ret = uart_get_count(state, (struct serial_icounter_struct *)arg); + break; + } - case TIOCSERCONFIG: - ret = uart_do_autoconfig(info); - break; + if (ret != -ENOIOCTLCMD) + goto out; - case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(info, (unsigned int *)arg); - break; + down(&state->sem); - /* - * 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: - ret = uart_wait_modem_status(info, arg); - break; + if (tty_hung_up_p(filp)) { + ret = -EIO; + goto out_up; + } - /* - * 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: - spin_lock_irq(&info->port->lock); - memcpy(&cnow, &info->port->icount, - sizeof(struct uart_icount)); - spin_unlock_irq(&info->port->lock); - - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - ret = copy_to_user((void *)arg, &icount, sizeof(icount)) - ? -EFAULT : 0; - break; + /* + * All these rely on hardware being present and need to be + * protected against the tty being hung up. + */ + switch (cmd) { + case TIOCMGET: + ret = uart_get_modem_info(state->port, (unsigned int *)arg); + break; - case TIOCSERGWILD: /* obsolete */ - case TIOCSERSWILD: /* obsolete */ - ret = 0; - break; + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + ret = uart_set_modem_info(state->port, cmd, + (unsigned int *)arg); + break; - default: { - struct uart_port *port = info->port; - if (port->ops->ioctl) - ret = port->ops->ioctl(port, cmd, arg); - break; - } + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } } + out_up: + up(&state->sem); + out: return ret; } static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; @@ -1137,11 +1176,11 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) return; - uart_change_speed(info, old_termios); + uart_change_speed(state, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) - uart_clear_mctrl(info->port, TIOCM_RTS | TIOCM_DTR); + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { @@ -1149,15 +1188,15 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; - uart_set_mctrl(info->port, mask); + uart_set_mctrl(state->port, mask); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&info->port->lock, flags); + spin_lock_irqsave(&state->port->lock, flags); tty->hw_stopped = 0; __uart_start(tty); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_unlock_irqrestore(&state->port->lock, flags); } #if 0 @@ -1169,7 +1208,7 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&state->info->open_wait); #endif } @@ -1180,29 +1219,17 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios */ static void uart_close(struct tty_struct *tty, struct file *filp) { - struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; - struct uart_state *state; - unsigned long flags; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; BUG_ON(!kernel_locked()); + DPRINTK("uart_close(%d) called\n", port->line); - if (!info) - return; - - state = info->state; + down(&state->sem); - DPRINTK("uart_close() called\n"); - - /* - * This is safe, as long as the BKL exists in - * do_tty_hangup(), and we're protected by the BKL. - */ if (tty_hung_up_p(filp)) goto done; - spin_lock_irqsave(&info->port->lock, flags); if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty @@ -1217,34 +1244,28 @@ static void uart_close(struct tty_struct *tty, struct file *filp) } if (--state->count < 0) { printk("rs_close: bad serial port count for %s%d: %d\n", - tty->driver.name, info->port->line, state->count); + tty->driver.name, port->line, state->count); state->count = 0; } - if (state->count) { - spin_unlock_irqrestore(&info->port->lock, flags); + if (state->count) goto done; - } - - /* - * The UIF_CLOSING flag protects us against further opens - * of this port. - */ - info->flags |= UIF_CLOSING; - spin_unlock_irqrestore(&info->port->lock, flags); /* * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. + * the line discipline to only process XON/XOFF characters by + * setting tty->closing. */ tty->closing = 1; - if (info->state->closing_wait != USF_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->state->closing_wait); + + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (info->flags & UIF_INITIALIZED) { + if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->ops->stop_rx(port); spin_unlock_irqrestore(&port->lock, flags); @@ -1255,46 +1276,38 @@ static void uart_close(struct tty_struct *tty, struct file *filp) */ uart_wait_until_sent(tty, port->timeout); } - down(&port_sem); - uart_shutdown(info); - up(&port_sem); + + uart_shutdown(state); uart_flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; - info->tty = NULL; - if (info->blocked_open) { - if (info->state->close_delay) { + state->info->tty = NULL; + + if (state->info->blocked_open) { + if (state->close_delay) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->state->close_delay); + schedule_timeout(state->close_delay); set_current_state(TASK_RUNNING); } - } else { -#ifdef CONFIG_PM - /* - * Put device into D3 state. - */ - pm_send(info->state->pm, PM_SUSPEND, (void *)3); -#else - if (port->ops->pm) - port->ops->pm(port, 3, 0); -#endif + } else if (!uart_console(port)) { + uart_change_pm(state, 3); } /* * Wake up anyone trying to open this port. */ - info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING); - wake_up_interruptible(&info->open_wait); + state->info->flags &= ~UIF_NORMAL_ACTIVE; + wake_up_interruptible(&state->info->open_wait); done: - module_put(drv->owner); + up(&state->sem); } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long char_time, expire; BUG_ON(!kernel_locked()); @@ -1358,23 +1371,22 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) */ static void uart_hangup(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_state *state = info->state; + struct uart_state *state = tty->driver_data; BUG_ON(!kernel_locked()); + DPRINTK("uart_hangup(%d)\n", state->port->line); - uart_flush_buffer(tty); - down(&port_sem); - if (info->flags & UIF_CLOSING) { - up(&port_sem); - return; + down(&state->sem); + if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + uart_flush_buffer(tty); + uart_shutdown(state); + state->count = 0; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); } - uart_shutdown(info); - state->count = 0; - info->flags &= ~UIF_NORMAL_ACTIVE; - info->tty = NULL; - up(&port_sem); - wake_up_interruptible(&info->open_wait); + up(&state->sem); } /* @@ -1383,18 +1395,15 @@ static void uart_hangup(struct tty_struct *tty) * kernel settings, and the settings init adopts when it opens the port * for the first time. */ -static void uart_update_termios(struct uart_info *info) +static void uart_update_termios(struct uart_state *state) { - struct tty_struct *tty = info->tty; - -#ifdef CONFIG_SERIAL_CORE_CONSOLE - struct console *c = info->port->cons; + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; - if (c && c->cflag && c->index == info->port->line) { - tty->termios->c_cflag = c->cflag; - c->cflag = 0; + if (uart_console(port) && port->cons->cflag) { + tty->termios->c_cflag = port->cons->cflag; + port->cons->cflag = 0; } -#endif /* * If the device failed to grab its irq resources, @@ -1405,22 +1414,26 @@ static void uart_update_termios(struct uart_info *info) /* * Make termios settings take effect. */ - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); /* * And finally enable the RTS and DTR signals. */ if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } } +/* + * Block the open until the port is ready. We must be called with + * the per-port semaphore held. + */ static int -uart_block_til_ready(struct file *filp, struct uart_info *info) +uart_block_til_ready(struct file *filp, struct uart_state *state) { DECLARE_WAITQUEUE(wait, current); - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; info->blocked_open++; state->count--; @@ -1432,18 +1445,10 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) /* * If we have been hung up, tell userspace/restart open. */ - if (tty_hung_up_p(filp)) + if (tty_hung_up_p(filp) || info->tty == NULL) break; /* - * If the device is in the middle of being closed, block - * until it's done. We will need to re-initialise the - * port. Hmm, is it legal to block a non-blocking open? - */ - if (info->flags & UIF_CLOSING) - goto wait; - - /* * If the port has been closed, tell userspace/restart open. */ if (!(info->flags & UIF_INITIALIZED)) @@ -1470,7 +1475,7 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) * the data from the modem. */ if (info->tty->termios->c_cflag & CBAUD) - uart_set_mctrl(info->port, TIOCM_DTR); + uart_set_mctrl(port, TIOCM_DTR); /* * and wait for the carrier to indicate that the @@ -1479,8 +1484,9 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) if (port->ops->get_mctrl(port) & TIOCM_CAR) break; - wait: + up(&state->sem); schedule(); + down(&state->sem); if (signal_pending(current)) break; @@ -1494,52 +1500,56 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) if (signal_pending(current)) return -ERESTARTSYS; - if (info->tty->flags & (1 << TTY_IO_ERROR)) - return 0; - - if (tty_hung_up_p(filp) || !(info->flags & UIF_INITIALIZED)) + if (!info->tty || tty_hung_up_p(filp)) return (port->flags & UPF_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; return 0; } -static struct uart_info *uart_get(struct uart_driver *drv, int line) +static struct uart_state *uart_get(struct uart_driver *drv, int line) { - struct uart_state *state = drv->state + line; - struct uart_info *info = NULL; + struct uart_state *state; down(&port_sem); - if (!state->port) + state = drv->state + line; + if (down_interruptible(&state->sem)) { + state = ERR_PTR(-ERESTARTSYS); goto out; + } state->count++; - info = state->info; + if (!state->port) { + state->count--; + up(&state->sem); + state = ERR_PTR(-ENXIO); + goto out; + } - if (!info) { - info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); - if (info) { - memset(info, 0, sizeof(struct uart_info)); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->delta_msr_wait); + if (!state->info) { + state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); /* * Link the info into the other structures. */ - info->port = state->port; - info->state = state; - state->port->info = info; - - tasklet_init(&info->tlet, uart_tasklet_action, - (unsigned long)info); - state->info = info; - } else + state->port->info = state->info; + + tasklet_init(&state->info->tlet, uart_tasklet_action, + (unsigned long)state); + } else { state->count--; + up(&state->sem); + state = ERR_PTR(-ENOMEM); + } } out: up(&port_sem); - return info; + return state; } /* @@ -1555,11 +1565,10 @@ static struct uart_info *uart_get(struct uart_driver *drv, int line) static int uart_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; - struct uart_info *info; + struct uart_state *state; int retval, line = minor(tty->device) - tty->driver.minor_start; BUG_ON(!kernel_locked()); - DPRINTK("uart_open(%d) called\n", line); /* @@ -1572,92 +1581,66 @@ static int uart_open(struct tty_struct *tty, struct file *filp) goto fail; /* - * If we fail to increment the module use count, we can't have - * any other users of this tty (since this implies that the module - * is about to be unloaded). Therefore, it is safe to set - * tty->driver_data to be NULL, so uart_close() doesn't bite us. + * We take the semaphore inside uart_get to guarantee that we won't + * be re-entered while allocating the info structure, or while we + * request any IRQs that the driver may need. This also has the nice + * side-effect that it delays the action of uart_hangup, so we can + * guarantee that info->tty will always contain something reasonable. */ - if (!try_module_get(drv->owner)) { - tty->driver_data = NULL; + state = uart_get(drv, line); + if (IS_ERR(state)) { + retval = PTR_ERR(state); goto fail; } /* - * FIXME: This one isn't fun. We can't guarantee that the tty isn't - * already in open, nor can we guarantee the state of tty->driver_data - */ - info = uart_get(drv, line); - retval = -ENOMEM; - if (!info) { - if (tty->driver_data) - goto fail; - else - goto out; - } - - /* * Once we set tty->driver_data here, we are guaranteed that * uart_close() will decrement the driver module use count. * Any failures from here onwards should not touch the count. */ - tty->driver_data = info; - tty->low_latency = (info->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; - info->tty = tty; + state->info->tty = tty; /* * If the port is in the middle of closing, bail out now. */ - if (tty_hung_up_p(filp) || (info->flags & UIF_CLOSING)) { - wait_event_interruptible(info->open_wait, - !(info->flags & UIF_CLOSING)); - retval = (info->port->flags & UPF_HUP_NOTIFY) ? + if (tty_hung_up_p(filp)) { + retval = (state->port->flags & UPF_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; + state->count--; + up(&state->sem); goto fail; } /* * Make sure the device is in D0 state. */ - if (info->state->count == 1) { -#ifdef CONFIG_PM - pm_send(info->state->pm, PM_RESUME, (void *)0); -#else - struct uart_port *port = info->port; - if (port->ops->pm) - port->ops->pm(port, 0, 3); -#endif - } + if (state->count == 1) + uart_change_pm(state, 0); /* - * Start up the serial port. We have this semaphore here to - * prevent uart_startup or uart_shutdown being re-entered if - * we sleep while requesting an IRQ. + * Start up the serial port. */ - down(&port_sem); - retval = uart_startup(info, 0); - up(&port_sem); - if (retval) - goto fail; + retval = uart_startup(state, 0); /* - * Wait until the port is ready. + * If we succeeded, wait until the port is ready. */ - retval = uart_block_til_ready(filp, info); + if (retval == 0) + retval = uart_block_til_ready(filp, state); + up(&state->sem); /* * If this is the first open to succeed, adjust things to suit. */ - if (retval == 0 && !(info->flags & UIF_NORMAL_ACTIVE)) { - info->flags |= UIF_NORMAL_ACTIVE; + if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { + state->info->flags |= UIF_NORMAL_ACTIVE; - uart_update_termios(info); + uart_update_termios(state); } - return retval; - - out: - module_put(drv->owner); fail: return retval; } @@ -1892,115 +1875,98 @@ uart_set_options(struct uart_port *port, struct console *co, } #endif /* CONFIG_SERIAL_CORE_CONSOLE */ -#ifdef CONFIG_PM -/* - * Serial port power management. - * - * This is pretty coarse at the moment - either all on or all off. We - * should probably some day do finer power management here some day. - * - * We don't actually save any state; the serial driver already has the - * state held internally to re-setup the port when we come out of D3. - */ -static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstate) +static void uart_change_pm(struct uart_state *state, int pm_state) { - struct uart_port *port; - struct uart_ops *ops; - int running = state->info && - state->info->flags & UIF_INITIALIZED; + struct uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; +} - down(&port_sem); +int uart_suspend_port(struct uart_driver *drv, struct uart_port *port, u32 level) +{ + struct uart_state *state = drv->state + port->line; - if (!state->port || state->port->type == PORT_UNKNOWN) { - up(&port_sem); - return 0; - } + down(&state->sem); - port = state->port; - ops = port->ops; + switch (level) { + case SUSPEND_SAVE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; - DPRINTK("pm: %08x: %d -> %d, %srunning\n", - port->iobase, dev->state, pm_state, running ? "" : "not "); + spin_lock_irq(&port->lock); + ops->stop_tx(port, 0); + ops->set_mctrl(port, 0); + ops->stop_rx(port); + spin_unlock_irq(&port->lock); - if (pm_state == 0) { - if (ops->pm) - ops->pm(port, pm_state, oldstate); - if (running) { /* - * The port lock isn't taken here - - * the port isn't initialised. + * Wait for the transmitter to empty. */ - ops->set_mctrl(port, 0); - ops->startup(port); - uart_change_speed(state->info, NULL); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, port->mctrl); - ops->start_tx(port, 0); - spin_unlock_irq(&port->lock); + while (!ops->tx_empty(port)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10*HZ/1000); + } + set_current_state(TASK_RUNNING); + + ops->shutdown(port); } + break; - /* - * Re-enable the console device after suspending. - */ - if (port->cons && port->cons->index == port->line) - port->cons->flags |= CON_ENABLED; - } else if (pm_state == 1) { - if (ops->pm) - ops->pm(port, pm_state, oldstate); - } else { + case SUSPEND_POWER_DOWN: /* * Disable the console device before suspending. */ - if (port->cons && port->cons->index == port->line) + if (uart_console(port)) port->cons->flags &= ~CON_ENABLED; - if (running) { - spin_lock_irq(&port->lock); - ops->stop_tx(port, 0); - ops->set_mctrl(port, 0); - ops->stop_rx(port); - spin_unlock_irq(&port->lock); - ops->shutdown(port); - } - if (ops->pm) - ops->pm(port, pm_state, oldstate); + uart_change_pm(state, 3); + break; } - up(&port_sem); + + up(&state->sem); return 0; } -/* - * Wakeup support. - */ -static int uart_pm_set_wakeup(struct uart_state *state, int data) +int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level) { - int err = 0; - - if (state->port->ops->set_wake) - err = state->port->ops->set_wake(state->port, data); + struct uart_state *state = drv->state + port->line; - return err; -} + down(&state->sem); -static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct uart_state *state = dev->data; - int err = 0; + switch (level) { + case RESUME_POWER_ON: + uart_change_pm(state, 0); - switch (rqst) { - case PM_SUSPEND: - case PM_RESUME: - err = uart_pm_set_state(state, (int)(long)data, dev->state); + /* + * Re-enable the console device after suspending. + */ + if (uart_console(port)) { + uart_change_speed(state, NULL); + port->cons->flags |= CON_ENABLED; + } break; - case PM_SET_WAKEUP: - err = uart_pm_set_wakeup(state, (int)(long)data); + case RESUME_RESTORE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + ops->set_mctrl(port, 0); + ops->startup(port); + uart_change_speed(state, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port, 0); + spin_unlock_irq(&port->lock); + } break; } - return err; + + up(&state->sem); + + return 0; } -#endif static inline void uart_report_port(struct uart_driver *drv, struct uart_port *port) @@ -2022,17 +1988,11 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) } static void -__uart_register_port(struct uart_driver *drv, struct uart_state *state, - struct uart_port *port) +uart_configure_port(struct uart_driver *drv, struct uart_state *state, + struct uart_port *port) { unsigned int flags; - state->port = port; - - spin_lock_init(&port->lock); - port->cons = drv->cons; - port->info = state->info; - /* * If there isn't a port here, don't do anything further. */ @@ -2051,12 +2011,6 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, port->ops->config_port(port, flags); } - /* - * Register the port whether it's detected or not. This allows - * setserial to be used to alter this ports parameters. - */ - tty_register_device(drv->tty_driver, drv->minor + port->line); - if (port->type != PORT_UNKNOWN) { unsigned long flags; @@ -2070,51 +2024,33 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, port->ops->set_mctrl(port, 0); spin_unlock_irqrestore(&port->lock, flags); -#ifdef CONFIG_PM /* * Power down all ports by default, except the - * console if we have one. We need to drop the - * port semaphore here. + * console if we have one. */ - if (state->pm && (!drv->cons || port->line != drv->cons->index)) { - up(&port_sem); - pm_send(state->pm, PM_SUSPEND, (void *)3); - down(&port_sem); - } -#endif + if (!uart_console(port)) + uart_change_pm(state, 3); } } /* - * Hangup the port. This must be done outside the port_sem - * since uart_hangup() grabs this same semaphore. Grr. + * This reverses the affects of uart_configure_port, hanging up the + * port before removal. */ static void -__uart_hangup_port(struct uart_driver *drv, struct uart_state *state) +uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) { + struct uart_port *port = state->port; struct uart_info *info = state->info; if (info && info->tty) tty_vhangup(info->tty); -} -/* - * This reverses the affects of __uart_register_port. - */ -static void -__uart_unregister_port(struct uart_driver *drv, struct uart_state *state) -{ - struct uart_port *port = state->port; - struct uart_info *info = state->info; + down(&state->sem); state->info = NULL; /* - * Remove the devices from devfs - */ - tty_unregister_device(drv->tty_driver, drv->minor + port->line); - - /* * Free the port IO and memory resources, if any. */ if (port->type != PORT_UNKNOWN) @@ -2132,6 +2068,8 @@ __uart_unregister_port(struct uart_driver *drv, struct uart_state *state) tasklet_kill(&info->tlet); kfree(info); } + + up(&state->sem); } /** @@ -2186,6 +2124,7 @@ int uart_register_driver(struct uart_driver *drv) drv->tty_driver = normal; normal->magic = TTY_DRIVER_MAGIC; + normal->owner = drv->owner; normal->driver_name = drv->driver_name; normal->name = drv->dev_name; normal->major = drv->major; @@ -2232,20 +2171,13 @@ int uart_register_driver(struct uart_driver *drv) state->close_delay = 5 * HZ / 10; state->closing_wait = 30 * HZ; -#ifdef CONFIG_PM - state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); - if (state->pm) - state->pm->data = state; -#endif + + init_MUTEX(&state->sem); } retval = tty_register_driver(normal); out: if (retval < 0) { -#ifdef CONFIG_PM - for (i = 0; i < drv->nr; i++) - pm_unregister(drv->state[i].pm); -#endif kfree(normal); kfree(drv->state); kfree(termios); @@ -2264,11 +2196,6 @@ int uart_register_driver(struct uart_driver *drv) */ void uart_unregister_driver(struct uart_driver *drv) { - int i; - - for (i = 0; i < drv->nr; i++) - pm_unregister(drv->state[i].pm); - tty_unregister_driver(drv->tty_driver); kfree(drv->state); @@ -2289,6 +2216,7 @@ void uart_unregister_driver(struct uart_driver *drv) int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state; + int ret = 0; BUG_ON(in_interrupt()); @@ -2298,10 +2226,29 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) state = drv->state + port->line; down(&port_sem); - __uart_register_port(drv, state, port); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + uart_configure_port(drv, state, port); + + /* + * Register the port whether it's detected or not. This allows + * setserial to be used to alter this ports parameters. + */ + tty_register_device(drv->tty_driver, drv->minor + port->line); + + out: up(&port_sem); - return 0; + return ret; } /** @@ -2323,10 +2270,14 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) printk(KERN_ALERT "Removing wrong port: %p != %p\n", state->port, port); - __uart_hangup_port(drv, state); - down(&port_sem); - __uart_unregister_port(drv, state); + + /* + * Remove the devices from devfs + */ + tty_unregister_device(drv->tty_driver, drv->minor + port->line); + + uart_unconfigure_port(drv, state); state->port = NULL; up(&port_sem); @@ -2426,8 +2377,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) * alter it underneath itself - the port may be open and * trying to do useful work. */ - if (state->count != 0 || - (state->info && state->info->blocked_open != 0)) { + if (uart_users(state) != 0) { ret = -EBUSY; goto out; } @@ -2447,7 +2397,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) state->port->line = state - drv->state; state->port->mapbase = port->mapbase; - __uart_register_port(drv, state, state->port); + uart_configure_port(drv, state, state->port); } ret = state->port->line; @@ -2479,16 +2429,16 @@ void uart_unregister_port(struct uart_driver *drv, int line) state = drv->state + line; - __uart_hangup_port(drv, state); - down(&port_sem); - __uart_unregister_port(drv, state); + uart_unconfigure_port(drv, state); up(&port_sem); } EXPORT_SYMBOL(uart_write_wakeup); EXPORT_SYMBOL(uart_register_driver); EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_suspend_port); +EXPORT_SYMBOL(uart_resume_port); EXPORT_SYMBOL(uart_register_port); EXPORT_SYMBOL(uart_unregister_port); EXPORT_SYMBOL(uart_add_one_port); diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index cf3160b26f57..b32608e75444 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -32,6 +32,7 @@ #include <linux/serial.h> #include <linux/console.h> #include <linux/sysrq.h> +#include <linux/device.h> #include <asm/io.h> #include <asm/irq.h> @@ -857,12 +858,54 @@ static struct uart_driver sa1100_reg = { .cons = SA1100_CONSOLE, }; +static int sa1100_serial_suspend(struct device *dev, u32 state, u32 level) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_suspend_port(&sa1100_reg, &sa1100_ports[i].port, level); + + return 0; +} + +static int sa1100_serial_resume(struct device *dev, u32 level) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_resume_port(&sa1100_reg, &sa1100_ports[i].port, level); + + return 0; +} + +static struct device_driver sa11x0_serial_driver = { + .name = "sa11x0_serial", + .bus = &system_bus_type, + .devclass = &tty_devclass, + .suspend = sa1100_serial_suspend, + .resume = sa1100_serial_resume, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices. + */ +static struct sys_device sa11x0_serial_devs = { + .name = "sa11x0_serial", + .id = 0, + .dev = { + .driver = &sa11x0_serial_driver, + }, +}; + static int __init sa1100_serial_init(void) { int ret; printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.50 $\n"); + driver_register(&sa11x0_serial_driver); + sys_device_register(&sa11x0_serial_devs); + sa1100_init_ports(); ret = uart_register_driver(&sa1100_reg); if (ret == 0) { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index e2e9b9f1c2c9..ba94052020f0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1235,6 +1235,7 @@ #define PCI_VENDOR_ID_XIRCOM 0x115d #define PCI_DEVICE_ID_XIRCOM_X3201_ETH 0x0003 +#define PCI_DEVICE_ID_XIRCOM_RBM56G 0x0101 #define PCI_DEVICE_ID_XIRCOM_X3201_MDM 0x0103 #define PCI_VENDOR_ID_RENDITION 0x1163 diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3412f8b7f2ce..80ba519dda77 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -208,12 +208,11 @@ struct uart_state { #define USF_CLOSING_WAIT_NONE (65535) int count; + int pm_state; struct uart_info *info; struct uart_port *port; -#ifdef CONFIG_PM - struct pm_dev *pm; -#endif + struct semaphore sem; }; #define UART_XMIT_SIZE 1024 @@ -224,8 +223,6 @@ struct uart_state { * stuff here. */ struct uart_info { - struct uart_port *port; - struct uart_state *state; struct tty_struct *tty; struct circ_buf xmit; unsigned int flags; @@ -237,7 +234,6 @@ struct uart_info { */ #define UIF_CHECK_CD (1 << 25) #define UIF_CTS_FLOW (1 << 26) -#define UIF_CLOSING (1 << 27) #define UIF_NORMAL_ACTIVE (1 << 29) #define UIF_INITIALIZED (1 << 31) @@ -307,6 +303,12 @@ int uart_register_port(struct uart_driver *reg, struct uart_port *port); int uart_add_one_port(struct uart_driver *reg, struct uart_port *port); int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port); +/* + * Power Management + */ +int uart_suspend_port(struct uart_driver *reg, struct uart_port *port, u32 level); +int uart_resume_port(struct uart_driver *reg, struct uart_port *port, u32 level); + #define uart_circ_empty(circ) ((circ)->head == (circ)->tail) #define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) |
