diff --git a/cmd_parser.c b/cmd_parser.c index 2b13edd..d1c23b6 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -748,18 +748,25 @@ void parse_mtu(void) write_char('\n'); } -void sfp_print_measurements(uint8_t sfp) +bool sfp_print_measurements(uint8_t sfp) { - print_string("Options: "); print_byte(sfp_read_reg(sfp, 92)); write_char('\n'); + if (!sfp_read_block(sfp, 92, 1)) + return false; + + print_string("Options: "); print_byte(sfp_buf[0]); write_char('\n'); if (!(sfp_options[sfp] & 0x40)) - return; - print_string("Temp: "); print_byte(sfp_read_reg(sfp, 224)); print_byte(sfp_read_reg(sfp, 225)); write_char('\n'); - print_string("Vcc: "); print_byte(sfp_read_reg(sfp, 226)); print_byte(sfp_read_reg(sfp, 227)); write_char('\n'); - print_string("TX Bias: "); print_byte(sfp_read_reg(sfp, 228)); print_byte(sfp_read_reg(sfp, 229)); write_char('\n'); - print_string("TX Power: "); print_byte(sfp_read_reg(sfp, 230)); print_byte(sfp_read_reg(sfp, 231)); write_char('\n'); - print_string("RX Power: "); print_byte(sfp_read_reg(sfp, 232)); print_byte(sfp_read_reg(sfp, 233)); write_char('\n'); - print_string("Laser: "); print_byte(sfp_read_reg(sfp, 234)); print_byte(sfp_read_reg(sfp, 235)); write_char('\n'); - print_string("State: "); print_byte(sfp_read_reg(sfp, 238)); write_char('\n'); + return true; + if (!sfp_read_block(sfp, 224, 16)) + return false; + print_string("Temp: "); print_byte(sfp_buf[0]); print_byte(sfp_buf[1]); write_char('\n'); + print_string("Vcc: "); print_byte(sfp_buf[2]); print_byte(sfp_buf[3]); write_char('\n'); + print_string("TX Bias: "); print_byte(sfp_buf[4]); print_byte(sfp_buf[5]); write_char('\n'); + print_string("TX Power: "); print_byte(sfp_buf[6]); print_byte(sfp_buf[7]); write_char('\n'); + print_string("RX Power: "); print_byte(sfp_buf[8]); print_byte(sfp_buf[9]); write_char('\n'); + print_string("Laser: "); print_byte(sfp_buf[10]); print_byte(sfp_buf[11]); write_char('\n'); + print_string("State: "); print_byte(sfp_buf[14]); write_char('\n'); + + return true; } @@ -777,13 +784,14 @@ void parse_sfp(void) print_string(" - empty\n"); continue; } - sfp_i2c_fail = 0; - print_string(" - Rate: "); print_byte(sfp_read_reg(slot, 12)); - print_string(" Encoding: "); print_byte(sfp_read_reg(slot, 11)); + if (!sfp_read_block(slot, 11, 2)) { + print_string(" - I2C read failed on this slot\n"); + continue; + } + print_string(" - Rate: "); print_byte(sfp_buf[1]); + print_string(" Encoding: "); print_byte(sfp_buf[0]); write_char('\n'); - sfp_print_info(slot); - sfp_print_measurements(slot); - if (sfp_i2c_fail) + if (!sfp_print_info(slot) || !sfp_print_measurements(slot)) print_string("I2C read failed on this slot\n"); } return; diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 19796ed..4101031 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -177,10 +177,12 @@ void reg_to_html_long(register uint16_t reg) void send_sfp_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 + for (uint8_t i = 16; i < 64; i++) { + if (!(i & 0xf)) + sfp_read_block(sfp, i, 16); + if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes continue; - uint8_t c = sfp_read_reg(sfp, i); + uint8_t c = sfp_buf[i & 0xf]; if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM char_to_html(c); } @@ -193,32 +195,10 @@ void sfp_send_data(uint8_t slot, uint8_t reg, uint8_t len) if (len > 16) return; - 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) | (len - 1) & 0xf, 0x51 >> 5, (0x51 << 3) & 0xff); - } else { - REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | (len - 1) & 0xf, 0x50 >> 5, (0x50 << 3) & 0xff); - } + sfp_read_block(slot, reg, len); - 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); - - for (uint8_t i = 0; i < len; i++) { - if (!(i & 0x3)) - reg_read_m(RTL837X_REG_I2C_OUT + i); - byte_to_html(sfr_data[3 - (i & 0x3)]); - } + for (uint8_t i = 0; i < len; i++) + byte_to_html(sfp_buf[i]); } diff --git a/rtl837x_common.h b/rtl837x_common.h index 5432343..9fe2c0c 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -149,8 +149,8 @@ void sleep(uint16_t t); void write_char_no_syslog(char c); void write_char(char c); void print_reg(uint16_t reg); -uint8_t sfp_read_reg(uint8_t slot, uint8_t reg) __banked; -extern __xdata uint8_t sfp_i2c_fail; +bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant; +extern __xdata uint8_t sfp_buf[16]; void reg_bit_set(uint16_t reg_addr, char bit); void reg_bit_clear(uint16_t reg_addr, char bit); uint8_t reg_bit_test(uint16_t reg_addr, char bit); @@ -169,7 +169,7 @@ 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); -void sfp_print_info(uint8_t sfp); +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); diff --git a/rtl837x_pins.c b/rtl837x_pins.c index ac0cea3..bee9818 100644 --- a/rtl837x_pins.c +++ b/rtl837x_pins.c @@ -129,39 +129,53 @@ void gpio_output_setup(uint8_t pin, __xdata uint8_t initial_val) __banked{ /* - * Read a register of the EEPROM via I2C + * Read up to 16 consecutive registers of the EEPROM via I2C into sfp_buf */ -uint8_t sfp_read_reg(uint8_t slot, uint8_t reg) __banked +bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant { - 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) | 0, 0x51 >> 5, (0x51 << 3) & 0xff); - } else { - REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 0, 0x50 >> 5, (0x50 << 3) & 0xff); - } + uint8_t dev; + uint8_t val; - 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); + len--; + if (len > 15) + return false; + + dev = (reg & 0x80) ? 0x51 : 0x50; // 0x51 holds the diagnostics, 0x50 the module data + reg &= 0x7f; REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg); - // Execute I2C Read - reg_bit_set(RTL837X_REG_I2C_CTRL, 0); + REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, + 0x1 << (I2C_MEM_ADDR_WIDTH - 16) | len, + (dev >> 5) | i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 + | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2, + ((dev << 3) & 0xff) | 0x1); - // Wait for execution to finish do { - reg_read_m(RTL837X_REG_I2C_CTRL); - } while (sfr_data[3] & 0x1); + reg_read(RTL837X_REG_I2C_CTRL); + } while (SFR_DATA_0 & 0x1); - /* Bit 1 is the controller's own failure indication, which the vendor SDK - * looks at and this did not. Without it an unacknowledged address comes - * back as an ordinary byte and the caller cannot tell it from data. */ - if (sfr_data[3] & 0x2) { - sfp_i2c_fail = 1; - return 0xff; + if (SFR_DATA_0 & 0x2) + return false; + + for (uint8_t i = 0; i <= len; i++) { + switch (i & 0x3) { + case 0: + reg_read(RTL837X_REG_I2C_OUT + i); + val = SFR_DATA_0; + break; + case 1: + val = SFR_DATA_8; + break; + case 2: + val = SFR_DATA_16; + break; + default: + val = SFR_DATA_24; + break; + } + sfp_buf[i] = val; } - reg_read_m(RTL837X_REG_I2C_OUT); - return sfr_data[3]; + return true; } diff --git a/rtlplayground.c b/rtlplayground.c index 6926ae5..420ec9e 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -139,7 +139,7 @@ __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_i2c_fail; /* set by sfp_read_reg() when the controller flags a failed transfer */ +__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; @@ -1188,36 +1188,46 @@ static inline uint8_t sfp_rate_to_sds_config(register uint8_t rate) } -void sfp_print_info(uint8_t sfp) +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 = 20; i < 60; i++) { - if (i >= 36 && i < 40) // Skip Non-ASCII codes + 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_read_reg(sfp, i); + 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 -void sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __reentrant +bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __reentrant { - dst[length] = '\0'; + if (!sfp_read_block(sfp, start, length)) + return false; - for (uint8_t i = 0; i < length; i++) - dst[i] = sfp_read_reg(sfp, start + i); + dst[length] = '\0'; + memcpy(dst, sfp_buf, length); while (length > 0 && dst[--length] == ' ') dst[length] = '\0'; + + return true; } -void sfp_get_info(uint8_t sfp) +bool sfp_get_info(uint8_t sfp) { - sfp_read_field(sfp_module_vendor[sfp], sfp, 20, 16); - sfp_read_field(sfp_module_model[sfp], sfp, 40, 16); - sfp_read_field(sfp_module_serial[sfp], sfp, 68, 16); + 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 @@ -1236,7 +1246,7 @@ void sfp_apply_quirks(uint8_t sfp) __reentrant 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_reg(sfp, 226) != 0xff) { + if (sfp_read_block(sfp, 226, 1) && sfp_buf[0] != 0xff) { sfp_options[sfp] |= 0x40; } } @@ -1260,6 +1270,45 @@ void setup_sfp_gpio(void) } } +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++) { @@ -1267,29 +1316,10 @@ void handle_sfp(void) if (sfp_pins_last & (0x1 << (sfp << 2))) { sfp_pins_last &= ~(0x01 << (sfp << 2)); print_string("\n 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 - sfp_i2c_fail = 0; - uint8_t rate = sfp_read_reg(sfp, 12); - 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_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); - sfp_apply_quirks(sfp); - if (sfp_i2c_fail) - print_string("SFP: an I2C read failed, the module data above may be wrong\n"); - sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate)); + 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)))) {