;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; 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.4A November 4th, 2010 ;; ;; Version 0.4 February 2nd, 2010 ;; ;; Version 0.3 April 9, 2007 ;; ;; Version 0.2 December 21st, 2002 ;; ;; ;; ;; Author: Victor Prodan, victorprodan@yahoo.com ;; ;; Mihailov Ilia, ghost.nsk@gmail.com ;; ;; Artem Jerdev, kolibri@jerdev.co.uk ;; ;; 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 mmio_pci_addr dw 0x100 ; default PCI device bdf-address dw 0 iglobal align 4 f62call: dd pci_fn_0 dd pci_fn_1 dd pci_fn_2 dd pci_service_not_supported ;3 dd pci_read_reg ;4 byte dd pci_read_reg ;5 word dd pci_read_reg ;6 dword dd pci_service_not_supported ;7 dd pci_write_reg ;8 byte dd pci_write_reg ;9 word dd pci_write_reg ;10 dword dd pci_mmio_init ;11 dd pci_mmio_map ;12 dd pci_mmio_unmap ;13 endg align 4 pci_api: ;cross mov eax, ebx mov ebx, ecx mov ecx, edx cmp [pci_access_enabled],1 jne pci_service_not_supported movzx edx, al cmp al, 13 ja pci_service_not_supported call dword [f62call+edx*4] mov dword [esp+32],eax ret align 4 pci_api_drv: cmp [pci_access_enabled],1 jne .fail cmp eax, 2 ja .fail jmp dword [f62call+eax*4] .fail: or eax,-1 ret ;; ============================================ pci_fn_0: ; PCI function 0: get pci version (AH.AL) movzx eax,word [BOOT_VAR+0x9022] ret pci_fn_1: ; PCI function 1: get last bus in AL mov al,[BOOT_VAR+0x9021] ret pci_fn_2: ; PCI function 2: get pci access mechanism mov al,[BOOT_VAR+0x9020] ret pci_service_not_supported: or eax,-1 mov dword [esp+32],eax ret ;*************************************************************************** ; (for backward compatibility only) ; 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: ; push edx ; xor edx, edx ; mov dh, ah ; bus ; mov dl, bh ; dev+fn ; shl edx, 12 ; mov dl, bl ; reg ; add edx, PCIe_CONFIG_SPACE ; ; and al, 2 ; jz @f ; mov eax, dword[edx] ; pop edx ; ret ;@@: ; and al, 1 ; jz @f ; mov ax, word[edx] ; pop edx ; ret ;@@: ; mov al, byte[edx] ; pop edx ; ret 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 ;*************************************************************************** ; 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: ; push edx ; xor edx, edx ; mov dh, ah ; bus ; mov dl, bh ; dev+fn ; shl edx, 12 ; mov dl, bl ; reg ; add edx, PCIe_CONFIG_SPACE ; ; test al, 2 ; jz @f ; mov dword[edx], ecx ; ret ;@@: ; test al, 1 ; jz @f ; mov word[edx], cx ; pop edx ; ret ;@@: ; mov byte[edx], cl ; pop edx ; ret 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 ;*************************************************************************** ; Function ; pci_mmio_init ; ; Description ; IN: bx = device's PCI bus address (bbbbbbbbdddddfff) ; Returns eax = phys. address of user-accessible DMA block ; Error codes ; eax = -1 : PCI user access blocked, ; eax = -3 : user heap initialization failure ;*************************************************************************** pci_mmio_init: mov [mmio_pci_addr],bx call init_heap ; (if not initialized yet) or eax,eax jz @f mov eax, [UserDMAaddr] ret @@: mov eax,-3 ret ;*************************************************************************** ; Function ; pci_mmio_map ; ; Description ; maps a block of PCI memory to user-accessible linear address ; ; ; IN: ah = BAR#; or ; IN: ah = 0xDA for DMA-mapping requests; ; IN: ebx = block size (bytes); ; IN: ecx = offset in MMIO block (in 4K-pages, to avoid misaligned pages); ; ; Returns eax = MMIO block's linear address in the userspace (if no error) ; ; ; Error codes ; eax = -1 : user access to PCI blocked, ; eax = -2 : an invalid BAR register referred ; eax = -3 : no i/o space on that BAR ; eax = -4 : a port i/o BAR register referred ; eax = -5 : dynamic userspace allocation problem ;*************************************************************************** pci_mmio_map: and edx,0x0ffff cmp ah, 0xDA jz .dma_map cmp ah,6 jb .bar_0_5 jz .bar_rom mov eax,-2 ret .dma_map: push ecx mov ecx,ebx mov eax,[UserDMAaddr] jmp .allocate_block .bar_rom: mov ah, 8 ; bar6 = Expansion ROM base address .bar_0_5: push ecx add ebx, 4095 and ebx,-4096 push ebx mov bl, ah ; bl = BAR# (0..5), however bl=8 for BAR6 shl bl, 1 shl bl, 1 add bl, 0x10 ; now bl = BAR offset in PCI config. space mov eax, dword[mmio_pci_addr] shl eax, 12 mov al, bl ; BAR offset add eax, PCIe_CONFIG_SPACE mov eax, [eax] ; read the BAR or eax, eax jnz @f mov eax,-3 ; empty I/O space jmp mmio_ret_fail @@: test eax, 1 jz @f mov eax,-4 ; damned ports (not MMIO space) jmp mmio_ret_fail @@: pop ecx ; ecx = block size, bytes (expanded to whole page) mov ebx, ecx ; user_alloc destroys eax, ecx, edx, but saves ebx and eax, 0xFFFFFFF0 .allocate_block: push eax ; store MMIO physical address + keep the stack 2x4b deep stdcall user_alloc, ecx or eax, eax jnz mmio_map_over mov eax,-5 ; problem with page allocation mmio_ret_fail: pop ecx pop edx ret mmio_map_over: mov ecx, ebx ; ecx = size (bytes, expanded to whole page) shr ecx, 12 ; ecx = number of pages mov ebx, eax ; ebx = linear address pop eax ; eax = MMIO start pop edx ; edx = MMIO shift (pages) shl edx, 12 ; edx = MMIO shift (bytes) add eax, edx ; eax = uMMIO physical address or eax, (PG_SHARED+PG_UW+PG_NOCACHE) mov edi, ebx call commit_pages mov eax, edi ret ;*************************************************************************** ; Function ; pci_mmio_unmap_page ; ; Description ; unmaps the linear space previously tied to a PCI memory block ; ; IN: ebx = linear address of space previously allocated by pci_mmio_map ; returns eax = 1 if successfully unmapped ; ; Error codes ; eax = -1 if no user PCI access allowed, ; eax = 0 if unmapping failed ;*************************************************************************** pci_mmio_unmap: stdcall user_free, ebx ret ;------------------------------------------------ align 4 sys_map1: ; copies a block from kernel to user space ; in: esi = address in kernel space ; edi = eddress in user space ; ebx = count (dwords) ; out: nothing ;------------------------------------------------ mov ecx, ebx pushfd cld rep movsd popfd ret ;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= uglobal align 4 ; VendID (2), DevID (2), Revision = 0 (1), Class Code (3), FNum (1), Bus (1) pci_emu_dat: times 30*10 db 0 endg ;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= align 4 sys_pcibios: 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 + 4 ], edi mov dword[esp + 8], esi .return_abcd: mov dword[esp + 24], edx .return_abc: mov dword[esp + 28], ecx .return_ab: mov dword[esp + 20], ebx .return_a: mov dword[esp + 32], eax ret