Initial commit of 1.11 source as released by PC Engines

Original source https://www.pcengines.ch/file/wbios111s.zip
This commit is contained in:
Steve Howes
2023-07-25 16:17:48 +01:00
commit d410ad0acb
57 changed files with 13831 additions and 0 deletions
+743
View File
@@ -0,0 +1,743 @@
;
; PCI plug & play
;
; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved.
; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5.
;
; pd 020716 pci_capa -> set latency timer and cache line size
; together (16 bit write)
; pd 011108 set latency timer if P_PRILAT defined
; pd 000830 add PCI_NORST2 option -> don't touch base register
; for second motherboard device (e.g. USB)
; pd 991115 fix PCI I/O allocation: some devices (e.g. ESS Tech)
; have 16 bit base registers.
; pd 990329 fix PCI I/O allocation: 4 byte -> 64 bit registers...
; pd 990216 rewrite I/O allocation
; pd 980728 change to call postcode routine
; pd 980728 fix PCI_NORST option -> skip entire device, not just
; function
;
; To make this BIOS more suitable for future hot plug PCI support,
; the PCI bus address space allocation has been designed to use
; minimum granularity and fixed size allocation for bridges, rather
; than packing things as tightly as possible. This also simplifies
; the code quite a bit.
;
; Limitations:
;
; - PCI bridge code not yet tested.
; - Fixed size allocation for bridges (better to support future
; hot plug, simpler). As implemented, this does not work for
; more than 2 levels of bridges.
; - Except for VGA BIOS, expansion ROMs are not supported.
; Assume first image, 32 KB size.
; - Memory allocation below 1MB does not handle memory holes.
;
; PCI inherent limitations:
;
; - Devices behind bridges don't support memory that must be
; allocated below 1MB.
;
; PCI configuration space structure
;
p_id equ 0 ;vendor, device ID
p_cmd equ 4 ;command register
p_stat equ 6 ;status register
p_class equ 8 ;class code, revision ID
p_linesz equ 12 ;cache line size
p_lat equ 13 ;latency timer
p_hedt equ 14 ;header type
p_bist equ 15 ;built-in self test
p_base equ 10h ;base address registers
pb_bus equ 18h ;bridge: primary bus number
pb_bus2 equ 19h ;bridge: secondary bus number
pb_bus3 equ 1ah ;bridge: subordinate bus number
pb_lat2 equ 1bh ;bridge: secondary latency timer
pb_io equ 1ch ;bridge: I/O limit
pb_stat equ 1eh ;bridge: secondary status
pb_mem equ 20h ;bridge: memory base low
pb_memp equ 24h ;bridge: prefetchable memory base
p_cis equ 28h ;end of base address registers
pb_mem2 equ 28h ;bridge: memory base high
pb_memp2 equ 2ch ;bridge: prefetchable memory base high
p_rom equ 30h ;expansion rom base
pb_io2 equ 34h ;bridge: I/O base high
pb_rom equ 38h ;bridge: ROM
p_line equ 3ch ;IRQ assigned to function
p_pin equ 3dh ;0 = not used, 1=A, 2=B, 3=C, 4=D
p_mingnt equ 3eh ;minimum grant time
pb_ctl equ 3eh ;bridge: control
p_maxlat equ 3fh ;maximum latency
;
; working variables for PNP
;
;+ must directly follow previous variable !
;
struc tmp_pci
p_int: dw ?,? ;PCI interrupt lines, LSB = INTA,
;MSB = INTD
p_irqpt: dw ? ;pointer to IRQ table entry
p_mem: dw ? ;regular memory (64K steps)
p_memlim: dw ? ;+ memory limit
p_memp: dw ? ;prefetchable memory (64K steps)
p_memplim: dw ? ;+ prefetchable memory limit
p_io: dw ? ;I/O address (16 byte steps)
p_iolim: dw ? ;+ I/O limit
p_memr: dw ? ;memory below 1MB (segment value)
p_memrlim: dw ? ;+ limit
p_capa dw ? ;low: 0 if back to back mode supported
;high: 0 for fast, 2 for medium, 4/6
;slow devsel
p_bus: db ? ;current bus
p_lastbus: db ? ;+ last bus number
ends
;
; get byte [EBX] -> AL
;
pci_getb: mov eax,ebx ;set index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
mov dl,bl ;I/O port index
or dl,0fch ;pci_dat assume $fc + bit mask
in al,dx
ret
;
; get word [EBX] -> AX
;
pci_getw: mov eax,ebx ;set index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
mov dl,bl ;I/O port index
or dl,0fch ;pci_dat assume $fc + bit mask
in ax,dx
ret
;
; get double word [EBX] -> EAX
;
pci_getd: mov eax,ebx ;set index
mov dx,pci_ad
out dx,eax
mov dl,low(pci_dat)
in eax,dx
ret
;
; assign interrupts to device [EBX]
;
pci_airq: mov bl,p_pin ;which interrupt pin does device use ?
call pci_getb
mov ah,0
sub al,1 ;0 -> FF
mov si,ax
jb pci_airq3 ;0 -> FF = no interrupt
cmp al,3
mov al,0ffh
ja pci_airq3 ;out of range -> FF = no interrupt
mov al,[si+p_int] ;get interrupt connected to this line
pci_airq3: mov bl,p_line ;store interrupt number
;V fall through
;
; set byte AL -> [EBX]
;
pci_setb: push ax ;save data
mov eax,ebx ;get index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
pop ax ;restore data
mov dl,bl ;I/O port index
or dl,0fch ;assume pci_dat $fc + bit mask
out dx,al
ret
;
; set word AX -> [EBX]
;
pci_setw: push ax ;save data
mov eax,ebx ;get index
mov dx,pci_ad
and al,0fch ;mask low bits
out dx,eax
pop ax ;restore data
mov dl,bl ;I/O port index
or dl,0fch ;assume pci_dat $fc + bit mask
out dx,ax
ret
;
; set double word EAX -> [EBX]
;
pci_setd: xchg eax,ebx ;swap data, index
mov dx,pci_ad
out dx,eax
xchg eax,ebx ;restore data
mov dl,low(pci_dat)
out dx,eax
ret
;
; allocate space for a device, CL = ending index
; (different for normal device / bridge)
;
pci_dev: mov bl,p_base
pci_dev1: mov eax,0ffffffffh ;find out how many valid bits
call pci_setd ;write
call pci_getd ;and read back
mov ch,al ;save register type
test al,1 ;I/O ?
jz pci_dev1a ;:no
and ax,ax ;high bit set ? (D15)
jns pci_dev9 ;no: this register doesn't work
jmp pci_io ;allocate I/O device
pci_dev1a: and eax,eax ;high bit set ? (D31)
jns pci_dev9 ;no: this register doesn't work !
test al,2 ;below 1 MB ?
jnz pci_memr
mov si,p_memp
test al,8 ;prefetchable ?
jnz pci_dev2 ;:yes
mov si,p_mem
; allocate memory (normal or prefetchable)
;
; we allocate with a minimum of 64KB granularity
pci_dev2: shr eax,16 ;/ 64KB
not ax ;get requested block size
cmp ax,P_MEMINC-1 ;round up to minimum increment
ja pci_dev3
mov ax,P_MEMINC-1
pci_dev3: test [si],ax ;do we need to round up base ?
jz pci_dev4 ;:no
or [si],ax ;round up base
inc word [si]
jz pci_err2 ;:overflow
pci_dev4: inc ax ;size + 1
add ax,[si] ;update base
jb pci_err2 ;overflow: error
cmp ax,[si+2] ;exceed limit ?
ja pci_err2 ;:yes
xchg ax,[si] ;set new base, get starting base
shl eax,16 ;-> high word, low base is 0
pci_dev7: call pci_setd ;set base register
pci_dev8: test ch,4 ;64 bit base register ?
jz pci_dev9 ;:32 bit
add bl,4
xor eax,eax ;clear high register
pci_dev8a: call pci_setd
pci_dev9: add bl,4
cmp bl,cl ;end of registers ?
jb pci_dev1 ;:try another
ret
pci_err2: mov al,0c2h ;& error code
jmp pci_err9
pci_err3: mov al,0c3h ;& error code
jmp pci_err9
pci_err4: mov al,0c4h ;& error code
jmp pci_err9
;
; Allocate <1MB memory space
;
pci_memr: test al,4 ;reserved type ?
jnz pci_err4 ;:yes
not eax ;find out requested block size
shr eax,4 ;/16
test eax,0ffff0000h ;error if device wants more than 1MB !
jnz pci_err3
cmp ax,P_MEMRINC-1 ;round up to minimum increment
ja pci_memr2
mov ax,P_MEMRINC-1
;&&& need to add base rounding
pci_memr2: add ax,[p_memr] ;update base
jb pci_err2 ;overflow: error
cmp ax,[p_memrlim] ;exceed limit ?
ja pci_err2 ;:yes
xchg [p_memr],ax ;get old base, set new
shl eax,4 ;-> get back into place
;(high EAX is 0 from 1MB test)
jmp short pci_dev7 ;set base register, continue
;
; Allocate I/O space
;
; Note that I/O is allocated in 256 byte blocks starting at
; $1000, $1400, etc. (aliases of chipset registers 0000..00FF)
;
; Space in x100 .. x3FF is reserved for ISA bus. Space in 0000..00FF
; and 0480..04FF is reserved for chipset peripherals. 0CF8..0CFF is
; the PCI config address. So we usually start at 1000.
;
pci_io: not ax ;find out requested block size
test ax,0ff00h ;> 256 bytes ?
jnz pci_err5 ;:error
;&&& jz pci_io1 ;&&& :ok allocation
;&&& mov ax,00ffh ;&&& allocate 256 bytes
pci_io1: ;&&&
or ax,P_IOINC-1 ;minimum allocation
test [p_io],ax ;do we need to round up ?
jz pci_ior2 ;:no
or [p_io],ax ;base must be multiple of block size
inc word [p_io]
test word [p_io],0300h ;block overrun ?
jz pci_ior2 ;:no
or word [p_io],03ffh ;next 1024 byte block
inc word [p_io]
jz pci_err5 ;:overflow
pci_ior2:
inc ax ;mask -> count
add ax,[p_io] ;base + block size -> future base
test ax,0300h ;block overrun ?
jz pci_ior3 ;:no
or ax,03ffh ;next 1024 byte block
inc ax
jz pci_err5 ;:overflow
pci_ior3: cmp ax,[p_iolim] ;exceeded limit ?
ja pci_err5 ;:overflow
xchg [p_io],ax ;get old base, set new
and eax,0ffffh ;clear high half of EAX
jmp pci_dev8a ;set base register, continue
pci_err5: mov al,bl ;&&&
out post,al
mov al,bh
out post+4,al
jmp pci_err5 ;&&&
mov al,0c5h ;& error code
jmp pci_err9
;
; round up [SI], mask CL, set limit using DX if primary bus,
; DI if secondary bus
;
pci_rnd: mov ax,[si]
test al,cl
jz pci_rnd1
or al,cl
inc ax
jz pci_err1
mov [si],ax
pci_rnd1: cmp byte [p_bus],0 ;primary bus ?
jnz pci_rnd2
add ax,dx ;primary bus limit
jmp short pci_rnd3
pci_rnd2: add ax,di ;secondary bus limit
pci_rnd3: jb pci_err1 ;:too much
mov [si+2],ax ;set limit
ret
pci_err1: jmp pci_bus9 ;allocation error
;
; allocate space for a bridge
;
pci_bri: mov ax,[p_bus] ;+ lastbus *** pd030520
push ax ;save initial bus number
inc ah ;get a new bus number
mov [p_lastbus],ah
mov bl,pb_bus ;set primary, secondary bus number
call pci_setw
mov ax,P_SECLAT * 256 + 0ffh ;subordinate = 255 for config
mov bl,pb_bus3 ;+ pb_lat2
call pci_setw
; save variables for recursion
mov al,[p_lastbus] ;save old bus number, set new
mov [p_bus],al ;*** pd030520 xchg al,[p_bus]
;*** push ax
push [p_memlim]
push [p_memplim]
push [p_iolim]
;bridges don't have fine granularity - start at 1MB / 4K boundaries
mov si,p_mem ;memory: 1MB boundaries
mov dx,P_MEMINC1 ;primary bus
mov di,P_MEMINC2 ;secondary bus
mov cl,0fh
call pci_rnd
mov si,p_memp ;prefetch memory: 1MB boundaries
;mov dx,P_MEMINC1 ;primary bus
;mov di,P_MEMINC2 ;secondary bus
;mov cl,0fh
call pci_rnd
mov si,p_io ;I/O: 4K boundaries
mov cl,0ffh
mov dx,P_IOINC1 ;primary bus
mov di,P_IOINC2 ;secondary bus
call pci_rnd
;set bridge base and limit registers
mov bl,pb_mem ;memory base / limit
mov eax,[p_mem] ;+ p_memlim
sub eax,10000h ;-> inclusive limit
call pci_setd
mov bl,pb_memp ;prefetchable base / limit
mov eax,[p_memp] ;+ p_memplim
sub eax,10000h ;-> inclusive limit
call pci_setd
mov bl,pb_io ;I/O base / limit
mov al,[p_io+1]
mov ah,[p_iolim+1]
dec ah ;-> inclusive limit
call pci_setw
;clear high registers
xor eax,eax ;clear high memory base
mov bl,pb_mem2
call pci_setd
mov bl,pb_memp2 ;clear high prefetchable base
call pci_setd
mov bl,pb_io2 ;clear high I/O base
call pci_setd
; save variables for recursion
push word [p_capa]
push dword [p_int]
mov word [p_capa],80h ;bus capabilities
mov bl,pb_stat ;read secondary status
call pci_getw
and byte [p_capa],al ;clear fast back to back if not supportd
or byte [p_capa+1],ah ;set devsel timing
mov bl,p_cmd ;command register: enable bus master
call pci_getw
or al,04
mov bl,p_cmd
call pci_setw
push ebx ;save current EBX
rol dword [p_int],8 ;precompensate for interrupt rotation
call pci_bus ;enumerate this bus
call pci_capa ;set capability flags
; limit -> new allocation pointer
mov ax,[p_memlim] ;memory
mov [p_mem],ax
mov ax,[p_memplim] ;prefetchable memory
mov [p_memp],ax
mov ax,[p_iolim] ;I/O
mov [p_io],ax
; restore variables
pop ebx ;restore after recursion
pop dword [p_int]
pop word [p_capa]
pop word [p_iolim]
pop word [p_memplim]
pop word [p_memlim]
pop ax
mov [p_bus],al ;*** pd030520 was ax, clobbered lastbus
mov al,[p_lastbus] ;set correct subordinate bus number
mov bl,pb_bus3
call pci_setb
mov bl,pb_ctl ;set bridge control register
mov ax,P_BRIDGE
and byte [p_capa],80h ;isolate fast back to back bit
or al,byte [p_capa] ;copy to control register
call pci_setw
ret
;
; get next interrupt assignment
;
pci_nint: test bh,7 ;function 0 ?
jnz pci_nint4 ;no: same interrupts
cmp byte [p_bus],0 ;primary bus ?
jnz pci_nint5 ;:no
cmp word [p_irqpt],pci_tab9 ;end of IRQ table ?
jz pci_nint4 ;yes: don't change
mov si,[p_irqpt] ;get next table entry
cs: lodsd
mov [p_irqpt],si
mov [p_int],eax ;save interrupt setting
pci_nint4: ret
pci_nint5: ror dword [p_int],8 ;rotate interrupts
ret
;
; enumerate a bus - called recursively
;
pci_bus: mov bh,80h ;enable config access
mov bl,[p_bus] ;bus number
shl ebx,16 ;device, function = 0
; check vendor ID, 0 or FFFF = nothing there, skip device
pci_bus0: call pci_nint ;get next interrupt assignment
mov bl,p_id ;get vendor / device ID
call pci_getd
inc ax ;FFFF = not present
jz pci_bus4 ;:skip device
dec ax ;0000 = no more functions
jz pci_bus4 ;:skip device
call pci_airq ;assign interrupts
call pci_vga ;handle PCI VGA
; check header type, different handling if bridge
mov bl,p_hedt ;get header type
call pci_getb
and al,7fh
cmp al,1 ;bridge
jz pci_bus1
#if def PCI_NORST
cmp bh,PCI_NORST ;don't touch base registers on this
jz pci_bus3 ;device (pd 980728: was jz pci_bus2)
#endif
#if def PCI_NORST2
cmp bh,PCI_NORST2
jz pci_bus3
#endif
#if def PCI_NORST3
cmp bh,PCI_NORST3
jz pci_bus3
#endif
mov cl,p_cis ;end index for normal device
call pci_dev ;allocate resources
jmp short pci_bus2
pci_bus1: mov cl,pb_bus ;end index for bridge
call pci_dev ;allocate resources
call pci_bri ;enumerate secondary buses
; try next function
pci_bus2: test bh,7 ;function 0 ?
jnz pci_bus4 ;:no, try next subfunction
; function 0 - is this a multifunction device
mov bl,p_hedt ;get header type
call pci_getb
and al,80h
jnz pci_bus4 ;:yes, multifunction device
pci_bus3: or bh,7 ;step to next device
pci_bus4: inc bh
jnz pci_bus0
mov ax,[p_mem] ;check allocation limits
cmp ax,[p_memlim]
ja pci_bus9 ;:error
mov ax,[p_memp]
cmp ax,[p_memplim]
ja pci_bus9 ;:error
mov ax,[p_io]
cmp ax,[p_iolim]
ja pci_bus9
ret
pci_bus9: mov al,0e5h ;error !
pci_err9: call postcode ;pd 980728: don't do direct I/O
pci_err99: jmp pci_err99
;
; handle PCI VGA
;
pci_vga: mov bl,p_class ;read class ID
call pci_getd
shr eax,16 ;check high 16 bits
cmp ax,0300h ;VGA ?
jnz pci_vga9 ;:no
mov bl,p_cmd ;enable memory access
call pci_getw
push ax
or al,2
call pci_setw
mov bl,p_rom ;enable ROM - at P_ROM0
mov esi,P_ROM0 shl 16
mov eax,esi
inc ax ;low bit = 1 -> enable
call pci_setd
push ebx ;save ebx context
call getunreal ;enter unreal mode
mov ax,[esi]
cmp ax,0aa55h ;ROM header ?
jnz pci_vga2 ;:no
call cs_vshad2 ;copy video BIOS to shadow RAM
;(chipset specific)
pci_vga2: pop ebx ;restore ebx
xor eax,eax ;disable ROM
call pci_setd
pop ax ;restore command register
mov bl,p_cmd
call pci_setw
xor ax,ax ;restore segment (from unreal mode)
mov ds,ax
mov es,ax
pci_vga9: ret
;
; set capability bits & enable devices
;
pci_capa: xor bx,bx ;start with device / function 0
mov cx,P_COMMAND ;standard command value
cmp byte [p_capa],80h ;all devices back-to-back capable ?
jnz pci_capa1 ;:no
or ch,02 ;enable fast back-to-back mode
pci_capa1:
#if def PCI_NORST3
cmp bh,PCI_NORST3
jz pci_capa1b
#endif
#if def PCI_NORST2
cmp bh,PCI_NORST2 ;skip this device ?
jnz pci_capa2 ;:no
pci_capa1b:
or bh,7 ;skip device
jmp short pci_capa4
pci_capa2:
#endif
mov bl,p_id ;get vendor / device ID
call pci_getw
inc ax ;FFFF = not present
jz pci_capa4 ;:skip device
dec ax ;0000 = no more functions
jz pci_capa4 ;:skip device
mov bl,p_cmd ;read command register
call pci_getw
and ax,0ff5fh ;clear stepping, palette snoop bits
or ax,cx ;enable according to P_COMMAND
call pci_setw ;set command register
mov bl,p_hedt ;is this a bridge ? &&& pd030520
call pci_getw
and al,07f ;normal device ?
cmp al,1 ;bridge ?
jz pci_capa3b ;:yes, don't overwrite bus ID
pci_capa3:
mov bl,p_linesz ;set p_linesz and p_lat
mov al,P_LINSIZE
mov ah,P_PRILAT
call pci_setw ;some devices don't support
;setting these registers individually
pci_capa3b:
; try next function
test bh,7 ;function 0 ?
jnz pci_capa4 ;:no, try next subfunction
; function 0 - is this a multifunction device
mov bl,p_hedt ;get header type
call pci_getb
; mov eax,ebx ;device address
; mov al,p_hedt and 0fch ;header type
; mov dx,pci_ad
; out dx,eax
; mov dl,low(pci_dat) + 2
; in al,dx
and al,80h
jnz pci_capa4 ;:yes, multifunction device
or bh,7 ;step to next device
pci_capa4: inc bh
jnz pci_capa1
ret
;
; PCI plug & play configuration
;
; EAX = scratch
; EBX = 80 / bus / device / function / index
; CX = scratch
; DX = port address
; SI = scratch
; DI = scratch
; BP = scratch
;
pci_pnp: mov word [p_mem],P_MEM0 ;set starting addresses
mov word [p_memp],P_MEMP0
mov word [p_memr],P_MEMR0
mov word [p_io],P_IO0
mov word [p_memlim],P_MEM9 ;set allocation limits
mov word [p_memplim],P_MEMP9
mov word [p_memrlim],P_MEMR9
mov word [p_iolim],P_IO9
;mov byte [p_bus],0
;mov byte [p_lastbus],0
mov word [p_irqpt],offset pci_tab
call pci_bus ;enumerate bus
call pci_capa ;set capability flags
mov al,[p_lastbus] ;set number of last PCI bus
mov byte [cs:d_lastbus],al
ret
;
; Disable all devices on primary bus
;
; NOTE: Called without stack ! On a chipset with PCI reset
; function, PCI reset is the easier way...
;
pci_rst:
#if def PCI_NOCLR
ret
#else
#if def PCI_NORST
mov eax,p_cmd+80000800h ;start with device 1 (don't cut off
;chipset)
#else
mov eax,p_cmd+80000000h ;access bus 0, device 0, function 0
#endif
pci_rst1:
#if def PCI_NORST
cmp ah,PCI_NORST ;don't reset this device
jnz pci_rst2
add ah,8
pci_rst2:
#endif
mov dx,pci_ad
out dx,eax ;write index
xchg ax,bx
mov dl,low(pci_dat)
in ax,dx ;read command register
and ax,0fff8h ;disable bus master, I/O, memory access
xchg ax,bx
mov dl,low(pci_ad)
out dx,eax ;write index
xchg ax,bx
mov dl,low(pci_dat)
out dx,ax ;write command register
xchg ax,bx
inc ah ;next device / function
jnz pci_rst1
ret
#endif