Files
Zorro-LAN-IDE/Driver/baxnet/source/sdnet/enc28j60.c
T
2019-01-03 00:43:32 +01:00

752 lines
19 KiB
C

/*
Microchip ENC28J60 Ethernet Interface Driver
Author: Henryk Richter
Copyright: GPL V2
Notes: when I ran into timing issues in early development, I moved over to quickly
testable ASM code for SPI communication. I didn't bother porting that stuff
back into C code. So this file now references enc28j60l.asm for lowlevel direct
hardware access.
As a consequence, the only functions needed from spi.c are the spi_init() and
spi_disable_eth().
TODO: - support for power down when unused, i.e. offline
- replace busy waiting loop after reset
Based on work by
Christian Vogelgsaang
Guido Socher
Pascal Stang (see enc28j60.c in AVRlib library for the original file)
*/
#undef _DO_DUMP
/* used for Delay() */
#include <dos/dos.h>
#include <proto/dos.h>
#ifdef _DO_DUMP
#include <stdio.h>
#endif
#include "compiler.h"
#include "enc28j60.h"
#include "enc28j60l.h" /* import lowlevel asm functions */
/* #include "spi.h" */ /* no direct use anymore, just for init purposes) */
#include "device.h"
/*
The RXSTART_INIT must be zero. See Rev. B4 Silicon Errata point 5.
Buffer boundaries applied to internal 8K ram
the entire available packet buffer space is allocated
rx packet layout
6 byte heaader
1518
sum: 1524
*/
#define RXSTART_INIT 0x0000 /* start of RX buffer, room for 4 packets */
#define RXSTOP_INIT 0x19FF /* end of RX buffer */
#define TXSTART_INIT 0x1A00 /* start of TX buffer, room for 1 packet */
#define TXSTOP_INIT 0x1FFF /* end of TX buffer */
/* max frame length which the conroller will accept: */
/* (note: maximum ethernet frame length would be 1518 when VLAN tagging is unused) */
#define MAX_FRAMELEN 1518
static u16 gNextPacketPtr;
static u08 is_full_duplex;
static u08 rev;
struct enc28j60_initvars {
u16 have_recv;
u08 last_mac[6];
u08 macon1;
u08 macon2;
u08 macon3;
u08 macon4;
};
static struct enc28j60_initvars initvars;
static void enc28j60_recv_reset( struct enc28j60_initvars *iv, u08 restart );
static int enc28j60_check_config( struct enc28j60_initvars *iv );
static uint8_t readOp (uint8_t op, uint8_t address)
{
return enc28j60l_ReadOp( op, address );
}
static void writeOp (uint8_t op, uint8_t address, uint8_t data)
{
enc28j60l_WriteOp( op, address,data );
}
static void readBuf(uint16_t len, uint8_t* data)
{
enc28j60l_ReadBuffer( data, (unsigned int)len );
}
void SetBank (uint8_t address)
{
enc28j60l_SetBank(address);
}
static uint8_t readRegByte (uint8_t address)
{
return enc28j60l_ReadRegByte( address );
}
#if 0
static uint16_t readReg(uint8_t address) {
return readRegByte(address) + (readRegByte(address+1) << 8);
}
#endif
static void writeRegByte (uint8_t address, uint8_t data)
{
enc28j60l_WriteRegByte( address, data );
}
static void writeReg(uint8_t address, uint16_t data)
{
enc28j60l_WriteReg( address, data );
}
/*
the PHY registers require special care
*/
static uint16_t readPhyByte (uint8_t address) {
writeRegByte(MIREGADR, address);
writeRegByte(MICMD, MICMD_MIIRD);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
writeRegByte(MICMD, 0x00);
return readRegByte(MIRD+1);
}
static void writePhy (uint8_t address, uint16_t data) {
writeRegByte(MIREGADR, address);
writeReg(MIWR, data);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
}
/* just a pass-through to the lowlevel routines */
void enc28j60_SetSPISpeed( unsigned long clock_divider )
{
enc28j60l_SetSPISpeed( clock_divider );
}
/* ---------- init ---------- */
/* Functions to enable/disable broadcast filter bits */
/* With the bit set, broadcast packets are filtered. */
void enc28j60_broadcast_multicast_filter( u08 flags )
{
u08 val = ERXFCON_UCEN|ERXFCON_CRCEN; /* default: crc check and unicast */
if( flags & PIO_INIT_PROMISC )
{
val = 0;
}
else
{
if( flags & PIO_INIT_BROAD_CAST )
val |= ERXFCON_BCEN;
if( flags & PIO_INIT_MULTI_CAST )
val |= ERXFCON_MCEN;
}
writeRegByte(ERXFCON, val );
}
static void enc28j60_recv_reset( struct enc28j60_initvars *iv, u08 restart )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ESTAT, ESTAT_TXABRT);
gNextPacketPtr = RXSTART_INIT;
writeReg(ERXST, RXSTART_INIT);
writeReg(ERXRDPT, RXSTOP_INIT );
writeReg(ERXND, RXSTOP_INIT);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF|EIR_RXERIF );
writeReg(MAMXFL, MAX_FRAMELEN);
writeRegByte(MACON4, iv->macon4 ); /* DEFER bit */
writeRegByte(MACON3, iv->macon3 );
writeRegByte(MACON2, iv->macon2 );
writeRegByte(MACON1, iv->macon1 );
enc28j60_setMAC( iv->last_mac );
writePhy(PHIE, PHIE_PGEIE|PHIE_LNKIE );
writePhy(PHLCON, 0x3c12 ); /* 3(fixed) C(LinkStat+RX Act) 1(TX Act) 2(Stretch enable) */
/* prepare flow control */
writeReg(EPAUS, 20 * 100); /* 100ms */
if( restart )
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
iv->have_recv = 0;
}
static int enc28j60_check_config( struct enc28j60_initvars *iv )
{
int ret = 1; /* def: be optimistic */
/* check RX registers */
if( !(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_RXEN ) )
ret = 0;
if( (readRegByte(MACON1)&iv->macon1) != iv->macon1 )
ret = 0;
if( (readRegByte(MACON2)&iv->macon2) != iv->macon2 )
ret = 0;
if( (readRegByte(MACON3)&iv->macon3) != iv->macon3 )
ret = 0;
/* check TX problems */
if( readRegByte(ESTAT) & ESTAT_TXABRT )
ret = 0;
if( readRegByte(EIR) & EIR_TXERIF )
ret = 0;
/* Verify MAC */
if( readRegByte(MAADR5) != iv->last_mac[0] )
ret = 0;
if( readRegByte(MAADR4) != iv->last_mac[1] )
ret = 0;
if( readRegByte(MAADR3) != iv->last_mac[2] )
ret = 0;
if( readRegByte(MAADR2) != iv->last_mac[3] )
ret = 0;
if( readRegByte(MAADR1) != iv->last_mac[4] )
ret = 0;
if( readRegByte(MAADR0) != iv->last_mac[5] )
ret = 0;
return ret;
}
u08 enc28j60_init(const u08 macaddr[6], u08 flags)
{
unsigned int count;
u08 mac3val;
struct enc28j60_initvars *iv = &initvars;
enc28j60l_Init(); /* spi_init(); spi_disable_eth(); */
{
int i;
for(i=0 ; i < 6 ; i++ )
iv->last_mac[i] = macaddr[i];
}
is_full_duplex = (flags & PIO_INIT_FULL_DUPLEX) == PIO_INIT_FULL_DUPLEX;
/* soft reset cpu */
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
#if 1
enc28j60l_UMinDelay( 2000 ); /* wait 2 ms */
#else
/* Delay(1);*/ /* _delay_ms(2); // errata B7/2 */
/* hard-coded delay right now (TODO: import timer functions) */
for( count = 0 ; count < 20000 ; count++ )
{
mac3val |= (u08)*((volatile unsigned short*)(0x00DE0004));
}
#endif
/* wait or error */
count = 0;
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) {
count ++;
if(count == 0xfff) {
return PIO_NOT_FOUND;
}
}
writeOp(ENC28J60_WRITE_CTRL_REG, ECON1, 0);
/* return rev */
rev = readRegByte(EREVID);
/* microchip forgot to step the number on the silcon when they */
/* released the revision B7. 6 is now rev B7. We still have */
/* to see what they do when they release B8. At the moment */
/* there is no B8 out yet */
if ( (rev < 1) || (rev > 10) ) /* I don't expect a rev > 10 (+1), so I use it to check for module presence */
return PIO_NOT_FOUND; /* important: check this before any writePhy -> no module gets us 0xff back */
if (rev > 5) ++rev;
writeReg(ETXST, TXSTART_INIT);
writeReg(ETXND, TXSTOP_INIT);
/* set packet filter */
enc28j60_broadcast_multicast_filter( flags );/* flags & (PIO_INIT_BROAD_CAST|PIO_INIT_MULTI_CAST) ); */
/* BIST pattern generator? */
writeReg(EPMM0, 0x303f);
writeReg(EPMCS, 0xf7f9);
if(is_full_duplex) {
writeRegByte(MABBIPG, 0x15);
writeReg(MAIPG, 0x0012);
} else {
writeRegByte(MABBIPG, 0x12);
writeReg(MAIPG, 0x0C12);
}
/* MAC init (with flow control) */
mac3val = MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN;
if(is_full_duplex) {
mac3val |= MACON3_FULDPX;
iv->macon4=0;
}
else
{
iv->macon4 = 1<<6; /* DEFER bit */
}
iv->macon3 = mac3val;
iv->macon2 = 0x00;
iv->macon1 = MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS;
/* set packet pointers */
enc28j60_recv_reset( iv, 0 );
/* PHY init */
if(is_full_duplex) {
writePhy(PHCON1, PHCON1_PDPXMD);
writePhy(PHCON2, 0);
} else {
writePhy(PHCON1, 0);
writePhy(PHCON2, PHCON2_HDLDIS);
}
writePhy(PHLCON, 0x3c12 ); /* 3(fixed) C(LinkStat+RX Act) 1(TX Act) 2(Stretch enable) */
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_DMAIF|EIR_LINKIF|EIR_TXIF|EIR_TXERIF|EIR_RXERIF|EIR_PKTIF);
writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); /* |EIE_LINKIE|EIE_TXIE|EIE_TXERIE|EIE_RXERIE); */
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
/* wait a while for "LINK UP" -> break after 200 ms */
{
unsigned char status;
int runs = 100;
while( runs-- )
{
enc28j60_status( PIO_STATUS_LINK_UP, &status );
if( status )
break;
enc28j60l_UMinDelay( 2000 ); /* wait 2+ ms */
}
}
return PIO_OK;
}
/* ---------- exit ---------- */
void enc28j60_exit(void)
{
enc28j60_setOffline();
enc28j60l_Shutdown();
}
u08 enc28j60_setOnline( void )
{
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
return 1;
}
u08 enc28j60_setOffline( void )
{
SetBank(EIR);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
return 1;
}
void enc28j60_setMAC( const u08 macaddr[6] )
{
/* set mac */
writeRegByte(MAADR5, macaddr[0]);
writeRegByte(MAADR4, macaddr[1]);
writeRegByte(MAADR3, macaddr[2]);
writeRegByte(MAADR2, macaddr[3]);
writeRegByte(MAADR1, macaddr[4]);
writeRegByte(MAADR0, macaddr[5]);
SetBank(EIR);
}
void enc28j60_getMAC( u08 macaddr[6] ) /* mainly for debug */
{
/* set mac */
macaddr[0] = readRegByte(MAADR5);
macaddr[1] = readRegByte(MAADR4);
macaddr[2] = readRegByte(MAADR3);
macaddr[3] = readRegByte(MAADR2);
macaddr[4] = readRegByte(MAADR1);
macaddr[5] = readRegByte(MAADR0);
}
/* ---------- control ---------- */
u08 enc28j60_control(u08 control_id, u08 value)
{
switch(control_id) {
case PIO_CONTROL_FLOW:
{
u08 flag;
if(is_full_duplex) {
flag = value ? 2 : 3;
} else {
flag = value ? 1 : 0;
}
writeRegByte(EFLOCON, flag);
return PIO_OK;
}
default:
return PIO_NOT_FOUND;
}
}
/* ---------- status ---------- */
u08 enc28j60_status(u08 status_id, u08 *value)
{
switch(status_id) {
case PIO_STATUS_VERSION:
*value = rev;
return PIO_OK;
case PIO_STATUS_LINK_UP:
*value = (readPhyByte(PHSTAT2) >> 2) & 1;
return PIO_OK;
case PIO_STATUS_FULLDPX:
*value = (readRegByte(MACON3) & MACON3_FULDPX) ? 1 : 0;/* + (((u16)readRegByte(MACON1+3))<<8); */
return PIO_OK;
default:
*value = 0;
return PIO_NOT_FOUND;
}
}
/* ---------- send ---------- */
u08 enc28j60_send(const u08 *data, u16 size)
{
/* wait for tx ready */
/* We can't do it here like on ATMEL MCUs, simply because
the Vampire SPI is way faster than 10 MBit/s so that
we would overwrite a frame still being sent out
-> wait first, then fill buffer
*/
while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
{
/* errata sheet issue 12 -> TXRTS never becomes 0 */
/* ignores errata issue 13 -> frame loss due to late collision */
/* eworks around errata sheet issue 15 -> ESTAT.LATECOL not set */
if (readRegByte(EIR) & EIR_TXERIF) {
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
}
}
/* write buffer */
#if 0
if(0)
{ u08* d = (u08*)data;
d[size]=0;
size=(size+1)&0xfffe;
}
#endif
enc28j60l_WriteBuffer( (u08*)data, size, TXSTART_INIT );
/* initiate send */
writeReg(ETXND, TXSTART_INIT+size);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
return PIO_OK;
}
/* ---------- recv ---------- */
/*
unrecoverable error in receiving, bad pointer or other causes
- reset the complete receive logic in order to start receiving anew
input: restart - 0 = don't re-enable RX automatically
1 = re-enable RX after resetting
*/
INLINE void next_pkt(void)
{
if (gNextPacketPtr - 1 > RXSTOP_INIT)
{
#if 1
enc28j60_recv_reset( &initvars, 1 );
#else
writeReg(ERXRDPT, RXSTOP_INIT);
gNextPacketPtr = 0;
#endif
}
else
writeReg(ERXRDPT, gNextPacketPtr - 1);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}
/* endian agnostic version of header read */
static u08 read_hdr(u16 *got_size)
{
struct {
uint8_t nextPacketL;
uint8_t nextPacketH;
uint8_t byteCountL;
uint8_t byteCountH;
uint8_t statusL;
uint8_t statusH;
} header;
readBuf(sizeof header, (uint8_t*) &header);
gNextPacketPtr = (u16)header.nextPacketL + (((u16)header.nextPacketH)<<8);
*got_size = (u16)header.byteCountL + (((u16)header.byteCountH)<<8); /* - 4;*//* -4: remove the CRC count */
return header.statusL;
}
u08 enc28j60_recv(u08 *data, u16 max_size, u16 *got_size)
{
u08 status,result;
u16 len;
/* EIR->PKTIF would be faster but errata sheet says "unreliable" */
u08 pcnt = 1+readRegByte(EPKTCNT); /* wraps to 0 for 0xff = unconnected module */
if( pcnt < 2 ) /* 0xff+1=0x00, 0x00+1=0x01, 0x01+1=0x02 */
return PIO_NOT_FOUND;
writeReg(ERDPT, gNextPacketPtr);
/* read chip's packet header */
status = read_hdr(got_size);
/* was a receive error? */
if ((status & 0x80)==0)
{
/* TODO: recv reset ? */
next_pkt();
return PIO_IO_ERR;
}
/* check size */
len = *got_size;
result = PIO_OK;
if(len > max_size) {
len = max_size;
result = PIO_TOO_LARGE;
}
/* read packet */
readBuf(len, data);
#if 0
{
data[len]=0;
data[len+1]=0;
data[len+2]=0;
data[len+3]=0;
}
#endif
next_pkt();
return result;
}
/* ---------- has_recv ---------- */
u08 enc28j60_has_recv(void)
{
u08 ret = readRegByte(EPKTCNT);
if( !ret )
{
initvars.have_recv++;
if( initvars.have_recv > 10 )
{
/* check for bad config vars and reset hw if that is the case */
if( !enc28j60_check_config( &initvars ) )
{
enc28j60_recv_reset( &initvars, 1 );
}
}
}
else
initvars.have_recv = 0;
return ret;
}
void enc28j60_dump_regs(void)
{
/* this will work only with DOSBase known globally, not when it's redirected to Plipbase */
#ifdef _DO_DUMP
u08 val,eir,eie,econ1,econ2,estat;
u16 erxrdpt,erxwrpt,erxst,erxnd,mamxfll;
u16 macon1,macon3,macon4;
eir = readRegByte(EIR);
eie = readRegByte(EIE);
econ1 = readRegByte(ECON1);
econ2 = readRegByte(ECON2);
estat = readRegByte(ESTAT);
printf("EIR EIE ECON1 ECON2 ESTAT\n");
printf("%02x %02x %02x %02x %02x\n",
eir,eie,econ1,econ2,estat);
erxrdpt = readRegByte(ERXRDPT) + (((u16)readRegByte(ERXRDPT+1))<<8);
erxwrpt = readRegByte(ERXWRPT) + (((u16)readRegByte(ERXWRPT+1))<<8);
erxst = readRegByte(ERXST) + (((u16)readRegByte(ERXST+1))<<8);
erxnd = readRegByte(ERXND) + (((u16)readRegByte(ERXND+1))<<8);
mamxfll = readRegByte(MAMXFL) + (((u16)readRegByte(MAMXFL+1))<<8);
printf("ERXRDPT ERXWRPT ERXST ERXND MAMXFL\n");
printf("%04x %04x %04x %04x %04x\n",
erxrdpt,erxwrpt,erxst,erxnd,mamxfll);
macon1 = readRegByte(MACON1);/* + (((u16)readRegByte(MACON1+1))<<8); */
macon3 = readRegByte(MACON3);/* + (((u16)readRegByte(MACON1+3))<<8); */
macon4 = readRegByte(MACON4);/* + (((u16)readRegByte(MACON1+4))<<8); */
printf("MACON1 MACON3 MACON4\n");
printf("%02x %02x %02x\n",
macon1,macon3,macon4);
printf("MAADR\n");
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
(int)readRegByte(MAADR5),
(int)readRegByte(MAADR4),
(int)readRegByte(MAADR3),
(int)readRegByte(MAADR2),
(int)readRegByte(MAADR1),
(int)readRegByte(MAADR0) );
val = readRegByte(EIR);
#endif
}
#if 0
/* Contributed by Alex M. Based on code from: http://blog.derouineau.fr */
/* /2011/07/putting-enc28j60-ethernet-controler-in-sleep-mode/ */
void enc28j60_power_down( void )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
while(readRegByte(ESTAT) & ESTAT_RXBUSY);
while(readRegByte(ECON1) & ECON1_TXRTS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS);
writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
}
void enc28j60_power_up( void )
{
writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
while(!readRegByte(ESTAT) & ESTAT_CLKRDY);
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}
uint8_t enc28j60_do_BIST ( void )
{
#define RANDOM_FILL 0b0000
#define ADDRESS_FILL 0b0100
#define PATTERN_SHIFT 0b1000
#define RANDOM_RACE 0b1100
/* init */
spi_disable_eth();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
_delay_ms(2); /* errata B7/2 */
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ;
/* now we can start the memory test */
uint16_t macResult;
uint16_t bitsResult;
/* clear some of the registers registers */
writeRegByte(ECON1, 0);
writeReg(EDMAST, 0);
/* Set up necessary pointers for the DMA to calculate over the entire memory */
writeReg(EDMAND, 0x1FFFu);
writeReg(ERXND, 0x1FFFu);
/* Enable Test Mode and do an Address Fill */
SetBank(EBSTCON);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_BISTST | ADDRESS_FILL);
/* wait for BISTST to be reset, only after that are we actually ready to */
/* start the test */
/* this was undocumented :( */
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* now start the actual reading an calculating the checksum until the end is */
/* reached */
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
/* Compare the results */
/* 0xF807 should always be generated in Address fill mode */
if ((macResult != bitsResult) || (bitsResult != 0xF807)) {
return 0;
}
/* reset test flag */
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* Now start the BIST with random data test, and also keep on swapping the */
/* DMA/BIST memory ports. */
writeRegByte(EBSTSD, 0b10101010 /* | millis()*/);
writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_PSEL | EBSTCON_BISTST | RANDOM_FILL);
/* wait for BISTST to be reset, only after that are we actually ready to */
/* start the test */
/* this was undocumented :( */
while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST);
writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME);
/* now start the actual reading an calculating the checksum until the end is */
/* reached */
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN);
SetBank(EDMACS);
while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST);
macResult = readReg(EDMACS);
bitsResult = readReg(EBSTCS);
/* The checksum should be equal */
return macResult == bitsResult;
}
#endif