mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
The current code for entering commands via the serial CLI is completely refactored and moved out of rtlplayground.c into a separate file cmd_edit.c putting code into BANK2. The serial ring buffer sbuf[] is now exclusively used for receiving keys, including escape sequences (DEL will generate a 4-byte escape sequence). The command is now held in cmd_buffer. This reduces XMEM use, because the serial buffer can be much smaller. Supported is only editing the current command line via backspace, del, cursor left and right. Because the code cannot distinguish escape sequences which are not yet complete (because the serial isr has not yet put all characters into the serial buffer) from the dozens of unsupported ones (F-keys/home, Page up/down, cursor up/down...) they are ignored and remain in the serial buffer until the ring-pointer overwrites them. This is standard behaviour for consoles, which will print out garbage for unsupported characters. In this implementation, the command line buffer and the visible command line in the terminal are always synced, so garbage can be removed again by ediing the line. The code makes several redundant checks in order to prevent race-conditions between the serial ISR and the comand-editing code.
2115 lines
55 KiB
C
2115 lines
55 KiB
C
#include <8051.h>
|
||
#include <stdint.h>
|
||
|
||
// #define REGDBG 1
|
||
// #define RXTXDBG 1
|
||
|
||
#include "rtl837x_sfr.h"
|
||
#include "rtl837x_regs.h"
|
||
#include "rtl837x_common.h"
|
||
#include "rtl837x_flash.h"
|
||
#include "rtl837x_pins.h"
|
||
#include "rtl837x_phy.h"
|
||
#include "rtl837x_port.h"
|
||
#include "rtl837x_stp.h"
|
||
#include "rtl837x_igmp.h"
|
||
#include "rtl837x_leds.h"
|
||
#include "dhcp.h"
|
||
#include "cmd_parser.h"
|
||
#include "cmd_editor.h"
|
||
#include "uip/uipopt.h"
|
||
#include "uip/uip.h"
|
||
#include "uip/uip_arp.h"
|
||
#include "machine.h"
|
||
#include "phy.h"
|
||
|
||
extern __code const struct machine machine;
|
||
|
||
extern __xdata uint16_t crc_value;
|
||
__xdata uint8_t crc_testbytes[10];
|
||
__xdata struct machine_runtime machine_detected;
|
||
void crc16(__xdata uint8_t *v) __naked;
|
||
|
||
// See setup_serial_timer1() for valid baudrate settings!
|
||
#define SERIAL_BAUD_RATE 115200
|
||
|
||
/* All RTL839x switches have an external 25MHz Oscillator,
|
||
VALID RTL8372/3 CPU frequencies found in switches are:
|
||
0x07735940 = 125,000,000
|
||
0x03b9aca0 = 62,500,000
|
||
0x01dcd650 = 31,250,000
|
||
0x013d6200 = 20,800,000
|
||
For the following frequencies, divider settings are known
|
||
and can be selected on all known HW (Register 0x6040)
|
||
*/
|
||
#define CLOCK_HZ 125000000
|
||
//#define CLOCK_HZ 20800000
|
||
|
||
// Derive the divider settings for the internal clock
|
||
#if CLOCK_HZ == 20800000
|
||
#define CLOCK_DIV 3
|
||
#elif CLOCK_HZ == 31250000
|
||
#define CLOCK_DIV 2
|
||
#elif CLOCK_HZ == 62500000
|
||
#define CLOCK_DIV 1
|
||
#elif CLOCK_HZ == 125000000
|
||
#define CLOCK_DIV 0
|
||
#endif
|
||
|
||
/* Derive divider for the system ticks
|
||
TIMER2 can divide the F_CPU by 4 or 12.
|
||
So the F_TICKS are in the range of:
|
||
- F_TIMER_DIV4_OVERFLOW = F_SYS / DIV4 / 1..65536 = 125MHz / 4 / 1..65536 = 31.25 MHz .. 476.8 Hz
|
||
- T_TIMER_DIV12_OVERFLOW = F_SYS / DIV12 / 1..65536 = 125MHz / 12 / 1..65536 = 10.42 MHz .. 158.9 Hz
|
||
Selecting dividor 12 settings to get lowest timer tick posiable which is already high.
|
||
*/
|
||
#define SYS_TICK_HZ 200
|
||
|
||
#define TIMER2_DIV (CLOCK_HZ / 12 / SYS_TICK_HZ)
|
||
#if TIMER2_DIV > 0xFFFF
|
||
#error "SYS_TICK_HZ to low, must be >= 159"
|
||
#endif
|
||
#define SYSTICK_TIMER2_VALUE (0x10000 - TIMER2_DIV)
|
||
|
||
__xdata uint8_t idle_ready;
|
||
|
||
__code uint8_t ownIP[] = { 192, 168, 2, 2 };
|
||
__code uint8_t gatewayIP[] = { 192, 168, 2, 22};
|
||
__code uint8_t netmask[] = { 255, 255, 255, 0};
|
||
|
||
__xdata struct uip_eth_addr uip_ethaddr;
|
||
|
||
volatile __xdata uint32_t ticks;
|
||
volatile __xdata uint8_t sec_counter;
|
||
volatile __xdata uint16_t sleep_ticks;
|
||
__xdata uint8_t stp_clock;
|
||
extern __xdata struct dhcp_state dhcp_state;
|
||
|
||
#define STP_TICK_DIVIDER 3
|
||
|
||
/* Buffer for serial input, SBUF_SIZE must be power of 2 < 256
|
||
* Writing to this buffer is under the sole control of the serial ISR
|
||
* Note that key-presses such as <cursor-left> can create multiple
|
||
* keys being sent via the serial line */
|
||
__xdata volatile uint8_t sbuf_ptr;
|
||
__xdata uint8_t sbuf[SBUF_SIZE];
|
||
|
||
__xdata uint8_t sfr_data[4];
|
||
|
||
extern __xdata uint8_t gpio_last_value[8];
|
||
|
||
extern __xdata struct flash_region_t flash_region;
|
||
|
||
__code uint8_t * __code greeting = "\nA minimal prompt to explore the RTL8372:\n";
|
||
__code uint8_t * __code hex = "0123456789abcdef";
|
||
|
||
__xdata uint8_t flash_buf[FLASH_BUF_SIZE];
|
||
|
||
// NIC buffers for packet RX/TX
|
||
__xdata uint8_t rx_headers[16]; // Packet header(s) on RX
|
||
__xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
||
|
||
__xdata uint16_t rx_packet_vlan;
|
||
__xdata uint16_t management_vlan;
|
||
__xdata uint8_t tx_seq;
|
||
|
||
__xdata uint8_t stpEnabled;
|
||
|
||
__code uint16_t bit_mask[16] = {
|
||
0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
|
||
0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000
|
||
};
|
||
|
||
|
||
__xdata uint8_t linkbits_last[4];
|
||
__xdata uint8_t linkbits_last_p89;
|
||
__xdata uint8_t sfp_pins_last;
|
||
__xdata char sfp_module_vendor[2][17];
|
||
__xdata char sfp_module_model[2][17];
|
||
__xdata char sfp_module_serial[2][17];
|
||
__xdata uint8_t sfp_options[2];
|
||
__xdata bool button_last;
|
||
__xdata uint8_t button_sec_counter_last;
|
||
__sbit tx_buf_empty;
|
||
|
||
#define ETHERTYPE_OFFSET (12 + VLAN_TAG_SIZE + RTL_TAG_SIZE)
|
||
|
||
void isr_timer0(void) __interrupt(1)
|
||
{
|
||
}
|
||
|
||
|
||
// Timer2: Handle SYS_TICK
|
||
void isr_timer2(void) __interrupt(5)
|
||
{
|
||
ticks++;
|
||
if (sleep_ticks > 0)
|
||
sleep_ticks--;
|
||
sec_counter++;
|
||
|
||
// Clear TF2 & EXF2 by software
|
||
T2CON &= ~0xC0;
|
||
}
|
||
|
||
|
||
void isr_serial(void) __interrupt(4)
|
||
{
|
||
if (RI == 1) {
|
||
RI = 0;
|
||
sbuf[sbuf_ptr] = SBUF;
|
||
sbuf_ptr = (sbuf_ptr + 1) & (SBUF_SIZE - 1);
|
||
}
|
||
if (TI == 1) {
|
||
TI = 0;
|
||
tx_buf_empty = 1;
|
||
}
|
||
}
|
||
|
||
|
||
void write_char(char c)
|
||
{
|
||
do {
|
||
} while (tx_buf_empty == 0);
|
||
if (c =='\n') {
|
||
tx_buf_empty = 0;
|
||
SBUF = '\r';
|
||
do {
|
||
} while (tx_buf_empty == 0);
|
||
}
|
||
tx_buf_empty = 0;
|
||
SBUF = c;
|
||
}
|
||
|
||
|
||
void itoa(uint8_t v)
|
||
{
|
||
uint8_t t = (v / 100);
|
||
// when print_zeros is not zero, we know that a non-zero number has printed.
|
||
// That have to print all the next numbers.
|
||
uint8_t print_zeros = t;
|
||
if (print_zeros)
|
||
write_char('0' + t);
|
||
t = (v / 10) % 10;
|
||
print_zeros |= t;
|
||
if (print_zeros)
|
||
write_char('0' + t);
|
||
write_char('0' + (v % 10));
|
||
}
|
||
|
||
|
||
void print_string(__code char *p)
|
||
{
|
||
while (*p)
|
||
write_char(*p++);
|
||
}
|
||
|
||
void print_string_x(__xdata char *p)
|
||
{
|
||
while (*p)
|
||
write_char(*p++);
|
||
}
|
||
|
||
|
||
void memcpy(__xdata void * __xdata dst, __xdata const void * __xdata src, uint16_t len)
|
||
{
|
||
__xdata uint8_t *d = dst;
|
||
__xdata const uint8_t *s = src;
|
||
while (len--)
|
||
*d++ = *s++;
|
||
}
|
||
|
||
void memcpyc(register __xdata uint8_t *dst, register __code uint8_t *src, register uint16_t len)
|
||
{
|
||
while (len--)
|
||
*dst++ = *src++;
|
||
}
|
||
|
||
|
||
void memset(register __xdata uint8_t *dst, register __xdata uint8_t v, register uint8_t len)
|
||
{
|
||
while (len--)
|
||
*dst++ = v;
|
||
}
|
||
|
||
uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s)
|
||
{
|
||
__xdata uint8_t *b = dst;
|
||
while (*s)
|
||
*dst++ = *s++;
|
||
*dst = 0;
|
||
return dst - b;
|
||
}
|
||
|
||
uint16_t strlen(register __code const char *s)
|
||
{
|
||
uint16_t l = 0;
|
||
while (s[l])
|
||
l++;
|
||
return l;
|
||
}
|
||
|
||
|
||
uint16_t strlen_x(register __xdata const char *s)
|
||
{
|
||
uint16_t l = 0;
|
||
while (s[l])
|
||
l++;
|
||
return l;
|
||
}
|
||
|
||
|
||
void print_short(uint16_t a)
|
||
{
|
||
print_string("0x");
|
||
for (signed char i = 12; i >= 0; i -= 4) {
|
||
write_char(hex[(a >> i) & 0xf]);
|
||
}
|
||
}
|
||
|
||
|
||
void print_long(__xdata uint32_t a)
|
||
{
|
||
print_string("0x");
|
||
for (signed char i = 28; i >= 0; i -= 4) {
|
||
write_char(hex[(a >> i) & 0xf]);
|
||
}
|
||
}
|
||
|
||
void print_byte(uint8_t a)
|
||
{
|
||
write_char(hex[(a >> 4) & 0xf]);
|
||
write_char(hex[a & 0xf]);
|
||
}
|
||
|
||
|
||
/*
|
||
* External IRQ 0 Service Routine: Called on link change?
|
||
* Note that all registers are being put on the STACK because of calling a subroutine
|
||
*/
|
||
void isr_ext0(void) __interrupt(0)
|
||
{
|
||
EX0 = 0; // Disable interrupt for the moment
|
||
write_char('X');
|
||
IT0 = 1; // Trigger on falling edge of external interrupt
|
||
EX0 = 1; // Re-enable interrupt
|
||
}
|
||
|
||
|
||
/*
|
||
* External IRQ 1 Service Routine, triggered by the NIC recieving a packet
|
||
* Note that all registers are being put on the STACK because of calling
|
||
* a subroutine (write_char), we shold do better...
|
||
*/
|
||
void isr_ext1(void) __interrupt(2)
|
||
{
|
||
// This flag should only be reset after all packets have been read
|
||
EX1 = 0;
|
||
write_char('Y');
|
||
EX1 = 1;
|
||
}
|
||
|
||
/*
|
||
* External IRQ 2 Service Routine
|
||
* Note that all registers are being put on the STACK because of calling a subroutine
|
||
*/
|
||
void isr_ext2(void) __interrupt(8)
|
||
{
|
||
EXIF &= 0xef; // Clear IRQ flag (bit 7) in EXIF
|
||
write_char('Z');
|
||
PCON |= 1; // Enter Idle mode until interrupt occurs
|
||
}
|
||
|
||
/*
|
||
* External IRQ 3 Service Routine
|
||
* Note that all registers are being put on the STACK because of calling a subroutine
|
||
*/
|
||
void isr_ext3(void) __interrupt(9)
|
||
{
|
||
EXIF &= 0xdf; // Clear IRQ flag (bit 6) in EXIF
|
||
write_char('W');
|
||
}
|
||
|
||
// Timer2: handles system tick.
|
||
void setup_timer2(void)
|
||
{
|
||
T2CON = 0x00; // Timer2: Mode 16-bit timer with auto-reload, disable the timer.
|
||
|
||
// Timer 2 clock select F_SYS / 12;
|
||
// T2M = 0 uses clk/12;
|
||
CKCON &= ~0x20;
|
||
|
||
// The RCAP2 registers contain the high/low byte that is loaded into
|
||
// timer2 when T2 overflows to 0x10000
|
||
RCAP2_U16 = SYSTICK_TIMER2_VALUE;
|
||
|
||
T2CON |= 0x04; // Timer2: Enable
|
||
|
||
// IP |= 0x20; // TEST: Make Timer 2 interrupt as high priority.
|
||
ET2 = 1; // Enable Timer2 interrupt.
|
||
|
||
|
||
}
|
||
|
||
|
||
void reg_read(uint16_t reg_addr)
|
||
{
|
||
SFR_REG_ADDR_U16 = reg_addr;
|
||
SFR_EXEC_GO = SFR_EXEC_READ_REG;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
/* The result is now in SFR A4, A5, A6, A7 */
|
||
}
|
||
|
||
|
||
void reg_read_m(uint16_t reg_addr)
|
||
{
|
||
#ifdef REGDBG
|
||
if (EA) { write_char('r'); print_byte(reg_addr >> 8); print_byte(reg_addr); write_char(':'); }
|
||
#endif
|
||
SFR_REG_ADDR_U16 = reg_addr;
|
||
SFR_EXEC_GO = SFR_EXEC_READ_REG;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
sfr_data[0] = SFR_DATA_24;
|
||
sfr_data[1] = SFR_DATA_16;
|
||
sfr_data[2] = SFR_DATA_8;
|
||
sfr_data[3] = SFR_DATA_0;
|
||
#ifdef REGDBG
|
||
if (EA) { print_byte(sfr_data[0]); print_byte(sfr_data[1]); print_byte(sfr_data[2]); print_byte(sfr_data[3]); write_char(' '); }
|
||
#endif
|
||
}
|
||
|
||
|
||
void reg_write(uint16_t reg_addr)
|
||
{
|
||
/* Data to write must be in SFR A4, A5, A6, A7 */
|
||
SFR_REG_ADDR_U16 = reg_addr;
|
||
SFR_EXEC_GO = SFR_EXEC_WRITE_REG;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
}
|
||
|
||
|
||
void reg_write_m(uint16_t reg_addr)
|
||
{
|
||
#ifdef REGDBG
|
||
if (EA) {
|
||
write_char('R'); print_byte(reg_addr >> 8); print_byte(reg_addr); write_char('-');
|
||
print_byte(sfr_data[0]); print_byte(sfr_data[1]); print_byte(sfr_data[2]); print_byte(sfr_data[3]); write_char(' ');
|
||
}
|
||
#endif
|
||
SFR_REG_ADDR_U16 = reg_addr;
|
||
SFR_DATA_24 = sfr_data[0] ;
|
||
SFR_DATA_16 = sfr_data[1];
|
||
SFR_DATA_8 = sfr_data[2];
|
||
SFR_DATA_0 = sfr_data[3];
|
||
|
||
SFR_EXEC_GO = SFR_EXEC_WRITE_REG;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
}
|
||
|
||
|
||
/*
|
||
* This sets a bit in the 32bit wide switch register reg_addr
|
||
*/
|
||
void reg_bit_set(uint16_t reg_addr, char bit)
|
||
{
|
||
uint8_t bit_mask = 1 << (bit & 0x7);
|
||
|
||
bit >>= 3;
|
||
reg_read_m(reg_addr);
|
||
sfr_data[3-bit] |= bit_mask;
|
||
reg_write_m(reg_addr);
|
||
}
|
||
|
||
|
||
/*
|
||
* This sets a bit in the 32bit wide switch register reg_addr
|
||
*/
|
||
void reg_bit_clear(uint16_t reg_addr, char bit)
|
||
{
|
||
uint8_t bit_mask = 1 << (bit & 0x7);
|
||
|
||
bit >>= 3;
|
||
reg_read_m(reg_addr);
|
||
bit_mask = ~bit_mask;
|
||
sfr_data[3-bit] &= bit_mask;
|
||
reg_write_m(reg_addr);
|
||
}
|
||
|
||
/*
|
||
* This masks the sfr data fields, first &-ing with ~mask, then setting the bits in set
|
||
*/
|
||
void sfr_mask_data(uint8_t n, uint8_t mask, uint8_t set)
|
||
{
|
||
uint8_t b = sfr_data[3-n];
|
||
b &= ~mask;
|
||
b |= set;
|
||
sfr_data[3-n] = b;
|
||
}
|
||
|
||
/*
|
||
* This zeros all the sfr data fields
|
||
*/
|
||
void sfr_set_zero(void) {
|
||
uint8_t idx = 4;
|
||
while (idx) {
|
||
idx -= 1;
|
||
sfr_data[idx] = 0;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Create 32 random number in sfr_data
|
||
*/
|
||
void get_random_32(void)
|
||
{
|
||
// In order to get a new random numner, this bit has to be set each time!
|
||
reg_bit_set(RTL837X_RLDP_RLPP, RLDP_RND_EN);
|
||
reg_read_m(RTL837X_RAND_NUM0);
|
||
}
|
||
|
||
|
||
/*
|
||
* Transfer Network Interface RX data from the ASIC to the 8051 XMEM
|
||
* data will be stored in the rx_header structure
|
||
* len is the length of data to be transferred
|
||
*/
|
||
void nic_rx_header(uint16_t ring_ptr)
|
||
{
|
||
uint16_t buffer = (uint16_t) &rx_headers[0];
|
||
SFR_NIC_DATA_U16LE = buffer;
|
||
SFR_NIC_RING_U16LE = ring_ptr;
|
||
SFR_NIC_CTRL = 1;
|
||
do { } while (SFR_NIC_CTRL != 0);
|
||
}
|
||
|
||
|
||
/*
|
||
* Transfer Network Interface RX data from the ASIC to the 8051 XMEM
|
||
* the description of the packet must be in the rx_headers data structure
|
||
* data will be returned in the xmem buffer points to
|
||
* ring_ptr is the current position of the RX Ring on the ASIC side
|
||
*/
|
||
void nic_rx_packet(register uint16_t buffer, register uint16_t ring_ptr)
|
||
{
|
||
SFR_NIC_DATA_U16LE = buffer;
|
||
SFR_NIC_RING_U16LE = ring_ptr;
|
||
|
||
uint16_t len = (((uint16_t)rx_headers[5]) << 8) | rx_headers[4];
|
||
len += 7;
|
||
len >>= 3;
|
||
#ifdef RXTXDBG
|
||
print_string(" len: ");
|
||
print_short(len);
|
||
#endif
|
||
SFR_NIC_CTRL = len;
|
||
do { } while (SFR_NIC_CTRL != 0);
|
||
}
|
||
|
||
|
||
/*
|
||
* Transfers data in XMEM to the ASIC for transmission by the nic
|
||
*/
|
||
void nic_tx_packet(uint16_t ring_ptr)
|
||
{
|
||
// uint16_t buffer = (uint16_t) tx_buf;
|
||
uint16_t buffer = (uint16_t) uip_buf + VLAN_TAG_SIZE;
|
||
SFR_NIC_DATA_U16LE = buffer;
|
||
|
||
ring_ptr <<= 3;
|
||
ring_ptr |= 0x8000;
|
||
SFR_NIC_RING_U16LE = ring_ptr;
|
||
|
||
uint16_t len = (((uint16_t)uip_buf[VLAN_TAG_SIZE + 5]) << 8) | uip_buf[VLAN_TAG_SIZE + 4];
|
||
len += 0xf;
|
||
len >>= 3;
|
||
SFR_NIC_CTRL = len;
|
||
do { } while (SFR_NIC_CTRL != 0);
|
||
}
|
||
|
||
|
||
/* Read flash using the MMIO capabilities of the DW8051 core
|
||
* Bank is < 0x3f and is the MSB
|
||
* addr gives the address in the bank
|
||
* Note that the address in the flash memory is not simply 0xbbaddr, because
|
||
* the size of a bank is merely 0xc000.
|
||
*/
|
||
uint8_t read_flash(uint8_t bank, __code uint8_t *addr)
|
||
{
|
||
uint8_t v;
|
||
uint8_t current_bank = PSBANK;
|
||
|
||
PSBANK = bank;
|
||
v = *addr;
|
||
PSBANK = current_bank;
|
||
return v;
|
||
}
|
||
|
||
|
||
void print_long_x(__xdata uint8_t v[])
|
||
{
|
||
write_char('0'); write_char('x');
|
||
for (uint8_t i=0; i < 4; i++) {
|
||
write_char(hex[v[i] >> 4]);
|
||
write_char(hex[v[i] & 0xf]);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Read a SerDes register in the SoC
|
||
* Input must be: sds_id = 0/1, page < 128, reg <= 0xff
|
||
* The result is in SFR A6 and A7 (SFR_DATA_8, SFR_DATA_0)
|
||
*/
|
||
void sds_read(uint8_t sds_id, uint8_t page, uint8_t reg)
|
||
{
|
||
#ifdef REGDBG
|
||
print_string("q"); print_byte(sds_id); print_byte(page); print_byte(reg);
|
||
#endif
|
||
SFR_93 = reg; // 93
|
||
SFR_94 = page << 1 | sds_id; // 94
|
||
SFR_EXEC_GO = SFR_EXEC_READ_SDS;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
|
||
#ifdef REGDBG
|
||
write_char(':'); print_byte(SFR_DATA_8); print_byte(SFR_DATA_0); write_char(' ');
|
||
#endif
|
||
}
|
||
|
||
|
||
/*
|
||
* Write a SerDes register in the SoC
|
||
* Input must be: sds_id = 0/1, page < 128, reg <= 0xff
|
||
* The value written must be in SFR A6 and A7 (SFR_DATA_8, SFR_DATA_0)
|
||
*/
|
||
void sds_write_v(uint8_t sds_id, uint8_t page, uint8_t reg, uint16_t v)
|
||
{
|
||
#ifdef REGDBG
|
||
print_string("Q"); print_byte(sds_id); print_byte(page); print_byte(reg);
|
||
write_char(':'); print_byte(v >> 8); print_byte(v); write_char(' ');
|
||
#endif
|
||
SFR_DATA_U16 = v;
|
||
SFR_93 = reg;
|
||
SFR_94 = page << 1 | sds_id;
|
||
SFR_EXEC_GO = SFR_EXEC_WRITE_SDS;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
}
|
||
|
||
|
||
void print_sfr_data(void)
|
||
{
|
||
write_char('0');
|
||
write_char('x');
|
||
write_char(hex[sfr_data[0] >> 4]);
|
||
write_char(hex[sfr_data[0] & 0xf]);
|
||
write_char(hex[sfr_data[1] >> 4]);
|
||
write_char(hex[sfr_data[1] & 0xf]);
|
||
write_char(hex[sfr_data[2] >> 4]);
|
||
write_char(hex[sfr_data[2] & 0xf]);
|
||
write_char(hex[sfr_data[3] >> 4]);
|
||
write_char(hex[sfr_data[3] & 0xf]);
|
||
}
|
||
|
||
|
||
void print_phy_data(void)
|
||
{
|
||
write_char('0');
|
||
write_char('x');
|
||
write_char(hex[SFR_DATA_8 >> 4]);
|
||
write_char(hex[SFR_DATA_8 & 0xf]);
|
||
write_char(hex[SFR_DATA_0 >> 4]);
|
||
write_char(hex[SFR_DATA_0 & 0xf]);
|
||
}
|
||
|
||
|
||
void print_reg(uint16_t reg)
|
||
{
|
||
reg_read_m(reg);
|
||
print_sfr_data();
|
||
}
|
||
|
||
|
||
/*
|
||
// TODO: This uses 2 DSEG bytes and is not used!
|
||
void print_sds_reg(uint8_t sds_id, uint8_t page, uint8_t reg)
|
||
{
|
||
sds_read(sds_id, page, reg);
|
||
print_phy_data();
|
||
}
|
||
*/
|
||
|
||
char cmp_4(__xdata uint8_t a[], __xdata uint8_t b[])
|
||
{
|
||
for (uint8_t i = 0; i < 4; i++) {
|
||
if (a[i] == b[i])
|
||
continue;
|
||
if (a[i] < b[i])
|
||
return -1;
|
||
else
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void cpy_4(__xdata uint8_t dest[], __xdata uint8_t source[])
|
||
{
|
||
for (uint8_t i = 0; i < 4; i++)
|
||
dest[i] = source[i];
|
||
}
|
||
|
||
|
||
void read_reg_timer(uint32_t * tmr)
|
||
{
|
||
uint8_t * val = (uint8_t *)tmr;
|
||
SFR_REG_ADDR_U16 = RTL837X_REG_SEC_COUNTER;
|
||
SFR_EXEC_GO = SFR_EXEC_READ_REG;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
*val++ = SFR_DATA_0;
|
||
*val++ = SFR_DATA_8;
|
||
*val++ = SFR_DATA_16;
|
||
*val = SFR_DATA_24;
|
||
}
|
||
|
||
|
||
void sds_config_mac(uint8_t sds, uint8_t mode)
|
||
{
|
||
reg_read_m(RTL837X_REG_SDS_MODES);
|
||
sfr_data[0] = 0;
|
||
sfr_data[1] = 0;
|
||
switch (sds) {
|
||
case 0:
|
||
sfr_mask_data(0, 0x1f, mode);
|
||
break;
|
||
case 1:
|
||
sfr_mask_data(0, 0xe0, mode << 5);
|
||
sfr_mask_data(1, 0x03, mode >> 3);
|
||
break;
|
||
case 2:
|
||
sfr_mask_data(1, 0xfc, 0x02 << 2);
|
||
}
|
||
if (machine_detected.isRTL8373) // Set 3rd SERDES Mode to 0x2 for RTL8224
|
||
sfr_mask_data(1, 0xfc, 0x02 << 2);
|
||
else
|
||
sfr_data[2] &= 0x03;
|
||
reg_write_m(RTL837X_REG_SDS_MODES);
|
||
print_string("\nRTL837X_REG_SDS_MODES: ");
|
||
print_reg(RTL837X_REG_SDS_MODES);
|
||
print_string("\n");
|
||
}
|
||
|
||
|
||
// Delay for given number of ticks without doing housekeeping
|
||
void delay(uint16_t t)
|
||
{
|
||
sleep_ticks = t;
|
||
while (sleep_ticks > 0)
|
||
PCON |= 1;
|
||
}
|
||
|
||
|
||
/*
|
||
* Configure the SerDes of the SoC for a particular mode
|
||
* to connect to an SFP module or a PHY
|
||
* Valid modes are SDS_10GR, SDS_QXGMII, SDS_HISGMII, SDS_HSG, SDS_SGMII and SDS_1000BX_FIBER
|
||
* The SerDes ID may be 0 or 1 for RTL8272 and 0-2 for RTL8373
|
||
*/
|
||
void sds_config(uint8_t sds, uint8_t mode)
|
||
{
|
||
print_string("sds_config sds: "); print_byte(sds); print_string(", mode: "); print_byte(mode); write_char('\n');
|
||
sds_config_mac(sds, mode);
|
||
|
||
if (mode == SDS_10GR || mode == SDS_QXGMII) // 10G Fiber, 10G connection to RTL8224
|
||
sds_write_v(sds, 0x21, 0x10, 0x4480); // Q002110:6480
|
||
else
|
||
sds_write_v(sds, 0x21, 0x10, 0x6480); // Q002110:6480
|
||
sds_write_v(sds, 0x21, 0x13, 0x0400); // Q002113:0400
|
||
sds_write_v(sds, 0x21, 0x18, 0x6d02); // Q002118:6d02
|
||
sds_write_v(sds, 0x21, 0x1b, 0x424e); // Q00211b:424e
|
||
sds_write_v(sds, 0x21, 0x1d, 0x0002); // Q00211d:0002
|
||
sds_write_v(sds, 0x36, 0x1c, 0x1390); // Q00361c:1390
|
||
sds_write_v(sds, 0x36, 0x14, 0x003f); // Q003614:003f
|
||
|
||
uint8_t page = 0;
|
||
uint16_t v = 0;
|
||
|
||
switch (mode) {
|
||
case SDS_SGMII:
|
||
case SDS_1000BX_FIBER:
|
||
v = 0x0300;
|
||
page = 0x24;
|
||
break;
|
||
case SDS_HISGMII:
|
||
case SDS_HSG:
|
||
v = 0x0200;
|
||
page = 0x28;
|
||
break;
|
||
case SDS_10GR:
|
||
case SDS_QXGMII:
|
||
v = 0x0200;
|
||
page = 0x2e;
|
||
break;
|
||
default:
|
||
print_string("Error in SDS Mode\n");
|
||
return;
|
||
}
|
||
sds_write_v(sds, 0x36, 0x10, v); // Q003610:0200
|
||
|
||
if (page == 0x2e) { // 10G Fiber
|
||
sds_write_v(sds, page, 0x04, 0x0080); // Q012e04:0080
|
||
sds_write_v(sds, page, 0x06, 0x0408); // Q012e06:0408
|
||
sds_write_v(sds, page, 0x07, 0x020d); // Q012e07:020d
|
||
sds_write_v(sds, page, 0x09, 0x0601); // Q012e09:0601
|
||
sds_write_v(sds, page, 0x0b, 0x222c); // Q012e0b:222c
|
||
sds_write_v(sds, page, 0x0c, 0xa217); // Q012e0c:a217
|
||
sds_write_v(sds, page, 0x0d, 0xfe40); // Q012e0d:fe40
|
||
sds_write_v(sds, page, 0x15, 0xf5c1); // Q012e15:f5c1
|
||
} else {
|
||
sds_write_v(sds, page, 0x04, 0x0080); // Q002804:0080
|
||
sds_write_v(sds, page, 0x07, 0x1201); // Q002807:1201
|
||
sds_write_v(sds, page, 0x09, 0x0601); // Q002809:0601
|
||
sds_write_v(sds, page, 0x0b, 0x232c); // Q00280b:232c
|
||
sds_write_v(sds, page, 0x0c, 0x9217); // Q00280c:9217
|
||
sds_write_v(sds, page, 0x0f, 0x5b50); // Q00280f:5b50
|
||
sds_write_v(sds, page, 0x15, 0xe7c1); // Q002815:e7f1 BUG !
|
||
}
|
||
|
||
sds_write_v(sds, page, 0x16, 0x0443); // Q002816:0443 / Q012e16:0443
|
||
sds_write_v(sds, page, 0x1d, 0xabb0); // Q00281d:abb0 / Q012e1d:abb0
|
||
|
||
sds_write_v(sds, 0x06, 0x12, 0x5078); // Q000612:5078
|
||
sds_write_v(sds, 0x07, 0x06, 0x9401); // Q000706:9401
|
||
sds_write_v(sds, 0x07, 0x08, 0x9401); // Q000708:9401
|
||
sds_write_v(sds, 0x07, 0x0a, 0x9401); // Q00070a:9401
|
||
sds_write_v(sds, 0x07, 0x0c, 0x9401); // Q00070c:9401
|
||
sds_write_v(sds, 0x1f, 0x0b, 0x0003); // Q001f0b:0003
|
||
sds_write_v(sds, 0x06, 0x03, 0xc45c); // Q000603:c45c
|
||
if (mode != SDS_QXGMII)
|
||
sds_write_v(sds, 0x06, 0x1f, 0x2100); // Q00061f:2100
|
||
|
||
if (sds == 0 && mode == SDS_1000BX_FIBER) {
|
||
sds_write_v(sds, 0x02, 0x04, 0x0020); // Q000204:0020
|
||
sds_write_v(sds, 0x00, 0x02, 0x73d0); // Q000002:73d0
|
||
sds_write_v(sds, 0x00, 0x04, 0x074d); // Q000004:074d
|
||
sds_write_v(sds, 0x20, 0x04, 0x0000); // Q002000:0000
|
||
sds_write_v(sds, 0x1f, 0x00, 0x0000); // Q001f00:0000
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Read a register of the EEPROM via I2C
|
||
*/
|
||
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg)
|
||
{
|
||
if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address
|
||
reg &= 0x7f;
|
||
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 1, 0x51 >> 5, (0x51 << 3) & 0xff);
|
||
} else {
|
||
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 1, 0x50 >> 5, (0x50 << 3) & 0xff);
|
||
}
|
||
|
||
reg_read_m(RTL837X_REG_I2C_CTRL);
|
||
sfr_mask_data(1, 0xfc, i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2);
|
||
reg_write_m(RTL837X_REG_I2C_CTRL);
|
||
|
||
REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg);
|
||
|
||
// Execute I2C Read
|
||
reg_bit_set(RTL837X_REG_I2C_CTRL, 0);
|
||
|
||
// Wait for execution to finish
|
||
do {
|
||
reg_read_m(RTL837X_REG_I2C_CTRL);
|
||
} while (sfr_data[3] & 0x1);
|
||
|
||
reg_read_m(RTL837X_REG_I2C_OUT);
|
||
return sfr_data[3];
|
||
}
|
||
|
||
|
||
/*
|
||
* Adds TX Header to uip_buf and calls nic_tx_packet to send the packet
|
||
* over the wire
|
||
*/
|
||
void tcpip_output(void)
|
||
{
|
||
// Add TX-TAG
|
||
uip_buf[VLAN_TAG_SIZE] = tx_seq++;
|
||
uip_buf[VLAN_TAG_SIZE + 1] = 0x07; // Enable all checksums
|
||
uip_buf[VLAN_TAG_SIZE + 5] = uip_len >> 8;
|
||
uip_buf[VLAN_TAG_SIZE + 4] = uip_len;
|
||
uip_buf[VLAN_TAG_SIZE + 2] = uip_buf[VLAN_TAG_SIZE + 3] = 0;
|
||
uip_buf[VLAN_TAG_SIZE + 6] = uip_buf[VLAN_TAG_SIZE + 7] = 0;
|
||
|
||
reg_read_m(RTL837X_REG_CPU_TX_CURR_PKT);
|
||
uint16_t ring_ptr = ((uint16_t)sfr_data[2]) << 8;
|
||
ring_ptr |= sfr_data[3];
|
||
|
||
#ifdef RXTXDBG
|
||
print_string("TX: \n");
|
||
for (uint8_t i = 0; i < 120; i++) {
|
||
print_byte(uip_buf[i]);
|
||
write_char(' ');
|
||
}
|
||
write_char('\n');
|
||
#endif
|
||
|
||
// Move data over from xmem buffer to ASIC side using DMA
|
||
nic_tx_packet(ring_ptr);
|
||
|
||
// New position of the ring-pointer on the NIC-side indicates number of bytes transmitted
|
||
reg_read_m(RTL837X_REG_NIC_TX_CURR_PKT);
|
||
|
||
// Do actual TX of data on ASIC side
|
||
REG_SET(RTL837X_REG_NIC_TXCMD, 1);
|
||
}
|
||
|
||
|
||
void handle_rx(void)
|
||
{
|
||
// Check the amount of data available on the NIC/ASIC side
|
||
reg_read_m(RTL837X_REG_NIC_RX_BUFF_DATA);
|
||
if (sfr_data[2] != 0 || sfr_data[3] != 0) {
|
||
reg_read_m(RTL837X_REG_CPU_RX_CURR_PKT);
|
||
uint16_t ring_ptr = ((uint16_t)sfr_data[2]) << 8;
|
||
ring_ptr |= sfr_data[3];
|
||
ring_ptr <<= 3;
|
||
nic_rx_header(ring_ptr);
|
||
#ifdef RXTXDBG
|
||
__xdata uint8_t *ptr = rx_headers;
|
||
print_string("RX on port "); print_byte(rx_headers[3] & 0xf);
|
||
print_string(": ");
|
||
for (uint8_t i = 0; i < 8; i++) {
|
||
print_byte(*ptr++);
|
||
write_char(' ');
|
||
}
|
||
#endif
|
||
nic_rx_packet((uint16_t) &uip_buf[0], ring_ptr + 8);
|
||
|
||
#ifdef RXTXDBG
|
||
print_string("\n<< ");
|
||
ptr = &uip_buf[0];
|
||
for (uint8_t i = 0; i < 80; i++) {
|
||
print_byte(*ptr++);
|
||
write_char(' ');
|
||
}
|
||
#endif
|
||
REG_SET(RTL837X_REG_NIC_RXCMD, 1);
|
||
uip_len = (((uint16_t)rx_headers[5]) << 8) | rx_headers[4];
|
||
|
||
// Retrieve VLAN from VLAN-tag
|
||
rx_packet_vlan = uip_buf[2 * sizeof (struct uip_eth_addr) + RTL_TAG_SIZE + 2] & 0xf;
|
||
rx_packet_vlan <<= 8;
|
||
rx_packet_vlan |= uip_buf[2 * sizeof (struct uip_eth_addr) + RTL_TAG_SIZE + 3];
|
||
#ifdef RXTXDBG
|
||
print_string(" RX-VLAN: "); print_short(rx_packet_vlan); write_char('\n');
|
||
print_string(" RX dst: "); print_byte(uip_buf[0]); print_byte(uip_buf[1]); print_byte(uip_buf[2]);
|
||
print_byte(uip_buf[3]); print_byte(uip_buf[4]); print_byte(uip_buf[5]); write_char('\n');
|
||
#endif
|
||
if (stpEnabled && uip_buf[0] == 0x01 && uip_buf[1] == 0x80 && uip_buf[2] == 0xc2 // STP packet?
|
||
&& uip_buf[3] == 0x00 && uip_buf[4] == 0x00 && uip_buf[5] == 0x00) {
|
||
stp_in();
|
||
if (uip_len) {
|
||
print_string("STP TX\n");
|
||
tcpip_output();
|
||
}
|
||
} else if (uip_buf[0] == 0x01 && uip_buf[1] == 0x00 && uip_buf[2] == 0x5e // IPv4-MC packet?
|
||
&& uip_buf[3] == 0x00 && uip_buf[4] == 0x00 && uip_buf[5] == 0x16) {
|
||
igmp_packet_handler();
|
||
if (uip_len) {
|
||
tcpip_output();
|
||
}
|
||
} else if (uip_buf[ETHERTYPE_OFFSET] == 0x08 && uip_buf[ETHERTYPE_OFFSET + 1] == 0x06) { // ARP?
|
||
uip_arp_arpin();
|
||
if (uip_len) {
|
||
tcpip_output();
|
||
}
|
||
} else if (uip_buf[ETHERTYPE_OFFSET] == 0x08 && uip_buf[ETHERTYPE_OFFSET + 1] == 0x00) { // TCP?
|
||
if (!management_vlan || management_vlan == rx_packet_vlan) {
|
||
uip_arp_ipin(); // Learn MAC addresses in TCP packets
|
||
uip_input();
|
||
if (uip_len) {
|
||
// Add ethernet frame
|
||
uip_arp_out();
|
||
tcpip_output();
|
||
}
|
||
}
|
||
} else {
|
||
#ifdef RXTXDBG
|
||
print_string("Unknown RX on port "); print_byte(rx_headers[3] & 0xf); write_char('\n');
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void handle_tx(void)
|
||
{
|
||
for(uint8_t i = 0; i < UIP_CONNS; i++) {
|
||
uip_periodic(i);
|
||
if(uip_len > 0) {
|
||
#ifdef RXTXDBG
|
||
write_char('.'); print_short(i);
|
||
#endif
|
||
uip_arp_out();
|
||
tcpip_output();
|
||
}
|
||
}
|
||
for(uint8_t i = 0; i < UIP_UDP_CONNS; i++) {
|
||
uip_udp_periodic(i);
|
||
if(uip_len > 0) {
|
||
uip_arp_out();
|
||
tcpip_output();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static inline uint8_t sfp_rate_to_sds_config(register uint8_t rate)
|
||
{
|
||
if (rate == 0xd)
|
||
return SDS_1000BX_FIBER;
|
||
if (rate == 0x1f) // Ethernet 2.5 GBit
|
||
return SDS_HSG;
|
||
if (rate > 0x65 && rate < 0x70)
|
||
return SDS_10GR;
|
||
return 0xff;
|
||
}
|
||
|
||
|
||
void sfp_print_info(uint8_t sfp)
|
||
{
|
||
// This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields
|
||
for (uint8_t i = 20; i < 60; i++) {
|
||
if (i >= 36 && i < 40) // Skip Non-ASCII codes
|
||
continue;
|
||
uint8_t c = sfp_read_reg(sfp, i);
|
||
if (c)
|
||
write_char(c);
|
||
}
|
||
print_string("\n");
|
||
}
|
||
|
||
|
||
void sfp_get_info(uint8_t sfp)
|
||
{
|
||
for (uint8_t i = 20; i < 36; i++)
|
||
sfp_module_vendor[sfp][i-20] = sfp_read_reg(sfp, i);
|
||
sfp_module_vendor[sfp][16] = '\0';
|
||
for (uint8_t i = 40; i < 56; i++)
|
||
sfp_module_model[sfp][i-40] = sfp_read_reg(sfp, i);
|
||
sfp_module_model[sfp][16] = '\0';
|
||
for (uint8_t i = 68; i < 84; i++)
|
||
sfp_module_serial[sfp][i-68] = sfp_read_reg(sfp, i);
|
||
sfp_module_serial[sfp][16] = '\0';
|
||
}
|
||
|
||
|
||
bool gpio_pin_test(uint8_t pin)
|
||
{
|
||
reg_read_m(RTL837X_REG_GPIO_00_31_INPUT + (pin > 31 ? 4 : 0));
|
||
return sfr_data[3-((pin >> 3) & 3)] & (1 << (pin & 7));
|
||
}
|
||
|
||
/* Inititalize SFP GPIOs */
|
||
void setup_sfp_gpio(void)
|
||
{
|
||
for (uint8_t sfp = 0; sfp < machine.n_sfp; sfp++) {
|
||
gpio_input_setup(machine.sfp_port[sfp].pin_detect);
|
||
gpio_input_setup(machine.sfp_port[sfp].pin_los);
|
||
gpio_output_setup(machine.sfp_port[sfp].pin_tx_disable, 0);
|
||
}
|
||
}
|
||
|
||
void handle_sfp(void)
|
||
{
|
||
for (uint8_t sfp = 0; sfp < machine.n_sfp; sfp++) {
|
||
if (!gpio_pin_test(machine.sfp_port[sfp].pin_detect)) {
|
||
if (sfp_pins_last & (0x1 << (sfp << 2))) {
|
||
sfp_pins_last &= ~(0x01 << (sfp << 2));
|
||
print_string("\n<MODULE INSERTED> Slot: "); write_char('1' + sfp);
|
||
// Read Reg 11: Encoding, see SFF-8472 and SFF-8024
|
||
// Read Reg 12: Signalling rate (including overhead) in 100Mbit: 0xd: 1Gbit, 0x67:10Gbit
|
||
delay(100); // Delay, because some modules need time to wake up
|
||
uint8_t rate = sfp_read_reg(sfp, 12);
|
||
print_string(" Rate: "); print_byte(rate); // Normally 1, but 0 for DAC, can be ignored?
|
||
print_string(" Encoding: "); print_byte(sfp_read_reg(sfp, 11));
|
||
print_string(" Module: "); sfp_print_info(sfp);
|
||
print_string("\n");
|
||
sfp_options[sfp] = sfp_read_reg(sfp, 92);
|
||
sfp_get_info(sfp);
|
||
sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate));
|
||
}
|
||
} else {
|
||
if (!(sfp_pins_last & (0x1 << (sfp << 2)))) {
|
||
sfp_pins_last |= 0x01 << (sfp << 2);
|
||
print_string("\n<MODULE REMOVED> Slot: "); write_char('1' + sfp); write_char('\n');
|
||
}
|
||
}
|
||
|
||
if (!gpio_pin_test(machine.sfp_port[sfp].pin_los)) {
|
||
if (sfp_pins_last & (0x2 << (sfp << 2))) { // 0x2 0x08
|
||
sfp_pins_last &= ~(0x02 << (sfp << 2));
|
||
print_string("\n<SFP-RX OK> Slot: "); write_char('1' + sfp); write_char('\n');
|
||
}
|
||
} else {
|
||
if (!(sfp_pins_last & 0x2 << (sfp << 2))) {
|
||
sfp_pins_last |= 0x02 << (sfp << 2);
|
||
print_string("\n<SFP-RX LOS> Slot: "); write_char('1' + sfp); write_char('\n');
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void flash_default_config(void)
|
||
{
|
||
__xdata uint32_t source = DEFAULT_CONFIG_START;
|
||
__xdata uint32_t dest = CONFIG_START;
|
||
|
||
flash_region.addr = CONFIG_START;
|
||
flash_sector_erase();
|
||
|
||
for (uint8_t i = 0; i < 8; i++) // 8 * 512 Byte = 4 kByte (1 sector)
|
||
{
|
||
flash_region.addr = source;
|
||
flash_region.len = FLASH_BUF_SIZE;
|
||
flash_read_bulk(flash_buf);
|
||
flash_region.addr = dest;
|
||
flash_region.len = FLASH_BUF_SIZE;
|
||
flash_write_bytes(flash_buf);
|
||
dest += FLASH_BUF_SIZE;
|
||
source += FLASH_BUF_SIZE;
|
||
}
|
||
|
||
print_string("Written default config to flash\n");
|
||
}
|
||
|
||
void handle_button(void)
|
||
{
|
||
if (machine.reset_pin == GPIO_NA) {
|
||
return;
|
||
}
|
||
|
||
bool button_pressed = !gpio_pin_test(machine.reset_pin);
|
||
if (button_last != button_pressed)
|
||
{
|
||
print_string(button_pressed ? "Button pressed\n" : "Button released\n");
|
||
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
||
uint8_t diff_sec_counter = sfr_data[3] - button_sec_counter_last;
|
||
button_last = button_pressed;
|
||
button_sec_counter_last = sfr_data[3];
|
||
|
||
if (!button_pressed)
|
||
{
|
||
if (diff_sec_counter > 10)
|
||
{
|
||
print_string(">10s button detected; reverting to default settings:\n");
|
||
flash_default_config();
|
||
print_string("Now resetting...\n");
|
||
reset_chip();
|
||
}
|
||
else if (diff_sec_counter > 3)
|
||
{
|
||
print_string(">3s button detected; resetting chip...\n");
|
||
reset_chip();
|
||
}
|
||
else
|
||
{
|
||
print_string("Short button press detected; no action.\n");
|
||
set_sys_led_state(SYS_LED_ON);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Give the user feedback for button press
|
||
set_sys_led_state(SYS_LED_SLOW);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// An idle function that sleeps for 1 tick and does all the house-keeping
|
||
//
|
||
void idle(void)
|
||
{
|
||
PCON |= 1;
|
||
if (sec_counter >= SYS_TICK_HZ) {
|
||
sec_counter -= SYS_TICK_HZ;
|
||
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
||
uint8_t v = sfr_data[3];
|
||
#ifdef DEBUG
|
||
print_string(" Tick counter: "); print_long(ticks); write_char('\n');
|
||
#endif
|
||
v++;
|
||
sfr_data[3] = v;
|
||
if (!v) {
|
||
v = sfr_data[2];
|
||
v++;
|
||
sfr_data[2] = v;
|
||
if (!v) {
|
||
v = sfr_data[1];
|
||
v++;
|
||
sfr_data[1] = v;
|
||
if (!v) {
|
||
v = sfr_data[0];
|
||
v++;
|
||
sfr_data[0] = v;
|
||
}
|
||
}
|
||
}
|
||
reg_write_m(RTL837X_REG_SEC_COUNTER);
|
||
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
||
|
||
// Check for button presses once a second
|
||
handle_button();
|
||
|
||
#ifdef DEBUG
|
||
print_sfr_data();
|
||
write_char('\n');
|
||
#endif
|
||
}
|
||
|
||
// Check for Link changes
|
||
reg_read_m(RTL837X_REG_LINKS_89);
|
||
__xdata uint8_t linkbits_p89 = sfr_data[3];
|
||
|
||
reg_read_m(RTL837X_REG_LINKS);
|
||
if (cmp_4(sfr_data, linkbits_last) || (linkbits_p89 != linkbits_last_p89)) {
|
||
print_string("\n<new link: ");
|
||
print_byte(linkbits_p89); print_byte(sfr_data[0]); print_byte(sfr_data[1]);
|
||
print_byte(sfr_data[2]); print_byte(sfr_data[3]);
|
||
print_string(", was ");
|
||
print_byte(linkbits_last_p89); print_byte(linkbits_last[0]); print_byte(linkbits_last[1]);
|
||
print_byte(linkbits_last[2]); print_byte(linkbits_last[3]);
|
||
print_string(">\n");
|
||
linkbits_last_p89 = linkbits_p89;
|
||
if (!machine_detected.isRTL8373 && machine.n_sfp != 2) {
|
||
uint8_t p5 = sfr_data[2] >> 4;
|
||
uint8_t p5_last = linkbits_last[2] >> 4;
|
||
cpy_4(linkbits_last, sfr_data);
|
||
// Handle link change of the RTL8221 PHY, adjust SDS mode
|
||
if (p5_last != p5) {
|
||
if (p5 == 0x5) // 2.5GBit Mode
|
||
sds_config(0, SDS_HISGMII);
|
||
else if (p5 == 0x2) // 1GBit
|
||
sds_config(0, SDS_SGMII);
|
||
}
|
||
} else {
|
||
cpy_4(linkbits_last, sfr_data);
|
||
}
|
||
}
|
||
|
||
// Check for changes with SFP modules
|
||
handle_sfp();
|
||
|
||
// Check new Packets RX
|
||
handle_rx();
|
||
// Check UIP for packets to transmit
|
||
handle_tx();
|
||
// If STP protocol enabled, decrease STP timers to trigger actions
|
||
if (stpEnabled) {
|
||
if (!stp_clock) {
|
||
stp_clock = STP_TICK_DIVIDER;
|
||
stp_timers();
|
||
} else {
|
||
stp_clock--;
|
||
}
|
||
}
|
||
// Check whether a command is waiting in the cmd_buffer and execute
|
||
if (cmd_available) {
|
||
cmd_available = 0;
|
||
if (!cmd_tokenize())
|
||
cmd_parser();
|
||
print_string("\n> ");
|
||
}
|
||
}
|
||
|
||
|
||
// Sleep the given number of ticks and perform idle tasks if initialized
|
||
void sleep(uint16_t t)
|
||
{
|
||
sleep_ticks = t;
|
||
while (sleep_ticks > 0) {
|
||
if (idle_ready)
|
||
idle();
|
||
else
|
||
PCON |= 1;
|
||
}
|
||
}
|
||
|
||
|
||
void reset_chip(void)
|
||
{
|
||
REG_SET(RTL837X_REG_RESET, 1);
|
||
while(1);
|
||
}
|
||
|
||
|
||
void setup_external_irqs(void)
|
||
{
|
||
REG_SET(0x5f84, 0x42);
|
||
REG_SET(0x5f34, 0x3ff);
|
||
|
||
// EX0 = 1; // Enable external IRQ 0 (Link-change)
|
||
EX0 = 0;
|
||
IT0 = 1; // External IRQ on falling edge
|
||
|
||
EX1 = 1; // External IRQ 1 enable
|
||
EX2 = 1; // External IRQ 2 enable: bit EIE.0
|
||
EX3 = 1; // External IRQ 3 enable: bit EIE.1
|
||
PX3 = 1; // Set EIP.1 = 1: External IRQ 3 set to high priority
|
||
}
|
||
|
||
|
||
void rtl8224_enable(void)
|
||
{
|
||
// Set Pin 4 low
|
||
reg_bit_clear(RTL837X_REG_GPIO_32_63_OUTPUT, 4);
|
||
// Configure Pin as output
|
||
reg_bit_set(RTL837X_REG_GPIO_32_63_DIRECTION, 4);
|
||
delay(100);
|
||
// Set pin 4 high
|
||
reg_bit_set(RTL837X_REG_GPIO_32_63_OUTPUT, 4);
|
||
delay(500);
|
||
}
|
||
|
||
|
||
/*
|
||
* Set dividers for a chosen CPU frequency
|
||
*/
|
||
void setup_clock(void)
|
||
{
|
||
reg_read_m(RTL837X_REG_HW_CONF);
|
||
sfr_mask_data(0, 0x30, 0);
|
||
#if CLOCK_DIV != 0
|
||
// Divider in bits 4 & 5
|
||
sfr_mask_data(0, 0, CLOCK_DIV << 4);
|
||
#endif
|
||
// Bit 8 is set in managed mode 125MHz to use fast SPI mode
|
||
sfr_mask_data(1, 0, 0x01);
|
||
reg_write_m(RTL837X_REG_HW_CONF);
|
||
|
||
// Enable serial interface, set bit 0
|
||
reg_read_m(RTL837X_PIN_MUX_1);
|
||
sfr_mask_data(0, 0x1, 0x1);
|
||
reg_write_m(RTL837X_PIN_MUX_1);
|
||
}
|
||
|
||
|
||
/*
|
||
* Write a register reg of multipule phys, using a mask to select them, in page page
|
||
* Data to be written is in v
|
||
*/
|
||
void phy_write_mask(uint16_t phy_mask, uint8_t dev_id, uint16_t reg, uint16_t v)
|
||
{
|
||
#ifdef REGDBG
|
||
print_string("P"); print_byte(phy_mask>>8); print_byte(phy_mask); print_byte(dev_id); write_char('.'); print_byte(reg>>8); print_byte(reg); write_char(':');
|
||
print_byte(v>>8); print_byte(v); write_char(' ');
|
||
#endif
|
||
SFR_DATA_U16 = v; // SFR_A6, SFR_A7
|
||
SFR_SMI_PHYMASK = phy_mask; // SFR_C5
|
||
SFR_SMI_REG_U16 = reg; // SFR_C2, SFR_C3
|
||
SFR_SMI_DEV = (phy_mask >> 8) | dev_id << 3 | 2; // SFR_C4: bit 2 can also be set for some option
|
||
SFR_EXEC_GO = SFR_EXEC_WRITE_SMI;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
}
|
||
|
||
/*
|
||
* Write a register reg of phy, using a mask to select them, in page page
|
||
* Data to be written is in v
|
||
*/
|
||
void phy_write(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t v)
|
||
{
|
||
uint16_t phy_mask = bit_mask[phy_id];
|
||
#ifdef REGDBG
|
||
print_string("P"); print_byte(phy_mask>>8); print_byte(phy_mask); print_byte(dev_id); write_char('.'); print_byte(reg>>8); print_byte(reg); write_char(':');
|
||
print_byte(v>>8); print_byte(v); write_char(' ');
|
||
#endif
|
||
SFR_DATA_U16 = v; // SFR_A6, SFR_A7
|
||
SFR_SMI_PHYMASK = phy_mask; // SFR_C5
|
||
SFR_SMI_REG_U16 = reg; // SFR_C2, SFR_C3
|
||
SFR_SMI_DEV = (phy_mask >> 8) | dev_id << 3 | 2; // SFR_C4: bit 2 can also be set for some option
|
||
SFR_EXEC_GO = SFR_EXEC_WRITE_SMI;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
}
|
||
|
||
|
||
/*
|
||
* Read a phy register via MDIO clause 45
|
||
* Input must be: phy_id < 64, device_id < 32, reg < 0x10000)
|
||
* The result is in SFR A6 and A7 (SFR_DATA_8, SFR_DATA_0)
|
||
*/
|
||
void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg)
|
||
{
|
||
#ifdef REGDBG
|
||
print_string("p"); print_byte(phy_id); print_byte(dev_id); write_char('.'); print_byte(reg>>8); print_byte(reg); write_char(':');
|
||
#endif
|
||
SFR_SMI_REG_U16 = reg; // c2, c2
|
||
|
||
SFR_SMI_PHY = phy_id; // a5
|
||
SFR_SMI_DEV = dev_id << 3 | 2; // c4
|
||
|
||
SFR_EXEC_GO = SFR_EXEC_READ_SMI;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
#ifdef REGDBG
|
||
print_byte(SFR_DATA_8); print_byte(SFR_DATA_0); write_char(' ');
|
||
#endif
|
||
}
|
||
|
||
/*
|
||
* Modify a register reg of phy phy_id, in page page
|
||
* Set: bit mask of bits to set.
|
||
* Mask: bit mask of bits to clear.
|
||
|
||
* Note: We assume that the registers `SFR_SMI_REG_U16`, `SFR_SMI_PHY` and `SFR_SMI_DEV`
|
||
* keep there value, and dont have to be rewritten everytime.
|
||
*/
|
||
void phy_modify(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t mask, uint16_t set)
|
||
{
|
||
uint8_t smi_phy = dev_id << 3 | 2;
|
||
|
||
// Read the data
|
||
SFR_SMI_REG_U16 = reg; // c2, c2
|
||
SFR_SMI_PHY = phy_id; // a5
|
||
SFR_SMI_DEV = smi_phy; // c4
|
||
SFR_EXEC_GO = SFR_EXEC_READ_SMI;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
|
||
// Modify the reed data.
|
||
// TODO: Check if we directly can modify SFR register directly.
|
||
uint16_t data = SFR_DATA_U16 & ~(mask);
|
||
data |= set;
|
||
|
||
uint16_t phy_mask = bit_mask[phy_id];
|
||
|
||
// Write it back
|
||
SFR_SMI_REG_U16 = reg;
|
||
SFR_DATA_U16 = data;
|
||
SFR_SMI_PHYMASK = phy_mask; // SFR_C5
|
||
SFR_SMI_DEV = smi_phy | (phy_mask >> 8);
|
||
SFR_EXEC_GO = SFR_EXEC_WRITE_SMI;
|
||
do {
|
||
} while (SFR_EXEC_STATUS != 0);
|
||
}
|
||
|
||
void nic_setup(void)
|
||
{
|
||
// Enable NIC
|
||
// r6040:00000100 R6040-00001100
|
||
reg_bit_set(RTL837X_REG_HW_CONF, 0xc);
|
||
|
||
// This sets the size of the RX buffer, the filling level is in 0x7874
|
||
// R7848-000004ff
|
||
REG_SET(RTL837X_REG_NIC_RXBUFF_RX, 0x4ff);
|
||
|
||
// R7844-000007fe
|
||
REG_SET(RTL837X_REG_NIC_BUFFSIZE_TX, 0x7fe);
|
||
|
||
// Configure NIC RX to receive various types of packets
|
||
// RTL837X_REG_RX_CTRL: Set bits 24-31 to 0x4, clear bits 16/17
|
||
reg_read_m(RTL837X_REG_RX_CTRL);
|
||
sfr_mask_data(3, 0xff, 0x04);
|
||
sfr_mask_data(2, 0x03, 0);
|
||
reg_write_m(RTL837X_REG_RX_CTRL);
|
||
|
||
// Enable NIC TX (set bit 0)
|
||
reg_bit_set(RTL837X_REG_TX_CTRL, 0);
|
||
|
||
// Enable NIC RX (set bit 0)
|
||
reg_bit_set(RTL837X_REG_RX_CTRL, 0);
|
||
|
||
// Drop packets with invalid CRC
|
||
reg_bit_clear(RTL837X_REG_RX_CTRL, 2);
|
||
|
||
// R603c-00000200
|
||
// CPU-port is CPU-Tag aware (bit 9)
|
||
REG_SET(RTL837X_REG_CPU_TAG_AWARE_PMASK, 0x200);
|
||
|
||
// Insert CPU-tag for internally received packets (bit 0), MODE is 0, i.e. ALL packets (bits 8-9)
|
||
reg_read_m(RTL837X_REG_CPU_TAG);
|
||
sfr_mask_data(0, 1, 1);
|
||
sfr_mask_data(1, 3, 0);
|
||
reg_write_m(RTL837X_REG_CPU_TAG);
|
||
|
||
// Force MAC mode of the CPU port (port 9)
|
||
// r6368:00000194 R6368-00000197
|
||
reg_read_m(RTL837X_REG_MAC_FORCE_MODE + 9 * 4);
|
||
sfr_mask_data(0, 0, 3); // Set bits 0, 1: Force link
|
||
reg_write_m(RTL837X_REG_MAC_FORCE_MODE+ 9 * 4);
|
||
|
||
// Sequence number of TX packets
|
||
tx_seq = 0;
|
||
}
|
||
|
||
|
||
/*
|
||
* Configure the PHY-Side of the SDS-SDS link between SoC and PHY
|
||
*/
|
||
void sds_init(void)
|
||
{
|
||
/*
|
||
p001e.000d:9535 R02f8-00009535 R02f4-0000953a P000001.1e00000d:953a
|
||
p001e.000d:953a p001e.000d:953a R02f8-0000953a R02f4-00009530 P000001.1e00000d:9530
|
||
|
||
RTL8373:
|
||
p001e.000d:0010 R02f8-00000010 R02f4-0000001a P000001.1e00000d:b7fe
|
||
p001e.000d:0010 p001e.000d:0010 R02f8-00000010 R02f4-00000010 P000001.1e00000d:b7fe
|
||
*/
|
||
phy_read(0, PHY_MMD30, 0xd);
|
||
uint16_t pval = SFR_DATA_U16;
|
||
|
||
// PHY Initialization:
|
||
REG_WRITE(0x2f8, 0, 0, pval >> 8, pval);
|
||
delay(20);
|
||
|
||
pval &= 0xfff0;
|
||
pval |= 0x0a;
|
||
REG_WRITE(0x2f4, 0, 0, pval >> 8, pval);
|
||
delay(10);
|
||
|
||
phy_write_mask(0x1, PHY_MMD30, 0xd, pval);
|
||
|
||
phy_read(0, PHY_MMD30, 0xd);
|
||
pval = SFR_DATA_U16;
|
||
|
||
REG_WRITE(0x2f8, 0, 0, pval >> 8, pval);
|
||
|
||
pval &= 0xfff0;
|
||
REG_WRITE(0x2f4, 0, 0, pval >> 8, pval);
|
||
|
||
phy_write_mask(0x1, PHY_MMD30, 0xd, pval);
|
||
|
||
if (machine_detected.isN) {
|
||
uint16_t pval;
|
||
|
||
print_string(" N-settings");
|
||
// Serdes 0 RX PN swap for 64B/66B
|
||
sds_read(1, 6, 2);
|
||
pval = SFR_DATA_U16;
|
||
sds_write_v(1, 6, 2, pval | 0x2000);
|
||
|
||
// Serdes 1 RX PN swap for 8B/10B
|
||
sds_read(1, 0, 0);
|
||
pval = SFR_DATA_U16;
|
||
sds_write_v(1, 0, 0, pval | 0x200);
|
||
|
||
// Serdes 0 RX PN swap for 64B/66B
|
||
sds_read(0, 6, 2);
|
||
pval = SFR_DATA_U16;
|
||
sds_write_v(0, 6, 2, pval | 0x2000);
|
||
|
||
if (machine_detected.isRTL8373) {
|
||
// RTL8224: Serdes 0 RX PN swap for 64B/66B
|
||
// We assume that RTL8373N always paired with RTL8224N.
|
||
// This sds register value is 0x0000 at reset.
|
||
// So only write to it.
|
||
RTL8224_SDS_WRITE(0, 6, 2, 0x2000);
|
||
} else {
|
||
// Serdes 0 RX PN swap for 8B/10B
|
||
sds_read(0, 0, 0);
|
||
pval = SFR_DATA_U16;
|
||
sds_write_v(0, 0, 0, pval | 0x200);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void set_sys_led_state(uint8_t state)
|
||
{
|
||
reg_read_m(RTL837X_REG_LED_MODE);
|
||
sfr_mask_data(2, 0x03, state);
|
||
reg_write_m(RTL837X_REG_LED_MODE);
|
||
}
|
||
|
||
void rtl8373_revision(void)
|
||
{
|
||
reg_read_m(RTL837X_REG_CHIP_INFO);
|
||
sfr_mask_data(2, 0x0a, 0x0a); // Enable reading version
|
||
reg_write_m(RTL837X_REG_CHIP_INFO);
|
||
delay(50);
|
||
|
||
reg_read_m(RTL837X_REG_CHIP_INFO);
|
||
print_string("CPU revision: "); print_byte(sfr_data[2]); print_byte(sfr_data[2]); write_char('\n');
|
||
sfr_mask_data(2, 0x0a, 0x00); // Enable reading version
|
||
reg_write_m(RTL837X_REG_CHIP_INFO);
|
||
}
|
||
|
||
|
||
void rtl8373_init(void)
|
||
{
|
||
print_string("\nrtl8373_init called\n");
|
||
|
||
// r65d8:3ffbedff R65d8-3ffbedff
|
||
reg_bit_set(0x65d8, 0x1d);
|
||
|
||
sds_init();
|
||
// Disable all SERDES for configuration
|
||
REG_SET(RTL837X_REG_SDS_MODES, 0x000037ff);
|
||
|
||
// q000601:c800 Q000601:c804 q000601:c804 Q000601:c800
|
||
sds_read(0, 0x06, 0x01);
|
||
uint16_t pval = SFR_DATA_U16;
|
||
sds_write_v(0, 0x06, 0x01, pval | 0x04);
|
||
delay(50);
|
||
sds_read(0, 0x06, 0x01);
|
||
pval = SFR_DATA_U16;
|
||
sds_write_v(0, 0x06, 0x01, pval & 0xfffb);
|
||
|
||
phy_config_8224();
|
||
sds_config_mac(1, SDS_OFF); // Off for now until SFP+ port used
|
||
sds_config_mac(2, SDS_SGMII); // For RTL8224
|
||
sds_config(0, SDS_QXGMII);
|
||
|
||
// SDS 1 setup
|
||
// q012100:4902 Q012100:4906 q013605:0000 Q013605:4000 Q011f02:001f q011f15:0086
|
||
sds_write_v(1, 0x21, 0x00, 0x4906);
|
||
sds_write_v(1, 0x36, 0x05, 0x4000);
|
||
sds_write_v(1, 0x1f, 0x02, 0x001f);
|
||
sds_read(1, 0x1f, 0x15);
|
||
pval = SFR_DATA_U16;
|
||
|
||
// r0a90:000000f3 R0a90-000000fc
|
||
reg_read_m(RTL837X_CFG_PHY_MDI_REVERSE);
|
||
sfr_mask_data(0, 0x0f,0x0c);
|
||
reg_write_m(RTL837X_CFG_PHY_MDI_REVERSE);
|
||
|
||
if (machine_detected.isN) {
|
||
print_string(" TX_POLARITY_SWAP\n");
|
||
// FOR N-Version: #TX_POLARITY_SWAP
|
||
reg_read_m(RTL837X_CFG_PHY_TX_POLARITY_SWAP);
|
||
sfr_data[2] = 0x59;
|
||
sfr_data[3] = 0x6a;
|
||
reg_write_m(RTL837X_CFG_PHY_TX_POLARITY_SWAP);
|
||
}
|
||
|
||
rtl8224_phy_enable();
|
||
|
||
// Disable PHYs for configuration
|
||
phy_write_mask(0xff,PHY_MMD31,0xa610,0x2858);
|
||
|
||
// Set bits 0x13 and 0x14 of 0x5fd4
|
||
// r5fd4:0002914a R5fd4-001a914a
|
||
reg_bit_set(0x5fd4, 0x13);
|
||
reg_bit_set(0x5fd4, 0x14);
|
||
|
||
// Configure ports
|
||
uint16_t reg = 0x1238; // Port base register for the bits we set
|
||
for (char i = 0; i < 9; i++) {
|
||
// Bit 7 (0x40) enables replacement of the RTL-VLAN tag with an 802.1Q VLAN tag
|
||
REG_SET(reg, 0xe77);
|
||
reg += 0x100;
|
||
}
|
||
|
||
// r0b7c:000000d8 R0b7c-000000f8 r6040:00000030 R6040-00000031
|
||
reg_bit_set(0xb7c, 5);
|
||
|
||
// R7124-00001050 R7128-00001050 R712c-00001050 R7130-00001050 R7134-00001050 R7138-00001050
|
||
// R713c-00001050 R7140-00001050 R7144-00001050 R7148-00001050
|
||
REG_SET(0x7124, 0x1050); REG_SET(0x7128, 0x1050); REG_SET(0x712c, 0x1050);
|
||
REG_SET(0x7130, 0x1050); REG_SET(0x7134, 0x1050); REG_SET(0x7138, 0x1050);
|
||
REG_SET(0x713c, 0x1050); REG_SET(0x7140, 0x1050); REG_SET(0x7144, 0x1050);
|
||
REG_SET(0x7148, 0x1050);
|
||
|
||
reg_bit_set(RTL837X_REG_HW_CONF, 0);
|
||
|
||
// enable EEE for all ports at 2.5G and 10G, but don't reset the PHYs
|
||
port_eee_enable_all(EEE_2G5 | EEE_NORESET);
|
||
|
||
// TODO: patch the PHYs
|
||
|
||
// Re-enable PHY after configuration
|
||
phy_write_mask(0xff,PHY_MMD31,0xa610,0x2058);
|
||
|
||
// Enables MAC access
|
||
// Set bits 0xc-0x14 of 0x632c to 0x1f8, see rtl8372_init
|
||
// r632c:00000540 R632c-001f8540 // RTL8373: 001ff540
|
||
reg_read_m(0x632c);
|
||
sfr_mask_data(1, 0x70, 0xf0); // The ports of the RTL8824
|
||
sfr_mask_data(2, 0x10, 0x1f);
|
||
reg_write_m(0x632c);
|
||
|
||
print_string("\nrtl8373_init done\n");
|
||
}
|
||
|
||
|
||
void rtl8372_init(void)
|
||
{
|
||
print_string("\nrtl8372_init called\n");
|
||
|
||
sds_init();
|
||
phy_config(8); // PHY configuration: External 8221B?
|
||
phy_config(3); // PHY configuration: all internal PHYs?
|
||
// Set the MAC SerDes Modes Bits 0-4: SDS 0 = 0x2 (0x2), Bits 5-9: SDS 1: 1f (off)
|
||
// r7b20:00000bff R7b20-00000bff r7b20:00000bff R7b20-00000bff r7b20:00000bff R7b20-000003ff r7b20:000003ff R7b20-000003e2 r7b20:000003e2 R7b20-000003e2
|
||
reg_read_m(RTL837X_REG_SDS_MODES);
|
||
sfr_mask_data(1, 0, 0x03);
|
||
sfr_mask_data(0, 0, 0xe2);
|
||
reg_write_m(RTL837X_REG_SDS_MODES);
|
||
|
||
// r0a90:000000f3 R0a90-000000fc
|
||
reg_read_m(RTL837X_CFG_PHY_MDI_REVERSE);
|
||
sfr_mask_data(0, 0x0f, 0x0c);
|
||
reg_write_m(RTL837X_CFG_PHY_MDI_REVERSE);
|
||
|
||
// Disable PHYs for configuration
|
||
phy_write_mask(0xf0,PHY_MMD31,0xa610,0x2858);
|
||
|
||
// Set bits 0x13 and 0x14 of 0x5fd4
|
||
// r5fd4:0002914a R5fd4-001a914a
|
||
reg_bit_set(0x5fd4, 0x13);
|
||
reg_bit_set(0x5fd4, 0x14);
|
||
|
||
// Configure ports 3-8:
|
||
//
|
||
// r1538:00000e33 R1538-00000e37 r1538:00000e37 R1538-00000e37 r1538:00000e37 R1538-00000f37
|
||
// [...]
|
||
///
|
||
uint16_t reg = 0x1238 + 0x300; // Port base register for the bits we set
|
||
for (char i = machine.min_port; i <= machine.max_port; i++) {
|
||
// Bit 7 (0x40) enables replacement of the RTL-VLAN tag with an 802.1Q VLAN tag
|
||
REG_SET(reg, 0xe77);
|
||
reg += 0x100;
|
||
}
|
||
|
||
// r0b7c:000000d8 R0b7c-000000f8 r6040:00000030 R6040-00000031
|
||
reg_bit_set(0xb7c, 5);
|
||
|
||
reg_bit_set(RTL837X_REG_HW_CONF, 0);
|
||
|
||
|
||
// enable EEE for all ports at 2.5G and 10G, but don't reset the PHYs
|
||
port_eee_enable_all(EEE_2G5 | EEE_NORESET);
|
||
|
||
// TODO: patch the PHYs
|
||
|
||
// Re-enable PHY after configuration
|
||
phy_write_mask(0xf0,PHY_MMD31,0xa610,0x2058);
|
||
|
||
// Enables MAC access
|
||
// Set bits 0xc-0x14 of 0x632c to 0x1f8, see rtl8372_init
|
||
// r632c:00000540 R632c-001f8540 // RTL8373: 001ff540
|
||
reg_read_m(0x632c);
|
||
sfr_mask_data(1, 0x70, 0x80);
|
||
sfr_mask_data(2, 0x10, 0x1f);
|
||
reg_write_m(0x632c);
|
||
print_string("\nrtl8372_init done\n");
|
||
}
|
||
|
||
|
||
/*
|
||
* The SoC manages Link-State for steering the LEDs and can set PHY-settings
|
||
* automatically through Realtek's SMI (Simple Managagement) Interface, a
|
||
* proprietary version of MDIO which for example allows for more PHYs on the same
|
||
* bus.
|
||
* Configure polling via SMI and the interface setup during boot.
|
||
*/
|
||
void init_smi(void)
|
||
{
|
||
print_string("\ninit_switch called\n");
|
||
|
||
/* Set the SMI(i.e.I2C) type for PHY polling, 0b01 is 2.5/10G PHY. Disable (0b00) for the SFP-ports
|
||
* which are at port 8 and additionally at port 3 for a dual SFP device
|
||
*/
|
||
REG_SET(RTL837X_REG_SMI_MAC_TYPE, machine.n_sfp == 2 ? 0x00005515 : 0x00005555);
|
||
|
||
// Configure polling of all PHYs by the MAC to detect link-state changes
|
||
if (machine_detected.isRTL8373) {
|
||
REG_SET(RTL837X_REG_SMI_PORT_POLLING, 0xff);
|
||
} else {
|
||
REG_SET(RTL837X_REG_SMI_PORT_POLLING, machine.n_sfp == 2 ? 0xf0 : 0x1f8);
|
||
}
|
||
// Enable MDC
|
||
reg_read_m(RTL837X_REG_SMI_CTRL);
|
||
sfr_mask_data(1, 0, 0x70); // Set bits 12-14 to enable MDC for SMI0-SMI2
|
||
reg_write_m(RTL837X_REG_SMI_CTRL);
|
||
delay(50);
|
||
|
||
if (!machine_detected.isRTL8373) {
|
||
// Change I2C addresses for SMI of the non-existent PHYs
|
||
// r6450:000020e6 R6450-000000e6
|
||
reg_read_m(RTL837X_REG_SMI_PORT6_9_ADDR);
|
||
sfr_mask_data(1, 0x7c, 0);
|
||
reg_write_m(RTL837X_REG_SMI_PORT6_9_ADDR);
|
||
|
||
// r644c:0a418820 R644c-0a400820
|
||
reg_read_m(RTL837X_REG_SMI_PORT0_5_ADDR);
|
||
sfr_mask_data(2, 0x0f, 0);
|
||
sfr_mask_data(1, 0x80, 0);
|
||
reg_write_m(RTL837X_REG_SMI_PORT0_5_ADDR);
|
||
}
|
||
}
|
||
|
||
|
||
/* Set up serial port 0 using Timer 1 as baudrate generator.
|
||
* For x Bd these settings are needed, see table below.
|
||
* NOTE: Settings only valid for F_SYS = 125 MHz!
|
||
* | Wanted | | TMR | F_SYS | | Actual | |
|
||
* | baudrate | SMOD0 | DIV | DIV | TH1 | baudrate | Error |
|
||
* | -------- | ----- | --- | ----- | ---- | -------- | ------ |
|
||
* | 1200 | 0 | 12 | 255 | 0x01 | 1276.6 | 6.00% |
|
||
* | 2400 | 0 | 12 | 136 | 0x78 | 2393.5 | −0.27% |
|
||
* | 4800 | 0 | 4 | 203 | 0x35 | 4810.7 | 0.22% |
|
||
* | 9600 | 1 | 4 | 203 | 0x35 | 9621.3 | 0.22% |
|
||
* | 14400 | 1 | 4 | 136 | 0x78 | 14361.2 | −0.27% |
|
||
* | 19200 | 1 | 4 | 102 | 0x9a | 19148.3 | −0.27% |
|
||
* | 38400 | 1 | 4 | 51 | 0xcd | 38296.6 | −0.27% |
|
||
* | 57600 | 1 | 4 | 34 | 0xde | 57444.9 | −0.27% |
|
||
* | 115200 | 1 | 4 | 17 | 0xef | 114889.7 | −0.27% |
|
||
*/
|
||
#if CLOCK_HZ != 125000000
|
||
#warning "SERIAL 0 baudrate setting may only valid for F_CPU = 125 MHz!"
|
||
#endif
|
||
void setup_serial_timer1(void)
|
||
{
|
||
// Timer 1: Mode 2: automatic reload
|
||
TMOD &= 0x0F;
|
||
TMOD |= 0x20; // Timer1: Mode2: Timer, 8-bit with auto-reload
|
||
CKCON |= 0x10; // Timer1 clock divider: F_SYS / 4: T2M = 1, Timer 1 uses clk/4
|
||
|
||
PCON |= 0x80; // SMOD0 = 1; Double the Baud Rate, don't divide Timer 1 Overflag signal.
|
||
|
||
SCON = 0x50; // Mode = 1: ASYNC 8N1 with Timer 2 as baud-rate generator, REN_0 Receive enable
|
||
|
||
/* The TH1 register contain the reload value, timer1 when T1 overflows to 0x100.
|
||
* NOTE: compiler computs the wrong value. 0xF0 is calculated but 0xEF is the right value for 115200.
|
||
* Also https://www.keil.com/products/c51/baudrate.asp confirms this.
|
||
* Added 32 before div by 64 to make sure rounding is correct so that the results are right.
|
||
*
|
||
* TH1 = 0x100 - (2^SMOD0 * F_SYS) / ( TMR1_DIV / BAUDRATE * 32)
|
||
*/
|
||
TH1 = (0x100 - (((CLOCK_HZ / SERIAL_BAUD_RATE) + 32) / (4 * 16))) & 0xff;
|
||
|
||
TCON |= 0x40; // Start timer 1
|
||
|
||
ET1 = 0; // Timer1 Interrupt is NOT wanted!
|
||
TI = 0; // Clear TI-interrupt flag
|
||
RI = 0; // Clear RI-interrupt flag
|
||
|
||
tx_buf_empty = 1; // Set tx `serial buffer is empty`-software flag.
|
||
|
||
ES = 1; // Enable serial IRQ
|
||
}
|
||
|
||
|
||
void setup_i2c(void)
|
||
{
|
||
REG_SET(RTL837X_REG_I2C_MST_IF_CTRL, 0);
|
||
// Configure SFP EEPROM address (0x50) as I2C device address
|
||
// Configure SFP readings address (0x51) as I2C device address
|
||
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16), 0x50 >> 5, (0x50 << 3) & 0xff);
|
||
|
||
REG_SET(RTL837X_REG_I2C_CTRL2, 0);
|
||
|
||
// HW Control register, enable I2C depending on PIN configuration
|
||
reg_read_m(RTL837X_PIN_MUX_1);
|
||
for (uint8_t sfp = 0; sfp < machine.n_sfp; sfp++) {
|
||
const uint8_t scl_bus = i2c_bus_from_scl_pin(machine.sfp_port[sfp].i2c.scl);
|
||
const uint8_t sda_bus = i2c_bus_from_sda_pin(machine.sfp_port[sfp].i2c.sda);
|
||
print_string("Configuring I2C for SFP idx="); print_byte(sfp); print_string(" SCL="); print_byte(scl_bus); print_string(", SDA="); print_byte(sda_bus); write_char('\n');
|
||
switch (scl_bus) {
|
||
case 3:
|
||
// Bit 5-6 0b10 -> SCL (implies enabled SDA on bus 3)
|
||
sfr_mask_data(0, 0x60, 0x40);
|
||
break;
|
||
case 2:
|
||
// Bit 15-16 0b01 -> SCL
|
||
sfr_mask_data(1, 0x80, 0x80);
|
||
sfr_mask_data(2, 0x01, 0x00);
|
||
break;
|
||
case 1:
|
||
// Bit 11-12 0b01 -> SCL
|
||
sfr_mask_data(1, 0x18, 0x08);
|
||
break;
|
||
case 0:
|
||
// Bit 7-8 0b01 -> SCL
|
||
sfr_mask_data(0, 0x80, 0x80);
|
||
sfr_mask_data(1, 0x01, 0x00);
|
||
break;
|
||
default:
|
||
print_string("Invalid SCL bus number: "); print_byte(scl_bus); write_char('\n');
|
||
}
|
||
|
||
switch (sda_bus) {
|
||
case 4:
|
||
// Bit 29 0b0 -> SDA
|
||
sfr_mask_data(3, 0x20, 0x00);
|
||
break;
|
||
case 3:
|
||
// Bit 5-6 0b10 -> SDA (implies enabled SCL on bus 3)
|
||
sfr_mask_data(0, 0x60, 0x40);
|
||
break;
|
||
case 2:
|
||
// Bit 17-18 0b01 -> SDA
|
||
sfr_mask_data(2, 0x06, 0x02);
|
||
break;
|
||
case 1:
|
||
// Bit 13-14 0b01 -> SDA
|
||
sfr_mask_data(1, 0x60, 0x20);
|
||
break;
|
||
case 0:
|
||
// Bit 9-10 0b01 -> SDA
|
||
sfr_mask_data(1, 0x06, 0x02);
|
||
break;
|
||
default:
|
||
print_string("Invalid SDA bus number: "); print_byte(sda_bus); write_char('\n');
|
||
}
|
||
}
|
||
reg_write_m(RTL837X_PIN_MUX_1);
|
||
}
|
||
|
||
|
||
void check_and_flash_update_image(void)
|
||
{
|
||
// Check if an update image is in flash
|
||
flash_region.addr = FIRMWARE_UPLOAD_START;
|
||
flash_region.len = 0x100;
|
||
flash_read_bulk(flash_buf);
|
||
if (flash_buf[0] == 0x00 && flash_buf[1] == 0x40)
|
||
{
|
||
// Yes, flash the new image to the start of flash and reset
|
||
__xdata uint32_t dest = 0x0;
|
||
__xdata uint32_t source = FIRMWARE_UPLOAD_START;
|
||
__xdata uint16_t i = 0;
|
||
__xdata uint16_t j = 0;
|
||
__xdata uint8_t * __xdata bptr;
|
||
print_string("Identified update image. Checking integrity...");
|
||
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing will fail
|
||
set_sys_led_state(SYS_LED_FAST);
|
||
crc_value = 0x0000;
|
||
for (i = 0; i < 1024; i++) {
|
||
flash_region.addr = source;
|
||
flash_region.len = FLASH_BUF_SIZE;
|
||
flash_read_bulk(flash_buf);
|
||
bptr = flash_buf;
|
||
for (j = 0; j < FLASH_BUF_SIZE; j++) {
|
||
crc16(bptr++);
|
||
}
|
||
source += FLASH_BUF_SIZE;
|
||
if (i%16 == 0)
|
||
write_char('.');
|
||
}
|
||
if (crc_value == 0xb001) {
|
||
print_string("\nChecksum OK\n");
|
||
print_string("Update in progress, moving firmware to start of FLASH.");
|
||
source = FIRMWARE_UPLOAD_START;
|
||
// A 512kByte = 4MBit Flash has 128*8=1024 512byte blocks, we copy only 896
|
||
// (don't overwrite config @ 0x70000)
|
||
for (i = 0; i < 896; i++) {
|
||
// print_string("Writing block: ");
|
||
// print_short(dest);
|
||
flash_region.addr = source;
|
||
flash_region.len = FLASH_BUF_SIZE;
|
||
flash_read_bulk(flash_buf);
|
||
if (!(i & 0x7)) {
|
||
flash_region.addr = dest;
|
||
flash_sector_erase();
|
||
write_char('.');
|
||
}
|
||
flash_region.addr = dest;
|
||
flash_region.len = FLASH_BUF_SIZE;
|
||
flash_write_bytes(flash_buf);
|
||
dest += FLASH_BUF_SIZE;
|
||
source += FLASH_BUF_SIZE;
|
||
}
|
||
print_string("\nDeleting uploaded flash image\n");
|
||
dest = FIRMWARE_UPLOAD_START;
|
||
for (register uint8_t i=0; i < 128; i++) // TODO: Erasing the entire 512kByte upload area is probably not necessary
|
||
{
|
||
flash_region.addr = dest;
|
||
flash_sector_erase();
|
||
dest += 0x1000;
|
||
}
|
||
print_string("Resetting now");
|
||
delay(200);
|
||
reset_chip();
|
||
}
|
||
print_string("Checksum incorrect, please upload the image again\n");
|
||
print_string("Erasing bad uploaded flash image\n");
|
||
dest = FIRMWARE_UPLOAD_START;
|
||
for (register uint8_t i=0; i < 128; i++) {
|
||
flash_region.addr = dest;
|
||
flash_sector_erase();
|
||
dest += 0x1000;
|
||
}
|
||
}
|
||
}
|
||
|
||
void bootloader(void)
|
||
{
|
||
ticks = 0;
|
||
stp_clock = STP_TICK_DIVIDER;
|
||
dhcp_state.state = DHCP_OFF;
|
||
sbuf_ptr = 0;
|
||
|
||
CKCON = 0; // Initial Clock configuration
|
||
SFR_97 = 0; // HADDR?
|
||
|
||
// Set in managed mode:
|
||
SFR_b9 = 0x00;
|
||
SFR_ba = 0x80;
|
||
|
||
// Disable all interrupts (global and individually) by setting IE register (SFR A8) to 0
|
||
IE = 0;
|
||
EIE = 0; // SFR e8: EIE. Disable all external IRQs
|
||
|
||
idle_ready = 0;
|
||
// HW setup, serial, timer, external IRQs
|
||
setup_clock();
|
||
setup_timer2();
|
||
setup_serial_timer1();
|
||
setup_external_irqs();
|
||
|
||
EA = 1; // Enable global interrupt
|
||
|
||
// Flash controller should be initialized before any code in other banks is being fetched
|
||
// See this issue: https://github.com/logicog/RTLPlayground/issues/70
|
||
print_string("\nInitializing Flash controller\n");
|
||
flash_init(1);
|
||
|
||
// Set default for SFP pins so we can start up a module already inserted
|
||
sfp_pins_last = 0x33; // signal LOS and no module inserted (for both slots, even if only 1 present)
|
||
// We have not detected any link
|
||
linkbits_last[0] = linkbits_last[1] = linkbits_last[2] = linkbits_last[3] = linkbits_last_p89 = 0;
|
||
|
||
button_last = 0;
|
||
button_sec_counter_last = 0;
|
||
|
||
machine_detected.isRTL8373 = 0;
|
||
machine_detected.isN = 0;
|
||
print_string("Detecting CPU: RTL837");
|
||
reg_read_m(RTL837X_REG_CHIP_ID);
|
||
if (sfr_data[1] == 0x73) { // Register was 0x8373xx00
|
||
machine_detected.isRTL8373 = 1;
|
||
write_char('3');
|
||
} else {
|
||
write_char('2');
|
||
}
|
||
// Detect non-N/N chip, 0xxxxx70xx
|
||
if (sfr_data[2] == 0x70) {
|
||
machine_detected.isN = 1;
|
||
write_char('N');
|
||
}
|
||
write_char('\n');
|
||
if (machine.isRTL8373 != machine_detected.isRTL8373) {
|
||
print_string("INCORRECT MACHINE!");
|
||
}
|
||
if (machine_detected.isRTL8373) {
|
||
rtl8224_enable(); // Power on the RTL8224
|
||
}
|
||
|
||
// Print SW version
|
||
print_sw_version();
|
||
|
||
// Reset NIC
|
||
reg_bit_set(RTL837X_REG_RESET, RESET_NIC_BIT);
|
||
do {
|
||
reg_read(RTL837X_REG_RESET);
|
||
} while (SFR_DATA_0 & (1 << RESET_NIC_BIT));
|
||
print_string("NIC reset\n");
|
||
|
||
uip_ipaddr(&uip_hostaddr, ownIP[0], ownIP[1], ownIP[2], ownIP[3]);
|
||
uip_ipaddr(&uip_draddr, gatewayIP[0], gatewayIP[1], gatewayIP[2], gatewayIP[3]);
|
||
uip_ipaddr(&uip_netmask, netmask[0], netmask[1], netmask[2], netmask[3]);
|
||
reg_read_m(RTL837X_REG_CHIP_UUID);
|
||
#ifdef DEBUG
|
||
print_string("SoC UUID: "); print_sfr_data();
|
||
#endif
|
||
uip_ethaddr.addr[0] = 0x06; // LAA prefix
|
||
uip_ethaddr.addr[3] = sfr_data[0] ^ sfr_data[3];
|
||
uip_ethaddr.addr[4] = sfr_data[1] ^ sfr_data[3];
|
||
uip_ethaddr.addr[5] = sfr_data[2] ^ sfr_data[3];
|
||
reg_read_m(RTL837X_REG_CHIP_LOT_NO);
|
||
#ifdef DEBUG
|
||
print_string(", LOT: "); print_sfr_data(); write_char(' ');
|
||
#endif
|
||
uip_ethaddr.addr[1] = sfr_data[0] ^ sfr_data[2];
|
||
uip_ethaddr.addr[2] = sfr_data[1] ^ sfr_data[3];
|
||
print_string("Setting MAC to: ");
|
||
print_byte(uip_ethaddr.addr[0]); write_char(':'); print_byte(uip_ethaddr.addr[1]); write_char(':');
|
||
print_byte(uip_ethaddr.addr[2]); write_char(':'); print_byte(uip_ethaddr.addr[3]); write_char(':');
|
||
print_byte(uip_ethaddr.addr[4]); write_char(':'); print_byte(uip_ethaddr.addr[5]); write_char('\n');
|
||
|
||
REG_SET(RTL837X_PIN_MUX_2, 0x0); // Disable pins for ACL
|
||
init_smi();
|
||
|
||
rtl8373_revision();
|
||
|
||
leds_setup();
|
||
machine_custom_init();
|
||
|
||
leds_dump();
|
||
|
||
set_sys_led_state(SYS_LED_SLOW);
|
||
|
||
if (machine_detected.isRTL8373)
|
||
rtl8373_init();
|
||
else
|
||
rtl8372_init();
|
||
delay(1000);
|
||
|
||
check_and_flash_update_image();
|
||
|
||
#ifdef DEBUG
|
||
// This register seems to work on the RTL8373 only if also the SDS
|
||
// Is correctly configured. Therefore, we can test it, here...
|
||
// Reset seconds counter
|
||
print_string("\nTIMER-TEST: \n");
|
||
REG_SET(RTL837X_REG_SEC_COUNTER, 0x0);
|
||
delay(100);
|
||
print_reg(RTL837X_REG_SEC_COUNTER); write_char(' ');
|
||
REG_SET(RTL837X_REG_SEC_COUNTER, 0x1);
|
||
delay(100);
|
||
print_reg(RTL837X_REG_SEC_COUNTER);
|
||
REG_SET(RTL837X_REG_SEC_COUNTER, 0x2); write_char(' ');
|
||
delay(100);
|
||
print_reg(RTL837X_REG_SEC_COUNTER);
|
||
REG_SET(RTL837X_REG_SEC_COUNTER, 0x3); write_char(' ');
|
||
print_reg(RTL837X_REG_SEC_COUNTER);
|
||
#endif
|
||
stpEnabled = 0;
|
||
nic_setup();
|
||
vlan_setup();
|
||
port_l2_setup();
|
||
igmp_setup();
|
||
uip_init();
|
||
uip_arp_init();
|
||
httpd_init();
|
||
|
||
management_vlan = 0; // Disabled
|
||
|
||
setup_i2c();
|
||
setup_sfp_gpio();
|
||
|
||
print_string(greeting);
|
||
|
||
print_string("\nClock register: ");
|
||
print_reg(0x6040);
|
||
print_string("\nRegister 0x7b20/RTL837X_REG_SDS_MODES: ");
|
||
print_reg(0x7b20);
|
||
|
||
print_string("\nVerifying PHY settings:\n");
|
||
// p031f.a610:2058 p041f.a610:2058 p051f.a610:2058 r4f3c:00000000 p061f.a610:2058 p071f.a610:2058
|
||
port_stats_print();
|
||
|
||
execute_config();
|
||
print_string("\n> ");
|
||
idle_ready = 1;
|
||
|
||
set_sys_led_state(SYS_LED_ON);
|
||
|
||
cmd_editor_init();
|
||
while (1) {
|
||
cmd_edit();
|
||
idle(); // Enter Idle mode until interrupt occurs
|
||
}
|
||
}
|