Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

#include <ll/i386/hw-instr.h>
#include <ll/i386/hw-data.h>
#include <ll/i386/hw-arch.h>
#include <ll/i386/hw-io.h>
#include <ll/i386/cons.h>
#include <ll/i386/mem.h>
#include <ll/stdlib.h>
#include <drivers/llpci.h>
#include <drivers/pci.h>
#include <drivers/linuxpci.h>
#include <kernel/log.h>

//#define DEBUG_PCISCAN

static struct pci_dev pci_devs[N_MAX_DEVS];

static struct pci_bus pci_root;
static struct pci_dev *pci_devices = NULL;

#if 0
/* scan the bus to find all the connected devices */
DWORD pci_scan_bus(struct pci_bus *bus)
{
    int ndev;
    DWORD tmp;
    DWORD max, class;
    BYTE hdr;
    int ok;
    WORD dev;
    struct pci_dev **bus_last;
    struct pci_dev *device = NULL;
    int present;

    max = bus->secondary;
    bus_last = &bus->devices;

    if (pcibios_present() == 0) return -1;
    ndev = 0;

    for (dev = 0; dev <= 0xFF; dev++) {
        present = 0;
        if ((dev & 0x07) == 0) {
            present = pcibios_read_config_byte(bus->number, dev, 0x0e, &hdr);
        }
        if (hdr & 0x80) {
            present = 1;
        }
        if (present) {
            ok = pcibios_read_config_dword(bus->number, dev, 0, &tmp);
            if ( ok && (((tmp & 0xFFFF) != 0xFFFF) && ((tmp >> 16) != 0xFFFF))) {
                /* Got a PCI Device!!! */
                device = &(pci_devs[ndev++]);
                device->bus = bus;
                device->devfn  = dev;
                device->vendor = tmp & 0xffff;
                device->device = (tmp >> 16) & 0xffff;

                /* PJ: non-destructively determine if device can be a master: */
                pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &cmd);
                pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
                pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &tmp);
                dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
                pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd);

                /* This is by Luca... We need to set base_addr */
                pcibios_read_config_dword(bus->number, dev, PCI_BASE_ADDRESS_0, &(device->base_address[0]));



                pcibios_read_config_byte(bus->number, dev, PCI_INTERRUPT_LINE, &irq1);
                device->irq = irq1;
               
                pcibios_read_config_dword(bus->number, dev, PCI_CLASS_REVISION, &class);
                class >>= 8;                                /* upper 3 bytes */
                //printk(KERN_INFO "pci_scan_bus: dev=%d, class=%ld", dev, class);
                device->class = class;
                class >>= 8;
                device->hdr_type = hdr;




#if 0
                switch (hdr & 0x7f) {                  /* header type */
                case PCI_HEADER_TYPE_NORMAL:                /* standard header */
                        if (class == PCI_CLASS_BRIDGE_PCI)
                                goto bad;
                        /*
                         * If the card generates interrupts, read IRQ number
                         * (some architectures change it during pcibios_fixup())
                         */

                        pcibios_read_config_byte(bus->number, device->devfn, PCI_INTERRUPT_PIN, &irq1);
                        if (irq1)
                                pcibios_read_config_byte(bus->number, device->devfn, PCI_INTERRUPT_LINE, &irq1);
                        device->irq = irq1;
                        /*
                         * read base address registers, again pcibios_fixup() can
                         * tweak these
                         */

                        pci_read_bases(device, 6);
                        pcibios_read_config_dword(bus->number, dev, PCI_ROM_ADDRESS, &l);
                        device->rom_address = (l == 0xffffffff) ? 0 : l;
                        break;
                case PCI_HEADER_TYPE_BRIDGE:                /* bridge header */
                        if (class != PCI_CLASS_BRIDGE_PCI)
                                goto bad;
                        pci_read_bases(device, 2);
                        pcibios_read_config_dword(bus->number, dev, PCI_ROM_ADDRESS1, &l);
                        device->rom_address = (l == 0xffffffff) ? 0 : l;
                        break;
                        case PCI_HEADER_TYPE_CARDBUS:               /* CardBus bridge header */
                        if (class != PCI_CLASS_BRIDGE_CARDBUS)
                                goto bad;
                        pci_read_bases(device, 1);
                        break;
                default:                                    /* unknown header */
                bad:
                        printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
                               bus->number, device->devfn, device->vendor, device->device, class, hdr);
                        continue;
                }
#endif




                //printk(KERN_DEBUG "PCI: %02x:%02x [%04x/%04x]\n", bus->number, device->devfn, device->vendor, device->device);

                device->next = pci_devices;
                pci_devices = device;

                /*
                 * Now insert it into the list of devices held
                 * by the parent bus.
                 */

                *bus_last = device;
                bus_last = &device->sibling;



            }
        }
    }

    // PJ: PCI bridges not supported???
#if 0
    for(device = bus->devices; device; device = device->sibling) {
        /*
        * If it's a bridge, scan the bus behind it.
        */

        if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
            unsigned int buses;
            unsigned int devfn = dev->devfn;
            unsigned short cr;

            /*
             * Insert it into the tree of buses.
             */

            child = kmalloc(sizeof(*child), GFP_ATOMIC);
            if(child==NULL) {
#ifdef DEBUG_PCISCAN
            printk(KERN_ERR "pci: out of memory for bridge.\n");
#endif
                continue;
            }
            memset(child, 0, sizeof(*child));
            child->next = bus->children;
            bus->children = child;
            child->self = dev;
            child->parent = bus;

            /*
             * Set up the primary, secondary and subordinate
             * bus numbers.
             */

            child->number = child->secondary = ++max;
            child->primary = bus->secondary;
            child->subordinate = 0xff;
            /*
             * Clear all status bits and turn off memory,
             * I/O and master enables.
             */

            pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr);
            pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000);
            pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff);
            /*
             * Read the existing primary/secondary/subordinate bus
             * number configuration to determine if the PCI bridge
             * has already been configured by the system.  If so,
             * do not modify the configuration, merely note it.
             */

            pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses);
            if ((buses & 0xFFFFFF) != 0) {
                unsigned int cmax;

                child->primary = buses & 0xFF;
                child->secondary = (buses >> 8) & 0xFF;
                child->subordinate = (buses >> 16) & 0xFF;
                child->number = child->secondary;
                cmax = pci_scan_bus(child);
                if (cmax > max) max = cmax;
            } else {
                /*
                 * Configure the bus numbers for this bridge:
                 */

                buses &= 0xff000000;
                buses |= (((unsigned int)(child->primary)     <<  0) |
                        ((unsigned int)(child->secondary)   <<  8) |
                        ((unsigned int)(child->subordinate) << 16));
                pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
                /*
                 * Now we can scan all subordinate buses:
                 */

                max = pci_scan_bus(child);
                /*
                 * Set the subordinate bus number to its real
                 * value:
                 */

                child->subordinate = max;
                buses = (buses & 0xff00ffff)
                | ((unsigned int)(child->subordinate) << 16);
                pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
            }
            pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr);
        }
    }
#endif
   
    return max;
}


#endif







































/* scan the bus to find all the connected devices */
DWORD pci_scan_bus(struct pci_bus *bus)
{
    int ndev;
    DWORD tmp;
    DWORD max, class;
    BYTE hdr, irq1;
    int ok;
    WORD dev;
    struct pci_dev **bus_last;
    struct pci_dev *device = NULL;
    int present;

    max = bus->secondary;
    bus_last = &bus->devices;

    if (pcibios_present() == 0) return -1;
    ndev = 0;

    for (dev = 0; dev <= 0xFF; dev++) {
        present = 0;
        if ((dev & 0x07) == 0) {
            present = pcibios_read_config_byte(bus->number, dev, 0x0e, &hdr);
        }
        if (hdr & 0x80) {
            present = 1;
        }
        if (present) {
            ok = pcibios_read_config_dword(bus->number, dev, 0, &tmp);
            if ( ok && (((tmp & 0xFFFF) != 0xFFFF) && ((tmp >> 16) != 0xFFFF))) {
                /* Got a PCI Device!!! */
                device = &(pci_devs[ndev++]);
                device->bus = bus;
                device->devfn  = dev;
                device->vendor = tmp & 0xffff;
                device->device = (tmp >> 16) & 0xffff;


                /* This is by Luca... We need to set base_addr */
                pcibios_read_config_dword(bus->number, dev, PCI_BASE_ADDRESS_0, &(device->base_address[0]));


                /* ...and also the interrupt number!!! */
                pcibios_read_config_byte(bus->number, dev, PCI_INTERRUPT_LINE, &irq1);
                device->irq = irq1;
               
                pcibios_read_config_dword(bus->number, dev, PCI_CLASS_REVISION, &class);
                class >>= 8;                                /* upper 3 bytes */
                device->class = class;
                class >>= 8;
                device->hdr_type = hdr;

#if 0
                switch (hdr_type & 0x7f) {                  /* header type */
                case PCI_HEADER_TYPE_NORMAL:                /* standard header */
                        if (class == PCI_CLASS_BRIDGE_PCI)
                                goto bad;
                        /*
                         * If the card generates interrupts, read IRQ number
                         * (some architectures change it during pcibios_fixup())
                         */

                        pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_PIN, &irq);
                        if (irq)
                                pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_LINE, &irq);
                        dev->irq = irq;
                        /*
                         * read base address registers, again pcibios_fixup() can
                         * tweak these
                         */

                        pci_read_bases(dev, 6);
                        pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &l);
                        dev->rom_address = (l == 0xffffffff) ? 0 : l;
                        break;
                case PCI_HEADER_TYPE_BRIDGE:                /* bridge header */
                        if (class != PCI_CLASS_BRIDGE_PCI)
                                goto bad;
                        pci_read_bases(dev, 2);
                        pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &l);
                        dev->rom_address = (l == 0xffffffff) ? 0 : l;
                        break;
                        case PCI_HEADER_TYPE_CARDBUS:               /* CardBus bridge header */
                        if (class != PCI_CLASS_BRIDGE_CARDBUS)
                                goto bad;
                        pci_read_bases(dev, 1);
                        break;
                default:                                    /* unknown header */
                bad:
                        printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
                               bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type);
                        continue;
                }

                DBG("PCI: %02x:%02x [%04x/%04x]\n", bus->number, dev->devfn, dev->vendor, dev->device);
#endif

                device->next = pci_devices;
                pci_devices = device;

                /*
                 * Now insert it into the list of devices held
                 * by the parent bus.
                 */

                *bus_last = device;
                bus_last = &device->sibling;



            }
        }
    }


    for(device = bus->devices; device; device = device->sibling) {
#if 0
        /*
        * If it's a bridge, scan the bus behind it.
        */

        if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
            unsigned int buses;
            unsigned int devfn = dev->devfn;
            unsigned short cr;

            /*
             * Insert it into the tree of buses.
             */

            child = kmalloc(sizeof(*child), GFP_ATOMIC);
            if(child==NULL) {
#ifdef DEBUG_PCISCAN
            printk(KERN_ERR "pci: out of memory for bridge.\n");
#endif
                continue;
            }
            memset(child, 0, sizeof(*child));
            child->next = bus->children;
            bus->children = child;
            child->self = dev;
            child->parent = bus;

            /*
             * Set up the primary, secondary and subordinate
             * bus numbers.
             */

            child->number = child->secondary = ++max;
            child->primary = bus->secondary;
            child->subordinate = 0xff;
            /*
             * Clear all status bits and turn off memory,
             * I/O and master enables.
             */

            pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr);
            pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000);
            pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff);
            /*
             * Read the existing primary/secondary/subordinate bus
             * number configuration to determine if the PCI bridge
             * has already been configured by the system.  If so,
             * do not modify the configuration, merely note it.
             */

            pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses);
            if ((buses & 0xFFFFFF) != 0) {
                unsigned int cmax;

                child->primary = buses & 0xFF;
                child->secondary = (buses >> 8) & 0xFF;
                child->subordinate = (buses >> 16) & 0xFF;
                child->number = child->secondary;
                cmax = pci_scan_bus(child);
                if (cmax > max) max = cmax;
            } else {
                /*
                 * Configure the bus numbers for this bridge:
                 */

                buses &= 0xff000000;
                buses |= (((unsigned int)(child->primary)     <<  0) |
                        ((unsigned int)(child->secondary)   <<  8) |
                        ((unsigned int)(child->subordinate) << 16));
                pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
                /*
                 * Now we can scan all subordinate buses:
                 */

                max = pci_scan_bus(child);
                /*
                 * Set the subordinate bus number to its real
                 * value:
                 */

                child->subordinate = max;
                buses = (buses & 0xff00ffff)
                | ((unsigned int)(child->subordinate) << 16);
                pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
            }
            pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr);
        }
#endif
    }

    return max;
}


void linuxpci_init(void)
{
    pcibios_init();

    if (!pci_present()) {
#ifdef DEBUG_PCISCAN
        printk("PCI: No PCI bus detected\n");
#endif
        return;
    }

#ifdef DEBUG_PCISCAN
    printk("PCI: Probing PCI hardware\n");
#endif
    memset(&pci_root, 0, sizeof(pci_root));
    pci_root.subordinate = pci_scan_bus(&pci_root);
}

struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
{
        if (!from)
                from = pci_devices;
        else
                from = from->next;
        while (from && (from->vendor != vendor || from->device != device))
                from = from->next;
        return from;
}

struct pci_dev *pci_find_class(unsigned int class, struct pci_dev *from)
{
        if (!from)
                from = pci_devices;
        else
                from = from->next;
        while (from && from->class != class)
                from = from->next;
        return from;
}

struct pci_dev *
pci_find_slot(unsigned int bus, unsigned int devfn)
{
        struct pci_dev *dev;

        for(dev=pci_devices; dev; dev=dev->next)
                if (dev->bus->number == bus && dev->devfn == devfn)
                        break;
        return dev;
}


void
pci_set_master(struct pci_dev *dev)
{
  unsigned short cmd;  // was: u16
  unsigned char lat;  // was: u8

        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (! (cmd & PCI_COMMAND_MASTER)) {
                printk("PCI: Enabling bus mastering for device %02x:%02x\n",
                        dev->bus->number, dev->devfn);
                cmd |= PCI_COMMAND_MASTER;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
        }
        pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
        if (lat < 16) {
                printk("PCI: Increasing latency timer of device %02x:%02x to 64\n",
                        dev->bus->number, dev->devfn);
                pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
        }
}