162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2015, 2016 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/delay.h> 762306a36Sopenharmony_ci#include <linux/pci.h> 862306a36Sopenharmony_ci#include <linux/vmalloc.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "hfi.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* for the given bus number, return the CSR for reading an i2c line */ 1362306a36Sopenharmony_cistatic inline u32 i2c_in_csr(u32 bus_num) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci return bus_num ? ASIC_QSFP2_IN : ASIC_QSFP1_IN; 1662306a36Sopenharmony_ci} 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* for the given bus number, return the CSR for writing an i2c line */ 1962306a36Sopenharmony_cistatic inline u32 i2c_oe_csr(u32 bus_num) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci return bus_num ? ASIC_QSFP2_OE : ASIC_QSFP1_OE; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic void hfi1_setsda(void *data, int state) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 2762306a36Sopenharmony_ci struct hfi1_devdata *dd = bus->controlling_dd; 2862306a36Sopenharmony_ci u64 reg; 2962306a36Sopenharmony_ci u32 target_oe; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci target_oe = i2c_oe_csr(bus->num); 3262306a36Sopenharmony_ci reg = read_csr(dd, target_oe); 3362306a36Sopenharmony_ci /* 3462306a36Sopenharmony_ci * The OE bit value is inverted and connected to the pin. When 3562306a36Sopenharmony_ci * OE is 0 the pin is left to be pulled up, when the OE is 1 3662306a36Sopenharmony_ci * the pin is driven low. This matches the "open drain" or "open 3762306a36Sopenharmony_ci * collector" convention. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci if (state) 4062306a36Sopenharmony_ci reg &= ~QSFP_HFI0_I2CDAT; 4162306a36Sopenharmony_ci else 4262306a36Sopenharmony_ci reg |= QSFP_HFI0_I2CDAT; 4362306a36Sopenharmony_ci write_csr(dd, target_oe, reg); 4462306a36Sopenharmony_ci /* do a read to force the write into the chip */ 4562306a36Sopenharmony_ci (void)read_csr(dd, target_oe); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void hfi1_setscl(void *data, int state) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 5162306a36Sopenharmony_ci struct hfi1_devdata *dd = bus->controlling_dd; 5262306a36Sopenharmony_ci u64 reg; 5362306a36Sopenharmony_ci u32 target_oe; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci target_oe = i2c_oe_csr(bus->num); 5662306a36Sopenharmony_ci reg = read_csr(dd, target_oe); 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * The OE bit value is inverted and connected to the pin. When 5962306a36Sopenharmony_ci * OE is 0 the pin is left to be pulled up, when the OE is 1 6062306a36Sopenharmony_ci * the pin is driven low. This matches the "open drain" or "open 6162306a36Sopenharmony_ci * collector" convention. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci if (state) 6462306a36Sopenharmony_ci reg &= ~QSFP_HFI0_I2CCLK; 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci reg |= QSFP_HFI0_I2CCLK; 6762306a36Sopenharmony_ci write_csr(dd, target_oe, reg); 6862306a36Sopenharmony_ci /* do a read to force the write into the chip */ 6962306a36Sopenharmony_ci (void)read_csr(dd, target_oe); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int hfi1_getsda(void *data) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 7562306a36Sopenharmony_ci u64 reg; 7662306a36Sopenharmony_ci u32 target_in; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci hfi1_setsda(data, 1); /* clear OE so we do not pull line down */ 7962306a36Sopenharmony_ci udelay(2); /* 1us pull up + 250ns hold */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci target_in = i2c_in_csr(bus->num); 8262306a36Sopenharmony_ci reg = read_csr(bus->controlling_dd, target_in); 8362306a36Sopenharmony_ci return !!(reg & QSFP_HFI0_I2CDAT); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int hfi1_getscl(void *data) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 8962306a36Sopenharmony_ci u64 reg; 9062306a36Sopenharmony_ci u32 target_in; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci hfi1_setscl(data, 1); /* clear OE so we do not pull line down */ 9362306a36Sopenharmony_ci udelay(2); /* 1us pull up + 250ns hold */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci target_in = i2c_in_csr(bus->num); 9662306a36Sopenharmony_ci reg = read_csr(bus->controlling_dd, target_in); 9762306a36Sopenharmony_ci return !!(reg & QSFP_HFI0_I2CCLK); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * Allocate and initialize the given i2c bus number. 10262306a36Sopenharmony_ci * Returns NULL on failure. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic struct hfi1_i2c_bus *init_i2c_bus(struct hfi1_devdata *dd, 10562306a36Sopenharmony_ci struct hfi1_asic_data *ad, int num) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct hfi1_i2c_bus *bus; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci bus = kzalloc(sizeof(*bus), GFP_KERNEL); 11162306a36Sopenharmony_ci if (!bus) 11262306a36Sopenharmony_ci return NULL; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci bus->controlling_dd = dd; 11562306a36Sopenharmony_ci bus->num = num; /* our bus number */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci bus->algo.setsda = hfi1_setsda; 11862306a36Sopenharmony_ci bus->algo.setscl = hfi1_setscl; 11962306a36Sopenharmony_ci bus->algo.getsda = hfi1_getsda; 12062306a36Sopenharmony_ci bus->algo.getscl = hfi1_getscl; 12162306a36Sopenharmony_ci bus->algo.udelay = 5; 12262306a36Sopenharmony_ci bus->algo.timeout = usecs_to_jiffies(100000); 12362306a36Sopenharmony_ci bus->algo.data = bus; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci bus->adapter.owner = THIS_MODULE; 12662306a36Sopenharmony_ci bus->adapter.algo_data = &bus->algo; 12762306a36Sopenharmony_ci bus->adapter.dev.parent = &dd->pcidev->dev; 12862306a36Sopenharmony_ci snprintf(bus->adapter.name, sizeof(bus->adapter.name), 12962306a36Sopenharmony_ci "hfi1_i2c%d", num); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ret = i2c_bit_add_bus(&bus->adapter); 13262306a36Sopenharmony_ci if (ret) { 13362306a36Sopenharmony_ci dd_dev_info(dd, "%s: unable to add i2c bus %d, err %d\n", 13462306a36Sopenharmony_ci __func__, num, ret); 13562306a36Sopenharmony_ci kfree(bus); 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return bus; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * Initialize i2c buses. 14462306a36Sopenharmony_ci * Return 0 on success, -errno on error. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ciint set_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci ad->i2c_bus0 = init_i2c_bus(dd, ad, 0); 14962306a36Sopenharmony_ci ad->i2c_bus1 = init_i2c_bus(dd, ad, 1); 15062306a36Sopenharmony_ci if (!ad->i2c_bus0 || !ad->i2c_bus1) 15162306a36Sopenharmony_ci return -ENOMEM; 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void clean_i2c_bus(struct hfi1_i2c_bus *bus) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (bus) { 15862306a36Sopenharmony_ci i2c_del_adapter(&bus->adapter); 15962306a36Sopenharmony_ci kfree(bus); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_civoid clean_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci if (!ad) 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci clean_i2c_bus(ad->i2c_bus0); 16862306a36Sopenharmony_ci ad->i2c_bus0 = NULL; 16962306a36Sopenharmony_ci clean_i2c_bus(ad->i2c_bus1); 17062306a36Sopenharmony_ci ad->i2c_bus1 = NULL; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int i2c_bus_write(struct hfi1_devdata *dd, struct hfi1_i2c_bus *i2c, 17462306a36Sopenharmony_ci u8 slave_addr, int offset, int offset_size, 17562306a36Sopenharmony_ci u8 *data, u16 len) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci int ret; 17862306a36Sopenharmony_ci int num_msgs; 17962306a36Sopenharmony_ci u8 offset_bytes[2]; 18062306a36Sopenharmony_ci struct i2c_msg msgs[2]; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci switch (offset_size) { 18362306a36Sopenharmony_ci case 0: 18462306a36Sopenharmony_ci num_msgs = 1; 18562306a36Sopenharmony_ci msgs[0].addr = slave_addr; 18662306a36Sopenharmony_ci msgs[0].flags = 0; 18762306a36Sopenharmony_ci msgs[0].len = len; 18862306a36Sopenharmony_ci msgs[0].buf = data; 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci case 2: 19162306a36Sopenharmony_ci offset_bytes[1] = (offset >> 8) & 0xff; 19262306a36Sopenharmony_ci fallthrough; 19362306a36Sopenharmony_ci case 1: 19462306a36Sopenharmony_ci num_msgs = 2; 19562306a36Sopenharmony_ci offset_bytes[0] = offset & 0xff; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci msgs[0].addr = slave_addr; 19862306a36Sopenharmony_ci msgs[0].flags = 0; 19962306a36Sopenharmony_ci msgs[0].len = offset_size; 20062306a36Sopenharmony_ci msgs[0].buf = offset_bytes; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci msgs[1].addr = slave_addr; 20362306a36Sopenharmony_ci msgs[1].flags = I2C_M_NOSTART; 20462306a36Sopenharmony_ci msgs[1].len = len; 20562306a36Sopenharmony_ci msgs[1].buf = data; 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci default: 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci i2c->controlling_dd = dd; 21262306a36Sopenharmony_ci ret = i2c_transfer(&i2c->adapter, msgs, num_msgs); 21362306a36Sopenharmony_ci if (ret != num_msgs) { 21462306a36Sopenharmony_ci dd_dev_err(dd, "%s: bus %d, i2c slave 0x%x, offset 0x%x, len 0x%x; write failed, ret %d\n", 21562306a36Sopenharmony_ci __func__, i2c->num, slave_addr, offset, len, ret); 21662306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int i2c_bus_read(struct hfi1_devdata *dd, struct hfi1_i2c_bus *bus, 22262306a36Sopenharmony_ci u8 slave_addr, int offset, int offset_size, 22362306a36Sopenharmony_ci u8 *data, u16 len) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci int ret; 22662306a36Sopenharmony_ci int num_msgs; 22762306a36Sopenharmony_ci u8 offset_bytes[2]; 22862306a36Sopenharmony_ci struct i2c_msg msgs[2]; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci switch (offset_size) { 23162306a36Sopenharmony_ci case 0: 23262306a36Sopenharmony_ci num_msgs = 1; 23362306a36Sopenharmony_ci msgs[0].addr = slave_addr; 23462306a36Sopenharmony_ci msgs[0].flags = I2C_M_RD; 23562306a36Sopenharmony_ci msgs[0].len = len; 23662306a36Sopenharmony_ci msgs[0].buf = data; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case 2: 23962306a36Sopenharmony_ci offset_bytes[1] = (offset >> 8) & 0xff; 24062306a36Sopenharmony_ci fallthrough; 24162306a36Sopenharmony_ci case 1: 24262306a36Sopenharmony_ci num_msgs = 2; 24362306a36Sopenharmony_ci offset_bytes[0] = offset & 0xff; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci msgs[0].addr = slave_addr; 24662306a36Sopenharmony_ci msgs[0].flags = 0; 24762306a36Sopenharmony_ci msgs[0].len = offset_size; 24862306a36Sopenharmony_ci msgs[0].buf = offset_bytes; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci msgs[1].addr = slave_addr; 25162306a36Sopenharmony_ci msgs[1].flags = I2C_M_RD; 25262306a36Sopenharmony_ci msgs[1].len = len; 25362306a36Sopenharmony_ci msgs[1].buf = data; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci default: 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci bus->controlling_dd = dd; 26062306a36Sopenharmony_ci ret = i2c_transfer(&bus->adapter, msgs, num_msgs); 26162306a36Sopenharmony_ci if (ret != num_msgs) { 26262306a36Sopenharmony_ci dd_dev_err(dd, "%s: bus %d, i2c slave 0x%x, offset 0x%x, len 0x%x; read failed, ret %d\n", 26362306a36Sopenharmony_ci __func__, bus->num, slave_addr, offset, len, ret); 26462306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* 27062306a36Sopenharmony_ci * Raw i2c write. No set-up or lock checking. 27162306a36Sopenharmony_ci * 27262306a36Sopenharmony_ci * Return 0 on success, -errno on error. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic int __i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, 27562306a36Sopenharmony_ci int offset, void *bp, int len) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 27862306a36Sopenharmony_ci struct hfi1_i2c_bus *bus; 27962306a36Sopenharmony_ci u8 slave_addr; 28062306a36Sopenharmony_ci int offset_size; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci bus = target ? dd->asic_data->i2c_bus1 : dd->asic_data->i2c_bus0; 28362306a36Sopenharmony_ci slave_addr = (i2c_addr & 0xff) >> 1; /* convert to 7-bit addr */ 28462306a36Sopenharmony_ci offset_size = (i2c_addr >> 8) & 0x3; 28562306a36Sopenharmony_ci return i2c_bus_write(dd, bus, slave_addr, offset, offset_size, bp, len); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * Caller must hold the i2c chain resource. 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * Return number of bytes written, or -errno. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ciint i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset, 29462306a36Sopenharmony_ci void *bp, int len) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci int ret; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 29962306a36Sopenharmony_ci return -EACCES; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ret = __i2c_write(ppd, target, i2c_addr, offset, bp, len); 30262306a36Sopenharmony_ci if (ret) 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return len; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/* 30962306a36Sopenharmony_ci * Raw i2c read. No set-up or lock checking. 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Return 0 on success, -errno on error. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cistatic int __i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, 31462306a36Sopenharmony_ci int offset, void *bp, int len) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 31762306a36Sopenharmony_ci struct hfi1_i2c_bus *bus; 31862306a36Sopenharmony_ci u8 slave_addr; 31962306a36Sopenharmony_ci int offset_size; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci bus = target ? dd->asic_data->i2c_bus1 : dd->asic_data->i2c_bus0; 32262306a36Sopenharmony_ci slave_addr = (i2c_addr & 0xff) >> 1; /* convert to 7-bit addr */ 32362306a36Sopenharmony_ci offset_size = (i2c_addr >> 8) & 0x3; 32462306a36Sopenharmony_ci return i2c_bus_read(dd, bus, slave_addr, offset, offset_size, bp, len); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* 32862306a36Sopenharmony_ci * Caller must hold the i2c chain resource. 32962306a36Sopenharmony_ci * 33062306a36Sopenharmony_ci * Return number of bytes read, or -errno. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ciint i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset, 33362306a36Sopenharmony_ci void *bp, int len) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci int ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 33862306a36Sopenharmony_ci return -EACCES; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ret = __i2c_read(ppd, target, i2c_addr, offset, bp, len); 34162306a36Sopenharmony_ci if (ret) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return len; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/* 34862306a36Sopenharmony_ci * Write page n, offset m of QSFP memory as defined by SFF 8636 34962306a36Sopenharmony_ci * by writing @addr = ((256 * n) + m) 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * Caller must hold the i2c chain resource. 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * Return number of bytes written or -errno. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ciint qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 35662306a36Sopenharmony_ci int len) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci int count = 0; 35962306a36Sopenharmony_ci int offset; 36062306a36Sopenharmony_ci int nwrite; 36162306a36Sopenharmony_ci int ret = 0; 36262306a36Sopenharmony_ci u8 page; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 36562306a36Sopenharmony_ci return -EACCES; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci while (count < len) { 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * Set the qsfp page based on a zero-based address 37062306a36Sopenharmony_ci * and a page size of QSFP_PAGESIZE bytes. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci page = (u8)(addr / QSFP_PAGESIZE); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 37562306a36Sopenharmony_ci QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1); 37662306a36Sopenharmony_ci /* QSFPs require a 5-10msec delay after write operations */ 37762306a36Sopenharmony_ci mdelay(5); 37862306a36Sopenharmony_ci if (ret) { 37962306a36Sopenharmony_ci hfi1_dev_porterr(ppd->dd, ppd->port, 38062306a36Sopenharmony_ci "QSFP chain %d can't write QSFP_PAGE_SELECT_BYTE: %d\n", 38162306a36Sopenharmony_ci target, ret); 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci offset = addr % QSFP_PAGESIZE; 38662306a36Sopenharmony_ci nwrite = len - count; 38762306a36Sopenharmony_ci /* truncate write to boundary if crossing boundary */ 38862306a36Sopenharmony_ci if (((addr % QSFP_RW_BOUNDARY) + nwrite) > QSFP_RW_BOUNDARY) 38962306a36Sopenharmony_ci nwrite = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 39262306a36Sopenharmony_ci offset, bp + count, nwrite); 39362306a36Sopenharmony_ci /* QSFPs require a 5-10msec delay after write operations */ 39462306a36Sopenharmony_ci mdelay(5); 39562306a36Sopenharmony_ci if (ret) /* stop on error */ 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci count += nwrite; 39962306a36Sopenharmony_ci addr += nwrite; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (ret < 0) 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci return count; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* 40862306a36Sopenharmony_ci * Perform a stand-alone single QSFP write. Acquire the resource, do the 40962306a36Sopenharmony_ci * write, then release the resource. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ciint one_qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 41262306a36Sopenharmony_ci int len) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 41562306a36Sopenharmony_ci u32 resource = qsfp_resource(dd); 41662306a36Sopenharmony_ci int ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ret = acquire_chip_resource(dd, resource, QSFP_WAIT); 41962306a36Sopenharmony_ci if (ret) 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci ret = qsfp_write(ppd, target, addr, bp, len); 42262306a36Sopenharmony_ci release_chip_resource(dd, resource); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return ret; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* 42862306a36Sopenharmony_ci * Access page n, offset m of QSFP memory as defined by SFF 8636 42962306a36Sopenharmony_ci * by reading @addr = ((256 * n) + m) 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * Caller must hold the i2c chain resource. 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * Return the number of bytes read or -errno. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ciint qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 43662306a36Sopenharmony_ci int len) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int count = 0; 43962306a36Sopenharmony_ci int offset; 44062306a36Sopenharmony_ci int nread; 44162306a36Sopenharmony_ci int ret = 0; 44262306a36Sopenharmony_ci u8 page; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 44562306a36Sopenharmony_ci return -EACCES; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci while (count < len) { 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * Set the qsfp page based on a zero-based address 45062306a36Sopenharmony_ci * and a page size of QSFP_PAGESIZE bytes. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci page = (u8)(addr / QSFP_PAGESIZE); 45362306a36Sopenharmony_ci ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 45462306a36Sopenharmony_ci QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1); 45562306a36Sopenharmony_ci /* QSFPs require a 5-10msec delay after write operations */ 45662306a36Sopenharmony_ci mdelay(5); 45762306a36Sopenharmony_ci if (ret) { 45862306a36Sopenharmony_ci hfi1_dev_porterr(ppd->dd, ppd->port, 45962306a36Sopenharmony_ci "QSFP chain %d can't write QSFP_PAGE_SELECT_BYTE: %d\n", 46062306a36Sopenharmony_ci target, ret); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci offset = addr % QSFP_PAGESIZE; 46562306a36Sopenharmony_ci nread = len - count; 46662306a36Sopenharmony_ci /* truncate read to boundary if crossing boundary */ 46762306a36Sopenharmony_ci if (((addr % QSFP_RW_BOUNDARY) + nread) > QSFP_RW_BOUNDARY) 46862306a36Sopenharmony_ci nread = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci ret = __i2c_read(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 47162306a36Sopenharmony_ci offset, bp + count, nread); 47262306a36Sopenharmony_ci if (ret) /* stop on error */ 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci count += nread; 47662306a36Sopenharmony_ci addr += nread; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (ret < 0) 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci return count; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/* 48562306a36Sopenharmony_ci * Perform a stand-alone single QSFP read. Acquire the resource, do the 48662306a36Sopenharmony_ci * read, then release the resource. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ciint one_qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 48962306a36Sopenharmony_ci int len) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 49262306a36Sopenharmony_ci u32 resource = qsfp_resource(dd); 49362306a36Sopenharmony_ci int ret; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = acquire_chip_resource(dd, resource, QSFP_WAIT); 49662306a36Sopenharmony_ci if (ret) 49762306a36Sopenharmony_ci return ret; 49862306a36Sopenharmony_ci ret = qsfp_read(ppd, target, addr, bp, len); 49962306a36Sopenharmony_ci release_chip_resource(dd, resource); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* 50562306a36Sopenharmony_ci * This function caches the QSFP memory range in 128 byte chunks. 50662306a36Sopenharmony_ci * As an example, the next byte after address 255 is byte 128 from 50762306a36Sopenharmony_ci * upper page 01H (if existing) rather than byte 0 from lower page 00H. 50862306a36Sopenharmony_ci * Access page n, offset m of QSFP memory as defined by SFF 8636 50962306a36Sopenharmony_ci * in the cache by reading byte ((128 * n) + m) 51062306a36Sopenharmony_ci * The calls to qsfp_{read,write} in this function correctly handle the 51162306a36Sopenharmony_ci * address map difference between this mapping and the mapping implemented 51262306a36Sopenharmony_ci * by those functions 51362306a36Sopenharmony_ci * 51462306a36Sopenharmony_ci * The caller must be holding the QSFP i2c chain resource. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ciint refresh_qsfp_cache(struct hfi1_pportdata *ppd, struct qsfp_data *cp) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci u32 target = ppd->dd->hfi1_id; 51962306a36Sopenharmony_ci int ret; 52062306a36Sopenharmony_ci unsigned long flags; 52162306a36Sopenharmony_ci u8 *cache = &cp->cache[0]; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* ensure sane contents on invalid reads, for cable swaps */ 52462306a36Sopenharmony_ci memset(cache, 0, (QSFP_MAX_NUM_PAGES * 128)); 52562306a36Sopenharmony_ci spin_lock_irqsave(&ppd->qsfp_info.qsfp_lock, flags); 52662306a36Sopenharmony_ci ppd->qsfp_info.cache_valid = 0; 52762306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->qsfp_info.qsfp_lock, flags); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!qsfp_mod_present(ppd)) { 53062306a36Sopenharmony_ci ret = -ENODEV; 53162306a36Sopenharmony_ci goto bail; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 0, cache, QSFP_PAGESIZE); 53562306a36Sopenharmony_ci if (ret != QSFP_PAGESIZE) { 53662306a36Sopenharmony_ci dd_dev_info(ppd->dd, 53762306a36Sopenharmony_ci "%s: Page 0 read failed, expected %d, got %d\n", 53862306a36Sopenharmony_ci __func__, QSFP_PAGESIZE, ret); 53962306a36Sopenharmony_ci goto bail; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Is paging enabled? */ 54362306a36Sopenharmony_ci if (!(cache[2] & 4)) { 54462306a36Sopenharmony_ci /* Paging enabled, page 03 required */ 54562306a36Sopenharmony_ci if ((cache[195] & 0xC0) == 0xC0) { 54662306a36Sopenharmony_ci /* all */ 54762306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 384, cache + 256, 128); 54862306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 54962306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 55062306a36Sopenharmony_ci goto bail; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 640, cache + 384, 128); 55362306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 55462306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 55562306a36Sopenharmony_ci goto bail; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 55862306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 55962306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 56062306a36Sopenharmony_ci goto bail; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } else if ((cache[195] & 0x80) == 0x80) { 56362306a36Sopenharmony_ci /* only page 2 and 3 */ 56462306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 640, cache + 384, 128); 56562306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 56662306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 56762306a36Sopenharmony_ci goto bail; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 57062306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 57162306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 57262306a36Sopenharmony_ci goto bail; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci } else if ((cache[195] & 0x40) == 0x40) { 57562306a36Sopenharmony_ci /* only page 1 and 3 */ 57662306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 384, cache + 256, 128); 57762306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 57862306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 57962306a36Sopenharmony_ci goto bail; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 58262306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 58362306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 58462306a36Sopenharmony_ci goto bail; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } else { 58762306a36Sopenharmony_ci /* only page 3 */ 58862306a36Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 58962306a36Sopenharmony_ci if (ret <= 0 || ret != 128) { 59062306a36Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 59162306a36Sopenharmony_ci goto bail; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci spin_lock_irqsave(&ppd->qsfp_info.qsfp_lock, flags); 59762306a36Sopenharmony_ci ppd->qsfp_info.cache_valid = 1; 59862306a36Sopenharmony_ci ppd->qsfp_info.cache_refresh_required = 0; 59962306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->qsfp_info.qsfp_lock, flags); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cibail: 60462306a36Sopenharmony_ci memset(cache, 0, (QSFP_MAX_NUM_PAGES * 128)); 60562306a36Sopenharmony_ci return ret; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ciconst char * const hfi1_qsfp_devtech[16] = { 60962306a36Sopenharmony_ci "850nm VCSEL", "1310nm VCSEL", "1550nm VCSEL", "1310nm FP", 61062306a36Sopenharmony_ci "1310nm DFB", "1550nm DFB", "1310nm EML", "1550nm EML", 61162306a36Sopenharmony_ci "Cu Misc", "1490nm DFB", "Cu NoEq", "Cu Eq", 61262306a36Sopenharmony_ci "Undef", "Cu Active BothEq", "Cu FarEq", "Cu NearEq" 61362306a36Sopenharmony_ci}; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci#define QSFP_DUMP_CHUNK 16 /* Holds longest string */ 61662306a36Sopenharmony_ci#define QSFP_DEFAULT_HDR_CNT 224 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci#define QSFP_PWR(pbyte) (((pbyte) >> 6) & 3) 61962306a36Sopenharmony_ci#define QSFP_HIGH_PWR(pbyte) ((pbyte) & 3) 62062306a36Sopenharmony_ci/* For use with QSFP_HIGH_PWR macro */ 62162306a36Sopenharmony_ci#define QSFP_HIGH_PWR_UNUSED 0 /* Bits [1:0] = 00 implies low power module */ 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* 62462306a36Sopenharmony_ci * Takes power class byte [Page 00 Byte 129] in SFF 8636 62562306a36Sopenharmony_ci * Returns power class as integer (1 through 7, per SFF 8636 rev 2.4) 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ciint get_qsfp_power_class(u8 power_byte) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci if (QSFP_HIGH_PWR(power_byte) == QSFP_HIGH_PWR_UNUSED) 63062306a36Sopenharmony_ci /* power classes count from 1, their bit encodings from 0 */ 63162306a36Sopenharmony_ci return (QSFP_PWR(power_byte) + 1); 63262306a36Sopenharmony_ci /* 63362306a36Sopenharmony_ci * 00 in the high power classes stands for unused, bringing 63462306a36Sopenharmony_ci * balance to the off-by-1 offset above, we add 4 here to 63562306a36Sopenharmony_ci * account for the difference between the low and high power 63662306a36Sopenharmony_ci * groups 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci return (QSFP_HIGH_PWR(power_byte) + 4); 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ciint qsfp_mod_present(struct hfi1_pportdata *ppd) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 64462306a36Sopenharmony_ci u64 reg; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci reg = read_csr(dd, dd->hfi1_id ? ASIC_QSFP2_IN : ASIC_QSFP1_IN); 64762306a36Sopenharmony_ci return !(reg & QSFP_HFI0_MODPRST_N); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* 65162306a36Sopenharmony_ci * This function maps QSFP memory addresses in 128 byte chunks in the following 65262306a36Sopenharmony_ci * fashion per the CableInfo SMA query definition in the IBA 1.3 spec/OPA Gen 1 65362306a36Sopenharmony_ci * spec 65462306a36Sopenharmony_ci * For addr 000-127, lower page 00h 65562306a36Sopenharmony_ci * For addr 128-255, upper page 00h 65662306a36Sopenharmony_ci * For addr 256-383, upper page 01h 65762306a36Sopenharmony_ci * For addr 384-511, upper page 02h 65862306a36Sopenharmony_ci * For addr 512-639, upper page 03h 65962306a36Sopenharmony_ci * 66062306a36Sopenharmony_ci * For addresses beyond this range, it returns the invalid range of data buffer 66162306a36Sopenharmony_ci * set to 0. 66262306a36Sopenharmony_ci * For upper pages that are optional, if they are not valid, returns the 66362306a36Sopenharmony_ci * particular range of bytes in the data buffer set to 0. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ciint get_cable_info(struct hfi1_devdata *dd, u32 port_num, u32 addr, u32 len, 66662306a36Sopenharmony_ci u8 *data) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct hfi1_pportdata *ppd; 66962306a36Sopenharmony_ci u32 excess_len = len; 67062306a36Sopenharmony_ci int ret = 0, offset = 0; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (port_num > dd->num_pports || port_num < 1) { 67362306a36Sopenharmony_ci dd_dev_info(dd, "%s: Invalid port number %d\n", 67462306a36Sopenharmony_ci __func__, port_num); 67562306a36Sopenharmony_ci ret = -EINVAL; 67662306a36Sopenharmony_ci goto set_zeroes; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci ppd = dd->pport + (port_num - 1); 68062306a36Sopenharmony_ci if (!qsfp_mod_present(ppd)) { 68162306a36Sopenharmony_ci ret = -ENODEV; 68262306a36Sopenharmony_ci goto set_zeroes; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (!ppd->qsfp_info.cache_valid) { 68662306a36Sopenharmony_ci ret = -EINVAL; 68762306a36Sopenharmony_ci goto set_zeroes; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (addr >= (QSFP_MAX_NUM_PAGES * 128)) { 69162306a36Sopenharmony_ci ret = -ERANGE; 69262306a36Sopenharmony_ci goto set_zeroes; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if ((addr + len) > (QSFP_MAX_NUM_PAGES * 128)) { 69662306a36Sopenharmony_ci excess_len = (addr + len) - (QSFP_MAX_NUM_PAGES * 128); 69762306a36Sopenharmony_ci memcpy(data, &ppd->qsfp_info.cache[addr], (len - excess_len)); 69862306a36Sopenharmony_ci data += (len - excess_len); 69962306a36Sopenharmony_ci goto set_zeroes; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci memcpy(data, &ppd->qsfp_info.cache[addr], len); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (addr <= QSFP_MONITOR_VAL_END && 70562306a36Sopenharmony_ci (addr + len) >= QSFP_MONITOR_VAL_START) { 70662306a36Sopenharmony_ci /* Overlap with the dynamic channel monitor range */ 70762306a36Sopenharmony_ci if (addr < QSFP_MONITOR_VAL_START) { 70862306a36Sopenharmony_ci if (addr + len <= QSFP_MONITOR_VAL_END) 70962306a36Sopenharmony_ci len = addr + len - QSFP_MONITOR_VAL_START; 71062306a36Sopenharmony_ci else 71162306a36Sopenharmony_ci len = QSFP_MONITOR_RANGE; 71262306a36Sopenharmony_ci offset = QSFP_MONITOR_VAL_START - addr; 71362306a36Sopenharmony_ci addr = QSFP_MONITOR_VAL_START; 71462306a36Sopenharmony_ci } else if (addr == QSFP_MONITOR_VAL_START) { 71562306a36Sopenharmony_ci offset = 0; 71662306a36Sopenharmony_ci if (addr + len > QSFP_MONITOR_VAL_END) 71762306a36Sopenharmony_ci len = QSFP_MONITOR_RANGE; 71862306a36Sopenharmony_ci } else { 71962306a36Sopenharmony_ci offset = 0; 72062306a36Sopenharmony_ci if (addr + len > QSFP_MONITOR_VAL_END) 72162306a36Sopenharmony_ci len = QSFP_MONITOR_VAL_END - addr + 1; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci /* Refresh the values of the dynamic monitors from the cable */ 72462306a36Sopenharmony_ci ret = one_qsfp_read(ppd, dd->hfi1_id, addr, data + offset, len); 72562306a36Sopenharmony_ci if (ret != len) { 72662306a36Sopenharmony_ci ret = -EAGAIN; 72762306a36Sopenharmony_ci goto set_zeroes; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ciset_zeroes: 73462306a36Sopenharmony_ci memset(data, 0, excess_len); 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic const char *pwr_codes[8] = {"N/AW", 73962306a36Sopenharmony_ci "1.5W", 74062306a36Sopenharmony_ci "2.0W", 74162306a36Sopenharmony_ci "2.5W", 74262306a36Sopenharmony_ci "3.5W", 74362306a36Sopenharmony_ci "4.0W", 74462306a36Sopenharmony_ci "4.5W", 74562306a36Sopenharmony_ci "5.0W" 74662306a36Sopenharmony_ci }; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ciint qsfp_dump(struct hfi1_pportdata *ppd, char *buf, int len) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci u8 *cache = &ppd->qsfp_info.cache[0]; 75162306a36Sopenharmony_ci u8 bin_buff[QSFP_DUMP_CHUNK]; 75262306a36Sopenharmony_ci char lenstr[6]; 75362306a36Sopenharmony_ci int sofar; 75462306a36Sopenharmony_ci int bidx = 0; 75562306a36Sopenharmony_ci u8 *atten = &cache[QSFP_ATTEN_OFFS]; 75662306a36Sopenharmony_ci u8 *vendor_oui = &cache[QSFP_VOUI_OFFS]; 75762306a36Sopenharmony_ci u8 power_byte = 0; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci sofar = 0; 76062306a36Sopenharmony_ci lenstr[0] = ' '; 76162306a36Sopenharmony_ci lenstr[1] = '\0'; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (ppd->qsfp_info.cache_valid) { 76462306a36Sopenharmony_ci if (QSFP_IS_CU(cache[QSFP_MOD_TECH_OFFS])) 76562306a36Sopenharmony_ci snprintf(lenstr, sizeof(lenstr), "%dM ", 76662306a36Sopenharmony_ci cache[QSFP_MOD_LEN_OFFS]); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci power_byte = cache[QSFP_MOD_PWR_OFFS]; 76962306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "PWR:%.3sW\n", 77062306a36Sopenharmony_ci pwr_codes[get_qsfp_power_class(power_byte)]); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "TECH:%s%s\n", 77362306a36Sopenharmony_ci lenstr, 77462306a36Sopenharmony_ci hfi1_qsfp_devtech[(cache[QSFP_MOD_TECH_OFFS]) >> 4]); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Vendor:%.*s\n", 77762306a36Sopenharmony_ci QSFP_VEND_LEN, &cache[QSFP_VEND_OFFS]); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "OUI:%06X\n", 78062306a36Sopenharmony_ci QSFP_OUI(vendor_oui)); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Part#:%.*s\n", 78362306a36Sopenharmony_ci QSFP_PN_LEN, &cache[QSFP_PN_OFFS]); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Rev:%.*s\n", 78662306a36Sopenharmony_ci QSFP_REV_LEN, &cache[QSFP_REV_OFFS]); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (QSFP_IS_CU(cache[QSFP_MOD_TECH_OFFS])) 78962306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, 79062306a36Sopenharmony_ci "Atten:%d, %d\n", 79162306a36Sopenharmony_ci QSFP_ATTEN_SDR(atten), 79262306a36Sopenharmony_ci QSFP_ATTEN_DDR(atten)); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Serial:%.*s\n", 79562306a36Sopenharmony_ci QSFP_SN_LEN, &cache[QSFP_SN_OFFS]); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Date:%.*s\n", 79862306a36Sopenharmony_ci QSFP_DATE_LEN, &cache[QSFP_DATE_OFFS]); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Lot:%.*s\n", 80162306a36Sopenharmony_ci QSFP_LOT_LEN, &cache[QSFP_LOT_OFFS]); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci while (bidx < QSFP_DEFAULT_HDR_CNT) { 80462306a36Sopenharmony_ci int iidx; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci memcpy(bin_buff, &cache[bidx], QSFP_DUMP_CHUNK); 80762306a36Sopenharmony_ci for (iidx = 0; iidx < QSFP_DUMP_CHUNK; ++iidx) { 80862306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, 80962306a36Sopenharmony_ci " %02X", bin_buff[iidx]); 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "\n"); 81262306a36Sopenharmony_ci bidx += QSFP_DUMP_CHUNK; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci return sofar; 81662306a36Sopenharmony_ci} 817