#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include "hfi.h"
static inline u32 i2c_in_csr(u32 bus_num)
{
return bus_num ? ASIC_QSFP2_IN : ASIC_QSFP1_IN;
}
static inline u32 i2c_oe_csr(u32 bus_num)
{
return bus_num ? ASIC_QSFP2_OE : ASIC_QSFP1_OE;
}
static void hfi1_setsda(void *data, int state)
{
struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data;
struct hfi1_devdata *dd = bus->controlling_dd;
u64 reg;
u32 target_oe;
target_oe = i2c_oe_csr(bus->num);
reg = read_csr(dd, target_oe);
if (state)
reg &= ~QSFP_HFI0_I2CDAT;
else
reg |= QSFP_HFI0_I2CDAT;
write_csr(dd, target_oe, reg);
(void)read_csr(dd, target_oe);
}
static void hfi1_setscl(void *data, int state)
{
struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data;
struct hfi1_devdata *dd = bus->controlling_dd;
u64 reg;
u32 target_oe;
target_oe = i2c_oe_csr(bus->num);
reg = read_csr(dd, target_oe);
if (state)
reg &= ~QSFP_HFI0_I2CCLK;
else
reg |= QSFP_HFI0_I2CCLK;
write_csr(dd, target_oe, reg);
(void)read_csr(dd, target_oe);
}
static int hfi1_getsda(void *data)
{
struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data;
u64 reg;
u32 target_in;
hfi1_setsda(data, 1);
udelay(2);
target_in = i2c_in_csr(bus->num);
reg = read_csr(bus->controlling_dd, target_in);
return !!(reg & QSFP_HFI0_I2CDAT);
}
static int hfi1_getscl(void *data)
{
struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data;
u64 reg;
u32 target_in;
hfi1_setscl(data, 1);
udelay(2);
target_in = i2c_in_csr(bus->num);
reg = read_csr(bus->controlling_dd, target_in);
return !!(reg & QSFP_HFI0_I2CCLK);
}
static struct hfi1_i2c_bus *init_i2c_bus(struct hfi1_devdata *dd,
struct hfi1_asic_data *ad, int num)
{
struct hfi1_i2c_bus *bus;
int ret;
bus = kzalloc(sizeof(*bus), GFP_KERNEL);
if (!bus)
return NULL;
bus->controlling_dd = dd;
bus->num = num;
bus->algo.setsda = hfi1_setsda;
bus->algo.setscl = hfi1_setscl;
bus->algo.getsda = hfi1_getsda;
bus->algo.getscl = hfi1_getscl;
bus->algo.udelay = 5;
bus->algo.timeout = usecs_to_jiffies(100000);
bus->algo.data = bus;
bus->adapter.owner = THIS_MODULE;
bus->adapter.algo_data = &bus->algo;
bus->adapter.dev.parent = &dd->pcidev->dev;
snprintf(bus->adapter.name, sizeof(bus->adapter.name),
"hfi1_i2c%d", num);
ret = i2c_bit_add_bus(&bus->adapter);
if (ret) {
dd_dev_info(dd, "%s: unable to add i2c bus %d, err %d\n",
__func__, num, ret);
kfree(bus);
return NULL;
}
return bus;
}
int set_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad)
{
ad->i2c_bus0 = init_i2c_bus(dd, ad, 0);
ad->i2c_bus1 = init_i2c_bus(dd, ad, 1);
if (!ad->i2c_bus0 || !ad->i2c_bus1)
return -ENOMEM;
return 0;
};
static void clean_i2c_bus(struct hfi1_i2c_bus *bus)
{
if (bus) {
i2c_del_adapter(&bus->adapter);
kfree(bus);
}
}
void clean_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad)
{
if (!ad)
return;
clean_i2c_bus(ad->i2c_bus0);
ad->i2c_bus0 = NULL;
clean_i2c_bus(ad->i2c_bus1);
ad->i2c_bus1 = NULL;
}
static int i2c_bus_write(struct hfi1_devdata *dd, struct hfi1_i2c_bus *i2c,
u8 slave_addr, int offset, int offset_size,
u8 *data, u16 len)
{
int ret;
int num_msgs;
u8 offset_bytes[2];
struct i2c_msg msgs[2];
switch (offset_size) {
case 0:
num_msgs = 1;
msgs[0].addr = slave_addr;
msgs[0].flags = 0;
msgs[0].len = len;
msgs[0].buf = data;
break;
case 2:
offset_bytes[1] = (offset >> 8) & 0xff;
fallthrough;
case 1:
num_msgs = 2;
offset_bytes[0] = offset & 0xff;
msgs[0].addr = slave_addr;
msgs[0].flags = 0;
msgs[0].len = offset_size;
msgs[0].buf = offset_bytes;
msgs[1].addr = slave_addr;
msgs[1].flags = I2C_M_NOSTART;
msgs[1].len = len;
msgs[1].buf = data;
break;
default:
return -EINVAL;
}
i2c->controlling_dd = dd;
ret = i2c_transfer(&i2c->adapter, msgs, num_msgs);
if (ret != num_msgs) {
dd_dev_err(dd, "%s: bus %d, i2c slave 0x%x, offset 0x%x, len 0x%x; write failed, ret %d\n",
__func__, i2c->num, slave_addr, offset, len, ret);
return ret < 0 ? ret : -EIO;
}
return 0;
}
static int i2c_bus_read(struct hfi1_devdata *dd, struct hfi1_i2c_bus *bus,
u8 slave_addr, int offset, int offset_size,
u8 *data, u16 len)
{
int ret;
int num_msgs;
u8 offset_bytes[2];
struct i2c_msg msgs[2];
switch (offset_size) {
case 0:
num_msgs = 1;
msgs[0].addr = slave_addr;
msgs[0].flags = I2C_M_RD;
msgs[0].len = len;
msgs[0].buf = data;
break;
case 2:
offset_bytes[1] = (offset >> 8) & 0xff;
fallthrough;
case 1:
num_msgs = 2;
offset_bytes[0] = offset & 0xff;
msgs[0].addr = slave_addr;
msgs[0].flags = 0;
msgs[0].len = offset_size;
msgs[0].buf = offset_bytes;
msgs[1].addr = slave_addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = len;
msgs[1].buf = data;
break;
default:
return -EINVAL;
}
bus->controlling_dd = dd;
ret = i2c_transfer(&bus->adapter, msgs, num_msgs);
if (ret != num_msgs) {
dd_dev_err(dd, "%s: bus %d, i2c slave 0x%x, offset 0x%x, len 0x%x; read failed, ret %d\n",
__func__, bus->num, slave_addr, offset, len, ret);
return ret < 0 ? ret : -EIO;
}
return 0;
}
static int __i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr,
int offset, void *bp, int len)
{
struct hfi1_devdata *dd = ppd->dd;
struct hfi1_i2c_bus *bus;
u8 slave_addr;
int offset_size;
bus = target ? dd->asic_data->i2c_bus1 : dd->asic_data->i2c_bus0;
slave_addr = (i2c_addr & 0xff) >> 1;
offset_size = (i2c_addr >> 8) & 0x3;
return i2c_bus_write(dd, bus, slave_addr, offset, offset_size, bp, len);
}
int i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset,
void *bp, int len)
{
int ret;
if (!check_chip_resource(ppd->dd, i2c_target(target), __func__))
return -EACCES;
ret = __i2c_write(ppd, target, i2c_addr, offset, bp, len);
if (ret)
return ret;
return len;
}
static int __i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr,
int offset, void *bp, int len)
{
struct hfi1_devdata *dd = ppd->dd;
struct hfi1_i2c_bus *bus;
u8 slave_addr;
int offset_size;
bus = target ? dd->asic_data->i2c_bus1 : dd->asic_data->i2c_bus0;
slave_addr = (i2c_addr & 0xff) >> 1;
offset_size = (i2c_addr >> 8) & 0x3;
return i2c_bus_read(dd, bus, slave_addr, offset, offset_size, bp, len);
}
int i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset,
void *bp, int len)
{
int ret;
if (!check_chip_resource(ppd->dd, i2c_target(target), __func__))
return -EACCES;
ret = __i2c_read(ppd, target, i2c_addr, offset, bp, len);
if (ret)
return ret;
return len;
}
int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
int len)
{
int count = 0;
int offset;
int nwrite;
int ret = 0;
u8 page;
if (!check_chip_resource(ppd->dd, i2c_target(target), __func__))
return -EACCES;
while (count < len) {
page = (u8)(addr / QSFP_PAGESIZE);
ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
mdelay(5);
if (ret) {
hfi1_dev_porterr(ppd->dd, ppd->port,
"QSFP chain %d can't write QSFP_PAGE_SELECT_BYTE: %d\n",
target, ret);
break;
}
offset = addr % QSFP_PAGESIZE;
nwrite = len - count;
if (((addr % QSFP_RW_BOUNDARY) + nwrite) > QSFP_RW_BOUNDARY)
nwrite = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY);
ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
offset, bp + count, nwrite);
mdelay(5);
if (ret)
break;
count += nwrite;
addr += nwrite;
}
if (ret < 0)
return ret;
return count;
}
int one_qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
int len)
{
struct hfi1_devdata *dd = ppd->dd;
u32 resource = qsfp_resource(dd);
int ret;
ret = acquire_chip_resource(dd, resource, QSFP_WAIT);
if (ret)
return ret;
ret = qsfp_write(ppd, target, addr, bp, len);
release_chip_resource(dd, resource);
return ret;
}
int qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
int len)
{
int count = 0;
int offset;
int nread;
int ret = 0;
u8 page;
if (!check_chip_resource(ppd->dd, i2c_target(target), __func__))
return -EACCES;
while (count < len) {
page = (u8)(addr / QSFP_PAGESIZE);
ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
mdelay(5);
if (ret) {
hfi1_dev_porterr(ppd->dd, ppd->port,
"QSFP chain %d can't write QSFP_PAGE_SELECT_BYTE: %d\n",
target, ret);
break;
}
offset = addr % QSFP_PAGESIZE;
nread = len - count;
if (((addr % QSFP_RW_BOUNDARY) + nread) > QSFP_RW_BOUNDARY)
nread = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY);
ret = __i2c_read(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
offset, bp + count, nread);
if (ret)
break;
count += nread;
addr += nread;
}
if (ret < 0)
return ret;
return count;
}
int one_qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
int len)
{
struct hfi1_devdata *dd = ppd->dd;
u32 resource = qsfp_resource(dd);
int ret;
ret = acquire_chip_resource(dd, resource, QSFP_WAIT);
if (ret)
return ret;
ret = qsfp_read(ppd, target, addr, bp, len);
release_chip_resource(dd, resource);
return ret;
}
int refresh_qsfp_cache(struct hfi1_pportdata *ppd, struct qsfp_data *cp)
{
u32 target = ppd->dd->hfi1_id;
int ret;
unsigned long flags;
u8 *cache = &cp->cache[0];
memset(cache, 0, (QSFP_MAX_NUM_PAGES * 128));
spin_lock_irqsave(&ppd->qsfp_info.qsfp_lock, flags);
ppd->qsfp_info.cache_valid = 0;
spin_unlock_irqrestore(&ppd->qsfp_info.qsfp_lock, flags);
if (!qsfp_mod_present(ppd)) {
ret = -ENODEV;
goto bail;
}
ret = qsfp_read(ppd, target, 0, cache, QSFP_PAGESIZE);
if (ret != QSFP_PAGESIZE) {
dd_dev_info(ppd->dd,
"%s: Page 0 read failed, expected %d, got %d\n",
__func__, QSFP_PAGESIZE, ret);
goto bail;
}
if (!(cache[2] & 4)) {
if ((cache[195] & 0xC0) == 0xC0) {
ret = qsfp_read(ppd, target, 384, cache + 256, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
ret = qsfp_read(ppd, target, 640, cache + 384, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
ret = qsfp_read(ppd, target, 896, cache + 512, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
} else if ((cache[195] & 0x80) == 0x80) {
ret = qsfp_read(ppd, target, 640, cache + 384, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
ret = qsfp_read(ppd, target, 896, cache + 512, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
} else if ((cache[195] & 0x40) == 0x40) {
ret = qsfp_read(ppd, target, 384, cache + 256, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
ret = qsfp_read(ppd, target, 896, cache + 512, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
} else {
ret = qsfp_read(ppd, target, 896, cache + 512, 128);
if (ret <= 0 || ret != 128) {
dd_dev_info(ppd->dd, "%s failed\n", __func__);
goto bail;
}
}
}
spin_lock_irqsave(&ppd->qsfp_info.qsfp_lock, flags);
ppd->qsfp_info.cache_valid = 1;
ppd->qsfp_info.cache_refresh_required = 0;
spin_unlock_irqrestore(&ppd->qsfp_info.qsfp_lock, flags);
return 0;
bail:
memset(cache, 0, (QSFP_MAX_NUM_PAGES * 128));
return ret;
}
const char * const hfi1_qsfp_devtech[16] = {
"850nm VCSEL", "1310nm VCSEL", "1550nm VCSEL", "1310nm FP",
"1310nm DFB", "1550nm DFB", "1310nm EML", "1550nm EML",
"Cu Misc", "1490nm DFB", "Cu NoEq", "Cu Eq",
"Undef", "Cu Active BothEq", "Cu FarEq", "Cu NearEq"
};
#define QSFP_DUMP_CHUNK 16 /* Holds longest string */
#define QSFP_DEFAULT_HDR_CNT 224
#define QSFP_PWR(pbyte) (((pbyte) >> 6) & 3)
#define QSFP_HIGH_PWR(pbyte) ((pbyte) & 3)
#define QSFP_HIGH_PWR_UNUSED 0 /* Bits [1:0] = 00 implies low power module */
int get_qsfp_power_class(u8 power_byte)
{
if (QSFP_HIGH_PWR(power_byte) == QSFP_HIGH_PWR_UNUSED)
return (QSFP_PWR(power_byte) + 1);
return (QSFP_HIGH_PWR(power_byte) + 4);
}
int qsfp_mod_present(struct hfi1_pportdata *ppd)
{
struct hfi1_devdata *dd = ppd->dd;
u64 reg;
reg = read_csr(dd, dd->hfi1_id ? ASIC_QSFP2_IN : ASIC_QSFP1_IN);
return !(reg & QSFP_HFI0_MODPRST_N);
}
int get_cable_info(struct hfi1_devdata *dd, u32 port_num, u32 addr, u32 len,
u8 *data)
{
struct hfi1_pportdata *ppd;
u32 excess_len = len;
int ret = 0, offset = 0;
if (port_num > dd->num_pports || port_num < 1) {
dd_dev_info(dd, "%s: Invalid port number %d\n",
__func__, port_num);
ret = -EINVAL;
goto set_zeroes;
}
ppd = dd->pport + (port_num - 1);
if (!qsfp_mod_present(ppd)) {
ret = -ENODEV;
goto set_zeroes;
}
if (!ppd->qsfp_info.cache_valid) {
ret = -EINVAL;
goto set_zeroes;
}
if (addr >= (QSFP_MAX_NUM_PAGES * 128)) {
ret = -ERANGE;
goto set_zeroes;
}
if ((addr + len) > (QSFP_MAX_NUM_PAGES * 128)) {
excess_len = (addr + len) - (QSFP_MAX_NUM_PAGES * 128);
memcpy(data, &ppd->qsfp_info.cache[addr], (len - excess_len));
data += (len - excess_len);
goto set_zeroes;
}
memcpy(data, &ppd->qsfp_info.cache[addr], len);
if (addr <= QSFP_MONITOR_VAL_END &&
(addr + len) >= QSFP_MONITOR_VAL_START) {
if (addr < QSFP_MONITOR_VAL_START) {
if (addr + len <= QSFP_MONITOR_VAL_END)
len = addr + len - QSFP_MONITOR_VAL_START;
else
len = QSFP_MONITOR_RANGE;
offset = QSFP_MONITOR_VAL_START - addr;
addr = QSFP_MONITOR_VAL_START;
} else if (addr == QSFP_MONITOR_VAL_START) {
offset = 0;
if (addr + len > QSFP_MONITOR_VAL_END)
len = QSFP_MONITOR_RANGE;
} else {
offset = 0;
if (addr + len > QSFP_MONITOR_VAL_END)
len = QSFP_MONITOR_VAL_END - addr + 1;
}
ret = one_qsfp_read(ppd, dd->hfi1_id, addr, data + offset, len);
if (ret != len) {
ret = -EAGAIN;
goto set_zeroes;
}
}
return 0;
set_zeroes:
memset(data, 0, excess_len);
return ret;
}
static const char *pwr_codes[8] = {"N/AW",
"1.5W",
"2.0W",
"2.5W",
"3.5W",
"4.0W",
"4.5W",
"5.0W"
};
int qsfp_dump(struct hfi1_pportdata *ppd, char *buf, int len)
{
u8 *cache = &ppd->qsfp_info.cache[0];
u8 bin_buff[QSFP_DUMP_CHUNK];
char lenstr[6];
int sofar;
int bidx = 0;
u8 *atten = &cache[QSFP_ATTEN_OFFS];
u8 *vendor_oui = &cache[QSFP_VOUI_OFFS];
u8 power_byte = 0;
sofar = 0;
lenstr[0] = ' ';
lenstr[1] = '\0';
if (ppd->qsfp_info.cache_valid) {
if (QSFP_IS_CU(cache[QSFP_MOD_TECH_OFFS]))
snprintf(lenstr, sizeof(lenstr), "%dM ",
cache[QSFP_MOD_LEN_OFFS]);
power_byte = cache[QSFP_MOD_PWR_OFFS];
sofar += scnprintf(buf + sofar, len - sofar, "PWR:%.3sW\n",
pwr_codes[get_qsfp_power_class(power_byte)]);
sofar += scnprintf(buf + sofar, len - sofar, "TECH:%s%s\n",
lenstr,
hfi1_qsfp_devtech[(cache[QSFP_MOD_TECH_OFFS]) >> 4]);
sofar += scnprintf(buf + sofar, len - sofar, "Vendor:%.*s\n",
QSFP_VEND_LEN, &cache[QSFP_VEND_OFFS]);
sofar += scnprintf(buf + sofar, len - sofar, "OUI:%06X\n",
QSFP_OUI(vendor_oui));
sofar += scnprintf(buf + sofar, len - sofar, "Part#:%.*s\n",
QSFP_PN_LEN, &cache[QSFP_PN_OFFS]);
sofar += scnprintf(buf + sofar, len - sofar, "Rev:%.*s\n",
QSFP_REV_LEN, &cache[QSFP_REV_OFFS]);
if (QSFP_IS_CU(cache[QSFP_MOD_TECH_OFFS]))
sofar += scnprintf(buf + sofar, len - sofar,
"Atten:%d, %d\n",
QSFP_ATTEN_SDR(atten),
QSFP_ATTEN_DDR(atten));
sofar += scnprintf(buf + sofar, len - sofar, "Serial:%.*s\n",
QSFP_SN_LEN, &cache[QSFP_SN_OFFS]);
sofar += scnprintf(buf + sofar, len - sofar, "Date:%.*s\n",
QSFP_DATE_LEN, &cache[QSFP_DATE_OFFS]);
sofar += scnprintf(buf + sofar, len - sofar, "Lot:%.*s\n",
QSFP_LOT_LEN, &cache[QSFP_LOT_OFFS]);
while (bidx < QSFP_DEFAULT_HDR_CNT) {
int iidx;
memcpy(bin_buff, &cache[bidx], QSFP_DUMP_CHUNK);
for (iidx = 0; iidx < QSFP_DUMP_CHUNK; ++iidx) {
sofar += scnprintf(buf + sofar, len - sofar,
" %02X", bin_buff[iidx]);
}
sofar += scnprintf(buf + sofar, len - sofar, "\n");
bidx += QSFP_DUMP_CHUNK;
}
}
return sofar;
}