Building the new src tree
This commit is contained in:
parent
2d2c3bee52
commit
96fa389618
9 changed files with 5099 additions and 0 deletions
215
src/cpu/p5/cpuid.c
Normal file
215
src/cpu/p5/cpuid.c
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
#include "intel_conf.h"
|
||||
#include "intel_subr.h"
|
||||
#include "printk.h"
|
||||
|
||||
#ifdef i586
|
||||
#include <asm/msr.h>
|
||||
#endif
|
||||
|
||||
#ifdef i586
|
||||
int intel_mtrr_check(void)
|
||||
{
|
||||
unsigned long low, high;
|
||||
|
||||
printk(KERN_INFO "\nMTRR check\n");
|
||||
|
||||
rdmsr(0x2ff, low, high);
|
||||
low = low >> 10;
|
||||
|
||||
printk(KERN_INFO "Fixed MTRRs : ");
|
||||
if (low & 0x01)
|
||||
printk(KERN_INFO "Enabled\n");
|
||||
else
|
||||
printk(KERN_INFO "Disabled\n");
|
||||
|
||||
printk(KERN_INFO "Variable MTRRs: ");
|
||||
if (low & 0x02)
|
||||
printk(KERN_INFO "Enabled\n");
|
||||
else
|
||||
printk(KERN_INFO "Disabled\n");
|
||||
|
||||
printk(KERN_INFO "\n");
|
||||
|
||||
return ((int) low);
|
||||
}
|
||||
#endif
|
||||
|
||||
void intel_display_cpuid(void)
|
||||
{
|
||||
int op, eax, ebx, ecx, edx;
|
||||
int max_op;
|
||||
|
||||
max_op = 0;
|
||||
|
||||
printk(KERN_INFO "\n");
|
||||
|
||||
for (op = 0; op <= max_op; op++) {
|
||||
intel_cpuid(op, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
if (0 == op) {
|
||||
max_op = eax;
|
||||
printk(KERN_INFO "Max cpuid index : %d\n", eax);
|
||||
printk(KERN_INFO "Vendor ID : "
|
||||
"%c%c%c%c%c%c%c%c%c%c%c%c\n",
|
||||
ebx, ebx >> 8, ebx >> 16, ebx >> 24, edx,
|
||||
edx >> 8, edx >> 16, edx >> 24, ecx, ecx >> 8,
|
||||
ecx >> 16, ecx >> 24);
|
||||
} else if (1 == op) {
|
||||
printk(KERN_INFO "Processor Type : 0x%02x\n",
|
||||
(eax >> 12) & 0x03);
|
||||
printk(KERN_INFO "Processor Family : 0x%02x\n",
|
||||
(eax >> 8) & 0x0f);
|
||||
printk(KERN_INFO "Processor Model : 0x%02x\n",
|
||||
(eax >> 4) & 0x0f);
|
||||
printk(KERN_INFO "Processor Mask : 0x%02x\n",
|
||||
(ecx >> 0) & 0x0f);
|
||||
printk(KERN_INFO "Processor Stepping : 0x%02x\n",
|
||||
(eax >> 0) & 0x0f);
|
||||
printk(KERN_INFO "Feature flags : 0x%08x\n", edx);
|
||||
} else if (2 == op) {
|
||||
int desc[4];
|
||||
int ii;
|
||||
int _desc;
|
||||
|
||||
printk(KERN_INFO "\n");
|
||||
|
||||
printk(KERN_INFO "Cache/TLB descriptor values: %d "
|
||||
"reads required\n", eax & 0xff);
|
||||
|
||||
desc[0] = eax;
|
||||
desc[1] = ebx;
|
||||
desc[2] = ecx;
|
||||
desc[3] = edx;
|
||||
|
||||
for (ii = 1; ii < 16; ii++) {
|
||||
if (desc[ii >> 2] & 0x80000000) {
|
||||
printk(KERN_INFO
|
||||
"reserved descriptor\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
_desc =
|
||||
((desc[ii>>2]) >> ((ii & 0x3) << 3)) & 0xff;
|
||||
printk(KERN_INFO "Desc 0x%02x : ", _desc);
|
||||
|
||||
switch (_desc) {
|
||||
case 0x00:
|
||||
printk(KERN_INFO "null\n");
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
printk(KERN_INFO "Instr TLB: "
|
||||
"4KB pages, "
|
||||
"4-way set assoc, "
|
||||
"32 entries\n");
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
printk(KERN_INFO "Instr TLB: "
|
||||
"4MB pages, "
|
||||
"fully assoc, "
|
||||
"2 entries\n");
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
printk(KERN_INFO "Data TLB: "
|
||||
"4KB pages, "
|
||||
"4-way set assoc, "
|
||||
"64 entries\n");
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
printk(KERN_INFO "Data TLB: "
|
||||
"4MB pages, "
|
||||
"4-way set assoc, "
|
||||
"8 entries\n");
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
printk(KERN_INFO "Inst cache: "
|
||||
"8K bytes, "
|
||||
"4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
printk(KERN_INFO "Inst cache: "
|
||||
"16K bytes, "
|
||||
"4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x0a:
|
||||
printk(KERN_INFO "Data cache: "
|
||||
"8K bytes, "
|
||||
"2-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x0c:
|
||||
printk(KERN_INFO "Data cache: "
|
||||
"16K bytes, "
|
||||
"2-way or 4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
printk(KERN_INFO "No L2 cache\n");
|
||||
break;
|
||||
|
||||
case 0x41:
|
||||
printk(KERN_INFO "L2 Unified cache: "
|
||||
"128K bytes, "
|
||||
"4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x42:
|
||||
printk(KERN_INFO "L2 Unified cache: "
|
||||
"256K bytes, "
|
||||
"4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x43:
|
||||
printk(KERN_INFO "L2 Unified cache: "
|
||||
"512K bytes, "
|
||||
"4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x44:
|
||||
printk(KERN_INFO "L2 Unified cache: "
|
||||
"1M byte, "
|
||||
"4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x45:
|
||||
printk(KERN_INFO "L2 Unified cache: "
|
||||
"2M byte, "
|
||||
"4-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
case 0x82:
|
||||
printk(KERN_INFO "L2 Unified cache: "
|
||||
"256K bytes, "
|
||||
"8-way set assoc, "
|
||||
"32 byte line size\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_INFO "UNKNOWN\n");
|
||||
}
|
||||
}
|
||||
printk(KERN_INFO "\n");
|
||||
} else {
|
||||
printk(KERN_INFO "op: 0x%02x eax:0x%08x "
|
||||
"ebx:0x%08x ecx:0x%08x edx:0x%08x\n",
|
||||
op, eax, ebx, ecx, edx);
|
||||
}
|
||||
}
|
||||
|
||||
printk(KERN_INFO "\n");
|
||||
}
|
||||
35
src/lib/definitions.h
Normal file
35
src/lib/definitions.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* Comment this out unless you are debugging under linux */
|
||||
#define EMULATE
|
||||
#undef EMULATE
|
||||
|
||||
#ifdef EMULATE
|
||||
#include <unistd.h>
|
||||
#else
|
||||
typedef unsigned long size_t;
|
||||
#endif
|
||||
|
||||
#ifdef EMULATE
|
||||
#define MEMSIZE 2*1024*1024 /* Make a 2MB fake memory space */
|
||||
char memimage[MEMSIZE]; /* Make a 2MB fake memory space */
|
||||
#else
|
||||
#define memimage 0x0 /* Ignore memimage */
|
||||
#endif
|
||||
|
||||
#define KERNEL_START (0x100000 + memimage) /* Put our copy of linux here */
|
||||
|
||||
#if 0
|
||||
#define ZIP_START (0x30000 + memimage) /* The zip file starts here */
|
||||
#define ZIP_SIZE 262164 /* linux.gz size (we ought to */
|
||||
#endif
|
||||
|
||||
/* with flash, it's a bunch of 64k segments. */
|
||||
#define ZIP_START (0xfff40000)
|
||||
#define ZIP_SIZE (0x10000)
|
||||
|
||||
#ifdef EMULATE
|
||||
char input_array[0x100000];
|
||||
#undef ZIP_START
|
||||
#define ZIP_START (input_array + 0x40000)
|
||||
#endif
|
||||
/* make this dynamic) */
|
||||
|
||||
1214
src/lib/inflate.c
Normal file
1214
src/lib/inflate.c
Normal file
File diff suppressed because it is too large
Load diff
1579
src/lib/linuxbiosmain.c
Normal file
1579
src/lib/linuxbiosmain.c
Normal file
File diff suppressed because it is too large
Load diff
478
src/lib/linuxpci.c
Normal file
478
src/lib/linuxpci.c
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* $Id$
|
||||
*
|
||||
* PCI Bus Services, see include/linux/pci.h for further explanation.
|
||||
*
|
||||
* Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
|
||||
* David Mosberger-Tang
|
||||
*
|
||||
* Copyright 1997 -- 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
|
||||
*/
|
||||
|
||||
/* the intent of this file is to easily copy any new pci.c from the
|
||||
* linux source tree, so keep your mods to a minimum, please
|
||||
* RGM
|
||||
*/
|
||||
#include <lbpci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
extern void intel_post(unsigned char value);
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(KERN_DEBUG x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is the root of the PCI tree. A PCI tree always has
|
||||
* one bus, bus 0. Bus 0 contains devices and bridges.
|
||||
*/
|
||||
struct pci_bus pci_root;
|
||||
/// Linked list of PCI devices. ALL devices are on this list
|
||||
struct pci_dev *pci_devices = 0;
|
||||
/// pointer to the last device */
|
||||
static struct pci_dev **pci_last_dev_p = &pci_devices;
|
||||
/// We're going to probably delete this -- flag to add in reverse order */
|
||||
static int pci_reverse = 0;
|
||||
|
||||
/**
|
||||
* Given a bus and a devfn number, find the device structure
|
||||
* @param bus The bus number
|
||||
* @param devfn a device/function number
|
||||
* @return pointer to the device structure
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Find a device of a given vendor and type
|
||||
* @param vendor Vendor ID (e.g. 0x8086 for Intel)
|
||||
* @param device Device ID
|
||||
* @param from Pointer to the device structure, used as a starting point
|
||||
* in the linked list of devices, which can be 0 to start at the
|
||||
* head of the list (i.e. pci_devices)
|
||||
* @return Pointer to the device struct
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Find a device of a given class
|
||||
* @param class Class of the device
|
||||
* @param from Pointer to the device structure, used as a starting point
|
||||
* in the linked list of devices, which can be 0 to start at the
|
||||
* head of the list (i.e. pci_devices)
|
||||
* @return Pointer to the device struct
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Given a device, set the PCI_COMMAND_MASTER bit in the command register
|
||||
* @param dev Pointer to the device structure
|
||||
*/
|
||||
void pci_set_master(struct pci_dev *dev)
|
||||
{
|
||||
u16 cmd;
|
||||
u8 lat;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a device and register, read the size of the BAR for that register.
|
||||
* @param dev Pointer to the device structure
|
||||
* @param reg Which register to use
|
||||
* @param addr Address to load into the register after size is found
|
||||
*/
|
||||
void pci_get_size(struct pci_dev *dev, unsigned long reg, unsigned long addr)
|
||||
{
|
||||
u32 size;
|
||||
unsigned long type;
|
||||
|
||||
/* FIXME: more consideration for 64-bit PCI devices */
|
||||
// get the size
|
||||
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), ~0);
|
||||
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &size);
|
||||
|
||||
// restore addr
|
||||
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), addr);
|
||||
|
||||
// Now compute the actual size, See PCI Spec 6.2.5.1 ...
|
||||
if (size & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
type = size & (~PCI_BASE_ADDRESS_IO_MASK);
|
||||
size &= (PCI_BASE_ADDRESS_IO_MASK);
|
||||
// BUG! Top 16 bits can be zero (or not)
|
||||
// So set them to 0xffff so they go away ...
|
||||
size |= 0xffff0000;
|
||||
size = ~size;
|
||||
size++;
|
||||
} else {
|
||||
type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
|
||||
size &= (PCI_BASE_ADDRESS_MEM_MASK);
|
||||
size = ~size;
|
||||
size++;
|
||||
}
|
||||
dev->size[reg] = size | type;
|
||||
}
|
||||
|
||||
/** Read the base address registers for a given device.
|
||||
* @param dev Pointer to the dev structure
|
||||
* @param howmany How many registers to read (6 for device, 2 for bridge)
|
||||
*/
|
||||
void pci_read_bases(struct pci_dev *dev, unsigned int howmany)
|
||||
{
|
||||
unsigned int reg;
|
||||
u32 /* unsigned long for 64 bits ?? */ addr;
|
||||
|
||||
/* FIXME: to deal with 64-bits PCI */
|
||||
for (reg = 0; reg < howmany; reg++) {
|
||||
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &addr);
|
||||
if (addr == 0xffffffff)
|
||||
continue;
|
||||
|
||||
/* get address space size */
|
||||
pci_get_size(dev, reg, addr);
|
||||
|
||||
addr &= (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK);
|
||||
if (addr == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) {
|
||||
/* this is a 64-bit memory base address */
|
||||
reg++;
|
||||
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (reg << 2), &addr);
|
||||
if (addr) {
|
||||
#if BITS_PER_LONG == 64
|
||||
dev->base_address[reg - 1] |= ((unsigned long) addr) << 32;
|
||||
#else
|
||||
printk("PCI: Unable to handle 64-bit address for device "
|
||||
"%02x:%02x\n", dev->bus->number, dev->devfn);
|
||||
dev->base_address[reg - 1] = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Scan the bus, first for bridges and next for devices.
|
||||
* @param pci_bus pointer to the bus structure
|
||||
* @return The maximum bus number found, after scanning all subordinate busses
|
||||
*/
|
||||
unsigned int pci_scan_bus(struct pci_bus *bus)
|
||||
{
|
||||
unsigned int devfn, max;
|
||||
struct pci_dev *dev, **bus_last;
|
||||
struct pci_bus *child;
|
||||
|
||||
DBG("PCI: pci_scan_bus for bus %d\n", bus->number);
|
||||
|
||||
bus_last = &bus->devices;
|
||||
max = bus->secondary;
|
||||
|
||||
intel_post(0x24);
|
||||
|
||||
/* probe all devices on this bus with some optimization for non-existance and
|
||||
single funcion devices */
|
||||
for (devfn = 0; devfn < 0xff; devfn++) {
|
||||
u32 id, class, addr;
|
||||
u8 cmd, tmp, hdr_type;
|
||||
|
||||
if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &id))
|
||||
continue;
|
||||
|
||||
/* some broken boards return 0 if a slot is empty: */
|
||||
if (id == 0xffffffff || id == 0x00000000 || id == 0x0000ffff || id == 0xffff0000) {
|
||||
if (PCI_FUNC(devfn) == 0x00) {
|
||||
/* if this is a function 0 device and it is not present,
|
||||
skip to next device */
|
||||
devfn += 0x07;
|
||||
}
|
||||
/* multi function device, skip to next function */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type))
|
||||
continue;
|
||||
|
||||
if (pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class))
|
||||
continue;
|
||||
|
||||
if ((dev = kmalloc(sizeof(*dev), GFP_ATOMIC)) == NULL) {
|
||||
printk("PCI: out of memory.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
dev->bus = bus;
|
||||
dev->devfn = devfn;
|
||||
dev->vendor = id & 0xffff;
|
||||
dev->device = (id >> 16) & 0xffff;
|
||||
dev->hdr_type = hdr_type;
|
||||
/* class code, the upper 3 bytes of PCI_CLASS_REVISION */
|
||||
dev->class = class >> 8;
|
||||
class >>= 16;
|
||||
|
||||
/* 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);
|
||||
|
||||
switch (hdr_type & 0x7f) { /* header type */
|
||||
case PCI_HEADER_TYPE_NORMAL: /* standard header */
|
||||
if (class == PCI_CLASS_BRIDGE_PCI)
|
||||
goto bad;
|
||||
/* read base address registers, again pci_fixup() can tweak these */
|
||||
pci_read_bases(dev, 6);
|
||||
pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &addr);
|
||||
dev->rom_address = (addr == 0xffffffff) ? 0 : addr;
|
||||
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, &addr);
|
||||
dev->rom_address = (addr == 0xffffffff) ? 0 : addr;
|
||||
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("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);
|
||||
|
||||
/* Put it into the global PCI device chain. It's used to find devices once
|
||||
everything is set up. */
|
||||
if (!pci_reverse) {
|
||||
*pci_last_dev_p = dev;
|
||||
pci_last_dev_p = &dev->next;
|
||||
} else {
|
||||
dev->next = pci_devices;
|
||||
pci_devices = dev;
|
||||
}
|
||||
|
||||
/* Now insert it into the list of devices held by the parent bus. */
|
||||
*bus_last = dev;
|
||||
bus_last = &dev->sibling;
|
||||
|
||||
if (PCI_FUNC(devfn) == 0x00 && (hdr_type & 0x80) != 0x80) {
|
||||
/* if this is not a multi function device, don't waste time probe
|
||||
another function. Skip to next device. */
|
||||
devfn += 0x07;
|
||||
}
|
||||
}
|
||||
|
||||
intel_post(0x25);
|
||||
/*
|
||||
* After performing arch-dependent fixup of the bus, look behind
|
||||
* all PCI-to-PCI bridges on this bus.
|
||||
*/
|
||||
//pcibios_fixup_bus(bus);
|
||||
/*
|
||||
* The fixup code may have just found some peer pci bridges on this
|
||||
* machine. Update the max variable if that happened so we don't
|
||||
* get duplicate bus numbers.
|
||||
*/
|
||||
for (child = &pci_root; child; child = child->next)
|
||||
max = ((max > child->subordinate) ? max : child->subordinate);
|
||||
|
||||
for (dev = bus->devices; dev; dev = dev->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;
|
||||
#define NOTUSED
|
||||
#ifdef NOTUSED
|
||||
/*
|
||||
* Check for a duplicate bus. If we already scanned
|
||||
* this bus number as a peer bus, don't also scan it
|
||||
* as a child bus
|
||||
*/
|
||||
if (((dev->vendor == PCI_VENDOR_ID_RCC) &&
|
||||
((dev->device == PCI_DEVICE_ID_RCC_HE) ||
|
||||
(dev->device == PCI_DEVICE_ID_RCC_LE))) ||
|
||||
((dev->vendor == PCI_VENDOR_ID_COMPAQ) &&
|
||||
(dev->device == PCI_DEVICE_ID_COMPAQ_6010)) ||
|
||||
((dev->vendor == PCI_VENDOR_ID_INTEL) &&
|
||||
((dev->device == PCI_DEVICE_ID_INTEL_82454NX)||
|
||||
(dev->device == PCI_DEVICE_ID_INTEL_82451NX))))
|
||||
goto skip_it;
|
||||
|
||||
/* Read the existing primary/secondary/subordinate bus number
|
||||
configuration to determine if the PCI bridge has already been
|
||||
configured by the system. If so, check to see if we've already
|
||||
scanned this bus as a result of peer bus scanning, if so, skip this.
|
||||
FIMXE: We are BIOS, is there anyone else doing this dirty job BEFORE us ?? */
|
||||
pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses);
|
||||
if ((buses & 0xFFFFFF) != 0) {
|
||||
for (child = pci_root.next; child; child = child->next)
|
||||
if (child->number == ((buses >> 8) & 0xff))
|
||||
goto skip_it;
|
||||
}
|
||||
#endif
|
||||
/* Insert it into the tree of buses. */
|
||||
if ((child = kmalloc(sizeof(*child), GFP_ATOMIC)) == NULL) {
|
||||
printk("PCI: out of memory for bridge.\n");
|
||||
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. We have
|
||||
no idea how many buses are behind this bridge yet, so we set the
|
||||
subordinate bus number to 0xff for the moment */
|
||||
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);
|
||||
|
||||
#ifdef BRIDGE_CONFIGURED_AT_POWERUP
|
||||
// There is some hardware (ALPHA) that configures bridges in hardware, at bootup.
|
||||
// We need to take that into account at some point.
|
||||
// At the same time, we're finding buggy bridge hardware that comes up
|
||||
// with these registers non-zero (VIA VT8601). Hence this #ifdef -- in some cases,
|
||||
// you should never check the buses; in other cases, you have no choice.
|
||||
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
|
||||
#endif
|
||||
{
|
||||
/* Configure the bus numbers for this bridge: the configuration
|
||||
transactions will not be propagated by the bridge if it is not
|
||||
correctly configured */
|
||||
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 i.e. the bus hehind the bridge */
|
||||
max = pci_scan_bus(child);
|
||||
|
||||
/* We know the number of buses behind this bridge. 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);
|
||||
skip_it:
|
||||
}
|
||||
/*
|
||||
* We've scanned the bus and so we know all about what's on
|
||||
* the other side of any bridges that may be on this bus plus
|
||||
* any devices.
|
||||
*
|
||||
* Return how far we've got finding sub-buses.
|
||||
*/
|
||||
DBG("PCI: pci_scan_bus returning with max=%02x\n", max);
|
||||
intel_post(0x55);
|
||||
return max;
|
||||
}
|
||||
|
||||
/** Scan a peer bridge.
|
||||
* First, scan all the bus structs for this bus number. If this bus
|
||||
* is found, that means it was configured, so just return the pointer.
|
||||
* If the bus is not found, it needs to be configured. Allocate memory
|
||||
* for the struct, link it in to the linked list of bridges, and then
|
||||
* scan it.
|
||||
* @param bus The bus number supported by the peer bridge
|
||||
* @return Pointer to the bus struct for this bus number.
|
||||
struct pci_bus *pci_scan_peer_bridge(int bus)
|
||||
{
|
||||
struct pci_bus *b;
|
||||
|
||||
b = &pci_root;
|
||||
while ((b != NULL) && (bus != 0)) {
|
||||
if (b->number == bus)
|
||||
return (b);
|
||||
b = b->next;
|
||||
}
|
||||
b = kmalloc(sizeof(*b), GFP_KERNEL);
|
||||
memset(b, 0, sizeof(*b));
|
||||
b->next = pci_root.next;
|
||||
pci_root.next = b;
|
||||
b->number = b->secondary = bus;
|
||||
b->subordinate = pci_scan_bus(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
/** Initialize pci root struct, then scan starting at the root.
|
||||
* Note that this function will recurse at each bridge.
|
||||
*/
|
||||
void pci_init(void)
|
||||
{
|
||||
memset(&pci_root, 0, sizeof(pci_root));
|
||||
pci_root.subordinate = pci_scan_bus(&pci_root);
|
||||
}
|
||||
993
src/lib/newpci.c
Normal file
993
src/lib/newpci.c
Normal file
|
|
@ -0,0 +1,993 @@
|
|||
/*
|
||||
* Low-Level PCI Support for PC
|
||||
*
|
||||
* (c) 1999--2000 Martin Mares <mj@suse.cz>
|
||||
*/
|
||||
/* lots of mods by ron minnich (rminnich@lanl.gov), with
|
||||
* the final architecture guidance from Tom Merritt (tjm@codegen.com)
|
||||
* In particular, we changed from the one-pass original version to
|
||||
* Tom's recommended multiple-pass version. I wasn't sure about doing
|
||||
* it with multiple passes, until I actually started doing it and saw
|
||||
* the wisdom of Tom's recommendations ...
|
||||
*/
|
||||
/* single-pass allocation appears to be the way to go. */
|
||||
#include <lbpci.h>
|
||||
#undef __KERNEL__
|
||||
#include <asm/io.h>
|
||||
#include <printk.h>
|
||||
|
||||
#ifdef EMULATE
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(KERN_DEBUG x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
#define ONEMEG (1 << 20)
|
||||
#undef TWO_PASS_ALLOCATE
|
||||
|
||||
#define PCI_MEM_START 0x80000000
|
||||
#define PCI_IO_START 0x1000
|
||||
|
||||
static const struct pci_ops *conf;
|
||||
|
||||
struct pci_ops {
|
||||
int (*read_byte) (u8 bus, int devfn, int where, u8 * val);
|
||||
int (*read_word) (u8 bus, int devfn, int where, u16 * val);
|
||||
int (*read_dword) (u8 bus, int devfn, int where, u32 * val);
|
||||
int (*write_byte) (u8 bus, int devfn, int where, u8 val);
|
||||
int (*write_word) (u8 bus, int devfn, int where, u16 val);
|
||||
int (*write_dword) (u8 bus, int devfn, int where, u32 val);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Direct access to PCI hardware...
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Functions for accessing PCI configuration space with type 1 accesses
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD(bus,devfn, where) (0x80000000 | (bus << 16) | (devfn << 8) | (where & ~3))
|
||||
|
||||
static int pci_conf1_read_config_byte(unsigned char bus, int devfn, int where, u8 * value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
|
||||
*value = inb(0xCFC + (where & 3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf1_read_config_word(unsigned char bus, int devfn, int where, u16 * value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
|
||||
*value = inw(0xCFC + (where & 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf1_read_config_dword(unsigned char bus, int devfn, int where, u32 * value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
|
||||
*value = inl(0xCFC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf1_write_config_byte(unsigned char bus, int devfn, int where, u8 value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
|
||||
outb(value, 0xCFC + (where & 3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf1_write_config_word(unsigned char bus, int devfn, int where, u16 value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
|
||||
outw(value, 0xCFC + (where & 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf1_write_config_dword(unsigned char bus, int devfn, int where, u32 value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus, devfn, where), 0xCF8);
|
||||
outl(value, 0xCFC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef CONFIG_CMD
|
||||
|
||||
static const struct pci_ops pci_direct_conf1 =
|
||||
{
|
||||
pci_conf1_read_config_byte,
|
||||
pci_conf1_read_config_word,
|
||||
pci_conf1_read_config_dword,
|
||||
pci_conf1_write_config_byte,
|
||||
pci_conf1_write_config_word,
|
||||
pci_conf1_write_config_dword
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions for accessing PCI configuration space with type 2 accesses
|
||||
*/
|
||||
|
||||
#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where)
|
||||
#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0)
|
||||
#define SET(bus,devfn) if (devfn & 0x80) return -1;outb(FUNC(devfn), 0xCF8); outb(bus, 0xCFA);
|
||||
|
||||
static int pci_conf2_read_config_byte(unsigned char bus, int devfn, int where, u8 * value)
|
||||
{
|
||||
SET(bus, devfn);
|
||||
*value = inb(IOADDR(devfn, where));
|
||||
outb(0, 0xCF8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf2_read_config_word(unsigned char bus, int devfn, int where, u16 * value)
|
||||
{
|
||||
SET(bus, devfn);
|
||||
*value = inw(IOADDR(devfn, where));
|
||||
outb(0, 0xCF8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf2_read_config_dword(unsigned char bus, int devfn, int where, u32 * value)
|
||||
{
|
||||
SET(bus, devfn);
|
||||
*value = inl(IOADDR(devfn, where));
|
||||
outb(0, 0xCF8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf2_write_config_byte(unsigned char bus, int devfn, int where, u8 value)
|
||||
{
|
||||
SET(bus, devfn);
|
||||
outb(value, IOADDR(devfn, where));
|
||||
outb(0, 0xCF8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf2_write_config_word(unsigned char bus, int devfn, int where, u16 value)
|
||||
{
|
||||
SET(bus, devfn);
|
||||
outw(value, IOADDR(devfn, where));
|
||||
outb(0, 0xCF8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_conf2_write_config_dword(unsigned char bus, int devfn, int where, u32 value)
|
||||
{
|
||||
SET(bus, devfn);
|
||||
outl(value, IOADDR(devfn, where));
|
||||
outb(0, 0xCF8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SET
|
||||
#undef IOADDR
|
||||
#undef FUNC
|
||||
|
||||
static const struct pci_ops pci_direct_conf2 =
|
||||
{
|
||||
pci_conf2_read_config_byte,
|
||||
pci_conf2_read_config_word,
|
||||
pci_conf2_read_config_dword,
|
||||
pci_conf2_write_config_byte,
|
||||
pci_conf2_write_config_word,
|
||||
pci_conf2_write_config_dword
|
||||
};
|
||||
|
||||
/*
|
||||
* Before we decide to use direct hardware access mechanisms, we try to do some
|
||||
* trivial checks to ensure it at least _seems_ to be working -- we just test
|
||||
* whether bus 00 contains a host bridge (this is similar to checking
|
||||
* techniques used in XFree86, but ours should be more reliable since we
|
||||
* attempt to make use of direct access hints provided by the PCI BIOS).
|
||||
*
|
||||
* This should be close to trivial, but it isn't, because there are buggy
|
||||
* chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
|
||||
*/
|
||||
static int pci_sanity_check(const struct pci_ops *o)
|
||||
{
|
||||
u16 x;
|
||||
u8 bus;
|
||||
int devfn;
|
||||
#define PCI_CLASS_BRIDGE_HOST 0x0600
|
||||
#define PCI_CLASS_DISPLAY_VGA 0x0300
|
||||
#define PCI_VENDOR_ID_COMPAQ 0x0e11
|
||||
#define PCI_VENDOR_ID_INTEL 0x8086
|
||||
|
||||
for (bus = 0, devfn = 0; devfn < 0x100; devfn++)
|
||||
if ((!o->read_word(bus, devfn, PCI_CLASS_DEVICE, &x) &&
|
||||
(x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) ||
|
||||
(!o->read_word(bus, devfn, PCI_VENDOR_ID, &x) &&
|
||||
(x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)))
|
||||
return 1;
|
||||
printk(KERN_ERR "PCI: Sanity check failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_ops *pci_check_direct(void)
|
||||
{
|
||||
unsigned int tmp;
|
||||
|
||||
/*
|
||||
* Check if configuration type 1 works.
|
||||
*/
|
||||
{
|
||||
outb(0x01, 0xCFB);
|
||||
tmp = inl(0xCF8);
|
||||
outl(0x80000000, 0xCF8);
|
||||
if (inl(0xCF8) == 0x80000000 &&
|
||||
pci_sanity_check(&pci_direct_conf1)) {
|
||||
outl(tmp, 0xCF8);
|
||||
printk(KERN_INFO "PCI: Using configuration type 1\n");
|
||||
return &pci_direct_conf1;
|
||||
}
|
||||
outl(tmp, 0xCF8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if configuration type 2 works.
|
||||
*/
|
||||
{
|
||||
outb(0x00, 0xCFB);
|
||||
outb(0x00, 0xCF8);
|
||||
outb(0x00, 0xCFA);
|
||||
if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 &&
|
||||
pci_sanity_check(&pci_direct_conf2)) {
|
||||
printk(KERN_INFO "PCI: Using configuration type 2\n");
|
||||
return &pci_direct_conf2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// Need to merge all these functions. Sorry about this.
|
||||
// changing horses in mid-stream!
|
||||
|
||||
|
||||
int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 * val)
|
||||
{
|
||||
return conf->read_byte(dev->bus->number, dev->devfn, where, val);
|
||||
}
|
||||
|
||||
int pci_read_config_word(struct pci_dev *dev, u8 where, u16 * val)
|
||||
{
|
||||
return conf->read_word(dev->bus->number, dev->devfn, where, val);
|
||||
}
|
||||
|
||||
int pci_read_config_dword(struct pci_dev *dev, u8 where, u32 * val)
|
||||
{
|
||||
return conf->read_dword(dev->bus->number, dev->devfn, where, val);
|
||||
}
|
||||
|
||||
int pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val)
|
||||
{
|
||||
return conf->write_byte(dev->bus->number, dev->devfn, where, val);
|
||||
}
|
||||
|
||||
int pci_write_config_word(struct pci_dev *dev, u8 where, u16 val)
|
||||
{
|
||||
return conf->write_word(dev->bus->number, dev->devfn, where, val);
|
||||
}
|
||||
|
||||
int pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val)
|
||||
{
|
||||
return conf->write_dword(dev->bus->number, dev->devfn, where, val);
|
||||
}
|
||||
|
||||
int pci_debugwrite_config_byte(struct pci_dev *dev, u8 where, u8 val)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
return conf->write_byte(dev->bus->number, dev->devfn, where, val);
|
||||
#else
|
||||
printk("Write config byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
|
||||
dev->bus->number, dev->devfn, where, val);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int pci_debugwrite_config_word(struct pci_dev *dev, u8 where, u16 val)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
return conf->write_word(dev->bus->number, dev->devfn, where, val);
|
||||
#else
|
||||
printk("Write config byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
|
||||
dev->bus->number, dev->devfn, where, val);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int pci_debugwrite_config_dword(struct pci_dev *dev, u8 where, u32 val)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
return conf->write_dword(dev->bus->number, dev->devfn, where, val);
|
||||
#else
|
||||
printk("Write config byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
|
||||
dev->bus->number, dev->devfn, where, val);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int pcibios_read_config_byte(unsigned char bus, unsigned char devfn, u8 where, u8 * val)
|
||||
{
|
||||
return conf->read_byte(bus, devfn, where, val);
|
||||
}
|
||||
|
||||
int pcibios_read_config_word(unsigned char bus, unsigned char devfn, u8 where, u16 * val)
|
||||
{
|
||||
return conf->read_word(bus, devfn, where, val);
|
||||
}
|
||||
|
||||
int pcibios_read_config_dword(unsigned char bus, unsigned char devfn, u8 where, u32 * val)
|
||||
{
|
||||
return conf->read_dword(bus, devfn, where, val);
|
||||
}
|
||||
|
||||
int pcibios_write_config_byte(unsigned char bus, unsigned char devfn, u8 where, u8 val)
|
||||
{
|
||||
return conf->write_byte(bus, devfn, where, val);
|
||||
}
|
||||
|
||||
int pcibios_write_config_word(unsigned char bus, unsigned char devfn, u8 where, u16 val)
|
||||
{
|
||||
return conf->write_word(bus, devfn, where, val);
|
||||
}
|
||||
|
||||
int pcibios_write_config_dword(unsigned char bus, unsigned char devfn, u8 where, u32 val)
|
||||
{
|
||||
return conf->write_dword(bus, devfn, where, val);
|
||||
}
|
||||
|
||||
|
||||
int pcibios_debugwrite_config_byte(unsigned char bus, unsigned char devfn, u8 where, u8 val)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
return conf->write_byte(bus, devfn, where, val);
|
||||
#else
|
||||
printk("Write byte bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
|
||||
bus, devfn, where, val);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int pcibios_debugwrite_config_word(unsigned char bus, unsigned char devfn, u8 where, u16 val)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
return conf->write_word(bus, devfn, where, val);
|
||||
#else
|
||||
printk("Write word bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
|
||||
bus, devfn, where, val);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int pcibios_debugwrite_config_dword(unsigned char bus, unsigned char devfn, u8 where, u32 val)
|
||||
{
|
||||
#ifndef DEBUG
|
||||
return conf->write_dword(bus, devfn, where, val);
|
||||
#else
|
||||
printk("Write doubleword bus %d, devfn 0x%x, reg 0x%x, val 0x%x\n",
|
||||
bus, devfn, where, val);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/** round a number to an alignment.
|
||||
* @param val the starting value
|
||||
* @param roundup Alignment as a power of two
|
||||
* @returns rounded up number
|
||||
*/
|
||||
unsigned long round(unsigned long val, unsigned long roundup)
|
||||
{
|
||||
// ROUNDUP MUST BE A POWER OF TWO.
|
||||
unsigned long inverse;
|
||||
inverse = ~(roundup - 1);
|
||||
val += (roundup - 1);
|
||||
val &= inverse;
|
||||
return val;
|
||||
}
|
||||
|
||||
/** Set the method to be used for PCI, type I or type II
|
||||
*/
|
||||
void pci_set_method()
|
||||
{
|
||||
conf = &pci_direct_conf1;
|
||||
conf = pci_check_direct();
|
||||
}
|
||||
|
||||
/* allocating resources on PCI is a mess. The reason is that
|
||||
* the BAR size is actually two things: one is the size, and
|
||||
* the other is the alignment of the data. Take, for example, the
|
||||
* SiS agp hardware. BAR 0 reports a size as follows: 0xf8000008.
|
||||
* This means prefetchable, and you can compute the size of
|
||||
* 0x8000000 (128 Mbytes). But it also turns you that only the
|
||||
* top five bits of the address are decoded. So you can not, for
|
||||
* example, allocate address space at 0x400000 for 0x8000000 bytes,
|
||||
* because in the register that will turn into 0. You have
|
||||
* to allocate address space using only the top five bits of the
|
||||
* PCI address space, i.e. you have to start allocating at 0x8000000.
|
||||
*
|
||||
* we have a more complex algorithm for address space allocation in the
|
||||
* works, that is actually simple code but gets the desired behavior.
|
||||
* For now, though, we operate as follows:
|
||||
* as you encounter BAR values, just round up the current usage
|
||||
* to be aligned to the BAR size. Then allocate.
|
||||
* This has the advantage of being simple, and in practice there are
|
||||
* so few large BAR areas that we expect it to cover all cases.
|
||||
* If we find problems with this strategy we'll go to the more complex
|
||||
* algorithm.
|
||||
*/
|
||||
/* it's worse than I thought ...
|
||||
* rules:
|
||||
* bridges contain all sub-bridges, and the address space for mem and
|
||||
* prefetch has to be contiguous.
|
||||
* Anyway, this has gotten so complicated we're going to a one-pass
|
||||
* allocate for now.
|
||||
*/
|
||||
|
||||
#ifdef TWO_PASS_ALLOCATE
|
||||
void compute_resources(struct pci_bus *bus)
|
||||
{
|
||||
int i;
|
||||
struct pci_bus *curbus;
|
||||
struct pci_dev *curdev;
|
||||
unsigned long mem, prefmem, io;
|
||||
mem = prefmem = io = 0;
|
||||
// first, walk all the bridges
|
||||
// Sum up the memory requirements of each bridge and add it into the
|
||||
// total.
|
||||
for (curbus = bus->children; curbus; curbus = curbus->next) {
|
||||
|
||||
compute_resources(curbus);
|
||||
mem += curbus->mem;
|
||||
prefmem += curbus->prefmem;
|
||||
io += curbus->io;
|
||||
printk(KERN_DEBUG "Bridge Bus %d,mem 0x%lx, "
|
||||
"prefmem 0x%lx, io 0x%lx\n",
|
||||
curbus->number, mem, prefmem, io);
|
||||
}
|
||||
|
||||
// second, walk all the devices. Add these.
|
||||
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
unsigned long size = curdev->size[i];
|
||||
if (size & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
unsigned long iosize;
|
||||
iosize = size & PCI_BASE_ADDRESS_IO_MASK;
|
||||
printk(KERN_DEBUG "Bus %d, devfn 0x%x, "
|
||||
"reg %d: io 0x%lx\n",
|
||||
curdev->bus->number, curdev->devfn,
|
||||
i, iosize);
|
||||
io += round(iosize, IO_ALIGN);
|
||||
} else {
|
||||
unsigned long memorysize = size & (PCI_BASE_ADDRESS_MEM_MASK);
|
||||
unsigned long type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
|
||||
// at this point it's memory. What kind?
|
||||
if (type == 0) { // normal
|
||||
|
||||
unsigned long regmem;
|
||||
// align the memory value
|
||||
regmem = round(memorysize, MEM_ALIGN);
|
||||
mem = round(mem, regmem);
|
||||
printk(KERN_DEBUG "Bus %d, "
|
||||
"devfn 0x%x, reg %d: "
|
||||
"mem 0x%lx\n",
|
||||
curdev->bus->number,
|
||||
curdev->devfn, i, regmem);
|
||||
mem += regmem;
|
||||
} else if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
|
||||
unsigned long regmem;
|
||||
// align the memory value
|
||||
regmem = round(memorysize, MEM_ALIGN);
|
||||
prefmem = round(prefmem, regmem);
|
||||
printk(KERN_DEBUG "Bus %d, "
|
||||
"devfn 0x%x, reg %d: "
|
||||
"prefmem 0x%lx\n",
|
||||
curdev->bus->number,
|
||||
curdev->devfn, i, regmem);
|
||||
prefmem += regmem;
|
||||
} else
|
||||
printk(KERN_DEBUG "Bus %d, "
|
||||
"devfn 0x%x: Unsupported "
|
||||
"memory type 0x%lx\n",
|
||||
curdev->bus->number,
|
||||
curdev->devfn, type);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assign to this bus.
|
||||
bus->mem = round(mem, ONEMEG);
|
||||
bus->prefmem = round(prefmem, ONEMEG);
|
||||
bus->io = round(io, IO_BRIDGE_ALIGN);
|
||||
}
|
||||
|
||||
void allocate_resources(struct pci_bus *bus)
|
||||
{
|
||||
int i;
|
||||
struct pci_bus *curbus;
|
||||
struct pci_dev *curdev;
|
||||
unsigned long mem, io, prefmem;
|
||||
|
||||
// first, walk all the bridges
|
||||
// Give each bridge what it needs, then call that bridge to configure
|
||||
// itself and its devices.
|
||||
mem = bus->membase;
|
||||
prefmem = bus->prefmembase;
|
||||
io = bus->iobase;
|
||||
|
||||
for (curbus = bus->children; curbus; curbus = curbus->next) {
|
||||
unsigned long memincrement;
|
||||
unsigned long ioincrement;
|
||||
// we don't need to round here -- all sizes are already rounded!
|
||||
|
||||
if (curbus->mem) {
|
||||
curbus->membase = mem;
|
||||
memincrement = curbus->mem;
|
||||
curbus->memlimit = curbus->membase + memincrement - 1;
|
||||
mem += memincrement;
|
||||
}
|
||||
if (curbus->prefmem) {
|
||||
curbus->prefmembase = prefmem;
|
||||
memincrement = curbus->prefmem;
|
||||
curbus->prefmemlimit = curbus->prefmembase + memincrement - 1;
|
||||
prefmem += memincrement;
|
||||
}
|
||||
if (curbus->io) {
|
||||
curbus->iobase = io;
|
||||
ioincrement = curbus->io;
|
||||
curbus->iolimit = curbus->io + ioincrement - 1;
|
||||
io += ioincrement;
|
||||
}
|
||||
allocate_resources(curbus);
|
||||
|
||||
printk(KERN_DEBUG "Bridge Bus %d,mem 0x%lx, "
|
||||
"prefmem 0x%lx, io 0x%lx\n",
|
||||
curbus->number, mem, prefmem, io);
|
||||
}
|
||||
|
||||
// Now hand out device memory.
|
||||
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (!curdev->size[i])
|
||||
continue;
|
||||
if (curdev->size[i] & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
curdev->command |= PCI_COMMAND_IO;
|
||||
curdev->base_address[i] = io;
|
||||
printk(KERN_DEBUG "bus %d devfn 0x%x "
|
||||
"reg %d io 0x%lx\n",
|
||||
bus->number, curdev->devfn, i, io);
|
||||
io += curdev->size[i] & PCI_BASE_ADDRESS_IO_MASK;
|
||||
} else {
|
||||
if (curdev->size[i] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
|
||||
unsigned long size = curdev->size[i] & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
// align the memory
|
||||
size = round(size, MEM_ALIGN);
|
||||
prefmem = round(prefmem, size);
|
||||
curdev->command |= PCI_COMMAND_MEMORY;
|
||||
curdev->base_address[i] = prefmem;
|
||||
printk(KERN_DEBUG "bus %d devfn 0x%x "
|
||||
"reg %d prefmem 0x%lx\n",
|
||||
bus->number, curdev->devfn,
|
||||
i, prefmem);
|
||||
prefmem += size;
|
||||
} else {
|
||||
unsigned long size = curdev->size[i] & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
// align the memory
|
||||
size = round(size, MEM_ALIGN);
|
||||
mem = round(mem, size);
|
||||
curdev->command |= PCI_COMMAND_MEMORY;
|
||||
curdev->base_address[i] = mem;
|
||||
printk(KERN_DEBUG "bus %d devfn 0x%x "
|
||||
"reg %d mem 0x%lx\n",
|
||||
bus->number, curdev->devfn,
|
||||
i, mem);
|
||||
mem += size;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#else /* single pass allocation */
|
||||
/** Given a desired amount of io, round it to IO_BRIDGE_ALIGN
|
||||
* @param amount Amount of memory desired.
|
||||
*/
|
||||
unsigned long iolimit(unsigned long amount)
|
||||
{
|
||||
amount = round(amount, IO_BRIDGE_ALIGN) - 1;
|
||||
return amount;
|
||||
}
|
||||
|
||||
/** Given a desired amount of memory, round it to ONEMEG
|
||||
* @param amount Amount of memory desired.
|
||||
*/
|
||||
unsigned long memlimit(unsigned long amount)
|
||||
{
|
||||
amount = round(amount, ONEMEG) - 1;
|
||||
return amount;
|
||||
}
|
||||
|
||||
/** Compute and allocate the io for this bus.
|
||||
* @param bus Pointer to the struct for this bus.
|
||||
*/
|
||||
void compute_allocate_io(struct pci_bus *bus)
|
||||
{
|
||||
int i;
|
||||
struct pci_bus *curbus;
|
||||
struct pci_dev *curdev;
|
||||
unsigned long io_base;
|
||||
|
||||
io_base = bus->iobase;
|
||||
DBG("compute_allocate_mem: base 0x%lx\n", bus->iobase);
|
||||
|
||||
/* First, walk all the bridges. When you return, grow the limit of the current bus
|
||||
since sub-busses need IO rounded to 4096 */
|
||||
for (curbus = bus->children; curbus; curbus = curbus->next) {
|
||||
curbus->iobase = io_base;
|
||||
compute_allocate_io(curbus);
|
||||
io_base = round(curbus->iolimit, IO_BRIDGE_ALIGN);
|
||||
DBG("BUSIO: done Bridge Bus 0x%x, iobase now 0x%lx\n",
|
||||
curbus->number, io_base);
|
||||
}
|
||||
|
||||
/* Walk through all the devices on current bus and oompute IO address space.*/
|
||||
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
unsigned long size = curdev->size[i];
|
||||
if (size & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
unsigned long iosize = size & PCI_BASE_ADDRESS_IO_MASK;
|
||||
if (!iosize)
|
||||
continue;
|
||||
|
||||
DBG("DEVIO: Bus 0x%x, devfn 0x%x, reg 0x%x: "
|
||||
"iosize 0x%lx\n",
|
||||
curdev->bus->number, curdev->devfn, i, iosize);
|
||||
curdev->base_address[i] = io_base;
|
||||
// some chipsets allow us to set/clear the IO bit.
|
||||
// (e.g. VIA 82c686a.) So set it to be safe)
|
||||
curdev->base_address[i] |=
|
||||
PCI_BASE_ADDRESS_SPACE_IO;
|
||||
DBG("-->set base to 0x%lx\n", io_base);
|
||||
io_base += round(iosize, IO_ALIGN);
|
||||
curdev->command |= PCI_COMMAND_IO;
|
||||
}
|
||||
}
|
||||
}
|
||||
bus->iolimit = iolimit(io_base);
|
||||
|
||||
DBG("BUS %d: set iolimit to 0x%lx\n", bus->number, bus->iolimit);
|
||||
}
|
||||
|
||||
/** Compute and allocate the memory for this bus.
|
||||
* @param bus Pointer to the struct for this bus.
|
||||
*/
|
||||
void compute_allocate_mem(struct pci_bus *bus)
|
||||
{
|
||||
int i;
|
||||
struct pci_bus *curbus;
|
||||
struct pci_dev *curdev;
|
||||
unsigned long mem_base;
|
||||
|
||||
mem_base = bus->membase;
|
||||
DBG("compute_allocate_mem: base 0x%lx\n", bus->membase);
|
||||
|
||||
/* First, walk all the bridges. When you return, grow the limit of the current bus
|
||||
since sub-busses need MEMORY rounded to 1 Mega */
|
||||
for (curbus = bus->children; curbus; curbus = curbus->next) {
|
||||
curbus->membase = mem_base;
|
||||
compute_allocate_mem(curbus);
|
||||
mem_base = round(curbus->memlimit, ONEMEG);
|
||||
DBG("BUSMEM: Bridge Bus 0x%x,membase now 0x%lx\n",
|
||||
curbus->number, mem_base);
|
||||
}
|
||||
|
||||
/* Walk through all the devices on current bus and oompute MEMORY address space.*/
|
||||
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
unsigned long size = curdev->size[i];
|
||||
unsigned long memorysize = size & (PCI_BASE_ADDRESS_MEM_MASK);
|
||||
unsigned long type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
|
||||
|
||||
if (!memorysize)
|
||||
continue;
|
||||
|
||||
if (type == 0) {
|
||||
/* this is normal memory space */
|
||||
unsigned long regmem;
|
||||
|
||||
/* PCI BUS Spec suggests that the memory address should be
|
||||
consumed in 4KB unit */
|
||||
regmem = round(memorysize, MEM_ALIGN);
|
||||
mem_base = round(mem_base, regmem);
|
||||
DBG("DEVMEM: Bus 0x%x, devfn 0x%x, reg 0x%x: "
|
||||
"memsize 0x%lx\n",
|
||||
curdev->bus->number, curdev->devfn, i, regmem);
|
||||
curdev->base_address[i] = mem_base;
|
||||
DBG("-->set base to 0x%lx\n", mem_base);
|
||||
mem_base += regmem;
|
||||
curdev->command |= PCI_COMMAND_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
bus->memlimit = memlimit(mem_base);
|
||||
|
||||
DBG("BUS %d: set memlimit to 0x%lx\n", bus->number, bus->memlimit);
|
||||
}
|
||||
|
||||
/** Compute and allocate the prefetch memory for this bus.
|
||||
* @param bus Pointer to the struct for this bus.
|
||||
*/
|
||||
void compute_allocate_prefmem(struct pci_bus *bus)
|
||||
{
|
||||
int i;
|
||||
struct pci_bus *curbus;
|
||||
struct pci_dev *curdev;
|
||||
unsigned long prefmem_base;
|
||||
|
||||
prefmem_base = bus->prefmembase;
|
||||
DBG("Compute_allocate_prefmem: base 0x%lx\n", bus->prefmembase);
|
||||
|
||||
/* First, walk all the bridges. When you return, grow the limit of the current bus
|
||||
since sub-busses need MEMORY rounded to 1 Mega */
|
||||
for (curbus = bus->children; curbus; curbus = curbus->next) {
|
||||
curbus->prefmembase = prefmem_base;
|
||||
compute_allocate_prefmem(curbus);
|
||||
prefmem_base = round(curbus->prefmemlimit, ONEMEG);
|
||||
DBG("BUSPREFMEM: Bridge Bus 0x%x, prefmem base now 0x%lx\n",
|
||||
curbus->number, prefmem_base);
|
||||
}
|
||||
|
||||
/* Walk through all the devices on current bus and oompute PREFETCHABLE MEMORY address space.*/
|
||||
for (curdev = bus->devices; curdev; curdev = curdev->sibling) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
unsigned long size = curdev->size[i];
|
||||
unsigned long memorysize = size & (PCI_BASE_ADDRESS_MEM_MASK);
|
||||
unsigned long type = size & (~PCI_BASE_ADDRESS_MEM_MASK);
|
||||
|
||||
if (!memorysize)
|
||||
continue;
|
||||
|
||||
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
|
||||
/* this is a prefetchable memory area */
|
||||
unsigned long regmem;
|
||||
|
||||
/* PCI BUS Spec suggests that the memory address should be
|
||||
consumed in 4KB unit */
|
||||
regmem = round(memorysize, MEM_ALIGN);
|
||||
prefmem_base = round(prefmem_base, regmem);
|
||||
DBG("DEVPREFMEM: Bus 0x%x, devfn 0x%x, reg 0x%x: "
|
||||
"prefmemsize 0x%lx\n",
|
||||
curdev->bus->number, curdev->devfn, i, regmem);
|
||||
curdev->base_address[i] = prefmem_base;
|
||||
DBG("-->set base to 0x%lx\n", prefmem_base);
|
||||
prefmem_base += regmem;
|
||||
curdev->command |= PCI_COMMAND_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
bus->prefmemlimit = memlimit(prefmem_base);
|
||||
|
||||
DBG("BUS %d: set prefmemlimit to 0x%lx\n", bus->number, bus->prefmemlimit);
|
||||
}
|
||||
|
||||
/** Compute and allocate resources.
|
||||
* This is a one-pass process. We first compute all the IO, then
|
||||
* memory, then prefetchable memory.
|
||||
* This is really only called at the top level
|
||||
* @param bus Pointer to the struct for this bus.
|
||||
*/
|
||||
void compute_allocate_resources(struct pci_bus *bus)
|
||||
{
|
||||
DBG("COMPUTE_ALLOCATE: do IO\n");
|
||||
compute_allocate_io(bus);
|
||||
|
||||
DBG("COMPUTE_ALLOCATE: do MEM\n");
|
||||
compute_allocate_mem(bus);
|
||||
|
||||
// now put the prefetchable memory at the end of the memory
|
||||
bus->prefmembase = round(bus->memlimit, ONEMEG);
|
||||
|
||||
DBG("COMPUTE_ALLOCATE: do PREFMEM\n");
|
||||
compute_allocate_prefmem(bus);
|
||||
}
|
||||
|
||||
#endif /* TWO_PASS_ALLOCATE */
|
||||
|
||||
/** Assign the computed resources to the bridges and devices on the bus.
|
||||
* Recurse to any bridges found on this bus first. Then do the devices
|
||||
* on this bus.
|
||||
* @param bus Pointer to the structure for this bus
|
||||
*/
|
||||
|
||||
void assign_resources(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *curdev = pci_devices;
|
||||
struct pci_bus *curbus;
|
||||
|
||||
DBG("ASSIGN RESOURCES, bus %d\n", bus->number);
|
||||
|
||||
/* wlak trhough all the buses, assign resources for bridges */
|
||||
for (curbus = bus->children; curbus; curbus = curbus->next) {
|
||||
curbus->self->command = 0;
|
||||
|
||||
/* set the IO ranges
|
||||
WARNING: we don't really do 32-bit addressing for IO yet! */
|
||||
if (curbus->iobase) {
|
||||
curbus->self->command |= PCI_COMMAND_IO;
|
||||
pci_write_config_byte(curbus->self, PCI_IO_BASE,
|
||||
curbus->iobase >> 8);
|
||||
pci_write_config_byte(curbus->self, PCI_IO_LIMIT,
|
||||
curbus->iolimit >> 8);
|
||||
DBG("Bus 0x%x iobase to 0x%x iolimit 0x%x\n",
|
||||
bus->number, curbus->iobase, curbus->iolimit);
|
||||
}
|
||||
|
||||
// set the memory range
|
||||
if (curbus->membase) {
|
||||
curbus->self->command |= PCI_COMMAND_MEMORY;
|
||||
pci_write_config_word(curbus->self, PCI_MEMORY_BASE,
|
||||
curbus->membase >> 16);
|
||||
pci_write_config_word(curbus->self, PCI_MEMORY_LIMIT,
|
||||
curbus->memlimit >> 16);
|
||||
DBG("Bus 0x%x membase to 0x%x memlimit 0x%x\n",
|
||||
bus->number, curbus->membase, curbus->memlimit);
|
||||
|
||||
}
|
||||
|
||||
// set the prefetchable memory range
|
||||
if (curbus->prefmembase) {
|
||||
curbus->self->command |= PCI_COMMAND_MEMORY;
|
||||
pci_write_config_word(curbus->self, PCI_PREF_MEMORY_BASE,
|
||||
curbus->prefmembase >> 16);
|
||||
pci_write_config_word(curbus->self, PCI_PREF_MEMORY_LIMIT,
|
||||
curbus->prefmemlimit >> 16);
|
||||
DBG("Bus 0x%x prefmembase to 0x%x prefmemlimit 0x%x\n",
|
||||
bus->number, curbus->prefmembase, curbus->prefmemlimit);
|
||||
|
||||
}
|
||||
curbus->self->command |= PCI_COMMAND_MASTER;
|
||||
}
|
||||
|
||||
for (curdev = pci_devices; curdev; curdev = curdev->next) {
|
||||
int i;
|
||||
for (i = 0; i < 6; i++) {
|
||||
unsigned long reg;
|
||||
if (curdev->base_address[i] == 0)
|
||||
continue;
|
||||
|
||||
reg = PCI_BASE_ADDRESS_0 + (i << 2);
|
||||
pci_write_config_dword(curdev, reg, curdev->base_address[i]);
|
||||
DBG("Bus 0x%x devfn 0x%x reg 0x%x base to 0x%lx\n",
|
||||
curdev->bus->number, curdev->devfn, i,
|
||||
curdev->base_address[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enable_resources(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *curdev = pci_devices;
|
||||
|
||||
/* walk through the chain of all pci device, this time we don't have to deal
|
||||
with the device v.s. bridge stuff, since every bridge has its own pci_dev
|
||||
assocaited with it */
|
||||
for (curdev = pci_devices; curdev; curdev = curdev->next) {
|
||||
u16 command;
|
||||
pci_read_config_word(curdev, PCI_COMMAND, &command);
|
||||
command |= curdev->command;
|
||||
pci_write_config_word(curdev, PCI_COMMAND, command);
|
||||
DBG("DEV Set command bus 0x%x devfn 0x%x to 0x%x\n",
|
||||
curdev->bus->number, curdev->devfn, command);
|
||||
}
|
||||
}
|
||||
|
||||
/** Enumerate the resources on the PCI by calling pci_init
|
||||
*/
|
||||
void pci_enumerate()
|
||||
{
|
||||
// scan it.
|
||||
pci_init();
|
||||
}
|
||||
|
||||
/** Starting at the root, compute what resources are needed and allocate them.
|
||||
* We start memory, prefetchable memory at PCI_MEM_START. I/O starts at
|
||||
* PCI_IO_START. Since the assignment is hierarchical we set the values
|
||||
* into the pci_root struct.
|
||||
*/
|
||||
void pci_configure()
|
||||
{
|
||||
pci_root.membase = PCI_MEM_START;
|
||||
pci_root.prefmembase = PCI_MEM_START;
|
||||
pci_root.iobase = PCI_IO_START;
|
||||
|
||||
compute_allocate_resources(&pci_root);
|
||||
// now just set things into registers ... we hope ...
|
||||
assign_resources(&pci_root);
|
||||
}
|
||||
|
||||
/** Starting at the root, walk the tree and enable all devices/bridges.
|
||||
* What really happens is computed COMMAND bits get set in register 4
|
||||
*/
|
||||
void pci_enable()
|
||||
{
|
||||
// now enable everything.
|
||||
enable_resources(&pci_root);
|
||||
}
|
||||
|
||||
// is this dead code? think so. -- RGM
|
||||
int configure_pci(unsigned long memstart, unsigned long iostart)
|
||||
{
|
||||
#ifdef EMULATE
|
||||
int iopl(int level);
|
||||
iopl(3);
|
||||
// pick how to scan the bus
|
||||
pci_set_method();
|
||||
#else
|
||||
// FIX ME. We really should use whatever is set up elsewhere.
|
||||
void malloc_init(unsigned long start, unsigned long end);
|
||||
// pick the last 1M of memory to put our structures in.
|
||||
malloc_init(memstart - ONEMEG, memstart - 1);
|
||||
#endif
|
||||
|
||||
// scan it.
|
||||
pci_init();
|
||||
|
||||
#ifdef TWO_PASS_ALLOCATE
|
||||
/* compute the memory resources. You can't just add up the
|
||||
* per-device resources. You have to walk the bridges, because
|
||||
* bridges require 1M granularity at 1M alignments (!)
|
||||
*/
|
||||
pci_root.mem = pci_root.prefmem = pci_root.io = 0;
|
||||
compute_resources(&pci_root);
|
||||
printk(KERN_DEBUG "Total: 0x%lx mem, 0x%lx prefmem, 0x%lx io\n",
|
||||
pci_root.mem, pci_root.prefmem, pci_root.io);
|
||||
// set in the bases and limits for the root. These really apply to the
|
||||
// subordinate busses and devices.
|
||||
pci_root.membase = memstart;
|
||||
pci_root.memlimit = pci_root.membase + pci_root.mem - 1;
|
||||
pci_root.prefmembase = round(pci_root.memlimit, ONEMEG);
|
||||
pci_root.prefmemlimit = pci_root.prefmembase + pci_root.prefmem - 1;
|
||||
pci_root.iobase = iostart;
|
||||
pci_root.iolimit = 0xffff - iostart;
|
||||
allocate_resources(&pci_root);
|
||||
#else
|
||||
pci_root.membase = PCI_MEM_START;
|
||||
pci_root.prefmembase = PCI_MEM_START;
|
||||
pci_root.iobase = PCI_IO_START;
|
||||
compute_allocate_resources(&pci_root);
|
||||
#endif /* TWO_PASS_ALLOCATE */
|
||||
|
||||
// now just set things into registers ... we hope ...
|
||||
assign_resources(&pci_root);
|
||||
// now enable everything.
|
||||
enable_resources(&pci_root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef EMULATE
|
||||
/** deprecated, you can run this code in user mode for certain testing
|
||||
* We haven't used this recently so it's unclear if it works.
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
unsigned long memstart = 0x40000000;
|
||||
unsigned long iostart = 0x1000;
|
||||
configure_pci(memstart, iostart);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
62
src/lib/printk.c
Normal file
62
src/lib/printk.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* blantantly copied from linux/kernel/printk.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
//typedef void * va_list;
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static char buf[1024];
|
||||
|
||||
/* printk's without a loglevel use this.. */
|
||||
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
|
||||
|
||||
/* We show everything that is MORE important than this.. */
|
||||
#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
|
||||
|
||||
#ifndef DEFAULT_CONSOLE_LOGLEVEL
|
||||
#define DEFAULT_CONSOLE_LOGLEVEL 8 /* anything MORE serious than KERN_WARNING */
|
||||
#endif
|
||||
|
||||
/* Keep together for sysctl support */
|
||||
|
||||
int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
|
||||
int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL;
|
||||
int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
|
||||
int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
|
||||
|
||||
void display(char*);
|
||||
extern int vsprintf(char *buf, const char *, va_list);
|
||||
|
||||
int printk(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
char *p;
|
||||
int msg_level;
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf)-4 */
|
||||
va_end(args);
|
||||
p = buf;
|
||||
if (
|
||||
p[0] == '<' &&
|
||||
p[1] > '0' &&
|
||||
p[1] <= '9' &&
|
||||
p[2] == '>'
|
||||
) {
|
||||
msg_level = p[1] - '0';
|
||||
p +=3;
|
||||
} else {
|
||||
msg_level = default_message_loglevel;
|
||||
}
|
||||
if (msg_level < console_loglevel) {
|
||||
display(p);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
331
src/lib/vsprintf.c
Normal file
331
src/lib/vsprintf.c
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* linux/lib/vsprintf.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*/
|
||||
|
||||
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
|
||||
/*
|
||||
* Wirzenius wrote this portably, Torvalds fucked it up :-)
|
||||
*/
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
/* haha, don't need ctype.c */
|
||||
#define isdigit(c) ((c) >= '0' && (c) <= '9')
|
||||
#define is_digit isdigit
|
||||
#define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
|
||||
#define islower(c) ((c) >= 'a' && (c) <= 'z')
|
||||
#define toupper(c) __toupper(c)
|
||||
|
||||
static inline unsigned char __toupper(unsigned char c)
|
||||
{
|
||||
if (islower(c))
|
||||
c -= 'a'-'A';
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
|
||||
{
|
||||
unsigned long result = 0,value;
|
||||
|
||||
if (!base) {
|
||||
base = 10;
|
||||
if (*cp == '0') {
|
||||
base = 8;
|
||||
cp++;
|
||||
if ((*cp == 'x') && isxdigit(cp[1])) {
|
||||
cp++;
|
||||
base = 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
|
||||
? toupper(*cp) : *cp)-'A'+10) < base) {
|
||||
result = result*base + value;
|
||||
cp++;
|
||||
}
|
||||
if (endp)
|
||||
*endp = (char *)cp;
|
||||
return result;
|
||||
}
|
||||
|
||||
long simple_strtol(const char *cp,char **endp,unsigned int base)
|
||||
{
|
||||
if(*cp=='-')
|
||||
return -simple_strtoul(cp+1,endp,base);
|
||||
return simple_strtoul(cp,endp,base);
|
||||
}
|
||||
|
||||
|
||||
static int skip_atoi(const char **s)
|
||||
{
|
||||
int i=0;
|
||||
|
||||
while (is_digit(**s))
|
||||
i = i*10 + *((*s)++) - '0';
|
||||
return i;
|
||||
}
|
||||
|
||||
#define ZEROPAD 1 /* pad with zero */
|
||||
#define SIGN 2 /* unsigned/signed long */
|
||||
#define PLUS 4 /* show plus */
|
||||
#define SPACE 8 /* space if plus */
|
||||
#define LEFT 16 /* left justified */
|
||||
#define SPECIAL 32 /* 0x */
|
||||
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
|
||||
|
||||
#define do_div(n,base) ({ \
|
||||
int __res; \
|
||||
__res = ((unsigned long) n) % (unsigned) base; \
|
||||
n = ((unsigned long) n) / (unsigned) base; \
|
||||
__res; })
|
||||
|
||||
static char * number(char * str, long num, int base, int size, int precision
|
||||
,int type)
|
||||
{
|
||||
char c,sign,tmp[66];
|
||||
const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
int i;
|
||||
|
||||
if (type & LARGE)
|
||||
digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
if (type & LEFT)
|
||||
type &= ~ZEROPAD;
|
||||
if (base < 2 || base > 36)
|
||||
return 0;
|
||||
c = (type & ZEROPAD) ? '0' : ' ';
|
||||
sign = 0;
|
||||
if (type & SIGN) {
|
||||
if (num < 0) {
|
||||
sign = '-';
|
||||
num = -num;
|
||||
size--;
|
||||
} else if (type & PLUS) {
|
||||
sign = '+';
|
||||
size--;
|
||||
} else if (type & SPACE) {
|
||||
sign = ' ';
|
||||
size--;
|
||||
}
|
||||
}
|
||||
if (type & SPECIAL) {
|
||||
if (base == 16)
|
||||
size -= 2;
|
||||
else if (base == 8)
|
||||
size--;
|
||||
}
|
||||
i = 0;
|
||||
if (num == 0)
|
||||
tmp[i++]='0';
|
||||
else while (num != 0)
|
||||
tmp[i++] = digits[do_div(num,base)];
|
||||
if (i > precision)
|
||||
precision = i;
|
||||
size -= precision;
|
||||
if (!(type&(ZEROPAD+LEFT)))
|
||||
while(size-->0)
|
||||
*str++ = ' ';
|
||||
if (sign)
|
||||
*str++ = sign;
|
||||
if (type & SPECIAL) {
|
||||
if (base==8)
|
||||
*str++ = '0';
|
||||
else if (base==16) {
|
||||
*str++ = '0';
|
||||
*str++ = digits[33];
|
||||
}
|
||||
}
|
||||
if (!(type & LEFT))
|
||||
while (size-- > 0)
|
||||
*str++ = c;
|
||||
while (i < precision--)
|
||||
*str++ = '0';
|
||||
while (i-- > 0)
|
||||
*str++ = tmp[i];
|
||||
while (size-- > 0)
|
||||
*str++ = ' ';
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Forward decl. needed for IP address printing stuff... */
|
||||
int sprintf(char * buf, const char *fmt, ...);
|
||||
|
||||
int vsprintf(char *buf, const char *fmt, va_list args)
|
||||
{
|
||||
int len;
|
||||
unsigned long num;
|
||||
int i, base;
|
||||
char * str;
|
||||
const char *s;
|
||||
|
||||
int flags; /* flags to number() */
|
||||
|
||||
int field_width; /* width of output field */
|
||||
int precision; /* min. # of digits for integers; max
|
||||
number of chars for from string */
|
||||
int qualifier; /* 'h', 'l', or 'L' for integer fields */
|
||||
|
||||
for (str=buf ; *fmt ; ++fmt) {
|
||||
if (*fmt != '%') {
|
||||
*str++ = *fmt;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* process flags */
|
||||
flags = 0;
|
||||
repeat:
|
||||
++fmt; /* this also skips first '%' */
|
||||
switch (*fmt) {
|
||||
case '-': flags |= LEFT; goto repeat;
|
||||
case '+': flags |= PLUS; goto repeat;
|
||||
case ' ': flags |= SPACE; goto repeat;
|
||||
case '#': flags |= SPECIAL; goto repeat;
|
||||
case '0': flags |= ZEROPAD; goto repeat;
|
||||
}
|
||||
|
||||
/* get field width */
|
||||
field_width = -1;
|
||||
if (is_digit(*fmt))
|
||||
field_width = skip_atoi(&fmt);
|
||||
else if (*fmt == '*') {
|
||||
++fmt;
|
||||
/* it's the next argument */
|
||||
field_width = va_arg(args, int);
|
||||
if (field_width < 0) {
|
||||
field_width = -field_width;
|
||||
flags |= LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
/* get the precision */
|
||||
precision = -1;
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
if (is_digit(*fmt))
|
||||
precision = skip_atoi(&fmt);
|
||||
else if (*fmt == '*') {
|
||||
++fmt;
|
||||
/* it's the next argument */
|
||||
precision = va_arg(args, int);
|
||||
}
|
||||
if (precision < 0)
|
||||
precision = 0;
|
||||
}
|
||||
|
||||
/* get the conversion qualifier */
|
||||
qualifier = -1;
|
||||
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
|
||||
qualifier = *fmt;
|
||||
++fmt;
|
||||
}
|
||||
|
||||
/* default base */
|
||||
base = 10;
|
||||
|
||||
switch (*fmt) {
|
||||
case 'c':
|
||||
if (!(flags & LEFT))
|
||||
while (--field_width > 0)
|
||||
*str++ = ' ';
|
||||
*str++ = (unsigned char) va_arg(args, int);
|
||||
while (--field_width > 0)
|
||||
*str++ = ' ';
|
||||
continue;
|
||||
|
||||
case 's':
|
||||
s = va_arg(args, char *);
|
||||
if (!s)
|
||||
s = "<NULL>";
|
||||
|
||||
len = strnlen(s, precision);
|
||||
|
||||
if (!(flags & LEFT))
|
||||
while (len < field_width--)
|
||||
*str++ = ' ';
|
||||
for (i = 0; i < len; ++i)
|
||||
*str++ = *s++;
|
||||
while (len < field_width--)
|
||||
*str++ = ' ';
|
||||
continue;
|
||||
|
||||
case 'p':
|
||||
if (field_width == -1) {
|
||||
field_width = 2*sizeof(void *);
|
||||
flags |= ZEROPAD;
|
||||
}
|
||||
str = number(str,
|
||||
(unsigned long) va_arg(args, void *), 16,
|
||||
field_width, precision, flags);
|
||||
continue;
|
||||
|
||||
|
||||
case 'n':
|
||||
if (qualifier == 'l') {
|
||||
long * ip = va_arg(args, long *);
|
||||
*ip = (str - buf);
|
||||
} else {
|
||||
int * ip = va_arg(args, int *);
|
||||
*ip = (str - buf);
|
||||
}
|
||||
continue;
|
||||
|
||||
case '%':
|
||||
*str++ = '%';
|
||||
continue;
|
||||
|
||||
/* integer number formats - set up the flags and "break" */
|
||||
case 'o':
|
||||
base = 8;
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
flags |= LARGE;
|
||||
case 'x':
|
||||
base = 16;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
flags |= SIGN;
|
||||
case 'u':
|
||||
break;
|
||||
|
||||
default:
|
||||
*str++ = '%';
|
||||
if (*fmt)
|
||||
*str++ = *fmt;
|
||||
else
|
||||
--fmt;
|
||||
continue;
|
||||
}
|
||||
if (qualifier == 'l')
|
||||
num = va_arg(args, unsigned long);
|
||||
else if (qualifier == 'h') {
|
||||
num = (unsigned short) va_arg(args, int);
|
||||
if (flags & SIGN)
|
||||
num = (short) num;
|
||||
} else if (flags & SIGN)
|
||||
num = va_arg(args, int);
|
||||
else
|
||||
num = va_arg(args, unsigned int);
|
||||
str = number(str, num, base, field_width, precision, flags);
|
||||
}
|
||||
*str = '\0';
|
||||
return str-buf;
|
||||
}
|
||||
|
||||
int sprintf(char * buf, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
|
||||
va_start(args, fmt);
|
||||
i=vsprintf(buf,fmt,args);
|
||||
va_end(args);
|
||||
return i;
|
||||
}
|
||||
192
src/pc80/serial.inc
Normal file
192
src/pc80/serial.inc
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
|
||||
/* Base Address */
|
||||
#define TTYS0 0x3f8
|
||||
|
||||
/* Data */
|
||||
#define TTYS0_RBR (TTYS0+0x00)
|
||||
|
||||
/* Control */
|
||||
#define TTYS0_TBR TTYS0_RBR
|
||||
#define TTYS0_IER (TTYS0+0x01)
|
||||
#define TTYS0_IIR (TTYS0+0x02)
|
||||
#define TTYS0_FCR TTYS0_IIR
|
||||
#define TTYS0_LCR (TTYS0+0x03)
|
||||
#define TTYS0_MCR (TTYS0+0x04)
|
||||
#define TTYS0_DLL TTYS0_RBR
|
||||
#define TTYS0_DLM TTYS0_IER
|
||||
|
||||
/* Status */
|
||||
#define TTYS0_LSR (TTYS0+0x05)
|
||||
#define TTYS0_MSR (TTYS0+0x06)
|
||||
#define TTYS0_SCR (TTYS0+0x07)
|
||||
|
||||
jmp serial0
|
||||
|
||||
/* uses: ax, dx */
|
||||
|
||||
#define TTYS0_TX_AL \
|
||||
mov %al, %ah ; \
|
||||
9: mov $TTYS0_LSR, %dx ; \
|
||||
inb %dx, %al ; \
|
||||
test $0x20, %al ; \
|
||||
je 9b ; \
|
||||
mov $TTYS0_TBR, %dx ; \
|
||||
mov %ah, %al ; \
|
||||
outb %al, %dx
|
||||
|
||||
/* uses: esp, ax, dx */
|
||||
#define TTYS0_TX_CHAR(byte) \
|
||||
mov byte, %al ; \
|
||||
CALLSP(ttys0_tx_al)
|
||||
|
||||
/* uses: esp, ax, dx */
|
||||
#define TTYS0_TX_HEX8(byte) \
|
||||
mov byte, %al ; \
|
||||
CALLSP(ttys0_tx_hex8)
|
||||
|
||||
/* uses: esp, eax, ebx, dx */
|
||||
#define TTYS0_TX_HEX32(lword) \
|
||||
mov lword, %eax ; \
|
||||
CALLSP(ttys0_tx_hex32)
|
||||
|
||||
/* uses: esp, ebx, ax, dx */
|
||||
#define TTYS0_TX_STRING(string) \
|
||||
mov string, %ebx ; \
|
||||
CALLSP(ttys0_tx_string)
|
||||
|
||||
/* uses: esp, ax, dx */
|
||||
ttys0_tx_al:
|
||||
TTYS0_TX_AL
|
||||
RETSP
|
||||
|
||||
/* uses: esp, ax, dx */
|
||||
ttys0_tx_hex8:
|
||||
mov $TTYS0_SCR, %dx
|
||||
outb %al, %dx
|
||||
shr $4, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
mov $TTYS0_SCR, %dx
|
||||
inb %dx, %al
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
RETSP
|
||||
|
||||
/* uses: esp, ebx, eax, dx */
|
||||
ttys0_tx_hex32:
|
||||
mov %eax, %ebx
|
||||
shr $28, %eax
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
mov %ebx, %eax
|
||||
shr $24, %eax
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
mov %ebx, %eax
|
||||
shr $20, %eax
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
mov %ebx, %eax
|
||||
shr $16, %eax
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
mov %ebx, %eax
|
||||
shr $12, %eax
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
mov %ebx, %eax
|
||||
shr $8, %eax
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
mov %ebx, %eax
|
||||
shr $4, %eax
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
mov %ebx, %eax
|
||||
and $0x0f, %al
|
||||
add $'0', %al
|
||||
cmp $'9', %al
|
||||
jle 9f
|
||||
add $39, %al
|
||||
9:
|
||||
TTYS0_TX_AL
|
||||
|
||||
RETSP
|
||||
|
||||
/* Uses esp, ebx, ax, dx */
|
||||
|
||||
ttys0_tx_string:
|
||||
mov (%ebx), %al
|
||||
inc %ebx
|
||||
cmp $0, %al
|
||||
jne 9f
|
||||
RETSP
|
||||
9: TTYS0_TX_AL
|
||||
jmp ttys0_tx_string
|
||||
|
||||
serial0:
|
||||
|
||||
/* Set 115.2Kbps,8n1 */
|
||||
|
||||
mov $TTYS0_LCR, %dx
|
||||
mov $0x83, %al
|
||||
out %al, %dx
|
||||
mov $TTYS0_DLL, %dx
|
||||
mov $0x01, %al
|
||||
out %al, %dx
|
||||
mov $TTYS0_DLM, %dx
|
||||
mov $0x00, %al
|
||||
out %al, %dx
|
||||
mov $TTYS0_LCR, %dx
|
||||
mov $0x03, %al
|
||||
out %al, %dx
|
||||
Loading…
Add table
Add a link
Reference in a new issue