diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2005-08-29 10:34:59 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-08-29 10:34:59 -0700 |
commit | 975f957dc408925805dd8f5aa4217b7eeea2d005 (patch) | |
tree | 2198bb72323a016d93c7440c9240bac94a5c0016 /drivers | |
parent | 2321fbd2b87539edc1fbfc2e186528a1ef93835f (diff) | |
parent | 661299d9d0437a0ff72240f3d60016ac3a361a6e (diff) |
Merge HEAD from master.kernel.org:/home/rmk/linux-2.6-serial.git
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/parport/parport_serial.c | 339 | ||||
-rw-r--r-- | drivers/serial/8250_pci.c | 474 |
2 files changed, 343 insertions, 470 deletions
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 00498e2f1205..d3dad0aac7cb 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -23,13 +23,8 @@ #include <linux/pci.h> #include <linux/parport.h> #include <linux/parport_pc.h> -#include <linux/serial.h> -#include <linux/serialP.h> -#include <linux/list.h> #include <linux/8250_pci.h> -#include <asm/serial.h> - enum parport_pc_pci_cards { titan_110l = 0, titan_210l, @@ -168,182 +163,147 @@ static struct pci_device_id parport_serial_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); -struct pci_board_no_ids { - int flags; - int num_ports; - int base_baud; - int uart_offset; - int reg_shift; - int (*init_fn)(struct pci_dev *dev, struct pci_board_no_ids *board, - int enable); - int first_uart_offset; -}; - -static int __devinit siig10x_init_fn(struct pci_dev *dev, struct pci_board_no_ids *board, int enable) -{ - return pci_siig10x_fn(dev, enable); -} - -static int __devinit siig20x_init_fn(struct pci_dev *dev, struct pci_board_no_ids *board, int enable) -{ - return pci_siig20x_fn(dev, enable); -} - -static int __devinit netmos_serial_init(struct pci_dev *dev, struct pci_board_no_ids *board, int enable) -{ - board->num_ports = dev->subsystem_device & 0xf; - return 0; -} - -static struct pci_board_no_ids pci_boards[] __devinitdata = { - /* - * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers, - * Register shift to use for memory-mapped I/O, - * Initialization function, first UART offset - */ - -// Cards not tested are marked n/t -// If you have one of these cards and it works for you, please tell me.. - -/* titan_110l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 1, 921600 }, -/* titan_210l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, -/* netmos_9xx5_combo */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200, 0, 0, netmos_serial_init }, -/* netmos_9855 */ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200, 0, 0, netmos_serial_init }, -/* avlab_1s1p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s1p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s1p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s2p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s2p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s2p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_2s1p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, -/* avlab_2s1p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, -/* avlab_2s1p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, -/* siig_1s1p_10x */ { SPCI_FL_BASE2, 1, 460800, 0, 0, siig10x_init_fn }, -/* siig_2s1p_10x */ { SPCI_FL_BASE2, 1, 921600, 0, 0, siig10x_init_fn }, -/* siig_2p1s_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, -/* siig_1s1p_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, -/* siig_2s1p_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, +/* + * This table describes the serial "geometry" of these boards. Any + * quirks for these can be found in drivers/serial/8250_pci.c + * + * Cards not tested are marked n/t + * If you have one of these cards and it works for you, please tell me.. + */ +static struct pciserial_board pci_parport_serial_boards[] __devinitdata = { + [titan_110l] = { + .flags = FL_BASE1 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [titan_210l] = { + .flags = FL_BASE1 | FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [netmos_9xx5_combo] = { + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [netmos_9855] = { + .flags = FL_BASE2 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_1s1p] = { /* n/t */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_1s1p_650] = { /* nt */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_1s1p_850] = { /* nt */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_1s2p] = { /* n/t */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_1s2p_650] = { /* nt */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_1s2p_850] = { /* nt */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_2s1p] = { /* n/t */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_2s1p_650] = { /* nt */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [avlab_2s1p_850] = { /* nt */ + .flags = FL_BASE0 | FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [siig_1s1p_10x] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [siig_2s1p_10x] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [siig_2p1s_20x] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [siig_1s1p_20x] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [siig_2s1p_20x] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, }; struct parport_serial_private { - int num_ser; - int line[20]; - struct pci_board_no_ids ser; + struct serial_private *serial; int num_par; struct parport *port[PARPORT_MAX]; struct parport_pc_pci par; }; -static int __devinit get_pci_port (struct pci_dev *dev, - struct pci_board_no_ids *board, - struct serial_struct *req, - int idx) -{ - unsigned long port; - int base_idx; - int max_port; - int offset; - - 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_resource_len(dev, base_idx) / 8; - if (idx >= max_port) - return 1; - } - - offset = board->first_uart_offset; - - /* Timedia/SUNIX uses a mixture of BARs and offsets */ - /* Ugh, this is ugly as all hell --- TYT */ - if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ - 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: base_idx=idx-2; /* BAR 5*/ - } - - port = pci_resource_start(dev, base_idx) + offset; - - if ((board->flags & SPCI_FL_BASE_TABLE) == 0) - port += idx * (board->uart_offset ? board->uart_offset : 8); - - if (pci_resource_flags (dev, base_idx) & IORESOURCE_IO) { - int high_bits_offset = ((sizeof(long)-sizeof(int))*8); - req->port = port; - if (high_bits_offset) - req->port_high = port >> high_bits_offset; - else - req->port_high = 0; - return 0; - } - req->io_type = SERIAL_IO_MEM; - req->iomem_base = ioremap(port, board->uart_offset); - req->iomem_reg_shift = board->reg_shift; - req->port = 0; - return req->iomem_base ? 0 : 1; -} - /* Register the serial port(s) of a PCI card. */ static int __devinit serial_register (struct pci_dev *dev, const struct pci_device_id *id) { - struct pci_board_no_ids *board; struct parport_serial_private *priv = pci_get_drvdata (dev); - struct serial_struct serial_req; - int base_baud; - int k; - int success = 0; - - priv->ser = pci_boards[id->driver_data]; - board = &priv->ser; - if (board->init_fn && ((board->init_fn) (dev, board, 1) != 0)) - return 1; - - base_baud = board->base_baud; - if (!base_baud) - base_baud = BASE_BAUD; - memset (&serial_req, 0, sizeof (serial_req)); - - for (k = 0; k < board->num_ports; k++) { - int line; + struct pciserial_board *board; + struct serial_private *serial; - if (priv->num_ser == ARRAY_SIZE (priv->line)) { - printk (KERN_WARNING - "parport_serial: %s: only %u serial lines " - "supported (%d reported)\n", pci_name (dev), - ARRAY_SIZE (priv->line), board->num_ports); - break; - } + board = &pci_parport_serial_boards[id->driver_data]; + serial = pciserial_init_ports(dev, board); - serial_req.irq = dev->irq; - if (get_pci_port (dev, board, &serial_req, k)) - break; - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; - serial_req.baud_base = base_baud; - line = register_serial (&serial_req); - if (line < 0) { - printk (KERN_DEBUG - "parport_serial: register_serial failed\n"); - continue; - } - priv->line[priv->num_ser++] = line; - success = 1; - } + if (IS_ERR(serial)) + return PTR_ERR(serial); - return success ? 0 : 1; + priv->serial = serial; + return 0; } /* Register the parallel port(s) of a PCI card. */ @@ -411,7 +371,7 @@ static int __devinit parport_serial_pci_probe (struct pci_dev *dev, priv = kmalloc (sizeof *priv, GFP_KERNEL); if (!priv) return -ENOMEM; - priv->num_ser = priv->num_par = 0; + memset(priv, 0, sizeof(struct parport_serial_private)); pci_set_drvdata (dev, priv); err = pci_enable_device (dev); @@ -444,15 +404,12 @@ static void __devexit parport_serial_pci_remove (struct pci_dev *dev) struct parport_serial_private *priv = pci_get_drvdata (dev); int i; + pci_set_drvdata(dev, NULL); + // Serial ports - for (i = 0; i < priv->num_ser; i++) { - unregister_serial (priv->line[i]); + if (priv->serial) + pciserial_remove_ports(priv->serial); - if (priv->ser.init_fn) - (priv->ser.init_fn) (dev, &priv->ser, 0); - } - pci_set_drvdata (dev, NULL); - // Parallel ports for (i = 0; i < priv->num_par; i++) parport_pc_unregister_port (priv->port[i]); @@ -461,11 +418,47 @@ static void __devexit parport_serial_pci_remove (struct pci_dev *dev) return; } +static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state) +{ + struct parport_serial_private *priv = pci_get_drvdata(dev); + + if (priv->serial) + pciserial_suspend_ports(priv->serial); + + /* FIXME: What about parport? */ + + pci_save_state(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + return 0; +} + +static int parport_serial_pci_resume(struct pci_dev *dev) +{ + struct parport_serial_private *priv = pci_get_drvdata(dev); + + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + + /* + * The device may have been disabled. Re-enable it. + */ + pci_enable_device(dev); + + if (priv->serial) + pciserial_resume_ports(priv->serial); + + /* FIXME: What about parport? */ + + return 0; +} + static struct pci_driver parport_serial_pci_driver = { .name = "parport_serial", .id_table = parport_serial_pci_tbl, .probe = parport_serial_pci_probe, .remove = __devexit_p(parport_serial_pci_remove), + .suspend = parport_serial_pci_suspend, + .resume = parport_serial_pci_resume, }; diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 07f05e9d0955..0e21f583690e 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -34,36 +34,6 @@ #undef SERIAL_DEBUG_PCI /* - * 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) - -/* Use successive BARs (PCI base address registers), - else use offset into some specified BAR */ -#define FL_BASE_BARS 0x0008 - -/* do not assign an irq */ -#define FL_NOIRQ 0x0080 - -/* Use the Base address register size to cap number of ports */ -#define FL_REGION_SZ_CAP 0x0100 - -struct pci_board { - unsigned int flags; - unsigned int num_ports; - unsigned int base_baud; - unsigned int uart_offset; - unsigned int reg_shift; - unsigned int first_offset; -}; - -/* * init function returns: * > 0 - number of ports * = 0 - use board->num_ports @@ -75,14 +45,15 @@ struct pci_serial_quirk { u32 subvendor; u32 subdevice; int (*init)(struct pci_dev *dev); - int (*setup)(struct pci_dev *dev, struct pci_board *board, - struct uart_port *port, int idx); + int (*setup)(struct serial_private *, struct pciserial_board *, + struct uart_port *, int); void (*exit)(struct pci_dev *dev); }; #define PCI_NUM_BAR_RESOURCES 6 struct serial_private { + struct pci_dev *dev; unsigned int nr; void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; struct pci_serial_quirk *quirk; @@ -101,17 +72,18 @@ static void moan_device(const char *str, struct pci_dev *dev) } static int -setup_port(struct pci_dev *dev, struct uart_port *port, +setup_port(struct serial_private *priv, struct uart_port *port, int bar, int offset, int regshift) { - struct serial_private *priv = pci_get_drvdata(dev); + struct pci_dev *dev = priv->dev; unsigned long base, len; if (bar >= PCI_NUM_BAR_RESOURCES) return -EINVAL; + base = pci_resource_start(dev, bar); + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { - base = pci_resource_start(dev, bar); len = pci_resource_len(dev, bar); if (!priv->remapped_bar[bar]) @@ -120,13 +92,16 @@ setup_port(struct pci_dev *dev, struct uart_port *port, return -ENOMEM; port->iotype = UPIO_MEM; + port->iobase = 0; port->mapbase = base + offset; port->membase = priv->remapped_bar[bar] + offset; port->regshift = regshift; } else { - base = pci_resource_start(dev, bar) + offset; port->iotype = UPIO_PORT; - port->iobase = base; + port->iobase = base + offset; + port->mapbase = 0; + port->membase = NULL; + port->regshift = 0; } return 0; } @@ -136,7 +111,7 @@ setup_port(struct pci_dev *dev, struct uart_port *port, * Not that ugly ;) -- HW */ static int -afavlab_setup(struct pci_dev *dev, struct pci_board *board, +afavlab_setup(struct serial_private *priv, struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -149,7 +124,7 @@ afavlab_setup(struct pci_dev *dev, struct pci_board *board, offset += (idx - 4) * board->uart_offset; } - return setup_port(dev, port, bar, offset, board->reg_shift); + return setup_port(priv, port, bar, offset, board->reg_shift); } /* @@ -189,13 +164,13 @@ static int __devinit pci_hp_diva_init(struct pci_dev *dev) * 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, +pci_hp_diva_setup(struct serial_private *priv, struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int offset = board->first_offset; unsigned int bar = FL_GET_BASE(board->flags); - switch (dev->subsystem_device) { + switch (priv->dev->subsystem_device) { case PCI_DEVICE_ID_HP_DIVA_MAESTRO: if (idx == 3) idx++; @@ -212,7 +187,7 @@ pci_hp_diva_setup(struct pci_dev *dev, struct pci_board *board, offset += idx * board->uart_offset; - return setup_port(dev, port, bar, offset, board->reg_shift); + return setup_port(priv, port, bar, offset, board->reg_shift); } /* @@ -307,7 +282,7 @@ static void __devexit pci_plx9050_exit(struct pci_dev *dev) /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ static int -sbs_setup(struct pci_dev *dev, struct pci_board *board, +sbs_setup(struct serial_private *priv, struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -323,7 +298,7 @@ sbs_setup(struct pci_dev *dev, struct pci_board *board, } else /* we have only 8 ports on PMC-OCTALPRO */ return 1; - return setup_port(dev, port, bar, offset, board->reg_shift); + return setup_port(priv, port, bar, offset, board->reg_shift); } /* @@ -389,6 +364,9 @@ static void __devexit sbs_exit(struct pci_dev *dev) * - 10x cards have control registers in IO and/or memory space; * - 20x cards have control registers in standard PCI configuration space. * + * Note: all 10x cards have PCI device ids 0x10.. + * all 20x cards have PCI device ids 0x20.. + * * There are also Quartet Serial cards which use Oxford Semiconductor * 16954 quad UART PCI chip clocked by 18.432 MHz quartz. * @@ -445,24 +423,18 @@ static int pci_siig20x_init(struct pci_dev *dev) return 0; } -int pci_siig10x_fn(struct pci_dev *dev, int enable) +static int pci_siig_init(struct pci_dev *dev) { - int ret = 0; - if (enable) - ret = pci_siig10x_init(dev); - return ret; -} + unsigned int type = dev->device & 0xff00; -int pci_siig20x_fn(struct pci_dev *dev, int enable) -{ - int ret = 0; - if (enable) - ret = pci_siig20x_init(dev); - return ret; -} + if (type == 0x1000) + return pci_siig10x_init(dev); + else if (type == 0x2000) + return pci_siig20x_init(dev); -EXPORT_SYMBOL(pci_siig10x_fn); -EXPORT_SYMBOL(pci_siig20x_fn); + moan_device("Unknown SIIG card", dev); + return -ENODEV; +} /* * Timedia has an explosion of boards, and to avoid the PCI table from @@ -523,7 +495,7 @@ static int __devinit pci_timedia_init(struct pci_dev *dev) * Ugh, this is ugly as all hell --- TYT */ static int -pci_timedia_setup(struct pci_dev *dev, struct pci_board *board, +pci_timedia_setup(struct serial_private *priv, struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar = 0, offset = board->first_offset; @@ -549,14 +521,15 @@ pci_timedia_setup(struct pci_dev *dev, struct pci_board *board, bar = idx - 2; } - return setup_port(dev, port, bar, offset, board->reg_shift); + return setup_port(priv, port, 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, +titan_400l_800l_setup(struct serial_private *priv, + struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset; @@ -573,7 +546,7 @@ titan_400l_800l_setup(struct pci_dev *dev, struct pci_board *board, offset = (idx - 2) * board->uart_offset; } - return setup_port(dev, port, bar, offset, board->reg_shift); + return setup_port(priv, port, bar, offset, board->reg_shift); } static int __devinit pci_xircom_init(struct pci_dev *dev) @@ -593,7 +566,7 @@ static int __devinit pci_netmos_init(struct pci_dev *dev) } static int -pci_default_setup(struct pci_dev *dev, struct pci_board *board, +pci_default_setup(struct serial_private *priv, struct pciserial_board *board, struct uart_port *port, int idx) { unsigned int bar, offset = board->first_offset, maxnr; @@ -604,13 +577,13 @@ pci_default_setup(struct pci_dev *dev, struct pci_board *board, else offset += idx * board->uart_offset; - maxnr = (pci_resource_len(dev, bar) - board->first_offset) / + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) / (8 << board->reg_shift); if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) return 1; - return setup_port(dev, port, bar, offset, board->reg_shift); + return setup_port(priv, port, bar, offset, board->reg_shift); } /* This should be in linux/pci_ids.h */ @@ -754,152 +727,15 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .setup = sbs_setup, .exit = __devexit_p(sbs_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, + .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .init = pci_siig20x_init, + .init = pci_siig_init, .setup = pci_default_setup, }, /* @@ -990,7 +826,7 @@ static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) } static _INLINE_ int -get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +get_pci_irq(struct pci_dev *dev, struct pciserial_board *board) { if (board->flags & FL_NOIRQ) return 0; @@ -1115,7 +951,7 @@ enum pci_board_num_t { * see first lines of serial_in() and serial_out() in 8250.c */ -static struct pci_board pci_boards[] __devinitdata = { +static struct pciserial_board pci_boards[] __devinitdata = { [pbn_default] = { .flags = FL_BASE0, .num_ports = 1, @@ -1575,7 +1411,7 @@ static struct pci_board pci_boards[] __devinitdata = { * serial specs. Returns 0 on success, 1 on failure. */ static int __devinit -serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) +serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) { int num_iomem, num_port, first_port = -1, i; @@ -1640,7 +1476,8 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) } static inline int -serial_pci_matches(struct pci_board *board, struct pci_board *guessed) +serial_pci_matches(struct pciserial_board *board, + struct pciserial_board *guessed) { return board->num_ports == guessed->num_ports && @@ -1650,58 +1487,14 @@ serial_pci_matches(struct pci_board *board, struct pci_board *guessed) board->first_offset == guessed->first_offset; } -/* - * Probe one serial board. Unfortunately, there is no rhyme nor reason - * to the arrangement of serial ports on a PCI card. - */ -static int __devinit -pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +struct serial_private * +pciserial_init_ports(struct pci_dev *dev, struct pciserial_board *board) { + struct uart_port serial_port; struct serial_private *priv; - struct pci_board *board, tmp; struct pci_serial_quirk *quirk; int rc, nr_ports, i; - if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { - printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", - ent->driver_data); - return -EINVAL; - } - - board = &pci_boards[ent->driver_data]; - - rc = pci_enable_device(dev); - if (rc) - return rc; - - if (ent->driver_data == pbn_default) { - /* - * Use a copy of the pci_board entry for this; - * avoid changing entries in the table. - */ - memcpy(&tmp, board, sizeof(struct pci_board)); - board = &tmp; - - /* - * We matched one of our class entries. Try to - * determine the parameters of this board. - */ - rc = serial_pci_guess_board(dev, board); - if (rc) - goto disable; - } else { - /* - * We matched an explicit entry. If we are able to - * detect this boards settings with our heuristic, - * then we no longer need this entry. - */ - memcpy(&tmp, &pci_boards[pbn_default], sizeof(struct pci_board)); - rc = serial_pci_guess_board(dev, &tmp); - if (rc == 0 && serial_pci_matches(board, &tmp)) - moan_device("Redundant entry in serial pci_table.", - dev); - } - nr_ports = board->num_ports; /* @@ -1718,8 +1511,10 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) */ if (quirk->init) { rc = quirk->init(dev); - if (rc < 0) - goto disable; + if (rc < 0) { + priv = ERR_PTR(rc); + goto err_out; + } if (rc) nr_ports = rc; } @@ -1728,27 +1523,26 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) sizeof(unsigned int) * nr_ports, GFP_KERNEL); if (!priv) { - rc = -ENOMEM; - goto deinit; + priv = ERR_PTR(-ENOMEM); + goto err_deinit; } memset(priv, 0, sizeof(struct serial_private) + sizeof(unsigned int) * nr_ports); + priv->dev = dev; priv->quirk = quirk; - pci_set_drvdata(dev, priv); + + memset(&serial_port, 0, sizeof(struct uart_port)); + serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + serial_port.uartclk = board->base_baud * 16; + serial_port.irq = get_pci_irq(dev, board); + serial_port.dev = &dev->dev; for (i = 0; i < nr_ports; i++) { - struct uart_port serial_port; - memset(&serial_port, 0, sizeof(struct uart_port)); - - serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | - UPF_SHARE_IRQ; - serial_port.uartclk = board->base_baud * 16; - serial_port.irq = get_pci_irq(dev, board, i); - serial_port.dev = &dev->dev; - if (quirk->setup(dev, board, &serial_port, i)) + if (quirk->setup(priv, board, &serial_port, i)) break; + #ifdef SERIAL_DEBUG_PCI printk("Setup PCI port: port %x, irq %d, type %d\n", serial_port.iobase, serial_port.irq, serial_port.iotype); @@ -1763,24 +1557,21 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) priv->nr = i; - return 0; + return priv; - deinit: + err_deinit: if (quirk->exit) quirk->exit(dev); - disable: - pci_disable_device(dev); - return rc; + err_out: + return priv; } +EXPORT_SYMBOL_GPL(pciserial_init_ports); -static void __devexit pciserial_remove_one(struct pci_dev *dev) +void pciserial_remove_ports(struct serial_private *priv) { - struct serial_private *priv = pci_get_drvdata(dev); struct pci_serial_quirk *quirk; int i; - pci_set_drvdata(dev, NULL); - for (i = 0; i < priv->nr; i++) serial8250_unregister_port(priv->line[i]); @@ -1793,25 +1584,123 @@ static void __devexit pciserial_remove_one(struct pci_dev *dev) /* * Find the exit quirks. */ - quirk = find_quirk(dev); + quirk = find_quirk(priv->dev); if (quirk->exit) - quirk->exit(dev); + quirk->exit(priv->dev); + + kfree(priv); +} +EXPORT_SYMBOL_GPL(pciserial_remove_ports); + +void pciserial_suspend_ports(struct serial_private *priv) +{ + int i; + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_suspend_port(priv->line[i]); +} +EXPORT_SYMBOL_GPL(pciserial_suspend_ports); + +void pciserial_resume_ports(struct serial_private *priv) +{ + int i; + + /* + * Ensure that the board is correctly configured. + */ + if (priv->quirk->init) + priv->quirk->init(priv->dev); + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_resume_port(priv->line[i]); +} +EXPORT_SYMBOL_GPL(pciserial_resume_ports); + +/* + * Probe one serial board. Unfortunately, there is no rhyme nor reason + * to the arrangement of serial ports on a PCI card. + */ +static int __devinit +pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct serial_private *priv; + struct pciserial_board *board, tmp; + int rc; + + if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { + printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", + ent->driver_data); + return -EINVAL; + } + + board = &pci_boards[ent->driver_data]; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (ent->driver_data == pbn_default) { + /* + * Use a copy of the pci_board entry for this; + * avoid changing entries in the table. + */ + memcpy(&tmp, board, sizeof(struct pciserial_board)); + board = &tmp; + + /* + * We matched one of our class entries. Try to + * determine the parameters of this board. + */ + rc = serial_pci_guess_board(dev, board); + if (rc) + goto disable; + } else { + /* + * We matched an explicit entry. If we are able to + * detect this boards settings with our heuristic, + * then we no longer need this entry. + */ + memcpy(&tmp, &pci_boards[pbn_default], + sizeof(struct pciserial_board)); + rc = serial_pci_guess_board(dev, &tmp); + if (rc == 0 && serial_pci_matches(board, &tmp)) + moan_device("Redundant entry in serial pci_table.", + dev); + } + priv = pciserial_init_ports(dev, board); + if (!IS_ERR(priv)) { + pci_set_drvdata(dev, priv); + return 0; + } + + rc = PTR_ERR(priv); + + disable: pci_disable_device(dev); + return rc; +} - kfree(priv); +static void __devexit pciserial_remove_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + pci_set_drvdata(dev, NULL); + + pciserial_remove_ports(priv); + + pci_disable_device(dev); } static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state) { struct serial_private *priv = pci_get_drvdata(dev); - if (priv) { - int i; + if (priv) + pciserial_suspend_ports(priv); - for (i = 0; i < priv->nr; i++) - serial8250_suspend_port(priv->line[i]); - } pci_save_state(dev); pci_set_power_state(dev, pci_choose_state(dev, state)); return 0; @@ -1825,21 +1714,12 @@ static int pciserial_resume_one(struct pci_dev *dev) pci_restore_state(dev); if (priv) { - int i; - /* * The device may have been disabled. Re-enable it. */ pci_enable_device(dev); - /* - * 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]); + pciserial_resume_ports(priv); } return 0; } |