From: Bjorn Helgaas There's a bugzilla entry for this here: http://bugzilla.kernel.org/show_bug.cgi?id=4334 This should fix all the problems I know about with Netmos combo cards: - 9735, 9835, and 9855 are not supported - combo cards with parallel are erroneously claimed by serial driver - serial and parport_serial blindly probe for ports parport_pc: Sort Netmos device IDs, no functional change. parport_serial: Previously supported 9735 and 9835. Add 9745, 9845, 9855, and add init hooks to discover how many serial/parallel ports are actually present (the boards are available in various configs). Add protection for overflow of static tables. quirks: Detect Netmos combo (parallel + serial) cards and change class from SERIAL to OTHER to prevent serial driver from claiming them. 8250: Add init hook to discover the number of serial ports present. This prevents us from poking at unused BARs. pci_ids: Add Netmos 9745, 9845, and sort. Signed-off-by: Bjorn Helgaas Signed-off-by: Andrew Morton --- 25-akpm/drivers/parport/parport_pc.c | 16 ++--- 25-akpm/drivers/parport/parport_serial.c | 92 +++++++++++++++++++++++-------- 25-akpm/drivers/pci/pci.ids | 17 ++++- 25-akpm/drivers/pci/quirks.c | 34 +++++++++++ 25-akpm/drivers/serial/8250_pci.c | 21 +++++++ 25-akpm/include/linux/pci_ids.h | 6 +- 6 files changed, 151 insertions(+), 35 deletions(-) diff -puN drivers/parport/parport_pc.c~netmos-parallel-serial-combo-support drivers/parport/parport_pc.c --- 25/drivers/parport/parport_pc.c~netmos-parallel-serial-combo-support 2005-03-23 01:31:43.000000000 -0800 +++ 25-akpm/drivers/parport/parport_pc.c 2005-03-23 01:31:44.000000000 -0800 @@ -2733,11 +2733,11 @@ enum parport_pc_pci_cards { aks_0100, mobility_pp, netmos_9705, + netmos_9715, + netmos_9755, netmos_9805, netmos_9815, netmos_9855, - netmos_9755, - netmos_9715 }; @@ -2808,11 +2808,11 @@ static struct parport_pc_pci { /* aks_0100 */ { 1, { { 0, -1 }, } }, /* mobility_pp */ { 1, { { 0, 1 }, } }, /* netmos_9705 */ { 1, { { 0, -1 }, } }, /* untested */ + /* netmos_9715 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ + /* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ /* netmos_9805 */ { 1, { { 0, -1 }, } }, /* untested */ /* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ /* netmos_9855 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ - /* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ - /* netmos_9715 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ }; static struct pci_device_id parport_pc_pci_tbl[] = { @@ -2885,16 +2885,16 @@ static struct pci_device_id parport_pc_p /* NetMos communication controllers */ { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 }, { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl); diff -puN drivers/parport/parport_serial.c~netmos-parallel-serial-combo-support drivers/parport/parport_serial.c --- 25/drivers/parport/parport_serial.c~netmos-parallel-serial-combo-support 2005-03-23 01:31:43.000000000 -0800 +++ 25-akpm/drivers/parport/parport_serial.c 2005-03-23 01:31:44.000000000 -0800 @@ -33,8 +33,7 @@ enum parport_pc_pci_cards { titan_110l = 0, titan_210l, - netmos_9735, - netmos_9835, + netmos_9xx5_combo, avlab_1s1p, avlab_1s1p_650, avlab_1s1p_850, @@ -51,9 +50,8 @@ enum parport_pc_pci_cards { siig_2s1p_20x, }; - /* each element directly indexed from enum list, above */ -static struct parport_pc_pci { +struct parport_pc_pci { int numports; struct { /* BAR (base address registers) numbers in the config space header */ @@ -65,16 +63,30 @@ static struct parport_pc_pci { /* If set, this is called immediately after pci_enable_device. * If it returns non-zero, no probing will take place and the * ports will not be used. */ - int (*preinit_hook) (struct pci_dev *pdev, int autoirq, int autodma); + int (*preinit_hook) (struct pci_dev *pdev, struct parport_pc_pci *card, + int autoirq, int autodma); /* If set, this is called after probing for ports. If 'failed' * is non-zero we couldn't use any of the ports. */ - void (*postinit_hook) (struct pci_dev *pdev, int failed); -} cards[] __devinitdata = { + void (*postinit_hook) (struct pci_dev *pdev, + struct parport_pc_pci *card, int failed); +}; + +static int __devinit netmos_parallel_init(struct pci_dev *dev, struct parport_pc_pci *card, int autoirq, int autodma) +{ + /* + * Netmos uses the subdevice ID to indicate the number of parallel + * and serial ports. The form is 0x00PS, where

is the number of + * parallel ports and is the number of serial ports. + */ + card->numports = (dev->subsystem_device & 0xf0) >> 4; + return 0; +} + +static struct parport_pc_pci cards[] __devinitdata = { /* titan_110l */ { 1, { { 3, -1 }, } }, /* titan_210l */ { 1, { { 3, -1 }, } }, - /* netmos_9735 (not tested) */ { 1, { { 2, -1 }, } }, - /* netmos_9835 */ { 1, { { 2, -1 }, } }, + /* netmos_9xx5_combo */ { 1, { { 2, -1 }, }, netmos_parallel_init }, /* avlab_1s1p */ { 1, { { 1, 2}, } }, /* avlab_1s1p_650 */ { 1, { { 1, 2}, } }, /* avlab_1s1p_850 */ { 1, { { 1, 2}, } }, @@ -98,9 +110,17 @@ static struct pci_device_id parport_seri { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L, PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9735 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9745, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9835 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ { 0x14db, 0x2110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p}, { 0x14db, 0x2111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p_650}, @@ -167,6 +187,12 @@ static int __devinit siig20x_init_fn(str 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, @@ -180,8 +206,7 @@ static struct pci_board_no_ids pci_board /* titan_110l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 1, 921600 }, /* titan_210l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, -/* netmos_9735 (n/t)*/ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, -/* netmos_9835 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, +/* netmos_9xx5_combo */ { SPCI_FL_BASE0 | 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 }, @@ -204,6 +229,7 @@ struct parport_serial_private { struct pci_board_no_ids ser; int num_par; struct parport *port[PARPORT_MAX]; + struct parport_pc_pci par; }; static int __devinit get_pci_port (struct pci_dev *dev, @@ -271,14 +297,15 @@ static int __devinit get_pci_port (struc static int __devinit serial_register (struct pci_dev *dev, const struct pci_device_id *id) { - struct pci_board_no_ids *board = &pci_boards[id->driver_data]; + 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 = *board; + priv->ser = pci_boards[id->driver_data]; + board = &priv->ser; if (board->init_fn && ((board->init_fn) (dev, board, 1) != 0)) return 1; @@ -289,6 +316,15 @@ static int __devinit serial_register (st for (k = 0; k < board->num_ports; k++) { int line; + + 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; + } + serial_req.irq = dev->irq; if (get_pci_port (dev, board, &serial_req, k)) break; @@ -311,19 +347,31 @@ static int __devinit serial_register (st static int __devinit parport_register (struct pci_dev *dev, const struct pci_device_id *id) { + struct parport_pc_pci *card; struct parport_serial_private *priv = pci_get_drvdata (dev); int i = id->driver_data, n; int success = 0; - if (cards[i].preinit_hook && - cards[i].preinit_hook (dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) + priv->par = cards[id->driver_data]; + card = &priv->par; + if (card->preinit_hook && + card->preinit_hook (dev, card, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) return -ENODEV; - for (n = 0; n < cards[i].numports; n++) { + for (n = 0; n < card->numports; n++) { struct parport *port; - int lo = cards[i].addr[n].lo; - int hi = cards[i].addr[n].hi; + int lo = card->addr[n].lo; + int hi = card->addr[n].hi; unsigned long io_lo, io_hi; + + if (priv->num_par == ARRAY_SIZE (priv->port)) { + printk (KERN_WARNING + "parport_serial: %s: only %u parallel ports " + "supported (%d reported)\n", pci_name (dev), + ARRAY_SIZE (priv->port), card->numports); + break; + } + io_lo = pci_resource_start (dev, lo); io_hi = 0; if ((hi >= 0) && (hi <= 6)) @@ -345,8 +393,8 @@ static int __devinit parport_register (s } } - if (cards[i].postinit_hook) - cards[i].postinit_hook (dev, !success); + if (card->postinit_hook) + card->postinit_hook (dev, card, !success); return success ? 0 : 1; } diff -puN drivers/pci/pci.ids~netmos-parallel-serial-combo-support drivers/pci/pci.ids --- 25/drivers/pci/pci.ids~netmos-parallel-serial-combo-support 2005-03-23 01:31:43.000000000 -0800 +++ 25-akpm/drivers/pci/pci.ids 2005-03-23 01:31:44.000000000 -0800 @@ -9903,14 +9903,25 @@ 6565 6565 9710 NetMos Technology 7780 USB IRDA-port - 9815 PCI 9815 Multi-I/O Controller + 9705 PCI 9705 Parallel Port + 9715 PCI 9715 Dual Parallel Port + 9735 PCI 9735 Multi-I/O Controller + 1000 0002 0P2S (2 serial) + 1000 0012 1P2S (1 parallel + 2 serial) + 9745 PCI 9745 Multi-I/O Controller + 1000 0002 0P2S (2 serial) + 1000 0012 1P2S (1 parallel + 2 serial) + 9755 PCI 9755 Parallel Port and ISA Bridge + 9805 PCI 9805 Parallel Port + 9815 PCI 9815 Dual Parallel Port 1000 0020 2P0S (2 port parallel adaptor) 9835 PCI 9835 Multi-I/O Controller - 1000 0002 2S (16C550 UART) + 1000 0002 0P2S (16C550 UART) 1000 0012 1P2S 9845 PCI 9845 Multi-I/O Controller 1000 0004 0P4S (4 port 16550A serial card) - 1000 0006 0P6S (6 port 16550a serial card) + 1000 0006 0P6S (6 port 16550A serial card) + 1000 0014 1P4S (4 port 16550A serial card + parallel) 9855 PCI 9855 Multi-I/O Controller 1000 0014 1P4S 9902 Stargen Inc. diff -puN drivers/pci/quirks.c~netmos-parallel-serial-combo-support drivers/pci/quirks.c --- 25/drivers/pci/quirks.c~netmos-parallel-serial-combo-support 2005-03-23 01:31:44.000000000 -0800 +++ 25-akpm/drivers/pci/quirks.c 2005-03-23 01:31:44.000000000 -0800 @@ -1259,6 +1259,40 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quirk_pcie_mch ); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quirk_pcie_mch ); +static void __devinit quirk_netmos(struct pci_dev *dev) +{ + unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4; + unsigned int num_serial = dev->subsystem_device & 0xf; + + /* + * These Netmos parts are multiport serial devices with optional + * parallel ports. Even when parallel ports are present, they + * are identified as class SERIAL, which means the serial driver + * will claim them. To prevent this, mark them as class OTHER. + * These combo devices should be claimed by parport_serial. + * + * The subdevice ID is of the form 0x00PS, where

is the number + * of parallel ports and is the number of serial ports. + */ + switch (dev->device) { + case PCI_DEVICE_ID_NETMOS_9735: + case PCI_DEVICE_ID_NETMOS_9745: + case PCI_DEVICE_ID_NETMOS_9835: + case PCI_DEVICE_ID_NETMOS_9845: + case PCI_DEVICE_ID_NETMOS_9855: + if ((dev->class >> 8) == PCI_CLASS_COMMUNICATION_SERIAL && + num_parallel) { + printk(KERN_INFO "PCI: Netmos %04x (%u parallel, " + "%u serial); changing class SERIAL to OTHER " + "(use parport_serial)\n", + dev->device, num_parallel, num_serial); + dev->class = (PCI_CLASS_COMMUNICATION_OTHER << 8) | + (dev->class & 0xff); + } + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, quirk_netmos); + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { while (f < end) { diff -puN drivers/serial/8250_pci.c~netmos-parallel-serial-combo-support drivers/serial/8250_pci.c --- 25/drivers/serial/8250_pci.c~netmos-parallel-serial-combo-support 2005-03-23 01:31:44.000000000 -0800 +++ 25-akpm/drivers/serial/8250_pci.c 2005-03-23 01:31:44.000000000 -0800 @@ -577,6 +577,16 @@ static int __devinit pci_xircom_init(str return 0; } +static int __devinit pci_netmos_init(struct pci_dev *dev) +{ + /* subdevice 0x00PS means

parallel, serial */ + unsigned int num_serial = dev->subsystem_device & 0xf; + + if (num_serial == 0) + return -ENODEV; + return num_serial; +} + static int pci_default_setup(struct pci_dev *dev, struct pci_board *board, struct uart_port *port, int idx) @@ -934,6 +944,17 @@ static struct pci_serial_quirk pci_seria .setup = pci_default_setup, }, /* + * Netmos cards + */ + { + .vendor = PCI_VENDOR_ID_NETMOS, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_netmos_init, + .setup = pci_default_setup, + }, + /* * Default "match everything" terminator entry */ { diff -puN include/linux/pci_ids.h~netmos-parallel-serial-combo-support include/linux/pci_ids.h --- 25/include/linux/pci_ids.h~netmos-parallel-serial-combo-support 2005-03-23 01:31:44.000000000 -0800 +++ 25-akpm/include/linux/pci_ids.h 2005-03-23 01:31:44.000000000 -0800 @@ -2537,13 +2537,15 @@ #define PCI_VENDOR_ID_NETMOS 0x9710 #define PCI_DEVICE_ID_NETMOS_9705 0x9705 +#define PCI_DEVICE_ID_NETMOS_9715 0x9715 #define PCI_DEVICE_ID_NETMOS_9735 0x9735 +#define PCI_DEVICE_ID_NETMOS_9745 0x9745 +#define PCI_DEVICE_ID_NETMOS_9755 0x9755 #define PCI_DEVICE_ID_NETMOS_9805 0x9805 #define PCI_DEVICE_ID_NETMOS_9815 0x9815 #define PCI_DEVICE_ID_NETMOS_9835 0x9835 +#define PCI_DEVICE_ID_NETMOS_9845 0x9845 #define PCI_DEVICE_ID_NETMOS_9855 0x9855 -#define PCI_DEVICE_ID_NETMOS_9755 0x9755 -#define PCI_DEVICE_ID_NETMOS_9715 0x9715 #define PCI_SUBVENDOR_ID_EXSYS 0xd84d #define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 _