diff --git a/Makefile b/Makefile index 6f369d78..4b0190d2 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,8 @@ SRCS = \ dhcp.c \ html_data.c \ rtlplayground.c \ + boot.c \ + sfp.c \ syslog.c \ udp_apps.c @@ -132,7 +134,7 @@ $(BUILDDIR)/%.rel: %.asm | create_build_dir # mv -f $(addprefix $(basename $^), .lst .rel .sym) . $(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel - $(CC) $(CC_FLAGS) -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-r -o $@ $^ + $(CC) $(CC_FLAGS) -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-r -o $@ $^ $(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx objcopy --input-target=ihex -O binary $< $@ diff --git a/boot.c b/boot.c new file mode 100644 index 00000000..c72e24ed --- /dev/null +++ b/boot.c @@ -0,0 +1,480 @@ +#pragma codeseg BANK3 +#pragma constseg BANK3 + +#include <8051.h> +#include +#include + +#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_init.h" +#include "machine.h" +#include "phy.h" +#include "boot.h" + +extern __code const struct machine machine; +extern __xdata struct machine_runtime machine_detected; +extern __xdata uint8_t sfr_data[4]; +extern volatile __xdata uint32_t ticks; +extern __xdata uint8_t tx_seq; +extern __code uint8_t * __code hex; + + + +void sds_config_mac(uint8_t sds, uint8_t mode) __banked +{ + 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"); +} + + + +void early_boot_handle_button(void) __banked +{ + if (machine.reset_pin == GPIO_NA) + return; + + gpio_input_setup(machine.reset_pin); + + // Debounce after init + delay(100); + // If the button is not already held at boot, continue normally. + if (gpio_pin_test(machine.reset_pin)) + return; + + set_sys_led_state(SYS_LED_FAST); + print_string("\n[Reset button held at boot]\n"); + + if (gpio_pin_test(machine.reset_pin)) + return; + + const __xdata uint32_t min_hold_ticks = 10UL * SYS_TICK_HZ; + const __xdata uint32_t max_hold_ticks = 30UL * SYS_TICK_HZ; + const __xdata uint32_t blink_ticks = SYS_TICK_HZ / 10; // 100 ms + const __xdata uint32_t pause_ticks = SYS_TICK_HZ / 2; // 500 ms + __xdata uint32_t start_ticks = ticks; + __xdata uint32_t last_blink_step = start_ticks; + __xdata uint8_t blink_step = 0; + + set_sys_led_state(SYS_LED_ON); + + while (!gpio_pin_test(machine.reset_pin)) { + __xdata uint32_t held_ticks = ticks - start_ticks; + + if (held_ticks > max_hold_ticks) { + print_string("[Button held >30s at boot; continuing normal boot]\n"); + return; + } + + // Double blink pattern while button is held: + // ON (100ms), OFF (100ms), ON (100ms), OFF (500ms) + __xdata uint32_t step_ticks = (blink_step == 3) ? pause_ticks : blink_ticks; + if ((ticks - last_blink_step) >= step_ticks) { + blink_step = (blink_step + 1) & 0x3; + set_sys_led_state((blink_step == 0 || blink_step == 2) ? SYS_LED_ON : SYS_LED_OFF); + last_blink_step = ticks; + } + + PCON |= 1; + } + + set_sys_led_state(SYS_LED_ON); + + if ((ticks - start_ticks) >= min_hold_ticks) { + print_string("[Button held 10s-30s at boot; restoring default config]\n"); + set_sys_led_state(SYS_LED_FAST); + flash_default_config(); + delay(3UL * SYS_TICK_HZ); + } +} + + +/* + * 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 + * SDS_QXGMII is used for 10G Fiber, RTL8224 and RTL8261BE + */ +void sds_config(uint8_t sds, uint8_t mode) __banked +{ + 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) + 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; + case SDS_100FX: + v = 0x0200; + page = 0x26; + 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_QXGMII + 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 + + // RTL8261BE + if (machine.n_10g && mode == SDS_QXGMII) { + sds_write_v(sds, 0x06, 0x1f, 0x2100); // Q00061f:2100 + sds_write_v(sds, 0x07, 0x11, 0x054f); // Q000711:054f + sds_write_v(sds, 0x20, 0x00, 0x0030); // Q002000:0030 + sds_write_v(sds, 0x20, 0x00, 0x0010); // Q002000:0010 + sds_write_v(sds, 0x20, 0x00, 0x0050); // Q002000:0050 + sds_write_v(sds, 0x20, 0x00, 0x00d0); // Q002000:00d0 + sds_write_v(sds, 0x20, 0x00, 0x0cd0); // Q002000:0cd0 + sds_write_v(sds, 0x20, 0x00, 0x04d0); // Q002000:04d0 + sds_write_v(sds, 0x20, 0x00, 0x04d0); // Q002000:04d0 + sds_write_v(sds, 0x20, 0x00, 0x0cd0); // Q002000:0cd0 + sds_write_v(sds, 0x20, 0x00, 0x00d0); // Q002000:00d0 + sds_write_v(sds, 0x20, 0x00, 0x00d0); // Q002000:00d0 + sds_write_v(sds, 0x20, 0x00, 0x0050); // Q002000:0050 + sds_write_v(sds, 0x20, 0x00, 0x0010); // Q002000:0010 + sds_write_v(sds, 0x20, 0x00, 0x0010); // Q002000:0010 + sds_write_v(sds, 0x20, 0x00, 0x0030); // Q002000:0030 + sds_write_v(sds, 0x20, 0x00, 0x0000); // Q002000:0000 + sds_write_v(sds, 0x1f, 0x00, 0x000b); // Q001f00:000b + sds_write_v(sds, 0x1f, 0x00, 0x0000); // Q001f00:0000 + return; + } + if (mode != SDS_QXGMII) + sds_write_v(sds, 0x06, 0x1f, 0x2100); // Q00061f:2100 + + if (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 + } +} + + + + +void rtl8224_enable(void) __banked +{ + // 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); +} + + + +void nic_setup(void) __banked +{ + // 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; +} + + + +void rtl8373_revision(void) __banked +{ + 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); +} + + +/* + * 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) __banked +{ + 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 + */ + + // Default: 0x00005555 + // Workaround for SDCC BUG 4070: SFR_DATA_U32 = 0x00005555; + SFR_DATA_U16_UPPER = 0x0000; + SFR_DATA_U16 = 0x5555; + if (machine.n_10g == 2) { + // 0x00015555, only change the bytes that differs from the default. + SFR_DATA_16 = 0x01; + } else if (machine.n_sfp == 2) + // 0x00005515 + SFR_DATA_0 = 0x15; + reg_write(RTL837X_REG_SMI_MAC_TYPE); + + // Configure polling of all PHYs by the MAC to detect link-state changes + // Default: 0x000000ff + // Workaround for SDCC BUG 4070: SFR_DATA_U32 = 0x000000ff; + SFR_DATA_U16_UPPER = 0x0000; + SFR_DATA_U16 = 0x00ff; + if (!machine_detected.isRTL8373) { + if (machine.n_sfp == 2) { + // 0x000000f0, only change the bytes that differs from the default. + SFR_DATA_0 = 0xf0; + } else { + // 0x000001f8, only change the bytes that differs from the default. + SFR_DATA_8 = 0x01; + SFR_DATA_0 = 0xf8; + } + } + reg_write(RTL837X_REG_SMI_PORT_POLLING); + // 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); + } + + if (machine.n_10g == 2) { + // Set address of second external PHY on port 8 + REG_SET(RTL837X_REG_SMI_PORT6_9_ADDR, 0x000040e6); + } +} + + + + +void setup_i2c(void) __banked +{ + 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); +} + + +/* Give the switch a name carrying the tail of its MAC, so several of them on + * one network are distinguishable out of the box. Called after the startup + * config has been replayed and returns at once if that config already set a + * name, so a configured switch does no work for it (suggested in review). + * + * Written without a loop on purpose. Locals - counters and pointers alike - + * land in the 8051's internal-RAM overlay, and on an image with LACP and STP + * both enabled that overlay is exhausted: a loop here makes the linker fail + * with "Could not get 8 consecutive bytes in internal RAM for area OSEG". + * Moving the code into its own function does not help; the overlay is shared + * across the whole image. Hoisting the locals to xdata does not help either, + * because itohex() is inline and brings its own frame. */ +void set_hostname_default(void) __banked +{ + if (hostname[0] != NUL) + return; + + strcpy((__xdata uint8_t *)hostname, "RTLPlayground-"); + hostname[14] = hex[uip_ethaddr.addr[3] >> 4]; + hostname[15] = hex[uip_ethaddr.addr[3] & 0xf]; + hostname[16] = hex[uip_ethaddr.addr[4] >> 4]; + hostname[17] = hex[uip_ethaddr.addr[4] & 0xf]; + hostname[18] = hex[uip_ethaddr.addr[5] >> 4]; + hostname[19] = hex[uip_ethaddr.addr[5] & 0xf]; + hostname[20] = NUL; +} diff --git a/boot.h b/boot.h new file mode 100644 index 00000000..18ffe88c --- /dev/null +++ b/boot.h @@ -0,0 +1,14 @@ +#ifndef BOOT_H +#define BOOT_H + +void sds_config_mac(uint8_t sds, uint8_t mode) __banked; +void sds_config(uint8_t sds, uint8_t mode) __banked; +void early_boot_handle_button(void) __banked; +void rtl8224_enable(void) __banked; +void nic_setup(void) __banked; +void rtl8373_revision(void) __banked; +void init_smi(void) __banked; +void setup_i2c(void) __banked; +void set_hostname_default(void) __banked; + +#endif diff --git a/cmd_parser.c b/cmd_parser.c index 5b769a24..5a3a3d60 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -14,6 +14,7 @@ #include "rtl837x_stp.h" #include "rtl837x_igmp.h" #include "rtl837x_bandwidth.h" +#include "sfp.h" #include "dhcp.h" #include "syslog.h" #include "uip/uip.h" @@ -49,9 +50,6 @@ __xdata char port_names[9][PORT_NAME_SIZE]; extern __xdata uint16_t management_vlan; extern __xdata struct uip_eth_addr uip_ethaddr; -extern __xdata uint8_t sfp_speed[2]; -extern __xdata uint8_t sfp_pins_last; -extern __xdata uint8_t sfp_options[2]; __xdata uint8_t gpio_last_value[8] = { 0 }; // Temporatly for str to hex convertion value. diff --git a/rtl837x_common.h b/rtl837x_common.h index e4b90fed..ee1c61bb 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -185,12 +185,9 @@ void tcpip_output(void); uint8_t read_flash(uint8_t bank, __code uint8_t *addr); void get_random_32(void); void read_reg_timer(__xdata uint32_t * tmr); -bool sfp_print_info(uint8_t sfp); bool gpio_pin_test(uint8_t pin); void set_sys_led_state(uint8_t state); void sds_read(uint8_t sds_id, uint8_t page, uint8_t reg); void sds_write_v(uint8_t sds_id, uint8_t page, uint8_t reg, uint16_t v); -void sds_config_mac(uint8_t sds, uint8_t mode); -void sds_config(uint8_t sds, uint8_t mode); -void handle_sfp(void); +void flash_default_config(void); #endif diff --git a/rtl837x_init.c b/rtl837x_init.c index 50d94f60..6b24843b 100644 --- a/rtl837x_init.c +++ b/rtl837x_init.c @@ -6,6 +6,7 @@ #include "rtl837x_phy.h" #include "phy.h" #include "machine.h" +#include "boot.h" extern __xdata uint8_t sfr_data[4]; extern __code struct machine machine; diff --git a/rtlplayground.c b/rtlplayground.c index 6ec1cf06..a2efbf5a 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -26,6 +26,8 @@ #include "phy.h" #include "syslog.h" #include "httpd/page_impl.h" +#include "boot.h" +#include "sfp.h" extern __code const struct machine machine; extern __xdata uint32_t flash_size; @@ -33,8 +35,6 @@ extern __xdata uint32_t flash_size; extern __xdata uint16_t crc_value; __xdata struct machine_runtime machine_detected; void crc16_bank1(__xdata uint8_t *v) __naked; -void flash_default_config(void); -void early_boot_handle_button(void); // See setup_serial_timer1() for valid baudrate settings! #define SERIAL_BAUD_RATE 115200 @@ -135,33 +135,10 @@ __code uint16_t bit_mask[16] = { __xdata uint8_t linkbits_last[4]; __xdata uint8_t linkbits_last_p89; // Last known state of the SFP detection/Loss of Signal pins -// SFP1 b0 = 1 => module missing, b1 = 1 => LOS; -// SFP2 b4 = 1 => module missing, b5 = 1 => LOS; -__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 uint8_t sfp_buf[16]; /* scratch for one I2C transaction, the controller reads at most 16 bytes */ -__xdata uint8_t sfp_speed[2]; -__xdata uint8_t sfp_quirks[2]; __xdata bool button_last; __xdata uint8_t button_sec_counter_last; volatile __bit tx_buf_empty; -__code enum sfp_quirk { - SFP_QUIRK_DDM = (1 << 0), -}; - -struct sfp_quirk_entry { - __code char *vendor; // Set vendor or model to 0 to act as wildcard - __code char *model; - uint8_t quirks; -}; - -static __code struct sfp_quirk_entry sfp_quirk_table[] = { - { "QSFPTEK", "QT-SFP+-T", SFP_QUIRK_DDM }, -}; struct eth_in { struct uip_eth_addr dst; @@ -944,33 +921,6 @@ void read_reg_timer(__xdata uint32_t * tmr) } -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) { @@ -979,181 +929,6 @@ void delay(uint16_t t) PCON |= 1; } -void early_boot_handle_button(void) -{ - if (machine.reset_pin == GPIO_NA) - return; - - gpio_input_setup(machine.reset_pin); - - // Debounce after init - delay(100); - // If the button is not already held at boot, continue normally. - if (gpio_pin_test(machine.reset_pin)) - return; - - set_sys_led_state(SYS_LED_FAST); - print_string("\n[Reset button held at boot]\n"); - - if (gpio_pin_test(machine.reset_pin)) - return; - - const __xdata uint32_t min_hold_ticks = 10UL * SYS_TICK_HZ; - const __xdata uint32_t max_hold_ticks = 30UL * SYS_TICK_HZ; - const __xdata uint32_t blink_ticks = SYS_TICK_HZ / 10; // 100 ms - const __xdata uint32_t pause_ticks = SYS_TICK_HZ / 2; // 500 ms - __xdata uint32_t start_ticks = ticks; - __xdata uint32_t last_blink_step = start_ticks; - __xdata uint8_t blink_step = 0; - - set_sys_led_state(SYS_LED_ON); - - while (!gpio_pin_test(machine.reset_pin)) { - __xdata uint32_t held_ticks = ticks - start_ticks; - - if (held_ticks > max_hold_ticks) { - print_string("[Button held >30s at boot; continuing normal boot]\n"); - return; - } - - // Double blink pattern while button is held: - // ON (100ms), OFF (100ms), ON (100ms), OFF (500ms) - __xdata uint32_t step_ticks = (blink_step == 3) ? pause_ticks : blink_ticks; - if ((ticks - last_blink_step) >= step_ticks) { - blink_step = (blink_step + 1) & 0x3; - set_sys_led_state((blink_step == 0 || blink_step == 2) ? SYS_LED_ON : SYS_LED_OFF); - last_blink_step = ticks; - } - - PCON |= 1; - } - - set_sys_led_state(SYS_LED_ON); - - if ((ticks - start_ticks) >= min_hold_ticks) { - print_string("[Button held 10s-30s at boot; restoring default config]\n"); - set_sys_led_state(SYS_LED_FAST); - flash_default_config(); - delay(3UL * SYS_TICK_HZ); - } -} - -/* - * 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 - * SDS_QXGMII is used for 10G Fiber, RTL8224 and RTL8261BE - */ -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) - 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; - case SDS_100FX: - v = 0x0200; - page = 0x26; - 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_QXGMII - 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 - - // RTL8261BE - if (machine.n_10g && mode == SDS_QXGMII) { - sds_write_v(sds, 0x06, 0x1f, 0x2100); // Q00061f:2100 - sds_write_v(sds, 0x07, 0x11, 0x054f); // Q000711:054f - sds_write_v(sds, 0x20, 0x00, 0x0030); // Q002000:0030 - sds_write_v(sds, 0x20, 0x00, 0x0010); // Q002000:0010 - sds_write_v(sds, 0x20, 0x00, 0x0050); // Q002000:0050 - sds_write_v(sds, 0x20, 0x00, 0x00d0); // Q002000:00d0 - sds_write_v(sds, 0x20, 0x00, 0x0cd0); // Q002000:0cd0 - sds_write_v(sds, 0x20, 0x00, 0x04d0); // Q002000:04d0 - sds_write_v(sds, 0x20, 0x00, 0x04d0); // Q002000:04d0 - sds_write_v(sds, 0x20, 0x00, 0x0cd0); // Q002000:0cd0 - sds_write_v(sds, 0x20, 0x00, 0x00d0); // Q002000:00d0 - sds_write_v(sds, 0x20, 0x00, 0x00d0); // Q002000:00d0 - sds_write_v(sds, 0x20, 0x00, 0x0050); // Q002000:0050 - sds_write_v(sds, 0x20, 0x00, 0x0010); // Q002000:0010 - sds_write_v(sds, 0x20, 0x00, 0x0010); // Q002000:0010 - sds_write_v(sds, 0x20, 0x00, 0x0030); // Q002000:0030 - sds_write_v(sds, 0x20, 0x00, 0x0000); // Q002000:0000 - sds_write_v(sds, 0x1f, 0x00, 0x000b); // Q001f00:000b - sds_write_v(sds, 0x1f, 0x00, 0x0000); // Q001f00:0000 - return; - } - if (mode != SDS_QXGMII) - sds_write_v(sds, 0x06, 0x1f, 0x2100); // Q00061f:2100 - - if (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 - } -} /* @@ -1303,85 +1078,6 @@ void handle_tx(void) } -static inline uint8_t sfp_rate_to_sds_config(uint8_t rate) -{ - if (rate == 0x1 || rate == 0x2) - return SDS_100FX; - if (rate == 0xc || rate == 0xd) - return SDS_1000BX_FIBER; - if (rate >= 0x19 && rate <= 0x20) // Ethernet 2.5 GBit - return SDS_HSG; - if (rate >= 0x62 && rate < 0x70) - return SDS_10GR; - return 0xff; -} - - -bool 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 = 16; i < 64; i++) { - if (!(i & 0xf) && !sfp_read_block(sfp, i, 16)) - return false; - if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes - continue; - uint8_t c = sfp_buf[i & 0xf]; - if (c) - write_char(c); - } - print_string("\n"); - - return true; -} - -// Normalize strings from EEPROM by removing any trailing spaces; this allows simpler comparisons -bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __reentrant -{ - if (!sfp_read_block(sfp, start, length)) - return false; - - dst[length] = NUL; - memcpy(dst, sfp_buf, length); - - while (length > 0 && dst[--length] == ' ') - dst[length] = NUL; - - return true; -} - -bool sfp_get_info(uint8_t sfp) -{ - if (!sfp_read_field(sfp_module_vendor[sfp], sfp, 20, 16)) - return false; - if (!sfp_read_field(sfp_module_model[sfp], sfp, 40, 16)) - return false; - - return sfp_read_field(sfp_module_serial[sfp], sfp, 68, 16); -} - -void sfp_apply_quirks(uint8_t sfp) __reentrant -{ - sfp_quirks[sfp] = 0; - - for (uint8_t i = 0; i < sizeof(sfp_quirk_table) / sizeof(*sfp_quirk_table); i++) { - if (!sfp_quirk_table[i].vendor || !strcmp(sfp_module_vendor[sfp], sfp_quirk_table[i].vendor)) { - if (!sfp_quirk_table[i].model || !strcmp(sfp_module_model[sfp], sfp_quirk_table[i].model)) { - sfp_quirks[sfp] |= sfp_quirk_table[i].quirks; - } - } - } - - if (sfp_quirks[sfp] & SFP_QUIRK_DDM) { - if (!(sfp_options[sfp] & 0x40)) { - // The module reports that DDM is not implemented, but try a dummy read to confirm - // 0xff would mean a failed I2C read or an impossible (per spec) voltage greater than 6.5V - if (sfp_read_block(sfp, 226, 1) && sfp_buf[0] != 0xff) { - sfp_options[sfp] |= 0x40; - } - } - } -} - bool gpio_pin_test(uint8_t pin) { @@ -1389,87 +1085,6 @@ bool gpio_pin_test(uint8_t pin) 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); - } -} - -static bool sfp_module_read(uint8_t sfp) -{ - uint8_t rate; - - // 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 - if (!sfp_read_block(sfp, 11, 2)) - return false; - - rate = sfp_buf[1]; - if (sfp_speed[sfp] == SFP_SPEED_100M) - rate = 0x1; - else if (sfp_speed[sfp] == SFP_SPEED_1G) - rate = 0xc; - else if (sfp_speed[sfp] == SFP_SPEED_2G5) - rate = 0x19; - else if (sfp_speed[sfp] == SFP_SPEED_10G) - rate = 0x69; - print_string(" Rate: "); print_byte(rate); // Normally 1, but 0 for DAC, can be ignored? - print_string(" Encoding: "); print_byte(sfp_buf[0]); - print_string(" Module: "); - if (!sfp_print_info(sfp)) - return false; - print_string("\n"); - - if (!sfp_read_block(sfp, 92, 1)) - return false; - sfp_options[sfp] = sfp_buf[0]; - if (!sfp_get_info(sfp)) - return false; - - sfp_apply_quirks(sfp); - sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate)); - - return true; -} - - -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 Slot: "); write_char('1' + sfp); - if (!sfp_module_read(sfp)) { - print_string("SFP: an I2C read failed, retrying on the next poll\n"); - sfp_pins_last |= 0x01 << (sfp << 2); - } - } - } else { - if (!(sfp_pins_last & (0x1 << (sfp << 2)))) { - sfp_pins_last |= 0x01 << (sfp << 2); - print_string("\n 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 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 Slot: "); write_char('1' + sfp); write_char('\n'); - } - } - } -} void flash_default_config(void) { @@ -1681,19 +1296,6 @@ void setup_external_irqs(void) } -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 */ @@ -1816,55 +1418,6 @@ void phy_modify(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t mask, uin } 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; -} - void set_sys_led_state(uint8_t state) { @@ -1873,88 +1426,7 @@ void set_sys_led_state(uint8_t 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); -} - - -/* - * 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 - */ - - // Default: 0x00005555 - // Workaround for SDCC BUG 4070: SFR_DATA_U32 = 0x00005555; - SFR_DATA_U16_UPPER = 0x0000; - SFR_DATA_U16 = 0x5555; - if (machine.n_10g == 2) { - // 0x00015555, only change the bytes that differs from the default. - SFR_DATA_16 = 0x01; - } else if (machine.n_sfp == 2) - // 0x00005515 - SFR_DATA_0 = 0x15; - reg_write(RTL837X_REG_SMI_MAC_TYPE); - - // Configure polling of all PHYs by the MAC to detect link-state changes - // Default: 0x000000ff - // Workaround for SDCC BUG 4070: SFR_DATA_U32 = 0x000000ff; - SFR_DATA_U16_UPPER = 0x0000; - SFR_DATA_U16 = 0x00ff; - if (!machine_detected.isRTL8373) { - if (machine.n_sfp == 2) { - // 0x000000f0, only change the bytes that differs from the default. - SFR_DATA_0 = 0xf0; - } else { - // 0x000001f8, only change the bytes that differs from the default. - SFR_DATA_8 = 0x01; - SFR_DATA_0 = 0xf8; - } - } - reg_write(RTL837X_REG_SMI_PORT_POLLING); - // 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); - } - - if (machine.n_10g == 2) { - // Set address of second external PHY on port 8 - REG_SET(RTL837X_REG_SMI_PORT6_9_ADDR, 0x000040e6); - } -} /* Set up serial port 0 using Timer 1 as baudrate generator. @@ -2008,73 +1480,6 @@ void setup_serial_timer1(void) } -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) { flash_read_jedecid(); // This initializes also __xdata flash_size variable @@ -2160,32 +1565,6 @@ void check_and_flash_update_image(void) } } -/* Give the switch a name carrying the tail of its MAC, so several of them on - * one network are distinguishable out of the box. Called after the startup - * config has been replayed and returns at once if that config already set a - * name, so a configured switch does no work for it (suggested in review). - * - * Written without a loop on purpose. Locals - counters and pointers alike - - * land in the 8051's internal-RAM overlay, and on an image with LACP and STP - * both enabled that overlay is exhausted: a loop here makes the linker fail - * with "Could not get 8 consecutive bytes in internal RAM for area OSEG". - * Moving the code into its own function does not help; the overlay is shared - * across the whole image. Hoisting the locals to xdata does not help either, - * because itohex() is inline and brings its own frame. */ -void set_hostname_default(void) -{ - if (hostname[0] != NUL) - return; - - strcpy((__xdata uint8_t *)hostname, "RTLPlayground-"); - hostname[14] = hex[uip_ethaddr.addr[3] >> 4]; - hostname[15] = hex[uip_ethaddr.addr[3] & 0xf]; - hostname[16] = hex[uip_ethaddr.addr[4] >> 4]; - hostname[17] = hex[uip_ethaddr.addr[4] & 0xf]; - hostname[18] = hex[uip_ethaddr.addr[5] >> 4]; - hostname[19] = hex[uip_ethaddr.addr[5] & 0xf]; - hostname[20] = NUL; -} void main(void) diff --git a/sfp.c b/sfp.c new file mode 100644 index 00000000..7bc4f301 --- /dev/null +++ b/sfp.c @@ -0,0 +1,221 @@ +#pragma codeseg BANK3 +#pragma constseg BANK3 + +#include <8051.h> +#include +#include + +#include "rtl837x_sfr.h" +#include "rtl837x_regs.h" +#include "rtl837x_common.h" +#include "rtl837x_pins.h" +#include "rtl837x_phy.h" +#include "rtl837x_port.h" +#include "machine.h" +#include "phy.h" +#include "boot.h" +#include "sfp.h" + +extern __code const struct machine machine; + +// SFP1 b0 = 1 => module missing, b1 = 1 => LOS; +// SFP2 b4 = 1 => module missing, b5 = 1 => LOS; +__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 uint8_t sfp_buf[16]; /* scratch for one I2C transaction, the controller reads at most 16 bytes */ +__xdata uint8_t sfp_speed[2]; +__xdata uint8_t sfp_quirks[2]; + + +__code enum sfp_quirk { + SFP_QUIRK_DDM = (1 << 0), +}; + +struct sfp_quirk_entry { + __code char *vendor; // Set vendor or model to 0 to act as wildcard + __code char *model; + uint8_t quirks; +}; + +static __code struct sfp_quirk_entry sfp_quirk_table[] = { + { "QSFPTEK", "QT-SFP+-T", SFP_QUIRK_DDM }, +}; + + + + +static inline uint8_t sfp_rate_to_sds_config(uint8_t rate) +{ + if (rate == 0x1 || rate == 0x2) + return SDS_100FX; + if (rate == 0xc || rate == 0xd) + return SDS_1000BX_FIBER; + if (rate >= 0x19 && rate <= 0x20) // Ethernet 2.5 GBit + return SDS_HSG; + if (rate >= 0x62 && rate < 0x70) + return SDS_10GR; + return 0xff; +} + + + + +bool sfp_print_info(uint8_t sfp) __banked +{ + // This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields + for (uint8_t i = 16; i < 64; i++) { + if (!(i & 0xf) && !sfp_read_block(sfp, i, 16)) + return false; + if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes + continue; + uint8_t c = sfp_buf[i & 0xf]; + if (c) + write_char(c); + } + print_string("\n"); + + return true; +} + + +// Normalize strings from EEPROM by removing any trailing spaces; this allows simpler comparisons +bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __banked __reentrant +{ + if (!sfp_read_block(sfp, start, length)) + return false; + + dst[length] = NUL; + memcpy(dst, sfp_buf, length); + + while (length > 0 && dst[--length] == ' ') + dst[length] = NUL; + + return true; +} + + + +bool sfp_get_info(uint8_t sfp) __banked +{ + if (!sfp_read_field(sfp_module_vendor[sfp], sfp, 20, 16)) + return false; + if (!sfp_read_field(sfp_module_model[sfp], sfp, 40, 16)) + return false; + + return sfp_read_field(sfp_module_serial[sfp], sfp, 68, 16); +} + + + +void sfp_apply_quirks(uint8_t sfp) __banked __reentrant +{ + sfp_quirks[sfp] = 0; + + for (uint8_t i = 0; i < sizeof(sfp_quirk_table) / sizeof(*sfp_quirk_table); i++) { + if (!sfp_quirk_table[i].vendor || !strcmp(sfp_module_vendor[sfp], sfp_quirk_table[i].vendor)) { + if (!sfp_quirk_table[i].model || !strcmp(sfp_module_model[sfp], sfp_quirk_table[i].model)) { + sfp_quirks[sfp] |= sfp_quirk_table[i].quirks; + } + } + } + + if (sfp_quirks[sfp] & SFP_QUIRK_DDM) { + if (!(sfp_options[sfp] & 0x40)) { + // The module reports that DDM is not implemented, but try a dummy read to confirm + // 0xff would mean a failed I2C read or an impossible (per spec) voltage greater than 6.5V + if (sfp_read_block(sfp, 226, 1) && sfp_buf[0] != 0xff) { + sfp_options[sfp] |= 0x40; + } + } + } +} + + +/* Inititalize SFP GPIOs */ +void setup_sfp_gpio(void) __banked +{ + 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); + } +} + + + +static bool sfp_module_read(uint8_t sfp) +{ + uint8_t rate; + + // 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 + if (!sfp_read_block(sfp, 11, 2)) + return false; + + rate = sfp_buf[1]; + if (sfp_speed[sfp] == SFP_SPEED_100M) + rate = 0x1; + else if (sfp_speed[sfp] == SFP_SPEED_1G) + rate = 0xc; + else if (sfp_speed[sfp] == SFP_SPEED_2G5) + rate = 0x19; + else if (sfp_speed[sfp] == SFP_SPEED_10G) + rate = 0x69; + print_string(" Rate: "); print_byte(rate); // Normally 1, but 0 for DAC, can be ignored? + print_string(" Encoding: "); print_byte(sfp_buf[0]); + print_string(" Module: "); + if (!sfp_print_info(sfp)) + return false; + print_string("\n"); + + if (!sfp_read_block(sfp, 92, 1)) + return false; + sfp_options[sfp] = sfp_buf[0]; + if (!sfp_get_info(sfp)) + return false; + + sfp_apply_quirks(sfp); + sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate)); + + return true; +} + + + + +void handle_sfp(void) __banked +{ + 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 Slot: "); write_char('1' + sfp); + if (!sfp_module_read(sfp)) { + print_string("SFP: an I2C read failed, retrying on the next poll\n"); + sfp_pins_last |= 0x01 << (sfp << 2); + } + } + } else { + if (!(sfp_pins_last & (0x1 << (sfp << 2)))) { + sfp_pins_last |= 0x01 << (sfp << 2); + print_string("\n 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 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 Slot: "); write_char('1' + sfp); write_char('\n'); + } + } + } +} diff --git a/sfp.h b/sfp.h new file mode 100644 index 00000000..250ce4d6 --- /dev/null +++ b/sfp.h @@ -0,0 +1,19 @@ +#ifndef SFP_H +#define SFP_H + +extern __xdata uint8_t sfp_pins_last; +extern __xdata char sfp_module_vendor[2][17]; +extern __xdata char sfp_module_model[2][17]; +extern __xdata char sfp_module_serial[2][17]; +extern __xdata uint8_t sfp_options[2]; +extern __xdata uint8_t sfp_speed[2]; +extern __xdata uint8_t sfp_quirks[2]; + +bool sfp_print_info(uint8_t sfp) __banked; +bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __banked __reentrant; +bool sfp_get_info(uint8_t sfp) __banked; +void sfp_apply_quirks(uint8_t sfp) __banked __reentrant; +void setup_sfp_gpio(void) __banked; +void handle_sfp(void) __banked; + +#endif