patch-2.0.37 linux/drivers/char/serial.c
Next file: linux/drivers/char/tga.c
Previous file: linux/drivers/char/selection.h
Back to the patch index
Back to the overall index
- Lines: 609
- Date:
Sun Jun 13 10:21:00 1999
- Orig file:
v2.0.36/linux/drivers/char/serial.c
- Orig date:
Mon Jul 13 13:46:28 1998
diff -u --recursive --new-file v2.0.36/linux/drivers/char/serial.c linux/drivers/char/serial.c
@@ -19,6 +19,9 @@
* flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
* Bernd Anhäupl 05/17/96.
*
+ * Added Support for PCI serial boards which contain 16x50 Chips
+ * 31.10.1998 Henning P. Schmiedehausen <hps@tanstaafl.de>
+ *
* This module exports the following rs232 io functions:
*
* int rs_init(void);
@@ -43,13 +46,18 @@
#include <linux/ioport.h>
#include <linux/mm.h>
+#ifdef CONFIG_SERIAL_PCI
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#endif
+
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/bitops.h>
static char *serial_name = "Serial driver";
-static char *serial_version = "4.13";
+static char *serial_version = "4.13p1";
DECLARE_TASK_QUEUE(tq_serial);
@@ -83,6 +91,10 @@
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
+#ifdef CONFIG_SERIAL_PCI
+# undef SERIAL_DEBUG_PCI
+#endif
+
#define RS_STROBE_TIME (10*HZ)
#define RS_ISR_PASS_LIMIT 256
@@ -121,6 +133,14 @@
*/
#define BASE_BAUD ( 1843200 / 16 )
+/*
+ * Well, it is not a 24,756 MHz clock but it is at least a start.
+ * This PCI board here has a 14,7456 MHz crystal oscillator which is
+ * eight times as fast as the standard serial clock...
+ */
+
+#define PCI_BAUD ( 14745600 / 16 )
+
/* Standard COM flags (except for COM4, because of the 8514 problem) */
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
@@ -130,6 +150,29 @@
#define BOCA_FLAGS 0
#define HUB6_FLAGS 0
+#ifdef CONFIG_SERIAL_PCI
+
+#define PCI_FLAGS (ASYNC_PCI|ASYNC_BOOT_AUTOCONF)
+
+#ifndef PCI_DEVICE_ID_PLX_SPCOM200
+#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103
+#endif
+
+/*
+ * The chips we know about
+ */
+
+#define PCISER_PLX9050 0 /* PLX 9050 local bus bridge as serial card */
+#define PCISER_PCCOM4 1 /* "PC COM PCI Bus 4 port serial Adapter" -- from Alvin Sim <alvin@alloycp.com.au> */
+
+struct pci_serial_boards pci_serial_tbl[] = {
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, "SPCom 200", PCISER_PLX9050, pci_space_0|pci_space_1, 1, 0, 128, PCI_BAUD },
+ { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, "PC COM 4", PCISER_PCCOM4, pci_space_0, 4, 8, 128, BASE_BAUD },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+#endif
+
/*
* The following define the access methods for the HUB6 card. All
* access is through two ports for all 24 possible chips. The card is
@@ -202,10 +245,42 @@
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */
#endif
+
+#ifdef CONFIG_SERIAL_PCI
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS32 or bigger... */
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS33 */
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS34 */
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS35 */
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS36 */
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS37 */
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS38 */
+ { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS39 */
+#endif
};
#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
+#ifdef CONFIG_SERIAL_PCI
+
+/*
+ * currently you can have up to four PCI serial boards in your
+ * system. Increase the size of this structure to have more
+ */
+
+struct pci_struct pci_rs_chips[] = {
+ {0, 0,},
+ {0, 0,},
+ {0, 0,},
+ {0, 0,},
+};
+
+#define PCI_NR_BOARDS (sizeof(pci_rs_chips)/sizeof(struct pci_struct))
+#define PCI_NR_PORTS 8
+
+#define PCI_PORT_START (NR_PORTS - PCI_NR_PORTS)
+
+#endif
+
static struct tty_struct *serial_table[NR_PORTS];
static struct termios *serial_termios[NR_PORTS];
static struct termios *serial_termios_locked[NR_PORTS];
@@ -1731,6 +1806,26 @@
/*
+ * rs_break() --- routine which turns the break handling on or off
+ * adapted from 2.1.124
+ */
+static void rs_break(struct async_struct * info, int break_state)
+{
+ unsigned long flags;
+
+ if (!info->port)
+ return;
+ save_flags(flags);cli();
+ if (break_state == -1)
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) | UART_LCR_SBC);
+ else
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+ restore_flags(flags);
+}
+
+/*
* This routine sends a break character out the serial port.
*/
static void send_break( struct async_struct * info, int duration)
@@ -1922,6 +2017,20 @@
}
switch (cmd) {
+ case TIOCSBRK: /* Turn break on, unconditionally */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ rs_break(info,-1);
+ return 0;
+ case TIOCCBRK: /* Turn break off, unconditionally */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ rs_break(info,0);
+ return 0;
case TCSBRK: /* SVID version: non-zero arg --> no break */
retval = tty_check_change(tty);
if (retval)
@@ -2478,6 +2587,10 @@
static void show_serial_version(void)
{
printk(KERN_INFO "%s version %s with", serial_name, serial_version);
+#ifdef CONFIG_SERIAL_PCI
+ printk(" PCI");
+#define SERIAL_OPT
+#endif
#ifdef CONFIG_HUB6
printk(" HUB-6");
#define SERIAL_OPT
@@ -2712,6 +2825,73 @@
restore_flags(flags);
}
+void display_uart_type(int type)
+{
+ switch (type) {
+ case PORT_8250:
+ printk(" is a 8250\n");
+ break;
+ case PORT_16450:
+ printk(" is a 16450\n");
+ break;
+ case PORT_16550:
+ printk(" is a 16550\n");
+ break;
+ case PORT_16550A:
+ printk(" is a 16550A\n");
+ break;
+ case PORT_16650:
+ printk(" is a 16650\n");
+ break;
+ default:
+ printk("\n");
+ break;
+ }
+}
+
+void init_port(struct async_struct *info, int num)
+{
+ info->magic = SERIAL_MAGIC;
+ info->line = num;
+ info->tty = 0;
+ info->type = PORT_UNKNOWN;
+ info->custom_divisor = 0;
+ info->close_delay = 5*HZ/10;
+ info->closing_wait = 30*HZ;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
+ info->callout_termios =callout_driver.init_termios;
+ info->normal_termios = serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ info->delta_msr_wait = 0;
+ info->icount.cts = info->icount.dsr =
+ info->icount.rng = info->icount.dcd = 0;
+ info->next_port = 0;
+ info->prev_port = 0;
+ if (info->irq == 2)
+ info->irq = 9;
+
+ if (info->type == PORT_UNKNOWN) {
+ if (!(info->flags & ASYNC_BOOT_AUTOCONF))
+ return;
+ autoconfig(info);
+ if (info->type == PORT_UNKNOWN)
+ return;
+ }
+ printk(KERN_INFO "ttyS%02d%s%s at 0x%04x (irq = %d)", info->line,
+ (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
+ (info->flags & ASYNC_PCI) ? " PCI" : "",
+ info->port, info->irq);
+ display_uart_type(info->type);
+}
+
int register_serial(struct serial_struct *req);
void unregister_serial(int line);
@@ -2722,9 +2902,212 @@
#include <linux/symtab_end.h>
};
+#ifdef CONFIG_SERIAL_PCI
+
+/*
+ * 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)
+{
+ u16 vendor, device;
+ static int pci_index = 0;
+ unsigned char pci_bus, pci_device_fn;
+ struct async_struct *pci_boards = &rs_table[PCI_PORT_START];
+ unsigned int port_num = 0;
+ unsigned int card_num = 0;
+
+ u32 device_ioaddr;
+ u8 device_irq;
+
+ enum pci_spc pci_space = pci_space_0;
+ unsigned int pci_space_offset = 0;
+
+
+#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;
+ }
+
+/*
+ * Start scanning the PCI bus for serial controllers ...
+ *
+ */
+
+ for (;pci_index < 0xff; pci_index++) {
+ int i = 0;
+
+ if (pcibios_find_class(PCI_CLASS_COMMUNICATION_SERIAL << 8,
+ pci_index,
+ &pci_bus,
+ &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+ break; /* for (; pci_index ... */
+
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device);
+
+ for (i = 0; pci_serial_tbl[i].board_name; i++) {
+ if (vendor == pci_serial_tbl[i].vendor_id &&
+ device == pci_serial_tbl[i].device_id)
+ break; /* for(i=0... */
+ }
+
+ if (pci_serial_tbl[i].board_name == 0) {
+#ifdef SERIAL_DEBUG_PCI
+ printk(KERN_DEBUG "Found Board (%x/%x) (not one of us)\n", vendor, device);
+#endif
+ continue; /* Found a serial communication controller but not one we know */
+ }
+
+/*
+ * At this point we found a serial board which we know
+ */
+
+ if(card_num >= PCI_NR_BOARDS) {
+ printk(KERN_ERR "Already %d boards configured, skipping\n", PCI_NR_BOARDS);
+ continue; /* for (;pci_index < 0xff */
+ }
+
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &device_irq);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_1, &device_ioaddr);
+
+#ifdef SERIAL_DEBUG_PCI
+ printk(KERN_DEBUG "Device %s at #%x found\n", pci_serial_tbl[i].board_name, device_ioaddr);
+#endif
+
+ if (check_region(device_ioaddr, pci_serial_tbl[i].io_size)) {
+ printk(KERN_ERR "Could not reserve %d bytes of I/O Space at %x\n", pci_serial_tbl[i].io_size, device_ioaddr);
+ continue; /* for (;pci_index < 0xff */
+ }
+
+/*
+ * Every PCI device brings 128 bytes (at least) of IO-Space with it
+ * reserve a region for it. It is not exactly necessary as PCI will
+ * ensure that no other device will be mapped onto this space (LOL)
+ * but we do it nevertheless so it will show up nicely on
+ * /proc/ioports -- hps
+ */
+
+ if((device_ioaddr & 1) == 0) {
+#ifdef SERIAL_DEBUG_PCI
+ device_ioaddr &= ~0x7f;
+ printk(KERN_DEBUG "%s has its config registers memory-mapped at #%x (ignoring)\n",
+ pci_serial_tbl[i].board_name, device_ioaddr);
+#endif
+ continue; /* for (;pci_index < 0xff */
+ }
+
+ device_ioaddr &= ~0x7f; /* Mask out the flag bits
+ * from this register. At least on the PLX9050
+ * they're always 0 but this is here nevertheless
+ * for sanity's sake
+ */
+
+ request_region(device_ioaddr, pci_serial_tbl[i].io_size, "serial (PCI Controller)");
+
+ pci_rs_chips[card_num].start = device_ioaddr;
+ pci_rs_chips[card_num].type = &pci_serial_tbl[i];
+
+
+/*
+ * Every PCI device can bring up to four PCI memory or IO spaces (at
+ * least according to the documentation I have. So we will now check
+ * with our config whether this device has one of these spaces and we
+ * should configure UARTs inside -- hps
+ */
+
+ for(; pci_space <= pci_space_3; pci_space <<= 1, pci_space_offset+= 4) {
+ u32 uart_chip_base;
+ u32 uart_chip_count;
+
+ if((pci_serial_tbl[i].pci_space & pci_space) == 0)
+ continue; /* for(;pci_space... */
+
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_2+pci_space_offset, &uart_chip_base);
+
+ if((uart_chip_base & 1) == 0) {
+#ifdef SERIAL_DEBUG_PCI
+ chip_base &= ~0x0f;
+ printk(KERN_DEBUG "%s has a memory-mapped IO Chip at #%x (ignoring)\n",
+ pci_serial_tbl[i].board_name, chip_base);
+#endif
+ continue; /* for(;pci_space... */
+ }
+
+ uart_chip_base &= ~0x0f;
+
+/*
+ * uart_chip_base now points to the IO-Space.
+ *
+ * Alvin Sim <alvin@alloycp.com.au> told me the following thing:
+ *
+ * UARTS can be "setserial"d by kernel 2.0.35, but ports needed to be
+ * manually specified. 4 ports start at 0x6100, in increments of 8
+ * addresses.
+ *
+ * so there is at least one board out there which can do more than one
+ * UART in a single PCI config space. My trustworthy SPCom 200 PCI has
+ * just one UART in one config space. So I added a check for more than
+ * one chip in a config space -- hps
+ *
+ */
+
+ for(uart_chip_count=0;uart_chip_count < pci_serial_tbl[i].dev_per_space; uart_chip_count++) {
+#ifdef SERIAL_DEBUG_PCI
+ printk(KERN_DEBUG "%s has an IO Chip at #%x\n",
+ pci_serial_tbl[i].board_name, uart_chip_base);
+#endif
+
+ if(port_num >= PCI_NR_PORTS) {
+ printk(KERN_ERR "Already %d ports configured, skipping\n", PCI_NR_PORTS);
+ break; /* for(;uart_chip_count... */
+ }
+
+ if (check_region(uart_chip_base, 8)) {
+ printk(KERN_ERR "Could not reserve %d bytes of I/O Space at %x\n", 8, uart_chip_base);
+ break; /* for(;uart_chip_count... */
+ }
+
+ request_region(uart_chip_base, 8, "serial (PCI)");
+ pci_boards[port_num].port = uart_chip_base;
+ pci_boards[port_num].irq = device_irq;
+ pci_boards[port_num].flags = PCI_FLAGS;
+ pci_boards[port_num].baud_base = pci_serial_tbl[i].baud_base;
+
+ port_num++;
+ uart_chip_base += pci_serial_tbl[i].dev_spacing;
+
+ } /* for(uart_chip_count... */
+ } /* for(pci_space ... */
+
+ card_num++;
+ } /* for */
+
+#ifdef SERIAL_DEBUG_PCI
+ printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n");
+#endif
+ return;
+}
+
+#endif /* CONFIG_SERIAL_PCI */
+
/*
* The serial driver boot-time initialization code!
*/
+
int rs_init(void)
{
int i;
@@ -2744,6 +3127,9 @@
}
show_serial_version();
+#ifdef CONFIG_SERIAL_PCI
+ probe_serial_pci();
+#endif
/* Initialize the tty_driver structure */
@@ -2795,67 +3181,15 @@
panic("Couldn't register callout driver\n");
for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- info->magic = SERIAL_MAGIC;
- info->line = i;
- info->tty = 0;
- info->type = PORT_UNKNOWN;
- info->custom_divisor = 0;
- info->close_delay = 5*HZ/10;
- info->closing_wait = 30*HZ;
- info->x_char = 0;
- info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
- info->tqueue.routine = do_softint;
- info->tqueue.data = info;
- info->tqueue_hangup.routine = do_serial_hangup;
- info->tqueue_hangup.data = info;
- info->callout_termios =callout_driver.init_termios;
- info->normal_termios = serial_driver.init_termios;
- info->open_wait = 0;
- info->close_wait = 0;
- info->delta_msr_wait = 0;
- info->icount.cts = info->icount.dsr =
- info->icount.rng = info->icount.dcd = 0;
- info->next_port = 0;
- info->prev_port = 0;
- if (info->irq == 2)
- info->irq = 9;
- if (info->type == PORT_UNKNOWN) {
- if (!(info->flags & ASYNC_BOOT_AUTOCONF))
- continue;
- autoconfig(info);
- if (info->type == PORT_UNKNOWN)
- continue;
- }
- printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line,
- (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
- info->port, info->irq);
- switch (info->type) {
- case PORT_8250:
- printk(" is a 8250\n");
- break;
- case PORT_16450:
- printk(" is a 16450\n");
- break;
- case PORT_16550:
- printk(" is a 16550\n");
- break;
- case PORT_16550A:
- printk(" is a 16550A\n");
- break;
- case PORT_16650:
- printk(" is a 16650\n");
- break;
- default:
- printk("\n");
- break;
- }
- }
+ init_port(info, i);
+ };
+
register_symtab(&serial_syms);
return 0;
}
+
+
/*
* register_serial and unregister_serial allows for serial ports to be
* configured at run-time, to support PCMCIA modems.
@@ -2898,20 +3232,9 @@
printk("register_serial(): autoconfig failed\n");
return -1;
}
- printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line,
+ printk(KERN_INFO "ttyS%02d at 0x%04x (irq = %d)", info->line,
info->port, info->irq);
- switch (info->type) {
- case PORT_8250:
- printk(" is a 8250\n"); break;
- case PORT_16450:
- printk(" is a 16450\n"); break;
- case PORT_16550:
- printk(" is a 16550\n"); break;
- case PORT_16550A:
- printk(" is a 16550A\n"); break;
- default:
- printk("\n"); break;
- }
+ display_uart_type(info->type);
restore_flags(flags);
return info->line;
}
@@ -2926,7 +3249,7 @@
if (info->tty)
tty_hangup(info->tty);
info->type = PORT_UNKNOWN;
- printk(KERN_INFO "tty%02d unloaded\n", info->line);
+ printk(KERN_INFO "ttyS%02d unloaded\n", info->line);
restore_flags(flags);
}
@@ -2960,6 +3283,18 @@
if (rs_table[i].type != PORT_UNKNOWN)
release_region(rs_table[i].port, 8);
}
+
+#ifdef CONFIG_SERIAL_PCI
+ for (i = 0; i < PCI_NR_BOARDS; i++) {
+ if (pci_rs_chips[i].start != 0x0) {
+#ifdef SERIAL_DEBUG_PCI
+ printk(KERN_DEBUG "Releasing %d Bytes at #%x\n", pci_rs_chips[i].type->io_size, pci_rs_chips[i].start);
+#endif
+ release_region(pci_rs_chips[i].start, pci_rs_chips[i].type->io_size);
+ }
+ }
+#endif
+
if (tmp_buf) {
free_page((unsigned long) tmp_buf);
tmp_buf = NULL;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov