Commit 0deb87aa authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://bk.arm.linux.org.uk

into home.transmeta.com:/home/torvalds/v2.5/linux
parents eb9d0aa4 c4ab9d78
...@@ -2235,14 +2235,19 @@ struct device_class tty_devclass = { ...@@ -2235,14 +2235,19 @@ struct device_class tty_devclass = {
}; };
EXPORT_SYMBOL(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 * Ok, now we can initialize the rest of the tty devices and can count
* on memory allocations, interrupts etc.. * on memory allocations, interrupts etc..
*/ */
void __init tty_init(void) void __init tty_init(void)
{ {
devclass_register(&tty_devclass);
/* /*
* dev_tty_driver and dev_console_driver are actually magic * dev_tty_driver and dev_console_driver are actually magic
* devices which get redirected at open time. Nevertheless, * devices which get redirected at open time. Nevertheless,
......
...@@ -93,13 +93,15 @@ unsigned int share_irqs = SERIAL8250_SHARE_IRQS; ...@@ -93,13 +93,15 @@ unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
#ifdef CONFIG_SERIAL_8250_MULTIPORT #ifdef CONFIG_SERIAL_8250_MULTIPORT
#define CONFIG_SERIAL_MULTIPORT 1 #define CONFIG_SERIAL_MULTIPORT 1
#endif #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 * HUB6 is always on. This will be removed once the header
* files have been cleaned. * files have been cleaned.
*/ */
#define CONFIG_HUB6 1 #define CONFIG_HUB6 1
#define CONFIG_SERIAL_MANY_PORTS 1
#include <asm/serial.h> #include <asm/serial.h>
...@@ -2095,6 +2097,28 @@ void serial8250_get_irq_map(unsigned int *map) ...@@ -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) static int __init serial8250_init(void)
{ {
int ret, i; int ret, i;
...@@ -2128,6 +2152,8 @@ module_exit(serial8250_exit); ...@@ -2128,6 +2152,8 @@ module_exit(serial8250_exit);
EXPORT_SYMBOL(register_serial); EXPORT_SYMBOL(register_serial);
EXPORT_SYMBOL(unregister_serial); EXPORT_SYMBOL(unregister_serial);
EXPORT_SYMBOL(serial8250_get_irq_map); EXPORT_SYMBOL(serial8250_get_irq_map);
EXPORT_SYMBOL(serial8250_suspend_port);
EXPORT_SYMBOL(serial8250_resume_port);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $"); MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $");
......
...@@ -27,6 +27,8 @@ struct serial8250_probe { ...@@ -27,6 +27,8 @@ struct serial8250_probe {
int serial8250_register_probe(struct serial8250_probe *probe); int serial8250_register_probe(struct serial8250_probe *probe);
void serial8250_unregister_probe(struct serial8250_probe *probe); void serial8250_unregister_probe(struct serial8250_probe *probe);
void serial8250_get_irq_map(unsigned int *map); 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 { struct old_serial_port {
unsigned int uart; unsigned int uart;
......
/* /*
* linux/drivers/serial/acorn.c * 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 * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/tty.h>
#include <linux/serial_core.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/ioport.h> #include <linux/ioport.h>
...@@ -16,6 +18,7 @@ ...@@ -16,6 +18,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/init.h> #include <linux/init.h>
#include <asm/io.h>
#include <asm/ecard.h> #include <asm/ecard.h>
#include <asm/string.h> #include <asm/string.h>
...@@ -24,36 +27,41 @@ ...@@ -24,36 +27,41 @@
struct serial_card_type { struct serial_card_type {
unsigned int num_ports; unsigned int num_ports;
unsigned int baud_base; unsigned int baud_base;
int type; unsigned int type;
int speed; unsigned int offset[MAX_PORTS];
int offset[MAX_PORTS];
}; };
struct serial_card_info { struct serial_card_info {
unsigned int num_ports; unsigned int num_ports;
int ports[MAX_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; struct serial_struct req;
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.baud_base = baud_base; req.irq = irq;
req.irq = irq; req.flags = UPF_AUTOPROBE | UPF_RESOURCES |
req.port = port; UPF_SHARE_IRQ;
req.flags = 0; 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); 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_info *info;
struct serial_card_type *type = id->data; struct serial_card_type *type = id->data;
unsigned long cardaddr, address; unsigned long bus_addr;
int port; unsigned char *virt_addr;
unsigned int port;
info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL); info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL);
if (!info) if (!info)
...@@ -64,20 +72,19 @@ static int __devinit serial_card_probe(struct expansion_card *ec, const struct e ...@@ -64,20 +72,19 @@ static int __devinit serial_card_probe(struct expansion_card *ec, const struct e
ecard_set_drvdata(ec, info); 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 ++) { for (port = 0; port < info->num_ports; port ++) {
address = cardaddr + type->offset[port]; unsigned long baddr = bus_addr + type->offset[port];
unsigned char *vaddr = virt_addr + type->offset[port];
info->ports[port] = -1;
info->base[port] = address;
if (!request_region(address, 8, "acornserial")) info->ports[port] = serial_register_onedev(baddr, vaddr,
continue; ec->irq, type->baud_base);
info->ports[port] = serial_register_onedev(address, ec->irq, type->baud_base);
if (info->ports[port] < 0)
break;
} }
return 0; return 0;
...@@ -90,12 +97,9 @@ static void __devexit serial_card_remove(struct expansion_card *ec) ...@@ -90,12 +97,9 @@ static void __devexit serial_card_remove(struct expansion_card *ec)
ecard_set_drvdata(ec, NULL); ecard_set_drvdata(ec, NULL);
for (i = 0; i < info->num_ports; i++) { for (i = 0; i < info->num_ports; i++)
if (info->ports[i] > 0) { if (info->ports[i] > 0)
unregister_serial(info->ports[i]); unregister_serial(info->ports[i]);
release_region(info->base[i], 8);
}
}
kfree(info); kfree(info);
} }
...@@ -103,17 +107,15 @@ static void __devexit serial_card_remove(struct expansion_card *ec) ...@@ -103,17 +107,15 @@ static void __devexit serial_card_remove(struct expansion_card *ec)
static struct serial_card_type atomwide_type = { static struct serial_card_type atomwide_type = {
.num_ports = 3, .num_ports = 3,
.baud_base = 7372800 / 16, .baud_base = 7372800 / 16,
.type = ECARD_IOC, .type = ECARD_RES_IOCSLOW,
.speed = ECARD_SLOW, .offset = { 0x2800, 0x2400, 0x2000 },
.offset = { 0xa00, 0x900, 0x800 },
}; };
static struct serial_card_type serport_type = { static struct serial_card_type serport_type = {
.num_ports = 2, .num_ports = 2,
.baud_base = 3686400 / 16, .baud_base = 3686400 / 16,
.type = ECARD_IOC, .type = ECARD_RES_IOCSLOW,
.speed = ECARD_SLOW, .offset = { 0x2000, 0x2020 },
.offset = { 0x800, 0x808 },
}; };
static const struct ecard_id serial_cids[] = { static const struct ecard_id serial_cids[] = {
...@@ -127,7 +129,8 @@ static struct ecard_driver serial_card_driver = { ...@@ -127,7 +129,8 @@ static struct ecard_driver serial_card_driver = {
.remove = __devexit_p(serial_card_remove), .remove = __devexit_p(serial_card_remove),
.id_table = serial_cids, .id_table = serial_cids,
.drv = { .drv = {
.name = "acornserial", .devclass = &tty_devclass,
.name = "8250_acorn",
}, },
}; };
...@@ -142,6 +145,7 @@ static void __exit serial_card_exit(void) ...@@ -142,6 +145,7 @@ static void __exit serial_card_exit(void)
} }
MODULE_AUTHOR("Russell King"); MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(serial_card_init); module_init(serial_card_init);
......
...@@ -20,8 +20,9 @@ ...@@ -20,8 +20,9 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/tty.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serialP.h> #include <linux/serial_core.h>
#include <asm/bitops.h> #include <asm/bitops.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
...@@ -30,174 +31,213 @@ ...@@ -30,174 +31,213 @@
#include "8250.h" #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 struct pci_board {
#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ unsigned int flags;
IORESOURCE_IO) unsigned int num_ports;
#endif unsigned int base_baud;
#ifndef IS_PCI_REGION_IOMEM unsigned int uart_offset;
#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ unsigned int reg_shift;
IORESOURCE_MEM) unsigned int first_offset;
#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];
}; };
/* /*
* init_fn returns: * init function returns:
* > 0 - number of ports * > 0 - number of ports
* = 0 - use board->num_ports * = 0 - use board->num_ports
* < 0 - error * < 0 - error
*/ */
struct pci_board { struct pci_serial_quirk {
int flags; u32 vendor;
int num_ports; u32 device;
int base_baud; u32 subvendor;
int uart_offset; u32 subdevice;
int reg_shift; int (*init)(struct pci_dev *dev);
int (*init_fn)(struct pci_dev *dev, int enable); int (*setup)(struct pci_dev *dev, struct pci_board *board,
int first_uart_offset; 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 static int
get_pci_port(struct pci_dev *dev, struct pci_board *board, setup_port(struct pci_dev *dev, struct serial_struct *req,
struct serial_struct *req, int idx) int bar, int offset, int regshift)
{ {
unsigned long port; struct serial_private *priv = pci_get_drvdata(dev);
int base_idx; unsigned long port, len;
int max_port;
int offset;
base_idx = SPCI_FL_GET_BASE(board->flags); if (bar >= PCI_NUM_BAR_RESOURCES)
if (board->flags & SPCI_FL_BASE_TABLE) return -EINVAL;
base_idx += idx;
if (board->flags & SPCI_FL_REGION_SZ_CAP) { if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
max_port = pci_resource_len(dev, base_idx) / 8; port = pci_resource_start(dev, bar);
if (idx >= max_port) len = pci_resource_len(dev, bar);
return 1;
}
offset = board->first_uart_offset;
/* if (!priv->remapped_bar[bar])
* Timedia/SUNIX uses a mixture of BARs and offsets priv->remapped_bar[bar] = ioremap(port, len);
* Ugh, this is ugly as all hell --- TYT if (!priv->remapped_bar[bar])
*/ return -ENOMEM;
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;
}
/* AFAVLAB uses a different mixture of BARs and offsets */ req->io_type = UPIO_MEM;
/* Not that ugly ;) -- HW */ req->iomap_base = port;
if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) { req->iomem_base = priv->remapped_bar[bar] + offset;
base_idx = 4; req->iomem_reg_shift = regshift;
offset = (idx - 4) * 8; } 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 && * AFAVLAB uses a different mixture of BARs and offsets
(dev->device == PCI_DEVICE_ID_TITAN_400L || * Not that ugly ;) -- HW
dev->device == PCI_DEVICE_ID_TITAN_800L)) { */
switch (idx) { static int
case 0: base_idx = 1; afavlab_setup(struct pci_dev *dev, struct pci_board *board,
break; struct serial_struct *req, int idx)
case 1: base_idx = 2; {
break; unsigned int bar, offset = board->first_offset;
default:
base_idx = 4; bar = FL_GET_BASE(board->flags);
offset = 8 * (idx - 2); 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 return setup_port(dev, req, bar, offset, board->reg_shift);
* 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) { * HP's Remote Management Console. The Diva chip came in several
switch (dev->subsystem_device) { * different versions. N-class, L2000 and A500 have two Diva chips, each
case PCI_DEVICE_ID_HP_DIVA_MAESTRO: * with 3 UARTs (the third UART on the second chip is unused). Superdome
if (idx == 3) * and Keystone have one Diva chip with 3 UARTs. Some later machines have
idx++; * one Diva chip, but it has been expanded to 5 UARTs.
break; */
case PCI_DEVICE_ID_HP_DIVA_EVEREST: static int __devinit pci_hp_diva_init(struct pci_dev *dev)
if (idx > 0) {
idx++; int rc = 0;
if (idx > 2)
idx++; switch (dev->subsystem_device) {
break; case PCI_DEVICE_ID_HP_DIVA_TOSCA1:
} case PCI_DEVICE_ID_HP_DIVA_HALFDOME:
if (idx > 2) { case PCI_DEVICE_ID_HP_DIVA_KEYSTONE:
offset = 0x18; 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)) { switch (dev->subsystem_device) {
req->port = port; case PCI_DEVICE_ID_HP_DIVA_MAESTRO:
if (HIGH_BITS_OFFSET) if (idx == 3)
req->port_high = port >> HIGH_BITS_OFFSET; idx++;
else break;
req->port_high = 0; case PCI_DEVICE_ID_HP_DIVA_EVEREST:
return 0; if (idx > 0)
idx++;
if (idx > 2)
idx++;
break;
} }
req->io_type = SERIAL_IO_MEM; if (idx > 2)
req->iomap_base = port; offset = 0x18;
req->iomem_base = ioremap(port, board->uart_offset);
if (req->iomem_base == NULL) offset += idx * board->uart_offset;
return -ENOMEM;
req->iomem_reg_shift = board->reg_shift; return setup_port(dev, req, bar, offset, board->reg_shift);
req->port = 0;
return 0;
} }
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) if (!(dev->subsystem_device & 0x1000))
return dev->irq; return -ENODEV;
base_idx = SPCI_FL_GET_IRQBASE(board->flags); /* is firmware started? */
if (board->flags & SPCI_FL_IRQ_TABLE) pci_read_config_dword(dev, 0x44, (void*) &oldval);
base_idx += idx; if (oldval == 0x00001000L) { /* RESET value */
printk(KERN_DEBUG "Local i960 firmware missing");
return PCI_IRQ_RESOURCE(dev, base_idx); return -ENODEV;
}
return 0;
} }
/* /*
...@@ -206,26 +246,29 @@ get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) ...@@ -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 * seems to be mainly needed on card using the PLX which also use I/O
* mapped memory. * 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; u8 *p, irq_config;
if (enable) { if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) {
irq_config = 0x41; moan_device("no memory in bar 0", dev);
if (dev->vendor == PCI_VENDOR_ID_PANACOM) return 0;
irq_config = 0x43; }
if ((dev->vendor == PCI_VENDOR_ID_PLX) &&
(dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { irq_config = 0x41;
/* if (dev->vendor == PCI_VENDOR_ID_PANACOM)
* As the megawolf cards have the int pins active irq_config = 0x43;
* high, and have 2 UART chips, both ints must be if ((dev->vendor == PCI_VENDOR_ID_PLX) &&
* enabled on the 9050. Also, the UARTS are set in (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) {
* 16450 mode by default, so we have to enable the /*
* 16C950 'enhanced' mode so that we can use the * As the megawolf cards have the int pins active
* deep FIFOs * high, and have 2 UART chips, both ints must be
*/ * enabled on the 9050. Also, the UARTS are set in
irq_config = 0x5b; * 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) ...@@ -245,6 +288,27 @@ static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable)
return 0; 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 * 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) ...@@ -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_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) #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; u16 data, *p;
if (!enable)
return 0;
switch (dev->device & 0xfff8) { switch (dev->device & 0xfff8) {
case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */
data = 0xffdf; data = 0xffdf;
break; break;
case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */
data = 0xf7ff; data = 0xf7ff;
break; break;
default: /* 1S1P, 4S */ default: /* 1S1P, 4S */
data = 0xfffb; data = 0xfffb;
break; break;
} }
p = ioremap(pci_resource_start(dev, 0), 0x80); p = ioremap(pci_resource_start(dev, 0), 0x80);
...@@ -294,22 +355,18 @@ int pci_siig10x_fn(struct pci_dev *dev, int enable) ...@@ -294,22 +355,18 @@ int pci_siig10x_fn(struct pci_dev *dev, int enable)
return -ENOMEM; return -ENOMEM;
writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28);
readw((unsigned long)p + 0x28);
iounmap(p); iounmap(p);
return 0; 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_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) #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; u8 data;
if (!enable)
return 0;
/* Change clock frequency for the first UART. */ /* Change clock frequency for the first UART. */
pci_read_config_byte(dev, 0x6f, &data); pci_read_config_byte(dev, 0x6f, &data);
pci_write_config_byte(dev, 0x6f, data & 0xef); pci_write_config_byte(dev, 0x6f, data & 0xef);
...@@ -323,28 +380,25 @@ int pci_siig20x_fn(struct pci_dev *dev, int enable) ...@@ -323,28 +380,25 @@ int pci_siig20x_fn(struct pci_dev *dev, int enable)
return 0; return 0;
} }
EXPORT_SYMBOL(pci_siig20x_fn); int pci_siig10x_fn(struct pci_dev *dev, int enable)
/* Added for EKF Intel i960 serial boards */
static int __devinit pci_inteli960ni_fn(struct pci_dev *dev, int enable)
{ {
unsigned long oldval; int ret = 0;
if (enable)
if (!(pci_get_subdevice(dev) & 0x1000)) ret = pci_siig10x_init(dev);
return -ENODEV; return ret;
}
if (!enable) /* is there something to deinit? */ int pci_siig20x_fn(struct pci_dev *dev, int enable)
return 0; {
int ret = 0;
/* is firmware started? */ if (enable)
pci_read_config_dword(dev, 0x44, (void*) &oldval); ret = pci_siig20x_init(dev);
if (oldval == 0x00001000L) { /* RESET value */ return ret;
printk(KERN_DEBUG "Local i960 firmware missing");
return -ENODEV;
}
return 0;
} }
EXPORT_SYMBOL(pci_siig10x_fn);
EXPORT_SYMBOL(pci_siig20x_fn);
/* /*
* Timedia has an explosion of boards, and to avoid the PCI table from * Timedia has an explosion of boards, and to avoid the PCI table from
* growing *huge*, we use this function to collapse some 70 entries * growing *huge*, we use this function to collapse some 70 entries
...@@ -385,78 +439,453 @@ static struct timedia_struct { ...@@ -385,78 +439,453 @@ static struct timedia_struct {
{ 0, 0 } { 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; unsigned short *ids;
int i, j;
if (!enable)
return 0;
for (i = 0; timedia_data[i].num; i++) { for (i = 0; timedia_data[i].num; i++) {
ids = timedia_data[i].ids; ids = timedia_data[i].ids;
for (j = 0; ids[j]; j++) 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 timedia_data[i].num;
} }
return 0; return 0;
} }
/* /*
* HP's Remote Management Console. The Diva chip came in several * Timedia/SUNIX uses a mixture of BARs and offsets
* different versions. N-class, L2000 and A500 have two Diva chips, each * Ugh, this is ugly as all hell --- TYT
* 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(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; unsigned int bar = 0, offset = board->first_offset;
if (!enable)
return 0;
switch (dev->subsystem_device) { switch (idx) {
case PCI_DEVICE_ID_HP_DIVA_TOSCA1: case 0:
case PCI_DEVICE_ID_HP_DIVA_HALFDOME: bar = 0;
case PCI_DEVICE_ID_HP_DIVA_KEYSTONE:
case PCI_DEVICE_ID_HP_DIVA_EVEREST:
rc = 3;
break; break;
case PCI_DEVICE_ID_HP_DIVA_TOSCA2: case 1:
rc = 2; offset = board->uart_offset;
bar = 0;
break; break;
case PCI_DEVICE_ID_HP_DIVA_MAESTRO: case 2:
rc = 4; bar = 1;
break;
case PCI_DEVICE_ID_HP_DIVA_POWERBAR:
rc = 1;
break; 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); __set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10); schedule_timeout(HZ/10);
return 0; 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 * 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 * 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 * value, which is encoded in the pci_device_id PCI probe table's
* driver_data member. * 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 { enum pci_board_num_t {
pbn_b0_1_115200,
pbn_default = 0, pbn_default = 0,
pbn_b0_1_115200,
pbn_b0_2_115200, pbn_b0_2_115200,
pbn_b0_4_115200, pbn_b0_4_115200,
pbn_b0_5_115200,
pbn_b0_1_921600, pbn_b0_1_921600,
pbn_b0_2_921600, pbn_b0_2_921600,
...@@ -465,171 +894,465 @@ enum pci_board_num_t { ...@@ -465,171 +894,465 @@ enum pci_board_num_t {
pbn_b0_bt_1_115200, pbn_b0_bt_1_115200,
pbn_b0_bt_2_115200, pbn_b0_bt_2_115200,
pbn_b0_bt_8_115200, pbn_b0_bt_8_115200,
pbn_b0_bt_1_460800, pbn_b0_bt_1_460800,
pbn_b0_bt_2_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_1_115200,
pbn_b1_2_115200, pbn_b1_2_115200,
pbn_b1_4_115200, pbn_b1_4_115200,
pbn_b1_8_115200, pbn_b1_8_115200,
pbn_b1_1_921600,
pbn_b1_2_921600, pbn_b1_2_921600,
pbn_b1_4_921600, pbn_b1_4_921600,
pbn_b1_8_921600, pbn_b1_8_921600,
pbn_b1_bt_2_921600,
pbn_b1_2_1382400, pbn_b1_2_1382400,
pbn_b1_4_1382400, pbn_b1_4_1382400,
pbn_b1_8_1382400, pbn_b1_8_1382400,
pbn_b2_1_115200, pbn_b2_1_115200,
pbn_b2_8_115200, pbn_b2_8_115200,
pbn_b2_1_460800,
pbn_b2_4_460800, pbn_b2_4_460800,
pbn_b2_8_460800, pbn_b2_8_460800,
pbn_b2_16_460800, pbn_b2_16_460800,
pbn_b2_1_921600,
pbn_b2_4_921600, pbn_b2_4_921600,
pbn_b2_8_921600, pbn_b2_8_921600,
pbn_b2_bt_1_115200, pbn_b2_bt_1_115200,
pbn_b2_bt_2_115200, pbn_b2_bt_2_115200,
pbn_b2_bt_4_115200, pbn_b2_bt_4_115200,
pbn_b2_bt_2_921600, pbn_b2_bt_2_921600,
pbn_b2_bt_4_921600,
pbn_b3_4_115200,
pbn_b3_8_115200,
/*
* Board-specific versions.
*/
pbn_panacom, pbn_panacom,
pbn_panacom2, pbn_panacom2,
pbn_panacom4, pbn_panacom4,
pbn_plx_romulus, pbn_plx_romulus,
pbn_oxsemi, pbn_oxsemi,
pbn_timedia,
pbn_intel_i960, pbn_intel_i960,
pbn_sgi_ioc3, pbn_sgi_ioc3,
pbn_hp_diva,
pbn_nec_nile4, 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_4,
pbn_computone_6, pbn_computone_6,
pbn_computone_8, pbn_computone_8,
}; };
static struct pci_board pci_boards[] __devinitdata = { 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, * Entries following this are board-specific.
* Offset to get to next UART's registers,
* Register shift to use for memory-mapped I/O,
* Initialization function, first UART offset
*/ */
/* Generic serial board, pbn_b0_1_115200, pbn_default */ /*
{ SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, * Panacom - IOMEM
pbn_default */ */
[pbn_panacom] = {
{ SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ .flags = FL_BASE2,
{ SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ .num_ports = 2,
.base_baud = 921600,
{ SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ .uart_offset = 0x400,
{ SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ .reg_shift = 7,
{ SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ },
[pbn_panacom2] = {
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ .flags = FL_BASE2|FL_BASE_BARS,
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ .num_ports = 2,
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */ .base_baud = 921600,
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ .uart_offset = 0x400,
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ .reg_shift = 7,
},
{ SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ [pbn_panacom4] = {
{ SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ .flags = FL_BASE2|FL_BASE_BARS,
{ SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ .num_ports = 4,
{ SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ .base_baud = 921600,
.uart_offset = 0x400,
{ SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ .reg_shift = 7,
{ SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ },
{ SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */
/* I think this entry is broken - the first_offset looks wrong --rmk */
{ SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ [pbn_plx_romulus] = {
{ SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ .flags = FL_BASE2,
{ SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ .num_ports = 4,
.base_baud = 921600,
{ SPCI_FL_BASE2, 1, 115200 }, /* pbn_b2_1_115200 */ .uart_offset = 8 << 2,
{ SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ .reg_shift = 2,
{ SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ .first_offset = 0x03,
{ 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 },
/* /*
* This board uses the size of PCI Base region 0 to * This board uses the size of PCI Base region 0 to
* signal now many ports are available * signal now many ports are available
*/ */
{ SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ [pbn_oxsemi] = {
{ SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ .flags = FL_BASE0|FL_REGION_SZ_CAP,
0, 0, pci_timedia_fn }, .num_ports = 32,
/* EKF addition for i960 Boards form EKF with serial port */ .base_baud = 115200,
{ SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ .uart_offset = 8,
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 */ * 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. * NEC Vrc-5074 (Nile 4) builtin UART.
*/ */
{ SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ [pbn_nec_nile4] = {
64, 3, NULL, 0x300 }, .flags = FL_BASE0,
.num_ports = 1,
{ SPCI_FL_BASE3, 4, 115200, 8 }, /* pbn_dci_pccom4 */ .base_baud = 520833,
{ SPCI_FL_BASE3, 8, 115200, 8 }, /* pbn_dci_pccom8 */ .uart_offset = 8 << 3,
.reg_shift = 3,
{ SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ .first_offset = 0x300,
0, 0, pci_xircom_fn }, },
{ SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ /*
0, 0, pci_siig10x_fn }, * Computone - uses IOMEM.
{ SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ */
0, 0, pci_siig10x_fn }, [pbn_computone_4] = {
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ .flags = FL_BASE0,
0, 0, pci_siig10x_fn }, .num_ports = 4,
{ SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ .base_baud = 921600,
0, 0, pci_siig10x_fn }, .uart_offset = 0x40,
{ SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ .reg_shift = 2,
0, 0, pci_siig20x_fn }, .first_offset = 0x200,
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ },
0, 0, pci_siig20x_fn }, [pbn_computone_6] = {
{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ .flags = FL_BASE0,
0, 0, pci_siig20x_fn }, .num_ports = 6,
.base_baud = 921600,
{ SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ .uart_offset = 0x40,
0x40, 2, NULL, 0x200 }, .reg_shift = 2,
{ SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ .first_offset = 0x200,
0x40, 2, NULL, 0x200 }, },
{ SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ [pbn_computone_8] = {
0x40, 2, NULL, 0x200 }, .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 = { ...@@ -640,8 +1363,7 @@ static struct pci_board pci_boards[] __devinitdata = {
static int __devinit static int __devinit
serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
{ {
int num_iomem = 0, num_port = 0, first_port = -1; int num_iomem, num_port, first_port = -1, i;
int i;
/* /*
* If it is not a communications device or the programming * 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) ...@@ -655,36 +1377,63 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
(dev->class & 0xff) > 6) (dev->class & 0xff) > 6)
return -ENODEV; return -ENODEV;
for (i = 0; i < 6; i++) { num_iomem = num_port = 0;
if (IS_PCI_REGION_IOPORT(dev, i)) { for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
num_port++; num_port++;
if (first_port == -1) if (first_port == -1)
first_port = i; first_port = i;
} }
if (IS_PCI_REGION_IOMEM(dev, i)) if (pci_resource_flags(dev, i) & IORESOURCE_MEM)
num_iomem++; num_iomem++;
} }
/* /*
* If there is 1 or 0 iomem regions, and exactly one port, use * If there is 1 or 0 iomem regions, and exactly one port,
* it. * use it. We guess the number of ports based on the IO
* region size.
*/ */
if (num_iomem <= 1 && num_port == 1) { if (num_iomem <= 1 && num_port == 1) {
board->flags = first_port; board->flags = first_port;
board->num_ports = pci_resource_len(dev, first_port) / 8;
return 0; 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; return -ENODEV;
} }
static inline int static inline int
serial_pci_matches(struct pci_board *board, int index) serial_pci_matches(struct pci_board *board, struct pci_board *guessed)
{ {
return return
board->base_baud == pci_boards[index].base_baud && board->num_ports == guessed->num_ports &&
board->num_ports == pci_boards[index].num_ports && board->base_baud == guessed->base_baud &&
board->uart_offset == pci_boards[index].uart_offset && board->uart_offset == guessed->uart_offset &&
board->reg_shift == pci_boards[index].reg_shift && board->reg_shift == guessed->reg_shift &&
board->first_uart_offset == pci_boards[index].first_uart_offset; board->first_offset == guessed->first_offset;
} }
/* /*
...@@ -692,10 +1441,11 @@ serial_pci_matches(struct pci_board *board, int index) ...@@ -692,10 +1441,11 @@ serial_pci_matches(struct pci_board *board, int index)
* to the arrangement of serial ports on a PCI card. * to the arrangement of serial ports on a PCI card.
*/ */
static int __devinit 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 serial_private *priv;
struct pci_board *board, tmp; struct pci_board *board, tmp;
struct pci_serial_quirk *quirk;
struct serial_struct serial_req; struct serial_struct serial_req;
int base_baud, rc, nr_ports, i; int base_baud, rc, nr_ports, i;
...@@ -732,92 +1482,109 @@ pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) ...@@ -732,92 +1482,109 @@ pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
* detect this boards settings with our heuristic, * detect this boards settings with our heuristic,
* then we no longer need this entry. * then we no longer need this entry.
*/ */
memcpy(&tmp, &pci_boards[pbn_default], sizeof(struct pci_board));
rc = serial_pci_guess_board(dev, &tmp); rc = serial_pci_guess_board(dev, &tmp);
if (rc == 0 && serial_pci_matches(board, pbn_default)) { if (rc == 0 && serial_pci_matches(board, &tmp))
printk(KERN_INFO moan_device("Redundant entry in serial pci_table.",
"Redundant entry in serial pci_table. Please send the output\n" dev);
"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));
}
} }
nr_ports = board->num_ports; nr_ports = board->num_ports;
/* /*
* Run the initialization function, if any. The initialization * Find an init and setup quirks.
* function returns: */
quirk = find_quirk(dev);
/*
* Run the new-style initialization function.
* The initialization function returns:
* <0 - error * <0 - error
* 0 - use board->num_ports * 0 - use board->num_ports
* >0 - number of ports * >0 - number of ports
*/ */
if (board->init_fn) { if (quirk->init) {
rc = board->init_fn(dev, 1); rc = quirk->init(dev);
if (rc < 0) if (rc < 0)
goto disable; goto disable;
if (rc) if (rc)
nr_ports = rc; nr_ports = rc;
} }
priv = kmalloc(sizeof(struct serial_private) + priv = kmalloc(sizeof(struct serial_private) +
sizeof(unsigned int) * nr_ports, sizeof(unsigned int) * nr_ports,
GFP_KERNEL); GFP_KERNEL);
if (!priv) { if (!priv) {
rc = -ENOMEM; rc = -ENOMEM;
goto deinit; 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; 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; base_baud = BASE_BAUD;
memset(&serial_req, 0, sizeof(serial_req)); }
for (i = 0; i < nr_ports; i++) { 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); 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; break;
#ifdef SERIAL_DEBUG_PCI #ifdef SERIAL_DEBUG_PCI
printk("Setup PCI port: port %x, irq %d, type %d\n", printk("Setup PCI port: port %x, irq %d, type %d\n",
serial_req.port, serial_req.irq, serial_req.io_type); serial_req.port, serial_req.irq, serial_req.io_type);
#endif #endif
serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
serial_req.baud_base = base_baud;
priv->line[i] = register_serial(&serial_req); priv->line[i] = register_serial(&serial_req);
if (priv->line[i] < 0) if (priv->line[i] < 0)
break; break;
} }
priv->board = board;
priv->nr = i; priv->nr = i;
pci_set_drvdata(dev, priv);
return 0; return 0;
deinit: deinit:
if (board->init_fn) if (quirk->exit)
board->init_fn(dev, 0); quirk->exit(dev);
disable: disable:
pci_disable_device(dev); pci_disable_device(dev);
return rc; 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); struct serial_private *priv = pci_get_drvdata(dev);
int i;
pci_set_drvdata(dev, NULL); pci_set_drvdata(dev, NULL);
if (priv) { if (priv) {
struct pci_serial_quirk *quirk;
int i;
for (i = 0; i < priv->nr; i++) for (i = 0; i < priv->nr; i++)
unregister_serial(priv->line[i]); unregister_serial(priv->line[i]);
if (priv->board->init_fn) for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
priv->board->init_fn(dev, 0); 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); pci_disable_device(dev);
...@@ -825,6 +1592,53 @@ static void __devexit pci_remove_one(struct pci_dev *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 = { static struct pci_device_id serial_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBVENDOR_ID_CONNECT_TECH,
...@@ -908,7 +1722,9 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -908,7 +1722,9 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_2_921600 }, 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_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_8_921600 }, pbn_b2_8_921600 },
...@@ -949,8 +1765,10 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -949,8 +1765,10 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBVENDOR_ID_CHASE_PCIRAS,
PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0,
pbn_b2_8_460800 }, 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, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
0x10b5, 0x106a, 0, 0, 0x10b5, 0x106a, 0, 0,
pbn_plx_romulus }, pbn_plx_romulus },
...@@ -976,16 +1794,24 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -976,16 +1794,24 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_115200 }, 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_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b1_1_115200 }, 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_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_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_1_921600 }, pbn_b0_1_921600 },
...@@ -999,120 +1825,76 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -999,120 +1825,76 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_4_921600 }, pbn_b0_4_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L,
PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
SPCI_FL_BASE1, 1, 921600 }, pbn_b1_1_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L,
PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, pbn_b1_bt_2_921600 },
/* The 400L and 800L have a custom hack in get_pci_port */
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L,
PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
SPCI_FL_BASE_TABLE, 4, 921600 }, pbn_b0_bt_4_921600 },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L,
PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
SPCI_FL_BASE_TABLE, 8, 921600 }, pbn_b0_bt_8_921600 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_0 }, pbn_b2_1_460800 },
{ 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 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig10x_2 }, pbn_b2_bt_2_921600 },
{ 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 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_0 }, pbn_b0_1_921600 },
{ 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 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_siig20x_2 }, pbn_b0_bt_2_921600 },
{ 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 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
0, 0, pbn_computone_4 }, 0, 0, pbn_computone_4 },
...@@ -1124,18 +1906,22 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -1124,18 +1906,22 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
0, 0, pbn_computone_6 }, 0, 0, pbn_computone_6 },
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, { 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_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, * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
pbn_b0_bt_2_115200 }, */
/* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> */
{ PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_8_115200 }, 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_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_115200 }, pbn_b0_bt_2_115200 },
...@@ -1158,26 +1944,40 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -1158,26 +1944,40 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_1_460800 }, 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_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_bt_2_115200 }, 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, 0xE4BF, PCI_ANY_ID, 0, 0,
pbn_intel_i960 }, pbn_intel_i960 },
/* Xircom Cardbus/Ethernet combos */ /*
* Xircom Cardbus/Ethernet combos
*/
{ PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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... * 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, { PCI_VENDOR_ID_ROCKWELL, 0x1004,
0x1048, 0x1500, 0, 0, 0x1048, 0x1500, 0, 0,
pbn_b1_1_115200 }, pbn_b1_1_115200 },
...@@ -1186,10 +1986,12 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -1186,10 +1986,12 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
0xFF00, 0, 0, 0, 0xFF00, 0, 0, 0,
pbn_sgi_ioc3 }, pbn_sgi_ioc3 },
/* HP Diva card */ /*
* HP Diva card
*/
{ PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_1_115200 }, pbn_b2_1_115200 },
...@@ -1203,15 +2005,14 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -1203,15 +2005,14 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 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_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_dci_pccom8 }, pbn_b3_8_115200 },
/* /*
* These entries match devices with class * These entries match devices with class COMMUNICATION_SERIAL,
* COMMUNICATION_SERIAL, COMMUNICATION_MODEM * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
* or COMMUNICATION_MULTISERIAL
*/ */
{ PCI_ANY_ID, PCI_ANY_ID, { PCI_ANY_ID, PCI_ANY_ID,
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 = { ...@@ -1230,9 +2031,15 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
static struct pci_driver serial_pci_driver = { static struct pci_driver serial_pci_driver = {
.name = "serial", .name = "serial",
.probe = pci_init_one, .probe = pciserial_init_one,
.remove = __devexit_p(pci_remove_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, .id_table = serial_pci_tbl,
.driver = {
.devclass = &tty_devclass,
},
}; };
static int __init serial8250_pci_init(void) static int __init serial8250_pci_init(void)
......
...@@ -21,9 +21,6 @@ ...@@ -21,9 +21,6 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 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/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -31,9 +28,9 @@ ...@@ -31,9 +28,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/pm.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/serial.h> /* for serial_state and serial_icounter_struct */ #include <linux/serial.h> /* for serial_state and serial_icounter_struct */
#include <asm/irq.h> #include <asm/irq.h>
...@@ -46,11 +43,6 @@ ...@@ -46,11 +43,6 @@
#define DPRINTK(x...) do { } while (0) #define DPRINTK(x...) do { } while (0)
#endif #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. * This is used to lock changes in serial line configuration.
*/ */
...@@ -58,8 +50,17 @@ static DECLARE_MUTEX(port_sem); ...@@ -58,8 +50,17 @@ static DECLARE_MUTEX(port_sem);
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) #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_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 * This routine is used by the interrupt handler to schedule processing in
...@@ -73,8 +74,8 @@ void uart_write_wakeup(struct uart_port *port) ...@@ -73,8 +74,8 @@ void uart_write_wakeup(struct uart_port *port)
static void uart_stop(struct tty_struct *tty) static void uart_stop(struct tty_struct *tty)
{ {
struct uart_info *info = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_port *port = info->port; struct uart_port *port = state->port;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&port->lock, flags); spin_lock_irqsave(&port->lock, flags);
...@@ -84,32 +85,31 @@ static void uart_stop(struct tty_struct *tty) ...@@ -84,32 +85,31 @@ static void uart_stop(struct tty_struct *tty)
static void __uart_start(struct tty_struct *tty) 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 = info->port; 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) !tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port, 1); port->ops->start_tx(port, 1);
} }
static void uart_start(struct tty_struct *tty) 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; unsigned long flags;
pm_access(info->state->pm); spin_lock_irqsave(&port->lock, flags);
spin_lock_irqsave(&info->port->lock, flags);
__uart_start(tty); __uart_start(tty);
spin_unlock_irqrestore(&info->port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
} }
static void uart_tasklet_action(unsigned long data) 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; struct tty_struct *tty;
tty = info->tty; tty = state->info->tty;
if (tty) { if (tty) {
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup) tty->ldisc.write_wakeup)
...@@ -137,11 +137,12 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) ...@@ -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 * 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; unsigned long page;
int retval = 0; int retval = 0;
...@@ -182,7 +183,7 @@ static int uart_startup(struct uart_info *info, int init_hw) ...@@ -182,7 +183,7 @@ static int uart_startup(struct uart_info *info, int init_hw)
/* /*
* Initialise the hardware port settings. * Initialise the hardware port settings.
*/ */
uart_change_speed(info, NULL); uart_change_speed(state, NULL);
/* /*
* Setup the RTS and DTR signals once the * Setup the RTS and DTR signals once the
...@@ -194,8 +195,7 @@ static int uart_startup(struct uart_info *info, int init_hw) ...@@ -194,8 +195,7 @@ static int uart_startup(struct uart_info *info, int init_hw)
info->flags |= UIF_INITIALIZED; 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)) if (retval && capable(CAP_SYS_ADMIN))
...@@ -207,11 +207,12 @@ static int uart_startup(struct uart_info *info, int init_hw) ...@@ -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 * 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 * 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)) if (!(info->flags & UIF_INITIALIZED))
return; return;
...@@ -220,7 +221,7 @@ static void uart_shutdown(struct uart_info *info) ...@@ -220,7 +221,7 @@ static void uart_shutdown(struct uart_info *info)
* Turn off DTR and RTS early. * Turn off DTR and RTS early.
*/ */
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) 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 * 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) ...@@ -412,10 +413,10 @@ uart_get_divisor(struct uart_port *port, unsigned int baud)
EXPORT_SYMBOL(uart_get_divisor); EXPORT_SYMBOL(uart_get_divisor);
static void 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 tty_struct *tty = state->info->tty;
struct uart_port *port = info->port; struct uart_port *port = state->port;
struct termios *termios; struct termios *termios;
/* /*
...@@ -431,14 +432,14 @@ uart_change_speed(struct uart_info *info, struct termios *old_termios) ...@@ -431,14 +432,14 @@ uart_change_speed(struct uart_info *info, struct termios *old_termios)
* Set flags based on termios cflag * Set flags based on termios cflag
*/ */
if (termios->c_cflag & CRTSCTS) if (termios->c_cflag & CRTSCTS)
info->flags |= UIF_CTS_FLOW; state->info->flags |= UIF_CTS_FLOW;
else else
info->flags &= ~UIF_CTS_FLOW; state->info->flags &= ~UIF_CTS_FLOW;
if (termios->c_cflag & CLOCAL) if (termios->c_cflag & CLOCAL)
info->flags &= ~UIF_CHECK_CD; state->info->flags &= ~UIF_CHECK_CD;
else else
info->flags |= UIF_CHECK_CD; state->info->flags |= UIF_CHECK_CD;
port->ops->set_termios(port, termios, old_termios); port->ops->set_termios(port, termios, old_termios);
} }
...@@ -526,10 +527,10 @@ __uart_kern_write(struct uart_port *port, struct circ_buf *circ, ...@@ -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) 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) 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) static void uart_flush_chars(struct tty_struct *tty)
...@@ -541,16 +542,16 @@ static int ...@@ -541,16 +542,16 @@ static int
uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf,
int count) int count)
{ {
struct uart_info *info = tty->driver_data; struct uart_state *state = tty->driver_data;
int ret; int ret;
if (!tty || !info->xmit.buf) if (!tty || !state->info->xmit.buf)
return 0; return 0;
if (from_user) 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 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); uart_start(tty);
return ret; return ret;
...@@ -558,29 +559,30 @@ uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, ...@@ -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) 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) 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) 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; unsigned long flags;
DPRINTK("uart_flush_buffer(%d) called\n", DPRINTK("uart_flush_buffer(%d) called\n",
minor(tty->device) - tty->driver.minor_start); minor(tty->device) - tty->driver.minor_start);
spin_lock_irqsave(&info->port->lock, flags); spin_lock_irqsave(&port->lock, flags);
uart_circ_clear(&info->xmit); uart_circ_clear(&state->info->xmit);
spin_unlock_irqrestore(&info->port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup) tty->ldisc.write_wakeup)
...@@ -593,8 +595,8 @@ static void uart_flush_buffer(struct tty_struct *tty) ...@@ -593,8 +595,8 @@ static void uart_flush_buffer(struct tty_struct *tty)
*/ */
static void uart_send_xchar(struct tty_struct *tty, char ch) static void uart_send_xchar(struct tty_struct *tty, char ch)
{ {
struct uart_info *info = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_port *port = info->port; struct uart_port *port = state->port;
unsigned long flags; unsigned long flags;
if (port->ops->send_xchar) if (port->ops->send_xchar)
...@@ -611,19 +613,19 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) ...@@ -611,19 +613,19 @@ static void uart_send_xchar(struct tty_struct *tty, char ch)
static void uart_throttle(struct tty_struct *tty) 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)) if (I_IXOFF(tty))
uart_send_xchar(tty, STOP_CHAR(tty)); uart_send_xchar(tty, STOP_CHAR(tty));
if (tty->termios->c_cflag & CRTSCTS) 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) static void uart_unthrottle(struct tty_struct *tty)
{ {
struct uart_info *info = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_port *port = info->port; struct uart_port *port = state->port;
if (I_IXOFF(tty)) { if (I_IXOFF(tty)) {
if (port->x_char) if (port->x_char)
...@@ -636,10 +638,9 @@ static void uart_unthrottle(struct tty_struct *tty) ...@@ -636,10 +638,9 @@ static void uart_unthrottle(struct tty_struct *tty)
uart_set_mctrl(port, TIOCM_RTS); 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 = state->port;
struct uart_port *port = info->port;
struct serial_struct tmp; struct serial_struct tmp;
memset(&tmp, 0, sizeof(tmp)); memset(&tmp, 0, sizeof(tmp));
...@@ -649,7 +650,7 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) ...@@ -649,7 +650,7 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo)
if (HIGH_BITS_OFFSET) if (HIGH_BITS_OFFSET)
tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET;
tmp.irq = port->irq; tmp.irq = port->irq;
tmp.flags = port->flags | info->flags; tmp.flags = port->flags;
tmp.xmit_fifo_size = port->fifosize; tmp.xmit_fifo_size = port->fifosize;
tmp.baud_base = port->uartclk / 16; tmp.baud_base = port->uartclk / 16;
tmp.close_delay = state->close_delay; tmp.close_delay = state->close_delay;
...@@ -666,11 +667,10 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) ...@@ -666,11 +667,10 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo)
} }
static int 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 serial_struct new_serial;
struct uart_state *state = info->state; struct uart_port *port = state->port;
struct uart_port *port = info->port;
unsigned long new_port; unsigned long new_port;
unsigned int change_irq, change_port, old_flags; unsigned int change_irq, change_port, old_flags;
unsigned int old_custom_divisor; unsigned int old_custom_divisor;
...@@ -692,7 +692,7 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) ...@@ -692,7 +692,7 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo)
* module insertion/removal doesn't change anything * module insertion/removal doesn't change anything
* under us. * under us.
*/ */
down(&port_sem); down(&state->sem);
change_irq = new_serial.irq != port->irq; change_irq = new_serial.irq != port->irq;
...@@ -745,14 +745,14 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) ...@@ -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. * 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; goto exit;
/* /*
* We need to shutdown the serial port at the old * We need to shutdown the serial port at the old
* port/type/irq combination. * port/type/irq combination.
*/ */
uart_shutdown(info); uart_shutdown(state);
} }
if (change_port) { if (change_port) {
...@@ -813,18 +813,21 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) ...@@ -813,18 +813,21 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo)
port->irq = new_serial.irq; port->irq = new_serial.irq;
port->uartclk = new_serial.baud_base * 16; 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; port->custom_divisor = new_serial.custom_divisor;
state->close_delay = new_serial.close_delay * HZ / 100; state->close_delay = new_serial.close_delay * HZ / 100;
state->closing_wait = new_serial.closing_wait * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100;
port->fifosize = new_serial.xmit_fifo_size; 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: check_and_exit:
retval = 0; retval = 0;
if (port->type == PORT_UNKNOWN) if (port->type == PORT_UNKNOWN)
goto exit; goto exit;
if (info->flags & UIF_INITIALIZED) { if (state->info->flags & UIF_INITIALIZED) {
if (((old_flags ^ port->flags) & UPF_SPD_MASK) || if (((old_flags ^ port->flags) & UPF_SPD_MASK) ||
old_custom_divisor != port->custom_divisor) { old_custom_divisor != port->custom_divisor) {
/* If they're setting up a custom divisor or speed, /* 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) ...@@ -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. */ * need to rate-limit; it's CAP_SYS_ADMIN only. */
if (port->flags & UPF_SPD_MASK) { if (port->flags & UPF_SPD_MASK) {
printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n", printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n",
current->comm, info->tty->driver.name, current->comm, state->info->tty->driver.name,
info->port->line); state->port->line);
} }
uart_change_speed(info, NULL); uart_change_speed(state, NULL);
} }
} else } else
retval = uart_startup(info, 1); retval = uart_startup(state, 1);
exit: exit:
up(&port_sem); up(&state->sem);
return retval; 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; unsigned int result;
result = port->ops->tx_empty(port); result = port->ops->tx_empty(port);
...@@ -861,9 +865,9 @@ static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) ...@@ -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 * avoid a race condition (depending on when the transmit
* interrupt happens). * interrupt happens).
*/ */
if (info->port->x_char || if (port->x_char ||
((uart_circ_chars_pending(&info->xmit) > 0) && ((uart_circ_chars_pending(&state->info->xmit) > 0) &&
!info->tty->stopped && !info->tty->hw_stopped)) !state->info->tty->stopped && !state->info->tty->hw_stopped))
result &= ~TIOCSER_TEMT; result &= ~TIOCSER_TEMT;
return put_user(result, value); return put_user(result, value);
...@@ -888,6 +892,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, ...@@ -888,6 +892,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd,
if (get_user(arg, value)) if (get_user(arg, value))
return -EFAULT; return -EFAULT;
arg &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2;
set = clear = 0; set = clear = 0;
switch (cmd) { switch (cmd) {
case TIOCMBIS: case TIOCMBIS:
...@@ -911,8 +917,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, ...@@ -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) static void uart_break_ctl(struct tty_struct *tty, int break_state)
{ {
struct uart_info *info = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_port *port = info->port; struct uart_port *port = state->port;
BUG_ON(!kernel_locked()); BUG_ON(!kernel_locked());
...@@ -920,25 +926,25 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) ...@@ -920,25 +926,25 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state)
port->ops->break_ctl(port, 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; int flags, ret;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
/* /*
* Take the 'count' lock. This prevents count * Take the per-port semaphore. This prevents count from
* from incrementing, and hence any extra opens * changing, and hence any extra opens of the port while
* of the port while we're auto-configging. * we're auto-configuring.
*/ */
if (down_interruptible(&port_sem)) if (down_interruptible(&state->sem))
return -ERESTARTSYS; return -ERESTARTSYS;
ret = -EBUSY; ret = -EBUSY;
if (info->state->count == 1 && info->blocked_open == 0) { if (uart_users(state) == 1) {
uart_shutdown(info); uart_shutdown(state);
/* /*
* If we already have a port type configured, * If we already have a port type configured,
...@@ -957,16 +963,22 @@ static int uart_do_autoconfig(struct uart_info *info) ...@@ -957,16 +963,22 @@ static int uart_do_autoconfig(struct uart_info *info)
*/ */
port->ops->config_port(port, flags); port->ops->config_port(port, flags);
ret = uart_startup(info, 1); ret = uart_startup(state, 1);
} }
up(&port_sem); up(&state->sem);
return ret; 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 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); DECLARE_WAITQUEUE(wait, current);
struct uart_icount cprev, cnow; struct uart_icount cprev, cnow;
int ret; int ret;
...@@ -983,7 +995,7 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) ...@@ -983,7 +995,7 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg)
port->ops->enable_ms(port); port->ops->enable_ms(port);
spin_unlock_irq(&port->lock); spin_unlock_irq(&port->lock);
add_wait_queue(&info->delta_msr_wait, &wait); add_wait_queue(&state->info->delta_msr_wait, &wait);
for (;;) { for (;;) {
spin_lock_irq(&port->lock); spin_lock_irq(&port->lock);
memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); memcpy(&cnow, &port->icount, sizeof(struct uart_icount));
...@@ -1011,117 +1023,144 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) ...@@ -1011,117 +1023,144 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg)
} }
current->state = TASK_RUNNING; current->state = TASK_RUNNING;
remove_wait_queue(&info->delta_msr_wait, &wait); remove_wait_queue(&state->info->delta_msr_wait, &wait);
return ret; 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 static int
uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt)
unsigned long arg)
{ {
struct uart_info *info = tty->driver_data;
struct serial_icounter_struct icount; struct serial_icounter_struct icount;
struct uart_icount cnow; 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; int ret = -ENOIOCTLCMD;
BUG_ON(!kernel_locked()); BUG_ON(!kernel_locked());
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && /*
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && * These ioctls don't rely on the hardware to be present.
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { */
if (tty->flags & (1 << TTY_IO_ERROR)) switch (cmd) {
return -EIO; 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) { if (ret != -ENOIOCTLCMD)
case TIOCMGET: goto out;
ret = uart_get_modem_info(info->port,
(unsigned int *)arg);
break;
case TIOCMBIS: if (tty->flags & (1 << TTY_IO_ERROR)) {
case TIOCMBIC: ret = -EIO;
case TIOCMSET: goto out;
ret = uart_set_modem_info(info->port, cmd, }
(unsigned int *)arg);
break;
case TIOCGSERIAL: /*
ret = uart_get_info(info, (struct serial_struct *)arg); * The following should only be used when hardware is present.
break; */
switch (cmd) {
case TIOCMIWAIT:
ret = uart_wait_modem_status(state, arg);
break;
case TIOCSSERIAL: case TIOCGICOUNT:
ret = uart_set_info(info, (struct serial_struct *)arg); ret = uart_get_count(state, (struct serial_icounter_struct *)arg);
break; break;
}
case TIOCSERCONFIG: if (ret != -ENOIOCTLCMD)
ret = uart_do_autoconfig(info); goto out;
break;
case TIOCSERGETLSR: /* Get line status register */ down(&state->sem);
ret = uart_get_lsr_info(info, (unsigned int *)arg);
break;
/* if (tty_hung_up_p(filp)) {
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change ret = -EIO;
* - mask passed in arg for lines of interest goto out_up;
* (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;
/* /*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * All these rely on hardware being present and need to be
* Return: write counters to the user passed counter struct * protected against the tty being hung up.
* NB: both 1->0 and 0->1 transitions are counted except for */
* RI where only 0->1 is counted. switch (cmd) {
*/ case TIOCMGET:
case TIOCGICOUNT: ret = uart_get_modem_info(state->port, (unsigned int *)arg);
spin_lock_irq(&info->port->lock); break;
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;
case TIOCSERGWILD: /* obsolete */ case TIOCMBIS:
case TIOCSERSWILD: /* obsolete */ case TIOCMBIC:
ret = 0; case TIOCMSET:
break; ret = uart_set_modem_info(state->port, cmd,
(unsigned int *)arg);
break;
default: { case TIOCSERGETLSR: /* Get line status register */
struct uart_port *port = info->port; ret = uart_get_lsr_info(state, (unsigned int *)arg);
if (port->ops->ioctl) break;
ret = port->ops->ioctl(port, cmd, 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; return ret;
} }
static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) 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 long flags;
unsigned int cflag = tty->termios->c_cflag; unsigned int cflag = tty->termios->c_cflag;
...@@ -1137,11 +1176,11 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios ...@@ -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) RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
return; return;
uart_change_speed(info, old_termios); uart_change_speed(state, old_termios);
/* Handle transition to B0 status */ /* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) 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 */ /* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { 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 ...@@ -1149,15 +1188,15 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios
if (!(cflag & CRTSCTS) || if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags)) !test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS; mask |= TIOCM_RTS;
uart_set_mctrl(info->port, mask); uart_set_mctrl(state->port, mask);
} }
/* Handle turning off CRTSCTS */ /* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & 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; tty->hw_stopped = 0;
__uart_start(tty); __uart_start(tty);
spin_unlock_irqrestore(&info->port->lock, flags); spin_unlock_irqrestore(&state->port->lock, flags);
} }
#if 0 #if 0
...@@ -1169,7 +1208,7 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios ...@@ -1169,7 +1208,7 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios
*/ */
if (!(old_termios->c_cflag & CLOCAL) && if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL)) (tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&info->open_wait); wake_up_interruptible(&state->info->open_wait);
#endif #endif
} }
...@@ -1180,29 +1219,17 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios ...@@ -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) static void uart_close(struct tty_struct *tty, struct file *filp)
{ {
struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; struct uart_state *state = tty->driver_data;
struct uart_info *info = tty->driver_data; struct uart_port *port = state->port;
struct uart_port *port = info->port;
struct uart_state *state;
unsigned long flags;
BUG_ON(!kernel_locked()); BUG_ON(!kernel_locked());
DPRINTK("uart_close(%d) called\n", port->line);
if (!info) down(&state->sem);
return;
state = info->state;
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)) if (tty_hung_up_p(filp))
goto done; goto done;
spin_lock_irqsave(&info->port->lock, flags);
if ((tty->count == 1) && (state->count != 1)) { if ((tty->count == 1) && (state->count != 1)) {
/* /*
* Uh, oh. tty->count is 1, which means that the tty * 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) ...@@ -1217,34 +1244,28 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
} }
if (--state->count < 0) { if (--state->count < 0) {
printk("rs_close: bad serial port count for %s%d: %d\n", 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; state->count = 0;
} }
if (state->count) { if (state->count)
spin_unlock_irqrestore(&info->port->lock, flags);
goto done; 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 * 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; 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 * At this point, we stop accepting input. To do this, we
* disable the receive line status interrupts. * 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); spin_lock_irqsave(&port->lock, flags);
port->ops->stop_rx(port); port->ops->stop_rx(port);
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
...@@ -1255,46 +1276,38 @@ static void uart_close(struct tty_struct *tty, struct file *filp) ...@@ -1255,46 +1276,38 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
*/ */
uart_wait_until_sent(tty, port->timeout); uart_wait_until_sent(tty, port->timeout);
} }
down(&port_sem);
uart_shutdown(info); uart_shutdown(state);
up(&port_sem);
uart_flush_buffer(tty); uart_flush_buffer(tty);
if (tty->ldisc.flush_buffer) if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty); tty->ldisc.flush_buffer(tty);
tty->closing = 0; tty->closing = 0;
info->tty = NULL; state->info->tty = NULL;
if (info->blocked_open) {
if (info->state->close_delay) { if (state->info->blocked_open) {
if (state->close_delay) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(info->state->close_delay); schedule_timeout(state->close_delay);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
} }
} else { } else if (!uart_console(port)) {
#ifdef CONFIG_PM uart_change_pm(state, 3);
/*
* 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
} }
/* /*
* Wake up anyone trying to open this port. * Wake up anyone trying to open this port.
*/ */
info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING); state->info->flags &= ~UIF_NORMAL_ACTIVE;
wake_up_interruptible(&info->open_wait); wake_up_interruptible(&state->info->open_wait);
done: done:
module_put(drv->owner); up(&state->sem);
} }
static void uart_wait_until_sent(struct tty_struct *tty, int timeout) static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
{ {
struct uart_info *info = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_port *port = info->port; struct uart_port *port = state->port;
unsigned long char_time, expire; unsigned long char_time, expire;
BUG_ON(!kernel_locked()); BUG_ON(!kernel_locked());
...@@ -1358,23 +1371,22 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) ...@@ -1358,23 +1371,22 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
*/ */
static void uart_hangup(struct tty_struct *tty) static void uart_hangup(struct tty_struct *tty)
{ {
struct uart_info *info = tty->driver_data; struct uart_state *state = tty->driver_data;
struct uart_state *state = info->state;
BUG_ON(!kernel_locked()); BUG_ON(!kernel_locked());
DPRINTK("uart_hangup(%d)\n", state->port->line);
uart_flush_buffer(tty); down(&state->sem);
down(&port_sem); if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) {
if (info->flags & UIF_CLOSING) { uart_flush_buffer(tty);
up(&port_sem); uart_shutdown(state);
return; 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); up(&state->sem);
state->count = 0;
info->flags &= ~UIF_NORMAL_ACTIVE;
info->tty = NULL;
up(&port_sem);
wake_up_interruptible(&info->open_wait);
} }
/* /*
...@@ -1383,18 +1395,15 @@ static void uart_hangup(struct tty_struct *tty) ...@@ -1383,18 +1395,15 @@ static void uart_hangup(struct tty_struct *tty)
* kernel settings, and the settings init adopts when it opens the port * kernel settings, and the settings init adopts when it opens the port
* for the first time. * 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; struct tty_struct *tty = state->info->tty;
struct uart_port *port = state->port;
#ifdef CONFIG_SERIAL_CORE_CONSOLE
struct console *c = info->port->cons;
if (c && c->cflag && c->index == info->port->line) { if (uart_console(port) && port->cons->cflag) {
tty->termios->c_cflag = c->cflag; tty->termios->c_cflag = port->cons->cflag;
c->cflag = 0; port->cons->cflag = 0;
} }
#endif
/* /*
* If the device failed to grab its irq resources, * If the device failed to grab its irq resources,
...@@ -1405,22 +1414,26 @@ static void uart_update_termios(struct uart_info *info) ...@@ -1405,22 +1414,26 @@ static void uart_update_termios(struct uart_info *info)
/* /*
* Make termios settings take effect. * Make termios settings take effect.
*/ */
uart_change_speed(info, NULL); uart_change_speed(state, NULL);
/* /*
* And finally enable the RTS and DTR signals. * And finally enable the RTS and DTR signals.
*/ */
if (tty->termios->c_cflag & CBAUD) 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 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); DECLARE_WAITQUEUE(wait, current);
struct uart_state *state = info->state; struct uart_info *info = state->info;
struct uart_port *port = info->port; struct uart_port *port = state->port;
info->blocked_open++; info->blocked_open++;
state->count--; state->count--;
...@@ -1432,17 +1445,9 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) ...@@ -1432,17 +1445,9 @@ uart_block_til_ready(struct file *filp, struct uart_info *info)
/* /*
* If we have been hung up, tell userspace/restart open. * 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; 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 the port has been closed, tell userspace/restart open.
*/ */
...@@ -1470,7 +1475,7 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) ...@@ -1470,7 +1475,7 @@ uart_block_til_ready(struct file *filp, struct uart_info *info)
* the data from the modem. * the data from the modem.
*/ */
if (info->tty->termios->c_cflag & CBAUD) 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 * and wait for the carrier to indicate that the
...@@ -1479,8 +1484,9 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) ...@@ -1479,8 +1484,9 @@ uart_block_til_ready(struct file *filp, struct uart_info *info)
if (port->ops->get_mctrl(port) & TIOCM_CAR) if (port->ops->get_mctrl(port) & TIOCM_CAR)
break; break;
wait: up(&state->sem);
schedule(); schedule();
down(&state->sem);
if (signal_pending(current)) if (signal_pending(current))
break; break;
...@@ -1494,52 +1500,56 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) ...@@ -1494,52 +1500,56 @@ uart_block_til_ready(struct file *filp, struct uart_info *info)
if (signal_pending(current)) if (signal_pending(current))
return -ERESTARTSYS; return -ERESTARTSYS;
if (info->tty->flags & (1 << TTY_IO_ERROR)) if (!info->tty || tty_hung_up_p(filp))
return 0;
if (tty_hung_up_p(filp) || !(info->flags & UIF_INITIALIZED))
return (port->flags & UPF_HUP_NOTIFY) ? return (port->flags & UPF_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS; -EAGAIN : -ERESTARTSYS;
return 0; 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_state *state;
struct uart_info *info = NULL;
down(&port_sem); down(&port_sem);
if (!state->port) state = drv->state + line;
if (down_interruptible(&state->sem)) {
state = ERR_PTR(-ERESTARTSYS);
goto out; goto out;
}
state->count++; state->count++;
info = state->info; if (!state->port) {
state->count--;
up(&state->sem);
state = ERR_PTR(-ENXIO);
goto out;
}
if (!info) { if (!state->info) {
info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL);
if (info) { if (state->info) {
memset(info, 0, sizeof(struct uart_info)); memset(state->info, 0, sizeof(struct uart_info));
init_waitqueue_head(&info->open_wait); init_waitqueue_head(&state->info->open_wait);
init_waitqueue_head(&info->delta_msr_wait); init_waitqueue_head(&state->info->delta_msr_wait);
/* /*
* Link the info into the other structures. * Link the info into the other structures.
*/ */
info->port = state->port; state->port->info = state->info;
info->state = state;
state->port->info = info; tasklet_init(&state->info->tlet, uart_tasklet_action,
(unsigned long)state);
tasklet_init(&info->tlet, uart_tasklet_action, } else {
(unsigned long)info);
state->info = info;
} else
state->count--; state->count--;
up(&state->sem);
state = ERR_PTR(-ENOMEM);
}
} }
out: out:
up(&port_sem); up(&port_sem);
return info; return state;
} }
/* /*
...@@ -1555,11 +1565,10 @@ static struct uart_info *uart_get(struct uart_driver *drv, int line) ...@@ -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) static int uart_open(struct tty_struct *tty, struct file *filp)
{ {
struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; 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; int retval, line = minor(tty->device) - tty->driver.minor_start;
BUG_ON(!kernel_locked()); BUG_ON(!kernel_locked());
DPRINTK("uart_open(%d) called\n", line); DPRINTK("uart_open(%d) called\n", line);
/* /*
...@@ -1572,92 +1581,66 @@ static int uart_open(struct tty_struct *tty, struct file *filp) ...@@ -1572,92 +1581,66 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
goto fail; goto fail;
/* /*
* If we fail to increment the module use count, we can't have * We take the semaphore inside uart_get to guarantee that we won't
* any other users of this tty (since this implies that the module * be re-entered while allocating the info structure, or while we
* is about to be unloaded). Therefore, it is safe to set * request any IRQs that the driver may need. This also has the nice
* tty->driver_data to be NULL, so uart_close() doesn't bite us. * 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)) { state = uart_get(drv, line);
tty->driver_data = NULL; if (IS_ERR(state)) {
retval = PTR_ERR(state);
goto fail; 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 * Once we set tty->driver_data here, we are guaranteed that
* uart_close() will decrement the driver module use count. * uart_close() will decrement the driver module use count.
* Any failures from here onwards should not touch the count. * Any failures from here onwards should not touch the count.
*/ */
tty->driver_data = info; tty->driver_data = state;
tty->low_latency = (info->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 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 the port is in the middle of closing, bail out now.
*/ */
if (tty_hung_up_p(filp) || (info->flags & UIF_CLOSING)) { if (tty_hung_up_p(filp)) {
wait_event_interruptible(info->open_wait, retval = (state->port->flags & UPF_HUP_NOTIFY) ?
!(info->flags & UIF_CLOSING));
retval = (info->port->flags & UPF_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS; -EAGAIN : -ERESTARTSYS;
state->count--;
up(&state->sem);
goto fail; goto fail;
} }
/* /*
* Make sure the device is in D0 state. * Make sure the device is in D0 state.
*/ */
if (info->state->count == 1) { if (state->count == 1)
#ifdef CONFIG_PM uart_change_pm(state, 0);
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
}
/* /*
* Start up the serial port. We have this semaphore here to * Start up the serial port.
* prevent uart_startup or uart_shutdown being re-entered if
* we sleep while requesting an IRQ.
*/ */
down(&port_sem); retval = uart_startup(state, 0);
retval = uart_startup(info, 0);
up(&port_sem);
if (retval)
goto fail;
/* /*
* 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 this is the first open to succeed, adjust things to suit.
*/ */
if (retval == 0 && !(info->flags & UIF_NORMAL_ACTIVE)) { if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) {
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: fail:
return retval; return retval;
} }
...@@ -1892,115 +1875,98 @@ uart_set_options(struct uart_port *port, struct console *co, ...@@ -1892,115 +1875,98 @@ uart_set_options(struct uart_port *port, struct console *co,
} }
#endif /* CONFIG_SERIAL_CORE_CONSOLE */ #endif /* CONFIG_SERIAL_CORE_CONSOLE */
#ifdef CONFIG_PM static void uart_change_pm(struct uart_state *state, int pm_state)
/*
* 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)
{ {
struct uart_port *port; struct uart_port *port = state->port;
struct uart_ops *ops; if (port->ops->pm)
int running = state->info && port->ops->pm(port, pm_state, state->pm_state);
state->info->flags & UIF_INITIALIZED; 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) { down(&state->sem);
up(&port_sem);
return 0;
}
port = state->port; switch (level) {
ops = port->ops; 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", spin_lock_irq(&port->lock);
port->iobase, dev->state, pm_state, running ? "" : "not "); 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 - * Wait for the transmitter to empty.
* the port isn't initialised.
*/ */
ops->set_mctrl(port, 0); while (!ops->tx_empty(port)) {
ops->startup(port); set_current_state(TASK_UNINTERRUPTIBLE);
uart_change_speed(state->info, NULL); schedule_timeout(10*HZ/1000);
spin_lock_irq(&port->lock); }
ops->set_mctrl(port, port->mctrl); set_current_state(TASK_RUNNING);
ops->start_tx(port, 0);
spin_unlock_irq(&port->lock); ops->shutdown(port);
} }
break;
/* case SUSPEND_POWER_DOWN:
* 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 {
/* /*
* Disable the console device before suspending. * Disable the console device before suspending.
*/ */
if (port->cons && port->cons->index == port->line) if (uart_console(port))
port->cons->flags &= ~CON_ENABLED; port->cons->flags &= ~CON_ENABLED;
if (running) { uart_change_pm(state, 3);
spin_lock_irq(&port->lock); break;
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);
} }
up(&port_sem);
up(&state->sem);
return 0; return 0;
} }
/* int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level)
* Wakeup support.
*/
static int uart_pm_set_wakeup(struct uart_state *state, int data)
{ {
int err = 0; struct uart_state *state = drv->state + port->line;
if (state->port->ops->set_wake)
err = state->port->ops->set_wake(state->port, data);
return err; down(&state->sem);
}
static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data) switch (level) {
{ case RESUME_POWER_ON:
struct uart_state *state = dev->data; uart_change_pm(state, 0);
int err = 0;
switch (rqst) { /*
case PM_SUSPEND: * Re-enable the console device after suspending.
case PM_RESUME: */
err = uart_pm_set_state(state, (int)(long)data, dev->state); if (uart_console(port)) {
uart_change_speed(state, NULL);
port->cons->flags |= CON_ENABLED;
}
break; break;
case PM_SET_WAKEUP: case RESUME_RESTORE_STATE:
err = uart_pm_set_wakeup(state, (int)(long)data); 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; break;
} }
return err;
up(&state->sem);
return 0;
} }
#endif
static inline void static inline void
uart_report_port(struct uart_driver *drv, struct uart_port *port) 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) ...@@ -2022,17 +1988,11 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
} }
static void static void
__uart_register_port(struct uart_driver *drv, struct uart_state *state, uart_configure_port(struct uart_driver *drv, struct uart_state *state,
struct uart_port *port) struct uart_port *port)
{ {
unsigned int flags; 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. * 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, ...@@ -2051,12 +2011,6 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state,
port->ops->config_port(port, flags); 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) { if (port->type != PORT_UNKNOWN) {
unsigned long flags; unsigned long flags;
...@@ -2070,50 +2024,32 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, ...@@ -2070,50 +2024,32 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state,
port->ops->set_mctrl(port, 0); port->ops->set_mctrl(port, 0);
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
#ifdef CONFIG_PM
/* /*
* Power down all ports by default, except the * Power down all ports by default, except the
* console if we have one. We need to drop the * console if we have one.
* port semaphore here.
*/ */
if (state->pm && (!drv->cons || port->line != drv->cons->index)) { if (!uart_console(port))
up(&port_sem); uart_change_pm(state, 3);
pm_send(state->pm, PM_SUSPEND, (void *)3);
down(&port_sem);
}
#endif
} }
} }
/* /*
* Hangup the port. This must be done outside the port_sem * This reverses the affects of uart_configure_port, hanging up the
* since uart_hangup() grabs this same semaphore. Grr. * port before removal.
*/ */
static void 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; struct uart_info *info = state->info;
if (info && info->tty) if (info && info->tty)
tty_vhangup(info->tty); tty_vhangup(info->tty);
}
/* down(&state->sem);
* 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;
state->info = NULL; 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. * Free the port IO and memory resources, if any.
*/ */
...@@ -2132,6 +2068,8 @@ __uart_unregister_port(struct uart_driver *drv, struct uart_state *state) ...@@ -2132,6 +2068,8 @@ __uart_unregister_port(struct uart_driver *drv, struct uart_state *state)
tasklet_kill(&info->tlet); tasklet_kill(&info->tlet);
kfree(info); kfree(info);
} }
up(&state->sem);
} }
/** /**
...@@ -2186,6 +2124,7 @@ int uart_register_driver(struct uart_driver *drv) ...@@ -2186,6 +2124,7 @@ int uart_register_driver(struct uart_driver *drv)
drv->tty_driver = normal; drv->tty_driver = normal;
normal->magic = TTY_DRIVER_MAGIC; normal->magic = TTY_DRIVER_MAGIC;
normal->owner = drv->owner;
normal->driver_name = drv->driver_name; normal->driver_name = drv->driver_name;
normal->name = drv->dev_name; normal->name = drv->dev_name;
normal->major = drv->major; normal->major = drv->major;
...@@ -2232,20 +2171,13 @@ int uart_register_driver(struct uart_driver *drv) ...@@ -2232,20 +2171,13 @@ int uart_register_driver(struct uart_driver *drv)
state->close_delay = 5 * HZ / 10; state->close_delay = 5 * HZ / 10;
state->closing_wait = 30 * HZ; state->closing_wait = 30 * HZ;
#ifdef CONFIG_PM
state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); init_MUTEX(&state->sem);
if (state->pm)
state->pm->data = state;
#endif
} }
retval = tty_register_driver(normal); retval = tty_register_driver(normal);
out: out:
if (retval < 0) { if (retval < 0) {
#ifdef CONFIG_PM
for (i = 0; i < drv->nr; i++)
pm_unregister(drv->state[i].pm);
#endif
kfree(normal); kfree(normal);
kfree(drv->state); kfree(drv->state);
kfree(termios); kfree(termios);
...@@ -2264,11 +2196,6 @@ int uart_register_driver(struct uart_driver *drv) ...@@ -2264,11 +2196,6 @@ int uart_register_driver(struct uart_driver *drv)
*/ */
void uart_unregister_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); tty_unregister_driver(drv->tty_driver);
kfree(drv->state); kfree(drv->state);
...@@ -2289,6 +2216,7 @@ void uart_unregister_driver(struct uart_driver *drv) ...@@ -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) int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{ {
struct uart_state *state; struct uart_state *state;
int ret = 0;
BUG_ON(in_interrupt()); BUG_ON(in_interrupt());
...@@ -2298,10 +2226,29 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) ...@@ -2298,10 +2226,29 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
state = drv->state + port->line; state = drv->state + port->line;
down(&port_sem); 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); up(&port_sem);
return 0; return ret;
} }
/** /**
...@@ -2323,10 +2270,14 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) ...@@ -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", printk(KERN_ALERT "Removing wrong port: %p != %p\n",
state->port, port); state->port, port);
__uart_hangup_port(drv, state);
down(&port_sem); 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; state->port = NULL;
up(&port_sem); up(&port_sem);
...@@ -2426,8 +2377,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) ...@@ -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 * alter it underneath itself - the port may be open and
* trying to do useful work. * trying to do useful work.
*/ */
if (state->count != 0 || if (uart_users(state) != 0) {
(state->info && state->info->blocked_open != 0)) {
ret = -EBUSY; ret = -EBUSY;
goto out; goto out;
} }
...@@ -2447,7 +2397,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) ...@@ -2447,7 +2397,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port)
state->port->line = state - drv->state; state->port->line = state - drv->state;
state->port->mapbase = port->mapbase; state->port->mapbase = port->mapbase;
__uart_register_port(drv, state, state->port); uart_configure_port(drv, state, state->port);
} }
ret = state->port->line; ret = state->port->line;
...@@ -2479,16 +2429,16 @@ void uart_unregister_port(struct uart_driver *drv, int line) ...@@ -2479,16 +2429,16 @@ void uart_unregister_port(struct uart_driver *drv, int line)
state = drv->state + line; state = drv->state + line;
__uart_hangup_port(drv, state);
down(&port_sem); down(&port_sem);
__uart_unregister_port(drv, state); uart_unconfigure_port(drv, state);
up(&port_sem); up(&port_sem);
} }
EXPORT_SYMBOL(uart_write_wakeup); EXPORT_SYMBOL(uart_write_wakeup);
EXPORT_SYMBOL(uart_register_driver); EXPORT_SYMBOL(uart_register_driver);
EXPORT_SYMBOL(uart_unregister_driver); EXPORT_SYMBOL(uart_unregister_driver);
EXPORT_SYMBOL(uart_suspend_port);
EXPORT_SYMBOL(uart_resume_port);
EXPORT_SYMBOL(uart_register_port); EXPORT_SYMBOL(uart_register_port);
EXPORT_SYMBOL(uart_unregister_port); EXPORT_SYMBOL(uart_unregister_port);
EXPORT_SYMBOL(uart_add_one_port); EXPORT_SYMBOL(uart_add_one_port);
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/sysrq.h> #include <linux/sysrq.h>
#include <linux/device.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -857,12 +858,54 @@ static struct uart_driver sa1100_reg = { ...@@ -857,12 +858,54 @@ static struct uart_driver sa1100_reg = {
.cons = SA1100_CONSOLE, .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) static int __init sa1100_serial_init(void)
{ {
int ret; int ret;
printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.50 $\n"); printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.50 $\n");
driver_register(&sa11x0_serial_driver);
sys_device_register(&sa11x0_serial_devs);
sa1100_init_ports(); sa1100_init_ports();
ret = uart_register_driver(&sa1100_reg); ret = uart_register_driver(&sa1100_reg);
if (ret == 0) { if (ret == 0) {
......
...@@ -1235,6 +1235,7 @@ ...@@ -1235,6 +1235,7 @@
#define PCI_VENDOR_ID_XIRCOM 0x115d #define PCI_VENDOR_ID_XIRCOM 0x115d
#define PCI_DEVICE_ID_XIRCOM_X3201_ETH 0x0003 #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_DEVICE_ID_XIRCOM_X3201_MDM 0x0103
#define PCI_VENDOR_ID_RENDITION 0x1163 #define PCI_VENDOR_ID_RENDITION 0x1163
......
...@@ -208,12 +208,11 @@ struct uart_state { ...@@ -208,12 +208,11 @@ struct uart_state {
#define USF_CLOSING_WAIT_NONE (65535) #define USF_CLOSING_WAIT_NONE (65535)
int count; int count;
int pm_state;
struct uart_info *info; struct uart_info *info;
struct uart_port *port; struct uart_port *port;
#ifdef CONFIG_PM struct semaphore sem;
struct pm_dev *pm;
#endif
}; };
#define UART_XMIT_SIZE 1024 #define UART_XMIT_SIZE 1024
...@@ -224,8 +223,6 @@ struct uart_state { ...@@ -224,8 +223,6 @@ struct uart_state {
* stuff here. * stuff here.
*/ */
struct uart_info { struct uart_info {
struct uart_port *port;
struct uart_state *state;
struct tty_struct *tty; struct tty_struct *tty;
struct circ_buf xmit; struct circ_buf xmit;
unsigned int flags; unsigned int flags;
...@@ -237,7 +234,6 @@ struct uart_info { ...@@ -237,7 +234,6 @@ struct uart_info {
*/ */
#define UIF_CHECK_CD (1 << 25) #define UIF_CHECK_CD (1 << 25)
#define UIF_CTS_FLOW (1 << 26) #define UIF_CTS_FLOW (1 << 26)
#define UIF_CLOSING (1 << 27)
#define UIF_NORMAL_ACTIVE (1 << 29) #define UIF_NORMAL_ACTIVE (1 << 29)
#define UIF_INITIALIZED (1 << 31) #define UIF_INITIALIZED (1 << 31)
...@@ -307,6 +303,12 @@ int uart_register_port(struct uart_driver *reg, struct uart_port *port); ...@@ -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_add_one_port(struct uart_driver *reg, struct uart_port *port);
int uart_remove_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_empty(circ) ((circ)->head == (circ)->tail)
#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) #define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment