MVME5500 status, and on to PMC interrupts
Kate Feng
feng1 at bnl.gov
Mon Feb 28 15:00:40 UTC 2005
Peter Dufault wrote:
> I used BSP_install_rtems_irq_handler to install what I thought
> would work, and caused the board to generate an interrupt, and I could
> see from its registers that it thought it was generating an interrupt,
> but nothing happened. That's when I found out that the "Marvell
> GT64260 system controller" on the MVME5500 is undocumented, and that
> you need an NDA to get the documentation or even to talk to technical
> support at Marvell.
Actaully, all the PMC related interrupts are described in the
"MVME5500 programmer's reference guide", which is downloadable
from the MOTOROLA.
If you are running from the CVS head, the PMC interrupts should
work. The PMC1 IRQ number is equivalent to
BSP_GPP8_IRQ_OFFSET, which is defined in irq.h. The PMC2 IRQ
number is BSP_GPP_PMC2_INTA. To be documentary, in irq.h, I added :
BSP_GPP_PMC1_INTA = BSP_GPP8_IRQ_OFFSET
Both of the VME interrupts and 1GHZ network interrupt work on my
board. If that is the case, the PMC interrupts should work unless there
is generic proramming difference between VME IRQ and PMC IRQ,
which I doubt. Attached is the code I wrote for the 1GHZ network
(e.g. PCI IRQ) for your reference regarding
BSP_install_rtems_irq_handler(). I have'nt spent time to optimize or
cleanup the code yet. However, it works fine. Additional support in
the RTEMS operating system is needed to support for checksum
offloading and TCP segmentation offload. You can reference the
VME code that Till Straumann wrote as well.
Cheers,
Kate
-------------- next part --------------
/* $NetBSD: if_wm.c,v 1.71 2004/05/16 02:34:47 thorpej Exp $ */
/*
* Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc.
*
* Copyright (c) 2003,2004 RTEMS/Mvme5500 port by S. Kate Feng <feng1 at bnl.gov>
*
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Wasabi Systems, Inc.
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* S. Kate Feng, other notes in addition to porting to RTEMS :
*
* 1) Additional support in the RTEMS operating system is needed to
* support for checksum offloading and TCP segmentation offload.
* The keyword search in the original driver is "checksum".
* 2) Enable the hardware Auto-Negotiation state machine.
* 3) Disable the link status change and receiving /C/ ordered sets interrupt
* because we do not use the software based Auto-Negotiation (forcing link).
* Thus there is no TBI mode of operation.
* 4) Set Big Endian mode in the WMREG_CTRL so that we do not need htole32
* because PPC is big endian mode.
* However, the data packet structure defined in if_wmreg.h
* should be redefined for the big endian mode.
* 5) Use the SYNCHRONIZATION BARRIERS or PCI_ORDERING to help software
* synchronization between the CPU and PCI activities ? (TO_CHECK) Or
* use WRTBK_PRIO_BUFFER (0x2d8)
* to configure the PCI1 as a cacheable region.
* 6) To ensure the cache coherence, the MOTLoad had the PCI
* snoop control registers (0x1f00) set to "snoop to WB region" for
* the entire 256MB of memory.
* 7) MOTLoad default :
* little endian mode, cache line size is 32 bytes, no checksum control,
* hardware auto-neg. state machine disabled. PCI control "snoop
* to WB region", MII mode (PHY) instead of TBI mode.
*/
#define BYTE_ORDER BIG_ENDIAN
#ifndef KERNEL
#define KERNEL
#endif
#define INET
#include <rtems.h>
#include <rtems/bspIo.h> /* printk */
#include <stdio.h> /* printf for statistics */
#include <string.h>
#include <libcpu/io.h> /* inp & friends */
#include <libcpu/spr.h> /* registers.h is included here */
#include <bsp.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <rtems/rtems_bsdnet.h>
#include <rtems/rtems_bsdnet_internal.h>
#include <rtems/error.h>
#include <errno.h>
#include <rtems/rtems/types.h>
#include <rtems/score/cpu.h>
#include <sys/queue.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h> /* SIOCADDMULTI, SIOC... */
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#ifdef INET
#include <netinet/in_var.h>
#endif
#include <bsp/irq.h>
#include <bsp/pci.h>
#include <bsp/pcireg.h>
#include <bsp/if_wmreg.h>
/*#define CKSUM_OFFLOAD*/
#define ETHERTYPE_FLOWCONTROL 0x8808 /* 802.3x flow control packet */
#define i82544EI_TASK_NAME "IGHZ"
#define SOFTC_ALIGN 4095
#define INTR_ERR_SIZE 16
/*#define WM_DEBUG*/
#ifdef WM_DEBUG
#define WM_DEBUG_LINK 0x01
#define WM_DEBUG_TX 0x02
#define WM_DEBUG_RX 0x04
#define WM_DEBUG_GMII 0x08
int wm_debug = WM_DEBUG_TX|WM_DEBUG_RX|WM_DEBUG_LINK;
#define DPRINTF(x, y) if (wm_debug & (x)) printk y
#else
#define DPRINTF(x, y) /* nothing */
#endif /* WM_DEBUG */
/* RTEMS event to kill the daemon */
#define KILL_EVENT RTEMS_EVENT_1
/* RTEMS event to (re)start the transmitter */
#define START_TRANSMIT_EVENT RTEMS_EVENT_2
/* RTEMS events used by the ISR */
#define RX_EVENT RTEMS_EVENT_3
#define TX_EVENT RTEMS_EVENT_4
#define ERR_EVENT RTEMS_EVENT_5
#define INIT_EVENT RTEMS_EVENT_6
#define ALL_EVENTS (KILL_EVENT|START_TRANSMIT_EVENT|RX_EVENT|TX_EVENT|ERR_EVENT|INIT_EVENT)
/*
* Transmit descriptor list size. Due to errata, we can only have
* 256 hardware descriptors in the ring. We tell the upper layers
* that they can queue a lot of packets, and we go ahead and manage
* up to 64 of them at a time. We allow up to 40 DMA segments per
* packet (there have been reports of jumbo frame packets with as
* many as 30 DMA segments!).
*/
#define WM_NTXSEGS 40
#define WM_IFQUEUELEN 256
#define WM_TXQUEUELEN 64
#define WM_TXQUEUELEN_MASK (WM_TXQUEUELEN - 1)
#define WM_TXQUEUE_GC (WM_TXQUEUELEN / 8)
#define NTXDESC 256
#define NTXDESC_MASK (NTXDESC - 1)
#define WM_NEXTTX(x) (((x) + 1) & NTXDESC_MASK)
#define TXSEG_MAX_LEN 16288
/*
* Receive descriptor list size. We have one Rx buffer for normal
* sized packets. Jumbo packets consume 5 Rx buffers for a full-sized
* packet. We allocate 128 receive descriptors, each with a 2k
* buffer (MCLBYTES), which gives us room for 25 jumbo packets.
*/
#define NRXDESC 128
#define NRXDESC_MASK (NRXDESC - 1)
#define WM_NEXTRX(x) (((x) + 1) & NRXDESC_MASK)
#define WM_PREVRX(x) (((x) - 1) & NRXDESC_MASK)
#define WM_CDOFF(x) offsetof(struct wm_control_data, x)
#define WM_CDTXOFF(x) WM_CDOFF(sc_txdescs[(x)])
#define WM_CDRXOFF(x) WM_CDOFF(sc_rxdescs[(x)])
#define TXQ_HiLmt_OFF 2
static uint32_t TxDescCmd;
/*
* Software state per device.
*/
struct wm_softc {
wiseman_txdesc_t sc_txdescs[NTXDESC]; /* transmit descriptor memory */
wiseman_rxdesc_t sc_rxdescs[NRXDESC]; /* receive descriptor memory */
struct mbuf *txs_mbuf[NTXDESC]; /* transmit buffer memory */
struct mbuf *rxs_mbuf[NRXDESC]; /* receive buffer memory */
struct wm_softc *next_module;
volatile unsigned int intr_errsts[INTR_ERR_SIZE]; /* intr_status */
unsigned int intr_err_ptr1; /* ptr used in GTeth_error() */
unsigned int intr_err_ptr2; /* ptr used in ISR */
int txs_firstdesc; /* first descriptor in packet */
int txs_lastdesc; /* last descriptor in packet */
int txs_ndesc; /* # of descriptors used */
volatile unsigned *sc_membase; /* Memory space base address */
unsigned *sc_memsize; /* Memory space size */
unsigned *sc_iobase; /* I/O space base address */
unsigned *sc_iosize; /* I/O space size */
char dv_xname[16]; /* external name (name + unit) */
void *sc_sdhook; /* shutdown hook */
struct arpcom arpcom; /* rtems if structure, contains ifnet */
int sc_flags; /* flags; see below */
int sc_bus_speed; /* PCI/PCIX bus speed */
int sc_pcix_offset; /* PCIX capability register offset */
int sc_flowflags; /* 802.3x flow control flags */
void *sc_ih; /* interrupt cookie */
int sc_ee_addrbits; /* EEPROM address bits */
int sc_align_tweak;
rtems_id daemonTid;
rtems_id daemonSync; /* synchronization with the daemon */
int txq_next; /* next Tx descriptor ready for transmitting */
uint32_t txq_nactive; /* number of active TX descriptors */
uint32_t txq_fi; /* next free Tx descriptor */
uint32_t txq_free; /* number of free Tx jobs */
uint32_t sc_txctx_ipcs; /* cached Tx IP cksum ctx */
uint32_t sc_txctx_tucs; /* cached Tx TCP/UDP cksum ctx */
int sc_rxptr; /* next ready Rx descriptor/queue ent */
int sc_rxdiscard;
int sc_rxlen;
struct mbuf *sc_rxhead;
struct mbuf *sc_rxtail;
struct mbuf **sc_rxtailp;
uint32_t sc_ctrl; /* prototype CTRL register */
#if 0
uint32_t sc_ctrl_ext; /* prototype CTRL_EXT register */
#endif
uint32_t sc_icr; /* prototype interrupt bits */
uint32_t sc_tctl; /* prototype TCTL register */
uint32_t sc_rctl; /* prototype RCTL register */
uint32_t sc_tipg; /* prototype TIPG register */
uint32_t sc_fcrtl; /* prototype FCRTL register */
int sc_mchash_type; /* multicast filter offset */
/* statistics */
struct {
volatile unsigned long rxInterrupts;
volatile unsigned long txInterrupts;
unsigned long txMultiBuffPacket;
unsigned long linkInterrupts;
unsigned long length_errors;
unsigned long frame_errors;
unsigned long crc_errors;
unsigned long rxOvrRunInterrupts; /* Rx overrun interrupt */
} stats;
};
/* <skf> our memory address seen from the PCI bus should be 1:1 */
#define htole32(x) le32toh(x)
#define le32toh(x) CPU_swap_u32((unsigned int) x)
#define le16toh(x) CPU_swap_u16(x)
#define WM_RXCHAIN_RESET(sc) \
do { \
(sc)->sc_rxtailp = &(sc)->sc_rxhead; \
*(sc)->sc_rxtailp = NULL; \
(sc)->sc_rxlen = 0; \
} while (/*CONSTCOND*/0)
#define WM_RXCHAIN_LINK(sc, m) \
do { \
*(sc)->sc_rxtailp = (sc)->sc_rxtail = (m); \
(sc)->sc_rxtailp = &(m)->m_next; \
} while (/*CONSTCOND*/0)
/* sc_flags */
#define WM_F_HAS_MII 0x01 /* has MII */
/* 82544 EI does not perform EEPROM handshake, EEPROM interface is not SPI */
#define WM_F_EEPROM_HANDSHAKE 0x02 /* requires EEPROM handshake */
#define WM_F_EEPROM_SPI 0x04 /* EEPROM is SPI */
#define WM_F_IOH_VALID 0x10 /* I/O handle is valid */
#define WM_F_BUS64 0x20 /* bus is 64-bit */
#define WM_F_PCIX 0x40 /* bus is PCI-X */
#ifdef PCI_ORDERING
extern void pciToCpuSync(int pci_num);
#define GT64260PCI1sync() pciToCpuSync(1)
#else
extern void CPU0_PciEnhanceSync(unsigned int syncVal);
#define GT64260PCI1sync() CPU0_PciEnhanceSync(6)
#endif
#define MEM_BASE 0xe1100000
#define CSR_READ(reg) in_le32((volatile unsigned *)(MEM_BASE+(reg)))
#define CSR_WRITE(reg, val) out_le32((volatile unsigned *)(MEM_BASE+(reg)), val)
#define WM_CDTXADDR(sc) ( (uint32_t) &sc->sc_txdescs[0] )
#define WM_CDRXADDR(sc) ( (uint32_t) &sc->sc_rxdescs[0] )
static struct wm_softc *root_i82544EI_dev = NULL;
static void i82544EI_ifstart(struct ifnet *ifp);
static int wm_ioctl(struct ifnet *ifp, u_long cmd,uint32_t data);
static void i82544EI_ifinit(void *arg);
static void wm_stop(struct ifnet *ifp, int disable);
static void wm_reset(struct wm_softc *sc);
static void wm_rxdrain(struct wm_softc *sc);
static int wm_add_rxbuf(struct wm_softc *sc, int idx);
static int wm_read_eeprom(struct wm_softc *sc,int word,int wordcnt, uint16_t *data);
static void i82544EI_daemon(void *arg);
static void wm_set_filter(struct wm_softc *sc);
static void i82544EI_isr();
static void i82544EI_sendpacket(struct wm_softc *sc, struct mbuf *m);
extern int pci_mem_find(), pci_io_find(), pci_get_capability();
extern char * ether_sprintf1();
uint32_t wm_io_read(struct wm_softc *sc, int reg)
{
out_le32((volatile unsigned *) sc->sc_iobase, reg);
return(in_le32((volatile unsigned *) (sc->sc_iobase+4)));
}
void wm_io_write(struct wm_softc *sc, int reg, uint32_t val)
{
out_le32((volatile unsigned *) sc->sc_iobase, reg);
out_le32((volatile unsigned *) (sc->sc_iobase+4), val);
}
static void i82544EI_irq_on(const rtems_irq_connect_data *irq)
{
}
static void i82544EI_irq_off(const rtems_irq_connect_data *irq)
{
}
static int ifwm_macaddr(char *hwaddr)
{
hwaddr[0] = 0x00;
hwaddr[1] = 0x01;
hwaddr[2] = 0xaf;
hwaddr[3] = 0x0b;
hwaddr[4] = 0xb4;
hwaddr[5] = 0x75;
return 0;
}
static int i82544EI_irq_is_on(const rtems_irq_connect_data *irq)
{
/* TODO */
return(1);
}
static rtems_irq_connect_data i82544IrqData={
BSP_GPP_82544_IRQ,
(rtems_irq_hdl) i82544EI_isr,
(rtems_irq_enable) i82544EI_irq_on,
(rtems_irq_disable) i82544EI_irq_off,
(rtems_irq_is_enabled) i82544EI_irq_is_on,
};
int rtems_i82544EI_driver_attach(struct rtems_bsdnet_ifconfig *config, int attach)
{
struct wm_softc *sc;
struct ifnet *ifp;
uint8_t enaddr[ETHER_ADDR_LEN];
uint16_t myea[ETHER_ADDR_LEN / 2], cfg1, cfg2, swdpin;
unsigned reg, preg;
int unit, pmreg;
void *softc_mem;
char *name;
unit = rtems_bsdnet_parse_driver_name(config, &name);
if (unit < 0) return 0;
printk("\nEthernet driver name %s unit %d \n",name, unit);
printk("Copyright (c) 2004 Allegro Networks, Inc., Wasabi Systems, Inc.\n");
printk("(c) 2004, S. Kate Feng <feng1 at bnl.gov> (RTEMS/mvme5500 port)\n");
/* Make sure certain elements e.g. descriptor lists are aligned.
softc_mem = rtems_bsdnet_malloc(sizeof(*sc) + SOFTC_ALIGN, M_FREE, M_NOWAIT);*/
softc_mem=(struct wm_softc *)calloc(1,sizeof(struct wm_softc)+SOFTC_ALIGN);
/* Check for the very unlikely case of no memory. */
if (softc_mem == NULL)
rtems_panic("i82544EI: OUT OF MEMORY");
sc = (void *)(((long)softc_mem + SOFTC_ALIGN) & ~SOFTC_ALIGN);
memset(sc, 0, sizeof(*sc));
sprintf(sc->dv_xname, "%s%d", name, unit);
sc->sc_ctrl |=CSR_READ(WMREG_CTRL);
/* Memory-mapped acccess is required for normal operation.*/
if ( pci_mem_find(PCI_MAPREG_START, &sc->sc_membase, &sc->sc_memsize))
rtems_panic("i82544EI: unable to map memory space\n");
#ifdef WM_DEBUG
printk("Memory base addr 0x%x\n", sc->sc_membase);
printk("txdesc[0] addr:0x%x, rxdesc[0] addr:0x%x, sizeof sc %d\n",&sc->sc_txdescs[0], &sc->sc_rxdescs[0], sizeof(*sc));
#endif
/* In addition, i82544 support I/O mapped indirect
* register access. It is not desirable (nor supported in
* this driver) to use it for normal operation, though it is
* required to work around bugs in some chip versions.
*/
#if 0
for (reg = PCI_MAPREG_START+4; reg < PCI_MAPREG_END; reg += 4) {
if (pci_io_find(reg, &sc->sc_iobase, &sc->sc_iosize)) break;
}
if (reg == PCI_MAPREG_END) {
printk("%s: ",sc->dv_xname);
rtems_panic("i82544EI: unable to map I/O space\n");
}
else {
sc->sc_flags |= WM_F_IOH_VALID;
printk("sc->sc_iobase 0x%x\n", sc->sc_iobase);
}
#endif
#if 0
/* Firmware takes care of it ? Or it is better to have it
* for the very cold boot case ?
* Get it out of power save mode, if needed.
*/
if (pci_get_capability(PCI_CAP_PWRMGMT, &pmreg, 0)) {
/* pmreg = 0xdc , 0xe0 returns 0x37002000 */
PCIx_read_config_dword(1,0,10,0,pmreg + PCI_PMCSR, &preg);
preg &= PCI_PMCSR_STATE_MASK;
if (preg == PCI_PMCSR_STATE_D3) {
/*
* The card has lost all configuration data in
* this state, so punt.
*/
printk("%s: unable to wake from power state D3\n",
sc->dv_xname);
return(0);
}
if (preg != PCI_PMCSR_STATE_D0) {
printk("%s: waking up from power state D%d\n",
sc->dv_xname, preg);
PCIx_write_config_byte(1,0,10,0,pmreg+PCI_PMCSR,PCI_PMCSR_STATE_D0);
}
}
#endif
/*
* Determine a few things about the bus we're connected to.
*/
reg = CSR_READ(WMREG_STATUS);
if (reg & STATUS_BUS64) sc->sc_flags |= WM_F_BUS64;
sc->sc_bus_speed = (reg & STATUS_PCI66) ? 66 : 33;
#ifdef WM_DEBUG
printk("%s%d: %d-bit %dMHz PCI bus\n",name, unit,
(sc->sc_flags & WM_F_BUS64) ? 64 : 32, sc->sc_bus_speed);
#endif
/*
* Reset the chip to a known state.
*
wm_reset(sc);*/
/*
* Setup some information about the EEPROM.
*/
sc->sc_ee_addrbits = 6;
printk("%s%d: %u word (%d address bits) MicroWire EEPROM\n",
name, unit, 1U << sc->sc_ee_addrbits,
sc->sc_ee_addrbits);
#if 1
/*
* Read the Ethernet address from the EEPROM.
*/
if (wm_read_eeprom(sc, EEPROM_OFF_MACADDR,
sizeof(myea) / sizeof(myea[0]), myea))
rtems_panic("i82544ei 1GHZ ethernet: unable to read Ethernet address");
enaddr[0] = myea[0] & 0xff;
enaddr[1] = myea[0] >> 8;
enaddr[2] = myea[1] & 0xff;
enaddr[3] = myea[1] >> 8;
enaddr[4] = myea[2] & 0xff;
enaddr[5] = myea[2] >> 8;
#else
ifwm_macaddr(enaddr);
#endif
memcpy(sc->arpcom.ac_enaddr, enaddr, ETHER_ADDR_LEN);
printk("%s: Ethernet address %s\n", sc->dv_xname,
ether_sprintf1(enaddr));
/*
* Read the config info from the EEPROM, and set up various
* bits in the control registers based on their contents.
*/
if (wm_read_eeprom(sc, EEPROM_OFF_CFG1, 1, &cfg1)) {
printk("%s: unable to read CFG1 from EEPROM\n",sc->dv_xname);
return(0);
}
if (wm_read_eeprom(sc, EEPROM_OFF_CFG2, 1, &cfg2)) {
printk("%s: unable to read CFG2 from EEPROM\n",sc->dv_xname);
return(0);
}
if (wm_read_eeprom(sc, EEPROM_OFF_SWDPIN, 1, &swdpin)) {
printk("%s: unable to read SWDPIN from EEPROM\n",sc->dv_xname);
return(0);
}
if (cfg1 & EEPROM_CFG1_ILOS) sc->sc_ctrl |= CTRL_ILOS;
sc->sc_ctrl|=((swdpin >> EEPROM_SWDPIN_SWDPIO_SHIFT) & 0xf) <<
CTRL_SWDPIO_SHIFT;
sc->sc_ctrl |= ((swdpin >> EEPROM_SWDPIN_SWDPIN_SHIFT) & 0xf) <<
CTRL_SWDPINS_SHIFT;
CSR_WRITE(WMREG_CTRL, sc->sc_ctrl);
#if 0
CSR_WRITE(WMREG_CTRL_EXT, sc->sc_ctrl_ext);
#endif
ifp = &sc->arpcom.ac_if;
/* set this interface's name and unit */
ifp->if_unit = unit;
ifp->if_name = name;
ifp->if_softc = sc;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
ifp->if_mtu = config->mtu ? config->mtu : ETHERMTU;
ifp->if_ioctl = wm_ioctl;
ifp->if_start = i82544EI_ifstart;
/* ifp->if_watchdog = wm_watchdog;*/
ifp->if_init = i82544EI_ifinit;
if (ifp->if_snd.ifq_maxlen == 0)
ifp->if_snd.ifq_maxlen = ifqmaxlen;
ifp->if_output = ether_output;
/* create the synchronization semaphore */
if (RTEMS_SUCCESSFUL != rtems_semaphore_create(
rtems_build_name('I','G','H','Z'),0,0,0,&sc->daemonSync))
rtems_panic("i82544EI: semaphore creation failed");
sc->next_module = root_i82544EI_dev;
root_i82544EI_dev = sc;
/* Attach the interface. */
if_attach(ifp);
ether_ifattach(ifp);
#ifdef WM_DEBUG
printk("82544EI: Ethernet driver has been attached (handle 0x%08x,ifp 0x%08x)\n",sc, ifp);
#endif
return(1);
}
/*
* i82544EI_ifstart: [ifnet interface function]
*
* Start packet transmission on the interface.
*/
static void
i82544EI_ifstart(struct ifnet *ifp)
{
struct wm_softc *sc = ifp->if_softc;
#ifdef WM_DEBUG
printk("i82544EI_ifstart(");
#endif
if ((ifp->if_flags & IFF_RUNNING) == 0) {
#ifdef WM_DEBUG
printk("IFF_RUNNING==0\n");
#endif
return;
}
ifp->if_flags |= IFF_OACTIVE;
rtems_event_send (sc->daemonTid, START_TRANSMIT_EVENT);
#ifdef WM_DEBUG
printk(")\n");
#endif
}
static void i82544EI_stats(struct wm_softc *sc)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
printf(" Rx Interrupts:%-8lu\n", sc->stats.rxInterrupts);
printf(" Receive Packets:%-8lu\n", CSR_READ(WMREG_GPRC));
printf(" Receive Overrun:%-8lu\n", sc->stats.rxOvrRunInterrupts);
printf(" Receive errors:%-8lu\n", CSR_READ(WMREG_RXERRC));
printf(" Crc Errors:%-8lu\n", CSR_READ(WMREG_CRCERRS));
printf(" Rx Length Errors:%-8lu\n", CSR_READ(WMREG_RLEC));
printf(" Tx Interrupts:%-8lu\n", sc->stats.txInterrupts);
printf("Multi-Buffer Packets:%-8lu\n", sc->stats.txMultiBuffPacket);
printf(" Transmitt Packets:%-8lu\n", CSR_READ(WMREG_GPTC));
printf(" Transmitt errors:%-8lu\n", ifp->if_oerrors);
printf(" collisions:%-8lu\n", CSR_READ(WMREG_COLC));
printf(" Active Txqs:%-8lu\n", sc->txq_nactive);
}
/*
* wm_ioctl: [ifnet interface function]
*
* Handle control requests from the operator.
*/
static int wm_ioctl(struct ifnet *ifp, u_long cmd,uint32_t data)
{
struct wm_softc *sc = ifp->if_softc;
int error;
switch (cmd) {
default:
error = ether_ioctl(ifp, cmd, data);
if (error == ENETRESET) {
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
wm_set_filter(sc);
error = 0;
}
break;
case SIO_RTEMS_SHOW_STATS:
i82544EI_stats(sc);
break;
}
/* Try to get more packets going.*/
i82544EI_ifstart(ifp);
return (error);
}
/*
* wm_isr:
*
* Interrupt service routine.
*/
static void i82544EI_isr()
{
volatile struct wm_softc *sc = root_i82544EI_dev;
uint32_t icr;
rtems_event_set events=0;
/* Reading the WMREG_ICR clears the interrupt bits */
icr = CSR_READ(WMREG_ICR);
if ( icr & (ICR_RXDMT0|ICR_RXT0)) {
sc->stats.rxInterrupts++;
events |= RX_EVENT;
}
if (icr & ICR_TXDW) {
sc->stats.txInterrupts++;
events |= TX_EVENT;
}
/* <SKF> Rx overrun : no available receive buffer
* or PCI receive bandwidth inadequate.
*/
if (icr & ICR_RXO) {
sc->stats.rxOvrRunInterrupts++;
events |= INIT_EVENT;
}
if ((!icr) || (icr & ICR_RXSEQ)) /* framing error */ {
sc->intr_errsts[sc->intr_err_ptr2++]=icr;
sc->intr_err_ptr2 %=INTR_ERR_SIZE;
events |= ERR_EVENT;
}
rtems_event_send(sc->daemonTid, events);
}
/*
* i82544EI_sendpacket:
*
* Helper; handle transmit interrupts.
*/
static void i82544EI_sendpacket(struct wm_softc *sc, struct mbuf *m)
{
#ifdef WM_DEBUG_TX
printk("sendpacket(");
#endif
if ( !(m->m_next)) { /* single buffer packet */
sc->txs_mbuf[sc->txq_next]= m;
/* Note: we currently only use 32-bit DMA addresses. */
sc->sc_txdescs[sc->txq_next].wtx_addr.wa_high = 0;
sc->sc_txdescs[sc->txq_next].wtx_addr.wa_low = htole32(mtod(m, void*));
sc->sc_txdescs[sc->txq_next].wtx_cmdlen =htole32(TxDescCmd | m->m_len);
sc->txs_lastdesc= sc->txq_next;
sc->txq_next = WM_NEXTTX(sc->txq_next);
sc->txq_nactive++;
sc->txq_free--;
}
else /* multiple mbufs in this packet */
{
struct mbuf *mtp, *mdest;
volatile unsigned char *pt;
int len, y;
#ifdef WM_DEBUG_TX
printk("multi mbufs ");
#endif
mtp = m;
while ( mtp) {
MGETHDR(mdest, M_WAIT, MT_DATA);
MCLGET(mdest, M_WAIT);
pt = (volatile unsigned char *)mdest->m_data;
for ( len=0;mtp;mtp=mtp->m_next) {
/* Each descriptor gets a 2k (MCLBYTES) buffer, although
* the length of each descriptor can be up to 16288 bytes.
* For packets which fill more than one buffer ( >2k), we
* chain them together.
* <Kate Feng> : This effective for packets > 2K
* The other way is effective for packets < 2K
*/
if ( ((y=(len+mtp->m_len)) > sizeof(union mcluster))) {
printk(">2048, use next descriptor\n");
break;
}
memcpy((void *)pt,(char *)mtp->m_data, mtp->m_len);
pt += mtp->m_len;
len += mtp->m_len;
} /* end for loop */
mdest->m_len=len;
sc->txs_mbuf[sc->txq_next] = mdest;
sc->stats.txMultiBuffPacket++;
/* Note: we currently only use 32-bit DMA addresses. */
sc->sc_txdescs[sc->txq_next].wtx_addr.wa_high = 0;
sc->sc_txdescs[sc->txq_next].wtx_addr.wa_low = htole32(mtod(mdest, void*));
sc->sc_txdescs[sc->txq_next].wtx_cmdlen = htole32(TxDescCmd|mdest->m_len);
sc->txs_lastdesc = sc->txq_next;
sc->txq_next = WM_NEXTTX(sc->txq_next);
sc->txq_nactive ++;
if (sc->txq_free)
sc->txq_free--;
else
rtems_panic("i8254EI : no more free descriptos");
} /* end for while */
/* free old mbuf chain */
m_freem(m);
} /* end multiple mbufs */
/*
* Set up the command byte on the last descriptor of
* the packet. If we're in the interrupt delay window,
* delay the interrupt.
*
sc->sc_txdescs[sc->txs_lastdesc].wtx_cmdlen|= htole32(WTX_CMD_EOP|WTX_CMD_IFCS); */
DPRINTF(WM_DEBUG_TX,("%s: TX: desc %d: cmdlen 0x%08x\n", sc->dv_xname,
sc->txs_lastdesc, le32toh(sc->sc_txdescs[sc->txs_lastdesc].wtx_cmdlen)));
DPRINTF(WM_DEBUG_TX,("status 0x%08x\n",sc->sc_txdescs[sc->txq_fi].wtx_fields.wtxu_status));
/* This is the location where software writes the first NEW descriptor */
CSR_WRITE(WMREG_TDT, sc->txq_next);
DPRINTF(WM_DEBUG_TX,("%s: addr 0x%08x, TX: TDH %d, TDT %d\n",sc->dv_xname,
le32toh(sc->sc_txdescs[sc->txs_lastdesc].wtx_addr.wa_low), CSR_READ(WMREG_TDH),
CSR_READ(WMREG_TDT)));
DPRINTF(WM_DEBUG_TX,("%s: TX: finished transmitting packet, job %d\n",
sc->dv_xname, sc->txq_next));
}
static void i82544EI_txq_free(struct wm_softc *sc, uint8_t status)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
/* We might use the statistics registers instead of variables
* to keep tack of the network statistics
*/
/* statistics */
ifp->if_opackets++;
if (status & (WTX_ST_EC|WTX_ST_LC)) {
ifp->if_oerrors++;
if (status & WTX_ST_LC)
printf("%s: late collision\n", sc->dv_xname);
else if (status & WTX_ST_EC) {
ifp->if_collisions += 16;
printf("%s: excessive collisions\n", sc->dv_xname);
}
}
/* Free the original mbuf chain */
m_freem(sc->txs_mbuf[sc->txq_fi]);
sc->txs_mbuf[sc->txq_fi] = 0;
sc->sc_txdescs[sc->txq_fi].wtx_fields.wtxu_status=0;
sc->txq_free ++;
sc->txq_fi = WM_NEXTTX(sc->txq_fi);
--sc->txq_nactive;
}
static void i82544EI_txq_done(struct wm_softc *sc)
{
uint8_t status;
/*
* Go through the Tx list and free mbufs for those
* frames which have been transmitted.
*/
while ( sc->txq_nactive > 0) {
status = sc->sc_txdescs[sc->txq_fi].wtx_fields.wtxu_status;
if ((status & WTX_ST_DD) == 0) break;
i82544EI_txq_free(sc, status);
DPRINTF(WM_DEBUG_TX,("%s: TX: job %d done\n",
sc->dv_xname, sc->txq_fi));
}
}
static void wm_init_rxdesc(struct wm_softc *sc, int x)
{
wiseman_rxdesc_t *__rxd = &(sc)->sc_rxdescs[(x)];
struct mbuf *m;
m = sc->rxs_mbuf[x];
/*
* Note: We scoot the packet forward 2 bytes in the buffer
* so that the payload after the Ethernet header is aligned
* to a 4-byte boundary.
*
* XXX BRAINDAMAGE ALERT!
* The stupid chip uses the same size for every buffer, which
* is set in the Receive Control register. We are using the 2K
* size option, but what we REALLY want is (2K - 2)! For this
* reason, we can't "scoot" packets longer than the standard
* Ethernet MTU. On strict-alignment platforms, if the total
* size exceeds (2K - 2) we set align_tweak to 0 and let
* the upper layer copy the headers.
*/
/* m->m_data = m->m_ext.ext_buf + (sc)->sc_align_tweak;
m->m_data = (sc)->sc_align_tweak; */
__rxd->wrx_addr.wa_low=htole32(mtod(m, void*)+(sc)->sc_align_tweak);
__rxd->wrx_addr.wa_high = 0;
__rxd->wrx_len = 0;
__rxd->wrx_cksum = 0;
__rxd->wrx_status = 0;
__rxd->wrx_errors = 0;
__rxd->wrx_special = 0;
/* Receive Descriptor Tail: add Rx desc. to H/W free list */
CSR_WRITE(WMREG_RDT, (x));
}
static void i82544EI_rx(struct wm_softc *sc)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
struct mbuf *m;
int i, len;
uint8_t status, errors;
struct ether_header *eh;
#ifdef WM_DEBUG
printk("i82544EI_rx()\n");
#endif
for (i = sc->sc_rxptr;; i = WM_NEXTRX(i)) {
DPRINTF(WM_DEBUG_RX, ("%s: RX: checking descriptor %d\n",
sc->dv_xname, i));
status = sc->sc_rxdescs[i].wrx_status;
errors = sc->sc_rxdescs[i].wrx_errors;
len = le16toh(sc->sc_rxdescs[i].wrx_len);
m = sc->rxs_mbuf[i];
if ((status & WRX_ST_DD) == 0) break; /* descriptor not done */
if (sc->sc_rxdiscard) {
DPRINTF(WM_DEBUG_RX,("%s: RX: discarding contents of descriptor %d\n",
sc->dv_xname, i));
wm_init_rxdesc(sc, i);
if (status & WRX_ST_EOP) {
/* Reset our state. */
DPRINTF(WM_DEBUG_RX,("%s: RX: resetting rxdiscard -> 0\n",
sc->dv_xname));
sc->sc_rxdiscard = 0;
}
continue;
}
WM_RXCHAIN_LINK(sc, m);
m->m_len = len;
DPRINTF(WM_DEBUG_RX,("%s: RX: buffer at %p len %d\n",
sc->dv_xname, m->m_data, len));
/*
* If this is not the end of the packet, keep
* looking.
*/
if ((status & WRX_ST_EOP) == 0) {
sc->sc_rxlen += len;
DPRINTF(WM_DEBUG_RX,("%s: RX: not yet EOP, rxlen -> %d\n",
sc->dv_xname, sc->sc_rxlen));
continue;
}
/*
* Okay, we have the entire packet now...
*/
*sc->sc_rxtailp = NULL;
m = sc->sc_rxhead;
len += sc->sc_rxlen;
WM_RXCHAIN_RESET(sc);
DPRINTF(WM_DEBUG_RX,("%s: RX: have entire packet, len -> %d\n",
sc->dv_xname, len));
/*
* If an error occurred, update stats and drop the packet.
*/
if (errors &(WRX_ER_CE|WRX_ER_SE|WRX_ER_SEQ|WRX_ER_CXE|WRX_ER_RXE)) {
ifp->if_ierrors++;
if (errors & WRX_ER_SE)
printk("%s: symbol error\n",sc->dv_xname);
else if (errors & WRX_ER_SEQ)
printk("%s: receive sequence error\n",sc->dv_xname);
else if (errors & WRX_ER_CE)
printk("%s: CRC error\n",sc->dv_xname);
m_freem(m);
goto give_it_back;
}
/*
* No errors. Receive the packet.
*
* Note, we have configured the chip to include the
* CRC with every packet.
*/
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = len;
eh = mtod (m, struct ether_header *);
m->m_data += sizeof(struct ether_header);
ether_input (ifp, eh, m);
/* Pass it on. */
ifp->if_ipackets++;
give_it_back:
/* Add a new receive buffer to the ring.*/
if (wm_add_rxbuf(sc, i) != 0) {
/*
* Failed, throw away what we've done so
* far, and discard the rest of the packet.
*/
ifp->if_ierrors++;
wm_init_rxdesc(sc, i);
if ((status & WRX_ST_EOP) == 0)
sc->sc_rxdiscard = 1;
if (sc->sc_rxhead != NULL)
m_freem(sc->sc_rxhead);
WM_RXCHAIN_RESET(sc);
DPRINTF(WM_DEBUG_RX,("%s: RX: Rx buffer allocation failed, "
"dropping packet%s\n", sc->dv_xname,
sc->sc_rxdiscard ? " (discard)" : ""));
continue;
}
} /* end for */
/* Update the receive pointer. */
sc->sc_rxptr = i;
DPRINTF(WM_DEBUG_RX, ("%s: RX: rxptr -> %d\n", sc->dv_xname, i));
}
static int i82544EI_init_hw(struct wm_softc *sc)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
int i,error;
uint8_t cksumfields;
/*
* HDR_ALIGNED_P is constant 1 if __NO_STRICT_ALIGMENT is set.
* There is a small but measurable benefit to avoiding the adjusment
* of the descriptor so that the headers are aligned, for normal mtu,
* on such platforms. One possibility is that the DMA itself is
* slightly more efficient if the front of the entire packet (instead
* of the front of the headers) is aligned.
*
* Note we must always set align_tweak to 0 if we are using
* jumbo frames.
*/
#if 1
sc->sc_align_tweak = 0;
#else
#ifdef __NO_STRICT_ALIGNMENT
sc->sc_align_tweak = 0;
#else
if ((ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN) > (MCLBYTES - 2))
sc->sc_align_tweak = 0;
else
sc->sc_align_tweak = 2;
#endif /* __NO_STRICT_ALIGNMENT */
#endif
#ifdef WM_DEBUG
printk("ifp->if_mtu= %d, sc->sc_align_tweak= %d \n",
ifp->if_mtu, sc->sc_align_tweak);
#endif
/* Cancel any pending I/O. */
wm_stop(ifp, 0);
/* Reset the chip to a known state.
wm_reset(sc);*/
/* Initialize the error buffer ring */
sc->intr_err_ptr1=0;
sc->intr_err_ptr2=0;
for (i=0; i< INTR_ERR_SIZE; i++) sc->intr_errsts[i]=0;
/* Initialize the transmit descriptor ring. */
memset(sc->sc_txdescs, 0, sizeof(sc->sc_txdescs));
sc->txq_free = NTXDESC;
sc->txq_next = 0;
sc->txs_lastdesc = 0;
sc->txq_next = 0;
sc->txq_free = NTXDESC;
sc->txq_nactive = 0;
sc->sc_txctx_ipcs = 0xffffffff;
sc->sc_txctx_tucs = 0xffffffff;
CSR_WRITE(WMREG_TBDAH, 0);
CSR_WRITE(WMREG_TBDAL, WM_CDTXADDR(sc));
#ifdef WM_DEBUG
printk("TBDAL 0x%x, TDLEN %d\n", WM_CDTXADDR(sc), sizeof(sc->sc_txdescs));
#endif
CSR_WRITE(WMREG_TDLEN, sizeof(sc->sc_txdescs));
CSR_WRITE(WMREG_TDH, 0);
CSR_WRITE(WMREG_TDT, 0);
CSR_WRITE(WMREG_TIDV, 100 );
CSR_WRITE(WMREG_TXDCTL, TXDCTL_PTHRESH(0) |
TXDCTL_HTHRESH(0) | TXDCTL_WTHRESH(0));
CSR_WRITE(WMREG_RXDCTL, RXDCTL_PTHRESH(0) |
RXDCTL_HTHRESH(0) | RXDCTL_WTHRESH(1) | RXDCTL_GRAN );
CSR_WRITE(WMREG_TQSA_LO, 0);
CSR_WRITE(WMREG_TQSA_HI, 0);
/*
* Set up checksum offload parameters for
* this packet.
*/
#ifdef CKSUM_OFFLOAD
if (m0->m_pkthdr.csum_flags &
(M_CSUM_IPv4|M_CSUM_TCPv4|M_CSUM_UDPv4)) {
if (wm_tx_cksum(sc, txs, &TxDescCmd,&cksumfields) != 0) {
/* Error message already displayed. */
continue;
}
} else {
#endif
TxDescCmd = 0;
cksumfields = 0;
#ifdef CKSUM_OFFLOAD
}
#endif
/* TxDescCmd |= WTX_CMD_IDE | WTX_CMD_RS; TODO ? interrupt delay enable */
TxDescCmd |= WTX_CMD_EOP|WTX_CMD_IFCS|WTX_CMD_RS;
/* Initialize the transmit job descriptors. */
for (i = 0; i < NTXDESC; i++) {
sc->txs_mbuf[i] = 0;
sc->sc_txdescs[i].wtx_fields.wtxu_options=cksumfields;
sc->sc_txdescs[i].wtx_addr.wa_high = 0;
sc->sc_txdescs[i].wtx_addr.wa_low = 0;
sc->sc_txdescs[i].wtx_cmdlen = htole32(TxDescCmd);
}
/*
* Initialize the receive descriptor and receive job
* descriptor rings.
*/
memset(sc->sc_rxdescs, 0, sizeof(sc->sc_rxdescs));
CSR_WRITE(WMREG_RDBAH, 0);
CSR_WRITE(WMREG_RDBAL, WM_CDRXADDR(sc));
CSR_WRITE(WMREG_RDLEN, sizeof(sc->sc_rxdescs));
CSR_WRITE(WMREG_RDH, 0);
CSR_WRITE(WMREG_RDT, 0);
for (i = 0; i < NRXDESC; i++) {
if (sc->rxs_mbuf[i] == NULL) {
if ((error = wm_add_rxbuf(sc, i)) != 0) {
printk("%s%d: unable to allocate or map rx buffer"
"%d, error = %d\n",ifp->if_name,ifp->if_unit, i, error);
/*
* XXX Should attempt to run with fewer receive
* XXX buffers instead of just failing.
*/
wm_rxdrain(sc);
return(error);
}
} else {
printk("sc->rxs_mbuf[%d] not NULL.\n", i);
wm_init_rxdesc(sc, i);
}
}
sc->sc_rxptr = 0;
sc->sc_rxdiscard = 0;
WM_RXCHAIN_RESET(sc);
/*
* Clear out the VLAN table -- we don't use it (yet).
*/
CSR_WRITE(WMREG_VET, 0);
for (i = 0; i < WM_VLAN_TABSIZE; i++)
CSR_WRITE(WMREG_VFTA + (i << 2), 0);
/*
* Set up flow-control parameters.
*
* XXX Values could probably stand some tuning.
*/
CSR_WRITE(WMREG_FCAL, FCAL_CONST);/*safe,even though MOTLOAD 0x00c28001 */
CSR_WRITE(WMREG_FCAH, FCAH_CONST);/*safe,even though MOTLOAD 0x00000100 */
CSR_WRITE(WMREG_FCT, ETHERTYPE_FLOWCONTROL);/*safe,even though MOTLoad 0x8808 */
/* safe,even though MOTLoad default all 0 */
sc->sc_fcrtl = FCRTL_DFLT;
CSR_WRITE(WMREG_FCRTH, FCRTH_DFLT);
CSR_WRITE(WMREG_FCRTL, sc->sc_fcrtl);
CSR_WRITE(WMREG_FCTTV, FCTTV_DFLT);
sc->sc_ctrl &= ~CTRL_VME;
/*sc->sc_ctrl |= CTRL_TFCE | CTRL_RFCE;*/
/* enable Big Endian Mode for the powerPC
sc->sc_ctrl |= CTRL_BEM;*/
/* Write the control registers. */
CSR_WRITE(WMREG_CTRL, sc->sc_ctrl);
#if 0
CSR_WRITE(WMREG_CTRL_EXT, sc->sc_ctrl_ext);
#endif
/* MOTLoad : WMREG_RXCSUM (0x5000)= 0, no Rx checksum offloading */
/*
* Set up the interrupt registers.
*/
CSR_WRITE(WMREG_IMC, 0xffffffffU);
sc->sc_icr = ICR_TXDW | ICR_RXSEQ | ICR_RXDMT0 | ICR_RXO | ICR_RXT0;
CSR_WRITE(WMREG_IMS, sc->sc_icr);
/* Set up the inter-packet gap. */
CSR_WRITE(WMREG_TIPG, sc->sc_tipg);
#if 0 /* XXXJRT */
/* Set the VLAN ethernetype. */
CSR_WRITE(WMREG_VET, ETHERTYPE_VLAN);
#endif
/*
* Set up the transmit control register; we start out with
* a collision distance suitable for FDX, but update it whe
* we resolve the media type.
*/
sc->sc_tctl = TCTL_EN | TCTL_PSP | TCTL_CT(TX_COLLISION_THRESHOLD) |
TCTL_COLD(TX_COLLISION_DISTANCE_FDX); /*transmitter enable*/
/*
* Set up the receive control register; we actually program
* the register when we set the receive filter. Use multicast
* address offset type 0.
*
* Only the i82544 has the ability to strip the incoming
* CRC, so we don't enable that feature.
*/
sc->sc_mchash_type = 0;
sc->sc_rctl = RCTL_EN | RCTL_LBM_NONE | RCTL_RDMTS_1_2 | RCTL_LPE |
RCTL_DPF | RCTL_MO(sc->sc_mchash_type);
/* (MCLBYTES == 2048) */
sc->sc_rctl |= RCTL_2k;
#ifdef WM_DEBUG
printk("RDBAL 0x%x,RDLEN %d, RDT %d\n",CSR_READ(WMREG_RDBAL),CSR_READ(WMREG_RDLEN), CSR_READ(WMREG_RDT));
#endif
CSR_WRITE(WMREG_RDTR, 28 |RDTR_FPD);
/* Set the receive filter. */
wm_set_filter(sc);
CSR_WRITE(WMREG_TCTL, sc->sc_tctl);
/* Map and establish our interrupt. */
if (!BSP_install_rtems_irq_handler(&i82544IrqData))
rtems_panic("1GHZ ethernet: unable to install ISR");
return(0);
}
/*
* wm_reset: Reset the i82544 chip.
*
* Device reset will globally reset the entire controller with the
* exception of the PCI configuration registers.
* If this is used, one has to make sure all the registers of the
* controller are set to the right value.
*
*/
static void wm_reset(struct wm_softc *sc)
{
int i;
/*
* Some chips have a problem with the memory-mapped
* write cycle when issuing the reset, so use I/O-mapped
* access, if possible.
*/
sc->sc_ctrl |=CSR_READ(WMREG_CTRL);
/* Device reset will globally reset the entire controller with the
* exception of the PCI configuration registers.
*
*/
CSR_WRITE(WMREG_CTRL, CTRL_RST);
rtems_bsp_delay(10000);
for (i = 0; i < 1000; i++) {
if ((CSR_READ(WMREG_CTRL) & CTRL_RST) == 0) break;
rtems_bsp_delay(20);
}
if ( CSR_READ(WMREG_CTRL) & CTRL_RST) {
printk("%s:",sc->dv_xname);
rtems_panic("reset failed to complete\n");
}
else {
CSR_WRITE(WMREG_CTRL,sc->sc_ctrl);
printk("After wm_reset(): WMREG_CTRL 0x%x\n", CSR_READ(WMREG_CTRL));
}
/* For internal PHY operation, set WMREG_TXCW to be 0x0, which is
* the MotLoad defalt anyway
*
*/
rtems_bsp_delay(10000);
}
/*
* i82544EI_ifinit: [ifnet interface function]
*
* Initialize the interface.
*/
static void i82544EI_ifinit(void *arg)
{
struct wm_softc *sc = (struct wm_softc*)arg;
#ifdef WM_DEBUG
printk("i82544EI_ifinit(): daemon ID: 0x%08x)\n", sc->daemonTid);
#endif
if (sc->daemonTid) {
#ifdef WM_DEBUG
printk("i82544EI: daemon already up, doing nothing\n");
#endif
return;
}
i82544EI_init_hw(sc);
sc->daemonTid = rtems_bsdnet_newproc(i82544EI_TASK_NAME,4096,i82544EI_daemon,arg);
/* ...all done! */
sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
#ifdef WM_DEBUG
printk(")");
#endif
}
/*
* wm_txdrain:
*
* Drain the transmit queue.
*/
static void wm_txdrain(struct wm_softc *sc)
{
int i;
/* Release any queued transmit buffers. */
for (i = 0; i < NTXDESC; i++) {
if (sc->txs_mbuf[i] != NULL) {
m_freem(sc->txs_mbuf[i]);
sc->txs_mbuf[i] = NULL;
}
}
}
/*
* wm_rxdrain:
*
* Drain the receive queue.
*/
static void wm_rxdrain(struct wm_softc *sc)
{
int i;
for (i = 0; i < NRXDESC; i++) {
if (sc->rxs_mbuf[i] != NULL) {
m_freem(sc->rxs_mbuf[i]);
sc->rxs_mbuf[i] = NULL;
}
}
}
static void i82544EI_tx_stop(struct wm_softc *sc)
{
wm_txdrain(sc);
}
static void i82544EI_rx_stop(struct wm_softc *sc)
{
wm_rxdrain(sc);
}
static void i82544EI_stop_hw(struct wm_softc *sc)
{
printk("i82544EI_stop_hw(");
/* remove our interrupt handler which will also
* disable interrupts at the MPIC and the device
* itself
*/
if (!BSP_remove_rtems_irq_handler(&i82544IrqData))
rtems_panic("Yellowfin: unable to remove IRQ handler!");
CSR_WRITE(WMREG_IMS, 0);
sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
i82544EI_tx_stop(sc);
i82544EI_rx_stop(sc);
printk(")");
}
/*
* wm_stop: [ifnet interface function]
*
* Stop transmission on the interface.
*/
static void wm_stop(struct ifnet *ifp, int disable)
{
struct wm_softc *sc = ifp->if_softc;
#ifdef WM_DEBUG
printk("wm_stop(");
#endif
/* Stop the transmit and receive processes. */
CSR_WRITE(WMREG_TCTL, 0);
CSR_WRITE(WMREG_RCTL, 0);
wm_txdrain(sc);
wm_rxdrain(sc);
/* Mark the interface as down */
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
#ifdef WM_DEBUG
printk(")\n");
#endif
}
/*
* wm_eeprom_sendbits:
*
* Send a series of bits to the EEPROM.
*/
static void wm_eeprom_sendbits(struct wm_softc *sc, uint32_t bits, int nbits)
{
uint32_t reg;
int x;
reg = CSR_READ(WMREG_EECD);
for (x = nbits; x > 0; x--) {
if (bits & (1U << (x - 1)))
reg |= EECD_DI;
else
reg &= ~EECD_DI;
CSR_WRITE(WMREG_EECD, reg);
rtems_bsp_delay(2);
CSR_WRITE(WMREG_EECD, reg | EECD_SK);
rtems_bsp_delay(2);
CSR_WRITE(WMREG_EECD, reg);
rtems_bsp_delay(2);
}
}
/*
* wm_eeprom_recvbits:
*
* Receive a series of bits from the EEPROM.
*/
static void wm_eeprom_recvbits(struct wm_softc *sc, uint32_t *valp, int nbits)
{
uint32_t reg, val;
int x;
reg = CSR_READ(WMREG_EECD) & ~EECD_DI;
val = 0;
for (x = nbits; x > 0; x--) {
CSR_WRITE(WMREG_EECD, reg | EECD_SK);
rtems_bsp_delay(2);
if (CSR_READ(WMREG_EECD) & EECD_DO)
val |= (1U << (x - 1));
CSR_WRITE(WMREG_EECD, reg);
rtems_bsp_delay(2);
}
*valp = val;
}
/*
* wm_read_eeprom_uwire:
*
* Read a word from the EEPROM using the MicroWire protocol.
*
* (The 82544EI Gigabit Ethernet Controller is compatible with
* most MicroWire interface, serial EEPROM devices.)
*/
static int wm_read_eeprom_uwire(struct wm_softc *sc, int word, int wordcnt, uint16_t *data)
{
uint32_t reg, val;
int i;
for (i = 0; i < wordcnt; i++) {
/* Clear SK and DI. */
reg = CSR_READ(WMREG_EECD) & ~(EECD_SK | EECD_DI);
CSR_WRITE(WMREG_EECD, reg);
/* Set CHIP SELECT. */
reg |= EECD_CS;
CSR_WRITE(WMREG_EECD, reg);
rtems_bsp_delay(2);
/* Shift in the READ command. */
wm_eeprom_sendbits(sc, UWIRE_OPC_READ, 3);
/* Shift in address. */
wm_eeprom_sendbits(sc, word + i, sc->sc_ee_addrbits);
/* Shift out the data. */
wm_eeprom_recvbits(sc, &val, 16);
data[i] = val & 0xffff;
/* Clear CHIP SELECT. */
reg = CSR_READ(WMREG_EECD) & ~EECD_CS;
CSR_WRITE(WMREG_EECD, reg);
rtems_bsp_delay(2);
}
return (0);
}
/*
* wm_acquire_eeprom:
*
* Perform the EEPROM handshake required on some chips.
*/
static int wm_acquire_eeprom(struct wm_softc *sc)
{
uint32_t reg;
int x;
reg = CSR_READ(WMREG_EECD);
/* Request EEPROM access. */
reg |= EECD_EE_REQ;
CSR_WRITE(WMREG_EECD, reg);
/* ..and wait for it to be granted. */
for (x = 0; x < 100; x++) {
reg = CSR_READ(WMREG_EECD);
if (reg & EECD_EE_GNT) break;
rtems_bsp_delay(500);
}
if ((reg & EECD_EE_GNT) == 0) {
printk("Could not acquire EEPROM GNT x= %d\n", x);
reg &= ~EECD_EE_REQ;
CSR_WRITE(WMREG_EECD, reg);
return (1);
}
return (0);
}
/*
* wm_read_eeprom:
*
* Read data from the serial EEPROM.
* 82544EI does not Perform the EEPROM handshake
*/
static int wm_read_eeprom(struct wm_softc *sc, int word, int wordcnt, uint16_t *data)
{
#if 0
/* base on the datasheet, this does not seem to be applicable */
if (wm_acquire_eeprom(sc))
return(1);
#endif
return(wm_read_eeprom_uwire(sc, word, wordcnt, data));
}
/*
* wm_add_rxbuf:
*
* Add a receive buffer to the indiciated descriptor.
*/
static int wm_add_rxbuf(struct wm_softc *sc, int idx)
{
struct mbuf *m;
MGETHDR(m, M_WAIT, MT_DATA);
if (m == NULL) return (ENOBUFS);
MCLGET(m, M_WAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
return (ENOBUFS);
}
m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
sc->rxs_mbuf[idx] = m;
/* m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;*/
wm_init_rxdesc(sc, idx);
#if 0
printk("sc->rxs_mbuf[%d]= 0x%x, mbuf @ 0x%x\n",
idx, sc->rxs_mbuf[idx], le32toh(sc->sc_rxdescs[idx].wrx_addr.wa_low));
#endif
return(0);
}
/*
* wm_set_ral:
*
* Set an entery in the receive address list.
*/
static void
wm_set_ral(struct wm_softc *sc, const uint8_t *enaddr, int idx)
{
uint32_t ral_lo, ral_hi;
if (enaddr != NULL) {
ral_lo = enaddr[0]|(enaddr[1] << 8)|(enaddr[2] << 16)|(enaddr[3] << 24);
ral_hi = enaddr[4] | (enaddr[5] << 8);
ral_hi |= RAL_AV;
} else {
ral_lo = 0;
ral_hi = 0;
}
CSR_WRITE(WMREG_RAL_LO(WMREG_CORDOVA_RAL_BASE, idx),ral_lo);
CSR_WRITE(WMREG_RAL_HI(WMREG_CORDOVA_RAL_BASE, idx),ral_hi);
}
/*
* wm_mchash:
*
* Compute the hash of the multicast address for the 4096-bit
* multicast filter.
*/
static uint32_t
wm_mchash(struct wm_softc *sc, const uint8_t *enaddr)
{
static const int lo_shift[4] = { 4, 3, 2, 0 };
static const int hi_shift[4] = { 4, 5, 6, 8 };
uint32_t hash;
hash = (enaddr[4] >> lo_shift[sc->sc_mchash_type]) |
(((uint16_t) enaddr[5]) << hi_shift[sc->sc_mchash_type]);
return (hash & 0xfff);
}
/*
* wm_set_filter: Set up the receive filter.
*/
static void wm_set_filter(struct wm_softc *sc)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
struct ether_multi *enm;
struct ether_multistep step;
uint32_t mta_reg;
uint32_t hash, reg, bit;
int i;
#ifdef WM_DEBUG
printk("wm_set_filter(");
#endif
mta_reg = WMREG_CORDOVA_MTA;
sc->sc_rctl &= ~(RCTL_BAM | RCTL_UPE | RCTL_MPE);
/* if (ifp->if_flags & IFF_BROADCAST)*/
sc->sc_rctl |= RCTL_BAM;
if (ifp->if_flags & IFF_PROMISC) {
sc->sc_rctl |= RCTL_UPE;
goto allmulti;
}
/*
* Set the station address in the first RAL slot, and
* clear the remaining slots.
*/
wm_set_ral(sc, sc->arpcom.ac_enaddr, 0);
for (i = 1; i < WM_RAL_TABSIZE; i++)
wm_set_ral(sc, NULL, i);
/* Clear out the multicast table. */
for (i = 0; i < WM_MC_TABSIZE; i++)
CSR_WRITE(mta_reg + (i << 2), 0);
ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
while (enm != NULL) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
/*
* We must listen to a range of multicast addresses.
* For now, just accept all multicasts, rather than
* trying to set only those filter bits needed to match
* the range. (At this time, the only use of address
* ranges is for IP multicast routing, for which the
* range is big enough to require all bits set.)
*/
goto allmulti;
}
hash = wm_mchash(sc, enm->enm_addrlo);
reg = (hash >> 5) & 0x7f;
bit = hash & 0x1f;
hash = CSR_READ(mta_reg + (reg << 2));
hash |= 1U << bit;
/* XXX Hardware bug?? */
if ((reg & 0xe) == 1) {
bit = CSR_READ(mta_reg + ((reg - 1) << 2));
CSR_WRITE(mta_reg + (reg << 2), hash);
CSR_WRITE(mta_reg + ((reg - 1) << 2), bit);
} else
CSR_WRITE(mta_reg + (reg << 2), hash);
ETHER_NEXT_MULTI(step, enm);
}
ifp->if_flags &= ~IFF_ALLMULTI;
goto setit;
allmulti:
ifp->if_flags |= IFF_ALLMULTI;
sc->sc_rctl |= RCTL_MPE;
setit:
CSR_WRITE(WMREG_RCTL, sc->sc_rctl);
#ifdef WM_DEBUG
printk("RCTL 0x%x)\n", CSR_READ(WMREG_RCTL));
#endif
}
static void i82544EI_error(struct wm_softc *sc)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
unsigned long intr_status= sc->intr_errsts[sc->intr_err_ptr1++];
/* read and reset the status; because this is written
* by the ISR, we must disable interrupts here
*/
if (intr_status) {
printk("Error %s%d:", ifp->if_name, ifp->if_unit);
if (intr_status & ICR_RXSEQ) {
printk("Rxq framing error (ICR= %x), if_ierrors %d\n",
intr_status, ifp->if_ierrors);
}
}
else
printk("%s%d: Ghost interrupt ?\n",ifp->if_name,ifp->if_unit);
}
void i82544EI_printStats()
{
i82544EI_stats(root_i82544EI_dev);
}
/* The daemon does all of the work; RX, TX and cleaning up buffers/descriptors */
static void i82544EI_daemon(void *arg)
{
struct wm_softc *sc = (struct wm_softc*)arg;
rtems_event_set events;
struct mbuf *m=0;
struct ifnet *ifp=&sc->arpcom.ac_if;
int i;
#ifdef WM_DEBUG
printk("i82544EI_daemon()\n");
#endif
/* NOTE: our creator possibly holds the bsdnet_semaphore.
* since that has PRIORITY_INVERSION enabled, our
* subsequent call to bsdnet_event_receive() will
* _not_ release it. It's still in posession of our
* owner.
* This is different from how killing this task
* is handled.
*/
for (;;) {
/* sleep until there's work to be done */
/* Note: bsdnet_event_receive() acquires
* the global bsdnet semaphore for
* mutual exclusion.
*/
rtems_bsdnet_event_receive(ALL_EVENTS,
RTEMS_WAIT | RTEMS_EVENT_ANY,
RTEMS_NO_TIMEOUT,
&events);
if (KILL_EVENT & events) break;
if (events & RX_EVENT) i82544EI_rx(sc);
/* clean up and try sending packets */
do {
#if 1
if (gpp_int_error!=0) {
printk("GPP interrupt error %d\n", gpp_int_error);
gpp_int_error=0;
}
#endif
i82544EI_txq_done(sc);
while (sc->txq_free>0) {
if (sc->txq_free>TXQ_HiLmt_OFF) {
m=0;
IF_DEQUEUE(&ifp->if_snd,m);
if (m==0) break;
i82544EI_sendpacket(sc, m);
}
else {
i82544EI_txq_done(sc);
break;
}
}
/* we leave this loop
* - either because there's no free buffer
* (m=0 initializer && !sc->txq_free)
* - or there's nothing to send (IF_DEQUEUE
* returned 0
*/
} while (m);
ifp->if_flags &= ~IFF_OACTIVE;
/* Log errors and other uncommon events. */
if (events & ERR_EVENT) i82544EI_error(sc);
/* Rx overrun */
if ( events & INIT_EVENT) {
printk("Warnning, Rx overrun. Make sure the old mbuf was free\n");
i82544EI_ifinit(arg);
}
} /* end for(;;) { rtems_bsdnet_event_receive() .....*/
ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
/* shut down the hardware */
i82544EI_stop_hw(sc);
/* flush the output queue */
for (;;) {
IF_DEQUEUE(&ifp->if_snd,m);
if (!m) break;
m_freem(m);
}
/* as of 'rtems_bsdnet_event_receive()' we own the
* networking semaphore
*/
rtems_bsdnet_semaphore_release();
rtems_semaphore_release(sc->daemonSync);
/* Note that I dont use sc->daemonTid here -
* theoretically, that variable could already
* hold a newly created TID
*/
rtems_task_delete(RTEMS_SELF);
}
More information about the users
mailing list