;***************************************************************************
;
;  PCI CODE FOLLOWS
;
;  the following functions provide access to the PCI interface.
;  These functions are used by scan_bus, and also some ethernet drivers
;
;***************************************************************************

; PCI Bus defines
PCI_HEADER_TYPE 	    equ     0x0e  ;8 bit
PCI_BASE_ADDRESS_0	    equ     0x10  ;32 bit
PCI_BASE_ADDRESS_5	    equ     0x24  ;32 bits
PCI_BASE_ADDRESS_SPACE_IO   equ     0x01
PCI_VENDOR_ID		    equ     0x00  ;16 bit
PCI_BASE_ADDRESS_IO_MASK    equ     0xFFFFFFFC

;***************************************************************************
;   Function
;      config_cmd
;
;   Description
;       creates a command dword  for use with the PCI bus
;       bus # in ebx
;      devfn in ecx
;       where in edx
;
;      command dword returned in eax
;       Only eax destroyed
;***************************************************************************
config_cmd:
    push    ecx
    mov     eax, ebx
    shl     eax, 16
    or	    eax, 0x80000000
    shl     ecx, 8
    or	    eax, ecx
    pop     ecx
    or	    eax, edx
    and     eax, 0xFFFFFFFC
    ret

;***************************************************************************
;   Function
;      pcibios_read_config_byte
;
;   Description
;       reads a byte from the PCI config space
;       bus # in ebx
;      devfn in ecx
;       where in edx ( ls 16 bits significant )
;
;      byte returned in al ( rest of eax zero )
;       Only eax/edx destroyed
;***************************************************************************
pcibios_read_config_byte:
    call    config_cmd
    push    dx
    mov     dx, 0xCF8
    out     dx, eax
    pop     dx

    xor     eax, eax
    and     dx, 0x03
    add     dx, 0xCFC
;   and     dx, 0xFFC
    in	    al, dx
    ret

;***************************************************************************
;   Function
;      pcibios_read_config_word
;
;   Description
;       reads a word from the PCI config space
;       bus # in ebx
;      devfn in ecx
;       where in edx ( ls 16 bits significant )
;
;      word returned in ax ( rest of eax zero )
;       Only eax/edx destroyed
;***************************************************************************
pcibios_read_config_word:
    call    config_cmd
    push    dx
    mov     dx, 0xCF8
    out     dx, eax
    pop     dx

    xor     eax, eax
    and     dx, 0x02
    add     dx, 0xCFC
;   and     dx, 0xFFC
    in	    ax, dx
    ret

;***************************************************************************
;   Function
;      pcibios_read_config_dword
;
;   Description
;       reads a dword from the PCI config space
;       bus # in ebx
;      devfn in ecx
;       where in edx ( ls 16 bits significant )
;
;      dword returned in eax
;       Only eax/edx destroyed
;***************************************************************************
pcibios_read_config_dword:
    push    edx
    call    config_cmd
    push    dx
    mov     dx, 0xCF8
    out     dx, eax
    pop     dx
    xor     eax, eax
    mov     dx, 0xCFC
    in	    eax, dx
    pop     edx
    ret

;***************************************************************************
;   Function
;      pcibios_write_config_byte
;
;   Description
;       write a byte in al to the PCI config space
;       bus # in ebx
;      devfn in ecx
;       where in edx ( ls 16 bits significant )
;
;       Only eax/edx destroyed
;***************************************************************************
pcibios_write_config_byte:
    push    ax
    call    config_cmd
    push    dx
    mov     dx, 0xCF8
    out     dx, eax
    pop     dx
    pop     ax

    and     dx, 0x03
    add     dx, 0xCFC
    out     dx, al
    ret

;***************************************************************************
;   Function
;      pcibios_write_config_word
;
;   Description
;       write a word in ax to the PCI config space
;       bus # in ebx
;      devfn in ecx
;       where in edx ( ls 16 bits significant )
;
;       Only eax/edx destroyed
;***************************************************************************
pcibios_write_config_word:
    push    ax
    call    config_cmd
    push    dx
    mov     dx, 0xCF8
    out     dx, eax
    pop     dx
    pop     ax

    and     dx, 0x02
    add     dx, 0xCFC
    out     dx, ax
    ret

;***************************************************************************
;   Function
;      delay_us
;
;   Description
;       delays for 30 to 60 us
;
;        I would prefer this routine to be able to delay for
;       a selectable number of microseconds, but this works for now.
;
;       If you know a better way to do 2us delay, pleae tell me!
;***************************************************************************
delay_us:
    push    eax
    push    ecx

    mov     ecx,2

    in	    al,0x61
    and     al,0x10
    mov     ah,al
    cld

dcnt1:
    in	    al,0x61
    and     al,0x10
    cmp     al,ah
    jz	    dcnt1

    mov     ah,al
    loop    dcnt1

    pop     ecx
    pop     eax

    ret

;***************************************************************************
;   Function
;      scan_bus
;
;   Description
;       Scans the PCI bus for a supported device
;        If a supported device is found, the drvr_ variables are initialised
;       to that drivers functions ( as defined in the PCICards table)
;
;        io_addr   holds card I/O space. 32 bit, but only LS 16 bits valid
;        pci_data  holds the PCI vendor + device code
;       pci_dev   holds PCI bus dev #
;       pci_bus   holds PCI bus #
;
;        io_addr will be zero if no card found
;
;***************************************************************************
scan_bus:
    xor     eax, eax
    mov     [hdrtype], al
    mov     [pci_data], eax

    xor     ebx, ebx	     ; ebx = bus# 0 .. 255

sb_bus_loop:
    xor     ecx, ecx	     ; ecx = devfn# 0 .. 254  ( not 255? )

sb_devf_loop:
    mov     eax, ecx
    and     eax, 0x07

    cmp     eax, 0
    jne     sb_001

    mov     edx, PCI_HEADER_TYPE
    call    pcibios_read_config_byte
    mov     [hdrtype], al
    jmp     sb_002

sb_001:
    mov     al, [hdrtype]
    and     al, 0x80
    cmp     al, 0x80
    jne     sb_inc_devf

sb_002:
    mov     edx, PCI_VENDOR_ID
    call    pcibios_read_config_dword
    mov     [vendor_device], eax
    cmp     eax, 0xffffffff
    je	    sb_empty
    cmp     eax, 0
    jne     sb_check_vendor

sb_empty:
    mov     [hdrtype], byte 0
    jmp     sb_inc_devf

sb_check_vendor:
    ; iterate though PCICards until end or match found
    mov     esi, PCICards

sb_check:
    cmp     [esi], dword 0
    je	    sb_inc_devf 	       ; Quit if at last entry
    cmp     eax, [esi]
    je	    sb_got_card
    add     esi, PCICARDS_ENTRY_SIZE
    jmp     sb_check

sb_got_card:
    ; indicate that we have found the card
    mov     [pci_data], eax
    mov     [pci_dev], ecx
    mov     [pci_bus], ebx

    ; Define the driver functions
    push    eax
    mov     eax, [esi+4]
    mov     [drvr_probe], eax
    mov     eax, [esi+8]
    mov     [drvr_reset], eax
    mov     eax, [esi+12]
    mov     [drvr_poll], eax
    mov     eax, [esi+16]
    mov     [drvr_transmit], eax
    mov     eax, [esi+20]
    mov     [drvr_cable], eax
    pop     eax

    mov     edx, PCI_BASE_ADDRESS_0

sb_reg_check:
    call    pcibios_read_config_dword
    mov     [io_addr], eax
    and     eax, PCI_BASE_ADDRESS_IO_MASK
    cmp     eax, 0
    je	    sb_inc_reg
    mov     eax, [io_addr]
    and     eax, PCI_BASE_ADDRESS_SPACE_IO
    cmp     eax, 0
    je	    sb_inc_reg

    mov     eax, [io_addr]
    and     eax, PCI_BASE_ADDRESS_IO_MASK
    mov     [io_addr], eax

sb_exit1:
    ret

sb_inc_reg:
    add     edx, 4
    cmp     edx, PCI_BASE_ADDRESS_5
    jbe     sb_reg_check

sb_inc_devf:
    inc     ecx
    cmp     ecx, 255
    jb	    sb_devf_loop
    inc     ebx
    cmp     ebx, 256
    jb	    sb_bus_loop

    ; We get here if we didn't find our card
    ; set io_addr to 0 as an indication
    xor     eax, eax
    mov     [io_addr], eax

sb_exit2:
    ret