diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index bc37acd..fee4c66 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
@@ -156,6 +156,7 @@ struct cp2112_device {
 	wait_queue_head_t wait;
 	u8 read_data[61];
 	u8 read_length;
+	u8 hwversion;
 	int xfer_status;
 	atomic_t read_avail;
 	atomic_t xfer_avail;
@@ -427,6 +428,156 @@ static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data,
 	return data_length + 4;
 }
 
+static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data,
+                                u8 data_length)
+{
+        struct cp2112_write_req_report *report = buf;
+
+        if (data_length > sizeof(report->data))
+                return -EINVAL;
+
+        report->report = CP2112_DATA_WRITE_REQUEST;
+        report->slave_address = slave_address << 1;
+        report->length = data_length;
+        memcpy(report->data, data, data_length);
+        return data_length + 3;
+}
+
+static int cp2112_i2c_write_read_req(void *buf, u8 slave_address,
+				     u8 *addr, int addr_length,
+				     int read_length)
+{
+	struct cp2112_write_read_req_report *report = buf;
+
+	if (read_length < 1 || read_length > 512 ||
+	    addr_length > sizeof(report->target_address))
+		return -EINVAL;
+
+	report->report = CP2112_DATA_WRITE_READ_REQUEST;
+	report->slave_address = slave_address << 1;
+	report->length = cpu_to_be16(read_length);
+	report->target_address_length = addr_length;
+	memcpy(report->target_address, addr, addr_length);
+	return addr_length + 5;
+}
+
+static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+                           int num)
+{
+        struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data;
+        struct hid_device *hdev = dev->hdev;
+        u8 buf[64];
+        ssize_t count;
+        ssize_t read_length = 0;
+        u8 *read_buf = NULL;
+        unsigned int retries;
+        int ret;
+
+        hid_dbg(hdev, "I2C %d messages\n", num);
+
+        if (num == 1) {
+            if (msgs->flags & I2C_M_RD) {
+                hid_dbg(hdev, "I2C read %#04x len %d\n",
+                    msgs->addr, msgs->len);
+                read_length = msgs->len;
+                read_buf = msgs->buf;
+                count = cp2112_read_req(buf, msgs->addr, msgs->len);
+            } else {
+                hid_dbg(hdev, "I2C write %#04x len %d\n",
+                    msgs->addr, msgs->len);
+                count = cp2112_i2c_write_req(buf, msgs->addr,
+                                     msgs->buf, msgs->len);
+            }
+            if (count < 0)
+                return count;
+        } else if (dev->hwversion > 1 &&  /* no repeated start in rev 1 */
+               num == 2 &&
+               msgs[0].addr == msgs[1].addr &&
+               !(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) {
+            hid_dbg(hdev, "I2C write-read %#04x wlen %d rlen %d\n",
+                msgs[0].addr, msgs[0].len, msgs[1].len);
+            read_length = msgs[1].len;
+            read_buf = msgs[1].buf;
+            count = cp2112_i2c_write_read_req(buf, msgs[0].addr,
+                    msgs[0].buf, msgs[0].len, msgs[1].len);
+            if (count < 0)
+                return count;
+        } else {
+                hid_err(hdev,
+                        "Multi-message I2C transactions not supported\n");
+                return -EOPNOTSUPP;
+        }
+
+        ret = hid_hw_power(hdev, PM_HINT_FULLON);
+        if (ret < 0) {
+                hid_err(hdev, "power management error: %d\n", ret);
+                return ret;
+        }
+
+        ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT);
+        if (ret < 0) {
+                hid_warn(hdev, "Error starting transaction: %d\n", ret);
+                goto power_normal;
+        }
+
+        for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) {
+                ret = cp2112_xfer_status(dev);
+                if (-EBUSY == ret)
+                        continue;
+                if (ret < 0)
+                        goto power_normal;
+                break;
+        }
+
+        if (XFER_STATUS_RETRIES <= retries) {
+                hid_warn(hdev, "Transfer timed out, cancelling.\n");
+                buf[0] = CP2112_CANCEL_TRANSFER;
+                buf[1] = 0x01;
+
+                ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
+                if (ret < 0)
+                        hid_warn(hdev, "Error cancelling transaction: %d\n",
+                                 ret);
+
+                ret = -ETIMEDOUT;
+                goto power_normal;
+        }
+
+        for (count = 0; count < read_length;) {
+            ret = cp2112_read(dev, read_buf + count, read_length - count);
+            if (ret < 0)
+                goto power_normal;
+            if (ret == 0) {
+                hid_err(hdev, "read returned 0\n");
+                ret = -EIO;
+                goto power_normal;
+            }
+            count += ret;
+            if (count > read_length) {
+                /*
+                 * The hardware returned too much data.
+                 * This is mostly harmless because cp2112_read()
+                 * has a limit check so didn't overrun our
+                 * buffer.  Nevertheless, we return an error
+                 * because something is seriously wrong and
+                 * it shouldn't go unnoticed.
+                 */
+                hid_err(hdev, "long read: %d > %zd\n",
+                    ret, read_length - count + ret);
+                ret = -EIO;
+                goto power_normal;
+            }
+        }
+
+        /* return the number of transferred messages */
+        ret = num;
+
+power_normal:
+        hid_hw_power(hdev, PM_HINT_NORMAL);
+        hid_dbg(hdev, "I2C transfer finished: %d\n", ret);
+        return ret;
+}
+
 static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
 		       unsigned short flags, char read_write, u8 command,
 		       int size, union i2c_smbus_data *data)
@@ -593,7 +744,8 @@ power_normal:
 
 static u32 cp2112_functionality(struct i2c_adapter *adap)
 {
-	return I2C_FUNC_SMBUS_BYTE |
+		return I2C_FUNC_I2C |
+		I2C_FUNC_SMBUS_BYTE |
 		I2C_FUNC_SMBUS_BYTE_DATA |
 		I2C_FUNC_SMBUS_WORD_DATA |
 		I2C_FUNC_SMBUS_BLOCK_DATA |
@@ -603,6 +755,7 @@ static u32 cp2112_functionality(struct i2c_adapter *adap)
 }
 
 static const struct i2c_algorithm smbus_algorithm = {
+	.master_xfer    = cp2112_i2c_xfer,
 	.smbus_xfer	= cp2112_xfer,
 	.functionality	= cp2112_functionality,
 };
@@ -925,6 +1078,7 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	dev->adap.dev.parent	= &hdev->dev;
 	snprintf(dev->adap.name, sizeof(dev->adap.name),
 		 "CP2112 SMBus Bridge on hiddev%d", hdev->minor);
+	dev->hwversion = buf[2];
 	init_waitqueue_head(&dev->wait);
 
 	hid_device_io_start(hdev);

