1,6 → 1,6 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2018-2020. All rights reserved. ;; |
;; Copyright (C) KolibriOS team 2018-2021. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;; AR81XX driver for KolibriOS ;; |
24,20 → 24,24 |
COMPATIBLE_API = 0x0100 |
API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API |
|
MAX_DEVICES = 16 |
; configureable area |
|
__DEBUG__ = 1 |
__DEBUG_LEVEL__ = 2 |
MAX_DEVICES = 16 ; Maximum number of devices this driver may handle |
|
TX_RING_SIZE = 128 ; RING sizes must be a power of 2 |
RX_RING_SIZE = 128 |
__DEBUG__ = 1 ; 1 = on, 0 = off |
__DEBUG_LEVEL__ = 2 ; 1 = verbose, 2 = errors only |
|
TX_RING_SIZE = 128 ; Number of packets in send ring buffer |
RX_RING_SIZE = 128 ; Number of packets in receive ring buffer |
|
RX_BUFFER_SIZE = 1536 |
|
SMB_TIMER = 400 |
IMT = 200 |
ITH_TPD = TX_RING_SIZE / 3 |
IMT = 200 ; IRQ Modulo Timer |
ITH_TPD = TX_RING_SIZE / 3 ; Interrupt Threshold TPD |
|
; end configureable area |
|
section '.flat' readable writable executable |
|
include '../proc32.inc' |
48,8 → 52,17 |
|
include 'ar81xx.inc' |
|
if (bsr TX_RING_SIZE)>(bsf TX_RING_SIZE) |
display 'TX_RING_SIZE must be a power of two' |
err |
end if |
|
if (bsr RX_RING_SIZE)>(bsf RX_RING_SIZE) |
display 'RX_RING_SIZE must be a power of two' |
err |
end if |
|
; Transmit Packet Descriptor |
|
struct alx_tpd |
length dw ? |
vlan_tag dw ? |
59,7 → 72,6 |
ends |
|
; Receive Return Descriptor |
|
struct alx_rrd |
word0 dd ? ; IP payload cksum + number of RFDs + start index of RFD-ring |
rss_hash dd ? |
68,13 → 80,11 |
ends |
|
; Receive Free Descriptor |
|
struct alx_rfd |
addr_l dd ? |
addr_h dd ? |
ends |
|
|
struct device ETH_DEVICE |
|
io_addr dd ? |
87,7 → 97,6 |
chip_rev dd ? |
mmio_addr dd ? |
|
; dma_chnl dd ? |
max_dma_chnl dd ? |
|
int_mask dd ? |
108,8 → 117,6 |
|
ends |
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; proc START ;; |
132,7 → 139,6 |
|
endp |
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; proc SERVICE_PROC ;; |
189,7 → 195,6 |
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 |
278,7 → 283,6 |
;------------------------------------------------------ |
endp |
|
|
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; |
;; ;; |
;; Actual Hardware dependent code starts here ;; |
285,7 → 289,6 |
;; ;; |
;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\;; |
|
|
align 4 |
unload: |
; TODO: (in this particular order) |
299,7 → 302,6 |
or eax, -1 |
ret |
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; |
;; probe: enables the device (if it really is AR81XX) |
311,13 → 313,13 |
DEBUGF 1,"Probing\n" |
|
; Make the device a bus master |
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command |
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header.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 |
invoke PciWrite16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header.command, eax |
|
; get device id |
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.device_id |
; get device ID |
invoke PciRead16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header.device_id |
mov [ebx + device.pci_did], ax |
|
mov esi, chiplist |
335,26 → 337,43 |
DEBUGF 1, "Chip type = %s\n", eax |
.done: |
|
; get revision id. |
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.revision_id |
; get revision ID |
invoke PciRead8, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header.revision_id |
and eax, 0xff |
DEBUGF 1,"PCI Revision: %u\n", eax |
mov [ebx + device.pci_rev], eax |
shr al, ALX_PCI_REVID_SHIFT |
mov [ebx + device.chip_rev], eax |
DEBUGF 1,"ALX Revision: %u\n", eax |
|
DEBUGF 1,"Revision: %u\n", al |
stdcall alx_reset_pcie |
|
;;; call alx_reset_pcie |
|
mov ecx, (ALX_PMCTRL_L0S_EN or ALX_PMCTRL_L1_EN or ALX_PMCTRL_ASPM_FCEN) |
call alx_enable_aspm |
stdcall alx_enable_aspm |
|
call alx_reset_phy |
stdcall alx_reset_phy |
|
call alx_reset_mac |
stdcall alx_reset_mac |
|
call alx_get_perm_macaddr |
; Setup link to put it in a known good starting state |
stdcall alx_write_phy_reg, ALX_MII_DBG_ADDR, 0 |
|
mov esi, [ebx + device.mmio_addr] |
mov eax, dword[esi + ALX_DRV] |
|
stdcall alx_write_phy_reg, MII_ADVERTISE, ADVERTISE_CSMA or ADVERTISE_10HALF or ADVERTISE_10FULL or ADVERTISE_100HALF or ADVERTISE_100FULL or ADVERTISE_PAUSE_CAP |
|
xor eax, eax |
test [ebx + device.pci_did], 1 ;;; FIXME: is gigabit device? |
jz @f |
mov eax, ADVERTISE_1000XFULL |
@@: |
stdcall alx_write_phy_reg, MII_CTRL1000, eax |
|
stdcall alx_write_phy_reg, MII_BMCR, BMCR_RESET or BMCR_ANENABLE or BMCR_ANRESTART |
|
stdcall alx_get_perm_macaddr |
|
align 4 |
reset: |
|
362,19 → 381,16 |
|
; alx init_sw |
|
call alx_identify_hw |
stdcall 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 |
stdcall alx_alloc_rings |
|
call alx_configure |
stdcall alx_configure |
|
call alx_request_irq |
stdcall alx_request_irq |
|
; attach interrupt handler |
|
393,34 → 409,29 |
mov eax, not ALX_ISR_DIS |
mov [edi + ALX_ISR], eax |
|
call alx_irq_enable |
stdcall alx_irq_enable |
|
; Set the mtu, kernel will be able to send now |
; Set the MTU, kernel will be able to send now |
mov [ebx + device.mtu], 1514 |
|
call alx_check_link |
stdcall alx_check_link |
|
DEBUGF 1,"Reset ok\n" |
xor eax, eax |
ret |
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Transmit ;; |
;; ;; |
;; In: pointer to device structure in ebx ;; |
;; Out: eax = 0 on success ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
;; alx_start_xmit |
;; alx_map_tx_skb |
|
align 16 |
proc transmit stdcall bufferptr |
|
pushf |
cli |
spin_lock_irqsave |
|
mov esi, [bufferptr] |
DEBUGF 1,"Transmitting packet, buffer:%x, size:%u\n", [bufferptr], [esi + NET_BUFF.length] |
431,13 → 442,15 |
[eax+13]:2,[eax+12]:2 |
|
cmp [esi + NET_BUFF.length], 1514 |
ja .fail |
ja .error |
cmp [esi + NET_BUFF.length], 60 |
jb .fail |
jb .error |
|
; Program the descriptor |
mov edi, [ebx + device.txq_write_idx] |
DEBUGF 1, "Using TPD: %u\n", edi |
cmp dword[ebx + device.tpd_ring_virt + edi*4], 0 |
jne .overrun |
mov dword[ebx + device.tpd_ring_virt + edi*4], esi |
shl edi, 4 |
lea edi, [ebx + device.tpd_ring + edi] |
471,16 → 484,25 |
xor eax, eax |
ret |
|
.fail: |
DEBUGF 2,"Send failed\n" |
.error: |
DEBUGF 2, "TX packet error\n" |
inc [ebx + device.packets_tx_err] |
invoke NetFree, [bufferptr] |
popf |
|
spin_unlock_irqrestore |
or eax, -1 |
ret |
|
endp |
.overrun: |
DEBUGF 2, "TX overrun\n" |
inc [ebx + device.packets_tx_ovr] |
invoke NetFree, [bufferptr] |
|
spin_unlock_irqrestore |
or eax, -1 |
ret |
|
endp |
|
;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
487,48 → 509,30 |
;; Interrupt handler ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;; |
|
align 4 |
align 16 |
int_handler: |
|
push ebx esi edi |
|
DEBUGF 1,"int\n" |
mov ebx, [esp+4*4] |
DEBUGF 1,"INT for 0x%x\n", ebx |
|
; Find pointer of device wich made IRQ occur |
; TODO? if we are paranoid, we can check that the value from ebx is present in the current device_list |
|
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 |
jz .nothing |
|
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 |
DEBUGF 1,"Status: %x\n", eax |
|
test eax, ALX_ISR_TX_Q0 |
jz .no_tx |
DEBUGF 1,"TX interrupt\n" |
pusha |
call alx_clean_tx_irq |
stdcall alx_clean_tx_irq |
popa |
.no_tx: |
|
536,7 → 540,7 |
jz .no_rx |
DEBUGF 1,"RX interrupt\n" |
pusha |
call alx_clean_rx_irq |
stdcall alx_clean_rx_irq |
popa |
|
.no_rx: |
545,18 → 549,28 |
DEBUGF 1,"PHY interrupt\n" |
pusha |
; TODO: queue link check and disable this interrupt cause meanwhile?? |
call alx_check_link |
stdcall alx_check_link |
popa |
|
.no_phy: |
mov dword[edi + ALX_ISR], 0 |
pop edi esi ebx |
xor eax, eax |
inc eax |
|
ret |
|
.nothing: |
pop edi esi ebx |
xor eax, eax |
|
align 16 |
alx_identify_hw: |
ret |
|
proc alx_identify_hw stdcall |
|
cmp [ebx + device.pci_did], ALX_DEV_ID_AR8131 |
je .alc |
|
cmp [ebx + device.chip_rev], ALX_REV_C0 |
ja .einval |
|
575,11 → 589,65 |
dec eax |
ret |
|
.alc: |
mov [ebx + device.max_dma_chnl], 2 |
xor eax, eax |
ret |
|
endp |
|
align 16 |
alx_clean_tx_irq: |
proc udelay stdcall microseconds |
|
; FIXME |
|
push esi ecx edx |
xor esi, esi |
inc esi |
invoke Sleep |
pop edx ecx esi |
|
ret |
|
endp |
|
proc alx_reset_pcie stdcall |
|
DEBUGF 1,"alx_reset_pcie\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 or PCI_CMD_MMIO or PCI_CMD_PIO |
and ax, not(PCI_CMD_INTX_DISABLE) |
invoke PciWrite16, [ebx + device.pci_bus], [ebx + device.pci_dev], PCI_header00.command, eax |
|
; Clear any powersaving setting |
invoke PciWrite16, [ebx + device.pci_bus], [ebx + device.pci_dev], 0x44, 0x0000 ;; FIXME |
|
; Mask some pcie error bits |
mov esi, [ebx + device.mmio_addr] |
mov eax, [esi + ALX_UE_SVRT] |
and eax, not(ALX_UE_SVRT_DLPROTERR or ALX_UE_SVRT_FCPROTERR) |
mov [esi + ALX_UE_SVRT], eax |
|
; pclk |
mov eax, [esi + ALX_MASTER] |
or eax, ALX_MASTER_WAKEN_25M |
and eax, not (ALX_MASTER_PCLKSEL_SRDS) |
cmp [ebx + device.chip_rev], ALX_REV_A1 |
ja @f |
test [ebx + device.pci_rev], ALX_PCI_REVID_WITH_CR |
jz @f |
or eax, ALX_MASTER_PCLKSEL_SRDS |
@@: |
mov [esi + ALX_MASTER], eax |
|
xor eax, eax |
ret |
|
endp |
|
proc alx_clean_tx_irq stdcall |
|
mov eax, [ebx + device.txq_read_idx] |
movzx ecx, word[edi + ALX_TPD_PRI0_CIDX] |
|
591,6 → 659,7 |
push eax ecx |
invoke NetFree, [ebx + device.tpd_ring_virt + eax*4] |
pop ecx eax |
mov [ebx + device.tpd_ring_virt + eax*4], 0 |
|
inc eax |
and eax, TX_RING_SIZE-1 |
600,9 → 669,9 |
|
ret |
|
endp |
|
align 16 |
alx_clean_rx_irq: |
proc alx_clean_rx_irq stdcall |
|
mov ecx, [ebx + device.rxq_read_idx] |
.loop: |
647,7 → 716,7 |
invoke NetAlloc, RX_BUFFER_SIZE+NET_BUFF.data |
pop edx ecx esi |
test eax, eax |
; jz .out_of_mem |
jz .rx_overrun |
mov [ebx + device.rfd_ring_virt + ecx], eax |
add eax, NET_BUFF.data |
invoke GetPhysAddr |
665,6 → 734,14 |
and ecx, RX_RING_SIZE-1 |
jmp .loop |
|
.rx_overrun: |
DEBUGF 2,"RX FIFO overrun\n" |
inc [ebx + device.packets_rx_ovr] |
shr ecx, 2 |
inc ecx |
and ecx, RX_RING_SIZE-1 |
jmp .loop |
|
.done: |
shr ecx, 2 |
mov [ebx + device.rxq_read_idx], ecx |
675,44 → 752,76 |
|
ret |
|
endp |
|
align 16 |
; ecx = additional bit flags (ALX_PMCTRL_L0S_EN, ALX_PMCTRL_L1_EN, ALX_PMCTRL_ASPM_FCEN) |
alx_enable_aspm: |
; IN: ecx = additional bit flags (ALX_PMCTRL_L0S_EN, ALX_PMCTRL_L1_EN, ALX_PMCTRL_ASPM_FCEN) |
proc alx_enable_aspm stdcall |
|
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) |
cmp [ebx + device.pci_did], ALX_DEV_ID_AR8131 |
je .alc_l1c |
|
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 |
test [ebx + device.pci_rev], ALX_PCI_REVID_WITH_CR |
jz @f |
or eax, ALX_PMCTRL_L1_SRDS_EN or ALX_PMCTRL_L1_SRDSPLL_EN |
or ecx, ALX_PMCTRL_L1_SRDS_EN or ALX_PMCTRL_L1_SRDSPLL_EN |
@@: |
|
mov eax, dword[esi + ALX_PMCTRL] |
and eax, not((ALX_PMCTRL_LCKDET_TIMER_MASK shl ALX_PMCTRL_LCKDET_TIMER_SHIFT) or \ |
(ALX_PMCTRL_L1REQ_TO_MASK shl ALX_PMCTRL_L1REQ_TO_SHIFT) or \ |
(ALX_PMCTRL_L1_TIMER_MASK shl ALX_PMCTRL_L1_TIMER_SHIFT) or \ |
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) |
or eax, (ALX_PMCTRL_LCKDET_TIMER_DEF shl ALX_PMCTRL_LCKDET_TIMER_SHIFT) or \ |
(ALX_PMCTRL_RCVR_WT_1US or ALX_PMCTRL_L1_CLKSW_EN or ALX_PMCTRL_L1_SRDSRX_PWD) or \ |
(ALX_PMCTRL_L1REG_TO_DEF shl ALX_PMCTRL_L1REQ_TO_SHIFT) or \ |
(ALX_PMCTRL_L1_TIMER_16US shl ALX_PMCTRL_L1_TIMER_SHIFT) |
or eax, ecx |
mov dword[esi + ALX_PMCTRL], eax |
|
ret |
|
align 16 |
alx_reset_mac: |
.alc_l1c: |
|
DEBUGF 1, "aspm for L1C\n" |
|
mov esi, [ebx + device.mmio_addr] |
mov eax, dword[esi + ALX_PMCTRL] |
|
and eax, not(ALX_PMCTRL_L0S_EN or ALX_PMCTRL_L1_EN or ALX_PMCTRL_ASPM_FCEN) |
or eax, (ALX_PMCTRL_LCKDET_TIMER_DEF shl ALX_PMCTRL_LCKDET_TIMER_SHIFT) ;\ |
; or (0 shl ALX_PMCTRL_L1_TIMER_SHIFT) |
|
or eax, ecx |
|
;;; FIXME if(linkon) |
|
or eax, (ALX_PMCTRL_L1_SRDS_EN or ALX_PMCTRL_L1_SRDSPLL_EN or ALX_PMCTRL_L1_BUFSRX_EN) |
and eax, not(ALX_PMCTRL_L1_SRDSRX_PWD or ALX_PMCTRL_L1_CLKSW_EN or ALX_PMCTRL_L0S_EN or ALX_PMCTRL_L1_EN) |
|
;; |
|
mov dword[esi + ALX_PMCTRL], eax |
|
ret |
|
endp |
|
proc alx_reset_mac stdcall |
|
DEBUGF 1, "reset mac\n" |
|
; disable all interrupts, RXQ/TXQ |
721,47 → 830,52 |
mov dword[esi + ALX_IMR], 0x0 |
mov dword[esi + ALX_ISR], ALX_ISR_DIS |
|
call alx_stop_mac |
stdcall alx_stop_mac |
|
; mac reset workaround |
mov dword[esi + ALX_RFD_PIDX], 1 |
|
; disable l0s/l1 before mac reset on some chips |
; 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 |
test [ebx + device.pci_rev], ALX_PCI_REVID_WITH_CR |
jz @f |
mov eax, [esi + ALX_PMCTRL] |
mov edx, eax |
test eax, ALX_PMCTRL_L1_EN or ALX_PMCTRL_L0S_EN |
jz @f |
and eax, not(ALX_PMCTRL_L1_EN or ALX_PMCTRL_L0S_EN) |
mov [esi + ALX_PMCTRL], eax |
@@: |
|
; reset whole mac safely |
; reset whole MAC safely |
mov eax, [esi + ALX_MASTER] |
or eax, ALX_MASTER_DMA_MAC_RST + ALX_MASTER_OOB_DIS |
or eax, ALX_MASTER_DMA_MAC_RST or 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 |
stdcall udelay, 10 |
|
mov ecx, ALX_DMA_MAC_RST_TO |
mov ecx, ALX_DMA_MAC_RST_TO ; timeout |
.loop1: |
mov eax, dword[esi + ALX_RFD_PIDX] |
test eax, eax |
jz @f |
|
stdcall udelay, 10 |
|
dec ecx |
jnz .loop1 |
jmp .error |
@@: |
|
.loop2: |
mov eax, dword[esi + ALX_MASTER] |
test eax, ALX_MASTER_DMA_MAC_RST |
jz @f |
|
stdcall udelay, 10 |
|
dec ecx |
jnz .loop2 |
jmp .error |
770,15 → 884,14 |
; 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 |
test [ebx + device.pci_rev], ALX_PCI_REVID_WITH_CR |
jz @f |
or eax, ALX_MASTER_PCLKSEL_SRDS |
mov [esi + ALX_MASTER], eax |
|
mov [esi + ALX_PMCTRL], edx |
@@: |
|
call alx_reset_osc |
stdcall alx_reset_osc |
|
; clear Internal OSC settings, switching OSC by hw itself, disable isolate for rev A devices |
|
796,16 → 909,13 |
@@: |
mov [esi + ALX_MISC], eax |
|
push esi |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(20); |
pop esi |
stdcall udelay, 20 |
|
; driver control speed/duplex, hash-alg |
mov eax, [ebx + device.rx_ctrl] |
mov [esi + ALX_MAC_CTRL], eax |
|
; clk sw |
mov eax, dword[esi + ALX_SERDES] |
or eax, ALX_SERDES_MACCLK_SLWDWN or ALX_SERDES_PHYCLK_SLWDWN |
mov dword[esi + ALX_SERDES], eax |
820,20 → 930,65 |
dec eax |
ret |
|
endp |
|
align 16 |
alx_reset_phy: |
proc alx_reset_phy stdcall |
|
DEBUGF 1, "Reset phy\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
;; TODO |
mov eax, dword [esi + ALX_PHY_CTRL] |
DEBUGF 1, "read ALX_PHY_CTRL = %x\n", eax |
and eax, not (ALX_PHY_CTRL_DSPRST_OUT or ALX_PHY_CTRL_IDDQ or ALX_PHY_CTRL_GATE_25M or ALX_PHY_CTRL_POWER_DOWN or ALX_PHY_CTRL_CLS) |
or eax, ALX_PHY_CTRL_RST_ANALOG |
or eax, ALX_PHY_CTRL_HIB_PULSE or ALX_PHY_CTRL_HIB_EN ; assume pws is enabled |
|
; set phy interrupt mask |
stdcall alx_read_phy_reg, 0, ALX_MII_IER |
DEBUGF 1, "write ALX_PHY_CTRL = %x\n", eax |
mov [esi + ALX_PHY_CTRL], eax |
|
stdcall udelay, 5 |
|
or eax, ALX_PHY_CTRL_DSPRST_OUT |
mov [esi + ALX_PHY_CTRL], eax |
|
stdcall udelay, 10 |
|
or eax, ALX_PHY_CTRL_DSPRST_OUT |
DEBUGF 1, "write ALX_PHY_CTRL = %x\n", eax |
mov dword [esi + ALX_PHY_CTRL], eax |
|
stdcall udelay, 800 |
|
; PHY power saving & hibernate |
stdcall alx_write_phy_dbg, ALX_MIIDBG_LEGCYPS, ALX_LEGCYPS_DEF |
stdcall alx_write_phy_dbg, ALX_MIIDBG_SYSMODCTRL, ALX_SYSMODCTRL_IECHOADJ_DEF |
stdcall alx_write_phy_ext, ALX_MIIEXT_PCS, ALX_MIIEXT_VDRVBIAS, ALX_VDRVBIAS_DEF |
|
; EEE advertisement |
mov eax, [esi + ALX_LPI_CTRL] |
and eax, not (ALX_LPI_CTRL_EN) |
mov [esi + ALX_LPI_CTRL], eax |
stdcall alx_write_phy_ext, ALX_MIIEXT_ANEG, ALX_MIIEXT_LOCAL_EEEADV, 0 |
|
; PHY power saving |
stdcall alx_write_phy_dbg, ALX_MIIDBG_TST10BTCFG, ALX_TST10BTCFG_DEF |
stdcall alx_write_phy_dbg, ALX_MIIDBG_SRDSYSMOD, ALX_SRDSYSMOD_DEF |
stdcall alx_write_phy_dbg, ALX_MIIDBG_TST100BTCFG, ALX_TST100BTCFG_DEF |
stdcall alx_write_phy_dbg, ALX_MIIDBG_ANACTRL, ALX_ANACTRL_DEF |
stdcall alx_read_phy_dbg, ALX_MIIDBG_GREENCFG2 |
and eax, not ALX_GREENCFG2_GATE_DFSE_EN |
stdcall alx_write_phy_dbg, ALX_MIIDBG_GREENCFG2, eax |
; rtl8139c, 120m issue */ |
stdcall alx_write_phy_ext, ALX_MIIEXT_ANEG, ALX_MIIEXT_NLP78, ALX_MIIEXT_NLP78_120M_DEF |
stdcall alx_write_phy_ext, ALX_MIIEXT_ANEG, ALX_MIIEXT_S3DIG10, ALX_MIIEXT_S3DIG10_DEF |
|
; TODO: link patch ? |
|
; set PHY interrupt mask |
stdcall alx_read_phy_reg, ALX_MII_IER |
or eax, ALX_IER_LINK_UP or ALX_IER_LINK_DOWN |
stdcall alx_write_phy_reg, 0, ALX_MII_IER , eax |
stdcall alx_write_phy_reg, ALX_MII_IER , eax |
|
DEBUGF 1, "OK\n" |
xor eax, eax |
845,27 → 1000,42 |
dec eax |
ret |
|
endp |
|
;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 |
proc alx_set_macaddr stdcall |
|
mov esi, [ebx + device.mmio_addr] |
|
align 16 |
alx_reset_osc: |
mov eax, dword[ebx + device.mac+2] |
bswap eax |
mov [esi + ALX_STAD0], eax |
mov ax, word[ebx + device.mac] |
xchg al, ah |
mov [esi + ALX_STAD1], ax |
|
|
ret |
endp |
|
proc alx_enable_osc stdcall |
|
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 |
|
endp |
|
proc alx_reset_osc stdcall |
|
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 |
876,7 → 1046,7 |
; 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 |
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) |
894,15 → 1064,11 |
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 |
stdcall udelay, 20 |
|
ret |
|
.rev_a: |
.rev_A: |
|
; disable isolate for rev A devices |
and eax, not ( ALX_MISC_ISO_EN) |
911,17 → 1077,14 |
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 |
stdcall udelay, 20 |
|
ret |
|
align 16 |
alx_read_macaddr: |
endp |
|
proc alx_read_macaddr stdcall |
|
mov esi, [ebx + device.mmio_addr] |
mov eax, dword[esi + ALX_STAD0] |
bswap eax |
944,7 → 1107,9 |
cmp word[ebx + device.mac + 4], 0xffff |
je .invalid |
@@: |
; TODO: check if it's not a multicast |
test byte[ebx + device.mac + 5], 0x01 ; Multicast |
jnz .invalid |
@@: |
xor eax, eax |
ret |
|
954,12 → 1119,12 |
inc eax |
ret |
|
endp |
|
align 16 |
alx_get_perm_macaddr: |
proc alx_get_perm_macaddr stdcall |
|
; try to get it from register first |
call alx_read_macaddr |
stdcall alx_read_macaddr |
test eax, eax |
jz .done |
|
1001,7 → 1166,7 |
jmp .loop2 |
@@: |
|
call alx_read_macaddr |
stdcall alx_read_macaddr |
test eax, eax |
jz .done |
|
1048,7 → 1213,7 |
jmp .loop4 |
@@: |
|
call alx_read_macaddr |
stdcall alx_read_macaddr |
test eax, eax |
jz .done |
|
1063,9 → 1228,10 |
xor eax, eax |
ret |
|
align 16 |
alx_stop_mac: |
endp |
|
proc alx_stop_mac stdcall |
|
DEBUGF 1,"alx_stop_mac\n" |
|
mov esi, [ebx + device.mmio_addr] |
1078,11 → 1244,7 |
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 |
stdcall udelay, 40 |
|
mov eax, [ebx + device.rx_ctrl] |
and eax, not(ALX_MAC_CTRL_TX_EN or ALX_MAC_CTRL_RX_EN) |
1095,11 → 1257,7 |
test eax, ALX_MAC_STS_IDLE |
jz .done |
|
push esi |
xor esi, esi |
inc esi |
invoke Sleep ; FIME: udelay(10) |
pop esi |
stdcall udelay, 10 |
|
dec ecx |
jnz .loop |
1114,9 → 1272,9 |
xor eax, eax |
ret |
|
endp |
|
align 16 |
alx_start_mac: |
proc alx_start_mac stdcall |
|
DEBUGF 1,"alx_start_mac\n" |
|
1133,33 → 1291,32 |
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 |
test [ebx + device.state], ETH_LINK_FULL_DUPLEX |
jz .no_fd |
or eax, ALX_MAC_CTRL_FULLD |
@@: |
.no_fd: |
and eax, not (ALX_MAC_CTRL_SPEED_MASK shl ALX_MAC_CTRL_SPEED_SHIFT) |
test [ebx + device.state], ETH_LINK_1G |
jz .10_100 |
mov ecx, [ebx + device.state] |
and ecx, ETH_LINK_SPEED_MASK |
cmp ecx, ETH_LINK_SPEED_1G |
jne .10_100 |
or eax, (ALX_MAC_CTRL_SPEED_1000 shl ALX_MAC_CTRL_SPEED_SHIFT) |
jmp .done |
|
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) |
|
.done: |
DEBUGF 1,"mac ctrl=0x%x\n", eax |
mov [ebx + device.rx_ctrl], eax |
mov [esi + ALX_MAC_CTRL], eax |
|
ret |
|
endp |
|
proc alx_init_ring_ptrs stdcall |
|
align 16 |
alx_init_ring_ptrs: |
|
DEBUGF 1,"alx_init_ring_ptrs\n" |
|
mov esi, [ebx + device.mmio_addr] |
1200,70 → 1357,33 |
|
ret |
|
endp |
|
align 16 |
alx_alloc_descriptors: |
proc alx_alloc_rings stdcall |
|
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); |
stdcall alx_reinit_rings |
|
call alx_reinit_rings |
.ret_err: |
ret |
|
endp |
|
align 16 |
alx_reinit_rings: |
proc alx_reinit_rings stdcall |
|
DEBUGF 1,"alx_reinit_rings\n" |
|
call alx_free_rx_ring |
call alx_init_ring_ptrs |
call alx_refill_rx_ring |
stdcall alx_free_rx_ring |
stdcall alx_init_ring_ptrs |
stdcall alx_refill_rx_ring |
|
ret |
|
endp |
|
align 16 |
alx_refill_rx_ring: |
proc alx_refill_rx_ring stdcall |
|
DEBUGF 1,"alx_refill_rx_ring\n" |
|
1303,9 → 1423,9 |
|
ret |
|
endp |
|
align 16 |
alx_free_rx_ring: |
proc alx_free_rx_ring stdcall |
|
DEBUGF 1,"alx_free_rx_ring\n" |
|
1329,15 → 1449,15 |
|
ret |
|
endp |
|
align 16 |
alx_configure: |
proc alx_configure stdcall |
|
DEBUGF 1,"alx_configure\n" |
|
call alx_configure_basic |
call alx_disable_rss |
call __alx_set_rx_mode |
stdcall alx_configure_basic |
stdcall alx_disable_rss |
call alx_set_rx_mode |
|
mov esi, [ebx + device.mmio_addr] |
mov eax, [ebx + device.rx_ctrl] |
1346,9 → 1466,9 |
xor eax, eax |
ret |
|
endp |
|
align 16 |
alx_irq_enable: |
proc alx_irq_enable stdcall |
|
DEBUGF 1,"alx_irq_enable\n" |
|
1357,13 → 1477,14 |
mov eax, [ebx + device.int_mask] |
mov [esi + ALX_IMR], eax |
|
call alx_post_write |
stdcall alx_post_write |
|
ret |
|
align 16 |
alx_irq_disable: |
endp |
|
proc alx_irq_disable stdcall |
|
DEBUGF 1,"alx_irq_disable\n" |
|
mov esi, [ebx + device.mmio_addr] |
1370,13 → 1491,13 |
mov dword[esi + ALX_ISR], ALX_ISR_DIS |
mov dword[esi + ALX_IMR], 0 |
|
call alx_post_write |
stdcall alx_post_write |
|
ret |
|
endp |
|
align 16 |
alx_post_write: |
proc alx_post_write stdcall |
|
push eax |
mov esi, [ebx + device.mmio_addr] |
1385,20 → 1506,19 |
|
ret |
|
endp |
|
align 16 |
alx_configure_basic: |
proc alx_configure_basic stdcall |
|
DEBUGF 1,"alx_configure_basic\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
;;; call alx_set_macaddr |
stdcall 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 |
1406,13 → 1526,15 |
|
mov dword[esi + ALX_SMB_TIMER], SMB_TIMER * 500 |
|
; Interrupt moderation |
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 |
; Set interupt moderator timer (max interupts per second) |
mov dword[esi + ALX_IRQ_MODU_TIMER], ((IMT) shl ALX_IRQ_MODU_TIMER1_SHIFT) or ((IMT / 2) shl ALX_IRQ_MODU_TIMER2_SHIFT) |
|
; intr re-trig timeout |
; Interrupt re-trigger timeout |
mov dword[esi + ALX_INT_RETRIG], ALX_INT_RETRIG_TO |
|
; tpd threshold to trig int |
1437,21 → 1559,32 |
jz @f |
or eax, ALX_RXQ0_ASPM_THRESH_100M shl ALX_RXQ0_ASPM_THRESH_SHIFT |
@@: |
mov dword[esi + ALX_RXQ0], eax |
mov [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 |
; DMA |
max_payload equ 2 ;;;; FIXME |
|
mov eax, [esi + ALX_DMA] ; Read and ignore? |
; Pre-B0 devices have 2 DMA channels |
mov 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 ) \ |
or ((2-1) shl ALX_DMA_RCHNL_SEL_SHIFT) |
|
cmp [ebx + device.chip_rev], ALX_REV_B0 |
jb @f |
; B0 and newer have 4 DMA channels |
mov 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 ) \ |
or ((4-1) shl ALX_DMA_RCHNL_SEL_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) \ |
1462,9 → 1595,9 |
|
ret |
|
endp |
|
align 16 |
alx_disable_rss: |
proc alx_disable_rss stdcall |
|
DEBUGF 1,"alx_disable_rss\n" |
|
1476,13 → 1609,16 |
|
ret |
|
align 16 |
__alx_set_rx_mode: |
endp |
|
proc alx_set_rx_mode stdcall |
|
DEBUGF 1,"__alx_set_rx_mode\n" |
|
mov esi, [ebx + device.mmio_addr] |
|
; TODO: proper multicast |
|
; if (!(netdev->flags & IFF_ALLMULTI)) { |
; netdev_for_each_mc_addr(ha, netdev) |
; alx_add_mc_addr(hw, ha->addr, mc_hash); |
1492,21 → 1628,21 |
; } |
|
mov eax, [ebx + device.rx_ctrl] |
or eax, ALX_MAC_CTRL_PROMISC_EN or ALX_MAC_CTRL_MULTIALL_EN |
or eax, ALX_MAC_CTRL_PROMISC_EN or ALX_MAC_CTRL_MULTIALL_EN ; FIXME: dont force promiscous mode.. |
mov [ebx + device.rx_ctrl], eax |
mov dword[esi + ALX_MAC_CTRL], eax |
|
ret |
|
endp |
|
align 16 |
alx_check_link: |
proc alx_check_link stdcall |
|
call alx_clear_phy_intr |
stdcall alx_clear_phy_intr |
|
mov edx, [ebx + device.state] |
|
call alx_get_phy_link |
stdcall alx_get_phy_link |
cmp eax, 0 |
jl .reset |
|
1524,10 → 1660,10 |
cmp [ebx + device.state], ETH_LINK_DOWN |
je .link_down |
|
call alx_post_phy_link |
stdcall 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 |
stdcall alx_enable_aspm |
stdcall alx_start_mac |
|
invoke NetLinkChanged |
|
1541,22 → 1677,22 |
.link_down: |
; Link is now down |
|
call alx_reset_mac |
stdcall alx_reset_mac |
test eax, eax |
jnz .reset |
|
call alx_irq_disable |
stdcall alx_irq_disable |
|
; MAC reset causes all HW settings to be lost, restore all |
call alx_reinit_rings |
stdcall alx_reinit_rings |
test eax, eax |
jnz .reset |
|
call alx_configure |
stdcall 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 |
stdcall alx_enable_aspm |
stdcall alx_post_phy_link |
stdcall alx_irq_enable |
|
invoke NetLinkChanged |
|
1563,15 → 1699,17 |
ret |
|
.reset: |
DEBUGF 1, "alx_schedule_reset" |
;;; call alx_schedule_reset |
DEBUGF 1, "alx_schedule_reset\n" |
;;; stdcall alx_schedule_reset |
|
ret |
|
endp |
|
align 16 |
alx_post_phy_link: |
proc alx_post_phy_link stdcall |
|
DEBUGF 1, "alx_post_phy_link\n" |
|
cmp [ebx + device.chip_rev], ALX_REV_B0 |
ja .done |
|
1578,10 → 1716,11 |
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 |
; TODO: vendor hocus-pocus to tune the PHY according the detected cable length |
stdcall alx_write_phy_dbg, ALX_MIIDBG_AZ_ANADECT, ALX_AZ_ANADECT_DEF |
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 |
@@: |
1590,22 → 1729,23 |
|
ret |
|
endp |
|
align 16 |
alx_clear_phy_intr: |
proc alx_clear_phy_intr stdcall |
|
stdcall alx_read_phy_reg, 0, ALX_MII_ISR |
DEBUGF 1,"alx_clear_phy_intr\n" |
stdcall alx_read_phy_reg, ALX_MII_ISR |
|
ret |
|
endp |
|
align 16 |
alx_get_phy_link: |
proc alx_get_phy_link stdcall |
|
DEBUGF 1,"alx_get_phy_link\n" |
|
stdcall alx_read_phy_reg, 0, MII_BMSR |
stdcall alx_read_phy_reg, 0, MII_BMSR |
stdcall alx_read_phy_reg, MII_BMSR |
stdcall alx_read_phy_reg, MII_BMSR |
|
mov [ebx + device.state], ETH_LINK_DOWN |
|
1615,7 → 1755,7 |
xor eax, eax |
ret |
@@: |
stdcall alx_read_phy_reg, 0, ALX_MII_GIGA_PSSR |
stdcall alx_read_phy_reg, ALX_MII_GIGA_PSSR |
test ax, ALX_GIGA_PSSR_SPD_DPLX_RESOLVED |
jz .wrong_speed |
|
1623,7 → 1763,7 |
|
test ax, ALX_GIGA_PSSR_DPLX |
jz @f |
or [ebx + device.state], ETH_LINK_FD |
or [ebx + device.state], ETH_LINK_FULL_DUPLEX |
DEBUGF 1,"full duplex\n" |
@@: |
|
1630,7 → 1770,7 |
and ax, ALX_GIGA_PSSR_SPEED |
cmp ax, ALX_GIGA_PSSR_1000MBS |
jne @f |
or [ebx + device.state], ETH_LINK_1G |
or [ebx + device.state], ETH_LINK_SPEED_1G |
DEBUGF 1,"1 gigabit\n" |
ret |
|
1637,15 → 1777,15 |
@@: |
cmp ax, ALX_GIGA_PSSR_100MBS |
jne @f |
or [ebx + device.state], ETH_LINK_100M |
DEBUGF 1,"100 mbit\n" |
or [ebx + device.state], ETH_LINK_SPEED_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" |
or [ebx + device.state], ETH_LINK_SPEED_10M |
DEBUGF 1,"10 Mbit\n" |
ret |
|
@@: |
1659,16 → 1799,14 |
dec eax |
ret |
|
endp |
|
proc alx_read_phy_reg stdcall, reg:dword |
|
; FIXME: fixed clock |
|
align 16 |
proc alx_read_phy_reg stdcall, phy_addr:dword, reg:dword |
DEBUGF 1,"alx_read_phy_reg reg=0x%x\n", [reg]:4 |
|
; 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] |
1682,16 → 1820,12 |
test eax, ALX_MDIO_BUSY |
jz .ready |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(10) |
pop ecx esi |
stdcall udelay, 10 |
|
dec ecx |
jnz .loop |
|
DEBUGF 1,"PHY read timeout!\n" |
DEBUGF 1,"alx_read_phy_reg read timeout!\n" |
xor eax, eax |
dec eax |
ret |
1700,22 → 1834,61 |
; shr eax, ALX_MDIO_DATA_SHIFT |
and eax, ALX_MDIO_DATA_MASK |
|
DEBUGF 1,"PHY read, val=0x%x\n", eax:4 |
DEBUGF 1,"alx_read_phy_reg data=0x%x\n", eax:4 |
|
ret |
|
endp |
|
proc alx_read_phy_ext stdcall, dev:dword, reg:dword |
|
; FIXME: fixed clock |
|
DEBUGF 1,"alx_read_phy_ext dev=0x%x reg=0x%x\n", [dev]:4, [reg]:4 |
|
align 16 |
proc alx_write_phy_reg stdcall, phy_addr:dword, reg:dword, val:dword |
mov esi, [ebx + device.mmio_addr] |
|
; FIXME: Only internal PHY for now, fixed clock |
mov eax, [dev] |
shl eax, ALX_MDIO_EXTN_DEVAD_SHIFT |
mov ax, word[reg] |
; shl eax, ALX_MDIO_EXTN_REG_SHIFT |
mov dword[esi + ALX_MDIO_EXTN], eax |
|
DEBUGF 1,"PHY write, addr=0x%x reg=0x%x, data=0x%x\n", [phy_addr]:8, [reg]:8, [val]:8 |
mov 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 or ALX_MDIO_MODE_EXT |
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 |
|
stdcall udelay, 10 |
|
dec ecx |
jnz .loop |
|
DEBUGF 1,"alx_read_phy_ext read timeout!\n" |
xor eax, eax |
dec eax |
ret |
|
.ready: |
; shr eax, ALX_MDIO_DATA_SHIFT |
and eax, ALX_MDIO_DATA_MASK |
|
DEBUGF 1,"alx_read_phy_ext data=0x%x\n", eax:4 |
|
ret |
|
endp |
|
proc alx_write_phy_reg stdcall, reg:dword, val:dword |
|
; FIXME: fixed clock |
|
DEBUGF 1,"alx_write_phy_reg reg=0x%x data=0x%x\n", [reg]:4, [val]:4 |
|
mov esi, [ebx + device.mmio_addr] |
|
mov eax, [reg] |
1730,28 → 1903,98 |
test eax, ALX_MDIO_BUSY |
jz .ready |
|
push esi ecx |
xor esi, esi |
inc esi |
invoke Sleep ;; FIXME: udelay(10) |
pop ecx esi |
stdcall udelay, 10 |
|
dec ecx |
jnz .loop |
|
DEBUGF 1,"PHY write timeout!\n" |
DEBUGF 1,"alx_write_phy_reg timeout!\n" |
xor eax, eax |
dec eax |
ret |
|
.ready: |
DEBUGF 1,"PHY write OK\n" |
DEBUGF 1,"alx_write_phy_reg OK\n" |
xor eax, eax |
|
ret |
endp |
|
align 16 |
proc alx_write_phy_dbg stdcall, reg:dword, val:dword |
|
DEBUGF 1,"alx_write_phy_dbg\n" |
|
stdcall alx_write_phy_reg, ALX_MII_DBG_ADDR, [reg] |
test eax, eax |
jnz @f |
stdcall alx_write_phy_reg, ALX_MII_DBG_DATA, [val] |
|
ret |
@@: |
DEBUGF 1,"alx_write_phy_dbg ERROR\n" |
|
ret |
|
endp |
|
proc alx_read_phy_dbg stdcall, reg:dword |
|
DEBUGF 1,"alx_read_phy_dbg\n" |
|
stdcall alx_write_phy_reg, ALX_MII_DBG_ADDR, [reg] |
test eax, eax |
jnz @f |
stdcall alx_read_phy_reg, ALX_MII_DBG_DATA |
|
ret |
@@: |
DEBUGF 1,"alx_read_phy_dbg ERROR\n" |
|
ret |
|
endp |
|
proc alx_write_phy_ext stdcall, dev:dword, reg:dword, val:dword |
|
; FIXME: fixed clock |
|
DEBUGF 1,"alx_write_phy_ext dev=0x%x reg=0x%x, data=0x%x\n", [dev]:4, [reg]:4, [val]:4 |
|
mov esi, [ebx + device.mmio_addr] |
|
mov eax, [dev] |
shl eax, ALX_MDIO_EXTN_DEVAD_SHIFT |
mov ax, word[reg] |
; shl eax, ALX_MDIO_EXTN_REG_SHIFT |
mov dword[esi + ALX_MDIO_EXTN], eax |
|
movzx eax, 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 or ALX_MDIO_MODE_EXT |
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 |
|
stdcall udelay, 10 |
|
dec ecx |
jnz .loop |
|
DEBUGF 1,"alx_write_phy_ext timeout!\n" |
xor eax, eax |
dec eax |
ret |
|
.ready: |
DEBUGF 1,"alx_write_phy_ext OK\n" |
xor eax, eax |
|
ret |
endp |
|
alx_request_irq: |
|
DEBUGF 1,"Request IRQ\n" |
1763,7 → 2006,6 |
|
ret |
|
|
; End of code |
|
data fixups |
1773,7 → 2015,6 |
|
my_service db 'AR81XX',0 ; max 16 chars include zero |
|
|
chiplist: |
dd (ALX_DEV_ID_AR8131 shl 16) or ALX_VEN_ID, ar8131_sz |
dd (ALX_DEV_ID_AR8161 shl 16) or ALX_VEN_ID, ar8161_sz |