0,0 → 1,1754 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2018-2020. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; AR81XX driver for KolibriOS ;; |
;; ;; |
;; based on alx driver from TI-OpenLink ;; |
;; ;; |
;; Written by hidnplayr (hidnplayr@gmail.com) ;; |
;; ;; |
;; GNU GENERAL PUBLIC LICENSE ;; |
;; Version 2, June 1991 ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
format PE DLL native |
entry START |
|
CURRENT_API = 0x0200 |
COMPATIBLE_API = 0x0100 |
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API |
|
MAX_DEVICES = 16 |
|
__DEBUG__ = 1 |
__DEBUG_LEVEL__ = 2 |
|
TX_RING_SIZE = 128 ; RING sizes must be a power of 2 |
RX_RING_SIZE = 128 |
|
RX_BUFFER_SIZE = 1536 |
|
SMB_TIMER = 400 |
IMT = 200 |
ITH_TPD = TX_RING_SIZE / 3 |
|
section '.flat' readable writable executable |
|
include '../proc32.inc' |
include '../struct.inc' |
include '../macros.inc' |
include '../fdo.inc' |
include '../netdrv.inc' |
|
include 'ar81xx.inc' |
|
; Transmit Packet Descriptor |
|
struct alx_tpd |
length dw ? |
vlan_tag dw ? |
word1 dd ? |
addr_l dd ? |
addr_h dd ? |
ends |
|
; Receive Return Descriptor |
|
struct alx_rrd |
word0 dd ? ; IP payload cksum + number of RFDs + start index of RFD-ring |
rss_hash dd ? |
word2 dd ? ; VLAN tag + Protocol ID + RSS Q num + RSS Hash algorithm |
word3 dd ? ; Packet length + status |
ends |
|
; Receive Free Descriptor |
|
struct alx_rfd |
addr_l dd ? |
addr_h dd ? |
ends |
|
|
struct device ETH_DEVICE |
|
io_addr dd ? |
pci_bus dd ? |
pci_dev dd ? |
irq_line dd ? |
pci_rev dd ? |
chip_rev dd ? |
mmio_addr dd ? |
|
; dma_chnl dd ? |
max_dma_chnl dd ? |
|
int_mask dd ? |
rx_ctrl dd ? |
|
rxq_read_idx dd ? |
rxq_write_idx dd ? |
; rxq_rrd_read_idx dd ? |
txq_read_idx dd ? |
txq_write_idx dd ? |
|
rb 0x100 - ($ and 0xff) ; align 256 |
tpd_ring rb ((TX_RING_SIZE*sizeof.alx_tpd+16) and 0xfffffff0) |
rrd_ring rb ((RX_RING_SIZE*sizeof.alx_rrd+16) and 0xfffffff0) |
rfd_ring rb ((RX_RING_SIZE*sizeof.alx_rfd+16) and 0xfffffff0) |
tpd_ring_virt rd TX_RING_SIZE |
rfd_ring_virt rd RX_RING_SIZE |
|
ends |
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; proc START ;; |
;; ;; |
;; (standard driver proc) ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
proc START c, reason:dword, cmdline:dword |
|
cmp [reason], DRV_ENTRY |
jne .fail |
|
DEBUGF 2,"Loading driver\n" |
invoke RegService, my_service, service_proc |
ret |
|
.fail: |
xor eax, eax |
ret |
|
endp |
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; proc SERVICE_PROC ;; |
;; ;; |
;; (standard driver proc) ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
proc service_proc stdcall, ioctl:dword |
|
mov edx, [ioctl] |
mov eax, [edx + IOCTL.io_code] |
|
;------------------------------------------------------ |
|
cmp eax, 0 ;SRV_GETVERSION |
jne @F |
|
cmp [edx + IOCTL.out_size], 4 |
jb .fail |
mov eax, [edx + IOCTL.output] |
mov [eax], dword API_VERSION |
|
xor eax, eax |
ret |
|
;------------------------------------------------------ |
@@: |
cmp eax, 1 ;SRV_HOOK |
jne .fail |
|
cmp [edx + IOCTL.inp_size], 3 ; Data input must be at least 3 bytes |
jb .fail |
|
mov eax, [edx + IOCTL.input] |
cmp byte [eax], 1 ; 1 means device number and bus number (pci) are given |
jne .fail ; other types arent supported for this card yet |
|
; check if the device is already listed |
|
mov esi, device_list |
mov ecx, [devices] |
test ecx, ecx |
jz .firstdevice |
|
; mov eax, [edx + IOCTL.input] ; get the pci bus and device numbers |
mov ax , [eax+1] ; |
.nextdevice: |
mov ebx, [esi] |
cmp al, byte[ebx + device.pci_bus] |
jne @f |
cmp ah, byte[ebx + device.pci_dev] |
je .find_devicenum ; Device is already loaded, let's find it's device number |
@@: |
add esi, 4 |
loop .nextdevice |
|
|
; This device doesnt have its own eth_device structure yet, lets create one |
.firstdevice: |
cmp [devices], MAX_DEVICES ; First check if the driver can handle one more card |
jae .fail |
|
allocate_and_clear ebx, sizeof.device, .fail ; Allocate the buffer for device structure |
|
; Fill in the direct call addresses into the struct |
|
mov [ebx + device.reset], reset |
mov [ebx + device.transmit], transmit |
mov [ebx + device.unload], unload |
mov [ebx + device.name], my_service |
|
; save the pci bus and device numbers |
|
mov eax, [edx + IOCTL.input] |
movzx ecx, byte[eax+1] |
mov [ebx + device.pci_bus], ecx |
movzx ecx, byte[eax+2] |
mov [ebx + device.pci_dev], ecx |
|
; Now, it's time to find the base mmio addres of the PCI device |
|
stdcall PCI_find_mmio, [ebx + device.pci_bus], [ebx + device.pci_dev] ; returns in eax |
test eax, eax |
jz .destroy |
|
; Create virtual mapping of the physical memory |
|
invoke MapIoMem, eax, 10000h, PG_SW+PG_NOCACHE |
mov [ebx + device.mmio_addr], eax |
|
; We've found the mmio address, find IRQ now |
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.interrupt_line |
and eax, 0xff |
mov [ebx + device.irq_line], eax |
|
DEBUGF 1,"Hooking into device, dev:%x, bus:%x, irq:%x, addr:%x\n",\ |
[ebx + device.pci_dev]:1, [ebx + device.pci_bus]:1, [ebx + device.irq_line]:1, [ebx + device.mmio_addr]:8 |
|
; Ok, the eth_device structure is ready, let's probe the device |
|
mov eax, [devices] ; Add the device structure to our device list |
mov [device_list+4*eax], ebx ; (IRQ handler uses this list to find device) |
inc [devices] ; |
|
call probe ; this function will output in eax |
test eax, eax |
jnz .err2 |
|
DEBUGF 2,"Initialised OK\n" |
|
mov [ebx + device.type], NET_TYPE_ETH |
invoke NetRegDev |
|
cmp eax, -1 |
je .destroy |
|
ret |
|
; If the device was already loaded, find the device number and return it in eax |
|
.find_devicenum: |
DEBUGF 2,"Trying to find device number of already registered device\n" |
invoke NetPtrToNum ; This kernel procedure converts a pointer to device struct in ebx |
; into a device number in edi |
mov eax, edi ; Application wants it in eax instead |
DEBUGF 2,"Kernel says: %u\n", eax |
ret |
|
; If an error occured, remove all allocated data and exit (returning -1 in eax) |
|
.destroy: |
; todo: reset device into virgin state |
|
.err2: |
dec [devices] |
.err: |
invoke KernelFree, ebx |
.fail: |
DEBUGF 2, "Failed to load\n" |
or eax, -1 |
ret |
|
;------------------------------------------------------ |
endp |
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; |
;; ;; |
;; Actual Hardware dependent code starts here ;; |
;; ;; |
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; |
|
|
align 4 |
unload: |
; TODO: (in this particular order) |
; |
; - Stop the device |
; - Detach int handler |
; - Remove device from local list (device_list) |
; - call unregister function in kernel |
; - Remove all allocated structures and buffers the card used |
|
or eax, -1 |
ret |
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; |
;; probe: enables the device (if it really is AR81XX) |
;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
align 4 |
probe: |
DEBUGF 1,"Probing\n" |
|
; Make the device a bus master |
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command |
or al, PCI_CMD_MASTER + PCI_CMD_MMIO + PCI_CMD_PIO |
and ax, not(PCI_CMD_INTX_DISABLE) |
invoke PciWrite16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command, eax |
|
; get revision id. |
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.revision_id |
and eax, 0xff |
mov [ebx + device.pci_rev], eax |
shr al, ALX_PCI_REVID_SHIFT |
mov [ebx + device.chip_rev], eax |
|
DEBUGF 1,"revision: %u\n", al |
|
;;; call alx_reset_pcie |
|
mov ecx, (ALX_PMCTRL_L0S_EN or ALX_PMCTRL_L1_EN or ALX_PMCTRL_ASPM_FCEN) |
call alx_enable_aspm |
|
call alx_reset_phy |
|
call alx_reset_mac |
|
call alx_get_perm_macaddr |
|
align 4 |
reset: |
|
DEBUGF 1,"Resetting\n" |
|
; alx init_sw |
|
call alx_identify_hw |
|
; mov eax, [ebx + device.max_dma_chnl] |
; mov [ebx + device.dma_chnl], eax |
|
mov [ebx + device.int_mask], ALX_ISR_MISC |
mov [ebx + device.rx_ctrl], ALX_MAC_CTRL_WOLSPED_SWEN or ALX_MAC_CTRL_MHASH_ALG_HI5B or ALX_MAC_CTRL_BRD_EN or ALX_MAC_CTRL_PCRCE or ALX_MAC_CTRL_CRCE or ALX_MAC_CTRL_RXFC_EN or ALX_MAC_CTRL_TXFC_EN or (7 shl ALX_MAC_CTRL_PRMBLEN_SHIFT) |
|
call alx_alloc_rings |
|
call alx_configure |
|
call alx_request_irq |
|
; attach interrupt handler |
|
mov eax, [ebx + device.irq_line] |
DEBUGF 1,"Attaching int handler to irq %x\n", eax:1 |
invoke AttachIntHandler, eax, int_handler, ebx |
test eax, eax |
jnz @f |
DEBUGF 2,"Could not attach int handler!\n" |
or eax, -1 |
ret |
@@: |
|
; Clear old interrupts |
mov edi, [ebx + device.mmio_addr] |
mov eax, not ALX_ISR_DIS |
mov [edi + ALX_ISR], eax |
|
call alx_irq_enable |
|
; Set the mtu, kernel will be able to send now |
mov [ebx + device.mtu], 1514 |
|
call alx_check_link |
|
DEBUGF 1,"Reset ok\n" |
xor eax, eax |
ret |
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Transmit ;; |
;; ;; |
;; In: pointer to device structure in ebx ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
;; alx_start_xmit |
;; alx_map_tx_skb |
|
proc transmit stdcall bufferptr |
|
pushf |
cli |
|
mov esi, [bufferptr] |
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [esi + NET_BUFF.length] |
lea eax, [esi + NET_BUFF.data] |
DEBUGF 1,"To: %x-%x-%x-%x-%x-%x From: %x-%x-%x-%x-%x-%x Type:%x%x\n",\ |
[eax+00]:2,[eax+01]:2,[eax+02]:2,[eax+03]:2,[eax+04]:2,[eax+05]:2,\ |
[eax+06]:2,[eax+07]:2,[eax+08]:2,[eax+09]:2,[eax+10]:2,[eax+11]:2,\ |
[eax+13]:2,[eax+12]:2 |
|
cmp [esi + NET_BUFF.length], 1514 |
ja .fail |
cmp [esi + NET_BUFF.length], 60 |
jb .fail |
|
; Program the descriptor |
mov edi, [ebx + device.txq_write_idx] |
DEBUGF 1, "Using TPD: %u\n", edi |
mov dword[ebx + device.tpd_ring_virt + edi*4], esi |
shl edi, 4 |
lea edi, [ebx + device.tpd_ring + edi] |
mov eax, esi |
add eax, [esi + NET_BUFF.offset] |
invoke GetPhysAddr |
mov [edi + alx_tpd.addr_l], eax |
mov [edi + alx_tpd.addr_h], 0 |
|
mov ecx, [esi + NET_BUFF.length] |
mov [edi + alx_tpd.length], cx |
|
mov [edi + alx_tpd.word1], 1 shl TPD_EOP_SHIFT |
|
; Update Producer Index |
mov eax, [ebx + device.txq_write_idx] |
inc eax |
and eax, TX_RING_SIZE - 1 |
mov [ebx + device.txq_write_idx], eax |
|
mov edi, [ebx + device.mmio_addr] |
mov word[edi + ALX_TPD_PRI0_PIDX], ax |
|
; Update stats |
inc [ebx + device.packets_tx] |
mov eax, [esi + NET_BUFF.length] |
add dword[ebx + device.bytes_tx], eax |
adc dword[ebx + device.bytes_tx + 4], 0 |
|
popf |
xor eax, eax |
ret |
|
.fail: |
DEBUGF 2,"Send failed\n" |
invoke NetFree, [bufferptr] |
popf |
or eax, -1 |
ret |
|
endp |
|
|
|
;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Interrupt handler ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;; |
|
align 4 |
int_handler: |
|
push ebx esi edi |
|
DEBUGF 1,"int\n" |
|
; Find pointer of device wich made IRQ occur |
|
mov ecx, [devices] |
test ecx, ecx |
jz .nothing |
mov esi, device_list |
.nextdevice: |
mov ebx, [esi] |
mov edi, [ebx + device.mmio_addr] |
mov eax, [edi + ALX_ISR] |
test eax, eax |
jnz .got_it |
.continue: |
add esi, 4 |
dec ecx |
jnz .nextdevice |
.nothing: |
pop edi esi ebx |
xor eax, eax |
|
ret ; If no device was found, abort |
|
; At this point, test for all possible reasons, and handle accordingly |
|
.got_it: |
or eax, ALX_ISR_DIS |
mov [edi + ALX_ISR], eax ; ACK interrupt |
DEBUGF 1,"Device: %x Status: %x\n", ebx, eax |
|
test eax, ALX_ISR_TX_Q0 |
jz .no_tx |
DEBUGF 1,"TX interrupt\n" |
pusha |
call alx_clean_tx_irq |
popa |
.no_tx: |
|
test eax, ALX_ISR_RX_Q0 |
jz .no_rx |
DEBUGF 1,"RX interrupt\n" |
pusha |
call alx_clean_rx_irq |
popa |
|
.no_rx: |
test eax, ALX_ISR_PHY |
jz .no_phy |
DEBUGF 1,"PHY interrupt\n" |
pusha |
; TODO: queue link check and disable this interrupt cause meanwhile?? |
call alx_check_link |
popa |
|
.no_phy: |
mov dword[edi + ALX_ISR], 0 |
pop edi esi ebx |
ret |
|
|
align 16 |
alx_identify_hw: |
|
cmp [ebx + device.chip_rev], ALX_REV_C0 |
ja .einval |
|
mov [ebx + device.max_dma_chnl], 2 |
cmp [ebx + device.chip_rev], ALX_REV_B0 |
jb @f |
mov [ebx + device.max_dma_chnl], 4 |
@@: |
xor eax, eax |
ret |
|
.einval: |
xor eax, eax |
dec eax |
ret |
|
|
|
align 16 |
alx_clean_tx_irq: |
|
mov eax, [ebx + device.txq_read_idx] |
movzx ecx, word[edi + ALX_TPD_PRI0_CIDX] |
|
.loop: |
cmp eax, ecx |
je .done |
|
DEBUGF 1,"Cleaning TX desc %u buffer 0x%x\n", eax, [ebx + device.tpd_ring_virt + eax*4] |
push eax ecx |
invoke NetFree, [ebx + device.tpd_ring_virt + eax*4] |
pop ecx eax |
|
inc eax |
and eax, TX_RING_SIZE-1 |
jmp .loop |
.done: |
mov [ebx + device.txq_read_idx], eax |
|
ret |
|
|
align 16 |
alx_clean_rx_irq: |
|
mov ecx, [ebx + device.rxq_read_idx] |
.loop: |
shl ecx, 2 |
lea esi, [ebx + device.rrd_ring + ecx*4] |
test [esi + alx_rrd.word3], 1 shl RRD_UPDATED_SHIFT |
jz .done |
and [esi + alx_rrd.word3], not(1 shl RRD_UPDATED_SHIFT) |
DEBUGF 1,"RRD=%u updated\n", [ebx + device.rxq_read_idx] |
|
mov eax, [esi + alx_rrd.word0] |
shr eax, RRD_SI_SHIFT |
and eax, RRD_SI_MASK |
cmp eax, [ebx + device.rxq_read_idx] |
; jne .error |
DEBUGF 1,"RFD=%u\n", eax |
|
mov eax, [esi + alx_rrd.word0] |
shr eax, RRD_NOR_SHIFT |
and eax, RRD_NOR_MASK |
cmp eax, 1 |
; jne .error |
|
mov eax, [esi + alx_rrd.word3] |
; shr eax, RRD_PKTLEN_SHIFT |
and eax, RRD_PKTLEN_MASK |
sub eax, 4 ;;;;; |
mov edx, [ebx + device.rfd_ring_virt + ecx] |
DEBUGF 1,"Received %u bytes in buffer 0x%x\n", eax, edx |
|
mov [edx + NET_BUFF.length], eax |
mov [edx + NET_BUFF.device], ebx |
mov [edx + NET_BUFF.offset], NET_BUFF.data |
|
; Update stats |
add dword[ebx + device.bytes_rx], eax |
adc dword[ebx + device.bytes_rx + 4], 0 |
inc [ebx + device.packets_rx] |
|
; Allocate new descriptor |
push esi ecx edx |
invoke NetAlloc, RX_BUFFER_SIZE+NET_BUFF.data |
pop edx ecx esi |
test eax, eax |
; jz .out_of_mem |
mov [ebx + device.rfd_ring_virt + ecx], eax |
add eax, NET_BUFF.data |
invoke GetPhysAddr |
mov dword[ebx + device.rfd_ring + ecx*2], eax |
|
push ecx ebx |
push .retaddr |
push edx |
jmp [EthInput] |
.retaddr: |
pop ebx ecx |
|
shr ecx, 2 |
inc ecx |
and ecx, RX_RING_SIZE-1 |
jmp .loop |
|
.done: |
shr ecx, 2 |
mov [ebx + device.rxq_read_idx], ecx |
|
; Update producer index |
mov esi, [ebx + device.mmio_addr] |
mov [esi + ALX_RFD_PIDX], cx |
|
ret |
|
|
align 16 |
; ecx = additional bit flags (ALX_PMCTRL_L0S_EN, ALX_PMCTRL_L1_EN, ALX_PMCTRL_ASPM_FCEN) |
alx_enable_aspm: |
|
DEBUGF 1,"alx_enable_aspm (0x%x)\n", ecx |
|
mov esi, [ebx + device.mmio_addr] |
mov eax, dword[esi + ALX_PMCTRL] |
|
and eax, not(ALX_PMCTRL_LCKDET_TIMER_MASK shl ALX_PMCTRL_LCKDET_TIMER_SHIFT) |
or eax, (ALX_PMCTRL_LCKDET_TIMER_DEF shl ALX_PMCTRL_LCKDET_TIMER_SHIFT) |
|
or eax, (ALX_PMCTRL_RCVR_WT_1US or ALX_PMCTRL_L1_CLKSW_EN or ALX_PMCTRL_L1_SRDSRX_PWD) |
|
and eax, not(ALX_PMCTRL_L1REQ_TO_MASK shl ALX_PMCTRL_L1REQ_TO_SHIFT) |
or eax, (ALX_PMCTRL_L1REG_TO_DEF shl ALX_PMCTRL_L1REQ_TO_SHIFT) |
|
and eax, not(ALX_PMCTRL_L1_TIMER_MASK shl ALX_PMCTRL_L1_TIMER_SHIFT) |
or eax, (ALX_PMCTRL_L1_TIMER_16US shl ALX_PMCTRL_L1_TIMER_SHIFT) |
|
and eax, not(ALX_PMCTRL_L1_SRDS_EN or ALX_PMCTRL_L1_SRDSPLL_EN or ALX_PMCTRL_L1_BUFSRX_EN or ALX_PMCTRL_SADLY_EN or ALX_PMCTRL_HOTRST_WTEN or ALX_PMCTRL_L0S_EN or ALX_PMCTRL_L1_EN or ALX_PMCTRL_ASPM_FCEN or ALX_PMCTRL_TXL1_AFTER_L0S or ALX_PMCTRL_RXL1_AFTER_L0S) |
|
cmp [ebx + device.chip_rev], ALX_REV_A1 |
ja @f |
test [ebx + device.pci_rev], 1 |
jz @f |
or eax, ALX_PMCTRL_L1_SRDS_EN or ALX_PMCTRL_L1_SRDSPLL_EN |
@@: |
|
or eax, ecx |
mov dword[esi + ALX_PMCTRL], eax |
|
ret |
|
align 16 |
alx_reset_mac: |
|
DEBUGF 1, "reset mac\n" |
|
; disable all interrupts, RXQ/TXQ |
mov esi, [ebx + device.mmio_addr] |
mov dword[esi + ALX_MSIX_MASK], 0xffffffff |
mov dword[esi + ALX_IMR], 0x0 |
mov dword[esi + ALX_ISR], ALX_ISR_DIS |
|
call alx_stop_mac |
|
; mac reset workaround |
mov dword[esi + ALX_RFD_PIDX], 1 |
|
; disable l0s/l1 before mac reset on some chips |
cmp [ebx + device.chip_rev], ALX_REV_A1 |
ja @f |
test [ebx + device.pci_rev], 1 ; Card reader function? FIXME: according register definitions, this should be bit 1 ISO 0 |
jz @f |
mov eax, [esi + ALX_PMCTRL] |
mov edx, eax |
and eax, not(ALX_PMCTRL_L1_EN or ALX_PMCTRL_L0S_EN) |
mov [esi + ALX_PMCTRL], eax |
@@: |
|
; reset whole mac safely |
mov eax, [esi + ALX_MASTER] |
or eax, ALX_MASTER_DMA_MAC_RST + ALX_MASTER_OOB_DIS |
mov [esi + ALX_MASTER], eax |
|
; make sure it's real idle |
push esi ecx edx |
xor esi, esi |
inc esi |
invoke Sleep ; FIXME |
pop edx ecx esi |
|
mov ecx, ALX_DMA_MAC_RST_TO |
.loop1: |
mov eax, dword[esi + ALX_RFD_PIDX] |
test eax, eax |
jz @f |
dec ecx |
jnz .loop1 |
jmp .error |
@@: |
.loop2: |
mov eax, dword[esi + ALX_MASTER] |
test eax, ALX_MASTER_DMA_MAC_RST |
jz @f |
dec ecx |
jnz .loop2 |
jmp .error |
@@: |
|
; restore l0s/l1 |
cmp [ebx + device.chip_rev], ALX_REV_A1 |
ja @f |
test [ebx + device.pci_rev], 1 ; Card reader function? FIXME: according register definitions, this should be bit 1 ISO 0 |
jz @f |
or eax, ALX_MASTER_PCLKSEL_SRDS |
mov [esi + ALX_MASTER], eax |
|
mov [esi + ALX_PMCTRL], edx |
@@: |
|
call alx_reset_osc |
|
; clear Internal OSC settings, switching OSC by hw itself, disable isolate for rev A devices |
|
mov eax, [esi + ALX_MISC3] |
and eax, not (ALX_MISC3_25M_BY_SW) |
or eax, ALX_MISC3_25M_NOTO_INTNL |
mov [esi + ALX_MISC3], eax |
|
mov eax, [esi + ALX_MISC] |
and eax, not (ALX_MISC_INTNLOSC_OPEN) |
|
cmp [ebx + device.chip_rev], ALX_REV_A1 |
ja @f |
and eax, not ALX_MISC_ISO_EN |
@@: |
mov [esi + ALX_MISC], eax |
|
push esi |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(20); |
pop esi |
|
; driver control speed/duplex, hash-alg |
mov eax, [ebx + device.rx_ctrl] |
mov [esi + ALX_MAC_CTRL], eax |
|
mov eax, dword[esi + ALX_SERDES] |
or eax, ALX_SERDES_MACCLK_SLWDWN or ALX_SERDES_PHYCLK_SLWDWN |
mov dword[esi + ALX_SERDES], eax |
|
DEBUGF 1, "OK\n" |
xor eax, eax |
ret |
|
.error: |
DEBUGF 1, "error\n" |
xor eax, eax |
dec eax |
ret |
|
|
align 16 |
alx_reset_phy: |
|
DEBUGF 1, "Reset phy\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
;; TODO |
|
; set phy interrupt mask |
stdcall alx_read_phy_reg, 0, ALX_MII_IER |
or eax, ALX_IER_LINK_UP or ALX_IER_LINK_DOWN |
stdcall alx_write_phy_reg, 0, ALX_MII_IER , eax |
|
DEBUGF 1, "OK\n" |
xor eax, eax |
ret |
|
.error: |
DEBUGF 1, "error\n" |
xor eax, eax |
dec eax |
ret |
|
|
;align 16 |
;alx_enable_osc: |
; |
; mov esi, [ebx + device.mmio_addr] |
; |
;; rising edge |
; mov eax, dword[esi + ALX_MISC] |
; and eax, not ALX_MISC_INTNLOSC_OPEN |
; mov dword[esi + ALX_MISC], eax |
; or eax, ALX_MISC_INTNLOSC_OPEN |
; mov dword[esi + ALX_MISC], eax |
; |
; ret |
|
|
align 16 |
alx_reset_osc: |
|
mov esi, [ebx + device.mmio_addr] |
|
; clear Internal OSC settings, switching OSC by hw itself |
mov eax, dword[esi + ALX_MISC3] |
and eax, not ALX_MISC3_25M_BY_SW |
or eax, ALX_MISC3_25M_NOTO_INTNL |
mov dword[esi + ALX_MISC3], eax |
|
; clk from chipset may be unstable 1s after de-assert of |
; PERST, driver need re-calibrate before enter Sleep for WoL |
mov eax, dword[esi + ALX_MISC] |
cmp [ebx + device.chip_rev], ALX_REV_B0 |
jb .rev_a |
|
; restore over current protection def-val, this val could be reset by MAC-RST |
and eax, not (ALX_MISC_PSW_OCP_MASK shl ALX_MISC_PSW_OCP_SHIFT) |
or eax, ALX_MISC_PSW_OCP_DEF shl ALX_MISC_PSW_OCP_SHIFT |
; a 0->1 change will update the internal val of osc |
and eax, not ALX_MISC_INTNLOSC_OPEN |
mov dword[esi + ALX_MISC], eax |
or eax, ALX_MISC_INTNLOSC_OPEN |
mov dword[esi + ALX_MISC], eax |
|
; hw will automatically dis OSC after cab |
mov eax, dword[esi + ALX_MSIC2] |
and eax, not ALX_MSIC2_CALB_START |
mov dword[esi + ALX_MSIC2], eax |
or eax, ALX_MSIC2_CALB_START |
mov dword[esi + ALX_MSIC2], eax |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(20) |
pop ecx esi |
|
ret |
|
.rev_a: |
|
; disable isolate for rev A devices |
and eax, not ( ALX_MISC_ISO_EN) |
or eax, ALX_MISC_INTNLOSC_OPEN |
mov dword[esi + ALX_MISC], eax |
and eax, not ALX_MISC_INTNLOSC_OPEN |
mov dword[esi + ALX_MISC], eax |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(20) |
pop ecx esi |
|
ret |
|
align 16 |
alx_read_macaddr: |
|
mov esi, [ebx + device.mmio_addr] |
mov eax, dword[esi + ALX_STAD0] |
bswap eax |
mov dword[ebx + device.mac + 2], eax |
mov ax, word[esi + ALX_STAD1] |
xchg al, ah |
mov word[ebx + device.mac], ax |
|
DEBUGF 1,"MAC = %x-%x-%x-%x-%x-%x\n",\ |
[ebx + device.mac+0]:2, [ebx + device.mac+1]:2, [ebx + device.mac+2]:2, [ebx + device.mac+3]:2, [ebx + device.mac+4]:2, [ebx + device.mac+5]:2 |
|
; check if it is a valid MAC |
cmp dword[ebx + device.mac], 0x0 |
jne @f |
cmp word[ebx + device.mac + 4], 0x0 |
je .invalid |
@@: |
cmp dword[ebx + device.mac], 0xffffffff |
jne @f |
cmp word[ebx + device.mac + 4], 0xffff |
je .invalid |
@@: |
; TODO: check if it's not a multicast |
xor eax, eax |
ret |
|
.invalid: |
xor eax, eax |
inc eax |
ret |
|
|
align 16 |
alx_get_perm_macaddr: |
|
; try to get it from register first |
call alx_read_macaddr |
test eax, eax |
jz .done |
|
; try to load from efuse |
mov esi, [ebx + device.mmio_addr] |
mov ecx, ALX_SLD_MAX_TO |
.loop1: |
mov eax, dword[esi + ALX_SLD] |
test eax, ALX_SLD_STAT or ALX_SLD_START |
jz @f |
|
dec ecx |
jz .error |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep |
pop ecx esi |
jmp .loop1 |
@@: |
or eax, ALX_SLD_START |
mov dword[esi + ALX_SLD], eax |
|
mov ecx, ALX_SLD_MAX_TO |
.loop2: |
mov eax, dword[esi + ALX_SLD] |
test eax, ALX_SLD_START |
jz @f |
|
dec ecx |
jz .error |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep |
pop ecx esi |
jmp .loop2 |
@@: |
|
call alx_read_macaddr |
test eax, eax |
jz .done |
|
; try to load from flash/eeprom (if present) |
set_io [ebx + device.io_addr], 0 |
set_io [ebx + device.io_addr], ALX_EFLD |
in eax, dx |
test eax, ALX_EFLD_F_EXIST or ALX_EFLD_E_EXIST |
jz .error |
|
mov ecx, ALX_SLD_MAX_TO |
.loop3: |
in eax, dx |
test eax, ALX_EFLD_STAT or ALX_EFLD_START |
jz @f |
|
dec ecx |
jz .error |
|
push esi edx ecx |
xor esi, esi |
inc esi |
invoke Sleep |
pop ecx edx esi |
jmp .loop3 |
@@: |
or eax, ALX_EFLD_START |
out dx, eax |
|
mov ecx, ALX_SLD_MAX_TO |
.loop4: |
in eax, dx |
test eax, ALX_EFLD_START |
jz @f |
|
dec ecx |
jz .error |
|
push esi edx ecx |
xor esi, esi |
inc esi |
invoke Sleep |
pop ecx edx esi |
jmp .loop4 |
@@: |
|
call alx_read_macaddr |
test eax, eax |
jz .done |
|
.error: |
DEBUGF 1, "error obtaining MAC\n" |
xor eax, eax |
dec eax |
ret |
|
.done: |
DEBUGF 1, "MAC OK\n" |
xor eax, eax |
ret |
|
align 16 |
alx_stop_mac: |
|
DEBUGF 1,"alx_stop_mac\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
mov eax, dword[esi + ALX_RXQ0] |
and eax, not ALX_RXQ0_EN |
mov dword[esi + ALX_RXQ0], eax |
|
mov eax, dword[esi + ALX_TXQ0] |
and eax, not ALX_TXQ0_EN |
mov dword[esi + ALX_TXQ0], eax |
|
push esi |
xor esi, esi |
inc esi |
invoke Sleep ; FIME: udelay(40) |
pop esi |
|
mov eax, [ebx + device.rx_ctrl] |
and eax, not(ALX_MAC_CTRL_TX_EN or ALX_MAC_CTRL_RX_EN) |
mov [ebx + device.rx_ctrl], eax |
mov [esi + ALX_MAC_CTRL], eax |
|
mov ecx, ALX_DMA_MAC_RST_TO |
.loop: |
mov eax, [esi + ALX_MAC_STS] |
test eax, ALX_MAC_STS_IDLE |
jz .done |
|
push esi |
xor esi, esi |
inc esi |
invoke Sleep ; FIME: udelay(10) |
pop esi |
|
dec ecx |
jnz .loop |
|
DEBUGF 1,"alx_stop_mac timeout!\n" |
xor eax, eax |
dec eax |
ret |
|
.done: |
DEBUGF 1,"alx_stop_mac ok\n" |
xor eax, eax |
ret |
|
|
align 16 |
alx_start_mac: |
|
DEBUGF 1,"alx_start_mac\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
mov eax, dword[esi + ALX_RXQ0] |
or eax, ALX_RXQ0_EN |
mov dword[esi + ALX_RXQ0], eax |
|
mov eax, dword[esi + ALX_TXQ0] |
or eax, ALX_TXQ0_EN |
mov dword[esi + ALX_TXQ0], eax |
|
mov eax, [ebx + device.rx_ctrl] |
or eax, ALX_MAC_CTRL_TX_EN or ALX_MAC_CTRL_RX_EN |
and eax, not ALX_MAC_CTRL_FULLD |
test [ebx + device.state], ETH_LINK_FD |
jz @f |
or eax, ALX_MAC_CTRL_FULLD |
@@: |
and eax, not (ALX_MAC_CTRL_SPEED_MASK shl ALX_MAC_CTRL_SPEED_SHIFT) |
test [ebx + device.state], ETH_LINK_1G |
jz .10_100 |
or eax, (ALX_MAC_CTRL_SPEED_1000 shl ALX_MAC_CTRL_SPEED_SHIFT) |
|
mov [ebx + device.rx_ctrl], eax |
mov [esi + ALX_MAC_CTRL], eax |
|
ret |
|
.10_100: |
or eax, (ALX_MAC_CTRL_SPEED_10_100 shl ALX_MAC_CTRL_SPEED_SHIFT) |
|
mov [ebx + device.rx_ctrl], eax |
mov [esi + ALX_MAC_CTRL], eax |
|
ret |
|
|
|
align 16 |
alx_init_ring_ptrs: |
|
DEBUGF 1,"alx_init_ring_ptrs\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
; Receive descriptors |
mov [ebx + device.rxq_read_idx], 0 |
mov [ebx + device.rxq_write_idx], 0 |
; mov [ebx + device.rxq_rrd_read_idx], 0 |
|
mov dword[esi + ALX_RX_BASE_ADDR_HI], 0 |
|
lea eax, [ebx + device.rrd_ring] |
invoke GetPhysAddr |
mov dword[esi + ALX_RRD_ADDR_LO], eax |
mov dword[esi + ALX_RRD_RING_SZ], RX_RING_SIZE |
|
lea eax, [ebx + device.rfd_ring] |
invoke GetPhysAddr |
mov dword[esi + ALX_RFD_ADDR_LO], eax |
mov dword[esi + ALX_RFD_RING_SZ], RX_RING_SIZE |
mov dword[esi + ALX_RFD_BUF_SZ], RX_BUFFER_SIZE |
|
; Transmit descriptors |
mov [ebx + device.txq_read_idx], 0 |
mov [ebx + device.txq_write_idx], 0 |
|
mov dword[esi + ALX_TX_BASE_ADDR_HI], 0 |
|
lea eax, [ebx + device.tpd_ring] |
invoke GetPhysAddr |
mov dword[esi + ALX_TPD_PRI0_ADDR_LO], eax |
mov dword[esi + ALX_TPD_RING_SZ], TX_RING_SIZE |
|
; Load these pointers into the chip |
mov dword[esi + ALX_SRAM9], ALX_SRAM_LOAD_PTR |
|
xor eax, eax |
|
ret |
|
|
align 16 |
alx_alloc_descriptors: |
|
DEBUGF 1,"alx_alloc_descriptors\n" |
|
; physical tx/rx ring descriptors |
|
; alx->descmem.size = sizeof.tx_desc * TX_RING_SIZE + sizeof.rx_desc * RX_RING_SIZE + sizeof(struct alx_rfd) * RX_RING_SIZE; |
; alx->descmem.virt = dma_zalloc_coherent(&alx->hw.pdev->dev, alx->descmem.size, &alx->descmem.dma, GFP_KERNEL); |
; if (!alx->descmem.virt) |
; goto out_free; |
; |
; alx->txq.tpd = (void *)alx->descmem.virt; |
; alx->txq.tpd_dma = alx->descmem.dma; |
|
; alignment requirement for next block |
; BUILD_BUG_ON(tx_desc.sizeof % 8); |
; |
; alx->rxq.rrd = (void *)((u8 *)alx->descmem.virt + tx_desc.sizeof * TX_RING_SIZE); |
; alx->rxq.rrd_dma = alx->descmem.dma + sizeof.tx_desc * TX_RING_SIZE; |
; |
; alignment requirement for next block |
; BUILD_BUG_ON(rx_desc.sizeof % 8); |
; |
; alx->rxq.rfd = (void *)((u8 *)alx->descmem.virt + sizeof.tx_desx * TX_RING_SIZE + sizeof.rx_desc * RX_RING_SIZE); |
; alx->rxq.rfd_dma = alx->descmem.dma + sizeof.tx_desc * TX_RING_SIZE + sizeof.rx_desc * RX_RING_SIZE; |
|
xor eax, eax |
ret |
|
|
align 16 |
alx_alloc_rings: |
|
DEBUGF 1,"alx_alloc_rings\n" |
|
call alx_alloc_descriptors |
test eax, eax |
jnz .ret_err |
|
and [ebx + device.int_mask], not ALX_ISR_ALL_QUEUES |
or [ebx + device.int_mask], ALX_ISR_TX_Q0 or ALX_ISR_RX_Q0 |
; netif_napi_add(alx->dev, &alx->napi, alx_poll, 64); |
|
call alx_reinit_rings |
.ret_err: |
ret |
|
|
align 16 |
alx_reinit_rings: |
|
DEBUGF 1,"alx_reinit_rings\n" |
|
call alx_free_rx_ring |
call alx_init_ring_ptrs |
call alx_refill_rx_ring |
|
ret |
|
|
align 16 |
alx_refill_rx_ring: |
|
DEBUGF 1,"alx_refill_rx_ring\n" |
|
mov ecx, [ebx + device.rxq_write_idx] |
.loop: |
cmp dword[ebx + device.rfd_ring+ecx*sizeof.alx_rfd + alx_rfd.addr_l], 0 |
jne .done |
|
invoke NetAlloc, NET_BUFF.data+RX_BUFFER_SIZE |
test eax, eax |
jz .done |
mov [ebx + device.rfd_ring_virt + ecx*4], eax |
add eax, NET_BUFF.data |
invoke GetPhysAddr |
mov dword[ebx + device.rfd_ring+ecx*sizeof.alx_rfd + alx_rfd.addr_l], eax |
mov dword[ebx + device.rfd_ring+ecx*sizeof.alx_rfd + alx_rfd.addr_h], 0 |
|
mov eax, ecx |
inc ecx |
and ecx, RX_RING_SIZE - 1 |
|
cmp ecx, [ebx + device.rxq_read_idx] |
jne .loop |
|
mov ecx, eax |
|
.done: |
cmp ecx, [ebx + device.rxq_write_idx] |
je .none |
|
mov [ebx + device.rxq_write_idx], ecx |
mov esi, [ebx + device.mmio_addr] |
mov [esi + ALX_RFD_PIDX], cx |
|
.none: |
xor eax, eax |
|
ret |
|
|
align 16 |
alx_free_rx_ring: |
|
DEBUGF 1,"alx_free_rx_ring\n" |
|
xor ecx, ecx |
.loop: |
mov eax, [ebx + device.rfd_ring_virt + ecx*4] |
test eax, eax |
jz .next |
|
invoke NetFree, eax |
|
xor eax, eax |
mov dword[ebx + device.rfd_ring+ecx*sizeof.alx_rfd + alx_rfd.addr_l], eax |
mov dword[ebx + device.rfd_ring+ecx*sizeof.alx_rfd + alx_rfd.addr_h], eax |
mov [ebx + device.rfd_ring_virt + ecx*4], eax |
|
.next: |
inc ecx |
cmp ecx, RX_RING_SIZE |
jb .loop |
|
ret |
|
|
align 16 |
alx_configure: |
|
DEBUGF 1,"alx_configure\n" |
|
call alx_configure_basic |
call alx_disable_rss |
call __alx_set_rx_mode |
|
mov esi, [ebx + device.mmio_addr] |
mov eax, [ebx + device.rx_ctrl] |
mov [esi + ALX_MAC_CTRL], eax |
|
xor eax, eax |
ret |
|
|
align 16 |
alx_irq_enable: |
|
DEBUGF 1,"alx_irq_enable\n" |
|
mov esi, [ebx + device.mmio_addr] |
mov dword[esi + ALX_ISR], 0 |
mov eax, [ebx + device.int_mask] |
mov [esi + ALX_IMR], eax |
|
call alx_post_write |
|
ret |
|
align 16 |
alx_irq_disable: |
|
DEBUGF 1,"alx_irq_disable\n" |
|
mov esi, [ebx + device.mmio_addr] |
mov dword[esi + ALX_ISR], ALX_ISR_DIS |
mov dword[esi + ALX_IMR], 0 |
|
call alx_post_write |
|
ret |
|
|
align 16 |
alx_post_write: |
|
push eax |
mov esi, [ebx + device.mmio_addr] |
mov eax, [esi] |
pop eax |
|
ret |
|
|
align 16 |
alx_configure_basic: |
|
DEBUGF 1,"alx_configure_basic\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
;;; call alx_set_macaddr |
|
mov dword[esi + ALX_CLK_GATE], ALX_CLK_GATE_ALL |
|
; idle timeout to switch clk_125M |
|
cmp [ebx + device.chip_rev], ALX_REV_B0 |
jb @f |
mov dword[esi + ALX_IDLE_DECISN_TIMER], ALX_IDLE_DECISN_TIMER_DEF |
@@: |
|
mov dword[esi + ALX_SMB_TIMER], SMB_TIMER * 500 |
|
mov eax, [esi + ALX_MASTER] |
or eax, ALX_MASTER_IRQMOD2_EN or ALX_MASTER_IRQMOD1_EN or ALX_MASTER_SYSALVTIMER_EN |
mov [esi + ALX_MASTER], eax |
|
mov dword[esi + ALX_IRQ_MODU_TIMER], (IMT / 2) shl ALX_IRQ_MODU_TIMER1_SHIFT |
|
; intr re-trig timeout |
mov dword[esi + ALX_INT_RETRIG], ALX_INT_RETRIG_TO |
|
; tpd threshold to trig int |
mov dword[esi + ALX_TINT_TPD_THRSHLD], ITH_TPD |
mov dword[esi + ALX_TINT_TIMER], IMT |
|
mov dword[esi + ALX_MTU], RX_BUFFER_SIZE + 8 ;;;; |
|
mov dword[esi + ALX_TXQ1], (((RX_BUFFER_SIZE + 8 + 7) shr 3) or ALX_TXQ1_ERRLGPKT_DROP_EN) |
|
; rxq, flow control |
|
; TODO set ALX_RXQ2 |
|
; RXQ0 |
mov eax, (ALX_RXQ0_NUM_RFD_PREF_DEF shl ALX_RXQ0_NUM_RFD_PREF_SHIFT) \ |
or (ALX_RXQ0_RSS_MODE_DIS shl ALX_RXQ0_RSS_MODE_SHIFT) \ |
or (ALX_RXQ0_IDT_TBL_SIZE_DEF shl ALX_RXQ0_IDT_TBL_SIZE_SHIFT) \ |
or ALX_RXQ0_RSS_HSTYP_ALL or ALX_RXQ0_RSS_HASH_EN or ALX_RXQ0_IPV6_PARSE_EN |
|
test [ebx + device.pci_dev], 1 ;;; FIXME: is gigabit device? |
jz @f |
or eax, ALX_RXQ0_ASPM_THRESH_100M shl ALX_RXQ0_ASPM_THRESH_SHIFT |
@@: |
mov dword[esi + ALX_RXQ0], eax |
|
; TODO: DMA |
; mov eax, [esi + ALX_DMA] ; read and ignore? |
; mov eax, [ebx + device.dma_chnl] |
; dec eax |
; shl eax, ALX_DMA_RCHNL_SEL_SHIFT |
; or eax, (ALX_DMA_RORDER_MODE_OUT shl ALX_DMA_RORDER_MODE_SHIFT) \ |
; or ALX_DMA_RREQ_PRI_DATA \ |
; or (max_payload shl ALX_DMA_RREQ_BLEN_SHIFT ) \ |
; or (ALX_DMA_WDLY_CNT_DEF shl ALX_DMA_WDLY_CNT_SHIFT ) \ |
; or (ALX_DMA_RDLY_CNT_DEF shl ALX_DMA_RDLY_CNT_SHIFT ) |
; mov [esi + ALX_DMA], eax |
|
|
; default multi-tx-q weights |
mov eax, (ALX_WRR_PRI_RESTRICT_NONE shl ALX_WRR_PRI_SHIFT) \ |
or (4 shl ALX_WRR_PRI0_SHIFT) \ |
or (4 shl ALX_WRR_PRI1_SHIFT) \ |
or (4 shl ALX_WRR_PRI2_SHIFT) \ |
or (4 shl ALX_WRR_PRI3_SHIFT) |
mov [esi + ALX_WRR], eax |
|
ret |
|
|
align 16 |
alx_disable_rss: |
|
DEBUGF 1,"alx_disable_rss\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
mov eax, [esi + ALX_RXQ0] |
and eax, not (ALX_RXQ0_RSS_HASH_EN) |
mov [esi + ALX_RXQ0] , eax |
|
ret |
|
align 16 |
__alx_set_rx_mode: |
|
DEBUGF 1,"__alx_set_rx_mode\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
; if (!(netdev->flags & IFF_ALLMULTI)) { |
; netdev_for_each_mc_addr(ha, netdev) |
; alx_add_mc_addr(hw, ha->addr, mc_hash); |
; |
; alx_write_mem32(hw, ALX_HASH_TBL0, mc_hash[0]); |
; alx_write_mem32(hw, ALX_HASH_TBL1, mc_hash[1]); |
; } |
|
mov eax, [ebx + device.rx_ctrl] |
or eax, ALX_MAC_CTRL_PROMISC_EN or ALX_MAC_CTRL_MULTIALL_EN |
mov [ebx + device.rx_ctrl], eax |
mov dword[esi + ALX_MAC_CTRL], eax |
|
ret |
|
|
align 16 |
alx_check_link: |
|
call alx_clear_phy_intr |
|
mov edx, [ebx + device.state] |
|
call alx_get_phy_link |
cmp eax, 0 |
jl .reset |
|
mov esi, [ebx + device.mmio_addr] |
|
spin_lock_irqsave |
or [ebx + device.int_mask], ALX_ISR_PHY |
mov eax, [ebx + device.int_mask] |
mov [esi + ALX_IMR], eax |
spin_unlock_irqrestore |
|
cmp edx, [ebx + device.state] |
je .no_change |
|
cmp [ebx + device.state], ETH_LINK_DOWN |
je .link_down |
|
call alx_post_phy_link |
mov ecx, (ALX_PMCTRL_L0S_EN or ALX_PMCTRL_L1_EN or ALX_PMCTRL_ASPM_FCEN) |
call alx_enable_aspm |
call alx_start_mac |
|
invoke NetLinkChanged |
|
ret |
|
.no_change: |
DEBUGF 1, "link state unchanged\n" |
|
ret |
|
.link_down: |
; Link is now down |
|
call alx_reset_mac |
test eax, eax |
jnz .reset |
|
call alx_irq_disable |
|
; MAC reset causes all HW settings to be lost, restore all |
call alx_reinit_rings |
test eax, eax |
jnz .reset |
|
call alx_configure |
mov ecx, (ALX_PMCTRL_L1_EN or ALX_PMCTRL_ASPM_FCEN) |
call alx_enable_aspm |
call alx_post_phy_link |
call alx_irq_enable |
|
invoke NetLinkChanged |
|
ret |
|
.reset: |
DEBUGF 1, "alx_schedule_reset" |
;;; call alx_schedule_reset |
|
ret |
|
|
align 16 |
alx_post_phy_link: |
|
cmp [ebx + device.chip_rev], ALX_REV_B0 |
ja .done |
|
cmp [ebx + device.state], ETH_LINK_UNKNOWN |
jae @f |
|
; TODO |
; stdcall alx_read_phy_ext, ALX_MIIEXT_AFE, ALX_MIIEXT_ANEG |
; and eax, not (ALX_AFE_10BT_100M_TH) |
; stdcall alx_write_phy_ext, ALX_MIIEXT_AFE, ALX_MIIEXT_ANEG, eax |
|
ret |
@@: |
|
.done: |
|
ret |
|
|
align 16 |
alx_clear_phy_intr: |
|
stdcall alx_read_phy_reg, 0, ALX_MII_ISR |
|
ret |
|
|
align 16 |
alx_get_phy_link: |
|
DEBUGF 1,"alx_get_phy_link\n" |
|
stdcall alx_read_phy_reg, 0, MII_BMSR |
stdcall alx_read_phy_reg, 0, MII_BMSR |
|
mov [ebx + device.state], ETH_LINK_DOWN |
|
test ax, BMSR_LSTATUS |
jnz @f |
DEBUGF 1,"link is down\n" |
xor eax, eax |
ret |
@@: |
stdcall alx_read_phy_reg, 0, ALX_MII_GIGA_PSSR |
test ax, ALX_GIGA_PSSR_SPD_DPLX_RESOLVED |
jz .wrong_speed |
|
DEBUGF 1,"link is up\n" |
|
test ax, ALX_GIGA_PSSR_DPLX |
jz @f |
or [ebx + device.state], ETH_LINK_FD |
DEBUGF 1,"full duplex\n" |
@@: |
|
and ax, ALX_GIGA_PSSR_SPEED |
cmp ax, ALX_GIGA_PSSR_1000MBS |
jne @f |
or [ebx + device.state], ETH_LINK_1G |
DEBUGF 1,"1 gigabit\n" |
ret |
|
@@: |
cmp ax, ALX_GIGA_PSSR_100MBS |
jne @f |
or [ebx + device.state], ETH_LINK_100M |
DEBUGF 1,"100 mbit\n" |
ret |
|
@@: |
cmp ax, ALX_GIGA_PSSR_10MBS |
jne @f |
or [ebx + device.state], ETH_LINK_10M |
DEBUGF 1,"10 mbit\n" |
ret |
|
@@: |
mov [ebx + device.state], ETH_LINK_UNKNOWN |
DEBUGF 1,"speed unknown\n" |
ret |
|
.wrong_speed: |
DEBUGF 1,"wrong speed\n" |
xor eax, eax |
dec eax |
ret |
|
|
|
|
align 16 |
proc alx_read_phy_reg stdcall, phy_addr:dword, reg:dword |
|
; FIXME: Only internal PHY for now, fixed clock |
|
DEBUGF 1,"PHY read, addr=0x%x reg=0x%x\n", [phy_addr]:8, [reg]:8 |
|
mov esi, [ebx + device.mmio_addr] |
|
mov eax, [reg] |
shl eax, ALX_MDIO_REG_SHIFT |
or eax, ALX_MDIO_SPRES_PRMBL or (ALX_MDIO_CLK_SEL_25MD4 shl ALX_MDIO_CLK_SEL_SHIFT) or ALX_MDIO_START or ALX_MDIO_OP_READ |
mov dword[esi + ALX_MDIO], eax |
|
mov ecx, ALX_MDIO_MAX_AC_TO |
.loop: |
mov eax, dword[esi + ALX_MDIO] |
test eax, ALX_MDIO_BUSY |
jz .ready |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(10) |
pop ecx esi |
|
dec ecx |
jnz .loop |
|
DEBUGF 1,"PHY read timeout!\n" |
xor eax, eax |
dec eax |
ret |
|
.ready: |
; shr eax, ALX_MDIO_DATA_SHIFT |
and eax, ALX_MDIO_DATA_MASK |
|
DEBUGF 1,"PHY read, val=0x%x\n", eax:4 |
|
ret |
|
endp |
|
|
|
|
align 16 |
proc alx_write_phy_reg stdcall, phy_addr:dword, reg:dword, val:dword |
|
; FIXME: Only internal PHY for now, fixed clock |
|
DEBUGF 1,"PHY write, addr=0x%x reg=0x%x, data=0x%x\n", [phy_addr]:8, [reg]:8, [val]:8 |
|
mov esi, [ebx + device.mmio_addr] |
|
mov eax, [reg] |
shl eax, ALX_MDIO_REG_SHIFT |
mov ax, word[val] ; data must be in 16 lower bits :) |
or eax, ALX_MDIO_SPRES_PRMBL or (ALX_MDIO_CLK_SEL_25MD4 shl ALX_MDIO_CLK_SEL_SHIFT) or ALX_MDIO_START |
mov dword[esi + ALX_MDIO], eax |
|
mov ecx, ALX_MDIO_MAX_AC_TO |
.loop: |
mov eax, dword[esi + ALX_MDIO] |
test eax, ALX_MDIO_BUSY |
jz .ready |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(10) |
pop ecx esi |
|
dec ecx |
jnz .loop |
|
DEBUGF 1,"PHY write timeout!\n" |
xor eax, eax |
dec eax |
ret |
|
.ready: |
DEBUGF 1,"PHY write OK\n" |
xor eax, eax |
|
ret |
endp |
|
align 16 |
alx_request_irq: |
|
DEBUGF 1,"Request IRQ\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
; Only legacy interrupts supported for now. |
mov dword[esi + ALX_MSI_RETRANS_TIMER], 0 |
|
ret |
|
|
; End of code |
|
data fixups |
end data |
|
include '../peimport.inc' |
|
my_service db 'AR81XX',0 ; max 16 chars include zero |
|
include_debug_strings |
|
align 4 |
devices dd 0 |
device_list rd MAX_DEVICES ; This list contains all pointers to device structures the driver is handling |
|