Re: 2.5.20 - Xircom PCI Cardbus doesn't work

Kai Germaschewski (kai@tp1.ruhr-uni-bochum.de)
Sat, 15 Jun 2002 18:00:53 -0500 (CDT)


On Sat, 15 Jun 2002, Jeff Garzik wrote:

> > So a complete API would be
> >
> > pci_request_{irq,io,mmio}
> > pci_release_{irq,io,mmio}
> > pci_enable_{irq,io,mmio}
> > pci_assign_{irq,io,mmio}
> >
> > but normally a driver would just use pci_request/release_*() + maybe
> > pci_assign_irq(), which will take care of the appropriate assign/enable
> > internally.
>
>
> That seems like a decent enough API, pending a bit of driver conversion
> to see how well it works out in practice. So I'm ok with it (with the
> pci_enable_device proviso, above)

Okay, so here's a patch which actually compiles and works here.

TODO:
o move the pci_set_power_state() before calling pci_driver::probe()
add pci_disable_device() after pci_driver::remove()
o fix other archs.

Currently, each arch has a

pcibios_enable_device()

function, which now needs to be split into

pcibios_assign_irq()
pcibios_enable_irq()

IO/MMIO is all taken care of by the generic code currently. (Possibly we
need to add callbacks into arch-specific code there)

Apart from breaking each arch but i386/x86_64, the patch is ready, at
least from my point of view ;)

--Kai

Pull from http://linux-isdn.bkbits.net/linux-2.5.pci

(Merging changesets omitted for clarity)

-----------------------------------------------------------------------------
ChangeSet@1.491, 2002-06-15 14:04:46-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Set pci_dev->driver before calling ::probe()

That's just preparation so that we can use pci_dev->driver inside
the probe() routine, e.g. for driver->name.

----------------------------------------------------------------------------
pci-driver.c | 10 ++++++----
1 files changed, 6 insertions(+), 4 deletions(-)

-----------------------------------------------------------------------------
ChangeSet@1.492, 2002-06-15 15:19:22-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Introduce pci_assign_irq() and pci_enable_irq()

The functions assign and route an IRQ on a PCI device, a bit inline
docu is available in drivers/pci/pci.c

Internally they call back into the arch specific
pcibios_assign/enable_irq(), which means everything but i386/x86_64
gets broken by this change (should be easy to fix, though).

Rename the function pointer pcibios_enable_irq to pcibios_enable_irq_func
to not clash with these functions.

----------------------------------------------------------------------------
arch/i386/pci/acpi.c | 2 -
arch/i386/pci/common.c | 18 +++++++++++++++-
arch/i386/pci/irq.c | 10 +++++----
arch/i386/pci/pci.h | 3 --
arch/x86_64/pci/acpi.c | 2 -
arch/x86_64/pci/common.c | 5 ++++
arch/x86_64/pci/irq.c | 10 +++++----
arch/x86_64/pci/pci.h | 3 --
drivers/pci/pci.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci.h | 2 +
10 files changed, 90 insertions(+), 15 deletions(-)

-----------------------------------------------------------------------------
ChangeSet@1.493, 2002-06-15 16:35:23-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Introduce wrapper to set/clear PCI command bits

It'd be worth it even if we wouldn't make yet more use of it in
the next patch.

----------------------------------------------------------------------------
pci.c | 61 ++++++++++++++++++++++++++++++++-----------------------------
1 files changed, 32 insertions(+), 29 deletions(-)

-----------------------------------------------------------------------------
ChangeSet@1.494, 2002-06-15 16:38:20-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Introduce pci_assign/enable_io/mmio functions

The main use of those will be internal to
pci_request/release_io/mmio. However, we export them, since
there'll always be hardware which needs special hacks...

----------------------------------------------------------------------------
drivers/pci/pci.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/pci.h | 7 ++
2 files changed, 128 insertions(+), 2 deletions(-)

-----------------------------------------------------------------------------
ChangeSet@1.495, 2002-06-15 17:23:10-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Add pci_request_* and pci_release_* API

Docu is available inline. When switching a driver to this API,
calling pci_enable_device() is not necessary anymore, the needed
resources will be activated at pci_request_* time.

In particular, that means if your driver only uses MMIO, IO resources
on your card won't be assigned and enabled (it's possible that the
BIOS did that, though).

----------------------------------------------------------------------------
drivers/pci/pci.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pci.h | 9 ++
2 files changed, 196 insertions(+)

-----------------------------------------------------------------------------
ChangeSet@1.496, 2002-06-15 17:31:38-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Convert two sample drivers to new interface

eepro100 and ymfpci still work fine on my laptop after the change,
even when zeroing out their BARs during boot.

----------------------------------------------------------------------------
drivers/net/eepro100.c | 83 +++++++++++++++++++++----------------------------
sound/oss/ymfpci.c | 45 ++++++++++----------------
2 files changed, 54 insertions(+), 74 deletions(-)

=============================================================================
unified diffs follow for reference
=============================================================================

-----------------------------------------------------------------------------
ChangeSet@1.491, 2002-06-15 14:04:46-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Set pci_dev->driver before calling ::probe()

That's just preparation so that we can use pci_dev->driver inside
the probe() routine, e.g. for driver->name.

---------------------------------------------------------------------------

diff -Nru a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
--- a/drivers/pci/pci-driver.c Sat Jun 15 17:59:18 2002
+++ b/drivers/pci/pci-driver.c Sat Jun 15 17:59:18 2002
@@ -48,12 +48,14 @@
const struct pci_device_id *id;

id = pci_match_device(drv->id_table, pci_dev);
- if (id)
- error = drv->probe(pci_dev, id);
- if (error >= 0) {
+ if (id) {
pci_dev->driver = drv;
- error = 0;
+ error = drv->probe(pci_dev, id);
}
+ if (error < 0)
+ pci_dev->driver = NULL;
+ else
+ error = 0;
}
return error;
}

-----------------------------------------------------------------------------
ChangeSet@1.492, 2002-06-15 15:19:22-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Introduce pci_assign_irq() and pci_enable_irq()

The functions assign and route an IRQ on a PCI device, a bit inline
docu is available in drivers/pci/pci.c

Internally they call back into the arch specific
pcibios_assign/enable_irq(), which means everything but i386/x86_64
gets broken by this change (should be easy to fix, though).

Rename the function pointer pcibios_enable_irq to pcibios_enable_irq_func
to not clash with these functions.

---------------------------------------------------------------------------

diff -Nru a/arch/i386/pci/acpi.c b/arch/i386/pci/acpi.c
--- a/arch/i386/pci/acpi.c Sat Jun 15 17:59:19 2002
+++ b/arch/i386/pci/acpi.c Sat Jun 15 17:59:19 2002
@@ -13,7 +13,7 @@
printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n");
printk(KERN_INFO "PCI: if you experience problems, try using option 'pci=noacpi'\n");
pcibios_scanned++;
- pcibios_enable_irq = acpi_pci_irq_enable;
+ pcibios_enable_irq_func = acpi_pci_irq_enable;
} else
printk(KERN_WARNING "PCI: Invalid ACPI-PCI IRQ routing table\n");

diff -Nru a/arch/i386/pci/common.c b/arch/i386/pci/common.c
--- a/arch/i386/pci/common.c Sat Jun 15 17:59:19 2002
+++ b/arch/i386/pci/common.c Sat Jun 15 17:59:19 2002
@@ -209,5 +209,21 @@
if ((err = pcibios_enable_resources(dev)) < 0)
return err;

- return pcibios_enable_irq(dev);
+ return pcibios_enable_irq_func(dev);
+}
+
+int pcibios_assign_irq(struct pci_dev *dev)
+{
+ return pcibios_enable_irq_func(dev);
+}
+
+/*
+ * We've done all the work in pcibios_assign_irq already.
+ * We may want to change this later to activate the actual routing at this
+ * point, though.
+ * It's only ever called after a successful pcibios_assign_irq()
+ */
+int pcibios_enable_irq(struct pci_dev *dev)
+{
+ return 0;
}
diff -Nru a/arch/i386/pci/irq.c b/arch/i386/pci/irq.c
--- a/arch/i386/pci/irq.c Sat Jun 15 17:59:19 2002
+++ b/arch/i386/pci/irq.c Sat Jun 15 17:59:19 2002
@@ -44,7 +44,7 @@
int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
};

-int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL;
+int (*pcibios_enable_irq_func)(struct pci_dev *dev);

/*
* Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
@@ -684,11 +684,13 @@
return 1;
}

+static int pirq_enable_irq(struct pci_dev *dev);
+
static int __init pcibios_irq_init(void)
{
DBG("PCI: IRQ init\n");

- if (pcibios_enable_irq)
+ if (pcibios_enable_irq_func)
return 0;

pirq_table = pirq_find_routing_table();
@@ -711,7 +713,7 @@
pirq_table = NULL;
}

- pcibios_enable_irq = pirq_enable_irq;
+ pcibios_enable_irq_func = pirq_enable_irq;

pcibios_fixup_irqs();
return 0;
@@ -794,7 +796,7 @@
pirq_penalty[irq] += 100;
}

-int pirq_enable_irq(struct pci_dev *dev)
+static int pirq_enable_irq(struct pci_dev *dev)
{
u8 pin;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
diff -Nru a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h
--- a/arch/i386/pci/pci.h Sat Jun 15 17:59:19 2002
+++ b/arch/i386/pci/pci.h Sat Jun 15 17:59:19 2002
@@ -70,6 +70,5 @@
extern spinlock_t pci_config_lock;

void pcibios_fixup_irqs(void);
-int pirq_enable_irq(struct pci_dev *dev);

-extern int (*pcibios_enable_irq)(struct pci_dev *dev);
+extern int (*pcibios_enable_irq_func)(struct pci_dev *dev);
diff -Nru a/arch/x86_64/pci/acpi.c b/arch/x86_64/pci/acpi.c
--- a/arch/x86_64/pci/acpi.c Sat Jun 15 17:59:19 2002
+++ b/arch/x86_64/pci/acpi.c Sat Jun 15 17:59:19 2002
@@ -13,7 +13,7 @@
printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n");
printk(KERN_INFO "PCI: if you experience problems, try using option 'pci=noacpi'\n");
pcibios_scanned++;
- pcibios_enable_irq = acpi_pci_irq_enable;
+ pcibios_enable_irq_func = acpi_pci_irq_enable;
} else
printk(KERN_WARNING "PCI: Invalid ACPI-PCI IRQ routing table\n");

diff -Nru a/arch/x86_64/pci/common.c b/arch/x86_64/pci/common.c
--- a/arch/x86_64/pci/common.c Sat Jun 15 17:59:19 2002
+++ b/arch/x86_64/pci/common.c Sat Jun 15 17:59:19 2002
@@ -194,3 +194,8 @@

return pcibios_enable_irq(dev);
}
+
+int pcibios_enable_irq(struct pci_dev *dev)
+{
+ return pcibios_enable_irq_func(dev);
+}
diff -Nru a/arch/x86_64/pci/irq.c b/arch/x86_64/pci/irq.c
--- a/arch/x86_64/pci/irq.c Sat Jun 15 17:59:19 2002
+++ b/arch/x86_64/pci/irq.c Sat Jun 15 17:59:19 2002
@@ -44,7 +44,7 @@
int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
};

-int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL;
+int (*pcibios_enable_irq_func)(struct pci_dev *dev);

/*
* Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table.
@@ -528,11 +528,13 @@
return 1;
}

+static int pirq_enable_irq(struct pci_dev *dev);
+
static int __init pcibios_irq_init(void)
{
DBG("PCI: IRQ init\n");

- if (pcibios_enable_irq)
+ if (pcibios_enable_irq_func)
return 0;

pirq_table = pirq_find_routing_table();
@@ -551,7 +553,7 @@
pirq_table = NULL;
}

- pcibios_enable_irq = pirq_enable_irq;
+ pcibios_enable_irq_func = pirq_enable_irq;

pcibios_fixup_irqs();
return 0;
@@ -634,7 +636,7 @@
pirq_penalty[irq] += 100;
}

-int pirq_enable_irq(struct pci_dev *dev)
+static int pirq_enable_irq(struct pci_dev *dev)
{
u8 pin;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
diff -Nru a/arch/x86_64/pci/pci.h b/arch/x86_64/pci/pci.h
--- a/arch/x86_64/pci/pci.h Sat Jun 15 17:59:19 2002
+++ b/arch/x86_64/pci/pci.h Sat Jun 15 17:59:19 2002
@@ -68,6 +68,5 @@
extern spinlock_t pci_config_lock;

void pcibios_fixup_irqs(void);
-int pirq_enable_irq(struct pci_dev *dev);

-extern int (*pcibios_enable_irq)(struct pci_dev *dev);
+extern int (*pcibios_enable_irq_func)(struct pci_dev *dev);
diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c
--- a/drivers/pci/pci.c Sat Jun 15 17:59:19 2002
+++ b/drivers/pci/pci.c Sat Jun 15 17:59:19 2002
@@ -555,6 +555,56 @@
return 0;
}

+/**
+ * pci_assign_irq - Assign an IRQ to a PCI device
+ * @dev: PCI device
+ *
+ * Figure out the the routing from the IRQ pin to an actual IRQ
+ * vector on the processor.
+ * Make sure the device is in D0 state (woken up).
+ * Returns an IRQ number (only supposed to be use for printk() or
+ * similar), or a negative error code.
+ */
+int
+pci_assign_irq(struct pci_dev *dev)
+{
+ int retval;
+
+ pci_set_power_state(dev, 0);
+
+ retval = pcibios_assign_irq(dev);
+ if (retval < 0)
+ return retval;
+
+ return dev->irq;
+}
+
+/**
+ * pci_enable_irq - Enable an IRQ on a PCI device
+ * @dev: PCI device
+ *
+ * Route an IRQ pin to an actual IRQ vector on the processor.
+ * Make sure the device is in D0 state (woken up).
+ * Returns an IRQ number (only supposed to be use for printk() or
+ * similar), or a negative error code.
+ */
+int
+pci_enable_irq(struct pci_dev *dev)
+{
+ int retval;
+
+ retval = pci_assign_irq(dev);
+ if (retval < 0)
+ return retval;
+
+ retval = pcibios_enable_irq(dev);
+ if (retval < 0)
+ return retval;
+
+ return dev->irq;
+}
+
+
static int __devinit pci_init(void)
{
struct pci_dev *dev;
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h Sat Jun 15 17:59:19 2002
+++ b/include/linux/pci.h Sat Jun 15 17:59:19 2002
@@ -500,6 +500,8 @@

void pcibios_fixup_bus(struct pci_bus *);
int pcibios_enable_device(struct pci_dev *);
+int pcibios_assign_irq(struct pci_dev *dev);
+int pcibios_enable_irq(struct pci_dev *dev);
char *pcibios_setup (char *str);

/* Used only when drivers/pci/setup.c is used */

-----------------------------------------------------------------------------
ChangeSet@1.493, 2002-06-15 16:35:23-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Introduce wrapper to set/clear PCI command bits

It'd be worth it even if we wouldn't make yet more use of it in
the next patch.

---------------------------------------------------------------------------

diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c
--- a/drivers/pci/pci.c Sat Jun 15 17:59:20 2002
+++ b/drivers/pci/pci.c Sat Jun 15 17:59:20 2002
@@ -239,6 +239,34 @@
return 0;
}

+static void
+pci_set_command(struct pci_dev *dev, u16 mask)
+{
+ u16 cmd;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+ if ((cmd | mask) != cmd) {
+ printk(KERN_DEBUG "PCI: Enabling device %s (%04x -> %04x)\n",
+ dev->slot_name, cmd, cmd | mask);
+ pci_write_config_word(dev, PCI_COMMAND, cmd | mask);
+ }
+}
+
+static void
+pci_clear_command(struct pci_dev *dev, u16 mask)
+{
+ u16 cmd;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+
+ if ((cmd & ~mask) != cmd) {
+ printk(KERN_DEBUG "PCI: Disabling device %s (%04x -> %04x)\n",
+ dev->slot_name, cmd, cmd & ~mask);
+ pci_write_config_word(dev, PCI_COMMAND, cmd & ~mask);
+ }
+}
+
/**
* pci_enable_device - Initialize device before it's used by a driver.
* @dev: PCI device to be initialized
@@ -268,13 +296,7 @@
void
pci_disable_device(struct pci_dev *dev)
{
- u16 pci_command;
-
- pci_read_config_word(dev, PCI_COMMAND, &pci_command);
- if (pci_command & PCI_COMMAND_MASTER) {
- pci_command &= ~PCI_COMMAND_MASTER;
- pci_write_config_word(dev, PCI_COMMAND, pci_command);
- }
+ pci_clear_command(dev, PCI_COMMAND_MASTER);
}

/**
@@ -426,14 +448,7 @@
void
pci_set_master(struct pci_dev *dev)
{
- u16 cmd;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- if (! (cmd & PCI_COMMAND_MASTER)) {
- DBG("PCI: Enabling bus mastering for device %s\n", dev->slot_name);
- cmd |= PCI_COMMAND_MASTER;
- pci_write_config_word(dev, PCI_COMMAND, cmd);
- }
+ pci_set_command(dev, PCI_COMMAND_MASTER);
pcibios_set_master(dev);
}

@@ -494,7 +509,6 @@
pci_set_mwi(struct pci_dev *dev)
{
int rc;
- u16 cmd;

#ifdef HAVE_ARCH_PCI_MWI
rc = pcibios_prep_mwi(dev);
@@ -505,12 +519,7 @@
if (rc)
return rc;

- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- if (! (cmd & PCI_COMMAND_INVALIDATE)) {
- DBG("PCI: Enabling Mem-Wr-Inval for device %s\n", dev->slot_name);
- cmd |= PCI_COMMAND_INVALIDATE;
- pci_write_config_word(dev, PCI_COMMAND, cmd);
- }
+ pci_set_command(dev, PCI_COMMAND_INVALIDATE);

return 0;
}
@@ -524,13 +533,7 @@
void
pci_clear_mwi(struct pci_dev *dev)
{
- u16 cmd;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- if (cmd & PCI_COMMAND_INVALIDATE) {
- cmd &= ~PCI_COMMAND_INVALIDATE;
- pci_write_config_word(dev, PCI_COMMAND, cmd);
- }
+ pci_clear_command(dev, PCI_COMMAND_INVALIDATE);
}

int

-----------------------------------------------------------------------------
ChangeSet@1.494, 2002-06-15 16:38:20-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Introduce pci_assign/enable_io/mmio functions

The main use of those will be internal to
pci_request/release_io/mmio. However, we export them, since
there'll always be hardware which needs special hacks...

---------------------------------------------------------------------------

diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c
--- a/drivers/pci/pci.c Sat Jun 15 17:59:22 2002
+++ b/drivers/pci/pci.c Sat Jun 15 17:59:22 2002
@@ -280,6 +280,13 @@
{
int err;

+ /* FIXME: this should be turned into
+ * pci_enable_irq(dev);
+ * pci_enable_mmio(dev);
+ * pci_enable_io(dev);
+ * after splitting the per arch pcibios_enable_device()
+ * into the appropriate parts
+ */
pci_set_power_state(dev, 0);
if ((err = pcibios_enable_device(dev)) < 0)
return err;
@@ -562,9 +569,9 @@
* pci_assign_irq - Assign an IRQ to a PCI device
* @dev: PCI device
*
+ * Make sure the device is in D0 state (woken up).
* Figure out the the routing from the IRQ pin to an actual IRQ
* vector on the processor.
- * Make sure the device is in D0 state (woken up).
* Returns an IRQ number (only supposed to be use for printk() or
* similar), or a negative error code.
*/
@@ -586,8 +593,8 @@
* pci_enable_irq - Enable an IRQ on a PCI device
* @dev: PCI device
*
- * Route an IRQ pin to an actual IRQ vector on the processor.
* Make sure the device is in D0 state (woken up).
+ * Route an IRQ pin to an actual IRQ vector on the processor.
* Returns an IRQ number (only supposed to be use for printk() or
* similar), or a negative error code.
*/
@@ -607,6 +614,111 @@
return dev->irq;
}

+static int
+pci_assign_resources(struct pci_dev *dev, unsigned long flags)
+{
+ int nr, retval = 0;
+ struct resource *r;
+
+ pci_set_power_state(dev, 0);
+
+ for (nr = 0; nr < PCI_ROM_RESOURCE; nr++) {
+ r = &dev->resource[nr];
+
+ /* Skip if other type */
+ if ((r->flags ^ flags) & (IORESOURCE_IO | IORESOURCE_MEM))
+ continue;
+
+ /* If unassigned, try to assign */
+ if (!r->start && r->end) {
+ retval = pci_assign_resource(dev, nr);
+ if (retval < 0)
+ break;
+ }
+ }
+ return retval;
+}
+
+/**
+ * pci_assign_mmio - Assign MMIO resources on a PCI device
+ * @dev: PCI device
+ *
+ * Make sure the device is in D0 state (woken up).
+ * Assign all as of yet unassigned MMIO resources for
+ * the PCI device.
+ * Returns 0 on success, or a negative error code.
+ */
+int
+pci_assign_mmio(struct pci_dev *dev)
+{
+ return pci_assign_resources(dev, IORESOURCE_MEM);
+}
+
+/**
+ * pci_assign_io - Assign IO resources on a PCI device
+ * @dev: PCI device
+ *
+ * Make sure the device is in D0 state (woken up).
+ * Assign all as of yet unassigned IO resources for
+ * the PCI device.
+ * Returns 0 on success, or a negative error code.
+ */
+int
+pci_assign_io(struct pci_dev *dev)
+{
+ return pci_assign_resources(dev, IORESOURCE_IO);
+}
+
+static int
+pci_enable_resources(struct pci_dev *dev, unsigned long flags)
+{
+ int retval;
+
+ retval = pci_assign_resources(dev, flags);
+ if (retval < 0)
+ return retval;
+
+ if (flags & IORESOURCE_IO)
+ pci_set_command(dev, PCI_COMMAND_IO);
+ if (flags & IORESOURCE_MEM)
+ pci_set_command(dev, PCI_COMMAND_MEMORY);
+
+ return 0;
+}
+
+/**
+ * pci_enable_mmio - Enable IO resources on a PCI device
+ * @dev: PCI device
+ *
+ * Make sure the device is in D0 state (woken up).
+ * Assign all as of yet unassigned IO resources for
+ * the PCI device.
+ * Enable MMIO in the PCI COMMAND config word
+ * Returns 0 on success, or a negative error code.
+ */
+int
+pci_enable_mmio(struct pci_dev *dev)
+{
+ return pci_enable_resources(dev, IORESOURCE_MEM);
+}
+
+/**
+ * pci_enable_io - Enable IO resources on a PCI device
+ * @dev: PCI device
+ *
+ * Make sure the device is in D0 state (woken up).
+ * Assign all as of yet unassigned IO resources for
+ * the PCI device.
+ * Enable IO in the PCI COMMAND config word
+ * Returns 0 on success, or a negative error code.
+ */
+int
+pci_enable_io(struct pci_dev *dev)
+{
+ return pci_enable_resources(dev, IORESOURCE_IO);
+}
+
+

static int __devinit pci_init(void)
{
@@ -654,6 +766,13 @@
EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
EXPORT_SYMBOL(pci_enable_wake);
+
+EXPORT_SYMBOL(pci_assign_irq);
+EXPORT_SYMBOL(pci_assign_mmio);
+EXPORT_SYMBOL(pci_assign_io);
+EXPORT_SYMBOL(pci_enable_irq);
+EXPORT_SYMBOL(pci_enable_mmio);
+EXPORT_SYMBOL(pci_enable_io);

/* Obsolete functions */

diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h Sat Jun 15 17:59:22 2002
+++ b/include/linux/pci.h Sat Jun 15 17:59:22 2002
@@ -576,6 +576,13 @@
int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
int pci_assign_resource(struct pci_dev *dev, int i);

+int pci_assign_irq(struct pci_dev *dev);
+int pci_enable_irq(struct pci_dev *dev);
+int pci_assign_mmio(struct pci_dev *dev);
+int pci_assign_io(struct pci_dev *dev);
+int pci_enable_mmio(struct pci_dev *dev);
+int pci_enable_io(struct pci_dev *dev);
+
/* Power management related routines */
int pci_save_state(struct pci_dev *dev, u32 *buffer);
int pci_restore_state(struct pci_dev *dev, u32 *buffer);

-----------------------------------------------------------------------------
ChangeSet@1.495, 2002-06-15 17:23:10-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Add pci_request_* and pci_release_* API

Docu is available inline. When switching a driver to this API,
calling pci_enable_device() is not necessary anymore, the needed
resources will be activated at pci_request_* time.

In particular, that means if your driver only uses MMIO, IO resources
on your card won't be assigned and enabled (it's possible that the
BIOS did that, though).

---------------------------------------------------------------------------

diff -Nru a/drivers/pci/pci.c b/drivers/pci/pci.c
--- a/drivers/pci/pci.c Sat Jun 15 17:59:23 2002
+++ b/drivers/pci/pci.c Sat Jun 15 17:59:23 2002
@@ -614,6 +614,57 @@
return dev->irq;
}

+/**
+ * pci_request_irq - Register an interrupt handler for a PCI device
+ * @dev: PCI device
+ * @handler: Function to be called when the IRQ occurs
+ * @irqflags: Interrupt type flags
+ * @dev_id: A cookie passed back to the handler function
+ *
+ * Make sure the device is in D0 state (woken up).
+ * Route an IRQ pin to an actual IRQ vector on the processor.
+ * Register a handler for this IRQ vector.
+ * Returns an IRQ number (only supposed to be use for printk() or
+ * similar), or a negative error code.
+ */
+int
+pci_request_irq(struct pci_dev *dev,
+ void (*handler)(int, void *, struct pt_regs *),
+ unsigned long flags, void *dev_id)
+{
+ int retval;
+
+ BUG_ON(!(flags & SA_SHIRQ));
+
+ retval = pcibios_assign_irq(dev);
+ if (retval < 0)
+ return retval;
+
+ retval = request_irq(dev->irq, handler, flags, dev->slot_name, dev_id);
+ if (retval < 0)
+ return retval;
+
+ retval = pcibios_enable_irq(dev);
+ if (retval < 0)
+ free_irq(dev->irq, dev_id);
+
+ return retval;
+}
+
+/**
+ * pci_release_irq - Unregister an interrupt handler for a PCI device
+ * @dev: PCI device
+ * @dev_id: Same cookie you passed when calling pci_request_irq()
+ *
+ * Unregister an IRQ handler previously registered with
+ * pci_request_irq().
+ */
+void
+pci_release_irq(struct pci_dev *dev, void *dev_id)
+{
+ free_irq(dev->irq, dev_id);
+}
+
static int
pci_assign_resources(struct pci_dev *dev, unsigned long flags)
{
@@ -718,7 +769,136 @@
return pci_enable_resources(dev, IORESOURCE_IO);
}

+static unsigned long
+pci_request_resources(struct pci_dev *pdev, unsigned int nr,
+ unsigned long flags, struct resource *root)
+{
+ struct pci_driver *drv = pci_dev_driver(pdev);
+ char *drv_name = drv ? drv->name : "unknown";
+ struct resource *res;
+
+ BUG_ON(nr >= PCI_ROM_RESOURCE);
+
+ /* Make sure we have the right type (IO/MMIO) */
+ if ((pci_resource_flags(pdev, nr) ^ flags) &
+ (IORESOURCE_IO | IORESOURCE_MEM))
+ goto err;
+
+ /* Assign and enable all resources of this type */
+ pci_enable_resources(pdev, flags);
+
+ res = __request_region(root, pci_resource_start(pdev, nr),
+ pci_resource_len(pdev, nr), drv_name);
+ if (!res)
+ goto err;
+
+ return res->start;
+
+ err:
+ /* Print extensive info so that drivers don't have to do it
+ themselves */
+ printk(KERN_INFO
+ "%s: failed to get %s(%d) for %s, %#lx-%#lx flags %#lx.\n",
+ pdev->slot_name, (flags == IORESOURCE_IO ? "IO" : "MMIO"),
+ nr, drv_name,
+ pci_resource_start(pdev, nr),
+ pci_resource_flags(pdev, nr),
+ pci_resource_end(pdev, nr));

+ return 0;
+}
+
+/**
+ * pci_request_mmio - Register a MMIO region on a PCI device
+ * @dev: PCI device
+ * @nr: The index of the Base Address Register (BAR)
+ *
+ * Make sure the device is in D0 state (woken up).
+ * Assign all as of yet unassigned MMIO resources for
+ * the PCI device.
+ * Enable MMIO in the PCI COMMAND config word
+ * Register the requested MMIO region with the kernel
+ * resource management.
+ * Map the MMIO region.
+ * Returns a cookie to be used with the MMIO functions
+ * (readb, writeb, ...), or NULL on error.
+ * In case of failure it also printk's extensive information
+ * about what went wrong (so you don't need to do that in your
+ * driver)
+ */
+void *
+pci_request_mmio(struct pci_dev *pdev, unsigned int nr)
+{
+ unsigned long base;
+ void *addr;
+
+ base = pci_request_resources(pdev, nr, IORESOURCE_MEM, &iomem_resource);
+ if (!base)
+ return 0;
+
+ addr = ioremap(base, pci_resource_len(pdev, nr));
+ if (!addr)
+ release_region(base, pci_resource_len(pdev, nr));
+
+ return addr;
+}
+
+/**
+ * pci_request_io - Register an IO region on a PCI device
+ * @dev: PCI device
+ * @nr: The index of the Base Address Register (BAR)
+ *
+ * Make sure the device is in D0 state (woken up).
+ * Assign all as of yet unassigned IO resources for
+ * the PCI device.
+ * Enable IO in the PCI COMMAND config word
+ * Register the requested IO region with the kernel
+ * resource management.
+ * Returns an IO port to be used with the IO functions
+ * (inb, outb, ...), or 0 on error.
+ * In case of failure it also printk's extensive information
+ * about what went wrong (so you don't need to do that in your
+ * driver)
+ */
+unsigned long
+pci_request_io(struct pci_dev *dev, unsigned int nr)
+{
+ return pci_request_resources(dev, nr, IORESOURCE_IO, &ioport_resource);
+}
+
+/**
+ * pci_release_mmio - Release an MMIO region on a PCI device
+ * @dev: PCI device
+ * @nr: The index of the Base Address Register (BAR)
+ * @addr: The cookie returned from pci_request_mmio()
+ *
+ * Unmaps and unregisters a MMIO region previously
+ * registered by pci_request_mmio().
+ */
+void
+pci_release_mmio(struct pci_dev *dev, unsigned int nr, void *addr)
+{
+ iounmap(addr);
+ __release_region(&iomem_resource,
+ pci_resource_start(dev, nr),
+ pci_resource_len(dev, nr));
+}
+
+/**
+ * pci_release_io - Release an IO region on a PCI device
+ * @dev: PCI device
+ * @nr: The index of the Base Address Register (BAR)
+ *
+ * Unregisters an IO region previously registered by
+ * pci_request_io().
+ */
+void
+pci_release_io(struct pci_dev *dev, unsigned int nr)
+{
+ __release_region(&ioport_resource,
+ pci_resource_start(dev, nr),
+ pci_resource_len(dev, nr));
+}

static int __devinit pci_init(void)
{
@@ -766,6 +946,13 @@
EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
EXPORT_SYMBOL(pci_enable_wake);
+
+EXPORT_SYMBOL(pci_request_irq);
+EXPORT_SYMBOL(pci_request_mmio);
+EXPORT_SYMBOL(pci_request_io);
+EXPORT_SYMBOL(pci_release_irq);
+EXPORT_SYMBOL(pci_release_mmio);
+EXPORT_SYMBOL(pci_release_io);

EXPORT_SYMBOL(pci_assign_irq);
EXPORT_SYMBOL(pci_assign_mmio);
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h Sat Jun 15 17:59:23 2002
+++ b/include/linux/pci.h Sat Jun 15 17:59:23 2002
@@ -576,6 +576,15 @@
int pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask);
int pci_assign_resource(struct pci_dev *dev, int i);

+int pci_request_irq(struct pci_dev *dev,
+ void (*handler)(int, void *, struct pt_regs *),
+ unsigned long flags, void *dev_id);
+void *pci_request_mmio(struct pci_dev *pdev, unsigned int nr);
+unsigned long pci_request_io(struct pci_dev *dev, unsigned int nr);
+void pci_release_irq(struct pci_dev *dev, void *dev_id);
+void pci_release_mmio(struct pci_dev *dev, unsigned int nr, void *addr);
+void pci_release_io(struct pci_dev *dev, unsigned int nr);
+
int pci_assign_irq(struct pci_dev *dev);
int pci_enable_irq(struct pci_dev *dev);
int pci_assign_mmio(struct pci_dev *dev);

-----------------------------------------------------------------------------
ChangeSet@1.496, 2002-06-15 17:31:38-05:00, kai@tp1.ruhr-uni-bochum.de
PCI: Convert two sample drivers to new interface

eepro100 and ymfpci still work fine on my laptop after the change,
even when zeroing out their BARs during boot.

---------------------------------------------------------------------------

diff -Nru a/drivers/net/eepro100.c b/drivers/net/eepro100.c
--- a/drivers/net/eepro100.c Sat Jun 15 17:59:24 2002
+++ b/drivers/net/eepro100.c Sat Jun 15 17:59:24 2002
@@ -558,6 +558,7 @@
static int __devinit eepro100_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
+ u16 cmd;
unsigned long ioaddr;
int irq;
int acpi_idle_state = 0, pm;
@@ -567,65 +568,55 @@
if (speedo_debug > 0 && did_version++ == 0)
printk(version);

- /* save power state before pci_enable_device overwrites it */
+ /* save power state before pci_request_* overwrites it */
pm = pci_find_capability(pdev, PCI_CAP_ID_PM);
if (pm) {
u16 pwr_command;
pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command);
acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
}
-
- if (pci_enable_device(pdev))
- goto err_out_free_mmio_region;
-
- pci_set_master(pdev);
-
- if (!request_region(pci_resource_start(pdev, 1),
- pci_resource_len(pdev, 1), "eepro100")) {
- printk (KERN_ERR "eepro100: cannot reserve I/O ports\n");
+ irq = pci_assign_irq(pdev);
+ if (irq < 0)
goto err_out_none;
- }
- if (!request_mem_region(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0), "eepro100")) {
- printk (KERN_ERR "eepro100: cannot reserve MMIO region\n");
- goto err_out_free_pio_region;
- }

- irq = pdev->irq;
#ifdef USE_IO
- ioaddr = pci_resource_start(pdev, 1);
+ ioaddr = pci_request_io(pdev, 1);
+ if (!ioaddr)
+ goto err_out_none;
+
if (speedo_debug > 2)
- printk("Found Intel i82557 PCI Speedo at I/O %#lx, IRQ %d.\n",
+ printk("Found Intel i82557 PCI Speedo at I/O %#lx IRQ %d.\n",
ioaddr, irq);
#else
- ioaddr = (unsigned long)ioremap(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
- if (!ioaddr) {
- printk (KERN_ERR "eepro100: cannot remap MMIO region %lx @ %lx\n",
- pci_resource_len(pdev, 0), pci_resource_start(pdev, 0));
- goto err_out_free_mmio_region;
- }
+ /* Even if using MMIO, the hardware won't work
+ unless IO is enabled, too */
+ if (pci_enable_io(pdev) < 0)
+ goto err_out_none;
+
+ ioaddr = (unsigned long) pci_request_mmio(pdev, 0);
+ if (!ioaddr)
+ goto err_out_none;
+
if (speedo_debug > 2)
- printk("Found Intel i82557 PCI Speedo, MMIO at %#lx, IRQ %d.\n",
+ printk("Found Intel i82557 PCI Speedo, MMIO at %#lx IRQ %d.\n",
pci_resource_start(pdev, 0), irq);
#endif
-
+ pci_set_master(pdev);

if (speedo_found1(pdev, ioaddr, cards_found, acpi_idle_state) == 0)
cards_found++;
else
- goto err_out_iounmap;
+ goto err_out_disable;

return 0;

-err_out_iounmap: ;
-#ifndef USE_IO
- iounmap ((void *)ioaddr);
+ err_out_disable:
+ pci_disable_device(pdev);
+#ifdef USE_IO
+ pci_release_io(pdev, 1);
+#else
+ pci_release_mmio(pdev, 0, (void *)ioaddr);
#endif
-err_out_free_mmio_region:
- release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-err_out_free_pio_region:
- release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
err_out_none:
return -ENODEV;
}
@@ -803,7 +794,6 @@
pci_set_drvdata (pdev, dev);

dev->base_addr = ioaddr;
- dev->irq = pdev->irq;

sp = dev->priv;
sp->pdev = pdev;
@@ -924,7 +914,7 @@
int retval;

if (speedo_debug > 1)
- printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
+ printk(KERN_DEBUG "%s: speedo_open()\n", dev->name);

MOD_INC_USE_COUNT;

@@ -939,11 +929,12 @@
sp->in_interrupt = 0;

/* .. we can safely take handler calls during init. */
- retval = request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, dev->name, dev);
- if (retval) {
+ retval = pci_request_irq(sp->pdev, &speedo_interrupt, SA_SHIRQ, dev);
+ if (retval < 0) {
MOD_DEC_USE_COUNT;
return retval;
}
+ dev->irq = retval;

dev->if_port = sp->default_port;

@@ -1834,7 +1825,7 @@
/* Shutting down the chip nicely fails to disable flow control. So.. */
outl(PortPartialReset, ioaddr + SCBPort);

- free_irq(dev->irq, dev);
+ pci_release_irq(sp->pdev, dev);

/* Print a few items for debugging. */
if (speedo_debug > 3)
@@ -2253,11 +2244,10 @@

unregister_netdev(dev);

- release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
- release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-
-#ifndef USE_IO
- iounmap((char *)dev->base_addr);
+#ifdef USE_IO
+ pci_release_io(pdev, 1);
+#else
+ pci_release_mmio(pdev, 0, (void *) dev->base_addr);
#endif

pci_free_consistent(pdev, TX_RING_SIZE * sizeof(struct TxFD)
@@ -2348,7 +2338,6 @@

/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
diff -Nru a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c
--- a/sound/oss/ymfpci.c Sat Jun 15 17:59:24 2002
+++ b/sound/oss/ymfpci.c Sat Jun 15 17:59:24 2002
@@ -2496,16 +2496,15 @@
static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent)
{
u16 ctrl;
- unsigned long base;
ymfpci_t *codec;
+ int irq, err;

- int err;
-
+#if 0
if ((err = pci_enable_device(pcidev)) != 0) {
printk(KERN_ERR "ymfpci: pci_enable_device failed\n");
return err;
}
- base = pci_resource_start(pcidev, 0);
+#endif

if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "ymfpci: no core\n");
@@ -2520,25 +2519,16 @@
codec->pci = pcidev;

pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev);
-
- if (request_mem_region(base, 0x8000, "ymfpci") == NULL) {
- printk(KERN_ERR "ymfpci: unable to request mem region\n");
+
+ codec->reg_area_virt = pci_request_mmio(pcidev, 0);
+ if (!codec->reg_area_virt)
goto out_free;
- }
-
- if ((codec->reg_area_virt = ioremap(base, 0x8000)) == NULL) {
- printk(KERN_ERR "ymfpci: unable to map registers\n");
- goto out_release_region;
- }

pci_set_master(pcidev);

- printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n",
- (char *)ent->driver_data, base, pcidev->irq);
-
ymfpci_aclink_reset(pcidev);
if (ymfpci_codec_ready(codec, 0, 1) < 0)
- goto out_unmap;
+ goto out_release_mmio;

#ifdef CONFIG_SOUND_YMFPCI_LEGACY
if (assigned == 0) {
@@ -2556,16 +2546,16 @@
goto out_disable_dsp;
ymf_memload(codec);

- if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) {
- printk(KERN_ERR "ymfpci: unable to request IRQ %d\n",
- pcidev->irq);
+ irq = pci_request_irq(pcidev, ymf_interrupt, SA_SHIRQ, codec);
+ if (irq < 0) {
+ printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", irq);
goto out_memfree;
}

/* register /dev/dsp */
if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) {
printk(KERN_ERR "ymfpci: unable to register dsp\n");
- goto out_free_irq;
+ goto out_release_irq;
}

/*
@@ -2591,6 +2581,9 @@
}
#endif /* CONFIG_SOUND_YMFPCI_LEGACY */

+ printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n",
+ (char *)ent->driver_data, pci_resource_start(pcidev, 0), irq);
+
/* put it into driver list */
list_add_tail(&codec->ymf_devs, &ymf_devs);
pci_set_drvdata(pcidev, codec);
@@ -2599,8 +2592,8 @@

out_unregister_sound_dsp:
unregister_sound_dsp(codec->dev_audio);
- out_free_irq:
- free_irq(pcidev->irq, codec);
+ out_release_irq:
+ pci_release_irq(pcidev, codec);
out_memfree:
ymfpci_memfree(codec);
out_disable_dsp:
@@ -2608,10 +2601,8 @@
ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL);
ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
ymfpci_writel(codec, YDSXGR_STATUS, ~0);
- out_unmap:
- iounmap(codec->reg_area_virt);
- out_release_region:
- release_mem_region(pci_resource_start(pcidev, 0), 0x8000);
+ out_release_mmio:
+ pci_release_mmio(pcidev, 0, codec->reg_area_virt);
out_free:
kfree(codec);
return -ENODEV;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/