/*
 * bios32.c - BIOS32, PCI BIOS functions.
 *
 * Sponsored by
 *	iX Multiuser Multitasking Magazine
 *	Hannover, Germany
 *	hm@ix.de
 *
 * Copyright 1993, 1994 Drew Eckhardt
 *      Visionary Computing
 *      (Unix and Linux consulting and custom programming)
 *      Drew@Colorado.EDU
 *      +1 (303) 786-7975
 *
 * For more information, please consult
 *
 * PCI BIOS Specification Revision
 * PCI Local Bus Specification
 * PCI System Design Guide
 *
 * PCI Special Interest Group
 * M/S HF3-15A
 * 5200 N.E. Elam Young Parkway
 * Hillsboro, Oregon 97124-6497
 * +1 (503) 696-2000
 * +1 (800) 433-5177
 *
 * Manuals are $25 each or $50 for all three, plus $7 shipping
 * within the United States, $35 abroad.
 *
 *
 * CHANGELOG :
 * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION
 *	Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard.
 *
 * Jan 5,  1995 : Modified to probe PCI hardware at boot time by Frederic
 *     Potter, potter@cao-vlsi.ibp.fr
 *
 * Jan 10, 1995 : Modified to store the information about configured pci
 *      devices into a list, which can be accessed via /proc/pci by
 *      Curtis Varner, cvarner@cs.ucr.edu
 *
 * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter.
 *	Alpha version. Intel & UMC chipset support only. See pci.h for more.
 */

#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/bios32.h>
#include <linux/pci.h>

#include <asm/segment.h>

#define PCIBIOS_PCI_FUNCTION_ID 	0xb1XX
#define PCIBIOS_PCI_BIOS_PRESENT 	0xb101
#define PCIBIOS_FIND_PCI_DEVICE		0xb102
#define PCIBIOS_FIND_PCI_CLASS_CODE	0xb103
#define PCIBIOS_GENERATE_SPECIAL_CYCLE	0xb106
#define PCIBIOS_READ_CONFIG_BYTE	0xb108
#define PCIBIOS_READ_CONFIG_WORD	0xb109
#define PCIBIOS_READ_CONFIG_DWORD	0xb10a
#define PCIBIOS_WRITE_CONFIG_BYTE	0xb10b
#define PCIBIOS_WRITE_CONFIG_WORD	0xb10c
#define PCIBIOS_WRITE_CONFIG_DWORD	0xb10d

#ifdef CONFIG_PCI
extern void add_pci_resource(unsigned char, unsigned char);
#endif

/* BIOS32 signature: "_32_" */
#define BIOS32_SIGNATURE	(('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))

/* PCI signature: "PCI " */
#define PCI_SIGNATURE		(('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24))

/* PCI service signature: "$PCI" */
#define PCI_SERVICE		(('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24))

/*
 * This is the standard structure used to identify the entry point
 * to the BIOS32 Service Directory, as documented in
 * 	Standard BIOS 32-bit Service Directory Proposal
 * 	Revision 0.4 May 24, 1993
 * 	Phoenix Technologies Ltd.
 *	Norwood, MA
 * and the PCI BIOS specification.
 */

#ifdef CONFIG_PCI
typedef struct pci_resource_t
{
   unsigned char bus;
   unsigned char dev_fn;
   struct pci_resource_t *next;
} pci_resource_t;
#endif

union bios32 {
	struct {
		unsigned long signature;	/* _32_ */
		unsigned long entry;		/* 32 bit physical address */
		unsigned char revision;		/* Revision level, 0 */
		unsigned char length;		/* Length in paragraphs should be 01 */
		unsigned char checksum;		/* All bytes must add up to zero */
		unsigned char reserved[5]; 	/* Must be zero */
	} fields;
	char chars[16];
};

/*
 * Physical address of the service directory.  I don't know if we're
 * allowed to have more than one of these or not, so just in case
 * we'll make bios32_init() take a memory start parameter and store
 * the array there.
 */

static unsigned long bios32_entry = 0;
static struct {
	unsigned long address;
	unsigned short segment;
} bios32_indirect = { 0, KERNEL_CS };

#ifdef CONFIG_PCI
/*
 * Returns the entry point for the given service, NULL on error
 */

#define PCI_LIST_SIZE 32

static pci_resource_t pci_list = { 0, 0, NULL };
static int pci_index = 0;
static pci_resource_t pci_table[PCI_LIST_SIZE];

static unsigned long bios32_service(unsigned long service)
{
	unsigned char return_code;	/* %al */
	unsigned long address;		/* %ebx */
	unsigned long length;		/* %ecx */
	unsigned long entry;		/* %edx */

	__asm__("lcall (%%edi)"
		: "=a" (return_code),
		  "=b" (address),
		  "=c" (length),
		  "=d" (entry)
		: "0" (service),
		  "1" (0),
		  "D" (&bios32_indirect));

	switch (return_code) {
		case 0:
			return address + entry;
		case 0x80:	/* Not present */
			printk("bios32_service(%ld) : not present\n", service);
			return 0;
		default: /* Shouldn't happen */
			printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
				service, return_code);
			return 0;
	}
}

static long pcibios_entry = 0;
static struct {
	unsigned long address;
	unsigned short segment;
} pci_indirect = { 0, KERNEL_CS };

void NCR53c810_test(void);

static unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
{
	unsigned long signature;
	unsigned char present_status;
	unsigned char major_revision;
	unsigned char minor_revision;
	int pack;

	if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
		pci_indirect.address = pcibios_entry;

		__asm__("lcall (%%edi)\n\t"
			"jc 1f\n\t"
			"xor %%ah, %%ah\n"
			"1:\tshl $8, %%eax\n\t"
			"movw %%bx, %%ax"
			: "=d" (signature),
			  "=a" (pack)
			: "1" (PCIBIOS_PCI_BIOS_PRESENT),
			  "D" (&pci_indirect)
			: "bx", "cx");

		present_status = (pack >> 16) & 0xff;
		major_revision = (pack >> 8) & 0xff;
		minor_revision = pack & 0xff;
		if (present_status || (signature != PCI_SIGNATURE)) {
			printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n"
				"	but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n"
				"	and signature of 0x%08lx (%c%c%c%c).  mail drew@Colorado.EDU\n",
				(signature == PCI_SIGNATURE) ?  "WARNING" : "ERROR",
				present_status, signature,
				(char) (signature >>  0), (char) (signature >>  8),
				(char) (signature >> 16), (char) (signature >> 24));

			if (signature != PCI_SIGNATURE)
				pcibios_entry = 0;
		}
		if (pcibios_entry) {
			printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
				major_revision, minor_revision, pcibios_entry);
		}
	}

#if 0
	NCR53c810_test();
#endif
	return memory_start;
}

int pcibios_present(void)
{
	return pcibios_entry ? 1 : 0;
}

int pcibios_find_class (unsigned long class_code, unsigned short index,
	unsigned char *bus, unsigned char *device_fn)
{
	unsigned long bx;
	unsigned long ret;

	__asm__ ("lcall (%%edi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=b" (bx),
		  "=a" (ret)
		: "1" (PCIBIOS_FIND_PCI_CLASS_CODE),
		  "c" (class_code),
		  "S" ((int) index),
		  "D" (&pci_indirect));
	*bus = (bx >> 8) & 0xff;
	*device_fn = bx & 0xff;
	return (int) (ret & 0xff00) >> 8;
}


int pcibios_find_device (unsigned short vendor, unsigned short device_id,
	unsigned short index, unsigned char *bus, unsigned char *device_fn)
{
	unsigned short bx;
	unsigned short ret;

	__asm__("lcall (%%edi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=b" (bx),
		  "=a" (ret)
		: "1" (PCIBIOS_FIND_PCI_DEVICE),
		  "c" (device_id),
		  "d" (vendor),
		  "S" ((int) index),
		  "D" (&pci_indirect));
	*bus = (bx >> 8) & 0xff;
	*device_fn = bx & 0xff;
	return (int) (ret & 0xff00) >> 8;
}

int pcibios_read_config_byte(unsigned char bus,
	unsigned char device_fn, unsigned char where, unsigned char *value)
{
	unsigned long ret;
	unsigned long bx = (bus << 8) | device_fn;

	__asm__("lcall (%%esi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=c" (*value),
		  "=a" (ret)
		: "1" (PCIBIOS_READ_CONFIG_BYTE),
		  "b" (bx),
		  "D" ((long) where),
		  "S" (&pci_indirect));
	return (int) (ret & 0xff00) >> 8;
}

int pcibios_read_config_word (unsigned char bus,
	unsigned char device_fn, unsigned char where, unsigned short *value)
{
	unsigned long ret;
	unsigned long bx = (bus << 8) | device_fn;

	__asm__("lcall (%%esi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=c" (*value),
		  "=a" (ret)
		: "1" (PCIBIOS_READ_CONFIG_WORD),
		  "b" (bx),
		  "D" ((long) where),
		  "S" (&pci_indirect));
	return (int) (ret & 0xff00) >> 8;
}

int pcibios_read_config_dword (unsigned char bus,
	unsigned char device_fn, unsigned char where, unsigned long *value)
{
	unsigned long ret;
	unsigned long bx = (bus << 8) | device_fn;

	__asm__("lcall (%%esi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=c" (*value),
		  "=a" (ret)
		: "1" (PCIBIOS_READ_CONFIG_DWORD),
		  "b" (bx),
		  "D" ((long) where),
		  "S" (&pci_indirect));
	return (int) (ret & 0xff00) >> 8;
}

int pcibios_write_config_byte (unsigned char bus,
	unsigned char device_fn, unsigned char where, unsigned char value)
{
	unsigned long ret;
	unsigned long bx = (bus << 8) | device_fn;

	__asm__("lcall (%%esi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=a" (ret)
		: "0" (PCIBIOS_WRITE_CONFIG_BYTE),
		  "c" (value),
		  "b" (bx),
		  "D" ((long) where),
		  "S" (&pci_indirect));
	return (int) (ret & 0xff00) >> 8;
}

int pcibios_write_config_word (unsigned char bus,
	unsigned char device_fn, unsigned char where, unsigned short value)
{
	unsigned long ret;
	unsigned long bx = (bus << 8) | device_fn;

	__asm__("lcall (%%esi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=a" (ret)
		: "0" (PCIBIOS_WRITE_CONFIG_WORD),
		  "c" (value),
		  "b" (bx),
		  "D" ((long) where),
		  "S" (&pci_indirect));
	return (int) (ret & 0xff00) >> 8;
}

int pcibios_write_config_dword (unsigned char bus,
	unsigned char device_fn, unsigned char where, unsigned long value)
{
	unsigned long ret;
	unsigned long bx = (bus << 8) | device_fn;

	__asm__("lcall (%%esi)\n\t"
		"jc 1f\n\t"
		"xor %%ah, %%ah\n"
		"1:"
		: "=a" (ret)
		: "0" (PCIBIOS_WRITE_CONFIG_DWORD),
		  "c" (value),
		  "b" (bx),
		  "D" ((long) where),
		  "S" (&pci_indirect));
	return (int) (ret & 0xff00) >> 8;
}

void NCR53c810_test(void)
{
	unsigned char bus, device_fn;
	unsigned short index;
	int ret;
	unsigned char row, col;
	unsigned long val;

	for (index = 0; index < 4; ++index) {
		ret = pcibios_find_device (
			(unsigned short) PCI_VENDOR_ID_NCR,
			(unsigned short) PCI_DEVICE_ID_NCR_53C810,
			index, &bus, &device_fn);
		if (ret)
			break;
		printk ("ncr53c810 : at PCI bus %d, device %d, function %d.",
			bus, ((device_fn & 0xf8) >> 3), (device_fn & 7));
		for (row = 0; row < 0x3c; row += 0x10) {
			printk ("\n	reg 0x%02x	", row);
			for (col = 0; col < 0x10; col += 4) {
			if (!(ret = pcibios_read_config_dword (bus, device_fn, row+col, &val)))
				printk ("0x%08lx	  ", val);
			else
				printk ("error 0x%02x ", ret);
			}
		}
		printk ("\n");
	}
}

char *pcibios_strerror (int error)
{
	static char buf[80];

	switch (error) {
		case PCIBIOS_SUCCESSFUL:
			return "SUCCESSFUL";

		case PCIBIOS_FUNC_NOT_SUPPORTED:
			return "FUNC_NOT_SUPPORTED";

		case PCIBIOS_BAD_VENDOR_ID:
			return "SUCCESSFUL";

		case PCIBIOS_DEVICE_NOT_FOUND:
			return "DEVICE_NOT_FOUND";

		case PCIBIOS_BAD_REGISTER_NUMBER:
			return "BAD_REGISTER_NUMBER";

		default:
			sprintf (buf, "UNKNOWN RETURN 0x%x", error);
			return buf;
	}
}


/* Recognize multi-function device */

int multi_function(unsigned char bus,unsigned char dev_fn)
{
	unsigned char header;
		pcibios_read_config_byte(
			bus, dev_fn, (unsigned char) PCI_HEADER_TYPE, &header);
		return (header&7==7);
}

/* Returns Interrupt register */

int interrupt_decode(unsigned char bus,unsigned char dev_fn)
{
	unsigned char interrupt;
		pcibios_read_config_byte(
			bus, dev_fn, (unsigned char) PCI_INTERRUPT_LINE, &interrupt);
		if (interrupt>16) return 0;
		return interrupt;
}

/* probe for being bist capable */

int bist_probe(unsigned char bus,unsigned char dev_fn)
{
	unsigned char   	bist;
		pcibios_read_config_byte(
			bus, dev_fn, (unsigned char) PCI_BIST, &bist);
		return (bist & PCI_BIST_CAPABLE !=0);
}


/* Get the chip revision */

int revision_decode(unsigned char bus,unsigned char dev_fn)
{
	unsigned char   	revision;
       		pcibios_read_config_byte(
			bus, dev_fn, (unsigned char) PCI_CLASS_REVISION, &revision);
		return (int) revision;
}



/* Gives the Class code using the 16 higher bits */
/* of the PCI_CLASS_REVISION configuration register */

int class_decode(unsigned char bus,unsigned char dev_fn)
{
	struct pci_class_type 	pci_class[PCI_CLASS_NUM] = PCI_CLASS_TYPE;
	int 			i;
	unsigned long   	class;
		pcibios_read_config_dword(
			bus, dev_fn, (unsigned char) PCI_CLASS_REVISION, &class);
		class=class >> 16;
		for (i=0;i<PCI_CLASS_NUM;i++)
			if (class==pci_class[i].class_id) break;
		return i;
}


int device_decode(unsigned char bus,unsigned char dev_fn,unsigned short vendor_num)
{
	struct pci_device_type 	pci_device[PCI_DEVICE_NUM] = PCI_DEVICE_TYPE;
	struct pci_vendor_type 	pci_vendor[PCI_VENDOR_NUM] = PCI_VENDOR_TYPE;
	int 			i;
	unsigned short   	device;

	pcibios_read_config_word(
		bus, dev_fn, (unsigned char) PCI_DEVICE_ID, &device);
	for (i=0;i<PCI_DEVICE_NUM;i++)
		if ((device==pci_device[i].device_id)
		 && (pci_vendor[vendor_num].vendor_id==pci_device[i].vendor_id)) return i;
	return 0x10000 + (int) device;
}


int vendor_decode(unsigned char bus,unsigned char dev_fn)
{
	struct pci_vendor_type 	pci_vendor[PCI_VENDOR_NUM] = PCI_VENDOR_TYPE;
	int			i;
	unsigned short   	vendor;

	pcibios_read_config_word(
		bus, dev_fn, (unsigned char) PCI_VENDOR_ID, &vendor);
	for (i=0;i<PCI_VENDOR_NUM;i++)
		if (vendor==pci_vendor[i].vendor_id) return i;
	return 0x10000 + (int) vendor;
}

#ifdef CONFIG_PCI_OPTIMIZE

unsigned char bridge_decode(int device)
{
	struct pci_device_type	pci_device[PCI_DEVICE_NUM] = PCI_DEVICE_TYPE;
	return (pci_device[device].bridge_id);
}

/* Turn on/off PCI bridge optimisation. This should allow benchmarking. */

void burst_bridge(unsigned char bus,unsigned char dev_fn,unsigned char pos, int turn_on)
{
	struct bridge_mapping_type bridge_mapping[5*BRIDGE_MAPPING_NUM] = BRIDGE_MAPPING_TYPE;
	struct optimisation_type optimisation[OPTIMISATION_NUM] = OPTIMISATION_TYPE;
	int i;
	unsigned char val;

	pos*=OPTIMISATION_NUM;
	printk("PCI bridge optimisation.\n");
	for (i=0;i<OPTIMISATION_NUM;i++)
	{
		printk("    %s : ",optimisation[i].type);
		if (bridge_mapping[pos+i].address==0) printk("Not supported.");
		else {
			pcibios_read_config_byte(
				bus, dev_fn, bridge_mapping[pos+i].address, &val);
			if ((val & bridge_mapping[pos+i].mask)==bridge_mapping[pos+i].value) 
			{
				printk("%s.",optimisation[i].on);
				if (turn_on==0) 
				{
				pcibios_write_config_byte(
					bus, dev_fn, bridge_mapping[pos+i].address,
					(val | bridge_mapping[pos+i].mask) -
					bridge_mapping[pos+i].value);
				printk("Changed! now %s.",optimisation[i].off);
				}
			} else {
				printk("%s.",optimisation[i].off);
				if (turn_on==1) 
				{
				pcibios_write_config_byte(
					bus, dev_fn, bridge_mapping[pos+i].address,
					(val & (0xff-bridge_mapping[pos+i].mask)) +
					bridge_mapping[pos+i].value);
				printk("Changed! now %s.",optimisation[i].on);
				}
			}
		}
		printk("\n");
	}
}

#endif

/* In future version in case we detect a PCI to PCi bridge, we will go
for a recursive device search*/

void probe_devices(unsigned char bus)
{
	unsigned long	res;
	unsigned char	dev_fn;

/* For a mysterious reason, my PC crash if I try to probe device 31 function 7  */
/* (i.e. dev_fn=0xff) It can be a bug in my BIOS, or I haven't understood all about */
/* PCI */


		for (dev_fn=0x0;dev_fn<0xff;dev_fn++) {
			pcibios_read_config_dword(
				bus, dev_fn, (unsigned char) PCI_CLASS_REVISION, &res);

/* First we won't try to talk to non_present chip */
/* Second, we get rid of non multi-function device that seems to be lazy  */
/* and not fully decode the function number */

			if ((res!=0xffffffff) &&
				(((dev_fn & 7) == 0) || multi_function(bus,dev_fn))) {
				add_pci_resource( bus, dev_fn);
			}
		}
}


void probe_pci(void)
{
	if (pcibios_present()==0) {
		printk("ProbePci PCI bios not detected.\n");
		return;
	}
	printk( "Probing PCI hardware.\n");
	probe_devices(0);
}


/*
 * Function to add a resource to the pci list...
 */
void add_pci_resource(unsigned char bus, unsigned char dev_fn)
{
	pci_resource_t* new_pci;
	pci_resource_t* temp;
	int vendor_id, device_id;
#ifdef CONFIG_PCI_OPTIMIZE
	unsigned char bridge_id;
#endif
	/*
	 * Verify if we know about this chip. If not, print Vendor & Device id
	 * + ask for report.
	 */
	vendor_id=vendor_decode(bus,dev_fn);
	device_id=device_decode(bus,dev_fn,vendor_id);
	if ((device_id & 0x10000)==0x10000)
	{
		printk("Unknown PCI device. PCI Vendor id=%x. PCI Device id=%x.\n",
			vendor_id & 0xffff,device_id & 0xffff);
		printk("PLEASE MAIL POTTER@CAO-VLSI.IBP.FR your hardware description and /proc/pci.\n");
		return;
	}
	/*
	 * If the PCI agent is a known bridge, then configure it.
	 */
#ifdef CONFIG_PCI_OPTIMIZE

	bridge_id=bridge_decode(device_id);
	if (bridge_id != 0xff)
	{
		burst_bridge(bus,dev_fn,bridge_id,1); /* Burst bridge */
	}
#endif
	/*
	 * Request and verify allocation of kernel RAM
	 */
	if(pci_index > PCI_LIST_SIZE-1)
	{
		printk("PCI resource list full.\n");
		return;
	}


	new_pci = &pci_table[pci_index];
	pci_index++;

	/*
	 * Enter the new node into the list....
	 *
	 */
	if(pci_list.next != NULL)
	{
		for(temp = pci_list.next; (temp->next); temp = temp->next)
			/* nothing */;

		temp->next = new_pci;
	}
	else
		pci_list.next = new_pci;

	/*
	 * Set the information for the node
	 */
	new_pci->next = NULL;
	new_pci->bus = bus;
	new_pci->dev_fn = dev_fn;

	return;
}


int get_pci_list(char* buf)
{
	int pr, length;
	pci_resource_t* temp = pci_list.next;
	struct	pci_class_type	pci_class[PCI_CLASS_NUM] = PCI_CLASS_TYPE;
	struct	pci_vendor_type	pci_vendor[PCI_VENDOR_NUM] = PCI_VENDOR_TYPE;
	struct	pci_device_type	pci_device[PCI_DEVICE_NUM] = PCI_DEVICE_TYPE;

	pr = sprintf(buf, "PCI devices found :\n"); 
	for (length = pr ; (temp) && (length<4000); temp = temp->next)
	{
		pr=vendor_decode(temp->bus,temp->dev_fn);

		length += sprintf(buf+length, "Bus %2d Device %3d Function %2d.\n",
			(int)temp->bus,
			(int)((temp->dev_fn & 0xf8) >> 3),
			(int) (temp->dev_fn & 7));

		length += sprintf(buf+length, "    %s : %s %s (rev %d). ",
			pci_class[class_decode(temp->bus, temp->dev_fn)].class_name,
			pci_vendor[pr].vendor_name,
			pci_device[device_decode(temp->bus, temp->dev_fn, pr)].device_name,
			revision_decode(temp->bus, temp->dev_fn));

		if (bist_probe(temp->bus, temp->dev_fn))
			length += sprintf(buf+length, "BIST capable. ");

		if ((pr = interrupt_decode(temp->bus, temp->dev_fn)) != 0)
			length += sprintf(buf+length, "8259's interrupt %d.", pr);

		length += sprintf(buf+length, "\n");
	}

	if (temp)
		length += sprintf(buf+length, "4K limit reached!\n");

	return length;
}


#endif

unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
{
	union bios32 *check;
	unsigned char sum;
	int i, length;

	/*
	 * Follow the standard procedure for locating the BIOS32 Service
	 * directory by scanning the permissible address range from
	 * 0xe0000 through 0xfffff for a valid BIOS32 structure.
	 *
	 * The PCI BIOS doesn't seem to work too well on many machines,
	 * so we disable this unless it's really needed (NCR SCSI driver)
	 */

	for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
		if (check->fields.signature != BIOS32_SIGNATURE)
			continue;
		length = check->fields.length * 16;
		if (!length)
			continue;
		sum = 0;
		for (i = 0; i < length ; ++i)
			sum += check->chars[i];
		if (sum != 0)
			continue;
		if (check->fields.revision != 0) {
			printk("bios32_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
				check->fields.revision, check);
			continue;
		}
		printk ("bios32_init : BIOS32 Service Directory structure at 0x%p\n", check);
		if (!bios32_entry) {
			bios32_indirect.address = bios32_entry = check->fields.entry;
			printk ("bios32_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
		} else {
			printk ("bios32_init : multiple entries, mail drew@colorado.edu\n");
			/*
			 * Jeremy Fitzhardinge reports at least one PCI BIOS
			 * with two different service directories, and as both
			 * worked for him, we'll just mention the fact, and
			 * not actually disallow it..
			 */
#if 0
			return memory_start;
#endif
		}
	}
#ifdef CONFIG_PCI
	if (bios32_entry) {
		memory_start = pcibios_init (memory_start, memory_end);
		probe_pci();
	}
#endif
	return memory_start;
}



