public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* Need break driver<-->pci-device automatic association
@ 2005-03-18  8:57 Jacques Goldberg
  2005-03-18 13:14 ` Alan Cox
  2005-03-18 16:51 ` Greg KH
  0 siblings, 2 replies; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-18  8:57 UTC (permalink / raw)
  To: linux-kernel


 Not subscribing because this is a one time question.
 Please Cc: to the reply address above , Jacques.Goldberg@cern.ch

 Several winmodem devices come with a hardware burnt-in identification
misleading the system to load the serial driver.
 As a result, it is not possible to load the special driver because the
PCI device is grabbed by the serial driver.

 Question: is there a way, as of kernels 2.6.10 and above, to release the
device from the serial driver, without having to recompile the kernel?

 We know how to attain the goal by patching 8250_pci.c but doing this will
quickly generate chaos. We know to detect the condition in the custom
driver. The fix would be trivial if we could  break the tie with the
serial driver.

                                            Thanks - Jacques

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-18  8:57 Need break driver<-->pci-device automatic association Jacques Goldberg
@ 2005-03-18 13:14 ` Alan Cox
  2005-03-18 13:58   ` Jacques Goldberg
  2005-03-18 16:51 ` Greg KH
  1 sibling, 1 reply; 16+ messages in thread
From: Alan Cox @ 2005-03-18 13:14 UTC (permalink / raw)
  To: Jacques Goldberg; +Cc: Linux Kernel Mailing List

On Gwe, 2005-03-18 at 08:57, Jacques Goldberg wrote:
>  Question: is there a way, as of kernels 2.6.10 and above, to release the
> device from the serial driver, without having to recompile the kernel?

There is an ugly way (fake a hot unplug 8)) but if you want to do it
properly you need to get the relevant pci check into the serial driver
proper by submitting it to Russell King. That way the serial driver can
skip the PCI devices that turn out to be modems


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-18 13:14 ` Alan Cox
@ 2005-03-18 13:58   ` Jacques Goldberg
  2005-03-18 14:39     ` Stuart MacDonald
  0 siblings, 1 reply; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-18 13:58 UTC (permalink / raw)
  To: Alan Cox; +Cc: Linux Kernel Mailing List

On Fri, 18 Mar 2005, Alan Cox wrote:

> On Gwe, 2005-03-18 at 08:57, Jacques Goldberg wrote:
> >Question: is there a way, as of kernels 2.6.10 and above, to release the
> > device from the serial driver, without having to recompile the kernel?
>
> There is an ugly way (fake a hot unplug 8)) butif you want to do it
> properly you need to get the relevant pci check into the serial driver
> proper by submitting it to Russell King. That way the serial driver can
> skip the PCI devices that turn out to be modems
>
  Thank you very much.
  To be ugly or to never be up to date, that's the question.
  We did patch 8250_pci.c but there is no way to build a stable list of
the devices to be handled that way.
  We will thus spend some time on the hot unplug solution.
  This is my very last question: is there a script able to do that? Google
quotes their existence but no link found. Or a doc showing how to code
that in a program?

  Many many thanks - Jacques

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: Need break driver<-->pci-device automatic association
  2005-03-18 13:58   ` Jacques Goldberg
@ 2005-03-18 14:39     ` Stuart MacDonald
  2005-03-19 18:18       ` Jacques Goldberg
  0 siblings, 1 reply; 16+ messages in thread
From: Stuart MacDonald @ 2005-03-18 14:39 UTC (permalink / raw)
  To: 'Jacques Goldberg', 'Alan Cox'
  Cc: 'Linux Kernel Mailing List'

From: Jacques Goldberg
>   To be ugly or to never be up to date, that's the question.
>   We did patch 8250_pci.c but there is no way to build a 
> stable list of
> the devices to be handled that way.
>   We will thus spend some time on the hot unplug solution.

I think what you want might be accomplished if the serial driver was
compiled as a module. Then have your driver grab all the PCI devices
it wants, and they shouldn't be grabbed by the serial driver when it
loads. If you can't get your driver to load before the serial driver
for whatever reason, unloading the serial driver should give up the
devices it had claimed.

..Stu


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-18  8:57 Need break driver<-->pci-device automatic association Jacques Goldberg
  2005-03-18 13:14 ` Alan Cox
@ 2005-03-18 16:51 ` Greg KH
  2005-03-19 18:33   ` Jacques Goldberg
  1 sibling, 1 reply; 16+ messages in thread
From: Greg KH @ 2005-03-18 16:51 UTC (permalink / raw)
  To: Jacques Goldberg; +Cc: linux-kernel

On Fri, Mar 18, 2005 at 10:57:14AM +0200, Jacques Goldberg wrote:
> 
>  Not subscribing because this is a one time question.
>  Please Cc: to the reply address above , Jacques.Goldberg@cern.ch
> 
>  Several winmodem devices come with a hardware burnt-in identification
> misleading the system to load the serial driver.
>  As a result, it is not possible to load the special driver because the
> PCI device is grabbed by the serial driver.
> 
>  Question: is there a way, as of kernels 2.6.10 and above, to release the
> device from the serial driver, without having to recompile the kernel?

I have a patch around somewhere that provides a way to do this from
userspace and sysfs, but it's racy and not really acceptable in it's
current form.  Search the lkml archives for details and the patch if you
are interested.

But that would require you to rebuild your kernel :)

And yes, we are working on adding this support for mainline, still
working on it...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: Need break driver<-->pci-device automatic association
  2005-03-18 14:39     ` Stuart MacDonald
@ 2005-03-19 18:18       ` Jacques Goldberg
  0 siblings, 0 replies; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-19 18:18 UTC (permalink / raw)
  To: Stuart MacDonald; +Cc: 'Alan Cox', 'Linux Kernel Mailing List'


   Stuart,
   Many thanks for this interesting approach.
   A huge advantage is that it can be implemented as a script.
   But:
   -it still requires the Linux newcomer who wants his modem to work, to
recompile her/his kernel - something which frightens beginners.
   -it implies that when adding a serial device such as an other modem, or
a  serial scanner, the kernel ought again to be recompiled according to
the new hardware configuration.
   -and at each kernel upgrade, often automatic in large organizations,
the kernel must again be tailored to the configuration.

                                         Thanks again - Jacques

On Fri, 18 Mar 2005, Stuart MacDonald wrote:

> From: Jacques Goldberg
> > To be ugly or to never be up to date, that's the question.
> > We did patch 8250_pci.c but there is no way to build a
> > stable list of
> > the devices to be handled that way.
> > We will thus spend some time on the hot unplug solution.
>
> I think what you want might be accomplished if the serial driver was
> compiled as a module. Then have your driver grab all the PCI devices
> it wants, and they shouldn't be grabbed by the serial driver when it
> loads. If you can't get your driver to load before the serial driver
> for whatever reason, unloading the serial driver should give up the
> devices it had claimed.
>
> ..Stu
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-18 16:51 ` Greg KH
@ 2005-03-19 18:33   ` Jacques Goldberg
  2005-03-21  8:16     ` Andrey Panin
  0 siblings, 1 reply; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-19 18:33 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel

[-- Attachment #1: Type: TEXT/PLAIN, Size: 703 bytes --]


   Good news!
   That's really what is needed (mainline).
   I attach the file which Sasha, author or the lmodem driver, has
modified and then it works for the chips hard-wired in the routine.
   To locate the patched area, look for 5457
   I am an experimental physicist, not a computer expert. If this kind of
algorithm could be implemented in the mainlin 8250_pci.c , then I would
dream of looking for an /etc/config file containing such a list, whose
objects would be skipped by the serial driver.
   Sorry to repeat myself, but avoiding the need for the users to
recompile a kernel should be the first worry when implementing a solution.

                                          Thanks - Jacques

[-- Attachment #2: Type: TEXT/PLAIN, Size: 57932 bytes --]

/*
 *  linux/drivers/char/8250_pci.c
 *
 *  Probe module for 8250/16550-type PCI serial ports.
 *
 *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
 *
 *  Copyright (C) 2001 Russell King, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 *  $Id: 8250_pci.c,v 1.28 2002/11/02 11:14:18 rmk Exp $
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/tty.h>
#include <linux/serial_core.h>
#include <linux/8250_pci.h>
#include <linux/bitops.h>

#include <asm/byteorder.h>
#include <asm/io.h>

#include "8250.h"

/*
 * 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
 *  < 0 - error
 */
struct pci_serial_quirk {
	u32	vendor;
	u32	device;
	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);
	void	(*exit)(struct pci_dev *dev);
};

#define PCI_NUM_BAR_RESOURCES	6

struct serial_private {
	unsigned int		nr;
	void __iomem		*remapped_bar[PCI_NUM_BAR_RESOURCES];
	struct pci_serial_quirk	*quirk;
	int			line[0];
};

static void moan_device(const char *str, struct pci_dev *dev)
{
	printk(KERN_WARNING "%s: %s\n"
	       KERN_WARNING "Please send the output of lspci -vv, this\n"
	       KERN_WARNING "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n"
	       KERN_WARNING "manufacturer and name of serial board or\n"
	       KERN_WARNING "modem board to rmk+serial@arm.linux.org.uk.\n",
	       pci_name(dev), str, dev->vendor, dev->device,
	       dev->subsystem_vendor, dev->subsystem_device);
}

static int
setup_port(struct pci_dev *dev, struct uart_port *port,
	   int bar, int offset, int regshift)
{
	struct serial_private *priv = pci_get_drvdata(dev);
	unsigned long base, len;

	if (bar >= PCI_NUM_BAR_RESOURCES)
		return -EINVAL;

	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])
			priv->remapped_bar[bar] = ioremap(base, len);
		if (!priv->remapped_bar[bar])
			return -ENOMEM;

		port->iotype = UPIO_MEM;
		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;
	}
	return 0;
}

/*
 * AFAVLAB uses a different mixture of BARs and offsets
 * Not that ugly ;) -- HW
 */
static int
afavlab_setup(struct pci_dev *dev, struct pci_board *board,
	      struct uart_port *port, int idx)
{
	unsigned int bar, offset = board->first_offset;
	
	bar = FL_GET_BASE(board->flags);
	if (idx < 4)
		bar += idx;
	else {
		bar = 4;
		offset += (idx - 4) * board->uart_offset;
	}

	return setup_port(dev, port, bar, offset, board->reg_shift);
}

/*
 * HP's Remote Management Console.  The Diva chip came in several
 * different versions.  N-class, L2000 and A500 have two Diva chips, each
 * with 3 UARTs (the third UART on the second chip is unused).  Superdome
 * and Keystone have one Diva chip with 3 UARTs.  Some later machines have
 * one Diva chip, but it has been expanded to 5 UARTs.
 */
static int __devinit pci_hp_diva_init(struct pci_dev *dev)
{
	int rc = 0;

	switch (dev->subsystem_device) {
	case PCI_DEVICE_ID_HP_DIVA_TOSCA1:
	case PCI_DEVICE_ID_HP_DIVA_HALFDOME:
	case PCI_DEVICE_ID_HP_DIVA_KEYSTONE:
	case PCI_DEVICE_ID_HP_DIVA_EVEREST:
		rc = 3;
		break;
	case PCI_DEVICE_ID_HP_DIVA_TOSCA2:
		rc = 2;
		break;
	case PCI_DEVICE_ID_HP_DIVA_MAESTRO:
		rc = 4;
		break;
	case PCI_DEVICE_ID_HP_DIVA_POWERBAR:
		rc = 1;
		break;
	}

	return rc;
}

/*
 * HP's Diva chip puts the 4th/5th serial port further out, and
 * 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,
	      struct uart_port *port, int idx)
{
	unsigned int offset = board->first_offset;
	unsigned int bar = FL_GET_BASE(board->flags);

	switch (dev->subsystem_device) {
	case PCI_DEVICE_ID_HP_DIVA_MAESTRO:
		if (idx == 3)
			idx++;
		break;
	case PCI_DEVICE_ID_HP_DIVA_EVEREST:
		if (idx > 0)
			idx++;
		if (idx > 2)
			idx++;
		break;
	}
	if (idx > 2)
		offset = 0x18;

	offset += idx * board->uart_offset;

	return setup_port(dev, port, bar, offset, board->reg_shift);
}

/*
 * Added for EKF Intel i960 serial boards
 */
static int __devinit pci_inteli960ni_init(struct pci_dev *dev)
{
	unsigned long oldval;

	if (!(dev->subsystem_device & 0x1000))
		return -ENODEV;

	/* is firmware started? */
	pci_read_config_dword(dev, 0x44, (void*) &oldval); 
	if (oldval == 0x00001000L) { /* RESET value */ 
		printk(KERN_DEBUG "Local i960 firmware missing");
		return -ENODEV;
	}
	return 0;
}

/*
 * 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.
 */
static int __devinit pci_plx9050_init(struct pci_dev *dev)
{
	u8 irq_config;
	void __iomem *p;

	if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) {
		moan_device("no memory in bar 0", dev);
		return 0;
	}

	irq_config = 0x41;
	if (dev->vendor == PCI_VENDOR_ID_PANACOM)
		irq_config = 0x43;
	if ((dev->vendor == PCI_VENDOR_ID_PLX) &&
	    (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) {
		/*
		 * As the megawolf cards have the int pins active
		 * high, and have 2 UART chips, both ints must be
		 * enabled on the 9050. Also, the UARTS are set in
		 * 16450 mode by default, so we have to enable the
		 * 16C950 'enhanced' mode so that we can use the
		 * deep FIFOs
		 */
		irq_config = 0x5b;
	}

	/*
	 * enable/disable interrupts
	 */
	p = ioremap(pci_resource_start(dev, 0), 0x80);
	if (p == NULL)
		return -ENOMEM;
	writel(irq_config, p + 0x4c);

	/*
	 * Read the register back to ensure that it took effect.
	 */
	readl(p + 0x4c);
	iounmap(p);

	return 0;
}

static void __devexit pci_plx9050_exit(struct pci_dev *dev)
{
	u8 __iomem *p;

	if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0)
		return;

	/*
	 * disable interrupts
	 */
	p = ioremap(pci_resource_start(dev, 0), 0x80);
	if (p != NULL) {
		writel(0, p + 0x4c);

		/*
		 * Read the register back to ensure that it took effect.
		 */
		readl(p + 0x4c);
		iounmap(p);
	}
}

/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
static int
sbs_setup(struct pci_dev *dev, struct pci_board *board,
		struct uart_port *port, int idx)
{
	unsigned int bar, offset = board->first_offset;

	bar = 0;

	if (idx < 4) {
		/* first four channels map to 0, 0x100, 0x200, 0x300 */
		offset += idx * board->uart_offset;
	} else if (idx < 8) {
		/* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */
		offset += idx * board->uart_offset + 0xC00;
	} else /* we have only 8 ports on PMC-OCTALPRO */
		return 1;

	return setup_port(dev, port, bar, offset, board->reg_shift);
}

/*
* This does initialization for PMC OCTALPRO cards:
* maps the device memory, resets the UARTs (needed, bc
* if the module is removed and inserted again, the card
* is in the sleep mode) and enables global interrupt.
*/

/* global control register offset for SBS PMC-OctalPro */
#define OCT_REG_CR_OFF		0x500

static int __devinit sbs_init(struct pci_dev *dev)
{
	u8 __iomem *p;

	p = ioremap(pci_resource_start(dev, 0),pci_resource_len(dev,0));

	if (p == NULL)
		return -ENOMEM;
	/* Set bit-4 Control Register (UART RESET) in to reset the uarts */
	writeb(0x10,p + OCT_REG_CR_OFF);
	udelay(50);
	writeb(0x0,p + OCT_REG_CR_OFF);

	/* Set bit-2 (INTENABLE) of Control Register */
	writeb(0x4, p + OCT_REG_CR_OFF);
	iounmap(p);

	return 0;
}

/*
 * Disables the global interrupt of PMC-OctalPro
 */

static void __devexit sbs_exit(struct pci_dev *dev)
{
	u8 __iomem *p;

	p = ioremap(pci_resource_start(dev, 0),pci_resource_len(dev,0));
	if (p != NULL) {
		writeb(0, p + OCT_REG_CR_OFF);
	}
	iounmap(p);
}

/*
 * SIIG serial cards have an PCI interface chip which also controls
 * the UART clocking frequency. Each UART can be clocked independently
 * (except cards equiped with 4 UARTs) and initial clocking settings
 * are stored in the EEPROM chip. It can cause problems because this
 * version of serial driver doesn't support differently clocked UART's
 * on single PCI card. To prevent this, initialization functions set
 * high frequency clocking for all UART's on given card. It is safe (I
 * hope) because it doesn't touch EEPROM settings to prevent conflicts
 * with other OSes (like M$ DOS).
 *
 *  SIIG support added by Andrey Panin <pazke@donpac.ru>, 10/1999
 * 
 * There is two family of SIIG serial cards with different PCI
 * interface chip and different configuration methods:
 *     - 10x cards have control registers in IO and/or memory space;
 *     - 20x cards have control registers in standard PCI configuration space.
 *
 * Note: some SIIG cards are probed by the parport_serial object.
 */

#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 pci_siig10x_init(struct pci_dev *dev)
{
	u16 data;
	void __iomem *p;

	switch (dev->device & 0xfff8) {
	case PCI_DEVICE_ID_SIIG_1S_10x:	/* 1S */
		data = 0xffdf;
		break;
	case PCI_DEVICE_ID_SIIG_2S_10x:	/* 2S, 2S1P */
		data = 0xf7ff;
		break;
	default:			/* 1S1P, 4S */
		data = 0xfffb;
		break;
	}

	p = ioremap(pci_resource_start(dev, 0), 0x80);
	if (p == NULL)
		return -ENOMEM;

	writew(readw(p + 0x28) & data, p + 0x28);
	readw(p + 0x28);
	iounmap(p);
	return 0;
}

#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 pci_siig20x_init(struct pci_dev *dev)
{
	u8 data;

	/* Change clock frequency for the first UART. */
	pci_read_config_byte(dev, 0x6f, &data);
	pci_write_config_byte(dev, 0x6f, data & 0xef);

	/* If this card has 2 UART, we have to do the same with second UART. */
	if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) ||
	    ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) {
		pci_read_config_byte(dev, 0x73, &data);
		pci_write_config_byte(dev, 0x73, data & 0xef);
	}
	return 0;
}

int pci_siig10x_fn(struct pci_dev *dev, int enable)
{
	int ret = 0;
	if (enable)
		ret = pci_siig10x_init(dev);
	return ret;
}

int pci_siig20x_fn(struct pci_dev *dev, int enable)
{
	int ret = 0;
	if (enable)
		ret = pci_siig20x_init(dev);
	return ret;
}

EXPORT_SYMBOL(pci_siig10x_fn);
EXPORT_SYMBOL(pci_siig20x_fn);

/*
 * Timedia has an explosion of boards, and to avoid the PCI table from
 * growing *huge*, we use this function to collapse some 70 entries
 * in the PCI table into one, for sanity's and compactness's sake.
 */
static unsigned short timedia_single_port[] = {
	0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0
};

static unsigned short timedia_dual_port[] = {
	0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085,
	0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, 
	0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, 
	0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079,
	0xD079, 0
};

static unsigned short timedia_quad_port[] = {
	0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, 
	0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, 
	0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056,
	0xB157, 0
};

static unsigned short timedia_eight_port[] = {
	0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, 
	0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0
};

static struct timedia_struct {
	int num;
	unsigned short *ids;
} timedia_data[] = {
	{ 1, timedia_single_port },
	{ 2, timedia_dual_port },
	{ 4, timedia_quad_port },
	{ 8, timedia_eight_port },
	{ 0, NULL }
};

static int __devinit pci_timedia_init(struct pci_dev *dev)
{
	unsigned short *ids;
	int i, j;

	for (i = 0; timedia_data[i].num; i++) {
		ids = timedia_data[i].ids;
		for (j = 0; ids[j]; j++)
			if (dev->subsystem_device == ids[j])
				return timedia_data[i].num;
	}
	return 0;
}

/*
 * Timedia/SUNIX uses a mixture of BARs and offsets
 * Ugh, this is ugly as all hell --- TYT
 */
static int
pci_timedia_setup(struct pci_dev *dev, struct pci_board *board,
		  struct uart_port *port, int idx)
{
	unsigned int bar = 0, offset = board->first_offset;

	switch (idx) {
	case 0:
		bar = 0;
		break;
	case 1:
		offset = board->uart_offset;
		bar = 0;
		break;
	case 2:
		bar = 1;
		break;
	case 3:
		offset = board->uart_offset;
		bar = 1;
	case 4: /* BAR 2 */
	case 5: /* BAR 3 */
	case 6: /* BAR 4 */
	case 7: /* BAR 5 */
		bar = idx - 2;
	}

	return setup_port(dev, 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,
		      struct uart_port *port, int idx)
{
	unsigned int bar, offset = board->first_offset;

	switch (idx) {
	case 0:
		bar = 1;
		break;
	case 1:
		bar = 2;
		break;
	default:
		bar = 4;
		offset = (idx - 2) * board->uart_offset;
	}

	return setup_port(dev, port, bar, offset, board->reg_shift);
}

static int __devinit pci_xircom_init(struct pci_dev *dev)
{
	msleep(100);
	return 0;
}

static int
pci_default_setup(struct pci_dev *dev, struct pci_board *board,
		  struct uart_port *port, int idx)
{
	unsigned int bar, offset = board->first_offset, maxnr;

	bar = FL_GET_BASE(board->flags);
	if (board->flags & FL_BASE_BARS)
		bar += idx;
	else
		offset += idx * board->uart_offset;

	maxnr = (pci_resource_len(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);
}

/* This should be in linux/pci_ids.h */
#define PCI_VENDOR_ID_SBSMODULARIO	0x124B
#define PCI_SUBVENDOR_ID_SBSMODULARIO	0x124B
#define PCI_DEVICE_ID_OCTPRO		0x0001
#define PCI_SUBDEVICE_ID_OCTPRO232	0x0108
#define PCI_SUBDEVICE_ID_OCTPRO422	0x0208
#define PCI_SUBDEVICE_ID_POCTAL232	0x0308
#define PCI_SUBDEVICE_ID_POCTAL422	0x0408

/*
 * Master list of serial port init/setup/exit quirks.
 * This does not describe the general nature of the port.
 * (ie, baud base, number and location of ports, etc)
 *
 * This list is ordered alphabetically by vendor then device.
 * Specific entries must come before more generic entries.
 */
static struct pci_serial_quirk pci_serial_quirks[] = {
	/*
	 * AFAVLAB cards.
	 *  It is not clear whether this applies to all products.
	 */
	{
		.vendor		= PCI_VENDOR_ID_AFAVLAB,
		.device		= PCI_ANY_ID,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.setup		= afavlab_setup,
	},
	/*
	 * HP Diva
	 */
	{
		.vendor		= PCI_VENDOR_ID_HP,
		.device		= PCI_DEVICE_ID_HP_DIVA,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.init		= pci_hp_diva_init,
		.setup		= pci_hp_diva_setup,
	},
	/*
	 * Intel
	 */
	{
		.vendor		= PCI_VENDOR_ID_INTEL,
		.device		= PCI_DEVICE_ID_INTEL_80960_RP,
		.subvendor	= 0xe4bf,
		.subdevice	= PCI_ANY_ID,
		.init		= pci_inteli960ni_init,
		.setup		= pci_default_setup,
	},
	/*
	 * Panacom
	 */
	{
		.vendor		= PCI_VENDOR_ID_PANACOM,
		.device		= PCI_DEVICE_ID_PANACOM_QUADMODEM,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.init		= pci_plx9050_init,
		.setup		= pci_default_setup,
		.exit		= __devexit_p(pci_plx9050_exit),
	},		
	{
		.vendor		= PCI_VENDOR_ID_PANACOM,
		.device		= PCI_DEVICE_ID_PANACOM_DUALMODEM,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.init		= pci_plx9050_init,
		.setup		= pci_default_setup,
		.exit		= __devexit_p(pci_plx9050_exit),
	},
	/*
	 * PLX
	 */
	{
		.vendor		= PCI_VENDOR_ID_PLX,
		.device		= PCI_DEVICE_ID_PLX_9050,
		.subvendor	= PCI_SUBVENDOR_ID_KEYSPAN,
		.subdevice	= PCI_SUBDEVICE_ID_KEYSPAN_SX2,
		.init		= pci_plx9050_init,
		.setup		= pci_default_setup,
		.exit		= __devexit_p(pci_plx9050_exit),
	},
	{
		.vendor		= PCI_VENDOR_ID_PLX,
		.device		= PCI_DEVICE_ID_PLX_ROMULUS,
		.subvendor	= PCI_VENDOR_ID_PLX,
		.subdevice	= PCI_DEVICE_ID_PLX_ROMULUS,
		.init		= pci_plx9050_init,
		.setup		= pci_default_setup,
		.exit		= __devexit_p(pci_plx9050_exit),
	},
	/*
	 * SBS Technologies, Inc., PMC-OCTALPRO 232
	 */
	{
		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
		.device		= PCI_DEVICE_ID_OCTPRO,
		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
		.subdevice	= PCI_SUBDEVICE_ID_OCTPRO232,
		.init		= sbs_init,
		.setup		= sbs_setup,
		.exit		= __devexit_p(sbs_exit),
	},
	/*
	 * SBS Technologies, Inc., PMC-OCTALPRO 422
	 */
	{
		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
		.device		= PCI_DEVICE_ID_OCTPRO,
		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
		.subdevice	= PCI_SUBDEVICE_ID_OCTPRO422,
		.init		= sbs_init,
		.setup		= sbs_setup,
		.exit		= __devexit_p(sbs_exit),
	},
	/*
	 * SBS Technologies, Inc., P-Octal 232
	 */
	{
		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
		.device		= PCI_DEVICE_ID_OCTPRO,
		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
		.subdevice	= PCI_SUBDEVICE_ID_POCTAL232,
		.init		= sbs_init,
		.setup		= sbs_setup,
		.exit		= __devexit_p(sbs_exit),
	},
	/*
	 * SBS Technologies, Inc., P-Octal 422
	 */
	{
		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
		.device		= PCI_DEVICE_ID_OCTPRO,
		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
		.subdevice	= PCI_SUBDEVICE_ID_POCTAL422,
		.init		= sbs_init,
		.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,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.init		= pci_siig20x_init,
		.setup		= pci_default_setup,
	},
	/*
	 * Titan cards
	 */
	{
		.vendor		= PCI_VENDOR_ID_TITAN,
		.device		= PCI_DEVICE_ID_TITAN_400L,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.setup		= titan_400l_800l_setup,
	},
	{
		.vendor		= PCI_VENDOR_ID_TITAN,
		.device		= PCI_DEVICE_ID_TITAN_800L,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.setup		= titan_400l_800l_setup,
	},
	/*
	 * Timedia cards
	 */
	{
		.vendor		= PCI_VENDOR_ID_TIMEDIA,
		.device		= PCI_DEVICE_ID_TIMEDIA_1889,
		.subvendor	= PCI_VENDOR_ID_TIMEDIA,
		.subdevice	= PCI_ANY_ID,
		.init		= pci_timedia_init,
		.setup		= pci_timedia_setup,
	},
	{
		.vendor		= PCI_VENDOR_ID_TIMEDIA,
		.device		= PCI_ANY_ID,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.setup		= pci_timedia_setup,
	},
	/*
	 * Xircom cards
	 */
	{
		.vendor		= PCI_VENDOR_ID_XIRCOM,
		.device		= PCI_DEVICE_ID_XIRCOM_X3201_MDM,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.init		= pci_xircom_init,
		.setup		= pci_default_setup,
	},
	/*
	 * Default "match everything" terminator entry
	 */
	{
		.vendor		= PCI_ANY_ID,
		.device		= PCI_ANY_ID,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.setup		= pci_default_setup,
	}
};

static inline int quirk_id_matches(u32 quirk_id, u32 dev_id)
{
	return quirk_id == PCI_ANY_ID || quirk_id == dev_id;
}

static struct pci_serial_quirk *find_quirk(struct pci_dev *dev)
{
	struct pci_serial_quirk *quirk;

	for (quirk = pci_serial_quirks; ; quirk++)
		if (quirk_id_matches(quirk->vendor, dev->vendor) &&
		    quirk_id_matches(quirk->device, dev->device) &&
		    quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) &&
		    quirk_id_matches(quirk->subdevice, dev->subsystem_device))
		 	break;
	return quirk;
}

static _INLINE_ int
get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx)
{
	if (board->flags & FL_NOIRQ)
		return 0;
	else
		return dev->irq;
}

/*
 * This is the configuration table for all of the PCI serial boards
 * which we support.  It is directly indexed by the pci_board_num_t enum
 * value, which is encoded in the pci_device_id PCI probe table's
 * driver_data member.
 *
 * The makeup of these names are:
 *  pbn_bn{_bt}_n_baud
 *
 *  bn   = PCI BAR number
 *  bt   = Index using PCI BARs
 *  n    = number of serial ports
 *  baud = baud rate
 *
 * Please note: in theory if n = 1, _bt infix should make no difference.
 * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200
 */
enum pci_board_num_t {
	pbn_default = 0,

	pbn_b0_1_115200,
	pbn_b0_2_115200,
	pbn_b0_4_115200,
	pbn_b0_5_115200,

	pbn_b0_1_921600,
	pbn_b0_2_921600,
	pbn_b0_4_921600,

	pbn_b0_bt_1_115200,
	pbn_b0_bt_2_115200,
	pbn_b0_bt_8_115200,

	pbn_b0_bt_1_460800,
	pbn_b0_bt_2_460800,
	pbn_b0_bt_4_460800,

	pbn_b0_bt_1_921600,
	pbn_b0_bt_2_921600,
	pbn_b0_bt_4_921600,
	pbn_b0_bt_8_921600,

	pbn_b1_1_115200,
	pbn_b1_2_115200,
	pbn_b1_4_115200,
	pbn_b1_8_115200,

	pbn_b1_1_921600,
	pbn_b1_2_921600,
	pbn_b1_4_921600,
	pbn_b1_8_921600,

	pbn_b1_bt_2_921600,

	pbn_b1_1_1382400,
	pbn_b1_2_1382400,
	pbn_b1_4_1382400,
	pbn_b1_8_1382400,

	pbn_b2_1_115200,
	pbn_b2_8_115200,

	pbn_b2_1_460800,
	pbn_b2_4_460800,
	pbn_b2_8_460800,
	pbn_b2_16_460800,

	pbn_b2_1_921600,
	pbn_b2_4_921600,
	pbn_b2_8_921600,

	pbn_b2_bt_1_115200,
	pbn_b2_bt_2_115200,
	pbn_b2_bt_4_115200,

	pbn_b2_bt_2_921600,
	pbn_b2_bt_4_921600,

	pbn_b3_4_115200,
	pbn_b3_8_115200,

	/*
	 * Board-specific versions.
	 */
	pbn_panacom,
	pbn_panacom2,
	pbn_panacom4,
	pbn_plx_romulus,
	pbn_oxsemi,
	pbn_intel_i960,
	pbn_sgi_ioc3,
	pbn_nec_nile4,
	pbn_computone_4,
	pbn_computone_6,
	pbn_computone_8,
	pbn_sbsxrsio,
	pbn_exar_XR17C152,
	pbn_exar_XR17C154,
	pbn_exar_XR17C158,
};

/*
 * uart_offset - the space between channels
 * reg_shift   - describes how the UART registers are mapped
 *               to PCI memory by the card.
 * For example IER register on SBS, Inc. PMC-OctPro is located at
 * offset 0x10 from the UART base, while UART_IER is defined as 1
 * in include/linux/serial_reg.h,
 * see first lines of serial_in() and serial_out() in 8250.c
*/

static struct pci_board pci_boards[] __devinitdata = {
	[pbn_default] = {
		.flags		= FL_BASE0,
		.num_ports	= 1,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b0_1_115200] = {
		.flags		= FL_BASE0,
		.num_ports	= 1,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b0_2_115200] = {
		.flags		= FL_BASE0,
		.num_ports	= 2,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b0_4_115200] = {
		.flags		= FL_BASE0,
		.num_ports	= 4,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b0_5_115200] = {
		.flags		= FL_BASE0,
		.num_ports	= 5,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},

	[pbn_b0_1_921600] = {
		.flags		= FL_BASE0,
		.num_ports	= 1,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b0_2_921600] = {
		.flags		= FL_BASE0,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b0_4_921600] = {
		.flags		= FL_BASE0,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},

	[pbn_b0_bt_1_115200] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 1,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b0_bt_2_115200] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 2,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b0_bt_8_115200] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 8,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},

	[pbn_b0_bt_1_460800] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 1,
		.base_baud	= 460800,
		.uart_offset	= 8,
	},
	[pbn_b0_bt_2_460800] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 2,
		.base_baud	= 460800,
		.uart_offset	= 8,
	},
	[pbn_b0_bt_4_460800] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 4,
		.base_baud	= 460800,
		.uart_offset	= 8,
	},

	[pbn_b0_bt_1_921600] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 1,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b0_bt_2_921600] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b0_bt_4_921600] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b0_bt_8_921600] = {
		.flags		= FL_BASE0|FL_BASE_BARS,
		.num_ports	= 8,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},

	[pbn_b1_1_115200] = {
		.flags		= FL_BASE1,
		.num_ports	= 1,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b1_2_115200] = {
		.flags		= FL_BASE1,
		.num_ports	= 2,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b1_4_115200] = {
		.flags		= FL_BASE1,
		.num_ports	= 4,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b1_8_115200] = {
		.flags		= FL_BASE1,
		.num_ports	= 8,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},

	[pbn_b1_1_921600] = {
		.flags		= FL_BASE1,
		.num_ports	= 1,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b1_2_921600] = {
		.flags		= FL_BASE1,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b1_4_921600] = {
		.flags		= FL_BASE1,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b1_8_921600] = {
		.flags		= FL_BASE1,
		.num_ports	= 8,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},

	[pbn_b1_bt_2_921600] = {
		.flags		= FL_BASE1|FL_BASE_BARS,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},

	[pbn_b1_1_1382400] = {
		.flags		= FL_BASE1,
		.num_ports	= 1,
		.base_baud	= 1382400,
		.uart_offset	= 8,
	},
	[pbn_b1_2_1382400] = {
		.flags		= FL_BASE1,
		.num_ports	= 2,
		.base_baud	= 1382400,
		.uart_offset	= 8,
	},
	[pbn_b1_4_1382400] = {
		.flags		= FL_BASE1,
		.num_ports	= 4,
		.base_baud	= 1382400,
		.uart_offset	= 8,
	},
	[pbn_b1_8_1382400] = {
		.flags		= FL_BASE1,
		.num_ports	= 8,
		.base_baud	= 1382400,
		.uart_offset	= 8,
	},

	[pbn_b2_1_115200] = {
		.flags		= FL_BASE2,
		.num_ports	= 1,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b2_8_115200] = {
		.flags		= FL_BASE2,
		.num_ports	= 8,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},

	[pbn_b2_1_460800] = {
		.flags		= FL_BASE2,
		.num_ports	= 1,
		.base_baud	= 460800,
		.uart_offset	= 8,
	},
	[pbn_b2_4_460800] = {
		.flags		= FL_BASE2,
		.num_ports	= 4,
		.base_baud	= 460800,
		.uart_offset	= 8,
	},
	[pbn_b2_8_460800] = {
		.flags		= FL_BASE2,
		.num_ports	= 8,
		.base_baud	= 460800,
		.uart_offset	= 8,
	},
	[pbn_b2_16_460800] = {
		.flags		= FL_BASE2,
		.num_ports	= 16,
		.base_baud	= 460800,
		.uart_offset	= 8,
	 },

	[pbn_b2_1_921600] = {
		.flags		= FL_BASE2,
		.num_ports	= 1,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b2_4_921600] = {
		.flags		= FL_BASE2,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b2_8_921600] = {
		.flags		= FL_BASE2,
		.num_ports	= 8,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},

	[pbn_b2_bt_1_115200] = {
		.flags		= FL_BASE2|FL_BASE_BARS,
		.num_ports	= 1,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b2_bt_2_115200] = {
		.flags		= FL_BASE2|FL_BASE_BARS,
		.num_ports	= 2,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b2_bt_4_115200] = {
		.flags		= FL_BASE2|FL_BASE_BARS,
		.num_ports	= 4,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},

	[pbn_b2_bt_2_921600] = {
		.flags		= FL_BASE2|FL_BASE_BARS,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},
	[pbn_b2_bt_4_921600] = {
		.flags		= FL_BASE2|FL_BASE_BARS,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 8,
	},

	[pbn_b3_4_115200] = {
		.flags		= FL_BASE3,
		.num_ports	= 4,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},
	[pbn_b3_8_115200] = {
		.flags		= FL_BASE3,
		.num_ports	= 8,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},

	/*
	 * Entries following this are board-specific.
	 */

	/*
	 * Panacom - IOMEM
	 */
	[pbn_panacom] = {
		.flags		= FL_BASE2,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 0x400,
		.reg_shift	= 7,
	},
	[pbn_panacom2] = {
		.flags		= FL_BASE2|FL_BASE_BARS,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 0x400,
		.reg_shift	= 7,
	},
	[pbn_panacom4] = {
		.flags		= FL_BASE2|FL_BASE_BARS,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 0x400,
		.reg_shift	= 7,
	},

	/* I think this entry is broken - the first_offset looks wrong --rmk */
	[pbn_plx_romulus] = {
		.flags		= FL_BASE2,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 8 << 2,
		.reg_shift	= 2,
		.first_offset	= 0x03,
	},

	/*
	 * This board uses the size of PCI Base region 0 to
	 * signal now many ports are available
	 */
	[pbn_oxsemi] = {
		.flags		= FL_BASE0|FL_REGION_SZ_CAP,
		.num_ports	= 32,
		.base_baud	= 115200,
		.uart_offset	= 8,
	},

	/*
	 * EKF addition for i960 Boards form EKF with serial port.
	 * Max 256 ports.
	 */
	[pbn_intel_i960] = {
		.flags		= FL_BASE0,
		.num_ports	= 32,
		.base_baud	= 921600,
		.uart_offset	= 8 << 2,
		.reg_shift	= 2,
		.first_offset	= 0x10000,
	},
	[pbn_sgi_ioc3] = {
		.flags		= FL_BASE0|FL_NOIRQ,
		.num_ports	= 1,
		.base_baud	= 458333,
		.uart_offset	= 8,
		.reg_shift	= 0,
		.first_offset	= 0x20178,
	},

	/*
	 * NEC Vrc-5074 (Nile 4) builtin UART.
	 */
	[pbn_nec_nile4] = {
		.flags		= FL_BASE0,
		.num_ports	= 1,
		.base_baud	= 520833,
		.uart_offset	= 8 << 3,
		.reg_shift	= 3,
		.first_offset	= 0x300,
	},

	/*
	 * Computone - uses IOMEM.
	 */
	[pbn_computone_4] = {
		.flags		= FL_BASE0,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 0x40,
		.reg_shift	= 2,
		.first_offset	= 0x200,
	},
	[pbn_computone_6] = {
		.flags		= FL_BASE0,
		.num_ports	= 6,
		.base_baud	= 921600,
		.uart_offset	= 0x40,
		.reg_shift	= 2,
		.first_offset	= 0x200,
	},
	[pbn_computone_8] = {
		.flags		= FL_BASE0,
		.num_ports	= 8,
		.base_baud	= 921600,
		.uart_offset	= 0x40,
		.reg_shift	= 2,
		.first_offset	= 0x200,
	},
	[pbn_sbsxrsio] = {
		.flags		= FL_BASE0,
		.num_ports	= 8,
		.base_baud	= 460800,
		.uart_offset	= 256,
		.reg_shift	= 4,
	},
	/*
	 * Exar Corp. XR17C15[248] Dual/Quad/Octal UART
	 *  Only basic 16550A support.
	 *  XR17C15[24] are not tested, but they should work.
	 */
	[pbn_exar_XR17C152] = {
		.flags		= FL_BASE0,
		.num_ports	= 2,
		.base_baud	= 921600,
		.uart_offset	= 0x200,
	},
	[pbn_exar_XR17C154] = {
		.flags		= FL_BASE0,
		.num_ports	= 4,
		.base_baud	= 921600,
		.uart_offset	= 0x200,
	},
	[pbn_exar_XR17C158] = {
		.flags		= FL_BASE0,
		.num_ports	= 8,
		.base_baud	= 921600,
		.uart_offset	= 0x200,
	},
};

/*
 * Given a complete unknown PCI device, try to use some heuristics to
 * guess what the configuration might be, based on the pitiful PCI
 * serial specs.  Returns 0 on success, 1 on failure.
 */
static int __devinit
serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
{
	int num_iomem, num_port, first_port = -1, 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 >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) ||
	    (dev->class & 0xff) > 6)
		return -ENODEV;

	num_iomem = num_port = 0;
	for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
		if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
			num_port++;
			if (first_port == -1)
				first_port = i;
		}
		if (pci_resource_flags(dev, i) & IORESOURCE_MEM)
			num_iomem++;
	}

	/*
	 * If there is 1 or 0 iomem regions, and exactly one port,
	 * use it.  We guess the number of ports based on the IO
	 * region size.
	 */
	if (num_iomem <= 1 && num_port == 1) {
		board->flags = first_port;
		board->num_ports = pci_resource_len(dev, first_port) / 8;
		return 0;
	}

	/*
	 * Now guess if we've got a board which indexes by BARs.
	 * Each IO BAR should be 8 bytes, and they should follow
	 * consecutively.
	 */
	first_port = -1;
	num_port = 0;
	for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
		if (pci_resource_flags(dev, i) & IORESOURCE_IO &&
		    pci_resource_len(dev, i) == 8 &&
		    (first_port == -1 || (first_port + num_port) == i)) {
			num_port++;
			if (first_port == -1)
				first_port = i;
		}
	}

	if (num_port > 1) {
		board->flags = first_port | FL_BASE_BARS;
		board->num_ports = num_port;
		return 0;
	}

	return -ENODEV;
}

static inline int
serial_pci_matches(struct pci_board *board, struct pci_board *guessed)
{
	return
	    board->num_ports == guessed->num_ports &&
	    board->base_baud == guessed->base_baud &&
	    board->uart_offset == guessed->uart_offset &&
	    board->reg_shift == guessed->reg_shift &&
	    board->first_offset == guessed->first_offset;
}

/*
 * pci devices with appropriate class declared, but known as
 * non modems or serial
 */
static struct pci_device_id __devinitdata non_serial_pci_tbl[] = {
       {       PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451,
               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
       {       PCI_VENDOR_ID_AL, 0x5457,
               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
       {       PCI_VENDOR_ID_AL, 0x5459,
               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
       { 0, }
};

/*
 * 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 pci_board *board, tmp;
	struct pci_serial_quirk *quirk;
	int rc, nr_ports, i;

        if (pci_match_device(non_serial_pci_tbl, dev))
                return -ENODEV;

	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;

	/*
	 * Find an init and setup quirks.
	 */
	quirk = find_quirk(dev);

	/*
	 * Run the new-style initialization function.
	 * The initialization function returns:
	 *  <0  - error
	 *   0  - use board->num_ports
	 *  >0  - number of ports
	 */
	if (quirk->init) {
		rc = quirk->init(dev);
		if (rc < 0)
			goto disable;
		if (rc)
			nr_ports = rc;
	}

	priv = kmalloc(sizeof(struct serial_private) +
		       sizeof(unsigned int) * nr_ports,
		       GFP_KERNEL);
	if (!priv) {
		rc = -ENOMEM;
		goto deinit;
	}

	memset(priv, 0, sizeof(struct serial_private) +
			sizeof(unsigned int) * nr_ports);

	priv->quirk = quirk;
	pci_set_drvdata(dev, priv);

	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))
			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);
#endif
		
		priv->line[i] = serial8250_register_port(&serial_port);
		if (priv->line[i] < 0) {
			printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
			break;
		}
	}

	priv->nr = i;

	return 0;

 deinit:
	if (quirk->exit)
		quirk->exit(dev);
 disable:
	pci_disable_device(dev);
	return rc;
}

static void __devexit pciserial_remove_one(struct pci_dev *dev)
{
	struct serial_private *priv = pci_get_drvdata(dev);

	pci_set_drvdata(dev, NULL);

	if (priv) {
		struct pci_serial_quirk *quirk;
		int i;

		for (i = 0; i < priv->nr; i++)
			serial8250_unregister_port(priv->line[i]);

		for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
			if (priv->remapped_bar[i])
				iounmap(priv->remapped_bar[i]);
			priv->remapped_bar[i] = NULL;
		}

		/*
		 * Find the exit quirks.
		 */
		quirk = find_quirk(dev);
		if (quirk->exit)
			quirk->exit(dev);

		pci_disable_device(dev);

		kfree(priv);
	}
}

static int pciserial_suspend_one(struct pci_dev *dev, u32 state)
{
	struct serial_private *priv = pci_get_drvdata(dev);

	if (priv) {
		int i;

		for (i = 0; i < priv->nr; i++)
			serial8250_suspend_port(priv->line[i]);
	}
	return 0;
}

static int pciserial_resume_one(struct pci_dev *dev)
{
	struct serial_private *priv = pci_get_drvdata(dev);

	if (priv) {
		int i;

		/*
		 * 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]);
	}
	return 0;
}

static struct pci_device_id serial_pci_tbl[] = {
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
		pbn_b1_8_1382400 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
		pbn_b1_4_1382400 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
		pbn_b1_2_1382400 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
		pbn_b1_8_1382400 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
		pbn_b1_4_1382400 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
		pbn_b1_2_1382400 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0,
		pbn_b1_8_921600 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0,
		pbn_b1_8_921600 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0,
		pbn_b1_4_921600 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0,
		pbn_b1_4_921600 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0,
		pbn_b1_2_921600 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0,
		pbn_b1_8_921600 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0,
		pbn_b1_8_921600 },
	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
		PCI_SUBVENDOR_ID_CONNECT_TECH,
		PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0,
		pbn_b1_4_921600 },

	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_bt_1_115200 },
	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_bt_2_115200 },
	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_bt_4_115200 },
	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_bt_2_115200 },
	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_bt_4_115200 },
	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_8_115200 },

	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_2_115200 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_2_921600 },
	/*
	 * VScom SPCOM800, from sl@s.pl
	 */
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, 
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_8_921600 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b2_4_921600 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
		PCI_SUBVENDOR_ID_KEYSPAN,
		PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
		pbn_panacom },
	{	PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_panacom4 },
	{	PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_panacom2 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
		PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, 
		pbn_b2_4_460800 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
		PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, 
		pbn_b2_8_460800 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
		PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, 
		pbn_b2_16_460800 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
		PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, 
		pbn_b2_16_460800 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
		PCI_SUBVENDOR_ID_CHASE_PCIRAS,
		PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, 
		pbn_b2_4_460800 },
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
		PCI_SUBVENDOR_ID_CHASE_PCIRAS,
		PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, 
		pbn_b2_8_460800 },
	/*
	 * Megawolf Romulus PCI Serial Card, from Mike Hudson
	 * (Exoray@isys.ca)
	 */
	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
		0x10b5, 0x106a, 0, 0,
		pbn_plx_romulus },
	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_4_115200 },
	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_2_115200 },
	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_8_115200 },
	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_8_115200 },
	{	PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
		PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0,
		pbn_b0_4_921600 },
	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_4_115200 },
	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_921600 },

	/*
	 * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards,
	 * from skokodyn@yahoo.com
	 */
	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0,
		pbn_sbsxrsio },
	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0,
		pbn_sbsxrsio },
	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0,
		pbn_sbsxrsio },
	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0,
		pbn_sbsxrsio },

	/*
	 * Digitan DS560-558, from jimd@esoft.com
	 */
	{	PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b1_1_115200 },

	/*
	 * Titan Electronic cards
	 *  The 400L and 800L have a custom setup quirk.
	 */
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b0_1_921600 },
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b0_2_921600 },
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b0_4_921600 },
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
		pbn_b0_4_921600 },
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_1_921600 },
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_bt_2_921600 },
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_4_921600 },
	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_8_921600 },

	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_1_460800 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_1_460800 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_1_460800 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_2_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_2_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_2_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_4_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_4_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_4_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_1_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_1_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_1_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_4_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_4_921600 },
	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_4_921600 },

	/*
	 * 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,
		0, 0, pbn_computone_4 },
	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
		0, 0, pbn_computone_8 },
	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
		0, 0, pbn_computone_6 },

	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_oxsemi },
	{	PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
		PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_1_921600 },

	/*
	 * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
	 */
	{	PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_8_115200 },
	{	PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_8_115200 },

	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_115200 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_115200 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_115200 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_4_460800 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_4_460800 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_460800 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_460800 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_2_460800 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_1_115200 },
	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_bt_1_460800 },

	/*
	 * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com
	 */
	{	PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_1_1382400 },

	/*
	 * Dell Remote Access Card III - Tim_T_Murphy@Dell.com
	 */
	{	PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b1_1_1382400 },

	/*
	 * RAStel 2 port modem, gerg@moreton.com.au
	 */
	{	PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_bt_2_115200 },

	/*
	 * EKF addition for i960 Boards form EKF with serial port
	 */
	{	PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP,
		0xE4BF, PCI_ANY_ID, 0, 0,
		pbn_intel_i960 },

	/*
	 * Xircom Cardbus/Ethernet combos
	 */
	{	PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_1_115200 },
	/*
	 * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry)
	 */
	{	PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_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, 0, 0,
		pbn_b1_1_115200 },

	{	PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
		0xFF00, 0, 0, 0,
		pbn_sgi_ioc3 },

	/*
	 * HP Diva card
	 */
	{	PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b0_5_115200 },
	{	PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b2_1_115200 },

	/*
	 * NEC Vrc-5074 (Nile 4) builtin UART.
	 */
	{	PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_nec_nile4 },

	{	PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b3_4_115200 },
	{	PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
		pbn_b3_8_115200 },

	/*
	 * Exar Corp. XR17C15[248] Dual/Quad/Octal UART
	 */
	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
		PCI_ANY_ID, PCI_ANY_ID,
		0,
		0, pbn_exar_XR17C152 },
	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
		PCI_ANY_ID, PCI_ANY_ID,
		0,
		0, pbn_exar_XR17C154 },
	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
		PCI_ANY_ID, PCI_ANY_ID,
		0,
		0, pbn_exar_XR17C158 },

	/*
	 * These entries match devices with class COMMUNICATION_SERIAL,
	 * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
	 */
	{	PCI_ANY_ID, PCI_ANY_ID,
		PCI_ANY_ID, PCI_ANY_ID,
		PCI_CLASS_COMMUNICATION_SERIAL << 8,
		0xffff00, pbn_default },
	{	PCI_ANY_ID, PCI_ANY_ID,
		PCI_ANY_ID, PCI_ANY_ID,
		PCI_CLASS_COMMUNICATION_MODEM << 8,
		0xffff00, pbn_default },
	{	PCI_ANY_ID, PCI_ANY_ID,
		PCI_ANY_ID, PCI_ANY_ID,
		PCI_CLASS_COMMUNICATION_MULTISERIAL << 8,
		0xffff00, pbn_default },
	{ 0, }
};

static struct pci_driver serial_pci_driver = {
	.name		= "serial",
	.probe		= pciserial_init_one,
	.remove		= __devexit_p(pciserial_remove_one),
	.suspend	= pciserial_suspend_one,
	.resume		= pciserial_resume_one,
	.id_table	= serial_pci_tbl,
};

static int __init serial8250_pci_init(void)
{
	return pci_module_init(&serial_pci_driver);
}

static void __exit serial8250_pci_exit(void)
{
	pci_unregister_driver(&serial_pci_driver);
}

module_init(serial8250_pci_init);
module_exit(serial8250_pci_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
MODULE_DEVICE_TABLE(pci, serial_pci_tbl);

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-19 18:33   ` Jacques Goldberg
@ 2005-03-21  8:16     ` Andrey Panin
  2005-03-21  8:22       ` Russell King
  2005-03-21 11:35       ` Jacques Goldberg
  0 siblings, 2 replies; 16+ messages in thread
From: Andrey Panin @ 2005-03-21  8:16 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, Jacques Goldberg

[-- Attachment #1: Type: text/plain, Size: 939 bytes --]

On 078, 03 19, 2005 at 08:33:14PM +0200, Jacques Goldberg wrote:
> 
>    Good news!
>    That's really what is needed (mainline).
>    I attach the file which Sasha, author or the lmodem driver, has
> modified and then it works for the chips hard-wired in the routine.
>    To locate the patched area, look for 5457

We can use PCI quirk here. Patch attached.

>    I am an experimental physicist, not a computer expert. If this kind of
> algorithm could be implemented in the mainlin 8250_pci.c , then I would
> dream of looking for an /etc/config file containing such a list, whose
> objects would be skipped by the serial driver.
>    Sorry to repeat myself, but avoiding the need for the users to
> recompile a kernel should be the first worry when implementing a solution.

It's not a reason to fill kernel code with ugly kludges :)

-- 
Andrey Panin		| Linux and UNIX system administrator
pazke@donpac.ru		| PGP key: wwwkeys.pgp.net

[-- Attachment #2: patch-quirk-winmodems --]
[-- Type: text/plain, Size: 1048 bytes --]

diff -urdpNX /usr/share/dontdiff linux-2.6.11.vanilla/drivers/pci/quirks.c linux-2.6.11/drivers/pci/quirks.c
--- linux-2.6.11.vanilla/drivers/pci/quirks.c	2005-03-02 10:37:31.000000000 +0300
+++ linux-2.6.11/drivers/pci/quirks.c	2005-03-21 11:02:53.000000000 +0300
@@ -1241,6 +1241,18 @@ 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 );
 
+/*
+ * Some stupid winmodems try to look as real modems. Fix them.
+ */
+static void __devinit quirk_winmodems(struct pci_dev *dev)
+{
+	dev->class = PCI_CLASS_COMMUNICATION_OTHER;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451, quirk_winmodems);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5457, quirk_winmodems);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5459, quirk_winmodems);
+
+
 static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end)
 {
 	while (f < end) {

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21  8:16     ` Andrey Panin
@ 2005-03-21  8:22       ` Russell King
  2005-03-21  8:40         ` Andrey Panin
  2005-03-21 11:39         ` Jacques Goldberg
  2005-03-21 11:35       ` Jacques Goldberg
  1 sibling, 2 replies; 16+ messages in thread
From: Russell King @ 2005-03-21  8:22 UTC (permalink / raw)
  To: Greg KH, linux-kernel, Jacques Goldberg

On Mon, Mar 21, 2005 at 11:16:38AM +0300, Andrey Panin wrote:
> On 078, 03 19, 2005 at 08:33:14PM +0200, Jacques Goldberg wrote:
> >    That's really what is needed (mainline).
> >    I attach the file which Sasha, author or the lmodem driver, has
> > modified and then it works for the chips hard-wired in the routine.
> >    To locate the patched area, look for 5457
> 
> We can use PCI quirk here. Patch attached.

I haven't seen any mail in this thread which provides the complete
PCI ID information for these cards.  Can someone oblige please, with:

lspci -vv

for the relevant card.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21  8:22       ` Russell King
@ 2005-03-21  8:40         ` Andrey Panin
  2005-03-21 11:39         ` Jacques Goldberg
  1 sibling, 0 replies; 16+ messages in thread
From: Andrey Panin @ 2005-03-21  8:40 UTC (permalink / raw)
  To: Russell King; +Cc: Greg KH, linux-kernel, Jacques Goldberg

On 080, 03 21, 2005 at 08:22:28AM +0000, Russell King wrote:
> On Mon, Mar 21, 2005 at 11:16:38AM +0300, Andrey Panin wrote:
> > On 078, 03 19, 2005 at 08:33:14PM +0200, Jacques Goldberg wrote:
> > >    That's really what is needed (mainline).
> > >    I attach the file which Sasha, author or the lmodem driver, has
> > > modified and then it works for the chips hard-wired in the routine.
> > >    To locate the patched area, look for 5457
> > 
> > We can use PCI quirk here. Patch attached.
> 
> I haven't seen any mail in this thread which provides the complete
> PCI ID information for these cards.

I took them from the patched 8250_pci.c file in Jacques' previous mail:

/*
 * pci devices with appropriate class declared, but known as
 * non modems or serial
 */
static struct pci_device_id __devinitdata non_serial_pci_tbl[] = {
	{	PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
	{	PCI_VENDOR_ID_AL, 0x5457,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
	{	PCI_VENDOR_ID_AL, 0x5459,
		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
	{ 0, }
};

-- 
Andrey Panin		| Linux and UNIX system administrator
pazke@donpac.ru		| PGP key: wwwkeys.pgp.net

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21  8:16     ` Andrey Panin
  2005-03-21  8:22       ` Russell King
@ 2005-03-21 11:35       ` Jacques Goldberg
  1 sibling, 0 replies; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-21 11:35 UTC (permalink / raw)
  To: Andrey Panin; +Cc: Greg KH, linux-kernel

On Mon, 21 Mar 2005, Andrey Panin wrote:

> We can use PCI quirk here. Patch attached.
> It's not a reason to fill kernel code with ugly kludges :)

  Sure. Thanks for the patch.
  We just did not know about "quirk". This was the purpose of my call for
help.
  Any idea how to make the table dynamic rather than static, so that the
next such device could be added without having to recompile?

                                                  Jacques

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21  8:22       ` Russell King
  2005-03-21  8:40         ` Andrey Panin
@ 2005-03-21 11:39         ` Jacques Goldberg
  2005-03-21 20:18           ` Russell King
  1 sibling, 1 reply; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-21 11:39 UTC (permalink / raw)
  To: Russell King; +Cc: Greg KH, linux-kernel


        Here is a modem which cannot be used because it is grabbed by the
serial driver:

00:0f.0 Modem: ALi Corporation SmartLink SmartPCI561 56K Modem (prog-if 00
[Gene
ric])
        Subsystem: ARCHTEK TELECOM Corp: Unknown device 9100
        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop-
ParErr- Step
ping- SERR- FastB2B-
        Status: Cap+ 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort-
<TAbort
- <MAbort- >SERR- <PERR-
        Latency: 64
        Interrupt: pin A routed to IRQ 10
        Region 0: Memory at f4101000 (32-bit, non-prefetchable) [size=4K]
        Region 1: I/O ports at 1000 [size=256]
        Capabilities: <available only to root>

and here is the corresponding  /proc/pci

  Bus  0, device  15, function  0:
    Class 0703: PCI device 10b9:5459 (rev 0).
      IRQ 10.
      Master Capable.  Latency=64.
      Non-prefetchable 32 bit memory at 0xf4101000 [0xf4101fff].
      I/O at 0x1000 [0x10ff].

There are more such devices.


On Mon, 21 Mar 2005, Russell King wrote:

> On Mon, Mar 21, 2005 at 11:16:38AM +0300, Andrey Panin wrote:
> > On 078, 03 19, 2005 at 08:33:14PM +0200, Jacques Goldberg wrote:
> > >  That's really what is needed (mainline).
> > >  I attach the file which Sasha, author or the lmodem driver, has
> > > modified and then it works for the chips hard-wired in the routine.
> > >  To locate the patched area, look for 5457
> >
> > We can use PCI quirk here. Patch attached.
>
> I haven't seen any mail in this thread which provides the complete
> PCI ID information for these cards.Can someone oblige please, with:
>
> lspci -vv
>
> for the relevant card.
>
> --
> Russell King
>  Linux kernel  2.6 ARM Linux   - http://www.arm.linux.org.uk/
>  maintainer of:2.6 Serial core
>

                                Jacques.Goldberg@cern.ch
                                >>>> Currently at TECHNION <<<<
                                PHONE: Technion=+(972)(0)(4)829.36.63
                                           CERN=+(41)(22)767.73.85
                                FAX:   Technion=+(972)(0)(4)829.39.01
                                           CERN=+(41)(22)767.31.00
                                HOME: Haifa=+(972)(4)825.29.04
                                GSM portable: +(972)(0)544.29.36.63

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21 11:39         ` Jacques Goldberg
@ 2005-03-21 20:18           ` Russell King
  2005-03-21 20:57             ` Jacques Goldberg
  0 siblings, 1 reply; 16+ messages in thread
From: Russell King @ 2005-03-21 20:18 UTC (permalink / raw)
  To: Jacques Goldberg; +Cc: Greg KH, linux-kernel

On Mon, Mar 21, 2005 at 01:39:30PM +0200, Jacques Goldberg wrote:
>         Here is a modem which cannot be used because it is grabbed by the
> serial driver:
> 
> 00:0f.0 Modem: ALi Corporation SmartLink SmartPCI561 56K Modem (prog-if 00
> [Generic])

Ok, this is what I wanted to know.

There seems to be growing evidence that 8250_pci should not claim the
"modem" class, but should match any such cards which do look like
serial ports by vendor/device IDs.  The problem is that dropping the
modem class id match could leave a fair number of people in the lurch,
but I'm game to try it and see.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21 20:18           ` Russell King
@ 2005-03-21 20:57             ` Jacques Goldberg
  2005-03-21 21:09               ` Russell King
  0 siblings, 1 reply; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-21 20:57 UTC (permalink / raw)
  To: Russell King; +Cc: Greg KH, linux-kernel


   Great, Russell. Now we understand each other.
   Actually some chip manufacturers are responsible for this mess. Some do
NOT burn a modem class flag in the hardware, others do.
   Is it nonsense to imagine that the part of 8250_pci which handles modem
class become a loadable module?
   We could then load the "linmodem" driver first, which would not disturb
use of true modems.
   My suggestion is very likely going to reveal the depth of my ignorance
about kernel architecture, I guess.

                                             Jacques

On Mon, 21 Mar 2005, Russell King wrote:

> On Mon, Mar 21, 2005 at 01:39:30PM +0200, Jacques Goldberg wrote:
> >       Here is a modem which cannot be used because it is grabbed by the
> > serial driver:
> >
> > 00:0f.0 Modem: ALi Corporation SmartLink SmartPCI561 56K Modem (prog-if 00
> > [Generic])
>
> Ok, this is what I wanted to know.
>
> There seems to be growing evidence that 8250_pci should not claim the
> "modem" class, but should match any such cards which do look like
> serial ports by vendor/device IDs.The problem is that dropping the
> modem class id match could leave a fair number of people in the lurch,
> but I'm game to try it and see.
>
> --
> Russell King
>  Linux kernel  2.6 ARM Linux   - http://www.arm.linux.org.uk/
>  maintainer of:2.6 Serial core
>

                                Jacques.Goldberg@cern.ch
                                >>>> Currently at TECHNION <<<<
                                PHONE: Technion=+(972)(0)(4)829.36.63
                                           CERN=+(41)(22)767.73.85
                                FAX:   Technion=+(972)(0)(4)829.39.01
                                           CERN=+(41)(22)767.31.00
                                HOME: Haifa=+(972)(4)825.29.04
                                GSM portable: +(972)(0)544.29.36.63

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21 20:57             ` Jacques Goldberg
@ 2005-03-21 21:09               ` Russell King
  2005-03-21 21:30                 ` Jacques Goldberg
  0 siblings, 1 reply; 16+ messages in thread
From: Russell King @ 2005-03-21 21:09 UTC (permalink / raw)
  To: Jacques Goldberg; +Cc: Greg KH, linux-kernel

On Mon, Mar 21, 2005 at 10:57:21PM +0200, Jacques Goldberg wrote:
> 
>    Great, Russell. Now we understand each other.
>    Actually some chip manufacturers are responsible for this mess. Some do
> NOT burn a modem class flag in the hardware, others do.
>    Is it nonsense to imagine that the part of 8250_pci which handles modem
> class become a loadable module?
>    We could then load the "linmodem" driver first, which would not disturb
> use of true modems.

No, that's not a reliable solution.

What it comes back to is that we _need_ driver match priorities, so we
can detect when a more specific driver for the device is loaded (iow
one which matches by vendor+device rather than just class), unbind the
existing driver, and bind the more specific one.

I've been mentioning this need for over a year now (for a different
scenario) and it hasn't particularly been going anywhere.  However,
your case boosts the reason why we need this functionality.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Need break driver<-->pci-device automatic association
  2005-03-21 21:09               ` Russell King
@ 2005-03-21 21:30                 ` Jacques Goldberg
  0 siblings, 0 replies; 16+ messages in thread
From: Jacques Goldberg @ 2005-03-21 21:30 UTC (permalink / raw)
  To: Russell King; +Cc: Greg KH, linux-kernel




On Mon, 21 Mar 2005, Russell King wrote:

> What it comes back to is that we _need_ driver match priorities, so we
> can detect when a more specific driver for the device is loaded (iow
> one which matches by vendor+device rather than just class), unbind the
> existing driver, and bind the more specific one.

  Absolutely agreed. This is what I dreamt of when starting this thread.
  My job is done now. The ball is in your hands.
  I shall now become mute but shall read further.
  If you ever need a real test you will always be welcome  to send it to
me since I have a case hardware.

  Thanks to all of you. Cheers. Jacques

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2005-03-21 21:35 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-18  8:57 Need break driver<-->pci-device automatic association Jacques Goldberg
2005-03-18 13:14 ` Alan Cox
2005-03-18 13:58   ` Jacques Goldberg
2005-03-18 14:39     ` Stuart MacDonald
2005-03-19 18:18       ` Jacques Goldberg
2005-03-18 16:51 ` Greg KH
2005-03-19 18:33   ` Jacques Goldberg
2005-03-21  8:16     ` Andrey Panin
2005-03-21  8:22       ` Russell King
2005-03-21  8:40         ` Andrey Panin
2005-03-21 11:39         ` Jacques Goldberg
2005-03-21 20:18           ` Russell King
2005-03-21 20:57             ` Jacques Goldberg
2005-03-21 21:09               ` Russell King
2005-03-21 21:30                 ` Jacques Goldberg
2005-03-21 11:35       ` Jacques Goldberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox