BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
e8658d5
Branches
Tags
master
flaneth
firmware.dartboard
rtl8019.c
initial commit after making CF identify work
Stefan Schuermans
commited
e8658d5
at 2012-04-15 19:57:57
rtl8019.c
Blame
History
Raw
/* flaneth - flash and ethernet - dartboard mod * version 0.1 date 2008-11-09 * Copyright (C) 2007-2008 Stefan Schuermans <stefan@schuermans.info> * Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html * a BlinkenArea project - http://www.blinkenarea.org/ */ #include <avr/io.h> #include "bus.h" #include "config.h" #include "debug.h" #include "ethernet.h" #include "macros.h" #include "random.h" #include "rtl8019.h" #include "timing.h" // maximum receive unit #define RTL_MRU (640) // reinitialization timeout // - if no reception is detected for this time, the RTL8019 is reinitialized #define RtlReinitTimeoutTicks (50) // in 200ms steps // IO pins of RTL8019 #define RTL_DDR_INT (DDRE) #define RTL_PORT_INT (PORTE) #define RTL_BIT_INT (5) #define RTL_DDR_nAEN (DDRE) #define RTL_PORT_nAEN (PORTE) #define RTL_BIT_nAEN (4) #define RTL_DDR_RST (DDRE) #define RTL_PORT_RST (PORTE) #define RTL_BIT_RST (3) // special pin commands #define RTL_AEN_ACT( ) (bit_clear( RTL_PORT_nAEN, RTL_BIT_nAEN )) #define RTL_AEN_IDLE( ) (bit_set( RTL_PORT_nAEN, RTL_BIT_nAEN )) #define RTL_RESET_ACT( ) (bit_set( RTL_PORT_RST, RTL_BIT_RST )) #define RTL_RESET_IDLE( ) (bit_clear( RTL_PORT_RST, RTL_BIT_RST )) // IO pins of RTL8019 EEPROM interface #define RTL_DDR_EECS (DDRD) #define RTL_PORT_EECS (PORTD) #define RTL_PIN_EECS (PIND) #define RTL_BIT_EECS (7) #define RTL_DDR_EESK (DDRD) #define RTL_PORT_EESK (PORTD) #define RTL_PIN_EESK (PIND) #define RTL_BIT_EESK (4) #define RTL_DDR_EEDI (DDRD) #define RTL_PORT_EEDI (PORTD) #define RTL_PIN_EEDI (PIND) #define RTL_BIT_EEDI (5) #define RTL_DDR_EEDO (DDRD) #define RTL_PORT_EEDO (PORTD) #define RTL_BIT_EEDO (6) // RTL8019 registers #define RTL_REG_CR (0x00) #define RTL_REG_PSTART (0x01) #define RTL_REG_PAR0 (0x01) #define RTL_REG_PSTOP (0x02) #define RTL_REG_BNRY (0x03) #define RTL_REG_TPSR (0x04) #define RTL_REG_TBCR0 (0x05) #define RTL_REG_TBCR1 (0x06) #define RTL_REG_ISR (0x07) #define RTL_REG_CURR (0x07) #define RTL_REG_RSAR0 (0x08) #define RTL_REG_CRDA0 (0x08) #define RTL_REG_RSAR1 (0x09) #define RTL_REG_CRDAl (0x09) #define RTL_REG_RBCR0 (0x0A) #define RTL_REG_ID0 (0x0A) #define RTL_REG_ID1 (0x0B) #define RTL_REG_RBCR1 (0x0B) #define RTL_REG_RSR (0x0C) #define RTL_REG_RCR (0x0C) #define RTL_REG_TCR (0x0D) #define RTL_REG_CNTR0 (0x0D) #define RTL_REG_DCR (0x0E) #define RTL_REG_CNTR1 (0x0E) #define RTL_REG_IMR (0x0F) #define RTL_REG_CNTR2 (0x0F) #define RTL_REG_RDMAPORT (0x10) #define RTL_REG_RSTPORT (0x18) // RTL8019AS CR register bits #define RTL_CR_STP (0) #define RTL_CR_STA (1) #define RTL_CR_TXP (2) #define RTL_CR_RD0 (3) #define RTL_CR_RD1 (4) #define RTL_CR_RD2 (5) #define RTL_CR_PS0 (6) #define RTL_CR_PS1 (7) // RTL8019 ISR register bits #define RTL_ISR_PRX (0) #define RTL_ISR_PTX (1) #define RTL_ISR_RXE (2) #define RTL_ISR_TXE (3) #define RTL_ISR_OVW (4) #define RTL_ISR_CNT (5) #define RTL_ISR_RDC (6) #define RTL_ISR_RST (7) // RTL8019 (initial) register values #define RTL_VAL_RCR (0x04) #define RTL_VAL_TCR (0x00) #define RTL_VAL_DCR (0x58) #define RTL_VAL_IMR (0x00) #define RTL_VAL_TXSTART (0x40) #define RTL_VAL_RXSTART (0x46) #define RTL_VAL_RXSTOP (0x60) // write a RTL8019 register extern inline void RtlWriteReg( unsigned char reg, unsigned char val ) // force inlining by using "extern" { BUS_DATA = val; // output value BUS_DATA_DDR = 0xFF; // data port to output BUS_ADDR = reg; // output address RTL_AEN_ACT( ); // set address enable BUS_WRITE_ACT( ); // activate write signal nop( ); nop( ); BUS_WRITE_IDLE( ); // take back write signal RTL_AEN_IDLE( ); // take back address enable BUS_DATA_DDR = 0x00; // data back port to input BUS_DATA = 0x00; // turn off pullups } // write buffer to RTL8019 register extern inline void RtlWriteRegBuf( unsigned char reg, unsigned char * p_buf, unsigned short len ) // force inlining by using "extern" { BUS_DATA = *p_buf; // output first value to initialize port status before switching to output BUS_DATA_DDR = 0xFF; // data port to output BUS_ADDR = reg; // output address RTL_AEN_ACT( ); // set address enable for( ; len > 0; p_buf++, len-- ) { BUS_DATA = *p_buf; // output value BUS_WRITE_ACT( ); // activate write signal nop( ); nop( ); BUS_WRITE_IDLE( ); // take back write signal } RTL_AEN_IDLE( ); // take back address enable BUS_DATA_DDR = 0x00; // data back port to input BUS_DATA = 0x00; // turn off pullups } // write constant to RTL8019 register multiple times extern inline void RtlWriteRegConst( unsigned char reg, unsigned char val, unsigned short cnt ) // force inlining by using "extern" { BUS_DATA = val; // output value BUS_DATA_DDR = 0xFF; // data port to output BUS_ADDR = reg; // output address RTL_AEN_ACT( ); // set address enable for( ; cnt > 0; cnt-- ) { BUS_WRITE_ACT( ); // activate write signal nop( ); nop( ); BUS_WRITE_IDLE( ); // take back write signal } RTL_AEN_IDLE( ); // take back address enable BUS_DATA_DDR = 0x00; // data back port to input BUS_DATA = 0x00; // turn off pullups } // read a RTL8019 register extern inline void RtlReadReg( unsigned char reg, unsigned char * p_var ) // force inlining by using "extern" { BUS_ADDR = reg; // output address RTL_AEN_ACT( ); // set address enable BUS_READ_ACT( ); // activate read signal nop( ); nop( ); *p_var = BUS_DATA_IN; // read data BUS_READ_IDLE( ); // take back read signal RTL_AEN_IDLE( ); // take back address enable } // read buffer from RTL8019 register extern inline void RtlReadRegBuf( unsigned char reg, unsigned char * p_buf, unsigned short len ) // force inlining by using "extern" { BUS_ADDR = reg; // output address RTL_AEN_ACT( ); // set address enable for( ; len > 0; p_buf++, len-- ) { BUS_READ_ACT( ); // activate read signal nop( ); nop( ); *p_buf = BUS_DATA_IN; // read data BUS_READ_IDLE( ); // take back read signal } RTL_AEN_IDLE( ); // take back address enable } // read a RTL8019 register multiple times and throw away data extern inline void RtlReadRegMulti( unsigned char reg, unsigned short cnt ) // force inlining by using "extern" { BUS_ADDR = reg; // output address RTL_AEN_ACT( ); // set address enable for( ; cnt > 0; cnt-- ) { BUS_READ_ACT( ); // activate read signal nop( ); nop( ); BUS_READ_IDLE( ); // take back read signal } RTL_AEN_IDLE( ); // take back address enable } // reinitialization timeout counter unsigned char RtlReinitTimeout = 0; // emulate eeprom containing RTL8019AS configuration static void RtlEmulateEeprom( void ) { static const unsigned char timeout = 150; unsigned int addr, data, entropy; unsigned char t, i; while( 1 ) { entropy = 0; // wait for CS HIGH for( t = 0; bit_is_clear( RTL_PIN_EECS, RTL_BIT_EECS ); t++ ) if( t >= timeout ) return; entropy += t; // ignore one clock // wait for clock HIGH for( t = 0; bit_is_clear( RTL_PIN_EESK, RTL_BIT_EESK ); t++ ) if( t >= timeout ) return; entropy += t; // wait for clock LOW for( t = 0; bit_is_set( RTL_PIN_EESK, RTL_BIT_EESK ); t++ ) if( t >= timeout ) return; entropy += t; // shift in address addr = 0; for( i = 0; i < 9; i++ ) { // wait for clock HIGH for( t = 0; bit_is_clear( RTL_PIN_EESK, RTL_BIT_EESK ); t++ ) if( t >= timeout ) return; entropy += t; // read next bit addr <<= 1; if( bit_is_set( RTL_PIN_EEDI, RTL_BIT_EEDI ) ) addr |= 0x01; // wait for clock LOW for( t = 0; bit_is_set( RTL_PIN_EESK, RTL_BIT_EESK ); t++ ) if( t >= timeout ) return; entropy += t; } // get data switch( addr ) { // CONFIG 3 = FUDUP,LEDS0; CONFIG 4 = - case 0x181: data = 0x0050; break; // output zero by default (as if no EEPROM is connected) default: data = 0x0000; break; } // shift out data for( i = 0; i < 16; i++ ) { // wait for clock HIGH for( t = 0; bit_is_clear( RTL_PIN_EESK, RTL_BIT_EESK ); t++ ) if( t >= timeout ) return; entropy += t; // write next bit if( data & 0x8000 ) bit_set( RTL_PORT_EEDO, RTL_BIT_EEDO ); else bit_clear( RTL_PORT_EEDO, RTL_BIT_EEDO ); data <<= 1; // wait for clock LOW for( t = 0; bit_is_set( RTL_PIN_EESK, RTL_BIT_EESK ); t++ ) if( t >= timeout ) return; entropy += t; } // wait for CS LOW for( t = 0; bit_is_set( RTL_PIN_EECS, RTL_BIT_EECS ); t++ ) if( t >= timeout ) return; entropy += t; // pass on entropy to randomness generator RandomProvideEntropy( (unsigned char)entropy ); } // while( 1 ) } // initialize void RtlInit( void ) // (extern) { unsigned char i; unsigned short j; // setup ports bit_clear( RTL_PORT_INT, RTL_BIT_INT ); // pull-up of interrupt pin off bit_clear( RTL_DDR_INT, RTL_BIT_INT ); // interrupt pin to input bit_set( RTL_PORT_nAEN, RTL_BIT_nAEN ); // address enable pin to HIGH bit_set( RTL_DDR_nAEN, RTL_BIT_nAEN ); // address enable pin to output bit_set( RTL_PORT_RST, RTL_BIT_RST ); // reset pin to HIGH bit_set( RTL_DDR_RST, RTL_BIT_RST ); // reset pin to output // configuration of EEPROM emulation bit_clear( RTL_PORT_EECS, RTL_BIT_EECS ); // EECS := input, no pull-up bit_clear( RTL_DDR_EECS, RTL_BIT_EECS ); bit_clear( RTL_PORT_EESK, RTL_BIT_EESK ); // EESK := input, no pull-up bit_clear( RTL_DDR_EESK, RTL_BIT_EESK ); bit_clear( RTL_PORT_EEDI, RTL_BIT_EEDI ); // EEDI := input, no pull-up bit_clear( RTL_DDR_EEDI, RTL_BIT_EEDI ); bit_clear( RTL_PORT_EEDO, RTL_BIT_EEDO ); // EEDO := output, LOW bit_set( RTL_DDR_EEDO, RTL_BIT_EEDO ); // take back reset for( i = 0; i < 10; i++ ) nop( ); RTL_RESET_IDLE( ); RtlEmulateEeprom( ); for( j = 0; j < 5000; j++ ) nop( ); // clear software reset RtlReadReg( RTL_REG_RSTPORT, &i ); RtlWriteReg( RTL_REG_RSTPORT, 0xFF ); for( i = 0; i < 100; i++ ) nop( ); // do the same as in reinitialization RtlReinit( ); } // re-initialize RT8019 (i.e. if MAC changed) void RtlReinit( void ) // (extern) { unsigned char i, j; // stop RTL8019 RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STP | 1<<RTL_CR_RD2 ); for( i = 0; i < 100; i++ ) nop( ); // output RTL8019 ID RtlReadReg( RTL_REG_ID0, &i ); RtlReadReg( RTL_REG_ID1, &j ); debug_rtl8019_printf( "RTL8019AS (re)init ID=0x%02X,0x%02X", i, j ); // set up RTL8019 RtlWriteReg( RTL_REG_DCR, RTL_VAL_DCR ); RtlWriteReg( RTL_REG_RBCR0, 0x00 ); RtlWriteReg( RTL_REG_RBCR1, 0x00 ); RtlWriteReg( RTL_REG_RCR, 0x04 ); RtlWriteReg( RTL_REG_TPSR, RTL_VAL_RXSTART ); RtlWriteReg( RTL_REG_TCR, 0x02 ); RtlWriteReg( RTL_REG_PSTART, RTL_VAL_RXSTART ); RtlWriteReg( RTL_REG_BNRY, RTL_VAL_RXSTART ); RtlWriteReg( RTL_REG_PSTOP, RTL_VAL_RXSTOP ); RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STP | 1<<RTL_CR_RD2 | 1<<RTL_CR_PS0 ); for( i = 0; i < 100; i++ ) nop( ); // write MAC to chip RtlWriteReg( RTL_REG_CURR, RTL_VAL_RXSTART ); for( i = 0; i < 6; i++ ) RtlWriteReg( RTL_REG_PAR0 + i, ConfigMac[i] ); // go on with intializing RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STP | 1<<RTL_CR_RD2 ); RtlWriteReg( RTL_REG_DCR, RTL_VAL_DCR ); RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD2 ); RtlWriteReg( RTL_REG_ISR, 1<<RTL_ISR_PRX | 1<<RTL_ISR_PTX | 1<<RTL_ISR_RXE | 1<<RTL_ISR_TXE | 1<<RTL_ISR_OVW | 1<<RTL_ISR_CNT | 1<<RTL_ISR_RDC | 1<<RTL_ISR_RST ); RtlWriteReg( RTL_REG_IMR, RTL_VAL_IMR ); RtlWriteReg( RTL_REG_TCR, RTL_VAL_TCR ); // start RTL8019 RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD2 ); // clear reinitialization timeout RtlReinitTimeout = 0; } // tick procedure - call every 200ms void RtlTick200( void ) // (extern) { // increment reinitialization timeout counter RtlReinitTimeout++; // reinitialization timeout if( RtlReinitTimeout >= RtlReinitTimeoutTicks ) // reinitialize RTL8019 (resets reinitialization timeout) RtlReinit( ); } // fetch and process a received packet static inline void RtlRecv( void ) { unsigned short PacketLen; unsigned char Packet[RTL_MRU]; // fetch packet from RTL8019 PacketLen = count( Packet ); RtlReadFrame( (unsigned char *)Packet, &PacketLen ); // pass packet on to ethernet EthernetRecv( Packet, PacketLen ); } // task function to do the work - call from main loop void RtlTask( void ) // (extern) { unsigned char isr, bnry, curr; // read interrupt register RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD2 ); RtlReadReg( RTL_REG_ISR, &isr ); // a packet was received if( isr & 1<<RTL_ISR_PRX ) // use current timestamp for generating some entropy TimingEntropy( ); // check if receive ring buffer is not empty RtlReadReg( RTL_REG_BNRY, &bnry ); RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD2 | 1<<RTL_CR_PS0 ); RtlReadReg( RTL_REG_CURR, &curr ); RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD2 ); if( bnry != curr ) // fetch and process a single received packet RtlRecv( ); // reset interrupt bits of RTL8019 RtlWriteReg( RTL_REG_ISR, 1<<RTL_ISR_PRX | 1<<RTL_ISR_PTX | 1<<RTL_ISR_RXE | 1<<RTL_ISR_TXE | 1<<RTL_ISR_OVW | 1<<RTL_ISR_CNT | 1<<RTL_ISR_RDC | 1<<RTL_ISR_RST ); // put RTL8019 in default state (default page selected, ...) RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD2 ); // clear reinitialization timeout RtlReinitTimeout = 0; } // write an ethernet frame to the RTL8019 void RtlWriteFrame( unsigned char * pData, unsigned short Length ) // (extern) { unsigned char val; debug_rtl8019_printf( "send len=%u", Length ); // initialize RTL8019 to transmit RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD2 ); RtlWriteReg( RTL_REG_TPSR, RTL_VAL_TXSTART ); RtlWriteReg( RTL_REG_RSAR0, 0x00 ); RtlWriteReg( RTL_REG_RSAR1, RTL_VAL_TXSTART ); RtlWriteReg( RTL_REG_ISR, 1<<RTL_ISR_PRX | 1<<RTL_ISR_PTX | 1<<RTL_ISR_RXE | 1<<RTL_ISR_TXE | 1<<RTL_ISR_OVW | 1<<RTL_ISR_CNT | 1<<RTL_ISR_RDC | 1<<RTL_ISR_RST ); if( Length < 0x3C ) // minimal length is 60 bytes { RtlWriteReg( RTL_REG_RBCR0, 0x3C ); RtlWriteReg( RTL_REG_RBCR1, 0x00 ); } else { RtlWriteReg( RTL_REG_RBCR0, (unsigned char)Length ); RtlWriteReg( RTL_REG_RBCR1, (unsigned char)(Length >> 8) ); } RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD1 ); // write data to RTL8019 RtlWriteRegBuf( RTL_REG_RDMAPORT, pData, Length ); if( Length < 0x3C ) // padding RtlWriteRegConst( RTL_REG_RDMAPORT, 0x00, 0x3C - Length ); // wait for RTL8019 do { RtlReadReg( RTL_REG_ISR, &val ); } while( (val & 1<<RTL_ISR_RDC) == 0x00 ); // start transmission if( Length < 0x3C ) // minimal length is 60 bytes { RtlWriteReg( RTL_REG_TBCR0, 0x3C ); RtlWriteReg( RTL_REG_TBCR1, 0x00 ); } else { RtlWriteReg( RTL_REG_TBCR0, (unsigned char)Length ); RtlWriteReg( RTL_REG_TBCR1, (unsigned char)(Length >> 8) ); } RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_TXP | 1<<RTL_CR_RD2 ); } // read an ethernet frame from the RTL8019 // *pLength must be initialized to the buffer size void RtlReadFrame( unsigned char * pData, unsigned short * pLength ) // (extern) { unsigned char tmp1, tmp2; unsigned short read_len, len, cnt; // get size of received packet RtlWriteReg( RTL_REG_CR, 1<<RTL_CR_STA | 1<<RTL_CR_RD0 | 1<<RTL_CR_RD1 ); RtlReadReg( RTL_REG_RDMAPORT, &tmp1 ); RtlReadReg( RTL_REG_RDMAPORT, &tmp1 ); RtlReadReg( RTL_REG_RDMAPORT, &tmp1 ); RtlReadReg( RTL_REG_RDMAPORT, &tmp2 ); read_len = ((unsigned short)tmp2 << 8 | (unsigned short)tmp1); // subtract length of CRC (4 bytes) len = read_len < 4 ? 0 : read_len - 4; debug_rtl8019_printf( "recv len=%u", len ); // read as much data as possible into buffer cnt = min( len, *pLength ); RtlReadRegBuf( RTL_REG_RDMAPORT, pData, cnt ); *pLength = cnt; // get rest of data (rest of real data that did not fit into buffer, CRC) if( read_len > cnt ) RtlReadRegMulti( RTL_REG_RDMAPORT, read_len - cnt ); }