diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-29 01:41:54 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-29 01:41:54 +0000 |
commit | f969d69ba9f952e5bdd38278e25e26a3e4a61a70 (patch) | |
tree | b3530d803df59d726afaabebc6626987dee1ca05 /drivers/char/pcmcia/serial_cb.c | |
parent | a10ce7ef2066b455d69187643ddf2073bfc4db24 (diff) |
Merge with 2.3.27.
Diffstat (limited to 'drivers/char/pcmcia/serial_cb.c')
-rw-r--r-- | drivers/char/pcmcia/serial_cb.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/drivers/char/pcmcia/serial_cb.c b/drivers/char/pcmcia/serial_cb.c new file mode 100644 index 000000000..8a2dede85 --- /dev/null +++ b/drivers/char/pcmcia/serial_cb.c @@ -0,0 +1,150 @@ +/*====================================================================== + + A driver for CardBus serial devices + + serial_cb.c 1.14 1999/11/11 02:18:08 + + Copyright 1998, 1999 by Donald Becker and David Hinds + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + All other rights reserved. + + This driver is an activator for CardBus serial cards, as + found on multifunction (e.g. Ethernet and Modem) CardBus cards. + + Donald Becker may be reached as becker@CESDIS.edu, or C/O + USRA Center of Excellence in Space Data and Information Sciences + Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771 + David Hinds may be reached at dhinds@pcmcia.sourceforge.org + +======================================================================*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/major.h> +#include <linux/pci.h> +#include <asm/io.h> + +#include <pcmcia/driver_ops.h> + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"serial_cb.c 1.14 1999/11/11 02:18:08 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================== + + Card-specific configuration hacks + +======================================================================*/ + +static void device_setup(u_char bus, u_char devfn, u_int ioaddr) +{ + u_short a, b; + + pcibios_read_config_word(bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &a); + pcibios_read_config_word(bus, devfn, PCI_SUBSYSTEM_ID, &b); + if ((a == 0x13a2) && (b == 0x8007)) { + /* Ositech Jack of Spades */ + DEBUG(0, " 83c175 NVCTL_m = 0x%4.4x.\n", inl(ioaddr+0x80)); + outl(0x4C00, ioaddr + 0x80); + outl(0x4C80, ioaddr + 0x80); + } + DEBUG(0, " modem registers are %2.2x %2.2x %2.2x " + "%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x.\n", + inb(ioaddr + 0), inb(ioaddr + 1), inb(ioaddr + 2), + inb(ioaddr + 3), inb(ioaddr + 4), inb(ioaddr + 5), + inb(ioaddr + 6), inb(ioaddr + 7), inb(ioaddr + 8)); +} + +/*====================================================================== + + serial_attach() creates a serial device "instance" and registers + it with the kernel serial driver, and serial_detach() unregisters + an instance. + +======================================================================*/ + +static dev_node_t *serial_attach(dev_locator_t *loc) +{ + u_int io; + u_char bus, devfn, irq; + int line; + struct serial_struct serial; + + if (loc->bus != LOC_PCI) return NULL; + bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; + printk(KERN_INFO "serial_attach(bus %d, fn %d)\n", bus, devfn); + pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); + pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); + if (io & PCI_BASE_ADDRESS_SPACE_IO) { + io &= PCI_BASE_ADDRESS_IO_MASK; + } else { + printk(KERN_NOTICE "serial_cb: PCI base address 0 is not IO\n"); + return NULL; + } + device_setup(bus, devfn, io); + memset(&serial, 0, sizeof(serial)); + serial.port = io; serial.irq = irq; + serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ; + + /* Some devices seem to need extra time */ + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/50); + + line = register_serial(&serial); + if (line < 0) { + printk(KERN_NOTICE "serial_cb: register_serial() at 0x%04x, " + "irq %d failed\n", serial.port, serial.irq); + return NULL; + } else { + dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); + sprintf(node->dev_name, "ttyS%d", line); + node->major = TTY_MAJOR; node->minor = 0x40 + line; + node->next = NULL; + MOD_INC_USE_COUNT; + return node; + } +} + +static void serial_detach(dev_node_t *node) +{ + DEBUG(0, "serial_detach(tty%02d)\n", node->minor - 0x40); + unregister_serial(node->minor - 0x40); + kfree(node); + MOD_DEC_USE_COUNT; +} + +/*====================================================================*/ + +struct driver_operations serial_ops = { + "serial_cb", serial_attach, NULL, NULL, serial_detach +}; + +static int __init init_serial_cb(void) +{ + DEBUG(0, "%s\n", version); + register_driver(&serial_ops); + return 0; +} + +static void __exit exit_serial_cb(void) +{ + DEBUG(0, "serial_cb: unloading\n"); + unregister_driver(&serial_ops); +} + +module_init(init_serial_cb); +module_exit(exit_serial_cb); |