diff options
Diffstat (limited to 'roms/u-boot-sam460ex/drivers/net')
97 files changed, 63369 insertions, 0 deletions
diff --git a/roms/u-boot-sam460ex/drivers/net/3c589.c b/roms/u-boot-sam460ex/drivers/net/3c589.c new file mode 100644 index 000000000..f2c7d326b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/3c589.c @@ -0,0 +1,517 @@ +/*------------------------------------------------------------------------ + . 3c589.c + . This is a driver for 3Com's 3C589 (Etherlink III) PCMCIA Ethernet device. + . + . (C) Copyright 2002 + . Sysgo Real-Time Solutions, GmbH <www.elinos.com> + . Rolf Offermanns <rof@sysgo.de> + . + . This program is free software; you can redistribute it and/or modify + . it under the terms of the GNU General Public License as published by + . the Free Software Foundation; either version 2 of the License, or + . (at your option) any later version. + . + . This program is distributed in the hope that it will be useful, + . but WITHOUT ANY WARRANTY; without even the implied warranty of + . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + . GNU General Public License for more details. + . + . You should have received a copy of the GNU General Public License + . along with this program; if not, write to the Free Software + . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + . + ----------------------------------------------------------------------------*/ + +#include <common.h> +#include <command.h> +#include <net.h> + +#include "3c589.h" + + +/* Use power-down feature of the chip */ +#define POWER_DOWN 0 + +#define NO_AUTOPROBE + +static const char version[] = + "Your ad here! :P\n"; + + +#undef EL_DEBUG + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; +/*------------------------------------------------------------------------ + . + . Configuration options, for the experienced user to change. + . + -------------------------------------------------------------------------*/ + +/* + . Wait time for memory to be free. This probably shouldn't be + . tuned that much, as waiting for this means nothing else happens + . in the system +*/ +#define MEMORY_WAIT_TIME 16 + + +#if (EL_DEBUG > 2 ) +#define PRINTK3(args...) printf(args) +#else +#define PRINTK3(args...) +#endif + +#if EL_DEBUG > 1 +#define PRINTK2(args...) printf(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef EL_DEBUG +#define PRINTK(args...) printf(args) +#else +#define PRINTK(args...) +#endif + +#define outb(args...) mmio_outb(args) +#define mmio_outb(value, addr) (*((volatile byte *)(addr)) = value) + +#define inb(args...) mmio_inb(args) +#define mmio_inb(addr) (*((volatile byte *)(addr))) + +#define outw(args...) mmio_outw(args) +#define mmio_outw(value, addr) (*((volatile word *)(addr)) = value) + +#define inw(args...) mmio_inw(args) +#define mmio_inw(addr) (*((volatile word *)(addr))) + +#define outsw(args...) mmio_outsw(args) +#define mmio_outsw(r,b,l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + mmio_outw( *(__b2 + __i), r); \ + } \ + }) + +#define insw(args...) mmio_insw(args) +#define mmio_insw(r,b,l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = mmio_inw(r); \ + mmio_inw(0); \ + }; \ + }) + +/*------------------------------------------------------------------------ + . + . The internal workings of the driver. If you are changing anything + . here with the 3Com stuff, you should have the datasheet and know + . what you are doing. + . + -------------------------------------------------------------------------*/ +#define EL_BASE_ADDR 0x20000000 + + +/* Offsets from base I/O address. */ +#define EL3_DATA 0x00 +#define EL3_TIMER 0x0a +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +#define EEPROM_READ 0x0080 + +#define EL3WINDOW(win_num) mmio_outw(SelectWindow + (win_num), EL_BASE_ADDR + EL3_CMD) + +/* The top five bits written to EL3_CMD are a command, the lower + 11 bits are the parameter, if applicable. */ +enum c509cmd { + TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, + TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, + SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, + SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, + StatsDisable = 22<<11, StopCoax = 23<<11, +}; + +enum c509status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 +}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 +}; + +/* Register window 1 offsets, the window used in normal operation. */ +#define TX_FIFO 0x00 +#define RX_FIFO 0x00 +#define RX_STATUS 0x08 +#define TX_STATUS 0x0B +#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ + + +/* + Read a word from the EEPROM using the regular EEPROM access register. + Assume that we are in register window zero. +*/ +static word read_eeprom(dword ioaddr, int index) +{ + int i; + outw(EEPROM_READ + index, ioaddr + 0xa); + /* Reading the eeprom takes 162 us */ + for (i = 1620; i >= 0; i--) + if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) + break; + return inw(ioaddr + 0xc); +} + +static void el_get_mac_addr( unsigned char *mac_addr ) +{ + int i; + union + { + word w; + unsigned char b[2]; + } wrd; + unsigned char old_window = inw( EL_BASE_ADDR + EL3_STATUS ) >> 13; + GO_WINDOW(0); + VX_BUSY_WAIT; + for (i = 0; i < 3; i++) + { + wrd.w = read_eeprom(EL_BASE_ADDR, 0xa+i); +#ifdef __BIG_ENDIAN + mac_addr[2*i] = wrd.b[0]; + mac_addr[2*i+1] = wrd.b[1]; +#else + mac_addr[2*i] = wrd.b[1]; + mac_addr[2*i+1] = wrd.b[0]; +#endif + } + GO_WINDOW(old_window); + VX_BUSY_WAIT; +} + + +#if EL_DEBUG > 1 +static void print_packet( byte * buf, int length ) +{ + int i; + int remainder; + int lines; + + PRINTK2("Packet of length %d \n", length ); + + lines = length / 16; + remainder = length % 16; + + for ( i = 0; i < lines ; i ++ ) { + int cur; + + for ( cur = 0; cur < 8; cur ++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + PRINTK2("%02x%02x ", a, b ); + } + PRINTK2("\n"); + } + for ( i = 0; i < remainder/2 ; i++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + PRINTK2("%02x%02x ", a, b ); + } + PRINTK2("\n"); +} +#endif /* EL_DEBUG > 1 */ + + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ +static void el_reset(bd_t *bd) +{ + /*********************************************************** + Reset 3Com 595 card + *************************************************************/ + /* QUICK HACK + * - adjust timing for 3c589 + * - enable io for PCMCIA */ + outw(0x0004, 0xa0000018); + udelay(100); + outw(0x0041, 0x28010000); + udelay(100); + + /* issue global reset */ + outw(GLOBAL_RESET, BASE + VX_COMMAND); + + /* must wait for at least 1ms */ + udelay(100000000); + + /* set mac addr */ + { + uchar mac_addr[6]; + int i; + + if (!eth_getenv_enetaddr("ethaddr", mac_addr)) { + el_get_mac_addr(mac_addr); + eth_setenv_enetaddr("ethaddr", mac_addr); + } + + GO_WINDOW(2); + VX_BUSY_WAIT; + + printf("3C589 MAC Addr.: "); + for (i = 0; i < 6; i++) + { + printf("%02x", mac_addr[i]); + outb(mac_addr[i], BASE + VX_W2_ADDR_0 + i); + VX_BUSY_WAIT; + } + printf("\n\n"); + } + + /* set RX filter */ + outw(SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST, BASE + VX_COMMAND); + VX_BUSY_WAIT; + + + /* set irq mask and read_zero */ + outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND); + VX_BUSY_WAIT; + + outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND); + VX_BUSY_WAIT; + + /* enable TP Linkbeat */ + GO_WINDOW(4); + VX_BUSY_WAIT; + + outw( ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE); + VX_BUSY_WAIT; + + +/* + * Attempt to get rid of any stray interrupts that occured during + * configuration. On the i386 this isn't possible because one may + * already be queued. However, a single stray interrupt is + * unimportant. + */ + + outw(ACK_INTR | 0xff, BASE + VX_COMMAND); + VX_BUSY_WAIT; + + /* enable TX and RX */ + outw( RX_ENABLE, BASE + VX_COMMAND ); + VX_BUSY_WAIT; + + outw( TX_ENABLE, BASE + VX_COMMAND ); + VX_BUSY_WAIT; + + + /* print the diag. regs. */ + PRINTK2("Diag. Regs\n"); + PRINTK2("--> MEDIA_TYPE: %04x\n", inw(BASE + VX_W4_MEDIA_TYPE)); + PRINTK2("--> NET_DIAG: %04x\n", inw(BASE + VX_W4_NET_DIAG)); + PRINTK2("--> FIFO_DIAG: %04x\n", inw(BASE + VX_W4_FIFO_DIAG)); + PRINTK2("--> CTRLR_STATUS: %04x\n", inw(BASE + VX_W4_CTRLR_STATUS)); + PRINTK2("\n\n"); + + /* enter working mode */ + GO_WINDOW(1); + VX_BUSY_WAIT; + + /* wait for another 1ms */ + udelay(100000000); +} + + +/*----------------------------------------------------------------- + . + . The driver can be entered at any of the following entry points. + . + .------------------------------------------------------------------ */ + +extern int eth_init(bd_t *bd); +extern void eth_halt(void); +extern int eth_rx(void); +extern int eth_send(volatile void *packet, int length); + + +/* + ------------------------------------------------------------ + . + . Internal routines + . + ------------------------------------------------------------ +*/ + +int eth_init(bd_t *bd) +{ + el_reset(bd); + return 0; +} + +void eth_halt() { + return; +} + +#define EDEBUG 1 + + +/************************************************************************** +ETH_POLL - Wait for a frame +***************************************************************************/ + +int eth_rx() +{ + word status, rx_status, packet_size; + + VX_BUSY_WAIT; + + status = inw( BASE + VX_STATUS ); + + if ( (status & S_RX_COMPLETE) == 0 ) return 0; /* nothing to do */ + + /* Packet waiting -> check RX_STATUS */ + rx_status = inw( BASE + VX_W1_RX_STATUS ); + + if ( rx_status & ERR_RX ) + { + /* error in packet -> discard */ + PRINTK("[ERROR] Invalid packet -> discarding\n"); + PRINTK("-- error code 0x%02x\n", rx_status & ERR_MASK); + PRINTK("-- rx bytes 0x%04d\n", rx_status & ((1<<11) - 1)); + PRINTK("[ERROR] Invalid packet -> discarding\n"); + outw( RX_DISCARD_TOP_PACK, BASE + VX_COMMAND ); + return 0; + } + + /* correct pack. waiting in fifo */ + packet_size = rx_status & RX_BYTES_MASK; + + PRINTK("Correct packet waiting in fifo, size: %d\n", packet_size); + + { + volatile word *packet_start = (word *)(BASE + VX_W1_RX_PIO_RD_1); + word *RcvBuffer = (word *)(NetRxPackets[0]); + int wcount = 0; + + for (wcount = 0; wcount < (packet_size >> 1); wcount++) + { + *RcvBuffer++ = *(packet_start); + } + + /* handle odd packets */ + if ( packet_size & 1 ) + { + *RcvBuffer++ = *(packet_start); + } + } + + /* fifo should now be empty (besides the padding bytes) */ + if ( ((*((word *)(BASE + VX_W1_RX_STATUS))) & RX_BYTES_MASK) > 3 ) + { + PRINTK("[ERROR] Fifo not empty after packet read (remaining pkts: %d)\n", + (((*(word *)(BASE + VX_W1_RX_STATUS))) & RX_BYTES_MASK)); + } + + /* discard packet */ + *((word *)(BASE + VX_COMMAND)) = RX_DISCARD_TOP_PACK; + + /* Pass Packets to upper Layer */ + NetReceive(NetRxPackets[0], packet_size); + return packet_size; +} + + +/************************************************************************** +ETH_TRANSMIT - Transmit a frame +***************************************************************************/ +static char padmap[] = { + 0, 3, 2, 1}; + + +int eth_send(volatile void *packet, int length) { + int pad; + int status; + volatile word *buf = (word *)packet; + int dummy = 0; + + /* padding stuff */ + pad = padmap[length & 3]; + + PRINTK("eth_send(), length: %d\n", length); + /* drop acknowledgements */ + while(( status=inb(EL_BASE_ADDR + VX_W1_TX_STATUS) )& TXS_COMPLETE ) { + if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) { + outw(TX_RESET, EL_BASE_ADDR + VX_COMMAND); + outw(TX_ENABLE, EL_BASE_ADDR + VX_COMMAND); + PRINTK("Bad status, resetting and reenabling transmitter\n"); + } + + outb(0x0, EL_BASE_ADDR + VX_W1_TX_STATUS); + } + + + while (inw(EL_BASE_ADDR + VX_W1_FREE_TX) < length + pad + 4) { + /* no room in FIFO */ + if (dummy == 0) + { + PRINTK("No room in FIFO, waiting...\n"); + dummy++; + } + + } + + PRINTK(" ---> FIFO ready\n"); + + + outw(length, EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); + + /* Second dword meaningless */ + outw(0x0, EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); + +#if EL_DEBUG > 1 + print_packet((byte *)buf, length); +#endif + + /* write packet */ + { + unsigned int i, totw; + + totw = ((length + 1) >> 1); + PRINTK("Buffer: (totw = %d)\n", totw); + for (i = 0; i < totw; i++) { + outw( *(buf+i), EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); + udelay(10); + } + if(totw & 1) + { /* pad to double word length */ + outw( 0, EL_BASE_ADDR + VX_W1_TX_PIO_WR_1); + udelay(10); + } + PRINTK("\n\n"); + } + + /* wait for Tx complete */ + PRINTK("Waiting for Tx to complete...\n"); + while(((status = inw(EL_BASE_ADDR + VX_STATUS)) & S_COMMAND_IN_PROGRESS) != 0) + { + udelay(10); + } + PRINTK(" ---> Tx completed, status = 0x%04x\n", status); + + return length; +} diff --git a/roms/u-boot-sam460ex/drivers/net/3c589.h b/roms/u-boot-sam460ex/drivers/net/3c589.h new file mode 100644 index 000000000..8f8cf5be1 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/3c589.h @@ -0,0 +1,435 @@ +/* + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved. + * + * 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. The name + * of the author may not be used to endorse or promote products derived from + * this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + October 2, 1994 + + Modified by: Andres Vega Garcia + + INRIA - Sophia Antipolis, France + e-mail: avega@sophia.inria.fr + finger: avega@pax.inria.fr + + */ + +/* + * Created from if_epreg.h by Fred Gray (fgray@rice.edu) to support the + * 3c590 family. + */ + +/* + * Modified by Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp> + * for etherboot + * Mar. 14, 2000 +*/ + +/* + * Ethernet software status per interface. + */ + +/* + * Some global constants + */ + +#define TX_INIT_RATE 16 +#define TX_INIT_MAX_RATE 64 +#define RX_INIT_LATENCY 64 +#define RX_INIT_EARLY_THRESH 64 +#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */ +#define MIN_RX_EARLY_THRESHL 4 + +#define EEPROMSIZE 0x40 +#define MAX_EEPROMBUSY 1000 +#define VX_LAST_TAG 0xd7 +#define VX_MAX_BOARDS 16 +#define VX_ID_PORT 0x100 + +/* + * some macros to acces long named fields + */ +#define BASE (EL_BASE_ADDR) + +/* + * Commands to read/write EEPROM trough EEPROM command register (Window 0, + * Offset 0xa) + */ +#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ +#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ +#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ +#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ + +#define EEPROM_BUSY (1<<15) + +/* + * Some short functions, worth to let them be a macro + */ + +/************************************************************************** + * * + * These define the EEPROM data structure. They are used in the probe + * function to verify the existence of the adapter after having sent + * the ID_Sequence. + * + * There are others but only the ones we use are defined here. + * + **************************************************************************/ + +#define EEPROM_NODE_ADDR_0 0x0 /* Word */ +#define EEPROM_NODE_ADDR_1 0x1 /* Word */ +#define EEPROM_NODE_ADDR_2 0x2 /* Word */ +#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ +#define EEPROM_MFG_ID 0x7 /* 0x6d50 */ +#define EEPROM_ADDR_CFG 0x8 /* Base addr */ +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ +#define EEPROM_OEM_ADDR_0 0xa /* Word */ +#define EEPROM_OEM_ADDR_1 0xb /* Word */ +#define EEPROM_OEM_ADDR_2 0xc /* Word */ +#define EEPROM_SOFT_INFO_2 0xf /* Software information 2 */ + +#define NO_RX_OVN_ANOMALY (1<<5) + +/************************************************************************** + * * + * These are the registers for the 3Com 3c509 and their bit patterns when * + * applicable. They have been taken out the the "EtherLink III Parallel * + * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual * + * from 3com. * + * * + **************************************************************************/ + +#define VX_COMMAND 0x0e /* Write. BASE+0x0e is always a + * command reg. */ +#define VX_STATUS 0x0e /* Read. BASE+0x0e is always status + * reg. */ +#define VX_WINDOW 0x0f /* Read. BASE+0x0f is always window + * reg. */ +/* + * Window 0 registers. Setup. + */ +/* Write */ +#define VX_W0_EEPROM_DATA 0x0c +#define VX_W0_EEPROM_COMMAND 0x0a +#define VX_W0_RESOURCE_CFG 0x08 +#define VX_W0_ADDRESS_CFG 0x06 +#define VX_W0_CONFIG_CTRL 0x04 + /* Read */ +#define VX_W0_PRODUCT_ID 0x02 +#define VX_W0_MFG_ID 0x00 + + +/* + * Window 1 registers. Operating Set. + */ +/* Write */ +#define VX_W1_TX_PIO_WR_2 0x02 +#define VX_W1_TX_PIO_WR_1 0x00 +/* Read */ +#define VX_W1_FREE_TX 0x0c +#define VX_W1_TX_STATUS 0x0b /* byte */ +#define VX_W1_TIMER 0x0a /* byte */ +#define VX_W1_RX_STATUS 0x08 +#define VX_W1_RX_PIO_RD_2 0x02 +#define VX_W1_RX_PIO_RD_1 0x00 + +/* + * Window 2 registers. Station Address Setup/Read + */ +/* Read/Write */ +#define VX_W2_ADDR_5 0x05 +#define VX_W2_ADDR_4 0x04 +#define VX_W2_ADDR_3 0x03 +#define VX_W2_ADDR_2 0x02 +#define VX_W2_ADDR_1 0x01 +#define VX_W2_ADDR_0 0x00 + +/* + * Window 3 registers. FIFO Management. + */ +/* Read */ +#define VX_W3_INTERNAL_CFG 0x00 +#define VX_W3_RESET_OPT 0x08 +#define VX_W3_FREE_TX 0x0c +#define VX_W3_FREE_RX 0x0a + +/* + * Window 4 registers. Diagnostics. + */ +/* Read/Write */ +#define VX_W4_MEDIA_TYPE 0x0a +#define VX_W4_CTRLR_STATUS 0x08 +#define VX_W4_NET_DIAG 0x06 +#define VX_W4_FIFO_DIAG 0x04 +#define VX_W4_HOST_DIAG 0x02 +#define VX_W4_TX_DIAG 0x00 + +/* + * Window 5 Registers. Results and Internal status. + */ +/* Read */ +#define VX_W5_READ_0_MASK 0x0c +#define VX_W5_INTR_MASK 0x0a +#define VX_W5_RX_FILTER 0x08 +#define VX_W5_RX_EARLY_THRESH 0x06 +#define VX_W5_TX_AVAIL_THRESH 0x02 +#define VX_W5_TX_START_THRESH 0x00 + +/* + * Window 6 registers. Statistics. + */ +/* Read/Write */ +#define TX_TOTAL_OK 0x0c +#define RX_TOTAL_OK 0x0a +#define TX_DEFERRALS 0x08 +#define RX_FRAMES_OK 0x07 +#define TX_FRAMES_OK 0x06 +#define RX_OVERRUNS 0x05 +#define TX_COLLISIONS 0x04 +#define TX_AFTER_1_COLLISION 0x03 +#define TX_AFTER_X_COLLISIONS 0x02 +#define TX_NO_SQE 0x01 +#define TX_CD_LOST 0x00 + +/**************************************** + * + * Register definitions. + * + ****************************************/ + +/* + * Command register. All windows. + * + * 16 bit register. + * 15-11: 5-bit code for command to be executed. + * 10-0: 11-bit arg if any. For commands with no args; + * this can be set to anything. + */ +#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms + * after issuing */ +#define WINDOW_SELECT (unsigned short) (0x1<<11) +#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to + * determine whether + * this is needed. If + * so; wait 800 uSec + * before using trans- + * ceiver. */ +#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on + * power-up */ +#define RX_ENABLE (unsigned short) (0x4<<11) +#define RX_RESET (unsigned short) (0x5<<11) +#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11) +#define TX_ENABLE (unsigned short) (0x9<<11) +#define TX_DISABLE (unsigned short) (0xa<<11) +#define TX_RESET (unsigned short) (0xb<<11) +#define REQ_INTR (unsigned short) (0xc<<11) +/* + * The following C_* acknowledge the various interrupts. Some of them don't + * do anything. See the manual. + */ +#define ACK_INTR (unsigned short) (0x6800) +# define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1) +# define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2) +# define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4) +# define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8) +# define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10) +# define C_RX_EARLY (unsigned short) (ACK_INTR|0x20) +# define C_INT_RQD (unsigned short) (ACK_INTR|0x40) +# define C_UPD_STATS (unsigned short) (ACK_INTR|0x80) +#define SET_INTR_MASK (unsigned short) (0xe<<11) +#define SET_RD_0_MASK (unsigned short) (0xf<<11) +#define SET_RX_FILTER (unsigned short) (0x10<<11) +# define FIL_INDIVIDUAL (unsigned short) (0x1) +# define FIL_MULTICAST (unsigned short) (0x02) +# define FIL_BRDCST (unsigned short) (0x04) +# define FIL_PROMISC (unsigned short) (0x08) +#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11) +#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11) +#define SET_TX_START_THRESH (unsigned short) (0x13<<11) +#define STATS_ENABLE (unsigned short) (0x15<<11) +#define STATS_DISABLE (unsigned short) (0x16<<11) +#define STOP_TRANSCEIVER (unsigned short) (0x17<<11) + +/* + * Status register. All windows. + * + * 15-13: Window number(0-7). + * 12: Command_in_progress. + * 11: reserved. + * 10: reserved. + * 9: reserved. + * 8: reserved. + * 7: Update Statistics. + * 6: Interrupt Requested. + * 5: RX Early. + * 4: RX Complete. + * 3: TX Available. + * 2: TX Complete. + * 1: Adapter Failure. + * 0: Interrupt Latch. + */ +#define S_INTR_LATCH (unsigned short) (0x1) +#define S_CARD_FAILURE (unsigned short) (0x2) +#define S_TX_COMPLETE (unsigned short) (0x4) +#define S_TX_AVAIL (unsigned short) (0x8) +#define S_RX_COMPLETE (unsigned short) (0x10) +#define S_RX_EARLY (unsigned short) (0x20) +#define S_INT_RQD (unsigned short) (0x40) +#define S_UPD_STATS (unsigned short) (0x80) +#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000) + +#define VX_BUSY_WAIT while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) + +/* Address Config. Register. + * Window 0/Port 06 + */ + +#define ACF_CONNECTOR_BITS 14 +#define ACF_CONNECTOR_UTP 0 +#define ACF_CONNECTOR_AUI 1 +#define ACF_CONNECTOR_BNC 3 + +#define INTERNAL_CONNECTOR_BITS 20 +#define INTERNAL_CONNECTOR_MASK 0x01700000 + +/* + * FIFO Registers. RX Status. + * + * 15: Incomplete or FIFO empty. + * 14: 1: Error in RX Packet 0: Incomplete or no error. + * 13-11: Type of error. + * 1000 = Overrun. + * 1011 = Run Packet Error. + * 1100 = Alignment Error. + * 1101 = CRC Error. + * 1001 = Oversize Packet Error (>1514 bytes) + * 0010 = Dribble Bits. + * (all other error codes, no errors.) + * + * 10-0: RX Bytes (0-1514) + */ +#define ERR_INCOMPLETE (unsigned short) (0x8000) +#define ERR_RX (unsigned short) (0x4000) +#define ERR_MASK (unsigned short) (0x7800) +#define ERR_OVERRUN (unsigned short) (0x4000) +#define ERR_RUNT (unsigned short) (0x5800) +#define ERR_ALIGNMENT (unsigned short) (0x6000) +#define ERR_CRC (unsigned short) (0x6800) +#define ERR_OVERSIZE (unsigned short) (0x4800) +#define ERR_DRIBBLE (unsigned short) (0x1000) + +/* + * TX Status. + * + * Reports the transmit status of a completed transmission. Writing this + * register pops the transmit completion stack. + * + * Window 1/Port 0x0b. + * + * 7: Complete + * 6: Interrupt on successful transmission requested. + * 5: Jabber Error (TP Only, TX Reset required. ) + * 4: Underrun (TX Reset required. ) + * 3: Maximum Collisions. + * 2: TX Status Overflow. + * 1-0: Undefined. + * + */ +#define TXS_COMPLETE 0x80 +#define TXS_INTR_REQ 0x40 +#define TXS_JABBER 0x20 +#define TXS_UNDERRUN 0x10 +#define TXS_MAX_COLLISION 0x8 +#define TXS_STATUS_OVERFLOW 0x4 + +#define RS_AUI (1<<5) +#define RS_BNC (1<<4) +#define RS_UTP (1<<3) +#define RS_T4 (1<<0) +#define RS_TX (1<<1) +#define RS_FX (1<<2) +#define RS_MII (1<<6) + + +/* + * FIFO Status (Window 4) + * + * Supports FIFO diagnostics + * + * Window 4/Port 0x04.1 + * + * 15: 1=RX receiving (RO). Set when a packet is being received + * into the RX FIFO. + * 14: Reserved + * 13: 1=RX underrun (RO). Generates Adapter Failure interrupt. + * Requires RX Reset or Global Reset command to recover. + * It is generated when you read past the end of a packet - + * reading past what has been received so far will give bad + * data. + * 12: 1=RX status overrun (RO). Set when there are already 8 + * packets in the RX FIFO. While this bit is set, no additional + * packets are received. Requires no action on the part of + * the host. The condition is cleared once a packet has been + * read out of the RX FIFO. + * 11: 1=RX overrun (RO). Set when the RX FIFO is full (there + * may not be an overrun packet yet). While this bit is set, + * no additional packets will be received (some additional + * bytes can still be pending between the wire and the RX + * FIFO). Requires no action on the part of the host. The + * condition is cleared once a few bytes have been read out + * from the RX FIFO. + * 10: 1=TX overrun (RO). Generates adapter failure interrupt. + * Requires TX Reset or Global Reset command to recover. + * Disables Transmitter. + * 9-8: Unassigned. + * 7-0: Built in self test bits for the RX and TX FIFO's. + */ +#define FIFOS_RX_RECEIVING (unsigned short) 0x8000 +#define FIFOS_RX_UNDERRUN (unsigned short) 0x2000 +#define FIFOS_RX_STATUS_OVERRUN (unsigned short) 0x1000 +#define FIFOS_RX_OVERRUN (unsigned short) 0x0800 +#define FIFOS_TX_OVERRUN (unsigned short) 0x0400 + +/* + * Misc defines for various things. + */ +#define TAG_ADAPTER 0xd0 +#define ACTIVATE_ADAPTER_TO_CONFIG 0xff +#define ENABLE_DRQ_IRQ 0x0001 +#define MFG_ID 0x506d /* `TCM' */ +#define PROD_ID 0x5090 +#define GO_WINDOW(x) outw(WINDOW_SELECT|(x),BASE+VX_COMMAND) +#define JABBER_GUARD_ENABLE 0x40 +#define LINKBEAT_ENABLE 0x80 +#define ENABLE_UTP (JABBER_GUARD_ENABLE | LINKBEAT_ENABLE) +#define DISABLE_UTP 0x0 +#define RX_BYTES_MASK (unsigned short) (0x07ff) +#define RX_ERROR 0x4000 +#define RX_INCOMPLETE 0x8000 +#define TX_INDICATE 1<<15 +#define is_eeprom_busy(b) (inw((b)+VX_W0_EEPROM_COMMAND)&EEPROM_BUSY) + +#define VX_IOSIZE 0x20 + +#define VX_CONNECTORS 8 + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/roms/u-boot-sam460ex/drivers/net/4xx_enet.c b/roms/u-boot-sam460ex/drivers/net/4xx_enet.c new file mode 100644 index 000000000..2fac64167 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/4xx_enet.c @@ -0,0 +1,2081 @@ +/*-----------------------------------------------------------------------------+ + * This source code is dual-licensed. You may use it under the terms of the + * GNU General Public License version 2, or under the license below. + * + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. + * + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. + * + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. + * + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + *-----------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------+ + * + * File Name: enetemac.c + * + * Function: Device driver for the ethernet EMAC3 macro on the 405GP. + * + * Author: Mark Wisner + * + * Change Activity- + * + * Date Description of Change BY + * --------- --------------------- --- + * 05-May-99 Created MKW + * 27-Jun-99 Clean up JWB + * 16-Jul-99 Added MAL error recovery and better IP packet handling MKW + * 29-Jul-99 Added Full duplex support MKW + * 06-Aug-99 Changed names for Mal CR reg MKW + * 23-Aug-99 Turned off SYE when running at 10Mbs MKW + * 24-Aug-99 Marked descriptor empty after call_xlc MKW + * 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG + * to avoid chaining maximum sized packets. Push starting + * RX descriptor address up to the next cache line boundary. + * 16-Jan-00 Added support for booting with IP of 0x0 MKW + * 15-Mar-00 Updated enetInit() to enable broadcast addresses in the + * EMAC0_RXM register. JWB + * 12-Mar-01 anne-sophie.harnois@nextream.fr + * - Variables are compatible with those already defined in + * include/net.h + * - Receive buffer descriptor ring is used to send buffers + * to the user + * - Info print about send/received/handled packet number if + * INFO_405_ENET is set + * 17-Apr-01 stefan.roese@esd-electronics.com + * - MAL reset in "eth_halt" included + * - Enet speed and duplex output now in one line + * 08-May-01 stefan.roese@esd-electronics.com + * - MAL error handling added (eth_init called again) + * 13-Nov-01 stefan.roese@esd-electronics.com + * - Set IST bit in EMAC0_MR1 reg upon 100MBit or full duplex + * 04-Jan-02 stefan.roese@esd-electronics.com + * - Wait for PHY auto negotiation to complete added + * 06-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in waiting for auto negotiation to complete + * 26-Feb-02 stefan.roese@esd-electronics.com + * - rx and tx buffer descriptors now allocated (no fixed address + * used anymore) + * 17-Jun-02 stefan.roese@esd-electronics.com + * - MAL error debug printf 'M' removed (rx de interrupt may + * occur upon many incoming packets with only 4 rx buffers). + *-----------------------------------------------------------------------------* + * 17-Nov-03 travis.sawyer@sandburst.com + * - ported from 405gp_enet.c to utilized upto 4 EMAC ports + * in the 440GX. This port should work with the 440GP + * (2 EMACs) also + * 15-Aug-05 sr@denx.de + * - merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c + now handling all 4xx cpu's. + *-----------------------------------------------------------------------------*/ + +#include <config.h> +#include <common.h> +#include <net.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/cache.h> +#include <asm/mmu.h> +#include <commproc.h> +#include <ppc4xx.h> +#include <ppc4xx_enet.h> +#include <405_mal.h> +#include <miiphy.h> +#include <malloc.h> + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_NET_MULTI) +#error "CONFIG_NET_MULTI has to be defined for NetConsole" +#endif + +#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */ +#define PHY_AUTONEGOTIATE_TIMEOUT 5000 /* 5000 ms autonegotiate timeout */ + +/* Ethernet Transmit and Receive Buffers */ +/* AS.HARNOIS + * In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from + * PKTSIZE and PKTSIZE_ALIGN (include/net.h) + */ +#define ENET_MAX_MTU PKTSIZE +#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN + +/*-----------------------------------------------------------------------------+ + * Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal + * Interrupt Controller). + *-----------------------------------------------------------------------------*/ +#define ETH_IRQ_NUM(dev) (VECNUM_ETH0 + ((dev) * VECNUM_ETH1_OFFS)) + +#if defined(CONFIG_HAS_ETH3) +#if !defined(CONFIG_440GX) +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1)) || \ + UIC_MASK(ETH_IRQ_NUM(2)) || UIC_MASK(ETH_IRQ_NUM(3))) +#else +/* Unfortunately 440GX spreads EMAC interrupts on multiple UIC's */ +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1))) +#define UIC_ETHxB (UIC_MASK(ETH_IRQ_NUM(2)) || UIC_MASK(ETH_IRQ_NUM(3))) +#endif /* !defined(CONFIG_440GX) */ +#elif defined(CONFIG_HAS_ETH2) +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1)) || \ + UIC_MASK(ETH_IRQ_NUM(2))) +#elif defined(CONFIG_HAS_ETH1) +#define UIC_ETHx (UIC_MASK(ETH_IRQ_NUM(0)) || UIC_MASK(ETH_IRQ_NUM(1))) +#else +#define UIC_ETHx UIC_MASK(ETH_IRQ_NUM(0)) +#endif + +/* + * Define a default version for UIC_ETHxB for non 440GX so that we can + * use common code for all 4xx variants + */ +#if !defined(UIC_ETHxB) +#define UIC_ETHxB 0 +#endif + +#define UIC_MAL_SERR UIC_MASK(VECNUM_MAL_SERR) +#define UIC_MAL_TXDE UIC_MASK(VECNUM_MAL_TXDE) +#define UIC_MAL_RXDE UIC_MASK(VECNUM_MAL_RXDE) +#define UIC_MAL_TXEOB UIC_MASK(VECNUM_MAL_TXEOB) +#define UIC_MAL_RXEOB UIC_MASK(VECNUM_MAL_RXEOB) + +#define MAL_UIC_ERR (UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE) +#define MAL_UIC_DEF (UIC_MAL_RXEOB | MAL_UIC_ERR) + +/* + * We have 3 different interrupt types: + * - MAL interrupts indicating successful transfer + * - MAL error interrupts indicating MAL related errors + * - EMAC interrupts indicating EMAC related errors + * + * All those interrupts can be on different UIC's, but since + * now at least all interrupts from one type are on the same + * UIC. Only exception is 440GX where the EMAC interrupts are + * spread over two UIC's! + */ +#if defined(CONFIG_440GX) +#define UIC_BASE_MAL UIC1_DCR_BASE +#define UIC_BASE_MAL_ERR UIC2_DCR_BASE +#define UIC_BASE_EMAC UIC2_DCR_BASE +#define UIC_BASE_EMAC_B UIC3_DCR_BASE +#else +#define UIC_BASE_MAL (UIC0_DCR_BASE + (UIC_NR(VECNUM_MAL_TXEOB) * 0x10)) +#define UIC_BASE_MAL_ERR (UIC0_DCR_BASE + (UIC_NR(VECNUM_MAL_SERR) * 0x10)) +#define UIC_BASE_EMAC (UIC0_DCR_BASE + (UIC_NR(ETH_IRQ_NUM(0)) * 0x10)) +#define UIC_BASE_EMAC_B (UIC0_DCR_BASE + (UIC_NR(ETH_IRQ_NUM(0)) * 0x10)) +#endif + +#undef INFO_4XX_ENET + +#define BI_PHYMODE_NONE 0 +#define BI_PHYMODE_ZMII 1 +#define BI_PHYMODE_RGMII 2 +#define BI_PHYMODE_GMII 3 +#define BI_PHYMODE_RTBI 4 +#define BI_PHYMODE_TBI 5 +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) +#define BI_PHYMODE_SMII 6 +#define BI_PHYMODE_MII 7 +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define BI_PHYMODE_RMII 8 +#endif +#endif +#define BI_PHYMODE_SGMII 9 + +#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) +#define SDR0_MFR_ETH_CLK_SEL_V(n) ((0x01<<27) / (n+1)) +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define SDR0_ETH_CFG_CLK_SEL_V(n) (0x01 << (8 + n)) +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +#define MAL_RX_CHAN_MUL 8 /* 460EX/GT uses MAL channel 8 for EMAC1 */ +#else +#define MAL_RX_CHAN_MUL 1 +#endif + +/*--------------------------------------------------------------------+ + * Fixed PHY (PHY-less) support for Ethernet Ports. + *--------------------------------------------------------------------*/ + +/* + * Some boards do not have a PHY for each ethernet port. These ports + * are known as Fixed PHY (or PHY-less) ports. For such ports, set + * the appropriate CONFIG_PHY_ADDR equal to CONFIG_FIXED_PHY and + * then define CONFIG_SYS_FIXED_PHY_PORTS to define what the speed and + * duplex should be for these ports in the board configuration + * file. + * + * For Example: + * #define CONFIG_FIXED_PHY 0xFFFFFFFF + * + * #define CONFIG_PHY_ADDR CONFIG_FIXED_PHY + * #define CONFIG_PHY1_ADDR 1 + * #define CONFIG_PHY2_ADDR CONFIG_FIXED_PHY + * #define CONFIG_PHY3_ADDR 3 + * + * #define CONFIG_SYS_FIXED_PHY_PORT(devnum,speed,duplex) \ + * {devnum, speed, duplex}, + * + * #define CONFIG_SYS_FIXED_PHY_PORTS \ + * CONFIG_SYS_FIXED_PHY_PORT(0,1000,FULL) \ + * CONFIG_SYS_FIXED_PHY_PORT(2,100,HALF) + */ + +#ifndef CONFIG_FIXED_PHY +#define CONFIG_FIXED_PHY 0xFFFFFFFF /* Fixed PHY (PHY-less) */ +#endif + +#ifndef CONFIG_SYS_FIXED_PHY_PORTS +#define CONFIG_SYS_FIXED_PHY_PORTS /* default is an empty array */ +#endif + +struct fixed_phy_port { + unsigned int devnum; /* ethernet port */ + unsigned int speed; /* specified speed 10,100 or 1000 */ + unsigned int duplex; /* specified duplex FULL or HALF */ +}; + +static const struct fixed_phy_port fixed_phy_port[] = { + CONFIG_SYS_FIXED_PHY_PORTS /* defined in board configuration file */ +}; + +/*-----------------------------------------------------------------------------+ + * Global variables. TX and RX descriptors and buffers. + *-----------------------------------------------------------------------------*/ + +/* + * Get count of EMAC devices (doesn't have to be the max. possible number + * supported by the cpu) + * + * CONFIG_BOARD_EMAC_COUNT added so now a "dynamic" way to configure the + * EMAC count is possible. As it is needed for the Kilauea/Haleakala + * 405EX/405EXr eval board, using the same binary. + */ +#if defined(CONFIG_BOARD_EMAC_COUNT) +#define LAST_EMAC_NUM board_emac_count() +#else /* CONFIG_BOARD_EMAC_COUNT */ +#if defined(CONFIG_HAS_ETH3) +#define LAST_EMAC_NUM 4 +#elif defined(CONFIG_HAS_ETH2) +#define LAST_EMAC_NUM 3 +#elif defined(CONFIG_HAS_ETH1) +#define LAST_EMAC_NUM 2 +#else +#define LAST_EMAC_NUM 1 +#endif +#endif /* CONFIG_BOARD_EMAC_COUNT */ + +/* normal boards start with EMAC0 */ +#if !defined(CONFIG_EMAC_NR_START) +#define CONFIG_EMAC_NR_START 0 +#endif + +#define MAL_RX_DESC_SIZE 2048 +#define MAL_TX_DESC_SIZE 2048 +#define MAL_ALLOC_SIZE (MAL_TX_DESC_SIZE + MAL_RX_DESC_SIZE) + +/*-----------------------------------------------------------------------------+ + * Prototypes and externals. + *-----------------------------------------------------------------------------*/ +static void enet_rcv (struct eth_device *dev, unsigned long malisr); + +int enetInt (struct eth_device *dev); +static void mal_err (struct eth_device *dev, unsigned long isr, + unsigned long uic, unsigned long maldef, + unsigned long mal_errr); +static void emac_err (struct eth_device *dev, unsigned long isr); + +extern int phy_setup_aneg (char *devname, unsigned char addr); +extern int emac4xx_miiphy_read (char *devname, unsigned char addr, + unsigned char reg, unsigned short *value); +extern int emac4xx_miiphy_write (char *devname, unsigned char addr, + unsigned char reg, unsigned short value); + +int board_emac_count(void); + +static void emac_loopback_enable(EMAC_4XX_HW_PST hw_p) +{ +#if defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_405EX) + u32 val; + + mfsdr(SDR0_MFR, val); + val |= SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_MFR, val); +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) + u32 val; + + mfsdr(SDR0_ETH_CFG, val); + val |= SDR0_ETH_CFG_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_ETH_CFG, val); +#endif +} + +static void emac_loopback_disable(EMAC_4XX_HW_PST hw_p) +{ +#if defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_405EX) + u32 val; + + mfsdr(SDR0_MFR, val); + val &= ~SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_MFR, val); +#elif defined(CONFIG_460EX) || defined(CONFIG_460GT) + u32 val; + + mfsdr(SDR0_ETH_CFG, val); + val &= ~SDR0_ETH_CFG_CLK_SEL_V(hw_p->devnum); + mtsdr(SDR0_ETH_CFG, val); +#endif +} + +/*-----------------------------------------------------------------------------+ +| ppc_4xx_eth_halt +| Disable MAL channel, and EMACn ++-----------------------------------------------------------------------------*/ +static void ppc_4xx_eth_halt (struct eth_device *dev) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + u32 val = 10000; + + out_be32((void *)EMAC0_IER + hw_p->hw_addr, 0x00000000); /* disable emac interrupts */ + + /* 1st reset MAL channel */ + /* Note: writing a 0 to a channel has no effect */ +#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + mtdcr (MAL0_TXCARR, (MAL_CR_MMSR >> (hw_p->devnum * 2))); +#else + mtdcr (MAL0_TXCARR, (MAL_CR_MMSR >> hw_p->devnum)); +#endif + mtdcr (MAL0_RXCARR, (MAL_CR_MMSR >> hw_p->devnum)); + + /* wait for reset */ + while (mfdcr (MAL0_RXCASR) & (MAL_CR_MMSR >> hw_p->devnum)) { + udelay (1000); /* Delay 1 MS so as not to hammer the register */ + val--; + if (val == 0) + break; + } + + /* provide clocks for EMAC internal loopback */ + emac_loopback_enable(hw_p); + + /* EMAC RESET */ + out_be32((void *)EMAC0_MR0 + hw_p->hw_addr, EMAC_MR0_SRST); + + /* remove clocks for EMAC internal loopback */ + emac_loopback_disable(hw_p); + +#ifndef CONFIG_NETCONSOLE + hw_p->print_speed = 1; /* print speed message again next time */ +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + /* don't bypass the TAHOE0/TAHOE1 cores for Linux */ + mfsdr(SDR0_ETH_CFG, val); + val &= ~(SDR0_ETH_CFG_TAHOE0_BYPASS | SDR0_ETH_CFG_TAHOE1_BYPASS); + mtsdr(SDR0_ETH_CFG, val); +#endif + + return; +} + +#if defined (CONFIG_440GX) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + unsigned long pfc1; + unsigned long zmiifer; + unsigned long rmiifer; + + mfsdr(SDR0_PFC1, pfc1); + pfc1 = SDR0_PFC1_EPS_DECODE(pfc1); + + zmiifer = 0; + rmiifer = 0; + + switch (pfc1) { + case 1: + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + case 2: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + case 3: + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 4: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 5: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 6: + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + break; + case 0: + default: + zmiifer = ZMII_FER_MII << ZMII_FER_V(devnum); + rmiifer = 0x0; + bis->bi_phymode[0] = BI_PHYMODE_ZMII; + bis->bi_phymode[1] = BI_PHYMODE_ZMII; + bis->bi_phymode[2] = BI_PHYMODE_ZMII; + bis->bi_phymode[3] = BI_PHYMODE_ZMII; + break; + } + + /* Ensure we setup mdio for this devnum and ONLY this devnum */ + zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum); + + out_be32((void *)ZMII0_FER, zmiifer); + out_be32((void *)RGMII_FER, rmiifer); + + return ((int)pfc1); +} +#endif /* CONFIG_440_GX */ + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + unsigned long zmiifer=0x0; + unsigned long pfc1; + + mfsdr(SDR0_PFC1, pfc1); + pfc1 &= SDR0_PFC1_SELECT_MASK; + + switch (pfc1) { + case SDR0_PFC1_SELECT_CONFIG_2: + /* 1 x GMII port */ + out_be32((void *)ZMII0_FER, 0x00); + out_be32((void *)RGMII_FER, 0x00000037); + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case SDR0_PFC1_SELECT_CONFIG_4: + /* 2 x RGMII ports */ + out_be32((void *)ZMII0_FER, 0x00); + out_be32((void *)RGMII_FER, 0x00000055); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + break; + case SDR0_PFC1_SELECT_CONFIG_6: + /* 2 x SMII ports */ + out_be32((void *)ZMII0_FER, + ((ZMII_FER_SMII) << ZMII_FER_V(0)) | + ((ZMII_FER_SMII) << ZMII_FER_V(1))); + out_be32((void *)RGMII_FER, 0x00000000); + bis->bi_phymode[0] = BI_PHYMODE_SMII; + bis->bi_phymode[1] = BI_PHYMODE_SMII; + break; + case SDR0_PFC1_SELECT_CONFIG_1_2: + /* only 1 x MII supported */ + out_be32((void *)ZMII0_FER, (ZMII_FER_MII) << ZMII_FER_V(0)); + out_be32((void *)RGMII_FER, 0x00000000); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + default: + break; + } + + /* Ensure we setup mdio for this devnum and ONLY this devnum */ + zmiifer = in_be32((void *)ZMII0_FER); + zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum); + out_be32((void *)ZMII0_FER, zmiifer); + + return ((int)0x0); +} +#endif /* CONFIG_440EPX */ + +#if defined(CONFIG_405EX) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + u32 rgmiifer = 0; + + /* + * The 405EX(r)'s RGMII bridge can operate in one of several + * modes, only one of which (2 x RGMII) allows the + * simultaneous use of both EMACs on the 405EX. + */ + + switch (CONFIG_EMAC_PHY_MODE) { + + case EMAC_PHY_MODE_NONE: + /* No ports */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_NONE_RGMII: + /* 1 x RGMII port on channel 0 */ + rgmiifer |= RGMII_FER_RGMII << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_RGMII_NONE: + /* 1 x RGMII port on channel 1 */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_RGMII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + break; + case EMAC_PHY_MODE_RGMII_RGMII: + /* 2 x RGMII ports */ + rgmiifer |= RGMII_FER_RGMII << 0; + rgmiifer |= RGMII_FER_RGMII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + break; + case EMAC_PHY_MODE_NONE_GMII: + /* 1 x GMII port on channel 0 */ + rgmiifer |= RGMII_FER_GMII << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_NONE_MII: + /* 1 x MII port on channel 0 */ + rgmiifer |= RGMII_FER_MII << 0; + rgmiifer |= RGMII_FER_DIS << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + break; + case EMAC_PHY_MODE_GMII_NONE: + /* 1 x GMII port on channel 1 */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_GMII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_GMII; + break; + case EMAC_PHY_MODE_MII_NONE: + /* 1 x MII port on channel 1 */ + rgmiifer |= RGMII_FER_DIS << 0; + rgmiifer |= RGMII_FER_MII << 4; + out_be32((void *)RGMII_FER, rgmiifer); + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_MII; + break; + default: + break; + } + + /* Ensure we setup mdio for this devnum and ONLY this devnum */ + rgmiifer = in_be32((void *)RGMII_FER); + rgmiifer |= (1 << (19-devnum)); + out_be32((void *)RGMII_FER, rgmiifer); + + return ((int)0x0); +} +#endif /* CONFIG_405EX */ + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) +int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis) +{ + u32 eth_cfg; + u32 zmiifer; /* ZMII0_FER reg. */ + u32 rmiifer; /* RGMII0_FER reg. Bridge 0 */ + u32 rmiifer1; /* RGMII0_FER reg. Bridge 1 */ + int mode; + + zmiifer = 0; + rmiifer = 0; + rmiifer1 = 0; + +#if defined(CONFIG_460EX) + mode = 9; + mfsdr(SDR0_ETH_CFG, eth_cfg); + if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) && + ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0)) + mode = 11; /* config SGMII */ +#else + mode = 10; + mfsdr(SDR0_ETH_CFG, eth_cfg); + if (((eth_cfg & SDR0_ETH_CFG_SGMII0_ENABLE) > 0) && + ((eth_cfg & SDR0_ETH_CFG_SGMII1_ENABLE) > 0) && + ((eth_cfg & SDR0_ETH_CFG_SGMII2_ENABLE) > 0)) + mode = 12; /* config SGMII */ +#endif + + /* TODO: + * NOTE: 460GT has 2 RGMII bridge cores: + * emac0 ------ RGMII0_BASE + * | + * emac1 -----+ + * + * emac2 ------ RGMII1_BASE + * | + * emac3 -----+ + * + * 460EX has 1 RGMII bridge core: + * and RGMII1_BASE is disabled + * emac0 ------ RGMII0_BASE + * | + * emac1 -----+ + */ + + /* + * Right now only 2*RGMII is supported. Please extend when needed. + * sr - 2008-02-19 + * Add SGMII support. + * vg - 2008-07-28 + */ + switch (mode) { + case 1: + /* 1 MII - 460EX */ + /* GMC0 EMAC4_0, ZMII Bridge */ + zmiifer |= ZMII_FER_MII << ZMII_FER_V(0); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 2: + /* 2 MII - 460GT */ + /* GMC0 EMAC4_0, GMC1 EMAC4_2, ZMII Bridge */ + zmiifer |= ZMII_FER_MII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_MII << ZMII_FER_V(2); + bis->bi_phymode[0] = BI_PHYMODE_MII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_MII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 3: + /* 2 RMII - 460EX */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, ZMII Bridge */ + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); + bis->bi_phymode[0] = BI_PHYMODE_RMII; + bis->bi_phymode[1] = BI_PHYMODE_RMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 4: + /* 4 RMII - 460GT */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, GMC1 EMAC4_2, GMC1, EMAC4_3 */ + /* ZMII Bridge */ + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_RMII; + bis->bi_phymode[1] = BI_PHYMODE_RMII; + bis->bi_phymode[2] = BI_PHYMODE_RMII; + bis->bi_phymode[3] = BI_PHYMODE_RMII; + break; + case 5: + /* 2 SMII - 460EX */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, ZMII Bridge */ + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + bis->bi_phymode[0] = BI_PHYMODE_SMII; + bis->bi_phymode[1] = BI_PHYMODE_SMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 6: + /* 4 SMII - 460GT */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, GMC0 EMAC4_3, GMC0 EMAC4_3 */ + /* ZMII Bridge */ + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(2); + zmiifer |= ZMII_FER_SMII << ZMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_SMII; + bis->bi_phymode[1] = BI_PHYMODE_SMII; + bis->bi_phymode[2] = BI_PHYMODE_SMII; + bis->bi_phymode[3] = BI_PHYMODE_SMII; + break; + case 7: + /* This is the default mode that we want for board bringup - Maple */ + /* 1 GMII - 460EX */ + /* GMC0 EMAC4_0, RGMII Bridge 0 */ + rmiifer |= RGMII_FER_MDIO(0); + + if (devnum == 0) { + rmiifer |= RGMII_FER_GMII << RGMII_FER_V(2); /* CH0CFG - EMAC0 */ + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + } else { + rmiifer |= RGMII_FER_GMII << RGMII_FER_V(3); /* CH1CFG - EMAC1 */ + bis->bi_phymode[0] = BI_PHYMODE_NONE; + bis->bi_phymode[1] = BI_PHYMODE_GMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + } + break; + case 8: + /* 2 GMII - 460GT */ + /* GMC0 EMAC4_0, RGMII Bridge 0 */ + /* GMC1 EMAC4_2, RGMII Bridge 1 */ + rmiifer |= RGMII_FER_GMII << RGMII_FER_V(2); /* CH0CFG - EMAC0 */ + rmiifer1 |= RGMII_FER_GMII << RGMII_FER_V(2); /* CH0CFG - EMAC2 */ + rmiifer |= RGMII_FER_MDIO(0); /* enable MDIO - EMAC0 */ + rmiifer1 |= RGMII_FER_MDIO(0); /* enable MDIO - EMAC2 */ + + bis->bi_phymode[0] = BI_PHYMODE_GMII; + bis->bi_phymode[1] = BI_PHYMODE_NONE; + bis->bi_phymode[2] = BI_PHYMODE_GMII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 9: + /* 2 RGMII - 460EX */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, RGMII Bridge 0 */ + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); + rmiifer |= RGMII_FER_MDIO(0); /* enable MDIO - EMAC0 */ + + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 10: + /* 4 RGMII - 460GT */ + /* GMC0 EMAC4_0, GMC0 EMAC4_1, RGMII Bridge 0 */ + /* GMC1 EMAC4_2, GMC1 EMAC4_3, RGMII Bridge 1 */ + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2); + rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3); + rmiifer1 |= RGMII_FER_RGMII << RGMII_FER_V(2); + rmiifer1 |= RGMII_FER_RGMII << RGMII_FER_V(3); + bis->bi_phymode[0] = BI_PHYMODE_RGMII; + bis->bi_phymode[1] = BI_PHYMODE_RGMII; + bis->bi_phymode[2] = BI_PHYMODE_RGMII; + bis->bi_phymode[3] = BI_PHYMODE_RGMII; + break; + case 11: + /* 2 SGMII - 460EX */ + bis->bi_phymode[0] = BI_PHYMODE_SGMII; + bis->bi_phymode[1] = BI_PHYMODE_SGMII; + bis->bi_phymode[2] = BI_PHYMODE_NONE; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + case 12: + /* 3 SGMII - 460GT */ + bis->bi_phymode[0] = BI_PHYMODE_SGMII; + bis->bi_phymode[1] = BI_PHYMODE_SGMII; + bis->bi_phymode[2] = BI_PHYMODE_SGMII; + bis->bi_phymode[3] = BI_PHYMODE_NONE; + break; + default: + break; + } + + /* Set EMAC for MDIO */ + mfsdr(SDR0_ETH_CFG, eth_cfg); + eth_cfg |= SDR0_ETH_CFG_MDIO_SEL_EMAC0; + mtsdr(SDR0_ETH_CFG, eth_cfg); + + out_be32((void *)RGMII_FER, rmiifer); +#if defined(CONFIG_460GT) + out_be32((void *)RGMII_FER + RGMII1_BASE_OFFSET, rmiifer1); +#endif + + /* bypass the TAHOE0/TAHOE1 cores for U-Boot */ + mfsdr(SDR0_ETH_CFG, eth_cfg); + eth_cfg |= (SDR0_ETH_CFG_TAHOE0_BYPASS | SDR0_ETH_CFG_TAHOE1_BYPASS); + mtsdr(SDR0_ETH_CFG, eth_cfg); + + return 0; +} +#endif /* CONFIG_460EX || CONFIG_460GT */ + +static inline void *malloc_aligned(u32 size, u32 align) +{ + return (void *)(((u32)malloc(size + align) + align - 1) & + ~(align - 1)); +} + +static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) +{ + int i; + unsigned long reg = 0; + unsigned long msr; + unsigned long speed; + unsigned long duplex; + unsigned long failsafe; + unsigned mode_reg; + unsigned short devnum; + unsigned short reg_short; +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + u32 opbfreq; + sys_info_t sysinfo; +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + int ethgroup = -1; +#endif +#endif + u32 bd_cached; + u32 bd_uncached = 0; +#ifdef CONFIG_4xx_DCACHE + static u32 last_used_ea = 0; +#endif +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + int rgmii_channel; +#endif + + EMAC_4XX_HW_PST hw_p = dev->priv; + + /* before doing anything, figure out if we have a MAC address */ + /* if not, bail */ + if (memcmp (dev->enetaddr, "\0\0\0\0\0\0", 6) == 0) { + printf("ERROR: ethaddr not set!\n"); + return -1; + } + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + /* Need to get the OPB frequency so we can access the PHY */ + get_sys_info (&sysinfo); +#endif + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); /* disable interrupts */ + + devnum = hw_p->devnum; + +#ifdef INFO_4XX_ENET + /* AS.HARNOIS + * We should have : + * hw_p->stats.pkts_handled <= hw_p->stats.pkts_rx <= hw_p->stats.pkts_handled+PKTBUFSRX + * In the most cases hw_p->stats.pkts_handled = hw_p->stats.pkts_rx, but it + * is possible that new packets (without relationship with + * current transfer) have got the time to arrived before + * netloop calls eth_halt + */ + printf ("About preceeding transfer (eth%d):\n" + "- Sent packet number %d\n" + "- Received packet number %d\n" + "- Handled packet number %d\n", + hw_p->devnum, + hw_p->stats.pkts_tx, + hw_p->stats.pkts_rx, hw_p->stats.pkts_handled); + + hw_p->stats.pkts_tx = 0; + hw_p->stats.pkts_rx = 0; + hw_p->stats.pkts_handled = 0; + hw_p->print_speed = 1; /* print speed message again next time */ +#endif + + hw_p->tx_err_index = 0; /* Transmit Error Index for tx_err_log */ + hw_p->rx_err_index = 0; /* Receive Error Index for rx_err_log */ + + hw_p->rx_slot = 0; /* MAL Receive Slot */ + hw_p->rx_i_index = 0; /* Receive Interrupt Queue Index */ + hw_p->rx_u_index = 0; /* Receive User Queue Index */ + + hw_p->tx_slot = 0; /* MAL Transmit Slot */ + hw_p->tx_i_index = 0; /* Transmit Interrupt Queue Index */ + hw_p->tx_u_index = 0; /* Transmit User Queue Index */ + +#if defined(CONFIG_440) && !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) + /* set RMII mode */ + /* NOTE: 440GX spec states that mode is mutually exclusive */ + /* NOTE: Therefore, disable all other EMACS, since we handle */ + /* NOTE: only one emac at a time */ + reg = 0; + out_be32((void *)ZMII0_FER, 0); + udelay (100); + +#if defined(CONFIG_440GP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + out_be32((void *)ZMII0_FER, (ZMII_FER_RMII | ZMII_FER_MDI) << ZMII_FER_V (devnum)); +#elif defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis); +#endif + + out_be32((void *)ZMII0_SSR, ZMII0_SSR_SP << ZMII0_SSR_V(devnum)); +#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */ +#if defined(CONFIG_405EX) + ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis); +#endif + + sync(); + + /* provide clocks for EMAC internal loopback */ + emac_loopback_enable(hw_p); + + /* EMAC RESET */ + out_be32((void *)EMAC0_MR0 + hw_p->hw_addr, EMAC_MR0_SRST); + + /* remove clocks for EMAC internal loopback */ + emac_loopback_disable(hw_p); + + failsafe = 1000; + while ((in_be32((void *)EMAC0_MR0 + hw_p->hw_addr) & (EMAC_MR0_SRST)) && failsafe) { + udelay (1000); + failsafe--; + } + if (failsafe <= 0) + printf("\nProblem resetting EMAC!\n"); + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + /* Whack the M1 register */ + mode_reg = 0x0; + mode_reg &= ~0x00000038; + opbfreq = sysinfo.freqOPB / 1000000; + if (opbfreq <= 50); + else if (opbfreq <= 66) + mode_reg |= EMAC_MR1_OBCI_66; + else if (opbfreq <= 83) + mode_reg |= EMAC_MR1_OBCI_83; + else if (opbfreq <= 100) + mode_reg |= EMAC_MR1_OBCI_100; + else + mode_reg |= EMAC_MR1_OBCI_GT100; + + out_be32((void *)EMAC0_MR1 + hw_p->hw_addr, mode_reg); +#endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */ + +#if defined(CONFIG_GPCS_PHY_ADDR) || defined(CONFIG_GPCS_PHY1_ADDR) || \ + defined(CONFIG_GPCS_PHY2_ADDR) || defined(CONFIG_GPCS_PHY3_ADDR) + if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) { + /* + * In SGMII mode, GPCS access is needed for + * communication with the internal SGMII SerDes. + */ + switch (devnum) { +#if defined(CONFIG_GPCS_PHY_ADDR) + case 0: + reg = CONFIG_GPCS_PHY_ADDR; + break; +#endif +#if defined(CONFIG_GPCS_PHY1_ADDR) + case 1: + reg = CONFIG_GPCS_PHY1_ADDR; + break; +#endif +#if defined(CONFIG_GPCS_PHY2_ADDR) + case 2: + reg = CONFIG_GPCS_PHY2_ADDR; + break; +#endif +#if defined(CONFIG_GPCS_PHY3_ADDR) + case 3: + reg = CONFIG_GPCS_PHY3_ADDR; + break; +#endif + } + + mode_reg = in_be32((void *)EMAC0_MR1 + hw_p->hw_addr); + mode_reg |= EMAC_MR1_MF_1000GPCS | EMAC_MR1_IPPA_SET(reg); + out_be32((void *)EMAC0_MR1 + hw_p->hw_addr, mode_reg); + + /* Configure GPCS interface to recommended setting for SGMII */ + miiphy_reset(dev->name, reg); + miiphy_write(dev->name, reg, 0x04, 0x8120); /* AsymPause, FDX */ + miiphy_write(dev->name, reg, 0x07, 0x2801); /* msg_pg, toggle */ + miiphy_write(dev->name, reg, 0x00, 0x0140); /* 1Gbps, FDX */ + } +#endif /* defined(CONFIG_GPCS_PHY_ADDR) */ + + /* wait for PHY to complete auto negotiation */ + reg_short = 0; + switch (devnum) { + case 0: + reg = CONFIG_PHY_ADDR; + break; +#if defined (CONFIG_PHY1_ADDR) + case 1: + reg = CONFIG_PHY1_ADDR; + break; +#endif +#if defined (CONFIG_PHY2_ADDR) + case 2: + reg = CONFIG_PHY2_ADDR; + break; +#endif +#if defined (CONFIG_PHY3_ADDR) + case 3: + reg = CONFIG_PHY3_ADDR; + break; +#endif + default: + reg = CONFIG_PHY_ADDR; + break; + } + + bis->bi_phynum[devnum] = reg; + + if (reg == CONFIG_FIXED_PHY) + goto get_speed; + +#if defined(CONFIG_PHY_RESET) + /* + * Reset the phy, only if its the first time through + * otherwise, just check the speeds & feeds + */ + if (hw_p->first_init == 0) { +#if defined(CONFIG_M88E1111_PHY) + miiphy_write (dev->name, reg, 0x14, 0x0ce3); + miiphy_write (dev->name, reg, 0x18, 0x4101); + miiphy_write (dev->name, reg, 0x09, 0x0e00); + miiphy_write (dev->name, reg, 0x04, 0x01e1); +#endif +#if defined(CONFIG_M88E1112_PHY) + if (bis->bi_phymode[devnum] == BI_PHYMODE_SGMII) { + /* + * Marvell 88E1112 PHY needs to have the SGMII MAC + * interace (page 2) properly configured to + * communicate with the 460EX/GT GPCS interface. + */ + + /* Set access to Page 2 */ + miiphy_write(dev->name, reg, 0x16, 0x0002); + + miiphy_write(dev->name, reg, 0x00, 0x0040); /* 1Gbps */ + miiphy_read(dev->name, reg, 0x1a, ®_short); + reg_short |= 0x8000; /* bypass Auto-Negotiation */ + miiphy_write(dev->name, reg, 0x1a, reg_short); + miiphy_reset(dev->name, reg); /* reset MAC interface */ + + /* Reset access to Page 0 */ + miiphy_write(dev->name, reg, 0x16, 0x0000); + } +#endif /* defined(CONFIG_M88E1112_PHY) */ + miiphy_reset (dev->name, reg); + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + +#if defined(CONFIG_CIS8201_PHY) + /* + * Cicada 8201 PHY needs to have an extended register whacked + * for RGMII mode. + */ + if (((devnum == 2) || (devnum == 3)) && (4 == ethgroup)) { +#if defined(CONFIG_CIS8201_SHORT_ETCH) + miiphy_write (dev->name, reg, 23, 0x1300); +#else + miiphy_write (dev->name, reg, 23, 0x1000); +#endif + /* + * Vitesse VSC8201/Cicada CIS8201 errata: + * Interoperability problem with Intel 82547EI phys + * This work around (provided by Vitesse) changes + * the default timer convergence from 8ms to 12ms + */ + miiphy_write (dev->name, reg, 0x1f, 0x2a30); + miiphy_write (dev->name, reg, 0x08, 0x0200); + miiphy_write (dev->name, reg, 0x1f, 0x52b5); + miiphy_write (dev->name, reg, 0x02, 0x0004); + miiphy_write (dev->name, reg, 0x01, 0x0671); + miiphy_write (dev->name, reg, 0x00, 0x8fae); + miiphy_write (dev->name, reg, 0x1f, 0x2a30); + miiphy_write (dev->name, reg, 0x08, 0x0000); + miiphy_write (dev->name, reg, 0x1f, 0x0000); + /* end Vitesse/Cicada errata */ + } +#endif /* defined(CONFIG_CIS8201_PHY) */ + +#if defined(CONFIG_ET1011C_PHY) + /* + * Agere ET1011c PHY needs to have an extended register whacked + * for RGMII mode. + */ + if (((devnum == 2) || (devnum ==3)) && (4 == ethgroup)) { + miiphy_read (dev->name, reg, 0x16, ®_short); + reg_short &= ~(0x7); + reg_short |= 0x6; /* RGMII DLL Delay*/ + miiphy_write (dev->name, reg, 0x16, reg_short); + + miiphy_read (dev->name, reg, 0x17, ®_short); + reg_short &= ~(0x40); + miiphy_write (dev->name, reg, 0x17, reg_short); + + miiphy_write(dev->name, reg, 0x1c, 0x74f0); + } +#endif /* defined(CONFIG_ET1011C_PHY) */ + +#endif /* defined(CONFIG_440GX) ... */ + /* Start/Restart autonegotiation */ + phy_setup_aneg (dev->name, reg); + udelay (1000); + } +#endif /* defined(CONFIG_PHY_RESET) */ + + miiphy_read (dev->name, reg, PHY_BMSR, ®_short); + + /* + * Wait if PHY is capable of autonegotiation and autonegotiation is not complete + */ + if ((reg_short & PHY_BMSR_AUTN_ABLE) + && !(reg_short & PHY_BMSR_AUTN_COMP)) { + puts ("Waiting for PHY auto negotiation to complete"); + i = 0; + while (!(reg_short & PHY_BMSR_AUTN_COMP)) { + /* + * Timeout reached ? + */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts (" TIMEOUT !\n"); + break; + } + + if ((i++ % 1000) == 0) { + putc ('.'); + } + udelay (1000); /* 1 ms */ + miiphy_read (dev->name, reg, PHY_BMSR, ®_short); + } + puts (" done\n"); + udelay (500000); /* another 500 ms (results in faster booting) */ + } + +get_speed: + if (reg == CONFIG_FIXED_PHY) { + for (i = 0; i < ARRAY_SIZE(fixed_phy_port); i++) { + if (devnum == fixed_phy_port[i].devnum) { + speed = fixed_phy_port[i].speed; + duplex = fixed_phy_port[i].duplex; + break; + } + } + + if (i == ARRAY_SIZE(fixed_phy_port)) { + printf("ERROR: PHY (%s) not configured correctly!\n", + dev->name); + return -1; + } + } else { + speed = miiphy_speed(dev->name, reg); + duplex = miiphy_duplex(dev->name, reg); + } + + if (hw_p->print_speed) { + hw_p->print_speed = 0; + printf ("ENET Speed is %d Mbps - %s duplex connection (EMAC%d)\n", + (int) speed, (duplex == HALF) ? "HALF" : "FULL", + hw_p->devnum); + } + +#if defined(CONFIG_440) && \ + !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \ + !defined(CONFIG_440EPX) && !defined(CONFIG_440GRX) && \ + !defined(CONFIG_460EX) && !defined(CONFIG_460GT) +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) + mfsdr(SDR0_MFR, reg); + if (speed == 100) { + reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_100M; + } else { + reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_10M; + } + mtsdr(SDR0_MFR, reg); +#endif + + /* Set ZMII/RGMII speed according to the phy link speed */ + reg = in_be32((void *)ZMII0_SSR); + if ( (speed == 100) || (speed == 1000) ) + out_be32((void *)ZMII0_SSR, reg | (ZMII0_SSR_SP << ZMII0_SSR_V (devnum))); + else + out_be32((void *)ZMII0_SSR, reg & (~(ZMII0_SSR_SP << ZMII0_SSR_V (devnum)))); + + if ((devnum == 2) || (devnum == 3)) { + if (speed == 1000) + reg = (RGMII_SSR_SP_1000MBPS << RGMII_SSR_V (devnum)); + else if (speed == 100) + reg = (RGMII_SSR_SP_100MBPS << RGMII_SSR_V (devnum)); + else if (speed == 10) + reg = (RGMII_SSR_SP_10MBPS << RGMII_SSR_V (devnum)); + else { + printf("Error in RGMII Speed\n"); + return -1; + } + out_be32((void *)RGMII_SSR, reg); + } +#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */ + +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + if (devnum >= 2) + rgmii_channel = devnum - 2; + else + rgmii_channel = devnum; + + if (speed == 1000) + reg = (RGMII_SSR_SP_1000MBPS << RGMII_SSR_V(rgmii_channel)); + else if (speed == 100) + reg = (RGMII_SSR_SP_100MBPS << RGMII_SSR_V(rgmii_channel)); + else if (speed == 10) + reg = (RGMII_SSR_SP_10MBPS << RGMII_SSR_V(rgmii_channel)); + else { + printf("Error in RGMII Speed\n"); + return -1; + } + out_be32((void *)RGMII_SSR, reg); +#if defined(CONFIG_460GT) + if ((devnum == 2) || (devnum == 3)) + out_be32((void *)RGMII_SSR + RGMII1_BASE_OFFSET, reg); +#endif +#endif + + /* set the Mal configuration reg */ +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + mtdcr (MAL0_CFG, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | + MAL_CR_PLBLT_DEFAULT | MAL_CR_EOPIE | 0x00330000); +#else + mtdcr (MAL0_CFG, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); + /* Errata 1.12: MAL_1 -- Disable MAL bursting */ + if (get_pvr() == PVR_440GP_RB) { + mtdcr (MAL0_CFG, mfdcr(MAL0_CFG) & ~MAL_CR_PLBB); + } +#endif + + /* + * Malloc MAL buffer desciptors, make sure they are + * aligned on cache line boundary size + * (401/403/IOP480 = 16, 405 = 32) + * and doesn't cross cache block boundaries. + */ + if (hw_p->first_init == 0) { + debug("*** Allocating descriptor memory ***\n"); + + bd_cached = (u32)malloc_aligned(MAL_ALLOC_SIZE, 4096); + if (!bd_cached) { + printf("%s: Error allocating MAL descriptor buffers!\n", __func__); + return -1; + } + +#ifdef CONFIG_4xx_DCACHE + flush_dcache_range(bd_cached, bd_cached + MAL_ALLOC_SIZE); + if (!last_used_ea) +#if defined(CONFIG_SYS_MEM_TOP_HIDE) + bd_uncached = bis->bi_memsize + CONFIG_SYS_MEM_TOP_HIDE; +#else + bd_uncached = bis->bi_memsize; +#endif + else + bd_uncached = last_used_ea + MAL_ALLOC_SIZE; + + last_used_ea = bd_uncached; + program_tlb(bd_cached, bd_uncached, MAL_ALLOC_SIZE, + TLB_WORD2_I_ENABLE); +#else + bd_uncached = bd_cached; +#endif + hw_p->tx_phys = bd_cached; + hw_p->rx_phys = bd_cached + MAL_TX_DESC_SIZE; + hw_p->tx = (mal_desc_t *)(bd_uncached); + hw_p->rx = (mal_desc_t *)(bd_uncached + MAL_TX_DESC_SIZE); + debug("hw_p->tx=%08x, hw_p->rx=%08x\n", hw_p->tx, hw_p->rx); + } + + for (i = 0; i < NUM_TX_BUFF; i++) { + hw_p->tx[i].ctrl = 0; + hw_p->tx[i].data_len = 0; + if (hw_p->first_init == 0) + hw_p->txbuf_ptr = malloc_aligned(MAL_ALLOC_SIZE, + L1_CACHE_BYTES); + hw_p->tx[i].data_ptr = hw_p->txbuf_ptr; + if ((NUM_TX_BUFF - 1) == i) + hw_p->tx[i].ctrl |= MAL_TX_CTRL_WRAP; + hw_p->tx_run[i] = -1; + debug("TX_BUFF %d @ 0x%08lx\n", i, (u32)hw_p->tx[i].data_ptr); + } + + for (i = 0; i < NUM_RX_BUFF; i++) { + hw_p->rx[i].ctrl = 0; + hw_p->rx[i].data_len = 0; + hw_p->rx[i].data_ptr = (char *)NetRxPackets[i]; + if ((NUM_RX_BUFF - 1) == i) + hw_p->rx[i].ctrl |= MAL_RX_CTRL_WRAP; + hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR; + hw_p->rx_ready[i] = -1; + debug("RX_BUFF %d @ 0x%08lx\n", i, (u32)hw_p->rx[i].data_ptr); + } + + reg = 0x00000000; + + reg |= dev->enetaddr[0]; /* set high address */ + reg = reg << 8; + reg |= dev->enetaddr[1]; + + out_be32((void *)EMAC0_IAH + hw_p->hw_addr, reg); + + reg = 0x00000000; + reg |= dev->enetaddr[2]; /* set low address */ + reg = reg << 8; + reg |= dev->enetaddr[3]; + reg = reg << 8; + reg |= dev->enetaddr[4]; + reg = reg << 8; + reg |= dev->enetaddr[5]; + + out_be32((void *)EMAC0_IAL + hw_p->hw_addr, reg); + + switch (devnum) { + case 1: + /* setup MAL tx & rx channel pointers */ +#if defined (CONFIG_405EP) || defined (CONFIG_440EP) || defined (CONFIG_440GR) + mtdcr (MAL0_TXCTP2R, hw_p->tx_phys); +#else + mtdcr (MAL0_TXCTP1R, hw_p->tx_phys); +#endif +#if defined(CONFIG_440) + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); +#endif + +#if defined(CONFIG_460EX) || defined(CONFIG_460GT) + mtdcr (MAL0_RXCTP8R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS8, ENET_MAX_MTU_ALIGNED / 16); +#else + mtdcr (MAL0_RXCTP1R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS1, ENET_MAX_MTU_ALIGNED / 16); +#endif + break; +#if defined (CONFIG_440GX) + case 2: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_TXCTP2R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP2R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS2, ENET_MAX_MTU_ALIGNED / 16); + break; + case 3: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_TXCTP3R, hw_p->tx_phys); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_RXCTP3R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS3, ENET_MAX_MTU_ALIGNED / 16); + break; +#endif /* CONFIG_440GX */ +#if defined (CONFIG_460GT) + case 2: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_TXCTP2R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP16R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS16, ENET_MAX_MTU_ALIGNED / 16); + break; + case 3: + /* setup MAL tx & rx channel pointers */ + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); + mtdcr (MAL0_TXCTP3R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP24R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS24, ENET_MAX_MTU_ALIGNED / 16); + break; +#endif /* CONFIG_460GT */ + case 0: + default: + /* setup MAL tx & rx channel pointers */ +#if defined(CONFIG_440) + mtdcr (MAL0_TXBADDR, 0x0); + mtdcr (MAL0_RXBADDR, 0x0); +#endif + mtdcr (MAL0_TXCTP0R, hw_p->tx_phys); + mtdcr (MAL0_RXCTP0R, hw_p->rx_phys); + /* set RX buffer size */ + mtdcr (MAL0_RCBS0, ENET_MAX_MTU_ALIGNED / 16); + break; + } + + /* Enable MAL transmit and receive channels */ +#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) + mtdcr (MAL0_TXCASR, (MAL_TXRX_CASR >> (hw_p->devnum*2))); +#else + mtdcr (MAL0_TXCASR, (MAL_TXRX_CASR >> hw_p->devnum)); +#endif + mtdcr (MAL0_RXCASR, (MAL_TXRX_CASR >> hw_p->devnum)); + + /* set transmit enable & receive enable */ + out_be32((void *)EMAC0_MR0 + hw_p->hw_addr, EMAC_MR0_TXE | EMAC_MR0_RXE); + + mode_reg = in_be32((void *)EMAC0_MR1 + hw_p->hw_addr); + + /* set rx-/tx-fifo size */ + mode_reg = (mode_reg & ~EMAC_MR1_FIFO_MASK) | EMAC_MR1_FIFO_SIZE; + + /* set speed */ + if (speed == _1000BASET) { +#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_440SP) || defined(CONFIG_440SPE) + unsigned long pfc1; + + mfsdr (SDR0_PFC1, pfc1); + pfc1 |= SDR0_PFC1_EM_1000; + mtsdr (SDR0_PFC1, pfc1); +#endif + mode_reg = mode_reg | EMAC_MR1_MF_1000MBPS | EMAC_MR1_IST; + } else if (speed == _100BASET) + mode_reg = mode_reg | EMAC_MR1_MF_100MBPS | EMAC_MR1_IST; + else + mode_reg = mode_reg & ~0x00C00000; /* 10 MBPS */ + if (duplex == FULL) + mode_reg = mode_reg | 0x80000000 | EMAC_MR1_IST; + + out_be32((void *)EMAC0_MR1 + hw_p->hw_addr, mode_reg); + + /* Enable broadcast and indvidual address */ + /* TBS: enabling runts as some misbehaved nics will send runts */ + out_be32((void *)EMAC0_RXM + hw_p->hw_addr, EMAC_RMR_BAE | EMAC_RMR_IAE); + + /* we probably need to set the tx mode1 reg? maybe at tx time */ + + /* set transmit request threshold register */ + out_be32((void *)EMAC0_TRTR + hw_p->hw_addr, 0x18000000); /* 256 byte threshold */ + + /* set receive low/high water mark register */ +#if defined(CONFIG_440) + /* 440s has a 64 byte burst length */ + out_be32((void *)EMAC0_RX_HI_LO_WMARK + hw_p->hw_addr, 0x80009000); +#else + /* 405s have a 16 byte burst length */ + out_be32((void *)EMAC0_RX_HI_LO_WMARK + hw_p->hw_addr, 0x0f002000); +#endif /* defined(CONFIG_440) */ + out_be32((void *)EMAC0_TMR1 + hw_p->hw_addr, 0xf8640000); + + /* Set fifo limit entry in tx mode 0 */ + out_be32((void *)EMAC0_TMR0 + hw_p->hw_addr, 0x00000003); + /* Frame gap set */ + out_be32((void *)EMAC0_I_FRAME_GAP_REG + hw_p->hw_addr, 0x00000008); + + /* Set EMAC IER */ + hw_p->emac_ier = EMAC_ISR_PTLE | EMAC_ISR_BFCS | EMAC_ISR_ORE | EMAC_ISR_IRE; + if (speed == _100BASET) + hw_p->emac_ier = hw_p->emac_ier | EMAC_ISR_SYE; + + out_be32((void *)EMAC0_ISR + hw_p->hw_addr, 0xffffffff); /* clear pending interrupts */ + out_be32((void *)EMAC0_IER + hw_p->hw_addr, hw_p->emac_ier); + + if (hw_p->first_init == 0) { + /* + * Connect interrupt service routines + */ + irq_install_handler(ETH_IRQ_NUM(hw_p->devnum), + (interrupt_handler_t *) enetInt, dev); + } + + mtmsr (msr); /* enable interrupts again */ + + hw_p->bis = bis; + hw_p->first_init = 1; + + return 0; +} + + +static int ppc_4xx_eth_send (struct eth_device *dev, volatile void *ptr, + int len) +{ + struct enet_frame *ef_ptr; + ulong time_start, time_now; + unsigned long temp_txm0; + EMAC_4XX_HW_PST hw_p = dev->priv; + + ef_ptr = (struct enet_frame *) ptr; + + /*-----------------------------------------------------------------------+ + * Copy in our address into the frame. + *-----------------------------------------------------------------------*/ + (void) memcpy (ef_ptr->source_addr, dev->enetaddr, ENET_ADDR_LENGTH); + + /*-----------------------------------------------------------------------+ + * If frame is too long or too short, modify length. + *-----------------------------------------------------------------------*/ + /* TBS: where does the fragment go???? */ + if (len > ENET_MAX_MTU) + len = ENET_MAX_MTU; + + /* memcpy ((void *) &tx_buff[tx_slot], (const void *) ptr, len); */ + memcpy ((void *) hw_p->txbuf_ptr, (const void *) ptr, len); + flush_dcache_range((u32)hw_p->txbuf_ptr, (u32)hw_p->txbuf_ptr + len); + + /*-----------------------------------------------------------------------+ + * set TX Buffer busy, and send it + *-----------------------------------------------------------------------*/ + hw_p->tx[hw_p->tx_slot].ctrl = (MAL_TX_CTRL_LAST | + EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP) & + ~(EMAC_TX_CTRL_ISA | EMAC_TX_CTRL_RSA); + if ((NUM_TX_BUFF - 1) == hw_p->tx_slot) + hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_WRAP; + + hw_p->tx[hw_p->tx_slot].data_len = (short) len; + hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_READY; + + sync(); + + out_be32((void *)EMAC0_TMR0 + hw_p->hw_addr, + in_be32((void *)EMAC0_TMR0 + hw_p->hw_addr) | EMAC_TMR0_GNP0); +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_tx++; +#endif + + /*-----------------------------------------------------------------------+ + * poll unitl the packet is sent and then make sure it is OK + *-----------------------------------------------------------------------*/ + time_start = get_timer (0); + while (1) { + temp_txm0 = in_be32((void *)EMAC0_TMR0 + hw_p->hw_addr); + /* loop until either TINT turns on or 3 seconds elapse */ + if ((temp_txm0 & EMAC_TMR0_GNP0) != 0) { + /* transmit is done, so now check for errors + * If there is an error, an interrupt should + * happen when we return + */ + time_now = get_timer (0); + if ((time_now - time_start) > 3000) { + return (-1); + } + } else { + return (len); + } + } +} + +int enetInt (struct eth_device *dev) +{ + int serviced; + int rc = -1; /* default to not us */ + u32 mal_isr; + u32 emac_isr = 0; + u32 mal_eob; + u32 uic_mal; + u32 uic_mal_err; + u32 uic_emac; + u32 uic_emac_b; + EMAC_4XX_HW_PST hw_p; + + /* + * Because the mal is generic, we need to get the current + * eth device + */ + dev = eth_get_dev(); + + hw_p = dev->priv; + + /* enter loop that stays in interrupt code until nothing to service */ + do { + serviced = 0; + + uic_mal = mfdcr(UIC_BASE_MAL + UIC_MSR); + uic_mal_err = mfdcr(UIC_BASE_MAL_ERR + UIC_MSR); + uic_emac = mfdcr(UIC_BASE_EMAC + UIC_MSR); + uic_emac_b = mfdcr(UIC_BASE_EMAC_B + UIC_MSR); + + if (!(uic_mal & (UIC_MAL_RXEOB | UIC_MAL_TXEOB)) + && !(uic_mal_err & (UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE)) + && !(uic_emac & UIC_ETHx) && !(uic_emac_b & UIC_ETHxB)) { + /* not for us */ + return (rc); + } + + /* get and clear controller status interrupts */ + /* look at MAL and EMAC error interrupts */ + if (uic_mal_err & (UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE)) { + /* we have a MAL error interrupt */ + mal_isr = mfdcr(MAL0_ESR); + mal_err(dev, mal_isr, uic_mal_err, + MAL_UIC_DEF, MAL_UIC_ERR); + + /* clear MAL error interrupt status bits */ + mtdcr(UIC_BASE_MAL_ERR + UIC_SR, + UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE); + + return -1; + } + + /* look for EMAC errors */ + if ((uic_emac & UIC_ETHx) || (uic_emac_b & UIC_ETHxB)) { + emac_isr = in_be32((void *)EMAC0_ISR + hw_p->hw_addr); + emac_err(dev, emac_isr); + + /* clear EMAC error interrupt status bits */ + mtdcr(UIC_BASE_EMAC + UIC_SR, UIC_ETHx); + mtdcr(UIC_BASE_EMAC_B + UIC_SR, UIC_ETHxB); + + return -1; + } + + /* handle MAX TX EOB interrupt from a tx */ + if (uic_mal & UIC_MAL_TXEOB) { + /* clear MAL interrupt status bits */ + mal_eob = mfdcr(MAL0_TXEOBISR); + mtdcr(MAL0_TXEOBISR, mal_eob); + mtdcr(UIC_BASE_MAL + UIC_SR, UIC_MAL_TXEOB); + + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + + /* handle MAL RX EOB interupt from a receive */ + /* check for EOB on valid channels */ + if (uic_mal & UIC_MAL_RXEOB) { + mal_eob = mfdcr(MAL0_RXEOBISR); + if (mal_eob & + (0x80000000 >> (hw_p->devnum * MAL_RX_CHAN_MUL))) { + /* push packet to upper layer */ + enet_rcv(dev, emac_isr); + + /* clear MAL interrupt status bits */ + mtdcr(UIC_BASE_MAL + UIC_SR, UIC_MAL_RXEOB); + + /* indicate that we serviced an interrupt */ + serviced = 1; + rc = 0; + } + } +#if defined(CONFIG_405EZ) + /* + * On 405EZ the RX-/TX-interrupts are coalesced into + * one IRQ bit in the UIC. We need to acknowledge the + * RX-/TX-interrupts in the SDR0_ICINTSTAT reg as well. + */ + mtsdr(SDR0_ICINTSTAT, + SDR_ICRX_STAT | SDR_ICTX0_STAT | SDR_ICTX1_STAT); +#endif /* defined(CONFIG_405EZ) */ + } while (serviced); + + return (rc); +} + +/*-----------------------------------------------------------------------------+ + * MAL Error Routine + *-----------------------------------------------------------------------------*/ +static void mal_err (struct eth_device *dev, unsigned long isr, + unsigned long uic, unsigned long maldef, + unsigned long mal_errr) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + + mtdcr (MAL0_ESR, isr); /* clear interrupt */ + + /* clear DE interrupt */ + mtdcr (MAL0_TXDEIR, 0xC0000000); + mtdcr (MAL0_RXDEIR, 0x80000000); + +#ifdef INFO_4XX_ENET + printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", isr, uic, maldef, mal_errr); +#endif + + eth_init (hw_p->bis); /* start again... */ +} + +/*-----------------------------------------------------------------------------+ + * EMAC Error Routine + *-----------------------------------------------------------------------------*/ +static void emac_err (struct eth_device *dev, unsigned long isr) +{ + EMAC_4XX_HW_PST hw_p = dev->priv; + + printf ("EMAC%d error occured.... ISR = %lx\n", hw_p->devnum, isr); + out_be32((void *)EMAC0_ISR + hw_p->hw_addr, isr); +} + +/*-----------------------------------------------------------------------------+ + * enet_rcv() handles the ethernet receive data + *-----------------------------------------------------------------------------*/ +static void enet_rcv (struct eth_device *dev, unsigned long malisr) +{ + struct enet_frame *ef_ptr; + unsigned long data_len; + unsigned long rx_eob_isr; + EMAC_4XX_HW_PST hw_p = dev->priv; + + int handled = 0; + int i; + int loop_count = 0; + + rx_eob_isr = mfdcr (MAL0_RXEOBISR); + if ((0x80000000 >> (hw_p->devnum * MAL_RX_CHAN_MUL)) & rx_eob_isr) { + /* clear EOB */ + mtdcr (MAL0_RXEOBISR, rx_eob_isr); + + /* EMAC RX done */ + while (1) { /* do all */ + i = hw_p->rx_slot; + + if ((MAL_RX_CTRL_EMPTY & hw_p->rx[i].ctrl) + || (loop_count >= NUM_RX_BUFF)) + break; + + loop_count++; + handled++; + data_len = (unsigned long) hw_p->rx[i].data_len & 0x0fff; /* Get len */ + if (data_len) { + if (data_len > ENET_MAX_MTU) /* Check len */ + data_len = 0; + else { + if (EMAC_RX_ERRORS & hw_p->rx[i].ctrl) { /* Check Errors */ + data_len = 0; + hw_p->stats.rx_err_log[hw_p-> + rx_err_index] + = hw_p->rx[i].ctrl; + hw_p->rx_err_index++; + if (hw_p->rx_err_index == + MAX_ERR_LOG) + hw_p->rx_err_index = + 0; + } /* emac_erros */ + } /* data_len < max mtu */ + } /* if data_len */ + if (!data_len) { /* no data */ + hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY; /* Free Recv Buffer */ + + hw_p->stats.data_len_err++; /* Error at Rx */ + } + + /* !data_len */ + /* AS.HARNOIS */ + /* Check if user has already eaten buffer */ + /* if not => ERROR */ + else if (hw_p->rx_ready[hw_p->rx_i_index] != -1) { + if (hw_p->is_receiving) + printf ("ERROR : Receive buffers are full!\n"); + break; + } else { + hw_p->stats.rx_frames++; + hw_p->stats.rx += data_len; + ef_ptr = (struct enet_frame *) hw_p->rx[i]. + data_ptr; +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_rx++; +#endif + /* AS.HARNOIS + * use ring buffer + */ + hw_p->rx_ready[hw_p->rx_i_index] = i; + hw_p->rx_i_index++; + if (NUM_RX_BUFF == hw_p->rx_i_index) + hw_p->rx_i_index = 0; + + hw_p->rx_slot++; + if (NUM_RX_BUFF == hw_p->rx_slot) + hw_p->rx_slot = 0; + + /* AS.HARNOIS + * free receive buffer only when + * buffer has been handled (eth_rx) + rx[i].ctrl |= MAL_RX_CTRL_EMPTY; + */ + } /* if data_len */ + } /* while */ + } /* if EMACK_RXCHL */ +} + + +static int ppc_4xx_eth_rx (struct eth_device *dev) +{ + int length; + int user_index; + unsigned long msr; + EMAC_4XX_HW_PST hw_p = dev->priv; + + hw_p->is_receiving = 1; /* tell driver */ + + for (;;) { + /* AS.HARNOIS + * use ring buffer and + * get index from rx buffer desciptor queue + */ + user_index = hw_p->rx_ready[hw_p->rx_u_index]; + if (user_index == -1) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + msr = mfmsr (); + mtmsr (msr & ~(MSR_EE)); + + length = hw_p->rx[user_index].data_len & 0x0fff; + + /* Pass the packet up to the protocol layers. */ + /* NetReceive(NetRxPackets[rxIdx], length - 4); */ + /* NetReceive(NetRxPackets[i], length); */ + invalidate_dcache_range((u32)hw_p->rx[user_index].data_ptr, + (u32)hw_p->rx[user_index].data_ptr + + length - 4); + NetReceive (NetRxPackets[user_index], length - 4); + /* Free Recv Buffer */ + hw_p->rx[user_index].ctrl |= MAL_RX_CTRL_EMPTY; + /* Free rx buffer descriptor queue */ + hw_p->rx_ready[hw_p->rx_u_index] = -1; + hw_p->rx_u_index++; + if (NUM_RX_BUFF == hw_p->rx_u_index) + hw_p->rx_u_index = 0; + +#ifdef INFO_4XX_ENET + hw_p->stats.pkts_handled++; +#endif + + mtmsr (msr); /* Enable IRQ's */ + } + + hw_p->is_receiving = 0; /* tell driver */ + + return length; +} + +int ppc_4xx_eth_initialize (bd_t * bis) +{ + static int virgin = 0; + struct eth_device *dev; + int eth_num = 0; + EMAC_4XX_HW_PST hw = NULL; + u8 ethaddr[4 + CONFIG_EMAC_NR_START][6]; + u32 hw_addr[4]; + u32 mal_ier; + +#if defined(CONFIG_440GX) + unsigned long pfc1; + + mfsdr (SDR0_PFC1, pfc1); + pfc1 &= ~(0x01e00000); + pfc1 |= 0x01200000; + mtsdr (SDR0_PFC1, pfc1); +#endif + + /* first clear all mac-addresses */ + for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) + memcpy(ethaddr[eth_num], "\0\0\0\0\0\0", 6); + + for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) { + int ethaddr_idx = eth_num + CONFIG_EMAC_NR_START; + switch (eth_num) { + default: /* fall through */ + case 0: + eth_getenv_enetaddr("ethaddr", ethaddr[ethaddr_idx]); + hw_addr[eth_num] = 0x0; + break; +#ifdef CONFIG_HAS_ETH1 + case 1: + eth_getenv_enetaddr("eth1addr", ethaddr[ethaddr_idx]); + hw_addr[eth_num] = 0x100; + break; +#endif +#ifdef CONFIG_HAS_ETH2 + case 2: + eth_getenv_enetaddr("eth2addr", ethaddr[ethaddr_idx]); +#if defined(CONFIG_460GT) + hw_addr[eth_num] = 0x300; +#else + hw_addr[eth_num] = 0x400; +#endif + break; +#endif +#ifdef CONFIG_HAS_ETH3 + case 3: + eth_getenv_enetaddr("eth3addr", ethaddr[ethaddr_idx]); +#if defined(CONFIG_460GT) + hw_addr[eth_num] = 0x400; +#else + hw_addr[eth_num] = 0x600; +#endif + break; +#endif + } + } + + /* set phy num and mode */ + bis->bi_phynum[0] = CONFIG_PHY_ADDR; + bis->bi_phymode[0] = 0; + +#if defined(CONFIG_PHY1_ADDR) + bis->bi_phynum[1] = CONFIG_PHY1_ADDR; + bis->bi_phymode[1] = 0; +#endif +#if defined(CONFIG_440GX) + bis->bi_phynum[2] = CONFIG_PHY2_ADDR; + bis->bi_phynum[3] = CONFIG_PHY3_ADDR; + bis->bi_phymode[2] = 2; + bis->bi_phymode[3] = 2; +#endif + +#if defined(CONFIG_440GX) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_405EX) + ppc_4xx_eth_setup_bridge(0, bis); +#endif + + for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) { + /* + * See if we can actually bring up the interface, + * otherwise, skip it + */ + if (memcmp (ethaddr[eth_num], "\0\0\0\0\0\0", 6) == 0) { + bis->bi_phymode[eth_num] = BI_PHYMODE_NONE; + continue; + } + + /* Allocate device structure */ + dev = (struct eth_device *) malloc (sizeof (*dev)); + if (dev == NULL) { + printf ("ppc_4xx_eth_initialize: " + "Cannot allocate eth_device %d\n", eth_num); + return (-1); + } + memset(dev, 0, sizeof(*dev)); + + /* Allocate our private use data */ + hw = (EMAC_4XX_HW_PST) malloc (sizeof (*hw)); + if (hw == NULL) { + printf ("ppc_4xx_eth_initialize: " + "Cannot allocate private hw data for eth_device %d", + eth_num); + free (dev); + return (-1); + } + memset(hw, 0, sizeof(*hw)); + + hw->hw_addr = hw_addr[eth_num]; + memcpy (dev->enetaddr, ethaddr[eth_num], 6); + hw->devnum = eth_num; + hw->print_speed = 1; + + sprintf (dev->name, "ppc_4xx_eth%d", eth_num - CONFIG_EMAC_NR_START); + dev->priv = (void *) hw; + dev->init = ppc_4xx_eth_init; + dev->halt = ppc_4xx_eth_halt; + dev->send = ppc_4xx_eth_send; + dev->recv = ppc_4xx_eth_rx; + + if (0 == virgin) { + /* set the MAL IER ??? names may change with new spec ??? */ +#if defined(CONFIG_440SPE) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ + defined(CONFIG_405EX) + mal_ier = + MAL_IER_PT | MAL_IER_PRE | MAL_IER_PWE | + MAL_IER_DE | MAL_IER_OTE | MAL_IER_OE | MAL_IER_PE ; +#else + mal_ier = + MAL_IER_DE | MAL_IER_NE | MAL_IER_TE | + MAL_IER_OPBE | MAL_IER_PLBE; +#endif + mtdcr (MAL0_ESR, 0xffffffff); /* clear pending interrupts */ + mtdcr (MAL0_TXDEIR, 0xffffffff); /* clear pending interrupts */ + mtdcr (MAL0_RXDEIR, 0xffffffff); /* clear pending interrupts */ + mtdcr (MAL0_IER, mal_ier); + + /* install MAL interrupt handler */ + irq_install_handler (VECNUM_MAL_SERR, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_TXEOB, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_RXEOB, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_TXDE, + (interrupt_handler_t *) enetInt, + dev); + irq_install_handler (VECNUM_MAL_RXDE, + (interrupt_handler_t *) enetInt, + dev); + virgin = 1; + } + + eth_register (dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + emac4xx_miiphy_read, emac4xx_miiphy_write); +#endif + } /* end for each supported device */ + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/5701rls.c b/roms/u-boot-sam460ex/drivers/net/5701rls.c new file mode 100644 index 000000000..86950d0f8 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/5701rls.c @@ -0,0 +1,46 @@ +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/* */ +/******************************************************************************/ + +#if INCLUDE_5701_AX_FIX + +#include "bcm570x_mm.h" +#include "5701rls.h" + +LM_STATUS LM_LoadRlsFirmware(PLM_DEVICE_BLOCK pDevice) +{ + T3_FWIMG_INFO FwImgInfo; + + FwImgInfo.StartAddress = t3FwStartAddr; + FwImgInfo.Text.Buffer = (PLM_UINT8)t3FwText; + FwImgInfo.Text.Offset = t3FwTextAddr; + FwImgInfo.Text.Length = t3FwTextLen; + FwImgInfo.ROnlyData.Buffer = (PLM_UINT8)t3FwRodata; + FwImgInfo.ROnlyData.Offset = t3FwRodataAddr; + FwImgInfo.ROnlyData.Length = t3FwRodataLen; + FwImgInfo.Data.Buffer = (PLM_UINT8)t3FwData; + FwImgInfo.Data.Offset = t3FwDataAddr; + FwImgInfo.Data.Length = t3FwDataLen; + + if (LM_LoadFirmware(pDevice, + &FwImgInfo, + T3_RX_CPU_ID | T3_TX_CPU_ID, + T3_RX_CPU_ID) != LM_STATUS_SUCCESS) + { + return LM_STATUS_FAILURE; + } + + return LM_STATUS_SUCCESS; +} + +#endif /* INCLUDE_5701_AX_FIX */ diff --git a/roms/u-boot-sam460ex/drivers/net/5701rls.h b/roms/u-boot-sam460ex/drivers/net/5701rls.h new file mode 100644 index 000000000..30b127a42 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/5701rls.h @@ -0,0 +1,198 @@ +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/******************************************************************************/ + +typedef unsigned long U32; +int t3FwReleaseMajor = 0x0; +int t3FwReleaseMinor = 0x0; +int t3FwReleaseFix = 0x0; +U32 t3FwStartAddr = 0x08000000; +U32 t3FwTextAddr = 0x08000000; +int t3FwTextLen = 0x9c0; +U32 t3FwRodataAddr = 0x080009c0; +int t3FwRodataLen = 0x60; +U32 t3FwDataAddr = 0x08000a40; +int t3FwDataLen = 0x20; +U32 t3FwSbssAddr = 0x08000a60; +int t3FwSbssLen = 0xc; +U32 t3FwBssAddr = 0x08000a70; +int t3FwBssLen = 0x10; +U32 t3FwText[(0x9c0/4) + 1] = { +0x0, +0x10000003, 0x0, 0xd, 0xd, +0x3c1d0800, 0x37bd3ffc, 0x3a0f021, 0x3c100800, +0x26100000, 0xe000018, 0x0, 0xd, +0x3c1d0800, 0x37bd3ffc, 0x3a0f021, 0x3c100800, +0x26100034, 0xe00021c, 0x0, 0xd, +0x0, 0x0, 0x0, 0x27bdffe0, +0x3c1cc000, 0xafbf0018, 0xaf80680c, 0xe00004c, +0x241b2105, 0x97850000, 0x97870002, 0x9782002c, +0x9783002e, 0x3c040800, 0x248409c0, 0xafa00014, +0x21400, 0x621825, 0x52c00, 0xafa30010, +0x8f860010, 0xe52825, 0xe000060, 0x24070102, +0x3c02ac00, 0x34420100, 0x3c03ac01, 0x34630100, +0xaf820490, 0x3c02ffff, 0xaf820494, 0xaf830498, +0xaf82049c, 0x24020001, 0xaf825ce0, 0xe00003f, +0xaf825d00, 0xe000140, 0x0, 0x8fbf0018, +0x3e00008, 0x27bd0020, 0x2402ffff, 0xaf825404, +0x8f835400, 0x34630400, 0xaf835400, 0xaf825404, +0x3c020800, 0x24420034, 0xaf82541c, 0x3e00008, +0xaf805400, 0x0, 0x0, 0x3c020800, +0x34423000, 0x3c030800, 0x34633000, 0x3c040800, +0x348437ff, 0x3c010800, 0xac220a64, 0x24020040, +0x3c010800, 0xac220a68, 0x3c010800, 0xac200a60, +0xac600000, 0x24630004, 0x83102b, 0x5040fffd, +0xac600000, 0x3e00008, 0x0, 0x804821, +0x8faa0010, 0x3c020800, 0x8c420a60, 0x3c040800, +0x8c840a68, 0x8fab0014, 0x24430001, 0x44102b, +0x3c010800, 0xac230a60, 0x14400003, 0x4021, +0x3c010800, 0xac200a60, 0x3c020800, 0x8c420a60, +0x3c030800, 0x8c630a64, 0x91240000, 0x21140, +0x431021, 0x481021, 0x25080001, 0xa0440000, +0x29020008, 0x1440fff4, 0x25290001, 0x3c020800, +0x8c420a60, 0x3c030800, 0x8c630a64, 0x8f84680c, +0x21140, 0x431021, 0xac440008, 0xac45000c, +0xac460010, 0xac470014, 0xac4a0018, 0x3e00008, +0xac4b001c, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x2000008, +0x0, 0xa0001e3, 0x3c0a0001, 0xa0001e3, +0x3c0a0002, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x3c0a0007, 0xa0001e3, 0x3c0a0008, 0xa0001e3, +0x3c0a0009, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x3c0a000b, 0xa0001e3, +0x3c0a000c, 0xa0001e3, 0x3c0a000d, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x3c0a000e, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x0, 0xa0001e3, +0x0, 0xa0001e3, 0x3c0a0013, 0xa0001e3, +0x3c0a0014, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x27bdffe0, +0x1821, 0x1021, 0xafbf0018, 0xafb10014, +0xafb00010, 0x3c010800, 0x220821, 0xac200a70, +0x3c010800, 0x220821, 0xac200a74, 0x3c010800, +0x220821, 0xac200a78, 0x24630001, 0x1860fff5, +0x2442000c, 0x24110001, 0x8f906810, 0x32020004, +0x14400005, 0x24040001, 0x3c020800, 0x8c420a78, +0x18400003, 0x2021, 0xe000182, 0x0, +0x32020001, 0x10400003, 0x0, 0xe000169, +0x0, 0xa000153, 0xaf915028, 0x8fbf0018, +0x8fb10014, 0x8fb00010, 0x3e00008, 0x27bd0020, +0x3c050800, 0x8ca50a70, 0x3c060800, 0x8cc60a80, +0x3c070800, 0x8ce70a78, 0x27bdffe0, 0x3c040800, +0x248409d0, 0xafbf0018, 0xafa00010, 0xe000060, +0xafa00014, 0xe00017b, 0x2021, 0x8fbf0018, +0x3e00008, 0x27bd0020, 0x24020001, 0x8f836810, +0x821004, 0x21027, 0x621824, 0x3e00008, +0xaf836810, 0x27bdffd8, 0xafbf0024, 0x1080002e, +0xafb00020, 0x8f825cec, 0xafa20018, 0x8f825cec, +0x3c100800, 0x26100a78, 0xafa2001c, 0x34028000, +0xaf825cec, 0x8e020000, 0x18400016, 0x0, +0x3c020800, 0x94420a74, 0x8fa3001c, 0x221c0, +0xac830004, 0x8fa2001c, 0x3c010800, 0xe000201, +0xac220a74, 0x10400005, 0x0, 0x8e020000, +0x24420001, 0xa0001df, 0xae020000, 0x3c020800, +0x8c420a70, 0x21c02, 0x321c0, 0xa0001c5, +0xafa2001c, 0xe000201, 0x0, 0x1040001f, +0x0, 0x8e020000, 0x8fa3001c, 0x24420001, +0x3c010800, 0xac230a70, 0x3c010800, 0xac230a74, +0xa0001df, 0xae020000, 0x3c100800, 0x26100a78, +0x8e020000, 0x18400028, 0x0, 0xe000201, +0x0, 0x14400024, 0x0, 0x8e020000, +0x3c030800, 0x8c630a70, 0x2442ffff, 0xafa3001c, +0x18400006, 0xae020000, 0x31402, 0x221c0, +0x8c820004, 0x3c010800, 0xac220a70, 0x97a2001e, +0x2442ff00, 0x2c420300, 0x1440000b, 0x24024000, +0x3c040800, 0x248409dc, 0xafa00010, 0xafa00014, +0x8fa6001c, 0x24050008, 0xe000060, 0x3821, +0xa0001df, 0x0, 0xaf825cf8, 0x3c020800, +0x8c420a40, 0x8fa3001c, 0x24420001, 0xaf835cf8, +0x3c010800, 0xac220a40, 0x8fbf0024, 0x8fb00020, +0x3e00008, 0x27bd0028, 0x27bdffe0, 0x3c040800, +0x248409e8, 0x2821, 0x3021, 0x3821, +0xafbf0018, 0xafa00010, 0xe000060, 0xafa00014, +0x8fbf0018, 0x3e00008, 0x27bd0020, 0x8f82680c, +0x8f85680c, 0x21827, 0x3182b, 0x31823, +0x431024, 0x441021, 0xa2282b, 0x10a00006, +0x0, 0x401821, 0x8f82680c, 0x43102b, +0x1440fffd, 0x0, 0x3e00008, 0x0, +0x3c040800, 0x8c840000, 0x3c030800, 0x8c630a40, +0x64102b, 0x54400002, 0x831023, 0x641023, +0x2c420008, 0x3e00008, 0x38420001, 0x27bdffe0, +0x802821, 0x3c040800, 0x24840a00, 0x3021, +0x3821, 0xafbf0018, 0xafa00010, 0xe000060, +0xafa00014, 0xa000216, 0x0, 0x8fbf0018, +0x3e00008, 0x27bd0020, 0x0, 0x27bdffe0, +0x3c1cc000, 0xafbf0018, 0xe00004c, 0xaf80680c, +0x3c040800, 0x24840a10, 0x3802821, 0x3021, +0x3821, 0xafa00010, 0xe000060, 0xafa00014, +0x2402ffff, 0xaf825404, 0x3c0200aa, 0xe000234, +0xaf825434, 0x8fbf0018, 0x3e00008, 0x27bd0020, +0x0, 0x0, 0x0, 0x27bdffe8, +0xafb00010, 0x24100001, 0xafbf0014, 0x3c01c003, +0xac200000, 0x8f826810, 0x30422000, 0x10400003, +0x0, 0xe000246, 0x0, 0xa00023a, +0xaf905428, 0x8fbf0014, 0x8fb00010, 0x3e00008, +0x27bd0018, 0x27bdfff8, 0x8f845d0c, 0x3c0200ff, +0x3c030800, 0x8c630a50, 0x3442fff8, 0x821024, +0x1043001e, 0x3c0500ff, 0x34a5fff8, 0x3c06c003, +0x3c074000, 0x851824, 0x8c620010, 0x3c010800, +0xac230a50, 0x30420008, 0x10400005, 0x871025, +0x8cc20000, 0x24420001, 0xacc20000, 0x871025, +0xaf825d0c, 0x8fa20000, 0x24420001, 0xafa20000, +0x8fa20000, 0x8fa20000, 0x24420001, 0xafa20000, +0x8fa20000, 0x8f845d0c, 0x3c030800, 0x8c630a50, +0x851024, 0x1443ffe8, 0x851824, 0x27bd0008, +0x3e00008, 0x0, 0x0, 0x0 }; +U32 t3FwRodata[(0x60/4) + 1] = { +0x35373031, 0x726c7341, 0x0, +0x0, 0x53774576, 0x656e7430, 0x0, +0x726c7045, 0x76656e74, 0x31000000, 0x556e6b6e, +0x45766e74, 0x0, 0x0, 0x0, +0x0, 0x66617461, 0x6c457272, 0x0, +0x0, 0x4d61696e, 0x43707542, 0x0, +0x0, 0x0 }; +U32 t3FwData[(0x20/4) + 1] = { +0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, +0x0, 0x0 }; diff --git a/roms/u-boot-sam460ex/drivers/net/8390.h b/roms/u-boot-sam460ex/drivers/net/8390.h new file mode 100644 index 000000000..f087217ed --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/8390.h @@ -0,0 +1,124 @@ +/* + +Ported to U-Boot by Christian Pellegrin <chri@ascensit.com> + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +*/ + +/* Generic NS8390 register definitions. */ +/* This file is part of Donald Becker's 8390 drivers, and is distributed + under the same license. Auto-loading of 8390.o only in v2.2 - Paul G. + Some of these names and comments originated from the Crynwr + packet drivers, which are distributed under the GPL. */ + +#ifndef _8390_h +#define _8390_h + +/* Some generic ethernet register configurations. */ +#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */ +#define E8390_RX_IRQ_MASK 0x5 +#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */ +#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */ +#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */ +#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */ + +/* Register accessed at EN_CMD, the 8390 base addr. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ + +/* + * Only generate indirect loads given a machine that needs them. + * - removed AMIGA_PCMCIA from this list, handled as ISA io now + */ + +#define n2k_inb(port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE))) +#define n2k_outb(val,port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE)) = val) + +#define EI_SHIFT(x) (x) + +#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */ +#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */ +#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */ +#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */ +#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */ +#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */ +#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */ +#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */ +#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */ +#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */ +#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */ +#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */ +#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */ +#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */ +#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */ +#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */ +#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */ +#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */ +#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */ +#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */ +#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */ +#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */ +#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */ +#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */ +#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ + +/* Bits in EN0_DCFG - Data config register */ +#define ENDCFG_WTS 0x01 /* word transfer mode selection */ +#define ENDCFG_BOS 0x02 /* byte order selection */ +#define ENDCFG_AUTO_INIT 0x10 /* Auto-init to remove packets from ring */ +#define ENDCFG_FIFO 0x40 /* 8 bytes */ + +/* Page 1 register offsets. */ +#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */ +#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */ +#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */ +#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */ +#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ + +#define NIC_RECEIVE_MONITOR_MODE 0x20 + +#endif /* _8390_h */ diff --git a/roms/u-boot-sam460ex/drivers/net/Makefile b/roms/u-boot-sam460ex/drivers/net/Makefile new file mode 100644 index 000000000..b75c02f8c --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/Makefile @@ -0,0 +1,97 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libnet.a + +COBJS-$(CONFIG_DRIVER_3C589) += 3c589.o +COBJS-$(CONFIG_PPC4xx_EMAC) += 4xx_enet.o +COBJS-$(CONFIG_ALTERA_TSE) += altera_tse.o +COBJS-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o +COBJS-$(CONFIG_DRIVER_AX88180) += ax88180.o +COBJS-$(CONFIG_BCM570x) += bcm570x.o bcm570x_autoneg.o 5701rls.o +COBJS-$(CONFIG_BFIN_MAC) += bfin_mac.o +COBJS-$(CONFIG_CS8900) += cs8900.o +COBJS-$(CONFIG_TULIP) += dc2114x.o +COBJS-$(CONFIG_DRIVER_DM9000) += dm9000x.o +COBJS-$(CONFIG_DNET) += dnet.o +COBJS-$(CONFIG_E1000) += e1000.o +COBJS-$(CONFIG_EEPRO100) += eepro100.o +COBJS-$(CONFIG_ENC28J60) += enc28j60.o +COBJS-$(CONFIG_EP93XX) += ep93xx_eth.o +COBJS-$(CONFIG_ETHOC) += ethoc.o +COBJS-$(CONFIG_FEC_MXC) += fec_mxc.o +COBJS-$(CONFIG_FSLDMAFEC) += fsl_mcdmafec.o mcfmii.o +COBJS-$(CONFIG_FTMAC100) += ftmac100.o +COBJS-$(CONFIG_GRETH) += greth.o +COBJS-$(CONFIG_INCA_IP_SWITCH) += inca-ip_sw.o +COBJS-$(CONFIG_KIRKWOOD_EGIGA) += kirkwood_egiga.o +COBJS-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o +COBJS-$(CONFIG_LAN91C96) += lan91c96.o +COBJS-$(CONFIG_MACB) += macb.o +COBJS-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o +COBJS-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o +COBJS-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o +COBJS-$(CONFIG_NATSEMI) += natsemi.o +COBJS-$(CONFIG_DRIVER_NE2000) += ne2000.o ne2000_base.o +COBJS-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o +COBJS-$(CONFIG_DRIVER_NETARMETH) += netarm_eth.o +COBJS-$(CONFIG_NETCONSOLE) += netconsole.o +COBJS-$(CONFIG_DRIVER_NS7520_ETHERNET) += ns7520_eth.o +COBJS-$(CONFIG_NS8382X) += ns8382x.o +COBJS-$(CONFIG_DRIVER_NS9750_ETHERNET) += ns9750_eth.o +COBJS-$(CONFIG_PCNET) += pcnet.o +COBJS-$(CONFIG_PLB2800_ETHER) += plb2800_eth.o +COBJS-$(CONFIG_DRIVER_RTL8019) += rtl8019.o +COBJS-$(CONFIG_RTL8139) += rtl8139.o +COBJS-$(CONFIG_RTL8169) += rtl8169.o +COBJS-$(CONFIG_DRIVER_S3C4510_ETH) += s3c4510b_eth.o +COBJS-$(CONFIG_SH_ETHER) += sh_eth.o +COBJS-$(CONFIG_SMC91111) += smc91111.o +COBJS-$(CONFIG_SMC911X) += smc911x.o +COBJS-$(CONFIG_TIGON3) += tigon3.o bcm570x_autoneg.o 5701rls.o +COBJS-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o +COBJS-$(CONFIG_TSEC_ENET) += tsec.o +COBJS-$(CONFIG_TSI108_ETH) += tsi108_eth.o +COBJS-$(CONFIG_ULI526X) += uli526x.o +COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o +COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/roms/u-boot-sam460ex/drivers/net/altera_tse.c b/roms/u-boot-sam460ex/drivers/net/altera_tse.c new file mode 100644 index 000000000..5c0c274ba --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/altera_tse.c @@ -0,0 +1,942 @@ +/* + * Altera 10/100/1000 triple speed ethernet mac driver + * + * Copyright (C) 2008 Altera Corporation. + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <command.h> +#include <asm/cache.h> +#include <asm/dma-mapping.h> +#include <miiphy.h> +#include "altera_tse.h" + +/* sgdma debug - print descriptor */ +static void alt_sgdma_print_desc(volatile struct alt_sgdma_descriptor *desc) +{ + debug("SGDMA DEBUG :\n"); + debug("desc->source : 0x%x \n", (unsigned int)desc->source); + debug("desc->destination : 0x%x \n", (unsigned int)desc->destination); + debug("desc->next : 0x%x \n", (unsigned int)desc->next); + debug("desc->source_pad : 0x%x \n", (unsigned int)desc->source_pad); + debug("desc->destination_pad : 0x%x \n", + (unsigned int)desc->destination_pad); + debug("desc->next_pad : 0x%x \n", (unsigned int)desc->next_pad); + debug("desc->bytes_to_transfer : 0x%x \n", + (unsigned int)desc->bytes_to_transfer); + debug("desc->actual_bytes_transferred : 0x%x \n", + (unsigned int)desc->actual_bytes_transferred); + debug("desc->descriptor_status : 0x%x \n", + (unsigned int)desc->descriptor_status); + debug("desc->descriptor_control : 0x%x \n", + (unsigned int)desc->descriptor_control); +} + +/* This is a generic routine that the SGDMA mode-specific routines + * call to populate a descriptor. + * arg1 :pointer to first SGDMA descriptor. + * arg2 :pointer to next SGDMA descriptor. + * arg3 :Address to where data to be written. + * arg4 :Address from where data to be read. + * arg5 :no of byte to transaction. + * arg6 :variable indicating to generate start of packet or not + * arg7 :read fixed + * arg8 :write fixed + * arg9 :read burst + * arg10 :write burst + * arg11 :atlantic_channel number + */ +static void alt_sgdma_construct_descriptor_burst( + volatile struct alt_sgdma_descriptor *desc, + volatile struct alt_sgdma_descriptor *next, + unsigned int *read_addr, + unsigned int *write_addr, + unsigned short length_or_eop, + int generate_eop, + int read_fixed, + int write_fixed_or_sop, + int read_burst, + int write_burst, + unsigned char atlantic_channel) +{ + /* + * Mark the "next" descriptor as "not" owned by hardware. This prevents + * The SGDMA controller from continuing to process the chain. This is + * done as a single IO write to bypass cache, without flushing + * the entire descriptor, since only the 8-bit descriptor status must + * be flushed. + */ + if (!next) + debug("Next descriptor not defined!!\n"); + + next->descriptor_control = (next->descriptor_control & + ~ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK); + + desc->source = (unsigned int *)((unsigned int)read_addr & 0x1FFFFFFF); + desc->destination = + (unsigned int *)((unsigned int)write_addr & 0x1FFFFFFF); + desc->next = (unsigned int *)((unsigned int)next & 0x1FFFFFFF); + desc->source_pad = 0x0; + desc->destination_pad = 0x0; + desc->next_pad = 0x0; + desc->bytes_to_transfer = length_or_eop; + desc->actual_bytes_transferred = 0; + desc->descriptor_status = 0x0; + + /* SGDMA burst not currently supported */ + desc->read_burst = 0; + desc->write_burst = 0; + + /* + * Set the descriptor control block as follows: + * - Set "owned by hardware" bit + * - Optionally set "generate EOP" bit + * - Optionally set the "read from fixed address" bit + * - Optionally set the "write to fixed address bit (which serves + * serves as a "generate SOP" control bit in memory-to-stream mode). + * - Set the 4-bit atlantic channel, if specified + * + * Note this step is performed after all other descriptor information + * has been filled out so that, if the controller already happens to be + * pointing at this descriptor, it will not run (via the "owned by + * hardware" bit) until all other descriptor has been set up. + */ + + desc->descriptor_control = + ((ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK) | + (generate_eop ? + ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK : 0x0) | + (read_fixed ? + ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK : 0x0) | + (write_fixed_or_sop ? + ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK : 0x0) | + (atlantic_channel ? ((atlantic_channel & 0x0F) << 3) : 0) + ); +} + +static int alt_sgdma_do_sync_transfer(volatile struct alt_sgdma_registers *dev, + volatile struct alt_sgdma_descriptor *desc) +{ + unsigned int status; + int counter = 0; + + /* Wait for any pending transfers to complete */ + alt_sgdma_print_desc(desc); + status = dev->status; + + counter = 0; + while (dev->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + debug("Timeout waiting sgdma in do sync!\n"); + + /* + * Clear any (previous) status register information + * that might occlude our error checking later. + */ + dev->status = 0xFF; + + /* Point the controller at the descriptor */ + dev->next_descriptor_pointer = (unsigned int)desc & 0x1FFFFFFF; + debug("next desc in sgdma 0x%x\n", + (unsigned int)dev->next_descriptor_pointer); + + /* + * Set up SGDMA controller to: + * - Disable interrupt generation + * - Run once a valid descriptor is written to controller + * - Stop on an error with any particular descriptor + */ + dev->control = (ALT_SGDMA_CONTROL_RUN_MSK | + ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK); + + /* Wait for the descriptor (chain) to complete */ + status = dev->status; + debug("wait for sgdma...."); + while (dev->status & ALT_SGDMA_STATUS_BUSY_MSK) + ; + debug("done\n"); + + /* Clear Run */ + dev->control = (dev->control & (~ALT_SGDMA_CONTROL_RUN_MSK)); + + /* Get & clear status register contents */ + status = dev->status; + dev->status = 0xFF; + + /* we really should check if the transfer completes properly */ + debug("tx sgdma status = 0x%x", status); + return 0; +} + +static int alt_sgdma_do_async_transfer(volatile struct alt_sgdma_registers *dev, + volatile struct alt_sgdma_descriptor *desc) +{ + unsigned int status; + int counter = 0; + + /* Wait for any pending transfers to complete */ + alt_sgdma_print_desc(desc); + status = dev->status; + + counter = 0; + while (dev->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + debug("Timeout waiting sgdma in do async!\n"); + + /* + * Clear any (previous) status register information + * that might occlude our error checking later. + */ + dev->status = 0xFF; + + /* Point the controller at the descriptor */ + dev->next_descriptor_pointer = (unsigned int)desc & 0x1FFFFFFF; + + /* + * Set up SGDMA controller to: + * - Disable interrupt generation + * - Run once a valid descriptor is written to controller + * - Stop on an error with any particular descriptor + */ + dev->control = (ALT_SGDMA_CONTROL_RUN_MSK | + ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK); + + /* we really should check if the transfer completes properly */ + return 0; +} + +/* u-boot interface */ +static int tse_adjust_link(struct altera_tse_priv *priv) +{ + unsigned int refvar; + + refvar = priv->mac_dev->command_config.image; + + if (!(priv->duplexity)) + refvar |= ALTERA_TSE_CMD_HD_ENA_MSK; + else + refvar &= ~ALTERA_TSE_CMD_HD_ENA_MSK; + + switch (priv->speed) { + case 1000: + refvar |= ALTERA_TSE_CMD_ETH_SPEED_MSK; + refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK; + break; + case 100: + refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK; + refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK; + break; + case 10: + refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK; + refvar |= ALTERA_TSE_CMD_ENA_10_MSK; + break; + } + priv->mac_dev->command_config.image = refvar; + + return 0; +} + +static int tse_eth_send(struct eth_device *dev, + volatile void *packet, int length) +{ + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx; + volatile struct alt_sgdma_descriptor *tx_desc = + (volatile struct alt_sgdma_descriptor *)priv->tx_desc; + + volatile struct alt_sgdma_descriptor *tx_desc_cur = + (volatile struct alt_sgdma_descriptor *)&tx_desc[0]; + + flush_dcache((unsigned long)packet, length); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&tx_desc[0], + (volatile struct alt_sgdma_descriptor *)&tx_desc[1], + (unsigned int *)packet, /* read addr */ + (unsigned int *)0, + length, /* length or EOP ,will change for each tx */ + 0x1, /* gen eop */ + 0x0, /* read fixed */ + 0x1, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + debug("TX Packet @ 0x%x,0x%x bytes", (unsigned int)packet, length); + + /* send the packet */ + debug("sending packet\n"); + alt_sgdma_do_sync_transfer(tx_sgdma, tx_desc_cur); + debug("sent %d bytes\n", tx_desc_cur->actual_bytes_transferred); + return tx_desc_cur->actual_bytes_transferred; +} + +static int tse_eth_rx(struct eth_device *dev) +{ + int packet_length = 0; + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_sgdma_descriptor *rx_desc = + (volatile struct alt_sgdma_descriptor *)priv->rx_desc; + volatile struct alt_sgdma_descriptor *rx_desc_cur = &rx_desc[0]; + + if (rx_desc_cur->descriptor_status & + ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK) { + debug("got packet\n"); + packet_length = rx_desc->actual_bytes_transferred; + NetReceive(NetRxPackets[0], packet_length); + + /* start descriptor again */ + flush_dcache((unsigned long)(NetRxPackets[0]), PKTSIZE_ALIGN); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&rx_desc[0], + (volatile struct alt_sgdma_descriptor *)&rx_desc[1], + (unsigned int)0x0, /* read addr */ + (unsigned int *)NetRxPackets[0], + 0x0, /* length or EOP */ + 0x0, /* gen eop */ + 0x0, /* read fixed */ + 0x0, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + + /* setup the sgdma */ + alt_sgdma_do_async_transfer(priv->sgdma_rx, &rx_desc[0]); + } + + return -1; +} + +static void tse_eth_halt(struct eth_device *dev) +{ + /* don't do anything! */ + /* this gets called after each uboot */ + /* network command. don't need to reset the thing all of the time */ +} + +static void tse_eth_reset(struct eth_device *dev) +{ + /* stop sgdmas, disable tse receive */ + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_tse_mac *mac_dev = priv->mac_dev; + volatile struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx; + volatile struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx; + int counter; + volatile struct alt_sgdma_descriptor *rx_desc = + (volatile struct alt_sgdma_descriptor *)&priv->rx_desc[0]; + + /* clear rx desc & wait for sgdma to complete */ + rx_desc->descriptor_control = 0; + rx_sgdma->control = 0; + counter = 0; + while (rx_sgdma->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) { + debug("Timeout waiting for rx sgdma!\n"); + rx_sgdma->control &= ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + rx_sgdma->control &= ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + } + + counter = 0; + tx_sgdma->control = 0; + while (tx_sgdma->status & ALT_SGDMA_STATUS_BUSY_MSK) { + if (counter++ > ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR) { + debug("Timeout waiting for tx sgdma!\n"); + tx_sgdma->control &= ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + tx_sgdma->control &= ALT_SGDMA_CONTROL_SOFTWARERESET_MSK; + } + /* reset the mac */ + mac_dev->command_config.bits.transmit_enable = 1; + mac_dev->command_config.bits.receive_enable = 1; + mac_dev->command_config.bits.software_reset = 1; + + counter = 0; + while (mac_dev->command_config.bits.software_reset) { + if (counter++ > ALT_TSE_SW_RESET_WATCHDOG_CNTR) + break; + } + + if (counter >= ALT_TSE_SW_RESET_WATCHDOG_CNTR) + debug("TSEMAC SW reset bit never cleared!\n"); +} + +static int tse_mdio_read(struct altera_tse_priv *priv, unsigned int regnum) +{ + volatile struct alt_tse_mac *mac_dev; + unsigned int *mdio_regs; + unsigned int data; + u16 value; + + mac_dev = priv->mac_dev; + + /* set mdio address */ + mac_dev->mdio_phy1_addr = priv->phyaddr; + mdio_regs = (unsigned int *)&mac_dev->mdio_phy1; + + /* get the data */ + data = mdio_regs[regnum]; + + value = data & 0xffff; + + return value; +} + +static int tse_mdio_write(struct altera_tse_priv *priv, unsigned int regnum, + unsigned int value) +{ + volatile struct alt_tse_mac *mac_dev; + unsigned int *mdio_regs; + unsigned int data; + + mac_dev = priv->mac_dev; + + /* set mdio address */ + mac_dev->mdio_phy1_addr = priv->phyaddr; + mdio_regs = (unsigned int *)&mac_dev->mdio_phy1; + + /* get the data */ + data = (unsigned int)value; + + mdio_regs[regnum] = data; + + return 0; +} + +/* MDIO access to phy */ +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII) +static int altera_tse_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct eth_device *dev; + struct altera_tse_priv *priv; + dev = eth_get_dev_by_name(devname); + priv = dev->priv; + + tse_mdio_write(priv, (uint) reg, (uint) value); + + return 0; +} + +static int altera_tse_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + struct eth_device *dev; + struct altera_tse_priv *priv; + volatile struct alt_tse_mac *mac_dev; + unsigned int *mdio_regs; + + dev = eth_get_dev_by_name(devname); + priv = dev->priv; + + mac_dev = priv->mac_dev; + mac_dev->mdio_phy1_addr = (int)addr; + mdio_regs = (unsigned int *)&mac_dev->mdio_phy1; + + *value = 0xffff & mdio_regs[reg]; + + return 0; + +} +#endif + +/* + * Also copied from tsec.c + */ +/* Parse the status register for link, and then do + * auto-negotiation + */ +static uint mii_parse_sr(uint mii_reg, struct altera_tse_priv *priv) +{ + /* + * Wait if the link is up, and autonegotiation is in progress + * (ie - we're capable and it's not done) + */ + mii_reg = tse_mdio_read(priv, MIIM_STATUS); + + if (!(mii_reg & MIIM_STATUS_LINK) && (mii_reg & PHY_BMSR_AUTN_ABLE) + && !(mii_reg & PHY_BMSR_AUTN_COMP)) { + int i = 0; + + puts("Waiting for PHY auto negotiation to complete"); + while (!(mii_reg & PHY_BMSR_AUTN_COMP)) { + /* + * Timeout reached ? + */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + priv->link = 0; + return 0; + } + + if ((i++ % 1000) == 0) + putc('.'); + udelay(1000); /* 1 ms */ + mii_reg = tse_mdio_read(priv, MIIM_STATUS); + } + puts(" done\n"); + priv->link = 1; + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_STATUS_LINK) { + debug("Link is up\n"); + priv->link = 1; + } else { + debug("Link is down\n"); + priv->link = 0; + } + } + + return 0; +} + +/* Parse the 88E1011's status register for speed and duplex + * information + */ +static uint mii_parse_88E1011_psr(uint mii_reg, struct altera_tse_priv *priv) +{ + uint speed; + + mii_reg = tse_mdio_read(priv, MIIM_88E1011_PHY_STATUS); + + if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) && + !(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { + int i = 0; + + puts("Waiting for PHY realtime link"); + while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { + /* Timeout reached ? */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + priv->link = 0; + break; + } + + if ((i++ == 1000) == 0) { + i = 0; + puts("."); + } + udelay(1000); /* 1 ms */ + mii_reg = tse_mdio_read(priv, MIIM_88E1011_PHY_STATUS); + } + puts(" done\n"); + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_88E1011_PHYSTAT_LINK) + priv->link = 1; + else + priv->link = 0; + } + + if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX) + priv->duplexity = 1; + else + priv->duplexity = 0; + + speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED); + + switch (speed) { + case MIIM_88E1011_PHYSTAT_GBIT: + priv->speed = 1000; + debug("PHY Speed is 1000Mbit\n"); + break; + case MIIM_88E1011_PHYSTAT_100: + debug("PHY Speed is 100Mbit\n"); + priv->speed = 100; + break; + default: + debug("PHY Speed is 10Mbit\n"); + priv->speed = 10; + } + + return 0; +} + +static uint mii_m88e1111s_setmode_sr(uint mii_reg, struct altera_tse_priv *priv) +{ + uint mii_data = tse_mdio_read(priv, mii_reg); + mii_data &= 0xfff0; + mii_data |= 0xb; + return mii_data; +} + +static uint mii_m88e1111s_setmode_cr(uint mii_reg, struct altera_tse_priv *priv) +{ + uint mii_data = tse_mdio_read(priv, mii_reg); + mii_data &= ~0x82; + mii_data |= 0x82; + return mii_data; +} + +/* + * Returns which value to write to the control register. + * For 10/100, the value is slightly different + */ +static uint mii_cr_init(uint mii_reg, struct altera_tse_priv *priv) +{ + return MIIM_CONTROL_INIT; +} + +/* + * PHY & MDIO code + * Need to add SGMII stuff + * + */ + +static struct phy_info phy_info_M88E1111S = { + 0x01410cc, + "Marvell 88E1111S", + 4, + (struct phy_cmd[]){ /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_88E1111_PHY_EXT_SR, 0x848f, + &mii_m88e1111s_setmode_sr}, + /* Delay RGMII TX and RX */ + {MIIM_88E1111_PHY_EXT_CR, 0x0cd2, + &mii_m88e1111s_setmode_cr}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]){ /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_88E1011_PHY_STATUS, miim_read, + &mii_parse_88E1011_psr}, + {miim_end,} + }, + (struct phy_cmd[]){ /* shutdown */ + {miim_end,} + }, +}; + +/* a generic flavor. */ +static struct phy_info phy_info_generic = { + 0, + "Unknown/Generic PHY", + 32, + (struct phy_cmd[]){ /* config */ + {PHY_BMCR, PHY_BMCR_RESET, NULL}, + {PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG, NULL}, + {miim_end,} + }, + (struct phy_cmd[]){ /* startup */ + {PHY_BMSR, miim_read, NULL}, + {PHY_BMSR, miim_read, &mii_parse_sr}, + {miim_end,} + }, + (struct phy_cmd[]){ /* shutdown */ + {miim_end,} + } +}; + +static struct phy_info *phy_info[] = { + &phy_info_M88E1111S, + NULL +}; + + /* Grab the identifier of the device's PHY, and search through + * all of the known PHYs to see if one matches. If so, return + * it, if not, return NULL + */ +static struct phy_info *get_phy_info(struct eth_device *dev) +{ + struct altera_tse_priv *priv = (struct altera_tse_priv *)dev->priv; + uint phy_reg, phy_ID; + int i; + struct phy_info *theInfo = NULL; + + /* Grab the bits from PHYIR1, and put them in the upper half */ + phy_reg = tse_mdio_read(priv, MIIM_PHYIR1); + phy_ID = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = tse_mdio_read(priv, MIIM_PHYIR2); + phy_ID |= (phy_reg & 0xffff); + + /* loop through all the known PHY types, and find one that */ + /* matches the ID we read from the PHY. */ + for (i = 0; phy_info[i]; i++) { + if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift)) { + theInfo = phy_info[i]; + break; + } + } + + if (theInfo == NULL) { + theInfo = &phy_info_generic; + debug("%s: No support for PHY id %x; assuming generic\n", + dev->name, phy_ID); + } else + debug("%s: PHY is %s (%x)\n", dev->name, theInfo->name, phy_ID); + + return theInfo; +} + +/* Execute the given series of commands on the given device's + * PHY, running functions as necessary + */ +static void phy_run_commands(struct altera_tse_priv *priv, struct phy_cmd *cmd) +{ + int i; + uint result; + + for (i = 0; cmd->mii_reg != miim_end; i++) { + if (cmd->mii_data == miim_read) { + result = tse_mdio_read(priv, cmd->mii_reg); + + if (cmd->funct != NULL) + (*(cmd->funct)) (result, priv); + + } else { + if (cmd->funct != NULL) + result = (*(cmd->funct)) (cmd->mii_reg, priv); + else + result = cmd->mii_data; + + tse_mdio_write(priv, cmd->mii_reg, result); + + } + cmd++; + } +} + +/* Phy init code */ +static int init_phy(struct eth_device *dev) +{ + struct altera_tse_priv *priv = (struct altera_tse_priv *)dev->priv; + struct phy_info *curphy; + + /* Get the cmd structure corresponding to the attached + * PHY */ + curphy = get_phy_info(dev); + + if (curphy == NULL) { + priv->phyinfo = NULL; + debug("%s: No PHY found\n", dev->name); + + return 0; + } else + debug("%s found\n", curphy->name); + priv->phyinfo = curphy; + + phy_run_commands(priv, priv->phyinfo->config); + + return 1; +} + +static int tse_set_mac_address(struct eth_device *dev) +{ + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_tse_mac *mac_dev = priv->mac_dev; + + debug("Setting MAC address to 0x%02x%02x%02x%02x%02x%02x\n", + dev->enetaddr[5], dev->enetaddr[4], + dev->enetaddr[3], dev->enetaddr[2], + dev->enetaddr[1], dev->enetaddr[0]); + mac_dev->mac_addr_0 = ((dev->enetaddr[3]) << 24 | + (dev->enetaddr[2]) << 16 | + (dev->enetaddr[1]) << 8 | (dev->enetaddr[0])); + + mac_dev->mac_addr_1 = ((dev->enetaddr[5] << 8 | + (dev->enetaddr[4])) & 0xFFFF); + + /* Set the MAC address */ + mac_dev->supp_mac_addr_0_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_0_1 = mac_dev->mac_addr_1; + + /* Set the MAC address */ + mac_dev->supp_mac_addr_1_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_1_1 = mac_dev->mac_addr_1; + + /* Set the MAC address */ + mac_dev->supp_mac_addr_2_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_2_1 = mac_dev->mac_addr_1; + + /* Set the MAC address */ + mac_dev->supp_mac_addr_3_0 = mac_dev->mac_addr_0; + mac_dev->supp_mac_addr_3_1 = mac_dev->mac_addr_1; + return 0; +} + +static int tse_eth_init(struct eth_device *dev, bd_t * bd) +{ + int dat; + struct altera_tse_priv *priv = dev->priv; + volatile struct alt_tse_mac *mac_dev = priv->mac_dev; + volatile struct alt_sgdma_descriptor *tx_desc = priv->tx_desc; + volatile struct alt_sgdma_descriptor *rx_desc = priv->rx_desc; + volatile struct alt_sgdma_descriptor *rx_desc_cur = + (volatile struct alt_sgdma_descriptor *)&rx_desc[0]; + + /* stop controller */ + debug("Reseting TSE & SGDMAs\n"); + tse_eth_reset(dev); + + /* start the phy */ + debug("Configuring PHY\n"); + phy_run_commands(priv, priv->phyinfo->startup); + + /* need to create sgdma */ + debug("Configuring tx desc\n"); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&tx_desc[0], + (volatile struct alt_sgdma_descriptor *)&tx_desc[1], + (unsigned int *)NULL, /* read addr */ + (unsigned int *)0, + 0, /* length or EOP ,will change for each tx */ + 0x1, /* gen eop */ + 0x0, /* read fixed */ + 0x1, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + debug("Configuring rx desc\n"); + flush_dcache((unsigned long)(NetRxPackets[0]), PKTSIZE_ALIGN); + alt_sgdma_construct_descriptor_burst( + (volatile struct alt_sgdma_descriptor *)&rx_desc[0], + (volatile struct alt_sgdma_descriptor *)&rx_desc[1], + (unsigned int)0x0, /* read addr */ + (unsigned int *)NetRxPackets[0], + 0x0, /* length or EOP */ + 0x0, /* gen eop */ + 0x0, /* read fixed */ + 0x0, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + /* start rx async transfer */ + debug("Starting rx sgdma\n"); + alt_sgdma_do_async_transfer(priv->sgdma_rx, rx_desc_cur); + + /* start TSE */ + debug("Configuring TSE Mac\n"); + /* Initialize MAC registers */ + mac_dev->max_frame_length = PKTSIZE_ALIGN; + mac_dev->rx_almost_empty_threshold = 8; + mac_dev->rx_almost_full_threshold = 8; + mac_dev->tx_almost_empty_threshold = 8; + mac_dev->tx_almost_full_threshold = 3; + mac_dev->tx_sel_empty_threshold = + CONFIG_SYS_ALTERA_TSE_TX_FIFO - 16; + mac_dev->tx_sel_full_threshold = 0; + mac_dev->rx_sel_empty_threshold = + CONFIG_SYS_ALTERA_TSE_TX_FIFO - 16; + mac_dev->rx_sel_full_threshold = 0; + + /* NO Shift */ + mac_dev->rx_cmd_stat.bits.rx_shift16 = 0; + mac_dev->tx_cmd_stat.bits.tx_shift16 = 0; + + /* enable MAC */ + dat = 0; + dat = ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK; + + mac_dev->command_config.image = dat; + + /* configure the TSE core */ + /* -- output clocks, */ + /* -- and later config stuff for SGMII */ + if (priv->link) { + debug("Adjusting TSE to link speed\n"); + tse_adjust_link(priv); + } + + return priv->link ? 0 : -1; +} + +/* TSE init code */ +int altera_tse_initialize(u8 dev_num, int mac_base, + int sgdma_rx_base, int sgdma_tx_base) +{ + struct altera_tse_priv *priv; + struct eth_device *dev; + struct alt_sgdma_descriptor *rx_desc; + struct alt_sgdma_descriptor *tx_desc; + unsigned long dma_handle; + + dev = (struct eth_device *)malloc(sizeof *dev); + + if (NULL == dev) + return 0; + + memset(dev, 0, sizeof *dev); + + priv = malloc(sizeof(*priv)); + + if (!priv) { + free(dev); + return 0; + } + tx_desc = dma_alloc_coherent(sizeof(*tx_desc) * (3 + PKTBUFSRX), + &dma_handle); + rx_desc = tx_desc + 2; + debug("tx desc: address = 0x%x\n", (unsigned int)tx_desc); + debug("rx desc: address = 0x%x\n", (unsigned int)rx_desc); + + if (!tx_desc) { + free(priv); + free(dev); + return 0; + } + memset(rx_desc, 0, (sizeof *rx_desc) * (PKTBUFSRX + 1)); + memset(tx_desc, 0, (sizeof *tx_desc) * 2); + + /* initialize tse priv */ + priv->mac_dev = (volatile struct alt_tse_mac *)mac_base; + priv->sgdma_rx = (volatile struct alt_sgdma_registers *)sgdma_rx_base; + priv->sgdma_tx = (volatile struct alt_sgdma_registers *)sgdma_tx_base; + priv->phyaddr = CONFIG_SYS_ALTERA_TSE_PHY_ADDR; + priv->flags = CONFIG_SYS_ALTERA_TSE_FLAGS; + priv->rx_desc = rx_desc; + priv->tx_desc = tx_desc; + + /* init eth structure */ + dev->priv = priv; + dev->init = tse_eth_init; + dev->halt = tse_eth_halt; + dev->send = tse_eth_send; + dev->recv = tse_eth_rx; + dev->write_hwaddr = tse_set_mac_address; + sprintf(dev->name, "%s-%hu", "ALTERA_TSE", dev_num); + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII) + miiphy_register(dev->name, altera_tse_miiphy_read, + altera_tse_miiphy_write); +#endif + + init_phy(dev); + + return 1; +} diff --git a/roms/u-boot-sam460ex/drivers/net/altera_tse.h b/roms/u-boot-sam460ex/drivers/net/altera_tse.h new file mode 100644 index 000000000..c1cb79e88 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/altera_tse.h @@ -0,0 +1,494 @@ +/* + * Altera 10/100/1000 triple speed ethernet mac + * + * Copyright (C) 2008 Altera Corporation. + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _ALTERA_TSE_H_ +#define _ALTERA_TSE_H_ + +#define __packed_1_ __attribute__ ((packed, aligned(1))) + +/* PHY Stuff */ +#define miim_end -2 +#define miim_read -1 + +#define PHY_AUTONEGOTIATE_TIMEOUT 5000 /* in ms */ + +#ifndef CONFIG_SYS_TBIPA_VALUE +#define CONFIG_SYS_TBIPA_VALUE 0x1f +#endif +#define MIIMCFG_INIT_VALUE 0x00000003 +#define MIIMCFG_RESET 0x80000000 + +#define MIIMIND_BUSY 0x00000001 +#define MIIMIND_NOTVALID 0x00000004 + +#define MIIM_CONTROL 0x00 +#define MIIM_CONTROL_RESET 0x00009140 +#define MIIM_CONTROL_INIT 0x00001140 +#define MIIM_CONTROL_RESTART 0x00001340 +#define MIIM_ANEN 0x00001000 + +#define MIIM_CR 0x00 +#define MIIM_CR_RST 0x00008000 +#define MIIM_CR_INIT 0x00001000 + +#define MIIM_STATUS 0x1 +#define MIIM_STATUS_AN_DONE 0x00000020 +#define MIIM_STATUS_LINK 0x0004 +#define PHY_BMSR_AUTN_ABLE 0x0008 +#define PHY_BMSR_AUTN_COMP 0x0020 + +#define MIIM_PHYIR1 0x2 +#define MIIM_PHYIR2 0x3 + +#define MIIM_ANAR 0x4 +#define MIIM_ANAR_INIT 0x1e1 + +#define MIIM_TBI_ANLPBPA 0x5 +#define MIIM_TBI_ANLPBPA_HALF 0x00000040 +#define MIIM_TBI_ANLPBPA_FULL 0x00000020 + +#define MIIM_TBI_ANEX 0x6 +#define MIIM_TBI_ANEX_NP 0x00000004 +#define MIIM_TBI_ANEX_PRX 0x00000002 + +#define MIIM_GBIT_CONTROL 0x9 +#define MIIM_GBIT_CONTROL_INIT 0xe00 + +#define MIIM_EXT_PAGE_ACCESS 0x1f + +/* 88E1011 PHY Status Register */ +#define MIIM_88E1011_PHY_STATUS 0x11 +#define MIIM_88E1011_PHYSTAT_SPEED 0xc000 +#define MIIM_88E1011_PHYSTAT_GBIT 0x8000 +#define MIIM_88E1011_PHYSTAT_100 0x4000 +#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000 +#define MIIM_88E1011_PHYSTAT_SPDDONE 0x0800 +#define MIIM_88E1011_PHYSTAT_LINK 0x0400 + +#define MIIM_88E1011_PHY_SCR 0x10 +#define MIIM_88E1011_PHY_MDI_X_AUTO 0x0060 + +#define MIIM_88E1111_PHY_EXT_CR 0x14 +#define MIIM_88E1111_PHY_EXT_SR 0x1b + +/* 88E1111 PHY LED Control Register */ +#define MIIM_88E1111_PHY_LED_CONTROL 24 +#define MIIM_88E1111_PHY_LED_DIRECT 0x4100 +#define MIIM_88E1111_PHY_LED_COMBINE 0x411C + +#define MIIM_READ_COMMAND 0x00000001 + +/* struct phy_info: a structure which defines attributes for a PHY + * id will contain a number which represents the PHY. During + * startup, the driver will poll the PHY to find out what its + * UID--as defined by registers 2 and 3--is. The 32-bit result + * gotten from the PHY will be shifted right by "shift" bits to + * discard any bits which may change based on revision numbers + * unimportant to functionality + * + * The struct phy_cmd entries represent pointers to an arrays of + * commands which tell the driver what to do to the PHY. + */ +struct phy_info { + uint id; + char *name; + uint shift; + /* Called to configure the PHY, and modify the controller + * based on the results */ + struct phy_cmd *config; + + /* Called when starting up the controller */ + struct phy_cmd *startup; + + /* Called when bringing down the controller */ + struct phy_cmd *shutdown; +}; + +/* SGDMA Stuff */ +#define ALT_SGDMA_STATUS_ERROR_MSK (0x00000001) +#define ALT_SGDMA_STATUS_EOP_ENCOUNTERED_MSK (0x00000002) +#define ALT_SGDMA_STATUS_DESC_COMPLETED_MSK (0x00000004) +#define ALT_SGDMA_STATUS_CHAIN_COMPLETED_MSK (0x00000008) +#define ALT_SGDMA_STATUS_BUSY_MSK (0x00000010) + +#define ALT_SGDMA_CONTROL_IE_ERROR_MSK (0x00000001) +#define ALT_SGDMA_CONTROL_IE_EOP_ENCOUNTERED_MSK (0x00000002) +#define ALT_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK (0x00000004) +#define ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK (0x00000008) +#define ALT_SGDMA_CONTROL_IE_GLOBAL_MSK (0x00000010) +#define ALT_SGDMA_CONTROL_RUN_MSK (0x00000020) +#define ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK (0x00000040) +#define ALT_SGDMA_CONTROL_IE_MAX_DESC_PROCESSED_MSK (0x00000080) +#define ALT_SGDMA_CONTROL_MAX_DESC_PROCESSED_MSK (0x0000FF00) +#define ALT_SGDMA_CONTROL_SOFTWARERESET_MSK (0x00010000) +#define ALT_SGDMA_CONTROL_PARK_MSK (0x00020000) +#define ALT_SGDMA_CONTROL_CLEAR_INTERRUPT_MSK (0x80000000) + +#define ALTERA_TSE_SGDMA_INTR_MASK (ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK \ + | ALT_SGDMA_STATUS_DESC_COMPLETED_MSK \ + | ALT_SGDMA_CONTROL_IE_GLOBAL_MSK) + +/* + * Descriptor control bit masks & offsets + * + * Note: The control byte physically occupies bits [31:24] in memory. + * The following bit-offsets are expressed relative to the LSB of + * the control register bitfield. + */ +#define ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK (0x00000001) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK (0x00000002) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK (0x00000004) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_ATLANTIC_CHANNEL_MSK (0x00000008) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK (0x00000080) + +/* + * Descriptor status bit masks & offsets + * + * Note: The status byte physically occupies bits [23:16] in memory. + * The following bit-offsets are expressed relative to the LSB of + * the status register bitfield. + */ +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_CRC_MSK (0x00000001) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_PARITY_MSK (0x00000002) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_OVERFLOW_MSK (0x00000004) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_SYNC_MSK (0x00000008) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_UEOP_MSK (0x00000010) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MEOP_MSK (0x00000020) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MSOP_MSK (0x00000040) +#define ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK (0x00000080) +#define ALT_SGDMA_DESCRIPTOR_STATUS_ERROR_MSK (0x0000007F) + +/* + * The SGDMA controller buffer descriptor allocates + * 64 bits for each address. To support ANSI C, the + * struct implementing a descriptor places 32-bits + * of padding directly above each address; each pad must + * be cleared when initializing a descriptor. + */ + +/* + * Buffer Descriptor data structure + * + */ +struct alt_sgdma_descriptor { + unsigned int *source; /* the address of data to be read. */ + unsigned int source_pad; + + unsigned int *destination; /* the address to write data */ + unsigned int destination_pad; + + unsigned int *next; /* the next descriptor in the list. */ + unsigned int next_pad; + + unsigned short bytes_to_transfer; /* the number of bytes to transfer */ + unsigned char read_burst; + unsigned char write_burst; + + unsigned short actual_bytes_transferred;/* bytes transferred by DMA */ + unsigned char descriptor_status; + unsigned char descriptor_control; + +} __packed_1_; + +/* SG-DMA Control/Status Slave registers map */ + +struct alt_sgdma_registers { + unsigned int status; + unsigned int status_pad[3]; + unsigned int control; + unsigned int control_pad[3]; + unsigned int next_descriptor_pointer; + unsigned int descriptor_pad[3]; +}; + +/* TSE Stuff */ +#define ALTERA_TSE_CMD_TX_ENA_MSK (0x00000001) +#define ALTERA_TSE_CMD_RX_ENA_MSK (0x00000002) +#define ALTERA_TSE_CMD_XON_GEN_MSK (0x00000004) +#define ALTERA_TSE_CMD_ETH_SPEED_MSK (0x00000008) +#define ALTERA_TSE_CMD_PROMIS_EN_MSK (0x00000010) +#define ALTERA_TSE_CMD_PAD_EN_MSK (0x00000020) +#define ALTERA_TSE_CMD_CRC_FWD_MSK (0x00000040) +#define ALTERA_TSE_CMD_PAUSE_FWD_MSK (0x00000080) +#define ALTERA_TSE_CMD_PAUSE_IGNORE_MSK (0x00000100) +#define ALTERA_TSE_CMD_TX_ADDR_INS_MSK (0x00000200) +#define ALTERA_TSE_CMD_HD_ENA_MSK (0x00000400) +#define ALTERA_TSE_CMD_EXCESS_COL_MSK (0x00000800) +#define ALTERA_TSE_CMD_LATE_COL_MSK (0x00001000) +#define ALTERA_TSE_CMD_SW_RESET_MSK (0x00002000) +#define ALTERA_TSE_CMD_MHASH_SEL_MSK (0x00004000) +#define ALTERA_TSE_CMD_LOOPBACK_MSK (0x00008000) +/* Bits (18:16) = address select */ +#define ALTERA_TSE_CMD_TX_ADDR_SEL_MSK (0x00070000) +#define ALTERA_TSE_CMD_MAGIC_ENA_MSK (0x00080000) +#define ALTERA_TSE_CMD_SLEEP_MSK (0x00100000) +#define ALTERA_TSE_CMD_WAKEUP_MSK (0x00200000) +#define ALTERA_TSE_CMD_XOFF_GEN_MSK (0x00400000) +#define ALTERA_TSE_CMD_CNTL_FRM_ENA_MSK (0x00800000) +#define ALTERA_TSE_CMD_NO_LENGTH_CHECK_MSK (0x01000000) +#define ALTERA_TSE_CMD_ENA_10_MSK (0x02000000) +#define ALTERA_TSE_CMD_RX_ERR_DISC_MSK (0x04000000) +/* Bits (30..27) reserved */ +#define ALTERA_TSE_CMD_CNT_RESET_MSK (0x80000000) + +#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 (0x00040000) +#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC (0x00020000) + +#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 (0x02000000) + +#define ALT_TSE_SW_RESET_WATCHDOG_CNTR 10000 +#define ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR 90000000 + +/* Command_Config Register Bit Definitions */ + +typedef volatile union __alt_tse_command_config { + unsigned int image; + struct { + unsigned int + transmit_enable:1, /* bit 0 */ + receive_enable:1, /* bit 1 */ + pause_frame_xon_gen:1, /* bit 2 */ + ethernet_speed:1, /* bit 3 */ + promiscuous_enable:1, /* bit 4 */ + pad_enable:1, /* bit 5 */ + crc_forward:1, /* bit 6 */ + pause_frame_forward:1, /* bit 7 */ + pause_frame_ignore:1, /* bit 8 */ + set_mac_address_on_tx:1, /* bit 9 */ + halfduplex_enable:1, /* bit 10 */ + excessive_collision:1, /* bit 11 */ + late_collision:1, /* bit 12 */ + software_reset:1, /* bit 13 */ + multicast_hash_mode_sel:1, /* bit 14 */ + loopback_enable:1, /* bit 15 */ + src_mac_addr_sel_on_tx:3, /* bit 18:16 */ + magic_packet_detect:1, /* bit 19 */ + sleep_mode_enable:1, /* bit 20 */ + wake_up_request:1, /* bit 21 */ + pause_frame_xoff_gen:1, /* bit 22 */ + control_frame_enable:1, /* bit 23 */ + payload_len_chk_disable:1, /* bit 24 */ + enable_10mbps_intf:1, /* bit 25 */ + rx_error_discard_enable:1, /* bit 26 */ + reserved_bits:4, /* bit 30:27 */ + self_clear_counter_reset:1; /* bit 31 */ + } __packed_1_ bits; +} __packed_1_ alt_tse_command_config; + +/* Tx_Cmd_Stat Register Bit Definitions */ + +typedef volatile union __alt_tse_tx_cmd_stat { + unsigned int image; + struct { + unsigned int reserved_lsbs:17, /* bit 16:0 */ + omit_crc:1, /* bit 17 */ + tx_shift16:1, /* bit 18 */ + reserved_msbs:13; /* bit 31:19 */ + + } __packed_1_ bits; +} alt_tse_tx_cmd_stat; + +/* Rx_Cmd_Stat Register Bit Definitions */ + +typedef volatile union __alt_tse_rx_cmd_stat { + unsigned int image; + struct { + unsigned int reserved_lsbs:25, /* bit 24:0 */ + rx_shift16:1, /* bit 25 */ + reserved_msbs:6; /* bit 31:26 */ + + } __packed_1_ bits; +} alt_tse_rx_cmd_stat; + +struct alt_tse_mdio { + unsigned int control; /*PHY device operation control register */ + unsigned int status; /*PHY device operation status register */ + unsigned int phy_id1; /*Bits 31:16 of PHY identifier. */ + unsigned int phy_id2; /*Bits 15:0 of PHY identifier. */ + unsigned int auto_negotiation_advertisement; + unsigned int remote_partner_base_page_ability; + + unsigned int reg6; + unsigned int reg7; + unsigned int reg8; + unsigned int reg9; + unsigned int rega; + unsigned int regb; + unsigned int regc; + unsigned int regd; + unsigned int rege; + unsigned int regf; + unsigned int reg10; + unsigned int reg11; + unsigned int reg12; + unsigned int reg13; + unsigned int reg14; + unsigned int reg15; + unsigned int reg16; + unsigned int reg17; + unsigned int reg18; + unsigned int reg19; + unsigned int reg1a; + unsigned int reg1b; + unsigned int reg1c; + unsigned int reg1d; + unsigned int reg1e; + unsigned int reg1f; +}; + +/* MAC register Space */ + +struct alt_tse_mac { + unsigned int megacore_revision; + unsigned int scratch_pad; + alt_tse_command_config command_config; + unsigned int mac_addr_0; + unsigned int mac_addr_1; + unsigned int max_frame_length; + unsigned int pause_quanta; + unsigned int rx_sel_empty_threshold; + unsigned int rx_sel_full_threshold; + unsigned int tx_sel_empty_threshold; + unsigned int tx_sel_full_threshold; + unsigned int rx_almost_empty_threshold; + unsigned int rx_almost_full_threshold; + unsigned int tx_almost_empty_threshold; + unsigned int tx_almost_full_threshold; + unsigned int mdio_phy0_addr; + unsigned int mdio_phy1_addr; + + /* only if 100/1000 BaseX PCS, reserved otherwise */ + unsigned int reservedx44[5]; + + unsigned int reg_read_access_status; + unsigned int min_tx_ipg_length; + + /* IEEE 802.3 oEntity Managed Object Support */ + unsigned int aMACID_1; /*The MAC addresses */ + unsigned int aMACID_2; + unsigned int aFramesTransmittedOK; + unsigned int aFramesReceivedOK; + unsigned int aFramesCheckSequenceErrors; + unsigned int aAlignmentErrors; + unsigned int aOctetsTransmittedOK; + unsigned int aOctetsReceivedOK; + + /* IEEE 802.3 oPausedEntity Managed Object Support */ + unsigned int aTxPAUSEMACCtrlFrames; + unsigned int aRxPAUSEMACCtrlFrames; + + /* IETF MIB (MIB-II) Object Support */ + unsigned int ifInErrors; + unsigned int ifOutErrors; + unsigned int ifInUcastPkts; + unsigned int ifInMulticastPkts; + unsigned int ifInBroadcastPkts; + unsigned int ifOutDiscards; + unsigned int ifOutUcastPkts; + unsigned int ifOutMulticastPkts; + unsigned int ifOutBroadcastPkts; + + /* IETF RMON MIB Object Support */ + unsigned int etherStatsDropEvent; + unsigned int etherStatsOctets; + unsigned int etherStatsPkts; + unsigned int etherStatsUndersizePkts; + unsigned int etherStatsOversizePkts; + unsigned int etherStatsPkts64Octets; + unsigned int etherStatsPkts65to127Octets; + unsigned int etherStatsPkts128to255Octets; + unsigned int etherStatsPkts256to511Octets; + unsigned int etherStatsPkts512to1023Octets; + unsigned int etherStatsPkts1024to1518Octets; + + unsigned int etherStatsPkts1519toXOctets; + unsigned int etherStatsJabbers; + unsigned int etherStatsFragments; + + unsigned int reservedxE4; + + /*FIFO control register. */ + alt_tse_tx_cmd_stat tx_cmd_stat; + alt_tse_rx_cmd_stat rx_cmd_stat; + + unsigned int ipaccTxConf; + unsigned int ipaccRxConf; + unsigned int ipaccRxStat; + unsigned int ipaccRxStatSum; + + /*Multicast address resolution table */ + unsigned int hash_table[64]; + + /*Registers 0 to 31 within PHY device 0/1 */ + struct alt_tse_mdio mdio_phy0; + struct alt_tse_mdio mdio_phy1; + + /*4 Supplemental MAC Addresses */ + unsigned int supp_mac_addr_0_0; + unsigned int supp_mac_addr_0_1; + unsigned int supp_mac_addr_1_0; + unsigned int supp_mac_addr_1_1; + unsigned int supp_mac_addr_2_0; + unsigned int supp_mac_addr_2_1; + unsigned int supp_mac_addr_3_0; + unsigned int supp_mac_addr_3_1; + + unsigned int reservedx320[56]; +}; + +/* flags: TSE MII modes */ +/* GMII/MII = 0 */ +/* RGMII = 1 */ +/* RGMII_ID = 2 */ +/* RGMII_TXID = 3 */ +/* RGMII_RXID = 4 */ +/* SGMII = 5 */ +struct altera_tse_priv { + char devname[16]; + volatile struct alt_tse_mac *mac_dev; + volatile struct alt_sgdma_registers *sgdma_rx; + volatile struct alt_sgdma_registers *sgdma_tx; + unsigned int rx_sgdma_irq; + unsigned int tx_sgdma_irq; + unsigned int has_descriptor_mem; + unsigned int descriptor_mem_base; + unsigned int descriptor_mem_size; + volatile struct alt_sgdma_descriptor *rx_desc; + volatile struct alt_sgdma_descriptor *tx_desc; + volatile unsigned char *rx_buf; + struct phy_info *phyinfo; + unsigned int phyaddr; + unsigned int flags; + unsigned int link; + unsigned int duplexity; + unsigned int speed; +}; + +/* Phy stuff continued */ +/* + * struct phy_cmd: A command for reading or writing a PHY register + * + * mii_reg: The register to read or write + * + * mii_data: For writes, the value to put in the register. + * A value of -1 indicates this is a read. + * + * funct: A function pointer which is invoked for each command. + * For reads, this function will be passed the value read + * from the PHY, and process it. + * For writes, the result of this function will be written + * to the PHY register + */ +struct phy_cmd { + uint mii_reg; + uint mii_data; + uint(*funct) (uint mii_reg, struct altera_tse_priv *priv); +}; +#endif /* _ALTERA_TSE_H_ */ diff --git a/roms/u-boot-sam460ex/drivers/net/at91_emac.c b/roms/u-boot-sam460ex/drivers/net/at91_emac.c new file mode 100644 index 000000000..239956998 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/at91_emac.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2009 BuS Elektronik GmbH & Co. KG + * Jens Scharsig (esw@bus-elektronik.de) + * + * (C) Copyright 2003 + * Author : Hamid Ikdoumi (Atmel) + + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#ifndef CONFIG_AT91_LEGACY +#include <asm/arch/hardware.h> +#include <asm/arch/at91_emac.h> +#include <asm/arch/at91_pmc.h> +#include <asm/arch/at91_pio.h> +#else +/* remove next 5 lines, if all RM9200 boards convert to at91 arch */ +#include <asm/arch-at91/at91rm9200.h> +#include <asm/arch-at91/hardware.h> +#include <asm/arch-at91/at91_emac.h> +#include <asm/arch-at91/at91_pmc.h> +#include <asm/arch-at91/at91_pio.h> +#endif +#include <net.h> +#include <netdev.h> +#include <malloc.h> +#include <miiphy.h> +#include <linux/mii.h> + +#undef MII_DEBUG +#undef ET_DEBUG + +#if (CONFIG_SYS_RX_ETH_BUFFER > 1024) +#error AT91 EMAC supports max 1024 RX buffers. \ + Please decrease the CONFIG_SYS_RX_ETH_BUFFER value +#endif + +/* MDIO clock must not exceed 2.5 MHz, so enable MCK divider */ +#if (AT91C_MASTER_CLOCK > 80000000) + #define HCLK_DIV AT91_EMAC_CFG_MCLK_64 +#elif (AT91C_MASTER_CLOCK > 40000000) + #define HCLK_DIV AT91_EMAC_CFG_MCLK_32 +#elif (AT91C_MASTER_CLOCK > 20000000) + #define HCLK_DIV AT91_EMAC_CFG_MCLK_16 +#else + #define HCLK_DIV AT91_EMAC_CFG_MCLK_8 +#endif + +#ifdef ET_DEBUG +#define DEBUG_AT91EMAC(...) printf(__VA_ARGS__); +#else +#define DEBUG_AT91EMAC(...) +#endif + +#ifdef MII_DEBUG +#define DEBUG_AT91PHY(...) printf(__VA_ARGS__); +#else +#define DEBUG_AT91PHY(...) +#endif + +#ifndef CONFIG_DRIVER_AT91EMAC_QUIET +#define VERBOSEP(...) printf(__VA_ARGS__); +#else +#define VERBOSEP(...) +#endif + +#define RBF_ADDR 0xfffffffc +#define RBF_OWNER (1<<0) +#define RBF_WRAP (1<<1) +#define RBF_BROADCAST (1<<31) +#define RBF_MULTICAST (1<<30) +#define RBF_UNICAST (1<<29) +#define RBF_EXTERNAL (1<<28) +#define RBF_UNKOWN (1<<27) +#define RBF_SIZE 0x07ff +#define RBF_LOCAL4 (1<<26) +#define RBF_LOCAL3 (1<<25) +#define RBF_LOCAL2 (1<<24) +#define RBF_LOCAL1 (1<<23) + +#define RBF_FRAMEMAX CONFIG_SYS_RX_ETH_BUFFER +#define RBF_FRAMELEN 0x600 + +typedef struct { + unsigned long addr, size; +} rbf_t; + +typedef struct { + rbf_t rbfdt[RBF_FRAMEMAX]; + unsigned long rbindex; +} emac_device; + +void at91emac_EnableMDIO(at91_emac_t *at91mac) +{ + /* Mac CTRL reg set for MDIO enable */ + writel(readl(&at91mac->ctl) | AT91_EMAC_CTL_MPE, &at91mac->ctl); +} + +void at91emac_DisableMDIO(at91_emac_t *at91mac) +{ + /* Mac CTRL reg set for MDIO disable */ + writel(readl(&at91mac->ctl) & ~AT91_EMAC_CTL_MPE, &at91mac->ctl); +} + +int at91emac_read(at91_emac_t *at91mac, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + at91emac_EnableMDIO(at91mac); + + writel(AT91_EMAC_MAN_HIGH | AT91_EMAC_MAN_RW_R | + AT91_EMAC_MAN_REGA(reg) | AT91_EMAC_MAN_CODE_802_3 | + AT91_EMAC_MAN_PHYA(addr), + &at91mac->man); + udelay(10000); + *value = readl(&at91mac->man) & AT91_EMAC_MAN_DATA_MASK; + + at91emac_DisableMDIO(at91mac); + + DEBUG_AT91PHY("AT91PHY read %x REG(%d)=%x\n", at91mac, reg, *value) + + return 0; +} + +int at91emac_write(at91_emac_t *at91mac, unsigned char addr, + unsigned char reg, unsigned short value) +{ + DEBUG_AT91PHY("AT91PHY write %x REG(%d)=%x\n", at91mac, reg, &value) + + at91emac_EnableMDIO(at91mac); + + writel(AT91_EMAC_MAN_HIGH | AT91_EMAC_MAN_RW_W | + AT91_EMAC_MAN_REGA(reg) | AT91_EMAC_MAN_CODE_802_3 | + AT91_EMAC_MAN_PHYA(addr) | (value & AT91_EMAC_MAN_DATA_MASK), + &at91mac->man); + udelay(10000); + + at91emac_DisableMDIO(at91mac); + return 0; +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + +at91_emac_t *get_emacbase_by_name(char *devname) +{ + struct eth_device *netdev; + + netdev = eth_get_dev_by_name(devname); + return (at91_emac_t *) netdev->iobase; +} + +int at91emac_mii_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + at91_emac_t *emac; + + emac = get_emacbase_by_name(devname); + at91emac_read(emac , addr, reg, value); + return 0; +} + + +int at91emac_mii_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + at91_emac_t *emac; + + emac = get_emacbase_by_name(devname); + at91emac_write(emac, addr, reg, value); + return 0; +} + +#endif + +static int at91emac_phy_reset(struct eth_device *netdev) +{ + int i; + u16 status, adv; + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + + adv = ADVERTISE_CSMA | ADVERTISE_ALL; + at91emac_write(emac, 0, MII_ADVERTISE, adv); + VERBOSEP("%s: Starting autonegotiation...\n", netdev->name); + at91emac_write(emac, 0, MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART)); + + for (i = 0; i < 100000 / 100; i++) { + at91emac_read(emac, 0, MII_BMSR, &status); + if (status & BMSR_ANEGCOMPLETE) + break; + udelay(100); + } + + if (status & BMSR_ANEGCOMPLETE) { + VERBOSEP("%s: Autonegotiation complete\n", netdev->name); + } else { + printf("%s: Autonegotiation timed out (status=0x%04x)\n", + netdev->name, status); + return 1; + } + return 0; +} + +static int at91emac_phy_init(struct eth_device *netdev) +{ + u16 phy_id, status, adv, lpa; + int media, speed, duplex; + int i; + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + + /* Check if the PHY is up to snuff... */ + at91emac_read(emac, 0, MII_PHYSID1, &phy_id); + if (phy_id == 0xffff) { + printf("%s: No PHY present\n", netdev->name); + return 1; + } + + at91emac_read(emac, 0, MII_BMSR, &status); + + if (!(status & BMSR_LSTATUS)) { + /* Try to re-negotiate if we don't have link already. */ + if (at91emac_phy_reset(netdev)) + return 2; + + for (i = 0; i < 100000 / 100; i++) { + at91emac_read(emac, 0, MII_BMSR, &status); + if (status & BMSR_LSTATUS) + break; + udelay(100); + } + } + if (!(status & BMSR_LSTATUS)) { + VERBOSEP("%s: link down\n", netdev->name); + return 3; + } else { + at91emac_read(emac, 0, MII_ADVERTISE, &adv); + at91emac_read(emac, 0, MII_LPA, &lpa); + media = mii_nway_result(lpa & adv); + speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) + ? 1 : 0); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + VERBOSEP("%s: link up, %sMbps %s-duplex\n", + netdev->name, + speed ? "100" : "10", + duplex ? "full" : "half"); + } + return 0; +} + +int at91emac_UpdateLinkSpeed(at91_emac_t *emac) +{ + unsigned short stat1; + + at91emac_read(emac, 0, MII_BMSR, &stat1); + + if (!(stat1 & BMSR_LSTATUS)) /* link status up? */ + return 1; + + if (stat1 & BMSR_100FULL) { + /*set Emac for 100BaseTX and Full Duplex */ + writel(readl(&emac->cfg) | + AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD, + &emac->cfg); + return 0; + } + + if (stat1 & BMSR_10FULL) { + /*set MII for 10BaseT and Full Duplex */ + writel((readl(&emac->cfg) & + ~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD) + ) | AT91_EMAC_CFG_FD, + &emac->cfg); + return 0; + } + + if (stat1 & BMSR_100HALF) { + /*set MII for 100BaseTX and Half Duplex */ + writel((readl(&emac->cfg) & + ~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD) + ) | AT91_EMAC_CFG_SPD, + &emac->cfg); + return 0; + } + + if (stat1 & BMSR_10HALF) { + /*set MII for 10BaseT and Half Duplex */ + writel((readl(&emac->cfg) & + ~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD)), + &emac->cfg); + return 0; + } + return 1; +} + +static int at91emac_init(struct eth_device *netdev, bd_t *bd) +{ + int i; + u32 value; + emac_device *dev; + at91_emac_t *emac; + at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE; + at91_pmc_t *pmc = (at91_pmc_t *) AT91_PMC_BASE; + + emac = (at91_emac_t *) netdev->iobase; + dev = (emac_device *) netdev->priv; + + /* PIO Disable Register */ + value = AT91_PMX_AA_EMDIO | AT91_PMX_AA_EMDC | + AT91_PMX_AA_ERXER | AT91_PMX_AA_ERX1 | + AT91_PMX_AA_ERX0 | AT91_PMX_AA_ECRS | + AT91_PMX_AA_ETX1 | AT91_PMX_AA_ETX0 | + AT91_PMX_AA_ETXEN | AT91_PMX_AA_EREFCK; + + writel(value, &pio->pioa.pdr); + writel(value, &pio->pioa.asr); + +#ifdef CONFIG_RMII + value = AT91_PMX_BA_ERXCK; +#else + value = AT91_PMX_BA_ERXCK | AT91_PMX_BA_ECOL | + AT91_PMX_BA_ERXDV | AT91_PMX_BA_ERX3 | + AT91_PMX_BA_ERX2 | AT91_PMX_BA_ETXER | + AT91_PMX_BA_ETX3 | AT91_PMX_BA_ETX2; +#endif + writel(value, &pio->piob.pdr); + writel(value, &pio->piob.bsr); + + writel(1 << AT91_ID_EMAC, &pmc->pcer); + writel(readl(&emac->ctl) | AT91_EMAC_CTL_CSR, &emac->ctl); + + DEBUG_AT91EMAC("init MAC-ADDR %x%x \n", + cpu_to_le16(*((u16 *)(netdev->enetaddr + 4))), + cpu_to_le32(*((u32 *)netdev->enetaddr))); + writel(cpu_to_le32(*((u32 *)netdev->enetaddr)), &emac->sa2l); + writel(cpu_to_le16(*((u16 *)(netdev->enetaddr + 4))), &emac->sa2h); + DEBUG_AT91EMAC("init MAC-ADDR %x%x \n", + readl(&emac->sa2h), readl(&emac->sa2l)); + + /* Init Ethernet buffers */ + for (i = 0; i < RBF_FRAMEMAX; i++) { + dev->rbfdt[i].addr = (unsigned long) NetRxPackets[i]; + dev->rbfdt[i].size = 0; + } + dev->rbfdt[RBF_FRAMEMAX - 1].addr |= RBF_WRAP; + dev->rbindex = 0; + writel((u32) &(dev->rbfdt[0]), &emac->rbqp); + + writel(readl(&emac->rsr) & + ~(AT91_EMAC_RSR_OVR | AT91_EMAC_RSR_REC | AT91_EMAC_RSR_BNA), + &emac->rsr); + + value = AT91_EMAC_CFG_CAF | AT91_EMAC_CFG_NBC | + HCLK_DIV; +#ifdef CONFIG_RMII + value |= AT91C_EMAC_RMII; +#endif + writel(value, &emac->cfg); + + writel(readl(&emac->ctl) | AT91_EMAC_CTL_TE | AT91_EMAC_CTL_RE, + &emac->ctl); + + if (!at91emac_phy_init(netdev)) { + at91emac_UpdateLinkSpeed(emac); + return 0; + } + return 1; +} + +static void at91emac_halt(struct eth_device *netdev) +{ + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + writel(readl(&emac->ctl) & ~(AT91_EMAC_CTL_TE | AT91_EMAC_CTL_RE), + &emac->ctl); + DEBUG_AT91EMAC("halt MAC\n"); +} + +static int at91emac_send(struct eth_device *netdev, volatile void *packet, + int length) +{ + at91_emac_t *emac; + + emac = (at91_emac_t *) netdev->iobase; + + while (!(readl(&emac->tsr) & AT91_EMAC_TSR_BNQ)) + ; + writel((u32) packet, &emac->tar); + writel(AT91_EMAC_TCR_LEN(length), &emac->tcr); + while (AT91_EMAC_TCR_LEN(readl(&emac->tcr))) + ; + DEBUG_AT91EMAC("Send %d \n", length); + writel(readl(&emac->tsr) | AT91_EMAC_TSR_COMP, &emac->tsr); + return 0; +} + +static int at91emac_recv(struct eth_device *netdev) +{ + emac_device *dev; + at91_emac_t *emac; + rbf_t *rbfp; + int size; + + emac = (at91_emac_t *) netdev->iobase; + dev = (emac_device *) netdev->priv; + + rbfp = &dev->rbfdt[dev->rbindex]; + while (rbfp->addr & RBF_OWNER) { + size = rbfp->size & RBF_SIZE; + NetReceive(NetRxPackets[dev->rbindex], size); + + DEBUG_AT91EMAC("Recv[%d]: %d bytes @ %x \n", + dev->rbindex, size, rbfp->addr); + + rbfp->addr &= ~RBF_OWNER; + rbfp->size = 0; + if (dev->rbindex < (RBF_FRAMEMAX-1)) + dev->rbindex++; + else + dev->rbindex = 0; + + rbfp = &(dev->rbfdt[dev->rbindex]); + if (!(rbfp->addr & RBF_OWNER)) + writel(readl(&emac->rsr) | AT91_EMAC_RSR_REC, + &emac->rsr); + } + + if (readl(&emac->isr) & AT91_EMAC_IxR_RBNA) { + /* EMAC silicon bug 41.3.1 workaround 1 */ + writel(readl(&emac->ctl) & ~AT91_EMAC_CTL_RE, &emac->ctl); + writel(readl(&emac->ctl) | AT91_EMAC_CTL_RE, &emac->ctl); + dev->rbindex = 0; + printf("%s: reset receiver (EMAC dead lock bug)\n", + netdev->name); + } + return 0; +} + +int at91emac_register(bd_t *bis, unsigned long iobase) +{ + emac_device *emac; + emac_device *emacfix; + struct eth_device *dev; + + if (iobase == 0) + iobase = AT91_EMAC_BASE; + emac = malloc(sizeof(*emac)+512); + if (emac == NULL) + return 1; + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + free(emac); + return 1; + } + /* alignment as per Errata (64 bytes) is insufficient! */ + emacfix = (emac_device *) (((unsigned long) emac + 0x1ff) & 0xFFFFFE00); + memset(emacfix, 0, sizeof(emac_device)); + + memset(dev, 0, sizeof(*dev)); +#ifndef CONFIG_RMII + sprintf(dev->name, "AT91 EMAC"); +#else + sprintf(dev->name, "AT91 EMAC RMII"); +#endif + dev->iobase = iobase; + dev->priv = emacfix; + dev->init = at91emac_init; + dev->halt = at91emac_halt; + dev->send = at91emac_send; + dev->recv = at91emac_recv; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, at91emac_mii_read, at91emac_mii_write); +#endif + return 1; +} diff --git a/roms/u-boot-sam460ex/drivers/net/ax88180.c b/roms/u-boot-sam460ex/drivers/net/ax88180.c new file mode 100644 index 000000000..d843397f3 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ax88180.c @@ -0,0 +1,727 @@ +/* + * ax88180: ASIX AX88180 Non-PCI Gigabit Ethernet u-boot driver + * + * This program is free software; you can distribute it and/or modify + * it under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * This program is distributed in the hope it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, + * USA. + */ + +/* + * ======================================================================== + * ASIX AX88180 Non-PCI 16/32-bit Gigabit Ethernet Linux Driver + * + * The AX88180 Ethernet controller is a high performance and highly + * integrated local CPU bus Ethernet controller with embedded 40K bytes + * SRAM and supports both 16-bit and 32-bit SRAM-Like interfaces for any + * embedded systems. + * The AX88180 is a single chip 10/100/1000Mbps Gigabit Ethernet + * controller that supports both MII and RGMII interfaces and is + * compliant to IEEE 802.3, IEEE 802.3u and IEEE 802.3z standards. + * + * Please visit ASIX's web site (http://www.asix.com.tw) for more + * details. + * + * Module Name : ax88180.c + * Date : 2008-07-07 + * History + * 09/06/2006 : New release for AX88180 US2 chip. + * 07/07/2008 : Fix up the coding style and using inline functions + * instead of macros + * ======================================================================== + */ +#include <common.h> +#include <command.h> +#include <net.h> +#include <malloc.h> +#include "ax88180.h" + +/* + * =========================================================================== + * Local SubProgram Declaration + * =========================================================================== + */ +static void ax88180_rx_handler (struct eth_device *dev); +static int ax88180_phy_initial (struct eth_device *dev); +static void ax88180_meidia_config (struct eth_device *dev); +static unsigned long get_CicadaPHY_meida_mode (struct eth_device *dev); +static unsigned long get_MarvellPHY_meida_mode (struct eth_device *dev); +static unsigned short ax88180_mdio_read (struct eth_device *dev, + unsigned long regaddr); +static void ax88180_mdio_write (struct eth_device *dev, + unsigned long regaddr, unsigned short regdata); + +/* + * =========================================================================== + * Local SubProgram Bodies + * =========================================================================== + */ +static int ax88180_mdio_check_complete (struct eth_device *dev) +{ + int us_cnt = 10000; + unsigned short tmpval; + + /* MDIO read/write should not take more than 10 ms */ + while (--us_cnt) { + tmpval = INW (dev, MDIOCTRL); + if (((tmpval & READ_PHY) == 0) && ((tmpval & WRITE_PHY) == 0)) + break; + } + + return us_cnt; +} + +static unsigned short +ax88180_mdio_read (struct eth_device *dev, unsigned long regaddr) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long tmpval = 0; + + OUTW (dev, (READ_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL); + + if (ax88180_mdio_check_complete (dev)) + tmpval = INW (dev, MDIODP); + else + printf ("Failed to read PHY register!\n"); + + return (unsigned short)(tmpval & 0xFFFF); +} + +static void +ax88180_mdio_write (struct eth_device *dev, unsigned long regaddr, + unsigned short regdata) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + + OUTW (dev, regdata, MDIODP); + + OUTW (dev, (WRITE_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL); + + if (!ax88180_mdio_check_complete (dev)) + printf ("Failed to write PHY register!\n"); +} + +static int ax88180_phy_reset (struct eth_device *dev) +{ + unsigned short delay_cnt = 500; + + ax88180_mdio_write (dev, BMCR, (PHY_RESET | AUTONEG_EN)); + + /* Wait for the reset to complete, or time out (500 ms) */ + while (ax88180_mdio_read (dev, BMCR) & PHY_RESET) { + udelay (1000); + if (--delay_cnt == 0) { + printf ("Failed to reset PHY!\n"); + return -1; + } + } + + return 0; +} + +static void ax88180_mac_reset (struct eth_device *dev) +{ + unsigned long tmpval; + unsigned char i; + + struct { + unsigned short offset, value; + } program_seq[] = { + { + MISC, MISC_NORMAL}, { + RXINDICATOR, DEFAULT_RXINDICATOR}, { + TXCMD, DEFAULT_TXCMD}, { + TXBS, DEFAULT_TXBS}, { + TXDES0, DEFAULT_TXDES0}, { + TXDES1, DEFAULT_TXDES1}, { + TXDES2, DEFAULT_TXDES2}, { + TXDES3, DEFAULT_TXDES3}, { + TXCFG, DEFAULT_TXCFG}, { + MACCFG2, DEFAULT_MACCFG2}, { + MACCFG3, DEFAULT_MACCFG3}, { + TXLEN, DEFAULT_TXLEN}, { + RXBTHD0, DEFAULT_RXBTHD0}, { + RXBTHD1, DEFAULT_RXBTHD1}, { + RXFULTHD, DEFAULT_RXFULTHD}, { + DOGTHD0, DEFAULT_DOGTHD0}, { + DOGTHD1, DEFAULT_DOGTHD1},}; + + OUTW (dev, MISC_RESET_MAC, MISC); + tmpval = INW (dev, MISC); + + for (i = 0; i < (sizeof (program_seq) / sizeof (program_seq[0])); i++) + OUTW (dev, program_seq[i].value, program_seq[i].offset); +} + +static int ax88180_poll_tx_complete (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long tmpval, txbs_txdp; + int TimeOutCnt = 10000; + + txbs_txdp = 1 << priv->NextTxDesc; + + while (TimeOutCnt--) { + + tmpval = INW (dev, TXBS); + + if ((tmpval & txbs_txdp) == 0) + break; + + udelay (100); + } + + if (TimeOutCnt) + return 0; + else + return -TimeOutCnt; +} + +static void ax88180_rx_handler (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long data_size; + unsigned short rxcurt_ptr, rxbound_ptr, next_ptr; + int i; +#if defined (CONFIG_DRIVER_AX88180_16BIT) + unsigned short *rxdata = (unsigned short *)NetRxPackets[0]; +#else + unsigned long *rxdata = (unsigned long *)NetRxPackets[0]; +#endif + unsigned short count; + + rxcurt_ptr = INW (dev, RXCURT); + rxbound_ptr = INW (dev, RXBOUND); + next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK; + + debug ("ax88180: RX original RXBOUND=0x%04x," + " RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); + + while (next_ptr != rxcurt_ptr) { + + OUTW (dev, RX_START_READ, RXINDICATOR); + + data_size = READ_RXBUF (dev) & 0xFFFF; + + if ((data_size == 0) || (data_size > MAX_RX_SIZE)) { + + OUTW (dev, RX_STOP_READ, RXINDICATOR); + + ax88180_mac_reset (dev); + printf ("ax88180: Invalid Rx packet length!" + " (len=0x%04lx)\n", data_size); + + debug ("ax88180: RX RXBOUND=0x%04x," + "RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); + return; + } + + rxbound_ptr += (((data_size + 0xF) & 0xFFF0) >> 4) + 1; + rxbound_ptr &= RX_PAGE_NUM_MASK; + + /* Comput access times */ + count = (data_size + priv->PadSize) >> priv->BusWidth; + + for (i = 0; i < count; i++) { + *(rxdata + i) = READ_RXBUF (dev); + } + + OUTW (dev, RX_STOP_READ, RXINDICATOR); + + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], data_size); + + OUTW (dev, rxbound_ptr, RXBOUND); + + rxcurt_ptr = INW (dev, RXCURT); + rxbound_ptr = INW (dev, RXBOUND); + next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK; + + debug ("ax88180: RX updated RXBOUND=0x%04x," + "RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr); + } + + return; +} + +static int ax88180_phy_initial (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long tmp_regval; + + /* Check avaliable PHY chipset */ + priv->PhyAddr = MARVELL_88E1111_PHYADDR; + priv->PhyID0 = ax88180_mdio_read (dev, PHYIDR0); + + if (priv->PhyID0 == MARVELL_88E1111_PHYIDR0) { + + debug ("ax88180: Found Marvell 88E1111 PHY." + " (PHY Addr=0x%x)\n", priv->PhyAddr); + + tmp_regval = ax88180_mdio_read (dev, M88_EXT_SSR); + if ((tmp_regval & HWCFG_MODE_MASK) == RGMII_COPPER_MODE) { + + ax88180_mdio_write (dev, M88_EXT_SCR, DEFAULT_EXT_SCR); + if (ax88180_phy_reset (dev) < 0) + return 0; + ax88180_mdio_write (dev, M88_IER, LINK_CHANGE_INT); + } + } else { + + priv->PhyAddr = CICADA_CIS8201_PHYADDR; + priv->PhyID0 = ax88180_mdio_read (dev, PHYIDR0); + + if (priv->PhyID0 == CICADA_CIS8201_PHYIDR0) { + + debug ("ax88180: Found CICADA CIS8201 PHY" + " chipset. (PHY Addr=0x%x)\n", priv->PhyAddr); + ax88180_mdio_write (dev, CIS_IMR, + (CIS_INT_ENABLE | LINK_CHANGE_INT)); + + /* Set CIS_SMI_PRIORITY bit before force the media mode */ + tmp_regval = + ax88180_mdio_read (dev, CIS_AUX_CTRL_STATUS); + tmp_regval &= ~CIS_SMI_PRIORITY; + ax88180_mdio_write (dev, CIS_AUX_CTRL_STATUS, + tmp_regval); + } else { + printf ("ax88180: Unknown PHY chipset!!\n"); + return 0; + } + } + + return 1; +} + +static void ax88180_meidia_config (struct eth_device *dev) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned long bmcr_val, bmsr_val; + unsigned long rxcfg_val, maccfg0_val, maccfg1_val; + unsigned long RealMediaMode; + int i; + + /* Waiting 2 seconds for PHY link stable */ + for (i = 0; i < 20000; i++) { + bmsr_val = ax88180_mdio_read (dev, BMSR); + if (bmsr_val & LINKOK) { + break; + } + udelay (100); + } + + bmsr_val = ax88180_mdio_read (dev, BMSR); + debug ("ax88180: BMSR=0x%04x\n", (unsigned int)bmsr_val); + + if (bmsr_val & LINKOK) { + bmcr_val = ax88180_mdio_read (dev, BMCR); + + if (bmcr_val & AUTONEG_EN) { + + /* + * Waiting for Auto-negotiation completion, this may + * take up to 5 seconds. + */ + debug ("ax88180: Auto-negotiation is " + "enabled. Waiting for NWay completion..\n"); + for (i = 0; i < 50000; i++) { + bmsr_val = ax88180_mdio_read (dev, BMSR); + if (bmsr_val & AUTONEG_COMPLETE) { + break; + } + udelay (100); + } + } else + debug ("ax88180: Auto-negotiation is disabled.\n"); + + debug ("ax88180: BMCR=0x%04x, BMSR=0x%04x\n", + (unsigned int)bmcr_val, (unsigned int)bmsr_val); + + /* Get real media mode here */ + if (priv->PhyID0 == MARVELL_88E1111_PHYIDR0) { + RealMediaMode = get_MarvellPHY_meida_mode (dev); + } else if (priv->PhyID0 == CICADA_CIS8201_PHYIDR0) { + RealMediaMode = get_CicadaPHY_meida_mode (dev); + } else { + RealMediaMode = MEDIA_1000FULL; + } + + priv->LinkState = INS_LINK_UP; + + switch (RealMediaMode) { + case MEDIA_1000FULL: + debug ("ax88180: 1000Mbps Full-duplex mode.\n"); + rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; + maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0; + maccfg1_val = GIGA_MODE_EN | RXFLOW_EN | + FULLDUPLEX | DEFAULT_MACCFG1; + break; + + case MEDIA_1000HALF: + debug ("ax88180: 1000Mbps Half-duplex mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = GIGA_MODE_EN | DEFAULT_MACCFG1; + break; + + case MEDIA_100FULL: + debug ("ax88180: 100Mbps Full-duplex mode.\n"); + rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; + maccfg0_val = SPEED100 | TXFLOW_ENABLE + | DEFAULT_MACCFG0; + maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1; + break; + + case MEDIA_100HALF: + debug ("ax88180: 100Mbps Half-duplex mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = SPEED100 | DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + break; + + case MEDIA_10FULL: + debug ("ax88180: 10Mbps Full-duplex mode.\n"); + rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG; + maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0; + maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1; + break; + + case MEDIA_10HALF: + debug ("ax88180: 10Mbps Half-duplex mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + break; + default: + debug ("ax88180: Unknow media mode.\n"); + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + + priv->LinkState = INS_LINK_DOWN; + break; + } + + } else { + rxcfg_val = DEFAULT_RXCFG; + maccfg0_val = DEFAULT_MACCFG0; + maccfg1_val = DEFAULT_MACCFG1; + + priv->LinkState = INS_LINK_DOWN; + } + + OUTW (dev, rxcfg_val, RXCFG); + OUTW (dev, maccfg0_val, MACCFG0); + OUTW (dev, maccfg1_val, MACCFG1); + + return; +} + +static unsigned long get_MarvellPHY_meida_mode (struct eth_device *dev) +{ + unsigned long m88_ssr; + unsigned long MediaMode; + + m88_ssr = ax88180_mdio_read (dev, M88_SSR); + switch (m88_ssr & SSR_MEDIA_MASK) { + case SSR_1000FULL: + MediaMode = MEDIA_1000FULL; + break; + case SSR_1000HALF: + MediaMode = MEDIA_1000HALF; + break; + case SSR_100FULL: + MediaMode = MEDIA_100FULL; + break; + case SSR_100HALF: + MediaMode = MEDIA_100HALF; + break; + case SSR_10FULL: + MediaMode = MEDIA_10FULL; + break; + case SSR_10HALF: + MediaMode = MEDIA_10HALF; + break; + default: + MediaMode = MEDIA_UNKNOWN; + break; + } + + return MediaMode; +} + +static unsigned long get_CicadaPHY_meida_mode (struct eth_device *dev) +{ + unsigned long tmp_regval; + unsigned long MediaMode; + + tmp_regval = ax88180_mdio_read (dev, CIS_AUX_CTRL_STATUS); + switch (tmp_regval & CIS_MEDIA_MASK) { + case CIS_1000FULL: + MediaMode = MEDIA_1000FULL; + break; + case CIS_1000HALF: + MediaMode = MEDIA_1000HALF; + break; + case CIS_100FULL: + MediaMode = MEDIA_100FULL; + break; + case CIS_100HALF: + MediaMode = MEDIA_100HALF; + break; + case CIS_10FULL: + MediaMode = MEDIA_10FULL; + break; + case CIS_10HALF: + MediaMode = MEDIA_10HALF; + break; + default: + MediaMode = MEDIA_UNKNOWN; + break; + } + + return MediaMode; +} + +static void ax88180_halt (struct eth_device *dev) +{ + /* Disable AX88180 TX/RX functions */ + OUTW (dev, WAKEMOD, CMD); +} + +static int ax88180_init (struct eth_device *dev, bd_t * bd) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned short tmp_regval; + + ax88180_mac_reset (dev); + + /* Disable interrupt */ + OUTW (dev, CLEAR_IMR, IMR); + + /* Disable AX88180 TX/RX functions */ + OUTW (dev, WAKEMOD, CMD); + + /* Fill the MAC address */ + tmp_regval = + dev->enetaddr[0] | (((unsigned short)dev->enetaddr[1]) << 8); + OUTW (dev, tmp_regval, MACID0); + + tmp_regval = + dev->enetaddr[2] | (((unsigned short)dev->enetaddr[3]) << 8); + OUTW (dev, tmp_regval, MACID1); + + tmp_regval = + dev->enetaddr[4] | (((unsigned short)dev->enetaddr[5]) << 8); + OUTW (dev, tmp_regval, MACID2); + + ax88180_meidia_config (dev); + + OUTW (dev, DEFAULT_RXFILTER, RXFILTER); + + /* Initial variables here */ + priv->FirstTxDesc = TXDP0; + priv->NextTxDesc = TXDP0; + + /* Check if there is any invalid interrupt status and clear it. */ + OUTW (dev, INW (dev, ISR), ISR); + + /* Start AX88180 TX/RX functions */ + OUTW (dev, (RXEN | TXEN | WAKEMOD), CMD); + + return 0; +} + +/* Get a data block via Ethernet */ +static int ax88180_recv (struct eth_device *dev) +{ + unsigned short ISR_Status; + unsigned short tmp_regval; + + /* Read and check interrupt status here. */ + ISR_Status = INW (dev, ISR); + + while (ISR_Status) { + /* Clear the interrupt status */ + OUTW (dev, ISR_Status, ISR); + + debug ("\nax88180: The interrupt status = 0x%04x\n", + ISR_Status); + + if (ISR_Status & ISR_PHY) { + /* Read ISR register once to clear PHY interrupt bit */ + tmp_regval = ax88180_mdio_read (dev, M88_ISR); + ax88180_meidia_config (dev); + } + + if ((ISR_Status & ISR_RX) || (ISR_Status & ISR_RXBUFFOVR)) { + ax88180_rx_handler (dev); + } + + /* Read and check interrupt status again */ + ISR_Status = INW (dev, ISR); + } + + return 0; +} + +/* Send a data block via Ethernet. */ +static int +ax88180_send (struct eth_device *dev, volatile void *packet, int length) +{ + struct ax88180_private *priv = (struct ax88180_private *)dev->priv; + unsigned short TXDES_addr; + unsigned short txcmd_txdp, txbs_txdp; + unsigned short tmp_data; + int i; +#if defined (CONFIG_DRIVER_AX88180_16BIT) + volatile unsigned short *txdata = (volatile unsigned short *)packet; +#else + volatile unsigned long *txdata = (volatile unsigned long *)packet; +#endif + unsigned short count; + + if (priv->LinkState != INS_LINK_UP) { + return 0; + } + + priv->FirstTxDesc = priv->NextTxDesc; + txbs_txdp = 1 << priv->FirstTxDesc; + + debug ("ax88180: TXDP%d is available\n", priv->FirstTxDesc); + + txcmd_txdp = priv->FirstTxDesc << 13; + TXDES_addr = TXDES0 + (priv->FirstTxDesc << 2); + + OUTW (dev, (txcmd_txdp | length | TX_START_WRITE), TXCMD); + + /* Comput access times */ + count = (length + priv->PadSize) >> priv->BusWidth; + + for (i = 0; i < count; i++) { + WRITE_TXBUF (dev, *(txdata + i)); + } + + OUTW (dev, txcmd_txdp | length, TXCMD); + OUTW (dev, txbs_txdp, TXBS); + OUTW (dev, (TXDPx_ENABLE | length), TXDES_addr); + + priv->NextTxDesc = (priv->NextTxDesc + 1) & TXDP_MASK; + + /* + * Check the available transmit descriptor, if we had exhausted all + * transmit descriptor ,then we have to wait for at least one free + * descriptor + */ + txbs_txdp = 1 << priv->NextTxDesc; + tmp_data = INW (dev, TXBS); + + if (tmp_data & txbs_txdp) { + if (ax88180_poll_tx_complete (dev) < 0) { + ax88180_mac_reset (dev); + priv->FirstTxDesc = TXDP0; + priv->NextTxDesc = TXDP0; + printf ("ax88180: Transmit time out occurred!\n"); + } + } + + return 0; +} + +static void ax88180_read_mac_addr (struct eth_device *dev) +{ + unsigned short macid0_val, macid1_val, macid2_val; + unsigned short tmp_regval; + unsigned short i; + + /* Reload MAC address from EEPROM */ + OUTW (dev, RELOAD_EEPROM, PROMCTRL); + + /* Waiting for reload eeprom completion */ + for (i = 0; i < 500; i++) { + tmp_regval = INW (dev, PROMCTRL); + if ((tmp_regval & RELOAD_EEPROM) == 0) + break; + udelay (1000); + } + + /* Get MAC addresses */ + macid0_val = INW (dev, MACID0); + macid1_val = INW (dev, MACID1); + macid2_val = INW (dev, MACID2); + + if (((macid0_val | macid1_val | macid2_val) != 0) && + ((macid0_val & 0x01) == 0)) { + dev->enetaddr[0] = (unsigned char)macid0_val; + dev->enetaddr[1] = (unsigned char)(macid0_val >> 8); + dev->enetaddr[2] = (unsigned char)macid1_val; + dev->enetaddr[3] = (unsigned char)(macid1_val >> 8); + dev->enetaddr[4] = (unsigned char)macid2_val; + dev->enetaddr[5] = (unsigned char)(macid2_val >> 8); + } +} + +/* +=========================================================================== +<<<<<< Exported SubProgram Bodies >>>>>> +=========================================================================== +*/ +int ax88180_initialize (bd_t * bis) +{ + struct eth_device *dev; + struct ax88180_private *priv; + + dev = (struct eth_device *)malloc (sizeof *dev); + + if (NULL == dev) + return 0; + + memset (dev, 0, sizeof *dev); + + priv = (struct ax88180_private *)malloc (sizeof (*priv)); + + if (NULL == priv) + return 0; + + memset (priv, 0, sizeof *priv); + + sprintf (dev->name, "ax88180"); + dev->iobase = AX88180_BASE; + dev->priv = priv; + dev->init = ax88180_init; + dev->halt = ax88180_halt; + dev->send = ax88180_send; + dev->recv = ax88180_recv; + + priv->BusWidth = BUS_WIDTH_32; + priv->PadSize = 3; +#if defined (CONFIG_DRIVER_AX88180_16BIT) + OUTW (dev, (START_BASE >> 8), BASE); + OUTW (dev, DECODE_EN, DECODE); + + priv->BusWidth = BUS_WIDTH_16; + priv->PadSize = 1; +#endif + + ax88180_mac_reset (dev); + + /* Disable interrupt */ + OUTW (dev, CLEAR_IMR, IMR); + + /* Disable AX88180 TX/RX functions */ + OUTW (dev, WAKEMOD, CMD); + + ax88180_read_mac_addr (dev); + + eth_register (dev); + + return ax88180_phy_initial (dev); + +} diff --git a/roms/u-boot-sam460ex/drivers/net/ax88180.h b/roms/u-boot-sam460ex/drivers/net/ax88180.h new file mode 100644 index 000000000..d2113df4b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ax88180.h @@ -0,0 +1,412 @@ +/* ax88180.h: ASIX AX88180 Non-PCI Gigabit Ethernet u-boot driver */ +/* + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + */ + +#ifndef _AX88180_H_ +#define _AX88180_H_ + +#include <asm/types.h> +#include <config.h> + +typedef enum _ax88180_link_state { + INS_LINK_DOWN, + INS_LINK_UP, + INS_LINK_UNKNOWN +} ax88180_link_state; + +struct ax88180_private { + unsigned char BusWidth; + unsigned char PadSize; + unsigned short PhyAddr; + unsigned short PhyID0; + unsigned short FirstTxDesc; + unsigned short NextTxDesc; + ax88180_link_state LinkState; +}; + +#define BUS_WIDTH_16 1 +#define BUS_WIDTH_32 2 + +#define ENABLE_JUMBO 1 +#define DISABLE_JUMBO 0 + +#define ENABLE_BURST 1 +#define DISABLE_BURST 0 + +#define NORMAL_RX_MODE 0 +#define RX_LOOPBACK_MODE 1 +#define RX_INIFINIT_LOOP_MODE 2 +#define TX_INIFINIT_LOOP_MODE 3 + +#define DEFAULT_ETH_MTU 1500 + +/* Jumbo packet size 4086 bytes included 4 bytes CRC*/ +#define MAX_JUMBO_MTU 4072 + +/* Max Tx Jumbo size 4086 bytes included 4 bytes CRC */ +#define MAX_TX_JUMBO_SIZE 4086 + +/* Max Rx Jumbo size is 15K Bytes */ +#define MAX_RX_SIZE 0x3C00 + +#define MARVELL_88E1111_PHYADDR 0x18 +#define MARVELL_88E1111_PHYIDR0 0x0141 + +#define CICADA_CIS8201_PHYADDR 0x01 +#define CICADA_CIS8201_PHYIDR0 0x000F + +#define MEDIA_AUTO 0 +#define MEDIA_1000FULL 1 +#define MEDIA_1000HALF 2 +#define MEDIA_100FULL 3 +#define MEDIA_100HALF 4 +#define MEDIA_10FULL 5 +#define MEDIA_10HALF 6 +#define MEDIA_UNKNOWN 7 + +#define AUTO_MEDIA 0 +#define FORCE_MEDIA 1 + +#define TXDP_MASK 3 +#define TXDP0 0 +#define TXDP1 1 +#define TXDP2 2 +#define TXDP3 3 + +#define CMD_MAP_SIZE 0x100 + +#if defined (CONFIG_DRIVER_AX88180_16BIT) + #define AX88180_MEMORY_SIZE 0x00004000 + #define START_BASE 0x1000 + + #define RX_BUF_SIZE 0x1000 + #define TX_BUF_SIZE 0x0F00 + + #define TX_BASE START_BASE + #define CMD_BASE (TX_BASE + TX_BUF_SIZE) + #define RX_BASE (CMD_BASE + CMD_MAP_SIZE) +#else + #define AX88180_MEMORY_SIZE 0x00010000 + + #define RX_BUF_SIZE 0x8000 + #define TX_BUF_SIZE 0x7C00 + + #define RX_BASE 0x0000 + #define TX_BASE (RX_BASE + RX_BUF_SIZE) + #define CMD_BASE (TX_BASE + TX_BUF_SIZE) +#endif + +/* AX88180 Memory Mapping Definition */ +#define RXBUFFER_START RX_BASE + #define RX_PACKET_LEN_OFFSET 0 + #define RX_PAGE_NUM_MASK 0x7FF /* RX pages 0~7FFh */ +#define TXBUFFER_START TX_BASE + +/* AX88180 MAC Register Definition */ +#define DECODE (0) + #define DECODE_EN 0x00000001 +#define BASE (6) +#define CMD (CMD_BASE + 0x0000) + #define WAKEMOD 0x00000001 + #define TXEN 0x00000100 + #define RXEN 0x00000200 + #define DEFAULT_CMD WAKEMOD +#define IMR (CMD_BASE + 0x0004) + #define IMR_RXBUFFOVR 0x00000001 + #define IMR_WATCHDOG 0x00000002 + #define IMR_TX 0x00000008 + #define IMR_RX 0x00000010 + #define IMR_PHY 0x00000020 + #define CLEAR_IMR 0x00000000 + #define DEFAULT_IMR (IMR_PHY | IMR_RX | IMR_TX |\ + IMR_RXBUFFOVR | IMR_WATCHDOG) +#define ISR (CMD_BASE + 0x0008) + #define ISR_RXBUFFOVR 0x00000001 + #define ISR_WATCHDOG 0x00000002 + #define ISR_TX 0x00000008 + #define ISR_RX 0x00000010 + #define ISR_PHY 0x00000020 +#define TXCFG (CMD_BASE + 0x0010) + #define AUTOPAD_CRC 0x00000050 + #define DEFAULT_TXCFG AUTOPAD_CRC +#define TXCMD (CMD_BASE + 0x0014) + #define TXCMD_TXDP_MASK 0x00006000 + #define TXCMD_TXDP0 0x00000000 + #define TXCMD_TXDP1 0x00002000 + #define TXCMD_TXDP2 0x00004000 + #define TXCMD_TXDP3 0x00006000 + #define TX_START_WRITE 0x00008000 + #define TX_STOP_WRITE 0x00000000 + #define DEFAULT_TXCMD 0x00000000 +#define TXBS (CMD_BASE + 0x0018) + #define TXDP0_USED 0x00000001 + #define TXDP1_USED 0x00000002 + #define TXDP2_USED 0x00000004 + #define TXDP3_USED 0x00000008 + #define DEFAULT_TXBS 0x00000000 +#define TXDES0 (CMD_BASE + 0x0020) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES0 0x00000000 +#define TXDES1 (CMD_BASE + 0x0024) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES1 0x00000000 +#define TXDES2 (CMD_BASE + 0x0028) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES2 0x00000000 +#define TXDES3 (CMD_BASE + 0x002C) + #define TXDPx_ENABLE 0x00008000 + #define TXDPx_LEN_MASK 0x00001FFF + #define DEFAULT_TXDES3 0x00000000 +#define RXCFG (CMD_BASE + 0x0030) + #define RXBUFF_PROTECT 0x00000001 + #define RXTCPCRC_CHECK 0x00000010 + #define RXFLOW_ENABLE 0x00000100 + #define DEFAULT_RXCFG RXBUFF_PROTECT +#define RXCURT (CMD_BASE + 0x0034) + #define DEFAULT_RXCURT 0x00000000 +#define RXBOUND (CMD_BASE + 0x0038) + #define DEFAULT_RXBOUND 0x7FF /* RX pages 0~7FFh */ +#define MACCFG0 (CMD_BASE + 0x0040) + #define MACCFG0_BIT3_0 0x00000007 + #define IPGT_VAL 0x00000150 + #define TXFLOW_ENABLE 0x00001000 + #define SPEED100 0x00008000 + #define DEFAULT_MACCFG0 (IPGT_VAL | MACCFG0_BIT3_0) +#define MACCFG1 (CMD_BASE + 0x0044) + #define RGMII_EN 0x00000002 + #define RXFLOW_EN 0x00000020 + #define FULLDUPLEX 0x00000040 + #define MAX_JUMBO_LEN 0x00000780 + #define RXJUMBO_EN 0x00000800 + #define GIGA_MODE_EN 0x00001000 + #define RXCRC_CHECK 0x00002000 + #define RXPAUSE_DA_CHECK 0x00004000 + + #define JUMBO_LEN_4K 0x00000200 + #define JUMBO_LEN_15K 0x00000780 + #define DEFAULT_MACCFG1 (RXCRC_CHECK | RXPAUSE_DA_CHECK | \ + RGMII_EN) + #define CICADA_DEFAULT_MACCFG1 (RXCRC_CHECK | RXPAUSE_DA_CHECK) +#define MACCFG2 (CMD_BASE + 0x0048) + #define MACCFG2_BIT15_8 0x00000100 + #define JAM_LIMIT_MASK 0x000000FC + #define DEFAULT_JAM_LIMIT 0x00000064 + #define DEFAULT_MACCFG2 MACCFG2_BIT15_8 +#define MACCFG3 (CMD_BASE + 0x004C) + #define IPGR2_VAL 0x0000000E + #define IPGR1_VAL 0x00000600 + #define NOABORT 0x00008000 + #define DEFAULT_MACCFG3 (IPGR1_VAL | IPGR2_VAL) +#define TXPAUT (CMD_BASE + 0x0054) + #define DEFAULT_TXPAUT 0x001FE000 +#define RXBTHD0 (CMD_BASE + 0x0058) + #define DEFAULT_RXBTHD0 0x00000300 +#define RXBTHD1 (CMD_BASE + 0x005C) + #define DEFAULT_RXBTHD1 0x00000600 +#define RXFULTHD (CMD_BASE + 0x0060) + #define DEFAULT_RXFULTHD 0x00000100 +#define MISC (CMD_BASE + 0x0068) + /* Normal operation mode */ + #define MISC_NORMAL 0x00000003 + /* Clear bit 0 to reset MAC */ + #define MISC_RESET_MAC 0x00000002 + /* Clear bit 1 to reset PHY */ + #define MISC_RESET_PHY 0x00000001 + /* Clear bit 0 and 1 to reset MAC and PHY */ + #define MISC_RESET_MAC_PHY 0x00000000 + #define DEFAULT_MISC MISC_NORMAL +#define MACID0 (CMD_BASE + 0x0070) +#define MACID1 (CMD_BASE + 0x0074) +#define MACID2 (CMD_BASE + 0x0078) +#define TXLEN (CMD_BASE + 0x007C) + #define DEFAULT_TXLEN 0x000005FC +#define RXFILTER (CMD_BASE + 0x0080) + #define RX_RXANY 0x00000001 + #define RX_MULTICAST 0x00000002 + #define RX_UNICAST 0x00000004 + #define RX_BROADCAST 0x00000008 + #define RX_MULTI_HASH 0x00000010 + #define DISABLE_RXFILTER 0x00000000 + #define DEFAULT_RXFILTER (RX_BROADCAST + RX_UNICAST) +#define MDIOCTRL (CMD_BASE + 0x0084) + #define PHY_ADDR_MASK 0x0000001F + #define REG_ADDR_MASK 0x00001F00 + #define READ_PHY 0x00004000 + #define WRITE_PHY 0x00008000 +#define MDIODP (CMD_BASE + 0x0088) +#define GPIOCTRL (CMD_BASE + 0x008C) +#define RXINDICATOR (CMD_BASE + 0x0090) + #define RX_START_READ 0x00000001 + #define RX_STOP_READ 0x00000000 + #define DEFAULT_RXINDICATOR RX_STOP_READ +#define TXST (CMD_BASE + 0x0094) +#define MDCCLKPAT (CMD_BASE + 0x00A0) +#define RXIPCRCCNT (CMD_BASE + 0x00A4) +#define RXCRCCNT (CMD_BASE + 0x00A8) +#define TXFAILCNT (CMD_BASE + 0x00AC) +#define PROMDP (CMD_BASE + 0x00B0) +#define PROMCTRL (CMD_BASE + 0x00B4) + #define RELOAD_EEPROM 0x00000200 +#define MAXRXLEN (CMD_BASE + 0x00B8) +#define HASHTAB0 (CMD_BASE + 0x00C0) +#define HASHTAB1 (CMD_BASE + 0x00C4) +#define HASHTAB2 (CMD_BASE + 0x00C8) +#define HASHTAB3 (CMD_BASE + 0x00CC) +#define DOGTHD0 (CMD_BASE + 0x00E0) + #define DEFAULT_DOGTHD0 0x0000FFFF +#define DOGTHD1 (CMD_BASE + 0x00E4) + #define START_WATCHDOG_TIMER 0x00008000 + #define DEFAULT_DOGTHD1 0x00000FFF +#define SOFTRST (CMD_BASE + 0x00EC) + #define SOFTRST_NORMAL 0x00000003 + #define SOFTRST_RESET_MAC 0x00000002 + +/* External PHY Register Definition */ +#define BMCR 0x0000 + #define LINE_SPEED_MSB 0x0040 + #define DUPLEX_MODE 0x0100 + #define RESTART_AUTONEG 0x0200 + #define POWER_DOWN 0x0800 + #define AUTONEG_EN 0x1000 + #define LINE_SPEED_LSB 0x2000 + #define PHY_RESET 0x8000 + + #define MEDIAMODE_MASK (LINE_SPEED_MSB | LINE_SPEED_LSB |\ + DUPLEX_MODE) + #define BMCR_SPEED_1000 LINE_SPEED_MSB + #define BMCR_SPEED_100 LINE_SPEED_LSB + #define BMCR_SPEED_10 0x0000 + + #define BMCR_1000FULL (BMCR_SPEED_1000 | DUPLEX_MODE) + #define BMCR_100FULL (BMCR_SPEED_100 | DUPLEX_MODE) + #define BMCR_100HALF BMCR_SPEED_100 + #define BMCR_10FULL DUPLEX_MODE + #define BMCR_10HALF 0x0000 +#define BMSR 0x0001 + #define LINKOK 0x0004 + #define AUTONEG_ENABLE_STS 0x0008 + #define AUTONEG_COMPLETE 0x0020 +#define PHYIDR0 0x0002 +#define PHYIDR1 0x0003 +#define ANAR 0x0004 + #define ANAR_PAUSE 0x0400 + #define ANAR_100FULL 0x0100 + #define ANAR_100HALF 0x0080 + #define ANAR_10FULL 0x0040 + #define ANAR_10HALF 0x0020 + #define ANAR_8023BIT 0x0001 +#define ANLPAR 0x0005 +#define ANER 0x0006 +#define AUX_1000_CTRL 0x0009 + #define ENABLE_1000HALF 0x0100 + #define ENABLE_1000FULL 0x0200 + #define DEFAULT_AUX_1000_CTRL (ENABLE_1000HALF | ENABLE_1000FULL) +#define AUX_1000_STATUS 0x000A + #define LP_1000HALF 0x0400 + #define LP_1000FULL 0x0800 + +/* Marvell 88E1111 Gigabit PHY Register Definition */ +#define M88_SSR 0x0011 + #define SSR_SPEED_MASK 0xC000 + #define SSR_SPEED_1000 0x8000 + #define SSR_SPEED_100 0x4000 + #define SSR_SPEED_10 0x0000 + #define SSR_DUPLEX 0x2000 + #define SSR_MEDIA_RESOLVED_OK 0x0800 + + #define SSR_MEDIA_MASK (SSR_SPEED_MASK | SSR_DUPLEX) + #define SSR_1000FULL (SSR_SPEED_1000 | SSR_DUPLEX) + #define SSR_1000HALF SSR_SPEED_1000 + #define SSR_100FULL (SSR_SPEED_100 | SSR_DUPLEX) + #define SSR_100HALF SSR_SPEED_100 + #define SSR_10FULL (SSR_SPEED_10 | SSR_DUPLEX) + #define SSR_10HALF SSR_SPEED_10 +#define M88_IER 0x0012 + #define LINK_CHANGE_INT 0x0400 +#define M88_ISR 0x0013 + #define LINK_CHANGE_STATUS 0x0400 +#define M88_EXT_SCR 0x0014 + #define RGMII_RXCLK_DELAY 0x0080 + #define RGMII_TXCLK_DELAY 0x0002 + #define DEFAULT_EXT_SCR (RGMII_TXCLK_DELAY | RGMII_RXCLK_DELAY) +#define M88_EXT_SSR 0x001B + #define HWCFG_MODE_MASK 0x000F + #define RGMII_COPPER_MODE 0x000B + +/* CICADA CIS8201 Gigabit PHY Register Definition */ +#define CIS_IMR 0x0019 + #define CIS_INT_ENABLE 0x8000 + #define CIS_LINK_CHANGE_INT 0x2000 +#define CIS_ISR 0x001A + #define CIS_INT_PENDING 0x8000 + #define CIS_LINK_CHANGE_STATUS 0x2000 +#define CIS_AUX_CTRL_STATUS 0x001C + #define CIS_AUTONEG_COMPLETE 0x8000 + #define CIS_SPEED_MASK 0x0018 + #define CIS_SPEED_1000 0x0010 + #define CIS_SPEED_100 0x0008 + #define CIS_SPEED_10 0x0000 + #define CIS_DUPLEX 0x0020 + + #define CIS_MEDIA_MASK (CIS_SPEED_MASK | CIS_DUPLEX) + #define CIS_1000FULL (CIS_SPEED_1000 | CIS_DUPLEX) + #define CIS_1000HALF CIS_SPEED_1000 + #define CIS_100FULL (CIS_SPEED_100 | CIS_DUPLEX) + #define CIS_100HALF CIS_SPEED_100 + #define CIS_10FULL (CIS_SPEED_10 | CIS_DUPLEX) + #define CIS_10HALF CIS_SPEED_10 + #define CIS_SMI_PRIORITY 0x0004 + +static inline unsigned short INW (struct eth_device *dev, unsigned long addr) +{ + return le16_to_cpu (*(volatile unsigned short *) (addr + dev->iobase)); +} + +static inline void OUTW (struct eth_device *dev, unsigned short command, unsigned long addr) +{ + *(volatile unsigned short *) ((addr + dev->iobase)) = cpu_to_le16 (command); +} + +/* + Access RXBUFFER_START/TXBUFFER_START to read RX buffer/write TX buffer +*/ +#if defined (CONFIG_DRIVER_AX88180_16BIT) +static inline unsigned short READ_RXBUF (struct eth_device *dev) +{ + return le16_to_cpu (*(volatile unsigned short *) (RXBUFFER_START + dev->iobase)); +} + +static inline void WRITE_TXBUF (struct eth_device *dev, unsigned short data) +{ + *(volatile unsigned short *) ((TXBUFFER_START + dev->iobase)) = cpu_to_le16 (data); +} +#else +static inline unsigned long READ_RXBUF (struct eth_device *dev) +{ + return le32_to_cpu (*(volatile unsigned long *) (RXBUFFER_START + dev->iobase)); +} + +static inline void WRITE_TXBUF (struct eth_device *dev, unsigned long data) +{ + *(volatile unsigned long *) ((TXBUFFER_START + dev->iobase)) = cpu_to_le32 (data); +} +#endif + +#endif /* _AX88180_H_ */ diff --git a/roms/u-boot-sam460ex/drivers/net/ax88796.c b/roms/u-boot-sam460ex/drivers/net/ax88796.c new file mode 100644 index 000000000..20891418a --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ax88796.c @@ -0,0 +1,156 @@ +/* + * (c) 2007 Nobuhiro Iwamatsu <iwamatsu@nigauri.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <common.h> +#include "ax88796.h" + +/* + * Set 1 bit data + */ +static void ax88796_bitset(u32 bit) +{ + /* DATA1 */ + if( bit ) + EEDI_HIGH; + else + EEDI_LOW; + + EECLK_LOW; + udelay(1000); + EECLK_HIGH; + udelay(1000); + EEDI_LOW; +} + +/* + * Get 1 bit data + */ +static u8 ax88796_bitget(void) +{ + u8 bit; + + EECLK_LOW; + udelay(1000); + /* DATA */ + bit = EEDO; + EECLK_HIGH; + udelay(1000); + + return bit; +} + +/* + * Send COMMAND to EEPROM + */ +static void ax88796_eep_cmd(u8 cmd) +{ + ax88796_bitset(BIT_DUMMY); + switch(cmd){ + case MAC_EEP_READ: + ax88796_bitset(1); + ax88796_bitset(1); + ax88796_bitset(0); + break; + + case MAC_EEP_WRITE: + ax88796_bitset(1); + ax88796_bitset(0); + ax88796_bitset(1); + break; + + case MAC_EEP_ERACE: + ax88796_bitset(1); + ax88796_bitset(1); + ax88796_bitset(1); + break; + + case MAC_EEP_EWEN: + ax88796_bitset(1); + ax88796_bitset(0); + ax88796_bitset(0); + break; + + case MAC_EEP_EWDS: + ax88796_bitset(1); + ax88796_bitset(0); + ax88796_bitset(0); + break; + default: + break; + } +} + +static void ax88796_eep_setaddr(u16 addr) +{ + int i ; + + for( i = 7 ; i >= 0 ; i-- ) + ax88796_bitset(addr & (1 << i)); +} + +/* + * Get data from EEPROM + */ +static u16 ax88796_eep_getdata(void) +{ + ushort data = 0; + int i; + + ax88796_bitget(); /* DUMMY */ + for( i = 0 ; i < 16 ; i++ ){ + data <<= 1; + data |= ax88796_bitget(); + } + return data; +} + +static void ax88796_mac_read(u8 *buff) +{ + int i ; + u16 data; + u16 addr = 0; + + for( i = 0 ; i < 3; i++ ) + { + EECS_HIGH; + EEDI_LOW; + udelay(1000); + /* READ COMMAND */ + ax88796_eep_cmd(MAC_EEP_READ); + /* ADDRESS */ + ax88796_eep_setaddr(addr++); + /* GET DATA */ + data = ax88796_eep_getdata(); + *buff++ = (uchar)(data & 0xff); + *buff++ = (uchar)((data >> 8) & 0xff); + EECLK_LOW; + EEDI_LOW; + EECS_LOW; + } +} + +int get_prom(u8* mac_addr, u8* base_addr) +{ + u8 prom[32]; + int i; + + ax88796_mac_read(prom); + for (i = 0; i < 6; i++){ + mac_addr[i] = prom[i]; + } + return 1; +} diff --git a/roms/u-boot-sam460ex/drivers/net/ax88796.h b/roms/u-boot-sam460ex/drivers/net/ax88796.h new file mode 100644 index 000000000..43a16393b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ax88796.h @@ -0,0 +1,81 @@ +/* + * AX88796L(NE2000) support + * + * (c) 2007 Nobuhiro Iwamatsu <iwamatsu@nigauri.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#ifndef __DRIVERS_AX88796L_H__ +#define __DRIVERS_AX88796L_H__ + +#define DP_DATA (0x10 << 1) +#define START_PG 0x40 /* First page of TX buffer */ +#define START_PG2 0x48 +#define STOP_PG 0x80 /* Last page +1 of RX ring */ +#define TX_PAGES 12 +#define RX_START (START_PG+TX_PAGES) +#define RX_END STOP_PG + +#define AX88796L_BASE_ADDRESS CONFIG_DRIVER_NE2000_BASE +#define AX88796L_BYTE_ACCESS 0x00001000 +#define AX88796L_OFFSET 0x00000400 +#define AX88796L_ADDRESS_BYTE AX88796L_BASE_ADDRESS + \ + AX88796L_BYTE_ACCESS + AX88796L_OFFSET +#define AX88796L_REG_MEMR AX88796L_ADDRESS_BYTE + (0x14<<1) +#define AX88796L_REG_CR AX88796L_ADDRESS_BYTE + (0x00<<1) + +#define AX88796L_CR (*(vu_short *)(AX88796L_REG_CR)) +#define AX88796L_MEMR (*(vu_short *)(AX88796L_REG_MEMR)) + +#define EECS_HIGH (AX88796L_MEMR |= 0x10) +#define EECS_LOW (AX88796L_MEMR &= 0xef) +#define EECLK_HIGH (AX88796L_MEMR |= 0x80) +#define EECLK_LOW (AX88796L_MEMR &= 0x7f) +#define EEDI_HIGH (AX88796L_MEMR |= 0x20) +#define EEDI_LOW (AX88796L_MEMR &= 0xdf) +#define EEDO ((AX88796L_MEMR & 0x40)>>6) + +#define PAGE0_SET (AX88796L_CR &= 0x3f) +#define PAGE1_SET (AX88796L_CR = (AX88796L_CR & 0x3f) | 0x40) + +#define BIT_DUMMY 0 +#define MAC_EEP_READ 1 +#define MAC_EEP_WRITE 2 +#define MAC_EEP_ERACE 3 +#define MAC_EEP_EWEN 4 +#define MAC_EEP_EWDS 5 + +/* R7780MP Specific code */ +#if defined(CONFIG_R7780MP) +#define ISA_OFFSET 0x1400 +#define DP_IN(_b_, _o_, _d_) (_d_) = \ + *( (vu_short *) ((_b_) + ((_o_) * 2) + ISA_OFFSET)) +#define DP_OUT(_b_, _o_, _d_) \ + *((vu_short *)((_b_) + ((_o_) * 2) + ISA_OFFSET)) = (_d_) +#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_short *) ((_b_) + ISA_OFFSET)) +#define DP_OUT_DATA(_b_, _d_) *( (vu_short *) ((_b_)+ISA_OFFSET)) = (_d_) +#else +/* Please change for your target boards */ +#define ISA_OFFSET 0x0000 +#define DP_IN(_b_, _o_, _d_) (_d_) = *( (vu_short *)((_b_)+(_o_ )+ISA_OFFSET)) +#define DP_OUT(_b_, _o_, _d_) *((vu_short *)((_b_)+(_o_)+ISA_OFFSET)) = (_d_) +#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_short *) ((_b_)+ISA_OFFSET)) +#define DP_OUT_DATA(_b_, _d_) *( (vu_short *) ((_b_)+ISA_OFFSET)) = (_d_) +#endif + +#endif /* __DRIVERS_AX88796L_H__ */ diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x.c b/roms/u-boot-sam460ex/drivers/net/bcm570x.c new file mode 100644 index 000000000..c250d446f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x.c @@ -0,0 +1,1598 @@ +/* + * Broadcom BCM570x Ethernet Driver for U-Boot. + * Support 5701, 5702, 5703, and 5704. Single instance driver. + * Copyright (C) 2002 James F. Dougherty (jfd@broadcom.com) + */ + +#include <common.h> + +#ifdef CONFIG_BMW +#include <mpc824x.h> +#endif +#include <net.h> +#include "bcm570x_mm.h" +#include "bcm570x_autoneg.h" +#include <pci.h> +#include <malloc.h> + +/* + * PCI Registers and definitions. + */ +#define PCI_CMD_MASK 0xffff0000 /* mask to save status bits */ +#define PCI_ANY_ID (~0) + +/* + * PCI memory base for Ethernet device as well as device Interrupt. + */ +#define BCM570X_MBAR 0x80100000 +#define BCM570X_ILINE 1 + +#define SECOND_USEC 1000000 +#define MAX_PACKET_SIZE 1600 +#define MAX_UNITS 4 + +/* Globals to this module */ +int initialized = 0; +unsigned int ioBase = 0; +volatile PLM_DEVICE_BLOCK pDevice = NULL; /* 570x softc */ +volatile PUM_DEVICE_BLOCK pUmDevice = NULL; + +/* Used to pass the full-duplex flag, etc. */ +int line_speed[MAX_UNITS] = { 0, 0, 0, 0 }; +static int full_duplex[MAX_UNITS] = { 1, 1, 1, 1 }; +static int rx_flow_control[MAX_UNITS] = { 0, 0, 0, 0 }; +static int tx_flow_control[MAX_UNITS] = { 0, 0, 0, 0 }; +static int auto_flow_control[MAX_UNITS] = { 0, 0, 0, 0 }; +static int tx_checksum[MAX_UNITS] = { 1, 1, 1, 1 }; +static int rx_checksum[MAX_UNITS] = { 1, 1, 1, 1 }; +static int auto_speed[MAX_UNITS] = { 1, 1, 1, 1 }; + +#if JUMBO_FRAMES +/* Jumbo MTU for interfaces. */ +static int mtu[MAX_UNITS] = { 0, 0, 0, 0 }; +#endif + +/* Turn on Wake-on lan for a device unit */ +static int enable_wol[MAX_UNITS] = { 0, 0, 0, 0 }; + +#define TX_DESC_CNT DEFAULT_TX_PACKET_DESC_COUNT +static unsigned int tx_pkt_desc_cnt[MAX_UNITS] = + { TX_DESC_CNT, TX_DESC_CNT, TX_DESC_CNT, TX_DESC_CNT }; + +#define RX_DESC_CNT DEFAULT_STD_RCV_DESC_COUNT +static unsigned int rx_std_desc_cnt[MAX_UNITS] = + { RX_DESC_CNT, RX_DESC_CNT, RX_DESC_CNT, RX_DESC_CNT }; + +static unsigned int rx_adaptive_coalesce[MAX_UNITS] = { 1, 1, 1, 1 }; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +#define JBO_DESC_CNT DEFAULT_JUMBO_RCV_DESC_COUNT +static unsigned int rx_jumbo_desc_cnt[MAX_UNITS] = + { JBO_DESC_CNT, JBO_DESC_CNT, JBO_DESC_CNT, JBO_DESC_CNT }; +#endif +#define RX_COAL_TK DEFAULT_RX_COALESCING_TICKS +static unsigned int rx_coalesce_ticks[MAX_UNITS] = + { RX_COAL_TK, RX_COAL_TK, RX_COAL_TK, RX_COAL_TK }; + +#define RX_COAL_FM DEFAULT_RX_MAX_COALESCED_FRAMES +static unsigned int rx_max_coalesce_frames[MAX_UNITS] = + { RX_COAL_FM, RX_COAL_FM, RX_COAL_FM, RX_COAL_FM }; + +#define TX_COAL_TK DEFAULT_TX_COALESCING_TICKS +static unsigned int tx_coalesce_ticks[MAX_UNITS] = + { TX_COAL_TK, TX_COAL_TK, TX_COAL_TK, TX_COAL_TK }; + +#define TX_COAL_FM DEFAULT_TX_MAX_COALESCED_FRAMES +static unsigned int tx_max_coalesce_frames[MAX_UNITS] = + { TX_COAL_FM, TX_COAL_FM, TX_COAL_FM, TX_COAL_FM }; + +#define ST_COAL_TK DEFAULT_STATS_COALESCING_TICKS +static unsigned int stats_coalesce_ticks[MAX_UNITS] = + { ST_COAL_TK, ST_COAL_TK, ST_COAL_TK, ST_COAL_TK }; + +/* + * Legitimate values for BCM570x device types + */ +typedef enum { + BCM5700VIGIL = 0, + BCM5700A6, + BCM5700T6, + BCM5700A9, + BCM5700T9, + BCM5700, + BCM5701A5, + BCM5701T1, + BCM5701T8, + BCM5701A7, + BCM5701A10, + BCM5701A12, + BCM5701, + BCM5702, + BCM5703, + BCM5703A31, + TC996T, + TC996ST, + TC996SSX, + TC996SX, + TC996BT, + TC997T, + TC997SX, + TC1000T, + TC940BR01, + TC942BR01, + NC6770, + NC7760, + NC7770, + NC7780 +} board_t; + +/* Chip-Rev names for each device-type */ +static struct { + char *name; +} chip_rev[] = { + { + "BCM5700VIGIL"}, { + "BCM5700A6"}, { + "BCM5700T6"}, { + "BCM5700A9"}, { + "BCM5700T9"}, { + "BCM5700"}, { + "BCM5701A5"}, { + "BCM5701T1"}, { + "BCM5701T8"}, { + "BCM5701A7"}, { + "BCM5701A10"}, { + "BCM5701A12"}, { + "BCM5701"}, { + "BCM5702"}, { + "BCM5703"}, { + "BCM5703A31"}, { + "TC996T"}, { + "TC996ST"}, { + "TC996SSX"}, { + "TC996SX"}, { + "TC996BT"}, { + "TC997T"}, { + "TC997SX"}, { + "TC1000T"}, { + "TC940BR01"}, { + "TC942BR01"}, { + "NC6770"}, { + "NC7760"}, { + "NC7770"}, { + "NC7780"}, { + 0} +}; + +/* indexed by board_t, above */ +static struct { + char *name; +} board_info[] = { + { + "Broadcom Vigil B5700 1000Base-T"}, { + "Broadcom BCM5700 1000Base-T"}, { + "Broadcom BCM5700 1000Base-SX"}, { + "Broadcom BCM5700 1000Base-SX"}, { + "Broadcom BCM5700 1000Base-T"}, { + "Broadcom BCM5700"}, { + "Broadcom BCM5701 1000Base-T"}, { + "Broadcom BCM5701 1000Base-T"}, { + "Broadcom BCM5701 1000Base-T"}, { + "Broadcom BCM5701 1000Base-SX"}, { + "Broadcom BCM5701 1000Base-T"}, { + "Broadcom BCM5701 1000Base-T"}, { + "Broadcom BCM5701"}, { + "Broadcom BCM5702 1000Base-T"}, { + "Broadcom BCM5703 1000Base-T"}, { + "Broadcom BCM5703 1000Base-SX"}, { + "3Com 3C996 10/100/1000 Server NIC"}, { + "3Com 3C996 10/100/1000 Server NIC"}, { + "3Com 3C996 Gigabit Fiber-SX Server NIC"}, { + "3Com 3C996 Gigabit Fiber-SX Server NIC"}, { + "3Com 3C996B Gigabit Server NIC"}, { + "3Com 3C997 Gigabit Server NIC"}, { + "3Com 3C997 Gigabit Fiber-SX Server NIC"}, { + "3Com 3C1000 Gigabit NIC"}, { + "3Com 3C940 Gigabit LOM (21X21)"}, { + "3Com 3C942 Gigabit LOM (31X31)"}, { + "Compaq NC6770 Gigabit Server Adapter"}, { + "Compaq NC7760 Gigabit Server Adapter"}, { + "Compaq NC7770 Gigabit Server Adapter"}, { + "Compaq NC7780 Gigabit Server Adapter"}, { +0},}; + +/* PCI Devices which use the 570x chipset */ +struct pci_device_table { + unsigned short vendor_id, device_id; /* Vendor/DeviceID */ + unsigned short subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ + unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */ + unsigned long board_id; /* Data private to the driver */ + int io_size, min_latency; +} bcm570xDevices[] = { + { + 0x14e4, 0x1644, 0x1014, 0x0277, 0, 0, BCM5700VIGIL, 128, 32}, { + 0x14e4, 0x1644, 0x14e4, 0x1644, 0, 0, BCM5700A6, 128, 32}, { + 0x14e4, 0x1644, 0x14e4, 0x2, 0, 0, BCM5700T6, 128, 32}, { + 0x14e4, 0x1644, 0x14e4, 0x3, 0, 0, BCM5700A9, 128, 32}, { + 0x14e4, 0x1644, 0x14e4, 0x4, 0, 0, BCM5700T9, 128, 32}, { + 0x14e4, 0x1644, 0x1028, 0xd1, 0, 0, BCM5700, 128, 32}, { + 0x14e4, 0x1644, 0x1028, 0x0106, 0, 0, BCM5700, 128, 32}, { + 0x14e4, 0x1644, 0x1028, 0x0109, 0, 0, BCM5700, 128, 32}, { + 0x14e4, 0x1644, 0x1028, 0x010a, 0, 0, BCM5700, 128, 32}, { + 0x14e4, 0x1644, 0x10b7, 0x1000, 0, 0, TC996T, 128, 32}, { + 0x14e4, 0x1644, 0x10b7, 0x1001, 0, 0, TC996ST, 128, 32}, { + 0x14e4, 0x1644, 0x10b7, 0x1002, 0, 0, TC996SSX, 128, 32}, { + 0x14e4, 0x1644, 0x10b7, 0x1003, 0, 0, TC997T, 128, 32}, { + 0x14e4, 0x1644, 0x10b7, 0x1005, 0, 0, TC997SX, 128, 32}, { + 0x14e4, 0x1644, 0x10b7, 0x1008, 0, 0, TC942BR01, 128, 32}, { + 0x14e4, 0x1644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5700, 128, 32}, { + 0x14e4, 0x1645, 0x14e4, 1, 0, 0, BCM5701A5, 128, 32}, { + 0x14e4, 0x1645, 0x14e4, 5, 0, 0, BCM5701T1, 128, 32}, { + 0x14e4, 0x1645, 0x14e4, 6, 0, 0, BCM5701T8, 128, 32}, { + 0x14e4, 0x1645, 0x14e4, 7, 0, 0, BCM5701A7, 128, 32}, { + 0x14e4, 0x1645, 0x14e4, 8, 0, 0, BCM5701A10, 128, 32}, { + 0x14e4, 0x1645, 0x14e4, 0x8008, 0, 0, BCM5701A12, 128, 32}, { + 0x14e4, 0x1645, 0x0e11, 0xc1, 0, 0, NC6770, 128, 32}, { + 0x14e4, 0x1645, 0x0e11, 0x7c, 0, 0, NC7770, 128, 32}, { + 0x14e4, 0x1645, 0x0e11, 0x85, 0, 0, NC7780, 128, 32}, { + 0x14e4, 0x1645, 0x1028, 0x0121, 0, 0, BCM5701, 128, 32}, { + 0x14e4, 0x1645, 0x10b7, 0x1004, 0, 0, TC996SX, 128, 32}, { + 0x14e4, 0x1645, 0x10b7, 0x1006, 0, 0, TC996BT, 128, 32}, { + 0x14e4, 0x1645, 0x10b7, 0x1007, 0, 0, TC1000T, 128, 32}, { + 0x14e4, 0x1645, 0x10b7, 0x1008, 0, 0, TC940BR01, 128, 32}, { + 0x14e4, 0x1645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5701, 128, 32}, { + 0x14e4, 0x1646, 0x14e4, 0x8009, 0, 0, BCM5702, 128, 32}, { + 0x14e4, 0x1646, 0x0e11, 0xbb, 0, 0, NC7760, 128, 32}, { + 0x14e4, 0x1646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702, 128, 32}, { + 0x14e4, 0x16a6, 0x14e4, 0x8009, 0, 0, BCM5702, 128, 32}, { + 0x14e4, 0x16a6, 0x0e11, 0xbb, 0, 0, NC7760, 128, 32}, { + 0x14e4, 0x16a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702, 128, 32}, { + 0x14e4, 0x1647, 0x14e4, 0x0009, 0, 0, BCM5703, 128, 32}, { + 0x14e4, 0x1647, 0x14e4, 0x000a, 0, 0, BCM5703A31, 128, 32}, { + 0x14e4, 0x1647, 0x14e4, 0x000b, 0, 0, BCM5703, 128, 32}, { + 0x14e4, 0x1647, 0x14e4, 0x800a, 0, 0, BCM5703, 128, 32}, { + 0x14e4, 0x1647, 0x0e11, 0x9a, 0, 0, NC7770, 128, 32}, { + 0x14e4, 0x1647, 0x0e11, 0x99, 0, 0, NC7780, 128, 32}, { + 0x14e4, 0x1647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703, 128, 32}, { + 0x14e4, 0x16a7, 0x14e4, 0x0009, 0, 0, BCM5703, 128, 32}, { + 0x14e4, 0x16a7, 0x14e4, 0x000a, 0, 0, BCM5703A31, 128, 32}, { + 0x14e4, 0x16a7, 0x14e4, 0x000b, 0, 0, BCM5703, 128, 32}, { + 0x14e4, 0x16a7, 0x14e4, 0x800a, 0, 0, BCM5703, 128, 32}, { + 0x14e4, 0x16a7, 0x0e11, 0x9a, 0, 0, NC7770, 128, 32}, { + 0x14e4, 0x16a7, 0x0e11, 0x99, 0, 0, NC7780, 128, 32}, { + 0x14e4, 0x16a7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703, 128, 32} +}; + +#define n570xDevices (sizeof(bcm570xDevices)/sizeof(bcm570xDevices[0])) + +/* + * Allocate a packet buffer from the bcm570x packet pool. + */ +void *bcm570xPktAlloc (int u, int pksize) +{ + return malloc (pksize); +} + +/* + * Free a packet previously allocated from the bcm570x packet + * buffer pool. + */ +void bcm570xPktFree (int u, void *p) +{ + free (p); +} + +int bcm570xReplenishRxBuffers (PUM_DEVICE_BLOCK pUmDevice) +{ + PLM_PACKET pPacket; + PUM_PACKET pUmPacket; + void *skb; + int queue_rx = 0; + int ret = 0; + + while ((pUmPacket = (PUM_PACKET) + QQ_PopHead (&pUmDevice->rx_out_of_buf_q.Container)) != 0) { + + pPacket = (PLM_PACKET) pUmPacket; + + /* reuse an old skb */ + if (pUmPacket->skbuff) { + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, + pPacket); + queue_rx = 1; + continue; + } + if ((skb = bcm570xPktAlloc (pUmDevice->index, + pPacket->u.Rx.RxBufferSize + 2)) == + 0) { + QQ_PushHead (&pUmDevice->rx_out_of_buf_q.Container, + pPacket); + printf ("NOTICE: Out of RX memory.\n"); + ret = 1; + break; + } + + pUmPacket->skbuff = skb; + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + queue_rx = 1; + } + + if (queue_rx) { + LM_QueueRxPackets (pDevice); + } + + return ret; +} + +/* + * Probe, Map, and Init 570x device. + */ +int eth_init (bd_t * bis) +{ + int i, rv, devFound = FALSE; + pci_dev_t devbusfn; + unsigned short status; + + /* Find PCI device, if it exists, configure ... */ + for (i = 0; i < n570xDevices; i++) { + devbusfn = pci_find_device (bcm570xDevices[i].vendor_id, + bcm570xDevices[i].device_id, 0); + if (devbusfn == -1) { + continue; /* No device of that vendor/device ID */ + } else { + + /* Set ILINE */ + pci_write_config_byte (devbusfn, + PCI_INTERRUPT_LINE, + BCM570X_ILINE); + + /* + * 0x10 - 0x14 define one 64-bit MBAR. + * 0x14 is the higher-order address bits of the BAR. + */ + pci_write_config_dword (devbusfn, + PCI_BASE_ADDRESS_1, 0); + + ioBase = BCM570X_MBAR; + + pci_write_config_dword (devbusfn, + PCI_BASE_ADDRESS_0, ioBase); + + /* + * Enable PCI memory, IO, and Master -- don't + * reset any status bits in doing so. + */ + pci_read_config_word (devbusfn, PCI_COMMAND, &status); + + status |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + + pci_write_config_word (devbusfn, PCI_COMMAND, status); + + printf + ("\n%s: bus %d, device %d, function %d: MBAR=0x%x\n", + board_info[bcm570xDevices[i].board_id].name, + PCI_BUS (devbusfn), PCI_DEV (devbusfn), + PCI_FUNC (devbusfn), ioBase); + + /* Allocate once, but always clear on init */ + if (!pDevice) { + pDevice = malloc (sizeof (UM_DEVICE_BLOCK)); + pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + memset (pDevice, 0x0, sizeof (UM_DEVICE_BLOCK)); + } + + /* Configure pci dev structure */ + pUmDevice->pdev = devbusfn; + pUmDevice->index = 0; + pUmDevice->tx_pkt = 0; + pUmDevice->rx_pkt = 0; + devFound = TRUE; + break; + } + } + + if (!devFound) { + printf + ("eth_init: FAILURE: no BCM570x Ethernet devices found.\n"); + return -1; + } + + /* Setup defaults for chip */ + pDevice->TaskToOffload = LM_TASK_OFFLOAD_NONE; + + if (pDevice->ChipRevId == T3_CHIP_ID_5700_B0) { + pDevice->TaskToOffload = LM_TASK_OFFLOAD_NONE; + } else { + + if (rx_checksum[i]) { + pDevice->TaskToOffload |= + LM_TASK_OFFLOAD_RX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_RX_UDP_CHECKSUM; + } + + if (tx_checksum[i]) { + pDevice->TaskToOffload |= + LM_TASK_OFFLOAD_TX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_TX_UDP_CHECKSUM; + pDevice->NoTxPseudoHdrChksum = TRUE; + } + } + + /* Set Device PCI Memory base address */ + pDevice->pMappedMemBase = (PLM_UINT8) ioBase; + + /* Pull down adapter info */ + if ((rv = LM_GetAdapterInfo (pDevice)) != LM_STATUS_SUCCESS) { + printf ("bcm570xEnd: LM_GetAdapterInfo failed: rv=%d!\n", rv); + return -2; + } + + /* Lock not needed */ + pUmDevice->do_global_lock = 0; + + if (T3_ASIC_REV (pUmDevice->lm_dev.ChipRevId) == T3_ASIC_REV_5700) { + /* The 5700 chip works best without interleaved register */ + /* accesses on certain machines. */ + pUmDevice->do_global_lock = 1; + } + + /* Setup timer delays */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + pDevice->UseTaggedStatus = TRUE; + pUmDevice->timer_interval = CONFIG_SYS_HZ; + } else { + pUmDevice->timer_interval = CONFIG_SYS_HZ / 50; + } + + /* Grab name .... */ + pUmDevice->name = + (char *)malloc (strlen (board_info[bcm570xDevices[i].board_id].name) + + 1); + strcpy (pUmDevice->name, board_info[bcm570xDevices[i].board_id].name); + + eth_getenv_enetaddr("ethaddr", pDevice->NodeAddress); + LM_SetMacAddress (pDevice); + /* Init queues .. */ + QQ_InitQueue (&pUmDevice->rx_out_of_buf_q.Container, + MAX_RX_PACKET_DESC_COUNT); + pUmDevice->rx_last_cnt = pUmDevice->tx_last_cnt = 0; + + /* delay for 4 seconds */ + pUmDevice->delayed_link_ind = (4 * CONFIG_SYS_HZ) / pUmDevice->timer_interval; + + pUmDevice->adaptive_expiry = CONFIG_SYS_HZ / pUmDevice->timer_interval; + + /* Sometimes we get spurious ints. after reset when link is down. */ + /* This field tells the isr to service the int. even if there is */ + /* no status block update. */ + pUmDevice->adapter_just_inited = + (3 * CONFIG_SYS_HZ) / pUmDevice->timer_interval; + + /* Initialize 570x */ + if (LM_InitializeAdapter (pDevice) != LM_STATUS_SUCCESS) { + printf ("ERROR: Adapter initialization failed.\n"); + return ERROR; + } + + /* Enable chip ISR */ + LM_EnableInterrupt (pDevice); + + /* Clear MC table */ + LM_MulticastClear (pDevice); + + /* Enable Multicast */ + LM_SetReceiveMask (pDevice, + pDevice->ReceiveMask | LM_ACCEPT_ALL_MULTICAST); + + pUmDevice->opened = 1; + pUmDevice->tx_full = 0; + pUmDevice->tx_pkt = 0; + pUmDevice->rx_pkt = 0; + printf ("eth%d: %s @0x%lx,", + pDevice->index, pUmDevice->name, (unsigned long)ioBase); + printf ("node addr "); + for (i = 0; i < 6; i++) { + printf ("%2.2x", pDevice->NodeAddress[i]); + } + printf ("\n"); + + printf ("eth%d: ", pDevice->index); + printf ("%s with ", chip_rev[bcm570xDevices[i].board_id].name); + + if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5400_PHY_ID) + printf ("Broadcom BCM5400 Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5401_PHY_ID) + printf ("Broadcom BCM5401 Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5411_PHY_ID) + printf ("Broadcom BCM5411 Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5701_PHY_ID) + printf ("Broadcom BCM5701 Integrated Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5703_PHY_ID) + printf ("Broadcom BCM5703 Integrated Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM8002_PHY_ID) + printf ("Broadcom BCM8002 SerDes "); + else if (pDevice->EnableTbi) + printf ("Agilent HDMP-1636 SerDes "); + else + printf ("Unknown "); + printf ("transceiver found\n"); + + printf ("eth%d: %s, MTU: %d,", + pDevice->index, pDevice->BusSpeedStr, 1500); + + if ((pDevice->ChipRevId != T3_CHIP_ID_5700_B0) && rx_checksum[i]) + printf ("Rx Checksum ON\n"); + else + printf ("Rx Checksum OFF\n"); + initialized++; + + return 0; +} + +/* Ethernet Interrupt service routine */ +void eth_isr (void) +{ + LM_UINT32 oldtag, newtag; + int i; + + pUmDevice->interrupt = 1; + + if (pDevice->UseTaggedStatus) { + if ((pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) || + pUmDevice->adapter_just_inited) { + MB_REG_WR (pDevice, Mailbox.Interrupt[0].Low, 1); + oldtag = pDevice->pStatusBlkVirt->StatusTag; + + for (i = 0;; i++) { + pDevice->pStatusBlkVirt->Status &= + ~STATUS_BLOCK_UPDATED; + LM_ServiceInterrupts (pDevice); + newtag = pDevice->pStatusBlkVirt->StatusTag; + if ((newtag == oldtag) || (i > 50)) { + MB_REG_WR (pDevice, + Mailbox.Interrupt[0].Low, + newtag << 24); + if (pDevice->UndiFix) { + REG_WR (pDevice, Grc.LocalCtrl, + pDevice-> + GrcLocalCtrl | 0x2); + } + break; + } + oldtag = newtag; + } + } + } else { + while (pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) { + unsigned int dummy; + + pDevice->pMemView->Mailbox.Interrupt[0].Low = 1; + pDevice->pStatusBlkVirt->Status &= + ~STATUS_BLOCK_UPDATED; + LM_ServiceInterrupts (pDevice); + pDevice->pMemView->Mailbox.Interrupt[0].Low = 0; + dummy = pDevice->pMemView->Mailbox.Interrupt[0].Low; + } + } + + /* Allocate new RX buffers */ + if (QQ_GetEntryCnt (&pUmDevice->rx_out_of_buf_q.Container)) { + bcm570xReplenishRxBuffers (pUmDevice); + } + + /* Queue packets */ + if (QQ_GetEntryCnt (&pDevice->RxPacketFreeQ.Container)) { + LM_QueueRxPackets (pDevice); + } + + if (pUmDevice->tx_queued) { + pUmDevice->tx_queued = 0; + } + + if (pUmDevice->tx_full) { + if (pDevice->LinkStatus != LM_STATUS_LINK_DOWN) { + printf + ("NOTICE: tx was previously blocked, restarting MUX\n"); + pUmDevice->tx_full = 0; + } + } + + pUmDevice->interrupt = 0; + +} + +int eth_send (volatile void *packet, int length) +{ + int status = 0; +#if ET_DEBUG + unsigned char *ptr = (unsigned char *)packet; +#endif + PLM_PACKET pPacket; + PUM_PACKET pUmPacket; + + /* Link down, return */ + while (pDevice->LinkStatus == LM_STATUS_LINK_DOWN) { +#if 0 + printf ("eth%d: link down - check cable or link partner.\n", + pUmDevice->index); +#endif + eth_isr (); + + /* Wait to see link for one-half a second before sending ... */ + udelay (1500000); + + } + + /* Clear sent flag */ + pUmDevice->tx_pkt = 0; + + /* Previously blocked */ + if (pUmDevice->tx_full) { + printf ("eth%d: tx blocked.\n", pUmDevice->index); + return 0; + } + + pPacket = (PLM_PACKET) + QQ_PopHead (&pDevice->TxPacketFreeQ.Container); + + if (pPacket == 0) { + pUmDevice->tx_full = 1; + printf ("bcm570xEndSend: TX full!\n"); + return 0; + } + + if (pDevice->SendBdLeft.counter == 0) { + pUmDevice->tx_full = 1; + printf ("bcm570xEndSend: no more TX descriptors!\n"); + QQ_PushHead (&pDevice->TxPacketFreeQ.Container, pPacket); + return 0; + } + + if (length <= 0) { + printf ("eth: bad packet size: %d\n", length); + goto out; + } + + /* Get packet buffers and fragment list */ + pUmPacket = (PUM_PACKET) pPacket; + /* Single DMA Descriptor transmit. + * Fragments may be provided, but one DMA descriptor max is + * used to send the packet. + */ + if (MM_CoalesceTxBuffer (pDevice, pPacket) != LM_STATUS_SUCCESS) { + if (pUmPacket->skbuff == NULL) { + /* Packet was discarded */ + printf ("TX: failed (1)\n"); + status = 1; + } else { + printf ("TX: failed (2)\n"); + status = 2; + } + QQ_PushHead (&pDevice->TxPacketFreeQ.Container, pPacket); + return status; + } + + /* Copy packet to DMA buffer */ + memset (pUmPacket->skbuff, 0x0, MAX_PACKET_SIZE); + memcpy ((void *)pUmPacket->skbuff, (void *)packet, length); + pPacket->PacketSize = length; + pPacket->Flags |= SND_BD_FLAG_END | SND_BD_FLAG_COAL_NOW; + pPacket->u.Tx.FragCount = 1; + /* We've already provided a frame ready for transmission */ + pPacket->Flags &= ~SND_BD_FLAG_TCP_UDP_CKSUM; + + if (LM_SendPacket (pDevice, pPacket) == LM_STATUS_FAILURE) { + /* + * A lower level send failure will push the packet descriptor back + * in the free queue, so just deal with the VxWorks clusters. + */ + if (pUmPacket->skbuff == NULL) { + printf ("TX failed (1)!\n"); + /* Packet was discarded */ + status = 3; + } else { + /* A resource problem ... */ + printf ("TX failed (2)!\n"); + status = 4; + } + + if (QQ_GetEntryCnt (&pDevice->TxPacketFreeQ.Container) == 0) { + printf ("TX: emptyQ!\n"); + pUmDevice->tx_full = 1; + } + } + + while (pUmDevice->tx_pkt == 0) { + /* Service TX */ + eth_isr (); + } +#if ET_DEBUG + printf ("eth_send: 0x%x, %d bytes\n" + "[%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x] ...\n", + (int)pPacket, length, + ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], + ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], + ptr[13], ptr[14], ptr[15]); +#endif + pUmDevice->tx_pkt = 0; + QQ_PushHead (&pDevice->TxPacketFreeQ.Container, pPacket); + + /* Done with send */ + out: + return status; +} + +/* Ethernet receive */ +int eth_rx (void) +{ + PLM_PACKET pPacket = NULL; + PUM_PACKET pUmPacket = NULL; + void *skb; + int size = 0; + + while (TRUE) { + + bcm570x_service_isr: + /* Pull down packet if it is there */ + eth_isr (); + + /* Indicate RX packets called */ + if (pUmDevice->rx_pkt) { + /* printf("eth_rx: got a packet...\n"); */ + pUmDevice->rx_pkt = 0; + } else { + /* printf("eth_rx: waiting for packet...\n"); */ + goto bcm570x_service_isr; + } + + pPacket = (PLM_PACKET) + QQ_PopHead (&pDevice->RxPacketReceivedQ.Container); + + if (pPacket == 0) { + printf ("eth_rx: empty packet!\n"); + goto bcm570x_service_isr; + } + + pUmPacket = (PUM_PACKET) pPacket; +#if ET_DEBUG + printf ("eth_rx: packet @0x%x\n", (int)pPacket); +#endif + /* If the packet generated an error, reuse buffer */ + if ((pPacket->PacketStatus != LM_STATUS_SUCCESS) || + ((size = pPacket->PacketSize) > pDevice->RxMtu)) { + + /* reuse skb */ + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, + pPacket); + printf ("eth_rx: error in packet dma!\n"); + goto bcm570x_service_isr; + } + + /* Set size and address */ + skb = pUmPacket->skbuff; + size = pPacket->PacketSize; + + /* Pass the packet up to the protocol + * layers. + */ + NetReceive (skb, size); + + /* Free packet buffer */ + bcm570xPktFree (pUmDevice->index, skb); + pUmPacket->skbuff = NULL; + + /* Reuse SKB */ + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + + return 0; /* Got a packet, bail ... */ + } + return size; +} + +/* Shut down device */ +void eth_halt (void) +{ + int i; + if (initialized) + if (pDevice && pUmDevice && pUmDevice->opened) { + printf ("\neth%d:%s,", pUmDevice->index, + pUmDevice->name); + printf ("HALT,"); + /* stop device */ + LM_Halt (pDevice); + printf ("POWER DOWN,"); + LM_SetPowerState (pDevice, LM_POWER_STATE_D3); + + /* Free the memory allocated by the device in tigon3 */ + for (i = 0; i < pUmDevice->mem_list_num; i++) { + if (pUmDevice->mem_list[i]) { + /* sanity check */ + if (pUmDevice->dma_list[i]) { /* cache-safe memory */ + free (pUmDevice->mem_list[i]); + } else { + free (pUmDevice->mem_list[i]); /* normal memory */ + } + } + } + pUmDevice->opened = 0; + free (pDevice); + pDevice = NULL; + pUmDevice = NULL; + initialized = 0; + printf ("done - offline.\n"); + } +} + +/* + * + * Middle Module: Interface between the HW driver (tigon3 modules) and + * the native (SENS) driver. These routines implement the system + * interface for tigon3 on VxWorks. + */ + +/* Middle module dependency - size of a packet descriptor */ +int MM_Packet_Desc_Size = sizeof (UM_PACKET); + +LM_STATUS +MM_ReadConfig32 (PLM_DEVICE_BLOCK pDevice, + LM_UINT32 Offset, LM_UINT32 * pValue32) +{ + UM_DEVICE_BLOCK *pUmDevice; + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_read_config_dword (pUmDevice->pdev, Offset, (u32 *) pValue32); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_WriteConfig32 (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, LM_UINT32 Value32) +{ + UM_DEVICE_BLOCK *pUmDevice; + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_write_config_dword (pUmDevice->pdev, Offset, Value32); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_ReadConfig16 (PLM_DEVICE_BLOCK pDevice, + LM_UINT32 Offset, LM_UINT16 * pValue16) +{ + UM_DEVICE_BLOCK *pUmDevice; + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_read_config_word (pUmDevice->pdev, Offset, (u16 *) pValue16); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_WriteConfig16 (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, LM_UINT16 Value16) +{ + UM_DEVICE_BLOCK *pUmDevice; + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_write_config_word (pUmDevice->pdev, Offset, Value16); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_AllocateSharedMemory (PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID * pMemoryBlockVirt, + PLM_PHYSICAL_ADDRESS pMemoryBlockPhy, LM_BOOL Cached) +{ + PLM_VOID pvirt; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + dma_addr_t mapping; + + pvirt = malloc (BlockSize); + mapping = (dma_addr_t) (pvirt); + if (!pvirt) + return LM_STATUS_FAILURE; + + pUmDevice->mem_list[pUmDevice->mem_list_num] = pvirt; + pUmDevice->dma_list[pUmDevice->mem_list_num] = mapping; + pUmDevice->mem_size_list[pUmDevice->mem_list_num++] = BlockSize; + memset (pvirt, 0, BlockSize); + + *pMemoryBlockVirt = (PLM_VOID) pvirt; + MM_SetAddr (pMemoryBlockPhy, (dma_addr_t) mapping); + + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_AllocateMemory (PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID * pMemoryBlockVirt) +{ + PLM_VOID pvirt; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + + pvirt = malloc (BlockSize); + + if (!pvirt) + return LM_STATUS_FAILURE; + + pUmDevice->mem_list[pUmDevice->mem_list_num] = pvirt; + pUmDevice->dma_list[pUmDevice->mem_list_num] = 0; + pUmDevice->mem_size_list[pUmDevice->mem_list_num++] = BlockSize; + memset (pvirt, 0, BlockSize); + *pMemoryBlockVirt = pvirt; + + return LM_STATUS_SUCCESS; +} + +LM_STATUS MM_MapMemBase (PLM_DEVICE_BLOCK pDevice) +{ + printf ("BCM570x PCI Memory base address @0x%x\n", + (unsigned int)pDevice->pMappedMemBase); + return LM_STATUS_SUCCESS; +} + +LM_STATUS MM_InitializeUmPackets (PLM_DEVICE_BLOCK pDevice) +{ + int i; + void *skb; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + PUM_PACKET pUmPacket = NULL; + PLM_PACKET pPacket = NULL; + + for (i = 0; i < pDevice->RxPacketDescCnt; i++) { + pPacket = QQ_PopHead (&pDevice->RxPacketFreeQ.Container); + pUmPacket = (PUM_PACKET) pPacket; + + if (pPacket == 0) { + printf ("MM_InitializeUmPackets: Bad RxPacketFreeQ\n"); + } + + skb = bcm570xPktAlloc (pUmDevice->index, + pPacket->u.Rx.RxBufferSize + 2); + + if (skb == 0) { + pUmPacket->skbuff = 0; + QQ_PushTail (&pUmDevice->rx_out_of_buf_q.Container, + pPacket); + printf ("MM_InitializeUmPackets: out of buffer.\n"); + continue; + } + + pUmPacket->skbuff = skb; + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + } + + pUmDevice->rx_low_buf_thresh = pDevice->RxPacketDescCnt / 8; + + return LM_STATUS_SUCCESS; +} + +LM_STATUS MM_GetConfig (PLM_DEVICE_BLOCK pDevice) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + int index = pDevice->index; + + if (auto_speed[index] == 0) + pDevice->DisableAutoNeg = TRUE; + else + pDevice->DisableAutoNeg = FALSE; + + if (line_speed[index] == 0) { + pDevice->RequestedMediaType = LM_REQUESTED_MEDIA_TYPE_AUTO; + pDevice->DisableAutoNeg = FALSE; + } else { + if (line_speed[index] == 1000) { + if (pDevice->EnableTbi) { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_FIBER_1000MBPS_FULL_DUPLEX; + } else if (full_duplex[index]) { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_UTP_1000MBPS_FULL_DUPLEX; + } else { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_UTP_1000MBPS; + } + if (!pDevice->EnableTbi) + pDevice->DisableAutoNeg = FALSE; + } else if (line_speed[index] == 100) { + if (full_duplex[index]) { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_UTP_100MBPS_FULL_DUPLEX; + } else { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_UTP_100MBPS; + } + } else if (line_speed[index] == 10) { + if (full_duplex[index]) { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS_FULL_DUPLEX; + } else { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS; + } + } else { + pDevice->RequestedMediaType = + LM_REQUESTED_MEDIA_TYPE_AUTO; + pDevice->DisableAutoNeg = FALSE; + } + + } + pDevice->FlowControlCap = 0; + if (rx_flow_control[index] != 0) { + pDevice->FlowControlCap |= LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + if (tx_flow_control[index] != 0) { + pDevice->FlowControlCap |= LM_FLOW_CONTROL_TRANSMIT_PAUSE; + } + if ((auto_flow_control[index] != 0) && + (pDevice->DisableAutoNeg == FALSE)) { + + pDevice->FlowControlCap |= LM_FLOW_CONTROL_AUTO_PAUSE; + if ((tx_flow_control[index] == 0) && + (rx_flow_control[index] == 0)) { + pDevice->FlowControlCap |= + LM_FLOW_CONTROL_TRANSMIT_PAUSE | + LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + } + + /* Default MTU for now */ + pUmDevice->mtu = 1500; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + if (pUmDevice->mtu > 1500) { + pDevice->RxMtu = pUmDevice->mtu; + pDevice->RxJumboDescCnt = DEFAULT_JUMBO_RCV_DESC_COUNT; + } else { + pDevice->RxJumboDescCnt = 0; + } + pDevice->RxJumboDescCnt = rx_jumbo_desc_cnt[index]; +#else + pDevice->RxMtu = pUmDevice->mtu; +#endif + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + pDevice->UseTaggedStatus = TRUE; + pUmDevice->timer_interval = CONFIG_SYS_HZ; + } else { + pUmDevice->timer_interval = CONFIG_SYS_HZ / 50; + } + + pDevice->TxPacketDescCnt = tx_pkt_desc_cnt[index]; + pDevice->RxStdDescCnt = rx_std_desc_cnt[index]; + /* Note: adaptive coalescence really isn't adaptive in this driver */ + pUmDevice->rx_adaptive_coalesce = rx_adaptive_coalesce[index]; + if (!pUmDevice->rx_adaptive_coalesce) { + pDevice->RxCoalescingTicks = rx_coalesce_ticks[index]; + if (pDevice->RxCoalescingTicks > MAX_RX_COALESCING_TICKS) + pDevice->RxCoalescingTicks = MAX_RX_COALESCING_TICKS; + pUmDevice->rx_curr_coalesce_ticks = pDevice->RxCoalescingTicks; + + pDevice->RxMaxCoalescedFrames = rx_max_coalesce_frames[index]; + if (pDevice->RxMaxCoalescedFrames > MAX_RX_MAX_COALESCED_FRAMES) + pDevice->RxMaxCoalescedFrames = + MAX_RX_MAX_COALESCED_FRAMES; + pUmDevice->rx_curr_coalesce_frames = + pDevice->RxMaxCoalescedFrames; + pDevice->StatsCoalescingTicks = stats_coalesce_ticks[index]; + if (pDevice->StatsCoalescingTicks > MAX_STATS_COALESCING_TICKS) + pDevice->StatsCoalescingTicks = + MAX_STATS_COALESCING_TICKS; + } else { + pUmDevice->rx_curr_coalesce_frames = + DEFAULT_RX_MAX_COALESCED_FRAMES; + pUmDevice->rx_curr_coalesce_ticks = DEFAULT_RX_COALESCING_TICKS; + } + pDevice->TxCoalescingTicks = tx_coalesce_ticks[index]; + if (pDevice->TxCoalescingTicks > MAX_TX_COALESCING_TICKS) + pDevice->TxCoalescingTicks = MAX_TX_COALESCING_TICKS; + pDevice->TxMaxCoalescedFrames = tx_max_coalesce_frames[index]; + if (pDevice->TxMaxCoalescedFrames > MAX_TX_MAX_COALESCED_FRAMES) + pDevice->TxMaxCoalescedFrames = MAX_TX_MAX_COALESCED_FRAMES; + + if (enable_wol[index]) { + pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_MAGIC_PACKET; + pDevice->WakeUpMode = LM_WAKE_UP_MODE_MAGIC_PACKET; + } + pDevice->NicSendBd = TRUE; + + /* Don't update status blocks during interrupt */ + pDevice->RxCoalescingTicksDuringInt = 0; + pDevice->TxCoalescingTicksDuringInt = 0; + + return LM_STATUS_SUCCESS; + +} + +LM_STATUS MM_StartTxDma (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + printf ("Start TX DMA: dev=%d packet @0x%x\n", + (int)pUmDevice->index, (unsigned int)pPacket); + + return LM_STATUS_SUCCESS; +} + +LM_STATUS MM_CompleteTxDma (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + printf ("Complete TX DMA: dev=%d packet @0x%x\n", + (int)pUmDevice->index, (unsigned int)pPacket); + return LM_STATUS_SUCCESS; +} + +LM_STATUS MM_IndicateStatus (PLM_DEVICE_BLOCK pDevice, LM_STATUS Status) +{ + char buf[128]; + char lcd[4]; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + LM_FLOW_CONTROL flow_control; + + pUmDevice->delayed_link_ind = 0; + memset (lcd, 0x0, 4); + + if (Status == LM_STATUS_LINK_DOWN) { + sprintf (buf, "eth%d: %s: NIC Link is down\n", + pUmDevice->index, pUmDevice->name); + lcd[0] = 'L'; + lcd[1] = 'N'; + lcd[2] = 'K'; + lcd[3] = '?'; + } else if (Status == LM_STATUS_LINK_ACTIVE) { + sprintf (buf, "eth%d:%s: ", pUmDevice->index, pUmDevice->name); + + if (pDevice->LineSpeed == LM_LINE_SPEED_1000MBPS) { + strcat (buf, "1000 Mbps "); + lcd[0] = '1'; + lcd[1] = 'G'; + lcd[2] = 'B'; + } else if (pDevice->LineSpeed == LM_LINE_SPEED_100MBPS) { + strcat (buf, "100 Mbps "); + lcd[0] = '1'; + lcd[1] = '0'; + lcd[2] = '0'; + } else if (pDevice->LineSpeed == LM_LINE_SPEED_10MBPS) { + strcat (buf, "10 Mbps "); + lcd[0] = '1'; + lcd[1] = '0'; + lcd[2] = ' '; + } + if (pDevice->DuplexMode == LM_DUPLEX_MODE_FULL) { + strcat (buf, "full duplex"); + lcd[3] = 'F'; + } else { + strcat (buf, "half duplex"); + lcd[3] = 'H'; + } + strcat (buf, " link up"); + + flow_control = pDevice->FlowControl & + (LM_FLOW_CONTROL_RECEIVE_PAUSE | + LM_FLOW_CONTROL_TRANSMIT_PAUSE); + + if (flow_control) { + if (flow_control & LM_FLOW_CONTROL_RECEIVE_PAUSE) { + strcat (buf, ", receive "); + if (flow_control & + LM_FLOW_CONTROL_TRANSMIT_PAUSE) + strcat (buf, " & transmit "); + } else { + strcat (buf, ", transmit "); + } + strcat (buf, "flow control ON"); + } else { + strcat (buf, ", flow control OFF"); + } + strcat (buf, "\n"); + printf ("%s", buf); + } +#if 0 + sysLedDsply (lcd[0], lcd[1], lcd[2], lcd[3]); +#endif + return LM_STATUS_SUCCESS; +} + +LM_STATUS MM_FreeRxBuffer (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + PUM_PACKET pUmPacket; + void *skb; + + pUmPacket = (PUM_PACKET) pPacket; + + if ((skb = pUmPacket->skbuff)) + bcm570xPktFree (pUmDevice->index, skb); + + pUmPacket->skbuff = 0; + + return LM_STATUS_SUCCESS; +} + +unsigned long MM_AnGetCurrentTime_us (PAN_STATE_INFO pAnInfo) +{ + return get_timer (0); +} + +/* + * Transform an MBUF chain into a single MBUF. + * This routine will fail if the amount of data in the + * chain overflows a transmit buffer. In that case, + * the incoming MBUF chain will be freed. This routine can + * also fail by not being able to allocate a new MBUF (including + * cluster and mbuf headers). In that case the failure is + * non-fatal. The incoming cluster chain is not freed, giving + * the caller the choice of whether to try a retransmit later. + */ +LM_STATUS MM_CoalesceTxBuffer (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + PUM_PACKET pUmPacket = (PUM_PACKET) pPacket; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + void *skbnew; + int len = 0; + + if (len == 0) + return (LM_STATUS_SUCCESS); + + if (len > MAX_PACKET_SIZE) { + printf ("eth%d: xmit frame discarded, too big!, size = %d\n", + pUmDevice->index, len); + return (LM_STATUS_FAILURE); + } + + skbnew = bcm570xPktAlloc (pUmDevice->index, MAX_PACKET_SIZE); + + if (skbnew == NULL) { + pUmDevice->tx_full = 1; + printf ("eth%d: out of transmit buffers", pUmDevice->index); + return (LM_STATUS_FAILURE); + } + + /* New packet values */ + pUmPacket->skbuff = skbnew; + pUmPacket->lm_packet.u.Tx.FragCount = 1; + + return (LM_STATUS_SUCCESS); +} + +LM_STATUS MM_IndicateRxPackets (PLM_DEVICE_BLOCK pDevice) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + pUmDevice->rx_pkt = 1; + return LM_STATUS_SUCCESS; +} + +LM_STATUS MM_IndicateTxPackets (PLM_DEVICE_BLOCK pDevice) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + PLM_PACKET pPacket; + PUM_PACKET pUmPacket; + void *skb; + while (TRUE) { + + pPacket = (PLM_PACKET) + QQ_PopHead (&pDevice->TxPacketXmittedQ.Container); + + if (pPacket == 0) + break; + + pUmPacket = (PUM_PACKET) pPacket; + skb = (void *)pUmPacket->skbuff; + + /* + * Free MBLK if we transmitted a fragmented packet or a + * non-fragmented packet straight from the VxWorks + * buffer pool. If packet was copied to a local transmit + * buffer, then there's no MBUF to free, just free + * the transmit buffer back to the cluster pool. + */ + + if (skb) + bcm570xPktFree (pUmDevice->index, skb); + + pUmPacket->skbuff = 0; + QQ_PushTail (&pDevice->TxPacketFreeQ.Container, pPacket); + pUmDevice->tx_pkt = 1; + } + if (pUmDevice->tx_full) { + if (QQ_GetEntryCnt (&pDevice->TxPacketFreeQ.Container) >= + (QQ_GetSize (&pDevice->TxPacketFreeQ.Container) >> 1)) + pUmDevice->tx_full = 0; + } + return LM_STATUS_SUCCESS; +} + +/* + * Scan an MBUF chain until we reach fragment number "frag" + * Return its length and physical address. + */ +void MM_MapTxDma + (PLM_DEVICE_BLOCK pDevice, + struct _LM_PACKET *pPacket, + T3_64BIT_HOST_ADDR * paddr, LM_UINT32 * len, int frag) { + PUM_PACKET pUmPacket = (PUM_PACKET) pPacket; + *len = pPacket->PacketSize; + MM_SetT3Addr (paddr, (dma_addr_t) pUmPacket->skbuff); +} + +/* + * Convert an mbuf address, a CPU local virtual address, + * to a physical address as seen from a PCI device. Store the + * result at paddr. + */ +void MM_MapRxDma (PLM_DEVICE_BLOCK pDevice, + struct _LM_PACKET *pPacket, T3_64BIT_HOST_ADDR * paddr) +{ + PUM_PACKET pUmPacket = (PUM_PACKET) pPacket; + MM_SetT3Addr (paddr, (dma_addr_t) pUmPacket->skbuff); +} + +void MM_SetAddr (LM_PHYSICAL_ADDRESS * paddr, dma_addr_t addr) +{ +#if (BITS_PER_LONG == 64) + paddr->High = ((unsigned long)addr) >> 32; + paddr->Low = ((unsigned long)addr) & 0xffffffff; +#else + paddr->High = 0; + paddr->Low = (unsigned long)addr; +#endif +} + +void MM_SetT3Addr (T3_64BIT_HOST_ADDR * paddr, dma_addr_t addr) +{ + unsigned long baddr = (unsigned long)addr; +#if (BITS_PER_LONG == 64) + set_64bit_addr (paddr, baddr & 0xffffffff, baddr >> 32); +#else + set_64bit_addr (paddr, baddr, 0); +#endif +} + +/* + * This combination of `inline' and `extern' has almost the effect of a + * macro. The way to use it is to put a function definition in a header + * file with these keywords, and put another copy of the definition + * (lacking `inline' and `extern') in a library file. The definition in + * the header file will cause most calls to the function to be inlined. + * If any uses of the function remain, they will refer to the single copy + * in the library. + */ +void atomic_set (atomic_t * entry, int val) +{ + entry->counter = val; +} + +int atomic_read (atomic_t * entry) +{ + return entry->counter; +} + +void atomic_inc (atomic_t * entry) +{ + if (entry) + entry->counter++; +} + +void atomic_dec (atomic_t * entry) +{ + if (entry) + entry->counter--; +} + +void atomic_sub (int a, atomic_t * entry) +{ + if (entry) + entry->counter -= a; +} + +void atomic_add (int a, atomic_t * entry) +{ + if (entry) + entry->counter += a; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +void QQ_InitQueue (PQQ_CONTAINER pQueue, unsigned int QueueSize) +{ + pQueue->Head = 0; + pQueue->Tail = 0; + pQueue->Size = QueueSize + 1; + atomic_set (&pQueue->EntryCnt, 0); +} /* QQ_InitQueue */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +char QQ_Full (PQQ_CONTAINER pQueue) +{ + unsigned int NewHead; + + NewHead = (pQueue->Head + 1) % pQueue->Size; + + return (NewHead == pQueue->Tail); +} /* QQ_Full */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +char QQ_Empty (PQQ_CONTAINER pQueue) +{ + return (pQueue->Head == pQueue->Tail); +} /* QQ_Empty */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +unsigned int QQ_GetSize (PQQ_CONTAINER pQueue) +{ + return pQueue->Size; +} /* QQ_GetSize */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +unsigned int QQ_GetEntryCnt (PQQ_CONTAINER pQueue) +{ + return atomic_read (&pQueue->EntryCnt); +} /* QQ_GetEntryCnt */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* TRUE entry was added successfully. */ +/* FALSE queue is full. */ +/******************************************************************************/ +char QQ_PushHead (PQQ_CONTAINER pQueue, PQQ_ENTRY pEntry) +{ + unsigned int Head; + + Head = (pQueue->Head + 1) % pQueue->Size; + +#if !defined(QQ_NO_OVERFLOW_CHECK) + if (Head == pQueue->Tail) { + return 0; + } /* if */ +#endif /* QQ_NO_OVERFLOW_CHECK */ + + pQueue->Array[pQueue->Head] = pEntry; + wmb (); + pQueue->Head = Head; + atomic_inc (&pQueue->EntryCnt); + + return -1; +} /* QQ_PushHead */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* TRUE entry was added successfully. */ +/* FALSE queue is full. */ +/******************************************************************************/ +char QQ_PushTail (PQQ_CONTAINER pQueue, PQQ_ENTRY pEntry) +{ + unsigned int Tail; + + Tail = pQueue->Tail; + if (Tail == 0) { + Tail = pQueue->Size; + } /* if */ + Tail--; + +#if !defined(QQ_NO_OVERFLOW_CHECK) + if (Tail == pQueue->Head) { + return 0; + } /* if */ +#endif /* QQ_NO_OVERFLOW_CHECK */ + + pQueue->Array[Tail] = pEntry; + wmb (); + pQueue->Tail = Tail; + atomic_inc (&pQueue->EntryCnt); + + return -1; +} /* QQ_PushTail */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +PQQ_ENTRY QQ_PopHead (PQQ_CONTAINER pQueue) +{ + unsigned int Head; + PQQ_ENTRY Entry; + + Head = pQueue->Head; + +#if !defined(QQ_NO_UNDERFLOW_CHECK) + if (Head == pQueue->Tail) { + return (PQQ_ENTRY) 0; + } /* if */ +#endif /* QQ_NO_UNDERFLOW_CHECK */ + + if (Head == 0) { + Head = pQueue->Size; + } /* if */ + Head--; + + Entry = pQueue->Array[Head]; + membar (); + + pQueue->Head = Head; + atomic_dec (&pQueue->EntryCnt); + + return Entry; +} /* QQ_PopHead */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +PQQ_ENTRY QQ_PopTail (PQQ_CONTAINER pQueue) +{ + unsigned int Tail; + PQQ_ENTRY Entry; + + Tail = pQueue->Tail; + +#if !defined(QQ_NO_UNDERFLOW_CHECK) + if (Tail == pQueue->Head) { + return (PQQ_ENTRY) 0; + } /* if */ +#endif /* QQ_NO_UNDERFLOW_CHECK */ + + Entry = pQueue->Array[Tail]; + membar (); + pQueue->Tail = (Tail + 1) % pQueue->Size; + atomic_dec (&pQueue->EntryCnt); + + return Entry; +} /* QQ_PopTail */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +PQQ_ENTRY QQ_GetHead (PQQ_CONTAINER pQueue, unsigned int Idx) +{ + if (Idx >= atomic_read (&pQueue->EntryCnt)) { + return (PQQ_ENTRY) 0; + } + + if (pQueue->Head > Idx) { + Idx = pQueue->Head - Idx; + } else { + Idx = pQueue->Size - (Idx - pQueue->Head); + } + Idx--; + + return pQueue->Array[Idx]; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +PQQ_ENTRY QQ_GetTail (PQQ_CONTAINER pQueue, unsigned int Idx) +{ + if (Idx >= atomic_read (&pQueue->EntryCnt)) { + return (PQQ_ENTRY) 0; + } + + Idx += pQueue->Tail; + if (Idx >= pQueue->Size) { + Idx = Idx - pQueue->Size; + } + + return pQueue->Array[Idx]; +} diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x_autoneg.c b/roms/u-boot-sam460ex/drivers/net/bcm570x_autoneg.c new file mode 100644 index 000000000..9023796aa --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x_autoneg.c @@ -0,0 +1,439 @@ +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2001 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/******************************************************************************/ +#if !defined(CONFIG_NET_MULTI) +#if INCLUDE_TBI_SUPPORT +#include "bcm570x_autoneg.h" +#include "bcm570x_mm.h" + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +void +MM_AnTxConfig( + PAN_STATE_INFO pAnInfo) +{ + PLM_DEVICE_BLOCK pDevice; + + pDevice = (PLM_DEVICE_BLOCK) pAnInfo->pContext; + + REG_WR(pDevice, MacCtrl.TxAutoNeg, (LM_UINT32) pAnInfo->TxConfig.AsUSHORT); + + pDevice->MacMode |= MAC_MODE_SEND_CONFIGS; + REG_WR(pDevice, MacCtrl.Mode, pDevice->MacMode); +} + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +void +MM_AnTxIdle( + PAN_STATE_INFO pAnInfo) +{ + PLM_DEVICE_BLOCK pDevice; + + pDevice = (PLM_DEVICE_BLOCK) pAnInfo->pContext; + + pDevice->MacMode &= ~MAC_MODE_SEND_CONFIGS; + REG_WR(pDevice, MacCtrl.Mode, pDevice->MacMode); +} + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +char +MM_AnRxConfig( + PAN_STATE_INFO pAnInfo, + unsigned short *pRxConfig) +{ + PLM_DEVICE_BLOCK pDevice; + LM_UINT32 Value32; + char Retcode; + + Retcode = AN_FALSE; + + pDevice = (PLM_DEVICE_BLOCK) pAnInfo->pContext; + + Value32 = REG_RD(pDevice, MacCtrl.Status); + if(Value32 & MAC_STATUS_RECEIVING_CFG) + { + Value32 = REG_RD(pDevice, MacCtrl.RxAutoNeg); + *pRxConfig = (unsigned short) Value32; + + Retcode = AN_TRUE; + } + + return Retcode; +} + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +void +AutonegInit( + PAN_STATE_INFO pAnInfo) +{ + unsigned long j; + + for(j = 0; j < sizeof(AN_STATE_INFO); j++) + { + ((unsigned char *) pAnInfo)[j] = 0; + } + + /* Initialize the default advertisement register. */ + pAnInfo->mr_adv_full_duplex = 1; + pAnInfo->mr_adv_sym_pause = 1; + pAnInfo->mr_adv_asym_pause = 1; + pAnInfo->mr_an_enable = 1; +} + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +AUTONEG_STATUS +Autoneg8023z( + PAN_STATE_INFO pAnInfo) +{ + unsigned short RxConfig; + unsigned long Delta_us; + AUTONEG_STATUS AnRet; + + /* Get the current time. */ + if(pAnInfo->State == AN_STATE_UNKNOWN) + { + pAnInfo->RxConfig.AsUSHORT = 0; + pAnInfo->CurrentTime_us = 0; + pAnInfo->LinkTime_us = 0; + pAnInfo->AbilityMatchCfg = 0; + pAnInfo->AbilityMatchCnt = 0; + pAnInfo->AbilityMatch = AN_FALSE; + pAnInfo->IdleMatch = AN_FALSE; + pAnInfo->AckMatch = AN_FALSE; + } + + /* Increment the timer tick. This function is called every microsecon. */ +/* pAnInfo->CurrentTime_us++; */ + + /* Set the AbilityMatch, IdleMatch, and AckMatch flags if their */ + /* corresponding conditions are satisfied. */ + if(MM_AnRxConfig(pAnInfo, &RxConfig)) + { + if(RxConfig != pAnInfo->AbilityMatchCfg) + { + pAnInfo->AbilityMatchCfg = RxConfig; + pAnInfo->AbilityMatch = AN_FALSE; + pAnInfo->AbilityMatchCnt = 0; + } + else + { + pAnInfo->AbilityMatchCnt++; + if(pAnInfo->AbilityMatchCnt > 1) + { + pAnInfo->AbilityMatch = AN_TRUE; + pAnInfo->AbilityMatchCfg = RxConfig; + } + } + + if(RxConfig & AN_CONFIG_ACK) + { + pAnInfo->AckMatch = AN_TRUE; + } + else + { + pAnInfo->AckMatch = AN_FALSE; + } + + pAnInfo->IdleMatch = AN_FALSE; + } + else + { + pAnInfo->IdleMatch = AN_TRUE; + + pAnInfo->AbilityMatchCfg = 0; + pAnInfo->AbilityMatchCnt = 0; + pAnInfo->AbilityMatch = AN_FALSE; + pAnInfo->AckMatch = AN_FALSE; + + RxConfig = 0; + } + + /* Save the last Config. */ + pAnInfo->RxConfig.AsUSHORT = RxConfig; + + /* Default return code. */ + AnRet = AUTONEG_STATUS_OK; + + /* Autoneg state machine as defined in 802.3z section 37.3.1.5. */ + switch(pAnInfo->State) + { + case AN_STATE_UNKNOWN: + if(pAnInfo->mr_an_enable || pAnInfo->mr_restart_an) + { + pAnInfo->CurrentTime_us = 0; + pAnInfo->State = AN_STATE_AN_ENABLE; + } + + /* Fall through.*/ + + case AN_STATE_AN_ENABLE: + pAnInfo->mr_an_complete = AN_FALSE; + pAnInfo->mr_page_rx = AN_FALSE; + + if(pAnInfo->mr_an_enable) + { + pAnInfo->LinkTime_us = 0; + pAnInfo->AbilityMatchCfg = 0; + pAnInfo->AbilityMatchCnt = 0; + pAnInfo->AbilityMatch = AN_FALSE; + pAnInfo->IdleMatch = AN_FALSE; + pAnInfo->AckMatch = AN_FALSE; + + pAnInfo->State = AN_STATE_AN_RESTART_INIT; + } + else + { + pAnInfo->State = AN_STATE_DISABLE_LINK_OK; + } + break; + + case AN_STATE_AN_RESTART_INIT: + pAnInfo->LinkTime_us = pAnInfo->CurrentTime_us; + pAnInfo->mr_np_loaded = AN_FALSE; + + pAnInfo->TxConfig.AsUSHORT = 0; + MM_AnTxConfig(pAnInfo); + + AnRet = AUTONEG_STATUS_TIMER_ENABLED; + + pAnInfo->State = AN_STATE_AN_RESTART; + + /* Fall through.*/ + + case AN_STATE_AN_RESTART: + /* Get the current time and compute the delta with the saved */ + /* link timer. */ + Delta_us = pAnInfo->CurrentTime_us - pAnInfo->LinkTime_us; + if(Delta_us > AN_LINK_TIMER_INTERVAL_US) + { + pAnInfo->State = AN_STATE_ABILITY_DETECT_INIT; + } + else + { + AnRet = AUTONEG_STATUS_TIMER_ENABLED; + } + break; + + case AN_STATE_DISABLE_LINK_OK: + AnRet = AUTONEG_STATUS_DONE; + break; + + case AN_STATE_ABILITY_DETECT_INIT: + /* Note: in the state diagram, this variable is set to */ + /* mr_adv_ability<12>. Is this right?. */ + pAnInfo->mr_toggle_tx = AN_FALSE; + + /* Send the config as advertised in the advertisement register. */ + pAnInfo->TxConfig.AsUSHORT = 0; + pAnInfo->TxConfig.D5_FD = pAnInfo->mr_adv_full_duplex; + pAnInfo->TxConfig.D6_HD = pAnInfo->mr_adv_half_duplex; + pAnInfo->TxConfig.D7_PS1 = pAnInfo->mr_adv_sym_pause; + pAnInfo->TxConfig.D8_PS2 = pAnInfo->mr_adv_asym_pause; + pAnInfo->TxConfig.D12_RF1 = pAnInfo->mr_adv_remote_fault1; + pAnInfo->TxConfig.D13_RF2 = pAnInfo->mr_adv_remote_fault2; + pAnInfo->TxConfig.D15_NP = pAnInfo->mr_adv_next_page; + + MM_AnTxConfig(pAnInfo); + + pAnInfo->State = AN_STATE_ABILITY_DETECT; + + break; + + case AN_STATE_ABILITY_DETECT: + if(pAnInfo->AbilityMatch == AN_TRUE && + pAnInfo->RxConfig.AsUSHORT != 0) + { + pAnInfo->State = AN_STATE_ACK_DETECT_INIT; + } + + break; + + case AN_STATE_ACK_DETECT_INIT: + pAnInfo->TxConfig.D14_ACK = 1; + MM_AnTxConfig(pAnInfo); + + pAnInfo->State = AN_STATE_ACK_DETECT; + + /* Fall through. */ + + case AN_STATE_ACK_DETECT: + if(pAnInfo->AckMatch == AN_TRUE) + { + if((pAnInfo->RxConfig.AsUSHORT & ~AN_CONFIG_ACK) == + (pAnInfo->AbilityMatchCfg & ~AN_CONFIG_ACK)) + { + pAnInfo->State = AN_STATE_COMPLETE_ACK_INIT; + } + else + { + pAnInfo->State = AN_STATE_AN_ENABLE; + } + } + else if(pAnInfo->AbilityMatch == AN_TRUE && + pAnInfo->RxConfig.AsUSHORT == 0) + { + pAnInfo->State = AN_STATE_AN_ENABLE; + } + + break; + + case AN_STATE_COMPLETE_ACK_INIT: + /* Make sure invalid bits are not set. */ + if(pAnInfo->RxConfig.bits.D0 || pAnInfo->RxConfig.bits.D1 || + pAnInfo->RxConfig.bits.D2 || pAnInfo->RxConfig.bits.D3 || + pAnInfo->RxConfig.bits.D4 || pAnInfo->RxConfig.bits.D9 || + pAnInfo->RxConfig.bits.D10 || pAnInfo->RxConfig.bits.D11) + { + AnRet = AUTONEG_STATUS_FAILED; + break; + } + + /* Set up the link partner advertisement register. */ + pAnInfo->mr_lp_adv_full_duplex = pAnInfo->RxConfig.D5_FD; + pAnInfo->mr_lp_adv_half_duplex = pAnInfo->RxConfig.D6_HD; + pAnInfo->mr_lp_adv_sym_pause = pAnInfo->RxConfig.D7_PS1; + pAnInfo->mr_lp_adv_asym_pause = pAnInfo->RxConfig.D8_PS2; + pAnInfo->mr_lp_adv_remote_fault1 = pAnInfo->RxConfig.D12_RF1; + pAnInfo->mr_lp_adv_remote_fault2 = pAnInfo->RxConfig.D13_RF2; + pAnInfo->mr_lp_adv_next_page = pAnInfo->RxConfig.D15_NP; + + pAnInfo->LinkTime_us = pAnInfo->CurrentTime_us; + + pAnInfo->mr_toggle_tx = !pAnInfo->mr_toggle_tx; + pAnInfo->mr_toggle_rx = pAnInfo->RxConfig.bits.D11; + pAnInfo->mr_np_rx = pAnInfo->RxConfig.D15_NP; + pAnInfo->mr_page_rx = AN_TRUE; + + pAnInfo->State = AN_STATE_COMPLETE_ACK; + AnRet = AUTONEG_STATUS_TIMER_ENABLED; + + break; + + case AN_STATE_COMPLETE_ACK: + if(pAnInfo->AbilityMatch == AN_TRUE && + pAnInfo->RxConfig.AsUSHORT == 0) + { + pAnInfo->State = AN_STATE_AN_ENABLE; + break; + } + + Delta_us = pAnInfo->CurrentTime_us - pAnInfo->LinkTime_us; + + if(Delta_us > AN_LINK_TIMER_INTERVAL_US) + { + if(pAnInfo->mr_adv_next_page == 0 || + pAnInfo->mr_lp_adv_next_page == 0) + { + pAnInfo->State = AN_STATE_IDLE_DETECT_INIT; + } + else + { + if(pAnInfo->TxConfig.bits.D15 == 0 && + pAnInfo->mr_np_rx == 0) + { + pAnInfo->State = AN_STATE_IDLE_DETECT_INIT; + } + else + { + AnRet = AUTONEG_STATUS_FAILED; + } + } + } + + break; + + case AN_STATE_IDLE_DETECT_INIT: + pAnInfo->LinkTime_us = pAnInfo->CurrentTime_us; + + MM_AnTxIdle(pAnInfo); + + pAnInfo->State = AN_STATE_IDLE_DETECT; + + AnRet = AUTONEG_STATUS_TIMER_ENABLED; + + break; + + case AN_STATE_IDLE_DETECT: + if(pAnInfo->AbilityMatch == AN_TRUE && + pAnInfo->RxConfig.AsUSHORT == 0) + { + pAnInfo->State = AN_STATE_AN_ENABLE; + break; + } + + Delta_us = pAnInfo->CurrentTime_us - pAnInfo->LinkTime_us; + if(Delta_us > AN_LINK_TIMER_INTERVAL_US) + { +#if 0 +/* if(pAnInfo->IdleMatch == AN_TRUE) */ +/* { */ +#endif + pAnInfo->State = AN_STATE_LINK_OK; +#if 0 +/* } */ +/* else */ +/* { */ +/* AnRet = AUTONEG_STATUS_FAILED; */ +/* break; */ +/* } */ +#endif + } + + break; + + case AN_STATE_LINK_OK: + pAnInfo->mr_an_complete = AN_TRUE; + pAnInfo->mr_link_ok = AN_TRUE; + AnRet = AUTONEG_STATUS_DONE; + + break; + + case AN_STATE_NEXT_PAGE_WAIT_INIT: + break; + + case AN_STATE_NEXT_PAGE_WAIT: + break; + + default: + AnRet = AUTONEG_STATUS_FAILED; + break; + } + + return AnRet; +} +#endif /* INCLUDE_TBI_SUPPORT */ + +#endif /* !defined(CONFIG_NET_MULTI) */ diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x_autoneg.h b/roms/u-boot-sam460ex/drivers/net/bcm570x_autoneg.h new file mode 100644 index 000000000..7830944b8 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x_autoneg.h @@ -0,0 +1,408 @@ +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2001 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/******************************************************************************/ + + +#ifndef AUTONEG_H +#define AUTONEG_H + + +/******************************************************************************/ +/* Constants. */ +/******************************************************************************/ + +#define AN_LINK_TIMER_INTERVAL_US 9000 /* 10ms */ + +/* TRUE, FALSE */ +#define AN_TRUE 1 +#define AN_FALSE 0 + + +/******************************************************************************/ +/* Main data structure for keeping track of 802.3z auto-negotation state */ +/* variables as shown in Figure 37-6 of the IEEE 802.3z specification. */ +/******************************************************************************/ + +typedef struct +{ + /* Current auto-negotiation state. */ + unsigned long State; + #define AN_STATE_UNKNOWN 0 + #define AN_STATE_AN_ENABLE 1 + #define AN_STATE_AN_RESTART_INIT 2 + #define AN_STATE_AN_RESTART 3 + #define AN_STATE_DISABLE_LINK_OK 4 + #define AN_STATE_ABILITY_DETECT_INIT 5 + #define AN_STATE_ABILITY_DETECT 6 + #define AN_STATE_ACK_DETECT_INIT 7 + #define AN_STATE_ACK_DETECT 8 + #define AN_STATE_COMPLETE_ACK_INIT 9 + #define AN_STATE_COMPLETE_ACK 10 + #define AN_STATE_IDLE_DETECT_INIT 11 + #define AN_STATE_IDLE_DETECT 12 + #define AN_STATE_LINK_OK 13 + #define AN_STATE_NEXT_PAGE_WAIT_INIT 14 + #define AN_STATE_NEXT_PAGE_WAIT 16 + + /* Link timer. */ + unsigned long LinkTime_us; + + /* Current time. */ + unsigned long CurrentTime_us; + + /* Need these values for consistency check. */ + unsigned short AbilityMatchCfg; + + /* Ability, idle, and ack match functions. */ + unsigned long AbilityMatchCnt; + char AbilityMatch; + char IdleMatch; + char AckMatch; + + /* Tx config data */ + union + { + /* The TxConfig register is arranged as follows: */ + /* */ + /* MSB LSB */ + /* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ */ + /* | D7| D6| D5| D4| D3| D2| D1| D0|D15|D14|D13|D12|D11|D10| D9| D8| */ + /* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ */ + struct + { +#ifdef BIG_ENDIAN_HOST + unsigned int D7:1; /* PS1 */ + unsigned int D6:1; /* HD */ + unsigned int D5:1; /* FD */ + unsigned int D4:1; + unsigned int D3:1; + unsigned int D2:1; + unsigned int D1:1; + unsigned int D0:1; + unsigned int D15:1; /* NP */ + unsigned int D14:1; /* ACK */ + unsigned int D13:1; /* RF2 */ + unsigned int D12:1; /* RF1 */ + unsigned int D11:1; + unsigned int D10:1; + unsigned int D9:1; + unsigned int D8:1; /* PS2 */ +#else /* BIG_ENDIAN_HOST */ + unsigned int D8:1; /* PS2 */ + unsigned int D9:1; + unsigned int D10:1; + unsigned int D11:1; + unsigned int D12:1; /* RF1 */ + unsigned int D13:1; /* RF2 */ + unsigned int D14:1; /* ACK */ + unsigned int D15:1; /* NP */ + unsigned int D0:1; + unsigned int D1:1; + unsigned int D2:1; + unsigned int D3:1; + unsigned int D4:1; + unsigned int D5:1; /* FD */ + unsigned int D6:1; /* HD */ + unsigned int D7:1; /* PS1 */ +#endif + } bits; + + unsigned short AsUSHORT; + + #define D8_PS2 bits.D8 + #define D12_RF1 bits.D12 + #define D13_RF2 bits.D13 + #define D14_ACK bits.D14 + #define D15_NP bits.D15 + #define D5_FD bits.D5 + #define D6_HD bits.D6 + #define D7_PS1 bits.D7 + } TxConfig; + + /* Rx config data */ + union + { + /* The RxConfig register is arranged as follows: */ + /* */ + /* MSB LSB */ + /* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ */ + /* | D7| D6| D5| D4| D3| D2| D1| D0|D15|D14|D13|D12|D11|D10| D9| D8| */ + /* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ */ + struct + { +#ifdef BIG_ENDIAN_HOST + unsigned int D7:1; /* PS1 */ + unsigned int D6:1; /* HD */ + unsigned int D5:1; /* FD */ + unsigned int D4:1; + unsigned int D3:1; + unsigned int D2:1; + unsigned int D1:1; + unsigned int D0:1; + unsigned int D15:1; /* NP */ + unsigned int D14:1; /* ACK */ + unsigned int D13:1; /* RF2 */ + unsigned int D12:1; /* RF1 */ + unsigned int D11:1; + unsigned int D10:1; + unsigned int D9:1; + unsigned int D8:1; /* PS2 */ +#else /* BIG_ENDIAN_HOST */ + unsigned int D8:1; /* PS2 */ + unsigned int D9:1; + unsigned int D10:1; + unsigned int D11:1; + unsigned int D12:1; /* RF1 */ + unsigned int D13:1; /* RF2 */ + unsigned int D14:1; /* ACK */ + unsigned int D15:1; /* NP */ + unsigned int D0:1; + unsigned int D1:1; + unsigned int D2:1; + unsigned int D3:1; + unsigned int D4:1; + unsigned int D5:1; /* FD */ + unsigned int D6:1; /* HD */ + unsigned int D7:1; /* PS1 */ +#endif + } bits; + + unsigned short AsUSHORT; + } RxConfig; + + #define AN_CONFIG_NP 0x0080 + #define AN_CONFIG_ACK 0x0040 + #define AN_CONFIG_RF2 0x0020 + #define AN_CONFIG_RF1 0x0010 + #define AN_CONFIG_PS2 0x0001 + #define AN_CONFIG_PS1 0x8000 + #define AN_CONFIG_HD 0x4000 + #define AN_CONFIG_FD 0x2000 + + + /* Management registers. */ + + /* Control register. */ + union + { + struct + { + unsigned int an_enable:1; + unsigned int loopback:1; + unsigned int reset:1; + unsigned int restart_an:1; + } bits; + + unsigned short AsUSHORT; + + #define mr_an_enable Mr0.bits.an_enable + #define mr_loopback Mr0.bits.loopback + #define mr_main_reset Mr0.bits.reset + #define mr_restart_an Mr0.bits.restart_an + } Mr0; + + /* Status register. */ + union + { + struct + { + unsigned int an_complete:1; + unsigned int link_ok:1; + } bits; + + unsigned short AsUSHORT; + + #define mr_an_complete Mr1.bits.an_complete + #define mr_link_ok Mr1.bits.link_ok + } Mr1; + + /* Advertisement register. */ + union + { + struct + { + unsigned int reserved_4:5; + unsigned int full_duplex:1; + unsigned int half_duplex:1; + unsigned int sym_pause:1; + unsigned int asym_pause:1; + unsigned int reserved_11:3; + unsigned int remote_fault1:1; + unsigned int remote_fault2:1; + unsigned int reserved_14:1; + unsigned int next_page:1; + } bits; + + unsigned short AsUSHORT; + + #define mr_adv_full_duplex Mr4.bits.full_duplex + #define mr_adv_half_duplex Mr4.bits.half_duplex + #define mr_adv_sym_pause Mr4.bits.sym_pause + #define mr_adv_asym_pause Mr4.bits.asym_pause + #define mr_adv_remote_fault1 Mr4.bits.remote_fault1 + #define mr_adv_remote_fault2 Mr4.bits.remote_fault2 + #define mr_adv_next_page Mr4.bits.next_page + } Mr4; + + /* Link partner advertisement register. */ + union + { + struct + { + unsigned int reserved_4:5; + unsigned int lp_full_duplex:1; + unsigned int lp_half_duplex:1; + unsigned int lp_sym_pause:1; + unsigned int lp_asym_pause:1; + unsigned int reserved_11:3; + unsigned int lp_remote_fault1:1; + unsigned int lp_remote_fault2:1; + unsigned int lp_ack:1; + unsigned int lp_next_page:1; + } bits; + + unsigned short AsUSHORT; + + #define mr_lp_adv_full_duplex Mr5.bits.lp_full_duplex + #define mr_lp_adv_half_duplex Mr5.bits.lp_half_duplex + #define mr_lp_adv_sym_pause Mr5.bits.lp_sym_pause + #define mr_lp_adv_asym_pause Mr5.bits.lp_asym_pause + #define mr_lp_adv_remote_fault1 Mr5.bits.lp_remote_fault1 + #define mr_lp_adv_remote_fault2 Mr5.bits.lp_remote_fault2 + #define mr_lp_adv_next_page Mr5.bits.lp_next_page + } Mr5; + + /* Auto-negotiation expansion register. */ + union + { + struct + { + unsigned int reserved_0:1; + unsigned int page_received:1; + unsigned int next_pageable:1; + unsigned int reserved_15:13; + } bits; + + unsigned short AsUSHORT; + } Mr6; + + /* Auto-negotiation next page transmit register. */ + union + { + struct + { + unsigned int code_field:11; + unsigned int toggle:1; + unsigned int ack2:1; + unsigned int message_page:1; + unsigned int reserved_14:1; + unsigned int next_page:1; + } bits; + + unsigned short AsUSHORT; + + #define mr_np_tx Mr7.AsUSHORT + } Mr7; + + /* Auto-negotiation link partner ability register. */ + union + { + struct + { + unsigned int code_field:11; + unsigned int toggle:1; + unsigned int ack2:1; + unsigned int message_page:1; + unsigned int ack:1; + unsigned int next_page:1; + } bits; + + unsigned short AsUSHORT; + + #define mr_lp_np_rx Mr8.AsUSHORT + } Mr8; + + /* Extended status register. */ + union + { + struct + { + unsigned int reserved_11:12; + unsigned int base1000_t_hd:1; + unsigned int base1000_t_fd:1; + unsigned int base1000_x_hd:1; + unsigned int base1000_x_fd:1; + } bits; + + unsigned short AsUSHORT; + } Mr15; + + /* Miscellaneous state variables. */ + union + { + struct + { + unsigned int toggle_tx:1; + unsigned int toggle_rx:1; + unsigned int np_rx:1; + unsigned int page_rx:1; + unsigned int np_loaded:1; + } bits; + + unsigned short AsUSHORT; + + #define mr_toggle_tx MrMisc.bits.toggle_tx + #define mr_toggle_rx MrMisc.bits.toggle_rx + #define mr_np_rx MrMisc.bits.np_rx + #define mr_page_rx MrMisc.bits.page_rx + #define mr_np_loaded MrMisc.bits.np_loaded + } MrMisc; + + + /* Implement specifics */ + + /* Pointer to the operating system specific data structure. */ + void *pContext; +} AN_STATE_INFO, *PAN_STATE_INFO; + + +/******************************************************************************/ +/* Return code of Autoneg8023z. */ +/******************************************************************************/ + +typedef enum +{ + AUTONEG_STATUS_OK = 0, + AUTONEG_STATUS_DONE = 1, + AUTONEG_STATUS_TIMER_ENABLED = 2, + AUTONEG_STATUS_FAILED = 0xfffffff +} AUTONEG_STATUS, *PAUTONEG_STATUS; + + +/******************************************************************************/ +/* Function prototypes. */ +/******************************************************************************/ + +AUTONEG_STATUS Autoneg8023z(PAN_STATE_INFO pAnInfo); +void AutonegInit(PAN_STATE_INFO pAnInfo); + + +/******************************************************************************/ +/* The following functions are defined in the os-dependent module. */ +/******************************************************************************/ + +void MM_AnTxConfig(PAN_STATE_INFO pAnInfo); +void MM_AnTxIdle(PAN_STATE_INFO pAnInfo); +char MM_AnRxConfig(PAN_STATE_INFO pAnInfo, unsigned short *pRxConfig); + + +#endif /* AUTONEG_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x_bits.h b/roms/u-boot-sam460ex/drivers/net/bcm570x_bits.h new file mode 100644 index 000000000..615d61e98 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x_bits.h @@ -0,0 +1,57 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/* 02/25/00 Hav Khauv Initial version. */ +/******************************************************************************/ + +#ifndef BITS_H +#define BITS_H + + +/******************************************************************************/ +/* Bit Mask definitions */ +/******************************************************************************/ +#define BIT_NONE 0x00 +#define BIT_0 0x01 +#define BIT_1 0x02 +#define BIT_2 0x04 +#define BIT_3 0x08 +#define BIT_4 0x10 +#define BIT_5 0x20 +#define BIT_6 0x40 +#define BIT_7 0x80 +#define BIT_8 0x0100 +#define BIT_9 0x0200 +#define BIT_10 0x0400 +#define BIT_11 0x0800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_16 0x010000 +#define BIT_17 0x020000 +#define BIT_18 0x040000 +#define BIT_19 0x080000 +#define BIT_20 0x100000 +#define BIT_21 0x200000 +#define BIT_22 0x400000 +#define BIT_23 0x800000 +#define BIT_24 0x01000000 +#define BIT_25 0x02000000 +#define BIT_26 0x04000000 +#define BIT_27 0x08000000 +#define BIT_28 0x10000000 +#define BIT_29 0x20000000 +#define BIT_30 0x40000000 +#define BIT_31 0x80000000 + +#endif /* BITS_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x_debug.h b/roms/u-boot-sam460ex/drivers/net/bcm570x_debug.h new file mode 100644 index 000000000..88e209b0f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x_debug.h @@ -0,0 +1,109 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/* 02/25/00 Hav Khauv Initial version. */ +/******************************************************************************/ + +#ifndef DEBUG_H +#define DEBUG_H + +#ifdef VXWORKS +#include <vxWorks.h> +#endif + +/******************************************************************************/ +/* Debug macros */ +/******************************************************************************/ + +/* Code path for controlling output debug messages. */ +/* Define your code path here. */ +#define CP_INIT 0x010000 +#define CP_SEND 0x020000 +#define CP_RCV 0x040000 +#define CP_INT 0x080000 +#define CP_UINIT 0x100000 +#define CP_RESET 0x200000 + +#define CP_ALL (CP_INIT | CP_SEND | CP_RCV | CP_INT | \ + CP_RESET | CP_UINIT) + +#define CP_MASK 0xffff0000 + + +/* Debug message levels. */ +#define LV_VERBOSE 0x03 +#define LV_INFORM 0x02 +#define LV_WARN 0x01 +#define LV_FATAL 0x00 + +#define LV_MASK 0xffff + + +/* Code path and messsage level combined. These are the first argument of */ +/* the DbgMessage macro. */ +#define INIT_V (CP_INIT | LV_VERBOSE) +#define INIT_I (CP_INIT | LV_INFORM) +#define INIT_W (CP_INIT | LV_WARN) +#define SEND_V (CP_SEND | LV_VERBOSE) +#define SEND_I (CP_SEND | LV_INFORM) +#define SEND_W (CP_SEND | LV_WARN) +#define RCV_V (CP_RCV | LV_VERBOSE) +#define RCV_I (CP_RCV | LV_INFORM) +#define RCV_W (CP_RCV | LV_WARN) +#define INT_V (CP_INT | LV_VERBOSE) +#define INT_I (CP_INT | LV_INFORM) +#define INT_W (CP_INT | LV_WARN) +#define UINIT_V (CP_UINIT | LV_VERBOSE) +#define UINIT_I (CP_UINIT | LV_INFORM) +#define UINIT_W (CP_UINIT | LV_WARN) +#define RESET_V (CP_RESET | LV_VERBOSE) +#define RESET_I (CP_RESET | LV_INFORM) +#define RESET_W (CP_RESET | LV_WARN) +#define CPALL_V (CP_ALL | LV_VERBOSE) +#define CPALL_I (CP_ALL | LV_INFORM) +#define CPALL_W (CP_ALL | LV_WARN) + + +/* All code path message levels. */ +#define FATAL (CP_ALL | LV_FATAL) +#define WARN (CP_ALL | LV_WARN) +#define INFORM (CP_ALL | LV_INFORM) +#define VERBOSE (CP_ALL | LV_VERBOSE) + + +/* These constants control the message output. */ +/* Set your debug message output level and code path here. */ +#ifndef DBG_MSG_CP +#define DBG_MSG_CP CP_ALL /* Where to output messages. */ +#endif + +#ifndef DBG_MSG_LV +#define DBG_MSG_LV LV_VERBOSE /* Level of message output. */ +#endif + +/* DbgMessage macro. */ +#if DBG +#define DbgMessage(CNTRL, MESSAGE) \ + if((CNTRL & DBG_MSG_CP) && ((CNTRL & LV_MASK) <= DBG_MSG_LV)) \ + printf MESSAGE +#define DbgBreak() DbgBreakPoint() +#undef STATIC +#define STATIC +#else +#define DbgMessage(CNTRL, MESSAGE) +#define DbgBreak() +#undef STATIC +#define STATIC static +#endif /* DBG */ + + +#endif /* DEBUG_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x_lm.h b/roms/u-boot-sam460ex/drivers/net/bcm570x_lm.h new file mode 100644 index 000000000..c07b76792 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x_lm.h @@ -0,0 +1,434 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/* 02/25/00 Hav Khauv Initial version. */ +/******************************************************************************/ + +#ifndef LM_H +#define LM_H + +#include "bcm570x_queue.h" +#include "bcm570x_bits.h" + +/******************************************************************************/ +/* Basic types. */ +/******************************************************************************/ + +typedef char LM_CHAR, *PLM_CHAR; +typedef unsigned int LM_UINT, *PLM_UINT; +typedef unsigned char LM_UINT8, *PLM_UINT8; +typedef unsigned short LM_UINT16, *PLM_UINT16; +typedef unsigned int LM_UINT32, *PLM_UINT32; +typedef unsigned int LM_COUNTER, *PLM_COUNTER; +typedef void LM_VOID, *PLM_VOID; +typedef char LM_BOOL, *PLM_BOOL; + +/* 64bit value. */ +typedef struct { +#ifdef BIG_ENDIAN_HOST + LM_UINT32 High; + LM_UINT32 Low; +#else /* BIG_ENDIAN_HOST */ + LM_UINT32 Low; + LM_UINT32 High; +#endif /* !BIG_ENDIAN_HOST */ +} LM_UINT64, *PLM_UINT64; + +typedef LM_UINT64 LM_PHYSICAL_ADDRESS, *PLM_PHYSICAL_ADDRESS; + +/* void LM_INC_PHYSICAL_ADDRESS(PLM_PHYSICAL_ADDRESS pAddr,LM_UINT32 IncSize) */ +#define LM_INC_PHYSICAL_ADDRESS(pAddr, IncSize) \ + { \ + LM_UINT32 OrgLow; \ + \ + OrgLow = (pAddr)->Low; \ + (pAddr)->Low += IncSize; \ + if((pAddr)->Low < OrgLow) { \ + (pAddr)->High++; /* Wrap around. */ \ + } \ + } + +#ifndef NULL +#define NULL ((void *) 0) +#endif /* NULL */ + +#ifndef OFFSETOF +#define OFFSETOF(_s, _m) (MM_UINT_PTR(&(((_s *) 0)->_m))) +#endif /* OFFSETOF */ + +/******************************************************************************/ +/* Simple macros. */ +/******************************************************************************/ + +#define IS_ETH_BROADCAST(_pEthAddr) \ + (((unsigned char *) (_pEthAddr))[0] == ((unsigned char) 0xff)) + +#define IS_ETH_MULTICAST(_pEthAddr) \ + (((unsigned char *) (_pEthAddr))[0] & ((unsigned char) 0x01)) + +#define IS_ETH_ADDRESS_EQUAL(_pEtherAddr1, _pEtherAddr2) \ + ((((unsigned char *) (_pEtherAddr1))[0] == \ + ((unsigned char *) (_pEtherAddr2))[0]) && \ + (((unsigned char *) (_pEtherAddr1))[1] == \ + ((unsigned char *) (_pEtherAddr2))[1]) && \ + (((unsigned char *) (_pEtherAddr1))[2] == \ + ((unsigned char *) (_pEtherAddr2))[2]) && \ + (((unsigned char *) (_pEtherAddr1))[3] == \ + ((unsigned char *) (_pEtherAddr2))[3]) && \ + (((unsigned char *) (_pEtherAddr1))[4] == \ + ((unsigned char *) (_pEtherAddr2))[4]) && \ + (((unsigned char *) (_pEtherAddr1))[5] == \ + ((unsigned char *) (_pEtherAddr2))[5])) + +#define COPY_ETH_ADDRESS(_Src, _Dst) \ + ((unsigned char *) (_Dst))[0] = ((unsigned char *) (_Src))[0]; \ + ((unsigned char *) (_Dst))[1] = ((unsigned char *) (_Src))[1]; \ + ((unsigned char *) (_Dst))[2] = ((unsigned char *) (_Src))[2]; \ + ((unsigned char *) (_Dst))[3] = ((unsigned char *) (_Src))[3]; \ + ((unsigned char *) (_Dst))[4] = ((unsigned char *) (_Src))[4]; \ + ((unsigned char *) (_Dst))[5] = ((unsigned char *) (_Src))[5]; + +/******************************************************************************/ +/* Constants. */ +/******************************************************************************/ + +#define ETHERNET_ADDRESS_SIZE 6 +#define ETHERNET_PACKET_HEADER_SIZE 14 +#define MIN_ETHERNET_PACKET_SIZE 64 /* with 4 byte crc. */ +#define MAX_ETHERNET_PACKET_SIZE 1518 /* with 4 byte crc. */ +#define MIN_ETHERNET_PACKET_SIZE_NO_CRC 60 +#define MAX_ETHERNET_PACKET_SIZE_NO_CRC 1514 +#define MAX_ETHERNET_PACKET_BUFFER_SIZE 1536 /* A nice even number. */ + +#ifndef LM_MAX_MC_TABLE_SIZE +#define LM_MAX_MC_TABLE_SIZE 32 +#endif /* LM_MAX_MC_TABLE_SIZE */ +#define LM_MC_ENTRY_SIZE (ETHERNET_ADDRESS_SIZE+1) +#define LM_MC_INSTANCE_COUNT_INDEX (LM_MC_ENTRY_SIZE-1) + +/* Receive filter masks. */ +#define LM_ACCEPT_UNICAST 0x0001 +#define LM_ACCEPT_MULTICAST 0x0002 +#define LM_ACCEPT_ALL_MULTICAST 0x0004 +#define LM_ACCEPT_BROADCAST 0x0008 +#define LM_ACCEPT_ERROR_PACKET 0x0010 + +#define LM_PROMISCUOUS_MODE 0x10000 + +/******************************************************************************/ +/* PCI registers. */ +/******************************************************************************/ + +#define PCI_VENDOR_ID_REG 0x00 +#define PCI_DEVICE_ID_REG 0x02 + +#define PCI_COMMAND_REG 0x04 +#define PCI_IO_SPACE_ENABLE 0x0001 +#define PCI_MEM_SPACE_ENABLE 0x0002 +#define PCI_BUSMASTER_ENABLE 0x0004 +#define PCI_MEMORY_WRITE_INVALIDATE 0x0010 +#define PCI_PARITY_ERROR_ENABLE 0x0040 +#define PCI_SYSTEM_ERROR_ENABLE 0x0100 +#define PCI_FAST_BACK_TO_BACK_ENABLE 0x0200 + +#define PCI_STATUS_REG 0x06 +#define PCI_REV_ID_REG 0x08 + +#define PCI_CACHE_LINE_SIZE_REG 0x0c + +#define PCI_IO_BASE_ADDR_REG 0x10 +#define PCI_IO_BASE_ADDR_MASK 0xfffffff0 + +#define PCI_MEM_BASE_ADDR_LOW 0x10 +#define PCI_MEM_BASE_ADDR_HIGH 0x14 + +#define PCI_SUBSYSTEM_VENDOR_ID_REG 0x2c +#define PCI_SUBSYSTEM_ID_REG 0x2e +#define PCI_INT_LINE_REG 0x3c + +#define PCIX_CAP_REG 0x40 +#define PCIX_ENABLE_RELAXED_ORDERING BIT_17 + +/******************************************************************************/ +/* Fragment structure. */ +/******************************************************************************/ + +typedef struct { + LM_UINT32 FragSize; + LM_PHYSICAL_ADDRESS FragBuf; +} LM_FRAG, *PLM_FRAG; + +typedef struct { + /* FragCount is initialized for the caller to the maximum array size, on */ + /* return FragCount is the number of the actual fragments in the array. */ + LM_UINT32 FragCount; + + /* Total buffer size. */ + LM_UINT32 TotalSize; + + /* Fragment array buffer. */ + LM_FRAG Fragments[1]; +} LM_FRAG_LIST, *PLM_FRAG_LIST; + +#define DECLARE_FRAG_LIST_BUFFER_TYPE(_FRAG_LIST_TYPE_NAME, _MAX_FRAG_COUNT) \ + typedef struct { \ + LM_FRAG_LIST FragList; \ + LM_FRAG FragListBuffer[_MAX_FRAG_COUNT-1]; \ + } _FRAG_LIST_TYPE_NAME, *P##_FRAG_LIST_TYPE_NAME + +/******************************************************************************/ +/* Status codes. */ +/******************************************************************************/ + +#define LM_STATUS_SUCCESS 0 +#define LM_STATUS_FAILURE 1 + +#define LM_STATUS_INTERRUPT_ACTIVE 2 +#define LM_STATUS_INTERRUPT_NOT_ACTIVE 3 + +#define LM_STATUS_LINK_ACTIVE 4 +#define LM_STATUS_LINK_DOWN 5 +#define LM_STATUS_LINK_SETTING_MISMATCH 6 + +#define LM_STATUS_TOO_MANY_FRAGMENTS 7 +#define LM_STATUS_TRANSMIT_ABORTED 8 +#define LM_STATUS_TRANSMIT_ERROR 9 +#define LM_STATUS_RECEIVE_ABORTED 10 +#define LM_STATUS_RECEIVE_ERROR 11 +#define LM_STATUS_INVALID_PACKET_SIZE 12 +#define LM_STATUS_OUT_OF_MAP_REGISTERS 13 +#define LM_STATUS_UNKNOWN_ADAPTER 14 + +typedef LM_UINT LM_STATUS, *PLM_STATUS; + +/******************************************************************************/ +/* Requested media type. */ +/******************************************************************************/ + +#define LM_REQUESTED_MEDIA_TYPE_AUTO 0 +#define LM_REQUESTED_MEDIA_TYPE_BNC 1 +#define LM_REQUESTED_MEDIA_TYPE_UTP_AUTO 2 +#define LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS 3 +#define LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS_FULL_DUPLEX 4 +#define LM_REQUESTED_MEDIA_TYPE_UTP_100MBPS 5 +#define LM_REQUESTED_MEDIA_TYPE_UTP_100MBPS_FULL_DUPLEX 6 +#define LM_REQUESTED_MEDIA_TYPE_UTP_1000MBPS 7 +#define LM_REQUESTED_MEDIA_TYPE_UTP_1000MBPS_FULL_DUPLEX 8 +#define LM_REQUESTED_MEDIA_TYPE_FIBER_100MBPS 9 +#define LM_REQUESTED_MEDIA_TYPE_FIBER_100MBPS_FULL_DUPLEX 10 +#define LM_REQUESTED_MEDIA_TYPE_FIBER_1000MBPS 11 +#define LM_REQUESTED_MEDIA_TYPE_FIBER_1000MBPS_FULL_DUPLEX 12 +#define LM_REQUESTED_MEDIA_TYPE_MAC_LOOPBACK 0xfffe +#define LM_REQUESTED_MEDIA_TYPE_PHY_LOOPBACK 0xffff + +typedef LM_UINT32 LM_REQUESTED_MEDIA_TYPE, *PLM_REQUESTED_MEDIA_TYPE; + +/******************************************************************************/ +/* Media type. */ +/******************************************************************************/ + +#define LM_MEDIA_TYPE_UNKNOWN -1 +#define LM_MEDIA_TYPE_AUTO 0 +#define LM_MEDIA_TYPE_UTP 1 +#define LM_MEDIA_TYPE_BNC 2 +#define LM_MEDIA_TYPE_AUI 3 +#define LM_MEDIA_TYPE_FIBER 4 + +typedef LM_UINT32 LM_MEDIA_TYPE, *PLM_MEDIA_TYPE; + +/******************************************************************************/ +/* Line speed. */ +/******************************************************************************/ + +#define LM_LINE_SPEED_UNKNOWN 0 +#define LM_LINE_SPEED_10MBPS 1 +#define LM_LINE_SPEED_100MBPS 2 +#define LM_LINE_SPEED_1000MBPS 3 + +typedef LM_UINT32 LM_LINE_SPEED, *PLM_LINE_SPEED; + +/******************************************************************************/ +/* Duplex mode. */ +/******************************************************************************/ + +#define LM_DUPLEX_MODE_UNKNOWN 0 +#define LM_DUPLEX_MODE_HALF 1 +#define LM_DUPLEX_MODE_FULL 2 + +typedef LM_UINT32 LM_DUPLEX_MODE, *PLM_DUPLEX_MODE; + +/******************************************************************************/ +/* Power state. */ +/******************************************************************************/ + +#define LM_POWER_STATE_D0 0 +#define LM_POWER_STATE_D1 1 +#define LM_POWER_STATE_D2 2 +#define LM_POWER_STATE_D3 3 + +typedef LM_UINT32 LM_POWER_STATE, *PLM_POWER_STATE; + +/******************************************************************************/ +/* Task offloading. */ +/******************************************************************************/ + +#define LM_TASK_OFFLOAD_NONE 0x0000 +#define LM_TASK_OFFLOAD_TX_IP_CHECKSUM 0x0001 +#define LM_TASK_OFFLOAD_RX_IP_CHECKSUM 0x0002 +#define LM_TASK_OFFLOAD_TX_TCP_CHECKSUM 0x0004 +#define LM_TASK_OFFLOAD_RX_TCP_CHECKSUM 0x0008 +#define LM_TASK_OFFLOAD_TX_UDP_CHECKSUM 0x0010 +#define LM_TASK_OFFLOAD_RX_UDP_CHECKSUM 0x0020 +#define LM_TASK_OFFLOAD_TCP_SEGMENTATION 0x0040 + +typedef LM_UINT32 LM_TASK_OFFLOAD, *PLM_TASK_OFFLOAD; + +/******************************************************************************/ +/* Flow control. */ +/******************************************************************************/ + +#define LM_FLOW_CONTROL_NONE 0x00 +#define LM_FLOW_CONTROL_RECEIVE_PAUSE 0x01 +#define LM_FLOW_CONTROL_TRANSMIT_PAUSE 0x02 +#define LM_FLOW_CONTROL_RX_TX_PAUSE (LM_FLOW_CONTROL_RECEIVE_PAUSE | \ + LM_FLOW_CONTROL_TRANSMIT_PAUSE) + +/* This value can be or-ed with RECEIVE_PAUSE and TRANSMIT_PAUSE. If the */ +/* auto-negotiation is disabled and the RECEIVE_PAUSE and TRANSMIT_PAUSE */ +/* bits are set, then flow control is enabled regardless of link partner's */ +/* flow control capability. */ +#define LM_FLOW_CONTROL_AUTO_PAUSE 0x80000000 + +typedef LM_UINT32 LM_FLOW_CONTROL, *PLM_FLOW_CONTROL; + +/******************************************************************************/ +/* Wake up mode. */ +/******************************************************************************/ + +#define LM_WAKE_UP_MODE_NONE 0 +#define LM_WAKE_UP_MODE_MAGIC_PACKET 1 +#define LM_WAKE_UP_MODE_NWUF 2 +#define LM_WAKE_UP_MODE_LINK_CHANGE 4 + +typedef LM_UINT32 LM_WAKE_UP_MODE, *PLM_WAKE_UP_MODE; + +/******************************************************************************/ +/* Counters. */ +/******************************************************************************/ + +#define LM_COUNTER_FRAMES_XMITTED_OK 0 +#define LM_COUNTER_FRAMES_RECEIVED_OK 1 +#define LM_COUNTER_ERRORED_TRANSMIT_COUNT 2 +#define LM_COUNTER_ERRORED_RECEIVE_COUNT 3 +#define LM_COUNTER_RCV_CRC_ERROR 4 +#define LM_COUNTER_ALIGNMENT_ERROR 5 +#define LM_COUNTER_SINGLE_COLLISION_FRAMES 6 +#define LM_COUNTER_MULTIPLE_COLLISION_FRAMES 7 +#define LM_COUNTER_FRAMES_DEFERRED 8 +#define LM_COUNTER_MAX_COLLISIONS 9 +#define LM_COUNTER_RCV_OVERRUN 10 +#define LM_COUNTER_XMIT_UNDERRUN 11 +#define LM_COUNTER_UNICAST_FRAMES_XMIT 12 +#define LM_COUNTER_MULTICAST_FRAMES_XMIT 13 +#define LM_COUNTER_BROADCAST_FRAMES_XMIT 14 +#define LM_COUNTER_UNICAST_FRAMES_RCV 15 +#define LM_COUNTER_MULTICAST_FRAMES_RCV 16 +#define LM_COUNTER_BROADCAST_FRAMES_RCV 17 + +typedef LM_UINT32 LM_COUNTER_TYPE, *PLM_COUNTER_TYPE; + +/******************************************************************************/ +/* Forward definition. */ +/******************************************************************************/ + +typedef struct _LM_DEVICE_BLOCK *PLM_DEVICE_BLOCK; +typedef struct _LM_PACKET *PLM_PACKET; + +/******************************************************************************/ +/* Function prototypes. */ +/******************************************************************************/ + +LM_STATUS LM_GetAdapterInfo (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_InitializeAdapter (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_ResetAdapter (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_DisableInterrupt (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_EnableInterrupt (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_SendPacket (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS LM_ServiceInterrupts (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_QueueRxPackets (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_SetReceiveMask (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Mask); +LM_STATUS LM_Halt (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_Abort (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_MulticastAdd (PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMcAddress); +LM_STATUS LM_MulticastDel (PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMcAddress); +LM_STATUS LM_MulticastClear (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_SetMacAddress (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_LoopbackAddress (PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pAddress); + +LM_UINT32 LM_GetCrcCounter (PLM_DEVICE_BLOCK pDevice); + +LM_WAKE_UP_MODE LM_PMCapabilities (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_NwufAdd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 ByteMaskSize, + LM_UINT8 * pByteMask, LM_UINT8 * pPattern); +LM_STATUS LM_NwufRemove (PLM_DEVICE_BLOCK pDevice, LM_UINT32 ByteMaskSize, + LM_UINT8 * pByteMask, LM_UINT8 * pPattern); +LM_STATUS LM_SetPowerState (PLM_DEVICE_BLOCK pDevice, + LM_POWER_STATE PowerLevel); + +LM_VOID LM_ReadPhy (PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, + PLM_UINT32 pData32); +LM_VOID LM_WritePhy (PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, + LM_UINT32 Data32); + +LM_STATUS LM_ControlLoopBack (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Control); +LM_STATUS LM_SetupPhy (PLM_DEVICE_BLOCK pDevice); +int LM_BlinkLED (PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlinkDuration); + +/******************************************************************************/ +/* These are the OS specific functions called by LMAC. */ +/******************************************************************************/ + +LM_STATUS MM_ReadConfig16 (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 * pValue16); +LM_STATUS MM_WriteConfig16 (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 Value16); +LM_STATUS MM_ReadConfig32 (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 * pValue32); +LM_STATUS MM_WriteConfig32 (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 Value32); +LM_STATUS MM_MapMemBase (PLM_DEVICE_BLOCK pDevice); +LM_STATUS MM_MapIoBase (PLM_DEVICE_BLOCK pDevice); +LM_STATUS MM_IndicateRxPackets (PLM_DEVICE_BLOCK pDevice); +LM_STATUS MM_IndicateTxPackets (PLM_DEVICE_BLOCK pDevice); +LM_STATUS MM_StartTxDma (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS MM_CompleteTxDma (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS MM_AllocateMemory (PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID * pMemoryBlockVirt); +LM_STATUS MM_AllocateSharedMemory (PLM_DEVICE_BLOCK pDevice, + LM_UINT32 BlockSize, + PLM_VOID * pMemoryBlockVirt, + PLM_PHYSICAL_ADDRESS pMemoryBlockPhy, + LM_BOOL Cached); +LM_STATUS MM_GetConfig (PLM_DEVICE_BLOCK pDevice); +LM_STATUS MM_IndicateStatus (PLM_DEVICE_BLOCK pDevice, LM_STATUS Status); +LM_STATUS MM_InitializeUmPackets (PLM_DEVICE_BLOCK pDevice); +LM_STATUS MM_FreeRxBuffer (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS MM_CoalesceTxBuffer (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket); +LM_STATUS LM_MbufWorkAround (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_SetLinkSpeed (PLM_DEVICE_BLOCK pDevice, + LM_REQUESTED_MEDIA_TYPE RequestedMediaType); + +#if INCLUDE_5703_A0_FIX +LM_STATUS LM_Load5703DmaWFirmware (PLM_DEVICE_BLOCK pDevice); +#endif + +#endif /* LM_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x_mm.h b/roms/u-boot-sam460ex/drivers/net/bcm570x_mm.h new file mode 100644 index 000000000..ff5302f47 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x_mm.h @@ -0,0 +1,158 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/******************************************************************************/ + +#ifndef MM_H +#define MM_H + +#define __raw_readl readl +#define __raw_writel writel + +#define BIG_ENDIAN_HOST 1 +#define readl(addr) (*(volatile unsigned int*)(addr)) +#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) + +/* Define memory barrier function here if needed */ +#define wmb() +#define membar() +#include <common.h> +#include <asm/types.h> +#include "bcm570x_lm.h" +#include "bcm570x_queue.h" +#include "tigon3.h" +#include <pci.h> + +#define FALSE 0 +#define TRUE 1 +#define ERROR -1 + +#if DBG +#define STATIC +#else +#define STATIC static +#endif + +extern int MM_Packet_Desc_Size; + +#define MM_PACKET_DESC_SIZE MM_Packet_Desc_Size + +DECLARE_QUEUE_TYPE (UM_RX_PACKET_Q, MAX_RX_PACKET_DESC_COUNT + 1); + +#define MAX_MEM 16 + +/* Synch */ +typedef int mutex_t; +typedef int spinlock_t; + +/* Embedded device control */ +typedef struct _UM_DEVICE_BLOCK { + LM_DEVICE_BLOCK lm_dev; + pci_dev_t pdev; + char *name; + void *mem_list[MAX_MEM]; + dma_addr_t dma_list[MAX_MEM]; + int mem_size_list[MAX_MEM]; + int mem_list_num; + int mtu; + int index; + int opened; + int delayed_link_ind; /* Delay link status during initial load */ + int adapter_just_inited; /* the first few seconds after init. */ + int spurious_int; /* new -- unsupported */ + int timer_interval; + int adaptive_expiry; + int crc_counter_expiry; /* new -- unsupported */ + int poll_tib_expiry; /* new -- unsupported */ + int tx_full; + int tx_queued; + int line_speed; /* in Mbps, 0 if link is down */ + UM_RX_PACKET_Q rx_out_of_buf_q; + int rx_out_of_buf; + int rx_low_buf_thresh; /* changed to rx_buf_repl_thresh */ + int rx_buf_repl_panic_thresh; + int rx_buf_align; /* new -- unsupported */ + int do_global_lock; + mutex_t global_lock; + mutex_t undi_lock; + long undi_flags; + volatile int interrupt; + int tasklet_pending; + int tasklet_busy; /* new -- unsupported */ + int rx_pkt; + int tx_pkt; +#ifdef NICE_SUPPORT /* unsupported, this is a linux ioctl */ + void (*nice_rx) (void *, void *); + void *nice_ctx; +#endif /* NICE_SUPPORT */ + int rx_adaptive_coalesce; + unsigned int rx_last_cnt; + unsigned int tx_last_cnt; + unsigned int rx_curr_coalesce_frames; + unsigned int rx_curr_coalesce_ticks; + unsigned int tx_curr_coalesce_frames; /* new -- unsupported */ +#if TIGON3_DEBUG /* new -- unsupported */ + uint tx_zc_count; + uint tx_chksum_count; + uint tx_himem_count; + uint rx_good_chksum_count; +#endif + unsigned int rx_bad_chksum_count; /* new -- unsupported */ + unsigned int rx_misc_errors; /* new -- unsupported */ +} UM_DEVICE_BLOCK, *PUM_DEVICE_BLOCK; + +/* Physical/PCI DMA address */ +typedef union { + dma_addr_t dma_map; +} dma_map_t; + +/* Packet */ +typedef struct + _UM_PACKET { + LM_PACKET lm_packet; + void *skbuff; /* Address of packet buffer */ +} UM_PACKET, *PUM_PACKET; + +#define MM_ACQUIRE_UNDI_LOCK(_pDevice) +#define MM_RELEASE_UNDI_LOCK(_pDevice) +#define MM_ACQUIRE_INT_LOCK(_pDevice) +#define MM_RELEASE_INT_LOCK(_pDevice) +#define MM_UINT_PTR(_ptr) ((unsigned long) (_ptr)) + +/* Macro for setting 64bit address struct */ +#define set_64bit_addr(paddr, low, high) \ + (paddr)->Low = low; \ + (paddr)->High = high; + +/* Assume that PCI controller's view of host memory is same as host */ + +#define MEM_TO_PCI_PHYS(addr) (addr) + +extern void MM_SetAddr (LM_PHYSICAL_ADDRESS * paddr, dma_addr_t addr); +extern void MM_SetT3Addr (T3_64BIT_HOST_ADDR * paddr, dma_addr_t addr); +extern void MM_MapTxDma (PLM_DEVICE_BLOCK pDevice, + struct _LM_PACKET *pPacket, T3_64BIT_HOST_ADDR * paddr, + LM_UINT32 * len, int frag); +extern void MM_MapRxDma (PLM_DEVICE_BLOCK pDevice, + struct _LM_PACKET *pPacket, + T3_64BIT_HOST_ADDR * paddr); + +/* BSP needs to provide sysUsecDelay and sysSerialPrintString */ +extern void sysSerialPrintString (char *s); +#define MM_Wait(usec) udelay(usec) + +/* Define memory barrier function here if needed */ +#define wmb() + +#if 0 +#define cpu_to_le32(val) LONGSWAP(val) +#endif +#endif /* MM_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/bcm570x_queue.h b/roms/u-boot-sam460ex/drivers/net/bcm570x_queue.h new file mode 100644 index 000000000..336b3caa4 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bcm570x_queue.h @@ -0,0 +1,387 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* Queue functions. */ +/* void QQ_InitQueue(PQQ_CONTAINER pQueue) */ +/* char QQ_Full(PQQ_CONTAINER pQueue) */ +/* char QQ_Empty(PQQ_CONTAINER pQueue) */ +/* unsigned int QQ_GetSize(PQQ_CONTAINER pQueue) */ +/* unsigned int QQ_GetEntryCnt(PQQ_CONTAINER pQueue) */ +/* char QQ_PushHead(PQQ_CONTAINER pQueue, PQQ_ENTRY pEntry) */ +/* char QQ_PushTail(PQQ_CONTAINER pQueue, PQQ_ENTRY pEntry) */ +/* PQQ_ENTRY QQ_PopHead(PQQ_CONTAINER pQueue) */ +/* PQQ_ENTRY QQ_PopTail(PQQ_CONTAINER pQueue) */ +/* PQQ_ENTRY QQ_GetHead(PQQ_CONTAINER pQueue, unsigned int Idx) */ +/* PQQ_ENTRY QQ_GetTail(PQQ_CONTAINER pQueue, unsigned int Idx) */ +/* */ +/* */ +/* History: */ +/* 02/25/00 Hav Khauv Initial version. */ +/******************************************************************************/ + +#ifndef BCM_QUEUE_H +#define BCM_QUEUE_H +#ifndef EMBEDDED +#define EMBEDDED 1 +#endif + +/******************************************************************************/ +/* Queue definitions. */ +/******************************************************************************/ + +/* Entry for queueing. */ +typedef void *PQQ_ENTRY; + +/* Linux Atomic Ops support */ +typedef struct { int counter; } atomic_t; + + +/* + * This combination of `inline' and `extern' has almost the effect of a + * macro. The way to use it is to put a function definition in a header + * file with these keywords, and put another copy of the definition + * (lacking `inline' and `extern') in a library file. The definition in + * the header file will cause most calls to the function to be inlined. + * If any uses of the function remain, they will refer to the single copy + * in the library. + */ +extern __inline void +atomic_set(atomic_t* entry, int val) +{ + entry->counter = val; +} +extern __inline int +atomic_read(atomic_t* entry) +{ + return entry->counter; +} +extern __inline void +atomic_inc(atomic_t* entry) +{ + if(entry) + entry->counter++; +} + +extern __inline void +atomic_dec(atomic_t* entry) +{ + if(entry) + entry->counter--; +} + +extern __inline void +atomic_sub(int a, atomic_t* entry) +{ + if(entry) + entry->counter -= a; +} +extern __inline void +atomic_add(int a, atomic_t* entry) +{ + if(entry) + entry->counter += a; +} + + +/* Queue header -- base type. */ +typedef struct { + unsigned int Head; + unsigned int Tail; + unsigned int Size; + atomic_t EntryCnt; + PQQ_ENTRY Array[1]; +} QQ_CONTAINER, *PQQ_CONTAINER; + + +/* Declare queue type macro. */ +#define DECLARE_QUEUE_TYPE(_QUEUE_TYPE, _QUEUE_SIZE) \ + \ + typedef struct { \ + QQ_CONTAINER Container; \ + PQQ_ENTRY EntryBuffer[_QUEUE_SIZE]; \ + } _QUEUE_TYPE, *P##_QUEUE_TYPE + + +/******************************************************************************/ +/* Compilation switches. */ +/******************************************************************************/ + +#if DBG +#undef QQ_NO_OVERFLOW_CHECK +#undef QQ_NO_UNDERFLOW_CHECK +#endif /* DBG */ + +#ifdef QQ_USE_MACROS +/* notdone */ +#else + +#ifdef QQ_NO_INLINE +#define __inline +#endif /* QQ_NO_INLINE */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline void +QQ_InitQueue( +PQQ_CONTAINER pQueue, +unsigned int QueueSize) { + pQueue->Head = 0; + pQueue->Tail = 0; + pQueue->Size = QueueSize+1; + atomic_set(&pQueue->EntryCnt, 0); +} /* QQ_InitQueue */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline char +QQ_Full( +PQQ_CONTAINER pQueue) { + unsigned int NewHead; + + NewHead = (pQueue->Head + 1) % pQueue->Size; + + return(NewHead == pQueue->Tail); +} /* QQ_Full */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline char +QQ_Empty( +PQQ_CONTAINER pQueue) { + return(pQueue->Head == pQueue->Tail); +} /* QQ_Empty */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline unsigned int +QQ_GetSize( +PQQ_CONTAINER pQueue) { + return pQueue->Size; +} /* QQ_GetSize */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline unsigned int +QQ_GetEntryCnt( +PQQ_CONTAINER pQueue) { + return atomic_read(&pQueue->EntryCnt); +} /* QQ_GetEntryCnt */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* TRUE entry was added successfully. */ +/* FALSE queue is full. */ +/******************************************************************************/ +extern __inline char +QQ_PushHead( +PQQ_CONTAINER pQueue, +PQQ_ENTRY pEntry) { + unsigned int Head; + + Head = (pQueue->Head + 1) % pQueue->Size; + +#if !defined(QQ_NO_OVERFLOW_CHECK) + if(Head == pQueue->Tail) { + return 0; + } /* if */ +#endif /* QQ_NO_OVERFLOW_CHECK */ + + pQueue->Array[pQueue->Head] = pEntry; + wmb(); + pQueue->Head = Head; + atomic_inc(&pQueue->EntryCnt); + + return -1; +} /* QQ_PushHead */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* TRUE entry was added successfully. */ +/* FALSE queue is full. */ +/******************************************************************************/ +extern __inline char +QQ_PushTail( +PQQ_CONTAINER pQueue, +PQQ_ENTRY pEntry) { + unsigned int Tail; + + Tail = pQueue->Tail; + if(Tail == 0) { + Tail = pQueue->Size; + } /* if */ + Tail--; + +#if !defined(QQ_NO_OVERFLOW_CHECK) + if(Tail == pQueue->Head) { + return 0; + } /* if */ +#endif /* QQ_NO_OVERFLOW_CHECK */ + + pQueue->Array[Tail] = pEntry; + wmb(); + pQueue->Tail = Tail; + atomic_inc(&pQueue->EntryCnt); + + return -1; +} /* QQ_PushTail */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline PQQ_ENTRY +QQ_PopHead( +PQQ_CONTAINER pQueue) { + unsigned int Head; + PQQ_ENTRY Entry; + + Head = pQueue->Head; + +#if !defined(QQ_NO_UNDERFLOW_CHECK) + if(Head == pQueue->Tail) { + return (PQQ_ENTRY) 0; + } /* if */ +#endif /* QQ_NO_UNDERFLOW_CHECK */ + + if(Head == 0) { + Head = pQueue->Size; + } /* if */ + Head--; + + Entry = pQueue->Array[Head]; +#ifdef EMBEDDED + membar(); +#else + mb(); +#endif + pQueue->Head = Head; + atomic_dec(&pQueue->EntryCnt); + + return Entry; +} /* QQ_PopHead */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline PQQ_ENTRY +QQ_PopTail( +PQQ_CONTAINER pQueue) { + unsigned int Tail; + PQQ_ENTRY Entry; + + Tail = pQueue->Tail; + +#if !defined(QQ_NO_UNDERFLOW_CHECK) + if(Tail == pQueue->Head) { + return (PQQ_ENTRY) 0; + } /* if */ +#endif /* QQ_NO_UNDERFLOW_CHECK */ + + Entry = pQueue->Array[Tail]; +#ifdef EMBEDDED + membar(); +#else + mb(); +#endif + pQueue->Tail = (Tail + 1) % pQueue->Size; + atomic_dec(&pQueue->EntryCnt); + + return Entry; +} /* QQ_PopTail */ + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline PQQ_ENTRY +QQ_GetHead( + PQQ_CONTAINER pQueue, + unsigned int Idx) +{ + if(Idx >= atomic_read(&pQueue->EntryCnt)) + { + return (PQQ_ENTRY) 0; + } + + if(pQueue->Head > Idx) + { + Idx = pQueue->Head - Idx; + } + else + { + Idx = pQueue->Size - (Idx - pQueue->Head); + } + Idx--; + + return pQueue->Array[Idx]; +} + + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +extern __inline PQQ_ENTRY +QQ_GetTail( + PQQ_CONTAINER pQueue, + unsigned int Idx) +{ + if(Idx >= atomic_read(&pQueue->EntryCnt)) + { + return (PQQ_ENTRY) 0; + } + + Idx += pQueue->Tail; + if(Idx >= pQueue->Size) + { + Idx = Idx - pQueue->Size; + } + + return pQueue->Array[Idx]; +} + +#endif /* QQ_USE_MACROS */ + + +#endif /* QUEUE_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/bfin_mac.c b/roms/u-boot-sam460ex/drivers/net/bfin_mac.c new file mode 100644 index 000000000..720e12605 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bfin_mac.c @@ -0,0 +1,520 @@ +/* + * Driver for Blackfin On-Chip MAC device + * + * Copyright (c) 2005-2008 Analog Device, Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <config.h> +#include <net.h> +#include <netdev.h> +#include <command.h> +#include <malloc.h> +#include <miiphy.h> +#include <linux/mii.h> + +#include <asm/blackfin.h> +#include <asm/mach-common/bits/dma.h> +#include <asm/mach-common/bits/emac.h> +#include <asm/mach-common/bits/pll.h> + +#include "bfin_mac.h" + +#ifndef CONFIG_PHY_ADDR +# define CONFIG_PHY_ADDR 1 +#endif +#ifndef CONFIG_PHY_CLOCK_FREQ +# define CONFIG_PHY_CLOCK_FREQ 2500000 +#endif + +#ifdef CONFIG_POST +#include <post.h> +#endif + +#define RXBUF_BASE_ADDR 0xFF900000 +#define TXBUF_BASE_ADDR 0xFF800000 +#define TX_BUF_CNT 1 + +#define TOUT_LOOP 1000000 + +static ADI_ETHER_BUFFER *txbuf[TX_BUF_CNT]; +static ADI_ETHER_BUFFER *rxbuf[PKTBUFSRX]; +static u16 txIdx; /* index of the current RX buffer */ +static u16 rxIdx; /* index of the current TX buffer */ + +/* DMAx_CONFIG values at DMA Restart */ +static const union { + u16 data; + ADI_DMA_CONFIG_REG reg; +} txdmacfg = { + .reg = { + .b_DMA_EN = 1, /* enabled */ + .b_WNR = 0, /* read from memory */ + .b_WDSIZE = 2, /* wordsize is 32 bits */ + .b_DMA2D = 0, + .b_RESTART = 0, + .b_DI_SEL = 0, + .b_DI_EN = 0, /* no interrupt */ + .b_NDSIZE = 5, /* 5 half words is desc size */ + .b_FLOW = 7 /* large desc flow */ + }, +}; + +static int bfin_miiphy_wait(void) +{ + /* poll the STABUSY bit */ + while (bfin_read_EMAC_STAADD() & STABUSY) + continue; + return 0; +} + +static int bfin_miiphy_read(char *devname, uchar addr, uchar reg, ushort *val) +{ + if (bfin_miiphy_wait()) + return 1; + bfin_write_EMAC_STAADD(SET_PHYAD(addr) | SET_REGAD(reg) | STABUSY); + if (bfin_miiphy_wait()) + return 1; + *val = bfin_read_EMAC_STADAT(); + return 0; +} + +static int bfin_miiphy_write(char *devname, uchar addr, uchar reg, ushort val) +{ + if (bfin_miiphy_wait()) + return 1; + bfin_write_EMAC_STADAT(val); + bfin_write_EMAC_STAADD(SET_PHYAD(addr) | SET_REGAD(reg) | STAOP | STABUSY); + return 0; +} + +int bfin_EMAC_initialize(bd_t *bis) +{ + struct eth_device *dev; + dev = malloc(sizeof(*dev)); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + sprintf(dev->name, "Blackfin EMAC"); + + dev->iobase = 0; + dev->priv = 0; + dev->init = bfin_EMAC_init; + dev->halt = bfin_EMAC_halt; + dev->send = bfin_EMAC_send; + dev->recv = bfin_EMAC_recv; + dev->write_hwaddr = bfin_EMAC_setup_addr; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, bfin_miiphy_read, bfin_miiphy_write); +#endif + + return 0; +} + +static int bfin_EMAC_send(struct eth_device *dev, volatile void *packet, + int length) +{ + int i; + int result = 0; + unsigned int *buf; + buf = (unsigned int *)packet; + + if (length <= 0) { + printf("Ethernet: bad packet size: %d\n", length); + goto out; + } + + if ((*pDMA2_IRQ_STATUS & DMA_ERR) != 0) { + printf("Ethernet: tx DMA error\n"); + goto out; + } + + for (i = 0; (*pDMA2_IRQ_STATUS & DMA_RUN) != 0; i++) { + if (i > TOUT_LOOP) { + puts("Ethernet: tx time out\n"); + goto out; + } + } + txbuf[txIdx]->FrmData->NoBytes = length; + memcpy(txbuf[txIdx]->FrmData->Dest, (void *)packet, length); + txbuf[txIdx]->Dma[0].START_ADDR = (u32) txbuf[txIdx]->FrmData; + *pDMA2_NEXT_DESC_PTR = txbuf[txIdx]->Dma; + *pDMA2_CONFIG = txdmacfg.data; + *pEMAC_OPMODE |= TE; + + for (i = 0; (txbuf[txIdx]->StatusWord & TX_COMP) == 0; i++) { + if (i > TOUT_LOOP) { + puts("Ethernet: tx error\n"); + goto out; + } + } + result = txbuf[txIdx]->StatusWord; + txbuf[txIdx]->StatusWord = 0; + if ((txIdx + 1) >= TX_BUF_CNT) + txIdx = 0; + else + txIdx++; + out: + debug("BFIN EMAC send: length = %d\n", length); + return result; +} + +static int bfin_EMAC_recv(struct eth_device *dev) +{ + int length = 0; + + for (;;) { + if ((rxbuf[rxIdx]->StatusWord & RX_COMP) == 0) { + length = -1; + break; + } + if ((rxbuf[rxIdx]->StatusWord & RX_DMAO) != 0) { + printf("Ethernet: rx dma overrun\n"); + break; + } + if ((rxbuf[rxIdx]->StatusWord & RX_OK) == 0) { + printf("Ethernet: rx error\n"); + break; + } + length = rxbuf[rxIdx]->StatusWord & 0x000007FF; + if (length <= 4) { + printf("Ethernet: bad frame\n"); + break; + } + + debug("%s: len = %d\n", __func__, length - 4); + + NetRxPackets[rxIdx] = + (volatile uchar *)(rxbuf[rxIdx]->FrmData->Dest); + NetReceive(NetRxPackets[rxIdx], length - 4); + *pDMA1_IRQ_STATUS |= DMA_DONE | DMA_ERR; + rxbuf[rxIdx]->StatusWord = 0x00000000; + if ((rxIdx + 1) >= PKTBUFSRX) + rxIdx = 0; + else + rxIdx++; + } + + return length; +} + +/************************************************************** + * + * Ethernet Initialization Routine + * + *************************************************************/ + +/* MDC = SCLK / MDC_freq / 2 - 1 */ +#define MDC_FREQ_TO_DIV(mdc_freq) (get_sclk() / (mdc_freq) / 2 - 1) + +static int bfin_miiphy_init(struct eth_device *dev, int *opmode) +{ + u16 phydat; + size_t count; + + /* Enable PHY output */ + *pVR_CTL |= CLKBUFOE; + + /* Set all the pins to peripheral mode */ +#ifdef CONFIG_RMII + /* grab RMII pins */ +# if defined(__ADSPBF51x__) + *pPORTF_MUX = (*pPORTF_MUX & \ + ~(PORT_x_MUX_3_MASK | PORT_x_MUX_4_MASK | PORT_x_MUX_5_MASK)) | \ + PORT_x_MUX_3_FUNC_1 | PORT_x_MUX_4_FUNC_1 | PORT_x_MUX_5_FUNC_1; + *pPORTF_FER |= PF8 | PF9 | PF10 | PF11 | PF12 | PF13 | PF14 | PF15; + *pPORTG_MUX = (*pPORTG_MUX & ~PORT_x_MUX_0_MASK) | PORT_x_MUX_0_FUNC_1; + *pPORTG_FER |= PG0 | PG1 | PG2; +# elif defined(__ADSPBF52x__) + *pPORTG_MUX = (*pPORTG_MUX & ~PORT_x_MUX_6_MASK) | PORT_x_MUX_6_FUNC_2; + *pPORTG_FER |= PG14 | PG15; + *pPORTH_MUX = (*pPORTH_MUX & ~(PORT_x_MUX_0_MASK | PORT_x_MUX_1_MASK)) | \ + PORT_x_MUX_0_FUNC_2 | PORT_x_MUX_1_FUNC_2; + *pPORTH_FER |= PH0 | PH1 | PH2 | PH3 | PH4 | PH5 | PH6 | PH7 | PH8; +# else + *pPORTH_FER |= PH0 | PH1 | PH4 | PH5 | PH6 | PH8 | PH9 | PH14 | PH15; +# endif +#else + /* grab MII & RMII pins */ +# if defined(__ADSPBF51x__) + *pPORTF_MUX = (*pPORTF_MUX & \ + ~(PORT_x_MUX_0_MASK | PORT_x_MUX_1_MASK | PORT_x_MUX_3_MASK | PORT_x_MUX_4_MASK | PORT_x_MUX_5_MASK)) | \ + PORT_x_MUX_0_FUNC_1 | PORT_x_MUX_1_FUNC_1 | PORT_x_MUX_3_FUNC_1 | PORT_x_MUX_4_FUNC_1 | PORT_x_MUX_5_FUNC_1; + *pPORTF_FER |= PF0 | PF1 | PF2 | PF3 | PF4 | PF5 | PF6 | PF8 | PF9 | PF10 | PF11 | PF12 | PF13 | PF14 | PF15; + *pPORTG_MUX = (*pPORTG_MUX & ~PORT_x_MUX_0_MASK) | PORT_x_MUX_0_FUNC_1; + *pPORTG_FER |= PG0 | PG1 | PG2; +# elif defined(__ADSPBF52x__) + *pPORTG_MUX = (*pPORTG_MUX & ~PORT_x_MUX_6_MASK) | PORT_x_MUX_6_FUNC_2; + *pPORTG_FER |= PG14 | PG15; + *pPORTH_MUX = PORT_x_MUX_0_FUNC_2 | PORT_x_MUX_1_FUNC_2 | PORT_x_MUX_2_FUNC_2; + *pPORTH_FER = -1; /* all pins */ +# else + *pPORTH_FER = -1; /* all pins */ +# endif +#endif + + /* Odd word alignment for Receive Frame DMA word */ + /* Configure checksum support and rcve frame word alignment */ + bfin_write_EMAC_SYSCTL(RXDWA | RXCKS | SET_MDCDIV(MDC_FREQ_TO_DIV(CONFIG_PHY_CLOCK_FREQ))); + + /* turn on auto-negotiation and wait for link to come up */ + bfin_miiphy_write(dev->name, CONFIG_PHY_ADDR, MII_BMCR, BMCR_ANENABLE); + count = 0; + while (1) { + ++count; + if (bfin_miiphy_read(dev->name, CONFIG_PHY_ADDR, MII_BMSR, &phydat)) + return -1; + if (phydat & BMSR_LSTATUS) + break; + if (count > 30000) { + printf("%s: link down, check cable\n", dev->name); + return -1; + } + udelay(100); + } + + /* see what kind of link we have */ + if (bfin_miiphy_read(dev->name, CONFIG_PHY_ADDR, MII_LPA, &phydat)) + return -1; + if (phydat & LPA_DUPLEX) + *opmode = FDMODE; + else + *opmode = 0; + + bfin_write_EMAC_MMC_CTL(RSTC | CROLL); + + /* Initialize the TX DMA channel registers */ + *pDMA2_X_COUNT = 0; + *pDMA2_X_MODIFY = 4; + *pDMA2_Y_COUNT = 0; + *pDMA2_Y_MODIFY = 0; + + /* Initialize the RX DMA channel registers */ + *pDMA1_X_COUNT = 0; + *pDMA1_X_MODIFY = 4; + *pDMA1_Y_COUNT = 0; + *pDMA1_Y_MODIFY = 0; + + return 0; +} + +static int bfin_EMAC_setup_addr(struct eth_device *dev) +{ + *pEMAC_ADDRLO = + dev->enetaddr[0] | + dev->enetaddr[1] << 8 | + dev->enetaddr[2] << 16 | + dev->enetaddr[3] << 24; + *pEMAC_ADDRHI = + dev->enetaddr[4] | + dev->enetaddr[5] << 8; + return 0; +} + +static int bfin_EMAC_init(struct eth_device *dev, bd_t *bd) +{ + u32 opmode; + int dat; + int i; + debug("Eth_init: ......\n"); + + txIdx = 0; + rxIdx = 0; + + /* Initialize System Register */ + if (bfin_miiphy_init(dev, &dat) < 0) + return -1; + + /* Initialize EMAC address */ + bfin_EMAC_setup_addr(dev); + + /* Initialize TX and RX buffer */ + for (i = 0; i < PKTBUFSRX; i++) { + rxbuf[i] = SetupRxBuffer(i); + if (i > 0) { + rxbuf[i - 1]->Dma[1].NEXT_DESC_PTR = rxbuf[i]->Dma; + if (i == (PKTBUFSRX - 1)) + rxbuf[i]->Dma[1].NEXT_DESC_PTR = rxbuf[0]->Dma; + } + } + for (i = 0; i < TX_BUF_CNT; i++) { + txbuf[i] = SetupTxBuffer(i); + if (i > 0) { + txbuf[i - 1]->Dma[1].NEXT_DESC_PTR = txbuf[i]->Dma; + if (i == (TX_BUF_CNT - 1)) + txbuf[i]->Dma[1].NEXT_DESC_PTR = txbuf[0]->Dma; + } + } + + /* Set RX DMA */ + *pDMA1_NEXT_DESC_PTR = rxbuf[0]->Dma; + *pDMA1_CONFIG = rxbuf[0]->Dma[0].CONFIG_DATA; + + /* Wait MII done */ + bfin_miiphy_wait(); + + /* We enable only RX here */ + /* ASTP : Enable Automatic Pad Stripping + PR : Promiscuous Mode for test + PSF : Receive frames with total length less than 64 bytes. + FDMODE : Full Duplex Mode + LB : Internal Loopback for test + RE : Receiver Enable */ + if (dat == FDMODE) + opmode = ASTP | FDMODE | PSF; + else + opmode = ASTP | PSF; + opmode |= RE; +#ifdef CONFIG_RMII + opmode |= TE | RMII; +#endif + /* Turn on the EMAC */ + *pEMAC_OPMODE = opmode; + return 0; +} + +static void bfin_EMAC_halt(struct eth_device *dev) +{ + debug("Eth_halt: ......\n"); + /* Turn off the EMAC */ + *pEMAC_OPMODE = 0x00000000; + /* Turn off the EMAC RX DMA */ + *pDMA1_CONFIG = 0x0000; + *pDMA2_CONFIG = 0x0000; + +} + +ADI_ETHER_BUFFER *SetupRxBuffer(int no) +{ + ADI_ETHER_FRAME_BUFFER *frmbuf; + ADI_ETHER_BUFFER *buf; + int nobytes_buffer = sizeof(ADI_ETHER_BUFFER[2]) / 2; /* ensure a multi. of 4 */ + int total_size = nobytes_buffer + RECV_BUFSIZE; + + buf = (void *) (RXBUF_BASE_ADDR + no * total_size); + frmbuf = (void *) (RXBUF_BASE_ADDR + no * total_size + nobytes_buffer); + + memset(buf, 0x00, nobytes_buffer); + buf->FrmData = frmbuf; + memset(frmbuf, 0xfe, RECV_BUFSIZE); + + /* set up first desc to point to receive frame buffer */ + buf->Dma[0].NEXT_DESC_PTR = &(buf->Dma[1]); + buf->Dma[0].START_ADDR = (u32) buf->FrmData; + buf->Dma[0].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[0].CONFIG.b_WNR = 1; /* Write to memory */ + buf->Dma[0].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[0].CONFIG.b_NDSIZE = 5; /* 5 half words is desc size. */ + buf->Dma[0].CONFIG.b_FLOW = 7; /* large desc flow */ + + /* set up second desc to point to status word */ + buf->Dma[1].NEXT_DESC_PTR = buf->Dma; + buf->Dma[1].START_ADDR = (u32) & buf->IPHdrChksum; + buf->Dma[1].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[1].CONFIG.b_WNR = 1; /* Write to memory */ + buf->Dma[1].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[1].CONFIG.b_DI_EN = 1; /* enable interrupt */ + buf->Dma[1].CONFIG.b_NDSIZE = 5; /* must be 0 when FLOW is 0 */ + buf->Dma[1].CONFIG.b_FLOW = 7; /* stop */ + + return buf; +} + +ADI_ETHER_BUFFER *SetupTxBuffer(int no) +{ + ADI_ETHER_FRAME_BUFFER *frmbuf; + ADI_ETHER_BUFFER *buf; + int nobytes_buffer = sizeof(ADI_ETHER_BUFFER[2]) / 2; /* ensure a multi. of 4 */ + int total_size = nobytes_buffer + RECV_BUFSIZE; + + buf = (void *) (TXBUF_BASE_ADDR + no * total_size); + frmbuf = (void *) (TXBUF_BASE_ADDR + no * total_size + nobytes_buffer); + + memset(buf, 0x00, nobytes_buffer); + buf->FrmData = frmbuf; + memset(frmbuf, 0x00, RECV_BUFSIZE); + + /* set up first desc to point to receive frame buffer */ + buf->Dma[0].NEXT_DESC_PTR = &(buf->Dma[1]); + buf->Dma[0].START_ADDR = (u32) buf->FrmData; + buf->Dma[0].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[0].CONFIG.b_WNR = 0; /* Read to memory */ + buf->Dma[0].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[0].CONFIG.b_NDSIZE = 5; /* 5 half words is desc size. */ + buf->Dma[0].CONFIG.b_FLOW = 7; /* large desc flow */ + + /* set up second desc to point to status word */ + buf->Dma[1].NEXT_DESC_PTR = &(buf->Dma[0]); + buf->Dma[1].START_ADDR = (u32) & buf->StatusWord; + buf->Dma[1].CONFIG.b_DMA_EN = 1; /* enabled */ + buf->Dma[1].CONFIG.b_WNR = 1; /* Write to memory */ + buf->Dma[1].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */ + buf->Dma[1].CONFIG.b_DI_EN = 1; /* enable interrupt */ + buf->Dma[1].CONFIG.b_NDSIZE = 0; /* must be 0 when FLOW is 0 */ + buf->Dma[1].CONFIG.b_FLOW = 0; /* stop */ + + return buf; +} + +#if defined(CONFIG_POST) && defined(CONFIG_SYS_POST_ETHER) +int ether_post_test(int flags) +{ + uchar buf[64]; + int i, value = 0; + int length; + + printf("\n--------"); + bfin_EMAC_init(NULL, NULL); + /* construct the package */ + buf[0] = buf[6] = (unsigned char)(*pEMAC_ADDRLO & 0xFF); + buf[1] = buf[7] = (unsigned char)((*pEMAC_ADDRLO & 0xFF00) >> 8); + buf[2] = buf[8] = (unsigned char)((*pEMAC_ADDRLO & 0xFF0000) >> 16); + buf[3] = buf[9] = (unsigned char)((*pEMAC_ADDRLO & 0xFF000000) >> 24); + buf[4] = buf[10] = (unsigned char)(*pEMAC_ADDRHI & 0xFF); + buf[5] = buf[11] = (unsigned char)((*pEMAC_ADDRHI & 0xFF00) >> 8); + buf[12] = 0x08; /* Type: ARP */ + buf[13] = 0x06; + buf[14] = 0x00; /* Hardware type: Ethernet */ + buf[15] = 0x01; + buf[16] = 0x08; /* Protocal type: IP */ + buf[17] = 0x00; + buf[18] = 0x06; /* Hardware size */ + buf[19] = 0x04; /* Protocol size */ + buf[20] = 0x00; /* Opcode: request */ + buf[21] = 0x01; + + for (i = 0; i < 42; i++) + buf[i + 22] = i; + printf("--------Send 64 bytes......\n"); + bfin_EMAC_send(NULL, (volatile void *)buf, 64); + for (i = 0; i < 100; i++) { + udelay(10000); + if ((rxbuf[rxIdx]->StatusWord & RX_COMP) != 0) { + value = 1; + break; + } + } + if (value == 0) { + printf("--------EMAC can't receive any data\n"); + eth_halt(); + return -1; + } + length = rxbuf[rxIdx]->StatusWord & 0x000007FF - 4; + for (i = 0; i < length; i++) { + if (rxbuf[rxIdx]->FrmData->Dest[i] != buf[i]) { + printf("--------EMAC receive error data!\n"); + eth_halt(); + return -1; + } + } + printf("--------receive %d bytes, matched\n", length); + bfin_EMAC_halt(NULL); + return 0; +} +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/bfin_mac.h b/roms/u-boot-sam460ex/drivers/net/bfin_mac.h new file mode 100644 index 000000000..c731c179b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/bfin_mac.h @@ -0,0 +1,65 @@ +/* + * bfin_mac.h - some defines/structures for the Blackfin on-chip MAC. + * + * Copyright (c) 2005-2008 Analog Device, Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __BFIN_MAC_H__ +#define __BFIN_MAC_H__ + +#define RECV_BUFSIZE (0x614) + +typedef struct ADI_DMA_CONFIG_REG { + u16 b_DMA_EN:1; /* 0 Enabled */ + u16 b_WNR:1; /* 1 Direction */ + u16 b_WDSIZE:2; /* 2:3 Transfer word size */ + u16 b_DMA2D:1; /* 4 DMA mode */ + u16 b_RESTART:1; /* 5 Retain FIFO */ + u16 b_DI_SEL:1; /* 6 Data interrupt timing select */ + u16 b_DI_EN:1; /* 7 Data interrupt enabled */ + u16 b_NDSIZE:4; /* 8:11 Flex descriptor size */ + u16 b_FLOW:3; /* 12:14Flow */ +} ADI_DMA_CONFIG_REG; + +typedef struct adi_ether_frame_buffer { + u16 NoBytes; /* the no. of following bytes */ + u8 Dest[6]; /* destination MAC address */ + u8 Srce[6]; /* source MAC address */ + u16 LTfield; /* length/type field */ + u8 Data[0]; /* payload bytes */ +} ADI_ETHER_FRAME_BUFFER; +/* 16 bytes/struct */ + +typedef struct dma_descriptor { + struct dma_descriptor *NEXT_DESC_PTR; + u32 START_ADDR; + union { + u16 CONFIG_DATA; + ADI_DMA_CONFIG_REG CONFIG; + }; +} DMA_DESCRIPTOR; +/* 10 bytes/struct in 12 bytes */ + +typedef struct adi_ether_buffer { + DMA_DESCRIPTOR Dma[2]; /* first for the frame, second for the status */ + ADI_ETHER_FRAME_BUFFER *FrmData;/* pointer to data */ + struct adi_ether_buffer *pNext; /* next buffer */ + struct adi_ether_buffer *pPrev; /* prev buffer */ + u16 IPHdrChksum; /* the IP header checksum */ + u16 IPPayloadChksum; /* the IP header and payload checksum */ + volatile u32 StatusWord; /* the frame status word */ +} ADI_ETHER_BUFFER; +/* 40 bytes/struct in 44 bytes */ + +static ADI_ETHER_BUFFER *SetupRxBuffer(int no); +static ADI_ETHER_BUFFER *SetupTxBuffer(int no); + +static int bfin_EMAC_init(struct eth_device *dev, bd_t *bd); +static void bfin_EMAC_halt(struct eth_device *dev); +static int bfin_EMAC_send(struct eth_device *dev, volatile void *packet, int length); +static int bfin_EMAC_recv(struct eth_device *dev); +static int bfin_EMAC_setup_addr(struct eth_device *dev); + +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/cs8900.c b/roms/u-boot-sam460ex/drivers/net/cs8900.c new file mode 100644 index 000000000..9424fb2bb --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/cs8900.c @@ -0,0 +1,337 @@ +/* + * Cirrus Logic CS8900A Ethernet + * + * (C) 2009 Ben Warren , biggerbadderben@gmail.com + * Converted to use CONFIG_NET_MULTI API + * + * (C) 2003 Wolfgang Denk, wd@denx.de + * Extension to synchronize ethaddr environment variable + * against value in EEPROM + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Copyright (C) 1999 Ben Williamson <benw@pobox.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is loaded into SRAM in bootstrap mode, where it waits + * for commands on UART1 to read and write memory, jump to code etc. + * A design goal for this program is to be entirely independent of the + * target board. Anything with a CL-PS7111 or EP7211 should be able to run + * this code in bootstrap mode. All the board specifics can be handled on + * the host. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <common.h> +#include <command.h> +#include <asm/io.h> +#include <net.h> +#include <malloc.h> +#include "cs8900.h" + +#undef DEBUG + +/* packet page register access functions */ + +#ifdef CONFIG_CS8900_BUS32 + +#define REG_WRITE(v, a) writel((v),(a)) +#define REG_READ(a) readl((a)) + +/* we don't need 16 bit initialisation on 32 bit bus */ +#define get_reg_init_bus(r,d) get_reg((r),(d)) + +#else + +#define REG_WRITE(v, a) writew((v),(a)) +#define REG_READ(a) readw((a)) + +static u16 get_reg_init_bus(struct eth_device *dev, int regno) +{ + /* force 16 bit busmode */ + volatile u8 c; + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + uint8_t volatile * const iob = (uint8_t volatile * const)dev->iobase; + + c = readb(iob); + c = readb(iob + 1); + c = readb(iob); + c = readb(iob + 1); + c = readb(iob); + + REG_WRITE(regno, &priv->regs->pptr); + return REG_READ(&priv->regs->pdata); +} +#endif + +static u16 get_reg(struct eth_device *dev, int regno) +{ + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + REG_WRITE(regno, &priv->regs->pptr); + return REG_READ(&priv->regs->pdata); +} + + +static void put_reg(struct eth_device *dev, int regno, u16 val) +{ + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + REG_WRITE(regno, &priv->regs->pptr); + REG_WRITE(val, &priv->regs->pdata); +} + +static void cs8900_reset(struct eth_device *dev) +{ + int tmo; + u16 us; + + /* reset NIC */ + put_reg(dev, PP_SelfCTL, get_reg(dev, PP_SelfCTL) | PP_SelfCTL_Reset); + + /* wait for 200ms */ + udelay(200000); + /* Wait until the chip is reset */ + + tmo = get_timer(0) + 1 * CONFIG_SYS_HZ; + while ((((us = get_reg_init_bus(dev, PP_SelfSTAT)) & + PP_SelfSTAT_InitD) == 0) && tmo < get_timer(0)) + /*NOP*/; +} + +static void cs8900_reginit(struct eth_device *dev) +{ + /* receive only error free packets addressed to this card */ + put_reg(dev, PP_RxCTL, + PP_RxCTL_IA | PP_RxCTL_Broadcast | PP_RxCTL_RxOK); + /* do not generate any interrupts on receive operations */ + put_reg(dev, PP_RxCFG, 0); + /* do not generate any interrupts on transmit operations */ + put_reg(dev, PP_TxCFG, 0); + /* do not generate any interrupts on buffer operations */ + put_reg(dev, PP_BufCFG, 0); + /* enable transmitter/receiver mode */ + put_reg(dev, PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx); +} + +void cs8900_get_enetaddr(struct eth_device *dev) +{ + int i; + + /* verify chip id */ + if (get_reg_init_bus(dev, PP_ChipID) != 0x630e) + return; + cs8900_reset(dev); + if ((get_reg(dev, PP_SelfSTAT) & + (PP_SelfSTAT_EEPROM | PP_SelfSTAT_EEPROM_OK)) == + (PP_SelfSTAT_EEPROM | PP_SelfSTAT_EEPROM_OK)) { + + /* Load the MAC from EEPROM */ + for (i = 0; i < 3; i++) { + u32 Addr; + + Addr = get_reg(dev, PP_IA + i * 2); + dev->enetaddr[i * 2] = Addr & 0xFF; + dev->enetaddr[i * 2 + 1] = Addr >> 8; + } + } +} + +void cs8900_halt(struct eth_device *dev) +{ + /* disable transmitter/receiver mode */ + put_reg(dev, PP_LineCTL, 0); + + /* "shutdown" to show ChipID or kernel wouldn't find he cs8900 ... */ + get_reg_init_bus(dev, PP_ChipID); +} + +static int cs8900_init(struct eth_device *dev, bd_t * bd) +{ + uchar *enetaddr = dev->enetaddr; + u16 id; + + /* verify chip id */ + id = get_reg_init_bus(dev, PP_ChipID); + if (id != 0x630e) { + printf ("CS8900 Ethernet chip not found: " + "ID=0x%04x instead 0x%04x\n", id, 0x630e); + return 1; + } + + cs8900_reset (dev); + /* set the ethernet address */ + put_reg(dev, PP_IA + 0, enetaddr[0] | (enetaddr[1] << 8)); + put_reg(dev, PP_IA + 2, enetaddr[2] | (enetaddr[3] << 8)); + put_reg(dev, PP_IA + 4, enetaddr[4] | (enetaddr[5] << 8)); + + cs8900_reginit(dev); + return 0; +} + +/* Get a data block via Ethernet */ +static int cs8900_recv(struct eth_device *dev) +{ + int i; + u16 rxlen; + u16 *addr; + u16 status; + + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + + status = get_reg(dev, PP_RER); + + if ((status & PP_RER_RxOK) == 0) + return 0; + + status = REG_READ(&priv->regs->rtdata); + rxlen = REG_READ(&priv->regs->rtdata); + + if (rxlen > PKTSIZE_ALIGN + PKTALIGN) + debug("packet too big!\n"); + for (addr = (u16 *) NetRxPackets[0], i = rxlen >> 1; i > 0; + i--) + *addr++ = REG_READ(&priv->regs->rtdata); + if (rxlen & 1) + *addr++ = REG_READ(&priv->regs->rtdata); + + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], rxlen); + return rxlen; +} + +/* Send a data block via Ethernet. */ +static int cs8900_send(struct eth_device *dev, + volatile void *packet, int length) +{ + volatile u16 *addr; + int tmo; + u16 s; + struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv); + +retry: + /* initiate a transmit sequence */ + REG_WRITE(PP_TxCmd_TxStart_Full, &priv->regs->txcmd); + REG_WRITE(length, &priv->regs->txlen); + + /* Test to see if the chip has allocated memory for the packet */ + if ((get_reg(dev, PP_BusSTAT) & PP_BusSTAT_TxRDY) == 0) { + /* Oops... this should not happen! */ + debug("cs: unable to send packet; retrying...\n"); + for (tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + get_timer(0) < tmo;) + /*NOP*/; + cs8900_reset(dev); + cs8900_reginit(dev); + goto retry; + } + + /* Write the contents of the packet */ + /* assume even number of bytes */ + for (addr = packet; length > 0; length -= 2) + REG_WRITE(*addr++, &priv->regs->rtdata); + + /* wait for transfer to succeed */ + tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + while ((s = get_reg(dev, PP_TER) & ~0x1F) == 0) { + if (get_timer(0) >= tmo) + break; + } + + /* nothing */ ; + if((s & (PP_TER_CRS | PP_TER_TxOK)) != PP_TER_TxOK) { + debug("\ntransmission error %#x\n", s); + } + + return 0; +} + +static void cs8900_e2prom_ready(struct eth_device *dev) +{ + while (get_reg(dev, PP_SelfSTAT) & SI_BUSY) + ; +} + +/***********************************************************/ +/* read a 16-bit word out of the EEPROM */ +/***********************************************************/ + +int cs8900_e2prom_read(struct eth_device *dev, + u8 addr, u16 *value) +{ + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EECMD, EEPROM_READ_CMD | addr); + cs8900_e2prom_ready(dev); + *value = get_reg(dev, PP_EEData); + + return 0; +} + + +/***********************************************************/ +/* write a 16-bit word into the EEPROM */ +/***********************************************************/ + +int cs8900_e2prom_write(struct eth_device *dev, u8 addr, u16 value) +{ + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EECMD, EEPROM_WRITE_EN); + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EEData, value); + put_reg(dev, PP_EECMD, EEPROM_WRITE_CMD | addr); + cs8900_e2prom_ready(dev); + put_reg(dev, PP_EECMD, EEPROM_WRITE_DIS); + cs8900_e2prom_ready(dev); + + return 0; +} + +int cs8900_initialize(u8 dev_num, int base_addr) +{ + struct eth_device *dev; + struct cs8900_priv *priv; + + dev = malloc(sizeof(*dev)); + if (!dev) { + return 0; + } + memset(dev, 0, sizeof(*dev)); + + priv = malloc(sizeof(*priv)); + if (!priv) { + free(dev); + return 0; + } + memset(priv, 0, sizeof(*priv)); + priv->regs = (struct cs8900_regs *)base_addr; + + dev->iobase = base_addr; + dev->priv = priv; + dev->init = cs8900_init; + dev->halt = cs8900_halt; + dev->send = cs8900_send; + dev->recv = cs8900_recv; + + /* Load MAC address from EEPROM */ + cs8900_get_enetaddr(dev); + + sprintf(dev->name, "%s-%hu", CS8900_DRIVERNAME, dev_num); + + eth_register(dev); + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/cs8900.h b/roms/u-boot-sam460ex/drivers/net/cs8900.h new file mode 100644 index 000000000..23c5cb07b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/cs8900.h @@ -0,0 +1,264 @@ +#ifndef CS8900_H +#define CS8900_H +/* + * Cirrus Logic CS8900A Ethernet + * + * (C) 2009 Ben Warren , biggerbadderben@gmail.com + * Converted to use CONFIG_NET_MULTI API + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Copyright (C) 1999 Ben Williamson <benw@pobox.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is loaded into SRAM in bootstrap mode, where it waits + * for commands on UART1 to read and write memory, jump to code etc. + * A design goal for this program is to be entirely independent of the + * target board. Anything with a CL-PS7111 or EP7211 should be able to run + * this code in bootstrap mode. All the board specifics can be handled on + * the host. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <asm/types.h> +#include <config.h> + +#define CS8900_DRIVERNAME "CS8900" +/* although the registers are 16 bit, they are 32-bit aligned on the + EDB7111. so we have to read them as 32-bit registers and ignore the + upper 16-bits. i'm not sure if this holds for the EDB7211. */ + +#ifdef CONFIG_CS8900_BUS16 + /* 16 bit aligned registers, 16 bit wide */ + #define CS8900_REG u16 +#elif defined(CONFIG_CS8900_BUS32) + /* 32 bit aligned registers, 16 bit wide (we ignore upper 16 bits) */ + #define CS8900_REG u32 +#else + #error unknown bussize ... +#endif + +struct cs8900_regs { + CS8900_REG rtdata; + CS8900_REG pad0; + CS8900_REG txcmd; + CS8900_REG txlen; + CS8900_REG isq; + CS8900_REG pptr; + CS8900_REG pdata; +}; + +struct cs8900_priv { + struct cs8900_regs *regs; +}; + +#define ISQ_RxEvent 0x04 +#define ISQ_TxEvent 0x08 +#define ISQ_BufEvent 0x0C +#define ISQ_RxMissEvent 0x10 +#define ISQ_TxColEvent 0x12 +#define ISQ_EventMask 0x3F + +/* packet page register offsets */ + +/* bus interface registers */ +#define PP_ChipID 0x0000 /* Chip identifier - must be 0x630E */ +#define PP_ChipRev 0x0002 /* Chip revision, model codes */ + +#define PP_IntReg 0x0022 /* Interrupt configuration */ +#define PP_IntReg_IRQ0 0x0000 /* Use INTR0 pin */ +#define PP_IntReg_IRQ1 0x0001 /* Use INTR1 pin */ +#define PP_IntReg_IRQ2 0x0002 /* Use INTR2 pin */ +#define PP_IntReg_IRQ3 0x0003 /* Use INTR3 pin */ + +/* status and control registers */ + +#define PP_RxCFG 0x0102 /* Receiver configuration */ +#define PP_RxCFG_Skip1 0x0040 /* Skip (i.e. discard) current frame */ +#define PP_RxCFG_Stream 0x0080 /* Enable streaming mode */ +#define PP_RxCFG_RxOK 0x0100 /* RxOK interrupt enable */ +#define PP_RxCFG_RxDMAonly 0x0200 /* Use RxDMA for all frames */ +#define PP_RxCFG_AutoRxDMA 0x0400 /* Select RxDMA automatically */ +#define PP_RxCFG_BufferCRC 0x0800 /* Include CRC characters in frame */ +#define PP_RxCFG_CRC 0x1000 /* Enable interrupt on CRC error */ +#define PP_RxCFG_RUNT 0x2000 /* Enable interrupt on RUNT frames */ +#define PP_RxCFG_EXTRA 0x4000 /* Enable interrupt on frames with extra data */ + +#define PP_RxCTL 0x0104 /* Receiver control */ +#define PP_RxCTL_IAHash 0x0040 /* Accept frames that match hash */ +#define PP_RxCTL_Promiscuous 0x0080 /* Accept any frame */ +#define PP_RxCTL_RxOK 0x0100 /* Accept well formed frames */ +#define PP_RxCTL_Multicast 0x0200 /* Accept multicast frames */ +#define PP_RxCTL_IA 0x0400 /* Accept frame that matches IA */ +#define PP_RxCTL_Broadcast 0x0800 /* Accept broadcast frames */ +#define PP_RxCTL_CRC 0x1000 /* Accept frames with bad CRC */ +#define PP_RxCTL_RUNT 0x2000 /* Accept runt frames */ +#define PP_RxCTL_EXTRA 0x4000 /* Accept frames that are too long */ + +#define PP_TxCFG 0x0106 /* Transmit configuration */ +#define PP_TxCFG_CRS 0x0040 /* Enable interrupt on loss of carrier */ +#define PP_TxCFG_SQE 0x0080 /* Enable interrupt on Signal Quality Error */ +#define PP_TxCFG_TxOK 0x0100 /* Enable interrupt on successful xmits */ +#define PP_TxCFG_Late 0x0200 /* Enable interrupt on "out of window" */ +#define PP_TxCFG_Jabber 0x0400 /* Enable interrupt on jabber detect */ +#define PP_TxCFG_Collision 0x0800 /* Enable interrupt if collision */ +#define PP_TxCFG_16Collisions 0x8000 /* Enable interrupt if > 16 collisions */ + +#define PP_TxCmd 0x0108 /* Transmit command status */ +#define PP_TxCmd_TxStart_5 0x0000 /* Start after 5 bytes in buffer */ +#define PP_TxCmd_TxStart_381 0x0040 /* Start after 381 bytes in buffer */ +#define PP_TxCmd_TxStart_1021 0x0080 /* Start after 1021 bytes in buffer */ +#define PP_TxCmd_TxStart_Full 0x00C0 /* Start after all bytes loaded */ +#define PP_TxCmd_Force 0x0100 /* Discard any pending packets */ +#define PP_TxCmd_OneCollision 0x0200 /* Abort after a single collision */ +#define PP_TxCmd_NoCRC 0x1000 /* Do not add CRC */ +#define PP_TxCmd_NoPad 0x2000 /* Do not pad short packets */ + +#define PP_BufCFG 0x010A /* Buffer configuration */ +#define PP_BufCFG_SWI 0x0040 /* Force interrupt via software */ +#define PP_BufCFG_RxDMA 0x0080 /* Enable interrupt on Rx DMA */ +#define PP_BufCFG_TxRDY 0x0100 /* Enable interrupt when ready for Tx */ +#define PP_BufCFG_TxUE 0x0200 /* Enable interrupt in Tx underrun */ +#define PP_BufCFG_RxMiss 0x0400 /* Enable interrupt on missed Rx packets */ +#define PP_BufCFG_Rx128 0x0800 /* Enable Rx interrupt after 128 bytes */ +#define PP_BufCFG_TxCol 0x1000 /* Enable int on Tx collision ctr overflow */ +#define PP_BufCFG_Miss 0x2000 /* Enable int on Rx miss ctr overflow */ +#define PP_BufCFG_RxDest 0x8000 /* Enable int on Rx dest addr match */ + +#define PP_LineCTL 0x0112 /* Line control */ +#define PP_LineCTL_Rx 0x0040 /* Enable receiver */ +#define PP_LineCTL_Tx 0x0080 /* Enable transmitter */ +#define PP_LineCTL_AUIonly 0x0100 /* AUI interface only */ +#define PP_LineCTL_AutoAUI10BT 0x0200 /* Autodetect AUI or 10BaseT interface */ +#define PP_LineCTL_ModBackoffE 0x0800 /* Enable modified backoff algorithm */ +#define PP_LineCTL_PolarityDis 0x1000 /* Disable Rx polarity autodetect */ +#define PP_LineCTL_2partDefDis 0x2000 /* Disable two-part defferal */ +#define PP_LineCTL_LoRxSquelch 0x4000 /* Reduce receiver squelch threshold */ + +#define PP_SelfCTL 0x0114 /* Chip self control */ +#define PP_SelfCTL_Reset 0x0040 /* Self-clearing reset */ +#define PP_SelfCTL_SWSuspend 0x0100 /* Initiate suspend mode */ +#define PP_SelfCTL_HWSleepE 0x0200 /* Enable SLEEP input */ +#define PP_SelfCTL_HWStandbyE 0x0400 /* Enable standby mode */ +#define PP_SelfCTL_HC0E 0x1000 /* use HCB0 for LINK LED */ +#define PP_SelfCTL_HC1E 0x2000 /* use HCB1 for BSTATUS LED */ +#define PP_SelfCTL_HCB0 0x4000 /* control LINK LED if HC0E set */ +#define PP_SelfCTL_HCB1 0x8000 /* control BSTATUS LED if HC1E set */ + +#define PP_BusCTL 0x0116 /* Bus control */ +#define PP_BusCTL_ResetRxDMA 0x0040 /* Reset RxDMA pointer */ +#define PP_BusCTL_DMAextend 0x0100 /* Extend DMA cycle */ +#define PP_BusCTL_UseSA 0x0200 /* Assert MEMCS16 on address decode */ +#define PP_BusCTL_MemoryE 0x0400 /* Enable memory mode */ +#define PP_BusCTL_DMAburst 0x0800 /* Limit DMA access burst */ +#define PP_BusCTL_IOCHRDYE 0x1000 /* Set IOCHRDY high impedence */ +#define PP_BusCTL_RxDMAsize 0x2000 /* Set DMA buffer size 64KB */ +#define PP_BusCTL_EnableIRQ 0x8000 /* Generate interrupt on interrupt event */ + +#define PP_TestCTL 0x0118 /* Test control */ +#define PP_TestCTL_DisableLT 0x0080 /* Disable link status */ +#define PP_TestCTL_ENDECloop 0x0200 /* Internal loopback */ +#define PP_TestCTL_AUIloop 0x0400 /* AUI loopback */ +#define PP_TestCTL_DisBackoff 0x0800 /* Disable backoff algorithm */ +#define PP_TestCTL_FDX 0x4000 /* Enable full duplex mode */ + +#define PP_ISQ 0x0120 /* Interrupt Status Queue */ + +#define PP_RER 0x0124 /* Receive event */ +#define PP_RER_IAHash 0x0040 /* Frame hash match */ +#define PP_RER_Dribble 0x0080 /* Frame had 1-7 extra bits after last byte */ +#define PP_RER_RxOK 0x0100 /* Frame received with no errors */ +#define PP_RER_Hashed 0x0200 /* Frame address hashed OK */ +#define PP_RER_IA 0x0400 /* Frame address matched IA */ +#define PP_RER_Broadcast 0x0800 /* Broadcast frame */ +#define PP_RER_CRC 0x1000 /* Frame had CRC error */ +#define PP_RER_RUNT 0x2000 /* Runt frame */ +#define PP_RER_EXTRA 0x4000 /* Frame was too long */ + +#define PP_TER 0x0128 /* Transmit event */ +#define PP_TER_CRS 0x0040 /* Carrier lost */ +#define PP_TER_SQE 0x0080 /* Signal Quality Error */ +#define PP_TER_TxOK 0x0100 /* Packet sent without error */ +#define PP_TER_Late 0x0200 /* Out of window */ +#define PP_TER_Jabber 0x0400 /* Stuck transmit? */ +#define PP_TER_NumCollisions 0x7800 /* Number of collisions */ +#define PP_TER_16Collisions 0x8000 /* > 16 collisions */ + +#define PP_BER 0x012C /* Buffer event */ +#define PP_BER_SWint 0x0040 /* Software interrupt */ +#define PP_BER_RxDMAFrame 0x0080 /* Received framed DMAed */ +#define PP_BER_Rdy4Tx 0x0100 /* Ready for transmission */ +#define PP_BER_TxUnderrun 0x0200 /* Transmit underrun */ +#define PP_BER_RxMiss 0x0400 /* Received frame missed */ +#define PP_BER_Rx128 0x0800 /* 128 bytes received */ +#define PP_BER_RxDest 0x8000 /* Received framed passed address filter */ + +#define PP_RxMiss 0x0130 /* Receiver miss counter */ + +#define PP_TxCol 0x0132 /* Transmit collision counter */ + +#define PP_LineSTAT 0x0134 /* Line status */ +#define PP_LineSTAT_LinkOK 0x0080 /* Line is connected and working */ +#define PP_LineSTAT_AUI 0x0100 /* Connected via AUI */ +#define PP_LineSTAT_10BT 0x0200 /* Connected via twisted pair */ +#define PP_LineSTAT_Polarity 0x1000 /* Line polarity OK (10BT only) */ +#define PP_LineSTAT_CRS 0x4000 /* Frame being received */ + +#define PP_SelfSTAT 0x0136 /* Chip self status */ +#define PP_SelfSTAT_33VActive 0x0040 /* supply voltage is 3.3V */ +#define PP_SelfSTAT_InitD 0x0080 /* Chip initialization complete */ +#define PP_SelfSTAT_SIBSY 0x0100 /* EEPROM is busy */ +#define PP_SelfSTAT_EEPROM 0x0200 /* EEPROM present */ +#define PP_SelfSTAT_EEPROM_OK 0x0400 /* EEPROM checks out */ +#define PP_SelfSTAT_ELPresent 0x0800 /* External address latch logic available */ +#define PP_SelfSTAT_EEsize 0x1000 /* Size of EEPROM */ + +#define PP_BusSTAT 0x0138 /* Bus status */ +#define PP_BusSTAT_TxBid 0x0080 /* Tx error */ +#define PP_BusSTAT_TxRDY 0x0100 /* Ready for Tx data */ + +#define PP_TDR 0x013C /* AUI Time Domain Reflectometer */ + +/* initiate transmit registers */ + +#define PP_TxCommand 0x0144 /* Tx Command */ +#define PP_TxLength 0x0146 /* Tx Length */ + + +/* address filter registers */ + +#define PP_LAF 0x0150 /* Logical address filter (6 bytes) */ +#define PP_IA 0x0158 /* Individual address (MAC) */ + +/* EEPROM Kram */ +#define SI_BUSY 0x0100 +#define PP_EECMD 0x0040 /* NVR Interface Command register */ +#define PP_EEData 0x0042 /* NVR Interface Data Register */ +#define EEPROM_WRITE_EN 0x00F0 +#define EEPROM_WRITE_DIS 0x0000 +#define EEPROM_WRITE_CMD 0x0100 +#define EEPROM_READ_CMD 0x0200 +#define EEPROM_ERASE_CMD 0x0300 + +/* Exported functions */ +int cs8900_e2prom_read(struct eth_device *dev, uchar, ushort *); +int cs8900_e2prom_write(struct eth_device *dev, uchar, ushort); + +#endif /* CS8900_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/davinci_emac.c b/roms/u-boot-sam460ex/drivers/net/davinci_emac.c new file mode 100644 index 000000000..02bbb8c0a --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/davinci_emac.c @@ -0,0 +1,701 @@ +/* + * Ethernet driver for TI TMS320DM644x (DaVinci) chips. + * + * Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net> + * + * Parts shamelessly stolen from TI's dm644x_emac.c. Original copyright + * follows: + * + * ---------------------------------------------------------------------------- + * + * dm644x_emac.c + * + * TI DaVinci (DM644X) EMAC peripheral driver source for DV-EVM + * + * Copyright (C) 2005 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + + * Modifications: + * ver. 1.0: Sep 2005, Anant Gole - Created EMAC version for uBoot. + * ver 1.1: Nov 2005, Anant Gole - Extended the RX logic for multiple descriptors + * + */ +#include <common.h> +#include <command.h> +#include <net.h> +#include <miiphy.h> +#include <malloc.h> +#include <asm/arch/emac_defs.h> +#include <asm/io.h> + +unsigned int emac_dbg = 0; +#define debug_emac(fmt,args...) if (emac_dbg) printf(fmt,##args) + +#ifdef DAVINCI_EMAC_GIG_ENABLE +#define emac_gigabit_enable() davinci_eth_gigabit_enable() +#else +#define emac_gigabit_enable() /* no gigabit to enable */ +#endif + +static void davinci_eth_mdio_enable(void); + +static int gen_init_phy(int phy_addr); +static int gen_is_phy_connected(int phy_addr); +static int gen_get_link_speed(int phy_addr); +static int gen_auto_negotiate(int phy_addr); + +void eth_mdio_enable(void) +{ + davinci_eth_mdio_enable(); +} + +static u_int8_t davinci_eth_mac_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +/* + * This function must be called before emac_open() if you want to override + * the default mac address. + */ +void davinci_eth_set_mac_addr(const u_int8_t *addr) +{ + int i; + + for (i = 0; i < sizeof (davinci_eth_mac_addr); i++) { + davinci_eth_mac_addr[i] = addr[i]; + } +} + +/* EMAC Addresses */ +static volatile emac_regs *adap_emac = (emac_regs *)EMAC_BASE_ADDR; +static volatile ewrap_regs *adap_ewrap = (ewrap_regs *)EMAC_WRAPPER_BASE_ADDR; +static volatile mdio_regs *adap_mdio = (mdio_regs *)EMAC_MDIO_BASE_ADDR; + +/* EMAC descriptors */ +static volatile emac_desc *emac_rx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE); +static volatile emac_desc *emac_tx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE); +static volatile emac_desc *emac_rx_active_head = 0; +static volatile emac_desc *emac_rx_active_tail = 0; +static int emac_rx_queue_active = 0; + +/* Receive packet buffers */ +static unsigned char emac_rx_buffers[EMAC_MAX_RX_BUFFERS * (EMAC_MAX_ETHERNET_PKT_SIZE + EMAC_PKT_ALIGN)]; + +/* PHY address for a discovered PHY (0xff - not found) */ +static volatile u_int8_t active_phy_addr = 0xff; + +phy_t phy; + +static void davinci_eth_mdio_enable(void) +{ + u_int32_t clkdiv; + + clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + + writel((clkdiv & 0xff) | + MDIO_CONTROL_ENABLE | + MDIO_CONTROL_FAULT | + MDIO_CONTROL_FAULT_ENABLE, + &adap_mdio->CONTROL); + + while (readl(&adap_mdio->CONTROL) & MDIO_CONTROL_IDLE) + ; +} + +/* + * Tries to find an active connected PHY. Returns 1 if address if found. + * If no active PHY (or more than one PHY) found returns 0. + * Sets active_phy_addr variable. + */ +static int davinci_eth_phy_detect(void) +{ + u_int32_t phy_act_state; + int i; + + active_phy_addr = 0xff; + + phy_act_state = readl(&adap_mdio->ALIVE) & EMAC_MDIO_PHY_MASK; + if (phy_act_state == 0) + return(0); /* No active PHYs */ + + debug_emac("davinci_eth_phy_detect(), ALIVE = 0x%08x\n", phy_act_state); + + for (i = 0; i < 32; i++) { + if (phy_act_state & (1 << i)) { + if (phy_act_state & ~(1 << i)) + return(0); /* More than one PHY */ + else { + active_phy_addr = i; + return(1); + } + } + } + + return(0); /* Just to make GCC happy */ +} + + +/* Read a PHY register via MDIO inteface. Returns 1 on success, 0 otherwise */ +int davinci_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data) +{ + int tmp; + + while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO) + ; + + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_READ | + ((reg_num & 0x1f) << 21) | + ((phy_addr & 0x1f) << 16), + &adap_mdio->USERACCESS0); + + /* Wait for command to complete */ + while ((tmp = readl(&adap_mdio->USERACCESS0)) & MDIO_USERACCESS0_GO) + ; + + if (tmp & MDIO_USERACCESS0_ACK) { + *data = tmp & 0xffff; + return(1); + } + + *data = -1; + return(0); +} + +/* Write to a PHY register via MDIO inteface. Blocks until operation is complete. */ +int davinci_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data) +{ + + while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO) + ; + + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_WRITE | + ((reg_num & 0x1f) << 21) | + ((phy_addr & 0x1f) << 16) | + (data & 0xffff), + &adap_mdio->USERACCESS0); + + /* Wait for command to complete */ + while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO) + ; + + return(1); +} + +/* PHY functions for a generic PHY */ +static int gen_init_phy(int phy_addr) +{ + int ret = 1; + + if (gen_get_link_speed(phy_addr)) { + /* Try another time */ + ret = gen_get_link_speed(phy_addr); + } + + return(ret); +} + +static int gen_is_phy_connected(int phy_addr) +{ + u_int16_t dummy; + + return(davinci_eth_phy_read(phy_addr, PHY_PHYIDR1, &dummy)); +} + +static int gen_get_link_speed(int phy_addr) +{ + u_int16_t tmp; + + if (davinci_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp) && (tmp & 0x04)) + return(1); + + return(0); +} + +static int gen_auto_negotiate(int phy_addr) +{ + u_int16_t tmp; + + if (!davinci_eth_phy_read(phy_addr, PHY_BMCR, &tmp)) + return(0); + + /* Restart Auto_negotiation */ + tmp |= PHY_BMCR_AUTON; + davinci_eth_phy_write(phy_addr, PHY_BMCR, tmp); + + /*check AutoNegotiate complete */ + udelay (10000); + if (!davinci_eth_phy_read(phy_addr, PHY_BMSR, &tmp)) + return(0); + + if (!(tmp & PHY_BMSR_AUTN_COMP)) + return(0); + + return(gen_get_link_speed(phy_addr)); +} +/* End of generic PHY functions */ + + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static int davinci_mii_phy_read(char *devname, unsigned char addr, unsigned char reg, unsigned short *value) +{ + return(davinci_eth_phy_read(addr, reg, value) ? 0 : 1); +} + +static int davinci_mii_phy_write(char *devname, unsigned char addr, unsigned char reg, unsigned short value) +{ + return(davinci_eth_phy_write(addr, reg, value) ? 0 : 1); +} +#endif + +static void __attribute__((unused)) davinci_eth_gigabit_enable(void) +{ + u_int16_t data; + + if (davinci_eth_phy_read(EMAC_MDIO_PHY_NUM, 0, &data)) { + if (data & (1 << 6)) { /* speed selection MSB */ + /* + * Check if link detected is giga-bit + * If Gigabit mode detected, enable gigbit in MAC + */ + writel(EMAC_MACCONTROL_GIGFORCE | + EMAC_MACCONTROL_GIGABIT_ENABLE, + &adap_emac->MACCONTROL); + } + } +} + +/* Eth device open */ +static int davinci_eth_open(struct eth_device *dev, bd_t *bis) +{ + dv_reg_p addr; + u_int32_t clkdiv, cnt; + volatile emac_desc *rx_desc; + unsigned long mac_hi; + unsigned long mac_lo; + + debug_emac("+ emac_open\n"); + + /* Reset EMAC module and disable interrupts in wrapper */ + writel(1, &adap_emac->SOFTRESET); + while (readl(&adap_emac->SOFTRESET) != 0) + ; +#if defined(DAVINCI_EMAC_VERSION2) + writel(1, &adap_ewrap->softrst); + while (readl(&adap_ewrap->softrst) != 0) + ; +#else + writel(0, &adap_ewrap->EWCTL); + for (cnt = 0; cnt < 5; cnt++) { + clkdiv = readl(&adap_ewrap->EWCTL); + } +#endif + + rx_desc = emac_rx_desc; + + writel(1, &adap_emac->TXCONTROL); + writel(1, &adap_emac->RXCONTROL); + + /* Set MAC Addresses & Init multicast Hash to 0 (disable any multicast receive) */ + /* Using channel 0 only - other channels are disabled */ + writel(0, &adap_emac->MACINDEX); + mac_hi = (davinci_eth_mac_addr[3] << 24) | + (davinci_eth_mac_addr[2] << 16) | + (davinci_eth_mac_addr[1] << 8) | + (davinci_eth_mac_addr[0]); + mac_lo = (davinci_eth_mac_addr[5] << 8) | + (davinci_eth_mac_addr[4]); + + writel(mac_hi, &adap_emac->MACADDRHI); +#if defined(DAVINCI_EMAC_VERSION2) + writel(mac_lo | EMAC_MAC_ADDR_IS_VALID | EMAC_MAC_ADDR_MATCH, + &adap_emac->MACADDRLO); +#else + writel(mac_lo, &adap_emac->MACADDRLO); +#endif + + writel(0, &adap_emac->MACHASH1); + writel(0, &adap_emac->MACHASH2); + + /* Set source MAC address - REQUIRED */ + writel(mac_hi, &adap_emac->MACSRCADDRHI); + writel(mac_lo, &adap_emac->MACSRCADDRLO); + + /* Set DMA 8 TX / 8 RX Head pointers to 0 */ + addr = &adap_emac->TX0HDP; + for(cnt = 0; cnt < 16; cnt++) + writel(0, addr++); + + addr = &adap_emac->RX0HDP; + for(cnt = 0; cnt < 16; cnt++) + writel(0, addr++); + + /* Clear Statistics (do this before setting MacControl register) */ + addr = &adap_emac->RXGOODFRAMES; + for(cnt = 0; cnt < EMAC_NUM_STATS; cnt++) + writel(0, addr++); + + /* No multicast addressing */ + writel(0, &adap_emac->MACHASH1); + writel(0, &adap_emac->MACHASH2); + + /* Create RX queue and set receive process in place */ + emac_rx_active_head = emac_rx_desc; + for (cnt = 0; cnt < EMAC_MAX_RX_BUFFERS; cnt++) { + rx_desc->next = (u_int32_t)(rx_desc + 1); + rx_desc->buffer = &emac_rx_buffers[cnt * (EMAC_MAX_ETHERNET_PKT_SIZE + EMAC_PKT_ALIGN)]; + rx_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE; + rx_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT; + rx_desc++; + } + + /* Finalize the rx desc list */ + rx_desc--; + rx_desc->next = 0; + emac_rx_active_tail = rx_desc; + emac_rx_queue_active = 1; + + /* Enable TX/RX */ + writel(EMAC_MAX_ETHERNET_PKT_SIZE, &adap_emac->RXMAXLEN); + writel(0, &adap_emac->RXBUFFEROFFSET); + + /* + * No fancy configs - Use this for promiscous debug + * - EMAC_RXMBPENABLE_RXCAFEN_ENABLE + */ + writel(EMAC_RXMBPENABLE_RXBROADEN, &adap_emac->RXMBPENABLE); + + /* Enable ch 0 only */ + writel(1, &adap_emac->RXUNICASTSET); + + /* Enable MII interface and Full duplex mode */ +#ifdef CONFIG_SOC_DA8XX + writel((EMAC_MACCONTROL_MIIEN_ENABLE | + EMAC_MACCONTROL_FULLDUPLEX_ENABLE | + EMAC_MACCONTROL_RMIISPEED_100), + &adap_emac->MACCONTROL); +#else + writel((EMAC_MACCONTROL_MIIEN_ENABLE | + EMAC_MACCONTROL_FULLDUPLEX_ENABLE), + &adap_emac->MACCONTROL); +#endif + + /* Init MDIO & get link state */ + clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | MDIO_CONTROL_FAULT, + &adap_mdio->CONTROL); + + /* We need to wait for MDIO to start */ + udelay(1000); + + if (!phy.get_link_speed(active_phy_addr)) + return(0); + + emac_gigabit_enable(); + + /* Start receive process */ + writel((u_int32_t)emac_rx_desc, &adap_emac->RX0HDP); + + debug_emac("- emac_open\n"); + + return(1); +} + +/* EMAC Channel Teardown */ +static void davinci_eth_ch_teardown(int ch) +{ + dv_reg dly = 0xff; + dv_reg cnt; + + debug_emac("+ emac_ch_teardown\n"); + + if (ch == EMAC_CH_TX) { + /* Init TX channel teardown */ + writel(1, &adap_emac->TXTEARDOWN); + do { + /* + * Wait here for Tx teardown completion interrupt to + * occur. Note: A task delay can be called here to pend + * rather than occupying CPU cycles - anyway it has + * been found that teardown takes very few cpu cycles + * and does not affect functionality + */ + dly--; + udelay(1); + if (dly == 0) + break; + cnt = readl(&adap_emac->TX0CP); + } while (cnt != 0xfffffffc); + writel(cnt, &adap_emac->TX0CP); + writel(0, &adap_emac->TX0HDP); + } else { + /* Init RX channel teardown */ + writel(1, &adap_emac->RXTEARDOWN); + do { + /* + * Wait here for Rx teardown completion interrupt to + * occur. Note: A task delay can be called here to pend + * rather than occupying CPU cycles - anyway it has + * been found that teardown takes very few cpu cycles + * and does not affect functionality + */ + dly--; + udelay(1); + if (dly == 0) + break; + cnt = readl(&adap_emac->RX0CP); + } while (cnt != 0xfffffffc); + writel(cnt, &adap_emac->RX0CP); + writel(0, &adap_emac->RX0HDP); + } + + debug_emac("- emac_ch_teardown\n"); +} + +/* Eth device close */ +static void davinci_eth_close(struct eth_device *dev) +{ + debug_emac("+ emac_close\n"); + + davinci_eth_ch_teardown(EMAC_CH_TX); /* TX Channel teardown */ + davinci_eth_ch_teardown(EMAC_CH_RX); /* RX Channel teardown */ + + /* Reset EMAC module and disable interrupts in wrapper */ + writel(1, &adap_emac->SOFTRESET); +#if defined(DAVINCI_EMAC_VERSION2) + writel(1, &adap_ewrap->softrst); +#else + writel(0, &adap_ewrap->EWCTL); +#endif + + debug_emac("- emac_close\n"); +} + +static int tx_send_loop = 0; + +/* + * This function sends a single packet on the network and returns + * positive number (number of bytes transmitted) or negative for error + */ +static int davinci_eth_send_packet (struct eth_device *dev, + volatile void *packet, int length) +{ + int ret_status = -1; + + tx_send_loop = 0; + + /* Return error if no link */ + if (!phy.get_link_speed (active_phy_addr)) { + printf ("WARN: emac_send_packet: No link\n"); + return (ret_status); + } + + emac_gigabit_enable(); + + /* Check packet size and if < EMAC_MIN_ETHERNET_PKT_SIZE, pad it up */ + if (length < EMAC_MIN_ETHERNET_PKT_SIZE) { + length = EMAC_MIN_ETHERNET_PKT_SIZE; + } + + /* Populate the TX descriptor */ + emac_tx_desc->next = 0; + emac_tx_desc->buffer = (u_int8_t *) packet; + emac_tx_desc->buff_off_len = (length & 0xffff); + emac_tx_desc->pkt_flag_len = ((length & 0xffff) | + EMAC_CPPI_SOP_BIT | + EMAC_CPPI_OWNERSHIP_BIT | + EMAC_CPPI_EOP_BIT); + /* Send the packet */ + writel((unsigned long)emac_tx_desc, &adap_emac->TX0HDP); + + /* Wait for packet to complete or link down */ + while (1) { + if (!phy.get_link_speed (active_phy_addr)) { + davinci_eth_ch_teardown (EMAC_CH_TX); + return (ret_status); + } + + emac_gigabit_enable(); + + if (readl(&adap_emac->TXINTSTATRAW) & 0x01) { + ret_status = length; + break; + } + tx_send_loop++; + } + + return (ret_status); +} + +/* + * This function handles receipt of a packet from the network + */ +static int davinci_eth_rcv_packet (struct eth_device *dev) +{ + volatile emac_desc *rx_curr_desc; + volatile emac_desc *curr_desc; + volatile emac_desc *tail_desc; + int status, ret = -1; + + rx_curr_desc = emac_rx_active_head; + status = rx_curr_desc->pkt_flag_len; + if ((rx_curr_desc) && ((status & EMAC_CPPI_OWNERSHIP_BIT) == 0)) { + if (status & EMAC_CPPI_RX_ERROR_FRAME) { + /* Error in packet - discard it and requeue desc */ + printf ("WARN: emac_rcv_pkt: Error in packet\n"); + } else { + NetReceive (rx_curr_desc->buffer, + (rx_curr_desc->buff_off_len & 0xffff)); + ret = rx_curr_desc->buff_off_len & 0xffff; + } + + /* Ack received packet descriptor */ + writel((unsigned long)rx_curr_desc, &adap_emac->RX0CP); + curr_desc = rx_curr_desc; + emac_rx_active_head = + (volatile emac_desc *) rx_curr_desc->next; + + if (status & EMAC_CPPI_EOQ_BIT) { + if (emac_rx_active_head) { + writel((unsigned long)emac_rx_active_head, + &adap_emac->RX0HDP); + } else { + emac_rx_queue_active = 0; + printf ("INFO:emac_rcv_packet: RX Queue not active\n"); + } + } + + /* Recycle RX descriptor */ + rx_curr_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE; + rx_curr_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT; + rx_curr_desc->next = 0; + + if (emac_rx_active_head == 0) { + printf ("INFO: emac_rcv_pkt: active queue head = 0\n"); + emac_rx_active_head = curr_desc; + emac_rx_active_tail = curr_desc; + if (emac_rx_queue_active != 0) { + writel((unsigned long)emac_rx_active_head, + &adap_emac->RX0HDP); + printf ("INFO: emac_rcv_pkt: active queue head = 0, HDP fired\n"); + emac_rx_queue_active = 1; + } + } else { + tail_desc = emac_rx_active_tail; + emac_rx_active_tail = curr_desc; + tail_desc->next = (unsigned int) curr_desc; + status = tail_desc->pkt_flag_len; + if (status & EMAC_CPPI_EOQ_BIT) { + writel((unsigned long)curr_desc, + &adap_emac->RX0HDP); + status &= ~EMAC_CPPI_EOQ_BIT; + tail_desc->pkt_flag_len = status; + } + } + return (ret); + } + return (0); +} + +/* + * This function initializes the emac hardware. It does NOT initialize + * EMAC modules power or pin multiplexors, that is done by board_init() + * much earlier in bootup process. Returns 1 on success, 0 otherwise. + */ +int davinci_emac_initialize(void) +{ + u_int32_t phy_id; + u_int16_t tmp; + int i; + struct eth_device *dev; + + dev = malloc(sizeof *dev); + + if (dev == NULL) + return -1; + + memset(dev, 0, sizeof *dev); + + dev->iobase = 0; + dev->init = davinci_eth_open; + dev->halt = davinci_eth_close; + dev->send = davinci_eth_send_packet; + dev->recv = davinci_eth_rcv_packet; + + eth_register(dev); + + davinci_eth_mdio_enable(); + + for (i = 0; i < 256; i++) { + if (readl(&adap_mdio->ALIVE)) + break; + udelay(10); + } + + if (i >= 256) { + printf("No ETH PHY detected!!!\n"); + return(0); + } + + /* Find if a PHY is connected and get it's address */ + if (!davinci_eth_phy_detect()) + return(0); + + /* Get PHY ID and initialize phy_ops for a detected PHY */ + if (!davinci_eth_phy_read(active_phy_addr, PHY_PHYIDR1, &tmp)) { + active_phy_addr = 0xff; + return(0); + } + + phy_id = (tmp << 16) & 0xffff0000; + + if (!davinci_eth_phy_read(active_phy_addr, PHY_PHYIDR2, &tmp)) { + active_phy_addr = 0xff; + return(0); + } + + phy_id |= tmp & 0x0000ffff; + + switch (phy_id) { + case PHY_LXT972: + sprintf(phy.name, "LXT972 @ 0x%02x", active_phy_addr); + phy.init = lxt972_init_phy; + phy.is_phy_connected = lxt972_is_phy_connected; + phy.get_link_speed = lxt972_get_link_speed; + phy.auto_negotiate = lxt972_auto_negotiate; + break; + case PHY_DP83848: + sprintf(phy.name, "DP83848 @ 0x%02x", active_phy_addr); + phy.init = dp83848_init_phy; + phy.is_phy_connected = dp83848_is_phy_connected; + phy.get_link_speed = dp83848_get_link_speed; + phy.auto_negotiate = dp83848_auto_negotiate; + break; + default: + sprintf(phy.name, "GENERIC @ 0x%02x", active_phy_addr); + phy.init = gen_init_phy; + phy.is_phy_connected = gen_is_phy_connected; + phy.get_link_speed = gen_get_link_speed; + phy.auto_negotiate = gen_auto_negotiate; + } + + printf("Ethernet PHY: %s\n", phy.name); + + miiphy_register(phy.name, davinci_mii_phy_read, davinci_mii_phy_write); + return(1); +} diff --git a/roms/u-boot-sam460ex/drivers/net/dc2114x.c b/roms/u-boot-sam460ex/drivers/net/dc2114x.c new file mode 100644 index 000000000..5ae53e816 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/dc2114x.c @@ -0,0 +1,768 @@ +/* + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <pci.h> + +#undef DEBUG_SROM +#undef DEBUG_SROM2 + +#undef UPDATE_SROM + +/* PCI Registers. + */ +#define PCI_CFDA_PSM 0x43 + +#define CFRV_RN 0x000000f0 /* Revision Number */ + +#define WAKEUP 0x00 /* Power Saving Wakeup */ +#define SLEEP 0x80 /* Power Saving Sleep Mode */ + +#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ + +/* Ethernet chip registers. + */ +#define DE4X5_BMR 0x000 /* Bus Mode Register */ +#define DE4X5_TPD 0x008 /* Transmit Poll Demand Reg */ +#define DE4X5_RRBA 0x018 /* RX Ring Base Address Reg */ +#define DE4X5_TRBA 0x020 /* TX Ring Base Address Reg */ +#define DE4X5_STS 0x028 /* Status Register */ +#define DE4X5_OMR 0x030 /* Operation Mode Register */ +#define DE4X5_SICR 0x068 /* SIA Connectivity Register */ +#define DE4X5_APROM 0x048 /* Ethernet Address PROM */ + +/* Register bits. + */ +#define BMR_SWR 0x00000001 /* Software Reset */ +#define STS_TS 0x00700000 /* Transmit Process State */ +#define STS_RS 0x000e0000 /* Receive Process State */ +#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ +#define OMR_SR 0x00000002 /* Start/Stop Receive */ +#define OMR_PS 0x00040000 /* Port Select */ +#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ +#define OMR_PM 0x00000080 /* Pass All Multicast */ + +/* Descriptor bits. + */ +#define R_OWN 0x80000000 /* Own Bit */ +#define RD_RER 0x02000000 /* Receive End Of Ring */ +#define RD_LS 0x00000100 /* Last Descriptor */ +#define RD_ES 0x00008000 /* Error Summary */ +#define TD_TER 0x02000000 /* Transmit End Of Ring */ +#define T_OWN 0x80000000 /* Own Bit */ +#define TD_LS 0x40000000 /* Last Segment */ +#define TD_FS 0x20000000 /* First Segment */ +#define TD_ES 0x00008000 /* Error Summary */ +#define TD_SET 0x08000000 /* Setup Packet */ + +/* The EEPROM commands include the alway-set leading bit. */ +#define SROM_WRITE_CMD 5 +#define SROM_READ_CMD 6 +#define SROM_ERASE_CMD 7 + +#define SROM_HWADD 0x0014 /* Hardware Address offset in SROM */ +#define SROM_RD 0x00004000 /* Read from Boot ROM */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x4801 +#define EE_WRITE_1 0x4805 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define SROM_SR 0x00000800 /* Select Serial ROM when set */ + +#define DT_IN 0x00000004 /* Serial Data In */ +#define DT_CLK 0x00000002 /* Serial ROM Clock */ +#define DT_CS 0x00000001 /* Serial ROM Chip Select */ + +#define POLL_DEMAND 1 + +#ifdef CONFIG_TULIP_FIX_DAVICOM +#define RESET_DM9102(dev) {\ + unsigned long i;\ + i=INL(dev, 0x0);\ + udelay(1000);\ + OUTL(dev, i | BMR_SWR, DE4X5_BMR);\ + udelay(1000);\ +} +#else +#define RESET_DE4X5(dev) {\ + int i;\ + i=INL(dev, DE4X5_BMR);\ + udelay(1000);\ + OUTL(dev, i | BMR_SWR, DE4X5_BMR);\ + udelay(1000);\ + OUTL(dev, i, DE4X5_BMR);\ + udelay(1000);\ + for (i=0;i<5;i++) {INL(dev, DE4X5_BMR); udelay(10000);}\ + udelay(1000);\ +} +#endif + +#define START_DE4X5(dev) {\ + s32 omr; \ + omr = INL(dev, DE4X5_OMR);\ + omr |= OMR_ST | OMR_SR;\ + OUTL(dev, omr, DE4X5_OMR); /* Enable the TX and/or RX */\ +} + +#define STOP_DE4X5(dev) {\ + s32 omr; \ + omr = INL(dev, DE4X5_OMR);\ + omr &= ~(OMR_ST|OMR_SR);\ + OUTL(dev, omr, DE4X5_OMR); /* Disable the TX and/or RX */ \ +} + +#define NUM_RX_DESC PKTBUFSRX +#ifndef CONFIG_TULIP_FIX_DAVICOM + #define NUM_TX_DESC 1 /* Number of TX descriptors */ +#else + #define NUM_TX_DESC 4 +#endif +#define RX_BUFF_SZ PKTSIZE_ALIGN + +#define TOUT_LOOP 1000000 + +#define SETUP_FRAME_LEN 192 +#define ETH_ALEN 6 + +struct de4x5_desc { + volatile s32 status; + u32 des1; + u32 buf; + u32 next; +}; + +static struct de4x5_desc rx_ring[NUM_RX_DESC] __attribute__ ((aligned(32))); /* RX descriptor ring */ +static struct de4x5_desc tx_ring[NUM_TX_DESC] __attribute__ ((aligned(32))); /* TX descriptor ring */ +static int rx_new; /* RX descriptor ring pointer */ +static int tx_new; /* TX descriptor ring pointer */ + +static char rxRingSize; +static char txRingSize; + +#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM) +static void sendto_srom(struct eth_device* dev, u_int command, u_long addr); +static int getfrom_srom(struct eth_device* dev, u_long addr); +static int do_eeprom_cmd(struct eth_device *dev, u_long ioaddr,int cmd,int cmd_len); +static int do_read_eeprom(struct eth_device *dev,u_long ioaddr,int location,int addr_len); +#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */ +#ifdef UPDATE_SROM +static int write_srom(struct eth_device *dev, u_long ioaddr, int index, int new_value); +static void update_srom(struct eth_device *dev, bd_t *bis); +#endif +#ifndef CONFIG_TULIP_FIX_DAVICOM +static int read_srom(struct eth_device *dev, u_long ioaddr, int index); +static void read_hw_addr(struct eth_device* dev, bd_t * bis); +#endif /* CONFIG_TULIP_FIX_DAVICOM */ +static void send_setup_frame(struct eth_device* dev, bd_t * bis); + +static int dc21x4x_init(struct eth_device* dev, bd_t* bis); +static int dc21x4x_send(struct eth_device* dev, volatile void *packet, int length); +static int dc21x4x_recv(struct eth_device* dev); +static void dc21x4x_halt(struct eth_device* dev); +#ifdef CONFIG_TULIP_SELECT_MEDIA +extern void dc21x4x_select_media(struct eth_device* dev); +#endif + +#if defined(CONFIG_E500) +#define phys_to_bus(a) (a) +#else +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) +#endif + +static int INL(struct eth_device* dev, u_long addr) +{ + return le32_to_cpu(*(volatile u_long *)(addr + dev->iobase)); +} + +static void OUTL(struct eth_device* dev, int command, u_long addr) +{ + *(volatile u_long *)(addr + dev->iobase) = cpu_to_le32(command); +} + +static struct pci_device_id supported[] = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142 }, +#ifdef CONFIG_TULIP_FIX_DAVICOM + { PCI_VENDOR_ID_DAVICOM, PCI_DEVICE_ID_DAVICOM_DM9102A }, +#endif + { } +}; + +int dc21x4x_initialize(bd_t *bis) +{ + int idx=0; + int card_number = 0; + unsigned int cfrv; + unsigned char timer; + pci_dev_t devbusfn; + unsigned int iobase; + unsigned short status; + struct eth_device* dev; + + while(1) { + devbusfn = pci_find_devices(supported, idx++); + if (devbusfn == -1) { + break; + } + + /* Get the chip configuration revision register. */ + pci_read_config_dword(devbusfn, PCI_REVISION_ID, &cfrv); + +#ifndef CONFIG_TULIP_FIX_DAVICOM + if ((cfrv & CFRV_RN) < DC2114x_BRK ) { + printf("Error: The chip is not DC21143.\n"); + continue; + } +#endif + + pci_read_config_word(devbusfn, PCI_COMMAND, &status); + status |= +#ifdef CONFIG_TULIP_USE_IO + PCI_COMMAND_IO | +#else + PCI_COMMAND_MEMORY | +#endif + PCI_COMMAND_MASTER; + pci_write_config_word(devbusfn, PCI_COMMAND, status); + + pci_read_config_word(devbusfn, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_IO)) { + printf("Error: Can not enable I/O access.\n"); + continue; + } + + if (!(status & PCI_COMMAND_IO)) { + printf("Error: Can not enable I/O access.\n"); + continue; + } + + if (!(status & PCI_COMMAND_MASTER)) { + printf("Error: Can not enable Bus Mastering.\n"); + continue; + } + + /* Check the latency timer for values >= 0x60. */ + pci_read_config_byte(devbusfn, PCI_LATENCY_TIMER, &timer); + + if (timer < 0x60) { + pci_write_config_byte(devbusfn, PCI_LATENCY_TIMER, 0x60); + } + +#ifdef CONFIG_TULIP_USE_IO + /* read BAR for memory space access */ + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &iobase); + iobase &= PCI_BASE_ADDRESS_IO_MASK; +#else + /* read BAR for memory space access */ + pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_1, &iobase); + iobase &= PCI_BASE_ADDRESS_MEM_MASK; +#endif + debug ("dc21x4x: DEC 21142 PCI Device @0x%x\n", iobase); + + dev = (struct eth_device*) malloc(sizeof *dev); + +#ifdef CONFIG_TULIP_FIX_DAVICOM + sprintf(dev->name, "Davicom#%d", card_number); +#else + sprintf(dev->name, "dc21x4x#%d", card_number); +#endif + +#ifdef CONFIG_TULIP_USE_IO + dev->iobase = pci_io_to_phys(devbusfn, iobase); +#else + dev->iobase = pci_mem_to_phys(devbusfn, iobase); +#endif + dev->priv = (void*) devbusfn; + dev->init = dc21x4x_init; + dev->halt = dc21x4x_halt; + dev->send = dc21x4x_send; + dev->recv = dc21x4x_recv; + + /* Ensure we're not sleeping. */ + pci_write_config_byte(devbusfn, PCI_CFDA_PSM, WAKEUP); + + udelay(10 * 1000); + +#ifndef CONFIG_TULIP_FIX_DAVICOM + read_hw_addr(dev, bis); +#endif + eth_register(dev); + + card_number++; + } + + return card_number; +} + +static int dc21x4x_init(struct eth_device* dev, bd_t* bis) +{ + int i; + int devbusfn = (int) dev->priv; + + /* Ensure we're not sleeping. */ + pci_write_config_byte(devbusfn, PCI_CFDA_PSM, WAKEUP); + +#ifdef CONFIG_TULIP_FIX_DAVICOM + RESET_DM9102(dev); +#else + RESET_DE4X5(dev); +#endif + + if ((INL(dev, DE4X5_STS) & (STS_TS | STS_RS)) != 0) { + printf("Error: Cannot reset ethernet controller.\n"); + return -1; + } + +#ifdef CONFIG_TULIP_SELECT_MEDIA + dc21x4x_select_media(dev); +#else + OUTL(dev, OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR); +#endif + + for (i = 0; i < NUM_RX_DESC; i++) { + rx_ring[i].status = cpu_to_le32(R_OWN); + rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + rx_ring[i].buf = cpu_to_le32(phys_to_bus((u32) NetRxPackets[i])); +#ifdef CONFIG_TULIP_FIX_DAVICOM + rx_ring[i].next = cpu_to_le32(phys_to_bus((u32) &rx_ring[(i+1) % NUM_RX_DESC])); +#else + rx_ring[i].next = 0; +#endif + } + + for (i=0; i < NUM_TX_DESC; i++) { + tx_ring[i].status = 0; + tx_ring[i].des1 = 0; + tx_ring[i].buf = 0; + +#ifdef CONFIG_TULIP_FIX_DAVICOM + tx_ring[i].next = cpu_to_le32(phys_to_bus((u32) &tx_ring[(i+1) % NUM_TX_DESC])); +#else + tx_ring[i].next = 0; +#endif + } + + rxRingSize = NUM_RX_DESC; + txRingSize = NUM_TX_DESC; + + /* Write the end of list marker to the descriptor lists. */ + rx_ring[rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); + tx_ring[txRingSize - 1].des1 |= cpu_to_le32(TD_TER); + + /* Tell the adapter where the TX/RX rings are located. */ + OUTL(dev, phys_to_bus((u32) &rx_ring), DE4X5_RRBA); + OUTL(dev, phys_to_bus((u32) &tx_ring), DE4X5_TRBA); + + START_DE4X5(dev); + + tx_new = 0; + rx_new = 0; + + send_setup_frame(dev, bis); + + return 0; +} + +static int dc21x4x_send(struct eth_device* dev, volatile void *packet, int length) +{ + int status = -1; + int i; + + if (length <= 0) { + printf("%s: bad packet size: %d\n", dev->name, length); + goto Done; + } + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx error buffer not ready\n", dev->name); + goto Done; + } + } + + tx_ring[tx_new].buf = cpu_to_le32(phys_to_bus((u32) packet)); + tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length); + tx_ring[tx_new].status = cpu_to_le32(T_OWN); + + OUTL(dev, POLL_DEMAND, DE4X5_TPD); + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf(".%s: tx buffer not ready\n", dev->name); + goto Done; + } + } + + if (le32_to_cpu(tx_ring[tx_new].status) & TD_ES) { +#if 0 /* test-only */ + printf("TX error status = 0x%08X\n", + le32_to_cpu(tx_ring[tx_new].status)); +#endif + tx_ring[tx_new].status = 0x0; + goto Done; + } + + status = length; + + Done: + tx_new = (tx_new+1) % NUM_TX_DESC; + return status; +} + +static int dc21x4x_recv(struct eth_device* dev) +{ + s32 status; + int length = 0; + + for ( ; ; ) { + status = (s32)le32_to_cpu(rx_ring[rx_new].status); + + if (status & R_OWN) { + break; + } + + if (status & RD_LS) { + /* Valid frame status. + */ + if (status & RD_ES) { + + /* There was an error. + */ + printf("RX error status = 0x%08X\n", status); + } else { + /* A valid frame received. + */ + length = (le32_to_cpu(rx_ring[rx_new].status) >> 16); + + /* Pass the packet up to the protocol + * layers. + */ + NetReceive(NetRxPackets[rx_new], length - 4); + } + + /* Change buffer ownership for this frame, back + * to the adapter. + */ + rx_ring[rx_new].status = cpu_to_le32(R_OWN); + } + + /* Update entry information. + */ + rx_new = (rx_new + 1) % rxRingSize; + } + + return length; +} + +static void dc21x4x_halt(struct eth_device* dev) +{ + int devbusfn = (int) dev->priv; + + STOP_DE4X5(dev); + OUTL(dev, 0, DE4X5_SICR); + + pci_write_config_byte(devbusfn, PCI_CFDA_PSM, SLEEP); +} + +static void send_setup_frame(struct eth_device* dev, bd_t *bis) +{ + int i; + char setup_frame[SETUP_FRAME_LEN]; + char *pa = &setup_frame[0]; + + memset(pa, 0xff, SETUP_FRAME_LEN); + + for (i = 0; i < ETH_ALEN; i++) { + *(pa + (i & 1)) = dev->enetaddr[i]; + if (i & 0x01) { + pa += 4; + } + } + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx error buffer not ready\n", dev->name); + goto Done; + } + } + + tx_ring[tx_new].buf = cpu_to_le32(phys_to_bus((u32) &setup_frame[0])); + tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_SET| SETUP_FRAME_LEN); + tx_ring[tx_new].status = cpu_to_le32(T_OWN); + + OUTL(dev, POLL_DEMAND, DE4X5_TPD); + + for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx buffer not ready\n", dev->name); + goto Done; + } + } + + if (le32_to_cpu(tx_ring[tx_new].status) != 0x7FFFFFFF) { + printf("TX error status2 = 0x%08X\n", le32_to_cpu(tx_ring[tx_new].status)); + } + tx_new = (tx_new+1) % NUM_TX_DESC; + +Done: + return; +} + +#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM) +/* SROM Read and write routines. + */ +static void +sendto_srom(struct eth_device* dev, u_int command, u_long addr) +{ + OUTL(dev, command, addr); + udelay(1); +} + +static int +getfrom_srom(struct eth_device* dev, u_long addr) +{ + s32 tmp; + + tmp = INL(dev, addr); + udelay(1); + + return tmp; +} + +/* Note: this routine returns extra data bits for size detection. */ +static int do_read_eeprom(struct eth_device *dev, u_long ioaddr, int location, int addr_len) +{ + int i; + unsigned retval = 0; + int read_cmd = location | (SROM_READ_CMD << addr_len); + + sendto_srom(dev, SROM_RD | SROM_SR, ioaddr); + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr); + +#ifdef DEBUG_SROM + printf(" EEPROM read at %d ", location); +#endif + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | dataval, ioaddr); + udelay(10); + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | dataval | DT_CLK, ioaddr); + udelay(10); +#ifdef DEBUG_SROM2 + printf("%X", getfrom_srom(dev, ioaddr) & 15); +#endif + retval = (retval << 1) | ((getfrom_srom(dev, ioaddr) & EE_DATA_READ) ? 1 : 0); + } + + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr); + +#ifdef DEBUG_SROM2 + printf(" :%X:", getfrom_srom(dev, ioaddr) & 15); +#endif + + for (i = 16; i > 0; i--) { + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr); + udelay(10); +#ifdef DEBUG_SROM2 + printf("%X", getfrom_srom(dev, ioaddr) & 15); +#endif + retval = (retval << 1) | ((getfrom_srom(dev, ioaddr) & EE_DATA_READ) ? 1 : 0); + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr); + udelay(10); + } + + /* Terminate the EEPROM access. */ + sendto_srom(dev, SROM_RD | SROM_SR, ioaddr); + +#ifdef DEBUG_SROM2 + printf(" EEPROM value at %d is %5.5x.\n", location, retval); +#endif + + return retval; +} +#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */ + +/* This executes a generic EEPROM command, typically a write or write + * enable. It returns the data output from the EEPROM, and thus may + * also be used for reads. + */ +#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM) +static int do_eeprom_cmd(struct eth_device *dev, u_long ioaddr, int cmd, int cmd_len) +{ + unsigned retval = 0; + +#ifdef DEBUG_SROM + printf(" EEPROM op 0x%x: ", cmd); +#endif + + sendto_srom(dev,SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr); + + /* Shift the command bits out. */ + do { + short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; + sendto_srom(dev,dataval, ioaddr); + udelay(10); + +#ifdef DEBUG_SROM2 + printf("%X", getfrom_srom(dev,ioaddr) & 15); +#endif + + sendto_srom(dev,dataval | DT_CLK, ioaddr); + udelay(10); + retval = (retval << 1) | ((getfrom_srom(dev,ioaddr) & EE_DATA_READ) ? 1 : 0); + } while (--cmd_len >= 0); + sendto_srom(dev,SROM_RD | SROM_SR | DT_CS, ioaddr); + + /* Terminate the EEPROM access. */ + sendto_srom(dev,SROM_RD | SROM_SR, ioaddr); + +#ifdef DEBUG_SROM + printf(" EEPROM result is 0x%5.5x.\n", retval); +#endif + + return retval; +} +#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */ + +#ifndef CONFIG_TULIP_FIX_DAVICOM +static int read_srom(struct eth_device *dev, u_long ioaddr, int index) +{ + int ee_addr_size = do_read_eeprom(dev, ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; + + return do_eeprom_cmd(dev, ioaddr, + (((SROM_READ_CMD << ee_addr_size) | index) << 16) + | 0xffff, 3 + ee_addr_size + 16); +} +#endif /* CONFIG_TULIP_FIX_DAVICOM */ + +#ifdef UPDATE_SROM +static int write_srom(struct eth_device *dev, u_long ioaddr, int index, int new_value) +{ + int ee_addr_size = do_read_eeprom(dev, ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; + int i; + unsigned short newval; + + udelay(10*1000); /* test-only */ + +#ifdef DEBUG_SROM + printf("ee_addr_size=%d.\n", ee_addr_size); + printf("Writing new entry 0x%4.4x to offset %d.\n", new_value, index); +#endif + + /* Enable programming modes. */ + do_eeprom_cmd(dev, ioaddr, (0x4f << (ee_addr_size-4)), 3+ee_addr_size); + + /* Do the actual write. */ + do_eeprom_cmd(dev, ioaddr, + (((SROM_WRITE_CMD<<ee_addr_size)|index) << 16) | new_value, + 3 + ee_addr_size + 16); + + /* Poll for write finished. */ + sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr); + for (i = 0; i < 10000; i++) /* Typical 2000 ticks */ + if (getfrom_srom(dev, ioaddr) & EE_DATA_READ) + break; + +#ifdef DEBUG_SROM + printf(" Write finished after %d ticks.\n", i); +#endif + + /* Disable programming. */ + do_eeprom_cmd(dev, ioaddr, (0x40 << (ee_addr_size-4)), 3 + ee_addr_size); + + /* And read the result. */ + newval = do_eeprom_cmd(dev, ioaddr, + (((SROM_READ_CMD<<ee_addr_size)|index) << 16) + | 0xffff, 3 + ee_addr_size + 16); +#ifdef DEBUG_SROM + printf(" New value at offset %d is %4.4x.\n", index, newval); +#endif + return 1; +} +#endif + +#ifndef CONFIG_TULIP_FIX_DAVICOM +static void read_hw_addr(struct eth_device *dev, bd_t *bis) +{ + u_short tmp, *p = (u_short *)(&dev->enetaddr[0]); + int i, j = 0; + + for (i = 0; i < (ETH_ALEN >> 1); i++) { + tmp = read_srom(dev, DE4X5_APROM, ((SROM_HWADD >> 1) + i)); + *p = le16_to_cpu(tmp); + j += *p++; + } + + if ((j == 0) || (j == 0x2fffd)) { + memset (dev->enetaddr, 0, ETH_ALEN); + debug ("Warning: can't read HW address from SROM.\n"); + goto Done; + } + + return; + +Done: +#ifdef UPDATE_SROM + update_srom(dev, bis); +#endif + return; +} +#endif /* CONFIG_TULIP_FIX_DAVICOM */ + +#ifdef UPDATE_SROM +static void update_srom(struct eth_device *dev, bd_t *bis) +{ + int i; + static unsigned short eeprom[0x40] = { + 0x140b, 0x6610, 0x0000, 0x0000, /* 00 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 04 */ + 0x00a3, 0x0103, 0x0000, 0x0000, /* 08 */ + 0x0000, 0x1f00, 0x0000, 0x0000, /* 0c */ + 0x0108, 0x038d, 0x0000, 0x0000, /* 10 */ + 0xe078, 0x0001, 0x0040, 0x0018, /* 14 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 18 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 1c */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 20 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 2c */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 30 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 34 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 38 */ + 0x0000, 0x0000, 0x0000, 0x4e07, /* 3c */ + }; + uchar enetaddr[6]; + + /* Ethernet Addr... */ + if (!eth_getenv_enetaddr("ethaddr", enetaddr)) + return; + eeprom[0x0a] = (enetaddr[1] << 8) | enetaddr[0]; + eeprom[0x0b] = (enetaddr[3] << 8) | enetaddr[2]; + eeprom[0x0c] = (enetaddr[5] << 8) | enetaddr[4]; + + for (i=0; i<0x40; i++) { + write_srom(dev, DE4X5_APROM, i, eeprom[i]); + } +} +#endif /* UPDATE_SROM */ diff --git a/roms/u-boot-sam460ex/drivers/net/dm9000x.c b/roms/u-boot-sam460ex/drivers/net/dm9000x.c new file mode 100644 index 000000000..137e41fef --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/dm9000x.c @@ -0,0 +1,635 @@ +/* + dm9000.c: Version 1.2 12/15/2003 + + A Davicom DM9000 ISA NIC fast Ethernet driver for Linux. + Copyright (C) 1997 Sten Wang + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. + +V0.11 06/20/2001 REG_0A bit3=1, default enable BP with DA match + 06/22/2001 Support DM9801 progrmming + E3: R25 = ((R24 + NF) & 0x00ff) | 0xf000 + E4: R25 = ((R24 + NF) & 0x00ff) | 0xc200 + R17 = (R17 & 0xfff0) | NF + 3 + E5: R25 = ((R24 + NF - 3) & 0x00ff) | 0xc200 + R17 = (R17 & 0xfff0) | NF + +v1.00 modify by simon 2001.9.5 + change for kernel 2.4.x + +v1.1 11/09/2001 fix force mode bug + +v1.2 03/18/2003 Weilun Huang <weilun_huang@davicom.com.tw>: + Fixed phy reset. + Added tx/rx 32 bit mode. + Cleaned up for kernel merge. + +-------------------------------------- + + 12/15/2003 Initial port to u-boot by + Sascha Hauer <saschahauer@web.de> + + 06/03/2008 Remy Bohmer <linux@bohmer.net> + - Fixed the driver to work with DM9000A. + (check on ISR receive status bit before reading the + FIFO as described in DM9000 programming guide and + application notes) + - Added autodetect of databus width. + - Made debug code compile again. + - Adapt eth_send such that it matches the DM9000* + application notes. Needed to make it work properly + for DM9000A. + - Adapted reset procedure to match DM9000 application + notes (i.e. double reset) + - some minor code cleanups + These changes are tested with DM9000{A,EP,E} together + with a 200MHz Atmel AT91SAM9261 core + +TODO: external MII is not functional, only internal at the moment. +*/ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <asm/io.h> +#include <dm9000.h> + +#include "dm9000x.h" + +/* Board/System/Debug information/definition ---------------- */ + +/* #define CONFIG_DM9000_DEBUG */ + +#ifdef CONFIG_DM9000_DEBUG +#define DM9000_DBG(fmt,args...) printf(fmt, ##args) +#define DM9000_DMP_PACKET(func,packet,length) \ + do { \ + int i; \ + printf("%s: length: %d\n", func, length); \ + for (i = 0; i < length; i++) { \ + if (i % 8 == 0) \ + printf("\n%s: %02x: ", func, i); \ + printf("%02x ", ((unsigned char *) packet)[i]); \ + } printf("\n"); \ + } while(0) +#else +#define DM9000_DBG(fmt,args...) +#define DM9000_DMP_PACKET(func,packet,length) +#endif + +/* Structure/enum declaration ------------------------------- */ +typedef struct board_info { + u32 runt_length_counter; /* counter: RX length < 64byte */ + u32 long_length_counter; /* counter: RX length > 1514byte */ + u32 reset_counter; /* counter: RESET */ + u32 reset_tx_timeout; /* RESET caused by TX Timeout */ + u32 reset_rx_status; /* RESET caused by RX Statsus wrong */ + u16 tx_pkt_cnt; + u16 queue_start_addr; + u16 dbug_cnt; + u8 phy_addr; + u8 device_wait_reset; /* device state */ + unsigned char srom[128]; + void (*outblk)(volatile void *data_ptr, int count); + void (*inblk)(void *data_ptr, int count); + void (*rx_status)(u16 *RxStatus, u16 *RxLen); + struct eth_device netdev; +} board_info_t; +static board_info_t dm9000_info; + + +/* function declaration ------------------------------------- */ +static int dm9000_probe(void); +static u16 phy_read(int); +static void phy_write(int, u16); +static u8 DM9000_ior(int); +static void DM9000_iow(int reg, u8 value); + +/* DM9000 network board routine ---------------------------- */ + +#define DM9000_outb(d,r) writeb(d, r) +#define DM9000_outw(d,r) writew(d, r) +#define DM9000_outl(d,r) writel(d, r) +#define DM9000_inb(r) readb(r) +#define DM9000_inw(r) readw(r) +#define DM9000_inl(r) readl(r) + +#ifdef CONFIG_DM9000_DEBUG +static void +dump_regs(void) +{ + DM9000_DBG("\n"); + DM9000_DBG("NCR (0x00): %02x\n", DM9000_ior(0)); + DM9000_DBG("NSR (0x01): %02x\n", DM9000_ior(1)); + DM9000_DBG("TCR (0x02): %02x\n", DM9000_ior(2)); + DM9000_DBG("TSRI (0x03): %02x\n", DM9000_ior(3)); + DM9000_DBG("TSRII (0x04): %02x\n", DM9000_ior(4)); + DM9000_DBG("RCR (0x05): %02x\n", DM9000_ior(5)); + DM9000_DBG("RSR (0x06): %02x\n", DM9000_ior(6)); + DM9000_DBG("ISR (0xFE): %02x\n", DM9000_ior(DM9000_ISR)); + DM9000_DBG("\n"); +} +#endif + +static void dm9000_outblk_8bit(volatile void *data_ptr, int count) +{ + int i; + for (i = 0; i < count; i++) + DM9000_outb((((u8 *) data_ptr)[i] & 0xff), DM9000_DATA); +} + +static void dm9000_outblk_16bit(volatile void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 1) / 2; + + for (i = 0; i < tmplen; i++) + DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA); +} +static void dm9000_outblk_32bit(volatile void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 3) / 4; + + for (i = 0; i < tmplen; i++) + DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA); +} + +static void dm9000_inblk_8bit(void *data_ptr, int count) +{ + int i; + for (i = 0; i < count; i++) + ((u8 *) data_ptr)[i] = DM9000_inb(DM9000_DATA); +} + +static void dm9000_inblk_16bit(void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 1) / 2; + + for (i = 0; i < tmplen; i++) + ((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA); +} +static void dm9000_inblk_32bit(void *data_ptr, int count) +{ + int i; + u32 tmplen = (count + 3) / 4; + + for (i = 0; i < tmplen; i++) + ((u32 *) data_ptr)[i] = DM9000_inl(DM9000_DATA); +} + +static void dm9000_rx_status_32bit(u16 *RxStatus, u16 *RxLen) +{ + u32 tmpdata; + + DM9000_outb(DM9000_MRCMD, DM9000_IO); + + tmpdata = DM9000_inl(DM9000_DATA); + *RxStatus = __le16_to_cpu(tmpdata); + *RxLen = __le16_to_cpu(tmpdata >> 16); +} + +static void dm9000_rx_status_16bit(u16 *RxStatus, u16 *RxLen) +{ + DM9000_outb(DM9000_MRCMD, DM9000_IO); + + *RxStatus = __le16_to_cpu(DM9000_inw(DM9000_DATA)); + *RxLen = __le16_to_cpu(DM9000_inw(DM9000_DATA)); +} + +static void dm9000_rx_status_8bit(u16 *RxStatus, u16 *RxLen) +{ + DM9000_outb(DM9000_MRCMD, DM9000_IO); + + *RxStatus = + __le16_to_cpu(DM9000_inb(DM9000_DATA) + + (DM9000_inb(DM9000_DATA) << 8)); + *RxLen = + __le16_to_cpu(DM9000_inb(DM9000_DATA) + + (DM9000_inb(DM9000_DATA) << 8)); +} + +/* + Search DM9000 board, allocate space and register it +*/ +int +dm9000_probe(void) +{ + u32 id_val; + id_val = DM9000_ior(DM9000_VIDL); + id_val |= DM9000_ior(DM9000_VIDH) << 8; + id_val |= DM9000_ior(DM9000_PIDL) << 16; + id_val |= DM9000_ior(DM9000_PIDH) << 24; + if (id_val == DM9000_ID) { + printf("dm9000 i/o: 0x%x, id: 0x%x \n", CONFIG_DM9000_BASE, + id_val); + return 0; + } else { + printf("dm9000 not found at 0x%08x id: 0x%08x\n", + CONFIG_DM9000_BASE, id_val); + return -1; + } +} + +/* General Purpose dm9000 reset routine */ +static void +dm9000_reset(void) +{ + DM9000_DBG("resetting DM9000\n"); + + /* Reset DM9000, + see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 */ + + /* DEBUG: Make all GPIO0 outputs, all others inputs */ + DM9000_iow(DM9000_GPCR, GPCR_GPIO0_OUT); + /* Step 1: Power internal PHY by writing 0 to GPIO0 pin */ + DM9000_iow(DM9000_GPR, 0); + /* Step 2: Software reset */ + DM9000_iow(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); + + do { + DM9000_DBG("resetting the DM9000, 1st reset\n"); + udelay(25); /* Wait at least 20 us */ + } while (DM9000_ior(DM9000_NCR) & 1); + + DM9000_iow(DM9000_NCR, 0); + DM9000_iow(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); /* Issue a second reset */ + + do { + DM9000_DBG("resetting the DM9000, 2nd reset\n"); + udelay(25); /* Wait at least 20 us */ + } while (DM9000_ior(DM9000_NCR) & 1); + + /* Check whether the ethernet controller is present */ + if ((DM9000_ior(DM9000_PIDL) != 0x0) || + (DM9000_ior(DM9000_PIDH) != 0x90)) + printf("ERROR: resetting DM9000 -> not responding\n"); +} + +/* Initialize dm9000 board +*/ +static int dm9000_init(struct eth_device *dev, bd_t *bd) +{ + int i, oft, lnk; + u8 io_mode; + struct board_info *db = &dm9000_info; + + DM9000_DBG("%s\n", __func__); + + /* RESET device */ + dm9000_reset(); + + if (dm9000_probe() < 0) + return -1; + + /* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */ + io_mode = DM9000_ior(DM9000_ISR) >> 6; + + switch (io_mode) { + case 0x0: /* 16-bit mode */ + printf("DM9000: running in 16 bit mode\n"); + db->outblk = dm9000_outblk_16bit; + db->inblk = dm9000_inblk_16bit; + db->rx_status = dm9000_rx_status_16bit; + break; + case 0x01: /* 32-bit mode */ + printf("DM9000: running in 32 bit mode\n"); + db->outblk = dm9000_outblk_32bit; + db->inblk = dm9000_inblk_32bit; + db->rx_status = dm9000_rx_status_32bit; + break; + case 0x02: /* 8 bit mode */ + printf("DM9000: running in 8 bit mode\n"); + db->outblk = dm9000_outblk_8bit; + db->inblk = dm9000_inblk_8bit; + db->rx_status = dm9000_rx_status_8bit; + break; + default: + /* Assume 8 bit mode, will probably not work anyway */ + printf("DM9000: Undefined IO-mode:0x%x\n", io_mode); + db->outblk = dm9000_outblk_8bit; + db->inblk = dm9000_inblk_8bit; + db->rx_status = dm9000_rx_status_8bit; + break; + } + + /* Program operating register, only internal phy supported */ + DM9000_iow(DM9000_NCR, 0x0); + /* TX Polling clear */ + DM9000_iow(DM9000_TCR, 0); + /* Less 3Kb, 200us */ + DM9000_iow(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US); + /* Flow Control : High/Low Water */ + DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8)); + /* SH FIXME: This looks strange! Flow Control */ + DM9000_iow(DM9000_FCR, 0x0); + /* Special Mode */ + DM9000_iow(DM9000_SMCR, 0); + /* clear TX status */ + DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END); + /* Clear interrupt status */ + DM9000_iow(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS); + + printf("MAC: %pM\n", dev->enetaddr); + + /* fill device MAC address registers */ + for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++) + DM9000_iow(oft, dev->enetaddr[i]); + for (i = 0, oft = 0x16; i < 8; i++, oft++) + DM9000_iow(oft, 0xff); + + /* read back mac, just to be sure */ + for (i = 0, oft = 0x10; i < 6; i++, oft++) + DM9000_DBG("%02x:", DM9000_ior(oft)); + DM9000_DBG("\n"); + + /* Activate DM9000 */ + /* RX enable */ + DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN); + /* Enable TX/RX interrupt mask */ + DM9000_iow(DM9000_IMR, IMR_PAR); + + i = 0; + while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */ + udelay(1000); + i++; + if (i == 10000) { + printf("could not establish link\n"); + return 0; + } + } + + /* see what we've got */ + lnk = phy_read(17) >> 12; + printf("operating at "); + switch (lnk) { + case 1: + printf("10M half duplex "); + break; + case 2: + printf("10M full duplex "); + break; + case 4: + printf("100M half duplex "); + break; + case 8: + printf("100M full duplex "); + break; + default: + printf("unknown: %d ", lnk); + break; + } + printf("mode\n"); + return 0; +} + +/* + Hardware start transmission. + Send a packet to media from the upper layer. +*/ +static int dm9000_send(struct eth_device *netdev, volatile void *packet, + int length) +{ + int tmo; + struct board_info *db = &dm9000_info; + + DM9000_DMP_PACKET(__func__ , packet, length); + + DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ + + /* Move data to DM9000 TX RAM */ + DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */ + + /* push the data to the TX-fifo */ + (db->outblk)(packet, length); + + /* Set TX length to DM9000 */ + DM9000_iow(DM9000_TXPLL, length & 0xff); + DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff); + + /* Issue TX polling command */ + DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */ + + /* wait for end of transmission */ + tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) || + !(DM9000_ior(DM9000_ISR) & IMR_PTM) ) { + if (get_timer(0) >= tmo) { + printf("transmission timeout\n"); + break; + } + } + DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */ + + DM9000_DBG("transmit done\n\n"); + return 0; +} + +/* + Stop the interface. + The interface is stopped when it is brought. +*/ +static void dm9000_halt(struct eth_device *netdev) +{ + DM9000_DBG("%s\n", __func__); + + /* RESET devie */ + phy_write(0, 0x8000); /* PHY RESET */ + DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */ + DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */ + DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */ +} + +/* + Received a packet and pass to upper layer +*/ +static int dm9000_rx(struct eth_device *netdev) +{ + u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0]; + u16 RxStatus, RxLen = 0; + struct board_info *db = &dm9000_info; + + /* Check packet ready or not, we must check + the ISR status first for DM9000A */ + if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */ + return 0; + + DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */ + + /* There is _at least_ 1 package in the fifo, read them all */ + for (;;) { + DM9000_ior(DM9000_MRCMDX); /* Dummy read */ + + /* Get most updated data, + only look at bits 0:1, See application notes DM9000 */ + rxbyte = DM9000_inb(DM9000_DATA) & 0x03; + + /* Status check: this byte must be 0 or 1 */ + if (rxbyte > DM9000_PKT_RDY) { + DM9000_iow(DM9000_RCR, 0x00); /* Stop Device */ + DM9000_iow(DM9000_ISR, 0x80); /* Stop INT request */ + printf("DM9000 error: status check fail: 0x%x\n", + rxbyte); + return 0; + } + + if (rxbyte != DM9000_PKT_RDY) + return 0; /* No packet received, ignore */ + + DM9000_DBG("receiving packet\n"); + + /* A packet ready now & Get status/length */ + (db->rx_status)(&RxStatus, &RxLen); + + DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen); + + /* Move data from DM9000 */ + /* Read received packet from RX SRAM */ + (db->inblk)(rdptr, RxLen); + + if ((RxStatus & 0xbf00) || (RxLen < 0x40) + || (RxLen > DM9000_PKT_MAX)) { + if (RxStatus & 0x100) { + printf("rx fifo error\n"); + } + if (RxStatus & 0x200) { + printf("rx crc error\n"); + } + if (RxStatus & 0x8000) { + printf("rx length error\n"); + } + if (RxLen > DM9000_PKT_MAX) { + printf("rx length too big\n"); + dm9000_reset(); + } + } else { + DM9000_DMP_PACKET(__func__ , rdptr, RxLen); + + DM9000_DBG("passing packet to upper layer\n"); + NetReceive(NetRxPackets[0], RxLen); + } + } + return 0; +} + +/* + Read a word data from SROM +*/ +#if !defined(CONFIG_DM9000_NO_SROM) +void dm9000_read_srom_word(int offset, u8 *to) +{ + DM9000_iow(DM9000_EPAR, offset); + DM9000_iow(DM9000_EPCR, 0x4); + udelay(8000); + DM9000_iow(DM9000_EPCR, 0x0); + to[0] = DM9000_ior(DM9000_EPDRL); + to[1] = DM9000_ior(DM9000_EPDRH); +} + +void dm9000_write_srom_word(int offset, u16 val) +{ + DM9000_iow(DM9000_EPAR, offset); + DM9000_iow(DM9000_EPDRH, ((val >> 8) & 0xff)); + DM9000_iow(DM9000_EPDRL, (val & 0xff)); + DM9000_iow(DM9000_EPCR, 0x12); + udelay(8000); + DM9000_iow(DM9000_EPCR, 0); +} +#endif + +static void dm9000_get_enetaddr(struct eth_device *dev) +{ +#if !defined(CONFIG_DM9000_NO_SROM) + int i; + for (i = 0; i < 3; i++) + dm9000_read_srom_word(i, dev->enetaddr + (2 * i)); +#endif +} + +/* + Read a byte from I/O port +*/ +static u8 +DM9000_ior(int reg) +{ + DM9000_outb(reg, DM9000_IO); + return DM9000_inb(DM9000_DATA); +} + +/* + Write a byte to I/O port +*/ +static void +DM9000_iow(int reg, u8 value) +{ + DM9000_outb(reg, DM9000_IO); + DM9000_outb(value, DM9000_DATA); +} + +/* + Read a word from phyxcer +*/ +static u16 +phy_read(int reg) +{ + u16 val; + + /* Fill the phyxcer register into REG_0C */ + DM9000_iow(DM9000_EPAR, DM9000_PHY | reg); + DM9000_iow(DM9000_EPCR, 0xc); /* Issue phyxcer read command */ + udelay(100); /* Wait read complete */ + DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer read command */ + val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL); + + /* The read data keeps on REG_0D & REG_0E */ + DM9000_DBG("phy_read(0x%x): 0x%x\n", reg, val); + return val; +} + +/* + Write a word to phyxcer +*/ +static void +phy_write(int reg, u16 value) +{ + + /* Fill the phyxcer register into REG_0C */ + DM9000_iow(DM9000_EPAR, DM9000_PHY | reg); + + /* Fill the written data into REG_0D & REG_0E */ + DM9000_iow(DM9000_EPDRL, (value & 0xff)); + DM9000_iow(DM9000_EPDRH, ((value >> 8) & 0xff)); + DM9000_iow(DM9000_EPCR, 0xa); /* Issue phyxcer write command */ + udelay(500); /* Wait write complete */ + DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer write command */ + DM9000_DBG("phy_write(reg:0x%x, value:0x%x)\n", reg, value); +} + +int dm9000_initialize(bd_t *bis) +{ + struct eth_device *dev = &(dm9000_info.netdev); + + /* Load MAC address from EEPROM */ + dm9000_get_enetaddr(dev); + + dev->init = dm9000_init; + dev->halt = dm9000_halt; + dev->send = dm9000_send; + dev->recv = dm9000_rx; + sprintf(dev->name, "dm9000"); + + eth_register(dev); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/dm9000x.h b/roms/u-boot-sam460ex/drivers/net/dm9000x.h new file mode 100644 index 000000000..0d123e2e1 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/dm9000x.h @@ -0,0 +1,140 @@ +/* + * dm9000 Ethernet + */ + +#ifdef CONFIG_DRIVER_DM9000 + +#define DM9000_ID 0x90000A46 +#define DM9000_PKT_MAX 1536 /* Received packet max size */ +#define DM9000_PKT_RDY 0x01 /* Packet ready to receive */ + +/* although the registers are 16 bit, they are 32-bit aligned. + */ + +#define DM9000_NCR 0x00 +#define DM9000_NSR 0x01 +#define DM9000_TCR 0x02 +#define DM9000_TSR1 0x03 +#define DM9000_TSR2 0x04 +#define DM9000_RCR 0x05 +#define DM9000_RSR 0x06 +#define DM9000_ROCR 0x07 +#define DM9000_BPTR 0x08 +#define DM9000_FCTR 0x09 +#define DM9000_FCR 0x0A +#define DM9000_EPCR 0x0B +#define DM9000_EPAR 0x0C +#define DM9000_EPDRL 0x0D +#define DM9000_EPDRH 0x0E +#define DM9000_WCR 0x0F + +#define DM9000_PAR 0x10 +#define DM9000_MAR 0x16 + +#define DM9000_GPCR 0x1e +#define DM9000_GPR 0x1f +#define DM9000_TRPAL 0x22 +#define DM9000_TRPAH 0x23 +#define DM9000_RWPAL 0x24 +#define DM9000_RWPAH 0x25 + +#define DM9000_VIDL 0x28 +#define DM9000_VIDH 0x29 +#define DM9000_PIDL 0x2A +#define DM9000_PIDH 0x2B + +#define DM9000_CHIPR 0x2C +#define DM9000_SMCR 0x2F + +#define DM9000_PHY 0x40 /* PHY address 0x01 */ + +#define DM9000_MRCMDX 0xF0 +#define DM9000_MRCMD 0xF2 +#define DM9000_MRRL 0xF4 +#define DM9000_MRRH 0xF5 +#define DM9000_MWCMDX 0xF6 +#define DM9000_MWCMD 0xF8 +#define DM9000_MWRL 0xFA +#define DM9000_MWRH 0xFB +#define DM9000_TXPLL 0xFC +#define DM9000_TXPLH 0xFD +#define DM9000_ISR 0xFE +#define DM9000_IMR 0xFF + +#define NCR_EXT_PHY (1<<7) +#define NCR_WAKEEN (1<<6) +#define NCR_FCOL (1<<4) +#define NCR_FDX (1<<3) +#define NCR_LBK (3<<1) +#define NCR_LBK_INT_MAC (1<<1) +#define NCR_LBK_INT_PHY (2<<1) +#define NCR_RST (1<<0) + +#define NSR_SPEED (1<<7) +#define NSR_LINKST (1<<6) +#define NSR_WAKEST (1<<5) +#define NSR_TX2END (1<<3) +#define NSR_TX1END (1<<2) +#define NSR_RXOV (1<<1) + +#define TCR_TJDIS (1<<6) +#define TCR_EXCECM (1<<5) +#define TCR_PAD_DIS2 (1<<4) +#define TCR_CRC_DIS2 (1<<3) +#define TCR_PAD_DIS1 (1<<2) +#define TCR_CRC_DIS1 (1<<1) +#define TCR_TXREQ (1<<0) + +#define TSR_TJTO (1<<7) +#define TSR_LC (1<<6) +#define TSR_NC (1<<5) +#define TSR_LCOL (1<<4) +#define TSR_COL (1<<3) +#define TSR_EC (1<<2) + +#define RCR_WTDIS (1<<6) +#define RCR_DIS_LONG (1<<5) +#define RCR_DIS_CRC (1<<4) +#define RCR_ALL (1<<3) +#define RCR_RUNT (1<<2) +#define RCR_PRMSC (1<<1) +#define RCR_RXEN (1<<0) + +#define RSR_RF (1<<7) +#define RSR_MF (1<<6) +#define RSR_LCS (1<<5) +#define RSR_RWTO (1<<4) +#define RSR_PLE (1<<3) +#define RSR_AE (1<<2) +#define RSR_CE (1<<1) +#define RSR_FOE (1<<0) + +#define EPCR_EPOS_PHY (1<<3) +#define EPCR_EPOS_EE (0<<3) +#define EPCR_ERPRR (1<<2) +#define EPCR_ERPRW (1<<1) +#define EPCR_ERRE (1<<0) + +#define FCTR_HWOT(ot) (( ot & 0xf ) << 4 ) +#define FCTR_LWOT(ot) ( ot & 0xf ) + +#define BPTR_BPHW(x) ((x) << 4) +#define BPTR_JPT_200US (0x07) +#define BPTR_JPT_600US (0x0f) + +#define IMR_PAR (1<<7) +#define IMR_ROOM (1<<3) +#define IMR_ROM (1<<2) +#define IMR_PTM (1<<1) +#define IMR_PRM (1<<0) + +#define ISR_ROOS (1<<3) +#define ISR_ROS (1<<2) +#define ISR_PTS (1<<1) +#define ISR_PRS (1<<0) + +#define GPCR_GPIO0_OUT (1<<0) + +#define GPR_PHY_PWROFF (1<<0) + +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/dnet.c b/roms/u-boot-sam460ex/drivers/net/dnet.c new file mode 100644 index 000000000..bfe87faa2 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/dnet.c @@ -0,0 +1,395 @@ +/* + * Dave Ethernet Controller driver + * + * Copyright (C) 2008 Dave S.r.l. <www.dave.eu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> + +#ifndef CONFIG_DNET_AUTONEG_TIMEOUT +#define CONFIG_DNET_AUTONEG_TIMEOUT 5000000 /* default value */ +#endif + +#include <net.h> +#include <malloc.h> +#include <linux/mii.h> + +#include <miiphy.h> +#include <asm/io.h> + +#include "dnet.h" + +struct dnet_device { + struct dnet_registers *regs; + const struct device *dev; + struct eth_device netdev; + unsigned short phy_addr; +}; + +/* get struct dnet_device from given struct netdev */ +#define to_dnet(_nd) container_of(_nd, struct dnet_device, netdev) + +/* function for reading internal MAC register */ +u16 dnet_readw_mac(struct dnet_device *dnet, u16 reg) +{ + u16 data_read; + + /* issue a read */ + writel(reg, &dnet->regs->MACREG_ADDR); + + /* since a read/write op to the MAC is very slow, + * we must wait before reading the data */ + udelay(1); + + /* read data read from the MAC register */ + data_read = readl(&dnet->regs->MACREG_DATA); + + /* all done */ + return data_read; +} + +/* function for writing internal MAC register */ +void dnet_writew_mac(struct dnet_device *dnet, u16 reg, u16 val) +{ + /* load data to write */ + writel(val, &dnet->regs->MACREG_DATA); + + /* issue a write */ + writel(reg | DNET_INTERNAL_WRITE, &dnet->regs->MACREG_ADDR); + + /* since a read/write op to the MAC is very slow, + * we must wait before exiting */ + udelay(1); +} + +static void dnet_mdio_write(struct dnet_device *dnet, u8 reg, u16 value) +{ + u16 tmp; + + debug(DRIVERNAME "dnet_mdio_write %02x:%02x <- %04x\n", + dnet->phy_addr, reg, value); + + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; + + /* prepare for a write operation */ + tmp = (1 << 13); + + /* only 5 bits allowed for register offset */ + reg &= 0x1f; + + /* prepare reg_value for a write */ + tmp |= (dnet->phy_addr << 8); + tmp |= reg; + + /* write data to write first */ + dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG, value); + + /* write control word */ + dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, tmp); + + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; +} + +static u16 dnet_mdio_read(struct dnet_device *dnet, u8 reg) +{ + u16 value; + + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; + + /* only 5 bits allowed for register offset*/ + reg &= 0x1f; + + /* prepare reg_value for a read */ + value = (dnet->phy_addr << 8); + value |= reg; + + /* write control word */ + dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, value); + + /* wait for end of transfer */ + while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) & + DNET_INTERNAL_GMII_MNG_CMD_FIN)) + ; + + value = dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG); + + debug(DRIVERNAME "dnet_mdio_read %02x:%02x <- %04x\n", + dnet->phy_addr, reg, value); + + return value; +} + +static int dnet_send(struct eth_device *netdev, volatile void *packet, + int length) +{ + struct dnet_device *dnet = to_dnet(netdev); + int i, len, wrsz; + unsigned int *bufp; + unsigned int tx_cmd; + + debug(DRIVERNAME "[%s] Sending %u bytes\n", __func__, length); + + /* frame size (words) */ + len = (length + 3) >> 2; + + bufp = (unsigned int *) (((u32)packet) & 0xFFFFFFFC); + wrsz = (u32)length + 3; + wrsz += ((u32)packet) & 0x3; + wrsz >>= 2; + tx_cmd = ((((unsigned int)(packet)) & 0x03) << 16) | (u32)length; + + /* check if there is enough room for the current frame */ + if (wrsz < (DNET_FIFO_SIZE - readl(&dnet->regs->TX_FIFO_WCNT))) { + for (i = 0; i < wrsz; i++) + writel(*bufp++, &dnet->regs->TX_DATA_FIFO); + /* + * inform MAC that a packet's written and ready + * to be shipped out + */ + writel(tx_cmd, &dnet->regs->TX_LEN_FIFO); + } else { + printf(DRIVERNAME "No free space (actual %d, required %d " + "(words))\n", DNET_FIFO_SIZE - + readl(&dnet->regs->TX_FIFO_WCNT), wrsz); + } + + /* No one cares anyway */ + return 0; +} + + +static int dnet_recv(struct eth_device *netdev) +{ + struct dnet_device *dnet = to_dnet(netdev); + unsigned int *data_ptr; + int pkt_len, poll, i; + u32 cmd_word; + + debug("Waiting for pkt (polling)\n"); + poll = 50; + while ((readl(&dnet->regs->RX_FIFO_WCNT) >> 16) == 0) { + udelay(10); /* wait 10 usec */ + if (--poll == 0) + return 0; /* no pkt available */ + } + + cmd_word = readl(&dnet->regs->RX_LEN_FIFO); + pkt_len = cmd_word & 0xFFFF; + + debug("Got pkt with size %d bytes\n", pkt_len); + + if (cmd_word & 0xDF180000) + printf("%s packet receive error %x\n", __func__, cmd_word); + + data_ptr = (unsigned int *) NetRxPackets[0]; + + for (i = 0; i < (pkt_len + 3) >> 2; i++) + *data_ptr++ = readl(&dnet->regs->RX_DATA_FIFO); + + NetReceive(NetRxPackets[0], pkt_len + 5); /* ok + 5 ?? */ + + return 0; +} + +static void dnet_set_hwaddr(struct eth_device *netdev) +{ + struct dnet_device *dnet = to_dnet(netdev); + u16 tmp; + + tmp = cpu_to_be16(*((u16 *)netdev->enetaddr)); + dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_0_REG, tmp); + tmp = cpu_to_be16(*((u16 *)(netdev->enetaddr + 2))); + dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_1_REG, tmp); + tmp = cpu_to_be16(*((u16 *)(netdev->enetaddr + 4))); + dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_2_REG, tmp); +} + +static void dnet_phy_reset(struct dnet_device *dnet) +{ + struct eth_device *netdev = &dnet->netdev; + int i; + u16 status, adv; + + adv = ADVERTISE_CSMA | ADVERTISE_ALL; + dnet_mdio_write(dnet, MII_ADVERTISE, adv); + printf("%s: Starting autonegotiation...\n", netdev->name); + dnet_mdio_write(dnet, MII_BMCR, (BMCR_ANENABLE + | BMCR_ANRESTART)); + + for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) { + status = dnet_mdio_read(dnet, MII_BMSR); + if (status & BMSR_ANEGCOMPLETE) + break; + udelay(100); + } + + if (status & BMSR_ANEGCOMPLETE) + printf("%s: Autonegotiation complete\n", netdev->name); + else + printf("%s: Autonegotiation timed out (status=0x%04x)\n", + netdev->name, status); +} + +static int dnet_phy_init(struct dnet_device *dnet) +{ + struct eth_device *netdev = &dnet->netdev; + u16 phy_id, status, adv, lpa; + int media, speed, duplex; + int i; + u32 ctl_reg; + + /* Find a PHY */ + for (i = 0; i < 32; i++) { + dnet->phy_addr = i; + phy_id = dnet_mdio_read(dnet, MII_PHYSID1); + if (phy_id != 0xffff) { + /* ok we found it */ + printf("Found PHY at address %d PHYID (%04x:%04x)\n", + i, phy_id, + dnet_mdio_read(dnet, MII_PHYSID2)); + break; + } + } + + /* Check if the PHY is up to snuff... */ + phy_id = dnet_mdio_read(dnet, MII_PHYSID1); + if (phy_id == 0xffff) { + printf("%s: No PHY present\n", netdev->name); + return -1; + } + + status = dnet_mdio_read(dnet, MII_BMSR); + if (!(status & BMSR_LSTATUS)) { + /* Try to re-negotiate if we don't have link already. */ + dnet_phy_reset(dnet); + + for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) { + status = dnet_mdio_read(dnet, MII_BMSR); + if (status & BMSR_LSTATUS) + break; + udelay(100); + } + } + + if (!(status & BMSR_LSTATUS)) { + printf("%s: link down (status: 0x%04x)\n", + netdev->name, status); + return -1; + } else { + adv = dnet_mdio_read(dnet, MII_ADVERTISE); + lpa = dnet_mdio_read(dnet, MII_LPA); + media = mii_nway_result(lpa & adv); + speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) + ? 1 : 0); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + /* 1000BaseT ethernet is not supported */ + printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n", + netdev->name, + speed ? "100" : "10", + duplex ? "full" : "half", + lpa); + + ctl_reg = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG); + + if (duplex) + ctl_reg &= ~(DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP); + else + ctl_reg |= DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP; + + dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, ctl_reg); + + return 0; + } +} + +static int dnet_init(struct eth_device *netdev, bd_t *bd) +{ + struct dnet_device *dnet = to_dnet(netdev); + u32 config; + + /* + * dnet_halt should have been called at some point before now, + * so we'll assume the controller is idle. + */ + + /* set hardware address */ + dnet_set_hwaddr(netdev); + + if (dnet_phy_init(dnet) < 0) + return -1; + + /* flush rx/tx fifos */ + writel(DNET_SYS_CTL_RXFIFOFLUSH | DNET_SYS_CTL_TXFIFOFLUSH, + &dnet->regs->SYS_CTL); + udelay(1000); + writel(0, &dnet->regs->SYS_CTL); + + config = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG); + + config |= DNET_INTERNAL_RXTX_CONTROL_RXPAUSE | + DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST | + DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL | + DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS; + + dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, config); + + /* Enable TX and RX */ + dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, + DNET_INTERNAL_MODE_RXEN | DNET_INTERNAL_MODE_TXEN); + + return 0; +} + +static void dnet_halt(struct eth_device *netdev) +{ + struct dnet_device *dnet = to_dnet(netdev); + + /* Disable TX and RX */ + dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, 0); +} + +int dnet_eth_initialize(int id, void *regs, unsigned int phy_addr) +{ + struct dnet_device *dnet; + struct eth_device *netdev; + unsigned int dev_capa; + + dnet = malloc(sizeof(struct dnet_device)); + if (!dnet) { + printf("Error: Failed to allocate memory for DNET%d\n", id); + return -1; + } + memset(dnet, 0, sizeof(struct dnet_device)); + + netdev = &dnet->netdev; + + dnet->regs = (struct dnet_registers *)regs; + dnet->phy_addr = phy_addr; + + sprintf(netdev->name, "dnet%d", id); + netdev->init = dnet_init; + netdev->halt = dnet_halt; + netdev->send = dnet_send; + netdev->recv = dnet_recv; + + dev_capa = readl(&dnet->regs->VERCAPS) & 0xFFFF; + debug("%s: has %smdio, %sirq, %sgigabit, %sdma \n", netdev->name, + (dev_capa & DNET_HAS_MDIO) ? "" : "no ", + (dev_capa & DNET_HAS_IRQ) ? "" : "no ", + (dev_capa & DNET_HAS_GIGABIT) ? "" : "no ", + (dev_capa & DNET_HAS_DMA) ? "" : "no "); + + eth_register(netdev); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/dnet.h b/roms/u-boot-sam460ex/drivers/net/dnet.h new file mode 100644 index 000000000..fdb4fd2d3 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/dnet.h @@ -0,0 +1,166 @@ +/* + * Dave Ethernet Controller driver + * + * Copyright (C) 2008 Dave S.r.l. <www.dave.eu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_DNET_H__ +#define __DRIVERS_DNET_H__ + +#define DRIVERNAME "dnet" + +struct dnet_registers { + /* ALL DNET FIFO REGISTERS */ + u32 RX_LEN_FIFO; + u32 RX_DATA_FIFO; + u32 TX_LEN_FIFO; + u32 TX_DATA_FIFO; + u32 pad1[0x3c]; + /* ALL DNET CONTROL/STATUS REGISTERS */ + u32 VERCAPS; + u32 INTR_SRC; + u32 INTR_ENB; + u32 RX_STATUS; + u32 TX_STATUS; + u32 RX_FRAMES_CNT; + u32 TX_FRAMES_CNT; + u32 RX_FIFO_TH; + u32 TX_FIFO_TH; + u32 SYS_CTL; + u32 PAUSE_TMR; + u32 RX_FIFO_WCNT; + u32 TX_FIFO_WCNT; + u32 pad2[0x33]; + /* ALL DNET MAC REGISTERS */ + u32 MACREG_DATA; /* Mac-Reg Data */ + u32 MACREG_ADDR; /* Mac-Reg Addr */ + u32 pad3[0x3e]; + /* ALL DNET RX STATISTICS COUNTERS */ + u32 RX_PKT_IGNR_CNT; + u32 RX_LEN_CHK_ERR_CNT; + u32 RX_LNG_FRM_CNT; + u32 RX_SHRT_FRM_CNT; + u32 RX_IPG_VIOL_CNT; + u32 RX_CRC_ERR_CNT; + u32 RX_OK_PKT_CNT; + u32 RX_CTL_FRM_CNT; + u32 RX_PAUSE_FRM_CNT; + u32 RX_MULTICAST_CNT; + u32 RX_BROADCAST_CNT; + u32 RX_VLAN_TAG_CNT; + u32 RX_PRE_SHRINK_CNT; + u32 RX_DRIB_NIB_CNT; + u32 RX_UNSUP_OPCD_CNT; + u32 RX_BYTE_CNT; + u32 pad4[0x30]; + /* DNET TX STATISTICS COUNTERS */ + u32 TX_UNICAST_CNT; + u32 TX_PAUSE_FRM_CNT; + u32 TX_MULTICAST_CNT; + u32 TX_BRDCAST_CNT; + u32 TX_VLAN_TAG_CNT; + u32 TX_BAD_FCS_CNT; + u32 TX_JUMBO_CNT; + u32 TX_BYTE_CNT; +}; + +/* SOME INTERNAL MAC-CORE REGISTER */ +#define DNET_INTERNAL_MODE_REG 0x0 +#define DNET_INTERNAL_RXTX_CONTROL_REG 0x2 +#define DNET_INTERNAL_MAX_PKT_SIZE_REG 0x4 +#define DNET_INTERNAL_IGP_REG 0x8 +#define DNET_INTERNAL_MAC_ADDR_0_REG 0xa +#define DNET_INTERNAL_MAC_ADDR_1_REG 0xc +#define DNET_INTERNAL_MAC_ADDR_2_REG 0xe +#define DNET_INTERNAL_TX_RX_STS_REG 0x12 +#define DNET_INTERNAL_GMII_MNG_CTL_REG 0x14 +#define DNET_INTERNAL_GMII_MNG_DAT_REG 0x16 + +#define DNET_INTERNAL_GMII_MNG_CMD_FIN (1 << 14) + +#define DNET_INTERNAL_WRITE (1 << 31) + +/* MAC-CORE REGISTER FIELDS */ + +/* MAC-CORE MODE REGISTER FIELDS */ +#define DNET_INTERNAL_MODE_GBITEN (1 << 0) +#define DNET_INTERNAL_MODE_FCEN (1 << 1) +#define DNET_INTERNAL_MODE_RXEN (1 << 2) +#define DNET_INTERNAL_MODE_TXEN (1 << 3) + +/* MAC-CORE RXTX CONTROL REGISTER FIELDS */ +#define DNET_INTERNAL_RXTX_CONTROL_RXSHORTFRAME (1 << 8) +#define DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST (1 << 7) +#define DNET_INTERNAL_RXTX_CONTROL_RXMULTICAST (1 << 4) +#define DNET_INTERNAL_RXTX_CONTROL_RXPAUSE (1 << 3) +#define DNET_INTERNAL_RXTX_CONTROL_DISTXFCS (1 << 2) +#define DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS (1 << 1) +#define DNET_INTERNAL_RXTX_CONTROL_ENPROMISC (1 << 0) +#define DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL (1 << 6) +#define DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP (1 << 5) + +/* SYSTEM CONTROL REGISTER FIELDS */ +#define DNET_SYS_CTL_IGNORENEXTPKT (1 << 0) +#define DNET_SYS_CTL_SENDPAUSE (1 << 2) +#define DNET_SYS_CTL_RXFIFOFLUSH (1 << 3) +#define DNET_SYS_CTL_TXFIFOFLUSH (1 << 4) + +/* TX STATUS REGISTER FIELDS */ +#define DNET_TX_STATUS_FIFO_ALMOST_EMPTY (1 << 2) +#define DNET_TX_STATUS_FIFO_ALMOST_FULL (1 << 1) + +/* INTERRUPT SOURCE REGISTER FIELDS */ +#define DNET_INTR_SRC_TX_PKTSENT (1 << 0) +#define DNET_INTR_SRC_TX_FIFOAF (1 << 1) +#define DNET_INTR_SRC_TX_FIFOAE (1 << 2) +#define DNET_INTR_SRC_TX_DISCFRM (1 << 3) +#define DNET_INTR_SRC_TX_FIFOFULL (1 << 4) +#define DNET_INTR_SRC_RX_CMDFIFOAF (1 << 8) +#define DNET_INTR_SRC_RX_CMDFIFOFF (1 << 9) +#define DNET_INTR_SRC_RX_DATAFIFOFF (1 << 10) +#define DNET_INTR_SRC_TX_SUMMARY (1 << 16) +#define DNET_INTR_SRC_RX_SUMMARY (1 << 17) +#define DNET_INTR_SRC_PHY (1 << 19) + +/* INTERRUPT ENABLE REGISTER FIELDS */ +#define DNET_INTR_ENB_TX_PKTSENT (1 << 0) +#define DNET_INTR_ENB_TX_FIFOAF (1 << 1) +#define DNET_INTR_ENB_TX_FIFOAE (1 << 2) +#define DNET_INTR_ENB_TX_DISCFRM (1 << 3) +#define DNET_INTR_ENB_TX_FIFOFULL (1 << 4) +#define DNET_INTR_ENB_RX_PKTRDY (1 << 8) +#define DNET_INTR_ENB_RX_FIFOAF (1 << 9) +#define DNET_INTR_ENB_RX_FIFOERR (1 << 10) +#define DNET_INTR_ENB_RX_ERROR (1 << 11) +#define DNET_INTR_ENB_RX_FIFOFULL (1 << 12) +#define DNET_INTR_ENB_RX_FIFOAE (1 << 13) +#define DNET_INTR_ENB_TX_SUMMARY (1 << 16) +#define DNET_INTR_ENB_RX_SUMMARY (1 << 17) +#define DNET_INTR_ENB_GLOBAL_ENABLE (1 << 18) + +/* + * Capabilities. Used by the driver to know the capabilities that + * the ethernet controller inside the FPGA have. + */ + +#define DNET_HAS_MDIO (1 << 0) +#define DNET_HAS_IRQ (1 << 1) +#define DNET_HAS_GIGABIT (1 << 2) +#define DNET_HAS_DMA (1 << 3) + +#define DNET_HAS_MII (1 << 4) /* or GMII */ +#define DNET_HAS_RMII (1 << 5) /* or RGMII */ + +#define DNET_CAPS_MASK 0xFFFF + +#define DNET_FIFO_SIZE 2048 /* 2K x 32 bit */ +#define DNET_FIFO_TX_DATA_AF_TH (DNET_FIFO_SIZE - 384) /* 384 = 1536 / 4 */ +#define DNET_FIFO_TX_DATA_AE_TH (384) + +#define DNET_FIFO_RX_CMD_AF_TH (1 << 16) /* just one frame inside the FIFO */ + +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/e1000.c b/roms/u-boot-sam460ex/drivers/net/e1000.c new file mode 100644 index 000000000..2825342ff --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/e1000.c @@ -0,0 +1,5238 @@ +/************************************************************************** +Intel Pro 1000 for ppcboot/das-u-boot +Drivers are port from Intel's Linux driver e1000-4.3.15 +and from Etherboot pro 1000 driver by mrakes at vivato dot net +tested on both gig copper and gig fiber boards +***************************************************************************/ +/******************************************************************************* + + + Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Linux NICS <linux.nics@intel.com> + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ +/* + * Copyright (C) Archway Digital Solutions. + * + * written by Chrsitopher Li <cli at arcyway dot com> or <chrisl at gnuchina dot org> + * 2/9/2002 + * + * Copyright (C) Linux Networx. + * Massive upgrade to work with the new intel gigabit NICs. + * <ebiederman at lnxi dot com> + */ + +#include "e1000.h" + +#define TOUT_LOOP 100000 + +#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v)) +#define bus_to_phys(devno, a) pci_mem_to_phys(devno, a) +#define mdelay(n) udelay((n)*1000) + +#define E1000_DEFAULT_PCI_PBA 0x00000030 +#define E1000_DEFAULT_PCIE_PBA 0x000a0026 + +/* NIC specific static variables go here */ + +static char tx_pool[128 + 16]; +static char rx_pool[128 + 16]; +static char packet[2096]; + +static struct e1000_tx_desc *tx_base; +static struct e1000_rx_desc *rx_base; + +static int tx_tail; +static int rx_tail, rx_last; + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82542}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82543GC_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82543GC_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544EI_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544EI_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544GC_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82544GC_LOM}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82540EM}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82545EM_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82545GM_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546EB_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82545EM_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546EB_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546GB_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82540EM_LOM}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82541ER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82541GI_LF}, + /* E1000 PCIe card */ + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_FIBER }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_SERDES }, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_QUAD_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571PT_QUAD_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_QUAD_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_QUAD_COPPER_LOWPROFILE}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_SERDES_DUAL}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82571EB_SERDES_QUAD}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI_FIBER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI_SERDES}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82572EI}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82573E}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82573E_IAMT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82573L}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82546GB_QUAD_COPPER_KSP3}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_COPPER_DPT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_SERDES_DPT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_COPPER_SPT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_SERDES_SPT}, + {} +}; + +/* Function forward declarations */ +static int e1000_setup_link(struct eth_device *nic); +static int e1000_setup_fiber_link(struct eth_device *nic); +static int e1000_setup_copper_link(struct eth_device *nic); +static int e1000_phy_setup_autoneg(struct e1000_hw *hw); +static void e1000_config_collision_dist(struct e1000_hw *hw); +static int e1000_config_mac_to_phy(struct e1000_hw *hw); +static int e1000_config_fc_after_link_up(struct e1000_hw *hw); +static int e1000_check_for_link(struct eth_device *nic); +static int e1000_wait_autoneg(struct e1000_hw *hw); +static int e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, + uint16_t * duplex); +static int e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t * phy_data); +static int e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t phy_data); +static int32_t e1000_phy_hw_reset(struct e1000_hw *hw); +static int e1000_phy_reset(struct e1000_hw *hw); +static int e1000_detect_gig_phy(struct e1000_hw *hw); +static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw); +static void e1000_set_media_type(struct e1000_hw *hw); + +static int32_t e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask); +static int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); +#define E1000_WRITE_REG(a, reg, value) (writel((value), ((a)->hw_addr + E1000_##reg))) +#define E1000_READ_REG(a, reg) (readl((a)->hw_addr + E1000_##reg)) +#define E1000_WRITE_REG_ARRAY(a, reg, offset, value) (\ + writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2)))) +#define E1000_READ_REG_ARRAY(a, reg, offset) ( \ + readl((a)->hw_addr + E1000_##reg + ((offset) << 2))) +#define E1000_WRITE_FLUSH(a) {uint32_t x; x = E1000_READ_REG(a, STATUS);} + +#ifndef CONFIG_AP1000 /* remove for warnings */ +static int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset, + uint16_t words, + uint16_t *data); +/****************************************************************************** + * Raises the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +static void +e1000_raise_ee_clk(struct e1000_hw *hw, uint32_t * eecd) +{ + /* Raise the clock input to the EEPROM (by setting the SK bit), and then + * wait 50 microseconds. + */ + *eecd = *eecd | E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + udelay(50); +} + +/****************************************************************************** + * Lowers the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +static void +e1000_lower_ee_clk(struct e1000_hw *hw, uint32_t * eecd) +{ + /* Lower the clock input to the EEPROM (by clearing the SK bit), and then + * wait 50 microseconds. + */ + *eecd = *eecd & ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + udelay(50); +} + +/****************************************************************************** + * Shift data bits out to the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * data - data to send to the EEPROM + * count - number of bits to shift out + *****************************************************************************/ +static void +e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data, uint16_t count) +{ + uint32_t eecd; + uint32_t mask; + + /* We need to shift "count" bits out to the EEPROM. So, value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + */ + mask = 0x01 << (count - 1); + eecd = E1000_READ_REG(hw, EECD); + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + do { + /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1", + * and then raising and then lowering the clock (the SK bit controls + * the clock input to the EEPROM). A "0" is shifted out to the EEPROM + * by setting "DI" to "0" and then raising and then lowering the clock. + */ + eecd &= ~E1000_EECD_DI; + + if (data & mask) + eecd |= E1000_EECD_DI; + + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + + udelay(50); + + e1000_raise_ee_clk(hw, &eecd); + e1000_lower_ee_clk(hw, &eecd); + + mask = mask >> 1; + + } while (mask); + + /* We leave the "DI" bit set to "0" when we leave this routine. */ + eecd &= ~E1000_EECD_DI; + E1000_WRITE_REG(hw, EECD, eecd); +} + +/****************************************************************************** + * Shift data bits in from the EEPROM + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static uint16_t +e1000_shift_in_ee_bits(struct e1000_hw *hw, uint16_t count) +{ + uint32_t eecd; + uint32_t i; + uint16_t data; + + /* In order to read a register from the EEPROM, we need to shift 'count' + * bits in from the EEPROM. Bits are "shifted in" by raising the clock + * input to the EEPROM (setting the SK bit), and then reading the + * value of the "DO" bit. During this "shifting in" process the + * "DI" bit should always be clear. + */ + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for (i = 0; i < count; i++) { + data = data << 1; + e1000_raise_ee_clk(hw, &eecd); + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DI); + if (eecd & E1000_EECD_DO) + data |= 1; + + e1000_lower_ee_clk(hw, &eecd); + } + + return data; +} + +/****************************************************************************** + * Returns EEPROM to a "standby" state + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_standby_eeprom(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + + eecd = E1000_READ_REG(hw, EECD); + + if (eeprom->type == e1000_eeprom_microwire) { + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Clock high */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Select EEPROM */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Clock low */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + eecd &= ~E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + } +} + +/*************************************************************************** +* Description: Determines if the onboard NVM is FLASH or EEPROM. +* +* hw - Struct containing variables accessed by shared code +****************************************************************************/ +static boolean_t e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) +{ + uint32_t eecd = 0; + + DEBUGFUNC(); + + if (hw->mac_type == e1000_ich8lan) + return FALSE; + + if (hw->mac_type == e1000_82573) { + eecd = E1000_READ_REG(hw, EECD); + + /* Isolate bits 15 & 16 */ + eecd = ((eecd >> 15) & 0x03); + + /* If both bits are set, device is Flash type */ + if (eecd == 0x03) + return FALSE; + } + return TRUE; +} + +/****************************************************************************** + * Prepares EEPROM for access + * + * hw - Struct containing variables accessed by shared code + * + * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This + * function should be called before issuing a command to the EEPROM. + *****************************************************************************/ +static int32_t +e1000_acquire_eeprom(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd, i = 0; + + DEBUGFUNC(); + + if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM)) + return -E1000_ERR_SWFW_SYNC; + eecd = E1000_READ_REG(hw, EECD); + + if (hw->mac_type != e1000_82573) { + /* Request EEPROM Access */ + if (hw->mac_type > e1000_82544) { + eecd |= E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + eecd = E1000_READ_REG(hw, EECD); + while ((!(eecd & E1000_EECD_GNT)) && + (i < E1000_EEPROM_GRANT_ATTEMPTS)) { + i++; + udelay(5); + eecd = E1000_READ_REG(hw, EECD); + } + if (!(eecd & E1000_EECD_GNT)) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + DEBUGOUT("Could not acquire EEPROM grant\n"); + return -E1000_ERR_EEPROM; + } + } + } + + /* Setup EEPROM for Read/Write */ + + if (eeprom->type == e1000_eeprom_microwire) { + /* Clear SK and DI */ + eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + + /* Set CS */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + udelay(1); + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Sets up eeprom variables in the hw struct. Must be called after mac_type + * is configured. Additionally, if this is ICH8, the flash controller GbE + * registers must be mapped, or this will crash. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t e1000_init_eeprom_params(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd = E1000_READ_REG(hw, EECD); + int32_t ret_val = E1000_SUCCESS; + uint16_t eeprom_size; + + DEBUGFUNC(); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + eeprom->type = e1000_eeprom_microwire; + eeprom->word_size = 64; + eeprom->opcode_bits = 3; + eeprom->address_bits = 6; + eeprom->delay_usec = 50; + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_SIZE) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if (eecd & E1000_EECD_TYPE) { + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + } else { + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82571: + case e1000_82572: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82573: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = TRUE; + eeprom->use_eewr = TRUE; + if (e1000_is_onboard_nvm_eeprom(hw) == FALSE) { + eeprom->type = e1000_eeprom_flash; + eeprom->word_size = 2048; + + /* Ensure that the Autonomous FLASH update bit is cleared due to + * Flash update issue on parts which use a FLASH for NVM. */ + eecd &= ~E1000_EECD_AUPDEN; + E1000_WRITE_REG(hw, EECD, eecd); + } + break; + case e1000_80003es2lan: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = TRUE; + eeprom->use_eewr = FALSE; + break; + + /* ich8lan does not support currently. if needed, please + * add corresponding code and functions. + */ +#if 0 + case e1000_ich8lan: + { + int32_t i = 0; + + eeprom->type = e1000_eeprom_ich8; + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + eeprom->word_size = E1000_SHADOW_RAM_WORDS; + uint32_t flash_size = E1000_READ_ICH_FLASH_REG(hw, + ICH_FLASH_GFPREG); + /* Zero the shadow RAM structure. But don't load it from NVM + * so as to save time for driver init */ + if (hw->eeprom_shadow_ram != NULL) { + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + hw->eeprom_shadow_ram[i].modified = FALSE; + hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; + } + } + + hw->flash_base_addr = (flash_size & ICH_GFPREG_BASE_MASK) * + ICH_FLASH_SECTOR_SIZE; + + hw->flash_bank_size = ((flash_size >> 16) + & ICH_GFPREG_BASE_MASK) + 1; + hw->flash_bank_size -= (flash_size & ICH_GFPREG_BASE_MASK); + + hw->flash_bank_size *= ICH_FLASH_SECTOR_SIZE; + + hw->flash_bank_size /= 2 * sizeof(uint16_t); + break; + } +#endif + default: + break; + } + + if (eeprom->type == e1000_eeprom_spi) { + /* eeprom_size will be an enum [0..8] that maps + * to eeprom sizes 128B to + * 32KB (incremented by powers of 2). + */ + if (hw->mac_type <= e1000_82547_rev_2) { + /* Set to default value for initial eeprom read. */ + eeprom->word_size = 64; + ret_val = e1000_read_eeprom(hw, EEPROM_CFG, 1, + &eeprom_size); + if (ret_val) + return ret_val; + eeprom_size = (eeprom_size & EEPROM_SIZE_MASK) + >> EEPROM_SIZE_SHIFT; + /* 256B eeprom size was not supported in earlier + * hardware, so we bump eeprom_size up one to + * ensure that "1" (which maps to 256B) is never + * the result used in the shifting logic below. */ + if (eeprom_size) + eeprom_size++; + } else { + eeprom_size = (uint16_t)((eecd & + E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + } + + eeprom->word_size = 1 << (eeprom_size + EEPROM_WORD_SIZE_SHIFT); + } + return ret_val; +} + +/****************************************************************************** + * Polls the status bit (bit 1) of the EERD to determine when the read is done. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) +{ + uint32_t attempts = 100000; + uint32_t i, reg = 0; + int32_t done = E1000_ERR_EEPROM; + + for (i = 0; i < attempts; i++) { + if (eerd == E1000_EEPROM_POLL_READ) + reg = E1000_READ_REG(hw, EERD); + else + reg = E1000_READ_REG(hw, EEWR); + + if (reg & E1000_EEPROM_RW_REG_DONE) { + done = E1000_SUCCESS; + break; + } + udelay(5); + } + + return done; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM using the EERD register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +static int32_t +e1000_read_eeprom_eerd(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + uint32_t i, eerd = 0; + int32_t error = 0; + + for (i = 0; i < words; i++) { + eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) + + E1000_EEPROM_RW_REG_START; + + E1000_WRITE_REG(hw, EERD, eerd); + error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ); + + if (error) + break; + data[i] = (E1000_READ_REG(hw, EERD) >> + E1000_EEPROM_RW_REG_DATA); + + } + + return error; +} + +static void +e1000_release_eeprom(struct e1000_hw *hw) +{ + uint32_t eecd; + + DEBUGFUNC(); + + eecd = E1000_READ_REG(hw, EECD); + + if (hw->eeprom.type == e1000_eeprom_spi) { + eecd |= E1000_EECD_CS; /* Pull CS high */ + eecd &= ~E1000_EECD_SK; /* Lower SCK */ + + E1000_WRITE_REG(hw, EECD, eecd); + + udelay(hw->eeprom.delay_usec); + } else if (hw->eeprom.type == e1000_eeprom_microwire) { + /* cleanup eeprom */ + + /* CS on Microwire is active-high */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); + + E1000_WRITE_REG(hw, EECD, eecd); + + /* Rising edge of clock */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); + + /* Falling edge of clock */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); + } + + /* Stop requesting EEPROM access */ + if (hw->mac_type > e1000_82544) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + } +} +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_spi_eeprom_ready(struct e1000_hw *hw) +{ + uint16_t retry_count = 0; + uint8_t spi_stat_reg; + + DEBUGFUNC(); + + /* Read "Status Register" repeatedly until the LSB is cleared. The + * EEPROM will signal that the command has been completed by clearing + * bit 0 of the internal status register. If it's not cleared within + * 5 milliseconds, then error out. + */ + retry_count = 0; + do { + e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI, + hw->eeprom.opcode_bits); + spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8); + if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI)) + break; + + udelay(5); + retry_count += 5; + + e1000_standby_eeprom(hw); + } while (retry_count < EEPROM_MAX_RETRY_SPI); + + /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and + * only 0-5mSec on 5V devices) + */ + if (retry_count >= EEPROM_MAX_RETRY_SPI) { + DEBUGOUT("SPI EEPROM Status error\n"); + return -E1000_ERR_EEPROM; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + *****************************************************************************/ +static int32_t +e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset, + uint16_t words, uint16_t *data) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t i = 0; + + DEBUGFUNC(); + + /* If eeprom is not yet detected, do so now */ + if (eeprom->word_size == 0) + e1000_init_eeprom_params(hw); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. + */ + if ((offset >= eeprom->word_size) || + (words > eeprom->word_size - offset) || + (words == 0)) { + DEBUGOUT("\"words\" parameter out of bounds." + "Words = %d, size = %d\n", offset, eeprom->word_size); + return -E1000_ERR_EEPROM; + } + + /* EEPROM's that don't use EERD to read require us to bit-bang the SPI + * directly. In this case, we need to acquire the EEPROM so that + * FW or other port software does not interrupt. + */ + if (e1000_is_onboard_nvm_eeprom(hw) == TRUE && + hw->eeprom.use_eerd == FALSE) { + + /* Prepare the EEPROM for bit-bang reading */ + if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + } + + /* Eerd register EEPROM access requires no eeprom aquire/release */ + if (eeprom->use_eerd == TRUE) + return e1000_read_eeprom_eerd(hw, offset, words, data); + + /* ich8lan does not support currently. if needed, please + * add corresponding code and functions. + */ +#if 0 + /* ICH EEPROM access is done via the ICH flash controller */ + if (eeprom->type == e1000_eeprom_ich8) + return e1000_read_eeprom_ich8(hw, offset, words, data); +#endif + /* Set up the SPI or Microwire EEPROM for bit-bang reading. We have + * acquired the EEPROM at this point, so any returns should relase it */ + if (eeprom->type == e1000_eeprom_spi) { + uint16_t word_in; + uint8_t read_opcode = EEPROM_READ_OPCODE_SPI; + + if (e1000_spi_eeprom_ready(hw)) { + e1000_release_eeprom(hw); + return -E1000_ERR_EEPROM; + } + + e1000_standby_eeprom(hw); + + /* Some SPI eeproms use the 8th address bit embedded in + * the opcode */ + if ((eeprom->address_bits == 8) && (offset >= 128)) + read_opcode |= EEPROM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), + eeprom->address_bits); + + /* Read the data. The address of the eeprom internally + * increments with each byte (spi) being read, saving on the + * overhead of eeprom setup and tear-down. The address + * counter will roll over if reading beyond the size of + * the eeprom, thus allowing the entire memory to be read + * starting from any offset. */ + for (i = 0; i < words; i++) { + word_in = e1000_shift_in_ee_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + } else if (eeprom->type == e1000_eeprom_microwire) { + for (i = 0; i < words; i++) { + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, + EEPROM_READ_OPCODE_MICROWIRE, + eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i), + eeprom->address_bits); + + /* Read the data. For microwire, each word requires + * the overhead of eeprom setup and tear-down. */ + data[i] = e1000_shift_in_ee_bits(hw, 16); + e1000_standby_eeprom(hw); + } + } + + /* End this read operation */ + e1000_release_eeprom(hw); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Verifies that the EEPROM has a valid checksum + * + * hw - Struct containing variables accessed by shared code + * + * Reads the first 64 16 bit words of the EEPROM and sums the values read. + * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is + * valid. + *****************************************************************************/ +static int +e1000_validate_eeprom_checksum(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint16_t checksum = 0; + uint16_t i, eeprom_data; + + DEBUGFUNC(); + + for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { + if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + checksum += eeprom_data; + } + + if (checksum == (uint16_t) EEPROM_SUM) { + return 0; + } else { + DEBUGOUT("EEPROM Checksum Invalid\n"); + return -E1000_ERR_EEPROM; + } +} + +/***************************************************************************** + * Set PHY to class A mode + * Assumes the following operations will follow to enable the new class mode. + * 1. Do a PHY soft reset + * 2. Restart auto-negotiation or force link. + * + * hw - Struct containing variables accessed by shared code + ****************************************************************************/ +static int32_t +e1000_set_phy_mode(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC(); + + if ((hw->mac_type == e1000_82545_rev_3) && + (hw->media_type == e1000_media_type_copper)) { + ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, + 1, &eeprom_data); + if (ret_val) + return ret_val; + + if ((eeprom_data != EEPROM_RESERVED_WORD) && + (eeprom_data & EEPROM_PHY_CLASS_A)) { + ret_val = e1000_write_phy_reg(hw, + M88E1000_PHY_PAGE_SELECT, 0x000B); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, + M88E1000_PHY_GEN_CONTROL, 0x8104); + if (ret_val) + return ret_val; + + hw->phy_reset_disable = FALSE; + } + } + + return E1000_SUCCESS; +} +#endif /* #ifndef CONFIG_AP1000 */ + +/*************************************************************************** + * + * Obtaining software semaphore bit (SMBI) before resetting PHY. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to obtain semaphore. + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_software_semaphore(struct e1000_hw *hw) +{ + int32_t timeout = hw->eeprom.word_size + 1; + uint32_t swsm; + + DEBUGFUNC(); + + if (hw->mac_type != e1000_80003es2lan) + return E1000_SUCCESS; + + while (timeout) { + swsm = E1000_READ_REG(hw, SWSM); + /* If SMBI bit cleared, it is now set and we hold + * the semaphore */ + if (!(swsm & E1000_SWSM_SMBI)) + break; + mdelay(1); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + return -E1000_ERR_RESET; + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * This function clears HW semaphore bits. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - None. + * + ***************************************************************************/ +static void +e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) +{ + uint32_t swsm; + + DEBUGFUNC(); + + if (!hw->eeprom_semaphore_present) + return; + + swsm = E1000_READ_REG(hw, SWSM); + if (hw->mac_type == e1000_80003es2lan) { + /* Release both semaphores. */ + swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); + } else + swsm &= ~(E1000_SWSM_SWESMBI); + E1000_WRITE_REG(hw, SWSM, swsm); +} + +/*************************************************************************** + * + * Using the combination of SMBI and SWESMBI semaphore bits when resetting + * adapter or Eeprom access. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_EEPROM if fail to access EEPROM. + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) +{ + int32_t timeout; + uint32_t swsm; + + DEBUGFUNC(); + + if (!hw->eeprom_semaphore_present) + return E1000_SUCCESS; + + if (hw->mac_type == e1000_80003es2lan) { + /* Get the SW semaphore. */ + if (e1000_get_software_semaphore(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + } + + /* Get the FW semaphore. */ + timeout = hw->eeprom.word_size + 1; + while (timeout) { + swsm = E1000_READ_REG(hw, SWSM); + swsm |= E1000_SWSM_SWESMBI; + E1000_WRITE_REG(hw, SWSM, swsm); + /* if we managed to set the bit we got the semaphore. */ + swsm = E1000_READ_REG(hw, SWSM); + if (swsm & E1000_SWSM_SWESMBI) + break; + + udelay(50); + timeout--; + } + + if (!timeout) { + /* Release semaphores */ + e1000_put_hw_eeprom_semaphore(hw); + DEBUGOUT("Driver can't access the Eeprom - " + "SWESMBI bit is set.\n"); + return -E1000_ERR_EEPROM; + } + + return E1000_SUCCESS; +} + +static int32_t +e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask) +{ + uint32_t swfw_sync = 0; + uint32_t swmask = mask; + uint32_t fwmask = mask << 16; + int32_t timeout = 200; + + DEBUGFUNC(); + while (timeout) { + if (e1000_get_hw_eeprom_semaphore(hw)) + return -E1000_ERR_SWFW_SYNC; + + swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* firmware currently using resource (fwmask) */ + /* or other software thread currently using resource (swmask) */ + e1000_put_hw_eeprom_semaphore(hw); + mdelay(5); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + return -E1000_ERR_SWFW_SYNC; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync); + + e1000_put_hw_eeprom_semaphore(hw); + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the + * second function of dual function devices + * + * nic - Struct containing variables accessed by shared code + *****************************************************************************/ +static int +e1000_read_mac_addr(struct eth_device *nic) +{ +#ifndef CONFIG_AP1000 + struct e1000_hw *hw = nic->priv; + uint16_t offset; + uint16_t eeprom_data; + int i; + + DEBUGFUNC(); + + for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) { + offset = i >> 1; + if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + nic->enetaddr[i] = eeprom_data & 0xff; + nic->enetaddr[i + 1] = (eeprom_data >> 8) & 0xff; + } + if ((hw->mac_type == e1000_82546) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + /* Invert the last bit if this is the second device */ + nic->enetaddr[5] += 1; + } +#ifdef CONFIG_E1000_FALLBACK_MAC + if ( *(u32*)(nic->enetaddr) == 0 || *(u32*)(nic->enetaddr) == ~0 ) { + unsigned char fb_mac[NODE_ADDRESS_SIZE] = CONFIG_E1000_FALLBACK_MAC; + + memcpy (nic->enetaddr, fb_mac, NODE_ADDRESS_SIZE); + } +#endif +#else + /* + * The AP1000's e1000 has no eeprom; the MAC address is stored in the + * environment variables. Currently this does not support the addition + * of a PMC e1000 card, which is certainly a possibility, so this should + * be updated to properly use the env variable only for the onboard e1000 + */ + + int ii; + char *s, *e; + + DEBUGFUNC(); + + s = getenv ("ethaddr"); + if (s == NULL) { + return -E1000_ERR_EEPROM; + } else { + for(ii = 0; ii < 6; ii++) { + nic->enetaddr[ii] = s ? simple_strtoul (s, &e, 16) : 0; + if (s){ + s = (*e) ? e + 1 : e; + } + } + } +#endif + return 0; +} + +/****************************************************************************** + * Initializes receive address filters. + * + * hw - Struct containing variables accessed by shared code + * + * Places the MAC address in receive address register 0 and clears the rest + * of the receive addresss registers. Clears the multicast table. Assumes + * the receiver is in reset when the routine is called. + *****************************************************************************/ +static void +e1000_init_rx_addrs(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t i; + uint32_t addr_low; + uint32_t addr_high; + + DEBUGFUNC(); + + /* Setup the receive address. */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + addr_low = (nic->enetaddr[0] | + (nic->enetaddr[1] << 8) | + (nic->enetaddr[2] << 16) | (nic->enetaddr[3] << 24)); + + addr_high = (nic->enetaddr[4] | (nic->enetaddr[5] << 8) | E1000_RAH_AV); + + E1000_WRITE_REG_ARRAY(hw, RA, 0, addr_low); + E1000_WRITE_REG_ARRAY(hw, RA, 1, addr_high); + + /* Zero out the other 15 receive addresses. */ + DEBUGOUT("Clearing RAR[1-15]\n"); + for (i = 1; i < E1000_RAR_ENTRIES; i++) { + E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + } +} + +/****************************************************************************** + * Clears the VLAN filer table + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_clear_vfta(struct e1000_hw *hw) +{ + uint32_t offset; + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, 0); +} + +/****************************************************************************** + * Set the mac type member in the hw struct. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_set_mac_type(struct e1000_hw *hw) +{ + DEBUGFUNC(); + + switch (hw->device_id) { + case E1000_DEV_ID_82542: + switch (hw->revision_id) { + case E1000_82542_2_0_REV_ID: + hw->mac_type = e1000_82542_rev2_0; + break; + case E1000_82542_2_1_REV_ID: + hw->mac_type = e1000_82542_rev2_1; + break; + default: + /* Invalid 82542 revision ID */ + return -E1000_ERR_MAC_TYPE; + } + break; + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + hw->mac_type = e1000_82543; + break; + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + hw->mac_type = e1000_82544; + break; + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82540EP: + case E1000_DEV_ID_82540EP_LOM: + case E1000_DEV_ID_82540EP_LP: + hw->mac_type = e1000_82540; + break; + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + hw->mac_type = e1000_82545; + break; + case E1000_DEV_ID_82545GM_COPPER: + case E1000_DEV_ID_82545GM_FIBER: + case E1000_DEV_ID_82545GM_SERDES: + hw->mac_type = e1000_82545_rev_3; + break; + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546EB_QUAD_COPPER: + hw->mac_type = e1000_82546; + break; + case E1000_DEV_ID_82546GB_COPPER: + case E1000_DEV_ID_82546GB_FIBER: + case E1000_DEV_ID_82546GB_SERDES: + case E1000_DEV_ID_82546GB_PCIE: + case E1000_DEV_ID_82546GB_QUAD_COPPER: + case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: + hw->mac_type = e1000_82546_rev_3; + break; + case E1000_DEV_ID_82541EI: + case E1000_DEV_ID_82541EI_MOBILE: + case E1000_DEV_ID_82541ER_LOM: + hw->mac_type = e1000_82541; + break; + case E1000_DEV_ID_82541ER: + case E1000_DEV_ID_82541GI: + case E1000_DEV_ID_82541GI_LF: + case E1000_DEV_ID_82541GI_MOBILE: + hw->mac_type = e1000_82541_rev_2; + break; + case E1000_DEV_ID_82547EI: + case E1000_DEV_ID_82547EI_MOBILE: + hw->mac_type = e1000_82547; + break; + case E1000_DEV_ID_82547GI: + hw->mac_type = e1000_82547_rev_2; + break; + case E1000_DEV_ID_82571EB_COPPER: + case E1000_DEV_ID_82571EB_FIBER: + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82571EB_QUAD_COPPER: + case E1000_DEV_ID_82571PT_QUAD_COPPER: + case E1000_DEV_ID_82571EB_QUAD_FIBER: + case E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE: + hw->mac_type = e1000_82571; + break; + case E1000_DEV_ID_82572EI_COPPER: + case E1000_DEV_ID_82572EI_FIBER: + case E1000_DEV_ID_82572EI_SERDES: + case E1000_DEV_ID_82572EI: + hw->mac_type = e1000_82572; + break; + case E1000_DEV_ID_82573E: + case E1000_DEV_ID_82573E_IAMT: + case E1000_DEV_ID_82573L: + hw->mac_type = e1000_82573; + break; + case E1000_DEV_ID_80003ES2LAN_COPPER_SPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_SPT: + case E1000_DEV_ID_80003ES2LAN_COPPER_DPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + hw->mac_type = e1000_80003es2lan; + break; + case E1000_DEV_ID_ICH8_IGP_M_AMT: + case E1000_DEV_ID_ICH8_IGP_AMT: + case E1000_DEV_ID_ICH8_IGP_C: + case E1000_DEV_ID_ICH8_IFE: + case E1000_DEV_ID_ICH8_IFE_GT: + case E1000_DEV_ID_ICH8_IFE_G: + case E1000_DEV_ID_ICH8_IGP_M: + hw->mac_type = e1000_ich8lan; + break; + default: + /* Should never have loaded on this device */ + return -E1000_ERR_MAC_TYPE; + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reset the transmit and receive units; mask and clear all interrupts. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void +e1000_reset_hw(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint32_t ctrl_ext; + uint32_t icr; + uint32_t manc; + uint32_t pba = 0; + + DEBUGFUNC(); + + /* get the correct pba value for both PCI and PCIe*/ + if (hw->mac_type < e1000_82571) + pba = E1000_DEFAULT_PCI_PBA; + else + pba = E1000_DEFAULT_PCIE_PBA; + + /* For 82542 (rev 2.0), disable MWI before issuing a device reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + pci_write_config_word(hw->pdev, PCI_COMMAND, + hw->pci_cmd_word & ~PCI_COMMAND_INVALIDATE); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, IMC, 0xffffffff); + + /* Disable the Transmit and Receive units. Then delay to allow + * any pending transactions to complete before we hit the MAC with + * the global reset. + */ + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_REG(hw, TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ + hw->tbi_compatibility_on = FALSE; + + /* Delay to allow any outstanding PCI transactions to complete before + * resetting the device + */ + mdelay(10); + + /* Issue a global reset to the MAC. This will reset the chip's + * transmit, receive, DMA, and link units. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + DEBUGOUT("Issuing a global reset to MAC\n"); + ctrl = E1000_READ_REG(hw, CTRL); + + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + + /* Force a reload from the EEPROM if necessary */ + if (hw->mac_type < e1000_82540) { + /* Wait for reset to complete */ + udelay(10); + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + /* Wait for EEPROM reload */ + mdelay(2); + } else { + /* Wait for EEPROM reload (it happens automatically) */ + mdelay(4); + /* Dissable HW ARPs on ASF enabled adapters */ + manc = E1000_READ_REG(hw, MANC); + manc &= ~(E1000_MANC_ARP_EN); + E1000_WRITE_REG(hw, MANC, manc); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, IMC, 0xffffffff); + + /* Clear any pending interrupt events. */ + icr = E1000_READ_REG(hw, ICR); + + /* If MWI was previously enabled, reenable it. */ + if (hw->mac_type == e1000_82542_rev2_0) { + pci_write_config_word(hw->pdev, PCI_COMMAND, hw->pci_cmd_word); + } + E1000_WRITE_REG(hw, PBA, pba); +} + +/****************************************************************************** + * + * Initialize a number of hardware-dependent bits + * + * hw: Struct containing variables accessed by shared code + * + * This function contains hardware limitation workarounds for PCI-E adapters + * + *****************************************************************************/ +static void +e1000_initialize_hardware_bits(struct e1000_hw *hw) +{ + if ((hw->mac_type >= e1000_82571) && + (!hw->initialize_hw_bits_disable)) { + /* Settings common to all PCI-express silicon */ + uint32_t reg_ctrl, reg_ctrl_ext; + uint32_t reg_tarc0, reg_tarc1; + uint32_t reg_tctl; + uint32_t reg_txdctl, reg_txdctl1; + + /* link autonegotiation/sync workarounds */ + reg_tarc0 = E1000_READ_REG(hw, TARC0); + reg_tarc0 &= ~((1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)); + + /* Enable not-done TX descriptor counting */ + reg_txdctl = E1000_READ_REG(hw, TXDCTL); + reg_txdctl |= E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, TXDCTL, reg_txdctl); + + reg_txdctl1 = E1000_READ_REG(hw, TXDCTL1); + reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, TXDCTL1, reg_txdctl1); + + switch (hw->mac_type) { + case e1000_82571: + case e1000_82572: + /* Clear PHY TX compatible mode bits */ + reg_tarc1 = E1000_READ_REG(hw, TARC1); + reg_tarc1 &= ~((1 << 30)|(1 << 29)); + + /* link autonegotiation/sync workarounds */ + reg_tarc0 |= ((1 << 26)|(1 << 25)|(1 << 24)|(1 << 23)); + + /* TX ring control fixes */ + reg_tarc1 |= ((1 << 26)|(1 << 25)|(1 << 24)); + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + case e1000_82573: + reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext &= ~(1 << 23); + reg_ctrl_ext |= (1 << 22); + + /* TX byte count fix */ + reg_ctrl = E1000_READ_REG(hw, CTRL); + reg_ctrl &= ~(1 << 29); + + E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); + E1000_WRITE_REG(hw, CTRL, reg_ctrl); + break; + case e1000_80003es2lan: + /* improve small packet performace for fiber/serdes */ + if ((hw->media_type == e1000_media_type_fiber) + || (hw->media_type == + e1000_media_type_internal_serdes)) { + reg_tarc0 &= ~(1 << 20); + } + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + reg_tarc1 = E1000_READ_REG(hw, TARC1); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + case e1000_ich8lan: + /* Reduce concurrent DMA requests to 3 from 4 */ + if ((hw->revision_id < 3) || + ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && + (hw->device_id != E1000_DEV_ID_ICH8_IGP_M))) + reg_tarc0 |= ((1 << 29)|(1 << 28)); + + reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext |= (1 << 22); + E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); + + /* workaround TX hang with TSO=on */ + reg_tarc0 |= ((1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)); + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + reg_tarc1 = E1000_READ_REG(hw, TARC1); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + /* workaround TX hang with TSO=on */ + reg_tarc1 |= ((1 << 30)|(1 << 26)|(1 << 24)); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + default: + break; + } + + E1000_WRITE_REG(hw, TARC0, reg_tarc0); + } +} + +/****************************************************************************** + * Performs basic configuration of the adapter. + * + * hw - Struct containing variables accessed by shared code + * + * Assumes that the controller has previously been reset and is in a + * post-reset uninitialized state. Initializes the receive address registers, + * multicast table, and VLAN filter table. Calls routines to setup link + * configuration and flow control settings. Clears all on-chip counters. Leaves + * the transmit and receive units disabled and uninitialized. + *****************************************************************************/ +static int +e1000_init_hw(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t ctrl; + uint32_t i; + int32_t ret_val; + uint16_t pcix_cmd_word; + uint16_t pcix_stat_hi_word; + uint16_t cmd_mmrbc; + uint16_t stat_mmrbc; + uint32_t mta_size; + uint32_t reg_data; + uint32_t ctrl_ext; + DEBUGFUNC(); + /* force full DMA clock frequency for 10/100 on ICH8 A0-B0 */ + if ((hw->mac_type == e1000_ich8lan) && + ((hw->revision_id < 3) || + ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && + (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))) { + reg_data = E1000_READ_REG(hw, STATUS); + reg_data &= ~0x80000000; + E1000_WRITE_REG(hw, STATUS, reg_data); + } + /* Do not need initialize Identification LED */ + + /* Set the media type and TBI compatibility */ + e1000_set_media_type(hw); + + /* Must be called after e1000_set_media_type + * because media_type is used */ + e1000_initialize_hardware_bits(hw); + + /* Disabling VLAN filtering. */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */ + if (hw->mac_type != e1000_ich8lan) { + if (hw->mac_type < e1000_82545_rev_3) + E1000_WRITE_REG(hw, VET, 0); + e1000_clear_vfta(hw); + } + + /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + pci_write_config_word(hw->pdev, PCI_COMMAND, + hw-> + pci_cmd_word & ~PCI_COMMAND_INVALIDATE); + E1000_WRITE_REG(hw, RCTL, E1000_RCTL_RST); + E1000_WRITE_FLUSH(hw); + mdelay(5); + } + + /* Setup the receive address. This involves initializing all of the Receive + * Address Registers (RARs 0 - 15). + */ + e1000_init_rx_addrs(nic); + + /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ + if (hw->mac_type == e1000_82542_rev2_0) { + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_FLUSH(hw); + mdelay(1); + pci_write_config_word(hw->pdev, PCI_COMMAND, hw->pci_cmd_word); + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + mta_size = E1000_MC_TBL_SIZE; + if (hw->mac_type == e1000_ich8lan) + mta_size = E1000_MC_TBL_SIZE_ICH8LAN; + for (i = 0; i < mta_size; i++) { + E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + /* use write flush to prevent Memory Write Block (MWB) from + * occuring when accessing our register space */ + E1000_WRITE_FLUSH(hw); + } +#if 0 + /* Set the PCI priority bit correctly in the CTRL register. This + * determines if the adapter gives priority to receives, or if it + * gives equal priority to transmits and receives. Valid only on + * 82542 and 82543 silicon. + */ + if (hw->dma_fairness && hw->mac_type <= e1000_82543) { + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR); + } +#endif + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ + if (hw->bus_type == e1000_bus_type_pcix) { + pci_read_config_word(hw->pdev, PCIX_COMMAND_REGISTER, + &pcix_cmd_word); + pci_read_config_word(hw->pdev, PCIX_STATUS_REGISTER_HI, + &pcix_stat_hi_word); + cmd_mmrbc = + (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >> + PCIX_COMMAND_MMRBC_SHIFT; + stat_mmrbc = + (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >> + PCIX_STATUS_HI_MMRBC_SHIFT; + if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K) + stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K; + if (cmd_mmrbc > stat_mmrbc) { + pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + pci_write_config_word(hw->pdev, PCIX_COMMAND_REGISTER, + pcix_cmd_word); + } + } + break; + } + + /* More time needed for PHY to initialize */ + if (hw->mac_type == e1000_ich8lan) + mdelay(15); + + /* Call a subroutine to configure the link and setup flow control. */ + ret_val = e1000_setup_link(nic); + + /* Set the transmit descriptor write-back policy */ + if (hw->mac_type > e1000_82544) { + ctrl = E1000_READ_REG(hw, TXDCTL); + ctrl = + (ctrl & ~E1000_TXDCTL_WTHRESH) | + E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, TXDCTL, ctrl); + } + + switch (hw->mac_type) { + default: + break; + case e1000_80003es2lan: + /* Enable retransmit on late collisions */ + reg_data = E1000_READ_REG(hw, TCTL); + reg_data |= E1000_TCTL_RTLC; + E1000_WRITE_REG(hw, TCTL, reg_data); + + /* Configure Gigabit Carry Extend Padding */ + reg_data = E1000_READ_REG(hw, TCTL_EXT); + reg_data &= ~E1000_TCTL_EXT_GCEX_MASK; + reg_data |= DEFAULT_80003ES2LAN_TCTL_EXT_GCEX; + E1000_WRITE_REG(hw, TCTL_EXT, reg_data); + + /* Configure Transmit Inter-Packet Gap */ + reg_data = E1000_READ_REG(hw, TIPG); + reg_data &= ~E1000_TIPG_IPGT_MASK; + reg_data |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; + E1000_WRITE_REG(hw, TIPG, reg_data); + + reg_data = E1000_READ_REG_ARRAY(hw, FFLT, 0x0001); + reg_data &= ~0x00100000; + E1000_WRITE_REG_ARRAY(hw, FFLT, 0x0001, reg_data); + /* Fall through */ + case e1000_82571: + case e1000_82572: + case e1000_ich8lan: + ctrl = E1000_READ_REG(hw, TXDCTL1); + ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) + | E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, TXDCTL1, ctrl); + break; + } + + if (hw->mac_type == e1000_82573) { + uint32_t gcr = E1000_READ_REG(hw, GCR); + gcr |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX; + E1000_WRITE_REG(hw, GCR, gcr); + } + +#if 0 + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs(hw); + + /* ICH8 No-snoop bits are opposite polarity. + * Set to snoop by default after reset. */ + if (hw->mac_type == e1000_ich8lan) + e1000_set_pci_ex_no_snoop(hw, PCI_EX_82566_SNOOP_ALL); +#endif + + if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER || + hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) { + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + /* Relaxed ordering must be disabled to avoid a parity + * error crash in a PCI slot. */ + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + return ret_val; +} + +/****************************************************************************** + * Configures flow control and link settings. + * + * hw - Struct containing variables accessed by shared code + * + * Determines which flow control settings to use. Calls the apropriate media- + * specific link configuration function. Configures the flow control settings. + * Assuming the adapter has a valid link partner, a valid link should be + * established. Assumes the hardware has previously been reset and the + * transmitter and receiver are not enabled. + *****************************************************************************/ +static int +e1000_setup_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t ctrl_ext; + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC(); + + /* In the case of the phy reset being blocked, we already have a link. + * We do not have to set it up again. */ + if (e1000_check_phy_reset_block(hw)) + return E1000_SUCCESS; + +#ifndef CONFIG_AP1000 + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + if (e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, 1, + &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } +#else + /* we have to hardcode the proper value for our hardware. */ + /* this value is for the 82540EM pci card used for prototyping, and it works. */ + eeprom_data = 0xb220; +#endif + + if (hw->fc == e1000_fc_default) { + switch (hw->mac_type) { + case e1000_ich8lan: + case e1000_82573: + hw->fc = e1000_fc_full; + break; + default: +#ifndef CONFIG_AP1000 + ret_val = e1000_read_eeprom(hw, + EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data); + if (ret_val) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } +#else + eeprom_data = 0xb220; +#endif + if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0) + hw->fc = e1000_fc_none; + else if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == + EEPROM_WORD0F_ASM_DIR) + hw->fc = e1000_fc_tx_pause; + else + hw->fc = e1000_fc_full; + break; + } + } + + /* We want to save off the original Flow Control configuration just + * in case we get disconnected and then reconnected into a different + * hub or switch with different Flow Control capabilities. + */ + if (hw->mac_type == e1000_82542_rev2_0) + hw->fc &= (~e1000_fc_tx_pause); + + if ((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1)) + hw->fc &= (~e1000_fc_rx_pause); + + hw->original_fc = hw->fc; + + DEBUGOUT("After fix-ups FlowControl is now = %x\n", hw->fc); + + /* Take the 4 bits from EEPROM word 0x0F that determine the initial + * polarity value for the SW controlled pins, and setup the + * Extended Device Control reg with that info. + * This is needed because one of the SW controlled pins is used for + * signal detection. So this should be done before e1000_setup_pcs_link() + * or e1000_phy_setup() is called. + */ + if (hw->mac_type == e1000_82543) { + ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << + SWDPIO__EXT_SHIFT); + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + /* Call the necessary subroutine to configure the link. */ + ret_val = (hw->media_type == e1000_media_type_fiber) ? + e1000_setup_fiber_link(nic) : e1000_setup_copper_link(nic); + if (ret_val < 0) { + return ret_val; + } + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing the Flow Control address, type" + "and timer regs\n"); + + /* FCAL/H and FCT are hardcoded to standard values in e1000_ich8lan. */ + if (hw->mac_type != e1000_ich8lan) { + E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW); + } + + E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames in not enabled, then these + * registers will be set to 0. + */ + if (!(hw->fc & e1000_fc_tx_pause)) { + E1000_WRITE_REG(hw, FCRTL, 0); + E1000_WRITE_REG(hw, FCRTH, 0); + } else { + /* We need to set up the Receive Threshold high and low water marks + * as well as (optionally) enabling the transmission of XON frames. + */ + if (hw->fc_send_xon) { + E1000_WRITE_REG(hw, FCRTL, + (hw->fc_low_water | E1000_FCRTL_XONE)); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } else { + E1000_WRITE_REG(hw, FCRTL, hw->fc_low_water); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } + } + return ret_val; +} + +/****************************************************************************** + * Sets up link for a fiber based adapter + * + * hw - Struct containing variables accessed by shared code + * + * Manipulates Physical Coding Sublayer functions in order to configure + * link. Assumes the hardware has been previously reset and the transmitter + * and receiver are not enabled. + *****************************************************************************/ +static int +e1000_setup_fiber_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t ctrl; + uint32_t status; + uint32_t txcw = 0; + uint32_t i; + uint32_t signal; + int32_t ret_val; + + DEBUGFUNC(); + /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal + */ + ctrl = E1000_READ_REG(hw, CTRL); + if ((hw->mac_type > e1000_82544) && !(ctrl & E1000_CTRL_ILOS)) + signal = E1000_CTRL_SWDPIN1; + else + signal = 0; + + printf("signal for %s is %x (ctrl %08x)!!!!\n", nic->name, signal, + ctrl); + /* Take the link out of reset */ + ctrl &= ~(E1000_CTRL_LRST); + + e1000_config_collision_dist(hw); + + /* Check for a software override of the flow control settings, and setup + * the device accordingly. If auto-negotiation is enabled, then software + * will have to set the "PAUSE" bits to the correct value in the Tranmsit + * Config Word Register (TXCW) and re-start auto-negotiation. However, if + * auto-negotiation is disabled, then software will have to manually + * configure the two flow control enable bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, but + * not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we do + * not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + */ + switch (hw->fc) { + case e1000_fc_none: + /* Flow control is completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case e1000_fc_rx_pause: + /* RX Flow control is enabled and TX Flow control is disabled by a + * software over-ride. Since there really isn't a way to advertise + * that we are capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case e1000_fc_tx_pause: + /* TX Flow control is enabled, and RX Flow control is disabled, by a + * software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case e1000_fc_full: + /* Flow control (both RX and TX) is enabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + break; + } + + /* Since auto-negotiation is enabled, take the link out of reset (the link + * will be in reset, because we previously reset the chip). This will + * restart auto-negotiation. If auto-neogtiation is successful then the + * link-up status bit will be set and the flow control enable bits (RFCE + * and TFCE) will be set according to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled (%#x)\n", txcw); + + E1000_WRITE_REG(hw, TXCW, txcw); + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + hw->txcw = txcw; + mdelay(1); + + /* If we have a signal (the cable is plugged in) then poll for a "Link-Up" + * indication in the Device Status Register. Time-out if a link isn't + * seen in 500 milliseconds seconds (Auto-negotiation should complete in + * less than 500 milliseconds even if the other end is doing it in SW). + */ + if ((E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) { + DEBUGOUT("Looking for Link\n"); + for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { + mdelay(10); + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_LU) + break; + } + if (i == (LINK_UP_TIMEOUT / 10)) { + /* AutoNeg failed to achieve a link, so we'll call + * e1000_check_for_link. This routine will force the link up if we + * detect a signal. This will allow us to communicate with + * non-autonegotiating link partners. + */ + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + hw->autoneg_failed = 1; + ret_val = e1000_check_for_link(nic); + if (ret_val < 0) { + DEBUGOUT("Error while checking for link\n"); + return ret_val; + } + hw->autoneg_failed = 0; + } else { + hw->autoneg_failed = 0; + DEBUGOUT("Valid Link Found\n"); + } + } else { + DEBUGOUT("No Signal Detected\n"); + return -E1000_ERR_NOLINK; + } + return 0; +} + +/****************************************************************************** +* Make sure we have a valid PHY and change PHY mode before link setup. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_copper_link_preconfig(struct e1000_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + ctrl = E1000_READ_REG(hw, CTRL); + /* With 82543, we need to force speed and duplex on the MAC equal to what + * the PHY speed and duplex configuration is. In addition, we need to + * perform a hardware reset on the PHY to take it out of reset. + */ + if (hw->mac_type > e1000_82543) { + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, CTRL, ctrl); + } else { + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX + | E1000_CTRL_SLU); + E1000_WRITE_REG(hw, CTRL, ctrl); + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + } + + /* Make sure we have a valid PHY */ + ret_val = e1000_detect_gig_phy(hw); + if (ret_val) { + DEBUGOUT("Error, did not detect valid phy.\n"); + return ret_val; + } + DEBUGOUT("Phy ID = %x \n", hw->phy_id); + +#ifndef CONFIG_AP1000 + /* Set PHY to class A mode (if necessary) */ + ret_val = e1000_set_phy_mode(hw); + if (ret_val) + return ret_val; +#endif + if ((hw->mac_type == e1000_82545_rev_3) || + (hw->mac_type == e1000_82546_rev_3)) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data); + phy_data |= 0x00000008; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + } + + if (hw->mac_type <= e1000_82543 || + hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82541_rev_2 + || hw->mac_type == e1000_82547_rev_2) + hw->phy_reset_disable = FALSE; + + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +static int32_t +e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active) +{ + uint32_t phy_ctrl = 0; + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC(); + + if (hw->phy_type != e1000_phy_igp && hw->phy_type != e1000_phy_igp_2 + && hw->phy_type != e1000_phy_igp_3) + return E1000_SUCCESS; + + /* During driver activity LPLU should not be used or it will attain link + * from the lowest speeds starting from 10Mbps. The capability is used + * for Dx transitions and states */ + if (hw->mac_type == e1000_82541_rev_2 + || hw->mac_type == e1000_82547_rev_2) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, + &phy_data); + if (ret_val) + return ret_val; + } else if (hw->mac_type == e1000_ich8lan) { + /* MAC writes into PHY register based on the state transition + * and start auto-negotiation. SW driver can overwrite the + * settings in CSR PHY power control E1000_PHY_CTRL register. */ + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else { + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + &phy_data); + if (ret_val) + return ret_val; + } + + if (!active) { + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data &= ~IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + phy_data); + if (ret_val) + return ret_val; + } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data &= ~IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + } + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during + * Dx states where the power conservation is most important. During + * driver activity we should enable SmartSpeed, so performance is + * maintained. */ + if (hw->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } else if (hw->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } + + } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) + || (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { + + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data |= IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_GMII_FIFO, phy_data); + if (ret_val) + return ret_val; + } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data |= IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + } + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu d0 state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +static int32_t +e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active) +{ + uint32_t phy_ctrl = 0; + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC(); + + if (hw->mac_type <= e1000_82547_rev_2) + return E1000_SUCCESS; + + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else { + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + &phy_data); + if (ret_val) + return ret_val; + } + + if (!active) { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during + * Dx states where the power conservation is most important. During + * driver activity we should enable SmartSpeed, so performance is + * maintained. */ + if (hw->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } else if (hw->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + } + + + } else { + + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data |= IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, + IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + + } + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_igp series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_igp_setup(struct e1000_hw *hw) +{ + uint32_t led_ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + /* Wait 15ms for MAC to configure PHY from eeprom settings */ + mdelay(15); + if (hw->mac_type != e1000_ich8lan) { + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* The NVM settings will configure LPLU in D3 for IGP2 and IGP3 PHYs */ + if (hw->phy_type == e1000_phy_igp) { + /* disable lplu d3 during driver init */ + ret_val = e1000_set_d3_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D3\n"); + return ret_val; + } + } + + /* disable lplu d0 during driver init */ + ret_val = e1000_set_d0_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D0\n"); + return ret_val; + } + /* Configure mdi-mdix settings */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + hw->dsp_config_state = e1000_dsp_config_disabled; + /* Force MDI for earlier revs of the IGP PHY */ + phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX + | IGP01E1000_PSCR_FORCE_MDI_MDIX); + hw->mdix = 1; + + } else { + hw->dsp_config_state = e1000_dsp_config_enabled; + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (hw->mdix) { + case 1: + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + phy_data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + } + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* set auto-master slave resolution settings */ + if (hw->autoneg) { + e1000_ms_type phy_ms_setting = hw->master_slave; + + if (hw->ffe_config_state == e1000_ffe_config_active) + hw->ffe_config_state = e1000_ffe_config_enabled; + + if (hw->dsp_config_state == e1000_dsp_config_activated) + hw->dsp_config_state = e1000_dsp_config_enabled; + + /* when autonegotiation advertisment is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. */ + if (hw->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + /* Set auto Master/Slave resolution process */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, + &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~CR_1000T_MS_ENABLE; + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* load defaults for future use */ + hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : + e1000_ms_auto; + + switch (phy_ms_setting) { + case e1000_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + default: + break; + } + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * This function checks the mode of the firmware. + * + * returns - TRUE when the mode is IAMT or FALSE. + ****************************************************************************/ +boolean_t +e1000_check_mng_mode(struct e1000_hw *hw) +{ + uint32_t fwsm; + DEBUGFUNC(); + + fwsm = E1000_READ_REG(hw, FWSM); + + if (hw->mac_type == e1000_ich8lan) { + if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_ICH_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return TRUE; + } else if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return TRUE; + + return FALSE; +} + +static int32_t +e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data) +{ + uint32_t reg_val; + uint16_t swfw; + DEBUGFUNC(); + + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) + & E1000_KUMCTRLSTA_OFFSET) | data; + E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + udelay(2); + + return E1000_SUCCESS; +} + +static int32_t +e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data) +{ + uint32_t reg_val; + uint16_t swfw; + DEBUGFUNC(); + + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + /* Write register address */ + reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & + E1000_KUMCTRLSTA_OFFSET) | E1000_KUMCTRLSTA_REN; + E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + udelay(2); + + /* Read the data returned */ + reg_val = E1000_READ_REG(hw, KUMCTRLSTA); + *data = (uint16_t)reg_val; + + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_gg82563 series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_ggp_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + uint32_t reg_data; + + DEBUGFUNC(); + + if (!hw->phy_reset_disable) { + /* Enable CRS on TX for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_MAC_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; + /* Use 25MHz for both link down and 1000BASE-T for Tx clock */ + phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ; + + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_MAC_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK; + + switch (hw->mdix) { + case 1: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI; + break; + case 2: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDIX; + break; + case 0: + default: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_SPEC_CTRL, phy_data); + + if (ret_val) + return ret_val; + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + } /* phy_reset_disable */ + + if (hw->mac_type == e1000_80003es2lan) { + /* Bypass RX and TX FIFO's */ + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL, + E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS + | E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_SPEC_CTRL_2, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_SPEC_CTRL_2, phy_data); + + if (ret_val) + return ret_val; + + reg_data = E1000_READ_REG(hw, CTRL_EXT); + reg_data &= ~(E1000_CTRL_EXT_LINK_MODE_MASK); + E1000_WRITE_REG(hw, CTRL_EXT, reg_data); + + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_PWR_MGMT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Do not init these registers when the HW is in IAMT mode, since the + * firmware will have already initialized them. We only initialize + * them if the HW is not in IAMT mode. + */ + if (e1000_check_mng_mode(hw) == FALSE) { + /* Enable Electrical Idle on the PHY */ + phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_PWR_MGMT_CTRL, phy_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_KMRN_MODE_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_KMRN_MODE_CTRL, phy_data); + + if (ret_val) + return ret_val; + } + + /* Workaround: Disable padding in Kumeran interface in the MAC + * and in the PHY to avoid CRC errors. + */ + ret_val = e1000_read_phy_reg(hw, + GG82563_PHY_INBAND_CTRL, &phy_data); + if (ret_val) + return ret_val; + phy_data |= GG82563_ICR_DIS_PADDING; + ret_val = e1000_write_phy_reg(hw, + GG82563_PHY_INBAND_CTRL, phy_data); + if (ret_val) + return ret_val; + } + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_m88 series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_mgp_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (hw->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if (hw->phy_revision < M88E1011_I_REV_4) { + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = e1000_read_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((hw->phy_revision == E1000_REVISION_2) && + (hw->phy_id == M88E1111_I_PHY_ID)) { + /* Vidalia Phy, set the downshift counter to 5x */ + phy_data &= ~(M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK); + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK + | M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X + | M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } + } + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/******************************************************************** +* Setup auto-negotiation and flow control advertisements, +* and then perform auto-negotiation. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_autoneg(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + /* Perform some bounds checking on the hw->autoneg_advertised + * parameter. If this variable is zero, then set it to the default. + */ + hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (hw->autoneg_advertised == 0) + hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* IFE phy only supports 10/100 */ + if (hw->phy_type == e1000_phy_ife) + hw->autoneg_advertised &= AUTONEG_ADVERTISE_10_100_ALL; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; + } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + /* If we do not wait for autonegtation to complete I + * do not see a valid link status. + * wait_autoneg_complete = 1 . + */ + if (hw->wait_autoneg_complete) { + ret_val = e1000_wait_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error while waiting for autoneg" + "to complete\n"); + return ret_val; + } + } + + hw->get_link_status = TRUE; + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Config the MAC and the PHY after link is up. +* 1) Set up the MAC to the current PHY speed/duplex +* if we are on 82543. If we +* are on newer silicon, we only need to configure +* collision distance in the Transmit Control Register. +* 2) Set up flow control on the MAC to that established with +* the link partner. +* 3) Config DSP to improve Gigabit link quality for some PHY revisions. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_copper_link_postconfig(struct e1000_hw *hw) +{ + int32_t ret_val; + DEBUGFUNC(); + + if (hw->mac_type >= e1000_82544) { + e1000_config_collision_dist(hw); + } else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error Configuring Flow Control\n"); + return ret_val; + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Detects which PHY is present and setup the speed and duplex +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +e1000_setup_copper_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + int32_t ret_val; + uint16_t i; + uint16_t phy_data; + uint16_t reg_data; + + DEBUGFUNC(); + + switch (hw->mac_type) { + case e1000_80003es2lan: + case e1000_ich8lan: + /* Set the mac to wait the maximum time between each + * iteration and increase the max iterations when + * polling the phy; this fixes erroneous timeouts at 10Mbps. */ + ret_val = e1000_write_kmrn_reg(hw, + GG82563_REG(0x34, 4), 0xFFFF); + if (ret_val) + return ret_val; + ret_val = e1000_read_kmrn_reg(hw, + GG82563_REG(0x34, 9), ®_data); + if (ret_val) + return ret_val; + reg_data |= 0x3F; + ret_val = e1000_write_kmrn_reg(hw, + GG82563_REG(0x34, 9), reg_data); + if (ret_val) + return ret_val; + default: + break; + } + + /* Check if it is a valid PHY and set PHY mode if necessary. */ + ret_val = e1000_copper_link_preconfig(hw); + if (ret_val) + return ret_val; + switch (hw->mac_type) { + case e1000_80003es2lan: + /* Kumeran registers are written-only */ + reg_data = + E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT; + reg_data |= E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_INB_CTRL, reg_data); + if (ret_val) + return ret_val; + break; + default: + break; + } + + if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) { + ret_val = e1000_copper_link_igp_setup(hw); + if (ret_val) + return ret_val; + } else if (hw->phy_type == e1000_phy_m88) { + ret_val = e1000_copper_link_mgp_setup(hw); + if (ret_val) + return ret_val; + } else if (hw->phy_type == e1000_phy_gg82563) { + ret_val = e1000_copper_link_ggp_setup(hw); + if (ret_val) + return ret_val; + } + + /* always auto */ + /* Setup autoneg and flow control advertisement + * and perform autonegotiation */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + return ret_val; + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + for (i = 0; i < 10; i++) { + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & MII_SR_LINK_STATUS) { + /* Config the MAC and PHY after link is up */ + ret_val = e1000_copper_link_postconfig(hw); + if (ret_val) + return ret_val; + + DEBUGOUT("Valid link established!!!\n"); + return E1000_SUCCESS; + } + udelay(10); + } + + DEBUGOUT("Unable to establish link!!!\n"); + return E1000_SUCCESS; +} + +/****************************************************************************** +* Configures PHY autoneg and flow control advertisement settings +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +e1000_phy_setup_autoneg(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t mii_autoneg_adv_reg; + uint16_t mii_1000t_ctrl_reg; + + DEBUGFUNC(); + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + if (hw->phy_type != e1000_phy_ife) { + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, + &mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } else + mii_1000t_ctrl_reg = 0; + + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~REG4_SPEED_MASK; + mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK; + + DEBUGOUT("autoneg_advertised %x\n", hw->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } + + /* Do we want to advertise 10 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } + + /* Do we want to advertise 100 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } + + /* Do we want to advertise 100 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } + + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if (hw->autoneg_advertised & ADVERTISE_1000_HALF) { + DEBUGOUT + ("Advertise 1000mb Half duplex requested, request denied!\n"); + } + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + } + + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (hw->fc) { + case e1000_fc_none: /* 0 */ + /* Flow control (RX & TX) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_rx_pause: /* 1 */ + /* RX Flow control is enabled, and TX Flow control is + * disabled, by a software over-ride. + */ + /* Since there really isn't a way to advertise that we are + * capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + *hw's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_tx_pause: /* 2 */ + /* TX Flow control is enabled, and RX Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case e1000_fc_full: /* 3 */ + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + DEBUGOUT("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + if (hw->phy_type != e1000_phy_ife) { + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, + mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Sets the collision distance in the Transmit Control register +* +* hw - Struct containing variables accessed by shared code +* +* Link should have been established previously. Reads the speed and duplex +* information from the Device Status register. +******************************************************************************/ +static void +e1000_config_collision_dist(struct e1000_hw *hw) +{ + uint32_t tctl, coll_dist; + + DEBUGFUNC(); + + if (hw->mac_type < e1000_82543) + coll_dist = E1000_COLLISION_DISTANCE_82542; + else + coll_dist = E1000_COLLISION_DISTANCE; + + tctl = E1000_READ_REG(hw, TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= coll_dist << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/****************************************************************************** +* Sets MAC speed and duplex settings to reflect the those in the PHY +* +* hw - Struct containing variables accessed by shared code +* mii_reg - data to write to the MII control register +* +* The contents of the PHY register containing the needed information need to +* be passed in. +******************************************************************************/ +static int +e1000_config_mac_to_phy(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint16_t phy_data; + + DEBUGFUNC(); + + /* Read the Device Control Register and set the bits to Force Speed + * and Duplex. + */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); + + /* Set up duplex in the Device Control and Transmit Control + * registers depending on negotiated values. + */ + if (e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (phy_data & M88E1000_PSSR_DPLX) + ctrl |= E1000_CTRL_FD; + else + ctrl &= ~E1000_CTRL_FD; + + e1000_config_collision_dist(hw); + + /* Set up speed in the Device Control register depending on + * negotiated values. + */ + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) + ctrl |= E1000_CTRL_SPD_1000; + else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) + ctrl |= E1000_CTRL_SPD_100; + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(hw, CTRL, ctrl); + return 0; +} + +/****************************************************************************** + * Forces the MAC's flow control settings. + * + * hw - Struct containing variables accessed by shared code + * + * Sets the TFCE and RFCE bits in the device control register to reflect + * the adapter settings. TFCE and RFCE need to be explicitly set by + * software when a Copper PHY is used because autonegotiation is managed + * by the PHY rather than the MAC. Software must also configure these + * bits when link is forced on a fiber connection. + *****************************************************************************/ +static int +e1000_force_mac_fc(struct e1000_hw *hw) +{ + uint32_t ctrl; + + DEBUGFUNC(); + + /* Get the current configuration of the Device Control Register */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "hw->fc" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and TX flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + + switch (hw->fc) { + case e1000_fc_none: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case e1000_fc_rx_pause: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case e1000_fc_tx_pause: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case e1000_fc_full: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + /* Disable TX Flow Control for 82542 (rev 2.0) */ + if (hw->mac_type == e1000_82542_rev2_0) + ctrl &= (~E1000_CTRL_TFCE); + + E1000_WRITE_REG(hw, CTRL, ctrl); + return 0; +} + +/****************************************************************************** + * Configures flow control settings after link is established + * + * hw - Struct containing variables accessed by shared code + * + * Should be called immediately after a valid link has been established. + * Forces MAC flow control settings if link was forced. When in MII/GMII mode + * and autonegotiation is enabled, the MAC flow control settings will be set + * based on the flow control negotiated by the PHY. In TBI mode, the TFCE + * and RFCE bits will be automaticaly set to the negotiated flow control mode. + *****************************************************************************/ +static int32_t +e1000_config_fc_after_link_up(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t mii_status_reg; + uint16_t mii_nway_adv_reg; + uint16_t mii_nway_lp_ability_reg; + uint16_t speed; + uint16_t duplex; + + DEBUGFUNC(); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) + || ((hw->media_type == e1000_media_type_internal_serdes) + && (hw->autoneg_failed)) + || ((hw->media_type == e1000_media_type_copper) + && (!hw->autoneg))) { + ret_val = e1000_force_mac_fc(hw); + if (ret_val < 0) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if (hw->media_type == e1000_media_type_copper) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + if (e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) { + DEBUGOUT("PHY Read Error \n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg) < 0) { + DEBUGOUT("PHY Read Error \n"); + return -E1000_ERR_PHY; + } + + if (mii_status_reg & MII_SR_AUTONEG_COMPLETE) { + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement Register + * (Address 4) and the Auto_Negotiation Base Page Ability + * Register (Address 5) to determine how flow control was + * negotiated. + */ + if (e1000_read_phy_reg + (hw, PHY_AUTONEG_ADV, &mii_nway_adv_reg) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg + (hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + /* Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | e1000_fc_full + * + */ + if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected RX ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->original_fc == e1000_fc_full) { + hw->fc = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\r\n"); + } else { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\r\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * + */ + else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) + { + hw->fc = e1000_fc_tx_pause; + DEBUGOUT + ("Flow Control = TX PAUSE frames only.\r\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + */ + else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) + { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\r\n"); + } + /* Per the IEEE spec, at this point flow control should be + * disabled. However, we want to consider that we could + * be connected to a legacy switch that doesn't advertise + * desired flow control, but can be forced on the link + * partner. So if we advertised no flow control, that is + * what we will resolve to. If we advertised some kind of + * receive capability (Rx Pause Only or Full Flow Control) + * and the link partner advertised none, we will configure + * ourselves to enable Rx Flow Control only. We can do + * this safely for two reasons: If the link partner really + * didn't want flow control enabled, and we enable Rx, no + * harm done since we won't be receiving any PAUSE frames + * anyway. If the intent on the link partner was to have + * flow control enabled, then by us enabling RX only, we + * can at least receive pause frames and process them. + * This is a good idea because in most cases, since we are + * predominantly a server NIC, more times than not we will + * be asked to delay transmission of packets than asking + * our link partner to pause transmission of frames. + */ + else if (hw->original_fc == e1000_fc_none || + hw->original_fc == e1000_fc_tx_pause) { + hw->fc = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\r\n"); + } else { + hw->fc = e1000_fc_rx_pause; + DEBUGOUT + ("Flow Control = RX PAUSE frames only.\r\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + e1000_get_speed_and_duplex(hw, &speed, &duplex); + + if (duplex == HALF_DUPLEX) + hw->fc = e1000_fc_none; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = e1000_force_mac_fc(hw); + if (ret_val < 0) { + DEBUGOUT + ("Error forcing flow control settings\n"); + return ret_val; + } + } else { + DEBUGOUT + ("Copper PHY and Auto Neg has not completed.\r\n"); + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Checks to see if the link status of the hardware has changed. + * + * hw - Struct containing variables accessed by shared code + * + * Called by any function that needs to check the link status of the adapter. + *****************************************************************************/ +static int +e1000_check_for_link(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + uint32_t rxcw; + uint32_t ctrl; + uint32_t status; + uint32_t rctl; + uint32_t signal; + int32_t ret_val; + uint16_t phy_data; + uint16_t lp_capability; + + DEBUGFUNC(); + + /* On adapters with a MAC newer that 82544, SW Defineable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal + */ + ctrl = E1000_READ_REG(hw, CTRL); + if ((hw->mac_type > e1000_82544) && !(ctrl & E1000_CTRL_ILOS)) + signal = E1000_CTRL_SWDPIN1; + else + signal = 0; + + status = E1000_READ_REG(hw, STATUS); + rxcw = E1000_READ_REG(hw, RXCW); + DEBUGOUT("ctrl: %#08x status %#08x rxcw %#08x\n", ctrl, status, rxcw); + + /* If we have a copper PHY then we only want to go out to the PHY + * registers to see if Auto-Neg has completed and/or if our link + * status has changed. The get_link_status flag will be set if we + * receive a Link Status Change interrupt or we have Rx Sequence + * Errors. + */ + if ((hw->media_type == e1000_media_type_copper) && hw->get_link_status) { + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + * Read the register twice since the link bit is sticky. + */ + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + + if (phy_data & MII_SR_LINK_STATUS) { + hw->get_link_status = FALSE; + } else { + /* No link detected */ + return -E1000_ERR_NOLINK; + } + + /* We have a M88E1000 PHY and Auto-Neg is enabled. If we + * have Si on board that is 82544 or newer, Auto + * Speed Detection takes care of MAC speed/duplex + * configuration. So we only need to configure Collision + * Distance in the MAC. Otherwise, we need to force + * speed/duplex on the MAC to the current PHY speed/duplex + * settings. + */ + if (hw->mac_type >= e1000_82544) + e1000_config_collision_dist(hw); + else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val < 0) { + DEBUGOUT + ("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + + /* Configure Flow Control now that Auto-Neg has completed. First, we + * need to restore the desired flow control settings because we may + * have had to re-autoneg with a different link partner. + */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val < 0) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + + /* At this point we know that we are on copper and we have + * auto-negotiated link. These are conditions for checking the link + * parter capability register. We use the link partner capability to + * determine if TBI Compatibility needs to be turned on or off. If + * the link partner advertises any speed in addition to Gigabit, then + * we assume that they are GMII-based, and TBI compatibility is not + * needed. If no other speeds are advertised, we assume the link + * partner is TBI-based, and we turn on TBI Compatibility. + */ + if (hw->tbi_compatibility_en) { + if (e1000_read_phy_reg + (hw, PHY_LP_ABILITY, &lp_capability) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (lp_capability & (NWAY_LPAR_10T_HD_CAPS | + NWAY_LPAR_10T_FD_CAPS | + NWAY_LPAR_100TX_HD_CAPS | + NWAY_LPAR_100TX_FD_CAPS | + NWAY_LPAR_100T4_CAPS)) { + /* If our link partner advertises anything in addition to + * gigabit, we do not need to enable TBI compatibility. + */ + if (hw->tbi_compatibility_on) { + /* If we previously were in the mode, turn it off. */ + rctl = E1000_READ_REG(hw, RCTL); + rctl &= ~E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + hw->tbi_compatibility_on = FALSE; + } + } else { + /* If TBI compatibility is was previously off, turn it on. For + * compatibility with a TBI link partner, we will store bad + * packets. Some frames have an additional byte on the end and + * will look like CRC errors to to the hardware. + */ + if (!hw->tbi_compatibility_on) { + hw->tbi_compatibility_on = TRUE; + rctl = E1000_READ_REG(hw, RCTL); + rctl |= E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + } + } + } + } + /* If we don't have link (auto-negotiation failed or link partner cannot + * auto-negotiate), the cable is plugged in (we have signal), and our + * link partner is not trying to auto-negotiate with us (we are receiving + * idles or data), we need to force link up. We also need to give + * auto-negotiation time to complete, in case the cable was just plugged + * in. The autoneg_failed flag does this. + */ + else if ((hw->media_type == e1000_media_type_fiber) && + (!(status & E1000_STATUS_LU)) && + ((ctrl & E1000_CTRL_SWDPIN1) == signal) && + (!(rxcw & E1000_RXCW_C))) { + if (hw->autoneg_failed == 0) { + hw->autoneg_failed = 1; + return 0; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\r\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, TXCW, (hw->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val < 0) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } + /* If we are forcing link and we are receiving /C/ ordered sets, re-enable + * auto-negotiation in the TXCW register and disable forced link in the + * Device Control register in an attempt to auto-negotiate with our link + * partner. + */ + else if ((hw->media_type == e1000_media_type_fiber) && + (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + DEBUGOUT + ("RXing /C/, enable AutoNeg and stop forcing link.\r\n"); + E1000_WRITE_REG(hw, TXCW, hw->txcw); + E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU)); + } + return 0; +} + +/****************************************************************************** +* Configure the MAC-to-PHY interface for 10/100Mbps +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, uint16_t duplex) +{ + int32_t ret_val = E1000_SUCCESS; + uint32_t tipg; + uint16_t reg_data; + + DEBUGFUNC(); + + reg_data = E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_HD_CTRL, reg_data); + if (ret_val) + return ret_val; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_10_100; + E1000_WRITE_REG(hw, TIPG, tipg); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + if (duplex == HALF_DUPLEX) + reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER; + else + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + + return ret_val; +} + +static int32_t +e1000_configure_kmrn_for_1000(struct e1000_hw *hw) +{ + int32_t ret_val = E1000_SUCCESS; + uint16_t reg_data; + uint32_t tipg; + + DEBUGFUNC(); + + reg_data = E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, + E1000_KUMCTRLSTA_OFFSET_HD_CTRL, reg_data); + if (ret_val) + return ret_val; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; + E1000_WRITE_REG(hw, TIPG, tipg); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + + return ret_val; +} + +/****************************************************************************** + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + *****************************************************************************/ +static int +e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, + uint16_t *duplex) +{ + uint32_t status; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + if (hw->mac_type >= e1000_82543) { + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if (status & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if (status & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\r\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT(" Half Duplex\r\n"); + } + } else { + DEBUGOUT("1000 Mbs, Full Duplex\r\n"); + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + } + + /* IGP01 PHY may advertise full duplex operation after speed downgrade + * even if it is operating at half duplex. Here we set the duplex + * settings to match the duplex in the link partner's capabilities. + */ + if (hw->phy_type == e1000_phy_igp && hw->speed_downgraded) { + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data); + if (ret_val) + return ret_val; + + if (!(phy_data & NWAY_ER_LP_NWAY_CAPS)) + *duplex = HALF_DUPLEX; + else { + ret_val = e1000_read_phy_reg(hw, + PHY_LP_ABILITY, &phy_data); + if (ret_val) + return ret_val; + if ((*speed == SPEED_100 && + !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) + || (*speed == SPEED_10 + && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) + *duplex = HALF_DUPLEX; + } + } + + if ((hw->mac_type == e1000_80003es2lan) && + (hw->media_type == e1000_media_type_copper)) { + if (*speed == SPEED_1000) + ret_val = e1000_configure_kmrn_for_1000(hw); + else + ret_val = e1000_configure_kmrn_for_10_100(hw, *duplex); + if (ret_val) + return ret_val; + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Blocks until autoneg completes or times out (~4.5 seconds) +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int +e1000_wait_autoneg(struct e1000_hw *hw) +{ + uint16_t i; + uint16_t phy_data; + + DEBUGFUNC(); + DEBUGOUT("Waiting for Auto-Neg to complete.\n"); + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for (i = PHY_AUTO_NEG_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (e1000_read_phy_reg(hw, PHY_STATUS, &phy_data) < 0) { + DEBUGOUT("PHY Read Error\n"); + return -E1000_ERR_PHY; + } + if (phy_data & MII_SR_AUTONEG_COMPLETE) { + DEBUGOUT("Auto-Neg complete.\n"); + return 0; + } + mdelay(100); + } + DEBUGOUT("Auto-Neg timedout.\n"); + return -E1000_ERR_TIMEOUT; +} + +/****************************************************************************** +* Raises the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t * ctrl) +{ + /* Raise the clock input to the Management Data Clock (by setting the MDC + * bit), and then delay 2 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + udelay(2); +} + +/****************************************************************************** +* Lowers the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t * ctrl) +{ + /* Lower the clock input to the Management Data Clock (by clearing the MDC + * bit), and then delay 2 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + udelay(2); +} + +/****************************************************************************** +* Shifts data bits out to the PHY +* +* hw - Struct containing variables accessed by shared code +* data - Data to send out to the PHY +* count - Number of bits to shift out +* +* Bits are shifted out in MSB to LSB order. +******************************************************************************/ +static void +e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data, uint16_t count) +{ + uint32_t ctrl; + uint32_t mask; + + /* We need to shift "count" number of bits out to the PHY. So, the value + * in the "data" parameter will be shifted out to the PHY one bit at a + * time. In order to do this, "data" must be broken down into bits. + */ + mask = 0x01; + mask <<= (count - 1); + + ctrl = E1000_READ_REG(hw, CTRL); + + /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */ + ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); + + while (mask) { + /* A "1" is shifted out to the PHY by setting the MDIO bit to "1" and + * then raising and lowering the Management Data Clock. A "0" is + * shifted out to the PHY by setting the MDIO bit to "0" and then + * raising and lowering the clock. + */ + if (data & mask) + ctrl |= E1000_CTRL_MDIO; + else + ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + udelay(2); + + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + mask = mask >> 1; + } +} + +/****************************************************************************** +* Shifts data bits in from the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Bits are shifted in in MSB to LSB order. +******************************************************************************/ +static uint16_t +e1000_shift_in_mdi_bits(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint16_t data = 0; + uint8_t i; + + /* In order to read a register from the PHY, we need to shift in a total + * of 18 bits from the PHY. The first two bit (turnaround) times are used + * to avoid contention on the MDIO pin when a read operation is performed. + * These two bits are ignored by us and thrown away. Bits are "shifted in" + * by raising the input to the Management Data Clock (setting the MDC bit), + * and then reading the value of the MDIO bit. + */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */ + ctrl &= ~E1000_CTRL_MDIO_DIR; + ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + /* Raise and Lower the clock before reading in the data. This accounts for + * the turnaround bits. The first clock occurred when we clocked out the + * last bit of the Register Address. + */ + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + for (data = 0, i = 0; i < 16; i++) { + data = data << 1; + e1000_raise_mdi_clk(hw, &ctrl); + ctrl = E1000_READ_REG(hw, CTRL); + /* Check to see if we shifted in a "1". */ + if (ctrl & E1000_CTRL_MDIO) + data |= 1; + e1000_lower_mdi_clk(hw, &ctrl); + } + + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + return data; +} + +/***************************************************************************** +* Reads the value from a PHY register +* +* hw - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to read +******************************************************************************/ +static int +e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t * phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if (hw->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, and register address in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + E1000_WRITE_REG(hw, MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < 64; i++) { + udelay(10); + mdic = E1000_READ_REG(hw, MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + *phy_data = (uint16_t) mdic; + } else { + /* We must first send a preamble through the MDIO pin to signal the + * beginning of an MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the next few fields that are required for a read + * operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine five different times. The format of + * a MII read instruction consists of a shift out of 14 bits and is + * defined as follows: + * <Preamble><SOF><Op Code><Phy Addr><Reg Addr> + * followed by a shift in of 18 bits. This first two bits shifted in + * are TurnAround bits used to avoid contention on the MDIO pin when a + * READ operation is performed. These two bits are thrown away + * followed by a shift in of 16 bits which contains the desired data. + */ + mdic = ((reg_addr) | (phy_addr << 5) | + (PHY_OP_READ << 10) | (PHY_SOF << 12)); + + e1000_shift_out_mdi_bits(hw, mdic, 14); + + /* Now that we've shifted out the read command to the MII, we need to + * "shift in" the 16-bit value (18 total bits) of the requested PHY + * register address. + */ + *phy_data = e1000_shift_in_mdi_bits(hw); + } + return 0; +} + +/****************************************************************************** +* Writes a value to a PHY register +* +* hw - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to write +* data - data to write to the PHY +******************************************************************************/ +static int +e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT("PHY Address %d is out of range\n", reg_addr); + return -E1000_ERR_PARAM; + } + + if (hw->mac_type > e1000_82543) { + /* Set up Op-code, Phy Address, register address, and data intended + * for the PHY register in the MDI Control register. The MAC will take + * care of interfacing with the PHY to send the desired data. + */ + mdic = (((uint32_t) phy_data) | + (reg_addr << E1000_MDIC_REG_SHIFT) | + (phy_addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + E1000_WRITE_REG(hw, MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < 64; i++) { + udelay(10); + mdic = E1000_READ_REG(hw, MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + return -E1000_ERR_PHY; + } + } else { + /* We'll need to use the SW defined pins to shift the write command + * out to the PHY. We first send a preamble to the PHY to signal the + * beginning of the MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the remaining required fields that will indicate a + * write operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine for each field in the command. The + * format of a MII write instruction is as follows: + * <Preamble><SOF><Op Code><Phy Addr><Reg Addr><Turnaround><Data>. + */ + mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | + (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); + mdic <<= 16; + mdic |= (uint32_t) phy_data; + + e1000_shift_out_mdi_bits(hw, mdic, 32); + } + return 0; +} + +/****************************************************************************** + * Checks if PHY reset is blocked due to SOL/IDER session, for example. + * Returning E1000_BLK_PHY_RESET isn't necessarily an error. But it's up to + * the caller to figure out how to deal with it. + * + * hw - Struct containing variables accessed by shared code + * + * returns: - E1000_BLK_PHY_RESET + * E1000_SUCCESS + * + *****************************************************************************/ +int32_t +e1000_check_phy_reset_block(struct e1000_hw *hw) +{ + uint32_t manc = 0; + uint32_t fwsm = 0; + + if (hw->mac_type == e1000_ich8lan) { + fwsm = E1000_READ_REG(hw, FWSM); + return (fwsm & E1000_FWSM_RSPCIPHY) ? E1000_SUCCESS + : E1000_BLK_PHY_RESET; + } + + if (hw->mac_type > e1000_82547_rev_2) + manc = E1000_READ_REG(hw, MANC); + return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? + E1000_BLK_PHY_RESET : E1000_SUCCESS; +} + +/*************************************************************************** + * Checks if the PHY configuration is done + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to reset MAC + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_phy_cfg_done(struct e1000_hw *hw) +{ + int32_t timeout = PHY_CFG_TIMEOUT; + uint32_t cfg_mask = E1000_EEPROM_CFG_DONE; + + DEBUGFUNC(); + + switch (hw->mac_type) { + default: + mdelay(10); + break; + case e1000_80003es2lan: + /* Separate *_CFG_DONE_* bit for each port */ + if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1; + /* Fall Through */ + case e1000_82571: + case e1000_82572: + while (timeout) { + if (E1000_READ_REG(hw, EEMNGCTL) & cfg_mask) + break; + else + mdelay(1); + timeout--; + } + if (!timeout) { + DEBUGOUT("MNG configuration cycle has not " + "completed.\n"); + return -E1000_ERR_RESET; + } + break; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Returns the PHY to the power-on reset state +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +e1000_phy_hw_reset(struct e1000_hw *hw) +{ + uint32_t ctrl, ctrl_ext; + uint32_t led_ctrl; + int32_t ret_val; + uint16_t swfw; + + DEBUGFUNC(); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = e1000_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + DEBUGOUT("Resetting Phy...\n"); + + if (hw->mac_type > e1000_82543) { + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) { + DEBUGOUT("Unable to acquire swfw sync\n"); + return -E1000_ERR_SWFW_SYNC; + } + /* Read the device control register and assert the E1000_CTRL_PHY_RST + * bit. Then, take it out of reset. + */ + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + + if (hw->mac_type < e1000_82571) + udelay(10); + else + udelay(100); + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + if (hw->mac_type >= e1000_82571) + mdelay(10); + + } else { + /* Read the Extended Device Control Register, assert the PHY_RESET_DIR + * bit to put the PHY into reset. Then, take it out of reset. + */ + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; + ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + mdelay(10); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + udelay(150); + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* Wait for FW to finish PHY configuration. */ + ret_val = e1000_get_phy_cfg_done(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + + return ret_val; +} + +/****************************************************************************** + * IGP phy init script - initializes the GbE PHY + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_phy_init_script(struct e1000_hw *hw) +{ + uint32_t ret_val; + uint16_t phy_saved_data; + DEBUGFUNC(); + + if (hw->phy_init_script) { + mdelay(20); + + /* Save off the current value of register 0x2F5B to be + * restored at the end of this routine. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + /* Disabled the PHY transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + + mdelay(20); + + e1000_write_phy_reg(hw, 0x0000, 0x0140); + + mdelay(5); + + switch (hw->mac_type) { + case e1000_82541: + case e1000_82547: + e1000_write_phy_reg(hw, 0x1F95, 0x0001); + + e1000_write_phy_reg(hw, 0x1F71, 0xBD21); + + e1000_write_phy_reg(hw, 0x1F79, 0x0018); + + e1000_write_phy_reg(hw, 0x1F30, 0x1600); + + e1000_write_phy_reg(hw, 0x1F31, 0x0014); + + e1000_write_phy_reg(hw, 0x1F32, 0x161C); + + e1000_write_phy_reg(hw, 0x1F94, 0x0003); + + e1000_write_phy_reg(hw, 0x1F96, 0x003F); + + e1000_write_phy_reg(hw, 0x2010, 0x0008); + break; + + case e1000_82541_rev_2: + case e1000_82547_rev_2: + e1000_write_phy_reg(hw, 0x1F73, 0x0099); + break; + default: + break; + } + + e1000_write_phy_reg(hw, 0x0000, 0x3300); + + mdelay(20); + + /* Now enable the transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (hw->mac_type == e1000_82547) { + uint16_t fused, fine, coarse; + + /* Move to analog registers page */ + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused); + + if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) { + e1000_read_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_STATUS, &fused); + + fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK; + coarse = fused + & IGP01E1000_ANALOG_FUSE_COARSE_MASK; + + if (coarse > + IGP01E1000_ANALOG_FUSE_COARSE_THRESH) { + coarse -= + IGP01E1000_ANALOG_FUSE_COARSE_10; + fine -= IGP01E1000_ANALOG_FUSE_FINE_1; + } else if (coarse + == IGP01E1000_ANALOG_FUSE_COARSE_THRESH) + fine -= IGP01E1000_ANALOG_FUSE_FINE_10; + + fused = (fused + & IGP01E1000_ANALOG_FUSE_POLY_MASK) | + (fine + & IGP01E1000_ANALOG_FUSE_FINE_MASK) | + (coarse + & IGP01E1000_ANALOG_FUSE_COARSE_MASK); + + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_CONTROL, fused); + e1000_write_phy_reg(hw, + IGP01E1000_ANALOG_FUSE_BYPASS, + IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL); + } + } + } +} + +/****************************************************************************** +* Resets the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Sets bit 15 of the MII Control register +******************************************************************************/ +int32_t +e1000_phy_reset(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC(); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = e1000_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + switch (hw->phy_type) { + case e1000_phy_igp: + case e1000_phy_igp_2: + case e1000_phy_igp_3: + case e1000_phy_ife: + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + break; + default: + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= MII_CR_RESET; + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + udelay(1); + break; + } + + if (hw->phy_type == e1000_phy_igp || hw->phy_type == e1000_phy_igp_2) + e1000_phy_init_script(hw); + + return E1000_SUCCESS; +} + +static int e1000_set_phy_type (struct e1000_hw *hw) +{ + DEBUGFUNC (); + + if (hw->mac_type == e1000_undefined) + return -E1000_ERR_PHY_TYPE; + + switch (hw->phy_id) { + case M88E1000_E_PHY_ID: + case M88E1000_I_PHY_ID: + case M88E1011_I_PHY_ID: + case M88E1111_I_PHY_ID: + hw->phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: + if (hw->mac_type == e1000_82541 || + hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82547_rev_2) { + hw->phy_type = e1000_phy_igp; + hw->phy_type = e1000_phy_igp; + break; + } + case IGP03E1000_E_PHY_ID: + hw->phy_type = e1000_phy_igp_3; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + hw->phy_type = e1000_phy_ife; + break; + case GG82563_E_PHY_ID: + if (hw->mac_type == e1000_80003es2lan) { + hw->phy_type = e1000_phy_gg82563; + break; + } + /* Fall Through */ + default: + /* Should never have loaded on this device */ + hw->phy_type = e1000_phy_undefined; + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Probes the expected PHY address for known PHY IDs +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_detect_gig_phy(struct e1000_hw *hw) +{ + int32_t phy_init_status, ret_val; + uint16_t phy_id_high, phy_id_low; + boolean_t match = FALSE; + + DEBUGFUNC(); + + /* The 82571 firmware may still be configuring the PHY. In this + * case, we cannot access the PHY until the configuration is done. So + * we explicitly set the PHY values. */ + if (hw->mac_type == e1000_82571 || + hw->mac_type == e1000_82572) { + hw->phy_id = IGP01E1000_I_PHY_ID; + hw->phy_type = e1000_phy_igp_2; + return E1000_SUCCESS; + } + + /* ESB-2 PHY reads require e1000_phy_gg82563 to be set because of a + * work- around that forces PHY page 0 to be set or the reads fail. + * The rest of the code in this routine uses e1000_read_phy_reg to + * read the PHY ID. So for ESB-2 we need to have this set so our + * reads won't fail. If the attached PHY is not a e1000_phy_gg82563, + * the routines below will figure this out as well. */ + if (hw->mac_type == e1000_80003es2lan) + hw->phy_type = e1000_phy_gg82563; + + /* Read the PHY ID Registers to identify which PHY is onboard. */ + ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high); + if (ret_val) + return ret_val; + + hw->phy_id = (uint32_t) (phy_id_high << 16); + udelay(20); + ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); + if (ret_val) + return ret_val; + + hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK); + hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK; + + switch (hw->mac_type) { + case e1000_82543: + if (hw->phy_id == M88E1000_E_PHY_ID) + match = TRUE; + break; + case e1000_82544: + if (hw->phy_id == M88E1000_I_PHY_ID) + match = TRUE; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + if (hw->phy_id == M88E1011_I_PHY_ID) + match = TRUE; + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if(hw->phy_id == IGP01E1000_I_PHY_ID) + match = TRUE; + + break; + case e1000_82573: + if (hw->phy_id == M88E1111_I_PHY_ID) + match = TRUE; + break; + case e1000_80003es2lan: + if (hw->phy_id == GG82563_E_PHY_ID) + match = TRUE; + break; + case e1000_ich8lan: + if (hw->phy_id == IGP03E1000_E_PHY_ID) + match = TRUE; + if (hw->phy_id == IFE_E_PHY_ID) + match = TRUE; + if (hw->phy_id == IFE_PLUS_E_PHY_ID) + match = TRUE; + if (hw->phy_id == IFE_C_E_PHY_ID) + match = TRUE; + break; + default: + DEBUGOUT("Invalid MAC type %d\n", hw->mac_type); + return -E1000_ERR_CONFIG; + } + + phy_init_status = e1000_set_phy_type(hw); + + if ((match) && (phy_init_status == E1000_SUCCESS)) { + DEBUGOUT("PHY ID 0x%X detected\n", hw->phy_id); + return 0; + } + DEBUGOUT("Invalid PHY ID 0x%X\n", hw->phy_id); + return -E1000_ERR_PHY; +} + +/***************************************************************************** + * Set media type and TBI compatibility. + * + * hw - Struct containing variables accessed by shared code + * **************************************************************************/ +void +e1000_set_media_type(struct e1000_hw *hw) +{ + uint32_t status; + + DEBUGFUNC(); + + if (hw->mac_type != e1000_82543) { + /* tbi_compatibility is only valid on 82543 */ + hw->tbi_compatibility_en = FALSE; + } + + switch (hw->device_id) { + case E1000_DEV_ID_82545GM_SERDES: + case E1000_DEV_ID_82546GB_SERDES: + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82572EI_SERDES: + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->media_type = e1000_media_type_fiber; + break; + case e1000_ich8lan: + case e1000_82573: + /* The STATUS_TBIMODE bit is reserved or reused + * for the this device. + */ + hw->media_type = e1000_media_type_copper; + break; + default: + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_TBIMODE) { + hw->media_type = e1000_media_type_fiber; + /* tbi_compatibility not valid on fiber */ + hw->tbi_compatibility_en = FALSE; + } else { + hw->media_type = e1000_media_type_copper; + } + break; + } + } +} + +/** + * e1000_sw_init - Initialize general software structures (struct e1000_adapter) + * + * e1000_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + **/ + +static int +e1000_sw_init(struct eth_device *nic, int cardnum) +{ + struct e1000_hw *hw = (typeof(hw)) nic->priv; + int result; + + /* PCI config space info */ + pci_read_config_word(hw->pdev, PCI_VENDOR_ID, &hw->vendor_id); + pci_read_config_word(hw->pdev, PCI_DEVICE_ID, &hw->device_id); + pci_read_config_word(hw->pdev, PCI_SUBSYSTEM_VENDOR_ID, + &hw->subsystem_vendor_id); + pci_read_config_word(hw->pdev, PCI_SUBSYSTEM_ID, &hw->subsystem_id); + + pci_read_config_byte(hw->pdev, PCI_REVISION_ID, &hw->revision_id); + pci_read_config_word(hw->pdev, PCI_COMMAND, &hw->pci_cmd_word); + + /* identify the MAC */ + result = e1000_set_mac_type(hw); + if (result) { + E1000_ERR("Unknown MAC Type\n"); + return result; + } + + switch (hw->mac_type) { + default: + break; + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + hw->phy_init_script = 1; + break; + } + + /* lan a vs. lan b settings */ + if (hw->mac_type == e1000_82546) + /*this also works w/ multiple 82546 cards */ + /*but not if they're intermingled /w other e1000s */ + hw->lan_loc = (cardnum % 2) ? e1000_lan_b : e1000_lan_a; + else + hw->lan_loc = e1000_lan_a; + + /* flow control settings */ + hw->fc_high_water = E1000_FC_HIGH_THRESH; + hw->fc_low_water = E1000_FC_LOW_THRESH; + hw->fc_pause_time = E1000_FC_PAUSE_TIME; + hw->fc_send_xon = 1; + + /* Media type - copper or fiber */ + e1000_set_media_type(hw); + + if (hw->mac_type >= e1000_82543) { + uint32_t status = E1000_READ_REG(hw, STATUS); + + if (status & E1000_STATUS_TBIMODE) { + DEBUGOUT("fiber interface\n"); + hw->media_type = e1000_media_type_fiber; + } else { + DEBUGOUT("copper interface\n"); + hw->media_type = e1000_media_type_copper; + } + } else { + hw->media_type = e1000_media_type_fiber; + } + + hw->tbi_compatibility_en = TRUE; + hw->wait_autoneg_complete = TRUE; + if (hw->mac_type < e1000_82543) + hw->report_tx_early = 0; + else + hw->report_tx_early = 1; + + return E1000_SUCCESS; +} + +void +fill_rx(struct e1000_hw *hw) +{ + struct e1000_rx_desc *rd; + + rx_last = rx_tail; + rd = rx_base + rx_tail; + rx_tail = (rx_tail + 1) % 8; + memset(rd, 0, 16); + rd->buffer_addr = cpu_to_le64((u32) & packet); + E1000_WRITE_REG(hw, RDT, rx_tail); +} + +/** + * e1000_configure_tx - Configure 8254x Transmit Unit after Reset + * @adapter: board private structure + * + * Configure the Tx unit of the MAC after a reset. + **/ + +static void +e1000_configure_tx(struct e1000_hw *hw) +{ + unsigned long ptr; + unsigned long tctl; + unsigned long tipg, tarc; + uint32_t ipgr1, ipgr2; + + ptr = (u32) tx_pool; + if (ptr & 0xf) + ptr = (ptr + 0x10) & (~0xf); + + tx_base = (typeof(tx_base)) ptr; + + E1000_WRITE_REG(hw, TDBAL, (u32) tx_base); + E1000_WRITE_REG(hw, TDBAH, 0); + + E1000_WRITE_REG(hw, TDLEN, 128); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + E1000_WRITE_REG(hw, TDH, 0); + E1000_WRITE_REG(hw, TDT, 0); + tx_tail = 0; + + /* Set the default values for the Tx Inter Packet Gap timer */ + if (hw->mac_type <= e1000_82547_rev_2 && + (hw->media_type == e1000_media_type_fiber || + hw->media_type == e1000_media_type_internal_serdes)) + tipg = DEFAULT_82543_TIPG_IPGT_FIBER; + else + tipg = DEFAULT_82543_TIPG_IPGT_COPPER; + + /* Set the default values for the Tx Inter Packet Gap timer */ + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + tipg = DEFAULT_82542_TIPG_IPGT; + ipgr1 = DEFAULT_82542_TIPG_IPGR1; + ipgr2 = DEFAULT_82542_TIPG_IPGR2; + break; + case e1000_80003es2lan: + ipgr1 = DEFAULT_82543_TIPG_IPGR1; + ipgr2 = DEFAULT_80003ES2LAN_TIPG_IPGR2; + break; + default: + ipgr1 = DEFAULT_82543_TIPG_IPGR1; + ipgr2 = DEFAULT_82543_TIPG_IPGR2; + break; + } + tipg |= ipgr1 << E1000_TIPG_IPGR1_SHIFT; + tipg |= ipgr2 << E1000_TIPG_IPGR2_SHIFT; + E1000_WRITE_REG(hw, TIPG, tipg); + /* Program the Transmit Control Register */ + tctl = E1000_READ_REG(hw, TCTL); + tctl &= ~E1000_TCTL_CT; + tctl |= E1000_TCTL_EN | E1000_TCTL_PSP | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT); + + if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) { + tarc = E1000_READ_REG(hw, TARC0); + /* set the speed mode bit, we'll clear it if we're not at + * gigabit link later */ + /* git bit can be set to 1*/ + } else if (hw->mac_type == e1000_80003es2lan) { + tarc = E1000_READ_REG(hw, TARC0); + tarc |= 1; + E1000_WRITE_REG(hw, TARC0, tarc); + tarc = E1000_READ_REG(hw, TARC1); + tarc |= 1; + E1000_WRITE_REG(hw, TARC1, tarc); + } + + + e1000_config_collision_dist(hw); + /* Setup Transmit Descriptor Settings for eop descriptor */ + hw->txd_cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS; + + /* Need to set up RS bit */ + if (hw->mac_type < e1000_82543) + hw->txd_cmd |= E1000_TXD_CMD_RPS; + else + hw->txd_cmd |= E1000_TXD_CMD_RS; + E1000_WRITE_REG(hw, TCTL, tctl); +} + +/** + * e1000_setup_rctl - configure the receive control register + * @adapter: Board private structure + **/ +static void +e1000_setup_rctl(struct e1000_hw *hw) +{ + uint32_t rctl; + + rctl = E1000_READ_REG(hw, RCTL); + + rctl &= ~(3 << E1000_RCTL_MO_SHIFT); + + rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO + | E1000_RCTL_RDMTS_HALF; /* | + (hw.mc_filter_type << E1000_RCTL_MO_SHIFT); */ + + if (hw->tbi_compatibility_on == 1) + rctl |= E1000_RCTL_SBP; + else + rctl &= ~E1000_RCTL_SBP; + + rctl &= ~(E1000_RCTL_SZ_4096); + rctl |= E1000_RCTL_SZ_2048; + rctl &= ~(E1000_RCTL_BSEX | E1000_RCTL_LPE); + E1000_WRITE_REG(hw, RCTL, rctl); +} + +/** + * e1000_configure_rx - Configure 8254x Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Rx unit of the MAC after a reset. + **/ +static void +e1000_configure_rx(struct e1000_hw *hw) +{ + unsigned long ptr; + unsigned long rctl, ctrl_ext; + rx_tail = 0; + /* make sure receives are disabled while setting up the descriptors */ + rctl = E1000_READ_REG(hw, RCTL); + E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN); + if (hw->mac_type >= e1000_82540) { + /* Set the interrupt throttling rate. Value is calculated + * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns) */ +#define MAX_INTS_PER_SEC 8000 +#define DEFAULT_ITR 1000000000/(MAX_INTS_PER_SEC * 256) + E1000_WRITE_REG(hw, ITR, DEFAULT_ITR); + } + + if (hw->mac_type >= e1000_82571) { + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + /* Reset delay timers after every interrupt */ + ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + /* Setup the Base and Length of the Rx Descriptor Ring */ + ptr = (u32) rx_pool; + if (ptr & 0xf) + ptr = (ptr + 0x10) & (~0xf); + rx_base = (typeof(rx_base)) ptr; + E1000_WRITE_REG(hw, RDBAL, (u32) rx_base); + E1000_WRITE_REG(hw, RDBAH, 0); + + E1000_WRITE_REG(hw, RDLEN, 128); + + /* Setup the HW Rx Head and Tail Descriptor Pointers */ + E1000_WRITE_REG(hw, RDH, 0); + E1000_WRITE_REG(hw, RDT, 0); + /* Enable Receives */ + + E1000_WRITE_REG(hw, RCTL, rctl); + fill_rx(hw); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int +e1000_poll(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + struct e1000_rx_desc *rd; + /* return true if there's an ethernet packet ready to read */ + rd = rx_base + rx_last; + if (!(le32_to_cpu(rd->status)) & E1000_RXD_STAT_DD) + return 0; + /*DEBUGOUT("recv: packet len=%d \n", rd->length); */ + NetReceive((uchar *)packet, le32_to_cpu(rd->length)); + fill_rx(hw); + return 1; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static int +e1000_transmit(struct eth_device *nic, volatile void *packet, int length) +{ + struct e1000_hw *hw = nic->priv; + struct e1000_tx_desc *txp; + int i = 0; + + txp = tx_base + tx_tail; + tx_tail = (tx_tail + 1) % 8; + + txp->buffer_addr = cpu_to_le64(virt_to_bus(hw->pdev, packet)); + txp->lower.data = cpu_to_le32(hw->txd_cmd | length); + txp->upper.data = 0; + E1000_WRITE_REG(hw, TDT, tx_tail); + + E1000_WRITE_FLUSH(hw); + while (!(le32_to_cpu(txp->upper.data) & E1000_TXD_STAT_DD)) { + if (i++ > TOUT_LOOP) { + DEBUGOUT("e1000: tx timeout\n"); + return 0; + } + udelay(10); /* give the nic a chance to write to the register */ + } + return 1; +} + +/*reset function*/ +static inline int +e1000_reset(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + + e1000_reset_hw(hw); + if (hw->mac_type >= e1000_82544) { + E1000_WRITE_REG(hw, WUC, 0); + } + return e1000_init_hw(nic); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void +e1000_disable(struct eth_device *nic) +{ + struct e1000_hw *hw = nic->priv; + + /* Turn off the ethernet interface */ + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_REG(hw, TCTL, 0); + + /* Clear the transmit ring */ + E1000_WRITE_REG(hw, TDH, 0); + E1000_WRITE_REG(hw, TDT, 0); + + /* Clear the receive ring */ + E1000_WRITE_REG(hw, RDH, 0); + E1000_WRITE_REG(hw, RDT, 0); + + /* put the card in its initial state */ +#if 0 + E1000_WRITE_REG(hw, CTRL, E1000_CTRL_RST); +#endif + mdelay(10); + +} + +/************************************************************************** +INIT - set up ethernet interface(s) +***************************************************************************/ +static int +e1000_init(struct eth_device *nic, bd_t * bis) +{ + struct e1000_hw *hw = nic->priv; + int ret_val = 0; + + ret_val = e1000_reset(nic); + if (ret_val < 0) { + if ((ret_val == -E1000_ERR_NOLINK) || + (ret_val == -E1000_ERR_TIMEOUT)) { + E1000_ERR("Valid Link not detected\n"); + } else { + E1000_ERR("Hardware Initialization Failed\n"); + } + return 0; + } + e1000_configure_tx(hw); + e1000_setup_rctl(hw); + e1000_configure_rx(hw); + return 1; +} + +/****************************************************************************** + * Gets the current PCI bus type of hardware + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void e1000_get_bus_type(struct e1000_hw *hw) +{ + uint32_t status; + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->bus_type = e1000_bus_type_pci; + break; + case e1000_82571: + case e1000_82572: + case e1000_82573: + case e1000_80003es2lan: + hw->bus_type = e1000_bus_type_pci_express; + break; + case e1000_ich8lan: + hw->bus_type = e1000_bus_type_pci_express; + break; + default: + status = E1000_READ_REG(hw, STATUS); + hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? + e1000_bus_type_pcix : e1000_bus_type_pci; + break; + } +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +You should omit the last argument struct pci_device * for a non-PCI NIC +***************************************************************************/ +int +e1000_initialize(bd_t * bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *nic = NULL; + struct e1000_hw *hw = NULL; + u32 iobase; + int idx = 0; + u32 PciCommandWord; + + DEBUGFUNC(); + + while (1) { /* Find PCI device(s) */ + if ((devno = pci_find_devices(supported, idx++)) < 0) { + break; + } + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_0, &iobase); + iobase &= ~0xf; /* Mask the bits that say "this is an io addr" */ + DEBUGOUT("e1000#%d: iobase 0x%08x\n", card_number, iobase); + + pci_write_config_dword(devno, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + /* Check if I/O accesses and Bus Mastering are enabled. */ + pci_read_config_dword(devno, PCI_COMMAND, &PciCommandWord); + if (!(PciCommandWord & PCI_COMMAND_MEMORY)) { + printf("Error: Can not enable MEM access.\n"); + continue; + } else if (!(PciCommandWord & PCI_COMMAND_MASTER)) { + printf("Error: Can not enable Bus Mastering.\n"); + continue; + } + + nic = (struct eth_device *) malloc(sizeof (*nic)); + hw = (struct e1000_hw *) malloc(sizeof (*hw)); + hw->pdev = devno; + nic->priv = hw; + + sprintf(nic->name, "e1000#%d", card_number); + + /* Are these variables needed? */ + hw->fc = e1000_fc_default; + hw->original_fc = e1000_fc_default; + hw->autoneg_failed = 0; + hw->autoneg = 1; + hw->get_link_status = TRUE; + hw->hw_addr = + pci_map_bar(devno, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + hw->mac_type = e1000_undefined; + + /* MAC and Phy settings */ + if (e1000_sw_init(nic, card_number) < 0) { + free(hw); + free(nic); + return 0; + } + if (e1000_check_phy_reset_block(hw)) + printf("phy reset block error \n"); + e1000_reset_hw(hw); +#if !(defined(CONFIG_AP1000) || defined(CONFIG_MVBC_1G)) + if (e1000_init_eeprom_params(hw)) { + printf("The EEPROM Checksum Is Not Valid\n"); + free(hw); + free(nic); + return 0; + } + if (e1000_validate_eeprom_checksum(nic) < 0) { + printf("The EEPROM Checksum Is Not Valid\n"); + free(hw); + free(nic); + return 0; + } +#endif + e1000_read_mac_addr(nic); + + /* get the bus type information */ + e1000_get_bus_type(hw); + + printf("e1000: %02x:%02x:%02x:%02x:%02x:%02x\n", + nic->enetaddr[0], nic->enetaddr[1], nic->enetaddr[2], + nic->enetaddr[3], nic->enetaddr[4], nic->enetaddr[5]); + + nic->init = e1000_init; + nic->recv = e1000_poll; + nic->send = e1000_transmit; + nic->halt = e1000_disable; + + eth_register(nic); + + card_number++; + } + return card_number; +} diff --git a/roms/u-boot-sam460ex/drivers/net/e1000.h b/roms/u-boot-sam460ex/drivers/net/e1000.h new file mode 100644 index 000000000..eb0804b41 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/e1000.h @@ -0,0 +1,2583 @@ +/******************************************************************************* + + + Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Linux NICS <linux.nics@intel.com> + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* e1000_hw.h + * Structures, enums, and macros for the MAC + */ + +#ifndef _E1000_HW_H_ +#define _E1000_HW_H_ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> + +#define E1000_ERR(args...) printf("e1000: " args) + +#ifdef E1000_DEBUG +#define E1000_DBG(args...) printf("e1000: " args) +#define DEBUGOUT(fmt,args...) printf(fmt ,##args) +#define DEBUGFUNC() printf("%s\n", __FUNCTION__); +#else +#define E1000_DBG(args...) +#define DEBUGFUNC() +#define DEBUGOUT(fmt,args...) +#endif + +/* Forward declarations of structures used by the shared code */ +struct e1000_hw; +struct e1000_hw_stats; + +typedef enum { + FALSE = 0, + TRUE = 1 +} boolean_t; + +/* Enumerated types specific to the e1000 hardware */ +/* Media Access Controlers */ +typedef enum { + e1000_undefined = 0, + e1000_82542_rev2_0, + e1000_82542_rev2_1, + e1000_82543, + e1000_82544, + e1000_82540, + e1000_82545, + e1000_82545_rev_3, + e1000_82546, + e1000_82546_rev_3, + e1000_82541, + e1000_82541_rev_2, + e1000_82547, + e1000_82547_rev_2, + e1000_82571, + e1000_82572, + e1000_82573, + e1000_80003es2lan, + e1000_ich8lan, + e1000_num_macs +} e1000_mac_type; + +/* Media Types */ +typedef enum { + e1000_media_type_copper = 0, + e1000_media_type_fiber = 1, + e1000_media_type_internal_serdes = 2, + e1000_num_media_types +} e1000_media_type; + +typedef enum { + e1000_eeprom_uninitialized = 0, + e1000_eeprom_spi, + e1000_eeprom_microwire, + e1000_eeprom_flash, + e1000_eeprom_ich8, + e1000_eeprom_none, /* No NVM support */ + e1000_num_eeprom_types +} e1000_eeprom_type; + +typedef enum { + e1000_10_half = 0, + e1000_10_full = 1, + e1000_100_half = 2, + e1000_100_full = 3 +} e1000_speed_duplex_type; + +typedef enum { + e1000_lan_a = 0, + e1000_lan_b = 1 +} e1000_lan_loc; + +/* Flow Control Settings */ +typedef enum { + e1000_fc_none = 0, + e1000_fc_rx_pause = 1, + e1000_fc_tx_pause = 2, + e1000_fc_full = 3, + e1000_fc_default = 0xFF +} e1000_fc_type; + +/* PCI bus types */ +typedef enum { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + e1000_bus_type_pci_express, + e1000_bus_type_reserved +} e1000_bus_type; + +/* PCI bus speeds */ +typedef enum { + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_133, + e1000_bus_speed_reserved +} e1000_bus_speed; + +/* PCI bus widths */ +typedef enum { + e1000_bus_width_unknown = 0, + e1000_bus_width_32, + e1000_bus_width_64 +} e1000_bus_width; + +/* PHY status info structure and supporting enums */ +typedef enum { + e1000_cable_length_50 = 0, + e1000_cable_length_50_80, + e1000_cable_length_80_110, + e1000_cable_length_110_140, + e1000_cable_length_140, + e1000_cable_length_undefined = 0xFF +} e1000_cable_length; + +typedef enum { + e1000_10bt_ext_dist_enable_normal = 0, + e1000_10bt_ext_dist_enable_lower, + e1000_10bt_ext_dist_enable_undefined = 0xFF +} e1000_10bt_ext_dist_enable; + +typedef enum { + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF +} e1000_rev_polarity; + +typedef enum { + e1000_polarity_reversal_enabled = 0, + e1000_polarity_reversal_disabled, + e1000_polarity_reversal_undefined = 0xFF +} e1000_polarity_reversal; + +typedef enum { + e1000_auto_x_mode_manual_mdi = 0, + e1000_auto_x_mode_manual_mdix, + e1000_auto_x_mode_auto1, + e1000_auto_x_mode_auto2, + e1000_auto_x_mode_undefined = 0xFF +} e1000_auto_x_mode; + +typedef enum { + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF +} e1000_1000t_rx_status; + +typedef enum { + e1000_phy_m88 = 0, + e1000_phy_igp, + e1000_phy_igp_2, + e1000_phy_gg82563, + e1000_phy_igp_3, + e1000_phy_ife, + e1000_phy_undefined = 0xFF +} e1000_phy_type; + +struct e1000_phy_info { + e1000_cable_length cable_length; + e1000_10bt_ext_dist_enable extended_10bt_distance; + e1000_rev_polarity cable_polarity; + e1000_polarity_reversal polarity_correction; + e1000_auto_x_mode mdix_mode; + e1000_1000t_rx_status local_rx; + e1000_1000t_rx_status remote_rx; +}; + +struct e1000_phy_stats { + uint32_t idle_errors; + uint32_t receive_errors; +}; + +/* Error Codes */ +#define E1000_SUCCESS 0 +#define E1000_ERR_EEPROM 1 +#define E1000_ERR_PHY 2 +#define E1000_ERR_CONFIG 3 +#define E1000_ERR_PARAM 4 +#define E1000_ERR_MAC_TYPE 5 +#define E1000_ERR_PHY_TYPE 6 +#define E1000_ERR_NOLINK 7 +#define E1000_ERR_TIMEOUT 8 +#define E1000_ERR_RESET 9 +#define E1000_ERR_MASTER_REQUESTS_PENDING 10 +#define E1000_ERR_HOST_INTERFACE_COMMAND 11 +#define E1000_BLK_PHY_RESET 12 +#define E1000_ERR_SWFW_SYNC 13 + +/* PCI Device IDs */ +#define E1000_DEV_ID_82542 0x1000 +#define E1000_DEV_ID_82543GC_FIBER 0x1001 +#define E1000_DEV_ID_82543GC_COPPER 0x1004 +#define E1000_DEV_ID_82544EI_COPPER 0x1008 +#define E1000_DEV_ID_82544EI_FIBER 0x1009 +#define E1000_DEV_ID_82544GC_COPPER 0x100C +#define E1000_DEV_ID_82544GC_LOM 0x100D +#define E1000_DEV_ID_82540EM 0x100E +#define E1000_DEV_ID_82540EM_LOM 0x1015 +#define E1000_DEV_ID_82540EP_LOM 0x1016 +#define E1000_DEV_ID_82540EP 0x1017 +#define E1000_DEV_ID_82540EP_LP 0x101E +#define E1000_DEV_ID_82545EM_COPPER 0x100F +#define E1000_DEV_ID_82545EM_FIBER 0x1011 +#define E1000_DEV_ID_82545GM_COPPER 0x1026 +#define E1000_DEV_ID_82545GM_FIBER 0x1027 +#define E1000_DEV_ID_82545GM_SERDES 0x1028 +#define E1000_DEV_ID_82546EB_COPPER 0x1010 +#define E1000_DEV_ID_82546EB_FIBER 0x1012 +#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D +#define E1000_DEV_ID_82541EI 0x1013 +#define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER_LOM 0x1014 +#define E1000_DEV_ID_82541ER 0x1078 +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#define E1000_DEV_ID_82541GI_LF 0x107C +#define E1000_DEV_ID_82546GB_COPPER 0x1079 +#define E1000_DEV_ID_82546GB_FIBER 0x107A +#define E1000_DEV_ID_82546GB_SERDES 0x107B +#define E1000_DEV_ID_82546GB_PCIE 0x108A +#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 +#define E1000_DEV_ID_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A +#define E1000_DEV_ID_82571EB_COPPER 0x105E +#define E1000_DEV_ID_82571EB_FIBER 0x105F +#define E1000_DEV_ID_82571EB_SERDES 0x1060 +#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 +#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5 +#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 +#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC +#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 +#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA +#define E1000_DEV_ID_82572EI_COPPER 0x107D +#define E1000_DEV_ID_82572EI_FIBER 0x107E +#define E1000_DEV_ID_82572EI_SERDES 0x107F +#define E1000_DEV_ID_82572EI 0x10B9 +#define E1000_DEV_ID_82573E 0x108B +#define E1000_DEV_ID_82573E_IAMT 0x108C +#define E1000_DEV_ID_82573L 0x109A +#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 +#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 +#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 +#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA +#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB + +#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 +#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A +#define E1000_DEV_ID_ICH8_IGP_C 0x104B +#define E1000_DEV_ID_ICH8_IFE 0x104C +#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 +#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 +#define E1000_DEV_ID_ICH8_IGP_M 0x104D + +#define IGP03E1000_E_PHY_ID 0x02A80390 +#define IFE_E_PHY_ID 0x02A80330 /* 10/100 PHY */ +#define IFE_PLUS_E_PHY_ID 0x02A80320 +#define IFE_C_E_PHY_ID 0x02A80310 + +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 /* 100BaseTx Extended Status, + Control and Address */ +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY special + control register */ +#define IFE_PHY_RCV_FALSE_CARRIER 0x13 /* 100BaseTx Receive False + Carrier Counter */ +#define IFE_PHY_RCV_DISCONNECT 0x14 /* 100BaseTx Receive Disconnet + Counter */ +#define IFE_PHY_RCV_ERROT_FRAME 0x15 /* 100BaseTx Receive Error + Frame Counter */ +#define IFE_PHY_RCV_SYMBOL_ERR 0x16 /* Receive Symbol Error + Counter */ +#define IFE_PHY_PREM_EOF_ERR 0x17 /* 100BaseTx Receive + Premature End Of Frame + Error Counter */ +#define IFE_PHY_RCV_EOF_ERR 0x18 /* 10BaseT Receive End Of + Frame Error Counter */ +#define IFE_PHY_TX_JABBER_DETECT 0x19 /* 10BaseT Transmit Jabber + Detect Counter */ +#define IFE_PHY_EQUALIZER 0x1A /* PHY Equalizer Control and + Status */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY special control and + LED configuration */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control register */ +#define IFE_PHY_HWI_CONTROL 0x1D /* Hardware Integrity Control + (HWI) */ + +#define IFE_PESC_REDUCED_POWER_DOWN_DISABLE 0x2000 /* Defaut 1 = Disable auto + reduced power down */ +#define IFE_PESC_100BTX_POWER_DOWN 0x0400 /* Indicates the power + state of 100BASE-TX */ +#define IFE_PESC_10BTX_POWER_DOWN 0x0200 /* Indicates the power + state of 10BASE-T */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 /* Indicates 10BASE-T + polarity */ +#define IFE_PESC_PHY_ADDR_MASK 0x007C /* Bit 6:2 for sampled PHY + address */ +#define IFE_PESC_SPEED 0x0002 /* Auto-negotiation speed + result 1=100Mbs, 0=10Mbs */ +#define IFE_PESC_DUPLEX 0x0001 /* Auto-negotiation + duplex result 1=Full, 0=Half */ +#define IFE_PESC_POLARITY_REVERSED_SHIFT 8 + +#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 /* 1 = Dyanmic Power Down + disabled */ +#define IFE_PSC_FORCE_POLARITY 0x0020 /* 1=Reversed Polarity, + 0=Normal */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 /* 1=Auto Polarity + Disabled, 0=Enabled */ +#define IFE_PSC_JABBER_FUNC_DISABLE 0x0001 /* 1=Jabber Disabled, + 0=Normal Jabber Operation */ +#define IFE_PSC_FORCE_POLARITY_SHIFT 5 +#define IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT 4 + +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X + feature, default 0=disabled */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, + 0=force MDI */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm + is completed */ +#define IFE_PMC_MDIX_MODE_SHIFT 6 +#define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ + +#define IFE_PHC_HWI_ENABLE 0x8000 /* Enable the HWI + feature */ +#define IFE_PHC_ABILITY_CHECK 0x4000 /* 1= Test Passed, + 0=failed */ +#define IFE_PHC_TEST_EXEC 0x2000 /* PHY launch test pulses + on the wire */ +#define IFE_PHC_HIGHZ 0x0200 /* 1 = Open Circuit */ +#define IFE_PHC_LOWZ 0x0400 /* 1 = Short Circuit */ +#define IFE_PHC_LOW_HIGH_Z_MASK 0x0600 /* Mask for indication + type of problem on the line */ +#define IFE_PHC_DISTANCE_MASK 0x01FF /* Mask for distance to + the cable problem, in 80cm granularity */ +#define IFE_PHC_RESET_ALL_MASK 0x0000 /* Disable HWI */ +#define IFE_PSCL_PROBE_MODE 0x0020 /* LED Probe mode */ +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 + off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + + +#define NUM_DEV_IDS 16 + +#define NODE_ADDRESS_SIZE 6 +#define ETH_LENGTH_OF_ADDRESS 6 + +/* MAC decode size is 128K - This is the size of BAR0 */ +#define MAC_DECODE_SIZE (128 * 1024) + +#define E1000_82542_2_0_REV_ID 2 +#define E1000_82542_2_1_REV_ID 3 +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 +#define E1000_REVISION_3 3 + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +/* The sizes (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* With FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */ +#define ETHERNET_FCS_SIZE 4 +#define MAXIMUM_ETHERNET_PACKET_SIZE \ + (MAXIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define MINIMUM_ETHERNET_PACKET_SIZE \ + (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define CRC_LENGTH ETHERNET_FCS_SIZE +#define MAX_JUMBO_FRAME_SIZE 0x3F00 + +/* 802.1q VLAN Packet Sizes */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */ + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ +#define ETHERNET_IP_TYPE 0x0800 /* IP packets */ +#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */ + +/* Packet Header defines */ +#define IP_PROTOCOL_TCP 6 +#define IP_PROTOCOL_UDP 0x11 + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + */ +#define POLL_IMS_ENABLE_MASK ( \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ) + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXT0 = Receiver Timer Interrupt (ring 0) + * o TXDW = Transmit Descriptor Written Back + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + * o LSC = Link Status Change + */ +#define IMS_ENABLE_MASK ( \ + E1000_IMS_RXT0 | \ + E1000_IMS_TXDW | \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ | \ + E1000_IMS_LSC) + +/* The number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. We + * reserve one of these spots for our directed address, allowing us room for + * E1000_RAR_ENTRIES - 1 multicast addresses. + */ +#define E1000_RAR_ENTRIES 16 + +#define MIN_NUMBER_OF_DESCRIPTORS 8 +#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8 + +/* Receive Descriptor */ +struct e1000_rx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + uint16_t length; /* Length of data DMAed into data buffer */ + uint16_t csum; /* Packet checksum */ + uint8_t status; /* Descriptor status */ + uint8_t errors; /* Descriptor Errors */ + uint16_t special; +}; + +/* Receive Decriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ +#define E1000_RXD_SPC_PRI_SHIFT 0x000D /* Priority is in upper 3 of 16 */ +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_SHIFT 0x000C /* CFI is bit 12 */ + +/* mask to determine if packets should be dropped due to frame errors */ +#define E1000_RXD_ERR_FRAME_ERR_MASK ( \ + E1000_RXD_ERR_CE | \ + E1000_RXD_ERR_SE | \ + E1000_RXD_ERR_SEQ | \ + E1000_RXD_ERR_CXE | \ + E1000_RXD_ERR_RXE) + +/* Transmit Descriptor */ +struct e1000_tx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + } fields; + } upper; +}; + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + uint32_t ip_config; + struct { + uint8_t ipcss; /* IP checksum start */ + uint8_t ipcso; /* IP checksum offset */ + uint16_t ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + uint32_t tcp_config; + struct { + uint8_t tucss; /* TCP checksum start */ + uint8_t tucso; /* TCP checksum offset */ + uint16_t tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + uint32_t cmd_and_length; /* */ + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t hdr_len; /* Header length */ + uint16_t mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct e1000_data_desc { + uint64_t buffer_addr; /* Address of the descriptor's buffer address */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t typ_len_ext; /* */ + uint8_t cmd; /* */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t popts; /* Packet Options */ + uint16_t special; /* */ + } fields; + } upper; +}; + +/* Filters */ +#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ +#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + +/* Receive Address Register */ +struct e1000_rar { + volatile uint32_t low; /* receive address low */ + volatile uint32_t high; /* receive address high */ +}; + +/* The number of entries in the Multicast Table Array (MTA). */ +#define E1000_NUM_MTA_REGISTERS 128 + +/* IPv4 Address Table Entry */ +struct e1000_ipv4_at_entry { + volatile uint32_t ipv4_addr; /* IP Address (RW) */ + volatile uint32_t reserved; +}; + +/* Four wakeup IP addresses are supported */ +#define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4 +#define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX +#define E1000_IP6AT_SIZE 1 + +/* IPv6 Address Table Entry */ +struct e1000_ipv6_at_entry { + volatile uint8_t ipv6_addr[16]; +}; + +/* Flexible Filter Length Table Entry */ +struct e1000_fflt_entry { + volatile uint32_t length; /* Flexible Filter Length (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Mask Table Entry */ +struct e1000_ffmt_entry { + volatile uint32_t mask; /* Flexible Filter Mask (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Value Table Entry */ +struct e1000_ffvt_entry { + volatile uint32_t value; /* Flexible Filter Value (RW) */ + volatile uint32_t reserved; +}; + +/* Four Flexible Filters are supported */ +#define E1000_FLEXIBLE_FILTER_COUNT_MAX 4 + +/* Each Flexible Filter is at most 128 (0x80) bytes in length */ +#define E1000_FLEXIBLE_FILTER_SIZE_MAX 128 + +#define E1000_FFLT_SIZE E1000_FLEXIBLE_FILTER_COUNT_MAX +#define E1000_FFMT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX +#define E1000_FFVT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX + +/* Register Set. (82543, 82544) + * + * Registers are defined to be 32 bits and should be accessed as 32 bit values. + * These registers are physically located on the NIC, but are mapped into the + * host memory address space. + * + * RW - register is both readable and writable + * RO - register is read only + * WO - register is write only + * R/clr - register is read only and is cleared when read + * A - register array + */ +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ +#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ +#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define FEXTNVM_SW_CONFIG 0x0001 +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_FLASH_UPDATES 1000 +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_FLASHT 0x01028 /* FLASH Timer Register */ +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLSWCTL 0x01030 /* FLASH control register */ +#define E1000_FLSWDATA 0x01034 /* FLASH data register */ +#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ +#define E1000_RXDCTL 0x02828 /* RX Descriptor Control - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ +#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ +#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ +#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ +#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ +#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ +#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ +#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ +#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */ +#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */ +#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */ +#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */ +#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */ +#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ +#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ +#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ + +/* Register Set (82542) + * + * Some of the 82542 registers are located at different offsets than they are + * in more current versions of the 8254x. Despite the difference in location, + * the registers function in the same manner. + */ +#define E1000_82542_CTRL E1000_CTRL +#define E1000_82542_STATUS E1000_STATUS +#define E1000_82542_EECD E1000_EECD +#define E1000_82542_EERD E1000_EERD +#define E1000_82542_CTRL_EXT E1000_CTRL_EXT +#define E1000_82542_MDIC E1000_MDIC +#define E1000_82542_FCAL E1000_FCAL +#define E1000_82542_FCAH E1000_FCAH +#define E1000_82542_FCT E1000_FCT +#define E1000_82542_VET E1000_VET +#define E1000_82542_RA 0x00040 +#define E1000_82542_ICR E1000_ICR +#define E1000_82542_ITR E1000_ITR +#define E1000_82542_ICS E1000_ICS +#define E1000_82542_IMS E1000_IMS +#define E1000_82542_IMC E1000_IMC +#define E1000_82542_RCTL E1000_RCTL +#define E1000_82542_RDTR 0x00108 +#define E1000_82542_RDBAL 0x00110 +#define E1000_82542_RDBAH 0x00114 +#define E1000_82542_RDLEN 0x00118 +#define E1000_82542_RDH 0x00120 +#define E1000_82542_RDT 0x00128 +#define E1000_82542_FCRTH 0x00160 +#define E1000_82542_FCRTL 0x00168 +#define E1000_82542_FCTTV E1000_FCTTV +#define E1000_82542_TXCW E1000_TXCW +#define E1000_82542_RXCW E1000_RXCW +#define E1000_82542_MTA 0x00200 +#define E1000_82542_TCTL E1000_TCTL +#define E1000_82542_TIPG E1000_TIPG +#define E1000_82542_TDBAL 0x00420 +#define E1000_82542_TDBAH 0x00424 +#define E1000_82542_TDLEN 0x00428 +#define E1000_82542_TDH 0x00430 +#define E1000_82542_TDT 0x00438 +#define E1000_82542_TIDV 0x00440 +#define E1000_82542_TBT E1000_TBT +#define E1000_82542_AIT E1000_AIT +#define E1000_82542_VFTA 0x00600 +#define E1000_82542_LEDCTL E1000_LEDCTL +#define E1000_82542_PBA E1000_PBA +#define E1000_82542_RXDCTL E1000_RXDCTL +#define E1000_82542_RADV E1000_RADV +#define E1000_82542_RSRPD E1000_RSRPD +#define E1000_82542_TXDMAC E1000_TXDMAC +#define E1000_82542_TXDCTL E1000_TXDCTL +#define E1000_82542_TADV E1000_TADV +#define E1000_82542_TSPMT E1000_TSPMT +#define E1000_82542_CRCERRS E1000_CRCERRS +#define E1000_82542_ALGNERRC E1000_ALGNERRC +#define E1000_82542_SYMERRS E1000_SYMERRS +#define E1000_82542_RXERRC E1000_RXERRC +#define E1000_82542_MPC E1000_MPC +#define E1000_82542_SCC E1000_SCC +#define E1000_82542_ECOL E1000_ECOL +#define E1000_82542_MCC E1000_MCC +#define E1000_82542_LATECOL E1000_LATECOL +#define E1000_82542_COLC E1000_COLC +#define E1000_82542_DC E1000_DC +#define E1000_82542_TNCRS E1000_TNCRS +#define E1000_82542_SEC E1000_SEC +#define E1000_82542_CEXTERR E1000_CEXTERR +#define E1000_82542_RLEC E1000_RLEC +#define E1000_82542_XONRXC E1000_XONRXC +#define E1000_82542_XONTXC E1000_XONTXC +#define E1000_82542_XOFFRXC E1000_XOFFRXC +#define E1000_82542_XOFFTXC E1000_XOFFTXC +#define E1000_82542_FCRUC E1000_FCRUC +#define E1000_82542_PRC64 E1000_PRC64 +#define E1000_82542_PRC127 E1000_PRC127 +#define E1000_82542_PRC255 E1000_PRC255 +#define E1000_82542_PRC511 E1000_PRC511 +#define E1000_82542_PRC1023 E1000_PRC1023 +#define E1000_82542_PRC1522 E1000_PRC1522 +#define E1000_82542_GPRC E1000_GPRC +#define E1000_82542_BPRC E1000_BPRC +#define E1000_82542_MPRC E1000_MPRC +#define E1000_82542_GPTC E1000_GPTC +#define E1000_82542_GORCL E1000_GORCL +#define E1000_82542_GORCH E1000_GORCH +#define E1000_82542_GOTCL E1000_GOTCL +#define E1000_82542_GOTCH E1000_GOTCH +#define E1000_82542_RNBC E1000_RNBC +#define E1000_82542_RUC E1000_RUC +#define E1000_82542_RFC E1000_RFC +#define E1000_82542_ROC E1000_ROC +#define E1000_82542_RJC E1000_RJC +#define E1000_82542_MGTPRC E1000_MGTPRC +#define E1000_82542_MGTPDC E1000_MGTPDC +#define E1000_82542_MGTPTC E1000_MGTPTC +#define E1000_82542_TORL E1000_TORL +#define E1000_82542_TORH E1000_TORH +#define E1000_82542_TOTL E1000_TOTL +#define E1000_82542_TOTH E1000_TOTH +#define E1000_82542_TPR E1000_TPR +#define E1000_82542_TPT E1000_TPT +#define E1000_82542_PTC64 E1000_PTC64 +#define E1000_82542_PTC127 E1000_PTC127 +#define E1000_82542_PTC255 E1000_PTC255 +#define E1000_82542_PTC511 E1000_PTC511 +#define E1000_82542_PTC1023 E1000_PTC1023 +#define E1000_82542_PTC1522 E1000_PTC1522 +#define E1000_82542_MPTC E1000_MPTC +#define E1000_82542_BPTC E1000_BPTC +#define E1000_82542_TSCTC E1000_TSCTC +#define E1000_82542_TSCTFC E1000_TSCTFC +#define E1000_82542_RXCSUM E1000_RXCSUM +#define E1000_82542_WUC E1000_WUC +#define E1000_82542_WUFC E1000_WUFC +#define E1000_82542_WUS E1000_WUS +#define E1000_82542_MANC E1000_MANC +#define E1000_82542_IPAV E1000_IPAV +#define E1000_82542_IP4AT E1000_IP4AT +#define E1000_82542_IP6AT E1000_IP6AT +#define E1000_82542_WUPL E1000_WUPL +#define E1000_82542_WUPM E1000_WUPM +#define E1000_82542_FFLT E1000_FFLT +#define E1000_82542_FFMT E1000_FFMT +#define E1000_82542_FFVT E1000_FFVT + +/* Statistics counters collected by the MAC */ +struct e1000_hw_stats { + uint64_t crcerrs; + uint64_t algnerrc; + uint64_t symerrs; + uint64_t rxerrc; + uint64_t mpc; + uint64_t scc; + uint64_t ecol; + uint64_t mcc; + uint64_t latecol; + uint64_t colc; + uint64_t dc; + uint64_t tncrs; + uint64_t sec; + uint64_t cexterr; + uint64_t rlec; + uint64_t xonrxc; + uint64_t xontxc; + uint64_t xoffrxc; + uint64_t xofftxc; + uint64_t fcruc; + uint64_t prc64; + uint64_t prc127; + uint64_t prc255; + uint64_t prc511; + uint64_t prc1023; + uint64_t prc1522; + uint64_t gprc; + uint64_t bprc; + uint64_t mprc; + uint64_t gptc; + uint64_t gorcl; + uint64_t gorch; + uint64_t gotcl; + uint64_t gotch; + uint64_t rnbc; + uint64_t ruc; + uint64_t rfc; + uint64_t roc; + uint64_t rjc; + uint64_t mgprc; + uint64_t mgpdc; + uint64_t mgptc; + uint64_t torl; + uint64_t torh; + uint64_t totl; + uint64_t toth; + uint64_t tpr; + uint64_t tpt; + uint64_t ptc64; + uint64_t ptc127; + uint64_t ptc255; + uint64_t ptc511; + uint64_t ptc1023; + uint64_t ptc1522; + uint64_t mptc; + uint64_t bptc; + uint64_t tsctc; + uint64_t tsctfc; +}; + +struct e1000_eeprom_info { + e1000_eeprom_type type; + uint16_t word_size; + uint16_t opcode_bits; + uint16_t address_bits; + uint16_t delay_usec; + uint16_t page_size; + boolean_t use_eerd; + boolean_t use_eewr; +}; + +typedef enum { + e1000_smart_speed_default = 0, + e1000_smart_speed_on, + e1000_smart_speed_off +} e1000_smart_speed; + +typedef enum { + e1000_dsp_config_disabled = 0, + e1000_dsp_config_enabled, + e1000_dsp_config_activated, + e1000_dsp_config_undefined = 0xFF +} e1000_dsp_config; + +typedef enum { + e1000_ms_hw_default = 0, + e1000_ms_force_master, + e1000_ms_force_slave, + e1000_ms_auto +} e1000_ms_type; + +typedef enum { + e1000_ffe_config_enabled = 0, + e1000_ffe_config_active, + e1000_ffe_config_blocked +} e1000_ffe_config; + + +/* Structure containing variables used by the shared code (e1000_hw.c) */ +struct e1000_hw { + pci_dev_t pdev; + uint8_t *hw_addr; + e1000_mac_type mac_type; + e1000_phy_type phy_type; + uint32_t phy_init_script; + uint32_t txd_cmd; + e1000_media_type media_type; + e1000_lan_loc lan_loc; + e1000_fc_type fc; + e1000_bus_type bus_type; +#if 0 + e1000_bus_speed bus_speed; + e1000_bus_width bus_width; + uint32_t io_base; +#endif + uint32_t asf_firmware_present; + uint32_t eeprom_semaphore_present; + uint32_t swfw_sync_present; + uint32_t swfwhw_semaphore_present; + struct e1000_eeprom_info eeprom; + e1000_ms_type master_slave; + e1000_ms_type original_master_slave; + e1000_ffe_config ffe_config_state; + uint32_t phy_id; + uint32_t phy_revision; + uint32_t phy_addr; + uint32_t original_fc; + uint32_t txcw; + uint32_t autoneg_failed; +#if 0 + uint32_t max_frame_size; + uint32_t min_frame_size; + uint32_t mc_filter_type; + uint32_t num_mc_addrs; + uint32_t collision_delta; + uint32_t tx_packet_delta; + uint32_t ledctl_default; + uint32_t ledctl_mode1; + uint32_t ledctl_mode2; +#endif + uint16_t autoneg_advertised; + uint16_t pci_cmd_word; + uint16_t fc_high_water; + uint16_t fc_low_water; + uint16_t fc_pause_time; +#if 0 + uint16_t current_ifs_val; + uint16_t ifs_min_val; + uint16_t ifs_max_val; + uint16_t ifs_step_size; + uint16_t ifs_ratio; +#endif + uint16_t device_id; + uint16_t vendor_id; + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; + uint8_t revision_id; + uint8_t autoneg; + uint8_t mdix; + uint8_t forced_speed_duplex; + uint8_t wait_autoneg_complete; + uint8_t dma_fairness; +#if 0 + uint8_t perm_mac_addr[NODE_ADDRESS_SIZE]; +#endif + boolean_t disable_polarity_correction; + boolean_t speed_downgraded; + boolean_t get_link_status; + boolean_t tbi_compatibility_en; + boolean_t tbi_compatibility_on; + boolean_t fc_strict_ieee; + boolean_t fc_send_xon; + boolean_t report_tx_early; + boolean_t phy_reset_disable; + boolean_t initialize_hw_bits_disable; +#if 0 + boolean_t adaptive_ifs; + boolean_t ifs_params_forced; + boolean_t in_ifs_mode; +#endif + e1000_smart_speed smart_speed; + e1000_dsp_config dsp_config_state; +}; + +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ +#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM + read/write registers */ +#define E1000_EEPROM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start + operation */ +#define E1000_EEPROM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write + complete */ +#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ +#define EEPROM_RESERVED_WORD 0xFFFF + +/* Register Bit Masks */ +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ +#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ +#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ + +/* Constants used to intrepret the masked PCI-X bus speed. */ +#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ +#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */ +#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */ + +/* EEPROM/Flash Control */ +#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ +#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ +#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ +#define E1000_EECD_FWE_MASK 0x00000030 +#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ +#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ +#define E1000_EECD_FWE_SHIFT 4 +#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ +#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ +#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type + * (0-small, 1-large) */ + +#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ +#ifndef E1000_EEPROM_GRANT_ATTEMPTS +#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ +#endif +#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ +#define E1000_EECD_SIZE_EX_SHIFT 11 +#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ +#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ +#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ +#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ +#define E1000_EECD_SECVAL_SHIFT 22 +#define E1000_STM_OPCODE 0xDB00 +#define E1000_HICR_FW_RESET 0xC0 + +#define E1000_SHADOW_RAM_WORDS 2048 +#define E1000_ICH_NVM_SIG_WORD 0x13 +#define E1000_ICH_NVM_SIG_MASK 0xC0 + +/* EEPROM Read */ +#define E1000_EERD_START 0x00000001 /* Start Read */ +#define E1000_EERD_DONE 0x00000010 /* Read Done */ +#define E1000_EERD_ADDR_SHIFT 8 +#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */ +#define E1000_EERD_DATA_SHIFT 16 +#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */ + +/* EEPROM Commands - Microwire */ +#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ +#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ +#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ +#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ + +/* EEPROM Commands - SPI */ +#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */ +#define EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Enable latch */ +#define EEPROM_WRDI_OPCODE_SPI 0x04 /* EEPROM reset Write Enable latch */ +#define EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status register */ +#define EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status register */ +#define EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */ +#define EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */ +#define EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */ + +/* EEPROM Size definitions */ +#define EEPROM_WORD_SIZE_SHIFT 6 +#define EEPROM_SIZE_SHIFT 10 +#define EEPROM_SIZE_MASK 0x1C00 + +/* EEPROM Word Offsets */ +#define EEPROM_COMPAT 0x0003 +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_VERSION 0x0005 +#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude + adjustment. */ +#define EEPROM_PHY_CLASS_WORD 0x0007 +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010 +#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 +#define EEPROM_INIT_3GIO_3 0x001A +#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020 +#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 +#define EEPROM_CFG 0x0012 +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ +#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ + +/* Extended Device Control */ +#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */ +#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */ +#define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN +#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */ +#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable + Pin 4 */ +#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable + Pin 5 */ +#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ +#define E1000_CTRL_EXT_SWDPIN6 0x00000040 /* SWDPIN 6 value */ +#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ +#define E1000_CTRL_EXT_SWDPIN7 0x00000080 /* SWDPIN 7 value */ +#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ +#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SWDPIO6 0x00000400 /* SWDPIN 6 Input or output */ +#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */ +#define E1000_CTRL_EXT_SWDPIO7 0x00000800 /* SWDPIN 7 Input or output */ +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ +#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 +#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000 +#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000 +#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000 +#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000 +#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000 + +/* MDI Control */ +#define E1000_MDIC_DATA_MASK 0x0000FFFF +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_INT_EN 0x20000000 +#define E1000_MDIC_ERROR 0x40000000 + +#define E1000_PHY_CTRL_SPD_EN 0x00000001 +#define E1000_PHY_CTRL_D0A_LPLU 0x00000002 +#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004 +#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008 +#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040 +#define E1000_PHY_CTRL_B2B_EN 0x00000080 +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_IVRT 0x00000040 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED1_MODE_MASK 0x00000F00 +#define E1000_LEDCTL_LED1_MODE_SHIFT 8 +#define E1000_LEDCTL_LED1_IVRT 0x00004000 +#define E1000_LEDCTL_LED1_BLINK 0x00008000 +#define E1000_LEDCTL_LED2_MODE_MASK 0x000F0000 +#define E1000_LEDCTL_LED2_MODE_SHIFT 16 +#define E1000_LEDCTL_LED2_IVRT 0x00400000 +#define E1000_LEDCTL_LED2_BLINK 0x00800000 +#define E1000_LEDCTL_LED3_MODE_MASK 0x0F000000 +#define E1000_LEDCTL_LED3_MODE_SHIFT 24 +#define E1000_LEDCTL_LED3_IVRT 0x40000000 +#define E1000_LEDCTL_LED3_BLINK 0x80000000 + +#define E1000_LEDCTL_MODE_LINK_10_1000 0x0 +#define E1000_LEDCTL_MODE_LINK_100_1000 0x1 +#define E1000_LEDCTL_MODE_LINK_UP 0x2 +#define E1000_LEDCTL_MODE_ACTIVITY 0x3 +#define E1000_LEDCTL_MODE_LINK_ACTIVITY 0x4 +#define E1000_LEDCTL_MODE_LINK_10 0x5 +#define E1000_LEDCTL_MODE_LINK_100 0x6 +#define E1000_LEDCTL_MODE_LINK_1000 0x7 +#define E1000_LEDCTL_MODE_PCIX_MODE 0x8 +#define E1000_LEDCTL_MODE_FULL_DUPLEX 0x9 +#define E1000_LEDCTL_MODE_COLLISION 0xA +#define E1000_LEDCTL_MODE_BUS_SPEED 0xB +#define E1000_LEDCTL_MODE_BUS_SIZE 0xC +#define E1000_LEDCTL_MODE_PAUSED 0xD +#define E1000_LEDCTL_MODE_LED_ON 0xE +#define E1000_LEDCTL_MODE_LED_OFF 0xF + +/* Receive Address */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_SRPD 0x00010000 + +/* Interrupt Cause Set */ +#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_ICS_SRPD E1000_ICR_SRPD + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_SRPD E1000_ICR_SRPD + +/* Interrupt Mask Clear */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMC_SRPD E1000_ICR_SRPD + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ + +/* SW_W_SYNC definitions */ +#define E1000_SWFW_EEP_SM 0x0001 +#define E1000_SWFW_PHY0_SM 0x0002 +#define E1000_SWFW_PHY1_SM 0x0004 +#define E1000_SWFW_MAC_CSR_SM 0x0008 + +/* Receive Descriptor */ +#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */ +#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */ +#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */ +#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */ +#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */ + +/* Flow Control */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ + +/* Receive Descriptor Control */ +#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */ +#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */ +#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */ +#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */ + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */ +#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ +#define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc. + still to be processed. */ + +/* Transmit Configuration Word */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */ +#define E1000_TXCW_NP 0x00008000 /* TXCW next page */ +#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */ +#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ + +/* Receive Configuration Word */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_CC 0x10000000 /* Receive config change */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ +#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ + +/* Receive Checksum Control */ +#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */ + +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ + +/* Wake Up Filter Control */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ +#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */ +#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */ +#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */ +#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */ +#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */ +#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Wake Up Status */ +#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */ +#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */ +#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */ +#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */ +#define E1000_WUS_BC 0x00000010 /* Broadcast Received */ +#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */ +#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */ +#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */ +#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */ +#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */ +#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */ +#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */ +#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ +#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ +#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ +#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ +#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ +#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery + * Filtering */ +#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ +#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ +#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ +#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ +#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ +#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ + +#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ +#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ + +/* Wake Up Packet Length */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ + +#define E1000_MDALIGN 4096 + +/* EEPROM Commands */ +#define EEPROM_READ_OPCODE 0x6 /* EERPOM read opcode */ +#define EEPROM_WRITE_OPCODE 0x5 /* EERPOM write opcode */ +#define EEPROM_ERASE_OPCODE 0x7 /* EERPOM erase opcode */ +#define EEPROM_EWEN_OPCODE 0x13 /* EERPOM erase/write enable */ +#define EEPROM_EWDS_OPCODE 0x10 /* EERPOM erast/write disable */ + +/* EEPROM Word Offsets */ +#define EEPROM_COMPAT 0x0003 +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ + (ID_LED_OFF1_OFF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEF1_DEF2 0x1 +#define ID_LED_DEF1_ON2 0x2 +#define ID_LED_DEF1_OFF2 0x3 +#define ID_LED_ON1_DEF2 0x4 +#define ID_LED_ON1_ON2 0x5 +#define ID_LED_ON1_OFF2 0x6 +#define ID_LED_OFF1_DEF2 0x7 +#define ID_LED_OFF1_ON2 0x8 +#define ID_LED_OFF1_OFF2 0x9 + +/* Mask bits for fields in Word 0x03 of the EEPROM */ +#define EEPROM_COMPAT_SERVER 0x0400 +#define EEPROM_COMPAT_CLIENT 0x0200 + +/* Mask bits for fields in Word 0x0a of the EEPROM */ +#define EEPROM_WORD0A_ILOS 0x0010 +#define EEPROM_WORD0A_SWDPIO 0x01E0 +#define EEPROM_WORD0A_LRST 0x0200 +#define EEPROM_WORD0A_FD 0x0400 +#define EEPROM_WORD0A_66MHZ 0x0800 + +/* Mask bits for fields in Word 0x0f of the EEPROM */ +#define EEPROM_WORD0F_PAUSE_MASK 0x3000 +#define EEPROM_WORD0F_PAUSE 0x1000 +#define EEPROM_WORD0F_ASM_DIR 0x2000 +#define EEPROM_WORD0F_ANE 0x0800 +#define EEPROM_WORD0F_SWPDIO_EXT 0x00F0 + +/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ +#define EEPROM_SUM 0xBABA + +/* EEPROM Map defines (WORD OFFSETS)*/ +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_PBA_BYTE_1 8 + +/* EEPROM Map Sizes (Byte Counts) */ +#define PBA_SIZE 4 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 0xF +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 63 +#define E1000_COLLISION_DISTANCE_82542 64 +#define E1000_FDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_HDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_GB_HDX_COLLISION_DISTANCE 512 +#define E1000_COLD_SHIFT 12 + +/* The number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Default values for the transmit IPG register */ +#define DEFAULT_82542_TIPG_IPGT 10 +#define DEFAULT_82543_TIPG_IPGT_FIBER 9 +#define DEFAULT_82543_TIPG_IPGT_COPPER 8 + +#define E1000_TIPG_IPGT_MASK 0x000003FF +#define E1000_TIPG_IPGR1_MASK 0x000FFC00 +#define E1000_TIPG_IPGR2_MASK 0x3FF00000 + +#define DEFAULT_82542_TIPG_IPGR1 2 +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#define DEFAULT_82542_TIPG_IPGR2 10 +#define DEFAULT_82543_TIPG_IPGR2 6 +#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7 +#define E1000_TIPG_IPGR2_SHIFT 20 + +#define E1000_TXDMAC_DPP 0x00000001 + +/* Adaptive IFS defines */ +#define TX_THRESHOLD_START 8 +#define TX_THRESHOLD_INCREMENT 10 +#define TX_THRESHOLD_DECREMENT 1 +#define TX_THRESHOLD_STOP 190 +#define TX_THRESHOLD_DISABLE 0 +#define TX_THRESHOLD_TIMER_MS 10000 +#define MIN_NUM_XMITS 1000 +#define IFS_MAX 80 +#define IFS_STEP 10 +#define IFS_MIN 40 +#define IFS_RATIO 4 + +/* PBA constants */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_38K 0x0026 +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* The historical defaults for the flow control values are given below. */ +#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */ +#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ +#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ + +/* Flow Control High-Watermark: 43464 bytes */ +#define E1000_FC_HIGH_THRESH 0xA9C8 +/* Flow Control Low-Watermark: 43456 bytes */ +#define E1000_FC_LOW_THRESH 0xA9C0 +/* Flow Control Pause Time: 858 usec */ +#define E1000_FC_PAUSE_TIME 0x0680 + +/* PCIX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA + +#define PCIX_COMMAND_MMRBC_MASK 0x000C +#define PCIX_COMMAND_MMRBC_SHIFT 0x2 +#define PCIX_STATUS_HI_MMRBC_MASK 0x0060 +#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5 +#define PCIX_STATUS_HI_MMRBC_4K 0x3 +#define PCIX_STATUS_HI_MMRBC_2K 0x2 + +/* The number of bits that we need to shift right to move the "pause" + * bits from the EEPROM (bits 13:12) to the "pause" (bits 8:7) field + * in the TXCW register + */ +#define PAUSE_SHIFT 5 + +/* The number of bits that we need to shift left to move the "SWDPIO" + * bits from the EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field + * in the CTRL register + */ +#define SWDPIO_SHIFT 17 + +/* The number of bits that we need to shift left to move the "SWDPIO_EXT" + * bits from the EEPROM word F (bits 7:4) to the bits 11:8 of The + * Extended CTRL register. + * in the CTRL register + */ +#define SWDPIO__EXT_SHIFT 4 + +/* The number of bits that we need to shift left to move the "ILOS" + * bit from the EEPROM (bit 4) to the "ILOS" (bit 7) field + * in the CTRL register + */ +#define ILOS_SHIFT 3 + +#define RECEIVE_BUFFER_ALIGN_SIZE (256) + +/* The number of milliseconds we wait for auto-negotiation to complete */ +#define LINK_UP_TIMEOUT 500 + +#define E1000_TX_BUFFER_SIZE ((uint32_t)1514) + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +/* TBI_ACCEPT macro definition: + * + * This macro requires: + * adapter = a pointer to struct e1000_hw + * status = the 8 bit status field of the RX descriptor with EOP set + * error = the 8 bit error field of the RX descriptor with EOP set + * length = the sum of all the length fields of the RX descriptors that + * make up the current frame + * last_byte = the last byte of the frame DMAed by the hardware + * max_frame_length = the maximum frame length we want to accept. + * min_frame_length = the minimum frame length we want to accept. + * + * This macro is a conditional that should be used in the interrupt + * handler's Rx processing routine when RxErrors have been detected. + * + * Typical use: + * ... + * if (TBI_ACCEPT) { + * accept_frame = TRUE; + * e1000_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = FALSE; + * } + * ... + */ + +#define TBI_ACCEPT(adapter, status, errors, length, last_byte) \ + ((adapter)->tbi_compatibility_on && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + (((status) & E1000_RXD_STAT_VP) ? \ + (((length) > ((adapter)->min_frame_size - VLAN_TAG_SIZE)) && \ + ((length) <= ((adapter)->max_frame_size + 1))) : \ + (((length) > (adapter)->min_frame_size) && \ + ((length) <= ((adapter)->max_frame_size + VLAN_TAG_SIZE + 1))))) + +/* Structures, enums, and macros for the PHY */ + +/* Bit definitions for the Management Data IO (MDIO) and Management Data + * Clock (MDC) pins in the Device Control Register. + */ +#define E1000_CTRL_PHY_RESET_DIR E1000_CTRL_SWDPIO0 +#define E1000_CTRL_PHY_RESET E1000_CTRL_SWDPIN0 +#define E1000_CTRL_MDIO_DIR E1000_CTRL_SWDPIO2 +#define E1000_CTRL_MDIO E1000_CTRL_SWDPIN2 +#define E1000_CTRL_MDC_DIR E1000_CTRL_SWDPIO3 +#define E1000_CTRL_MDC E1000_CTRL_SWDPIN3 +#define E1000_CTRL_PHY_RESET_DIR4 E1000_CTRL_EXT_SDP4_DIR +#define E1000_CTRL_PHY_RESET4 E1000_CTRL_EXT_SDP4_DATA + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ +#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ + +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ + +/* M88EC018 Rev 2 specific DownShift settings */ +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_1X 0x0000 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_2X 0x0200 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_3X 0x0400 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_4X 0x0600 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_6X 0x0A00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X 0x0C00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X 0x0E00 + +/* IGP01E1000 specifics */ +#define IGP01E1000_IEEE_REGS_PAGE 0x0000 +#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300 +#define IGP01E1000_IEEE_FORCE_GIGA 0x0140 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */ +#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */ +#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */ + +/* IGP01E1000 AGC Registers - stores the cable length values*/ +#define IGP01E1000_PHY_AGC_A 0x1172 +#define IGP01E1000_PHY_AGC_B 0x1272 +#define IGP01E1000_PHY_AGC_C 0x1472 +#define IGP01E1000_PHY_AGC_D 0x1872 + +/* IGP01E1000 Specific Port Config Register - R/W */ +#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010 +#define IGP01E1000_PSCFR_PRE_EN 0x0020 +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 +#define IGP01E1000_PSCFR_DISABLE_TPLOOPBACK 0x0100 +#define IGP01E1000_PSCFR_DISABLE_JABBER 0x0400 +#define IGP01E1000_PSCFR_DISABLE_TRANSMIT 0x2000 +/* IGP02E1000 AGC Registers for cable length values */ +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 + +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* Enable LPLU in + non-D0a modes */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* Enable LPLU in + D0a mode */ + +/* IGP01E1000 DSP Reset Register */ +#define IGP01E1000_PHY_DSP_RESET 0x1F33 +#define IGP01E1000_PHY_DSP_SET 0x1F71 +#define IGP01E1000_PHY_DSP_FFE 0x1F35 + +#define IGP01E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_CHANNEL_NUM 4 + +#define IGP01E1000_PHY_AGC_PARAM_A 0x1171 +#define IGP01E1000_PHY_AGC_PARAM_B 0x1271 +#define IGP01E1000_PHY_AGC_PARAM_C 0x1471 +#define IGP01E1000_PHY_AGC_PARAM_D 0x1871 + +#define IGP01E1000_PHY_EDAC_MU_INDEX 0xC000 +#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000 + +#define IGP01E1000_PHY_ANALOG_TX_STATE 0x2890 +#define IGP01E1000_PHY_ANALOG_CLASS_A 0x2000 +#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE 0x0004 +#define IGP01E1000_PHY_DSP_FFE_CM_CP 0x0069 + +#define IGP01E1000_PHY_DSP_FFE_DEFAULT 0x002A +/* IGP01E1000 PCS Initialization register - stores the polarity status when + * speed = 1000 Mbps. */ +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_PCS_CTRL_REG 0x00B5 + +#define IGP01E1000_ANALOG_REGS_PAGE 0x20C0 + +/* IGP01E1000 GMII FIFO Register */ +#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed + * on Link-Up */ +#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */ + +/* IGP01E1000 Analog Register */ +#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1 +#define IGP01E1000_ANALOG_FUSE_STATUS 0x20D0 +#define IGP01E1000_ANALOG_FUSE_CONTROL 0x20DC +#define IGP01E1000_ANALOG_FUSE_BYPASS 0x20DE + +#define IGP01E1000_ANALOG_FUSE_POLY_MASK 0xF000 +#define IGP01E1000_ANALOG_FUSE_FINE_MASK 0x0F80 +#define IGP01E1000_ANALOG_FUSE_COARSE_MASK 0x0070 +#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED 0x0100 +#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002 + +#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH 0x0040 +#define IGP01E1000_ANALOG_FUSE_COARSE_10 0x0010 +#define IGP01E1000_ANALOG_FUSE_FINE_1 0x0080 +#define IGP01E1000_ANALOG_FUSE_FINE_10 0x0500 + +/* IGP01E1000 Specific Port Control Register - R/W */ +#define IGP01E1000_PSCR_TP_LOOPBACK 0x0010 +#define IGP01E1000_PSCR_CORRECT_NC_SCMBLR 0x0200 +#define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400 +#define IGP01E1000_PSCR_FLIP_CHIP 0x0800 +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */ +/* GG82563 PHY Specific Status Register (Page 0, Register 16 */ +#define GG82563_PSCR_DISABLE_JABBER 0x0001 /* 1=Disable Jabber */ +#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Polarity Reversal + Disabled */ +#define GG82563_PSCR_POWER_DOWN 0x0004 /* 1=Power Down */ +#define GG82563_PSCR_COPPER_TRANSMITER_DISABLE 0x0008 /* 1=Transmitter + Disabled */ +#define GG82563_PSCR_CROSSOVER_MODE_MASK 0x0060 +#define GG82563_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI + configuration */ +#define GG82563_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX + configuration */ +#define GG82563_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Automatic + crossover */ +#define GG82563_PSCR_ENALBE_EXTENDED_DISTANCE 0x0080 /* 1=Enable Extended + Distance */ +#define GG82563_PSCR_ENERGY_DETECT_MASK 0x0300 +#define GG82563_PSCR_ENERGY_DETECT_OFF 0x0000 /* 00,01=Off */ +#define GG82563_PSCR_ENERGY_DETECT_RX 0x0200 /* 10=Sense on Rx only + (Energy Detect) */ +#define GG82563_PSCR_ENERGY_DETECT_RX_TM 0x0300 /* 11=Sense and Tx NLP */ +#define GG82563_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force Link Good */ +#define GG82563_PSCR_DOWNSHIFT_ENABLE 0x0800 /* 1=Enable Downshift */ +#define GG82563_PSCR_DOWNSHIFT_COUNTER_MASK 0x7000 +#define GG82563_PSCR_DOWNSHIFT_COUNTER_SHIFT 12 + +/* PHY Specific Status Register (Page 0, Register 17) */ +#define GG82563_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define GG82563_PSSR_POLARITY 0x0002 /* 1=Polarity Reversed */ +#define GG82563_PSSR_LINK 0x0008 /* 1=Link is Up */ +#define GG82563_PSSR_ENERGY_DETECT 0x0010 /* 1=Sleep, 0=Active */ +#define GG82563_PSSR_DOWNSHIFT 0x0020 /* 1=Downshift */ +#define GG82563_PSSR_CROSSOVER_STATUS 0x0040 /* 1=MDIX, 0=MDI */ +#define GG82563_PSSR_RX_PAUSE_ENABLED 0x0100 /* 1=Receive Pause Enabled */ +#define GG82563_PSSR_TX_PAUSE_ENABLED 0x0200 /* 1=Transmit Pause Enabled */ +#define GG82563_PSSR_LINK_UP 0x0400 /* 1=Link Up */ +#define GG82563_PSSR_SPEED_DUPLEX_RESOLVED 0x0800 /* 1=Resolved */ +#define GG82563_PSSR_PAGE_RECEIVED 0x1000 /* 1=Page Received */ +#define GG82563_PSSR_DUPLEX 0x2000 /* 1-Full-Duplex */ +#define GG82563_PSSR_SPEED_MASK 0xC000 +#define GG82563_PSSR_SPEED_10MBPS 0x0000 /* 00=10Mbps */ +#define GG82563_PSSR_SPEED_100MBPS 0x4000 /* 01=100Mbps */ +#define GG82563_PSSR_SPEED_1000MBPS 0x8000 /* 10=1000Mbps */ + +/* PHY Specific Status Register 2 (Page 0, Register 19) */ +#define GG82563_PSSR2_JABBER 0x0001 /* 1=Jabber */ +#define GG82563_PSSR2_POLARITY_CHANGED 0x0002 /* 1=Polarity Changed */ +#define GG82563_PSSR2_ENERGY_DETECT_CHANGED 0x0010 /* 1=Energy Detect Changed */ +#define GG82563_PSSR2_DOWNSHIFT_INTERRUPT 0x0020 /* 1=Downshift Detected */ +#define GG82563_PSSR2_MDI_CROSSOVER_CHANGE 0x0040 /* 1=Crossover Changed */ +#define GG82563_PSSR2_FALSE_CARRIER 0x0100 /* 1=False Carrier */ +#define GG82563_PSSR2_SYMBOL_ERROR 0x0200 /* 1=Symbol Error */ +#define GG82563_PSSR2_LINK_STATUS_CHANGED 0x0400 /* 1=Link Status Changed */ +#define GG82563_PSSR2_AUTO_NEG_COMPLETED 0x0800 /* 1=Auto-Neg Completed */ +#define GG82563_PSSR2_PAGE_RECEIVED 0x1000 /* 1=Page Received */ +#define GG82563_PSSR2_DUPLEX_CHANGED 0x2000 /* 1=Duplex Changed */ +#define GG82563_PSSR2_SPEED_CHANGED 0x4000 /* 1=Speed Changed */ +#define GG82563_PSSR2_AUTO_NEG_ERROR 0x8000 /* 1=Auto-Neg Error */ + +/* PHY Specific Control Register 2 (Page 0, Register 26) */ +#define GG82563_PSCR2_10BT_POLARITY_FORCE 0x0002 /* 1=Force Negative + Polarity */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_MASK 0x000C +#define GG82563_PSCR2_1000MB_TEST_SELECT_NORMAL 0x0000 /* 00,01=Normal + Operation */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_112NS 0x0008 /* 10=Select 112ns + Sequence */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_16NS 0x000C /* 11=Select 16ns + Sequence */ +#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 /* 1=Reverse + Auto-Negotiation */ +#define GG82563_PSCR2_1000BT_DISABLE 0x4000 /* 1=Disable + 1000BASE-T */ +#define GG82563_PSCR2_TRANSMITER_TYPE_MASK 0x8000 +#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_B 0x0000 /* 0=Class B */ +#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_A 0x8000 /* 1=Class A */ + +/* MAC Specific Control Register (Page 2, Register 21) */ +/* Tx clock speed for Link Down and 1000BASE-T for the following speeds */ +#define GG82563_MSCR_TX_CLK_MASK 0x0007 +#define GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ 0x0004 +#define GG82563_MSCR_TX_CLK_100MBPS_25MHZ 0x0005 +#define GG82563_MSCR_TX_CLK_1000MBPS_2_5MHZ 0x0006 +#define GG82563_MSCR_TX_CLK_1000MBPS_25MHZ 0x0007 + +#define GG82563_MSCR_ASSERT_CRS_ON_TX 0x0010 /* 1=Assert */ + +/* DSP Distance Register (Page 5, Register 26) */ +#define GG82563_DSPD_CABLE_LENGTH 0x0007 /* 0 = <50M; + 1 = 50-80M; + 2 = 80-110M; + 3 = 110-140M; + 4 = >140M */ + +/* Kumeran Mode Control Register (Page 193, Register 16) */ +#define GG82563_KMCR_PHY_LEDS_EN 0x0020 /* 1=PHY LEDs, + 0=Kumeran Inband LEDs */ +#define GG82563_KMCR_FORCE_LINK_UP 0x0040 /* 1=Force Link Up */ +#define GG82563_KMCR_SUPPRESS_SGMII_EPD_EXT 0x0080 +#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT_MASK 0x0400 +#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT 0x0400 /* 1=6.25MHz, + 0=0.8MHz */ +#define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800 + +/* Power Management Control Register (Page 193, Register 20) */ +#define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE 0x0001 /* 1=Enalbe SERDES + Electrical Idle */ +#define GG82563_PMCR_DISABLE_PORT 0x0002 /* 1=Disable Port */ +#define GG82563_PMCR_DISABLE_SERDES 0x0004 /* 1=Disable SERDES */ +#define GG82563_PMCR_REVERSE_AUTO_NEG 0x0008 /* 1=Enable Reverse + Auto-Negotiation */ +#define GG82563_PMCR_DISABLE_1000_NON_D0 0x0010 /* 1=Disable 1000Mbps + Auto-Neg in non D0 */ +#define GG82563_PMCR_DISABLE_1000 0x0020 /* 1=Disable 1000Mbps + Auto-Neg Always */ +#define GG82563_PMCR_REVERSE_AUTO_NEG_D0A 0x0040 /* 1=Enable D0a + Reverse Auto-Negotiation */ +#define GG82563_PMCR_FORCE_POWER_STATE 0x0080 /* 1=Force Power State */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_MASK 0x0300 +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_DR 0x0000 /* 00=Dr */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0U 0x0100 /* 01=D0u */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0A 0x0200 /* 10=D0a */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D3 0x0300 /* 11=D3 */ + +/* In-Band Control Register (Page 194, Register 18) */ +#define GG82563_ICR_DIS_PADDING 0x0010 /* Disable Padding Use */ + + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define GG82563_PAGE_SHIFT 5 +#define GG82563_REG(page, reg) \ + (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) +#define GG82563_MIN_ALT_REG 30 + +/* GG82563 Specific Registers */ +#define GG82563_PHY_SPEC_CTRL \ + GG82563_REG(0, 16) /* PHY Specific Control */ +#define GG82563_PHY_SPEC_STATUS \ + GG82563_REG(0, 17) /* PHY Specific Status */ +#define GG82563_PHY_INT_ENABLE \ + GG82563_REG(0, 18) /* Interrupt Enable */ +#define GG82563_PHY_SPEC_STATUS_2 \ + GG82563_REG(0, 19) /* PHY Specific Status 2 */ +#define GG82563_PHY_RX_ERR_CNTR \ + GG82563_REG(0, 21) /* Receive Error Counter */ +#define GG82563_PHY_PAGE_SELECT \ + GG82563_REG(0, 22) /* Page Select */ +#define GG82563_PHY_SPEC_CTRL_2 \ + GG82563_REG(0, 26) /* PHY Specific Control 2 */ +#define GG82563_PHY_PAGE_SELECT_ALT \ + GG82563_REG(0, 29) /* Alternate Page Select */ +#define GG82563_PHY_TEST_CLK_CTRL \ + GG82563_REG(0, 30) /* Test Clock Control (use reg. 29 to select) */ + +#define GG82563_PHY_MAC_SPEC_CTRL \ + GG82563_REG(2, 21) /* MAC Specific Control Register */ +#define GG82563_PHY_MAC_SPEC_CTRL_2 \ + GG82563_REG(2, 26) /* MAC Specific Control 2 */ + +#define GG82563_PHY_DSP_DISTANCE \ + GG82563_REG(5, 26) /* DSP Distance */ + +/* Page 193 - Port Control Registers */ +#define GG82563_PHY_KMRN_MODE_CTRL \ + GG82563_REG(193, 16) /* Kumeran Mode Control */ +#define GG82563_PHY_PORT_RESET \ + GG82563_REG(193, 17) /* Port Reset */ +#define GG82563_PHY_REVISION_ID \ + GG82563_REG(193, 18) /* Revision ID */ +#define GG82563_PHY_DEVICE_ID \ + GG82563_REG(193, 19) /* Device ID */ +#define GG82563_PHY_PWR_MGMT_CTRL \ + GG82563_REG(193, 20) /* Power Management Control */ +#define GG82563_PHY_RATE_ADAPT_CTRL \ + GG82563_REG(193, 25) /* Rate Adaptation Control */ + +/* Page 194 - KMRN Registers */ +#define GG82563_PHY_KMRN_FIFO_CTRL_STAT \ + GG82563_REG(194, 16) /* FIFO's Control/Status */ +#define GG82563_PHY_KMRN_CTRL \ + GG82563_REG(194, 17) /* Control */ +#define GG82563_PHY_INBAND_CTRL \ + GG82563_REG(194, 18) /* Inband Control */ +#define GG82563_PHY_KMRN_DIAGNOSTIC \ + GG82563_REG(194, 19) /* Diagnostic */ +#define GG82563_PHY_ACK_TIMEOUTS \ + GG82563_REG(194, 20) /* Acknowledge Timeouts */ +#define GG82563_PHY_ADV_ABILITY \ + GG82563_REG(194, 21) /* Advertised Ability */ +#define GG82563_PHY_LINK_PARTNER_ADV_ABILITY \ + GG82563_REG(194, 23) /* Link Partner Advertised Ability */ +#define GG82563_PHY_ADV_NEXT_PAGE \ + GG82563_REG(194, 24) /* Advertised Next Page */ +#define GG82563_PHY_LINK_PARTNER_ADV_NEXT_PAGE \ + GG82563_REG(194, 25) /* Link Partner Advertised Next page */ +#define GG82563_PHY_KMRN_MISC \ + GG82563_REG(194, 26) /* Misc. */ + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0100 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 + +/* Extended Status Register */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* M88E1000 PHY Specific Control Register */ +#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, + * 0=CLK125 toggling + */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ + /* Manual MDI configuration */ +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, + * 100BASE-TX/10BASE-T: + * MDI Mode + */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled + * all speeds. + */ +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080 + /* 1=Enable Extended 10BASE-T distance + * (Lower 10BASE-T RX Threshold) + * 0=Normal 10BASE-T RX Threshold */ +#define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100 + /* 1=5-Bit interface in 100BASE-TX + * 0=MII interface in 100BASE-TX */ +#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ + +#define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5 +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 + +/* M88E1000 PHY Specific Status Register */ +#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M; + * 3=110-140M;4=>140M */ +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */ +#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#define M88E1000_PSSR_REV_POLARITY_SHIFT 1 +#define M88E1000_PSSR_MDIX_SHIFT 6 +#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 + +/* M88E1000 Extended PHY Specific Control Register */ +#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */ +#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled. + * Will assert lost lock and bring + * link down if idle not seen + * within 1ms in 1000BASE-T + */ +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master */ +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_2X 0x0400 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_3X 0x0800 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_4X 0x0C00 +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the slave */ +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_DIS 0x0000 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_2X 0x0200 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_3X 0x0300 +#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ + +/* Bit definitions for valid PHY IDs. */ +#define M88E1000_E_PHY_ID 0x01410C50 +#define M88E1000_I_PHY_ID 0x01410C30 +#define M88E1011_I_PHY_ID 0x01410C20 +#define M88E1000_12_PHY_ID M88E1000_E_PHY_ID +#define M88E1000_14_PHY_ID M88E1000_E_PHY_ID +#define IGP01E1000_I_PHY_ID 0x02A80380 +#define M88E1011_I_REV_4 0x04 +#define M88E1111_I_PHY_ID 0x01410CC0 +#define L1LXT971A_PHY_ID 0x001378E0 +#define GG82563_E_PHY_ID 0x01410CA0 + +/* Miscellaneous PHY bit definitions. */ +#define PHY_PREAMBLE 0xFFFFFFFF +#define PHY_SOF 0x01 +#define PHY_OP_READ 0x02 +#define PHY_OP_WRITE 0x01 +#define PHY_TURNAROUND 0x02 +#define PHY_PREAMBLE_SIZE 32 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 +#define E1000_PHY_ADDRESS 0x01 +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */ +#define REG4_SPEED_MASK 0x01E0 +#define REG9_SPEED_MASK 0x0300 +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 +#define ADVERTISE_1000_FULL 0x0020 +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ + +#define ICH_FLASH_GFPREG 0x0000 +#define ICH_FLASH_HSFSTS 0x0004 +#define ICH_FLASH_HSFCTL 0x0006 +#define ICH_FLASH_FADDR 0x0008 +#define ICH_FLASH_FDATA0 0x0010 +#define ICH_FLASH_FRACC 0x0050 +#define ICH_FLASH_FREG0 0x0054 +#define ICH_FLASH_FREG1 0x0058 +#define ICH_FLASH_FREG2 0x005C +#define ICH_FLASH_FREG3 0x0060 +#define ICH_FLASH_FPR0 0x0074 +#define ICH_FLASH_FPR1 0x0078 +#define ICH_FLASH_SSFSTS 0x0090 +#define ICH_FLASH_SSFCTL 0x0092 +#define ICH_FLASH_PREOP 0x0094 +#define ICH_FLASH_OPTYPE 0x0096 +#define ICH_FLASH_OPMENU 0x0098 + +#define ICH_FLASH_REG_MAPSIZE 0x00A0 +#define ICH_FLASH_SECTOR_SIZE 4096 +#define ICH_GFPREG_BASE_MASK 0x1FFF +#define ICH_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF + +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ + +/* SPI EEPROM Status Register */ +#define EEPROM_STATUS_RDY_SPI 0x01 +#define EEPROM_STATUS_WEN_SPI 0x02 +#define EEPROM_STATUS_BP0_SPI 0x04 +#define EEPROM_STATUS_BP1_SPI 0x08 +#define EEPROM_STATUS_WPEN_SPI 0x80 + +/* SW Semaphore Register */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_WMNG 0x00000004 /* Wake MNG Clock */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ + +/* FW Semaphore Register */ +#define E1000_FWSM_MODE_MASK 0x0000000E /* FW mode */ +#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */ + +#define E1000_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI reset */ +#define E1000_FWSM_DISSW 0x10000000 /* FW disable SW Write Access */ +#define E1000_FWSM_SKUSEL_MASK 0x60000000 /* LAN SKU select */ +#define E1000_FWSM_SKUEL_SHIFT 29 +#define E1000_FWSM_SKUSEL_EMB 0x0 /* Embedded SKU */ +#define E1000_FWSM_SKUSEL_CONS 0x1 /* Consumer SKU */ +#define E1000_FWSM_SKUSEL_PERF_100 0x2 /* Perf & Corp 10/100 SKU */ +#define E1000_FWSM_SKUSEL_PERF_GBE 0x3 /* Perf & Copr GbE SKU */ + +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Inteface Control */ + +#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF +#define IGP_ACTIVITY_LED_ENABLE 0x0300 +#define IGP_LED3_MODE 0x07000000 + +/* Mask bit for PHY class in Word 7 of the EEPROM */ +#define EEPROM_PHY_CLASS_A 0x8000 +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ +#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/ +#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/ + +#define E1000_KUMCTRLSTA_MASK 0x0000FFFF +#define E1000_KUMCTRLSTA_OFFSET 0x001F0000 +#define E1000_KUMCTRLSTA_OFFSET_SHIFT 16 +#define E1000_KUMCTRLSTA_REN 0x00200000 + +#define E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL 0x00000000 +#define E1000_KUMCTRLSTA_OFFSET_CTRL 0x00000001 +#define E1000_KUMCTRLSTA_OFFSET_INB_CTRL 0x00000002 +#define E1000_KUMCTRLSTA_OFFSET_DIAG 0x00000003 +#define E1000_KUMCTRLSTA_OFFSET_TIMEOUTS 0x00000004 +#define E1000_KUMCTRLSTA_OFFSET_INB_PARAM 0x00000009 +#define E1000_KUMCTRLSTA_OFFSET_HD_CTRL 0x00000010 +#define E1000_KUMCTRLSTA_OFFSET_M2P_SERDES 0x0000001E +#define E1000_KUMCTRLSTA_OFFSET_M2P_MODES 0x0000001F + +/* FIFO Control */ +#define E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS 0x00000008 +#define E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS 0x00000800 + +/* In-Band Control */ +#define E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT 0x00000500 +#define E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING 0x00000010 + +/* Half-Duplex Control */ +#define E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004 +#define E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT 0x00000000 + +#define E1000_KUMCTRLSTA_OFFSET_K0S_CTRL 0x0000001E + +#define E1000_KUMCTRLSTA_DIAG_FELPBK 0x2000 +#define E1000_KUMCTRLSTA_DIAG_NELPBK 0x1000 + +#define E1000_KUMCTRLSTA_K0S_100_EN 0x2000 +#define E1000_KUMCTRLSTA_K0S_GBE_EN 0x1000 +#define E1000_KUMCTRLSTA_K0S_ENTRY_LATENCY_MASK 0x0003 + +#define E1000_MNG_ICH_IAMT_MODE 0x2 +#define E1000_MNG_IAMT_MODE 0x3 +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ +/* Number of milliseconds we wait for PHY configuration done after MAC reset */ +#define PHY_CFG_TIMEOUT 100 +#define DEFAULT_80003ES2LAN_TIPG_IPGT_10_100 0x00000009 +#define DEFAULT_80003ES2LAN_TIPG_IPGT_1000 0x00000008 +#define E1000_TXDMAC_DPP 0x00000001 +#define AUTO_ALL_MODES 0 + +#ifndef E1000_MASTER_SLAVE +/* Switch to override PHY master/slave setting */ +#define E1000_MASTER_SLAVE e1000_ms_hw_default +#endif +/* Extended Transmit Control */ +#define E1000_TCTL_EXT_BST_MASK 0x000003FF /* Backoff Slot Time */ +#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ + +#define DEFAULT_80003ES2LAN_TCTL_EXT_GCEX 0x00010000 + +#define PCI_EX_82566_SNOOP_ALL PCI_EX_NO_SNOOP_ALL + +#define E1000_GCR_L1_ACT_WITHOUT_L0S_RX 0x08000000 +#define E1000_MC_TBL_SIZE_ICH8LAN 32 + +#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers + after IMS clear */ +#endif /* _E1000_HW_H_ */ diff --git a/roms/u-boot-sam460ex/drivers/net/eepro100.c b/roms/u-boot-sam460ex/drivers/net/eepro100.c new file mode 100644 index 000000000..9c06b2556 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/eepro100.c @@ -0,0 +1,944 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> +#include <miiphy.h> + +#undef DEBUG + + /* Ethernet chip registers. + */ +#define SCBStatus 0 /* Rx/Command Unit Status *Word* */ +#define SCBIntAckByte 1 /* Rx/Command Unit STAT/ACK byte */ +#define SCBCmd 2 /* Rx/Command Unit Command *Word* */ +#define SCBIntrCtlByte 3 /* Rx/Command Unit Intr.Control Byte */ +#define SCBPointer 4 /* General purpose pointer. */ +#define SCBPort 8 /* Misc. commands and operands. */ +#define SCBflash 12 /* Flash memory control. */ +#define SCBeeprom 14 /* EEPROM memory control. */ +#define SCBCtrlMDI 16 /* MDI interface control. */ +#define SCBEarlyRx 20 /* Early receive byte count. */ +#define SCBGenControl 28 /* 82559 General Control Register */ +#define SCBGenStatus 29 /* 82559 General Status register */ + + /* 82559 SCB status word defnitions + */ +#define SCB_STATUS_CX 0x8000 /* CU finished command (transmit) */ +#define SCB_STATUS_FR 0x4000 /* frame received */ +#define SCB_STATUS_CNA 0x2000 /* CU left active state */ +#define SCB_STATUS_RNR 0x1000 /* receiver left ready state */ +#define SCB_STATUS_MDI 0x0800 /* MDI read/write cycle done */ +#define SCB_STATUS_SWI 0x0400 /* software generated interrupt */ +#define SCB_STATUS_FCP 0x0100 /* flow control pause interrupt */ + +#define SCB_INTACK_MASK 0xFD00 /* all the above */ + +#define SCB_INTACK_TX (SCB_STATUS_CX | SCB_STATUS_CNA) +#define SCB_INTACK_RX (SCB_STATUS_FR | SCB_STATUS_RNR) + + /* System control block commands + */ +/* CU Commands */ +#define CU_NOP 0x0000 +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 /* Load Dump Statistics ctrs addr */ +#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ +#define CU_ADDR_LOAD 0x0060 /* Base address to add to CU commands */ +#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ + +/* RUC Commands */ +#define RUC_NOP 0x0000 +#define RUC_START 0x0001 +#define RUC_RESUME 0x0002 +#define RUC_ABORT 0x0004 +#define RUC_ADDR_LOAD 0x0006 /* (seems not to clear on acceptance) */ +#define RUC_RESUMENR 0x0007 + +#define CU_CMD_MASK 0x00f0 +#define RU_CMD_MASK 0x0007 + +#define SCB_M 0x0100 /* 0 = enable interrupt, 1 = disable */ +#define SCB_SWI 0x0200 /* 1 - cause device to interrupt */ + +#define CU_STATUS_MASK 0x00C0 +#define RU_STATUS_MASK 0x003C + +#define RU_STATUS_IDLE (0<<2) +#define RU_STATUS_SUS (1<<2) +#define RU_STATUS_NORES (2<<2) +#define RU_STATUS_READY (4<<2) +#define RU_STATUS_NO_RBDS_SUS ((1<<2)|(8<<2)) +#define RU_STATUS_NO_RBDS_NORES ((2<<2)|(8<<2)) +#define RU_STATUS_NO_RBDS_READY ((4<<2)|(8<<2)) + + /* 82559 Port interface commands. + */ +#define I82559_RESET 0x00000000 /* Software reset */ +#define I82559_SELFTEST 0x00000001 /* 82559 Selftest command */ +#define I82559_SELECTIVE_RESET 0x00000002 +#define I82559_DUMP 0x00000003 +#define I82559_DUMP_WAKEUP 0x00000007 + + /* 82559 Eeprom interface. + */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) +#define EE_CMD_BITS 3 +#define EE_DATA_BITS 16 + + /* The EEPROM commands include the alway-set leading bit. + */ +#define EE_EWENB_CMD (4 << addr_len) +#define EE_WRITE_CMD (5 << addr_len) +#define EE_READ_CMD (6 << addr_len) +#define EE_ERASE_CMD (7 << addr_len) + + /* Receive frame descriptors. + */ +struct RxFD { + volatile u16 status; + volatile u16 control; + volatile u32 link; /* struct RxFD * */ + volatile u32 rx_buf_addr; /* void * */ + volatile u32 count; + + volatile u8 data[PKTSIZE_ALIGN]; +}; + +#define RFD_STATUS_C 0x8000 /* completion of received frame */ +#define RFD_STATUS_OK 0x2000 /* frame received with no errors */ + +#define RFD_CONTROL_EL 0x8000 /* 1=last RFD in RFA */ +#define RFD_CONTROL_S 0x4000 /* 1=suspend RU after receiving frame */ +#define RFD_CONTROL_H 0x0010 /* 1=RFD is a header RFD */ +#define RFD_CONTROL_SF 0x0008 /* 0=simplified, 1=flexible mode */ + +#define RFD_COUNT_MASK 0x3fff +#define RFD_COUNT_F 0x4000 +#define RFD_COUNT_EOF 0x8000 + +#define RFD_RX_CRC 0x0800 /* crc error */ +#define RFD_RX_ALIGNMENT 0x0400 /* alignment error */ +#define RFD_RX_RESOURCE 0x0200 /* out of space, no resources */ +#define RFD_RX_DMA_OVER 0x0100 /* DMA overrun */ +#define RFD_RX_SHORT 0x0080 /* short frame error */ +#define RFD_RX_LENGTH 0x0020 +#define RFD_RX_ERROR 0x0010 /* receive error */ +#define RFD_RX_NO_ADR_MATCH 0x0004 /* no address match */ +#define RFD_RX_IA_MATCH 0x0002 /* individual address does not match */ +#define RFD_RX_TCO 0x0001 /* TCO indication */ + + /* Transmit frame descriptors + */ +struct TxFD { /* Transmit frame descriptor set. */ + volatile u16 status; + volatile u16 command; + volatile u32 link; /* void * */ + volatile u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */ + volatile s32 count; + + volatile u32 tx_buf_addr0; /* void *, frame to be transmitted. */ + volatile s32 tx_buf_size0; /* Length of Tx frame. */ + volatile u32 tx_buf_addr1; /* void *, frame to be transmitted. */ + volatile s32 tx_buf_size1; /* Length of Tx frame. */ +}; + +#define TxCB_CMD_TRANSMIT 0x0004 /* transmit command */ +#define TxCB_CMD_SF 0x0008 /* 0=simplified, 1=flexible mode */ +#define TxCB_CMD_NC 0x0010 /* 0=CRC insert by controller */ +#define TxCB_CMD_I 0x2000 /* generate interrupt on completion */ +#define TxCB_CMD_S 0x4000 /* suspend on completion */ +#define TxCB_CMD_EL 0x8000 /* last command block in CBL */ + +#define TxCB_COUNT_MASK 0x3fff +#define TxCB_COUNT_EOF 0x8000 + + /* The Speedo3 Rx and Tx frame/buffer descriptors. + */ +struct descriptor { /* A generic descriptor. */ + volatile u16 status; + volatile u16 command; + volatile u32 link; /* struct descriptor * */ + + unsigned char params[0]; +}; + +#define CONFIG_SYS_CMD_EL 0x8000 +#define CONFIG_SYS_CMD_SUSPEND 0x4000 +#define CONFIG_SYS_CMD_INT 0x2000 +#define CONFIG_SYS_CMD_IAS 0x0001 /* individual address setup */ +#define CONFIG_SYS_CMD_CONFIGURE 0x0002 /* configure */ + +#define CONFIG_SYS_STATUS_C 0x8000 +#define CONFIG_SYS_STATUS_OK 0x2000 + + /* Misc. + */ +#define NUM_RX_DESC PKTBUFSRX +#define NUM_TX_DESC 1 /* Number of TX descriptors */ + +#define TOUT_LOOP 1000000 + +#define ETH_ALEN 6 + +static struct RxFD rx_ring[NUM_RX_DESC]; /* RX descriptor ring */ +static struct TxFD tx_ring[NUM_TX_DESC]; /* TX descriptor ring */ +static int rx_next; /* RX descriptor ring pointer */ +static int tx_next; /* TX descriptor ring pointer */ +static int tx_threshold; + +/* + * The parameters for a CmdConfigure operation. + * There are so many options that it would be difficult to document + * each bit. We mostly use the default or recommended settings. + */ +static const char i82557_config_cmd[] = { + 22, 0x08, 0, 0, 0, 0, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0, + 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ + 0x3f, 0x05, +}; +static const char i82558_config_cmd[] = { + 22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0x08, 0x88, + 0x68, 0, 0x40, 0xf2, 0x84, /* Disable FC */ + 0x31, 0x05, +}; + +static void init_rx_ring (struct eth_device *dev); +static void purge_tx_ring (struct eth_device *dev); + +static void read_hw_addr (struct eth_device *dev, bd_t * bis); + +static int eepro100_init (struct eth_device *dev, bd_t * bis); +static int eepro100_send (struct eth_device *dev, volatile void *packet, + int length); +static int eepro100_recv (struct eth_device *dev); +static void eepro100_halt (struct eth_device *dev); + +#if defined(CONFIG_E500) || defined(CONFIG_DB64360) || defined(CONFIG_DB64460) +#define bus_to_phys(a) (a) +#define phys_to_bus(a) (a) +#else +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) +#endif + +static inline int INW (struct eth_device *dev, u_long addr) +{ + return le16_to_cpu (*(volatile u16 *) (addr + dev->iobase)); +} + +static inline void OUTW (struct eth_device *dev, int command, u_long addr) +{ + *(volatile u16 *) ((addr + dev->iobase)) = cpu_to_le16 (command); +} + +static inline void OUTL (struct eth_device *dev, int command, u_long addr) +{ + *(volatile u32 *) ((addr + dev->iobase)) = cpu_to_le32 (command); +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static inline int INL (struct eth_device *dev, u_long addr) +{ + return le32_to_cpu (*(volatile u32 *) (addr + dev->iobase)); +} + +static int get_phyreg (struct eth_device *dev, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + int cmd; + int timeout = 50; + + /* read requested data */ + cmd = (2 << 26) | ((addr & 0x1f) << 21) | ((reg & 0x1f) << 16); + OUTL (dev, cmd, SCBCtrlMDI); + + do { + udelay(1000); + cmd = INL (dev, SCBCtrlMDI); + } while (!(cmd & (1 << 28)) && (--timeout)); + + if (timeout == 0) + return -1; + + *value = (unsigned short) (cmd & 0xffff); + + return 0; +} + +static int set_phyreg (struct eth_device *dev, unsigned char addr, + unsigned char reg, unsigned short value) +{ + int cmd; + int timeout = 50; + + /* write requested data */ + cmd = (1 << 26) | ((addr & 0x1f) << 21) | ((reg & 0x1f) << 16); + OUTL (dev, cmd | value, SCBCtrlMDI); + + while (!(INL (dev, SCBCtrlMDI) & (1 << 28)) && (--timeout)) + udelay(1000); + + if (timeout == 0) + return -1; + + return 0; +} + +/* Check if given phyaddr is valid, i.e. there is a PHY connected. + * Do this by checking model value field from ID2 register. + */ +static struct eth_device* verify_phyaddr (char *devname, unsigned char addr) +{ + struct eth_device *dev; + unsigned short value; + unsigned char model; + + dev = eth_get_dev_by_name(devname); + if (dev == NULL) { + printf("%s: no such device\n", devname); + return NULL; + } + + /* read id2 register */ + if (get_phyreg(dev, addr, PHY_PHYIDR2, &value) != 0) { + printf("%s: mii read timeout!\n", devname); + return NULL; + } + + /* get model */ + model = (unsigned char)((value >> 4) & 0x003f); + + if (model == 0) { + printf("%s: no PHY at address %d\n", devname, addr); + return NULL; + } + + return dev; +} + +static int eepro100_miiphy_read (char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + struct eth_device *dev; + + dev = verify_phyaddr(devname, addr); + if (dev == NULL) + return -1; + + if (get_phyreg(dev, addr, reg, value) != 0) { + printf("%s: mii read timeout!\n", devname); + return -1; + } + + return 0; +} + +static int eepro100_miiphy_write (char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct eth_device *dev; + + dev = verify_phyaddr(devname, addr); + if (dev == NULL) + return -1; + + if (set_phyreg(dev, addr, reg, value) != 0) { + printf("%s: mii write timeout!\n", devname); + return -1; + } + + return 0; +} + +#endif + +/* Wait for the chip get the command. +*/ +static int wait_for_eepro100 (struct eth_device *dev) +{ + int i; + + for (i = 0; INW (dev, SCBCmd) & (CU_CMD_MASK | RU_CMD_MASK); i++) { + if (i >= TOUT_LOOP) { + return 0; + } + } + + return 1; +} + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82557}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559ER}, + {} +}; + +int eepro100_initialize (bd_t * bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase, status; + int idx = 0; + + while (1) { + /* Find PCI device + */ + if ((devno = pci_find_devices (supported, idx++)) < 0) { + break; + } + + pci_read_config_dword (devno, PCI_BASE_ADDRESS_0, &iobase); + iobase &= ~0xf; + +#ifdef DEBUG + printf ("eepro100: Intel i82559 PCI EtherExpressPro @0x%x\n", + iobase); +#endif + + pci_write_config_dword (devno, + PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if I/O accesses and Bus Mastering are enabled. + */ + pci_read_config_dword (devno, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_MEMORY)) { + printf ("Error: Can not enable MEM access.\n"); + continue; + } + + if (!(status & PCI_COMMAND_MASTER)) { + printf ("Error: Can not enable Bus Mastering.\n"); + continue; + } + + dev = (struct eth_device *) malloc (sizeof *dev); + + sprintf (dev->name, "i82559#%d", card_number); + dev->priv = (void *) devno; /* this have to come before bus_to_phys() */ + dev->iobase = bus_to_phys (iobase); + dev->init = eepro100_init; + dev->halt = eepro100_halt; + dev->send = eepro100_send; + dev->recv = eepro100_recv; + + eth_register (dev); + +#if defined (CONFIG_MII) || defined(CONFIG_CMD_MII) + /* register mii command access routines */ + miiphy_register(dev->name, + eepro100_miiphy_read, eepro100_miiphy_write); +#endif + + card_number++; + + /* Set the latency timer for value. + */ + pci_write_config_byte (devno, PCI_LATENCY_TIMER, 0x20); + + udelay (10 * 1000); + + read_hw_addr (dev, bis); + } + + return card_number; +} + + +static int eepro100_init (struct eth_device *dev, bd_t * bis) +{ + int i, status = -1; + int tx_cur; + struct descriptor *ias_cmd, *cfg_cmd; + + /* Reset the ethernet controller + */ + OUTL (dev, I82559_SELECTIVE_RESET, SCBPort); + udelay (20); + + OUTL (dev, I82559_RESET, SCBPort); + udelay (20); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd); + + /* Initialize Rx and Tx rings. + */ + init_rx_ring (dev); + purge_tx_ring (dev); + + /* Tell the adapter where the RX ring is located. + */ + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & rx_ring[rx_next]), SCBPointer); + OUTW (dev, SCB_M | RUC_START, SCBCmd); + + /* Send the Configure frame */ + tx_cur = tx_next; + tx_next = ((tx_next + 1) % NUM_TX_DESC); + + cfg_cmd = (struct descriptor *) &tx_ring[tx_cur]; + cfg_cmd->command = cpu_to_le16 ((CONFIG_SYS_CMD_SUSPEND | CONFIG_SYS_CMD_CONFIGURE)); + cfg_cmd->status = 0; + cfg_cmd->link = cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next])); + + memcpy (cfg_cmd->params, i82558_config_cmd, + sizeof (i82558_config_cmd)); + + if (!wait_for_eepro100 (dev)) { + printf ("Error---CONFIG_SYS_CMD_CONFIGURE: Can not reset ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer); + OUTW (dev, SCB_M | CU_START, SCBCmd); + + for (i = 0; + !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C); + i++) { + if (i >= TOUT_LOOP) { + printf ("%s: Tx error buffer not ready\n", dev->name); + goto Done; + } + } + + if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) { + printf ("TX error status = 0x%08X\n", + le16_to_cpu (tx_ring[tx_cur].status)); + goto Done; + } + + /* Send the Individual Address Setup frame + */ + tx_cur = tx_next; + tx_next = ((tx_next + 1) % NUM_TX_DESC); + + ias_cmd = (struct descriptor *) &tx_ring[tx_cur]; + ias_cmd->command = cpu_to_le16 ((CONFIG_SYS_CMD_SUSPEND | CONFIG_SYS_CMD_IAS)); + ias_cmd->status = 0; + ias_cmd->link = cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next])); + + memcpy (ias_cmd->params, dev->enetaddr, 6); + + /* Tell the adapter where the TX ring is located. + */ + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer); + OUTW (dev, SCB_M | CU_START, SCBCmd); + + for (i = 0; !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C); + i++) { + if (i >= TOUT_LOOP) { + printf ("%s: Tx error buffer not ready\n", + dev->name); + goto Done; + } + } + + if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) { + printf ("TX error status = 0x%08X\n", + le16_to_cpu (tx_ring[tx_cur].status)); + goto Done; + } + + status = 0; + + Done: + return status; +} + +static int eepro100_send (struct eth_device *dev, volatile void *packet, int length) +{ + int i, status = -1; + int tx_cur; + + if (length <= 0) { + printf ("%s: bad packet size: %d\n", dev->name, length); + goto Done; + } + + tx_cur = tx_next; + tx_next = (tx_next + 1) % NUM_TX_DESC; + + tx_ring[tx_cur].command = cpu_to_le16 ( TxCB_CMD_TRANSMIT | + TxCB_CMD_SF | + TxCB_CMD_S | + TxCB_CMD_EL ); + tx_ring[tx_cur].status = 0; + tx_ring[tx_cur].count = cpu_to_le32 (tx_threshold); + tx_ring[tx_cur].link = + cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next])); + tx_ring[tx_cur].tx_desc_addr = + cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_cur].tx_buf_addr0)); + tx_ring[tx_cur].tx_buf_addr0 = + cpu_to_le32 (phys_to_bus ((u_long) packet)); + tx_ring[tx_cur].tx_buf_size0 = cpu_to_le32 (length); + + if (!wait_for_eepro100 (dev)) { + printf ("%s: Tx error ethernet controller not ready.\n", + dev->name); + goto Done; + } + + /* Send the packet. + */ + OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer); + OUTW (dev, SCB_M | CU_START, SCBCmd); + + for (i = 0; !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C); + i++) { + if (i >= TOUT_LOOP) { + printf ("%s: Tx error buffer not ready\n", dev->name); + goto Done; + } + } + + if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) { + printf ("TX error status = 0x%08X\n", + le16_to_cpu (tx_ring[tx_cur].status)); + goto Done; + } + + status = length; + + Done: + return status; +} + +static int eepro100_recv (struct eth_device *dev) +{ + u16 status, stat; + int rx_prev, length = 0; + + stat = INW (dev, SCBStatus); + OUTW (dev, stat & SCB_STATUS_RNR, SCBStatus); + + for (;;) { + status = le16_to_cpu (rx_ring[rx_next].status); + + if (!(status & RFD_STATUS_C)) { + break; + } + + /* Valid frame status. + */ + if ((status & RFD_STATUS_OK)) { + /* A valid frame received. + */ + length = le32_to_cpu (rx_ring[rx_next].count) & 0x3fff; + + /* Pass the packet up to the protocol + * layers. + */ + NetReceive (rx_ring[rx_next].data, length); + } else { + /* There was an error. + */ + printf ("RX error status = 0x%08X\n", status); + } + + rx_ring[rx_next].control = cpu_to_le16 (RFD_CONTROL_S); + rx_ring[rx_next].status = 0; + rx_ring[rx_next].count = cpu_to_le32 (PKTSIZE_ALIGN << 16); + + rx_prev = (rx_next + NUM_RX_DESC - 1) % NUM_RX_DESC; + rx_ring[rx_prev].control = 0; + + /* Update entry information. + */ + rx_next = (rx_next + 1) % NUM_RX_DESC; + } + + if (stat & SCB_STATUS_RNR) { + + printf ("%s: Receiver is not ready, restart it !\n", dev->name); + + /* Reinitialize Rx ring. + */ + init_rx_ring (dev); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not restart ethernet controller.\n"); + goto Done; + } + + OUTL (dev, phys_to_bus ((u32) & rx_ring[rx_next]), SCBPointer); + OUTW (dev, SCB_M | RUC_START, SCBCmd); + } + + Done: + return length; +} + +static void eepro100_halt (struct eth_device *dev) +{ + /* Reset the ethernet controller + */ + OUTL (dev, I82559_SELECTIVE_RESET, SCBPort); + udelay (20); + + OUTL (dev, I82559_RESET, SCBPort); + udelay (20); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd); + + if (!wait_for_eepro100 (dev)) { + printf ("Error: Can not reset ethernet controller.\n"); + goto Done; + } + OUTL (dev, 0, SCBPointer); + OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd); + + Done: + return; +} + + /* SROM Read. + */ +static int read_eeprom (struct eth_device *dev, int location, int addr_len) +{ + unsigned short retval = 0; + int read_cmd = location | EE_READ_CMD; + int i; + + OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom); + OUTW (dev, EE_ENB, SCBeeprom); + + /* Shift the read command bits out. */ + for (i = 12; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + + OUTW (dev, EE_ENB | dataval, SCBeeprom); + udelay (1); + OUTW (dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay (1); + } + OUTW (dev, EE_ENB, SCBeeprom); + + for (i = 15; i >= 0; i--) { + OUTW (dev, EE_ENB | EE_SHIFT_CLK, SCBeeprom); + udelay (1); + retval = (retval << 1) | + ((INW (dev, SCBeeprom) & EE_DATA_READ) ? 1 : 0); + OUTW (dev, EE_ENB, SCBeeprom); + udelay (1); + } + + /* Terminate the EEPROM access. */ + OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom); + return retval; +} + +#ifdef CONFIG_EEPRO100_SROM_WRITE +int eepro100_write_eeprom (struct eth_device* dev, int location, int addr_len, unsigned short data) +{ + unsigned short dataval; + int enable_cmd = 0x3f | EE_EWENB_CMD; + int write_cmd = location | EE_WRITE_CMD; + int i; + unsigned long datalong, tmplong; + + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB, SCBeeprom); + + /* Shift the enable command bits out. */ + for (i = (addr_len+EE_CMD_BITS-1); i >= 0; i--) + { + dataval = (enable_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay(1); + } + + OUTW(dev, EE_ENB, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB, SCBeeprom); + + + /* Shift the write command bits out. */ + for (i = (addr_len+EE_CMD_BITS-1); i >= 0; i--) + { + dataval = (write_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay(1); + } + + /* Write the data */ + datalong= (unsigned long) ((((data) & 0x00ff) << 8) | ( (data) >> 8)); + + for (i = 0; i< EE_DATA_BITS; i++) + { + /* Extract and move data bit to bit DI */ + dataval = ((datalong & 0x8000)>>13) ? EE_DATA_WRITE : 0; + + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom); + udelay(1); + OUTW(dev, EE_ENB | dataval, SCBeeprom); + udelay(1); + + datalong = datalong << 1; /* Adjust significant data bit*/ + } + + /* Finish up command (toggle CS) */ + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + udelay(1); /* delay for more than 250 ns */ + OUTW(dev, EE_ENB, SCBeeprom); + + /* Wait for programming ready (D0 = 1) */ + tmplong = 10; + do + { + dataval = INW(dev, SCBeeprom); + if (dataval & EE_DATA_READ) + break; + udelay(10000); + } + while (-- tmplong); + + if (tmplong == 0) + { + printf ("Write i82559 eeprom timed out (100 ms waiting for data ready.\n"); + return -1; + } + + /* Terminate the EEPROM access. */ + OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom); + + return 0; +} +#endif + +static void init_rx_ring (struct eth_device *dev) +{ + int i; + + for (i = 0; i < NUM_RX_DESC; i++) { + rx_ring[i].status = 0; + rx_ring[i].control = + (i == NUM_RX_DESC - 1) ? cpu_to_le16 (RFD_CONTROL_S) : 0; + rx_ring[i].link = + cpu_to_le32 (phys_to_bus + ((u32) & rx_ring[(i + 1) % NUM_RX_DESC])); + rx_ring[i].rx_buf_addr = 0xffffffff; + rx_ring[i].count = cpu_to_le32 (PKTSIZE_ALIGN << 16); + } + + rx_next = 0; +} + +static void purge_tx_ring (struct eth_device *dev) +{ + int i; + + tx_next = 0; + tx_threshold = 0x01208000; + + for (i = 0; i < NUM_TX_DESC; i++) { + tx_ring[i].status = 0; + tx_ring[i].command = 0; + tx_ring[i].link = 0; + tx_ring[i].tx_desc_addr = 0; + tx_ring[i].count = 0; + + tx_ring[i].tx_buf_addr0 = 0; + tx_ring[i].tx_buf_size0 = 0; + tx_ring[i].tx_buf_addr1 = 0; + tx_ring[i].tx_buf_size1 = 0; + } +} + +static void read_hw_addr (struct eth_device *dev, bd_t * bis) +{ + u16 eeprom[0x40]; + u16 sum = 0; + int i, j; + int addr_len = read_eeprom (dev, 0, 6) == 0xffff ? 8 : 6; + + for (j = 0, i = 0; i < 0x40; i++) { + u16 value = read_eeprom (dev, i, addr_len); + + eeprom[i] = value; + sum += value; + if (i < 3) { + dev->enetaddr[j++] = value; + dev->enetaddr[j++] = value >> 8; + } + } + + if (sum != 0xBABA) { + memset (dev->enetaddr, 0, ETH_ALEN); +#ifdef DEBUG + printf ("%s: Invalid EEPROM checksum %#4.4x, " + "check settings before activating this device!\n", + dev->name, sum); +#endif + } +} diff --git a/roms/u-boot-sam460ex/drivers/net/enc28j60.c b/roms/u-boot-sam460ex/drivers/net/enc28j60.c new file mode 100644 index 000000000..3238a502c --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/enc28j60.c @@ -0,0 +1,982 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <common.h> +#include <net.h> +#include <asm/arch/hardware.h> +#include <asm/arch/spi.h> + +/* + * Control Registers in Bank 0 + */ + +#define CTL_REG_ERDPTL 0x00 +#define CTL_REG_ERDPTH 0x01 +#define CTL_REG_EWRPTL 0x02 +#define CTL_REG_EWRPTH 0x03 +#define CTL_REG_ETXSTL 0x04 +#define CTL_REG_ETXSTH 0x05 +#define CTL_REG_ETXNDL 0x06 +#define CTL_REG_ETXNDH 0x07 +#define CTL_REG_ERXSTL 0x08 +#define CTL_REG_ERXSTH 0x09 +#define CTL_REG_ERXNDL 0x0A +#define CTL_REG_ERXNDH 0x0B +#define CTL_REG_ERXRDPTL 0x0C +#define CTL_REG_ERXRDPTH 0x0D +#define CTL_REG_ERXWRPTL 0x0E +#define CTL_REG_ERXWRPTH 0x0F +#define CTL_REG_EDMASTL 0x10 +#define CTL_REG_EDMASTH 0x11 +#define CTL_REG_EDMANDL 0x12 +#define CTL_REG_EDMANDH 0x13 +#define CTL_REG_EDMADSTL 0x14 +#define CTL_REG_EDMADSTH 0x15 +#define CTL_REG_EDMACSL 0x16 +#define CTL_REG_EDMACSH 0x17 +/* these are common in all banks */ +#define CTL_REG_EIE 0x1B +#define CTL_REG_EIR 0x1C +#define CTL_REG_ESTAT 0x1D +#define CTL_REG_ECON2 0x1E +#define CTL_REG_ECON1 0x1F + +/* + * Control Registers in Bank 1 + */ + +#define CTL_REG_EHT0 0x00 +#define CTL_REG_EHT1 0x01 +#define CTL_REG_EHT2 0x02 +#define CTL_REG_EHT3 0x03 +#define CTL_REG_EHT4 0x04 +#define CTL_REG_EHT5 0x05 +#define CTL_REG_EHT6 0x06 +#define CTL_REG_EHT7 0x07 +#define CTL_REG_EPMM0 0x08 +#define CTL_REG_EPMM1 0x09 +#define CTL_REG_EPMM2 0x0A +#define CTL_REG_EPMM3 0x0B +#define CTL_REG_EPMM4 0x0C +#define CTL_REG_EPMM5 0x0D +#define CTL_REG_EPMM6 0x0E +#define CTL_REG_EPMM7 0x0F +#define CTL_REG_EPMCSL 0x10 +#define CTL_REG_EPMCSH 0x11 +#define CTL_REG_EPMOL 0x14 +#define CTL_REG_EPMOH 0x15 +#define CTL_REG_EWOLIE 0x16 +#define CTL_REG_EWOLIR 0x17 +#define CTL_REG_ERXFCON 0x18 +#define CTL_REG_EPKTCNT 0x19 + +/* + * Control Registers in Bank 2 + */ + +#define CTL_REG_MACON1 0x00 +#define CTL_REG_MACON2 0x01 +#define CTL_REG_MACON3 0x02 +#define CTL_REG_MACON4 0x03 +#define CTL_REG_MABBIPG 0x04 +#define CTL_REG_MAIPGL 0x06 +#define CTL_REG_MAIPGH 0x07 +#define CTL_REG_MACLCON1 0x08 +#define CTL_REG_MACLCON2 0x09 +#define CTL_REG_MAMXFLL 0x0A +#define CTL_REG_MAMXFLH 0x0B +#define CTL_REG_MAPHSUP 0x0D +#define CTL_REG_MICON 0x11 +#define CTL_REG_MICMD 0x12 +#define CTL_REG_MIREGADR 0x14 +#define CTL_REG_MIWRL 0x16 +#define CTL_REG_MIWRH 0x17 +#define CTL_REG_MIRDL 0x18 +#define CTL_REG_MIRDH 0x19 + +/* + * Control Registers in Bank 3 + */ + +#define CTL_REG_MAADR1 0x00 +#define CTL_REG_MAADR0 0x01 +#define CTL_REG_MAADR3 0x02 +#define CTL_REG_MAADR2 0x03 +#define CTL_REG_MAADR5 0x04 +#define CTL_REG_MAADR4 0x05 +#define CTL_REG_EBSTSD 0x06 +#define CTL_REG_EBSTCON 0x07 +#define CTL_REG_EBSTCSL 0x08 +#define CTL_REG_EBSTCSH 0x09 +#define CTL_REG_MISTAT 0x0A +#define CTL_REG_EREVID 0x12 +#define CTL_REG_ECOCON 0x15 +#define CTL_REG_EFLOCON 0x17 +#define CTL_REG_EPAUSL 0x18 +#define CTL_REG_EPAUSH 0x19 + + +/* + * PHY Register + */ + +#define PHY_REG_PHID1 0x02 +#define PHY_REG_PHID2 0x03 +/* taken from the Linux driver */ +#define PHY_REG_PHCON1 0x00 +#define PHY_REG_PHCON2 0x10 +#define PHY_REG_PHLCON 0x14 + +/* + * Receive Filter Register (ERXFCON) bits + */ + +#define ENC_RFR_UCEN 0x80 +#define ENC_RFR_ANDOR 0x40 +#define ENC_RFR_CRCEN 0x20 +#define ENC_RFR_PMEN 0x10 +#define ENC_RFR_MPEN 0x08 +#define ENC_RFR_HTEN 0x04 +#define ENC_RFR_MCEN 0x02 +#define ENC_RFR_BCEN 0x01 + +/* + * ECON1 Register Bits + */ + +#define ENC_ECON1_TXRST 0x80 +#define ENC_ECON1_RXRST 0x40 +#define ENC_ECON1_DMAST 0x20 +#define ENC_ECON1_CSUMEN 0x10 +#define ENC_ECON1_TXRTS 0x08 +#define ENC_ECON1_RXEN 0x04 +#define ENC_ECON1_BSEL1 0x02 +#define ENC_ECON1_BSEL0 0x01 + +/* + * ECON2 Register Bits + */ +#define ENC_ECON2_AUTOINC 0x80 +#define ENC_ECON2_PKTDEC 0x40 +#define ENC_ECON2_PWRSV 0x20 +#define ENC_ECON2_VRPS 0x08 + +/* + * EIR Register Bits + */ +#define ENC_EIR_PKTIF 0x40 +#define ENC_EIR_DMAIF 0x20 +#define ENC_EIR_LINKIF 0x10 +#define ENC_EIR_TXIF 0x08 +#define ENC_EIR_WOLIF 0x04 +#define ENC_EIR_TXERIF 0x02 +#define ENC_EIR_RXERIF 0x01 + +/* + * ESTAT Register Bits + */ + +#define ENC_ESTAT_INT 0x80 +#define ENC_ESTAT_LATECOL 0x10 +#define ENC_ESTAT_RXBUSY 0x04 +#define ENC_ESTAT_TXABRT 0x02 +#define ENC_ESTAT_CLKRDY 0x01 + +/* + * EIE Register Bits + */ + +#define ENC_EIE_INTIE 0x80 +#define ENC_EIE_PKTIE 0x40 +#define ENC_EIE_DMAIE 0x20 +#define ENC_EIE_LINKIE 0x10 +#define ENC_EIE_TXIE 0x08 +#define ENC_EIE_WOLIE 0x04 +#define ENC_EIE_TXERIE 0x02 +#define ENC_EIE_RXERIE 0x01 + +/* + * MACON1 Register Bits + */ +#define ENC_MACON1_LOOPBK 0x10 +#define ENC_MACON1_TXPAUS 0x08 +#define ENC_MACON1_RXPAUS 0x04 +#define ENC_MACON1_PASSALL 0x02 +#define ENC_MACON1_MARXEN 0x01 + + +/* + * MACON2 Register Bits + */ +#define ENC_MACON2_MARST 0x80 +#define ENC_MACON2_RNDRST 0x40 +#define ENC_MACON2_MARXRST 0x08 +#define ENC_MACON2_RFUNRST 0x04 +#define ENC_MACON2_MATXRST 0x02 +#define ENC_MACON2_TFUNRST 0x01 + +/* + * MACON3 Register Bits + */ +#define ENC_MACON3_PADCFG2 0x80 +#define ENC_MACON3_PADCFG1 0x40 +#define ENC_MACON3_PADCFG0 0x20 +#define ENC_MACON3_TXCRCEN 0x10 +#define ENC_MACON3_PHDRLEN 0x08 +#define ENC_MACON3_HFRMEN 0x04 +#define ENC_MACON3_FRMLNEN 0x02 +#define ENC_MACON3_FULDPX 0x01 + +/* + * MICMD Register Bits + */ +#define ENC_MICMD_MIISCAN 0x02 +#define ENC_MICMD_MIIRD 0x01 + +/* + * MISTAT Register Bits + */ +#define ENC_MISTAT_NVALID 0x04 +#define ENC_MISTAT_SCAN 0x02 +#define ENC_MISTAT_BUSY 0x01 + +/* + * PHID1 and PHID2 values + */ +#define ENC_PHID1_VALUE 0x0083 +#define ENC_PHID2_VALUE 0x1400 +#define ENC_PHID2_MASK 0xFC00 + + +#define ENC_SPI_SLAVE_CS 0x00010000 /* pin P1.16 */ +#define ENC_RESET 0x00020000 /* pin P1.17 */ + +#define FAILSAFE_VALUE 5000 + +/* + * Controller memory layout: + * + * 0x0000 - 0x17ff 6k bytes receive buffer + * 0x1800 - 0x1fff 2k bytes transmit buffer + */ +/* Use the lower memory for receiver buffer. See errata pt. 5 */ +#define ENC_RX_BUF_START 0x0000 +#define ENC_TX_BUF_START 0x1800 +/* taken from the Linux driver */ +#define ENC_RX_BUF_END 0x17ff +#define ENC_TX_BUF_END 0x1fff + +/* maximum frame length */ +#define ENC_MAX_FRM_LEN 1518 + +#define enc_enable() PUT32(IO1CLR, ENC_SPI_SLAVE_CS) +#define enc_disable() PUT32(IO1SET, ENC_SPI_SLAVE_CS) +#define enc_cfg_spi() spi_set_cfg(0, 0, 0); spi_set_clock(8); + + +static unsigned char encReadReg (unsigned char regNo); +static void encWriteReg (unsigned char regNo, unsigned char data); +static void encWriteRegRetry (unsigned char regNo, unsigned char data, int c); +static void encReadBuff (unsigned short length, unsigned char *pBuff); +static void encWriteBuff (unsigned short length, unsigned char *pBuff); +static void encBitSet (unsigned char regNo, unsigned char data); +static void encBitClr (unsigned char regNo, unsigned char data); +static void encReset (void); +static void encInit (unsigned char *pEthAddr); +static unsigned short phyRead (unsigned char addr); +static void phyWrite(unsigned char, unsigned short); +static void encPoll (void); +static void encRx (void); + +#define m_nic_read(reg) encReadReg(reg) +#define m_nic_write(reg, data) encWriteReg(reg, data) +#define m_nic_write_retry(reg, data, count) encWriteRegRetry(reg, data, count) +#define m_nic_read_data(len, buf) encReadBuff((len), (buf)) +#define m_nic_write_data(len, buf) encWriteBuff((len), (buf)) + +/* bit field set */ +#define m_nic_bfs(reg, data) encBitSet(reg, data) + +/* bit field clear */ +#define m_nic_bfc(reg, data) encBitClr(reg, data) + +static unsigned char bank = 0; /* current bank in enc28j60 */ +static unsigned char next_pointer_lsb; +static unsigned char next_pointer_msb; + +static unsigned char buffer[ENC_MAX_FRM_LEN]; +static int rxResetCounter = 0; + +#define RX_RESET_COUNTER 1000; + +/*----------------------------------------------------------------------------- + * Always returns 0 + */ +int eth_init (bd_t * bis) +{ + unsigned char estatVal; + uchar enetaddr[6]; + + /* configure GPIO */ + (*((volatile unsigned long *) IO1DIR)) |= ENC_SPI_SLAVE_CS; + (*((volatile unsigned long *) IO1DIR)) |= ENC_RESET; + + /* CS and RESET active low */ + PUT32 (IO1SET, ENC_SPI_SLAVE_CS); + PUT32 (IO1SET, ENC_RESET); + + spi_init (); + + /* taken from the Linux driver - dangerous stuff here! */ + /* Wait for CLKRDY to become set (i.e., check that we can communicate with + the ENC) */ + do + { + estatVal = m_nic_read(CTL_REG_ESTAT); + } while ((estatVal & 0x08) || (~estatVal & ENC_ESTAT_CLKRDY)); + + /* initialize controller */ + encReset (); + eth_getenv_enetaddr("ethaddr", enetaddr); + encInit (enetaddr); + + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_RXEN); /* enable receive */ + + return 0; +} + +int eth_send (volatile void *packet, int length) +{ + /* check frame length, etc. */ + /* TODO: */ + + /* switch to bank 0 */ + m_nic_bfc (CTL_REG_ECON1, (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0)); + + /* set EWRPT */ + m_nic_write (CTL_REG_EWRPTL, (ENC_TX_BUF_START & 0xff)); + m_nic_write (CTL_REG_EWRPTH, (ENC_TX_BUF_START >> 8)); + + /* set ETXND */ + m_nic_write (CTL_REG_ETXNDL, (length + ENC_TX_BUF_START) & 0xFF); + m_nic_write (CTL_REG_ETXNDH, (length + ENC_TX_BUF_START) >> 8); + + /* set ETXST */ + m_nic_write (CTL_REG_ETXSTL, ENC_TX_BUF_START & 0xFF); + m_nic_write (CTL_REG_ETXSTH, ENC_TX_BUF_START >> 8); + + /* write packet */ + m_nic_write_data (length, (unsigned char *) packet); + + /* taken from the Linux driver */ + /* Verify that the internal transmit logic has not been altered by excessive + collisions. See Errata B4 12 and 14. + */ + if (m_nic_read(CTL_REG_EIR) & ENC_EIR_TXERIF) { + m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_TXRST); + m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_TXRST); + } + m_nic_bfc(CTL_REG_EIR, (ENC_EIR_TXERIF | ENC_EIR_TXIF)); + + /* set ECON1.TXRTS */ + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_TXRTS); + + return 0; +} + + +/***************************************************************************** + * This function resets the receiver only. This function may be called from + * interrupt-context. + */ +static void encReceiverReset (void) +{ + unsigned char econ1; + + econ1 = m_nic_read (CTL_REG_ECON1); + if ((econ1 & ENC_ECON1_RXRST) == 0) { + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_RXRST); + rxResetCounter = RX_RESET_COUNTER; + } +} + +/***************************************************************************** + * receiver reset timer + */ +static void encReceiverResetCallback (void) +{ + m_nic_bfc (CTL_REG_ECON1, ENC_ECON1_RXRST); + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_RXEN); /* enable receive */ +} + +/*----------------------------------------------------------------------------- + * Check for received packets. Call NetReceive for each packet. The return + * value is ignored by the caller. + */ +int eth_rx (void) +{ + if (rxResetCounter > 0 && --rxResetCounter == 0) { + encReceiverResetCallback (); + } + + encPoll (); + + return 0; +} + +void eth_halt (void) +{ + m_nic_bfc (CTL_REG_ECON1, ENC_ECON1_RXEN); /* disable receive */ +} + +/*****************************************************************************/ + +static void encPoll (void) +{ + unsigned char eir_reg; + volatile unsigned char estat_reg; + unsigned char pkt_cnt; + +#ifdef CONFIG_USE_IRQ + /* clear global interrupt enable bit in enc28j60 */ + m_nic_bfc (CTL_REG_EIE, ENC_EIE_INTIE); +#endif + estat_reg = m_nic_read (CTL_REG_ESTAT); + + eir_reg = m_nic_read (CTL_REG_EIR); + + if (eir_reg & ENC_EIR_TXIF) { + /* clear TXIF bit in EIR */ + m_nic_bfc (CTL_REG_EIR, ENC_EIR_TXIF); + } + + /* We have to use pktcnt and not pktif bit, see errata pt. 6 */ + + /* move to bank 1 */ + m_nic_bfc (CTL_REG_ECON1, ENC_ECON1_BSEL1); + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_BSEL0); + + /* read pktcnt */ + pkt_cnt = m_nic_read (CTL_REG_EPKTCNT); + + if (pkt_cnt > 0) { + if ((eir_reg & ENC_EIR_PKTIF) == 0) { + /*printf("encPoll: pkt cnt > 0, but pktif not set\n"); */ + } + encRx (); + /* clear PKTIF bit in EIR, this should not need to be done but it + seems like we get problems if we do not */ + m_nic_bfc (CTL_REG_EIR, ENC_EIR_PKTIF); + } + + if (eir_reg & ENC_EIR_RXERIF) { + printf ("encPoll: rx error\n"); + m_nic_bfc (CTL_REG_EIR, ENC_EIR_RXERIF); + } + if (eir_reg & ENC_EIR_TXERIF) { + printf ("encPoll: tx error\n"); + m_nic_bfc (CTL_REG_EIR, ENC_EIR_TXERIF); + } + +#ifdef CONFIG_USE_IRQ + /* set global interrupt enable bit in enc28j60 */ + m_nic_bfs (CTL_REG_EIE, ENC_EIE_INTIE); +#endif +} + +static void encRx (void) +{ + unsigned short pkt_len; + unsigned short copy_len; + unsigned short status; + unsigned char eir_reg; + unsigned char pkt_cnt = 0; + unsigned short rxbuf_rdpt; + + /* switch to bank 0 */ + m_nic_bfc (CTL_REG_ECON1, (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0)); + + m_nic_write (CTL_REG_ERDPTL, next_pointer_lsb); + m_nic_write (CTL_REG_ERDPTH, next_pointer_msb); + + do { + m_nic_read_data (6, buffer); + next_pointer_lsb = buffer[0]; + next_pointer_msb = buffer[1]; + pkt_len = buffer[2]; + pkt_len |= (unsigned short) buffer[3] << 8; + status = buffer[4]; + status |= (unsigned short) buffer[5] << 8; + + if (pkt_len <= ENC_MAX_FRM_LEN) + copy_len = pkt_len; + else + copy_len = 0; + + if ((status & (1L << 7)) == 0) /* check Received Ok bit */ + copy_len = 0; + + /* taken from the Linux driver */ + /* check if next pointer is resonable */ + if ((((unsigned int)next_pointer_msb << 8) | + (unsigned int)next_pointer_lsb) >= ENC_TX_BUF_START) + copy_len = 0; + + if (copy_len > 0) { + m_nic_read_data (copy_len, buffer); + } + + /* advance read pointer to next pointer */ + m_nic_write (CTL_REG_ERDPTL, next_pointer_lsb); + m_nic_write (CTL_REG_ERDPTH, next_pointer_msb); + + /* decrease packet counter */ + m_nic_bfs (CTL_REG_ECON2, ENC_ECON2_PKTDEC); + + /* taken from the Linux driver */ + /* Only odd values should be written to ERXRDPTL, + * see errata B4 pt.13 + */ + rxbuf_rdpt = (next_pointer_msb << 8 | next_pointer_lsb) - 1; + if ((rxbuf_rdpt < (m_nic_read(CTL_REG_ERXSTH) << 8 | + m_nic_read(CTL_REG_ERXSTL))) || (rxbuf_rdpt > + (m_nic_read(CTL_REG_ERXNDH) << 8 | + m_nic_read(CTL_REG_ERXNDL)))) { + m_nic_write(CTL_REG_ERXRDPTL, m_nic_read(CTL_REG_ERXNDL)); + m_nic_write(CTL_REG_ERXRDPTH, m_nic_read(CTL_REG_ERXNDH)); + } else { + m_nic_write(CTL_REG_ERXRDPTL, rxbuf_rdpt & 0xFF); + m_nic_write(CTL_REG_ERXRDPTH, rxbuf_rdpt >> 8); + } + + /* move to bank 1 */ + m_nic_bfc (CTL_REG_ECON1, ENC_ECON1_BSEL1); + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_BSEL0); + + /* read pktcnt */ + pkt_cnt = m_nic_read (CTL_REG_EPKTCNT); + + /* switch to bank 0 */ + m_nic_bfc (CTL_REG_ECON1, + (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0)); + + if (copy_len == 0) { + eir_reg = m_nic_read (CTL_REG_EIR); + encReceiverReset (); + printf ("eth_rx: copy_len=0\n"); + continue; + } + + NetReceive ((unsigned char *) buffer, pkt_len); + + eir_reg = m_nic_read (CTL_REG_EIR); + } while (pkt_cnt); /* Use EPKTCNT not EIR.PKTIF flag, see errata pt. 6 */ +} + +static void encWriteReg (unsigned char regNo, unsigned char data) +{ + spi_lock (); + enc_cfg_spi (); + enc_enable (); + + spi_write (0x40 | regNo); /* write in regNo */ + spi_write (data); + + enc_disable (); + enc_enable (); + + spi_write (0x1f); /* write reg 0x1f */ + + enc_disable (); + spi_unlock (); +} + +static void encWriteRegRetry (unsigned char regNo, unsigned char data, int c) +{ + unsigned char readback; + int i; + + spi_lock (); + + for (i = 0; i < c; i++) { + enc_cfg_spi (); + enc_enable (); + + spi_write (0x40 | regNo); /* write in regNo */ + spi_write (data); + + enc_disable (); + enc_enable (); + + spi_write (0x1f); /* write reg 0x1f */ + + enc_disable (); + + spi_unlock (); /* we must unlock spi first */ + + readback = encReadReg (regNo); + + spi_lock (); + + if (readback == data) + break; + } + spi_unlock (); + + if (i == c) { + printf ("enc28j60: write reg %d failed\n", regNo); + } +} + +static unsigned char encReadReg (unsigned char regNo) +{ + unsigned char rxByte; + + spi_lock (); + enc_cfg_spi (); + enc_enable (); + + spi_write (0x1f); /* read reg 0x1f */ + + bank = spi_read () & 0x3; + + enc_disable (); + enc_enable (); + + spi_write (regNo); + rxByte = spi_read (); + + /* check if MAC or MII register */ + if (((bank == 2) && (regNo <= 0x1a)) || + ((bank == 3) && (regNo <= 0x05 || regNo == 0x0a))) { + /* ignore first byte and read another byte */ + rxByte = spi_read (); + } + + enc_disable (); + spi_unlock (); + + return rxByte; +} + +static void encReadBuff (unsigned short length, unsigned char *pBuff) +{ + spi_lock (); + enc_cfg_spi (); + enc_enable (); + + spi_write (0x20 | 0x1a); /* read buffer memory */ + + while (length--) { + if (pBuff != NULL) + *pBuff++ = spi_read (); + else + spi_write (0); + } + + enc_disable (); + spi_unlock (); +} + +static void encWriteBuff (unsigned short length, unsigned char *pBuff) +{ + spi_lock (); + enc_cfg_spi (); + enc_enable (); + + spi_write (0x60 | 0x1a); /* write buffer memory */ + + spi_write (0x00); /* control byte */ + + while (length--) + spi_write (*pBuff++); + + enc_disable (); + spi_unlock (); +} + +static void encBitSet (unsigned char regNo, unsigned char data) +{ + spi_lock (); + enc_cfg_spi (); + enc_enable (); + + spi_write (0x80 | regNo); /* bit field set */ + spi_write (data); + + enc_disable (); + spi_unlock (); +} + +static void encBitClr (unsigned char regNo, unsigned char data) +{ + spi_lock (); + enc_cfg_spi (); + enc_enable (); + + spi_write (0xA0 | regNo); /* bit field clear */ + spi_write (data); + + enc_disable (); + spi_unlock (); +} + +static void encReset (void) +{ + spi_lock (); + enc_cfg_spi (); + enc_enable (); + + spi_write (0xff); /* soft reset */ + + enc_disable (); + spi_unlock (); + + /* sleep 1 ms. See errata pt. 2 */ + udelay (1000); +} + +static void encInit (unsigned char *pEthAddr) +{ + unsigned short phid1 = 0; + unsigned short phid2 = 0; + + /* switch to bank 0 */ + m_nic_bfc (CTL_REG_ECON1, (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0)); + + /* + * Setup the buffer space. The reset values are valid for the + * other pointers. + */ + /* We shall not write to ERXST, see errata pt. 5. Instead we + have to make sure that ENC_RX_BUS_START is 0. */ + m_nic_write_retry (CTL_REG_ERXSTL, (ENC_RX_BUF_START & 0xFF), 1); + m_nic_write_retry (CTL_REG_ERXSTH, (ENC_RX_BUF_START >> 8), 1); + + /* taken from the Linux driver */ + m_nic_write_retry (CTL_REG_ERXNDL, (ENC_RX_BUF_END & 0xFF), 1); + m_nic_write_retry (CTL_REG_ERXNDH, (ENC_RX_BUF_END >> 8), 1); + + m_nic_write_retry (CTL_REG_ERDPTL, (ENC_RX_BUF_START & 0xFF), 1); + m_nic_write_retry (CTL_REG_ERDPTH, (ENC_RX_BUF_START >> 8), 1); + + next_pointer_lsb = (ENC_RX_BUF_START & 0xFF); + next_pointer_msb = (ENC_RX_BUF_START >> 8); + + /* verify identification */ + phid1 = phyRead (PHY_REG_PHID1); + phid2 = phyRead (PHY_REG_PHID2); + + if (phid1 != ENC_PHID1_VALUE + || (phid2 & ENC_PHID2_MASK) != ENC_PHID2_VALUE) { + printf ("ERROR: failed to identify controller\n"); + printf ("phid1 = %x, phid2 = %x\n", + phid1, (phid2 & ENC_PHID2_MASK)); + printf ("should be phid1 = %x, phid2 = %x\n", + ENC_PHID1_VALUE, ENC_PHID2_VALUE); + } + + /* + * --- MAC Initialization --- + */ + + /* Pull MAC out of Reset */ + + /* switch to bank 2 */ + m_nic_bfc (CTL_REG_ECON1, ENC_ECON1_BSEL0); + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_BSEL1); + + /* enable MAC to receive frames */ + /* added some bits from the Linux driver */ + m_nic_write_retry (CTL_REG_MACON1 + ,(ENC_MACON1_MARXEN | ENC_MACON1_TXPAUS | ENC_MACON1_RXPAUS) + ,10); + + /* configure pad, tx-crc and duplex */ + /* added a bit from the Linux driver */ + m_nic_write_retry (CTL_REG_MACON3 + ,(ENC_MACON3_PADCFG0 | ENC_MACON3_TXCRCEN | ENC_MACON3_FRMLNEN) + ,10); + + /* added 4 new lines from the Linux driver */ + /* Allow infinite deferals if the medium is continously busy */ + m_nic_write_retry(CTL_REG_MACON4, (1<<6) /*ENC_MACON4_DEFER*/, 10); + + /* Late collisions occur beyond 63 bytes */ + m_nic_write_retry(CTL_REG_MACLCON2, 63, 10); + + /* Set (low byte) Non-Back-to_Back Inter-Packet Gap. Recommended 0x12 */ + m_nic_write_retry(CTL_REG_MAIPGL, 0x12, 10); + + /* + * Set (high byte) Non-Back-to_Back Inter-Packet Gap. Recommended + * 0x0c for half-duplex. Nothing for full-duplex + */ + m_nic_write_retry(CTL_REG_MAIPGH, 0x0C, 10); + + /* set maximum frame length */ + m_nic_write_retry (CTL_REG_MAMXFLL, (ENC_MAX_FRM_LEN & 0xff), 10); + m_nic_write_retry (CTL_REG_MAMXFLH, (ENC_MAX_FRM_LEN >> 8), 10); + + /* + * Set MAC back-to-back inter-packet gap. Recommended 0x12 for half duplex + * and 0x15 for full duplex. + */ + m_nic_write_retry (CTL_REG_MABBIPG, 0x12, 10); + + /* set MAC address */ + + /* switch to bank 3 */ + m_nic_bfs (CTL_REG_ECON1, (ENC_ECON1_BSEL0 | ENC_ECON1_BSEL1)); + + m_nic_write_retry (CTL_REG_MAADR0, pEthAddr[5], 1); + m_nic_write_retry (CTL_REG_MAADR1, pEthAddr[4], 1); + m_nic_write_retry (CTL_REG_MAADR2, pEthAddr[3], 1); + m_nic_write_retry (CTL_REG_MAADR3, pEthAddr[2], 1); + m_nic_write_retry (CTL_REG_MAADR4, pEthAddr[1], 1); + m_nic_write_retry (CTL_REG_MAADR5, pEthAddr[0], 1); + + /* + * PHY Initialization taken from the Linux driver + */ + + /* Prevent automatic loopback of data beeing transmitted by setting + ENC_PHCON2_HDLDIS */ + phyWrite(PHY_REG_PHCON2, (1<<8)); + + /* LEDs configuration + * LEDA: LACFG = 0100 -> display link status + * LEDB: LBCFG = 0111 -> display TX & RX activity + * STRCH = 1 -> LED pulses + */ + phyWrite(PHY_REG_PHLCON, 0x0472); + + /* Reset PDPXMD-bit => half duplex */ + phyWrite(PHY_REG_PHCON1, 0); + + /* + * Receive settings + */ + +#ifdef CONFIG_USE_IRQ + /* enable interrupts */ + m_nic_bfs (CTL_REG_EIE, ENC_EIE_PKTIE); + m_nic_bfs (CTL_REG_EIE, ENC_EIE_TXIE); + m_nic_bfs (CTL_REG_EIE, ENC_EIE_RXERIE); + m_nic_bfs (CTL_REG_EIE, ENC_EIE_TXERIE); + m_nic_bfs (CTL_REG_EIE, ENC_EIE_INTIE); +#endif +} + +/***************************************************************************** + * + * Description: + * Read PHY registers. + * + * NOTE! This function will change to Bank 2. + * + * Params: + * [in] addr address of the register to read + * + * Returns: + * The value in the register + */ +static unsigned short phyRead (unsigned char addr) +{ + unsigned short ret = 0; + + /* move to bank 2 */ + m_nic_bfc (CTL_REG_ECON1, ENC_ECON1_BSEL0); + m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_BSEL1); + + /* write address to MIREGADR */ + m_nic_write (CTL_REG_MIREGADR, addr); + + /* set MICMD.MIIRD */ + m_nic_write (CTL_REG_MICMD, ENC_MICMD_MIIRD); + + /* taken from the Linux driver */ + /* move to bank 3 */ + m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL0); + m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL1); + + /* poll MISTAT.BUSY bit until operation is complete */ + while ((m_nic_read (CTL_REG_MISTAT) & ENC_MISTAT_BUSY) != 0) { + static int cnt = 0; + + if (cnt++ >= 1000) { + /* GJ - this seems extremely dangerous! */ + /* printf("#"); */ + cnt = 0; + } + } + + /* taken from the Linux driver */ + /* move to bank 2 */ + m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_BSEL0); + m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL1); + + /* clear MICMD.MIIRD */ + m_nic_write (CTL_REG_MICMD, 0); + + ret = (m_nic_read (CTL_REG_MIRDH) << 8); + ret |= (m_nic_read (CTL_REG_MIRDL) & 0xFF); + + return ret; +} + +/***************************************************************************** + * + * Taken from the Linux driver. + * Description: + * Write PHY registers. + * + * NOTE! This function will change to Bank 3. + * + * Params: + * [in] addr address of the register to write to + * [in] data to be written + * + * Returns: + * None + */ +static void phyWrite(unsigned char addr, unsigned short data) +{ + /* move to bank 2 */ + m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_BSEL0); + m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL1); + + /* write address to MIREGADR */ + m_nic_write(CTL_REG_MIREGADR, addr); + + m_nic_write(CTL_REG_MIWRL, data & 0xff); + m_nic_write(CTL_REG_MIWRH, data >> 8); + + /* move to bank 3 */ + m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL0); + m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL1); + + /* poll MISTAT.BUSY bit until operation is complete */ + while((m_nic_read(CTL_REG_MISTAT) & ENC_MISTAT_BUSY) != 0) { + static int cnt = 0; + + if(cnt++ >= 1000) { + cnt = 0; + } + } +} diff --git a/roms/u-boot-sam460ex/drivers/net/ep93xx_eth.c b/roms/u-boot-sam460ex/drivers/net/ep93xx_eth.c new file mode 100644 index 000000000..4e39948d2 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ep93xx_eth.c @@ -0,0 +1,653 @@ +/* + * Cirrus Logic EP93xx ethernet MAC / MII driver. + * + * Copyright (C) 2010, 2009 + * Matthias Kaehlcke <matthias@kaehlcke.net> + * + * Copyright (C) 2004, 2005 + * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com> + * + * Based on the original eth.[ch] Cirrus Logic EP93xx Rev D. Ethernet Driver, + * which is + * + * (C) Copyright 2002 2003 + * Adam Bezanson, Network Audio Technologies, Inc. + * <bezanson@netaudiotech.com> + * + * See file CREDITS for list of people who contributed to this project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <command.h> +#include <common.h> +#include <asm/arch/ep93xx.h> +#include <asm/io.h> +#include <malloc.h> +#include <miiphy.h> +#include <linux/types.h> +#include "ep93xx_eth.h" + +#define GET_PRIV(eth_dev) ((struct ep93xx_priv *)(eth_dev)->priv) +#define GET_REGS(eth_dev) (GET_PRIV(eth_dev)->regs) + +/* ep93xx_miiphy ops forward declarations */ +static int ep93xx_miiphy_read(char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short * const value); +static int ep93xx_miiphy_write(char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short const value); + +#if defined(EP93XX_MAC_DEBUG) +/** + * Dump ep93xx_mac values to the terminal. + */ +static void dump_dev(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_dev()\n"); + printf(" rx_dq.base %p\n", priv->rx_dq.base); + printf(" rx_dq.current %p\n", priv->rx_dq.current); + printf(" rx_dq.end %p\n", priv->rx_dq.end); + printf(" rx_sq.base %p\n", priv->rx_sq.base); + printf(" rx_sq.current %p\n", priv->rx_sq.current); + printf(" rx_sq.end %p\n", priv->rx_sq.end); + + for (i = 0; i < NUMRXDESC; i++) + printf(" rx_buffer[%2.d] %p\n", i, NetRxPackets[i]); + + printf(" tx_dq.base %p\n", priv->tx_dq.base); + printf(" tx_dq.current %p\n", priv->tx_dq.current); + printf(" tx_dq.end %p\n", priv->tx_dq.end); + printf(" tx_sq.base %p\n", priv->tx_sq.base); + printf(" tx_sq.current %p\n", priv->tx_sq.current); + printf(" tx_sq.end %p\n", priv->tx_sq.end); +} + +/** + * Dump all RX status queue entries to the terminal. + */ +static void dump_rx_status_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_rx_status_queue()\n"); + printf(" descriptor address word1 word2\n"); + for (i = 0; i < NUMRXDESC; i++) { + printf(" [ %p ] %08X %08X\n", + priv->rx_sq.base + i, + (priv->rx_sq.base + i)->word1, + (priv->rx_sq.base + i)->word2); + } +} + +/** + * Dump all RX descriptor queue entries to the terminal. + */ +static void dump_rx_descriptor_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_rx_descriptor_queue()\n"); + printf(" descriptor address word1 word2\n"); + for (i = 0; i < NUMRXDESC; i++) { + printf(" [ %p ] %08X %08X\n", + priv->rx_dq.base + i, + (priv->rx_dq.base + i)->word1, + (priv->rx_dq.base + i)->word2); + } +} + +/** + * Dump all TX descriptor queue entries to the terminal. + */ +static void dump_tx_descriptor_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_tx_descriptor_queue()\n"); + printf(" descriptor address word1 word2\n"); + for (i = 0; i < NUMTXDESC; i++) { + printf(" [ %p ] %08X %08X\n", + priv->tx_dq.base + i, + (priv->tx_dq.base + i)->word1, + (priv->tx_dq.base + i)->word2); + } +} + +/** + * Dump all TX status queue entries to the terminal. + */ +static void dump_tx_status_queue(struct eth_device *dev) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + int i; + + printf("\ndump_tx_status_queue()\n"); + printf(" descriptor address word1\n"); + for (i = 0; i < NUMTXDESC; i++) { + printf(" [ %p ] %08X\n", + priv->rx_sq.base + i, + (priv->rx_sq.base + i)->word1); + } +} +#else +#define dump_dev(x) +#define dump_rx_descriptor_queue(x) +#define dump_rx_status_queue(x) +#define dump_tx_descriptor_queue(x) +#define dump_tx_status_queue(x) +#endif /* defined(EP93XX_MAC_DEBUG) */ + +/** + * Reset the EP93xx MAC by twiddling the soft reset bit and spinning until + * it's cleared. + */ +static void ep93xx_mac_reset(struct eth_device *dev) +{ + struct mac_regs *mac = GET_REGS(dev); + uint32_t value; + + debug("+ep93xx_mac_reset"); + + value = readl(&mac->selfctl); + value |= SELFCTL_RESET; + writel(value, &mac->selfctl); + + while (readl(&mac->selfctl) & SELFCTL_RESET) + ; /* noop */ + + debug("-ep93xx_mac_reset"); +} + +/* Eth device open */ +static int ep93xx_eth_open(struct eth_device *dev, bd_t *bd) +{ + struct ep93xx_priv *priv = GET_PRIV(dev); + struct mac_regs *mac = GET_REGS(dev); + uchar *mac_addr = dev->enetaddr; + int i; + + debug("+ep93xx_eth_open"); + + /* Reset the MAC */ + ep93xx_mac_reset(dev); + + /* Reset the descriptor queues' current and end address values */ + priv->tx_dq.current = priv->tx_dq.base; + priv->tx_dq.end = (priv->tx_dq.base + NUMTXDESC); + + priv->tx_sq.current = priv->tx_sq.base; + priv->tx_sq.end = (priv->tx_sq.base + NUMTXDESC); + + priv->rx_dq.current = priv->rx_dq.base; + priv->rx_dq.end = (priv->rx_dq.base + NUMRXDESC); + + priv->rx_sq.current = priv->rx_sq.base; + priv->rx_sq.end = (priv->rx_sq.base + NUMRXDESC); + + /* + * Set the transmit descriptor and status queues' base address, + * current address, and length registers. Set the maximum frame + * length and threshold. Enable the transmit descriptor processor. + */ + writel((uint32_t)priv->tx_dq.base, &mac->txdq.badd); + writel((uint32_t)priv->tx_dq.base, &mac->txdq.curadd); + writel(sizeof(struct tx_descriptor) * NUMTXDESC, &mac->txdq.blen); + + writel((uint32_t)priv->tx_sq.base, &mac->txstsq.badd); + writel((uint32_t)priv->tx_sq.base, &mac->txstsq.curadd); + writel(sizeof(struct tx_status) * NUMTXDESC, &mac->txstsq.blen); + + writel(0x00040000, &mac->txdthrshld); + writel(0x00040000, &mac->txststhrshld); + + writel((TXSTARTMAX << 0) | (PKTSIZE_ALIGN << 16), &mac->maxfrmlen); + writel(BMCTL_TXEN, &mac->bmctl); + + /* + * Set the receive descriptor and status queues' base address, + * current address, and length registers. Enable the receive + * descriptor processor. + */ + writel((uint32_t)priv->rx_dq.base, &mac->rxdq.badd); + writel((uint32_t)priv->rx_dq.base, &mac->rxdq.curadd); + writel(sizeof(struct rx_descriptor) * NUMRXDESC, &mac->rxdq.blen); + + writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.badd); + writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.curadd); + writel(sizeof(struct rx_status) * NUMRXDESC, &mac->rxstsq.blen); + + writel(0x00040000, &mac->rxdthrshld); + + writel(BMCTL_RXEN, &mac->bmctl); + + writel(0x00040000, &mac->rxststhrshld); + + /* Wait until the receive descriptor processor is active */ + while (!(readl(&mac->bmsts) & BMSTS_RXACT)) + ; /* noop */ + + /* + * Initialize the RX descriptor queue. Clear the TX descriptor queue. + * Clear the RX and TX status queues. Enqueue the RX descriptor and + * status entries to the MAC. + */ + for (i = 0; i < NUMRXDESC; i++) { + /* set buffer address */ + (priv->rx_dq.base + i)->word1 = (uint32_t)NetRxPackets[i]; + + /* set buffer length, clear buffer index and NSOF */ + (priv->rx_dq.base + i)->word2 = PKTSIZE_ALIGN; + } + + memset(priv->tx_dq.base, 0, + (sizeof(struct tx_descriptor) * NUMTXDESC)); + memset(priv->rx_sq.base, 0, + (sizeof(struct rx_status) * NUMRXDESC)); + memset(priv->tx_sq.base, 0, + (sizeof(struct tx_status) * NUMTXDESC)); + + writel(NUMRXDESC, &mac->rxdqenq); + writel(NUMRXDESC, &mac->rxstsqenq); + + /* Set the primary MAC address */ + writel(AFP_IAPRIMARY, &mac->afp); + writel(mac_addr[0] | (mac_addr[1] << 8) | + (mac_addr[2] << 16) | (mac_addr[3] << 24), + &mac->indad); + writel(mac_addr[4] | (mac_addr[5] << 8), &mac->indad_upper); + + /* Turn on RX and TX */ + writel(RXCTL_IA0 | RXCTL_BA | RXCTL_SRXON | + RXCTL_RCRCA | RXCTL_MA, &mac->rxctl); + writel(TXCTL_STXON, &mac->txctl); + + /* Dump data structures if we're debugging */ + dump_dev(dev); + dump_rx_descriptor_queue(dev); + dump_rx_status_queue(dev); + dump_tx_descriptor_queue(dev); + dump_tx_status_queue(dev); + + debug("-ep93xx_eth_open"); + + return 1; +} + +/** + * Halt EP93xx MAC transmit and receive by clearing the TxCTL and RxCTL + * registers. + */ +static void ep93xx_eth_close(struct eth_device *dev) +{ + struct mac_regs *mac = GET_REGS(dev); + + debug("+ep93xx_eth_close"); + + writel(0x00000000, &mac->rxctl); + writel(0x00000000, &mac->txctl); + + debug("-ep93xx_eth_close"); +} + +/** + * Copy a frame of data from the MAC into the protocol layer for further + * processing. + */ +static int ep93xx_eth_rcv_packet(struct eth_device *dev) +{ + struct mac_regs *mac = GET_REGS(dev); + struct ep93xx_priv *priv = GET_PRIV(dev); + int len = -1; + + debug("+ep93xx_eth_rcv_packet"); + + if (RX_STATUS_RFP(priv->rx_sq.current)) { + if (RX_STATUS_RWE(priv->rx_sq.current)) { + /* + * We have a good frame. Extract the frame's length + * from the current rx_status_queue entry, and copy + * the frame's data into NetRxPackets[] of the + * protocol stack. We track the total number of + * bytes in the frame (nbytes_frame) which will be + * used when we pass the data off to the protocol + * layer via NetReceive(). + */ + len = RX_STATUS_FRAME_LEN(priv->rx_sq.current); + + NetReceive((uchar *)priv->rx_dq.current->word1, len); + + debug("reporting %d bytes...\n", len); + } else { + /* Do we have an erroneous packet? */ + error("packet rx error, status %08X %08X", + priv->rx_sq.current->word1, + priv->rx_sq.current->word2); + dump_rx_descriptor_queue(dev); + dump_rx_status_queue(dev); + } + + /* + * Clear the associated status queue entry, and + * increment our current pointers to the next RX + * descriptor and status queue entries (making sure + * we wrap properly). + */ + memset((void *)priv->rx_sq.current, 0, + sizeof(struct rx_status)); + + priv->rx_sq.current++; + if (priv->rx_sq.current >= priv->rx_sq.end) + priv->rx_sq.current = priv->rx_sq.base; + + priv->rx_dq.current++; + if (priv->rx_dq.current >= priv->rx_dq.end) + priv->rx_dq.current = priv->rx_dq.base; + + /* + * Finally, return the RX descriptor and status entries + * back to the MAC engine, and loop again, checking for + * more descriptors to process. + */ + writel(1, &mac->rxdqenq); + writel(1, &mac->rxstsqenq); + } else { + len = 0; + } + + debug("-ep93xx_eth_rcv_packet %d", len); + return len; +} + +/** + * Send a block of data via ethernet. + */ +static int ep93xx_eth_send_packet(struct eth_device *dev, + volatile void * const packet, int const length) +{ + struct mac_regs *mac = GET_REGS(dev); + struct ep93xx_priv *priv = GET_PRIV(dev); + int ret = -1; + + debug("+ep93xx_eth_send_packet"); + + /* Parameter check */ + BUG_ON(packet == NULL); + + /* + * Initialize the TX descriptor queue with the new packet's info. + * Clear the associated status queue entry. Enqueue the packet + * to the MAC for transmission. + */ + + /* set buffer address */ + priv->tx_dq.current->word1 = (uint32_t)packet; + + /* set buffer length and EOF bit */ + priv->tx_dq.current->word2 = length | TX_DESC_EOF; + + /* clear tx status */ + priv->tx_sq.current->word1 = 0; + + /* enqueue the TX descriptor */ + writel(1, &mac->txdqenq); + + /* wait for the frame to become processed */ + while (!TX_STATUS_TXFP(priv->tx_sq.current)) + ; /* noop */ + + if (!TX_STATUS_TXWE(priv->tx_sq.current)) { + error("packet tx error, status %08X", + priv->tx_sq.current->word1); + dump_tx_descriptor_queue(dev); + dump_tx_status_queue(dev); + + /* TODO: Add better error handling? */ + goto eth_send_out; + } + + ret = 0; + /* Fall through */ + +eth_send_out: + debug("-ep93xx_eth_send_packet %d", ret); + return ret; +} + +#if defined(CONFIG_MII) +int ep93xx_miiphy_initialize(bd_t * const bd) +{ + miiphy_register("ep93xx_eth0", ep93xx_miiphy_read, ep93xx_miiphy_write); + return 0; +} +#endif + +/** + * Initialize the EP93xx MAC. The MAC hardware is reset. Buffers are + * allocated, if necessary, for the TX and RX descriptor and status queues, + * as well as for received packets. The EP93XX MAC hardware is initialized. + * Transmit and receive operations are enabled. + */ +int ep93xx_eth_initialize(u8 dev_num, int base_addr) +{ + int ret = -1; + struct eth_device *dev; + struct ep93xx_priv *priv; + + debug("+ep93xx_eth_initialize"); + + priv = malloc(sizeof(*priv)); + if (!priv) { + error("malloc() failed"); + goto eth_init_failed_0; + } + memset(priv, 0, sizeof(*priv)); + + priv->regs = (struct mac_regs *)base_addr; + + priv->tx_dq.base = calloc(NUMTXDESC, + sizeof(struct tx_descriptor)); + if (priv->tx_dq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_1; + } + + priv->tx_sq.base = calloc(NUMTXDESC, + sizeof(struct tx_status)); + if (priv->tx_sq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_2; + } + + priv->rx_dq.base = calloc(NUMRXDESC, + sizeof(struct rx_descriptor)); + if (priv->rx_dq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_3; + } + + priv->rx_sq.base = calloc(NUMRXDESC, + sizeof(struct rx_status)); + if (priv->rx_sq.base == NULL) { + error("calloc() failed"); + goto eth_init_failed_4; + } + + dev = malloc(sizeof *dev); + if (dev == NULL) { + error("malloc() failed"); + goto eth_init_failed_5; + } + memset(dev, 0, sizeof *dev); + + dev->iobase = base_addr; + dev->priv = priv; + dev->init = ep93xx_eth_open; + dev->halt = ep93xx_eth_close; + dev->send = ep93xx_eth_send_packet; + dev->recv = ep93xx_eth_rcv_packet; + + sprintf(dev->name, "ep93xx_eth-%hu", dev_num); + + eth_register(dev); + + /* Done! */ + ret = 1; + goto eth_init_done; + +eth_init_failed_5: + free(priv->rx_sq.base); + /* Fall through */ + +eth_init_failed_4: + free(priv->rx_dq.base); + /* Fall through */ + +eth_init_failed_3: + free(priv->tx_sq.base); + /* Fall through */ + +eth_init_failed_2: + free(priv->tx_dq.base); + /* Fall through */ + +eth_init_failed_1: + free(priv); + /* Fall through */ + +eth_init_failed_0: + /* Fall through */ + +eth_init_done: + debug("-ep93xx_eth_initialize %d", ret); + return ret; +} + +#if defined(CONFIG_MII) + +/** + * Maximum MII address we support + */ +#define MII_ADDRESS_MAX 31 + +/** + * Maximum MII register address we support + */ +#define MII_REGISTER_MAX 31 + +/** + * Read a 16-bit value from an MII register. + */ +static int ep93xx_miiphy_read(char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short * const value) +{ + struct mac_regs *mac = (struct mac_regs *)MAC_BASE; + int ret = -1; + uint32_t self_ctl; + + debug("+ep93xx_miiphy_read"); + + /* Parameter checks */ + BUG_ON(dev == NULL); + BUG_ON(addr > MII_ADDRESS_MAX); + BUG_ON(reg > MII_REGISTER_MAX); + BUG_ON(value == NULL); + + /* + * Save the current SelfCTL register value. Set MAC to suppress + * preamble bits. Wait for any previous MII command to complete + * before issuing the new command. + */ + self_ctl = readl(&mac->selfctl); +#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) + writel(self_ctl & ~(1 << 8), &mac->selfctl); +#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ + + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + /* + * Issue the MII 'read' command. Wait for the command to complete. + * Read the MII data value. + */ + writel(MIICMD_OPCODE_READ | ((uint32_t)addr << 5) | (uint32_t)reg, + &mac->miicmd); + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + *value = (unsigned short)readl(&mac->miidata); + + /* Restore the saved SelfCTL value and return. */ + writel(self_ctl, &mac->selfctl); + + ret = 0; + /* Fall through */ + + debug("-ep93xx_miiphy_read"); + return ret; +} + +/** + * Write a 16-bit value to an MII register. + */ +static int ep93xx_miiphy_write(char * const dev, unsigned char const addr, + unsigned char const reg, unsigned short const value) +{ + struct mac_regs *mac = (struct mac_regs *)MAC_BASE; + int ret = -1; + uint32_t self_ctl; + + debug("+ep93xx_miiphy_write"); + + /* Parameter checks */ + BUG_ON(dev == NULL); + BUG_ON(addr > MII_ADDRESS_MAX); + BUG_ON(reg > MII_REGISTER_MAX); + + /* + * Save the current SelfCTL register value. Set MAC to suppress + * preamble bits. Wait for any previous MII command to complete + * before issuing the new command. + */ + self_ctl = readl(&mac->selfctl); +#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) + writel(self_ctl & ~(1 << 8), &mac->selfctl); +#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ + + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + /* Issue the MII 'write' command. Wait for the command to complete. */ + writel((uint32_t)value, &mac->miidata); + writel(MIICMD_OPCODE_WRITE | ((uint32_t)addr << 5) | (uint32_t)reg, + &mac->miicmd); + while (readl(&mac->miists) & MIISTS_BUSY) + ; /* noop */ + + /* Restore the saved SelfCTL value and return. */ + writel(self_ctl, &mac->selfctl); + + ret = 0; + /* Fall through */ + + debug("-ep93xx_miiphy_write"); + return ret; +} +#endif /* defined(CONFIG_MII) */ diff --git a/roms/u-boot-sam460ex/drivers/net/ep93xx_eth.h b/roms/u-boot-sam460ex/drivers/net/ep93xx_eth.h new file mode 100644 index 000000000..4654b2f0c --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ep93xx_eth.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net> + * + * Copyright (C) 2004, 2005 + * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#ifndef _EP93XX_ETH_H +#define _EP93XX_ETH_H + +#include <net.h> + +/** + * #define this to dump device status and queue info during initialization and + * following errors. + */ +#undef EP93XX_MAC_DEBUG + +/** + * Number of descriptor and status entries in our RX queues. + * It must be power of 2 ! + */ +#define NUMRXDESC PKTBUFSRX + +/** + * Number of descriptor and status entries in our TX queues. + */ +#define NUMTXDESC 1 + +/** + * 944 = (1024 - 64) - 16, Fifo size - Minframesize - 16 (Chip FACT) + */ +#define TXSTARTMAX 944 + +/** + * Receive descriptor queue entry + */ +struct rx_descriptor { + uint32_t word1; + uint32_t word2; +}; + +/** + * Receive status queue entry + */ +struct rx_status { + uint32_t word1; + uint32_t word2; +}; + +#define RX_STATUS_RWE(rx_status) ((rx_status->word1 >> 30) & 0x01) +#define RX_STATUS_RFP(rx_status) ((rx_status->word1 >> 31) & 0x01) +#define RX_STATUS_FRAME_LEN(rx_status) (rx_status->word2 & 0xFFFF) + +/** + * Transmit descriptor queue entry + */ +struct tx_descriptor { + uint32_t word1; + uint32_t word2; +}; + +#define TX_DESC_EOF (1 << 31) + +/** + * Transmit status queue entry + */ +struct tx_status { + uint32_t word1; +}; + +#define TX_STATUS_TXWE(tx_status) (((tx_status)->word1 >> 30) & 0x01) +#define TX_STATUS_TXFP(tx_status) (((tx_status)->word1 >> 31) & 0x01) + +/** + * Transmit descriptor queue + */ +struct tx_descriptor_queue { + struct tx_descriptor *base; + struct tx_descriptor *current; + struct tx_descriptor *end; +}; + +/** + * Transmit status queue + */ +struct tx_status_queue { + struct tx_status *base; + volatile struct tx_status *current; + struct tx_status *end; +}; + +/** + * Receive descriptor queue + */ +struct rx_descriptor_queue { + struct rx_descriptor *base; + struct rx_descriptor *current; + struct rx_descriptor *end; +}; + +/** + * Receive status queue + */ +struct rx_status_queue { + struct rx_status *base; + volatile struct rx_status *current; + struct rx_status *end; +}; + +/** + * EP93xx MAC private data structure + */ +struct ep93xx_priv { + struct rx_descriptor_queue rx_dq; + struct rx_status_queue rx_sq; + void *rx_buffer[NUMRXDESC]; + + struct tx_descriptor_queue tx_dq; + struct tx_status_queue tx_sq; + + struct mac_regs *regs; +}; + +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/ethoc.c b/roms/u-boot-sam460ex/drivers/net/ethoc.c new file mode 100644 index 000000000..34cc47f39 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ethoc.c @@ -0,0 +1,511 @@ +/* + * Opencore 10/100 ethernet mac driver + * + * Copyright (C) 2007-2008 Avionic Design Development GmbH + * Copyright (C) 2008-2009 Avionic Design GmbH + * Thierry Reding <thierry.reding@avionic-design.de> + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include <asm/io.h> +#include <asm/cache.h> + +/* register offsets */ +#define MODER 0x00 +#define INT_SOURCE 0x04 +#define INT_MASK 0x08 +#define IPGT 0x0c +#define IPGR1 0x10 +#define IPGR2 0x14 +#define PACKETLEN 0x18 +#define COLLCONF 0x1c +#define TX_BD_NUM 0x20 +#define CTRLMODER 0x24 +#define MIIMODER 0x28 +#define MIICOMMAND 0x2c +#define MIIADDRESS 0x30 +#define MIITX_DATA 0x34 +#define MIIRX_DATA 0x38 +#define MIISTATUS 0x3c +#define MAC_ADDR0 0x40 +#define MAC_ADDR1 0x44 +#define ETH_HASH0 0x48 +#define ETH_HASH1 0x4c +#define ETH_TXCTRL 0x50 + +/* mode register */ +#define MODER_RXEN (1 << 0) /* receive enable */ +#define MODER_TXEN (1 << 1) /* transmit enable */ +#define MODER_NOPRE (1 << 2) /* no preamble */ +#define MODER_BRO (1 << 3) /* broadcast address */ +#define MODER_IAM (1 << 4) /* individual address mode */ +#define MODER_PRO (1 << 5) /* promiscuous mode */ +#define MODER_IFG (1 << 6) /* interframe gap for incoming frames */ +#define MODER_LOOP (1 << 7) /* loopback */ +#define MODER_NBO (1 << 8) /* no back-off */ +#define MODER_EDE (1 << 9) /* excess defer enable */ +#define MODER_FULLD (1 << 10) /* full duplex */ +#define MODER_RESET (1 << 11) /* FIXME: reset (undocumented) */ +#define MODER_DCRC (1 << 12) /* delayed CRC enable */ +#define MODER_CRC (1 << 13) /* CRC enable */ +#define MODER_HUGE (1 << 14) /* huge packets enable */ +#define MODER_PAD (1 << 15) /* padding enabled */ +#define MODER_RSM (1 << 16) /* receive small packets */ + +/* interrupt source and mask registers */ +#define INT_MASK_TXF (1 << 0) /* transmit frame */ +#define INT_MASK_TXE (1 << 1) /* transmit error */ +#define INT_MASK_RXF (1 << 2) /* receive frame */ +#define INT_MASK_RXE (1 << 3) /* receive error */ +#define INT_MASK_BUSY (1 << 4) +#define INT_MASK_TXC (1 << 5) /* transmit control frame */ +#define INT_MASK_RXC (1 << 6) /* receive control frame */ + +#define INT_MASK_TX (INT_MASK_TXF | INT_MASK_TXE) +#define INT_MASK_RX (INT_MASK_RXF | INT_MASK_RXE) + +#define INT_MASK_ALL ( \ + INT_MASK_TXF | INT_MASK_TXE | \ + INT_MASK_RXF | INT_MASK_RXE | \ + INT_MASK_TXC | INT_MASK_RXC | \ + INT_MASK_BUSY \ + ) + +/* packet length register */ +#define PACKETLEN_MIN(min) (((min) & 0xffff) << 16) +#define PACKETLEN_MAX(max) (((max) & 0xffff) << 0) +#define PACKETLEN_MIN_MAX(min, max) (PACKETLEN_MIN(min) | \ + PACKETLEN_MAX(max)) + +/* transmit buffer number register */ +#define TX_BD_NUM_VAL(x) (((x) <= 0x80) ? (x) : 0x80) + +/* control module mode register */ +#define CTRLMODER_PASSALL (1 << 0) /* pass all receive frames */ +#define CTRLMODER_RXFLOW (1 << 1) /* receive control flow */ +#define CTRLMODER_TXFLOW (1 << 2) /* transmit control flow */ + +/* MII mode register */ +#define MIIMODER_CLKDIV(x) ((x) & 0xfe) /* needs to be an even number */ +#define MIIMODER_NOPRE (1 << 8) /* no preamble */ + +/* MII command register */ +#define MIICOMMAND_SCAN (1 << 0) /* scan status */ +#define MIICOMMAND_READ (1 << 1) /* read status */ +#define MIICOMMAND_WRITE (1 << 2) /* write control data */ + +/* MII address register */ +#define MIIADDRESS_FIAD(x) (((x) & 0x1f) << 0) +#define MIIADDRESS_RGAD(x) (((x) & 0x1f) << 8) +#define MIIADDRESS_ADDR(phy, reg) (MIIADDRESS_FIAD(phy) | \ + MIIADDRESS_RGAD(reg)) + +/* MII transmit data register */ +#define MIITX_DATA_VAL(x) ((x) & 0xffff) + +/* MII receive data register */ +#define MIIRX_DATA_VAL(x) ((x) & 0xffff) + +/* MII status register */ +#define MIISTATUS_LINKFAIL (1 << 0) +#define MIISTATUS_BUSY (1 << 1) +#define MIISTATUS_INVALID (1 << 2) + +/* TX buffer descriptor */ +#define TX_BD_CS (1 << 0) /* carrier sense lost */ +#define TX_BD_DF (1 << 1) /* defer indication */ +#define TX_BD_LC (1 << 2) /* late collision */ +#define TX_BD_RL (1 << 3) /* retransmission limit */ +#define TX_BD_RETRY_MASK (0x00f0) +#define TX_BD_RETRY(x) (((x) & 0x00f0) >> 4) +#define TX_BD_UR (1 << 8) /* transmitter underrun */ +#define TX_BD_CRC (1 << 11) /* TX CRC enable */ +#define TX_BD_PAD (1 << 12) /* pad enable */ +#define TX_BD_WRAP (1 << 13) +#define TX_BD_IRQ (1 << 14) /* interrupt request enable */ +#define TX_BD_READY (1 << 15) /* TX buffer ready */ +#define TX_BD_LEN(x) (((x) & 0xffff) << 16) +#define TX_BD_LEN_MASK (0xffff << 16) + +#define TX_BD_STATS (TX_BD_CS | TX_BD_DF | TX_BD_LC | \ + TX_BD_RL | TX_BD_RETRY_MASK | TX_BD_UR) + +/* RX buffer descriptor */ +#define RX_BD_LC (1 << 0) /* late collision */ +#define RX_BD_CRC (1 << 1) /* RX CRC error */ +#define RX_BD_SF (1 << 2) /* short frame */ +#define RX_BD_TL (1 << 3) /* too long */ +#define RX_BD_DN (1 << 4) /* dribble nibble */ +#define RX_BD_IS (1 << 5) /* invalid symbol */ +#define RX_BD_OR (1 << 6) /* receiver overrun */ +#define RX_BD_MISS (1 << 7) +#define RX_BD_CF (1 << 8) /* control frame */ +#define RX_BD_WRAP (1 << 13) +#define RX_BD_IRQ (1 << 14) /* interrupt request enable */ +#define RX_BD_EMPTY (1 << 15) +#define RX_BD_LEN(x) (((x) & 0xffff) << 16) + +#define RX_BD_STATS (RX_BD_LC | RX_BD_CRC | RX_BD_SF | RX_BD_TL | \ + RX_BD_DN | RX_BD_IS | RX_BD_OR | RX_BD_MISS) + +#define ETHOC_BUFSIZ 1536 +#define ETHOC_ZLEN 64 +#define ETHOC_BD_BASE 0x400 +#define ETHOC_TIMEOUT (HZ / 2) +#define ETHOC_MII_TIMEOUT (1 + (HZ / 5)) + +/** + * struct ethoc - driver-private device structure + * @num_tx: number of send buffers + * @cur_tx: last send buffer written + * @dty_tx: last buffer actually sent + * @num_rx: number of receive buffers + * @cur_rx: current receive buffer + */ +struct ethoc { + u32 num_tx; + u32 cur_tx; + u32 dty_tx; + u32 num_rx; + u32 cur_rx; +}; + +/** + * struct ethoc_bd - buffer descriptor + * @stat: buffer statistics + * @addr: physical memory address + */ +struct ethoc_bd { + u32 stat; + u32 addr; +}; + +static inline u32 ethoc_read(struct eth_device *dev, loff_t offset) +{ + return readl(dev->iobase + offset); +} + +static inline void ethoc_write(struct eth_device *dev, loff_t offset, u32 data) +{ + writel(data, dev->iobase + offset); +} + +static inline void ethoc_read_bd(struct eth_device *dev, int index, + struct ethoc_bd *bd) +{ + loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); + bd->stat = ethoc_read(dev, offset + 0); + bd->addr = ethoc_read(dev, offset + 4); +} + +static inline void ethoc_write_bd(struct eth_device *dev, int index, + const struct ethoc_bd *bd) +{ + loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); + ethoc_write(dev, offset + 0, bd->stat); + ethoc_write(dev, offset + 4, bd->addr); +} + +static int ethoc_set_mac_address(struct eth_device *dev) +{ + u8 *mac = dev->enetaddr; + + ethoc_write(dev, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | + (mac[4] << 8) | (mac[5] << 0)); + ethoc_write(dev, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); + return 0; +} + +static inline void ethoc_ack_irq(struct eth_device *dev, u32 mask) +{ + ethoc_write(dev, INT_SOURCE, mask); +} + +static inline void ethoc_enable_rx_and_tx(struct eth_device *dev) +{ + u32 mode = ethoc_read(dev, MODER); + mode |= MODER_RXEN | MODER_TXEN; + ethoc_write(dev, MODER, mode); +} + +static inline void ethoc_disable_rx_and_tx(struct eth_device *dev) +{ + u32 mode = ethoc_read(dev, MODER); + mode &= ~(MODER_RXEN | MODER_TXEN); + ethoc_write(dev, MODER, mode); +} + +static int ethoc_init_ring(struct eth_device *dev) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + struct ethoc_bd bd; + int i; + + priv->cur_tx = 0; + priv->dty_tx = 0; + priv->cur_rx = 0; + + /* setup transmission buffers */ + bd.stat = TX_BD_IRQ | TX_BD_CRC; + + for (i = 0; i < priv->num_tx; i++) { + if (i == priv->num_tx - 1) + bd.stat |= TX_BD_WRAP; + + ethoc_write_bd(dev, i, &bd); + } + + bd.stat = RX_BD_EMPTY | RX_BD_IRQ; + + for (i = 0; i < priv->num_rx; i++) { + bd.addr = (u32)NetRxPackets[i]; + if (i == priv->num_rx - 1) + bd.stat |= RX_BD_WRAP; + + flush_dcache(bd.addr, PKTSIZE_ALIGN); + ethoc_write_bd(dev, priv->num_tx + i, &bd); + } + + return 0; +} + +static int ethoc_reset(struct eth_device *dev) +{ + u32 mode; + + /* TODO: reset controller? */ + + ethoc_disable_rx_and_tx(dev); + + /* TODO: setup registers */ + + /* enable FCS generation and automatic padding */ + mode = ethoc_read(dev, MODER); + mode |= MODER_CRC | MODER_PAD; + ethoc_write(dev, MODER, mode); + + /* set full-duplex mode */ + mode = ethoc_read(dev, MODER); + mode |= MODER_FULLD; + ethoc_write(dev, MODER, mode); + ethoc_write(dev, IPGT, 0x15); + + ethoc_ack_irq(dev, INT_MASK_ALL); + ethoc_enable_rx_and_tx(dev); + return 0; +} + +static int ethoc_init(struct eth_device *dev, bd_t * bd) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + printf("ethoc\n"); + + priv->num_tx = 1; + priv->num_rx = PKTBUFSRX; + ethoc_write(dev, TX_BD_NUM, priv->num_tx); + ethoc_init_ring(dev); + ethoc_reset(dev); + + return 0; +} + +static int ethoc_update_rx_stats(struct ethoc_bd *bd) +{ + int ret = 0; + + if (bd->stat & RX_BD_TL) { + debug("ETHOC: " "RX: frame too long\n"); + ret++; + } + + if (bd->stat & RX_BD_SF) { + debug("ETHOC: " "RX: frame too short\n"); + ret++; + } + + if (bd->stat & RX_BD_DN) + debug("ETHOC: " "RX: dribble nibble\n"); + + if (bd->stat & RX_BD_CRC) { + debug("ETHOC: " "RX: wrong CRC\n"); + ret++; + } + + if (bd->stat & RX_BD_OR) { + debug("ETHOC: " "RX: overrun\n"); + ret++; + } + + if (bd->stat & RX_BD_LC) { + debug("ETHOC: " "RX: late collision\n"); + ret++; + } + + return ret; +} + +static int ethoc_rx(struct eth_device *dev, int limit) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + int count; + + for (count = 0; count < limit; ++count) { + u32 entry; + struct ethoc_bd bd; + + entry = priv->num_tx + (priv->cur_rx % priv->num_rx); + ethoc_read_bd(dev, entry, &bd); + if (bd.stat & RX_BD_EMPTY) + break; + + debug("%s(): RX buffer %d, %x received\n", + __func__, priv->cur_rx, bd.stat); + if (ethoc_update_rx_stats(&bd) == 0) { + int size = bd.stat >> 16; + size -= 4; /* strip the CRC */ + NetReceive((void *)bd.addr, size); + } + + /* clear the buffer descriptor so it can be reused */ + flush_dcache(bd.addr, PKTSIZE_ALIGN); + bd.stat &= ~RX_BD_STATS; + bd.stat |= RX_BD_EMPTY; + ethoc_write_bd(dev, entry, &bd); + priv->cur_rx++; + } + + return count; +} + +static int ethoc_update_tx_stats(struct ethoc_bd *bd) +{ + if (bd->stat & TX_BD_LC) + debug("ETHOC: " "TX: late collision\n"); + + if (bd->stat & TX_BD_RL) + debug("ETHOC: " "TX: retransmit limit\n"); + + if (bd->stat & TX_BD_UR) + debug("ETHOC: " "TX: underrun\n"); + + if (bd->stat & TX_BD_CS) + debug("ETHOC: " "TX: carrier sense lost\n"); + + return 0; +} + +static void ethoc_tx(struct eth_device *dev) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + u32 entry = priv->dty_tx % priv->num_tx; + struct ethoc_bd bd; + + ethoc_read_bd(dev, entry, &bd); + if ((bd.stat & TX_BD_READY) == 0) + (void)ethoc_update_tx_stats(&bd); +} + +static int ethoc_send(struct eth_device *dev, volatile void *packet, int length) +{ + struct ethoc *priv = (struct ethoc *)dev->priv; + struct ethoc_bd bd; + u32 entry; + u32 pending; + int tmo; + + entry = priv->cur_tx % priv->num_tx; + ethoc_read_bd(dev, entry, &bd); + if (unlikely(length < ETHOC_ZLEN)) + bd.stat |= TX_BD_PAD; + else + bd.stat &= ~TX_BD_PAD; + bd.addr = (u32)packet; + + flush_dcache(bd.addr, length); + bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); + bd.stat |= TX_BD_LEN(length); + ethoc_write_bd(dev, entry, &bd); + + /* start transmit */ + bd.stat |= TX_BD_READY; + ethoc_write_bd(dev, entry, &bd); + + /* wait for transfer to succeed */ + tmo = get_timer(0) + 5 * CONFIG_SYS_HZ; + while (1) { + pending = ethoc_read(dev, INT_SOURCE); + ethoc_ack_irq(dev, pending & ~INT_MASK_RX); + if (pending & INT_MASK_BUSY) + debug("%s(): packet dropped\n", __func__); + + if (pending & INT_MASK_TX) { + ethoc_tx(dev); + break; + } + if (get_timer(0) >= tmo) { + debug("%s(): timed out\n", __func__); + return -1; + } + } + + debug("%s(): packet sent\n", __func__); + return 0; +} + +static void ethoc_halt(struct eth_device *dev) +{ + ethoc_disable_rx_and_tx(dev); +} + +static int ethoc_recv(struct eth_device *dev) +{ + u32 pending; + + pending = ethoc_read(dev, INT_SOURCE); + ethoc_ack_irq(dev, pending); + if (pending & INT_MASK_BUSY) + debug("%s(): packet dropped\n", __func__); + if (pending & INT_MASK_RX) { + debug("%s(): rx irq\n", __func__); + ethoc_rx(dev, PKTBUFSRX); + } + + return 0; +} + +int ethoc_initialize(u8 dev_num, int base_addr) +{ + struct ethoc *priv; + struct eth_device *dev; + + priv = malloc(sizeof(*priv)); + if (!priv) + return 0; + dev = malloc(sizeof(*dev)); + if (!dev) { + free(priv); + return 0; + } + + memset(dev, 0, sizeof(*dev)); + dev->priv = priv; + dev->iobase = base_addr; + dev->init = ethoc_init; + dev->halt = ethoc_halt; + dev->send = ethoc_send; + dev->recv = ethoc_recv; + dev->write_hwaddr = ethoc_set_mac_address; + sprintf(dev->name, "%s-%hu", "ETHOC", dev_num); + + eth_register(dev); + return 1; +} diff --git a/roms/u-boot-sam460ex/drivers/net/fec_mxc.c b/roms/u-boot-sam460ex/drivers/net/fec_mxc.c new file mode 100644 index 000000000..57f89a37a --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/fec_mxc.c @@ -0,0 +1,768 @@ +/* + * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com> + * (C) Copyright 2008,2009 Eric Jarrige <eric.jarrige@armadeus.org> + * (C) Copyright 2008 Armadeus Systems nc + * (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> +#include "fec_mxc.h" + +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/io.h> +#include <asm/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_MII +#error "CONFIG_MII has to be defined!" +#endif + +#undef DEBUG + +struct nbuf { + uint8_t data[1500]; /**< actual data */ + int length; /**< actual length */ + int used; /**< buffer in use or not */ + uint8_t head[16]; /**< MAC header(6 + 6 + 2) + 2(aligned) */ +}; + +struct fec_priv gfec = { + .eth = (struct ethernet_regs *)IMX_FEC_BASE, + .xcv_type = MII100, + .rbd_base = NULL, + .rbd_index = 0, + .tbd_base = NULL, + .tbd_index = 0, + .bd = NULL, + .rdb_ptr = NULL, + .base_ptr = NULL, +}; + +/* + * MII-interface related functions + */ +static int fec_miiphy_read(char *dev, uint8_t phyAddr, uint8_t regAddr, + uint16_t *retVal) +{ + struct eth_device *edev = eth_get_dev_by_name(dev); + struct fec_priv *fec = (struct fec_priv *)edev->priv; + + uint32_t reg; /* convenient holder for the PHY register */ + uint32_t phy; /* convenient holder for the PHY */ + uint32_t start; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + writel(FEC_IEVENT_MII, &fec->eth->ievent); + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | + phy | reg, &fec->eth->mii_data); + + /* + * wait for the related interrupt + */ + start = get_timer_masked(); + while (!(readl(&fec->eth->ievent) & FEC_IEVENT_MII)) { + if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { + printf("Read MDIO failed...\n"); + return -1; + } + } + + /* + * clear mii interrupt bit + */ + writel(FEC_IEVENT_MII, &fec->eth->ievent); + + /* + * it's now safe to read the PHY's register + */ + *retVal = readl(&fec->eth->mii_data); + debug("fec_miiphy_read: phy: %02x reg:%02x val:%#x\n", phyAddr, + regAddr, *retVal); + return 0; +} + +static void fec_mii_setspeed(struct fec_priv *fec) +{ + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + writel((((imx_get_fecclk() / 1000000) + 2) / 5) << 1, + &fec->eth->mii_speed); + debug("fec_init: mii_speed %#lx\n", + fec->eth->mii_speed); +} +static int fec_miiphy_write(char *dev, uint8_t phyAddr, uint8_t regAddr, + uint16_t data) +{ + struct eth_device *edev = eth_get_dev_by_name(dev); + struct fec_priv *fec = (struct fec_priv *)edev->priv; + + uint32_t reg; /* convenient holder for the PHY register */ + uint32_t phy; /* convenient holder for the PHY */ + uint32_t start; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data, &fec->eth->mii_data); + + /* + * wait for the MII interrupt + */ + start = get_timer_masked(); + while (!(readl(&fec->eth->ievent) & FEC_IEVENT_MII)) { + if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { + printf("Write MDIO failed...\n"); + return -1; + } + } + + /* + * clear MII interrupt bit + */ + writel(FEC_IEVENT_MII, &fec->eth->ievent); + debug("fec_miiphy_write: phy: %02x reg:%02x val:%#x\n", phyAddr, + regAddr, data); + + return 0; +} + +static int miiphy_restart_aneg(struct eth_device *dev) +{ + /* + * Wake up from sleep if necessary + * Reset PHY, then delay 300ns + */ +#ifdef CONFIG_MX27 + miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_MIPGSR, 0x00FF); +#endif + miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_BMCR, + PHY_BMCR_RESET); + udelay(1000); + + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_ANAR, + PHY_ANLPAR_TXFD | PHY_ANLPAR_TX | PHY_ANLPAR_10FD | + PHY_ANLPAR_10 | PHY_ANLPAR_PSB_802_3); + miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_BMCR, + PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); + + return 0; +} + +static int miiphy_wait_aneg(struct eth_device *dev) +{ + uint32_t start; + uint16_t status; + + /* + * Wait for AN completion + */ + start = get_timer_masked(); + do { + if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { + printf("%s: Autonegotiation timeout\n", dev->name); + return -1; + } + + if (miiphy_read(dev->name, CONFIG_FEC_MXC_PHYADDR, + PHY_BMSR, &status)) { + printf("%s: Autonegotiation failed. status: 0x%04x\n", + dev->name, status); + return -1; + } + } while (!(status & PHY_BMSR_LS)); + + return 0; +} +static int fec_rx_task_enable(struct fec_priv *fec) +{ + writel(1 << 24, &fec->eth->r_des_active); + return 0; +} + +static int fec_rx_task_disable(struct fec_priv *fec) +{ + return 0; +} + +static int fec_tx_task_enable(struct fec_priv *fec) +{ + writel(1 << 24, &fec->eth->x_des_active); + return 0; +} + +static int fec_tx_task_disable(struct fec_priv *fec) +{ + return 0; +} + +/** + * Initialize receive task's buffer descriptors + * @param[in] fec all we know about the device yet + * @param[in] count receive buffer count to be allocated + * @param[in] size size of each receive buffer + * @return 0 on success + * + * For this task we need additional memory for the data buffers. And each + * data buffer requires some alignment. Thy must be aligned to a specific + * boundary each (DB_DATA_ALIGNMENT). + */ +static int fec_rbd_init(struct fec_priv *fec, int count, int size) +{ + int ix; + uint32_t p = 0; + + /* reserve data memory and consider alignment */ + if (fec->rdb_ptr == NULL) + fec->rdb_ptr = malloc(size * count + DB_DATA_ALIGNMENT); + p = (uint32_t)fec->rdb_ptr; + if (!p) { + puts("fec_mxc: not enough malloc memory\n"); + return -ENOMEM; + } + memset((void *)p, 0, size * count + DB_DATA_ALIGNMENT); + p += DB_DATA_ALIGNMENT-1; + p &= ~(DB_DATA_ALIGNMENT-1); + + for (ix = 0; ix < count; ix++) { + writel(p, &fec->rbd_base[ix].data_pointer); + p += size; + writew(FEC_RBD_EMPTY, &fec->rbd_base[ix].status); + writew(0, &fec->rbd_base[ix].data_length); + } + /* + * mark the last RBD to close the ring + */ + writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &fec->rbd_base[ix - 1].status); + fec->rbd_index = 0; + + return 0; +} + +/** + * Initialize transmit task's buffer descriptors + * @param[in] fec all we know about the device yet + * + * Transmit buffers are created externally. We only have to init the BDs here.\n + * Note: There is a race condition in the hardware. When only one BD is in + * use it must be marked with the WRAP bit to use it for every transmitt. + * This bit in combination with the READY bit results into double transmit + * of each data buffer. It seems the state machine checks READY earlier then + * resetting it after the first transfer. + * Using two BDs solves this issue. + */ +static void fec_tbd_init(struct fec_priv *fec) +{ + writew(0x0000, &fec->tbd_base[0].status); + writew(FEC_TBD_WRAP, &fec->tbd_base[1].status); + fec->tbd_index = 0; +} + +/** + * Mark the given read buffer descriptor as free + * @param[in] last 1 if this is the last buffer descriptor in the chain, else 0 + * @param[in] pRbd buffer descriptor to mark free again + */ +static void fec_rbd_clean(int last, struct fec_bd *pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if (last) + writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &pRbd->status); + else + writew(FEC_RBD_EMPTY, &pRbd->status); + /* + * no data in it + */ + writew(0, &pRbd->data_length); +} + +static int fec_get_hwaddr(struct eth_device *dev, unsigned char *mac) +{ +/* + * The MX27 can store the mac address in internal eeprom + * This mechanism is not supported now by MX51 or MX25 + */ +#if defined(CONFIG_MX51) || defined(CONFIG_MX25) + return -1; +#else + struct iim_regs *iim = (struct iim_regs *)IMX_IIM_BASE; + int i; + + for (i = 0; i < 6; i++) + mac[6-1-i] = readl(&iim->iim_bank_area0[IIM0_MAC + i]); + + return !is_valid_ether_addr(mac); +#endif +} + +static int fec_set_hwaddr(struct eth_device *dev) +{ + uchar *mac = dev->enetaddr; + struct fec_priv *fec = (struct fec_priv *)dev->priv; + + writel(0, &fec->eth->iaddr1); + writel(0, &fec->eth->iaddr2); + writel(0, &fec->eth->gaddr1); + writel(0, &fec->eth->gaddr2); + + /* + * Set physical address + */ + writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], + &fec->eth->paddr1); + writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, &fec->eth->paddr2); + + return 0; +} + +/** + * Start the FEC engine + * @param[in] dev Our device to handle + */ +static int fec_open(struct eth_device *edev) +{ + struct fec_priv *fec = (struct fec_priv *)edev->priv; + + debug("fec_open: fec_open(dev)\n"); + /* full-duplex, heartbeat disabled */ + writel(1 << 2, &fec->eth->x_cntrl); + fec->rbd_index = 0; + + /* + * Enable FEC-Lite controller + */ + writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_ETHER_EN, + &fec->eth->ecntrl); +#ifdef CONFIG_MX25 + udelay(100); + /* + * setup the MII gasket for RMII mode + */ + + /* disable the gasket */ + writew(0, &fec->eth->miigsk_enr); + + /* wait for the gasket to be disabled */ + while (readw(&fec->eth->miigsk_enr) & MIIGSK_ENR_READY) + udelay(2); + + /* configure gasket for RMII, 50 MHz, no loopback, and no echo */ + writew(MIIGSK_CFGR_IF_MODE_RMII, &fec->eth->miigsk_cfgr); + + /* re-enable the gasket */ + writew(MIIGSK_ENR_EN, &fec->eth->miigsk_enr); + + /* wait until MII gasket is ready */ + int max_loops = 10; + while ((readw(&fec->eth->miigsk_enr) & MIIGSK_ENR_READY) == 0) { + if (--max_loops <= 0) { + printf("WAIT for MII Gasket ready timed out\n"); + break; + } + } +#endif + + miiphy_wait_aneg(edev); + miiphy_speed(edev->name, CONFIG_FEC_MXC_PHYADDR); + miiphy_duplex(edev->name, CONFIG_FEC_MXC_PHYADDR); + + /* + * Enable SmartDMA receive task + */ + fec_rx_task_enable(fec); + + udelay(100000); + return 0; +} + +static int fec_init(struct eth_device *dev, bd_t* bd) +{ + uint32_t base; + struct fec_priv *fec = (struct fec_priv *)dev->priv; + + /* + * reserve memory for both buffer descriptor chains at once + * Datasheet forces the startaddress of each chain is 16 byte + * aligned + */ + if (fec->base_ptr == NULL) + fec->base_ptr = malloc((2 + FEC_RBD_NUM) * + sizeof(struct fec_bd) + DB_ALIGNMENT); + base = (uint32_t)fec->base_ptr; + if (!base) { + puts("fec_mxc: not enough malloc memory\n"); + return -ENOMEM; + } + memset((void *)base, 0, (2 + FEC_RBD_NUM) * + sizeof(struct fec_bd) + DB_ALIGNMENT); + base += (DB_ALIGNMENT-1); + base &= ~(DB_ALIGNMENT-1); + + fec->rbd_base = (struct fec_bd *)base; + + base += FEC_RBD_NUM * sizeof(struct fec_bd); + + fec->tbd_base = (struct fec_bd *)base; + + /* + * Set interrupt mask register + */ + writel(0x00000000, &fec->eth->imask); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + writel(0xffffffff, &fec->eth->ievent); + + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + if (fec->xcv_type == SEVENWIRE) { + /* + * Frame length=1518; 7-wire mode + */ + writel(0x05ee0020, &fec->eth->r_cntrl); /* FIXME 0x05ee0000 */ + } else { + /* + * Frame length=1518; MII mode; + */ + writel(0x05ee0024, &fec->eth->r_cntrl); /* FIXME 0x05ee0004 */ + + fec_mii_setspeed(fec); + } + /* + * Set Opcode/Pause Duration Register + */ + writel(0x00010020, &fec->eth->op_pause); /* FIXME 0xffff0020; */ + writel(0x2, &fec->eth->x_wmrk); + /* + * Set multicast address filter + */ + writel(0x00000000, &fec->eth->gaddr1); + writel(0x00000000, &fec->eth->gaddr2); + + + /* clear MIB RAM */ + long *mib_ptr = (long *)(IMX_FEC_BASE + 0x200); + while (mib_ptr <= (long *)(IMX_FEC_BASE + 0x2FC)) + *mib_ptr++ = 0; + + /* FIFO receive start register */ + writel(0x520, &fec->eth->r_fstart); + + /* size and address of each buffer */ + writel(FEC_MAX_PKT_SIZE, &fec->eth->emrbr); + writel((uint32_t)fec->tbd_base, &fec->eth->etdsr); + writel((uint32_t)fec->rbd_base, &fec->eth->erdsr); + + /* + * Initialize RxBD/TxBD rings + */ + if (fec_rbd_init(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE) < 0) { + free(fec->base_ptr); + fec->base_ptr = NULL; + return -ENOMEM; + } + fec_tbd_init(fec); + + + if (fec->xcv_type != SEVENWIRE) + miiphy_restart_aneg(dev); + + fec_open(dev); + return 0; +} + +/** + * Halt the FEC engine + * @param[in] dev Our device to handle + */ +static void fec_halt(struct eth_device *dev) +{ + struct fec_priv *fec = &gfec; + int counter = 0xffff; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + writel(FEC_TCNTRL_GTS | readl(&fec->eth->x_cntrl), + &fec->eth->x_cntrl); + + debug("eth_halt: wait for stop regs\n"); + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(readl(&fec->eth->ievent) & FEC_IEVENT_GRA))) + udelay(1); + + /* + * Disable SmartDMA tasks + */ + fec_tx_task_disable(fec); + fec_rx_task_disable(fec); + + /* + * Disable the Ethernet Controller + * Note: this will also reset the BD index counter! + */ + writel(readl(&fec->eth->ecntrl) & ~FEC_ECNTRL_ETHER_EN, + &fec->eth->ecntrl); + fec->rbd_index = 0; + fec->tbd_index = 0; + debug("eth_halt: done\n"); +} + +/** + * Transmit one frame + * @param[in] dev Our ethernet device to handle + * @param[in] packet Pointer to the data to be transmitted + * @param[in] length Data count in bytes + * @return 0 on success + */ +static int fec_send(struct eth_device *dev, volatile void* packet, int length) +{ + unsigned int status; + + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + struct fec_priv *fec = (struct fec_priv *)dev->priv; + + /* + * Check for valid length of data. + */ + if ((length > 1500) || (length <= 0)) { + printf("Payload (%d) too large\n", length); + return -1; + } + + /* + * Setup the transmit buffer + * Note: We are always using the first buffer for transmission, + * the second will be empty and only used to stop the DMA engine + */ + writew(length, &fec->tbd_base[fec->tbd_index].data_length); + writel((uint32_t)packet, &fec->tbd_base[fec->tbd_index].data_pointer); + /* + * update BD's status now + * This block: + * - is always the last in a chain (means no chain) + * - should transmitt the CRC + * - might be the last BD in the list, so the address counter should + * wrap (-> keep the WRAP flag) + */ + status = readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_WRAP; + status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + writew(status, &fec->tbd_base[fec->tbd_index].status); + + /* + * Enable SmartDMA transmit task + */ + fec_tx_task_enable(fec); + + /* + * wait until frame is sent . + */ + while (readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_READY) { + udelay(1); + } + debug("fec_send: status 0x%x index %d\n", + readw(&fec->tbd_base[fec->tbd_index].status), + fec->tbd_index); + /* for next transmission use the other buffer */ + if (fec->tbd_index) + fec->tbd_index = 0; + else + fec->tbd_index = 1; + + return 0; +} + +/** + * Pull one frame from the card + * @param[in] dev Our ethernet device to handle + * @return Length of packet read + */ +static int fec_recv(struct eth_device *dev) +{ + struct fec_priv *fec = (struct fec_priv *)dev->priv; + struct fec_bd *rbd = &fec->rbd_base[fec->rbd_index]; + unsigned long ievent; + int frame_length, len = 0; + struct nbuf *frame; + uint16_t bd_status; + uchar buff[FEC_MAX_PKT_SIZE]; + + /* + * Check if any critical events have happened + */ + ievent = readl(&fec->eth->ievent); + writel(ievent, &fec->eth->ievent); + debug("fec_recv: ievent 0x%x\n", ievent); + if (ievent & FEC_IEVENT_BABR) { + fec_halt(dev); + fec_init(dev, fec->bd); + printf("some error: 0x%08lx\n", ievent); + return 0; + } + if (ievent & FEC_IEVENT_HBERR) { + /* Heartbeat error */ + writel(0x00000001 | readl(&fec->eth->x_cntrl), + &fec->eth->x_cntrl); + } + if (ievent & FEC_IEVENT_GRA) { + /* Graceful stop complete */ + if (readl(&fec->eth->x_cntrl) & 0x00000001) { + fec_halt(dev); + writel(~0x00000001 & readl(&fec->eth->x_cntrl), + &fec->eth->x_cntrl); + fec_init(dev, fec->bd); + } + } + + /* + * ensure reading the right buffer status + */ + bd_status = readw(&rbd->status); + debug("fec_recv: status 0x%x\n", bd_status); + + if (!(bd_status & FEC_RBD_EMPTY)) { + if ((bd_status & FEC_RBD_LAST) && !(bd_status & FEC_RBD_ERR) && + ((readw(&rbd->data_length) - 4) > 14)) { + /* + * Get buffer address and size + */ + frame = (struct nbuf *)readl(&rbd->data_pointer); + frame_length = readw(&rbd->data_length) - 4; + /* + * Fill the buffer and pass it to upper layers + */ + memcpy(buff, frame->data, frame_length); + NetReceive(buff, frame_length); + len = frame_length; + } else { + if (bd_status & FEC_RBD_ERR) + printf("error frame: 0x%08lx 0x%08x\n", + (ulong)rbd->data_pointer, + bd_status); + } + /* + * free the current buffer, restart the engine + * and move forward to the next buffer + */ + fec_rbd_clean(fec->rbd_index == (FEC_RBD_NUM - 1) ? 1 : 0, rbd); + fec_rx_task_enable(fec); + fec->rbd_index = (fec->rbd_index + 1) % FEC_RBD_NUM; + } + debug("fec_recv: stop\n"); + + return len; +} + +static int fec_probe(bd_t *bd) +{ + struct eth_device *edev; + struct fec_priv *fec = &gfec; + unsigned char ethaddr[6]; + + /* create and fill edev struct */ + edev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!edev) { + puts("fec_mxc: not enough malloc memory\n"); + return -ENOMEM; + } + edev->priv = fec; + edev->init = fec_init; + edev->send = fec_send; + edev->recv = fec_recv; + edev->halt = fec_halt; + edev->write_hwaddr = fec_set_hwaddr; + + fec->eth = (struct ethernet_regs *)IMX_FEC_BASE; + fec->bd = bd; + + fec->xcv_type = MII100; + + /* Reset chip. */ + writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_RESET, &fec->eth->ecntrl); + while (readl(&fec->eth->ecntrl) & 1) + udelay(10); + + /* + * Set interrupt mask register + */ + writel(0x00000000, &fec->eth->imask); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + writel(0xffffffff, &fec->eth->ievent); + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + /* + * Frame length=1518; MII mode; + */ + writel(0x05ee0024, &fec->eth->r_cntrl); /* FIXME 0x05ee0004 */ + fec_mii_setspeed(fec); + + sprintf(edev->name, "FEC_MXC"); + + miiphy_register(edev->name, fec_miiphy_read, fec_miiphy_write); + + eth_register(edev); + + if (fec_get_hwaddr(edev, ethaddr) == 0) { + printf("got MAC address from EEPROM: %pM\n", ethaddr); + memcpy(edev->enetaddr, ethaddr, 6); + } + + return 0; +} + +int fecmxc_initialize(bd_t *bd) +{ + int lout = 1; + + debug("eth_init: fec_probe(bd)\n"); + lout = fec_probe(bd); + + return lout; +} diff --git a/roms/u-boot-sam460ex/drivers/net/fec_mxc.h b/roms/u-boot-sam460ex/drivers/net/fec_mxc.h new file mode 100644 index 000000000..5d0d69d5e --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/fec_mxc.h @@ -0,0 +1,332 @@ +/* + * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com> + * (C) Copyright 2008 Armadeus Systems, nc + * (C) Copyright 2008 Eric Jarrige <eric.jarrige@armadeus.org> + * (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de> + * + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.h + * (C) Copyright Motorola, Inc., 2000 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + + +#ifndef __FEC_MXC_H +#define __FEC_MXC_H + +/** + * Layout description of the FEC + */ +struct ethernet_regs { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + uint32_t res0[1]; /* MBAR_ETH + 0x000 */ + uint32_t ievent; /* MBAR_ETH + 0x004 */ + uint32_t imask; /* MBAR_ETH + 0x008 */ + + uint32_t res1[1]; /* MBAR_ETH + 0x00C */ + uint32_t r_des_active; /* MBAR_ETH + 0x010 */ + uint32_t x_des_active; /* MBAR_ETH + 0x014 */ + uint32_t res2[3]; /* MBAR_ETH + 0x018-20 */ + uint32_t ecntrl; /* MBAR_ETH + 0x024 */ + + uint32_t res3[6]; /* MBAR_ETH + 0x028-03C */ + uint32_t mii_data; /* MBAR_ETH + 0x040 */ + uint32_t mii_speed; /* MBAR_ETH + 0x044 */ + uint32_t res4[7]; /* MBAR_ETH + 0x048-60 */ + uint32_t mib_control; /* MBAR_ETH + 0x064 */ + + uint32_t res5[7]; /* MBAR_ETH + 0x068-80 */ + uint32_t r_cntrl; /* MBAR_ETH + 0x084 */ + uint32_t res6[15]; /* MBAR_ETH + 0x088-C0 */ + uint32_t x_cntrl; /* MBAR_ETH + 0x0C4 */ + uint32_t res7[7]; /* MBAR_ETH + 0x0C8-E0 */ + uint32_t paddr1; /* MBAR_ETH + 0x0E4 */ + uint32_t paddr2; /* MBAR_ETH + 0x0E8 */ + uint32_t op_pause; /* MBAR_ETH + 0x0EC */ + + uint32_t res8[10]; /* MBAR_ETH + 0x0F0-114 */ + uint32_t iaddr1; /* MBAR_ETH + 0x118 */ + uint32_t iaddr2; /* MBAR_ETH + 0x11C */ + uint32_t gaddr1; /* MBAR_ETH + 0x120 */ + uint32_t gaddr2; /* MBAR_ETH + 0x124 */ + uint32_t res9[7]; /* MBAR_ETH + 0x128-140 */ + + uint32_t x_wmrk; /* MBAR_ETH + 0x144 */ + uint32_t res10[1]; /* MBAR_ETH + 0x148 */ + uint32_t r_bound; /* MBAR_ETH + 0x14C */ + uint32_t r_fstart; /* MBAR_ETH + 0x150 */ + uint32_t res11[11]; /* MBAR_ETH + 0x154-17C */ + uint32_t erdsr; /* MBAR_ETH + 0x180 */ + uint32_t etdsr; /* MBAR_ETH + 0x184 */ + uint32_t emrbr; /* MBAR_ETH + 0x188 */ + uint32_t res12[29]; /* MBAR_ETH + 0x18C-1FC */ + +/* MIB COUNTERS (Offset 200-2FF) */ + + uint32_t rmon_t_drop; /* MBAR_ETH + 0x200 */ + uint32_t rmon_t_packets; /* MBAR_ETH + 0x204 */ + uint32_t rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + uint32_t rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + uint32_t rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + uint32_t rmon_t_undersize; /* MBAR_ETH + 0x214 */ + uint32_t rmon_t_oversize; /* MBAR_ETH + 0x218 */ + uint32_t rmon_t_frag; /* MBAR_ETH + 0x21C */ + uint32_t rmon_t_jab; /* MBAR_ETH + 0x220 */ + uint32_t rmon_t_col; /* MBAR_ETH + 0x224 */ + uint32_t rmon_t_p64; /* MBAR_ETH + 0x228 */ + uint32_t rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + uint32_t rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + uint32_t rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + uint32_t rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + uint32_t rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ + uint32_t rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + uint32_t rmon_t_octets; /* MBAR_ETH + 0x244 */ + uint32_t ieee_t_drop; /* MBAR_ETH + 0x248 */ + uint32_t ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + uint32_t ieee_t_1col; /* MBAR_ETH + 0x250 */ + uint32_t ieee_t_mcol; /* MBAR_ETH + 0x254 */ + uint32_t ieee_t_def; /* MBAR_ETH + 0x258 */ + uint32_t ieee_t_lcol; /* MBAR_ETH + 0x25C */ + uint32_t ieee_t_excol; /* MBAR_ETH + 0x260 */ + uint32_t ieee_t_macerr; /* MBAR_ETH + 0x264 */ + uint32_t ieee_t_cserr; /* MBAR_ETH + 0x268 */ + uint32_t ieee_t_sqe; /* MBAR_ETH + 0x26C */ + uint32_t t_fdxfc; /* MBAR_ETH + 0x270 */ + uint32_t ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + uint32_t res13[2]; /* MBAR_ETH + 0x278-27C */ + uint32_t rmon_r_drop; /* MBAR_ETH + 0x280 */ + uint32_t rmon_r_packets; /* MBAR_ETH + 0x284 */ + uint32_t rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + uint32_t rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + uint32_t rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + uint32_t rmon_r_undersize; /* MBAR_ETH + 0x294 */ + uint32_t rmon_r_oversize; /* MBAR_ETH + 0x298 */ + uint32_t rmon_r_frag; /* MBAR_ETH + 0x29C */ + uint32_t rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + uint32_t rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + uint32_t rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + uint32_t rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + uint32_t rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + uint32_t rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + uint32_t rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + uint32_t rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ + uint32_t rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + uint32_t rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + uint32_t ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + uint32_t ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + uint32_t ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + uint32_t ieee_r_align; /* MBAR_ETH + 0x2D4 */ + uint32_t r_macerr; /* MBAR_ETH + 0x2D8 */ + uint32_t r_fdxfc; /* MBAR_ETH + 0x2DC */ + uint32_t ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + uint32_t res14[7]; /* MBAR_ETH + 0x2E4-2FC */ + +#ifdef CONFIG_MX25 + uint16_t miigsk_cfgr; /* MBAR_ETH + 0x300 */ + uint16_t res15[3]; /* MBAR_ETH + 0x302-306 */ + uint16_t miigsk_enr; /* MBAR_ETH + 0x308 */ + uint16_t res16[3]; /* MBAR_ETH + 0x30a-30e */ + uint32_t res17[60]; /* MBAR_ETH + 0x300-3FF */ +#else + uint32_t res15[64]; /* MBAR_ETH + 0x300-3FF */ +#endif +}; + +#define FEC_IEVENT_HBERR 0x80000000 +#define FEC_IEVENT_BABR 0x40000000 +#define FEC_IEVENT_BABT 0x20000000 +#define FEC_IEVENT_GRA 0x10000000 +#define FEC_IEVENT_TXF 0x08000000 +#define FEC_IEVENT_TXB 0x04000000 +#define FEC_IEVENT_RXF 0x02000000 +#define FEC_IEVENT_RXB 0x01000000 +#define FEC_IEVENT_MII 0x00800000 +#define FEC_IEVENT_EBERR 0x00400000 +#define FEC_IEVENT_LC 0x00200000 +#define FEC_IEVENT_RL 0x00100000 +#define FEC_IEVENT_UN 0x00080000 + +#define FEC_IMASK_HBERR 0x80000000 +#define FEC_IMASK_BABR 0x40000000 +#define FEC_IMASKT_BABT 0x20000000 +#define FEC_IMASK_GRA 0x10000000 +#define FEC_IMASKT_TXF 0x08000000 +#define FEC_IMASK_TXB 0x04000000 +#define FEC_IMASKT_RXF 0x02000000 +#define FEC_IMASK_RXB 0x01000000 +#define FEC_IMASK_MII 0x00800000 +#define FEC_IMASK_EBERR 0x00400000 +#define FEC_IMASK_LC 0x00200000 +#define FEC_IMASKT_RL 0x00100000 +#define FEC_IMASK_UN 0x00080000 + + +#define FEC_RCNTRL_MAX_FL_SHIFT 16 +#define FEC_RCNTRL_LOOP 0x00000001 +#define FEC_RCNTRL_DRT 0x00000002 +#define FEC_RCNTRL_MII_MODE 0x00000004 +#define FEC_RCNTRL_PROM 0x00000008 +#define FEC_RCNTRL_BC_REJ 0x00000010 +#define FEC_RCNTRL_FCE 0x00000020 + +#define FEC_TCNTRL_GTS 0x00000001 +#define FEC_TCNTRL_HBC 0x00000002 +#define FEC_TCNTRL_FDEN 0x00000004 +#define FEC_TCNTRL_TFC_PAUSE 0x00000008 +#define FEC_TCNTRL_RFC_PAUSE 0x00000010 + +#define FEC_ECNTRL_RESET 0x00000001 /* reset the FEC */ +#define FEC_ECNTRL_ETHER_EN 0x00000002 /* enable the FEC */ + +#ifdef CONFIG_MX25 +/* defines for MIIGSK */ +/* RMII frequency control: 0=50MHz, 1=5MHz */ +#define MIIGSK_CFGR_FRCONT (1 << 6) +/* loopback mode */ +#define MIIGSK_CFGR_LBMODE (1 << 4) +/* echo mode */ +#define MIIGSK_CFGR_EMODE (1 << 3) +/* MII gasket mode field */ +#define MIIGSK_CFGR_IF_MODE_MASK (3 << 0) +/* MMI/7-Wire mode */ +#define MIIGSK_CFGR_IF_MODE_MII (0 << 0) +/* RMII mode */ +#define MIIGSK_CFGR_IF_MODE_RMII (1 << 0) +/* reflects MIIGSK Enable bit (RO) */ +#define MIIGSK_ENR_READY (1 << 2) +/* enable MIGSK (set by default) */ +#define MIIGSK_ENR_EN (1 << 1) +#endif + +/** + * @brief Descriptor buffer alignment + * + * i.MX27 requires a 16 byte alignment (but for the first element only) + */ +#define DB_ALIGNMENT 16 + +/** + * @brief Data buffer alignment + * + * i.MX27 requires a four byte alignment for transmit and 16 bits + * alignment for receive so take 16 + * Note: Valid for member data_pointer in struct buffer_descriptor + */ +#define DB_DATA_ALIGNMENT 16 + +/** + * @brief Receive & Transmit Buffer Descriptor definitions + * + * Note: The first BD must be aligned (see DB_ALIGNMENT) + */ +struct fec_bd { + uint16_t data_length; /* payload's length in bytes */ + uint16_t status; /* BD's staus (see datasheet) */ + uint32_t data_pointer; /* payload's buffer address */ +}; + +/** + * Supported phy types on this platform + */ +enum xceiver_type { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +}; + +/** + * @brief i.MX27-FEC private structure + */ +struct fec_priv { + struct ethernet_regs *eth; /* pointer to register'S base */ + enum xceiver_type xcv_type; /* transceiver type */ + struct fec_bd *rbd_base; /* RBD ring */ + int rbd_index; /* next receive BD to read */ + struct fec_bd *tbd_base; /* TBD ring */ + int tbd_index; /* next transmit BD to write */ + bd_t *bd; + void *rdb_ptr; + void *base_ptr; +}; + +/** + * @brief Numbers of buffer descriptors for receiving + * + * The number defines the stocked memory buffers for the receiving task. + * Larger values makes no sense in this limited environment. + */ +#define FEC_RBD_NUM 64 + +/** + * @brief Define the ethernet packet size limit in memory + * + * Note: Do not shrink this number. This will force the FEC to spread larger + * frames in more than one BD. This is nothing to worry about, but the current + * driver can't handle it. + */ +#define FEC_MAX_PKT_SIZE 1536 + +/* Receive BD status bits */ +#define FEC_RBD_EMPTY 0x8000 /* Receive BD status: Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Receive BD status: Last BD in ring */ +/* Receive BD status: Buffer is last in frame (useless here!) */ +#define FEC_RBD_LAST 0x0800 +#define FEC_RBD_MISS 0x0100 /* Receive BD status: Miss bit for prom mode */ +/* Receive BD status: The received frame is broadcast frame */ +#define FEC_RBD_BC 0x0080 +/* Receive BD status: The received frame is multicast frame */ +#define FEC_RBD_MC 0x0040 +#define FEC_RBD_LG 0x0020 /* Receive BD status: Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Receive BD status: Nonoctet align frame */ +#define FEC_RBD_CR 0x0004 /* Receive BD status: CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive BD status: Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Receive BD status: Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* Transmit BD status bits */ +#define FEC_TBD_READY 0x8000 /* Tansmit BD status: Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Tansmit BD status: Mark as last BD in ring */ +#define FEC_TBD_LAST 0x0800 /* Tansmit BD status: Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Tansmit BD status: Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Tansmit BD status: Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __FEC_MXC_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/fsl_mcdmafec.c b/roms/u-boot-sam460ex/drivers/net/fsl_mcdmafec.c new file mode 100644 index 000000000..35a6dfbe9 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/fsl_mcdmafec.c @@ -0,0 +1,588 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <command.h> +#include <config.h> +#include <net.h> +#include <miiphy.h> + +#undef ET_DEBUG +#undef MII_DEBUG + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1536 +#define LAST_PKTBUFSRX PKTBUFSRX - 1 +#define BD_ENET_RX_W_E (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY) +#define BD_ENET_TX_RDY_LST (BD_ENET_TX_READY | BD_ENET_TX_LAST) +#define FIFO_ERRSTAT (FIFO_STAT_RXW | FIFO_STAT_UF | FIFO_STAT_OF) + +/* RxBD bits definitions */ +#define BD_ENET_RX_ERR (BD_ENET_RX_LG | BD_ENET_RX_NO | BD_ENET_RX_CR | \ + BD_ENET_RX_OV | BD_ENET_RX_TR) + +#include <asm/immap.h> +#include <asm/fsl_mcdmafec.h> + +#include "MCD_dma.h" + +DECLARE_GLOBAL_DATA_PTR; + +struct fec_info_dma fec_info[] = { +#ifdef CONFIG_SYS_FEC0_IOBASE + { + 0, /* index */ + CONFIG_SYS_FEC0_IOBASE, /* io base */ + CONFIG_SYS_FEC0_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC0_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phyname init */ + 0, /* RX BD */ + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_dma *)-1, /* next */ + FEC0_RX_TASK, /* rxTask */ + FEC0_TX_TASK, /* txTask */ + FEC0_RX_PRIORITY, /* rxPri */ + FEC0_TX_PRIORITY, /* txPri */ + FEC0_RX_INIT, /* rxInit */ + FEC0_TX_INIT, /* txInit */ + 0, /* usedTbdIndex */ + 0, /* cleanTbdNum */ + }, +#endif +#ifdef CONFIG_SYS_FEC1_IOBASE + { + 1, /* index */ + CONFIG_SYS_FEC1_IOBASE, /* io base */ + CONFIG_SYS_FEC1_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC1_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phy name init */ +#ifdef CONFIG_SYS_DMA_USE_INTSRAM + (cbd_t *)DBUF_LENGTH, /* RX BD */ +#else + 0, /* RX BD */ +#endif + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_dma *)-1, /* next */ + FEC1_RX_TASK, /* rxTask */ + FEC1_TX_TASK, /* txTask */ + FEC1_RX_PRIORITY, /* rxPri */ + FEC1_TX_PRIORITY, /* txPri */ + FEC1_RX_INIT, /* rxInit */ + FEC1_TX_INIT, /* txInit */ + 0, /* usedTbdIndex */ + 0, /* cleanTbdNum */ + } +#endif +}; + +static int fec_send(struct eth_device *dev, volatile void *packet, int length); +static int fec_recv(struct eth_device *dev); +static int fec_init(struct eth_device *dev, bd_t * bd); +static void fec_halt(struct eth_device *dev); + +#ifdef ET_DEBUG +static void dbg_fec_regs(struct eth_device *dev) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + + printf("=====\n"); + printf("ievent %x - %x\n", (int)&fecp->eir, fecp->eir); + printf("imask %x - %x\n", (int)&fecp->eimr, fecp->eimr); + printf("ecntrl %x - %x\n", (int)&fecp->ecr, fecp->ecr); + printf("mii_mframe %x - %x\n", (int)&fecp->mmfr, fecp->mmfr); + printf("mii_speed %x - %x\n", (int)&fecp->mscr, fecp->mscr); + printf("mii_ctrlstat %x - %x\n", (int)&fecp->mibc, fecp->mibc); + printf("r_cntrl %x - %x\n", (int)&fecp->rcr, fecp->rcr); + printf("r hash %x - %x\n", (int)&fecp->rhr, fecp->rhr); + printf("x_cntrl %x - %x\n", (int)&fecp->tcr, fecp->tcr); + printf("padr_l %x - %x\n", (int)&fecp->palr, fecp->palr); + printf("padr_u %x - %x\n", (int)&fecp->paur, fecp->paur); + printf("op_pause %x - %x\n", (int)&fecp->opd, fecp->opd); + printf("iadr_u %x - %x\n", (int)&fecp->iaur, fecp->iaur); + printf("iadr_l %x - %x\n", (int)&fecp->ialr, fecp->ialr); + printf("gadr_u %x - %x\n", (int)&fecp->gaur, fecp->gaur); + printf("gadr_l %x - %x\n", (int)&fecp->galr, fecp->galr); + printf("x_wmrk %x - %x\n", (int)&fecp->tfwr, fecp->tfwr); + printf("r_fdata %x - %x\n", (int)&fecp->rfdr, fecp->rfdr); + printf("r_fstat %x - %x\n", (int)&fecp->rfsr, fecp->rfsr); + printf("r_fctrl %x - %x\n", (int)&fecp->rfcr, fecp->rfcr); + printf("r_flrfp %x - %x\n", (int)&fecp->rlrfp, fecp->rlrfp); + printf("r_flwfp %x - %x\n", (int)&fecp->rlwfp, fecp->rlwfp); + printf("r_frfar %x - %x\n", (int)&fecp->rfar, fecp->rfar); + printf("r_frfrp %x - %x\n", (int)&fecp->rfrp, fecp->rfrp); + printf("r_frfwp %x - %x\n", (int)&fecp->rfwp, fecp->rfwp); + printf("t_fdata %x - %x\n", (int)&fecp->tfdr, fecp->tfdr); + printf("t_fstat %x - %x\n", (int)&fecp->tfsr, fecp->tfsr); + printf("t_fctrl %x - %x\n", (int)&fecp->tfcr, fecp->tfcr); + printf("t_flrfp %x - %x\n", (int)&fecp->tlrfp, fecp->tlrfp); + printf("t_flwfp %x - %x\n", (int)&fecp->tlwfp, fecp->tlwfp); + printf("t_ftfar %x - %x\n", (int)&fecp->tfar, fecp->tfar); + printf("t_ftfrp %x - %x\n", (int)&fecp->tfrp, fecp->tfrp); + printf("t_ftfwp %x - %x\n", (int)&fecp->tfwp, fecp->tfwp); + printf("frst %x - %x\n", (int)&fecp->frst, fecp->frst); + printf("ctcwr %x - %x\n", (int)&fecp->ctcwr, fecp->ctcwr); +} +#endif + +static void set_fec_duplex_speed(volatile fecdma_t * fecp, bd_t * bd, + int dup_spd) +{ + if ((dup_spd >> 16) == FULL) { + /* Set maximum frame length */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE | + FEC_RCR_PROM | 0x100; + fecp->tcr = FEC_TCR_FDEN; + } else { + /* Half duplex mode */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | + FEC_RCR_MII_MODE | FEC_RCR_DRT; + fecp->tcr &= ~FEC_TCR_FDEN; + } + + if ((dup_spd & 0xFFFF) == _100BASET) { +#ifdef MII_DEBUG + printf("100Mbps\n"); +#endif + bd->bi_ethspeed = 100; + } else { +#ifdef MII_DEBUG + printf("10Mbps\n"); +#endif + bd->bi_ethspeed = 10; + } +} + +static int fec_send(struct eth_device *dev, volatile void *packet, int length) +{ + struct fec_info_dma *info = dev->priv; + cbd_t *pTbd, *pUsedTbd; + u16 phyStatus; + + miiphy_read(dev->name, info->phy_addr, PHY_BMSR, &phyStatus); + + /* process all the consumed TBDs */ + while (info->cleanTbdNum < CONFIG_SYS_TX_ETH_BUFFER) { + pUsedTbd = &info->txbd[info->usedTbdIdx]; + if (pUsedTbd->cbd_sc & BD_ENET_TX_READY) { +#ifdef ET_DEBUG + printf("Cannot clean TBD %d, in use\n", + info->cleanTbdNum); +#endif + return 0; + } + + /* clean this buffer descriptor */ + if (info->usedTbdIdx == (CONFIG_SYS_TX_ETH_BUFFER - 1)) + pUsedTbd->cbd_sc = BD_ENET_TX_WRAP; + else + pUsedTbd->cbd_sc = 0; + + /* update some indeces for a correct handling of the TBD ring */ + info->cleanTbdNum++; + info->usedTbdIdx = (info->usedTbdIdx + 1) % CONFIG_SYS_TX_ETH_BUFFER; + } + + /* Check for valid length of data. */ + if ((length > 1500) || (length <= 0)) { + return -1; + } + + /* Check the number of vacant TxBDs. */ + if (info->cleanTbdNum < 1) { + printf("No available TxBDs ...\n"); + return -1; + } + + /* Get the first TxBD to send the mac header */ + pTbd = &info->txbd[info->txIdx]; + pTbd->cbd_datlen = length; + pTbd->cbd_bufaddr = (u32) packet; + pTbd->cbd_sc |= BD_ENET_TX_LAST | BD_ENET_TX_TC | BD_ENET_TX_READY; + info->txIdx = (info->txIdx + 1) % CONFIG_SYS_TX_ETH_BUFFER; + + /* Enable DMA transmit task */ + MCD_continDma(info->txTask); + + info->cleanTbdNum -= 1; + + /* wait until frame is sent . */ + while (pTbd->cbd_sc & BD_ENET_TX_READY) { + udelay(10); + } + + return (int)(info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_STATS); +} + +static int fec_recv(struct eth_device *dev) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + + cbd_t *pRbd = &info->rxbd[info->rxIdx]; + u32 ievent; + int frame_length, len = 0; + + /* Check if any critical events have happened */ + ievent = fecp->eir; + if (ievent != 0) { + fecp->eir = ievent; + + if (ievent & (FEC_EIR_BABT | FEC_EIR_TXERR | FEC_EIR_RXERR)) { + printf("fec_recv: error\n"); + fec_halt(dev); + fec_init(dev, NULL); + return 0; + } + + if (ievent & FEC_EIR_HBERR) { + /* Heartbeat error */ + fecp->tcr |= FEC_TCR_GTS; + } + + if (ievent & FEC_EIR_GRA) { + /* Graceful stop complete */ + if (fecp->tcr & FEC_TCR_GTS) { + printf("fec_recv: tcr_gts\n"); + fec_halt(dev); + fecp->tcr &= ~FEC_TCR_GTS; + fec_init(dev, NULL); + } + } + } + + if (!(pRbd->cbd_sc & BD_ENET_RX_EMPTY)) { + if ((pRbd->cbd_sc & BD_ENET_RX_LAST) + && !(pRbd->cbd_sc & BD_ENET_RX_ERR) + && ((pRbd->cbd_datlen - 4) > 14)) { + + /* Get buffer address and size */ + frame_length = pRbd->cbd_datlen - 4; + + /* Fill the buffer and pass it to upper layers */ + NetReceive((volatile uchar *)pRbd->cbd_bufaddr, + frame_length); + len = frame_length; + } + + /* Reset buffer descriptor as empty */ + if ((info->rxIdx) == (PKTBUFSRX - 1)) + pRbd->cbd_sc = (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY); + else + pRbd->cbd_sc = BD_ENET_RX_EMPTY; + + pRbd->cbd_datlen = PKTSIZE_ALIGN; + + /* Now, we have an empty RxBD, restart the DMA receive task */ + MCD_continDma(info->rxTask); + + /* Increment BD count */ + info->rxIdx = (info->rxIdx + 1) % PKTBUFSRX; + } + + return len; +} + +static void fec_set_hwaddr(volatile fecdma_t * fecp, u8 * mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* Set individual hash table register */ + if (crc >= 32) { + fecp->ialr = (1 << (crc - 32)); + fecp->iaur = 0; + } else { + fecp->ialr = 0; + fecp->iaur = (1 << crc); + } + + /* Set physical address */ + fecp->palr = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fecp->paur = (mac[4] << 24) + (mac[5] << 16) + 0x8808; + + /* Clear multicast address hash table */ + fecp->gaur = 0; + fecp->galr = 0; +} + +static int fec_init(struct eth_device *dev, bd_t * bd) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + int i; + uchar enetaddr[6]; + +#ifdef ET_DEBUG + printf("fec_init: iobase 0x%08x ...\n", info->iobase); +#endif + + fecpin_setclear(dev, 1); + + fec_halt(dev); + +#if defined(CONFIG_CMD_MII) || defined (CONFIG_MII) || \ + defined (CONFIG_SYS_DISCOVER_PHY) + + mii_init(); + + set_fec_duplex_speed(fecp, bd, info->dup_spd); +#else +#ifndef CONFIG_SYS_DISCOVER_PHY + set_fec_duplex_speed(fecp, bd, (FECDUPLEX << 16) | FECSPEED); +#endif /* ifndef CONFIG_SYS_DISCOVER_PHY */ +#endif /* CONFIG_CMD_MII || CONFIG_MII */ + + /* We use strictly polling mode only */ + fecp->eimr = 0; + + /* Clear any pending interrupt */ + fecp->eir = 0xffffffff; + + /* Set station address */ + if ((u32) fecp == CONFIG_SYS_FEC0_IOBASE) + eth_getenv_enetaddr("ethaddr", enetaddr); + else + eth_getenv_enetaddr("eth1addr", enetaddr); + fec_set_hwaddr(fecp, enetaddr); + + /* Set Opcode/Pause Duration Register */ + fecp->opd = 0x00010020; + + /* Setup Buffers and Buffer Desriptors */ + info->rxIdx = 0; + info->txIdx = 0; + + /* Setup Receiver Buffer Descriptors (13.14.24.18) + * Settings: Empty, Wrap */ + for (i = 0; i < PKTBUFSRX; i++) { + info->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + info->rxbd[i].cbd_datlen = PKTSIZE_ALIGN; + info->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; + } + info->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) + * Settings: Last, Tx CRC */ + for (i = 0; i < CONFIG_SYS_TX_ETH_BUFFER; i++) { + info->txbd[i].cbd_sc = 0; + info->txbd[i].cbd_datlen = 0; + info->txbd[i].cbd_bufaddr = (uint) (&info->txbuf[0]); + } + info->txbd[CONFIG_SYS_TX_ETH_BUFFER - 1].cbd_sc |= BD_ENET_TX_WRAP; + + info->usedTbdIdx = 0; + info->cleanTbdNum = CONFIG_SYS_TX_ETH_BUFFER; + + /* Set Rx FIFO alarm and granularity value */ + fecp->rfcr = 0x0c000000; + fecp->rfar = 0x0000030c; + + /* Set Tx FIFO granularity value */ + fecp->tfcr = FIFO_CTRL_FRAME | FIFO_CTRL_GR(6) | 0x00040000; + fecp->tfar = 0x00000080; + + fecp->tfwr = 0x2; + fecp->ctcwr = 0x03000000; + + /* Enable DMA receive task */ + MCD_startDma(info->rxTask, /* Dma channel */ + (s8 *) info->rxbd, /*Source Address */ + 0, /* Source increment */ + (s8 *) (&fecp->rfdr), /* dest */ + 4, /* dest increment */ + 0, /* DMA size */ + 4, /* xfer size */ + info->rxInit, /* initiator */ + info->rxPri, /* priority */ + (MCD_FECRX_DMA | MCD_TT_FLAGS_DEF), /* Flags */ + (MCD_NO_CSUM | MCD_NO_BYTE_SWAP) /* Function description */ + ); + + /* Enable DMA tx task with no ready buffer descriptors */ + MCD_startDma(info->txTask, /* Dma channel */ + (s8 *) info->txbd, /*Source Address */ + 0, /* Source increment */ + (s8 *) (&fecp->tfdr), /* dest */ + 4, /* dest incr */ + 0, /* DMA size */ + 4, /* xfer size */ + info->txInit, /* initiator */ + info->txPri, /* priority */ + (MCD_FECTX_DMA | MCD_TT_FLAGS_DEF), /* Flags */ + (MCD_NO_CSUM | MCD_NO_BYTE_SWAP) /* Function description */ + ); + + /* Now enable the transmit and receive processing */ + fecp->ecr |= FEC_ECR_ETHER_EN; + + return 1; +} + +static void fec_halt(struct eth_device *dev) +{ + struct fec_info_dma *info = dev->priv; + volatile fecdma_t *fecp = (fecdma_t *) (info->iobase); + int counter = 0xffff; + + /* issue graceful stop command to the FEC transmitter if necessary */ + fecp->tcr |= FEC_TCR_GTS; + + /* wait for graceful stop to register */ + while ((counter--) && (!(fecp->eir & FEC_EIR_GRA))) ; + + /* Disable DMA tasks */ + MCD_killDma(info->txTask); + MCD_killDma(info->rxTask);; + + /* Disable the Ethernet Controller */ + fecp->ecr &= ~FEC_ECR_ETHER_EN; + + /* Clear FIFO status registers */ + fecp->rfsr &= FIFO_ERRSTAT; + fecp->tfsr &= FIFO_ERRSTAT; + + fecp->frst = 0x01000000; + + /* Issue a reset command to the FEC chip */ + fecp->ecr |= FEC_ECR_RESET; + + /* wait at least 20 clock cycles */ + udelay(10000); + +#ifdef ET_DEBUG + printf("Ethernet task stopped\n"); +#endif +} + +int mcdmafec_initialize(bd_t * bis) +{ + struct eth_device *dev; + int i; +#ifdef CONFIG_SYS_DMA_USE_INTSRAM + u32 tmp = CONFIG_SYS_INTSRAM + 0x2000; +#endif + + for (i = 0; i < sizeof(fec_info) / sizeof(fec_info[0]); i++) { + + dev = + (struct eth_device *)memalign(CONFIG_SYS_CACHELINE_SIZE, + sizeof *dev); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "FEC%d", fec_info[i].index); + + dev->priv = &fec_info[i]; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + /* setup Receive and Transmit buffer descriptor */ +#ifdef CONFIG_SYS_DMA_USE_INTSRAM + fec_info[i].rxbd = (cbd_t *)((u32)fec_info[i].rxbd + tmp); + tmp = (u32)fec_info[i].rxbd; + fec_info[i].txbd = + (cbd_t *)((u32)fec_info[i].txbd + tmp + + (PKTBUFSRX * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbd; + fec_info[i].txbuf = + (char *)((u32)fec_info[i].txbuf + tmp + + (CONFIG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbuf; +#else + fec_info[i].rxbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (PKTBUFSRX * sizeof(cbd_t))); + fec_info[i].txbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (CONFIG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); + fec_info[i].txbuf = + (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, DBUF_LENGTH); +#endif + +#ifdef ET_DEBUG + printf("rxbd %x txbd %x\n", + (int)fec_info[i].rxbd, (int)fec_info[i].txbd); +#endif + + fec_info[i].phy_name = (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, 32); + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, + mcffec_miiphy_read, mcffec_miiphy_write); +#endif + + if (i > 0) + fec_info[i - 1].next = &fec_info[i]; + } + fec_info[i - 1].next = &fec_info[0]; + + /* default speed */ + bis->bi_ethspeed = 10; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/ftmac100.c b/roms/u-boot-sam460ex/drivers/net/ftmac100.c new file mode 100644 index 000000000..2328cb51d --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ftmac100.c @@ -0,0 +1,278 @@ +/* + * Faraday FTMAC100 Ethernet + * + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang <ratbert@faraday-tech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <asm/io.h> + +#include "ftmac100.h" + +#define ETH_ZLEN 60 + +struct ftmac100_data { + volatile struct ftmac100_txdes txdes[1]; + volatile struct ftmac100_rxdes rxdes[PKTBUFSRX]; + int rx_index; +}; + +/* + * Reset MAC + */ +static void ftmac100_reset (struct eth_device *dev) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + + debug ("%s()\n", __func__); + + writel (FTMAC100_MACCR_SW_RST, &ftmac100->maccr); + + while (readl (&ftmac100->maccr) & FTMAC100_MACCR_SW_RST) + ; +} + +/* + * Set MAC address + */ +static void ftmac100_set_mac (struct eth_device *dev, const unsigned char *mac) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + unsigned int maddr = mac[0] << 8 | mac[1]; + unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; + + debug ("%s(%x %x)\n", __func__, maddr, laddr); + + writel (maddr, &ftmac100->mac_madr); + writel (laddr, &ftmac100->mac_ladr); +} + +static void ftmac100_set_mac_from_env (struct eth_device *dev) +{ + eth_getenv_enetaddr ("ethaddr", dev->enetaddr); + + ftmac100_set_mac (dev, dev->enetaddr); +} + +/* + * disable transmitter, receiver + */ +static void ftmac100_halt (struct eth_device *dev) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + + debug ("%s()\n", __func__); + + writel (0, &ftmac100->maccr); +} + +static int ftmac100_init (struct eth_device *dev, bd_t *bd) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + struct ftmac100_data *priv = dev->priv; + volatile struct ftmac100_txdes *txdes = priv->txdes; + volatile struct ftmac100_rxdes *rxdes = priv->rxdes; + unsigned int maccr; + int i; + + debug ("%s()\n", __func__); + + ftmac100_reset (dev); + + /* set the ethernet address */ + + ftmac100_set_mac_from_env (dev); + + /* disable all interrupts */ + + writel (0, &ftmac100->imr); + + /* initialize descriptors */ + + priv->rx_index = 0; + + txdes[0].txdes1 = FTMAC100_TXDES1_EDOTR; + rxdes[PKTBUFSRX - 1].rxdes1 = FTMAC100_RXDES1_EDORR; + + for (i = 0; i < PKTBUFSRX; i++) { + /* RXBUF_BADR */ + rxdes[i].rxdes2 = (unsigned int)NetRxPackets[i]; + rxdes[i].rxdes1 |= FTMAC100_RXDES1_RXBUF_SIZE (PKTSIZE_ALIGN); + rxdes[i].rxdes0 = FTMAC100_RXDES0_RXDMA_OWN; + } + + /* transmit ring */ + + writel ((unsigned int)txdes, &ftmac100->txr_badr); + + /* receive ring */ + + writel ((unsigned int)rxdes, &ftmac100->rxr_badr); + + /* poll receive descriptor automatically */ + + writel (FTMAC100_APTC_RXPOLL_CNT (1), &ftmac100->aptc); + + /* enable transmitter, receiver */ + + maccr = FTMAC100_MACCR_XMT_EN | + FTMAC100_MACCR_RCV_EN | + FTMAC100_MACCR_XDMA_EN | + FTMAC100_MACCR_RDMA_EN | + FTMAC100_MACCR_CRC_APD | + FTMAC100_MACCR_ENRX_IN_HALFTX | + FTMAC100_MACCR_RX_RUNT | + FTMAC100_MACCR_RX_BROADPKT; + + writel (maccr, &ftmac100->maccr); + + return 0; +} + +/* + * Get a data block via Ethernet + */ +static int ftmac100_recv (struct eth_device *dev) +{ + struct ftmac100_data *priv = dev->priv; + volatile struct ftmac100_rxdes *curr_des; + unsigned short rxlen; + + curr_des = &priv->rxdes[priv->rx_index]; + + if (curr_des->rxdes0 & FTMAC100_RXDES0_RXDMA_OWN) + return -1; + + if (curr_des->rxdes0 & (FTMAC100_RXDES0_RX_ERR | + FTMAC100_RXDES0_CRC_ERR | + FTMAC100_RXDES0_FTL | + FTMAC100_RXDES0_RUNT | + FTMAC100_RXDES0_RX_ODD_NB)) { + return -1; + } + + rxlen = FTMAC100_RXDES0_RFL (curr_des->rxdes0); + + debug ("%s(): RX buffer %d, %x received\n", + __func__, priv->rx_index, rxlen); + + /* pass the packet up to the protocol layers. */ + + NetReceive ((void *)curr_des->rxdes2, rxlen); + + /* release buffer to DMA */ + + curr_des->rxdes0 |= FTMAC100_RXDES0_RXDMA_OWN; + + priv->rx_index = (priv->rx_index + 1) % PKTBUFSRX; + + return 0; +} + +/* + * Send a data block via Ethernet + */ +static int +ftmac100_send (struct eth_device *dev, volatile void *packet, int length) +{ + struct ftmac100 *ftmac100 = (struct ftmac100 *)dev->iobase; + struct ftmac100_data *priv = dev->priv; + volatile struct ftmac100_txdes *curr_des = priv->txdes; + int tmo; + + if (curr_des->txdes0 & FTMAC100_TXDES0_TXDMA_OWN) { + debug ("%s(): no TX descriptor available\n", __func__); + return -1; + } + + debug ("%s(%x, %x)\n", __func__, (int)packet, length); + + length = (length < ETH_ZLEN) ? ETH_ZLEN : length; + + /* initiate a transmit sequence */ + + curr_des->txdes2 = (unsigned int)packet; /* TXBUF_BADR */ + + curr_des->txdes1 &= FTMAC100_TXDES1_EDOTR; + curr_des->txdes1 |= FTMAC100_TXDES1_FTS | + FTMAC100_TXDES1_LTS | + FTMAC100_TXDES1_TXBUF_SIZE (length); + + curr_des->txdes0 = FTMAC100_TXDES0_TXDMA_OWN; + + /* start transmit */ + + writel (1, &ftmac100->txpd); + + /* wait for transfer to succeed */ + + tmo = get_timer (0) + 5 * CONFIG_SYS_HZ; + while (curr_des->txdes0 & FTMAC100_TXDES0_TXDMA_OWN) { + if (get_timer (0) >= tmo) { + debug ("%s(): timed out\n", __func__); + return -1; + } + } + + debug ("%s(): packet sent\n", __func__); + + return 0; +} + +int ftmac100_initialize (bd_t *bd) +{ + struct eth_device *dev; + struct ftmac100_data *priv; + + dev = malloc (sizeof *dev); + if (!dev) { + printf ("%s(): failed to allocate dev\n", __func__); + goto out; + } + + /* Transmit and receive descriptors should align to 16 bytes */ + + priv = memalign (16, sizeof (struct ftmac100_data)); + if (!priv) { + printf ("%s(): failed to allocate priv\n", __func__); + goto free_dev; + } + + memset (dev, 0, sizeof (*dev)); + memset (priv, 0, sizeof (*priv)); + + sprintf (dev->name, "FTMAC100"); + dev->iobase = CONFIG_FTMAC100_BASE; + dev->init = ftmac100_init; + dev->halt = ftmac100_halt; + dev->send = ftmac100_send; + dev->recv = ftmac100_recv; + dev->priv = priv; + + eth_register (dev); + + return 1; + +free_dev: + free (dev); +out: + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/ftmac100.h b/roms/u-boot-sam460ex/drivers/net/ftmac100.h new file mode 100644 index 000000000..21142d966 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ftmac100.h @@ -0,0 +1,154 @@ +/* + * Faraday FTMAC100 Ethernet + * + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang <ratbert@faraday-tech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __FTMAC100_H +#define __FTMAC100_H + +struct ftmac100 { + unsigned int isr; /* 0x00 */ + unsigned int imr; /* 0x04 */ + unsigned int mac_madr; /* 0x08 */ + unsigned int mac_ladr; /* 0x0c */ + unsigned int maht0; /* 0x10 */ + unsigned int maht1; /* 0x14 */ + unsigned int txpd; /* 0x18 */ + unsigned int rxpd; /* 0x1c */ + unsigned int txr_badr; /* 0x20 */ + unsigned int rxr_badr; /* 0x24 */ + unsigned int itc; /* 0x28 */ + unsigned int aptc; /* 0x2c */ + unsigned int dblac; /* 0x30 */ + unsigned int pad1[3]; /* 0x34 - 0x3c */ + unsigned int pad2[16]; /* 0x40 - 0x7c */ + unsigned int pad3[2]; /* 0x80 - 0x84 */ + unsigned int maccr; /* 0x88 */ + unsigned int macsr; /* 0x8c */ + unsigned int phycr; /* 0x90 */ + unsigned int phywdata; /* 0x94 */ + unsigned int fcr; /* 0x98 */ + unsigned int bpr; /* 0x9c */ + unsigned int pad4[8]; /* 0xa0 - 0xbc */ + unsigned int pad5; /* 0xc0 */ + unsigned int ts; /* 0xc4 */ + unsigned int dmafifos; /* 0xc8 */ + unsigned int tm; /* 0xcc */ + unsigned int pad6; /* 0xd0 */ + unsigned int tx_mcol_scol; /* 0xd4 */ + unsigned int rpf_aep; /* 0xd8 */ + unsigned int xm_pg; /* 0xdc */ + unsigned int runt_tlcc; /* 0xe0 */ + unsigned int crcer_ftl; /* 0xe4 */ + unsigned int rlc_rcc; /* 0xe8 */ + unsigned int broc; /* 0xec */ + unsigned int mulca; /* 0xf0 */ + unsigned int rp; /* 0xf4 */ + unsigned int xp; /* 0xf8 */ +}; + +/* + * Interrupt status register & interrupt mask register + */ +#define FTMAC100_INT_RPKT_FINISH (1 << 0) +#define FTMAC100_INT_NORXBUF (1 << 1) +#define FTMAC100_INT_XPKT_FINISH (1 << 2) +#define FTMAC100_INT_NOTXBUF (1 << 3) +#define FTMAC100_INT_XPKT_OK (1 << 4) +#define FTMAC100_INT_XPKT_LOST (1 << 5) +#define FTMAC100_INT_RPKT_SAV (1 << 6) +#define FTMAC100_INT_RPKT_LOST (1 << 7) +#define FTMAC100_INT_AHB_ERR (1 << 8) +#define FTMAC100_INT_PHYSTS_CHG (1 << 9) + +/* + * Automatic polling timer control register + */ +#define FTMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0) +#define FTMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) +#define FTMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8) +#define FTMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) + +/* + * MAC control register + */ +#define FTMAC100_MACCR_XDMA_EN (1 << 0) +#define FTMAC100_MACCR_RDMA_EN (1 << 1) +#define FTMAC100_MACCR_SW_RST (1 << 2) +#define FTMAC100_MACCR_LOOP_EN (1 << 3) +#define FTMAC100_MACCR_CRC_DIS (1 << 4) +#define FTMAC100_MACCR_XMT_EN (1 << 5) +#define FTMAC100_MACCR_ENRX_IN_HALFTX (1 << 6) +#define FTMAC100_MACCR_RCV_EN (1 << 8) +#define FTMAC100_MACCR_HT_MULTI_EN (1 << 9) +#define FTMAC100_MACCR_RX_RUNT (1 << 10) +#define FTMAC100_MACCR_RX_FTL (1 << 11) +#define FTMAC100_MACCR_RCV_ALL (1 << 12) +#define FTMAC100_MACCR_CRC_APD (1 << 14) +#define FTMAC100_MACCR_FULLDUP (1 << 15) +#define FTMAC100_MACCR_RX_MULTIPKT (1 << 16) +#define FTMAC100_MACCR_RX_BROADPKT (1 << 17) + +/* + * Transmit descriptor, aligned to 16 bytes + */ +struct ftmac100_txdes { + unsigned int txdes0; + unsigned int txdes1; + unsigned int txdes2; /* TXBUF_BADR */ + unsigned int txdes3; /* not used by HW */ +} __attribute__ ((aligned(16))); + +#define FTMAC100_TXDES0_TXPKT_LATECOL (1 << 0) +#define FTMAC100_TXDES0_TXPKT_EXSCOL (1 << 1) +#define FTMAC100_TXDES0_TXDMA_OWN (1 << 31) + +#define FTMAC100_TXDES1_TXBUF_SIZE(x) ((x) & 0x7ff) +#define FTMAC100_TXDES1_LTS (1 << 27) +#define FTMAC100_TXDES1_FTS (1 << 28) +#define FTMAC100_TXDES1_TX2FIC (1 << 29) +#define FTMAC100_TXDES1_TXIC (1 << 30) +#define FTMAC100_TXDES1_EDOTR (1 << 31) + +/* + * Receive descriptor, aligned to 16 bytes + */ +struct ftmac100_rxdes { + unsigned int rxdes0; + unsigned int rxdes1; + unsigned int rxdes2; /* RXBUF_BADR */ + unsigned int rxdes3; /* not used by HW */ +} __attribute__ ((aligned(16))); + +#define FTMAC100_RXDES0_RFL(des) ((des) & 0x7ff) +#define FTMAC100_RXDES0_MULTICAST (1 << 16) +#define FTMAC100_RXDES0_BROADCAST (1 << 17) +#define FTMAC100_RXDES0_RX_ERR (1 << 18) +#define FTMAC100_RXDES0_CRC_ERR (1 << 19) +#define FTMAC100_RXDES0_FTL (1 << 20) +#define FTMAC100_RXDES0_RUNT (1 << 21) +#define FTMAC100_RXDES0_RX_ODD_NB (1 << 22) +#define FTMAC100_RXDES0_LRS (1 << 28) +#define FTMAC100_RXDES0_FRS (1 << 29) +#define FTMAC100_RXDES0_RXDMA_OWN (1 << 31) + +#define FTMAC100_RXDES1_RXBUF_SIZE(x) ((x) & 0x7ff) +#define FTMAC100_RXDES1_EDORR (1 << 31) + +#endif /* __FTMAC100_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/greth.c b/roms/u-boot-sam460ex/drivers/net/greth.c new file mode 100644 index 000000000..79bc4d9d2 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/greth.c @@ -0,0 +1,662 @@ +/* Gaisler.com GRETH 10/100/1000 Ethernet MAC driver + * + * Driver use polling mode (no Interrupt) + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <netdev.h> +#include <malloc.h> +#include <asm/processor.h> +#include <ambapp.h> +#include <asm/leon.h> + +/* #define DEBUG */ + +#include "greth.h" + +/* Default to 3s timeout on autonegotiation */ +#ifndef GRETH_PHY_TIMEOUT_MS +#define GRETH_PHY_TIMEOUT_MS 3000 +#endif + +/* ByPass Cache when reading regs */ +#define GRETH_REGLOAD(addr) SPARC_NOCACHE_READ(addr) +/* Write-through cache ==> no bypassing needed on writes */ +#define GRETH_REGSAVE(addr,data) (*(unsigned int *)(addr) = (data)) +#define GRETH_REGORIN(addr,data) GRETH_REGSAVE(addr,GRETH_REGLOAD(addr)|data) +#define GRETH_REGANDIN(addr,data) GRETH_REGSAVE(addr,GRETH_REGLOAD(addr)&data) + +#define GRETH_RXBD_CNT 4 +#define GRETH_TXBD_CNT 1 + +#define GRETH_RXBUF_SIZE 1540 +#define GRETH_BUF_ALIGN 4 +#define GRETH_RXBUF_EFF_SIZE \ + ( (GRETH_RXBUF_SIZE&~(GRETH_BUF_ALIGN-1))+GRETH_BUF_ALIGN ) + +typedef struct { + greth_regs *regs; + int irq; + struct eth_device *dev; + + /* Hardware info */ + unsigned char phyaddr; + int gbit_mac; + + /* Current operating Mode */ + int gb; /* GigaBit */ + int fd; /* Full Duplex */ + int sp; /* 10/100Mbps speed (1=100,0=10) */ + int auto_neg; /* Auto negotiate done */ + + unsigned char hwaddr[6]; /* MAC Address */ + + /* Descriptors */ + greth_bd *rxbd_base, *rxbd_max; + greth_bd *txbd_base, *txbd_max; + + greth_bd *rxbd_curr; + + /* rx buffers in rx descriptors */ + void *rxbuf_base; /* (GRETH_RXBUF_SIZE+ALIGNBYTES) * GRETH_RXBD_CNT */ + + /* unused for gbit_mac, temp buffer for sending packets with unligned + * start. + * Pointer to packet allocated with malloc. + */ + void *txbuf; + + struct { + /* rx status */ + unsigned int rx_packets, + rx_crc_errors, rx_frame_errors, rx_length_errors, rx_errors; + + /* tx stats */ + unsigned int tx_packets, + tx_latecol_errors, + tx_underrun_errors, tx_limit_errors, tx_errors; + } stats; +} greth_priv; + +/* Read MII register 'addr' from core 'regs' */ +static int read_mii(int addr, volatile greth_regs * regs) +{ + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + + GRETH_REGSAVE(®s->mdio, (0 << 11) | ((addr & 0x1F) << 6) | 2); + + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + + if (!(GRETH_REGLOAD(®s->mdio) & GRETH_MII_NVALID)) { + return (GRETH_REGLOAD(®s->mdio) >> 16) & 0xFFFF; + } else { + return -1; + } +} + +static void write_mii(int addr, int data, volatile greth_regs * regs) +{ + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + + GRETH_REGSAVE(®s->mdio, + ((data & 0xFFFF) << 16) | (0 << 11) | ((addr & 0x1F) << 6) + | 1); + + while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { + } + +} + +/* init/start hardware and allocate descriptor buffers for rx side + * + */ +int greth_init(struct eth_device *dev, bd_t * bis) +{ + int i; + + greth_priv *greth = dev->priv; + greth_regs *regs = greth->regs; +#ifdef DEBUG + printf("greth_init\n"); +#endif + + GRETH_REGSAVE(®s->control, 0); + + if (!greth->rxbd_base) { + + /* allocate descriptors */ + greth->rxbd_base = (greth_bd *) + memalign(0x1000, GRETH_RXBD_CNT * sizeof(greth_bd)); + greth->txbd_base = (greth_bd *) + memalign(0x1000, GRETH_RXBD_CNT * sizeof(greth_bd)); + + /* allocate buffers to all descriptors */ + greth->rxbuf_base = + malloc(GRETH_RXBUF_EFF_SIZE * GRETH_RXBD_CNT); + } + + /* initate rx decriptors */ + for (i = 0; i < GRETH_RXBD_CNT; i++) { + greth->rxbd_base[i].addr = (unsigned int) + greth->rxbuf_base + (GRETH_RXBUF_EFF_SIZE * i); + /* enable desciptor & set wrap bit if last descriptor */ + if (i >= (GRETH_RXBD_CNT - 1)) { + greth->rxbd_base[i].stat = GRETH_BD_EN | GRETH_BD_WR; + } else { + greth->rxbd_base[i].stat = GRETH_BD_EN; + } + } + + /* initiate indexes */ + greth->rxbd_curr = greth->rxbd_base; + greth->rxbd_max = greth->rxbd_base + (GRETH_RXBD_CNT - 1); + greth->txbd_max = greth->txbd_base + (GRETH_TXBD_CNT - 1); + /* + * greth->txbd_base->addr = 0; + * greth->txbd_base->stat = GRETH_BD_WR; + */ + + /* initate tx decriptors */ + for (i = 0; i < GRETH_TXBD_CNT; i++) { + greth->txbd_base[i].addr = 0; + /* enable desciptor & set wrap bit if last descriptor */ + if (i >= (GRETH_RXBD_CNT - 1)) { + greth->txbd_base[i].stat = GRETH_BD_WR; + } else { + greth->txbd_base[i].stat = 0; + } + } + + /**** SET HARDWARE REGS ****/ + + /* Set pointer to tx/rx descriptor areas */ + GRETH_REGSAVE(®s->rx_desc_p, (unsigned int)&greth->rxbd_base[0]); + GRETH_REGSAVE(®s->tx_desc_p, (unsigned int)&greth->txbd_base[0]); + + /* Enable Transmitter, GRETH will now scan descriptors for packets + * to transmitt */ +#ifdef DEBUG + printf("greth_init: enabling receiver\n"); +#endif + GRETH_REGORIN(®s->control, GRETH_RXEN); + + return 0; +} + +/* Initiate PHY to a relevant speed + * return: + * - 0 = success + * - 1 = timeout/fail + */ +int greth_init_phy(greth_priv * dev, bd_t * bis) +{ + greth_regs *regs = dev->regs; + int tmp, tmp1, tmp2, i; + unsigned int start, timeout; + + /* X msecs to ticks */ + timeout = usec2ticks(GRETH_PHY_TIMEOUT_MS * 1000); + + /* Get system timer0 current value + * Total timeout is 5s + */ + start = get_timer(0); + + /* get phy control register default values */ + + while ((tmp = read_mii(0, regs)) & 0x8000) { + if (get_timer(start) > timeout) + return 1; /* Fail */ + } + + /* reset PHY and wait for completion */ + write_mii(0, 0x8000 | tmp, regs); + + while (((tmp = read_mii(0, regs))) & 0x8000) { + if (get_timer(start) > timeout) + return 1; /* Fail */ + } + + /* Check if PHY is autoneg capable and then determine operating + * mode, otherwise force it to 10 Mbit halfduplex + */ + dev->gb = 0; + dev->fd = 0; + dev->sp = 0; + dev->auto_neg = 0; + if (!((tmp >> 12) & 1)) { + write_mii(0, 0, regs); + } else { + /* wait for auto negotiation to complete and then check operating mode */ + dev->auto_neg = 1; + i = 0; + while (!(((tmp = read_mii(1, regs)) >> 5) & 1)) { + if (get_timer(start) > timeout) { + printf("Auto negotiation timed out. " + "Selecting default config\n"); + tmp = read_mii(0, regs); + dev->gb = ((tmp >> 6) & 1) + && !((tmp >> 13) & 1); + dev->sp = !((tmp >> 6) & 1) + && ((tmp >> 13) & 1); + dev->fd = (tmp >> 8) & 1; + goto auto_neg_done; + } + } + if ((tmp >> 8) & 1) { + tmp1 = read_mii(9, regs); + tmp2 = read_mii(10, regs); + if ((tmp1 & GRETH_MII_EXTADV_1000FD) && + (tmp2 & GRETH_MII_EXTPRT_1000FD)) { + dev->gb = 1; + dev->fd = 1; + } + if ((tmp1 & GRETH_MII_EXTADV_1000HD) && + (tmp2 & GRETH_MII_EXTPRT_1000HD)) { + dev->gb = 1; + dev->fd = 0; + } + } + if ((dev->gb == 0) || ((dev->gb == 1) && (dev->gbit_mac == 0))) { + tmp1 = read_mii(4, regs); + tmp2 = read_mii(5, regs); + if ((tmp1 & GRETH_MII_100TXFD) && + (tmp2 & GRETH_MII_100TXFD)) { + dev->sp = 1; + dev->fd = 1; + } + if ((tmp1 & GRETH_MII_100TXHD) && + (tmp2 & GRETH_MII_100TXHD)) { + dev->sp = 1; + dev->fd = 0; + } + if ((tmp1 & GRETH_MII_10FD) && (tmp2 & GRETH_MII_10FD)) { + dev->fd = 1; + } + if ((dev->gb == 1) && (dev->gbit_mac == 0)) { + dev->gb = 0; + dev->fd = 0; + write_mii(0, dev->sp << 13, regs); + } + } + + } + auto_neg_done: +#ifdef DEBUG + printf("%s GRETH Ethermac at [0x%x] irq %d. Running \ + %d Mbps %s duplex\n", dev->gbit_mac ? "10/100/1000" : "10/100", (unsigned int)(regs), (unsigned int)(dev->irq), dev->gb ? 1000 : (dev->sp ? 100 : 10), dev->fd ? "full" : "half"); +#endif + /* Read out PHY info if extended registers are available */ + if (tmp & 1) { + tmp1 = read_mii(2, regs); + tmp2 = read_mii(3, regs); + tmp1 = (tmp1 << 6) | ((tmp2 >> 10) & 0x3F); + tmp = tmp2 & 0xF; + + tmp2 = (tmp2 >> 4) & 0x3F; +#ifdef DEBUG + printf("PHY: Vendor %x Device %x Revision %d\n", tmp1, + tmp2, tmp); +#endif + } else { + printf("PHY info not available\n"); + } + + /* set speed and duplex bits in control register */ + GRETH_REGORIN(®s->control, + (dev->gb << 8) | (dev->sp << 7) | (dev->fd << 4)); + + return 0; +} + +void greth_halt(struct eth_device *dev) +{ + greth_priv *greth; + greth_regs *regs; + int i; +#ifdef DEBUG + printf("greth_halt\n"); +#endif + if (!dev || !dev->priv) + return; + + greth = dev->priv; + regs = greth->regs; + + if (!regs) + return; + + /* disable receiver/transmitter by clearing the enable bits */ + GRETH_REGANDIN(®s->control, ~(GRETH_RXEN | GRETH_TXEN)); + + /* reset rx/tx descriptors */ + if (greth->rxbd_base) { + for (i = 0; i < GRETH_RXBD_CNT; i++) { + greth->rxbd_base[i].stat = + (i >= (GRETH_RXBD_CNT - 1)) ? GRETH_BD_WR : 0; + } + } + + if (greth->txbd_base) { + for (i = 0; i < GRETH_TXBD_CNT; i++) { + greth->txbd_base[i].stat = + (i >= (GRETH_TXBD_CNT - 1)) ? GRETH_BD_WR : 0; + } + } +} + +int greth_send(struct eth_device *dev, volatile void *eth_data, int data_length) +{ + greth_priv *greth = dev->priv; + greth_regs *regs = greth->regs; + greth_bd *txbd; + void *txbuf; + unsigned int status; +#ifdef DEBUG + printf("greth_send\n"); +#endif + /* send data, wait for data to be sent, then return */ + if (((unsigned int)eth_data & (GRETH_BUF_ALIGN - 1)) + && !greth->gbit_mac) { + /* data not aligned as needed by GRETH 10/100, solve this by allocating 4 byte aligned buffer + * and copy data to before giving it to GRETH. + */ + if (!greth->txbuf) { + greth->txbuf = malloc(GRETH_RXBUF_SIZE); +#ifdef DEBUG + printf("GRETH: allocated aligned tx-buf\n"); +#endif + } + + txbuf = greth->txbuf; + + /* copy data info buffer */ + memcpy((char *)txbuf, (char *)eth_data, data_length); + + /* keep buffer to next time */ + } else { + txbuf = (void *)eth_data; + } + /* get descriptor to use, only 1 supported... hehe easy */ + txbd = greth->txbd_base; + + /* setup descriptor to wrap around to it self */ + txbd->addr = (unsigned int)txbuf; + txbd->stat = GRETH_BD_EN | GRETH_BD_WR | data_length; + + /* Remind Core which descriptor to use when sending */ + GRETH_REGSAVE(®s->tx_desc_p, (unsigned int)txbd); + + /* initate send by enabling transmitter */ + GRETH_REGORIN(®s->control, GRETH_TXEN); + + /* Wait for data to be sent */ + while ((status = GRETH_REGLOAD(&txbd->stat)) & GRETH_BD_EN) { + ; + } + + /* was the packet transmitted succesfully? */ + if (status & GRETH_TXBD_ERR_AL) { + greth->stats.tx_limit_errors++; + } + + if (status & GRETH_TXBD_ERR_UE) { + greth->stats.tx_underrun_errors++; + } + + if (status & GRETH_TXBD_ERR_LC) { + greth->stats.tx_latecol_errors++; + } + + if (status & + (GRETH_TXBD_ERR_LC | GRETH_TXBD_ERR_UE | GRETH_TXBD_ERR_AL)) { + /* any error */ + greth->stats.tx_errors++; + return -1; + } + + /* bump tx packet counter */ + greth->stats.tx_packets++; + + /* return succefully */ + return 0; +} + +int greth_recv(struct eth_device *dev) +{ + greth_priv *greth = dev->priv; + greth_regs *regs = greth->regs; + greth_bd *rxbd; + unsigned int status, len = 0, bad; + unsigned char *d; + int enable = 0; + int i; +#ifdef DEBUG +/* printf("greth_recv\n"); */ +#endif + /* Receive One packet only, but clear as many error packets as there are + * available. + */ + { + /* current receive descriptor */ + rxbd = greth->rxbd_curr; + + /* get status of next received packet */ + status = GRETH_REGLOAD(&rxbd->stat); + + bad = 0; + + /* stop if no more packets received */ + if (status & GRETH_BD_EN) { + goto done; + } +#ifdef DEBUG + printf("greth_recv: packet 0x%lx, 0x%lx, len: %d\n", + (unsigned int)rxbd, status, status & GRETH_BD_LEN); +#endif + + /* Check status for errors. + */ + if (status & GRETH_RXBD_ERR_FT) { + greth->stats.rx_length_errors++; + bad = 1; + } + if (status & (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE)) { + greth->stats.rx_frame_errors++; + bad = 1; + } + if (status & GRETH_RXBD_ERR_CRC) { + greth->stats.rx_crc_errors++; + bad = 1; + } + if (bad) { + greth->stats.rx_errors++; + printf + ("greth_recv: Bad packet (%d, %d, %d, 0x%08x, %d)\n", + greth->stats.rx_length_errors, + greth->stats.rx_frame_errors, + greth->stats.rx_crc_errors, status, + greth->stats.rx_packets); + /* print all rx descriptors */ + for (i = 0; i < GRETH_RXBD_CNT; i++) { + printf("[%d]: Stat=0x%lx, Addr=0x%lx\n", i, + GRETH_REGLOAD(&greth->rxbd_base[i].stat), + GRETH_REGLOAD(&greth->rxbd_base[i]. + addr)); + } + } else { + /* Process the incoming packet. */ + len = status & GRETH_BD_LEN; + d = (char *)rxbd->addr; +#ifdef DEBUG + printf + ("greth_recv: new packet, length: %d. data: %x %x %x %x %x %x %x %x\n", + len, d[0], d[1], d[2], d[3], d[4], d[5], d[6], + d[7]); +#endif + /* flush all data cache to make sure we're not reading old packet data */ + sparc_dcache_flush_all(); + + /* pass packet on to network subsystem */ + NetReceive((void *)d, len); + + /* bump stats counters */ + greth->stats.rx_packets++; + + /* bad is now 0 ==> will stop loop */ + } + + /* reenable descriptor to receive more packet with this descriptor, wrap around if needed */ + rxbd->stat = + GRETH_BD_EN | + (((unsigned int)greth->rxbd_curr >= + (unsigned int)greth->rxbd_max) ? GRETH_BD_WR : 0); + enable = 1; + + /* increase index */ + greth->rxbd_curr = + ((unsigned int)greth->rxbd_curr >= + (unsigned int)greth->rxbd_max) ? greth-> + rxbd_base : (greth->rxbd_curr + 1); + + }; + + if (enable) { + GRETH_REGORIN(®s->control, GRETH_RXEN); + } + done: + /* return positive length of packet or 0 if non recieved */ + return len; +} + +void greth_set_hwaddr(greth_priv * greth, unsigned char *mac) +{ + /* save new MAC address */ + greth->dev->enetaddr[0] = greth->hwaddr[0] = mac[0]; + greth->dev->enetaddr[1] = greth->hwaddr[1] = mac[1]; + greth->dev->enetaddr[2] = greth->hwaddr[2] = mac[2]; + greth->dev->enetaddr[3] = greth->hwaddr[3] = mac[3]; + greth->dev->enetaddr[4] = greth->hwaddr[4] = mac[4]; + greth->dev->enetaddr[5] = greth->hwaddr[5] = mac[5]; + greth->regs->esa_msb = (mac[0] << 8) | mac[1]; + greth->regs->esa_lsb = + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5]; +#ifdef DEBUG + printf("GRETH: New MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +#endif +} + +int greth_initialize(bd_t * bis) +{ + greth_priv *greth; + ambapp_apbdev apbdev; + struct eth_device *dev; + int i; + char *addr_str, *end; + unsigned char addr[6]; +#ifdef DEBUG + printf("Scanning for GRETH\n"); +#endif + /* Find Device & IRQ via AMBA Plug&Play information */ + if (ambapp_apb_first(VENDOR_GAISLER, GAISLER_ETHMAC, &apbdev) != 1) { + return -1; /* GRETH not found */ + } + + greth = (greth_priv *) malloc(sizeof(greth_priv)); + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + memset(dev, 0, sizeof(struct eth_device)); + memset(greth, 0, sizeof(greth_priv)); + + greth->regs = (greth_regs *) apbdev.address; + greth->irq = apbdev.irq; +#ifdef DEBUG + printf("Found GRETH at 0x%lx, irq %d\n", greth->regs, greth->irq); +#endif + dev->priv = (void *)greth; + dev->iobase = (unsigned int)greth->regs; + dev->init = greth_init; + dev->halt = greth_halt; + dev->send = greth_send; + dev->recv = greth_recv; + greth->dev = dev; + + /* Reset Core */ + GRETH_REGSAVE(&greth->regs->control, GRETH_RESET); + + /* Wait for core to finish reset cycle */ + while (GRETH_REGLOAD(&greth->regs->control) & GRETH_RESET) ; + + /* Get the phy address which assumed to have been set + correctly with the reset value in hardware */ + greth->phyaddr = (GRETH_REGLOAD(&greth->regs->mdio) >> 11) & 0x1F; + + /* Check if mac is gigabit capable */ + greth->gbit_mac = (GRETH_REGLOAD(&greth->regs->control) >> 27) & 1; + + /* Make descriptor string */ + if (greth->gbit_mac) { + sprintf(dev->name, "GRETH 10/100/GB"); + } else { + sprintf(dev->name, "GRETH 10/100"); + } + + /* initiate PHY, select speed/duplex depending on connected PHY */ + if (greth_init_phy(greth, bis)) { + /* Failed to init PHY (timedout) */ + return -1; + } + + /* Register Device to EtherNet subsystem */ + eth_register(dev); + + /* Get MAC address */ + if ((addr_str = getenv("ethaddr")) != NULL) { + for (i = 0; i < 6; i++) { + addr[i] = + addr_str ? simple_strtoul(addr_str, &end, 16) : 0; + if (addr_str) { + addr_str = (*end) ? end + 1 : end; + } + } + } else { + /* HW Address not found in environment, Set default HW address */ + addr[0] = GRETH_HWADDR_0; /* MSB */ + addr[1] = GRETH_HWADDR_1; + addr[2] = GRETH_HWADDR_2; + addr[3] = GRETH_HWADDR_3; + addr[4] = GRETH_HWADDR_4; + addr[5] = GRETH_HWADDR_5; /* LSB */ + } + + /* set and remember MAC address */ + greth_set_hwaddr(greth, addr); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/greth.h b/roms/u-boot-sam460ex/drivers/net/greth.h new file mode 100644 index 000000000..7d5fbd327 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/greth.h @@ -0,0 +1,97 @@ +/* Gaisler.com GRETH 10/100/1000 Ethernet MAC driver + * + * (C) Copyright 2007 + * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#define GRETH_FD 0x10 +#define GRETH_RESET 0x40 +#define GRETH_MII_BUSY 0x8 +#define GRETH_MII_NVALID 0x10 + +/* MII registers */ +#define GRETH_MII_EXTADV_1000FD 0x00000200 +#define GRETH_MII_EXTADV_1000HD 0x00000100 +#define GRETH_MII_EXTPRT_1000FD 0x00000800 +#define GRETH_MII_EXTPRT_1000HD 0x00000400 + +#define GRETH_MII_100T4 0x00000200 +#define GRETH_MII_100TXFD 0x00000100 +#define GRETH_MII_100TXHD 0x00000080 +#define GRETH_MII_10FD 0x00000040 +#define GRETH_MII_10HD 0x00000020 + +#define GRETH_BD_EN 0x800 +#define GRETH_BD_WR 0x1000 +#define GRETH_BD_IE 0x2000 +#define GRETH_BD_LEN 0x7FF + +#define GRETH_TXEN 0x1 +#define GRETH_INT_TX 0x8 +#define GRETH_TXI 0x4 +#define GRETH_TXBD_STATUS 0x0001C000 +#define GRETH_TXBD_MORE 0x20000 +#define GRETH_TXBD_IPCS 0x40000 +#define GRETH_TXBD_TCPCS 0x80000 +#define GRETH_TXBD_UDPCS 0x100000 +#define GRETH_TXBD_ERR_LC 0x10000 +#define GRETH_TXBD_ERR_UE 0x4000 +#define GRETH_TXBD_ERR_AL 0x8000 +#define GRETH_TXBD_NUM 128 +#define GRETH_TXBD_NUM_MASK (GRETH_TXBD_NUM-1) +#define GRETH_TX_BUF_SIZE 2048 + +#define GRETH_INT_RX 0x4 +#define GRETH_RXEN 0x2 +#define GRETH_RXI 0x8 +#define GRETH_RXBD_STATUS 0xFFFFC000 +#define GRETH_RXBD_ERR_AE 0x4000 +#define GRETH_RXBD_ERR_FT 0x8000 +#define GRETH_RXBD_ERR_CRC 0x10000 +#define GRETH_RXBD_ERR_OE 0x20000 +#define GRETH_RXBD_ERR_LE 0x40000 +#define GRETH_RXBD_IP_DEC 0x80000 +#define GRETH_RXBD_IP_CSERR 0x100000 +#define GRETH_RXBD_UDP_DEC 0x200000 +#define GRETH_RXBD_UDP_CSERR 0x400000 +#define GRETH_RXBD_TCP_DEC 0x800000 +#define GRETH_RXBD_TCP_CSERR 0x1000000 + +#define GRETH_RXBD_NUM 128 +#define GRETH_RXBD_NUM_MASK (GRETH_RXBD_NUM-1) +#define GRETH_RX_BUF_SIZE 2048 + +/* Ethernet configuration registers */ +typedef struct _greth_regs { + volatile unsigned int control; + volatile unsigned int status; + volatile unsigned int esa_msb; + volatile unsigned int esa_lsb; + volatile unsigned int mdio; + volatile unsigned int tx_desc_p; + volatile unsigned int rx_desc_p; +} greth_regs; + +/* Ethernet buffer descriptor */ +typedef struct _greth_bd { + volatile unsigned int stat; + unsigned int addr; /* Buffer address not changed by HW */ +} greth_bd; diff --git a/roms/u-boot-sam460ex/drivers/net/inca-ip_sw.c b/roms/u-boot-sam460ex/drivers/net/inca-ip_sw.c new file mode 100644 index 000000000..492f5ce8f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/inca-ip_sw.c @@ -0,0 +1,813 @@ +/* + * INCA-IP internal switch ethernet driver. + * + * (C) Copyright 2003-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + + +#include <common.h> + +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/inca-ip.h> +#include <asm/addrspace.h> + + +#define NUM_RX_DESC PKTBUFSRX +#define NUM_TX_DESC 3 +#define TOUT_LOOP 1000000 + + +#define DELAY udelay(10000) + /* Sometimes the store word instruction hangs while writing to one + * of the Switch registers. Moving the instruction into a separate + * function somehow makes the problem go away. + */ +static void SWORD(volatile u32 * reg, u32 value) +{ + *reg = value; +} + +#define DMA_WRITE_REG(reg, value) *((volatile u32 *)reg) = (u32)value; +#define DMA_READ_REG(reg, value) value = (u32)*((volatile u32*)reg) +#define SW_WRITE_REG(reg, value) \ + SWORD(reg, value);\ + DELAY;\ + SWORD(reg, value); + +#define SW_READ_REG(reg, value) \ + value = (u32)*((volatile u32*)reg);\ + DELAY;\ + value = (u32)*((volatile u32*)reg); + +#define INCA_DMA_TX_POLLING_TIME 0x07 +#define INCA_DMA_RX_POLLING_TIME 0x07 + +#define INCA_DMA_TX_HOLD 0x80000000 +#define INCA_DMA_TX_EOP 0x40000000 +#define INCA_DMA_TX_SOP 0x20000000 +#define INCA_DMA_TX_ICPT 0x10000000 +#define INCA_DMA_TX_IEOP 0x08000000 + +#define INCA_DMA_RX_C 0x80000000 +#define INCA_DMA_RX_SOP 0x40000000 +#define INCA_DMA_RX_EOP 0x20000000 + +#define INCA_SWITCH_PHY_SPEED_10H 0x1 +#define INCA_SWITCH_PHY_SPEED_10F 0x5 +#define INCA_SWITCH_PHY_SPEED_100H 0x2 +#define INCA_SWITCH_PHY_SPEED_100F 0x6 + +/************************ Auto MDIX settings ************************/ +#define INCA_IP_AUTO_MDIX_LAN_PORTS_DIR INCA_IP_Ports_P1_DIR +#define INCA_IP_AUTO_MDIX_LAN_PORTS_ALTSEL INCA_IP_Ports_P1_ALTSEL +#define INCA_IP_AUTO_MDIX_LAN_PORTS_OUT INCA_IP_Ports_P1_OUT +#define INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX 16 + +#define WAIT_SIGNAL_RETRIES 100 +#define WAIT_LINK_RETRIES 100 +#define LINK_RETRY_DELAY 2000 /* ms */ +/********************************************************************/ + +typedef struct +{ + union { + struct { + volatile u32 HOLD :1; + volatile u32 ICpt :1; + volatile u32 IEop :1; + volatile u32 offset :3; + volatile u32 reserved0 :4; + volatile u32 NFB :22; + }field; + + volatile u32 word; + }params; + + volatile u32 nextRxDescPtr; + + volatile u32 RxDataPtr; + + union { + struct { + volatile u32 C :1; + volatile u32 Sop :1; + volatile u32 Eop :1; + volatile u32 reserved3 :12; + volatile u32 NBT :17; + }field; + + volatile u32 word; + }status; + +} inca_rx_descriptor_t; + + +typedef struct +{ + union { + struct { + volatile u32 HOLD :1; + volatile u32 Eop :1; + volatile u32 Sop :1; + volatile u32 ICpt :1; + volatile u32 IEop :1; + volatile u32 reserved0 :5; + volatile u32 NBA :22; + }field; + + volatile u32 word; + }params; + + volatile u32 nextTxDescPtr; + + volatile u32 TxDataPtr; + + volatile u32 C :1; + volatile u32 reserved3 :31; + +} inca_tx_descriptor_t; + + +static inca_rx_descriptor_t rx_ring[NUM_RX_DESC] __attribute__ ((aligned(16))); +static inca_tx_descriptor_t tx_ring[NUM_TX_DESC] __attribute__ ((aligned(16))); + +static int tx_new, rx_new, tx_hold, rx_hold; +static int tx_old_hold = -1; +static int initialized = 0; + + +static int inca_switch_init(struct eth_device *dev, bd_t * bis); +static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length); +static int inca_switch_recv(struct eth_device *dev); +static void inca_switch_halt(struct eth_device *dev); +static void inca_init_switch_chip(void); +static void inca_dma_init(void); +static int inca_amdix(void); + + +int inca_switch_initialize(bd_t * bis) +{ + struct eth_device *dev; + +#if 0 + printf("Entered inca_switch_initialize()\n"); +#endif + + if (!(dev = (struct eth_device *) malloc (sizeof *dev))) { + printf("Failed to allocate memory\n"); + return 0; + } + memset(dev, 0, sizeof(*dev)); + + inca_dma_init(); + + inca_init_switch_chip(); + +#if defined(CONFIG_INCA_IP_SWITCH_AMDIX) + inca_amdix(); +#endif + + sprintf(dev->name, "INCA-IP Switch"); + dev->init = inca_switch_init; + dev->halt = inca_switch_halt; + dev->send = inca_switch_send; + dev->recv = inca_switch_recv; + + eth_register(dev); + +#if 0 + printf("Leaving inca_switch_initialize()\n"); +#endif + + return 0; +} + + +static int inca_switch_init(struct eth_device *dev, bd_t * bis) +{ + int i; + u32 v, regValue; + u16 wTmp; + +#if 0 + printf("Entering inca_switch_init()\n"); +#endif + + /* Set MAC address. + */ + wTmp = (u16)dev->enetaddr[0]; + regValue = (wTmp << 8) | dev->enetaddr[1]; + + SW_WRITE_REG(INCA_IP_Switch_PMAC_SA1, regValue); + + wTmp = (u16)dev->enetaddr[2]; + regValue = (wTmp << 8) | dev->enetaddr[3]; + regValue = regValue << 16; + wTmp = (u16)dev->enetaddr[4]; + regValue |= (wTmp<<8) | dev->enetaddr[5]; + + SW_WRITE_REG(INCA_IP_Switch_PMAC_SA2, regValue); + + /* Initialize the descriptor rings. + */ + for (i = 0; i < NUM_RX_DESC; i++) { + inca_rx_descriptor_t * rx_desc = (inca_rx_descriptor_t *)CKSEG1ADDR(&rx_ring[i]); + memset(rx_desc, 0, sizeof(rx_ring[i])); + + /* Set maximum size of receive buffer. + */ + rx_desc->params.field.NFB = PKTSIZE_ALIGN; + + /* Set the offset of the receive buffer. Zero means + * that the offset mechanism is not used. + */ + rx_desc->params.field.offset = 0; + + /* Check if it is the last descriptor. + */ + if (i == (NUM_RX_DESC - 1)) { + /* Let the last descriptor point to the first + * one. + */ + rx_desc->nextRxDescPtr = (u32)CKSEG1ADDR(rx_ring); + } else { + /* Set the address of the next descriptor. + */ + rx_desc->nextRxDescPtr = (u32)CKSEG1ADDR(&rx_ring[i+1]); + } + + rx_desc->RxDataPtr = (u32)CKSEG1ADDR(NetRxPackets[i]); + } + +#if 0 + printf("rx_ring = 0x%08X 0x%08X\n", (u32)rx_ring, (u32)&rx_ring[0]); + printf("tx_ring = 0x%08X 0x%08X\n", (u32)tx_ring, (u32)&tx_ring[0]); +#endif + + for (i = 0; i < NUM_TX_DESC; i++) { + inca_tx_descriptor_t * tx_desc = (inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[i]); + + memset(tx_desc, 0, sizeof(tx_ring[i])); + + tx_desc->params.word = 0; + tx_desc->params.field.HOLD = 1; + tx_desc->C = 1; + + /* Check if it is the last descriptor. + */ + if (i == (NUM_TX_DESC - 1)) { + /* Let the last descriptor point to the + * first one. + */ + tx_desc->nextTxDescPtr = (u32)CKSEG1ADDR(tx_ring); + } else { + /* Set the address of the next descriptor. + */ + tx_desc->nextTxDescPtr = (u32)CKSEG1ADDR(&tx_ring[i+1]); + } + } + + /* Initialize RxDMA. + */ + DMA_READ_REG(INCA_IP_DMA_DMA_RXISR, v); +#if 0 + printf("RX status = 0x%08X\n", v); +#endif + + /* Writing to the FRDA of CHANNEL. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXFRDA0, (u32)rx_ring); + + /* Writing to the COMMAND REG. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_INIT); + + /* Initialize TxDMA. + */ + DMA_READ_REG(INCA_IP_DMA_DMA_TXISR, v); +#if 0 + printf("TX status = 0x%08X\n", v); +#endif + + /* Writing to the FRDA of CHANNEL. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXFRDA0, (u32)tx_ring); + + tx_new = rx_new = 0; + + tx_hold = NUM_TX_DESC - 1; + rx_hold = NUM_RX_DESC - 1; + +#if 0 + rx_ring[rx_hold].params.field.HOLD = 1; +#endif + /* enable spanning tree forwarding, enable the CPU port */ + /* ST_PT: + * CPS (CPU port status) 0x3 (forwarding) + * LPS (LAN port status) 0x3 (forwarding) + * PPS (PC port status) 0x3 (forwarding) + */ + SW_WRITE_REG(INCA_IP_Switch_ST_PT,0x3f); + +#if 0 + printf("Leaving inca_switch_init()\n"); +#endif + + return 0; +} + + +static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length) +{ + int i; + int res = -1; + u32 command; + u32 regValue; + inca_tx_descriptor_t * tx_desc = (inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_new]); + +#if 0 + printf("Entered inca_switch_send()\n"); +#endif + + if (length <= 0) { + printf ("%s: bad packet size: %d\n", dev->name, length); + goto Done; + } + + for(i = 0; tx_desc->C == 0; i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx error buffer not ready\n", dev->name); + goto Done; + } + } + + if (tx_old_hold >= 0) { + ((inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_old_hold]))->params.field.HOLD = 1; + } + tx_old_hold = tx_hold; + + tx_desc->params.word = + (INCA_DMA_TX_SOP | INCA_DMA_TX_EOP | INCA_DMA_TX_HOLD); + + tx_desc->C = 0; + tx_desc->TxDataPtr = (u32)packet; + tx_desc->params.field.NBA = length; + + ((inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_hold]))->params.field.HOLD = 0; + + tx_hold = tx_new; + tx_new = (tx_new + 1) % NUM_TX_DESC; + + + if (! initialized) { + command = INCA_IP_DMA_DMA_TXCCR0_INIT; + initialized = 1; + } else { + command = INCA_IP_DMA_DMA_TXCCR0_HR; + } + + DMA_READ_REG(INCA_IP_DMA_DMA_TXCCR0, regValue); + regValue |= command; +#if 0 + printf("regValue = 0x%x\n", regValue); +#endif + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, regValue); + +#if 1 + for(i = 0; ((inca_tx_descriptor_t *)CKSEG1ADDR(&tx_ring[tx_hold]))->C == 0; i++) { + if (i >= TOUT_LOOP) { + printf("%s: tx buffer not ready\n", dev->name); + goto Done; + } + } +#endif + res = length; +Done: +#if 0 + printf("Leaving inca_switch_send()\n"); +#endif + return res; +} + + +static int inca_switch_recv(struct eth_device *dev) +{ + int length = 0; + inca_rx_descriptor_t * rx_desc; + +#if 0 + printf("Entered inca_switch_recv()\n"); +#endif + + for (;;) { + rx_desc = (inca_rx_descriptor_t *)CKSEG1ADDR(&rx_ring[rx_new]); + + if (rx_desc->status.field.C == 0) { + break; + } + +#if 0 + rx_ring[rx_new].params.field.HOLD = 1; +#endif + + if (! rx_desc->status.field.Eop) { + printf("Partly received packet!!!\n"); + break; + } + + length = rx_desc->status.field.NBT; + rx_desc->status.word &= + ~(INCA_DMA_RX_EOP | INCA_DMA_RX_SOP | INCA_DMA_RX_C); +#if 0 +{ + int i; + for (i=0;i<length - 4;i++) { + if (i % 16 == 0) printf("\n%04x: ", i); + printf("%02X ", NetRxPackets[rx_new][i]); + } + printf("\n"); +} +#endif + + if (length) { +#if 0 + printf("Received %d bytes\n", length); +#endif + NetReceive((void*)CKSEG1ADDR(NetRxPackets[rx_new]), length - 4); + } else { +#if 1 + printf("Zero length!!!\n"); +#endif + } + + + ((inca_rx_descriptor_t *)CKSEG1ADDR(&rx_ring[rx_hold]))->params.field.HOLD = 0; + + rx_hold = rx_new; + + rx_new = (rx_new + 1) % NUM_RX_DESC; + } + +#if 0 + printf("Leaving inca_switch_recv()\n"); +#endif + + return length; +} + + +static void inca_switch_halt(struct eth_device *dev) +{ +#if 0 + printf("Entered inca_switch_halt()\n"); +#endif + +#if 1 + initialized = 0; +#endif +#if 1 + /* Disable forwarding to the CPU port. + */ + SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf); + + /* Close RxDMA channel. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF); + + /* Close TxDMA channel. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_TXCCR0_OFF); + + +#endif +#if 0 + printf("Leaving inca_switch_halt()\n"); +#endif +} + + +static void inca_init_switch_chip(void) +{ + u32 regValue; + + /* To workaround a problem with collision counter + * (see Errata sheet). + */ + SW_WRITE_REG(INCA_IP_Switch_PC_TX_CTL, 0x00000001); + SW_WRITE_REG(INCA_IP_Switch_LAN_TX_CTL, 0x00000001); + +#if 1 + /* init MDIO configuration: + * MDS (Poll speed): 0x01 (4ms) + * PHY_LAN_ADDR: 0x06 + * PHY_PC_ADDR: 0x05 + * UEP (Use External PHY): 0x00 (Internal PHY is used) + * PS (Port Select): 0x00 (PT/UMM for LAN) + * PT (PHY Test): 0x00 (no test mode) + * UMM (Use MDIO Mode): 0x00 (state machine is disabled) + */ + SW_WRITE_REG(INCA_IP_Switch_MDIO_CFG, 0x4c50); + + /* init PHY: + * SL (Auto Neg. Speed for LAN) + * SP (Auto Neg. Speed for PC) + * LL (Link Status for LAN) + * LP (Link Status for PC) + * DL (Duplex Status for LAN) + * DP (Duplex Status for PC) + * PL (Auto Neg. Pause Status for LAN) + * PP (Auto Neg. Pause Status for PC) + */ + SW_WRITE_REG (INCA_IP_Switch_EPHY, 0xff); + + /* MDIO_ACC: + * RA (Request/Ack) 0x01 (Request) + * RW (Read/Write) 0x01 (Write) + * PHY_ADDR 0x05 (PC) + * REG_ADDR 0x00 (PHY_BCR: basic control register) + * PHY_DATA 0x8000 + * Reset - software reset + * LB (loop back) - normal + * SS (speed select) - 10 Mbit/s + * ANE (auto neg. enable) - enable + * PD (power down) - normal + * ISO (isolate) - normal + * RAN (restart auto neg.) - normal + * DM (duplex mode) - half duplex + * CT (collision test) - enable + */ + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0a09000); + + /* MDIO_ACC: + * RA (Request/Ack) 0x01 (Request) + * RW (Read/Write) 0x01 (Write) + * PHY_ADDR 0x06 (LAN) + * REG_ADDR 0x00 (PHY_BCR: basic control register) + * PHY_DATA 0x8000 + * Reset - software reset + * LB (loop back) - normal + * SS (speed select) - 10 Mbit/s + * ANE (auto neg. enable) - enable + * PD (power down) - normal + * ISO (isolate) - normal + * RAN (restart auto neg.) - normal + * DM (duplex mode) - half duplex + * CT (collision test) - enable + */ + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0c09000); + +#endif + + /* Make sure the CPU port is disabled for now. We + * don't want packets to get stacked for us until + * we enable DMA and are prepared to receive them. + */ + SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf); + + SW_READ_REG(INCA_IP_Switch_ARL_CTL, regValue); + + /* CRC GEN is enabled. + */ + regValue |= 0x00000200; + SW_WRITE_REG(INCA_IP_Switch_ARL_CTL, regValue); + + /* ADD TAG is disabled. + */ + SW_READ_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue); + regValue &= ~0x00000002; + SW_WRITE_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue); +} + + +static void inca_dma_init(void) +{ + /* Switch off all DMA channels. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF); + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR1, INCA_IP_DMA_DMA_RXCCR1_OFF); + + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF); + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR1, INCA_IP_DMA_DMA_TXCCR1_OFF); + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR2, INCA_IP_DMA_DMA_TXCCR2_OFF); + + /* Setup TX channel polling time. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXPOLL, INCA_DMA_TX_POLLING_TIME); + + /* Setup RX channel polling time. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXPOLL, INCA_DMA_RX_POLLING_TIME); + + /* ERRATA: write reset value into the DMA RX IMR register. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXIMR, 0xFFFFFFFF); + + /* Just in case: disable all transmit interrupts also. + */ + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXIMR, 0xFFFFFFFF); + + DMA_WRITE_REG(INCA_IP_DMA_DMA_TXISR, 0xFFFFFFFF); + DMA_WRITE_REG(INCA_IP_DMA_DMA_RXISR, 0xFFFFFFFF); +} + +#if defined(CONFIG_INCA_IP_SWITCH_AMDIX) +static int inca_amdix(void) +{ + u32 phyReg1 = 0; + u32 phyReg4 = 0; + u32 phyReg5 = 0; + u32 phyReg6 = 0; + u32 phyReg31 = 0; + u32 regEphy = 0; + int mdi_flag; + int retries; + + /* Setup GPIO pins. + */ + *INCA_IP_AUTO_MDIX_LAN_PORTS_DIR |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); + *INCA_IP_AUTO_MDIX_LAN_PORTS_ALTSEL |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); + +#if 0 + /* Wait for signal. + */ + retries = WAIT_SIGNAL_RETRIES; + while (--retries) { + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, + (0x1 << 31) | /* RA */ + (0x0 << 30) | /* Read */ + (0x6 << 21) | /* LAN */ + (17 << 16)); /* PHY_MCSR */ + do { + SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1); + } while (phyReg1 & (1 << 31)); + + if (phyReg1 & (1 << 1)) { + /* Signal detected */ + break; + } + } + + if (!retries) + goto Fail; +#endif + + /* Set MDI mode. + */ + *INCA_IP_AUTO_MDIX_LAN_PORTS_OUT &= ~(1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); + mdi_flag = 1; + + /* Wait for link. + */ + retries = WAIT_LINK_RETRIES; + while (--retries) { + udelay(LINK_RETRY_DELAY * 1000); + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, + (0x1 << 31) | /* RA */ + (0x0 << 30) | /* Read */ + (0x6 << 21) | /* LAN */ + (1 << 16)); /* PHY_BSR */ + do { + SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1); + } while (phyReg1 & (1 << 31)); + + if (phyReg1 & (1 << 2)) { + /* Link is up */ + break; + } else if (mdi_flag) { + /* Set MDIX mode */ + *INCA_IP_AUTO_MDIX_LAN_PORTS_OUT |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); + mdi_flag = 0; + } else { + /* Set MDI mode */ + *INCA_IP_AUTO_MDIX_LAN_PORTS_OUT &= ~(1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); + mdi_flag = 1; + } + } + + if (!retries) { + goto Fail; + } else { + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, + (0x1 << 31) | /* RA */ + (0x0 << 30) | /* Read */ + (0x6 << 21) | /* LAN */ + (1 << 16)); /* PHY_BSR */ + do { + SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1); + } while (phyReg1 & (1 << 31)); + + /* Auto-negotiation / Parallel detection complete + */ + if (phyReg1 & (1 << 5)) { + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, + (0x1 << 31) | /* RA */ + (0x0 << 30) | /* Read */ + (0x6 << 21) | /* LAN */ + (31 << 16)); /* PHY_SCSR */ + do { + SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg31); + } while (phyReg31 & (1 << 31)); + + switch ((phyReg31 >> 2) & 0x7) { + case INCA_SWITCH_PHY_SPEED_10H: + /* 10Base-T Half-duplex */ + regEphy = 0; + break; + case INCA_SWITCH_PHY_SPEED_10F: + /* 10Base-T Full-duplex */ + regEphy = INCA_IP_Switch_EPHY_DL; + break; + case INCA_SWITCH_PHY_SPEED_100H: + /* 100Base-TX Half-duplex */ + regEphy = INCA_IP_Switch_EPHY_SL; + break; + case INCA_SWITCH_PHY_SPEED_100F: + /* 100Base-TX Full-duplex */ + regEphy = INCA_IP_Switch_EPHY_SL | INCA_IP_Switch_EPHY_DL; + break; + } + + /* In case of Auto-negotiation, + * update the negotiated PAUSE support status + */ + if (phyReg1 & (1 << 3)) { + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, + (0x1 << 31) | /* RA */ + (0x0 << 30) | /* Read */ + (0x6 << 21) | /* LAN */ + (6 << 16)); /* PHY_ANER */ + do { + SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg6); + } while (phyReg6 & (1 << 31)); + + /* We are Autoneg-able. + * Is Link partner also able to autoneg? + */ + if (phyReg6 & (1 << 0)) { + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, + (0x1 << 31) | /* RA */ + (0x0 << 30) | /* Read */ + (0x6 << 21) | /* LAN */ + (4 << 16)); /* PHY_ANAR */ + do { + SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg4); + } while (phyReg4 & (1 << 31)); + + /* We advertise PAUSE capab. + * Does link partner also advertise it? + */ + if (phyReg4 & (1 << 10)) { + SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, + (0x1 << 31) | /* RA */ + (0x0 << 30) | /* Read */ + (0x6 << 21) | /* LAN */ + (5 << 16)); /* PHY_ANLPAR */ + do { + SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg5); + } while (phyReg5 & (1 << 31)); + + /* Link partner is PAUSE capab. + */ + if (phyReg5 & (1 << 10)) { + regEphy |= INCA_IP_Switch_EPHY_PL; + } + } + } + + } + + /* Link is up */ + regEphy |= INCA_IP_Switch_EPHY_LL; + + SW_WRITE_REG(INCA_IP_Switch_EPHY, regEphy); + } + } + + return 0; + +Fail: + printf("No Link on LAN port\n"); + return -1; +} +#endif /* CONFIG_INCA_IP_SWITCH_AMDIX */ diff --git a/roms/u-boot-sam460ex/drivers/net/kirkwood_egiga.c b/roms/u-boot-sam460ex/drivers/net/kirkwood_egiga.c new file mode 100644 index 000000000..932792e36 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/kirkwood_egiga.c @@ -0,0 +1,719 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * (C) Copyright 2003 + * Ingo Assmus <ingo.assmus@keymile.com> + * + * based on - Driver for MV64360X ethernet ports + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <net.h> +#include <malloc.h> +#include <miiphy.h> +#include <asm/errno.h> +#include <asm/types.h> +#include <asm/byteorder.h> +#include <asm/arch/kirkwood.h> +#include "kirkwood_egiga.h" + +#define KIRKWOOD_PHY_ADR_REQUEST 0xee +#define KWGBE_SMI_REG (((struct kwgbe_registers *)KW_EGIGA0_BASE)->smi) + +/* + * smi_reg_read - miiphy_read callback function. + * + * Returns 16bit phy register value, or 0xffff on error + */ +static int smi_reg_read(char *devname, u8 phy_adr, u8 reg_ofs, u16 * data) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct kwgbe_device *dkwgbe = to_dkwgbe(dev); + struct kwgbe_registers *regs = dkwgbe->regs; + u32 smi_reg; + u32 timeout; + + /* Phyadr read request */ + if (phy_adr == KIRKWOOD_PHY_ADR_REQUEST && + reg_ofs == KIRKWOOD_PHY_ADR_REQUEST) { + /* */ + *data = (u16) (KWGBEREG_RD(regs->phyadr) & PHYADR_MASK); + return 0; + } + /* check parameters */ + if (phy_adr > PHYADR_MASK) { + printf("Err..(%s) Invalid PHY address %d\n", + __FUNCTION__, phy_adr); + return -EFAULT; + } + if (reg_ofs > PHYREG_MASK) { + printf("Err..(%s) Invalid register offset %d\n", + __FUNCTION__, reg_ofs); + return -EFAULT; + } + + timeout = KWGBE_PHY_SMI_TIMEOUT; + /* wait till the SMI is not busy */ + do { + /* read smi register */ + smi_reg = KWGBEREG_RD(KWGBE_SMI_REG); + if (timeout-- == 0) { + printf("Err..(%s) SMI busy timeout\n", __FUNCTION__); + return -EFAULT; + } + } while (smi_reg & KWGBE_PHY_SMI_BUSY_MASK); + + /* fill the phy address and regiser offset and read opcode */ + smi_reg = (phy_adr << KWGBE_PHY_SMI_DEV_ADDR_OFFS) + | (reg_ofs << KWGBE_SMI_REG_ADDR_OFFS) + | KWGBE_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + KWGBEREG_WR(KWGBE_SMI_REG, smi_reg); + + /*wait till read value is ready */ + timeout = KWGBE_PHY_SMI_TIMEOUT; + + do { + /* read smi register */ + smi_reg = KWGBEREG_RD(KWGBE_SMI_REG); + if (timeout-- == 0) { + printf("Err..(%s) SMI read ready timeout\n", + __FUNCTION__); + return -EFAULT; + } + } while (!(smi_reg & KWGBE_PHY_SMI_READ_VALID_MASK)); + + /* Wait for the data to update in the SMI register */ + for (timeout = 0; timeout < KWGBE_PHY_SMI_TIMEOUT; timeout++) ; + + *data = (u16) (KWGBEREG_RD(KWGBE_SMI_REG) & KWGBE_PHY_SMI_DATA_MASK); + + debug("%s:(adr %d, off %d) value= %04x\n", __FUNCTION__, phy_adr, + reg_ofs, *data); + + return 0; +} + +/* + * smi_reg_write - imiiphy_write callback function. + * + * Returns 0 if write succeed, -EINVAL on bad parameters + * -ETIME on timeout + */ +static int smi_reg_write(char *devname, u8 phy_adr, u8 reg_ofs, u16 data) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct kwgbe_device *dkwgbe = to_dkwgbe(dev); + struct kwgbe_registers *regs = dkwgbe->regs; + u32 smi_reg; + u32 timeout; + + /* Phyadr write request*/ + if (phy_adr == KIRKWOOD_PHY_ADR_REQUEST && + reg_ofs == KIRKWOOD_PHY_ADR_REQUEST) { + KWGBEREG_WR(regs->phyadr, data); + return 0; + } + + /* check parameters */ + if (phy_adr > PHYADR_MASK) { + printf("Err..(%s) Invalid phy address\n", __FUNCTION__); + return -EINVAL; + } + if (reg_ofs > PHYREG_MASK) { + printf("Err..(%s) Invalid register offset\n", __FUNCTION__); + return -EINVAL; + } + + /* wait till the SMI is not busy */ + timeout = KWGBE_PHY_SMI_TIMEOUT; + do { + /* read smi register */ + smi_reg = KWGBEREG_RD(KWGBE_SMI_REG); + if (timeout-- == 0) { + printf("Err..(%s) SMI busy timeout\n", __FUNCTION__); + return -ETIME; + } + } while (smi_reg & KWGBE_PHY_SMI_BUSY_MASK); + + /* fill the phy addr and reg offset and write opcode and data */ + smi_reg = (data << KWGBE_PHY_SMI_DATA_OFFS); + smi_reg |= (phy_adr << KWGBE_PHY_SMI_DEV_ADDR_OFFS) + | (reg_ofs << KWGBE_SMI_REG_ADDR_OFFS); + smi_reg &= ~KWGBE_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + KWGBEREG_WR(KWGBE_SMI_REG, smi_reg); + + return 0; +} + +/* Stop and checks all queues */ +static void stop_queue(u32 * qreg) +{ + u32 reg_data; + + reg_data = readl(qreg); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + writel((reg_data << 8), qreg); + + /* Wait for all queue activity to terminate. */ + do { + /* + * Check port cause register that all queues + * are stopped + */ + reg_data = readl(qreg); + } + while (reg_data & 0xFF); + } +} + +/* + * set_access_control - Config address decode parameters for Ethernet unit + * + * This function configures the address decode parameters for the Gigabit + * Ethernet Controller according the given parameters struct. + * + * @regs Register struct pointer. + * @param Address decode parameter struct. + */ +static void set_access_control(struct kwgbe_registers *regs, + struct kwgbe_winparam *param) +{ + u32 access_prot_reg; + + /* Set access control register */ + access_prot_reg = KWGBEREG_RD(regs->epap); + /* clear window permission */ + access_prot_reg &= (~(3 << (param->win * 2))); + access_prot_reg |= (param->access_ctrl << (param->win * 2)); + KWGBEREG_WR(regs->epap, access_prot_reg); + + /* Set window Size reg (SR) */ + KWGBEREG_WR(regs->barsz[param->win].size, + (((param->size / 0x10000) - 1) << 16)); + + /* Set window Base address reg (BA) */ + KWGBEREG_WR(regs->barsz[param->win].bar, + (param->target | param->attrib | param->base_addr)); + /* High address remap reg (HARR) */ + if (param->win < 4) + KWGBEREG_WR(regs->ha_remap[param->win], param->high_addr); + + /* Base address enable reg (BARER) */ + if (param->enable == 1) + KWGBEREG_BITS_RESET(regs->bare, (1 << param->win)); + else + KWGBEREG_BITS_SET(regs->bare, (1 << param->win)); +} + +static void set_dram_access(struct kwgbe_registers *regs) +{ + struct kwgbe_winparam win_param; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + /* Set access parameters for DRAM bank i */ + win_param.win = i; /* Use Ethernet window i */ + /* Window target - DDR */ + win_param.target = KWGBE_TARGET_DRAM; + /* Enable full access */ + win_param.access_ctrl = EWIN_ACCESS_FULL; + win_param.high_addr = 0; + /* Get bank base */ + win_param.base_addr = kw_sdram_bar(i); + win_param.size = kw_sdram_bs(i); /* Get bank size */ + if (win_param.size == 0) + win_param.enable = 0; + else + win_param.enable = 1; /* Enable the access */ + + /* Enable DRAM bank */ + switch (i) { + case 0: + win_param.attrib = EBAR_DRAM_CS0; + break; + case 1: + win_param.attrib = EBAR_DRAM_CS1; + break; + case 2: + win_param.attrib = EBAR_DRAM_CS2; + break; + case 3: + win_param.attrib = EBAR_DRAM_CS3; + break; + default: + /* invalide bank, disable access */ + win_param.enable = 0; + win_param.attrib = 0; + break; + } + /* Set the access control for address window(EPAPR) RD/WR */ + set_access_control(regs, &win_param); + } +} + +/* + * port_init_mac_tables - Clear all entrance in the UC, SMC and OMC tables + * + * Go through all the DA filter tables (Unicast, Special Multicast & Other + * Multicast) and set each entry to 0. + */ +static void port_init_mac_tables(struct kwgbe_registers *regs) +{ + int table_index; + + /* Clear DA filter unicast table (Ex_dFUT) */ + for (table_index = 0; table_index < 4; ++table_index) + KWGBEREG_WR(regs->dfut[table_index], 0); + + for (table_index = 0; table_index < 64; ++table_index) { + /* Clear DA filter special multicast table (Ex_dFSMT) */ + KWGBEREG_WR(regs->dfsmt[table_index], 0); + /* Clear DA filter other multicast table (Ex_dFOMT) */ + KWGBEREG_WR(regs->dfomt[table_index], 0); + } +} + +/* + * port_uc_addr - This function Set the port unicast address table + * + * This function locates the proper entry in the Unicast table for the + * specified MAC nibble and sets its properties according to function + * parameters. + * This function add/removes MAC addresses from the port unicast address + * table. + * + * @uc_nibble Unicast MAC Address last nibble. + * @option 0 = Add, 1 = remove address. + * + * RETURN: 1 if output succeeded. 0 if option parameter is invalid. + */ +static int port_uc_addr(struct kwgbe_registers *regs, u8 uc_nibble, + int option) +{ + u32 unicast_reg; + u32 tbl_offset; + u32 reg_offset; + + /* Locate the Unicast table entry */ + uc_nibble = (0xf & uc_nibble); + /* Register offset from unicast table base */ + tbl_offset = (uc_nibble / 4); + /* Entry offset within the above register */ + reg_offset = uc_nibble % 4; + + switch (option) { + case REJECT_MAC_ADDR: + /* + * Clear accepts frame bit at specified unicast + * DA table entry + */ + unicast_reg = KWGBEREG_RD(regs->dfut[tbl_offset]); + unicast_reg &= (0xFF << (8 * reg_offset)); + KWGBEREG_WR(regs->dfut[tbl_offset], unicast_reg); + break; + case ACCEPT_MAC_ADDR: + /* Set accepts frame bit at unicast DA filter table entry */ + unicast_reg = KWGBEREG_RD(regs->dfut[tbl_offset]); + unicast_reg &= (0xFF << (8 * reg_offset)); + unicast_reg |= ((0x01 | (RXUQ << 1)) << (8 * reg_offset)); + KWGBEREG_WR(regs->dfut[tbl_offset], unicast_reg); + break; + default: + return 0; + } + return 1; +} + +/* + * port_uc_addr_set - This function Set the port Unicast address. + */ +static void port_uc_addr_set(struct kwgbe_registers *regs, u8 * p_addr) +{ + u32 mac_h; + u32 mac_l; + + mac_l = (p_addr[4] << 8) | (p_addr[5]); + mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) | (p_addr[2] << 8) | + (p_addr[3] << 0); + + KWGBEREG_WR(regs->macal, mac_l); + KWGBEREG_WR(regs->macah, mac_h); + + /* Accept frames of this address */ + port_uc_addr(regs, p_addr[5], ACCEPT_MAC_ADDR); +} + +/* + * kwgbe_init_rx_desc_ring - Curve a Rx chain desc list and buffer in memory. + */ +static void kwgbe_init_rx_desc_ring(struct kwgbe_device *dkwgbe) +{ + struct kwgbe_rxdesc *p_rx_desc; + int i; + + /* initialize the Rx descriptors ring */ + p_rx_desc = dkwgbe->p_rxdesc; + for (i = 0; i < RINGSZ; i++) { + p_rx_desc->cmd_sts = + KWGBE_BUFFER_OWNED_BY_DMA | KWGBE_RX_EN_INTERRUPT; + p_rx_desc->buf_size = PKTSIZE_ALIGN; + p_rx_desc->byte_cnt = 0; + p_rx_desc->buf_ptr = dkwgbe->p_rxbuf + i * PKTSIZE_ALIGN; + if (i == (RINGSZ - 1)) + p_rx_desc->nxtdesc_p = dkwgbe->p_rxdesc; + else { + p_rx_desc->nxtdesc_p = (struct kwgbe_rxdesc *) + ((u32) p_rx_desc + KW_RXQ_DESC_ALIGNED_SIZE); + p_rx_desc = p_rx_desc->nxtdesc_p; + } + } + dkwgbe->p_rxdesc_curr = dkwgbe->p_rxdesc; +} + +static int kwgbe_init(struct eth_device *dev) +{ + struct kwgbe_device *dkwgbe = to_dkwgbe(dev); + struct kwgbe_registers *regs = dkwgbe->regs; +#if (defined (CONFIG_MII) || defined (CONFIG_CMD_MII)) \ + && defined (CONFIG_SYS_FAULT_ECHO_LINK_DOWN) + int i; +#endif + /* setup RX rings */ + kwgbe_init_rx_desc_ring(dkwgbe); + + /* Clear the ethernet port interrupts */ + KWGBEREG_WR(regs->ic, 0); + KWGBEREG_WR(regs->ice, 0); + /* Unmask RX buffer and TX end interrupt */ + KWGBEREG_WR(regs->pim, INT_CAUSE_UNMASK_ALL); + /* Unmask phy and link status changes interrupts */ + KWGBEREG_WR(regs->peim, INT_CAUSE_UNMASK_ALL_EXT); + + set_dram_access(regs); + port_init_mac_tables(regs); + port_uc_addr_set(regs, dkwgbe->dev.enetaddr); + + /* Assign port configuration and command. */ + KWGBEREG_WR(regs->pxc, PRT_CFG_VAL); + KWGBEREG_WR(regs->pxcx, PORT_CFG_EXTEND_VALUE); + KWGBEREG_WR(regs->psc0, PORT_SERIAL_CONTROL_VALUE); + + /* Assign port SDMA configuration */ + KWGBEREG_WR(regs->sdc, PORT_SDMA_CFG_VALUE); + KWGBEREG_WR(regs->tqx[0].qxttbc, QTKNBKT_DEF_VAL); + KWGBEREG_WR(regs->tqx[0].tqxtbc, (QMTBS_DEF_VAL << 16) | QTKNRT_DEF_VAL); + /* Turn off the port/RXUQ bandwidth limitation */ + KWGBEREG_WR(regs->pmtu, 0); + + /* Set maximum receive buffer to 9700 bytes */ + KWGBEREG_WR(regs->psc0, KWGBE_MAX_RX_PACKET_9700BYTE + | (KWGBEREG_RD(regs->psc0) & MRU_MASK)); + + /* Enable port initially */ + KWGBEREG_BITS_SET(regs->psc0, KWGBE_SERIAL_PORT_EN); + + /* + * Set ethernet MTU for leaky bucket mechanism to 0 - this will + * disable the leaky bucket mechanism . + */ + KWGBEREG_WR(regs->pmtu, 0); + + /* Assignment of Rx CRDB of given RXUQ */ + KWGBEREG_WR(regs->rxcdp[RXUQ], (u32) dkwgbe->p_rxdesc_curr); + /* Enable port Rx. */ + KWGBEREG_WR(regs->rqc, (1 << RXUQ)); + +#if (defined (CONFIG_MII) || defined (CONFIG_CMD_MII)) \ + && defined (CONFIG_SYS_FAULT_ECHO_LINK_DOWN) + /* Wait up to 5s for the link status */ + for (i = 0; i < 5; i++) { + u16 phyadr; + + miiphy_read(dev->name, KIRKWOOD_PHY_ADR_REQUEST, + KIRKWOOD_PHY_ADR_REQUEST, &phyadr); + /* Return if we get link up */ + if (miiphy_link(dev->name, phyadr)) + return 0; + udelay(1000000); + } + + printf("No link on %s\n", dev->name); + return -1; +#endif + return 0; +} + +static int kwgbe_halt(struct eth_device *dev) +{ + struct kwgbe_device *dkwgbe = to_dkwgbe(dev); + struct kwgbe_registers *regs = dkwgbe->regs; + + /* Disable all gigE address decoder */ + KWGBEREG_WR(regs->bare, 0x3f); + + stop_queue(®s->tqc); + stop_queue(®s->rqc); + + /* Disable port */ + KWGBEREG_BITS_RESET(regs->psc0, KWGBE_SERIAL_PORT_EN); + /* Set port is not reset */ + KWGBEREG_BITS_RESET(regs->psc1, 1 << 4); +#ifdef CONFIG_SYS_MII_MODE + /* Set MMI interface up */ + KWGBEREG_BITS_RESET(regs->psc1, 1 << 3); +#endif + /* Disable & mask ethernet port interrupts */ + KWGBEREG_WR(regs->ic, 0); + KWGBEREG_WR(regs->ice, 0); + KWGBEREG_WR(regs->pim, 0); + KWGBEREG_WR(regs->peim, 0); + + return 0; +} + +static int kwgbe_write_hwaddr(struct eth_device *dev) +{ + struct kwgbe_device *dkwgbe = to_dkwgbe(dev); + struct kwgbe_registers *regs = dkwgbe->regs; + + /* Programs net device MAC address after initialization */ + port_uc_addr_set(regs, dkwgbe->dev.enetaddr); + return 0; +} + +static int kwgbe_send(struct eth_device *dev, volatile void *dataptr, + int datasize) +{ + struct kwgbe_device *dkwgbe = to_dkwgbe(dev); + struct kwgbe_registers *regs = dkwgbe->regs; + struct kwgbe_txdesc *p_txdesc = dkwgbe->p_txdesc; + void *p = (void *)dataptr; + u32 cmd_sts; + + /* Copy buffer if it's misaligned */ + if ((u32) dataptr & 0x07) { + if (datasize > PKTSIZE_ALIGN) { + printf("Non-aligned data too large (%d)\n", + datasize); + return -1; + } + + memcpy(dkwgbe->p_aligned_txbuf, p, datasize); + p = dkwgbe->p_aligned_txbuf; + } + + p_txdesc->cmd_sts = KWGBE_ZERO_PADDING | KWGBE_GEN_CRC; + p_txdesc->cmd_sts |= KWGBE_TX_FIRST_DESC | KWGBE_TX_LAST_DESC; + p_txdesc->cmd_sts |= KWGBE_BUFFER_OWNED_BY_DMA; + p_txdesc->cmd_sts |= KWGBE_TX_EN_INTERRUPT; + p_txdesc->buf_ptr = (u8 *) p; + p_txdesc->byte_cnt = datasize; + + /* Apply send command using zeroth TXUQ */ + KWGBEREG_WR(regs->tcqdp[TXUQ], (u32) p_txdesc); + KWGBEREG_WR(regs->tqc, (1 << TXUQ)); + + /* + * wait for packet xmit completion + */ + cmd_sts = readl(&p_txdesc->cmd_sts); + while (cmd_sts & KWGBE_BUFFER_OWNED_BY_DMA) { + /* return fail if error is detected */ + if ((cmd_sts & (KWGBE_ERROR_SUMMARY | KWGBE_TX_LAST_FRAME)) == + (KWGBE_ERROR_SUMMARY | KWGBE_TX_LAST_FRAME) && + cmd_sts & (KWGBE_UR_ERROR | KWGBE_RL_ERROR)) { + printf("Err..(%s) in xmit packet\n", __FUNCTION__); + return -1; + } + cmd_sts = readl(&p_txdesc->cmd_sts); + }; + return 0; +} + +static int kwgbe_recv(struct eth_device *dev) +{ + struct kwgbe_device *dkwgbe = to_dkwgbe(dev); + struct kwgbe_rxdesc *p_rxdesc_curr = dkwgbe->p_rxdesc_curr; + u32 cmd_sts; + u32 timeout = 0; + + /* wait untill rx packet available or timeout */ + do { + if (timeout < KWGBE_PHY_SMI_TIMEOUT) + timeout++; + else { + debug("%s time out...\n", __FUNCTION__); + return -1; + } + } while (readl(&p_rxdesc_curr->cmd_sts) & KWGBE_BUFFER_OWNED_BY_DMA); + + if (p_rxdesc_curr->byte_cnt != 0) { + debug("%s: Received %d byte Packet @ 0x%x (cmd_sts= %08x)\n", + __FUNCTION__, (u32) p_rxdesc_curr->byte_cnt, + (u32) p_rxdesc_curr->buf_ptr, + (u32) p_rxdesc_curr->cmd_sts); + } + + /* + * In case received a packet without first/last bits on + * OR the error summary bit is on, + * the packets needs to be dropeed. + */ + cmd_sts = readl(&p_rxdesc_curr->cmd_sts); + + if ((cmd_sts & + (KWGBE_RX_FIRST_DESC | KWGBE_RX_LAST_DESC)) + != (KWGBE_RX_FIRST_DESC | KWGBE_RX_LAST_DESC)) { + + printf("Err..(%s) Dropping packet spread on" + " multiple descriptors\n", __FUNCTION__); + + } else if (cmd_sts & KWGBE_ERROR_SUMMARY) { + + printf("Err..(%s) Dropping packet with errors\n", + __FUNCTION__); + + } else { + /* !!! call higher layer processing */ + debug("%s: Sending Received packet to" + " upper layer (NetReceive)\n", __FUNCTION__); + + /* let the upper layer handle the packet */ + NetReceive((p_rxdesc_curr->buf_ptr + RX_BUF_OFFSET), + (int)(p_rxdesc_curr->byte_cnt - RX_BUF_OFFSET)); + } + /* + * free these descriptors and point next in the ring + */ + p_rxdesc_curr->cmd_sts = + KWGBE_BUFFER_OWNED_BY_DMA | KWGBE_RX_EN_INTERRUPT; + p_rxdesc_curr->buf_size = PKTSIZE_ALIGN; + p_rxdesc_curr->byte_cnt = 0; + + writel((unsigned)p_rxdesc_curr->nxtdesc_p, (u32) &dkwgbe->p_rxdesc_curr); + + return 0; +} + +int kirkwood_egiga_initialize(bd_t * bis) +{ + struct kwgbe_device *dkwgbe; + struct eth_device *dev; + int devnum; + char *s; + u8 used_ports[MAX_KWGBE_DEVS] = CONFIG_KIRKWOOD_EGIGA_PORTS; + + for (devnum = 0; devnum < MAX_KWGBE_DEVS; devnum++) { + /*skip if port is configured not to use */ + if (used_ports[devnum] == 0) + continue; + + if (!(dkwgbe = malloc(sizeof(struct kwgbe_device)))) + goto error1; + + memset(dkwgbe, 0, sizeof(struct kwgbe_device)); + + if (!(dkwgbe->p_rxdesc = + (struct kwgbe_rxdesc *)memalign(PKTALIGN, + KW_RXQ_DESC_ALIGNED_SIZE + * RINGSZ + 1))) + goto error2; + + if (!(dkwgbe->p_rxbuf = (u8 *) memalign(PKTALIGN, RINGSZ + * PKTSIZE_ALIGN + 1))) + goto error3; + + if (!(dkwgbe->p_aligned_txbuf = memalign(8, PKTSIZE_ALIGN))) + goto error4; + + if (!(dkwgbe->p_txdesc = (struct kwgbe_txdesc *) + memalign(PKTALIGN, sizeof(struct kwgbe_txdesc) + 1))) { + free(dkwgbe->p_aligned_txbuf); + error4: + free(dkwgbe->p_rxbuf); + error3: + free(dkwgbe->p_rxdesc); + error2: + free(dkwgbe); + error1: + printf("Err.. %s Failed to allocate memory\n", + __FUNCTION__); + return -1; + } + + dev = &dkwgbe->dev; + + /* must be less than NAMESIZE (16) */ + sprintf(dev->name, "egiga%d", devnum); + + /* Extract the MAC address from the environment */ + switch (devnum) { + case 0: + dkwgbe->regs = (void *)KW_EGIGA0_BASE; + s = "ethaddr"; + break; + case 1: + dkwgbe->regs = (void *)KW_EGIGA1_BASE; + s = "eth1addr"; + break; + default: /* this should never happen */ + printf("Err..(%s) Invalid device number %d\n", + __FUNCTION__, devnum); + return -1; + } + + while (!eth_getenv_enetaddr(s, dev->enetaddr)) { + /* Generate Random Private MAC addr if not set */ + dev->enetaddr[0] = 0x02; + dev->enetaddr[1] = 0x50; + dev->enetaddr[2] = 0x43; + dev->enetaddr[3] = get_random_hex(); + dev->enetaddr[4] = get_random_hex(); + dev->enetaddr[5] = get_random_hex(); + eth_setenv_enetaddr(s, dev->enetaddr); + } + + dev->init = (void *)kwgbe_init; + dev->halt = (void *)kwgbe_halt; + dev->send = (void *)kwgbe_send; + dev->recv = (void *)kwgbe_recv; + dev->write_hwaddr = (void *)kwgbe_write_hwaddr; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, smi_reg_read, smi_reg_write); + /* Set phy address of the port */ + miiphy_write(dev->name, KIRKWOOD_PHY_ADR_REQUEST, + KIRKWOOD_PHY_ADR_REQUEST, PHY_BASE_ADR + devnum); +#endif + } + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/kirkwood_egiga.h b/roms/u-boot-sam460ex/drivers/net/kirkwood_egiga.h new file mode 100644 index 000000000..30c773ca5 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/kirkwood_egiga.h @@ -0,0 +1,505 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * based on - Driver for MV64360X ethernet ports + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __EGIGA_H__ +#define __EGIGA_H__ + +#define MAX_KWGBE_DEVS 2 /*controller has two ports */ + +/* PHY_BASE_ADR is board specific and can be configured */ +#if defined (CONFIG_PHY_BASE_ADR) +#define PHY_BASE_ADR CONFIG_PHY_BASE_ADR +#else +#define PHY_BASE_ADR 0x08 /* default phy base addr */ +#endif + +/* Constants */ +#define INT_CAUSE_UNMASK_ALL 0x0007ffff +#define INT_CAUSE_UNMASK_ALL_EXT 0x0011ffff +#define MRU_MASK 0xfff1ffff +#define PHYADR_MASK 0x0000001f +#define PHYREG_MASK 0x0000001f +#define QTKNBKT_DEF_VAL 0x3fffffff +#define QMTBS_DEF_VAL 0x000003ff +#define QTKNRT_DEF_VAL 0x0000fcff +#define RXUQ 0 /* Used Rx queue */ +#define TXUQ 0 /* Used Rx queue */ + +#define to_dkwgbe(_kd) container_of(_kd, struct kwgbe_device, dev) +#define KWGBEREG_WR(adr, val) writel(val, &adr) +#define KWGBEREG_RD(adr) readl(&adr) +#define KWGBEREG_BITS_RESET(adr, val) writel(readl(&adr) & ~(val), &adr) +#define KWGBEREG_BITS_SET(adr, val) writel(readl(&adr) | val, &adr) + +/* Default port configuration value */ +#define PRT_CFG_VAL ( \ + KWGBE_UCAST_MOD_NRML | \ + KWGBE_DFLT_RXQ(RXUQ) | \ + KWGBE_DFLT_RX_ARPQ(RXUQ) | \ + KWGBE_RX_BC_IF_NOT_IP_OR_ARP | \ + KWGBE_RX_BC_IF_IP | \ + KWGBE_RX_BC_IF_ARP | \ + KWGBE_CPTR_TCP_FRMS_DIS | \ + KWGBE_CPTR_UDP_FRMS_DIS | \ + KWGBE_DFLT_RX_TCPQ(RXUQ) | \ + KWGBE_DFLT_RX_UDPQ(RXUQ) | \ + KWGBE_DFLT_RX_BPDUQ(RXUQ)) + +/* Default port extend configuration value */ +#define PORT_CFG_EXTEND_VALUE \ + KWGBE_SPAN_BPDU_PACKETS_AS_NORMAL | \ + KWGBE_PARTITION_DIS | \ + KWGBE_TX_CRC_GENERATION_EN + +#define GT_KWGBE_IPG_INT_RX(value) ((value & 0x3fff) << 8) + +/* Default sdma control value */ +#define PORT_SDMA_CFG_VALUE ( \ + KWGBE_RX_BURST_SIZE_16_64BIT | \ + KWGBE_BLM_RX_NO_SWAP | \ + KWGBE_BLM_TX_NO_SWAP | \ + GT_KWGBE_IPG_INT_RX(RXUQ) | \ + KWGBE_TX_BURST_SIZE_16_64BIT) + +/* Default port serial control value */ +#define PORT_SERIAL_CONTROL_VALUE ( \ + KWGBE_FORCE_LINK_PASS | \ + KWGBE_DIS_AUTO_NEG_FOR_DUPLX | \ + KWGBE_DIS_AUTO_NEG_FOR_FLOW_CTRL | \ + KWGBE_ADV_NO_FLOW_CTRL | \ + KWGBE_FORCE_FC_MODE_NO_PAUSE_DIS_TX | \ + KWGBE_FORCE_BP_MODE_NO_JAM | \ + (1 << 9) /* Reserved bit has to be 1 */ | \ + KWGBE_DO_NOT_FORCE_LINK_FAIL | \ + KWGBE_EN_AUTO_NEG_SPEED_GMII | \ + KWGBE_DTE_ADV_0 | \ + KWGBE_MIIPHY_MAC_MODE | \ + KWGBE_AUTO_NEG_NO_CHANGE | \ + KWGBE_MAX_RX_PACKET_1552BYTE | \ + KWGBE_CLR_EXT_LOOPBACK | \ + KWGBE_SET_FULL_DUPLEX_MODE | \ + KWGBE_DIS_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX) + +/* Tx WRR confoguration macros */ +#define PORT_MAX_TRAN_UNIT 0x24 /* MTU register (default) 9KByte */ +#define PORT_MAX_TOKEN_BUCKET_SIZE 0x_FFFF /* PMTBS reg (default) */ +#define PORT_TOKEN_RATE 1023 /* PTTBRC reg (default) */ +/* MAC accepet/reject macros */ +#define ACCEPT_MAC_ADDR 0 +#define REJECT_MAC_ADDR 1 +/* Size of a Tx/Rx descriptor used in chain list data structure */ +#define KW_RXQ_DESC_ALIGNED_SIZE \ + (((sizeof(struct kwgbe_rxdesc) / PKTALIGN) + 1) * PKTALIGN) +/* Buffer offset from buffer pointer */ +#define RX_BUF_OFFSET 0x2 + +/* Port serial status reg (PSR) */ +#define KWGBE_INTERFACE_GMII_MII 0 +#define KWGBE_INTERFACE_PCM 1 +#define KWGBE_LINK_IS_DOWN 0 +#define KWGBE_LINK_IS_UP (1 << 1) +#define KWGBE_PORT_AT_HALF_DUPLEX 0 +#define KWGBE_PORT_AT_FULL_DUPLEX (1 << 2) +#define KWGBE_RX_FLOW_CTRL_DISD 0 +#define KWGBE_RX_FLOW_CTRL_ENBALED (1 << 3) +#define KWGBE_GMII_SPEED_100_10 0 +#define KWGBE_GMII_SPEED_1000 (1 << 4) +#define KWGBE_MII_SPEED_10 0 +#define KWGBE_MII_SPEED_100 (1 << 5) +#define KWGBE_NO_TX 0 +#define KWGBE_TX_IN_PROGRESS (1 << 7) +#define KWGBE_BYPASS_NO_ACTIVE 0 +#define KWGBE_BYPASS_ACTIVE (1 << 8) +#define KWGBE_PORT_NOT_AT_PARTN_STT 0 +#define KWGBE_PORT_AT_PARTN_STT (1 << 9) +#define KWGBE_PORT_TX_FIFO_NOT_EMPTY 0 +#define KWGBE_PORT_TX_FIFO_EMPTY (1 << 10) + +/* These macros describes the Port configuration reg (Px_cR) bits */ +#define KWGBE_UCAST_MOD_NRML 0 +#define KWGBE_UNICAST_PROMISCUOUS_MODE 1 +#define KWGBE_DFLT_RXQ(_x) (_x << 1) +#define KWGBE_DFLT_RX_ARPQ(_x) (_x << 4) +#define KWGBE_RX_BC_IF_NOT_IP_OR_ARP 0 +#define KWGBE_REJECT_BC_IF_NOT_IP_OR_ARP (1 << 7) +#define KWGBE_RX_BC_IF_IP 0 +#define KWGBE_REJECT_BC_IF_IP (1 << 8) +#define KWGBE_RX_BC_IF_ARP 0 +#define KWGBE_REJECT_BC_IF_ARP (1 << 9) +#define KWGBE_TX_AM_NO_UPDATE_ERR_SMRY (1 << 12) +#define KWGBE_CPTR_TCP_FRMS_DIS 0 +#define KWGBE_CPTR_TCP_FRMS_EN (1 << 14) +#define KWGBE_CPTR_UDP_FRMS_DIS 0 +#define KWGBE_CPTR_UDP_FRMS_EN (1 << 15) +#define KWGBE_DFLT_RX_TCPQ(_x) (_x << 16) +#define KWGBE_DFLT_RX_UDPQ(_x) (_x << 19) +#define KWGBE_DFLT_RX_BPDUQ(_x) (_x << 22) +#define KWGBE_DFLT_RX_TCP_CHKSUM_MODE (1 << 25) + +/* These macros describes the Port configuration extend reg (Px_cXR) bits*/ +#define KWGBE_CLASSIFY_EN 1 +#define KWGBE_SPAN_BPDU_PACKETS_AS_NORMAL 0 +#define KWGBE_SPAN_BPDU_PACKETS_TO_RX_Q7 (1 << 1) +#define KWGBE_PARTITION_DIS 0 +#define KWGBE_PARTITION_EN (1 << 2) +#define KWGBE_TX_CRC_GENERATION_EN 0 +#define KWGBE_TX_CRC_GENERATION_DIS (1 << 3) + +/* These macros describes the Port Sdma configuration reg (SDCR) bits */ +#define KWGBE_RIFB 1 +#define KWGBE_RX_BURST_SIZE_1_64BIT 0 +#define KWGBE_RX_BURST_SIZE_2_64BIT (1 << 1) +#define KWGBE_RX_BURST_SIZE_4_64BIT (1 << 2) +#define KWGBE_RX_BURST_SIZE_8_64BIT ((1 << 2) | (1 << 1)) +#define KWGBE_RX_BURST_SIZE_16_64BIT (1 << 3) +#define KWGBE_BLM_RX_NO_SWAP (1 << 4) +#define KWGBE_BLM_RX_BYTE_SWAP 0 +#define KWGBE_BLM_TX_NO_SWAP (1 << 5) +#define KWGBE_BLM_TX_BYTE_SWAP 0 +#define KWGBE_DESCRIPTORS_BYTE_SWAP (1 << 6) +#define KWGBE_DESCRIPTORS_NO_SWAP 0 +#define KWGBE_TX_BURST_SIZE_1_64BIT 0 +#define KWGBE_TX_BURST_SIZE_2_64BIT (1 << 22) +#define KWGBE_TX_BURST_SIZE_4_64BIT (1 << 23) +#define KWGBE_TX_BURST_SIZE_8_64BIT ((1 << 23) | (1 << 22)) +#define KWGBE_TX_BURST_SIZE_16_64BIT (1 << 24) + +/* These macros describes the Port serial control reg (PSCR) bits */ +#define KWGBE_SERIAL_PORT_DIS 0 +#define KWGBE_SERIAL_PORT_EN 1 +#define KWGBE_FORCE_LINK_PASS (1 << 1) +#define KWGBE_DO_NOT_FORCE_LINK_PASS 0 +#define KWGBE_EN_AUTO_NEG_FOR_DUPLX 0 +#define KWGBE_DIS_AUTO_NEG_FOR_DUPLX (1 << 2) +#define KWGBE_EN_AUTO_NEG_FOR_FLOW_CTRL 0 +#define KWGBE_DIS_AUTO_NEG_FOR_FLOW_CTRL (1 << 3) +#define KWGBE_ADV_NO_FLOW_CTRL 0 +#define KWGBE_ADV_SYMMETRIC_FLOW_CTRL (1 << 4) +#define KWGBE_FORCE_FC_MODE_NO_PAUSE_DIS_TX 0 +#define KWGBE_FORCE_FC_MODE_TX_PAUSE_DIS (1 << 5) +#define KWGBE_FORCE_BP_MODE_NO_JAM 0 +#define KWGBE_FORCE_BP_MODE_JAM_TX (1 << 7) +#define KWGBE_FORCE_BP_MODE_JAM_TX_ON_RX_ERR (1 << 8) +#define KWGBE_FORCE_LINK_FAIL 0 +#define KWGBE_DO_NOT_FORCE_LINK_FAIL (1 << 10) +#define KWGBE_DIS_AUTO_NEG_SPEED_GMII (1 << 13) +#define KWGBE_EN_AUTO_NEG_SPEED_GMII 0 +#define KWGBE_DTE_ADV_0 0 +#define KWGBE_DTE_ADV_1 (1 << 14) +#define KWGBE_MIIPHY_MAC_MODE 0 +#define KWGBE_MIIPHY_PHY_MODE (1 << 15) +#define KWGBE_AUTO_NEG_NO_CHANGE 0 +#define KWGBE_RESTART_AUTO_NEG (1 << 16) +#define KWGBE_MAX_RX_PACKET_1518BYTE 0 +#define KWGBE_MAX_RX_PACKET_1522BYTE (1 << 17) +#define KWGBE_MAX_RX_PACKET_1552BYTE (1 << 18) +#define KWGBE_MAX_RX_PACKET_9022BYTE ((1 << 18) | (1 << 17)) +#define KWGBE_MAX_RX_PACKET_9192BYTE (1 << 19) +#define KWGBE_MAX_RX_PACKET_9700BYTE ((1 << 19) | (1 << 17)) +#define KWGBE_SET_EXT_LOOPBACK (1 << 20) +#define KWGBE_CLR_EXT_LOOPBACK 0 +#define KWGBE_SET_FULL_DUPLEX_MODE (1 << 21) +#define KWGBE_SET_HALF_DUPLEX_MODE 0 +#define KWGBE_EN_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX (1 << 22) +#define KWGBE_DIS_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX 0 +#define KWGBE_SET_GMII_SPEED_TO_10_100 0 +#define KWGBE_SET_GMII_SPEED_TO_1000 (1 << 23) +#define KWGBE_SET_MII_SPEED_TO_10 0 +#define KWGBE_SET_MII_SPEED_TO_100 (1 << 24) + +/* SMI register fields */ +#define KWGBE_PHY_SMI_TIMEOUT 10000 +#define KWGBE_PHY_SMI_DATA_OFFS 0 /* Data */ +#define KWGBE_PHY_SMI_DATA_MASK (0xffff << KWGBE_PHY_SMI_DATA_OFFS) +#define KWGBE_PHY_SMI_DEV_ADDR_OFFS 16 /* PHY device address */ +#define KWGBE_PHY_SMI_DEV_ADDR_MASK (PHYADR_MASK << KWGBE_PHY_SMI_DEV_ADDR_OFFS) +#define KWGBE_SMI_REG_ADDR_OFFS 21 /* PHY device reg addr */ +#define KWGBE_SMI_REG_ADDR_MASK (PHYADR_MASK << KWGBE_SMI_REG_ADDR_OFFS) +#define KWGBE_PHY_SMI_OPCODE_OFFS 26 /* Write/Read opcode */ +#define KWGBE_PHY_SMI_OPCODE_MASK (3 << KWGBE_PHY_SMI_OPCODE_OFFS) +#define KWGBE_PHY_SMI_OPCODE_WRITE (0 << KWGBE_PHY_SMI_OPCODE_OFFS) +#define KWGBE_PHY_SMI_OPCODE_READ (1 << KWGBE_PHY_SMI_OPCODE_OFFS) +#define KWGBE_PHY_SMI_READ_VALID_MASK (1 << 27) /* Read Valid */ +#define KWGBE_PHY_SMI_BUSY_MASK (1 << 28) /* Busy */ + +/* SDMA command status fields macros */ +/* Tx & Rx descriptors status */ +#define KWGBE_ERROR_SUMMARY 1 +/* Tx & Rx descriptors command */ +#define KWGBE_BUFFER_OWNED_BY_DMA (1 << 31) +/* Tx descriptors status */ +#define KWGBE_LC_ERROR 0 +#define KWGBE_UR_ERROR (1 << 1) +#define KWGBE_RL_ERROR (1 << 2) +#define KWGBE_LLC_SNAP_FORMAT (1 << 9) +#define KWGBE_TX_LAST_FRAME (1 << 20) + +/* Rx descriptors status */ +#define KWGBE_CRC_ERROR 0 +#define KWGBE_OVERRUN_ERROR (1 << 1) +#define KWGBE_MAX_FRAME_LENGTH_ERROR (1 << 2) +#define KWGBE_RESOURCE_ERROR ((1 << 2) | (1 << 1)) +#define KWGBE_VLAN_TAGGED (1 << 19) +#define KWGBE_BPDU_FRAME (1 << 20) +#define KWGBE_TCP_FRAME_OVER_IP_V_4 0 +#define KWGBE_UDP_FRAME_OVER_IP_V_4 (1 << 21) +#define KWGBE_OTHER_FRAME_TYPE (1 << 22) +#define KWGBE_LAYER_2_IS_KWGBE_V_2 (1 << 23) +#define KWGBE_FRAME_TYPE_IP_V_4 (1 << 24) +#define KWGBE_FRAME_HEADER_OK (1 << 25) +#define KWGBE_RX_LAST_DESC (1 << 26) +#define KWGBE_RX_FIRST_DESC (1 << 27) +#define KWGBE_UNKNOWN_DESTINATION_ADDR (1 << 28) +#define KWGBE_RX_EN_INTERRUPT (1 << 29) +#define KWGBE_LAYER_4_CHECKSUM_OK (1 << 30) + +/* Rx descriptors byte count */ +#define KWGBE_FRAME_FRAGMENTED (1 << 2) + +/* Tx descriptors command */ +#define KWGBE_LAYER_4_CHECKSUM_FIRST_DESC (1 << 10) +#define KWGBE_FRAME_SET_TO_VLAN (1 << 15) +#define KWGBE_TCP_FRAME 0 +#define KWGBE_UDP_FRAME (1 << 16) +#define KWGBE_GEN_TCP_UDP_CHECKSUM (1 << 17) +#define KWGBE_GEN_IP_V_4_CHECKSUM (1 << 18) +#define KWGBE_ZERO_PADDING (1 << 19) +#define KWGBE_TX_LAST_DESC (1 << 20) +#define KWGBE_TX_FIRST_DESC (1 << 21) +#define KWGBE_GEN_CRC (1 << 22) +#define KWGBE_TX_EN_INTERRUPT (1 << 23) +#define KWGBE_AUTO_MODE (1 << 30) + +/* Address decode parameters */ +/* Ethernet Base Address Register bits */ +#define EBAR_TARGET_DRAM 0x00000000 +#define EBAR_TARGET_DEVICE 0x00000001 +#define EBAR_TARGET_CBS 0x00000002 +#define EBAR_TARGET_PCI0 0x00000003 +#define EBAR_TARGET_PCI1 0x00000004 +#define EBAR_TARGET_CUNIT 0x00000005 +#define EBAR_TARGET_AUNIT 0x00000006 +#define EBAR_TARGET_GUNIT 0x00000007 + +/* Window attrib */ +#define EBAR_DRAM_CS0 0x00000E00 +#define EBAR_DRAM_CS1 0x00000D00 +#define EBAR_DRAM_CS2 0x00000B00 +#define EBAR_DRAM_CS3 0x00000700 + +/* DRAM Target interface */ +#define EBAR_DRAM_NO_CACHE_COHERENCY 0x00000000 +#define EBAR_DRAM_CACHE_COHERENCY_WT 0x00001000 +#define EBAR_DRAM_CACHE_COHERENCY_WB 0x00002000 + +/* Device Bus Target interface */ +#define EBAR_DEVICE_DEVCS0 0x00001E00 +#define EBAR_DEVICE_DEVCS1 0x00001D00 +#define EBAR_DEVICE_DEVCS2 0x00001B00 +#define EBAR_DEVICE_DEVCS3 0x00001700 +#define EBAR_DEVICE_BOOTCS3 0x00000F00 + +/* PCI Target interface */ +#define EBAR_PCI_BYTE_SWAP 0x00000000 +#define EBAR_PCI_NO_SWAP 0x00000100 +#define EBAR_PCI_BYTE_WORD_SWAP 0x00000200 +#define EBAR_PCI_WORD_SWAP 0x00000300 +#define EBAR_PCI_NO_SNOOP_NOT_ASSERT 0x00000000 +#define EBAR_PCI_NO_SNOOP_ASSERT 0x00000400 +#define EBAR_PCI_IO_SPACE 0x00000000 +#define EBAR_PCI_MEMORY_SPACE 0x00000800 +#define EBAR_PCI_REQ64_FORCE 0x00000000 +#define EBAR_PCI_REQ64_SIZE 0x00001000 + +/* Window access control */ +#define EWIN_ACCESS_NOT_ALLOWED 0 +#define EWIN_ACCESS_READ_ONLY 1 +#define EWIN_ACCESS_FULL ((1 << 1) | 1) + +/* structures represents Controller registers */ +struct kwgbe_barsz { + u32 bar; + u32 size; +}; + +struct kwgbe_rxcdp { + struct kwgbe_rxdesc *rxcdp; + u32 rxcdp_pad[3]; +}; + +struct kwgbe_tqx { + u32 qxttbc; + u32 tqxtbc; + u32 tqxac; + u32 tqxpad; +}; + +struct kwgbe_registers { + u32 phyadr; + u32 smi; + u32 euda; + u32 eudid; + u8 pad1[0x080 - 0x00c - 4]; + u32 euic; + u32 euim; + u8 pad2[0x094 - 0x084 - 4]; + u32 euea; + u32 euiae; + u8 pad3[0x0b0 - 0x098 - 4]; + u32 euc; + u8 pad3a[0x200 - 0x0b0 - 4]; + struct kwgbe_barsz barsz[6]; + u8 pad4[0x280 - 0x22c - 4]; + u32 ha_remap[4]; + u32 bare; + u32 epap; + u8 pad5[0x400 - 0x294 - 4]; + u32 pxc; + u32 pxcx; + u32 mii_ser_params; + u8 pad6[0x410 - 0x408 - 4]; + u32 evlane; + u32 macal; + u32 macah; + u32 sdc; + u32 dscp[7]; + u32 psc0; + u32 vpt2p; + u32 ps0; + u32 tqc; + u32 psc1; + u32 ps1; + u32 mrvl_header; + u8 pad7[0x460 - 0x454 - 4]; + u32 ic; + u32 ice; + u32 pim; + u32 peim; + u8 pad8[0x474 - 0x46c - 4]; + u32 pxtfut; + u32 pad9; + u32 pxmfs; + u32 pad10; + u32 pxdfc; + u32 pxofc; + u8 pad11[0x494 - 0x488 - 4]; + u32 peuiae; + u8 pad12[0x4bc - 0x494 - 4]; + u32 eth_type_prio; + u8 pad13[0x4dc - 0x4bc - 4]; + u32 tqfpc; + u32 pttbrc; + u32 tqc1; + u32 pmtu; + u32 pmtbs; + u8 pad14[0x60c - 0x4ec - 4]; + struct kwgbe_rxcdp rxcdp[7]; + struct kwgbe_rxdesc *rxcdp7; + u32 rqc; + struct kwgbe_txdesc *tcsdp; + u8 pad15[0x6c0 - 0x684 - 4]; + struct kwgbe_txdesc *tcqdp[8]; + u8 pad16[0x700 - 0x6dc - 4]; + struct kwgbe_tqx tqx[8]; + u32 pttbc; + u8 pad17[0x7a8 - 0x780 - 4]; + u32 tqxipg0; + u32 pad18[3]; + u32 tqxipg1; + u8 pad19[0x7c0 - 0x7b8 - 4]; + u32 hitkninlopkt; + u32 hitkninasyncpkt; + u32 lotkninasyncpkt; + u32 pad20; + u32 ts; + u8 pad21[0x3000 - 0x27d0 - 4]; + u32 pad20_1[32]; /* mib counter registes */ + u8 pad22[0x3400 - 0x3000 - sizeof(u32) * 32]; + u32 dfsmt[64]; + u32 dfomt[64]; + u32 dfut[4]; + u8 pad23[0xe20c0 - 0x7360c - 4]; + u32 pmbus_top_arbiter; +}; + +/* structures/enums needed by driver */ +enum kwgbe_adrwin { + KWGBE_WIN0, + KWGBE_WIN1, + KWGBE_WIN2, + KWGBE_WIN3, + KWGBE_WIN4, + KWGBE_WIN5 +}; + +enum kwgbe_target { + KWGBE_TARGET_DRAM, + KWGBE_TARGET_DEV, + KWGBE_TARGET_CBS, + KWGBE_TARGET_PCI0, + KWGBE_TARGET_PCI1 +}; + +struct kwgbe_winparam { + enum kwgbe_adrwin win; /* Window number */ + enum kwgbe_target target; /* System targets */ + u16 attrib; /* BAR attrib. See above macros */ + u32 base_addr; /* Window base address in u32 form */ + u32 high_addr; /* Window high address in u32 form */ + u32 size; /* Size in MBytes. Must be % 64Kbyte. */ + int enable; /* Enable/disable access to the window. */ + u16 access_ctrl; /*Access ctrl register. see above macros */ +}; + +struct kwgbe_rxdesc { + u32 cmd_sts; /* Descriptor command status */ + u16 buf_size; /* Buffer size */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u8 *buf_ptr; /* Descriptor buffer pointer */ + struct kwgbe_rxdesc *nxtdesc_p; /* Next descriptor pointer */ +}; + +struct kwgbe_txdesc { + u32 cmd_sts; /* Descriptor command status */ + u16 l4i_chk; /* CPU provided TCP Checksum */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u8 *buf_ptr; /* Descriptor buffer ptr */ + struct kwgbe_txdesc *nxtdesc_p; /* Next descriptor ptr */ +}; + +/* port device data struct */ +struct kwgbe_device { + struct eth_device dev; + struct kwgbe_registers *regs; + struct kwgbe_txdesc *p_txdesc; + struct kwgbe_rxdesc *p_rxdesc; + struct kwgbe_rxdesc *p_rxdesc_curr; + u8 *p_rxbuf; + u8 *p_aligned_txbuf; +}; + +#endif /* __EGIGA_H__ */ diff --git a/roms/u-boot-sam460ex/drivers/net/ks8695eth.c b/roms/u-boot-sam460ex/drivers/net/ks8695eth.c new file mode 100644 index 000000000..5ea6e7fda --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ks8695eth.c @@ -0,0 +1,228 @@ +/* + * ks8695eth.c -- KS8695 ethernet driver + * + * (C) Copyright 2004-2005, Greg Ungerer <greg.ungerer@opengear.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/****************************************************************************/ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <asm/io.h> +#include <asm/arch/platform.h> + +/****************************************************************************/ + +/* + * Hardware register access to the KS8695 LAN ethernet port + * (well, it is the 4 port switch really). + */ +#define ks8695_read(a) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) +#define ks8695_write(a,v) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) = (v) + +/****************************************************************************/ + +/* + * Define the descriptor in-memory data structures. + */ +struct ks8695_txdesc { + uint32_t owner; + uint32_t ctrl; + uint32_t addr; + uint32_t next; +}; + +struct ks8695_rxdesc { + uint32_t status; + uint32_t ctrl; + uint32_t addr; + uint32_t next; +}; + +/****************************************************************************/ + +/* + * Allocate local data structures to use for receiving and sending + * packets. Just to keep it all nice and simple. + */ + +#define TXDESCS 4 +#define RXDESCS 4 +#define BUFSIZE 2048 + +volatile struct ks8695_txdesc ks8695_tx[TXDESCS] __attribute__((aligned(256))); +volatile struct ks8695_rxdesc ks8695_rx[RXDESCS] __attribute__((aligned(256))); +volatile uint8_t ks8695_bufs[BUFSIZE*(TXDESCS+RXDESCS)] __attribute__((aligned(2048)));; + +/****************************************************************************/ + +/* + * Ideally we want to use the MAC address stored in flash. + * But we do some sanity checks in case they are not present + * first. + */ +unsigned char eth_mac[] = { + 0x00, 0x13, 0xc6, 0x00, 0x00, 0x00 +}; + +void ks8695_getmac(void) +{ + unsigned char *fp; + int i; + + /* Check if flash MAC is valid */ + fp = (unsigned char *) 0x0201c000; + for (i = 0; (i < 6); i++) { + if ((fp[i] != 0) && (fp[i] != 0xff)) + break; + } + + /* If we found a valid looking MAC address then use it */ + if (i < 6) + memcpy(ð_mac[0], fp, 6); +} + +/****************************************************************************/ + +void eth_reset(bd_t *bd) +{ + int i; + + debug ("%s(%d): eth_reset()\n", __FILE__, __LINE__); + + /* Reset the ethernet engines first */ + ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); + ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); + + ks8695_getmac(); + + /* Set MAC address */ + ks8695_write(KS8695_LAN_MAC_LOW, (eth_mac[5] | (eth_mac[4] << 8) | + (eth_mac[3] << 16) | (eth_mac[2] << 24))); + ks8695_write(KS8695_LAN_MAC_HIGH, (eth_mac[1] | (eth_mac[0] << 8))); + + /* Turn the 4 port switch on */ + i = ks8695_read(KS8695_SWITCH_CTRL0); + ks8695_write(KS8695_SWITCH_CTRL0, (i | 0x1)); + /* ks8695_write(KS8695_WAN_CONTROL, 0x3f000066); */ + + /* Initialize descriptor rings */ + for (i = 0; (i < TXDESCS); i++) { + ks8695_tx[i].owner = 0; + ks8695_tx[i].ctrl = 0; + ks8695_tx[i].addr = (uint32_t) &ks8695_bufs[i*BUFSIZE]; + ks8695_tx[i].next = (uint32_t) &ks8695_tx[i+1]; + } + ks8695_tx[TXDESCS-1].ctrl = 0x02000000; + ks8695_tx[TXDESCS-1].next = (uint32_t) &ks8695_tx[0]; + + for (i = 0; (i < RXDESCS); i++) { + ks8695_rx[i].status = 0x80000000; + ks8695_rx[i].ctrl = BUFSIZE - 4; + ks8695_rx[i].addr = (uint32_t) &ks8695_bufs[(i+TXDESCS)*BUFSIZE]; + ks8695_rx[i].next = (uint32_t) &ks8695_rx[i+1]; + } + ks8695_rx[RXDESCS-1].ctrl |= 0x00080000; + ks8695_rx[RXDESCS-1].next = (uint32_t) &ks8695_rx[0]; + + /* The KS8695 is pretty slow reseting the ethernets... */ + udelay(2000000); + + /* Enable the ethernet engine */ + ks8695_write(KS8695_LAN_TX_LIST, (uint32_t) &ks8695_tx[0]); + ks8695_write(KS8695_LAN_RX_LIST, (uint32_t) &ks8695_rx[0]); + ks8695_write(KS8695_LAN_DMA_TX, 0x3); + ks8695_write(KS8695_LAN_DMA_RX, 0x71); + ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); + + printf("KS8695 ETHERNET: %pM\n", eth_mac); +} + +/****************************************************************************/ + +int eth_init(bd_t *bd) +{ + debug ("%s(%d): eth_init()\n", __FILE__, __LINE__); + + eth_reset(bd); + return 0; +} + +/****************************************************************************/ + +void eth_halt(void) +{ + debug ("%s(%d): eth_halt()\n", __FILE__, __LINE__); + + /* Reset the ethernet engines */ + ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); + ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); +} + +/****************************************************************************/ + +int eth_rx(void) +{ + volatile struct ks8695_rxdesc *dp; + int i, len = 0; + + debug ("%s(%d): eth_rx()\n", __FILE__, __LINE__); + + for (i = 0; (i < RXDESCS); i++) { + dp= &ks8695_rx[i]; + if ((dp->status & 0x80000000) == 0) { + len = (dp->status & 0x7ff) - 4; + NetReceive((void *) dp->addr, len); + dp->status = 0x80000000; + ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); + break; + } + } + + return len; +} + +/****************************************************************************/ + +int eth_send(volatile void *packet, int len) +{ + volatile struct ks8695_txdesc *dp; + static int next = 0; + + debug ("%s(%d): eth_send(packet=%x,len=%d)\n", __FILE__, __LINE__, + packet, len); + + dp = &ks8695_tx[next]; + memcpy((void *) dp->addr, (void *) packet, len); + + if (len < 64) { + memset((void *) (dp->addr + len), 0, 64-len); + len = 64; + } + + dp->ctrl = len | 0xe0000000; + dp->owner = 0x80000000; + + ks8695_write(KS8695_LAN_DMA_TX, 0x3); + ks8695_write(KS8695_LAN_DMA_TX_START, 0x1); + + if (++next >= TXDESCS) + next = 0; + + return len; +} diff --git a/roms/u-boot-sam460ex/drivers/net/lan91c96.c b/roms/u-boot-sam460ex/drivers/net/lan91c96.c new file mode 100644 index 000000000..810079f03 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/lan91c96.c @@ -0,0 +1,820 @@ +/*------------------------------------------------------------------------ + * lan91c96.c + * This is a driver for SMSC's LAN91C96 single-chip Ethernet device, based + * on the SMC91111 driver from U-boot. + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Rolf Offermanns <rof@sysgo.de> + * + * Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + * Developed by Simple Network Magic Corporation (SNMC) + * Copyright (C) 1996 by Erik Stahlman (ES) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Information contained in this file was obtained from the LAN91C96 + * manual from SMC. To get a copy, if you really want one, you can find + * information under www.smsc.com. + * + * + * "Features" of the SMC chip: + * 6144 byte packet memory. ( for the 91C96 ) + * EEPROM for configuration + * AUI/TP selection ( mine has 10Base2/10BaseT select ) + * + * Arguments: + * io = for the base address + * irq = for the IRQ + * + * author: + * Erik Stahlman ( erik@vt.edu ) + * Daris A Nevil ( dnevil@snmc.com ) + * + * + * Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + * + * Sources: + * o SMSC LAN91C96 databook (www.smsc.com) + * o smc91111.c (u-boot driver) + * o smc9194.c (linux kernel driver) + * o lan91c96.c (Intel Diagnostic Manager driver) + * + * History: + * 04/30/03 Mathijs Haarman Modified smc91111.c (u-boot version) + * for lan91c96 + *--------------------------------------------------------------------------- + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include "lan91c96.h" +#include <net.h> + +/*------------------------------------------------------------------------ + * + * Configuration options, for the experienced user to change. + * + -------------------------------------------------------------------------*/ + +/* Use power-down feature of the chip */ +#define POWER_DOWN 0 + +/* + * Wait time for memory to be free. This probably shouldn't be + * tuned that much, as waiting for this means nothing else happens + * in the system +*/ +#define MEMORY_WAIT_TIME 16 + +#define SMC_DEBUG 0 + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(args...) printf(args) +#else +#define PRINTK3(args...) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(args...) printf(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(args...) printf(args) +#else +#define PRINTK(args...) +#endif + + +/*------------------------------------------------------------------------ + * + * The internal workings of the driver. If you are changing anything + * here with the SMC stuff, you should have the datasheet and know + * what you are doing. + * + *------------------------------------------------------------------------ + */ +#define DRIVER_NAME "LAN91C96" +#define SMC_ALLOC_MAX_TRY 5 +#define SMC_TX_TIMEOUT 30 + +#define ETH_ZLEN 60 + +#ifdef CONFIG_LAN91C96_USE_32_BIT +#define USE_32_BIT 1 +#else +#undef USE_32_BIT +#endif + +/* See if a MAC address is defined in the current environment. If so use it. If not + . print a warning and set the environment and other globals with the default. + . If an EEPROM is present it really should be consulted. +*/ +static int smc_get_ethaddr(bd_t *bd, struct eth_device *dev); +static int get_rom_mac(struct eth_device *dev, unsigned char *v_rom_mac); + +/* ------------------------------------------------------------ + * Internal routines + * ------------------------------------------------------------ + */ + +static unsigned char smc_mac_addr[] = { 0xc0, 0x00, 0x00, 0x1b, 0x62, 0x9c }; + +/* + * This function must be called before smc_open() if you want to override + * the default mac address. + */ + +static void smc_set_mac_addr(const unsigned char *addr) +{ + int i; + + for (i = 0; i < sizeof (smc_mac_addr); i++) { + smc_mac_addr[i] = addr[i]; + } +} + +/*********************************************** + * Show available memory * + ***********************************************/ +void dump_memory_info(struct eth_device *dev) +{ + word mem_info; + word old_bank; + + old_bank = SMC_inw(dev, LAN91C96_BANK_SELECT) & 0xF; + + SMC_SELECT_BANK(dev, 0); + mem_info = SMC_inw(dev, LAN91C96_MIR); + PRINTK2 ("Memory: %4d available\n", (mem_info >> 8) * 2048); + + SMC_SELECT_BANK(dev, old_bank); +} + +/* + * A rather simple routine to print out a packet for debugging purposes. + */ +#if SMC_DEBUG > 2 +static void print_packet (byte *, int); +#endif + +static int poll4int (struct eth_device *dev, byte mask, int timeout) +{ + int tmo = get_timer (0) + timeout * CONFIG_SYS_HZ; + int is_timeout = 0; + word old_bank = SMC_inw(dev, LAN91C96_BANK_SELECT); + + PRINTK2 ("Polling...\n"); + SMC_SELECT_BANK(dev, 2); + while ((SMC_inw(dev, LAN91C96_INT_STATS) & mask) == 0) { + if (get_timer (0) >= tmo) { + is_timeout = 1; + break; + } + } + + /* restore old bank selection */ + SMC_SELECT_BANK(dev, old_bank); + + if (is_timeout) + return 1; + else + return 0; +} + +/* + * Function: smc_reset + * Purpose: + * This sets the SMC91111 chip to its normal state, hopefully from whatever + * mess that any other DOS driver has put it in. + * + * Maybe I should reset more registers to defaults in here? SOFTRST should + * do that for me. + * + * Method: + * 1. send a SOFT RESET + * 2. wait for it to finish + * 3. enable autorelease mode + * 4. reset the memory management unit + * 5. clear all interrupts + * +*/ +static void smc_reset(struct eth_device *dev) +{ + PRINTK2("%s:smc_reset\n", dev->name); + + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK(dev, 0); + SMC_outw(dev, LAN91C96_RCR_SOFT_RST, LAN91C96_RCR); + + udelay (10); + + /* Disable transmit and receive functionality */ + SMC_outw(dev, 0, LAN91C96_RCR); + SMC_outw(dev, 0, LAN91C96_TCR); + + /* set the control register */ + SMC_SELECT_BANK(dev, 1); + SMC_outw(dev, SMC_inw(dev, LAN91C96_CONTROL) | LAN91C96_CTR_BIT_8, + LAN91C96_CONTROL); + + /* Disable all interrupts */ + SMC_outb(dev, 0, LAN91C96_INT_MASK); +} + +/* + * Function: smc_enable + * Purpose: let the chip talk to the outside work + * Method: + * 1. Initialize the Memory Configuration Register + * 2. Enable the transmitter + * 3. Enable the receiver +*/ +static void smc_enable(struct eth_device *dev) +{ + PRINTK2("%s:smc_enable\n", dev->name); + SMC_SELECT_BANK(dev, 0); + + /* Initialize the Memory Configuration Register. See page + 49 of the LAN91C96 data sheet for details. */ + SMC_outw(dev, LAN91C96_MCR_TRANSMIT_PAGES, LAN91C96_MCR); + + /* Initialize the Transmit Control Register */ + SMC_outw(dev, LAN91C96_TCR_TXENA, LAN91C96_TCR); + /* Initialize the Receive Control Register + * FIXME: + * The promiscuous bit set because I could not receive ARP reply + * packets from the server when I send a ARP request. It only works + * when I set the promiscuous bit + */ + SMC_outw(dev, LAN91C96_RCR_RXEN | LAN91C96_RCR_PRMS, LAN91C96_RCR); +} + +/* + * Function: smc_shutdown + * Purpose: closes down the SMC91xxx chip. + * Method: + * 1. zero the interrupt mask + * 2. clear the enable receive flag + * 3. clear the enable xmit flags + * + * TODO: + * (1) maybe utilize power down mode. + * Why not yet? Because while the chip will go into power down mode, + * the manual says that it will wake up in response to any I/O requests + * in the register space. Empirical results do not show this working. + */ +static void smc_shutdown(struct eth_device *dev) +{ + PRINTK2("%s:smc_shutdown\n", dev->name); + + /* no more interrupts for me */ + SMC_SELECT_BANK(dev, 2); + SMC_outb(dev, 0, LAN91C96_INT_MASK); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK(dev, 0); + SMC_outb(dev, 0, LAN91C96_RCR); + SMC_outb(dev, 0, LAN91C96_TCR); +} + + +/* + * Function: smc_hardware_send_packet(struct net_device * ) + * Purpose: + * This sends the actual packet to the SMC9xxx chip. + * + * Algorithm: + * First, see if a saved_skb is available. + * ( this should NOT be called if there is no 'saved_skb' + * Now, find the packet number that the chip allocated + * Point the data pointers at it in memory + * Set the length word in the chip's memory + * Dump the packet to chip memory + * Check if a last byte is needed ( odd length packet ) + * if so, set the control flag right + * Tell the card to send it + * Enable the transmit interrupt, so I know if it failed + * Free the kernel data if I actually sent it. + */ +static int smc_send_packet(struct eth_device *dev, volatile void *packet, + int packet_length) +{ + byte packet_no; + unsigned long ioaddr; + byte *buf; + int length; + int numPages; + int try = 0; + int time_out; + byte status; + + + PRINTK3("%s:smc_hardware_send_packet\n", dev->name); + + length = ETH_ZLEN < packet_length ? packet_length : ETH_ZLEN; + + /* allocate memory + ** The MMU wants the number of pages to be the number of 256 bytes + ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** + ** The 91C111 ignores the size bits, but the code is left intact + ** for backwards and future compatibility. + ** + ** Pkt size for allocating is data length +6 (for additional status + ** words, length and ctl!) + ** + ** If odd size then last byte is included in this header. + */ + numPages = ((length & 0xfffe) + 6); + numPages >>= 8; /* Divide by 256 */ + + if (numPages > 7) { + printf("%s: Far too big packet error. \n", dev->name); + return 0; + } + + /* now, try to allocate the memory */ + + SMC_SELECT_BANK(dev, 2); + SMC_outw(dev, LAN91C96_MMUCR_ALLOC_TX | numPages, LAN91C96_MMU); + + again: + try++; + time_out = MEMORY_WAIT_TIME; + do { + status = SMC_inb(dev, LAN91C96_INT_STATS); + if (status & LAN91C96_IST_ALLOC_INT) { + + SMC_outb(dev, LAN91C96_IST_ALLOC_INT, + LAN91C96_INT_STATS); + break; + } + } while (--time_out); + + if (!time_out) { + PRINTK2 ("%s: memory allocation, try %d failed ...\n", + dev->name, try); + if (try < SMC_ALLOC_MAX_TRY) + goto again; + else + return 0; + } + + PRINTK2 ("%s: memory allocation, try %d succeeded ...\n", + dev->name, try); + + /* I can send the packet now.. */ + + ioaddr = dev->iobase; + + buf = (byte *) packet; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = SMC_inb(dev, LAN91C96_ARR); + if (packet_no & LAN91C96_ARR_FAILED) { + /* or isn't there? BAD CHIP! */ + printf("%s: Memory allocation failed. \n", dev->name); + return 0; + } + + /* we have a packet address, so tell the card to use it */ + SMC_outb(dev, packet_no, LAN91C96_PNR); + + /* point to the beginning of the packet */ + SMC_outw(dev, LAN91C96_PTR_AUTO_INCR, LAN91C96_POINTER); + + PRINTK3("%s: Trying to xmit packet of length %x\n", + dev->name, length); + +#if SMC_DEBUG > 2 + printf ("Transmitting Packet\n"); + print_packet (buf, length); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ) */ +#ifdef USE_32_BIT + SMC_outl(dev, (length + 6) << 16, LAN91C96_DATA_HIGH); +#else + SMC_outw(dev, 0, LAN91C96_DATA_HIGH); + /* send the packet length ( +6 for status words, length, and ctl */ + SMC_outw(dev, (length + 6), LAN91C96_DATA_HIGH); +#endif /* USE_32_BIT */ + + /* send the actual data + * I _think_ it's faster to send the longs first, and then + * mop up by sending the last word. It depends heavily + * on alignment, at least on the 486. Maybe it would be + * a good idea to check which is optimal? But that could take + * almost as much time as is saved? + */ +#ifdef USE_32_BIT + SMC_outsl(dev, LAN91C96_DATA_HIGH, buf, length >> 2); + if (length & 0x2) + SMC_outw(dev, *((word *) (buf + (length & 0xFFFFFFFC))), + LAN91C96_DATA_HIGH); +#else + SMC_outsw(dev, LAN91C96_DATA_HIGH, buf, (length) >> 1); +#endif /* USE_32_BIT */ + + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) { + SMC_outw(dev, 0, LAN91C96_DATA_HIGH); + } else { + SMC_outw(dev, buf[length - 1] | 0x2000, LAN91C96_DATA_HIGH); + } + + /* and let the chipset deal with it */ + SMC_outw(dev, LAN91C96_MMUCR_ENQUEUE, LAN91C96_MMU); + + /* poll for TX INT */ + if (poll4int (dev, LAN91C96_MSK_TX_INT, SMC_TX_TIMEOUT)) { + /* sending failed */ + PRINTK2("%s: TX timeout, sending failed...\n", dev->name); + + /* release packet */ + SMC_outw(dev, LAN91C96_MMUCR_RELEASE_TX, LAN91C96_MMU); + + /* wait for MMU getting ready (low) */ + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (10); + + PRINTK2("MMU ready\n"); + + + return 0; + } else { + /* ack. int */ + SMC_outw(dev, LAN91C96_IST_TX_INT, LAN91C96_INT_STATS); + + PRINTK2("%s: Sent packet of length %d \n", dev->name, length); + + /* release packet */ + SMC_outw(dev, LAN91C96_MMUCR_RELEASE_TX, LAN91C96_MMU); + + /* wait for MMU getting ready (low) */ + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (10); + + PRINTK2 ("MMU ready\n"); + } + + return length; +} + + +/* + * Open and Initialize the board + * + * Set up everything, reset the card, etc .. + * + */ +static int smc_open(bd_t *bd, struct eth_device *dev) +{ + int i, err; /* used to set hw ethernet address */ + + PRINTK2("%s:smc_open\n", dev->name); + + /* reset the hardware */ + + smc_reset(dev); + smc_enable(dev); + + SMC_SELECT_BANK(dev, 1); + /* set smc_mac_addr, and sync it with u-boot globals */ + err = smc_get_ethaddr(bd, dev); + if (err < 0) + return -1; +#ifdef USE_32_BIT + for (i = 0; i < 6; i += 2) { + word address; + + address = smc_mac_addr[i + 1] << 8; + address |= smc_mac_addr[i]; + SMC_outw(dev, address, LAN91C96_IA0 + i); + } +#else + for (i = 0; i < 6; i++) + SMC_outb(dev, smc_mac_addr[i], LAN91C96_IA0 + i); +#endif + return 0; +} + +/*------------------------------------------------------------- + * + * smc_rcv - receive a packet from the card + * + * There is ( at least ) a packet waiting to be read from + * chip-memory. + * + * o Read the status + * o If an error, record it + * o otherwise, read in the packet + *------------------------------------------------------------- + */ +static int smc_rcv(struct eth_device *dev) +{ + int packet_number; + word status; + word packet_length; + int is_error = 0; + +#ifdef USE_32_BIT + dword stat_len; +#endif + + + SMC_SELECT_BANK(dev, 2); + packet_number = SMC_inw(dev, LAN91C96_FIFO); + + if (packet_number & LAN91C96_FIFO_RXEMPTY) { + return 0; + } + + PRINTK3("%s:smc_rcv\n", dev->name); + /* start reading from the start of the packet */ + SMC_outw(dev, LAN91C96_PTR_READ | LAN91C96_PTR_RCV | + LAN91C96_PTR_AUTO_INCR, LAN91C96_POINTER); + + /* First two words are status and packet_length */ +#ifdef USE_32_BIT + stat_len = SMC_inl(dev, LAN91C96_DATA_HIGH); + status = stat_len & 0xffff; + packet_length = stat_len >> 16; +#else + status = SMC_inw(dev, LAN91C96_DATA_HIGH); + packet_length = SMC_inw(dev, LAN91C96_DATA_HIGH); +#endif + + packet_length &= 0x07ff; /* mask off top bits */ + + PRINTK2 ("RCV: STATUS %4x LENGTH %4x\n", status, packet_length); + + if (!(status & FRAME_FILTER)) { + /* Adjust for having already read the first two words */ + packet_length -= 4; /*4; */ + + + /* set odd length for bug in LAN91C111, */ + /* which never sets RS_ODDFRAME */ + /* TODO ? */ + + +#ifdef USE_32_BIT + PRINTK3 (" Reading %d dwords (and %d bytes) \n", + packet_length >> 2, packet_length & 3); + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + SMC_insl(dev, LAN91C96_DATA_HIGH, NetRxPackets[0], + packet_length >> 2); + /* read the left over bytes */ + if (packet_length & 3) { + int i; + + byte *tail = (byte *) (NetRxPackets[0] + (packet_length & ~3)); + dword leftover = SMC_inl(dev, LAN91C96_DATA_HIGH); + + for (i = 0; i < (packet_length & 3); i++) + *tail++ = (byte) (leftover >> (8 * i)) & 0xff; + } +#else + PRINTK3 (" Reading %d words and %d byte(s) \n", + (packet_length >> 1), packet_length & 1); + SMC_insw(dev, LAN91C96_DATA_HIGH, NetRxPackets[0], + packet_length >> 1); + +#endif /* USE_32_BIT */ + +#if SMC_DEBUG > 2 + printf ("Receiving Packet\n"); + print_packet((byte *)NetRxPackets[0], packet_length); +#endif + } else { + /* error ... */ + /* TODO ? */ + is_error = 1; + } + + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (1); /* Wait until not busy */ + + /* error or good, tell the card to get rid of this packet */ + SMC_outw(dev, LAN91C96_MMUCR_RELEASE_RX, LAN91C96_MMU); + + while (SMC_inw(dev, LAN91C96_MMU) & LAN91C96_MMUCR_NO_BUSY) + udelay (1); /* Wait until not busy */ + + if (!is_error) { + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], packet_length); + return packet_length; + } else { + return 0; + } + +} + +/*---------------------------------------------------- + * smc_close + * + * this makes the board clean up everything that it can + * and not talk to the outside world. Caused by + * an 'ifconfig ethX down' + * + -----------------------------------------------------*/ +static int smc_close(struct eth_device *dev) +{ + PRINTK2("%s:smc_close\n", dev->name); + + /* clear everything */ + smc_shutdown(dev); + + return 0; +} + +#if SMC_DEBUG > 2 +static void print_packet(byte *buf, int length) +{ +#if 0 + int i; + int remainder; + int lines; + + printf ("Packet of length %d \n", length); + + lines = length / 16; + remainder = length % 16; + + for (i = 0; i < lines; i++) { + int cur; + + for (cur = 0; cur < 8; cur++) { + byte a, b; + + a = *(buf++); + b = *(buf++); + printf ("%02x%02x ", a, b); + } + printf ("\n"); + } + for (i = 0; i < remainder / 2; i++) { + byte a, b; + + a = *(buf++); + b = *(buf++); + printf ("%02x%02x ", a, b); + } + printf ("\n"); +#endif /* 0 */ +} +#endif /* SMC_DEBUG > 2 */ + +static int lan91c96_init(struct eth_device *dev, bd_t *bd) +{ + return smc_open(bd, dev); +} + +static void lan91c96_halt(struct eth_device *dev) +{ + smc_close(dev); +} + +static int lan91c96_recv(struct eth_device *dev) +{ + return smc_rcv(dev); +} + +static int lan91c96_send(struct eth_device *dev, volatile void *packet, + int length) +{ + return smc_send_packet(dev, packet, length); +} + +/* smc_get_ethaddr + * + * This checks both the environment and the ROM for an ethernet address. If + * found, the environment takes precedence. + */ + +static int smc_get_ethaddr(bd_t *bd, struct eth_device *dev) +{ + uchar v_mac[6]; + + if (!eth_getenv_enetaddr("ethaddr", v_mac)) { + /* get ROM mac value if any */ + if (!get_rom_mac(dev, v_mac)) { + printf("\n*** ERROR: ethaddr is NOT set !!\n"); + return -1; + } + eth_setenv_enetaddr("ethaddr", v_mac); + } + + smc_set_mac_addr(v_mac); /* use old function to update smc default */ + PRINTK("Using MAC Address %pM\n", v_mac); + return 0; +} + +/* + * get_rom_mac() + * Note, this has omly been tested for the OMAP730 P2. + */ + +static int get_rom_mac(struct eth_device *dev, unsigned char *v_rom_mac) +{ +#ifdef HARDCODE_MAC /* used for testing or to supress run time warnings */ + char hw_mac_addr[] = { 0x02, 0x80, 0xad, 0x20, 0x31, 0xb8 }; + + memcpy (v_rom_mac, hw_mac_addr, 6); + return (1); +#else + int i; + SMC_SELECT_BANK(dev, 1); + for (i=0; i<6; i++) + { + v_rom_mac[i] = SMC_inb(dev, LAN91C96_IA0 + i); + } + return (1); +#endif +} + +/* Structure to detect the device IDs */ +struct id_type { + u8 id; + char *name; +}; +static struct id_type supported_chips[] = { + {0, ""}, /* Dummy entry to prevent id check failure */ + {9, "LAN91C110"}, + {8, "LAN91C100FD"}, + {7, "LAN91C100"}, + {5, "LAN91C95"}, + {4, "LAN91C94/LAN91C96"}, + {3, "LAN91C90/LAN91C92"}, +}; +/* lan91c96_detect_chip + * See: + * http://www.embeddedsys.com/subpages/resources/images/documents/LAN91C96_datasheet.pdf + * page 71 - that is the closest we get to detect this device + */ +static int lan91c96_detect_chip(struct eth_device *dev) +{ + u8 chip_id; + int r; + SMC_SELECT_BANK(dev, 3); + chip_id = SMC_inw(dev, 0xA) & LAN91C96_REV_REVID; + SMC_SELECT_BANK(dev, 0); + for (r = 0; r < sizeof(supported_chips) / sizeof(struct id_type); r++) + if (chip_id == supported_chips[r].id) + return r; + return 0; +} + +int lan91c96_initialize(u8 dev_num, int base_addr) +{ + struct eth_device *dev; + int r = 0; + + dev = malloc(sizeof(*dev)); + if (!dev) { + return 0; + } + memset(dev, 0, sizeof(*dev)); + + dev->iobase = base_addr; + + /* Try to detect chip. Will fail if not present. */ + r = lan91c96_detect_chip(dev); + if (!r) { + free(dev); + return 0; + } + get_rom_mac(dev, dev->enetaddr); + + dev->init = lan91c96_init; + dev->halt = lan91c96_halt; + dev->send = lan91c96_send; + dev->recv = lan91c96_recv; + sprintf(dev->name, "%s-%hu", supported_chips[r].name, dev_num); + + eth_register(dev); + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/lan91c96.h b/roms/u-boot-sam460ex/drivers/net/lan91c96.h new file mode 100644 index 000000000..6fbb0e3cb --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/lan91c96.h @@ -0,0 +1,635 @@ +/*------------------------------------------------------------------------ + * lan91c96.h + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Rolf Offermanns <rof@sysgo.de> + * Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + * Developed by Simple Network Magic Corporation (SNMC) + * Copyright (C) 1996 by Erik Stahlman (ES) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This file contains register information and access macros for + * the LAN91C96 single chip ethernet controller. It is a modified + * version of the smc9111.h file. + * + * Information contained in this file was obtained from the LAN91C96 + * manual from SMC. To get a copy, if you really want one, you can find + * information under www.smsc.com. + * + * Authors + * Erik Stahlman ( erik@vt.edu ) + * Daris A Nevil ( dnevil@snmc.com ) + * + * History + * 04/30/03 Mathijs Haarman Modified smc91111.h (u-boot version) + * for lan91c96 + *------------------------------------------------------------------------- + */ +#ifndef _LAN91C96_H_ +#define _LAN91C96_H_ + +#include <asm/types.h> +#include <asm/io.h> +#include <config.h> + +/* I want some simple types */ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; + +/* + * DEBUGGING LEVELS + * + * 0 for normal operation + * 1 for slightly more details + * >2 for various levels of increasingly useless information + * 2 for interrupt tracking, status flags + * 3 for packet info + * 4 for complete packet dumps + */ +/*#define SMC_DEBUG 0 */ + +/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */ + +#define SMC_IO_EXTENT 16 + +#ifdef CONFIG_PXA250 + +#ifdef CONFIG_LUBBOCK +#define SMC_IO_SHIFT 2 +#undef USE_32_BIT + +#else +#define SMC_IO_SHIFT 0 +#endif + +#define SMCREG(edev, r) ((edev)->iobase+((r)<<SMC_IO_SHIFT)) + +#define SMC_inl(edev, r) (*((volatile dword *)SMCREG(edev, r))) +#define SMC_inw(edev, r) (*((volatile word *)SMCREG(edev, r))) +#define SMC_inb(edev, p) ({ \ + unsigned int __p = p; \ + unsigned int __v = SMC_inw(edev, __p & ~1); \ + if (__p & 1) __v >>= 8; \ + else __v &= 0xff; \ + __v; }) + +#define SMC_outl(edev, d, r) (*((volatile dword *)SMCREG(edev, r)) = d) +#define SMC_outw(edev, d, r) (*((volatile word *)SMCREG(edev, r)) = d) +#define SMC_outb(edev, d, r) ({ word __d = (byte)(d); \ + word __w = SMC_inw(edev, (r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw(edev, __w, (r)&~1); \ + }) + +#define SMC_outsl(edev, r, b, l) ({ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl(edev, *(__b2 + __i),\ + r); \ + } \ + }) + +#define SMC_outsw(edev, r, b, l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw(edev, *(__b2 + __i),\ + r); \ + } \ + }) + +#define SMC_insl(edev, r, b, l) ({ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl(edev,\ + r); \ + SMC_inl(edev, 0); \ + }; \ + }) + +#define SMC_insw(edev, r, b, l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw(edev,\ + r); \ + SMC_inw(edev, 0); \ + }; \ + }) + +#define SMC_insb(edev, r, b, l) ({ int __i ; \ + byte *__b2; \ + __b2 = (byte *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inb(edev,\ + r); \ + SMC_inb(edev, 0); \ + }; \ + }) + +#else /* if not CONFIG_PXA250 */ + +/* + * We have only 16 Bit PCMCIA access on Socket 0 + */ + +#define SMC_inw(edev, r) (*((volatile word *)((edev)->iobase+(r)))) +#define SMC_inb(edev, r) (((r)&1) ? SMC_inw(edev, (r)&~1)>>8 :\ + SMC_inw(edev, r)&0xFF) + +#define SMC_outw(edev, d, r) (*((volatile word *)((edev)->iobase+(r))) = d) +#define SMC_outb(edev, d, r) ({ word __d = (byte)(d); \ + word __w = SMC_inw(edev, (r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw(edev, __w, (r)&~1); \ + }) +#define SMC_outsw(edev, r, b, l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw(edev, *(__b2 + __i),\ + r); \ + } \ + }) + +#define SMC_insw(edev, r, b, l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw(edev,\ + r); \ + SMC_inw(edev, 0); \ + }; \ + }) + +#endif + +/* + **************************************************************************** + * Bank Select Field + **************************************************************************** + */ +#define LAN91C96_BANK_SELECT 14 /* Bank Select Register */ +#define LAN91C96_BANKSELECT (0x3UC << 0) +#define BANK0 0x00 +#define BANK1 0x01 +#define BANK2 0x02 +#define BANK3 0x03 +#define BANK4 0x04 + +/* + **************************************************************************** + * EEPROM Addresses. + **************************************************************************** + */ +#define EEPROM_MAC_OFFSET_1 0x6020 +#define EEPROM_MAC_OFFSET_2 0x6021 +#define EEPROM_MAC_OFFSET_3 0x6022 + +/* + **************************************************************************** + * Bank 0 Register Map in I/O Space + **************************************************************************** + */ +#define LAN91C96_TCR 0 /* Transmit Control Register */ +#define LAN91C96_EPH_STATUS 2 /* EPH Status Register */ +#define LAN91C96_RCR 4 /* Receive Control Register */ +#define LAN91C96_COUNTER 6 /* Counter Register */ +#define LAN91C96_MIR 8 /* Memory Information Register */ +#define LAN91C96_MCR 10 /* Memory Configuration Register */ + +/* + **************************************************************************** + * Transmit Control Register - Bank 0 - Offset 0 + **************************************************************************** + */ +#define LAN91C96_TCR_TXENA (0x1U << 0) +#define LAN91C96_TCR_LOOP (0x1U << 1) +#define LAN91C96_TCR_FORCOL (0x1U << 2) +#define LAN91C96_TCR_TXP_EN (0x1U << 3) +#define LAN91C96_TCR_PAD_EN (0x1U << 7) +#define LAN91C96_TCR_NOCRC (0x1U << 8) +#define LAN91C96_TCR_MON_CSN (0x1U << 10) +#define LAN91C96_TCR_FDUPLX (0x1U << 11) +#define LAN91C96_TCR_STP_SQET (0x1U << 12) +#define LAN91C96_TCR_EPH_LOOP (0x1U << 13) +#define LAN91C96_TCR_ETEN_TYPE (0x1U << 14) +#define LAN91C96_TCR_FDSE (0x1U << 15) + +/* + **************************************************************************** + * EPH Status Register - Bank 0 - Offset 2 + **************************************************************************** + */ +#define LAN91C96_EPHSR_TX_SUC (0x1U << 0) +#define LAN91C96_EPHSR_SNGL_COL (0x1U << 1) +#define LAN91C96_EPHSR_MUL_COL (0x1U << 2) +#define LAN91C96_EPHSR_LTX_MULT (0x1U << 3) +#define LAN91C96_EPHSR_16COL (0x1U << 4) +#define LAN91C96_EPHSR_SQET (0x1U << 5) +#define LAN91C96_EPHSR_LTX_BRD (0x1U << 6) +#define LAN91C96_EPHSR_TX_DEFR (0x1U << 7) +#define LAN91C96_EPHSR_WAKEUP (0x1U << 8) +#define LAN91C96_EPHSR_LATCOL (0x1U << 9) +#define LAN91C96_EPHSR_LOST_CARR (0x1U << 10) +#define LAN91C96_EPHSR_EXC_DEF (0x1U << 11) +#define LAN91C96_EPHSR_CTR_ROL (0x1U << 12) + +#define LAN91C96_EPHSR_LINK_OK (0x1U << 14) +#define LAN91C96_EPHSR_TX_UNRN (0x1U << 15) + +#define LAN91C96_EPHSR_ERRORS (LAN91C96_EPHSR_SNGL_COL | \ + LAN91C96_EPHSR_MUL_COL | \ + LAN91C96_EPHSR_16COL | \ + LAN91C96_EPHSR_SQET | \ + LAN91C96_EPHSR_TX_DEFR | \ + LAN91C96_EPHSR_LATCOL | \ + LAN91C96_EPHSR_LOST_CARR | \ + LAN91C96_EPHSR_EXC_DEF | \ + LAN91C96_EPHSR_LINK_OK | \ + LAN91C96_EPHSR_TX_UNRN) + +/* + **************************************************************************** + * Receive Control Register - Bank 0 - Offset 4 + **************************************************************************** + */ +#define LAN91C96_RCR_RX_ABORT (0x1U << 0) +#define LAN91C96_RCR_PRMS (0x1U << 1) +#define LAN91C96_RCR_ALMUL (0x1U << 2) +#define LAN91C96_RCR_RXEN (0x1U << 8) +#define LAN91C96_RCR_STRIP_CRC (0x1U << 9) +#define LAN91C96_RCR_FILT_CAR (0x1U << 14) +#define LAN91C96_RCR_SOFT_RST (0x1U << 15) + +/* + **************************************************************************** + * Counter Register - Bank 0 - Offset 6 + **************************************************************************** + */ +#define LAN91C96_ECR_SNGL_COL (0xFU << 0) +#define LAN91C96_ECR_MULT_COL (0xFU << 5) +#define LAN91C96_ECR_DEF_TX (0xFU << 8) +#define LAN91C96_ECR_EXC_DEF_TX (0xFU << 12) + +/* + **************************************************************************** + * Memory Information Register - Bank 0 - OFfset 8 + **************************************************************************** + */ +#define LAN91C96_MIR_SIZE (0x18 << 0) /* 6144 bytes */ + +/* + **************************************************************************** + * Memory Configuration Register - Bank 0 - Offset 10 + **************************************************************************** + */ +#define LAN91C96_MCR_MEM_RES (0xFFU << 0) +#define LAN91C96_MCR_MEM_MULT (0x3U << 9) +#define LAN91C96_MCR_HIGH_ID (0x3U << 12) + +#define LAN91C96_MCR_TRANSMIT_PAGES 0x6 + +/* + **************************************************************************** + * Bank 1 Register Map in I/O Space + **************************************************************************** + */ +#define LAN91C96_CONFIG 0 /* Configuration Register */ +#define LAN91C96_BASE 2 /* Base Address Register */ +#define LAN91C96_IA0 4 /* Individual Address Register - 0 */ +#define LAN91C96_IA1 5 /* Individual Address Register - 1 */ +#define LAN91C96_IA2 6 /* Individual Address Register - 2 */ +#define LAN91C96_IA3 7 /* Individual Address Register - 3 */ +#define LAN91C96_IA4 8 /* Individual Address Register - 4 */ +#define LAN91C96_IA5 9 /* Individual Address Register - 5 */ +#define LAN91C96_GEN_PURPOSE 10 /* General Address Registers */ +#define LAN91C96_CONTROL 12 /* Control Register */ + +/* + **************************************************************************** + * Configuration Register - Bank 1 - Offset 0 + **************************************************************************** + */ +#define LAN91C96_CR_INT_SEL0 (0x1U << 1) +#define LAN91C96_CR_INT_SEL1 (0x1U << 2) +#define LAN91C96_CR_RES (0x3U << 3) +#define LAN91C96_CR_DIS_LINK (0x1U << 6) +#define LAN91C96_CR_16BIT (0x1U << 7) +#define LAN91C96_CR_AUI_SELECT (0x1U << 8) +#define LAN91C96_CR_SET_SQLCH (0x1U << 9) +#define LAN91C96_CR_FULL_STEP (0x1U << 10) +#define LAN91C96_CR_NO_WAIT (0x1U << 12) + +/* + **************************************************************************** + * Base Address Register - Bank 1 - Offset 2 + **************************************************************************** + */ +#define LAN91C96_BAR_RA_BITS (0x27U << 0) +#define LAN91C96_BAR_ROM_SIZE (0x1U << 6) +#define LAN91C96_BAR_A_BITS (0xFFU << 8) + +/* + **************************************************************************** + * Control Register - Bank 1 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_CTR_STORE (0x1U << 0) +#define LAN91C96_CTR_RELOAD (0x1U << 1) +#define LAN91C96_CTR_EEPROM (0x1U << 2) +#define LAN91C96_CTR_TE_ENABLE (0x1U << 5) +#define LAN91C96_CTR_CR_ENABLE (0x1U << 6) +#define LAN91C96_CTR_LE_ENABLE (0x1U << 7) +#define LAN91C96_CTR_BIT_8 (0x1U << 8) +#define LAN91C96_CTR_AUTO_RELEASE (0x1U << 11) +#define LAN91C96_CTR_WAKEUP_EN (0x1U << 12) +#define LAN91C96_CTR_PWRDN (0x1U << 13) +#define LAN91C96_CTR_RCV_BAD (0x1U << 14) + +/* + **************************************************************************** + * Bank 2 Register Map in I/O Space + **************************************************************************** + */ +#define LAN91C96_MMU 0 /* MMU Command Register */ +#define LAN91C96_AUTO_TX_START 1 /* Auto Tx Start Register */ +#define LAN91C96_PNR 2 /* Packet Number Register */ +#define LAN91C96_ARR 3 /* Allocation Result Register */ +#define LAN91C96_FIFO 4 /* FIFO Ports Register */ +#define LAN91C96_POINTER 6 /* Pointer Register */ +#define LAN91C96_DATA_HIGH 8 /* Data High Register */ +#define LAN91C96_DATA_LOW 10 /* Data Low Register */ +#define LAN91C96_INT_STATS 12 /* Interrupt Status Register - RO */ +#define LAN91C96_INT_ACK 12 /* Interrupt Acknowledge Register -WO */ +#define LAN91C96_INT_MASK 13 /* Interrupt Mask Register */ + +/* + **************************************************************************** + * MMU Command Register - Bank 2 - Offset 0 + **************************************************************************** + */ +#define LAN91C96_MMUCR_NO_BUSY (0x1U << 0) +#define LAN91C96_MMUCR_N1 (0x1U << 1) +#define LAN91C96_MMUCR_N2 (0x1U << 2) +#define LAN91C96_MMUCR_COMMAND (0xFU << 4) +#define LAN91C96_MMUCR_ALLOC_TX (0x2U << 4) /* WXYZ = 0010 */ +#define LAN91C96_MMUCR_RESET_MMU (0x4U << 4) /* WXYZ = 0100 */ +#define LAN91C96_MMUCR_REMOVE_RX (0x6U << 4) /* WXYZ = 0110 */ +#define LAN91C96_MMUCR_REMOVE_TX (0x7U << 4) /* WXYZ = 0111 */ +#define LAN91C96_MMUCR_RELEASE_RX (0x8U << 4) /* WXYZ = 1000 */ +#define LAN91C96_MMUCR_RELEASE_TX (0xAU << 4) /* WXYZ = 1010 */ +#define LAN91C96_MMUCR_ENQUEUE (0xCU << 4) /* WXYZ = 1100 */ +#define LAN91C96_MMUCR_RESET_TX (0xEU << 4) /* WXYZ = 1110 */ + +/* + **************************************************************************** + * Auto Tx Start Register - Bank 2 - Offset 1 + **************************************************************************** + */ +#define LAN91C96_AUTOTX (0xFFU << 0) + +/* + **************************************************************************** + * Packet Number Register - Bank 2 - Offset 2 + **************************************************************************** + */ +#define LAN91C96_PNR_TX (0x1FU << 0) + +/* + **************************************************************************** + * Allocation Result Register - Bank 2 - Offset 3 + **************************************************************************** + */ +#define LAN91C96_ARR_ALLOC_PN (0x7FU << 0) +#define LAN91C96_ARR_FAILED (0x1U << 7) + +/* + **************************************************************************** + * FIFO Ports Register - Bank 2 - Offset 4 + **************************************************************************** + */ +#define LAN91C96_FIFO_TX_DONE_PN (0x1FU << 0) +#define LAN91C96_FIFO_TEMPTY (0x1U << 7) +#define LAN91C96_FIFO_RX_DONE_PN (0x1FU << 8) +#define LAN91C96_FIFO_RXEMPTY (0x1U << 15) + +/* + **************************************************************************** + * Pointer Register - Bank 2 - Offset 6 + **************************************************************************** + */ +#define LAN91C96_PTR_LOW (0xFFU << 0) +#define LAN91C96_PTR_HIGH (0x7U << 8) +#define LAN91C96_PTR_AUTO_TX (0x1U << 11) +#define LAN91C96_PTR_ETEN (0x1U << 12) +#define LAN91C96_PTR_READ (0x1U << 13) +#define LAN91C96_PTR_AUTO_INCR (0x1U << 14) +#define LAN91C96_PTR_RCV (0x1U << 15) + +#define LAN91C96_PTR_RX_FRAME (LAN91C96_PTR_RCV | \ + LAN91C96_PTR_AUTO_INCR | \ + LAN91C96_PTR_READ) + +/* + **************************************************************************** + * Data Register - Bank 2 - Offset 8 + **************************************************************************** + */ +#define LAN91C96_CONTROL_CRC (0x1U << 4) /* CRC bit */ +#define LAN91C96_CONTROL_ODD (0x1U << 5) /* ODD bit */ + +/* + **************************************************************************** + * Interrupt Status Register - Bank 2 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_IST_RCV_INT (0x1U << 0) +#define LAN91C96_IST_TX_INT (0x1U << 1) +#define LAN91C96_IST_TX_EMPTY_INT (0x1U << 2) +#define LAN91C96_IST_ALLOC_INT (0x1U << 3) +#define LAN91C96_IST_RX_OVRN_INT (0x1U << 4) +#define LAN91C96_IST_EPH_INT (0x1U << 5) +#define LAN91C96_IST_ERCV_INT (0x1U << 6) +#define LAN91C96_IST_RX_IDLE_INT (0x1U << 7) + +/* + **************************************************************************** + * Interrupt Acknowledge Register - Bank 2 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_ACK_TX_INT (0x1U << 1) +#define LAN91C96_ACK_TX_EMPTY_INT (0x1U << 2) +#define LAN91C96_ACK_RX_OVRN_INT (0x1U << 4) +#define LAN91C96_ACK_ERCV_INT (0x1U << 6) + +/* + **************************************************************************** + * Interrupt Mask Register - Bank 2 - Offset 13 + **************************************************************************** + */ +#define LAN91C96_MSK_RCV_INT (0x1U << 0) +#define LAN91C96_MSK_TX_INT (0x1U << 1) +#define LAN91C96_MSK_TX_EMPTY_INT (0x1U << 2) +#define LAN91C96_MSK_ALLOC_INT (0x1U << 3) +#define LAN91C96_MSK_RX_OVRN_INT (0x1U << 4) +#define LAN91C96_MSK_EPH_INT (0x1U << 5) +#define LAN91C96_MSK_ERCV_INT (0x1U << 6) +#define LAN91C96_MSK_TX_IDLE_INT (0x1U << 7) + +/* + **************************************************************************** + * Bank 3 Register Map in I/O Space + ************************************************************************** + */ +#define LAN91C96_MGMT_MDO (0x1U << 0) +#define LAN91C96_MGMT_MDI (0x1U << 1) +#define LAN91C96_MGMT_MCLK (0x1U << 2) +#define LAN91C96_MGMT_MDOE (0x1U << 3) +#define LAN91C96_MGMT_LOW_ID (0x3U << 4) +#define LAN91C96_MGMT_IOS0 (0x1U << 8) +#define LAN91C96_MGMT_IOS1 (0x1U << 9) +#define LAN91C96_MGMT_IOS2 (0x1U << 10) +#define LAN91C96_MGMT_nXNDEC (0x1U << 11) +#define LAN91C96_MGMT_HIGH_ID (0x3U << 12) + +/* + **************************************************************************** + * Revision Register - Bank 3 - Offset 10 + **************************************************************************** + */ +#define LAN91C96_REV_REVID (0xFU << 0) +#define LAN91C96_REV_CHIPID (0xFU << 4) + +/* + **************************************************************************** + * Early RCV Register - Bank 3 - Offset 12 + **************************************************************************** + */ +#define LAN91C96_ERCV_THRESHOLD (0x1FU << 0) +#define LAN91C96_ERCV_RCV_DISCRD (0x1U << 7) + +/* + **************************************************************************** + * PCMCIA Configuration Registers + **************************************************************************** + */ +#define LAN91C96_ECOR 0x8000 /* Ethernet Configuration Register */ +#define LAN91C96_ECSR 0x8002 /* Ethernet Configuration and Status */ + +/* + **************************************************************************** + * PCMCIA Ethernet Configuration Option Register (ECOR) + **************************************************************************** + */ +#define LAN91C96_ECOR_ENABLE (0x1U << 0) +#define LAN91C96_ECOR_WR_ATTRIB (0x1U << 2) +#define LAN91C96_ECOR_LEVEL_REQ (0x1U << 6) +#define LAN91C96_ECOR_SRESET (0x1U << 7) + +/* + **************************************************************************** + * PCMCIA Ethernet Configuration and Status Register (ECSR) + **************************************************************************** + */ +#define LAN91C96_ECSR_INTR (0x1U << 1) +#define LAN91C96_ECSR_PWRDWN (0x1U << 2) +#define LAN91C96_ECSR_IOIS8 (0x1U << 5) + +/* + **************************************************************************** + * Receive Frame Status Word - See page 38 of the LAN91C96 specification. + **************************************************************************** + */ +#define LAN91C96_TOO_SHORT (0x1U << 10) +#define LAN91C96_TOO_LONG (0x1U << 11) +#define LAN91C96_ODD_FRM (0x1U << 12) +#define LAN91C96_BAD_CRC (0x1U << 13) +#define LAN91C96_BROD_CAST (0x1U << 14) +#define LAN91C96_ALGN_ERR (0x1U << 15) + +#define FRAME_FILTER (LAN91C96_TOO_SHORT | LAN91C96_TOO_LONG | LAN91C96_BAD_CRC | LAN91C96_ALGN_ERR) + +/* + **************************************************************************** + * Default MAC Address + **************************************************************************** + */ +#define MAC_DEF_HI 0x0800 +#define MAC_DEF_MED 0x3333 +#define MAC_DEF_LO 0x0100 + +/* + **************************************************************************** + * Default I/O Signature - 0x33 + **************************************************************************** + */ +#define LAN91C96_LOW_SIGNATURE (0x33U << 0) +#define LAN91C96_HIGH_SIGNATURE (0x33U << 8) +#define LAN91C96_SIGNATURE (LAN91C96_HIGH_SIGNATURE | LAN91C96_LOW_SIGNATURE) + +#define LAN91C96_MAX_PAGES 6 /* Maximum number of 256 pages. */ +#define ETHERNET_MAX_LENGTH 1514 + + +/*------------------------------------------------------------------------- + * I define some macros to make it easier to do somewhat common + * or slightly complicated, repeated tasks. + *------------------------------------------------------------------------- + */ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(edev, x) { SMC_outw(edev, x, LAN91C96_BANK_SELECT); } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(edev, x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(edev, 2);\ + mask = SMC_inb(edev, LAN91C96_INT_MASK);\ + mask |= (x);\ + SMC_outb(edev, mask, LAN91C96_INT_MASK); \ +} + +/* this disables an interrupt from the interrupt mask register */ + +#define SMC_DISABLE_INT(edev, x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(edev, 2);\ + mask = SMC_inb(edev, LAN91C96_INT_MASK);\ + mask &= ~(x);\ + SMC_outb(edev, mask, LAN91C96_INT_MASK); \ +} + +/*---------------------------------------------------------------------- + * Define the interrupts that I want to receive from the card + * + * I want: + * LAN91C96_IST_EPH_INT, for nasty errors + * LAN91C96_IST_RCV_INT, for happy received packets + * LAN91C96_IST_RX_OVRN_INT, because I have to kick the receiver + *------------------------------------------------------------------------- + */ +#define SMC_INTERRUPT_MASK (LAN91C96_IST_EPH_INT | LAN91C96_IST_RX_OVRN_INT | LAN91C96_IST_RCV_INT) + +#endif /* _LAN91C96_H_ */ diff --git a/roms/u-boot-sam460ex/drivers/net/macb.c b/roms/u-boot-sam460ex/drivers/net/macb.c new file mode 100644 index 000000000..dcb885023 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/macb.c @@ -0,0 +1,582 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <common.h> + +/* + * The u-boot networking stack is a little weird. It seems like the + * networking core allocates receive buffers up front without any + * regard to the hardware that's supposed to actually receive those + * packets. + * + * The MACB receives packets into 128-byte receive buffers, so the + * buffers allocated by the core isn't very practical to use. We'll + * allocate our own, but we need one such buffer in case a packet + * wraps around the DMA ring so that we have to copy it. + * + * Therefore, define CONFIG_SYS_RX_ETH_BUFFER to 1 in the board-specific + * configuration header. This way, the core allocates one RX buffer + * and one TX buffer, each of which can hold a ethernet packet of + * maximum size. + * + * For some reason, the networking core unconditionally specifies a + * 32-byte packet "alignment" (which really should be called + * "padding"). MACB shouldn't need that, but we'll refrain from any + * core modifications here... + */ + +#include <net.h> +#include <netdev.h> +#include <malloc.h> +#include <miiphy.h> + +#include <linux/mii.h> +#include <asm/io.h> +#include <asm/dma-mapping.h> +#include <asm/arch/clk.h> + +#include "macb.h" + +#define barrier() asm volatile("" ::: "memory") + +#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 +#define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) +#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_TIMEOUT 1000 +#define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000 + +struct macb_dma_desc { + u32 addr; + u32 ctrl; +}; + +#define RXADDR_USED 0x00000001 +#define RXADDR_WRAP 0x00000002 + +#define RXBUF_FRMLEN_MASK 0x00000fff +#define RXBUF_FRAME_START 0x00004000 +#define RXBUF_FRAME_END 0x00008000 +#define RXBUF_TYPEID_MATCH 0x00400000 +#define RXBUF_ADDR4_MATCH 0x00800000 +#define RXBUF_ADDR3_MATCH 0x01000000 +#define RXBUF_ADDR2_MATCH 0x02000000 +#define RXBUF_ADDR1_MATCH 0x04000000 +#define RXBUF_BROADCAST 0x80000000 + +#define TXBUF_FRMLEN_MASK 0x000007ff +#define TXBUF_FRAME_END 0x00008000 +#define TXBUF_NOCRC 0x00010000 +#define TXBUF_EXHAUSTED 0x08000000 +#define TXBUF_UNDERRUN 0x10000000 +#define TXBUF_MAXRETRY 0x20000000 +#define TXBUF_WRAP 0x40000000 +#define TXBUF_USED 0x80000000 + +struct macb_device { + void *regs; + + unsigned int rx_tail; + unsigned int tx_head; + unsigned int tx_tail; + + void *rx_buffer; + void *tx_buffer; + struct macb_dma_desc *rx_ring; + struct macb_dma_desc *tx_ring; + + unsigned long rx_buffer_dma; + unsigned long rx_ring_dma; + unsigned long tx_ring_dma; + + const struct device *dev; + struct eth_device netdev; + unsigned short phy_addr; +}; +#define to_macb(_nd) container_of(_nd, struct macb_device, netdev) + +static void macb_mdio_write(struct macb_device *macb, u8 reg, u16 value) +{ + unsigned long netctl; + unsigned long netstat; + unsigned long frame; + + netctl = macb_readl(macb, NCR); + netctl |= MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); + + frame = (MACB_BF(SOF, 1) + | MACB_BF(RW, 1) + | MACB_BF(PHYA, macb->phy_addr) + | MACB_BF(REGA, reg) + | MACB_BF(CODE, 2) + | MACB_BF(DATA, value)); + macb_writel(macb, MAN, frame); + + do { + netstat = macb_readl(macb, NSR); + } while (!(netstat & MACB_BIT(IDLE))); + + netctl = macb_readl(macb, NCR); + netctl &= ~MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); +} + +static u16 macb_mdio_read(struct macb_device *macb, u8 reg) +{ + unsigned long netctl; + unsigned long netstat; + unsigned long frame; + + netctl = macb_readl(macb, NCR); + netctl |= MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); + + frame = (MACB_BF(SOF, 1) + | MACB_BF(RW, 2) + | MACB_BF(PHYA, macb->phy_addr) + | MACB_BF(REGA, reg) + | MACB_BF(CODE, 2)); + macb_writel(macb, MAN, frame); + + do { + netstat = macb_readl(macb, NSR); + } while (!(netstat & MACB_BIT(IDLE))); + + frame = macb_readl(macb, MAN); + + netctl = macb_readl(macb, NCR); + netctl &= ~MACB_BIT(MPE); + macb_writel(macb, NCR, netctl); + + return MACB_BFEXT(DATA, frame); +} + +#if defined(CONFIG_CMD_MII) + +int macb_miiphy_read(char *devname, u8 phy_adr, u8 reg, u16 *value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct macb_device *macb = to_macb(dev); + + if ( macb->phy_addr != phy_adr ) + return -1; + + *value = macb_mdio_read(macb, reg); + + return 0; +} + +int macb_miiphy_write(char *devname, u8 phy_adr, u8 reg, u16 value) +{ + struct eth_device *dev = eth_get_dev_by_name(devname); + struct macb_device *macb = to_macb(dev); + + if ( macb->phy_addr != phy_adr ) + return -1; + + macb_mdio_write(macb, reg, value); + + return 0; +} +#endif + + +#if defined(CONFIG_CMD_NET) + +static int macb_send(struct eth_device *netdev, volatile void *packet, + int length) +{ + struct macb_device *macb = to_macb(netdev); + unsigned long paddr, ctrl; + unsigned int tx_head = macb->tx_head; + int i; + + paddr = dma_map_single(packet, length, DMA_TO_DEVICE); + + ctrl = length & TXBUF_FRMLEN_MASK; + ctrl |= TXBUF_FRAME_END; + if (tx_head == (CONFIG_SYS_MACB_TX_RING_SIZE - 1)) { + ctrl |= TXBUF_WRAP; + macb->tx_head = 0; + } else + macb->tx_head++; + + macb->tx_ring[tx_head].ctrl = ctrl; + macb->tx_ring[tx_head].addr = paddr; + barrier(); + macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART)); + + /* + * I guess this is necessary because the networking core may + * re-use the transmit buffer as soon as we return... + */ + for (i = 0; i <= CONFIG_SYS_MACB_TX_TIMEOUT; i++) { + barrier(); + ctrl = macb->tx_ring[tx_head].ctrl; + if (ctrl & TXBUF_USED) + break; + udelay(1); + } + + dma_unmap_single(packet, length, paddr); + + if (i <= CONFIG_SYS_MACB_TX_TIMEOUT) { + if (ctrl & TXBUF_UNDERRUN) + printf("%s: TX underrun\n", netdev->name); + if (ctrl & TXBUF_EXHAUSTED) + printf("%s: TX buffers exhausted in mid frame\n", + netdev->name); + } else { + printf("%s: TX timeout\n", netdev->name); + } + + /* No one cares anyway */ + return 0; +} + +static void reclaim_rx_buffers(struct macb_device *macb, + unsigned int new_tail) +{ + unsigned int i; + + i = macb->rx_tail; + while (i > new_tail) { + macb->rx_ring[i].addr &= ~RXADDR_USED; + i++; + if (i > CONFIG_SYS_MACB_RX_RING_SIZE) + i = 0; + } + + while (i < new_tail) { + macb->rx_ring[i].addr &= ~RXADDR_USED; + i++; + } + + barrier(); + macb->rx_tail = new_tail; +} + +static int macb_recv(struct eth_device *netdev) +{ + struct macb_device *macb = to_macb(netdev); + unsigned int rx_tail = macb->rx_tail; + void *buffer; + int length; + int wrapped = 0; + u32 status; + + for (;;) { + if (!(macb->rx_ring[rx_tail].addr & RXADDR_USED)) + return -1; + + status = macb->rx_ring[rx_tail].ctrl; + if (status & RXBUF_FRAME_START) { + if (rx_tail != macb->rx_tail) + reclaim_rx_buffers(macb, rx_tail); + wrapped = 0; + } + + if (status & RXBUF_FRAME_END) { + buffer = macb->rx_buffer + 128 * macb->rx_tail; + length = status & RXBUF_FRMLEN_MASK; + if (wrapped) { + unsigned int headlen, taillen; + + headlen = 128 * (CONFIG_SYS_MACB_RX_RING_SIZE + - macb->rx_tail); + taillen = length - headlen; + memcpy((void *)NetRxPackets[0], + buffer, headlen); + memcpy((void *)NetRxPackets[0] + headlen, + macb->rx_buffer, taillen); + buffer = (void *)NetRxPackets[0]; + } + + NetReceive(buffer, length); + if (++rx_tail >= CONFIG_SYS_MACB_RX_RING_SIZE) + rx_tail = 0; + reclaim_rx_buffers(macb, rx_tail); + } else { + if (++rx_tail >= CONFIG_SYS_MACB_RX_RING_SIZE) { + wrapped = 1; + rx_tail = 0; + } + } + barrier(); + } + + return 0; +} + +static void macb_phy_reset(struct macb_device *macb) +{ + struct eth_device *netdev = &macb->netdev; + int i; + u16 status, adv; + + adv = ADVERTISE_CSMA | ADVERTISE_ALL; + macb_mdio_write(macb, MII_ADVERTISE, adv); + printf("%s: Starting autonegotiation...\n", netdev->name); + macb_mdio_write(macb, MII_BMCR, (BMCR_ANENABLE + | BMCR_ANRESTART)); + + for (i = 0; i < CONFIG_SYS_MACB_AUTONEG_TIMEOUT / 100; i++) { + status = macb_mdio_read(macb, MII_BMSR); + if (status & BMSR_ANEGCOMPLETE) + break; + udelay(100); + } + + if (status & BMSR_ANEGCOMPLETE) + printf("%s: Autonegotiation complete\n", netdev->name); + else + printf("%s: Autonegotiation timed out (status=0x%04x)\n", + netdev->name, status); +} + +#ifdef CONFIG_MACB_SEARCH_PHY +static int macb_phy_find(struct macb_device *macb) +{ + int i; + u16 phy_id; + + /* Search for PHY... */ + for (i = 0; i < 32; i++) { + macb->phy_addr = i; + phy_id = macb_mdio_read(macb, MII_PHYSID1); + if (phy_id != 0xffff) { + printf("%s: PHY present at %d\n", macb->netdev.name, i); + return 1; + } + } + + /* PHY isn't up to snuff */ + printf("%s: PHY not found", macb->netdev.name); + + return 0; +} +#endif /* CONFIG_MACB_SEARCH_PHY */ + + +static int macb_phy_init(struct macb_device *macb) +{ + struct eth_device *netdev = &macb->netdev; + u32 ncfgr; + u16 phy_id, status, adv, lpa; + int media, speed, duplex; + int i; + +#ifdef CONFIG_MACB_SEARCH_PHY + /* Auto-detect phy_addr */ + if (!macb_phy_find(macb)) { + return 0; + } +#endif /* CONFIG_MACB_SEARCH_PHY */ + + /* Check if the PHY is up to snuff... */ + phy_id = macb_mdio_read(macb, MII_PHYSID1); + if (phy_id == 0xffff) { + printf("%s: No PHY present\n", netdev->name); + return 0; + } + + status = macb_mdio_read(macb, MII_BMSR); + if (!(status & BMSR_LSTATUS)) { + /* Try to re-negotiate if we don't have link already. */ + macb_phy_reset(macb); + + for (i = 0; i < CONFIG_SYS_MACB_AUTONEG_TIMEOUT / 100; i++) { + status = macb_mdio_read(macb, MII_BMSR); + if (status & BMSR_LSTATUS) + break; + udelay(100); + } + } + + if (!(status & BMSR_LSTATUS)) { + printf("%s: link down (status: 0x%04x)\n", + netdev->name, status); + return 0; + } else { + adv = macb_mdio_read(macb, MII_ADVERTISE); + lpa = macb_mdio_read(macb, MII_LPA); + media = mii_nway_result(lpa & adv); + speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) + ? 1 : 0); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n", + netdev->name, + speed ? "100" : "10", + duplex ? "full" : "half", + lpa); + + ncfgr = macb_readl(macb, NCFGR); + ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); + if (speed) + ncfgr |= MACB_BIT(SPD); + if (duplex) + ncfgr |= MACB_BIT(FD); + macb_writel(macb, NCFGR, ncfgr); + return 1; + } +} + +static int macb_init(struct eth_device *netdev, bd_t *bd) +{ + struct macb_device *macb = to_macb(netdev); + unsigned long paddr; + u32 hwaddr_bottom; + u16 hwaddr_top; + int i; + + /* + * macb_halt should have been called at some point before now, + * so we'll assume the controller is idle. + */ + + /* initialize DMA descriptors */ + paddr = macb->rx_buffer_dma; + for (i = 0; i < CONFIG_SYS_MACB_RX_RING_SIZE; i++) { + if (i == (CONFIG_SYS_MACB_RX_RING_SIZE - 1)) + paddr |= RXADDR_WRAP; + macb->rx_ring[i].addr = paddr; + macb->rx_ring[i].ctrl = 0; + paddr += 128; + } + for (i = 0; i < CONFIG_SYS_MACB_TX_RING_SIZE; i++) { + macb->tx_ring[i].addr = 0; + if (i == (CONFIG_SYS_MACB_TX_RING_SIZE - 1)) + macb->tx_ring[i].ctrl = TXBUF_USED | TXBUF_WRAP; + else + macb->tx_ring[i].ctrl = TXBUF_USED; + } + macb->rx_tail = macb->tx_head = macb->tx_tail = 0; + + macb_writel(macb, RBQP, macb->rx_ring_dma); + macb_writel(macb, TBQP, macb->tx_ring_dma); + + /* set hardware address */ + hwaddr_bottom = cpu_to_le32(*((u32 *)netdev->enetaddr)); + macb_writel(macb, SA1B, hwaddr_bottom); + hwaddr_top = cpu_to_le16(*((u16 *)(netdev->enetaddr + 4))); + macb_writel(macb, SA1T, hwaddr_top); + + /* choose RMII or MII mode. This depends on the board */ +#ifdef CONFIG_RMII +#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \ + defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \ + defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) + macb_writel(macb, USRIO, MACB_BIT(RMII) | MACB_BIT(CLKEN)); +#else + macb_writel(macb, USRIO, 0); +#endif +#else +#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \ + defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \ + defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45) + macb_writel(macb, USRIO, MACB_BIT(CLKEN)); +#else + macb_writel(macb, USRIO, MACB_BIT(MII)); +#endif +#endif /* CONFIG_RMII */ + + if (!macb_phy_init(macb)) + return -1; + + /* Enable TX and RX */ + macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE)); + + return 0; +} + +static void macb_halt(struct eth_device *netdev) +{ + struct macb_device *macb = to_macb(netdev); + u32 ncr, tsr; + + /* Halt the controller and wait for any ongoing transmission to end. */ + ncr = macb_readl(macb, NCR); + ncr |= MACB_BIT(THALT); + macb_writel(macb, NCR, ncr); + + do { + tsr = macb_readl(macb, TSR); + } while (tsr & MACB_BIT(TGO)); + + /* Disable TX and RX, and clear statistics */ + macb_writel(macb, NCR, MACB_BIT(CLRSTAT)); +} + +int macb_eth_initialize(int id, void *regs, unsigned int phy_addr) +{ + struct macb_device *macb; + struct eth_device *netdev; + unsigned long macb_hz; + u32 ncfgr; + + macb = malloc(sizeof(struct macb_device)); + if (!macb) { + printf("Error: Failed to allocate memory for MACB%d\n", id); + return -1; + } + memset(macb, 0, sizeof(struct macb_device)); + + netdev = &macb->netdev; + + macb->rx_buffer = dma_alloc_coherent(CONFIG_SYS_MACB_RX_BUFFER_SIZE, + &macb->rx_buffer_dma); + macb->rx_ring = dma_alloc_coherent(CONFIG_SYS_MACB_RX_RING_SIZE + * sizeof(struct macb_dma_desc), + &macb->rx_ring_dma); + macb->tx_ring = dma_alloc_coherent(CONFIG_SYS_MACB_TX_RING_SIZE + * sizeof(struct macb_dma_desc), + &macb->tx_ring_dma); + + macb->regs = regs; + macb->phy_addr = phy_addr; + + sprintf(netdev->name, "macb%d", id); + netdev->init = macb_init; + netdev->halt = macb_halt; + netdev->send = macb_send; + netdev->recv = macb_recv; + + /* + * Do some basic initialization so that we at least can talk + * to the PHY + */ + macb_hz = get_macb_pclk_rate(id); + if (macb_hz < 20000000) + ncfgr = MACB_BF(CLK, MACB_CLK_DIV8); + else if (macb_hz < 40000000) + ncfgr = MACB_BF(CLK, MACB_CLK_DIV16); + else if (macb_hz < 80000000) + ncfgr = MACB_BF(CLK, MACB_CLK_DIV32); + else + ncfgr = MACB_BF(CLK, MACB_CLK_DIV64); + + macb_writel(macb, NCFGR, ncfgr); + + eth_register(netdev); + +#if defined(CONFIG_CMD_MII) + miiphy_register(netdev->name, macb_miiphy_read, macb_miiphy_write); +#endif + return 0; +} + +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/macb.h b/roms/u-boot-sam460ex/drivers/net/macb.h new file mode 100644 index 000000000..f92a20c70 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/macb.h @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#ifndef __DRIVERS_MACB_H__ +#define __DRIVERS_MACB_H__ + +/* MACB register offsets */ +#define MACB_NCR 0x0000 +#define MACB_NCFGR 0x0004 +#define MACB_NSR 0x0008 +#define MACB_TSR 0x0014 +#define MACB_RBQP 0x0018 +#define MACB_TBQP 0x001c +#define MACB_RSR 0x0020 +#define MACB_ISR 0x0024 +#define MACB_IER 0x0028 +#define MACB_IDR 0x002c +#define MACB_IMR 0x0030 +#define MACB_MAN 0x0034 +#define MACB_PTR 0x0038 +#define MACB_PFR 0x003c +#define MACB_FTO 0x0040 +#define MACB_SCF 0x0044 +#define MACB_MCF 0x0048 +#define MACB_FRO 0x004c +#define MACB_FCSE 0x0050 +#define MACB_ALE 0x0054 +#define MACB_DTF 0x0058 +#define MACB_LCOL 0x005c +#define MACB_EXCOL 0x0060 +#define MACB_TUND 0x0064 +#define MACB_CSE 0x0068 +#define MACB_RRE 0x006c +#define MACB_ROVR 0x0070 +#define MACB_RSE 0x0074 +#define MACB_ELE 0x0078 +#define MACB_RJA 0x007c +#define MACB_USF 0x0080 +#define MACB_STE 0x0084 +#define MACB_RLE 0x0088 +#define MACB_TPF 0x008c +#define MACB_HRB 0x0090 +#define MACB_HRT 0x0094 +#define MACB_SA1B 0x0098 +#define MACB_SA1T 0x009c +#define MACB_SA2B 0x00a0 +#define MACB_SA2T 0x00a4 +#define MACB_SA3B 0x00a8 +#define MACB_SA3T 0x00ac +#define MACB_SA4B 0x00b0 +#define MACB_SA4T 0x00b4 +#define MACB_TID 0x00b8 +#define MACB_TPQ 0x00bc +#define MACB_USRIO 0x00c0 +#define MACB_WOL 0x00c4 + +/* Bitfields in NCR */ +#define MACB_LB_OFFSET 0 +#define MACB_LB_SIZE 1 +#define MACB_LLB_OFFSET 1 +#define MACB_LLB_SIZE 1 +#define MACB_RE_OFFSET 2 +#define MACB_RE_SIZE 1 +#define MACB_TE_OFFSET 3 +#define MACB_TE_SIZE 1 +#define MACB_MPE_OFFSET 4 +#define MACB_MPE_SIZE 1 +#define MACB_CLRSTAT_OFFSET 5 +#define MACB_CLRSTAT_SIZE 1 +#define MACB_INCSTAT_OFFSET 6 +#define MACB_INCSTAT_SIZE 1 +#define MACB_WESTAT_OFFSET 7 +#define MACB_WESTAT_SIZE 1 +#define MACB_BP_OFFSET 8 +#define MACB_BP_SIZE 1 +#define MACB_TSTART_OFFSET 9 +#define MACB_TSTART_SIZE 1 +#define MACB_THALT_OFFSET 10 +#define MACB_THALT_SIZE 1 +#define MACB_NCR_TPF_OFFSET 11 +#define MACB_NCR_TPF_SIZE 1 +#define MACB_TZQ_OFFSET 12 +#define MACB_TZQ_SIZE 1 + +/* Bitfields in NCFGR */ +#define MACB_SPD_OFFSET 0 +#define MACB_SPD_SIZE 1 +#define MACB_FD_OFFSET 1 +#define MACB_FD_SIZE 1 +#define MACB_BIT_RATE_OFFSET 2 +#define MACB_BIT_RATE_SIZE 1 +#define MACB_JFRAME_OFFSET 3 +#define MACB_JFRAME_SIZE 1 +#define MACB_CAF_OFFSET 4 +#define MACB_CAF_SIZE 1 +#define MACB_NBC_OFFSET 5 +#define MACB_NBC_SIZE 1 +#define MACB_NCFGR_MTI_OFFSET 6 +#define MACB_NCFGR_MTI_SIZE 1 +#define MACB_UNI_OFFSET 7 +#define MACB_UNI_SIZE 1 +#define MACB_BIG_OFFSET 8 +#define MACB_BIG_SIZE 1 +#define MACB_EAE_OFFSET 9 +#define MACB_EAE_SIZE 1 +#define MACB_CLK_OFFSET 10 +#define MACB_CLK_SIZE 2 +#define MACB_RTY_OFFSET 12 +#define MACB_RTY_SIZE 1 +#define MACB_PAE_OFFSET 13 +#define MACB_PAE_SIZE 1 +#define MACB_RBOF_OFFSET 14 +#define MACB_RBOF_SIZE 2 +#define MACB_RLCE_OFFSET 16 +#define MACB_RLCE_SIZE 1 +#define MACB_DRFCS_OFFSET 17 +#define MACB_DRFCS_SIZE 1 +#define MACB_EFRHD_OFFSET 18 +#define MACB_EFRHD_SIZE 1 +#define MACB_IRXFCS_OFFSET 19 +#define MACB_IRXFCS_SIZE 1 + +/* Bitfields in NSR */ +#define MACB_NSR_LINK_OFFSET 0 +#define MACB_NSR_LINK_SIZE 1 +#define MACB_MDIO_OFFSET 1 +#define MACB_MDIO_SIZE 1 +#define MACB_IDLE_OFFSET 2 +#define MACB_IDLE_SIZE 1 + +/* Bitfields in TSR */ +#define MACB_UBR_OFFSET 0 +#define MACB_UBR_SIZE 1 +#define MACB_COL_OFFSET 1 +#define MACB_COL_SIZE 1 +#define MACB_TSR_RLE_OFFSET 2 +#define MACB_TSR_RLE_SIZE 1 +#define MACB_TGO_OFFSET 3 +#define MACB_TGO_SIZE 1 +#define MACB_BEX_OFFSET 4 +#define MACB_BEX_SIZE 1 +#define MACB_COMP_OFFSET 5 +#define MACB_COMP_SIZE 1 +#define MACB_UND_OFFSET 6 +#define MACB_UND_SIZE 1 + +/* Bitfields in RSR */ +#define MACB_BNA_OFFSET 0 +#define MACB_BNA_SIZE 1 +#define MACB_REC_OFFSET 1 +#define MACB_REC_SIZE 1 +#define MACB_OVR_OFFSET 2 +#define MACB_OVR_SIZE 1 + +/* Bitfields in ISR/IER/IDR/IMR */ +#define MACB_MFD_OFFSET 0 +#define MACB_MFD_SIZE 1 +#define MACB_RCOMP_OFFSET 1 +#define MACB_RCOMP_SIZE 1 +#define MACB_RXUBR_OFFSET 2 +#define MACB_RXUBR_SIZE 1 +#define MACB_TXUBR_OFFSET 3 +#define MACB_TXUBR_SIZE 1 +#define MACB_ISR_TUND_OFFSET 4 +#define MACB_ISR_TUND_SIZE 1 +#define MACB_ISR_RLE_OFFSET 5 +#define MACB_ISR_RLE_SIZE 1 +#define MACB_TXERR_OFFSET 6 +#define MACB_TXERR_SIZE 1 +#define MACB_TCOMP_OFFSET 7 +#define MACB_TCOMP_SIZE 1 +#define MACB_ISR_LINK_OFFSET 9 +#define MACB_ISR_LINK_SIZE 1 +#define MACB_ISR_ROVR_OFFSET 10 +#define MACB_ISR_ROVR_SIZE 1 +#define MACB_HRESP_OFFSET 11 +#define MACB_HRESP_SIZE 1 +#define MACB_PFR_OFFSET 12 +#define MACB_PFR_SIZE 1 +#define MACB_PTZ_OFFSET 13 +#define MACB_PTZ_SIZE 1 + +/* Bitfields in MAN */ +#define MACB_DATA_OFFSET 0 +#define MACB_DATA_SIZE 16 +#define MACB_CODE_OFFSET 16 +#define MACB_CODE_SIZE 2 +#define MACB_REGA_OFFSET 18 +#define MACB_REGA_SIZE 5 +#define MACB_PHYA_OFFSET 23 +#define MACB_PHYA_SIZE 5 +#define MACB_RW_OFFSET 28 +#define MACB_RW_SIZE 2 +#define MACB_SOF_OFFSET 30 +#define MACB_SOF_SIZE 2 + +/* Bitfields in USRIO */ +#define MACB_MII_OFFSET 0 +#define MACB_MII_SIZE 1 +#define MACB_EAM_OFFSET 1 +#define MACB_EAM_SIZE 1 +#define MACB_TX_PAUSE_OFFSET 2 +#define MACB_TX_PAUSE_SIZE 1 +#define MACB_TX_PAUSE_ZERO_OFFSET 3 +#define MACB_TX_PAUSE_ZERO_SIZE 1 + +/* Bitfields in USRIO (AT91) */ +#define MACB_RMII_OFFSET 0 +#define MACB_RMII_SIZE 1 +#define MACB_CLKEN_OFFSET 1 +#define MACB_CLKEN_SIZE 1 + +/* Bitfields in WOL */ +#define MACB_IP_OFFSET 0 +#define MACB_IP_SIZE 16 +#define MACB_MAG_OFFSET 16 +#define MACB_MAG_SIZE 1 +#define MACB_ARP_OFFSET 17 +#define MACB_ARP_SIZE 1 +#define MACB_SA1_OFFSET 18 +#define MACB_SA1_SIZE 1 +#define MACB_WOL_MTI_OFFSET 19 +#define MACB_WOL_MTI_SIZE 1 + +/* Constants for CLK */ +#define MACB_CLK_DIV8 0 +#define MACB_CLK_DIV16 1 +#define MACB_CLK_DIV32 2 +#define MACB_CLK_DIV64 3 + +/* Constants for MAN register */ +#define MACB_MAN_SOF 1 +#define MACB_MAN_WRITE 1 +#define MACB_MAN_READ 2 +#define MACB_MAN_CODE 2 + +/* Bit manipulation macros */ +#define MACB_BIT(name) \ + (1 << MACB_##name##_OFFSET) +#define MACB_BF(name,value) \ + (((value) & ((1 << MACB_##name##_SIZE) - 1)) \ + << MACB_##name##_OFFSET) +#define MACB_BFEXT(name,value)\ + (((value) >> MACB_##name##_OFFSET) \ + & ((1 << MACB_##name##_SIZE) - 1)) +#define MACB_BFINS(name,value,old) \ + (((old) & ~(((1 << MACB_##name##_SIZE) - 1) \ + << MACB_##name##_OFFSET)) \ + | MACB_BF(name,value)) + +/* Register access macros */ +#define macb_readl(port,reg) \ + readl((port)->regs + MACB_##reg) +#define macb_writel(port,reg,value) \ + writel((value), (port)->regs + MACB_##reg) + +#endif /* __DRIVERS_MACB_H__ */ diff --git a/roms/u-boot-sam460ex/drivers/net/mcffec.c b/roms/u-boot-sam460ex/drivers/net/mcffec.c new file mode 100644 index 000000000..64be5de52 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/mcffec.c @@ -0,0 +1,626 @@ +/* + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * (C) Copyright 2007 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> + +#include <command.h> +#include <net.h> +#include <netdev.h> +#include <miiphy.h> + +#include <asm/fec.h> +#include <asm/immap.h> + +#undef ET_DEBUG +#undef MII_DEBUG + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH 1520 +#define TX_BUF_CNT 2 +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 +#define LAST_PKTBUFSRX PKTBUFSRX - 1 +#define BD_ENET_RX_W_E (BD_ENET_RX_WRAP | BD_ENET_RX_EMPTY) +#define BD_ENET_TX_RDY_LST (BD_ENET_TX_READY | BD_ENET_TX_LAST) + +DECLARE_GLOBAL_DATA_PTR; + +struct fec_info_s fec_info[] = { +#ifdef CONFIG_SYS_FEC0_IOBASE + { + 0, /* index */ + CONFIG_SYS_FEC0_IOBASE, /* io base */ + CONFIG_SYS_FEC0_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC0_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phyname init */ + 0, /* RX BD */ + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_s *)-1, + }, +#endif +#ifdef CONFIG_SYS_FEC1_IOBASE + { + 1, /* index */ + CONFIG_SYS_FEC1_IOBASE, /* io base */ + CONFIG_SYS_FEC1_PINMUX, /* gpio pin muxing */ + CONFIG_SYS_FEC1_MIIBASE, /* mii base */ + -1, /* phy_addr */ + 0, /* duplex and speed */ + 0, /* phy name */ + 0, /* phy name init */ +#ifdef CONFIG_SYS_FEC_BUF_USE_SRAM + (cbd_t *)DBUF_LENGTH, /* RX BD */ +#else + 0, /* RX BD */ +#endif + 0, /* TX BD */ + 0, /* rx Index */ + 0, /* tx Index */ + 0, /* tx buffer */ + 0, /* initialized flag */ + (struct fec_info_s *)-1, + } +#endif +}; + +int fec_send(struct eth_device *dev, volatile void *packet, int length); +int fec_recv(struct eth_device *dev); +int fec_init(struct eth_device *dev, bd_t * bd); +void fec_halt(struct eth_device *dev); +void fec_reset(struct eth_device *dev); + +void setFecDuplexSpeed(volatile fec_t * fecp, bd_t * bd, int dup_spd) +{ + if ((dup_spd >> 16) == FULL) { + /* Set maximum frame length */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE | + FEC_RCR_PROM | 0x100; + fecp->tcr = FEC_TCR_FDEN; + } else { + /* Half duplex mode */ + fecp->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | + FEC_RCR_MII_MODE | FEC_RCR_DRT; + fecp->tcr &= ~FEC_TCR_FDEN; + } + + if ((dup_spd & 0xFFFF) == _100BASET) { +#ifdef CONFIG_MCF5445x + fecp->rcr &= ~0x200; /* disabled 10T base */ +#endif +#ifdef MII_DEBUG + printf("100Mbps\n"); +#endif + bd->bi_ethspeed = 100; + } else { +#ifdef CONFIG_MCF5445x + fecp->rcr |= 0x200; /* enabled 10T base */ +#endif +#ifdef MII_DEBUG + printf("10Mbps\n"); +#endif + bd->bi_ethspeed = 10; + } +} + +int fec_send(struct eth_device *dev, volatile void *packet, int length) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int j, rc; + u16 phyStatus; + + miiphy_read(dev->name, info->phy_addr, PHY_BMSR, &phyStatus); + + /* section 16.9.23.3 + * Wait for ready + */ + j = 0; + while ((info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_READY) && + (j < MCFFEC_TOUT_LOOP)) { + udelay(1); + j++; + } + if (j >= MCFFEC_TOUT_LOOP) { + printf("TX not ready\n"); + } + + info->txbd[info->txIdx].cbd_bufaddr = (uint) packet; + info->txbd[info->txIdx].cbd_datlen = length; + info->txbd[info->txIdx].cbd_sc |= BD_ENET_TX_RDY_LST; + + /* Activate transmit Buffer Descriptor polling */ + fecp->tdar = 0x01000000; /* Descriptor polling active */ + +#ifndef CONFIG_SYS_FEC_BUF_USE_SRAM + /* + * FEC unable to initial transmit data packet. + * A nop will ensure the descriptor polling active completed. + * CF Internal RAM has shorter cycle access than DRAM. If use + * DRAM as Buffer descriptor and data, a nop is a must. + * Affect only V2 and V3. + */ + __asm__ ("nop"); + +#endif + +#ifdef CONFIG_SYS_UNIFY_CACHE + icache_invalid(); +#endif + + j = 0; + while ((info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_READY) && + (j < MCFFEC_TOUT_LOOP)) { + udelay(1); + j++; + } + if (j >= MCFFEC_TOUT_LOOP) { + printf("TX timeout\n"); + } + +#ifdef ET_DEBUG + printf("%s[%d] %s: cycles: %d status: %x retry cnt: %d\n", + __FILE__, __LINE__, __FUNCTION__, j, + info->txbd[info->txIdx].cbd_sc, + (info->txbd[info->txIdx].cbd_sc & 0x003C) >> 2); +#endif + + /* return only status bits */ + rc = (info->txbd[info->txIdx].cbd_sc & BD_ENET_TX_STATS); + info->txIdx = (info->txIdx + 1) % TX_BUF_CNT; + + return rc; +} + +int fec_recv(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int length; + + for (;;) { +#ifndef CONFIG_SYS_FEC_BUF_USE_SRAM +#endif +#ifdef CONFIG_SYS_UNIFY_CACHE + icache_invalid(); +#endif + /* section 16.9.23.2 */ + if (info->rxbd[info->rxIdx].cbd_sc & BD_ENET_RX_EMPTY) { + length = -1; + break; /* nothing received - leave for() loop */ + } + + length = info->rxbd[info->rxIdx].cbd_datlen; + + if (info->rxbd[info->rxIdx].cbd_sc & 0x003f) { + printf("%s[%d] err: %x\n", + __FUNCTION__, __LINE__, + info->rxbd[info->rxIdx].cbd_sc); +#ifdef ET_DEBUG + printf("%s[%d] err: %x\n", + __FUNCTION__, __LINE__, + info->rxbd[info->rxIdx].cbd_sc); +#endif + } else { + + length -= 4; + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[info->rxIdx], length); + + fecp->eir |= FEC_EIR_RXF; + } + + /* Give the buffer back to the FEC. */ + info->rxbd[info->rxIdx].cbd_datlen = 0; + + /* wrap around buffer index when necessary */ + if (info->rxIdx == LAST_PKTBUFSRX) { + info->rxbd[PKTBUFSRX - 1].cbd_sc = BD_ENET_RX_W_E; + info->rxIdx = 0; + } else { + info->rxbd[info->rxIdx].cbd_sc = BD_ENET_RX_EMPTY; + info->rxIdx++; + } + + /* Try to fill Buffer Descriptors */ + fecp->rdar = 0x01000000; /* Descriptor polling active */ + } + + return length; +} + +#ifdef ET_DEBUG +void dbgFecRegs(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + + printf("=====\n"); + printf("ievent %x - %x\n", (int)&fecp->eir, fecp->eir); + printf("imask %x - %x\n", (int)&fecp->eimr, fecp->eimr); + printf("r_des_active %x - %x\n", (int)&fecp->rdar, fecp->rdar); + printf("x_des_active %x - %x\n", (int)&fecp->tdar, fecp->tdar); + printf("ecntrl %x - %x\n", (int)&fecp->ecr, fecp->ecr); + printf("mii_mframe %x - %x\n", (int)&fecp->mmfr, fecp->mmfr); + printf("mii_speed %x - %x\n", (int)&fecp->mscr, fecp->mscr); + printf("mii_ctrlstat %x - %x\n", (int)&fecp->mibc, fecp->mibc); + printf("r_cntrl %x - %x\n", (int)&fecp->rcr, fecp->rcr); + printf("x_cntrl %x - %x\n", (int)&fecp->tcr, fecp->tcr); + printf("padr_l %x - %x\n", (int)&fecp->palr, fecp->palr); + printf("padr_u %x - %x\n", (int)&fecp->paur, fecp->paur); + printf("op_pause %x - %x\n", (int)&fecp->opd, fecp->opd); + printf("iadr_u %x - %x\n", (int)&fecp->iaur, fecp->iaur); + printf("iadr_l %x - %x\n", (int)&fecp->ialr, fecp->ialr); + printf("gadr_u %x - %x\n", (int)&fecp->gaur, fecp->gaur); + printf("gadr_l %x - %x\n", (int)&fecp->galr, fecp->galr); + printf("x_wmrk %x - %x\n", (int)&fecp->tfwr, fecp->tfwr); + printf("r_bound %x - %x\n", (int)&fecp->frbr, fecp->frbr); + printf("r_fstart %x - %x\n", (int)&fecp->frsr, fecp->frsr); + printf("r_drng %x - %x\n", (int)&fecp->erdsr, fecp->erdsr); + printf("x_drng %x - %x\n", (int)&fecp->etdsr, fecp->etdsr); + printf("r_bufsz %x - %x\n", (int)&fecp->emrbr, fecp->emrbr); + + printf("\n"); + printf("rmon_t_drop %x - %x\n", (int)&fecp->rmon_t_drop, + fecp->rmon_t_drop); + printf("rmon_t_packets %x - %x\n", (int)&fecp->rmon_t_packets, + fecp->rmon_t_packets); + printf("rmon_t_bc_pkt %x - %x\n", (int)&fecp->rmon_t_bc_pkt, + fecp->rmon_t_bc_pkt); + printf("rmon_t_mc_pkt %x - %x\n", (int)&fecp->rmon_t_mc_pkt, + fecp->rmon_t_mc_pkt); + printf("rmon_t_crc_align %x - %x\n", (int)&fecp->rmon_t_crc_align, + fecp->rmon_t_crc_align); + printf("rmon_t_undersize %x - %x\n", (int)&fecp->rmon_t_undersize, + fecp->rmon_t_undersize); + printf("rmon_t_oversize %x - %x\n", (int)&fecp->rmon_t_oversize, + fecp->rmon_t_oversize); + printf("rmon_t_frag %x - %x\n", (int)&fecp->rmon_t_frag, + fecp->rmon_t_frag); + printf("rmon_t_jab %x - %x\n", (int)&fecp->rmon_t_jab, + fecp->rmon_t_jab); + printf("rmon_t_col %x - %x\n", (int)&fecp->rmon_t_col, + fecp->rmon_t_col); + printf("rmon_t_p64 %x - %x\n", (int)&fecp->rmon_t_p64, + fecp->rmon_t_p64); + printf("rmon_t_p65to127 %x - %x\n", (int)&fecp->rmon_t_p65to127, + fecp->rmon_t_p65to127); + printf("rmon_t_p128to255 %x - %x\n", (int)&fecp->rmon_t_p128to255, + fecp->rmon_t_p128to255); + printf("rmon_t_p256to511 %x - %x\n", (int)&fecp->rmon_t_p256to511, + fecp->rmon_t_p256to511); + printf("rmon_t_p512to1023 %x - %x\n", (int)&fecp->rmon_t_p512to1023, + fecp->rmon_t_p512to1023); + printf("rmon_t_p1024to2047 %x - %x\n", (int)&fecp->rmon_t_p1024to2047, + fecp->rmon_t_p1024to2047); + printf("rmon_t_p_gte2048 %x - %x\n", (int)&fecp->rmon_t_p_gte2048, + fecp->rmon_t_p_gte2048); + printf("rmon_t_octets %x - %x\n", (int)&fecp->rmon_t_octets, + fecp->rmon_t_octets); + + printf("\n"); + printf("ieee_t_drop %x - %x\n", (int)&fecp->ieee_t_drop, + fecp->ieee_t_drop); + printf("ieee_t_frame_ok %x - %x\n", (int)&fecp->ieee_t_frame_ok, + fecp->ieee_t_frame_ok); + printf("ieee_t_1col %x - %x\n", (int)&fecp->ieee_t_1col, + fecp->ieee_t_1col); + printf("ieee_t_mcol %x - %x\n", (int)&fecp->ieee_t_mcol, + fecp->ieee_t_mcol); + printf("ieee_t_def %x - %x\n", (int)&fecp->ieee_t_def, + fecp->ieee_t_def); + printf("ieee_t_lcol %x - %x\n", (int)&fecp->ieee_t_lcol, + fecp->ieee_t_lcol); + printf("ieee_t_excol %x - %x\n", (int)&fecp->ieee_t_excol, + fecp->ieee_t_excol); + printf("ieee_t_macerr %x - %x\n", (int)&fecp->ieee_t_macerr, + fecp->ieee_t_macerr); + printf("ieee_t_cserr %x - %x\n", (int)&fecp->ieee_t_cserr, + fecp->ieee_t_cserr); + printf("ieee_t_sqe %x - %x\n", (int)&fecp->ieee_t_sqe, + fecp->ieee_t_sqe); + printf("ieee_t_fdxfc %x - %x\n", (int)&fecp->ieee_t_fdxfc, + fecp->ieee_t_fdxfc); + printf("ieee_t_octets_ok %x - %x\n", (int)&fecp->ieee_t_octets_ok, + fecp->ieee_t_octets_ok); + + printf("\n"); + printf("rmon_r_drop %x - %x\n", (int)&fecp->rmon_r_drop, + fecp->rmon_r_drop); + printf("rmon_r_packets %x - %x\n", (int)&fecp->rmon_r_packets, + fecp->rmon_r_packets); + printf("rmon_r_bc_pkt %x - %x\n", (int)&fecp->rmon_r_bc_pkt, + fecp->rmon_r_bc_pkt); + printf("rmon_r_mc_pkt %x - %x\n", (int)&fecp->rmon_r_mc_pkt, + fecp->rmon_r_mc_pkt); + printf("rmon_r_crc_align %x - %x\n", (int)&fecp->rmon_r_crc_align, + fecp->rmon_r_crc_align); + printf("rmon_r_undersize %x - %x\n", (int)&fecp->rmon_r_undersize, + fecp->rmon_r_undersize); + printf("rmon_r_oversize %x - %x\n", (int)&fecp->rmon_r_oversize, + fecp->rmon_r_oversize); + printf("rmon_r_frag %x - %x\n", (int)&fecp->rmon_r_frag, + fecp->rmon_r_frag); + printf("rmon_r_jab %x - %x\n", (int)&fecp->rmon_r_jab, + fecp->rmon_r_jab); + printf("rmon_r_p64 %x - %x\n", (int)&fecp->rmon_r_p64, + fecp->rmon_r_p64); + printf("rmon_r_p65to127 %x - %x\n", (int)&fecp->rmon_r_p65to127, + fecp->rmon_r_p65to127); + printf("rmon_r_p128to255 %x - %x\n", (int)&fecp->rmon_r_p128to255, + fecp->rmon_r_p128to255); + printf("rmon_r_p256to511 %x - %x\n", (int)&fecp->rmon_r_p256to511, + fecp->rmon_r_p256to511); + printf("rmon_r_p512to1023 %x - %x\n", (int)&fecp->rmon_r_p512to1023, + fecp->rmon_r_p512to1023); + printf("rmon_r_p1024to2047 %x - %x\n", (int)&fecp->rmon_r_p1024to2047, + fecp->rmon_r_p1024to2047); + printf("rmon_r_p_gte2048 %x - %x\n", (int)&fecp->rmon_r_p_gte2048, + fecp->rmon_r_p_gte2048); + printf("rmon_r_octets %x - %x\n", (int)&fecp->rmon_r_octets, + fecp->rmon_r_octets); + + printf("\n"); + printf("ieee_r_drop %x - %x\n", (int)&fecp->ieee_r_drop, + fecp->ieee_r_drop); + printf("ieee_r_frame_ok %x - %x\n", (int)&fecp->ieee_r_frame_ok, + fecp->ieee_r_frame_ok); + printf("ieee_r_crc %x - %x\n", (int)&fecp->ieee_r_crc, + fecp->ieee_r_crc); + printf("ieee_r_align %x - %x\n", (int)&fecp->ieee_r_align, + fecp->ieee_r_align); + printf("ieee_r_macerr %x - %x\n", (int)&fecp->ieee_r_macerr, + fecp->ieee_r_macerr); + printf("ieee_r_fdxfc %x - %x\n", (int)&fecp->ieee_r_fdxfc, + fecp->ieee_r_fdxfc); + printf("ieee_r_octets_ok %x - %x\n", (int)&fecp->ieee_r_octets_ok, + fecp->ieee_r_octets_ok); + + printf("\n\n\n"); +} +#endif + +int fec_init(struct eth_device *dev, bd_t * bd) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int i; + uchar ea[6]; + + fecpin_setclear(dev, 1); + + fec_reset(dev); + +#if defined(CONFIG_CMD_MII) || defined (CONFIG_MII) || \ + defined (CONFIG_SYS_DISCOVER_PHY) + + mii_init(); + + setFecDuplexSpeed(fecp, bd, info->dup_spd); +#else +#ifndef CONFIG_SYS_DISCOVER_PHY + setFecDuplexSpeed(fecp, bd, (FECDUPLEX << 16) | FECSPEED); +#endif /* ifndef CONFIG_SYS_DISCOVER_PHY */ +#endif /* CONFIG_CMD_MII || CONFIG_MII */ + + /* We use strictly polling mode only */ + fecp->eimr = 0; + + /* Clear any pending interrupt */ + fecp->eir = 0xffffffff; + + /* Set station address */ + if ((u32) fecp == CONFIG_SYS_FEC0_IOBASE) { +#ifdef CONFIG_SYS_FEC1_IOBASE + volatile fec_t *fecp1 = (fec_t *) (CONFIG_SYS_FEC1_IOBASE); + eth_getenv_enetaddr("eth1addr", ea); + fecp1->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp1->paur = (ea[4] << 24) | (ea[5] << 16); +#endif + eth_getenv_enetaddr("ethaddr", ea); + fecp->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp->paur = (ea[4] << 24) | (ea[5] << 16); + } else { +#ifdef CONFIG_SYS_FEC0_IOBASE + volatile fec_t *fecp0 = (fec_t *) (CONFIG_SYS_FEC0_IOBASE); + eth_getenv_enetaddr("ethaddr", ea); + fecp0->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp0->paur = (ea[4] << 24) | (ea[5] << 16); +#endif +#ifdef CONFIG_SYS_FEC1_IOBASE + eth_getenv_enetaddr("eth1addr", ea); + fecp->palr = + (ea[0] << 24) | (ea[1] << 16) | (ea[2] << 8) | (ea[3]); + fecp->paur = (ea[4] << 24) | (ea[5] << 16); +#endif + } + + /* Clear unicast address hash table */ + fecp->iaur = 0; + fecp->ialr = 0; + + /* Clear multicast address hash table */ + fecp->gaur = 0; + fecp->galr = 0; + + /* Set maximum receive buffer size. */ + fecp->emrbr = PKT_MAXBLR_SIZE; + + /* + * Setup Buffers and Buffer Desriptors + */ + info->rxIdx = 0; + info->txIdx = 0; + + /* + * Setup Receiver Buffer Descriptors (13.14.24.18) + * Settings: + * Empty, Wrap + */ + for (i = 0; i < PKTBUFSRX; i++) { + info->rxbd[i].cbd_sc = BD_ENET_RX_EMPTY; + info->rxbd[i].cbd_datlen = 0; /* Reset */ + info->rxbd[i].cbd_bufaddr = (uint) NetRxPackets[i]; + } + info->rxbd[PKTBUFSRX - 1].cbd_sc |= BD_ENET_RX_WRAP; + + /* + * Setup Ethernet Transmitter Buffer Descriptors (13.14.24.19) + * Settings: + * Last, Tx CRC + */ + for (i = 0; i < TX_BUF_CNT; i++) { + info->txbd[i].cbd_sc = BD_ENET_TX_LAST | BD_ENET_TX_TC; + info->txbd[i].cbd_datlen = 0; /* Reset */ + info->txbd[i].cbd_bufaddr = (uint) (&info->txbuf[0]); + } + info->txbd[TX_BUF_CNT - 1].cbd_sc |= BD_ENET_TX_WRAP; + + /* Set receive and transmit descriptor base */ + fecp->erdsr = (unsigned int)(&info->rxbd[0]); + fecp->etdsr = (unsigned int)(&info->txbd[0]); + + /* Now enable the transmit and receive processing */ + fecp->ecr |= FEC_ECR_ETHER_EN; + + /* And last, try to fill Rx Buffer Descriptors */ + fecp->rdar = 0x01000000; /* Descriptor polling active */ + + return 1; +} + +void fec_reset(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + volatile fec_t *fecp = (fec_t *) (info->iobase); + int i; + + fecp->ecr = FEC_ECR_RESET; + for (i = 0; (fecp->ecr & FEC_ECR_RESET) && (i < FEC_RESET_DELAY); ++i) { + udelay(1); + } + if (i == FEC_RESET_DELAY) { + printf("FEC_RESET_DELAY timeout\n"); + } +} + +void fec_halt(struct eth_device *dev) +{ + struct fec_info_s *info = dev->priv; + + fec_reset(dev); + + fecpin_setclear(dev, 0); + + info->rxIdx = info->txIdx = 0; + memset(info->rxbd, 0, PKTBUFSRX * sizeof(cbd_t)); + memset(info->txbd, 0, TX_BUF_CNT * sizeof(cbd_t)); + memset(info->txbuf, 0, DBUF_LENGTH); +} + +int mcffec_initialize(bd_t * bis) +{ + struct eth_device *dev; + int i; +#ifdef CONFIG_SYS_FEC_BUF_USE_SRAM + u32 tmp = CONFIG_SYS_INIT_RAM_ADDR + 0x1000; +#endif + + for (i = 0; i < sizeof(fec_info) / sizeof(fec_info[0]); i++) { + + dev = + (struct eth_device *)memalign(CONFIG_SYS_CACHELINE_SIZE, + sizeof *dev); + if (dev == NULL) + hang(); + + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "FEC%d", fec_info[i].index); + + dev->priv = &fec_info[i]; + dev->init = fec_init; + dev->halt = fec_halt; + dev->send = fec_send; + dev->recv = fec_recv; + + /* setup Receive and Transmit buffer descriptor */ +#ifdef CONFIG_SYS_FEC_BUF_USE_SRAM + fec_info[i].rxbd = (cbd_t *)((u32)fec_info[i].rxbd + tmp); + tmp = (u32)fec_info[i].rxbd; + fec_info[i].txbd = + (cbd_t *)((u32)fec_info[i].txbd + tmp + + (PKTBUFSRX * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbd; + fec_info[i].txbuf = + (char *)((u32)fec_info[i].txbuf + tmp + + (CONFIG_SYS_TX_ETH_BUFFER * sizeof(cbd_t))); + tmp = (u32)fec_info[i].txbuf; +#else + fec_info[i].rxbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (PKTBUFSRX * sizeof(cbd_t))); + fec_info[i].txbd = + (cbd_t *) memalign(CONFIG_SYS_CACHELINE_SIZE, + (TX_BUF_CNT * sizeof(cbd_t))); + fec_info[i].txbuf = + (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, DBUF_LENGTH); +#endif + +#ifdef ET_DEBUG + printf("rxbd %x txbd %x\n", + (int)fec_info[i].rxbd, (int)fec_info[i].txbd); +#endif + + fec_info[i].phy_name = (char *)memalign(CONFIG_SYS_CACHELINE_SIZE, 32); + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, + mcffec_miiphy_read, mcffec_miiphy_write); +#endif + if (i > 0) + fec_info[i - 1].next = &fec_info[i]; + } + fec_info[i - 1].next = &fec_info[0]; + + /* default speed */ + bis->bi_ethspeed = 10; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/mcfmii.c b/roms/u-boot-sam460ex/drivers/net/mcfmii.c new file mode 100644 index 000000000..060bdd739 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/mcfmii.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2004-2008 Freescale Semiconductor, Inc. + * TsiChung Liew (Tsi-Chung.Liew@freescale.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <config.h> +#include <net.h> +#include <netdev.h> + +#ifdef CONFIG_MCF547x_8x +#include <asm/fsl_mcdmafec.h> +#else +#include <asm/fec.h> +#endif +#include <asm/immap.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) +#undef MII_DEBUG +#undef ET_DEBUG + +/*extern int fecpin_setclear(struct eth_device *dev, int setclear);*/ + +#if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_CMD_MII) +#include <miiphy.h> + +/* Make MII read/write commands for the FEC. */ +#define mk_mii_read(ADDR, REG) (0x60020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18)) +#define mk_mii_write(ADDR, REG, VAL) (0x50020000 | ((ADDR << 23) | \ + (REG & 0x1f) << 18) | (VAL & 0xffff)) + +#ifndef CONFIG_SYS_UNSPEC_PHYID +# define CONFIG_SYS_UNSPEC_PHYID 0 +#endif +#ifndef CONFIG_SYS_UNSPEC_STRID +# define CONFIG_SYS_UNSPEC_STRID 0 +#endif + +#ifdef CONFIG_MCF547x_8x +typedef struct fec_info_dma FEC_INFO_T; +#define FEC_T fecdma_t +#else +typedef struct fec_info_s FEC_INFO_T; +#define FEC_T fec_t +#endif + +typedef struct phy_info_struct { + u32 phyid; + char *strid; +} phy_info_t; + +phy_info_t phyinfo[] = { + {0x0022561B, "AMD79C784VC"}, /* AMD 79C784VC */ + {0x00406322, "BCM5222"}, /* Broadcom 5222 */ + {0x02a80150, "Intel82555"}, /* Intel 82555 */ + {0x0016f870, "LSI80225"}, /* LSI 80225 */ + {0x0016f880, "LSI80225/B"}, /* LSI 80225/B */ + {0x78100000, "LXT970"}, /* LXT970 */ + {0x001378e0, "LXT971"}, /* LXT971 and 972 */ + {0x00221619, "KS8721BL"}, /* Micrel KS8721BL/SL */ + {0x00221512, "KSZ8041NL"}, /* Micrel KSZ8041NL */ + {0x20005CE1, "N83640"}, /* National 83640 */ + {0x20005C90, "N83848"}, /* National 83848 */ + {0x20005CA2, "N83849"}, /* National 83849 */ + {0x01814400, "QS6612"}, /* QS6612 */ +#if defined(CONFIG_SYS_UNSPEC_PHYID) && defined(CONFIG_SYS_UNSPEC_STRID) + {CONFIG_SYS_UNSPEC_PHYID, CONFIG_SYS_UNSPEC_STRID}, +#endif + {0, 0} +}; + +/* + * mii_init -- Initialize the MII for MII command without ethernet + * This function is a subset of eth_init + */ +void mii_reset(FEC_INFO_T *info) +{ + volatile FEC_T *fecp = (FEC_T *) (info->miibase); + int i; + + fecp->ecr = FEC_ECR_RESET; + + for (i = 0; (fecp->ecr & FEC_ECR_RESET) && (i < FEC_RESET_DELAY); ++i) { + udelay(1); + } + if (i == FEC_RESET_DELAY) + printf("FEC_RESET_DELAY timeout\n"); +} + +/* send command to phy using mii, wait for result */ +uint mii_send(uint mii_cmd) +{ + FEC_INFO_T *info; + volatile FEC_T *ep; + struct eth_device *dev; + uint mii_reply; + int j = 0; + + /* retrieve from register structure */ + dev = eth_get_dev(); + info = dev->priv; + + ep = (FEC_T *) info->miibase; + + ep->mmfr = mii_cmd; /* command to phy */ + + /* wait for mii complete */ + while (!(ep->eir & FEC_EIR_MII) && (j < MCFFEC_TOUT_LOOP)) { + udelay(1); + j++; + } + if (j >= MCFFEC_TOUT_LOOP) { + printf("MII not complete\n"); + return -1; + } + + mii_reply = ep->mmfr; /* result from phy */ + ep->eir = FEC_EIR_MII; /* clear MII complete */ +#ifdef ET_DEBUG + printf("%s[%d] %s: sent=0x%8.8x, reply=0x%8.8x\n", + __FILE__, __LINE__, __FUNCTION__, mii_cmd, mii_reply); +#endif + + return (mii_reply & 0xffff); /* data read from phy */ +} +#endif /* CONFIG_SYS_DISCOVER_PHY || (CONFIG_MII) */ + +#if defined(CONFIG_SYS_DISCOVER_PHY) +int mii_discover_phy(struct eth_device *dev) +{ +#define MAX_PHY_PASSES 11 + FEC_INFO_T *info = dev->priv; + int phyaddr, pass; + uint phyno, phytype; + int i, found = 0; + + if (info->phyname_init) + return info->phy_addr; + + phyaddr = -1; /* didn't find a PHY yet */ + for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) { + if (pass > 1) { + /* PHY may need more time to recover from reset. + * The LXT970 needs 50ms typical, no maximum is + * specified, so wait 10ms before try again. + * With 11 passes this gives it 100ms to wake up. + */ + udelay(10000); /* wait 10ms */ + } + + for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) { + + phytype = mii_send(mk_mii_read(phyno, PHY_PHYIDR1)); +#ifdef ET_DEBUG + printf("PHY type 0x%x pass %d type\n", phytype, pass); +#endif + if (phytype == 0xffff) + continue; + phyaddr = phyno; + phytype <<= 16; + phytype |= + mii_send(mk_mii_read(phyno, PHY_PHYIDR2)); + +#ifdef ET_DEBUG + printf("PHY @ 0x%x pass %d\n", phyno, pass); +#endif + + for (i = 0; (i < (sizeof(phyinfo) / sizeof(phy_info_t))) + && (phyinfo[i].phyid != 0); i++) { + if (phyinfo[i].phyid == phytype) { +#ifdef ET_DEBUG + printf("phyid %x - %s\n", + phyinfo[i].phyid, + phyinfo[i].strid); +#endif + strcpy(info->phy_name, phyinfo[i].strid); + info->phyname_init = 1; + found = 1; + break; + } + } + + if (!found) { +#ifdef ET_DEBUG + printf("0x%08x\n", phytype); +#endif + strcpy(info->phy_name, "unknown"); + info->phyname_init = 1; + break; + } + } + } + + if (phyaddr < 0) + printf("No PHY device found.\n"); + + return phyaddr; +} +#endif /* CONFIG_SYS_DISCOVER_PHY */ + +void mii_init(void) __attribute__((weak,alias("__mii_init"))); + +void __mii_init(void) +{ + FEC_INFO_T *info; + volatile FEC_T *fecp; + struct eth_device *dev; + int miispd = 0, i = 0; + u16 status = 0; + u16 linkgood = 0; + + /* retrieve from register structure */ + dev = eth_get_dev(); + info = dev->priv; + + fecp = (FEC_T *) info->miibase; + + fecpin_setclear(dev, 1); + + mii_reset(info); + + /* We use strictly polling mode only */ + fecp->eimr = 0; + + /* Clear any pending interrupt */ + fecp->eir = 0xffffffff; + + /* Set MII speed */ + miispd = (gd->bus_clk / 1000000) / 5; + fecp->mscr = miispd << 1; + + info->phy_addr = mii_discover_phy(dev); + + while (i < MCFFEC_TOUT_LOOP) { + status = 0; + i++; + /* Read PHY control register */ + miiphy_read(dev->name, info->phy_addr, PHY_BMCR, &status); + + /* If phy set to autonegotiate, wait for autonegotiation done, + * if phy is not autonegotiating, just wait for link up. + */ + if ((status & PHY_BMCR_AUTON) == PHY_BMCR_AUTON) { + linkgood = (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS); + } else { + linkgood = PHY_BMSR_LS; + } + /* Read PHY status register */ + miiphy_read(dev->name, info->phy_addr, PHY_BMSR, &status); + if ((status & linkgood) == linkgood) + break; + + udelay(1); + } + if (i >= MCFFEC_TOUT_LOOP) { + printf("Link UP timeout\n"); + } + + /* adapt to the duplex and speed settings of the phy */ + info->dup_spd = miiphy_duplex(dev->name, info->phy_addr) << 16; + info->dup_spd |= miiphy_speed(dev->name, info->phy_addr); +} + +/* + * Read and write a MII PHY register, routines used by MII Utilities + * + * FIXME: These routines are expected to return 0 on success, but mii_send + * does _not_ return an error code. Maybe 0xFFFF means error, i.e. + * no PHY connected... + * For now always return 0. + * FIXME: These routines only work after calling eth_init() at least once! + * Otherwise they hang in mii_send() !!! Sorry! + */ + +int mcffec_miiphy_read(char *devname, unsigned char addr, unsigned char reg, + unsigned short *value) +{ + short rdreg; /* register working value */ + +#ifdef MII_DEBUG + printf("miiphy_read(0x%x) @ 0x%x = ", reg, addr); +#endif + rdreg = mii_send(mk_mii_read(addr, reg)); + + *value = rdreg; + +#ifdef MII_DEBUG + printf("0x%04x\n", *value); +#endif + + return 0; +} + +int mcffec_miiphy_write(char *devname, unsigned char addr, unsigned char reg, + unsigned short value) +{ + short rdreg; /* register working value */ + +#ifdef MII_DEBUG + printf("miiphy_write(0x%x) @ 0x%x = ", reg, addr); +#endif + + rdreg = mii_send(mk_mii_write(addr, reg, value)); + +#ifdef MII_DEBUG + printf("0x%04x\n", value); +#endif + + return 0; +} + +#endif /* CONFIG_CMD_NET, FEC_ENET & NET_MULTI */ diff --git a/roms/u-boot-sam460ex/drivers/net/mpc512x_fec.c b/roms/u-boot-sam460ex/drivers/net/mpc512x_fec.c new file mode 100644 index 000000000..c580c827a --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/mpc512x_fec.c @@ -0,0 +1,759 @@ +/* + * (C) Copyright 2003-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx FEC driver. + * Adapted for MPC512x by Grzegorz Bernacki <gjb@semihalf.com> + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <miiphy.h> +#include <asm/io.h> +#include "mpc512x_fec.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define DEBUG 0 + +#if defined(CONFIG_CMD_NET) && defined(CONFIG_NET_MULTI) && \ + defined(CONFIG_MPC512x_FEC) + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +int fec512x_miiphy_read(char *devname, u8 phyAddr, u8 regAddr, u16 * retVal); +int fec512x_miiphy_write(char *devname, u8 phyAddr, u8 regAddr, u16 data); +int mpc512x_fec_init_phy(struct eth_device *dev, bd_t * bis); + +static uchar rx_buff[FEC_BUFFER_SIZE]; +static int rx_buff_idx = 0; + +/********************************************************************/ +#if (DEBUG & 0x2) +static void mpc512x_fec_phydump (char *devname) +{ + u16 phyStatus, i; + u8 phyAddr = CONFIG_PHY_ADDR; + u8 reg_mask[] = { + /* regs to print: 0...8, 21,27,31 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read (devname, phyAddr, i, &phyStatus); + printf ("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc512x_fec_bd_init (mpc512x_fec_priv *fec) +{ + int ix; + + /* + * Receive BDs init + */ + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + fec->bdBase->rbd[ix].dataPointer = + (u32)&fec->bdBase->recv_frames[ix]; + fec->bdBase->rbd[ix].status = FEC_RBD_EMPTY; + fec->bdBase->rbd[ix].dataLength = 0; + } + + /* + * have the last RBD to close the ring + */ + fec->bdBase->rbd[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + /* + * Trasmit BDs init + */ + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->bdBase->tbd[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->bdBase->tbd[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; + + return 0; +} + +/********************************************************************/ +static void mpc512x_fec_rbd_clean (mpc512x_fec_priv *fec, volatile FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; + + /* + * Now, we have an empty RxBD, notify FEC + * Set Descriptor polling active + */ + out_be32(&fec->eth->r_des_active, 0x01000000); +} + +/********************************************************************/ +static void mpc512x_fec_tbd_scrub (mpc512x_fec_priv *fec) +{ + volatile FEC_TBD *pUsedTbd; + +#if (DEBUG & 0x1) + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->bdBase->tbd[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#if (DEBUG & 0x20) + printf ("Cannot clean TBD %d, in use\n", fec->usedTbdIndex); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc512x_fec_set_hwaddr (mpc512x_fec_priv *fec, unsigned char *mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + out_be32(&fec->eth->iaddr1, (1 << (crc - 32))); + out_be32(&fec->eth->iaddr2, 0); + } else { + out_be32(&fec->eth->iaddr1, 0); + out_be32(&fec->eth->iaddr2, (1 << crc)); + } + + /* + * Set physical address + */ + out_be32(&fec->eth->paddr1, (mac[0] << 24) + (mac[1] << 16) + + (mac[2] << 8) + mac[3]); + out_be32(&fec->eth->paddr2, (mac[4] << 24) + (mac[5] << 16) + + 0x8808); +} + +/********************************************************************/ +static int mpc512x_fec_init (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init... Begin\n"); +#endif + + mpc512x_fec_set_hwaddr (fec, dev->enetaddr); + out_be32(&fec->eth->gaddr1, 0x00000000); + out_be32(&fec->eth->gaddr2, 0x00000000); + + mpc512x_fec_init_phy (dev, bis); + + /* Set interrupt mask register */ + out_be32(&fec->eth->imask, 0x00000000); + + /* Clear FEC-Lite interrupt event register(IEVENT) */ + out_be32(&fec->eth->ievent, 0xffffffff); + + /* Set transmit fifo watermark register(X_WMRK), default = 64 */ + out_be32(&fec->eth->x_wmrk, 0x0); + + /* Set Opcode/Pause Duration Register */ + out_be32(&fec->eth->op_pause, 0x00010020); + + /* Frame length=1522; MII mode */ + out_be32(&fec->eth->r_cntrl, (FEC_MAX_FRAME_LEN << 16) | 0x24); + + /* Half-duplex, heartbeat disabled */ + out_be32(&fec->eth->x_cntrl, 0x00000000); + + /* Enable MIB counters */ + out_be32(&fec->eth->mib_control, 0x0); + + /* Setup recv fifo start and buff size */ + out_be32(&fec->eth->r_fstart, 0x500); + out_be32(&fec->eth->r_buff_size, FEC_BUFFER_SIZE); + + /* Setup BD base addresses */ + out_be32(&fec->eth->r_des_start, (u32)fec->bdBase->rbd); + out_be32(&fec->eth->x_des_start, (u32)fec->bdBase->tbd); + + /* DMA Control */ + out_be32(&fec->eth->dma_control, 0xc0000000); + + /* Enable FEC */ + setbits_be32(&fec->eth->ecntrl, 0x00000006); + + /* Initilize addresses and status words of BDs */ + mpc512x_fec_bd_init (fec); + + /* Descriptor polling active */ + out_be32(&fec->eth->r_des_active, 0x01000000); + +#if (DEBUG & 0x1) + printf("mpc512x_fec_init... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +int mpc512x_fec_init_phy (struct eth_device *dev, bd_t * bis) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + const u8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + int timeout = 1; + u16 phyStatus; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Begin\n"); +#endif + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + out_be32(&fec->eth->ievent, 0xffffffff); + + /* + * Set interrupt mask register + */ + out_be32(&fec->eth->imask, 0x00000000); + + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + out_be32(&fec->eth->mii_speed, + (((gd->ips_clk / 1000000) / 5) + 1) << 1); + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x8000); + udelay (1000); + + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#if (DEBUG & 0x2) + printf ("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); + + miiphy_write (dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay (10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay (10000); + if ((timeout--) == 0) { + printf ("failed. Link is down.\n"); + break; + } + miiphy_read (dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf ("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#if (DEBUG & 0x2) + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write (dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write (dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 2500; + do { + udelay (1000); + + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read (dev->name, phyAddr, 0x1, &phyStatus) != 0) { +#if (DEBUG & 0x2) + printf ("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#if (DEBUG & 0x2) + printf ("PHY auto neg complete! \n"); +#endif + } + } + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_init_phy... Done \n"); +#endif + return 1; +} + +/********************************************************************/ +static void mpc512x_fec_halt (struct eth_device *dev) +{ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + int counter = 0xffff; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc512x_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + out_be32(&fec->eth->imask, 0); + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + setbits_be32(&fec->eth->x_cntrl, 0x00000001); + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(in_be32(&fec->eth->ievent) & 0x10000000))) + ; + + /* + * Disable the Ethernet Controller + */ + clrbits_be32(&fec->eth->ecntrl, 0x00000002); + + /* + * Issue a reset command to the FEC chip + */ + setbits_be32(&fec->eth->ecntrl, 0x1); + + /* + * wait at least 16 clock cycles + */ + udelay (10); +#if (DEBUG & 0x3) + printf ("Ethernet task stopped\n"); +#endif +} + +/********************************************************************/ + +static int mpc512x_fec_send (struct eth_device *dev, volatile void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_TBD *pTbd; + +#if (DEBUG & 0x20) + printf("tbd status: 0x%04x\n", fec->tbdBase[fec->tbdIndex].status); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc512x_fec_tbd_scrub (fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#if (DEBUG & 0x20) + printf ("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->bdBase->tbd[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (u32)eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + + /* Activate transmit Buffer Descriptor polling */ + out_be32(&fec->eth->x_des_active, 0x01000000); + +#if (DEBUG & 0x8) + printf ( "+" ); +#endif + + fec->cleanTbdNum -= 1; + + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay (10); +#if (DEBUG & 0x8) + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc512x_fec_recv (struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc512x_fec_priv *fec = (mpc512x_fec_priv *)dev->priv; + volatile FEC_RBD *pRbd = &fec->bdBase->rbd[fec->rbdIndex]; + unsigned long ievent; + int frame_length = 0; + +#if (DEBUG & 0x1) + printf ("mpc512x_fec_recv %d Start...\n", fec->rbdIndex); +#endif +#if (DEBUG & 0x8) + printf( "-" ); +#endif + + /* + * Check if any critical events have happened + */ + ievent = in_be32(&fec->eth->ievent); + out_be32(&fec->eth->ievent, ievent); + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc512x_fec_halt (dev); + mpc512x_fec_init (dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + setbits_be32(&fec->eth->x_cntrl, 0x00000001); + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (in_be32(&fec->eth->x_cntrl) & 0x00000001) { + mpc512x_fec_halt (dev); + clrbits_be32(&fec->eth->x_cntrl, 0x00000001);; + mpc512x_fec_init (dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if (!(pRbd->status & FEC_RBD_ERR) && + ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer size + */ + if (pRbd->status & FEC_RBD_LAST) + frame_length = pRbd->dataLength - 4; + else + frame_length = pRbd->dataLength; +#if (DEBUG & 0x20) + { + int i; + printf ("recv data length 0x%08x data hdr: ", + pRbd->dataLength); + for (i = 0; i < 14; i++) + printf ("%x ", *((u8*)pRbd->dataPointer + i)); + printf("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ + memcpy (&rx_buff[rx_buff_idx], (void*)pRbd->dataPointer, + frame_length - rx_buff_idx); + rx_buff_idx = frame_length; + + if (pRbd->status & FEC_RBD_LAST) { + NetReceive ((uchar*)rx_buff, frame_length); + rx_buff_idx = 0; + } + } + + /* + * Reset buffer descriptor as empty + */ + mpc512x_fec_rbd_clean (fec, pRbd); + } + + /* Try to fill Buffer Descriptors */ + out_be32(&fec->eth->r_des_active, 0x01000000); + + return frame_length; +} + +/********************************************************************/ +int mpc512x_fec_initialize (bd_t * bis) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + mpc512x_fec_priv *fec; + struct eth_device *dev; + void * bd; + + fec = (mpc512x_fec_priv *) malloc (sizeof(*fec)); + dev = (struct eth_device *) malloc (sizeof(*dev)); + memset (dev, 0, sizeof *dev); + + fec->eth = &im->fec; + +# ifndef CONFIG_FEC_10MBIT + fec->xcv_type = MII100; +# else + fec->xcv_type = MII10; +# endif + dev->priv = (void *)fec; + dev->iobase = (int)&im->fec; + dev->init = mpc512x_fec_init; + dev->halt = mpc512x_fec_halt; + dev->send = mpc512x_fec_send; + dev->recv = mpc512x_fec_recv; + + sprintf (dev->name, "FEC ETHERNET"); + eth_register (dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec512x_miiphy_read, fec512x_miiphy_write); +#endif + + /* Clean up space FEC's MIB and FIFO RAM ...*/ + memset ((void *)&im->fec.mib, 0x00, sizeof(im->fec.mib)); + memset ((void *)&im->fec.fifo, 0x00, sizeof(im->fec.fifo)); + + /* + * Malloc space for BDs (must be quad word-aligned) + * this pointer is lost, so cannot be freed + */ + bd = malloc (sizeof(mpc512x_buff_descs) + 0x1f); + fec->bdBase = (mpc512x_buff_descs*)((u32)bd & 0xfffffff0); + memset ((void *) bd, 0x00, sizeof(mpc512x_buff_descs) + 0x1f); + + /* + * Set interrupt mask register + */ + out_be32(&fec->eth->imask, 0x00000000); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + out_be32(&fec->eth->ievent, 0xffffffff); + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec512x_miiphy_read (char *devname, u8 phyAddr, u8 regAddr, u16 * retVal) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile fec512x_t *eth = &im->fec; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + out_be32(ð->mii_data, FEC_MII_DATA_ST | + FEC_MII_DATA_OP_RD | + FEC_MII_DATA_TA | + phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(in_be32(ð->ievent) & 0x00800000))) + ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + out_be32(ð->ievent, 0x00800000); + + /* + * it's now safe to read the PHY's register + */ + *retVal = (u16) in_be32(ð->mii_data); + + return 0; +} + +/********************************************************************/ +int fec512x_miiphy_write (char *devname, u8 phyAddr, u8 regAddr, u16 data) +{ + volatile immap_t *im = (immap_t *) CONFIG_SYS_IMMR; + volatile fec512x_t *eth = &im->fec; + u32 reg; /* convenient holder for the PHY register */ + u32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + out_be32(ð->mii_data, FEC_MII_DATA_ST | + FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | + phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(in_be32(ð->ievent) & 0x00800000))) + ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + out_be32(ð->ievent, 0x00800000); + + return 0; +} + +#endif /* CONFIG_MPC512x_FEC */ diff --git a/roms/u-boot-sam460ex/drivers/net/mpc512x_fec.h b/roms/u-boot-sam460ex/drivers/net/mpc512x_fec.h new file mode 100644 index 000000000..a083cca2f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/mpc512x_fec.h @@ -0,0 +1,98 @@ +/* + * (C) Copyright 2003 - 2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Derived from the MPC8xx driver's header file. + */ + +#ifndef __MPC512X_FEC_H +#define __MPC512X_FEC_H + +#include <common.h> + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_RBD; + +typedef struct { + u16 status; + u16 dataLength; + u32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_FRAME_LEN 1522 /* recommended default value */ + +/* Buffer size must be evenly divisible by 16 */ +#define FEC_BUFFER_SIZE ((FEC_MAX_FRAME_LEN + 0x10) & (~0xf)) + +typedef struct { + u8 frame[FEC_BUFFER_SIZE]; +} mpc512x_frame; + +typedef struct { + FEC_RBD rbd[FEC_RBD_NUM]; /* RBD ring */ + FEC_TBD tbd[FEC_TBD_NUM]; /* TBD ring */ + mpc512x_frame recv_frames[FEC_RBD_NUM]; /* receive buff */ +} mpc512x_buff_descs; + +typedef struct { + volatile fec512x_t *eth; + xceiver_type xcv_type; /* transceiver type */ + mpc512x_buff_descs *bdBase; /* BD rings and recv buffer */ + u16 rbdIndex; /* next receive BD to read */ + u16 tbdIndex; /* next transmit BD to send */ + u16 usedTbdIndex; /* next transmit BD to clean */ + u16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc512x_fec_priv; + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC512X_FEC_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/mpc5xxx_fec.c b/roms/u-boot-sam460ex/drivers/net/mpc5xxx_fec.c new file mode 100644 index 000000000..1681e2672 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/mpc5xxx_fec.c @@ -0,0 +1,1017 @@ +/* + * (C) Copyright 2003-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.c, + * (C) Copyright Motorola, Inc., 2000 + */ + +#include <common.h> +#include <mpc5xxx.h> +#include <mpc5xxx_sdma.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <miiphy.h> +#include "mpc5xxx_fec.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* #define DEBUG 0x28 */ + +#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) +#error "CONFIG_MII has to be defined!" +#endif + +#if (DEBUG & 0x60) +static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec); +static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec); +#endif /* DEBUG */ + +typedef struct { + uint8 data[1500]; /* actual data */ + int length; /* actual length */ + int used; /* buffer in use or not */ + uint8 head[16]; /* MAC header(6 + 6 + 2) + 2(aligned) */ +} NBUF; + +int fec5xxx_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal); +int fec5xxx_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data); + +static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis); + +/********************************************************************/ +#if (DEBUG & 0x2) +static void mpc5xxx_fec_phydump (char *devname) +{ + uint16 phyStatus, i; + uint8 phyAddr = CONFIG_PHY_ADDR; + uint8 reg_mask[] = { +#if CONFIG_PHY_TYPE == 0x79c874 /* AMD Am79C874 */ + /* regs to print: 0...7, 16...19, 21, 23, 24 */ + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, +#else + /* regs to print: 0...8, 16...20 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif + }; + + for (i = 0; i < 32; i++) { + if (reg_mask[i]) { + miiphy_read(devname, phyAddr, i, &phyStatus); + printf("Mii reg %d: 0x%04x\n", i, phyStatus); + } + } +} +#endif + +/********************************************************************/ +static int mpc5xxx_fec_rbd_init(mpc5xxx_fec_priv *fec) +{ + int ix; + char *data; + static int once = 0; + + for (ix = 0; ix < FEC_RBD_NUM; ix++) { + if (!once) { + data = (char *)malloc(FEC_MAX_PKT_SIZE); + if (data == NULL) { + printf ("RBD INIT FAILED\n"); + return -1; + } + fec->rbdBase[ix].dataPointer = (uint32)data; + } + fec->rbdBase[ix].status = FEC_RBD_EMPTY; + fec->rbdBase[ix].dataLength = 0; + } + once ++; + + /* + * have the last RBD to close the ring + */ + fec->rbdBase[ix - 1].status |= FEC_RBD_WRAP; + fec->rbdIndex = 0; + + return 0; +} + +/********************************************************************/ +static void mpc5xxx_fec_tbd_init(mpc5xxx_fec_priv *fec) +{ + int ix; + + for (ix = 0; ix < FEC_TBD_NUM; ix++) { + fec->tbdBase[ix].status = 0; + } + + /* + * Have the last TBD to close the ring + */ + fec->tbdBase[ix - 1].status |= FEC_TBD_WRAP; + + /* + * Initialize some indices + */ + fec->tbdIndex = 0; + fec->usedTbdIndex = 0; + fec->cleanTbdNum = FEC_TBD_NUM; +} + +/********************************************************************/ +static void mpc5xxx_fec_rbd_clean(mpc5xxx_fec_priv *fec, volatile FEC_RBD * pRbd) +{ + /* + * Reset buffer descriptor as empty + */ + if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) + pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); + else + pRbd->status = FEC_RBD_EMPTY; + + pRbd->dataLength = 0; + + /* + * Now, we have an empty RxBD, restart the SmartDMA receive task + */ + SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); + + /* + * Increment BD count + */ + fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; +} + +/********************************************************************/ +static void mpc5xxx_fec_tbd_scrub(mpc5xxx_fec_priv *fec) +{ + volatile FEC_TBD *pUsedTbd; + +#if (DEBUG & 0x1) + printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", + fec->cleanTbdNum, fec->usedTbdIndex); +#endif + + /* + * process all the consumed TBDs + */ + while (fec->cleanTbdNum < FEC_TBD_NUM) { + pUsedTbd = &fec->tbdBase[fec->usedTbdIndex]; + if (pUsedTbd->status & FEC_TBD_READY) { +#if (DEBUG & 0x20) + printf("Cannot clean TBD %d, in use\n", fec->cleanTbdNum); +#endif + return; + } + + /* + * clean this buffer descriptor + */ + if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) + pUsedTbd->status = FEC_TBD_WRAP; + else + pUsedTbd->status = 0; + + /* + * update some indeces for a correct handling of the TBD ring + */ + fec->cleanTbdNum++; + fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; + } +} + +/********************************************************************/ +static void mpc5xxx_fec_set_hwaddr(mpc5xxx_fec_priv *fec, char *mac) +{ + uint8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + uint32 crc = 0xffffffff; /* initial value */ + + /* + * The algorithm used is the following: + * we loop on each of the six bytes of the provided address, + * and we compute the CRC by left-shifting the previous + * value by one position, so that each bit in the current + * byte of the address may contribute the calculation. If + * the latter and the MSB in the CRC are different, then + * the CRC value so computed is also ex-ored with the + * "polynomium generator". The current byte of the address + * is also shifted right by one bit at each iteration. + * This is because the CRC generatore in hardware is implemented + * as a shift-register with as many ex-ores as the radixes + * in the polynomium. This suggests that we represent the + * polynomiumm itself as a 32-bit constant. + */ + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + + /* + * Set individual hash table register + */ + if (crc >= 32) { + fec->eth->iaddr1 = (1 << (crc - 32)); + fec->eth->iaddr2 = 0; + } else { + fec->eth->iaddr1 = 0; + fec->eth->iaddr2 = (1 << crc); + } + + /* + * Set physical address + */ + fec->eth->paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; + fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; +} + +/********************************************************************/ +static int mpc5xxx_fec_init(struct eth_device *dev, bd_t * bis) +{ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_init... Begin\n"); +#endif + + mpc5xxx_fec_init_phy(dev, bis); + + /* + * Initialize RxBD/TxBD rings + */ + mpc5xxx_fec_rbd_init(fec); + mpc5xxx_fec_tbd_init(fec); + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + + /* + * Set FEC-Lite receive control register(R_CNTRL): + */ + if (fec->xcv_type == SEVENWIRE) { + /* + * Frame length=1518; 7-wire mode + */ + fec->eth->r_cntrl = 0x05ee0020; /*0x05ee0000;FIXME */ + } else { + /* + * Frame length=1518; MII mode; + */ + fec->eth->r_cntrl = 0x05ee0024; /*0x05ee0004;FIXME */ + } + + fec->eth->x_cntrl = 0x00000000; /* half-duplex, heartbeat disabled */ + + /* + * Set Opcode/Pause Duration Register + */ + fec->eth->op_pause = 0x00010020; /*FIXME 0xffff0020; */ + + /* + * Set Rx FIFO alarm and granularity value + */ + fec->eth->rfifo_cntrl = 0x0c000000 + | (fec->eth->rfifo_cntrl & ~0x0f000000); + fec->eth->rfifo_alarm = 0x0000030c; +#if (DEBUG & 0x22) + if (fec->eth->rfifo_status & 0x00700000 ) { + printf("mpc5xxx_fec_init() RFIFO error\n"); + } +#endif + + /* + * Set Tx FIFO granularity value + */ + fec->eth->tfifo_cntrl = 0x0c000000 + | (fec->eth->tfifo_cntrl & ~0x0f000000); +#if (DEBUG & 0x2) + printf("tfifo_status: 0x%08x\n", fec->eth->tfifo_status); + printf("tfifo_alarm: 0x%08x\n", fec->eth->tfifo_alarm); +#endif + + /* + * Set transmit fifo watermark register(X_WMRK), default = 64 + */ + fec->eth->tfifo_alarm = 0x00000080; + fec->eth->x_wmrk = 0x2; + + /* + * Set individual address filter for unicast address + * and set physical address registers. + */ + mpc5xxx_fec_set_hwaddr(fec, (char *)dev->enetaddr); + + /* + * Set multicast address filter + */ + fec->eth->gaddr1 = 0x00000000; + fec->eth->gaddr2 = 0x00000000; + + /* + * Turn ON cheater FSM: ???? + */ + fec->eth->xmit_fsm = 0x03000000; + + /* + * Turn off COMM bus prefetch in the MPC5200 BestComm. It doesn't + * work w/ the current receive task. + */ + sdma->PtdCntrl |= 0x00000001; + + /* + * Set priority of different initiators + */ + sdma->IPR0 = 7; /* always */ + sdma->IPR3 = 6; /* Eth RX */ + sdma->IPR4 = 5; /* Eth Tx */ + + /* + * Clear SmartDMA task interrupt pending bits + */ + SDMA_CLEAR_IEVENT(FEC_RECV_TASK_NO); + + /* + * Initialize SmartDMA parameters stored in SRAM + */ + *(volatile int *)FEC_TBD_BASE = (int)fec->tbdBase; + *(volatile int *)FEC_RBD_BASE = (int)fec->rbdBase; + *(volatile int *)FEC_TBD_NEXT = (int)fec->tbdBase; + *(volatile int *)FEC_RBD_NEXT = (int)fec->rbdBase; + + /* + * Enable FEC-Lite controller + */ + fec->eth->ecntrl |= 0x00000006; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + /* + * Enable SmartDMA receive task + */ + SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); + +#if (DEBUG & 0x1) + printf("mpc5xxx_fec_init... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis) +{ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + const uint8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ + static int initialized = 0; + + if(initialized) + return 0; + initialized = 1; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_init_phy... Begin\n"); +#endif + + /* + * Initialize GPIO pins + */ + if (fec->xcv_type == SEVENWIRE) { + /* 10MBit with 7-wire operation */ +#if defined(CONFIG_TOTAL5200) + /* 7-wire and USB2 on Ethernet */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00030000; +#else /* !CONFIG_TOTAL5200 */ + /* 7-wire only */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00020000; +#endif /* CONFIG_TOTAL5200 */ + } else { + /* 100MBit with MD operation */ + *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00050000; + } + + /* + * Clear FEC-Lite interrupt event register(IEVENT) + */ + fec->eth->ievent = 0xffffffff; + + /* + * Set interrupt mask register + */ + fec->eth->imask = 0x00000000; + +/* + * In original Promess-provided code PHY initialization is disabled with the + * following comment: "Phy initialization is DISABLED for now. There was a + * problem with running 100 Mbps on PRO board". Thus we temporarily disable + * PHY initialization for the Motion-PRO board, until a proper fix is found. + */ + + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ + } + + if (fec->xcv_type != SEVENWIRE) { + /* + * Initialize PHY(LXT971A): + * + * Generally, on power up, the LXT971A reads its configuration + * pins to check for forced operation, If not cofigured for + * forced operation, it uses auto-negotiation/parallel detection + * to automatically determine line operating conditions. + * If the PHY device on the other side of the link supports + * auto-negotiation, the LXT971A auto-negotiates with it + * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not + * support auto-negotiation, the LXT971A automatically detects + * the presence of either link pulses(10Mbps PHY) or Idle + * symbols(100Mbps) and sets its operating conditions accordingly. + * + * When auto-negotiation is controlled by software, the following + * steps are recommended. + * + * Note: + * The physical address is dependent on hardware configuration. + * + */ + int timeout = 1; + uint16 phyStatus; + + /* + * Reset PHY, then delay 300ns + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x8000); + udelay(1000); + +#if defined(CONFIG_UC101) || defined(CONFIG_MUCMC52) + /* Set the LED configuration Register for the UC101 + and MUCMC52 Board */ + miiphy_write(dev->name, phyAddr, 0x14, 0x4122); +#endif + if (fec->xcv_type == MII10) { + /* + * Force 10Base-T, FDX operation + */ +#if (DEBUG & 0x2) + printf("Forcing 10 Mbps ethernet link... "); +#endif + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); + /* + miiphy_write(dev->name, fec, phyAddr, 0x0, 0x0100); + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x0180); + + timeout = 20; + do { /* wait for link status to go down */ + udelay(10000); + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf("hmmm, should not have waited..."); +#endif + break; + } + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf("="); +#endif + } while ((phyStatus & 0x0004)); /* !link up */ + + timeout = 1000; + do { /* wait for link status to come back up */ + udelay(10000); + if ((timeout--) == 0) { + printf("failed. Link is down.\n"); + break; + } + miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); +#if (DEBUG & 0x2) + printf("+"); +#endif + } while (!(phyStatus & 0x0004)); /* !link up */ + +#if (DEBUG & 0x2) + printf ("done.\n"); +#endif + } else { /* MII100 */ + /* + * Set the auto-negotiation advertisement register bits + */ + miiphy_write(dev->name, phyAddr, 0x4, 0x01e1); + + /* + * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation + */ + miiphy_write(dev->name, phyAddr, 0x0, 0x1200); + + /* + * Wait for AN completion + */ + timeout = 5000; + do { + udelay(1000); + + if ((timeout--) == 0) { +#if (DEBUG & 0x2) + printf("PHY auto neg 0 failed...\n"); +#endif + return -1; + } + + if (miiphy_read(dev->name, phyAddr, 0x1, &phyStatus) != 0) { +#if (DEBUG & 0x2) + printf("PHY auto neg 1 failed 0x%04x...\n", phyStatus); +#endif + return -1; + } + } while (!(phyStatus & 0x0004)); + +#if (DEBUG & 0x2) + printf("PHY auto neg complete! \n"); +#endif + } + + } + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + +#if (DEBUG & 0x1) + printf("mpc5xxx_fec_init_phy... Done \n"); +#endif + + return 1; +} + +/********************************************************************/ +static void mpc5xxx_fec_halt(struct eth_device *dev) +{ + struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + int counter = 0xffff; + +#if (DEBUG & 0x2) + if (fec->xcv_type != SEVENWIRE) + mpc5xxx_fec_phydump (dev->name); +#endif + + /* + * mask FEC chip interrupts + */ + fec->eth->imask = 0; + + /* + * issue graceful stop command to the FEC transmitter if necessary + */ + fec->eth->x_cntrl |= 0x00000001; + + /* + * wait for graceful stop to register + */ + while ((counter--) && (!(fec->eth->ievent & 0x10000000))) ; + + /* + * Disable SmartDMA tasks + */ + SDMA_TASK_DISABLE (FEC_XMIT_TASK_NO); + SDMA_TASK_DISABLE (FEC_RECV_TASK_NO); + + /* + * Turn on COMM bus prefetch in the MPC5200 BestComm after we're + * done. It doesn't work w/ the current receive task. + */ + sdma->PtdCntrl &= ~0x00000001; + + /* + * Disable the Ethernet Controller + */ + fec->eth->ecntrl &= 0xfffffffd; + + /* + * Clear FIFO status registers + */ + fec->eth->rfifo_status &= 0x00700000; + fec->eth->tfifo_status &= 0x00700000; + + fec->eth->reset_cntrl = 0x01000000; + + /* + * Issue a reset command to the FEC chip + */ + fec->eth->ecntrl |= 0x1; + + /* + * wait at least 16 clock cycles + */ + udelay(10); + + /* don't leave the MII speed set to zero */ + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ + } + +#if (DEBUG & 0x3) + printf("Ethernet task stopped\n"); +#endif +} + +#if (DEBUG & 0x60) +/********************************************************************/ + +static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec) +{ + uint16 phyAddr = CONFIG_PHY_ADDR; + uint16 phyStatus; + + if ((fec->eth->tfifo_lrf_ptr != fec->eth->tfifo_lwf_ptr) + || (fec->eth->tfifo_rdptr != fec->eth->tfifo_wrptr)) { + + miiphy_read(devname, phyAddr, 0x1, &phyStatus); + printf("\nphyStatus: 0x%04x\n", phyStatus); + printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf("ievent: 0x%08x\n", fec->eth->ievent); + printf("x_status: 0x%08x\n", fec->eth->x_status); + printf("tfifo: status 0x%08x\n", fec->eth->tfifo_status); + + printf(" control 0x%08x\n", fec->eth->tfifo_cntrl); + printf(" lrfp 0x%08x\n", fec->eth->tfifo_lrf_ptr); + printf(" lwfp 0x%08x\n", fec->eth->tfifo_lwf_ptr); + printf(" alarm 0x%08x\n", fec->eth->tfifo_alarm); + printf(" readptr 0x%08x\n", fec->eth->tfifo_rdptr); + printf(" writptr 0x%08x\n", fec->eth->tfifo_wrptr); + } +} + +static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec) +{ + uint16 phyAddr = CONFIG_PHY_ADDR; + uint16 phyStatus; + + if ((fec->eth->rfifo_lrf_ptr != fec->eth->rfifo_lwf_ptr) + || (fec->eth->rfifo_rdptr != fec->eth->rfifo_wrptr)) { + + miiphy_read(devname, phyAddr, 0x1, &phyStatus); + printf("\nphyStatus: 0x%04x\n", phyStatus); + printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); + printf("ievent: 0x%08x\n", fec->eth->ievent); + printf("x_status: 0x%08x\n", fec->eth->x_status); + printf("rfifo: status 0x%08x\n", fec->eth->rfifo_status); + + printf(" control 0x%08x\n", fec->eth->rfifo_cntrl); + printf(" lrfp 0x%08x\n", fec->eth->rfifo_lrf_ptr); + printf(" lwfp 0x%08x\n", fec->eth->rfifo_lwf_ptr); + printf(" alarm 0x%08x\n", fec->eth->rfifo_alarm); + printf(" readptr 0x%08x\n", fec->eth->rfifo_rdptr); + printf(" writptr 0x%08x\n", fec->eth->rfifo_wrptr); + } +} +#endif /* DEBUG */ + +/********************************************************************/ + +static int mpc5xxx_fec_send(struct eth_device *dev, volatile void *eth_data, + int data_length) +{ + /* + * This routine transmits one frame. This routine only accepts + * 6-byte Ethernet addresses. + */ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + volatile FEC_TBD *pTbd; + +#if (DEBUG & 0x20) + printf("tbd status: 0x%04x\n", fec->tbdBase[0].status); + tfifo_print(dev->name, fec); +#endif + + /* + * Clear Tx BD ring at first + */ + mpc5xxx_fec_tbd_scrub(fec); + + /* + * Check for valid length of data. + */ + if ((data_length > 1500) || (data_length <= 0)) { + return -1; + } + + /* + * Check the number of vacant TxBDs. + */ + if (fec->cleanTbdNum < 1) { +#if (DEBUG & 0x20) + printf("No available TxBDs ...\n"); +#endif + return -1; + } + + /* + * Get the first TxBD to send the mac header + */ + pTbd = &fec->tbdBase[fec->tbdIndex]; + pTbd->dataLength = data_length; + pTbd->dataPointer = (uint32)eth_data; + pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; + fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; + +#if (DEBUG & 0x100) + printf("SDMA_TASK_ENABLE, fec->tbdIndex = %d \n", fec->tbdIndex); +#endif + + /* + * Kick the MII i/f + */ + if (fec->xcv_type != SEVENWIRE) { + uint16 phyStatus; + miiphy_read(dev->name, 0, 0x1, &phyStatus); + } + + /* + * Enable SmartDMA transmit task + */ + +#if (DEBUG & 0x20) + tfifo_print(dev->name, fec); +#endif + SDMA_TASK_ENABLE (FEC_XMIT_TASK_NO); +#if (DEBUG & 0x20) + tfifo_print(dev->name, fec); +#endif +#if (DEBUG & 0x8) + printf( "+" ); +#endif + + fec->cleanTbdNum -= 1; + +#if (DEBUG & 0x129) && (DEBUG & 0x80000000) + printf ("smartDMA ethernet Tx task enabled\n"); +#endif + /* + * wait until frame is sent . + */ + while (pTbd->status & FEC_TBD_READY) { + udelay(10); +#if (DEBUG & 0x8) + printf ("TDB status = %04x\n", pTbd->status); +#endif + } + + return 0; +} + + +/********************************************************************/ +static int mpc5xxx_fec_recv(struct eth_device *dev) +{ + /* + * This command pulls one frame from the card + */ + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; + volatile FEC_RBD *pRbd = &fec->rbdBase[fec->rbdIndex]; + unsigned long ievent; + int frame_length, len = 0; + NBUF *frame; + uchar buff[FEC_MAX_PKT_SIZE]; + +#if (DEBUG & 0x1) + printf ("mpc5xxx_fec_recv %d Start...\n", fec->rbdIndex); +#endif +#if (DEBUG & 0x8) + printf( "-" ); +#endif + + /* + * Check if any critical events have happened + */ + ievent = fec->eth->ievent; + fec->eth->ievent = ievent; + if (ievent & 0x20060000) { + /* BABT, Rx/Tx FIFO errors */ + mpc5xxx_fec_halt(dev); + mpc5xxx_fec_init(dev, NULL); + return 0; + } + if (ievent & 0x80000000) { + /* Heartbeat error */ + fec->eth->x_cntrl |= 0x00000001; + } + if (ievent & 0x10000000) { + /* Graceful stop complete */ + if (fec->eth->x_cntrl & 0x00000001) { + mpc5xxx_fec_halt(dev); + fec->eth->x_cntrl &= ~0x00000001; + mpc5xxx_fec_init(dev, NULL); + } + } + + if (!(pRbd->status & FEC_RBD_EMPTY)) { + if ((pRbd->status & FEC_RBD_LAST) && !(pRbd->status & FEC_RBD_ERR) && + ((pRbd->dataLength - 4) > 14)) { + + /* + * Get buffer address and size + */ + frame = (NBUF *)pRbd->dataPointer; + frame_length = pRbd->dataLength - 4; + +#if (DEBUG & 0x20) + { + int i; + printf("recv data hdr:"); + for (i = 0; i < 14; i++) + printf("%x ", *(frame->head + i)); + printf("\n"); + } +#endif + /* + * Fill the buffer and pass it to upper layers + */ + memcpy(buff, frame->head, 14); + memcpy(buff + 14, frame->data, frame_length); + NetReceive(buff, frame_length); + len = frame_length; + } + /* + * Reset buffer descriptor as empty + */ + mpc5xxx_fec_rbd_clean(fec, pRbd); + } + SDMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); + return len; +} + + +/********************************************************************/ +int mpc5xxx_fec_initialize(bd_t * bis) +{ + mpc5xxx_fec_priv *fec; + struct eth_device *dev; + char *tmp, *end; + char env_enetaddr[6]; + int i; + + fec = (mpc5xxx_fec_priv *)malloc(sizeof(*fec)); + dev = (struct eth_device *)malloc(sizeof(*dev)); + memset(dev, 0, sizeof *dev); + + fec->eth = (ethernet_regs *)MPC5XXX_FEC; + fec->tbdBase = (FEC_TBD *)FEC_BD_BASE; + fec->rbdBase = (FEC_RBD *)(FEC_BD_BASE + FEC_TBD_NUM * sizeof(FEC_TBD)); +#if defined(CONFIG_MPC5xxx_FEC_MII100) + fec->xcv_type = MII100; +#elif defined(CONFIG_MPC5xxx_FEC_MII10) + fec->xcv_type = MII10; +#elif defined(CONFIG_MPC5xxx_FEC_SEVENWIRE) + fec->xcv_type = SEVENWIRE; +#else +#error fec->xcv_type not initialized. +#endif + if (fec->xcv_type != SEVENWIRE) { + /* + * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock + * and do not drop the Preamble. + */ + fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ + } + + dev->priv = (void *)fec; + dev->iobase = MPC5XXX_FEC; + dev->init = mpc5xxx_fec_init; + dev->halt = mpc5xxx_fec_halt; + dev->send = mpc5xxx_fec_send; + dev->recv = mpc5xxx_fec_recv; + + sprintf(dev->name, "FEC ETHERNET"); + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register (dev->name, + fec5xxx_miiphy_read, fec5xxx_miiphy_write); +#endif + + /* + * Try to set the mac address now. The fec mac address is + * a garbage after reset. When not using fec for booting + * the Linux fec driver will try to work with this garbage. + */ + tmp = getenv("ethaddr"); + if (tmp) { + for (i=0; i<6; i++) { + env_enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end+1 : end; + } + mpc5xxx_fec_set_hwaddr(fec, env_enetaddr); + } + + return 1; +} + +/* MII-interface related functions */ +/********************************************************************/ +int fec5xxx_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal) +{ + ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + /* + * reading from any PHY's register is done by properly + * programming the FEC's MII data register. + */ + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy | reg); + + /* + * wait for the related interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Read MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear mii interrupt bit + */ + eth->ievent = 0x00800000; + + /* + * it's now safe to read the PHY's register + */ + *retVal = (uint16) eth->mii_data; + + return 0; +} + +/********************************************************************/ +int fec5xxx_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data) +{ + ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; + uint32 reg; /* convenient holder for the PHY register */ + uint32 phy; /* convenient holder for the PHY */ + int timeout = 0xffff; + + reg = regAddr << FEC_MII_DATA_RA_SHIFT; + phy = phyAddr << FEC_MII_DATA_PA_SHIFT; + + eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | + FEC_MII_DATA_TA | phy | reg | data); + + /* + * wait for the MII interrupt + */ + while ((timeout--) && (!(eth->ievent & 0x00800000))) ; + + if (timeout == 0) { +#if (DEBUG & 0x2) + printf ("Write MDIO failed...\n"); +#endif + return -1; + } + + /* + * clear MII interrupt bit + */ + eth->ievent = 0x00800000; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/mpc5xxx_fec.h b/roms/u-boot-sam460ex/drivers/net/mpc5xxx_fec.h new file mode 100644 index 000000000..16c3e8e91 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/mpc5xxx_fec.h @@ -0,0 +1,282 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This file is based on mpc4200fec.h + * (C) Copyright Motorola, Inc., 2000 + * + * odin ethernet header file + */ + +#ifndef __MPC5XXX_FEC_H +#define __MPC5XXX_FEC_H + +typedef unsigned long uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; + +typedef struct ethernet_register_set { + +/* [10:2]addr = 00 */ + +/* Control and status Registers (offset 000-1FF) */ + + volatile uint32 fec_id; /* MBAR_ETH + 0x000 */ + volatile uint32 ievent; /* MBAR_ETH + 0x004 */ + volatile uint32 imask; /* MBAR_ETH + 0x008 */ + + volatile uint32 RES0[1]; /* MBAR_ETH + 0x00C */ + volatile uint32 r_des_active; /* MBAR_ETH + 0x010 */ + volatile uint32 x_des_active; /* MBAR_ETH + 0x014 */ + volatile uint32 r_des_active_cl; /* MBAR_ETH + 0x018 */ + volatile uint32 x_des_active_cl; /* MBAR_ETH + 0x01C */ + volatile uint32 ivent_set; /* MBAR_ETH + 0x020 */ + volatile uint32 ecntrl; /* MBAR_ETH + 0x024 */ + + volatile uint32 RES1[6]; /* MBAR_ETH + 0x028-03C */ + volatile uint32 mii_data; /* MBAR_ETH + 0x040 */ + volatile uint32 mii_speed; /* MBAR_ETH + 0x044 */ + volatile uint32 mii_status; /* MBAR_ETH + 0x048 */ + + volatile uint32 RES2[5]; /* MBAR_ETH + 0x04C-05C */ + volatile uint32 mib_data; /* MBAR_ETH + 0x060 */ + volatile uint32 mib_control; /* MBAR_ETH + 0x064 */ + + volatile uint32 RES3[6]; /* MBAR_ETH + 0x068-7C */ + volatile uint32 r_activate; /* MBAR_ETH + 0x080 */ + volatile uint32 r_cntrl; /* MBAR_ETH + 0x084 */ + volatile uint32 r_hash; /* MBAR_ETH + 0x088 */ + volatile uint32 r_data; /* MBAR_ETH + 0x08C */ + volatile uint32 ar_done; /* MBAR_ETH + 0x090 */ + volatile uint32 r_test; /* MBAR_ETH + 0x094 */ + volatile uint32 r_mib; /* MBAR_ETH + 0x098 */ + volatile uint32 r_da_low; /* MBAR_ETH + 0x09C */ + volatile uint32 r_da_high; /* MBAR_ETH + 0x0A0 */ + + volatile uint32 RES4[7]; /* MBAR_ETH + 0x0A4-0BC */ + volatile uint32 x_activate; /* MBAR_ETH + 0x0C0 */ + volatile uint32 x_cntrl; /* MBAR_ETH + 0x0C4 */ + volatile uint32 backoff; /* MBAR_ETH + 0x0C8 */ + volatile uint32 x_data; /* MBAR_ETH + 0x0CC */ + volatile uint32 x_status; /* MBAR_ETH + 0x0D0 */ + volatile uint32 x_mib; /* MBAR_ETH + 0x0D4 */ + volatile uint32 x_test; /* MBAR_ETH + 0x0D8 */ + volatile uint32 fdxfc_da1; /* MBAR_ETH + 0x0DC */ + volatile uint32 fdxfc_da2; /* MBAR_ETH + 0x0E0 */ + volatile uint32 paddr1; /* MBAR_ETH + 0x0E4 */ + volatile uint32 paddr2; /* MBAR_ETH + 0x0E8 */ + volatile uint32 op_pause; /* MBAR_ETH + 0x0EC */ + + volatile uint32 RES5[4]; /* MBAR_ETH + 0x0F0-0FC */ + volatile uint32 instr_reg; /* MBAR_ETH + 0x100 */ + volatile uint32 context_reg; /* MBAR_ETH + 0x104 */ + volatile uint32 test_cntrl; /* MBAR_ETH + 0x108 */ + volatile uint32 acc_reg; /* MBAR_ETH + 0x10C */ + volatile uint32 ones; /* MBAR_ETH + 0x110 */ + volatile uint32 zeros; /* MBAR_ETH + 0x114 */ + volatile uint32 iaddr1; /* MBAR_ETH + 0x118 */ + volatile uint32 iaddr2; /* MBAR_ETH + 0x11C */ + volatile uint32 gaddr1; /* MBAR_ETH + 0x120 */ + volatile uint32 gaddr2; /* MBAR_ETH + 0x124 */ + volatile uint32 random; /* MBAR_ETH + 0x128 */ + volatile uint32 rand1; /* MBAR_ETH + 0x12C */ + volatile uint32 tmp; /* MBAR_ETH + 0x130 */ + + volatile uint32 RES6[3]; /* MBAR_ETH + 0x134-13C */ + volatile uint32 fifo_id; /* MBAR_ETH + 0x140 */ + volatile uint32 x_wmrk; /* MBAR_ETH + 0x144 */ + volatile uint32 fcntrl; /* MBAR_ETH + 0x148 */ + volatile uint32 r_bound; /* MBAR_ETH + 0x14C */ + volatile uint32 r_fstart; /* MBAR_ETH + 0x150 */ + volatile uint32 r_count; /* MBAR_ETH + 0x154 */ + volatile uint32 r_lag; /* MBAR_ETH + 0x158 */ + volatile uint32 r_read; /* MBAR_ETH + 0x15C */ + volatile uint32 r_write; /* MBAR_ETH + 0x160 */ + volatile uint32 x_count; /* MBAR_ETH + 0x164 */ + volatile uint32 x_lag; /* MBAR_ETH + 0x168 */ + volatile uint32 x_retry; /* MBAR_ETH + 0x16C */ + volatile uint32 x_write; /* MBAR_ETH + 0x170 */ + volatile uint32 x_read; /* MBAR_ETH + 0x174 */ + + volatile uint32 RES7[2]; /* MBAR_ETH + 0x178-17C */ + volatile uint32 fm_cntrl; /* MBAR_ETH + 0x180 */ + volatile uint32 rfifo_data; /* MBAR_ETH + 0x184 */ + volatile uint32 rfifo_status; /* MBAR_ETH + 0x188 */ + volatile uint32 rfifo_cntrl; /* MBAR_ETH + 0x18C */ + volatile uint32 rfifo_lrf_ptr; /* MBAR_ETH + 0x190 */ + volatile uint32 rfifo_lwf_ptr; /* MBAR_ETH + 0x194 */ + volatile uint32 rfifo_alarm; /* MBAR_ETH + 0x198 */ + volatile uint32 rfifo_rdptr; /* MBAR_ETH + 0x19C */ + volatile uint32 rfifo_wrptr; /* MBAR_ETH + 0x1A0 */ + volatile uint32 tfifo_data; /* MBAR_ETH + 0x1A4 */ + volatile uint32 tfifo_status; /* MBAR_ETH + 0x1A8 */ + volatile uint32 tfifo_cntrl; /* MBAR_ETH + 0x1AC */ + volatile uint32 tfifo_lrf_ptr; /* MBAR_ETH + 0x1B0 */ + volatile uint32 tfifo_lwf_ptr; /* MBAR_ETH + 0x1B4 */ + volatile uint32 tfifo_alarm; /* MBAR_ETH + 0x1B8 */ + volatile uint32 tfifo_rdptr; /* MBAR_ETH + 0x1BC */ + volatile uint32 tfifo_wrptr; /* MBAR_ETH + 0x1C0 */ + + volatile uint32 reset_cntrl; /* MBAR_ETH + 0x1C4 */ + volatile uint32 xmit_fsm; /* MBAR_ETH + 0x1C8 */ + + volatile uint32 RES8[3]; /* MBAR_ETH + 0x1CC-1D4 */ + volatile uint32 rdes_data0; /* MBAR_ETH + 0x1D8 */ + volatile uint32 rdes_data1; /* MBAR_ETH + 0x1DC */ + volatile uint32 r_length; /* MBAR_ETH + 0x1E0 */ + volatile uint32 x_length; /* MBAR_ETH + 0x1E4 */ + volatile uint32 x_addr; /* MBAR_ETH + 0x1E8 */ + volatile uint32 cdes_data; /* MBAR_ETH + 0x1EC */ + volatile uint32 status; /* MBAR_ETH + 0x1F0 */ + volatile uint32 dma_control; /* MBAR_ETH + 0x1F4 */ + volatile uint32 des_cmnd; /* MBAR_ETH + 0x1F8 */ + volatile uint32 data; /* MBAR_ETH + 0x1FC */ + +/* MIB COUNTERS (Offset 200-2FF) */ + + volatile uint32 rmon_t_drop; /* MBAR_ETH + 0x200 */ + volatile uint32 rmon_t_packets; /* MBAR_ETH + 0x204 */ + volatile uint32 rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */ + volatile uint32 rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */ + volatile uint32 rmon_t_crc_align; /* MBAR_ETH + 0x210 */ + volatile uint32 rmon_t_undersize; /* MBAR_ETH + 0x214 */ + volatile uint32 rmon_t_oversize; /* MBAR_ETH + 0x218 */ + volatile uint32 rmon_t_frag; /* MBAR_ETH + 0x21C */ + volatile uint32 rmon_t_jab; /* MBAR_ETH + 0x220 */ + volatile uint32 rmon_t_col; /* MBAR_ETH + 0x224 */ + volatile uint32 rmon_t_p64; /* MBAR_ETH + 0x228 */ + volatile uint32 rmon_t_p65to127; /* MBAR_ETH + 0x22C */ + volatile uint32 rmon_t_p128to255; /* MBAR_ETH + 0x230 */ + volatile uint32 rmon_t_p256to511; /* MBAR_ETH + 0x234 */ + volatile uint32 rmon_t_p512to1023; /* MBAR_ETH + 0x238 */ + volatile uint32 rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */ + volatile uint32 rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */ + volatile uint32 rmon_t_octets; /* MBAR_ETH + 0x244 */ + volatile uint32 ieee_t_drop; /* MBAR_ETH + 0x248 */ + volatile uint32 ieee_t_frame_ok; /* MBAR_ETH + 0x24C */ + volatile uint32 ieee_t_1col; /* MBAR_ETH + 0x250 */ + volatile uint32 ieee_t_mcol; /* MBAR_ETH + 0x254 */ + volatile uint32 ieee_t_def; /* MBAR_ETH + 0x258 */ + volatile uint32 ieee_t_lcol; /* MBAR_ETH + 0x25C */ + volatile uint32 ieee_t_excol; /* MBAR_ETH + 0x260 */ + volatile uint32 ieee_t_macerr; /* MBAR_ETH + 0x264 */ + volatile uint32 ieee_t_cserr; /* MBAR_ETH + 0x268 */ + volatile uint32 ieee_t_sqe; /* MBAR_ETH + 0x26C */ + volatile uint32 t_fdxfc; /* MBAR_ETH + 0x270 */ + volatile uint32 ieee_t_octets_ok; /* MBAR_ETH + 0x274 */ + + volatile uint32 RES9[2]; /* MBAR_ETH + 0x278-27C */ + volatile uint32 rmon_r_drop; /* MBAR_ETH + 0x280 */ + volatile uint32 rmon_r_packets; /* MBAR_ETH + 0x284 */ + volatile uint32 rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */ + volatile uint32 rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */ + volatile uint32 rmon_r_crc_align; /* MBAR_ETH + 0x290 */ + volatile uint32 rmon_r_undersize; /* MBAR_ETH + 0x294 */ + volatile uint32 rmon_r_oversize; /* MBAR_ETH + 0x298 */ + volatile uint32 rmon_r_frag; /* MBAR_ETH + 0x29C */ + volatile uint32 rmon_r_jab; /* MBAR_ETH + 0x2A0 */ + + volatile uint32 rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */ + + volatile uint32 rmon_r_p64; /* MBAR_ETH + 0x2A8 */ + volatile uint32 rmon_r_p65to127; /* MBAR_ETH + 0x2AC */ + volatile uint32 rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */ + volatile uint32 rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */ + volatile uint32 rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */ + volatile uint32 rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */ + volatile uint32 rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */ + volatile uint32 rmon_r_octets; /* MBAR_ETH + 0x2C4 */ + volatile uint32 ieee_r_drop; /* MBAR_ETH + 0x2C8 */ + volatile uint32 ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */ + volatile uint32 ieee_r_crc; /* MBAR_ETH + 0x2D0 */ + volatile uint32 ieee_r_align; /* MBAR_ETH + 0x2D4 */ + volatile uint32 r_macerr; /* MBAR_ETH + 0x2D8 */ + volatile uint32 r_fdxfc; /* MBAR_ETH + 0x2DC */ + volatile uint32 ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */ + + volatile uint32 RES10[6]; /* MBAR_ETH + 0x2E4-2FC */ + + volatile uint32 RES11[64]; /* MBAR_ETH + 0x300-3FF */ +} ethernet_regs; + +/* Receive & Transmit Buffer Descriptor definitions */ +typedef struct BufferDescriptor { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_RBD; +typedef struct { + uint16 status; + uint16 dataLength; + uint32 dataPointer; +} FEC_TBD; + +/* private structure */ +typedef enum { + SEVENWIRE, /* 7-wire */ + MII10, /* MII 10Mbps */ + MII100 /* MII 100Mbps */ +} xceiver_type; + +typedef struct { + ethernet_regs *eth; + xceiver_type xcv_type; /* transceiver type */ + FEC_RBD *rbdBase; /* RBD ring */ + FEC_TBD *tbdBase; /* TBD ring */ + uint16 rbdIndex; /* next receive BD to read */ + uint16 tbdIndex; /* next transmit BD to send */ + uint16 usedTbdIndex; /* next transmit BD to clean */ + uint16 cleanTbdNum; /* the number of available transmit BDs */ +} mpc5xxx_fec_priv; + +/* Ethernet parameter area */ +#define FEC_TBD_BASE (FEC_PARAM_BASE + 0x00) +#define FEC_TBD_NEXT (FEC_PARAM_BASE + 0x04) +#define FEC_RBD_BASE (FEC_PARAM_BASE + 0x08) +#define FEC_RBD_NEXT (FEC_PARAM_BASE + 0x0c) + +/* BD Numer definitions */ +#define FEC_TBD_NUM 48 /* The user can adjust this value */ +#define FEC_RBD_NUM 32 /* The user can adjust this value */ + +/* packet size limit */ +#define FEC_MAX_PKT_SIZE 1536 + +/* RBD bits definitions */ +#define FEC_RBD_EMPTY 0x8000 /* Buffer is empty */ +#define FEC_RBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_RBD_INT 0x1000 /* Interrupt */ +#define FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */ +#define FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */ +#define FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */ +#define FEC_RBD_MC 0x0040 /* The received frame is multicast frame */ +#define FEC_RBD_LG 0x0020 /* Frame length violation */ +#define FEC_RBD_NO 0x0010 /* Nonoctet align frame */ +#define FEC_RBD_SH 0x0008 /* Short frame */ +#define FEC_RBD_CR 0x0004 /* CRC error */ +#define FEC_RBD_OV 0x0002 /* Receive FIFO overrun */ +#define FEC_RBD_TR 0x0001 /* Frame is truncated */ +#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ + FEC_RBD_OV | FEC_RBD_TR) + +/* TBD bits definitions */ +#define FEC_TBD_READY 0x8000 /* Buffer is ready */ +#define FEC_TBD_WRAP 0x2000 /* Last BD in ring */ +#define FEC_TBD_INT 0x1000 /* Interrupt */ +#define FEC_TBD_LAST 0x0800 /* Buffer is last in frame */ +#define FEC_TBD_TC 0x0400 /* Transmit the CRC */ +#define FEC_TBD_ABC 0x0200 /* Append bad CRC */ + +/* MII-related definitios */ +#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */ +#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */ +#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */ +#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */ +#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */ +#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */ +#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */ + +#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */ +#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */ + +#endif /* __MPC5XXX_FEC_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/natsemi.c b/roms/u-boot-sam460ex/drivers/net/natsemi.c new file mode 100644 index 000000000..e09da1d2a --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/natsemi.c @@ -0,0 +1,879 @@ +/* + natsemi.c: A U-Boot driver for the NatSemi DP8381x series. + Author: Mark A. Rakes (mark_rakes@vivato.net) + + Adapted from an Etherboot driver written by: + + Copyright (C) 2001 Entity Cyber, Inc. + + This development of this Etherboot driver was funded by + + Sicom Systems: http://www.sicompos.com/ + + Author: Marty Connor (mdc@thinguin.org) + Adapted from a Linux driver which was written by Donald Becker + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + Original Copyright Notice: + + Written/copyright 1999-2001 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. License for under other terms may be + available. Contact the original author for details. + + The original author may be reached as becker@scyld.com, or at + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support information and updates available at + http://www.scyld.com/network/netsemi.html + + References: + http://www.scyld.com/expert/100mbps.html + http://www.scyld.com/expert/NWay.html + Datasheet is available from: + http://www.national.com/pf/DP/DP83815.html +*/ + +/* Revision History + * October 2002 mar 1.0 + * Initial U-Boot Release. Tested with Netgear FA311 board + * and dp83815 chipset on custom board +*/ + +/* Includes */ +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> + +/* defines */ +#define EEPROM_SIZE 0xb /*12 16-bit chunks, or 24 bytes*/ + +#define DSIZE 0x00000FFF +#define ETH_ALEN 6 +#define CRC_SIZE 4 +#define TOUT_LOOP 500000 +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ + +/* Offsets to the device registers. + Unlike software-only systems, device drivers interact with complex hardware. + It's not useful to define symbolic names for every register bit in the + device. */ +enum register_offsets { + ChipCmd = 0x00, + ChipConfig = 0x04, + EECtrl = 0x08, + IntrMask = 0x14, + IntrEnable = 0x18, + TxRingPtr = 0x20, + TxConfig = 0x24, + RxRingPtr = 0x30, + RxConfig = 0x34, + ClkRun = 0x3C, + RxFilterAddr = 0x48, + RxFilterData = 0x4C, + SiliconRev = 0x58, + PCIPM = 0x44, + BasicControl = 0x80, + BasicStatus = 0x84, + /* These are from the spec, around page 78... on a separate table. */ + PGSEL = 0xCC, + PMDCSR = 0xE4, + TSTDAT = 0xFC, + DSPCFG = 0xF4, + SDCFG = 0x8C +}; + +/* Bit in ChipCmd. */ +enum ChipCmdBits { + ChipReset = 0x100, + RxReset = 0x20, + TxReset = 0x10, + RxOff = 0x08, + RxOn = 0x04, + TxOff = 0x02, + TxOn = 0x01 +}; + +enum ChipConfigBits { + LinkSts = 0x80000000, + HundSpeed = 0x40000000, + FullDuplex = 0x20000000, + TenPolarity = 0x10000000, + AnegDone = 0x08000000, + AnegEnBothBoth = 0x0000E000, + AnegDis100Full = 0x0000C000, + AnegEn100Both = 0x0000A000, + AnegDis100Half = 0x00008000, + AnegEnBothHalf = 0x00006000, + AnegDis10Full = 0x00004000, + AnegEn10Both = 0x00002000, + DuplexMask = 0x00008000, + SpeedMask = 0x00004000, + AnegMask = 0x00002000, + AnegDis10Half = 0x00000000, + ExtPhy = 0x00001000, + PhyRst = 0x00000400, + PhyDis = 0x00000200, + BootRomDisable = 0x00000004, + BEMode = 0x00000001, +}; + +enum TxConfig_bits { + TxDrthMask = 0x3f, + TxFlthMask = 0x3f00, + TxMxdmaMask = 0x700000, + TxMxdma_512 = 0x0, + TxMxdma_4 = 0x100000, + TxMxdma_8 = 0x200000, + TxMxdma_16 = 0x300000, + TxMxdma_32 = 0x400000, + TxMxdma_64 = 0x500000, + TxMxdma_128 = 0x600000, + TxMxdma_256 = 0x700000, + TxCollRetry = 0x800000, + TxAutoPad = 0x10000000, + TxMacLoop = 0x20000000, + TxHeartIgn = 0x40000000, + TxCarrierIgn = 0x80000000 +}; + +enum RxConfig_bits { + RxDrthMask = 0x3e, + RxMxdmaMask = 0x700000, + RxMxdma_512 = 0x0, + RxMxdma_4 = 0x100000, + RxMxdma_8 = 0x200000, + RxMxdma_16 = 0x300000, + RxMxdma_32 = 0x400000, + RxMxdma_64 = 0x500000, + RxMxdma_128 = 0x600000, + RxMxdma_256 = 0x700000, + RxAcceptLong = 0x8000000, + RxAcceptTx = 0x10000000, + RxAcceptRunt = 0x40000000, + RxAcceptErr = 0x80000000 +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0xC0000000, + AcceptMulticast = 0x00200000, + AcceptAllMulticast = 0x20000000, + AcceptAllPhys = 0x10000000, + AcceptMyPhys = 0x08000000 +}; + +typedef struct _BufferDesc { + u32 link; + vu_long cmdsts; + u32 bufptr; + u32 software_use; +} BufferDesc; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn = 0x80000000, DescMore = 0x40000000, DescIntr = 0x20000000, + DescNoCRC = 0x10000000, DescPktOK = 0x08000000, + DescSizeMask = 0xfff, + + DescTxAbort = 0x04000000, DescTxFIFO = 0x02000000, + DescTxCarrier = 0x01000000, DescTxDefer = 0x00800000, + DescTxExcDefer = 0x00400000, DescTxOOWCol = 0x00200000, + DescTxExcColl = 0x00100000, DescTxCollCount = 0x000f0000, + + DescRxAbort = 0x04000000, DescRxOver = 0x02000000, + DescRxDest = 0x01800000, DescRxLong = 0x00400000, + DescRxRunt = 0x00200000, DescRxInvalid = 0x00100000, + DescRxCRC = 0x00080000, DescRxAlign = 0x00040000, + DescRxLoop = 0x00020000, DesRxColl = 0x00010000, +}; + +/* Globals */ +#ifdef NATSEMI_DEBUG +static int natsemi_debug = 0; /* 1 verbose debugging, 0 normal */ +#endif +static u32 SavedClkRun; +static unsigned int cur_rx; +static unsigned int advertising; +static unsigned int rx_config; +static unsigned int tx_config; + +/* Note: transmit and receive buffers and descriptors must be + longword aligned */ +static BufferDesc txd __attribute__ ((aligned(4))); +static BufferDesc rxd[NUM_RX_DESC] __attribute__ ((aligned(4))); + +static unsigned char txb[TX_BUF_SIZE] __attribute__ ((aligned(4))); +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE] + __attribute__ ((aligned(4))); + +/* Function Prototypes */ +#if 0 +static void write_eeprom(struct eth_device *dev, long addr, int location, + short value); +#endif +static int read_eeprom(struct eth_device *dev, long addr, int location); +static int mdio_read(struct eth_device *dev, int phy_id, int location); +static int natsemi_init(struct eth_device *dev, bd_t * bis); +static void natsemi_reset(struct eth_device *dev); +static void natsemi_init_rxfilter(struct eth_device *dev); +static void natsemi_init_txd(struct eth_device *dev); +static void natsemi_init_rxd(struct eth_device *dev); +static void natsemi_set_rx_mode(struct eth_device *dev); +static void natsemi_check_duplex(struct eth_device *dev); +static int natsemi_send(struct eth_device *dev, volatile void *packet, + int length); +static int natsemi_poll(struct eth_device *dev); +static void natsemi_disable(struct eth_device *dev); + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83815}, + {} +}; + +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) + +static inline int +INW(struct eth_device *dev, u_long addr) +{ + return le16_to_cpu(*(vu_short *) (addr + dev->iobase)); +} + +static int +INL(struct eth_device *dev, u_long addr) +{ + return le32_to_cpu(*(vu_long *) (addr + dev->iobase)); +} + +static inline void +OUTW(struct eth_device *dev, int command, u_long addr) +{ + *(vu_short *) ((addr + dev->iobase)) = cpu_to_le16(command); +} + +static inline void +OUTL(struct eth_device *dev, int command, u_long addr) +{ + *(vu_long *) ((addr + dev->iobase)) = cpu_to_le32(command); +} + +/* + * Function: natsemi_initialize + * + * Description: Retrieves the MAC address of the card, and sets up some + * globals required by other routines, and initializes the NIC, making it + * ready to send and receive packets. + * + * Side effects: + * leaves the natsemi initialized, and ready to recieve packets. + * + * Returns: struct eth_device *: pointer to NIC data structure + */ + +int +natsemi_initialize(bd_t * bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase, status, chip_config; + int i, idx = 0; + int prev_eedata; + u32 tmp; + + while (1) { + /* Find PCI device(s) */ + if ((devno = pci_find_devices(supported, idx++)) < 0) { + break; + } + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_0, &iobase); + iobase &= ~0x3; /* bit 1: unused and bit 0: I/O Space Indicator */ + + pci_write_config_dword(devno, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if I/O accesses and Bus Mastering are enabled. */ + pci_read_config_dword(devno, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_MEMORY)) { + printf("Error: Can not enable MEM access.\n"); + continue; + } else if (!(status & PCI_COMMAND_MASTER)) { + printf("Error: Can not enable Bus Mastering.\n"); + continue; + } + + dev = (struct eth_device *) malloc(sizeof *dev); + + sprintf(dev->name, "dp83815#%d", card_number); + dev->iobase = bus_to_phys(iobase); +#ifdef NATSEMI_DEBUG + printf("natsemi: NatSemi ns8381[56] @ %#x\n", dev->iobase); +#endif + dev->priv = (void *) devno; + dev->init = natsemi_init; + dev->halt = natsemi_disable; + dev->send = natsemi_send; + dev->recv = natsemi_poll; + + eth_register(dev); + + card_number++; + + /* Set the latency timer for value. */ + pci_write_config_byte(devno, PCI_LATENCY_TIMER, 0x20); + + udelay(10 * 1000); + + /* natsemi has a non-standard PM control register + * in PCI config space. Some boards apparently need + * to be brought to D0 in this manner. */ + pci_read_config_dword(devno, PCIPM, &tmp); + if (tmp & (0x03 | 0x100)) { + /* D0 state, disable PME assertion */ + u32 newtmp = tmp & ~(0x03 | 0x100); + pci_write_config_dword(devno, PCIPM, newtmp); + } + + printf("natsemi: EEPROM contents:\n"); + for (i = 0; i <= EEPROM_SIZE; i++) { + short eedata = read_eeprom(dev, EECtrl, i); + printf(" %04hx", eedata); + } + printf("\n"); + + /* get MAC address */ + prev_eedata = read_eeprom(dev, EECtrl, 6); + for (i = 0; i < 3; i++) { + int eedata = read_eeprom(dev, EECtrl, i + 7); + dev->enetaddr[i*2] = (eedata << 1) + (prev_eedata >> 15); + dev->enetaddr[i*2+1] = eedata >> 7; + prev_eedata = eedata; + } + + /* Reset the chip to erase any previous misconfiguration. */ + OUTL(dev, ChipReset, ChipCmd); + + advertising = mdio_read(dev, 1, 4); + chip_config = INL(dev, ChipConfig); +#ifdef NATSEMI_DEBUG + printf("%s: Transceiver status %#08X advertising %#08X\n", + dev->name, (int) INL(dev, BasicStatus), advertising); + printf("%s: Transceiver default autoneg. %s 10%s %s duplex.\n", + dev->name, chip_config & AnegMask ? "enabled, advertise" : + "disabled, force", chip_config & SpeedMask ? "0" : "", + chip_config & DuplexMask ? "full" : "half"); +#endif + chip_config |= AnegEnBothBoth; +#ifdef NATSEMI_DEBUG + printf("%s: changed to autoneg. %s 10%s %s duplex.\n", + dev->name, chip_config & AnegMask ? "enabled, advertise" : + "disabled, force", chip_config & SpeedMask ? "0" : "", + chip_config & DuplexMask ? "full" : "half"); +#endif + /*write new autoneg bits, reset phy*/ + OUTL(dev, (chip_config | PhyRst), ChipConfig); + /*un-reset phy*/ + OUTL(dev, chip_config, ChipConfig); + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + SavedClkRun = INL(dev, ClkRun); + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + } + return card_number; +} + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. + The EEPROM code is for common 93c06/46 EEPROMs w/ 6bit addresses. */ + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33MHz PCI, but future 66MHz + access may need a delay. */ +#define eeprom_delay(ee_addr) INL(dev, ee_addr) + +enum EEPROM_Ctrl_Bits { + EE_ShiftClk = 0x04, + EE_DataIn = 0x01, + EE_ChipSelect = 0x08, + EE_DataOut = 0x02 +}; + +#define EE_Write0 (EE_ChipSelect) +#define EE_Write1 (EE_ChipSelect | EE_DataIn) +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { + EE_WrEnCmd = (4 << 6), EE_WriteCmd = (5 << 6), + EE_ReadCmd = (6 << 6), EE_EraseCmd = (7 << 6), +}; + +#if 0 +static void +write_eeprom(struct eth_device *dev, long addr, int location, short value) +{ + int i; + int ee_addr = (typeof(ee_addr))addr; + short wren_cmd = EE_WrEnCmd | 0x30; /*wren is 100 + 11XXXX*/ + short write_cmd = location | EE_WriteCmd; + +#ifdef NATSEMI_DEBUG + printf("write_eeprom: %08x, %04hx, %04hx\n", + dev->iobase + ee_addr, write_cmd, value); +#endif + /* Shift the write enable command bits out. */ + for (i = 9; i >= 0; i--) { + short cmdval = (wren_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, cmdval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, cmdval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + + OUTL(dev, 0, ee_addr); /*bring chip select low*/ + OUTL(dev, EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + + /* Shift the write command bits out. */ + for (i = 9; i >= 0; i--) { + short cmdval = (write_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, cmdval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, cmdval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + + for (i = 0; i < 16; i++) { + short cmdval = (value & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, cmdval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, cmdval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + + OUTL(dev, 0, ee_addr); /*bring chip select low*/ + OUTL(dev, EE_ShiftClk, ee_addr); + for (i = 0; i < 200000; i++) { + OUTL(dev, EE_Write0, ee_addr); /*poll for done*/ + if (INL(dev, ee_addr) & EE_DataOut) { + break; /*finished*/ + } + } + eeprom_delay(ee_addr); + + /* Terminate the EEPROM access. */ + OUTL(dev, EE_Write0, ee_addr); + OUTL(dev, 0, ee_addr); + return; +} +#endif + +static int +read_eeprom(struct eth_device *dev, long addr, int location) +{ + int i; + int retval = 0; + int ee_addr = (typeof(ee_addr))addr; + int read_cmd = location | EE_ReadCmd; + + OUTL(dev, EE_Write0, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + OUTL(dev, dataval, ee_addr); + eeprom_delay(ee_addr); + OUTL(dev, dataval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + OUTL(dev, EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + + for (i = 0; i < 16; i++) { + OUTL(dev, EE_ChipSelect | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + retval |= (INL(dev, ee_addr) & EE_DataOut) ? 1 << i : 0; + OUTL(dev, EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + } + + /* Terminate the EEPROM access. */ + OUTL(dev, EE_Write0, ee_addr); + OUTL(dev, 0, ee_addr); +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("read_eeprom: %08x, %08x, retval %08x\n", + dev->iobase + ee_addr, read_cmd, retval); +#endif + return retval; +} + +/* MII transceiver control section. + The 83815 series has an internal transceiver, and we present the + management registers as if they were MII connected. */ + +static int +mdio_read(struct eth_device *dev, int phy_id, int location) +{ + if (phy_id == 1 && location < 32) + return INL(dev, BasicControl+(location<<2))&0xffff; + else + return 0xffff; +} + +/* Function: natsemi_init + * + * Description: resets the ethernet controller chip and configures + * registers and data structures required for sending and receiving packets. + * + * Arguments: struct eth_device *dev: NIC data structure + * + * returns: int. + */ + +static int +natsemi_init(struct eth_device *dev, bd_t * bis) +{ + + natsemi_reset(dev); + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + + natsemi_init_rxfilter(dev); + natsemi_init_txd(dev); + natsemi_init_rxd(dev); + + /* Configure the PCI bus bursts and FIFO thresholds. */ + tx_config = TxAutoPad | TxCollRetry | TxMxdma_256 | (0x1002); + rx_config = RxMxdma_256 | 0x20; + +#ifdef NATSEMI_DEBUG + printf("%s: Setting TxConfig Register %#08X\n", dev->name, tx_config); + printf("%s: Setting RxConfig Register %#08X\n", dev->name, rx_config); +#endif + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); + + natsemi_check_duplex(dev); + natsemi_set_rx_mode(dev); + + OUTL(dev, (RxOn | TxOn), ChipCmd); + return 1; +} + +/* + * Function: natsemi_reset + * + * Description: soft resets the controller chip + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ +static void +natsemi_reset(struct eth_device *dev) +{ + OUTL(dev, ChipReset, ChipCmd); + + /* On page 78 of the spec, they recommend some settings for "optimum + performance" to be done in sequence. These settings optimize some + of the 100Mbit autodetection circuitry. Also, we only want to do + this for rev C of the chip. */ + if (INL(dev, SiliconRev) == 0x302) { + OUTW(dev, 0x0001, PGSEL); + OUTW(dev, 0x189C, PMDCSR); + OUTW(dev, 0x0000, TSTDAT); + OUTW(dev, 0x5040, DSPCFG); + OUTW(dev, 0x008C, SDCFG); + } + /* Disable interrupts using the mask. */ + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); +} + +/* Function: natsemi_init_rxfilter + * + * Description: sets receive filter address to our MAC address + * + * Arguments: struct eth_device *dev: NIC data structure + * + * returns: void. + */ + +static void +natsemi_init_rxfilter(struct eth_device *dev) +{ + int i; + + for (i = 0; i < ETH_ALEN; i += 2) { + OUTL(dev, i, RxFilterAddr); + OUTW(dev, dev->enetaddr[i] + (dev->enetaddr[i + 1] << 8), + RxFilterData); + } +} + +/* + * Function: natsemi_init_txd + * + * Description: initializes the Tx descriptor + * + * Arguments: struct eth_device *dev: NIC data structure + * + * returns: void. + */ + +static void +natsemi_init_txd(struct eth_device *dev) +{ + txd.link = (u32) 0; + txd.cmdsts = (u32) 0; + txd.bufptr = (u32) & txb[0]; + + /* load Transmit Descriptor Register */ + OUTL(dev, (u32) & txd, TxRingPtr); +#ifdef NATSEMI_DEBUG + printf("natsemi_init_txd: TX descriptor reg loaded with: %#08X\n", + INL(dev, TxRingPtr)); +#endif +} + +/* Function: natsemi_init_rxd + * + * Description: initializes the Rx descriptor ring + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_init_rxd(struct eth_device *dev) +{ + int i; + + cur_rx = 0; + + /* init RX descriptor */ + for (i = 0; i < NUM_RX_DESC; i++) { + rxd[i].link = + cpu_to_le32((i + 1 < + NUM_RX_DESC) ? (u32) & rxd[i + + 1] : (u32) & + rxd[0]); + rxd[i].cmdsts = cpu_to_le32((u32) RX_BUF_SIZE); + rxd[i].bufptr = cpu_to_le32((u32) & rxb[i * RX_BUF_SIZE]); +#ifdef NATSEMI_DEBUG + printf + ("natsemi_init_rxd: rxd[%d]=%p link=%X cmdsts=%lX bufptr=%X\n", + i, &rxd[i], le32_to_cpu(rxd[i].link), + rxd[i].cmdsts, rxd[i].bufptr); +#endif + } + + /* load Receive Descriptor Register */ + OUTL(dev, (u32) & rxd[0], RxRingPtr); + +#ifdef NATSEMI_DEBUG + printf("natsemi_init_rxd: RX descriptor register loaded with: %X\n", + INL(dev, RxRingPtr)); +#endif +} + +/* Function: natsemi_set_rx_mode + * + * Description: + * sets the receive mode to accept all broadcast packets and packets + * with our MAC address, and reject all multicast packets. + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_set_rx_mode(struct eth_device *dev) +{ + u32 rx_mode = AcceptBroadcast | AcceptMyPhys; + + OUTL(dev, rx_mode, RxFilterAddr); +} + +static void +natsemi_check_duplex(struct eth_device *dev) +{ + int duplex = INL(dev, ChipConfig) & FullDuplex ? 1 : 0; + +#ifdef NATSEMI_DEBUG + printf("%s: Setting %s-duplex based on negotiated link" + " capability.\n", dev->name, duplex ? "full" : "half"); +#endif + if (duplex) { + rx_config |= RxAcceptTx; + tx_config |= (TxCarrierIgn | TxHeartIgn); + } else { + rx_config &= ~RxAcceptTx; + tx_config &= ~(TxCarrierIgn | TxHeartIgn); + } + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); +} + +/* Function: natsemi_send + * + * Description: transmits a packet and waits for completion or timeout. + * + * Returns: void. */ +static int +natsemi_send(struct eth_device *dev, volatile void *packet, int length) +{ + u32 i, status = 0; + u32 tx_status = 0; + u32 *tx_ptr = &tx_status; + vu_long *res = (vu_long *)tx_ptr; + + /* Stop the transmitter */ + OUTL(dev, TxOff, ChipCmd); + +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("natsemi_send: sending %d bytes\n", (int) length); +#endif + + /* set the transmit buffer descriptor and enable Transmit State Machine */ + txd.link = cpu_to_le32(0); + txd.bufptr = cpu_to_le32(phys_to_bus((u32) packet)); + txd.cmdsts = cpu_to_le32(DescOwn | length); + + /* load Transmit Descriptor Register */ + OUTL(dev, phys_to_bus((u32) & txd), TxRingPtr); +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("natsemi_send: TX descriptor register loaded with: %#08X\n", + INL(dev, TxRingPtr)); +#endif + /* restart the transmitter */ + OUTL(dev, TxOn, ChipCmd); + + for (i = 0; + (*res = le32_to_cpu(txd.cmdsts)) & DescOwn; + i++) { + if (i >= TOUT_LOOP) { + printf + ("%s: tx error buffer not ready: txd.cmdsts == %#X\n", + dev->name, tx_status); + goto Done; + } + } + + if (!(tx_status & DescPktOK)) { + printf("natsemi_send: Transmit error, Tx status %X.\n", + tx_status); + goto Done; + } + + status = 1; + Done: + return status; +} + +/* Function: natsemi_poll + * + * Description: checks for a received packet and returns it if found. + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: 1 if packet was received. + * 0 if no packet was received. + * + * Side effects: + * Returns (copies) the packet to the array dev->packet. + * Returns the length of the packet. + */ + +static int +natsemi_poll(struct eth_device *dev) +{ + int retstat = 0; + int length = 0; + u32 rx_status = le32_to_cpu(rxd[cur_rx].cmdsts); + + if (!(rx_status & (u32) DescOwn)) + return retstat; +#ifdef NATSEMI_DEBUG + if (natsemi_debug) + printf("natsemi_poll: got a packet: cur_rx:%d, status:%X\n", + cur_rx, rx_status); +#endif + length = (rx_status & DSIZE) - CRC_SIZE; + + if ((rx_status & (DescMore | DescPktOK | DescRxLong)) != DescPktOK) { + printf + ("natsemi_poll: Corrupted packet received, buffer status = %X\n", + rx_status); + retstat = 0; + } else { /* give packet to higher level routine */ + NetReceive((rxb + cur_rx * RX_BUF_SIZE), length); + retstat = 1; + } + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].cmdsts = cpu_to_le32(RX_BUF_SIZE); + rxd[cur_rx].bufptr = cpu_to_le32((u32) & rxb[cur_rx * RX_BUF_SIZE]); + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* re-enable the potentially idle receive state machine */ + OUTL(dev, RxOn, ChipCmd); + + return retstat; +} + +/* Function: natsemi_disable + * + * Description: Turns off interrupts and stops Tx and Rx engines + * + * Arguments: struct eth_device *dev: NIC data structure + * + * Returns: void. + */ + +static void +natsemi_disable(struct eth_device *dev) +{ + /* Disable interrupts using the mask. */ + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); + + /* Stop the chip's Tx and Rx processes. */ + OUTL(dev, RxOff | TxOff, ChipCmd); + + /* Restore PME enable bit */ + OUTL(dev, SavedClkRun, ClkRun); +} diff --git a/roms/u-boot-sam460ex/drivers/net/ne2000.c b/roms/u-boot-sam460ex/drivers/net/ne2000.c new file mode 100644 index 000000000..ab5eec772 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ne2000.c @@ -0,0 +1,259 @@ +/* +Ported to U-Boot by Christian Pellegrin <chri@ascensit.com> + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +========================================================================== + +dev/if_dp83902a.c + +Ethernet device driver for NS DP83902a ethernet controller + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### +------------------------------------------- +This file is part of eCos, the Embedded Configurable Operating System. +Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + +eCos is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 or (at your option) any later version. + +eCos is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with eCos; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +As a special exception, if other files instantiate templates or use macros +or inline functions from this file, or you compile this file and link it +with other works to produce a work based on this file, this file does not +by itself cause the resulting work to be covered by the GNU General Public +License. However the source code for this file must still be made available +in accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based on +this file might be covered by the GNU General Public License. + +Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +at http://sources.redhat.com/ecos/ecos-license/ +------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + +------------------------------------------- + +Portions of this software may have been derived from OpenBSD or other sources, +and are covered by the appropriate copyright disclaimers included herein. + +------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + +Author(s): gthomas +Contributors: gthomas, jskov, rsandifo +Date: 2001-06-13 +Purpose: +Description: + +FIXME: Will fail if pinged with large packets (1520 bytes) +Add promisc config +Add SNMP + +####DESCRIPTIONEND#### + +========================================================================== +*/ + +#include <common.h> +#include <command.h> + +/* NE2000 base header file */ +#include "ne2000_base.h" + +#define mdelay(n) udelay((n)*1000) +/* find prom (taken from pc_net_cs.c from Linux) */ + +#include "8390.h" +/* +typedef struct hw_info_t { + u_int offset; + u_char a0, a1, a2; + u_int flags; +} hw_info_t; +*/ +#define DELAY_OUTPUT 0x01 +#define HAS_MISC_REG 0x02 +#define USE_BIG_BUF 0x04 +#define HAS_IBM_MISC 0x08 +#define IS_DL10019 0x10 +#define IS_DL10022 0x20 +#define HAS_MII 0x40 +#define USE_SHMEM 0x80 /* autodetected */ + +#define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */ +#define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */ +#define MII_PHYID_REV_MASK 0xfffffff0 +#define MII_PHYID_REG1 0x02 +#define MII_PHYID_REG2 0x03 + +static hw_info_t hw_info[] = { + { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT }, + { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 }, + { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 }, + { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94, + DELAY_OUTPUT | HAS_IBM_MISC }, + { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 }, + { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 }, + { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 }, + { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 }, + { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 }, + { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 }, + { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 }, + { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 }, + { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 }, + { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 }, + { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 }, + { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 }, + { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 }, + { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 }, + { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45, + HAS_MISC_REG | HAS_IBM_MISC }, + { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 }, + { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 }, + { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 }, + { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b, + DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF }, + { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 }, + { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 }, + { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 }, + { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 }, + { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }, + { /* Qemu */ 0x0, 0x52, 0x54, 0x00, 0 } +}; + +#define NR_INFO (sizeof(hw_info)/sizeof(hw_info_t)) + +#define PCNET_CMD 0x00 +#define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */ + +static void pcnet_reset_8390(u8* addr) +{ + int i, r; + + n2k_outb(E8390_NODMA + E8390_PAGE0+E8390_STOP, E8390_CMD); + PRINTK("cmd (at %lx) is %x\n", addr + E8390_CMD, n2k_inb(E8390_CMD)); + n2k_outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, E8390_CMD); + PRINTK("cmd (at %lx) is %x\n", addr + E8390_CMD, n2k_inb(E8390_CMD)); + n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); + PRINTK("cmd (at %lx) is %x\n", addr + E8390_CMD, n2k_inb(E8390_CMD)); + n2k_outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD); + + n2k_outb(n2k_inb(PCNET_RESET), PCNET_RESET); + + for (i = 0; i < 100; i++) { + if ((r = (n2k_inb(EN0_ISR) & ENISR_RESET)) != 0) + break; + PRINTK("got %x in reset\n", r); + udelay(100); + } + n2k_outb(ENISR_RESET, EN0_ISR); /* Ack intr. */ + + if (i == 100) + printf("pcnet_reset_8390() did not complete.\n"); +} /* pcnet_reset_8390 */ + +int get_prom(u8* mac_addr, u8* base_addr) +{ + u8 prom[32]; + int i, j; + struct { + u_char value, offset; + } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + + PRINTK ("trying to get MAC via prom reading\n"); + + pcnet_reset_8390 (base_addr); + + mdelay (10); + + for (i = 0; i < sizeof (program_seq) / sizeof (program_seq[0]); i++) + n2k_outb (program_seq[i].value, program_seq[i].offset); + + PRINTK ("PROM:"); + for (i = 0; i < 32; i++) { + prom[i] = n2k_inb (PCNET_DATAPORT); + PRINTK (" %02x", prom[i]); + } + PRINTK ("\n"); + for (i = 0; i < NR_INFO; i++) { + if ((prom[0] == hw_info[i].a0) && + (prom[2] == hw_info[i].a1) && + (prom[4] == hw_info[i].a2)) { + PRINTK ("matched board %d\n", i); + break; + } + } + if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) { + PRINTK ("on exit i is %d/%ld\n", i, NR_INFO); + PRINTK ("MAC address is "); + for (j = 0; j < 6; j++) { + mac_addr[j] = prom[j << 1]; + PRINTK ("%02x:", mac_addr[i]); + } + PRINTK ("\n"); + return (i < NR_INFO) ? i : 0; + } + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/ne2000.h b/roms/u-boot-sam460ex/drivers/net/ne2000.h new file mode 100644 index 000000000..2cde6be43 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ne2000.h @@ -0,0 +1,94 @@ +/* +Ported to U-Boot by Christian Pellegrin <chri@ascensit.com> + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +========================================================================== + + dev/dp83902a.h + + National Semiconductor DP83902a ethernet chip + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### + ------------------------------------------- + This file is part of eCos, the Embedded Configurable Operating System. + Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + eCos is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 or (at your option) any later version. + + eCos is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with eCos; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. + + Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + at http://sources.redhat.com/ecos/ecos-license/ + ------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + + ------------------------------------------- + + Portions of this software may have been derived from OpenBSD or other sources, + and are covered by the appropriate copyright disclaimers included herein. + + ------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + + Author(s): gthomas + Contributors: gthomas, jskov + Date: 2001-06-13 + Purpose: + Description: + +####DESCRIPTIONEND#### + +========================================================================== +*/ + +/* + * NE2000 support header file. + * Created by Nobuhiro Iwamatsu <iwamatsu@nigauri.org> + */ + +#ifndef __DRIVERS_NE2000_H__ +#define __DRIVERS_NE2000_H__ + +/* Enable NE2000 basic init function */ +#define NE2000_BASIC_INIT + +#define DP_DATA 0x10 +#define START_PG 0x50 /* First page of TX buffer */ +#define START_PG2 0x48 +#define STOP_PG 0x80 /* Last page +1 of RX ring */ + +#define RX_START 0x50 +#define RX_END 0x80 + +#define DP_IN(_b_, _o_, _d_) (_d_) = *( (vu_char *) ((_b_)+(_o_))) +#define DP_OUT(_b_, _o_, _d_) *( (vu_char *) ((_b_)+(_o_))) = (_d_) +#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_char *) ((_b_))) +#define DP_OUT_DATA(_b_, _d_) *( (vu_char *) ((_b_))) = (_d_) +#endif /* __DRIVERS_NE2000_H__ */ diff --git a/roms/u-boot-sam460ex/drivers/net/ne2000_base.c b/roms/u-boot-sam460ex/drivers/net/ne2000_base.c new file mode 100644 index 000000000..f93f93227 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ne2000_base.c @@ -0,0 +1,757 @@ +/* +Ported to U-Boot by Christian Pellegrin <chri@ascensit.com> + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + +========================================================================== + +dev/if_dp83902a.c + +Ethernet device driver for NS DP83902a ethernet controller + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### +------------------------------------------- +This file is part of eCos, the Embedded Configurable Operating System. +Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + +eCos is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 or (at your option) any later version. + +eCos is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License along +with eCos; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +As a special exception, if other files instantiate templates or use macros +or inline functions from this file, or you compile this file and link it +with other works to produce a work based on this file, this file does not +by itself cause the resulting work to be covered by the GNU General Public +License. However the source code for this file must still be made available +in accordance with section (3) of the GNU General Public License. + +This exception does not invalidate any other reasons why a work based on +this file might be covered by the GNU General Public License. + +Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +at http://sources.redhat.com/ecos/ecos-license/ +------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + +------------------------------------------- + +Portions of this software may have been derived from OpenBSD or other sources, +and are covered by the appropriate copyright disclaimers included herein. + +------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + +Author(s): gthomas +Contributors: gthomas, jskov, rsandifo +Date: 2001-06-13 +Purpose: +Description: + +FIXME: Will fail if pinged with large packets (1520 bytes) +Add promisc config +Add SNMP + +####DESCRIPTIONEND#### + +========================================================================== +*/ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <malloc.h> + +#define mdelay(n) udelay((n)*1000) +/* forward definition of function used for the uboot interface */ +void uboot_push_packet_len(int len); +void uboot_push_tx_done(int key, int val); + +/* NE2000 base header file */ +#include "ne2000_base.h" + +#if defined(CONFIG_DRIVER_AX88796L) +/* AX88796L support */ +#include "ax88796.h" +#else +/* Basic NE2000 chip support */ +#include "ne2000.h" +#endif + +static dp83902a_priv_data_t nic; /* just one instance of the card supported */ + +static bool +dp83902a_init(void) +{ + dp83902a_priv_data_t *dp = &nic; + u8* base; +#if defined(NE2000_BASIC_INIT) + int i; +#endif + + DEBUG_FUNCTION(); + + base = dp->base; + if (!base) + return false; /* No device found */ + + DEBUG_LINE(); + +#if defined(NE2000_BASIC_INIT) + /* AX88796L doesn't need */ + /* Prepare ESA */ + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE1); /* Select page 1 */ + /* Use the address from the serial EEPROM */ + for (i = 0; i < 6; i++) + DP_IN(base, DP_P1_PAR0+i, dp->esa[i]); + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE0); /* Select page 0 */ + + printf("NE2000 - %s ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", + "eeprom", + dp->esa[0], + dp->esa[1], + dp->esa[2], + dp->esa[3], + dp->esa[4], + dp->esa[5] ); + +#endif /* NE2000_BASIC_INIT */ + return true; +} + +static void +dp83902a_stop(void) +{ + dp83902a_priv_data_t *dp = &nic; + u8 *base = dp->base; + + DEBUG_FUNCTION(); + + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); /* Brutal */ + DP_OUT(base, DP_ISR, 0xFF); /* Clear any pending interrupts */ + DP_OUT(base, DP_IMR, 0x00); /* Disable all interrupts */ + + dp->running = false; +} + +/* + * This function is called to "start up" the interface. It may be called + * multiple times, even when the hardware is already running. It will be + * called whenever something "hardware oriented" changes and should leave + * the hardware ready to send/receive packets. + */ +static void +dp83902a_start(u8 * enaddr) +{ + dp83902a_priv_data_t *dp = &nic; + u8 *base = dp->base; + int i; + + DEBUG_FUNCTION(); + + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_STOP); /* Brutal */ + DP_OUT(base, DP_DCR, DP_DCR_INIT); + DP_OUT(base, DP_RBCH, 0); /* Remote byte count */ + DP_OUT(base, DP_RBCL, 0); + DP_OUT(base, DP_RCR, DP_RCR_MON); /* Accept no packets */ + DP_OUT(base, DP_TCR, DP_TCR_LOCAL); /* Transmitter [virtually] off */ + DP_OUT(base, DP_TPSR, dp->tx_buf1); /* Transmitter start page */ + dp->tx1 = dp->tx2 = 0; + dp->tx_next = dp->tx_buf1; + dp->tx_started = false; + dp->running = true; + DP_OUT(base, DP_PSTART, dp->rx_buf_start); /* Receive ring start page */ + DP_OUT(base, DP_BNDRY, dp->rx_buf_end - 1); /* Receive ring boundary */ + DP_OUT(base, DP_PSTOP, dp->rx_buf_end); /* Receive ring end page */ + dp->rx_next = dp->rx_buf_start - 1; + dp->running = true; + DP_OUT(base, DP_ISR, 0xFF); /* Clear any pending interrupts */ + DP_OUT(base, DP_IMR, DP_IMR_All); /* Enable all interrupts */ + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE1 | DP_CR_STOP); /* Select page 1 */ + DP_OUT(base, DP_P1_CURP, dp->rx_buf_start); /* Current page - next free page for Rx */ + dp->running = true; + for (i = 0; i < ETHER_ADDR_LEN; i++) { + /* FIXME */ + /*((vu_short*)( base + ((DP_P1_PAR0 + i) * 2) + + * 0x1400)) = enaddr[i];*/ + DP_OUT(base, DP_P1_PAR0+i, enaddr[i]); + } + /* Enable and start device */ + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_OUT(base, DP_TCR, DP_TCR_NORMAL); /* Normal transmit operations */ + DP_OUT(base, DP_RCR, DP_RCR_AB); /* Accept broadcast, no errors, no multicast */ + dp->running = true; +} + +/* + * This routine is called to start the transmitter. It is split out from the + * data handling routine so it may be called either when data becomes first + * available or when an Tx interrupt occurs + */ + +static void +dp83902a_start_xmit(int start_page, int len) +{ + dp83902a_priv_data_t *dp = (dp83902a_priv_data_t *) &nic; + u8 *base = dp->base; + + DEBUG_FUNCTION(); + +#if DEBUG & 1 + printf("Tx pkt %d len %d\n", start_page, len); + if (dp->tx_started) + printf("TX already started?!?\n"); +#endif + + DP_OUT(base, DP_ISR, (DP_ISR_TxP | DP_ISR_TxE)); + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_OUT(base, DP_TBCL, len & 0xFF); + DP_OUT(base, DP_TBCH, len >> 8); + DP_OUT(base, DP_TPSR, start_page); + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_TXPKT | DP_CR_START); + + dp->tx_started = true; +} + +/* + * This routine is called to send data to the hardware. It is known a-priori + * that there is free buffer space (dp->tx_next). + */ +static void +dp83902a_send(u8 *data, int total_len, u32 key) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + int len, start_page, pkt_len, i, isr; +#if DEBUG & 4 + int dx; +#endif + + DEBUG_FUNCTION(); + + len = pkt_len = total_len; + if (pkt_len < IEEE_8023_MIN_FRAME) + pkt_len = IEEE_8023_MIN_FRAME; + + start_page = dp->tx_next; + if (dp->tx_next == dp->tx_buf1) { + dp->tx1 = start_page; + dp->tx1_len = pkt_len; + dp->tx1_key = key; + dp->tx_next = dp->tx_buf2; + } else { + dp->tx2 = start_page; + dp->tx2_len = pkt_len; + dp->tx2_key = key; + dp->tx_next = dp->tx_buf1; + } + +#if DEBUG & 5 + printf("TX prep page %d len %d\n", start_page, pkt_len); +#endif + + DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ + { + /* + * Dummy read. The manual sez something slightly different, + * but the code is extended a bit to do what Hitachi's monitor + * does (i.e., also read data). + */ + + u16 tmp; + int len = 1; + + DP_OUT(base, DP_RSAL, 0x100 - len); + DP_OUT(base, DP_RSAH, (start_page - 1) & 0xff); + DP_OUT(base, DP_RBCL, len); + DP_OUT(base, DP_RBCH, 0); + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_RDMA | DP_CR_START); + DP_IN_DATA(dp->data, tmp); + } + +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA + /* + * Stall for a bit before continuing to work around random data + * corruption problems on some platforms. + */ + CYGACC_CALL_IF_DELAY_US(1); +#endif + + /* Send data to device buffer(s) */ + DP_OUT(base, DP_RSAL, 0); + DP_OUT(base, DP_RSAH, start_page); + DP_OUT(base, DP_RBCL, pkt_len & 0xFF); + DP_OUT(base, DP_RBCH, pkt_len >> 8); + DP_OUT(base, DP_CR, DP_CR_WDMA | DP_CR_START); + + /* Put data into buffer */ +#if DEBUG & 4 + printf(" sg buf %08lx len %08x\n ", (u32)data, len); + dx = 0; +#endif + while (len > 0) { +#if DEBUG & 4 + printf(" %02x", *data); + if (0 == (++dx % 16)) printf("\n "); +#endif + + DP_OUT_DATA(dp->data, *data++); + len--; + } +#if DEBUG & 4 + printf("\n"); +#endif + if (total_len < pkt_len) { +#if DEBUG & 4 + printf(" + %d bytes of padding\n", pkt_len - total_len); +#endif + /* Padding to 802.3 length was required */ + for (i = total_len; i < pkt_len;) { + i++; + DP_OUT_DATA(dp->data, 0); + } + } + +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA + /* + * After last data write, delay for a bit before accessing the + * device again, or we may get random data corruption in the last + * datum (on some platforms). + */ + CYGACC_CALL_IF_DELAY_US(1); +#endif + + /* Wait for DMA to complete */ + do { + DP_IN(base, DP_ISR, isr); + } while ((isr & DP_ISR_RDC) == 0); + + /* Then disable DMA */ + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + + /* Start transmit if not already going */ + if (!dp->tx_started) { + if (start_page == dp->tx1) { + dp->tx_int = 1; /* Expecting interrupt from BUF1 */ + } else { + dp->tx_int = 2; /* Expecting interrupt from BUF2 */ + } + dp83902a_start_xmit(start_page, pkt_len); + } +} + +/* + * This function is called when a packet has been received. It's job is + * to prepare to unload the packet from the hardware. Once the length of + * the packet is known, the upper layer of the driver can be told. When + * the upper layer is ready to unload the packet, the internal function + * 'dp83902a_recv' will be called to actually fetch it from the hardware. + */ +static void +dp83902a_RxEvent(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + u8 rsr; + u8 rcv_hdr[4]; + int i, len, pkt, cur; + + DEBUG_FUNCTION(); + + DP_IN(base, DP_RSR, rsr); + while (true) { + /* Read incoming packet header */ + DP_OUT(base, DP_CR, DP_CR_PAGE1 | DP_CR_NODMA | DP_CR_START); + DP_IN(base, DP_P1_CURP, cur); + DP_OUT(base, DP_P1_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_IN(base, DP_BNDRY, pkt); + + pkt += 1; + if (pkt == dp->rx_buf_end) + pkt = dp->rx_buf_start; + + if (pkt == cur) { + break; + } + DP_OUT(base, DP_RBCL, sizeof(rcv_hdr)); + DP_OUT(base, DP_RBCH, 0); + DP_OUT(base, DP_RSAL, 0); + DP_OUT(base, DP_RSAH, pkt); + if (dp->rx_next == pkt) { + if (cur == dp->rx_buf_start) + DP_OUT(base, DP_BNDRY, dp->rx_buf_end - 1); + else + DP_OUT(base, DP_BNDRY, cur - 1); /* Update pointer */ + return; + } + dp->rx_next = pkt; + DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ + DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA + CYGACC_CALL_IF_DELAY_US(10); +#endif + + /* read header (get data size)*/ + for (i = 0; i < sizeof(rcv_hdr);) { + DP_IN_DATA(dp->data, rcv_hdr[i++]); + } + +#if DEBUG & 5 + printf("rx hdr %02x %02x %02x %02x\n", + rcv_hdr[0], rcv_hdr[1], rcv_hdr[2], rcv_hdr[3]); +#endif + len = ((rcv_hdr[3] << 8) | rcv_hdr[2]) - sizeof(rcv_hdr); + + /* data read */ + uboot_push_packet_len(len); + + if (rcv_hdr[1] == dp->rx_buf_start) + DP_OUT(base, DP_BNDRY, dp->rx_buf_end - 1); + else + DP_OUT(base, DP_BNDRY, rcv_hdr[1] - 1); /* Update pointer */ + } +} + +/* + * This function is called as a result of the "eth_drv_recv()" call above. + * It's job is to actually fetch data for a packet from the hardware once + * memory buffers have been allocated for the packet. Note that the buffers + * may come in pieces, using a scatter-gather list. This allows for more + * efficient processing in the upper layers of the stack. + */ +static void +dp83902a_recv(u8 *data, int len) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + int i, mlen; + u8 saved_char = 0; + bool saved; +#if DEBUG & 4 + int dx; +#endif + + DEBUG_FUNCTION(); + +#if DEBUG & 5 + printf("Rx packet %d length %d\n", dp->rx_next, len); +#endif + + /* Read incoming packet data */ + DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START); + DP_OUT(base, DP_RBCL, len & 0xFF); + DP_OUT(base, DP_RBCH, len >> 8); + DP_OUT(base, DP_RSAL, 4); /* Past header */ + DP_OUT(base, DP_RSAH, dp->rx_next); + DP_OUT(base, DP_ISR, DP_ISR_RDC); /* Clear end of DMA */ + DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START); +#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA + CYGACC_CALL_IF_DELAY_US(10); +#endif + + saved = false; + for (i = 0; i < 1; i++) { + if (data) { + mlen = len; +#if DEBUG & 4 + printf(" sg buf %08lx len %08x \n", (u32) data, mlen); + dx = 0; +#endif + while (0 < mlen) { + /* Saved byte from previous loop? */ + if (saved) { + *data++ = saved_char; + mlen--; + saved = false; + continue; + } + + { + u8 tmp; + DP_IN_DATA(dp->data, tmp); +#if DEBUG & 4 + printf(" %02x", tmp); + if (0 == (++dx % 16)) printf("\n "); +#endif + *data++ = tmp;; + mlen--; + } + } +#if DEBUG & 4 + printf("\n"); +#endif + } + } +} + +static void +dp83902a_TxEvent(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + u8 tsr; + u32 key; + + DEBUG_FUNCTION(); + + DP_IN(base, DP_TSR, tsr); + if (dp->tx_int == 1) { + key = dp->tx1_key; + dp->tx1 = 0; + } else { + key = dp->tx2_key; + dp->tx2 = 0; + } + /* Start next packet if one is ready */ + dp->tx_started = false; + if (dp->tx1) { + dp83902a_start_xmit(dp->tx1, dp->tx1_len); + dp->tx_int = 1; + } else if (dp->tx2) { + dp83902a_start_xmit(dp->tx2, dp->tx2_len); + dp->tx_int = 2; + } else { + dp->tx_int = 0; + } + /* Tell higher level we sent this packet */ + uboot_push_tx_done(key, 0); +} + +/* + * Read the tally counters to clear them. Called in response to a CNT + * interrupt. + */ +static void +dp83902a_ClearCounters(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + u8 cnt1, cnt2, cnt3; + + DP_IN(base, DP_FER, cnt1); + DP_IN(base, DP_CER, cnt2); + DP_IN(base, DP_MISSED, cnt3); + DP_OUT(base, DP_ISR, DP_ISR_CNT); +} + +/* + * Deal with an overflow condition. This code follows the procedure set + * out in section 7.0 of the datasheet. + */ +static void +dp83902a_Overflow(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)&nic; + u8 *base = dp->base; + u8 isr; + + /* Issue a stop command and wait 1.6ms for it to complete. */ + DP_OUT(base, DP_CR, DP_CR_STOP | DP_CR_NODMA); + CYGACC_CALL_IF_DELAY_US(1600); + + /* Clear the remote byte counter registers. */ + DP_OUT(base, DP_RBCL, 0); + DP_OUT(base, DP_RBCH, 0); + + /* Enter loopback mode while we clear the buffer. */ + DP_OUT(base, DP_TCR, DP_TCR_LOCAL); + DP_OUT(base, DP_CR, DP_CR_START | DP_CR_NODMA); + + /* + * Read in as many packets as we can and acknowledge any and receive + * interrupts. Since the buffer has overflowed, a receive event of + * some kind will have occured. + */ + dp83902a_RxEvent(); + DP_OUT(base, DP_ISR, DP_ISR_RxP|DP_ISR_RxE); + + /* Clear the overflow condition and leave loopback mode. */ + DP_OUT(base, DP_ISR, DP_ISR_OFLW); + DP_OUT(base, DP_TCR, DP_TCR_NORMAL); + + /* + * If a transmit command was issued, but no transmit event has occured, + * restart it here. + */ + DP_IN(base, DP_ISR, isr); + if (dp->tx_started && !(isr & (DP_ISR_TxP|DP_ISR_TxE))) { + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_TXPKT | DP_CR_START); + } +} + +static void +dp83902a_poll(void) +{ + struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *) &nic; + u8 *base = dp->base; + u8 isr; + + DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE0 | DP_CR_START); + DP_IN(base, DP_ISR, isr); + while (0 != isr) { + /* + * The CNT interrupt triggers when the MSB of one of the error + * counters is set. We don't much care about these counters, but + * we should read their values to reset them. + */ + if (isr & DP_ISR_CNT) { + dp83902a_ClearCounters(); + } + /* + * Check for overflow. It's a special case, since there's a + * particular procedure that must be followed to get back into + * a running state.a + */ + if (isr & DP_ISR_OFLW) { + dp83902a_Overflow(); + } else { + /* + * Other kinds of interrupts can be acknowledged simply by + * clearing the relevant bits of the ISR. Do that now, then + * handle the interrupts we care about. + */ + DP_OUT(base, DP_ISR, isr); /* Clear set bits */ + if (!dp->running) break; /* Is this necessary? */ + /* + * Check for tx_started on TX event since these may happen + * spuriously it seems. + */ + if (isr & (DP_ISR_TxP|DP_ISR_TxE) && dp->tx_started) { + dp83902a_TxEvent(); + } + if (isr & (DP_ISR_RxP|DP_ISR_RxE)) { + dp83902a_RxEvent(); + } + } + DP_IN(base, DP_ISR, isr); + } +} + + +/* U-boot specific routines */ +static u8 *pbuf = NULL; + +static int pkey = -1; +static int initialized = 0; + +void uboot_push_packet_len(int len) { + PRINTK("pushed len = %d\n", len); + if (len >= 2000) { + printf("NE2000: packet too big\n"); + return; + } + dp83902a_recv(&pbuf[0], len); + + /*Just pass it to the upper layer*/ + NetReceive(&pbuf[0], len); +} + +void uboot_push_tx_done(int key, int val) { + PRINTK("pushed key = %d\n", key); + pkey = key; +} + +int eth_init(bd_t *bd) { + int r; + u8 dev_addr[6]; + char ethaddr[20]; + + PRINTK("### eth_init\n"); + + if (!pbuf) { + pbuf = malloc(2000); + if (!pbuf) { + printf("Cannot allocate rx buffer\n"); + return -1; + } + } + +#ifdef CONFIG_DRIVER_NE2000_CCR + { + vu_char *p = (vu_char *) CONFIG_DRIVER_NE2000_CCR; + + PRINTK("CCR before is %x\n", *p); + *p = CONFIG_DRIVER_NE2000_VAL; + PRINTK("CCR after is %x\n", *p); + } +#endif + + nic.base = (u8 *) CONFIG_DRIVER_NE2000_BASE; + + r = get_prom(dev_addr, nic.base); + if (!r) + return -1; + + sprintf (ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X", + dev_addr[0], dev_addr[1], + dev_addr[2], dev_addr[3], + dev_addr[4], dev_addr[5]) ; + PRINTK("Set environment from HW MAC addr = \"%s\"\n", ethaddr); + setenv ("ethaddr", ethaddr); + + nic.data = nic.base + DP_DATA; + nic.tx_buf1 = START_PG; + nic.tx_buf2 = START_PG2; + nic.rx_buf_start = RX_START; + nic.rx_buf_end = RX_END; + + if (dp83902a_init() == false) + return -1; + + dp83902a_start(dev_addr); + initialized = 1; + + return 0; +} + +void eth_halt() { + + PRINTK("### eth_halt\n"); + if(initialized) + dp83902a_stop(); + initialized = 0; +} + +int eth_rx() { + dp83902a_poll(); + return 1; +} + +int eth_send(volatile void *packet, int length) { + int tmo; + + PRINTK("### eth_send\n"); + + pkey = -1; + + dp83902a_send((u8 *) packet, length, 666); + tmo = get_timer (0) + TOUT * CONFIG_SYS_HZ; + while(1) { + dp83902a_poll(); + if (pkey != -1) { + PRINTK("Packet sucesfully sent\n"); + return 0; + } + if (get_timer (0) >= tmo) { + printf("transmission error (timoeut)\n"); + return 0; + } + + } + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/ne2000_base.h b/roms/u-boot-sam460ex/drivers/net/ne2000_base.h new file mode 100644 index 000000000..5446de4bb --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ne2000_base.h @@ -0,0 +1,308 @@ +/* +Ported to U-Boot by Christian Pellegrin <chri@ascensit.com> + +Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and +eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world +are GPL, so this is, of course, GPL. + + +========================================================================== + + dev/dp83902a.h + + National Semiconductor DP83902a ethernet chip + +========================================================================== +####ECOSGPLCOPYRIGHTBEGIN#### + ------------------------------------------- + This file is part of eCos, the Embedded Configurable Operating System. + Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + + eCos is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 or (at your option) any later version. + + eCos is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with eCos; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file does not + by itself cause the resulting work to be covered by the GNU General Public + License. However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based on + this file might be covered by the GNU General Public License. + + Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. + at http://sources.redhat.com/ecos/ecos-license/ + ------------------------------------------- +####ECOSGPLCOPYRIGHTEND#### +####BSDCOPYRIGHTBEGIN#### + + ------------------------------------------- + + Portions of this software may have been derived from OpenBSD or other sources, + and are covered by the appropriate copyright disclaimers included herein. + + ------------------------------------------- + +####BSDCOPYRIGHTEND#### +========================================================================== +#####DESCRIPTIONBEGIN#### + + Author(s): gthomas + Contributors: gthomas, jskov + Date: 2001-06-13 + Purpose: + Description: + +####DESCRIPTIONEND#### + +========================================================================== + +*/ + +/* + ------------------------------------------------------------------------ + Macros for accessing DP registers + These can be overridden by the platform header +*/ + +#ifndef __NE2000_BASE_H__ +#define __NE2000_BASE_H__ + +#define bool int +#define false 0 +#define true 1 + +/* + * Debugging details + * + * Set to perms of: + * 0 disables all debug output + * 1 for process debug output + * 2 for added data IO output: get_reg, put_reg + * 4 for packet allocation/free output + * 8 for only startup status, so we can tell we're installed OK + */ +#if 0 +#define DEBUG 0xf +#else +#define DEBUG 0 +#endif + +#if DEBUG & 1 +#define DEBUG_FUNCTION() do { printf("%s\n", __FUNCTION__); } while (0) +#define DEBUG_LINE() do { printf("%d\n", __LINE__); } while (0) +#define PRINTK(args...) printf(args) +#else +#define DEBUG_FUNCTION() do {} while(0) +#define DEBUG_LINE() do {} while(0) +#define PRINTK(args...) +#endif + +/* timeout for tx/rx in s */ +#define TOUT 5 +/* Ether MAC address size */ +#define ETHER_ADDR_LEN 6 + + +#define CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA 1 +#define CYGACC_CALL_IF_DELAY_US(X) udelay(X) + +/* H/W infomation struct */ +typedef struct hw_info_t { + u32 offset; + u8 a0, a1, a2; + u32 flags; +} hw_info_t; + +typedef struct dp83902a_priv_data { + u8* base; + u8* data; + u8* reset; + int tx_next; /* First free Tx page */ + int tx_int; /* Expecting interrupt from this buffer */ + int rx_next; /* First free Rx page */ + int tx1, tx2; /* Page numbers for Tx buffers */ + u32 tx1_key, tx2_key; /* Used to ack when packet sent */ + int tx1_len, tx2_len; + bool tx_started, running, hardwired_esa; + u8 esa[6]; + void* plf_priv; + + /* Buffer allocation */ + int tx_buf1, tx_buf2; + int rx_buf_start, rx_buf_end; +} dp83902a_priv_data_t; + +/* ------------------------------------------------------------------------ */ +/* Register offsets */ + +#define DP_CR 0x00 +#define DP_CLDA0 0x01 +#define DP_PSTART 0x01 /* write */ +#define DP_CLDA1 0x02 +#define DP_PSTOP 0x02 /* write */ +#define DP_BNDRY 0x03 +#define DP_TSR 0x04 +#define DP_TPSR 0x04 /* write */ +#define DP_NCR 0x05 +#define DP_TBCL 0x05 /* write */ +#define DP_FIFO 0x06 +#define DP_TBCH 0x06 /* write */ +#define DP_ISR 0x07 +#define DP_CRDA0 0x08 +#define DP_RSAL 0x08 /* write */ +#define DP_CRDA1 0x09 +#define DP_RSAH 0x09 /* write */ +#define DP_RBCL 0x0a /* write */ +#define DP_RBCH 0x0b /* write */ +#define DP_RSR 0x0c +#define DP_RCR 0x0c /* write */ +#define DP_FER 0x0d +#define DP_TCR 0x0d /* write */ +#define DP_CER 0x0e +#define DP_DCR 0x0e /* write */ +#define DP_MISSED 0x0f +#define DP_IMR 0x0f /* write */ +#define DP_DATAPORT 0x10 /* "eprom" data port */ + +#define DP_P1_CR 0x00 +#define DP_P1_PAR0 0x01 +#define DP_P1_PAR1 0x02 +#define DP_P1_PAR2 0x03 +#define DP_P1_PAR3 0x04 +#define DP_P1_PAR4 0x05 +#define DP_P1_PAR5 0x06 +#define DP_P1_CURP 0x07 +#define DP_P1_MAR0 0x08 +#define DP_P1_MAR1 0x09 +#define DP_P1_MAR2 0x0a +#define DP_P1_MAR3 0x0b +#define DP_P1_MAR4 0x0c +#define DP_P1_MAR5 0x0d +#define DP_P1_MAR6 0x0e +#define DP_P1_MAR7 0x0f + +#define DP_P2_CR 0x00 +#define DP_P2_PSTART 0x01 +#define DP_P2_CLDA0 0x01 /* write */ +#define DP_P2_PSTOP 0x02 +#define DP_P2_CLDA1 0x02 /* write */ +#define DP_P2_RNPP 0x03 +#define DP_P2_TPSR 0x04 +#define DP_P2_LNPP 0x05 +#define DP_P2_ACH 0x06 +#define DP_P2_ACL 0x07 +#define DP_P2_RCR 0x0c +#define DP_P2_TCR 0x0d +#define DP_P2_DCR 0x0e +#define DP_P2_IMR 0x0f + +/* Command register - common to all pages */ + +#define DP_CR_STOP 0x01 /* Stop: software reset */ +#define DP_CR_START 0x02 /* Start: initialize device */ +#define DP_CR_TXPKT 0x04 /* Transmit packet */ +#define DP_CR_RDMA 0x08 /* Read DMA (recv data from device) */ +#define DP_CR_WDMA 0x10 /* Write DMA (send data to device) */ +#define DP_CR_SEND 0x18 /* Send packet */ +#define DP_CR_NODMA 0x20 /* Remote (or no) DMA */ +#define DP_CR_PAGE0 0x00 /* Page select */ +#define DP_CR_PAGE1 0x40 +#define DP_CR_PAGE2 0x80 +#define DP_CR_PAGEMSK 0x3F /* Used to mask out page bits */ + +/* Data configuration register */ + +#define DP_DCR_WTS 0x01 /* 1=16 bit word transfers */ +#define DP_DCR_BOS 0x02 /* 1=Little Endian */ +#define DP_DCR_LAS 0x04 /* 1=Single 32 bit DMA mode */ +#define DP_DCR_LS 0x08 /* 1=normal mode, 0=loopback */ +#define DP_DCR_ARM 0x10 /* 0=no send command (program I/O) */ +#define DP_DCR_FIFO_1 0x00 /* FIFO threshold */ +#define DP_DCR_FIFO_2 0x20 +#define DP_DCR_FIFO_4 0x40 +#define DP_DCR_FIFO_6 0x60 + +#define DP_DCR_INIT (DP_DCR_LS|DP_DCR_FIFO_4) + +/* Interrupt status register */ + +#define DP_ISR_RxP 0x01 /* Packet received */ +#define DP_ISR_TxP 0x02 /* Packet transmitted */ +#define DP_ISR_RxE 0x04 /* Receive error */ +#define DP_ISR_TxE 0x08 /* Transmit error */ +#define DP_ISR_OFLW 0x10 /* Receive overflow */ +#define DP_ISR_CNT 0x20 /* Tally counters need emptying */ +#define DP_ISR_RDC 0x40 /* Remote DMA complete */ +#define DP_ISR_RESET 0x80 /* Device has reset (shutdown, error) */ + +/* Interrupt mask register */ + +#define DP_IMR_RxP 0x01 /* Packet received */ +#define DP_IMR_TxP 0x02 /* Packet transmitted */ +#define DP_IMR_RxE 0x04 /* Receive error */ +#define DP_IMR_TxE 0x08 /* Transmit error */ +#define DP_IMR_OFLW 0x10 /* Receive overflow */ +#define DP_IMR_CNT 0x20 /* Tall counters need emptying */ +#define DP_IMR_RDC 0x40 /* Remote DMA complete */ + +#define DP_IMR_All 0x3F /* Everything but remote DMA */ + +/* Receiver control register */ + +#define DP_RCR_SEP 0x01 /* Save bad(error) packets */ +#define DP_RCR_AR 0x02 /* Accept runt packets */ +#define DP_RCR_AB 0x04 /* Accept broadcast packets */ +#define DP_RCR_AM 0x08 /* Accept multicast packets */ +#define DP_RCR_PROM 0x10 /* Promiscuous mode */ +#define DP_RCR_MON 0x20 /* Monitor mode - 1=accept no packets */ + +/* Receiver status register */ + +#define DP_RSR_RxP 0x01 /* Packet received */ +#define DP_RSR_CRC 0x02 /* CRC error */ +#define DP_RSR_FRAME 0x04 /* Framing error */ +#define DP_RSR_FO 0x08 /* FIFO overrun */ +#define DP_RSR_MISS 0x10 /* Missed packet */ +#define DP_RSR_PHY 0x20 /* 0=pad match, 1=mad match */ +#define DP_RSR_DIS 0x40 /* Receiver disabled */ +#define DP_RSR_DFR 0x80 /* Receiver processing deferred */ + +/* Transmitter control register */ + +#define DP_TCR_NOCRC 0x01 /* 1=inhibit CRC */ +#define DP_TCR_NORMAL 0x00 /* Normal transmitter operation */ +#define DP_TCR_LOCAL 0x02 /* Internal NIC loopback */ +#define DP_TCR_INLOOP 0x04 /* Full internal loopback */ +#define DP_TCR_OUTLOOP 0x08 /* External loopback */ +#define DP_TCR_ATD 0x10 /* Auto transmit disable */ +#define DP_TCR_OFFSET 0x20 /* Collision offset adjust */ + +/* Transmit status register */ + +#define DP_TSR_TxP 0x01 /* Packet transmitted */ +#define DP_TSR_COL 0x04 /* Collision (at least one) */ +#define DP_TSR_ABT 0x08 /* Aborted because of too many collisions */ +#define DP_TSR_CRS 0x10 /* Lost carrier */ +#define DP_TSR_FU 0x20 /* FIFO underrun */ +#define DP_TSR_CDH 0x40 /* Collision Detect Heartbeat */ +#define DP_TSR_OWC 0x80 /* Collision outside normal window */ + +#define IEEE_8023_MAX_FRAME 1518 /* Largest possible ethernet frame */ +#define IEEE_8023_MIN_FRAME 64 /* Smallest possible ethernet frame */ + +/* Functions */ +int get_prom(u8* mac_addr, u8* base_addr); + +#endif /* __NE2000_BASE_H__ */ diff --git a/roms/u-boot-sam460ex/drivers/net/netarm_eth.c b/roms/u-boot-sam460ex/drivers/net/netarm_eth.c new file mode 100644 index 000000000..c9e324ee2 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/netarm_eth.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2004 IMMS gGmbH <www.imms.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * author(s): Thomas Elste, <info@elste.org> + * (some parts derived from uCLinux Netarm Ethernet Driver) + */ + + +#include <common.h> +#include <command.h> +#include <net.h> +#include "netarm_eth.h" +#include <asm/arch/netarm_registers.h> + +static int na_mii_poll_busy (void); + +static void na_get_mac_addr (void) +{ + unsigned short p[3]; + char *m_addr; + char ethaddr[20]; + + m_addr = (char *) p; + + p[0] = (unsigned short) GET_EADDR (NETARM_ETH_SAL_STATION_ADDR_1); + p[1] = (unsigned short) GET_EADDR (NETARM_ETH_SAL_STATION_ADDR_2); + p[2] = (unsigned short) GET_EADDR (NETARM_ETH_SAL_STATION_ADDR_3); + + sprintf (ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X", + m_addr[0], m_addr[1], + m_addr[2], m_addr[3], m_addr[4], m_addr[5]); + + printf ("HW-MAC Address: %s\n", ethaddr); + + /* set env, todo: check if already an adress is set */ + setenv ("ethaddr", ethaddr); +} + +static void na_mii_write (int reg, int value) +{ + int mii_addr; + + /* Select register */ + mii_addr = CONFIG_SYS_ETH_PHY_ADDR + reg; + SET_EADDR (NETARM_ETH_MII_ADDR, mii_addr); + /* Write value */ + SET_EADDR (NETARM_ETH_MII_WRITE, value); + na_mii_poll_busy (); +} + +static unsigned int na_mii_read (int reg) +{ + int mii_addr, val; + + /* Select register */ + mii_addr = CONFIG_SYS_ETH_PHY_ADDR + reg; + SET_EADDR (NETARM_ETH_MII_ADDR, mii_addr); + /* do one management cycle */ + SET_EADDR (NETARM_ETH_MII_CMD, + GET_EADDR (NETARM_ETH_MII_CMD) | NETARM_ETH_MIIC_RSTAT); + na_mii_poll_busy (); + /* Return read value */ + val = GET_EADDR (NETARM_ETH_MII_READ); + return val; +} + +static int na_mii_poll_busy (void) +{ + /* arm simple, non interrupt dependent timer */ + reset_timer_masked (); + while (get_timer_masked () < NA_MII_POLL_BUSY_DELAY) { + if (!(GET_EADDR (NETARM_ETH_MII_IND) & NETARM_ETH_MIII_BUSY)) { + return 1; + } + } + printf ("na_mii_busy timeout\n"); + return (0); +} + +static int na_mii_identify_phy (void) +{ + int id_reg_a = 0; + + /* get phy id register */ + id_reg_a = na_mii_read (MII_PHY_ID); + + if (id_reg_a == 0x0043) { + /* This must be an Enable or a Lucent LU3X31 PHY chip */ + return 1; + } else if (id_reg_a == 0x0013) { + /* it is an Intel LXT971A */ + return 1; + } + return (0); +} + +static int na_mii_negotiate (void) +{ + int i = 0; + + /* Enable auto-negotiation */ + na_mii_write (MII_PHY_AUTONEGADV, 0x01e1); + /* FIXME: 0x01E1 is 100Mb half and full duplex, 0x0061 is 10Mb only */ + /* Restart auto-negotiation */ + na_mii_write (MII_PHY_CONTROL, 0x1200); + + /* status register is 0xffff after setting the autoneg restart bit */ + while (na_mii_read (MII_PHY_STATUS) == 0xffff) { + i++; + } + + /* na_mii_read uses the timer already, so we can't use it again for + timeout checking. + Instead we just try some times. + */ + for (i = 0; i < 40000; i++) { + if ((na_mii_read (MII_PHY_STATUS) & 0x0024) == 0x0024) { + return 0; + } + } + /* + printf("*Warning* autonegotiation timeout, status: 0x%x\n",na_mii_read(MII_PHY_STATUS)); + */ + return (1); +} + +static unsigned int na_mii_check_speed (void) +{ + unsigned int status; + + /* Read Status register */ + status = na_mii_read (MII_PHY_STATUS); + /* Check link status. If 0, default to 100 Mbps. */ + if ((status & 0x0004) == 0) { + printf ("*Warning* no link detected, set default speed to 100Mbs\n"); + return 1; + } else { + if ((na_mii_read (17) & 0x4000) != 0) { + printf ("100Mbs link detected\n"); + return 1; + } else { + printf ("10Mbs link detected\n"); + return 0; + } + } + return 0; +} + +static int reset_eth (void) +{ + int pt; + + na_get_mac_addr (); + pt = na_mii_identify_phy (); + + /* reset the phy */ + na_mii_write (MII_PHY_CONTROL, 0x8000); + reset_timer_masked (); + while (get_timer_masked () < NA_MII_NEGOTIATE_DELAY) { + if ((na_mii_read (MII_PHY_STATUS) & 0x8000) == 0) { + break; + } + } + if (get_timer_masked () >= NA_MII_NEGOTIATE_DELAY) + printf ("phy reset timeout\n"); + + /* set the PCS reg */ + SET_EADDR (NETARM_ETH_PCS_CFG, NETARM_ETH_PCSC_CLKS_25M | + NETARM_ETH_PCSC_ENJAB | NETARM_ETH_PCSC_NOCFR); + + na_mii_negotiate (); + na_mii_check_speed (); + + /* Delay 10 millisecond. (Maybe this should be 1 second.) */ + udelay (10000); + + /* Turn receive on. + Enable statistics register autozero on read. + Do not insert MAC address on transmit. + Do not enable special test modes. */ + SET_EADDR (NETARM_ETH_STL_CFG, + (NETARM_ETH_STLC_AUTOZ | NETARM_ETH_STLC_RXEN)); + + /* Set the inter-packet gap delay to 0.96us for MII. + The NET+ARM H/W Reference Guide indicates that the Back-to-back IPG + Gap Timer Register should be set to 0x15 and the Non Back-to-back IPG + Gap Timer Register should be set to 0x00000C12 for the MII PHY. */ + SET_EADDR (NETARM_ETH_B2B_IPG_GAP_TMR, 0x15); + SET_EADDR (NETARM_ETH_NB2B_IPG_GAP_TMR, 0x00000C12); + + /* Add CRC to end of packets. + Pad packets to minimum length of 64 bytes. + Allow unlimited length transmit packets. + Receive all broadcast packets. + NOTE: Multicast addressing is NOT enabled here currently. */ + SET_EADDR (NETARM_ETH_MAC_CFG, + (NETARM_ETH_MACC_CRCEN | + NETARM_ETH_MACC_PADEN | NETARM_ETH_MACC_HUGEN)); + SET_EADDR (NETARM_ETH_SAL_FILTER, NETARM_ETH_SALF_BROAD); + + /* enable fifos */ + SET_EADDR (NETARM_ETH_GEN_CTRL, + (NETARM_ETH_GCR_ERX | NETARM_ETH_GCR_ETX)); + + return (0); +} + + +extern int eth_init (bd_t * bd) +{ + reset_eth (); + return 0; +} + +extern void eth_halt (void) +{ + SET_EADDR (NETARM_ETH_GEN_CTRL, 0); +} + +/* Get a data block via Ethernet */ +extern int eth_rx (void) +{ + int i; + unsigned short rxlen; + unsigned int *addr; + unsigned int rxstatus, lastrxlen; + char *pa; + + /* RXBR is 1, data block was received */ + if ((GET_EADDR (NETARM_ETH_GEN_STAT) & NETARM_ETH_GST_RXBR) == 0) + return 0; + + /* get status register and the length of received block */ + rxstatus = GET_EADDR (NETARM_ETH_RX_STAT); + rxlen = (rxstatus & NETARM_ETH_RXSTAT_SIZE) >> 16; + + if (rxlen == 0) + return 0; + + /* clear RXBR to make fifo available */ + SET_EADDR (NETARM_ETH_GEN_STAT, + GET_EADDR (NETARM_ETH_GEN_STAT) & ~NETARM_ETH_GST_RXBR); + + /* clear TXBC to make fifo available */ + /* According to NETARM50 data manual you just have to clear + RXBR but that has no effect. Only after clearing TXBC the + Fifo becomes readable. */ + SET_EADDR (NETARM_ETH_GEN_STAT, + GET_EADDR (NETARM_ETH_GEN_STAT) & ~NETARM_ETH_GST_TXBC); + + addr = (unsigned int *) NetRxPackets[0]; + pa = (char *) NetRxPackets[0]; + + /* read the fifo */ + for (i = 0; i < rxlen / 4; i++) { + *addr = GET_EADDR (NETARM_ETH_FIFO_DAT1); + addr++; + } + + if (GET_EADDR (NETARM_ETH_GEN_STAT) & NETARM_ETH_GST_RXREGR) { + /* RXFDB indicates wether the last word is 1,2,3 or 4 bytes long */ + lastrxlen = + (GET_EADDR (NETARM_ETH_GEN_STAT) & + NETARM_ETH_GST_RXFDB) >> 28; + *addr = GET_EADDR (NETARM_ETH_FIFO_DAT1); + switch (lastrxlen) { + case 1: + *addr &= 0xff000000; + break; + case 2: + *addr &= 0xffff0000; + break; + case 3: + *addr &= 0xffffff00; + break; + } + } + + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], rxlen); + + return rxlen; +} + +/* Send a data block via Ethernet. */ +extern int eth_send (volatile void *packet, int length) +{ + int i, length32; + char *pa; + unsigned int *pa32, lastp = 0, rest; + + pa = (char *) packet; + pa32 = (unsigned int *) packet; + length32 = length / 4; + rest = length % 4; + + /* make sure there's no garbage in the last word */ + switch (rest) { + case 0: + lastp = pa32[length32]; + length32--; + break; + case 1: + lastp = pa32[length32] & 0x000000ff; + break; + case 2: + lastp = pa32[length32] & 0x0000ffff; + break; + case 3: + lastp = pa32[length32] & 0x00ffffff; + break; + } + + /* write to the fifo */ + for (i = 0; i < length32; i++) + SET_EADDR (NETARM_ETH_FIFO_DAT1, pa32[i]); + + /* the last word is written to an extra register, this + starts the transmission */ + SET_EADDR (NETARM_ETH_FIFO_DAT2, lastp); + + /* NETARM_ETH_TXSTAT_TXOK should be checked, to know if the transmission + went fine. But we can't use the timer for a timeout loop because + of it is used already in upper layers. So we just try some times. */ + i = 0; + while (i < 50000) { + if ((GET_EADDR (NETARM_ETH_TX_STAT) & NETARM_ETH_TXSTAT_TXOK) + == NETARM_ETH_TXSTAT_TXOK) + return 0; + i++; + } + + printf ("eth_send timeout\n"); + return 1; +} diff --git a/roms/u-boot-sam460ex/drivers/net/netarm_eth.h b/roms/u-boot-sam460ex/drivers/net/netarm_eth.h new file mode 100644 index 000000000..8edab82da --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/netarm_eth.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2003 IMMS gGmbH <www.imms.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * author(s): Thomas Elste, <info@elste.org> + */ + +#include <asm/types.h> +#include <config.h> + +#ifdef CONFIG_DRIVER_NETARMETH + +#define SET_EADDR(ad,val) *(volatile unsigned int*)(ad + NETARM_ETH_MODULE_BASE) = val +#define GET_EADDR(ad) (*(volatile unsigned int*)(ad + NETARM_ETH_MODULE_BASE)) + +#define NA_MII_POLL_BUSY_DELAY 900 + +/* MII negotiation timeout value + 500 jiffies = 5 seconds */ +#define NA_MII_NEGOTIATE_DELAY 30 + +/* Registers in the physical layer chip */ +#define MII_PHY_CONTROL 0 +#define MII_PHY_STATUS 1 +#define MII_PHY_ID 2 +#define MII_PHY_AUTONEGADV 4 + +#endif /* CONFIG_DRIVER_NETARMETH */ diff --git a/roms/u-boot-sam460ex/drivers/net/netconsole.c b/roms/u-boot-sam460ex/drivers/net/netconsole.c new file mode 100644 index 000000000..e27bb3e71 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/netconsole.c @@ -0,0 +1,262 @@ +/* + * (C) Copyright 2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <stdio_dev.h> +#include <net.h> + +DECLARE_GLOBAL_DATA_PTR; + +static char input_buffer[512]; +static int input_size = 0; /* char count in input buffer */ +static int input_offset = 0; /* offset to valid chars in input buffer */ +static int input_recursion = 0; +static int output_recursion = 0; +static int net_timeout; +static uchar nc_ether[6]; /* server enet address */ +static IPaddr_t nc_ip; /* server ip */ +static short nc_port; /* source/target port */ +static const char *output_packet; /* used by first send udp */ +static int output_packet_len = 0; + +static void nc_wait_arp_handler (uchar * pkt, unsigned dest, unsigned src, + unsigned len) +{ + NetState = NETLOOP_SUCCESS; /* got arp reply - quit net loop */ +} + +static void nc_handler (uchar * pkt, unsigned dest, unsigned src, + unsigned len) +{ + if (input_size) + NetState = NETLOOP_SUCCESS; /* got input - quit net loop */ +} + +static void nc_timeout (void) +{ + NetState = NETLOOP_SUCCESS; +} + +void NcStart (void) +{ + if (!output_packet_len || memcmp (nc_ether, NetEtherNullAddr, 6)) { + /* going to check for input packet */ + NetSetHandler (nc_handler); + NetSetTimeout (net_timeout, nc_timeout); + } else { + /* send arp request */ + uchar *pkt; + NetSetHandler (nc_wait_arp_handler); + pkt = (uchar *) NetTxPacket + NetEthHdrSize () + IP_HDR_SIZE; + memcpy (pkt, output_packet, output_packet_len); + NetSendUDPPacket (nc_ether, nc_ip, nc_port, nc_port, output_packet_len); + } +} + +int nc_input_packet (uchar * pkt, unsigned dest, unsigned src, unsigned len) +{ + int end, chunk; + + if (dest != nc_port || !len) + return 0; /* not for us */ + + if (input_size == sizeof input_buffer) + return 1; /* no space */ + if (len > sizeof input_buffer - input_size) + len = sizeof input_buffer - input_size; + + end = input_offset + input_size; + if (end > sizeof input_buffer) + end -= sizeof input_buffer; + + chunk = len; + if (end + len > sizeof input_buffer) { + chunk = sizeof input_buffer - end; + memcpy(input_buffer, pkt + chunk, len - chunk); + } + memcpy (input_buffer + end, pkt, chunk); + + input_size += len; + + return 1; +} + +static void nc_send_packet (const char *buf, int len) +{ + struct eth_device *eth; + int inited = 0; + uchar *pkt; + uchar *ether; + IPaddr_t ip; + + if ((eth = eth_get_dev ()) == NULL) { + return; + } + + if (!memcmp (nc_ether, NetEtherNullAddr, 6)) { + if (eth->state == ETH_STATE_ACTIVE) + return; /* inside net loop */ + output_packet = buf; + output_packet_len = len; + NetLoop (NETCONS); /* wait for arp reply and send packet */ + output_packet_len = 0; + return; + } + + if (eth->state != ETH_STATE_ACTIVE) { + if (eth_init (gd->bd) < 0) + return; + inited = 1; + } + pkt = (uchar *) NetTxPacket + NetEthHdrSize () + IP_HDR_SIZE; + memcpy (pkt, buf, len); + ether = nc_ether; + ip = nc_ip; + NetSendUDPPacket (ether, ip, nc_port, nc_port, len); + + if (inited) + eth_halt (); +} + +static int nc_start(void) +{ + int netmask, our_ip; + + nc_port = 6666; /* default port */ + + if (getenv ("ncip")) { + char *p; + + nc_ip = getenv_IPaddr ("ncip"); + if (!nc_ip) + return -1; /* ncip is 0.0.0.0 */ + if ((p = strchr (getenv ("ncip"), ':')) != NULL) + nc_port = simple_strtoul (p + 1, NULL, 10); + } else + nc_ip = ~0; /* ncip is not set */ + + our_ip = getenv_IPaddr ("ipaddr"); + netmask = getenv_IPaddr ("netmask"); + + if (nc_ip == ~0 || /* 255.255.255.255 */ + ((netmask & our_ip) == (netmask & nc_ip) && /* on the same net */ + (netmask | nc_ip) == ~0)) /* broadcast to our net */ + memset (nc_ether, 0xff, sizeof nc_ether); + else + memset (nc_ether, 0, sizeof nc_ether); /* force arp request */ + + return 0; +} + +static void nc_putc(char c) +{ + if (output_recursion) + return; + output_recursion = 1; + + nc_send_packet (&c, 1); + + output_recursion = 0; +} + +static void nc_puts(const char *s) +{ + int len; + + if (output_recursion) + return; + output_recursion = 1; + + if ((len = strlen (s)) > 512) + len = 512; + + nc_send_packet (s, len); + + output_recursion = 0; +} + +static int nc_getc(void) +{ + uchar c; + + input_recursion = 1; + + net_timeout = 0; /* no timeout */ + while (!input_size) + NetLoop (NETCONS); + + input_recursion = 0; + + c = input_buffer[input_offset++]; + + if (input_offset >= sizeof input_buffer) + input_offset -= sizeof input_buffer; + input_size--; + + return c; +} + +static int nc_tstc(void) +{ + struct eth_device *eth; + + if (input_recursion) + return 0; + + if (input_size) + return 1; + + eth = eth_get_dev (); + if (eth && eth->state == ETH_STATE_ACTIVE) + return 0; /* inside net loop */ + + input_recursion = 1; + + net_timeout = 1; + NetLoop (NETCONS); /* kind of poll */ + + input_recursion = 0; + + return input_size != 0; +} + +int drv_nc_init (void) +{ + struct stdio_dev dev; + int rc; + + memset (&dev, 0, sizeof (dev)); + + strcpy (dev.name, "nc"); + dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + dev.start = nc_start; + dev.putc = nc_putc; + dev.puts = nc_puts; + dev.getc = nc_getc; + dev.tstc = nc_tstc; + + rc = stdio_register (&dev); + + return (rc == 0) ? 1 : rc; +} diff --git a/roms/u-boot-sam460ex/drivers/net/nicext.h b/roms/u-boot-sam460ex/drivers/net/nicext.h new file mode 100644 index 000000000..ff422e773 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/nicext.h @@ -0,0 +1,109 @@ +/**************************************************************************** + * Copyright(c) 2000-2001 Broadcom Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Name: nicext.h + * + * Description: Broadcom Network Interface Card Extension (NICE) is an + * extension to Linux NET device kernel mode drivers. + * NICE is designed to provide additional functionalities, + * such as receive packet intercept. To support Broadcom NICE, + * the network device driver can be modified by adding an + * device ioctl handler and by indicating receiving packets + * to the NICE receive handler. Broadcom NICE will only be + * enabled by a NICE-aware intermediate driver, such as + * Broadcom Advanced Server Program Driver (BASP). When NICE + * is not enabled, the modified network device drivers + * functions exactly as other non-NICE aware drivers. + * + * Author: Frankie Fan + * + * Created: September 17, 2000 + * + ****************************************************************************/ +#ifndef _nicext_h_ +#define _nicext_h_ + +/* + * ioctl for NICE + */ +#define SIOCNICE SIOCDEVPRIVATE+7 + +/* + * SIOCNICE: + * + * The following structure needs to be less than IFNAMSIZ (16 bytes) because + * we're overloading ifreq.ifr_ifru. + * + * If 16 bytes is not enough, we should consider relaxing this because + * this is no field after ifr_ifru in the ifreq structure. But we may + * run into future compatiability problem in case of changing struct ifreq. + */ +struct nice_req +{ + __u32 cmd; + + union + { +#ifdef __KERNEL__ + /* cmd = NICE_CMD_SET_RX or NICE_CMD_GET_RX */ + struct + { + void (*nrqus1_rx)( struct sk_buff*, void* ); + void* nrqus1_ctx; + } nrqu_nrqus1; + + /* cmd = NICE_CMD_QUERY_SUPPORT */ + struct + { + __u32 nrqus2_magic; + __u32 nrqus2_support_rx:1; + __u32 nrqus2_support_vlan:1; + __u32 nrqus2_support_get_speed:1; + } nrqu_nrqus2; +#endif + + /* cmd = NICE_CMD_GET_SPEED */ + struct + { + unsigned int nrqus3_speed; /* 0 if link is down, */ + /* otherwise speed in Mbps */ + } nrqu_nrqus3; + + /* cmd = NICE_CMD_BLINK_LED */ + struct + { + unsigned int nrqus4_blink_time; /* blink duration in seconds */ + } nrqu_nrqus4; + + } nrq_nrqu; +}; + +#define nrq_rx nrq_nrqu.nrqu_nrqus1.nrqus1_rx +#define nrq_ctx nrq_nrqu.nrqu_nrqus1.nrqus1_ctx +#define nrq_support_rx nrq_nrqu.nrqu_nrqus2.nrqus2_support_rx +#define nrq_magic nrq_nrqu.nrqu_nrqus2.nrqus2_magic +#define nrq_support_vlan nrq_nrqu.nrqu_nrqus2.nrqus2_support_vlan +#define nrq_support_get_speed nrq_nrqu.nrqu_nrqus2.nrqus2_support_get_speed +#define nrq_speed nrq_nrqu.nrqu_nrqus3.nrqus3_speed +#define nrq_blink_time nrq_nrqu.nrqu_nrqus4.nrqus4_blink_time + +/* + * magic constants + */ +#define NICE_REQUESTOR_MAGIC 0x4543494E /* NICE in ascii */ +#define NICE_DEVICE_MAGIC 0x4E494345 /* ECIN in ascii */ + +/* + * command field + */ +#define NICE_CMD_QUERY_SUPPORT 0x00000001 +#define NICE_CMD_SET_RX 0x00000002 +#define NICE_CMD_GET_RX 0x00000003 +#define NICE_CMD_GET_SPEED 0x00000004 +#define NICE_CMD_BLINK_LED 0x00000005 + +#endif /* _nicext_h_ */ diff --git a/roms/u-boot-sam460ex/drivers/net/ns7520_eth.c b/roms/u-boot-sam460ex/drivers/net/ns7520_eth.c new file mode 100644 index 000000000..c28726e69 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ns7520_eth.c @@ -0,0 +1,850 @@ +/*********************************************************************** + * + * Copyright (C) 2005 by Videon Central, Inc. + * + * $Id$ + * @Author: Arthur Shipkowski + * @Descr: Ethernet driver for the NS7520. Uses polled Ethernet, like + * the older netarmeth driver. Note that attempting to filter + * broadcast and multicast out in the SAFR register will cause + * bad things due to released errata. + * @References: [1] NS7520 Hardware Reference, December 2003 + * [2] Intel LXT971 Datasheet #249414 Rev. 02 + * + ***********************************************************************/ + +#include <common.h> + +#include <net.h> /* NetSendPacket */ +#include <asm/arch/netarm_registers.h> +#include <asm/arch/netarm_dma_module.h> + +#include "ns7520_eth.h" /* for Ethernet and PHY */ + +/** + * Send an error message to the terminal. + */ +#define ERROR(x) \ +do { \ + char *__foo = strrchr(__FILE__, '/'); \ + \ + printf("%s: %d: %s(): ", (__foo == NULL ? __FILE__ : (__foo + 1)), \ + __LINE__, __FUNCTION__); \ + printf x; printf("\n"); \ +} while (0); + +/* some definition to make transistion to linux easier */ + +#define NS7520_DRIVER_NAME "eth" +#define KERN_WARNING "Warning:" +#define KERN_ERR "Error:" +#define KERN_INFO "Info:" + +#if 1 +# define DEBUG +#endif + +#ifdef DEBUG +# define printk printf + +# define DEBUG_INIT 0x0001 +# define DEBUG_MINOR 0x0002 +# define DEBUG_RX 0x0004 +# define DEBUG_TX 0x0008 +# define DEBUG_INT 0x0010 +# define DEBUG_POLL 0x0020 +# define DEBUG_LINK 0x0040 +# define DEBUG_MII 0x0100 +# define DEBUG_MII_LOW 0x0200 +# define DEBUG_MEM 0x0400 +# define DEBUG_ERROR 0x4000 +# define DEBUG_ERROR_CRIT 0x8000 + +static int nDebugLvl = DEBUG_ERROR_CRIT; + +# define DEBUG_ARGS0( FLG, a0 ) if( ( nDebugLvl & (FLG) ) == (FLG) ) \ + printf("%s: " a0, __FUNCTION__, 0, 0, 0, 0, 0, 0 ) +# define DEBUG_ARGS1( FLG, a0, a1 ) if( ( nDebugLvl & (FLG) ) == (FLG)) \ + printf("%s: " a0, __FUNCTION__, (int)(a1), 0, 0, 0, 0, 0 ) +# define DEBUG_ARGS2( FLG, a0, a1, a2 ) if( (nDebugLvl & (FLG)) ==(FLG))\ + printf("%s: " a0, __FUNCTION__, (int)(a1), (int)(a2), 0, 0,0,0 ) +# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) if((nDebugLvl &(FLG))==(FLG))\ + printf("%s: "a0,__FUNCTION__,(int)(a1),(int)(a2),(int)(a3),0,0,0) +# define DEBUG_FN( FLG ) if( (nDebugLvl & (FLG)) == (FLG) ) \ + printf("\r%s:line %d\n", (int)__FUNCTION__, __LINE__, 0,0,0,0); +# define ASSERT( expr, func ) if( !( expr ) ) { \ + printf( "Assertion failed! %s:line %d %s\n", \ + (int)__FUNCTION__,__LINE__,(int)(#expr),0,0,0); \ + func } +#else /* DEBUG */ +# define printk(...) +# define DEBUG_ARGS0( FLG, a0 ) +# define DEBUG_ARGS1( FLG, a0, a1 ) +# define DEBUG_ARGS2( FLG, a0, a1, a2 ) +# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) +# define DEBUG_FN( n ) +# define ASSERT(expr, func) +#endif /* DEBUG */ + +#define NS7520_MII_NEG_DELAY (5*CONFIG_SYS_HZ) /* in s */ +#define TX_TIMEOUT (5*CONFIG_SYS_HZ) /* in s */ +#define RX_STALL_WORKAROUND_CNT 100 + +static int ns7520_eth_reset(void); + +static void ns7520_link_auto_negotiate(void); +static void ns7520_link_update_egcr(void); +static void ns7520_link_print_changed(void); + +/* the PHY stuff */ + +static char ns7520_mii_identify_phy(void); +static unsigned short ns7520_mii_read(unsigned short uiRegister); +static void ns7520_mii_write(unsigned short uiRegister, + unsigned short uiData); +static unsigned int ns7520_mii_get_clock_divisor(unsigned int + unMaxMDIOClk); +static unsigned int ns7520_mii_poll_busy(void); + +static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; +static unsigned int uiLastLinkStatus; +static PhyType phyDetected = PHY_NONE; + +/*********************************************************************** + * @Function: eth_init + * @Return: -1 on failure otherwise 0 + * @Descr: Initializes the ethernet engine and uses either FS Forth's default + * MAC addr or the one in environment + ***********************************************************************/ + +int eth_init(bd_t * pbis) +{ + unsigned char aucMACAddr[6]; + char *pcTmp = getenv("ethaddr"); + char *pcEnd; + int i; + + DEBUG_FN(DEBUG_INIT); + + /* no need to check for hardware */ + + if (!ns7520_eth_reset()) + return -1; + + if (NULL == pcTmp) + return -1; + + for (i = 0; i < 6; i++) { + aucMACAddr[i] = + pcTmp ? simple_strtoul(pcTmp, &pcEnd, 16) : 0; + pcTmp = (*pcTmp) ? pcEnd + 1 : pcEnd; + } + + /* configure ethernet address */ + + *get_eth_reg_addr(NS7520_ETH_SA1) = + aucMACAddr[5] << 8 | aucMACAddr[4]; + *get_eth_reg_addr(NS7520_ETH_SA2) = + aucMACAddr[3] << 8 | aucMACAddr[2]; + *get_eth_reg_addr(NS7520_ETH_SA3) = + aucMACAddr[1] << 8 | aucMACAddr[0]; + + /* enable hardware */ + + *get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN; + *get_eth_reg_addr(NS7520_ETH_SUPP) = NS7520_ETH_SUPP_JABBER; + *get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN; + + /* the linux kernel may give packets < 60 bytes, for example arp */ + *get_eth_reg_addr(NS7520_ETH_MAC2) = NS7520_ETH_MAC2_CRCEN | + NS7520_ETH_MAC2_PADEN | NS7520_ETH_MAC2_HUGE; + + /* Broadcast/multicast allowed; if you don't set this even unicast chokes */ + /* Based on NS7520 errata documentation */ + *get_eth_reg_addr(NS7520_ETH_SAFR) = + NS7520_ETH_SAFR_BROAD | NS7520_ETH_SAFR_PRM; + + /* enable receive and transmit FIFO, use 10/100 Mbps MII */ + *get_eth_reg_addr(NS7520_ETH_EGCR) |= + NS7520_ETH_EGCR_ETXWM_75 | + NS7520_ETH_EGCR_ERX | + NS7520_ETH_EGCR_ERXREG | + NS7520_ETH_EGCR_ERXBR | NS7520_ETH_EGCR_ETX; + + return 0; +} + +/*********************************************************************** + * @Function: eth_send + * @Return: -1 on timeout otherwise 1 + * @Descr: sends one frame by DMA + ***********************************************************************/ + +int eth_send(volatile void *pPacket, int nLen) +{ + int i, length32, retval = 1; + char *pa; + unsigned int *pa32, lastp = 0, rest; + unsigned int status; + + pa = (char *) pPacket; + pa32 = (unsigned int *) pPacket; + length32 = nLen / 4; + rest = nLen % 4; + + /* make sure there's no garbage in the last word */ + switch (rest) { + case 0: + lastp = pa32[length32 - 1]; + length32--; + break; + case 1: + lastp = pa32[length32] & 0x000000ff; + break; + case 2: + lastp = pa32[length32] & 0x0000ffff; + break; + case 3: + lastp = pa32[length32] & 0x00ffffff; + break; + } + + while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) & + NS7520_ETH_EGSR_TXREGE) + == 0) { + } + + /* write to the fifo */ + for (i = 0; i < length32; i++) + *get_eth_reg_addr(NS7520_ETH_FIFO) = pa32[i]; + + /* the last word is written to an extra register, this + starts the transmission */ + *get_eth_reg_addr(NS7520_ETH_FIFOL) = lastp; + + /* Wait for it to be done */ + while ((*get_eth_reg_addr(NS7520_ETH_EGSR) & NS7520_ETH_EGSR_TXBC) + == 0) { + } + status = (*get_eth_reg_addr(NS7520_ETH_ETSR)); + *get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_TXBC; /* Clear it now */ + + if (status & NS7520_ETH_ETSR_TXOK) { + retval = 0; /* We're OK! */ + } else if (status & NS7520_ETH_ETSR_TXDEF) { + printf("Deferred, we'll see.\n"); + retval = 0; + } else if (status & NS7520_ETH_ETSR_TXAL) { + printf("Late collision error, %d collisions.\n", + (*get_eth_reg_addr(NS7520_ETH_ETSR)) & + NS7520_ETH_ETSR_TXCOLC); + } else if (status & NS7520_ETH_ETSR_TXAEC) { + printf("Excessive collisions: %d\n", + (*get_eth_reg_addr(NS7520_ETH_ETSR)) & + NS7520_ETH_ETSR_TXCOLC); + } else if (status & NS7520_ETH_ETSR_TXAED) { + printf("Excessive deferral on xmit.\n"); + } else if (status & NS7520_ETH_ETSR_TXAUR) { + printf("Packet underrun.\n"); + } else if (status & NS7520_ETH_ETSR_TXAJ) { + printf("Jumbo packet error.\n"); + } else { + printf("Error: Should never get here.\n"); + } + + return (retval); +} + +/*********************************************************************** + * @Function: eth_rx + * @Return: size of last frame in bytes or 0 if no frame available + * @Descr: gives one frame to U-Boot which has been copied by DMA engine already + * to NetRxPackets[ 0 ]. + ***********************************************************************/ + +int eth_rx(void) +{ + int i; + unsigned short rxlen; + unsigned short totrxlen = 0; + unsigned int *addr; + unsigned int rxstatus, lastrxlen; + char *pa; + + /* If RXBR is 1, data block was received */ + while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) & + NS7520_ETH_EGSR_RXBR) == NS7520_ETH_EGSR_RXBR) { + + /* get status register and the length of received block */ + rxstatus = *get_eth_reg_addr(NS7520_ETH_ERSR); + rxlen = (rxstatus & NS7520_ETH_ERSR_RXSIZE) >> 16; + + /* clear RXBR to make fifo available */ + *get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_RXBR; + + if (rxstatus & NS7520_ETH_ERSR_ROVER) { + printf("Receive overrun, resetting FIFO.\n"); + *get_eth_reg_addr(NS7520_ETH_EGCR) &= + ~NS7520_ETH_EGCR_ERX; + udelay(20); + *get_eth_reg_addr(NS7520_ETH_EGCR) |= + NS7520_ETH_EGCR_ERX; + } + if (rxlen == 0) { + printf("Nothing.\n"); + return 0; + } + + addr = (unsigned int *) NetRxPackets[0]; + pa = (char *) NetRxPackets[0]; + + /* read the fifo */ + for (i = 0; i < rxlen / 4; i++) { + *addr = *get_eth_reg_addr(NS7520_ETH_FIFO); + addr++; + } + + if ((*get_eth_reg_addr(NS7520_ETH_EGSR)) & + NS7520_ETH_EGSR_RXREGR) { + /* RXFDB indicates wether the last word is 1,2,3 or 4 bytes long */ + lastrxlen = + ((*get_eth_reg_addr(NS7520_ETH_EGSR)) & + NS7520_ETH_EGSR_RXFDB_MA) >> 28; + *addr = *get_eth_reg_addr(NS7520_ETH_FIFO); + switch (lastrxlen) { + case 1: + *addr &= 0xff000000; + break; + case 2: + *addr &= 0xffff0000; + break; + case 3: + *addr &= 0xffffff00; + break; + } + } + + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[0], rxlen - 4); + totrxlen += rxlen - 4; + } + + return totrxlen; +} + +/*********************************************************************** + * @Function: eth_halt + * @Return: n/a + * @Descr: stops the ethernet engine + ***********************************************************************/ + +void eth_halt(void) +{ + DEBUG_FN(DEBUG_INIT); + + *get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_RXEN; + *get_eth_reg_addr(NS7520_ETH_EGCR) &= ~(NS7520_ETH_EGCR_ERX | + NS7520_ETH_EGCR_ERXDMA | + NS7520_ETH_EGCR_ERXREG | + NS7520_ETH_EGCR_ERXBR | + NS7520_ETH_EGCR_ETX | + NS7520_ETH_EGCR_ETXDMA); +} + +/*********************************************************************** + * @Function: ns7520_eth_reset + * @Return: 0 on failure otherwise 1 + * @Descr: resets the ethernet interface and the PHY, + * performs auto negotiation or fixed modes + ***********************************************************************/ + +static int ns7520_eth_reset(void) +{ + DEBUG_FN(DEBUG_MINOR); + + /* Reset important registers */ + *get_eth_reg_addr(NS7520_ETH_EGCR) = 0; /* Null it out! */ + *get_eth_reg_addr(NS7520_ETH_MAC1) &= NS7520_ETH_MAC1_SRST; + *get_eth_reg_addr(NS7520_ETH_MAC2) = 0; + /* Reset MAC */ + *get_eth_reg_addr(NS7520_ETH_EGCR) |= NS7520_ETH_EGCR_MAC_RES; + udelay(5); + *get_eth_reg_addr(NS7520_ETH_EGCR) &= ~NS7520_ETH_EGCR_MAC_RES; + + /* reset and initialize PHY */ + + *get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_SRST; + + /* we don't support hot plugging of PHY, therefore we don't reset + phyDetected and nPhyMaxMdioClock here. The risk is if the setting is + incorrect the first open + may detect the PHY correctly but succeding will fail + For reseting the PHY and identifying we have to use the standard + MDIO CLOCK value 2.5 MHz only after hardware reset + After having identified the PHY we will do faster */ + + *get_eth_reg_addr(NS7520_ETH_MCFG) = + ns7520_mii_get_clock_divisor(nPhyMaxMdioClock); + + /* reset PHY */ + ns7520_mii_write(PHY_BMCR, PHY_BMCR_RESET); + ns7520_mii_write(PHY_BMCR, 0); + + udelay(3000); /* [2] p.70 says at least 300us reset recovery time. */ + + /* MII clock has been setup to default, ns7520_mii_identify_phy should + work for all */ + + if (!ns7520_mii_identify_phy()) { + printk(KERN_ERR NS7520_DRIVER_NAME + ": Unsupported PHY, aborting\n"); + return 0; + } + + /* now take the highest MDIO clock possible after detection */ + *get_eth_reg_addr(NS7520_ETH_MCFG) = + ns7520_mii_get_clock_divisor(nPhyMaxMdioClock); + + /* PHY has been detected, so there can be no abort reason and we can + finish initializing ethernet */ + + uiLastLinkStatus = 0xff; /* undefined */ + + ns7520_link_auto_negotiate(); + + if (phyDetected == PHY_LXT971A) + /* set LED2 to link mode */ + ns7520_mii_write(PHY_LXT971_LED_CFG, + (PHY_LXT971_LED_CFG_LINK_ACT << + PHY_LXT971_LED_CFG_SHIFT_LED2) | + (PHY_LXT971_LED_CFG_TRANSMIT << + PHY_LXT971_LED_CFG_SHIFT_LED1)); + + return 1; +} + +/*********************************************************************** + * @Function: ns7520_link_auto_negotiate + * @Return: void + * @Descr: performs auto-negotation of link. + ***********************************************************************/ + +static void ns7520_link_auto_negotiate(void) +{ + unsigned long ulStartJiffies; + unsigned short uiStatus; + + DEBUG_FN(DEBUG_LINK); + + /* run auto-negotation */ + /* define what we are capable of */ + ns7520_mii_write(PHY_ANAR, + PHY_ANLPAR_TXFD | + PHY_ANLPAR_TX | + PHY_ANLPAR_10FD | + PHY_ANLPAR_10 | + PHY_ANLPAR_PSB_802_3); + /* start auto-negotiation */ + ns7520_mii_write(PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); + + /* wait for completion */ + + ulStartJiffies = get_timer(0); + while (get_timer(0) < ulStartJiffies + NS7520_MII_NEG_DELAY) { + uiStatus = ns7520_mii_read(PHY_BMSR); + if ((uiStatus & + (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) == + (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) { + /* lucky we are, auto-negotiation succeeded */ + ns7520_link_print_changed(); + ns7520_link_update_egcr(); + return; + } + } + + DEBUG_ARGS0(DEBUG_LINK, "auto-negotiation timed out\n"); + /* ignore invalid link settings */ +} + +/*********************************************************************** + * @Function: ns7520_link_update_egcr + * @Return: void + * @Descr: updates the EGCR and MAC2 link status after mode change or + * auto-negotation + ***********************************************************************/ + +static void ns7520_link_update_egcr(void) +{ + unsigned int unEGCR; + unsigned int unMAC2; + unsigned int unIPGT; + + DEBUG_FN(DEBUG_LINK); + + unEGCR = *get_eth_reg_addr(NS7520_ETH_EGCR); + unMAC2 = *get_eth_reg_addr(NS7520_ETH_MAC2); + unIPGT = + *get_eth_reg_addr(NS7520_ETH_IPGT) & ~NS7520_ETH_IPGT_IPGT; + + unEGCR &= ~NS7520_ETH_EGCR_EFULLD; + unMAC2 &= ~NS7520_ETH_MAC2_FULLD; + if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE) + == PHY_LXT971_STAT2_DUPLEX_MODE) { + unEGCR |= NS7520_ETH_EGCR_EFULLD; + unMAC2 |= NS7520_ETH_MAC2_FULLD; + unIPGT |= 0x15; /* see [1] p. 167 */ + } else + unIPGT |= 0x12; /* see [1] p. 167 */ + + *get_eth_reg_addr(NS7520_ETH_MAC2) = unMAC2; + *get_eth_reg_addr(NS7520_ETH_EGCR) = unEGCR; + *get_eth_reg_addr(NS7520_ETH_IPGT) = unIPGT; +} + +/*********************************************************************** + * @Function: ns7520_link_print_changed + * @Return: void + * @Descr: checks whether the link status has changed and if so prints + * the new mode + ***********************************************************************/ + +static void ns7520_link_print_changed(void) +{ + unsigned short uiStatus; + unsigned short uiControl; + + DEBUG_FN(DEBUG_LINK); + + uiControl = ns7520_mii_read(PHY_BMCR); + + if ((uiControl & PHY_BMCR_AUTON) == PHY_BMCR_AUTON) { + /* PHY_BMSR_LS is only set on autonegotiation */ + uiStatus = ns7520_mii_read(PHY_BMSR); + + if (!(uiStatus & PHY_BMSR_LS)) { + printk(KERN_WARNING NS7520_DRIVER_NAME + ": link down\n"); + /* @TODO Linux: carrier_off */ + } else { + /* @TODO Linux: carrier_on */ + if (phyDetected == PHY_LXT971A) { + uiStatus = + ns7520_mii_read(PHY_LXT971_STAT2); + uiStatus &= + (PHY_LXT971_STAT2_100BTX | + PHY_LXT971_STAT2_DUPLEX_MODE | + PHY_LXT971_STAT2_AUTO_NEG); + + /* mask out all uninteresting parts */ + } + /* other PHYs must store there link information in + uiStatus as PHY_LXT971 */ + } + } else { + /* mode has been forced, so uiStatus should be the same as the + last link status, enforce printing */ + uiStatus = uiLastLinkStatus; + uiLastLinkStatus = 0xff; + } + + if (uiStatus != uiLastLinkStatus) { + /* save current link status */ + uiLastLinkStatus = uiStatus; + + /* print new link status */ + + printk(KERN_INFO NS7520_DRIVER_NAME + ": link mode %i Mbps %s duplex %s\n", + (uiStatus & PHY_LXT971_STAT2_100BTX) ? 100 : 10, + (uiStatus & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" : + "half", + (uiStatus & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" : + ""); + } +} + +/*********************************************************************** + * the MII low level stuff + ***********************************************************************/ + +/*********************************************************************** + * @Function: ns7520_mii_identify_phy + * @Return: 1 if supported PHY has been detected otherwise 0 + * @Descr: checks for supported PHY and prints the IDs. + ***********************************************************************/ + +static char ns7520_mii_identify_phy(void) +{ + unsigned short uiID1; + unsigned short uiID2; + unsigned char *szName; + char cRes = 0; + + DEBUG_FN(DEBUG_MII); + + phyDetected = (PhyType) uiID1 = ns7520_mii_read(PHY_PHYIDR1); + + switch (phyDetected) { + case PHY_LXT971A: + szName = "LXT971A"; + uiID2 = ns7520_mii_read(PHY_PHYIDR2); + nPhyMaxMdioClock = PHY_LXT971_MDIO_MAX_CLK; + cRes = 1; + break; + case PHY_NONE: + default: + /* in case uiID1 == 0 && uiID2 == 0 we may have the wrong + address or reset sets the wrong NS7520_ETH_MCFG_CLKS */ + + uiID2 = 0; + szName = "unknown"; + nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; + phyDetected = PHY_NONE; + } + + printk(KERN_INFO NS7520_DRIVER_NAME + ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName); + + return cRes; +} + +/*********************************************************************** + * @Function: ns7520_mii_read + * @Return: the data read from PHY register uiRegister + * @Descr: the data read may be invalid if timed out. If so, a message + * is printed but the invalid data is returned. + * The fixed device address is being used. + ***********************************************************************/ + +static unsigned short ns7520_mii_read(unsigned short uiRegister) +{ + DEBUG_FN(DEBUG_MII_LOW); + + /* write MII register to be read */ + *get_eth_reg_addr(NS7520_ETH_MADR) = + CONFIG_PHY_ADDR << 8 | uiRegister; + + *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ; + + if (!ns7520_mii_poll_busy()) + printk(KERN_WARNING NS7520_DRIVER_NAME + ": MII still busy in read\n"); + /* continue to read */ + + *get_eth_reg_addr(NS7520_ETH_MCMD) = 0; + + return (unsigned short) (*get_eth_reg_addr(NS7520_ETH_MRDD)); +} + +/*********************************************************************** + * @Function: ns7520_mii_write + * @Return: nothing + * @Descr: writes the data to the PHY register. In case of a timeout, + * no special handling is performed but a message printed + * The fixed device address is being used. + ***********************************************************************/ + +static void ns7520_mii_write(unsigned short uiRegister, + unsigned short uiData) +{ + DEBUG_FN(DEBUG_MII_LOW); + + /* write MII register to be written */ + *get_eth_reg_addr(NS7520_ETH_MADR) = + CONFIG_PHY_ADDR << 8 | uiRegister; + + *get_eth_reg_addr(NS7520_ETH_MWTD) = uiData; + + if (!ns7520_mii_poll_busy()) { + printf(KERN_WARNING NS7520_DRIVER_NAME + ": MII still busy in write\n"); + } +} + +/*********************************************************************** + * @Function: ns7520_mii_get_clock_divisor + * @Return: the clock divisor that should be used in NS7520_ETH_MCFG_CLKS + * @Descr: if no clock divisor can be calculated for the + * current SYSCLK and the maximum MDIO Clock, a warning is printed + * and the greatest divisor is taken + ***********************************************************************/ + +static unsigned int ns7520_mii_get_clock_divisor(unsigned int unMaxMDIOClk) +{ + struct { + unsigned int unSysClkDivisor; + unsigned int unClks; /* field for NS7520_ETH_MCFG_CLKS */ + } PHYClockDivisors[] = { + { + 4, NS7520_ETH_MCFG_CLKS_4}, { + 6, NS7520_ETH_MCFG_CLKS_6}, { + 8, NS7520_ETH_MCFG_CLKS_8}, { + 10, NS7520_ETH_MCFG_CLKS_10}, { + 14, NS7520_ETH_MCFG_CLKS_14}, { + 20, NS7520_ETH_MCFG_CLKS_20}, { + 28, NS7520_ETH_MCFG_CLKS_28} + }; + + int nIndexSysClkDiv; + int nArraySize = + sizeof(PHYClockDivisors) / sizeof(PHYClockDivisors[0]); + unsigned int unClks = NS7520_ETH_MCFG_CLKS_28; /* defaults to + greatest div */ + + DEBUG_FN(DEBUG_INIT); + + for (nIndexSysClkDiv = 0; nIndexSysClkDiv < nArraySize; + nIndexSysClkDiv++) { + /* find first sysclock divisor that isn't higher than 2.5 MHz + clock */ + if (NETARM_XTAL_FREQ / + PHYClockDivisors[nIndexSysClkDiv].unSysClkDivisor <= + unMaxMDIOClk) { + unClks = PHYClockDivisors[nIndexSysClkDiv].unClks; + break; + } + } + + DEBUG_ARGS2(DEBUG_INIT, + "Taking MDIO Clock bit mask 0x%0x for max clock %i\n", + unClks, unMaxMDIOClk); + + /* return greatest divisor */ + return unClks; +} + +/*********************************************************************** + * @Function: ns7520_mii_poll_busy + * @Return: 0 if timed out otherwise the remaing timeout + * @Descr: waits until the MII has completed a command or it times out + * code may be interrupted by hard interrupts. + * It is not checked what happens on multiple actions when + * the first is still being busy and we timeout. + ***********************************************************************/ + +static unsigned int ns7520_mii_poll_busy(void) +{ + unsigned int unTimeout = 1000; + + DEBUG_FN(DEBUG_MII_LOW); + + while (((*get_eth_reg_addr(NS7520_ETH_MIND) & NS7520_ETH_MIND_BUSY) + == NS7520_ETH_MIND_BUSY) && unTimeout) + unTimeout--; + + return unTimeout; +} + +/* ---------------------------------------------------------------------------- + * Net+ARM ethernet MII functionality. + */ +#if defined(CONFIG_MII) + +/** + * Maximum MII address we support + */ +#define MII_ADDRESS_MAX (31) + +/** + * Maximum MII register address we support + */ +#define MII_REGISTER_MAX (31) + +/** + * Ethernet MII interface return values for public functions. + */ +enum mii_status { + MII_STATUS_SUCCESS = 0, + MII_STATUS_FAILURE = 1, +}; + +/** + * Read a 16-bit value from an MII register. + */ +extern int ns7520_miiphy_read(char *devname, unsigned char const addr, + unsigned char const reg, unsigned short *const value) +{ + int ret = MII_STATUS_FAILURE; + + /* Parameter checks */ + if (addr > MII_ADDRESS_MAX) { + ERROR(("invalid addr, 0x%02X", addr)); + goto miiphy_read_failed_0; + } + + if (reg > MII_REGISTER_MAX) { + ERROR(("invalid reg, 0x%02X", reg)); + goto miiphy_read_failed_0; + } + + if (value == NULL) { + ERROR(("NULL value")); + goto miiphy_read_failed_0; + } + + DEBUG_FN(DEBUG_MII_LOW); + + /* write MII register to be read */ + *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg; + + *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ; + + if (!ns7520_mii_poll_busy()) + printk(KERN_WARNING NS7520_DRIVER_NAME + ": MII still busy in read\n"); + /* continue to read */ + + *get_eth_reg_addr(NS7520_ETH_MCMD) = 0; + + *value = (*get_eth_reg_addr(NS7520_ETH_MRDD)); + ret = MII_STATUS_SUCCESS; + /* Fall through */ + + miiphy_read_failed_0: + return (ret); +} + +/** + * Write a 16-bit value to an MII register. + */ +extern int ns7520_miiphy_write(char *devname, unsigned char const addr, + unsigned char const reg, unsigned short const value) +{ + int ret = MII_STATUS_FAILURE; + + /* Parameter checks */ + if (addr > MII_ADDRESS_MAX) { + ERROR(("invalid addr, 0x%02X", addr)); + goto miiphy_write_failed_0; + } + + if (reg > MII_REGISTER_MAX) { + ERROR(("invalid reg, 0x%02X", reg)); + goto miiphy_write_failed_0; + } + + /* write MII register to be written */ + *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg; + + *get_eth_reg_addr(NS7520_ETH_MWTD) = value; + + if (!ns7520_mii_poll_busy()) { + printf(KERN_WARNING NS7520_DRIVER_NAME + ": MII still busy in write\n"); + } + + ret = MII_STATUS_SUCCESS; + /* Fall through */ + + miiphy_write_failed_0: + return (ret); +} +#endif /* defined(CONFIG_MII) */ + +int ns7520_miiphy_initialize(bd_t *bis) +{ +#if defined(CONFIG_MII) + miiphy_register("ns7520phy", ns7520_miiphy_read, ns7520_miiphy_write); +#endif + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/ns8382x.c b/roms/u-boot-sam460ex/drivers/net/ns8382x.c new file mode 100644 index 000000000..198f73dee --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ns8382x.c @@ -0,0 +1,859 @@ +/* + ns8382x.c: A U-Boot driver for the NatSemi DP8382[01]. + ported by: Mark A. Rakes (mark_rakes@vivato.net) + + Adapted from: + 1. an Etherboot driver for DP8381[56] written by: + Copyright (C) 2001 Entity Cyber, Inc. + + This development of this Etherboot driver was funded by + Sicom Systems: http://www.sicompos.com/ + + Author: Marty Connor (mdc@thinguin.org) + Adapted from a Linux driver which was written by Donald Becker + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + 2. A Linux driver by Donald Becker, ns820.c: + Written/copyright 1999-2002 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. License for under other terms may be + available. Contact the original author for details. + + The original author may be reached as becker@scyld.com, or at + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support information and updates available at + http://www.scyld.com/network/netsemi.html + + Datasheets available from: + http://www.national.com/pf/DP/DP83820.html + http://www.national.com/pf/DP/DP83821.html +*/ + +/* Revision History + * October 2002 mar 1.0 + * Initial U-Boot Release. + * Tested with Netgear GA622T (83820) + * and SMC9452TX (83821) + * NOTE: custom boards with these chips may (likely) require + * a programmed EEPROM device (if present) in order to work + * correctly. +*/ + +/* Includes */ +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> + +/* defines */ +#define DSIZE 0x00000FFF +#define ETH_ALEN 6 +#define CRC_SIZE 4 +#define TOUT_LOOP 500000 +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ + +enum register_offsets { + ChipCmd = 0x00, + ChipConfig = 0x04, + EECtrl = 0x08, + IntrMask = 0x14, + IntrEnable = 0x18, + TxRingPtr = 0x20, + TxRingPtrHi = 0x24, + TxConfig = 0x28, + RxRingPtr = 0x30, + RxRingPtrHi = 0x34, + RxConfig = 0x38, + PriQueue = 0x3C, + RxFilterAddr = 0x48, + RxFilterData = 0x4C, + ClkRun = 0xCC, + PCIPM = 0x44, +}; + +enum ChipCmdBits { + ChipReset = 0x100, + RxReset = 0x20, + TxReset = 0x10, + RxOff = 0x08, + RxOn = 0x04, + TxOff = 0x02, + TxOn = 0x01 +}; + +enum ChipConfigBits { + LinkSts = 0x80000000, + GigSpeed = 0x40000000, + HundSpeed = 0x20000000, + FullDuplex = 0x10000000, + TBIEn = 0x01000000, + Mode1000 = 0x00400000, + T64En = 0x00004000, + D64En = 0x00001000, + M64En = 0x00000800, + PhyRst = 0x00000400, + PhyDis = 0x00000200, + ExtStEn = 0x00000100, + BEMode = 0x00000001, +}; +#define SpeedStatus_Polarity ( GigSpeed | HundSpeed | FullDuplex) + +enum TxConfig_bits { + TxDrthMask = 0x000000ff, + TxFlthMask = 0x0000ff00, + TxMxdmaMask = 0x00700000, + TxMxdma_8 = 0x00100000, + TxMxdma_16 = 0x00200000, + TxMxdma_32 = 0x00300000, + TxMxdma_64 = 0x00400000, + TxMxdma_128 = 0x00500000, + TxMxdma_256 = 0x00600000, + TxMxdma_512 = 0x00700000, + TxMxdma_1024 = 0x00000000, + TxCollRetry = 0x00800000, + TxAutoPad = 0x10000000, + TxMacLoop = 0x20000000, + TxHeartIgn = 0x40000000, + TxCarrierIgn = 0x80000000 +}; + +enum RxConfig_bits { + RxDrthMask = 0x0000003e, + RxMxdmaMask = 0x00700000, + RxMxdma_8 = 0x00100000, + RxMxdma_16 = 0x00200000, + RxMxdma_32 = 0x00300000, + RxMxdma_64 = 0x00400000, + RxMxdma_128 = 0x00500000, + RxMxdma_256 = 0x00600000, + RxMxdma_512 = 0x00700000, + RxMxdma_1024 = 0x00000000, + RxAcceptLenErr = 0x04000000, + RxAcceptLong = 0x08000000, + RxAcceptTx = 0x10000000, + RxStripCRC = 0x20000000, + RxAcceptRunt = 0x40000000, + RxAcceptErr = 0x80000000, +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { + RxFilterEnable = 0x80000000, + AcceptAllBroadcast = 0x40000000, + AcceptAllMulticast = 0x20000000, + AcceptAllUnicast = 0x10000000, + AcceptPerfectMatch = 0x08000000, +}; + +typedef struct _BufferDesc { + u32 link; + u32 bufptr; + vu_long cmdsts; + u32 extsts; /*not used here */ +} BufferDesc; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn = 0x80000000, DescMore = 0x40000000, DescIntr = 0x20000000, + DescNoCRC = 0x10000000, DescPktOK = 0x08000000, + DescSizeMask = 0xfff, + + DescTxAbort = 0x04000000, DescTxFIFO = 0x02000000, + DescTxCarrier = 0x01000000, DescTxDefer = 0x00800000, + DescTxExcDefer = 0x00400000, DescTxOOWCol = 0x00200000, + DescTxExcColl = 0x00100000, DescTxCollCount = 0x000f0000, + + DescRxAbort = 0x04000000, DescRxOver = 0x02000000, + DescRxDest = 0x01800000, DescRxLong = 0x00400000, + DescRxRunt = 0x00200000, DescRxInvalid = 0x00100000, + DescRxCRC = 0x00080000, DescRxAlign = 0x00040000, + DescRxLoop = 0x00020000, DesRxColl = 0x00010000, +}; + +/* Bits in MEAR */ +enum mii_reg_bits { + MDIO_ShiftClk = 0x0040, + MDIO_EnbOutput = 0x0020, + MDIO_Data = 0x0010, +}; + +/* PHY Register offsets. */ +enum phy_reg_offsets { + BMCR = 0x00, + BMSR = 0x01, + PHYIDR1 = 0x02, + PHYIDR2 = 0x03, + ANAR = 0x04, + KTCR = 0x09, +}; + +/* basic mode control register bits */ +enum bmcr_bits { + Bmcr_Reset = 0x8000, + Bmcr_Loop = 0x4000, + Bmcr_Speed0 = 0x2000, + Bmcr_AutoNegEn = 0x1000, /*if set ignores Duplex, Speed[01] */ + Bmcr_RstAutoNeg = 0x0200, + Bmcr_Duplex = 0x0100, + Bmcr_Speed1 = 0x0040, + Bmcr_Force10H = 0x0000, + Bmcr_Force10F = 0x0100, + Bmcr_Force100H = 0x2000, + Bmcr_Force100F = 0x2100, + Bmcr_Force1000H = 0x0040, + Bmcr_Force1000F = 0x0140, +}; + +/* auto negotiation advertisement register */ +enum anar_bits { + anar_adv_100F = 0x0100, + anar_adv_100H = 0x0080, + anar_adv_10F = 0x0040, + anar_adv_10H = 0x0020, + anar_ieee_8023 = 0x0001, +}; + +/* 1K-base T control register */ +enum ktcr_bits { + ktcr_adv_1000H = 0x0100, + ktcr_adv_1000F = 0x0200, +}; + +/* Globals */ +static u32 SavedClkRun; +static unsigned int cur_rx; +static unsigned int rx_config; +static unsigned int tx_config; + +/* Note: transmit and receive buffers and descriptors must be + long long word aligned */ +static BufferDesc txd __attribute__ ((aligned(8))); +static BufferDesc rxd[NUM_RX_DESC] __attribute__ ((aligned(8))); +static unsigned char txb[TX_BUF_SIZE] __attribute__ ((aligned(8))); +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE] + __attribute__ ((aligned(8))); + +/* Function Prototypes */ +static int mdio_read(struct eth_device *dev, int phy_id, int addr); +static void mdio_write(struct eth_device *dev, int phy_id, int addr, int value); +static void mdio_sync(struct eth_device *dev, u32 offset); +static int ns8382x_init(struct eth_device *dev, bd_t * bis); +static void ns8382x_reset(struct eth_device *dev); +static void ns8382x_init_rxfilter(struct eth_device *dev); +static void ns8382x_init_txd(struct eth_device *dev); +static void ns8382x_init_rxd(struct eth_device *dev); +static void ns8382x_set_rx_mode(struct eth_device *dev); +static void ns8382x_check_duplex(struct eth_device *dev); +static int ns8382x_send(struct eth_device *dev, volatile void *packet, + int length); +static int ns8382x_poll(struct eth_device *dev); +static void ns8382x_disable(struct eth_device *dev); + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83820}, + {} +}; + +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) + +static inline int +INW(struct eth_device *dev, u_long addr) +{ + return le16_to_cpu(*(vu_short *) (addr + dev->iobase)); +} + +static int +INL(struct eth_device *dev, u_long addr) +{ + return le32_to_cpu(*(vu_long *) (addr + dev->iobase)); +} + +static inline void +OUTW(struct eth_device *dev, int command, u_long addr) +{ + *(vu_short *) ((addr + dev->iobase)) = cpu_to_le16(command); +} + +static inline void +OUTL(struct eth_device *dev, int command, u_long addr) +{ + *(vu_long *) ((addr + dev->iobase)) = cpu_to_le32(command); +} + +/* Function: ns8382x_initialize + * Description: Retrieves the MAC address of the card, and sets up some + * globals required by other routines, and initializes the NIC, making it + * ready to send and receive packets. + * Side effects: initializes ns8382xs, ready to recieve packets. + * Returns: int: number of cards found + */ + +int +ns8382x_initialize(bd_t * bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase, status; + int i, idx = 0; + u32 phyAddress; + u32 tmp; + u32 chip_config; + + while (1) { /* Find PCI device(s) */ + if ((devno = pci_find_devices(supported, idx++)) < 0) + break; + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + iobase &= ~0x3; /* 1: unused and 0:I/O Space Indicator */ + +#ifdef NS8382X_DEBUG + printf("ns8382x: NatSemi dp8382x @ 0x%x\n", iobase); +#endif + + pci_write_config_dword(devno, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + /* Check if I/O accesses and Bus Mastering are enabled. */ + pci_read_config_dword(devno, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_MEMORY)) { + printf("Error: Can not enable MEM access.\n"); + continue; + } else if (!(status & PCI_COMMAND_MASTER)) { + printf("Error: Can not enable Bus Mastering.\n"); + continue; + } + + dev = (struct eth_device *) malloc(sizeof *dev); + + sprintf(dev->name, "dp8382x#%d", card_number); + dev->iobase = bus_to_phys(iobase); + dev->priv = (void *) devno; + dev->init = ns8382x_init; + dev->halt = ns8382x_disable; + dev->send = ns8382x_send; + dev->recv = ns8382x_poll; + + /* ns8382x has a non-standard PM control register + * in PCI config space. Some boards apparently need + * to be brought to D0 in this manner. */ + pci_read_config_dword(devno, PCIPM, &tmp); + if (tmp & (0x03 | 0x100)) { /* D0 state, disable PME assertion */ + u32 newtmp = tmp & ~(0x03 | 0x100); + pci_write_config_dword(devno, PCIPM, newtmp); + } + + /* get MAC address */ + for (i = 0; i < 3; i++) { + u32 data; + char *mac = (char *)&dev->enetaddr[i * 2]; + + OUTL(dev, i * 2, RxFilterAddr); + data = INL(dev, RxFilterData); + *mac++ = data; + *mac++ = data >> 8; + } + /* get PHY address, can't be zero */ + for (phyAddress = 1; phyAddress < 32; phyAddress++) { + u32 rev, phy1; + + phy1 = mdio_read(dev, phyAddress, PHYIDR1); + if (phy1 == 0x2000) { /*check for 83861/91 */ + rev = mdio_read(dev, phyAddress, PHYIDR2); + if ((rev & ~(0x000f)) == 0x00005c50 || + (rev & ~(0x000f)) == 0x00005c60) { +#ifdef NS8382X_DEBUG + printf("phy rev is %x\n", rev); + printf("phy address is %x\n", + phyAddress); +#endif + break; + } + } + } + + /* set phy to autonegotiate && advertise everything */ + mdio_write(dev, phyAddress, KTCR, + (ktcr_adv_1000H | ktcr_adv_1000F)); + mdio_write(dev, phyAddress, ANAR, + (anar_adv_100F | anar_adv_100H | anar_adv_10H | + anar_adv_10F | anar_ieee_8023)); + mdio_write(dev, phyAddress, BMCR, 0x0); /*restore */ + mdio_write(dev, phyAddress, BMCR, + (Bmcr_AutoNegEn | Bmcr_RstAutoNeg)); + /* Reset the chip to erase any previous misconfiguration. */ + OUTL(dev, (ChipReset), ChipCmd); + + chip_config = INL(dev, ChipConfig); + /* reset the phy */ + OUTL(dev, (chip_config | PhyRst), ChipConfig); + /* power up and initialize transceiver */ + OUTL(dev, (chip_config & ~(PhyDis)), ChipConfig); + + mdio_sync(dev, EECtrl); +#ifdef NS8382X_DEBUG + { + u32 chpcfg = + INL(dev, ChipConfig) ^ SpeedStatus_Polarity; + + printf("%s: Transceiver 10%s %s duplex.\n", dev->name, + (chpcfg & GigSpeed) ? "00" : (chpcfg & HundSpeed) + ? "0" : "", + chpcfg & FullDuplex ? "full" : "half"); + printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, + dev->enetaddr[0], dev->enetaddr[1], + dev->enetaddr[2], dev->enetaddr[3], + dev->enetaddr[4], dev->enetaddr[5]); + } +#endif + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + SavedClkRun = INL(dev, ClkRun); + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + + eth_register(dev); + + card_number++; + + pci_write_config_byte(devno, PCI_LATENCY_TIMER, 0x60); + + udelay(10 * 1000); + } + return card_number; +} + +/* MII transceiver control section. + Read and write MII registers using software-generated serial MDIO + protocol. See the MII specifications or DP83840A data sheet for details. + + The maximum data clock rate is 2.5 MHz. To meet minimum timing we + must flush writes to the PCI bus with a PCI read. */ +#define mdio_delay(mdio_addr) INL(dev, mdio_addr) + +#define MDIO_EnbIn (0) +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void +mdio_sync(struct eth_device *dev, u32 offset) +{ + int bits = 32; + + /* Establish sync by sending at least 32 logic ones. */ + while (--bits >= 0) { + OUTL(dev, MDIO_WRITE1, offset); + mdio_delay(offset); + OUTL(dev, MDIO_WRITE1 | MDIO_ShiftClk, offset); + mdio_delay(offset); + } +} + +static int +mdio_read(struct eth_device *dev, int phy_id, int addr) +{ + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | addr; + int i, retval = 0; + + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + OUTL(dev, dataval, EECtrl); + mdio_delay(EECtrl); + OUTL(dev, dataval | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + OUTL(dev, MDIO_EnbIn, EECtrl); + mdio_delay(EECtrl); + retval = + (retval << 1) | ((INL(dev, EECtrl) & MDIO_Data) ? 1 : 0); + OUTL(dev, MDIO_EnbIn | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + return (retval >> 1) & 0xffff; +} + +static void +mdio_write(struct eth_device *dev, int phy_id, int addr, int value) +{ + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (addr << 18) | value; + int i; + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + OUTL(dev, dataval, EECtrl); + mdio_delay(EECtrl); + OUTL(dev, dataval | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + OUTL(dev, MDIO_EnbIn, EECtrl); + mdio_delay(EECtrl); + OUTL(dev, MDIO_EnbIn | MDIO_ShiftClk, EECtrl); + mdio_delay(EECtrl); + } + return; +} + +/* Function: ns8382x_init + * Description: resets the ethernet controller chip and configures + * registers and data structures required for sending and receiving packets. + * Arguments: struct eth_device *dev: NIC data structure + * returns: int. + */ + +static int +ns8382x_init(struct eth_device *dev, bd_t * bis) +{ + u32 config; + + ns8382x_reset(dev); + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. */ + OUTL(dev, SavedClkRun & ~0x100, ClkRun); + + ns8382x_init_rxfilter(dev); + ns8382x_init_txd(dev); + ns8382x_init_rxd(dev); + + /*set up ChipConfig */ + config = INL(dev, ChipConfig); + /*turn off 64 bit ops && Ten-bit interface + * && big-endian mode && extended status */ + config &= ~(TBIEn | Mode1000 | T64En | D64En | M64En | BEMode | PhyDis | ExtStEn); + OUTL(dev, config, ChipConfig); + + /* Configure the PCI bus bursts and FIFO thresholds. */ + tx_config = TxCarrierIgn | TxHeartIgn | TxAutoPad + | TxCollRetry | TxMxdma_1024 | (0x1002); + rx_config = RxMxdma_1024 | 0x20; +#ifdef NS8382X_DEBUG + printf("%s: Setting TxConfig Register %#08X\n", dev->name, tx_config); + printf("%s: Setting RxConfig Register %#08X\n", dev->name, rx_config); +#endif + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); + + /*turn off priority queueing */ + OUTL(dev, 0x0, PriQueue); + + ns8382x_check_duplex(dev); + ns8382x_set_rx_mode(dev); + + OUTL(dev, (RxOn | TxOn), ChipCmd); + return 1; +} + +/* Function: ns8382x_reset + * Description: soft resets the controller chip + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ +static void +ns8382x_reset(struct eth_device *dev) +{ + OUTL(dev, ChipReset, ChipCmd); + while (INL(dev, ChipCmd)) + /*wait until done */ ; + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); +} + +/* Function: ns8382x_init_rxfilter + * Description: sets receive filter address to our MAC address + * Arguments: struct eth_device *dev: NIC data structure + * returns: void. + */ + +static void +ns8382x_init_rxfilter(struct eth_device *dev) +{ + int i; + + for (i = 0; i < ETH_ALEN; i += 2) { + OUTL(dev, i, RxFilterAddr); + OUTW(dev, dev->enetaddr[i] + (dev->enetaddr[i + 1] << 8), + RxFilterData); + } +} + +/* Function: ns8382x_init_txd + * Description: initializes the Tx descriptor + * Arguments: struct eth_device *dev: NIC data structure + * returns: void. + */ + +static void +ns8382x_init_txd(struct eth_device *dev) +{ + txd.link = (u32) 0; + txd.bufptr = cpu_to_le32((u32) & txb[0]); + txd.cmdsts = (u32) 0; + txd.extsts = (u32) 0; + + OUTL(dev, 0x0, TxRingPtrHi); + OUTL(dev, phys_to_bus((u32)&txd), TxRingPtr); +#ifdef NS8382X_DEBUG + printf("ns8382x_init_txd: TX descriptor register loaded with: %#08X (&txd: %p)\n", + INL(dev, TxRingPtr), &txd); +#endif +} + +/* Function: ns8382x_init_rxd + * Description: initializes the Rx descriptor ring + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ + +static void +ns8382x_init_rxd(struct eth_device *dev) +{ + int i; + + OUTL(dev, 0x0, RxRingPtrHi); + + cur_rx = 0; + for (i = 0; i < NUM_RX_DESC; i++) { + rxd[i].link = + cpu_to_le32((i + 1 < + NUM_RX_DESC) ? (u32) & rxd[i + + 1] : (u32) & + rxd[0]); + rxd[i].extsts = cpu_to_le32((u32) 0x0); + rxd[i].cmdsts = cpu_to_le32((u32) RX_BUF_SIZE); + rxd[i].bufptr = cpu_to_le32((u32) & rxb[i * RX_BUF_SIZE]); +#ifdef NS8382X_DEBUG + printf + ("ns8382x_init_rxd: rxd[%d]=%p link=%X cmdsts=%X bufptr=%X\n", + i, &rxd[i], le32_to_cpu(rxd[i].link), + le32_to_cpu(rxd[i].cmdsts), le32_to_cpu(rxd[i].bufptr)); +#endif + } + OUTL(dev, phys_to_bus((u32) & rxd), RxRingPtr); + +#ifdef NS8382X_DEBUG + printf("ns8382x_init_rxd: RX descriptor register loaded with: %X\n", + INL(dev, RxRingPtr)); +#endif +} + +/* Function: ns8382x_set_rx_mode + * Description: + * sets the receive mode to accept all broadcast packets and packets + * with our MAC address, and reject all multicast packets. + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ + +static void +ns8382x_set_rx_mode(struct eth_device *dev) +{ + u32 rx_mode = 0x0; + /*spec says RxFilterEnable has to be 0 for rest of + * this stuff to be properly configured. Linux driver + * seems to support this*/ +/* OUTL(dev, rx_mode, RxFilterAddr);*/ + rx_mode = (RxFilterEnable | AcceptAllBroadcast | AcceptPerfectMatch); + OUTL(dev, rx_mode, RxFilterAddr); + printf("ns8382x_set_rx_mode: set to %X\n", rx_mode); + /*now we turn RxFilterEnable back on */ + /*rx_mode |= RxFilterEnable; + OUTL(dev, rx_mode, RxFilterAddr);*/ +} + +static void +ns8382x_check_duplex(struct eth_device *dev) +{ + int gig = 0; + int hun = 0; + int duplex = 0; + int config = (INL(dev, ChipConfig) ^ SpeedStatus_Polarity); + + duplex = (config & FullDuplex) ? 1 : 0; + gig = (config & GigSpeed) ? 1 : 0; + hun = (config & HundSpeed) ? 1 : 0; +#ifdef NS8382X_DEBUG + printf("%s: Setting 10%s %s-duplex based on negotiated link" + " capability.\n", dev->name, (gig) ? "00" : (hun) ? "0" : "", + duplex ? "full" : "half"); +#endif + if (duplex) { + rx_config |= RxAcceptTx; + tx_config |= (TxCarrierIgn | TxHeartIgn); + } else { + rx_config &= ~RxAcceptTx; + tx_config &= ~(TxCarrierIgn | TxHeartIgn); + } +#ifdef NS8382X_DEBUG + printf("%s: Resetting TxConfig Register %#08X\n", dev->name, tx_config); + printf("%s: Resetting RxConfig Register %#08X\n", dev->name, rx_config); +#endif + OUTL(dev, tx_config, TxConfig); + OUTL(dev, rx_config, RxConfig); + + /*if speed is 10 or 100, remove MODE1000, + * if it's 1000, then set it */ + config = INL(dev, ChipConfig); + if (gig) + config |= Mode1000; + else + config &= ~Mode1000; + +#ifdef NS8382X_DEBUG + printf("%s: %setting Mode1000\n", dev->name, (gig) ? "S" : "Uns"); +#endif + OUTL(dev, config, ChipConfig); +} + +/* Function: ns8382x_send + * Description: transmits a packet and waits for completion or timeout. + * Returns: void. */ +static int +ns8382x_send(struct eth_device *dev, volatile void *packet, int length) +{ + u32 i, status = 0; + vu_long tx_stat = 0; + + /* Stop the transmitter */ + OUTL(dev, TxOff, ChipCmd); +#ifdef NS8382X_DEBUG + printf("ns8382x_send: sending %d bytes\n", (int)length); +#endif + + /* set the transmit buffer descriptor and enable Transmit State Machine */ + txd.link = cpu_to_le32(0x0); + txd.bufptr = cpu_to_le32(phys_to_bus((u32)packet)); + txd.extsts = cpu_to_le32(0x0); + txd.cmdsts = cpu_to_le32(DescOwn | length); + + /* load Transmit Descriptor Register */ + OUTL(dev, phys_to_bus((u32) & txd), TxRingPtr); +#ifdef NS8382X_DEBUG + printf("ns8382x_send: TX descriptor register loaded with: %#08X\n", + INL(dev, TxRingPtr)); + printf("\ttxd.link:%X\tbufp:%X\texsts:%X\tcmdsts:%X\n", + le32_to_cpu(txd.link), le32_to_cpu(txd.bufptr), + le32_to_cpu(txd.extsts), le32_to_cpu(txd.cmdsts)); +#endif + /* restart the transmitter */ + OUTL(dev, TxOn, ChipCmd); + + for (i = 0; (tx_stat = le32_to_cpu(txd.cmdsts)) & DescOwn; i++) { + if (i >= TOUT_LOOP) { + printf ("%s: tx error buffer not ready: txd.cmdsts %#lX\n", + dev->name, tx_stat); + goto Done; + } + } + + if (!(tx_stat & DescPktOK)) { + printf("ns8382x_send: Transmit error, Tx status %lX.\n", tx_stat); + goto Done; + } +#ifdef NS8382X_DEBUG + printf("ns8382x_send: tx_stat: %#08X\n", tx_stat); +#endif + + status = 1; + Done: + return status; +} + +/* Function: ns8382x_poll + * Description: checks for a received packet and returns it if found. + * Arguments: struct eth_device *dev: NIC data structure + * Returns: 1 if packet was received. + * 0 if no packet was received. + * Side effects: + * Returns (copies) the packet to the array dev->packet. + * Returns the length of the packet. + */ + +static int +ns8382x_poll(struct eth_device *dev) +{ + int retstat = 0; + int length = 0; + vu_long rx_status = le32_to_cpu(rxd[cur_rx].cmdsts); + + if (!(rx_status & (u32) DescOwn)) + return retstat; +#ifdef NS8382X_DEBUG + printf("ns8382x_poll: got a packet: cur_rx:%u, status:%lx\n", + cur_rx, rx_status); +#endif + length = (rx_status & DSIZE) - CRC_SIZE; + + if ((rx_status & (DescMore | DescPktOK | DescRxLong)) != DescPktOK) { + /* corrupted packet received */ + printf("ns8382x_poll: Corrupted packet, status:%lx\n", rx_status); + retstat = 0; + } else { + /* give packet to higher level routine */ + NetReceive((rxb + cur_rx * RX_BUF_SIZE), length); + retstat = 1; + } + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].cmdsts = cpu_to_le32(RX_BUF_SIZE); + rxd[cur_rx].bufptr = cpu_to_le32((u32) & rxb[cur_rx * RX_BUF_SIZE]); + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* re-enable the potentially idle receive state machine */ + OUTL(dev, RxOn, ChipCmd); + + return retstat; +} + +/* Function: ns8382x_disable + * Description: Turns off interrupts and stops Tx and Rx engines + * Arguments: struct eth_device *dev: NIC data structure + * Returns: void. + */ + +static void +ns8382x_disable(struct eth_device *dev) +{ + /* Disable interrupts using the mask. */ + OUTL(dev, 0, IntrMask); + OUTL(dev, 0, IntrEnable); + + /* Stop the chip's Tx and Rx processes. */ + OUTL(dev, (RxOff | TxOff), ChipCmd); + + /* Restore PME enable bit */ + OUTL(dev, SavedClkRun, ClkRun); +} diff --git a/roms/u-boot-sam460ex/drivers/net/ns9750_eth.c b/roms/u-boot-sam460ex/drivers/net/ns9750_eth.c new file mode 100644 index 000000000..d4901b411 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/ns9750_eth.c @@ -0,0 +1,790 @@ +/*********************************************************************** + * + * Copyright (C) 2004 by FS Forth-Systeme GmbH. + * All rights reserved. + * + * $Id: ns9750_eth.c,v 1.2 2004/02/24 14:09:39 mpietrek Exp $ + * @Author: Markus Pietrek + * @Descr: Ethernet driver for the NS9750. Uses DMA Engine with polling + * interrupt status. But interrupts are not enabled. + * Only one tx buffer descriptor and the RXA buffer descriptor are used + * Currently no transmit lockup handling is included. eth_send has a 5s + * timeout for sending frames. No retransmits are performed when an + * error occurs. + * @References: [1] NS9750 Hardware Reference, December 2003 + * [2] Intel LXT971 Datasheet #249414 Rev. 02 + * [3] NS7520 Linux Ethernet Driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ***********************************************************************/ + +#include <common.h> +#include <net.h> /* NetSendPacket */ + +#include "ns9750_eth.h" /* for Ethernet and PHY */ + +/* some definition to make transition to linux easier */ + +#define NS9750_DRIVER_NAME "eth" +#define KERN_WARNING "Warning:" +#define KERN_ERR "Error:" +#define KERN_INFO "Info:" + +#if 0 +# define DEBUG +#endif + +#ifdef DEBUG +# define printk printf + +# define DEBUG_INIT 0x0001 +# define DEBUG_MINOR 0x0002 +# define DEBUG_RX 0x0004 +# define DEBUG_TX 0x0008 +# define DEBUG_INT 0x0010 +# define DEBUG_POLL 0x0020 +# define DEBUG_LINK 0x0040 +# define DEBUG_MII 0x0100 +# define DEBUG_MII_LOW 0x0200 +# define DEBUG_MEM 0x0400 +# define DEBUG_ERROR 0x4000 +# define DEBUG_ERROR_CRIT 0x8000 + +static int nDebugLvl = DEBUG_ERROR_CRIT; + +# define DEBUG_ARGS0( FLG, a0 ) if( ( nDebugLvl & (FLG) ) == (FLG) ) \ + printf("%s: " a0, __FUNCTION__, 0, 0, 0, 0, 0, 0 ) +# define DEBUG_ARGS1( FLG, a0, a1 ) if( ( nDebugLvl & (FLG) ) == (FLG)) \ + printf("%s: " a0, __FUNCTION__, (int)(a1), 0, 0, 0, 0, 0 ) +# define DEBUG_ARGS2( FLG, a0, a1, a2 ) if( (nDebugLvl & (FLG)) ==(FLG))\ + printf("%s: " a0, __FUNCTION__, (int)(a1), (int)(a2), 0, 0,0,0 ) +# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) if((nDebugLvl &(FLG))==(FLG))\ + printf("%s: "a0,__FUNCTION__,(int)(a1),(int)(a2),(int)(a3),0,0,0) +# define DEBUG_FN( FLG ) if( (nDebugLvl & (FLG)) == (FLG) ) \ + printf("\r%s:line %d\n", (int)__FUNCTION__, __LINE__, 0,0,0,0); +# define ASSERT( expr, func ) if( !( expr ) ) { \ + printf( "Assertion failed! %s:line %d %s\n", \ + (int)__FUNCTION__,__LINE__,(int)(#expr),0,0,0); \ + func } +#else /* DEBUG */ +# define printk(...) +# define DEBUG_ARGS0( FLG, a0 ) +# define DEBUG_ARGS1( FLG, a0, a1 ) +# define DEBUG_ARGS2( FLG, a0, a1, a2 ) +# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) +# define DEBUG_FN( n ) +# define ASSERT(expr, func) +#endif /* DEBUG */ + +#define NS9750_MII_NEG_DELAY (5*CONFIG_SYS_HZ) /* in s */ +#define TX_TIMEOUT (5*CONFIG_SYS_HZ) /* in s */ + +/* @TODO move it to eeprom.h */ +#define FS_EEPROM_AUTONEG_MASK 0x7 +#define FS_EEPROM_AUTONEG_SPEED_MASK 0x1 +#define FS_EEPROM_AUTONEG_SPEED_10 0x0 +#define FS_EEPROM_AUTONEG_SPEED_100 0x1 +#define FS_EEPROM_AUTONEG_DUPLEX_MASK 0x2 +#define FS_EEPROM_AUTONEG_DUPLEX_HALF 0x0 +#define FS_EEPROM_AUTONEG_DUPLEX_FULL 0x2 +#define FS_EEPROM_AUTONEG_ENABLE_MASK 0x4 +#define FS_EEPROM_AUTONEG_DISABLE 0x0 +#define FS_EEPROM_AUTONEG_ENABLE 0x4 + +/* buffer descriptors taken from [1] p.306 */ +typedef struct +{ + unsigned int* punSrc; + unsigned int unLen; /* 11 bits */ + unsigned int* punDest; /* unused */ + union { + unsigned int unReg; + struct { + unsigned uStatus : 16; + unsigned uRes : 12; + unsigned uFull : 1; + unsigned uEnable : 1; + unsigned uInt : 1; + unsigned uWrap : 1; + } bits; + } s; +} rx_buffer_desc_t; + +typedef struct +{ + unsigned int* punSrc; + unsigned int unLen; /* 10 bits */ + unsigned int* punDest; /* unused */ + union { + unsigned int unReg; /* only 32bit accesses may done to NS9750 + * eth engine */ + struct { + unsigned uStatus : 16; + unsigned uRes : 12; + unsigned uFull : 1; + unsigned uLast : 1; + unsigned uInt : 1; + unsigned uWrap : 1; + } bits; + } s; +} tx_buffer_desc_t; + +static int ns9750_eth_reset( void ); + +static void ns9750_link_force( void ); +static void ns9750_link_auto_negotiate( void ); +static void ns9750_link_update_egcr( void ); +static void ns9750_link_print_changed( void ); + +/* the PHY stuff */ + +static char ns9750_mii_identify_phy( void ); +static unsigned short ns9750_mii_read( unsigned short uiRegister ); +static void ns9750_mii_write( unsigned short uiRegister, unsigned short uiData ); +static unsigned int ns9750_mii_get_clock_divisor( unsigned int unMaxMDIOClk ); +static unsigned int ns9750_mii_poll_busy( void ); + +static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; +static unsigned char ucLinkMode = FS_EEPROM_AUTONEG_ENABLE; +static unsigned int uiLastLinkStatus; +static PhyType phyDetected = PHY_NONE; + +/* we use only one tx buffer descriptor */ +static tx_buffer_desc_t* pTxBufferDesc = + (tx_buffer_desc_t*) get_eth_reg_addr( NS9750_ETH_TXBD ); + +/* we use only one rx buffer descriptor of the 4 */ +static rx_buffer_desc_t aRxBufferDesc[ 4 ]; + +/*********************************************************************** + * @Function: eth_init + * @Return: -1 on failure otherwise 0 + * @Descr: Initializes the ethernet engine and uses either FS Forth's default + * MAC addr or the one in environment + ***********************************************************************/ + +int eth_init (bd_t * pbis) +{ + /* This default MAC Addr is reserved by FS Forth-Systeme for the case of + EEPROM failures */ + unsigned char aucMACAddr[6] = { 0x00, 0x04, 0xf3, 0x00, 0x06, 0x35 }; + char *pcTmp = getenv ("ethaddr"); + char *pcEnd; + int i; + + DEBUG_FN (DEBUG_INIT); + + /* no need to check for hardware */ + + if (!ns9750_eth_reset ()) + return -1; + + if (pcTmp != NULL) + for (i = 0; i < 6; i++) { + aucMACAddr[i] = + pcTmp ? simple_strtoul (pcTmp, &pcEnd, + 16) : 0; + pcTmp = (*pcTmp) ? pcEnd + 1 : pcEnd; + } + + /* configure ethernet address */ + + *get_eth_reg_addr (NS9750_ETH_SA1) = + aucMACAddr[5] << 8 | aucMACAddr[4]; + *get_eth_reg_addr (NS9750_ETH_SA2) = + aucMACAddr[3] << 8 | aucMACAddr[2]; + *get_eth_reg_addr (NS9750_ETH_SA3) = + aucMACAddr[1] << 8 | aucMACAddr[0]; + + /* enable hardware */ + + *get_eth_reg_addr (NS9750_ETH_MAC1) = NS9750_ETH_MAC1_RXEN; + + /* the linux kernel may give packets < 60 bytes, for example arp */ + *get_eth_reg_addr (NS9750_ETH_MAC2) = NS9750_ETH_MAC2_CRCEN | + NS9750_ETH_MAC2_PADEN | NS9750_ETH_MAC2_HUGE; + + /* enable receive and transmit FIFO, use 10/100 Mbps MII */ + *get_eth_reg_addr (NS9750_ETH_EGCR1) = + NS9750_ETH_EGCR1_ETXWM | + NS9750_ETH_EGCR1_ERX | + NS9750_ETH_EGCR1_ERXDMA | + NS9750_ETH_EGCR1_ETX | + NS9750_ETH_EGCR1_ETXDMA | NS9750_ETH_EGCR1_ITXA; + + /* prepare DMA descriptors */ + for (i = 0; i < 4; i++) { + aRxBufferDesc[i].punSrc = 0; + aRxBufferDesc[i].unLen = 0; + aRxBufferDesc[i].s.bits.uWrap = 1; + aRxBufferDesc[i].s.bits.uInt = 1; + aRxBufferDesc[i].s.bits.uEnable = 0; + aRxBufferDesc[i].s.bits.uFull = 0; + } + + /* NetRxPackets[ 0 ] is initialized before eth_init is called and never + changes. NetRxPackets is 32bit aligned */ + aRxBufferDesc[0].punSrc = (unsigned int *) NetRxPackets[0]; + aRxBufferDesc[0].s.bits.uEnable = 1; + aRxBufferDesc[0].unLen = 1522; /* as stated in [1] p.307 */ + + *get_eth_reg_addr (NS9750_ETH_RXAPTR) = + (unsigned int) &aRxBufferDesc[0]; + + /* [1] Tab. 221 states less than 5us */ + *get_eth_reg_addr (NS9750_ETH_EGCR1) |= NS9750_ETH_EGCR1_ERXINIT; + while (! + (*get_eth_reg_addr (NS9750_ETH_EGSR) & NS9750_ETH_EGSR_RXINIT)) + /* wait for finish */ + udelay (1); + + /* @TODO do we need to clear RXINIT? */ + *get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~NS9750_ETH_EGCR1_ERXINIT; + + *get_eth_reg_addr (NS9750_ETH_RXFREE) = 0x1; + + return 0; +} + +/*********************************************************************** + * @Function: eth_send + * @Return: -1 on timeout otherwise 1 + * @Descr: sends one frame by DMA + ***********************************************************************/ + +int eth_send (volatile void *pPacket, int nLen) +{ + ulong ulTimeout; + + DEBUG_FN (DEBUG_TX); + + /* clear old status values */ + *get_eth_reg_addr (NS9750_ETH_EINTR) &= + *get_eth_reg_addr (NS9750_ETH_EINTR) & NS9750_ETH_EINTR_TX_MA; + + /* prepare Tx Descriptors */ + + pTxBufferDesc->punSrc = (unsigned int *) pPacket; /* pPacket is 32bit + * aligned */ + pTxBufferDesc->unLen = nLen; + /* only 32bit accesses allowed. wrap, full, interrupt and enabled to 1 */ + pTxBufferDesc->s.unReg = 0xf0000000; + /* pTxBufferDesc is the first possible buffer descriptor */ + *get_eth_reg_addr (NS9750_ETH_TXPTR) = 0x0; + + /* enable processor for next frame */ + + *get_eth_reg_addr (NS9750_ETH_EGCR2) &= ~NS9750_ETH_EGCR2_TCLER; + *get_eth_reg_addr (NS9750_ETH_EGCR2) |= NS9750_ETH_EGCR2_TCLER; + + ulTimeout = get_timer (0); + + DEBUG_ARGS0 (DEBUG_TX | DEBUG_MINOR, + "Waiting for transmission to finish\n"); + while (! + (*get_eth_reg_addr (NS9750_ETH_EINTR) & + (NS9750_ETH_EINTR_TXDONE | NS9750_ETH_EINTR_TXERR))) { + /* do nothing, wait for completion */ + if (get_timer (0) - ulTimeout > TX_TIMEOUT) { + DEBUG_ARGS0 (DEBUG_TX, "Transmit Timed out\n"); + return -1; + } + } + DEBUG_ARGS0 (DEBUG_TX | DEBUG_MINOR, "transmitted...\n"); + + return 0; +} + +/*********************************************************************** + * @Function: eth_rx + * @Return: size of last frame in bytes or 0 if no frame available + * @Descr: gives one frame to U-Boot which has been copied by DMA engine already + * to NetRxPackets[ 0 ]. + ***********************************************************************/ + +int eth_rx (void) +{ + int nLen = 0; + unsigned int unStatus; + + unStatus = + *get_eth_reg_addr (NS9750_ETH_EINTR) & NS9750_ETH_EINTR_RX_MA; + + if (!unStatus) + /* no packet available, return immediately */ + return 0; + + DEBUG_FN (DEBUG_RX); + + /* unLen always < max(nLen) and discard checksum */ + nLen = (int) aRxBufferDesc[0].unLen - 4; + + /* acknowledge status register */ + *get_eth_reg_addr (NS9750_ETH_EINTR) = unStatus; + + aRxBufferDesc[0].unLen = 1522; + aRxBufferDesc[0].s.bits.uFull = 0; + + /* Buffer A descriptor available again */ + *get_eth_reg_addr (NS9750_ETH_RXFREE) |= 0x1; + + /* NetReceive may call eth_send. Due to a possible bug of the NS9750 we + * have to acknowledge the received frame before sending a new one */ + if (unStatus & NS9750_ETH_EINTR_RXDONEA) + NetReceive (NetRxPackets[0], nLen); + + return nLen; +} + +/*********************************************************************** + * @Function: eth_halt + * @Return: n/a + * @Descr: stops the ethernet engine + ***********************************************************************/ + +void eth_halt (void) +{ + DEBUG_FN (DEBUG_INIT); + + *get_eth_reg_addr (NS9750_ETH_MAC1) &= ~NS9750_ETH_MAC1_RXEN; + *get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~(NS9750_ETH_EGCR1_ERX | + NS9750_ETH_EGCR1_ERXDMA | + NS9750_ETH_EGCR1_ETX | + NS9750_ETH_EGCR1_ETXDMA); +} + +/*********************************************************************** + * @Function: ns9750_eth_reset + * @Return: 0 on failure otherwise 1 + * @Descr: resets the ethernet interface and the PHY, + * performs auto negotiation or fixed modes + ***********************************************************************/ + +static int ns9750_eth_reset (void) +{ + DEBUG_FN (DEBUG_MINOR); + + /* Reset MAC */ + *get_eth_reg_addr (NS9750_ETH_EGCR1) |= NS9750_ETH_EGCR1_MAC_HRST; + udelay (5); /* according to [1], p.322 */ + *get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~NS9750_ETH_EGCR1_MAC_HRST; + + /* reset and initialize PHY */ + + *get_eth_reg_addr (NS9750_ETH_MAC1) &= ~NS9750_ETH_MAC1_SRST; + + /* we don't support hot plugging of PHY, therefore we don't reset + phyDetected and nPhyMaxMdioClock here. The risk is if the setting is + incorrect the first open + may detect the PHY correctly but succeding will fail + For reseting the PHY and identifying we have to use the standard + MDIO CLOCK value 2.5 MHz only after hardware reset + After having identified the PHY we will do faster */ + + *get_eth_reg_addr (NS9750_ETH_MCFG) = + ns9750_mii_get_clock_divisor (nPhyMaxMdioClock); + + /* reset PHY */ + ns9750_mii_write(PHY_BMCR, PHY_BMCR_RESET); + ns9750_mii_write(PHY_BMCR, 0); + + /* @TODO check time */ + udelay (3000); /* [2] p.70 says at least 300us reset recovery time. But + go sure, it didn't worked stable at higher timer + frequencies under LxNETES-2.x */ + + /* MII clock has been setup to default, ns9750_mii_identify_phy should + work for all */ + + if (!ns9750_mii_identify_phy ()) { + printk (KERN_ERR NS9750_DRIVER_NAME + ": Unsupported PHY, aborting\n"); + return 0; + } + + /* now take the highest MDIO clock possible after detection */ + *get_eth_reg_addr (NS9750_ETH_MCFG) = + ns9750_mii_get_clock_divisor (nPhyMaxMdioClock); + + + /* PHY has been detected, so there can be no abort reason and we can + finish initializing ethernet */ + + uiLastLinkStatus = 0xff; /* undefined */ + + if ((ucLinkMode & FS_EEPROM_AUTONEG_ENABLE_MASK) == + FS_EEPROM_AUTONEG_DISABLE) + /* use parameters defined */ + ns9750_link_force (); + else + ns9750_link_auto_negotiate (); + + if (phyDetected == PHY_LXT971A) + /* set LED2 to link mode */ + ns9750_mii_write (PHY_LXT971_LED_CFG, + PHY_LXT971_LED_CFG_LINK_ACT << + PHY_LXT971_LED_CFG_SHIFT_LED2); + + return 1; +} + +/*********************************************************************** + * @Function: ns9750_link_force + * @Return: void + * @Descr: configures eth and MII to use the link mode defined in + * ucLinkMode + ***********************************************************************/ + +static void ns9750_link_force (void) +{ + unsigned short uiControl; + + DEBUG_FN (DEBUG_LINK); + + uiControl = ns9750_mii_read(PHY_BMCR); + uiControl &= ~(PHY_BMCR_SPEED_MASK | + PHY_BMCR_AUTON | PHY_BMCR_DPLX); + + uiLastLinkStatus = 0; + + if ((ucLinkMode & FS_EEPROM_AUTONEG_SPEED_MASK) == + FS_EEPROM_AUTONEG_SPEED_100) { + uiControl |= PHY_BMCR_100MB; + uiLastLinkStatus |= PHY_LXT971_STAT2_100BTX; + } else + uiControl |= PHY_BMCR_10_MBPS; + + if ((ucLinkMode & FS_EEPROM_AUTONEG_DUPLEX_MASK) == + FS_EEPROM_AUTONEG_DUPLEX_FULL) { + uiControl |= PHY_BMCR_DPLX; + uiLastLinkStatus |= PHY_LXT971_STAT2_DUPLEX_MODE; + } + + ns9750_mii_write(PHY_BMCR, uiControl); + + ns9750_link_print_changed (); + ns9750_link_update_egcr (); +} + +/*********************************************************************** + * @Function: ns9750_link_auto_negotiate + * @Return: void + * @Descr: performs auto-negotation of link. + ***********************************************************************/ + +static void ns9750_link_auto_negotiate (void) +{ + unsigned long ulStartJiffies; + unsigned short uiStatus; + + DEBUG_FN (DEBUG_LINK); + + /* run auto-negotation */ + /* define what we are capable of */ + ns9750_mii_write(PHY_ANAR, + PHY_ANLPAR_TXFD | + PHY_ANLPAR_TX | + PHY_ANLPAR_10FD | + PHY_ANLPAR_10 | + PHY_ANLPAR_PSB_802_3); + /* start auto-negotiation */ + ns9750_mii_write(PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); + + /* wait for completion */ + + ulStartJiffies = get_ticks (); + while (get_ticks () < ulStartJiffies + NS9750_MII_NEG_DELAY) { + uiStatus = ns9750_mii_read(PHY_BMSR); + if ((uiStatus & + (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) == + (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) { + /* lucky we are, auto-negotiation succeeded */ + ns9750_link_print_changed (); + ns9750_link_update_egcr (); + return; + } + } + + DEBUG_ARGS0 (DEBUG_LINK, "auto-negotiation timed out\n"); + /* ignore invalid link settings */ +} + +/*********************************************************************** + * @Function: ns9750_link_update_egcr + * @Return: void + * @Descr: updates the EGCR and MAC2 link status after mode change or + * auto-negotation + ***********************************************************************/ + +static void ns9750_link_update_egcr (void) +{ + unsigned int unEGCR; + unsigned int unMAC2; + unsigned int unIPGT; + + DEBUG_FN (DEBUG_LINK); + + unEGCR = *get_eth_reg_addr (NS9750_ETH_EGCR1); + unMAC2 = *get_eth_reg_addr (NS9750_ETH_MAC2); + unIPGT = *get_eth_reg_addr (NS9750_ETH_IPGT) & ~NS9750_ETH_IPGT_MA; + + unMAC2 &= ~NS9750_ETH_MAC2_FULLD; + if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE) + == PHY_LXT971_STAT2_DUPLEX_MODE) { + unMAC2 |= NS9750_ETH_MAC2_FULLD; + unIPGT |= 0x15; /* see [1] p. 339 */ + } else + unIPGT |= 0x12; /* see [1] p. 339 */ + + *get_eth_reg_addr (NS9750_ETH_MAC2) = unMAC2; + *get_eth_reg_addr (NS9750_ETH_EGCR1) = unEGCR; + *get_eth_reg_addr (NS9750_ETH_IPGT) = unIPGT; +} + +/*********************************************************************** + * @Function: ns9750_link_print_changed + * @Return: void + * @Descr: checks whether the link status has changed and if so prints + * the new mode + ***********************************************************************/ + +static void ns9750_link_print_changed (void) +{ + unsigned short uiStatus; + unsigned short uiControl; + + DEBUG_FN (DEBUG_LINK); + + uiControl = ns9750_mii_read(PHY_BMCR); + + if ((uiControl & PHY_BMCR_AUTON) == PHY_BMCR_AUTON) { + /* PHY_BMSR_LS is only set on autonegotiation */ + uiStatus = ns9750_mii_read(PHY_BMSR); + + if (!(uiStatus & PHY_BMSR_LS)) { + printk (KERN_WARNING NS9750_DRIVER_NAME + ": link down\n"); + /* @TODO Linux: carrier_off */ + } else { + /* @TODO Linux: carrier_on */ + if (phyDetected == PHY_LXT971A) { + uiStatus = ns9750_mii_read (PHY_LXT971_STAT2); + uiStatus &= (PHY_LXT971_STAT2_100BTX | + PHY_LXT971_STAT2_DUPLEX_MODE | + PHY_LXT971_STAT2_AUTO_NEG); + + /* mask out all uninteresting parts */ + } + /* other PHYs must store their link information in + uiStatus as PHY_LXT971 */ + } + } else { + /* mode has been forced, so uiStatus should be the same as the + last link status, enforce printing */ + uiStatus = uiLastLinkStatus; + uiLastLinkStatus = 0xff; + } + + if (uiStatus != uiLastLinkStatus) { + /* save current link status */ + uiLastLinkStatus = uiStatus; + + /* print new link status */ + + printk (KERN_INFO NS9750_DRIVER_NAME + ": link mode %i Mbps %s duplex %s\n", + (uiStatus & PHY_LXT971_STAT2_100BTX) ? 100 : 10, + (uiStatus & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" : + "half", + (uiStatus & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" : + ""); + } +} + +/*********************************************************************** + * the MII low level stuff + ***********************************************************************/ + +/*********************************************************************** + * @Function: ns9750_mii_identify_phy + * @Return: 1 if supported PHY has been detected otherwise 0 + * @Descr: checks for supported PHY and prints the IDs. + ***********************************************************************/ + +static char ns9750_mii_identify_phy (void) +{ + unsigned short uiID1; + unsigned short uiID2; + unsigned char *szName; + char cRes = 0; + + DEBUG_FN (DEBUG_MII); + + phyDetected = (PhyType) uiID1 = ns9750_mii_read(PHY_PHYIDR1); + + switch (phyDetected) { + case PHY_LXT971A: + szName = "LXT971A"; + uiID2 = ns9750_mii_read(PHY_PHYIDR2); + nPhyMaxMdioClock = PHY_LXT971_MDIO_MAX_CLK; + cRes = 1; + break; + case PHY_NONE: + default: + /* in case uiID1 == 0 && uiID2 == 0 we may have the wrong + address or reset sets the wrong NS9750_ETH_MCFG_CLKS */ + + uiID2 = 0; + szName = "unknown"; + nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; + phyDetected = PHY_NONE; + } + + printk (KERN_INFO NS9750_DRIVER_NAME + ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName); + + return cRes; +} + +/*********************************************************************** + * @Function: ns9750_mii_read + * @Return: the data read from PHY register uiRegister + * @Descr: the data read may be invalid if timed out. If so, a message + * is printed but the invalid data is returned. + * The fixed device address is being used. + ***********************************************************************/ + +static unsigned short ns9750_mii_read (unsigned short uiRegister) +{ + DEBUG_FN (DEBUG_MII_LOW); + + /* write MII register to be read */ + *get_eth_reg_addr (NS9750_ETH_MADR) = + NS9750_ETH_PHY_ADDRESS << 8 | uiRegister; + + *get_eth_reg_addr (NS9750_ETH_MCMD) = NS9750_ETH_MCMD_READ; + + if (!ns9750_mii_poll_busy ()) + printk (KERN_WARNING NS9750_DRIVER_NAME + ": MII still busy in read\n"); + /* continue to read */ + + *get_eth_reg_addr (NS9750_ETH_MCMD) = 0; + + return (unsigned short) (*get_eth_reg_addr (NS9750_ETH_MRDD)); +} + + +/*********************************************************************** + * @Function: ns9750_mii_write + * @Return: nothing + * @Descr: writes the data to the PHY register. In case of a timeout, + * no special handling is performed but a message printed + * The fixed device address is being used. + ***********************************************************************/ + +static void ns9750_mii_write (unsigned short uiRegister, + unsigned short uiData) +{ + DEBUG_FN (DEBUG_MII_LOW); + + /* write MII register to be written */ + *get_eth_reg_addr (NS9750_ETH_MADR) = + NS9750_ETH_PHY_ADDRESS << 8 | uiRegister; + + *get_eth_reg_addr (NS9750_ETH_MWTD) = uiData; + + if (!ns9750_mii_poll_busy ()) { + printf (KERN_WARNING NS9750_DRIVER_NAME + ": MII still busy in write\n"); + } +} + + +/*********************************************************************** + * @Function: ns9750_mii_get_clock_divisor + * @Return: the clock divisor that should be used in NS9750_ETH_MCFG_CLKS + * @Descr: if no clock divisor can be calculated for the + * current SYSCLK and the maximum MDIO Clock, a warning is printed + * and the greatest divisor is taken + ***********************************************************************/ + +static unsigned int ns9750_mii_get_clock_divisor (unsigned int unMaxMDIOClk) +{ + struct { + unsigned int unSysClkDivisor; + unsigned int unClks; /* field for NS9750_ETH_MCFG_CLKS */ + } PHYClockDivisors[] = { + { + 4, NS9750_ETH_MCFG_CLKS_4}, { + 6, NS9750_ETH_MCFG_CLKS_6}, { + 8, NS9750_ETH_MCFG_CLKS_8}, { + 10, NS9750_ETH_MCFG_CLKS_10}, { + 20, NS9750_ETH_MCFG_CLKS_20}, { + 30, NS9750_ETH_MCFG_CLKS_30}, { + 40, NS9750_ETH_MCFG_CLKS_40} + }; + + int nIndexSysClkDiv; + int nArraySize = + sizeof (PHYClockDivisors) / sizeof (PHYClockDivisors[0]); + unsigned int unClks = NS9750_ETH_MCFG_CLKS_40; /* defaults to + greatest div */ + + DEBUG_FN (DEBUG_INIT); + + for (nIndexSysClkDiv = 0; nIndexSysClkDiv < nArraySize; + nIndexSysClkDiv++) { + /* find first sysclock divisor that isn't higher than 2.5 MHz + clock */ + if (AHB_CLK_FREQ / + PHYClockDivisors[nIndexSysClkDiv].unSysClkDivisor <= + unMaxMDIOClk) { + unClks = PHYClockDivisors[nIndexSysClkDiv].unClks; + break; + } + } + + DEBUG_ARGS2 (DEBUG_INIT, + "Taking MDIO Clock bit mask 0x%0x for max clock %i\n", + unClks, unMaxMDIOClk); + + /* return greatest divisor */ + return unClks; +} + +/*********************************************************************** + * @Function: ns9750_mii_poll_busy + * @Return: 0 if timed out otherwise the remaing timeout + * @Descr: waits until the MII has completed a command or it times out + * code may be interrupted by hard interrupts. + * It is not checked what happens on multiple actions when + * the first is still being busy and we timeout. + ***********************************************************************/ + +static unsigned int ns9750_mii_poll_busy (void) +{ + unsigned int unTimeout = 10000; + + DEBUG_FN (DEBUG_MII_LOW); + + while (((*get_eth_reg_addr (NS9750_ETH_MIND) & NS9750_ETH_MIND_BUSY) + == NS9750_ETH_MIND_BUSY) && unTimeout) + unTimeout--; + + return unTimeout; +} diff --git a/roms/u-boot-sam460ex/drivers/net/pcnet.c b/roms/u-boot-sam460ex/drivers/net/pcnet.c new file mode 100644 index 000000000..99b69429e --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/pcnet.c @@ -0,0 +1,537 @@ +/* + * (C) Copyright 2002 Wolfgang Grandegger, wg@denx.de. + * + * This driver for AMD PCnet network controllers is derived from the + * Linux driver pcnet32.c written 1996-1999 by Thomas Bogendoerfer. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> + +#if 0 +#define PCNET_DEBUG_LEVEL 0 /* 0=off, 1=init, 2=rx/tx */ +#endif + +#if PCNET_DEBUG_LEVEL > 0 +#define PCNET_DEBUG1(fmt,args...) printf (fmt ,##args) +#if PCNET_DEBUG_LEVEL > 1 +#define PCNET_DEBUG2(fmt,args...) printf (fmt ,##args) +#else +#define PCNET_DEBUG2(fmt,args...) +#endif +#else +#define PCNET_DEBUG1(fmt,args...) +#define PCNET_DEBUG2(fmt,args...) +#endif + +#if !defined(CONF_PCNET_79C973) && defined(CONF_PCNET_79C975) +#error "Macro for PCnet chip version is not defined!" +#endif + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. + * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). + */ +#define PCNET_LOG_TX_BUFFERS 0 +#define PCNET_LOG_RX_BUFFERS 2 + +#define TX_RING_SIZE (1 << (PCNET_LOG_TX_BUFFERS)) +#define TX_RING_LEN_BITS ((PCNET_LOG_TX_BUFFERS) << 12) + +#define RX_RING_SIZE (1 << (PCNET_LOG_RX_BUFFERS)) +#define RX_RING_LEN_BITS ((PCNET_LOG_RX_BUFFERS) << 4) + +#define PKT_BUF_SZ 1544 + +/* The PCNET Rx and Tx ring descriptors. */ +struct pcnet_rx_head { + u32 base; + s16 buf_length; + s16 status; + u32 msg_length; + u32 reserved; +}; + +struct pcnet_tx_head { + u32 base; + s16 length; + s16 status; + u32 misc; + u32 reserved; +}; + +/* The PCNET 32-Bit initialization block, described in databook. */ +struct pcnet_init_block { + u16 mode; + u16 tlen_rlen; + u8 phys_addr[6]; + u16 reserved; + u32 filter[2]; + /* Receive and transmit ring base, along with extra bits. */ + u32 rx_ring; + u32 tx_ring; + u32 reserved2; +}; + +typedef struct pcnet_priv { + struct pcnet_rx_head rx_ring[RX_RING_SIZE]; + struct pcnet_tx_head tx_ring[TX_RING_SIZE]; + struct pcnet_init_block init_block; + /* Receive Buffer space */ + unsigned char rx_buf[RX_RING_SIZE][PKT_BUF_SZ + 4]; + int cur_rx; + int cur_tx; +} pcnet_priv_t; + +static pcnet_priv_t *lp; + +/* Offsets from base I/O address for WIO mode */ +#define PCNET_RDP 0x10 +#define PCNET_RAP 0x12 +#define PCNET_RESET 0x14 +#define PCNET_BDP 0x16 + +static u16 pcnet_read_csr (struct eth_device *dev, int index) +{ + outw (index, dev->iobase + PCNET_RAP); + return inw (dev->iobase + PCNET_RDP); +} + +static void pcnet_write_csr (struct eth_device *dev, int index, u16 val) +{ + outw (index, dev->iobase + PCNET_RAP); + outw (val, dev->iobase + PCNET_RDP); +} + +static u16 pcnet_read_bcr (struct eth_device *dev, int index) +{ + outw (index, dev->iobase + PCNET_RAP); + return inw (dev->iobase + PCNET_BDP); +} + +static void pcnet_write_bcr (struct eth_device *dev, int index, u16 val) +{ + outw (index, dev->iobase + PCNET_RAP); + outw (val, dev->iobase + PCNET_BDP); +} + +static void pcnet_reset (struct eth_device *dev) +{ + inw (dev->iobase + PCNET_RESET); +} + +static int pcnet_check (struct eth_device *dev) +{ + outw (88, dev->iobase + PCNET_RAP); + return (inw (dev->iobase + PCNET_RAP) == 88); +} + +static int pcnet_init (struct eth_device *dev, bd_t * bis); +static int pcnet_send (struct eth_device *dev, volatile void *packet, + int length); +static int pcnet_recv (struct eth_device *dev); +static void pcnet_halt (struct eth_device *dev); +static int pcnet_probe (struct eth_device *dev, bd_t * bis, int dev_num); + +#define PCI_TO_MEM(d,a) pci_phys_to_mem((pci_dev_t)d->priv, (u_long)(a)) +#define PCI_TO_MEM_LE(d,a) (u32)(cpu_to_le32(PCI_TO_MEM(d,a))) + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE}, + {} +}; + + +int pcnet_initialize (bd_t * bis) +{ + pci_dev_t devbusfn; + struct eth_device *dev; + u16 command, status; + int dev_nr = 0; + + PCNET_DEBUG1 ("\npcnet_initialize...\n"); + + for (dev_nr = 0;; dev_nr++) { + + /* + * Find the PCnet PCI device(s). + */ + if ((devbusfn = pci_find_devices (supported, dev_nr)) < 0) { + break; + } + + /* + * Allocate and pre-fill the device structure. + */ + dev = (struct eth_device *) malloc (sizeof *dev); + dev->priv = (void *) devbusfn; + sprintf (dev->name, "pcnet#%d", dev_nr); + + /* + * Setup the PCI device. + */ + pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0, + (unsigned int *) &dev->iobase); + dev->iobase=pci_io_to_phys (devbusfn, dev->iobase); + dev->iobase &= ~0xf; + + PCNET_DEBUG1 ("%s: devbusfn=0x%x iobase=0x%x: ", + dev->name, devbusfn, dev->iobase); + + command = PCI_COMMAND_IO | PCI_COMMAND_MASTER; + pci_write_config_word (devbusfn, PCI_COMMAND, command); + pci_read_config_word (devbusfn, PCI_COMMAND, &status); + if ((status & command) != command) { + printf ("%s: Couldn't enable IO access or Bus Mastering\n", dev->name); + free (dev); + continue; + } + + pci_write_config_byte (devbusfn, PCI_LATENCY_TIMER, 0x40); + + /* + * Probe the PCnet chip. + */ + if (pcnet_probe (dev, bis, dev_nr) < 0) { + free (dev); + continue; + } + + /* + * Setup device structure and register the driver. + */ + dev->init = pcnet_init; + dev->halt = pcnet_halt; + dev->send = pcnet_send; + dev->recv = pcnet_recv; + + eth_register (dev); + } + + udelay (10 * 1000); + + return dev_nr; +} + +static int pcnet_probe (struct eth_device *dev, bd_t * bis, int dev_nr) +{ + int chip_version; + char *chipname; + +#ifdef PCNET_HAS_PROM + int i; +#endif + + /* Reset the PCnet controller */ + pcnet_reset (dev); + + /* Check if register access is working */ + if (pcnet_read_csr (dev, 0) != 4 || !pcnet_check (dev)) { + printf ("%s: CSR register access check failed\n", dev->name); + return -1; + } + + /* Identify the chip */ + chip_version = + pcnet_read_csr (dev, 88) | (pcnet_read_csr (dev, 89) << 16); + if ((chip_version & 0xfff) != 0x003) + return -1; + chip_version = (chip_version >> 12) & 0xffff; + switch (chip_version) { + case 0x2621: + chipname = "PCnet/PCI II 79C970A"; /* PCI */ + break; +#ifdef CONFIG_PCNET_79C973 + case 0x2625: + chipname = "PCnet/FAST III 79C973"; /* PCI */ + break; +#endif +#ifdef CONFIG_PCNET_79C975 + case 0x2627: + chipname = "PCnet/FAST III 79C975"; /* PCI */ + break; +#endif + default: + printf ("%s: PCnet version %#x not supported\n", + dev->name, chip_version); + return -1; + } + + PCNET_DEBUG1 ("AMD %s\n", chipname); + +#ifdef PCNET_HAS_PROM + /* + * In most chips, after a chip reset, the ethernet address is read from + * the station address PROM at the base address and programmed into the + * "Physical Address Registers" CSR12-14. + */ + for (i = 0; i < 3; i++) { + unsigned int val; + + val = pcnet_read_csr (dev, i + 12) & 0x0ffff; + /* There may be endianness issues here. */ + dev->enetaddr[2 * i] = val & 0x0ff; + dev->enetaddr[2 * i + 1] = (val >> 8) & 0x0ff; + } +#endif /* PCNET_HAS_PROM */ + + return 0; +} + +static int pcnet_init (struct eth_device *dev, bd_t * bis) +{ + int i, val; + u32 addr; + + PCNET_DEBUG1 ("%s: pcnet_init...\n", dev->name); + + /* Switch pcnet to 32bit mode */ + pcnet_write_bcr (dev, 20, 2); + +#ifdef CONFIG_PN62 + /* Setup LED registers */ + val = pcnet_read_bcr (dev, 2) | 0x1000; + pcnet_write_bcr (dev, 2, val); /* enable LEDPE */ + pcnet_write_bcr (dev, 4, 0x5080); /* 100MBit */ + pcnet_write_bcr (dev, 5, 0x40c0); /* LNKSE */ + pcnet_write_bcr (dev, 6, 0x4090); /* TX Activity */ + pcnet_write_bcr (dev, 7, 0x4084); /* RX Activity */ +#endif + + /* Set/reset autoselect bit */ + val = pcnet_read_bcr (dev, 2) & ~2; + val |= 2; + pcnet_write_bcr (dev, 2, val); + + /* Enable auto negotiate, setup, disable fd */ + val = pcnet_read_bcr (dev, 32) & ~0x98; + val |= 0x20; + pcnet_write_bcr (dev, 32, val); + + /* + * We only maintain one structure because the drivers will never + * be used concurrently. In 32bit mode the RX and TX ring entries + * must be aligned on 16-byte boundaries. + */ + if (lp == NULL) { + addr = (u32) malloc (sizeof (pcnet_priv_t) + 0x10); + addr = (addr + 0xf) & ~0xf; + lp = (pcnet_priv_t *) addr; + } + + lp->init_block.mode = cpu_to_le16 (0x0000); + lp->init_block.filter[0] = 0x00000000; + lp->init_block.filter[1] = 0x00000000; + + /* + * Initialize the Rx ring. + */ + lp->cur_rx = 0; + for (i = 0; i < RX_RING_SIZE; i++) { + lp->rx_ring[i].base = PCI_TO_MEM_LE (dev, lp->rx_buf[i]); + lp->rx_ring[i].buf_length = cpu_to_le16 (-PKT_BUF_SZ); + lp->rx_ring[i].status = cpu_to_le16 (0x8000); + PCNET_DEBUG1 + ("Rx%d: base=0x%x buf_length=0x%hx status=0x%hx\n", i, + lp->rx_ring[i].base, lp->rx_ring[i].buf_length, + lp->rx_ring[i].status); + } + + /* + * Initialize the Tx ring. The Tx buffer address is filled in as + * needed, but we do need to clear the upper ownership bit. + */ + lp->cur_tx = 0; + for (i = 0; i < TX_RING_SIZE; i++) { + lp->tx_ring[i].base = 0; + lp->tx_ring[i].status = 0; + } + + /* + * Setup Init Block. + */ + PCNET_DEBUG1 ("Init block at 0x%p: MAC", &lp->init_block); + + for (i = 0; i < 6; i++) { + lp->init_block.phys_addr[i] = dev->enetaddr[i]; + PCNET_DEBUG1 (" %02x", lp->init_block.phys_addr[i]); + } + + lp->init_block.tlen_rlen = cpu_to_le16 (TX_RING_LEN_BITS | + RX_RING_LEN_BITS); + lp->init_block.rx_ring = PCI_TO_MEM_LE (dev, lp->rx_ring); + lp->init_block.tx_ring = PCI_TO_MEM_LE (dev, lp->tx_ring); + + PCNET_DEBUG1 ("\ntlen_rlen=0x%x rx_ring=0x%x tx_ring=0x%x\n", + lp->init_block.tlen_rlen, + lp->init_block.rx_ring, lp->init_block.tx_ring); + + /* + * Tell the controller where the Init Block is located. + */ + addr = PCI_TO_MEM (dev, &lp->init_block); + pcnet_write_csr (dev, 1, addr & 0xffff); + pcnet_write_csr (dev, 2, (addr >> 16) & 0xffff); + + pcnet_write_csr (dev, 4, 0x0915); + pcnet_write_csr (dev, 0, 0x0001); /* start */ + + /* Wait for Init Done bit */ + for (i = 10000; i > 0; i--) { + if (pcnet_read_csr (dev, 0) & 0x0100) + break; + udelay (10); + } + if (i <= 0) { + printf ("%s: TIMEOUT: controller init failed\n", dev->name); + pcnet_reset (dev); + return -1; + } + + /* + * Finally start network controller operation. + */ + pcnet_write_csr (dev, 0, 0x0002); + + return 0; +} + +static int pcnet_send (struct eth_device *dev, volatile void *packet, + int pkt_len) +{ + int i, status; + struct pcnet_tx_head *entry = &lp->tx_ring[lp->cur_tx]; + + PCNET_DEBUG2 ("Tx%d: %d bytes from 0x%p ", lp->cur_tx, pkt_len, + packet); + + /* Wait for completion by testing the OWN bit */ + for (i = 1000; i > 0; i--) { + status = le16_to_cpu (entry->status); + if ((status & 0x8000) == 0) + break; + udelay (100); + PCNET_DEBUG2 ("."); + } + if (i <= 0) { + printf ("%s: TIMEOUT: Tx%d failed (status = 0x%x)\n", + dev->name, lp->cur_tx, status); + pkt_len = 0; + goto failure; + } + + /* + * Setup Tx ring. Caution: the write order is important here, + * set the status with the "ownership" bits last. + */ + status = 0x8300; + entry->length = le16_to_cpu (-pkt_len); + entry->misc = 0x00000000; + entry->base = PCI_TO_MEM_LE (dev, packet); + entry->status = le16_to_cpu (status); + + /* Trigger an immediate send poll. */ + pcnet_write_csr (dev, 0, 0x0008); + + failure: + if (++lp->cur_tx >= TX_RING_SIZE) + lp->cur_tx = 0; + + PCNET_DEBUG2 ("done\n"); + return pkt_len; +} + +static int pcnet_recv (struct eth_device *dev) +{ + struct pcnet_rx_head *entry; + int pkt_len = 0; + u16 status; + + while (1) { + entry = &lp->rx_ring[lp->cur_rx]; + /* + * If we own the next entry, it's a new packet. Send it up. + */ + if (((status = le16_to_cpu (entry->status)) & 0x8000) != 0) { + break; + } + status >>= 8; + + if (status != 0x03) { /* There was an error. */ + + printf ("%s: Rx%d", dev->name, lp->cur_rx); + PCNET_DEBUG1 (" (status=0x%x)", status); + if (status & 0x20) + printf (" Frame"); + if (status & 0x10) + printf (" Overflow"); + if (status & 0x08) + printf (" CRC"); + if (status & 0x04) + printf (" Fifo"); + printf (" Error\n"); + entry->status &= le16_to_cpu (0x03ff); + + } else { + + pkt_len = + (le32_to_cpu (entry->msg_length) & 0xfff) - 4; + if (pkt_len < 60) { + printf ("%s: Rx%d: invalid packet length %d\n", dev->name, lp->cur_rx, pkt_len); + } else { + NetReceive (lp->rx_buf[lp->cur_rx], pkt_len); + PCNET_DEBUG2 ("Rx%d: %d bytes from 0x%p\n", + lp->cur_rx, pkt_len, + lp->rx_buf[lp->cur_rx]); + } + } + entry->status |= cpu_to_le16 (0x8000); + + if (++lp->cur_rx >= RX_RING_SIZE) + lp->cur_rx = 0; + } + return pkt_len; +} + +static void pcnet_halt (struct eth_device *dev) +{ + int i; + + PCNET_DEBUG1 ("%s: pcnet_halt...\n", dev->name); + + /* Reset the PCnet controller */ + pcnet_reset (dev); + + /* Wait for Stop bit */ + for (i = 1000; i > 0; i--) { + if (pcnet_read_csr (dev, 0) & 0x4) + break; + udelay (10); + } + if (i <= 0) { + printf ("%s: TIMEOUT: controller reset failed\n", dev->name); + } +} diff --git a/roms/u-boot-sam460ex/drivers/net/phy/Makefile b/roms/u-boot-sam460ex/drivers/net/phy/Makefile new file mode 100644 index 000000000..3b92614ac --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/phy/Makefile @@ -0,0 +1,47 @@ +# +# (C) Copyright 2008 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libphy.a + +COBJS-$(CONFIG_BITBANGMII) += miiphybb.o +COBJS-$(CONFIG_MV88E61XX_SWITCH) += mv88e61xx.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/roms/u-boot-sam460ex/drivers/net/phy/miiphybb.c b/roms/u-boot-sam460ex/drivers/net/phy/miiphybb.c new file mode 100644 index 000000000..2768c7584 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/phy/miiphybb.c @@ -0,0 +1,380 @@ +/* + * (C) Copyright 2009 Industrie Dial Face S.p.A. + * Luigi 'Comio' Mantellini <luigi.mantellini@idf-hit.com> + * + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This provides a bit-banged interface to the ethernet MII management + * channel. + */ + +#include <common.h> +#include <ioports.h> +#include <ppc_asm.tmpl> +#include <miiphy.h> + +#define BB_MII_RELOCATE(v,off) (v += (v?off:0)) + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_BITBANGMII_MULTI + +/* + * If CONFIG_BITBANGMII_MULTI is not defined we use a + * compatibility layer with the previous miiphybb implementation + * based on macros usage. + * + */ +static int bb_mii_init_wrap(struct bb_miiphy_bus *bus) +{ +#ifdef MII_INIT + MII_INIT; +#endif + return 0; +} + +static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + MDIO_ACTIVE; + return 0; +} + +static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + MDIO_TRISTATE; + return 0; +} + +static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + MDIO(v); + return 0; +} + +static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v) +{ +#ifdef MDIO_DECLARE + MDIO_DECLARE; +#endif + *v = MDIO_READ; + return 0; +} + +static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v) +{ +#ifdef MDC_DECLARE + MDC_DECLARE; +#endif + MDC(v); + return 0; +} + +static int bb_delay_wrap(struct bb_miiphy_bus *bus) +{ + MIIDELAY; + return 0; +} + +struct bb_miiphy_bus bb_miiphy_buses[] = { + { + .name = BB_MII_DEVNAME, + .init = bb_mii_init_wrap, + .mdio_active = bb_mdio_active_wrap, + .mdio_tristate = bb_mdio_tristate_wrap, + .set_mdio = bb_set_mdio_wrap, + .get_mdio = bb_get_mdio_wrap, + .set_mdc = bb_set_mdc_wrap, + .delay = bb_delay_wrap, + } +}; + +int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) / + sizeof(bb_miiphy_buses[0]); +#endif + +void bb_miiphy_init(void) +{ + int i; + + for (i = 0; i < bb_miiphy_buses_num; i++) { +#if !defined(CONFIG_RELOC_FIXUP_WORKS) + /* Relocate the hook pointers*/ + BB_MII_RELOCATE(bb_miiphy_buses[i].init, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_active, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_tristate, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdio, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].get_mdio, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdc, gd->reloc_off); + BB_MII_RELOCATE(bb_miiphy_buses[i].delay, gd->reloc_off); +#endif + if (bb_miiphy_buses[i].init != NULL) { + bb_miiphy_buses[i].init(&bb_miiphy_buses[i]); + } + } +} + +static inline struct bb_miiphy_bus *bb_miiphy_getbus(char *devname) +{ +#ifdef CONFIG_BITBANGMII_MULTI + int i; + + /* Search the correct bus */ + for (i = 0; i < bb_miiphy_buses_num; i++) { + if (!strcmp(bb_miiphy_buses[i].name, devname)) { + return &bb_miiphy_buses[i]; + } + } + return NULL; +#else + /* We have just one bitbanging bus */ + return &bb_miiphy_buses[0]; +#endif +} + +/***************************************************************************** + * + * Utility to send the preamble, address, and register (common to read + * and write). + */ +static void miiphy_pre(struct bb_miiphy_bus *bus, char read, + unsigned char addr, unsigned char reg) +{ + int j; + + /* + * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure. + * The IEEE spec says this is a PHY optional requirement. The AMD + * 79C874 requires one after power up and one after a MII communications + * error. This means that we are doing more preambles than we need, + * but it is safer and will be much more robust. + */ + + bus->mdio_active(bus); + bus->set_mdio(bus, 1); + for (j = 0; j < 32; j++) { + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + } + + /* send the start bit (01) and the read opcode (10) or write (10) */ + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, read); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, !read); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + /* send the PHY address */ + for (j = 0; j < 5; j++) { + bus->set_mdc(bus, 0); + if ((addr & 0x10) == 0) { + bus->set_mdio(bus, 0); + } else { + bus->set_mdio(bus, 1); + } + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + addr <<= 1; + } + + /* send the register address */ + for (j = 0; j < 5; j++) { + bus->set_mdc(bus, 0); + if ((reg & 0x10) == 0) { + bus->set_mdio(bus, 0); + } else { + bus->set_mdio(bus, 1); + } + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + reg <<= 1; + } +} + +/***************************************************************************** + * + * Read a MII PHY register. + * + * Returns: + * 0 on success + */ +int bb_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + short rdreg; /* register working value */ + int v; + int j; /* counter */ + struct bb_miiphy_bus *bus; + + bus = bb_miiphy_getbus(devname); + if (bus == NULL) { + return -1; + } + + if (value == NULL) { + puts("NULL value pointer\n"); + return -1; + } + + miiphy_pre (bus, 1, addr, reg); + + /* tri-state our MDIO I/O pin so we can read */ + bus->set_mdc(bus, 0); + bus->mdio_tristate(bus); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + /* check the turnaround bit: the PHY should be driving it to zero */ + bus->get_mdio(bus, &v); + if (v != 0) { + /* puts ("PHY didn't drive TA low\n"); */ + for (j = 0; j < 32; j++) { + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + } + /* There is no PHY, set value to 0xFFFF and return */ + *value = 0xFFFF; + return -1; + } + + bus->set_mdc(bus, 0); + bus->delay(bus); + + /* read 16 bits of register data, MSB first */ + rdreg = 0; + for (j = 0; j < 16; j++) { + bus->set_mdc(bus, 1); + bus->delay(bus); + rdreg <<= 1; + bus->get_mdio(bus, &v); + rdreg |= (v & 0x1); + bus->set_mdc(bus, 0); + bus->delay(bus); + } + + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + *value = rdreg; + +#ifdef DEBUG + printf ("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, *value); +#endif + + return 0; +} + + +/***************************************************************************** + * + * Write a MII PHY register. + * + * Returns: + * 0 on success + */ +int bb_miiphy_write (char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct bb_miiphy_bus *bus; + int j; /* counter */ + + bus = bb_miiphy_getbus(devname); + if (bus == NULL) { + /* Bus not found! */ + return -1; + } + + miiphy_pre (bus, 0, addr, reg); + + /* send the turnaround (10) */ + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + bus->set_mdc(bus, 0); + bus->set_mdio(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + /* write 16 bits of register data, MSB first */ + for (j = 0; j < 16; j++) { + bus->set_mdc(bus, 0); + if ((value & 0x00008000) == 0) { + bus->set_mdio(bus, 0); + } else { + bus->set_mdio(bus, 1); + } + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + value <<= 1; + } + + /* + * Tri-state the MDIO line. + */ + bus->mdio_tristate(bus); + bus->set_mdc(bus, 0); + bus->delay(bus); + bus->set_mdc(bus, 1); + bus->delay(bus); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/phy/mv88e61xx.c b/roms/u-boot-sam460ex/drivers/net/phy/mv88e61xx.c new file mode 100644 index 000000000..2d1de0291 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/phy/mv88e61xx.c @@ -0,0 +1,418 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Prafulla Wadaskar <prafulla@marvell.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <netdev.h> +#include "mv88e61xx.h" + +#ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE +/* Chip Address mode + * The Switch support two modes of operation + * 1. single chip mode and + * 2. Multi-chip mode + * Refer section 9.2 &9.3 in chip datasheet-02 for more details + * + * By default single chip mode is configured + * multichip mode operation can be configured in board header + */ +static int mv88e61xx_busychk_multic(char *name, u32 devaddr) +{ + u16 reg = 0; + u32 timeout = MV88E61XX_PHY_TIMEOUT; + + /* Poll till SMIBusy bit is clear */ + do { + miiphy_read(name, devaddr, 0x0, ®); + if (timeout-- == 0) { + printf("SMI busy timeout\n"); + return -1; + } + } while (reg & (1 << 15)); + return 0; +} + +static void mv88e61xx_wr_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 data) +{ + u16 mii_dev_addr; + + /* command to read PHY dev address */ + if (miiphy_read(name, 0xEE, 0xEE, &mii_dev_addr)) { + printf("Error..could not read PHY dev address\n"); + return; + } + mv88e61xx_busychk_multic(name, mii_dev_addr); + /* Write data to Switch indirect data register */ + miiphy_write(name, mii_dev_addr, 0x1, data); + /* Write command to Switch indirect command register (write) */ + miiphy_write(name, mii_dev_addr, 0x0, + reg_ofs | (phy_adr << 5) | (1 << 10) | (1 << 12) | (1 << + 15)); +} + +static void mv88e61xx_rd_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 * data) +{ + u16 mii_dev_addr; + + /* command to read PHY dev address */ + if (miiphy_read(name, 0xEE, 0xEE, &mii_dev_addr)) { + printf("Error..could not read PHY dev address\n"); + return; + } + mv88e61xx_busychk_multic(name, mii_dev_addr); + /* Write command to Switch indirect command register (read) */ + miiphy_write(name, mii_dev_addr, 0x0, + reg_ofs | (phy_adr << 5) | (1 << 11) | (1 << 12) | (1 << + 15)); + mv88e61xx_busychk_multic(name, mii_dev_addr); + /* Read data from Switch indirect data register */ + miiphy_read(name, mii_dev_addr, 0x1, data); +} +#endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ + +static void mv88e61xx_port_vlan_config(struct mv88e61xx_config *swconfig, + u32 max_prtnum, u32 ports_ofs) +{ + u32 prt; + u16 reg; + char *name = swconfig->name; + u32 cpu_port = swconfig->cpuport; + u32 port_mask = swconfig->ports_enabled; + enum mv88e61xx_cfg_vlan vlancfg = swconfig->vlancfg; + + /* be sure all ports are disabled */ + for (prt = 0; prt < max_prtnum; prt++) { + RD_PHY(name, ports_ofs + prt, MV88E61XX_PRT_CTRL_REG, ®); + reg &= ~0x3; + WR_PHY(name, ports_ofs + prt, MV88E61XX_PRT_CTRL_REG, reg); + + if (!(cpu_port & (1 << prt))) + continue; + /* Set CPU port VID to 0x1 */ + RD_PHY(name, (ports_ofs + prt), MV88E61XX_PRT_VID_REG, ®); + reg &= ~0xfff; + reg |= 0x1; + WR_PHY(name, (ports_ofs + prt), MV88E61XX_PRT_VID_REG, reg); + } + + /* Setting Port default priority for all ports to zero */ + for (prt = 0; prt < max_prtnum; prt++) { + RD_PHY(name, ports_ofs + prt, MV88E61XX_PRT_VID_REG, ®); + reg &= ~0xc000; + WR_PHY(name, ports_ofs + prt, MV88E61XX_PRT_VID_REG, reg); + } + /* Setting VID and VID map for all ports except CPU port */ + for (prt = 0; prt < max_prtnum; prt++) { + /* only for enabled ports */ + if ((1 << prt) & port_mask) { + /* skip CPU port */ + if ((1 << prt) & cpu_port) { + /* + * Set Vlan map table for cpu_port to see + * all ports + */ + RD_PHY(name, (ports_ofs + prt), + MV88E61XX_PRT_VMAP_REG, ®); + reg &= ~((1 << max_prtnum) - 1); + reg |= port_mask & ~(1 << prt); + WR_PHY(name, (ports_ofs + prt), + MV88E61XX_PRT_VMAP_REG, reg); + } else { + + /* + * set Ports VLAN Mapping. + * port prt <--> cpu_port VLAN #prt+1. + */ + RD_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_VID_REG, ®); + reg &= ~0x0fff; + reg |= (prt + 1); + WR_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_VID_REG, reg); + + RD_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_VMAP_REG, ®); + if (vlancfg == MV88E61XX_VLANCFG_DEFAULT) { + /* + * all any port can send frames to all other ports + * ref: sec 3.2.1.1 of datasheet + */ + reg |= 0x03f; + reg &= ~(1 << prt); + } else if (vlancfg == MV88E61XX_VLANCFG_ROUTER) { + /* + * all other ports can send frames to CPU port only + * ref: sec 3.2.1.2 of datasheet + */ + reg &= ~((1 << max_prtnum) - 1); + reg |= cpu_port; + } + WR_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_VMAP_REG, reg); + } + } + } + + /* + * enable only appropriate ports to forwarding mode + * and disable the others + */ + for (prt = 0; prt < max_prtnum; prt++) { + if ((1 << prt) & port_mask) { + RD_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_CTRL_REG, ®); + reg |= 0x3; + WR_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_CTRL_REG, reg); + } else { + /* Disable port */ + RD_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_CTRL_REG, ®); + reg &= ~0x3; + WR_PHY(name, ports_ofs + prt, + MV88E61XX_PRT_CTRL_REG, reg); + } + } +} + +/* + * Make sure SMIBusy bit cleared before another + * SMI operation can take place + */ +static int mv88e61xx_busychk(char *name) +{ + u16 reg = 0; + u32 timeout = MV88E61XX_PHY_TIMEOUT; + do { + RD_PHY(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, ®); + if (timeout-- == 0) { + printf("SMI busy timeout\n"); + return -1; + } + } while (reg & 1 << 15); /* busy mask */ + return 0; +} + +/* + * Power up the specified port and reset PHY + */ +static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 prt) +{ + char *name = swconfig->name; + + /* Write Copper Specific control reg1 (0x14) for- + * Enable Phy power up + * Energy Detect on (sense&Xmit NLP Periodically + * reset other settings default + */ + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, 0x3360); + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, (0x9410 | (prt << 5))); + + if (mv88e61xx_busychk(name)) + return -1; + + /* Write PHY ctrl reg (0x0) to apply + * Phy reset (set bit 15 low) + * reset other default values + */ + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, 0x1140); + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, (0x9400 | (prt << 5))); + + if (mv88e61xx_busychk(name)) + return -1; + + return 0; +} + +/* + * Default Setup for LED[0]_Control (ref: Table 46 Datasheet-3) + * is set to "On-1000Mb/s Link, Off Else" + * This function sets it to "On-Link, Blink-Activity, Off-NoLink" + * + * This is optional settings may be needed on some boards + * to setup PHY LEDs default configuration to detect 10/100/1000Mb/s + * Link status + */ +static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 prt) +{ + char *name = swconfig->name; + u16 reg; + + if (swconfig->led_init != MV88E61XX_LED_INIT_EN) + return 0; + + /* set page address to 3 */ + reg = 3; + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | + 1 << MV88E61XX_MODE_OFST | + 1 << MV88E61XX_OP_OFST | + prt << MV88E61XX_ADDR_OFST | 22)); + + if (mv88e61xx_busychk(name)) + return -1; + + /* set LED Func Ctrl reg */ + reg = 1; /* LED[0] On-Link, Blink-Activity, Off-NoLink */ + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | + 1 << MV88E61XX_MODE_OFST | + 1 << MV88E61XX_OP_OFST | + prt << MV88E61XX_ADDR_OFST | 16)); + + if (mv88e61xx_busychk(name)) + return -1; + + /* set page address to 0 */ + reg = 0; + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | + 1 << MV88E61XX_MODE_OFST | + 1 << MV88E61XX_OP_OFST | + prt << MV88E61XX_ADDR_OFST | 22)); + + if (mv88e61xx_busychk(name)) + return -1; + + return 0; +} + +/* + * Reverse Transmit polarity for Media Dependent Interface + * Pins (MDIP) bits in Copper Specific Control Register 3 + * (Page 0, Reg 20 for each phy (except cpu port) + * Reference: Section 1.1 Switch datasheet-3 + * + * This is optional settings may be needed on some boards + * for PHY<->magnetics h/w tuning + */ +static int mv88361xx_reverse_mdipn(struct mv88e61xx_config *swconfig, u32 prt) +{ + char *name = swconfig->name; + u16 reg; + + if (swconfig->mdip != MV88E61XX_MDIP_REVERSE) + return 0; + + reg = 0x0f; /*Reverse MDIP/N[3:0] bits */ + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); + WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, + MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | + 1 << MV88E61XX_MODE_OFST | + 1 << MV88E61XX_OP_OFST | + prt << MV88E61XX_ADDR_OFST | 20)); + + if (mv88e61xx_busychk(name)) + return -1; + + return 0; +} + +/* + * Marvell 88E61XX Switch initialization + */ +int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig) +{ + u32 prt; + u16 reg; + char *idstr; + char *name = swconfig->name; + + if (miiphy_set_current_dev(name)) { + printf("%s failed\n", __FUNCTION__); + return -1; + } + + if (!(swconfig->cpuport & ((1 << 4) | (1 << 5)))) { + swconfig->cpuport = (1 << 5); + printf("Invalid cpu port config, using default port5\n"); + } + + RD_PHY(name, MV88E61XX_PRT_OFST, PHY_PHYIDR2, ®); + switch (reg &= 0xfff0) { + case 0x1610: + idstr = "88E6161"; + break; + case 0x1650: + idstr = "88E6165"; + break; + case 0x1210: + idstr = "88E6123"; + /* ports 2,3,4 not available */ + swconfig->ports_enabled &= 0x023; + break; + default: + /* Could not detect switch id */ + idstr = "88E61??"; + break; + } + + /* Port based VLANs configuration */ + if ((swconfig->vlancfg == MV88E61XX_VLANCFG_DEFAULT) + || (swconfig->vlancfg == MV88E61XX_VLANCFG_ROUTER)) + mv88e61xx_port_vlan_config(swconfig, MV88E61XX_MAX_PORTS_NUM, + MV88E61XX_PRT_OFST); + else { + printf("Unsupported mode %s failed\n", __FUNCTION__); + return -1; + } + + if (swconfig->rgmii_delay == MV88E61XX_RGMII_DELAY_EN) { + /* + * Enable RGMII delay on Tx and Rx for CPU port + * Ref: sec 9.5 of chip datasheet-02 + */ + WR_PHY(name, MV88E61XX_PRT_OFST + 5, + MV88E61XX_RGMII_TIMECTRL_REG, 0x18); + WR_PHY(name, MV88E61XX_PRT_OFST + 4, + MV88E61XX_RGMII_TIMECTRL_REG, 0xc1e7); + } + + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { + if (!((1 << prt) & swconfig->cpuport)) { + + if (mv88361xx_led_init(swconfig, prt)) + return -1; + if (mv88361xx_reverse_mdipn(swconfig, prt)) + return -1; + if (mv88361xx_powerup(swconfig, prt)) + return -1; + } + + /*Program port state */ + RD_PHY(name, MV88E61XX_PRT_OFST + prt, + MV88E61XX_PRT_CTRL_REG, ®); + WR_PHY(name, MV88E61XX_PRT_OFST + prt, + MV88E61XX_PRT_CTRL_REG, + reg | (swconfig->portstate & 0x03)); + } + + printf("%s Initialized on %s\n", idstr, name); + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/phy/mv88e61xx.h b/roms/u-boot-sam460ex/drivers/net/phy/mv88e61xx.h new file mode 100644 index 000000000..57762b686 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/phy/mv88e61xx.h @@ -0,0 +1,62 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Prafulla Wadaskar <prafulla@marvell.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _MV88E61XX_H +#define _MV88E61XX_H + +#include <miiphy.h> + +#define MV88E61XX_CPU_PORT 0x5 +#define MV88E61XX_MAX_PORTS_NUM 0x6 + +#define MV88E61XX_PHY_TIMEOUT 100000 + +#define MV88E61XX_PRT_STS_REG 0x1 +#define MV88E61XX_PRT_CTRL_REG 0x4 +#define MV88E61XX_PRT_VMAP_REG 0x6 +#define MV88E61XX_PRT_VID_REG 0x7 + +#define MV88E61XX_PRT_OFST 0x10 +#define MV88E61XX_PHY_CMD 0x18 +#define MV88E61XX_PHY_DATA 0x19 +#define MV88E61XX_RGMII_TIMECTRL_REG 0x1A +#define MV88E61XX_GLB2REG_DEVADR 0x1C + +#define MV88E61XX_BUSY_OFST 15 +#define MV88E61XX_MODE_OFST 12 +#define MV88E61XX_OP_OFST 10 +#define MV88E61XX_ADDR_OFST 5 + +#ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE +static int mv88e61xx_busychk_multic(char *name, u32 devaddr); +static void mv88e61xx_wr_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 data); +static void mv88e61xx_rd_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 * data); +#define WR_PHY mv88e61xx_wr_phy +#define RD_PHY mv88e61xx_rd_phy +#else +#define WR_PHY miiphy_write +#define RD_PHY miiphy_read +#endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ + +#endif /* _MV88E61XX_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/plb2800_eth.c b/roms/u-boot-sam460ex/drivers/net/plb2800_eth.c new file mode 100644 index 000000000..d799c7382 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/plb2800_eth.c @@ -0,0 +1,391 @@ +/* + * PLB2800 internal switch ethernet driver. + * + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/addrspace.h> + + +#define NUM_RX_DESC PKTBUFSRX +#define TOUT_LOOP 1000000 + +#define LONG_REF(addr) (*((volatile unsigned long*)addr)) + +#define CMAC_CRX_CTRL LONG_REF(0xb800c870) +#define CMAC_CTX_CTRL LONG_REF(0xb800c874) +#define SYS_MAC_ADDR_0 LONG_REF(0xb800c878) +#define SYS_MAC_ADDR_1 LONG_REF(0xb800c87c) +#define MIPS_H_MASK LONG_REF(0xB800C810) + +#define MA_LEARN LONG_REF(0xb8008004) +#define DA_LOOKUP LONG_REF(0xb8008008) + +#define CMAC_CRX_CTRL_PD 0x00000001 +#define CMAC_CRX_CTRL_CG 0x00000002 +#define CMAC_CRX_CTRL_PL_SHIFT 2 +#define CMAC_CRIT 0x0 +#define CMAC_NON_CRIT 0x1 +#define MBOX_STAT_ID_SHF 28 +#define MBOX_STAT_CP 0x80000000 +#define MBOX_STAT_MB 0x00000001 +#define EN_MA_LEARN 0x02000000 +#define EN_DA_LKUP 0x01000000 +#define MA_DEST_SHF 11 +#define DA_DEST_SHF 11 +#define DA_STATE_SHF 19 +#define TSTAMP_MS 0x00000000 +#define SW_H_MBOX4_MASK 0x08000000 +#define SW_H_MBOX3_MASK 0x04000000 +#define SW_H_MBOX2_MASK 0x02000000 +#define SW_H_MBOX1_MASK 0x01000000 + +typedef volatile struct { + unsigned int stat; + unsigned int cmd; + unsigned int cnt; + unsigned int adr; +} mailbox_t; + +#define MBOX_REG(mb) ((mailbox_t*)(0xb800c830+(mb<<4))) + +typedef volatile struct { + unsigned int word0; + unsigned int word1; + unsigned int word2; +} mbhdr_t; + +#define MBOX_MEM(mb) ((void*)(0xb800a000+((3-mb)<<11))) + + +static int plb2800_eth_init(struct eth_device *dev, bd_t * bis); +static int plb2800_eth_send(struct eth_device *dev, volatile void *packet, + int length); +static int plb2800_eth_recv(struct eth_device *dev); +static void plb2800_eth_halt(struct eth_device *dev); + +static void plb2800_set_mac_addr(struct eth_device *dev, unsigned char * addr); +static unsigned char * plb2800_get_mac_addr(void); + +static int rx_new; +static int mac_addr_set = 0; + + +int plb2800_eth_initialize(bd_t * bis) +{ + struct eth_device *dev; + ulong temp; + +#ifdef DEBUG + printf("Entered plb2800_eth_initialize()\n"); +#endif + + if (!(dev = (struct eth_device *) malloc (sizeof *dev))) + { + printf("Failed to allocate memory\n"); + return -1; + } + memset(dev, 0, sizeof(*dev)); + + sprintf(dev->name, "PLB2800 Switch"); + dev->init = plb2800_eth_init; + dev->halt = plb2800_eth_halt; + dev->send = plb2800_eth_send; + dev->recv = plb2800_eth_recv; + + eth_register(dev); + + /* bug fix */ + *(ulong *)0xb800e800 = 0x838; + + /* Set MBOX ownership */ + temp = CMAC_CRIT << MBOX_STAT_ID_SHF; + MBOX_REG(0)->stat = temp; + MBOX_REG(1)->stat = temp; + + temp = CMAC_NON_CRIT << MBOX_STAT_ID_SHF; + MBOX_REG(2)->stat = temp; + MBOX_REG(3)->stat = temp; + + plb2800_set_mac_addr(dev, plb2800_get_mac_addr()); + + /* Disable all Mbox interrupt */ + temp = MIPS_H_MASK; + temp &= ~ (SW_H_MBOX1_MASK | SW_H_MBOX2_MASK | SW_H_MBOX3_MASK | SW_H_MBOX4_MASK) ; + MIPS_H_MASK = temp; + +#ifdef DEBUG + printf("Leaving plb2800_eth_initialize()\n"); +#endif + + return 0; +} + +static int plb2800_eth_init(struct eth_device *dev, bd_t * bis) +{ +#ifdef DEBUG + printf("Entering plb2800_eth_init()\n"); +#endif + + plb2800_set_mac_addr(dev, dev->enetaddr); + + rx_new = 0; + +#ifdef DEBUG + printf("Leaving plb2800_eth_init()\n"); +#endif + + return 0; +} + + +static int plb2800_eth_send(struct eth_device *dev, volatile void *packet, + int length) +{ + int i; + int res = -1; + u32 temp; + mailbox_t * mb = MBOX_REG(0); + char * mem = MBOX_MEM(0); + +#ifdef DEBUG + printf("Entered plb2800_eth_send()\n"); +#endif + + if (length <= 0) + { + printf ("%s: bad packet size: %d\n", dev->name, length); + goto Done; + } + + if (length < 64) + { + length = 64; + } + + temp = CMAC_CRX_CTRL_CG | ((length + 4) << CMAC_CRX_CTRL_PL_SHIFT); + +#ifdef DEBUG + printf("0 mb->stat = 0x%x\n", mb->stat); +#endif + + for(i = 0; mb->stat & (MBOX_STAT_CP | MBOX_STAT_MB); i++) + { + if (i >= TOUT_LOOP) + { + printf("%s: tx buffer not ready\n", dev->name); + printf("1 mb->stat = 0x%x\n", mb->stat); + goto Done; + } + } + + /* For some strange reason, memcpy doesn't work, here! + */ + do + { + int words = (length >> 2) + 1; + unsigned int* dst = (unsigned int*)(mem); + unsigned int* src = (unsigned int*)(packet); + for (i = 0; i < words; i++) + { + *dst = *src; + dst++; + src++; + }; + } while(0); + + CMAC_CRX_CTRL = temp; + mb->cmd = MBOX_STAT_CP; + +#ifdef DEBUG + printf("2 mb->stat = 0x%x\n", mb->stat); +#endif + + res = length; +Done: + +#ifdef DEBUG + printf("Leaving plb2800_eth_send()\n"); +#endif + + return res; +} + + +static int plb2800_eth_recv(struct eth_device *dev) +{ + int length = 0; + mailbox_t * mbox = MBOX_REG(3); + unsigned char * hdr = MBOX_MEM(3); + unsigned int stat; + +#ifdef DEBUG + printf("Entered plb2800_eth_recv()\n"); +#endif + + for (;;) + { + stat = mbox->stat; + + if (!(stat & MBOX_STAT_CP)) + { + break; + } + + length = ((*(hdr + 6) & 0x3f) << 8) + *(hdr + 7); + memcpy((void *)NetRxPackets[rx_new], hdr + 12, length); + + stat &= ~MBOX_STAT_CP; + mbox->stat = stat; +#ifdef DEBUG + { + int i; + for (i=0;i<length - 4;i++) + { + if (i % 16 == 0) printf("\n%04x: ", i); + printf("%02X ", NetRxPackets[rx_new][i]); + } + printf("\n"); + } +#endif + + if (length) + { +#ifdef DEBUG + printf("Received %d bytes\n", length); +#endif + NetReceive((void*)(NetRxPackets[rx_new]), + length - 4); + } + else + { +#if 1 + printf("Zero length!!!\n"); +#endif + } + + rx_new = (rx_new + 1) % NUM_RX_DESC; + } + +#ifdef DEBUG + printf("Leaving plb2800_eth_recv()\n"); +#endif + + return length; +} + + +static void plb2800_eth_halt(struct eth_device *dev) +{ +#ifdef DEBUG + printf("Entered plb2800_eth_halt()\n"); +#endif + +#ifdef DEBUG + printf("Leaving plb2800_eth_halt()\n"); +#endif +} + +static void plb2800_set_mac_addr(struct eth_device *dev, unsigned char * addr) +{ + char packet[60]; + ulong temp; + int ix; + + if (mac_addr_set || + NULL == addr || memcmp(addr, "\0\0\0\0\0\0", 6) == 0) + { + return; + } + + /* send one packet through CPU port + * in order to learn system MAC address + */ + + /* Set DA_LOOKUP register */ + temp = EN_MA_LEARN | (0 << DA_STATE_SHF) | (63 << DA_DEST_SHF); + DA_LOOKUP = temp; + + /* Set MA_LEARN register */ + temp = 50 << MA_DEST_SHF; /* static entry */ + MA_LEARN = temp; + + /* set destination address */ + for (ix=0;ix<6;ix++) + packet[ix] = 0xff; + + /* set source address = system MAC address */ + for (ix=0;ix<6;ix++) + packet[6+ix] = addr[ix]; + + /* set type field */ + packet[12]=0xaa; + packet[13]=0x55; + + /* set data field */ + for(ix=14;ix<60;ix++) + packet[ix] = 0x00; + +#ifdef DEBUG + for (ix=0;ix<6;ix++) + printf("mac_addr[%d]=%02X\n", ix, (unsigned char)packet[6+ix]); +#endif + + /* set one packet */ + plb2800_eth_send(dev, packet, sizeof(packet)); + + /* delay for a while */ + for(ix=0;ix<65535;ix++) + temp = ~temp; + + /* Set CMAC_CTX_CTRL register */ + temp = TSTAMP_MS; /* no autocast */ + CMAC_CTX_CTRL = temp; + + /* Set DA_LOOKUP register */ + temp = EN_DA_LKUP; + DA_LOOKUP = temp; + + mac_addr_set = 1; +} + +static unsigned char * plb2800_get_mac_addr(void) +{ + static unsigned char addr[6]; + char *tmp, *end; + int i; + + tmp = getenv ("ethaddr"); + if (NULL == tmp) return NULL; + + for (i=0; i<6; i++) { + addr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; + if (tmp) + tmp = (*end) ? end+1 : end; + } + + return addr; +} diff --git a/roms/u-boot-sam460ex/drivers/net/rtl8019.c b/roms/u-boot-sam460ex/drivers/net/rtl8019.c new file mode 100644 index 000000000..f516afe6b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/rtl8019.c @@ -0,0 +1,271 @@ +/* + * Realtek 8019AS Ethernet + * (C) Copyright 2002-2003 + * Xue Ligong(lgxue@hotmail.com),Wang Kehao, ESLAB, whut.edu.cn + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This code works in 8bit mode. + * If you need to work in 16bit mode, PLS change it! + */ + +#include <common.h> +#include <command.h> +#include "rtl8019.h" +#include <net.h> + +/* packet page register access functions */ + +static unsigned char get_reg (unsigned int regno) +{ + return (*(unsigned char *) regno); +} + +static void put_reg (unsigned int regno, unsigned char val) +{ + *(volatile unsigned char *) regno = val; +} + +static void eth_reset (void) +{ + unsigned char ucTemp; + + /* reset NIC */ + ucTemp = get_reg (RTL8019_RESET); + put_reg (RTL8019_RESET, ucTemp); + put_reg (RTL8019_INTERRUPTSTATUS, 0xff); + udelay (2000); /* wait for 2ms */ +} + +void rtl8019_get_enetaddr (uchar * addr) +{ + unsigned char i; + unsigned char temp; + + eth_reset (); + + put_reg (RTL8019_COMMAND, RTL8019_REMOTEDMARD); + put_reg (RTL8019_DATACONFIGURATION, 0x48); + put_reg (RTL8019_REMOTESTARTADDRESS0, 0x00); + put_reg (RTL8019_REMOTESTARTADDRESS1, 0x00); + put_reg (RTL8019_REMOTEBYTECOUNT0, 12); + put_reg (RTL8019_REMOTEBYTECOUNT1, 0x00); + put_reg (RTL8019_COMMAND, RTL8019_REMOTEDMARD); + printf ("MAC: "); + for (i = 0; i < 6; i++) { + temp = get_reg (RTL8019_DMA_DATA); + *addr++ = temp; + temp = get_reg (RTL8019_DMA_DATA); + printf ("%x:", temp); + } + + while ((!get_reg (RTL8019_INTERRUPTSTATUS) & 0x40)); + printf ("\b \n"); + put_reg (RTL8019_REMOTEBYTECOUNT0, 0x00); + put_reg (RTL8019_REMOTEBYTECOUNT1, 0x00); + put_reg (RTL8019_COMMAND, RTL8019_PAGE0); +} + +void eth_halt (void) +{ + put_reg (RTL8019_COMMAND, 0x01); +} + +int eth_init (bd_t * bd) +{ + uchar enetaddr[6]; + eth_reset (); + put_reg (RTL8019_COMMAND, RTL8019_PAGE0STOP); + put_reg (RTL8019_DATACONFIGURATION, 0x48); + put_reg (RTL8019_REMOTEBYTECOUNT0, 0x00); + put_reg (RTL8019_REMOTEBYTECOUNT1, 0x00); + put_reg (RTL8019_RECEIVECONFIGURATION, 0x00); /*00; */ + put_reg (RTL8019_TRANSMITPAGE, RTL8019_TPSTART); + put_reg (RTL8019_TRANSMITCONFIGURATION, 0x02); + put_reg (RTL8019_PAGESTART, RTL8019_PSTART); + put_reg (RTL8019_BOUNDARY, RTL8019_PSTART); + put_reg (RTL8019_PAGESTOP, RTL8019_PSTOP); + put_reg (RTL8019_INTERRUPTSTATUS, 0xff); + put_reg (RTL8019_INTERRUPTMASK, 0x11); /*b; */ + put_reg (RTL8019_COMMAND, RTL8019_PAGE1STOP); + eth_getenv_enetaddr("ethaddr", enetaddr); + put_reg (RTL8019_PHYSICALADDRESS0, enetaddr[0]); + put_reg (RTL8019_PHYSICALADDRESS1, enetaddr[1]); + put_reg (RTL8019_PHYSICALADDRESS2, enetaddr[2]); + put_reg (RTL8019_PHYSICALADDRESS3, enetaddr[3]); + put_reg (RTL8019_PHYSICALADDRESS4, enetaddr[4]); + put_reg (RTL8019_PHYSICALADDRESS5, enetaddr[5]); + put_reg (RTL8019_MULTIADDRESS0, 0x00); + put_reg (RTL8019_MULTIADDRESS1, 0x00); + put_reg (RTL8019_MULTIADDRESS2, 0x00); + put_reg (RTL8019_MULTIADDRESS3, 0x00); + put_reg (RTL8019_MULTIADDRESS4, 0x00); + put_reg (RTL8019_MULTIADDRESS5, 0x00); + put_reg (RTL8019_MULTIADDRESS6, 0x00); + put_reg (RTL8019_MULTIADDRESS7, 0x00); + put_reg (RTL8019_CURRENT, RTL8019_PSTART); + put_reg (RTL8019_COMMAND, RTL8019_PAGE0); + put_reg (RTL8019_TRANSMITCONFIGURATION, 0xe0); /*58; */ + + return 0; +} + +static unsigned char nic_to_pc (void) +{ + unsigned char rec_head_status; + unsigned char next_packet_pointer; + unsigned char packet_length0; + unsigned char packet_length1; + unsigned short rxlen = 0; + unsigned int i = 4; + unsigned char current_point; + unsigned char *addr; + + /* + * The RTL8019's first 4B is packet status,page of next packet + * and packet length(2B).So we receive the fist 4B. + */ + put_reg (RTL8019_REMOTESTARTADDRESS1, get_reg (RTL8019_BOUNDARY)); + put_reg (RTL8019_REMOTESTARTADDRESS0, 0x00); + put_reg (RTL8019_REMOTEBYTECOUNT1, 0x00); + put_reg (RTL8019_REMOTEBYTECOUNT0, 0x04); + + put_reg (RTL8019_COMMAND, RTL8019_REMOTEDMARD); + + rec_head_status = get_reg (RTL8019_DMA_DATA); + next_packet_pointer = get_reg (RTL8019_DMA_DATA); + packet_length0 = get_reg (RTL8019_DMA_DATA); + packet_length1 = get_reg (RTL8019_DMA_DATA); + + put_reg (RTL8019_COMMAND, RTL8019_PAGE0); + /*Packet length is in two 8bit registers */ + rxlen = packet_length1; + rxlen = (((rxlen << 8) & 0xff00) + packet_length0); + rxlen -= 4; + + if (rxlen > PKTSIZE_ALIGN + PKTALIGN) + printf ("packet too big!\n"); + + /*Receive the packet */ + put_reg (RTL8019_REMOTESTARTADDRESS0, 0x04); + put_reg (RTL8019_REMOTESTARTADDRESS1, get_reg (RTL8019_BOUNDARY)); + + put_reg (RTL8019_REMOTEBYTECOUNT0, (rxlen & 0xff)); + put_reg (RTL8019_REMOTEBYTECOUNT1, ((rxlen >> 8) & 0xff)); + + + put_reg (RTL8019_COMMAND, RTL8019_REMOTEDMARD); + + for (addr = (unsigned char *) NetRxPackets[0], i = rxlen; i > 0; i--) + *addr++ = get_reg (RTL8019_DMA_DATA); + /* Pass the packet up to the protocol layers. */ + NetReceive (NetRxPackets[0], rxlen); + + while (!(get_reg (RTL8019_INTERRUPTSTATUS)) & 0x40); /* wait for the op. */ + + /* + * To test whether the packets are all received,get the + * location of current point + */ + put_reg (RTL8019_COMMAND, RTL8019_PAGE1); + current_point = get_reg (RTL8019_CURRENT); + put_reg (RTL8019_COMMAND, RTL8019_PAGE0); + put_reg (RTL8019_BOUNDARY, next_packet_pointer); + return current_point; +} + +/* Get a data block via Ethernet */ +extern int eth_rx (void) +{ + unsigned char temp, current_point; + + put_reg (RTL8019_COMMAND, RTL8019_PAGE0); + + while (1) { + temp = get_reg (RTL8019_INTERRUPTSTATUS); + + if (temp & 0x90) { + /*overflow */ + put_reg (RTL8019_COMMAND, RTL8019_PAGE0STOP); + udelay (2000); + put_reg (RTL8019_REMOTEBYTECOUNT0, 0); + put_reg (RTL8019_REMOTEBYTECOUNT1, 0); + put_reg (RTL8019_TRANSMITCONFIGURATION, 2); + do { + current_point = nic_to_pc (); + } while (get_reg (RTL8019_BOUNDARY) != current_point); + + put_reg (RTL8019_TRANSMITCONFIGURATION, 0xe0); + } + + if (temp & 0x1) { + /*packet received */ + do { + put_reg (RTL8019_INTERRUPTSTATUS, 0x01); + current_point = nic_to_pc (); + } while (get_reg (RTL8019_BOUNDARY) != current_point); + } + + if (!(temp & 0x1)) + return 0; + /* done and exit. */ + } +} + +/* Send a data block via Ethernet. */ +extern int eth_send (volatile void *packet, int length) +{ + volatile unsigned char *p; + unsigned int pn; + + pn = length; + p = (volatile unsigned char *) packet; + + while (get_reg (RTL8019_COMMAND) == RTL8019_TRANSMIT); + + put_reg (RTL8019_REMOTESTARTADDRESS0, 0); + put_reg (RTL8019_REMOTESTARTADDRESS1, RTL8019_TPSTART); + put_reg (RTL8019_REMOTEBYTECOUNT0, (pn & 0xff)); + put_reg (RTL8019_REMOTEBYTECOUNT1, ((pn >> 8) & 0xff)); + + put_reg (RTL8019_COMMAND, RTL8019_REMOTEDMAWR); + while (pn > 0) { + put_reg (RTL8019_DMA_DATA, *p++); + pn--; + } + + pn = length; + + while (pn < 60) { /*Padding */ + put_reg (RTL8019_DMA_DATA, 0); + pn++; + } + + while (!(get_reg (RTL8019_INTERRUPTSTATUS)) & 0x40); + + put_reg (RTL8019_INTERRUPTSTATUS, 0x40); + put_reg (RTL8019_TRANSMITPAGE, RTL8019_TPSTART); + put_reg (RTL8019_TRANSMITBYTECOUNT0, (pn & 0xff)); + put_reg (RTL8019_TRANSMITBYTECOUNT1, ((pn >> 8 & 0xff))); + put_reg (RTL8019_COMMAND, RTL8019_TRANSMIT); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/rtl8019.h b/roms/u-boot-sam460ex/drivers/net/rtl8019.h new file mode 100644 index 000000000..ae5163c43 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/rtl8019.h @@ -0,0 +1,114 @@ +/* + * Realtek 8019AS Ethernet + * (C) Copyright 2002-2003 + * Xue Ligong(lgxue@hotmail.com),Wang Kehao, ESLAB, whut.edu.cn + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * This code works in 8bit mode. + * If you need to work in 16bit mode, PLS change it! + */ + +#include <asm/types.h> +#include <config.h> + +#ifdef CONFIG_DRIVER_RTL8019 + +#define RTL8019_REG_00 (RTL8019_BASE + 0x00) +#define RTL8019_REG_01 (RTL8019_BASE + 0x01) +#define RTL8019_REG_02 (RTL8019_BASE + 0x02) +#define RTL8019_REG_03 (RTL8019_BASE + 0x03) +#define RTL8019_REG_04 (RTL8019_BASE + 0x04) +#define RTL8019_REG_05 (RTL8019_BASE + 0x05) +#define RTL8019_REG_06 (RTL8019_BASE + 0x06) +#define RTL8019_REG_07 (RTL8019_BASE + 0x07) +#define RTL8019_REG_08 (RTL8019_BASE + 0x08) +#define RTL8019_REG_09 (RTL8019_BASE + 0x09) +#define RTL8019_REG_0a (RTL8019_BASE + 0x0a) +#define RTL8019_REG_0b (RTL8019_BASE + 0x0b) +#define RTL8019_REG_0c (RTL8019_BASE + 0x0c) +#define RTL8019_REG_0d (RTL8019_BASE + 0x0d) +#define RTL8019_REG_0e (RTL8019_BASE + 0x0e) +#define RTL8019_REG_0f (RTL8019_BASE + 0x0f) +#define RTL8019_REG_10 (RTL8019_BASE + 0x10) +#define RTL8019_REG_1f (RTL8019_BASE + 0x1f) + +#define RTL8019_COMMAND RTL8019_REG_00 +#define RTL8019_PAGESTART RTL8019_REG_01 +#define RTL8019_PAGESTOP RTL8019_REG_02 +#define RTL8019_BOUNDARY RTL8019_REG_03 +#define RTL8019_TRANSMITSTATUS RTL8019_REG_04 +#define RTL8019_TRANSMITPAGE RTL8019_REG_04 +#define RTL8019_TRANSMITBYTECOUNT0 RTL8019_REG_05 +#define RTL8019_NCR RTL8019_REG_05 +#define RTL8019_TRANSMITBYTECOUNT1 RTL8019_REG_06 +#define RTL8019_INTERRUPTSTATUS RTL8019_REG_07 +#define RTL8019_CURRENT RTL8019_REG_07 +#define RTL8019_REMOTESTARTADDRESS0 RTL8019_REG_08 +#define RTL8019_CRDMA0 RTL8019_REG_08 +#define RTL8019_REMOTESTARTADDRESS1 RTL8019_REG_09 +#define RTL8019_CRDMA1 RTL8019_REG_09 +#define RTL8019_REMOTEBYTECOUNT0 RTL8019_REG_0a +#define RTL8019_REMOTEBYTECOUNT1 RTL8019_REG_0b +#define RTL8019_RECEIVESTATUS RTL8019_REG_0c +#define RTL8019_RECEIVECONFIGURATION RTL8019_REG_0c +#define RTL8019_TRANSMITCONFIGURATION RTL8019_REG_0d +#define RTL8019_FAE_TALLY RTL8019_REG_0d +#define RTL8019_DATACONFIGURATION RTL8019_REG_0e +#define RTL8019_CRC_TALLY RTL8019_REG_0e +#define RTL8019_INTERRUPTMASK RTL8019_REG_0f +#define RTL8019_MISS_PKT_TALLY RTL8019_REG_0f +#define RTL8019_PHYSICALADDRESS0 RTL8019_REG_01 +#define RTL8019_PHYSICALADDRESS1 RTL8019_REG_02 +#define RTL8019_PHYSICALADDRESS2 RTL8019_REG_03 +#define RTL8019_PHYSICALADDRESS3 RTL8019_REG_04 +#define RTL8019_PHYSICALADDRESS4 RTL8019_REG_05 +#define RTL8019_PHYSICALADDRESS5 RTL8019_REG_06 +#define RTL8019_MULTIADDRESS0 RTL8019_REG_08 +#define RTL8019_MULTIADDRESS1 RTL8019_REG_09 +#define RTL8019_MULTIADDRESS2 RTL8019_REG_0a +#define RTL8019_MULTIADDRESS3 RTL8019_REG_0b +#define RTL8019_MULTIADDRESS4 RTL8019_REG_0c +#define RTL8019_MULTIADDRESS5 RTL8019_REG_0d +#define RTL8019_MULTIADDRESS6 RTL8019_REG_0e +#define RTL8019_MULTIADDRESS7 RTL8019_REG_0f +#define RTL8019_DMA_DATA RTL8019_REG_10 +#define RTL8019_RESET RTL8019_REG_1f + +#define RTL8019_PAGE0 0x22 +#define RTL8019_PAGE1 0x62 +#define RTL8019_PAGE0DMAWRITE 0x12 +#define RTL8019_PAGE2DMAWRITE 0x92 +#define RTL8019_REMOTEDMAWR 0x12 +#define RTL8019_REMOTEDMARD 0x0A +#define RTL8019_ABORTDMAWR 0x32 +#define RTL8019_ABORTDMARD 0x2A +#define RTL8019_PAGE0STOP 0x21 +#define RTL8019_PAGE1STOP 0x61 +#define RTL8019_TRANSMIT 0x26 +#define RTL8019_TXINPROGRESS 0x04 +#define RTL8019_SEND 0x1A + +#define RTL8019_PSTART 0x4c +#define RTL8019_PSTOP 0x80 +#define RTL8019_TPSTART 0x40 + +#endif /*end of CONFIG_DRIVER_RTL8019*/ diff --git a/roms/u-boot-sam460ex/drivers/net/rtl8139.c b/roms/u-boot-sam460ex/drivers/net/rtl8139.c new file mode 100644 index 000000000..db8a727c8 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/rtl8139.c @@ -0,0 +1,545 @@ +/* + * rtl8139.c : U-Boot driver for the RealTek RTL8139 + * + * Masami Komiya (mkomiya@sonare.it) + * + * Most part is taken from rtl8139.c of etherboot + * + */ + +/* rtl8139.c - etherboot driver for the Realtek 8139 chipset + + ported from the linux driver written by Donald Becker + by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999 + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + changes to the original driver: + - removed support for interrupts, switching to polling mode (yuck!) + - removed support for the 8129 chip (external MII) + +*/ + +/*********************************************************************/ +/* Revision History */ +/*********************************************************************/ + +/* + 28 Dec 2002 ken_yap@users.sourceforge.net (Ken Yap) + Put in virt_to_bus calls to allow Etherboot relocation. + + 06 Apr 2001 ken_yap@users.sourceforge.net (Ken Yap) + Following email from Hyun-Joon Cha, added a disable routine, otherwise + NIC remains live and can crash the kernel later. + + 4 Feb 2000 espenlaub@informatik.uni-ulm.de (Klaus Espenlaub) + Shuffled things around, removed the leftovers from the 8129 support + that was in the Linux driver and added a bit more 8139 definitions. + Moved the 8K receive buffer to a fixed, available address outside the + 0x98000-0x9ffff range. This is a bit of a hack, but currently the only + way to make room for the Etherboot features that need substantial amounts + of code like the ANSI console support. Currently the buffer is just below + 0x10000, so this even conforms to the tagged boot image specification, + which reserves the ranges 0x00000-0x10000 and 0x98000-0xA0000. My + interpretation of this "reserved" is that Etherboot may do whatever it + likes, as long as its environment is kept intact (like the BIOS + variables). Hopefully fixed rtl_poll() once and for all. The symptoms + were that if Etherboot was left at the boot menu for several minutes, the + first eth_poll failed. Seems like I am the only person who does this. + First of all I fixed the debugging code and then set out for a long bug + hunting session. It took me about a week full time work - poking around + various places in the driver, reading Don Becker's and Jeff Garzik's Linux + driver and even the FreeBSD driver (what a piece of crap!) - and + eventually spotted the nasty thing: the transmit routine was acknowledging + each and every interrupt pending, including the RxOverrun and RxFIFIOver + interrupts. This confused the RTL8139 thoroughly. It destroyed the + Rx ring contents by dumping the 2K FIFO contents right where we wanted to + get the next packet. Oh well, what fun. + + 18 Jan 2000 mdc@thinguin.org (Marty Connor) + Drastically simplified error handling. Basically, if any error + in transmission or reception occurs, the card is reset. + Also, pointed all transmit descriptors to the same buffer to + save buffer space. This should decrease driver size and avoid + corruption because of exceeding 32K during runtime. + + 28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de) + rtl_poll was quite broken: it used the RxOK interrupt flag instead + of the RxBufferEmpty flag which often resulted in very bad + transmission performace - below 1kBytes/s. + +*/ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> + +#define RTL_TIMEOUT 100000 + +#define ETH_FRAME_LEN 1514 +#define ETH_ALEN 6 +#define ETH_ZLEN 60 + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ +#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ +#define TX_DMA_BURST 4 /* Calculate as 16<<val. */ +#define NUM_TX_DESC 4 /* Number of Tx descriptor registers. */ +#define TX_BUF_SIZE ETH_FRAME_LEN /* FCS is added by the chip */ +#define RX_BUF_LEN_IDX 0 /* 0, 1, 2 is allowed - 8,16,32K rx buffer */ +#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) + +#undef DEBUG_TX +#undef DEBUG_RX + +#define currticks() get_timer(0) +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0=0, /* Ethernet hardware address. */ + MAR0=8, /* Multicast filter. */ + TxStatus0=0x10, /* Transmit status (four 32bit registers). */ + TxAddr0=0x20, /* Tx descriptors (also four 32bit). */ + RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36, + ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A, + IntrMask=0x3C, IntrStatus=0x3E, + TxConfig=0x40, RxConfig=0x44, + Timer=0x48, /* general-purpose counter. */ + RxMissed=0x4C, /* 24 bits valid, write clears. */ + Cfg9346=0x50, Config0=0x51, Config1=0x52, + TimerIntrReg=0x54, /* intr if gp counter reaches this value */ + MediaStatus=0x58, + Config3=0x59, + MultiIntr=0x5C, + RevisionID=0x5E, /* revision of the RTL8139 chip */ + TxSummary=0x60, + MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, + NWayExpansion=0x6A, + DisconnectCnt=0x6C, FalseCarrierCnt=0x6E, + NWayTestReg=0x70, + RxCnt=0x72, /* packet received counter */ + CSCR=0x74, /* chip status and configuration register */ + PhyParm1=0x78,TwisterParm=0x7c,PhyParm2=0x80, /* undocumented */ + /* from 0x84 onwards are a number of power management/wakeup frame + * definitions we will probably never need to know about. */ +}; + +enum ChipCmdBits { + CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, }; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr=0x8000, PCSTimeout=0x4000, CableLenChange= 0x2000, + RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10, + TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01, +}; +enum TxStatusBits { + TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000, + TxOutOfWindow=0x20000000, TxAborted=0x40000000, + TxCarrierLost=0x80000000, +}; +enum RxStatusBits { + RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000, + RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004, + RxBadAlign=0x0002, RxStatusOK=0x0001, +}; + +enum MediaStatusBits { + MSRTxFlowEnable=0x80, MSRRxFlowEnable=0x40, MSRSpeed10=0x08, + MSRLinkFail=0x04, MSRRxPauseFlag=0x02, MSRTxPauseFlag=0x01, +}; + +enum MIIBMCRBits { + BMCRReset=0x8000, BMCRSpeed100=0x2000, BMCRNWayEnable=0x1000, + BMCRRestartNWay=0x0200, BMCRDuplex=0x0100, +}; + +enum CSCRBits { + CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800, + CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0, + CSCR_LinkDownCmd=0x0f3c0, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + RxCfgWrap=0x80, + AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08, + AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01, +}; + +static int ioaddr; +static unsigned int cur_rx,cur_tx; + +/* The RTL8139 can only transmit from a contiguous, aligned memory block. */ +static unsigned char tx_buffer[TX_BUF_SIZE] __attribute__((aligned(4))); +static unsigned char rx_ring[RX_BUF_LEN+16] __attribute__((aligned(4))); + +static int rtl8139_probe(struct eth_device *dev, bd_t *bis); +static int read_eeprom(int location, int addr_len); +static void rtl_reset(struct eth_device *dev); +static int rtl_transmit(struct eth_device *dev, volatile void *packet, int length); +static int rtl_poll(struct eth_device *dev); +static void rtl_disable(struct eth_device *dev); +#ifdef CONFIG_MCAST_TFTP/* This driver already accepts all b/mcast */ +static int rtl_bcast_addr (struct eth_device *dev, u8 bcast_mac, u8 set) +{ + return (0); +} +#endif + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139}, + {PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_8139}, + {} +}; + +int rtl8139_initialize(bd_t *bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase; + int idx=0; + + while(1){ + /* Find RTL8139 */ + if ((devno = pci_find_devices(supported, idx++)) < 0) + break; + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + iobase &= ~0xf; + + debug ("rtl8139: REALTEK RTL8139 @0x%x\n", iobase); + + dev = (struct eth_device *)malloc(sizeof *dev); + + sprintf (dev->name, "RTL8139#%d", card_number); + + dev->priv = (void *) devno; + dev->iobase = (int)bus_to_phys(iobase); + dev->init = rtl8139_probe; + dev->halt = rtl_disable; + dev->send = rtl_transmit; + dev->recv = rtl_poll; +#ifdef CONFIG_MCAST_TFTP + dev->mcast = rtl_bcast_addr; +#endif + + eth_register (dev); + + card_number++; + + pci_write_config_byte (devno, PCI_LATENCY_TIMER, 0x20); + + udelay (10 * 1000); + } + + return card_number; +} + +static int rtl8139_probe(struct eth_device *dev, bd_t *bis) +{ + int i; + int speed10, fullduplex; + int addr_len; + unsigned short *ap = (unsigned short *)dev->enetaddr; + + ioaddr = dev->iobase; + + /* Bring the chip out of low-power mode. */ + outb(0x00, ioaddr + Config1); + + addr_len = read_eeprom(0,8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + *ap++ = le16_to_cpu (read_eeprom(i + 7, addr_len)); + + speed10 = inb(ioaddr + MediaStatus) & MSRSpeed10; + fullduplex = inw(ioaddr + MII_BMCR) & BMCRDuplex; + + rtl_reset(dev); + + if (inb(ioaddr + MediaStatus) & MSRLinkFail) { + printf("Cable not connected or other link failure\n"); + return -1 ; + } + + return 0; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* + Delay between EEPROM clock transitions. + No extra delay is needed with 33MHz PCI, but 66MHz may change this. +*/ + +#define eeprom_delay() inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +static int read_eeprom(int location, int addr_len) +{ + int i; + unsigned int retval = 0; + long ee_addr = ioaddr + Cfg9346; + int read_cmd = location | (EE_READ_CMD << addr_len); + + outb(EE_ENB & ~EE_CS, ee_addr); + outb(EE_ENB, ee_addr); + eeprom_delay(); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outb(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outb(EE_ENB, ee_addr); + eeprom_delay(); + + for (i = 16; i > 0; i--) { + outb(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); + outb(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outb(~EE_CS, ee_addr); + eeprom_delay(); + return retval; +} + +static const unsigned int rtl8139_rx_config = + (RX_BUF_LEN_IDX << 11) | + (RX_FIFO_THRESH << 13) | + (RX_DMA_BURST << 8); + +static void set_rx_mode(struct eth_device *dev) { + unsigned int mc_filter[2]; + int rx_mode; + /* !IFF_PROMISC */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + + outl(rtl8139_rx_config | rx_mode, ioaddr + RxConfig); + + outl(mc_filter[0], ioaddr + MAR0 + 0); + outl(mc_filter[1], ioaddr + MAR0 + 4); +} + +static void rtl_reset(struct eth_device *dev) +{ + int i; + + outb(CmdReset, ioaddr + ChipCmd); + + cur_rx = 0; + cur_tx = 0; + + /* Give the chip 10ms to finish the reset. */ + for (i=0; i<100; ++i){ + if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) break; + udelay (100); /* wait 100us */ + } + + + for (i = 0; i < ETH_ALEN; i++) + outb(dev->enetaddr[i], ioaddr + MAC0 + i); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + outl((RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8), + ioaddr + RxConfig); /* accept no frames yet! */ + outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig); + + /* The Linux driver changes Config1 here to use a different LED pattern + * for half duplex or full/autodetect duplex (for full/autodetect, the + * outputs are TX/RX, Link10/100, FULL, while for half duplex it uses + * TX/RX, Link100, Link10). This is messy, because it doesn't match + * the inscription on the mounting bracket. It should not be changed + * from the configuration EEPROM default, because the card manufacturer + * should have set that to match the card. */ + +#ifdef DEBUG_RX + printf("rx ring address is %X\n",(unsigned long)rx_ring); +#endif + flush_cache((unsigned long)rx_ring, RX_BUF_LEN); + outl(phys_to_bus((int)rx_ring), ioaddr + RxBuf); + + /* If we add multicast support, the MAR0 register would have to be + * initialized to 0xffffffffffffffff (two 32 bit accesses). Etherboot + * only needs broadcast (for ARP/RARP/BOOTP/DHCP) and unicast. */ + + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + + outl(rtl8139_rx_config, ioaddr + RxConfig); + + /* Start the chip's Tx and Rx process. */ + outl(0, ioaddr + RxMissed); + + /* set_rx_mode */ + set_rx_mode(dev); + + /* Disable all known interrupts by setting the interrupt mask. */ + outw(0, ioaddr + IntrMask); +} + +static int rtl_transmit(struct eth_device *dev, volatile void *packet, int length) +{ + unsigned int status; + unsigned long txstatus; + unsigned int len = length; + int i = 0; + + ioaddr = dev->iobase; + + memcpy((char *)tx_buffer, (char *)packet, (int)length); + +#ifdef DEBUG_TX + printf("sending %d bytes\n", len); +#endif + + /* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4 + * bytes are sent automatically for the FCS, totalling to 64 bytes). */ + while (len < ETH_ZLEN) { + tx_buffer[len++] = '\0'; + } + + flush_cache((unsigned long)tx_buffer, length); + outl(phys_to_bus((int)tx_buffer), ioaddr + TxAddr0 + cur_tx*4); + outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len, + ioaddr + TxStatus0 + cur_tx*4); + + do { + status = inw(ioaddr + IntrStatus); + /* Only acknlowledge interrupt sources we can properly handle + * here - the RxOverflow/RxFIFOOver MUST be handled in the + * rtl_poll() function. */ + outw(status & (TxOK | TxErr | PCIErr), ioaddr + IntrStatus); + if ((status & (TxOK | TxErr | PCIErr)) != 0) break; + udelay(10); + } while (i++ < RTL_TIMEOUT); + + txstatus = inl(ioaddr + TxStatus0 + cur_tx*4); + + if (status & TxOK) { + cur_tx = (cur_tx + 1) % NUM_TX_DESC; +#ifdef DEBUG_TX + printf("tx done (%d ticks), status %hX txstatus %X\n", + to-currticks(), status, txstatus); +#endif + return length; + } else { +#ifdef DEBUG_TX + printf("tx timeout/error (%d usecs), status %hX txstatus %X\n", + 10*i, status, txstatus); +#endif + rtl_reset(dev); + + return 0; + } +} + +static int rtl_poll(struct eth_device *dev) +{ + unsigned int status; + unsigned int ring_offs; + unsigned int rx_size, rx_status; + int length=0; + + ioaddr = dev->iobase; + + if (inb(ioaddr + ChipCmd) & RxBufEmpty) { + return 0; + } + + status = inw(ioaddr + IntrStatus); + /* See below for the rest of the interrupt acknowledges. */ + outw(status & ~(RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); + +#ifdef DEBUG_RX + printf("rtl_poll: int %hX ", status); +#endif + + ring_offs = cur_rx % RX_BUF_LEN; + /* ring_offs is guaranteed being 4-byte aligned */ + rx_status = le32_to_cpu(*(unsigned int *)(rx_ring + ring_offs)); + rx_size = rx_status >> 16; + rx_status &= 0xffff; + + if ((rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) || + (rx_size < ETH_ZLEN) || (rx_size > ETH_FRAME_LEN + 4)) { + printf("rx error %hX\n", rx_status); + rtl_reset(dev); /* this clears all interrupts still pending */ + return 0; + } + + /* Received a good packet */ + length = rx_size - 4; /* no one cares about the FCS */ + if (ring_offs+4+rx_size-4 > RX_BUF_LEN) { + int semi_count = RX_BUF_LEN - ring_offs - 4; + unsigned char rxdata[RX_BUF_LEN]; + + memcpy(rxdata, rx_ring + ring_offs + 4, semi_count); + memcpy(&(rxdata[semi_count]), rx_ring, rx_size-4-semi_count); + + NetReceive(rxdata, length); +#ifdef DEBUG_RX + printf("rx packet %d+%d bytes", semi_count,rx_size-4-semi_count); +#endif + } else { + NetReceive(rx_ring + ring_offs + 4, length); +#ifdef DEBUG_RX + printf("rx packet %d bytes", rx_size-4); +#endif + } + flush_cache((unsigned long)rx_ring, RX_BUF_LEN); + + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + outw(cur_rx - 16, ioaddr + RxBufPtr); + /* See RTL8139 Programming Guide V0.1 for the official handling of + * Rx overflow situations. The document itself contains basically no + * usable information, except for a few exception handling rules. */ + outw(status & (RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); + return length; +} + +static void rtl_disable(struct eth_device *dev) +{ + int i; + + ioaddr = dev->iobase; + + /* reset the chip */ + outb(CmdReset, ioaddr + ChipCmd); + + /* Give the chip 10ms to finish the reset. */ + for (i=0; i<100; ++i){ + if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) break; + udelay (100); /* wait 100us */ + } +} diff --git a/roms/u-boot-sam460ex/drivers/net/rtl8169.c b/roms/u-boot-sam460ex/drivers/net/rtl8169.c new file mode 100644 index 000000000..e45d1a522 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/rtl8169.c @@ -0,0 +1,915 @@ +/* + * rtl8169.c : U-Boot driver for the RealTek RTL8169 + * + * Masami Komiya (mkomiya@sonare.it) + * + * Most part is taken from r8169.c of etherboot + * + */ + +/************************************************************************** +* r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit +* Written 2003 by Timothy Legge <tlegge@rogers.com> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Portions of this code based on: +* r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver +* for Linux kernel 2.4.x. +* +* Written 2002 ShuChen <shuchen@realtek.com.tw> +* See Linux Driver for full information +* +* Linux Driver Version 1.27a, 10.02.2002 +* +* Thanks to: +* Jean Chen of RealTek Semiconductor Corp. for +* providing the evaluation NIC used to develop +* this driver. RealTek's support for Etherboot +* is appreciated. +* +* REVISION HISTORY: +* ================ +* +* v1.0 11-26-2003 timlegge Initial port of Linux driver +* v1.5 01-17-2004 timlegge Initial driver output cleanup +* +* Indent Options: indent -kr -i8 +***************************************************************************/ +/* + * 26 August 2006 Mihai Georgian <u-boot@linuxnotincluded.org.uk> + * Modified to use le32_to_cpu and cpu_to_le32 properly + */ +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> + +#undef DEBUG_RTL8169 +#undef DEBUG_RTL8169_TX +#undef DEBUG_RTL8169_RX + +#define drv_version "v1.5" +#define drv_date "01-17-2004" + +static u32 ioaddr; + +/* Condensed operations for readability. */ +#define currticks() get_timer(0) + +/* media options */ +#define MAX_UNITS 8 +static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; + +/* MAC address length*/ +#define MAC_ADDR_LEN 6 + +/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/ +#define MAX_ETH_FRAME_SIZE 1536 + +#define TX_FIFO_THRESH 256 /* In bytes */ + +#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */ +#define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */ +#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ + +#define NUM_TX_DESC 1 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#define RX_BUF_SIZE 1536 /* Rx Buffer size */ +#define RX_BUF_LEN 8192 + +#define RTL_MIN_IO_SIZE 0x80 +#define TX_TIMEOUT (6*HZ) + +/* write/read MMIO register. Notice: {read,write}[wl] do the necessary swapping */ +#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg)) +#define RTL_R8(reg) readb (ioaddr + (reg)) +#define RTL_R16(reg) readw (ioaddr + (reg)) +#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) + +#define ETH_FRAME_LEN MAX_ETH_FRAME_SIZE +#define ETH_ALEN MAC_ADDR_LEN +#define ETH_ZLEN 60 + +#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, (pci_addr_t)a) +#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, (phys_addr_t)a) + +enum RTL8169_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, + FLASH = 0x30, + ERSR = 0x36, + ChipCmd = 0x37, + TxPoll = 0x38, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + RxMissed = 0x4C, + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + Config2 = 0x53, + Config3 = 0x54, + Config4 = 0x55, + Config5 = 0x56, + MultiIntr = 0x5C, + PHYAR = 0x60, + TBICSR = 0x64, + TBI_ANAR = 0x68, + TBI_LPAR = 0x6A, + PHYstatus = 0x6C, + RxMaxSize = 0xDA, + CPlusCmd = 0xE0, + RxDescStartAddrLow = 0xE4, + RxDescStartAddrHigh = 0xE8, + EarlyTxThres = 0xEC, + FuncEvent = 0xF0, + FuncEventMask = 0xF4, + FuncPresetState = 0xF8, + FuncForceEvent = 0xFC, +}; + +enum RTL8169_register_content { + /*InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x80, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + /*RxStatusDesc */ + RxRES = 0x00200000, + RxCRC = 0x00080000, + RxRUNT = 0x00100000, + RxRWT = 0x00400000, + + /*ChipCmdBits */ + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, + + /*Cfg9346Bits */ + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, + + /*rx_mode_bits */ + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, + + /*RxConfigBits */ + RxCfgFIFOShift = 13, + RxCfgDMAShift = 8, + + /*TxConfigBits */ + TxInterFrameGapShift = 24, + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + + /*rtl8169_PHYstatus */ + TBI_Enable = 0x80, + TxFlowCtrl = 0x40, + RxFlowCtrl = 0x20, + _1000bpsF = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LinkStatus = 0x02, + FullDup = 0x01, + + /*GIGABIT_PHY_registers */ + PHY_CTRL_REG = 0, + PHY_STAT_REG = 1, + PHY_AUTO_NEGO_REG = 4, + PHY_1000_CTRL_REG = 9, + + /*GIGABIT_PHY_REG_BIT */ + PHY_Restart_Auto_Nego = 0x0200, + PHY_Enable_Auto_Nego = 0x1000, + + /* PHY_STAT_REG = 1; */ + PHY_Auto_Nego_Comp = 0x0020, + + /* PHY_AUTO_NEGO_REG = 4; */ + PHY_Cap_10_Half = 0x0020, + PHY_Cap_10_Full = 0x0040, + PHY_Cap_100_Half = 0x0080, + PHY_Cap_100_Full = 0x0100, + + /* PHY_1000_CTRL_REG = 9; */ + PHY_Cap_1000_Full = 0x0200, + + PHY_Cap_Null = 0x0, + + /*_MediaType*/ + _10_Half = 0x01, + _10_Full = 0x02, + _100_Half = 0x04, + _100_Full = 0x08, + _1000_Full = 0x10, + + /*_TBICSRBit*/ + TBILinkOK = 0x02000000, +}; + +static struct { + const char *name; + u8 version; /* depend on RTL8169 docs */ + u32 RxConfigMask; /* should clear the bits supported by this chip */ +} rtl_chip_info[] = { + {"RTL-8169", 0x00, 0xff7e1880,}, + {"RTL-8169", 0x04, 0xff7e1880,}, + {"RTL-8169", 0x00, 0xff7e1880,}, + {"RTL-8169s/8110s", 0x02, 0xff7e1880,}, + {"RTL-8169s/8110s", 0x04, 0xff7e1880,}, + {"RTL-8169sb/8110sb", 0x10, 0xff7e1880,}, + {"RTL-8169sc/8110sc", 0x18, 0xff7e1880,}, + {"RTL-8168b/8111sb", 0x30, 0xff7e1880,}, + {"RTL-8168b/8111sb", 0x38, 0xff7e1880,}, + {"RTL-8101e", 0x34, 0xff7e1880,}, + {"RTL-8100e", 0x32, 0xff7e1880,}, +}; + +enum _DescStatusBit { + OWNbit = 0x80000000, + EORbit = 0x40000000, + FSbit = 0x20000000, + LSbit = 0x10000000, +}; + +struct TxDesc { + u32 status; + u32 vlan_tag; + u32 buf_addr; + u32 buf_Haddr; +}; + +struct RxDesc { + u32 status; + u32 vlan_tag; + u32 buf_addr; + u32 buf_Haddr; +}; + +/* Define the TX Descriptor */ +static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; +/* __attribute__ ((aligned(256))); */ + +/* Create a static buffer of size RX_BUF_SZ for each +TX Descriptor. All descriptors point to a +part of this buffer */ +static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; + +/* Define the RX Descriptor */ +static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256]; + /* __attribute__ ((aligned(256))); */ + +/* Create a static buffer of size RX_BUF_SZ for each +RX Descriptor All descriptors point to a +part of this buffer */ +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; + +struct rtl8169_private { + void *mmio_addr; /* memory map physical address */ + int chipset; + unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + unsigned long dirty_tx; + unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ + unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ + struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ + struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ + unsigned char *RxBufferRings; /* Index of Rx Buffer */ + unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */ + unsigned char *Tx_skbuff[NUM_TX_DESC]; +} tpx; + +static struct rtl8169_private *tpc; + +static const u16 rtl8169_intr_mask = + SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | + TxOK | RxErr | RxOK; +static const unsigned int rtl8169_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); + +static struct pci_device_id supported[] = { + {PCI_VENDOR_ID_REALTEK, 0x8167}, + {PCI_VENDOR_ID_REALTEK, 0x8169}, + {} +}; + +void mdio_write(int RegAddr, int value) +{ + int i; + + RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value); + udelay(1000); + + for (i = 2000; i > 0; i--) { + /* Check if the RTL8169 has completed writing to the specified MII register */ + if (!(RTL_R32(PHYAR) & 0x80000000)) { + break; + } else { + udelay(100); + } + } +} + +int mdio_read(int RegAddr) +{ + int i, value = -1; + + RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16); + udelay(1000); + + for (i = 2000; i > 0; i--) { + /* Check if the RTL8169 has completed retrieving data from the specified MII register */ + if (RTL_R32(PHYAR) & 0x80000000) { + value = (int) (RTL_R32(PHYAR) & 0xFFFF); + break; + } else { + udelay(100); + } + } + return value; +} + +static int rtl8169_init_board(struct eth_device *dev) +{ + int i; + u32 tmp; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + ioaddr = dev->iobase; + + /* Soft reset the chip. */ + RTL_W8(ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) + if ((RTL_R8(ChipCmd) & CmdReset) == 0) + break; + else + udelay(10); + + /* identify chip attached to board */ + tmp = RTL_R32(TxConfig); + tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24; + + for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--){ + if (tmp == rtl_chip_info[i].version) { + tpc->chipset = i; + goto match; + } + } + + /* if unknown chip, assume array element #0, original RTL-8169 in this case */ + printf("PCI device %s: unknown chip version, assuming RTL-8169\n", dev->name); + printf("PCI device: TxConfig = 0x%lX\n", (unsigned long) RTL_R32(TxConfig)); + tpc->chipset = 0; + +match: + return 0; +} + +/************************************************************************** +RECV - Receive a frame +***************************************************************************/ +static int rtl_recv(struct eth_device *dev) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + int cur_rx; + int length = 0; + +#ifdef DEBUG_RTL8169_RX + printf ("%s\n", __FUNCTION__); +#endif + ioaddr = dev->iobase; + + cur_rx = tpc->cur_rx; + flush_cache((unsigned long)&tpc->RxDescArray[cur_rx], + sizeof(struct RxDesc)); + if ((le32_to_cpu(tpc->RxDescArray[cur_rx].status) & OWNbit) == 0) { + if (!(le32_to_cpu(tpc->RxDescArray[cur_rx].status) & RxRES)) { + unsigned char rxdata[RX_BUF_LEN]; + length = (int) (le32_to_cpu(tpc->RxDescArray[cur_rx]. + status) & 0x00001FFF) - 4; + + memcpy(rxdata, tpc->RxBufferRing[cur_rx], length); + NetReceive(rxdata, length); + + if (cur_rx == NUM_RX_DESC - 1) + tpc->RxDescArray[cur_rx].status = + cpu_to_le32((OWNbit | EORbit) + RX_BUF_SIZE); + else + tpc->RxDescArray[cur_rx].status = + cpu_to_le32(OWNbit + RX_BUF_SIZE); + tpc->RxDescArray[cur_rx].buf_addr = + cpu_to_le32(bus_to_phys(tpc->RxBufferRing[cur_rx])); + flush_cache((unsigned long)tpc->RxBufferRing[cur_rx], + RX_BUF_SIZE); + } else { + puts("Error Rx"); + } + cur_rx = (cur_rx + 1) % NUM_RX_DESC; + tpc->cur_rx = cur_rx; + return 1; + + } else { + ushort sts = RTL_R8(IntrStatus); + RTL_W8(IntrStatus, sts & ~(TxErr | RxErr | SYSErr)); + udelay(100); /* wait */ + } + tpc->cur_rx = cur_rx; + return (0); /* initially as this is called to flush the input */ +} + +#define HZ 1000 +/************************************************************************** +SEND - Transmit a frame +***************************************************************************/ +static int rtl_send(struct eth_device *dev, volatile void *packet, int length) +{ + /* send the packet to destination */ + + u32 to; + u8 *ptxb; + int entry = tpc->cur_tx % NUM_TX_DESC; + u32 len = length; + int ret; + +#ifdef DEBUG_RTL8169_TX + int stime = currticks(); + printf ("%s\n", __FUNCTION__); + printf("sending %d bytes\n", len); +#endif + + ioaddr = dev->iobase; + + /* point to the current txb incase multiple tx_rings are used */ + ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE]; + memcpy(ptxb, (char *)packet, (int)length); + flush_cache((unsigned long)ptxb, length); + + while (len < ETH_ZLEN) + ptxb[len++] = '\0'; + + tpc->TxDescArray[entry].buf_Haddr = 0; + tpc->TxDescArray[entry].buf_addr = cpu_to_le32(bus_to_phys(ptxb)); + if (entry != (NUM_TX_DESC - 1)) { + tpc->TxDescArray[entry].status = + cpu_to_le32((OWNbit | FSbit | LSbit) | + ((len > ETH_ZLEN) ? len : ETH_ZLEN)); + } else { + tpc->TxDescArray[entry].status = + cpu_to_le32((OWNbit | EORbit | FSbit | LSbit) | + ((len > ETH_ZLEN) ? len : ETH_ZLEN)); + } + RTL_W8(TxPoll, 0x40); /* set polling bit */ + + tpc->cur_tx++; + to = currticks() + TX_TIMEOUT; + do { + flush_cache((unsigned long)&tpc->TxDescArray[entry], + sizeof(struct TxDesc)); + } while ((le32_to_cpu(tpc->TxDescArray[entry].status) & OWNbit) + && (currticks() < to)); /* wait */ + + if (currticks() >= to) { +#ifdef DEBUG_RTL8169_TX + puts ("tx timeout/error\n"); + printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); +#endif + ret = 0; + } else { +#ifdef DEBUG_RTL8169_TX + puts("tx done\n"); +#endif + ret = length; + } + /* Delay to make net console (nc) work properly */ + udelay(20); + return ret; +} + +static void rtl8169_set_rx_mode(struct eth_device *dev) +{ + u32 mc_filter[2]; /* Multicast hash filter */ + int rx_mode; + u32 tmp = 0; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + + /* IFF_ALLMULTI */ + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + + tmp = rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) & + rtl_chip_info[tpc->chipset].RxConfigMask); + + RTL_W32(RxConfig, tmp); + RTL_W32(MAR0 + 0, mc_filter[0]); + RTL_W32(MAR0 + 4, mc_filter[1]); +} + +static void rtl8169_hw_start(struct eth_device *dev) +{ + u32 i; + +#ifdef DEBUG_RTL8169 + int stime = currticks(); + printf ("%s\n", __FUNCTION__); +#endif + +#if 0 + /* Soft reset the chip. */ + RTL_W8(ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) { + if ((RTL_R8(ChipCmd) & CmdReset) == 0) + break; + else + udelay(10); + } +#endif + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + /* RTL-8169sb/8110sb or previous version */ + if (tpc->chipset <= 5) + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + /* For gigabit rtl8169 */ + RTL_W16(RxMaxSize, RxPacketMaxSize); + + /* Set Rx Config register */ + i = rtl8169_rx_config | (RTL_R32(RxConfig) & + rtl_chip_info[tpc->chipset].RxConfigMask); + RTL_W32(RxConfig, i); + + /* Set DMA burst size and Interframe Gap Time */ + RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); + + + tpc->cur_rx = 0; + + RTL_W32(TxDescStartAddrLow, bus_to_phys(tpc->TxDescArray)); + RTL_W32(TxDescStartAddrHigh, (unsigned long)0); + RTL_W32(RxDescStartAddrLow, bus_to_phys(tpc->RxDescArray)); + RTL_W32(RxDescStartAddrHigh, (unsigned long)0); + + /* RTL-8169sc/8110sc or later version */ + if (tpc->chipset > 5) + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(Cfg9346, Cfg9346_Lock); + udelay(10); + + RTL_W32(RxMissed, 0); + + rtl8169_set_rx_mode(dev); + + /* no early-rx interrupts */ + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + +#ifdef DEBUG_RTL8169 + printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); +#endif +} + +static void rtl8169_init_ring(struct eth_device *dev) +{ + int i; + +#ifdef DEBUG_RTL8169 + int stime = currticks(); + printf ("%s\n", __FUNCTION__); +#endif + + tpc->cur_rx = 0; + tpc->cur_tx = 0; + tpc->dirty_tx = 0; + memset(tpc->TxDescArray, 0x0, NUM_TX_DESC * sizeof(struct TxDesc)); + memset(tpc->RxDescArray, 0x0, NUM_RX_DESC * sizeof(struct RxDesc)); + + for (i = 0; i < NUM_TX_DESC; i++) { + tpc->Tx_skbuff[i] = &txb[i]; + } + + for (i = 0; i < NUM_RX_DESC; i++) { + if (i == (NUM_RX_DESC - 1)) + tpc->RxDescArray[i].status = + cpu_to_le32((OWNbit | EORbit) + RX_BUF_SIZE); + else + tpc->RxDescArray[i].status = + cpu_to_le32(OWNbit + RX_BUF_SIZE); + + tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE]; + tpc->RxDescArray[i].buf_addr = + cpu_to_le32(bus_to_phys(tpc->RxBufferRing[i])); + flush_cache((unsigned long)tpc->RxBufferRing[i], RX_BUF_SIZE); + } + +#ifdef DEBUG_RTL8169 + printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); +#endif +} + +/************************************************************************** +RESET - Finish setting up the ethernet interface +***************************************************************************/ +static int rtl_reset(struct eth_device *dev, bd_t *bis) +{ + int i; + +#ifdef DEBUG_RTL8169 + int stime = currticks(); + printf ("%s\n", __FUNCTION__); +#endif + + tpc->TxDescArrays = tx_ring; + /* Tx Desscriptor needs 256 bytes alignment; */ + tpc->TxDescArray = (struct TxDesc *) ((unsigned long)(tpc->TxDescArrays + + 255) & ~255); + + tpc->RxDescArrays = rx_ring; + /* Rx Desscriptor needs 256 bytes alignment; */ + tpc->RxDescArray = (struct RxDesc *) ((unsigned long)(tpc->RxDescArrays + + 255) & ~255); + + rtl8169_init_ring(dev); + rtl8169_hw_start(dev); + /* Construct a perfect filter frame with the mac address as first match + * and broadcast for all others */ + for (i = 0; i < 192; i++) + txb[i] = 0xFF; + + txb[0] = dev->enetaddr[0]; + txb[1] = dev->enetaddr[1]; + txb[2] = dev->enetaddr[2]; + txb[3] = dev->enetaddr[3]; + txb[4] = dev->enetaddr[4]; + txb[5] = dev->enetaddr[5]; + +#ifdef DEBUG_RTL8169 + printf ("%s elapsed time : %d\n", __FUNCTION__, currticks()-stime); +#endif + return 0; +} + +/************************************************************************** +HALT - Turn off ethernet interface +***************************************************************************/ +static void rtl_halt(struct eth_device *dev) +{ + int i; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + + ioaddr = dev->iobase; + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8(ChipCmd, 0x00); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16(IntrMask, 0x0000); + + RTL_W32(RxMissed, 0); + + tpc->TxDescArrays = NULL; + tpc->RxDescArrays = NULL; + tpc->TxDescArray = NULL; + tpc->RxDescArray = NULL; + for (i = 0; i < NUM_RX_DESC; i++) { + tpc->RxBufferRing[i] = NULL; + } +} + +/************************************************************************** +INIT - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +#define board_found 1 +#define valid_link 0 +static int rtl_init(struct eth_device *dev, bd_t *bis) +{ + static int board_idx = -1; + static int printed_version = 0; + int i, rc; + int option = -1, Cap10_100 = 0, Cap1000 = 0; + +#ifdef DEBUG_RTL8169 + printf ("%s\n", __FUNCTION__); +#endif + + ioaddr = dev->iobase; + + board_idx++; + + printed_version = 1; + + /* point to private storage */ + tpc = &tpx; + + rc = rtl8169_init_board(dev); + if (rc) + return rc; + + /* Get MAC address. FIXME: read EEPROM */ + for (i = 0; i < MAC_ADDR_LEN; i++) + dev->enetaddr[i] = RTL_R8(MAC0 + i); + +#ifdef DEBUG_RTL8169 + printf("chipset = %d\n", tpc->chipset); + printf("MAC Address"); + for (i = 0; i < MAC_ADDR_LEN; i++) + printf(":%02x", dev->enetaddr[i]); + putc('\n'); +#endif + +#ifdef DEBUG_RTL8169 + /* Print out some hardware info */ + printf("%s: at ioaddr 0x%x\n", dev->name, ioaddr); +#endif + + /* if TBI is not endbled */ + if (!(RTL_R8(PHYstatus) & TBI_Enable)) { + int val = mdio_read(PHY_AUTO_NEGO_REG); + + option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; + /* Force RTL8169 in 10/100/1000 Full/Half mode. */ + if (option > 0) { +#ifdef DEBUG_RTL8169 + printf("%s: Force-mode Enabled.\n", dev->name); +#endif + Cap10_100 = 0, Cap1000 = 0; + switch (option) { + case _10_Half: + Cap10_100 = PHY_Cap_10_Half; + Cap1000 = PHY_Cap_Null; + break; + case _10_Full: + Cap10_100 = PHY_Cap_10_Full; + Cap1000 = PHY_Cap_Null; + break; + case _100_Half: + Cap10_100 = PHY_Cap_100_Half; + Cap1000 = PHY_Cap_Null; + break; + case _100_Full: + Cap10_100 = PHY_Cap_100_Full; + Cap1000 = PHY_Cap_Null; + break; + case _1000_Full: + Cap10_100 = PHY_Cap_Null; + Cap1000 = PHY_Cap_1000_Full; + break; + default: + break; + } + mdio_write(PHY_AUTO_NEGO_REG, Cap10_100 | (val & 0x1F)); /* leave PHY_AUTO_NEGO_REG bit4:0 unchanged */ + mdio_write(PHY_1000_CTRL_REG, Cap1000); + } else { +#ifdef DEBUG_RTL8169 + printf("%s: Auto-negotiation Enabled.\n", + dev->name); +#endif + /* enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged */ + mdio_write(PHY_AUTO_NEGO_REG, + PHY_Cap_10_Half | PHY_Cap_10_Full | + PHY_Cap_100_Half | PHY_Cap_100_Full | + (val & 0x1F)); + + /* enable 1000 Full Mode */ + mdio_write(PHY_1000_CTRL_REG, PHY_Cap_1000_Full); + + } + + /* Enable auto-negotiation and restart auto-nigotiation */ + mdio_write(PHY_CTRL_REG, + PHY_Enable_Auto_Nego | PHY_Restart_Auto_Nego); + udelay(100); + + /* wait for auto-negotiation process */ + for (i = 10000; i > 0; i--) { + /* check if auto-negotiation complete */ + if (mdio_read(PHY_STAT_REG) & PHY_Auto_Nego_Comp) { + udelay(100); + option = RTL_R8(PHYstatus); + if (option & _1000bpsF) { +#ifdef DEBUG_RTL8169 + printf("%s: 1000Mbps Full-duplex operation.\n", + dev->name); +#endif + } else { +#ifdef DEBUG_RTL8169 + printf("%s: %sMbps %s-duplex operation.\n", + dev->name, + (option & _100bps) ? "100" : + "10", + (option & FullDup) ? "Full" : + "Half"); +#endif + } + break; + } else { + udelay(100); + } + } /* end for-loop to wait for auto-negotiation process */ + + } else { + udelay(100); +#ifdef DEBUG_RTL8169 + printf + ("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n", + dev->name, + (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed"); +#endif + } + + return 1; +} + +int rtl8169_initialize(bd_t *bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + u32 iobase; + int idx=0; + + while(1){ + /* Find RTL8169 */ + if ((devno = pci_find_devices(supported, idx++)) < 0) + break; + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + iobase &= ~0xf; + + debug ("rtl8169: REALTEK RTL8169 @0x%x\n", iobase); + + dev = (struct eth_device *)malloc(sizeof *dev); + + sprintf (dev->name, "RTL8169#%d", card_number); + + dev->priv = (void *) devno; + dev->iobase = (int)pci_mem_to_phys(devno, iobase); + + dev->init = rtl_reset; + dev->halt = rtl_halt; + dev->send = rtl_send; + dev->recv = rtl_recv; + + eth_register (dev); + + rtl_init(dev, bis); + + card_number++; + } + return card_number; +} diff --git a/roms/u-boot-sam460ex/drivers/net/s3c4510b_eth.c b/roms/u-boot-sam460ex/drivers/net/s3c4510b_eth.c new file mode 100644 index 000000000..818ed3d34 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/s3c4510b_eth.c @@ -0,0 +1,241 @@ +/*********************************************************************** + * + * Copyright (c) 2004 Cucy Systems (http://www.cucy.com) + * Curt Brune <curt@cucy.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Description: Ethernet interface for Samsung S3C4510B SoC + */ + +#include <common.h> +#include <command.h> +#include <net.h> +#include <asm/hardware.h> +#include "s3c4510b_eth.h" + +static TX_FrameDescriptor txFDbase[ETH_MaxTxFrames]; +static MACFrame txFrameBase[ETH_MaxTxFrames]; +static RX_FrameDescriptor rxFDbase[PKTBUFSRX]; +static ETH m_eth; + +static s32 TxFDinit( ETH *eth) { + + s32 i; + MACFrame *txFrmBase; + + /* disable cache for access to the TX buffers */ + txFrmBase = (MACFrame *)( (u32)txFrameBase | CACHE_DISABLE_MASK); + + /* store start of Tx descriptors and set current */ + eth->m_curTX_FD = (TX_FrameDescriptor *) ((u32)txFDbase | CACHE_DISABLE_MASK); + eth->m_baseTX_FD = eth->m_curTX_FD; + + for ( i = 0; i < ETH_MaxTxFrames; i++) { + eth->m_baseTX_FD[i].m_frameDataPtr.bf.dataPtr = (u32)&txFrmBase[i]; + eth->m_baseTX_FD[i].m_frameDataPtr.bf.owner = 0x0; /* CPU owner */ + eth->m_baseTX_FD[i].m_opt.ui = 0x0; + eth->m_baseTX_FD[i].m_status.ui = 0x0; + eth->m_baseTX_FD[i].m_nextFD = ð->m_baseTX_FD[i+1]; + } + + /* make the list circular */ + eth->m_baseTX_FD[i-1].m_nextFD = ð->m_baseTX_FD[0]; + + PUT_REG( REG_BDMATXPTR, (u32)eth->m_curTX_FD); + + return 0; +} + +static s32 RxFDinit( ETH *eth) { + + s32 i; + /* MACFrame *rxFrmBase; */ + + /* disable cache for access to the RX buffers */ + /* rxFrmBase = (MACFrame *)( (u32)rxFrameBase | CACHE_DISABLE_MASK); */ + + /* store start of Rx descriptors and set current */ + eth->m_curRX_FD = (RX_FrameDescriptor *)((u32)rxFDbase | CACHE_DISABLE_MASK); + eth->m_baseRX_FD = eth->m_curRX_FD; + for ( i = 0; i < PKTBUFSRX; i++) { + eth->m_baseRX_FD[i].m_frameDataPtr.bf.dataPtr = (u32)NetRxPackets[i] | CACHE_DISABLE_MASK; + eth->m_baseRX_FD[i].m_frameDataPtr.bf.owner = 0x1; /* BDMA owner */ + eth->m_baseRX_FD[i].m_reserved = 0x0; + eth->m_baseRX_FD[i].m_status.ui = 0x0; + eth->m_baseRX_FD[i].m_nextFD = ð->m_baseRX_FD[i+1]; + } + + /* make the list circular */ + eth->m_baseRX_FD[i-1].m_nextFD = ð->m_baseRX_FD[0]; + + PUT_REG( REG_BDMARXPTR, (u32)eth->m_curRX_FD); + + return 0; +} + +/* + * Public u-boot interface functions below + */ + +int eth_init(bd_t *bis) +{ + + ETH *eth = &m_eth; + + /* store our MAC address */ + eth_getenv_enetaddr("ethaddr", eth->m_mac); + + /* setup DBMA and MAC */ + PUT_REG( REG_BDMARXCON, ETH_BRxRS); /* reset BDMA RX machine */ + PUT_REG( REG_BDMATXCON, ETH_BTxRS); /* reset BDMA TX machine */ + PUT_REG( REG_MACCON , ETH_SwReset); /* reset MAC machine */ + PUT_REG( REG_BDMARXLSZ, sizeof(MACFrame)); + PUT_REG( REG_MACCON , 0); /* reset MAC machine */ + + /* init frame descriptors */ + TxFDinit( eth); + RxFDinit( eth); + + /* init the CAM with our MAC address */ + PUT_REG( REG_CAM_BASE, (eth->m_mac[0] << 24) | + (eth->m_mac[1] << 16) | + (eth->m_mac[2] << 8) | + (eth->m_mac[3])); + PUT_REG( REG_CAM_BASE + 0x4, (eth->m_mac[4] << 24) | + (eth->m_mac[5] << 16)); + + /* enable CAM address 1 -- the MAC we just loaded */ + PUT_REG( REG_CAMEN, 0x1); + + PUT_REG( REG_CAMCON, + ETH_BroadAcc | /* accept broadcast packetes */ + ETH_CompEn); /* enable compare mode (check against the CAM) */ + + /* configure the BDMA Transmitter control */ + PUT_REG( REG_BDMATXCON, + ETH_BTxBRST | /* BDMA Tx burst size 16 words */ + ETH_BTxMSL110 | /* BDMA Tx wait to fill 6/8 of the BDMA */ + ETH_BTxSTSKO | /* BDMA Tx interrupt(Stop) on non-owner TX FD */ + ETH_BTxEn); /* BDMA Tx Enable */ + + /* configure the MAC Transmitter control */ + PUT_REG( REG_MACTXCON, + ETH_EnComp | /* interrupt when the MAC transmits or discards packet */ + ETH_TxEn); /* MAC transmit enable */ + + /* configure the BDMA Receiver control */ + PUT_REG( REG_BDMARXCON, + ETH_BRxBRST | /* BDMA Rx Burst Size 16 words */ + ETH_BRxSTSKO | /* BDMA Rx interrupt(Stop) on non-owner RX FD */ + ETH_BRxMAINC | /* BDMA Rx Memory Address increment */ + ETH_BRxDIE | /* BDMA Rx Every Received Frame Interrupt Enable */ + ETH_BRxNLIE | /* BDMA Rx NULL List Interrupt Enable */ + ETH_BRxNOIE | /* BDMA Rx Not Owner Interrupt Enable */ + ETH_BRxLittle | /* BDMA Rx Little endian */ + ETH_BRxEn); /* BDMA Rx Enable */ + + /* configure the MAC Receiver control */ + PUT_REG( REG_MACRXCON, + ETH_RxEn); /* MAC ETH_RxEn */ + + return 0; + +} + +/* Send a packet */ +s32 eth_send(volatile void *packet, s32 length) +{ + + u32 i; + ETH *eth = &m_eth; + + if ( eth->m_curTX_FD->m_frameDataPtr.bf.owner) { + printf("eth_send(): TX Frame. CPU not owner.\n"); + return -1; + } + + /* copy user data into frame data pointer */ + memcpy((void *)((u32)(eth->m_curTX_FD->m_frameDataPtr.bf.dataPtr)), + (void *)packet, + length); + + /* Set TX Frame flags */ + eth->m_curTX_FD->m_opt.bf.widgetAlign = 0; + eth->m_curTX_FD->m_opt.bf.frameDataDir = 1; + eth->m_curTX_FD->m_opt.bf.littleEndian = 1; + eth->m_curTX_FD->m_opt.bf.macTxIrqEnbl = 1; + eth->m_curTX_FD->m_opt.bf.no_crc = 0; + eth->m_curTX_FD->m_opt.bf.no_padding = 0; + + /* Set TX Frame length */ + eth->m_curTX_FD->m_status.bf.len = length; + + /* Change ownership to BDMA */ + eth->m_curTX_FD->m_frameDataPtr.bf.owner = 1; + + /* Enable MAC and BDMA Tx control register */ + SET_REG( REG_BDMATXCON, ETH_BTxEn); + SET_REG( REG_MACTXCON, ETH_TxEn); + + /* poll on TX completion status */ + while ( !eth->m_curTX_FD->m_status.bf.complete) { + /* sleep */ + for ( i = 0; i < 0x10000; i ++); + } + + /* Change the Tx frame descriptor for next use */ + eth->m_curTX_FD = eth->m_curTX_FD->m_nextFD; + + return 0; +} + +/* Check for received packets */ +s32 eth_rx (void) +{ + s32 nLen = 0; + ETH *eth = &m_eth; + + /* check if packet ready */ + if ( (GET_REG( REG_BDMASTAT)) & ETH_S_BRxRDF) { + /* process all waiting packets */ + while ( !eth->m_curRX_FD->m_frameDataPtr.bf.owner) { + nLen = eth->m_curRX_FD->m_status.bf.len; + /* call back u-boot -- may call eth_send() */ + NetReceive ((u8 *)eth->m_curRX_FD->m_frameDataPtr.ui, nLen); + /* set owner back to CPU */ + eth->m_curRX_FD->m_frameDataPtr.bf.owner = 1; + /* clear status */ + eth->m_curRX_FD->m_status.ui = 0x0; + /* advance to next descriptor */ + eth->m_curRX_FD = eth->m_curRX_FD->m_nextFD; + /* clear received frame bit */ + PUT_REG( REG_BDMASTAT, ETH_S_BRxRDF); + } + } + + return nLen; +} + +/* Halt ethernet engine */ +void eth_halt(void) +{ + /* disable MAC */ + PUT_REG( REG_MACCON, ETH_HaltReg); +} diff --git a/roms/u-boot-sam460ex/drivers/net/s3c4510b_eth.h b/roms/u-boot-sam460ex/drivers/net/s3c4510b_eth.h new file mode 100644 index 000000000..18a52a7f4 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/s3c4510b_eth.h @@ -0,0 +1,302 @@ +#ifndef __S3C4510B_ETH_H +#define __S3C4510B_ETH_H +/* + * Copyright (c) 2004 Cucy Systems (http://www.cucy.com) + * Curt Brune <curt@cucy.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * MODULE: $Id:$ + * Description: Ethernet interface + * Runtime Env: ARM7TDMI + * Change History: + * 03-02-04 Create (Curt Brune) curt@cucy.com + * + */ + +#define ETH_MAC_ADDR_SIZE (6) /* dst,src addr is 6bytes each */ +#define ETH_MaxTxFrames (16) /* Max number of Tx Frames */ + +/* Buffered DMA Receiver Control Register */ +#define ETH_BRxBRST 0x0000F /* BDMA Rx Burst Size * BRxBRST */ + /* = Burst Data Size 16 */ +#define ETH_BRxSTSKO 0x00020 /* BDMA Rx Stop/Skip Frame or Interrupt(=1) */ + /* case of not OWNER the current Frame */ +#define ETH_BRxMAINC 0x00040 /* BDMA Rx Memory Address Inc/Dec */ +#define ETH_BRxDIE 0x00080 /* BDMA Rx Every Received Frame Interrupt Enable */ +#define ETH_BRxNLIE 0x00100 /* BDMA Rx NULL List Interrupt Enable */ +#define ETH_BRxNOIE 0x00200 /* BDMA Rx Not Owner Interrupt Enable */ +#define ETH_BRxMSOIE 0x00400 /* BDMA Rx Maximum Size over Interrupr Enable */ +#define ETH_BRxLittle 0x00800 /* BDMA Rx Big/Little Endian */ +#define ETH_BRxBig 0x00000 /* BDMA Rx Big/Little Endian */ +#define ETH_BRxWA01 0x01000 /* BDMA Rx Word Alignment- one invalid byte */ +#define ETH_BRxWA10 0x02000 /* BDMA Rx Word Alignment- two invalid byte */ +#define ETH_BRxWA11 0x03000 /* BDMA Rx Word Alignment- three invalid byte */ +#define ETH_BRxEn 0x04000 /* BDMA Rx Enable */ +#define ETH_BRxRS 0x08000 /* BDMA Rx Reset */ +#define ETH_RxEmpty 0x10000 /* BDMA Rx Buffer empty interrupt */ +#define ETH_BRxEarly 0x20000 /* BDMA Rx Early notify Interrupt */ + +/* Buffered DMA Trasmit Control Register(BDMATXCON) */ +#define ETH_BTxBRST 0x0000F /* BDMA Tx Burst Size = 16 */ +#define ETH_BTxSTSKO 0x00020 /* BDMA Tx Stop/Skip Frame or Interrupt in case */ + /* of not Owner the current frame */ +#define ETH_BTxCPIE 0x00080 /* BDMA Tx Complete to send control */ + /* packet Enable */ +#define ETH_BTxNOIE 0x00200 /* BDMA Tx Buffer Not Owner */ +#define ETH_BTxEmpty 0x00400 /* BDMA Tx Buffer Empty Interrupt */ + +/* BDMA Tx buffer can be moved to the MAC Tx IO when the new frame comes in. */ +#define ETH_BTxMSL000 0x00000 /* No wait to fill the BDMA */ +#define ETH_BTxMSL001 0x00800 /* wait to fill 1/8 of the BDMA */ +#define ETH_BTxMSL010 0x01000 /* wait to fill 2/8 of the BDMA */ +#define ETH_BTxMSL011 0x01800 /* wait to fill 3/8 of the BDMA */ +#define ETH_BTxMSL100 0x02000 /* wait to fill 4/8 of the BDMA */ +#define ETH_BTxMSL101 0x02800 /* wait to fill 5/8 of the BDMA */ +#define ETH_BTxMSL110 0x03000 /* wait to fill 6/8 of the BDMA */ +#define ETH_BTxMSL111 0x03800 /* wait to fill 7/8 of the BDMA */ +#define ETH_BTxEn 0x04000 /* BDMA Tx Enable */ +#define ETH_BTxRS 0x08000 /* BDMA Tx Reset */ + +/* BDMA Status Register */ +#define ETH_S_BRxRDF 0x00001 /* BDMA Rx Done Every Received Frame */ +#define ETH_S_BRxNL 0x00002 /* BDMA Rx NULL List */ +#define ETH_S_BRxNO 0x00004 /* BDMA Rx Not Owner */ +#define ETH_S_BRxMSO 0x00008 /* BDMA Rx Maximum Size Over */ +#define ETH_S_BRxEmpty 0x00010 /* BDMA Rx Buffer Empty */ +#define ETH_S_BRxSEarly 0x00020 /* Early Notify */ +#define ETH_S_BRxFRF 0x00080 /* One more frame data in BDMA receive buffer */ +#define ETH_S_BTxCCP 0x10000 /* BDMA Tx Complete to send Control Packet */ +#define ETH_S_BTxNL 0x20000 /* BDMA Tx Null List */ +#define ETH_S_BTxNO 0x40000 /* BDMA Tx Not Owner */ +#define ETH_S_BTxEmpty 0x100000 /* BDMA Tx Buffer Empty */ + +/* MAC Control Register */ +#define ETH_HaltReg 0x0001 /* stop transmission and reception */ + /* after completion of any current packets */ +#define ETH_HaltImm 0x0002 /* Stop transmission and reception immediately */ +#define ETH_SwReset 0x0004 /* reset all Ethernet controller state machines */ + /* and FIFOs */ +#define ETH_FullDup 0x0008 /* allow transmission to begin while reception */ + /* is occurring */ +#define ETH_MACLoop 0x0010 /* MAC loopback */ +#define ETH_ConnM00 0x0000 /* Automatic-default */ +#define ETH_ConnM01 0x0020 /* Force 10Mbits endec */ +#define ETH_ConnM10 0x0040 /* Force MII (rate determined by MII clock */ +#define ETH_MIIOFF 0x0040 /* Force MII (rate determined by MII clock */ +#define ETH_Loop10 0x0080 /* Loop 10Mbps */ +#define ETH_MissRoll 0x0400 /* Missed error counter rolled over */ +#define ETH_MDCOFF 0x1000 /* MII Station Management Clock Off */ +#define ETH_EnMissRoll 0x2000 /* Interrupt when missed error counter rolls */ + /* over */ +#define ETH_Link10 0x8000 /* Link status 10Mbps */ + +/* CAM control register(CAMCON) */ +#define ETH_StationAcc 0x0001 /* Accept any packet with a unicast station */ + /* address */ +#define ETH_GroupAcc 0x0002 /* Accept any packet with multicast-group */ + /* station address */ +#define ETH_BroadAcc 0x0004 /* Accept any packet with a broadcast station */ + /* address */ +#define ETH_NegCAM 0x0008 /* 0: Accept packets CAM recognizes, */ + /* reject others */ + /* 1: reject packets CAM recognizes, */ + /* accept others */ +#define ETH_CompEn 0x0010 /* Compare Enable mode */ + +/* Transmit Control Register(MACTXCON) */ +#define ETH_TxEn 0x0001 /* transmit Enable */ +#define ETH_TxHalt 0x0002 /* Transmit Halt Request */ +#define ETH_NoPad 0x0004 /* suppress Padding */ +#define ETH_NoCRC 0x0008 /* Suppress CRC */ +#define ETH_FBack 0x0010 /* Fast Back-off */ +#define ETH_NoDef 0x0020 /* Disable the defer counter */ +#define ETH_SdPause 0x0040 /* Send Pause */ +#define ETH_MII10En 0x0080 /* MII 10Mbps mode enable */ +#define ETH_EnUnder 0x0100 /* Enable Underrun */ +#define ETH_EnDefer 0x0200 /* Enable Deferral */ +#define ETH_EnNCarr 0x0400 /* Enable No Carrier */ +#define ETH_EnExColl 0x0800 /* interrupt if 16 collision occur */ + /* in the same packet */ +#define ETH_EnLateColl 0x1000 /* interrupt if collision occurs after */ + /* 512 bit times(64 bytes times) */ +#define ETH_EnTxPar 0x2000 /* interrupt if the MAC transmit FIFO */ + /* has a parity error */ +#define ETH_EnComp 0x4000 /* interrupt when the MAC transmits or */ + /* discards one packet */ + +/* Transmit Status Register(MACTXSTAT) */ +#define ETH_ExColl 0x0010 /* Excessive collision */ +#define ETH_TxDeffered 0x0020 /* set if 16 collisions occur for same packet */ +#define ETH_Paused 0x0040 /* packet waited because of pause during */ + /* transmission */ +#define ETH_IntTx 0x0080 /* set if transmission of packet causes an */ + /* interrupt condiftion */ +#define ETH_Under 0x0100 /* MAC transmit FIFO becomes empty during */ + /* transmission */ +#define ETH_Defer 0x0200 /* MAC defers for MAC deferral */ +#define ETH_NCarr 0x0400 /* No carrier sense detected during the */ + /* transmission of a packet */ +#define ETH_SQE 0x0800 /* Signal Quality Error */ +#define ETH_LateColl 0x1000 /* a collision occures after 512 bit times */ +#define ETH_TxPar 0x2000 /* MAC transmit FIFO has detected a parity error */ +#define ETH_Comp 0x4000 /* MAC transmit or discards one packet */ +#define ETH_TxHalted 0x8000 /* Transmission was halted by clearing */ + /* TxEn or Halt immedite */ + +/* Receive Control Register (MACRXCON) */ +#define ETH_RxEn 0x0001 +#define ETH_RxHalt 0x0002 +#define ETH_LongEn 0x0004 +#define ETH_ShortEn 0x0008 +#define ETH_StripCRC 0x0010 +#define ETH_PassCtl 0x0020 +#define ETH_IgnoreCRC 0x0040 +#define ETH_EnAlign 0x0100 +#define ETH_EnCRCErr 0x0200 +#define ETH_EnOver 0x0400 +#define ETH_EnLongErr 0x0800 +#define ETH_EnRxPar 0x2000 +#define ETH_EnGood 0x4000 + +/* Receive Status Register(MACRXSTAT) */ +#define ETH_MCtlRecd 0x0020 +#define ETH_MIntRx 0x0040 +#define ETH_MRx10Stat 0x0080 +#define ETH_MAllignErr 0x0100 +#define ETH_MCRCErr 0x0200 +#define ETH_MOverflow 0x0400 +#define ETH_MLongErr 0x0800 +#define ETH_MRxPar 0x2000 +#define ETH_MRxGood 0x4000 +#define ETH_MRxHalted 0x8000 + +/* type of ethernet packets */ +#define ETH_TYPE_ARP (0x0806) +#define ETH_TYPE_IP (0x0800) + +#define ETH_HDR_SIZE (14) + +/* bit field for frame data pointer word */ +typedef struct __BF_FrameDataPtr { + u32 dataPtr:31; + u32 owner: 1; +} BF_FrameDataPtr; + +typedef union _FrameDataPtr { + u32 ui; + BF_FrameDataPtr bf; +} FrameDataPtr; + +typedef struct __BF_TX_Options { + u32 no_padding: 1; + u32 no_crc: 1; + u32 macTxIrqEnbl: 1; + u32 littleEndian: 1; + u32 frameDataDir: 1; + u32 widgetAlign: 2; + u32 reserved:25; +} BF_TX_Options; + +typedef union _TX_Options { + u32 ui; + BF_TX_Options bf; +} TX_Options; + +typedef struct __BF_RX_Status { + u32 len:16; /* frame length */ + u32 reserved1: 3; + u32 overMax: 1; + u32 reserved2: 1; + u32 ctrlRcv: 1; + u32 intRx: 1; + u32 rx10stat: 1; + u32 alignErr: 1; + u32 crcErr: 1; + u32 overFlow: 1; + u32 longErr: 1; + u32 reserved3: 1; + u32 parityErr: 1; + u32 good: 1; + u32 halted: 1; +} BF_RX_Status; + +typedef union _RX_Status { + u32 ui; + BF_RX_Status bf; +} RX_Status; + +typedef struct __BF_TX_Status { + u32 len:16; /* frame length */ + u32 txCollCnt: 4; + u32 exColl: 1; + u32 txDefer: 1; + u32 paused: 1; + u32 intTx: 1; + u32 underRun: 1; + u32 defer: 1; + u32 noCarrier: 1; + u32 SQErr: 1; + u32 lateColl: 1; + u32 parityErr: 1; + u32 complete: 1; + u32 halted: 1; +} BF_TX_Status; + +typedef union _TX_Status { + u32 ui; + BF_TX_Status bf; +} TX_Status; + +/* TX descriptor structure */ +typedef struct __TX_FrameDescriptor { + volatile FrameDataPtr m_frameDataPtr; + TX_Options m_opt; + volatile TX_Status m_status; + struct __TX_FrameDescriptor *m_nextFD; +} TX_FrameDescriptor; + +/* RX descriptor structure */ +typedef struct __RX_FrameDescriptor { + volatile FrameDataPtr m_frameDataPtr; + u32 m_reserved; + volatile RX_Status m_status; + struct __RX_FrameDescriptor *m_nextFD; +} RX_FrameDescriptor; + +/* MAC Frame Structure */ +struct __MACFrame { + u8 m_dstAddr[6]; + u8 m_srcAddr[6]; + u16 m_lengthOrType; + u8 m_payload[1506]; +} __attribute__ ((packed)); + +typedef struct __MACFrame MACFrame; + +/* Ethernet Control block */ +typedef struct __ETH { + TX_FrameDescriptor *m_curTX_FD; /* pointer to current TX frame descriptor */ + TX_FrameDescriptor *m_baseTX_FD; /* pointer to base TX frame descriptor */ + RX_FrameDescriptor *m_curRX_FD; /* pointer to current RX frame descriptor */ + RX_FrameDescriptor *m_baseRX_FD; /* pointer to base RX frame descriptor */ + u8 m_mac[6]; /* pointer to our MAC address */ +} ETH; + +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/sh_eth.c b/roms/u-boot-sam460ex/drivers/net/sh_eth.c new file mode 100644 index 000000000..86cc324e9 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/sh_eth.c @@ -0,0 +1,680 @@ +/* + * sh_eth.c - Driver for Renesas SH7763's ethernet controler. + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Copyright (c) 2008 Nobuhiro Iwamatsu + * Copyright (c) 2007 Carlos Munoz <carlos@kenati.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/errno.h> +#include <asm/io.h> + +#include "sh_eth.h" + +#ifndef CONFIG_SH_ETHER_USE_PORT +# error "Please define CONFIG_SH_ETHER_USE_PORT" +#endif +#ifndef CONFIG_SH_ETHER_PHY_ADDR +# error "Please define CONFIG_SH_ETHER_PHY_ADDR" +#endif + +#define SH_ETH_PHY_DELAY 50000 + +/* + * Bits are written to the PHY serially using the + * PIR register, just like a bit banger. + */ +static void sh_eth_mii_write_phy_bits(int port, u32 val, int len) +{ + int i; + u32 pir; + + /* Bit positions is 1 less than the number of bits */ + for (i = len - 1; i >= 0; i--) { + /* Write direction, bit to write, clock is low */ + pir = 2 | ((val & 1 << i) ? 1 << 2 : 0); + outl(pir, PIR(port)); + udelay(1); + /* Write direction, bit to write, clock is high */ + pir = 3 | ((val & 1 << i) ? 1 << 2 : 0); + outl(pir, PIR(port)); + udelay(1); + /* Write direction, bit to write, clock is low */ + pir = 2 | ((val & 1 << i) ? 1 << 2 : 0); + outl(pir, PIR(port)); + udelay(1); + } +} + +static void sh_eth_mii_bus_release(int port) +{ + /* Read direction, clock is low */ + outl(0, PIR(port)); + udelay(1); + /* Read direction, clock is high */ + outl(1, PIR(port)); + udelay(1); + /* Read direction, clock is low */ + outl(0, PIR(port)); + udelay(1); +} + +static void sh_eth_mii_ind_bus_release(int port) +{ + /* Read direction, clock is low */ + outl(0, PIR(port)); + udelay(1); +} + +static void sh_eth_mii_read_phy_bits(int port, u32 *val, int len) +{ + int i; + u32 pir; + + *val = 0; + for (i = len - 1; i >= 0; i--) { + /* Read direction, clock is high */ + outl(1, PIR(port)); + udelay(1); + /* Read bit */ + pir = inl(PIR(port)); + *val |= (pir & 8) ? 1 << i : 0; + /* Read direction, clock is low */ + outl(0, PIR(port)); + udelay(1); + } +} + +#define PHY_INIT 0xFFFFFFFF +#define PHY_READ 0x02 +#define PHY_WRITE 0x01 +/* + * To read a phy register, mii managements frames are sent to the phy. + * The frames look like this: + * pre (32 bits): 0xffff ffff + * st (2 bits): 01 + * op (2bits): 10: read 01: write + * phyad (5 bits): xxxxx + * regad (5 bits): xxxxx + * ta (Bus release): + * data (16 bits): read data + */ +static u32 sh_eth_mii_read_phy_reg(int port, u8 phy_addr, int reg) +{ + u32 val; + + /* Sent mii management frame */ + /* pre */ + sh_eth_mii_write_phy_bits(port, PHY_INIT, 32); + /* st (start of frame) */ + sh_eth_mii_write_phy_bits(port, 0x1, 2); + /* op (code) */ + sh_eth_mii_write_phy_bits(port, PHY_READ, 2); + /* phy address */ + sh_eth_mii_write_phy_bits(port, phy_addr, 5); + /* Register to read */ + sh_eth_mii_write_phy_bits(port, reg, 5); + + /* Bus release */ + sh_eth_mii_bus_release(port); + + /* Read register */ + sh_eth_mii_read_phy_bits(port, &val, 16); + + return val; +} + +/* + * To write a phy register, mii managements frames are sent to the phy. + * The frames look like this: + * pre (32 bits): 0xffff ffff + * st (2 bits): 01 + * op (2bits): 10: read 01: write + * phyad (5 bits): xxxxx + * regad (5 bits): xxxxx + * ta (2 bits): 10 + * data (16 bits): write data + * idle (Independent bus release) + */ +static void sh_eth_mii_write_phy_reg(int port, u8 phy_addr, int reg, u16 val) +{ + /* Sent mii management frame */ + /* pre */ + sh_eth_mii_write_phy_bits(port, PHY_INIT, 32); + /* st (start of frame) */ + sh_eth_mii_write_phy_bits(port, 0x1, 2); + /* op (code) */ + sh_eth_mii_write_phy_bits(port, PHY_WRITE, 2); + /* phy address */ + sh_eth_mii_write_phy_bits(port, phy_addr, 5); + /* Register to read */ + sh_eth_mii_write_phy_bits(port, reg, 5); + /* ta */ + sh_eth_mii_write_phy_bits(port, PHY_READ, 2); + /* Write register data */ + sh_eth_mii_write_phy_bits(port, val, 16); + + /* Independent bus release */ + sh_eth_mii_ind_bus_release(port); +} + +int sh_eth_send(struct eth_device *dev, volatile void *packet, int len) +{ + struct sh_eth_dev *eth = dev->priv; + int port = eth->port, ret = 0, timeout; + struct sh_eth_info *port_info = ð->port_info[port]; + + if (!packet || len > 0xffff) { + printf(SHETHER_NAME ": %s: Invalid argument\n", __func__); + ret = -EINVAL; + goto err; + } + + /* packet must be a 4 byte boundary */ + if ((int)packet & (4 - 1)) { + printf(SHETHER_NAME ": %s: packet not 4 byte alligned\n", __func__); + ret = -EFAULT; + goto err; + } + + /* Update tx descriptor */ + port_info->tx_desc_cur->td2 = ADDR_TO_PHY(packet); + port_info->tx_desc_cur->td1 = len << 16; + /* Must preserve the end of descriptor list indication */ + if (port_info->tx_desc_cur->td0 & TD_TDLE) + port_info->tx_desc_cur->td0 = TD_TACT | TD_TFP | TD_TDLE; + else + port_info->tx_desc_cur->td0 = TD_TACT | TD_TFP; + + /* Restart the transmitter if disabled */ + if (!(inl(EDTRR(port)) & EDTRR_TRNS)) + outl(EDTRR_TRNS, EDTRR(port)); + + /* Wait until packet is transmitted */ + timeout = 1000; + while (port_info->tx_desc_cur->td0 & TD_TACT && timeout--) + udelay(100); + + if (timeout < 0) { + printf(SHETHER_NAME ": transmit timeout\n"); + ret = -ETIMEDOUT; + goto err; + } + + port_info->tx_desc_cur++; + if (port_info->tx_desc_cur >= port_info->tx_desc_base + NUM_TX_DESC) + port_info->tx_desc_cur = port_info->tx_desc_base; + + return ret; +err: + return ret; +} + +int sh_eth_recv(struct eth_device *dev) +{ + struct sh_eth_dev *eth = dev->priv; + int port = eth->port, len = 0; + struct sh_eth_info *port_info = ð->port_info[port]; + volatile u8 *packet; + + /* Check if the rx descriptor is ready */ + if (!(port_info->rx_desc_cur->rd0 & RD_RACT)) { + /* Check for errors */ + if (!(port_info->rx_desc_cur->rd0 & RD_RFE)) { + len = port_info->rx_desc_cur->rd1 & 0xffff; + packet = (volatile u8 *) + ADDR_TO_P2(port_info->rx_desc_cur->rd2); + NetReceive(packet, len); + } + + /* Make current descriptor available again */ + if (port_info->rx_desc_cur->rd0 & RD_RDLE) + port_info->rx_desc_cur->rd0 = RD_RACT | RD_RDLE; + else + port_info->rx_desc_cur->rd0 = RD_RACT; + + /* Point to the next descriptor */ + port_info->rx_desc_cur++; + if (port_info->rx_desc_cur >= + port_info->rx_desc_base + NUM_RX_DESC) + port_info->rx_desc_cur = port_info->rx_desc_base; + } + + /* Restart the receiver if disabled */ + if (!(inl(EDRRR(port)) & EDRRR_R)) + outl(EDRRR_R, EDRRR(port)); + + return len; +} + +#define EDMR_INIT_CNT 1000 +static int sh_eth_reset(struct sh_eth_dev *eth) +{ + int port = eth->port; + int ret = 0, i; + + /* Start e-dmac transmitter and receiver */ + outl(EDSR_ENALL, EDSR(port)); + + /* Perform a software reset and wait for it to complete */ + outl(EDMR_SRST, EDMR(port)); + for (i = 0; i < EDMR_INIT_CNT; i++) { + if (!(inl(EDMR(port)) & EDMR_SRST)) + break; + udelay(1000); + } + + if (i == EDMR_INIT_CNT) { + printf(SHETHER_NAME ": Software reset timeout\n"); + ret = -EIO; + } + + return ret; +} + +static int sh_eth_tx_desc_init(struct sh_eth_dev *eth) +{ + int port = eth->port, i, ret = 0; + u32 tmp_addr; + struct sh_eth_info *port_info = ð->port_info[port]; + struct tx_desc_s *cur_tx_desc; + + /* + * Allocate tx descriptors. They must be TX_DESC_SIZE bytes aligned + */ + port_info->tx_desc_malloc = malloc(NUM_TX_DESC * + sizeof(struct tx_desc_s) + + TX_DESC_SIZE - 1); + if (!port_info->tx_desc_malloc) { + printf(SHETHER_NAME ": malloc failed\n"); + ret = -ENOMEM; + goto err; + } + + tmp_addr = (u32) (((int)port_info->tx_desc_malloc + TX_DESC_SIZE - 1) & + ~(TX_DESC_SIZE - 1)); + /* Make sure we use a P2 address (non-cacheable) */ + port_info->tx_desc_base = (struct tx_desc_s *)ADDR_TO_P2(tmp_addr); + port_info->tx_desc_cur = port_info->tx_desc_base; + + /* Initialize all descriptors */ + for (cur_tx_desc = port_info->tx_desc_base, i = 0; i < NUM_TX_DESC; + cur_tx_desc++, i++) { + cur_tx_desc->td0 = 0x00; + cur_tx_desc->td1 = 0x00; + cur_tx_desc->td2 = 0x00; + } + + /* Mark the end of the descriptors */ + cur_tx_desc--; + cur_tx_desc->td0 |= TD_TDLE; + + /* Point the controller to the tx descriptor list. Must use physical + addresses */ + outl(ADDR_TO_PHY(port_info->tx_desc_base), TDLAR(port)); + outl(ADDR_TO_PHY(port_info->tx_desc_base), TDFAR(port)); + outl(ADDR_TO_PHY(cur_tx_desc), TDFXR(port)); + outl(0x01, TDFFR(port));/* Last discriptor bit */ + +err: + return ret; +} + +static int sh_eth_rx_desc_init(struct sh_eth_dev *eth) +{ + int port = eth->port, i , ret = 0; + struct sh_eth_info *port_info = ð->port_info[port]; + struct rx_desc_s *cur_rx_desc; + u32 tmp_addr; + u8 *rx_buf; + + /* + * Allocate rx descriptors. They must be RX_DESC_SIZE bytes aligned + */ + port_info->rx_desc_malloc = malloc(NUM_RX_DESC * + sizeof(struct rx_desc_s) + + RX_DESC_SIZE - 1); + if (!port_info->rx_desc_malloc) { + printf(SHETHER_NAME ": malloc failed\n"); + ret = -ENOMEM; + goto err; + } + + tmp_addr = (u32) (((int)port_info->rx_desc_malloc + RX_DESC_SIZE - 1) & + ~(RX_DESC_SIZE - 1)); + /* Make sure we use a P2 address (non-cacheable) */ + port_info->rx_desc_base = (struct rx_desc_s *)ADDR_TO_P2(tmp_addr); + + port_info->rx_desc_cur = port_info->rx_desc_base; + + /* + * Allocate rx data buffers. They must be 32 bytes aligned and in + * P2 area + */ + port_info->rx_buf_malloc = malloc(NUM_RX_DESC * MAX_BUF_SIZE + 31); + if (!port_info->rx_buf_malloc) { + printf(SHETHER_NAME ": malloc failed\n"); + ret = -ENOMEM; + goto err_buf_malloc; + } + + tmp_addr = (u32)(((int)port_info->rx_buf_malloc + (32 - 1)) & + ~(32 - 1)); + port_info->rx_buf_base = (u8 *)ADDR_TO_P2(tmp_addr); + + /* Initialize all descriptors */ + for (cur_rx_desc = port_info->rx_desc_base, + rx_buf = port_info->rx_buf_base, i = 0; + i < NUM_RX_DESC; cur_rx_desc++, rx_buf += MAX_BUF_SIZE, i++) { + cur_rx_desc->rd0 = RD_RACT; + cur_rx_desc->rd1 = MAX_BUF_SIZE << 16; + cur_rx_desc->rd2 = (u32) ADDR_TO_PHY(rx_buf); + } + + /* Mark the end of the descriptors */ + cur_rx_desc--; + cur_rx_desc->rd0 |= RD_RDLE; + + /* Point the controller to the rx descriptor list */ + outl(ADDR_TO_PHY(port_info->rx_desc_base), RDLAR(port)); + outl(ADDR_TO_PHY(port_info->rx_desc_base), RDFAR(port)); + outl(ADDR_TO_PHY(cur_rx_desc), RDFXR(port)); + outl(RDFFR_RDLF, RDFFR(port)); + + return ret; + +err_buf_malloc: + free(port_info->rx_desc_malloc); + port_info->rx_desc_malloc = NULL; + +err: + return ret; +} + +static void sh_eth_tx_desc_free(struct sh_eth_dev *eth) +{ + int port = eth->port; + struct sh_eth_info *port_info = ð->port_info[port]; + + if (port_info->tx_desc_malloc) { + free(port_info->tx_desc_malloc); + port_info->tx_desc_malloc = NULL; + } +} + +static void sh_eth_rx_desc_free(struct sh_eth_dev *eth) +{ + int port = eth->port; + struct sh_eth_info *port_info = ð->port_info[port]; + + if (port_info->rx_desc_malloc) { + free(port_info->rx_desc_malloc); + port_info->rx_desc_malloc = NULL; + } + + if (port_info->rx_buf_malloc) { + free(port_info->rx_buf_malloc); + port_info->rx_buf_malloc = NULL; + } +} + +static int sh_eth_desc_init(struct sh_eth_dev *eth) +{ + int ret = 0; + + ret = sh_eth_tx_desc_init(eth); + if (ret) + goto err_tx_init; + + ret = sh_eth_rx_desc_init(eth); + if (ret) + goto err_rx_init; + + return ret; +err_rx_init: + sh_eth_tx_desc_free(eth); + +err_tx_init: + return ret; +} + +static int sh_eth_phy_config(struct sh_eth_dev *eth) +{ + int port = eth->port, timeout, ret = 0; + struct sh_eth_info *port_info = ð->port_info[port]; + u32 val; + + /* Reset phy */ + sh_eth_mii_write_phy_reg + (port, port_info->phy_addr, PHY_CTRL, PHY_C_RESET); + timeout = 10; + while (timeout--) { + val = sh_eth_mii_read_phy_reg(port, + port_info->phy_addr, PHY_CTRL); + if (!(val & PHY_C_RESET)) + break; + udelay(SH_ETH_PHY_DELAY); + } + + if (timeout < 0) { + printf(SHETHER_NAME ": phy reset timeout\n"); + ret = -EIO; + goto err_tout; + } + + /* Advertise 100/10 baseT full/half duplex */ + sh_eth_mii_write_phy_reg(port, port_info->phy_addr, PHY_ANA, + (PHY_A_FDX|PHY_A_HDX|PHY_A_10FDX|PHY_A_10HDX|PHY_A_EXT)); + /* Autonegotiation, normal operation, full duplex, enable tx */ + sh_eth_mii_write_phy_reg(port, port_info->phy_addr, PHY_CTRL, + (PHY_C_ANEGEN|PHY_C_RANEG)); + /* Wait for autonegotiation to complete */ + timeout = 100; + while (timeout--) { + val = sh_eth_mii_read_phy_reg(port, port_info->phy_addr, 1); + if (val & PHY_S_ANEGC) + break; + + udelay(SH_ETH_PHY_DELAY); + } + + if (timeout < 0) { + printf(SHETHER_NAME ": phy auto-negotiation failed\n"); + ret = -ETIMEDOUT; + goto err_tout; + } + + return ret; + +err_tout: + return ret; +} + +static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd) +{ + int port = eth->port, ret = 0; + u32 val, phy_status; + struct sh_eth_info *port_info = ð->port_info[port]; + struct eth_device *dev = port_info->dev; + + /* Configure e-dmac registers */ + outl((inl(EDMR(port)) & ~EMDR_DESC_R) | EDMR_EL, EDMR(port)); + outl(0, EESIPR(port)); + outl(0, TRSCER(port)); + outl(0, TFTR(port)); + outl((FIFO_SIZE_T | FIFO_SIZE_R), FDR(port)); + outl(RMCR_RST, RMCR(port)); + outl(0, RPADIR(port)); + outl((FIFO_F_D_RFF | FIFO_F_D_RFD), FCFTR(port)); + + /* Configure e-mac registers */ + outl(0, ECSIPR(port)); + + /* Set Mac address */ + val = dev->enetaddr[0] << 24 | dev->enetaddr[1] << 16 | + dev->enetaddr[2] << 8 | dev->enetaddr[3]; + outl(val, MAHR(port)); + + val = dev->enetaddr[4] << 8 | dev->enetaddr[5]; + outl(val, MALR(port)); + + outl(RFLR_RFL_MIN, RFLR(port)); + outl(0, PIPR(port)); + outl(APR_AP, APR(port)); + outl(MPR_MP, MPR(port)); + outl(TPAUSER_TPAUSE, TPAUSER(port)); + + /* Configure phy */ + ret = sh_eth_phy_config(eth); + if (ret) { + printf(SHETHER_NAME ": phy config timeout\n"); + goto err_phy_cfg; + } + /* Read phy status to finish configuring the e-mac */ + phy_status = sh_eth_mii_read_phy_reg(port, port_info->phy_addr, 1); + + /* Set the transfer speed */ + if (phy_status & (PHY_S_100X_F|PHY_S_100X_H)) { + printf(SHETHER_NAME ": 100Base/"); + outl(GECMR_100B, GECMR(port)); + } else { + printf(SHETHER_NAME ": 10Base/"); + outl(GECMR_10B, GECMR(port)); + } + + /* Check if full duplex mode is supported by the phy */ + if (phy_status & (PHY_S_100X_F|PHY_S_10T_F)) { + printf("Full\n"); + outl((ECMR_CHG_DM|ECMR_RE|ECMR_TE|ECMR_DM), ECMR(port)); + } else { + printf("Half\n"); + outl((ECMR_CHG_DM|ECMR_RE|ECMR_TE), ECMR(port)); + } + + return ret; + +err_phy_cfg: + return ret; +} + +static void sh_eth_start(struct sh_eth_dev *eth) +{ + /* + * Enable the e-dmac receiver only. The transmitter will be enabled when + * we have something to transmit + */ + outl(EDRRR_R, EDRRR(eth->port)); +} + +static void sh_eth_stop(struct sh_eth_dev *eth) +{ + outl(~EDRRR_R, EDRRR(eth->port)); +} + +int sh_eth_init(struct eth_device *dev, bd_t *bd) +{ + int ret = 0; + struct sh_eth_dev *eth = dev->priv; + + ret = sh_eth_reset(eth); + if (ret) + goto err; + + ret = sh_eth_desc_init(eth); + if (ret) + goto err; + + ret = sh_eth_config(eth, bd); + if (ret) + goto err_config; + + sh_eth_start(eth); + + return ret; + +err_config: + sh_eth_tx_desc_free(eth); + sh_eth_rx_desc_free(eth); + +err: + return ret; +} + +void sh_eth_halt(struct eth_device *dev) +{ + struct sh_eth_dev *eth = dev->priv; + sh_eth_stop(eth); +} + +int sh_eth_initialize(bd_t *bd) +{ + int ret = 0; + struct sh_eth_dev *eth = NULL; + struct eth_device *dev = NULL; + + eth = (struct sh_eth_dev *)malloc(sizeof(struct sh_eth_dev)); + if (!eth) { + printf(SHETHER_NAME ": %s: malloc failed\n", __func__); + ret = -ENOMEM; + goto err; + } + + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!dev) { + printf(SHETHER_NAME ": %s: malloc failed\n", __func__); + ret = -ENOMEM; + goto err; + } + memset(dev, 0, sizeof(struct eth_device)); + memset(eth, 0, sizeof(struct sh_eth_dev)); + + eth->port = CONFIG_SH_ETHER_USE_PORT; + eth->port_info[eth->port].phy_addr = CONFIG_SH_ETHER_PHY_ADDR; + + dev->priv = (void *)eth; + dev->iobase = 0; + dev->init = sh_eth_init; + dev->halt = sh_eth_halt; + dev->send = sh_eth_send; + dev->recv = sh_eth_recv; + eth->port_info[eth->port].dev = dev; + + sprintf(dev->name, SHETHER_NAME); + + /* Register Device to EtherNet subsystem */ + eth_register(dev); + + if (!eth_getenv_enetaddr("ethaddr", dev->enetaddr)) + puts("Please set MAC address\n"); + + return ret; + +err: + if (dev) + free(dev); + + if (eth) + free(eth); + + printf(SHETHER_NAME ": Failed\n"); + return ret; +} diff --git a/roms/u-boot-sam460ex/drivers/net/sh_eth.h b/roms/u-boot-sam460ex/drivers/net/sh_eth.h new file mode 100644 index 000000000..e153849e3 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/sh_eth.h @@ -0,0 +1,448 @@ +/* + * sh_eth.h - Driver for Renesas SH7763's gigabit ethernet controler. + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Copyright (c) 2008 Nobuhiro Iwamatsu + * Copyright (c) 2007 Carlos Munoz <carlos@kenati.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <netdev.h> +#include <asm/types.h> + +#define SHETHER_NAME "sh_eth" + +/* Malloc returns addresses in the P1 area (cacheable). However we need to + use area P2 (non-cacheable) */ +#define ADDR_TO_P2(addr) ((((int)(addr) & ~0xe0000000) | 0xa0000000)) + +/* The ethernet controller needs to use physical addresses */ +#define ADDR_TO_PHY(addr) ((int)(addr) & ~0xe0000000) + +/* Number of supported ports */ +#define MAX_PORT_NUM 2 + +/* Buffers must be big enough to hold the largest ethernet frame. Also, rx + buffers must be a multiple of 32 bytes */ +#define MAX_BUF_SIZE (48 * 32) + +/* The number of tx descriptors must be large enough to point to 5 or more + frames. If each frame uses 2 descriptors, at least 10 descriptors are needed. + We use one descriptor per frame */ +#define NUM_TX_DESC 8 + +/* The size of the tx descriptor is determined by how much padding is used. + 4, 20, or 52 bytes of padding can be used */ +#define TX_DESC_PADDING 4 +#define TX_DESC_SIZE (12 + TX_DESC_PADDING) + +/* Tx descriptor. We always use 3 bytes of padding */ +struct tx_desc_s { + volatile u32 td0; + u32 td1; + u32 td2; /* Buffer start */ + u32 padding; +}; + +/* There is no limitation in the number of rx descriptors */ +#define NUM_RX_DESC 8 + +/* The size of the rx descriptor is determined by how much padding is used. + 4, 20, or 52 bytes of padding can be used */ +#define RX_DESC_PADDING 4 +#define RX_DESC_SIZE (12 + RX_DESC_PADDING) + +/* Rx descriptor. We always use 4 bytes of padding */ +struct rx_desc_s { + volatile u32 rd0; + volatile u32 rd1; + u32 rd2; /* Buffer start */ + u32 padding; +}; + +struct sh_eth_info { + struct tx_desc_s *tx_desc_malloc; + struct tx_desc_s *tx_desc_base; + struct tx_desc_s *tx_desc_cur; + struct rx_desc_s *rx_desc_malloc; + struct rx_desc_s *rx_desc_base; + struct rx_desc_s *rx_desc_cur; + u8 *rx_buf_malloc; + u8 *rx_buf_base; + u8 mac_addr[6]; + u8 phy_addr; + struct eth_device *dev; +}; + +struct sh_eth_dev { + int port; + struct sh_eth_info port_info[MAX_PORT_NUM]; +}; + +/* Register Address */ +#define BASE_IO_ADDR 0xfee00000 + +#define EDSR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0000) + +#define TDLAR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0010) +#define TDFAR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0014) +#define TDFXR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0018) +#define TDFFR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x001c) + +#define RDLAR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0030) +#define RDFAR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0034) +#define RDFXR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0038) +#define RDFFR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x003c) + +#define EDMR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0400) +#define EDTRR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0408) +#define EDRRR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0410) +#define EESR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0428) +#define EESIPR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0430) +#define TRSCER(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0438) +#define TFTR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0448) +#define FDR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0450) +#define RMCR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0458) +#define RPADIR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0460) +#define FCFTR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0468) +#define ECMR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0500) +#define RFLR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0508) +#define ECSIPR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0518) +#define PIR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0520) +#define PIPR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x052c) +#define APR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0554) +#define MPR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0558) +#define TPAUSER(port) (BASE_IO_ADDR + 0x800 * (port) + 0x0564) +#define GECMR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x05b0) +#define MALR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x05c8) +#define MAHR(port) (BASE_IO_ADDR + 0x800 * (port) + 0x05c0) + +/* + * Register's bits + * Copy from Linux driver source code + */ +#ifdef CONFIG_CPU_SH7763 +/* EDSR */ +enum EDSR_BIT { + EDSR_ENT = 0x01, EDSR_ENR = 0x02, +}; +#define EDSR_ENALL (EDSR_ENT|EDSR_ENR) +#endif + +/* EDMR */ +enum DMAC_M_BIT { + EDMR_DL1 = 0x20, EDMR_DL0 = 0x10, +#ifdef CONFIG_CPU_SH7763 + EDMR_SRST = 0x03, + EMDR_DESC_R = 0x30, /* Descriptor reserve size */ + EDMR_EL = 0x40, /* Litte endian */ +#else /* CONFIG_CPU_SH7763 */ + EDMR_SRST = 0x01, +#endif +}; + +/* RFLR */ +#define RFLR_RFL_MIN 0x05EE /* Recv Frame length 1518 byte */ + +/* EDTRR */ +enum DMAC_T_BIT { +#ifdef CONFIG_CPU_SH7763 + EDTRR_TRNS = 0x03, +#else + EDTRR_TRNS = 0x01, +#endif +}; + +/* GECMR */ +enum GECMR_BIT { + GECMR_1000B = 0x01, GECMR_100B = 0x04, GECMR_10B = 0x00, +}; + +/* EDRRR*/ +enum EDRRR_R_BIT { + EDRRR_R = 0x01, +}; + +/* TPAUSER */ +enum TPAUSER_BIT { + TPAUSER_TPAUSE = 0x0000ffff, + TPAUSER_UNLIMITED = 0, +}; + +/* BCFR */ +enum BCFR_BIT { + BCFR_RPAUSE = 0x0000ffff, + BCFR_UNLIMITED = 0, +}; + +/* PIR */ +enum PIR_BIT { + PIR_MDI = 0x08, PIR_MDO = 0x04, PIR_MMD = 0x02, PIR_MDC = 0x01, +}; + +/* PSR */ +enum PHY_STATUS_BIT { PHY_ST_LINK = 0x01, }; + +/* EESR */ +enum EESR_BIT { +#ifndef CONFIG_CPU_SH7763 + EESR_TWB = 0x40000000, +#else + EESR_TWB = 0xC0000000, + EESR_TC1 = 0x20000000, + EESR_TUC = 0x10000000, + EESR_ROC = 0x80000000, +#endif + EESR_TABT = 0x04000000, + EESR_RABT = 0x02000000, EESR_RFRMER = 0x01000000, +#ifndef CONFIG_CPU_SH7763 + EESR_ADE = 0x00800000, +#endif + EESR_ECI = 0x00400000, + EESR_FTC = 0x00200000, EESR_TDE = 0x00100000, + EESR_TFE = 0x00080000, EESR_FRC = 0x00040000, + EESR_RDE = 0x00020000, EESR_RFE = 0x00010000, +#ifndef CONFIG_CPU_SH7763 + EESR_CND = 0x00000800, +#endif + EESR_DLC = 0x00000400, + EESR_CD = 0x00000200, EESR_RTO = 0x00000100, + EESR_RMAF = 0x00000080, EESR_CEEF = 0x00000040, + EESR_CELF = 0x00000020, EESR_RRF = 0x00000010, + rESR_RTLF = 0x00000008, EESR_RTSF = 0x00000004, + EESR_PRE = 0x00000002, EESR_CERF = 0x00000001, +}; + + +#ifdef CONFIG_CPU_SH7763 +# define TX_CHECK (EESR_TC1 | EESR_FTC) +# define EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RDE \ + | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI) +# define TX_ERROR_CEHCK (EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE) + +#else +# define TX_CHECK (EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO) +# define EESR_ERR_CHECK (EESR_TWB | EESR_TABT | EESR_RABT | EESR_RDE \ + | EESR_RFRMER | EESR_ADE | EESR_TFE | EESR_TDE | EESR_ECI) +# define TX_ERROR_CEHCK (EESR_TWB | EESR_TABT | EESR_ADE | EESR_TDE | EESR_TFE) +#endif + +/* EESIPR */ +enum DMAC_IM_BIT { + DMAC_M_TWB = 0x40000000, DMAC_M_TABT = 0x04000000, + DMAC_M_RABT = 0x02000000, + DMAC_M_RFRMER = 0x01000000, DMAC_M_ADF = 0x00800000, + DMAC_M_ECI = 0x00400000, DMAC_M_FTC = 0x00200000, + DMAC_M_TDE = 0x00100000, DMAC_M_TFE = 0x00080000, + DMAC_M_FRC = 0x00040000, DMAC_M_RDE = 0x00020000, + DMAC_M_RFE = 0x00010000, DMAC_M_TINT4 = 0x00000800, + DMAC_M_TINT3 = 0x00000400, DMAC_M_TINT2 = 0x00000200, + DMAC_M_TINT1 = 0x00000100, DMAC_M_RINT8 = 0x00000080, + DMAC_M_RINT5 = 0x00000010, DMAC_M_RINT4 = 0x00000008, + DMAC_M_RINT3 = 0x00000004, DMAC_M_RINT2 = 0x00000002, + DMAC_M_RINT1 = 0x00000001, +}; + +/* Receive descriptor bit */ +enum RD_STS_BIT { + RD_RACT = 0x80000000, RD_RDLE = 0x40000000, + RD_RFP1 = 0x20000000, RD_RFP0 = 0x10000000, + RD_RFE = 0x08000000, RD_RFS10 = 0x00000200, + RD_RFS9 = 0x00000100, RD_RFS8 = 0x00000080, + RD_RFS7 = 0x00000040, RD_RFS6 = 0x00000020, + RD_RFS5 = 0x00000010, RD_RFS4 = 0x00000008, + RD_RFS3 = 0x00000004, RD_RFS2 = 0x00000002, + RD_RFS1 = 0x00000001, +}; +#define RDF1ST RD_RFP1 +#define RDFEND RD_RFP0 +#define RD_RFP (RD_RFP1|RD_RFP0) + +/* RDFFR*/ +enum RDFFR_BIT { + RDFFR_RDLF = 0x01, +}; + +/* FCFTR */ +enum FCFTR_BIT { + FCFTR_RFF2 = 0x00040000, FCFTR_RFF1 = 0x00020000, + FCFTR_RFF0 = 0x00010000, FCFTR_RFD2 = 0x00000004, + FCFTR_RFD1 = 0x00000002, FCFTR_RFD0 = 0x00000001, +}; +#define FIFO_F_D_RFF (FCFTR_RFF2|FCFTR_RFF1|FCFTR_RFF0) +#define FIFO_F_D_RFD (FCFTR_RFD2|FCFTR_RFD1|FCFTR_RFD0) + +/* Transfer descriptor bit */ +enum TD_STS_BIT { +#ifdef CONFIG_CPU_SH7763 + TD_TACT = 0x80000000, +#else + TD_TACT = 0x7fffffff, +#endif + TD_TDLE = 0x40000000, TD_TFP1 = 0x20000000, + TD_TFP0 = 0x10000000, +}; +#define TDF1ST TD_TFP1 +#define TDFEND TD_TFP0 +#define TD_TFP (TD_TFP1|TD_TFP0) + +/* RMCR */ +enum RECV_RST_BIT { RMCR_RST = 0x01, }; +/* ECMR */ +enum FELIC_MODE_BIT { +#ifdef CONFIG_CPU_SH7763 + ECMR_TRCCM=0x04000000, ECMR_RCSC= 0x00800000, ECMR_DPAD= 0x00200000, + ECMR_RZPF = 0x00100000, +#endif + ECMR_ZPF = 0x00080000, ECMR_PFR = 0x00040000, ECMR_RXF = 0x00020000, + ECMR_TXF = 0x00010000, ECMR_MCT = 0x00002000, ECMR_PRCEF = 0x00001000, + ECMR_PMDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020, + ECMR_ILB = 0x00000008, ECMR_ELB = 0x00000004, ECMR_DM = 0x00000002, + ECMR_PRM = 0x00000001, +}; + +#ifdef CONFIG_CPU_SH7763 +#define ECMR_CHG_DM (ECMR_TRCCM | ECMR_RZPF | ECMR_ZPF | ECMR_PFR | ECMR_RXF | \ + ECMR_TXF | ECMR_MCT) +#else +#define ECMR_CHG_DM (ECMR_ZPF | ECMR_PFR ECMR_RXF | ECMR_TXF | ECMR_MCT) +#endif + +/* ECSR */ +enum ECSR_STATUS_BIT { +#ifndef CONFIG_CPU_SH7763 + ECSR_BRCRX = 0x20, ECSR_PSRTO = 0x10, +#endif + ECSR_LCHNG = 0x04, + ECSR_MPD = 0x02, ECSR_ICD = 0x01, +}; + +#ifdef CONFIG_CPU_SH7763 +# define ECSR_INIT (ECSR_ICD | ECSIPR_MPDIP) +#else +# define ECSR_INIT (ECSR_BRCRX | ECSR_PSRTO | \ + ECSR_LCHNG | ECSR_ICD | ECSIPR_MPDIP) +#endif + +/* ECSIPR */ +enum ECSIPR_STATUS_MASK_BIT { +#ifndef CONFIG_CPU_SH7763 + ECSIPR_BRCRXIP = 0x20, ECSIPR_PSRTOIP = 0x10, +#endif + ECSIPR_LCHNGIP = 0x04, + ECSIPR_MPDIP = 0x02, ECSIPR_ICDIP = 0x01, +}; + +#ifdef CONFIG_CPU_SH7763 +# define ECSIPR_INIT (ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP) +#else +# define ECSIPR_INIT (ECSIPR_BRCRXIP | ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | \ + ECSIPR_ICDIP | ECSIPR_MPDIP) +#endif + +/* APR */ +enum APR_BIT { + APR_AP = 0x00000004, +}; + +/* MPR */ +enum MPR_BIT { + MPR_MP = 0x00000006, +}; + +/* TRSCER */ +enum DESC_I_BIT { + DESC_I_TINT4 = 0x0800, DESC_I_TINT3 = 0x0400, DESC_I_TINT2 = 0x0200, + DESC_I_TINT1 = 0x0100, DESC_I_RINT8 = 0x0080, DESC_I_RINT5 = 0x0010, + DESC_I_RINT4 = 0x0008, DESC_I_RINT3 = 0x0004, DESC_I_RINT2 = 0x0002, + DESC_I_RINT1 = 0x0001, +}; + +/* RPADIR */ +enum RPADIR_BIT { + RPADIR_PADS1 = 0x20000, RPADIR_PADS0 = 0x10000, + RPADIR_PADR = 0x0003f, +}; + +#ifdef CONFIG_CPU_SH7763 +# define RPADIR_INIT (0x00) +#else +# define RPADIR_INIT (RPADIR_PADS1) +#endif + +/* FDR */ +enum FIFO_SIZE_BIT { + FIFO_SIZE_T = 0x00000700, FIFO_SIZE_R = 0x00000007, +}; + +enum PHY_OFFSETS { + PHY_CTRL = 0, PHY_STAT = 1, PHY_IDT1 = 2, PHY_IDT2 = 3, + PHY_ANA = 4, PHY_ANL = 5, PHY_ANE = 6, + PHY_16 = 16, +}; + +/* PHY_CTRL */ +enum PHY_CTRL_BIT { + PHY_C_RESET = 0x8000, PHY_C_LOOPBK = 0x4000, PHY_C_SPEEDSL = 0x2000, + PHY_C_ANEGEN = 0x1000, PHY_C_PWRDN = 0x0800, PHY_C_ISO = 0x0400, + PHY_C_RANEG = 0x0200, PHY_C_DUPLEX = 0x0100, PHY_C_COLT = 0x0080, +}; +#define DM9161_PHY_C_ANEGEN 0 /* auto nego special */ + +/* PHY_STAT */ +enum PHY_STAT_BIT { + PHY_S_100T4 = 0x8000, PHY_S_100X_F = 0x4000, PHY_S_100X_H = 0x2000, + PHY_S_10T_F = 0x1000, PHY_S_10T_H = 0x0800, PHY_S_ANEGC = 0x0020, + PHY_S_RFAULT = 0x0010, PHY_S_ANEGA = 0x0008, PHY_S_LINK = 0x0004, + PHY_S_JAB = 0x0002, PHY_S_EXTD = 0x0001, +}; + +/* PHY_ANA */ +enum PHY_ANA_BIT { + PHY_A_NP = 0x8000, PHY_A_ACK = 0x4000, PHY_A_RF = 0x2000, + PHY_A_FCS = 0x0400, PHY_A_T4 = 0x0200, PHY_A_FDX = 0x0100, + PHY_A_HDX = 0x0080, PHY_A_10FDX = 0x0040, PHY_A_10HDX = 0x0020, + PHY_A_SEL = 0x001e, + PHY_A_EXT = 0x0001, +}; + +/* PHY_ANL */ +enum PHY_ANL_BIT { + PHY_L_NP = 0x8000, PHY_L_ACK = 0x4000, PHY_L_RF = 0x2000, + PHY_L_FCS = 0x0400, PHY_L_T4 = 0x0200, PHY_L_FDX = 0x0100, + PHY_L_HDX = 0x0080, PHY_L_10FDX = 0x0040, PHY_L_10HDX = 0x0020, + PHY_L_SEL = 0x001f, +}; + +/* PHY_ANE */ +enum PHY_ANE_BIT { + PHY_E_PDF = 0x0010, PHY_E_LPNPA = 0x0008, PHY_E_NPA = 0x0004, + PHY_E_PRX = 0x0002, PHY_E_LPANEGA = 0x0001, +}; + +/* DM9161 */ +enum PHY_16_BIT { + PHY_16_BP4B45 = 0x8000, PHY_16_BPSCR = 0x4000, PHY_16_BPALIGN = 0x2000, + PHY_16_BP_ADPOK = 0x1000, PHY_16_Repeatmode = 0x0800, + PHY_16_TXselect = 0x0400, + PHY_16_Rsvd = 0x0200, PHY_16_RMIIEnable = 0x0100, + PHY_16_Force100LNK = 0x0080, + PHY_16_APDLED_CTL = 0x0040, PHY_16_COLLED_CTL = 0x0020, + PHY_16_RPDCTR_EN = 0x0010, + PHY_16_ResetStMch = 0x0008, PHY_16_PreamSupr = 0x0004, + PHY_16_Sleepmode = 0x0002, + PHY_16_RemoteLoopOut = 0x0001, +}; diff --git a/roms/u-boot-sam460ex/drivers/net/smc91111.c b/roms/u-boot-sam460ex/drivers/net/smc91111.c new file mode 100644 index 000000000..54a1bfb2a --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/smc91111.c @@ -0,0 +1,1381 @@ +/*------------------------------------------------------------------------ + . smc91111.c + . This is a driver for SMSC's 91C111 single-chip Ethernet device. + . + . (C) Copyright 2002 + . Sysgo Real-Time Solutions, GmbH <www.elinos.com> + . Rolf Offermanns <rof@sysgo.de> + . + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + . This program is free software; you can redistribute it and/or modify + . it under the terms of the GNU General Public License as published by + . the Free Software Foundation; either version 2 of the License, or + . (at your option) any later version. + . + . This program is distributed in the hope that it will be useful, + . but WITHOUT ANY WARRANTY; without even the implied warranty of + . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + . GNU General Public License for more details. + . + . You should have received a copy of the GNU General Public License + . along with this program; if not, write to the Free Software + . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . + . "Features" of the SMC chip: + . Integrated PHY/MAC for 10/100BaseT Operation + . Supports internal and external MII + . Integrated 8K packet memory + . EEPROM interface for configuration + . + . Arguments: + . io = for the base address + . irq = for the IRQ + . + . author: + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . + . + . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + . + . Sources: + . o SMSC LAN91C111 databook (www.smsc.com) + . o smc9194.c by Erik Stahlman + . o skeleton.c by Donald Becker ( becker@cesdis.gsfc.nasa.gov ) + . + . History: + . 06/19/03 Richard Woodruff Made u-boot environment aware and added mac addr checks. + . 10/17/01 Marco Hasewinkel Modify for DNP/1110 + . 07/25/01 Woojung Huh Modify for ADS Bitsy + . 04/25/01 Daris A Nevil Initial public release through SMSC + . 03/16/01 Daris A Nevil Modified smc9194.c for use with LAN91C111 + ----------------------------------------------------------------------------*/ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <malloc.h> +#include "smc91111.h" +#include <net.h> + +/* Use power-down feature of the chip */ +#define POWER_DOWN 0 + +#define NO_AUTOPROBE + +#define SMC_DEBUG 0 + +#if SMC_DEBUG > 1 +static const char version[] = + "smc91111.c:v1.0 04/25/01 by Daris A Nevil (dnevil@snmc.com)\n"; +#endif + +/* Autonegotiation timeout in seconds */ +#ifndef CONFIG_SMC_AUTONEG_TIMEOUT +#define CONFIG_SMC_AUTONEG_TIMEOUT 10 +#endif + +/*------------------------------------------------------------------------ + . + . Configuration options, for the experienced user to change. + . + -------------------------------------------------------------------------*/ + +/* + . Wait time for memory to be free. This probably shouldn't be + . tuned that much, as waiting for this means nothing else happens + . in the system +*/ +#define MEMORY_WAIT_TIME 16 + + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(args...) printf(args) +#else +#define PRINTK3(args...) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(args...) printf(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(args...) printf(args) +#else +#define PRINTK(args...) +#endif + + +/*------------------------------------------------------------------------ + . + . The internal workings of the driver. If you are changing anything + . here with the SMC stuff, you should have the datasheet and know + . what you are doing. + . + -------------------------------------------------------------------------*/ + +/* Memory sizing constant */ +#define LAN91C111_MEMORY_MULTIPLIER (1024*2) + +#ifndef CONFIG_SMC91111_BASE +#error "SMC91111 Base address must be passed to initialization funciton" +/* #define CONFIG_SMC91111_BASE 0x20000300 */ +#endif + +#define SMC_DEV_NAME "SMC91111" +#define SMC_PHY_ADDR 0x0000 +#define SMC_ALLOC_MAX_TRY 5 +#define SMC_TX_TIMEOUT 30 + +#define SMC_PHY_CLOCK_DELAY 1000 + +#define ETH_ZLEN 60 + +#ifdef CONFIG_SMC_USE_32_BIT +#define USE_32_BIT 1 +#else +#undef USE_32_BIT +#endif + +#ifdef SHARED_RESOURCES +extern void swap_to(int device_id); +#else +# define swap_to(x) +#endif + +#ifndef CONFIG_SMC91111_EXT_PHY +static void smc_phy_configure(struct eth_device *dev); +#endif /* !CONFIG_SMC91111_EXT_PHY */ + +/* + ------------------------------------------------------------ + . + . Internal routines + . + ------------------------------------------------------------ +*/ + +#ifdef CONFIG_SMC_USE_IOFUNCS +/* + * input and output functions + * + * Implemented due to inx,outx macros accessing the device improperly + * and putting the device into an unkown state. + * + * For instance, on Sharp LPD7A400 SDK, affects were chip memory + * could not be free'd (hence the alloc failures), duplicate packets, + * packets being corrupt (shifted) on the wire, etc. Switching to the + * inx,outx functions fixed this problem. + */ + +#define barrier() __asm__ __volatile__("": : :"memory") + +static inline word SMC_inw(struct eth_device *dev, dword offset) +{ + word v; + v = *((volatile word*)(dev->iobase + offset)); + barrier(); *(volatile u32*)(0xc0000000); + return v; +} + +static inline void SMC_outw(struct eth_device *dev, word value, dword offset) +{ + *((volatile word*)(dev->iobase + offset)) = value; + barrier(); *(volatile u32*)(0xc0000000); +} + +static inline byte SMC_inb(struct eth_device *dev, dword offset) +{ + word _w; + + _w = SMC_inw(dev, offset & ~((dword)1)); + return (offset & 1) ? (byte)(_w >> 8) : (byte)(_w); +} + +static inline void SMC_outb(struct eth_device *dev, byte value, dword offset) +{ + word _w; + + _w = SMC_inw(dev, offset & ~((dword)1)); + if (offset & 1) + *((volatile word*)(dev->iobase + (offset & ~((dword)1)))) = + (value<<8) | (_w & 0x00ff); + else + *((volatile word*)(dev->iobase + offset)) = + value | (_w & 0xff00); +} + +static inline void SMC_insw(struct eth_device *dev, dword offset, + volatile uchar* buf, dword len) +{ + volatile word *p = (volatile word *)buf; + + while (len-- > 0) { + *p++ = SMC_inw(dev, offset); + barrier(); + *((volatile u32*)(0xc0000000)); + } +} + +static inline void SMC_outsw(struct eth_device *dev, dword offset, + uchar* buf, dword len) +{ + volatile word *p = (volatile word *)buf; + + while (len-- > 0) { + SMC_outw(dev, *p++, offset); + barrier(); + *(volatile u32*)(0xc0000000); + } +} +#endif /* CONFIG_SMC_USE_IOFUNCS */ + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet( byte *, int ); +#endif + +#define tx_done(dev) 1 + +static int poll4int (struct eth_device *dev, byte mask, int timeout) +{ + int tmo = get_timer (0) + timeout * CONFIG_SYS_HZ; + int is_timeout = 0; + word old_bank = SMC_inw (dev, BSR_REG); + + PRINTK2 ("Polling...\n"); + SMC_SELECT_BANK (dev, 2); + while ((SMC_inw (dev, SMC91111_INT_REG) & mask) == 0) { + if (get_timer (0) >= tmo) { + is_timeout = 1; + break; + } + } + + /* restore old bank selection */ + SMC_SELECT_BANK (dev, old_bank); + + if (is_timeout) + return 1; + else + return 0; +} + +/* Only one release command at a time, please */ +static inline void smc_wait_mmu_release_complete (struct eth_device *dev) +{ + int count = 0; + + /* assume bank 2 selected */ + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) { + udelay (1); /* Wait until not busy */ + if (++count > 200) + break; + } +} + +/* + . Function: smc_reset( void ) + . Purpose: + . This sets the SMC91111 chip to its normal state, hopefully from whatever + . mess that any other DOS driver has put it in. + . + . Maybe I should reset more registers to defaults in here? SOFTRST should + . do that for me. + . + . Method: + . 1. send a SOFT RESET + . 2. wait for it to finish + . 3. enable autorelease mode + . 4. reset the memory management unit + . 5. clear all interrupts + . +*/ +static void smc_reset (struct eth_device *dev) +{ + PRINTK2 ("%s: smc_reset\n", SMC_DEV_NAME); + + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK (dev, 0); + SMC_outw (dev, RCR_SOFTRST, RCR_REG); + + /* Setup the Configuration Register */ + /* This is necessary because the CONFIG_REG is not affected */ + /* by a soft reset */ + + SMC_SELECT_BANK (dev, 1); +#if defined(CONFIG_SMC91111_EXT_PHY) + SMC_outw (dev, CONFIG_DEFAULT | CONFIG_EXT_PHY, CONFIG_REG); +#else + SMC_outw (dev, CONFIG_DEFAULT, CONFIG_REG); +#endif + + + /* Release from possible power-down state */ + /* Configuration register is not affected by Soft Reset */ + SMC_outw (dev, SMC_inw (dev, CONFIG_REG) | CONFIG_EPH_POWER_EN, + CONFIG_REG); + + SMC_SELECT_BANK (dev, 0); + + /* this should pause enough for the chip to be happy */ + udelay (10); + + /* Disable transmit and receive functionality */ + SMC_outw (dev, RCR_CLEAR, RCR_REG); + SMC_outw (dev, TCR_CLEAR, TCR_REG); + + /* set the control register */ + SMC_SELECT_BANK (dev, 1); + SMC_outw (dev, CTL_DEFAULT, CTL_REG); + + /* Reset the MMU */ + SMC_SELECT_BANK (dev, 2); + smc_wait_mmu_release_complete (dev); + SMC_outw (dev, MC_RESET, MMU_CMD_REG); + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) + udelay (1); /* Wait until not busy */ + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + but this is a place where future chipsets _COULD_ break. Be wary + of issuing another MMU command right after this */ + + /* Disable all interrupts */ + SMC_outb (dev, 0, IM_REG); +} + +/* + . Function: smc_enable + . Purpose: let the chip talk to the outside work + . Method: + . 1. Enable the transmitter + . 2. Enable the receiver + . 3. Enable interrupts +*/ +static void smc_enable(struct eth_device *dev) +{ + PRINTK2("%s: smc_enable\n", SMC_DEV_NAME); + SMC_SELECT_BANK( dev, 0 ); + /* see the header file for options in TCR/RCR DEFAULT*/ + SMC_outw( dev, TCR_DEFAULT, TCR_REG ); + SMC_outw( dev, RCR_DEFAULT, RCR_REG ); + + /* clear MII_DIS */ +/* smc_write_phy_register(PHY_CNTL_REG, 0x0000); */ +} + +/* + . Function: smc_halt + . Purpose: closes down the SMC91xxx chip. + . Method: + . 1. zero the interrupt mask + . 2. clear the enable receive flag + . 3. clear the enable xmit flags + . + . TODO: + . (1) maybe utilize power down mode. + . Why not yet? Because while the chip will go into power down mode, + . the manual says that it will wake up in response to any I/O requests + . in the register space. Empirical results do not show this working. +*/ +static void smc_halt(struct eth_device *dev) +{ + PRINTK2("%s: smc_halt\n", SMC_DEV_NAME); + + /* no more interrupts for me */ + SMC_SELECT_BANK( dev, 2 ); + SMC_outb( dev, 0, IM_REG ); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK( dev, 0 ); + SMC_outb( dev, RCR_CLEAR, RCR_REG ); + SMC_outb( dev, TCR_CLEAR, TCR_REG ); + + swap_to(FLASH); +} + + +/* + . Function: smc_send(struct net_device * ) + . Purpose: + . This sends the actual packet to the SMC9xxx chip. + . + . Algorithm: + . First, see if a saved_skb is available. + . ( this should NOT be called if there is no 'saved_skb' + . Now, find the packet number that the chip allocated + . Point the data pointers at it in memory + . Set the length word in the chip's memory + . Dump the packet to chip memory + . Check if a last byte is needed ( odd length packet ) + . if so, set the control flag right + . Tell the card to send it + . Enable the transmit interrupt, so I know if it failed + . Free the kernel data if I actually sent it. +*/ +static int smc_send(struct eth_device *dev, volatile void *packet, + int packet_length) +{ + byte packet_no; + byte *buf; + int length; + int numPages; + int try = 0; + int time_out; + byte status; + byte saved_pnr; + word saved_ptr; + + /* save PTR and PNR registers before manipulation */ + SMC_SELECT_BANK (dev, 2); + saved_pnr = SMC_inb( dev, PN_REG ); + saved_ptr = SMC_inw( dev, PTR_REG ); + + PRINTK3 ("%s: smc_hardware_send_packet\n", SMC_DEV_NAME); + + length = ETH_ZLEN < packet_length ? packet_length : ETH_ZLEN; + + /* allocate memory + ** The MMU wants the number of pages to be the number of 256 bytes + ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** + ** The 91C111 ignores the size bits, but the code is left intact + ** for backwards and future compatibility. + ** + ** Pkt size for allocating is data length +6 (for additional status + ** words, length and ctl!) + ** + ** If odd size then last byte is included in this header. + */ + numPages = ((length & 0xfffe) + 6); + numPages >>= 8; /* Divide by 256 */ + + if (numPages > 7) { + printf ("%s: Far too big packet error. \n", SMC_DEV_NAME); + return 0; + } + + /* now, try to allocate the memory */ + SMC_SELECT_BANK (dev, 2); + SMC_outw (dev, MC_ALLOC | numPages, MMU_CMD_REG); + + /* FIXME: the ALLOC_INT bit never gets set * + * so the following will always give a * + * memory allocation error. * + * same code works in armboot though * + * -ro + */ + +again: + try++; + time_out = MEMORY_WAIT_TIME; + do { + status = SMC_inb (dev, SMC91111_INT_REG); + if (status & IM_ALLOC_INT) { + /* acknowledge the interrupt */ + SMC_outb (dev, IM_ALLOC_INT, SMC91111_INT_REG); + break; + } + } while (--time_out); + + if (!time_out) { + PRINTK2 ("%s: memory allocation, try %d failed ...\n", + SMC_DEV_NAME, try); + if (try < SMC_ALLOC_MAX_TRY) + goto again; + else + return 0; + } + + PRINTK2 ("%s: memory allocation, try %d succeeded ...\n", + SMC_DEV_NAME, try); + + buf = (byte *) packet; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = SMC_inb (dev, AR_REG); + if (packet_no & AR_FAILED) { + /* or isn't there? BAD CHIP! */ + printf ("%s: Memory allocation failed. \n", SMC_DEV_NAME); + return 0; + } + + /* we have a packet address, so tell the card to use it */ +#ifndef CONFIG_XAENIAX + SMC_outb (dev, packet_no, PN_REG); +#else + /* On Xaeniax board, we can't use SMC_outb here because that way + * the Allocate MMU command will end up written to the command register + * as well, which will lead to a problem. + */ + SMC_outl (dev, packet_no << 16, 0); +#endif + /* do not write new ptr value if Write data fifo not empty */ + while ( saved_ptr & PTR_NOTEMPTY ) + printf ("Write data fifo not empty!\n"); + + /* point to the beginning of the packet */ + SMC_outw (dev, PTR_AUTOINC, PTR_REG); + + PRINTK3 ("%s: Trying to xmit packet of length %x\n", + SMC_DEV_NAME, length); + +#if SMC_DEBUG > 2 + printf ("Transmitting Packet\n"); + print_packet (buf, length); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ) */ +#ifdef USE_32_BIT + SMC_outl (dev, (length + 6) << 16, SMC91111_DATA_REG); +#else + SMC_outw (dev, 0, SMC91111_DATA_REG); + /* send the packet length ( +6 for status words, length, and ctl */ + SMC_outw (dev, (length + 6), SMC91111_DATA_REG); +#endif + + /* send the actual data + . I _think_ it's faster to send the longs first, and then + . mop up by sending the last word. It depends heavily + . on alignment, at least on the 486. Maybe it would be + . a good idea to check which is optimal? But that could take + . almost as much time as is saved? + */ +#ifdef USE_32_BIT + SMC_outsl (dev, SMC91111_DATA_REG, buf, length >> 2); +#ifndef CONFIG_XAENIAX + if (length & 0x2) + SMC_outw (dev, *((word *) (buf + (length & 0xFFFFFFFC))), + SMC91111_DATA_REG); +#else + /* On XANEIAX, we can only use 32-bit writes, so we need to handle + * unaligned tail part specially. The standard code doesn't work. + */ + if ((length & 3) == 3) { + u16 * ptr = (u16*) &buf[length-3]; + SMC_outl(dev, (*ptr) | ((0x2000 | buf[length-1]) << 16), + SMC91111_DATA_REG); + } else if ((length & 2) == 2) { + u16 * ptr = (u16*) &buf[length-2]; + SMC_outl(dev, *ptr, SMC91111_DATA_REG); + } else if (length & 1) { + SMC_outl(dev, (0x2000 | buf[length-1]), SMC91111_DATA_REG); + } else { + SMC_outl(dev, 0, SMC91111_DATA_REG); + } +#endif +#else + SMC_outsw (dev, SMC91111_DATA_REG, buf, (length) >> 1); +#endif /* USE_32_BIT */ + +#ifndef CONFIG_XAENIAX + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) { + SMC_outw (dev, 0, SMC91111_DATA_REG); + } else { + SMC_outw (dev, buf[length - 1] | 0x2000, SMC91111_DATA_REG); + } +#endif + + /* and let the chipset deal with it */ + SMC_outw (dev, MC_ENQUEUE, MMU_CMD_REG); + + /* poll for TX INT */ + /* if (poll4int (dev, IM_TX_INT, SMC_TX_TIMEOUT)) { */ + /* poll for TX_EMPTY INT - autorelease enabled */ + if (poll4int(dev, IM_TX_EMPTY_INT, SMC_TX_TIMEOUT)) { + /* sending failed */ + PRINTK2 ("%s: TX timeout, sending failed...\n", SMC_DEV_NAME); + + /* release packet */ + /* no need to release, MMU does that now */ +#ifdef CONFIG_XAENIAX + SMC_outw (dev, MC_FREEPKT, MMU_CMD_REG); +#endif + + /* wait for MMU getting ready (low) */ + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) { + udelay (10); + } + + PRINTK2 ("MMU ready\n"); + + + return 0; + } else { + /* ack. int */ + SMC_outb (dev, IM_TX_EMPTY_INT, SMC91111_INT_REG); + /* SMC_outb (IM_TX_INT, SMC91111_INT_REG); */ + PRINTK2 ("%s: Sent packet of length %d \n", SMC_DEV_NAME, + length); + + /* release packet */ + /* no need to release, MMU does that now */ +#ifdef CONFIG_XAENIAX + SMC_outw (dev, MC_FREEPKT, MMU_CMD_REG); +#endif + + /* wait for MMU getting ready (low) */ + while (SMC_inw (dev, MMU_CMD_REG) & MC_BUSY) { + udelay (10); + } + + PRINTK2 ("MMU ready\n"); + + + } + + /* restore previously saved registers */ +#ifndef CONFIG_XAENIAX + SMC_outb( dev, saved_pnr, PN_REG ); +#else + /* On Xaeniax board, we can't use SMC_outb here because that way + * the Allocate MMU command will end up written to the command register + * as well, which will lead to a problem. + */ + SMC_outl(dev, saved_pnr << 16, 0); +#endif + SMC_outw( dev, saved_ptr, PTR_REG ); + + return length; +} + +/* + * Open and Initialize the board + * + * Set up everything, reset the card, etc .. + * + */ +static int smc_init(struct eth_device *dev, bd_t *bd) +{ + int i; + + swap_to(ETHERNET); + + PRINTK2 ("%s: smc_init\n", SMC_DEV_NAME); + + /* reset the hardware */ + smc_reset (dev); + smc_enable (dev); + + /* Configure the PHY */ +#ifndef CONFIG_SMC91111_EXT_PHY + smc_phy_configure (dev); +#endif + + /* conservative setting (10Mbps, HalfDuplex, no AutoNeg.) */ +/* SMC_SELECT_BANK(dev, 0); */ +/* SMC_outw(dev, 0, RPC_REG); */ + SMC_SELECT_BANK (dev, 1); + +#ifdef USE_32_BIT + for (i = 0; i < 6; i += 2) { + word address; + + address = dev->enetaddr[i + 1] << 8; + address |= dev->enetaddr[i]; + SMC_outw(dev, address, (ADDR0_REG + i)); + } +#else + for (i = 0; i < 6; i++) + SMC_outb(dev, dev->enetaddr[i], (ADDR0_REG + i)); +#endif + + printf(SMC_DEV_NAME ": MAC %pM\n", dev->enetaddr); + + return 0; +} + +/*------------------------------------------------------------- + . + . smc_rcv - receive a packet from the card + . + . There is ( at least ) a packet waiting to be read from + . chip-memory. + . + . o Read the status + . o If an error, record it + . o otherwise, read in the packet + -------------------------------------------------------------- +*/ +static int smc_rcv(struct eth_device *dev) +{ + int packet_number; + word status; + word packet_length; + int is_error = 0; +#ifdef USE_32_BIT + dword stat_len; +#endif + byte saved_pnr; + word saved_ptr; + + SMC_SELECT_BANK(dev, 2); + /* save PTR and PTR registers */ + saved_pnr = SMC_inb( dev, PN_REG ); + saved_ptr = SMC_inw( dev, PTR_REG ); + + packet_number = SMC_inw( dev, RXFIFO_REG ); + + if ( packet_number & RXFIFO_REMPTY ) { + + return 0; + } + + PRINTK3("%s: smc_rcv\n", SMC_DEV_NAME); + /* start reading from the start of the packet */ + SMC_outw( dev, PTR_READ | PTR_RCV | PTR_AUTOINC, PTR_REG ); + + /* First two words are status and packet_length */ +#ifdef USE_32_BIT + stat_len = SMC_inl(dev, SMC91111_DATA_REG); + status = stat_len & 0xffff; + packet_length = stat_len >> 16; +#else + status = SMC_inw( dev, SMC91111_DATA_REG ); + packet_length = SMC_inw( dev, SMC91111_DATA_REG ); +#endif + + packet_length &= 0x07ff; /* mask off top bits */ + + PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ); + + if ( !(status & RS_ERRORS ) ){ + /* Adjust for having already read the first two words */ + packet_length -= 4; /*4; */ + + + /* set odd length for bug in LAN91C111, */ + /* which never sets RS_ODDFRAME */ + /* TODO ? */ + + +#ifdef USE_32_BIT + PRINTK3(" Reading %d dwords (and %d bytes) \n", + packet_length >> 2, packet_length & 3 ); + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + SMC_insl( dev, SMC91111_DATA_REG, NetRxPackets[0], + packet_length >> 2 ); + /* read the left over bytes */ + if (packet_length & 3) { + int i; + + byte *tail = (byte *)(NetRxPackets[0] + + (packet_length & ~3)); + dword leftover = SMC_inl(dev, SMC91111_DATA_REG); + for (i=0; i<(packet_length & 3); i++) + *tail++ = (byte) (leftover >> (8*i)) & 0xff; + } +#else + PRINTK3(" Reading %d words and %d byte(s) \n", + (packet_length >> 1 ), packet_length & 1 ); + SMC_insw(dev, SMC91111_DATA_REG , NetRxPackets[0], + packet_length >> 1); + +#endif /* USE_32_BIT */ + +#if SMC_DEBUG > 2 + printf("Receiving Packet\n"); + print_packet( NetRxPackets[0], packet_length ); +#endif + } else { + /* error ... */ + /* TODO ? */ + is_error = 1; + } + + while ( SMC_inw( dev, MMU_CMD_REG ) & MC_BUSY ) + udelay(1); /* Wait until not busy */ + + /* error or good, tell the card to get rid of this packet */ + SMC_outw( dev, MC_RELEASE, MMU_CMD_REG ); + + while ( SMC_inw( dev, MMU_CMD_REG ) & MC_BUSY ) + udelay(1); /* Wait until not busy */ + + /* restore saved registers */ +#ifndef CONFIG_XAENIAX + SMC_outb( dev, saved_pnr, PN_REG ); +#else + /* On Xaeniax board, we can't use SMC_outb here because that way + * the Allocate MMU command will end up written to the command register + * as well, which will lead to a problem. + */ + SMC_outl( dev, saved_pnr << 16, 0); +#endif + SMC_outw( dev, saved_ptr, PTR_REG ); + + if (!is_error) { + /* Pass the packet up to the protocol layers. */ + NetReceive(NetRxPackets[0], packet_length); + return packet_length; + } else { + return 0; + } + +} + + +#if 0 +/*------------------------------------------------------------ + . Modify a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static word smc_modify_regbit(struct eth_device *dev, int bank, int ioaddr, int reg, + unsigned int bit, int val) +{ + word regval; + + SMC_SELECT_BANK( dev, bank ); + + regval = SMC_inw( dev, reg ); + if (val) + regval |= bit; + else + regval &= ~bit; + + SMC_outw( dev, regval, 0 ); + return(regval); +} + + +/*------------------------------------------------------------ + . Retrieve a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static int smc_get_regbit(struct eth_device *dev, int bank, int ioaddr, int reg, unsigned int bit) +{ + SMC_SELECT_BANK( dev, bank ); + if ( SMC_inw( dev, reg ) & bit) + return(1); + else + return(0); +} + + +/*------------------------------------------------------------ + . Modify a LAN91C111 register (word access only) + .-------------------------------------------------------------*/ +static void smc_modify_reg(struct eth_device *dev, int bank, int ioaddr, int reg, word val) +{ + SMC_SELECT_BANK( dev, bank ); + SMC_outw( dev, val, reg ); +} + + +/*------------------------------------------------------------ + . Retrieve a LAN91C111 register (word access only) + .-------------------------------------------------------------*/ +static int smc_get_reg(struct eth_device *dev, int bank, int ioaddr, int reg) +{ + SMC_SELECT_BANK( dev, bank ); + return(SMC_inw( dev, reg )); +} + +#endif /* 0 */ + +/*---PHY CONTROL AND CONFIGURATION----------------------------------------- */ + +#if (SMC_DEBUG > 2 ) + +/*------------------------------------------------------------ + . Debugging function for viewing MII Management serial bitstream + .-------------------------------------------------------------*/ +static void smc_dump_mii_stream (byte * bits, int size) +{ + int i; + + printf ("BIT#:"); + for (i = 0; i < size; ++i) { + printf ("%d", i % 10); + } + + printf ("\nMDOE:"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDOE) + printf ("1"); + else + printf ("0"); + } + + printf ("\nMDO :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDO) + printf ("1"); + else + printf ("0"); + } + + printf ("\nMDI :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDI) + printf ("1"); + else + printf ("0"); + } + + printf ("\n"); +} +#endif + +/*------------------------------------------------------------ + . Reads a register from the MII Management serial interface + .-------------------------------------------------------------*/ +#ifndef CONFIG_SMC91111_EXT_PHY +static word smc_read_phy_register (struct eth_device *dev, byte phyreg) +{ + int oldBank; + int i; + byte mask; + word mii_reg; + byte bits[64]; + int clk_idx = 0; + int input_idx; + word phydata; + byte phyaddr = SMC_PHY_ADDR; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Start code <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Read command <10> */ + bits[clk_idx++] = MII_MDOE | MII_MDO; + bits[clk_idx++] = MII_MDOE; + + /* Output the PHY address, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Output the phy register number, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + /*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; + + /* Save the current bank */ + oldBank = SMC_inw (dev, BANK_SELECT); + + /* Select bank 3 */ + SMC_SELECT_BANK (dev, 3); + + /* Get the current MII register value */ + mii_reg = SMC_inw (dev, MII_REG); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + /* Clock all 64 cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + SMC_outw (dev, mii_reg | bits[i], MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + + /* Clock Hi - input data */ + SMC_outw (dev, mii_reg | bits[i] | MII_MCLK, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + bits[i] |= SMC_inw (dev, MII_REG) & MII_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + SMC_outw (dev, mii_reg, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + /* Restore original bank select */ + SMC_SELECT_BANK (dev, oldBank); + + /* Recover input data */ + phydata = 0; + for (i = 0; i < 16; ++i) { + phydata <<= 1; + + if (bits[input_idx++] & MII_MDI) + phydata |= 0x0001; + } + +#if (SMC_DEBUG > 2 ) + printf ("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream (bits, sizeof bits); +#endif + + return (phydata); +} + + +/*------------------------------------------------------------ + . Writes a register to the MII Management serial interface + .-------------------------------------------------------------*/ +static void smc_write_phy_register (struct eth_device *dev, byte phyreg, + word phydata) +{ + int oldBank; + int i; + word mask; + word mii_reg; + byte bits[65]; + int clk_idx = 0; + byte phyaddr = SMC_PHY_ADDR; + + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Start code <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Write command <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + /* Output the PHY address, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Output the phy register number, msb first */ + mask = (byte) 0x10; + for (i = 0; i < 5; ++i) { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Tristate and turnaround (2 bit times) */ + 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++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + /* Shift to next lowest bit */ + mask >>= 1; + } + + /* Final clock bit (tristate) */ + bits[clk_idx++] = 0; + + /* Save the current bank */ + oldBank = SMC_inw (dev, BANK_SELECT); + + /* Select bank 3 */ + SMC_SELECT_BANK (dev, 3); + + /* Get the current MII register value */ + mii_reg = SMC_inw (dev, MII_REG); + + /* Turn off all MII Interface bits */ + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + /* Clock all cycles */ + for (i = 0; i < sizeof bits; ++i) { + /* Clock Low - output data */ + SMC_outw (dev, mii_reg | bits[i], MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + + /* Clock Hi - input data */ + SMC_outw (dev, mii_reg | bits[i] | MII_MCLK, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + bits[i] |= SMC_inw (dev, MII_REG) & MII_MDI; + } + + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + SMC_outw (dev, mii_reg, MII_REG); + udelay (SMC_PHY_CLOCK_DELAY); + + /* Restore original bank select */ + SMC_SELECT_BANK (dev, oldBank); + +#if (SMC_DEBUG > 2 ) + printf ("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream (bits, sizeof bits); +#endif +} +#endif /* !CONFIG_SMC91111_EXT_PHY */ + + +/*------------------------------------------------------------ + . Waits the specified number of milliseconds - kernel friendly + .-------------------------------------------------------------*/ +#ifndef CONFIG_SMC91111_EXT_PHY +static void smc_wait_ms(unsigned int ms) +{ + udelay(ms*1000); +} +#endif /* !CONFIG_SMC91111_EXT_PHY */ + + +/*------------------------------------------------------------ + . Configures the specified PHY using Autonegotiation. Calls + . smc_phy_fixed() if the user has requested a certain config. + .-------------------------------------------------------------*/ +#ifndef CONFIG_SMC91111_EXT_PHY +static void smc_phy_configure (struct eth_device *dev) +{ + int timeout; + byte phyaddr; + word my_phy_caps; /* My PHY capabilities */ + word my_ad_caps; /* My Advertised capabilities */ + word status = 0; /*;my status = 0 */ + int failed = 0; + + PRINTK3 ("%s: smc_program_phy()\n", SMC_DEV_NAME); + + + /* Get the detected phy address */ + phyaddr = SMC_PHY_ADDR; + + /* Reset the PHY, setting all other bits to zero */ + smc_write_phy_register (dev, PHY_CNTL_REG, PHY_CNTL_RST); + + /* Wait for the reset to complete, or time out */ + timeout = 6; /* Wait up to 3 seconds */ + while (timeout--) { + if (!(smc_read_phy_register (dev, PHY_CNTL_REG) + & PHY_CNTL_RST)) { + /* reset complete */ + break; + } + + smc_wait_ms (500); /* wait 500 millisecs */ + } + + if (timeout < 1) { + printf ("%s:PHY reset timed out\n", SMC_DEV_NAME); + goto smc_phy_configure_exit; + } + + /* Read PHY Register 18, Status Output */ + /* lp->lastPhy18 = smc_read_phy_register(PHY_INT_REG); */ + + /* Enable PHY Interrupts (for register 18) */ + /* Interrupts listed here are disabled */ + smc_write_phy_register (dev, PHY_MASK_REG, 0xffff); + + /* Configure the Receive/Phy Control register */ + SMC_SELECT_BANK (dev, 0); + SMC_outw (dev, RPC_DEFAULT, RPC_REG); + + /* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */ + my_phy_caps = smc_read_phy_register (dev, PHY_STAT_REG); + my_ad_caps = PHY_AD_CSMA; /* I am CSMA capable */ + + if (my_phy_caps & PHY_STAT_CAP_T4) + my_ad_caps |= PHY_AD_T4; + + if (my_phy_caps & PHY_STAT_CAP_TXF) + my_ad_caps |= PHY_AD_TX_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TXH) + my_ad_caps |= PHY_AD_TX_HDX; + + if (my_phy_caps & PHY_STAT_CAP_TF) + my_ad_caps |= PHY_AD_10_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TH) + my_ad_caps |= PHY_AD_10_HDX; + + /* Update our Auto-Neg Advertisement Register */ + smc_write_phy_register (dev, PHY_AD_REG, my_ad_caps); + + /* Read the register back. Without this, it appears that when */ + /* auto-negotiation is restarted, sometimes it isn't ready and */ + /* the link does not come up. */ + smc_read_phy_register(dev, PHY_AD_REG); + + PRINTK2 ("%s: phy caps=%x\n", SMC_DEV_NAME, my_phy_caps); + PRINTK2 ("%s: phy advertised caps=%x\n", SMC_DEV_NAME, my_ad_caps); + + /* Restart auto-negotiation process in order to advertise my caps */ + smc_write_phy_register (dev, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST); + + /* Wait for the auto-negotiation to complete. This may take from */ + /* 2 to 3 seconds. */ + /* Wait for the reset to complete, or time out */ + timeout = CONFIG_SMC_AUTONEG_TIMEOUT * 2; + while (timeout--) { + + status = smc_read_phy_register (dev, PHY_STAT_REG); + if (status & PHY_STAT_ANEG_ACK) { + /* auto-negotiate complete */ + break; + } + + smc_wait_ms (500); /* wait 500 millisecs */ + + /* Restart auto-negotiation if remote fault */ + if (status & PHY_STAT_REM_FLT) { + printf ("%s: PHY remote fault detected\n", + SMC_DEV_NAME); + + /* Restart auto-negotiation */ + printf ("%s: PHY restarting auto-negotiation\n", + SMC_DEV_NAME); + smc_write_phy_register (dev, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | + PHY_CNTL_ANEG_RST | + PHY_CNTL_SPEED | + PHY_CNTL_DPLX); + } + } + + if (timeout < 1) { + printf ("%s: PHY auto-negotiate timed out\n", SMC_DEV_NAME); + failed = 1; + } + + /* Fail if we detected an auto-negotiate remote fault */ + if (status & PHY_STAT_REM_FLT) { + printf ("%s: PHY remote fault detected\n", SMC_DEV_NAME); + failed = 1; + } + + /* Re-Configure the Receive/Phy Control register */ + SMC_outw (dev, RPC_DEFAULT, RPC_REG); + +smc_phy_configure_exit: ; + +} +#endif /* !CONFIG_SMC91111_EXT_PHY */ + + +#if SMC_DEBUG > 2 +static void print_packet( byte * buf, int length ) +{ + int i; + int remainder; + int lines; + + printf("Packet of length %d \n", length ); + +#if SMC_DEBUG > 3 + lines = length / 16; + remainder = length % 16; + + for ( i = 0; i < lines ; i ++ ) { + int cur; + + for ( cur = 0; cur < 8; cur ++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printf("%02x%02x ", a, b ); + } + printf("\n"); + } + for ( i = 0; i < remainder/2 ; i++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printf("%02x%02x ", a, b ); + } + printf("\n"); +#endif +} +#endif + +int smc91111_initialize(u8 dev_num, int base_addr) +{ + struct smc91111_priv *priv; + struct eth_device *dev; + int i; + + priv = malloc(sizeof(*priv)); + if (!priv) + return 0; + dev = malloc(sizeof(*dev)); + if (!dev) { + free(priv); + return 0; + } + + priv->dev_num = dev_num; + dev->priv = priv; + dev->iobase = base_addr; + + swap_to(ETHERNET); + SMC_SELECT_BANK(dev, 1); + for (i = 0; i < 6; ++i) + dev->enetaddr[i] = SMC_inb(dev, (ADDR0_REG + i)); + swap_to(FLASH); + + dev->init = smc_init; + dev->halt = smc_halt; + dev->send = smc_send; + dev->recv = smc_rcv; + sprintf(dev->name, "%s-%hu", SMC_DEV_NAME, dev_num); + + eth_register(dev); + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/smc91111.h b/roms/u-boot-sam460ex/drivers/net/smc91111.h new file mode 100644 index 000000000..895c74978 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/smc91111.h @@ -0,0 +1,792 @@ +/*------------------------------------------------------------------------ + . smc91111.h - macros for the LAN91C111 Ethernet Driver + . + . (C) Copyright 2002 + . Sysgo Real-Time Solutions, GmbH <www.elinos.com> + . Rolf Offermanns <rof@sysgo.de> + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + . This program is free software; you can redistribute it and/or modify + . it under the terms of the GNU General Public License as published by + . the Free Software Foundation; either version 2 of the License, or + . (at your option) any later version. + . + . This program is distributed in the hope that it will be useful, + . but WITHOUT ANY WARRANTY; without even the implied warranty of + . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + . GNU General Public License for more details. + . + . You should have received a copy of the GNU General Public License + . along with this program; if not, write to the Free Software + . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + . + . This file contains register information and access macros for + . the LAN91C111 single chip ethernet controller. It is a modified + . version of the smc9194.h file. + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . Authors + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . + . History + . 03/16/01 Daris A Nevil Modified for use with LAN91C111 device + . + ---------------------------------------------------------------------------*/ +#ifndef _SMC91111_H_ +#define _SMC91111_H_ + +#include <asm/types.h> +#include <config.h> + +/* + * This function may be called by the board specific initialisation code + * in order to override the default mac address. + */ + +void smc_set_mac_addr (const unsigned char *addr); + + +/* I want some simple types */ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; + +struct smc91111_priv{ + u8 dev_num; +}; + +/* + . DEBUGGING LEVELS + . + . 0 for normal operation + . 1 for slightly more details + . >2 for various levels of increasingly useless information + . 2 for interrupt tracking, status flags + . 3 for packet info + . 4 for complete packet dumps +*/ +/*#define SMC_DEBUG 0 */ + +/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */ + +#define SMC_IO_EXTENT 16 + +#ifdef CONFIG_PXA250 + +#ifdef CONFIG_XSENGINE +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+((r)<<1)))) +#define SMC_inw(a,r) (*((volatile word *)((a)->iobase+((r)<<1)))) +#define SMC_inb(a,p) ({ \ + unsigned int __p = (unsigned int)((a)->iobase + ((p)<<1)); \ + unsigned int __v = *(volatile unsigned short *)((__p) & ~2); \ + if (__p & 2) __v >>= 8; \ + else __v &= 0xff; \ + __v; }) +#elif defined(CONFIG_XAENIAX) +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r)))) +#define SMC_inw(a,z) ({ \ + unsigned int __p = (unsigned int)((a)->iobase + (z)); \ + unsigned int __v = *(volatile unsigned int *)((__p) & ~3); \ + if (__p & 3) __v >>= 16; \ + else __v &= 0xffff; \ + __v; }) +#define SMC_inb(a,p) ({ \ + unsigned int ___v = SMC_inw((a),(p) & ~1); \ + if ((p) & 1) ___v >>= 8; \ + else ___v &= 0xff; \ + ___v; }) +#else +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r)))) +#define SMC_inw(a,r) (*((volatile word *)((a)->iobase+(r)))) +#define SMC_inb(a,p) ({ \ + unsigned int __p = (unsigned int)((a)->iobase + (p)); \ + unsigned int __v = *(volatile unsigned short *)((__p) & ~1); \ + if (__p & 1) __v >>= 8; \ + else __v &= 0xff; \ + __v; }) +#endif + +#ifdef CONFIG_XSENGINE +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r<<1))) = d) +#define SMC_outw(a,d,r) (*((volatile word *)((a)->iobase+(r<<1))) = d) +#elif defined (CONFIG_XAENIAX) +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r))) = d) +#define SMC_outw(a,d,p) ({ \ + dword __dwo = SMC_inl((a),(p) & ~3); \ + dword __dwn = (word)(d); \ + __dwo &= ((p) & 3) ? 0x0000ffff : 0xffff0000; \ + __dwo |= ((p) & 3) ? __dwn << 16 : __dwn; \ + SMC_outl((a), __dwo, (p) & ~3); \ +}) +#else +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r))) = d) +#define SMC_outw(a,d,r) (*((volatile word *)((a)->iobase+(r))) = d) +#endif + +#define SMC_outb(a,d,r) ({ word __d = (byte)(d); \ + word __w = SMC_inw((a),(r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw((a),__w,(r)&~1); \ + }) + +#define SMC_outsl(a,r,b,l) ({ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl((a), *(__b2 + __i), r); \ + } \ + }) + +#define SMC_outsw(a,r,b,l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw((a), *(__b2 + __i), r); \ + } \ + }) + +#define SMC_insl(a,r,b,l) ({ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl((a),(r)); \ + SMC_inl((a),0); \ + }; \ + }) + +#define SMC_insw(a,r,b,l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw((a),(r)); \ + SMC_inw((a),0); \ + }; \ + }) + +#define SMC_insb(a,r,b,l) ({ int __i ; \ + byte *__b2; \ + __b2 = (byte *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inb((a),(r)); \ + SMC_inb((a),0); \ + }; \ + }) + +#elif defined(CONFIG_LEON) /* if not CONFIG_PXA250 */ + +#define SMC_LEON_SWAP16(_x_) ({ word _x = (_x_); ((_x << 8) | (_x >> 8)); }) + +#define SMC_LEON_SWAP32(_x_) \ + ({ dword _x = (_x_); \ + ((_x << 24) | \ + ((0x0000FF00UL & _x) << 8) | \ + ((0x00FF0000UL & _x) >> 8) | \ + (_x >> 24)); }) + +#define SMC_inl(a,r) (SMC_LEON_SWAP32((*(volatile dword *)((a)->iobase+((r)<<0))))) +#define SMC_inl_nosw(a,r) ((*(volatile dword *)((a)->iobase+((r)<<0)))) +#define SMC_inw(a,r) (SMC_LEON_SWAP16((*(volatile word *)((a)->iobase+((r)<<0))))) +#define SMC_inw_nosw(a,r) ((*(volatile word *)((a)->iobase+((r)<<0)))) +#define SMC_inb(a,p) ({ \ + word ___v = SMC_inw((a),(p) & ~1); \ + if ((p) & 1) ___v >>= 8; \ + else ___v &= 0xff; \ + ___v; }) + +#define SMC_outl(a,d,r) (*(volatile dword *)((a)->iobase+((r)<<0))=SMC_LEON_SWAP32(d)) +#define SMC_outl_nosw(a,d,r) (*(volatile dword *)((a)->iobase+((r)<<0))=(d)) +#define SMC_outw(a,d,r) (*(volatile word *)((a)->iobase+((r)<<0))=SMC_LEON_SWAP16(d)) +#define SMC_outw_nosw(a,d,r) (*(volatile word *)((a)->iobase+((r)<<0))=(d)) +#define SMC_outb(a,d,r) do{ word __d = (byte)(d); \ + word __w = SMC_inw((a),(r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw((a),__w,(r)&~1); \ + }while(0) +#define SMC_outsl(a,r,b,l) do{ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl_nosw((a), *(__b2 + __i), r); \ + } \ + }while(0) +#define SMC_outsw(a,r,b,l) do{ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw_nosw((a), *(__b2 + __i), r); \ + } \ + }while(0) +#define SMC_insl(a,r,b,l) do{ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl_nosw((a),(r)); \ + }; \ + }while(0) + +#define SMC_insw(a,r,b,l) do{ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw_nosw((a),(r)); \ + }; \ + }while(0) + +#define SMC_insb(a,r,b,l) do{ int __i ; \ + byte *__b2; \ + __b2 = (byte *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inb((a),(r)); \ + }; \ + }while(0) + +#else /* if not CONFIG_PXA250 and not CONFIG_LEON */ + +#ifndef CONFIG_SMC_USE_IOFUNCS /* these macros don't work on some boards */ +/* + * We have only 16 Bit PCMCIA access on Socket 0 + */ + +#ifdef CONFIG_ADNPESC1 +#define SMC_inw(a,r) (*((volatile word *)((a)->iobase+((r)<<1)))) +#elif CONFIG_BLACKFIN +#define SMC_inw(a,r) ({ word __v = (*((volatile word *)((a)->iobase+(r)))); SSYNC(); __v;}) +#else +#define SMC_inw(a,r) (*((volatile word *)((a)->iobase+(r)))) +#endif +#define SMC_inb(a,r) (((r)&1) ? SMC_inw((a),(r)&~1)>>8 : SMC_inw((a),(r)&0xFF)) + +#ifdef CONFIG_ADNPESC1 +#define SMC_outw(a,d,r) (*((volatile word *)((a)->iobase+((r)<<1))) = d) +#elif CONFIG_BLACKFIN +#define SMC_outw(a,d,r) {(*((volatile word *)((a)->iobase+(r))) = d); SSYNC();} +#else +#define SMC_outw(a,d,r) (*((volatile word *)((a)->iobase+(r))) = d) +#endif +#define SMC_outb(a,d,r) ({ word __d = (byte)(d); \ + word __w = SMC_inw((a),(r)&~1); \ + __w &= ((r)&1) ? 0x00FF : 0xFF00; \ + __w |= ((r)&1) ? __d<<8 : __d; \ + SMC_outw((a),__w,(r)&~1); \ + }) +#if 0 +#define SMC_outsw(a,r,b,l) outsw((a)->iobase+(r), (b), (l)) +#else +#define SMC_outsw(a,r,b,l) ({ int __i; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw((a), *(__b2 + __i), r); \ + } \ + }) +#endif + +#if 0 +#define SMC_insw(a,r,b,l) insw((a)->iobase+(r), (b), (l)) +#else +#define SMC_insw(a,r,b,l) ({ int __i ; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw((a),(r)); \ + SMC_inw((a),0); \ + }; \ + }) +#endif + +#endif /* CONFIG_SMC_USE_IOFUNCS */ + +#if defined(CONFIG_SMC_USE_32_BIT) + +#ifdef CONFIG_XSENGINE +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r<<1)))) +#else +#define SMC_inl(a,r) (*((volatile dword *)((a)->iobase+(r)))) +#endif + +#define SMC_insl(a,r,b,l) ({ int __i ; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inl((a),(r)); \ + SMC_inl((a),0); \ + }; \ + }) + +#ifdef CONFIG_XSENGINE +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r<<1))) = d) +#else +#define SMC_outl(a,d,r) (*((volatile dword *)((a)->iobase+(r))) = d) +#endif +#define SMC_outsl(a,r,b,l) ({ int __i; \ + dword *__b2; \ + __b2 = (dword *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outl((a), *(__b2 + __i), r); \ + } \ + }) + +#endif /* CONFIG_SMC_USE_32_BIT */ + +#endif + +/*--------------------------------------------------------------- + . + . A description of the SMSC registers is probably in order here, + . although for details, the SMC datasheet is invaluable. + . + . Basically, the chip has 4 banks of registers ( 0 to 3 ), which + . are accessed by writing a number into the BANK_SELECT register + . ( I also use a SMC_SELECT_BANK macro for this ). + . + . The banks are configured so that for most purposes, bank 2 is all + . that is needed for simple run time tasks. + -----------------------------------------------------------------------*/ + +/* + . Bank Select Register: + . + . yyyy yyyy 0000 00xx + . xx = bank number + . yyyy yyyy = 0x33, for identification purposes. +*/ +#define BANK_SELECT 14 + +/* Transmit Control Register */ +/* BANK 0 */ +#define TCR_REG 0x0000 /* transmit control register */ +#define TCR_ENABLE 0x0001 /* When 1 we can transmit */ +#define TCR_LOOP 0x0002 /* Controls output pin LBK */ +#define TCR_FORCOL 0x0004 /* When 1 will force a collision */ +#define TCR_PAD_EN 0x0080 /* When 1 will pad tx frames < 64 bytes w/0 */ +#define TCR_NOCRC 0x0100 /* When 1 will not append CRC to tx frames */ +#define TCR_MON_CSN 0x0400 /* When 1 tx monitors carrier */ +#define TCR_FDUPLX 0x0800 /* When 1 enables full duplex operation */ +#define TCR_STP_SQET 0x1000 /* When 1 stops tx if Signal Quality Error */ +#define TCR_EPH_LOOP 0x2000 /* When 1 enables EPH block loopback */ +#define TCR_SWFDUP 0x8000 /* When 1 enables Switched Full Duplex mode */ + +#define TCR_CLEAR 0 /* do NOTHING */ +/* the default settings for the TCR register : */ +/* QUESTION: do I want to enable padding of short packets ? */ +#define TCR_DEFAULT TCR_ENABLE + + +/* EPH Status Register */ +/* BANK 0 */ +#define EPH_STATUS_REG 0x0002 +#define ES_TX_SUC 0x0001 /* Last TX was successful */ +#define ES_SNGL_COL 0x0002 /* Single collision detected for last tx */ +#define ES_MUL_COL 0x0004 /* Multiple collisions detected for last tx */ +#define ES_LTX_MULT 0x0008 /* Last tx was a multicast */ +#define ES_16COL 0x0010 /* 16 Collisions Reached */ +#define ES_SQET 0x0020 /* Signal Quality Error Test */ +#define ES_LTXBRD 0x0040 /* Last tx was a broadcast */ +#define ES_TXDEFR 0x0080 /* Transmit Deferred */ +#define ES_LATCOL 0x0200 /* Late collision detected on last tx */ +#define ES_LOSTCARR 0x0400 /* Lost Carrier Sense */ +#define ES_EXC_DEF 0x0800 /* Excessive Deferral */ +#define ES_CTR_ROL 0x1000 /* Counter Roll Over indication */ +#define ES_LINK_OK 0x4000 /* Driven by inverted value of nLNK pin */ +#define ES_TXUNRN 0x8000 /* Tx Underrun */ + + +/* Receive Control Register */ +/* BANK 0 */ +#define RCR_REG 0x0004 +#define RCR_RX_ABORT 0x0001 /* Set if a rx frame was aborted */ +#define RCR_PRMS 0x0002 /* Enable promiscuous mode */ +#define RCR_ALMUL 0x0004 /* When set accepts all multicast frames */ +#define RCR_RXEN 0x0100 /* IFF this is set, we can receive packets */ +#define RCR_STRIP_CRC 0x0200 /* When set strips CRC from rx packets */ +#define RCR_ABORT_ENB 0x0200 /* When set will abort rx on collision */ +#define RCR_FILT_CAR 0x0400 /* When set filters leading 12 bit s of carrier */ +#define RCR_SOFTRST 0x8000 /* resets the chip */ + +/* the normal settings for the RCR register : */ +#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN) +#define RCR_CLEAR 0x0 /* set it to a base state */ + +/* Counter Register */ +/* BANK 0 */ +#define COUNTER_REG 0x0006 + +/* Memory Information Register */ +/* BANK 0 */ +#define MIR_REG 0x0008 + +/* Receive/Phy Control Register */ +/* BANK 0 */ +#define RPC_REG 0x000A +#define RPC_SPEED 0x2000 /* When 1 PHY is in 100Mbps mode. */ +#define RPC_DPLX 0x1000 /* When 1 PHY is in Full-Duplex Mode */ +#define RPC_ANEG 0x0800 /* When 1 PHY is in Auto-Negotiate Mode */ +#define RPC_LSXA_SHFT 5 /* Bits to shift LS2A,LS1A,LS0A to lsb */ +#define RPC_LSXB_SHFT 2 /* Bits to get LS2B,LS1B,LS0B to lsb */ +#define RPC_LED_100_10 (0x00) /* LED = 100Mbps OR's with 10Mbps link detect */ +#define RPC_LED_RES (0x01) /* LED = Reserved */ +#define RPC_LED_10 (0x02) /* LED = 10Mbps link detect */ +#define RPC_LED_FD (0x03) /* LED = Full Duplex Mode */ +#define RPC_LED_TX_RX (0x04) /* LED = TX or RX packet occurred */ +#define RPC_LED_100 (0x05) /* LED = 100Mbps link dectect */ +#define RPC_LED_TX (0x06) /* LED = TX packet occurred */ +#define RPC_LED_RX (0x07) /* LED = RX packet occurred */ +#if defined(CONFIG_DK1C20) || defined(CONFIG_DK1S10) +/* buggy schematic: LEDa -> yellow, LEDb --> green */ +#define RPC_DEFAULT ( RPC_SPEED | RPC_DPLX | RPC_ANEG \ + | (RPC_LED_TX_RX << RPC_LSXA_SHFT) \ + | (RPC_LED_100_10 << RPC_LSXB_SHFT) ) +#elif defined(CONFIG_ADNPESC1) +/* SSV ADNP/ESC1 has only one LED: LEDa -> Rx/Tx indicator */ +#define RPC_DEFAULT ( RPC_SPEED | RPC_DPLX | RPC_ANEG \ + | (RPC_LED_TX_RX << RPC_LSXA_SHFT) \ + | (RPC_LED_100_10 << RPC_LSXB_SHFT) ) +#else +/* SMSC reference design: LEDa --> green, LEDb --> yellow */ +#define RPC_DEFAULT ( RPC_SPEED | RPC_DPLX | RPC_ANEG \ + | (RPC_LED_100_10 << RPC_LSXA_SHFT) \ + | (RPC_LED_TX_RX << RPC_LSXB_SHFT) ) +#endif + +/* Bank 0 0x000C is reserved */ + +/* Bank Select Register */ +/* All Banks */ +#define BSR_REG 0x000E + + +/* Configuration Reg */ +/* BANK 1 */ +#define CONFIG_REG 0x0000 +#define CONFIG_EXT_PHY 0x0200 /* 1=external MII, 0=internal Phy */ +#define CONFIG_GPCNTRL 0x0400 /* Inverse value drives pin nCNTRL */ +#define CONFIG_NO_WAIT 0x1000 /* When 1 no extra wait states on ISA bus */ +#define CONFIG_EPH_POWER_EN 0x8000 /* When 0 EPH is placed into low power mode. */ + +/* Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low */ +#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN) + + +/* Base Address Register */ +/* BANK 1 */ +#define BASE_REG 0x0002 + + +/* Individual Address Registers */ +/* BANK 1 */ +#define ADDR0_REG 0x0004 +#define ADDR1_REG 0x0006 +#define ADDR2_REG 0x0008 + + +/* General Purpose Register */ +/* BANK 1 */ +#define GP_REG 0x000A + + +/* Control Register */ +/* BANK 1 */ +#define CTL_REG 0x000C +#define CTL_RCV_BAD 0x4000 /* When 1 bad CRC packets are received */ +#define CTL_AUTO_RELEASE 0x0800 /* When 1 tx pages are released automatically */ +#define CTL_LE_ENABLE 0x0080 /* When 1 enables Link Error interrupt */ +#define CTL_CR_ENABLE 0x0040 /* When 1 enables Counter Rollover interrupt */ +#define CTL_TE_ENABLE 0x0020 /* When 1 enables Transmit Error interrupt */ +#define CTL_EEPROM_SELECT 0x0004 /* Controls EEPROM reload & store */ +#define CTL_RELOAD 0x0002 /* When set reads EEPROM into registers */ +#define CTL_STORE 0x0001 /* When set stores registers into EEPROM */ +#define CTL_DEFAULT (0x1A10) /* Autorelease enabled*/ + +/* MMU Command Register */ +/* BANK 2 */ +#define MMU_CMD_REG 0x0000 +#define MC_BUSY 1 /* When 1 the last release has not completed */ +#define MC_NOP (0<<5) /* No Op */ +#define MC_ALLOC (1<<5) /* OR with number of 256 byte packets */ +#define MC_RESET (2<<5) /* Reset MMU to initial state */ +#define MC_REMOVE (3<<5) /* Remove the current rx packet */ +#define MC_RELEASE (4<<5) /* Remove and release the current rx packet */ +#define MC_FREEPKT (5<<5) /* Release packet in PNR register */ +#define MC_ENQUEUE (6<<5) /* Enqueue the packet for transmit */ +#define MC_RSTTXFIFO (7<<5) /* Reset the TX FIFOs */ + + +/* Packet Number Register */ +/* BANK 2 */ +#define PN_REG 0x0002 + + +/* Allocation Result Register */ +/* BANK 2 */ +#define AR_REG 0x0003 +#define AR_FAILED 0x80 /* Alocation Failed */ + + +/* RX FIFO Ports Register */ +/* BANK 2 */ +#define RXFIFO_REG 0x0004 /* Must be read as a word */ +#define RXFIFO_REMPTY 0x8000 /* RX FIFO Empty */ + + +/* TX FIFO Ports Register */ +/* BANK 2 */ +#define TXFIFO_REG RXFIFO_REG /* Must be read as a word */ +#define TXFIFO_TEMPTY 0x80 /* TX FIFO Empty */ + + +/* Pointer Register */ +/* BANK 2 */ +#define PTR_REG 0x0006 +#define PTR_RCV 0x8000 /* 1=Receive area, 0=Transmit area */ +#define PTR_AUTOINC 0x4000 /* Auto increment the pointer on each access */ +#define PTR_READ 0x2000 /* When 1 the operation is a read */ +#define PTR_NOTEMPTY 0x0800 /* When 1 _do not_ write fifo DATA REG */ + + +/* Data Register */ +/* BANK 2 */ +#define SMC91111_DATA_REG 0x0008 + + +/* Interrupt Status/Acknowledge Register */ +/* BANK 2 */ +#define SMC91111_INT_REG 0x000C + + +/* Interrupt Mask Register */ +/* BANK 2 */ +#define IM_REG 0x000D +#define IM_MDINT 0x80 /* PHY MI Register 18 Interrupt */ +#define IM_ERCV_INT 0x40 /* Early Receive Interrupt */ +#define IM_EPH_INT 0x20 /* Set by Etheret Protocol Handler section */ +#define IM_RX_OVRN_INT 0x10 /* Set by Receiver Overruns */ +#define IM_ALLOC_INT 0x08 /* Set when allocation request is completed */ +#define IM_TX_EMPTY_INT 0x04 /* Set if the TX FIFO goes empty */ +#define IM_TX_INT 0x02 /* Transmit Interrrupt */ +#define IM_RCV_INT 0x01 /* Receive Interrupt */ + + +/* Multicast Table Registers */ +/* BANK 3 */ +#define MCAST_REG1 0x0000 +#define MCAST_REG2 0x0002 +#define MCAST_REG3 0x0004 +#define MCAST_REG4 0x0006 + + +/* Management Interface Register (MII) */ +/* BANK 3 */ +#define MII_REG 0x0008 +#define MII_MSK_CRS100 0x4000 /* Disables CRS100 detection during tx half dup */ +#define MII_MDOE 0x0008 /* MII Output Enable */ +#define MII_MCLK 0x0004 /* MII Clock, pin MDCLK */ +#define MII_MDI 0x0002 /* MII Input, pin MDI */ +#define MII_MDO 0x0001 /* MII Output, pin MDO */ + + +/* Revision Register */ +/* BANK 3 */ +#define REV_REG 0x000A /* ( hi: chip id low: rev # ) */ + + +/* Early RCV Register */ +/* BANK 3 */ +/* this is NOT on SMC9192 */ +#define ERCV_REG 0x000C +#define ERCV_RCV_DISCRD 0x0080 /* When 1 discards a packet being received */ +#define ERCV_THRESHOLD 0x001F /* ERCV Threshold Mask */ + +/* External Register */ +/* BANK 7 */ +#define EXT_REG 0x0000 + + +#define CHIP_9192 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 6 +#define CHIP_91100 7 +#define CHIP_91100FD 8 +#define CHIP_91111FD 9 + +#if 0 +static const char * chip_ids[ 15 ] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + /* 6 */ "SMC91C96", + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + /* 9 */ "SMC91C111", + NULL, NULL, + NULL, NULL, NULL}; +#endif + +/* + . Transmit status bits +*/ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL 0x0200 +#define TS_16COL 0x0010 + +/* + . Receive status bits +*/ +#define RS_ALGNERR 0x8000 +#define RS_BRODCAST 0x4000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 /* bug: the LAN91C111 never sets this on receive */ +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + + +/* PHY Types */ +enum { + PHY_LAN83C183 = 1, /* LAN91C111 Internal PHY */ + PHY_LAN83C180 +}; + + +/* PHY Register Addresses (LAN91C111 Internal PHY) */ + +/* PHY Control Register */ +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 /* 1=PHY Reset */ +#define PHY_CNTL_LPBK 0x4000 /* 1=PHY Loopback */ +#define PHY_CNTL_SPEED 0x2000 /* 1=100Mbps, 0=10Mpbs */ +#define PHY_CNTL_ANEG_EN 0x1000 /* 1=Enable Auto negotiation */ +#define PHY_CNTL_PDN 0x0800 /* 1=PHY Power Down mode */ +#define PHY_CNTL_MII_DIS 0x0400 /* 1=MII 4 bit interface disabled */ +#define PHY_CNTL_ANEG_RST 0x0200 /* 1=Reset Auto negotiate */ +#define PHY_CNTL_DPLX 0x0100 /* 1=Full Duplex, 0=Half Duplex */ +#define PHY_CNTL_COLTST 0x0080 /* 1= MII Colision Test */ + +/* PHY Status Register */ +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 /* 1=100Base-T4 capable */ +#define PHY_STAT_CAP_TXF 0x4000 /* 1=100Base-X full duplex capable */ +#define PHY_STAT_CAP_TXH 0x2000 /* 1=100Base-X half duplex capable */ +#define PHY_STAT_CAP_TF 0x1000 /* 1=10Mbps full duplex capable */ +#define PHY_STAT_CAP_TH 0x0800 /* 1=10Mbps half duplex capable */ +#define PHY_STAT_CAP_SUPR 0x0040 /* 1=recv mgmt frames with not preamble */ +#define PHY_STAT_ANEG_ACK 0x0020 /* 1=ANEG has completed */ +#define PHY_STAT_REM_FLT 0x0010 /* 1=Remote Fault detected */ +#define PHY_STAT_CAP_ANEG 0x0008 /* 1=Auto negotiate capable */ +#define PHY_STAT_LINK 0x0004 /* 1=valid link */ +#define PHY_STAT_JAB 0x0002 /* 1=10Mbps jabber condition */ +#define PHY_STAT_EXREG 0x0001 /* 1=extended registers implemented */ + +/* PHY Identifier Registers */ +#define PHY_ID1_REG 0x02 /* PHY Identifier 1 */ +#define PHY_ID2_REG 0x03 /* PHY Identifier 2 */ + +/* PHY Auto-Negotiation Advertisement Register */ +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 /* 1=PHY requests exchange of Next Page */ +#define PHY_AD_ACK 0x4000 /* 1=got link code word from remote */ +#define PHY_AD_RF 0x2000 /* 1=advertise remote fault */ +#define PHY_AD_T4 0x0200 /* 1=PHY is capable of 100Base-T4 */ +#define PHY_AD_TX_FDX 0x0100 /* 1=PHY is capable of 100Base-TX FDPLX */ +#define PHY_AD_TX_HDX 0x0080 /* 1=PHY is capable of 100Base-TX HDPLX */ +#define PHY_AD_10_FDX 0x0040 /* 1=PHY is capable of 10Base-T FDPLX */ +#define PHY_AD_10_HDX 0x0020 /* 1=PHY is capable of 10Base-T HDPLX */ +#define PHY_AD_CSMA 0x0001 /* 1=PHY is capable of 802.3 CMSA */ + +/* PHY Auto-negotiation Remote End Capability Register */ +#define PHY_RMT_REG 0x05 +/* Uses same bit definitions as PHY_AD_REG */ + +/* PHY Configuration Register 1 */ +#define PHY_CFG1_REG 0x10 +#define PHY_CFG1_LNKDIS 0x8000 /* 1=Rx Link Detect Function disabled */ +#define PHY_CFG1_XMTDIS 0x4000 /* 1=TP Transmitter Disabled */ +#define PHY_CFG1_XMTPDN 0x2000 /* 1=TP Transmitter Powered Down */ +#define PHY_CFG1_BYPSCR 0x0400 /* 1=Bypass scrambler/descrambler */ +#define PHY_CFG1_UNSCDS 0x0200 /* 1=Unscramble Idle Reception Disable */ +#define PHY_CFG1_EQLZR 0x0100 /* 1=Rx Equalizer Disabled */ +#define PHY_CFG1_CABLE 0x0080 /* 1=STP(150ohm), 0=UTP(100ohm) */ +#define PHY_CFG1_RLVL0 0x0040 /* 1=Rx Squelch level reduced by 4.5db */ +#define PHY_CFG1_TLVL_SHIFT 2 /* Transmit Output Level Adjust */ +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 /* Transmitter Rise/Fall time */ + + +/* PHY Configuration Register 2 */ +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 /* 1=Auto Polarity Correction disabled */ +#define PHY_CFG2_JABDIS 0x0010 /* 1=Jabber disabled */ +#define PHY_CFG2_MREG 0x0008 /* 1=Multiple register access (MII mgt) */ +#define PHY_CFG2_INTMDIO 0x0004 /* 1=Interrupt signaled with MDIO pulseo */ + +/* PHY Status Output (and Interrupt status) Register */ +#define PHY_INT_REG 0x12 /* Status Output (Interrupt Status) */ +#define PHY_INT_INT 0x8000 /* 1=bits have changed since last read */ +#define PHY_INT_LNKFAIL 0x4000 /* 1=Link Not detected */ +#define PHY_INT_LOSSSYNC 0x2000 /* 1=Descrambler has lost sync */ +#define PHY_INT_CWRD 0x1000 /* 1=Invalid 4B5B code detected on rx */ +#define PHY_INT_SSD 0x0800 /* 1=No Start Of Stream detected on rx */ +#define PHY_INT_ESD 0x0400 /* 1=No End Of Stream detected on rx */ +#define PHY_INT_RPOL 0x0200 /* 1=Reverse Polarity detected */ +#define PHY_INT_JAB 0x0100 /* 1=Jabber detected */ +#define PHY_INT_SPDDET 0x0080 /* 1=100Base-TX mode, 0=10Base-T mode */ +#define PHY_INT_DPLXDET 0x0040 /* 1=Device in Full Duplex */ + +/* PHY Interrupt/Status Mask Register */ +#define PHY_MASK_REG 0x13 /* Interrupt Mask */ +/* Uses the same bit definitions as PHY_INT_REG */ + + +/*------------------------------------------------------------------------- + . I define some macros to make it easier to do somewhat common + . or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(a,x) { SMC_outw((a), (x), BANK_SELECT ); } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(a,x) {\ + unsigned char mask;\ + SMC_SELECT_BANK((a),2);\ + mask = SMC_inb((a), IM_REG );\ + mask |= (x);\ + SMC_outb( (a), mask, IM_REG ); \ +} + +/* this disables an interrupt from the interrupt mask register */ + +#define SMC_DISABLE_INT(a,x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(2);\ + mask = SMC_inb( (a), IM_REG );\ + mask &= ~(x);\ + SMC_outb( (a), mask, IM_REG ); \ +} + +/*---------------------------------------------------------------------- + . Define the interrupts that I want to receive from the card + . + . I want: + . IM_EPH_INT, for nasty errors + . IM_RCV_INT, for happy received packets + . IM_RX_OVRN_INT, because I have to kick the receiver + . IM_MDINT, for PHY Register 18 Status Changes + --------------------------------------------------------------------------*/ +#define SMC_INTERRUPT_MASK (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | \ + IM_MDINT) + +#endif /* _SMC_91111_H_ */ diff --git a/roms/u-boot-sam460ex/drivers/net/smc911x.c b/roms/u-boot-sam460ex/drivers/net/smc911x.c new file mode 100644 index 000000000..3da4c35fc --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/smc911x.c @@ -0,0 +1,277 @@ +/* + * SMSC LAN9[12]1[567] Network driver + * + * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <net.h> +#include <miiphy.h> + +#include "smc911x.h" + +u32 pkt_data_pull(struct eth_device *dev, u32 addr) \ + __attribute__ ((weak, alias ("smc911x_reg_read"))); +void pkt_data_push(struct eth_device *dev, u32 addr, u32 val) \ + __attribute__ ((weak, alias ("smc911x_reg_write"))); + +#define mdelay(n) udelay((n)*1000) + +static void smc911x_handle_mac_address(struct eth_device *dev) +{ + unsigned long addrh, addrl; + uchar *m = dev->enetaddr; + + addrl = m[0] | (m[1] << 8) | (m[2] << 16) | (m[3] << 24); + addrh = m[4] | (m[5] << 8); + smc911x_set_mac_csr(dev, ADDRL, addrl); + smc911x_set_mac_csr(dev, ADDRH, addrh); + + printf(DRIVERNAME ": MAC %pM\n", m); +} + +static int smc911x_miiphy_read(struct eth_device *dev, + u8 phy, u8 reg, u16 *val) +{ + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + + smc911x_set_mac_csr(dev, MII_ACC, phy << 11 | reg << 6 | + MII_ACC_MII_BUSY); + + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + + *val = smc911x_get_mac_csr(dev, MII_DATA); + + return 0; +} + +static int smc911x_miiphy_write(struct eth_device *dev, + u8 phy, u8 reg, u16 val) +{ + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + + smc911x_set_mac_csr(dev, MII_DATA, val); + smc911x_set_mac_csr(dev, MII_ACC, + phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE); + + while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) + ; + return 0; +} + +static int smc911x_phy_reset(struct eth_device *dev) +{ + u32 reg; + + reg = smc911x_reg_read(dev, PMT_CTRL); + reg &= ~0xfffff030; + reg |= PMT_CTRL_PHY_RST; + smc911x_reg_write(dev, PMT_CTRL, reg); + + mdelay(100); + + return 0; +} + +static void smc911x_phy_configure(struct eth_device *dev) +{ + int timeout; + u16 status; + + smc911x_phy_reset(dev); + + smc911x_miiphy_write(dev, 1, PHY_BMCR, PHY_BMCR_RESET); + mdelay(1); + smc911x_miiphy_write(dev, 1, PHY_ANAR, 0x01e1); + smc911x_miiphy_write(dev, 1, PHY_BMCR, PHY_BMCR_AUTON | + PHY_BMCR_RST_NEG); + + timeout = 5000; + do { + mdelay(1); + if ((timeout--) == 0) + goto err_out; + + if (smc911x_miiphy_read(dev, 1, PHY_BMSR, &status) != 0) + goto err_out; + } while (!(status & PHY_BMSR_LS)); + + printf(DRIVERNAME ": phy initialized\n"); + + return; + +err_out: + printf(DRIVERNAME ": autonegotiation timed out\n"); +} + +static void smc911x_enable(struct eth_device *dev) +{ + /* Enable TX */ + smc911x_reg_write(dev, HW_CFG, 8 << 16 | HW_CFG_SF); + + smc911x_reg_write(dev, GPT_CFG, GPT_CFG_TIMER_EN | 10000); + + smc911x_reg_write(dev, TX_CFG, TX_CFG_TX_ON); + + /* no padding to start of packets */ + smc911x_reg_write(dev, RX_CFG, 0); + + smc911x_set_mac_csr(dev, MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | + MAC_CR_HBDIS); + +} + +static int smc911x_init(struct eth_device *dev, bd_t * bd) +{ + struct chip_id *id = dev->priv; + + printf(DRIVERNAME ": detected %s controller\n", id->name); + + smc911x_reset(dev); + + /* Configure the PHY, initialize the link state */ + smc911x_phy_configure(dev); + + smc911x_handle_mac_address(dev); + + /* Turn on Tx + Rx */ + smc911x_enable(dev); + + return 0; +} + +static int smc911x_send(struct eth_device *dev, + volatile void *packet, int length) +{ + u32 *data = (u32*)packet; + u32 tmplen; + u32 status; + + smc911x_reg_write(dev, TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | + TX_CMD_A_INT_LAST_SEG | length); + smc911x_reg_write(dev, TX_DATA_FIFO, length); + + tmplen = (length + 3) / 4; + + while (tmplen--) + pkt_data_push(dev, TX_DATA_FIFO, *data++); + + /* wait for transmission */ + while (!((smc911x_reg_read(dev, TX_FIFO_INF) & + TX_FIFO_INF_TSUSED) >> 16)); + + /* get status. Ignore 'no carrier' error, it has no meaning for + * full duplex operation + */ + status = smc911x_reg_read(dev, TX_STATUS_FIFO) & + (TX_STS_LOC | TX_STS_LATE_COLL | TX_STS_MANY_COLL | + TX_STS_MANY_DEFER | TX_STS_UNDERRUN); + + if (!status) + return 0; + + printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n", + status & TX_STS_LOC ? "TX_STS_LOC " : "", + status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "", + status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "", + status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "", + status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : ""); + + return -1; +} + +static void smc911x_halt(struct eth_device *dev) +{ + smc911x_reset(dev); +} + +static int smc911x_rx(struct eth_device *dev) +{ + u32 *data = (u32 *)NetRxPackets[0]; + u32 pktlen, tmplen; + u32 status; + + if ((smc911x_reg_read(dev, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) { + status = smc911x_reg_read(dev, RX_STATUS_FIFO); + pktlen = (status & RX_STS_PKT_LEN) >> 16; + + smc911x_reg_write(dev, RX_CFG, 0); + + tmplen = (pktlen + 3) / 4; + while (tmplen--) + *data++ = pkt_data_pull(dev, RX_DATA_FIFO); + + if (status & RX_STS_ES) + printf(DRIVERNAME + ": dropped bad packet. Status: 0x%08x\n", + status); + else + NetReceive(NetRxPackets[0], pktlen); + } + + return 0; +} + +int smc911x_initialize(u8 dev_num, int base_addr) +{ + unsigned long addrl, addrh; + struct eth_device *dev; + + dev = malloc(sizeof(*dev)); + if (!dev) { + return -1; + } + memset(dev, 0, sizeof(*dev)); + + dev->iobase = base_addr; + + /* Try to detect chip. Will fail if not present. */ + if (smc911x_detect_chip(dev)) { + free(dev); + return 0; + } + + addrh = smc911x_get_mac_csr(dev, ADDRH); + addrl = smc911x_get_mac_csr(dev, ADDRL); + if (!(addrl == 0xffffffff && addrh == 0x0000ffff)) { + /* address is obtained from optional eeprom */ + dev->enetaddr[0] = addrl; + dev->enetaddr[1] = addrl >> 8; + dev->enetaddr[2] = addrl >> 16; + dev->enetaddr[3] = addrl >> 24; + dev->enetaddr[4] = addrh; + dev->enetaddr[5] = addrh >> 8; + } + + dev->init = smc911x_init; + dev->halt = smc911x_halt; + dev->send = smc911x_send; + dev->recv = smc911x_rx; + sprintf(dev->name, "%s-%hu", DRIVERNAME, dev_num); + + eth_register(dev); + return 1; +} diff --git a/roms/u-boot-sam460ex/drivers/net/smc911x.h b/roms/u-boot-sam460ex/drivers/net/smc911x.h new file mode 100644 index 000000000..05e007c6d --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/smc911x.h @@ -0,0 +1,511 @@ +/* + * SMSC LAN9[12]1[567] Network driver + * + * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _SMC911X_H_ +#define _SMC911X_H_ + +#include <linux/types.h> + +#define DRIVERNAME "smc911x" + +#if defined (CONFIG_SMC911X_32_BIT) && \ + defined (CONFIG_SMC911X_16_BIT) +#error "SMC911X: Only one of CONFIG_SMC911X_32_BIT and \ + CONFIG_SMC911X_16_BIT shall be set" +#endif + +#if defined (CONFIG_SMC911X_32_BIT) +static inline u32 __smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + return *(volatile u32*)(dev->iobase + offset); +} +u32 smc911x_reg_read(struct eth_device *dev, u32 offset) + __attribute__((weak, alias("__smc911x_reg_read"))); + +static inline void __smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u32*)(dev->iobase + offset) = val; +} +void smc911x_reg_write(struct eth_device *dev, u32 offset, u32 val) + __attribute__((weak, alias("__smc911x_reg_write"))); +#elif defined (CONFIG_SMC911X_16_BIT) +static inline u32 smc911x_reg_read(struct eth_device *dev, u32 offset) +{ + volatile u16 *addr_16 = (u16 *)(dev->iobase + offset); + return ((*addr_16 & 0x0000ffff) | (*(addr_16 + 1) << 16)); +} +static inline void smc911x_reg_write(struct eth_device *dev, + u32 offset, u32 val) +{ + *(volatile u16 *)(dev->iobase + offset) = (u16)val; + *(volatile u16 *)(dev->iobase + offset + 2) = (u16)(val >> 16); +} +#else +#error "SMC911X: undefined bus width" +#endif /* CONFIG_SMC911X_16_BIT */ + +/* Below are the register offsets and bit definitions + * of the Lan911x memory space + */ +#define RX_DATA_FIFO 0x00 + +#define TX_DATA_FIFO 0x20 +#define TX_CMD_A_INT_ON_COMP 0x80000000 +#define TX_CMD_A_INT_BUF_END_ALGN 0x03000000 +#define TX_CMD_A_INT_4_BYTE_ALGN 0x00000000 +#define TX_CMD_A_INT_16_BYTE_ALGN 0x01000000 +#define TX_CMD_A_INT_32_BYTE_ALGN 0x02000000 +#define TX_CMD_A_INT_DATA_OFFSET 0x001F0000 +#define TX_CMD_A_INT_FIRST_SEG 0x00002000 +#define TX_CMD_A_INT_LAST_SEG 0x00001000 +#define TX_CMD_A_BUF_SIZE 0x000007FF +#define TX_CMD_B_PKT_TAG 0xFFFF0000 +#define TX_CMD_B_ADD_CRC_DISABLE 0x00002000 +#define TX_CMD_B_DISABLE_PADDING 0x00001000 +#define TX_CMD_B_PKT_BYTE_LENGTH 0x000007FF + +#define RX_STATUS_FIFO 0x40 +#define RX_STS_PKT_LEN 0x3FFF0000 +#define RX_STS_ES 0x00008000 +#define RX_STS_BCST 0x00002000 +#define RX_STS_LEN_ERR 0x00001000 +#define RX_STS_RUNT_ERR 0x00000800 +#define RX_STS_MCAST 0x00000400 +#define RX_STS_TOO_LONG 0x00000080 +#define RX_STS_COLL 0x00000040 +#define RX_STS_ETH_TYPE 0x00000020 +#define RX_STS_WDOG_TMT 0x00000010 +#define RX_STS_MII_ERR 0x00000008 +#define RX_STS_DRIBBLING 0x00000004 +#define RX_STS_CRC_ERR 0x00000002 +#define RX_STATUS_FIFO_PEEK 0x44 +#define TX_STATUS_FIFO 0x48 +#define TX_STS_TAG 0xFFFF0000 +#define TX_STS_ES 0x00008000 +#define TX_STS_LOC 0x00000800 +#define TX_STS_NO_CARR 0x00000400 +#define TX_STS_LATE_COLL 0x00000200 +#define TX_STS_MANY_COLL 0x00000100 +#define TX_STS_COLL_CNT 0x00000078 +#define TX_STS_MANY_DEFER 0x00000004 +#define TX_STS_UNDERRUN 0x00000002 +#define TX_STS_DEFERRED 0x00000001 +#define TX_STATUS_FIFO_PEEK 0x4C +#define ID_REV 0x50 +#define ID_REV_CHIP_ID 0xFFFF0000 /* RO */ +#define ID_REV_REV_ID 0x0000FFFF /* RO */ + +#define INT_CFG 0x54 +#define INT_CFG_INT_DEAS 0xFF000000 /* R/W */ +#define INT_CFG_INT_DEAS_CLR 0x00004000 +#define INT_CFG_INT_DEAS_STS 0x00002000 +#define INT_CFG_IRQ_INT 0x00001000 /* RO */ +#define INT_CFG_IRQ_EN 0x00000100 /* R/W */ + /* R/W Not Affected by SW Reset */ +#define INT_CFG_IRQ_POL 0x00000010 + /* R/W Not Affected by SW Reset */ +#define INT_CFG_IRQ_TYPE 0x00000001 + +#define INT_STS 0x58 +#define INT_STS_SW_INT 0x80000000 /* R/WC */ +#define INT_STS_TXSTOP_INT 0x02000000 /* R/WC */ +#define INT_STS_RXSTOP_INT 0x01000000 /* R/WC */ +#define INT_STS_RXDFH_INT 0x00800000 /* R/WC */ +#define INT_STS_RXDF_INT 0x00400000 /* R/WC */ +#define INT_STS_TX_IOC 0x00200000 /* R/WC */ +#define INT_STS_RXD_INT 0x00100000 /* R/WC */ +#define INT_STS_GPT_INT 0x00080000 /* R/WC */ +#define INT_STS_PHY_INT 0x00040000 /* RO */ +#define INT_STS_PME_INT 0x00020000 /* R/WC */ +#define INT_STS_TXSO 0x00010000 /* R/WC */ +#define INT_STS_RWT 0x00008000 /* R/WC */ +#define INT_STS_RXE 0x00004000 /* R/WC */ +#define INT_STS_TXE 0x00002000 /* R/WC */ +/*#define INT_STS_ERX 0x00001000*/ /* R/WC */ +#define INT_STS_TDFU 0x00000800 /* R/WC */ +#define INT_STS_TDFO 0x00000400 /* R/WC */ +#define INT_STS_TDFA 0x00000200 /* R/WC */ +#define INT_STS_TSFF 0x00000100 /* R/WC */ +#define INT_STS_TSFL 0x00000080 /* R/WC */ +/*#define INT_STS_RXDF 0x00000040*/ /* R/WC */ +#define INT_STS_RDFO 0x00000040 /* R/WC */ +#define INT_STS_RDFL 0x00000020 /* R/WC */ +#define INT_STS_RSFF 0x00000010 /* R/WC */ +#define INT_STS_RSFL 0x00000008 /* R/WC */ +#define INT_STS_GPIO2_INT 0x00000004 /* R/WC */ +#define INT_STS_GPIO1_INT 0x00000002 /* R/WC */ +#define INT_STS_GPIO0_INT 0x00000001 /* R/WC */ +#define INT_EN 0x5C +#define INT_EN_SW_INT_EN 0x80000000 /* R/W */ +#define INT_EN_TXSTOP_INT_EN 0x02000000 /* R/W */ +#define INT_EN_RXSTOP_INT_EN 0x01000000 /* R/W */ +#define INT_EN_RXDFH_INT_EN 0x00800000 /* R/W */ +/*#define INT_EN_RXDF_INT_EN 0x00400000*/ /* R/W */ +#define INT_EN_TIOC_INT_EN 0x00200000 /* R/W */ +#define INT_EN_RXD_INT_EN 0x00100000 /* R/W */ +#define INT_EN_GPT_INT_EN 0x00080000 /* R/W */ +#define INT_EN_PHY_INT_EN 0x00040000 /* R/W */ +#define INT_EN_PME_INT_EN 0x00020000 /* R/W */ +#define INT_EN_TXSO_EN 0x00010000 /* R/W */ +#define INT_EN_RWT_EN 0x00008000 /* R/W */ +#define INT_EN_RXE_EN 0x00004000 /* R/W */ +#define INT_EN_TXE_EN 0x00002000 /* R/W */ +/*#define INT_EN_ERX_EN 0x00001000*/ /* R/W */ +#define INT_EN_TDFU_EN 0x00000800 /* R/W */ +#define INT_EN_TDFO_EN 0x00000400 /* R/W */ +#define INT_EN_TDFA_EN 0x00000200 /* R/W */ +#define INT_EN_TSFF_EN 0x00000100 /* R/W */ +#define INT_EN_TSFL_EN 0x00000080 /* R/W */ +/*#define INT_EN_RXDF_EN 0x00000040*/ /* R/W */ +#define INT_EN_RDFO_EN 0x00000040 /* R/W */ +#define INT_EN_RDFL_EN 0x00000020 /* R/W */ +#define INT_EN_RSFF_EN 0x00000010 /* R/W */ +#define INT_EN_RSFL_EN 0x00000008 /* R/W */ +#define INT_EN_GPIO2_INT 0x00000004 /* R/W */ +#define INT_EN_GPIO1_INT 0x00000002 /* R/W */ +#define INT_EN_GPIO0_INT 0x00000001 /* R/W */ + +#define BYTE_TEST 0x64 +#define FIFO_INT 0x68 +#define FIFO_INT_TX_AVAIL_LEVEL 0xFF000000 /* R/W */ +#define FIFO_INT_TX_STS_LEVEL 0x00FF0000 /* R/W */ +#define FIFO_INT_RX_AVAIL_LEVEL 0x0000FF00 /* R/W */ +#define FIFO_INT_RX_STS_LEVEL 0x000000FF /* R/W */ + +#define RX_CFG 0x6C +#define RX_CFG_RX_END_ALGN 0xC0000000 /* R/W */ +#define RX_CFG_RX_END_ALGN4 0x00000000 /* R/W */ +#define RX_CFG_RX_END_ALGN16 0x40000000 /* R/W */ +#define RX_CFG_RX_END_ALGN32 0x80000000 /* R/W */ +#define RX_CFG_RX_DMA_CNT 0x0FFF0000 /* R/W */ +#define RX_CFG_RX_DUMP 0x00008000 /* R/W */ +#define RX_CFG_RXDOFF 0x00001F00 /* R/W */ +/*#define RX_CFG_RXBAD 0x00000001*/ /* R/W */ + +#define TX_CFG 0x70 +/*#define TX_CFG_TX_DMA_LVL 0xE0000000*/ /* R/W */ + /* R/W Self Clearing */ +/*#define TX_CFG_TX_DMA_CNT 0x0FFF0000*/ +#define TX_CFG_TXS_DUMP 0x00008000 /* Self Clearing */ +#define TX_CFG_TXD_DUMP 0x00004000 /* Self Clearing */ +#define TX_CFG_TXSAO 0x00000004 /* R/W */ +#define TX_CFG_TX_ON 0x00000002 /* R/W */ +#define TX_CFG_STOP_TX 0x00000001 /* Self Clearing */ + +#define HW_CFG 0x74 +#define HW_CFG_TTM 0x00200000 /* R/W */ +#define HW_CFG_SF 0x00100000 /* R/W */ +#define HW_CFG_TX_FIF_SZ 0x000F0000 /* R/W */ +#define HW_CFG_TR 0x00003000 /* R/W */ +#define HW_CFG_PHY_CLK_SEL 0x00000060 /* R/W */ +#define HW_CFG_PHY_CLK_SEL_INT_PHY 0x00000000 /* R/W */ +#define HW_CFG_PHY_CLK_SEL_EXT_PHY 0x00000020 /* R/W */ +#define HW_CFG_PHY_CLK_SEL_CLK_DIS 0x00000040 /* R/W */ +#define HW_CFG_SMI_SEL 0x00000010 /* R/W */ +#define HW_CFG_EXT_PHY_DET 0x00000008 /* RO */ +#define HW_CFG_EXT_PHY_EN 0x00000004 /* R/W */ +#define HW_CFG_32_16_BIT_MODE 0x00000004 /* RO */ +#define HW_CFG_SRST_TO 0x00000002 /* RO */ +#define HW_CFG_SRST 0x00000001 /* Self Clearing */ + +#define RX_DP_CTRL 0x78 +#define RX_DP_CTRL_RX_FFWD 0x80000000 /* R/W */ +#define RX_DP_CTRL_FFWD_BUSY 0x80000000 /* RO */ + +#define RX_FIFO_INF 0x7C +#define RX_FIFO_INF_RXSUSED 0x00FF0000 /* RO */ +#define RX_FIFO_INF_RXDUSED 0x0000FFFF /* RO */ + +#define TX_FIFO_INF 0x80 +#define TX_FIFO_INF_TSUSED 0x00FF0000 /* RO */ +#define TX_FIFO_INF_TDFREE 0x0000FFFF /* RO */ + +#define PMT_CTRL 0x84 +#define PMT_CTRL_PM_MODE 0x00003000 /* Self Clearing */ +#define PMT_CTRL_PHY_RST 0x00000400 /* Self Clearing */ +#define PMT_CTRL_WOL_EN 0x00000200 /* R/W */ +#define PMT_CTRL_ED_EN 0x00000100 /* R/W */ + /* R/W Not Affected by SW Reset */ +#define PMT_CTRL_PME_TYPE 0x00000040 +#define PMT_CTRL_WUPS 0x00000030 /* R/WC */ +#define PMT_CTRL_WUPS_NOWAKE 0x00000000 /* R/WC */ +#define PMT_CTRL_WUPS_ED 0x00000010 /* R/WC */ +#define PMT_CTRL_WUPS_WOL 0x00000020 /* R/WC */ +#define PMT_CTRL_WUPS_MULTI 0x00000030 /* R/WC */ +#define PMT_CTRL_PME_IND 0x00000008 /* R/W */ +#define PMT_CTRL_PME_POL 0x00000004 /* R/W */ + /* R/W Not Affected by SW Reset */ +#define PMT_CTRL_PME_EN 0x00000002 +#define PMT_CTRL_READY 0x00000001 /* RO */ + +#define GPIO_CFG 0x88 +#define GPIO_CFG_LED3_EN 0x40000000 /* R/W */ +#define GPIO_CFG_LED2_EN 0x20000000 /* R/W */ +#define GPIO_CFG_LED1_EN 0x10000000 /* R/W */ +#define GPIO_CFG_GPIO2_INT_POL 0x04000000 /* R/W */ +#define GPIO_CFG_GPIO1_INT_POL 0x02000000 /* R/W */ +#define GPIO_CFG_GPIO0_INT_POL 0x01000000 /* R/W */ +#define GPIO_CFG_EEPR_EN 0x00700000 /* R/W */ +#define GPIO_CFG_GPIOBUF2 0x00040000 /* R/W */ +#define GPIO_CFG_GPIOBUF1 0x00020000 /* R/W */ +#define GPIO_CFG_GPIOBUF0 0x00010000 /* R/W */ +#define GPIO_CFG_GPIODIR2 0x00000400 /* R/W */ +#define GPIO_CFG_GPIODIR1 0x00000200 /* R/W */ +#define GPIO_CFG_GPIODIR0 0x00000100 /* R/W */ +#define GPIO_CFG_GPIOD4 0x00000010 /* R/W */ +#define GPIO_CFG_GPIOD3 0x00000008 /* R/W */ +#define GPIO_CFG_GPIOD2 0x00000004 /* R/W */ +#define GPIO_CFG_GPIOD1 0x00000002 /* R/W */ +#define GPIO_CFG_GPIOD0 0x00000001 /* R/W */ + +#define GPT_CFG 0x8C +#define GPT_CFG_TIMER_EN 0x20000000 /* R/W */ +#define GPT_CFG_GPT_LOAD 0x0000FFFF /* R/W */ + +#define GPT_CNT 0x90 +#define GPT_CNT_GPT_CNT 0x0000FFFF /* RO */ + +#define ENDIAN 0x98 +#define FREE_RUN 0x9C +#define RX_DROP 0xA0 +#define MAC_CSR_CMD 0xA4 +#define MAC_CSR_CMD_CSR_BUSY 0x80000000 /* Self Clearing */ +#define MAC_CSR_CMD_R_NOT_W 0x40000000 /* R/W */ +#define MAC_CSR_CMD_CSR_ADDR 0x000000FF /* R/W */ + +#define MAC_CSR_DATA 0xA8 +#define AFC_CFG 0xAC +#define AFC_CFG_AFC_HI 0x00FF0000 /* R/W */ +#define AFC_CFG_AFC_LO 0x0000FF00 /* R/W */ +#define AFC_CFG_BACK_DUR 0x000000F0 /* R/W */ +#define AFC_CFG_FCMULT 0x00000008 /* R/W */ +#define AFC_CFG_FCBRD 0x00000004 /* R/W */ +#define AFC_CFG_FCADD 0x00000002 /* R/W */ +#define AFC_CFG_FCANY 0x00000001 /* R/W */ + +#define E2P_CMD 0xB0 +#define E2P_CMD_EPC_BUSY 0x80000000 /* Self Clearing */ +#define E2P_CMD_EPC_CMD 0x70000000 /* R/W */ +#define E2P_CMD_EPC_CMD_READ 0x00000000 /* R/W */ +#define E2P_CMD_EPC_CMD_EWDS 0x10000000 /* R/W */ +#define E2P_CMD_EPC_CMD_EWEN 0x20000000 /* R/W */ +#define E2P_CMD_EPC_CMD_WRITE 0x30000000 /* R/W */ +#define E2P_CMD_EPC_CMD_WRAL 0x40000000 /* R/W */ +#define E2P_CMD_EPC_CMD_ERASE 0x50000000 /* R/W */ +#define E2P_CMD_EPC_CMD_ERAL 0x60000000 /* R/W */ +#define E2P_CMD_EPC_CMD_RELOAD 0x70000000 /* R/W */ +#define E2P_CMD_EPC_TIMEOUT 0x00000200 /* RO */ +#define E2P_CMD_MAC_ADDR_LOADED 0x00000100 /* RO */ +#define E2P_CMD_EPC_ADDR 0x000000FF /* R/W */ + +#define E2P_DATA 0xB4 +#define E2P_DATA_EEPROM_DATA 0x000000FF /* R/W */ +/* end of LAN register offsets and bit definitions */ + +/* MAC Control and Status registers */ +#define MAC_CR 0x01 /* R/W */ + +/* MAC_CR - MAC Control Register */ +#define MAC_CR_RXALL 0x80000000 +/* TODO: delete this bit? It is not described in the data sheet. */ +#define MAC_CR_HBDIS 0x10000000 +#define MAC_CR_RCVOWN 0x00800000 +#define MAC_CR_LOOPBK 0x00200000 +#define MAC_CR_FDPX 0x00100000 +#define MAC_CR_MCPAS 0x00080000 +#define MAC_CR_PRMS 0x00040000 +#define MAC_CR_INVFILT 0x00020000 +#define MAC_CR_PASSBAD 0x00010000 +#define MAC_CR_HFILT 0x00008000 +#define MAC_CR_HPFILT 0x00002000 +#define MAC_CR_LCOLL 0x00001000 +#define MAC_CR_BCAST 0x00000800 +#define MAC_CR_DISRTY 0x00000400 +#define MAC_CR_PADSTR 0x00000100 +#define MAC_CR_BOLMT_MASK 0x000000C0 +#define MAC_CR_DFCHK 0x00000020 +#define MAC_CR_TXEN 0x00000008 +#define MAC_CR_RXEN 0x00000004 + +#define ADDRH 0x02 /* R/W mask 0x0000FFFFUL */ +#define ADDRL 0x03 /* R/W mask 0xFFFFFFFFUL */ +#define HASHH 0x04 /* R/W */ +#define HASHL 0x05 /* R/W */ + +#define MII_ACC 0x06 /* R/W */ +#define MII_ACC_PHY_ADDR 0x0000F800 +#define MII_ACC_MIIRINDA 0x000007C0 +#define MII_ACC_MII_WRITE 0x00000002 +#define MII_ACC_MII_BUSY 0x00000001 + +#define MII_DATA 0x07 /* R/W mask 0x0000FFFFUL */ + +#define FLOW 0x08 /* R/W */ +#define FLOW_FCPT 0xFFFF0000 +#define FLOW_FCPASS 0x00000004 +#define FLOW_FCEN 0x00000002 +#define FLOW_FCBSY 0x00000001 + +#define VLAN1 0x09 /* R/W mask 0x0000FFFFUL */ +#define VLAN1_VTI1 0x0000ffff + +#define VLAN2 0x0A /* R/W mask 0x0000FFFFUL */ +#define VLAN2_VTI2 0x0000ffff + +#define WUFF 0x0B /* WO */ + +#define WUCSR 0x0C /* R/W */ +#define WUCSR_GUE 0x00000200 +#define WUCSR_WUFR 0x00000040 +#define WUCSR_MPR 0x00000020 +#define WUCSR_WAKE_EN 0x00000004 +#define WUCSR_MPEN 0x00000002 + +/* Chip ID values */ +#define CHIP_9115 0x115 +#define CHIP_9116 0x116 +#define CHIP_9117 0x117 +#define CHIP_9118 0x118 +#define CHIP_9211 0x9211 +#define CHIP_9215 0x115a +#define CHIP_9216 0x116a +#define CHIP_9217 0x117a +#define CHIP_9218 0x118a +#define CHIP_9220 0x9220 +#define CHIP_9221 0x9221 + +struct chip_id { + u16 id; + char *name; +}; + +static const struct chip_id chip_ids[] = { + { CHIP_9115, "LAN9115" }, + { CHIP_9116, "LAN9116" }, + { CHIP_9117, "LAN9117" }, + { CHIP_9118, "LAN9118" }, + { CHIP_9211, "LAN9211" }, + { CHIP_9215, "LAN9215" }, + { CHIP_9216, "LAN9216" }, + { CHIP_9217, "LAN9217" }, + { CHIP_9218, "LAN9218" }, + { CHIP_9220, "LAN9220" }, + { CHIP_9221, "LAN9221" }, + { 0, NULL }, +}; + +static u32 smc911x_get_mac_csr(struct eth_device *dev, u8 reg) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_CMD, + MAC_CSR_CMD_CSR_BUSY | MAC_CSR_CMD_R_NOT_W | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + + return smc911x_reg_read(dev, MAC_CSR_DATA); +} + +static void smc911x_set_mac_csr(struct eth_device *dev, u8 reg, u32 data) +{ + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; + smc911x_reg_write(dev, MAC_CSR_DATA, data); + smc911x_reg_write(dev, MAC_CSR_CMD, MAC_CSR_CMD_CSR_BUSY | reg); + while (smc911x_reg_read(dev, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY) + ; +} + +static int smc911x_detect_chip(struct eth_device *dev) +{ + unsigned long val, i; + + val = smc911x_reg_read(dev, BYTE_TEST); + if (val == 0xffffffff) { + /* Special case -- no chip present */ + return -1; + } else if (val != 0x87654321) { + printf(DRIVERNAME ": Invalid chip endian 0x%08lx\n", val); + return -1; + } + + val = smc911x_reg_read(dev, ID_REV) >> 16; + for (i = 0; chip_ids[i].id != 0; i++) { + if (chip_ids[i].id == val) break; + } + if (!chip_ids[i].id) { + printf(DRIVERNAME ": Unknown chip ID %04lx\n", val); + return -1; + } + + dev->priv = (void *)&chip_ids[i]; + + return 0; +} + +static void smc911x_reset(struct eth_device *dev) +{ + int timeout; + + /* Take out of PM setting first */ + if (smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY) { + /* Write to the bytetest will take out of powerdown */ + smc911x_reg_write(dev, BYTE_TEST, 0x0); + + timeout = 10; + + while (timeout-- && + !(smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY)) + udelay(10); + if (!timeout) { + printf(DRIVERNAME + ": timeout waiting for PM restore\n"); + return; + } + } + + /* Disable interrupts */ + smc911x_reg_write(dev, INT_EN, 0); + + smc911x_reg_write(dev, HW_CFG, HW_CFG_SRST); + + timeout = 1000; + while (timeout-- && smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) + udelay(10); + + if (!timeout) { + printf(DRIVERNAME ": reset timeout\n"); + return; + } + + /* Reset the FIFO level and flow control settings */ + smc911x_set_mac_csr(dev, FLOW, FLOW_FCPT | FLOW_FCEN); + smc911x_reg_write(dev, AFC_CFG, 0x0050287F); + + /* Set to LED outputs */ + smc911x_reg_write(dev, GPIO_CFG, 0x70070000); +} + +#endif diff --git a/roms/u-boot-sam460ex/drivers/net/tigon3.c b/roms/u-boot-sam460ex/drivers/net/tigon3.c new file mode 100644 index 000000000..33cb447b1 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/tigon3.c @@ -0,0 +1,5697 @@ +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/******************************************************************************/ +#include <common.h> +#include <asm/types.h> + +#ifdef CONFIG_BMW +#include <mpc824x.h> +#endif +#include <malloc.h> +#include <linux/byteorder/big_endian.h> +#include "bcm570x_mm.h" + +#define EMBEDDED 1 +/******************************************************************************/ +/* Local functions. */ +/******************************************************************************/ + +LM_STATUS LM_Abort (PLM_DEVICE_BLOCK pDevice); +LM_STATUS LM_QueueRxPackets (PLM_DEVICE_BLOCK pDevice); + +static LM_STATUS LM_TranslateRequestedMediaType (LM_REQUESTED_MEDIA_TYPE + RequestedMediaType, + PLM_MEDIA_TYPE pMediaType, + PLM_LINE_SPEED pLineSpeed, + PLM_DUPLEX_MODE pDuplexMode); + +static LM_STATUS LM_InitBcm540xPhy (PLM_DEVICE_BLOCK pDevice); + +__inline static LM_VOID LM_ServiceRxInterrupt (PLM_DEVICE_BLOCK pDevice); +__inline static LM_VOID LM_ServiceTxInterrupt (PLM_DEVICE_BLOCK pDevice); + +static LM_STATUS LM_ForceAutoNegBcm540xPhy (PLM_DEVICE_BLOCK pDevice, + LM_REQUESTED_MEDIA_TYPE + RequestedMediaType); +static LM_STATUS LM_ForceAutoNeg (PLM_DEVICE_BLOCK pDevice, + LM_REQUESTED_MEDIA_TYPE RequestedMediaType); +static LM_UINT32 GetPhyAdFlowCntrlSettings (PLM_DEVICE_BLOCK pDevice); +STATIC LM_STATUS LM_SetFlowControl (PLM_DEVICE_BLOCK pDevice, + LM_UINT32 LocalPhyAd, + LM_UINT32 RemotePhyAd); +#if INCLUDE_TBI_SUPPORT +STATIC LM_STATUS LM_SetupFiberPhy (PLM_DEVICE_BLOCK pDevice); +STATIC LM_STATUS LM_InitBcm800xPhy (PLM_DEVICE_BLOCK pDevice); +#endif +STATIC LM_STATUS LM_SetupCopperPhy (PLM_DEVICE_BLOCK pDevice); +STATIC PLM_ADAPTER_INFO LM_GetAdapterInfoBySsid (LM_UINT16 Svid, + LM_UINT16 Ssid); +STATIC LM_STATUS LM_DmaTest (PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pBufferVirt, + LM_PHYSICAL_ADDRESS BufferPhy, + LM_UINT32 BufferSize); +STATIC LM_STATUS LM_HaltCpu (PLM_DEVICE_BLOCK pDevice, LM_UINT32 cpu_number); +STATIC LM_STATUS LM_ResetChip (PLM_DEVICE_BLOCK pDevice); +STATIC LM_STATUS LM_Test4GBoundary (PLM_DEVICE_BLOCK pDevice, + PLM_PACKET pPacket, PT3_SND_BD pSendBd); + +/******************************************************************************/ +/* External functions. */ +/******************************************************************************/ + +LM_STATUS LM_LoadRlsFirmware (PLM_DEVICE_BLOCK pDevice); + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_UINT32 LM_RegRdInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Register) +{ + LM_UINT32 Value32; + +#if PCIX_TARGET_WORKAROUND + MM_ACQUIRE_UNDI_LOCK (pDevice); +#endif + MM_WriteConfig32 (pDevice, T3_PCI_REG_ADDR_REG, Register); + MM_ReadConfig32 (pDevice, T3_PCI_REG_DATA_REG, &Value32); +#if PCIX_TARGET_WORKAROUND + MM_RELEASE_UNDI_LOCK (pDevice); +#endif + + return Value32; +} /* LM_RegRdInd */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_VOID +LM_RegWrInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Register, LM_UINT32 Value32) +{ + +#if PCIX_TARGET_WORKAROUND + MM_ACQUIRE_UNDI_LOCK (pDevice); +#endif + MM_WriteConfig32 (pDevice, T3_PCI_REG_ADDR_REG, Register); + MM_WriteConfig32 (pDevice, T3_PCI_REG_DATA_REG, Value32); +#if PCIX_TARGET_WORKAROUND + MM_RELEASE_UNDI_LOCK (pDevice); +#endif +} /* LM_RegWrInd */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_UINT32 LM_MemRdInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 MemAddr) +{ + LM_UINT32 Value32; + + MM_ACQUIRE_UNDI_LOCK (pDevice); +#ifdef BIG_ENDIAN_HOST + MM_WriteConfig32 (pDevice, T3_PCI_MEM_WIN_ADDR_REG, MemAddr); + Value32 = REG_RD (pDevice, PciCfg.MemWindowData); + /* Value32 = REG_RD(pDevice,uIntMem.Mbuf[(MemAddr & 0x7fff)/4]); */ +#else + MM_WriteConfig32 (pDevice, T3_PCI_MEM_WIN_ADDR_REG, MemAddr); + MM_ReadConfig32 (pDevice, T3_PCI_MEM_WIN_DATA_REG, &Value32); +#endif + MM_RELEASE_UNDI_LOCK (pDevice); + + return Value32; +} /* LM_MemRdInd */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_VOID +LM_MemWrInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 MemAddr, LM_UINT32 Value32) +{ + MM_ACQUIRE_UNDI_LOCK (pDevice); +#ifdef BIG_ENDIAN_HOST + REG_WR (pDevice, PciCfg.MemWindowBaseAddr, MemAddr); + REG_WR (pDevice, uIntMem.Mbuf[(MemAddr & 0x7fff) / 4], Value32); +#else + MM_WriteConfig32 (pDevice, T3_PCI_MEM_WIN_ADDR_REG, MemAddr); + MM_WriteConfig32 (pDevice, T3_PCI_MEM_WIN_DATA_REG, Value32); +#endif + MM_RELEASE_UNDI_LOCK (pDevice); +} /* LM_MemWrInd */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_QueueRxPackets (PLM_DEVICE_BLOCK pDevice) +{ + LM_STATUS Lmstatus; + PLM_PACKET pPacket; + PT3_RCV_BD pRcvBd; + LM_UINT32 StdBdAdded = 0; +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + LM_UINT32 JumboBdAdded = 0; +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + Lmstatus = LM_STATUS_SUCCESS; + + pPacket = (PLM_PACKET) QQ_PopHead (&pDevice->RxPacketFreeQ.Container); + while (pPacket) { + switch (pPacket->u.Rx.RcvProdRing) { +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + case T3_JUMBO_RCV_PROD_RING: /* Jumbo Receive Ring. */ + /* Initialize the buffer descriptor. */ + pRcvBd = + &pDevice->pRxJumboBdVirt[pDevice->RxJumboProdIdx]; + pRcvBd->Flags = + RCV_BD_FLAG_END | RCV_BD_FLAG_JUMBO_RING; + pRcvBd->Len = (LM_UINT16) pDevice->RxJumboBufferSize; + + /* Initialize the receive buffer pointer */ +#if 0 /* Jimmy, deleted in new */ + pRcvBd->HostAddr.Low = pPacket->u.Rx.RxBufferPhy.Low; + pRcvBd->HostAddr.High = pPacket->u.Rx.RxBufferPhy.High; +#endif + MM_MapRxDma (pDevice, pPacket, &pRcvBd->HostAddr); + + /* The opaque field may point to an offset from a fix addr. */ + pRcvBd->Opaque = (LM_UINT32) (MM_UINT_PTR (pPacket) - + MM_UINT_PTR (pDevice-> + pPacketDescBase)); + + /* Update the producer index. */ + pDevice->RxJumboProdIdx = + (pDevice->RxJumboProdIdx + + 1) & T3_JUMBO_RCV_RCB_ENTRY_COUNT_MASK; + + JumboBdAdded++; + break; +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + case T3_STD_RCV_PROD_RING: /* Standard Receive Ring. */ + /* Initialize the buffer descriptor. */ + pRcvBd = &pDevice->pRxStdBdVirt[pDevice->RxStdProdIdx]; + pRcvBd->Flags = RCV_BD_FLAG_END; + pRcvBd->Len = MAX_STD_RCV_BUFFER_SIZE; + + /* Initialize the receive buffer pointer */ +#if 0 /* Jimmy, deleted in new replaced with MM_MapRxDma */ + pRcvBd->HostAddr.Low = pPacket->u.Rx.RxBufferPhy.Low; + pRcvBd->HostAddr.High = pPacket->u.Rx.RxBufferPhy.High; +#endif + MM_MapRxDma (pDevice, pPacket, &pRcvBd->HostAddr); + + /* The opaque field may point to an offset from a fix addr. */ + pRcvBd->Opaque = (LM_UINT32) (MM_UINT_PTR (pPacket) - + MM_UINT_PTR (pDevice-> + pPacketDescBase)); + + /* Update the producer index. */ + pDevice->RxStdProdIdx = (pDevice->RxStdProdIdx + 1) & + T3_STD_RCV_RCB_ENTRY_COUNT_MASK; + + StdBdAdded++; + break; + + case T3_UNKNOWN_RCV_PROD_RING: + default: + Lmstatus = LM_STATUS_FAILURE; + break; + } /* switch */ + + /* Bail out if there is any error. */ + if (Lmstatus != LM_STATUS_SUCCESS) { + break; + } + + pPacket = + (PLM_PACKET) QQ_PopHead (&pDevice->RxPacketFreeQ.Container); + } /* while */ + + wmb (); + /* Update the procedure index. */ + if (StdBdAdded) { + MB_REG_WR (pDevice, Mailbox.RcvStdProdIdx.Low, + pDevice->RxStdProdIdx); + } +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + if (JumboBdAdded) { + MB_REG_WR (pDevice, Mailbox.RcvJumboProdIdx.Low, + pDevice->RxJumboProdIdx); + } +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + return Lmstatus; +} /* LM_QueueRxPackets */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +STATIC LM_VOID LM_NvramInit (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + LM_UINT32 j; + + /* Intialize clock period and state machine. */ + Value32 = SEEPROM_ADDR_CLK_PERD (SEEPROM_CLOCK_PERIOD) | + SEEPROM_ADDR_FSM_RESET; + REG_WR (pDevice, Grc.EepromAddr, Value32); + + for (j = 0; j < 100; j++) { + MM_Wait (10); + } + + /* Serial eeprom access using the Grc.EepromAddr/EepromData registers. */ + Value32 = REG_RD (pDevice, Grc.LocalCtrl); + REG_WR (pDevice, Grc.LocalCtrl, + Value32 | GRC_MISC_LOCAL_CTRL_AUTO_SEEPROM); + + /* Set the 5701 compatibility mode if we are using EEPROM. */ + if (T3_ASIC_REV (pDevice->ChipRevId) != T3_ASIC_REV_5700 && + T3_ASIC_REV (pDevice->ChipRevId) != T3_ASIC_REV_5701) { + Value32 = REG_RD (pDevice, Nvram.Config1); + if ((Value32 & FLASH_INTERFACE_ENABLE) == 0) { + /* Use the new interface to read EEPROM. */ + Value32 &= ~FLASH_COMPAT_BYPASS; + + REG_WR (pDevice, Nvram.Config1, Value32); + } + } +} /* LM_NvRamInit */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +STATIC LM_STATUS +LM_EepromRead (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, LM_UINT32 * pData) +{ + LM_UINT32 Value32; + LM_UINT32 Addr; + LM_UINT32 Dev; + LM_UINT32 j; + + if (Offset > SEEPROM_CHIP_SIZE) { + return LM_STATUS_FAILURE; + } + + Dev = Offset / SEEPROM_CHIP_SIZE; + Addr = Offset % SEEPROM_CHIP_SIZE; + + Value32 = REG_RD (pDevice, Grc.EepromAddr); + Value32 &= ~(SEEPROM_ADDR_ADDRESS_MASK | SEEPROM_ADDR_DEV_ID_MASK | + SEEPROM_ADDR_RW_MASK); + REG_WR (pDevice, Grc.EepromAddr, Value32 | SEEPROM_ADDR_DEV_ID (Dev) | + SEEPROM_ADDR_ADDRESS (Addr) | SEEPROM_ADDR_START | + SEEPROM_ADDR_READ); + + for (j = 0; j < 1000; j++) { + Value32 = REG_RD (pDevice, Grc.EepromAddr); + if (Value32 & SEEPROM_ADDR_COMPLETE) { + break; + } + MM_Wait (10); + } + + if (Value32 & SEEPROM_ADDR_COMPLETE) { + Value32 = REG_RD (pDevice, Grc.EepromData); + *pData = Value32; + + return LM_STATUS_SUCCESS; + } + + return LM_STATUS_FAILURE; +} /* LM_EepromRead */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +STATIC LM_STATUS +LM_NvramRead (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, LM_UINT32 * pData) +{ + LM_UINT32 Value32; + LM_STATUS Status; + LM_UINT32 j; + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Status = LM_EepromRead (pDevice, Offset, pData); + } else { + /* Determine if we have flash or EEPROM. */ + Value32 = REG_RD (pDevice, Nvram.Config1); + if (Value32 & FLASH_INTERFACE_ENABLE) { + if (Value32 & FLASH_SSRAM_BUFFERRED_MODE) { + Offset = ((Offset / BUFFERED_FLASH_PAGE_SIZE) << + BUFFERED_FLASH_PAGE_POS) + + (Offset % BUFFERED_FLASH_PAGE_SIZE); + } + } + + REG_WR (pDevice, Nvram.SwArb, SW_ARB_REQ_SET1); + for (j = 0; j < 1000; j++) { + if (REG_RD (pDevice, Nvram.SwArb) & SW_ARB_GNT1) { + break; + } + MM_Wait (20); + } + if (j == 1000) { + return LM_STATUS_FAILURE; + } + + /* Read from flash or EEPROM with the new 5703/02 interface. */ + REG_WR (pDevice, Nvram.Addr, Offset & NVRAM_ADDRESS_MASK); + + REG_WR (pDevice, Nvram.Cmd, NVRAM_CMD_RD | NVRAM_CMD_DO_IT | + NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE); + + /* Wait for the done bit to clear. */ + for (j = 0; j < 500; j++) { + MM_Wait (10); + + Value32 = REG_RD (pDevice, Nvram.Cmd); + if (!(Value32 & NVRAM_CMD_DONE)) { + break; + } + } + + /* Wait for the done bit. */ + if (!(Value32 & NVRAM_CMD_DONE)) { + for (j = 0; j < 500; j++) { + MM_Wait (10); + + Value32 = REG_RD (pDevice, Nvram.Cmd); + if (Value32 & NVRAM_CMD_DONE) { + MM_Wait (10); + + *pData = + REG_RD (pDevice, Nvram.ReadData); + + /* Change the endianess. */ + *pData = + ((*pData & 0xff) << 24) | + ((*pData & 0xff00) << 8) | + ((*pData & 0xff0000) >> 8) | + ((*pData >> 24) & 0xff); + + break; + } + } + } + + REG_WR (pDevice, Nvram.SwArb, SW_ARB_REQ_CLR1); + if (Value32 & NVRAM_CMD_DONE) { + Status = LM_STATUS_SUCCESS; + } else { + Status = LM_STATUS_FAILURE; + } + } + + return Status; +} /* LM_NvramRead */ + +STATIC void LM_ReadVPD (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Vpd_arr[256 / 4]; + LM_UINT8 *Vpd = (LM_UINT8 *) & Vpd_arr[0]; + LM_UINT32 *Vpd_dptr = &Vpd_arr[0]; + LM_UINT32 Value32; + unsigned int j; + + /* Read PN from VPD */ + for (j = 0; j < 256; j += 4, Vpd_dptr++) { + if (LM_NvramRead (pDevice, 0x100 + j, &Value32) != + LM_STATUS_SUCCESS) { + printf ("BCM570x: LM_ReadVPD: VPD read failed" + " (no EEPROM onboard)\n"); + return; + } + *Vpd_dptr = cpu_to_le32 (Value32); + } + for (j = 0; j < 256;) { + unsigned int Vpd_r_len; + unsigned int Vpd_r_end; + + if ((Vpd[j] == 0x82) || (Vpd[j] == 0x91)) { + j = j + 3 + Vpd[j + 1] + (Vpd[j + 2] << 8); + } else if (Vpd[j] == 0x90) { + Vpd_r_len = Vpd[j + 1] + (Vpd[j + 2] << 8); + j += 3; + Vpd_r_end = Vpd_r_len + j; + while (j < Vpd_r_end) { + if ((Vpd[j] == 'P') && (Vpd[j + 1] == 'N')) { + unsigned int len = Vpd[j + 2]; + + if (len <= 24) { + memcpy (pDevice->PartNo, + &Vpd[j + 3], len); + } + break; + } else { + if (Vpd[j + 2] == 0) { + break; + } + j = j + Vpd[j + 2]; + } + } + break; + } else { + break; + } + } +} + +STATIC void LM_ReadBootCodeVersion (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32, offset, ver_offset; + int i; + + if (LM_NvramRead (pDevice, 0x0, &Value32) != LM_STATUS_SUCCESS) + return; + if (Value32 != 0xaa559966) + return; + if (LM_NvramRead (pDevice, 0xc, &offset) != LM_STATUS_SUCCESS) + return; + + offset = ((offset & 0xff) << 24) | ((offset & 0xff00) << 8) | + ((offset & 0xff0000) >> 8) | ((offset >> 24) & 0xff); + if (LM_NvramRead (pDevice, offset, &Value32) != LM_STATUS_SUCCESS) + return; + if ((Value32 == 0x0300000e) && + (LM_NvramRead (pDevice, offset + 4, &Value32) == LM_STATUS_SUCCESS) + && (Value32 == 0)) { + + if (LM_NvramRead (pDevice, offset + 8, &ver_offset) != + LM_STATUS_SUCCESS) + return; + ver_offset = ((ver_offset & 0xff0000) >> 8) | + ((ver_offset >> 24) & 0xff); + for (i = 0; i < 16; i += 4) { + if (LM_NvramRead + (pDevice, offset + ver_offset + i, + &Value32) != LM_STATUS_SUCCESS) { + return; + } + *((LM_UINT32 *) & pDevice->BootCodeVer[i]) = + cpu_to_le32 (Value32); + } + } else { + char c; + + if (LM_NvramRead (pDevice, 0x94, &Value32) != LM_STATUS_SUCCESS) + return; + + i = 0; + c = ((Value32 & 0xff0000) >> 16); + + if (c < 10) { + pDevice->BootCodeVer[i++] = c + '0'; + } else { + pDevice->BootCodeVer[i++] = (c / 10) + '0'; + pDevice->BootCodeVer[i++] = (c % 10) + '0'; + } + pDevice->BootCodeVer[i++] = '.'; + c = (Value32 & 0xff000000) >> 24; + if (c < 10) { + pDevice->BootCodeVer[i++] = c + '0'; + } else { + pDevice->BootCodeVer[i++] = (c / 10) + '0'; + pDevice->BootCodeVer[i++] = (c % 10) + '0'; + } + pDevice->BootCodeVer[i] = 0; + } +} + +STATIC void LM_GetBusSpeed (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 PciState = pDevice->PciState; + LM_UINT32 ClockCtrl; + char *SpeedStr = ""; + + if (PciState & T3_PCI_STATE_32BIT_PCI_BUS) { + strcpy (pDevice->BusSpeedStr, "32-bit "); + } else { + strcpy (pDevice->BusSpeedStr, "64-bit "); + } + if (PciState & T3_PCI_STATE_CONVENTIONAL_PCI_MODE) { + strcat (pDevice->BusSpeedStr, "PCI "); + if (PciState & T3_PCI_STATE_HIGH_BUS_SPEED) { + SpeedStr = "66MHz"; + } else { + SpeedStr = "33MHz"; + } + } else { + strcat (pDevice->BusSpeedStr, "PCIX "); + if (pDevice->BondId == GRC_MISC_BD_ID_5704CIOBE) { + SpeedStr = "133MHz"; + } else { + ClockCtrl = REG_RD (pDevice, PciCfg.ClockCtrl) & 0x1f; + switch (ClockCtrl) { + case 0: + SpeedStr = "33MHz"; + break; + + case 2: + SpeedStr = "50MHz"; + break; + + case 4: + SpeedStr = "66MHz"; + break; + + case 6: + SpeedStr = "100MHz"; + break; + + case 7: + SpeedStr = "133MHz"; + break; + } + } + } + strcat (pDevice->BusSpeedStr, SpeedStr); +} + +/******************************************************************************/ +/* Description: */ +/* This routine initializes default parameters and reads the PCI */ +/* configurations. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_GetAdapterInfo (PLM_DEVICE_BLOCK pDevice) +{ + PLM_ADAPTER_INFO pAdapterInfo; + LM_UINT32 Value32; + LM_STATUS Status; + LM_UINT32 j; + LM_UINT32 EeSigFound; + LM_UINT32 EePhyTypeSerdes = 0; + LM_UINT32 EePhyLedMode = 0; + LM_UINT32 EePhyId = 0; + + /* Get Device Id and Vendor Id */ + Status = MM_ReadConfig32 (pDevice, PCI_VENDOR_ID_REG, &Value32); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + pDevice->PciVendorId = (LM_UINT16) Value32; + pDevice->PciDeviceId = (LM_UINT16) (Value32 >> 16); + + /* If we are not getting the write adapter, exit. */ + if ((Value32 != T3_PCI_ID_BCM5700) && + (Value32 != T3_PCI_ID_BCM5701) && + (Value32 != T3_PCI_ID_BCM5702) && + (Value32 != T3_PCI_ID_BCM5702x) && + (Value32 != T3_PCI_ID_BCM5702FE) && + (Value32 != T3_PCI_ID_BCM5703) && + (Value32 != T3_PCI_ID_BCM5703x) && (Value32 != T3_PCI_ID_BCM5704)) { + return LM_STATUS_FAILURE; + } + + Status = MM_ReadConfig32 (pDevice, PCI_REV_ID_REG, &Value32); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + pDevice->PciRevId = (LM_UINT8) Value32; + + /* Get IRQ. */ + Status = MM_ReadConfig32 (pDevice, PCI_INT_LINE_REG, &Value32); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + pDevice->Irq = (LM_UINT8) Value32; + + /* Get interrupt pin. */ + pDevice->IntPin = (LM_UINT8) (Value32 >> 8); + + /* Get chip revision id. */ + Status = MM_ReadConfig32 (pDevice, T3_PCI_MISC_HOST_CTRL_REG, &Value32); + pDevice->ChipRevId = Value32 >> 16; + + /* Get subsystem vendor. */ + Status = + MM_ReadConfig32 (pDevice, PCI_SUBSYSTEM_VENDOR_ID_REG, &Value32); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + pDevice->SubsystemVendorId = (LM_UINT16) Value32; + + /* Get PCI subsystem id. */ + pDevice->SubsystemId = (LM_UINT16) (Value32 >> 16); + + /* Get the cache line size. */ + MM_ReadConfig32 (pDevice, PCI_CACHE_LINE_SIZE_REG, &Value32); + pDevice->CacheLineSize = (LM_UINT8) Value32; + pDevice->SavedCacheLineReg = Value32; + + if (pDevice->ChipRevId != T3_CHIP_ID_5703_A1 && + pDevice->ChipRevId != T3_CHIP_ID_5703_A2 && + pDevice->ChipRevId != T3_CHIP_ID_5704_A0) { + pDevice->UndiFix = FALSE; + } +#if !PCIX_TARGET_WORKAROUND + pDevice->UndiFix = FALSE; +#endif + /* Map the memory base to system address space. */ + if (!pDevice->UndiFix) { + Status = MM_MapMemBase (pDevice); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + /* Initialize the memory view pointer. */ + pDevice->pMemView = (PT3_STD_MEM_MAP) pDevice->pMappedMemBase; + } +#if PCIX_TARGET_WORKAROUND + /* store whether we are in PCI are PCI-X mode */ + pDevice->EnablePciXFix = FALSE; + + MM_ReadConfig32 (pDevice, T3_PCI_STATE_REG, &Value32); + if ((Value32 & T3_PCI_STATE_CONVENTIONAL_PCI_MODE) == 0) { + /* Enable PCI-X workaround only if we are running on 5700 BX. */ + if (T3_CHIP_REV (pDevice->ChipRevId) == T3_CHIP_REV_5700_BX) { + pDevice->EnablePciXFix = TRUE; + } + } + if (pDevice->UndiFix) { + pDevice->EnablePciXFix = TRUE; + } +#endif + /* Bx bug: due to the "byte_enable bug" in PCI-X mode, the power */ + /* management register may be clobbered which may cause the */ + /* BCM5700 to go into D3 state. While in this state, we will */ + /* not have memory mapped register access. As a workaround, we */ + /* need to restore the device to D0 state. */ + MM_ReadConfig32 (pDevice, T3_PCI_PM_STATUS_CTRL_REG, &Value32); + Value32 |= T3_PM_PME_ASSERTED; + Value32 &= ~T3_PM_POWER_STATE_MASK; + Value32 |= T3_PM_POWER_STATE_D0; + MM_WriteConfig32 (pDevice, T3_PCI_PM_STATUS_CTRL_REG, Value32); + + /* read the current PCI command word */ + MM_ReadConfig32 (pDevice, PCI_COMMAND_REG, &Value32); + + /* Make sure bus-mastering is enabled. */ + Value32 |= PCI_BUSMASTER_ENABLE; + +#if PCIX_TARGET_WORKAROUND + /* if we are in PCI-X mode, also make sure mem-mapping and SERR#/PERR# + are enabled */ + if (pDevice->EnablePciXFix == TRUE) { + Value32 |= (PCI_MEM_SPACE_ENABLE | PCI_SYSTEM_ERROR_ENABLE | + PCI_PARITY_ERROR_ENABLE); + } + if (pDevice->UndiFix) { + Value32 &= ~PCI_MEM_SPACE_ENABLE; + } +#endif + + if (pDevice->EnableMWI) { + Value32 |= PCI_MEMORY_WRITE_INVALIDATE; + } else { + Value32 &= (~PCI_MEMORY_WRITE_INVALIDATE); + } + + /* Error out if mem-mapping is NOT enabled for PCI systems */ + if (!(Value32 | PCI_MEM_SPACE_ENABLE)) { + return LM_STATUS_FAILURE; + } + + /* save the value we are going to write into the PCI command word */ + pDevice->PciCommandStatusWords = Value32; + + Status = MM_WriteConfig32 (pDevice, PCI_COMMAND_REG, Value32); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + + /* Set power state to D0. */ + LM_SetPowerState (pDevice, LM_POWER_STATE_D0); + +#ifdef BIG_ENDIAN_PCI + pDevice->MiscHostCtrl = + MISC_HOST_CTRL_MASK_PCI_INT | + MISC_HOST_CTRL_ENABLE_INDIRECT_ACCESS | + MISC_HOST_CTRL_ENABLE_ENDIAN_WORD_SWAP | + MISC_HOST_CTRL_ENABLE_PCI_STATE_REG_RW; +#else /* No CPU Swap modes for PCI IO */ + + /* Setup the mode registers. */ + pDevice->MiscHostCtrl = + MISC_HOST_CTRL_MASK_PCI_INT | + MISC_HOST_CTRL_ENABLE_ENDIAN_WORD_SWAP | +#ifdef BIG_ENDIAN_HOST + MISC_HOST_CTRL_ENABLE_ENDIAN_BYTE_SWAP | +#endif /* BIG_ENDIAN_HOST */ + MISC_HOST_CTRL_ENABLE_INDIRECT_ACCESS | + MISC_HOST_CTRL_ENABLE_PCI_STATE_REG_RW; +#endif /* !BIG_ENDIAN_PCI */ + + /* write to PCI misc host ctr first in order to enable indirect accesses */ + MM_WriteConfig32 (pDevice, T3_PCI_MISC_HOST_CTRL_REG, + pDevice->MiscHostCtrl); + + REG_WR (pDevice, PciCfg.MiscHostCtrl, pDevice->MiscHostCtrl); + +#ifdef BIG_ENDIAN_PCI + Value32 = GRC_MODE_WORD_SWAP_DATA | GRC_MODE_WORD_SWAP_NON_FRAME_DATA; +#else +/* No CPU Swap modes for PCI IO */ +#ifdef BIG_ENDIAN_HOST + Value32 = GRC_MODE_BYTE_SWAP_NON_FRAME_DATA | + GRC_MODE_WORD_SWAP_NON_FRAME_DATA; +#else + Value32 = GRC_MODE_BYTE_SWAP_NON_FRAME_DATA | GRC_MODE_BYTE_SWAP_DATA; +#endif +#endif /* !BIG_ENDIAN_PCI */ + + REG_WR (pDevice, Grc.Mode, Value32); + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + REG_WR (pDevice, Grc.LocalCtrl, + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT1 | + GRC_MISC_LOCAL_CTRL_GPIO_OE1); + } + MM_Wait (40); + + /* Enable indirect memory access */ + REG_WR (pDevice, MemArbiter.Mode, T3_MEM_ARBITER_MODE_ENABLE); + + if (REG_RD (pDevice, PciCfg.ClockCtrl) & T3_PCI_44MHZ_CORE_CLOCK) { + REG_WR (pDevice, PciCfg.ClockCtrl, T3_PCI_44MHZ_CORE_CLOCK | + T3_PCI_SELECT_ALTERNATE_CLOCK); + REG_WR (pDevice, PciCfg.ClockCtrl, + T3_PCI_SELECT_ALTERNATE_CLOCK); + MM_Wait (40); /* required delay is 27usec */ + } + REG_WR (pDevice, PciCfg.ClockCtrl, 0); + REG_WR (pDevice, PciCfg.MemWindowBaseAddr, 0); + +#if PCIX_TARGET_WORKAROUND + MM_ReadConfig32 (pDevice, T3_PCI_STATE_REG, &Value32); + if ((pDevice->EnablePciXFix == FALSE) && + ((Value32 & T3_PCI_STATE_CONVENTIONAL_PCI_MODE) == 0)) { + if (pDevice->ChipRevId == T3_CHIP_ID_5701_A0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B2 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B5) { + __raw_writel (0, + &(pDevice->pMemView->uIntMem. + MemBlock32K[0x300])); + __raw_writel (0, + &(pDevice->pMemView->uIntMem. + MemBlock32K[0x301])); + __raw_writel (0xffffffff, + &(pDevice->pMemView->uIntMem. + MemBlock32K[0x301])); + if (__raw_readl + (&(pDevice->pMemView->uIntMem.MemBlock32K[0x300]))) + { + pDevice->EnablePciXFix = TRUE; + } + } + } +#endif +#if 1 + /* + * This code was at the beginning of else block below, but that's + * a bug if node address in shared memory. + */ + MM_Wait (50); + LM_NvramInit (pDevice); +#endif + /* Get the node address. First try to get in from the shared memory. */ + /* If the signature is not present, then get it from the NVRAM. */ + Value32 = MEM_RD_OFFSET (pDevice, T3_MAC_ADDR_HIGH_MAILBOX); + if ((Value32 >> 16) == 0x484b) { + + pDevice->NodeAddress[0] = (LM_UINT8) (Value32 >> 8); + pDevice->NodeAddress[1] = (LM_UINT8) Value32; + + Value32 = MEM_RD_OFFSET (pDevice, T3_MAC_ADDR_LOW_MAILBOX); + + pDevice->NodeAddress[2] = (LM_UINT8) (Value32 >> 24); + pDevice->NodeAddress[3] = (LM_UINT8) (Value32 >> 16); + pDevice->NodeAddress[4] = (LM_UINT8) (Value32 >> 8); + pDevice->NodeAddress[5] = (LM_UINT8) Value32; + + Status = LM_STATUS_SUCCESS; + } else { + Status = LM_NvramRead (pDevice, 0x7c, &Value32); + if (Status == LM_STATUS_SUCCESS) { + pDevice->NodeAddress[0] = (LM_UINT8) (Value32 >> 16); + pDevice->NodeAddress[1] = (LM_UINT8) (Value32 >> 24); + + Status = LM_NvramRead (pDevice, 0x80, &Value32); + + pDevice->NodeAddress[2] = (LM_UINT8) Value32; + pDevice->NodeAddress[3] = (LM_UINT8) (Value32 >> 8); + pDevice->NodeAddress[4] = (LM_UINT8) (Value32 >> 16); + pDevice->NodeAddress[5] = (LM_UINT8) (Value32 >> 24); + } + } + + /* Assign a default address. */ + if (Status != LM_STATUS_SUCCESS) { +#ifndef EMBEDDED + printk (KERN_ERR + "Cannot get MAC addr from NVRAM. Using default.\n"); +#endif + pDevice->NodeAddress[0] = 0x00; + pDevice->NodeAddress[1] = 0x10; + pDevice->NodeAddress[2] = 0x18; + pDevice->NodeAddress[3] = 0x68; + pDevice->NodeAddress[4] = 0x61; + pDevice->NodeAddress[5] = 0x76; + } + + pDevice->PermanentNodeAddress[0] = pDevice->NodeAddress[0]; + pDevice->PermanentNodeAddress[1] = pDevice->NodeAddress[1]; + pDevice->PermanentNodeAddress[2] = pDevice->NodeAddress[2]; + pDevice->PermanentNodeAddress[3] = pDevice->NodeAddress[3]; + pDevice->PermanentNodeAddress[4] = pDevice->NodeAddress[4]; + pDevice->PermanentNodeAddress[5] = pDevice->NodeAddress[5]; + + /* Initialize the default values. */ + pDevice->NoTxPseudoHdrChksum = FALSE; + pDevice->NoRxPseudoHdrChksum = FALSE; + pDevice->NicSendBd = FALSE; + pDevice->TxPacketDescCnt = DEFAULT_TX_PACKET_DESC_COUNT; + pDevice->RxStdDescCnt = DEFAULT_STD_RCV_DESC_COUNT; + pDevice->RxCoalescingTicks = DEFAULT_RX_COALESCING_TICKS; + pDevice->TxCoalescingTicks = DEFAULT_TX_COALESCING_TICKS; + pDevice->RxMaxCoalescedFrames = DEFAULT_RX_MAX_COALESCED_FRAMES; + pDevice->TxMaxCoalescedFrames = DEFAULT_TX_MAX_COALESCED_FRAMES; + pDevice->RxCoalescingTicksDuringInt = BAD_DEFAULT_VALUE; + pDevice->TxCoalescingTicksDuringInt = BAD_DEFAULT_VALUE; + pDevice->RxMaxCoalescedFramesDuringInt = BAD_DEFAULT_VALUE; + pDevice->TxMaxCoalescedFramesDuringInt = BAD_DEFAULT_VALUE; + pDevice->StatsCoalescingTicks = DEFAULT_STATS_COALESCING_TICKS; + pDevice->EnableMWI = FALSE; + pDevice->TxMtu = MAX_ETHERNET_PACKET_SIZE_NO_CRC; + pDevice->RxMtu = MAX_ETHERNET_PACKET_SIZE_NO_CRC; + pDevice->DisableAutoNeg = FALSE; + pDevice->PhyIntMode = T3_PHY_INT_MODE_AUTO; + pDevice->LinkChngMode = T3_LINK_CHNG_MODE_AUTO; + pDevice->LedMode = LED_MODE_AUTO; + pDevice->ResetPhyOnInit = TRUE; + pDevice->DelayPciGrant = TRUE; + pDevice->UseTaggedStatus = FALSE; + pDevice->OneDmaAtOnce = BAD_DEFAULT_VALUE; + + pDevice->DmaMbufLowMark = T3_DEF_DMA_MBUF_LOW_WMARK_JUMBO; + pDevice->RxMacMbufLowMark = T3_DEF_RX_MAC_MBUF_LOW_WMARK_JUMBO; + pDevice->MbufHighMark = T3_DEF_MBUF_HIGH_WMARK_JUMBO; + + pDevice->RequestedMediaType = LM_REQUESTED_MEDIA_TYPE_AUTO; + pDevice->TaskOffloadCap = LM_TASK_OFFLOAD_NONE; + pDevice->FlowControlCap = LM_FLOW_CONTROL_AUTO_PAUSE; + pDevice->EnableTbi = FALSE; +#if INCLUDE_TBI_SUPPORT + pDevice->PollTbiLink = BAD_DEFAULT_VALUE; +#endif + + switch (T3_ASIC_REV (pDevice->ChipRevId)) { + case T3_ASIC_REV_5704: + pDevice->MbufBase = T3_NIC_MBUF_POOL_ADDR; + pDevice->MbufSize = T3_NIC_MBUF_POOL_SIZE64; + break; + default: + pDevice->MbufBase = T3_NIC_MBUF_POOL_ADDR; + pDevice->MbufSize = T3_NIC_MBUF_POOL_SIZE96; + break; + } + + pDevice->LinkStatus = LM_STATUS_LINK_DOWN; + pDevice->QueueRxPackets = TRUE; + + pDevice->EnableWireSpeed = TRUE; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + pDevice->RxJumboDescCnt = DEFAULT_JUMBO_RCV_DESC_COUNT; +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + /* Make this is a known adapter. */ + pAdapterInfo = LM_GetAdapterInfoBySsid (pDevice->SubsystemVendorId, + pDevice->SubsystemId); + + pDevice->BondId = REG_RD (pDevice, Grc.MiscCfg) & GRC_MISC_BD_ID_MASK; + if (pDevice->BondId != GRC_MISC_BD_ID_5700 && + pDevice->BondId != GRC_MISC_BD_ID_5701 && + pDevice->BondId != GRC_MISC_BD_ID_5702FE && + pDevice->BondId != GRC_MISC_BD_ID_5703 && + pDevice->BondId != GRC_MISC_BD_ID_5703S && + pDevice->BondId != GRC_MISC_BD_ID_5704 && + pDevice->BondId != GRC_MISC_BD_ID_5704CIOBE) { + return LM_STATUS_UNKNOWN_ADAPTER; + } + + pDevice->SplitModeEnable = SPLIT_MODE_DISABLE; + if ((pDevice->ChipRevId == T3_CHIP_ID_5704_A0) && + (pDevice->BondId == GRC_MISC_BD_ID_5704CIOBE)) { + pDevice->SplitModeEnable = SPLIT_MODE_ENABLE; + pDevice->SplitModeMaxReq = SPLIT_MODE_5704_MAX_REQ; + } + + /* Get Eeprom info. */ + Value32 = MEM_RD_OFFSET (pDevice, T3_NIC_DATA_SIG_ADDR); + if (Value32 == T3_NIC_DATA_SIG) { + EeSigFound = TRUE; + Value32 = MEM_RD_OFFSET (pDevice, T3_NIC_DATA_NIC_CFG_ADDR); + + /* Determine PHY type. */ + switch (Value32 & T3_NIC_CFG_PHY_TYPE_MASK) { + case T3_NIC_CFG_PHY_TYPE_COPPER: + EePhyTypeSerdes = FALSE; + break; + + case T3_NIC_CFG_PHY_TYPE_FIBER: + EePhyTypeSerdes = TRUE; + break; + + default: + EePhyTypeSerdes = FALSE; + break; + } + + /* Determine PHY led mode. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + switch (Value32 & T3_NIC_CFG_LED_MODE_MASK) { + case T3_NIC_CFG_LED_MODE_TRIPLE_SPEED: + EePhyLedMode = LED_MODE_THREE_LINK; + break; + + case T3_NIC_CFG_LED_MODE_LINK_SPEED: + EePhyLedMode = LED_MODE_LINK10; + break; + + default: + EePhyLedMode = LED_MODE_AUTO; + break; + } + } else { + switch (Value32 & T3_NIC_CFG_LED_MODE_MASK) { + case T3_NIC_CFG_LED_MODE_OPEN_DRAIN: + EePhyLedMode = LED_MODE_OPEN_DRAIN; + break; + + case T3_NIC_CFG_LED_MODE_OUTPUT: + EePhyLedMode = LED_MODE_OUTPUT; + break; + + default: + EePhyLedMode = LED_MODE_AUTO; + break; + } + } + if (pDevice->ChipRevId == T3_CHIP_ID_5703_A1 || + pDevice->ChipRevId == T3_CHIP_ID_5703_A2) { + /* Enable EEPROM write protection. */ + if (Value32 & T3_NIC_EEPROM_WP) { + pDevice->EepromWp = TRUE; + } + } + + /* Get the PHY Id. */ + Value32 = MEM_RD_OFFSET (pDevice, T3_NIC_DATA_PHY_ID_ADDR); + if (Value32) { + EePhyId = (((Value32 & T3_NIC_PHY_ID1_MASK) >> 16) & + PHY_ID1_OUI_MASK) << 10; + + Value32 = Value32 & T3_NIC_PHY_ID2_MASK; + + EePhyId |= ((Value32 & PHY_ID2_OUI_MASK) << 16) | + (Value32 & PHY_ID2_MODEL_MASK) | (Value32 & + PHY_ID2_REV_MASK); + } else { + EePhyId = 0; + } + } else { + EeSigFound = FALSE; + } + + /* Set the PHY address. */ + pDevice->PhyAddr = PHY_DEVICE_ID; + + /* Disable auto polling. */ + pDevice->MiMode = 0xc0000; + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode); + MM_Wait (40); + + /* Get the PHY id. */ + LM_ReadPhy (pDevice, PHY_ID1_REG, &Value32); + pDevice->PhyId = (Value32 & PHY_ID1_OUI_MASK) << 10; + + LM_ReadPhy (pDevice, PHY_ID2_REG, &Value32); + pDevice->PhyId |= ((Value32 & PHY_ID2_OUI_MASK) << 16) | + (Value32 & PHY_ID2_MODEL_MASK) | (Value32 & PHY_ID2_REV_MASK); + + /* Set the EnableTbi flag to false if we have a copper PHY. */ + switch (pDevice->PhyId & PHY_ID_MASK) { + case PHY_BCM5400_PHY_ID: + pDevice->EnableTbi = FALSE; + break; + + case PHY_BCM5401_PHY_ID: + pDevice->EnableTbi = FALSE; + break; + + case PHY_BCM5411_PHY_ID: + pDevice->EnableTbi = FALSE; + break; + + case PHY_BCM5701_PHY_ID: + pDevice->EnableTbi = FALSE; + break; + + case PHY_BCM5703_PHY_ID: + pDevice->EnableTbi = FALSE; + break; + + case PHY_BCM5704_PHY_ID: + pDevice->EnableTbi = FALSE; + break; + + case PHY_BCM8002_PHY_ID: + pDevice->EnableTbi = TRUE; + break; + + default: + + if (pAdapterInfo) { + pDevice->PhyId = pAdapterInfo->PhyId; + pDevice->EnableTbi = pAdapterInfo->Serdes; + } else if (EeSigFound) { + pDevice->PhyId = EePhyId; + pDevice->EnableTbi = EePhyTypeSerdes; + } + break; + } + + /* Bail out if we don't know the copper PHY id. */ + if (UNKNOWN_PHY_ID (pDevice->PhyId) && !pDevice->EnableTbi) { + return LM_STATUS_FAILURE; + } + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5703) { + if ((pDevice->SavedCacheLineReg & 0xff00) < 0x4000) { + pDevice->SavedCacheLineReg &= 0xffff00ff; + pDevice->SavedCacheLineReg |= 0x4000; + } + } + /* Change driver parameters. */ + Status = MM_GetConfig (pDevice); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } +#if INCLUDE_5701_AX_FIX + if (pDevice->ChipRevId == T3_CHIP_ID_5701_A0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B0) { + pDevice->ResetPhyOnInit = TRUE; + } +#endif + + /* Save the current phy link status. */ + if (!pDevice->EnableTbi) { + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + + /* If we don't have link reset the PHY. */ + if (!(Value32 & PHY_STATUS_LINK_PASS) + || pDevice->ResetPhyOnInit) { + + LM_WritePhy (pDevice, PHY_CTRL_REG, PHY_CTRL_PHY_RESET); + + for (j = 0; j < 100; j++) { + MM_Wait (10); + + LM_ReadPhy (pDevice, PHY_CTRL_REG, &Value32); + if (Value32 && !(Value32 & PHY_CTRL_PHY_RESET)) { + MM_Wait (40); + break; + } + } + +#if INCLUDE_5701_AX_FIX + /* 5701_AX_BX bug: only advertises 10mb speed. */ + if (pDevice->ChipRevId == T3_CHIP_ID_5701_A0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B0) { + + Value32 = PHY_AN_AD_PROTOCOL_802_3_CSMA_CD | + PHY_AN_AD_10BASET_HALF | + PHY_AN_AD_10BASET_FULL | + PHY_AN_AD_100BASETX_FULL | + PHY_AN_AD_100BASETX_HALF; + Value32 |= GetPhyAdFlowCntrlSettings (pDevice); + LM_WritePhy (pDevice, PHY_AN_AD_REG, Value32); + pDevice->advertising = Value32; + + Value32 = BCM540X_AN_AD_1000BASET_HALF | + BCM540X_AN_AD_1000BASET_FULL | + BCM540X_CONFIG_AS_MASTER | + BCM540X_ENABLE_CONFIG_AS_MASTER; + LM_WritePhy (pDevice, + BCM540X_1000BASET_CTRL_REG, + Value32); + pDevice->advertising1000 = Value32; + + LM_WritePhy (pDevice, PHY_CTRL_REG, + PHY_CTRL_AUTO_NEG_ENABLE | + PHY_CTRL_RESTART_AUTO_NEG); + } +#endif + if (T3_ASIC_REV (pDevice->ChipRevId) == + T3_ASIC_REV_5703) { + LM_WritePhy (pDevice, 0x18, 0x0c00); + LM_WritePhy (pDevice, 0x17, 0x201f); + LM_WritePhy (pDevice, 0x15, 0x2aaa); + } + if (pDevice->ChipRevId == T3_CHIP_ID_5704_A0) { + LM_WritePhy (pDevice, 0x1c, 0x8d68); + LM_WritePhy (pDevice, 0x1c, 0x8d68); + } + /* Enable Ethernet@WireSpeed. */ + if (pDevice->EnableWireSpeed) { + LM_WritePhy (pDevice, 0x18, 0x7007); + LM_ReadPhy (pDevice, 0x18, &Value32); + LM_WritePhy (pDevice, 0x18, + Value32 | BIT_15 | BIT_4); + } + } + } + + /* Turn off tap power management. */ + if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5401_PHY_ID) { + LM_WritePhy (pDevice, BCM5401_AUX_CTRL, 0x0c20); + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x0012); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x1804); + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x0013); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x1204); + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x8006); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x0132); + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x8006); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x0232); + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x201f); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x0a20); + + MM_Wait (40); + } +#if INCLUDE_TBI_SUPPORT + pDevice->IgnoreTbiLinkChange = FALSE; + + if (pDevice->EnableTbi) { + pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_NONE; + pDevice->PhyIntMode = T3_PHY_INT_MODE_LINK_READY; + if ((pDevice->PollTbiLink == BAD_DEFAULT_VALUE) || + pDevice->DisableAutoNeg) { + pDevice->PollTbiLink = FALSE; + } + } else { + pDevice->PollTbiLink = FALSE; + } +#endif /* INCLUDE_TBI_SUPPORT */ + + /* UseTaggedStatus is only valid for 5701 and later. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + pDevice->UseTaggedStatus = FALSE; + + pDevice->CoalesceMode = 0; + } else { + pDevice->CoalesceMode = + HOST_COALESCE_CLEAR_TICKS_ON_RX_BD_EVENT | + HOST_COALESCE_CLEAR_TICKS_ON_TX_BD_EVENT; + } + + /* Set the status block size. */ + if (T3_CHIP_REV (pDevice->ChipRevId) != T3_CHIP_REV_5700_AX && + T3_CHIP_REV (pDevice->ChipRevId) != T3_CHIP_REV_5700_BX) { + pDevice->CoalesceMode |= HOST_COALESCE_32_BYTE_STATUS_MODE; + } + + /* Check the DURING_INT coalescing ticks parameters. */ + if (pDevice->UseTaggedStatus) { + if (pDevice->RxCoalescingTicksDuringInt == BAD_DEFAULT_VALUE) { + pDevice->RxCoalescingTicksDuringInt = + DEFAULT_RX_COALESCING_TICKS_DURING_INT; + } + + if (pDevice->TxCoalescingTicksDuringInt == BAD_DEFAULT_VALUE) { + pDevice->TxCoalescingTicksDuringInt = + DEFAULT_TX_COALESCING_TICKS_DURING_INT; + } + + if (pDevice->RxMaxCoalescedFramesDuringInt == BAD_DEFAULT_VALUE) { + pDevice->RxMaxCoalescedFramesDuringInt = + DEFAULT_RX_MAX_COALESCED_FRAMES_DURING_INT; + } + + if (pDevice->TxMaxCoalescedFramesDuringInt == BAD_DEFAULT_VALUE) { + pDevice->TxMaxCoalescedFramesDuringInt = + DEFAULT_TX_MAX_COALESCED_FRAMES_DURING_INT; + } + } else { + if (pDevice->RxCoalescingTicksDuringInt == BAD_DEFAULT_VALUE) { + pDevice->RxCoalescingTicksDuringInt = 0; + } + + if (pDevice->TxCoalescingTicksDuringInt == BAD_DEFAULT_VALUE) { + pDevice->TxCoalescingTicksDuringInt = 0; + } + + if (pDevice->RxMaxCoalescedFramesDuringInt == BAD_DEFAULT_VALUE) { + pDevice->RxMaxCoalescedFramesDuringInt = 0; + } + + if (pDevice->TxMaxCoalescedFramesDuringInt == BAD_DEFAULT_VALUE) { + pDevice->TxMaxCoalescedFramesDuringInt = 0; + } + } + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + if (pDevice->RxMtu <= (MAX_STD_RCV_BUFFER_SIZE - 8 /* CRC */ )) { + pDevice->RxJumboDescCnt = 0; + if (pDevice->RxMtu <= MAX_ETHERNET_PACKET_SIZE_NO_CRC) { + pDevice->RxMtu = MAX_ETHERNET_PACKET_SIZE_NO_CRC; + } + } else { + pDevice->RxJumboBufferSize = + (pDevice->RxMtu + 8 /* CRC + VLAN */ + + COMMON_CACHE_LINE_SIZE - 1) & ~COMMON_CACHE_LINE_MASK; + + if (pDevice->RxJumboBufferSize > MAX_JUMBO_RCV_BUFFER_SIZE) { + pDevice->RxJumboBufferSize = + DEFAULT_JUMBO_RCV_BUFFER_SIZE; + pDevice->RxMtu = + pDevice->RxJumboBufferSize - 8 /* CRC + VLAN */ ; + } + pDevice->TxMtu = pDevice->RxMtu; + + } +#else + pDevice->RxMtu = MAX_ETHERNET_PACKET_SIZE_NO_CRC; +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + pDevice->RxPacketDescCnt = +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + pDevice->RxJumboDescCnt + +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + pDevice->RxStdDescCnt; + + if (pDevice->TxMtu < MAX_ETHERNET_PACKET_SIZE_NO_CRC) { + pDevice->TxMtu = MAX_ETHERNET_PACKET_SIZE_NO_CRC; + } + + if (pDevice->TxMtu > MAX_JUMBO_TX_BUFFER_SIZE) { + pDevice->TxMtu = MAX_JUMBO_TX_BUFFER_SIZE; + } + + /* Configure the proper ways to get link change interrupt. */ + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_AUTO) { + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + pDevice->PhyIntMode = T3_PHY_INT_MODE_MI_INTERRUPT; + } else { + pDevice->PhyIntMode = T3_PHY_INT_MODE_LINK_READY; + } + } else if (pDevice->PhyIntMode == T3_PHY_INT_MODE_AUTO_POLLING) { + /* Auto-polling does not work on 5700_AX and 5700_BX. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + pDevice->PhyIntMode = T3_PHY_INT_MODE_MI_INTERRUPT; + } + } + + /* Determine the method to get link change status. */ + if (pDevice->LinkChngMode == T3_LINK_CHNG_MODE_AUTO) { + /* The link status bit in the status block does not work on 5700_AX */ + /* and 5700_BX chips. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + pDevice->LinkChngMode = + T3_LINK_CHNG_MODE_USE_STATUS_REG; + } else { + pDevice->LinkChngMode = + T3_LINK_CHNG_MODE_USE_STATUS_BLOCK; + } + } + + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_MI_INTERRUPT || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + pDevice->LinkChngMode = T3_LINK_CHNG_MODE_USE_STATUS_REG; + } + + /* Configure PHY led mode. */ + if (pDevice->LedMode == LED_MODE_AUTO) { + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + if (pDevice->SubsystemVendorId == T3_SVID_DELL) { + pDevice->LedMode = LED_MODE_LINK10; + } else { + pDevice->LedMode = LED_MODE_THREE_LINK; + + if (EeSigFound && EePhyLedMode != LED_MODE_AUTO) { + pDevice->LedMode = EePhyLedMode; + } + } + + /* bug? 5701 in LINK10 mode does not seem to work when */ + /* PhyIntMode is LINK_READY. */ + if (T3_ASIC_REV (pDevice->ChipRevId) != T3_ASIC_REV_5700 + && +#if INCLUDE_TBI_SUPPORT + pDevice->EnableTbi == FALSE && +#endif + pDevice->LedMode == LED_MODE_LINK10) { + pDevice->PhyIntMode = + T3_PHY_INT_MODE_MI_INTERRUPT; + pDevice->LinkChngMode = + T3_LINK_CHNG_MODE_USE_STATUS_REG; + } + + if (pDevice->EnableTbi) { + pDevice->LedMode = LED_MODE_THREE_LINK; + } + } else { + if (EeSigFound && EePhyLedMode != LED_MODE_AUTO) { + pDevice->LedMode = EePhyLedMode; + } else { + pDevice->LedMode = LED_MODE_OPEN_DRAIN; + } + } + } + + /* Enable OneDmaAtOnce. */ + if (pDevice->OneDmaAtOnce == BAD_DEFAULT_VALUE) { + pDevice->OneDmaAtOnce = FALSE; + } + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + pDevice->ChipRevId == T3_CHIP_ID_5701_A0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B2) { + pDevice->WolSpeed = WOL_SPEED_10MB; + } else { + pDevice->WolSpeed = WOL_SPEED_100MB; + } + + /* Offloadings. */ + pDevice->TaskToOffload = LM_TASK_OFFLOAD_NONE; + + /* Turn off task offloading on Ax. */ + if (pDevice->ChipRevId == T3_CHIP_ID_5700_B0) { + pDevice->TaskOffloadCap &= ~(LM_TASK_OFFLOAD_TX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_TX_UDP_CHECKSUM); + } + pDevice->PciState = REG_RD (pDevice, PciCfg.PciState); + LM_ReadVPD (pDevice); + LM_ReadBootCodeVersion (pDevice); + LM_GetBusSpeed (pDevice); + + return LM_STATUS_SUCCESS; +} /* LM_GetAdapterInfo */ + +STATIC PLM_ADAPTER_INFO LM_GetAdapterInfoBySsid (LM_UINT16 Svid, LM_UINT16 Ssid) +{ + static LM_ADAPTER_INFO AdapterArr[] = { + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95700A6, + PHY_BCM5401_PHY_ID, 0}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95701A5, + PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95700T6, + PHY_BCM8002_PHY_ID, 1}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95700A9, 0, 1}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95701T1, + PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95701T8, + PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95701A7, 0, 1}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95701A10, + PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95701A12, + PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95703Ax1, + PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_BROADCOM, T3_SSID_BROADCOM_BCM95703Ax2, + PHY_BCM5701_PHY_ID, 0}, + + {T3_SVID_3COM, T3_SSID_3COM_3C996T, PHY_BCM5401_PHY_ID, 0}, + {T3_SVID_3COM, T3_SSID_3COM_3C996BT, PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_3COM, T3_SSID_3COM_3C996SX, 0, 1}, + {T3_SVID_3COM, T3_SSID_3COM_3C1000T, PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_3COM, T3_SSID_3COM_3C940BR01, PHY_BCM5701_PHY_ID, 0}, + + {T3_SVID_DELL, T3_SSID_DELL_VIPER, PHY_BCM5401_PHY_ID, 0}, + {T3_SVID_DELL, T3_SSID_DELL_JAGUAR, PHY_BCM5401_PHY_ID, 0}, + {T3_SVID_DELL, T3_SSID_DELL_MERLOT, PHY_BCM5411_PHY_ID, 0}, + {T3_SVID_DELL, T3_SSID_DELL_SLIM_MERLOT, PHY_BCM5411_PHY_ID, 0}, + + {T3_SVID_COMPAQ, T3_SSID_COMPAQ_BANSHEE, PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_COMPAQ, T3_SSID_COMPAQ_BANSHEE_2, PHY_BCM5701_PHY_ID, + 0}, + {T3_SVID_COMPAQ, T3_SSID_COMPAQ_CHANGELING, 0, 1}, + {T3_SVID_COMPAQ, T3_SSID_COMPAQ_NC7780, PHY_BCM5701_PHY_ID, 0}, + {T3_SVID_COMPAQ, T3_SSID_COMPAQ_NC7780_2, PHY_BCM5701_PHY_ID, + 0}, + + }; + LM_UINT32 j; + + for (j = 0; j < sizeof (AdapterArr) / sizeof (LM_ADAPTER_INFO); j++) { + if (AdapterArr[j].Svid == Svid && AdapterArr[j].Ssid == Ssid) { + return &AdapterArr[j]; + } + } + + return NULL; +} + +/******************************************************************************/ +/* Description: */ +/* This routine sets up receive/transmit buffer descriptions queues. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_InitializeAdapter (PLM_DEVICE_BLOCK pDevice) +{ + LM_PHYSICAL_ADDRESS MemPhy; + PLM_UINT8 pMemVirt; + PLM_PACKET pPacket; + LM_STATUS Status; + LM_UINT32 Size; + LM_UINT32 j; + + /* Set power state to D0. */ + LM_SetPowerState (pDevice, LM_POWER_STATE_D0); + + /* Intialize the queues. */ + QQ_InitQueue (&pDevice->RxPacketReceivedQ.Container, + MAX_RX_PACKET_DESC_COUNT); + QQ_InitQueue (&pDevice->RxPacketFreeQ.Container, + MAX_RX_PACKET_DESC_COUNT); + + QQ_InitQueue (&pDevice->TxPacketFreeQ.Container, + MAX_TX_PACKET_DESC_COUNT); + QQ_InitQueue (&pDevice->TxPacketActiveQ.Container, + MAX_TX_PACKET_DESC_COUNT); + QQ_InitQueue (&pDevice->TxPacketXmittedQ.Container, + MAX_TX_PACKET_DESC_COUNT); + + /* Allocate shared memory for: status block, the buffers for receive */ + /* rings -- standard, mini, jumbo, and return rings. */ + Size = T3_STATUS_BLOCK_SIZE + sizeof (T3_STATS_BLOCK) + + T3_STD_RCV_RCB_ENTRY_COUNT * sizeof (T3_RCV_BD) + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + T3_JUMBO_RCV_RCB_ENTRY_COUNT * sizeof (T3_RCV_BD) + +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + T3_RCV_RETURN_RCB_ENTRY_COUNT * sizeof (T3_RCV_BD); + + /* Memory for host based Send BD. */ + if (pDevice->NicSendBd == FALSE) { + Size += sizeof (T3_SND_BD) * T3_SEND_RCB_ENTRY_COUNT; + } + + /* Allocate the memory block. */ + Status = + MM_AllocateSharedMemory (pDevice, Size, (PLM_VOID) & pMemVirt, + &MemPhy, FALSE); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + + /* Program DMA Read/Write */ + if (pDevice->PciState & T3_PCI_STATE_NOT_PCI_X_BUS) { + pDevice->DmaReadWriteCtrl = 0x763f000f; + } else { + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5704) { + pDevice->DmaReadWriteCtrl = 0x761f0000; + } else { + pDevice->DmaReadWriteCtrl = 0x761b000f; + } + if (pDevice->ChipRevId == T3_CHIP_ID_5703_A1 || + pDevice->ChipRevId == T3_CHIP_ID_5703_A2) { + pDevice->OneDmaAtOnce = TRUE; + } + } + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5703) { + pDevice->DmaReadWriteCtrl &= 0xfffffff0; + } + + if (pDevice->OneDmaAtOnce) { + pDevice->DmaReadWriteCtrl |= DMA_CTRL_WRITE_ONE_DMA_AT_ONCE; + } + REG_WR (pDevice, PciCfg.DmaReadWriteCtrl, pDevice->DmaReadWriteCtrl); + + if (LM_DmaTest (pDevice, pMemVirt, MemPhy, 0x400) != LM_STATUS_SUCCESS) { + return LM_STATUS_FAILURE; + } + + /* Status block. */ + pDevice->pStatusBlkVirt = (PT3_STATUS_BLOCK) pMemVirt; + pDevice->StatusBlkPhy = MemPhy; + pMemVirt += T3_STATUS_BLOCK_SIZE; + LM_INC_PHYSICAL_ADDRESS (&MemPhy, T3_STATUS_BLOCK_SIZE); + + /* Statistics block. */ + pDevice->pStatsBlkVirt = (PT3_STATS_BLOCK) pMemVirt; + pDevice->StatsBlkPhy = MemPhy; + pMemVirt += sizeof (T3_STATS_BLOCK); + LM_INC_PHYSICAL_ADDRESS (&MemPhy, sizeof (T3_STATS_BLOCK)); + + /* Receive standard BD buffer. */ + pDevice->pRxStdBdVirt = (PT3_RCV_BD) pMemVirt; + pDevice->RxStdBdPhy = MemPhy; + + pMemVirt += T3_STD_RCV_RCB_ENTRY_COUNT * sizeof (T3_RCV_BD); + LM_INC_PHYSICAL_ADDRESS (&MemPhy, + T3_STD_RCV_RCB_ENTRY_COUNT * + sizeof (T3_RCV_BD)); + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + /* Receive jumbo BD buffer. */ + pDevice->pRxJumboBdVirt = (PT3_RCV_BD) pMemVirt; + pDevice->RxJumboBdPhy = MemPhy; + + pMemVirt += T3_JUMBO_RCV_RCB_ENTRY_COUNT * sizeof (T3_RCV_BD); + LM_INC_PHYSICAL_ADDRESS (&MemPhy, + T3_JUMBO_RCV_RCB_ENTRY_COUNT * + sizeof (T3_RCV_BD)); +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + /* Receive return BD buffer. */ + pDevice->pRcvRetBdVirt = (PT3_RCV_BD) pMemVirt; + pDevice->RcvRetBdPhy = MemPhy; + + pMemVirt += T3_RCV_RETURN_RCB_ENTRY_COUNT * sizeof (T3_RCV_BD); + LM_INC_PHYSICAL_ADDRESS (&MemPhy, + T3_RCV_RETURN_RCB_ENTRY_COUNT * + sizeof (T3_RCV_BD)); + + /* Set up Send BD. */ + if (pDevice->NicSendBd == FALSE) { + pDevice->pSendBdVirt = (PT3_SND_BD) pMemVirt; + pDevice->SendBdPhy = MemPhy; + + pMemVirt += sizeof (T3_SND_BD) * T3_SEND_RCB_ENTRY_COUNT; + LM_INC_PHYSICAL_ADDRESS (&MemPhy, + sizeof (T3_SND_BD) * + T3_SEND_RCB_ENTRY_COUNT); + } else { + pDevice->pSendBdVirt = (PT3_SND_BD) + pDevice->pMemView->uIntMem.First32k.BufferDesc; + pDevice->SendBdPhy.High = 0; + pDevice->SendBdPhy.Low = T3_NIC_SND_BUFFER_DESC_ADDR; + } + + /* Allocate memory for packet descriptors. */ + Size = (pDevice->RxPacketDescCnt + + pDevice->TxPacketDescCnt) * MM_PACKET_DESC_SIZE; + Status = MM_AllocateMemory (pDevice, Size, (PLM_VOID *) & pPacket); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + pDevice->pPacketDescBase = (PLM_VOID) pPacket; + + /* Create transmit packet descriptors from the memory block and add them */ + /* to the TxPacketFreeQ for each send ring. */ + for (j = 0; j < pDevice->TxPacketDescCnt; j++) { + /* Ring index. */ + pPacket->Flags = 0; + + /* Queue the descriptor in the TxPacketFreeQ of the 'k' ring. */ + QQ_PushTail (&pDevice->TxPacketFreeQ.Container, pPacket); + + /* Get the pointer to the next descriptor. MM_PACKET_DESC_SIZE */ + /* is the total size of the packet descriptor including the */ + /* os-specific extensions in the UM_PACKET structure. */ + pPacket = + (PLM_PACKET) ((PLM_UINT8) pPacket + MM_PACKET_DESC_SIZE); + } /* for(j.. */ + + /* Create receive packet descriptors from the memory block and add them */ + /* to the RxPacketFreeQ. Create the Standard packet descriptors. */ + for (j = 0; j < pDevice->RxStdDescCnt; j++) { + /* Receive producer ring. */ + pPacket->u.Rx.RcvProdRing = T3_STD_RCV_PROD_RING; + + /* Receive buffer size. */ + pPacket->u.Rx.RxBufferSize = MAX_STD_RCV_BUFFER_SIZE; + + /* Add the descriptor to RxPacketFreeQ. */ + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + + /* Get the pointer to the next descriptor. MM_PACKET_DESC_SIZE */ + /* is the total size of the packet descriptor including the */ + /* os-specific extensions in the UM_PACKET structure. */ + pPacket = + (PLM_PACKET) ((PLM_UINT8) pPacket + MM_PACKET_DESC_SIZE); + } /* for */ + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + /* Create the Jumbo packet descriptors. */ + for (j = 0; j < pDevice->RxJumboDescCnt; j++) { + /* Receive producer ring. */ + pPacket->u.Rx.RcvProdRing = T3_JUMBO_RCV_PROD_RING; + + /* Receive buffer size. */ + pPacket->u.Rx.RxBufferSize = pDevice->RxJumboBufferSize; + + /* Add the descriptor to RxPacketFreeQ. */ + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + + /* Get the pointer to the next descriptor. MM_PACKET_DESC_SIZE */ + /* is the total size of the packet descriptor including the */ + /* os-specific extensions in the UM_PACKET structure. */ + pPacket = + (PLM_PACKET) ((PLM_UINT8) pPacket + MM_PACKET_DESC_SIZE); + } /* for */ +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + /* Initialize the rest of the packet descriptors. */ + Status = MM_InitializeUmPackets (pDevice); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + + /* if */ + /* Default receive mask. */ + pDevice->ReceiveMask = LM_ACCEPT_MULTICAST | LM_ACCEPT_BROADCAST | + LM_ACCEPT_UNICAST; + + /* Make sure we are in the first 32k memory window or NicSendBd. */ + REG_WR (pDevice, PciCfg.MemWindowBaseAddr, 0); + + /* Initialize the hardware. */ + Status = LM_ResetAdapter (pDevice); + if (Status != LM_STATUS_SUCCESS) { + return Status; + } + + /* We are done with initialization. */ + pDevice->InitDone = TRUE; + + return LM_STATUS_SUCCESS; +} /* LM_InitializeAdapter */ + +/******************************************************************************/ +/* Description: */ +/* This function Enables/Disables a given block. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS +LM_CntrlBlock (PLM_DEVICE_BLOCK pDevice, LM_UINT32 mask, LM_UINT32 cntrl) +{ + LM_UINT32 j, i, data; + LM_UINT32 MaxWaitCnt; + + MaxWaitCnt = 2; + j = 0; + + for (i = 0; i < 32; i++) { + if (!(mask & (1 << i))) + continue; + + switch (1 << i) { + case T3_BLOCK_DMA_RD: + data = REG_RD (pDevice, DmaRead.Mode); + if (cntrl == LM_DISABLE) { + data &= ~DMA_READ_MODE_ENABLE; + REG_WR (pDevice, DmaRead.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, DmaRead.Mode) & + DMA_READ_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, DmaRead.Mode, + data | DMA_READ_MODE_ENABLE); + break; + + case T3_BLOCK_DMA_COMP: + data = REG_RD (pDevice, DmaComp.Mode); + if (cntrl == LM_DISABLE) { + data &= ~DMA_COMP_MODE_ENABLE; + REG_WR (pDevice, DmaComp.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, DmaComp.Mode) & + DMA_COMP_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, DmaComp.Mode, + data | DMA_COMP_MODE_ENABLE); + break; + + case T3_BLOCK_RX_BD_INITIATOR: + data = REG_RD (pDevice, RcvBdIn.Mode); + if (cntrl == LM_DISABLE) { + data &= ~RCV_BD_IN_MODE_ENABLE; + REG_WR (pDevice, RcvBdIn.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, RcvBdIn.Mode) & + RCV_BD_IN_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, RcvBdIn.Mode, + data | RCV_BD_IN_MODE_ENABLE); + break; + + case T3_BLOCK_RX_BD_COMP: + data = REG_RD (pDevice, RcvBdComp.Mode); + if (cntrl == LM_DISABLE) { + data &= ~RCV_BD_COMP_MODE_ENABLE; + REG_WR (pDevice, RcvBdComp.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, RcvBdComp.Mode) & + RCV_BD_COMP_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, RcvBdComp.Mode, + data | RCV_BD_COMP_MODE_ENABLE); + break; + + case T3_BLOCK_DMA_WR: + data = REG_RD (pDevice, DmaWrite.Mode); + if (cntrl == LM_DISABLE) { + data &= ~DMA_WRITE_MODE_ENABLE; + REG_WR (pDevice, DmaWrite.Mode, data); + + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, DmaWrite.Mode) & + DMA_WRITE_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, DmaWrite.Mode, + data | DMA_WRITE_MODE_ENABLE); + break; + + case T3_BLOCK_MSI_HANDLER: + data = REG_RD (pDevice, Msi.Mode); + if (cntrl == LM_DISABLE) { + data &= ~MSI_MODE_ENABLE; + REG_WR (pDevice, Msi.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, Msi.Mode) & + MSI_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, Msi.Mode, + data | MSI_MODE_ENABLE); + break; + + case T3_BLOCK_RX_LIST_PLMT: + data = REG_RD (pDevice, RcvListPlmt.Mode); + if (cntrl == LM_DISABLE) { + data &= ~RCV_LIST_PLMT_MODE_ENABLE; + REG_WR (pDevice, RcvListPlmt.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, RcvListPlmt.Mode) + & RCV_LIST_PLMT_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, RcvListPlmt.Mode, + data | RCV_LIST_PLMT_MODE_ENABLE); + break; + + case T3_BLOCK_RX_LIST_SELECTOR: + data = REG_RD (pDevice, RcvListSel.Mode); + if (cntrl == LM_DISABLE) { + data &= ~RCV_LIST_SEL_MODE_ENABLE; + REG_WR (pDevice, RcvListSel.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, RcvListSel.Mode) & + RCV_LIST_SEL_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, RcvListSel.Mode, + data | RCV_LIST_SEL_MODE_ENABLE); + break; + + case T3_BLOCK_RX_DATA_INITIATOR: + data = REG_RD (pDevice, RcvDataBdIn.Mode); + if (cntrl == LM_DISABLE) { + data &= ~RCV_DATA_BD_IN_MODE_ENABLE; + REG_WR (pDevice, RcvDataBdIn.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, RcvDataBdIn.Mode) + & RCV_DATA_BD_IN_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, RcvDataBdIn.Mode, + data | RCV_DATA_BD_IN_MODE_ENABLE); + break; + + case T3_BLOCK_RX_DATA_COMP: + data = REG_RD (pDevice, RcvDataComp.Mode); + if (cntrl == LM_DISABLE) { + data &= ~RCV_DATA_COMP_MODE_ENABLE; + REG_WR (pDevice, RcvDataComp.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, RcvDataBdIn.Mode) + & RCV_DATA_COMP_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, RcvDataComp.Mode, + data | RCV_DATA_COMP_MODE_ENABLE); + break; + + case T3_BLOCK_HOST_COALESING: + data = REG_RD (pDevice, HostCoalesce.Mode); + if (cntrl == LM_DISABLE) { + data &= ~HOST_COALESCE_ENABLE; + REG_WR (pDevice, HostCoalesce.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, SndBdIn.Mode) & + HOST_COALESCE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, HostCoalesce.Mode, + data | HOST_COALESCE_ENABLE); + break; + + case T3_BLOCK_MAC_RX_ENGINE: + if (cntrl == LM_DISABLE) { + pDevice->RxMode &= ~RX_MODE_ENABLE; + REG_WR (pDevice, MacCtrl.RxMode, + pDevice->RxMode); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, MacCtrl.RxMode) & + RX_MODE_ENABLE)) { + break; + } + MM_Wait (10); + } + } else { + pDevice->RxMode |= RX_MODE_ENABLE; + REG_WR (pDevice, MacCtrl.RxMode, + pDevice->RxMode); + } + break; + + case T3_BLOCK_MBUF_CLUSTER_FREE: + data = REG_RD (pDevice, MbufClusterFree.Mode); + if (cntrl == LM_DISABLE) { + data &= ~MBUF_CLUSTER_FREE_MODE_ENABLE; + REG_WR (pDevice, MbufClusterFree.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD + (pDevice, + MbufClusterFree. + Mode) & + MBUF_CLUSTER_FREE_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, MbufClusterFree.Mode, + data | MBUF_CLUSTER_FREE_MODE_ENABLE); + break; + + case T3_BLOCK_SEND_BD_INITIATOR: + data = REG_RD (pDevice, SndBdIn.Mode); + if (cntrl == LM_DISABLE) { + data &= ~SND_BD_IN_MODE_ENABLE; + REG_WR (pDevice, SndBdIn.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, SndBdIn.Mode) & + SND_BD_IN_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, SndBdIn.Mode, + data | SND_BD_IN_MODE_ENABLE); + break; + + case T3_BLOCK_SEND_BD_COMP: + data = REG_RD (pDevice, SndBdComp.Mode); + if (cntrl == LM_DISABLE) { + data &= ~SND_BD_COMP_MODE_ENABLE; + REG_WR (pDevice, SndBdComp.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, SndBdComp.Mode) & + SND_BD_COMP_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, SndBdComp.Mode, + data | SND_BD_COMP_MODE_ENABLE); + break; + + case T3_BLOCK_SEND_BD_SELECTOR: + data = REG_RD (pDevice, SndBdSel.Mode); + if (cntrl == LM_DISABLE) { + data &= ~SND_BD_SEL_MODE_ENABLE; + REG_WR (pDevice, SndBdSel.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, SndBdSel.Mode) & + SND_BD_SEL_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, SndBdSel.Mode, + data | SND_BD_SEL_MODE_ENABLE); + break; + + case T3_BLOCK_SEND_DATA_INITIATOR: + data = REG_RD (pDevice, SndDataIn.Mode); + if (cntrl == LM_DISABLE) { + data &= ~T3_SND_DATA_IN_MODE_ENABLE; + REG_WR (pDevice, SndDataIn.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, SndDataIn.Mode) & + T3_SND_DATA_IN_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, SndDataIn.Mode, + data | T3_SND_DATA_IN_MODE_ENABLE); + break; + + case T3_BLOCK_SEND_DATA_COMP: + data = REG_RD (pDevice, SndDataComp.Mode); + if (cntrl == LM_DISABLE) { + data &= ~SND_DATA_COMP_MODE_ENABLE; + REG_WR (pDevice, SndDataComp.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, SndDataComp.Mode) + & SND_DATA_COMP_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, SndDataComp.Mode, + data | SND_DATA_COMP_MODE_ENABLE); + break; + + case T3_BLOCK_MAC_TX_ENGINE: + if (cntrl == LM_DISABLE) { + pDevice->TxMode &= ~TX_MODE_ENABLE; + REG_WR (pDevice, MacCtrl.TxMode, + pDevice->TxMode); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, MacCtrl.TxMode) & + TX_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else { + pDevice->TxMode |= TX_MODE_ENABLE; + REG_WR (pDevice, MacCtrl.TxMode, + pDevice->TxMode); + } + break; + + case T3_BLOCK_MEM_ARBITOR: + data = REG_RD (pDevice, MemArbiter.Mode); + if (cntrl == LM_DISABLE) { + data &= ~T3_MEM_ARBITER_MODE_ENABLE; + REG_WR (pDevice, MemArbiter.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, MemArbiter.Mode) & + T3_MEM_ARBITER_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, MemArbiter.Mode, + data | T3_MEM_ARBITER_MODE_ENABLE); + break; + + case T3_BLOCK_MBUF_MANAGER: + data = REG_RD (pDevice, BufMgr.Mode); + if (cntrl == LM_DISABLE) { + data &= ~BUFMGR_MODE_ENABLE; + REG_WR (pDevice, BufMgr.Mode, data); + for (j = 0; j < MaxWaitCnt; j++) { + if (! + (REG_RD (pDevice, BufMgr.Mode) & + BUFMGR_MODE_ENABLE)) + break; + MM_Wait (10); + } + } else + REG_WR (pDevice, BufMgr.Mode, + data | BUFMGR_MODE_ENABLE); + break; + + case T3_BLOCK_MAC_GLOBAL: + if (cntrl == LM_DISABLE) { + pDevice->MacMode &= ~(MAC_MODE_ENABLE_TDE | + MAC_MODE_ENABLE_RDE | + MAC_MODE_ENABLE_FHDE); + } else { + pDevice->MacMode |= (MAC_MODE_ENABLE_TDE | + MAC_MODE_ENABLE_RDE | + MAC_MODE_ENABLE_FHDE); + } + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode); + break; + + default: + return LM_STATUS_FAILURE; + } /* switch */ + + if (j >= MaxWaitCnt) { + return LM_STATUS_FAILURE; + } + } + + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* This function reinitializes the adapter. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_ResetAdapter (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + LM_UINT16 Value16; + LM_UINT32 j, k; + + /* Disable interrupt. */ + LM_DisableInterrupt (pDevice); + + /* May get a spurious interrupt */ + pDevice->pStatusBlkVirt->Status = STATUS_BLOCK_UPDATED; + + /* Disable transmit and receive DMA engines. Abort all pending requests. */ + if (pDevice->InitDone) { + LM_Abort (pDevice); + } + + pDevice->ShuttingDown = FALSE; + + LM_ResetChip (pDevice); + + /* Bug: Athlon fix for B3 silicon only. This bit does not do anything */ + /* in other chip revisions. */ + if (pDevice->DelayPciGrant) { + Value32 = REG_RD (pDevice, PciCfg.ClockCtrl); + REG_WR (pDevice, PciCfg.ClockCtrl, Value32 | BIT_31); + } + + if (pDevice->ChipRevId == T3_CHIP_ID_5704_A0) { + if (!(pDevice->PciState & T3_PCI_STATE_CONVENTIONAL_PCI_MODE)) { + Value32 = REG_RD (pDevice, PciCfg.PciState); + Value32 |= T3_PCI_STATE_RETRY_SAME_DMA; + REG_WR (pDevice, PciCfg.PciState, Value32); + } + } + + /* Enable TaggedStatus mode. */ + if (pDevice->UseTaggedStatus) { + pDevice->MiscHostCtrl |= + MISC_HOST_CTRL_ENABLE_TAGGED_STATUS_MODE; + } + + /* Restore PCI configuration registers. */ + MM_WriteConfig32 (pDevice, PCI_CACHE_LINE_SIZE_REG, + pDevice->SavedCacheLineReg); + MM_WriteConfig32 (pDevice, PCI_SUBSYSTEM_VENDOR_ID_REG, + (pDevice->SubsystemId << 16) | pDevice-> + SubsystemVendorId); + + /* Clear the statistics block. */ + for (j = 0x0300; j < 0x0b00; j++) { + MEM_WR_OFFSET (pDevice, j, 0); + } + + /* Initialize the statistis Block */ + pDevice->pStatusBlkVirt->Status = 0; + pDevice->pStatusBlkVirt->RcvStdConIdx = 0; + pDevice->pStatusBlkVirt->RcvJumboConIdx = 0; + pDevice->pStatusBlkVirt->RcvMiniConIdx = 0; + + for (j = 0; j < 16; j++) { + pDevice->pStatusBlkVirt->Idx[j].RcvProdIdx = 0; + pDevice->pStatusBlkVirt->Idx[j].SendConIdx = 0; + } + + for (k = 0; k < T3_STD_RCV_RCB_ENTRY_COUNT; k++) { + pDevice->pRxStdBdVirt[k].HostAddr.High = 0; + pDevice->pRxStdBdVirt[k].HostAddr.Low = 0; + } + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + /* Receive jumbo BD buffer. */ + for (k = 0; k < T3_JUMBO_RCV_RCB_ENTRY_COUNT; k++) { + pDevice->pRxJumboBdVirt[k].HostAddr.High = 0; + pDevice->pRxJumboBdVirt[k].HostAddr.Low = 0; + } +#endif + + REG_WR (pDevice, PciCfg.DmaReadWriteCtrl, pDevice->DmaReadWriteCtrl); + + /* GRC mode control register. */ +#ifdef BIG_ENDIAN_PCI /* Jimmy, this ifdef block deleted in new code! */ + Value32 = + GRC_MODE_WORD_SWAP_DATA | + GRC_MODE_WORD_SWAP_NON_FRAME_DATA | + GRC_MODE_INT_ON_MAC_ATTN | GRC_MODE_HOST_STACK_UP; +#else + /* No CPU Swap modes for PCI IO */ + Value32 = +#ifdef BIG_ENDIAN_HOST + GRC_MODE_BYTE_SWAP_NON_FRAME_DATA | + GRC_MODE_WORD_SWAP_NON_FRAME_DATA | + GRC_MODE_BYTE_SWAP_DATA | GRC_MODE_WORD_SWAP_DATA | +#else + GRC_MODE_WORD_SWAP_NON_FRAME_DATA | + GRC_MODE_BYTE_SWAP_DATA | GRC_MODE_WORD_SWAP_DATA | +#endif + GRC_MODE_INT_ON_MAC_ATTN | GRC_MODE_HOST_STACK_UP; +#endif /* !BIG_ENDIAN_PCI */ + + /* Configure send BD mode. */ + if (pDevice->NicSendBd == FALSE) { + Value32 |= GRC_MODE_HOST_SEND_BDS; + } else { + Value32 |= GRC_MODE_4X_NIC_BASED_SEND_RINGS; + } + + /* Configure pseudo checksum mode. */ + if (pDevice->NoTxPseudoHdrChksum) { + Value32 |= GRC_MODE_TX_NO_PSEUDO_HEADER_CHKSUM; + } + + if (pDevice->NoRxPseudoHdrChksum) { + Value32 |= GRC_MODE_RX_NO_PSEUDO_HEADER_CHKSUM; + } + + REG_WR (pDevice, Grc.Mode, Value32); + + /* Setup the timer prescalar register. */ + REG_WR (pDevice, Grc.MiscCfg, 65 << 1); /* Clock is alwasy 66MHz. */ + + /* Set up the MBUF pool base address and size. */ + REG_WR (pDevice, BufMgr.MbufPoolAddr, pDevice->MbufBase); + REG_WR (pDevice, BufMgr.MbufPoolSize, pDevice->MbufSize); + + /* Set up the DMA descriptor pool base address and size. */ + REG_WR (pDevice, BufMgr.DmaDescPoolAddr, T3_NIC_DMA_DESC_POOL_ADDR); + REG_WR (pDevice, BufMgr.DmaDescPoolSize, T3_NIC_DMA_DESC_POOL_SIZE); + + /* Configure MBUF and Threshold watermarks */ + /* Configure the DMA read MBUF low water mark. */ + if (pDevice->DmaMbufLowMark) { + REG_WR (pDevice, BufMgr.MbufReadDmaLowWaterMark, + pDevice->DmaMbufLowMark); + } else { + if (pDevice->TxMtu < MAX_ETHERNET_PACKET_BUFFER_SIZE) { + REG_WR (pDevice, BufMgr.MbufReadDmaLowWaterMark, + T3_DEF_DMA_MBUF_LOW_WMARK); + } else { + REG_WR (pDevice, BufMgr.MbufReadDmaLowWaterMark, + T3_DEF_DMA_MBUF_LOW_WMARK_JUMBO); + } + } + + /* Configure the MAC Rx MBUF low water mark. */ + if (pDevice->RxMacMbufLowMark) { + REG_WR (pDevice, BufMgr.MbufMacRxLowWaterMark, + pDevice->RxMacMbufLowMark); + } else { + if (pDevice->TxMtu < MAX_ETHERNET_PACKET_BUFFER_SIZE) { + REG_WR (pDevice, BufMgr.MbufMacRxLowWaterMark, + T3_DEF_RX_MAC_MBUF_LOW_WMARK); + } else { + REG_WR (pDevice, BufMgr.MbufMacRxLowWaterMark, + T3_DEF_RX_MAC_MBUF_LOW_WMARK_JUMBO); + } + } + + /* Configure the MBUF high water mark. */ + if (pDevice->MbufHighMark) { + REG_WR (pDevice, BufMgr.MbufHighWaterMark, + pDevice->MbufHighMark); + } else { + if (pDevice->TxMtu < MAX_ETHERNET_PACKET_BUFFER_SIZE) { + REG_WR (pDevice, BufMgr.MbufHighWaterMark, + T3_DEF_MBUF_HIGH_WMARK); + } else { + REG_WR (pDevice, BufMgr.MbufHighWaterMark, + T3_DEF_MBUF_HIGH_WMARK_JUMBO); + } + } + + REG_WR (pDevice, BufMgr.DmaLowWaterMark, T3_DEF_DMA_DESC_LOW_WMARK); + REG_WR (pDevice, BufMgr.DmaHighWaterMark, T3_DEF_DMA_DESC_HIGH_WMARK); + + /* Enable buffer manager. */ + REG_WR (pDevice, BufMgr.Mode, + BUFMGR_MODE_ENABLE | BUFMGR_MODE_ATTN_ENABLE); + + for (j = 0; j < 2000; j++) { + if (REG_RD (pDevice, BufMgr.Mode) & BUFMGR_MODE_ENABLE) + break; + MM_Wait (10); + } + + if (j >= 2000) { + return LM_STATUS_FAILURE; + } + + /* Enable the FTQs. */ + REG_WR (pDevice, Ftq.Reset, 0xffffffff); + REG_WR (pDevice, Ftq.Reset, 0); + + /* Wait until FTQ is ready */ + for (j = 0; j < 2000; j++) { + if (REG_RD (pDevice, Ftq.Reset) == 0) + break; + MM_Wait (10); + } + + if (j >= 2000) { + return LM_STATUS_FAILURE; + } + + /* Initialize the Standard Receive RCB. */ + REG_WR (pDevice, RcvDataBdIn.StdRcvRcb.HostRingAddr.High, + pDevice->RxStdBdPhy.High); + REG_WR (pDevice, RcvDataBdIn.StdRcvRcb.HostRingAddr.Low, + pDevice->RxStdBdPhy.Low); + REG_WR (pDevice, RcvDataBdIn.StdRcvRcb.u.MaxLen_Flags, + MAX_STD_RCV_BUFFER_SIZE << 16); + + /* Initialize the Jumbo Receive RCB. */ + REG_WR (pDevice, RcvDataBdIn.JumboRcvRcb.u.MaxLen_Flags, + T3_RCB_FLAG_RING_DISABLED); +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + REG_WR (pDevice, RcvDataBdIn.JumboRcvRcb.HostRingAddr.High, + pDevice->RxJumboBdPhy.High); + REG_WR (pDevice, RcvDataBdIn.JumboRcvRcb.HostRingAddr.Low, + pDevice->RxJumboBdPhy.Low); + + REG_WR (pDevice, RcvDataBdIn.JumboRcvRcb.u.MaxLen_Flags, 0); + +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + /* Initialize the Mini Receive RCB. */ + REG_WR (pDevice, RcvDataBdIn.MiniRcvRcb.u.MaxLen_Flags, + T3_RCB_FLAG_RING_DISABLED); + + { + REG_WR (pDevice, RcvDataBdIn.StdRcvRcb.NicRingAddr, + (LM_UINT32) T3_NIC_STD_RCV_BUFFER_DESC_ADDR); + REG_WR (pDevice, RcvDataBdIn.JumboRcvRcb.NicRingAddr, + (LM_UINT32) T3_NIC_JUMBO_RCV_BUFFER_DESC_ADDR); + } + + /* Receive BD Ring replenish threshold. */ + REG_WR (pDevice, RcvBdIn.StdRcvThreshold, pDevice->RxStdDescCnt / 8); +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + REG_WR (pDevice, RcvBdIn.JumboRcvThreshold, + pDevice->RxJumboDescCnt / 8); +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + /* Disable all the unused rings. */ + for (j = 0; j < T3_MAX_SEND_RCB_COUNT; j++) { + MEM_WR (pDevice, SendRcb[j].u.MaxLen_Flags, + T3_RCB_FLAG_RING_DISABLED); + } /* for */ + + /* Initialize the indices. */ + pDevice->SendProdIdx = 0; + pDevice->SendConIdx = 0; + + MB_REG_WR (pDevice, Mailbox.SendHostProdIdx[0].Low, 0); + MB_REG_WR (pDevice, Mailbox.SendNicProdIdx[0].Low, 0); + + /* Set up host or NIC based send RCB. */ + if (pDevice->NicSendBd == FALSE) { + MEM_WR (pDevice, SendRcb[0].HostRingAddr.High, + pDevice->SendBdPhy.High); + MEM_WR (pDevice, SendRcb[0].HostRingAddr.Low, + pDevice->SendBdPhy.Low); + + /* Set up the NIC ring address in the RCB. */ + MEM_WR (pDevice, SendRcb[0].NicRingAddr, + T3_NIC_SND_BUFFER_DESC_ADDR); + + /* Setup the RCB. */ + MEM_WR (pDevice, SendRcb[0].u.MaxLen_Flags, + T3_SEND_RCB_ENTRY_COUNT << 16); + + for (k = 0; k < T3_SEND_RCB_ENTRY_COUNT; k++) { + pDevice->pSendBdVirt[k].HostAddr.High = 0; + pDevice->pSendBdVirt[k].HostAddr.Low = 0; + } + } else { + MEM_WR (pDevice, SendRcb[0].HostRingAddr.High, 0); + MEM_WR (pDevice, SendRcb[0].HostRingAddr.Low, 0); + MEM_WR (pDevice, SendRcb[0].NicRingAddr, + pDevice->SendBdPhy.Low); + + for (k = 0; k < T3_SEND_RCB_ENTRY_COUNT; k++) { + __raw_writel (0, + &(pDevice->pSendBdVirt[k].HostAddr.High)); + __raw_writel (0, + &(pDevice->pSendBdVirt[k].HostAddr.Low)); + __raw_writel (0, + &(pDevice->pSendBdVirt[k].u1.Len_Flags)); + pDevice->ShadowSendBd[k].HostAddr.High = 0; + pDevice->ShadowSendBd[k].u1.Len_Flags = 0; + } + } + atomic_set (&pDevice->SendBdLeft, T3_SEND_RCB_ENTRY_COUNT - 1); + + /* Configure the receive return rings. */ + for (j = 0; j < T3_MAX_RCV_RETURN_RCB_COUNT; j++) { + MEM_WR (pDevice, RcvRetRcb[j].u.MaxLen_Flags, + T3_RCB_FLAG_RING_DISABLED); + } + + pDevice->RcvRetConIdx = 0; + + MEM_WR (pDevice, RcvRetRcb[0].HostRingAddr.High, + pDevice->RcvRetBdPhy.High); + MEM_WR (pDevice, RcvRetRcb[0].HostRingAddr.Low, + pDevice->RcvRetBdPhy.Low); + + /* Set up the NIC ring address in the RCB. */ + /* Not very clear from the spec. I am guessing that for Receive */ + /* Return Ring, NicRingAddr is not used. */ + MEM_WR (pDevice, RcvRetRcb[0].NicRingAddr, 0); + + /* Setup the RCB. */ + MEM_WR (pDevice, RcvRetRcb[0].u.MaxLen_Flags, + T3_RCV_RETURN_RCB_ENTRY_COUNT << 16); + + /* Reinitialize RX ring producer index */ + MB_REG_WR (pDevice, Mailbox.RcvStdProdIdx.Low, 0); + MB_REG_WR (pDevice, Mailbox.RcvJumboProdIdx.Low, 0); + MB_REG_WR (pDevice, Mailbox.RcvMiniProdIdx.Low, 0); + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + pDevice->RxJumboProdIdx = 0; + pDevice->RxJumboQueuedCnt = 0; +#endif + + /* Reinitialize our copy of the indices. */ + pDevice->RxStdProdIdx = 0; + pDevice->RxStdQueuedCnt = 0; + +#if T3_JUMBO_RCV_ENTRY_COUNT + pDevice->RxJumboProdIdx = 0; +#endif /* T3_JUMBO_RCV_ENTRY_COUNT */ + + /* Configure the MAC address. */ + LM_SetMacAddress (pDevice); + + /* Initialize the transmit random backoff seed. */ + Value32 = (pDevice->NodeAddress[0] + pDevice->NodeAddress[1] + + pDevice->NodeAddress[2] + pDevice->NodeAddress[3] + + pDevice->NodeAddress[4] + pDevice->NodeAddress[5]) & + MAC_TX_BACKOFF_SEED_MASK; + REG_WR (pDevice, MacCtrl.TxBackoffSeed, Value32); + + /* Receive MTU. Frames larger than the MTU is marked as oversized. */ + REG_WR (pDevice, MacCtrl.MtuSize, pDevice->RxMtu + 8); /* CRC + VLAN. */ + + /* Configure Time slot/IPG per 802.3 */ + REG_WR (pDevice, MacCtrl.TxLengths, 0x2620); + + /* + * Configure Receive Rules so that packets don't match + * Programmble rule will be queued to Return Ring 1 + */ + REG_WR (pDevice, MacCtrl.RcvRuleCfg, RX_RULE_DEFAULT_CLASS); + + /* + * Configure to have 16 Classes of Services (COS) and one + * queue per class. Bad frames are queued to RRR#1. + * And frames don't match rules are also queued to COS#1. + */ + REG_WR (pDevice, RcvListPlmt.Config, 0x181); + + /* Enable Receive Placement Statistics */ + REG_WR (pDevice, RcvListPlmt.StatsEnableMask, 0xffffff); + REG_WR (pDevice, RcvListPlmt.StatsCtrl, RCV_LIST_STATS_ENABLE); + + /* Enable Send Data Initator Statistics */ + REG_WR (pDevice, SndDataIn.StatsEnableMask, 0xffffff); + REG_WR (pDevice, SndDataIn.StatsCtrl, + T3_SND_DATA_IN_STATS_CTRL_ENABLE | + T3_SND_DATA_IN_STATS_CTRL_FASTER_UPDATE); + + /* Disable the host coalescing state machine before configuring it's */ + /* parameters. */ + REG_WR (pDevice, HostCoalesce.Mode, 0); + for (j = 0; j < 2000; j++) { + Value32 = REG_RD (pDevice, HostCoalesce.Mode); + if (!(Value32 & HOST_COALESCE_ENABLE)) { + break; + } + MM_Wait (10); + } + + /* Host coalescing configurations. */ + REG_WR (pDevice, HostCoalesce.RxCoalescingTicks, + pDevice->RxCoalescingTicks); + REG_WR (pDevice, HostCoalesce.TxCoalescingTicks, + pDevice->TxCoalescingTicks); + REG_WR (pDevice, HostCoalesce.RxMaxCoalescedFrames, + pDevice->RxMaxCoalescedFrames); + REG_WR (pDevice, HostCoalesce.TxMaxCoalescedFrames, + pDevice->TxMaxCoalescedFrames); + REG_WR (pDevice, HostCoalesce.RxCoalescedTickDuringInt, + pDevice->RxCoalescingTicksDuringInt); + REG_WR (pDevice, HostCoalesce.TxCoalescedTickDuringInt, + pDevice->TxCoalescingTicksDuringInt); + REG_WR (pDevice, HostCoalesce.RxMaxCoalescedFramesDuringInt, + pDevice->RxMaxCoalescedFramesDuringInt); + REG_WR (pDevice, HostCoalesce.TxMaxCoalescedFramesDuringInt, + pDevice->TxMaxCoalescedFramesDuringInt); + + /* Initialize the address of the status block. The NIC will DMA */ + /* the status block to this memory which resides on the host. */ + REG_WR (pDevice, HostCoalesce.StatusBlkHostAddr.High, + pDevice->StatusBlkPhy.High); + REG_WR (pDevice, HostCoalesce.StatusBlkHostAddr.Low, + pDevice->StatusBlkPhy.Low); + + /* Initialize the address of the statistics block. The NIC will DMA */ + /* the statistics to this block of memory. */ + REG_WR (pDevice, HostCoalesce.StatsBlkHostAddr.High, + pDevice->StatsBlkPhy.High); + REG_WR (pDevice, HostCoalesce.StatsBlkHostAddr.Low, + pDevice->StatsBlkPhy.Low); + + REG_WR (pDevice, HostCoalesce.StatsCoalescingTicks, + pDevice->StatsCoalescingTicks); + + REG_WR (pDevice, HostCoalesce.StatsBlkNicAddr, 0x300); + REG_WR (pDevice, HostCoalesce.StatusBlkNicAddr, 0xb00); + + /* Enable Host Coalesing state machine */ + REG_WR (pDevice, HostCoalesce.Mode, HOST_COALESCE_ENABLE | + pDevice->CoalesceMode); + + /* Enable the Receive BD Completion state machine. */ + REG_WR (pDevice, RcvBdComp.Mode, RCV_BD_COMP_MODE_ENABLE | + RCV_BD_COMP_MODE_ATTN_ENABLE); + + /* Enable the Receive List Placement state machine. */ + REG_WR (pDevice, RcvListPlmt.Mode, RCV_LIST_PLMT_MODE_ENABLE); + + /* Enable the Receive List Selector state machine. */ + REG_WR (pDevice, RcvListSel.Mode, RCV_LIST_SEL_MODE_ENABLE | + RCV_LIST_SEL_MODE_ATTN_ENABLE); + + /* Enable transmit DMA, clear statistics. */ + pDevice->MacMode = MAC_MODE_ENABLE_TX_STATISTICS | + MAC_MODE_ENABLE_RX_STATISTICS | MAC_MODE_ENABLE_TDE | + MAC_MODE_ENABLE_RDE | MAC_MODE_ENABLE_FHDE; + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode | + MAC_MODE_CLEAR_RX_STATISTICS | MAC_MODE_CLEAR_TX_STATISTICS); + + /* GRC miscellaneous local control register. */ + pDevice->GrcLocalCtrl = GRC_MISC_LOCAL_CTRL_INT_ON_ATTN | + GRC_MISC_LOCAL_CTRL_AUTO_SEEPROM; + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + pDevice->GrcLocalCtrl |= GRC_MISC_LOCAL_CTRL_GPIO_OE1 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT1; + } + + REG_WR (pDevice, Grc.LocalCtrl, pDevice->GrcLocalCtrl); + MM_Wait (40); + + /* Reset RX counters. */ + for (j = 0; j < sizeof (LM_RX_COUNTERS); j++) { + ((PLM_UINT8) & pDevice->RxCounters)[j] = 0; + } + + /* Reset TX counters. */ + for (j = 0; j < sizeof (LM_TX_COUNTERS); j++) { + ((PLM_UINT8) & pDevice->TxCounters)[j] = 0; + } + + MB_REG_WR (pDevice, Mailbox.Interrupt[0].Low, 0); + + /* Enable the DMA Completion state machine. */ + REG_WR (pDevice, DmaComp.Mode, DMA_COMP_MODE_ENABLE); + + /* Enable the DMA Write state machine. */ + Value32 = DMA_WRITE_MODE_ENABLE | + DMA_WRITE_MODE_TARGET_ABORT_ATTN_ENABLE | + DMA_WRITE_MODE_MASTER_ABORT_ATTN_ENABLE | + DMA_WRITE_MODE_PARITY_ERROR_ATTN_ENABLE | + DMA_WRITE_MODE_ADDR_OVERFLOW_ATTN_ENABLE | + DMA_WRITE_MODE_FIFO_OVERRUN_ATTN_ENABLE | + DMA_WRITE_MODE_FIFO_UNDERRUN_ATTN_ENABLE | + DMA_WRITE_MODE_FIFO_OVERREAD_ATTN_ENABLE | + DMA_WRITE_MODE_LONG_READ_ATTN_ENABLE; + REG_WR (pDevice, DmaWrite.Mode, Value32); + + if (!(pDevice->PciState & T3_PCI_STATE_CONVENTIONAL_PCI_MODE)) { + if (pDevice->ChipRevId == T3_CHIP_ID_5704_A0) { + Value16 = REG_RD (pDevice, PciCfg.PciXCommand); + Value16 &= + ~(PCIX_CMD_MAX_SPLIT_MASK | + PCIX_CMD_MAX_BURST_MASK); + Value16 |= + ((PCIX_CMD_MAX_BURST_CPIOB << + PCIX_CMD_MAX_BURST_SHL) & + PCIX_CMD_MAX_BURST_MASK); + if (pDevice->SplitModeEnable == SPLIT_MODE_ENABLE) { + Value16 |= + (pDevice-> + SplitModeMaxReq << PCIX_CMD_MAX_SPLIT_SHL) + & PCIX_CMD_MAX_SPLIT_MASK; + } + REG_WR (pDevice, PciCfg.PciXCommand, Value16); + } + } + + /* Enable the Read DMA state machine. */ + Value32 = DMA_READ_MODE_ENABLE | + DMA_READ_MODE_TARGET_ABORT_ATTN_ENABLE | + DMA_READ_MODE_MASTER_ABORT_ATTN_ENABLE | + DMA_READ_MODE_PARITY_ERROR_ATTN_ENABLE | + DMA_READ_MODE_ADDR_OVERFLOW_ATTN_ENABLE | + DMA_READ_MODE_FIFO_OVERRUN_ATTN_ENABLE | + DMA_READ_MODE_FIFO_UNDERRUN_ATTN_ENABLE | + DMA_READ_MODE_FIFO_OVERREAD_ATTN_ENABLE | + DMA_READ_MODE_LONG_READ_ATTN_ENABLE; + + if (pDevice->SplitModeEnable == SPLIT_MODE_ENABLE) { + Value32 |= DMA_READ_MODE_SPLIT_ENABLE; + } + REG_WR (pDevice, DmaRead.Mode, Value32); + + /* Enable the Receive Data Completion state machine. */ + REG_WR (pDevice, RcvDataComp.Mode, RCV_DATA_COMP_MODE_ENABLE | + RCV_DATA_COMP_MODE_ATTN_ENABLE); + + /* Enable the Mbuf Cluster Free state machine. */ + REG_WR (pDevice, MbufClusterFree.Mode, MBUF_CLUSTER_FREE_MODE_ENABLE); + + /* Enable the Send Data Completion state machine. */ + REG_WR (pDevice, SndDataComp.Mode, SND_DATA_COMP_MODE_ENABLE); + + /* Enable the Send BD Completion state machine. */ + REG_WR (pDevice, SndBdComp.Mode, SND_BD_COMP_MODE_ENABLE | + SND_BD_COMP_MODE_ATTN_ENABLE); + + /* Enable the Receive BD Initiator state machine. */ + REG_WR (pDevice, RcvBdIn.Mode, RCV_BD_IN_MODE_ENABLE | + RCV_BD_IN_MODE_BD_IN_DIABLED_RCB_ATTN_ENABLE); + + /* Enable the Receive Data and Receive BD Initiator state machine. */ + REG_WR (pDevice, RcvDataBdIn.Mode, RCV_DATA_BD_IN_MODE_ENABLE | + RCV_DATA_BD_IN_MODE_INVALID_RING_SIZE); + + /* Enable the Send Data Initiator state machine. */ + REG_WR (pDevice, SndDataIn.Mode, T3_SND_DATA_IN_MODE_ENABLE); + + /* Enable the Send BD Initiator state machine. */ + REG_WR (pDevice, SndBdIn.Mode, SND_BD_IN_MODE_ENABLE | + SND_BD_IN_MODE_ATTN_ENABLE); + + /* Enable the Send BD Selector state machine. */ + REG_WR (pDevice, SndBdSel.Mode, SND_BD_SEL_MODE_ENABLE | + SND_BD_SEL_MODE_ATTN_ENABLE); + +#if INCLUDE_5701_AX_FIX + /* Load the firmware for the 5701_A0 workaround. */ + if (pDevice->ChipRevId == T3_CHIP_ID_5701_A0) { + LM_LoadRlsFirmware (pDevice); + } +#endif + + /* Enable the transmitter. */ + pDevice->TxMode = TX_MODE_ENABLE; + REG_WR (pDevice, MacCtrl.TxMode, pDevice->TxMode); + + /* Enable the receiver. */ + pDevice->RxMode = RX_MODE_ENABLE; + REG_WR (pDevice, MacCtrl.RxMode, pDevice->RxMode); + + if (pDevice->RestoreOnWakeUp) { + pDevice->RestoreOnWakeUp = FALSE; + pDevice->DisableAutoNeg = pDevice->WakeUpDisableAutoNeg; + pDevice->RequestedMediaType = pDevice->WakeUpRequestedMediaType; + } + + /* Disable auto polling. */ + pDevice->MiMode = 0xc0000; + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode); + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Value32 = LED_CTRL_PHY_MODE_1; + } else { + if (pDevice->LedMode == LED_MODE_OUTPUT) { + Value32 = LED_CTRL_PHY_MODE_2; + } else { + Value32 = LED_CTRL_PHY_MODE_1; + } + } + REG_WR (pDevice, MacCtrl.LedCtrl, Value32); + + /* Activate Link to enable MAC state machine */ + REG_WR (pDevice, MacCtrl.MiStatus, MI_STATUS_ENABLE_LINK_STATUS_ATTN); + + if (pDevice->EnableTbi) { + REG_WR (pDevice, MacCtrl.RxMode, RX_MODE_RESET); + MM_Wait (10); + REG_WR (pDevice, MacCtrl.RxMode, pDevice->RxMode); + if (pDevice->ChipRevId == T3_CHIP_ID_5703_A1) { + REG_WR (pDevice, MacCtrl.SerdesCfg, 0x616000); + } + } + /* Setup the phy chip. */ + LM_SetupPhy (pDevice); + + if (!pDevice->EnableTbi) { + /* Clear CRC stats */ + LM_ReadPhy (pDevice, 0x1e, &Value32); + LM_WritePhy (pDevice, 0x1e, Value32 | 0x8000); + LM_ReadPhy (pDevice, 0x14, &Value32); + } + + /* Set up the receive mask. */ + LM_SetReceiveMask (pDevice, pDevice->ReceiveMask); + + /* Queue Rx packet buffers. */ + if (pDevice->QueueRxPackets) { + LM_QueueRxPackets (pDevice); + } + + /* Enable interrupt to the host. */ + if (pDevice->InitDone) { + LM_EnableInterrupt (pDevice); + } + + return LM_STATUS_SUCCESS; +} /* LM_ResetAdapter */ + +/******************************************************************************/ +/* Description: */ +/* This routine disables the adapter from generating interrupts. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_DisableInterrupt (PLM_DEVICE_BLOCK pDevice) +{ + REG_WR (pDevice, PciCfg.MiscHostCtrl, pDevice->MiscHostCtrl | + MISC_HOST_CTRL_MASK_PCI_INT); + MB_REG_WR (pDevice, Mailbox.Interrupt[0].Low, 1); + + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* This routine enables the adapter to generate interrupts. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_EnableInterrupt (PLM_DEVICE_BLOCK pDevice) +{ + REG_WR (pDevice, PciCfg.MiscHostCtrl, pDevice->MiscHostCtrl & + ~MISC_HOST_CTRL_MASK_PCI_INT); + MB_REG_WR (pDevice, Mailbox.Interrupt[0].Low, 0); + + if (pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) { + REG_WR (pDevice, Grc.LocalCtrl, pDevice->GrcLocalCtrl | + GRC_MISC_LOCAL_CTRL_SET_INT); + } + + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* This routine puts a packet on the wire if there is a transmit DMA */ +/* descriptor available; otherwise the packet is queued for later */ +/* transmission. If the second argue is NULL, this routine will put */ +/* the queued packet on the wire if possible. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +#if 0 +LM_STATUS LM_SendPacket (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + LM_UINT32 FragCount; + PT3_SND_BD pSendBd; + PT3_SND_BD pShadowSendBd; + LM_UINT32 Value32, Len; + LM_UINT32 Idx; + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + return LM_5700SendPacket (pDevice, pPacket); + } + + /* Update the SendBdLeft count. */ + atomic_sub (pPacket->u.Tx.FragCount, &pDevice->SendBdLeft); + + /* Initalize the send buffer descriptors. */ + Idx = pDevice->SendProdIdx; + + pSendBd = &pDevice->pSendBdVirt[Idx]; + + /* Next producer index. */ + if (pDevice->NicSendBd == TRUE) { + T3_64BIT_HOST_ADDR paddr; + + pShadowSendBd = &pDevice->ShadowSendBd[Idx]; + for (FragCount = 0;;) { + MM_MapTxDma (pDevice, pPacket, &paddr, &Len, FragCount); + /* Initialize the pointer to the send buffer fragment. */ + if (paddr.High != pShadowSendBd->HostAddr.High) { + __raw_writel (paddr.High, + &(pSendBd->HostAddr.High)); + pShadowSendBd->HostAddr.High = paddr.High; + } + __raw_writel (paddr.Low, &(pSendBd->HostAddr.Low)); + + /* Setup the control flags and send buffer size. */ + Value32 = (Len << 16) | pPacket->Flags; + + Idx = (Idx + 1) & T3_SEND_RCB_ENTRY_COUNT_MASK; + + FragCount++; + if (FragCount >= pPacket->u.Tx.FragCount) { + Value32 |= SND_BD_FLAG_END; + if (Value32 != pShadowSendBd->u1.Len_Flags) { + __raw_writel (Value32, + &(pSendBd->u1.Len_Flags)); + pShadowSendBd->u1.Len_Flags = Value32; + } + if (pPacket->Flags & SND_BD_FLAG_VLAN_TAG) { + __raw_writel (pPacket->VlanTag, + &(pSendBd->u2.VlanTag)); + } + break; + } else { + if (Value32 != pShadowSendBd->u1.Len_Flags) { + __raw_writel (Value32, + &(pSendBd->u1.Len_Flags)); + pShadowSendBd->u1.Len_Flags = Value32; + } + if (pPacket->Flags & SND_BD_FLAG_VLAN_TAG) { + __raw_writel (pPacket->VlanTag, + &(pSendBd->u2.VlanTag)); + } + } + + pSendBd++; + pShadowSendBd++; + if (Idx == 0) { + pSendBd = &pDevice->pSendBdVirt[0]; + pShadowSendBd = &pDevice->ShadowSendBd[0]; + } + } /* for */ + + /* Put the packet descriptor in the ActiveQ. */ + QQ_PushTail (&pDevice->TxPacketActiveQ.Container, pPacket); + + wmb (); + MB_REG_WR (pDevice, Mailbox.SendNicProdIdx[0].Low, Idx); + + } else { + for (FragCount = 0;;) { + /* Initialize the pointer to the send buffer fragment. */ + MM_MapTxDma (pDevice, pPacket, &pSendBd->HostAddr, &Len, + FragCount); + + pSendBd->u2.VlanTag = pPacket->VlanTag; + + /* Setup the control flags and send buffer size. */ + Value32 = (Len << 16) | pPacket->Flags; + + Idx = (Idx + 1) & T3_SEND_RCB_ENTRY_COUNT_MASK; + + FragCount++; + if (FragCount >= pPacket->u.Tx.FragCount) { + pSendBd->u1.Len_Flags = + Value32 | SND_BD_FLAG_END; + break; + } else { + pSendBd->u1.Len_Flags = Value32; + } + pSendBd++; + if (Idx == 0) { + pSendBd = &pDevice->pSendBdVirt[0]; + } + } /* for */ + + /* Put the packet descriptor in the ActiveQ. */ + QQ_PushTail (&pDevice->TxPacketActiveQ.Container, pPacket); + + wmb (); + MB_REG_WR (pDevice, Mailbox.SendHostProdIdx[0].Low, Idx); + + } + + /* Update the producer index. */ + pDevice->SendProdIdx = Idx; + + return LM_STATUS_SUCCESS; +} +#endif + +LM_STATUS LM_SendPacket (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + LM_UINT32 FragCount; + PT3_SND_BD pSendBd, pTmpSendBd, pShadowSendBd; + T3_SND_BD NicSendBdArr[MAX_FRAGMENT_COUNT]; + LM_UINT32 StartIdx, Idx; + + while (1) { + /* Initalize the send buffer descriptors. */ + StartIdx = Idx = pDevice->SendProdIdx; + + if (pDevice->NicSendBd) { + pTmpSendBd = pSendBd = &NicSendBdArr[0]; + } else { + pTmpSendBd = pSendBd = &pDevice->pSendBdVirt[Idx]; + } + + /* Next producer index. */ + for (FragCount = 0;;) { + LM_UINT32 Value32, Len; + + /* Initialize the pointer to the send buffer fragment. */ + MM_MapTxDma (pDevice, pPacket, &pSendBd->HostAddr, &Len, + FragCount); + + pSendBd->u2.VlanTag = pPacket->VlanTag; + + /* Setup the control flags and send buffer size. */ + Value32 = (Len << 16) | pPacket->Flags; + + Idx = (Idx + 1) & T3_SEND_RCB_ENTRY_COUNT_MASK; + + FragCount++; + if (FragCount >= pPacket->u.Tx.FragCount) { + pSendBd->u1.Len_Flags = + Value32 | SND_BD_FLAG_END; + break; + } else { + pSendBd->u1.Len_Flags = Value32; + } + pSendBd++; + if ((Idx == 0) && !pDevice->NicSendBd) { + pSendBd = &pDevice->pSendBdVirt[0]; + } + } /* for */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + if (LM_Test4GBoundary (pDevice, pPacket, pTmpSendBd) == + LM_STATUS_SUCCESS) { + if (MM_CoalesceTxBuffer (pDevice, pPacket) != + LM_STATUS_SUCCESS) { + QQ_PushHead (&pDevice->TxPacketFreeQ. + Container, pPacket); + return LM_STATUS_FAILURE; + } + continue; + } + } + break; + } + /* Put the packet descriptor in the ActiveQ. */ + QQ_PushTail (&pDevice->TxPacketActiveQ.Container, pPacket); + + if (pDevice->NicSendBd) { + pSendBd = &pDevice->pSendBdVirt[StartIdx]; + pShadowSendBd = &pDevice->ShadowSendBd[StartIdx]; + + while (StartIdx != Idx) { + LM_UINT32 Value32; + + if ((Value32 = pTmpSendBd->HostAddr.High) != + pShadowSendBd->HostAddr.High) { + __raw_writel (Value32, + &(pSendBd->HostAddr.High)); + pShadowSendBd->HostAddr.High = Value32; + } + + __raw_writel (pTmpSendBd->HostAddr.Low, + &(pSendBd->HostAddr.Low)); + + if ((Value32 = pTmpSendBd->u1.Len_Flags) != + pShadowSendBd->u1.Len_Flags) { + __raw_writel (Value32, + &(pSendBd->u1.Len_Flags)); + pShadowSendBd->u1.Len_Flags = Value32; + } + + if (pPacket->Flags & SND_BD_FLAG_VLAN_TAG) { + __raw_writel (pTmpSendBd->u2.VlanTag, + &(pSendBd->u2.VlanTag)); + } + + StartIdx = + (StartIdx + 1) & T3_SEND_RCB_ENTRY_COUNT_MASK; + if (StartIdx == 0) + pSendBd = &pDevice->pSendBdVirt[0]; + else + pSendBd++; + pTmpSendBd++; + } + wmb (); + MB_REG_WR (pDevice, Mailbox.SendNicProdIdx[0].Low, Idx); + + if (T3_CHIP_REV (pDevice->ChipRevId) == T3_CHIP_REV_5700_BX) { + MB_REG_WR (pDevice, Mailbox.SendNicProdIdx[0].Low, Idx); + } + } else { + wmb (); + MB_REG_WR (pDevice, Mailbox.SendHostProdIdx[0].Low, Idx); + + if (T3_CHIP_REV (pDevice->ChipRevId) == T3_CHIP_REV_5700_BX) { + MB_REG_WR (pDevice, Mailbox.SendHostProdIdx[0].Low, + Idx); + } + } + + /* Update the SendBdLeft count. */ + atomic_sub (pPacket->u.Tx.FragCount, &pDevice->SendBdLeft); + + /* Update the producer index. */ + pDevice->SendProdIdx = Idx; + + return LM_STATUS_SUCCESS; +} + +STATIC LM_STATUS +LM_Test4GBoundary (PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket, + PT3_SND_BD pSendBd) +{ + int FragCount; + LM_UINT32 Idx, Base, Len; + + Idx = pDevice->SendProdIdx; + for (FragCount = 0;;) { + Len = pSendBd->u1.Len_Flags >> 16; + if (((Base = pSendBd->HostAddr.Low) > 0xffffdcc0) && + (pSendBd->HostAddr.High == 0) && + ((Base + 8 + Len) < Base)) { + return LM_STATUS_SUCCESS; + } + FragCount++; + if (FragCount >= pPacket->u.Tx.FragCount) { + break; + } + pSendBd++; + if (!pDevice->NicSendBd) { + Idx = (Idx + 1) & T3_SEND_RCB_ENTRY_COUNT_MASK; + if (Idx == 0) { + pSendBd = &pDevice->pSendBdVirt[0]; + } + } + } + return LM_STATUS_FAILURE; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static unsigned long +ComputeCrc32 (unsigned char *pBuffer, unsigned long BufferSize) +{ + unsigned long Reg; + unsigned long Tmp; + unsigned long j, k; + + Reg = 0xffffffff; + + for (j = 0; j < BufferSize; j++) { + Reg ^= pBuffer[j]; + + for (k = 0; k < 8; k++) { + Tmp = Reg & 0x01; + + Reg >>= 1; + + if (Tmp) { + Reg ^= 0xedb88320; + } + } + } + + return ~Reg; +} /* ComputeCrc32 */ + +/******************************************************************************/ +/* Description: */ +/* This routine sets the receive control register according to ReceiveMask */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_SetReceiveMask (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Mask) +{ + LM_UINT32 ReceiveMask; + LM_UINT32 RxMode; + LM_UINT32 j, k; + + ReceiveMask = Mask; + + RxMode = pDevice->RxMode; + + if (Mask & LM_ACCEPT_UNICAST) { + Mask &= ~LM_ACCEPT_UNICAST; + } + + if (Mask & LM_ACCEPT_MULTICAST) { + Mask &= ~LM_ACCEPT_MULTICAST; + } + + if (Mask & LM_ACCEPT_ALL_MULTICAST) { + Mask &= ~LM_ACCEPT_ALL_MULTICAST; + } + + if (Mask & LM_ACCEPT_BROADCAST) { + Mask &= ~LM_ACCEPT_BROADCAST; + } + + RxMode &= ~RX_MODE_PROMISCUOUS_MODE; + if (Mask & LM_PROMISCUOUS_MODE) { + RxMode |= RX_MODE_PROMISCUOUS_MODE; + Mask &= ~LM_PROMISCUOUS_MODE; + } + + RxMode &= ~(RX_MODE_ACCEPT_RUNTS | RX_MODE_ACCEPT_OVERSIZED); + if (Mask & LM_ACCEPT_ERROR_PACKET) { + RxMode |= RX_MODE_ACCEPT_RUNTS | RX_MODE_ACCEPT_OVERSIZED; + Mask &= ~LM_ACCEPT_ERROR_PACKET; + } + + /* Make sure all the bits are valid before committing changes. */ + if (Mask) { + return LM_STATUS_FAILURE; + } + + /* Commit the new filter. */ + pDevice->RxMode = RxMode; + REG_WR (pDevice, MacCtrl.RxMode, RxMode); + + pDevice->ReceiveMask = ReceiveMask; + + /* Set up the MC hash table. */ + if (ReceiveMask & LM_ACCEPT_ALL_MULTICAST) { + for (k = 0; k < 4; k++) { + REG_WR (pDevice, MacCtrl.HashReg[k], 0xffffffff); + } + } else if (ReceiveMask & LM_ACCEPT_MULTICAST) { + LM_UINT32 HashReg[4]; + + HashReg[0] = 0; + HashReg[1] = 0; + HashReg[2] = 0; + HashReg[3] = 0; + for (j = 0; j < pDevice->McEntryCount; j++) { + LM_UINT32 RegIndex; + LM_UINT32 Bitpos; + LM_UINT32 Crc32; + + Crc32 = + ComputeCrc32 (pDevice->McTable[j], + ETHERNET_ADDRESS_SIZE); + + /* The most significant 7 bits of the CRC32 (no inversion), */ + /* are used to index into one of the possible 128 bit positions. */ + Bitpos = ~Crc32 & 0x7f; + + /* Hash register index. */ + RegIndex = (Bitpos & 0x60) >> 5; + + /* Bit to turn on within a hash register. */ + Bitpos &= 0x1f; + + /* Enable the multicast bit. */ + HashReg[RegIndex] |= (1 << Bitpos); + } + + /* REV_AX has problem with multicast filtering where it uses both */ + /* DA and SA to perform hashing. */ + for (k = 0; k < 4; k++) { + REG_WR (pDevice, MacCtrl.HashReg[k], HashReg[k]); + } + } else { + /* Reject all multicast frames. */ + for (j = 0; j < 4; j++) { + REG_WR (pDevice, MacCtrl.HashReg[j], 0); + } + } + + /* By default, Tigon3 will accept broadcast frames. We need to setup */ + if (ReceiveMask & LM_ACCEPT_BROADCAST) { + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE1_REJECT_BROADCAST_IDX].Rule, + REJECT_BROADCAST_RULE1_RULE & RCV_DISABLE_RULE_MASK); + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE1_REJECT_BROADCAST_IDX].Value, + REJECT_BROADCAST_RULE1_VALUE & RCV_DISABLE_RULE_MASK); + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE2_REJECT_BROADCAST_IDX].Rule, + REJECT_BROADCAST_RULE1_RULE & RCV_DISABLE_RULE_MASK); + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE2_REJECT_BROADCAST_IDX].Value, + REJECT_BROADCAST_RULE1_VALUE & RCV_DISABLE_RULE_MASK); + } else { + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE1_REJECT_BROADCAST_IDX].Rule, + REJECT_BROADCAST_RULE1_RULE); + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE1_REJECT_BROADCAST_IDX].Value, + REJECT_BROADCAST_RULE1_VALUE); + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE2_REJECT_BROADCAST_IDX].Rule, + REJECT_BROADCAST_RULE2_RULE); + REG_WR (pDevice, + MacCtrl.RcvRules[RCV_RULE2_REJECT_BROADCAST_IDX].Value, + REJECT_BROADCAST_RULE2_VALUE); + } + + /* disable the rest of the rules. */ + for (j = RCV_LAST_RULE_IDX; j < 16; j++) { + REG_WR (pDevice, MacCtrl.RcvRules[j].Rule, 0); + REG_WR (pDevice, MacCtrl.RcvRules[j].Value, 0); + } + + return LM_STATUS_SUCCESS; +} /* LM_SetReceiveMask */ + +/******************************************************************************/ +/* Description: */ +/* Disable the interrupt and put the transmitter and receiver engines in */ +/* an idle state. Also aborts all pending send requests and receive */ +/* buffers. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_Abort (PLM_DEVICE_BLOCK pDevice) +{ + PLM_PACKET pPacket; + LM_UINT Idx; + + LM_DisableInterrupt (pDevice); + + /* Disable all the state machines. */ + LM_CntrlBlock (pDevice, T3_BLOCK_MAC_RX_ENGINE, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_RX_BD_INITIATOR, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_RX_LIST_PLMT, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_RX_LIST_SELECTOR, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_RX_DATA_INITIATOR, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_RX_DATA_COMP, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_RX_BD_COMP, LM_DISABLE); + + LM_CntrlBlock (pDevice, T3_BLOCK_SEND_BD_SELECTOR, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_SEND_BD_INITIATOR, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_SEND_DATA_INITIATOR, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_DMA_RD, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_SEND_DATA_COMP, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_DMA_COMP, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_SEND_BD_COMP, LM_DISABLE); + + /* Clear TDE bit */ + pDevice->MacMode &= ~MAC_MODE_ENABLE_TDE; + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode); + + LM_CntrlBlock (pDevice, T3_BLOCK_MAC_TX_ENGINE, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_HOST_COALESING, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_DMA_WR, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_MBUF_CLUSTER_FREE, LM_DISABLE); + + /* Reset all FTQs */ + REG_WR (pDevice, Ftq.Reset, 0xffffffff); + REG_WR (pDevice, Ftq.Reset, 0x0); + + LM_CntrlBlock (pDevice, T3_BLOCK_MBUF_MANAGER, LM_DISABLE); + LM_CntrlBlock (pDevice, T3_BLOCK_MEM_ARBITOR, LM_DISABLE); + + MM_ACQUIRE_INT_LOCK (pDevice); + + /* Abort packets that have already queued to go out. */ + pPacket = (PLM_PACKET) QQ_PopHead (&pDevice->TxPacketActiveQ.Container); + while (pPacket) { + + pPacket->PacketStatus = LM_STATUS_TRANSMIT_ABORTED; + pDevice->TxCounters.TxPacketAbortedCnt++; + + atomic_add (pPacket->u.Tx.FragCount, &pDevice->SendBdLeft); + + QQ_PushTail (&pDevice->TxPacketXmittedQ.Container, pPacket); + + pPacket = (PLM_PACKET) + QQ_PopHead (&pDevice->TxPacketActiveQ.Container); + } + + /* Cleanup the receive return rings. */ + LM_ServiceRxInterrupt (pDevice); + + /* Don't want to indicate rx packets in Ndis miniport shutdown context. */ + /* Doing so may cause system crash. */ + if (!pDevice->ShuttingDown) { + /* Indicate packets to the protocol. */ + MM_IndicateTxPackets (pDevice); + + /* Indicate received packets to the protocols. */ + MM_IndicateRxPackets (pDevice); + } else { + /* Move the receive packet descriptors in the ReceivedQ to the */ + /* free queue. */ + for (;;) { + pPacket = + (PLM_PACKET) QQ_PopHead (&pDevice-> + RxPacketReceivedQ. + Container); + if (pPacket == NULL) { + break; + } + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, + pPacket); + } + } + + /* Clean up the Std Receive Producer ring. */ + Idx = pDevice->pStatusBlkVirt->RcvStdConIdx; + + while (Idx != pDevice->RxStdProdIdx) { + pPacket = (PLM_PACKET) (MM_UINT_PTR (pDevice->pPacketDescBase) + + MM_UINT_PTR (pDevice->pRxStdBdVirt[Idx]. + Opaque)); + + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + + Idx = (Idx + 1) & T3_STD_RCV_RCB_ENTRY_COUNT_MASK; + } /* while */ + + /* Reinitialize our copy of the indices. */ + pDevice->RxStdProdIdx = 0; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + /* Clean up the Jumbo Receive Producer ring. */ + Idx = pDevice->pStatusBlkVirt->RcvJumboConIdx; + + while (Idx != pDevice->RxJumboProdIdx) { + pPacket = (PLM_PACKET) (MM_UINT_PTR (pDevice->pPacketDescBase) + + MM_UINT_PTR (pDevice-> + pRxJumboBdVirt[Idx]. + Opaque)); + + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + + Idx = (Idx + 1) & T3_JUMBO_RCV_RCB_ENTRY_COUNT_MASK; + } /* while */ + + /* Reinitialize our copy of the indices. */ + pDevice->RxJumboProdIdx = 0; +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + MM_RELEASE_INT_LOCK (pDevice); + + /* Initialize the statistis Block */ + pDevice->pStatusBlkVirt->Status = 0; + pDevice->pStatusBlkVirt->RcvStdConIdx = 0; + pDevice->pStatusBlkVirt->RcvJumboConIdx = 0; + pDevice->pStatusBlkVirt->RcvMiniConIdx = 0; + + return LM_STATUS_SUCCESS; +} /* LM_Abort */ + +/******************************************************************************/ +/* Description: */ +/* Disable the interrupt and put the transmitter and receiver engines in */ +/* an idle state. Aborts all pending send requests and receive buffers. */ +/* Also free all the receive buffers. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_Halt (PLM_DEVICE_BLOCK pDevice) +{ + PLM_PACKET pPacket; + LM_UINT32 EntryCnt; + + LM_Abort (pDevice); + + /* Get the number of entries in the queue. */ + EntryCnt = QQ_GetEntryCnt (&pDevice->RxPacketFreeQ.Container); + + /* Make sure all the packets have been accounted for. */ + for (EntryCnt = 0; EntryCnt < pDevice->RxPacketDescCnt; EntryCnt++) { + pPacket = + (PLM_PACKET) QQ_PopHead (&pDevice->RxPacketFreeQ.Container); + if (pPacket == 0) + break; + + MM_FreeRxBuffer (pDevice, pPacket); + + QQ_PushTail (&pDevice->RxPacketFreeQ.Container, pPacket); + } + + LM_ResetChip (pDevice); + + /* Restore PCI configuration registers. */ + MM_WriteConfig32 (pDevice, PCI_CACHE_LINE_SIZE_REG, + pDevice->SavedCacheLineReg); + LM_RegWrInd (pDevice, PCI_SUBSYSTEM_VENDOR_ID_REG, + (pDevice->SubsystemId << 16) | pDevice->SubsystemVendorId); + + /* Reprogram the MAC address. */ + LM_SetMacAddress (pDevice); + + return LM_STATUS_SUCCESS; +} /* LM_Halt */ + +STATIC LM_STATUS LM_ResetChip (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + LM_UINT32 j; + + /* Wait for access to the nvram interface before resetting. This is */ + /* a workaround to prevent EEPROM corruption. */ + if (T3_ASIC_REV (pDevice->ChipRevId) != T3_ASIC_REV_5700 && + T3_ASIC_REV (pDevice->ChipRevId) != T3_ASIC_REV_5701) { + /* Request access to the flash interface. */ + REG_WR (pDevice, Nvram.SwArb, SW_ARB_REQ_SET1); + + for (j = 0; j < 100000; j++) { + Value32 = REG_RD (pDevice, Nvram.SwArb); + if (Value32 & SW_ARB_GNT1) { + break; + } + MM_Wait (10); + } + } + + /* Global reset. */ + REG_WR (pDevice, Grc.MiscCfg, GRC_MISC_CFG_CORE_CLOCK_RESET); + MM_Wait (40); + MM_Wait (40); + MM_Wait (40); + + /* make sure we re-enable indirect accesses */ + MM_WriteConfig32 (pDevice, T3_PCI_MISC_HOST_CTRL_REG, + pDevice->MiscHostCtrl); + + /* Set MAX PCI retry to zero. */ + Value32 = + T3_PCI_STATE_PCI_ROM_ENABLE | T3_PCI_STATE_PCI_ROM_RETRY_ENABLE; + if (pDevice->ChipRevId == T3_CHIP_ID_5704_A0) { + if (!(pDevice->PciState & T3_PCI_STATE_CONVENTIONAL_PCI_MODE)) { + Value32 |= T3_PCI_STATE_RETRY_SAME_DMA; + } + } + MM_WriteConfig32 (pDevice, T3_PCI_STATE_REG, Value32); + + /* Restore PCI command register. */ + MM_WriteConfig32 (pDevice, PCI_COMMAND_REG, + pDevice->PciCommandStatusWords); + + /* Disable PCI-X relaxed ordering bit. */ + MM_ReadConfig32 (pDevice, PCIX_CAP_REG, &Value32); + Value32 &= ~PCIX_ENABLE_RELAXED_ORDERING; + MM_WriteConfig32 (pDevice, PCIX_CAP_REG, Value32); + + /* Enable memory arbiter. */ + REG_WR (pDevice, MemArbiter.Mode, T3_MEM_ARBITER_MODE_ENABLE); + +#ifdef BIG_ENDIAN_PCI /* This from jfd */ + Value32 = GRC_MODE_WORD_SWAP_DATA | GRC_MODE_WORD_SWAP_NON_FRAME_DATA; +#else +#ifdef BIG_ENDIAN_HOST + /* Reconfigure the mode register. */ + Value32 = GRC_MODE_BYTE_SWAP_NON_FRAME_DATA | + GRC_MODE_WORD_SWAP_NON_FRAME_DATA | + GRC_MODE_BYTE_SWAP_DATA | GRC_MODE_WORD_SWAP_DATA; +#else + /* Reconfigure the mode register. */ + Value32 = GRC_MODE_BYTE_SWAP_NON_FRAME_DATA | GRC_MODE_BYTE_SWAP_DATA; +#endif +#endif + REG_WR (pDevice, Grc.Mode, Value32); + + /* Prevent PXE from restarting. */ + MEM_WR_OFFSET (pDevice, 0x0b50, T3_MAGIC_NUM); + + if (pDevice->EnableTbi) { + pDevice->MacMode = MAC_MODE_PORT_MODE_TBI; + REG_WR (pDevice, MacCtrl.Mode, MAC_MODE_PORT_MODE_TBI); + } else { + REG_WR (pDevice, MacCtrl.Mode, 0); + } + + /* Wait for the firmware to finish initialization. */ + for (j = 0; j < 100000; j++) { + MM_Wait (10); + + Value32 = MEM_RD_OFFSET (pDevice, 0x0b50); + if (Value32 == ~T3_MAGIC_NUM) { + break; + } + } + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static void LM_ServiceTxInterrupt (PLM_DEVICE_BLOCK pDevice) +{ + PLM_PACKET pPacket; + LM_UINT32 HwConIdx; + LM_UINT32 SwConIdx; + + HwConIdx = pDevice->pStatusBlkVirt->Idx[0].SendConIdx; + + /* Get our copy of the consumer index. The buffer descriptors */ + /* that are in between the consumer indices are freed. */ + SwConIdx = pDevice->SendConIdx; + + /* Move the packets from the TxPacketActiveQ that are sent out to */ + /* the TxPacketXmittedQ. Packets that are sent use the */ + /* descriptors that are between SwConIdx and HwConIdx. */ + while (SwConIdx != HwConIdx) { + /* Get the packet that was sent from the TxPacketActiveQ. */ + pPacket = + (PLM_PACKET) QQ_PopHead (&pDevice->TxPacketActiveQ. + Container); + + /* Set the return status. */ + pPacket->PacketStatus = LM_STATUS_SUCCESS; + + /* Put the packet in the TxPacketXmittedQ for indication later. */ + QQ_PushTail (&pDevice->TxPacketXmittedQ.Container, pPacket); + + /* Move to the next packet's BD. */ + SwConIdx = (SwConIdx + pPacket->u.Tx.FragCount) & + T3_SEND_RCB_ENTRY_COUNT_MASK; + + /* Update the number of unused BDs. */ + atomic_add (pPacket->u.Tx.FragCount, &pDevice->SendBdLeft); + + /* Get the new updated HwConIdx. */ + HwConIdx = pDevice->pStatusBlkVirt->Idx[0].SendConIdx; + } /* while */ + + /* Save the new SwConIdx. */ + pDevice->SendConIdx = SwConIdx; + +} /* LM_ServiceTxInterrupt */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +__inline static void LM_ServiceRxInterrupt (PLM_DEVICE_BLOCK pDevice) +{ + PLM_PACKET pPacket; + PT3_RCV_BD pRcvBd; + LM_UINT32 HwRcvRetProdIdx; + LM_UINT32 SwRcvRetConIdx; + + /* Loop thru the receive return rings for received packets. */ + HwRcvRetProdIdx = pDevice->pStatusBlkVirt->Idx[0].RcvProdIdx; + + SwRcvRetConIdx = pDevice->RcvRetConIdx; + while (SwRcvRetConIdx != HwRcvRetProdIdx) { + pRcvBd = &pDevice->pRcvRetBdVirt[SwRcvRetConIdx]; + + /* Get the received packet descriptor. */ + pPacket = (PLM_PACKET) (MM_UINT_PTR (pDevice->pPacketDescBase) + + MM_UINT_PTR (pRcvBd->Opaque)); + + /* Check the error flag. */ + if (pRcvBd->ErrorFlag && + pRcvBd->ErrorFlag != RCV_BD_ERR_ODD_NIBBLED_RCVD_MII) { + pPacket->PacketStatus = LM_STATUS_FAILURE; + + pDevice->RxCounters.RxPacketErrCnt++; + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_BAD_CRC) { + pDevice->RxCounters.RxErrCrcCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_COLL_DETECT) { + pDevice->RxCounters.RxErrCollCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_LINK_LOST_DURING_PKT) { + pDevice->RxCounters.RxErrLinkLostCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_PHY_DECODE_ERR) { + pDevice->RxCounters.RxErrPhyDecodeCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_ODD_NIBBLED_RCVD_MII) { + pDevice->RxCounters.RxErrOddNibbleCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_MAC_ABORT) { + pDevice->RxCounters.RxErrMacAbortCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_LEN_LT_64) { + pDevice->RxCounters.RxErrShortPacketCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_TRUNC_NO_RESOURCES) { + pDevice->RxCounters.RxErrNoResourceCnt++; + } + + if (pRcvBd->ErrorFlag & RCV_BD_ERR_GIANT_FRAME_RCVD) { + pDevice->RxCounters.RxErrLargePacketCnt++; + } + } else { + pPacket->PacketStatus = LM_STATUS_SUCCESS; + pPacket->PacketSize = pRcvBd->Len - 4; + + pPacket->Flags = pRcvBd->Flags; + if (pRcvBd->Flags & RCV_BD_FLAG_VLAN_TAG) { + pPacket->VlanTag = pRcvBd->VlanTag; + } + + pPacket->u.Rx.TcpUdpChecksum = pRcvBd->TcpUdpCksum; + } + + /* Put the packet descriptor containing the received packet */ + /* buffer in the RxPacketReceivedQ for indication later. */ + QQ_PushTail (&pDevice->RxPacketReceivedQ.Container, pPacket); + + /* Go to the next buffer descriptor. */ + SwRcvRetConIdx = (SwRcvRetConIdx + 1) & + T3_RCV_RETURN_RCB_ENTRY_COUNT_MASK; + + /* Get the updated HwRcvRetProdIdx. */ + HwRcvRetProdIdx = pDevice->pStatusBlkVirt->Idx[0].RcvProdIdx; + } /* while */ + + pDevice->RcvRetConIdx = SwRcvRetConIdx; + + /* Update the receive return ring consumer index. */ + MB_REG_WR (pDevice, Mailbox.RcvRetConIdx[0].Low, SwRcvRetConIdx); +} /* LM_ServiceRxInterrupt */ + +/******************************************************************************/ +/* Description: */ +/* This is the interrupt event handler routine. It acknowledges all */ +/* pending interrupts and process all pending events. */ +/* */ +/* Return: */ +/* LM_STATUS_SUCCESS */ +/******************************************************************************/ +LM_STATUS LM_ServiceInterrupts (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + int ServicePhyInt = FALSE; + + /* Setup the phy chip whenever the link status changes. */ + if (pDevice->LinkChngMode == T3_LINK_CHNG_MODE_USE_STATUS_REG) { + Value32 = REG_RD (pDevice, MacCtrl.Status); + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_MI_INTERRUPT) { + if (Value32 & MAC_STATUS_MI_INTERRUPT) { + ServicePhyInt = TRUE; + } + } else if (Value32 & MAC_STATUS_LINK_STATE_CHANGED) { + ServicePhyInt = TRUE; + } + } else { + if (pDevice->pStatusBlkVirt-> + Status & STATUS_BLOCK_LINK_CHANGED_STATUS) { + pDevice->pStatusBlkVirt->Status = + STATUS_BLOCK_UPDATED | (pDevice->pStatusBlkVirt-> + Status & + ~STATUS_BLOCK_LINK_CHANGED_STATUS); + ServicePhyInt = TRUE; + } + } +#if INCLUDE_TBI_SUPPORT + if (pDevice->IgnoreTbiLinkChange == TRUE) { + ServicePhyInt = FALSE; + } +#endif + if (ServicePhyInt == TRUE) { + LM_SetupPhy (pDevice); + } + + /* Service receive and transmit interrupts. */ + LM_ServiceRxInterrupt (pDevice); + LM_ServiceTxInterrupt (pDevice); + + /* No spinlock for this queue since this routine is serialized. */ + if (!QQ_Empty (&pDevice->RxPacketReceivedQ.Container)) { + /* Indicate receive packets. */ + MM_IndicateRxPackets (pDevice); + /* LM_QueueRxPackets(pDevice); */ + } + + /* No spinlock for this queue since this routine is serialized. */ + if (!QQ_Empty (&pDevice->TxPacketXmittedQ.Container)) { + MM_IndicateTxPackets (pDevice); + } + + return LM_STATUS_SUCCESS; +} /* LM_ServiceInterrupts */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_MulticastAdd (PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMcAddress) +{ + PLM_UINT8 pEntry; + LM_UINT32 j; + + pEntry = pDevice->McTable[0]; + for (j = 0; j < pDevice->McEntryCount; j++) { + if (IS_ETH_ADDRESS_EQUAL (pEntry, pMcAddress)) { + /* Found a match, increment the instance count. */ + pEntry[LM_MC_INSTANCE_COUNT_INDEX] += 1; + + return LM_STATUS_SUCCESS; + } + + pEntry += LM_MC_ENTRY_SIZE; + } + + if (pDevice->McEntryCount >= LM_MAX_MC_TABLE_SIZE) { + return LM_STATUS_FAILURE; + } + + pEntry = pDevice->McTable[pDevice->McEntryCount]; + + COPY_ETH_ADDRESS (pMcAddress, pEntry); + pEntry[LM_MC_INSTANCE_COUNT_INDEX] = 1; + + pDevice->McEntryCount++; + + LM_SetReceiveMask (pDevice, pDevice->ReceiveMask | LM_ACCEPT_MULTICAST); + + return LM_STATUS_SUCCESS; +} /* LM_MulticastAdd */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_MulticastDel (PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pMcAddress) +{ + PLM_UINT8 pEntry; + LM_UINT32 j; + + pEntry = pDevice->McTable[0]; + for (j = 0; j < pDevice->McEntryCount; j++) { + if (IS_ETH_ADDRESS_EQUAL (pEntry, pMcAddress)) { + /* Found a match, decrement the instance count. */ + pEntry[LM_MC_INSTANCE_COUNT_INDEX] -= 1; + + /* No more instance left, remove the address from the table. */ + /* Move the last entry in the table to the delete slot. */ + if (pEntry[LM_MC_INSTANCE_COUNT_INDEX] == 0 && + pDevice->McEntryCount > 1) { + + COPY_ETH_ADDRESS (pDevice-> + McTable[pDevice-> + McEntryCount - 1], + pEntry); + pEntry[LM_MC_INSTANCE_COUNT_INDEX] = + pDevice->McTable[pDevice->McEntryCount - 1] + [LM_MC_INSTANCE_COUNT_INDEX]; + } + pDevice->McEntryCount--; + + /* Update the receive mask if the table is empty. */ + if (pDevice->McEntryCount == 0) { + LM_SetReceiveMask (pDevice, + pDevice-> + ReceiveMask & + ~LM_ACCEPT_MULTICAST); + } + + return LM_STATUS_SUCCESS; + } + + pEntry += LM_MC_ENTRY_SIZE; + } + + return LM_STATUS_FAILURE; +} /* LM_MulticastDel */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_MulticastClear (PLM_DEVICE_BLOCK pDevice) +{ + pDevice->McEntryCount = 0; + + LM_SetReceiveMask (pDevice, + pDevice->ReceiveMask & ~LM_ACCEPT_MULTICAST); + + return LM_STATUS_SUCCESS; +} /* LM_MulticastClear */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_SetMacAddress (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 j; + PLM_UINT8 pMacAddress = pDevice->NodeAddress; + + for (j = 0; j < 4; j++) { + REG_WR (pDevice, MacCtrl.MacAddr[j].High, + (pMacAddress[0] << 8) | pMacAddress[1]); + REG_WR (pDevice, MacCtrl.MacAddr[j].Low, + (pMacAddress[2] << 24) | (pMacAddress[3] << 16) | + (pMacAddress[4] << 8) | pMacAddress[5]); + } + + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* Sets up the default line speed, and duplex modes based on the requested */ +/* media type. */ +/* */ +/* Return: */ +/* None. */ +/******************************************************************************/ +static LM_STATUS +LM_TranslateRequestedMediaType (LM_REQUESTED_MEDIA_TYPE RequestedMediaType, + PLM_MEDIA_TYPE pMediaType, + PLM_LINE_SPEED pLineSpeed, + PLM_DUPLEX_MODE pDuplexMode) +{ + *pMediaType = LM_MEDIA_TYPE_AUTO; + *pLineSpeed = LM_LINE_SPEED_UNKNOWN; + *pDuplexMode = LM_DUPLEX_MODE_UNKNOWN; + + /* determine media type */ + switch (RequestedMediaType) { + case LM_REQUESTED_MEDIA_TYPE_BNC: + *pMediaType = LM_MEDIA_TYPE_BNC; + *pLineSpeed = LM_LINE_SPEED_10MBPS; + *pDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case LM_REQUESTED_MEDIA_TYPE_UTP_AUTO: + *pMediaType = LM_MEDIA_TYPE_UTP; + break; + + case LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS: + *pMediaType = LM_MEDIA_TYPE_UTP; + *pLineSpeed = LM_LINE_SPEED_10MBPS; + *pDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS_FULL_DUPLEX: + *pMediaType = LM_MEDIA_TYPE_UTP; + *pLineSpeed = LM_LINE_SPEED_10MBPS; + *pDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + case LM_REQUESTED_MEDIA_TYPE_UTP_100MBPS: + *pMediaType = LM_MEDIA_TYPE_UTP; + *pLineSpeed = LM_LINE_SPEED_100MBPS; + *pDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case LM_REQUESTED_MEDIA_TYPE_UTP_100MBPS_FULL_DUPLEX: + *pMediaType = LM_MEDIA_TYPE_UTP; + *pLineSpeed = LM_LINE_SPEED_100MBPS; + *pDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + case LM_REQUESTED_MEDIA_TYPE_UTP_1000MBPS: + *pMediaType = LM_MEDIA_TYPE_UTP; + *pLineSpeed = LM_LINE_SPEED_1000MBPS; + *pDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case LM_REQUESTED_MEDIA_TYPE_UTP_1000MBPS_FULL_DUPLEX: + *pMediaType = LM_MEDIA_TYPE_UTP; + *pLineSpeed = LM_LINE_SPEED_1000MBPS; + *pDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + case LM_REQUESTED_MEDIA_TYPE_FIBER_100MBPS: + *pMediaType = LM_MEDIA_TYPE_FIBER; + *pLineSpeed = LM_LINE_SPEED_100MBPS; + *pDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case LM_REQUESTED_MEDIA_TYPE_FIBER_100MBPS_FULL_DUPLEX: + *pMediaType = LM_MEDIA_TYPE_FIBER; + *pLineSpeed = LM_LINE_SPEED_100MBPS; + *pDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + case LM_REQUESTED_MEDIA_TYPE_FIBER_1000MBPS: + *pMediaType = LM_MEDIA_TYPE_FIBER; + *pLineSpeed = LM_LINE_SPEED_1000MBPS; + *pDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case LM_REQUESTED_MEDIA_TYPE_FIBER_1000MBPS_FULL_DUPLEX: + *pMediaType = LM_MEDIA_TYPE_FIBER; + *pLineSpeed = LM_LINE_SPEED_1000MBPS; + *pDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + default: + break; + } /* switch */ + + return LM_STATUS_SUCCESS; +} /* LM_TranslateRequestedMediaType */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* LM_STATUS_LINK_ACTIVE */ +/* LM_STATUS_LINK_DOWN */ +/******************************************************************************/ +static LM_STATUS LM_InitBcm540xPhy (PLM_DEVICE_BLOCK pDevice) +{ + LM_LINE_SPEED CurrentLineSpeed; + LM_DUPLEX_MODE CurrentDuplexMode; + LM_STATUS CurrentLinkStatus; + LM_UINT32 Value32; + LM_UINT32 j; + +#if 1 /* jmb: bugfix -- moved here, out of code that sets initial pwr state */ + LM_WritePhy (pDevice, BCM5401_AUX_CTRL, 0x2); +#endif + if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5401_PHY_ID) { + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + + if (!pDevice->InitDone) { + Value32 = 0; + } + + if (!(Value32 & PHY_STATUS_LINK_PASS)) { + LM_WritePhy (pDevice, BCM5401_AUX_CTRL, 0x0c20); + + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x0012); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x1804); + + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x0013); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x1204); + + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x8006); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x0132); + + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x8006); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x0232); + + LM_WritePhy (pDevice, BCM540X_DSP_ADDRESS_REG, 0x201f); + LM_WritePhy (pDevice, BCM540X_DSP_RW_PORT, 0x0a20); + + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + for (j = 0; j < 1000; j++) { + MM_Wait (10); + + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + if (Value32 & PHY_STATUS_LINK_PASS) { + MM_Wait (40); + break; + } + } + + if ((pDevice->PhyId & PHY_ID_REV_MASK) == + PHY_BCM5401_B0_REV) { + if (!(Value32 & PHY_STATUS_LINK_PASS) + && (pDevice->OldLineSpeed == + LM_LINE_SPEED_1000MBPS)) { + LM_WritePhy (pDevice, PHY_CTRL_REG, + PHY_CTRL_PHY_RESET); + for (j = 0; j < 100; j++) { + MM_Wait (10); + + LM_ReadPhy (pDevice, + PHY_CTRL_REG, + &Value32); + if (! + (Value32 & + PHY_CTRL_PHY_RESET)) { + MM_Wait (40); + break; + } + } + + LM_WritePhy (pDevice, BCM5401_AUX_CTRL, + 0x0c20); + + LM_WritePhy (pDevice, + BCM540X_DSP_ADDRESS_REG, + 0x0012); + LM_WritePhy (pDevice, + BCM540X_DSP_RW_PORT, + 0x1804); + + LM_WritePhy (pDevice, + BCM540X_DSP_ADDRESS_REG, + 0x0013); + LM_WritePhy (pDevice, + BCM540X_DSP_RW_PORT, + 0x1204); + + LM_WritePhy (pDevice, + BCM540X_DSP_ADDRESS_REG, + 0x8006); + LM_WritePhy (pDevice, + BCM540X_DSP_RW_PORT, + 0x0132); + + LM_WritePhy (pDevice, + BCM540X_DSP_ADDRESS_REG, + 0x8006); + LM_WritePhy (pDevice, + BCM540X_DSP_RW_PORT, + 0x0232); + + LM_WritePhy (pDevice, + BCM540X_DSP_ADDRESS_REG, + 0x201f); + LM_WritePhy (pDevice, + BCM540X_DSP_RW_PORT, + 0x0a20); + } + } + } + } else if (pDevice->ChipRevId == T3_CHIP_ID_5701_A0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B0) { + /* Bug: 5701 A0, B0 TX CRC workaround. */ + LM_WritePhy (pDevice, 0x15, 0x0a75); + LM_WritePhy (pDevice, 0x1c, 0x8c68); + LM_WritePhy (pDevice, 0x1c, 0x8d68); + LM_WritePhy (pDevice, 0x1c, 0x8c68); + } + + /* Acknowledge interrupts. */ + LM_ReadPhy (pDevice, BCM540X_INT_STATUS_REG, &Value32); + LM_ReadPhy (pDevice, BCM540X_INT_STATUS_REG, &Value32); + + /* Configure the interrupt mask. */ + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_MI_INTERRUPT) { + LM_WritePhy (pDevice, BCM540X_INT_MASK_REG, + ~BCM540X_INT_LINK_CHANGE); + } + + /* Configure PHY led mode. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701 || + (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700)) { + if (pDevice->LedMode == LED_MODE_THREE_LINK) { + LM_WritePhy (pDevice, BCM540X_EXT_CTRL_REG, + BCM540X_EXT_CTRL_LINK3_LED_MODE); + } else { + LM_WritePhy (pDevice, BCM540X_EXT_CTRL_REG, 0); + } + } + + CurrentLinkStatus = LM_STATUS_LINK_DOWN; + + /* Get current link and duplex mode. */ + for (j = 0; j < 100; j++) { + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + + if (Value32 & PHY_STATUS_LINK_PASS) { + break; + } + MM_Wait (40); + } + + if (Value32 & PHY_STATUS_LINK_PASS) { + + /* Determine the current line and duplex settings. */ + LM_ReadPhy (pDevice, BCM540X_AUX_STATUS_REG, &Value32); + for (j = 0; j < 2000; j++) { + MM_Wait (10); + + LM_ReadPhy (pDevice, BCM540X_AUX_STATUS_REG, &Value32); + if (Value32) { + break; + } + } + + switch (Value32 & BCM540X_AUX_SPEED_MASK) { + case BCM540X_AUX_10BASET_HD: + CurrentLineSpeed = LM_LINE_SPEED_10MBPS; + CurrentDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case BCM540X_AUX_10BASET_FD: + CurrentLineSpeed = LM_LINE_SPEED_10MBPS; + CurrentDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + case BCM540X_AUX_100BASETX_HD: + CurrentLineSpeed = LM_LINE_SPEED_100MBPS; + CurrentDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case BCM540X_AUX_100BASETX_FD: + CurrentLineSpeed = LM_LINE_SPEED_100MBPS; + CurrentDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + case BCM540X_AUX_100BASET_HD: + CurrentLineSpeed = LM_LINE_SPEED_1000MBPS; + CurrentDuplexMode = LM_DUPLEX_MODE_HALF; + break; + + case BCM540X_AUX_100BASET_FD: + CurrentLineSpeed = LM_LINE_SPEED_1000MBPS; + CurrentDuplexMode = LM_DUPLEX_MODE_FULL; + break; + + default: + + CurrentLineSpeed = LM_LINE_SPEED_UNKNOWN; + CurrentDuplexMode = LM_DUPLEX_MODE_UNKNOWN; + break; + } + + /* Make sure we are in auto-neg mode. */ + for (j = 0; j < 200; j++) { + LM_ReadPhy (pDevice, PHY_CTRL_REG, &Value32); + if (Value32 && Value32 != 0x7fff) { + break; + } + + if (Value32 == 0 && pDevice->RequestedMediaType == + LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS) { + break; + } + + MM_Wait (10); + } + + /* Use the current line settings for "auto" mode. */ + if (pDevice->RequestedMediaType == LM_REQUESTED_MEDIA_TYPE_AUTO + || pDevice->RequestedMediaType == + LM_REQUESTED_MEDIA_TYPE_UTP_AUTO) { + if (Value32 & PHY_CTRL_AUTO_NEG_ENABLE) { + CurrentLinkStatus = LM_STATUS_LINK_ACTIVE; + + /* We may be exiting low power mode and the link is in */ + /* 10mb. In this case, we need to restart autoneg. */ + LM_ReadPhy (pDevice, BCM540X_1000BASET_CTRL_REG, + &Value32); + pDevice->advertising1000 = Value32; + /* 5702FE supports 10/100Mb only. */ + if (T3_ASIC_REV (pDevice->ChipRevId) != + T3_ASIC_REV_5703 + || pDevice->BondId != + GRC_MISC_BD_ID_5702FE) { + if (! + (Value32 & + (BCM540X_AN_AD_1000BASET_HALF | + BCM540X_AN_AD_1000BASET_FULL))) { + CurrentLinkStatus = + LM_STATUS_LINK_SETTING_MISMATCH; + } + } + } else { + CurrentLinkStatus = + LM_STATUS_LINK_SETTING_MISMATCH; + } + } else { + /* Force line settings. */ + /* Use the current setting if it matches the user's requested */ + /* setting. */ + LM_ReadPhy (pDevice, PHY_CTRL_REG, &Value32); + if ((pDevice->LineSpeed == CurrentLineSpeed) && + (pDevice->DuplexMode == CurrentDuplexMode)) { + if ((pDevice->DisableAutoNeg && + !(Value32 & PHY_CTRL_AUTO_NEG_ENABLE)) || + (!pDevice->DisableAutoNeg && + (Value32 & PHY_CTRL_AUTO_NEG_ENABLE))) { + CurrentLinkStatus = + LM_STATUS_LINK_ACTIVE; + } else { + CurrentLinkStatus = + LM_STATUS_LINK_SETTING_MISMATCH; + } + } else { + CurrentLinkStatus = + LM_STATUS_LINK_SETTING_MISMATCH; + } + } + + /* Save line settings. */ + pDevice->LineSpeed = CurrentLineSpeed; + pDevice->DuplexMode = CurrentDuplexMode; + pDevice->MediaType = LM_MEDIA_TYPE_UTP; + } + + return CurrentLinkStatus; +} /* LM_InitBcm540xPhy */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS +LM_SetFlowControl (PLM_DEVICE_BLOCK pDevice, + LM_UINT32 LocalPhyAd, LM_UINT32 RemotePhyAd) +{ + LM_FLOW_CONTROL FlowCap; + + /* Resolve flow control. */ + FlowCap = LM_FLOW_CONTROL_NONE; + + /* See Table 28B-3 of 802.3ab-1999 spec. */ + if (pDevice->FlowControlCap & LM_FLOW_CONTROL_AUTO_PAUSE) { + if (LocalPhyAd & PHY_AN_AD_PAUSE_CAPABLE) { + if (LocalPhyAd & PHY_AN_AD_ASYM_PAUSE) { + if (RemotePhyAd & + PHY_LINK_PARTNER_PAUSE_CAPABLE) { + FlowCap = + LM_FLOW_CONTROL_TRANSMIT_PAUSE | + LM_FLOW_CONTROL_RECEIVE_PAUSE; + } else if (RemotePhyAd & + PHY_LINK_PARTNER_ASYM_PAUSE) { + FlowCap = LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + } else { + if (RemotePhyAd & + PHY_LINK_PARTNER_PAUSE_CAPABLE) { + FlowCap = + LM_FLOW_CONTROL_TRANSMIT_PAUSE | + LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + } + } else if (LocalPhyAd & PHY_AN_AD_ASYM_PAUSE) { + if ((RemotePhyAd & PHY_LINK_PARTNER_PAUSE_CAPABLE) && + (RemotePhyAd & PHY_LINK_PARTNER_ASYM_PAUSE)) { + FlowCap = LM_FLOW_CONTROL_TRANSMIT_PAUSE; + } + } + } else { + FlowCap = pDevice->FlowControlCap; + } + + /* Enable/disable rx PAUSE. */ + pDevice->RxMode &= ~RX_MODE_ENABLE_FLOW_CONTROL; + if (FlowCap & LM_FLOW_CONTROL_RECEIVE_PAUSE && + (pDevice->FlowControlCap == LM_FLOW_CONTROL_AUTO_PAUSE || + pDevice->FlowControlCap & LM_FLOW_CONTROL_RECEIVE_PAUSE)) { + pDevice->FlowControl |= LM_FLOW_CONTROL_RECEIVE_PAUSE; + pDevice->RxMode |= RX_MODE_ENABLE_FLOW_CONTROL; + + } + REG_WR (pDevice, MacCtrl.RxMode, pDevice->RxMode); + + /* Enable/disable tx PAUSE. */ + pDevice->TxMode &= ~TX_MODE_ENABLE_FLOW_CONTROL; + if (FlowCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE && + (pDevice->FlowControlCap == LM_FLOW_CONTROL_AUTO_PAUSE || + pDevice->FlowControlCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE)) { + pDevice->FlowControl |= LM_FLOW_CONTROL_TRANSMIT_PAUSE; + pDevice->TxMode |= TX_MODE_ENABLE_FLOW_CONTROL; + + } + REG_WR (pDevice, MacCtrl.TxMode, pDevice->TxMode); + + return LM_STATUS_SUCCESS; +} + +#if INCLUDE_TBI_SUPPORT +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +STATIC LM_STATUS LM_InitBcm800xPhy (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + LM_UINT32 j; + + Value32 = REG_RD (pDevice, MacCtrl.Status); + + /* Reset the SERDES during init and when we have link. */ + if (!pDevice->InitDone || Value32 & MAC_STATUS_PCS_SYNCED) { + /* Set PLL lock range. */ + LM_WritePhy (pDevice, 0x16, 0x8007); + + /* Software reset. */ + LM_WritePhy (pDevice, 0x00, 0x8000); + + /* Wait for reset to complete. */ + for (j = 0; j < 500; j++) { + MM_Wait (10); + } + + /* Config mode; seletct PMA/Ch 1 regs. */ + LM_WritePhy (pDevice, 0x10, 0x8411); + + /* Enable auto-lock and comdet, select txclk for tx. */ + LM_WritePhy (pDevice, 0x11, 0x0a10); + + LM_WritePhy (pDevice, 0x18, 0x00a0); + LM_WritePhy (pDevice, 0x16, 0x41ff); + + /* Assert and deassert POR. */ + LM_WritePhy (pDevice, 0x13, 0x0400); + MM_Wait (40); + LM_WritePhy (pDevice, 0x13, 0x0000); + + LM_WritePhy (pDevice, 0x11, 0x0a50); + MM_Wait (40); + LM_WritePhy (pDevice, 0x11, 0x0a10); + + /* Delay for signal to stabilize. */ + for (j = 0; j < 15000; j++) { + MM_Wait (10); + } + + /* Deselect the channel register so we can read the PHY id later. */ + LM_WritePhy (pDevice, 0x10, 0x8011); + } + + return LM_STATUS_SUCCESS; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +STATIC LM_STATUS LM_SetupFiberPhy (PLM_DEVICE_BLOCK pDevice) +{ + LM_STATUS CurrentLinkStatus; + AUTONEG_STATUS AnStatus = 0; + LM_UINT32 Value32; + LM_UINT32 Cnt; + LM_UINT32 j, k; + + pDevice->MacMode &= ~(MAC_MODE_HALF_DUPLEX | MAC_MODE_PORT_MODE_MASK); + + /* Initialize the send_config register. */ + REG_WR (pDevice, MacCtrl.TxAutoNeg, 0); + + /* Enable TBI and full duplex mode. */ + pDevice->MacMode |= MAC_MODE_PORT_MODE_TBI; + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode); + + /* Initialize the BCM8002 SERDES PHY. */ + switch (pDevice->PhyId & PHY_ID_MASK) { + case PHY_BCM8002_PHY_ID: + LM_InitBcm800xPhy (pDevice); + break; + + default: + break; + } + + /* Enable link change interrupt. */ + REG_WR (pDevice, MacCtrl.MacEvent, + MAC_EVENT_ENABLE_LINK_STATE_CHANGED_ATTN); + + /* Default to link down. */ + CurrentLinkStatus = LM_STATUS_LINK_DOWN; + + /* Get the link status. */ + Value32 = REG_RD (pDevice, MacCtrl.Status); + if (Value32 & MAC_STATUS_PCS_SYNCED) { + if ((pDevice->RequestedMediaType == + LM_REQUESTED_MEDIA_TYPE_AUTO) + || (pDevice->DisableAutoNeg == FALSE)) { + /* auto-negotiation mode. */ + /* Initialize the autoneg default capaiblities. */ + AutonegInit (&pDevice->AnInfo); + + /* Set the context pointer to point to the main device structure. */ + pDevice->AnInfo.pContext = pDevice; + + /* Setup flow control advertisement register. */ + Value32 = GetPhyAdFlowCntrlSettings (pDevice); + if (Value32 & PHY_AN_AD_PAUSE_CAPABLE) { + pDevice->AnInfo.mr_adv_sym_pause = 1; + } else { + pDevice->AnInfo.mr_adv_sym_pause = 0; + } + + if (Value32 & PHY_AN_AD_ASYM_PAUSE) { + pDevice->AnInfo.mr_adv_asym_pause = 1; + } else { + pDevice->AnInfo.mr_adv_asym_pause = 0; + } + + /* Try to autoneg up to six times. */ + if (pDevice->IgnoreTbiLinkChange) { + Cnt = 1; + } else { + Cnt = 6; + } + for (j = 0; j < Cnt; j++) { + REG_WR (pDevice, MacCtrl.TxAutoNeg, 0); + + Value32 = + pDevice->MacMode & ~MAC_MODE_PORT_MODE_MASK; + REG_WR (pDevice, MacCtrl.Mode, Value32); + MM_Wait (20); + + REG_WR (pDevice, MacCtrl.Mode, + pDevice-> + MacMode | MAC_MODE_SEND_CONFIGS); + + MM_Wait (20); + + pDevice->AnInfo.State = AN_STATE_UNKNOWN; + pDevice->AnInfo.CurrentTime_us = 0; + + REG_WR (pDevice, Grc.Timer, 0); + for (k = 0; + (pDevice->AnInfo.CurrentTime_us < 75000) + && (k < 75000); k++) { + AnStatus = + Autoneg8023z (&pDevice->AnInfo); + + if ((AnStatus == AUTONEG_STATUS_DONE) || + (AnStatus == AUTONEG_STATUS_FAILED)) + { + break; + } + + pDevice->AnInfo.CurrentTime_us = + REG_RD (pDevice, Grc.Timer); + + } + if ((AnStatus == AUTONEG_STATUS_DONE) || + (AnStatus == AUTONEG_STATUS_FAILED)) { + break; + } + if (j >= 1) { + if (!(REG_RD (pDevice, MacCtrl.Status) & + MAC_STATUS_PCS_SYNCED)) { + break; + } + } + } + + /* Stop sending configs. */ + MM_AnTxIdle (&pDevice->AnInfo); + + /* Resolve flow control settings. */ + if ((AnStatus == AUTONEG_STATUS_DONE) && + pDevice->AnInfo.mr_an_complete + && pDevice->AnInfo.mr_link_ok + && pDevice->AnInfo.mr_lp_adv_full_duplex) { + LM_UINT32 RemotePhyAd; + LM_UINT32 LocalPhyAd; + + LocalPhyAd = 0; + if (pDevice->AnInfo.mr_adv_sym_pause) { + LocalPhyAd |= PHY_AN_AD_PAUSE_CAPABLE; + } + + if (pDevice->AnInfo.mr_adv_asym_pause) { + LocalPhyAd |= PHY_AN_AD_ASYM_PAUSE; + } + + RemotePhyAd = 0; + if (pDevice->AnInfo.mr_lp_adv_sym_pause) { + RemotePhyAd |= + PHY_LINK_PARTNER_PAUSE_CAPABLE; + } + + if (pDevice->AnInfo.mr_lp_adv_asym_pause) { + RemotePhyAd |= + PHY_LINK_PARTNER_ASYM_PAUSE; + } + + LM_SetFlowControl (pDevice, LocalPhyAd, + RemotePhyAd); + + CurrentLinkStatus = LM_STATUS_LINK_ACTIVE; + } + for (j = 0; j < 30; j++) { + MM_Wait (20); + REG_WR (pDevice, MacCtrl.Status, + MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED); + MM_Wait (20); + if ((REG_RD (pDevice, MacCtrl.Status) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + if (pDevice->PollTbiLink) { + Value32 = REG_RD (pDevice, MacCtrl.Status); + if (Value32 & MAC_STATUS_RECEIVING_CFG) { + pDevice->IgnoreTbiLinkChange = TRUE; + } else { + pDevice->IgnoreTbiLinkChange = FALSE; + } + } + Value32 = REG_RD (pDevice, MacCtrl.Status); + if (CurrentLinkStatus == LM_STATUS_LINK_DOWN && + (Value32 & MAC_STATUS_PCS_SYNCED) && + ((Value32 & MAC_STATUS_RECEIVING_CFG) == 0)) { + CurrentLinkStatus = LM_STATUS_LINK_ACTIVE; + } + } else { + /* We are forcing line speed. */ + pDevice->FlowControlCap &= ~LM_FLOW_CONTROL_AUTO_PAUSE; + LM_SetFlowControl (pDevice, 0, 0); + + CurrentLinkStatus = LM_STATUS_LINK_ACTIVE; + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode | + MAC_MODE_SEND_CONFIGS); + } + } + /* Set the link polarity bit. */ + pDevice->MacMode &= ~MAC_MODE_LINK_POLARITY; + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode); + + pDevice->pStatusBlkVirt->Status = STATUS_BLOCK_UPDATED | + (pDevice->pStatusBlkVirt-> + Status & ~STATUS_BLOCK_LINK_CHANGED_STATUS); + + for (j = 0; j < 100; j++) { + REG_WR (pDevice, MacCtrl.Status, MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED); + MM_Wait (5); + if ((REG_RD (pDevice, MacCtrl.Status) & + (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + + Value32 = REG_RD (pDevice, MacCtrl.Status); + if ((Value32 & MAC_STATUS_PCS_SYNCED) == 0) { + CurrentLinkStatus = LM_STATUS_LINK_DOWN; + if (pDevice->DisableAutoNeg == FALSE) { + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode | + MAC_MODE_SEND_CONFIGS); + MM_Wait (1); + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode); + } + } + + /* Initialize the current link status. */ + if (CurrentLinkStatus == LM_STATUS_LINK_ACTIVE) { + pDevice->LineSpeed = LM_LINE_SPEED_1000MBPS; + pDevice->DuplexMode = LM_DUPLEX_MODE_FULL; + REG_WR (pDevice, MacCtrl.LedCtrl, LED_CTRL_OVERRIDE_LINK_LED | + LED_CTRL_1000MBPS_LED_ON); + } else { + pDevice->LineSpeed = LM_LINE_SPEED_UNKNOWN; + pDevice->DuplexMode = LM_DUPLEX_MODE_UNKNOWN; + REG_WR (pDevice, MacCtrl.LedCtrl, LED_CTRL_OVERRIDE_LINK_LED | + LED_CTRL_OVERRIDE_TRAFFIC_LED); + } + + /* Indicate link status. */ + if (pDevice->LinkStatus != CurrentLinkStatus) { + pDevice->LinkStatus = CurrentLinkStatus; + MM_IndicateStatus (pDevice, CurrentLinkStatus); + } + + return LM_STATUS_SUCCESS; +} +#endif /* INCLUDE_TBI_SUPPORT */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_SetupCopperPhy (PLM_DEVICE_BLOCK pDevice) +{ + LM_STATUS CurrentLinkStatus; + LM_UINT32 Value32; + + /* Assume there is not link first. */ + CurrentLinkStatus = LM_STATUS_LINK_DOWN; + + /* Disable phy link change attention. */ + REG_WR (pDevice, MacCtrl.MacEvent, 0); + + /* Clear link change attention. */ + REG_WR (pDevice, MacCtrl.Status, MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED); + + /* Disable auto-polling for the moment. */ + pDevice->MiMode = 0xc0000; + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode); + MM_Wait (40); + + /* Determine the requested line speed and duplex. */ + pDevice->OldLineSpeed = pDevice->LineSpeed; + LM_TranslateRequestedMediaType (pDevice->RequestedMediaType, + &pDevice->MediaType, + &pDevice->LineSpeed, + &pDevice->DuplexMode); + + /* Initialize the phy chip. */ + switch (pDevice->PhyId & PHY_ID_MASK) { + case PHY_BCM5400_PHY_ID: + case PHY_BCM5401_PHY_ID: + case PHY_BCM5411_PHY_ID: + case PHY_BCM5701_PHY_ID: + case PHY_BCM5703_PHY_ID: + case PHY_BCM5704_PHY_ID: + CurrentLinkStatus = LM_InitBcm540xPhy (pDevice); + break; + + default: + break; + } + + if (CurrentLinkStatus == LM_STATUS_LINK_SETTING_MISMATCH) { + CurrentLinkStatus = LM_STATUS_LINK_DOWN; + } + + /* Setup flow control. */ + pDevice->FlowControl = LM_FLOW_CONTROL_NONE; + if (CurrentLinkStatus == LM_STATUS_LINK_ACTIVE) { + LM_FLOW_CONTROL FlowCap; /* Flow control capability. */ + + FlowCap = LM_FLOW_CONTROL_NONE; + + if (pDevice->DuplexMode == LM_DUPLEX_MODE_FULL) { + if (pDevice->DisableAutoNeg == FALSE || + pDevice->RequestedMediaType == + LM_REQUESTED_MEDIA_TYPE_AUTO + || pDevice->RequestedMediaType == + LM_REQUESTED_MEDIA_TYPE_UTP_AUTO) { + LM_UINT32 ExpectedPhyAd; + LM_UINT32 LocalPhyAd; + LM_UINT32 RemotePhyAd; + + LM_ReadPhy (pDevice, PHY_AN_AD_REG, + &LocalPhyAd); + pDevice->advertising = LocalPhyAd; + LocalPhyAd &= + (PHY_AN_AD_ASYM_PAUSE | + PHY_AN_AD_PAUSE_CAPABLE); + + ExpectedPhyAd = + GetPhyAdFlowCntrlSettings (pDevice); + + if (LocalPhyAd != ExpectedPhyAd) { + CurrentLinkStatus = LM_STATUS_LINK_DOWN; + } else { + LM_ReadPhy (pDevice, + PHY_LINK_PARTNER_ABILITY_REG, + &RemotePhyAd); + + LM_SetFlowControl (pDevice, LocalPhyAd, + RemotePhyAd); + } + } else { + pDevice->FlowControlCap &= + ~LM_FLOW_CONTROL_AUTO_PAUSE; + LM_SetFlowControl (pDevice, 0, 0); + } + } + } + + if (CurrentLinkStatus == LM_STATUS_LINK_DOWN) { + LM_ForceAutoNeg (pDevice, pDevice->RequestedMediaType); + + /* If we force line speed, we make get link right away. */ + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + if (Value32 & PHY_STATUS_LINK_PASS) { + CurrentLinkStatus = LM_STATUS_LINK_ACTIVE; + } + } + + /* GMII interface. */ + pDevice->MacMode &= ~MAC_MODE_PORT_MODE_MASK; + if (CurrentLinkStatus == LM_STATUS_LINK_ACTIVE) { + if (pDevice->LineSpeed == LM_LINE_SPEED_100MBPS || + pDevice->LineSpeed == LM_LINE_SPEED_10MBPS) { + pDevice->MacMode |= MAC_MODE_PORT_MODE_MII; + } else { + pDevice->MacMode |= MAC_MODE_PORT_MODE_GMII; + } + } else { + pDevice->MacMode |= MAC_MODE_PORT_MODE_GMII; + } + + /* Set the MAC to operate in the appropriate duplex mode. */ + pDevice->MacMode &= ~MAC_MODE_HALF_DUPLEX; + if (pDevice->DuplexMode == LM_DUPLEX_MODE_HALF) { + pDevice->MacMode |= MAC_MODE_HALF_DUPLEX; + } + + /* Set the link polarity bit. */ + pDevice->MacMode &= ~MAC_MODE_LINK_POLARITY; + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + if ((pDevice->LedMode == LED_MODE_LINK10) || + (CurrentLinkStatus == LM_STATUS_LINK_ACTIVE && + pDevice->LineSpeed == LM_LINE_SPEED_10MBPS)) { + pDevice->MacMode |= MAC_MODE_LINK_POLARITY; + } + } else { + if (CurrentLinkStatus == LM_STATUS_LINK_ACTIVE) { + pDevice->MacMode |= MAC_MODE_LINK_POLARITY; + } + + /* Set LED mode. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Value32 = LED_CTRL_PHY_MODE_1; + } else { + if (pDevice->LedMode == LED_MODE_OUTPUT) { + Value32 = LED_CTRL_PHY_MODE_2; + } else { + Value32 = LED_CTRL_PHY_MODE_1; + } + } + REG_WR (pDevice, MacCtrl.LedCtrl, Value32); + } + + REG_WR (pDevice, MacCtrl.Mode, pDevice->MacMode); + + /* Enable auto polling. */ + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_AUTO_POLLING) { + pDevice->MiMode |= MI_MODE_AUTO_POLLING_ENABLE; + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode); + } + + /* Enable phy link change attention. */ + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_MI_INTERRUPT) { + REG_WR (pDevice, MacCtrl.MacEvent, + MAC_EVENT_ENABLE_MI_INTERRUPT); + } else { + REG_WR (pDevice, MacCtrl.MacEvent, + MAC_EVENT_ENABLE_LINK_STATE_CHANGED_ATTN); + } + if ((T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) && + (CurrentLinkStatus == LM_STATUS_LINK_ACTIVE) && + (pDevice->LineSpeed == LM_LINE_SPEED_1000MBPS) && + (((pDevice->PciState & T3_PCI_STATE_CONVENTIONAL_PCI_MODE) && + (pDevice->PciState & T3_PCI_STATE_BUS_SPEED_HIGH)) || + !(pDevice->PciState & T3_PCI_STATE_CONVENTIONAL_PCI_MODE))) { + MM_Wait (120); + REG_WR (pDevice, MacCtrl.Status, MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED); + MEM_WR_OFFSET (pDevice, T3_FIRMWARE_MAILBOX, + T3_MAGIC_NUM_DISABLE_DMAW_ON_LINK_CHANGE); + } + + /* Indicate link status. */ + if (pDevice->LinkStatus != CurrentLinkStatus) { + pDevice->LinkStatus = CurrentLinkStatus; + MM_IndicateStatus (pDevice, CurrentLinkStatus); + } + + return LM_STATUS_SUCCESS; +} /* LM_SetupCopperPhy */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_SetupPhy (PLM_DEVICE_BLOCK pDevice) +{ + LM_STATUS LmStatus; + LM_UINT32 Value32; + +#if INCLUDE_TBI_SUPPORT + if (pDevice->EnableTbi) { + LmStatus = LM_SetupFiberPhy (pDevice); + } else +#endif /* INCLUDE_TBI_SUPPORT */ + { + LmStatus = LM_SetupCopperPhy (pDevice); + } + if (pDevice->ChipRevId == T3_CHIP_ID_5704_A0) { + if (!(pDevice->PciState & T3_PCI_STATE_CONVENTIONAL_PCI_MODE)) { + Value32 = REG_RD (pDevice, PciCfg.PciState); + REG_WR (pDevice, PciCfg.PciState, + Value32 | T3_PCI_STATE_RETRY_SAME_DMA); + } + } + if ((pDevice->LineSpeed == LM_LINE_SPEED_1000MBPS) && + (pDevice->DuplexMode == LM_DUPLEX_MODE_HALF)) { + REG_WR (pDevice, MacCtrl.TxLengths, 0x26ff); + } else { + REG_WR (pDevice, MacCtrl.TxLengths, 0x2620); + } + + return LmStatus; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_VOID +LM_ReadPhy (PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, PLM_UINT32 pData32) +{ + LM_UINT32 Value32; + LM_UINT32 j; + + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_AUTO_POLLING) { + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode & + ~MI_MODE_AUTO_POLLING_ENABLE); + MM_Wait (40); + } + + Value32 = (pDevice->PhyAddr << MI_COM_FIRST_PHY_ADDR_BIT) | + ((PhyReg & MI_COM_PHY_REG_ADDR_MASK) << + MI_COM_FIRST_PHY_REG_ADDR_BIT) | MI_COM_CMD_READ | MI_COM_START; + + REG_WR (pDevice, MacCtrl.MiCom, Value32); + + for (j = 0; j < 20; j++) { + MM_Wait (25); + + Value32 = REG_RD (pDevice, MacCtrl.MiCom); + + if (!(Value32 & MI_COM_BUSY)) { + MM_Wait (5); + Value32 = REG_RD (pDevice, MacCtrl.MiCom); + Value32 &= MI_COM_PHY_DATA_MASK; + break; + } + } + + if (Value32 & MI_COM_BUSY) { + Value32 = 0; + } + + *pData32 = Value32; + + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_AUTO_POLLING) { + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode); + MM_Wait (40); + } +} /* LM_ReadPhy */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_VOID +LM_WritePhy (PLM_DEVICE_BLOCK pDevice, LM_UINT32 PhyReg, LM_UINT32 Data32) +{ + LM_UINT32 Value32; + LM_UINT32 j; + + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_AUTO_POLLING) { + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode & + ~MI_MODE_AUTO_POLLING_ENABLE); + MM_Wait (40); + } + + Value32 = (pDevice->PhyAddr << MI_COM_FIRST_PHY_ADDR_BIT) | + ((PhyReg & MI_COM_PHY_REG_ADDR_MASK) << + MI_COM_FIRST_PHY_REG_ADDR_BIT) | (Data32 & MI_COM_PHY_DATA_MASK) | + MI_COM_CMD_WRITE | MI_COM_START; + + REG_WR (pDevice, MacCtrl.MiCom, Value32); + + for (j = 0; j < 20; j++) { + MM_Wait (25); + + Value32 = REG_RD (pDevice, MacCtrl.MiCom); + + if (!(Value32 & MI_COM_BUSY)) { + MM_Wait (5); + break; + } + } + + if (pDevice->PhyIntMode == T3_PHY_INT_MODE_AUTO_POLLING) { + REG_WR (pDevice, MacCtrl.MiMode, pDevice->MiMode); + MM_Wait (40); + } +} /* LM_WritePhy */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_SetPowerState (PLM_DEVICE_BLOCK pDevice, LM_POWER_STATE PowerLevel) +{ + LM_UINT32 PmeSupport; + LM_UINT32 Value32; + LM_UINT32 PmCtrl; + + /* make sureindirect accesses are enabled */ + MM_WriteConfig32 (pDevice, T3_PCI_MISC_HOST_CTRL_REG, + pDevice->MiscHostCtrl); + + /* Clear the PME_ASSERT bit and the power state bits. Also enable */ + /* the PME bit. */ + MM_ReadConfig32 (pDevice, T3_PCI_PM_STATUS_CTRL_REG, &PmCtrl); + + PmCtrl |= T3_PM_PME_ASSERTED; + PmCtrl &= ~T3_PM_POWER_STATE_MASK; + + /* Set the appropriate power state. */ + if (PowerLevel == LM_POWER_STATE_D0) { + + /* Bring the card out of low power mode. */ + PmCtrl |= T3_PM_POWER_STATE_D0; + MM_WriteConfig32 (pDevice, T3_PCI_PM_STATUS_CTRL_REG, PmCtrl); + + REG_WR (pDevice, Grc.LocalCtrl, pDevice->GrcLocalCtrl); + MM_Wait (40); +#if 0 /* Bugfix by jmb...can't call WritePhy here because pDevice not fully initialized */ + LM_WritePhy (pDevice, BCM5401_AUX_CTRL, 0x02); +#endif + + return LM_STATUS_SUCCESS; + } else if (PowerLevel == LM_POWER_STATE_D1) { + PmCtrl |= T3_PM_POWER_STATE_D1; + } else if (PowerLevel == LM_POWER_STATE_D2) { + PmCtrl |= T3_PM_POWER_STATE_D2; + } else if (PowerLevel == LM_POWER_STATE_D3) { + PmCtrl |= T3_PM_POWER_STATE_D3; + } else { + return LM_STATUS_FAILURE; + } + PmCtrl |= T3_PM_PME_ENABLE; + + /* Mask out all interrupts so LM_SetupPhy won't be called while we are */ + /* setting new line speed. */ + Value32 = REG_RD (pDevice, PciCfg.MiscHostCtrl); + REG_WR (pDevice, PciCfg.MiscHostCtrl, + Value32 | MISC_HOST_CTRL_MASK_PCI_INT); + + if (!pDevice->RestoreOnWakeUp) { + pDevice->RestoreOnWakeUp = TRUE; + pDevice->WakeUpDisableAutoNeg = pDevice->DisableAutoNeg; + pDevice->WakeUpRequestedMediaType = pDevice->RequestedMediaType; + } + + /* Force auto-negotiation to 10 line speed. */ + pDevice->DisableAutoNeg = FALSE; + pDevice->RequestedMediaType = LM_REQUESTED_MEDIA_TYPE_UTP_10MBPS; + LM_SetupPhy (pDevice); + + /* Put the driver in the initial state, and go through the power down */ + /* sequence. */ + LM_Halt (pDevice); + + MM_ReadConfig32 (pDevice, T3_PCI_PM_CAP_REG, &PmeSupport); + + if (pDevice->WakeUpModeCap != LM_WAKE_UP_MODE_NONE) { + + /* Enable WOL. */ + LM_WritePhy (pDevice, BCM5401_AUX_CTRL, 0x5a); + MM_Wait (40); + + /* Set LED mode. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Value32 = LED_CTRL_PHY_MODE_1; + } else { + if (pDevice->LedMode == LED_MODE_OUTPUT) { + Value32 = LED_CTRL_PHY_MODE_2; + } else { + Value32 = LED_CTRL_PHY_MODE_1; + } + } + + Value32 = MAC_MODE_PORT_MODE_MII; + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700) { + if (pDevice->LedMode == LED_MODE_LINK10 || + pDevice->WolSpeed == WOL_SPEED_10MB) { + Value32 |= MAC_MODE_LINK_POLARITY; + } + } else { + Value32 |= MAC_MODE_LINK_POLARITY; + } + REG_WR (pDevice, MacCtrl.Mode, Value32); + MM_Wait (40); + MM_Wait (40); + MM_Wait (40); + + /* Always enable magic packet wake-up if we have vaux. */ + if ((PmeSupport & T3_PCI_PM_CAP_PME_D3COLD) && + (pDevice->WakeUpModeCap & LM_WAKE_UP_MODE_MAGIC_PACKET)) { + Value32 |= MAC_MODE_DETECT_MAGIC_PACKET_ENABLE; + } + + REG_WR (pDevice, MacCtrl.Mode, Value32); + + /* Enable the receiver. */ + REG_WR (pDevice, MacCtrl.RxMode, RX_MODE_ENABLE); + } + + /* Disable tx/rx clocks, and seletect an alternate clock. */ + if (pDevice->WolSpeed == WOL_SPEED_100MB) { + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Value32 = + T3_PCI_DISABLE_RX_CLOCK | T3_PCI_DISABLE_TX_CLOCK | + T3_PCI_SELECT_ALTERNATE_CLOCK; + } else { + Value32 = T3_PCI_SELECT_ALTERNATE_CLOCK; + } + REG_WR (pDevice, PciCfg.ClockCtrl, Value32); + + MM_Wait (40); + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Value32 = + T3_PCI_DISABLE_RX_CLOCK | T3_PCI_DISABLE_TX_CLOCK | + T3_PCI_SELECT_ALTERNATE_CLOCK | + T3_PCI_44MHZ_CORE_CLOCK; + } else { + Value32 = T3_PCI_SELECT_ALTERNATE_CLOCK | + T3_PCI_44MHZ_CORE_CLOCK; + } + + REG_WR (pDevice, PciCfg.ClockCtrl, Value32); + + MM_Wait (40); + + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Value32 = + T3_PCI_DISABLE_RX_CLOCK | T3_PCI_DISABLE_TX_CLOCK | + T3_PCI_44MHZ_CORE_CLOCK; + } else { + Value32 = T3_PCI_44MHZ_CORE_CLOCK; + } + + REG_WR (pDevice, PciCfg.ClockCtrl, Value32); + } else { + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + Value32 = + T3_PCI_DISABLE_RX_CLOCK | T3_PCI_DISABLE_TX_CLOCK | + T3_PCI_SELECT_ALTERNATE_CLOCK | + T3_PCI_POWER_DOWN_PCI_PLL133; + } else { + Value32 = T3_PCI_SELECT_ALTERNATE_CLOCK | + T3_PCI_POWER_DOWN_PCI_PLL133; + } + + REG_WR (pDevice, PciCfg.ClockCtrl, Value32); + } + + MM_Wait (40); + + if (!pDevice->EepromWp + && (pDevice->WakeUpModeCap != LM_WAKE_UP_MODE_NONE)) { + /* Switch adapter to auxilliary power. */ + if (T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV (pDevice->ChipRevId) == T3_ASIC_REV_5701) { + /* GPIO0 = 1, GPIO1 = 1, GPIO2 = 0. */ + REG_WR (pDevice, Grc.LocalCtrl, pDevice->GrcLocalCtrl | + GRC_MISC_LOCAL_CTRL_GPIO_OE0 | + GRC_MISC_LOCAL_CTRL_GPIO_OE1 | + GRC_MISC_LOCAL_CTRL_GPIO_OE2 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT0 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT1); + MM_Wait (40); + } else { + /* GPIO0 = 0, GPIO1 = 1, GPIO2 = 1. */ + REG_WR (pDevice, Grc.LocalCtrl, pDevice->GrcLocalCtrl | + GRC_MISC_LOCAL_CTRL_GPIO_OE0 | + GRC_MISC_LOCAL_CTRL_GPIO_OE1 | + GRC_MISC_LOCAL_CTRL_GPIO_OE2 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT1 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT2); + MM_Wait (40); + + /* GPIO0 = 1, GPIO1 = 1, GPIO2 = 1. */ + REG_WR (pDevice, Grc.LocalCtrl, pDevice->GrcLocalCtrl | + GRC_MISC_LOCAL_CTRL_GPIO_OE0 | + GRC_MISC_LOCAL_CTRL_GPIO_OE1 | + GRC_MISC_LOCAL_CTRL_GPIO_OE2 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT0 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT1 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT2); + MM_Wait (40); + + /* GPIO0 = 1, GPIO1 = 1, GPIO2 = 0. */ + REG_WR (pDevice, Grc.LocalCtrl, pDevice->GrcLocalCtrl | + GRC_MISC_LOCAL_CTRL_GPIO_OE0 | + GRC_MISC_LOCAL_CTRL_GPIO_OE1 | + GRC_MISC_LOCAL_CTRL_GPIO_OE2 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT0 | + GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT1); + MM_Wait (40); + } + } + + /* Set the phy to low power mode. */ + /* Put the the hardware in low power mode. */ + MM_WriteConfig32 (pDevice, T3_PCI_PM_STATUS_CTRL_REG, PmCtrl); + + return LM_STATUS_SUCCESS; +} /* LM_SetPowerState */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +static LM_UINT32 GetPhyAdFlowCntrlSettings (PLM_DEVICE_BLOCK pDevice) +{ + LM_UINT32 Value32; + + Value32 = 0; + + /* Auto negotiation flow control only when autonegotiation is enabled. */ + if (pDevice->DisableAutoNeg == FALSE || + pDevice->RequestedMediaType == LM_REQUESTED_MEDIA_TYPE_AUTO || + pDevice->RequestedMediaType == LM_REQUESTED_MEDIA_TYPE_UTP_AUTO) { + /* Please refer to Table 28B-3 of the 802.3ab-1999 spec. */ + if ((pDevice->FlowControlCap == LM_FLOW_CONTROL_AUTO_PAUSE) || + ((pDevice->FlowControlCap & LM_FLOW_CONTROL_RECEIVE_PAUSE) + && (pDevice-> + FlowControlCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE))) { + Value32 |= PHY_AN_AD_PAUSE_CAPABLE; + } else if (pDevice-> + FlowControlCap & LM_FLOW_CONTROL_TRANSMIT_PAUSE) { + Value32 |= PHY_AN_AD_ASYM_PAUSE; + } else if (pDevice-> + FlowControlCap & LM_FLOW_CONTROL_RECEIVE_PAUSE) { + Value32 |= + PHY_AN_AD_PAUSE_CAPABLE | PHY_AN_AD_ASYM_PAUSE; + } + } + + return Value32; +} + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/* LM_STATUS_FAILURE */ +/* LM_STATUS_SUCCESS */ +/* */ +/******************************************************************************/ +static LM_STATUS +LM_ForceAutoNegBcm540xPhy (PLM_DEVICE_BLOCK pDevice, + LM_REQUESTED_MEDIA_TYPE RequestedMediaType) +{ + LM_MEDIA_TYPE MediaType; + LM_LINE_SPEED LineSpeed; + LM_DUPLEX_MODE DuplexMode; + LM_UINT32 NewPhyCtrl; + LM_UINT32 Value32; + LM_UINT32 Cnt; + + /* Get the interface type, line speed, and duplex mode. */ + LM_TranslateRequestedMediaType (RequestedMediaType, &MediaType, + &LineSpeed, &DuplexMode); + + if (pDevice->RestoreOnWakeUp) { + LM_WritePhy (pDevice, BCM540X_1000BASET_CTRL_REG, 0); + pDevice->advertising1000 = 0; + Value32 = PHY_AN_AD_10BASET_FULL | PHY_AN_AD_10BASET_HALF; + if (pDevice->WolSpeed == WOL_SPEED_100MB) { + Value32 |= + PHY_AN_AD_100BASETX_FULL | PHY_AN_AD_100BASETX_HALF; + } + Value32 |= PHY_AN_AD_PROTOCOL_802_3_CSMA_CD; + Value32 |= GetPhyAdFlowCntrlSettings (pDevice); + LM_WritePhy (pDevice, PHY_AN_AD_REG, Value32); + pDevice->advertising = Value32; + } + /* Setup the auto-negotiation advertisement register. */ + else if (LineSpeed == LM_LINE_SPEED_UNKNOWN) { + /* Setup the 10/100 Mbps auto-negotiation advertisement register. */ + Value32 = PHY_AN_AD_PROTOCOL_802_3_CSMA_CD | + PHY_AN_AD_10BASET_HALF | PHY_AN_AD_10BASET_FULL | + PHY_AN_AD_100BASETX_FULL | PHY_AN_AD_100BASETX_HALF; + Value32 |= GetPhyAdFlowCntrlSettings (pDevice); + + LM_WritePhy (pDevice, PHY_AN_AD_REG, Value32); + pDevice->advertising = Value32; + + /* Advertise 1000Mbps */ + Value32 = + BCM540X_AN_AD_1000BASET_HALF | BCM540X_AN_AD_1000BASET_FULL; + +#if INCLUDE_5701_AX_FIX + /* Bug: workaround for CRC error in gigabit mode when we are in */ + /* slave mode. This will force the PHY to operate in */ + /* master mode. */ + if (pDevice->ChipRevId == T3_CHIP_ID_5701_A0 || + pDevice->ChipRevId == T3_CHIP_ID_5701_B0) { + Value32 |= BCM540X_CONFIG_AS_MASTER | + BCM540X_ENABLE_CONFIG_AS_MASTER; + } +#endif + + LM_WritePhy (pDevice, BCM540X_1000BASET_CTRL_REG, Value32); + pDevice->advertising1000 = Value32; + } else { + if (LineSpeed == LM_LINE_SPEED_1000MBPS) { + Value32 = PHY_AN_AD_PROTOCOL_802_3_CSMA_CD; + Value32 |= GetPhyAdFlowCntrlSettings (pDevice); + + LM_WritePhy (pDevice, PHY_AN_AD_REG, Value32); + pDevice->advertising = Value32; + + if (DuplexMode != LM_DUPLEX_MODE_FULL) { + Value32 = BCM540X_AN_AD_1000BASET_HALF; + } else { + Value32 = BCM540X_AN_AD_1000BASET_FULL; + } + + LM_WritePhy (pDevice, BCM540X_1000BASET_CTRL_REG, + Value32); + pDevice->advertising1000 = Value32; + } else if (LineSpeed == LM_LINE_SPEED_100MBPS) { + LM_WritePhy (pDevice, BCM540X_1000BASET_CTRL_REG, 0); + pDevice->advertising1000 = 0; + + if (DuplexMode != LM_DUPLEX_MODE_FULL) { + Value32 = PHY_AN_AD_100BASETX_HALF; + } else { + Value32 = PHY_AN_AD_100BASETX_FULL; + } + + Value32 |= PHY_AN_AD_PROTOCOL_802_3_CSMA_CD; + Value32 |= GetPhyAdFlowCntrlSettings (pDevice); + + LM_WritePhy (pDevice, PHY_AN_AD_REG, Value32); + pDevice->advertising = Value32; + } else if (LineSpeed == LM_LINE_SPEED_10MBPS) { + LM_WritePhy (pDevice, BCM540X_1000BASET_CTRL_REG, 0); + pDevice->advertising1000 = 0; + + if (DuplexMode != LM_DUPLEX_MODE_FULL) { + Value32 = PHY_AN_AD_10BASET_HALF; + } else { + Value32 = PHY_AN_AD_10BASET_FULL; + } + + Value32 |= PHY_AN_AD_PROTOCOL_802_3_CSMA_CD; + Value32 |= GetPhyAdFlowCntrlSettings (pDevice); + + LM_WritePhy (pDevice, PHY_AN_AD_REG, Value32); + pDevice->advertising = Value32; + } + } + + /* Force line speed if auto-negotiation is disabled. */ + if (pDevice->DisableAutoNeg && LineSpeed != LM_LINE_SPEED_UNKNOWN) { + /* This code path is executed only when there is link. */ + pDevice->MediaType = MediaType; + pDevice->LineSpeed = LineSpeed; + pDevice->DuplexMode = DuplexMode; + + /* Force line seepd. */ + NewPhyCtrl = 0; + switch (LineSpeed) { + case LM_LINE_SPEED_10MBPS: + NewPhyCtrl |= PHY_CTRL_SPEED_SELECT_10MBPS; + break; + case LM_LINE_SPEED_100MBPS: + NewPhyCtrl |= PHY_CTRL_SPEED_SELECT_100MBPS; + break; + case LM_LINE_SPEED_1000MBPS: + NewPhyCtrl |= PHY_CTRL_SPEED_SELECT_1000MBPS; + break; + default: + NewPhyCtrl |= PHY_CTRL_SPEED_SELECT_1000MBPS; + break; + } + + if (DuplexMode == LM_DUPLEX_MODE_FULL) { + NewPhyCtrl |= PHY_CTRL_FULL_DUPLEX_MODE; + } + + /* Don't do anything if the PHY_CTRL is already what we wanted. */ + LM_ReadPhy (pDevice, PHY_CTRL_REG, &Value32); + if (Value32 != NewPhyCtrl) { + /* Temporary bring the link down before forcing line speed. */ + LM_WritePhy (pDevice, PHY_CTRL_REG, + PHY_CTRL_LOOPBACK_MODE); + + /* Wait for link to go down. */ + for (Cnt = 0; Cnt < 15000; Cnt++) { + MM_Wait (10); + + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + LM_ReadPhy (pDevice, PHY_STATUS_REG, &Value32); + + if (!(Value32 & PHY_STATUS_LINK_PASS)) { + MM_Wait (40); + break; + } + } + + LM_WritePhy (pDevice, PHY_CTRL_REG, NewPhyCtrl); + MM_Wait (40); + } + } else { + LM_WritePhy (pDevice, PHY_CTRL_REG, PHY_CTRL_AUTO_NEG_ENABLE | + PHY_CTRL_RESTART_AUTO_NEG); + } + + return LM_STATUS_SUCCESS; +} /* LM_ForceAutoNegBcm540xPhy */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +static LM_STATUS +LM_ForceAutoNeg (PLM_DEVICE_BLOCK pDevice, + LM_REQUESTED_MEDIA_TYPE RequestedMediaType) +{ + LM_STATUS LmStatus; + + /* Initialize the phy chip. */ + switch (pDevice->PhyId & PHY_ID_MASK) { + case PHY_BCM5400_PHY_ID: + case PHY_BCM5401_PHY_ID: + case PHY_BCM5411_PHY_ID: + case PHY_BCM5701_PHY_ID: + case PHY_BCM5703_PHY_ID: + case PHY_BCM5704_PHY_ID: + LmStatus = + LM_ForceAutoNegBcm540xPhy (pDevice, RequestedMediaType); + break; + + default: + LmStatus = LM_STATUS_FAILURE; + break; + } + + return LmStatus; +} /* LM_ForceAutoNeg */ + +/******************************************************************************/ +/* Description: */ +/* */ +/* Return: */ +/******************************************************************************/ +LM_STATUS LM_LoadFirmware (PLM_DEVICE_BLOCK pDevice, + PT3_FWIMG_INFO pFwImg, + LM_UINT32 LoadCpu, LM_UINT32 StartCpu) +{ + LM_UINT32 i; + LM_UINT32 address; + + if (LoadCpu & T3_RX_CPU_ID) { + if (LM_HaltCpu (pDevice, T3_RX_CPU_ID) != LM_STATUS_SUCCESS) { + return LM_STATUS_FAILURE; + } + + /* First of all clear scrach pad memory */ + for (i = 0; i < T3_RX_CPU_SPAD_SIZE; i += 4) { + LM_RegWrInd (pDevice, T3_RX_CPU_SPAD_ADDR + i, 0); + } + + /* Copy code first */ + address = T3_RX_CPU_SPAD_ADDR + (pFwImg->Text.Offset & 0xffff); + for (i = 0; i <= pFwImg->Text.Length; i += 4) { + LM_RegWrInd (pDevice, address + i, + ((LM_UINT32 *) pFwImg->Text.Buffer)[i / + 4]); + } + + address = + T3_RX_CPU_SPAD_ADDR + (pFwImg->ROnlyData.Offset & 0xffff); + for (i = 0; i <= pFwImg->ROnlyData.Length; i += 4) { + LM_RegWrInd (pDevice, address + i, + ((LM_UINT32 *) pFwImg->ROnlyData. + Buffer)[i / 4]); + } + + address = T3_RX_CPU_SPAD_ADDR + (pFwImg->Data.Offset & 0xffff); + for (i = 0; i <= pFwImg->Data.Length; i += 4) { + LM_RegWrInd (pDevice, address + i, + ((LM_UINT32 *) pFwImg->Data.Buffer)[i / + 4]); + } + } + + if (LoadCpu & T3_TX_CPU_ID) { + if (LM_HaltCpu (pDevice, T3_TX_CPU_ID) != LM_STATUS_SUCCESS) { + return LM_STATUS_FAILURE; + } + + /* First of all clear scrach pad memory */ + for (i = 0; i < T3_TX_CPU_SPAD_SIZE; i += 4) { + LM_RegWrInd (pDevice, T3_TX_CPU_SPAD_ADDR + i, 0); + } + + /* Copy code first */ + address = T3_TX_CPU_SPAD_ADDR + (pFwImg->Text.Offset & 0xffff); + for (i = 0; i <= pFwImg->Text.Length; i += 4) { + LM_RegWrInd (pDevice, address + i, + ((LM_UINT32 *) pFwImg->Text.Buffer)[i / + 4]); + } + + address = + T3_TX_CPU_SPAD_ADDR + (pFwImg->ROnlyData.Offset & 0xffff); + for (i = 0; i <= pFwImg->ROnlyData.Length; i += 4) { + LM_RegWrInd (pDevice, address + i, + ((LM_UINT32 *) pFwImg->ROnlyData. + Buffer)[i / 4]); + } + + address = T3_TX_CPU_SPAD_ADDR + (pFwImg->Data.Offset & 0xffff); + for (i = 0; i <= pFwImg->Data.Length; i += 4) { + LM_RegWrInd (pDevice, address + i, + ((LM_UINT32 *) pFwImg->Data.Buffer)[i / + 4]); + } + } + + if (StartCpu & T3_RX_CPU_ID) { + /* Start Rx CPU */ + REG_WR (pDevice, rxCpu.reg.state, 0xffffffff); + REG_WR (pDevice, rxCpu.reg.PC, pFwImg->StartAddress); + for (i = 0; i < 5; i++) { + if (pFwImg->StartAddress == + REG_RD (pDevice, rxCpu.reg.PC)) + break; + + REG_WR (pDevice, rxCpu.reg.state, 0xffffffff); + REG_WR (pDevice, rxCpu.reg.mode, CPU_MODE_HALT); + REG_WR (pDevice, rxCpu.reg.PC, pFwImg->StartAddress); + MM_Wait (1000); + } + + REG_WR (pDevice, rxCpu.reg.state, 0xffffffff); + REG_WR (pDevice, rxCpu.reg.mode, 0); + } + + if (StartCpu & T3_TX_CPU_ID) { + /* Start Tx CPU */ + REG_WR (pDevice, txCpu.reg.state, 0xffffffff); + REG_WR (pDevice, txCpu.reg.PC, pFwImg->StartAddress); + for (i = 0; i < 5; i++) { + if (pFwImg->StartAddress == + REG_RD (pDevice, txCpu.reg.PC)) + break; + + REG_WR (pDevice, txCpu.reg.state, 0xffffffff); + REG_WR (pDevice, txCpu.reg.mode, CPU_MODE_HALT); + REG_WR (pDevice, txCpu.reg.PC, pFwImg->StartAddress); + MM_Wait (1000); + } + + REG_WR (pDevice, txCpu.reg.state, 0xffffffff); + REG_WR (pDevice, txCpu.reg.mode, 0); + } + + return LM_STATUS_SUCCESS; +} + +STATIC LM_STATUS LM_HaltCpu (PLM_DEVICE_BLOCK pDevice, LM_UINT32 cpu_number) +{ + LM_UINT32 i; + + if (cpu_number == T3_RX_CPU_ID) { + for (i = 0; i < 10000; i++) { + REG_WR (pDevice, rxCpu.reg.state, 0xffffffff); + REG_WR (pDevice, rxCpu.reg.mode, CPU_MODE_HALT); + + if (REG_RD (pDevice, rxCpu.reg.mode) & CPU_MODE_HALT) + break; + } + + REG_WR (pDevice, rxCpu.reg.state, 0xffffffff); + REG_WR (pDevice, rxCpu.reg.mode, CPU_MODE_HALT); + MM_Wait (10); + } else { + for (i = 0; i < 10000; i++) { + REG_WR (pDevice, txCpu.reg.state, 0xffffffff); + REG_WR (pDevice, txCpu.reg.mode, CPU_MODE_HALT); + + if (REG_RD (pDevice, txCpu.reg.mode) & CPU_MODE_HALT) + break; + } + } + + return ((i == 10000) ? LM_STATUS_FAILURE : LM_STATUS_SUCCESS); +} + +int LM_BlinkLED (PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlinkDurationSec) +{ + LM_UINT32 Oldcfg; + int j; + int ret = 0; + + if (BlinkDurationSec == 0) { + return 0; + } + if (BlinkDurationSec > 120) { + BlinkDurationSec = 120; + } + + Oldcfg = REG_RD (pDevice, MacCtrl.LedCtrl); + for (j = 0; j < BlinkDurationSec * 2; j++) { + if (j % 2) { + /* Turn on the LEDs. */ + REG_WR (pDevice, MacCtrl.LedCtrl, + LED_CTRL_OVERRIDE_LINK_LED | + LED_CTRL_1000MBPS_LED_ON | + LED_CTRL_100MBPS_LED_ON | + LED_CTRL_10MBPS_LED_ON | + LED_CTRL_OVERRIDE_TRAFFIC_LED | + LED_CTRL_BLINK_TRAFFIC_LED | + LED_CTRL_TRAFFIC_LED); + } else { + /* Turn off the LEDs. */ + REG_WR (pDevice, MacCtrl.LedCtrl, + LED_CTRL_OVERRIDE_LINK_LED | + LED_CTRL_OVERRIDE_TRAFFIC_LED); + } + +#ifndef EMBEDDED + current->state = TASK_INTERRUPTIBLE; + if (schedule_timeout (HZ / 2) != 0) { + ret = -EINTR; + break; + } +#else + udelay (100000); /* 1s sleep */ +#endif + } + REG_WR (pDevice, MacCtrl.LedCtrl, Oldcfg); + return ret; +} + +int t3_do_dma (PLM_DEVICE_BLOCK pDevice, + LM_PHYSICAL_ADDRESS host_addr_phy, int length, int dma_read) +{ + T3_DMA_DESC dma_desc; + int i; + LM_UINT32 dma_desc_addr; + LM_UINT32 value32; + + REG_WR (pDevice, BufMgr.Mode, 0); + REG_WR (pDevice, Ftq.Reset, 0); + + dma_desc.host_addr.High = host_addr_phy.High; + dma_desc.host_addr.Low = host_addr_phy.Low; + dma_desc.nic_mbuf = 0x2100; + dma_desc.len = length; + dma_desc.flags = 0x00000004; /* Generate Rx-CPU event */ + + if (dma_read) { + dma_desc.cqid_sqid = (T3_QID_RX_BD_COMP << 8) | + T3_QID_DMA_HIGH_PRI_READ; + REG_WR (pDevice, DmaRead.Mode, DMA_READ_MODE_ENABLE); + } else { + dma_desc.cqid_sqid = (T3_QID_RX_DATA_COMP << 8) | + T3_QID_DMA_HIGH_PRI_WRITE; + REG_WR (pDevice, DmaWrite.Mode, DMA_WRITE_MODE_ENABLE); + } + + dma_desc_addr = T3_NIC_DMA_DESC_POOL_ADDR; + + /* Writing this DMA descriptor to DMA memory */ + for (i = 0; i < sizeof (T3_DMA_DESC); i += 4) { + value32 = *((PLM_UINT32) (((PLM_UINT8) & dma_desc) + i)); + MM_WriteConfig32 (pDevice, T3_PCI_MEM_WIN_ADDR_REG, + dma_desc_addr + i); + MM_WriteConfig32 (pDevice, T3_PCI_MEM_WIN_DATA_REG, + cpu_to_le32 (value32)); + } + MM_WriteConfig32 (pDevice, T3_PCI_MEM_WIN_ADDR_REG, 0); + + if (dma_read) + REG_WR (pDevice, Ftq.DmaHighReadFtqFifoEnqueueDequeue, + dma_desc_addr); + else + REG_WR (pDevice, Ftq.DmaHighWriteFtqFifoEnqueueDequeue, + dma_desc_addr); + + for (i = 0; i < 40; i++) { + if (dma_read) + value32 = + REG_RD (pDevice, + Ftq.RcvBdCompFtqFifoEnqueueDequeue); + else + value32 = + REG_RD (pDevice, + Ftq.RcvDataCompFtqFifoEnqueueDequeue); + + if ((value32 & 0xffff) == dma_desc_addr) + break; + + MM_Wait (10); + } + + return LM_STATUS_SUCCESS; +} + +STATIC LM_STATUS +LM_DmaTest (PLM_DEVICE_BLOCK pDevice, PLM_UINT8 pBufferVirt, + LM_PHYSICAL_ADDRESS BufferPhy, LM_UINT32 BufferSize) +{ + int j; + LM_UINT32 *ptr; + int dma_success = 0; + + if (T3_ASIC_REV (pDevice->ChipRevId) != T3_ASIC_REV_5700 && + T3_ASIC_REV (pDevice->ChipRevId) != T3_ASIC_REV_5701) { + return LM_STATUS_SUCCESS; + } + while (!dma_success) { + /* Fill data with incremental patterns */ + ptr = (LM_UINT32 *) pBufferVirt; + for (j = 0; j < BufferSize / 4; j++) + *ptr++ = j; + + if (t3_do_dma (pDevice, BufferPhy, BufferSize, 1) == + LM_STATUS_FAILURE) { + return LM_STATUS_FAILURE; + } + + MM_Wait (40); + ptr = (LM_UINT32 *) pBufferVirt; + /* Fill data with zero */ + for (j = 0; j < BufferSize / 4; j++) + *ptr++ = 0; + + if (t3_do_dma (pDevice, BufferPhy, BufferSize, 0) == + LM_STATUS_FAILURE) { + return LM_STATUS_FAILURE; + } + + MM_Wait (40); + /* Check for data */ + ptr = (LM_UINT32 *) pBufferVirt; + for (j = 0; j < BufferSize / 4; j++) { + if (*ptr++ != j) { + if ((pDevice-> + DmaReadWriteCtrl & + DMA_CTRL_WRITE_BOUNDARY_MASK) + == DMA_CTRL_WRITE_BOUNDARY_DISABLE) { + pDevice->DmaReadWriteCtrl = + (pDevice-> + DmaReadWriteCtrl & + ~DMA_CTRL_WRITE_BOUNDARY_MASK) | + DMA_CTRL_WRITE_BOUNDARY_16; + REG_WR (pDevice, + PciCfg.DmaReadWriteCtrl, + pDevice->DmaReadWriteCtrl); + break; + } else { + return LM_STATUS_FAILURE; + } + } + } + if (j == (BufferSize / 4)) + dma_success = 1; + } + return LM_STATUS_SUCCESS; +} diff --git a/roms/u-boot-sam460ex/drivers/net/tigon3.h b/roms/u-boot-sam460ex/drivers/net/tigon3.h new file mode 100644 index 000000000..551107bc7 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/tigon3.h @@ -0,0 +1,3339 @@ + +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation, located in the file LICENSE. */ +/* */ +/* History: */ +/* */ +/******************************************************************************/ + +#ifndef TIGON3_H +#define TIGON3_H + +#include "bcm570x_lm.h" +#if INCLUDE_TBI_SUPPORT +#include "bcm570x_autoneg.h" +#endif + +/* io defines */ +#if !defined(BIG_ENDIAN_HOST) +#define readl(addr) \ + (LONGSWAP((*(volatile unsigned int *)(addr)))) +#define writel(b,addr) \ + ((*(volatile unsigned int *)(addr)) = (LONGSWAP(b))) +#else +#if 0 /* !defined(PPC603) */ +#define readl(addr) (*(volatile unsigned int*)(0xa0000000 + (unsigned long)(addr))) +#define writel(b,addr) ((*(volatile unsigned int *) ((unsigned long)(addr) + 0xa0000000)) = (b)) +#else +#if 1 +#define readl(addr) (*(volatile unsigned int*)(addr)) +#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) +#else +extern int sprintf (char *buf, const char *f, ...); +static __inline unsigned int readl (void *addr) +{ + char buf[128]; + unsigned int tmp = (*(volatile unsigned int *)(addr)); + sprintf (buf, "%s:%s: read 0x%x from 0x%x\n", __FILE__, __LINE__, tmp, + addr, 0, 0); + sysSerialPrintString (buf); + return tmp; +} +static __inline void writel (unsigned int b, unsigned int addr) +{ + char buf[128]; + ((*(volatile unsigned int *)(addr)) = (b)); + sprintf (buf, "%s:%s: write 0x%x to 0x%x\n", __FILE__, __LINE__, b, + addr, 0, 0); + sysSerialPrintString (buf); +} +#endif +#endif /* PPC603 */ +#endif + +/******************************************************************************/ +/* Constants. */ +/******************************************************************************/ + +/* Maxim number of packet descriptors used for sending packets. */ +#define MAX_TX_PACKET_DESC_COUNT 600 +#define DEFAULT_TX_PACKET_DESC_COUNT 2 + +/* Maximum number of packet descriptors used for receiving packets. */ +#if T3_JUMBO_RCB_ENTRY_COUNT +#define MAX_RX_PACKET_DESC_COUNT \ + (T3_STD_RCV_RCB_ENTRY_COUNT + T3_JUMBO_RCV_RCB_ENTRY_COUNT) +#else +#define MAX_RX_PACKET_DESC_COUNT 800 +#endif +#define DEFAULT_RX_PACKET_DESC_COUNT 2 + +/* Threshhold for double copying small tx packets. 0 will disable double */ +/* copying of small Tx packets. */ +#define DEFAULT_TX_COPY_BUFFER_SIZE 0 +#define MIN_TX_COPY_BUFFER_SIZE 64 +#define MAX_TX_COPY_BUFFER_SIZE 512 + +/* Cache line. */ +#define COMMON_CACHE_LINE_SIZE 0x20 +#define COMMON_CACHE_LINE_MASK (COMMON_CACHE_LINE_SIZE-1) + +/* Maximum number of fragment we can handle. */ +#ifndef MAX_FRAGMENT_COUNT +#define MAX_FRAGMENT_COUNT 32 +#endif + +/* B0 bug. */ +#define BCM5700_BX_MIN_FRAG_SIZE 10 +#define BCM5700_BX_MIN_FRAG_BUF_SIZE 16 /* nice aligned size. */ +#define BCM5700_BX_MIN_FRAG_BUF_SIZE_MASK (BCM5700_BX_MIN_FRAG_BUF_SIZE-1) +#define BCM5700_BX_TX_COPY_BUF_SIZE (BCM5700_BX_MIN_FRAG_BUF_SIZE * \ + MAX_FRAGMENT_COUNT) + +/* MAGIC number. */ +/* #define T3_MAGIC_NUM 'KevT' */ +#define T3_FIRMWARE_MAILBOX 0x0b50 +#define T3_MAGIC_NUM 0x4B657654 +#define T3_MAGIC_NUM_DISABLE_DMAW_ON_LINK_CHANGE 0x4861764b + +#define T3_NIC_DATA_SIG_ADDR 0x0b54 +#define T3_NIC_DATA_SIG 0x4b657654 + +#define T3_NIC_DATA_NIC_CFG_ADDR 0x0b58 +#define T3_NIC_CFG_LED_MODE_UNKNOWN BIT_NONE +#define T3_NIC_CFG_LED_MODE_TRIPLE_SPEED BIT_2 +#define T3_NIC_CFG_LED_MODE_LINK_SPEED BIT_3 +#define T3_NIC_CFG_LED_MODE_OPEN_DRAIN BIT_2 +#define T3_NIC_CFG_LED_MODE_OUTPUT BIT_3 +#define T3_NIC_CFG_LED_MODE_MASK (BIT_2 | BIT_3) +#define T3_NIC_CFG_PHY_TYPE_UNKNOWN BIT_NONE +#define T3_NIC_CFG_PHY_TYPE_COPPER BIT_4 +#define T3_NIC_CFG_PHY_TYPE_FIBER BIT_5 +#define T3_NIC_CFG_PHY_TYPE_MASK (BIT_4 | BIT_5) +#define T3_NIC_CFG_ENABLE_WOL BIT_6 +#define T3_NIC_CFG_ENABLE_ASF BIT_7 +#define T3_NIC_EEPROM_WP BIT_8 + +#define T3_NIC_DATA_PHY_ID_ADDR 0x0b74 +#define T3_NIC_PHY_ID1_MASK 0xffff0000 +#define T3_NIC_PHY_ID2_MASK 0x0000ffff + +#define T3_CMD_MAILBOX 0x0b78 +#define T3_CMD_NICDRV_ALIVE 0x01 +#define T3_CMD_NICDRV_PAUSE_FW 0x02 +#define T3_CMD_NICDRV_IPV4ADDR_CHANGE 0x03 +#define T3_CMD_NICDRV_IPV6ADDR_CHANGE 0x04 +#define T3_CMD_5703A0_FIX_DMAFW_DMAR 0x05 +#define T3_CMD_5703A0_FIX_DMAFW_DMAW 0x06 + +#define T3_CMD_LENGTH_MAILBOX 0x0b7c +#define T3_CMD_DATA_MAILBOX 0x0b80 + +#define T3_ASF_FW_STATUS_MAILBOX 0x0c00 + +#define T3_DRV_STATE_MAILBOX 0x0c04 +#define T3_DRV_STATE_START 0x01 +#define T3_DRV_STATE_UNLOAD 0x02 +#define T3_DRV_STATE_WOL 0x03 +#define T3_DRV_STATE_SUSPEND 0x04 + +#define T3_FW_RESET_TYPE_MAILBOX 0x0c08 + +#define T3_MAC_ADDR_HIGH_MAILBOX 0x0c14 +#define T3_MAC_ADDR_LOW_MAILBOX 0x0c18 + +/******************************************************************************/ +/* Hardware constants. */ +/******************************************************************************/ + +/* Number of entries in the send ring: must be 512. */ +#define T3_SEND_RCB_ENTRY_COUNT 512 +#define T3_SEND_RCB_ENTRY_COUNT_MASK (T3_SEND_RCB_ENTRY_COUNT-1) + +/* Number of send RCBs. May be 1-16 but for now, only support one. */ +#define T3_MAX_SEND_RCB_COUNT 16 + +/* Number of entries in the Standard Receive RCB. Must be 512 entries. */ +#define T3_STD_RCV_RCB_ENTRY_COUNT 512 +#define T3_STD_RCV_RCB_ENTRY_COUNT_MASK (T3_STD_RCV_RCB_ENTRY_COUNT-1) +#define DEFAULT_STD_RCV_DESC_COUNT 200 /* Must be < 512. */ +#define MAX_STD_RCV_BUFFER_SIZE 0x600 + +/* Number of entries in the Mini Receive RCB. This value can either be */ +/* 0, 1024. Currently Mini Receive RCB is disabled. */ +#ifndef T3_MINI_RCV_RCB_ENTRY_COUNT +#define T3_MINI_RCV_RCB_ENTRY_COUNT 0 +#endif /* T3_MINI_RCV_RCB_ENTRY_COUNT */ +#define T3_MINI_RCV_RCB_ENTRY_COUNT_MASK (T3_MINI_RCV_RCB_ENTRY_COUNT-1) +#define MAX_MINI_RCV_BUFFER_SIZE 512 +#define DEFAULT_MINI_RCV_BUFFER_SIZE 64 +#define DEFAULT_MINI_RCV_DESC_COUNT 100 /* Must be < 1024. */ + +/* Number of entries in the Jumbo Receive RCB. This value must 256 or 0. */ +/* Currently, Jumbo Receive RCB is disabled. */ +#ifndef T3_JUMBO_RCV_RCB_ENTRY_COUNT +#define T3_JUMBO_RCV_RCB_ENTRY_COUNT 0 +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ +#define T3_JUMBO_RCV_RCB_ENTRY_COUNT_MASK (T3_JUMBO_RCV_RCB_ENTRY_COUNT-1) + +#define MAX_JUMBO_RCV_BUFFER_SIZE (10 * 1024) /* > 1514 */ +#define DEFAULT_JUMBO_RCV_BUFFER_SIZE (4 * 1024) /* > 1514 */ +#define DEFAULT_JUMBO_RCV_DESC_COUNT 128 /* Must be < 256. */ + +#define MAX_JUMBO_TX_BUFFER_SIZE (8 * 1024) /* > 1514 */ +#define DEFAULT_JUMBO_TX_BUFFER_SIZE (4 * 1024) /* > 1514 */ + +/* Number of receive return RCBs. Maybe 1-16 but for now, only support one. */ +#define T3_MAX_RCV_RETURN_RCB_COUNT 16 + +/* Number of entries in a Receive Return ring. This value is either 1024 */ +/* or 2048. */ +#ifndef T3_RCV_RETURN_RCB_ENTRY_COUNT +#define T3_RCV_RETURN_RCB_ENTRY_COUNT 1024 +#endif /* T3_RCV_RETURN_RCB_ENTRY_COUNT */ +#define T3_RCV_RETURN_RCB_ENTRY_COUNT_MASK (T3_RCV_RETURN_RCB_ENTRY_COUNT-1) + +/* Default coalescing parameters. */ +#define DEFAULT_RX_COALESCING_TICKS 100 +#define MAX_RX_COALESCING_TICKS 500 +#define DEFAULT_TX_COALESCING_TICKS 400 +#define MAX_TX_COALESCING_TICKS 500 +#define DEFAULT_RX_MAX_COALESCED_FRAMES 10 +#define MAX_RX_MAX_COALESCED_FRAMES 100 +#define ADAPTIVE_LO_RX_MAX_COALESCED_FRAMES 5 +#define ADAPTIVE_HI_RX_MAX_COALESCED_FRAMES 42 +#define ADAPTIVE_LO_RX_COALESCING_TICKS 50 +#define ADAPTIVE_HI_RX_COALESCING_TICKS 300 +#define ADAPTIVE_LO_PKT_THRESH 30000 +#define ADAPTIVE_HI_PKT_THRESH 74000 +#define DEFAULT_TX_MAX_COALESCED_FRAMES 40 +#define ADAPTIVE_LO_TX_MAX_COALESCED_FRAMES 25 +#define ADAPTIVE_HI_TX_MAX_COALESCED_FRAMES 75 +#define MAX_TX_MAX_COALESCED_FRAMES 100 + +#define DEFAULT_RX_COALESCING_TICKS_DURING_INT 25 +#define DEFAULT_TX_COALESCING_TICKS_DURING_INT 25 +#define DEFAULT_RX_MAX_COALESCED_FRAMES_DURING_INT 5 +#define DEFAULT_TX_MAX_COALESCED_FRAMES_DURING_INT 5 + +#define BAD_DEFAULT_VALUE 0xffffffff + +#define DEFAULT_STATS_COALESCING_TICKS 1000000 +#define MAX_STATS_COALESCING_TICKS 3600000000U + +/* Receive BD Replenish thresholds. */ +#define DEFAULT_RCV_STD_BD_REPLENISH_THRESHOLD 4 +#define DEFAULT_RCV_JUMBO_BD_REPLENISH_THRESHOLD 4 + +#define SPLIT_MODE_DISABLE 0 +#define SPLIT_MODE_ENABLE 1 + +#define SPLIT_MODE_5704_MAX_REQ 3 + +/* Maximum physical fragment size. */ +#define MAX_FRAGMENT_SIZE (64 * 1024) + +/* Standard view. */ +#define T3_STD_VIEW_SIZE (64 * 1024) +#define T3_FLAT_VIEW_SIZE (32 * 1024 * 1024) + +/* Buffer descriptor base address on the NIC's memory. */ + +#define T3_NIC_SND_BUFFER_DESC_ADDR 0x4000 +#define T3_NIC_STD_RCV_BUFFER_DESC_ADDR 0x6000 +#define T3_NIC_JUMBO_RCV_BUFFER_DESC_ADDR 0x7000 + +#define T3_NIC_STD_RCV_BUFFER_DESC_ADDR_EXT_MEM 0xc000 +#define T3_NIC_JUMBO_RCV_BUFFER_DESC_ADDR_EXT_MEM 0xd000 +#define T3_NIC_MINI_RCV_BUFFER_DESC_ADDR_EXT_MEM 0xe000 + +#define T3_NIC_SND_BUFFER_DESC_SIZE (T3_SEND_RCB_ENTRY_COUNT * \ + sizeof(T3_SND_BD) / 4) + +#define T3_NIC_STD_RCV_BUFFER_DESC_SIZE (T3_STD_RCV_RCB_ENTRY_COUNT * \ + sizeof(T3_RCV_BD) / 4) + +#define T3_NIC_JUMBO_RCV_BUFFER_DESC_SIZE (T3_JUMBO_RCV_RCB_ENTRY_COUNT * \ + sizeof(T3_EXT_RCV_BD) / 4) + +/* MBUF pool. */ +#define T3_NIC_MBUF_POOL_ADDR 0x8000 +/* #define T3_NIC_MBUF_POOL_SIZE 0x18000 */ +#define T3_NIC_MBUF_POOL_SIZE96 0x18000 +#define T3_NIC_MBUF_POOL_SIZE64 0x10000 + +#define T3_NIC_MBUF_POOL_ADDR_EXT_MEM 0x20000 + +/* DMA descriptor pool */ +#define T3_NIC_DMA_DESC_POOL_ADDR 0x2000 +#define T3_NIC_DMA_DESC_POOL_SIZE 0x2000 /* 8KB. */ + +#define T3_DEF_DMA_MBUF_LOW_WMARK 0x40 +#define T3_DEF_RX_MAC_MBUF_LOW_WMARK 0x20 +#define T3_DEF_MBUF_HIGH_WMARK 0x60 + +#define T3_DEF_DMA_MBUF_LOW_WMARK_JUMBO 304 +#define T3_DEF_RX_MAC_MBUF_LOW_WMARK_JUMBO 152 +#define T3_DEF_MBUF_HIGH_WMARK_JUMBO 380 + +#define T3_DEF_DMA_DESC_LOW_WMARK 5 +#define T3_DEF_DMA_DESC_HIGH_WMARK 10 + +/* Maximum size of giant TCP packet can be sent */ +#define T3_TCP_SEG_MAX_OFFLOAD_SIZE 64*1000 +#define T3_TCP_SEG_MIN_NUM_SEG 20 + +#define T3_RX_CPU_ID 0x1 +#define T3_TX_CPU_ID 0x2 +#define T3_RX_CPU_SPAD_ADDR 0x30000 +#define T3_RX_CPU_SPAD_SIZE 0x4000 +#define T3_TX_CPU_SPAD_ADDR 0x34000 +#define T3_TX_CPU_SPAD_SIZE 0x4000 + +typedef struct T3_DIR_ENTRY { + PLM_UINT8 Buffer; + LM_UINT32 Offset; + LM_UINT32 Length; +} T3_DIR_ENTRY, *PT3_DIR_ENTRY; + +typedef struct T3_FWIMG_INFO { + LM_UINT32 StartAddress; + T3_DIR_ENTRY Text; + T3_DIR_ENTRY ROnlyData; + T3_DIR_ENTRY Data; + T3_DIR_ENTRY Sbss; + T3_DIR_ENTRY Bss; +} T3_FWIMG_INFO, *PT3_FWIMG_INFO; + +/******************************************************************************/ +/* Tigon3 PCI Registers. */ +/******************************************************************************/ +#define T3_PCI_ID_BCM5700 0x164414e4 +#define T3_PCI_ID_BCM5701 0x164514e4 +#define T3_PCI_ID_BCM5702 0x164614e4 +#define T3_PCI_ID_BCM5702x 0x16A614e4 +#define T3_PCI_ID_BCM5703 0x164714e4 +#define T3_PCI_ID_BCM5703x 0x16A714e4 +#define T3_PCI_ID_BCM5702FE 0x164D14e4 +#define T3_PCI_ID_BCM5704 0x164814e4 + +#define T3_PCI_VENDOR_ID (T3_PCI_ID & 0xffff) +#define T3_PCI_DEVICE_ID (T3_PCI_ID >> 16) + +#define T3_PCI_MISC_HOST_CTRL_REG 0x68 + +/* The most significant 16bit of register 0x68. */ +/* ChipId:4, ChipRev:4, MetalRev:8 */ +#define T3_CHIP_ID_5700_A0 0x7000 +#define T3_CHIP_ID_5700_A1 0x7001 +#define T3_CHIP_ID_5700_B0 0x7100 +#define T3_CHIP_ID_5700_B1 0x7101 +#define T3_CHIP_ID_5700_C0 0x7200 + +#define T3_CHIP_ID_5701_A0 0x0000 +#define T3_CHIP_ID_5701_B0 0x0100 +#define T3_CHIP_ID_5701_B2 0x0102 +#define T3_CHIP_ID_5701_B5 0x0105 + +#define T3_CHIP_ID_5703_A0 0x1000 +#define T3_CHIP_ID_5703_A1 0x1001 +#define T3_CHIP_ID_5703_A2 0x1002 + +#define T3_CHIP_ID_5704_A0 0x2000 + +/* Chip Id. */ +#define T3_ASIC_REV(_ChipRevId) ((_ChipRevId) >> 12) +#define T3_ASIC_REV_5700 0x07 +#define T3_ASIC_REV_5701 0x00 +#define T3_ASIC_REV_5703 0x01 +#define T3_ASIC_REV_5704 0x02 + +/* Chip id and revision. */ +#define T3_CHIP_REV(_ChipRevId) ((_ChipRevId) >> 8) +#define T3_CHIP_REV_5700_AX 0x70 +#define T3_CHIP_REV_5700_BX 0x71 +#define T3_CHIP_REV_5700_CX 0x72 +#define T3_CHIP_REV_5701_AX 0x00 + +/* Metal revision. */ +#define T3_METAL_REV(_ChipRevId) ((_ChipRevId) & 0xff) +#define T3_METAL_REV_A0 0x00 +#define T3_METAL_REV_A1 0x01 +#define T3_METAL_REV_B0 0x00 +#define T3_METAL_REV_B1 0x01 +#define T3_METAL_REV_B2 0x02 + +#define T3_PCI_REG_CLOCK_CTRL 0x74 + +#define T3_PCI_DISABLE_RX_CLOCK BIT_10 +#define T3_PCI_DISABLE_TX_CLOCK BIT_11 +#define T3_PCI_SELECT_ALTERNATE_CLOCK BIT_12 +#define T3_PCI_POWER_DOWN_PCI_PLL133 BIT_15 +#define T3_PCI_44MHZ_CORE_CLOCK BIT_18 + +#define T3_PCI_REG_ADDR_REG 0x78 +#define T3_PCI_REG_DATA_REG 0x80 + +#define T3_PCI_MEM_WIN_ADDR_REG 0x7c +#define T3_PCI_MEM_WIN_DATA_REG 0x84 + +#define T3_PCI_PM_CAP_REG 0x48 + +#define T3_PCI_PM_CAP_PME_D3COLD BIT_31 +#define T3_PCI_PM_CAP_PME_D3HOT BIT_30 + +#define T3_PCI_PM_STATUS_CTRL_REG 0x4c + +#define T3_PM_POWER_STATE_MASK (BIT_0 | BIT_1) +#define T3_PM_POWER_STATE_D0 BIT_NONE +#define T3_PM_POWER_STATE_D1 BIT_0 +#define T3_PM_POWER_STATE_D2 BIT_1 +#define T3_PM_POWER_STATE_D3 (BIT_0 | BIT_1) + +#define T3_PM_PME_ENABLE BIT_8 +#define T3_PM_PME_ASSERTED BIT_15 + +/* PCI state register. */ +#define T3_PCI_STATE_REG 0x70 + +#define T3_PCI_STATE_FORCE_RESET BIT_0 +#define T3_PCI_STATE_INT_NOT_ACTIVE BIT_1 +#define T3_PCI_STATE_CONVENTIONAL_PCI_MODE BIT_2 +#define T3_PCI_STATE_BUS_SPEED_HIGH BIT_3 +#define T3_PCI_STATE_32BIT_PCI_BUS BIT_4 + +/* Broadcom subsystem/subvendor IDs. */ +#define T3_SVID_BROADCOM 0x14e4 + +#define T3_SSID_BROADCOM_BCM95700A6 0x1644 +#define T3_SSID_BROADCOM_BCM95701A5 0x0001 +#define T3_SSID_BROADCOM_BCM95700T6 0x0002 /* BCM8002 */ +#define T3_SSID_BROADCOM_BCM95700A9 0x0003 /* Agilent */ +#define T3_SSID_BROADCOM_BCM95701T1 0x0005 +#define T3_SSID_BROADCOM_BCM95701T8 0x0006 +#define T3_SSID_BROADCOM_BCM95701A7 0x0007 /* Agilent */ +#define T3_SSID_BROADCOM_BCM95701A10 0x0008 +#define T3_SSID_BROADCOM_BCM95701A12 0x8008 +#define T3_SSID_BROADCOM_BCM95703Ax1 0x0009 +#define T3_SSID_BROADCOM_BCM95703Ax2 0x8009 + +/* 3COM subsystem/subvendor IDs. */ +#define T3_SVID_3COM 0x10b7 + +#define T3_SSID_3COM_3C996T 0x1000 +#define T3_SSID_3COM_3C996BT 0x1006 +#define T3_SSID_3COM_3C996CT 0x1002 +#define T3_SSID_3COM_3C997T 0x1003 +#define T3_SSID_3COM_3C1000T 0x1007 +#define T3_SSID_3COM_3C940BR01 0x1008 + +/* Fiber boards. */ +#define T3_SSID_3COM_3C996SX 0x1004 +#define T3_SSID_3COM_3C997SX 0x1005 + +/* Dell subsystem/subvendor IDs. */ + +#define T3_SVID_DELL 0x1028 + +#define T3_SSID_DELL_VIPER 0x00d1 +#define T3_SSID_DELL_JAGUAR 0x0106 +#define T3_SSID_DELL_MERLOT 0x0109 +#define T3_SSID_DELL_SLIM_MERLOT 0x010a + +/* Compaq subsystem/subvendor IDs */ + +#define T3_SVID_COMPAQ 0x0e11 + +#define T3_SSID_COMPAQ_BANSHEE 0x007c +#define T3_SSID_COMPAQ_BANSHEE_2 0x009a +#define T3_SSID_COMPAQ_CHANGELING 0x007d +#define T3_SSID_COMPAQ_NC7780 0x0085 +#define T3_SSID_COMPAQ_NC7780_2 0x0099 + +/******************************************************************************/ +/* MII registers. */ +/******************************************************************************/ + +/* Control register. */ +#define PHY_CTRL_REG 0x00 + +#define PHY_CTRL_SPEED_MASK (BIT_6 | BIT_13) +#define PHY_CTRL_SPEED_SELECT_10MBPS BIT_NONE +#define PHY_CTRL_SPEED_SELECT_100MBPS BIT_13 +#define PHY_CTRL_SPEED_SELECT_1000MBPS BIT_6 +#define PHY_CTRL_COLLISION_TEST_ENABLE BIT_7 +#define PHY_CTRL_FULL_DUPLEX_MODE BIT_8 +#define PHY_CTRL_RESTART_AUTO_NEG BIT_9 +#define PHY_CTRL_ISOLATE_PHY BIT_10 +#define PHY_CTRL_LOWER_POWER_MODE BIT_11 +#define PHY_CTRL_AUTO_NEG_ENABLE BIT_12 +#define PHY_CTRL_LOOPBACK_MODE BIT_14 +#define PHY_CTRL_PHY_RESET BIT_15 + +/* Status register. */ +#define PHY_STATUS_REG 0x01 + +#define PHY_STATUS_LINK_PASS BIT_2 +#define PHY_STATUS_AUTO_NEG_COMPLETE BIT_5 + +/* Phy Id registers. */ +#define PHY_ID1_REG 0x02 +#define PHY_ID1_OUI_MASK 0xffff + +#define PHY_ID2_REG 0x03 +#define PHY_ID2_REV_MASK 0x000f +#define PHY_ID2_MODEL_MASK 0x03f0 +#define PHY_ID2_OUI_MASK 0xfc00 + +/* Auto-negotiation advertisement register. */ +#define PHY_AN_AD_REG 0x04 + +#define PHY_AN_AD_ASYM_PAUSE BIT_11 +#define PHY_AN_AD_PAUSE_CAPABLE BIT_10 +#define PHY_AN_AD_10BASET_HALF BIT_5 +#define PHY_AN_AD_10BASET_FULL BIT_6 +#define PHY_AN_AD_100BASETX_HALF BIT_7 +#define PHY_AN_AD_100BASETX_FULL BIT_8 +#define PHY_AN_AD_PROTOCOL_802_3_CSMA_CD 0x01 + +/* Auto-negotiation Link Partner Ability register. */ +#define PHY_LINK_PARTNER_ABILITY_REG 0x05 + +#define PHY_LINK_PARTNER_ASYM_PAUSE BIT_11 +#define PHY_LINK_PARTNER_PAUSE_CAPABLE BIT_10 + +/* Auto-negotiation expansion register. */ +#define PHY_AN_EXPANSION_REG 0x06 + +/******************************************************************************/ +/* BCM5400 and BCM5401 phy info. */ +/******************************************************************************/ + +#define PHY_DEVICE_ID 1 + +/* OUI: bit 31-10; Model#: bit 9-4; Rev# bit 3-0. */ +#define PHY_UNKNOWN_PHY 0x00000000 +#define PHY_BCM5400_PHY_ID 0x60008040 +#define PHY_BCM5401_PHY_ID 0x60008050 +#define PHY_BCM5411_PHY_ID 0x60008070 +#define PHY_BCM5701_PHY_ID 0x60008110 +#define PHY_BCM5703_PHY_ID 0x60008160 +#define PHY_BCM5704_PHY_ID 0x60008190 +#define PHY_BCM8002_PHY_ID 0x60010140 + +#define PHY_BCM5401_B0_REV 0x1 +#define PHY_BCM5401_B2_REV 0x3 +#define PHY_BCM5401_C0_REV 0x6 + +#define PHY_ID_OUI_MASK 0xfffffc00 +#define PHY_ID_MODEL_MASK 0x000003f0 +#define PHY_ID_REV_MASK 0x0000000f +#define PHY_ID_MASK (PHY_ID_OUI_MASK | \ + PHY_ID_MODEL_MASK) + +#define UNKNOWN_PHY_ID(x) ((((x) & PHY_ID_MASK) != PHY_BCM5400_PHY_ID) && \ + (((x) & PHY_ID_MASK) != PHY_BCM5401_PHY_ID) && \ + (((x) & PHY_ID_MASK) != PHY_BCM5411_PHY_ID) && \ + (((x) & PHY_ID_MASK) != PHY_BCM5701_PHY_ID) && \ + (((x) & PHY_ID_MASK) != PHY_BCM5703_PHY_ID) && \ + (((x) & PHY_ID_MASK) != PHY_BCM5704_PHY_ID) && \ + (((x) & PHY_ID_MASK) != PHY_BCM8002_PHY_ID)) + +/* 1000Base-T control register. */ +#define BCM540X_1000BASET_CTRL_REG 0x09 + +#define BCM540X_AN_AD_1000BASET_HALF BIT_8 +#define BCM540X_AN_AD_1000BASET_FULL BIT_9 +#define BCM540X_CONFIG_AS_MASTER BIT_11 +#define BCM540X_ENABLE_CONFIG_AS_MASTER BIT_12 + +/* Extended control register. */ +#define BCM540X_EXT_CTRL_REG 0x10 + +#define BCM540X_EXT_CTRL_LINK3_LED_MODE BIT_1 +#define BCM540X_EXT_CTRL_TBI BIT_15 + +/* PHY extended status register. */ +#define BCM540X_EXT_STATUS_REG 0x11 + +#define BCM540X_EXT_STATUS_LINK_PASS BIT_8 + +/* DSP Coefficient Read/Write Port. */ +#define BCM540X_DSP_RW_PORT 0x15 + +/* DSP Coeficient Address Register. */ +#define BCM540X_DSP_ADDRESS_REG 0x17 + +#define BCM540X_DSP_TAP_NUMBER_MASK 0x00 +#define BCM540X_DSP_AGC_A 0x00 +#define BCM540X_DSP_AGC_B 0x01 +#define BCM540X_DSP_MSE_PAIR_STATUS 0x02 +#define BCM540X_DSP_SOFT_DECISION 0x03 +#define BCM540X_DSP_PHASE_REG 0x04 +#define BCM540X_DSP_SKEW 0x05 +#define BCM540X_DSP_POWER_SAVER_UPPER_BOUND 0x06 +#define BCM540X_DSP_POWER_SAVER_LOWER_BOUND 0x07 +#define BCM540X_DSP_LAST_ECHO 0x08 +#define BCM540X_DSP_FREQUENCY 0x09 +#define BCM540X_DSP_PLL_BANDWIDTH 0x0a +#define BCM540X_DSP_PLL_PHASE_OFFSET 0x0b + +#define BCM540X_DSP_FILTER_DCOFFSET (BIT_10 | BIT_11) +#define BCM540X_DSP_FILTER_FEXT3 (BIT_8 | BIT_9 | BIT_11) +#define BCM540X_DSP_FILTER_FEXT2 (BIT_9 | BIT_11) +#define BCM540X_DSP_FILTER_FEXT1 (BIT_8 | BIT_11) +#define BCM540X_DSP_FILTER_FEXT0 BIT_11 +#define BCM540X_DSP_FILTER_NEXT3 (BIT_8 | BIT_9 | BIT_10) +#define BCM540X_DSP_FILTER_NEXT2 (BIT_9 | BIT_10) +#define BCM540X_DSP_FILTER_NEXT1 (BIT_8 | BIT_10) +#define BCM540X_DSP_FILTER_NEXT0 BIT_10 +#define BCM540X_DSP_FILTER_ECHO (BIT_8 | BIT_9) +#define BCM540X_DSP_FILTER_DFE BIT_9 +#define BCM540X_DSP_FILTER_FFE BIT_8 + +#define BCM540X_DSP_CONTROL_ALL_FILTERS BIT_12 + +#define BCM540X_DSP_SEL_CH_0 BIT_NONE +#define BCM540X_DSP_SEL_CH_1 BIT_13 +#define BCM540X_DSP_SEL_CH_2 BIT_14 +#define BCM540X_DSP_SEL_CH_3 (BIT_13 | BIT_14) + +#define BCM540X_CONTROL_ALL_CHANNELS BIT_15 + +/* Auxilliary Control Register (Shadow Register) */ +#define BCM5401_AUX_CTRL 0x18 + +#define BCM5401_SHADOW_SEL_MASK 0x7 +#define BCM5401_SHADOW_SEL_NORMAL 0x00 +#define BCM5401_SHADOW_SEL_10BASET 0x01 +#define BCM5401_SHADOW_SEL_POWER_CONTROL 0x02 +#define BCM5401_SHADOW_SEL_IP_PHONE 0x03 +#define BCM5401_SHADOW_SEL_MISC_TEST1 0x04 +#define BCM5401_SHADOW_SEL_MISC_TEST2 0x05 +#define BCM5401_SHADOW_SEL_IP_PHONE_SEED 0x06 + +/* Shadow register selector == '000' */ +#define BCM5401_SHDW_NORMAL_DIAG_MODE BIT_3 +#define BCM5401_SHDW_NORMAL_DISABLE_MBP BIT_4 +#define BCM5401_SHDW_NORMAL_DISABLE_LOW_PWR BIT_5 +#define BCM5401_SHDW_NORMAL_DISABLE_INV_PRF BIT_6 +#define BCM5401_SHDW_NORMAL_DISABLE_PRF BIT_7 +#define BCM5401_SHDW_NORMAL_RX_SLICING_NORMAL BIT_NONE +#define BCM5401_SHDW_NORMAL_RX_SLICING_4D BIT_8 +#define BCM5401_SHDW_NORMAL_RX_SLICING_3LVL_1D BIT_9 +#define BCM5401_SHDW_NORMAL_RX_SLICING_5LVL_1D (BIT_8 | BIT_9) +#define BCM5401_SHDW_NORMAL_TX_6DB_CODING BIT_10 +#define BCM5401_SHDW_NORMAL_ENABLE_SM_DSP_CLOCK BIT_11 +#define BCM5401_SHDW_NORMAL_EDGERATE_CTRL_4NS BIT_NONE +#define BCM5401_SHDW_NORMAL_EDGERATE_CTRL_5NS BIT_12 +#define BCM5401_SHDW_NORMAL_EDGERATE_CTRL_3NS BIT_13 +#define BCM5401_SHDW_NORMAL_EDGERATE_CTRL_0NS (BIT_12 | BIT_13) +#define BCM5401_SHDW_NORMAL_EXT_PACKET_LENGTH BIT_14 +#define BCM5401_SHDW_NORMAL_EXTERNAL_LOOPBACK BIT_15 + +/* Auxilliary status summary. */ +#define BCM540X_AUX_STATUS_REG 0x19 + +#define BCM540X_AUX_LINK_PASS BIT_2 +#define BCM540X_AUX_SPEED_MASK (BIT_8 | BIT_9 | BIT_10) +#define BCM540X_AUX_10BASET_HD BIT_8 +#define BCM540X_AUX_10BASET_FD BIT_9 +#define BCM540X_AUX_100BASETX_HD (BIT_8 | BIT_9) +#define BCM540X_AUX_100BASET4 BIT_10 +#define BCM540X_AUX_100BASETX_FD (BIT_8 | BIT_10) +#define BCM540X_AUX_100BASET_HD (BIT_9 | BIT_10) +#define BCM540X_AUX_100BASET_FD (BIT_8 | BIT_9 | BIT_10) + +/* Interrupt status. */ +#define BCM540X_INT_STATUS_REG 0x1a + +#define BCM540X_INT_LINK_CHANGE BIT_1 +#define BCM540X_INT_SPEED_CHANGE BIT_2 +#define BCM540X_INT_DUPLEX_CHANGE BIT_3 +#define BCM540X_INT_AUTO_NEG_PAGE_RX BIT_10 + +/* Interrupt mask register. */ +#define BCM540X_INT_MASK_REG 0x1b + +/******************************************************************************/ +/* Register definitions. */ +/******************************************************************************/ + +typedef volatile LM_UINT8 T3_8BIT_REGISTER, *PT3_8BIT_REGISTER; +typedef volatile LM_UINT16 T3_16BIT_REGISTER, *PT3_16BIT_REGISTER; +typedef volatile LM_UINT32 T3_32BIT_REGISTER, *PT3_32BIT_REGISTER; + +typedef struct { + /* Big endian format. */ + T3_32BIT_REGISTER High; + T3_32BIT_REGISTER Low; +} T3_64BIT_REGISTER, *PT3_64BIT_REGISTER; + +typedef T3_64BIT_REGISTER T3_64BIT_HOST_ADDR, *PT3_64BIT_HOST_ADDR; + +#define T3_NUM_OF_DMA_DESC 256 +#define T3_NUM_OF_MBUF 768 + +typedef struct { + T3_64BIT_REGISTER host_addr; + T3_32BIT_REGISTER nic_mbuf; + T3_16BIT_REGISTER len; + T3_16BIT_REGISTER cqid_sqid; + T3_32BIT_REGISTER flags; + T3_32BIT_REGISTER opaque1; + T3_32BIT_REGISTER opaque2; + T3_32BIT_REGISTER opaque3; +} T3_DMA_DESC, *PT3_DMA_DESC; + +/******************************************************************************/ +/* Ring control block. */ +/******************************************************************************/ + +typedef struct { + T3_64BIT_REGISTER HostRingAddr; + + union { + struct { +#ifdef BIG_ENDIAN_HOST + T3_16BIT_REGISTER MaxLen; + T3_16BIT_REGISTER Flags; +#else /* BIG_ENDIAN_HOST */ + T3_16BIT_REGISTER Flags; + T3_16BIT_REGISTER MaxLen; +#endif + } s; + + T3_32BIT_REGISTER MaxLen_Flags; + } u; + + T3_32BIT_REGISTER NicRingAddr; +} T3_RCB, *PT3_RCB; + +#define T3_RCB_FLAG_USE_EXT_RECV_BD BIT_0 +#define T3_RCB_FLAG_RING_DISABLED BIT_1 + +/******************************************************************************/ +/* Status block. */ +/******************************************************************************/ + +/* + * Size of status block is actually 0x50 bytes. Use 0x80 bytes for + * cache line alignment. + */ +#define T3_STATUS_BLOCK_SIZE 0x80 + +typedef struct { + volatile LM_UINT32 Status; +#define STATUS_BLOCK_UPDATED BIT_0 +#define STATUS_BLOCK_LINK_CHANGED_STATUS BIT_1 +#define STATUS_BLOCK_ERROR BIT_2 + + volatile LM_UINT32 StatusTag; + +#ifdef BIG_ENDIAN_HOST + volatile LM_UINT16 RcvStdConIdx; + volatile LM_UINT16 RcvJumboConIdx; + + volatile LM_UINT16 Reserved2; + volatile LM_UINT16 RcvMiniConIdx; + + struct { + volatile LM_UINT16 SendConIdx; /* Send consumer index. */ + volatile LM_UINT16 RcvProdIdx; /* Receive producer index. */ + } Idx[16]; +#else /* BIG_ENDIAN_HOST */ + volatile LM_UINT16 RcvJumboConIdx; + volatile LM_UINT16 RcvStdConIdx; + + volatile LM_UINT16 RcvMiniConIdx; + volatile LM_UINT16 Reserved2; + + struct { + volatile LM_UINT16 RcvProdIdx; /* Receive producer index. */ + volatile LM_UINT16 SendConIdx; /* Send consumer index. */ + } Idx[16]; +#endif +} T3_STATUS_BLOCK, *PT3_STATUS_BLOCK; + +/******************************************************************************/ +/* Receive buffer descriptors. */ +/******************************************************************************/ + +typedef struct { + T3_64BIT_HOST_ADDR HostAddr; + +#ifdef BIG_ENDIAN_HOST + volatile LM_UINT16 Index; + volatile LM_UINT16 Len; + + volatile LM_UINT16 Type; + volatile LM_UINT16 Flags; + + volatile LM_UINT16 IpCksum; + volatile LM_UINT16 TcpUdpCksum; + + volatile LM_UINT16 ErrorFlag; + volatile LM_UINT16 VlanTag; +#else /* BIG_ENDIAN_HOST */ + volatile LM_UINT16 Len; + volatile LM_UINT16 Index; + + volatile LM_UINT16 Flags; + volatile LM_UINT16 Type; + + volatile LM_UINT16 TcpUdpCksum; + volatile LM_UINT16 IpCksum; + + volatile LM_UINT16 VlanTag; + volatile LM_UINT16 ErrorFlag; +#endif + + volatile LM_UINT32 Reserved; + volatile LM_UINT32 Opaque; +} T3_RCV_BD, *PT3_RCV_BD; + +typedef struct { + T3_64BIT_HOST_ADDR HostAddr[3]; + +#ifdef BIG_ENDIAN_HOST + LM_UINT16 Len1; + LM_UINT16 Len2; + + LM_UINT16 Len3; + LM_UINT16 Reserved1; +#else /* BIG_ENDIAN_HOST */ + LM_UINT16 Len2; + LM_UINT16 Len1; + + LM_UINT16 Reserved1; + LM_UINT16 Len3; +#endif + + T3_RCV_BD StdRcvBd; +} T3_EXT_RCV_BD, *PT3_EXT_RCV_BD; + +/* Error flags. */ +#define RCV_BD_ERR_BAD_CRC 0x0001 +#define RCV_BD_ERR_COLL_DETECT 0x0002 +#define RCV_BD_ERR_LINK_LOST_DURING_PKT 0x0004 +#define RCV_BD_ERR_PHY_DECODE_ERR 0x0008 +#define RCV_BD_ERR_ODD_NIBBLED_RCVD_MII 0x0010 +#define RCV_BD_ERR_MAC_ABORT 0x0020 +#define RCV_BD_ERR_LEN_LT_64 0x0040 +#define RCV_BD_ERR_TRUNC_NO_RESOURCES 0x0080 +#define RCV_BD_ERR_GIANT_FRAME_RCVD 0x0100 + +/* Buffer descriptor flags. */ +#define RCV_BD_FLAG_END 0x0004 +#define RCV_BD_FLAG_JUMBO_RING 0x0020 +#define RCV_BD_FLAG_VLAN_TAG 0x0040 +#define RCV_BD_FLAG_FRAME_HAS_ERROR 0x0400 +#define RCV_BD_FLAG_MINI_RING 0x0800 +#define RCV_BD_FLAG_IP_CHKSUM_FIELD 0x1000 +#define RCV_BD_FLAG_TCP_UDP_CHKSUM_FIELD 0x2000 +#define RCV_BD_FLAG_TCP_PACKET 0x4000 + +/******************************************************************************/ +/* Send buffer descriptor. */ +/******************************************************************************/ + +typedef struct { + T3_64BIT_HOST_ADDR HostAddr; + + union { + struct { +#ifdef BIG_ENDIAN_HOST + LM_UINT16 Len; + LM_UINT16 Flags; +#else /* BIG_ENDIAN_HOST */ + LM_UINT16 Flags; + LM_UINT16 Len; +#endif + } s1; + + LM_UINT32 Len_Flags; + } u1; + + union { + struct { +#ifdef BIG_ENDIAN_HOST + LM_UINT16 Reserved; + LM_UINT16 VlanTag; +#else /* BIG_ENDIAN_HOST */ + LM_UINT16 VlanTag; + LM_UINT16 Reserved; +#endif + } s2; + + LM_UINT32 VlanTag; + } u2; +} T3_SND_BD, *PT3_SND_BD; + +/* Send buffer descriptor flags. */ +#define SND_BD_FLAG_TCP_UDP_CKSUM 0x0001 +#define SND_BD_FLAG_IP_CKSUM 0x0002 +#define SND_BD_FLAG_END 0x0004 +#define SND_BD_FLAG_IP_FRAG 0x0008 +#define SND_BD_FLAG_IP_FRAG_END 0x0010 +#define SND_BD_FLAG_VLAN_TAG 0x0040 +#define SND_BD_FLAG_COAL_NOW 0x0080 +#define SND_BD_FLAG_CPU_PRE_DMA 0x0100 +#define SND_BD_FLAG_CPU_POST_DMA 0x0200 +#define SND_BD_FLAG_INSERT_SRC_ADDR 0x1000 +#define SND_BD_FLAG_CHOOSE_SRC_ADDR 0x6000 +#define SND_BD_FLAG_DONT_GEN_CRC 0x8000 + +/* MBUFs */ +typedef struct T3_MBUF_FRAME_DESC { +#ifdef BIG_ENDIAN_HOST + LM_UINT32 status_control; + union { + struct { + LM_UINT8 cqid; + LM_UINT8 reserved1; + LM_UINT16 length; + } s1; + LM_UINT32 word; + } u1; + union { + struct { + LM_UINT16 ip_hdr_start; + LM_UINT16 tcp_udp_hdr_start; + } s2; + + LM_UINT32 word; + } u2; + + union { + struct { + LM_UINT16 data_start; + LM_UINT16 vlan_id; + } s3; + + LM_UINT32 word; + } u3; + + union { + struct { + LM_UINT16 ip_checksum; + LM_UINT16 tcp_udp_checksum; + } s4; + + LM_UINT32 word; + } u4; + + union { + struct { + LM_UINT16 pseudo_checksum; + LM_UINT16 checksum_status; + } s5; + + LM_UINT32 word; + } u5; + + union { + struct { + LM_UINT16 rule_match; + LM_UINT8 class; + LM_UINT8 rupt; + } s6; + + LM_UINT32 word; + } u6; + + union { + struct { + LM_UINT16 reserved2; + LM_UINT16 mbuf_num; + } s7; + + LM_UINT32 word; + } u7; + + LM_UINT32 reserved3; + LM_UINT32 reserved4; +#else + LM_UINT32 status_control; + union { + struct { + LM_UINT16 length; + LM_UINT8 reserved1; + LM_UINT8 cqid; + } s1; + LM_UINT32 word; + } u1; + union { + struct { + LM_UINT16 tcp_udp_hdr_start; + LM_UINT16 ip_hdr_start; + } s2; + + LM_UINT32 word; + } u2; + + union { + struct { + LM_UINT16 vlan_id; + LM_UINT16 data_start; + } s3; + + LM_UINT32 word; + } u3; + + union { + struct { + LM_UINT16 tcp_udp_checksum; + LM_UINT16 ip_checksum; + } s4; + + LM_UINT32 word; + } u4; + + union { + struct { + LM_UINT16 checksum_status; + LM_UINT16 pseudo_checksum; + } s5; + + LM_UINT32 word; + } u5; + + union { + struct { + LM_UINT8 rupt; + LM_UINT8 class; + LM_UINT16 rule_match; + } s6; + + LM_UINT32 word; + } u6; + + union { + struct { + LM_UINT16 mbuf_num; + LM_UINT16 reserved2; + } s7; + + LM_UINT32 word; + } u7; + + LM_UINT32 reserved3; + LM_UINT32 reserved4; +#endif +} T3_MBUF_FRAME_DESC, *PT3_MBUF_FRAME_DESC; + +typedef struct T3_MBUF_HDR { + union { + struct { + unsigned int C:1; + unsigned int F:1; + unsigned int reserved1:7; + unsigned int next_mbuf:16; + unsigned int length:7; + } s1; + + LM_UINT32 word; + } u1; + + LM_UINT32 next_frame_ptr; +} T3_MBUF_HDR, *PT3_MBUF_HDR; + +typedef struct T3_MBUF { + T3_MBUF_HDR hdr; + union { + struct { + T3_MBUF_FRAME_DESC frame_hdr; + LM_UINT32 data[20]; + } s1; + + struct { + LM_UINT32 data[30]; + } s2; + } body; +} T3_MBUF, *PT3_MBUF; + +#define T3_MBUF_BASE (T3_NIC_MBUF_POOL_ADDR >> 7) +#define T3_MBUF_END ((T3_NIC_MBUF_POOL_ADDR + T3_NIC_MBUF_POOL_SIZE) >> 7) + +/******************************************************************************/ +/* Statistics block. */ +/******************************************************************************/ + +typedef struct { + LM_UINT8 Reserved0[0x400 - 0x300]; + + /* Statistics maintained by Receive MAC. */ + T3_64BIT_REGISTER ifHCInOctets; + T3_64BIT_REGISTER Reserved1; + T3_64BIT_REGISTER etherStatsFragments; + T3_64BIT_REGISTER ifHCInUcastPkts; + T3_64BIT_REGISTER ifHCInMulticastPkts; + T3_64BIT_REGISTER ifHCInBroadcastPkts; + T3_64BIT_REGISTER dot3StatsFCSErrors; + T3_64BIT_REGISTER dot3StatsAlignmentErrors; + T3_64BIT_REGISTER xonPauseFramesReceived; + T3_64BIT_REGISTER xoffPauseFramesReceived; + T3_64BIT_REGISTER macControlFramesReceived; + T3_64BIT_REGISTER xoffStateEntered; + T3_64BIT_REGISTER dot3StatsFramesTooLong; + T3_64BIT_REGISTER etherStatsJabbers; + T3_64BIT_REGISTER etherStatsUndersizePkts; + T3_64BIT_REGISTER inRangeLengthError; + T3_64BIT_REGISTER outRangeLengthError; + T3_64BIT_REGISTER etherStatsPkts64Octets; + T3_64BIT_REGISTER etherStatsPkts65Octetsto127Octets; + T3_64BIT_REGISTER etherStatsPkts128Octetsto255Octets; + T3_64BIT_REGISTER etherStatsPkts256Octetsto511Octets; + T3_64BIT_REGISTER etherStatsPkts512Octetsto1023Octets; + T3_64BIT_REGISTER etherStatsPkts1024Octetsto1522Octets; + T3_64BIT_REGISTER etherStatsPkts1523Octetsto2047Octets; + T3_64BIT_REGISTER etherStatsPkts2048Octetsto4095Octets; + T3_64BIT_REGISTER etherStatsPkts4096Octetsto8191Octets; + T3_64BIT_REGISTER etherStatsPkts8192Octetsto9022Octets; + + T3_64BIT_REGISTER Unused1[37]; + + /* Statistics maintained by Transmit MAC. */ + T3_64BIT_REGISTER ifHCOutOctets; + T3_64BIT_REGISTER Reserved2; + T3_64BIT_REGISTER etherStatsCollisions; + T3_64BIT_REGISTER outXonSent; + T3_64BIT_REGISTER outXoffSent; + T3_64BIT_REGISTER flowControlDone; + T3_64BIT_REGISTER dot3StatsInternalMacTransmitErrors; + T3_64BIT_REGISTER dot3StatsSingleCollisionFrames; + T3_64BIT_REGISTER dot3StatsMultipleCollisionFrames; + T3_64BIT_REGISTER dot3StatsDeferredTransmissions; + T3_64BIT_REGISTER Reserved3; + T3_64BIT_REGISTER dot3StatsExcessiveCollisions; + T3_64BIT_REGISTER dot3StatsLateCollisions; + T3_64BIT_REGISTER dot3Collided2Times; + T3_64BIT_REGISTER dot3Collided3Times; + T3_64BIT_REGISTER dot3Collided4Times; + T3_64BIT_REGISTER dot3Collided5Times; + T3_64BIT_REGISTER dot3Collided6Times; + T3_64BIT_REGISTER dot3Collided7Times; + T3_64BIT_REGISTER dot3Collided8Times; + T3_64BIT_REGISTER dot3Collided9Times; + T3_64BIT_REGISTER dot3Collided10Times; + T3_64BIT_REGISTER dot3Collided11Times; + T3_64BIT_REGISTER dot3Collided12Times; + T3_64BIT_REGISTER dot3Collided13Times; + T3_64BIT_REGISTER dot3Collided14Times; + T3_64BIT_REGISTER dot3Collided15Times; + T3_64BIT_REGISTER ifHCOutUcastPkts; + T3_64BIT_REGISTER ifHCOutMulticastPkts; + T3_64BIT_REGISTER ifHCOutBroadcastPkts; + T3_64BIT_REGISTER dot3StatsCarrierSenseErrors; + T3_64BIT_REGISTER ifOutDiscards; + T3_64BIT_REGISTER ifOutErrors; + + T3_64BIT_REGISTER Unused2[31]; + + /* Statistics maintained by Receive List Placement. */ + T3_64BIT_REGISTER COSIfHCInPkts[16]; + T3_64BIT_REGISTER COSFramesDroppedDueToFilters; + T3_64BIT_REGISTER nicDmaWriteQueueFull; + T3_64BIT_REGISTER nicDmaWriteHighPriQueueFull; + T3_64BIT_REGISTER nicNoMoreRxBDs; + T3_64BIT_REGISTER ifInDiscards; + T3_64BIT_REGISTER ifInErrors; + T3_64BIT_REGISTER nicRecvThresholdHit; + + T3_64BIT_REGISTER Unused3[9]; + + /* Statistics maintained by Send Data Initiator. */ + T3_64BIT_REGISTER COSIfHCOutPkts[16]; + T3_64BIT_REGISTER nicDmaReadQueueFull; + T3_64BIT_REGISTER nicDmaReadHighPriQueueFull; + T3_64BIT_REGISTER nicSendDataCompQueueFull; + + /* Statistics maintained by Host Coalescing. */ + T3_64BIT_REGISTER nicRingSetSendProdIndex; + T3_64BIT_REGISTER nicRingStatusUpdate; + T3_64BIT_REGISTER nicInterrupts; + T3_64BIT_REGISTER nicAvoidedInterrupts; + T3_64BIT_REGISTER nicSendThresholdHit; + + LM_UINT8 Reserved4[0xb00 - 0x9c0]; +} T3_STATS_BLOCK, *PT3_STATS_BLOCK; + +/******************************************************************************/ +/* PCI configuration registers. */ +/******************************************************************************/ + +typedef struct { + T3_16BIT_REGISTER VendorId; + T3_16BIT_REGISTER DeviceId; + + T3_16BIT_REGISTER Command; + T3_16BIT_REGISTER Status; + + T3_32BIT_REGISTER ClassCodeRevId; + + T3_8BIT_REGISTER CacheLineSize; + T3_8BIT_REGISTER LatencyTimer; + T3_8BIT_REGISTER HeaderType; + T3_8BIT_REGISTER Bist; + + T3_32BIT_REGISTER MemBaseAddrLow; + T3_32BIT_REGISTER MemBaseAddrHigh; + + LM_UINT8 Unused1[20]; + + T3_16BIT_REGISTER SubsystemVendorId; + T3_16BIT_REGISTER SubsystemId; + + T3_32BIT_REGISTER RomBaseAddr; + + T3_8BIT_REGISTER PciXCapiblityPtr; + LM_UINT8 Unused2[7]; + + T3_8BIT_REGISTER IntLine; + T3_8BIT_REGISTER IntPin; + T3_8BIT_REGISTER MinGnt; + T3_8BIT_REGISTER MaxLat; + + T3_8BIT_REGISTER PciXCapabilities; + T3_8BIT_REGISTER PmCapabilityPtr; + T3_16BIT_REGISTER PciXCommand; + + T3_32BIT_REGISTER PciXStatus; + + T3_8BIT_REGISTER PmCapabilityId; + T3_8BIT_REGISTER VpdCapabilityPtr; + T3_16BIT_REGISTER PmCapabilities; + + T3_16BIT_REGISTER PmCtrlStatus; +#define PM_CTRL_PME_STATUS BIT_15 +#define PM_CTRL_PME_ENABLE BIT_8 +#define PM_CTRL_PME_POWER_STATE_D0 0 +#define PM_CTRL_PME_POWER_STATE_D1 1 +#define PM_CTRL_PME_POWER_STATE_D2 2 +#define PM_CTRL_PME_POWER_STATE_D3H 3 + + T3_8BIT_REGISTER BridgeSupportExt; + T3_8BIT_REGISTER PmData; + + T3_8BIT_REGISTER VpdCapabilityId; + T3_8BIT_REGISTER MsiCapabilityPtr; + T3_16BIT_REGISTER VpdAddrFlag; +#define VPD_FLAG_WRITE (1 << 15) +#define VPD_FLAG_RW_MASK (1 << 15) +#define VPD_FLAG_READ 0 + + T3_32BIT_REGISTER VpdData; + + T3_8BIT_REGISTER MsiCapabilityId; + T3_8BIT_REGISTER NextCapabilityPtr; + T3_16BIT_REGISTER MsiCtrl; +#define MSI_CTRL_64BIT_CAP (1 << 7) +#define MSI_CTRL_MSG_ENABLE(x) (x << 4) +#define MSI_CTRL_MSG_CAP(x) (x << 1) +#define MSI_CTRL_ENABLE (1 << 0) + + T3_32BIT_REGISTER MsiAddrLow; + T3_32BIT_REGISTER MsiAddrHigh; + + T3_16BIT_REGISTER MsiData; + T3_16BIT_REGISTER Unused3; + + T3_32BIT_REGISTER MiscHostCtrl; +#define MISC_HOST_CTRL_CLEAR_INT BIT_0 +#define MISC_HOST_CTRL_MASK_PCI_INT BIT_1 +#define MISC_HOST_CTRL_ENABLE_ENDIAN_BYTE_SWAP BIT_2 +#define MISC_HOST_CTRL_ENABLE_ENDIAN_WORD_SWAP BIT_3 +#define MISC_HOST_CTRL_ENABLE_PCI_STATE_REG_RW BIT_4 +#define MISC_HOST_CTRL_ENABLE_CLK_REG_RW BIT_5 +#define MISC_HOST_CTRL_ENABLE_REG_WORD_SWAP BIT_6 +#define MISC_HOST_CTRL_ENABLE_INDIRECT_ACCESS BIT_7 +#define MISC_HOST_CTRL_ENABLE_INT_MASK_MODE BIT_8 +#define MISC_HOST_CTRL_ENABLE_TAGGED_STATUS_MODE BIT_9 + + T3_32BIT_REGISTER DmaReadWriteCtrl; +#define DMA_CTRL_WRITE_BOUNDARY_MASK (BIT_11 | BIT_12 | BIT_13) +#define DMA_CTRL_WRITE_BOUNDARY_DISABLE 0 +#define DMA_CTRL_WRITE_BOUNDARY_16 BIT_11 +#define DMA_CTRL_WRITE_BOUNDARY_32 BIT_12 +#define DMA_CTRL_WRITE_BOUNDARY_64 (BIT_12 | BIT_11) +#define DMA_CTRL_WRITE_BOUNDARY_128 BIT_13 +#define DMA_CTRL_WRITE_BOUNDARY_256 (BIT_13 | BIT_11) +#define DMA_CTRL_WRITE_BOUNDARY_512 (BIT_13 | BIT_12) +#define DMA_CTRL_WRITE_BOUNDARY_1024 (BIT_13 | BIT_12 | BIT_11) +#define DMA_CTRL_WRITE_ONE_DMA_AT_ONCE BIT_14 + + T3_32BIT_REGISTER PciState; +#define T3_PCI_STATE_FORCE_PCI_RESET BIT_0 +#define T3_PCI_STATE_INTERRUPT_NOT_ACTIVE BIT_1 +#define T3_PCI_STATE_NOT_PCI_X_BUS BIT_2 +#define T3_PCI_STATE_HIGH_BUS_SPEED BIT_3 +#define T3_PCI_STATE_32BIT_PCI_BUS BIT_4 +#define T3_PCI_STATE_PCI_ROM_ENABLE BIT_5 +#define T3_PCI_STATE_PCI_ROM_RETRY_ENABLE BIT_6 +#define T3_PCI_STATE_FLAT_VIEW BIT_8 +#define T3_PCI_STATE_RETRY_SAME_DMA BIT_13 + + T3_32BIT_REGISTER ClockCtrl; +#define T3_PCI_CLKCTRL_TXCPU_CLK_DISABLE BIT_11 +#define T3_PCI_CLKCTRL_RXCPU_CLK_DISABLE BIT_10 +#define T3_PCI_CLKCTRL_CORE_CLK_DISABLE BIT_9 + + T3_32BIT_REGISTER RegBaseAddr; + + T3_32BIT_REGISTER MemWindowBaseAddr; + +#ifdef NIC_CPU_VIEW + /* These registers are ONLY visible to NIC CPU */ + T3_32BIT_REGISTER PowerConsumed; + T3_32BIT_REGISTER PowerDissipated; +#else /* NIC_CPU_VIEW */ + T3_32BIT_REGISTER RegData; + T3_32BIT_REGISTER MemWindowData; +#endif /* !NIC_CPU_VIEW */ + + T3_32BIT_REGISTER ModeCtrl; + + T3_32BIT_REGISTER MiscCfg; + + T3_32BIT_REGISTER MiscLocalCtrl; + + T3_32BIT_REGISTER Unused4; + + /* NOTE: Big/Little-endian clarification needed. Are these register */ + /* in big or little endian formate. */ + T3_64BIT_REGISTER StdRingProdIdx; + T3_64BIT_REGISTER RcvRetRingConIdx; + T3_64BIT_REGISTER SndProdIdx; + + LM_UINT8 Unused5[80]; +} T3_PCI_CONFIGURATION, *PT3_PCI_CONFIGURATION; + +#define PCIX_CMD_MAX_SPLIT_MASK 0x0070 +#define PCIX_CMD_MAX_SPLIT_SHL 4 +#define PCIX_CMD_MAX_BURST_MASK 0x000c +#define PCIX_CMD_MAX_BURST_SHL 2 +#define PCIX_CMD_MAX_BURST_CPIOB 2 + +/******************************************************************************/ +/* Mac control registers. */ +/******************************************************************************/ + +typedef struct { + /* MAC mode control. */ + T3_32BIT_REGISTER Mode; +#define MAC_MODE_GLOBAL_RESET BIT_0 +#define MAC_MODE_HALF_DUPLEX BIT_1 +#define MAC_MODE_PORT_MODE_MASK (BIT_2 | BIT_3) +#define MAC_MODE_PORT_MODE_TBI (BIT_2 | BIT_3) +#define MAC_MODE_PORT_MODE_GMII BIT_3 +#define MAC_MODE_PORT_MODE_MII BIT_2 +#define MAC_MODE_PORT_MODE_NONE BIT_NONE +#define MAC_MODE_PORT_INTERNAL_LOOPBACK BIT_4 +#define MAC_MODE_TAGGED_MAC_CONTROL BIT_7 +#define MAC_MODE_TX_BURSTING BIT_8 +#define MAC_MODE_MAX_DEFER BIT_9 +#define MAC_MODE_LINK_POLARITY BIT_10 +#define MAC_MODE_ENABLE_RX_STATISTICS BIT_11 +#define MAC_MODE_CLEAR_RX_STATISTICS BIT_12 +#define MAC_MODE_FLUSH_RX_STATISTICS BIT_13 +#define MAC_MODE_ENABLE_TX_STATISTICS BIT_14 +#define MAC_MODE_CLEAR_TX_STATISTICS BIT_15 +#define MAC_MODE_FLUSH_TX_STATISTICS BIT_16 +#define MAC_MODE_SEND_CONFIGS BIT_17 +#define MAC_MODE_DETECT_MAGIC_PACKET_ENABLE BIT_18 +#define MAC_MODE_ACPI_POWER_ON_ENABLE BIT_19 +#define MAC_MODE_ENABLE_MIP BIT_20 +#define MAC_MODE_ENABLE_TDE BIT_21 +#define MAC_MODE_ENABLE_RDE BIT_22 +#define MAC_MODE_ENABLE_FHDE BIT_23 + + /* MAC status */ + T3_32BIT_REGISTER Status; +#define MAC_STATUS_PCS_SYNCED BIT_0 +#define MAC_STATUS_SIGNAL_DETECTED BIT_1 +#define MAC_STATUS_RECEIVING_CFG BIT_2 +#define MAC_STATUS_CFG_CHANGED BIT_3 +#define MAC_STATUS_SYNC_CHANGED BIT_4 +#define MAC_STATUS_PORT_DECODE_ERROR BIT_10 +#define MAC_STATUS_LINK_STATE_CHANGED BIT_12 +#define MAC_STATUS_MI_COMPLETION BIT_22 +#define MAC_STATUS_MI_INTERRUPT BIT_23 +#define MAC_STATUS_AP_ERROR BIT_24 +#define MAC_STATUS_ODI_ERROR BIT_25 +#define MAC_STATUS_RX_STATS_OVERRUN BIT_26 +#define MAC_STATUS_TX_STATS_OVERRUN BIT_27 + + /* Event Enable */ + T3_32BIT_REGISTER MacEvent; +#define MAC_EVENT_ENABLE_PORT_DECODE_ERR BIT_10 +#define MAC_EVENT_ENABLE_LINK_STATE_CHANGED_ATTN BIT_12 +#define MAC_EVENT_ENABLE_MI_COMPLETION BIT_22 +#define MAC_EVENT_ENABLE_MI_INTERRUPT BIT_23 +#define MAC_EVENT_ENABLE_AP_ERROR BIT_24 +#define MAC_EVENT_ENABLE_ODI_ERROR BIT_25 +#define MAC_EVENT_ENABLE_RX_STATS_OVERRUN BIT_26 +#define MAC_EVENT_ENABLE_TX_STATS_OVERRUN BIT_27 + + /* Led control. */ + T3_32BIT_REGISTER LedCtrl; +#define LED_CTRL_OVERRIDE_LINK_LED BIT_0 +#define LED_CTRL_1000MBPS_LED_ON BIT_1 +#define LED_CTRL_100MBPS_LED_ON BIT_2 +#define LED_CTRL_10MBPS_LED_ON BIT_3 +#define LED_CTRL_OVERRIDE_TRAFFIC_LED BIT_4 +#define LED_CTRL_BLINK_TRAFFIC_LED BIT_5 +#define LED_CTRL_TRAFFIC_LED BIT_6 +#define LED_CTRL_1000MBPS_LED_STATUS BIT_7 +#define LED_CTRL_100MBPS_LED_STATUS BIT_8 +#define LED_CTRL_10MBPS_LED_STATUS BIT_9 +#define LED_CTRL_TRAFFIC_LED_STATUS BIT_10 +#define LED_CTRL_MAC_MODE BIT_NONE +#define LED_CTRL_PHY_MODE_1 BIT_11 +#define LED_CTRL_PHY_MODE_2 BIT_12 +#define LED_CTRL_BLINK_RATE_MASK 0x7ff80000 +#define LED_CTRL_OVERRIDE_BLINK_PERIOD BIT_19 +#define LED_CTRL_OVERRIDE_BLINK_RATE BIT_31 + + /* MAC addresses. */ + struct { + T3_32BIT_REGISTER High; /* Upper 2 bytes. */ + T3_32BIT_REGISTER Low; /* Lower 4 bytes. */ + } MacAddr[4]; + + /* ACPI Mbuf pointer. */ + T3_32BIT_REGISTER AcpiMbufPtr; + + /* ACPI Length and Offset. */ + T3_32BIT_REGISTER AcpiLengthOffset; +#define ACPI_LENGTH_MASK 0xffff +#define ACPI_OFFSET_MASK 0x0fff0000 +#define ACPI_LENGTH(x) x +#define ACPI_OFFSET(x) ((x) << 16) + + /* Transmit random backoff. */ + T3_32BIT_REGISTER TxBackoffSeed; +#define MAC_TX_BACKOFF_SEED_MASK 0x3ff + + /* Receive MTU */ + T3_32BIT_REGISTER MtuSize; +#define MAC_RX_MTU_MASK 0xffff + + /* Gigabit PCS Test. */ + T3_32BIT_REGISTER PcsTest; +#define MAC_PCS_TEST_DATA_PATTERN_MASK 0x0fffff +#define MAC_PCS_TEST_ENABLE BIT_20 + + /* Transmit Gigabit Auto-Negotiation. */ + T3_32BIT_REGISTER TxAutoNeg; +#define MAC_AN_TX_AN_DATA_MASK 0xffff + + /* Receive Gigabit Auto-Negotiation. */ + T3_32BIT_REGISTER RxAutoNeg; +#define MAC_AN_RX_AN_DATA_MASK 0xffff + + /* MI Communication. */ + T3_32BIT_REGISTER MiCom; +#define MI_COM_CMD_MASK (BIT_26 | BIT_27) +#define MI_COM_CMD_WRITE BIT_26 +#define MI_COM_CMD_READ BIT_27 +#define MI_COM_READ_FAILED BIT_28 +#define MI_COM_START BIT_29 +#define MI_COM_BUSY BIT_29 + +#define MI_COM_PHY_ADDR_MASK 0x1f +#define MI_COM_FIRST_PHY_ADDR_BIT 21 + +#define MI_COM_PHY_REG_ADDR_MASK 0x1f +#define MI_COM_FIRST_PHY_REG_ADDR_BIT 16 + +#define MI_COM_PHY_DATA_MASK 0xffff + + /* MI Status. */ + T3_32BIT_REGISTER MiStatus; +#define MI_STATUS_ENABLE_LINK_STATUS_ATTN BIT_0 + + /* MI Mode. */ + T3_32BIT_REGISTER MiMode; +#define MI_MODE_CLOCK_SPEED_10MHZ BIT_0 +#define MI_MODE_USE_SHORT_PREAMBLE BIT_1 +#define MI_MODE_AUTO_POLLING_ENABLE BIT_4 +#define MI_MODE_CORE_CLOCK_SPEED_62MHZ BIT_15 + + /* Auto-polling status. */ + T3_32BIT_REGISTER AutoPollStatus; +#define AUTO_POLL_ERROR BIT_0 + + /* Transmit MAC mode. */ + T3_32BIT_REGISTER TxMode; +#define TX_MODE_RESET BIT_0 +#define TX_MODE_ENABLE BIT_1 +#define TX_MODE_ENABLE_FLOW_CONTROL BIT_4 +#define TX_MODE_ENABLE_BIG_BACKOFF BIT_5 +#define TX_MODE_ENABLE_LONG_PAUSE BIT_6 + + /* Transmit MAC status. */ + T3_32BIT_REGISTER TxStatus; +#define TX_STATUS_RX_CURRENTLY_XOFFED BIT_0 +#define TX_STATUS_SENT_XOFF BIT_1 +#define TX_STATUS_SENT_XON BIT_2 +#define TX_STATUS_LINK_UP BIT_3 +#define TX_STATUS_ODI_UNDERRUN BIT_4 +#define TX_STATUS_ODI_OVERRUN BIT_5 + + /* Transmit MAC length. */ + T3_32BIT_REGISTER TxLengths; +#define TX_LEN_SLOT_TIME_MASK 0xff +#define TX_LEN_IPG_MASK 0x0f00 +#define TX_LEN_IPG_CRS_MASK (BIT_12 | BIT_13) + + /* Receive MAC mode. */ + T3_32BIT_REGISTER RxMode; +#define RX_MODE_RESET BIT_0 +#define RX_MODE_ENABLE BIT_1 +#define RX_MODE_ENABLE_FLOW_CONTROL BIT_2 +#define RX_MODE_KEEP_MAC_CONTROL BIT_3 +#define RX_MODE_KEEP_PAUSE BIT_4 +#define RX_MODE_ACCEPT_OVERSIZED BIT_5 +#define RX_MODE_ACCEPT_RUNTS BIT_6 +#define RX_MODE_LENGTH_CHECK BIT_7 +#define RX_MODE_PROMISCUOUS_MODE BIT_8 +#define RX_MODE_NO_CRC_CHECK BIT_9 +#define RX_MODE_KEEP_VLAN_TAG BIT_10 + + /* Receive MAC status. */ + T3_32BIT_REGISTER RxStatus; +#define RX_STATUS_REMOTE_TRANSMITTER_XOFFED BIT_0 +#define RX_STATUS_XOFF_RECEIVED BIT_1 +#define RX_STATUS_XON_RECEIVED BIT_2 + + /* Hash registers. */ + T3_32BIT_REGISTER HashReg[4]; + + /* Receive placement rules registers. */ + struct { + T3_32BIT_REGISTER Rule; + T3_32BIT_REGISTER Value; + } RcvRules[16]; + +#define RCV_DISABLE_RULE_MASK 0x7fffffff + +#define RCV_RULE1_REJECT_BROADCAST_IDX 0x00 +#define REJECT_BROADCAST_RULE1_RULE 0xc2000000 +#define REJECT_BROADCAST_RULE1_VALUE 0xffffffff + +#define RCV_RULE2_REJECT_BROADCAST_IDX 0x01 +#define REJECT_BROADCAST_RULE2_RULE 0x86000004 +#define REJECT_BROADCAST_RULE2_VALUE 0xffffffff + +#if INCLUDE_5701_AX_FIX +#define RCV_LAST_RULE_IDX 0x04 +#else +#define RCV_LAST_RULE_IDX 0x02 +#endif + + T3_32BIT_REGISTER RcvRuleCfg; +#define RX_RULE_DEFAULT_CLASS (1 << 3) + + LM_UINT8 Reserved1[140]; + + T3_32BIT_REGISTER SerdesCfg; + T3_32BIT_REGISTER SerdesStatus; + + LM_UINT8 Reserved2[104]; + + volatile LM_UINT8 TxMacState[16]; + volatile LM_UINT8 RxMacState[20]; + + LM_UINT8 Reserved3[476]; + + T3_32BIT_REGISTER RxStats[26]; + + LM_UINT8 Reserved4[24]; + + T3_32BIT_REGISTER TxStats[28]; + + LM_UINT8 Reserved5[784]; +} T3_MAC_CONTROL, *PT3_MAC_CONTROL; + +/******************************************************************************/ +/* Send data initiator control registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define T3_SND_DATA_IN_MODE_RESET BIT_0 +#define T3_SND_DATA_IN_MODE_ENABLE BIT_1 +#define T3_SND_DATA_IN_MODE_STATS_OFLW_ATTN_ENABLE BIT_2 + + T3_32BIT_REGISTER Status; +#define T3_SND_DATA_IN_STATUS_STATS_OFLW_ATTN BIT_2 + + T3_32BIT_REGISTER StatsCtrl; +#define T3_SND_DATA_IN_STATS_CTRL_ENABLE BIT_0 +#define T3_SND_DATA_IN_STATS_CTRL_FASTER_UPDATE BIT_1 +#define T3_SND_DATA_IN_STATS_CTRL_CLEAR BIT_2 +#define T3_SND_DATA_IN_STATS_CTRL_FLUSH BIT_3 +#define T3_SND_DATA_IN_STATS_CTRL_FORCE_ZERO BIT_4 + + T3_32BIT_REGISTER StatsEnableMask; + T3_32BIT_REGISTER StatsIncMask; + + LM_UINT8 Reserved[108]; + + T3_32BIT_REGISTER ClassOfServCnt[16]; + T3_32BIT_REGISTER DmaReadQFullCnt; + T3_32BIT_REGISTER DmaPriorityReadQFullCnt; + T3_32BIT_REGISTER SdcQFullCnt; + + T3_32BIT_REGISTER NicRingSetSendProdIdxCnt; + T3_32BIT_REGISTER StatusUpdatedCnt; + T3_32BIT_REGISTER InterruptsCnt; + T3_32BIT_REGISTER AvoidInterruptsCnt; + T3_32BIT_REGISTER SendThresholdHitCnt; + + /* Unused space. */ + LM_UINT8 Unused[800]; +} T3_SEND_DATA_INITIATOR, *PT3_SEND_DATA_INITIATOR; + +/******************************************************************************/ +/* Send data completion control registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define SND_DATA_COMP_MODE_RESET BIT_0 +#define SND_DATA_COMP_MODE_ENABLE BIT_1 + + /* Unused space. */ + LM_UINT8 Unused[1020]; +} T3_SEND_DATA_COMPLETION, *PT3_SEND_DATA_COMPLETION; + +/******************************************************************************/ +/* Send BD Ring Selector Control Registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define SND_BD_SEL_MODE_RESET BIT_0 +#define SND_BD_SEL_MODE_ENABLE BIT_1 +#define SND_BD_SEL_MODE_ATTN_ENABLE BIT_2 + + T3_32BIT_REGISTER Status; +#define SND_BD_SEL_STATUS_ERROR_ATTN BIT_2 + + T3_32BIT_REGISTER HwDiag; + + /* Unused space. */ + LM_UINT8 Unused1[52]; + + /* Send BD Ring Selector Local NIC Send BD Consumer Index. */ + T3_32BIT_REGISTER NicSendBdSelConIdx[16]; + + /* Unused space. */ + LM_UINT8 Unused2[896]; +} T3_SEND_BD_SELECTOR, *PT3_SEND_BD_SELECTOR; + +/******************************************************************************/ +/* Send BD initiator control registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define SND_BD_IN_MODE_RESET BIT_0 +#define SND_BD_IN_MODE_ENABLE BIT_1 +#define SND_BD_IN_MODE_ATTN_ENABLE BIT_2 + + T3_32BIT_REGISTER Status; +#define SND_BD_IN_STATUS_ERROR_ATTN BIT_2 + + /* Send BD initiator local NIC send BD producer index. */ + T3_32BIT_REGISTER NicSendBdInProdIdx[16]; + + /* Unused space. */ + LM_UINT8 Unused2[952]; +} T3_SEND_BD_INITIATOR, *PT3_SEND_BD_INITIATOR; + +/******************************************************************************/ +/* Send BD Completion Control. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define SND_BD_COMP_MODE_RESET BIT_0 +#define SND_BD_COMP_MODE_ENABLE BIT_1 +#define SND_BD_COMP_MODE_ATTN_ENABLE BIT_2 + + /* Unused space. */ + LM_UINT8 Unused2[1020]; +} T3_SEND_BD_COMPLETION, *PT3_SEND_BD_COMPLETION; + +/******************************************************************************/ +/* Receive list placement control registers. */ +/******************************************************************************/ + +typedef struct { + /* Mode. */ + T3_32BIT_REGISTER Mode; +#define RCV_LIST_PLMT_MODE_RESET BIT_0 +#define RCV_LIST_PLMT_MODE_ENABLE BIT_1 +#define RCV_LIST_PLMT_MODE_CLASS0_ATTN_ENABLE BIT_2 +#define RCV_LIST_PLMT_MODE_MAPPING_OOR_ATTN_ENABLE BIT_3 +#define RCV_LIST_PLMT_MODE_STATS_OFLOW_ATTN_ENABLE BIT_4 + + /* Status. */ + T3_32BIT_REGISTER Status; +#define RCV_LIST_PLMT_STATUS_CLASS0_ATTN BIT_2 +#define RCV_LIST_PLMT_STATUS_MAPPING_ATTN BIT_3 +#define RCV_LIST_PLMT_STATUS_STATS_OFLOW_ATTN BIT_4 + + /* Receive selector list lock register. */ + T3_32BIT_REGISTER Lock; +#define RCV_LIST_SEL_LOCK_REQUEST_MASK 0xffff +#define RCV_LIST_SEL_LOCK_GRANT_MASK 0xffff0000 + + /* Selector non-empty bits. */ + T3_32BIT_REGISTER NonEmptyBits; +#define RCV_LIST_SEL_NON_EMPTY_MASK 0xffff + + /* Receive list placement configuration register. */ + T3_32BIT_REGISTER Config; + + /* Receive List Placement statistics Control. */ + T3_32BIT_REGISTER StatsCtrl; +#define RCV_LIST_STATS_ENABLE BIT_0 +#define RCV_LIST_STATS_FAST_UPDATE BIT_1 + + /* Receive List Placement statistics Enable Mask. */ + T3_32BIT_REGISTER StatsEnableMask; + + /* Receive List Placement statistics Increment Mask. */ + T3_32BIT_REGISTER StatsIncMask; + + /* Unused space. */ + LM_UINT8 Unused1[224]; + + struct { + T3_32BIT_REGISTER Head; + T3_32BIT_REGISTER Tail; + T3_32BIT_REGISTER Count; + + /* Unused space. */ + LM_UINT8 Unused[4]; + } RcvSelectorList[16]; + + /* Local statistics counter. */ + T3_32BIT_REGISTER ClassOfServCnt[16]; + + T3_32BIT_REGISTER DropDueToFilterCnt; + T3_32BIT_REGISTER DmaWriteQFullCnt; + T3_32BIT_REGISTER DmaHighPriorityWriteQFullCnt; + T3_32BIT_REGISTER NoMoreReceiveBdCnt; + T3_32BIT_REGISTER IfInDiscardsCnt; + T3_32BIT_REGISTER IfInErrorsCnt; + T3_32BIT_REGISTER RcvThresholdHitCnt; + + /* Another unused space. */ + LM_UINT8 Unused2[420]; +} T3_RCV_LIST_PLACEMENT, *PT3_RCV_LIST_PLACEMENT; + +/******************************************************************************/ +/* Receive Data and Receive BD Initiator Control. */ +/******************************************************************************/ + +typedef struct { + /* Mode. */ + T3_32BIT_REGISTER Mode; +#define RCV_DATA_BD_IN_MODE_RESET BIT_0 +#define RCV_DATA_BD_IN_MODE_ENABLE BIT_1 +#define RCV_DATA_BD_IN_MODE_JUMBO_BD_NEEDED BIT_2 +#define RCV_DATA_BD_IN_MODE_FRAME_TOO_BIG BIT_3 +#define RCV_DATA_BD_IN_MODE_INVALID_RING_SIZE BIT_4 + + /* Status. */ + T3_32BIT_REGISTER Status; +#define RCV_DATA_BD_IN_STATUS_JUMBO_BD_NEEDED BIT_2 +#define RCV_DATA_BD_IN_STATUS_FRAME_TOO_BIG BIT_3 +#define RCV_DATA_BD_IN_STATUS_INVALID_RING_SIZE BIT_4 + + /* Split frame minium size. */ + T3_32BIT_REGISTER SplitFrameMinSize; + + /* Unused space. */ + LM_UINT8 Unused1[0x2440 - 0x240c]; + + /* Receive RCBs. */ + T3_RCB JumboRcvRcb; + T3_RCB StdRcvRcb; + T3_RCB MiniRcvRcb; + + /* Receive Data and Receive BD Ring Initiator Local NIC Receive */ + /* BD Consumber Index. */ + T3_32BIT_REGISTER NicJumboConIdx; + T3_32BIT_REGISTER NicStdConIdx; + T3_32BIT_REGISTER NicMiniConIdx; + + /* Unused space. */ + LM_UINT8 Unused2[4]; + + /* Receive Data and Receive BD Initiator Local Receive Return ProdIdx. */ + T3_32BIT_REGISTER RcvDataBdProdIdx[16]; + + /* Receive Data and Receive BD Initiator Hardware Diagnostic. */ + T3_32BIT_REGISTER HwDiag; + + /* Unused space. */ + LM_UINT8 Unused3[828]; +} T3_RCV_DATA_BD_INITIATOR, *PT3_RCV_DATA_BD_INITIATOR; + +/******************************************************************************/ +/* Receive Data Completion Control Registes. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define RCV_DATA_COMP_MODE_RESET BIT_0 +#define RCV_DATA_COMP_MODE_ENABLE BIT_1 +#define RCV_DATA_COMP_MODE_ATTN_ENABLE BIT_2 + + /* Unused spaced. */ + LM_UINT8 Unused[1020]; +} T3_RCV_DATA_COMPLETION, *PT3_RCV_DATA_COMPLETION; + +/******************************************************************************/ +/* Receive BD Initiator Control. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define RCV_BD_IN_MODE_RESET BIT_0 +#define RCV_BD_IN_MODE_ENABLE BIT_1 +#define RCV_BD_IN_MODE_BD_IN_DIABLED_RCB_ATTN_ENABLE BIT_2 + + T3_32BIT_REGISTER Status; +#define RCV_BD_IN_STATUS_BD_IN_DIABLED_RCB_ATTN BIT_2 + + T3_32BIT_REGISTER NicJumboRcvProdIdx; + T3_32BIT_REGISTER NicStdRcvProdIdx; + T3_32BIT_REGISTER NicMiniRcvProdIdx; + + T3_32BIT_REGISTER MiniRcvThreshold; + T3_32BIT_REGISTER StdRcvThreshold; + T3_32BIT_REGISTER JumboRcvThreshold; + + /* Unused space. */ + LM_UINT8 Unused[992]; +} T3_RCV_BD_INITIATOR, *PT3_RCV_BD_INITIATOR; + +/******************************************************************************/ +/* Receive BD Completion Control Registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define RCV_BD_COMP_MODE_RESET BIT_0 +#define RCV_BD_COMP_MODE_ENABLE BIT_1 +#define RCV_BD_COMP_MODE_ATTN_ENABLE BIT_2 + + T3_32BIT_REGISTER Status; +#define RCV_BD_COMP_STATUS_ERROR_ATTN BIT_2 + + T3_32BIT_REGISTER NicJumboRcvBdProdIdx; + T3_32BIT_REGISTER NicStdRcvBdProdIdx; + T3_32BIT_REGISTER NicMiniRcvBdProdIdx; + + /* Unused space. */ + LM_UINT8 Unused[1004]; +} T3_RCV_BD_COMPLETION, *PT3_RCV_BD_COMPLETION; + +/******************************************************************************/ +/* Receive list selector control register. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define RCV_LIST_SEL_MODE_RESET BIT_0 +#define RCV_LIST_SEL_MODE_ENABLE BIT_1 +#define RCV_LIST_SEL_MODE_ATTN_ENABLE BIT_2 + + T3_32BIT_REGISTER Status; +#define RCV_LIST_SEL_STATUS_ERROR_ATTN BIT_2 + + /* Unused space. */ + LM_UINT8 Unused[1016]; +} T3_RCV_LIST_SELECTOR, *PT3_RCV_LIST_SELECTOR; + +/******************************************************************************/ +/* Mbuf cluster free registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define MBUF_CLUSTER_FREE_MODE_RESET BIT_0 +#define MBUF_CLUSTER_FREE_MODE_ENABLE BIT_1 + + T3_32BIT_REGISTER Status; + + /* Unused space. */ + LM_UINT8 Unused[1016]; +} T3_MBUF_CLUSTER_FREE, *PT3_MBUF_CLUSTER_FREE; + +/******************************************************************************/ +/* Host coalescing control registers. */ +/******************************************************************************/ + +typedef struct { + /* Mode. */ + T3_32BIT_REGISTER Mode; +#define HOST_COALESCE_RESET BIT_0 +#define HOST_COALESCE_ENABLE BIT_1 +#define HOST_COALESCE_ATTN BIT_2 +#define HOST_COALESCE_NOW BIT_3 +#define HOST_COALESCE_FULL_STATUS_MODE BIT_NONE +#define HOST_COALESCE_64_BYTE_STATUS_MODE BIT_7 +#define HOST_COALESCE_32_BYTE_STATUS_MODE BIT_8 +#define HOST_COALESCE_CLEAR_TICKS_ON_RX_BD_EVENT BIT_9 +#define HOST_COALESCE_CLEAR_TICKS_ON_TX_BD_EVENT BIT_10 +#define HOST_COALESCE_NO_INT_ON_COALESCE_NOW_MODE BIT_11 +#define HOST_COALESCE_NO_INT_ON_FORCE_DMAD_MODE BIT_12 + + /* Status. */ + T3_32BIT_REGISTER Status; +#define HOST_COALESCE_ERROR_ATTN BIT_2 + + /* Receive coalescing ticks. */ + T3_32BIT_REGISTER RxCoalescingTicks; + + /* Send coalescing ticks. */ + T3_32BIT_REGISTER TxCoalescingTicks; + + /* Receive max coalesced frames. */ + T3_32BIT_REGISTER RxMaxCoalescedFrames; + + /* Send max coalesced frames. */ + T3_32BIT_REGISTER TxMaxCoalescedFrames; + + /* Receive coalescing ticks during interrupt. */ + T3_32BIT_REGISTER RxCoalescedTickDuringInt; + + /* Send coalescing ticks during interrupt. */ + T3_32BIT_REGISTER TxCoalescedTickDuringInt; + + /* Receive max coalesced frames during interrupt. */ + T3_32BIT_REGISTER RxMaxCoalescedFramesDuringInt; + + /* Send max coalesced frames during interrupt. */ + T3_32BIT_REGISTER TxMaxCoalescedFramesDuringInt; + + /* Statistics tick. */ + T3_32BIT_REGISTER StatsCoalescingTicks; + + /* Unused space. */ + LM_UINT8 Unused2[4]; + + /* Statistics host address. */ + T3_64BIT_REGISTER StatsBlkHostAddr; + + /* Status block host address. */ + T3_64BIT_REGISTER StatusBlkHostAddr; + + /* Statistics NIC address. */ + T3_32BIT_REGISTER StatsBlkNicAddr; + + /* Statust block NIC address. */ + T3_32BIT_REGISTER StatusBlkNicAddr; + + /* Flow attention registers. */ + T3_32BIT_REGISTER FlowAttn; + + /* Unused space. */ + LM_UINT8 Unused3[4]; + + T3_32BIT_REGISTER NicJumboRcvBdConIdx; + T3_32BIT_REGISTER NicStdRcvBdConIdx; + T3_32BIT_REGISTER NicMiniRcvBdConIdx; + + /* Unused space. */ + LM_UINT8 Unused4[36]; + + T3_32BIT_REGISTER NicRetProdIdx[16]; + T3_32BIT_REGISTER NicSndBdConIdx[16]; + + /* Unused space. */ + LM_UINT8 Unused5[768]; +} T3_HOST_COALESCING, *PT3_HOST_COALESCING; + +/******************************************************************************/ +/* Memory arbiter registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define T3_MEM_ARBITER_MODE_RESET BIT_0 +#define T3_MEM_ARBITER_MODE_ENABLE BIT_1 + + T3_32BIT_REGISTER Status; + + T3_32BIT_REGISTER ArbTrapAddrLow; + T3_32BIT_REGISTER ArbTrapAddrHigh; + + /* Unused space. */ + LM_UINT8 Unused[1008]; +} T3_MEM_ARBITER, *PT3_MEM_ARBITER; + +/******************************************************************************/ +/* Buffer manager control register. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define BUFMGR_MODE_RESET BIT_0 +#define BUFMGR_MODE_ENABLE BIT_1 +#define BUFMGR_MODE_ATTN_ENABLE BIT_2 +#define BUFMGR_MODE_BM_TEST BIT_3 +#define BUFMGR_MODE_MBUF_LOW_ATTN_ENABLE BIT_4 + + T3_32BIT_REGISTER Status; +#define BUFMGR_STATUS_ERROR BIT_2 +#define BUFMGR_STATUS_MBUF_LOW BIT_4 + + T3_32BIT_REGISTER MbufPoolAddr; + T3_32BIT_REGISTER MbufPoolSize; + T3_32BIT_REGISTER MbufReadDmaLowWaterMark; + T3_32BIT_REGISTER MbufMacRxLowWaterMark; + T3_32BIT_REGISTER MbufHighWaterMark; + + T3_32BIT_REGISTER RxCpuMbufAllocReq; +#define BUFMGR_MBUF_ALLOC_BIT BIT_31 + T3_32BIT_REGISTER RxCpuMbufAllocResp; + T3_32BIT_REGISTER TxCpuMbufAllocReq; + T3_32BIT_REGISTER TxCpuMbufAllocResp; + + T3_32BIT_REGISTER DmaDescPoolAddr; + T3_32BIT_REGISTER DmaDescPoolSize; + T3_32BIT_REGISTER DmaLowWaterMark; + T3_32BIT_REGISTER DmaHighWaterMark; + + T3_32BIT_REGISTER RxCpuDmaAllocReq; + T3_32BIT_REGISTER RxCpuDmaAllocResp; + T3_32BIT_REGISTER TxCpuDmaAllocReq; + T3_32BIT_REGISTER TxCpuDmaAllocResp; + + T3_32BIT_REGISTER Hwdiag[3]; + + /* Unused space. */ + LM_UINT8 Unused[936]; +} T3_BUFFER_MANAGER, *PT3_BUFFER_MANAGER; + +/******************************************************************************/ +/* Read DMA control registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define DMA_READ_MODE_RESET BIT_0 +#define DMA_READ_MODE_ENABLE BIT_1 +#define DMA_READ_MODE_TARGET_ABORT_ATTN_ENABLE BIT_2 +#define DMA_READ_MODE_MASTER_ABORT_ATTN_ENABLE BIT_3 +#define DMA_READ_MODE_PARITY_ERROR_ATTN_ENABLE BIT_4 +#define DMA_READ_MODE_ADDR_OVERFLOW_ATTN_ENABLE BIT_5 +#define DMA_READ_MODE_FIFO_OVERRUN_ATTN_ENABLE BIT_6 +#define DMA_READ_MODE_FIFO_UNDERRUN_ATTN_ENABLE BIT_7 +#define DMA_READ_MODE_FIFO_OVERREAD_ATTN_ENABLE BIT_8 +#define DMA_READ_MODE_LONG_READ_ATTN_ENABLE BIT_9 +#define DMA_READ_MODE_SPLIT_ENABLE BIT_11 +#define DMA_READ_MODE_SPLIT_RESET BIT_12 + + T3_32BIT_REGISTER Status; +#define DMA_READ_STATUS_TARGET_ABORT_ATTN BIT_2 +#define DMA_READ_STATUS_MASTER_ABORT_ATTN BIT_3 +#define DMA_READ_STATUS_PARITY_ERROR_ATTN BIT_4 +#define DMA_READ_STATUS_ADDR_OVERFLOW_ATTN BIT_5 +#define DMA_READ_STATUS_FIFO_OVERRUN_ATTN BIT_6 +#define DMA_READ_STATUS_FIFO_UNDERRUN_ATTN BIT_7 +#define DMA_READ_STATUS_FIFO_OVERREAD_ATTN BIT_8 +#define DMA_READ_STATUS_LONG_READ_ATTN BIT_9 + + /* Unused space. */ + LM_UINT8 Unused[1016]; +} T3_DMA_READ, *PT3_DMA_READ; + +typedef union T3_CPU { + struct { + T3_32BIT_REGISTER mode; +#define CPU_MODE_HALT BIT_10 +#define CPU_MODE_RESET BIT_0 + T3_32BIT_REGISTER state; + T3_32BIT_REGISTER EventMask; + T3_32BIT_REGISTER reserved1[4]; + T3_32BIT_REGISTER PC; + T3_32BIT_REGISTER Instruction; + T3_32BIT_REGISTER SpadUnderflow; + T3_32BIT_REGISTER WatchdogClear; + T3_32BIT_REGISTER WatchdogVector; + T3_32BIT_REGISTER WatchdogSavedPC; + T3_32BIT_REGISTER HardwareBp; + T3_32BIT_REGISTER reserved2[3]; + T3_32BIT_REGISTER WatchdogSavedState; + T3_32BIT_REGISTER LastBrchAddr; + T3_32BIT_REGISTER SpadUnderflowSet; + T3_32BIT_REGISTER reserved3[(0x200 - 0x50) / 4]; + T3_32BIT_REGISTER Regs[32]; + T3_32BIT_REGISTER reserved4[(0x400 - 0x280) / 4]; + } reg; +} T3_CPU, *PT3_CPU; + +/******************************************************************************/ +/* Write DMA control registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define DMA_WRITE_MODE_RESET BIT_0 +#define DMA_WRITE_MODE_ENABLE BIT_1 +#define DMA_WRITE_MODE_TARGET_ABORT_ATTN_ENABLE BIT_2 +#define DMA_WRITE_MODE_MASTER_ABORT_ATTN_ENABLE BIT_3 +#define DMA_WRITE_MODE_PARITY_ERROR_ATTN_ENABLE BIT_4 +#define DMA_WRITE_MODE_ADDR_OVERFLOW_ATTN_ENABLE BIT_5 +#define DMA_WRITE_MODE_FIFO_OVERRUN_ATTN_ENABLE BIT_6 +#define DMA_WRITE_MODE_FIFO_UNDERRUN_ATTN_ENABLE BIT_7 +#define DMA_WRITE_MODE_FIFO_OVERREAD_ATTN_ENABLE BIT_8 +#define DMA_WRITE_MODE_LONG_READ_ATTN_ENABLE BIT_9 + + T3_32BIT_REGISTER Status; +#define DMA_WRITE_STATUS_TARGET_ABORT_ATTN BIT_2 +#define DMA_WRITE_STATUS_MASTER_ABORT_ATTN BIT_3 +#define DMA_WRITE_STATUS_PARITY_ERROR_ATTN BIT_4 +#define DMA_WRITE_STATUS_ADDR_OVERFLOW_ATTN BIT_5 +#define DMA_WRITE_STATUS_FIFO_OVERRUN_ATTN BIT_6 +#define DMA_WRITE_STATUS_FIFO_UNDERRUN_ATTN BIT_7 +#define DMA_WRITE_STATUS_FIFO_OVERREAD_ATTN BIT_8 +#define DMA_WRITE_STATUS_LONG_READ_ATTN BIT_9 + + /* Unused space. */ + LM_UINT8 Unused[1016]; +} T3_DMA_WRITE, *PT3_DMA_WRITE; + +/******************************************************************************/ +/* Mailbox registers. */ +/******************************************************************************/ + +typedef struct { + /* Interrupt mailbox registers. */ + T3_64BIT_REGISTER Interrupt[4]; + + /* General mailbox registers. */ + T3_64BIT_REGISTER General[8]; + + /* Reload statistics mailbox. */ + T3_64BIT_REGISTER ReloadStat; + + /* Receive BD ring producer index registers. */ + T3_64BIT_REGISTER RcvStdProdIdx; + T3_64BIT_REGISTER RcvJumboProdIdx; + T3_64BIT_REGISTER RcvMiniProdIdx; + + /* Receive return ring consumer index registers. */ + T3_64BIT_REGISTER RcvRetConIdx[16]; + + /* Send BD ring host producer index registers. */ + T3_64BIT_REGISTER SendHostProdIdx[16]; + + /* Send BD ring nic producer index registers. */ + T3_64BIT_REGISTER SendNicProdIdx[16]; +} T3_MAILBOX, *PT3_MAILBOX; + +typedef struct { + T3_MAILBOX Mailbox; + + /* Priority mailbox registers. */ + T3_32BIT_REGISTER HighPriorityEventVector; + T3_32BIT_REGISTER HighPriorityEventMask; + T3_32BIT_REGISTER LowPriorityEventVector; + T3_32BIT_REGISTER LowPriorityEventMask; + + /* Unused space. */ + LM_UINT8 Unused[496]; +} T3_GRC_MAILBOX, *PT3_GRC_MAILBOX; + +/******************************************************************************/ +/* Flow through queues. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Reset; + + LM_UINT8 Unused[12]; + + T3_32BIT_REGISTER DmaNormalReadFtqCtrl; + T3_32BIT_REGISTER DmaNormalReadFtqFullCnt; + T3_32BIT_REGISTER DmaNormalReadFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER DmaNormalReadFtqFifoWritePeek; + + T3_32BIT_REGISTER DmaHighReadFtqCtrl; + T3_32BIT_REGISTER DmaHighReadFtqFullCnt; + T3_32BIT_REGISTER DmaHighReadFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER DmaHighReadFtqFifoWritePeek; + + T3_32BIT_REGISTER DmaCompDiscardFtqCtrl; + T3_32BIT_REGISTER DmaCompDiscardFtqFullCnt; + T3_32BIT_REGISTER DmaCompDiscardFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER DmaCompDiscardFtqFifoWritePeek; + + T3_32BIT_REGISTER SendBdCompFtqCtrl; + T3_32BIT_REGISTER SendBdCompFtqFullCnt; + T3_32BIT_REGISTER SendBdCompFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER SendBdCompFtqFifoWritePeek; + + T3_32BIT_REGISTER SendDataInitiatorFtqCtrl; + T3_32BIT_REGISTER SendDataInitiatorFtqFullCnt; + T3_32BIT_REGISTER SendDataInitiatorFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER SendDataInitiatorFtqFifoWritePeek; + + T3_32BIT_REGISTER DmaNormalWriteFtqCtrl; + T3_32BIT_REGISTER DmaNormalWriteFtqFullCnt; + T3_32BIT_REGISTER DmaNormalWriteFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER DmaNormalWriteFtqFifoWritePeek; + + T3_32BIT_REGISTER DmaHighWriteFtqCtrl; + T3_32BIT_REGISTER DmaHighWriteFtqFullCnt; + T3_32BIT_REGISTER DmaHighWriteFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER DmaHighWriteFtqFifoWritePeek; + + T3_32BIT_REGISTER SwType1FtqCtrl; + T3_32BIT_REGISTER SwType1FtqFullCnt; + T3_32BIT_REGISTER SwType1FtqFifoEnqueueDequeue; + T3_32BIT_REGISTER SwType1FtqFifoWritePeek; + + T3_32BIT_REGISTER SendDataCompFtqCtrl; + T3_32BIT_REGISTER SendDataCompFtqFullCnt; + T3_32BIT_REGISTER SendDataCompFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER SendDataCompFtqFifoWritePeek; + + T3_32BIT_REGISTER HostCoalesceFtqCtrl; + T3_32BIT_REGISTER HostCoalesceFtqFullCnt; + T3_32BIT_REGISTER HostCoalesceFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER HostCoalesceFtqFifoWritePeek; + + T3_32BIT_REGISTER MacTxFtqCtrl; + T3_32BIT_REGISTER MacTxFtqFullCnt; + T3_32BIT_REGISTER MacTxFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER MacTxFtqFifoWritePeek; + + T3_32BIT_REGISTER MbufClustFreeFtqCtrl; + T3_32BIT_REGISTER MbufClustFreeFtqFullCnt; + T3_32BIT_REGISTER MbufClustFreeFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER MbufClustFreeFtqFifoWritePeek; + + T3_32BIT_REGISTER RcvBdCompFtqCtrl; + T3_32BIT_REGISTER RcvBdCompFtqFullCnt; + T3_32BIT_REGISTER RcvBdCompFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER RcvBdCompFtqFifoWritePeek; + + T3_32BIT_REGISTER RcvListPlmtFtqCtrl; + T3_32BIT_REGISTER RcvListPlmtFtqFullCnt; + T3_32BIT_REGISTER RcvListPlmtFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER RcvListPlmtFtqFifoWritePeek; + + T3_32BIT_REGISTER RcvDataBdInitiatorFtqCtrl; + T3_32BIT_REGISTER RcvDataBdInitiatorFtqFullCnt; + T3_32BIT_REGISTER RcvDataBdInitiatorFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER RcvDataBdInitiatorFtqFifoWritePeek; + + T3_32BIT_REGISTER RcvDataCompFtqCtrl; + T3_32BIT_REGISTER RcvDataCompFtqFullCnt; + T3_32BIT_REGISTER RcvDataCompFtqFifoEnqueueDequeue; + T3_32BIT_REGISTER RcvDataCompFtqFifoWritePeek; + + T3_32BIT_REGISTER SwType2FtqCtrl; + T3_32BIT_REGISTER SwType2FtqFullCnt; + T3_32BIT_REGISTER SwType2FtqFifoEnqueueDequeue; + T3_32BIT_REGISTER SwType2FtqFifoWritePeek; + + /* Unused space. */ + LM_UINT8 Unused2[736]; +} T3_FTQ, *PT3_FTQ; + +/******************************************************************************/ +/* Message signaled interrupt registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define MSI_MODE_RESET BIT_0 +#define MSI_MODE_ENABLE BIT_1 + T3_32BIT_REGISTER Status; + + T3_32BIT_REGISTER MsiFifoAccess; + + /* Unused space. */ + LM_UINT8 Unused[1012]; +} T3_MSG_SIGNALED_INT, *PT3_MSG_SIGNALED_INT; + +/******************************************************************************/ +/* DMA Completion registes. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Mode; +#define DMA_COMP_MODE_RESET BIT_0 +#define DMA_COMP_MODE_ENABLE BIT_1 + + /* Unused space. */ + LM_UINT8 Unused[1020]; +} T3_DMA_COMPLETION, *PT3_DMA_COMPLETION; + +/******************************************************************************/ +/* GRC registers. */ +/******************************************************************************/ + +typedef struct { + /* Mode control register. */ + T3_32BIT_REGISTER Mode; +#define GRC_MODE_UPDATE_ON_COALESCING BIT_0 +#define GRC_MODE_BYTE_SWAP_NON_FRAME_DATA BIT_1 +#define GRC_MODE_WORD_SWAP_NON_FRAME_DATA BIT_2 +#define GRC_MODE_BYTE_SWAP_DATA BIT_4 +#define GRC_MODE_WORD_SWAP_DATA BIT_5 +#define GRC_MODE_SPLIT_HEADER_MODE BIT_8 +#define GRC_MODE_NO_FRAME_CRACKING BIT_9 +#define GRC_MODE_INCLUDE_CRC BIT_10 +#define GRC_MODE_ALLOW_BAD_FRAMES BIT_11 +#define GRC_MODE_NO_INTERRUPT_ON_SENDS BIT_13 +#define GRC_MODE_NO_INTERRUPT_ON_RECEIVE BIT_14 +#define GRC_MODE_FORCE_32BIT_PCI_BUS_MODE BIT_15 +#define GRC_MODE_HOST_STACK_UP BIT_16 +#define GRC_MODE_HOST_SEND_BDS BIT_17 +#define GRC_MODE_TX_NO_PSEUDO_HEADER_CHKSUM BIT_20 +#define GRC_MODE_RX_NO_PSEUDO_HEADER_CHKSUM BIT_23 +#define GRC_MODE_INT_ON_TX_CPU_ATTN BIT_24 +#define GRC_MODE_INT_ON_RX_CPU_ATTN BIT_25 +#define GRC_MODE_INT_ON_MAC_ATTN BIT_26 +#define GRC_MODE_INT_ON_DMA_ATTN BIT_27 +#define GRC_MODE_INT_ON_FLOW_ATTN BIT_28 +#define GRC_MODE_4X_NIC_BASED_SEND_RINGS BIT_29 +#define GRC_MODE_MULTICAST_FRAME_ENABLE BIT_30 + + /* Misc configuration register. */ + T3_32BIT_REGISTER MiscCfg; +#define GRC_MISC_CFG_CORE_CLOCK_RESET BIT_0 +#define GRC_MISC_PRESCALAR_TIMER_MASK 0xfe +#define GRC_MISC_BD_ID_MASK 0x0001e000 +#define GRC_MISC_BD_ID_5700 0x0001e000 +#define GRC_MISC_BD_ID_5701 0x00000000 +#define GRC_MISC_BD_ID_5703 0x00000000 +#define GRC_MISC_BD_ID_5703S 0x00002000 +#define GRC_MISC_BD_ID_5702FE 0x00004000 +#define GRC_MISC_BD_ID_5704 0x00000000 +#define GRC_MISC_BD_ID_5704CIOBE 0x00004000 + + /* Miscellaneous local control register. */ + T3_32BIT_REGISTER LocalCtrl; +#define GRC_MISC_LOCAL_CTRL_INT_ACTIVE BIT_0 +#define GRC_MISC_LOCAL_CTRL_CLEAR_INT BIT_1 +#define GRC_MISC_LOCAL_CTRL_SET_INT BIT_2 +#define GRC_MISC_LOCAL_CTRL_INT_ON_ATTN BIT_3 +#define GRC_MISC_LOCAL_CTRL_GPIO_INPUT0 BIT_8 +#define GRC_MISC_LOCAL_CTRL_GPIO_INPUT1 BIT_9 +#define GRC_MISC_LOCAL_CTRL_GPIO_INPUT2 BIT_10 +#define GRC_MISC_LOCAL_CTRL_GPIO_OE0 BIT_11 +#define GRC_MISC_LOCAL_CTRL_GPIO_OE1 BIT_12 +#define GRC_MISC_LOCAL_CTRL_GPIO_OE2 BIT_13 +#define GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT0 BIT_14 +#define GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT1 BIT_15 +#define GRC_MISC_LOCAL_CTRL_GPIO_OUTPUT2 BIT_16 +#define GRC_MISC_LOCAL_CTRL_ENABLE_EXT_MEMORY BIT_17 +#define GRC_MISC_LOCAL_CTRL_BANK_SELECT BIT_21 +#define GRC_MISC_LOCAL_CTRL_SSRAM_TYPE BIT_22 + +#define GRC_MISC_MEMSIZE_256K 0 +#define GRC_MISC_MEMSIZE_512K (1 << 18) +#define GRC_MISC_MEMSIZE_1024K (2 << 18) +#define GRC_MISC_MEMSIZE_2048K (3 << 18) +#define GRC_MISC_MEMSIZE_4096K (4 << 18) +#define GRC_MISC_MEMSIZE_8192K (5 << 18) +#define GRC_MISC_MEMSIZE_16M (6 << 18) +#define GRC_MISC_LOCAL_CTRL_AUTO_SEEPROM BIT_24 + + T3_32BIT_REGISTER Timer; + + T3_32BIT_REGISTER RxCpuEvent; + T3_32BIT_REGISTER RxTimerRef; + T3_32BIT_REGISTER RxCpuSemaphore; + T3_32BIT_REGISTER RemoteRxCpuAttn; + + T3_32BIT_REGISTER TxCpuEvent; + T3_32BIT_REGISTER TxTimerRef; + T3_32BIT_REGISTER TxCpuSemaphore; + T3_32BIT_REGISTER RemoteTxCpuAttn; + + T3_64BIT_REGISTER MemoryPowerUp; + + T3_32BIT_REGISTER EepromAddr; +#define SEEPROM_ADDR_WRITE 0 +#define SEEPROM_ADDR_READ (1 << 31) +#define SEEPROM_ADDR_RW_MASK 0x80000000 +#define SEEPROM_ADDR_COMPLETE (1 << 30) +#define SEEPROM_ADDR_FSM_RESET (1 << 29) +#define SEEPROM_ADDR_DEV_ID(x) (x << 26) +#define SEEPROM_ADDR_DEV_ID_MASK 0x1c000000 +#define SEEPROM_ADDR_START (1 << 25) +#define SEEPROM_ADDR_CLK_PERD(x) (x << 16) +#define SEEPROM_ADDR_ADDRESS(x) (x & 0xfffc) +#define SEEPROM_ADDR_ADDRESS_MASK 0x0000ffff + +#define SEEPROM_CLOCK_PERIOD 60 +#define SEEPROM_CHIP_SIZE (64 * 1024) + + T3_32BIT_REGISTER EepromData; + T3_32BIT_REGISTER EepromCtrl; + + T3_32BIT_REGISTER MdiCtrl; + T3_32BIT_REGISTER SepromDelay; + + /* Unused space. */ + LM_UINT8 Unused[948]; +} T3_GRC, *PT3_GRC; + +/******************************************************************************/ +/* NVRAM control registers. */ +/******************************************************************************/ + +typedef struct { + T3_32BIT_REGISTER Cmd; +#define NVRAM_CMD_RESET BIT_0 +#define NVRAM_CMD_DONE BIT_3 +#define NVRAM_CMD_DO_IT BIT_4 +#define NVRAM_CMD_WR BIT_5 +#define NVRAM_CMD_RD BIT_NONE +#define NVRAM_CMD_ERASE BIT_6 +#define NVRAM_CMD_FIRST BIT_7 +#define NVRAM_CMD_LAST BIT_8 + + T3_32BIT_REGISTER Status; + T3_32BIT_REGISTER WriteData; + + T3_32BIT_REGISTER Addr; +#define NVRAM_ADDRESS_MASK 0xffffff + + T3_32BIT_REGISTER ReadData; + + /* Flash config 1 register. */ + T3_32BIT_REGISTER Config1; +#define FLASH_INTERFACE_ENABLE BIT_0 +#define FLASH_SSRAM_BUFFERRED_MODE BIT_1 +#define FLASH_PASS_THRU_MODE BIT_2 +#define FLASH_BIT_BANG_MODE BIT_3 +#define FLASH_COMPAT_BYPASS BIT_31 + + /* Buffered flash (Atmel: AT45DB011B) specific information */ +#define BUFFERED_FLASH_PAGE_POS 9 +#define BUFFERED_FLASH_BYTE_ADDR_MASK ((1<<BUFFERED_FLASH_PAGE_POS) - 1) +#define BUFFERED_FLASH_PAGE_SIZE 264 +#define BUFFERED_FLASH_PHY_PAGE_SIZE 512 + + T3_32BIT_REGISTER Config2; + T3_32BIT_REGISTER Config3; + T3_32BIT_REGISTER SwArb; +#define SW_ARB_REQ_SET0 BIT_0 +#define SW_ARB_REQ_SET1 BIT_1 +#define SW_ARB_REQ_SET2 BIT_2 +#define SW_ARB_REQ_SET3 BIT_3 +#define SW_ARB_REQ_CLR0 BIT_4 +#define SW_ARB_REQ_CLR1 BIT_5 +#define SW_ARB_REQ_CLR2 BIT_6 +#define SW_ARB_REQ_CLR3 BIT_7 +#define SW_ARB_GNT0 BIT_8 +#define SW_ARB_GNT1 BIT_9 +#define SW_ARB_GNT2 BIT_10 +#define SW_ARB_GNT3 BIT_11 +#define SW_ARB_REQ0 BIT_12 +#define SW_ARB_REQ1 BIT_13 +#define SW_ARB_REQ2 BIT_14 +#define SW_ARB_REQ3 BIT_15 + + /* Unused space. */ + LM_UINT8 Unused[988]; +} T3_NVRAM, *PT3_NVRAM; + +/******************************************************************************/ +/* NIC's internal memory. */ +/******************************************************************************/ + +typedef struct { + /* Page zero for the internal CPUs. */ + LM_UINT8 PageZero[0x100]; /* 0x0000 */ + + /* Send RCBs. */ + T3_RCB SendRcb[16]; /* 0x0100 */ + + /* Receive Return RCBs. */ + T3_RCB RcvRetRcb[16]; /* 0x0200 */ + + /* Statistics block. */ + T3_STATS_BLOCK StatsBlk; /* 0x0300 */ + + /* Status block. */ + T3_STATUS_BLOCK StatusBlk; /* 0x0b00 */ + + /* Reserved for software. */ + LM_UINT8 Reserved[1200]; /* 0x0b50 */ + + /* Unmapped region. */ + LM_UINT8 Unmapped[4096]; /* 0x1000 */ + + /* DMA descriptors. */ + LM_UINT8 DmaDesc[8192]; /* 0x2000 */ + + /* Buffer descriptors. */ + LM_UINT8 BufferDesc[16384]; /* 0x4000 */ +} T3_FIRST_32K_SRAM, *PT3_FIRST_32K_SRAM; + +/******************************************************************************/ +/* Memory layout. */ +/******************************************************************************/ + +typedef struct { + /* PCI configuration registers. */ + T3_PCI_CONFIGURATION PciCfg; + + /* Unused. */ + LM_UINT8 Unused1[0x100]; /* 0x0100 */ + + /* Mailbox . */ + T3_MAILBOX Mailbox; /* 0x0200 */ + + /* MAC control registers. */ + T3_MAC_CONTROL MacCtrl; /* 0x0400 */ + + /* Send data initiator control registers. */ + T3_SEND_DATA_INITIATOR SndDataIn; /* 0x0c00 */ + + /* Send data completion Control registers. */ + T3_SEND_DATA_COMPLETION SndDataComp; /* 0x1000 */ + + /* Send BD ring selector. */ + T3_SEND_BD_SELECTOR SndBdSel; /* 0x1400 */ + + /* Send BD initiator control registers. */ + T3_SEND_BD_INITIATOR SndBdIn; /* 0x1800 */ + + /* Send BD completion control registers. */ + T3_SEND_BD_COMPLETION SndBdComp; /* 0x1c00 */ + + /* Receive list placement control registers. */ + T3_RCV_LIST_PLACEMENT RcvListPlmt; /* 0x2000 */ + + /* Receive Data and Receive BD Initiator Control. */ + T3_RCV_DATA_BD_INITIATOR RcvDataBdIn; /* 0x2400 */ + + /* Receive Data Completion Control */ + T3_RCV_DATA_COMPLETION RcvDataComp; /* 0x2800 */ + + /* Receive BD Initiator Control Registers. */ + T3_RCV_BD_INITIATOR RcvBdIn; /* 0x2c00 */ + + /* Receive BD Completion Control Registers. */ + T3_RCV_BD_COMPLETION RcvBdComp; /* 0x3000 */ + + /* Receive list selector control registers. */ + T3_RCV_LIST_SELECTOR RcvListSel; /* 0x3400 */ + + /* Mbuf cluster free registers. */ + T3_MBUF_CLUSTER_FREE MbufClusterFree; /* 0x3800 */ + + /* Host coalescing control registers. */ + T3_HOST_COALESCING HostCoalesce; /* 0x3c00 */ + + /* Memory arbiter control registers. */ + T3_MEM_ARBITER MemArbiter; /* 0x4000 */ + + /* Buffer manger control registers. */ + T3_BUFFER_MANAGER BufMgr; /* 0x4400 */ + + /* Read DMA control registers. */ + T3_DMA_READ DmaRead; /* 0x4800 */ + + /* Write DMA control registers. */ + T3_DMA_WRITE DmaWrite; /* 0x4c00 */ + + T3_CPU rxCpu; /* 0x5000 */ + T3_CPU txCpu; /* 0x5400 */ + + /* Mailboxes. */ + T3_GRC_MAILBOX GrcMailbox; /* 0x5800 */ + + /* Flow Through queues. */ + T3_FTQ Ftq; /* 0x5c00 */ + + /* Message signaled interrupt registes. */ + T3_MSG_SIGNALED_INT Msi; /* 0x6000 */ + + /* DMA completion registers. */ + T3_DMA_COMPLETION DmaComp; /* 0x6400 */ + + /* GRC registers. */ + T3_GRC Grc; /* 0x6800 */ + + /* Unused space. */ + LM_UINT8 Unused2[1024]; /* 0x6c00 */ + + /* NVRAM registers. */ + T3_NVRAM Nvram; /* 0x7000 */ + + /* Unused space. */ + LM_UINT8 Unused3[3072]; /* 0x7400 */ + + /* The 32k memory window into the NIC's */ + /* internal memory. The memory window is */ + /* controlled by the Memory Window Base */ + /* Address register. This register is located */ + /* in the PCI configuration space. */ + union { /* 0x8000 */ + T3_FIRST_32K_SRAM First32k; + + /* Use the memory window base address register to determine the */ + /* MBUF segment. */ + LM_UINT32 Mbuf[32768 / 4]; + LM_UINT32 MemBlock32K[32768 / 4]; + } uIntMem; +} T3_STD_MEM_MAP, *PT3_STD_MEM_MAP; + +/******************************************************************************/ +/* Adapter info. */ +/******************************************************************************/ + +typedef struct { + LM_UINT16 Svid; + LM_UINT16 Ssid; + LM_UINT32 PhyId; + LM_UINT32 Serdes; /* 0 = copper PHY, 1 = Serdes */ +} LM_ADAPTER_INFO, *PLM_ADAPTER_INFO; + +/******************************************************************************/ +/* Packet queues. */ +/******************************************************************************/ + +DECLARE_QUEUE_TYPE (LM_RX_PACKET_Q, MAX_RX_PACKET_DESC_COUNT); +DECLARE_QUEUE_TYPE (LM_TX_PACKET_Q, MAX_TX_PACKET_DESC_COUNT); + +/******************************************************************************/ +/* Tx counters. */ +/******************************************************************************/ + +typedef struct { + LM_COUNTER TxPacketGoodCnt; + LM_COUNTER TxBytesGoodCnt; + LM_COUNTER TxPacketAbortedCnt; + LM_COUNTER NoSendBdLeftCnt; + LM_COUNTER NoMapRegisterLeftCnt; + LM_COUNTER TooManyFragmentsCnt; + LM_COUNTER NoTxPacketDescCnt; +} LM_TX_COUNTERS, *PLM_TX_COUNTERS; + +/******************************************************************************/ +/* Rx counters. */ +/******************************************************************************/ + +typedef struct { + LM_COUNTER RxPacketGoodCnt; + LM_COUNTER RxBytesGoodCnt; + LM_COUNTER RxPacketErrCnt; + LM_COUNTER RxErrCrcCnt; + LM_COUNTER RxErrCollCnt; + LM_COUNTER RxErrLinkLostCnt; + LM_COUNTER RxErrPhyDecodeCnt; + LM_COUNTER RxErrOddNibbleCnt; + LM_COUNTER RxErrMacAbortCnt; + LM_COUNTER RxErrShortPacketCnt; + LM_COUNTER RxErrNoResourceCnt; + LM_COUNTER RxErrLargePacketCnt; +} LM_RX_COUNTERS, *PLM_RX_COUNTERS; + +/******************************************************************************/ +/* Receive producer rings. */ +/******************************************************************************/ + +typedef enum { + T3_UNKNOWN_RCV_PROD_RING = 0, + T3_STD_RCV_PROD_RING = 1, + T3_MINI_RCV_PROD_RING = 2, + T3_JUMBO_RCV_PROD_RING = 3 +} T3_RCV_PROD_RING, *PT3_RCV_PROD_RING; + +/******************************************************************************/ +/* Packet descriptor. */ +/******************************************************************************/ + +#define LM_PACKET_SIGNATURE_TX 0x6861766b +#define LM_PACKET_SIGNATURE_RX 0x6b766168 + +typedef struct _LM_PACKET { + /* Set in LM. */ + LM_STATUS PacketStatus; + + /* Set in LM for Rx, in UM for Tx. */ + LM_UINT32 PacketSize; + + LM_UINT16 Flags; + + LM_UINT16 VlanTag; + + union { + /* Send info. */ + struct { + /* Set up by UM. */ + LM_UINT32 FragCount; + + } Tx; + + /* Receive info. */ + struct { + /* This descriptor belongs to either Std, Mini, or Jumbo ring. */ + T3_RCV_PROD_RING RcvProdRing; + + /* Receive buffer size */ + LM_UINT32 RxBufferSize; + + /* Checksum information. */ + LM_UINT16 IpChecksum; + LM_UINT16 TcpUdpChecksum; + + } Rx; + } u; +} LM_PACKET; + +/******************************************************************************/ +/* Tigon3 device block. */ +/******************************************************************************/ + +typedef struct _LM_DEVICE_BLOCK { + int index; /* Device ID */ + /* Memory view. */ + PT3_STD_MEM_MAP pMemView; + + /* Base address of the block of memory in which the LM_PACKET descriptors */ + /* are allocated from. */ + PLM_VOID pPacketDescBase; + + LM_UINT32 MiscHostCtrl; + LM_UINT32 GrcLocalCtrl; + LM_UINT32 DmaReadWriteCtrl; + LM_UINT32 PciState; + + /* Rx info */ + LM_UINT32 RxStdDescCnt; + LM_UINT32 RxStdQueuedCnt; + LM_UINT32 RxStdProdIdx; + + PT3_RCV_BD pRxStdBdVirt; + LM_PHYSICAL_ADDRESS RxStdBdPhy; + + LM_UINT32 RxPacketDescCnt; + LM_RX_PACKET_Q RxPacketFreeQ; + LM_RX_PACKET_Q RxPacketReceivedQ; + + /* Receive info. */ + PT3_RCV_BD pRcvRetBdVirt; + LM_PHYSICAL_ADDRESS RcvRetBdPhy; + LM_UINT32 RcvRetConIdx; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + LM_UINT32 RxJumboDescCnt; + LM_UINT32 RxJumboBufferSize; + LM_UINT32 RxJumboQueuedCnt; + + LM_UINT32 RxJumboProdIdx; + + PT3_RCV_BD pRxJumboBdVirt; + LM_PHYSICAL_ADDRESS RxJumboBdPhy; +#endif /* T3_JUMBO_RCV_RCB_ENTRY_COUNT */ + + /* These values are used by the upper module to inform the protocol */ + /* of the maximum transmit/receive packet size. */ + LM_UINT32 TxMtu; /* Does not include CRC. */ + LM_UINT32 RxMtu; /* Does not include CRC. */ + + /* We need to shadow the EMAC, Rx, Tx mode registers. With B0 silicon, */ + /* we may have problems reading any MAC registers in 10mb mode. */ + LM_UINT32 MacMode; + LM_UINT32 RxMode; + LM_UINT32 TxMode; + + /* MiMode register. */ + LM_UINT32 MiMode; + + /* Host coalesce mode register. */ + LM_UINT32 CoalesceMode; + + /* Send info. */ + LM_UINT32 TxPacketDescCnt; + + /* Tx info. */ + LM_TX_PACKET_Q TxPacketFreeQ; + LM_TX_PACKET_Q TxPacketActiveQ; + LM_TX_PACKET_Q TxPacketXmittedQ; + + /* Pointers to SendBd. */ + PT3_SND_BD pSendBdVirt; + LM_PHYSICAL_ADDRESS SendBdPhy; /* Only valid for Host based Send BD. */ + + /* Send producer and consumer indices. */ + LM_UINT32 SendProdIdx; + LM_UINT32 SendConIdx; + + /* Number of BD left. */ + atomic_t SendBdLeft; + + T3_SND_BD ShadowSendBd[T3_SEND_RCB_ENTRY_COUNT]; + + /* Counters. */ + LM_RX_COUNTERS RxCounters; + LM_TX_COUNTERS TxCounters; + + /* Host coalescing parameters. */ + LM_UINT32 RxCoalescingTicks; + LM_UINT32 TxCoalescingTicks; + LM_UINT32 RxMaxCoalescedFrames; + LM_UINT32 TxMaxCoalescedFrames; + LM_UINT32 StatsCoalescingTicks; + LM_UINT32 RxCoalescingTicksDuringInt; + LM_UINT32 TxCoalescingTicksDuringInt; + LM_UINT32 RxMaxCoalescedFramesDuringInt; + LM_UINT32 TxMaxCoalescedFramesDuringInt; + + /* DMA water marks. */ + LM_UINT32 DmaMbufLowMark; + LM_UINT32 RxMacMbufLowMark; + LM_UINT32 MbufHighMark; + + /* Status block. */ + PT3_STATUS_BLOCK pStatusBlkVirt; + LM_PHYSICAL_ADDRESS StatusBlkPhy; + + /* Statistics block. */ + PT3_STATS_BLOCK pStatsBlkVirt; + LM_PHYSICAL_ADDRESS StatsBlkPhy; + + /* Current receive mask. */ + LM_UINT32 ReceiveMask; + + /* Task offload capabilities. */ + LM_TASK_OFFLOAD TaskOffloadCap; + + /* Task offload selected. */ + LM_TASK_OFFLOAD TaskToOffload; + + /* Wake up capability. */ + LM_WAKE_UP_MODE WakeUpModeCap; + + /* Wake up capability. */ + LM_WAKE_UP_MODE WakeUpMode; + + /* Flow control. */ + LM_FLOW_CONTROL FlowControlCap; + LM_FLOW_CONTROL FlowControl; + + /* Enable or disable PCI MWI. */ + LM_UINT32 EnableMWI; + + /* Enable 5701 tagged status mode. */ + LM_UINT32 UseTaggedStatus; + + /* NIC will not compute the pseudo header checksum. The driver or OS */ + /* must seed the checksum field with the pseudo checksum. */ + LM_UINT32 NoTxPseudoHdrChksum; + + /* The receive checksum in the BD does not include the pseudo checksum. */ + /* The OS or the driver must calculate the pseudo checksum and add it to */ + /* the checksum in the BD. */ + LM_UINT32 NoRxPseudoHdrChksum; + + /* Current node address. */ + LM_UINT8 NodeAddress[8]; + + /* The adapter's node address. */ + LM_UINT8 PermanentNodeAddress[8]; + + /* Adapter info. */ + LM_UINT16 BusNum; + LM_UINT8 DevNum; + LM_UINT8 FunctNum; + LM_UINT16 PciVendorId; + LM_UINT16 PciDeviceId; + LM_UINT32 BondId; + LM_UINT8 Irq; + LM_UINT8 IntPin; + LM_UINT8 CacheLineSize; + LM_UINT8 PciRevId; +#if PCIX_TARGET_WORKAROUND + LM_UINT32 EnablePciXFix; +#endif + LM_UINT32 UndiFix; /* new, jimmy */ + LM_UINT32 PciCommandStatusWords; + LM_UINT32 ChipRevId; + LM_UINT16 SubsystemVendorId; + LM_UINT16 SubsystemId; +#if 0 /* Jimmy, deleted in new driver */ + LM_UINT32 MemBaseLow; + LM_UINT32 MemBaseHigh; + LM_UINT32 MemBaseSize; +#endif + PLM_UINT8 pMappedMemBase; + + /* Saved PCI configuration registers for restoring after a reset. */ + LM_UINT32 SavedCacheLineReg; + + /* Phy info. */ + LM_UINT32 PhyAddr; + LM_UINT32 PhyId; + + /* Requested phy settings. */ + LM_REQUESTED_MEDIA_TYPE RequestedMediaType; + + /* Disable auto-negotiation. */ + LM_UINT32 DisableAutoNeg; + + /* Ways for the MAC to get link change interrupt. */ + LM_UINT32 PhyIntMode; +#define T3_PHY_INT_MODE_AUTO 0 +#define T3_PHY_INT_MODE_MI_INTERRUPT 1 +#define T3_PHY_INT_MODE_LINK_READY 2 +#define T3_PHY_INT_MODE_AUTO_POLLING 3 + + /* Ways to determine link change status. */ + LM_UINT32 LinkChngMode; +#define T3_LINK_CHNG_MODE_AUTO 0 +#define T3_LINK_CHNG_MODE_USE_STATUS_REG 1 +#define T3_LINK_CHNG_MODE_USE_STATUS_BLOCK 2 + + /* LED mode. */ + LM_UINT32 LedMode; + +#define LED_MODE_AUTO 0 + + /* 5700/01 LED mode. */ +#define LED_MODE_THREE_LINK 1 +#define LED_MODE_LINK10 2 + + /* 5703/02/04 LED mode. */ +#define LED_MODE_OPEN_DRAIN 1 +#define LED_MODE_OUTPUT 2 + + /* WOL Speed */ + LM_UINT32 WolSpeed; +#define WOL_SPEED_10MB 1 +#define WOL_SPEED_100MB 2 + + /* Reset the PHY on initialization. */ + LM_UINT32 ResetPhyOnInit; + + LM_UINT32 RestoreOnWakeUp; + LM_REQUESTED_MEDIA_TYPE WakeUpRequestedMediaType; + LM_UINT32 WakeUpDisableAutoNeg; + + /* Current phy settings. */ + LM_MEDIA_TYPE MediaType; + LM_LINE_SPEED LineSpeed; + LM_LINE_SPEED OldLineSpeed; + LM_DUPLEX_MODE DuplexMode; + LM_STATUS LinkStatus; + LM_UINT32 advertising; /* Jimmy, new! */ + LM_UINT32 advertising1000; /* Jimmy, new! */ + + /* Multicast address list. */ + LM_UINT32 McEntryCount; + LM_UINT8 McTable[LM_MAX_MC_TABLE_SIZE][LM_MC_ENTRY_SIZE]; + + /* Use NIC or Host based send BD. */ + LM_UINT32 NicSendBd; + + /* Athlon fix. */ + LM_UINT32 DelayPciGrant; + + /* Enable OneDmaAtOnce */ + LM_UINT32 OneDmaAtOnce; + + /* Split Mode flags, Jimmy new */ + LM_UINT32 SplitModeEnable; + LM_UINT32 SplitModeMaxReq; + + /* Init flag. */ + LM_BOOL InitDone; + + /* Shutdown flag. Set by the upper module. */ + LM_BOOL ShuttingDown; + + /* Flag to determine whether to call LM_QueueRxPackets or not in */ + /* LM_ResetAdapter routine. */ + LM_BOOL QueueRxPackets; + + LM_UINT32 MbufBase; + LM_UINT32 MbufSize; + + /* TRUE if we have a SERDES PHY. */ + LM_UINT32 EnableTbi; + + /* Ethernet@WireSpeed. */ + LM_UINT32 EnableWireSpeed; + + LM_UINT32 EepromWp; + +#if INCLUDE_TBI_SUPPORT + /* Autoneg state info. */ + AN_STATE_INFO AnInfo; + LM_UINT32 PollTbiLink; + LM_UINT32 IgnoreTbiLinkChange; +#endif + char PartNo[24]; + char BootCodeVer[16]; + char BusSpeedStr[24]; /* Jimmy, new! */ + LM_UINT32 PhyCrcCount; +} LM_DEVICE_BLOCK; + +#define T3_REG_CPU_VIEW 0xc0000000 + +#define T3_BLOCK_DMA_RD (1 << 0) +#define T3_BLOCK_DMA_COMP (1 << 1) +#define T3_BLOCK_RX_BD_INITIATOR (1 << 2) +#define T3_BLOCK_RX_BD_COMP (1 << 3) +#define T3_BLOCK_DMA_WR (1 << 4) +#define T3_BLOCK_MSI_HANDLER (1 << 5) +#define T3_BLOCK_RX_LIST_PLMT (1 << 6) +#define T3_BLOCK_RX_LIST_SELECTOR (1 << 7) +#define T3_BLOCK_RX_DATA_INITIATOR (1 << 8) +#define T3_BLOCK_RX_DATA_COMP (1 << 9) +#define T3_BLOCK_HOST_COALESING (1 << 10) +#define T3_BLOCK_MAC_RX_ENGINE (1 << 11) +#define T3_BLOCK_MBUF_CLUSTER_FREE (1 << 12) +#define T3_BLOCK_SEND_BD_INITIATOR (1 << 13) +#define T3_BLOCK_SEND_BD_COMP (1 << 14) +#define T3_BLOCK_SEND_BD_SELECTOR (1 << 15) +#define T3_BLOCK_SEND_DATA_INITIATOR (1 << 16) +#define T3_BLOCK_SEND_DATA_COMP (1 << 17) +#define T3_BLOCK_MAC_TX_ENGINE (1 << 18) +#define T3_BLOCK_MEM_ARBITOR (1 << 19) +#define T3_BLOCK_MBUF_MANAGER (1 << 20) +#define T3_BLOCK_MAC_GLOBAL (1 << 21) + +#define LM_ENABLE 1 +#define LM_DISABLE 2 + +#define RX_CPU_EVT_SW0 0 +#define RX_CPU_EVT_SW1 1 +#define RX_CPU_EVT_RLP 2 +#define RX_CPU_EVT_SW3 3 +#define RX_CPU_EVT_RLS 4 +#define RX_CPU_EVT_SW4 5 +#define RX_CPU_EVT_RX_BD_COMP 6 +#define RX_CPU_EVT_SW5 7 +#define RX_CPU_EVT_RDI 8 +#define RX_CPU_EVT_DMA_WR 9 +#define RX_CPU_EVT_DMA_RD 10 +#define RX_CPU_EVT_SWQ 11 +#define RX_CPU_EVT_SW6 12 +#define RX_CPU_EVT_RDC 13 +#define RX_CPU_EVT_SW7 14 +#define RX_CPU_EVT_HOST_COALES 15 +#define RX_CPU_EVT_SW8 16 +#define RX_CPU_EVT_HIGH_DMA_WR 17 +#define RX_CPU_EVT_HIGH_DMA_RD 18 +#define RX_CPU_EVT_SW9 19 +#define RX_CPU_EVT_DMA_ATTN 20 +#define RX_CPU_EVT_LOW_P_MBOX 21 +#define RX_CPU_EVT_HIGH_P_MBOX 22 +#define RX_CPU_EVT_SW10 23 +#define RX_CPU_EVT_TX_CPU_ATTN 24 +#define RX_CPU_EVT_MAC_ATTN 25 +#define RX_CPU_EVT_RX_CPU_ATTN 26 +#define RX_CPU_EVT_FLOW_ATTN 27 +#define RX_CPU_EVT_SW11 28 +#define RX_CPU_EVT_TIMER 29 +#define RX_CPU_EVT_SW12 30 +#define RX_CPU_EVT_SW13 31 + +/* RX-CPU event */ +#define RX_CPU_EVENT_SW_EVENT0 (1 << RX_CPU_EVT_SW0) +#define RX_CPU_EVENT_SW_EVENT1 (1 << RX_CPU_EVT_SW1) +#define RX_CPU_EVENT_RLP (1 << RX_CPU_EVT_RLP) +#define RX_CPU_EVENT_SW_EVENT3 (1 << RX_CPU_EVT_SW3) +#define RX_CPU_EVENT_RLS (1 << RX_CPU_EVT_RLS) +#define RX_CPU_EVENT_SW_EVENT4 (1 << RX_CPU_EVT_SW4) +#define RX_CPU_EVENT_RX_BD_COMP (1 << RX_CPU_EVT_RX_BD_COMP) +#define RX_CPU_EVENT_SW_EVENT5 (1 << RX_CPU_EVT_SW5) +#define RX_CPU_EVENT_RDI (1 << RX_CPU_EVT_RDI) +#define RX_CPU_EVENT_DMA_WR (1 << RX_CPU_EVT_DMA_WR) +#define RX_CPU_EVENT_DMA_RD (1 << RX_CPU_EVT_DMA_RD) +#define RX_CPU_EVENT_SWQ (1 << RX_CPU_EVT_SWQ) +#define RX_CPU_EVENT_SW_EVENT6 (1 << RX_CPU_EVT_SW6) +#define RX_CPU_EVENT_RDC (1 << RX_CPU_EVT_RDC) +#define RX_CPU_EVENT_SW_EVENT7 (1 << RX_CPU_EVT_SW7) +#define RX_CPU_EVENT_HOST_COALES (1 << RX_CPU_EVT_HOST_COALES) +#define RX_CPU_EVENT_SW_EVENT8 (1 << RX_CPU_EVT_SW8) +#define RX_CPU_EVENT_HIGH_DMA_WR (1 << RX_CPU_EVT_HIGH_DMA_WR) +#define RX_CPU_EVENT_HIGH_DMA_RD (1 << RX_CPU_EVT_HIGH_DMA_RD) +#define RX_CPU_EVENT_SW_EVENT9 (1 << RX_CPU_EVT_SW9) +#define RX_CPU_EVENT_DMA_ATTN (1 << RX_CPU_EVT_DMA_ATTN) +#define RX_CPU_EVENT_LOW_P_MBOX (1 << RX_CPU_EVT_LOW_P_MBOX) +#define RX_CPU_EVENT_HIGH_P_MBOX (1 << RX_CPU_EVT_HIGH_P_MBOX) +#define RX_CPU_EVENT_SW_EVENT10 (1 << RX_CPU_EVT_SW10) +#define RX_CPU_EVENT_TX_CPU_ATTN (1 << RX_CPU_EVT_TX_CPU_ATTN) +#define RX_CPU_EVENT_MAC_ATTN (1 << RX_CPU_EVT_MAC_ATTN) +#define RX_CPU_EVENT_RX_CPU_ATTN (1 << RX_CPU_EVT_RX_CPU_ATTN) +#define RX_CPU_EVENT_FLOW_ATTN (1 << RX_CPU_EVT_FLOW_ATTN) +#define RX_CPU_EVENT_SW_EVENT11 (1 << RX_CPU_EVT_SW11) +#define RX_CPU_EVENT_TIMER (1 << RX_CPU_EVT_TIMER) +#define RX_CPU_EVENT_SW_EVENT12 (1 << RX_CPU_EVT_SW12) +#define RX_CPU_EVENT_SW_EVENT13 (1 << RX_CPU_EVT_SW13) + +#define RX_CPU_MASK (RX_CPU_EVENT_SW_EVENT0 | \ + RX_CPU_EVENT_RLP | \ + RX_CPU_EVENT_RDI | \ + RX_CPU_EVENT_RDC) + +#define TX_CPU_EVT_SW0 0 +#define TX_CPU_EVT_SW1 1 +#define TX_CPU_EVT_SW2 2 +#define TX_CPU_EVT_SW3 3 +#define TX_CPU_EVT_TX_MAC 4 +#define TX_CPU_EVT_SW4 5 +#define TX_CPU_EVT_SBDC 6 +#define TX_CPU_EVT_SW5 7 +#define TX_CPU_EVT_SDI 8 +#define TX_CPU_EVT_DMA_WR 9 +#define TX_CPU_EVT_DMA_RD 10 +#define TX_CPU_EVT_SWQ 11 +#define TX_CPU_EVT_SW6 12 +#define TX_CPU_EVT_SDC 13 +#define TX_CPU_EVT_SW7 14 +#define TX_CPU_EVT_HOST_COALES 15 +#define TX_CPU_EVT_SW8 16 +#define TX_CPU_EVT_HIGH_DMA_WR 17 +#define TX_CPU_EVT_HIGH_DMA_RD 18 +#define TX_CPU_EVT_SW9 19 +#define TX_CPU_EVT_DMA_ATTN 20 +#define TX_CPU_EVT_LOW_P_MBOX 21 +#define TX_CPU_EVT_HIGH_P_MBOX 22 +#define TX_CPU_EVT_SW10 23 +#define TX_CPU_EVT_RX_CPU_ATTN 24 +#define TX_CPU_EVT_MAC_ATTN 25 +#define TX_CPU_EVT_TX_CPU_ATTN 26 +#define TX_CPU_EVT_FLOW_ATTN 27 +#define TX_CPU_EVT_SW11 28 +#define TX_CPU_EVT_TIMER 29 +#define TX_CPU_EVT_SW12 30 +#define TX_CPU_EVT_SW13 31 + +/* TX-CPU event */ +#define TX_CPU_EVENT_SW_EVENT0 (1 << TX_CPU_EVT_SW0) +#define TX_CPU_EVENT_SW_EVENT1 (1 << TX_CPU_EVT_SW1) +#define TX_CPU_EVENT_SW_EVENT2 (1 << TX_CPU_EVT_SW2) +#define TX_CPU_EVENT_SW_EVENT3 (1 << TX_CPU_EVT_SW3) +#define TX_CPU_EVENT_TX_MAC (1 << TX_CPU_EVT_TX_MAC) +#define TX_CPU_EVENT_SW_EVENT4 (1 << TX_CPU_EVT_SW4) +#define TX_CPU_EVENT_SBDC (1 << TX_CPU_EVT_SBDC) +#define TX_CPU_EVENT_SW_EVENT5 (1 << TX_CPU_EVT_SW5) +#define TX_CPU_EVENT_SDI (1 << TX_CPU_EVT_SDI) +#define TX_CPU_EVENT_DMA_WR (1 << TX_CPU_EVT_DMA_WR) +#define TX_CPU_EVENT_DMA_RD (1 << TX_CPU_EVT_DMA_RD) +#define TX_CPU_EVENT_SWQ (1 << TX_CPU_EVT_SWQ) +#define TX_CPU_EVENT_SW_EVENT6 (1 << TX_CPU_EVT_SW6) +#define TX_CPU_EVENT_SDC (1 << TX_CPU_EVT_SDC) +#define TX_CPU_EVENT_SW_EVENT7 (1 << TX_CPU_EVT_SW7) +#define TX_CPU_EVENT_HOST_COALES (1 << TX_CPU_EVT_HOST_COALES) +#define TX_CPU_EVENT_SW_EVENT8 (1 << TX_CPU_EVT_SW8) +#define TX_CPU_EVENT_HIGH_DMA_WR (1 << TX_CPU_EVT_HIGH_DMA_WR) +#define TX_CPU_EVENT_HIGH_DMA_RD (1 << TX_CPU_EVT_HIGH_DMA_RD) +#define TX_CPU_EVENT_SW_EVENT9 (1 << TX_CPU_EVT_SW9) +#define TX_CPU_EVENT_DMA_ATTN (1 << TX_CPU_EVT_DMA_ATTN) +#define TX_CPU_EVENT_LOW_P_MBOX (1 << TX_CPU_EVT_LOW_P_MBOX) +#define TX_CPU_EVENT_HIGH_P_MBOX (1 << TX_CPU_EVT_HIGH_P_MBOX) +#define TX_CPU_EVENT_SW_EVENT10 (1 << TX_CPU_EVT_SW10) +#define TX_CPU_EVENT_RX_CPU_ATTN (1 << TX_CPU_EVT_RX_CPU_ATTN) +#define TX_CPU_EVENT_MAC_ATTN (1 << TX_CPU_EVT_MAC_ATTN) +#define TX_CPU_EVENT_TX_CPU_ATTN (1 << TX_CPU_EVT_TX_CPU_ATTN) +#define TX_CPU_EVENT_FLOW_ATTN (1 << TX_CPU_EVT_FLOW_ATTN) +#define TX_CPU_EVENT_SW_EVENT11 (1 << TX_CPU_EVT_SW11) +#define TX_CPU_EVENT_TIMER (1 << TX_CPU_EVT_TIMER) +#define TX_CPU_EVENT_SW_EVENT12 (1 << TX_CPU_EVT_SW12) +#define TX_CPU_EVENT_SW_EVENT13 (1 << TX_CPU_EVT_SW13) + +#define TX_CPU_MASK (TX_CPU_EVENT_SW_EVENT0 | \ + TX_CPU_EVENT_SDI | \ + TX_CPU_EVENT_SDC) + +#define T3_FTQ_TYPE1_UNDERFLOW_BIT (1 << 29) +#define T3_FTQ_TYPE1_PASS_BIT (1 << 30) +#define T3_FTQ_TYPE1_SKIP_BIT (1 << 31) + +#define T3_FTQ_TYPE2_UNDERFLOW_BIT (1 << 13) +#define T3_FTQ_TYPE2_PASS_BIT (1 << 14) +#define T3_FTQ_TYPE2_SKIP_BIT (1 << 15) + +#define T3_QID_DMA_READ 1 +#define T3_QID_DMA_HIGH_PRI_READ 2 +#define T3_QID_DMA_COMP_DX 3 +#define T3_QID_SEND_BD_COMP 4 +#define T3_QID_SEND_DATA_INITIATOR 5 +#define T3_QID_DMA_WRITE 6 +#define T3_QID_DMA_HIGH_PRI_WRITE 7 +#define T3_QID_SW_TYPE_1 8 +#define T3_QID_SEND_DATA_COMP 9 +#define T3_QID_HOST_COALESCING 10 +#define T3_QID_MAC_TX 11 +#define T3_QID_MBUF_CLUSTER_FREE 12 +#define T3_QID_RX_BD_COMP 13 +#define T3_QID_RX_LIST_PLM 14 +#define T3_QID_RX_DATA_BD_INITIATOR 15 +#define T3_QID_RX_DATA_COMP 16 +#define T3_QID_SW_TYPE2 17 + +LM_STATUS LM_LoadFirmware (PLM_DEVICE_BLOCK pDevice, + PT3_FWIMG_INFO pFwImg, + LM_UINT32 LoadCpu, LM_UINT32 StartCpu); + +/******************************************************************************/ +/* NIC register read/write macros. */ +/******************************************************************************/ + +#if 0 /* Jimmy */ +/* MAC register access. */ +LM_UINT32 LM_RegRdInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Register); +LM_VOID LM_RegWrInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Register, + LM_UINT32 Value32); + +/* MAC memory access. */ +LM_UINT32 LM_MemRdInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 MemAddr); +LM_VOID LM_MemWrInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 MemAddr, + LM_UINT32 Value32); + +#if PCIX_TARGET_WORKAROUND + +/* use memory-mapped accesses for mailboxes and reads, UNDI accesses + for writes to all other registers */ +#define REG_RD(pDevice, OffsetName) \ + readl(&((pDevice)->pMemView->OffsetName)) + +#define REG_WR(pDevice, OffsetName, Value32) \ + (((OFFSETOF(T3_STD_MEM_MAP, OffsetName) >=0x200 ) && \ + (OFFSETOF(T3_STD_MEM_MAP, OffsetName) <0x400)) || \ + ((pDevice)->EnablePciXFix == FALSE)) ? \ + (void) writel(Value32, &((pDevice)->pMemView->OffsetName)) : \ + LM_RegWrInd(pDevice, OFFSETOF(T3_STD_MEM_MAP, OffsetName), Value32) + +#define MB_REG_RD(pDevice, OffsetName) \ + readl(&((pDevice)->pMemView->OffsetName)) + +#define MB_REG_WR(pDevice, OffsetName, Value32) \ + writel(Value32, &((pDevice)->pMemView->OffsetName)) + +#define REG_RD_OFFSET(pDevice, Offset) \ + readl(&((LM_UINT8 *) (pDevice)->pMemView + Offset)) + +#define REG_WR_OFFSET(pDevice, Offset, Value32) \ + (((Offset >=0x200 ) && (Offset < 0x400)) || \ + ((pDevice)->EnablePciXFix == FALSE)) ? \ + (void) writel(Value32, ((LM_UINT8 *) (pDevice)->pMemView + Offset)) : \ + LM_RegWrInd(pDevice, Offset, Value32) + +#define MEM_RD(pDevice, AddrName) \ + LM_MemRdInd(pDevice, OFFSETOF(T3_FIRST_32K_SRAM, AddrName)) +#define MEM_WR(pDevice, AddrName, Value32) \ + LM_MemWrInd(pDevice, OFFSETOF(T3_FIRST_32K_SRAM, AddrName), Value32) + +#define MEM_RD_OFFSET(pDevice, Offset) \ + LM_MemRdInd(pDevice, Offset) +#define MEM_WR_OFFSET(pDevice, Offset, Value32) \ + LM_MemWrInd(pDevice, Offset, Value32) + +#else /* normal target access path below */ + +/* Register access. */ +#define REG_RD(pDevice, OffsetName) \ + readl(&((pDevice)->pMemView->OffsetName)) +#define REG_WR(pDevice, OffsetName, Value32) \ + writel(Value32, &((pDevice)->pMemView->OffsetName)) + +#define REG_RD_OFFSET(pDevice, Offset) \ + readl(((LM_UINT8 *) (pDevice)->pMemView + Offset)) +#define REG_WR_OFFSET(pDevice, Offset, Value32) \ + writel(Value32, ((LM_UINT8 *) (pDevice)->pMemView + Offset)) + +/* There could be problem access the memory window directly. For now, */ +/* we have to go through the PCI configuration register. */ +#define MEM_RD(pDevice, AddrName) \ + LM_MemRdInd(pDevice, OFFSETOF(T3_FIRST_32K_SRAM, AddrName)) +#define MEM_WR(pDevice, AddrName, Value32) \ + LM_MemWrInd(pDevice, OFFSETOF(T3_FIRST_32K_SRAM, AddrName), Value32) + +#define MEM_RD_OFFSET(pDevice, Offset) \ + LM_MemRdInd(pDevice, Offset) +#define MEM_WR_OFFSET(pDevice, Offset, Value32) \ + LM_MemWrInd(pDevice, Offset, Value32) + +#endif /* PCIX_TARGET_WORKAROUND */ + +#endif /* Jimmy, merging */ + + /* Jimmy...rest of file is new stuff! */ +/******************************************************************************/ +/* NIC register read/write macros. */ +/******************************************************************************/ + +/* MAC register access. */ +LM_UINT32 LM_RegRdInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Register); +LM_VOID LM_RegWrInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 Register, + LM_UINT32 Value32); + +/* MAC memory access. */ +LM_UINT32 LM_MemRdInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 MemAddr); +LM_VOID LM_MemWrInd (PLM_DEVICE_BLOCK pDevice, LM_UINT32 MemAddr, + LM_UINT32 Value32); + +#define MB_REG_WR(pDevice, OffsetName, Value32) \ + ((pDevice)->UndiFix) ? \ + LM_RegWrInd(pDevice, OFFSETOF(T3_STD_MEM_MAP, OffsetName)+0x5600, \ + Value32) : \ + (void) __raw_writel(Value32, &((pDevice)->pMemView->OffsetName)) + +#define MB_REG_RD(pDevice, OffsetName) \ + (((pDevice)->UndiFix) ? \ + LM_RegRdInd(pDevice, OFFSETOF(T3_STD_MEM_MAP, OffsetName)+0x5600) : \ + __raw_readl(&((pDevice)->pMemView->OffsetName))) + +#define REG_RD(pDevice, OffsetName) \ + (((pDevice)->UndiFix) ? \ + LM_RegRdInd(pDevice, OFFSETOF(T3_STD_MEM_MAP, OffsetName)) : \ + __raw_readl(&((pDevice)->pMemView->OffsetName))) + +#if PCIX_TARGET_WORKAROUND + +#define REG_WR(pDevice, OffsetName, Value32) \ + ((pDevice)->EnablePciXFix == FALSE) ? \ + (void) __raw_writel(Value32, &((pDevice)->pMemView->OffsetName)) : \ + LM_RegWrInd(pDevice, OFFSETOF(T3_STD_MEM_MAP, OffsetName), Value32) + +#else + +#define REG_WR(pDevice, OffsetName, Value32) \ + __raw_writel(Value32, &((pDevice)->pMemView->OffsetName)) + +#endif + +#define MEM_RD(pDevice, AddrName) \ + LM_MemRdInd(pDevice, OFFSETOF(T3_FIRST_32K_SRAM, AddrName)) +#define MEM_WR(pDevice, AddrName, Value32) \ + LM_MemWrInd(pDevice, OFFSETOF(T3_FIRST_32K_SRAM, AddrName), Value32) + +#define MEM_RD_OFFSET(pDevice, Offset) \ + LM_MemRdInd(pDevice, Offset) +#define MEM_WR_OFFSET(pDevice, Offset, Value32) \ + LM_MemWrInd(pDevice, Offset, Value32) + +#endif /* TIGON3_H */ diff --git a/roms/u-boot-sam460ex/drivers/net/tsec.c b/roms/u-boot-sam460ex/drivers/net/tsec.c new file mode 100644 index 000000000..5fa6f6100 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/tsec.c @@ -0,0 +1,1971 @@ +/* + * Freescale Three Speed Ethernet Controller driver + * + * This software may be used and distributed according to the + * terms of the GNU Public License, Version 2, incorporated + * herein by reference. + * + * Copyright 2004-2009 Freescale Semiconductor, Inc. + * (C) Copyright 2003, Motorola, Inc. + * author Andy Fleming + * + */ + +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <command.h> +#include <tsec.h> +#include <asm/errno.h> + +#include "miiphy.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define TX_BUF_CNT 2 + +static uint rxIdx; /* index of the current RX buffer */ +static uint txIdx; /* index of the current TX buffer */ + +typedef volatile struct rtxbd { + txbd8_t txbd[TX_BUF_CNT]; + rxbd8_t rxbd[PKTBUFSRX]; +} RTXBD; + +#define MAXCONTROLLERS (8) + +static struct tsec_private *privlist[MAXCONTROLLERS]; +static int num_tsecs = 0; + +#ifdef __GNUC__ +static RTXBD rtx __attribute__ ((aligned(8))); +#else +#error "rtx must be 64-bit aligned" +#endif + +static int tsec_send(struct eth_device *dev, + volatile void *packet, int length); +static int tsec_recv(struct eth_device *dev); +static int tsec_init(struct eth_device *dev, bd_t * bd); +static int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info); +static void tsec_halt(struct eth_device *dev); +static void init_registers(volatile tsec_t * regs); +static void startup_tsec(struct eth_device *dev); +static int init_phy(struct eth_device *dev); +void write_phy_reg(struct tsec_private *priv, uint regnum, uint value); +uint read_phy_reg(struct tsec_private *priv, uint regnum); +static struct phy_info *get_phy_info(struct eth_device *dev); +static void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd); +static void adjust_link(struct eth_device *dev); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) +static int tsec_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value); +static int tsec_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value); +#endif +#ifdef CONFIG_MCAST_TFTP +static int tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set); +#endif + +/* Default initializations for TSEC controllers. */ + +static struct tsec_info_struct tsec_info[] = { +#ifdef CONFIG_TSEC1 + STD_TSEC_INFO(1), /* TSEC1 */ +#endif +#ifdef CONFIG_TSEC2 + STD_TSEC_INFO(2), /* TSEC2 */ +#endif +#ifdef CONFIG_MPC85XX_FEC + { + .regs = (tsec_t *)(TSEC_BASE_ADDR + 0x2000), + .miiregs = (tsec_mdio_t *)(MDIO_BASE_ADDR), + .devname = CONFIG_MPC85XX_FEC_NAME, + .phyaddr = FEC_PHY_ADDR, + .flags = FEC_FLAGS + }, /* FEC */ +#endif +#ifdef CONFIG_TSEC3 + STD_TSEC_INFO(3), /* TSEC3 */ +#endif +#ifdef CONFIG_TSEC4 + STD_TSEC_INFO(4), /* TSEC4 */ +#endif +}; + +int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsecs, int num) +{ + int i; + + for (i = 0; i < num; i++) + tsec_initialize(bis, &tsecs[i]); + + return 0; +} + +int tsec_standard_init(bd_t *bis) +{ + return tsec_eth_init(bis, tsec_info, ARRAY_SIZE(tsec_info)); +} + +/* Initialize device structure. Returns success if PHY + * initialization succeeded (i.e. if it recognizes the PHY) + */ +static int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info) +{ + struct eth_device *dev; + int i; + struct tsec_private *priv; + + dev = (struct eth_device *)malloc(sizeof *dev); + + if (NULL == dev) + return 0; + + memset(dev, 0, sizeof *dev); + + priv = (struct tsec_private *)malloc(sizeof(*priv)); + + if (NULL == priv) + return 0; + + privlist[num_tsecs++] = priv; + priv->regs = tsec_info->regs; + priv->phyregs = tsec_info->miiregs; + priv->phyregs_sgmii = tsec_info->miiregs_sgmii; + + priv->phyaddr = tsec_info->phyaddr; + priv->flags = tsec_info->flags; + + sprintf(dev->name, tsec_info->devname); + dev->iobase = 0; + dev->priv = priv; + dev->init = tsec_init; + dev->halt = tsec_halt; + dev->send = tsec_send; + dev->recv = tsec_recv; +#ifdef CONFIG_MCAST_TFTP + dev->mcast = tsec_mcast_addr; +#endif + + /* Tell u-boot to get the addr from the env */ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = 0; + + eth_register(dev); + + /* Reset the MAC */ + priv->regs->maccfg1 |= MACCFG1_SOFT_RESET; + udelay(2); /* Soft Reset must be asserted for 3 TX clocks */ + priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) + miiphy_register(dev->name, tsec_miiphy_read, tsec_miiphy_write); +#endif + + /* Try to initialize PHY here, and return */ + return init_phy(dev); +} + +/* Initializes data structures and registers for the controller, + * and brings the interface up. Returns the link status, meaning + * that it returns success if the link is up, failure otherwise. + * This allows u-boot to find the first active controller. + */ +static int tsec_init(struct eth_device *dev, bd_t * bd) +{ + uint tempval; + char tmpbuf[MAC_ADDR_LEN]; + int i; + struct tsec_private *priv = (struct tsec_private *)dev->priv; + volatile tsec_t *regs = priv->regs; + + /* Make sure the controller is stopped */ + tsec_halt(dev); + + /* Init MACCFG2. Defaults to GMII */ + regs->maccfg2 = MACCFG2_INIT_SETTINGS; + + /* Init ECNTRL */ + regs->ecntrl = ECNTRL_INIT_SETTINGS; + + /* Copy the station address into the address registers. + * Backwards, because little endian MACS are dumb */ + for (i = 0; i < MAC_ADDR_LEN; i++) { + tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i]; + } + tempval = (tmpbuf[0] << 24) | (tmpbuf[1] << 16) | (tmpbuf[2] << 8) | + tmpbuf[3]; + + regs->macstnaddr1 = tempval; + + tempval = *((uint *) (tmpbuf + 4)); + + regs->macstnaddr2 = tempval; + + /* reset the indices to zero */ + rxIdx = 0; + txIdx = 0; + + /* Clear out (for the most part) the other registers */ + init_registers(regs); + + /* Ready the device for tx/rx */ + startup_tsec(dev); + + /* If there's no link, fail */ + return (priv->link ? 0 : -1); +} + +/* Writes the given phy's reg with value, using the specified MDIO regs */ +static void tsec_local_mdio_write(volatile tsec_mdio_t *phyregs, uint addr, + uint reg, uint value) +{ + int timeout = 1000000; + + phyregs->miimadd = (addr << 8) | reg; + phyregs->miimcon = value; + asm("sync"); + + timeout = 1000000; + while ((phyregs->miimind & MIIMIND_BUSY) && timeout--) ; +} + + +/* Provide the default behavior of writing the PHY of this ethernet device */ +#define write_phy_reg(priv, regnum, value) \ + tsec_local_mdio_write(priv->phyregs,priv->phyaddr,regnum,value) + +/* Reads register regnum on the device's PHY through the + * specified registers. It lowers and raises the read + * command, and waits for the data to become valid (miimind + * notvalid bit cleared), and the bus to cease activity (miimind + * busy bit cleared), and then returns the value + */ +static uint tsec_local_mdio_read(volatile tsec_mdio_t *phyregs, + uint phyid, uint regnum) +{ + uint value; + + /* Put the address of the phy, and the register + * number into MIIMADD */ + phyregs->miimadd = (phyid << 8) | regnum; + + /* Clear the command register, and wait */ + phyregs->miimcom = 0; + asm("sync"); + + /* Initiate a read command, and wait */ + phyregs->miimcom = MIIM_READ_COMMAND; + asm("sync"); + + /* Wait for the the indication that the read is done */ + while ((phyregs->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ; + + /* Grab the value read from the PHY */ + value = phyregs->miimstat; + + return value; +} + +/* #define to provide old read_phy_reg functionality without duplicating code */ +#define read_phy_reg(priv,regnum) \ + tsec_local_mdio_read(priv->phyregs,priv->phyaddr,regnum) + +#define TBIANA_SETTINGS ( \ + TBIANA_ASYMMETRIC_PAUSE \ + | TBIANA_SYMMETRIC_PAUSE \ + | TBIANA_FULL_DUPLEX \ + ) + +/* By default force the TBI PHY into 1000Mbps full duplex when in SGMII mode */ +#ifndef CONFIG_TSEC_TBICR_SETTINGS +#define TBICR_SETTINGS ( \ + TBICR_PHY_RESET \ + | TBICR_FULL_DUPLEX \ + | TBICR_SPEED1_SET \ + ) +#else +#define TBICR_SETTINGS CONFIG_TSEC_TBICR_SETTINGS +#endif /* CONFIG_TSEC_TBICR_SETTINGS */ + +/* Configure the TBI for SGMII operation */ +static void tsec_configure_serdes(struct tsec_private *priv) +{ + /* Access TBI PHY registers at given TSEC register offset as opposed + * to the register offset used for external PHY accesses */ + tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_ANA, + TBIANA_SETTINGS); + tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_TBICON, + TBICON_CLK_SELECT); + tsec_local_mdio_write(priv->phyregs_sgmii, priv->regs->tbipa, TBI_CR, + TBICR_SETTINGS); +} + +/* Discover which PHY is attached to the device, and configure it + * properly. If the PHY is not recognized, then return 0 + * (failure). Otherwise, return 1 + */ +static int init_phy(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct phy_info *curphy; + volatile tsec_t *regs = priv->regs; + + /* Assign a Physical address to the TBI */ + regs->tbipa = CONFIG_SYS_TBIPA_VALUE; + asm("sync"); + + /* Reset MII (due to new addresses) */ + priv->phyregs->miimcfg = MIIMCFG_RESET; + asm("sync"); + priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE; + asm("sync"); + while (priv->phyregs->miimind & MIIMIND_BUSY) ; + + /* Get the cmd structure corresponding to the attached + * PHY */ + curphy = get_phy_info(dev); + + if (curphy == NULL) { + priv->phyinfo = NULL; + printf("%s: No PHY found\n", dev->name); + + return 0; + } + + if (regs->ecntrl & ECNTRL_SGMII_MODE) + tsec_configure_serdes(priv); + + priv->phyinfo = curphy; + + phy_run_commands(priv, priv->phyinfo->config); + + return 1; +} + +/* + * Returns which value to write to the control register. + * For 10/100, the value is slightly different + */ +static uint mii_cr_init(uint mii_reg, struct tsec_private * priv) +{ + if (priv->flags & TSEC_GIGABIT) + return MIIM_CONTROL_INIT; + else + return MIIM_CR_INIT; +} + +/* + * Wait for auto-negotiation to complete, then determine link + */ +static uint mii_parse_sr(uint mii_reg, struct tsec_private * priv) +{ + /* + * Wait if the link is up, and autonegotiation is in progress + * (ie - we're capable and it's not done) + */ + mii_reg = read_phy_reg(priv, MIIM_STATUS); + if ((mii_reg & PHY_BMSR_AUTN_ABLE) && !(mii_reg & PHY_BMSR_AUTN_COMP)) { + int i = 0; + + puts("Waiting for PHY auto negotiation to complete"); + while (!(mii_reg & PHY_BMSR_AUTN_COMP)) { + /* + * Timeout reached ? + */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + priv->link = 0; + return 0; + } + + if (ctrlc()) { + puts("user interrupt!\n"); + priv->link = 0; + return -EINTR; + } + + if ((i++ % 1000) == 0) { + putc('.'); + } + udelay(1000); /* 1 ms */ + mii_reg = read_phy_reg(priv, MIIM_STATUS); + } + puts(" done\n"); + + /* Link status bit is latched low, read it again */ + mii_reg = read_phy_reg(priv, MIIM_STATUS); + + udelay(500000); /* another 500 ms (results in faster booting) */ + } + + priv->link = mii_reg & MIIM_STATUS_LINK ? 1 : 0; + + return 0; +} + +/* Generic function which updates the speed and duplex. If + * autonegotiation is enabled, it uses the AND of the link + * partner's advertised capabilities and our advertised + * capabilities. If autonegotiation is disabled, we use the + * appropriate bits in the control register. + * + * Stolen from Linux's mii.c and phy_device.c + */ +static uint mii_parse_link(uint mii_reg, struct tsec_private *priv) +{ + /* We're using autonegotiation */ + if (mii_reg & PHY_BMSR_AUTN_ABLE) { + uint lpa = 0; + uint gblpa = 0; + + /* Check for gigabit capability */ + if (mii_reg & PHY_BMSR_EXT) { + /* We want a list of states supported by + * both PHYs in the link + */ + gblpa = read_phy_reg(priv, PHY_1000BTSR); + gblpa &= read_phy_reg(priv, PHY_1000BTCR) << 2; + } + + /* Set the baseline so we only have to set them + * if they're different + */ + priv->speed = 10; + priv->duplexity = 0; + + /* Check the gigabit fields */ + if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) { + priv->speed = 1000; + + if (gblpa & PHY_1000BTSR_1000FD) + priv->duplexity = 1; + + /* We're done! */ + return 0; + } + + lpa = read_phy_reg(priv, PHY_ANAR); + lpa &= read_phy_reg(priv, PHY_ANLPAR); + + if (lpa & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) { + priv->speed = 100; + + if (lpa & PHY_ANLPAR_TXFD) + priv->duplexity = 1; + + } else if (lpa & PHY_ANLPAR_10FD) + priv->duplexity = 1; + } else { + uint bmcr = read_phy_reg(priv, PHY_BMCR); + + priv->speed = 10; + priv->duplexity = 0; + + if (bmcr & PHY_BMCR_DPLX) + priv->duplexity = 1; + + if (bmcr & PHY_BMCR_1000_MBPS) + priv->speed = 1000; + else if (bmcr & PHY_BMCR_100_MBPS) + priv->speed = 100; + } + + return 0; +} + +/* + * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain + * circumstances. eg a gigabit TSEC connected to a gigabit switch with + * a 4-wire ethernet cable. Both ends advertise gigabit, but can't + * link. "Ethernet@Wirespeed" reduces advertised speed until link + * can be achieved. + */ +static uint mii_BCM54xx_wirespeed(uint mii_reg, struct tsec_private *priv) +{ + return (read_phy_reg(priv, mii_reg) & 0x8FFF) | 0x8010; +} + +/* + * Parse the BCM54xx status register for speed and duplex information. + * The linux sungem_phy has this information, but in a table format. + */ +static uint mii_parse_BCM54xx_sr(uint mii_reg, struct tsec_private *priv) +{ + /* If there is no link, speed and duplex don't matter */ + if (!priv->link) + return 0; + + switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >> + MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) { + case 1: + priv->duplexity = 0; + priv->speed = 10; + break; + case 2: + priv->duplexity = 1; + priv->speed = 10; + break; + case 3: + priv->duplexity = 0; + priv->speed = 100; + break; + case 5: + priv->duplexity = 1; + priv->speed = 100; + break; + case 6: + priv->duplexity = 0; + priv->speed = 1000; + break; + case 7: + priv->duplexity = 1; + priv->speed = 1000; + break; + default: + printf("Auto-neg error, defaulting to 10BT/HD\n"); + priv->duplexity = 0; + priv->speed = 10; + break; + } + + return 0; +} + +/* + * Find out if PHY is in copper or serdes mode by looking at Expansion Reg + * 0x42 - "Operating Mode Status Register" + */ +static int BCM8482_is_serdes(struct tsec_private *priv) +{ + u16 val; + int serdes = 0; + + write_phy_reg(priv, MIIM_BCM54XX_EXP_SEL, MIIM_BCM54XX_EXP_SEL_ER | 0x42); + val = read_phy_reg(priv, MIIM_BCM54XX_EXP_DATA); + + switch (val & 0x1f) { + case 0x0d: /* RGMII-to-100Base-FX */ + case 0x0e: /* RGMII-to-SGMII */ + case 0x0f: /* RGMII-to-SerDes */ + case 0x12: /* SGMII-to-SerDes */ + case 0x13: /* SGMII-to-100Base-FX */ + case 0x16: /* SerDes-to-Serdes */ + serdes = 1; + break; + case 0x6: /* RGMII-to-Copper */ + case 0x14: /* SGMII-to-Copper */ + case 0x17: /* SerDes-to-Copper */ + break; + default: + printf("ERROR, invalid PHY mode (0x%x\n)", val); + break; + } + + return serdes; +} + +/* + * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating + * Mode Status Register" + */ +uint mii_parse_BCM5482_serdes_sr(struct tsec_private *priv) +{ + u16 val; + int i = 0; + + /* Wait 1s for link - Clause 37 autonegotiation happens very fast */ + while (1) { + write_phy_reg(priv, MIIM_BCM54XX_EXP_SEL, + MIIM_BCM54XX_EXP_SEL_ER | 0x42); + val = read_phy_reg(priv, MIIM_BCM54XX_EXP_DATA); + + if (val & 0x8000) + break; + + if (i++ > 1000) { + priv->link = 0; + return 1; + } + + udelay(1000); /* 1 ms */ + } + + priv->link = 1; + switch ((val >> 13) & 0x3) { + case (0x00): + priv->speed = 10; + break; + case (0x01): + priv->speed = 100; + break; + case (0x02): + priv->speed = 1000; + break; + } + + priv->duplexity = (val & 0x1000) == 0x1000; + + return 0; +} + +/* + * Figure out if BCM5482 is in serdes or copper mode and determine link + * configuration accordingly + */ +static uint mii_parse_BCM5482_sr(uint mii_reg, struct tsec_private *priv) +{ + if (BCM8482_is_serdes(priv)) { + mii_parse_BCM5482_serdes_sr(priv); + priv->flags |= TSEC_FIBER; + } else { + /* Wait for auto-negotiation to complete or fail */ + mii_parse_sr(mii_reg, priv); + + /* Parse BCM54xx copper aux status register */ + mii_reg = read_phy_reg(priv, MIIM_BCM54xx_AUXSTATUS); + mii_parse_BCM54xx_sr(mii_reg, priv); + } + + return 0; +} + +/* Parse the 88E1011's status register for speed and duplex + * information + */ +static uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private * priv) +{ + uint speed; + + mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS); + + if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) && + !(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { + int i = 0; + + puts("Waiting for PHY realtime link"); + while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { + /* Timeout reached ? */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + priv->link = 0; + break; + } + + if ((i++ % 1000) == 0) { + putc('.'); + } + udelay(1000); /* 1 ms */ + mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS); + } + puts(" done\n"); + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_88E1011_PHYSTAT_LINK) + priv->link = 1; + else + priv->link = 0; + } + + if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX) + priv->duplexity = 1; + else + priv->duplexity = 0; + + speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED); + + switch (speed) { + case MIIM_88E1011_PHYSTAT_GBIT: + priv->speed = 1000; + break; + case MIIM_88E1011_PHYSTAT_100: + priv->speed = 100; + break; + default: + priv->speed = 10; + } + + return 0; +} + +/* Parse the RTL8211B's status register for speed and duplex + * information + */ +static uint mii_parse_RTL8211B_sr(uint mii_reg, struct tsec_private * priv) +{ + uint speed; + + mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS); + if (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) { + int i = 0; + + /* in case of timeout ->link is cleared */ + priv->link = 1; + puts("Waiting for PHY realtime link"); + while (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) { + /* Timeout reached ? */ + if (i > PHY_AUTONEGOTIATE_TIMEOUT) { + puts(" TIMEOUT !\n"); + priv->link = 0; + break; + } + + if ((i++ % 1000) == 0) { + putc('.'); + } + udelay(1000); /* 1 ms */ + mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS); + } + puts(" done\n"); + udelay(500000); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_RTL8211B_PHYSTAT_LINK) + priv->link = 1; + else + priv->link = 0; + } + + if (mii_reg & MIIM_RTL8211B_PHYSTAT_DUPLEX) + priv->duplexity = 1; + else + priv->duplexity = 0; + + speed = (mii_reg & MIIM_RTL8211B_PHYSTAT_SPEED); + + switch (speed) { + case MIIM_RTL8211B_PHYSTAT_GBIT: + priv->speed = 1000; + break; + case MIIM_RTL8211B_PHYSTAT_100: + priv->speed = 100; + break; + default: + priv->speed = 10; + } + + return 0; +} + +/* Parse the cis8201's status register for speed and duplex + * information + */ +static uint mii_parse_cis8201(uint mii_reg, struct tsec_private * priv) +{ + uint speed; + + if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX) + priv->duplexity = 1; + else + priv->duplexity = 0; + + speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED; + switch (speed) { + case MIIM_CIS8201_AUXCONSTAT_GBIT: + priv->speed = 1000; + break; + case MIIM_CIS8201_AUXCONSTAT_100: + priv->speed = 100; + break; + default: + priv->speed = 10; + break; + } + + return 0; +} + +/* Parse the vsc8244's status register for speed and duplex + * information + */ +static uint mii_parse_vsc8244(uint mii_reg, struct tsec_private * priv) +{ + uint speed; + + if (mii_reg & MIIM_VSC8244_AUXCONSTAT_DUPLEX) + priv->duplexity = 1; + else + priv->duplexity = 0; + + speed = mii_reg & MIIM_VSC8244_AUXCONSTAT_SPEED; + switch (speed) { + case MIIM_VSC8244_AUXCONSTAT_GBIT: + priv->speed = 1000; + break; + case MIIM_VSC8244_AUXCONSTAT_100: + priv->speed = 100; + break; + default: + priv->speed = 10; + break; + } + + return 0; +} + +/* Parse the DM9161's status register for speed and duplex + * information + */ +static uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private * priv) +{ + if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H)) + priv->speed = 100; + else + priv->speed = 10; + + if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F)) + priv->duplexity = 1; + else + priv->duplexity = 0; + + return 0; +} + +/* + * Hack to write all 4 PHYs with the LED values + */ +static uint mii_cis8204_fixled(uint mii_reg, struct tsec_private * priv) +{ + uint phyid; + volatile tsec_mdio_t *regbase = priv->phyregs; + int timeout = 1000000; + + for (phyid = 0; phyid < 4; phyid++) { + regbase->miimadd = (phyid << 8) | mii_reg; + regbase->miimcon = MIIM_CIS8204_SLEDCON_INIT; + asm("sync"); + + timeout = 1000000; + while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ; + } + + return MIIM_CIS8204_SLEDCON_INIT; +} + +static uint mii_cis8204_setmode(uint mii_reg, struct tsec_private * priv) +{ + if (priv->flags & TSEC_REDUCED) + return MIIM_CIS8204_EPHYCON_INIT | MIIM_CIS8204_EPHYCON_RGMII; + else + return MIIM_CIS8204_EPHYCON_INIT; +} + +static uint mii_m88e1111s_setmode(uint mii_reg, struct tsec_private *priv) +{ + uint mii_data = read_phy_reg(priv, mii_reg); + + if (priv->flags & TSEC_REDUCED) + mii_data = (mii_data & 0xfff0) | 0x000b; + return mii_data; +} + +/* Initialized required registers to appropriate values, zeroing + * those we don't care about (unless zero is bad, in which case, + * choose a more appropriate value) + */ +static void init_registers(volatile tsec_t * regs) +{ + /* Clear IEVENT */ + regs->ievent = IEVENT_INIT_CLEAR; + + regs->imask = IMASK_INIT_CLEAR; + + regs->hash.iaddr0 = 0; + regs->hash.iaddr1 = 0; + regs->hash.iaddr2 = 0; + regs->hash.iaddr3 = 0; + regs->hash.iaddr4 = 0; + regs->hash.iaddr5 = 0; + regs->hash.iaddr6 = 0; + regs->hash.iaddr7 = 0; + + regs->hash.gaddr0 = 0; + regs->hash.gaddr1 = 0; + regs->hash.gaddr2 = 0; + regs->hash.gaddr3 = 0; + regs->hash.gaddr4 = 0; + regs->hash.gaddr5 = 0; + regs->hash.gaddr6 = 0; + regs->hash.gaddr7 = 0; + + regs->rctrl = 0x00000000; + + /* Init RMON mib registers */ + memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t)); + + regs->rmon.cam1 = 0xffffffff; + regs->rmon.cam2 = 0xffffffff; + + regs->mrblr = MRBLR_INIT_SETTINGS; + + regs->minflr = MINFLR_INIT_SETTINGS; + + regs->attr = ATTR_INIT_SETTINGS; + regs->attreli = ATTRELI_INIT_SETTINGS; + +} + +/* Configure maccfg2 based on negotiated speed and duplex + * reported by PHY handling code + */ +static void adjust_link(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + volatile tsec_t *regs = priv->regs; + + if (priv->link) { + if (priv->duplexity != 0) + regs->maccfg2 |= MACCFG2_FULL_DUPLEX; + else + regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX); + + switch (priv->speed) { + case 1000: + regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF)) + | MACCFG2_GMII); + break; + case 100: + case 10: + regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF)) + | MACCFG2_MII); + + /* Set R100 bit in all modes although + * it is only used in RGMII mode + */ + if (priv->speed == 100) + regs->ecntrl |= ECNTRL_R100; + else + regs->ecntrl &= ~(ECNTRL_R100); + break; + default: + printf("%s: Speed was bad\n", dev->name); + break; + } + + printf("Speed: %d, %s duplex%s\n", priv->speed, + (priv->duplexity) ? "full" : "half", + (priv->flags & TSEC_FIBER) ? ", fiber mode" : ""); + + } else { + printf("%s: No link.\n", dev->name); + } +} + +/* Set up the buffers and their descriptors, and bring up the + * interface + */ +static void startup_tsec(struct eth_device *dev) +{ + int i; + struct tsec_private *priv = (struct tsec_private *)dev->priv; + volatile tsec_t *regs = priv->regs; + + /* Point to the buffer descriptors */ + regs->tbase = (unsigned int)(&rtx.txbd[txIdx]); + regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]); + + /* Initialize the Rx Buffer descriptors */ + for (i = 0; i < PKTBUFSRX; i++) { + rtx.rxbd[i].status = RXBD_EMPTY; + rtx.rxbd[i].length = 0; + rtx.rxbd[i].bufPtr = (uint) NetRxPackets[i]; + } + rtx.rxbd[PKTBUFSRX - 1].status |= RXBD_WRAP; + + /* Initialize the TX Buffer Descriptors */ + for (i = 0; i < TX_BUF_CNT; i++) { + rtx.txbd[i].status = 0; + rtx.txbd[i].length = 0; + rtx.txbd[i].bufPtr = 0; + } + rtx.txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP; + + /* Start up the PHY */ + if(priv->phyinfo) + phy_run_commands(priv, priv->phyinfo->startup); + + adjust_link(dev); + + /* Enable Transmit and Receive */ + regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN); + + /* Tell the DMA it is clear to go */ + regs->dmactrl |= DMACTRL_INIT_SETTINGS; + regs->tstat = TSTAT_CLEAR_THALT; + regs->rstat = RSTAT_CLEAR_RHALT; + regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); +} + +/* This returns the status bits of the device. The return value + * is never checked, and this is what the 8260 driver did, so we + * do the same. Presumably, this would be zero if there were no + * errors + */ +static int tsec_send(struct eth_device *dev, volatile void *packet, int length) +{ + int i; + int result = 0; + struct tsec_private *priv = (struct tsec_private *)dev->priv; + volatile tsec_t *regs = priv->regs; + + /* Find an empty buffer descriptor */ + for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) { + if (i >= TOUT_LOOP) { + debug("%s: tsec: tx buffers full\n", dev->name); + return result; + } + } + + rtx.txbd[txIdx].bufPtr = (uint) packet; + rtx.txbd[txIdx].length = length; + rtx.txbd[txIdx].status |= + (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT); + + /* Tell the DMA to go */ + regs->tstat = TSTAT_CLEAR_THALT; + + /* Wait for buffer to be transmitted */ + for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) { + if (i >= TOUT_LOOP) { + debug("%s: tsec: tx error\n", dev->name); + return result; + } + } + + txIdx = (txIdx + 1) % TX_BUF_CNT; + result = rtx.txbd[txIdx].status & TXBD_STATS; + + return result; +} + +static int tsec_recv(struct eth_device *dev) +{ + int length; + struct tsec_private *priv = (struct tsec_private *)dev->priv; + volatile tsec_t *regs = priv->regs; + + while (!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) { + + length = rtx.rxbd[rxIdx].length; + + /* Send the packet up if there were no errors */ + if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) { + NetReceive(NetRxPackets[rxIdx], length - 4); + } else { + printf("Got error %x\n", + (rtx.rxbd[rxIdx].status & RXBD_STATS)); + } + + rtx.rxbd[rxIdx].length = 0; + + /* Set the wrap bit if this is the last element in the list */ + rtx.rxbd[rxIdx].status = + RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0); + + rxIdx = (rxIdx + 1) % PKTBUFSRX; + } + + if (regs->ievent & IEVENT_BSY) { + regs->ievent = IEVENT_BSY; + regs->rstat = RSTAT_CLEAR_RHALT; + } + + return -1; + +} + +/* Stop the interface */ +static void tsec_halt(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + volatile tsec_t *regs = priv->regs; + + regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); + regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS); + + while ((regs->ievent & (IEVENT_GRSC | IEVENT_GTSC)) + != (IEVENT_GRSC | IEVENT_GTSC)) ; + + regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN); + + /* Shut down the PHY, as needed */ + if(priv->phyinfo) + phy_run_commands(priv, priv->phyinfo->shutdown); +} + +static struct phy_info phy_info_M88E1149S = { + 0x1410ca, + "Marvell 88E1149S", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {0x1d, 0x1f, NULL}, + {0x1e, 0x200c, NULL}, + {0x1d, 0x5, NULL}, + {0x1e, 0x0, NULL}, + {0x1e, 0x100, NULL}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +/* The 5411 id is 0x206070, the 5421 is 0x2060e0 */ +static struct phy_info phy_info_BCM5461S = { + 0x02060c1, /* 5461 ID */ + "Broadcom BCM5461S", + 0, /* not clear to me what minor revisions we can shift away */ + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_BCM54xx_AUXSTATUS, miim_read, &mii_parse_BCM54xx_sr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_BCM5464S = { + 0x02060b1, /* 5464 ID */ + "Broadcom BCM5464S", + 0, /* not clear to me what minor revisions we can shift away */ + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_BCM54xx_AUXSTATUS, miim_read, &mii_parse_BCM54xx_sr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_BCM5482S = { + 0x0143bcb, + "Broadcom BCM5482S", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + /* Setup read from auxilary control shadow register 7 */ + {MIIM_BCM54xx_AUXCNTL, MIIM_BCM54xx_AUXCNTL_ENCODE(7), NULL}, + /* Read Misc Control register and or in Ethernet@Wirespeed */ + {MIIM_BCM54xx_AUXCNTL, 0, &mii_BCM54xx_wirespeed}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + /* Initial config/enable of secondary SerDes interface */ + {MIIM_BCM54XX_SHD, MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf), NULL}, + /* Write intial value to secondary SerDes Contol */ + {MIIM_BCM54XX_EXP_SEL, MIIM_BCM54XX_EXP_SEL_SSD | 0, NULL}, + {MIIM_BCM54XX_EXP_DATA, MIIM_CONTROL_RESTART, NULL}, + /* Enable copper/fiber auto-detect */ + {MIIM_BCM54XX_SHD, MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201)}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Determine copper/fiber, auto-negotiate, and read the result */ + {MIIM_STATUS, miim_read, &mii_parse_BCM5482_sr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_M88E1011S = { + 0x01410c6, + "Marvell 88E1011S", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {0x1d, 0x1f, NULL}, + {0x1e, 0x200c, NULL}, + {0x1d, 0x5, NULL}, + {0x1e, 0x0, NULL}, + {0x1e, 0x100, NULL}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_M88E1111S = { + 0x01410cc, + "Marvell 88E1111S", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {0x1b, 0x848f, &mii_m88e1111s_setmode}, + {0x14, 0x0cd2, NULL}, /* Delay RGMII TX and RX */ + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_M88E1118 = { + 0x01410e1, + "Marvell 88E1118", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {0x16, 0x0002, NULL}, /* Change Page Number */ + {0x15, 0x1070, NULL}, /* Delay RGMII TX and RX */ + {0x16, 0x0003, NULL}, /* Change Page Number */ + {0x10, 0x021e, NULL}, /* Adjust LED control */ + {0x16, 0x0000, NULL}, /* Change Page Number */ + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + {0x16, 0x0000, NULL}, /* Change Page Number */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_88E1011_PHY_STATUS, miim_read, + &mii_parse_88E1011_psr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +/* + * Since to access LED register we need do switch the page, we + * do LED configuring in the miim_read-like function as follows + */ +static uint mii_88E1121_set_led (uint mii_reg, struct tsec_private *priv) +{ + uint pg; + + /* Switch the page to access the led register */ + pg = read_phy_reg(priv, MIIM_88E1121_PHY_PAGE); + write_phy_reg(priv, MIIM_88E1121_PHY_PAGE, MIIM_88E1121_PHY_LED_PAGE); + + /* Configure leds */ + write_phy_reg(priv, MIIM_88E1121_PHY_LED_CTRL, + MIIM_88E1121_PHY_LED_DEF); + + /* Restore the page pointer */ + write_phy_reg(priv, MIIM_88E1121_PHY_PAGE, pg); + return 0; +} + +static struct phy_info phy_info_M88E1121R = { + 0x01410cb, + "Marvell 88E1121R", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + /* Configure leds */ + {MIIM_88E1121_PHY_LED_CTRL, miim_read, &mii_88E1121_set_led}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + /* Disable IRQs and de-assert interrupt */ + {MIIM_88E1121_PHY_IRQ_EN, 0, NULL}, + {MIIM_88E1121_PHY_IRQ_STATUS, miim_read, NULL}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + {MIIM_STATUS, miim_read, &mii_parse_sr}, + {MIIM_STATUS, miim_read, &mii_parse_link}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static unsigned int m88e1145_setmode(uint mii_reg, struct tsec_private *priv) +{ + uint mii_data = read_phy_reg(priv, mii_reg); + + /* Setting MIIM_88E1145_PHY_EXT_CR */ + if (priv->flags & TSEC_REDUCED) + return mii_data | + MIIM_M88E1145_RGMII_RX_DELAY | MIIM_M88E1145_RGMII_TX_DELAY; + else + return mii_data; +} + +static struct phy_info phy_info_M88E1145 = { + 0x01410cd, + "Marvell 88E1145", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + + /* Errata E0, E1 */ + {29, 0x001b, NULL}, + {30, 0x418f, NULL}, + {29, 0x0016, NULL}, + {30, 0xa2da, NULL}, + + /* Configure the PHY */ + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_88E1011_PHY_SCR, MIIM_88E1011_PHY_MDI_X_AUTO, NULL}, + {MIIM_88E1145_PHY_EXT_CR, 0, &m88e1145_setmode}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, NULL}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + {MIIM_88E1111_PHY_LED_CONTROL, MIIM_88E1111_PHY_LED_DIRECT, NULL}, + /* Read the Status */ + {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_cis8204 = { + 0x3f11, + "Cicada Cis8204", + 6, + (struct phy_cmd[]) { /* config */ + /* Override PHY config settings */ + {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {MIIM_CIS8204_SLED_CON, MIIM_CIS8204_SLEDCON_INIT, + &mii_cis8204_fixled}, + {MIIM_CIS8204_EPHY_CON, MIIM_CIS8204_EPHYCON_INIT, + &mii_cis8204_setmode}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Read the Status (2x to make sure link is right) */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +/* Cicada 8201 */ +static struct phy_info phy_info_cis8201 = { + 0xfc41, + "CIS8201", + 4, + (struct phy_cmd[]) { /* config */ + /* Override PHY config settings */ + {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, + /* Set up the interface mode */ + {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL}, + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Read the Status (2x to make sure link is right) */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_VSC8211 = { + 0xfc4b, + "Vitesse VSC8211", + 4, + (struct phy_cmd[]) { /* config */ + /* Override PHY config settings */ + {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, + /* Set up the interface mode */ + {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL}, + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Read the Status (2x to make sure link is right) */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_VSC8244 = { + 0x3f1b, + "Vitesse VSC8244", + 6, + (struct phy_cmd[]) { /* config */ + /* Override PHY config settings */ + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Read the Status (2x to make sure link is right) */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_VSC8244_AUX_CONSTAT, miim_read, &mii_parse_vsc8244}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_VSC8641 = { + 0x7043, + "Vitesse VSC8641", + 4, + (struct phy_cmd[]) { /* config */ + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Read the Status (2x to make sure link is right) */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_VSC8244_AUX_CONSTAT, miim_read, &mii_parse_vsc8244}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_VSC8221 = { + 0xfc55, + "Vitesse VSC8221", + 4, + (struct phy_cmd[]) { /* config */ + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Read the Status (2x to make sure link is right) */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_VSC8244_AUX_CONSTAT, miim_read, &mii_parse_vsc8244}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_VSC8601 = { + 0x00007042, + "Vitesse VSC8601", + 4, + (struct phy_cmd[]) { /* config */ + /* Override PHY config settings */ + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, +#ifdef CONFIG_SYS_VSC8601_SKEWFIX + {MIIM_VSC8601_EPHY_CON,MIIM_VSC8601_EPHY_CON_INIT_SKEW,NULL}, +#if defined(CONFIG_SYS_VSC8601_SKEW_TX) && defined(CONFIG_SYS_VSC8601_SKEW_RX) + {MIIM_EXT_PAGE_ACCESS,1,NULL}, +#define VSC8101_SKEW \ + (CONFIG_SYS_VSC8601_SKEW_TX << 14) | (CONFIG_SYS_VSC8601_SKEW_RX << 12) + {MIIM_VSC8601_SKEW_CTRL,VSC8101_SKEW,NULL}, + {MIIM_EXT_PAGE_ACCESS,0,NULL}, +#endif +#endif + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESTART, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Read the Status (2x to make sure link is right) */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_VSC8244_AUX_CONSTAT, miim_read, &mii_parse_vsc8244}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_dm9161 = { + 0x0181b88, + "Davicom DM9161E", + 4, + (struct phy_cmd[]) { /* config */ + {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL}, + /* Do not bypass the scrambler/descrambler */ + {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL}, + /* Clear 10BTCSR to default */ + {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL}, + /* Configure some basic stuff */ + {MIIM_CONTROL, MIIM_CR_INIT, NULL}, + /* Restart Auto Negotiation */ + {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_DM9161_SCSR, miim_read, &mii_parse_dm9161_scsr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +/* a generic flavor. */ +static struct phy_info phy_info_generic = { + 0, + "Unknown/Generic PHY", + 32, + (struct phy_cmd[]) { /* config */ + {PHY_BMCR, PHY_BMCR_RESET, NULL}, + {PHY_BMCR, PHY_BMCR_AUTON|PHY_BMCR_RST_NEG, NULL}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + {PHY_BMSR, miim_read, NULL}, + {PHY_BMSR, miim_read, &mii_parse_sr}, + {PHY_BMSR, miim_read, &mii_parse_link}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + } +}; + +static uint mii_parse_lxt971_sr2(uint mii_reg, struct tsec_private *priv) +{ + unsigned int speed; + if (priv->link) { + speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK; + + switch (speed) { + case MIIM_LXT971_SR2_10HDX: + priv->speed = 10; + priv->duplexity = 0; + break; + case MIIM_LXT971_SR2_10FDX: + priv->speed = 10; + priv->duplexity = 1; + break; + case MIIM_LXT971_SR2_100HDX: + priv->speed = 100; + priv->duplexity = 0; + break; + default: + priv->speed = 100; + priv->duplexity = 1; + } + } else { + priv->speed = 0; + priv->duplexity = 0; + } + + return 0; +} + +static struct phy_info phy_info_lxt971 = { + 0x0001378e, + "LXT971", + 4, + (struct phy_cmd[]) { /* config */ + {MIIM_CR, MIIM_CR_INIT, mii_cr_init}, /* autonegotiate */ + {miim_end,} + }, + (struct phy_cmd[]) { /* startup - enable interrupts */ + /* { 0x12, 0x00f2, NULL }, */ + {MIIM_STATUS, miim_read, NULL}, + {MIIM_STATUS, miim_read, &mii_parse_sr}, + {MIIM_LXT971_SR2, miim_read, &mii_parse_lxt971_sr2}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown - disable interrupts */ + {miim_end,} + }, +}; + +/* Parse the DP83865's link and auto-neg status register for speed and duplex + * information + */ +static uint mii_parse_dp83865_lanr(uint mii_reg, struct tsec_private *priv) +{ + switch (mii_reg & MIIM_DP83865_SPD_MASK) { + + case MIIM_DP83865_SPD_1000: + priv->speed = 1000; + break; + + case MIIM_DP83865_SPD_100: + priv->speed = 100; + break; + + default: + priv->speed = 10; + break; + + } + + if (mii_reg & MIIM_DP83865_DPX_FULL) + priv->duplexity = 1; + else + priv->duplexity = 0; + + return 0; +} + +static struct phy_info phy_info_dp83865 = { + 0x20005c7, + "NatSemi DP83865", + 4, + (struct phy_cmd[]) { /* config */ + {MIIM_CONTROL, MIIM_DP83865_CR_INIT, NULL}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the link and auto-neg status */ + {MIIM_DP83865_LANR, miim_read, &mii_parse_dp83865_lanr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info phy_info_rtl8211b = { + 0x001cc91, + "RealTek RTL8211B", + 4, + (struct phy_cmd[]) { /* config */ + /* Reset and configure the PHY */ + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, + {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, + {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, + {miim_end,} + }, + (struct phy_cmd[]) { /* startup */ + /* Status is read once to clear old link state */ + {MIIM_STATUS, miim_read, NULL}, + /* Auto-negotiate */ + {MIIM_STATUS, miim_read, &mii_parse_sr}, + /* Read the status */ + {MIIM_RTL8211B_PHY_STATUS, miim_read, &mii_parse_RTL8211B_sr}, + {miim_end,} + }, + (struct phy_cmd[]) { /* shutdown */ + {miim_end,} + }, +}; + +static struct phy_info *phy_info[] = { + &phy_info_cis8204, + &phy_info_cis8201, + &phy_info_BCM5461S, + &phy_info_BCM5464S, + &phy_info_BCM5482S, + &phy_info_M88E1011S, + &phy_info_M88E1111S, + &phy_info_M88E1118, + &phy_info_M88E1121R, + &phy_info_M88E1145, + &phy_info_M88E1149S, + &phy_info_dm9161, + &phy_info_lxt971, + &phy_info_VSC8211, + &phy_info_VSC8244, + &phy_info_VSC8601, + &phy_info_VSC8641, + &phy_info_VSC8221, + &phy_info_dp83865, + &phy_info_rtl8211b, + &phy_info_generic, /* must be last; has ID 0 and 32 bit mask */ + NULL +}; + +/* Grab the identifier of the device's PHY, and search through + * all of the known PHYs to see if one matches. If so, return + * it, if not, return NULL + */ +static struct phy_info *get_phy_info(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + uint phy_reg, phy_ID; + int i; + struct phy_info *theInfo = NULL; + + /* Grab the bits from PHYIR1, and put them in the upper half */ + phy_reg = read_phy_reg(priv, MIIM_PHYIR1); + phy_ID = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = read_phy_reg(priv, MIIM_PHYIR2); + phy_ID |= (phy_reg & 0xffff); + + /* loop through all the known PHY types, and find one that */ + /* matches the ID we read from the PHY. */ + for (i = 0; phy_info[i]; i++) { + if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift)) { + theInfo = phy_info[i]; + break; + } + } + + if (theInfo == &phy_info_generic) { + printf("%s: No support for PHY id %x; assuming generic\n", + dev->name, phy_ID); + } else { + debug("%s: PHY is %s (%x)\n", dev->name, theInfo->name, phy_ID); + } + + return theInfo; +} + +/* Execute the given series of commands on the given device's + * PHY, running functions as necessary + */ +static void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd) +{ + int i; + uint result; + volatile tsec_mdio_t *phyregs = priv->phyregs; + + phyregs->miimcfg = MIIMCFG_RESET; + + phyregs->miimcfg = MIIMCFG_INIT_VALUE; + + while (phyregs->miimind & MIIMIND_BUSY) ; + + for (i = 0; cmd->mii_reg != miim_end; i++) { + if (cmd->mii_data == miim_read) { + result = read_phy_reg(priv, cmd->mii_reg); + + if (cmd->funct != NULL) + (*(cmd->funct)) (result, priv); + + } else { + if (cmd->funct != NULL) + result = (*(cmd->funct)) (cmd->mii_reg, priv); + else + result = cmd->mii_data; + + write_phy_reg(priv, cmd->mii_reg, result); + + } + cmd++; + } +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ + && !defined(BITBANGMII) + +/* + * Read a MII PHY register. + * + * Returns: + * 0 on success + */ +static int tsec_miiphy_read(char *devname, unsigned char addr, + unsigned char reg, unsigned short *value) +{ + unsigned short ret; + struct tsec_private *priv = privlist[0]; + + if (NULL == priv) { + printf("Can't read PHY at address %d\n", addr); + return -1; + } + + ret = (unsigned short)tsec_local_mdio_read(priv->phyregs, addr, reg); + *value = ret; + + return 0; +} + +/* + * Write a MII PHY register. + * + * Returns: + * 0 on success + */ +static int tsec_miiphy_write(char *devname, unsigned char addr, + unsigned char reg, unsigned short value) +{ + struct tsec_private *priv = privlist[0]; + + if (NULL == priv) { + printf("Can't write PHY at address %d\n", addr); + return -1; + } + + tsec_local_mdio_write(priv->phyregs, addr, reg, value); + + return 0; +} + +#endif + +#ifdef CONFIG_MCAST_TFTP + +/* CREDITS: linux gianfar driver, slightly adjusted... thanx. */ + +/* Set the appropriate hash bit for the given addr */ + +/* The algorithm works like so: + * 1) Take the Destination Address (ie the multicast address), and + * do a CRC on it (little endian), and reverse the bits of the + * result. + * 2) Use the 8 most significant bits as a hash into a 256-entry + * table. The table is controlled through 8 32-bit registers: + * gaddr0-7. gaddr0's MSB is entry 0, and gaddr7's LSB is + * gaddr7. This means that the 3 most significant bits in the + * hash index which gaddr register to use, and the 5 other bits + * indicate which bit (assuming an IBM numbering scheme, which + * for PowerPC (tm) is usually the case) in the tregister holds + * the entry. */ +static int +tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set) +{ + struct tsec_private *priv = privlist[1]; + volatile tsec_t *regs = priv->regs; + volatile u32 *reg_array, value; + u8 result, whichbit, whichreg; + + result = (u8)((ether_crc(MAC_ADDR_LEN,mcast_mac) >> 24) & 0xff); + whichbit = result & 0x1f; /* the 5 LSB = which bit to set */ + whichreg = result >> 5; /* the 3 MSB = which reg to set it in */ + value = (1 << (31-whichbit)); + + reg_array = &(regs->hash.gaddr0); + + if (set) { + reg_array[whichreg] |= value; + } else { + reg_array[whichreg] &= ~value; + } + return 0; +} +#endif /* Multicast TFTP ? */ diff --git a/roms/u-boot-sam460ex/drivers/net/tsi108_eth.c b/roms/u-boot-sam460ex/drivers/net/tsi108_eth.c new file mode 100644 index 000000000..079354aaf --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/tsi108_eth.c @@ -0,0 +1,1032 @@ +/*********************************************************************** + * + * Copyright (c) 2005 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Description: + * Ethernet interface for Tundra TSI108 bridge chip + * + ***********************************************************************/ + +#include <config.h> + +#if !defined(CONFIG_TSI108_ETH_NUM_PORTS) || (CONFIG_TSI108_ETH_NUM_PORTS > 2) +#error "CONFIG_TSI108_ETH_NUM_PORTS must be defined as 1 or 2" +#endif + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/cache.h> + +#ifdef DEBUG +#define TSI108_ETH_DEBUG 7 +#else +#define TSI108_ETH_DEBUG 0 +#endif + +#if TSI108_ETH_DEBUG > 0 +#define debug_lev(lev, fmt, args...) \ +if (lev <= TSI108_ETH_DEBUG) \ +printf ("%s %d: " fmt, __FUNCTION__, __LINE__, ##args) +#else +#define debug_lev(lev, fmt, args...) do{}while(0) +#endif + +#define RX_PRINT_ERRORS +#define TX_PRINT_ERRORS + +#define ETH_BASE (CONFIG_SYS_TSI108_CSR_BASE + 0x6000) + +#define ETH_PORT_OFFSET 0x400 + +#define __REG32(base, offset) (*((volatile u32 *)((char *)(base) + (offset)))) + +#define reg_MAC_CONFIG_1(base) __REG32(base, 0x00000000) +#define MAC_CONFIG_1_TX_ENABLE (0x00000001) +#define MAC_CONFIG_1_SYNC_TX_ENABLE (0x00000002) +#define MAC_CONFIG_1_RX_ENABLE (0x00000004) +#define MAC_CONFIG_1_SYNC_RX_ENABLE (0x00000008) +#define MAC_CONFIG_1_TX_FLOW_CONTROL (0x00000010) +#define MAC_CONFIG_1_RX_FLOW_CONTROL (0x00000020) +#define MAC_CONFIG_1_LOOP_BACK (0x00000100) +#define MAC_CONFIG_1_RESET_TX_FUNCTION (0x00010000) +#define MAC_CONFIG_1_RESET_RX_FUNCTION (0x00020000) +#define MAC_CONFIG_1_RESET_TX_MAC (0x00040000) +#define MAC_CONFIG_1_RESET_RX_MAC (0x00080000) +#define MAC_CONFIG_1_SIM_RESET (0x40000000) +#define MAC_CONFIG_1_SOFT_RESET (0x80000000) + +#define reg_MAC_CONFIG_2(base) __REG32(base, 0x00000004) +#define MAC_CONFIG_2_FULL_DUPLEX (0x00000001) +#define MAC_CONFIG_2_CRC_ENABLE (0x00000002) +#define MAC_CONFIG_2_PAD_CRC (0x00000004) +#define MAC_CONFIG_2_LENGTH_CHECK (0x00000010) +#define MAC_CONFIG_2_HUGE_FRAME (0x00000020) +#define MAC_CONFIG_2_INTERFACE_MODE(val) (((val) & 0x3) << 8) +#define MAC_CONFIG_2_PREAMBLE_LENGTH(val) (((val) & 0xf) << 12) +#define INTERFACE_MODE_NIBBLE 1 /* 10/100 Mb/s MII) */ +#define INTERFACE_MODE_BYTE 2 /* 1000 Mb/s GMII/TBI */ + +#define reg_MAXIMUM_FRAME_LENGTH(base) __REG32(base, 0x00000010) + +#define reg_MII_MGMT_CONFIG(base) __REG32(base, 0x00000020) +#define MII_MGMT_CONFIG_MGMT_CLOCK_SELECT(val) ((val) & 0x7) +#define MII_MGMT_CONFIG_NO_PREAMBLE (0x00000010) +#define MII_MGMT_CONFIG_SCAN_INCREMENT (0x00000020) +#define MII_MGMT_CONFIG_RESET_MGMT (0x80000000) + +#define reg_MII_MGMT_COMMAND(base) __REG32(base, 0x00000024) +#define MII_MGMT_COMMAND_READ_CYCLE (0x00000001) +#define MII_MGMT_COMMAND_SCAN_CYCLE (0x00000002) + +#define reg_MII_MGMT_ADDRESS(base) __REG32(base, 0x00000028) +#define reg_MII_MGMT_CONTROL(base) __REG32(base, 0x0000002c) +#define reg_MII_MGMT_STATUS(base) __REG32(base, 0x00000030) + +#define reg_MII_MGMT_INDICATORS(base) __REG32(base, 0x00000034) +#define MII_MGMT_INDICATORS_BUSY (0x00000001) +#define MII_MGMT_INDICATORS_SCAN (0x00000002) +#define MII_MGMT_INDICATORS_NOT_VALID (0x00000004) + +#define reg_INTERFACE_STATUS(base) __REG32(base, 0x0000003c) +#define INTERFACE_STATUS_LINK_FAIL (0x00000008) +#define INTERFACE_STATUS_EXCESS_DEFER (0x00000200) + +#define reg_STATION_ADDRESS_1(base) __REG32(base, 0x00000040) +#define reg_STATION_ADDRESS_2(base) __REG32(base, 0x00000044) + +#define reg_PORT_CONTROL(base) __REG32(base, 0x00000200) +#define PORT_CONTROL_PRI (0x00000001) +#define PORT_CONTROL_BPT (0x00010000) +#define PORT_CONTROL_SPD (0x00040000) +#define PORT_CONTROL_RBC (0x00080000) +#define PORT_CONTROL_PRB (0x00200000) +#define PORT_CONTROL_DIS (0x00400000) +#define PORT_CONTROL_TBI (0x00800000) +#define PORT_CONTROL_STE (0x10000000) +#define PORT_CONTROL_ZOR (0x20000000) +#define PORT_CONTROL_CLR (0x40000000) +#define PORT_CONTROL_SRT (0x80000000) + +#define reg_TX_CONFIG(base) __REG32(base, 0x00000220) +#define TX_CONFIG_START_Q (0x00000003) +#define TX_CONFIG_EHP (0x00400000) +#define TX_CONFIG_CHP (0x00800000) +#define TX_CONFIG_RST (0x80000000) + +#define reg_TX_CONTROL(base) __REG32(base, 0x00000224) +#define TX_CONTROL_GO (0x00008000) +#define TX_CONTROL_MP (0x01000000) +#define TX_CONTROL_EAI (0x20000000) +#define TX_CONTROL_ABT (0x40000000) +#define TX_CONTROL_EII (0x80000000) + +#define reg_TX_STATUS(base) __REG32(base, 0x00000228) +#define TX_STATUS_QUEUE_USABLE (0x0000000f) +#define TX_STATUS_CURR_Q (0x00000300) +#define TX_STATUS_ACT (0x00008000) +#define TX_STATUS_QUEUE_IDLE (0x000f0000) +#define TX_STATUS_EOQ_PENDING (0x0f000000) + +#define reg_TX_EXTENDED_STATUS(base) __REG32(base, 0x0000022c) +#define TX_EXTENDED_STATUS_END_OF_QUEUE_CONDITION (0x0000000f) +#define TX_EXTENDED_STATUS_END_OF_FRAME_CONDITION (0x00000f00) +#define TX_EXTENDED_STATUS_DESCRIPTOR_INTERRUPT_CONDITION (0x000f0000) +#define TX_EXTENDED_STATUS_ERROR_FLAG (0x0f000000) + +#define reg_TX_THRESHOLDS(base) __REG32(base, 0x00000230) + +#define reg_TX_DIAGNOSTIC_ADDR(base) __REG32(base, 0x00000270) +#define TX_DIAGNOSTIC_ADDR_INDEX (0x0000007f) +#define TX_DIAGNOSTIC_ADDR_DFR (0x40000000) +#define TX_DIAGNOSTIC_ADDR_AI (0x80000000) + +#define reg_TX_DIAGNOSTIC_DATA(base) __REG32(base, 0x00000274) + +#define reg_TX_ERROR_STATUS(base) __REG32(base, 0x00000278) +#define TX_ERROR_STATUS (0x00000278) +#define TX_ERROR_STATUS_QUEUE_0_ERROR_RESPONSE (0x0000000f) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_0 (0x00000010) +#define TX_ERROR_STATUS_RER_ON_QUEUE_0 (0x00000020) +#define TX_ERROR_STATUS_TER_ON_QUEUE_0 (0x00000040) +#define TX_ERROR_STATUS_DER_ON_QUEUE_0 (0x00000080) +#define TX_ERROR_STATUS_QUEUE_1_ERROR_RESPONSE (0x00000f00) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_1 (0x00001000) +#define TX_ERROR_STATUS_RER_ON_QUEUE_1 (0x00002000) +#define TX_ERROR_STATUS_TER_ON_QUEUE_1 (0x00004000) +#define TX_ERROR_STATUS_DER_ON_QUEUE_1 (0x00008000) +#define TX_ERROR_STATUS_QUEUE_2_ERROR_RESPONSE (0x000f0000) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_2 (0x00100000) +#define TX_ERROR_STATUS_RER_ON_QUEUE_2 (0x00200000) +#define TX_ERROR_STATUS_TER_ON_QUEUE_2 (0x00400000) +#define TX_ERROR_STATUS_DER_ON_QUEUE_2 (0x00800000) +#define TX_ERROR_STATUS_QUEUE_3_ERROR_RESPONSE (0x0f000000) +#define TX_ERROR_STATUS_TEA_ON_QUEUE_3 (0x10000000) +#define TX_ERROR_STATUS_RER_ON_QUEUE_3 (0x20000000) +#define TX_ERROR_STATUS_TER_ON_QUEUE_3 (0x40000000) +#define TX_ERROR_STATUS_DER_ON_QUEUE_3 (0x80000000) + +#define reg_TX_QUEUE_0_CONFIG(base) __REG32(base, 0x00000280) +#define TX_QUEUE_0_CONFIG_OCN_PORT (0x0000003f) +#define TX_QUEUE_0_CONFIG_BSWP (0x00000400) +#define TX_QUEUE_0_CONFIG_WSWP (0x00000800) +#define TX_QUEUE_0_CONFIG_AM (0x00004000) +#define TX_QUEUE_0_CONFIG_GVI (0x00008000) +#define TX_QUEUE_0_CONFIG_EEI (0x00010000) +#define TX_QUEUE_0_CONFIG_ELI (0x00020000) +#define TX_QUEUE_0_CONFIG_ENI (0x00040000) +#define TX_QUEUE_0_CONFIG_ESI (0x00080000) +#define TX_QUEUE_0_CONFIG_EDI (0x00100000) + +#define reg_TX_QUEUE_0_BUF_CONFIG(base) __REG32(base, 0x00000284) +#define TX_QUEUE_0_BUF_CONFIG_OCN_PORT (0x0000003f) +#define TX_QUEUE_0_BUF_CONFIG_BURST (0x00000300) +#define TX_QUEUE_0_BUF_CONFIG_BSWP (0x00000400) +#define TX_QUEUE_0_BUF_CONFIG_WSWP (0x00000800) + +#define OCN_PORT_HLP 0 /* HLP Interface */ +#define OCN_PORT_PCI_X 1 /* PCI-X Interface */ +#define OCN_PORT_PROCESSOR_MASTER 2 /* Processor Interface (master) */ +#define OCN_PORT_PROCESSOR_SLAVE 3 /* Processor Interface (slave) */ +#define OCN_PORT_MEMORY 4 /* Memory Controller */ +#define OCN_PORT_DMA 5 /* DMA Controller */ +#define OCN_PORT_ETHERNET 6 /* Ethernet Controller */ +#define OCN_PORT_PRINT 7 /* Print Engine Interface */ + +#define reg_TX_QUEUE_0_PTR_LOW(base) __REG32(base, 0x00000288) + +#define reg_TX_QUEUE_0_PTR_HIGH(base) __REG32(base, 0x0000028c) +#define TX_QUEUE_0_PTR_HIGH_VALID (0x80000000) + +#define reg_RX_CONFIG(base) __REG32(base, 0x00000320) +#define RX_CONFIG_DEF_Q (0x00000003) +#define RX_CONFIG_EMF (0x00000100) +#define RX_CONFIG_EUF (0x00000200) +#define RX_CONFIG_BFE (0x00000400) +#define RX_CONFIG_MFE (0x00000800) +#define RX_CONFIG_UFE (0x00001000) +#define RX_CONFIG_SE (0x00002000) +#define RX_CONFIG_ABF (0x00200000) +#define RX_CONFIG_APE (0x00400000) +#define RX_CONFIG_CHP (0x00800000) +#define RX_CONFIG_RST (0x80000000) + +#define reg_RX_CONTROL(base) __REG32(base, 0x00000324) +#define GE_E0_RX_CONTROL_QUEUE_ENABLES (0x0000000f) +#define GE_E0_RX_CONTROL_GO (0x00008000) +#define GE_E0_RX_CONTROL_EAI (0x20000000) +#define GE_E0_RX_CONTROL_ABT (0x40000000) +#define GE_E0_RX_CONTROL_EII (0x80000000) + +#define reg_RX_EXTENDED_STATUS(base) __REG32(base, 0x0000032c) +#define RX_EXTENDED_STATUS (0x0000032c) +#define RX_EXTENDED_STATUS_EOQ (0x0000000f) +#define RX_EXTENDED_STATUS_EOQ_0 (0x00000001) +#define RX_EXTENDED_STATUS_EOF (0x00000f00) +#define RX_EXTENDED_STATUS_DESCRIPTOR_INTERRUPT_CONDITION (0x000f0000) +#define RX_EXTENDED_STATUS_ERROR_FLAG (0x0f000000) + +#define reg_RX_THRESHOLDS(base) __REG32(base, 0x00000330) + +#define reg_RX_DIAGNOSTIC_ADDR(base) __REG32(base, 0x00000370) +#define RX_DIAGNOSTIC_ADDR_INDEX (0x0000007f) +#define RX_DIAGNOSTIC_ADDR_DFR (0x40000000) +#define RX_DIAGNOSTIC_ADDR_AI (0x80000000) + +#define reg_RX_DIAGNOSTIC_DATA(base) __REG32(base, 0x00000374) + +#define reg_RX_QUEUE_0_CONFIG(base) __REG32(base, 0x00000380) +#define RX_QUEUE_0_CONFIG_OCN_PORT (0x0000003f) +#define RX_QUEUE_0_CONFIG_BSWP (0x00000400) +#define RX_QUEUE_0_CONFIG_WSWP (0x00000800) +#define RX_QUEUE_0_CONFIG_AM (0x00004000) +#define RX_QUEUE_0_CONFIG_EEI (0x00010000) +#define RX_QUEUE_0_CONFIG_ELI (0x00020000) +#define RX_QUEUE_0_CONFIG_ENI (0x00040000) +#define RX_QUEUE_0_CONFIG_ESI (0x00080000) +#define RX_QUEUE_0_CONFIG_EDI (0x00100000) + +#define reg_RX_QUEUE_0_BUF_CONFIG(base) __REG32(base, 0x00000384) +#define RX_QUEUE_0_BUF_CONFIG_OCN_PORT (0x0000003f) +#define RX_QUEUE_0_BUF_CONFIG_BURST (0x00000300) +#define RX_QUEUE_0_BUF_CONFIG_BSWP (0x00000400) +#define RX_QUEUE_0_BUF_CONFIG_WSWP (0x00000800) + +#define reg_RX_QUEUE_0_PTR_LOW(base) __REG32(base, 0x00000388) + +#define reg_RX_QUEUE_0_PTR_HIGH(base) __REG32(base, 0x0000038c) +#define RX_QUEUE_0_PTR_HIGH_VALID (0x80000000) + +/* + * PHY register definitions + */ +/* the first 15 PHY registers are standard. */ +#define PHY_CTRL_REG 0 /* Control Register */ +#define PHY_STATUS_REG 1 /* Status Regiser */ +#define PHY_ID1_REG 2 /* Phy Id Reg (word 1) */ +#define PHY_ID2_REG 3 /* Phy Id Reg (word 2) */ +#define PHY_AN_ADV_REG 4 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY_REG 5 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP_REG 6 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX_REG 7 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE_REG 8 /* Link Partner Next Page */ +#define PHY_1000T_CTRL_REG 9 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS_REG 10 /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS_REG 11 /* Extended Status Reg */ + +/* + * PHY Register bit masks. + */ +#define PHY_CTRL_RESET (1 << 15) +#define PHY_CTRL_LOOPBACK (1 << 14) +#define PHY_CTRL_SPEED0 (1 << 13) +#define PHY_CTRL_AN_EN (1 << 12) +#define PHY_CTRL_PWR_DN (1 << 11) +#define PHY_CTRL_ISOLATE (1 << 10) +#define PHY_CTRL_RESTART_AN (1 << 9) +#define PHY_CTRL_FULL_DUPLEX (1 << 8) +#define PHY_CTRL_CT_EN (1 << 7) +#define PHY_CTRL_SPEED1 (1 << 6) + +#define PHY_STAT_100BASE_T4 (1 << 15) +#define PHY_STAT_100BASE_X_FD (1 << 14) +#define PHY_STAT_100BASE_X_HD (1 << 13) +#define PHY_STAT_10BASE_T_FD (1 << 12) +#define PHY_STAT_10BASE_T_HD (1 << 11) +#define PHY_STAT_100BASE_T2_FD (1 << 10) +#define PHY_STAT_100BASE_T2_HD (1 << 9) +#define PHY_STAT_EXT_STAT (1 << 8) +#define PHY_STAT_RESERVED (1 << 7) +#define PHY_STAT_MFPS (1 << 6) /* Management Frames Preamble Suppression */ +#define PHY_STAT_AN_COMPLETE (1 << 5) +#define PHY_STAT_REM_FAULT (1 << 4) +#define PHY_STAT_AN_CAP (1 << 3) +#define PHY_STAT_LINK_UP (1 << 2) +#define PHY_STAT_JABBER (1 << 1) +#define PHY_STAT_EXT_CAP (1 << 0) + +#define TBI_CONTROL_2 0x11 +#define TBI_CONTROL_2_ENABLE_COMMA_DETECT 0x0001 +#define TBI_CONTROL_2_ENABLE_WRAP 0x0002 +#define TBI_CONTROL_2_G_MII_MODE 0x0010 +#define TBI_CONTROL_2_RECEIVE_CLOCK_SELECT 0x0020 +#define TBI_CONTROL_2_AUTO_NEGOTIATION_SENSE 0x0100 +#define TBI_CONTROL_2_DISABLE_TRANSMIT_RUNNING_DISPARITY 0x1000 +#define TBI_CONTROL_2_DISABLE_RECEIVE_RUNNING_DISPARITY 0x2000 +#define TBI_CONTROL_2_SHORTCUT_LINK_TIMER 0x4000 +#define TBI_CONTROL_2_SOFT_RESET 0x8000 + +/* marvel specific */ +#define MV1111_EXT_CTRL1_REG 16 /* PHY Specific Control Reg */ +#define MV1111_SPEC_STAT_REG 17 /* PHY Specific Status Reg */ +#define MV1111_EXT_CTRL2_REG 20 /* Extended PHY Specific Control Reg */ + +/* + * MARVELL 88E1111 PHY register bit masks + */ +/* PHY Specific Status Register (MV1111_EXT_CTRL1_REG) */ + +#define SPEC_STAT_SPEED_MASK (3 << 14) +#define SPEC_STAT_FULL_DUP (1 << 13) +#define SPEC_STAT_PAGE_RCVD (1 << 12) +#define SPEC_STAT_RESOLVED (1 << 11) /* Speed and Duplex Resolved */ +#define SPEC_STAT_LINK_UP (1 << 10) +#define SPEC_STAT_CABLE_LEN_MASK (7 << 7)/* Cable Length (100/1000 modes only) */ +#define SPEC_STAT_MDIX (1 << 6) +#define SPEC_STAT_POLARITY (1 << 1) +#define SPEC_STAT_JABBER (1 << 0) + +#define SPEED_1000 (2 << 14) +#define SPEED_100 (1 << 14) +#define SPEED_10 (0 << 14) + +#define TBI_ADDR 0x1E /* Ten Bit Interface address */ + +/* negotiated link parameters */ +#define LINK_SPEED_UNKNOWN 0 +#define LINK_SPEED_10 1 +#define LINK_SPEED_100 2 +#define LINK_SPEED_1000 3 + +#define LINK_DUPLEX_UNKNOWN 0 +#define LINK_DUPLEX_HALF 1 +#define LINK_DUPLEX_FULL 2 + +static unsigned int phy_address[] = { 8, 9 }; + +#define vuint32 volatile u32 + +/* TX/RX buffer descriptors. MUST be cache line aligned in memory. (32 byte) + * This structure is accessed by the ethernet DMA engine which means it + * MUST be in LITTLE ENDIAN format */ +struct dma_descriptor { + vuint32 start_addr0; /* buffer address, least significant bytes. */ + vuint32 start_addr1; /* buffer address, most significant bytes. */ + vuint32 next_descr_addr0;/* next descriptor address, least significant bytes. Must be 64-bit aligned. */ + vuint32 next_descr_addr1;/* next descriptor address, most significant bytes. */ + vuint32 vlan_byte_count;/* VLAN tag(top 2 bytes) and byte countt (bottom 2 bytes). */ + vuint32 config_status; /* Configuration/Status. */ + vuint32 reserved1; /* reserved to make the descriptor cache line aligned. */ + vuint32 reserved2; /* reserved to make the descriptor cache line aligned. */ +}; + +/* last next descriptor address flag */ +#define DMA_DESCR_LAST (1 << 31) + +/* TX DMA descriptor config status bits */ +#define DMA_DESCR_TX_EOF (1 << 0) /* end of frame */ +#define DMA_DESCR_TX_SOF (1 << 1) /* start of frame */ +#define DMA_DESCR_TX_PFVLAN (1 << 2) +#define DMA_DESCR_TX_HUGE (1 << 3) +#define DMA_DESCR_TX_PAD (1 << 4) +#define DMA_DESCR_TX_CRC (1 << 5) +#define DMA_DESCR_TX_DESCR_INT (1 << 14) +#define DMA_DESCR_TX_RETRY_COUNT 0x000F0000 +#define DMA_DESCR_TX_ONE_COLLISION (1 << 20) +#define DMA_DESCR_TX_LATE_COLLISION (1 << 24) +#define DMA_DESCR_TX_UNDERRUN (1 << 25) +#define DMA_DESCR_TX_RETRY_LIMIT (1 << 26) +#define DMA_DESCR_TX_OK (1 << 30) +#define DMA_DESCR_TX_OWNER (1 << 31) + +/* RX DMA descriptor status bits */ +#define DMA_DESCR_RX_EOF (1 << 0) +#define DMA_DESCR_RX_SOF (1 << 1) +#define DMA_DESCR_RX_VTF (1 << 2) +#define DMA_DESCR_RX_FRAME_IS_TYPE (1 << 3) +#define DMA_DESCR_RX_SHORT_FRAME (1 << 4) +#define DMA_DESCR_RX_HASH_MATCH (1 << 7) +#define DMA_DESCR_RX_BAD_FRAME (1 << 8) +#define DMA_DESCR_RX_OVERRUN (1 << 9) +#define DMA_DESCR_RX_MAX_FRAME_LEN (1 << 11) +#define DMA_DESCR_RX_CRC_ERROR (1 << 12) +#define DMA_DESCR_RX_DESCR_INT (1 << 13) +#define DMA_DESCR_RX_OWNER (1 << 15) + +#define RX_BUFFER_SIZE PKTSIZE +#define NUM_RX_DESC PKTBUFSRX + +static struct dma_descriptor tx_descriptor __attribute__ ((aligned(32))); + +static struct dma_descriptor rx_descr_array[NUM_RX_DESC] + __attribute__ ((aligned(32))); + +static struct dma_descriptor *rx_descr_current; + +static int tsi108_eth_probe (struct eth_device *dev, bd_t * bis); +static int tsi108_eth_send (struct eth_device *dev, + volatile void *packet, int length); +static int tsi108_eth_recv (struct eth_device *dev); +static void tsi108_eth_halt (struct eth_device *dev); +static unsigned int read_phy (unsigned int base, + unsigned int phy_addr, unsigned int phy_reg); +static void write_phy (unsigned int base, + unsigned int phy_addr, + unsigned int phy_reg, unsigned int phy_data); + +#if TSI108_ETH_DEBUG > 100 +/* + * print phy debug infomation + */ +static void dump_phy_regs (unsigned int phy_addr) +{ + int i; + + printf ("PHY %d registers\n", phy_addr); + for (i = 0; i <= 30; i++) { + printf ("%2d 0x%04x\n", i, read_phy (ETH_BASE, phy_addr, i)); + } + printf ("\n"); + +} +#else +#define dump_phy_regs(base) do{}while(0) +#endif + +#if TSI108_ETH_DEBUG > 100 +/* + * print debug infomation + */ +static void tx_diag_regs (unsigned int base) +{ + int i; + unsigned long dummy; + + printf ("TX diagnostics registers\n"); + reg_TX_DIAGNOSTIC_ADDR(base) = 0x00 | TX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_TX_DIAGNOSTIC_DATA(base); + for (i = 0x00; i <= 0x05; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_TX_DIAGNOSTIC_DATA(base)); + } + reg_TX_DIAGNOSTIC_ADDR(base) = 0x40 | TX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_TX_DIAGNOSTIC_DATA(base); + for (i = 0x40; i <= 0x47; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_TX_DIAGNOSTIC_DATA(base)); + } + printf ("\n"); + +} +#else +#define tx_diag_regs(base) do{}while(0) +#endif + +#if TSI108_ETH_DEBUG > 100 +/* + * print debug infomation + */ +static void rx_diag_regs (unsigned int base) +{ + int i; + unsigned long dummy; + + printf ("RX diagnostics registers\n"); + reg_RX_DIAGNOSTIC_ADDR(base) = 0x00 | RX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_RX_DIAGNOSTIC_DATA(base); + for (i = 0x00; i <= 0x05; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_RX_DIAGNOSTIC_DATA(base)); + } + reg_RX_DIAGNOSTIC_ADDR(base) = 0x40 | RX_DIAGNOSTIC_ADDR_AI; + udelay (1000); + dummy = reg_RX_DIAGNOSTIC_DATA(base); + for (i = 0x08; i <= 0x0a; i++) { + udelay (1000); + printf ("0x%02x 0x%08x\n", i, reg_RX_DIAGNOSTIC_DATA(base)); + } + printf ("\n"); + +} +#else +#define rx_diag_regs(base) do{}while(0) +#endif + +#if TSI108_ETH_DEBUG > 100 +/* + * print debug infomation + */ +static void debug_mii_regs (unsigned int base) +{ + printf ("MII_MGMT_CONFIG 0x%08x\n", reg_MII_MGMT_CONFIG(base)); + printf ("MII_MGMT_COMMAND 0x%08x\n", reg_MII_MGMT_COMMAND(base)); + printf ("MII_MGMT_ADDRESS 0x%08x\n", reg_MII_MGMT_ADDRESS(base)); + printf ("MII_MGMT_CONTROL 0x%08x\n", reg_MII_MGMT_CONTROL(base)); + printf ("MII_MGMT_STATUS 0x%08x\n", reg_MII_MGMT_STATUS(base)); + printf ("MII_MGMT_INDICATORS 0x%08x\n", reg_MII_MGMT_INDICATORS(base)); + printf ("\n"); + +} +#else +#define debug_mii_regs(base) do{}while(0) +#endif + +/* + * Wait until the phy bus is non-busy + */ +static void phy_wait (unsigned int base, unsigned int condition) +{ + int timeout; + + timeout = 0; + while (reg_MII_MGMT_INDICATORS(base) & condition) { + udelay (10); + if (++timeout > 10000) { + printf ("ERROR: timeout waiting for phy bus (%d)\n", + condition); + break; + } + } +} + +/* + * read phy register + */ +static unsigned int read_phy (unsigned int base, + unsigned int phy_addr, unsigned int phy_reg) +{ + unsigned int value; + + phy_wait (base, MII_MGMT_INDICATORS_BUSY); + + reg_MII_MGMT_ADDRESS(base) = (phy_addr << 8) | phy_reg; + + /* Ensure that the Read Cycle bit is cleared prior to next read cycle */ + reg_MII_MGMT_COMMAND(base) = 0; + + /* start the read */ + reg_MII_MGMT_COMMAND(base) = MII_MGMT_COMMAND_READ_CYCLE; + + /* wait for the read to complete */ + phy_wait (base, + MII_MGMT_INDICATORS_NOT_VALID | MII_MGMT_INDICATORS_BUSY); + + value = reg_MII_MGMT_STATUS(base); + + reg_MII_MGMT_COMMAND(base) = 0; + + return value; +} + +/* + * write phy register + */ +static void write_phy (unsigned int base, + unsigned int phy_addr, + unsigned int phy_reg, unsigned int phy_data) +{ + phy_wait (base, MII_MGMT_INDICATORS_BUSY); + + reg_MII_MGMT_ADDRESS(base) = (phy_addr << 8) | phy_reg; + + /* Ensure that the Read Cycle bit is cleared prior to next cycle */ + reg_MII_MGMT_COMMAND(base) = 0; + + /* start the write */ + reg_MII_MGMT_CONTROL(base) = phy_data; +} + +/* + * configure the marvell 88e1111 phy + */ +static int marvell_88e_phy_config (struct eth_device *dev, int *speed, + int *duplex) +{ + unsigned long base; + unsigned long phy_addr; + unsigned int phy_status; + unsigned int phy_spec_status; + int timeout; + int phy_speed; + int phy_duplex; + unsigned int value; + + phy_speed = LINK_SPEED_UNKNOWN; + phy_duplex = LINK_DUPLEX_UNKNOWN; + + base = dev->iobase; + phy_addr = (unsigned long)dev->priv; + + /* Take the PHY out of reset. */ + write_phy (ETH_BASE, phy_addr, PHY_CTRL_REG, PHY_CTRL_RESET); + + /* Wait for the reset process to complete. */ + udelay (10); + timeout = 0; + while ((phy_status = + read_phy (ETH_BASE, phy_addr, PHY_CTRL_REG)) & PHY_CTRL_RESET) { + udelay (10); + if (++timeout > 10000) { + printf ("ERROR: timeout waiting for phy reset\n"); + break; + } + } + + /* TBI Configuration. */ + write_phy (base, TBI_ADDR, TBI_CONTROL_2, TBI_CONTROL_2_G_MII_MODE | + TBI_CONTROL_2_RECEIVE_CLOCK_SELECT); + /* Wait for the link to be established. */ + timeout = 0; + do { + udelay (20000); + phy_status = read_phy (ETH_BASE, phy_addr, PHY_STATUS_REG); + if (++timeout > 100) { + debug_lev(1, "ERROR: unable to establish link!!!\n"); + break; + } + } while ((phy_status & PHY_STAT_LINK_UP) == 0); + + if ((phy_status & PHY_STAT_LINK_UP) == 0) + return 0; + + value = 0; + phy_spec_status = read_phy (ETH_BASE, phy_addr, MV1111_SPEC_STAT_REG); + if (phy_spec_status & SPEC_STAT_RESOLVED) { + switch (phy_spec_status & SPEC_STAT_SPEED_MASK) { + case SPEED_1000: + phy_speed = LINK_SPEED_1000; + value |= PHY_CTRL_SPEED1; + break; + case SPEED_100: + phy_speed = LINK_SPEED_100; + value |= PHY_CTRL_SPEED0; + break; + case SPEED_10: + phy_speed = LINK_SPEED_10; + break; + } + if (phy_spec_status & SPEC_STAT_FULL_DUP) { + phy_duplex = LINK_DUPLEX_FULL; + value |= PHY_CTRL_FULL_DUPLEX; + } else + phy_duplex = LINK_DUPLEX_HALF; + } + /* set TBI speed */ + write_phy (base, TBI_ADDR, PHY_CTRL_REG, value); + write_phy (base, TBI_ADDR, PHY_AN_ADV_REG, 0x0060); + +#if TSI108_ETH_DEBUG > 0 + printf ("%s link is up", dev->name); + phy_spec_status = read_phy (ETH_BASE, phy_addr, MV1111_SPEC_STAT_REG); + if (phy_spec_status & SPEC_STAT_RESOLVED) { + switch (phy_speed) { + case LINK_SPEED_1000: + printf (", 1000 Mbps"); + break; + case LINK_SPEED_100: + printf (", 100 Mbps"); + break; + case LINK_SPEED_10: + printf (", 10 Mbps"); + break; + } + if (phy_duplex == LINK_DUPLEX_FULL) + printf (", Full duplex"); + else + printf (", Half duplex"); + } + printf ("\n"); +#endif + + dump_phy_regs (TBI_ADDR); + if (speed) + *speed = phy_speed; + if (duplex) + *duplex = phy_duplex; + + return 1; +} + +/* + * External interface + * + * register the tsi108 ethernet controllers with the multi-ethernet system + */ +int tsi108_eth_initialize (bd_t * bis) +{ + struct eth_device *dev; + int index; + + for (index = 0; index < CONFIG_TSI108_ETH_NUM_PORTS; index++) { + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + + sprintf (dev->name, "TSI108_eth%d", index); + + dev->iobase = ETH_BASE + (index * ETH_PORT_OFFSET); + dev->priv = (void *)(phy_address[index]); + dev->init = tsi108_eth_probe; + dev->halt = tsi108_eth_halt; + dev->send = tsi108_eth_send; + dev->recv = tsi108_eth_recv; + + eth_register(dev); + } + return index; +} + +/* + * probe for and initialize a single ethernet interface + */ +static int tsi108_eth_probe (struct eth_device *dev, bd_t * bis) +{ + unsigned long base; + unsigned long value; + int index; + struct dma_descriptor *tx_descr; + struct dma_descriptor *rx_descr; + int speed; + int duplex; + + base = dev->iobase; + + reg_PORT_CONTROL(base) = PORT_CONTROL_STE | PORT_CONTROL_BPT; + + /* Bring DMA/FIFO out of reset. */ + reg_TX_CONFIG(base) = 0x00000000; + reg_RX_CONFIG(base) = 0x00000000; + + reg_TX_THRESHOLDS(base) = (192 << 16) | 192; + reg_RX_THRESHOLDS(base) = (192 << 16) | 112; + + /* Bring MAC out of reset. */ + reg_MAC_CONFIG_1(base) = 0x00000000; + + /* DMA MAC configuration. */ + reg_MAC_CONFIG_1(base) = + MAC_CONFIG_1_RX_ENABLE | MAC_CONFIG_1_TX_ENABLE; + + reg_MII_MGMT_CONFIG(base) = MII_MGMT_CONFIG_NO_PREAMBLE; + reg_MAXIMUM_FRAME_LENGTH(base) = RX_BUFFER_SIZE; + + /* Note: Early tsi108 manual did not have correct byte order + * for the station address.*/ + reg_STATION_ADDRESS_1(base) = (dev->enetaddr[5] << 24) | + (dev->enetaddr[4] << 16) | + (dev->enetaddr[3] << 8) | (dev->enetaddr[2] << 0); + + reg_STATION_ADDRESS_2(base) = (dev->enetaddr[1] << 24) | + (dev->enetaddr[0] << 16); + + if (marvell_88e_phy_config(dev, &speed, &duplex) == 0) + return -1; + + value = + MAC_CONFIG_2_PREAMBLE_LENGTH(7) | MAC_CONFIG_2_PAD_CRC | + MAC_CONFIG_2_CRC_ENABLE; + if (speed == LINK_SPEED_1000) + value |= MAC_CONFIG_2_INTERFACE_MODE(INTERFACE_MODE_BYTE); + else { + value |= MAC_CONFIG_2_INTERFACE_MODE(INTERFACE_MODE_NIBBLE); + reg_PORT_CONTROL(base) |= PORT_CONTROL_SPD; + } + if (duplex == LINK_DUPLEX_FULL) { + value |= MAC_CONFIG_2_FULL_DUPLEX; + reg_PORT_CONTROL(base) &= ~PORT_CONTROL_BPT; + } else + reg_PORT_CONTROL(base) |= PORT_CONTROL_BPT; + reg_MAC_CONFIG_2(base) = value; + + reg_RX_CONFIG(base) = RX_CONFIG_SE; + reg_RX_QUEUE_0_CONFIG(base) = OCN_PORT_MEMORY; + reg_RX_QUEUE_0_BUF_CONFIG(base) = OCN_PORT_MEMORY; + + /* initialize the RX DMA descriptors */ + rx_descr = &rx_descr_array[0]; + rx_descr_current = rx_descr; + for (index = 0; index < NUM_RX_DESC; index++) { + /* make sure the receive buffers are not in cache */ + invalidate_dcache_range((unsigned long)NetRxPackets[index], + (unsigned long)NetRxPackets[index] + + RX_BUFFER_SIZE); + rx_descr->start_addr0 = + cpu_to_le32((vuint32) NetRxPackets[index]); + rx_descr->start_addr1 = 0; + rx_descr->next_descr_addr0 = + cpu_to_le32((vuint32) (rx_descr + 1)); + rx_descr->next_descr_addr1 = 0; + rx_descr->vlan_byte_count = 0; + rx_descr->config_status = cpu_to_le32((RX_BUFFER_SIZE << 16) | + DMA_DESCR_RX_OWNER); + rx_descr++; + } + rx_descr--; + rx_descr->next_descr_addr0 = 0; + rx_descr->next_descr_addr1 = cpu_to_le32(DMA_DESCR_LAST); + /* Push the descriptors to RAM so the ethernet DMA can see them */ + invalidate_dcache_range((unsigned long)rx_descr_array, + (unsigned long)rx_descr_array + + sizeof(rx_descr_array)); + + /* enable RX queue */ + reg_RX_CONTROL(base) = TX_CONTROL_GO | 0x01; + reg_RX_QUEUE_0_PTR_LOW(base) = (u32) rx_descr_current; + /* enable receive DMA */ + reg_RX_QUEUE_0_PTR_HIGH(base) = RX_QUEUE_0_PTR_HIGH_VALID; + + reg_TX_QUEUE_0_CONFIG(base) = OCN_PORT_MEMORY; + reg_TX_QUEUE_0_BUF_CONFIG(base) = OCN_PORT_MEMORY; + + /* initialize the TX DMA descriptor */ + tx_descr = &tx_descriptor; + + tx_descr->start_addr0 = 0; + tx_descr->start_addr1 = 0; + tx_descr->next_descr_addr0 = 0; + tx_descr->next_descr_addr1 = cpu_to_le32(DMA_DESCR_LAST); + tx_descr->vlan_byte_count = 0; + tx_descr->config_status = cpu_to_le32(DMA_DESCR_TX_OK | + DMA_DESCR_TX_SOF | + DMA_DESCR_TX_EOF); + /* enable TX queue */ + reg_TX_CONTROL(base) = TX_CONTROL_GO | 0x01; + + return 0; +} + +/* + * send a packet + */ +static int tsi108_eth_send (struct eth_device *dev, + volatile void *packet, int length) +{ + unsigned long base; + int timeout; + struct dma_descriptor *tx_descr; + unsigned long status; + + base = dev->iobase; + tx_descr = &tx_descriptor; + + /* Wait until the last packet has been transmitted. */ + timeout = 0; + do { + /* make sure we see the changes made by the DMA engine */ + invalidate_dcache_range((unsigned long)tx_descr, + (unsigned long)tx_descr + + sizeof(struct dma_descriptor)); + + if (timeout != 0) + udelay (15); + if (++timeout > 10000) { + tx_diag_regs(base); + debug_lev(1, + "ERROR: timeout waiting for last transmit packet to be sent\n"); + return 0; + } + } while (tx_descr->config_status & cpu_to_le32(DMA_DESCR_TX_OWNER)); + + status = le32_to_cpu(tx_descr->config_status); + if ((status & DMA_DESCR_TX_OK) == 0) { +#ifdef TX_PRINT_ERRORS + printf ("TX packet error: 0x%08lx\n %s%s%s%s\n", status, + status & DMA_DESCR_TX_OK ? "tx error, " : "", + status & DMA_DESCR_TX_RETRY_LIMIT ? + "retry limit reached, " : "", + status & DMA_DESCR_TX_UNDERRUN ? "underrun, " : "", + status & DMA_DESCR_TX_LATE_COLLISION ? "late collision, " + : ""); +#endif + } + + debug_lev (9, "sending packet %d\n", length); + tx_descr->start_addr0 = cpu_to_le32((vuint32) packet); + tx_descr->start_addr1 = 0; + tx_descr->next_descr_addr0 = 0; + tx_descr->next_descr_addr1 = cpu_to_le32(DMA_DESCR_LAST); + tx_descr->vlan_byte_count = cpu_to_le32(length); + tx_descr->config_status = cpu_to_le32(DMA_DESCR_TX_OWNER | + DMA_DESCR_TX_CRC | + DMA_DESCR_TX_PAD | + DMA_DESCR_TX_SOF | + DMA_DESCR_TX_EOF); + + invalidate_dcache_range((unsigned long)tx_descr, + (unsigned long)tx_descr + + sizeof(struct dma_descriptor)); + + invalidate_dcache_range((unsigned long)packet, + (unsigned long)packet + length); + + reg_TX_QUEUE_0_PTR_LOW(base) = (u32) tx_descr; + reg_TX_QUEUE_0_PTR_HIGH(base) = TX_QUEUE_0_PTR_HIGH_VALID; + + return length; +} + +/* + * Check for received packets and send them up the protocal stack + */ +static int tsi108_eth_recv (struct eth_device *dev) +{ + struct dma_descriptor *rx_descr; + unsigned long base; + int length = 0; + unsigned long status; + volatile uchar *buffer; + + base = dev->iobase; + + /* make sure we see the changes made by the DMA engine */ + invalidate_dcache_range ((unsigned long)rx_descr_array, + (unsigned long)rx_descr_array + + sizeof(rx_descr_array)); + + /* process all of the received packets */ + rx_descr = rx_descr_current; + while ((rx_descr->config_status & cpu_to_le32(DMA_DESCR_RX_OWNER)) == 0) { + /* check for error */ + status = le32_to_cpu(rx_descr->config_status); + if (status & DMA_DESCR_RX_BAD_FRAME) { +#ifdef RX_PRINT_ERRORS + printf ("RX packet error: 0x%08lx\n %s%s%s%s%s%s\n", + status, + status & DMA_DESCR_RX_FRAME_IS_TYPE ? "too big, " + : "", + status & DMA_DESCR_RX_SHORT_FRAME ? "too short, " + : "", + status & DMA_DESCR_RX_BAD_FRAME ? "bad frame, " : + "", + status & DMA_DESCR_RX_OVERRUN ? "overrun, " : "", + status & DMA_DESCR_RX_MAX_FRAME_LEN ? + "max length, " : "", + status & DMA_DESCR_RX_CRC_ERROR ? "CRC error, " : + ""); +#endif + } else { + length = + le32_to_cpu(rx_descr->vlan_byte_count) & 0xFFFF; + + /*** process packet ***/ + buffer = + (volatile uchar + *)(le32_to_cpu (rx_descr->start_addr0)); + NetReceive (buffer, length); + + invalidate_dcache_range ((unsigned long)buffer, + (unsigned long)buffer + + RX_BUFFER_SIZE); + } + /* Give this buffer back to the DMA engine */ + rx_descr->vlan_byte_count = 0; + rx_descr->config_status = cpu_to_le32 ((RX_BUFFER_SIZE << 16) | + DMA_DESCR_RX_OWNER); + /* move descriptor pointer forward */ + rx_descr = + (struct dma_descriptor + *)(le32_to_cpu (rx_descr->next_descr_addr0)); + if (rx_descr == 0) + rx_descr = &rx_descr_array[0]; + } + /* remember where we are for next time */ + rx_descr_current = rx_descr; + + /* If the DMA engine has reached the end of the queue + * start over at the begining */ + if (reg_RX_EXTENDED_STATUS(base) & RX_EXTENDED_STATUS_EOQ_0) { + + reg_RX_EXTENDED_STATUS(base) = RX_EXTENDED_STATUS_EOQ_0; + reg_RX_QUEUE_0_PTR_LOW(base) = (u32) & rx_descr_array[0]; + reg_RX_QUEUE_0_PTR_HIGH(base) = RX_QUEUE_0_PTR_HIGH_VALID; + } + + return length; +} + +/* + * disable an ethernet interface + */ +static void tsi108_eth_halt (struct eth_device *dev) +{ + unsigned long base; + + base = dev->iobase; + + /* Put DMA/FIFO into reset state. */ + reg_TX_CONFIG(base) = TX_CONFIG_RST; + reg_RX_CONFIG(base) = RX_CONFIG_RST; + + /* Put MAC into reset state. */ + reg_MAC_CONFIG_1(base) = MAC_CONFIG_1_SOFT_RESET; +} diff --git a/roms/u-boot-sam460ex/drivers/net/uli526x.c b/roms/u-boot-sam460ex/drivers/net/uli526x.c new file mode 100644 index 000000000..9477851a7 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/uli526x.c @@ -0,0 +1,993 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. + * + * Author: Roy Zang <tie-fei.zang@freescale.com>, Sep, 2007 + * + * Description: + * ULI 526x Ethernet port driver. + * Based on the Linux driver: drivers/net/tulip/uli526x.c + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <netdev.h> +#include <asm/io.h> +#include <pci.h> +#include <miiphy.h> + +/* some kernel function compatible define */ + +#undef DEBUG + +/* Board/System/Debug information/definition */ +#define ULI_VENDOR_ID 0x10B9 +#define ULI5261_DEVICE_ID 0x5261 +#define ULI5263_DEVICE_ID 0x5263 +/* ULi M5261 ID*/ +#define PCI_ULI5261_ID (ULI5261_DEVICE_ID << 16 | ULI_VENDOR_ID) +/* ULi M5263 ID*/ +#define PCI_ULI5263_ID (ULI5263_DEVICE_ID << 16 | ULI_VENDOR_ID) + +#define ULI526X_IO_SIZE 0x100 +#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ +#define RX_DESC_CNT PKTBUFSRX /* Allocated Rx descriptors */ +#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ +#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ +#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) +#define TX_BUF_ALLOC 0x300 +#define RX_ALLOC_SIZE PKTSIZE +#define ULI526X_RESET 1 +#define CR0_DEFAULT 0 +#define CR6_DEFAULT 0x22200000 +#define CR7_DEFAULT 0x180c1 +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define ULI5261_MAX_MULTICAST 14 +#define RX_COPY_SIZE 100 +#define MAX_CHECK_PACKET 0x8000 + +#define ULI526X_10MHF 0 +#define ULI526X_100MHF 1 +#define ULI526X_10MFD 4 +#define ULI526X_100MFD 5 +#define ULI526X_AUTO 8 + +#define ULI526X_TXTH_72 0x400000 /* TX TH 72 byte */ +#define ULI526X_TXTH_96 0x404000 /* TX TH 96 byte */ +#define ULI526X_TXTH_128 0x0000 /* TX TH 128 byte */ +#define ULI526X_TXTH_256 0x4000 /* TX TH 256 byte */ +#define ULI526X_TXTH_512 0x8000 /* TX TH 512 byte */ +#define ULI526X_TXTH_1K 0xC000 /* TX TH 1K byte */ + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define PHY_POWER_DOWN 0x800 + +#define SROM_V41_CODE 0x14 + +#define SROM_CLK_WRITE(data, ioaddr) do { \ + outl(data|CR9_SROM_READ|CR9_SRCS, ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK, ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS, ioaddr); \ + udelay(5); \ + } while (0) + +/* Structure/enum declaration */ + +struct tx_desc { + u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + char *tx_buf_ptr; /* Data for us */ + struct tx_desc *next_tx_desc; +}; + +struct rx_desc { + u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + char *rx_buf_ptr; /* Data for us */ + struct rx_desc *next_rx_desc; +}; + +struct uli526x_board_info { + u32 chip_id; /* Chip vendor/Device ID */ + pci_dev_t pdev; + + long ioaddr; /* I/O base address */ + u32 cr0_data; + u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + + /* pointer for memory physical address */ + dma_addr_t buf_pool_dma_ptr; /* Tx buffer pool memory */ + dma_addr_t buf_pool_dma_start; /* Tx buffer pool align dword */ + dma_addr_t desc_pool_dma_ptr; /* descriptor pool memory */ + dma_addr_t first_tx_desc_dma; + dma_addr_t first_rx_desc_dma; + + /* descriptor pointer */ + unsigned char *buf_pool_ptr; /* Tx buffer pool memory */ + unsigned char *buf_pool_start; /* Tx buffer pool align dword */ + unsigned char *desc_pool_ptr; /* descriptor pool memory */ + struct tx_desc *first_tx_desc; + struct tx_desc *tx_insert_ptr; + struct tx_desc *tx_remove_ptr; + struct rx_desc *first_rx_desc; + struct rx_desc *rx_ready_ptr; /* packet come pointer */ + unsigned long tx_packet_cnt; /* transmitted packet count */ + + u16 PHY_reg4; /* Saved Phyxcer register 4 value */ + + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work dedia mode */ + u8 phy_addr; + + /* NIC SROM data */ + unsigned char srom[128]; +}; + +enum uli526x_offsets { + DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, + DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, + DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = 0x70, + DCR15 = 0x78 +}; + +enum uli526x_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, + CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, + CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration -- */ + +static unsigned char uli526x_media_mode = ULI526X_AUTO; + +static struct tx_desc desc_pool_array[DESC_ALL_CNT + 0x20] + __attribute__ ((aligned(32))); +static char buf_pool[TX_BUF_ALLOC * TX_DESC_CNT + 4]; + +/* For module input parameter */ +static int mode = 8; + +/* function declaration -- */ +static int uli526x_start_xmit(struct eth_device *dev, + volatile void *packet, int length); +static const struct ethtool_ops netdev_ethtool_ops; +static u16 read_srom_word(long, int); +static void uli526x_descriptor_init(struct uli526x_board_info *, unsigned long); +static void allocate_rx_buffer(struct uli526x_board_info *); +static void update_cr6(u32, unsigned long); +static u16 phy_read(unsigned long, u8, u8, u32); +static u16 phy_readby_cr10(unsigned long, u8, u8); +static void phy_write(unsigned long, u8, u8, u16, u32); +static void phy_writeby_cr10(unsigned long, u8, u8, u16); +static void phy_write_1bit(unsigned long, u32, u32); +static u16 phy_read_1bit(unsigned long, u32); +static int uli526x_rx_packet(struct eth_device *); +static void uli526x_free_tx_pkt(struct eth_device *, + struct uli526x_board_info *); +static void uli526x_reuse_buf(struct rx_desc *); +static void uli526x_init(struct eth_device *); +static void uli526x_set_phyxcer(struct uli526x_board_info *); + + +static int uli526x_init_one(struct eth_device *, bd_t *); +static void uli526x_disable(struct eth_device *); +static void set_mac_addr(struct eth_device *); + +static struct pci_device_id uli526x_pci_tbl[] = { + { ULI_VENDOR_ID, ULI5261_DEVICE_ID}, /* 5261 device */ + { ULI_VENDOR_ID, ULI5263_DEVICE_ID}, /* 5263 device */ + {} +}; + +/* ULI526X network board routine */ + +/* + * Search ULI526X board, register it + */ + +int uli526x_initialize(bd_t *bis) +{ + pci_dev_t devno; + int card_number = 0; + struct eth_device *dev; + struct uli526x_board_info *db; /* board information structure */ + + u32 iobase; + int idx = 0; + + while (1) { + /* Find PCI device */ + devno = pci_find_devices(uli526x_pci_tbl, idx++); + if (devno < 0) + break; + + pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); + iobase &= ~0xf; + + dev = (struct eth_device *)malloc(sizeof *dev); + sprintf(dev->name, "uli526x#%d\n", card_number); + db = (struct uli526x_board_info *) + malloc(sizeof(struct uli526x_board_info)); + + dev->priv = db; + db->pdev = devno; + dev->iobase = iobase; + + dev->init = uli526x_init_one; + dev->halt = uli526x_disable; + dev->send = uli526x_start_xmit; + dev->recv = uli526x_rx_packet; + + /* init db */ + db->ioaddr = dev->iobase; + /* get chip id */ + + pci_read_config_dword(devno, PCI_VENDOR_ID, &db->chip_id); +#ifdef DEBUG + printf("uli526x: uli526x @0x%x\n", iobase); + printf("uli526x: chip_id%x\n", db->chip_id); +#endif + eth_register(dev); + card_number++; + pci_write_config_byte(devno, PCI_LATENCY_TIMER, 0x20); + udelay(10 * 1000); + } + return card_number; +} + +static int uli526x_init_one(struct eth_device *dev, bd_t *bis) +{ + + struct uli526x_board_info *db = dev->priv; + int i; + + switch (mode) { + case ULI526X_10MHF: + case ULI526X_100MHF: + case ULI526X_10MFD: + case ULI526X_100MFD: + uli526x_media_mode = mode; + break; + default: + uli526x_media_mode = ULI526X_AUTO; + break; + } + + /* Allocate Tx/Rx descriptor memory */ + db->desc_pool_ptr = (uchar *)&desc_pool_array[0]; + db->desc_pool_dma_ptr = (dma_addr_t)&desc_pool_array[0]; + if (db->desc_pool_ptr == NULL) + return -1; + + db->buf_pool_ptr = (uchar *)&buf_pool[0]; + db->buf_pool_dma_ptr = (dma_addr_t)&buf_pool[0]; + if (db->buf_pool_ptr == NULL) + return -1; + + db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; + db->first_tx_desc_dma = db->desc_pool_dma_ptr; + + db->buf_pool_start = db->buf_pool_ptr; + db->buf_pool_dma_start = db->buf_pool_dma_ptr; + +#ifdef DEBUG + printf("%s(): db->ioaddr= 0x%x\n", + __FUNCTION__, db->ioaddr); + printf("%s(): media_mode= 0x%x\n", + __FUNCTION__, uli526x_media_mode); + printf("%s(): db->desc_pool_ptr= 0x%x\n", + __FUNCTION__, db->desc_pool_ptr); + printf("%s(): db->desc_pool_dma_ptr= 0x%x\n", + __FUNCTION__, db->desc_pool_dma_ptr); + printf("%s(): db->buf_pool_ptr= 0x%x\n", + __FUNCTION__, db->buf_pool_ptr); + printf("%s(): db->buf_pool_dma_ptr= 0x%x\n", + __FUNCTION__, db->buf_pool_dma_ptr); +#endif + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, + i)); + + /* Set Node address */ + if (((u16 *) db->srom)[0] == 0xffff || ((u16 *) db->srom)[0] == 0) + /* SROM absent, so write MAC address to ID Table */ + set_mac_addr(dev); + else { /*Exist SROM*/ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = db->srom[20 + i]; + } +#ifdef DEBUG + for (i = 0; i < 6; i++) + printf("%c%02x", i ? ':' : ' ', dev->enetaddr[i]); +#endif + db->PHY_reg4 = 0x1e0; + + /* system variable init */ + db->cr6_data = CR6_DEFAULT ; + db->cr6_data |= ULI526X_TXTH_256; + db->cr0_data = CR0_DEFAULT; + uli526x_init(dev); + return 0; +} + +static void uli526x_disable(struct eth_device *dev) +{ +#ifdef DEBUG + printf("uli526x_disable\n"); +#endif + struct uli526x_board_info *db = dev->priv; + + if (!((inl(db->ioaddr + DCR12)) & 0x8)) { + /* Reset & stop ULI526X board */ + outl(ULI526X_RESET, db->ioaddr + DCR0); + udelay(5); + phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id); + + /* reset the board */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); /* Disable Tx/Rx */ + update_cr6(db->cr6_data, dev->iobase); + outl(0, dev->iobase + DCR7); /* Disable Interrupt */ + outl(inl(dev->iobase + DCR5), dev->iobase + DCR5); + } +} + +/* Initialize ULI526X board + * Reset ULI526X board + * Initialize TX/Rx descriptor chain structure + * Send the set-up frame + * Enable Tx/Rx machine + */ + +static void uli526x_init(struct eth_device *dev) +{ + + struct uli526x_board_info *db = dev->priv; + u8 phy_tmp; + u16 phy_value; + u16 phy_reg_reset; + + /* Reset M526x MAC controller */ + outl(ULI526X_RESET, db->ioaddr + DCR0); /* RESET MAC */ + udelay(100); + outl(db->cr0_data, db->ioaddr + DCR0); + udelay(5); + + /* Phy addr : In some boards,M5261/M5263 phy address != 1 */ + db->phy_addr = 1; + db->tx_packet_cnt = 0; + for (phy_tmp = 0; phy_tmp < 32; phy_tmp++) { + /* peer add */ + phy_value = phy_read(db->ioaddr, phy_tmp, 3, db->chip_id); + if (phy_value != 0xffff && phy_value != 0) { + db->phy_addr = phy_tmp; + break; + } + } + +#ifdef DEBUG + printf("%s(): db->ioaddr= 0x%x\n", __FUNCTION__, db->ioaddr); + printf("%s(): db->phy_addr= 0x%x\n", __FUNCTION__, db->phy_addr); +#endif + if (phy_tmp == 32) + printf("Can not find the phy address!!!"); + + /* Parser SROM and media mode */ + db->media_mode = uli526x_media_mode; + + if (!(inl(db->ioaddr + DCR12) & 0x8)) { + /* Phyxcer capability setting */ + phy_reg_reset = phy_read(db->ioaddr, + db->phy_addr, 0, db->chip_id); + phy_reg_reset = (phy_reg_reset | 0x8000); + phy_write(db->ioaddr, db->phy_addr, 0, + phy_reg_reset, db->chip_id); + udelay(500); + + /* Process Phyxcer Media Mode */ + uli526x_set_phyxcer(db); + } + /* Media Mode Process */ + if (!(db->media_mode & ULI526X_AUTO)) + db->op_mode = db->media_mode; /* Force Mode */ + + /* Initialize Transmit/Receive decriptor and CR3/4 */ + uli526x_descriptor_init(db, db->ioaddr); + + /* Init CR6 to program M526X operation */ + update_cr6(db->cr6_data, db->ioaddr); + + /* Init CR7, interrupt active bit */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, db->ioaddr + DCR7); + + /* Init CR15, Tx jabber and Rx watchdog timer */ + outl(db->cr15_data, db->ioaddr + DCR15); + + /* Enable ULI526X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC; + update_cr6(db->cr6_data, db->ioaddr); + while (!(inl(db->ioaddr + DCR12) & 0x8)) + udelay(10); +} + +/* + * Hardware start transmission. + * Send a packet to media from the upper layer. + */ + +static int uli526x_start_xmit(struct eth_device *dev, + volatile void *packet, int length) +{ + struct uli526x_board_info *db = dev->priv; + struct tx_desc *txptr; + unsigned int len = length; + /* Too large packet check */ + if (len > MAX_PACKET_SIZE) { + printf(": big packet = %d\n", len); + return 0; + } + + /* No Tx resource check, it never happen nromally */ + if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) { + printf("No Tx resource %ld\n", db->tx_packet_cnt); + return 0; + } + + /* Disable NIC interrupt */ + outl(0, dev->iobase + DCR7); + + /* transmit this packet */ + txptr = db->tx_insert_ptr; + memcpy((char *)txptr->tx_buf_ptr, (char *)packet, (int)length); + txptr->tdes1 = cpu_to_le32(0xe1000000 | length); + + /* Point to next transmit free descriptor */ + db->tx_insert_ptr = txptr->next_tx_desc; + + /* Transmit Packet Process */ + if ((db->tx_packet_cnt < TX_DESC_CNT)) { + txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ + db->tx_packet_cnt++; /* Ready to send */ + outl(0x1, dev->iobase + DCR1); /* Issue Tx polling */ + } + + /* Got ULI526X status */ + db->cr5_data = inl(db->ioaddr + DCR5); + outl(db->cr5_data, db->ioaddr + DCR5); + +#ifdef TX_DEBUG + printf("%s(): length = 0x%x\n", __FUNCTION__, length); + printf("%s(): cr5_data=%x\n", __FUNCTION__, db->cr5_data); +#endif + + outl(db->cr7_data, dev->iobase + DCR7); + uli526x_free_tx_pkt(dev, db); + + return length; +} + +/* + * Free TX resource after TX complete + */ + +static void uli526x_free_tx_pkt(struct eth_device *dev, + struct uli526x_board_info *db) +{ + struct tx_desc *txptr; + u32 tdes0; + + txptr = db->tx_remove_ptr; + while (db->tx_packet_cnt) { + tdes0 = le32_to_cpu(txptr->tdes0); + /* printf(DRV_NAME ": tdes0=%x\n", tdes0); */ + if (tdes0 & 0x80000000) + break; + + /* A packet sent completed */ + db->tx_packet_cnt--; + + if (tdes0 != 0x7fffffff) { +#ifdef TX_DEBUG + printf("%s()tdes0=%x\n", __FUNCTION__, tdes0); +#endif + if (tdes0 & TDES0_ERR_MASK) { + if (tdes0 & 0x0002) { /* UnderRun */ + if (!(db->cr6_data & CR6_SFT)) { + db->cr6_data = db->cr6_data | + CR6_SFT; + update_cr6(db->cr6_data, + db->ioaddr); + } + } + } + } + + txptr = txptr->next_tx_desc; + }/* End of while */ + + /* Update TX remove pointer to next */ + db->tx_remove_ptr = txptr; +} + + +/* + * Receive the come packet and pass to upper layer + */ + +static int uli526x_rx_packet(struct eth_device *dev) +{ + struct uli526x_board_info *db = dev->priv; + struct rx_desc *rxptr; + int rxlen = 0; + u32 rdes0; + + rxptr = db->rx_ready_ptr; + + rdes0 = le32_to_cpu(rxptr->rdes0); +#ifdef RX_DEBUG + printf("%s(): rxptr->rdes0=%x:%x\n", __FUNCTION__, rxptr->rdes0); +#endif + if (!(rdes0 & 0x80000000)) { /* packet owner check */ + if ((rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + /* reuse this buf */ + printf("A packet without First/Last flag"); + uli526x_reuse_buf(rxptr); + } else { + /* A packet with First/Last flag */ + rxlen = ((rdes0 >> 16) & 0x3fff) - 4; +#ifdef RX_DEBUG + printf("%s(): rxlen =%x\n", __FUNCTION__, rxlen); +#endif + /* error summary bit check */ + if (rdes0 & 0x8000) { + /* This is a error packet */ + printf("Error: rdes0: %x\n", rdes0); + } + + if (!(rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen > 6))) { + +#ifdef RX_DEBUG + printf("%s(): rx_skb_ptr =%x\n", + __FUNCTION__, rxptr->rx_buf_ptr); + printf("%s(): rxlen =%x\n", + __FUNCTION__, rxlen); + + printf("%s(): buf addr =%x\n", + __FUNCTION__, rxptr->rx_buf_ptr); + printf("%s(): rxlen =%x\n", + __FUNCTION__, rxlen); + int i; + for (i = 0; i < 0x20; i++) + printf("%s(): data[%x] =%x\n", + __FUNCTION__, i, rxptr->rx_buf_ptr[i]); +#endif + + NetReceive((uchar *)rxptr->rx_buf_ptr, rxlen); + uli526x_reuse_buf(rxptr); + + } else { + /* Reuse SKB buffer when the packet is error */ + printf("Reuse buffer, rdes0"); + uli526x_reuse_buf(rxptr); + } + } + + rxptr = rxptr->next_rx_desc; + } + + db->rx_ready_ptr = rxptr; + return rxlen; +} + +/* + * Reuse the RX buffer + */ + +static void uli526x_reuse_buf(struct rx_desc *rxptr) +{ + + if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) + rxptr->rdes0 = cpu_to_le32(0x80000000); + else + printf("Buffer reuse method error"); +} +/* + * Initialize transmit/Receive descriptor + * Using Chain structure, and allocate Tx/Rx buffer + */ + +static void uli526x_descriptor_init(struct uli526x_board_info *db, + unsigned long ioaddr) +{ + struct tx_desc *tmp_tx; + struct rx_desc *tmp_rx; + unsigned char *tmp_buf; + dma_addr_t tmp_tx_dma, tmp_rx_dma; + dma_addr_t tmp_buf_dma; + int i; + /* tx descriptor start pointer */ + db->tx_insert_ptr = db->first_tx_desc; + db->tx_remove_ptr = db->first_tx_desc; + + outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + db->first_rx_desc = (void *)db->first_tx_desc + + sizeof(struct tx_desc) * TX_DESC_CNT; + db->first_rx_desc_dma = db->first_tx_desc_dma + + sizeof(struct tx_desc) * TX_DESC_CNT; + db->rx_ready_ptr = db->first_rx_desc; + outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ +#ifdef DEBUG + printf("%s(): db->first_tx_desc= 0x%x\n", + __FUNCTION__, db->first_tx_desc); + printf("%s(): db->first_rx_desc_dma= 0x%x\n", + __FUNCTION__, db->first_rx_desc_dma); +#endif + /* Init Transmit chain */ + tmp_buf = db->buf_pool_start; + tmp_buf_dma = db->buf_pool_dma_start; + tmp_tx_dma = db->first_tx_desc_dma; + for (tmp_tx = db->first_tx_desc, i = 0; + i < TX_DESC_CNT; i++, tmp_tx++) { + tmp_tx->tx_buf_ptr = (char *)tmp_buf; + tmp_tx->tdes0 = cpu_to_le32(0); + tmp_tx->tdes1 = cpu_to_le32(0x81000000); /* IC, chain */ + tmp_tx->tdes2 = cpu_to_le32(tmp_buf_dma); + tmp_tx_dma += sizeof(struct tx_desc); + tmp_tx->tdes3 = cpu_to_le32(tmp_tx_dma); + tmp_tx->next_tx_desc = tmp_tx + 1; + tmp_buf = tmp_buf + TX_BUF_ALLOC; + tmp_buf_dma = tmp_buf_dma + TX_BUF_ALLOC; + } + (--tmp_tx)->tdes3 = cpu_to_le32(db->first_tx_desc_dma); + tmp_tx->next_tx_desc = db->first_tx_desc; + + /* Init Receive descriptor chain */ + tmp_rx_dma = db->first_rx_desc_dma; + for (tmp_rx = db->first_rx_desc, i = 0; i < RX_DESC_CNT; + i++, tmp_rx++) { + tmp_rx->rdes0 = cpu_to_le32(0); + tmp_rx->rdes1 = cpu_to_le32(0x01000600); + tmp_rx_dma += sizeof(struct rx_desc); + tmp_rx->rdes3 = cpu_to_le32(tmp_rx_dma); + tmp_rx->next_rx_desc = tmp_rx + 1; + } + (--tmp_rx)->rdes3 = cpu_to_le32(db->first_rx_desc_dma); + tmp_rx->next_rx_desc = db->first_rx_desc; + + /* pre-allocate Rx buffer */ + allocate_rx_buffer(db); +} + +/* + * Update CR6 value + * Firstly stop ULI526X, then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + + outl(cr6_data, ioaddr + DCR6); + udelay(5); +} + +/* + * Allocate rx buffer, + */ + +static void allocate_rx_buffer(struct uli526x_board_info *db) +{ + int index; + struct rx_desc *rxptr; + rxptr = db->first_rx_desc; + u32 addr; + + for (index = 0; index < RX_DESC_CNT; index++) { + addr = (u32)NetRxPackets[index]; + addr += (16 - (addr & 15)); + rxptr->rx_buf_ptr = (char *) addr; + rxptr->rdes2 = cpu_to_le32(addr); + rxptr->rdes0 = cpu_to_le32(0x80000000); +#ifdef DEBUG + printf("%s(): Number 0x%x:\n", __FUNCTION__, index); + printf("%s(): addr 0x%x:\n", __FUNCTION__, addr); + printf("%s(): rxptr address = 0x%x\n", __FUNCTION__, rxptr); + printf("%s(): rxptr buf address = 0x%x\n", \ + __FUNCTION__, rxptr->rx_buf_ptr); + printf("%s(): rdes2 = 0x%x\n", __FUNCTION__, rxptr->rdes2); +#endif + rxptr = rxptr->next_rx_desc; + } +} + +/* + * Read one word data from the serial ROM + */ + +static u16 read_srom_word(long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + udelay(5); + srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) + ? 1 : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + udelay(5); + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + +/* + * Set 10/100 phyxcer capability + * AUTO mode : phyxcer register4 is NIC capability + * Force mode: phyxcer register4 is the force media + */ + +static void uli526x_set_phyxcer(struct uli526x_board_info *db) +{ + u16 phy_reg; + + /* Phyxcer capability setting */ + phy_reg = phy_read(db->ioaddr, db->phy_addr, 4, db->chip_id) & ~0x01e0; + + if (db->media_mode & ULI526X_AUTO) { + /* AUTO Mode */ + phy_reg |= db->PHY_reg4; + } else { + /* Force Mode */ + switch (db->media_mode) { + case ULI526X_10MHF: phy_reg |= 0x20; break; + case ULI526X_10MFD: phy_reg |= 0x40; break; + case ULI526X_100MHF: phy_reg |= 0x80; break; + case ULI526X_100MFD: phy_reg |= 0x100; break; + } + + } + + /* Write new capability to Phyxcer Reg4 */ + if (!(phy_reg & 0x01e0)) { + phy_reg |= db->PHY_reg4; + db->media_mode |= ULI526X_AUTO; + } + phy_write(db->ioaddr, db->phy_addr, 4, phy_reg, db->chip_id); + + /* Restart Auto-Negotiation */ + phy_write(db->ioaddr, db->phy_addr, 0, 0x1200, db->chip_id); + udelay(50); +} + +/* + * Write a word to Phy register + */ + +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, + u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if (chip_id == PCI_ULI5263_ID) { + phy_writeby_cr10(iobase, phy_addr, offset, phy_data); + return; + } + /* M5261/M5263 Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + + /* Write a word data to PHY controller */ + for (i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, phy_data & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); +} + +/* + * Read a word data from phy register + */ + +static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if (chip_id == PCI_ULI5263_ID) + return phy_readby_cr10(iobase, phy_addr, offset); + /* M5261/M5263 Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1, chip_id); + phy_write_1bit(ioaddr, PHY_DATA_0, chip_id); + + /* Send Phy address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, phy_addr & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Send register address */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, offset & i ? + PHY_DATA_1 : PHY_DATA_0, chip_id); + + /* Skip transition state */ + phy_read_1bit(ioaddr, chip_id); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr, chip_id); + } + + return phy_data; +} + +static u16 phy_readby_cr10(unsigned long iobase, u8 phy_addr, u8 offset) +{ + unsigned long ioaddr, cr10_value; + + ioaddr = iobase + DCR10; + cr10_value = phy_addr; + cr10_value = (cr10_value<<5) + offset; + cr10_value = (cr10_value<<16) + 0x08000000; + outl(cr10_value, ioaddr); + udelay(1); + while (1) { + cr10_value = inl(ioaddr); + if (cr10_value & 0x10000000) + break; + } + return (cr10_value&0x0ffff); +} + +static void phy_writeby_cr10(unsigned long iobase, u8 phy_addr, + u8 offset, u16 phy_data) +{ + unsigned long ioaddr, cr10_value; + + ioaddr = iobase + DCR10; + cr10_value = phy_addr; + cr10_value = (cr10_value<<5) + offset; + cr10_value = (cr10_value<<16) + 0x04000000 + phy_data; + outl(cr10_value, ioaddr); + udelay(1); +} +/* + * Write one bit data to Phy Controller + */ + +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data, u32 chip_id) +{ + outl(phy_data , ioaddr); /* MII Clock Low */ + udelay(1); + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + udelay(1); + outl(phy_data , ioaddr); /* MII Clock Low */ + udelay(1); +} + +/* + * Read one bit phy data from PHY controller + */ + +static u16 phy_read_1bit(unsigned long ioaddr, u32 chip_id) +{ + u16 phy_data; + + outl(0x50000 , ioaddr); + udelay(1); + phy_data = (inl(ioaddr) >> 19) & 0x1; + outl(0x40000 , ioaddr); + udelay(1); + + return phy_data; +} + +/* + * Set MAC address to ID Table + */ + +static void set_mac_addr(struct eth_device *dev) +{ + int i; + u16 addr; + struct uli526x_board_info *db = dev->priv; + outl(0x10000, db->ioaddr + DCR0); /* Diagnosis mode */ + /* Reset dianostic pointer port */ + outl(0x1c0, db->ioaddr + DCR13); + outl(0, db->ioaddr + DCR14); /* Clear reset port */ + outl(0x10, db->ioaddr + DCR14); /* Reset ID Table pointer */ + outl(0, db->ioaddr + DCR14); /* Clear reset port */ + outl(0, db->ioaddr + DCR13); /* Clear CR13 */ + /* Select ID Table access port */ + outl(0x1b0, db->ioaddr + DCR13); + /* Read MAC address from CR14 */ + for (i = 0; i < 3; i++) { + addr = dev->enetaddr[2 * i] | (dev->enetaddr[2 * i + 1] << 8); + outl(addr, db->ioaddr + DCR14); + } + /* write end */ + outl(0, db->ioaddr + DCR13); /* Clear CR13 */ + outl(0, db->ioaddr + DCR0); /* Clear CR0 */ + udelay(10); + return; +} diff --git a/roms/u-boot-sam460ex/drivers/net/vsc7385.c b/roms/u-boot-sam460ex/drivers/net/vsc7385.c new file mode 100644 index 000000000..ada42c41f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/vsc7385.c @@ -0,0 +1,96 @@ +/* + * Vitesse 7385 Switch Firmware Upload + * + * Author: Timur Tabi <timur@freescale.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. This file is licensed + * under the terms of the GNU General Public License version 2. This + * program is licensed "as is" without any warranty of any kind, whether + * express or implied. + * + * This module uploads proprietary firmware for the Vitesse VSC7385 5-port + * switch. + */ + +#include <config.h> +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> + +/* + * Upload a Vitesse VSC7385 firmware image to the hardware + * + * This function takes a pointer to a VSC7385 firmware image and a size, and + * uploads that firmware to the VSC7385. + * + * This firmware is typically located at a board-specific flash address, + * and the size is typically 8KB. + * + * The firmware is Vitesse proprietary. + * + * Further details on the register information can be obtained from Vitesse. + */ +int vsc7385_upload_firmware(void *firmware, unsigned int size) +{ + u8 *fw = firmware; + unsigned int i; + + u32 *gloreset = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c050); + u32 *icpu_ctrl = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c040); + u32 *icpu_addr = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c044); + u32 *icpu_data = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c048); + u32 *icpu_rom_map = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c070); +#ifdef DEBUG + u32 *chipid = (u32 *) (CONFIG_SYS_VSC7385_BASE + 0x1c060); +#endif + + out_be32(gloreset, 3); + udelay(200); + + out_be32(icpu_ctrl, 0x8E); + udelay(20); + + out_be32(icpu_rom_map, 1); + udelay(20); + + /* Write the firmware to I-RAM */ + out_be32(icpu_addr, 0); + udelay(20); + + for (i = 0; i < size; i++) { + out_be32(icpu_data, fw[i]); + udelay(20); + if (ctrlc()) + return -EINTR; + } + + /* Read back and compare */ + out_be32(icpu_addr, 0); + udelay(20); + + for (i = 0; i < size; i++) { + u8 value; + + value = (u8) in_be32(icpu_data); + udelay(20); + if (value != fw[i]) { + debug("VSC7385: Upload mismatch: address 0x%x, " + "read value 0x%x, image value 0x%x\n", + i, value, fw[i]); + + return -EIO; + } + if (ctrlc()) + break; + } + + out_be32(icpu_ctrl, 0x0B); + udelay(20); + +#ifdef DEBUG + printf("VSC7385: Chip ID is %08x\n", in_be32(chipid)); + udelay(20); +#endif + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/net/xilinx_emaclite.c b/roms/u-boot-sam460ex/drivers/net/xilinx_emaclite.c new file mode 100644 index 000000000..0820daa2b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/net/xilinx_emaclite.c @@ -0,0 +1,355 @@ +/* + * (C) Copyright 2007-2009 Michal Simek + * (C) Copyright 2003 Xilinx Inc. + * + * Michal SIMEK <monstr@monstr.eu> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <net.h> +#include <config.h> +#include <asm/io.h> + +#undef DEBUG + +#define ENET_MAX_MTU PKTSIZE +#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN +#define ENET_ADDR_LENGTH 6 + +/* EmacLite constants */ +#define XEL_BUFFER_OFFSET 0x0800 /* Next buffer's offset */ +#define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */ +#define XEL_TSR_OFFSET 0x07FC /* Tx status */ +#define XEL_RSR_OFFSET 0x17FC /* Rx status */ +#define XEL_RXBUFF_OFFSET 0x1000 /* Receive Buffer */ + +/* Xmit complete */ +#define XEL_TSR_XMIT_BUSY_MASK 0x00000001UL +/* Xmit interrupt enable bit */ +#define XEL_TSR_XMIT_IE_MASK 0x00000008UL +/* Buffer is active, SW bit only */ +#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000UL +/* Program the MAC address */ +#define XEL_TSR_PROGRAM_MASK 0x00000002UL +/* define for programming the MAC address into the EMAC Lite */ +#define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) + +/* Transmit packet length upper byte */ +#define XEL_TPLR_LENGTH_MASK_HI 0x0000FF00UL +/* Transmit packet length lower byte */ +#define XEL_TPLR_LENGTH_MASK_LO 0x000000FFUL + +/* Recv complete */ +#define XEL_RSR_RECV_DONE_MASK 0x00000001UL +/* Recv interrupt enable bit */ +#define XEL_RSR_RECV_IE_MASK 0x00000008UL + +typedef struct { + unsigned int baseaddress; /* Base address for device (IPIF) */ + unsigned int nexttxbuffertouse; /* Next TX buffer to write to */ + unsigned int nextrxbuffertouse; /* Next RX buffer to read from */ + unsigned char deviceid; /* Unique ID of device - for future */ +} xemaclite; + +static xemaclite emaclite; + +static u32 etherrxbuff[PKTSIZE_ALIGN/4]; /* Receive buffer */ + +/* hardcoded MAC address for the Xilinx EMAC Core when env is nowhere*/ +#ifdef CONFIG_ENV_IS_NOWHERE +static u8 emacaddr[ENET_ADDR_LENGTH] = { 0x00, 0x0a, 0x35, 0x00, 0x22, 0x01 }; +#else +static u8 emacaddr[ENET_ADDR_LENGTH]; +#endif + +void xemaclite_alignedread (u32 * srcptr, void *destptr, unsigned bytecount) +{ + unsigned int i; + u32 alignbuffer; + u32 *to32ptr; + u32 *from32ptr; + u8 *to8ptr; + u8 *from8ptr; + + from32ptr = (u32 *) srcptr; + + /* Word aligned buffer, no correction needed. */ + to32ptr = (u32 *) destptr; + while (bytecount > 3) { + *to32ptr++ = *from32ptr++; + bytecount -= 4; + } + to8ptr = (u8 *) to32ptr; + + alignbuffer = *from32ptr++; + from8ptr = (u8 *) & alignbuffer; + + for (i = 0; i < bytecount; i++) { + *to8ptr++ = *from8ptr++; + } +} + +void xemaclite_alignedwrite (void *srcptr, u32 destptr, unsigned bytecount) +{ + unsigned i; + u32 alignbuffer; + u32 *to32ptr = (u32 *) destptr; + u32 *from32ptr; + u8 *to8ptr; + u8 *from8ptr; + + from32ptr = (u32 *) srcptr; + while (bytecount > 3) { + + *to32ptr++ = *from32ptr++; + bytecount -= 4; + } + + alignbuffer = 0; + to8ptr = (u8 *) & alignbuffer; + from8ptr = (u8 *) from32ptr; + + for (i = 0; i < bytecount; i++) { + *to8ptr++ = *from8ptr++; + } + + *to32ptr++ = alignbuffer; +} + +void eth_halt (void) +{ + debug ("eth_halt\n"); +} + +int eth_init (bd_t * bis) +{ + uchar enetaddr[6]; + + debug ("EmacLite Initialization Started\n"); + memset (&emaclite, 0, sizeof (xemaclite)); + emaclite.baseaddress = XILINX_EMACLITE_BASEADDR; + + if (!eth_getenv_enetaddr("ethaddr", enetaddr)) { + memcpy(enetaddr, emacaddr, ENET_ADDR_LENGTH); + eth_setenv_enetaddr("ethaddr", enetaddr); + } + +/* + * TX - TX_PING & TX_PONG initialization + */ + /* Restart PING TX */ + out_be32 (emaclite.baseaddress + XEL_TSR_OFFSET, 0); + /* Copy MAC address */ + xemaclite_alignedwrite (enetaddr, + emaclite.baseaddress, ENET_ADDR_LENGTH); + /* Set the length */ + out_be32 (emaclite.baseaddress + XEL_TPLR_OFFSET, ENET_ADDR_LENGTH); + /* Update the MAC address in the EMAC Lite */ + out_be32 (emaclite.baseaddress + XEL_TSR_OFFSET, XEL_TSR_PROG_MAC_ADDR); + /* Wait for EMAC Lite to finish with the MAC address update */ + while ((in_be32 (emaclite.baseaddress + XEL_TSR_OFFSET) & + XEL_TSR_PROG_MAC_ADDR) != 0) ; + +#ifdef CONFIG_XILINX_EMACLITE_TX_PING_PONG + /* The same operation with PONG TX */ + out_be32 (emaclite.baseaddress + XEL_TSR_OFFSET + XEL_BUFFER_OFFSET, 0); + xemaclite_alignedwrite (enetaddr, emaclite.baseaddress + + XEL_BUFFER_OFFSET, ENET_ADDR_LENGTH); + out_be32 (emaclite.baseaddress + XEL_TPLR_OFFSET, ENET_ADDR_LENGTH); + out_be32 (emaclite.baseaddress + XEL_TSR_OFFSET + XEL_BUFFER_OFFSET, + XEL_TSR_PROG_MAC_ADDR); + while ((in_be32 (emaclite.baseaddress + XEL_TSR_OFFSET + + XEL_BUFFER_OFFSET) & XEL_TSR_PROG_MAC_ADDR) != 0) ; +#endif + +/* + * RX - RX_PING & RX_PONG initialization + */ + /* Write out the value to flush the RX buffer */ + out_be32 (emaclite.baseaddress + XEL_RSR_OFFSET, XEL_RSR_RECV_IE_MASK); +#ifdef CONFIG_XILINX_EMACLITE_RX_PING_PONG + out_be32 (emaclite.baseaddress + XEL_RSR_OFFSET + XEL_BUFFER_OFFSET, + XEL_RSR_RECV_IE_MASK); +#endif + + debug ("EmacLite Initialization complete\n"); + return 0; +} + +int xemaclite_txbufferavailable (xemaclite * instanceptr) +{ + u32 reg; + u32 txpingbusy; + u32 txpongbusy; + /* + * Read the other buffer register + * and determine if the other buffer is available + */ + reg = in_be32 (instanceptr->baseaddress + + instanceptr->nexttxbuffertouse + 0); + txpingbusy = ((reg & XEL_TSR_XMIT_BUSY_MASK) == + XEL_TSR_XMIT_BUSY_MASK); + + reg = in_be32 (instanceptr->baseaddress + + (instanceptr->nexttxbuffertouse ^ XEL_TSR_OFFSET) + 0); + txpongbusy = ((reg & XEL_TSR_XMIT_BUSY_MASK) == + XEL_TSR_XMIT_BUSY_MASK); + + return (!(txpingbusy && txpongbusy)); +} + +int eth_send (volatile void *ptr, int len) { + + unsigned int reg; + unsigned int baseaddress; + + unsigned maxtry = 1000; + + if (len > ENET_MAX_MTU) + len = ENET_MAX_MTU; + + while (!xemaclite_txbufferavailable (&emaclite) && maxtry) { + udelay (10); + maxtry--; + } + + if (!maxtry) { + printf ("Error: Timeout waiting for ethernet TX buffer\n"); + /* Restart PING TX */ + out_be32 (emaclite.baseaddress + XEL_TSR_OFFSET, 0); +#ifdef CONFIG_XILINX_EMACLITE_TX_PING_PONG + out_be32 (emaclite.baseaddress + XEL_TSR_OFFSET + + XEL_BUFFER_OFFSET, 0); +#endif + return 0; + } + + /* Determine the expected TX buffer address */ + baseaddress = (emaclite.baseaddress + emaclite.nexttxbuffertouse); + + /* Determine if the expected buffer address is empty */ + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + if (((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) + && ((in_be32 ((baseaddress) + XEL_TSR_OFFSET) + & XEL_TSR_XMIT_ACTIVE_MASK) == 0)) { + +#ifdef CONFIG_XILINX_EMACLITE_TX_PING_PONG + emaclite.nexttxbuffertouse ^= XEL_BUFFER_OFFSET; +#endif + debug ("Send packet from 0x%x\n", baseaddress); + /* Write the frame to the buffer */ + xemaclite_alignedwrite ((void *) ptr, baseaddress, len); + out_be32 (baseaddress + XEL_TPLR_OFFSET,(len & + (XEL_TPLR_LENGTH_MASK_HI | XEL_TPLR_LENGTH_MASK_LO))); + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + reg |= XEL_TSR_XMIT_BUSY_MASK; + if ((reg & XEL_TSR_XMIT_IE_MASK) != 0) { + reg |= XEL_TSR_XMIT_ACTIVE_MASK; + } + out_be32 (baseaddress + XEL_TSR_OFFSET, reg); + return 1; + } +#ifdef CONFIG_XILINX_EMACLITE_TX_PING_PONG + /* Switch to second buffer */ + baseaddress ^= XEL_BUFFER_OFFSET; + /* Determine if the expected buffer address is empty */ + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + if (((reg & XEL_TSR_XMIT_BUSY_MASK) == 0) + && ((in_be32 ((baseaddress) + XEL_TSR_OFFSET) + & XEL_TSR_XMIT_ACTIVE_MASK) == 0)) { + debug ("Send packet from 0x%x\n", baseaddress); + /* Write the frame to the buffer */ + xemaclite_alignedwrite ((void *) ptr, baseaddress, len); + out_be32 (baseaddress + XEL_TPLR_OFFSET,(len & + (XEL_TPLR_LENGTH_MASK_HI | XEL_TPLR_LENGTH_MASK_LO))); + reg = in_be32 (baseaddress + XEL_TSR_OFFSET); + reg |= XEL_TSR_XMIT_BUSY_MASK; + if ((reg & XEL_TSR_XMIT_IE_MASK) != 0) { + reg |= XEL_TSR_XMIT_ACTIVE_MASK; + } + out_be32 (baseaddress + XEL_TSR_OFFSET, reg); + return 1; + } +#endif + puts ("Error while sending frame\n"); + return 0; +} + +int eth_rx (void) +{ + unsigned int length; + unsigned int reg; + unsigned int baseaddress; + + baseaddress = emaclite.baseaddress + emaclite.nextrxbuffertouse; + reg = in_be32 (baseaddress + XEL_RSR_OFFSET); + debug ("Testing data at address 0x%x\n", baseaddress); + if ((reg & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { +#ifdef CONFIG_XILINX_EMACLITE_RX_PING_PONG + emaclite.nextrxbuffertouse ^= XEL_BUFFER_OFFSET; +#endif + } else { +#ifndef CONFIG_XILINX_EMACLITE_RX_PING_PONG + debug ("No data was available - address 0x%x\n", baseaddress); + return 0; +#else + baseaddress ^= XEL_BUFFER_OFFSET; + reg = in_be32 (baseaddress + XEL_RSR_OFFSET); + if ((reg & XEL_RSR_RECV_DONE_MASK) != + XEL_RSR_RECV_DONE_MASK) { + debug ("No data was available - address 0x%x\n", + baseaddress); + return 0; + } +#endif + } + /* Get the length of the frame that arrived */ + switch(((in_be32 (baseaddress + XEL_RXBUFF_OFFSET + 0xC)) & + 0xFFFF0000 ) >> 16) { + case 0x806: + length = 42 + 20; /* FIXME size of ARP */ + debug ("ARP Packet\n"); + break; + case 0x800: + length = 14 + 14 + + (((in_be32 (baseaddress + XEL_RXBUFF_OFFSET + 0x10)) & + 0xFFFF0000) >> 16); /* FIXME size of IP packet */ + debug ("IP Packet\n"); + break; + default: + debug ("Other Packet\n"); + length = ENET_MAX_MTU; + break; + } + + xemaclite_alignedread ((u32 *) (baseaddress + XEL_RXBUFF_OFFSET), + etherrxbuff, length); + + /* Acknowledge the frame */ + reg = in_be32 (baseaddress + XEL_RSR_OFFSET); + reg &= ~XEL_RSR_RECV_DONE_MASK; + out_be32 (baseaddress + XEL_RSR_OFFSET, reg); + + debug ("Packet receive from 0x%x, length %dB\n", baseaddress, length); + NetReceive ((uchar *) etherrxbuff, length); + return 1; + +} |