BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
6b957dc
Branches
Tags
master
flaneth
firmware
rtl8019.c
increase MRU from 320 to 640
Stefan Schuermans
commited
6b957dc
at 2019-05-01 19:02:17
rtl8019.c
Blame
History
Raw
/* flaneth - flash and ethernet Copyright (C) 2007-2012 Stefan Schuermans <stefan@blinkenarea.org> 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); }