patch-2.4.22 linux-2.4.22/arch/x86_64/kernel/pci-pc.c
Next file: linux-2.4.22/arch/x86_64/kernel/process.c
Previous file: linux-2.4.22/arch/x86_64/kernel/pci-irq.c
Back to the patch index
Back to the overall index
- Lines: 267
- Date:
2003-08-25 04:44:40.000000000 -0700
- Orig file:
linux-2.4.21/arch/x86_64/kernel/pci-pc.c
- Orig date:
2002-11-28 15:53:12.000000000 -0800
diff -urN linux-2.4.21/arch/x86_64/kernel/pci-pc.c linux-2.4.22/arch/x86_64/kernel/pci-pc.c
@@ -18,6 +18,7 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/acpi.h>
#include <asm/segment.h>
#include <asm/io.h>
@@ -31,6 +32,14 @@
struct pci_bus *pci_root_bus;
struct pci_ops *pci_root_ops;
+int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *value) = NULL;
+int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 value) = NULL;
+
+static int pci_using_acpi_prt = 0;
+
+/* XXX: not taken by all accesses currently */
+static spinlock_t pci_config_lock = SPIN_LOCK_UNLOCKED;
+
/*
* Direct access to PCI hardware...
*/
@@ -43,6 +52,66 @@
#define CONFIG_CMD(dev, where) (0x80000000 | (dev->bus->number << 16) | (dev->devfn << 8) | (where & ~3))
+#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \
+ (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
+
+static int pci_conf1_read (int seg, int bus, int dev, int fn, int reg, int len,
+ u32 *value)
+{
+ unsigned long flags;
+
+ if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+ return -EINVAL;
+
+ spin_lock_irqsave(&pci_config_lock, flags);
+
+ outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
+
+ switch (len) {
+ case 1:
+ *value = inb(0xCFC + (reg & 3));
+ break;
+ case 2:
+ *value = inw(0xCFC + (reg & 2));
+ break;
+ case 4:
+ *value = inl(0xCFC);
+ break;
+ }
+
+ spin_unlock_irqrestore(&pci_config_lock, flags);
+
+ return 0;
+}
+
+static int pci_conf1_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value)
+{
+ unsigned long flags;
+
+ if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+ return -EINVAL;
+
+ spin_lock_irqsave(&pci_config_lock, flags);
+
+ outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
+
+ switch (len) {
+ case 1:
+ outb((u8)value, 0xCFC + (reg & 3));
+ break;
+ case 2:
+ outw((u16)value, 0xCFC + (reg & 2));
+ break;
+ case 4:
+ outl((u32)value, 0xCFC);
+ break;
+ }
+
+ spin_unlock_irqrestore(&pci_config_lock, flags);
+
+ return 0;
+}
+
static int pci_conf1_read_config_byte(struct pci_dev *dev, int where, u8 *value)
{
outl(CONFIG_CMD(dev,where), 0xCF8);
@@ -106,6 +175,76 @@
outb(FUNC(dev->devfn), 0xCF8); \
outb(dev->bus->number, 0xCFA);
+#define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg)
+
+static int pci_conf2_read (int seg, int bus, int dev, int fn, int reg, int len, u32 *value)
+{
+ unsigned long flags;
+
+ if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+ return -EINVAL;
+
+ if (dev & 0x10)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ spin_lock_irqsave(&pci_config_lock, flags);
+
+ outb((u8)(0xF0 | (fn << 1)), 0xCF8);
+ outb((u8)bus, 0xCFA);
+
+ switch (len) {
+ case 1:
+ *value = inb(PCI_CONF2_ADDRESS(dev, reg));
+ break;
+ case 2:
+ *value = inw(PCI_CONF2_ADDRESS(dev, reg));
+ break;
+ case 4:
+ *value = inl(PCI_CONF2_ADDRESS(dev, reg));
+ break;
+ }
+
+ outb (0, 0xCF8);
+
+ spin_unlock_irqrestore(&pci_config_lock, flags);
+
+ return 0;
+}
+
+static int pci_conf2_write (int seg, int bus, int dev, int fn, int reg, int len, u32 value)
+{
+ unsigned long flags;
+
+ if ((bus > 255) || (dev > 31) || (fn > 7) || (reg > 255))
+ return -EINVAL;
+
+ if (dev & 0x10)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ spin_lock_irqsave(&pci_config_lock, flags);
+
+ outb((u8)(0xF0 | (fn << 1)), 0xCF8);
+ outb((u8)bus, 0xCFA);
+
+ switch (len) {
+ case 1:
+ outb ((u8)value, PCI_CONF2_ADDRESS(dev, reg));
+ break;
+ case 2:
+ outw ((u16)value, PCI_CONF2_ADDRESS(dev, reg));
+ break;
+ case 4:
+ outl ((u32)value, PCI_CONF2_ADDRESS(dev, reg));
+ break;
+ }
+
+ outb (0, 0xCF8);
+
+ spin_unlock_irqrestore(&pci_config_lock, flags);
+
+ return 0;
+}
+
static int pci_conf2_read_config_byte(struct pci_dev *dev, int where, u8 *value)
{
SET(dev);
@@ -244,6 +383,23 @@
#endif
+struct pci_bus * __devinit pcibios_scan_root(int busnum)
+{
+ struct list_head *list;
+ struct pci_bus *bus;
+
+ list_for_each(list, &pci_root_buses) {
+ bus = pci_bus_b(list);
+ if (bus->number == busnum) {
+ /* Already scanned */
+ return bus;
+ }
+ }
+
+ printk("PCI: Probing PCI hardware (bus %02x)\n", busnum);
+
+ return pci_scan_bus(busnum, pci_root_ops, NULL);
+}
/*
* Several buggy motherboards address only 16 devices and mirror
@@ -387,10 +543,38 @@
pci_read_bridge_bases(b);
}
+void __devinit pcibios_config_init(void)
+{
+ /*
+ * Try all known PCI access methods. Note that we support using
+ * both PCI BIOS and direct access, with a preference for direct.
+ */
+
+#ifdef CONFIG_PCI_DIRECT
+ if ((pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2))
+ && (pci_root_ops = pci_check_direct())) {
+ if (pci_root_ops == &pci_direct_conf1) {
+ pci_config_read = pci_conf1_read;
+ pci_config_write = pci_conf1_write;
+ }
+ else {
+ pci_config_read = pci_conf2_read;
+ pci_config_write = pci_conf2_write;
+ }
+ } else
+ printk("??? no pci access\n");
+#endif
+
+ return;
+}
+
void __devinit pcibios_init(void)
{
struct pci_ops *dir = NULL;
+ if (!pci_root_ops)
+ pcibios_config_init();
+
#ifdef CONFIG_PCI_DIRECT
if (pci_probe & (PCI_PROBE_CONF1 | PCI_PROBE_CONF2))
dir = pci_check_direct();
@@ -403,12 +587,18 @@
}
printk(KERN_INFO "PCI: Probing PCI hardware\n");
- pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL);
-
+#ifdef CONFIG_ACPI_PCI
+ if (!acpi_pci_irq_init())
+ pci_using_acpi_prt = 1;
+#endif
+ if (!pci_using_acpi_prt) {
+ pci_root_bus = pcibios_scan_root(0);
pcibios_irq_init();
pci_scan_mptable();
pcibios_fixup_peer_bridges();
pcibios_fixup_irqs();
+ }
+
pcibios_resource_survey();
#ifdef CONFIG_GART_IOMMU
@@ -469,6 +659,15 @@
if ((err = pcibios_enable_resources(dev, mask)) < 0)
return err;
+
+#ifdef CONFIG_ACPI_PCI
+ if (pci_using_acpi_prt) {
+ acpi_pci_irq_enable(dev);
+ return 0;
+ }
+#endif
+
pcibios_enable_irq(dev);
+
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)