From 1a3d1f3e80f14a156a432a835983ce46c039e24a Mon Sep 17 00:00:00 2001 From: Jay Monkman Date: Thu, 15 Jul 2004 06:12:05 +0000 Subject: 2004-07-15 Jay Monkman * ChangeLog, Makefile.am, README, bsp_specs, configure.ac, times, console/uart.c, include/bsp.h, include/tm27.h, network/lan91c11x.c, network/lan91c11x.h, network/network.c, start/start.S, startup/bspstart.c, startup/exit.c, startup/linkcmds, startup/memmap.c: New files. --- c/src/lib/libbsp/arm/csb336/network/lan91c11x.c | 267 +++++++++ c/src/lib/libbsp/arm/csb336/network/lan91c11x.h | 216 +++++++ c/src/lib/libbsp/arm/csb336/network/network.c | 724 ++++++++++++++++++++++++ 3 files changed, 1207 insertions(+) create mode 100644 c/src/lib/libbsp/arm/csb336/network/lan91c11x.c create mode 100644 c/src/lib/libbsp/arm/csb336/network/lan91c11x.h create mode 100644 c/src/lib/libbsp/arm/csb336/network/network.c (limited to 'c/src/lib/libbsp/arm/csb336/network') diff --git a/c/src/lib/libbsp/arm/csb336/network/lan91c11x.c b/c/src/lib/libbsp/arm/csb336/network/lan91c11x.c new file mode 100644 index 0000000000..961746a636 --- /dev/null +++ b/c/src/lib/libbsp/arm/csb336/network/lan91c11x.c @@ -0,0 +1,267 @@ +/* + * Helper functions for SMSC LAN91C11x + * + * Copyright (c) 2004 by Cogent Computer Systems + * Written by Jay Monkman + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + */ +#include +#include "lan91c11x.h" + +static rtems_interrupt_level level; + +void lan91c11x_lock(void) +{ + _CPU_ISR_Disable(level); +} + +void lan91c11x_unlock(void) +{ + _CPU_ISR_Enable(level); +} + +uint16_t lan91c11x_read_reg(int reg) +{ + volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR; + uint16_t old_bank; + uint16_t val; + rtems_interrupt_level level; + + _CPU_ISR_Disable(level); + + /* save the bank register */ + old_bank = ptr[7] & 0x7; + + /* set the bank register */ + ptr[7] = (reg >> 4) & 0x7; + + val = ptr[((reg & 0xf) >> 1)]; + + /* restore the bank register */ + ptr[7] = old_bank; + + _CPU_ISR_Enable(level); + return val; +} + +void lan91c11x_write_reg(int reg, uint16_t value) +{ + volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR; + uint16_t old_bank; + rtems_interrupt_level level; + + _CPU_ISR_Disable(level); + + /* save the bank register */ + old_bank = ptr[7] & 0x7; + + /* set the bank register */ + ptr[7] = (reg >> 4) & 0x7; + + ptr[((reg & 0xf) >> 1)] = value; + + /* restore the bank register */ + ptr[7] = old_bank; + + _CPU_ISR_Enable(level); +} + +uint16_t lan91c11x_read_reg_fast(int reg) +{ + volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR; + uint16_t val; + + val = ptr[((reg & 0xf) >> 1)]; + + return val; +} + +void lan91c11x_write_reg_fast(int reg, uint16_t value) +{ + volatile uint16_t *ptr = (uint16_t *)LAN91C11X_BASE_ADDR; + + ptr[((reg & 0xf) >> 1)] = value; +} + + +uint16_t lan91c11x_read_phy_reg(int reg) +{ + int i; + uint16_t mask; + uint16_t bits[64]; + int clk_idx = 0; + int input_idx = 0; + uint16_t phydata; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + } + + /* Start code <01> */ + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + + /* Read command <10> */ + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + + /* Output the PHY address, msb first - Internal PHY is address 0 */ + for (i = 0; i < 5; ++i) { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + } + + /* Output the phy register number, msb first */ + mask = 0x10; + for (i = 0; i < 5; ++i) { + if (reg & mask) { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + } else { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + } + + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* 1 bit time for turnaround */ + bits[clk_idx++] = 0; + + /* Input starts at this bit time */ + input_idx = clk_idx; + + /* Will input 16 bits */ + for (i = 0; i < 16; ++i) { + bits[clk_idx++] = 0; + } + + /* Final clock bit */ + bits[clk_idx++] = 0; + + /* Turn off all MII Interface bits */ + lan91c11x_write_reg(LAN91C11X_MGMT, + lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0); + + /* Clock all 64 cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + lan91c11x_write_reg(LAN91C11X_MGMT, bits[i]); + rtems_task_wake_after(1); + + /* Clock Hi - input data */ + lan91c11x_write_reg(LAN91C11X_MGMT, bits[i] | LAN91C11X_MGMT_MCLK); + rtems_task_wake_after(1); + bits[i] |= lan91c11x_read_reg(LAN91C11X_MGMT) & LAN91C11X_MGMT_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + lan91c11x_write_reg(LAN91C11X_MGMT, lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0); + rtems_task_wake_after(1); + + /* Recover input data */ + phydata = 0; + for (i = 0; i < 16; ++i) { + phydata <<= 1; + + if (bits[input_idx++] & LAN91C11X_MGMT_MDI) { + phydata |= 0x0001; + } + } + + return phydata; +} + + + +void lan91c11x_write_phy_reg(int reg, uint16_t phydata) +{ + int i; + ushort mask; + ushort bits[64]; + int clk_idx = 0; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + } + + /* Start code <01> */ + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + + /* Write command <01> */ + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + + /* Output the PHY address, msb first - Internal PHY is address 0 */ + for (i = 0; i < 5; ++i) { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + } + + /* Output the phy register number, msb first */ + mask = 0x10; + for (i = 0; i < 5; ++i) { + if (reg & mask) { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + } else { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + } + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* 2 extra bit times for turnaround */ + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; + + /* Write out 16 bits of data, msb first */ + mask = 0x8000; + for (i = 0; i < 16; ++i) { + if (phydata & mask) { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE | LAN91C11X_MGMT_MDO; + } else { + bits[clk_idx++] = LAN91C11X_MGMT_MDOE; + } + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Final clock bit */ + bits[clk_idx++] = 0; + + /* Turn off all MII Interface bits */ + lan91c11x_write_reg(LAN91C11X_MGMT, + lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0); + + /* Clock all 64 cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + lan91c11x_write_reg(LAN91C11X_MGMT, bits[i]); + rtems_task_wake_after(1); + + /* Clock Hi - input data */ + lan91c11x_write_reg(LAN91C11X_MGMT, bits[i] | LAN91C11X_MGMT_MCLK); + rtems_task_wake_after(1); + bits[i] |= lan91c11x_read_reg(LAN91C11X_MGMT) & LAN91C11X_MGMT_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + lan91c11x_write_reg(LAN91C11X_MGMT, + lan91c11x_read_reg(LAN91C11X_MGMT) & 0xfff0); + rtems_task_wake_after(1); + +} + + + diff --git a/c/src/lib/libbsp/arm/csb336/network/lan91c11x.h b/c/src/lib/libbsp/arm/csb336/network/lan91c11x.h new file mode 100644 index 0000000000..6f7d12a3b3 --- /dev/null +++ b/c/src/lib/libbsp/arm/csb336/network/lan91c11x.h @@ -0,0 +1,216 @@ +/* + * Header file for SMSC LAN91C11x ethernet devices + * + * Copyright (c) 2004 by Cogent Computer Systems + * Written by Jay Monkman + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + */ +#ifndef __LAN91C11X_H__ +#define __LAN91C11X_H__ + +#include +#include + +uint16_t lan91c11x_read_reg(int); +void lan91c11x_write_reg(int, uint16_t); +uint16_t lan91c11x_read_reg_fast(int); +void lan91c11x_write_reg_fast(int, uint16_t); +void lan91c11x_write_phy_reg(int , uint16_t); +uint16_t lan91c11x_read_phy_reg(int); +void lan91c11x_unlock(void); +void lan91c11x_lock(void); + +#define LAN91C11X_BASE_ADDR 0x12000000 + +#define LAN91C11X_REG(_b_, _r_) ((((_b_) & 0xf) << 4) | ((_r_) & 0xf)) + + +#define LAN91C11X_TCR (LAN91C11X_REG(0, 0x0)) +#define LAN91C11X_EPHSTAT (LAN91C11X_REG(0, 0x2)) +#define LAN91C11X_RCR (LAN91C11X_REG(0, 0x4)) +#define LAN91C11X_CNTR (LAN91C11X_REG(0, 0x6)) +#define LAN91C11X_MIR (LAN91C11X_REG(0, 0x8)) +#define LAN91C11X_RPCR (LAN91C11X_REG(0, 0xa)) +#define LAN91C11X_BANK (LAN91C11X_REG(0, 0xe)) +#define LAN91C11X_CONFIG (LAN91C11X_REG(1, 0x0)) +#define LAN91C11X_BASE (LAN91C11X_REG(1, 0x2)) +#define LAN91C11X_IA0 (LAN91C11X_REG(1, 0x4)) +#define LAN91C11X_IA2 (LAN91C11X_REG(1, 0x6)) +#define LAN91C11X_IA4 (LAN91C11X_REG(1, 0x8)) +#define LAN91C11X_GNRL (LAN91C11X_REG(1, 0xa)) +#define LAN91C11X_CTRL (LAN91C11X_REG(1, 0xc)) +#define LAN91C11X_MMUCMD (LAN91C11X_REG(2, 0x0)) +#define LAN91C11X_PNR (LAN91C11X_REG(2, 0x2)) +#define LAN91C11X_FIFO (LAN91C11X_REG(2, 0x4)) +#define LAN91C11X_PTR (LAN91C11X_REG(2, 0x6)) +#define LAN91C11X_DATA (LAN91C11X_REG(2, 0x8)) +#define LAN91C11X_INT (LAN91C11X_REG(2, 0xc)) +#define LAN91C11X_MT0 (LAN91C11X_REG(3, 0x0)) +#define LAN91C11X_MT2 (LAN91C11X_REG(3, 0x2)) +#define LAN91C11X_MT4 (LAN91C11X_REG(3, 0x4)) +#define LAN91C11X_MT6 (LAN91C11X_REG(3, 0x6)) +#define LAN91C11X_MGMT (LAN91C11X_REG(3, 0x8)) +#define LAN91C11X_REV (LAN91C11X_REG(3, 0xa)) +#define LAN91C11X_ERCV (LAN91C11X_REG(3, 0xc)) + + +#define LAN91C11X_TCR_TXENA (bit(0)) +#define LAN91C11X_TCR_LOOP (bit(1)) +#define LAN91C11X_TCR_FORCOL (bit(2)) +#define LAN91C11X_TCR_PADEN (bit(7)) +#define LAN91C11X_TCR_NOCRC (bit(8)) +#define LAN91C11X_TCR_MONCSN (bit(10)) +#define LAN91C11X_TCR_FDUPLX (bit(11)) +#define LAN91C11X_TCR_STPSQET (bit(12)) +#define LAN91C11X_TCR_EPHLOOP (bit(13)) +#define LAN91C11X_TCR_SWFDUP (bit(15)) + +#define LAN91C11X_EPHSTAT_TXSUC (bit(0)) +#define LAN91C11X_EPHSTAT_SNGLCOL (bit(1)) +#define LAN91C11X_EPHSTAT_MULCOL (bit(2)) +#define LAN91C11X_EPHSTAT_LTXMUL (bit(3)) +#define LAN91C11X_EPHSTAT_16COL (bit(4)) +#define LAN91C11X_EPHSTAT_SQET (bit(5)) +#define LAN91C11X_EPHSTAT_LTXBRD (bit(6)) +#define LAN91C11X_EPHSTAT_TXDFR (bit(7)) +#define LAN91C11X_EPHSTAT_LATCOL (bit(9)) +#define LAN91C11X_EPHSTAT_LOST (bit(10)) +#define LAN91C11X_EPHSTAT_EXCDEF (bit(11)) +#define LAN91C11X_EPHSTAT_CTRROL (bit(12)) +#define LAN91C11X_EPHSTAT_LINK (bit(14)) +#define LAN91C11X_EPHSTAT_TXUNRN (bit(15)) + +#define LAN91C11X_RCR_RXABT (bit(0)) +#define LAN91C11X_RCR_PRMS (bit(1)) +#define LAN91C11X_RCR_ALMUL (bit(2)) +#define LAN91C11X_RCR_RXEN (bit(8)) +#define LAN91C11X_RCR_STRIP (bit(9)) +#define LAN91C11X_RCR_ABTENB (bit(13)) +#define LAN91C11X_RCR_FILT (bit(14)) +#define LAN91C11X_RCR_RST (bit(15)) + +#define LAN91C11X_RPCR_LS0B (bit(2)) +#define LAN91C11X_RPCR_LS1B (bit(3)) +#define LAN91C11X_RPCR_LS2B (bit(4)) +#define LAN91C11X_RPCR_LS0A (bit(5)) +#define LAN91C11X_RPCR_LS1A (bit(6)) +#define LAN91C11X_RPCR_LS2A (bit(7)) +#define LAN91C11X_RPCR_ANEG (bit(11)) +#define LAN91C11X_RPCR_DPLX (bit(12)) +#define LAN91C11X_RPCR_SPEED (bit(13)) + +#define LAN91C11X_CONFIG_EXTPHY (bit(9)) +#define LAN91C11X_CONFIG_GPCTRL (bit(10)) +#define LAN91C11X_CONFIG_NOWAIT (bit(12)) +#define LAN91C11X_CONFIG_PWR (bit(15)) + +#define LAN91C11X_CTRL_STORE (bit(0)) +#define LAN91C11X_CTRL_RELOAD (bit(1)) +#define LAN91C11X_CTRL_EEPROM (bit(2)) +#define LAN91C11X_CTRL_TEEN (bit(5)) +#define LAN91C11X_CTRL_CREN (bit(6)) +#define LAN91C11X_CTRL_LEEN (bit(7)) +#define LAN91C11X_CTRL_AUTO (bit(11)) +#define LAN91C11X_CTRL_RCVBAD (bit(14)) + +#define LAN91C11X_MMUCMD_BUSY (bit(0)) +#define LAN91C11X_MMUCMD_NOOP (0 << 5) +#define LAN91C11X_MMUCMD_ALLOCTX (1 << 5) +#define LAN91C11X_MMUCMD_RESETMMU (2 << 5) +#define LAN91C11X_MMUCMD_REMFRM (3 << 5) +#define LAN91C11X_MMUCMD_REMTOP (4 << 5) +#define LAN91C11X_MMUCMD_RELEASE (5 << 5) +#define LAN91C11X_MMUCMD_ENQUEUE (6 << 5) +#define LAN91C11X_MMUCMD_RESETTX (7 << 5) + +#define LAN91C11X_PTR_MASK (0x7ff) +#define LAN91C11X_PTR_NE (bit(11)) +#define LAN91C11X_PTR_ETEN (bit(12)) +#define LAN91C11X_PTR_READ (bit(13)) +#define LAN91C11X_PTR_AUTOINC (bit(14)) +#define LAN91C11X_PTR_RCV (bit(15)) + +#define LAN91C11X_INT_RX (bit(0)) +#define LAN91C11X_INT_TX (bit(1)) +#define LAN91C11X_INT_TXE (bit(2)) +#define LAN91C11X_INT_ALLOC (bit(3)) +#define LAN91C11X_INT_RXOV (bit(4)) +#define LAN91C11X_INT_EPH (bit(5)) +#define LAN91C11X_INT_ERX (bit(6)) +#define LAN91C11X_INT_MD (bit(7)) +#define LAN91C11X_INT_RXMASK (bit(8)) +#define LAN91C11X_INT_TXMASK (bit(9)) +#define LAN91C11X_INT_TXEMASK (bit(10)) +#define LAN91C11X_INT_ALLOCMASK (bit(11)) +#define LAN91C11X_INT_RXOVMASK (bit(12)) +#define LAN91C11X_INT_EPHMASK (bit(13)) +#define LAN91C11X_INT_ERXMASK (bit(14)) +#define LAN91C11X_INT_MDMASK (bit(15)) + +#define LAN91C11X_MGMT_MDO (bit(0)) +#define LAN91C11X_MGMT_MDI (bit(1)) +#define LAN91C11X_MGMT_MCLK (bit(2)) +#define LAN91C11X_MGMT_MDOE (bit(3)) +#define LAN91C11X_MGMT_MSKCRS100 (bit(14)) + + +#define LAN91C11X_PKT_CTRL_CRC (bit(4)) +#define LAN91C11X_PKT_CTRL_ODD (bit(5)) + + +/* PHY Registers */ +#define PHY_CTRL 0x00 /* PHY Control */ +#define PHY_STAT 0x01 /* PHY Status */ +#define PHY_ID1 0x02 /* PHY Identifier 1 */ +#define PHY_ID2 0x03 /* PHY Identifier 2 */ +#define PHY_AD 0x04 /* PHY Auto-negotiate Control */ +#define PHY_RMT 0x05 /* PHY Auto-neg Remote End Cap Register */ +#define PHY_CFG1 0x10 /* PHY Configuration 1 */ +#define PHY_CFG2 0x11 /* PHY Configuration 2 */ +#define PHY_INT 0x12 /* Status Output (Interrupt Status) */ +#define PHY_MASK 0x13 /* Interrupt Mask */ + +/* PHY Control Register Bit Defines */ +#define PHY_CTRL_RST 0x8000 /* PHY Reset */ +#define PHY_CTRL_LPBK 0x4000 /* PHY Loopback */ +#define PHY_CTRL_SPEED 0x2000 /* 100Mbps, 0=10Mpbs */ +#define PHY_CTRL_ANEGEN 0x1000 /* Enable Auto negotiation */ +#define PHY_CTRL_PDN 0x0800 /* PHY Power Down mode */ +#define PHY_CTRL_MIIDIS 0x0400 /* MII 4 bit interface disabled */ +#define PHY_CTRL_ANEGRST 0x0200 /* Reset Auto negotiate */ +#define PHY_CTRL_DPLX 0x0100 /* Full Duplex, 0=Half Duplex */ +#define PHY_CTRL_COLTST 0x0080 /* MII Colision Test */ + +#define PHY_STAT_CAPT4 0x8000 +#define PHY_STAT_CAPTXF 0x4000 +#define PHY_STAT_CAPTXH 0x2000 +#define PHY_STAT_CAPTF 0x1000 +#define PHY_STAT_CAPTH 0x0800 +#define PHY_STAT_CAPSUPR 0x0040 +#define PHY_STAT_ANEGACK 0x0020 +#define PHY_STAT_REMFLT 0x0010 +#define PHY_STAT_CAPANEG 0x0008 +#define PHY_STAT_LINK 0x0004 +#define PHY_STAT_JAB 0x0002 +#define PHY_STAT_EXREG 0x0001 + +#define PHY_ADV_NP 0x8000 +#define PHY_ADV_ACK 0x4000 +#define PHY_ADV_RF 0x2000 +#define PHY_ADV_T4 0x0200 +#define PHY_ADV_TXFDX 0x0100 +#define PHY_ADV_TXHDX 0x0080 +#define PHY_ADV_10FDX 0x0040 +#define PHY_ADV_10HDX 0x0020 +#define PHY_ADV_CSMA 0x0001 + + + + +#endif /* __LAN91C11X_H__ */ diff --git a/c/src/lib/libbsp/arm/csb336/network/network.c b/c/src/lib/libbsp/arm/csb336/network/network.c new file mode 100644 index 0000000000..0d63201f99 --- /dev/null +++ b/c/src/lib/libbsp/arm/csb336/network/network.c @@ -0,0 +1,724 @@ +/* + * MC9323MXL Ethernet driver + * + * Copyright (c) 2004 by Cogent Computer Systems + * Written by Jay Monkman + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * + * http://www.OARcorp.com/rtems/license.html. + * + * $Id$ + */ +#include +#include +#include +#include "lan91c11x.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +/* RTEMS event used by interrupt handler to start receive daemon. */ +#define START_RECEIVE_EVENT RTEMS_EVENT_1 + +/* RTEMS event used to start transmit daemon. */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + +static rtems_isr enet_isr(rtems_vector_number vector); +static void enet_isr_on(const rtems_irq_connect_data *unused); +static void enet_isr_off(const rtems_irq_connect_data *unused); +static int enet_isr_is_on(const rtems_irq_connect_data *irq); + +/* Replace the first value with the clock's interrupt name. */ +rtems_irq_connect_data mc9328mxl_enet_isr_data = {BSP_INT_GPIO_PORTA, + (rtems_irq_hdl)enet_isr, + enet_isr_on, + enet_isr_off, + enet_isr_is_on, + 3, /* unused for ARM */ + 0 }; /* unused for ARM */ + +typedef struct { + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ + unsigned long interrupts; /* total number of interrupts */ + unsigned long rx_interrupts; /* total number of rx interrupts */ + unsigned long tx_interrupts; /* total number of tx interrupts */ + unsigned long txerr_interrupts; /* total number of tx error interrupts */ + +} eth_stats_t; + +/* + * Hardware-specific storage + */ +typedef struct +{ + /* + * Connection to networking code + * This entry *must* be the first in the sonic_softc structure. + */ + struct arpcom arpcom; + + int accept_bcast; + + /* Tasks waiting for interrupts */ + rtems_id rx_task; + rtems_id tx_task; + + eth_stats_t stats; + +} mc9328mxl_enet_softc_t; + +static mc9328mxl_enet_softc_t softc; + + +/* function prototypes */ +int rtems_mc9328mxl_enet_attach(struct rtems_bsdnet_ifconfig *config, + void *chip); +void mc9328mxl_enet_init(void *arg); +void mc9328mxl_enet_init_hw(mc9328mxl_enet_softc_t *sc); +void mc9328mxl_enet_start(struct ifnet *ifp); +void mc9328mxl_enet_stop (mc9328mxl_enet_softc_t *sc); +void mc9328mxl_enet_tx_task (void *arg); +void mc9328mxl_enet_sendpacket (struct ifnet *ifp, struct mbuf *m); +void mc9328mxl_enet_rx_task(void *arg); +void mc9328mxl_enet_stats(mc9328mxl_enet_softc_t *sc); +static int mc9328mxl_enet_ioctl(struct ifnet *ifp, int command, caddr_t data); + + +int rtems_mc9328mxl_enet_attach ( + struct rtems_bsdnet_ifconfig *config, + void *chip /* only one ethernet, so no chip number */ + ) +{ + struct ifnet *ifp; + int mtu; + int unitnumber; + char *unitname; + int tmp; + + /* + * Parse driver name + */ + unitnumber = rtems_bsdnet_parse_driver_name(config, &unitname); + if (unitnumber < 0) { + return 0; + } + + /* + * Is driver free? + */ + if (unitnumber != 0) { + printf ("Bad MC9328MXL unit number.\n"); + return 0; + } + + ifp = &softc.arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf ("Driver already in use.\n"); + return 0; + } + + /* zero out the control structure */ + memset( &softc, 0, sizeof(softc) ); + + + /* set the MAC address */ + tmp = lan91c11x_read_reg(LAN91C11X_IA0); + softc.arpcom.ac_enaddr[0] = tmp & 0xff; + softc.arpcom.ac_enaddr[1] = (tmp >> 8) & 0xff; + + tmp = lan91c11x_read_reg(LAN91C11X_IA2); + softc.arpcom.ac_enaddr[2] = tmp & 0xff; + softc.arpcom.ac_enaddr[3] = (tmp >> 8) & 0xff; + + tmp = lan91c11x_read_reg(LAN91C11X_IA4); + softc.arpcom.ac_enaddr[4] = tmp & 0xff; + softc.arpcom.ac_enaddr[5] = (tmp >> 8) & 0xff; + + if (config->mtu) { + mtu = config->mtu; + } else { + mtu = ETHERMTU; + } + + softc.accept_bcast = !config->ignore_broadcast; + + /* + * Set up network interface values + */ + ifp->if_softc = &softc; + ifp->if_unit = unitnumber; + ifp->if_name = unitname; + ifp->if_mtu = mtu; + ifp->if_init = mc9328mxl_enet_init; + ifp->if_ioctl = mc9328mxl_enet_ioctl; + ifp->if_start = mc9328mxl_enet_start; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST; + if (ifp->if_snd.ifq_maxlen == 0) { + ifp->if_snd.ifq_maxlen = ifqmaxlen; + } + + /* Attach the interface */ + if_attach (ifp); + ether_ifattach (ifp); + return 1; +} + +void mc9328mxl_enet_init(void *arg) +{ + mc9328mxl_enet_softc_t *sc = arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + + /* + *This is for stuff that only gets done once (mc9328mxl_enet_init() + * gets called multiple times + */ + if (sc->tx_task == 0) + { + /* Set up ENET hardware */ + mc9328mxl_enet_init_hw(sc); + + /* Start driver tasks */ + sc->rx_task = rtems_bsdnet_newproc("ENrx", + 4096, + mc9328mxl_enet_rx_task, + sc); + sc->tx_task = rtems_bsdnet_newproc("ENtx", + 4096, + mc9328mxl_enet_tx_task, + sc); + } /* if tx_task */ + + + /* Configure for promiscuous if needed */ + if (ifp->if_flags & IFF_PROMISC) { + lan91c11x_write_reg(LAN91C11X_RCR, + (lan91c11x_read_reg(LAN91C11X_RCR) | + LAN91C11X_RCR_PRMS)); + } + + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + + /* Enable TX/RX */ + lan91c11x_write_reg(LAN91C11X_TCR, + (lan91c11x_read_reg(LAN91C11X_TCR) | + LAN91C11X_TCR_TXENA)); + + lan91c11x_write_reg(LAN91C11X_RCR, + (lan91c11x_read_reg(LAN91C11X_RCR) | + LAN91C11X_RCR_RXEN)); + + +} /* mc9328mxl_enet_init() */ + +void mc9328mxl_enet_init_hw(mc9328mxl_enet_softc_t *sc) +{ + uint16_t stat; + uint16_t my = 0; + + lan91c11x_write_reg(LAN91C11X_RCR, LAN91C11X_RCR_RST); + lan91c11x_write_reg(LAN91C11X_RCR, 0); + rtems_task_wake_after(1); + + /* Reset the PHY */ + lan91c11x_write_phy_reg(PHY_CTRL, PHY_CTRL_RST); + while(lan91c11x_read_phy_reg(PHY_CTRL) & PHY_CTRL_RST) { + rtems_task_wake_after(1); + } + + + stat = lan91c11x_read_phy_reg(PHY_STAT); + + if(stat & PHY_STAT_CAPT4) { + my |= PHY_ADV_T4; + } +/* 100Mbs doesn't work, so we won't advertise it */ + + if(stat & PHY_STAT_CAPTXF) { + my |= PHY_ADV_TXFDX; + } + if(stat & PHY_STAT_CAPTXH) { + my |= PHY_ADV_TXHDX; + } + + if(stat & PHY_STAT_CAPTF) { + my |= PHY_ADV_10FDX; + } + + if(stat & PHY_STAT_CAPTH) { + my |= PHY_ADV_10HDX; + } + + my |= PHY_ADV_CSMA; + + lan91c11x_write_phy_reg(PHY_AD, my); + + + /* Enable Autonegotiation */ +#if 0 + lan91c11x_write_phy_reg(PHY_CTRL, + (PHY_CTRL_ANEGEN | PHY_CTRL_ANEGRST)); +#endif + + /* Enable full duplex, let MAC take care + * of padding and CRC. + */ + lan91c11x_write_reg(LAN91C11X_TCR, + (LAN91C11X_TCR_PADEN | + LAN91C11X_TCR_SWFDUP)); + + /* Disable promisc, don'tstrip CRC */ + lan91c11x_write_reg(LAN91C11X_RCR, 0); + + /* Enable auto-negotiation, LEDA is link, LEDB is traffic */ + lan91c11x_write_reg(LAN91C11X_RPCR, + (LAN91C11X_RPCR_ANEG | + LAN91C11X_RPCR_LS2B)); + + /* Don't add wait states, enable PHY power */ + lan91c11x_write_reg(LAN91C11X_CONFIG, + (LAN91C11X_CONFIG_NOWAIT | + LAN91C11X_CONFIG_PWR)); + + /* Disable error interrupts, enable auto release */ + lan91c11x_write_reg(LAN91C11X_CTRL, LAN91C11X_CTRL_AUTO); + + /* Reset MMU */ + lan91c11x_write_reg(LAN91C11X_MMUCMD, + LAN91C11X_MMUCMD_RESETMMU ); + + + rtems_task_wake_after(100); + /* Enable Autonegotiation */ + lan91c11x_write_phy_reg(PHY_CTRL, 0x3000); + rtems_task_wake_after(100); + + /* Enable Interrupts for RX */ + lan91c11x_write_reg(LAN91C11X_INT, LAN91C11X_INT_RXMASK); + + /* Enable interrupts on GPIO Port A3 */ + /* Make pin 3 an input */ + MC9328MXL_GPIOA_DDIR &= ~bit(3); + + /* Use GPIO function for pin 3 */ + MC9328MXL_GPIOA_GIUS |= bit(3); + + /* Set for active high, level triggered interupt */ + MC9328MXL_GPIOA_ICR1 = ((MC9328MXL_GPIOA_ICR1 & ~(3 << 6)) | + (2 << 6)); + + /* Enable GPIO port A3 interrupt */ + MC9328MXL_GPIOA_IMR |= bit(3); + + /* Install the interrupt handler */ + BSP_install_rtems_irq_handler(&mc9328mxl_enet_isr_data); + +} /* mc9328mxl_enet_init_hw() */ + +void mc9328mxl_enet_start(struct ifnet *ifp) +{ + mc9328mxl_enet_softc_t *sc = ifp->if_softc; + + rtems_event_send(sc->tx_task, START_TRANSMIT_EVENT); + ifp->if_flags |= IFF_OACTIVE; +} + +void mc9328mxl_enet_stop (mc9328mxl_enet_softc_t *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + + ifp->if_flags &= ~IFF_RUNNING; + + + /* Stop the transmitter and receiver. */ + lan91c11x_write_reg(LAN91C11X_TCR, + (lan91c11x_read_reg(LAN91C11X_TCR) & + ~LAN91C11X_TCR_TXENA)); + + lan91c11x_write_reg(LAN91C11X_RCR, + (lan91c11x_read_reg(LAN91C11X_RCR) & + ~LAN91C11X_RCR_RXEN)); + +} + +/* + * Driver transmit daemon + */ +void mc9328mxl_enet_tx_task(void *arg) +{ + mc9328mxl_enet_softc_t *sc = (mc9328mxl_enet_softc_t *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + rtems_event_set events; + + for (;;) + { + 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; + } + mc9328mxl_enet_sendpacket (ifp, m); + softc.stats.tx_packets++; + + } + ifp->if_flags &= ~IFF_OACTIVE; + } +} + +/* Send packet */ +void mc9328mxl_enet_sendpacket (struct ifnet *ifp, struct mbuf *m) +{ + struct mbuf *l = NULL; + int size = 0; + int tmp; + int i; + int start; + uint16_t d; + + /* How big is the packet ? */ + l = m; + do { + size += l->m_len; + l = l->m_next; + } while (l != NULL); + + /* Allocate a TX buffer */ + lan91c11x_write_reg(LAN91C11X_MMUCMD, + (LAN91C11X_MMUCMD_ALLOCTX | + (size >> 8))); + + /* Wait for the allocation */ + while ((lan91c11x_read_reg(LAN91C11X_INT) & LAN91C11X_INT_ALLOC) == 0) { + continue; + } + + tmp = lan91c11x_read_reg(LAN91C11X_PNR); + lan91c11x_write_reg(LAN91C11X_PNR, ((tmp >> 8) & 0xff)); + + /* Set the data pointer for auto increment */ + lan91c11x_write_reg(LAN91C11X_PTR, LAN91C11X_PTR_AUTOINC); + + /* A delay is needed between pointer and data access ?!? */ + for (i = 0; i < 10; i++) { + continue; + } + + /* Write status word */ + lan91c11x_write_reg(LAN91C11X_DATA, 0); + + /* Write byte count */ + if (size & 1) { + size++; + } + lan91c11x_write_reg(LAN91C11X_DATA, size + 6); + + lan91c11x_lock(); + + /* Copy the mbuf */ + l = m; + start = 0; + d = 0; + while (l != NULL) + { + uint8_t *data; + + data = mtod(l, uint8_t *); + + for (i = start; i < l->m_len; i++) { + if ((i & 1) == 0) { + d = data[i] << 8; + } else { + d = d | data[i]; + lan91c11x_write_reg_fast(LAN91C11X_DATA, htons(d)); + } + } + + /* If everything is 2 byte aligned, i will be even */ + start = (i & 1); + + l = l->m_next; + } + + /* write control byte */ + if (i & 1) { + lan91c11x_write_reg_fast(LAN91C11X_DATA, + htons(LAN91C11X_PKT_CTRL_ODD | d)); + } else { + lan91c11x_write_reg_fast(LAN91C11X_DATA, 0); + } + + lan91c11x_unlock(); + + /* Enable TX interrupts */ + lan91c11x_write_reg(LAN91C11X_INT, + (lan91c11x_read_reg(LAN91C11X_INT) | + LAN91C11X_INT_TXMASK | + LAN91C11X_INT_TXEMASK)); + + /* Enqueue it */ + lan91c11x_write_reg(LAN91C11X_MMUCMD, + LAN91C11X_MMUCMD_ENQUEUE); + + /* free the mbuf chain we just copied */ + m_freem(m); + +} /* mc9328mxl_enet_sendpacket () */ + + +/* reader task */ +void mc9328mxl_enet_rx_task(void *arg) +{ + mc9328mxl_enet_softc_t *sc = (mc9328mxl_enet_softc_t *)arg; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mbuf *m; + struct ether_header *eh; + rtems_event_set events; + int pktlen; + uint16_t rsw; + uint16_t bc; + uint16_t cbyte; + int i; + uint16_t int_reg; + + /* Input packet handling loop */ + while (1) { + rtems_bsdnet_event_receive( + START_RECEIVE_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &events); + + /* Configure for reads from RX data area */ + lan91c11x_write_reg(LAN91C11X_PTR, + (LAN91C11X_PTR_AUTOINC | + LAN91C11X_PTR_RCV | + LAN91C11X_PTR_READ)); + + /* read the receive status word */ + rsw = lan91c11x_read_reg(LAN91C11X_DATA); + /* TBD: Need to check rsw here */ + + /* read the byte count */ + bc = lan91c11x_read_reg(LAN91C11X_DATA); + pktlen = (bc & 0x7ff) - 6; + + /* get an mbuf for this packet */ + MGETHDR(m, M_WAIT, MT_DATA); + + /* now get a cluster pointed to by the mbuf */ + /* since an mbuf by itself is too small */ + MCLGET(m, M_WAIT); + + lan91c11x_lock(); + + /* Copy the received packet into an mbuf */ + for (i = 0; i < (pktlen / 2); i++) { + ((uint16_t*)m->m_ext.ext_buf)[i] = + lan91c11x_read_reg_fast(LAN91C11X_DATA); + } + + cbyte = lan91c11x_read_reg_fast(LAN91C11X_DATA); + if (cbyte & LAN91C11X_PKT_CTRL_ODD) { + ((uint16_t*)m->m_ext.ext_buf)[i] = cbyte; + pktlen++; + } + lan91c11x_unlock(); + + /* Release the packets memory */ + lan91c11x_write_reg(LAN91C11X_MMUCMD, + LAN91C11X_MMUCMD_REMTOP); + + /* set the receiving interface */ + m->m_pkthdr.rcvif = ifp; + m->m_nextpkt = 0; + + /* set the length of the mbuf */ + m->m_len = pktlen - (sizeof(struct ether_header)); + m->m_pkthdr.len = m->m_len; + + /* strip off the ethernet header from the mbuf */ + /* but save the pointer to it */ + eh = mtod (m, struct ether_header *); + m->m_data += sizeof(struct ether_header); + + + softc.stats.rx_packets++; + + /* give all this stuff to the stack */ + ether_input(ifp, eh, m); + + /* renable RX interrupts */ + int_reg = lan91c11x_read_reg(LAN91C11X_INT); + int_reg |= LAN91C11X_INT_RXMASK; + lan91c11x_write_reg(LAN91C11X_INT, int_reg); + + } +} /* mc9328mxl_enet_rx_task */ + + +/* Show interface statistics */ +void mc9328mxl_enet_stats (mc9328mxl_enet_softc_t *sc) +{ + printf (" Total Interrupts:%-8lu", sc->stats.interrupts); + printf (" Rx Interrupts:%-8lu", sc->stats.rx_interrupts); + printf (" Tx Interrupts:%-8lu\n", sc->stats.tx_interrupts); + printf (" Tx Error Interrupts:%-8lu\n", sc->stats.txerr_interrupts); + printf (" Rx Packets:%-8lu", sc->stats.rx_packets); + printf (" Tx Packets:%-8lu\n", sc->stats.tx_packets); +} + + +/* Enables mc9328mxl_enet interrupts. */ +static void enet_isr_on(const rtems_irq_connect_data *unused) +{ + /* Enable interrupts */ + MC9328MXL_AITC_INTENNUM = MC9328MXL_INT_GPIO_PORTA; + + return; +} + +/* Disables enet interrupts */ +static void enet_isr_off(const rtems_irq_connect_data *unused) +{ + /* disable all various TX/RX interrupts */ + MC9328MXL_AITC_INTDISNUM = MC9328MXL_INT_GPIO_PORTA; + + return; +} + +/* Tests to see if mc9328mxl_enet interrupts are enabled, and returns non-0 if so. + * If interrupt is not enabled, returns 0. + */ +static int enet_isr_is_on(const rtems_irq_connect_data *irq) +{ + return MC9328MXL_AITC_INTENABLEL & (1 << MC9328MXL_INT_GPIO_PORTA); +} + +/* Driver ioctl handler */ +static int +mc9328mxl_enet_ioctl (struct ifnet *ifp, int command, caddr_t data) +{ + mc9328mxl_enet_softc_t *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: + mc9328mxl_enet_stop (sc); + break; + + case IFF_UP: + mc9328mxl_enet_init (sc); + break; + + case IFF_UP | IFF_RUNNING: + mc9328mxl_enet_stop (sc); + mc9328mxl_enet_init (sc); + break; + + default: + break; + } /* switch (if_flags) */ + break; + + case SIO_RTEMS_SHOW_STATS: + mc9328mxl_enet_stats (sc); + break; + + /* + * FIXME: All sorts of multicast commands need to be added here! + */ + default: + error = EINVAL; + break; + } /* switch (command) */ + return error; +} + +/* interrupt handler */ +rtems_isr enet_isr (rtems_vector_number v) +{ + uint16_t int_reg; + + softc.stats.interrupts++; + /* get the ISR status and determine RX or TX */ + int_reg = lan91c11x_read_reg(LAN91C11X_INT); + + /* Handle RX interrupts */ + if ((int_reg & LAN91C11X_INT_RX) && (int_reg & LAN91C11X_INT_RXMASK)) { + softc.stats.rx_interrupts++; + + /* Disable the interrupt */ + int_reg &= ~LAN91C11X_INT_RXMASK; + + rtems_event_send (softc.rx_task, START_RECEIVE_EVENT); + } + + /* Handle TX Empty interrupts */ + if ((int_reg & LAN91C11X_INT_TXE) && (int_reg & LAN91C11X_INT_TXEMASK)) { + softc.stats.tx_interrupts++; + + /* Disable the interrupt */ + int_reg &= ~LAN91C11X_INT_TXEMASK; + + /* Acknowledge the interrupt */ + int_reg |= LAN91C11X_INT_TXE; + + rtems_event_send(softc.tx_task, START_TRANSMIT_EVENT); + + } + + /* Handle interrupts for transmit errors */ + if ((int_reg & LAN91C11X_INT_TX) && (int_reg & LAN91C11X_INT_TXMASK)) { + softc.stats.txerr_interrupts++; + printk("Caught TX interrupt - error on transmission\n"); + } + + /* Update the interrupt register on the 91c11x */ + lan91c11x_write_reg(LAN91C11X_INT, int_reg); + + /* clear GPIO Int. status */ + MC9328MXL_GPIOA_ISR |= bit(3); +} + -- cgit v1.2.3