Commit f1bf2963 authored by Dean Luick's avatar Dean Luick Committed by Doug Ledford

staging/rdma/hfi1: Fix for generic I2C interface

The original I2C interface was geared for QSFP accesses. Modify
the interface to behave more like a generic I2C controller such
that reads and writes can accept multi-byte offsets. Removed
reads following writes and moved reset to top level.
Reviewed-by: default avatarEaswar Hariharan <easwar.hariharan@intel.com>
Reviewed-by: default avatarDean Luick <dean.luick@intel.com>
Signed-off-by: default avatarPablo Cacho <pablo.cacho@intel.com>
Signed-off-by: default avatarJubin John <jubin.john@intel.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 89abfc8d
...@@ -463,7 +463,8 @@ static ssize_t __i2c_debugfs_write(struct file *file, const char __user *buf, ...@@ -463,7 +463,8 @@ static ssize_t __i2c_debugfs_write(struct file *file, const char __user *buf,
goto _free; goto _free;
} }
i2c_addr = (*ppos >> 16) & 0xff; /* byte offset format: [offsetSize][i2cAddr][offsetHigh][offsetLow] */
i2c_addr = (*ppos >> 16) & 0xffff;
offset = *ppos & 0xffff; offset = *ppos & 0xffff;
total_written = i2c_write(ppd, target, i2c_addr, offset, buff, count); total_written = i2c_write(ppd, target, i2c_addr, offset, buff, count);
...@@ -517,7 +518,8 @@ static ssize_t __i2c_debugfs_read(struct file *file, char __user *buf, ...@@ -517,7 +518,8 @@ static ssize_t __i2c_debugfs_read(struct file *file, char __user *buf,
goto _return; goto _return;
} }
i2c_addr = (*ppos >> 16) & 0xff; /* byte offset format: [offsetSize][i2cAddr][offsetHigh][offsetLow] */
i2c_addr = (*ppos >> 16) & 0xffff;
offset = *ppos & 0xffff; offset = *ppos & 0xffff;
total_read = i2c_read(ppd, target, i2c_addr, offset, buff, count); total_read = i2c_read(ppd, target, i2c_addr, offset, buff, count);
......
...@@ -71,14 +71,6 @@ static int __i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, ...@@ -71,14 +71,6 @@ static int __i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr,
int ret, cnt; int ret, cnt;
u8 *buff = bp; u8 *buff = bp;
/* Make sure TWSI bus is in sane state. */
ret = hfi1_twsi_reset(dd, target);
if (ret) {
hfi1_dev_porterr(dd, ppd->port,
"I2C interface Reset for write failed\n");
return -EIO;
}
cnt = 0; cnt = 0;
while (cnt < len) { while (cnt < len) {
int wlen = len - cnt; int wlen = len - cnt;
...@@ -106,11 +98,22 @@ int i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset, ...@@ -106,11 +98,22 @@ int i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset,
int ret; int ret;
ret = mutex_lock_interruptible(&dd->qsfp_i2c_mutex); ret = mutex_lock_interruptible(&dd->qsfp_i2c_mutex);
if (!ret) { if (ret)
ret = __i2c_write(ppd, target, i2c_addr, offset, bp, len); return ret;
mutex_unlock(&dd->qsfp_i2c_mutex);
/* make sure the TWSI bus is in a sane state */
ret = hfi1_twsi_reset(ppd->dd, target);
if (ret) {
hfi1_dev_porterr(ppd->dd, ppd->port,
"I2C write interface reset failed\n");
ret = -EIO;
goto done;
} }
ret = __i2c_write(ppd, target, i2c_addr, offset, bp, len);
done:
mutex_unlock(&dd->qsfp_i2c_mutex);
return ret; return ret;
} }
...@@ -125,16 +128,6 @@ static int __i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, ...@@ -125,16 +128,6 @@ static int __i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr,
int stuck = 0; int stuck = 0;
u8 *buff = bp; u8 *buff = bp;
/* Make sure TWSI bus is in sane state. */
ret = hfi1_twsi_reset(dd, target);
if (ret) {
hfi1_dev_porterr(dd, ppd->port,
"I2C interface Reset for read failed\n");
ret = -EIO;
stuck = 1;
goto exit;
}
cnt = 0; cnt = 0;
while (cnt < len) { while (cnt < len) {
int rlen = len - cnt; int rlen = len - cnt;
...@@ -178,11 +171,22 @@ int i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset, ...@@ -178,11 +171,22 @@ int i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset,
int ret; int ret;
ret = mutex_lock_interruptible(&dd->qsfp_i2c_mutex); ret = mutex_lock_interruptible(&dd->qsfp_i2c_mutex);
if (!ret) { if (ret)
ret = __i2c_read(ppd, target, i2c_addr, offset, bp, len); return ret;
mutex_unlock(&dd->qsfp_i2c_mutex);
/* make sure the TWSI bus is in a sane state */
ret = hfi1_twsi_reset(ppd->dd, target);
if (ret) {
hfi1_dev_porterr(ppd->dd, ppd->port,
"I2C read interface reset failed\n");
ret = -EIO;
goto done;
} }
ret = __i2c_read(ppd, target, i2c_addr, offset, bp, len);
done:
mutex_unlock(&dd->qsfp_i2c_mutex);
return ret; return ret;
} }
...@@ -203,6 +207,15 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, ...@@ -203,6 +207,15 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
if (ret) if (ret)
return ret; return ret;
/* make sure the TWSI bus is in a sane state */
ret = hfi1_twsi_reset(ppd->dd, target);
if (ret) {
hfi1_dev_porterr(ppd->dd, ppd->port,
"QSFP write interface reset failed\n");
mutex_unlock(&ppd->dd->qsfp_i2c_mutex);
return -EIO;
}
while (count < len) { while (count < len) {
/* /*
* Set the qsfp page based on a zero-based addresss * Set the qsfp page based on a zero-based addresss
...@@ -210,7 +223,7 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, ...@@ -210,7 +223,7 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
*/ */
page = (u8)(addr / QSFP_PAGESIZE); page = (u8)(addr / QSFP_PAGESIZE);
ret = __i2c_write(ppd, target, QSFP_DEV, ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1); QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
if (ret != 1) { if (ret != 1) {
hfi1_dev_porterr( hfi1_dev_porterr(
...@@ -227,8 +240,8 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, ...@@ -227,8 +240,8 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
if (((addr % QSFP_RW_BOUNDARY) + nwrite) > QSFP_RW_BOUNDARY) if (((addr % QSFP_RW_BOUNDARY) + nwrite) > QSFP_RW_BOUNDARY)
nwrite = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY); nwrite = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY);
ret = __i2c_write(ppd, target, QSFP_DEV, offset, bp + count, ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
nwrite); offset, bp + count, nwrite);
if (ret <= 0) /* stop on error or nothing written */ if (ret <= 0) /* stop on error or nothing written */
break; break;
...@@ -260,13 +273,22 @@ int qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, ...@@ -260,13 +273,22 @@ int qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
if (ret) if (ret)
return ret; return ret;
/* make sure the TWSI bus is in a sane state */
ret = hfi1_twsi_reset(ppd->dd, target);
if (ret) {
hfi1_dev_porterr(ppd->dd, ppd->port,
"QSFP read interface reset failed\n");
mutex_unlock(&ppd->dd->qsfp_i2c_mutex);
return -EIO;
}
while (count < len) { while (count < len) {
/* /*
* Set the qsfp page based on a zero-based address * Set the qsfp page based on a zero-based address
* and a page size of QSFP_PAGESIZE bytes. * and a page size of QSFP_PAGESIZE bytes.
*/ */
page = (u8)(addr / QSFP_PAGESIZE); page = (u8)(addr / QSFP_PAGESIZE);
ret = __i2c_write(ppd, target, QSFP_DEV, ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1); QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
if (ret != 1) { if (ret != 1) {
hfi1_dev_porterr( hfi1_dev_porterr(
...@@ -283,8 +305,10 @@ int qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, ...@@ -283,8 +305,10 @@ int qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
if (((addr % QSFP_RW_BOUNDARY) + nread) > QSFP_RW_BOUNDARY) if (((addr % QSFP_RW_BOUNDARY) + nread) > QSFP_RW_BOUNDARY)
nread = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY); nread = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY);
ret = __i2c_read(ppd, target, QSFP_DEV, offset, bp + count, /* QSFPs require a 5-10msec delay after write operations */
nread); mdelay(5);
ret = __i2c_read(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
offset, bp + count, nread);
if (ret <= 0) /* stop on error or nothing read */ if (ret <= 0) /* stop on error or nothing read */
break; break;
......
...@@ -70,6 +70,10 @@ ...@@ -70,6 +70,10 @@
/* Reads/writes cannot cross 128 byte boundaries */ /* Reads/writes cannot cross 128 byte boundaries */
#define QSFP_RW_BOUNDARY 128 #define QSFP_RW_BOUNDARY 128
/* number of bytes in i2c offset for QSFP devices */
#define __QSFP_OFFSET_SIZE 1 /* num address bytes */
#define QSFP_OFFSET_SIZE (__QSFP_OFFSET_SIZE << 8) /* shifted value */
/* Defined fields that Intel requires of qualified cables */ /* Defined fields that Intel requires of qualified cables */
/* Byte 0 is Identifier, not checked */ /* Byte 0 is Identifier, not checked */
/* Byte 1 is reserved "status MSB" */ /* Byte 1 is reserved "status MSB" */
......
...@@ -365,17 +365,25 @@ static int twsi_wr(struct hfi1_devdata *dd, u32 target, int data, int flags) ...@@ -365,17 +365,25 @@ static int twsi_wr(struct hfi1_devdata *dd, u32 target, int data, int flags)
* HFI1_TWSI_NO_DEV and does the correct operation for the legacy part, * HFI1_TWSI_NO_DEV and does the correct operation for the legacy part,
* which responded to all TWSI device codes, interpreting them as * which responded to all TWSI device codes, interpreting them as
* address within device. On all other devices found on board handled by * address within device. On all other devices found on board handled by
* this driver, the device is followed by a one-byte "address" which selects * this driver, the device is followed by a N-byte "address" which selects
* the "register" or "offset" within the device from which data should * the "register" or "offset" within the device from which data should
* be read. * be read.
*/ */
int hfi1_twsi_blk_rd(struct hfi1_devdata *dd, u32 target, int dev, int addr, int hfi1_twsi_blk_rd(struct hfi1_devdata *dd, u32 target, int dev, int addr,
void *buffer, int len) void *buffer, int len)
{ {
int ret;
u8 *bp = buffer; u8 *bp = buffer;
int ret = 1;
int i;
int offset_size;
/* obtain the offset size, strip it from the device address */
offset_size = (dev >> 8) & 0xff;
dev &= 0xff;
ret = 1; /* allow at most a 2 byte offset */
if (offset_size > 2)
goto bail;
if (dev == HFI1_TWSI_NO_DEV) { if (dev == HFI1_TWSI_NO_DEV) {
/* legacy not-really-I2C */ /* legacy not-really-I2C */
...@@ -383,34 +391,29 @@ int hfi1_twsi_blk_rd(struct hfi1_devdata *dd, u32 target, int dev, int addr, ...@@ -383,34 +391,29 @@ int hfi1_twsi_blk_rd(struct hfi1_devdata *dd, u32 target, int dev, int addr,
ret = twsi_wr(dd, target, addr, HFI1_TWSI_START); ret = twsi_wr(dd, target, addr, HFI1_TWSI_START);
} else { } else {
/* Actual I2C */ /* Actual I2C */
ret = twsi_wr(dd, target, dev | WRITE_CMD, HFI1_TWSI_START); if (offset_size) {
ret = twsi_wr(dd, target,
dev | WRITE_CMD, HFI1_TWSI_START);
if (ret) { if (ret) {
stop_cmd(dd, target); stop_cmd(dd, target);
ret = 1;
goto bail; goto bail;
} }
/*
* SFF spec claims we do _not_ stop after the addr
* but simply issue a start with the "read" dev-addr.
* Since we are implicitly waiting for ACK here,
* we need t_buf (nominally 20uSec) before that start,
* and cannot rely on the delay built in to the STOP
*/
ret = twsi_wr(dd, target, addr, 0);
udelay(TWSI_BUF_WAIT_USEC);
for (i = 0; i < offset_size; i++) {
ret = twsi_wr(dd, target,
(addr >> (i * 8)) & 0xff, 0);
udelay(TWSI_BUF_WAIT_USEC);
if (ret) { if (ret) {
dd_dev_err(dd, dd_dev_err(dd, "Failed to write byte %d of offset 0x%04X\n",
"Failed to write interface read addr %02X\n", i, addr);
addr);
ret = 1;
goto bail; goto bail;
} }
}
}
ret = twsi_wr(dd, target, dev | READ_CMD, HFI1_TWSI_START); ret = twsi_wr(dd, target, dev | READ_CMD, HFI1_TWSI_START);
} }
if (ret) { if (ret) {
stop_cmd(dd, target); stop_cmd(dd, target);
ret = 1;
goto bail; goto bail;
} }
...@@ -442,19 +445,26 @@ int hfi1_twsi_blk_rd(struct hfi1_devdata *dd, u32 target, int dev, int addr, ...@@ -442,19 +445,26 @@ int hfi1_twsi_blk_rd(struct hfi1_devdata *dd, u32 target, int dev, int addr,
* HFI1_TWSI_NO_DEV and does the correct operation for the legacy part, * HFI1_TWSI_NO_DEV and does the correct operation for the legacy part,
* which responded to all TWSI device codes, interpreting them as * which responded to all TWSI device codes, interpreting them as
* address within device. On all other devices found on board handled by * address within device. On all other devices found on board handled by
* this driver, the device is followed by a one-byte "address" which selects * this driver, the device is followed by a N-byte "address" which selects
* the "register" or "offset" within the device to which data should * the "register" or "offset" within the device to which data should
* be written. * be written.
*/ */
int hfi1_twsi_blk_wr(struct hfi1_devdata *dd, u32 target, int dev, int addr, int hfi1_twsi_blk_wr(struct hfi1_devdata *dd, u32 target, int dev, int addr,
const void *buffer, int len) const void *buffer, int len)
{ {
int sub_len;
const u8 *bp = buffer; const u8 *bp = buffer;
int max_wait_time, i;
int ret = 1; int ret = 1;
int i;
int offset_size;
/* obtain the offset size, strip it from the device address */
offset_size = (dev >> 8) & 0xff;
dev &= 0xff;
/* allow at most a 2 byte offset */
if (offset_size > 2)
goto bail;
while (len > 0) {
if (dev == HFI1_TWSI_NO_DEV) { if (dev == HFI1_TWSI_NO_DEV) {
if (twsi_wr(dd, target, (addr << 1) | WRITE_CMD, if (twsi_wr(dd, target, (addr << 1) | WRITE_CMD,
HFI1_TWSI_START)) { HFI1_TWSI_START)) {
...@@ -462,56 +472,28 @@ int hfi1_twsi_blk_wr(struct hfi1_devdata *dd, u32 target, int dev, int addr, ...@@ -462,56 +472,28 @@ int hfi1_twsi_blk_wr(struct hfi1_devdata *dd, u32 target, int dev, int addr,
} }
} else { } else {
/* Real I2C */ /* Real I2C */
if (twsi_wr(dd, target, if (twsi_wr(dd, target, dev | WRITE_CMD, HFI1_TWSI_START))
dev | WRITE_CMD, HFI1_TWSI_START))
goto failed_write; goto failed_write;
ret = twsi_wr(dd, target, addr, 0); }
for (i = 0; i < offset_size; i++) {
ret = twsi_wr(dd, target, (addr >> (i * 8)) & 0xff, 0);
udelay(TWSI_BUF_WAIT_USEC);
if (ret) { if (ret) {
dd_dev_err(dd, dd_dev_err(dd, "Failed to write byte %d of offset 0x%04X\n",
"Failed to write interface write addr %02X\n", i, addr);
addr); goto bail;
goto failed_write;
} }
} }
sub_len = min(len, 4); for (i = 0; i < len; i++)
addr += sub_len;
len -= sub_len;
for (i = 0; i < sub_len; i++)
if (twsi_wr(dd, target, *bp++, 0)) if (twsi_wr(dd, target, *bp++, 0))
goto failed_write; goto failed_write;
stop_cmd(dd, target);
/*
* Wait for write complete by waiting for a successful
* read (the chip replies with a zero after the write
* cmd completes, and before it writes to the eeprom.
* The startcmd for the read will fail the ack until
* the writes have completed. We do this inline to avoid
* the debug prints that are in the real read routine
* if the startcmd fails.
* We also use the proper device address, so it doesn't matter
* whether we have real eeprom_dev. Legacy likes any address.
*/
max_wait_time = 100;
while (twsi_wr(dd, target,
dev | READ_CMD, HFI1_TWSI_START)) {
stop_cmd(dd, target);
if (!--max_wait_time)
goto failed_write;
}
/* now read (and ignore) the resulting byte */
rd_byte(dd, target, 1);
}
ret = 0; ret = 0;
goto bail;
failed_write: failed_write:
stop_cmd(dd, target); stop_cmd(dd, target);
ret = 1;
bail: bail:
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment