Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

pci.c

/*
 * $Id: pci.c,v 1.134 2009-02-01 19:42:49 vrsieh Exp $
 * This is a pretty cheap implementation of a pci bios.
 * This mainly assigns resources to the interface cards, as most of them
 * won't work properly otherwise.
 *
 * Copyright (C) 2004-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "build_config.h"
#include "compiler.h"

#include "assert.h"
#include "stdio.h"

#include "ptrace.h"
#include "pci.h"
#include "io.h"
#include "debug.h"
#include "segment.h"
#include "const.h"
#include "cmos.h"
#include "entry.h"
#include "const.h"
#include <stdbool.h>

/* Where we start to search for space to map pci roms into */
#define ROMSPACESTART   0x000C0000L

#define PCI_COMMAND           0x04  /* 16 bits */
#define  PCI_COMMAND_IO       0x1   /* Enable response in I/O space */
#define  PCI_COMMAND_MEMORY   0x2   /* Enable response in Memory space */
#define  PCI_COMMAND_MASTER     0x4     /* Enable Bus Master Function */
#define PCI_BASE_ADDRESS_0    0x10  /* 32 bits */
#define  PCI_BASE_ADDRESS_SPACE           0x01  /* 0 = memory, 1 = I/O */
#define  PCI_BASE_ADDRESS_SPACE_IO  0x01
#define  PCI_BASE_ADDRESS_SPACE_MEMORY    0x00
#define  PCI_BASE_ADDRESS_MEM_TYPE_MASK   0x06
#define  PCI_BASE_ADDRESS_MEM_TYPE_32     0x00  /* 32 bit address */
#define  PCI_BASE_ADDRESS_MEM_TYPE_1M     0x02  /* Below 1M [obsolete] */
#define  PCI_BASE_ADDRESS_MEM_TYPE_64     0x04  /* 64 bit address */
#define  PCI_BASE_ADDRESS_MEM_PREFETCH    0x08  /* prefetchable? */
#define  PCI_BASE_ADDRESS_MEM_MASK  (~0x0fUL)
#define  PCI_BASE_ADDRESS_IO_MASK   (~0xFFFF0003UL)
#define PCI_HEADER_TYPE         0x0e    /* 8 bits */
#define  PCI_HEADER_TYPE_NORMAL           0
#define  PCI_HEADER_TYPE_BRIDGE           1
#define  PCI_HEADER_TYPE_CARDBUS    2
#define  PCI_HEADER_TYPE_MULTIFUNCDEVICE  0x80
#define PCI_ROM_ADDRESS       0x30  /* Bits 31..11 are address, 10..1 reserved */
#define  PCI_ROM_ADDRESS_ENABLE     0x01
#define PCI_ROM_ADDRESS_MASK  (~0x7ffUL)
#define PCI_INTERRUPT_LINE    0x3c  /* 8 bits */

#define PCI_CLASS_DISPLAY_VGA       0x0300
#define PCI_DEV_ID_SOUTHBRIDGE      0x07

/* Definitions for the Programmable Attribute Map Registers */
#define PAM_READABLE    1
#define PAM_WRITEABLE   2

/* FIXME JOSEF: old pci_devs no more needed already ? */

#define PCI_IO_START    0xc000
#define PCI_IO_END      0xcfff

#define PCI_MEM_START   0x50000000L
#define PCI_MEM_END     0x6fffffffL

/* ==================== RUNTIME_PM || RUNTIME_RM ==================== */
#if defined(RUNTIME_PM) || defined(RUNTIME_RM)

struct pci_routing_table_t {
      unsigned char signature[4];
      unsigned char version[2];
      unsigned short size;
      unsigned char router_bus;
      unsigned char router_dev;
      unsigned short pci_exclusive_irqs;
      unsigned short pci_interrupt_router_vendor_id;
      unsigned short pci_interrupt_router_device_id;
      unsigned short miniport_data[2];
      unsigned char reserved[11];
      unsigned char checksum;
      struct {
            unsigned char bus;
            unsigned char dev;
            unsigned char link_a;
            unsigned short bit_a __attribute__((__packed__));
            unsigned char link_b;
            unsigned short bit_b __attribute__((__packed__));
            unsigned char link_c;
            unsigned short bit_c __attribute__((__packed__));
            unsigned char link_d;
            unsigned short bit_d __attribute__((__packed__));
            unsigned char slot;
            unsigned char reserved;
      } table[8];
};

#endif /* RUNTIME_PM || RUNTIME_RM */
/* ==================== RUNTIME_PM || RUNTIME_RM ==================== */
#if defined(RUNTIME_PM) || defined(RUNTIME_RM)
#ifdef RUNTIME_RM
/*
 * See http://www.microsoft.com/whdc/archive/pciirq.mspx for specification
 * of this PCI interrupt routing table.
 */
/* FIXME VOSSI */
const struct pci_routing_table_t
      __attribute__((__aligned__(16), section(".rodata.pci.routing_table")))
pci_routing_table = {
      { '$', 'P', 'I', 'R' }, /* signature */
      { 0, 1 },         /* version */
      32 + (8 * 16),          /* table size */
      0,                /* PCI interrupt router bus */
      7 << 3,                 /* PCI interrupt router dev */
      0x0000,                 /* PCI exclusive IRQs */
      0x8086,                 /* PCI interrupt router vendor ID */
      0x122e,                 /* PCI interrupt router device ID */
      { 0, 0 },         /* Miniport data */
      { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* reserved */
      0,                /* checksum */

      { /* Data: PCI bus number, PCI dev number (Bit 7-3), link value and
             IRQ bitmap INTA-INTD, slot (0=embedded), reserved */
                /*      Bus, DevNr
                 *      linkA, bitmA, linkB, bitmB, linkC, bitmC, linkD, bitmD,
                 *      slot, reserved */
                { /* Device 0: Host to PCI Bridge */
                        0, 0 << 3,
                        0x60, 0xdef8, 0x61, 0xdef8, 0x62, 0xdef8, 0x63, 0xdef8,
                        0, 0
            },
            { /* Device 1: PCI to AGP Bridge */
                  0, 1 << 3,
                  0x61, 0xdef8, 0x62, 0xdef8, 0x63, 0xdef8, 0x60, 0xdef8,
                  0, 0
            },
            /* Device 2 - 6: Unused */
            { /* Device 7: PCI to ISA Bridge */
                  0, 7 << 3,
                  0x60, 0xdef8, 0x61, 0xdef8, 0x62, 0xdef8, 0x63, 0xdef8,
                  0, 0
            },
            { /* Device 8: First slot */
                  0, 2 << 3,
                  0x60, 0xdef8, 0x61, 0xdef8, 0x62, 0xdef8, 0x63, 0xdef8,
                  8, 0
            },
            { /* Device 9: Second slot */
                  0, 3 << 3,
                  0x61, 0xdef8, 0x62, 0xdef8, 0x63, 0xdef8, 0x60, 0xdef8,
                  9, 0
            },
            { /* Device 10: Third slot */
                  0, 4 << 3,
                        0x62, 0xdef8, 0x63, 0xdef8, 0x60, 0xdef8, 0x61, 0xdef8,
                  10, 0
            },
            { /* Device 11: Fourth slot */
                  0, 5 << 3,
                  0x63, 0xdef8, 0x60, 0xdef8, 0x61, 0xdef8, 0x62, 0xdef8,
                  11, 0
            },
            /* In case of GA-686DLX: Device 12: Adaptec IAC-7880U */
            { /* Device 12: Fifth slot */
                  0, 6 << 3,
                  0x60, 0xdef8, 0x61, 0xdef8, 0x62, 0xdef8, 0x63, 0xdef8,
                  12, 0
            },

      }
};
#else
extern const struct pci_routing_table_t pci_routing_table;
#endif
#endif /* RUNTIME_PM || RUNTIME_RM */
/* ==================== RUNTIME_PM || RUNTIME_RM || INIT_RM ==================== */
#if defined(RUNTIME_PM) || defined(RUNTIME_RM) || defined(INIT_RM)
/*
 * Helper macro function: Returns the next address after start that can be
 * divided by size (or start, if start already is aligned)
 */
#define ALIGNTO(start, size) ((((start) + (size) - 1) / (size)) * (size))

/*
 * Returns 1 if there is a device on the PCI Controller address, or 0
 * otherwise.
 * This function could/should (?) be made more reliable by additional
 * checks
 */
/* This function **must** be inline! */
static inline __attribute__((__always_inline__)) int
pci_checkpcicontr(void)
{
      outl(PCI_ADDR(0, 0, 0) | 0x80000000, PCICONF_ADDR);
      return inl(PCICONF_ADDR) == (PCI_ADDR(0, 0, 0) | 0x80000000);
}

#endif /* RUNTIME_PM || RUNTIME_RM || INIT_RM */
/* ==================== RUNTIME_PM || RUNTIME_RM ==================== */
#if defined(RUNTIME_PM) || defined(RUNTIME_RM)
/*
 * No code generation.
 * All functions defined as "always_inline".
 */

/* This function **must** be inline! */
static inline __attribute__((__always_inline__)) void
pci_real_select_reg(unsigned short bdf, unsigned short reg)
{
      outl(0x80000000 | ((uint32_t) bdf << 8) | (reg & 0x00fc), PCICONF_ADDR);
}

/* This **must** be inline! */
static inline __attribute__((__always_inline__)) void
func_b1xx(struct regs *regs)
{
      if (! pci_checkpcicontr()) {
            /* No pci found. */
            AH = 0xff;
            F |= 1 << 0; /* Set carry. */
            return;
      }

      if (AL == 0x01) {
            /*
             * Installation check.
             */
            AL = 0x00;
            /* Configuration space access mechanism 1 supported */
            AL |= (1 << 0);
            /* Configuration space access mechanism 2 supported */
            AL |= (0 << 1);
            /* Bit 2-3 reserved. */
            /* Special Cycle generation mechanism 1 supported */
            AL |= (0 << 4);
            /* Special Cycle generation mechanism 2 supported */
            AL |= (0 << 5);
            /* Bit 6-7 reserved. */

            /* Major/minor PCI spec. */
            BX = (0x02 << 8) | (0x10 << 0);

            /* Number of last PCI bus in system. */
            CX = 0x0000;
            /* Identification. */
            EDX = ((uint32_t) 'P' << 0)
                  | ((uint32_t) 'C' << 8)
                  | ((uint32_t) 'I' << 16)
                  | ((uint32_t) ' ' << 24);

#if 0
            /* Physical address protected-mode interface. */
            /*
             * Not specified by PCI-BIOS specification.
             * But described by Ralf Browns interrupt list and
             * implemented by Bochs BIOS.
             */
            EDI = ((unsigned long) get_cs() << 4)
                  | ((unsigned long) bios_pci_pm_entry);
#endif

      } else if (AL == 0x02) {
            /*
             * Find PCI device.
             */
            uint32_t id;

            id = ((uint32_t) CX << 16) | (uint32_t) DX;

            /* Should scan *all* PCI busses. FIXME VOSSI */
            for (BX = 0x00; ; BX++) {
                  if (BX == 0x0100) {
                        /* Device not found. */
                        AH = 0x86;
                        DX = CX;
                        ECX <<= 16;
                        F |= 1 << 0; /* Set carry. */
                        return;
                  }

                  pci_real_select_reg(BX, 0x00);

                  if (inl(PCICONF_DATA) == id) {
                        if (SI == 0) {
                              break;
                        } else {
                              SI--;
                        }
                  }
            }

#if 0
      } else if (AL == 0x03) {
            /*
             * Find PCI class code.
             */
            assert(0);

      } else if (AL == 0x06) {
            /*
             * Generate special cycle.
             *
             * In BH:   bus number
             *    EAX:  special cycle data
             *
             * Out      AH:   return code
             *    Carry:      0: success, 1: error
             */
            /* Correct? FIXME VOSSI */
            outl(0x8000ff00, PCICONF_ADDR);
            outl(EDX, PCICONF_DATA);
#endif

      } else if (AL == 0x08) {
            /*
             * Read configuration byte.
             */
            pci_real_select_reg(BX, DI);
            CL = inb(PCICONF_DATA + (DI & 0x0003));

      } else if (AL == 0x09) {
            /*
             * Read configuration word.
             */
            pci_real_select_reg(BX, DI);
            CX = inw(PCICONF_DATA + (DI & 0x0002));

      } else if (AL == 0x0a) {
            /*
             * Read configuration dword.
             */
            pci_real_select_reg(BX, DI);
            ECX = inl(PCICONF_DATA);

      } else if (AL == 0x0b) {
            /*
             * Write configuration byte.
             */
            pci_real_select_reg(BX, DI);
            outb(CL, PCICONF_DATA + (DI & 0x0003));

      } else if (AL == 0x0c) {
            /*
             * Write configuration word.
             */
            pci_real_select_reg(BX, DI);
            outw(CX, PCICONF_DATA + (DI & 0x0002));
            
      } else if (AL == 0x0d) {
            /*
             * Write configuration dword.
             */
            pci_real_select_reg(BX, DI);
            outl(ECX, PCICONF_DATA);

      } else if (AL == 0x0e) {
            /*
             * Get PCI interrupt routing info.
             */
            unsigned short size;
            unsigned short seg;
            unsigned short off;
            unsigned short n;
            const unsigned short *src;
            unsigned short dst;

            /* Get parameter. */
            size = get_word(ES, DI + 0);
            off  = get_word(ES, DI + 2);
            seg  = get_word(ES, DI + 4);

            /* Check size. */
            if (size < sizeof(pci_routing_table.table)) {
                  AH = 0x89;  /* Buffer too small. */
                  F |= 1 << 0;      /* Set carry. */
                  return;
            }
            size = sizeof(pci_routing_table.table);

            /* Return info. */
            put_word(ES, DI + 0, size);

            src = (const unsigned short *) pci_routing_table.table;
            dst = off;
            for (n = 0; n < size; n += sizeof(*src)) {
                  put_word(seg, dst, const_get(*src));
                  src++;
                  dst++;
            }

            BX = const_get(pci_routing_table.pci_exclusive_irqs);

            DEBUGPRINT(10, "0x0e: get PCI interrupt info\n");

#if 0
      } else if (AL == 0x0f) {
            /*
             * Set PCI interrupt routing info.
             */
#endif

      } else {
            AH = 0x81;
            F |= 1 << 0; /* Set carry. */
            return;
      }

      AH = 0x00;
      F &= ~(1 << 0); /* Clear carry. */
}

#endif /* RUNTIME_PM || RUNTIME_RM */
/* ==================== RUNTIME_RM ==================== */
#ifdef RUNTIME_RM
CODE16;

/*
 * See Bochs rombios.c.
 * FIXME
 */
void
bios_1a_b0xx(struct regs *regs)
{
      AH = AL;
      F |= 1 << 0; /* Set carry. */
}

void
bios_1a_b1xx(struct regs *regs)
{
      /* Must be completely inline! */
      func_b1xx(regs);
}

#endif /* RUNTIME_RM */
/* ==================== RUNTIME_PM ==================== */
#ifdef RUNTIME_PM
CODE32;

__attribute((section(".text.pci"))) void
bios_pci_pm(struct regs *regs)
{
      /* Must be completely inline! */
      func_b1xx(regs);
}

#endif /* RUNTIME_PM */
/* ==================== INIT_RM ==================== */
#ifdef INIT_RM
CODE16;

/** check, if any memory space flags are set, that the bios cannot handle.
 *  @param flags value as read from a base address register.
 *  @return true, if no unsupported flags are set, false otherwise.
 */
static bool
pci_can_handle_memflags(uint32_t flags)
{
      /* check if either PCI_BASE_ADDRESS_MEM_TYPE_64 or
       * PCI_BASE_ADDRESS_MEM_TYPE_1M is set. We cannot handle
       * these.
       */
      uint32_t cannot_handle = PCI_BASE_ADDRESS_MEM_TYPE_1M 
                        | PCI_BASE_ADDRESS_MEM_TYPE_64;

      return (flags & cannot_handle) == 0;
}

/** init one base address referring to mem/io space of one pci
 *  device.
 *  @param bus bus of the pci device.
 *  @param unit unit of the pci device.
 *  @param fun function of the pci device.
 *  @param addr address of the base register.
 *  @param currentio TODO
 *  @param currentmem TODO
 *  @return mask that contains either memory or io flag (according to the
 *          region), or 0 if it is not valid.
 */
static uint32_t
pci_device_base_addr_init(
      unsigned char bus,
      unsigned char unit,
      unsigned char fun,
      unsigned char addr,
      unsigned short *currentio,
      unsigned long *currentmem
)
{
      uint32_t tmpr;
      uint32_t oldr;
      uint32_t bamask;
      uint32_t baadrsp;
      uint32_t reqsize;

      tmpr = pci_creadl(bus, unit, fun, addr);
      baadrsp = tmpr & PCI_BASE_ADDRESS_SPACE;
      if (baadrsp == PCI_BASE_ADDRESS_SPACE_IO) {
            /* IO Space */
            bamask = PCI_BASE_ADDRESS_IO_MASK;
      } else { 
            /* Memory space */
            bamask = PCI_BASE_ADDRESS_MEM_MASK;

            if (! pci_can_handle_memflags(tmpr)) {
                  /* We cannot handle this */
                  return 0;
            }
      }
      if ((tmpr & bamask) != 0
       && (tmpr & bamask) != (0xffffffff & bamask)) {
            /* It seems this is already configured. */
            /* FIXME potyra: is this sane? */
            return 0;
      }
      oldr = tmpr; /* Save old value, then try to configure */
      outl((0xffffffff & bamask) | baadrsp, PCICONF_DATA);
      tmpr = inl(PCICONF_DATA);
      outl(oldr, PCICONF_DATA);
      if ((tmpr & bamask) == 0
       || (tmpr & bamask) == (0xffffffff & bamask)) {
            /* Not a valid reply. Abort. */
            return 0;
      }
      /* Now lets find out how much the device actually wants */
      reqsize = ~(tmpr & bamask) + 1;
      if (baadrsp == PCI_BASE_ADDRESS_SPACE_IO) {
            /* I/O Space */
            reqsize &= 0x0000ffff;
            if (256 < reqsize) {
                  /* Too much */
                  return 0;
            }
      } else {
            /* Memory Space */
            if ((uint32_t) 64*1024*1024 < reqsize) {
                  /* Too much */
                  return 0;
            }
      }
      DEBUGPRINT(50, "Bus %d Device %02xh Function %d wants %08x ",
                   bus, unit, fun, reqsize);
      /* OK, all is well... Lets assign it */
      if (baadrsp == PCI_BASE_ADDRESS_SPACE_IO) {
            /* I/O Space */
            DEBUGPRINT(50, "IO\nAssigning %04x - ",
                         ALIGNTO(*currentio, reqsize));
            outl(ALIGNTO(*currentio, reqsize) | baadrsp,
                        PCICONF_DATA);
            *currentio = ALIGNTO(*currentio, reqsize) + reqsize;
            assert(*currentio < PCI_IO_END + 2);
            DEBUGPRINT(50, "%04x\n", *currentio - 1);
            return PCI_COMMAND_IO;
      }

      /* Memory Space */
      DEBUGPRINT(50, "Memory\nAssigning %08x - ",
                   ALIGNTO(*currentmem, reqsize));
      outl(ALIGNTO(*currentmem, reqsize) | baadrsp,
            PCICONF_DATA);
      *currentmem = ALIGNTO(*currentmem, reqsize) + reqsize;
      assert(*currentmem < (PCI_MEM_END + 2));
      DEBUGPRINT(50, "%08x\n", *currentmem - 1);

      return PCI_COMMAND_MEMORY;
}

/** enable the pci device's io or mem space.
 *  @param bus pci bus
 *  @param unit pci device unit
 *  @param fun pci device function
 *  @param addr_io_mask mask with io or memory bit set (or both).
 */
static void
pci_enable_device(
      unsigned char bus,
      unsigned char unit,
      unsigned char fun,
      uint32_t addr_io_mask
)
{
      uint32_t cmd;

      /* check, if io or mem is present in mask. If not do nothing. */
      if ((addr_io_mask & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) == 0) {
            /* no io region or address space present, don't enable the
             * device */
            return;
      }

      cmd = pci_creadl(bus, unit, fun, PCI_COMMAND);
      cmd |= PCI_COMMAND_MASTER | addr_io_mask;
      pci_cwritel(bus, unit, fun, PCI_COMMAND, cmd);
}

/** enable the eeprom part of a pci device, if present.
 *  @param bus pci bus
 *  @param unit pci device unit
 *  @param fun pci device function
 *  @param currentmem TODO
 */
static void
pci_device_enable_eeprom(
      unsigned char bus,
      unsigned char unit,
      unsigned char fun,
      unsigned long *currentmem
)
{
      uint32_t oldr;
      uint32_t tmpr;
      uint32_t reqsize;

      /* Done with all the BASE_ADDRESS registers - now
       * assign space to the ROM too */
      oldr = pci_creadl(bus, unit, fun, PCI_ROM_ADDRESS);
      if (oldr == 0
       || oldr == (0xFFFFFFFF & PCI_ROM_ADDRESS_MASK)) {
            /* Doesn't have an address assigned yet */
            tmpr = (0xFFFFFFFF & PCI_ROM_ADDRESS_MASK);
            outl(tmpr, PCICONF_DATA);
            tmpr = inl(PCICONF_DATA) & PCI_ROM_ADDRESS_MASK;
            outl(oldr, PCICONF_DATA);
            if (tmpr != 0
             && tmpr != (0xFFFFFFFF & PCI_ROM_ADDRESS_MASK)) {
                  /* Device accepts an address */
                  reqsize = ~tmpr + 1;
                  if (reqsize <= (uint32_t) 16*1024*1024) {
                        /* PCI Specs allow no more than 16 MB for ROM */
                        outl(ALIGNTO(*currentmem, reqsize)
                                    & PCI_ROM_ADDRESS_MASK,
                              PCICONF_DATA);
                        *currentmem = ALIGNTO(*currentmem, reqsize)
                                   + reqsize;
                        assert(*currentmem < PCI_MEM_END + 2);
                  }
            }
      }
}


/** initialize one normal pci device.
 *  @param u pci device unit
 *  @param f pci device function
 *  @param currentio TODO
 *  @param currentmem TODO
 *  @param dev_vendor vendor/device id of the pci device
 */
static void
pci_device_init(
      unsigned char u,
      unsigned char f,
      unsigned short *currentio,
      unsigned long *currentmem,
      uint32_t dev_vendor
)
{
      uint32_t memoffs;
      uint32_t addr_io_mask = 0;

      /* check if we needs some quirks for specific devices.
       * drivers/pci/quirks.c of the linux kernel might be referred to
       * as a good read in regards to broken HW.
       */
      switch (dev_vendor) {
      case 0x71138086:
            /* Intel 82371AB/EB/MB PIIX4 ACPI 
             * (cf. arch_power_management.c)
             *
             *  base address (io space) at
             *     0x40 for power management
             *     0x90 for smbus
             */
            addr_io_mask |= pci_device_base_addr_init(0, u, f, 0x40, 
                                    currentio, currentmem);

            addr_io_mask |= pci_device_base_addr_init(0, u, f, 0x90, 
                                    currentio, currentmem);
            /* TODO potyra: doesn't appear in bios output */
            break;

      default:
            /* standard initialization below. */
            break;

      }


      /* intialize all (standard) base address regions */
      for (memoffs = 0; memoffs < 6; memoffs++) {
            addr_io_mask |= pci_device_base_addr_init(
                              0,
                              u,
                              f,
                              PCI_BASE_ADDRESS_0 + memoffs * 4,
                              currentio,
                              currentmem);
      }

      /* and enable the device eventually */
      /* FIXME potyra do this after rom? or does it not matter? */
      pci_enable_device(0, u, f, addr_io_mask);
      pci_device_enable_eeprom(0, u, f, currentmem);
}

static void
pci_bridge_init(void)
{
      /* FIXME someone */
}

static void
pci_bus_scan(unsigned char, unsigned char *, unsigned short *, unsigned long *, unsigned char *);

static void
cardbus_bridge_init(
      unsigned char b,
      unsigned char u,
      unsigned char f,
      unsigned char * pciirqs,
      unsigned short * currentio,
      unsigned long * currentmem,
      unsigned char * currentbus
) {
#define SZ_MEMWINDOW    0x02000000
#define SZ_IOWINDOW     0x4

      unsigned long tmpr;

      /* 
       * CardBus bridges are supposed to have the following registers
       * that need to be initialised by the bios for non-plug-and-play
       * operating systems:
       * CardBus socket base address (0x10) should get some free 4K memory area
       * two base and limit registers for memory windows 0x1C, 0x20 ,0x24, 0x28
       * two base and limit registers for IO windows 0x2C, 0x30, 0x34, 0x38
       * Command register (0x04) must be set to 0x07
       * Interrupt line register (0x3C) needs to be set to 0xFF
       * Legacy base address (0x44) should be assigned some useful value
       */

      DEBUGPRINT(1, "Configuring PCI-CardBus bridge on Bus %d...\n", b);
      
      pci_cwritel(b, u, f, 0x10, ALIGNTO(*currentmem, 0x1000));
      *currentmem = ALIGNTO(*currentmem, 0x1000) + 0x1000;

      pci_cwritel(b, u, f, 0x1C, ALIGNTO(*currentmem, SZ_MEMWINDOW));
      pci_cwritel(b, u, f, 0x20, SZ_MEMWINDOW);
      *currentmem = ALIGNTO(*currentmem, SZ_MEMWINDOW) + SZ_MEMWINDOW;

      pci_cwritel(b, u, f, 0x24, ALIGNTO(*currentmem, SZ_MEMWINDOW));
      pci_cwritel(b, u, f, 0x28, SZ_MEMWINDOW);
      *currentmem = ALIGNTO(*currentmem, SZ_MEMWINDOW) + SZ_MEMWINDOW;

      pci_cwritel(b, u, f, 0x2C, ALIGNTO(*currentio, SZ_IOWINDOW));
      pci_cwritel(b, u, f, 0x30, SZ_IOWINDOW);
      *currentio = ALIGNTO(*currentio, SZ_IOWINDOW) + SZ_IOWINDOW;
      
      pci_cwritel(b, u, f, 0x34, ALIGNTO(*currentio, SZ_IOWINDOW));
      pci_cwritel(b, u, f, 0x38, SZ_IOWINDOW);
      *currentio = ALIGNTO(*currentio, SZ_IOWINDOW) + SZ_IOWINDOW;

      tmpr = pci_creadl(b, u, f, 0x04);
      tmpr |= 0x07;
      outl(tmpr, PCICONF_DATA);

#if 0
      tmpr = pci_creadl(b, u, f, 0x3C);
      tmpr |= 0xFF;
      outl(tmpr, PCICONF_DATA);
#endif

      /* 
       * This is not very nice but we must check if register 0x44 is
       * already configured because some bridges share this register
       * between their functions.
       */
      tmpr = pci_creadl(b, u, f, 0x44);
      if (tmpr == 1) {
            outl( ALIGNTO(*currentio, SZ_IOWINDOW), PCICONF_DATA);
            *currentio = ALIGNTO(*currentio, SZ_IOWINDOW) + SZ_IOWINDOW;
      }
      
      /* 
       * Fill in values for primary bus, CardBus bus and subordinate
       * bus registers. This is a little bit too simple as I put the
       * same value in registers 19h and 1Ah indicating that there is
       * no other bus below the CardBus bus. I doubt a real BIOS would
       * do something else and to quote the PC Card Host System
       * Specification: "Normally, a CardBus bridge will be at the bottom
       * of the bus hierarchy and this register [1Ah] will hold the same
       * value as the CardBus Bus Number register."
       * Plug and Play Operating Systems reprogram these registers anyway.
       */
      (*currentbus)++;
      DEBUGPRINT(50, "Assigning CardBus bus number %d...\n", *currentbus);
      pci_cwriteb(b, u, f, 0x18, b);
      pci_cwriteb(b, u, f, 0x19, *currentbus);
      pci_cwriteb(b, u, f, 0x1A, *currentbus);
      pci_bus_scan(*currentbus, pciirqs, currentio, currentmem, currentbus);
}


/** init one function of a pci device, enabling the io space assigning eeprom
 *  memory regions and allocating irq(s).
 *  @param bus PCI bus number
 *  @param unit PCI unit
 *  @param fun PCI function
 *  @param pciirqs TODO
 *  @param currentio TODO
 *  @param currentmem TODO
 *  @param currentbus TODO
 *  @return true, if there might be more functions for this unit (i.e. if
 *          the header says it's a multifunction device, and the scanned
 *          function is actually present.
 */
static bool
pci_bus_init_function(
      unsigned char bus,
      unsigned char unit,
      unsigned char fun,
      unsigned char *pciirqs,
      unsigned short *currentio,
      unsigned long *currentmem,
      unsigned char *currentbus
)
{
      uint32_t tmpr;
      uint32_t dev_vendor;
      bool ret = true;

      dev_vendor = pci_creadl(bus, unit, fun, 0x00);
      if ((dev_vendor & 0x0000FFFF) == 0x0000FFFF) {
            /* invalid vendor id, not a pci device.
             * skip other functions. */
            return false;
      }

      /* Alright, it seems as if there really is a device
       * here... Lets see what kind of device it is, by
       * looking at the header type... */
      tmpr = (pci_creadb(bus, unit, fun, PCI_HEADER_TYPE));

      if ((tmpr & PCI_HEADER_TYPE_MULTIFUNCDEVICE) == 0) {
            /* first function, but no multi function device?
             * -> there won't be any more functions on this device,
             *  set return value to false 
             */
            if (fun == 0) {
                  ret = false;
            }
      }
      tmpr &= ~PCI_HEADER_TYPE_MULTIFUNCDEVICE;
      
      /* Before we start probing for mem/io-addresses, we
       * actually should disable the devices reaction to
       * any mem/io-access. This can be done in the STATUS
       * register. _HOWEVER_ we are in the BIOS, the first
       * thing that runs after a reset, which includes a
       * PCI-bus reset, and a PCI-bus reset automatically
       * lets devices go to that state. So we can safely
       * skip this. */

      switch (tmpr) {
      case PCI_HEADER_TYPE_NORMAL:
            pci_device_init(unit, fun, currentio, currentmem, dev_vendor);
            break;

      case PCI_HEADER_TYPE_BRIDGE:
            pci_bridge_init();
            break;

      case PCI_HEADER_TYPE_CARDBUS:
            cardbus_bridge_init(bus, unit, fun, pciirqs, 
                              currentio,
                              currentmem, 
                              currentbus);
            break;

      default:
            /* broken hw? */
            return ret;
      }
      
      /* Okay all Memory spaces should have been assigned.
       * Lets assign IRQs too. */
      tmpr = pci_creadl(bus, unit, fun, PCI_INTERRUPT_LINE);
      if ((tmpr & 0x000000FF) != 0) {
            /* already has an irq */
            return ret; 
      }

      if (((tmpr & 0x0000FF00)>>8) < 1
       || ((tmpr & 0x0000FF00)>>8) > 4) {
            /* not in INTA..INTD range */
            return ret;
      }

      /* The device seems to want an interrupt assigned. */
      /* slot 0 has first irq for INT_A, slot 1 2nd irq, slot 2 3rd irq... */
      /* INTB adds 1, INTC 2, INTD 3 to index */

      /* special case: southbridge does not have 'rotated' IRQs */
      if (unit == PCI_DEV_ID_SOUTHBRIDGE) {
            tmpr |= pciirqs[(0 + ((tmpr & 0x0000FF00)>>8) - 1) & 3];
      } else {
            tmpr |= pciirqs[(unit + ((tmpr & 0x0000FF00)>>8) - 1) & 3];
      }
      outl(tmpr, PCICONF_DATA);
      DEBUGPRINT(50, "Bus %d Device %02xh Function %d gets IRQ %d\n",
                   bus, unit, fun, (tmpr & 0x000000FF));

      return ret;
}

/* 
 * Scan the pci bus for devices that need to be configured.
 * This function will just detect the type of device and call the appropriate
 * functions. It will be called from pci_init with parameter b=0 and should be
 * called from pci_bridge_init (when it is implemented) to detect and configure
 * devices on secondary pci busses.
 */
static void
pci_bus_scan(
      unsigned char b,  /* PCI bus number */
      unsigned char * pciirqs,
      unsigned short * currentio,
      unsigned long * currentmem,
      unsigned char * currentbus
) {
      unsigned char u, f;     /* PCI unit, function */
      bool ret;
      
      DEBUGPRINT(1, "Configuring PCI devices on Bus %d...\n", b);
      for (u = 0; u <= 0x1f; u++) {
            for (f=0; f < 8; f++) {
                  ret = pci_bus_init_function(b, 
                                    u, 
                                    f, 
                                    pciirqs,
                                    currentio, 
                                    currentmem, 
                                    currentbus);

                  if (! ret) {
                        /* skip remaining functions */
                        break;
                  }
            }
      }
}

/*
 * We try to initialize all PCI cards on Bus 0 to nonconflicting values.
 */
void
pci_init(void)
{
      unsigned char u; /* PCI Unit */
      unsigned short currentio;
      unsigned long currentmem;
      unsigned char currentbus;
      unsigned long tmpr;
      unsigned char pciirqs[4];
      
      currentio = PCI_IO_START;
      currentmem = PCI_MEM_START;
      currentbus = 0;
      
      if (!pci_checkpcicontr()) {
            DEBUGPRINT(1, "No PCI controller detected...\n");
            return;
      }
      pciirqs[0] =  9;
      pciirqs[1] = 10;
      pciirqs[2] = 11;
      pciirqs[3] =  5;
      tmpr = pci_creadl(0, PCI_DEV_ID_SOUTHBRIDGE, 0, 0x00);
      if (tmpr == 0x71108086) {
            /* Program IRQ lines on BX southbr. to match bios setting */
            for (u = 0; u < 4; u++) {
                  tmpr = cmos_ext_get(pciirqs[u]);
                  if        (tmpr < 10) {
                        DEBUGPRINT(5, "int %d: No valid pci irq data "
                              "in cmos_ext, using default %d\n",
                              u+1, pciirqs[u]);
                  } else if (tmpr < 11) {
                        /* Assign no IRQ */
                        pciirqs[u] = 0;
                  } else if (tmpr < 16) { /* 11 = 3, 12 = 4 ... */
                        pciirqs[u] = tmpr - 8;
                  } else if (tmpr < 20) { /* 16 = 9, 17 = 10 ... */
                        pciirqs[u] = tmpr - 7;
                  } else if (tmpr < 22) { /* 20 = 14, 21 = 15 */
                        pciirqs[u] = tmpr - 6;
                  } else {
                        DEBUGPRINT(5, "int %d: No valid pci irq data "
                              "in cmos_ext, using default %d\n",
                              u+1, pciirqs[u]);
                  }
                  /* NOTE: this assumes the southbridge has device
                   * id 1, which it should have. */
                  pci_creadl(0, PCI_DEV_ID_SOUTHBRIDGE, 0, 0x60);
                  if (pciirqs[u] == 0) {
                        /* Disable int line */
                        outb(0x80, PCICONF_DATA + u);
                  } else {
                        outb(pciirqs[u], PCICONF_DATA + u);
                  }
            }
      } else {
            DEBUGPRINT(1, "Not a BX southbridge\n");
            /* use default irq 9, 10, 11, 12 for pci devices - and
             * pray that that is correct */
            return;
      }
      
      /* now look for devices attached to the PCI bus */
      pci_bus_scan(0, pciirqs, &currentio, &currentmem, &currentbus);
}

static void
pci_copymem(unsigned char * dst, unsigned char * src, unsigned long n)
{
      while (3<n) {
            *((unsigned long *)dst) = *((unsigned long *)src);
            dst += 4;
            src += 4;
            n -= 4;
      }
      while (0<n) {
            *dst = *src;
            dst++;
            src++;
            n--;
      }
}

void
pci_pamset(unsigned char * addr, unsigned char mode)
{
      unsigned long tmpr;
      unsigned long pamregnr;
      
      pamregnr = ((unsigned long)addr - 0xC0000UL) / 0x4000;
      assert(pamregnr < 12);
      pamregnr += 4; /* Just to make addressing easier */
      tmpr = pci_creadl(0, 0, 0,
                        (unsigned char)(0x58 + ((pamregnr >> 3) << 2)));
      tmpr &= ~(0x3UL << ((pamregnr % 8) << 2));
      tmpr |= ((uint32_t) (mode & 0x03) << ((pamregnr % 8) << 2));
      outl(tmpr, PCICONF_DATA);
}

/*
 * This function tries to find free space of size "howmuch" in the standard
 * below 1 MB expansion ROM range (ROMSPACESTART to 0xEFFF).
 * It will return the first fitting space it sees, or 0 if there wasn't any
 * space available.
 */
static void *
pci_findfreeromspace(unsigned long howmuch)
{
      unsigned short *curpos;
      unsigned short *lastfr;
      
      curpos = (unsigned short *) ROMSPACESTART;
      lastfr = (unsigned short *) ROMSPACESTART;
      while (curpos < (unsigned short *) 0x000F0000) {
            if (*curpos == 0xAA55) { /* Already used space */
                  unsigned short size;
                  /* add size of ROM - byte 2 * 512 */
                  size = *(curpos+1) & 0x00FF;
                  /* round up to 16k boundary */
                  if ( size % 32 ) {
                        size += 32 - (size % 32);
                  }
                  curpos += size * (512 / 2);
                  lastfr = curpos;
            } else {
                  curpos += (0x4000 / 2); /* 16k steps */
                  if (howmuch
                      <= ((unsigned long)curpos - (unsigned long)lastfr)) {
                        return (void *)lastfr;
                  }
            }
      }
      return (void *)0;
}

static int
pci_copyrom(void * p)
{
      struct pci_rom_exp_header {
            uint16_t rom_sig;
            uint8_t rom_sig_len;
            uint8_t init_vec[0x18 - 1 - 2];
            uint16_t pci_rom_data_off;
      } *header;
      struct pci_rom_data {
            uint32_t signature;
            uint16_t vendor_id;
            uint16_t device_id;
            uint16_t vital_data_off;
            uint16_t struct_len;
            uint16_t struct_rev;
            uint8_t class_code_base;
            uint8_t class_code_sub_class;
            uint8_t class_code_pio_int;
            uint8_t _dummy;
            uint16_t image_length;
            uint16_t code_revision;
            uint8_t code_type;
            uint8_t indicator;
      } *data;
      unsigned char *romp;
      unsigned char *d;
      unsigned long reqsize;
      unsigned long i;

      header = (struct pci_rom_exp_header *) p;
      if (header->rom_sig != 0xaa55) {
            /* No valid header. */
            return 0;
      }
      data = (struct pci_rom_data *)
                  ((unsigned char *) p + header->pci_rom_data_off);
      if (data->signature != 0x52494350) { /* 0x52494350 = 'PCIR' */
            /*
             * The ROM has a valid signature, but no PCI Data Structure
             * as it should. This is not a real PCI ROM then, more of a
             * ISA ROM someone put onto a PCI card. It seems a lot
             * of ROMs, including the Etherboot ROM, are that way. So we
             * assume the whole image is for i386 and start copying.
             */

      } else {
            /* We have a valid pci_rom_data structure. */
            if (data->code_type != 0) {
                  /* No x86 code. */
                  return 0;
            }
      }

      romp = (unsigned char *) p;
      reqsize = (uint32_t) *(romp + 2) << 9;
      d = (unsigned char *)pci_findfreeromspace(reqsize);
      if (d == 0) {
            return 0;
      }
      for (i = 0; i < (reqsize + 0x3FFF) / 0x4000; i++) {
            pci_pamset(d + (i * 0x4000), PAM_WRITEABLE | PAM_READABLE);
      }
      pci_copymem(d, romp, reqsize);
      return (uint32_t) d >> 4;
}

/*
 * Write protects the ROM at address given by p
 */
static int
pci_wprom(void * p)
{
      unsigned long i;
      unsigned long reqsize;
      unsigned char * romp;
      
      romp = (unsigned char *)p;
      reqsize = *(romp + 2) << 9;
      for (i=0; i < ((reqsize + 0x3FFF) / 0x4000); i++) {
            pci_pamset(romp + (i * 0x4000), PAM_READABLE);
      }
      return 0;
}

/*
 * Scan for and run PCI expansion ROMs (e.g. BootROM on NIC)
 *
 * We basically do this twice: First for VGA cards, then for
 * all the other cards. The reason for this is that VGA cards
 * HAVE to be mapped to C0000 if at all possible.
 */
void
pci_runexpansionroms(int onlydovga)
{
      /* This is _SLIGHTLY_ more complicated than doing this on ISA.
       * The main differences are: PCI ROMs ALWAYS get copied to RAM, they
       * can (and should!) be selfmodifying, and they can contain code for
       * multiple architectures (not only i386). */
      unsigned char u, f; /* PCI Unit and Function */
      unsigned long tmpr, oldr;
      unsigned char skipsubfs;

      tmpr = pci_creadl(0, 0, 0, 0x00);
      if (tmpr != 0x71928086) {
            /* Not a BX chipset, so abort. We cannot know how to program
             * memory mappings on this chipset, so we cannot possibly
             * initialize PCI ROMs. */
            return;
      }

      /* FIXME JOSEF */

      for (u=0; u<=0x1f; u++) {
            skipsubfs = 0;
            for (f=0; f<8; f++) {
                  if ((skipsubfs) && (f == 1)) {
                        break;
                  }
                  tmpr = pci_creadl(0, u, f, 0x00);
                  tmpr &= 0x0000FFFF;
                  if ((tmpr == 0x0000FFFF)
                   || (tmpr == 0x00000000)) {
                        skipsubfs = 1;
                        continue;
                  }
                  /* So there is a device - is it a normal device? */
                  tmpr = (pci_creadl(0, u, f, 0x0c)
                        & 0x00ff0000) >> 16;
                  if ((tmpr & PCI_HEADER_TYPE_MULTIFUNCDEVICE) == 0) {
                        skipsubfs = 1;
                  }
                  tmpr &= ~PCI_HEADER_TYPE_MULTIFUNCDEVICE;
                  if (tmpr != PCI_HEADER_TYPE_NORMAL) {
                        continue;
                  }
                  tmpr = pci_creadl(0, u, f, 0x08) >> 16;
                  if (onlydovga == 1) {
                        if (tmpr != PCI_CLASS_DISPLAY_VGA) {
                              continue;
                        }
                        tmpr = pci_creadl(0, u, f, PCI_COMMAND);
                        tmpr |= PCI_COMMAND_IO;
                        outl(tmpr, PCICONF_DATA);
                  } else {
                        if (tmpr == PCI_CLASS_DISPLAY_VGA) {
                              continue;
                        }
                  }

                  /* lets see if it has a ROM */
                  oldr = (pci_creadl(0, u, f, PCI_ROM_ADDRESS)
                        & PCI_ROM_ADDRESS_MASK);
                  if ((oldr == 0)
                   || (oldr == (0xFFFFFFFF & PCI_ROM_ADDRESS_MASK))) {
                        /* Doesn't have an address assigned */
                        continue;
                  }
                  /* OK now map it. Note that while the ROM is mapped,
                   * the card is allowed to ignore accesses to her
                   * other memory space(s) */
                  tmpr = (oldr | PCI_ROM_ADDRESS_ENABLE);
                  outl(tmpr,PCICONF_DATA);
                  tmpr = pci_copyrom((void *)(oldr & PCI_ROM_ADDRESS_MASK));
                  /* pci_copyrom modifies pci regs, so we need
                   * to readdress the right confspace reg */
                  pci_creadl(0, u, f, PCI_ROM_ADDRESS);
                  outl(oldr, PCICONF_DATA); /* Put back old setting */
                  if (tmpr == 0) {  /* Error - either there was
                                 * no valid ROM, or we are
                                 * out of memory. */
                        DEBUGPRINT(1,
                             "Could not copy BootROM!\n");
                  } else {
                        /* now call the copied ROM */
                        DEBUGPRINT(10, "PCI BootROM copied to 0x%04x\n",
                                 tmpr);
                        
                        call_ext(tmpr);
                        
                        DEBUGPRINT(10, "PCI BootROM executed\n");
                        if (get_byte(tmpr, 2) == 0) {
                              /* The Init-Function set its own ROM
                               * size to 0, that means it wants to
                               * be removed completly. So we destroy
                               * the signature to mark the space
                               * as free. */
                              put_word(tmpr, 0, 0);
                        } else {
                              /* Write protect the remaining ROM */
                              pci_wprom((void *) (tmpr << 4));
                        }
                  }
            }
      }
}

static const struct {
      unsigned short classid;
      unsigned short idmask;
      const char * desc;
} pcidevs[] = {
      /* Special categories need to come first (list is searched from
       * top to bottom) */
      { 0x0101, 0xffff, "IDE Controller" },
      { 0x0200, 0xffff, "Ethernet Controller" },
      { 0x0600, 0xffff, "Host Bridge" },
      { 0x0601, 0xffff, "ISA Bridge" },
      { 0x0c03, 0xffff, "USB Controller" },
      /* Now follow the more general categories */
      { 0x0000, 0xff00, "\"Undefined\"" },
      { 0x0100, 0xff00, "Storage Controller" },
      { 0x0200, 0xff00, "Network Controller" },
      { 0x0300, 0xff00, "Display Adapter" },
      { 0x0400, 0xff00, "Multimedia Device" },
      { 0x0500, 0xff00, "Memory" },
      { 0x0600, 0xff00, "Bridge" },
      { 0x0700, 0xff00, "Communication Device" },
      { 0x0800, 0xff00, "System Device" },
      { 0x0900, 0xff00, "Input Device" },
      { 0x0a00, 0xff00, "Docking Device" },
      { 0x0b00, 0xff00, "Processor" },
      { 0x0c00, 0xff00, "Serial Device" },
      { 0, 0, (const char *)0 } /* list is 0-terminated */
};

void
pci_listdevices(CONST char * linestart)
{
      int devicesfound;
      unsigned char u, f; /* PCI Unit and Function */
      unsigned long memoffs;
      unsigned long iopo;
      unsigned long mema;
      unsigned long tmpr;
      unsigned char headertype;
      unsigned long badsp;
      unsigned char skipsubfs;
      unsigned short i;

      devicesfound = 0;
      if (!pci_checkpcicontr()) {
            bprintf("%sNo PCI controller detected.\n",
                   linestart);
            return;
      }
      DEBUGPRINT(1, "Scanning for PCI devices on Bus 0...\n");
      for (u=0; u<=0x1f; u++) {
            skipsubfs = 0;
            for (f=0; f<8; f++) {
                  if ((skipsubfs) && (f == 1)) {
                        break;
                  }
                  tmpr = pci_creadl(0, u, f, 0x00);
                  tmpr &= 0x0000FFFF;
                  if ((tmpr == 0x0000FFFF) || (tmpr == 0x00000000)) {
                        skipsubfs = 1;
                        continue;
                  }
                  tmpr = inl(PCICONF_DATA); /* Read again because we cleared
                                             * a few bits above... */
                  if (devicesfound == 0) { /* First entry - print table header */
                        bprintf("%sAddr    Vend Dev. I/O  Mem      IRQ Devicetype\n",
                              linestart);
                  }
                  devicesfound++;
                  bprintf("%s%02x:%02x.%d %04x %04x ",
                        linestart,
                        0, u, f,
                        (tmpr & 0xFFFF), (tmpr >> 16));
                  iopo = mema = 0;
                  /* Look at headertype (to see if it's a bridge or smth. */
                  headertype = pci_creadb(0, u, f, PCI_HEADER_TYPE);
                  if ((headertype & PCI_HEADER_TYPE_MULTIFUNCDEVICE) == 0) {
                        skipsubfs = 1;
                  }
                  headertype &= ~PCI_HEADER_TYPE_MULTIFUNCDEVICE;
                  if (headertype == PCI_HEADER_TYPE_NORMAL) {
                        for (memoffs=0; memoffs<6; memoffs++) {
                              tmpr=pci_creadl(0, u, f,
                                      PCI_BASE_ADDRESS_0+(memoffs*4));
                              if ((tmpr & PCI_BASE_ADDRESS_SPACE)
                               == PCI_BASE_ADDRESS_SPACE_IO) {
                                    if (iopo != 0) continue;
                                    badsp = tmpr & PCI_BASE_ADDRESS_IO_MASK;
                                    if (badsp == 0) continue;
                                    if ((0xffffffff & PCI_BASE_ADDRESS_IO_MASK)
                                     == badsp) { /* invalid */
                                          continue;
                                    }
                                    iopo=badsp;
                              } else { /* Memory space */
                                    if (mema != 0) continue;
                                    badsp = tmpr & PCI_BASE_ADDRESS_MEM_MASK;
                                    if (badsp == 0) continue;
                                    if ((0xffffffff & PCI_BASE_ADDRESS_MEM_MASK)
                                     == badsp) { /* invalid */
                                          continue;
                                    }
                                    mema=badsp;
                              }
                        }
                        if (iopo == 0) {
                              bprintf("none ");
                        } else {
                              bprintf("%04x ", iopo);
                        }
                        if (mema == 0) {
                              bprintf("none      ");
                        } else {
                              bprintf("%08x  ", mema);
                        }
                        tmpr = pci_creadl(0, u, f, PCI_INTERRUPT_LINE);
                        if ((tmpr & 0xff) == 0) { /* IRQ */
                              bprintf("-  ");
                        } else {
                              bprintf("%2d ", (tmpr & 0x000000FF));
                        }
                        tmpr = (pci_creadl(0, u, f, 0x08) >> 16);
                        i = 0;
                        while (const_get(pcidevs[i].idmask) != 0) {
                              if ((const_get(pcidevs[i].classid)
                                 & const_get(pcidevs[i].idmask))
                               == (tmpr
                                 & const_get(pcidevs[i].idmask))) {
                                    bprintf("%s", const_get(pcidevs[i].desc));
                                    break;
                              }
                              i++;
                        }
                        if (const_get(pcidevs[i].idmask) == 0) {
                              /* So we found nothing */
                              bprintf("Unknown Device Type 0x%04x",
                                    tmpr);
                        }
                  } else if (headertype == PCI_HEADER_TYPE_BRIDGE) {
                        bprintf("???      ???      ?   ");
                        bprintf("PCI-to-PCI Bridge");
                  } else if (headertype == PCI_HEADER_TYPE_CARDBUS) {
                        /* In my opinion it is better to print no
                         * information about memory and io windows
                         * at all than printing only half of it.
                         * I did not see a real bios anyway that
                         * prints the devices' used space at system
                         * boot... */
                        bprintf("n/a  n/a       ");
                        tmpr = pci_creadl(0, u, f, PCI_INTERRUPT_LINE);
                        if (((tmpr & 0xFF) == 0)
                        || ((tmpr & 0xFF) == 0xFF)) {
                              bprintf("-  ");
                        } else {
                              bprintf("%2d ", (tmpr & 0x000000FF));
                        }
                        bprintf("PCI Cardbus Bridge");
                  } else { /* No normal device */
                        bprintf("Unknown Header Type 0x%02x", headertype);
                  }
                  
                  bprintf("\n");
            }
      }
}

#endif /* INIT_RM */

Generated by  Doxygen 1.6.0   Back to index