patch-2.3.41 linux/drivers/char/serial.c
Next file: linux/drivers/char/sx.c
Previous file: linux/drivers/char/saa5249.c
Back to the patch index
Back to the overall index
- Lines: 1044
- Date:
Thu Jan 27 16:40:09 2000
- Orig file:
v2.3.40/linux/drivers/char/serial.c
- Orig date:
Fri Jan 7 19:13:21 2000
diff -u --recursive --new-file v2.3.40/linux/drivers/char/serial.c linux/drivers/char/serial.c
@@ -44,8 +44,8 @@
* int rs_init(void);
*/
-static char *serial_version = "4.91";
-static char *serial_revdate = "1999-11-17";
+static char *serial_version = "4.92";
+static char *serial_revdate = "2000-1-27";
/*
* Serial driver configuration section. Here are the various options:
@@ -74,12 +74,10 @@
#include <linux/config.h>
#include <linux/version.h>
-#include <linux/sysrq.h>
#undef SERIAL_PARANOIA_CHECK
#define CONFIG_SERIAL_NOPAUSE_IO
#define SERIAL_DO_RESTART
-#define CONFIG_SERIAL_PCI_MEMMAPPED
#if 0
/* These defines are normally controlled by the autoconf.h */
@@ -100,6 +98,12 @@
#endif
#endif
+#ifdef CONFIG_ISAPNP
+#ifndef ENABLE_SERIAL_PNP
+#define ENABLE_SERIAL_PNP
+#endif
+#endif
+
/* Set of debugging defines */
#undef SERIAL_DEBUG_INTR
@@ -133,31 +137,13 @@
#define SERIAL_INLINE
#endif
-#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
-#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
-#else
-#define DBG_CNT(s)
-#endif
-
/*
* End of serial driver configuration section.
*/
-#define NEW_MODULES
-#ifdef LOCAL_HEADERS /* We're building standalone */
-#define MODULE
-#endif
-
-#ifdef NEW_MODULES
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
-#else /* !NEW_MODULES */
-#ifdef MODVERSIONS
-#define MODULE
-#endif
-#endif /* NEW_MODULES */
#include <linux/module.h>
#include <linux/types.h>
@@ -198,13 +184,19 @@
#ifdef ENABLE_SERIAL_PCI
#include <linux/pci.h>
#endif
+#ifdef ENABLE_SERIAL_PNP
+#include <linux/isapnp.h>
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#endif
/*
* All of the compatibilty code so we can compile serial.c against
* older kernels is hidden in serial_compat.h
*/
-#if (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */
-#include "serial_compat.h"
+#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */
+#include "serial_compat.h"
#endif
#include <asm/system.h>
@@ -276,7 +268,7 @@
UART_STARTECH },
{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
{ "Startech", 1, 0}, /* usurped by cyclades.c */
- { "16C950", 128, UART_CLEAR_FIFO | UART_USE_FIFO},
+ { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO},
{ "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO |
UART_STARTECH },
{ "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO |
@@ -290,16 +282,35 @@
#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
-#ifdef ENABLE_SERIAL_PCI
+#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP))
#define NR_PCI_BOARDS 8
+#ifdef MODULE
+/* We don't unregister PCI boards right now */
static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS];
static int serial_pci_board_idx = 0;
-#ifdef PCI_NUM_RESOURCES
+#endif
+#ifndef PCI_BASE_ADDRESS
#define PCI_BASE_ADDRESS(dev, r) ((dev)->resource[r].start)
-#else
-#define PCI_BASE_ADDRESS(dev, r) ((dev)->base_address[r])
+#define PCI_BASE_REGION_SIZE(dev, r) ((dev)->resource[r].end - \
+ (dev)->resource[r].start)
+#define IS_PCI_REGION_IOPORT(dev, r) ((dev)->resource[r].flags & \
+ IORESOURCE_IO)
+#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
+#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */
+
+#ifndef PREPARE_FUNC
+#define PREPARE_FUNC(dev) (dev->prepare)
+#define ACTIVATE_FUNC(dev) (dev->activate)
+#define DEACTIVATE_FUNC(dev) (dev->deactivate)
#endif
-#endif /* ENABLE_SERIAL_PCI */
+
static struct tty_struct *serial_table[NR_PORTS];
static struct termios *serial_termios[NR_PORTS];
@@ -309,6 +320,13 @@
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
+#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
+#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
+ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
+#else
+#define DBG_CNT(s)
+#endif
+
/*
* tmp_buf is used as a temporary buffer by serial_write. We need to
* lock it in case the copy_from_user blocks while swapping in a page,
@@ -355,11 +373,9 @@
outb(info->hub6 - 1 + offset, info->port);
return inb(info->port+1);
#endif
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
case SERIAL_IO_MEM:
return readb(info->iomem_base +
(offset<<info->iomem_reg_shift));
-#endif
#ifdef CONFIG_SERIAL_GSC
case SERIAL_IO_GSC:
return gsc_readb(info->iomem_base + offset);
@@ -379,12 +395,10 @@
outb(value, info->port+1);
break;
#endif
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
case SERIAL_IO_MEM:
writeb(value, info->iomem_base +
(offset<<info->iomem_reg_shift));
break;
-#endif
#ifdef CONFIG_SERIAL_GSC
case SERIAL_IO_GSC:
gsc_writeb(value, info->iomem_base + offset);
@@ -905,11 +919,16 @@
continue;
info = IRQ_ports[irq];
+ /*
+ * The user was a bonehead, and misconfigured their
+ * multiport info. Rather than lock up the kernel
+ * in an infinite loop, if we loop too many times,
+ * print a message and break out of the loop.
+ */
if (pass_counter++ > RS_ISR_PASS_LIMIT) {
-#if 1
- printk("rs_multi loop break\n");
-#endif
- break; /* Prevent infinite loops */
+ printk("Misconfigured multiport serial info "
+ "for irq %d. Breaking out irq loop\n", irq);
+ break;
}
if (multi->port_monitor)
printk("rs port monitor irq %d: 0x%x, 0x%x\n",
@@ -1540,6 +1559,15 @@
/* As a last resort, if the quotient is zero, default to 9600 bps */
if (!quot)
quot = baud_base / 9600;
+ /*
+ * Work around a bug in the Oxford Semiconductor 952 rev B
+ * chip which causes it to seriously miscalculate baud rates
+ * when DLL is 0.
+ */
+ if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) &&
+ (info->state->revision == 0x5202))
+ quot++;
+
info->quot = quot;
info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
info->timeout += HZ/50; /* Add .02 seconds of slop */
@@ -2090,6 +2118,8 @@
if (arg & TIOCM_OUT2)
info->MCR |= UART_MCR_OUT2;
#endif
+ if (arg & TIOCM_LOOP)
+ info->MCR |= UART_MCR_LOOP;
break;
case TIOCMBIC:
if (arg & TIOCM_RTS)
@@ -2102,6 +2132,8 @@
if (arg & TIOCM_OUT2)
info->MCR &= ~UART_MCR_OUT2;
#endif
+ if (arg & TIOCM_LOOP)
+ info->MCR &= ~UART_MCR_LOOP;
break;
case TIOCMSET:
info->MCR = ((info->MCR & ~(UART_MCR_RTS |
@@ -2109,12 +2141,14 @@
UART_MCR_OUT1 |
UART_MCR_OUT2 |
#endif
+ UART_MCR_LOOP |
UART_MCR_DTR))
| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
#ifdef TIOCM_OUT1
| ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
| ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
#endif
+ | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0)
| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
break;
default:
@@ -2579,6 +2613,7 @@
return;
}
info->flags |= ASYNC_CLOSING;
+ restore_flags(flags);
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
@@ -2630,7 +2665,6 @@
ASYNC_CLOSING);
wake_up_interruptible(&info->close_wait);
MOD_DEC_USE_COUNT;
- restore_flags(flags);
}
/*
@@ -2712,6 +2746,8 @@
state = info->state;
rs_flush_buffer(tty);
+ if (info->flags & ASYNC_CLOSING)
+ return;
shutdown(info);
info->event = 0;
state->count = 0;
@@ -2884,10 +2920,8 @@
info->port = sstate->port;
info->flags = sstate->flags;
info->io_type = sstate->io_type;
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
info->iomem_base = sstate->iomem_base;
info->iomem_reg_shift = sstate->iomem_reg_shift;
-#endif
info->xmit_fifo_size = sstate->xmit_fifo_size;
info->line = line;
info->tqueue.routine = do_softint;
@@ -3123,43 +3157,47 @@
* number, and identifies which options were configured into this
* driver.
*/
-static _INLINE_ void show_serial_version(void)
-{
- printk(KERN_INFO "%s version %s%s (%s) with", serial_name,
- serial_version, LOCAL_VERSTRING, serial_revdate);
+static char serial_options[] __initdata =
#ifdef CONFIG_HUB6
- printk(" HUB-6");
+ " HUB-6"
#define SERIAL_OPT
#endif
#ifdef CONFIG_SERIAL_MANY_PORTS
- printk(" MANY_PORTS");
+ " MANY_PORTS"
#define SERIAL_OPT
#endif
#ifdef CONFIG_SERIAL_MULTIPORT
- printk(" MULTIPORT");
+ " MULTIPORT"
#define SERIAL_OPT
#endif
#ifdef CONFIG_SERIAL_SHARE_IRQ
- printk(" SHARE_IRQ");
+ " SHARE_IRQ"
#define SERIAL_OPT
#endif
#ifdef CONFIG_SERIAL_DETECT_IRQ
- printk(" DETECT_IRQ");
+ " DETECT_IRQ"
#define SERIAL_OPT
#endif
#ifdef ENABLE_SERIAL_PCI
- printk(" SERIAL_PCI");
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
- printk(" PCI_IOMEM");
+ " SERIAL_PCI"
+#define SERIAL_OPT
#endif
+#ifdef ENABLE_SERIAL_PNP
+ " ISAPNP"
#define SERIAL_OPT
#endif
#ifdef SERIAL_OPT
- printk(" enabled\n");
+ " enabled\n";
#else
- printk(" no serial options enabled\n");
+ " no serial options enabled\n";
#endif
#undef SERIAL_OPT
+
+static _INLINE_ void show_serial_version(void)
+{
+ printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name,
+ serial_version, LOCAL_VERSTRING, serial_revdate,
+ serial_options);
}
/*
@@ -3188,11 +3226,15 @@
}
#endif
scr_info.magic = SERIAL_MAGIC;
+ scr_info.state = state;
scr_info.port = state->port;
scr_info.flags = state->flags;
#ifdef CONFIG_HUB6
scr_info.hub6 = state->hub6;
#endif
+ scr_info.io_type = state->io_type;
+ scr_info.iomem_base = state->iomem_base;
+ scr_info.iomem_reg_shift = state->iomem_reg_shift;
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
@@ -3296,7 +3338,8 @@
(scratch3 == 0x50 || scratch3 == 0x52 ||
scratch3 == 0x54)) {
state->type = PORT_16C950;
- state->revision = serial_icr_read(info, UART_REV);
+ state->revision = serial_icr_read(info, UART_REV) |
+ (scratch3 << 8);
return;
}
}
@@ -3367,10 +3410,8 @@
info->hub6 = state->hub6;
#endif
info->io_type = state->io_type;
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
info->iomem_base = state->iomem_base;
info->iomem_reg_shift = state->iomem_reg_shift;
-#endif
save_flags(flags); cli();
@@ -3536,18 +3577,151 @@
};
#endif
+
+#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
+
+static void __init printk_pnp_dev_id(unsigned short vendor,
+ unsigned short device)
+{
+ printk("%c%c%c%x%x%x%x",
+ 'A' + ((vendor >> 2) & 0x3f) - 1,
+ 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1,
+ 'A' + ((vendor >> 8) & 0x1f) - 1,
+ (device >> 4) & 0x0f,
+ device & 0x0f,
+ (device >> 12) & 0x0f,
+ (device >> 8) & 0x0f);
+}
+
+static _INLINE_ int get_pci_port(struct pci_dev *dev,
+ struct pci_board *board,
+ struct serial_struct *state,
+ int idx)
+{
+ unsigned long port;
+ int base_idx;
+ int max_port;
+
+ base_idx = SPCI_FL_GET_BASE(board->flags);
+ if (board->flags & SPCI_FL_BASE_TABLE)
+ base_idx += idx;
+
+ if (board->flags & SPCI_FL_REGION_SZ_CAP) {
+ max_port = PCI_BASE_REGION_SIZE(dev, base_idx) / 8;
+ if (idx >= max_port)
+ return 1;
+ }
+
+ port = PCI_BASE_ADDRESS(dev, base_idx) + board->first_uart_offset;
+
+ if ((board->flags & SPCI_FL_BASE_TABLE) == 0)
+ port += idx * (board->uart_offset ? board->uart_offset : 8);
+
+ if (IS_PCI_REGION_IOPORT(dev, base_idx)) {
+ state->port = port;
+ return 0;
+ }
+ state->io_type = SERIAL_IO_MEM;
+ state->iomem_base = ioremap(port, board->uart_offset);
+ state->iomem_reg_shift = board->reg_shift;
+ state->port = 0;
+ return 0;
+}
+
+static _INLINE_ int get_pci_irq(struct pci_dev *dev,
+ struct pci_board *board,
+ int idx)
+{
+ int base_idx;
+
+ if ((board->flags & SPCI_FL_IRQRESOURCE) == 0)
+ return dev->irq;
+
+ base_idx = SPCI_FL_GET_IRQBASE(board->flags);
+ if (board->flags & SPCI_FL_IRQ_TABLE)
+ base_idx += idx;
+
+ return PCI_IRQ_RESOURCE(dev, base_idx);
+}
+
+/*
+ * Common enabler code shared by both PCI and ISAPNP probes
+ */
+static void __init start_pci_pnp_board(struct pci_dev *dev,
+ struct pci_board *board)
+{
+ int k, line;
+ struct serial_struct fake_state;
+ int base_baud;
+
+ if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
+ printk("SERIAL: PNP device '");
+ printk_pnp_dev_id(board->vendor, board->device);
+ printk("' prepare failed\n");
+ return;
+ }
+
+ if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
+ printk("SERIAL: PNP device '");
+ printk_pnp_dev_id(board->vendor, board->device);
+ printk("' activate failed\n");
+ return;
+ }
+
+ /*
+ * Run the initialization function, if any
+ */
+ if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0))
+ return;
+
+#ifdef MODULE
+ /*
+ * Register the serial board in the array if we need to
+ * shutdown the board on a module unload.
+ */
+ if (DEACTIVATE_FUNC(dev) || board->init_fn) {
+ if (serial_pci_board_idx >= NR_PCI_BOARDS)
+ return;
+ serial_pci_board[serial_pci_board_idx].board = *board;
+ serial_pci_board[serial_pci_board_idx].dev = dev;
+ serial_pci_board_idx++;
+ }
+#endif
+
+ base_baud = board->base_baud;
+ if (!base_baud)
+ base_baud = BASE_BAUD;
+ memset(&fake_state, 0, sizeof(fake_state));
+
+ for (k=0; k < board->num_ports; k++) {
+ fake_state.irq = get_pci_irq(dev, board, k);
+ if (get_pci_port(dev, board, &fake_state, k))
+ break;
+ fake_state.flags = ASYNC_SKIP_TEST;
+#ifdef SERIAL_DEBUG_PCI
+ printk("Setup PCI/PNP port: port %x, irq %d, type %d\n",
+ fake_state.port, fake_state.irq, fake_state.io_type);
+#endif
+ line = register_serial(&fake_state);
+ if (line < 0)
+ break;
+ rs_table[line].baud_base = base_baud;
+ }
+}
+#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */
+
#ifdef ENABLE_SERIAL_PCI
/*
* Some PCI serial cards using the PLX 9050 PCI interface chip require
* that the card interrupt be explicitly enabled or disabled. This
* seems to be mainly needed on card using the PLX which also use I/O
* mapped memory.
- *
- * Note that __init is a no-op if MODULE is defined; we depend on this.
*/
-static int __init pci_plx9050_fn(struct pci_dev *dev,
- struct pci_board *board,
- int enable)
+static int
+#ifndef MODULE
+__init
+#endif
+pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
u8 data, *p, scratch;
@@ -3559,7 +3733,7 @@
/* enable/disable interrupts */
p = ioremap(PCI_BASE_ADDRESS(dev, 0), 0x80);
- if (board->vendor == PCI_VENDOR_ID_PANACOM) {
+ if (dev->vendor == PCI_VENDOR_ID_PANACOM) {
scratch = readl(p + 0x4c);
if (enable)
scratch |= 0x40;
@@ -3598,9 +3772,11 @@
#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)
-static int __init pci_siig10x_fn(struct pci_dev *dev,
- struct pci_board *board,
- int enable)
+static int
+#ifndef MODULE
+__init
+#endif
+pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
u16 data, *p;
@@ -3628,9 +3804,11 @@
#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)
-static int __init pci_siig20x_fn(struct pci_dev *dev,
- struct pci_board *board,
- int enable)
+static int
+#ifndef MODULE
+__init
+#endif
+pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{
u8 data;
@@ -3649,11 +3827,12 @@
return 0;
}
+
/*
* This is the configuration table for all of the PCI serial boards
* which we support.
*/
-static struct pci_board pci_boards[] = {
+static struct pci_board pci_boards[] __initdata = {
/*
* Vendor ID, Device ID,
* Subvendor ID, Subdevice ID,
@@ -3752,7 +3931,7 @@
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_KEYSPAN,
PCI_SUBDEVICE_ID_KEYSPAN_SX2,
- SPCI_FL_BASE2 | SPCI_FL_IOMEM, 2, 921600,
+ SPCI_FL_BASE2, 2, 921600, /* IOMEM */
0x400, 7, pci_plx9050_fn },
{ PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
PCI_ANY_ID, PCI_ANY_ID,
@@ -3798,18 +3977,47 @@
{ PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE1, 8, 115200 },
- { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
- PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
- SPCI_FL_BASE0 , 4, 115200 },
{ PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
SPCI_FL_BASE0 , 4, 921600 },
+ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 , 4, 115200 },
+ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 , 2, 115200 },
+ /* This board uses the size of PCI Base region 0 to
+ * signal now many ports are available */
+ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 },
{ PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE0 , 2, 921600 },
- { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_SERIAL,
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 },
+ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650,
+ PCI_ANY_ID, PCI_ANY_ID,
+ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 },
{ PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE2, 1, 460800,
@@ -3945,60 +4153,105 @@
/* Computone devices submitted by Doug McNash dmcnash@computone.com */
{ PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
- SPCI_FL_IOMEM | SPCI_FL_BASE0, 4, 921600,
+ SPCI_FL_BASE0, 4, 921600, /* IOMEM */
0x40, 2, NULL, 0x200 },
{ PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
- SPCI_FL_IOMEM | SPCI_FL_BASE0, 8, 921600,
+ SPCI_FL_BASE0, 8, 921600, /* IOMEM */
0x40, 2, NULL, 0x200 },
{ PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
- SPCI_FL_IOMEM | SPCI_FL_BASE0, 6, 921600,
+ SPCI_FL_BASE0, 6, 921600, /* IOMEM */
0x40, 2, NULL, 0x200 },
- /*
- * Untested PCI modems, sent in from various folks...
- */
- /* at+t zoom 56K faxmodem, from dougd@shieldsbag.com */
- { PCI_VENDOR_ID_ATT, 0x442,
+ /* Digitan DS560-558, from jimd@esoft.com */
+ { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
PCI_ANY_ID, PCI_ANY_ID,
SPCI_FL_BASE1, 1, 115200 },
- /* at&t unknown modem, from jimd@esoft.com */
- { PCI_VENDOR_ID_ATT, 0x480,
+ /* 3Com US Robotics 56k Voice Internal PCI model 5610 */
+ { PCI_VENDOR_ID_USR, 0x1008,
PCI_ANY_ID, PCI_ANY_ID,
- SPCI_FL_BASE1, 1, 115200 },
+ SPCI_FL_BASE0, 1, 115200 },
+ /*
+ * Untested PCI modems, sent in from various folks...
+ */
/* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */
{ PCI_VENDOR_ID_ROCKWELL, 0x1004,
0x1048, 0x1500,
SPCI_FL_BASE1, 1, 115200 },
- /* 3Com US Robotics 56k* Voice Internal PCI, model# 2884 */
- /* from evidal@iti.upv.es */
- /* XXX complete guess this may not work!!! */
- { PCI_VENDOR_ID_USR, 0x1006,
- 0x12b9, 0x0060,
- SPCI_FL_BASE1 | SPCI_FL_IOMEM, 1, 115200 },
- { 0, }
+ /* Generic serial board */
+ { 0, 0,
+ 0, 0,
+ SPCI_FL_BASE0, 1, 115200 },
};
/*
+ * Given a complete unknown PCI device, try to use some hueristics to
+ * guess what the configuration might be, based on the pitiful PCI
+ * serial specs. Returns 0 on success, 1 on failure.
+ */
+static int _INLINE_ serial_guess_board(struct pci_dev *dev,
+ struct pci_board *board)
+{
+ int num_iomem = 0, num_port = 0, first_port = -1;
+ int i;
+
+ /*
+ * If it is not a communications device or the programming
+ * interface is greater than 6, give up.
+ *
+ * (Should we try to make guesses for multiport serial devices
+ * later?)
+ */
+ if ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL ||
+ (dev->class & 0xff) > 6)
+ return 1;
+
+ for (i=0; i < 6; i++) {
+ if (IS_PCI_REGION_IOPORT(dev, i)) {
+ num_port = 0;
+ if (first_port == -1)
+ first_port = i;
+ } else {
+ num_iomem++;
+ }
+ }
+
+ /*
+ * If there is 1 or 0 iomem regions, and exactly one port, use
+ * it.
+ */
+ if (num_iomem <= 1 && num_port == 1) {
+ board->flags = first_port;
+ return 0;
+ }
+ return 1;
+}
+
+
+
+/*
* Query PCI space for known serial boards
* If found, add them to the PCI device space in rs_table[]
*
* Accept a maximum of eight boards
*
*/
-static void probe_serial_pci(void)
+static void __init probe_serial_pci(void)
{
- int k, line;
struct pci_dev *dev = NULL;
struct pci_board *board;
- struct serial_struct fake_state;
- int uart_offset, base_baud, base_idx;
- unsigned long port;
#ifdef SERIAL_DEBUG_PCI
printk(KERN_DEBUG "Entered probe_serial_pci()\n");
#endif
+ if (!pcibios_present()) {
+#ifdef SERIAL_DEBUG_PCI
+ printk(KERN_DEBUG "Leaving probe_serial_pci() (no pcibios)\n");
+#endif
+ return;
+ }
+
pci_for_each_dev(dev) {
for (board = pci_boards; board->vendor; board++) {
if (board->vendor != (unsigned short) PCI_ANY_ID &&
@@ -4008,121 +4261,87 @@
dev->device != board->device)
continue;
if (board->subvendor != (unsigned short) PCI_ANY_ID &&
- dev->subsystem_vendor != board->subvendor)
+ pci_get_subvendor(dev) != board->subvendor)
continue;
if (board->subdevice != (unsigned short) PCI_ANY_ID &&
- dev->subsystem_device != board->subdevice)
+ pci_get_subdevice(dev) != board->subdevice)
continue;
break;
}
- if (board->vendor == 0) {
+ if (board->vendor == 0 && serial_guess_board(dev, board))
+ continue;
+
+ start_pci_pnp_board(dev, board);
+ }
+
#ifdef SERIAL_DEBUG_PCI
- printk(KERN_DEBUG
- "Found unknown serial board: %x:%x, %x:%x, %x\n",
- dev->vendor, dev->device, subvendor, subdevice,
- dev->class);
- printk(KERN_DEBUG
- " Addresses: %lx, %lx, %lx, %lx\n",
- PCI_BASE_ADDRESS(dev, 0), PCI_BASE_ADDRESS(dev, 1),
- PCI_BASE_ADDRESS(dev, 2), PCI_BASE_ADDRESS(dev, 3));
+ printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n");
#endif
- continue;
- }
+ return;
+}
- /*
- * Run the initialization function, if any
- */
- if (board->init_fn)
- if ((board->init_fn)(dev, board, 1) != 0)
- continue;
+#endif /* ENABLE_SERIAL_PCI */
- /*
- * Register the serial board in the array so we can
- * shutdown the board later, if necessary.
- */
- if (serial_pci_board_idx >= NR_PCI_BOARDS)
- continue;
- serial_pci_board[serial_pci_board_idx].board = board;
- serial_pci_board[serial_pci_board_idx].dev = dev;
- serial_pci_board_idx++;
+#ifdef ENABLE_SERIAL_PNP
- base_idx = board->flags & SPCI_FL_BASE_MASK;
- port = PCI_BASE_ADDRESS(dev, base_idx) +
- board->first_uart_offset;
- if (board->flags & SPCI_FL_IOMEM)
- port &= PCI_BASE_ADDRESS_MEM_MASK;
- else
- port &= PCI_BASE_ADDRESS_IO_MASK;
+static struct pci_board pnp_devices[] __initdata = {
+ /* Rockwell 56K ACF II Fax+Data+Voice Modem */
+ { ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* Boca Research 33,600 ACF Modem */
+ { ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* Best Data Products Inc. Smart One 336F PnP Modem */
+ { ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* These ID's are taken from M$ documentation */
+ /* Compaq 14400 Modem */
+ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* Compaq 2400/9600 Modem */
+ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* Generic standard PC COM port */
+ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* Generic 16550A-compatible COM port */
+ { ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ { 0, }
+};
- /*
- * Set some defaults for the loop below, which
- * actually registers each serial port belonging to
- * the card.
- */
- uart_offset = board->uart_offset;
- if (!uart_offset)
- uart_offset = 8;
- base_baud = board->base_baud;
- if (!base_baud)
- base_baud = BASE_BAUD;
-#ifndef CONFIG_SERIAL_PCI_MEMMAPPED
- if (board->flags & SPCI_FL_IOMEM) {
-#ifdef SERIAL_DEBUG_PCI
- printk(KERN_DEBUG
- "Can't support memory mapped PCI serial device\n");
+static void __init probe_serial_pnp(void)
+{
+ struct pci_dev *dev = NULL;
+ struct pci_board *board;
+
+#ifdef SERIAL_DEBUG_PNP
+ printk("Entered probe_serial_pnp()\n");
#endif
- continue;
- }
+ if (!isapnp_present()) {
+#ifdef SERIAL_DEBUG_PNP
+ printk("Leaving probe_serial_pnp() (no isapnp)\n");
#endif
- memset(&fake_state, 0, sizeof(fake_state));
+ return;
+ }
-#ifdef SERIAL_DEBUG_PCI
- printk(KERN_DEBUG
- "Found Serial PCI device: %x:%x, %x:%x, %x\n",
- dev->vendor, dev->device, subvendor, subdevice,
- dev->class);
- printk(KERN_DEBUG
- " IRQ: %d, base: %lx (%s), num_ports: %d\n",
- dev->irq, port, board->flags & SPCI_FL_IOMEM ?
- "iomem" : "port", board->num_ports);
-#endif
+ for (board = pnp_devices; board->vendor; board++) {
+ while ((dev = isapnp_find_dev(NULL, board->vendor,
+ board->device, dev))) {
+
+ start_pci_pnp_board(dev, board);
- for (k=0; k < board->num_ports; k++) {
- if (board->flags & SPCI_FL_BASE_TABLE) {
- port = PCI_BASE_ADDRESS(dev, base_idx++);
- if (board->flags & SPCI_FL_IOMEM)
- port &= PCI_BASE_ADDRESS_MEM_MASK;
- else
- port &= PCI_BASE_ADDRESS_IO_MASK;
- }
- fake_state.irq = dev->irq;
- fake_state.port = port;
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
- if (board->flags & SPCI_FL_IOMEM) {
- fake_state.io_type = SERIAL_IO_MEM;
- fake_state.iomem_base =
- ioremap(port, board->uart_offset);
- fake_state.iomem_reg_shift = board->reg_shift;
- fake_state.port = 0;
- }
-#endif
- port += uart_offset;
- fake_state.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
- line = register_serial(&fake_state);
- if (line < 0)
- break;
- rs_table[line].baud_base = base_baud;
- }
- }
-
-#ifdef SERIAL_DEBUG_PCI
- printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n");
+ }
+ }
+
+#ifdef SERIAL_DEBUG_PNP
+ printk("Leaving probe_serial_pnp() (probe finished)\n");
#endif
- return;
+ return;
}
-#endif /* ENABLE_SERIAL_PCI */
+#endif /* ENABLE_SERIAL_PNP */
/*
* The serial driver boot-time initialization code!
@@ -4270,6 +4489,9 @@
#ifdef ENABLE_SERIAL_PCI
probe_serial_pci();
#endif
+#ifdef ENABLE_SERIAL_PNP
+ probe_serial_pnp();
+#endif
return 0;
}
@@ -4282,6 +4504,7 @@
int i;
unsigned long flags;
struct serial_state *state;
+ struct async_struct *info;
save_flags(flags);
cli();
@@ -4311,13 +4534,17 @@
state->port = req->port;
state->flags = req->flags;
state->io_type = req->io_type;
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
state->iomem_base = req->iomem_base;
state->iomem_reg_shift = req->iomem_reg_shift;
-#endif
if (req->baud_base)
state->baud_base = req->baud_base;
-
+ if ((info = state->info) != NULL) {
+ info->port = req->port;
+ info->flags = req->flags;
+ info->io_type = req->io_type;
+ info->iomem_base = req->iomem_base;
+ info->iomem_reg_shift = req->iomem_reg_shift;
+ }
autoconfig(state);
if (state->type == PORT_UNKNOWN) {
restore_flags(flags);
@@ -4387,17 +4614,20 @@
}
if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port)
release_region(rs_table[i].port, 8);
-#if defined(ENABLE_SERIAL_PCI) && defined (CONFIG_SERIAL_PCI_MEMMAPPED)
+#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
if (rs_table[i].iomem_base)
iounmap(rs_table[i].iomem_base);
#endif
}
-#ifdef ENABLE_SERIAL_PCI
+#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
for (i=0; i < serial_pci_board_idx; i++) {
struct pci_board_inst *brd = &serial_pci_board[i];
- if (brd->board->init_fn)
- (brd->board->init_fn)(brd->dev, brd->board, 0);
+ if (brd->board.init_fn)
+ (brd->board.init_fn)(brd->dev, &brd->board, 0);
+
+ if (DEACTIVATE_FUNC(brd->dev))
+ (DEACTIVATE_FUNC(brd->dev))(brd->dev);
}
#endif
if (tmp_buf) {
@@ -4599,10 +4829,8 @@
info->hub6 = state->hub6;
#endif
info->io_type = state->io_type;
-#ifdef CONFIG_SERIAL_PCI_MEMMAPPED
info->iomem_base = state->iomem_base;
info->iomem_reg_shift = state->iomem_reg_shift;
-#endif
quot = state->baud_base / baud;
cval = cflag & (CSIZE | CSTOPB);
#if defined(__powerpc__) || defined(__alpha__)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)