;      SCCSID = src/bios/pcisad.asm, oldr, 6.xxx 96/05/30
       page       ,132
;****************************************************************************
;*                                                                          *
;* Source File: pcisad.asm                                                  *
;*                                                                          *
;* Description:                                                             *
;*      Detect PCI busses which contain hard drive controllers and unmask   *
;*      appropriate IRQs so that standalone dump can be taken to hard       *
;*      drives on PCI busses.                                               *
;*                                                                          *
;* Entry Points:                                                            *
;*      PCISAD - called by SET8259 procedure in hdrive.asm.                 *                                                              *
;*                                                                          *
;* Change history:                                                          *
;*      155547 - File created. Code provided by Scott Dunham (Raleigh).     *
;*      156609 - LRN - 155547 fix hung system when sadump attempted on      *
;*               9595 machines. Changed code so that when no PCI bus was    *
;*               detected, that no irq's would be touched (moved EndPCI     *
;*               label to the end of the procedure).                        *
;*                                                                          *
;****************************************************************************
code      SEGMENT word public 'CODE'
            assume cs:code, ds:code, es:nothing, ss:nothing

	public PCISAD
        extrn  DoDelayO:near
.386
assume  cs:code, ds:code

READ_CONFIG_DWORD       EQU     0Ah
READ_CONFIG_BYTE        EQU     08h
PCI_BIOS_PRESENT        EQU     01h
PCI_FUNCTION_ID         EQU    0B1h
HFC                     EQU     01h
BASECLASS               EQU     0Bh
HEADERTYPE              EQU     0Eh
IRQLINE                 EQU     3Ch


IRQmask dw      0FFFFh


PCISAD  proc    near
; Assumes Interrupt Controllers are initialized to enable all necessary IRQs
; except for PCI hardfile controller IRQs.


        pushad

        call    PCIexist           ; determine PCI BIOS existence and CL<-maxbus
        jc      short EndPCI
        xor     ch,ch              ; clear upper 1/2 of cx
        inc     cx                 ; Must scan thru bus 0
        mov     bh,cl              ; Bus # to scan (plus 1 here) goes into bh
BusScan:
        dec     bh                 ; next bus number
        call    ScanforHFC         ; Scan this bus for hardfile controller (HFC)
        loop    BusScan

        mov     bx, IRQmask        ; read mask determined by this routine
        in      al, 21h            ; read master IC
        call    DoDelayO
        and     al, bl             ; Clear (enable) IRQs required by PCI HFCs
        out     21h, al            ; write master IC
        call    DoDelayO
        in      al, 0a1h           ; read slave IC
        call    DoDelayO
        and     al, bh             ; Clear (enable) IRQs required by PCI HFCs
        out     0a1h, al           ; write slave IC
        call    DoDelayO

EndPCI:
        popad
        ret

PCISAD  endp


PCIexist  proc    near
; returns max bus number in CL

        push    edx
        push    ax
        push    bx

        mov     ah, PCI_FUNCTION_ID     ; input parm
        mov     al, PCI_BIOS_PRESENT    ; input parm
        int     1ah                     ; PCI interrupt
        jc      short NoPCI             ; check results
        cmp     edx, ' ICP'             ; check results
        jne     short NoPCI
        clc                             ; indicate PCI BIOS exists
        jmp     short DonePCI           ; cl contains max bus number
NoPCI:
        stc                             ; indicate PCI BIOS doesn't exist
DonePCI:
        pop     bx
        pop     ax
        pop     edx
        ret
PCIexist  endp


ScanforHFC proc    near
; looks to see if a device exists on this bus
; input - bh contains bus number to scan

        pusha

        mov     cx, 32                  ; setup loop count
        mov     di,0                    ; read cfg reg 0
        mov     al, READ_CONFIG_DWORD   ; input parm
NewDev:
        mov     bl, cl                  ; setup device number
        dec     bl                      ; dev # is 1 less than loop cnt remains
        shl     bl, 3                   ; dev # goes in upper 5 bits, func# is 0
        mov     ah, PCI_FUNCTION_ID     ; input parm which is changed during INT
        push    cx                      ; save loop count
        int     1ah                     ; PCI interrupt
        jc      short DoNext            ; If error, do next device
        cmp     ecx, 0FFFFFFFFh         ; check if device exists
        je      short DoNext            ; Do next device if no device here
        call    FindHFCdevfun           ; Check for HFC on this dev or its funcs
DoNext:
        pop     cx                      ; restore loop count
        loop    NewDev                  ; go do next device

        popa
        ret
ScanforHFC endp


FindHFCdevfun proc    near
; looks for HFC in this dev or its functions
; input - bh contains bus number to scan
;         bl contains dev number in upper 5 bits, and 0 in lower 3 bits


        pusha

        mov     di, HEADERTYPE          ; read cfg reg of Header Byte
        mov     al, READ_CONFIG_BYTE    ; input parm
        mov     ah, PCI_FUNCTION_ID     ; input parm which is changed during INT
        int     1ah                     ; PCI interrupt
        jc      short OneDev            ; If error, assume 1 device
        test    cl, 080h                ; check if this is multifunc dev
        je      short OneDev            ; Jump if NOT multi func

        mov     cx, 8                   ; setup loop count for multi-func dev
        jmp     short DoneDet
OneDev:
        mov     cx, 1                   ; setup loop count for 1 dev
DoneDet:
        mov     di, BASECLASS           ; read cfg reg of Base Class
                                        ; This assumes that if func doesn't
                                        ; exist it won't return HFC value
NewFun:
        or      bl, cl                  ; or in the func # + 1
        dec     bl                      ; dec func #
        mov     ah, PCI_FUNCTION_ID     ; input parm which is changed during INT
        push    cx                      ; save loop count
        int     1ah                     ; PCI interrupt
        jc      short DoNextFun         ; If error, do next device
        cmp     cl, HFC                 ; check if this is an HFC
        jne     short DoNextFun         ; Do next device if no device here
        call    UnMaskIRQ               ; Go unmask the IRQ associated with it
DoNextFun:
        pop     cx                      ; restore loop count
        loop    NewFun                  ; go do next device

        popa
        ret
FindHFCdevfun endp

UnMaskIRQ     proc    near
; unmasks the IRQ of this device
; input - bh contains bus number of device
;         bl contains dev number in upper 5 bits, and func# in lower 3 bits

        pusha

        mov     di, IRQLINE             ; read cfg reg of Base Class
        mov     al, READ_CONFIG_BYTE    ; read a byte
        mov     ah, PCI_FUNCTION_ID     ; input parm which is changed during INT
        int     1ah                     ; PCI interrupt
        jc      short NoIRQ             ; If error, assume no IRQ
        cmp     cl, 0Fh                 ; check if valid IRQ
        ja      short NoIRQ             ; No valid IRQ, so leave
        mov     ax,1                    ; set bit 0
        shl     ax,cl                   ; shift to bit corresponding to IRQ
        not     ax                      ; complement it
        and     IRQmask, ax             ; update mask
NoIRQ:
        popa
        ret
UnMaskIRQ     endp

code ends
end
