diff options
author | Joel Sherrill <joel.sherrill@OARcorp.com> | 1999-02-17 20:24:53 +0000 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@OARcorp.com> | 1999-02-17 20:24:53 +0000 |
commit | ee733965291f61dd959c0f75659b0482df7a64ca (patch) | |
tree | 1b7cd7bafa0e265b562fb8c6df14070293161c9a /c/src/lib/libbsp/powerpc/eth_comm/network | |
parent | Patch from Eric Valette <valette@crf.canon.fr> to undo the patch (diff) | |
download | rtems-ee733965291f61dd959c0f75659b0482df7a64ca.tar.bz2 |
Jay Monkman <jmonkman@frasca.com> submitted the eth_comm BSP for a PPC860
based board.
Diffstat (limited to 'c/src/lib/libbsp/powerpc/eth_comm/network')
-rw-r--r-- | c/src/lib/libbsp/powerpc/eth_comm/network/Makefile.in | 55 | ||||
-rw-r--r-- | c/src/lib/libbsp/powerpc/eth_comm/network/README | 294 | ||||
-rw-r--r-- | c/src/lib/libbsp/powerpc/eth_comm/network/network.c | 1620 |
3 files changed, 1969 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/powerpc/eth_comm/network/Makefile.in b/c/src/lib/libbsp/powerpc/eth_comm/network/Makefile.in new file mode 100644 index 0000000000..59728b648d --- /dev/null +++ b/c/src/lib/libbsp/powerpc/eth_comm/network/Makefile.in @@ -0,0 +1,55 @@ +# +# $Id$ +# + +@SET_MAKE@ +srcdir = @srcdir@ +VPATH = @srcdir@ +RTEMS_ROOT = @top_srcdir@ +PROJECT_ROOT = @PROJECT_ROOT@ + +PGM=${ARCH}/network.rel + +# C source names, if any, go here -- minus the .c +C_PIECES=network +C_FILES=$(C_PIECES:%=%.c) +C_O_FILES=$(C_PIECES:%=${ARCH}/%.o) + +H_FILES= + +SRCS=$(C_FILES) $(H_FILES) +OBJS=$(C_O_FILES) + +include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg +include $(RTEMS_ROOT)/make/leaf.cfg + +# +# (OPTIONAL) Add local stuff here using += +# + +DEFINES += -D_COMPILING_BSD_KERNEL_ -DKERNEL -DINET -DNFS \ + -DDIAGNOSTIC -DBOOTP_COMPAT +CPPFLAGS += +CFLAGS += + +LD_PATHS += +LD_LIBS += +LDFLAGS += + +# +# Add your list of files to delete here. The config files +# already know how to delete some stuff, so you may want +# to just run 'make clean' first to see what gets missed. +# 'make clobber' already includes 'make clean' +# + +CLEAN_ADDITIONS += +CLOBBER_ADDITIONS += + +${PGM}: ${SRCS} ${OBJS} + $(make-rel) + +all: ${ARCH} $(SRCS) $(PGM) + +# the .rel file built here will be put into libbsp.a by ../wrapup/Makefile +install: all diff --git a/c/src/lib/libbsp/powerpc/eth_comm/network/README b/c/src/lib/libbsp/powerpc/eth_comm/network/README new file mode 100644 index 0000000000..8e14552641 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/eth_comm/network/README @@ -0,0 +1,294 @@ +# +# $Id$ +# + +# +# This package requires a version of GCC that supports the `-mcpu32' option. +# + +# +# Please send any comments, improvements, or bug reports to: +# W. Eric Norum +# Saskatchewan Accelerator Laboratory +# 107 North Road +# University of Saskatchewan +# Saskatoon, Saskatchewan, CANADA +# S7N 5C6 +# eric@skatter.usask.ca +# + +# +# This board support package works with several different versions of +# MC68360 systems. The choice of hardware is made at the final link-edit +# phase by setting the Makefile CLAGS_LD definition appropriately. +# +# Decisions made at compile time include: +# - If the CPU is a member of the 68040 family, the BSP is +# compiled for a generic 68040/68360 system as described +# in Chapter 9 of the MC68360 User's Manual. +# - If the preprocessor symbol M68360_ATLAS_HSB is defined, +# the BSP is compiled for an Atlas HSB card. +# - Otherwise, the BSP is compiled for a generic 68360 system +# as described in Chapter 9 of the MC68360 User's Manual. +# +# Decisions to be made a link-edit time are: +# - The size of the memory allocator heap. The default value is +# 64 kbytes. If the network package is used the heap +# should be at least 256 kbytes. If your network is large, or +# busy, the heap should be even larger. +# To choose a heap size of 256 kbytes, +# CFLAGS_LD += -Wl,--defsym -Wl,HeapSize=0x40000 + +BSP NAME: gen68360 or gen68360_040 +BOARD: Generic 68360 as described in Motorola MC68360 User's Manual +BOARD: Atlas Computer Equipment Inc. High Speed Bridge (HSB) +BOARD: Atlas Computer Equipment Inc. Advanced Communication Engine (ACE) +BOARD: Arnewsh SBC360 68040/68360 card +BUS: none +CPU FAMILY: Motorola CPU32+, Motorola 68040 +COPROCESSORS: none +MODE: not applicable + +DEBUG MONITOR: none (Hardware provides BDM) + +PERIPHERALS +=========== +TIMERS: PIT, Watchdog, 4 general purpose, 16 RISC + RESOLUTION: one microsecond +SERIAL PORTS: 4 SCC, 2 SMC, 1 SPI +REAL-TIME CLOCK: +DMA: Each serial port, 2 general purpose +VIDEO: none +SCSI: none +NETWORKING: Ethernet on SCC1. + +DRIVER INFORMATION +================== +CLOCK DRIVER: Programmable Interval Timer +IOSUPP DRIVER: Serial Management Controller 1 +SHMSUPP: none +TIMER DRIVER: Timer 1 + +STDIO +===== +PORT: SMC1 +ELECTRICAL: EIA-232 (if board supplies level shifter) +BAUD: 9600 +BITS PER CHARACTER: 8 +PARITY: None +STOP BITS: 1 + +NOTES +===== + +Board description +----------------- +clock rate: 25 MHz +bus width: 8-bit PROM, 32-bit DRAM +ROM: To 1 MByte, 180 nsec (3 wait states), chip select 0 +RAM: 1 to 16 MByte DRAM SIMM, 60 nsec (0 wait states), parity or nonparity + +Host System +----------- +OPENSTEP 4.2 (Intel and Motorola), Solaris 2.5, Linux 2.0.29 + +Verification (Standalone 68360) +------------------------------- +Single processor tests: Passed +Multi-processort tests: not applicable +Timing tests: + Context Switch + + context switch: self 10 + context switch: to another task 11 + context switch: no floating point contexts 40 + fp context switch: restore 1st FP task 41 + fp context switch: save initialized, restore initialized 14 + fp context switch: save idle, restore initialized 14 + fp context switch: save idle, restore idle 43 + + Task Manager + + rtems_task_create 133 + rtems_task_ident 351 + rtems_task_start 77 + rtems_task_restart: calling task 93 + rtems_task_restart: suspended task -- returns to caller 90 + rtems_task_restart: blocked task -- returns to caller 120 + rtems_task_restart: ready task -- returns to caller 92 + rtems_task_restart: suspended task -- preempts caller 121 + rtems_task_restart: blocked task -- preempts caller 143 + rtems_task_restart: ready task -- preempts caller 138 + rtems_task_delete: calling task 158 + rtems_task_delete: suspended task 129 + rtems_task_delete: blocked task 134 + rtems_task_delete: ready task 136 + rtems_task_suspend: calling task 71 + rtems_task_suspend: returns to caller 47 + rtems_task_resume: task readied -- returns to caller 48 + rtems_task_resume: task readied -- preempts caller 67 + rtems_task_set_priority: obtain current priority 36 + rtems_task_set_priority: returns to caller 65 + rtems_task_set_priority: preempts caller 102 + rtems_task_mode: obtain current mode 13 + rtems_task_mode: no reschedule 15 + rtems_task_mode: reschedule -- returns to caller 22 + rtems_task_mode: reschedule -- preempts caller 61 + rtems_task_get_note 38 + rtems_task_set_note 37 + rtems_task_wake_after: yield -- returns to caller 22 + rtems_task_wake_after: yields -- preempts caller 56 + rtems_task_wake_when 110 + + Interrupt Manager + + interrupt entry overhead: returns to nested interrupt 8 + interrupt entry overhead: returns to interrupted task 8 + interrupt entry overhead: returns to preempting task 8 + interrupt exit overhead: returns to nested interrupt 7 + interrupt exit overhead: returns to interrupted task 8 + interrupt exit overhead: returns to preempting task 52 + + Clock Manager + + rtems_clock_set 82 + rtems_clock_get 2 + rtems_clock_tick 15 + + Timer Manager + + rtems_timer_create 33 + rtems_timer_ident 343 + rtems_timer_delete: inactive 47 + rtems_timer_delete: active 50 + rtems_timer_fire_after: inactive 59 + rtems_timer_fire_after: active 63 + rtems_timer_fire_when: inactive 83 + rtems_timer_fire_when: active 83 + rtems_timer_reset: inactive 55 + rtems_timer_reset: active 58 + rtems_timer_cancel: inactive 35 + rtems_timer_cancel: active 38 + + Semaphore Manager + + rtems_semaphore_create 62 + rtems_semaphore_ident 368 + rtems_semaphore_delete 61 + rtems_semaphore_obtain: available 42 + rtems_semaphore_obtain: not available -- NO_WAIT 42 + rtems_semaphore_obtain: not available -- caller blocks 105 + rtems_semaphore_release: no waiting tasks 46 + rtems_semaphore_release: task readied -- returns to caller 64 + rtems_semaphore_release: task readied -- preempts caller 84 + + Message Queue Manager + + rtems_message_queue_create 240 + rtems_message_queue_ident 342 + rtems_message_queue_delete 79 + rtems_message_queue_send: no waiting tasks 93 + rtems_message_queue_send: task readied -- returns to caller 96 + rtems_message_queue_send: task readied -- preempts caller 116 + rtems_message_queue_urgent: no waiting tasks 93 + rtems_message_queue_urgent: task readied -- returns to caller 97 + rtems_message_queue_urgent: task readied -- preempts caller 117 + rtems_message_queue_broadcast: no waiting tasks 54 + rtems_message_queue_broadcast: task readied -- returns to caller 106 + rtems_message_queue_broadcast: task readied -- preempts caller 126 + rtems_message_queue_receive: available 79 + rtems_message_queue_receive: not available -- NO_WAIT 48 + rtems_message_queue_receive: not available -- caller blocks 111 + rtems_message_queue_flush: no messages flushed 35 + rtems_message_queue_flush: messages flushed 44 + + Event Manager + + rtems_event_send: no task readied 30 + rtems_event_send: task readied -- returns to caller 59 + rtems_event_send: task readied -- preempts caller 81 + rtems_event_receive: obtain current events 1 + rtems_event_receive: available 34 + rtems_event_receive: not available -- NO_WAIT 31 + rtems_event_receive: not available -- caller blocks 84 + + Signal Manager + + rtems_signal_catch 24 + rtems_signal_send: returns to caller 42 + rtems_signal_send: signal to self 47 + exit ASR overhead: returns to calling task 33 + exit ASR overhead: returns to preempting task 58 + + Partition Manager + + rtems_partition_create 78 + rtems_partition_ident 342 + rtems_partition_delete 46 + rtems_partition_get_buffer: available 40 + rtems_partition_get_buffer: not available 39 + rtems_partition_return_buffer 47 + + Region Manager + + rtems_region_create 65 + rtems_region_ident 349 + rtems_region_delete 45 + rtems_region_get_segment: available 55 + rtems_region_get_segment: not available -- NO_WAIT 52 + rtems_region_get_segment: not available -- caller blocks 119 + rtems_region_return_segment: no waiting tasks 57 + rtems_region_return_segment: task readied -- returns to caller 106 + rtems_region_return_segment: task readied -- preempts caller 127 + + Dual-Ported Memory Manager + + rtems_port_create 40 + rtems_port_ident 342 + rtems_port_delete 44 + rtems_port_internal_to_external 32 + rtems_port_external_to_internal 32 + + IO Manager + + rtems_io_initialize 4 + rtems_io_open 1 + rtems_io_close 1 + rtems_io_read 1 + rtems_io_write 1 + rtems_io_control 1 + + Rate Monotonic Manager + + rtems_rate_monotonic_create 39 + rtems_rate_monotonic_ident 343 + rtems_rate_monotonic_cancel 43 + rtems_rate_monotonic_delete: active 54 + rtems_rate_monotonic_delete: inactive 52 + rtems_rate_monotonic_period: obtain status 37 + rtems_rate_monotonic_period: initiate period -- returns to caller 58 + rtems_rate_monotonic_period: conclude periods -- caller blocks 75 + +Porting +------- +This board support package is written for a 68360 system similar to that +described in chapter 9 of the Motorola MC68360 Quad Integrated Communication +Processor Users' Manual. The salient features of this hardware are: + + 25 MHz external clock + DRAM address multiplexing provided by 68360 + 8-bit 180nsec PROM to CS0* + 4 MBytes of 60 nsec parity DRAM (1Mx36) to RAS1*/CAS1* + Console serial port on SMC1 + Ethernet interface on SCC1 + +The board support package has been tested with: + A home-built 68360 board + An ACE360A and an HSB board produced by: + Atlas Computer Equipment + 703 Colina Lane + Santa Barbara, CA 93103 + A 68040/68360 board (SBC360) produced by: + Arnewsh Inc. + P.O. Box 270352 + Fort Collins, CO 80527-0352 diff --git a/c/src/lib/libbsp/powerpc/eth_comm/network/network.c b/c/src/lib/libbsp/powerpc/eth_comm/network/network.c new file mode 100644 index 0000000000..f9211c45b0 --- /dev/null +++ b/c/src/lib/libbsp/powerpc/eth_comm/network/network.c @@ -0,0 +1,1620 @@ +/* + * RTEMS/KA9Q driver for MPC860 SCC1 Ethernet + * + * Modified for MPC860 by Jay Monkman (jmonkman@frasca.com) + * + * This supports ethernet on either SCC1 or the FEC of the MPC860T. + * Right now, we only do 10 Mbps, even with the FEC. The function + * rtems_m860_enet_driver_attach determines which one to use. Currently, + * only one may be used at a time. + * + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + * + * $Id$ + */ +#include <bsp.h> +#include <mpc860.h> +#include <stdio.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +/* + * Number of interfaces supported by this driver + */ +#define NIFACES 1 + +/* + * Default number of buffer descriptors set aside for this driver. + * The number of transmit buffer descriptors has to be quite large + * since a single frame often uses four or more buffer descriptors. + */ +#define RX_BUF_COUNT 32 +#define TX_BUF_COUNT 8 +#define TX_BD_PER_BUF 4 + +/* + * RTEMS event used by interrupt handler to signal daemons. + * This must *not* be the same event used by the KA9Q task synchronization. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + * This must not be the same as INTERRUPT_EVENT. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +/* + * Receive buffer size -- Allow for a full ethernet packet plus a pointer + */ +#define RBUF_SIZE 1520 + +#if (MCLBYTES < RBUF_SIZE) +# error "Driver must have MCLBYTES > RBUF_SIZE" +#endif + +/* + * Per-device data + */ +struct m860_enet_struct { + struct arpcom arpcom; + struct mbuf **rxMbuf; + struct mbuf **txMbuf; + int acceptBroadcast; + int rxBdCount; + int txBdCount; + int txBdHead; + int txBdTail; + int txBdActiveCount; + m860BufferDescriptor_t *rxBdBase; + m860BufferDescriptor_t *txBdBase; + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + /* + * Statistics + */ + unsigned long rxInterrupts; + unsigned long rxNotFirst; + unsigned long rxNotLast; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxRunt; + unsigned long rxBadCRC; + unsigned long rxOverrun; + unsigned long rxCollision; + + unsigned long txInterrupts; + unsigned long txDeferred; + unsigned long txHeartbeat; + unsigned long txLateCollision; + unsigned long txRetryLimit; + unsigned long txUnderrun; + unsigned long txLostCarrier; + unsigned long txRawWait; +}; +static struct m860_enet_struct enet_driver[NIFACES]; + +/* + * SCC1 interrupt handler + */ +static rtems_isr +m860_scc1_interrupt_handler (rtems_vector_number v) +{ + /* + * Frame received? + */ + if ((m860.scc1.sccm & 0x8) && (m860.scc1.scce & 0x8)) { + m860.scc1.scce = 0x8; + /* I don't think the next line is needed. It was in + * the 68360 stuff, though. + * m860.scc1.sccm &= ~0x8; + */ + enet_driver[0].rxInterrupts++; + rtems_event_send (enet_driver[0].rxDaemonTid, INTERRUPT_EVENT); + } + + /* + * Buffer transmitted or transmitter error? + */ + if ((m860.scc1.sccm & 0x12) && (m860.scc1.scce & 0x12)) { + m860.scc1.scce = 0x12; + /* I don't think the next line is needed. It was in + * the 68360 stuff, though. + * m860.scc1.sccm &= ~0x12; + */ + enet_driver[0].txInterrupts++; + rtems_event_send (enet_driver[0].txDaemonTid, INTERRUPT_EVENT); + } + m860.cisr = 1UL << 30; /* Clear SCC1 interrupt-in-service bit */ +} + +/* + * FEC interrupt handler + */ +static rtems_isr +m860_fec_interrupt_handler (rtems_vector_number v) +{ + /* + * Frame received? + */ + if (m860.fec.ievent & M860_FEC_IEVENT_RFINT) { + m860.fec.ievent = M860_FEC_IEVENT_RFINT; + enet_driver[0].rxInterrupts++; + rtems_event_send (enet_driver[0].rxDaemonTid, INTERRUPT_EVENT); + } + + /* + * Buffer transmitted or transmitter error? + */ + if (m860.fec.ievent & M860_FEC_IEVENT_TFINT) { + m860.fec.ievent = M860_FEC_IEVENT_TFINT; + enet_driver[0].txInterrupts++; + rtems_event_send (enet_driver[0].txDaemonTid, INTERRUPT_EVENT); + } +} + +/* + * Initialize the ethernet hardware + */ +static void +m860_scc_initialize_hardware (struct m860_enet_struct *sc) +{ + int i; + unsigned char *hwaddr; + rtems_status_code status; + rtems_isr_entry old_handler; + + /* + * Configure port A CLK1, CLK2, TXD1 and RXD1 pins + */ + m860.papar |= 0x303; + m860.padir &= ~0x303; + m860.paodr &= ~0x303; + + /* + * Configure port C CTS1* and CD1* pins, and PC4-PC7 + * + */ + m860.pcpar &= ~0x30; + m860.pcdir |= 0x0f00; + m860.pcdir &= ~0x30; + m860.pcso |= 0x30; + m860.pcdat &= ~0x0f00; /* Clear LOOP */ + m860.pcdat |= 0x0700; /* Set FULDL, TPSQEL, TPAPCE */ + + /* + * Connect CLK1 and CLK2 to SCC1 + */ + m860.sicr &= ~0xFF; + m860.sicr |= (5 << 3) | 4; + + /* + * Initialize SDMA configuration register + */ + m860.sdcr = 1; + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc (sc->rxBdCount * sizeof *sc->rxMbuf, + M_MBUF, M_NOWAIT); + sc->txMbuf = malloc (sc->txBdCount * sizeof *sc->txMbuf, + M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic ("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = M860AllocateBufferDescriptors(sc->rxBdCount); + sc->txBdBase = M860AllocateBufferDescriptors(sc->txBdCount); + m860.scc1p.rbase = (char *)sc->rxBdBase - (char *)&m860; + m860.scc1p.tbase = (char *)sc->txBdBase - (char *)&m860; + + /* + * Send "Init parameters" command + */ + M860ExecuteRISC (M860_CR_OP_INIT_RX_TX | M860_CR_CHAN_SCC1); + + /* + * Set receive and transmit function codes + */ + m860.scc1p.rfcr = M860_RFCR_MOT | M860_RFCR_DMA_SPACE(0); + m860.scc1p.tfcr = M860_TFCR_MOT | M860_TFCR_DMA_SPACE(0); + + /* + * Set maximum receive buffer length + */ + m860.scc1p.mrblr = RBUF_SIZE; + + /* + * Set CRC parameters + */ + m860.scc1p.un.ethernet.c_pres = 0xFFFFFFFF; + m860.scc1p.un.ethernet.c_mask = 0xDEBB20E3; + + /* + * Clear diagnostic counters + */ + m860.scc1p.un.ethernet.crcec = 0; + m860.scc1p.un.ethernet.alec = 0; + m860.scc1p.un.ethernet.disfc = 0; + + /* + * Set pad value + */ + m860.scc1p.un.ethernet.pads = 0x8888; + + /* + * Set retry limit + */ + m860.scc1p.un.ethernet.ret_lim = 15; + + /* + * Set maximum and minimum frame length + */ + m860.scc1p.un.ethernet.mflr = 1518; + m860.scc1p.un.ethernet.minflr = 64; + m860.scc1p.un.ethernet.maxd1 = RBUF_SIZE; + m860.scc1p.un.ethernet.maxd2 = RBUF_SIZE; + + /* + * Clear group address hash table + */ + m860.scc1p.un.ethernet.gaddr1 = 0; + m860.scc1p.un.ethernet.gaddr2 = 0; + m860.scc1p.un.ethernet.gaddr3 = 0; + m860.scc1p.un.ethernet.gaddr4 = 0; + + /* + * Set our physical address + */ + hwaddr = sc->arpcom.ac_enaddr; + + m860.scc1p.un.ethernet.paddr_h = (hwaddr[5] << 8) | hwaddr[4]; + m860.scc1p.un.ethernet.paddr_m = (hwaddr[3] << 8) | hwaddr[2]; + m860.scc1p.un.ethernet.paddr_l = (hwaddr[1] << 8) | hwaddr[0]; + + /* + * Aggressive retry + */ + m860.scc1p.un.ethernet.p_per = 0; + + /* + * Clear individual address hash table + */ + m860.scc1p.un.ethernet.iaddr1 = 0; + m860.scc1p.un.ethernet.iaddr2 = 0; + m860.scc1p.un.ethernet.iaddr3 = 0; + m860.scc1p.un.ethernet.iaddr4 = 0; + + /* + * Clear temp address + */ + m860.scc1p.un.ethernet.taddr_l = 0; + m860.scc1p.un.ethernet.taddr_m = 0; + m860.scc1p.un.ethernet.taddr_h = 0; + + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) { + (sc->rxBdBase + i)->status = 0; + } + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + (sc->txBdBase + i)->status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + /* + * Clear any outstanding events + */ + m860.scc1.scce = 0xFFFF; + + /* + * Set up interrupts + */ + status = rtems_interrupt_catch (m860_scc1_interrupt_handler, + PPC_IRQ_CPM_SCC1, + &old_handler); + if (status != RTEMS_SUCCESSFUL) { + rtems_panic ("Can't attach M860 SCC1 interrupt handler: %s\n", + rtems_status_text (status)); + } + m860.scc1.sccm = 0; /* No interrupts unmasked till necessary */ + m860.cimr |= (1UL << 30); /* Enable SCC1 interrupt */ + + /* + * Set up General SCC Mode Register + * Ethernet configuration + */ + m860.scc1.gsmr_h = 0x0; + m860.scc1.gsmr_l = 0x1088000c; + + /* + * Set up data synchronization register + * Ethernet synchronization pattern + */ + m860.scc1.dsr = 0xd555; + + /* + * Set up protocol-specific mode register + * No Heartbeat check + * No force collision + * Discard short frames + * Individual address mode + * Ethernet CRC + * Not promisuous + * Ignore/accept broadcast packets as specified + * Normal backoff timer + * No loopback + * No input sample at end of frame + * 64-byte limit for late collision + * Wait 22 bits before looking for start of frame delimiter + * Disable full-duplex operation + */ + m860.scc1.psmr = 0x080A | (sc->acceptBroadcast ? 0 : 0x100); + + /* + * Enable the TENA (RTS1*) pin + */ + m860.pcpar |= 0x1; + m860.pcdir &= ~0x1; + + + /* + * Set up interrupts + * FIXME: DANGER: WARNING: + * CICR and SIMASK must be set in any module that uses + * the CPM. Currently those are console-generic.c and + * network.c. If the registers are not set the same + * in both places, strange things may happen. + * If they are only set in one place, then an application + * that used the other module won't work correctly. + * Put this comment in each module that sets these 2 registers + */ + m860.cicr = 0x00e43e80; /* SCaP=SCC1, SCbP=SCC2, SCcP=SCC3, + SCdP=SCC4, IRL=1, HP=SCC1, IEN=1 */ + m860.simask |= M860_SIMASK_LVM1; + + /* + * Enable receiver and transmitter + */ + m860.scc1.gsmr_l = 0x1088003c; +} + +static void +m860_fec_initialize_hardware (struct m860_enet_struct *sc) +{ + int i; + unsigned char *hwaddr; + rtems_status_code status; + rtems_isr_entry old_handler; + + /* + * Issue reset to FEC + */ + m860.fec.ecntrl=0x1; + + /* + * Put ethernet transciever in reset + */ + m860.pgcra |= 0x80; + + /* + * Configure I/O ports + */ + m860.pdpar = 0x1fff; + m860.pddir = 0x1c58; + + /* + * Take ethernet transciever out of reset + */ + m860.pgcra &= ~0x80; + + + /* + * Set SIU interrupt level to LVL2 + * + */ + m860.fec.ivec = 0x02 << 29; + + /* + * Set the TX and RX fifo sizes. For now, we'll split it evenly + */ + /* If you uncomment these, the FEC will not work right. + m860.fec.r_fstart = ((m860.fec.r_bound & 0x3ff) >> 2) & 0x3ff; + m860.fec.x_fstart = 0; + */ + + /* + * Set our physical address + */ + hwaddr = sc->arpcom.ac_enaddr; + + m860.fec.addr_low = (hwaddr[0] << 24) | (hwaddr[1] << 16) | + (hwaddr[2] << 8) | (hwaddr[3] << 0); + m860.fec.addr_high = (hwaddr[4] << 24) | (hwaddr[5] << 16); + + /* + * Clear the hash table + */ + m860.fec.hash_table_high = 0; + m860.fec.hash_table_low = 0; + + /* + * Set up receive buffer size + */ + m860.fec.r_buf_size = 0x5f0; /* set to 1520 */ + + /* + * Allocate mbuf pointers + */ + sc->rxMbuf = malloc (sc->rxBdCount * sizeof *sc->rxMbuf, + M_MBUF, M_NOWAIT); + sc->txMbuf = malloc (sc->txBdCount * sizeof *sc->txMbuf, + M_MBUF, M_NOWAIT); + if (!sc->rxMbuf || !sc->txMbuf) + rtems_panic ("No memory for mbuf pointers"); + + /* + * Set receiver and transmitter buffer descriptor bases + */ + sc->rxBdBase = M860AllocateBufferDescriptors(sc->rxBdCount); + sc->txBdBase = M860AllocateBufferDescriptors(sc->txBdCount); + m860.fec.r_des_start = (int)sc->rxBdBase; + m860.fec.x_des_start = (int)sc->txBdBase; + + /* + * Set up Receive Control Register: + * Not promiscuous mode + * MII mode + * Half duplex + * No loopback + */ + m860.fec.r_cntrl = 0x00000006; + + /* + * Set up Transmit Control Register: + * Half duplex + * No heartbeat + */ + m860.fec.x_cntrl = 0x00000000; + + /* + * Set up DMA function code: + * Big-endian + * DMA functino code = 0 + */ + m860.fec.fun_code = 0x78000000; + + /* + * Initialize SDMA configuration register + * SDMA ignores FRZ + * FEC not aggressive + * FEC arbitration ID = 0 => U-bus arbitration = 6 + * RISC arbitration ID = 1 => U-bus arbitration = 5 + */ + m860.sdcr = 1; + + /* + * Set MII speed to 2.5 MHz for 25 Mhz system clock + */ + m860.fec.mii_speed = 0x0a; + m860.fec.mii_data = 0x58021000; + + /* + * Set up receive buffer descriptors + */ + for (i = 0 ; i < sc->rxBdCount ; i++) + (sc->rxBdBase + i)->status = 0; + + /* + * Set up transmit buffer descriptors + */ + for (i = 0 ; i < sc->txBdCount ; i++) { + (sc->txBdBase + i)->status = 0; + sc->txMbuf[i] = NULL; + } + sc->txBdHead = sc->txBdTail = 0; + sc->txBdActiveCount = 0; + + + + /* + * Mask all FEC interrupts and clear events + */ + m860.fec.imask = M860_FEC_IEVENT_TFINT | + M860_FEC_IEVENT_RFINT; + m860.fec.ievent = ~0; + + /* + * Set up interrupts + */ + status = rtems_interrupt_catch (m860_fec_interrupt_handler, + PPC_IRQ_LVL2, + &old_handler); + if (status != RTEMS_SUCCESSFUL) + rtems_panic ("Can't attach M860 FEC interrupt handler: %s\n", + rtems_status_text (status)); + + m860.simask |= M860_SIMASK_LVM2; + +} + + +/* + * Soak up buffer descriptors that have been sent + * Note that a buffer descriptor can't be retired as soon as it becomes + * ready. The MC68360 Errata (May 96) says that, "If an Ethernet frame is + * made up of multiple buffers, the user should not reuse the first buffer + * descriptor until the last buffer descriptor of the frame has had its + * ready bit cleared by the CPM". + */ +static void +m860Enet_retire_tx_bd (struct m860_enet_struct *sc) +{ + rtems_unsigned16 status; + int i; + int nRetired; + struct mbuf *m, *n; + + i = sc->txBdTail; + nRetired = 0; + while ((sc->txBdActiveCount != 0) + && (((status = (sc->txBdBase + i)->status) & M860_BD_READY) == 0)) { + /* + * See if anything went wrong + */ + if (status & (M860_BD_DEFER | + M860_BD_HEARTBEAT | + M860_BD_LATE_COLLISION | + M860_BD_RETRY_LIMIT | + M860_BD_UNDERRUN | + M860_BD_CARRIER_LOST)) { + /* + * Check for errors which stop the transmitter. + */ + if (status & (M860_BD_LATE_COLLISION | + M860_BD_RETRY_LIMIT | + M860_BD_UNDERRUN)) { + if (status & M860_BD_LATE_COLLISION) + enet_driver[0].txLateCollision++; + if (status & M860_BD_RETRY_LIMIT) + enet_driver[0].txRetryLimit++; + if (status & M860_BD_UNDERRUN) + enet_driver[0].txUnderrun++; + + /* + * Restart the transmitter + */ + /* FIXME: this should get executed only if using the SCC */ + M860ExecuteRISC (M860_CR_OP_RESTART_TX | M860_CR_CHAN_SCC1); + } + if (status & M860_BD_DEFER) + enet_driver[0].txDeferred++; + if (status & M860_BD_HEARTBEAT) + enet_driver[0].txHeartbeat++; + if (status & M860_BD_CARRIER_LOST) + enet_driver[0].txLostCarrier++; + } + nRetired++; + if (status & M860_BD_LAST) { + /* + * A full frame has been transmitted. + * Free all the associated buffer descriptors. + */ + sc->txBdActiveCount -= nRetired; + while (nRetired) { + nRetired--; + m = sc->txMbuf[sc->txBdTail]; + MFREE (m, n); + if (++sc->txBdTail == sc->txBdCount) + sc->txBdTail = 0; + } + } + if (++i == sc->txBdCount) + i = 0; + } +} + +/* + * reader task + */ +static void +scc_rxDaemon (void *arg) +{ + struct m860_enet_struct *sc = (struct m860_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_unsigned16 status; + m860BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + rxBd->status = M860_BD_EMPTY | M860_BD_INTERRUPT; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= M860_BD_WRAP; + break; + } + } + + /* + * Input packet handling loop + */ + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & M860_BD_EMPTY) { + /* + * Clear old events + */ + m860.scc1.scce = 0x8; + + /* + * Wait for packet + * Note that the buffer descriptor is checked + * *before* the event wait -- this catches the + * possibility that a packet arrived between the + * `if' above, and the clearing of the event register. + */ + while ((status = rxBd->status) & M860_BD_EMPTY) { + rtems_event_set events; + + /* + * Unmask RXF (Full frame received) event + */ + m860.scc1.sccm |= 0x8; + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + } + + /* + * Check that packet is valid + */ + if ((status & (M860_BD_LAST | + M860_BD_FIRST_IN_FRAME | + M860_BD_LONG | + M860_BD_NONALIGNED | + M860_BD_SHORT | + M860_BD_CRC_ERROR | + M860_BD_OVERRUN | + M860_BD_COLLISION)) == + (M860_BD_LAST | + M860_BD_FIRST_IN_FRAME)) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + + m = sc->rxMbuf[rxBdIndex]; + m->m_len = m->m_pkthdr.len = rxBd->length - + sizeof(rtems_unsigned32) - + sizeof(struct ether_header); + eh = mtod (m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input (ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + } + else { + /* + * Something went wrong with the reception + */ + if (!(status & M860_BD_LAST)) + sc->rxNotLast++; + if (!(status & M860_BD_FIRST_IN_FRAME)) + sc->rxNotFirst++; + if (status & M860_BD_LONG) + sc->rxGiant++; + if (status & M860_BD_NONALIGNED) + sc->rxNonOctet++; + if (status & M860_BD_SHORT) + sc->rxRunt++; + if (status & M860_BD_CRC_ERROR) + sc->rxBadCRC++; + if (status & M860_BD_OVERRUN) + sc->rxOverrun++; + if (status & M860_BD_COLLISION) + sc->rxCollision++; + } + + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & (M860_BD_WRAP | M860_BD_INTERRUPT)) | + M860_BD_EMPTY; + + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void +fec_rxDaemon (void *arg) +{ + struct m860_enet_struct *sc = (struct m860_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_unsigned16 status; + m860BufferDescriptor_t *rxBd; + int rxBdIndex; + + /* + * Allocate space for incoming packets and start reception + */ + for (rxBdIndex = 0 ; ;) { + rxBd = sc->rxBdBase + rxBdIndex; + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + rxBd->status = M860_BD_EMPTY; + m860.fec.r_des_active = 0x1000000; + if (++rxBdIndex == sc->rxBdCount) { + rxBd->status |= M860_BD_WRAP; + break; + } + } + + /* + * Input packet handling loop + */ + rxBdIndex = 0; + for (;;) { + rxBd = sc->rxBdBase + rxBdIndex; + + /* + * Wait for packet if there's not one ready + */ + if ((status = rxBd->status) & M860_BD_EMPTY) { + /* + * Clear old events + */ + m860.fec.ievent = M860_FEC_IEVENT_RFINT; + + /* + * Wait for packet + * Note that the buffer descriptor is checked + * *before* the event wait -- this catches the + * possibility that a packet arrived between the + * `if' above, and the clearing of the event register. + */ + while ((status = rxBd->status) & M860_BD_EMPTY) { + rtems_event_set events; + + /* + * Unmask RXF (Full frame received) event + */ + m860.fec.ievent |= M860_FEC_IEVENT_RFINT; + + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + } + } + + /* + * Check that packet is valid + */ + if (status & M860_BD_LAST) { + /* + * Pass the packet up the chain. + * FIXME: Packet filtering hook could be done here. + */ + struct ether_header *eh; + + m = sc->rxMbuf[rxBdIndex]; + m->m_len = m->m_pkthdr.len = rxBd->length - + sizeof(rtems_unsigned32) - + sizeof(struct ether_header); + eh = mtod (m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + ether_input (ifp, eh, m); + + /* + * Allocate a new mbuf + */ + MGETHDR (m, M_WAIT, MT_DATA); + MCLGET (m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rxMbuf[rxBdIndex] = m; + rxBd->buffer = mtod (m, void *); + } + else { + /* + * Something went wrong with the reception + */ + if (!(status & M860_BD_LAST)) + sc->rxNotLast++; + if (status & M860_BD_LONG) + sc->rxGiant++; + if (status & M860_BD_NONALIGNED) + sc->rxNonOctet++; + if (status & M860_BD_SHORT) + sc->rxRunt++; + if (status & M860_BD_CRC_ERROR) + sc->rxBadCRC++; + if (status & M860_BD_OVERRUN) + sc->rxOverrun++; + if (status & M860_BD_COLLISION) + sc->rxCollision++; + } + /* + * Reenable the buffer descriptor + */ + rxBd->status = (status & M860_BD_WRAP) | + M860_BD_EMPTY; + m860.fec.r_des_active = 0x1000000; + /* + * Move to next buffer descriptor + */ + if (++rxBdIndex == sc->rxBdCount) + rxBdIndex = 0; + } +} + +static void +scc_sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct m860_enet_struct *sc = ifp->if_softc; + volatile m860BufferDescriptor_t *firstTxBd, *txBd; + struct mbuf *l = NULL; + rtems_unsigned16 status; + int nAdded; + + /* + * Free up buffer descriptors + */ + m860Enet_retire_tx_bd (sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + txBd = firstTxBd = sc->txBdBase + sc->txBdHead; + for (;;) { + /* + * Wait for buffer descriptor to become available. + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events + */ + m860.scc1.scce = 0x12; + + /* + * Wait for buffer descriptor to become available. + * Note that the buffer descriptors are checked + * *before* * entering the wait loop -- this catches + * the possibility that a buffer descriptor became + * available between the `if' above, and the clearing + * of the event register. + * This is to catch the case where the transmitter + * stops in the middle of a frame -- and only the + * last buffer descriptor in a frame can generate + * an interrupt. + */ + m860Enet_retire_tx_bd (sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + + /* + * Unmask TXB (buffer transmitted) and + * TXE (transmitter error) events. + */ + m860.scc1.sccm |= 0x12; + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + m860Enet_retire_tx_bd (sc); + } + } + + /* + * Don't set the READY flag till the + * whole packet has been readied. + */ + status = nAdded ? M860_BD_READY : 0; + + /* + * FIXME: Why not deal with empty mbufs at at higher level? + * The IP fragmentation routine in ip_output + * can produce packet fragments with zero length. + * I think that ip_output should be changed to get + * rid of these zero-length mbufs, but for now, + * I'll deal with them here. + */ + if (m->m_len) { + /* + * Fill in the buffer descriptor + */ + txBd->buffer = mtod (m, void *); + txBd->length = m->m_len; + sc->txMbuf[sc->txBdHead] = m; + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= M860_BD_WRAP; + sc->txBdHead = 0; + } + l = m; + m = m->m_next; + } + else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE (m, n); + m = n; + if (l != NULL) + l->m_next = m; + } + + /* + * Set the transmit buffer status. + * Break out of the loop if this mbuf is the last in the frame. + */ + if (m == NULL) { + if (nAdded) { + status |= M860_BD_PAD | M860_BD_LAST | M860_BD_TX_CRC | M860_BD_INTERRUPT; + txBd->status = status; + firstTxBd->status |= M860_BD_READY; + sc->txBdActiveCount += nAdded; + } + break; + } + txBd->status = status; + txBd = sc->txBdBase + sc->txBdHead; + } +} + +static void +fec_sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct m860_enet_struct *sc = ifp->if_softc; + volatile m860BufferDescriptor_t *firstTxBd, *txBd; + /* struct mbuf *l = NULL; */ + rtems_unsigned16 status; + int nAdded; + + /* + * Free up buffer descriptors + */ + m860Enet_retire_tx_bd (sc); + + /* + * Set up the transmit buffer descriptors. + * No need to pad out short packets since the + * hardware takes care of that automatically. + * No need to copy the packet to a contiguous buffer + * since the hardware is capable of scatter/gather DMA. + */ + nAdded = 0; + txBd = firstTxBd = sc->txBdBase + sc->txBdHead; + for (;;) { + /* + * Wait for buffer descriptor to become available. + */ + if ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + /* + * Clear old events + */ + m860.fec.ievent = M860_FEC_IEVENT_TFINT; + + /* + * Wait for buffer descriptor to become available. + * Note that the buffer descriptors are checked + * *before* * entering the wait loop -- this catches + * the possibility that a buffer descriptor became + * available between the `if' above, and the clearing + * of the event register. + * This is to catch the case where the transmitter + * stops in the middle of a frame -- and only the + * last buffer descriptor in a frame can generate + * an interrupt. + */ + m860Enet_retire_tx_bd (sc); + while ((sc->txBdActiveCount + nAdded) == sc->txBdCount) { + rtems_event_set events; + + /* + * Unmask TXB (buffer transmitted) and + * TXE (transmitter error) events. + */ + m860.fec.ievent |= M860_FEC_IEVENT_TFINT; + rtems_bsdnet_event_receive (INTERRUPT_EVENT, + RTEMS_WAIT|RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, + &events); + m860Enet_retire_tx_bd (sc); + } + } + + /* + * Don't set the READY flag till the + * whole packet has been readied. + */ + status = nAdded ? M860_BD_READY : 0; + + /* + * FIXME: Why not deal with empty mbufs at at higher level? + * The IP fragmentation routine in ip_output + * can produce packet fragments with zero length. + * I think that ip_output should be changed to get + * rid of these zero-length mbufs, but for now, + * I'll deal with them here. + */ + if (m->m_len) { + /* + * Fill in the buffer descriptor + */ + txBd->buffer = mtod (m, void *); + txBd->length = m->m_len; + sc->txMbuf[sc->txBdHead] = m; + nAdded++; + if (++sc->txBdHead == sc->txBdCount) { + status |= M860_BD_WRAP; + sc->txBdHead = 0; + } + /* l = m;*/ + m = m->m_next; + } + else { + /* + * Just toss empty mbufs + */ + struct mbuf *n; + MFREE (m, n); + m = n; + /* + if (l != NULL) + l->m_next = m; + */ + } + + /* + * Set the transmit buffer status. + * Break out of the loop if this mbuf is the last in the frame. + */ + if (m == NULL) { + if (nAdded) { + status |= M860_BD_LAST | M860_BD_TX_CRC; + txBd->status = status; + firstTxBd->status |= M860_BD_READY; + m860.fec.x_des_active = 0x1000000; + sc->txBdActiveCount += nAdded; + } + break; + } + txBd->status = status; + txBd = sc->txBdBase + sc->txBdHead; + } +} + +/* + * Driver transmit daemon + */ +void +scc_txDaemon (void *arg) +{ + struct m860_enet_struct *sc = (struct m860_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); + + /* + * Send packets till queue is empty + */ + for (;;) { + /* + * Get the next mbuf chain to transmit. + */ + IF_DEQUEUE(&ifp->if_snd, m); + if (!m) + break; + scc_sendpacket (ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + +void +fec_txDaemon (void *arg) +{ + struct m860_enet_struct *sc = (struct m860_enet_struct *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) { + /* + * Wait for packet + */ + rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + + /* + * Send packets till queue is empty + */ + for (;;) { + /* + * Get the next mbuf chain to transmit. + */ + IF_DEQUEUE(&ifp->if_snd, m); + if (!m) + break; + fec_sendpacket (ifp, m); + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + +/* + * Send packet (caller provides header). + */ +static void +m860_enet_start (struct ifnet *ifp) +{ + struct m860_enet_struct *sc = ifp->if_softc; + + rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +/* + * Initialize and start the device + */ +static void +scc_init (void *arg) +{ + struct m860_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + + /* + * Set up SCC hardware + */ + m860_scc_initialize_hardware (sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, scc_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, scc_rxDaemon, sc); + + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + m860.scc1.psmr |= 0x200; + else + m860.scc1.psmr &= ~0x200; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + m860.scc1.gsmr_l |= 0x30; +} + +static void +fec_init (void *arg) +{ + struct m860_enet_struct *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + if (sc->txDaemonTid == 0) { + + /* + * Set up SCC hardware + */ + m860_fec_initialize_hardware (sc); + + /* + * Start driver tasks + */ + sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, fec_txDaemon, sc); + sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, fec_rxDaemon, sc); + + } + + /* + * Set flags appropriately + */ + if (ifp->if_flags & IFF_PROMISC) + m860.fec.r_cntrl |= 0x8; + else + m860.fec.r_cntrl &= ~0x8; + + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* + * Enable receiver and transmitter + */ + m860.fec.ecntrl = 0x2; +} + + +/* + * Stop the device + */ +static void +scc_stop (struct m860_enet_struct *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + m860.scc1.gsmr_l &= ~0x30; +} + +static void +fec_stop (struct m860_enet_struct *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + /* + * Shut down receiver and transmitter + */ + m860.fec.ecntrl = 0x0; +} + +/* + * Show interface statistics + */ +static void +enet_stats (struct m860_enet_struct *sc) +{ + printf (" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf (" Not First:%-8lu", sc->rxNotFirst); + printf (" Not Last:%-8lu\n", sc->rxNotLast); + printf (" Giant:%-8lu", sc->rxGiant); + printf (" Runt:%-8lu", sc->rxRunt); + printf (" Non-octet:%-8lu\n", sc->rxNonOctet); + printf (" Bad CRC:%-8lu", sc->rxBadCRC); + printf (" Overrun:%-8lu", sc->rxOverrun); + printf (" Collision:%-8lu\n", sc->rxCollision); + printf (" Discarded:%-8lu\n", (unsigned long)m860.scc1p.un.ethernet.disfc); + + printf (" Tx Interrupts:%-8lu", sc->txInterrupts); + printf (" Deferred:%-8lu", sc->txDeferred); + printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat); + printf (" No Carrier:%-8lu", sc->txLostCarrier); + printf ("Retransmit Limit:%-8lu", sc->txRetryLimit); + printf (" Late Collision:%-8lu\n", sc->txLateCollision); + printf (" Underrun:%-8lu", sc->txUnderrun); + printf (" Raw output wait:%-8lu\n", sc->txRawWait); +} + +/* + * Driver ioctl handler + */ +static int +scc_ioctl (struct ifnet *ifp, int command, caddr_t data) +{ + struct m860_enet_struct *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + ether_ioctl (ifp, command, data); + break; + + case SIOCSIFFLAGS: + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + scc_stop (sc); + break; + + case IFF_UP: + scc_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + scc_stop (sc); + scc_init (sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + enet_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +static int +fec_ioctl (struct ifnet *ifp, int command, caddr_t data) +{ + struct m860_enet_struct *sc = ifp->if_softc; + int error = 0; + + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + ether_ioctl (ifp, command, data); + break; + + case SIOCSIFFLAGS: + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + fec_stop (sc); + break; + + case IFF_UP: + fec_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + fec_stop (sc); + fec_init (sc); + break; + + default: + break; + } + break; + + case SIO_RTEMS_SHOW_STATS: + enet_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } + return error; +} + +/* + * Attach an SCC driver to the system + */ +int +rtems_scc1_driver_attach (struct rtems_bsdnet_ifconfig *config) +{ + struct m860_enet_struct *sc; + struct ifnet *ifp; + int mtu; + int i; + + /* + * Find a free driver + */ + for (i = 0 ; i < NIFACES ; i++) { + sc = &enet_driver[i]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc == NULL) + break; + } + if (i >= NIFACES) { + printf ("Too many SCC drivers.\n"); + return 0; + } + + /* + * Process options + */ + if (config->hardware_address) { + memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } + else { + sc->arpcom.ac_enaddr[0] = 0x44; + sc->arpcom.ac_enaddr[1] = 0x22; + sc->arpcom.ac_enaddr[2] = 0x33; + sc->arpcom.ac_enaddr[3] = 0x33; + sc->arpcom.ac_enaddr[4] = 0x22; + sc->arpcom.ac_enaddr[5] = 0x44; + } + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + sc->acceptBroadcast = !config->ignore_broadcast; + + /* + * Set up network interface values + */ + ifp->if_softc = sc; + ifp->if_unit = i + 1; + ifp->if_name = "eth"; + ifp->if_mtu = mtu; + ifp->if_init = scc_init; + ifp->if_ioctl = scc_ioctl; + ifp->if_start = m860_enet_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* + * Attach the interface + */ + if_attach (ifp); + ether_ifattach (ifp); + return 1; +}; + +int +rtems_fec_driver_attach (struct rtems_bsdnet_ifconfig *config) +{ + struct m860_enet_struct *sc; + struct ifnet *ifp; + int mtu; + + /* + * Find a free driver + */ + sc = &enet_driver[0]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) + return 0; + + + /* + * Process options + */ + if (config->hardware_address) { + memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + } + else { + sc->arpcom.ac_enaddr[0] = 0x44; + sc->arpcom.ac_enaddr[1] = 0x22; + sc->arpcom.ac_enaddr[2] = 0x33; + sc->arpcom.ac_enaddr[3] = 0x33; + sc->arpcom.ac_enaddr[4] = 0x22; + sc->arpcom.ac_enaddr[5] = 0x44; + } + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxBdCount = config->rbuf_count; + else + sc->rxBdCount = RX_BUF_COUNT; + if (config->xbuf_count) + sc->txBdCount = config->xbuf_count; + else + sc->txBdCount = TX_BUF_COUNT * TX_BD_PER_BUF; + sc->acceptBroadcast = !config->ignore_broadcast; + + /* + * Set up network interface values + */ + ifp->if_softc = sc; + ifp->if_unit = 1; + ifp->if_name = "eth"; + ifp->if_mtu = mtu; + ifp->if_init = fec_init; + ifp->if_ioctl = fec_ioctl; + ifp->if_start = m860_enet_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + /* + * Attach the interface + */ + if_attach (ifp); + ether_ifattach (ifp); + return 1; +}; + +int +rtems_enet_driver_attach (struct rtems_bsdnet_ifconfig *config) +{ + int i; + + if ((m860.fec.mii_data & 0xffff) == 0x2000) { +/* rtems_scc1_driver_attach(config);*/ + return rtems_fec_driver_attach(config); + } + else { + return rtems_scc1_driver_attach(config); + } +} |