blob: 47c478b27d515b4cdb0e96fdc2f32183a03676d8 [file] [log] [blame]
/*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, daniel@omicron.se
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* x86 realmode assembly implementation of a PCI BIOS
* for platforms that use one PCI hose and configuration
* access type 1. (The common case for low-end PC's)
*/
#include "bios.h"
#define PCI_BIOS_DEBUG
.section .bios, "ax"
.code16
.globl realmode_pci_bios_call_entry
.hidden realmode_pci_bios_call_entry
.type realmode_pci_bios_call_entry, @function
realmode_pci_bios_call_entry:
MAKE_BIOS_STACK
call realmode_pci_bios
RESTORE_CALLERS_STACK
ret
.globl realmode_pci_bios
realmode_pci_bios:
gs movw OFFS_AX(%bp), %ax
cmpb $1, %al
je pci_bios_present
cmpb $2, %al
je pci_bios_find_device
cmpb $3, %al
je pci_bios_find_class
cmpb $6, %al
je pci_bios_generate_special_cycle
cmpb $8, %al
je pci_bios_read_cfg_byte
cmpb $9, %al
je pci_bios_read_cfg_word
cmpb $10, %al
je pci_bios_read_cfg_dword
cmpb $11, %al
je pci_bios_write_cfg_byte
cmpb $12, %al
je pci_bios_write_cfg_word
cmpb $13, %al
je pci_bios_write_cfg_dword
cmpb $14, %al
je pci_bios_get_irq_routing
cmpb $15, %al
je pci_bios_set_irq
jmp unknown_function
/*****************************************************************************/
pci_bios_present:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_present
#endif
movl $0x20494350, %eax
gs movl %eax, OFFS_EDX(%bp)
/* We support cfg type 1 version 2.10 */
movb $0x01, %al
gs movb %al, OFFS_AL(%bp)
movw $0x0210, %ax
gs movw %ax, OFFS_BX(%bp)
/* last bus number */
cs movb pci_last_bus, %al
gs movb %al, OFFS_CL(%bp)
jmp clear_carry
/*****************************************************************************/
/* device 0-31, function 0-7 */
pci_bios_find_device:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_find_device
#endif
gs movw OFFS_CX(%bp), %di
shll $16, %edi
gs movw OFFS_DX(%bp), %di
/* edi now holds device in upper 16 bits and vendor in lower 16 bits */
gs movw OFFS_SI(%bp), %si
/* start at bus 0 dev 0 function 0 */
xorw %bx, %bx
pfd_loop:
/* dword 0 is vendor/device */
xorw %ax, %ax
call __pci_bios_select_register
movw $0xcfc, %dx
inl %dx, %eax
/* our device ? */
cmpl %edi, %eax
je pfd_found_one
pfd_next_dev:
/* check for multi function devices */
movw %bx, %ax
andw $3, %ax
jnz pfd_function_not_zero
movw $0x000c, %ax
call __pci_bios_select_register
movw $0xcfe, %dx
inb %dx, %al
andb $0x80, %al
jz pfd_not_multi_function
pfd_function_not_zero:
/* next function, overflows in to device number, then bus number */
incw %bx
jmp pfd_check_bus
pfd_not_multi_function:
/* remove function bits */
andw $0xfff8, %bx
/* next device, overflows in to bus number */
addw $0x0008, %bx
pfd_check_bus:
cs movb pci_last_bus, %ah
cmpb %ah, %bh
ja pfd_not_found
jmp pfd_loop
pfd_found_one:
decw %si
js pfd_done
jmp pfd_next_dev
pfd_done:
gs movw %bx, OFFS_BX(%bp)
jmp clear_carry
pfd_not_found:
/* device not found */
movb $0x86, %ah
jmp set_carry
/*****************************************************************************/
pci_bios_find_class:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_find_class
#endif
gs movl OFFS_ECX(%bp), %edi
/* edi now holds class-code in lower 24 bits */
andl $0x00ffffff, %edi
gs movw OFFS_SI(%bp), %si
/* start at bus 0 dev 0 function 0 */
xorw %bx, %bx
pfc_loop:
/* dword 8 is class-code high 24bits */
movw $8, %ax
call __pci_bios_select_register
movw $0xcfc, %dx
inl %dx, %eax
shrl $8, %eax
andl $0x00ffffff, %eax
/* our device ? */
cmpl %edi, %eax
je pfc_found_one
pfc_next_dev:
/* check for multi function devices */
andw $3, %bx
jnz pfc_function_not_zero
movw $0x000c, %ax
call __pci_bios_select_register
movw $0xcfe, %dx
inb %dx, %al
andb $0x80, %al
jz pfc_not_multi_function
pfc_function_not_zero:
/* next function, overflows in to device number, then bus number */
incw %bx
jmp pfc_check_bus
pfc_not_multi_function:
/* remove function bits */
andw $0xfff8, %bx
/* next device, overflows in to bus number */
addw $0x0008, %bx
pfc_check_bus:
cs movb pci_last_bus, %ah
cmpb %ah, %bh
ja pfc_not_found
jmp pfc_loop
pfc_found_one:
decw %si
js pfc_done
jmp pfc_next_dev
pfc_done:
gs movw %bx, OFFS_BX(%bp)
jmp clear_carry
pfc_not_found:
/* device not found */
movb $0x86, %ah
jmp set_carry
/*****************************************************************************/
pci_bios_generate_special_cycle:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_generate_special_cycle
#endif
/* function not supported */
movb $0x81, %ah
jmp set_carry
/*****************************************************************************/
pci_bios_read_cfg_byte:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_read_cfg_byte
#endif
call pci_bios_select_register
gs movw OFFS_DI(%bp), %dx
andw $3, %dx
addw $0xcfc, %dx
inb %dx, %al
gs movb %al, OFFS_CL(%bp)
jmp clear_carry
/*****************************************************************************/
pci_bios_read_cfg_word:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_read_cfg_word
#endif
call pci_bios_select_register
gs movw OFFS_DI(%bp), %dx
andw $2, %dx
addw $0xcfc, %dx
inw %dx, %ax
gs movw %ax, OFFS_CX(%bp)
jmp clear_carry
/*****************************************************************************/
pci_bios_read_cfg_dword:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_read_cfg_dword
#endif
call pci_bios_select_register
movw $0xcfc, %dx
inl %dx, %eax
gs movl %eax, OFFS_ECX(%bp)
jmp clear_carry
/*****************************************************************************/
pci_bios_write_cfg_byte:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_write_cfg_byte
#endif
call pci_bios_select_register
gs movw OFFS_DI(%bp), %dx
gs movb OFFS_CL(%bp), %al
andw $3, %dx
addw $0xcfc, %dx
outb %al, %dx
jmp clear_carry
/*****************************************************************************/
pci_bios_write_cfg_word:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_write_cfg_word
#endif
call pci_bios_select_register
gs movw OFFS_DI(%bp), %dx
gs movw OFFS_CX(%bp), %ax
andw $2, %dx
addw $0xcfc, %dx
outw %ax, %dx
jmp clear_carry
/*****************************************************************************/
pci_bios_write_cfg_dword:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_write_cfg_dword
#endif
call pci_bios_select_register
gs movl OFFS_ECX(%bp), %eax
movw $0xcfc, %dx
outl %eax, %dx
jmp clear_carry
/*****************************************************************************/
pci_bios_get_irq_routing:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_get_irq_routing
#endif
/* function not supported */
movb $0x81, %ah
jmp set_carry
/*****************************************************************************/
pci_bios_set_irq:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_set_irq
#endif
/* function not supported */
movb $0x81, %ah
jmp set_carry
/*****************************************************************************/
unknown_function:
#ifdef PCI_BIOS_DEBUG
cs incl num_pci_bios_unknown_function
#endif
/* function not supported */
movb $0x81, %ah
jmp set_carry
/*****************************************************************************/
pci_bios_select_register:
gs movw OFFS_BX(%bp), %bx
gs movw OFFS_DI(%bp), %ax
/* destroys eax, dx */
__pci_bios_select_register:
/* BX holds device id, AX holds register index */
pushl %ebx
andl $0xfc, %eax
andl $0xffff, %ebx
shll $8, %ebx
orl %ebx, %eax
orl $0x80000000, %eax
movw $0xcf8, %dx
outl %eax, %dx
popl %ebx
ret
clear_carry:
gs movw OFFS_FLAGS(%bp), %ax
/* clear carry -- function succeeded */
andw $0xfffe, %ax
gs movw %ax, OFFS_FLAGS(%bp)
xorw %ax, %ax
gs movb %ah, OFFS_AH(%bp)
ret
set_carry:
gs movb %ah, OFFS_AH(%bp)
gs movw OFFS_FLAGS(%bp), %ax
/* return carry -- function not supported */
orw $1, %ax
gs movw %ax, OFFS_FLAGS(%bp)
movw $-1, %ax
ret
/*****************************************************************************/
.globl pci_last_bus
pci_last_bus:
.byte 0
#ifdef PCI_BIOS_DEBUG
.globl num_pci_bios_present
num_pci_bios_present:
.long 0
.globl num_pci_bios_find_device
num_pci_bios_find_device:
.long 0
.globl num_pci_bios_find_class
num_pci_bios_find_class:
.long 0
.globl num_pci_bios_generate_special_cycle
num_pci_bios_generate_special_cycle:
.long 0
.globl num_pci_bios_read_cfg_byte
num_pci_bios_read_cfg_byte:
.long 0
.globl num_pci_bios_read_cfg_word
num_pci_bios_read_cfg_word:
.long 0
.globl num_pci_bios_read_cfg_dword
num_pci_bios_read_cfg_dword:
.long 0
.globl num_pci_bios_write_cfg_byte
num_pci_bios_write_cfg_byte:
.long 0
.globl num_pci_bios_write_cfg_word
num_pci_bios_write_cfg_word:
.long 0
.globl num_pci_bios_write_cfg_dword
num_pci_bios_write_cfg_dword:
.long 0
.globl num_pci_bios_get_irq_routing
num_pci_bios_get_irq_routing:
.long 0
.globl num_pci_bios_set_irq
num_pci_bios_set_irq:
.long 0
.globl num_pci_bios_unknown_function
num_pci_bios_unknown_function:
.long 0
#endif