i2c: Add i2c block transaction support

The previous code always uses SMBus block read/write.
On some PSU (e.g. FP5280G2's PSU) the I2C block read/write is
required, so add that support.

Specifically, add a Mode enum class and add the parameter for block
read/write to indicate whether SMBus or I2C block read/write is to be
called.

Tested: Verify the code works on FP5280G2 with I2C block write.

Note: Currently there is no case for I2C block read, so that function is
      not tested.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I5f77ffe6900d14f3703dae7241799a7b37c5a726
diff --git a/tools/i2c/i2c.cpp b/tools/i2c/i2c.cpp
index 76756be..624bc60 100644
--- a/tools/i2c/i2c.cpp
+++ b/tools/i2c/i2c.cpp
@@ -76,6 +76,13 @@
                                    devAddr);
             }
             break;
+        case I2C_SMBUS_I2C_BLOCK_DATA:
+            if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+            {
+                throw I2CException("Missing I2C_FUNC_SMBUS_READ_I2C_BLOCK",
+                                   busStr, devAddr);
+            }
+            break;
         default:
             fprintf(stderr, "Unexpected read size type: %d\n", type);
             assert(false);
@@ -123,6 +130,13 @@
                                    devAddr);
             }
             break;
+        case I2C_SMBUS_I2C_BLOCK_DATA:
+            if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
+            {
+                throw I2CException("Missing I2C_FUNC_SMBUS_WRITE_I2C_BLOCK",
+                                   busStr, devAddr);
+            }
+            break;
         default:
             fprintf(stderr, "Unexpected read size type: %d\n", type);
             assert(false);
@@ -164,11 +178,25 @@
     data = static_cast<uint16_t>(ret);
 }
 
-void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data)
+void I2CDevice::read(uint8_t addr, uint8_t& size, uint8_t* data, Mode mode)
 {
-    checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
-
-    int ret = i2c_smbus_read_block_data(fd, addr, data);
+    int ret;
+    switch (mode)
+    {
+        case Mode::SMBUS:
+            checkReadFuncs(I2C_SMBUS_BLOCK_DATA);
+            ret = i2c_smbus_read_block_data(fd, addr, data);
+            break;
+        case Mode::I2C:
+            checkReadFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
+            ret = i2c_smbus_read_i2c_block_data(fd, addr, size, data);
+            if (ret != size)
+            {
+                throw I2CException("Failed to read i2c block data", busStr,
+                                   devAddr, errno);
+            }
+            break;
+    }
     if (ret < 0)
     {
         throw I2CException("Failed to read block data", busStr, devAddr, errno);
@@ -206,11 +234,22 @@
     }
 }
 
-void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data)
+void I2CDevice::write(uint8_t addr, uint8_t size, const uint8_t* data,
+                      Mode mode)
 {
-    checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
-
-    if (i2c_smbus_write_block_data(fd, addr, size, data) < 0)
+    int ret;
+    switch (mode)
+    {
+        case Mode::SMBUS:
+            checkWriteFuncs(I2C_SMBUS_BLOCK_DATA);
+            ret = i2c_smbus_write_block_data(fd, addr, size, data);
+            break;
+        case Mode::I2C:
+            checkWriteFuncs(I2C_SMBUS_I2C_BLOCK_DATA);
+            ret = i2c_smbus_write_i2c_block_data(fd, addr, size, data);
+            break;
+    }
+    if (ret < 0)
     {
         throw I2CException("Failed to write block data", busStr, devAddr,
                            errno);