;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; R6040 driver for KolibriOS ;; ;; ;; ;; based on R6040.c from linux ;; ;; ;; ;; Written by Asper (asper.85@mail.ru) ;; ;; and hidnplayr (hidnplayr@gmail.com) ;; ;; ;; ;; GNU GENERAL PUBLIC LICENSE ;; ;; Version 2, June 1991 ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;******************************************************************** ; Interface ; r6040_reset ; r6040_probe ; r6040_poll ; r6040_transmit ; ; These functions are referenced in ethernet.inc ; ;******************************************************************** ;; A few user-configurable values. TX_RING_SIZE equ 4 RX_RING_SIZE equ 4 ; ethernet address length ETH_ALEN equ 6 ETH_HLEN equ (2 * ETH_ALEN + 2) ETH_ZLEN equ 60 ; 60 + 4bytes auto payload for ; mininmum 64bytes frame length ; system timer frequency HZ equ 1000 ; max time out delay time W_MAX_TIMEOUT equ 0x0FFF ;; Size of the in-memory receive ring. RX_BUF_LEN_IDX equ 3 ;; 0==8K, 1==16K, 2==32K, 3==64K RX_BUF_LEN equ (8192 << RX_BUF_LEN_IDX) ;-; Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). ;-TX_BUF_SIZE equ 1536 ;-RX_BUF_SIZE equ 1536 ;; PCI Tuning Parameters ; Threshold is bytes transferred to chip before transmission starts. TX_FIFO_THRESH equ 256 ;; In bytes, rounded down to 32 byte units. ;; The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. RX_FIFO_THRESH equ 4 ;; Rx buffer level before first PCI xfer. RX_DMA_BURST equ 4 ;; Maximum PCI burst, '4' is 256 bytes TX_DMA_BURST equ 4 ;; Operational parameters that usually are not changed. PHY1_ADDR equ 1 ;For MAC1 PHY2_ADDR equ 3 ;For MAC2 PHY_MODE equ 0x3100 ;PHY CHIP Register 0 PHY_CAP equ 0x01E1 ;PHY CHIP Register 4 ;; Time in jiffies before concluding the transmitter is hung. TX_TIMEOUT equ ((6000*HZ)/1000) R6040_IO_SIZE equ 256 ; RDC MAC I/O Size MAX_MAC equ 2 ; MAX RDC MAC ;************************************************************************** ; RDC R6040 Register Definitions ;************************************************************************** MCR0 equ 0x00 ;Control register 0 MCR1 equ 0x01 ;Control register 1 MAC_RST equ 0x0001 ;Reset the MAC MBCR equ 0x08 ;Bus control MT_ICR equ 0x0C ;TX interrupt control MR_ICR equ 0x10 ;RX interrupt control MTPR equ 0x14 ;TX poll command register MR_BSR equ 0x18 ;RX buffer size MR_DCR equ 0x1A ;RX descriptor control MLSR equ 0x1C ;Last status MMDIO equ 0x20 ;MDIO control register MDIO_WRITE equ 0x4000 ;MDIO write MDIO_READ equ 0x2000 ;MDIO read MMRD equ 0x24 ;MDIO read data register MMWD equ 0x28 ;MDIO write data register MTD_SA0 equ 0x2C ;TX descriptor start address 0 MTD_SA1 equ 0x30 ;TX descriptor start address 1 MRD_SA0 equ 0x34 ;RX descriptor start address 0 MRD_SA1 equ 0x38 ;RX descriptor start address 1 MISR equ 0x3C ;Status register MIER equ 0x40 ;INT enable register MSK_INT equ 0x0000 ;Mask off interrupts RX_FINISH equ 0x0001 ;RX finished RX_NO_DESC equ 0x0002 ;No RX descriptor available RX_FIFO_FULL equ 0x0004 ;RX FIFO full RX_EARLY equ 0x0008 ;RX early TX_FINISH equ 0x0010 ;TX finished TX_EARLY equ 0x0080 ;TX early EVENT_OVRFL equ 0x0100 ;Event counter overflow LINK_CHANGED equ 0x0200 ;PHY link changed ME_CISR equ 0x44 ;Event counter INT status ME_CIER equ 0x48 ;Event counter INT enable MR_CNT equ 0x50 ;Successfully received packet counter ME_CNT0 equ 0x52 ;Event counter 0 ME_CNT1 equ 0x54 ;Event counter 1 ME_CNT2 equ 0x56 ;Event counter 2 ME_CNT3 equ 0x58 ;Event counter 3 MT_CNT equ 0x5A ;Successfully transmit packet counter ME_CNT4 equ 0x5C ;Event counter 4 MP_CNT equ 0x5E ;Pause frame counter register MAR0 equ 0x60 ;Hash table 0 MAR1 equ 0x62 ;Hash table 1 MAR2 equ 0x64 ;Hash table 2 MAR3 equ 0x66 ;Hash table 3 MID_0L equ 0x68 ;Multicast address MID0 Low MID_0M equ 0x6A ;Multicast address MID0 Medium MID_0H equ 0x6C ;Multicast address MID0 High MID_1L equ 0x70 ;MID1 Low MID_1M equ 0x72 ;MID1 Medium MID_1H equ 0x74 ;MID1 High MID_2L equ 0x78 ;MID2 Low MID_2M equ 0x7A ;MID2 Medium MID_2H equ 0x7C ;MID2 High MID_3L equ 0x80 ;MID3 Low MID_3M equ 0x82 ;MID3 Medium MID_3H equ 0x84 ;MID3 High PHY_CC equ 0x88 ;PHY status change configuration register PHY_ST equ 0x8A ;PHY status register MAC_SM equ 0xAC ;MAC status machine MAC_ID equ 0xBE ;Identifier register MAX_BUF_SIZE equ 0x600 ;1536 MBCR_DEFAULT equ 0x012A ;MAC Bus Control Register MCAST_MAX equ 3 ;Max number multicast addresses to filter ;Descriptor status DSC_OWNER_MAC equ 0x8000 ;MAC is the owner of this descriptor DSC_RX_OK equ 0x4000 ;RX was successfull DSC_RX_ERR equ 0x0800 ;RX PHY error DSC_RX_ERR_DRI equ 0x0400 ;RX dribble packet DSC_RX_ERR_BUF equ 0x0200 ;RX length exceeds buffer size DSC_RX_ERR_LONG equ 0x0100 ;RX length > maximum packet length DSC_RX_ERR_RUNT equ 0x0080 ;RX packet length < 64 byte DSC_RX_ERR_CRC equ 0x0040 ;RX CRC error DSC_RX_BCAST equ 0x0020 ;RX broadcast (no error) DSC_RX_MCAST equ 0x0010 ;RX multicast (no error) DSC_RX_MCH_HIT equ 0x0008 ;RX multicast hit in hash table (no error) DSC_RX_MIDH_HIT equ 0x0004 ;RX MID table hit (no error) DSC_RX_IDX_MID_MASK equ 3 ;RX mask for the index of matched MIDx ;PHY settings ICPLUS_PHY_ID equ 0x0243 RX_INTS equ RX_FIFO_FULL or RX_NO_DESC or RX_FINISH TX_INTS equ TX_FINISH INT_MASK equ RX_INTS or TX_INTS r6040_txb equ (eth_data_start) r6040_rxb equ ((r6040_txb+(MAX_BUF_SIZE*TX_RING_SIZE)+32) and 0xfffffff0) r6040_tx_ring equ ((r6040_rxb+(MAX_BUF_SIZE*RX_RING_SIZE)+32) and 0xfffffff0) r6040_rx_ring equ ((r6040_tx_ring+(r6040_x_head.sizeof*TX_RING_SIZE)+32) and 0xfffffff0) virtual at ((r6040_rx_ring+(r6040_x_head.sizeof*RX_RING_SIZE)+32) and 0xfffffff0) r6040_private: .rx_ring dd ? .tx_ring dd ? .cur_rx dw ? .cur_tx dw ? .phy_addr dw ? .phy_mode dw ? .mcr0 dw ? .mcr1 dw ? .switch_sig dw ? end virtual virtual at 0 r6040_x_head: .status dw ? ;0-1 .len dw ? ;2-3 .buf dd ? ;4-7 .ndesc dd ? ;8-B .rev1 dd ? ;C-F .vbufp dd ? ;10-13 .vndescp dd ? ;14-17 .skb_ptr dd ? ;18-1B .rev2 dd ? ;1C-1F .sizeof: end virtual ; Read a word data from PHY Chip proc r6040_phy_read stdcall, phy_addr:dword, reg:dword push ecx edx mov eax, [phy_addr] shl eax, 8 add eax, [reg] add eax, MDIO_READ mov edx, [io_addr] add edx, MMDIO out dx, ax ;Wait for the read bit to be cleared. mov ecx, 2048 ;limit xor eax, eax .read: in ax, dx test ax, MDIO_READ jz @f dec ecx test ecx, ecx jnz .read @@: mov edx, [io_addr] add edx, MMRD in ax, dx and eax, 0xFFFF pop edx ecx ret endp ; Write a word data to PHY Chip proc r6040_phy_write stdcall, phy_addr:dword, reg:dword, val:dword push eax ecx edx mov eax, [val] mov edx, [io_addr] add edx, MMWD out dx, ax ;Write the command to the MDIO bus mov eax, [phy_addr] shl eax, 8 add eax, [reg] add eax, MDIO_WRITE mov edx, [io_addr] add edx, MMDIO out dx, ax ;Wait for the write bit to be cleared. mov ecx, 2048 ;limit xor eax, eax .write: in ax, dx test ax, MDIO_WRITE jz @f dec ecx test ecx, ecx jnz .write @@: pop edx ecx eax ret endp macro r6040_mdio_write reg, val { stdcall r6040_phy_read, [io_addr], [r6040_private.phy_addr], reg } macro r6040_mdio_write reg, val { stdcall r6040_phy_write, [io_addr], [r6040_private.phy_addr], reg, val } proc r6040_init_ring_desc stdcall, desc_ring:dword, size:dword push eax ecx esi mov ecx, [size] test ecx, ecx jz .out mov esi, [desc_ring] mov eax, esi .next_desc: add eax, r6040_x_head.sizeof - OS_BASE mov [esi+r6040_x_head.ndesc], eax add eax, OS_BASE mov [esi+r6040_x_head.vndescp], eax mov esi, eax dec ecx jnz .next_desc sub esi, r6040_x_head.sizeof mov eax, [desc_ring] mov [esi+r6040_x_head.vndescp], eax sub eax, OS_BASE mov [esi+r6040_x_head.ndesc], eax .out: pop esi ecx eax ret endp r6040_init_rxbufs: stdcall r6040_init_ring_desc, r6040_rx_ring, RX_RING_SIZE ; Allocate skbs for the rx descriptors mov esi, r6040_rx_ring mov ebx, r6040_rxb mov ecx, RX_RING_SIZE mov eax, r6040_rx_ring .next_desc: mov [esi+r6040_x_head.skb_ptr], ebx mov [esi+r6040_x_head.buf], ebx sub [esi+r6040_x_head.buf], OS_BASE mov [esi+r6040_x_head.status], DSC_OWNER_MAC mov eax, [esi+r6040_x_head.vndescp] mov esi, eax add ebx, MAX_BUF_SIZE dec ecx jnz .next_desc xor eax, eax .out: ret r6040_probe: DEBUGF 1, "Probing r6040\n" call adjust_pci_device ; If PHY status change register is still set to zero ; it means the bootloader didn't initialize it mov edx, [io_addr] add edx, PHY_CC in ax, dx test ax, ax jnz @f mov eax, 0x9F07 out dx, ax @@: ; Set MAC address mov ecx, 3 mov edi, node_addr mov edx, [io_addr] add edx, MID_0L .mac: in ax, dx stosw add edx, 2 dec ecx jnz .mac ; Some bootloaders/BIOSes do not initialize ; MAC address, warn about that and eax, 0xFF or eax, [node_addr] test eax, eax jnz @f DEBUGF 1, "K : MAC address not initialized\n" ;, generating random" ;Asper: Add here generate function call! ; Temporary workaround: init by constant adress mov dword [node_addr], 0x00006000 mov word [node_addr+4], 0x0001 @@: ; Init RDC private data mov [r6040_private.mcr0], 0x1002 ;mov [r6040_private.phy_addr], 1 ; Asper: Only one network card is supported now. mov [r6040_private.switch_sig], 0 ; Check the vendor ID on the PHY, if 0xFFFF assume none attached stdcall r6040_phy_read, 1, 2 cmp ax, 0xFFFF jne @f DEBUGF 1, "K : Failed to detect an attached PHY\n" ;, generating random" mov eax, -1 ret @@: ; Set MAC address call r6040_mac_address ; Initialize and alloc RX/TX buffers stdcall r6040_init_ring_desc, r6040_tx_ring, TX_RING_SIZE call r6040_init_rxbufs ;r6040_alloc_rxbufs test eax, eax jnz .out ; Read the PHY ID mov [r6040_private.phy_mode], 0x8000 stdcall r6040_phy_read, 0, 2 mov [r6040_private.switch_sig], ax cmp ax, ICPLUS_PHY_ID jne @f stdcall r6040_phy_write, 29, 31, 0x175C ; Enable registers jmp .phy_readen @@: ; PHY Mode Check movzx eax, [r6040_private.phy_addr] stdcall r6040_phy_write, eax, 4, PHY_CAP stdcall r6040_phy_write, eax, 0, PHY_MODE ; if PHY_MODE = 0x3100 call r6040_phy_mode_chk mov [r6040_private.phy_mode], ax jmp .phy_readen ; end if ; if not (PHY_MODE and 0x0100) mov [r6040_private.phy_mode], 0 ; end if .phy_readen: ; Set duplex mode mov ax, [r6040_private.phy_mode] or [r6040_private.mcr0], ax ; improve performance (by RDC guys) stdcall r6040_phy_read, 30, 17 or ax, 0x4000 stdcall r6040_phy_write, 30, 17, eax stdcall r6040_phy_read, 30, 17 xor ax, -1 or ax, 0x2000 xor ax, -1 stdcall r6040_phy_write, 30, 17, eax stdcall r6040_phy_write, 0, 19, 0x0000 stdcall r6040_phy_write, 0, 30, 0x01F0 ; Initialize all Mac registers call r6040_reset xor eax, eax .out: ret align 4 r6040_reset: DEBUGF 1, "Resetting r6040\n" push eax ecx edx ; Mask off Interrupt mov eax, MSK_INT mov edx, [io_addr] add edx, MIER out dx, ax ;Reset RDC MAC mov eax, MAC_RST mov edx, [io_addr] add edx, MCR1 out dx, ax mov ecx, 2048 ;limit .read: in ax, dx test ax, 0x1 jnz @f dec ecx test ecx, ecx jnz .read @@: ;Reset internal state machine mov ax, 2 mov edx, [io_addr] add edx, MAC_SM out dx, ax xor ax, ax out dx, ax mov esi, 5 call delay_ms ;MAC Bus Control Register mov ax, MBCR_DEFAULT mov edx, [io_addr] add edx, MBCR out dx, ax ;Buffer Size Register mov ax, MAX_BUF_SIZE mov edx, [io_addr] add edx, MR_BSR out dx, ax ;Write TX ring start address mov eax, r6040_tx_ring - OS_BASE ;Asper: Maybe we can just write dword? Hidnplayr: better use word, as described in datasheet. mov edx, [io_addr] add edx, MTD_SA0 out dx, ax shr eax, 16 add edx, MTD_SA1 - MTD_SA0 out dx, ax ;Write RX ring start address mov eax, r6040_rx_ring - OS_BASE ;Asper: Maybe we can just write dword? mov edx, [io_addr] add edx, MRD_SA0 out dx, ax shr eax, 16 add edx, MRD_SA1 - MRD_SA0 out dx, ax ;Set interrupt waiting time and packet numbers xor ax, ax mov edx, [io_addr] add edx, MT_ICR out dx, ax ;Asper: ~ Disable ints ;Enable interrupts ;mov ax, MSK_INT ;INT_MASK ;Asper ~ ;mov edx, [io_addr] ;add edx, MIER ;out dx, ax ;Enable TX and RX mov ax, [r6040_private.mcr0] or ax, 0x0002 mov edx, [io_addr] out dx, ax ;Let TX poll the descriptors ;we may got called by r6040_tx_timeout which has left ;some unset tx buffers xor ax, ax inc ax mov edx, [io_addr] add edx, MTPR out dx, ax pop edx ecx eax DEBUGF 1, "reset ok!\n" ; Indicate that we have successfully reset the card mov eax, [pci_data] mov [eth_status], eax ret proc r6040_tx_timeout push eax edx ;... inc [stats.tx_errors] ;Reset MAC and re-init all registers call r6040_init_mac_regs pop edx eax ret endp proc r6040_get_stats push eax edx mov edx, [io_addr] add edx, ME_CNT1 in al, dx add [stats.rx_crc_errors], al mov edx, [io_addr] add edx, ME_CNT0 in al, dx add [stats.multicast], al pop edx eax ret endp ;... proc r6040_phy_mode_chk push ebx ;PHY Link Status Check movzx eax, [r6040_private.phy_addr] stdcall r6040_phy_read, eax, 1 test eax, 0x4 jnz @f mov eax, 0x8000 ;Link Failed, full duplex @@: ;PHY Chip Auto-Negotiation Status movzx eax, [r6040_private.phy_addr] stdcall r6040_phy_read, eax, 1 test eax, 0x0020 jz .force_mode ;Auto Negotuiation Mode movzx eax, [r6040_private.phy_addr] stdcall r6040_phy_read, eax, 5 mov ebx, eax movzx eax, [r6040_private.phy_addr] stdcall r6040_phy_read, eax, 4 and eax, ebx test eax, 0x140 jz .ret_0 jmp .ret_0x8000 .force_mode: ;Force Mode movzx eax, [r6040_private.phy_addr] stdcall r6040_phy_read, eax, 0 test eax, 0x100 jz .ret_0 .ret_0x8000: mov eax, 0x8000 pop ebx ret .ret_0: xor eax, eax pop ebx ret endp ;*************************************************************************** ; Function ; r6040_rx ; Description ; polls card to see if there is a packet waiting ; ; Currently only supports one descriptor per packet, if packet is fragmented ; between multiple descriptors you will lose part of the packet ;*************************************************************************** r6040_poll: push ebx ecx esi edi xor eax, eax mov [eth_rx_data_len], ax movzx eax, [r6040_private.cur_rx] mov ebx, eax shl ebx, 5 mov cx, [ebx+r6040_rx_ring+r6040_x_head.status] ; Read the descriptor status test cx, DSC_OWNER_MAC jnz .out test cx, DSC_RX_ERR ; Global error status set jz .no_dsc_rx_err ;... jmp .out .no_dsc_rx_err: ; Packet successfully received movzx ecx, [ebx+r6040_rx_ring+r6040_x_head.len] and ecx, 0xFFF sub ecx, 4 ; Do not count the CRC mov [eth_rx_data_len], cx mov esi, [ebx+r6040_rx_ring+r6040_x_head.skb_ptr] push ecx shr ecx, 2 mov edi, Ether_buffer cld rep movsd pop ecx and ecx, 3 rep movsb or [ebx+r6040_rx_ring+r6040_x_head.status], DSC_OWNER_MAC inc [r6040_private.cur_rx] and [r6040_private.cur_rx], RX_RING_SIZE-1 xor eax, eax .out: pop edi esi ecx ebx ret ;*************************************************************************** ; Function ; r6040_transmit ; Description ; Transmits a packet of data via the ethernet card ; Pointer to 48 bit destination address in edi ; Type of packet in bx ; size of packet in ecx ; pointer to packet data in esi ; ;*************************************************************************** r6040_transmit: cmp ecx, MAX_BUF_SIZE jg .out ; packet is too long push edi esi ebx ecx movzx eax, [r6040_private.cur_tx] shl eax, 5 ; DEBUGF 1,"R6040: TX buffer status: 0x%x, eax=%u\n", [eax + r6040_tx_ring + r6040_x_head.status]:4, eax test [r6040_tx_ring + eax + r6040_x_head.status], 0x8000 ; check if buffer is available jz .l3 push ecx esi mov ecx, [timer_ticks] add ecx, 100 .l2: test [r6040_tx_ring + eax + r6040_x_head.status], 0x8000 jz .l5 cmp ecx, [timer_ticks] jb .l4 mov esi, 10 call delay_ms jmp .l2 .l4: pop esi ecx DEBUGF 1,"R6040: Send timeout\n" jmp .out .l5: pop esi ecx .l3: push eax mov esi, edi ; point to the current tx buffer movzx edi, [r6040_private.cur_tx] imul edi, MAX_BUF_SIZE add edi, r6040_txb lea eax, [edi - OS_BASE] ; real buffer address in eax ; copy destination address movsd movsw ; copy source address mov esi, node_addr movsd movsw ; copy packet type mov [edi], bx add edi, 2 mov esi, [esp+8+4] mov ecx, [esp+4] ; copy the packet data push ecx shr ecx, 2 rep movsd pop ecx and ecx, 3 rep movsb pop edi mov ecx, [esp] add ecx, ETH_HLEN cmp cx, ETH_ZLEN jae @f mov cx, ETH_ZLEN @@: mov [r6040_tx_ring + edi + r6040_x_head.len], cx mov [r6040_tx_ring + edi + r6040_x_head.buf], eax mov [r6040_tx_ring + edi + r6040_x_head.status], 0x8000 ; Trigger the MAC to check the TX descriptor mov ax, 0x01 mov edx, [io_addr] add edx, MTPR out dx, ax inc [r6040_private.cur_tx] and [r6040_private.cur_tx], TX_RING_SIZE-1 xor eax, eax pop ecx ebx esi edi .out: ret r6040_mac_address: push eax ecx edx esi edi ; MAC operation register mov ax, 1 mov edx, [io_addr] add edx, MCR1 out dx, ax ; Reset MAC mov ax, 2 mov edx, [io_addr] add edx, MAC_SM out dx, ax ; Reset internal state machine xor ax, ax out dx, ax mov esi, 5 call delay_ms ; Restore MAC Address mov ecx, 3 mov edi, node_addr mov edx, [io_addr] add edx, MID_0L .mac: in ax, dx stosw add edx, 2 dec ecx jnz .mac pop edi esi edx ecx eax ret