OpenCloudOS-Kernel/drivers/net/ethernet/xscale/ixp2000/ixp2400_rx.uc

409 lines
8.5 KiB
Ucode
Raw Normal View History

[PATCH] intel ixp2000 network driver 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>
2005-11-12 01:23:13 +08:00
/*
* 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