|  | /* | 
|  | * 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 | 
|  |  | 
|  |  |