mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-19 08:28:51 +09:00
![Mike Frysinger](/assets/img/avatar_default.png)
The environment is the canonical storage location of the mac address, so we're killing off the global data location and moving everything to querying the env directly. The drivers that get converted here: 3c589 4xx_enet dc2114x dm9000x enc28j60 fsl_mcdmafec ks8695eth mcffec rtl8019 rtl8169 s3c4510b_eth xilinx_emac xilinx_emaclite Signed-off-by: Mike Frysinger <vapier@gentoo.org> CC: Ben Warren <biggerbadderben@gmail.com> CC: Rolf Offermanns <rof@sysgo.de> CC: Stefan Roese <sr@denx.de> CC: Sascha Hauer <saschahauer@web.de> CC: TsiChung Liew <Tsi-Chung.Liew@freescale.com> CC: Greg Ungerer <greg.ungerer@opengear.com> CC: Xue Ligong <lgxue@hotmail.com> CC: Masami Komiya <mkomiya@sonare.it> CC: Curt Brune <curt@cucy.com> CC: Michal SIMEK <monstr@monstr.eu>
518 lines
12 KiB
C
518 lines
12 KiB
C
/*------------------------------------------------------------------------
|
|
. 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;
|
|
}
|