/*
|
/*
|
* bios32.c - BIOS32, PCI BIOS functions.
|
* bios32.c - BIOS32, PCI BIOS functions.
|
*
|
*
|
* $Id: bios32.c,v 1.1 2005-12-20 09:42:23 jcastillo Exp $
|
* $Id: bios32.c,v 1.1 2005-12-20 09:42:23 jcastillo Exp $
|
*
|
*
|
* Sponsored by
|
* Sponsored by
|
* iX Multiuser Multitasking Magazine
|
* iX Multiuser Multitasking Magazine
|
* Hannover, Germany
|
* Hannover, Germany
|
* hm@ix.de
|
* hm@ix.de
|
*
|
*
|
* Copyright 1993, 1994 Drew Eckhardt
|
* Copyright 1993, 1994 Drew Eckhardt
|
* Visionary Computing
|
* Visionary Computing
|
* (Unix and Linux consulting and custom programming)
|
* (Unix and Linux consulting and custom programming)
|
* Drew@Colorado.EDU
|
* Drew@Colorado.EDU
|
* +1 (303) 786-7975
|
* +1 (303) 786-7975
|
*
|
*
|
* For more information, please consult
|
* For more information, please consult
|
*
|
*
|
* PCI BIOS Specification Revision
|
* PCI BIOS Specification Revision
|
* PCI Local Bus Specification
|
* PCI Local Bus Specification
|
* PCI System Design Guide
|
* PCI System Design Guide
|
*
|
*
|
* PCI Special Interest Group
|
* PCI Special Interest Group
|
* M/S HF3-15A
|
* M/S HF3-15A
|
* 5200 N.E. Elam Young Parkway
|
* 5200 N.E. Elam Young Parkway
|
* Hillsboro, Oregon 97124-6497
|
* Hillsboro, Oregon 97124-6497
|
* +1 (503) 696-2000
|
* +1 (503) 696-2000
|
* +1 (800) 433-5177
|
* +1 (800) 433-5177
|
*
|
*
|
* Manuals are $25 each or $50 for all three, plus $7 shipping
|
* Manuals are $25 each or $50 for all three, plus $7 shipping
|
* within the United States, $35 abroad.
|
* within the United States, $35 abroad.
|
*
|
*
|
*
|
*
|
* CHANGELOG :
|
* CHANGELOG :
|
* Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION
|
* 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.
|
* 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
|
* Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic
|
* Potter, potter@cao-vlsi.ibp.fr
|
* Potter, potter@cao-vlsi.ibp.fr
|
*
|
*
|
* Jan 10, 1995 : Modified to store the information about configured pci
|
* Jan 10, 1995 : Modified to store the information about configured pci
|
* devices into a list, which can be accessed via /proc/pci by
|
* devices into a list, which can be accessed via /proc/pci by
|
* Curtis Varner, cvarner@cs.ucr.edu
|
* Curtis Varner, cvarner@cs.ucr.edu
|
*
|
*
|
* Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter.
|
* Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter.
|
* Alpha version. Intel & UMC chipset support only.
|
* Alpha version. Intel & UMC chipset support only.
|
*
|
*
|
* Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
|
* Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
|
* moved to drivers/pci/pci.c.
|
* moved to drivers/pci/pci.c.
|
*
|
*
|
* Dec 7, 1996 : Added support for direct configuration access of boards
|
* Dec 7, 1996 : Added support for direct configuration access of boards
|
* with Intel compatible access schemes (tsbogend@alpha.franken.de)
|
* with Intel compatible access schemes (tsbogend@alpha.franken.de)
|
*
|
*
|
* Feb 3, 1997 : Set internal functions to static, save/restore flags
|
* Feb 3, 1997 : Set internal functions to static, save/restore flags
|
* avoid dead locks reading broken PCI BIOS, werner@suse.de
|
* avoid dead locks reading broken PCI BIOS, werner@suse.de
|
*
|
*
|
* Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS
|
* Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS
|
* (mj@atrey.karlin.mff.cuni.cz)
|
* (mj@atrey.karlin.mff.cuni.cz)
|
*
|
*
|
* May 7, 1997 : Added some missing cli()'s. [mj]
|
* May 7, 1997 : Added some missing cli()'s. [mj]
|
*
|
*
|
* Jun 20, 1997 : Corrected problems in "conf1" type accesses.
|
* Jun 20, 1997 : Corrected problems in "conf1" type accesses.
|
* (paubert@iram.es)
|
* (paubert@iram.es)
|
*/
|
*/
|
|
|
#include <linux/config.h>
|
#include <linux/config.h>
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/bios32.h>
|
#include <linux/bios32.h>
|
#include <linux/pci.h>
|
#include <linux/pci.h>
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
#include <asm/system.h>
|
#include <asm/system.h>
|
#include <asm/io.h>
|
#include <asm/io.h>
|
|
|
#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
|
#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
|
#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
|
#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
|
#define PCIBIOS_FIND_PCI_DEVICE 0xb102
|
#define PCIBIOS_FIND_PCI_DEVICE 0xb102
|
#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103
|
#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103
|
#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106
|
#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106
|
#define PCIBIOS_READ_CONFIG_BYTE 0xb108
|
#define PCIBIOS_READ_CONFIG_BYTE 0xb108
|
#define PCIBIOS_READ_CONFIG_WORD 0xb109
|
#define PCIBIOS_READ_CONFIG_WORD 0xb109
|
#define PCIBIOS_READ_CONFIG_DWORD 0xb10a
|
#define PCIBIOS_READ_CONFIG_DWORD 0xb10a
|
#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b
|
#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b
|
#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
|
#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
|
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
|
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
|
|
|
|
|
/* BIOS32 signature: "_32_" */
|
/* BIOS32 signature: "_32_" */
|
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
|
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
|
|
|
/* PCI signature: "PCI " */
|
/* PCI signature: "PCI " */
|
#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24))
|
#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24))
|
|
|
/* PCI service signature: "$PCI" */
|
/* PCI service signature: "$PCI" */
|
#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24))
|
#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24))
|
|
|
/*
|
/*
|
* This is the standard structure used to identify the entry point
|
* This is the standard structure used to identify the entry point
|
* to the BIOS32 Service Directory, as documented in
|
* to the BIOS32 Service Directory, as documented in
|
* Standard BIOS 32-bit Service Directory Proposal
|
* Standard BIOS 32-bit Service Directory Proposal
|
* Revision 0.4 May 24, 1993
|
* Revision 0.4 May 24, 1993
|
* Phoenix Technologies Ltd.
|
* Phoenix Technologies Ltd.
|
* Norwood, MA
|
* Norwood, MA
|
* and the PCI BIOS specification.
|
* and the PCI BIOS specification.
|
*/
|
*/
|
|
|
union bios32 {
|
union bios32 {
|
struct {
|
struct {
|
unsigned long signature; /* _32_ */
|
unsigned long signature; /* _32_ */
|
unsigned long entry; /* 32 bit physical address */
|
unsigned long entry; /* 32 bit physical address */
|
unsigned char revision; /* Revision level, 0 */
|
unsigned char revision; /* Revision level, 0 */
|
unsigned char length; /* Length in paragraphs should be 01 */
|
unsigned char length; /* Length in paragraphs should be 01 */
|
unsigned char checksum; /* All bytes must add up to zero */
|
unsigned char checksum; /* All bytes must add up to zero */
|
unsigned char reserved[5]; /* Must be zero */
|
unsigned char reserved[5]; /* Must be zero */
|
} fields;
|
} fields;
|
char chars[16];
|
char chars[16];
|
};
|
};
|
|
|
#ifdef CONFIG_PCI
|
#ifdef CONFIG_PCI
|
/*
|
/*
|
* Physical address of the service directory. I don't know if we're
|
* 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
|
* allowed to have more than one of these or not, so just in case
|
* we'll make pcibios_present() take a memory start parameter and store
|
* we'll make pcibios_present() take a memory start parameter and store
|
* the array there.
|
* the array there.
|
*/
|
*/
|
|
|
static unsigned long bios32_entry = 0;
|
static unsigned long bios32_entry = 0;
|
static struct {
|
static struct {
|
unsigned long address;
|
unsigned long address;
|
unsigned short segment;
|
unsigned short segment;
|
} bios32_indirect = { 0, KERNEL_CS };
|
} bios32_indirect = { 0, KERNEL_CS };
|
|
|
|
|
/*
|
/*
|
* function table for accessing PCI configuration space
|
* function table for accessing PCI configuration space
|
*/
|
*/
|
struct pci_access {
|
struct pci_access {
|
int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *);
|
int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *);
|
int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *);
|
int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *);
|
int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *);
|
int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *);
|
int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *);
|
int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *);
|
int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *);
|
int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *);
|
int (*write_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char);
|
int (*write_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char);
|
int (*write_config_word)(unsigned char, unsigned char, unsigned char, unsigned short);
|
int (*write_config_word)(unsigned char, unsigned char, unsigned char, unsigned short);
|
int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int);
|
int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int);
|
};
|
};
|
|
|
/*
|
/*
|
* pointer to selected PCI access function table
|
* pointer to selected PCI access function table
|
*/
|
*/
|
static struct pci_access *access_pci = NULL;
|
static struct pci_access *access_pci = NULL;
|
|
|
|
|
|
|
/*
|
/*
|
* Returns the entry point for the given service, NULL on error
|
* Returns the entry point for the given service, NULL on error
|
*/
|
*/
|
|
|
static unsigned long bios32_service(unsigned long service)
|
static unsigned long bios32_service(unsigned long service)
|
{
|
{
|
unsigned char return_code; /* %al */
|
unsigned char return_code; /* %al */
|
unsigned long address; /* %ebx */
|
unsigned long address; /* %ebx */
|
unsigned long length; /* %ecx */
|
unsigned long length; /* %ecx */
|
unsigned long entry; /* %edx */
|
unsigned long entry; /* %edx */
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%edi)"
|
__asm__("lcall (%%edi)"
|
: "=a" (return_code),
|
: "=a" (return_code),
|
"=b" (address),
|
"=b" (address),
|
"=c" (length),
|
"=c" (length),
|
"=d" (entry)
|
"=d" (entry)
|
: "0" (service),
|
: "0" (service),
|
"1" (0),
|
"1" (0),
|
"D" (&bios32_indirect));
|
"D" (&bios32_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
|
|
switch (return_code) {
|
switch (return_code) {
|
case 0:
|
case 0:
|
return address + entry;
|
return address + entry;
|
case 0x80: /* Not present */
|
case 0x80: /* Not present */
|
printk("bios32_service(0x%lx) : not present\n", service);
|
printk("bios32_service(0x%lx) : not present\n", service);
|
return 0;
|
return 0;
|
default: /* Shouldn't happen */
|
default: /* Shouldn't happen */
|
printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n",
|
printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n",
|
service, return_code);
|
service, return_code);
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
static long pcibios_entry = 0;
|
static long pcibios_entry = 0;
|
static struct {
|
static struct {
|
unsigned long address;
|
unsigned long address;
|
unsigned short segment;
|
unsigned short segment;
|
} pci_indirect = { 0, KERNEL_CS };
|
} pci_indirect = { 0, KERNEL_CS };
|
|
|
|
|
static int check_pcibios(void)
|
static int check_pcibios(void)
|
{
|
{
|
unsigned long signature;
|
unsigned long signature;
|
unsigned char present_status;
|
unsigned char present_status;
|
unsigned char major_revision;
|
unsigned char major_revision;
|
unsigned char minor_revision;
|
unsigned char minor_revision;
|
unsigned long flags;
|
unsigned long flags;
|
int pack;
|
int pack;
|
|
|
if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
|
if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
|
pci_indirect.address = pcibios_entry;
|
pci_indirect.address = pcibios_entry;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%edi)\n\t"
|
__asm__("lcall (%%edi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:\tshl $8, %%eax\n\t"
|
"1:\tshl $8, %%eax\n\t"
|
"movw %%bx, %%ax"
|
"movw %%bx, %%ax"
|
: "=d" (signature),
|
: "=d" (signature),
|
"=a" (pack)
|
"=a" (pack)
|
: "1" (PCIBIOS_PCI_BIOS_PRESENT),
|
: "1" (PCIBIOS_PCI_BIOS_PRESENT),
|
"D" (&pci_indirect)
|
"D" (&pci_indirect)
|
: "bx", "cx");
|
: "bx", "cx");
|
restore_flags(flags);
|
restore_flags(flags);
|
|
|
present_status = (pack >> 16) & 0xff;
|
present_status = (pack >> 16) & 0xff;
|
major_revision = (pack >> 8) & 0xff;
|
major_revision = (pack >> 8) & 0xff;
|
minor_revision = pack & 0xff;
|
minor_revision = pack & 0xff;
|
if (present_status || (signature != PCI_SIGNATURE)) {
|
if (present_status || (signature != PCI_SIGNATURE)) {
|
printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n"
|
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"
|
" 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",
|
" and signature of 0x%08lx (%c%c%c%c). mail drew@Colorado.EDU\n",
|
(signature == PCI_SIGNATURE) ? "WARNING" : "ERROR",
|
(signature == PCI_SIGNATURE) ? "WARNING" : "ERROR",
|
present_status, signature,
|
present_status, signature,
|
(char) (signature >> 0), (char) (signature >> 8),
|
(char) (signature >> 0), (char) (signature >> 8),
|
(char) (signature >> 16), (char) (signature >> 24));
|
(char) (signature >> 16), (char) (signature >> 24));
|
|
|
if (signature != PCI_SIGNATURE)
|
if (signature != PCI_SIGNATURE)
|
pcibios_entry = 0;
|
pcibios_entry = 0;
|
}
|
}
|
if (pcibios_entry) {
|
if (pcibios_entry) {
|
printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
|
printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
|
major_revision, minor_revision, pcibios_entry);
|
major_revision, minor_revision, pcibios_entry);
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
static int pci_bios_find_class (unsigned int class_code, unsigned short index,
|
static int pci_bios_find_class (unsigned int class_code, unsigned short index,
|
unsigned char *bus, unsigned char *device_fn)
|
unsigned char *bus, unsigned char *device_fn)
|
{
|
{
|
unsigned long bx;
|
unsigned long bx;
|
unsigned long ret;
|
unsigned long ret;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__ ("lcall (%%edi)\n\t"
|
__asm__ ("lcall (%%edi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=b" (bx),
|
: "=b" (bx),
|
"=a" (ret)
|
"=a" (ret)
|
: "1" (PCIBIOS_FIND_PCI_CLASS_CODE),
|
: "1" (PCIBIOS_FIND_PCI_CLASS_CODE),
|
"c" (class_code),
|
"c" (class_code),
|
"S" ((int) index),
|
"S" ((int) index),
|
"D" (&pci_indirect));
|
"D" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
*bus = (bx >> 8) & 0xff;
|
*bus = (bx >> 8) & 0xff;
|
*device_fn = bx & 0xff;
|
*device_fn = bx & 0xff;
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
|
|
static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
|
static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
|
unsigned short index, unsigned char *bus, unsigned char *device_fn)
|
unsigned short index, unsigned char *bus, unsigned char *device_fn)
|
{
|
{
|
unsigned short bx;
|
unsigned short bx;
|
unsigned short ret;
|
unsigned short ret;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%edi)\n\t"
|
__asm__("lcall (%%edi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=b" (bx),
|
: "=b" (bx),
|
"=a" (ret)
|
"=a" (ret)
|
: "1" (PCIBIOS_FIND_PCI_DEVICE),
|
: "1" (PCIBIOS_FIND_PCI_DEVICE),
|
"c" (device_id),
|
"c" (device_id),
|
"d" (vendor),
|
"d" (vendor),
|
"S" ((int) index),
|
"S" ((int) index),
|
"D" (&pci_indirect));
|
"D" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
*bus = (bx >> 8) & 0xff;
|
*bus = (bx >> 8) & 0xff;
|
*device_fn = bx & 0xff;
|
*device_fn = bx & 0xff;
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
static int pci_bios_read_config_byte(unsigned char bus,
|
static int pci_bios_read_config_byte(unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned char *value)
|
unsigned char device_fn, unsigned char where, unsigned char *value)
|
{
|
{
|
unsigned long ret;
|
unsigned long ret;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%esi)\n\t"
|
__asm__("lcall (%%esi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=c" (*value),
|
: "=c" (*value),
|
"=a" (ret)
|
"=a" (ret)
|
: "1" (PCIBIOS_READ_CONFIG_BYTE),
|
: "1" (PCIBIOS_READ_CONFIG_BYTE),
|
"b" (bx),
|
"b" (bx),
|
"D" ((long) where),
|
"D" ((long) where),
|
"S" (&pci_indirect));
|
"S" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
static int pci_bios_read_config_word (unsigned char bus,
|
static int pci_bios_read_config_word (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned short *value)
|
unsigned char device_fn, unsigned char where, unsigned short *value)
|
{
|
{
|
unsigned long ret;
|
unsigned long ret;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%esi)\n\t"
|
__asm__("lcall (%%esi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=c" (*value),
|
: "=c" (*value),
|
"=a" (ret)
|
"=a" (ret)
|
: "1" (PCIBIOS_READ_CONFIG_WORD),
|
: "1" (PCIBIOS_READ_CONFIG_WORD),
|
"b" (bx),
|
"b" (bx),
|
"D" ((long) where),
|
"D" ((long) where),
|
"S" (&pci_indirect));
|
"S" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
static int pci_bios_read_config_dword (unsigned char bus,
|
static int pci_bios_read_config_dword (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned int *value)
|
unsigned char device_fn, unsigned char where, unsigned int *value)
|
{
|
{
|
unsigned long ret;
|
unsigned long ret;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%esi)\n\t"
|
__asm__("lcall (%%esi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=c" (*value),
|
: "=c" (*value),
|
"=a" (ret)
|
"=a" (ret)
|
: "1" (PCIBIOS_READ_CONFIG_DWORD),
|
: "1" (PCIBIOS_READ_CONFIG_DWORD),
|
"b" (bx),
|
"b" (bx),
|
"D" ((long) where),
|
"D" ((long) where),
|
"S" (&pci_indirect));
|
"S" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
static int pci_bios_write_config_byte (unsigned char bus,
|
static int pci_bios_write_config_byte (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned char value)
|
unsigned char device_fn, unsigned char where, unsigned char value)
|
{
|
{
|
unsigned long ret;
|
unsigned long ret;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%esi)\n\t"
|
__asm__("lcall (%%esi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=a" (ret)
|
: "=a" (ret)
|
: "0" (PCIBIOS_WRITE_CONFIG_BYTE),
|
: "0" (PCIBIOS_WRITE_CONFIG_BYTE),
|
"c" (value),
|
"c" (value),
|
"b" (bx),
|
"b" (bx),
|
"D" ((long) where),
|
"D" ((long) where),
|
"S" (&pci_indirect));
|
"S" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
static int pci_bios_write_config_word (unsigned char bus,
|
static int pci_bios_write_config_word (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned short value)
|
unsigned char device_fn, unsigned char where, unsigned short value)
|
{
|
{
|
unsigned long ret;
|
unsigned long ret;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%esi)\n\t"
|
__asm__("lcall (%%esi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=a" (ret)
|
: "=a" (ret)
|
: "0" (PCIBIOS_WRITE_CONFIG_WORD),
|
: "0" (PCIBIOS_WRITE_CONFIG_WORD),
|
"c" (value),
|
"c" (value),
|
"b" (bx),
|
"b" (bx),
|
"D" ((long) where),
|
"D" ((long) where),
|
"S" (&pci_indirect));
|
"S" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
static int pci_bios_write_config_dword (unsigned char bus,
|
static int pci_bios_write_config_dword (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned int value)
|
unsigned char device_fn, unsigned char where, unsigned int value)
|
{
|
{
|
unsigned long ret;
|
unsigned long ret;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long bx = (bus << 8) | device_fn;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
__asm__("lcall (%%esi)\n\t"
|
__asm__("lcall (%%esi)\n\t"
|
"jc 1f\n\t"
|
"jc 1f\n\t"
|
"xor %%ah, %%ah\n"
|
"xor %%ah, %%ah\n"
|
"1:"
|
"1:"
|
: "=a" (ret)
|
: "=a" (ret)
|
: "0" (PCIBIOS_WRITE_CONFIG_DWORD),
|
: "0" (PCIBIOS_WRITE_CONFIG_DWORD),
|
"c" (value),
|
"c" (value),
|
"b" (bx),
|
"b" (bx),
|
"D" ((long) where),
|
"D" ((long) where),
|
"S" (&pci_indirect));
|
"S" (&pci_indirect));
|
restore_flags(flags);
|
restore_flags(flags);
|
return (int) (ret & 0xff00) >> 8;
|
return (int) (ret & 0xff00) >> 8;
|
}
|
}
|
|
|
/*
|
/*
|
* function table for BIOS32 access
|
* function table for BIOS32 access
|
*/
|
*/
|
static struct pci_access pci_bios_access = {
|
static struct pci_access pci_bios_access = {
|
pci_bios_find_device,
|
pci_bios_find_device,
|
pci_bios_find_class,
|
pci_bios_find_class,
|
pci_bios_read_config_byte,
|
pci_bios_read_config_byte,
|
pci_bios_read_config_word,
|
pci_bios_read_config_word,
|
pci_bios_read_config_dword,
|
pci_bios_read_config_dword,
|
pci_bios_write_config_byte,
|
pci_bios_write_config_byte,
|
pci_bios_write_config_word,
|
pci_bios_write_config_word,
|
pci_bios_write_config_dword
|
pci_bios_write_config_dword
|
};
|
};
|
|
|
|
|
|
|
/*
|
/*
|
* Given the vendor and device ids, find the n'th instance of that device
|
* Given the vendor and device ids, find the n'th instance of that device
|
* in the system.
|
* in the system.
|
*/
|
*/
|
static int pci_direct_find_device (unsigned short vendor, unsigned short device_id,
|
static int pci_direct_find_device (unsigned short vendor, unsigned short device_id,
|
unsigned short index, unsigned char *bus,
|
unsigned short index, unsigned char *bus,
|
unsigned char *devfn)
|
unsigned char *devfn)
|
{
|
{
|
unsigned int curr = 0;
|
unsigned int curr = 0;
|
struct pci_dev *dev;
|
struct pci_dev *dev;
|
|
|
for (dev = pci_devices; dev; dev = dev->next) {
|
for (dev = pci_devices; dev; dev = dev->next) {
|
if (dev->vendor == vendor && dev->device == device_id) {
|
if (dev->vendor == vendor && dev->device == device_id) {
|
if (curr == index) {
|
if (curr == index) {
|
*devfn = dev->devfn;
|
*devfn = dev->devfn;
|
*bus = dev->bus->number;
|
*bus = dev->bus->number;
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
++curr;
|
++curr;
|
}
|
}
|
}
|
}
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* Given the class, find the n'th instance of that device
|
* Given the class, find the n'th instance of that device
|
* in the system.
|
* in the system.
|
*/
|
*/
|
static int pci_direct_find_class (unsigned int class_code, unsigned short index,
|
static int pci_direct_find_class (unsigned int class_code, unsigned short index,
|
unsigned char *bus, unsigned char *devfn)
|
unsigned char *bus, unsigned char *devfn)
|
{
|
{
|
unsigned int curr = 0;
|
unsigned int curr = 0;
|
struct pci_dev *dev;
|
struct pci_dev *dev;
|
|
|
for (dev = pci_devices; dev; dev = dev->next) {
|
for (dev = pci_devices; dev; dev = dev->next) {
|
if (dev->class == class_code) {
|
if (dev->class == class_code) {
|
if (curr == index) {
|
if (curr == index) {
|
*devfn = dev->devfn;
|
*devfn = dev->devfn;
|
*bus = dev->bus->number;
|
*bus = dev->bus->number;
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
++curr;
|
++curr;
|
}
|
}
|
}
|
}
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
}
|
}
|
|
|
/*
|
/*
|
* Functions for accessing PCI configuration space with type 1 accesses
|
* Functions for accessing PCI configuration space with type 1 accesses
|
*/
|
*/
|
#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
|
#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
|
|
|
static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
|
static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned char *value)
|
unsigned char where, unsigned char *value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
*value = inb(0xCFC + (where&3));
|
*value = inb(0xCFC + (where&3));
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf1_read_config_word (unsigned char bus,
|
static int pci_conf1_read_config_word (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned short *value)
|
unsigned char device_fn, unsigned char where, unsigned short *value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
|
if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
*value = inw(0xCFC + (where&2));
|
*value = inw(0xCFC + (where&2));
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn,
|
static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned int *value)
|
unsigned char where, unsigned int *value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
|
if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
*value = inl(0xCFC);
|
*value = inl(0xCFC);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn,
|
static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned char value)
|
unsigned char where, unsigned char value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outb(value, 0xCFC + (where&3));
|
outb(value, 0xCFC + (where&3));
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn,
|
static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned short value)
|
unsigned char where, unsigned short value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
|
if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outw(value, 0xCFC + (where&2));
|
outw(value, 0xCFC + (where&2));
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn,
|
static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned int value)
|
unsigned char where, unsigned int value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
|
if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
|
outl(value, 0xCFC);
|
outl(value, 0xCFC);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
#undef CONFIG_CMD
|
#undef CONFIG_CMD
|
|
|
/*
|
/*
|
* functiontable for type 1
|
* functiontable for type 1
|
*/
|
*/
|
static struct pci_access pci_direct_conf1 = {
|
static struct pci_access pci_direct_conf1 = {
|
pci_direct_find_device,
|
pci_direct_find_device,
|
pci_direct_find_class,
|
pci_direct_find_class,
|
pci_conf1_read_config_byte,
|
pci_conf1_read_config_byte,
|
pci_conf1_read_config_word,
|
pci_conf1_read_config_word,
|
pci_conf1_read_config_dword,
|
pci_conf1_read_config_dword,
|
pci_conf1_write_config_byte,
|
pci_conf1_write_config_byte,
|
pci_conf1_write_config_word,
|
pci_conf1_write_config_word,
|
pci_conf1_write_config_dword
|
pci_conf1_write_config_dword
|
};
|
};
|
|
|
/*
|
/*
|
* Functions for accessing PCI configuration space with type 2 accesses
|
* Functions for accessing PCI configuration space with type 2 accesses
|
*/
|
*/
|
#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where)
|
#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where)
|
#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0)
|
#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0)
|
|
|
static int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn,
|
static int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned char *value)
|
unsigned char where, unsigned char *value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (device_fn & 0x80)
|
if (device_fn & 0x80)
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outb (FUNC(device_fn), 0xCF8);
|
outb (FUNC(device_fn), 0xCF8);
|
outb (bus, 0xCFA);
|
outb (bus, 0xCFA);
|
*value = inb(IOADDR(device_fn,where));
|
*value = inb(IOADDR(device_fn,where));
|
outb (0, 0xCF8);
|
outb (0, 0xCF8);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_fn,
|
static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned short *value)
|
unsigned char where, unsigned short *value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (device_fn & 0x80)
|
if (device_fn & 0x80)
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outb (FUNC(device_fn), 0xCF8);
|
outb (FUNC(device_fn), 0xCF8);
|
outb (bus, 0xCFA);
|
outb (bus, 0xCFA);
|
*value = inw(IOADDR(device_fn,where));
|
*value = inw(IOADDR(device_fn,where));
|
outb (0, 0xCF8);
|
outb (0, 0xCF8);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_fn,
|
static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned int *value)
|
unsigned char where, unsigned int *value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (device_fn & 0x80)
|
if (device_fn & 0x80)
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outb (FUNC(device_fn), 0xCF8);
|
outb (FUNC(device_fn), 0xCF8);
|
outb (bus, 0xCFA);
|
outb (bus, 0xCFA);
|
*value = inl (IOADDR(device_fn,where));
|
*value = inl (IOADDR(device_fn,where));
|
outb (0, 0xCF8);
|
outb (0, 0xCF8);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_fn,
|
static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned char value)
|
unsigned char where, unsigned char value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outb (FUNC(device_fn), 0xCF8);
|
outb (FUNC(device_fn), 0xCF8);
|
outb (bus, 0xCFA);
|
outb (bus, 0xCFA);
|
outb (value, IOADDR(device_fn,where));
|
outb (value, IOADDR(device_fn,where));
|
outb (0, 0xCF8);
|
outb (0, 0xCF8);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_fn,
|
static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned short value)
|
unsigned char where, unsigned short value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outb (FUNC(device_fn), 0xCF8);
|
outb (FUNC(device_fn), 0xCF8);
|
outb (bus, 0xCFA);
|
outb (bus, 0xCFA);
|
outw (value, IOADDR(device_fn,where));
|
outw (value, IOADDR(device_fn,where));
|
outb (0, 0xCF8);
|
outb (0, 0xCF8);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device_fn,
|
static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device_fn,
|
unsigned char where, unsigned int value)
|
unsigned char where, unsigned int value)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
outb (FUNC(device_fn), 0xCF8);
|
outb (FUNC(device_fn), 0xCF8);
|
outb (bus, 0xCFA);
|
outb (bus, 0xCFA);
|
outl (value, IOADDR(device_fn,where));
|
outl (value, IOADDR(device_fn,where));
|
outb (0, 0xCF8);
|
outb (0, 0xCF8);
|
restore_flags(flags);
|
restore_flags(flags);
|
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
}
|
}
|
|
|
#undef IOADDR
|
#undef IOADDR
|
#undef FUNC
|
#undef FUNC
|
|
|
/*
|
/*
|
* functiontable for type 2
|
* functiontable for type 2
|
*/
|
*/
|
static struct pci_access pci_direct_conf2 = {
|
static struct pci_access pci_direct_conf2 = {
|
pci_direct_find_device,
|
pci_direct_find_device,
|
pci_direct_find_class,
|
pci_direct_find_class,
|
pci_conf2_read_config_byte,
|
pci_conf2_read_config_byte,
|
pci_conf2_read_config_word,
|
pci_conf2_read_config_word,
|
pci_conf2_read_config_dword,
|
pci_conf2_read_config_dword,
|
pci_conf2_write_config_byte,
|
pci_conf2_write_config_byte,
|
pci_conf2_write_config_word,
|
pci_conf2_write_config_word,
|
pci_conf2_write_config_dword
|
pci_conf2_write_config_dword
|
};
|
};
|
|
|
|
|
static struct pci_access *check_direct_pci(void)
|
static struct pci_access *check_direct_pci(void)
|
{
|
{
|
unsigned int tmp;
|
unsigned int tmp;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
save_flags(flags); cli();
|
save_flags(flags); cli();
|
|
|
/*
|
/*
|
* check if configuration type 1 works
|
* check if configuration type 1 works
|
*/
|
*/
|
outb (0x01, 0xCFB);
|
outb (0x01, 0xCFB);
|
tmp = inl (0xCF8);
|
tmp = inl (0xCF8);
|
outl (0x80000000, 0xCF8);
|
outl (0x80000000, 0xCF8);
|
if (inl (0xCF8) == 0x80000000) {
|
if (inl (0xCF8) == 0x80000000) {
|
outl (tmp, 0xCF8);
|
outl (tmp, 0xCF8);
|
restore_flags(flags);
|
restore_flags(flags);
|
printk("pcibios_init: Using configuration type 1\n");
|
printk("pcibios_init: Using configuration type 1\n");
|
return &pci_direct_conf1;
|
return &pci_direct_conf1;
|
}
|
}
|
outl (tmp, 0xCF8);
|
outl (tmp, 0xCF8);
|
|
|
/*
|
/*
|
* check if configuration type 2 works
|
* check if configuration type 2 works
|
*/
|
*/
|
outb (0x00, 0xCFB);
|
outb (0x00, 0xCFB);
|
outb (0x00, 0xCF8);
|
outb (0x00, 0xCF8);
|
outb (0x00, 0xCFA);
|
outb (0x00, 0xCFA);
|
if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
|
if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
|
restore_flags(flags);
|
restore_flags(flags);
|
printk("pcibios_init: Using configuration type 2\n");
|
printk("pcibios_init: Using configuration type 2\n");
|
return &pci_direct_conf2;
|
return &pci_direct_conf2;
|
}
|
}
|
restore_flags(flags);
|
restore_flags(flags);
|
printk("pcibios_init: Not supported chipset for direct PCI access !\n");
|
printk("pcibios_init: Not supported chipset for direct PCI access !\n");
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* access defined pcibios functions via
|
* access defined pcibios functions via
|
* the function table
|
* the function table
|
*/
|
*/
|
|
|
int pcibios_present(void)
|
int pcibios_present(void)
|
{
|
{
|
return access_pci ? 1 : 0;
|
return access_pci ? 1 : 0;
|
}
|
}
|
|
|
int pcibios_find_class (unsigned int class_code, unsigned short index,
|
int pcibios_find_class (unsigned int class_code, unsigned short index,
|
unsigned char *bus, unsigned char *device_fn)
|
unsigned char *bus, unsigned char *device_fn)
|
{
|
{
|
if (access_pci && access_pci->find_class)
|
if (access_pci && access_pci->find_class)
|
return access_pci->find_class(class_code, index, bus, device_fn);
|
return access_pci->find_class(class_code, index, bus, device_fn);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
int pcibios_find_device (unsigned short vendor, unsigned short device_id,
|
int pcibios_find_device (unsigned short vendor, unsigned short device_id,
|
unsigned short index, unsigned char *bus, unsigned char *device_fn)
|
unsigned short index, unsigned char *bus, unsigned char *device_fn)
|
{
|
{
|
if (access_pci && access_pci->find_device)
|
if (access_pci && access_pci->find_device)
|
return access_pci->find_device(vendor, device_id, index, bus, device_fn);
|
return access_pci->find_device(vendor, device_id, index, bus, device_fn);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
int pcibios_read_config_byte (unsigned char bus,
|
int pcibios_read_config_byte (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned char *value)
|
unsigned char device_fn, unsigned char where, unsigned char *value)
|
{
|
{
|
if (access_pci && access_pci->read_config_byte)
|
if (access_pci && access_pci->read_config_byte)
|
return access_pci->read_config_byte(bus, device_fn, where, value);
|
return access_pci->read_config_byte(bus, device_fn, where, value);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
int pcibios_read_config_word (unsigned char bus,
|
int pcibios_read_config_word (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned short *value)
|
unsigned char device_fn, unsigned char where, unsigned short *value)
|
{
|
{
|
if (access_pci && access_pci->read_config_word)
|
if (access_pci && access_pci->read_config_word)
|
return access_pci->read_config_word(bus, device_fn, where, value);
|
return access_pci->read_config_word(bus, device_fn, where, value);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
int pcibios_read_config_dword (unsigned char bus,
|
int pcibios_read_config_dword (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned int *value)
|
unsigned char device_fn, unsigned char where, unsigned int *value)
|
{
|
{
|
if (access_pci && access_pci->read_config_dword)
|
if (access_pci && access_pci->read_config_dword)
|
return access_pci->read_config_dword(bus, device_fn, where, value);
|
return access_pci->read_config_dword(bus, device_fn, where, value);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
int pcibios_write_config_byte (unsigned char bus,
|
int pcibios_write_config_byte (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned char value)
|
unsigned char device_fn, unsigned char where, unsigned char value)
|
{
|
{
|
if (access_pci && access_pci->write_config_byte)
|
if (access_pci && access_pci->write_config_byte)
|
return access_pci->write_config_byte(bus, device_fn, where, value);
|
return access_pci->write_config_byte(bus, device_fn, where, value);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
int pcibios_write_config_word (unsigned char bus,
|
int pcibios_write_config_word (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned short value)
|
unsigned char device_fn, unsigned char where, unsigned short value)
|
{
|
{
|
if (access_pci && access_pci->write_config_word)
|
if (access_pci && access_pci->write_config_word)
|
return access_pci->write_config_word(bus, device_fn, where, value);
|
return access_pci->write_config_word(bus, device_fn, where, value);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
int pcibios_write_config_dword (unsigned char bus,
|
int pcibios_write_config_dword (unsigned char bus,
|
unsigned char device_fn, unsigned char where, unsigned int value)
|
unsigned char device_fn, unsigned char where, unsigned int value)
|
{
|
{
|
if (access_pci && access_pci->write_config_dword)
|
if (access_pci && access_pci->write_config_dword)
|
return access_pci->write_config_dword(bus, device_fn, where, value);
|
return access_pci->write_config_dword(bus, device_fn, where, value);
|
|
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
}
|
}
|
|
|
const char *pcibios_strerror (int error)
|
const char *pcibios_strerror (int error)
|
{
|
{
|
static char buf[80];
|
static char buf[80];
|
|
|
switch (error) {
|
switch (error) {
|
case PCIBIOS_SUCCESSFUL:
|
case PCIBIOS_SUCCESSFUL:
|
return "SUCCESSFUL";
|
return "SUCCESSFUL";
|
|
|
case PCIBIOS_FUNC_NOT_SUPPORTED:
|
case PCIBIOS_FUNC_NOT_SUPPORTED:
|
return "FUNC_NOT_SUPPORTED";
|
return "FUNC_NOT_SUPPORTED";
|
|
|
case PCIBIOS_BAD_VENDOR_ID:
|
case PCIBIOS_BAD_VENDOR_ID:
|
return "SUCCESSFUL";
|
return "SUCCESSFUL";
|
|
|
case PCIBIOS_DEVICE_NOT_FOUND:
|
case PCIBIOS_DEVICE_NOT_FOUND:
|
return "DEVICE_NOT_FOUND";
|
return "DEVICE_NOT_FOUND";
|
|
|
case PCIBIOS_BAD_REGISTER_NUMBER:
|
case PCIBIOS_BAD_REGISTER_NUMBER:
|
return "BAD_REGISTER_NUMBER";
|
return "BAD_REGISTER_NUMBER";
|
|
|
case PCIBIOS_SET_FAILED:
|
case PCIBIOS_SET_FAILED:
|
return "SET_FAILED";
|
return "SET_FAILED";
|
|
|
case PCIBIOS_BUFFER_TOO_SMALL:
|
case PCIBIOS_BUFFER_TOO_SMALL:
|
return "BUFFER_TOO_SMALL";
|
return "BUFFER_TOO_SMALL";
|
|
|
default:
|
default:
|
sprintf (buf, "UNKNOWN RETURN 0x%x", error);
|
sprintf (buf, "UNKNOWN RETURN 0x%x", error);
|
return buf;
|
return buf;
|
}
|
}
|
}
|
}
|
|
|
|
|
unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
|
unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
|
{
|
{
|
return mem_start;
|
return mem_start;
|
}
|
}
|
|
|
#endif
|
#endif
|
|
|
unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
|
unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
|
{
|
{
|
#ifdef CONFIG_PCI
|
#ifdef CONFIG_PCI
|
union bios32 *check;
|
union bios32 *check;
|
unsigned char sum;
|
unsigned char sum;
|
int i, length;
|
int i, length;
|
|
|
/*
|
/*
|
* Follow the standard procedure for locating the BIOS32 Service
|
* Follow the standard procedure for locating the BIOS32 Service
|
* directory by scanning the permissible address range from
|
* directory by scanning the permissible address range from
|
* 0xe0000 through 0xfffff for a valid BIOS32 structure.
|
* 0xe0000 through 0xfffff for a valid BIOS32 structure.
|
*
|
*
|
*/
|
*/
|
|
|
for (check = (union bios32 *) 0xe0000;
|
for (check = (union bios32 *) 0xe0000;
|
check <= (union bios32 *) 0xffff0;
|
check <= (union bios32 *) 0xffff0;
|
++check) {
|
++check) {
|
if (check->fields.signature != BIOS32_SIGNATURE)
|
if (check->fields.signature != BIOS32_SIGNATURE)
|
continue;
|
continue;
|
length = check->fields.length * 16;
|
length = check->fields.length * 16;
|
if (!length)
|
if (!length)
|
continue;
|
continue;
|
sum = 0;
|
sum = 0;
|
for (i = 0; i < length ; ++i)
|
for (i = 0; i < length ; ++i)
|
sum += check->chars[i];
|
sum += check->chars[i];
|
if (sum != 0)
|
if (sum != 0)
|
continue;
|
continue;
|
if (check->fields.revision != 0) {
|
if (check->fields.revision != 0) {
|
printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
|
printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
|
check->fields.revision, check);
|
check->fields.revision, check);
|
continue;
|
continue;
|
}
|
}
|
printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
|
printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
|
if (!bios32_entry) {
|
if (!bios32_entry) {
|
if (check->fields.entry >= 0x100000) {
|
if (check->fields.entry >= 0x100000) {
|
printk("pcibios_init: entry in high memory, trying direct PCI access\n");
|
printk("pcibios_init: entry in high memory, trying direct PCI access\n");
|
access_pci = check_direct_pci();
|
access_pci = check_direct_pci();
|
} else {
|
} else {
|
bios32_entry = check->fields.entry;
|
bios32_entry = check->fields.entry;
|
printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
|
printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
|
bios32_indirect.address = bios32_entry;
|
bios32_indirect.address = bios32_entry;
|
}
|
}
|
}
|
}
|
}
|
}
|
if (bios32_entry && check_pcibios())
|
if (bios32_entry && check_pcibios())
|
access_pci = &pci_bios_access;
|
access_pci = &pci_bios_access;
|
#endif
|
#endif
|
return memory_start;
|
return memory_start;
|
}
|
}
|
|
|