mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 01:10:04 +00:00
![Lennert Buytenhek](/assets/img/avatar_default.png)
The way the hardware and firmware work is that there is one shared RX queue and IRQ for a number of different network interfaces. Due to this, we would like to process received packets for every interface in the same NAPI poll handler, so we need a pseudo-device to schedule polling on. What the driver currently does is that it always schedules polling for the first network interface in the list, and processes packets for every interface in the poll handler for that first interface -- however, this scheme breaks down if the first network interface happens to not be up, since netif_rx_schedule_prep() checks netif_running(). sky2 apparently has the same issue, and Stephen Hemminger suggested a way to work around this: create a variant of netif_rx_schedule_prep() that does not check netif_running(). I implemented this locally and called it netif_rx_schedule_prep_notup(), and it seems to work well, but it's something that probably not everyone would be happy with. The ixp2000 is an ARM CPU with a high-speed network interface in the CPU itself (full duplex 4Gb/s or 10Gb/s depending on the IXP model.) The CPU package also contains 8 or 16 (again depending on the IXP model) 'microengines', which are somewhat primitive but very fast and efficient processor cores which can be used to offload various things from the main CPU. This driver makes the high-speed network interface in the CPU visible and usable as a regular linux network device. Currently, it only supports the Radisys ENP2611 IXP board, but adding support for other board types should be fairly easy. Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
408 lines
8.5 KiB
Ucode
408 lines
8.5 KiB
Ucode
/*
|
|
* 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
|
|
|
|
|