Merge pull request #345 from DrDoof/fix/sfp-i2c-error

sfp: notice when an I2C read fails
This commit is contained in:
logicog
2026-08-25 05:13:47 +02:00
committed by GitHub
5 changed files with 161 additions and 108 deletions
+25 -14
View File
@@ -759,18 +759,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;
}
@@ -788,11 +795,15 @@ void parse_sfp(void)
print_string(" - empty\n");
continue;
}
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_print_info(slot) || !sfp_print_measurements(slot))
print_string("I2C read failed on this slot\n");
}
return;
}
+8 -28
View File
@@ -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]);
}
+3 -2
View File
@@ -150,7 +150,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);
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);
@@ -171,7 +172,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);
+58
View File
@@ -1,6 +1,11 @@
#include "rtl837x_pins.h"
#include "rtl837x_common.h"
#include "rtl837x_sfr.h"
#include "rtl837x_regs.h"
#include "machine.h"
extern __code const struct machine machine;
extern __xdata uint8_t sfr_data[4];
#pragma codeseg BANK2
#pragma constseg BANK2
@@ -121,3 +126,56 @@ void gpio_output_setup(uint8_t pin, __xdata uint8_t initial_val) __banked{
reg_bit_set(gpio_direction_reg(pin), (pin % 32));
}
/*
* Read up to 16 consecutive registers of the EEPROM via I2C into sfp_buf
*/
bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant
{
uint8_t dev;
uint8_t val;
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);
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);
do {
reg_read(RTL837X_REG_I2C_CTRL);
} while (SFR_DATA_0 & 0x1);
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;
}
return true;
}
+58 -55
View File
@@ -140,6 +140,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_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;
@@ -1068,37 +1069,6 @@ void sds_config(uint8_t sds, uint8_t mode)
}
/*
* 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) | 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);
}
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
@@ -1251,36 +1221,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
@@ -1299,7 +1279,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;
}
}
@@ -1323,17 +1303,17 @@ void setup_sfp_gpio(void)
}
}
void handle_sfp(void)
static bool sfp_module_read(uint8_t sfp)
{
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);
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
uint8_t rate = sfp_read_reg(sfp, 12);
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)
@@ -1343,13 +1323,36 @@ void handle_sfp(void)
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(" Encoding: "); print_byte(sfp_buf[0]);
print_string(" Module: ");
if (!sfp_print_info(sfp))
return false;
print_string("\n");
sfp_options[sfp] = sfp_read_reg(sfp, 92);
sfp_get_info(sfp);
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<MODULE INSERTED> 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)))) {