summaryrefslogtreecommitdiff
path: root/linux/pcmcia-cs/modules/pci_fixup.c
diff options
context:
space:
mode:
authorThomas Schwinge <tschwinge@gnu.org>2006-07-26 22:52:56 +0000
committerThomas Schwinge <tschwinge@gnu.org>2009-06-18 00:26:38 +0200
commitaffb871b497129f7f191cfaf7bd458342b22c05f (patch)
treea4397a3b44fd945c92f7e6f8905dc52044f5dff6 /linux/pcmcia-cs/modules/pci_fixup.c
parent93e37a911f657e6e0b1f9b47a0761bb5fbf4ac35 (diff)
2006-07-27 Thomas Schwinge <tschwinge@gnu.org>
Import a number of files from the pcmcia-cs package, version 3.2.8, available from <http://pcmcia-cs.sourceforge.net/>. * linux/pcmcia-cs/clients/3c574_cs.c: Import file. * linux/pcmcia-cs/clients/3c589_cs.c: Likewise. * linux/pcmcia-cs/clients/ax8390.h: Likewise. * linux/pcmcia-cs/clients/axnet_cs.c: Likewise. * linux/pcmcia-cs/clients/fmvj18x_cs.c: Likewise. * linux/pcmcia-cs/clients/nmclan_cs.c: Likewise. * linux/pcmcia-cs/clients/ositech.h: Likewise. * linux/pcmcia-cs/clients/pcnet_cs.c: Likewise. * linux/pcmcia-cs/clients/smc91c92_cs.c: Likewise. * linux/pcmcia-cs/clients/xirc2ps_cs.c: Likewise. * linux/pcmcia-cs/include/linux/crc32.h: Likewise. * linux/pcmcia-cs/include/linux/init.h: Likewise. * linux/pcmcia-cs/include/linux/slab.h: Likewise. * linux/pcmcia-cs/include/pcmcia/bulkmem.h: Likewise. * linux/pcmcia-cs/include/pcmcia/bus_ops.h: Likewise. * linux/pcmcia-cs/include/pcmcia/ciscode.h: Likewise. * linux/pcmcia-cs/include/pcmcia/cisreg.h: Likewise. * linux/pcmcia-cs/include/pcmcia/cistpl.h: Likewise. * linux/pcmcia-cs/include/pcmcia/cs.h: Likewise. * linux/pcmcia-cs/include/pcmcia/cs_types.h: Likewise. * linux/pcmcia-cs/include/pcmcia/driver_ops.h: Likewise. * linux/pcmcia-cs/include/pcmcia/ds.h: Likewise. * linux/pcmcia-cs/include/pcmcia/mem_op.h: Likewise. * linux/pcmcia-cs/include/pcmcia/ss.h: Likewise. * linux/pcmcia-cs/include/pcmcia/version.h: Likewise. * linux/pcmcia-cs/modules/bulkmem.c: Likewise. * linux/pcmcia-cs/modules/cirrus.h: Likewise. * linux/pcmcia-cs/modules/cistpl.c: Likewise. * linux/pcmcia-cs/modules/cs.c: Likewise. * linux/pcmcia-cs/modules/cs_internal.h: Likewise. * linux/pcmcia-cs/modules/ds.c: Likewise. * linux/pcmcia-cs/modules/ene.h: Likewise. * linux/pcmcia-cs/modules/i82365.c: Likewise. * linux/pcmcia-cs/modules/i82365.h: Likewise. * linux/pcmcia-cs/modules/o2micro.h: Likewise. * linux/pcmcia-cs/modules/pci_fixup.c: Likewise. * linux/pcmcia-cs/modules/ricoh.h: Likewise. * linux/pcmcia-cs/modules/rsrc_mgr.c: Likewise. * linux/pcmcia-cs/modules/smc34c90.h: Likewise. * linux/pcmcia-cs/modules/ti113x.h: Likewise. * linux/pcmcia-cs/modules/topic.h: Likewise. * linux/pcmcia-cs/modules/vg468.h: Likewise. * linux/pcmcia-cs/modules/yenta.h: Likewise. * linux/pcmcia-cs/wireless/hermes.c: Likewise. * linux/pcmcia-cs/wireless/hermes.h: Likewise. * linux/pcmcia-cs/wireless/hermes_rid.h: Likewise. * linux/pcmcia-cs/wireless/ieee802_11.h: Likewise. * linux/pcmcia-cs/wireless/orinoco.c: Likewise. * linux/pcmcia-cs/wireless/orinoco.h: Likewise. * linux/pcmcia-cs/wireless/orinoco_cs.c: Likewise.
Diffstat (limited to 'linux/pcmcia-cs/modules/pci_fixup.c')
-rw-r--r--linux/pcmcia-cs/modules/pci_fixup.c674
1 files changed, 674 insertions, 0 deletions
diff --git a/linux/pcmcia-cs/modules/pci_fixup.c b/linux/pcmcia-cs/modules/pci_fixup.c
new file mode 100644
index 0000000..b892f63
--- /dev/null
+++ b/linux/pcmcia-cs/modules/pci_fixup.c
@@ -0,0 +1,674 @@
+/*======================================================================
+
+ Kernel fixups for PCI device support
+
+ pci_fixup.c 1.33 2002/10/12 19:02:59
+
+ PCI bus fixups: various bits of code that don't really belong in
+ the PCMCIA subsystem, but may or may not be available from the
+ kernel, depending on kernel version. The basic idea is to make
+ 2.0.* and 2.2.* kernels look like they have the 2.3.* features.
+
+======================================================================*/
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+/* We use these for setting up CardBus bridges */
+#include "yenta.h"
+#include "i82365.h"
+
+#define VERSION KERNEL_VERSION
+#if (LINUX_VERSION_CODE < VERSION(2,3,24))
+
+/* Default memory base addresses for CardBus controllers */
+static u_int cb_mem_base[] = { 0x0, 0x68000000, 0xf8000000 };
+MODULE_PARM(cb_mem_base, "i");
+
+/* PCI bus number overrides for CardBus controllers */
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+INT_MODULE_PARM(cb_bus_base, 0);
+INT_MODULE_PARM(cb_bus_step, 2);
+INT_MODULE_PARM(cb_pci_irq, 0);
+
+#endif
+
+/* (exported) mask of interrupts reserved for PCI devices */
+u32 pci_irq_mask = 0;
+
+/*======================================================================
+
+ Basic PCI services missing from older kernels: device lookup, etc
+
+======================================================================*/
+
+#if (LINUX_VERSION_CODE < VERSION(2,1,0))
+struct pci_dev *pci_devices = NULL;
+struct pci_bus pci_root = {
+ parent: NULL,
+ children: NULL,
+ next: NULL,
+ self: NULL,
+ devices: NULL,
+ number: 0
+};
+#endif
+
+#if (LINUX_VERSION_CODE < VERSION(2,1,93))
+
+struct pci_dev *pci_find_slot(u_int bus, u_int devfn)
+{
+ struct pci_dev *dev;
+ for (dev = pci_devices; dev; dev = dev->next)
+ if ((dev->devfn == devfn) && (bus == dev->bus->number))
+ return dev;
+#if (LINUX_VERSION_CODE > VERSION(2,1,0))
+ return NULL;
+#else
+ {
+ struct pci_bus *b;
+ u8 hdr;
+ u32 id, class;
+
+ if (pcibios_read_config_byte(bus, devfn & ~7, PCI_HEADER_TYPE, &hdr))
+ return NULL;
+ if (PCI_FUNC(devfn) && !(hdr & 0x80))
+ return NULL;
+ pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &id);
+ if ((id == 0) || (id == 0xffffffff))
+ return NULL;
+ dev = kmalloc(sizeof *dev, GFP_ATOMIC);
+ if (!dev)
+ return NULL;
+ memset(dev, 0, sizeof *dev);
+ dev->devfn = devfn;
+ pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &dev->irq);
+ dev->vendor = id & 0xffff;
+ dev->device = id >> 16;
+ pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
+ if (dev->irq == 255)
+ dev->irq = 0;
+ dev->class = class >> 8;
+ for (b = &pci_root; b; b = b->next)
+ if (b->number == bus) break;
+ if (!b) {
+ b = kmalloc(sizeof *b, GFP_ATOMIC);
+ if (!b) {
+ kfree(dev);
+ return NULL;
+ }
+ memset(b, 0, sizeof *b);
+ b->number = bus;
+ b->next = pci_root.next;
+ pci_root.next = b;
+ }
+ dev->bus = b;
+ return dev;
+ }
+#endif
+}
+
+struct pci_dev *pci_find_class(u_int class, struct pci_dev *from)
+{
+ static u16 index = 0;
+ u8 bus, devfn;
+ if (from == NULL)
+ index = 0;
+ if (pcibios_find_class(class, index++, &bus, &devfn) == 0)
+ return pci_find_slot(bus, devfn);
+ else
+ return NULL;
+}
+
+#endif /* (LINUX_VERSION_CODE < VERSION(2,1,93)) */
+
+/*======================================================================
+
+ PCI Interrupt Routing Table parser
+
+ This only needs to be done once per boot: we scan the BIOS for
+ the routing table, and then look for devices that have interrupt
+ assignments that the kernel doesn't know about. If we find any,
+ we update their pci_dev structures and write the PCI interrupt
+ line registers.
+
+======================================================================*/
+
+#if (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__)
+
+#pragma pack(1)
+
+struct slot_entry {
+ u8 bus, devfn;
+ struct pirq_pin {
+ u8 link;
+ u16 irq_map;
+ } pin[4];
+ u8 slot;
+ u8 reserved;
+};
+
+struct routing_table {
+ u32 signature;
+ u8 minor, major;
+ u16 size;
+ u8 bus, devfn;
+ u16 pci_mask;
+ u32 compat;
+ u32 miniport;
+ u8 reserved[11];
+ u8 checksum;
+ struct slot_entry entry[0];
+};
+
+#pragma pack()
+
+/*
+ The meaning of the link bytes in the routing table is vendor
+ specific. We need code to get and set the routing information.
+*/
+
+static u8 pIIx_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq;
+ /* link should be 0x60, 0x61, 0x62, 0x63 */
+ pci_read_config_byte(router, link, &pirq);
+ return (pirq < 16) ? pirq : 0;
+}
+
+static void pIIx_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ pci_write_config_byte(router, link, irq);
+}
+
+static u8 via_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq = 0;
+ /* link should be 1, 2, 3, 5 */
+ if (link < 6)
+ pci_read_config_byte(router, 0x55 + (link>>1), &pirq);
+ return (link & 1) ? (pirq >> 4) : (pirq & 15);
+}
+
+static void via_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ u8 pirq;
+ pci_read_config_byte(router, 0x55 + (link>>1), &pirq);
+ pirq &= (link & 1) ? 0x0f : 0xf0;
+ pirq |= (link & 1) ? (irq << 4) : (irq & 15);
+ pci_write_config_byte(router, 0x55 + (link>>1), pirq);
+}
+
+static u8 opti_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq = 0;
+ /* link should be 0x02, 0x12, 0x22, 0x32 */
+ if ((link & 0xcf) == 0x02)
+ pci_read_config_byte(router, 0xb8 + (link >> 5), &pirq);
+ return (link & 0x10) ? (pirq >> 4) : (pirq & 15);
+}
+
+static void opti_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ u8 pirq;
+ pci_read_config_byte(router, 0xb8 + (link >> 5), &pirq);
+ pirq &= (link & 0x10) ? 0x0f : 0xf0;
+ pirq |= (link & 0x10) ? (irq << 4) : (irq & 15);
+ pci_write_config_byte(router, 0xb8 + (link >> 5), pirq);
+}
+
+static u8 ali_link(struct pci_dev *router, u8 link)
+{
+ /* No, you're not dreaming */
+ static const u8 map[] =
+ { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+ u8 pirq;
+ /* link should be 0x01..0x08 */
+ pci_read_config_byte(router, 0x48 + ((link-1)>>1), &pirq);
+ return (link & 1) ? map[pirq&15] : map[pirq>>4];
+}
+
+static void ali_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ /* Inverse of map in ali_link */
+ static const u8 map[] =
+ { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
+ u8 pirq;
+ pci_read_config_byte(router, 0x48 + ((link-1)>>1), &pirq);
+ pirq &= (link & 1) ? 0x0f : 0xf0;
+ pirq |= (link & 1) ? (map[irq] << 4) : (map[irq] & 15);
+ pci_write_config_byte(router, 0x48 + ((link-1)>>1), pirq);
+}
+
+static u8 cyrix_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq;
+ /* link should be 1, 2, 3, 4 */
+ link--;
+ pci_read_config_byte(router, 0x5c + (link>>1), &pirq);
+ return ((link & 1) ? pirq >> 4 : pirq & 15);
+}
+
+static void cyrix_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ u8 pirq;
+ link--;
+ pci_read_config_byte(router, 0x5c + (link>>1), &pirq);
+ pirq &= (link & 1) ? 0x0f : 0xf0;
+ pirq |= (link & 1) ? (irq << 4) : (irq & 15);
+ pci_write_config_byte(router, 0x5c + (link>>1), pirq);
+}
+
+/*
+ A table of all the PCI interrupt routers for which we know how to
+ interpret the link bytes.
+*/
+
+#ifndef PCI_DEVICE_ID_INTEL_82371FB_0
+#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82371SB_0
+#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82371AB_0
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82443MX_1
+#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7198
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82443MX_1
+#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7198
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801AA_0
+#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801AB_0
+#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BA_0
+#define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BAM_0
+#define PCI_DEVICE_ID_INTEL_82801BAM_0 0x244c
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C586_0
+#define PCI_DEVICE_ID_VIA_82C586_0 0x0586
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C596
+#define PCI_DEVICE_ID_VIA_82C596 0x0596
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C686
+#define PCI_DEVICE_ID_VIA_82C686 0x0686
+#endif
+#ifndef PCI_DEVICE_ID_SI
+#define PCI_DEVICE_ID_SI 0x1039
+#endif
+#ifndef PCI_DEVICE_ID_SI_503
+#define PCI_DEVICE_ID_SI_503 0x0008
+#endif
+#ifndef PCI_DEVICE_ID_SI_496
+#define PCI_DEVICE_ID_SI_496 0x0496
+#endif
+
+#define ID(a,b) PCI_VENDOR_ID_##a,PCI_DEVICE_ID_##a##_##b
+
+struct router {
+ u16 vendor, device;
+ u8 (*xlate)(struct pci_dev *, u8);
+ void (*init)(struct pci_dev *, u8, u8);
+} router_table[] = {
+ { ID(INTEL, 82371FB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82371SB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82371AB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82443MX_1), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801AA_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801AB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801BA_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801BAM_0), &pIIx_link, &pIIx_init },
+ { ID(VIA, 82C586_0), &via_link, &via_init },
+ { ID(VIA, 82C596), &via_link, &via_init },
+ { ID(VIA, 82C686), &via_link, &via_init },
+ { ID(OPTI, 82C700), &opti_link, &opti_init },
+ { ID(AL, M1533), &ali_link, &ali_init },
+ { ID(SI, 503), &pIIx_link, &pIIx_init },
+ { ID(SI, 496), &pIIx_link, &pIIx_init },
+ { ID(CYRIX, 5530_LEGACY), &cyrix_link, &cyrix_init }
+};
+#define ROUTER_COUNT (sizeof(router_table)/sizeof(router_table[0]))
+
+/* Global variables for current interrupt routing table */
+static struct routing_table *pirq = NULL;
+static struct pci_dev *router_dev = NULL;
+static struct router *router_info = NULL;
+
+#ifndef __va
+#define __va(x) (x)
+#endif
+
+static void scan_pirq_table(void)
+{
+ struct routing_table *r;
+ struct pci_dev *router, *dev;
+ u8 pin, fn, *p;
+ int i, j;
+ struct slot_entry *e;
+
+ /* Scan the BIOS for the routing table signature */
+ for (p = (u8 *)__va(0xf0000); p < (u8 *)__va(0xfffff); p += 16)
+ if ((p[0] == '$') && (p[1] == 'P') &&
+ (p[2] == 'I') && (p[3] == 'R')) break;
+ if (p >= (u8 *)__va(0xfffff))
+ return;
+
+ pirq = r = (struct routing_table *)p;
+ printk(KERN_INFO "PCI routing table version %d.%d at %#06x\n",
+ r->major, r->minor, (u32)r & 0xfffff);
+ for (i = j = 0; i < 16; i++)
+ j += (r->pci_mask >> i) & 1;
+ if (j > 4)
+ printk(KERN_NOTICE " bogus PCI irq mask %#04x!\n",
+ r->pci_mask);
+ else
+ pci_irq_mask |= r->pci_mask;
+
+ router_dev = router = pci_find_slot(r->bus, r->devfn);
+ if (router) {
+ for (i = 0; i < ROUTER_COUNT; i++) {
+ if ((router->vendor == router_table[i].vendor) &&
+ (router->device == router_table[i].device))
+ break;
+ if (((r->compat & 0xffff) == router_table[i].vendor) &&
+ ((r->compat >> 16) == router_table[i].device))
+ break;
+ }
+ if (i == ROUTER_COUNT)
+ printk(KERN_INFO " unknown PCI interrupt router %04x:%04x\n",
+ router->vendor, router->device);
+ else
+ router_info = &router_table[i];
+ }
+
+ for (e = r->entry; (u8 *)e < p+r->size; e++) {
+ for (fn = 0; fn < 8; fn++) {
+ dev = pci_find_slot(e->bus, e->devfn | fn);
+ if ((dev == NULL) || (dev->irq != 0)) continue;
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ if ((pin == 0) || (pin == 255)) continue;
+ if (router_info) {
+ dev->irq = router_info->xlate(router, e->pin[pin-1].link);
+ } else {
+ /* Fallback: see if only one irq possible */
+ int map = e->pin[pin-1].irq_map;
+ if (map && (!(map & (map-1))))
+ dev->irq = ffs(map)-1;
+ }
+ if (dev->irq) {
+ printk(KERN_INFO " %02x:%02x.%1x -> irq %d\n",
+ e->bus, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), dev->irq);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
+ dev->irq);
+ }
+ }
+ }
+}
+
+#endif /* (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__) */
+
+/*======================================================================
+
+ PCI device enabler
+
+ This is not at all generic... it is mostly a hack to correctly
+ configure CardBus bridges.
+
+======================================================================*/
+
+#if (LINUX_VERSION_CODE < VERSION(2,3,24))
+
+static int check_cb_mapping(u_int phys)
+{
+ /* A few sanity checks to validate the bridge mapping */
+ char *virt = ioremap(phys, 0x1000);
+ int ret = ((readb(virt+0x800+I365_IDENT) & 0x70) ||
+ (readb(virt+0x800+I365_CSC) &&
+ readb(virt+0x800+I365_CSC) &&
+ readb(virt+0x800+I365_CSC)));
+ int state = readl(virt+CB_SOCKET_STATE) >> 16;
+ ret |= (state & ~0x3000) || !(state & 0x3000);
+ ret |= readl(virt+CB_SOCKET_FORCE);
+ iounmap(virt);
+ return ret;
+}
+
+static void setup_cb_bridge(struct pci_dev *dev)
+{
+ u8 bus, sub;
+ u32 phys;
+ int i;
+
+ /* This is nasty, but where else can we put it? */
+ if (PCI_FUNC(dev->devfn) == 0) {
+ struct pci_dev *sib;
+ sib = pci_find_slot(dev->bus->number, dev->devfn+1);
+ if (sib) {
+ u8 a, b;
+ u32 c, d;
+ /* Check for bad PCI bus numbering */
+ pci_read_config_byte(dev, CB_CARDBUS_BUS, &a);
+ pci_read_config_byte(sib, CB_CARDBUS_BUS, &b);
+ if (a == b) {
+ pci_write_config_byte(dev, CB_CARDBUS_BUS, 0);
+ pci_write_config_byte(sib, CB_CARDBUS_BUS, 0);
+ }
+ /* check for bad register mapping */
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &c);
+ pci_read_config_dword(sib, PCI_BASE_ADDRESS_0, &d);
+ if ((c != 0) && (c == d)) {
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0);
+ pci_write_config_dword(sib, PCI_BASE_ADDRESS_0, 0);
+ }
+ }
+ }
+
+ /* Assign PCI bus numbers, if needed */
+ pci_read_config_byte(dev, CB_CARDBUS_BUS, &bus);
+ pci_read_config_byte(dev, CB_SUBORD_BUS, &sub);
+ if ((cb_bus_base > 0) || (bus == 0)) {
+ if (cb_bus_base <= 0) cb_bus_base = 0x20;
+ bus = cb_bus_base;
+ sub = cb_bus_base+cb_bus_step;
+ cb_bus_base += cb_bus_step+1;
+ pci_write_config_byte(dev, CB_CARDBUS_BUS, bus);
+ pci_write_config_byte(dev, CB_SUBORD_BUS, sub);
+ }
+
+ /* Create pci_bus structure for the CardBus, if needed */
+ {
+ struct pci_bus *child, *parent = dev->bus;
+ for (child = parent->children; child; child = child->next)
+ if (child->number == bus) break;
+ if (!child) {
+ child = kmalloc(sizeof(struct pci_bus), GFP_KERNEL);
+ memset(child, 0, sizeof(struct pci_bus));
+ child->self = dev;
+ child->primary = bus;
+ child->number = child->secondary = bus;
+ child->subordinate = sub;
+ child->parent = parent;
+#if (LINUX_VERSION_CODE >= VERSION(2,3,15))
+ child->ops = parent->ops;
+#endif
+ child->next = parent->children;
+ parent->children = child;
+ }
+ }
+
+ /* Map the CardBus bridge registers, if needed */
+ pci_write_config_dword(dev, CB_LEGACY_MODE_BASE, 0);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &phys);
+ if ((phys == 0) || (cb_mem_base[0] != 0)) {
+ /* Make sure the bridge is awake so we can test it */
+ pci_set_power_state(dev, 0);
+ for (i = 0; i < sizeof(cb_mem_base)/sizeof(u_int); i++) {
+ phys = cb_mem_base[i];
+ if (phys == 0) continue;
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, phys);
+ if ((i == 0) || (check_cb_mapping(phys) == 0)) break;
+ }
+ if (i == sizeof(cb_mem_base)/sizeof(u_int)) {
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0);
+ } else {
+ cb_mem_base[0] = cb_mem_base[i] + 0x1000;
+ }
+ }
+}
+
+#ifdef __i386__
+
+static u8 pirq_init(struct pci_dev *router, struct pirq_pin *pin)
+{
+ u16 map = pin->irq_map;
+ u8 irq = 0;
+ if (pirq->pci_mask)
+ map &= pirq->pci_mask;
+ if (cb_pci_irq)
+ map = 1<<cb_pci_irq;
+ /* Be conservative: only init irq if the mask is unambiguous */
+ if (map && (!(map & (map-1)))) {
+ irq = ffs(map)-1;
+ router_info->init(router, pin->link, irq);
+ pci_irq_mask |= (1<<irq);
+ }
+ return irq;
+}
+
+static void setup_cb_bridge_irq(struct pci_dev *dev)
+{
+ struct slot_entry *e;
+ u8 pin;
+ u32 phys;
+ char *virt;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &phys);
+ if (!pin || !phys)
+ return;
+ virt = ioremap(phys, 0x1000);
+ if (virt) {
+ /* Disable any pending interrupt sources */
+ writel(0, virt+CB_SOCKET_MASK);
+ writel(-1, virt+CB_SOCKET_EVENT);
+ iounmap(virt);
+ }
+ for (e = pirq->entry; (u8 *)e < (u8 *)pirq + pirq->size; e++) {
+ if ((e->bus != dev->bus->number) ||
+ (e->devfn != (dev->devfn & ~7)))
+ continue;
+ dev->irq = pirq_init(router_dev, &e->pin[pin-1]);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+ break;
+ }
+}
+
+#endif
+
+int pci_enable_device(struct pci_dev *dev)
+{
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) {
+ setup_cb_bridge(dev);
+ }
+#ifdef __i386__
+ /* In certain cases, if the interrupt can be deduced, but was
+ unrouted when the pirq table was scanned, we'll try to set it
+ up now. */
+ if (!dev->irq && pirq && (router_info) &&
+ ((dev->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS)) {
+ setup_cb_bridge_irq(dev);
+ }
+#endif
+ return 0;
+}
+
+int pci_set_power_state(struct pci_dev *dev, int state)
+{
+ u16 tmp, cmd;
+ u32 base, bus;
+ u8 a, b, pmcs;
+ pci_read_config_byte(dev, PCI_STATUS, &a);
+ if (a & PCI_STATUS_CAPLIST) {
+ pci_read_config_byte(dev, PCI_CB_CAPABILITY_POINTER, &b);
+ while (b != 0) {
+ pci_read_config_byte(dev, b+PCI_CAPABILITY_ID, &a);
+ if (a == PCI_CAPABILITY_PM) {
+ pmcs = b + PCI_PM_CONTROL_STATUS;
+ /* Make sure we're in D0 state */
+ pci_read_config_word(dev, pmcs, &tmp);
+ if (!(tmp & PCI_PMCS_PWR_STATE_MASK)) break;
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &base);
+ pci_read_config_dword(dev, CB_PRIMARY_BUS, &bus);
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, pmcs, PCI_PMCS_PWR_STATE_D0);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, base);
+ pci_write_config_dword(dev, CB_PRIMARY_BUS, bus);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ break;
+ }
+ pci_read_config_byte(dev, b+PCI_NEXT_CAPABILITY, &b);
+ }
+ }
+ return 0;
+}
+
+#endif /* (LINUX_VERSION_CODE < VERSION(2,3,24)) */
+
+/*======================================================================
+
+ General setup and cleanup entry points
+
+======================================================================*/
+
+void pci_fixup_init(void)
+{
+ struct pci_dev *p;
+
+#if (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__)
+ scan_pirq_table();
+ pci_for_each_dev(p)
+ if (((p->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) &&
+ (p->irq == 0)) break;
+ if (p && !pirq)
+ printk(KERN_INFO "No PCI interrupt routing table!\n");
+ if (!pirq && cb_pci_irq)
+ printk(KERN_INFO "cb_pci_irq will be ignored.\n");
+#endif
+
+ pci_for_each_dev(p)
+ pci_irq_mask |= (1<<p->irq);
+
+#ifdef __alpha__
+#define PIC 0x4d0
+ pci_irq_mask |= inb(PIC) | (inb(PIC+1) << 8);
+#endif
+}
+
+void pci_fixup_done(void)
+{
+#if (LINUX_VERSION_CODE < VERSION(2,1,0))
+ struct pci_dev *d, *dn;
+ struct pci_bus *b, *bn;
+ for (d = pci_devices; d; d = dn) {
+ dn = d->next;
+ kfree(d);
+ }
+ for (b = pci_root.next; b; b = bn) {
+ bn = b->next;
+ kfree(b);
+ }
+#endif
+}