Re: 2.5.20 - Xircom PCI Cardbus doesn't work

From: Kai Germaschewski (kai@tp1.ruhr-uni-bochum.de)
Date: Sat Jun 15 2002 - 18:00:53 EST


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/



This archive was generated by hypermail 2b29 : Sat Jun 15 2002 - 22:00:33 EST