staging/rdma/hfi1: Fix for generic I2C interface
authorDean Luick <dean.luick@intel.com>
Wed, 3 Feb 2016 22:34:15 +0000 (14:34 -0800)
committerDoug Ledford <dledford@redhat.com>
Fri, 11 Mar 2016 01:37:53 +0000 (20:37 -0500)
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: Easwar Hariharan <easwar.hariharan@intel.com>
Reviewed-by: Dean Luick <dean.luick@intel.com>
Signed-off-by: Pablo Cacho <pablo.cacho@intel.com>
Signed-off-by: Jubin John <jubin.john@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/staging/rdma/hfi1/debugfs.c
drivers/staging/rdma/hfi1/qsfp.c
drivers/staging/rdma/hfi1/qsfp.h
drivers/staging/rdma/hfi1/twsi.c

index acd2269e9f1431e953b46ebe3e17a35af71deb64..d6dc339fb2a3a97b9fb630741abc7a98456ad8f5 100644 (file)
@@ -463,7 +463,8 @@ static ssize_t __i2c_debugfs_write(struct file *file, const char __user *buf,
                goto _free;
        }
 
-       i2c_addr = (*ppos >> 16) & 0xff;
+       /* byte offset format: [offsetSize][i2cAddr][offsetHigh][offsetLow] */
+       i2c_addr = (*ppos >> 16) & 0xffff;
        offset = *ppos & 0xffff;
 
        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,
                goto _return;
        }
 
-       i2c_addr = (*ppos >> 16) & 0xff;
+       /* byte offset format: [offsetSize][i2cAddr][offsetHigh][offsetLow] */
+       i2c_addr = (*ppos >> 16) & 0xffff;
        offset = *ppos & 0xffff;
 
        total_read = i2c_read(ppd, target, i2c_addr, offset, buff, count);
index 0d2ec972ea9fcccfc1fc8a5f5e40c91bfd89ccab..0e1a49294d9971555e5e12b045e96d318623b7a2 100644 (file)
@@ -71,14 +71,6 @@ static int __i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr,
        int ret, cnt;
        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;
        while (cnt < len) {
                int wlen = len - cnt;
@@ -106,11 +98,22 @@ int i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset,
        int ret;
 
        ret = mutex_lock_interruptible(&dd->qsfp_i2c_mutex);
-       if (!ret) {
-               ret = __i2c_write(ppd, target, i2c_addr, offset, bp, len);
-               mutex_unlock(&dd->qsfp_i2c_mutex);
+       if (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,
+                                "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;
 }
 
@@ -125,16 +128,6 @@ static int __i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr,
        int stuck = 0;
        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;
        while (cnt < len) {
                int rlen = len - cnt;
@@ -178,11 +171,22 @@ int i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset,
        int ret;
 
        ret = mutex_lock_interruptible(&dd->qsfp_i2c_mutex);
-       if (!ret) {
-               ret = __i2c_read(ppd, target, i2c_addr, offset, bp, len);
-               mutex_unlock(&dd->qsfp_i2c_mutex);
+       if (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,
+                                "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;
 }
 
@@ -203,6 +207,15 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
        if (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) {
                /*
                 * Set the qsfp page based on a zero-based addresss
@@ -210,8 +223,8 @@ int qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
                 */
                page = (u8)(addr / QSFP_PAGESIZE);
 
-               ret = __i2c_write(ppd, target, QSFP_DEV,
-                                       QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
+               ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
+                                 QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
                if (ret != 1) {
                        hfi1_dev_porterr(
                        ppd->dd,
@@ -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)
                        nwrite = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY);
 
-               ret = __i2c_write(ppd, target, QSFP_DEV, offset, bp + count,
-                                       nwrite);
+               ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
+                                 offset, bp + count, nwrite);
                if (ret <= 0)   /* stop on error or nothing written */
                        break;
 
@@ -260,14 +273,23 @@ int qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp,
        if (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) {
                /*
                 * Set the qsfp page based on a zero-based address
                 * and a page size of QSFP_PAGESIZE bytes.
                 */
                page = (u8)(addr / QSFP_PAGESIZE);
-               ret = __i2c_write(ppd, target, QSFP_DEV,
-                                       QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
+               ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE,
+                                 QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1);
                if (ret != 1) {
                        hfi1_dev_porterr(
                        ppd->dd,
@@ -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)
                        nread = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY);
 
-               ret = __i2c_read(ppd, target, QSFP_DEV, offset, bp + count,
-                                       nread);
+               /* QSFPs require a 5-10msec delay after write operations */
+               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 */
                        break;
 
index b1b9e4a2329f4f2c796fcf17a3fa70ffd5906715..af59a43b2d5f663b1ae98c1bdc50af9ab2c1a968 100644 (file)
 /* Reads/writes cannot cross 128 byte boundaries */
 #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 */
 /* Byte 0 is Identifier, not checked */
 /* Byte 1 is reserved "status MSB" */
index ea54fd2700adc3e0aede0f4aa6ca361a511fa868..7c579b343844224ae906ec471b10397e4cfaa90c 100644 (file)
@@ -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,
  * which responded to all TWSI device codes, interpreting them as
  * 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
  * be read.
  */
 int hfi1_twsi_blk_rd(struct hfi1_devdata *dd, u32 target, int dev, int addr,
                     void *buffer, int len)
 {
-       int ret;
        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) {
                /* legacy not-really-I2C */
@@ -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);
        } else {
                /* Actual I2C */
-               ret = twsi_wr(dd, target, dev | WRITE_CMD, HFI1_TWSI_START);
-               if (ret) {
-                       stop_cmd(dd, target);
-                       ret = 1;
-                       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);
+               if (offset_size) {
+                       ret = twsi_wr(dd, target,
+                                     dev | WRITE_CMD, HFI1_TWSI_START);
+                       if (ret) {
+                               stop_cmd(dd, target);
+                               goto bail;
+                       }
 
-               if (ret) {
-                       dd_dev_err(dd,
-                               "Failed to write interface read addr %02X\n",
-                               addr);
-                       ret = 1;
-                       goto bail;
+                       for (i = 0; i < offset_size; i++) {
+                               ret = twsi_wr(dd, target,
+                                             (addr >> (i * 8)) & 0xff, 0);
+                               udelay(TWSI_BUF_WAIT_USEC);
+                               if (ret) {
+                                       dd_dev_err(dd, "Failed to write byte %d of offset 0x%04X\n",
+                                                  i, addr);
+                                       goto bail;
+                               }
+                       }
                }
                ret = twsi_wr(dd, target, dev | READ_CMD, HFI1_TWSI_START);
        }
        if (ret) {
                stop_cmd(dd, target);
-               ret = 1;
                goto bail;
        }
 
@@ -442,76 +445,55 @@ bail:
  * HFI1_TWSI_NO_DEV and does the correct operation for the legacy part,
  * which responded to all TWSI device codes, interpreting them as
  * 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
  * be written.
  */
 int hfi1_twsi_blk_wr(struct hfi1_devdata *dd, u32 target, int dev, int addr,
                     const void *buffer, int len)
 {
-       int sub_len;
        const u8 *bp = buffer;
-       int max_wait_time, i;
        int ret = 1;
+       int i;
+       int offset_size;
 
-       while (len > 0) {
-               if (dev == HFI1_TWSI_NO_DEV) {
-                       if (twsi_wr(dd, target, (addr << 1) | WRITE_CMD,
-                                   HFI1_TWSI_START)) {
-                               goto failed_write;
-                       }
-               } else {
-                       /* Real I2C */
-                       if (twsi_wr(dd, target,
-                                   dev | WRITE_CMD, HFI1_TWSI_START))
-                               goto failed_write;
-                       ret = twsi_wr(dd, target, addr, 0);
-                       if (ret) {
-                               dd_dev_err(dd,
-                                       "Failed to write interface write addr %02X\n",
-                                       addr);
-                               goto failed_write;
-                       }
-               }
+       /* obtain the offset size, strip it from the device address */
+       offset_size = (dev >> 8) & 0xff;
+       dev &= 0xff;
 
-               sub_len = min(len, 4);
-               addr += sub_len;
-               len -= sub_len;
-
-               for (i = 0; i < sub_len; i++)
-                       if (twsi_wr(dd, target, *bp++, 0))
-                               goto failed_write;
+       /* allow at most a 2 byte offset */
+       if (offset_size > 2)
+               goto bail;
 
-               stop_cmd(dd, target);
+       if (dev == HFI1_TWSI_NO_DEV) {
+               if (twsi_wr(dd, target, (addr << 1) | WRITE_CMD,
+                           HFI1_TWSI_START)) {
+                       goto failed_write;
+               }
+       } else {
+               /* Real I2C */
+               if (twsi_wr(dd, target, dev | WRITE_CMD, HFI1_TWSI_START))
+                       goto failed_write;
+       }
 
-               /*
-                * 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;
+       for (i = 0; i < offset_size; i++) {
+               ret = twsi_wr(dd, target, (addr >> (i * 8)) & 0xff, 0);
+               udelay(TWSI_BUF_WAIT_USEC);
+               if (ret) {
+                       dd_dev_err(dd, "Failed to write byte %d of offset 0x%04X\n",
+                                  i, addr);
+                       goto bail;
                }
-               /* now read (and ignore) the resulting byte */
-               rd_byte(dd, target, 1);
        }
 
+       for (i = 0; i < len; i++)
+               if (twsi_wr(dd, target, *bp++, 0))
+                       goto failed_write;
+
        ret = 0;
-       goto bail;
 
 failed_write:
        stop_cmd(dd, target);
-       ret = 1;
 
 bail:
        return ret;