2005-12-24 23:06:46 +03:00
/* $NetBSD: pcibios.c,v 1.30 2005/12/24 20:07:11 perry Exp $ */
1999-11-17 04:16:36 +03:00
/*-
* Copyright ( c ) 1999 The NetBSD Foundation , Inc .
* All rights reserved .
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R . Thorpe of the Numerical Aerospace Simulation Facility ,
* NASA Ames Research Center .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement :
* This product includes software developed by the NetBSD
* Foundation , Inc . and its contributors .
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION , INC . AND CONTRIBUTORS
* ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED
* TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
/*
* Copyright ( c ) 1999 , by UCHIYAMA Yasushi
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. The name of the developer may NOT be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
*/
/*
* Interface to the PCI BIOS and PCI Interrupt Routing table .
*/
2001-11-15 10:03:28 +03:00
# include <sys/cdefs.h>
2005-12-24 23:06:46 +03:00
__KERNEL_RCSID ( 0 , " $NetBSD: pcibios.c,v 1.30 2005/12/24 20:07:11 perry Exp $ " ) ;
2001-11-15 10:03:28 +03:00
1999-11-17 04:16:36 +03:00
# include "opt_pcibios.h"
2005-06-21 12:19:25 +04:00
# include "opt_pcifixup.h"
1999-11-17 04:16:36 +03:00
# include <sys/param.h>
# include <sys/systm.h>
# include <sys/device.h>
# include <sys/malloc.h>
# include <dev/isa/isareg.h>
# include <machine/isa_machdep.h>
# include <dev/pci/pcireg.h>
# include <dev/pci/pcivar.h>
2000-04-28 21:15:15 +04:00
# include <dev/pci/pcidevs.h>
1999-11-17 04:16:36 +03:00
# include <i386/pci/pcibios.h>
2005-06-21 15:46:25 +04:00
# if defined(PCIBIOS_INTR_FIXUP) || defined(PCIBIOS_ADDR_FIXUP) || \
defined ( PCIBIOS_BUS_FIXUP )
# error The options PCIBIOS_INTR_FIXUP, PCIBIOS_ADDR_FIXUP, and PCIBIOS_BUS_FIXUP have been obsoleted by PCI_INTR_FIXUP, PCI_ADDR_FIXUP, and PCI_BUS_FIXUP. Please adjust your kernel configuration file.
# endif
2005-06-21 12:19:25 +04:00
# ifdef PCI_INTR_FIXUP
1999-11-17 04:16:36 +03:00
# include <i386/pci/pci_intr_fixup.h>
# endif
# include <machine/bios32.h>
2000-07-18 15:15:25 +04:00
# ifdef PCIBIOSVERBOSE
int pcibiosverbose = 1 ;
# endif
1999-11-17 04:16:36 +03:00
int pcibios_present ;
struct pcibios_pir_header pcibios_pir_header ;
struct pcibios_intr_routing * pcibios_pir_table ;
int pcibios_pir_table_nentries ;
int pcibios_max_bus ;
struct bios32_entry pcibios_entry ;
2004-04-11 10:00:25 +04:00
void pcibios_pir_init ( void ) ;
1999-11-17 04:16:36 +03:00
2004-04-11 10:00:25 +04:00
int pcibios_get_status ( u_int32_t * , u_int32_t * , u_int32_t * ,
u_int32_t * , u_int32_t * , u_int32_t * , u_int32_t * ) ;
int pcibios_get_intr_routing ( struct pcibios_intr_routing * ,
int * , u_int16_t * ) ;
1999-11-17 04:16:36 +03:00
2004-04-11 10:00:25 +04:00
int pcibios_return_code ( u_int16_t , const char * ) ;
1999-11-17 04:16:36 +03:00
2004-04-11 10:00:25 +04:00
void pcibios_print_exclirq ( void ) ;
2004-04-30 06:45:37 +04:00
# ifdef PCIBIOS_LIBRETTO_FIXUP
/* for Libretto L2/L3 hack */
static void pcibios_fixup_pir_table ( void ) ;
static void pcibios_fixup_pir_table_mask ( struct pcibios_linkmap * ) ;
struct pcibios_linkmap pir_mask [ ] = {
{ 2 , 0x0040 } ,
{ 7 , 0x0080 } ,
{ 8 , 0x0020 } ,
{ 0 , 0x0000 }
} ;
# endif
2004-10-21 21:12:41 +04:00
# ifdef PCIBIOS_SHARP_MM20_FIXUP
static void pcibios_mm20_fixup ( void ) ;
# endif
1999-11-17 04:16:36 +03:00
# ifdef PCIINTR_DEBUG
2004-04-11 10:00:25 +04:00
void pcibios_print_pir_table ( void ) ;
1999-11-17 04:16:36 +03:00
# endif
# define PCI_IRQ_TABLE_START 0xf0000
# define PCI_IRQ_TABLE_END 0xfffff
void
2005-02-04 00:35:44 +03:00
pcibios_init ( void )
1999-11-17 04:16:36 +03:00
{
struct bios32_entry_info ei ;
u_int32_t rev_maj , rev_min , mech1 , mech2 , scmech1 , scmech2 ;
if ( bios32_service ( BIOS32_MAKESIG ( ' $ ' , ' P ' , ' C ' , ' I ' ) ,
& pcibios_entry , & ei ) = = 0 ) {
/*
* No PCI BIOS found ; will fall back on old
* mechanism .
*/
return ;
}
/*
* We ' ve located the PCI BIOS service ; get some information
* about it .
*/
if ( pcibios_get_status ( & rev_maj , & rev_min , & mech1 , & mech2 ,
& scmech1 , & scmech2 , & pcibios_max_bus ) ! = PCIBIOS_SUCCESS ) {
/*
* We can ' t use the PCI BIOS ; will fall back on old
* mechanism .
*/
return ;
}
printf ( " PCI BIOS rev. %d.%d found at 0x%lx \n " , rev_maj , rev_min > > 4 ,
ei . bei_entry ) ;
# ifdef PCIBIOSVERBOSE
printf ( " pcibios: config mechanism %s%s, special cycles %s%s, "
" last bus %d \n " ,
mech1 ? " [1] " : " [x] " ,
mech2 ? " [2] " : " [x] " ,
scmech1 ? " [1] " : " [x] " ,
scmech2 ? " [2] " : " [x] " ,
pcibios_max_bus ) ;
# endif
/*
* The PCI BIOS tells us the config mechanism ; fill it in now
* so that pci_mode_detect ( ) doesn ' t have to look for it .
*/
pci_mode = mech1 ? 1 : 2 ;
pcibios_present = 1 ;
/*
* Find the PCI IRQ Routing table .
*/
pcibios_pir_init ( ) ;
2005-06-21 12:19:25 +04:00
# ifdef PCI_INTR_FIXUP
1999-11-17 04:16:36 +03:00
if ( pcibios_pir_table ! = NULL ) {
int rv ;
u_int16_t pciirq ;
/*
* Fixup interrupt routing .
*/
2003-02-27 01:21:19 +03:00
rv = pci_intr_fixup ( NULL , X86_BUS_SPACE_IO , & pciirq ) ;
1999-11-17 04:16:36 +03:00
switch ( rv ) {
case - 1 :
/* Non-fatal error. */
printf ( " Warning: unable to fix up PCI interrupt "
" routing \n " ) ;
break ;
case 1 :
/* Fatal error. */
panic ( " pcibios_init: interrupt fixup failed " ) ;
break ;
}
/*
* XXX Clear ` pciirq ' from the ISA interrupt allocation
* XXX mask .
*/
}
# endif
}
void
2005-02-04 00:35:44 +03:00
pcibios_pir_init ( void )
1999-11-17 04:16:36 +03:00
{
2004-05-03 11:08:46 +04:00
char * devinfo ;
1999-11-17 04:16:36 +03:00
paddr_t pa ;
caddr_t p ;
unsigned char cksum ;
u_int16_t tablesize ;
u_int8_t rev_maj , rev_min ;
int i ;
for ( pa = PCI_IRQ_TABLE_START ; pa < PCI_IRQ_TABLE_END ; pa + = 16 ) {
p = ( caddr_t ) ISA_HOLE_VADDR ( pa ) ;
2002-01-29 02:53:08 +03:00
if ( * ( int * ) p ! = BIOS32_MAKESIG ( ' $ ' , ' P ' , ' I ' , ' R ' ) ) {
/*
2003-09-17 15:57:34 +04:00
* XXX : Some laptops ( Toshiba / Libretto L series )
2002-01-29 02:53:08 +03:00
* use _PIR instead of $ PIR . So we try that too .
*/
if ( * ( int * ) p ! = BIOS32_MAKESIG ( ' _ ' , ' P ' , ' I ' , ' R ' ) )
continue ;
}
1999-11-17 04:16:36 +03:00
rev_min = * ( p + 4 ) ;
rev_maj = * ( p + 5 ) ;
tablesize = * ( u_int16_t * ) ( p + 6 ) ;
cksum = 0 ;
for ( i = 0 ; i < tablesize ; i + + )
cksum + = * ( unsigned char * ) ( p + i ) ;
printf ( " PCI IRQ Routing Table rev. %d.%d found at 0x%lx, "
" size %d bytes (%d entries) \n " , rev_maj , rev_min , pa ,
tablesize , ( tablesize - 32 ) / 16 ) ;
if ( cksum ! = 0 ) {
printf ( " pcibios_pir_init: bad IRQ table checksum \n " ) ;
continue ;
}
if ( tablesize < 32 | | ( tablesize % 16 ) ! = 0 ) {
printf ( " pcibios_pir_init: bad IRQ table size \n " ) ;
continue ;
}
if ( rev_maj ! = 1 | | rev_min ! = 0 ) {
printf ( " pcibios_pir_init: unsupported IRQ table "
" version \n " ) ;
continue ;
}
/*
* We can handle this table ! Make a copy of it .
*/
memcpy ( & pcibios_pir_header , p , 32 ) ;
pcibios_pir_table = malloc ( tablesize - 32 , M_DEVBUF ,
M_NOWAIT ) ;
if ( pcibios_pir_table = = NULL ) {
printf ( " pcibios_pir_init: no memory for $PIR \n " ) ;
return ;
}
memcpy ( pcibios_pir_table , p + 32 , tablesize - 32 ) ;
pcibios_pir_table_nentries = ( tablesize - 32 ) / 16 ;
printf ( " PCI Interrupt Router at %03d:%02d:%01d " ,
pcibios_pir_header . router_bus ,
2000-07-18 15:15:25 +04:00
PIR_DEVFUNC_DEVICE ( pcibios_pir_header . router_devfunc ) ,
PIR_DEVFUNC_FUNCTION ( pcibios_pir_header . router_devfunc ) ) ;
1999-11-17 04:16:36 +03:00
if ( pcibios_pir_header . compat_router ! = 0 ) {
2004-05-03 11:08:46 +04:00
devinfo = malloc ( 256 , M_DEVBUF , M_NOWAIT ) ;
if ( devinfo ) {
pci_devinfo ( pcibios_pir_header . compat_router ,
0 , 0 , devinfo , 256 ) ;
printf ( " (%s compatible) " , devinfo ) ;
free ( devinfo , M_DEVBUF ) ;
}
1999-11-17 04:16:36 +03:00
}
printf ( " \n " ) ;
pcibios_print_exclirq ( ) ;
2004-04-30 06:45:37 +04:00
# ifdef PCIBIOS_LIBRETTO_FIXUP
/* for Libretto L2/L3 hack */
pcibios_fixup_pir_table ( ) ;
# endif
2004-10-21 21:12:41 +04:00
# ifdef PCIBIOS_SHARP_MM20_FIXUP
pcibios_mm20_fixup ( ) ;
# endif
1999-11-17 04:16:36 +03:00
# ifdef PCIINTR_DEBUG
pcibios_print_pir_table ( ) ;
# endif
return ;
}
/*
* If there was no PIR table found , try using the PCI BIOS
* Get Interrupt Routing call .
*
* XXX The interface to this call sucks ; just allocate enough
* XXX room for 32 entries .
*/
pcibios_pir_table_nentries = 32 ;
pcibios_pir_table = malloc ( pcibios_pir_table_nentries *
sizeof ( * pcibios_pir_table ) , M_DEVBUF , M_NOWAIT ) ;
if ( pcibios_pir_table = = NULL ) {
printf ( " pcibios_pir_init: no memory for $PIR \n " ) ;
return ;
}
if ( pcibios_get_intr_routing ( pcibios_pir_table ,
& pcibios_pir_table_nentries ,
& pcibios_pir_header . exclusive_irq ) ! = PCIBIOS_SUCCESS ) {
printf ( " No PCI IRQ Routing information available. \n " ) ;
free ( pcibios_pir_table , M_DEVBUF ) ;
pcibios_pir_table = NULL ;
pcibios_pir_table_nentries = 0 ;
return ;
}
printf ( " PCI BIOS has %d Interrupt Routing table entries \n " ,
pcibios_pir_table_nentries ) ;
pcibios_print_exclirq ( ) ;
2004-04-30 06:45:37 +04:00
# ifdef PCIBIOS_LIBRETTO_FIXUP
/* for Libretto L2/L3 hack */
pcibios_fixup_pir_table ( ) ;
# endif
2004-10-21 21:12:41 +04:00
# ifdef PCIBIOS_SHARP_MM20_FIXUP
pcibios_mm20_fixup ( ) ;
# endif
1999-11-17 04:16:36 +03:00
# ifdef PCIINTR_DEBUG
pcibios_print_pir_table ( ) ;
# endif
}
int
2004-04-11 10:00:25 +04:00
pcibios_get_status ( u_int32_t * rev_maj , u_int32_t * rev_min ,
u_int32_t * mech1 , u_int32_t * mech2 , u_int32_t * scmech1 , u_int32_t * scmech2 ,
u_int32_t * maxbus )
1999-11-17 04:16:36 +03:00
{
u_int16_t ax , bx , cx ;
u_int32_t edx ;
int rv ;
2005-12-24 23:06:46 +03:00
__asm volatile ( " lcall *(%%edi) ; \
1999-11-17 04:16:36 +03:00
jc 1f ; \
xor % % ah , % % ah ; \
1 : "
: " =a " ( ax ) , " =b " ( bx ) , " =c " ( cx ) , " =d " ( edx )
: " 0 " ( 0xb101 ) , " D " ( & pcibios_entry ) ) ;
rv = pcibios_return_code ( ax , " pcibios_get_status " ) ;
if ( rv ! = PCIBIOS_SUCCESS )
return ( rv ) ;
if ( edx ! = BIOS32_MAKESIG ( ' P ' , ' C ' , ' I ' , ' ' ) )
return ( PCIBIOS_SERVICE_NOT_PRESENT ) ; /* XXX */
/*
* Fill in the various pieces if info we ' re looking for .
*/
* mech1 = ax & 1 ;
* mech2 = ax & ( 1 < < 1 ) ;
* scmech1 = ax & ( 1 < < 4 ) ;
* scmech2 = ax & ( 1 < < 5 ) ;
* rev_maj = ( bx > > 8 ) & 0xff ;
* rev_min = bx & 0xff ;
* maxbus = cx & 0xff ;
return ( PCIBIOS_SUCCESS ) ;
}
int
2004-04-11 10:00:25 +04:00
pcibios_get_intr_routing ( struct pcibios_intr_routing * table ,
int * nentries , u_int16_t * exclirq )
1999-11-17 04:16:36 +03:00
{
u_int16_t ax , bx ;
int rv ;
struct {
u_int16_t size ;
caddr_t offset ;
u_int16_t segment ;
} __attribute__ ( ( __packed__ ) ) args ;
args . size = * nentries * sizeof ( * table ) ;
args . offset = ( caddr_t ) table ;
args . segment = GSEL ( GDATA_SEL , SEL_KPL ) ;
memset ( table , 0 , args . size ) ;
2005-12-24 23:06:46 +03:00
__asm volatile ( " lcall *(%%esi) ; \
1999-11-17 04:16:36 +03:00
jc 1f ; \
xor % % ah , % % ah ; \
1 : movw % w2 , % % ds ; \
movw % w2 , % % es "
: " =a " ( ax ) , " =b " ( bx )
: " r " GSEL ( GDATA_SEL , SEL_KPL ) , " 0 " ( 0xb10e ) , " 1 " ( 0 ) ,
" D " ( & args ) , " S " ( & pcibios_entry ) ) ;
rv = pcibios_return_code ( ax , " pcibios_get_intr_routing " ) ;
if ( rv ! = PCIBIOS_SUCCESS )
return ( rv ) ;
* nentries = args . size / sizeof ( * table ) ;
* exclirq = bx ;
return ( PCIBIOS_SUCCESS ) ;
}
int
2004-04-11 10:00:25 +04:00
pcibios_return_code ( u_int16_t ax , const char * func )
1999-11-17 04:16:36 +03:00
{
const char * errstr ;
int rv = ax > > 8 ;
switch ( rv ) {
case PCIBIOS_SUCCESS :
return ( PCIBIOS_SUCCESS ) ;
case PCIBIOS_SERVICE_NOT_PRESENT :
errstr = " service not present " ;
break ;
case PCIBIOS_FUNCTION_NOT_SUPPORTED :
errstr = " function not supported " ;
break ;
case PCIBIOS_BAD_VENDOR_ID :
errstr = " bad vendor ID " ;
break ;
case PCIBIOS_DEVICE_NOT_FOUND :
errstr = " device not found " ;
break ;
case PCIBIOS_BAD_REGISTER_NUMBER :
errstr = " bad register number " ;
break ;
case PCIBIOS_SET_FAILED :
errstr = " set failed " ;
break ;
case PCIBIOS_BUFFER_TOO_SMALL :
errstr = " buffer too small " ;
break ;
default :
printf ( " %s: unknown return code 0x%x \n " , func , rv ) ;
return ( rv ) ;
}
printf ( " %s: %s \n " , func , errstr ) ;
return ( rv ) ;
}
void
2005-02-04 00:35:44 +03:00
pcibios_print_exclirq ( void )
1999-11-17 04:16:36 +03:00
{
int i ;
if ( pcibios_pir_header . exclusive_irq ) {
printf ( " PCI Exclusive IRQs: " ) ;
for ( i = 0 ; i < 16 ; i + + ) {
if ( pcibios_pir_header . exclusive_irq & ( 1 < < i ) )
printf ( " %d " , i ) ;
}
printf ( " \n " ) ;
}
}
2004-04-30 06:45:37 +04:00
# ifdef PCIBIOS_LIBRETTO_FIXUP
/* for Libretto L2/L3 hack */
static void
2005-02-04 00:35:44 +03:00
pcibios_fixup_pir_table ( void )
2004-04-30 06:45:37 +04:00
{
struct pcibios_linkmap * m ;
for ( m = pir_mask ; m - > link ! = 0 ; m + + )
pcibios_fixup_pir_table_mask ( m ) ;
}
void
2005-02-04 00:35:44 +03:00
pcibios_fixup_pir_table_mask ( struct pcibios_linkmap * mask )
2004-04-30 06:45:37 +04:00
{
int i , j ;
for ( i = 0 ; i < pcibios_pir_table_nentries ; i + + ) {
for ( j = 0 ; j < 4 ; j + + ) {
if ( pcibios_pir_table [ i ] . linkmap [ j ] . link = = mask - > link ) {
pcibios_pir_table [ i ] . linkmap [ j ] . bitmap
& = mask - > bitmap ;
}
}
}
}
# endif
1999-11-17 04:16:36 +03:00
# ifdef PCIINTR_DEBUG
void
2005-02-04 00:35:44 +03:00
pcibios_print_pir_table ( void )
1999-11-17 04:16:36 +03:00
{
int i , j ;
for ( i = 0 ; i < pcibios_pir_table_nentries ; i + + ) {
printf ( " PIR Entry %d: \n " , i ) ;
printf ( " \t Bus: %d Device: %d \n " ,
pcibios_pir_table [ i ] . bus ,
2000-07-18 15:15:25 +04:00
PIR_DEVFUNC_DEVICE ( pcibios_pir_table [ i ] . device ) ) ;
1999-11-17 04:16:36 +03:00
for ( j = 0 ; j < 4 ; j + + ) {
printf ( " \t \t INT%c: link 0x%02x bitmap 0x%04x \n " ,
' A ' + j ,
pcibios_pir_table [ i ] . linkmap [ j ] . link ,
pcibios_pir_table [ i ] . linkmap [ j ] . bitmap ) ;
}
}
}
# endif
2000-04-28 21:15:15 +04:00
2004-10-21 21:12:41 +04:00
# ifdef PCIBIOS_SHARP_MM20_FIXUP
/*
* This is a gross hack to get the interrupt from the EHCI controller
* working on a Sharp MM20 . The BIOS is just incredibly buggy .
*
* The story thus far :
* The modern way to route the interrupt is to use ACPI . But using
* ACPI fails with an error message about an uninitialized local
* variable in the AML code . ( It works in Windows , but fails in NetBSD
* and Linux . )
*
* The second attempt is to use PCI Interrupt Routing table . But this
* fails because the table does not contain any information about the
* interrupt from the EHCI controller . This is probably due to the fact
* that the table is compatible with ALi M1543 , but the MM20 has an ALi M1563 .
* The M1563 has additional interrupt lines . The ali1543 . c code also
* cannot handle the M1653 ' s extended interrupts . And fixing this is
* difficult since getting a data sheet from ALi requires signing an NDA .
*
* The third attempt is to use a BIOS call to route the interrupt
* ( as FreeBSD does ) with manually generated information . But the BIOS call
* fails because the BIOS code is not quite position independent . It makes
* some assumption about where the code segment register points .
*
* So the solution is to use the third attempt , but with a patched version
* of the BIOS .
* - - lennart @ augustsson . net
*/
# define BIOS32_START 0xe0000
# define BIOS32_SIZE 0x20000
static char pcibios_shadow [ BIOS32_SIZE ] ;
static struct bios32_entry pcibios_entry_shadow ;
/*
* Copy BIOS and zap offending instruction .
* The bad instruction is
* mov % cs : 0x63c ( % ebx ) , % ah
* NetBSD does not have the code segment set up for this to work .
* Using the value 0xff for the table entry seems to work .
* The replacement is
* mov $ 0xff , % ah ; nop ; nop ; nop ; nop ; nop
*/
static void
pcibios_copy_bios ( void )
{
u_int8_t * bad_instr ;
memcpy ( pcibios_shadow , ISA_HOLE_VADDR ( BIOS32_START ) , BIOS32_SIZE ) ;
pcibios_entry_shadow = pcibios_entry ;
pcibios_entry_shadow . offset =
( void * ) ( ( u_long ) pcibios_shadow +
( u_long ) pcibios_entry . offset -
( u_long ) ISA_HOLE_VADDR ( BIOS32_START ) ) ;
bad_instr = ( u_int8_t * ) pcibios_entry_shadow . offset + 0x499 ;
if ( * bad_instr ! = 0x2e )
panic ( " bad bios " ) ;
bad_instr [ 0 ] = 0xb4 ; bad_instr [ 1 ] = 0xff ; /* mov $0xff,%ah */
bad_instr [ 2 ] = 0x90 ; /* nop */
bad_instr [ 3 ] = 0x90 ; /* nop */
bad_instr [ 4 ] = 0x90 ; /* nop */
bad_instr [ 5 ] = 0x90 ; /* nop */
bad_instr [ 6 ] = 0x90 ; /* nop */
}
/*
* Call BIOS to route an interrupt .
* The PCI device is identified by bus , device , func .
* The interrupt is on pin PIN ( A - D ) and interrupt IRQ .
* BIOS knows the magic for the interrupt controller .
*/
static int
pcibios_biosroute ( int bus , int device , int func , int pin , int irq )
{
u_int16_t ax , bx , cx ;
int rv ;
printf ( " pcibios_biosroute: b,d,f=%d,%d,%d pin=%x irq=%d \n " ,
bus , device , func , pin + 0xa , irq ) ;
bx = ( bus < < 8 ) | ( device < < 3 ) | func ;
cx = ( irq < < 8 ) | ( 0xa + pin ) ;
2005-12-24 23:06:46 +03:00
__asm volatile ( " lcall *(%%esi) ; \
2004-10-21 21:12:41 +04:00
jc 1f ; \
xor % % ah , % % ah ; \
1 : movw % w1 , % % ds ; \
movw % w1 , % % es "
: " =a " ( ax )
: " r " GSEL ( GDATA_SEL , SEL_KPL ) , " 0 " ( 0xb10f ) ,
" b " ( bx ) , " c " ( cx ) ,
" S " ( & pcibios_entry_shadow ) ) ;
rv = pcibios_return_code ( ax , " pcibios_biosroute " ) ;
return rv ;
}
2004-11-22 01:00:00 +03:00
# define MM20_PCI_BUS 0
# define MM20_PCI_EHCI_DEV 15
# define MM20_PCI_EHCI_FUNC 3
# define MM20_PCI_EHCI_PIN 3
# define MM20_PCI_EHCI_INTR 11
# define MM20_PCI_ISA_DEV 3
# define MM20_PCI_ISA_FUNC 0
2004-10-21 21:12:41 +04:00
static void
pcibios_mm20_fixup ( void )
{
2004-11-22 01:00:00 +03:00
pci_chipset_tag_t pc ;
pcitag_t tag ;
2004-10-21 21:12:41 +04:00
/* Copy BIOS */
pcibios_copy_bios ( ) ;
/* Route the interrupt for the EHCI controller. */
2004-11-22 01:00:00 +03:00
( void ) pcibios_biosroute ( MM20_PCI_BUS ,
MM20_PCI_EHCI_DEV ,
MM20_PCI_EHCI_FUNC ,
MM20_PCI_EHCI_PIN ,
MM20_PCI_EHCI_INTR ) ;
/* Fake some tags. */
pc = NULL ;
tag = pci_make_tag ( pc , MM20_PCI_BUS , MM20_PCI_EHCI_DEV ,
MM20_PCI_EHCI_FUNC ) ;
/* Set interrupt register in EHCI controller */
pci_conf_write ( pc , tag , 0x3c , 0x50000400 + MM20_PCI_EHCI_INTR ) ;
tag = pci_make_tag ( pc , MM20_PCI_BUS , MM20_PCI_ISA_DEV ,
MM20_PCI_ISA_FUNC ) ;
/* Set some unknown registers in the ISA bridge. */
pci_conf_write ( pc , tag , 0x58 , 0xd87f5300 ) ;
pci_conf_write ( pc , tag , 0x74 , 0x00000009 ) ;
2004-10-21 21:12:41 +04:00
}
# endif /* PCIBIOS_SHARP_MM20_FIXUP */