You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
408 lines
8.5 KiB
408 lines
8.5 KiB
/*
|
|
* RX ucode for the Intel IXP2400 in POS-PHY mode.
|
|
* Copyright (C) 2004, 2005 Lennert Buytenhek
|
|
* Dedicated to Marija Kulikova.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Assumptions made in this code:
|
|
* - The IXP2400 MSF is configured for POS-PHY mode, in a mode where
|
|
* only one full element list is used. This includes, for example,
|
|
* 1x32 SPHY and 1x32 MPHY32, but not 4x8 SPHY or 1x32 MPHY4. (This
|
|
* is not an exhaustive list.)
|
|
* - The RBUF uses 64-byte mpackets.
|
|
* - RX descriptors reside in SRAM, and have the following format:
|
|
* struct rx_desc
|
|
* {
|
|
* // to uengine
|
|
* u32 buf_phys_addr;
|
|
* u32 buf_length;
|
|
*
|
|
* // from uengine
|
|
* u32 channel;
|
|
* u32 pkt_length;
|
|
* };
|
|
* - Packet data resides in DRAM.
|
|
* - Packet buffer addresses are 8-byte aligned.
|
|
* - Scratch ring 0 is rx_pending.
|
|
* - Scratch ring 1 is rx_done, and has status condition 'full'.
|
|
* - The host triggers rx_done flush and rx_pending refill on seeing INTA.
|
|
* - This code is run on all eight threads of the microengine it runs on.
|
|
*
|
|
* Local memory is used for per-channel RX state.
|
|
*/
|
|
|
|
#define RX_THREAD_FREELIST_0 0x0030
|
|
#define RBUF_ELEMENT_DONE 0x0044
|
|
|
|
#define CHANNEL_FLAGS *l$index0[0]
|
|
#define CHANNEL_FLAG_RECEIVING 1
|
|
#define PACKET_LENGTH *l$index0[1]
|
|
#define PACKET_CHECKSUM *l$index0[2]
|
|
#define BUFFER_HANDLE *l$index0[3]
|
|
#define BUFFER_START *l$index0[4]
|
|
#define BUFFER_LENGTH *l$index0[5]
|
|
|
|
#define CHANNEL_STATE_SIZE 24 // in bytes
|
|
#define CHANNEL_STATE_SHIFT 5 // ceil(log2(state size))
|
|
|
|
|
|
.sig volatile sig1
|
|
.sig volatile sig2
|
|
.sig volatile sig3
|
|
|
|
.sig mpacket_arrived
|
|
.reg add_to_rx_freelist
|
|
.reg read $rsw0, $rsw1
|
|
.xfer_order $rsw0 $rsw1
|
|
|
|
.reg zero
|
|
|
|
/*
|
|
* Initialise add_to_rx_freelist.
|
|
*/
|
|
.begin
|
|
.reg temp
|
|
.reg temp2
|
|
|
|
immed[add_to_rx_freelist, RX_THREAD_FREELIST_0]
|
|
immed_w1[add_to_rx_freelist, (&$rsw0 | (&mpacket_arrived << 12))]
|
|
|
|
local_csr_rd[ACTIVE_CTX_STS]
|
|
immed[temp, 0]
|
|
alu[temp2, temp, and, 0x1f]
|
|
alu_shf[add_to_rx_freelist, add_to_rx_freelist, or, temp2, <<20]
|
|
alu[temp2, temp, and, 0x80]
|
|
alu_shf[add_to_rx_freelist, add_to_rx_freelist, or, temp2, <<18]
|
|
.end
|
|
|
|
immed[zero, 0]
|
|
|
|
/*
|
|
* Skip context 0 initialisation?
|
|
*/
|
|
.begin
|
|
br!=ctx[0, mpacket_receive_loop#]
|
|
.end
|
|
|
|
/*
|
|
* Initialise local memory.
|
|
*/
|
|
.begin
|
|
.reg addr
|
|
.reg temp
|
|
|
|
immed[temp, 0]
|
|
init_local_mem_loop#:
|
|
alu_shf[addr, --, b, temp, <<CHANNEL_STATE_SHIFT]
|
|
local_csr_wr[ACTIVE_LM_ADDR_0, addr]
|
|
nop
|
|
nop
|
|
nop
|
|
|
|
immed[CHANNEL_FLAGS, 0]
|
|
|
|
alu[temp, temp, +, 1]
|
|
alu[--, temp, and, 0x20]
|
|
beq[init_local_mem_loop#]
|
|
.end
|
|
|
|
/*
|
|
* Initialise signal pipeline.
|
|
*/
|
|
.begin
|
|
local_csr_wr[SAME_ME_SIGNAL, (&sig1 << 3)]
|
|
.set_sig sig1
|
|
|
|
local_csr_wr[SAME_ME_SIGNAL, (&sig2 << 3)]
|
|
.set_sig sig2
|
|
|
|
local_csr_wr[SAME_ME_SIGNAL, (&sig3 << 3)]
|
|
.set_sig sig3
|
|
.end
|
|
|
|
mpacket_receive_loop#:
|
|
/*
|
|
* Synchronise and wait for mpacket.
|
|
*/
|
|
.begin
|
|
ctx_arb[sig1]
|
|
local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig1 << 3))]
|
|
|
|
msf[fast_wr, --, add_to_rx_freelist, 0]
|
|
.set_sig mpacket_arrived
|
|
ctx_arb[mpacket_arrived]
|
|
.set $rsw0 $rsw1
|
|
.end
|
|
|
|
/*
|
|
* We halt if we see {inbparerr,parerr,null,soperror}.
|
|
*/
|
|
.begin
|
|
alu_shf[--, 0x1b, and, $rsw0, >>8]
|
|
bne[abort_rswerr#]
|
|
.end
|
|
|
|
/*
|
|
* Point local memory pointer to this channel's state area.
|
|
*/
|
|
.begin
|
|
.reg chanaddr
|
|
|
|
alu[chanaddr, $rsw0, and, 0x1f]
|
|
alu_shf[chanaddr, --, b, chanaddr, <<CHANNEL_STATE_SHIFT]
|
|
local_csr_wr[ACTIVE_LM_ADDR_0, chanaddr]
|
|
nop
|
|
nop
|
|
nop
|
|
.end
|
|
|
|
/*
|
|
* Check whether we received a SOP mpacket while we were already
|
|
* working on a packet, or a non-SOP mpacket while there was no
|
|
* packet pending. (SOP == RECEIVING -> abort) If everything's
|
|
* okay, update the RECEIVING flag to reflect our new state.
|
|
*/
|
|
.begin
|
|
.reg temp
|
|
.reg eop
|
|
|
|
#if CHANNEL_FLAG_RECEIVING != 1
|
|
#error CHANNEL_FLAG_RECEIVING is not 1
|
|
#endif
|
|
|
|
alu_shf[temp, 1, and, $rsw0, >>15]
|
|
alu[temp, temp, xor, CHANNEL_FLAGS]
|
|
alu[--, temp, and, CHANNEL_FLAG_RECEIVING]
|
|
beq[abort_proterr#]
|
|
|
|
alu_shf[eop, 1, and, $rsw0, >>14]
|
|
alu[CHANNEL_FLAGS, temp, xor, eop]
|
|
.end
|
|
|
|
/*
|
|
* Copy the mpacket into the right spot, and in case of EOP,
|
|
* write back the descriptor and pass the packet on.
|
|
*/
|
|
.begin
|
|
.reg buffer_offset
|
|
.reg _packet_length
|
|
.reg _packet_checksum
|
|
.reg _buffer_handle
|
|
.reg _buffer_start
|
|
.reg _buffer_length
|
|
|
|
/*
|
|
* Determine buffer_offset, _packet_length and
|
|
* _packet_checksum.
|
|
*/
|
|
.begin
|
|
.reg temp
|
|
|
|
alu[--, 1, and, $rsw0, >>15]
|
|
beq[not_sop#]
|
|
|
|
immed[PACKET_LENGTH, 0]
|
|
immed[PACKET_CHECKSUM, 0]
|
|
|
|
not_sop#:
|
|
alu[buffer_offset, --, b, PACKET_LENGTH]
|
|
alu_shf[temp, 0xff, and, $rsw0, >>16]
|
|
alu[_packet_length, buffer_offset, +, temp]
|
|
alu[PACKET_LENGTH, --, b, _packet_length]
|
|
|
|
immed[temp, 0xffff]
|
|
alu[temp, $rsw1, and, temp]
|
|
alu[_packet_checksum, PACKET_CHECKSUM, +, temp]
|
|
alu[PACKET_CHECKSUM, --, b, _packet_checksum]
|
|
.end
|
|
|
|
/*
|
|
* Allocate buffer in case of SOP.
|
|
*/
|
|
.begin
|
|
.reg temp
|
|
|
|
alu[temp, 1, and, $rsw0, >>15]
|
|
beq[skip_buffer_alloc#]
|
|
|
|
.begin
|
|
.sig zzz
|
|
.reg read $stemp $stemp2
|
|
.xfer_order $stemp $stemp2
|
|
|
|
rx_nobufs#:
|
|
scratch[get, $stemp, zero, 0, 1], ctx_swap[zzz]
|
|
alu[_buffer_handle, --, b, $stemp]
|
|
beq[rx_nobufs#]
|
|
|
|
sram[read, $stemp, _buffer_handle, 0, 2],
|
|
ctx_swap[zzz]
|
|
alu[_buffer_start, --, b, $stemp]
|
|
alu[_buffer_length, --, b, $stemp2]
|
|
.end
|
|
|
|
skip_buffer_alloc#:
|
|
.end
|
|
|
|
/*
|
|
* Resynchronise.
|
|
*/
|
|
.begin
|
|
ctx_arb[sig2]
|
|
local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig2 << 3))]
|
|
.end
|
|
|
|
/*
|
|
* Synchronise buffer state.
|
|
*/
|
|
.begin
|
|
.reg temp
|
|
|
|
alu[temp, 1, and, $rsw0, >>15]
|
|
beq[copy_from_local_mem#]
|
|
|
|
alu[BUFFER_HANDLE, --, b, _buffer_handle]
|
|
alu[BUFFER_START, --, b, _buffer_start]
|
|
alu[BUFFER_LENGTH, --, b, _buffer_length]
|
|
br[sync_state_done#]
|
|
|
|
copy_from_local_mem#:
|
|
alu[_buffer_handle, --, b, BUFFER_HANDLE]
|
|
alu[_buffer_start, --, b, BUFFER_START]
|
|
alu[_buffer_length, --, b, BUFFER_LENGTH]
|
|
|
|
sync_state_done#:
|
|
.end
|
|
|
|
#if 0
|
|
/*
|
|
* Debug buffer state management.
|
|
*/
|
|
.begin
|
|
.reg temp
|
|
|
|
alu[temp, 1, and, $rsw0, >>14]
|
|
beq[no_poison#]
|
|
immed[BUFFER_HANDLE, 0xdead]
|
|
immed[BUFFER_START, 0xdead]
|
|
immed[BUFFER_LENGTH, 0xdead]
|
|
no_poison#:
|
|
|
|
immed[temp, 0xdead]
|
|
alu[--, _buffer_handle, -, temp]
|
|
beq[state_corrupted#]
|
|
alu[--, _buffer_start, -, temp]
|
|
beq[state_corrupted#]
|
|
alu[--, _buffer_length, -, temp]
|
|
beq[state_corrupted#]
|
|
.end
|
|
#endif
|
|
|
|
/*
|
|
* Check buffer length.
|
|
*/
|
|
.begin
|
|
alu[--, _buffer_length, -, _packet_length]
|
|
blo[buffer_overflow#]
|
|
.end
|
|
|
|
/*
|
|
* Copy the mpacket and give back the RBUF element.
|
|
*/
|
|
.begin
|
|
.reg element
|
|
.reg xfer_size
|
|
.reg temp
|
|
.sig copy_sig
|
|
|
|
alu_shf[element, 0x7f, and, $rsw0, >>24]
|
|
alu_shf[xfer_size, 0xff, and, $rsw0, >>16]
|
|
|
|
alu[xfer_size, xfer_size, -, 1]
|
|
alu_shf[xfer_size, 0x10, or, xfer_size, >>3]
|
|
alu_shf[temp, 0x10, or, xfer_size, <<21]
|
|
alu_shf[temp, temp, or, element, <<11]
|
|
alu_shf[--, temp, or, 1, <<18]
|
|
|
|
dram[rbuf_rd, --, _buffer_start, buffer_offset, max_8],
|
|
indirect_ref, sig_done[copy_sig]
|
|
ctx_arb[copy_sig]
|
|
|
|
alu[temp, RBUF_ELEMENT_DONE, or, element, <<16]
|
|
msf[fast_wr, --, temp, 0]
|
|
.end
|
|
|
|
/*
|
|
* If EOP, write back the packet descriptor.
|
|
*/
|
|
.begin
|
|
.reg write $stemp $stemp2
|
|
.xfer_order $stemp $stemp2
|
|
.sig zzz
|
|
|
|
alu_shf[--, 1, and, $rsw0, >>14]
|
|
beq[no_writeback#]
|
|
|
|
alu[$stemp, $rsw0, and, 0x1f]
|
|
alu[$stemp2, --, b, _packet_length]
|
|
sram[write, $stemp, _buffer_handle, 8, 2], ctx_swap[zzz]
|
|
|
|
no_writeback#:
|
|
.end
|
|
|
|
/*
|
|
* Resynchronise.
|
|
*/
|
|
.begin
|
|
ctx_arb[sig3]
|
|
local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig3 << 3))]
|
|
.end
|
|
|
|
/*
|
|
* If EOP, put the buffer back onto the scratch ring.
|
|
*/
|
|
.begin
|
|
.reg write $stemp
|
|
.sig zzz
|
|
|
|
br_inp_state[SCR_Ring1_Status, rx_done_ring_overflow#]
|
|
|
|
alu_shf[--, 1, and, $rsw0, >>14]
|
|
beq[mpacket_receive_loop#]
|
|
|
|
alu[--, 1, and, $rsw0, >>10]
|
|
bne[rxerr#]
|
|
|
|
alu[$stemp, --, b, _buffer_handle]
|
|
scratch[put, $stemp, zero, 4, 1], ctx_swap[zzz]
|
|
cap[fast_wr, 0, XSCALE_INT_A]
|
|
br[mpacket_receive_loop#]
|
|
|
|
rxerr#:
|
|
alu[$stemp, --, b, _buffer_handle]
|
|
scratch[put, $stemp, zero, 0, 1], ctx_swap[zzz]
|
|
br[mpacket_receive_loop#]
|
|
.end
|
|
.end
|
|
|
|
|
|
abort_rswerr#:
|
|
halt
|
|
|
|
abort_proterr#:
|
|
halt
|
|
|
|
state_corrupted#:
|
|
halt
|
|
|
|
buffer_overflow#:
|
|
halt
|
|
|
|
rx_done_ring_overflow#:
|
|
halt
|
|
|
|
|
|
|