From: Jan Klemkow Subject: Re: ifconfig transceiver support for ice(4) To: tech@openbsd.org Date: Mon, 18 Aug 2025 13:44:16 +0200 On Sat, Aug 16, 2025 at 02:15:23PM +0200, Stefan Sperling wrote: > On Fri, Aug 15, 2025 at 11:43:14AM +0200, Stefan Sperling wrote: > > This patch makes ifconfig ice0 transceiver (as root) display information > > about inserted transceivers. > > Updated version which adds eeprom page flipping for SFP devices, > as requested by dlg@. > > ok? Diff looks good and works for me. ok jan@ ot37# ifconfig ice sff ice0: flags=8802 mtu 1500 lladdr 50:7c:6f:86:fc:80 index 11 priority 0 llprio 3 media: Ethernet autoselect (25GbaseSR full-duplex) status: active transceiver: SFP LC, 850 nm, 140m OM3, 10m model: FS SFP-10/25GSR-85 rev 1A serial: S2308417658, date: 2023-10-13 voltage: 3.34 V, bias current: 6.23 mA temp: 33.00 C (low -5.00 C, high 75.00 C) tx: 0.74 dBm (low -11.40 dBm, high 4.00 dBm) rx: 1.10 dBm (low -13.31 dBm, high 4.00 dBm) root@ot41:.../~# ifconfig ice sff ice0: flags=8843 mtu 1500 lladdr 6c:fe:54:9b:12:e8 description: Intel E810-C QSFP index 11 priority 0 llprio 3 media: Ethernet autoselect (100GbaseSR4 full-duplex) status: active transceiver: QSFP28 MPO 1x12, 850 nm, 70m OM3, 50m model: Intel Corp Q.851HG.02 rev A0 serial: FHC36G6 date: 2024-11-12 max case temp: 70 C temp: 42.66 C, voltage: 3.35 V channel 1: bias current: 6.63 mA, rx: -0.95 dBm, tx: -0.11 dBm channel 2: bias current: 6.68 mA, rx: -0.16 dBm, tx: -0.86 dBm channel 3: bias current: 6.61 mA, rx: -0.39 dBm, tx: -0.74 dBm channel 4: bias current: 6.58 mA, rx: -0.46 dBm, tx: -1.03 dBm > M sys/dev/pci/if_ice.c | 163+ 0- > M sys/dev/pci/if_icereg.h | 5+ 0- > M sys/dev/pci/if_icevar.h | 2+ 0- > > 3 files changed, 170 insertions(+), 0 deletions(-) > > commit - eec461d818a542e83f2ba358378f3c806987b4c1 > commit + 521a9cf1dca52ced98fabf1e946f0ac2f2c9770d > blob - 306e5c3df38b8fa103eb8f65cae9e5b8b0d5a6f7 > blob + f909a9dcedd04bd0d87c597c5b2d0c70b674ea38 > --- sys/dev/pci/if_ice.c > +++ sys/dev/pci/if_ice.c > @@ -261,6 +261,8 @@ struct ice_intr_vector { > > #define ICE_MAX_VECTORS 8 /* XXX this is pretty arbitrary */ > > +static struct rwlock ice_sff_lock = RWLOCK_INITIALIZER("icesff"); > + > struct ice_softc { > struct device sc_dev; > struct arpcom sc_ac; > @@ -13568,7 +13570,161 @@ ice_down(struct ice_softc *sc) > return 0; > } > > +/* Read SFF EEPROM (0x06EE) */ > int > +ice_aq_sff_eeprom(struct ice_hw *hw, uint16_t lport, uint8_t bus_addr, > + uint16_t mem_addr, uint8_t page, uint8_t set_page, > + uint8_t *data, uint8_t length, int write, struct ice_sq_cd *cd) > +{ > + struct ice_aqc_sff_eeprom *cmd; > + struct ice_aq_desc desc; > + int status; > + > + if (!data || (mem_addr & 0xff00)) > + return ICE_ERR_PARAM; > + > + ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_sff_eeprom); > + cmd = &desc.params.read_write_sff_param; > + desc.flags = htole16(ICE_AQ_FLAG_RD); > + cmd->lport_num = (uint8_t)(lport & 0xff); > + cmd->lport_num_valid = (uint8_t)((lport >> 8) & 0x01); > + cmd->i2c_bus_addr = htole16( > + ((bus_addr >> 1) & ICE_AQC_SFF_I2CBUS_7BIT_M) | > + ((set_page << ICE_AQC_SFF_SET_EEPROM_PAGE_S) & > + ICE_AQC_SFF_SET_EEPROM_PAGE_M)); > + cmd->i2c_mem_addr = htole16(mem_addr & 0xff); > + cmd->eeprom_page = htole16((uint16_t)page << ICE_AQC_SFF_EEPROM_PAGE_S); > + if (write) > + cmd->i2c_bus_addr |= htole16(ICE_AQC_SFF_IS_WRITE); > + > + status = ice_aq_send_cmd(hw, &desc, data, length, cd); > + return status; > +} > + > +int > +ice_rw_sff_eeprom(struct ice_softc *sc, uint16_t dev_addr, uint16_t offset, > + uint8_t page, uint8_t* data, uint16_t length, uint8_t set_page, int write) > +{ > + struct ice_hw *hw = &sc->hw; > + int ret = 0, retries = 0; > + int status; > + > + if (length > 16) > + return (EINVAL); > + > + if (ice_test_state(&sc->state, ICE_STATE_RECOVERY_MODE)) > + return (ENOSYS); > + > + if (ice_test_state(&sc->state, ICE_STATE_NO_MEDIA)) > + return (ENXIO); > + > + do { > + status = ice_aq_sff_eeprom(hw, 0, dev_addr, offset, page, > + set_page, data, length, write, NULL); > + if (!status) { > + ret = 0; > + break; > + } > + if (status == ICE_ERR_AQ_ERROR && > + hw->adminq.sq_last_status == ICE_AQ_RC_EBUSY) { > + ret = EBUSY; > + continue; > + } > + if (status == ICE_ERR_AQ_ERROR && > + hw->adminq.sq_last_status == ICE_AQ_RC_EACCES) { > + /* FW says I2C access isn't supported */ > + ret = EACCES; > + break; > + } > + if (status == ICE_ERR_AQ_ERROR && > + hw->adminq.sq_last_status == ICE_AQ_RC_EPERM) { > + ret = EPERM; > + break; > + } else { > + ret = EIO; > + break; > + } > + } while (retries++ < ICE_I2C_MAX_RETRIES); > + > + return (ret); > +} > + > +/* > + * Read from the SFF eeprom. > + * The I2C device address is typically 0xA0 or 0xA2. For more details on > + * the contents of an SFF eeprom, refer to SFF-8724 (SFP), SFF-8636 (QSFP), > + * and SFF-8024 (both). > + */ > +int > +ice_read_sff_eeprom(struct ice_softc *sc, uint16_t dev_addr, uint16_t offset, > + uint8_t page, uint8_t* data, uint16_t length) > +{ > + return ice_rw_sff_eeprom(sc, dev_addr, offset, page, data, length, > + 0, 0); > +} > + > +/* Write to the SFF eeprom. */ > +int > +ice_write_sff_eeprom(struct ice_softc *sc, uint16_t dev_addr, uint16_t offset, > + uint8_t page, uint8_t* data, uint16_t length, uint8_t set_page) > +{ > + return ice_rw_sff_eeprom(sc, dev_addr, offset, page, data, length, > + 1, set_page); > +} > + > +int > +ice_get_sffpage(struct ice_softc *sc, struct if_sffpage *sff) > +{ > + struct ice_hw *hw = &sc->hw; > + struct ice_port_info *pi = hw->port_info; > + struct ice_link_status *li = &pi->phy.link_info; > + const uint16_t chunksize = 16; > + uint16_t offset = 0; > + uint8_t curpage = 0; > + int error; > + > + if (sff->sff_addr != IFSFF_ADDR_EEPROM && > + sff->sff_addr != IFSFF_ADDR_DDM) > + return (EINVAL); > + > + if (li->module_type[0] == ICE_SFF8024_ID_NONE) > + return (ENXIO); > + > + if (sff->sff_addr == IFSFF_ADDR_EEPROM && > + li->module_type[0] == ICE_SFF8024_ID_SFP) { > + error = ice_read_sff_eeprom(sc, sff->sff_addr, 127, 0, > + &curpage, 1); > + if (error) > + return error; > + > + if (curpage != sff->sff_page) { > + error = ice_write_sff_eeprom(sc, sff->sff_addr, 127, 0, > + &sff->sff_page, 1, 1); > + if (error) > + return error; > + } > + } > + > + for (; offset <= IFSFF_DATA_LEN - chunksize; offset += chunksize) { > + error = ice_read_sff_eeprom(sc, sff->sff_addr, offset, > + sff->sff_page, &sff->sff_data[0] + offset, chunksize); > + if (error) > + return error; > + } > + > + if (sff->sff_addr == IFSFF_ADDR_EEPROM && > + li->module_type[0] == ICE_SFF8024_ID_SFP && > + curpage != sff->sff_page) { > + error = ice_write_sff_eeprom(sc, sff->sff_addr, 127, 0, > + &curpage, 1, 1); > + if (error) > + return error; > + } > + > + return 0; > +} > + > +int > ice_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) > { > struct ice_softc *sc = ifp->if_softc; > @@ -13637,6 +13793,13 @@ ice_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) > } > } > break; > + case SIOCGIFSFFPAGE: > + error = rw_enter(&ice_sff_lock, RW_WRITE|RW_INTR); > + if (error) > + break; > + error = ice_get_sffpage(sc, (struct if_sffpage *)data); > + rw_exit(&ice_sff_lock); > + break; > default: > error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); > break; > blob - 14492fcd80c0b755e7520839bec186cec82d6976 > blob + 9efcd6b6fb3223541bc528c2eec39ba96dcc1486 > --- sys/dev/pci/if_icereg.h > +++ sys/dev/pci/if_icereg.h > @@ -10900,6 +10900,11 @@ struct ice_aqc_get_phy_caps_data { > uint8_t extended_compliance_code; > #define ICE_MODULE_TYPE_TOTAL_BYTE 3 > uint8_t module_type[ICE_MODULE_TYPE_TOTAL_BYTE]; > +#define ICE_SFF8024_ID_NONE 0x00 > +#define ICE_SFF8024_ID_SFP 0x03 > +#define ICE_SFF8024_ID_QSFP 0x0c > +#define ICE_SFF8024_ID_QSFP_PLUS 0x0d > +#define ICE_SFF8024_ID_QSFP28 0x11 > #define ICE_AQC_MOD_TYPE_BYTE0_SFP_PLUS 0xA0 > #define ICE_AQC_MOD_TYPE_BYTE0_QSFP_PLUS 0x80 > #define ICE_AQC_MOD_TYPE_IDENT 1 > blob - b73c60d9c9575c5ccdb6e9795b8d170fa16d1af3 > blob + 6f02d39411437519a5da618307f55fc27765b826 > --- sys/dev/pci/if_icevar.h > +++ sys/dev/pci/if_icevar.h > @@ -4694,3 +4694,5 @@ struct ice_vsi { > > /* Driver always calls main vsi_handle first */ > #define ICE_MAIN_VSI_HANDLE 0 > + > +#define ICE_I2C_MAX_RETRIES 10 >