;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;                                                              ;;
;;  PCI32.INC                                                   ;;
;;                                                              ;;
;;  32 bit PCI driver code                                      ;;
;;                                                              ;;
;;  Version 0.3  April 9, 2007                                  ;;
;;  Version 0.2  December 21st, 2002                            ;;
;;                                                              ;;
;;  Author: Victor Prodan, victorprodan@yahoo.com               ;;
;;          Mihailov Ilia, ghost.nsk@gmail.com                  ;;
;;    Credits:                                                  ;;
;;          Ralf Brown                                          ;;
;;          Mike Hibbett, mikeh@oceanfree.net                   ;;
;;                                                              ;;
;;  See file COPYING for details                                ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$

;***************************************************************************
;   Function
;      pci_api:
;
;   Description
;       entry point for system PCI calls
;***************************************************************************

align 4

pci_api:

        cmp  [pci_access_enabled],1
        jne  no_pci_access_for_applications

        or al,al
        jnz pci_fn_1
        ; PCI function 0: get pci version (AH.AL)
        movzx eax,word [BOOT_VAR+0x9022]
        ret

pci_fn_1:
        cmp al,1
        jnz pci_fn_2

        ; PCI function 1: get last bus in AL
        mov al,[BOOT_VAR+0x9021]
        ret

pci_fn_2:
        cmp al,2
        jne pci_fn_3
        ; PCI function 2: get pci access mechanism
        mov al,[BOOT_VAR+0x9020]
        ret
pci_fn_3:

        cmp al,4
        jz pci_read_reg   ;byte
        cmp al,5
        jz pci_read_reg   ;word
        cmp al,6
        jz pci_read_reg   ;dword

        cmp al,8
        jz pci_write_reg  ;byte
        cmp al,9
        jz pci_write_reg  ;word
        cmp al,10
        jz pci_write_reg  ;dword

      no_pci_access_for_applications:

        mov eax,-1

        ret

;***************************************************************************
;   Function
;      pci_make_config_cmd
;
;   Description
;       creates a command dword  for use with the PCI bus
;       bus # in ah
;       device+func in bh (dddddfff)
;       register in bl
;
;      command dword returned in eax ( 10000000 bbbbbbbb dddddfff rrrrrr00 )
;***************************************************************************

align 4

pci_make_config_cmd:
    shl     eax,8          ; move bus to bits 16-23
    mov     ax,bx          ; combine all
    and     eax,0xffffff
    or      eax,0x80000000
    ret

;***************************************************************************
;   Function
;      pci_read_reg:
;
;   Description
;       read a register from the PCI config space into EAX/AX/AL
;       IN: ah=bus,device+func=bh,register address=bl
;           number of bytes to read (1,2,4) coded into AL, bits 0-1
;           (0 - byte, 1 - word, 2 - dword)
;***************************************************************************

align 4

pci_read_reg:
        cmp     byte [BOOT_VAR+0x9020],2 ;what mechanism will we use?
        je      pci_read_reg_2

                ; mechanism 1
        push    esi   ; save register size into ESI
        mov     esi,eax
        and     esi,3

        call    pci_make_config_cmd
        mov     ebx,eax
                ; get current state
        mov     dx,0xcf8
        in      eax, dx
        push    eax
                ; set up addressing to config data
        mov     eax,ebx
        and     al,0xfc ; make address dword-aligned
        out     dx,eax
                ; get requested DWORD of config data
        mov     dl,0xfc
        and     bl,3
        or      dl,bl    ; add to port address first 2 bits of register address

        or      esi,esi
        jz      pci_read_byte1
        cmp     esi,1
        jz      pci_read_word1
        cmp     esi,2
        jz      pci_read_dword1
        jmp     pci_fin_read1

pci_read_byte1:
        in      al,dx
        jmp pci_fin_read1
pci_read_word1:
        in      ax,dx
        jmp pci_fin_read1
pci_read_dword1:
        in      eax,dx
        jmp     pci_fin_read1
pci_fin_read1:
                ; restore configuration control
        xchg    eax,[esp]
        mov     dx,0xcf8
        out     dx,eax

        pop     eax
        pop     esi
        ret
pci_read_reg_2:

        test    bh,128  ;mech#2 only supports 16 devices per bus
        jnz     pci_read_reg_err

        push esi   ; save register size into ESI
        mov esi,eax
        and esi,3

        push    eax
                ;store current state of config space
        mov     dx,0xcf8
        in      al,dx
        mov     ah,al
        mov     dl,0xfa
        in      al,dx

        xchg    eax,[esp]
                ; out 0xcfa,bus
        mov     al,ah
        out     dx,al
                ; out 0xcf8,0x80
        mov     dl,0xf8
        mov     al,0x80
        out     dx,al
                ; compute addr
        shr     bh,3 ; func is ignored in mechanism 2
        or      bh,0xc0
        mov     dx,bx

        or      esi,esi
        jz      pci_read_byte2
        cmp     esi,1
        jz      pci_read_word2
        cmp     esi,2
        jz      pci_read_dword2
        jmp     pci_fin_read2

pci_read_byte2:
        in      al,dx
        jmp pci_fin_read2
pci_read_word2:
        in      ax,dx
        jmp pci_fin_read2
pci_read_dword2:
        in      eax,dx
;       jmp pci_fin_read2
pci_fin_read2:

                ; restore configuration space
        xchg    eax,[esp]
        mov     dx,0xcfa
        out     dx,al
        mov     dl,0xf8
        mov     al,ah
        out     dx,al

        pop     eax
        pop     esi
        ret

pci_read_reg_err:
        xor     eax,eax
        dec     eax
        ret


;***************************************************************************
;   Function
;      pci_write_reg:
;
;   Description
;       write a register from ECX/CX/CL into the PCI config space
;       IN: ah=bus,device+func=bh,register address (dword aligned)=bl,
;           value to write in ecx
;           number of bytes to write (1,2,4) coded into AL, bits 0-1
;           (0 - byte, 1 - word, 2 - dword)
;***************************************************************************

align 4

pci_write_reg:
        cmp byte [BOOT_VAR+0x9020],2 ;what mechanism will we use?
        je pci_write_reg_2

                ; mechanism 1
        push    esi   ; save register size into ESI
        mov     esi,eax
        and     esi,3

        call    pci_make_config_cmd
        mov     ebx,eax
                ; get current state into ecx
        mov     dx,0xcf8
        in      eax, dx
        push    eax
                ; set up addressing to config data
        mov     eax,ebx
        and     al,0xfc ; make address dword-aligned
        out     dx,eax
                ; write DWORD of config data
        mov     dl,0xfc
        and     bl,3
        or      dl,bl
        mov     eax,ecx

        or      esi,esi
        jz      pci_write_byte1
        cmp     esi,1
        jz      pci_write_word1
        cmp     esi,2
        jz      pci_write_dword1
        jmp     pci_fin_write1

pci_write_byte1:
        out     dx,al
        jmp pci_fin_write1
pci_write_word1:
        out     dx,ax
        jmp pci_fin_write1
pci_write_dword1:
        out     dx,eax
        jmp     pci_fin_write1
pci_fin_write1:

                ; restore configuration control
        pop     eax
        mov     dl,0xf8
        out     dx,eax

        xor     eax,eax
        pop     esi

        ret
pci_write_reg_2:

        test    bh,128  ;mech#2 only supports 16 devices per bus
        jnz     pci_write_reg_err


        push esi   ; save register size into ESI
        mov esi,eax
        and esi,3

        push    eax
                ;store current state of config space
        mov     dx,0xcf8
        in      al,dx
        mov     ah,al
        mov     dl,0xfa
        in      al,dx
        xchg    eax,[esp]
                ; out 0xcfa,bus
        mov     al,ah
        out     dx,al
                ; out 0xcf8,0x80
        mov     dl,0xf8
        mov     al,0x80
        out     dx,al
                ; compute addr
        shr     bh,3 ; func is ignored in mechanism 2
        or      bh,0xc0
        mov     dx,bx
                ; write register
        mov     eax,ecx

        or      esi,esi
        jz      pci_write_byte2
        cmp     esi,1
        jz      pci_write_word2
        cmp     esi,2
        jz      pci_write_dword2
        jmp     pci_fin_write2

pci_write_byte2:
        out     dx,al
        jmp pci_fin_write2
pci_write_word2:
        out     dx,ax
        jmp pci_fin_write2
pci_write_dword2:
        out     dx,eax
        jmp     pci_fin_write2
pci_fin_write2:
                ; restore configuration space
        pop     eax
        mov     dx,0xcfa
        out     dx,al
        mov     dl,0xf8
        mov     al,ah
        out     dx,al

        xor     eax,eax
        pop     esi
        ret

pci_write_reg_err:
        xor     eax,eax
        dec     eax
        ret

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

; VendID (2), DevID (2), Revision = 0 (1), Class Code (3), FNum (1), Bus (1)
pci_emu_dat:	times	30*10 db 0

;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
align 4
sys_pcibios:
	xchg	ebx, eax
	xchg	ecx, eax
	xchg	edx, eax
	xchg	esi, eax
        xchg    edi, eax
	cmp	[pci_access_enabled], 1
        jne	.unsupported_func
        cmp     [pci_bios_entry], 0
	jz	.emulate_bios

        push    ds
        mov     ax, pci_data_sel
        mov     ds, ax
        mov     eax, ebp
        mov     ah, 0B1h
        call    pword [cs:pci_bios_entry]
        pop     ds

	jmp	.return
	;-=-=-=-=-=-=-=-=
.emulate_bios:
	cmp	ebp, 1			; PCI_FUNCTION_ID
	jnz	.not_PCI_BIOS_PRESENT
	mov	edx, 'PCI '
        mov     al, [OS_BASE+0x2F0000 + 0x9020]
        mov     bx, [OS_BASE+0x2F0000 + 0x9022]
        mov     cl, [OS_BASE+0x2F0000 + 0x9021]
        xor     ah, ah
	jmp	.return_abcd

.not_PCI_BIOS_PRESENT:
	cmp	ebp, 2			; FIND_PCI_DEVICE
	jne	.not_FIND_PCI_DEVICE
	mov	ebx, pci_emu_dat
..nxt:	cmp	[ebx], dx
	jne	..no
	cmp	[ebx + 2], cx
	jne	..no
	dec	si
	jns	..no
	mov	bx, [ebx + 4]
	xor     ah, ah
	jmp	.return_ab
..no:	cmp	word[ebx], 0
	je	..dev_not_found
	add	ebx, 10
	jmp	..nxt
..dev_not_found:
	mov	ah, 0x86		; DEVICE_NOT_FOUND
	jmp	.return_a

.not_FIND_PCI_DEVICE:
	cmp	ebp, 3			; FIND_PCI_CLASS_CODE
	jne	.not_FIND_PCI_CLASS_CODE
	mov	esi, pci_emu_dat
	shl	ecx, 8
..nxt2:	cmp	[esi], ecx
	jne	..no2
	mov	bx, [esi]
	xor     ah, ah
	jmp	.return_ab
..no2:	cmp	dword[esi], 0
	je	..dev_not_found
	add	esi, 10
	jmp	..nxt2

.not_FIND_PCI_CLASS_CODE:
	cmp	ebp, 8			; READ_CONFIG_*
	jb	.not_READ_CONFIG
	cmp	ebp, 0x0A
	ja	.not_READ_CONFIG
	mov     eax, ebp
	mov     ah, bh
	mov     edx, edi
	mov     bh, bl
	mov     bl, dl
	call	pci_read_reg
	mov	ecx, eax
	xor	ah, ah			; SUCCESSFUL
	jmp	.return_abc
.not_READ_CONFIG:
	cmp	ebp, 0x0B		; WRITE_CONFIG_*
	jb	.not_WRITE_CONFIG
	cmp	ebp, 0x0D
	ja	.not_WRITE_CONFIG
	lea     eax, [ebp+1]
	mov     ah, bh
	mov     edx, edi
	mov     bh, bl
	mov     bl, dl
	call	pci_write_reg
	xor	ah, ah			; SUCCESSFUL
	jmp	.return_abc
.not_WRITE_CONFIG:
.unsupported_func:
	mov	ah, 0x81		; FUNC_NOT_SUPPORTED
.return:mov	dword[esp + 8 ], edi
	mov	dword[esp + 12], esi
.return_abcd:
	mov	dword[esp + 28], edx
.return_abc:
	mov	dword[esp + 32], ecx
.return_ab:
	mov	dword[esp + 24], ebx
.return_a:
	mov	dword[esp + 36], eax
	ret