#include <8051.h> #include #include "rtl837x_sfr.h" #include "rtl837x_flash.h" #define SYS_TICK_HZ 100 #define SERIAL_BAUD_RATE 57600 /* 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 #define RTL837X_REG_LED_MODE 0x6520 #define RTL837X_REG_SMI_CTRL 0x6454 #define RTL837X_REG_RESET 0x0024 // Blink rate is defined by setAsicRegBits(0x6520,0xe00000,rate); #define SFR_EXEC_READ_REG 1 #define SFR_EXEC_WRITE_REG 3 #define SFR_EXEC_READ_SMI 9 #define SFR_EXEC_WRITE_SMI 11 __xdata unsigned short ticks; #define N_WORDS 10 __xdata signed char cmd_words_b[N_WORDS]; // Buffer for serial input, SBUF_SIZE must be power of 2 < 256 #define SBUF_SIZE 32 __xdata char sbuf_ptr; __xdata unsigned sbuf[SBUF_SIZE]; __code unsigned char * __code greeting = "HI! This is a minimal prompt to explore the RTL8372!\r\n"; __code unsigned char * __code hex = "0123456789abcdef"; #define N_COMMANDS 1 struct command { unsigned char *cmd; unsigned char id; }; void isr_timer0(void) __interrupt(1) { TR0 = 0; // Stop timer 0 TH0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) >> 8; TL0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) % 0xff; ticks++; /* SFR_DATA_24 = 0x00; SFR_DATA_16 = 0x23; SFR_DATA_8 = 0xe0; SFR_DATA_0 = 0xf0; SFR_REG_ADDRH = RTL837X_REG_LED_MODE >> 8; SFR_REG_ADDRL = RTL837X_REG_LED_MODE & 0xff; SFR_EXEC_GO = SFR_EXEC_WRITE_REG; do { } while (SFR_EXEC_STATUS != 0); */ TR0 = 1; // Re-start timer 0 } void isr_serial(void) __interrupt(4) { if (RI == 1) { sbuf[sbuf_ptr] = SBUF; sbuf_ptr = (sbuf_ptr + 1) & (SBUF_SIZE - 1); RI = 0; } } void write_char(char c) { do { } while (TI == 0); TI = 0; SBUF = c; } void print_string(__code char *p) { while (*p) write_char(*p++); } void print_short(unsigned short a) { print_string("0x"); for (signed char i = 12; i >= 0; i -= 4) { write_char(hex[(a >> i) & 0xf]); } } void print_byte(unsigned char a) { write_char(hex[(a >> 4) & 0xf]); write_char(hex[a & 0xf]); write_char(' '); } /* * External IRQ 0 Service Routine * Note that all registers are being put on the STACK because of calling a subroutine */ void isr_ext0(void) __interrupt(0) { EX0 = 0; write_char('X'); IT0 = 1; EX0 = 1; } /* * 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'); } /* * 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'); } void setup_timer0(void) { TMOD = 0x11; // Timer 1: Mode 1, Timer 0: Mode 1, i.e. 16 bit counters, no auto-reload // The TH0 registers contain the high/low byte that is loaded into // timer0 when T0 overflows to 0x10000 TH0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) >> 8; TL0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) % 0xff; TCON = 0x10; // Start timer 0 CKCON &= 0xc7; ET0 = 1; // Enable timer interrupts } void reg_read(uint16_t reg_addr) { SFR_REG_ADDRH = reg_addr >> 8; SFR_REG_ADDRL = 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_write(uint16_t reg_addr) { /* Data to write must be in SFR A4, A5, A6, A7 */ SFR_REG_ADDRH = reg_addr >> 8; SFR_REG_ADDRL = reg_addr; 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) { reg_read(reg_addr); unsigned char bit_mask = 1 << (bit & 0x3); switch (bit >> 3) { case 0: SFR_DATA_0 |= bit_mask; break; case 1: SFR_DATA_8 |= bit_mask; break; case 2: SFR_DATA_16 |= bit_mask; break; case 3: SFR_DATA_24 |= bit_mask; break; } reg_write(reg_addr); } /* This sets a bit in the 32bit wide switch register reg_addr */ void reg_bit_clear(uint16_t reg_addr, char bit) { reg_read(reg_addr); unsigned char bit_mask = 1 << (bit & 0x3); bit_mask = ~bit_mask; switch (bit >> 3) { case 0: SFR_DATA_0 &= bit_mask; break; case 1: SFR_DATA_8 &= bit_mask; break; case 2: SFR_DATA_16 &= bit_mask; break; case 3: SFR_DATA_24 &= bit_mask; break; } reg_write(reg_addr); } /* 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. */ unsigned char read_flash(unsigned char bank, __code unsigned char *addr) { unsigned char v; unsigned char current_bank = SFR_BANK; SFR_BANK = bank; v = *addr; SFR_BANK = current_bank; return v; } void reset_chip(void) { SFR_DATA_24 = 0x00; SFR_DATA_16 = 0x00; SFR_DATA_8 = 0x00; SFR_DATA_0 = 0x01; reg_write(RTL837X_REG_RESET); } void setup_external_irqs(void) { SFR_DATA_24 = 0x00; SFR_DATA_16 = 0x00; SFR_DATA_8 = 0x00; SFR_DATA_0 = 0x42; reg_write(0x5f84); SFR_DATA_8 = 0x03; SFR_DATA_0 = 0xff; reg_write(0x5f34); EX0 = 1; // Enable external IRQ 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 reset_rtl8224(void) { /* Toggle reset pin on RTL8224 on RTL8373 */ reg_bit_clear(0x40, 4); reg_bit_set(0x50, 4); reg_bit_set(0x40, 4); } /* * Set dividers for a chosen CPU frequency */ void setup_clock(void) { reg_read(0x6040); SFR_DATA_0 &= ~0x30; #if CLOCK_DIV != 0 SFR_DATA_0 |= CLOCK_DIV << 4; // Divider in bits 4 & 5 #endif SFR_DATA_8 |= 0x01; // This is set in managed mode 125MHz reg_write(0x6040); reg_read(0x7f90); SFR_DATA_0 &= 0xfd; SFR_DATA_0 |= 0x01; reg_write(0x7f90); } void print_sfr_data(void) { write_char('0'); write_char('x'); write_char(hex[SFR_DATA_24 >> 4]); write_char(hex[SFR_DATA_24 & 0xf]); write_char(hex[SFR_DATA_16 >> 4]); write_char(hex[SFR_DATA_16 & 0xf]); 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]); } /* * Write a register reg of phy phy_id, in page page * Data to be written must be in SFR a6/a7 */ void phy_write(unsigned char phy_mask, unsigned char dev_id, unsigned short reg, unsigned short v) { SFR_DATA_8 = v >> 8; SFR_DATA_0 = v; SFR_SMI_PHYMASK = phy_mask; SFR_SMI_REG_H = reg >> 8; SFR_SMI_REG_L = reg; SFR_SMI_DEV = dev_id << 3 | 2; // SFR_EXEC_GO = SFR_EXEC_WRITE_SMI; do { } while (SFR_EXEC_STATUS != 0); } void rtl8372_init(void) { // From run, set bits 0-1 to 1 print_string("rtl8372_init called\r\n"); reg_read(0x7f90); SFR_DATA_0 &= 0xfc; SFR_DATA_0 |= 0x01; reg_write(0x7f90); reg_read(0x6330); SFR_DATA_0 |= 0xc0; // Set Bits 6, 7 SFR_DATA_16 &= 0xfc; // Delete bits 16, 17 reg_write(0x6330); reg_read(0x6334); // Also in sdsMode_set SFR_DATA_8 |= 0x01; // Set bits 3-8, On RTL8373+8224 set bits 0-7 SFR_DATA_0 |= 0xf8; reg_write(0x6334); // Enable MDC reg_read(RTL837X_REG_SMI_CTRL); SFR_DATA_8 |= 0x70; // Set bits 0xc-0xe to enable MDC for SMI0-SMI2 reg_write(RTL837X_REG_SMI_CTRL); /* FUN_CODE_2023(); FUN_CODE_01a6(); */ reg_read(RTL837X_REG_LED_MODE); // SFR_DATA_24 = 0x00; SFR_DATA_16 &= 0x1f; // Mask blink rate field (0xe0) SFR_DATA_16 |= 0x23; // Set blink rate and LED to solid (set bit 1 = bit 17 overall) // Configure led-mode (serial?) SFR_DATA_8 &= 0xe3; // 0xe0; SFR_DATA_8 |= 0x0c; SFR_DATA_0 &= 0x1f; // 0xf0; SFR_DATA_0 |= 0xe0; reg_write(RTL837X_REG_LED_MODE); /* BEFORE: 0x0021fdb0 AFTER: 0x0021fdf0 CORRECT:0x0023e0f0; rVar1 = rtl8373_setAsicRegBits(0x6520,0xe0 0000,rate); write_xmem_32((uint *)0xb1,0xc); write_xmem_32((uint *)0xb5,9); write_xmem_32((uint *)0xb9,3); rtl8373_setAsicRegBits_call_b1(0x6520); write_xmem_32((uint *)0xb1,8); write_xmem_32((uint *)0xb5,5); write_xmem_32((uint *)0xb9,5); rtl8373_setAsicRegBits_call_b1(0x6520); // LED Enable */ // Clear bits 0,1 of 0x65f8 reg_read(0x65f8); SFR_DATA_0 &= 0xfc; reg_write(0x65f8); // Set 0x65fc to 0xfffff000 SFR_DATA_24 = 0xff; SFR_DATA_16 = 0xff; SFR_DATA_8 = 0xf0; SFR_DATA_0 = 0x00; reg_write(0x65fc); // Set bits 0-3 of 0x6600 to 0xf reg_read(0x6600); SFR_DATA_0 |= 0x0f; reg_write(0x6600); // Set bit 0x1d of 0x65dc, clear bit 1b reg_bit_set(0x65dc, 0x1d); reg_bit_clear(0x65dc, 0x1b); // Set bits 1b/1d of 0x7f8c reg_bit_set(0x7f8c, 0x1d); reg_bit_set(0x7f8c, 0x1b); // Configure LED_SET_0, ledid 0/1 SFR_DATA_24 = 0x00; SFR_DATA_16 = 0x41; SFR_DATA_8 = 0x01; SFR_DATA_0 = 0x75; reg_write(0x6548); // Configure LED_SET_0 ledid 2 reg_read(0x6544); SFR_DATA_8 = 0x00; SFR_DATA_0 = 0x44; reg_write(0x6544); // Further configure LED_SET_0 reg_read(0x6528); SFR_DATA_0 = 0x11; reg_write(0x6528); // Part of the SDS configuration, see sdsMode_set, set bits 0xa-0xe to 0 reg_read(0x6450); SFR_DATA_8 &= 0x83; reg_write(0x6450); // SDS bits 10-1f set to 0 reg_read(0x644c); SFR_DATA_8 &= 0xe0; reg_write(0x644c); /* FUN_CODE_018d(0,1,0,8); FUN_CODE_018d(0,1,0,3); */ // Set the SerDes mode. Bits 0-4: SDS 0, Bits 5-9: SDS 1. Bits set to 1f reg_read(0x7b20); SFR_DATA_8 |= 0x03; SFR_DATA_0 = 0xff; reg_write(0x7b20); /* calll_4464_bank1(); calll_4464_bank1(); calll_4464_bank1(); */ /* FUN_CODE_2023(); phy_setting_up_somehow(); FUN_CODE_2023(); phy_setting_up_somehow(); */ reg_read(0xa90); SFR_DATA_0 &= 0xf0; SFR_DATA_0 |= 0xc; reg_write(0xa90); // Disable PHYs for configuration phy_write(0xf0,0x1f,0xa610,0x2858); // Set bits 0x13 and 0x14 of 0x5fd4 reg_bit_set(0x5fd4, 0x13); reg_bit_set(0x5fd4, 0x14); // Configure ports 3-8: uint16_t reg = 0x1238 + 0x300; // Port base register for the bits we set for (char i = 0; i < 6; i++) { reg_bit_set(reg, 0x2); reg_bit_set(reg, 0x8); reg_bit_set(reg, 0x8); reg += 0x100; } reg_bit_set(0xb7c, 5); reg_bit_set(0x6040, 0); /* FUN_CODE_2023(); uVar4 = 1; FUN_CODE_0617(0x23f); bVar7 = eql_l(a,uVar4); if (bVar7 == 0) { FUN_CODE_019c(); FUN_CODE_01a1(); } else { uVar4 = 2; FUN_CODE_0617(0x23f); bVar7 = eql_l(a,uVar4); if (bVar7 == 0) { FUN_CODE_0192(); FUN_CODE_0197(); } } */ /* FUN_CODE_01bf(); FUN_CODE_01ab(); FUN_CODE_01b0(); FUN_CODE_01ba(); FUN_CODE_01b5(); */ // Re-enable PHY after configuration phy_write(0xf0,0x1f,0xa610,0x2058);; // Set bits 0xc-0x14 of 0x632c to 0x1f8, see rtl8372_init reg_read(0x632c); SFR_DATA_8 &= 0x8f; SFR_DATA_8 |= 0x80; SFR_DATA_16 &= 0xe0; SFR_DATA_16 |= 0x1f; reg_write(0x632c); } /* * 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(unsigned char phy_id, unsigned char device, unsigned short reg) { SFR_SMI_PHY = phy_id; SFR_SMI_REG_H = reg >> 8; SFR_SMI_REG_L = reg; SFR_SMI_DEV = device << 3 | 2; SFR_EXEC_GO = SFR_EXEC_READ_SMI; do { } while (SFR_EXEC_STATUS != 0); } void led_enable(void) { reg_read(RTL837X_REG_LED_MODE); SFR_DATA_24 = 0x00; SFR_DATA_16 = 0x23; SFR_DATA_8 = 0xe0; SFR_DATA_0 = 0xf0; // SFR_DATA_0 &= 0xe0; // SFR_DATA_8 &= 0x1f; // SFR_DATA_0 |= 0xe0; // SFR_DATA_8 |= 0x06; reg_write(RTL837X_REG_LED_MODE); } void led_disable(void) { SFR_DATA_24 = 0x00; SFR_DATA_16 = 0x00; SFR_DATA_8 = 0x00; SFR_DATA_0 = 0x00; reg_write(RTL837X_REG_LED_MODE); } void port_leds_on(void) { reg_read(0x6528); SFR_DATA_0 = 0x11; reg_write(0x6528); } /* Set up serial port 0 using Timer 2 with an external trigger * as baud generator. * The external clock generator uses a crystal at 25MHz. */ void setup_serial(void) { IE = 0; T2CON = 0x34; // Enable RCLK/TCLK (serial transmit/receive clock for T2), TR2 (Timer 2 RUN), disable CP/RL2 (bit 0) SCON = 0x50; // Mode = 1: ASYNC 8N1 with T2 as baud-rate generator, REN_0 Receive enable // The RCAP2 registers contain the high/low byte that is loaded into // timer2 when T2 overflows to 0x10000 RCAP2H = (0x10000 - (CLOCK_HZ / SERIAL_BAUD_RATE / 32)) >> 8; RCAP2L = (0x10000 - (CLOCK_HZ / SERIAL_BAUD_RATE / 32)) % 0xff; PCON |= 0x80; // Double the Baud Rate SCON = 0x50; TI = 1; RI = 0; ES = 1; // Enable serial IRQ } void print_reg(unsigned short reg) { reg_read(reg); print_sfr_data(); } void print_phy_reg(unsigned char phy_id, unsigned char device, unsigned short reg) { phy_read(phy_id, device, reg); SFR_DATA_16 = SFR_DATA_24 = 0; print_sfr_data(); } __code struct command commands[N_COMMANDS] = { { "reset", 1 }, }; unsigned char cmd_compare(unsigned char start, unsigned char * __code cmd) { signed char i; signed char j = 0; for (i = cmd_words_b[start]; i < cmd_words_b[start + 1] && sbuf[i] != ' '; i++) { // print_short(i); write_char(':'); print_short(j); write_char('#'); print_string("\r\n"); // write_char('>'); write_char(cmd[j]); write_char('-'); write_char(sbuf[i]); print_string("\r\n"); if (!cmd[j]) return 1; if (sbuf[i] != cmd[j++]) break; } // write_char('.'); print_short(i); write_char(':'); print_short(i); if (i >= cmd_words_b[start + 1] || sbuf[i] == ' ') return 1; return 0; } void bootloader(void) { ticks = 0; sbuf_ptr = 0; CKCON = 0; SFR_97 = 0; // 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 // Disable all interrupts (global interrupt enable bit) EA = 0; // SFR A8.7 / IE.7 // HW setup, serial, timer, external IRQs setup_clock(); setup_serial(); setup_timer0(); setup_external_irqs(); EA = 1; // Enable all IRQs // port_leds_on(); print_string("\r\nStarting up...\r\n"); print_string(" Flash controller\r\n"); flash_init(); print_string(" > OK\r\n current status: "); print_short(flash_read_status()); // The following will only show something else then 0xff if it was programmed for a managed switch print_string("\r\n Testing read Securty Register 1\r\n"); flash_read_security(0x0001000, 40); print_string("\r\n Testing read Securty Register 2\r\n"); flash_read_security(0x0002000, 40); print_string("\r\n Testing read Securty Register 3\r\n"); flash_read_security(0x0003000, 40); print_string(" > Flash status: "); print_short(flash_read_status()); print_string("\r\n Dumping flash at 0x100\r\n"); flash_dump(0x100, 252); print_string("\r\nREADING PHY 0x1, 0x4, 0: "); print_phy_reg(0x1, 0x4, 0); rtl8372_init(); print_string(greeting); print_string("READING PHY 0x4, 0x1, 0: "); print_phy_reg(0x4, 0x1, 0); print_string("\r\nREADING PHY 0x1, 0x4, 0: "); print_phy_reg(0x1, 0x4, 0); print_string("\r\nREADING PHY 0x5, 0x1, 1: "); print_phy_reg(0x5, 0x1, 1); print_string("\r\nREADING PHY 0x1, 0x1, 0: "); print_phy_reg(0x1, 0x1, 0); print_string("\r\nREADING PHY 0x7, 0x1f, 0xa412:"); print_phy_reg(0x7, 0x1f, 0xa412); print_string("\r\nCPU version: "); print_reg(0x4); print_string("\r\nClock register: "); print_reg(0x6040); print_string("\r\n> "); char l = sbuf_ptr; char line_ptr = l; char is_white = 1; while (1) { while (l != sbuf_ptr) { write_char(sbuf[l]); // Check whether there is a full line: if (sbuf[l] == '\n' || sbuf[l] == '\r') { print_short(ticks); // Print line and parse command into words print_string("\r\n CMD: "); is_white = 1; unsigned char word = 0; cmd_words_b[0] = -1; while (line_ptr != l) { if (is_white && sbuf[line_ptr] != ' ') { is_white = 0; cmd_words_b[word++] = line_ptr; } if (sbuf[line_ptr] == ' ') is_white = 1; write_char(sbuf[line_ptr++]); line_ptr &= SBUF_SIZE - 1; } cmd_words_b[word++] = line_ptr; cmd_words_b[word++] = -1; line_ptr = (l + 1) & (SBUF_SIZE - 1); // Identify command signed char i = cmd_words_b[0]; if (i >= 0 && cmd_words_b[1] >= 0) { // print_string("\r\n THERE may be a command: "); // print_short(i); print_string("\r\n"); // print_short(cmd_words_b[0]); print_string("\r\n"); // print_short(cmd_words_b[1]); print_string("\r\n"); // print_short(cmd_words_b[2]); print_string("\r\n"); // print_short(cmd_words_b[3]); print_string("\r\n"); if (cmd_compare(0, "reset")) { print_string("\r\nRESET\n\n"); reset_chip(); } if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && sbuf[cmd_words_b[1]] == 'd') { print_string("\r\nDUMPING FLASH\n\n"); flash_dump(0, 255); } if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && sbuf[cmd_words_b[1]] == 'j') { print_string("\r\nJEDEC ID\n\n"); flash_read_jedecid(); } if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && sbuf[cmd_words_b[1]] == 'u') { print_string("\r\nUNIQUE ID\n\n"); flash_read_uid(); } // Switch to flash 62.5 MHz mode if (cmd_compare(0, "flash") && cmd_words_b[1] > 0 && sbuf[cmd_words_b[1]] == 's') { print_string("\r\nFLASH FAST MODE\n\n"); flash_init_fast(); print_string("\r\nNow dumping flash\n\n"); flash_dump_fast(0, 255); } } print_string("\r\n> "); } l++; l &= (SBUF_SIZE - 1); } PCON |= 1; // Enter Idle mode until interrupt occurs } }