linux-gbmc: add i2c-usb driver

Tested: can build and boot without seeing any obvious issue.
Google-Bug-Id: 416173054
Change-Id: Id5f6f491490ddb38d22fed11f38df04fca39acba
Signed-off-by: Tom Tung <tomtung@google.com>
diff --git a/recipes-kernel/linux/files/0001-PRODKERNEL-platforms-i2c-I2C-over-USB-driver.patch b/recipes-kernel/linux/files/0001-PRODKERNEL-platforms-i2c-I2C-over-USB-driver.patch
new file mode 100644
index 0000000..cf9d1f6
--- /dev/null
+++ b/recipes-kernel/linux/files/0001-PRODKERNEL-platforms-i2c-I2C-over-USB-driver.patch
@@ -0,0 +1,1152 @@
+From 84df0f6737b2c0f3f35f89ae9052553b45b181a8 Mon Sep 17 00:00:00 2001
+From: Havard Skinnemoen <hskinnemoen@google.com>
+Date: Wed, 14 Aug 2013 17:14:49 -0700
+Subject: [PATCH] PRODKERNEL: platforms/i2c: I2C over USB driver
+
+This driver binds to a vendor-specific USB interface on Luftig and
+possibly other devices in the future. Each USB interface contains one or
+more i2c busses which are registered with the i2c core.
+
+See the design doc at go/usb-i2c for details.
+
+The following 9xx commits were squashed:
+
+3d807f35a4c6 drivers-i2c: I2C over USB driver
+543b3b681000 i2c: Update i2c-usb.c master_xfer to return values
+
+master_xfer callback requires us to return the number of messages sent
+instead of 0 on success. On 9xx, this was checked/enforced correctly
+on below commit:
+https://prodkernel.git.corp.google.com/kernel/release/9xx/+/refs/heads/staging/platforms/next/drivers/i2c/muxes/i2c-mux-pca954x.c#182
+
+Without this change i2c mux on plugin cards are not currently
+programmed.
+
+Tested:
+New kokonut test: go/kcl/36903
+
+Also tested against real hardware: luftig +
+a device with i2c mux + v340(A device without i2c mux).
+
+enri12:# gsys fru info
+enri12:# gsys sensor list
+
+Output from 11xx kernel is here:
+https://paste.googleplex.com/6258587744600064?raw
+Output from 9xx kernel for comparison:
+https://paste.googleplex.com/5327255258529792?raw
+
+Manually checked to make sure all the sensors are present on
+both and none are invalid.
+
+Effort: platforms/i2c
+Google-Bug-Id: 200639255
+Rebase-Tested-11xx: Ran i2c_usb test: http://sponge2/e0277f3e-d55d-490b-aa10-35eb0eadec95
+Upstream-Plan: 186466985 temporary for gsys
+Signed-off-by: Zhichuang Sun <zhichuang@google.com>
+
+Rebase-Tested-14xx: gbuild GTESTS=1
+
+Origin-13xx-SHA1: a728f231a9391100390c2e5fa9932562f734a423
+Change-Id: I989800e133a27b4be5db290848f55d70a8739a63
+Signed-off-by: Tom Tung <shes050117@gmail.com>
+---
+ drivers/i2c/busses/Kconfig   |   10 +
+ drivers/i2c/busses/Makefile  |    1 +
+ drivers/i2c/busses/i2c-usb.c | 1051 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 1062 insertions(+)
+ create mode 100644 drivers/i2c/busses/i2c-usb.c
+
+diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
+index 97d27e01a6ee..f224e059ba03 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -301,6 +301,16 @@ config I2C_SIS96X
+ 	  This driver can also be built as a module.  If so, the module
+ 	  will be called i2c-sis96x.
+ 
++config I2C_USB
++	tristate "I2C over USB"
++	depends on USB
++	help
++	  This driver supports the Google-specific I2C-over-USB interface
++	  described by go/usb-i2c.
++
++	  This driver can also be built as a module. If so, the module will be
++	  called i2c-usb.
++
+ config I2C_VIA
+ 	tristate "VIA VT82C586B"
+ 	depends on PCI
+diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
+index 9be9fdb07f3d..6c3b2a4bcabb 100644
+--- a/drivers/i2c/busses/Makefile
++++ b/drivers/i2c/busses/Makefile
+@@ -137,6 +137,7 @@ obj-$(CONFIG_I2C_PCI1XXXX)	+= i2c-mchp-pci1xxxx.o
+ obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF)	+= i2c-robotfuzz-osif.o
+ obj-$(CONFIG_I2C_TAOS_EVM)	+= i2c-taos-evm.o
+ obj-$(CONFIG_I2C_TINY_USB)	+= i2c-tiny-usb.o
++obj-$(CONFIG_I2C_USB)		+= i2c-usb.o
+ obj-$(CONFIG_I2C_VIPERBOARD)	+= i2c-viperboard.o
+ 
+ # Other I2C/SMBus bus drivers
+diff --git a/drivers/i2c/busses/i2c-usb.c b/drivers/i2c/busses/i2c-usb.c
+new file mode 100644
+index 000000000000..29210020ea7b
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-usb.c
+@@ -0,0 +1,1051 @@
++/*
++ * I2C-over-USB bus driver. Design doc: go/usb-i2c
++ *
++ * This drivers supports a limited subset of generic i2c commands which is
++ * enough to emulate all SMBus transfer types as well as a few non-standard
++ * ones (e.g. multi-byte commands).
++ */
++#include <linux/atomic.h>
++#include <linux/completion.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++#include <linux/usb/ch9.h>
++
++/* Max time to wait for a control request without data */
++#define I2C_USB_CTRL_TIMEOUT_MS		100
++/* Max time to wait for a control request expected to return data */
++#define I2C_USB_CTRL_READ_TIMEOUT_MS	200
++
++/*
++ * We use a vendor-defined class (0xff), so we need to match on the vendor ID
++ * and subclass in addition to the class.
++ */
++#define USB_DEVICE_ID_MATCH_VENDOR_INT_SUBCLASS			\
++		(USB_DEVICE_ID_MATCH_VENDOR			\
++		| USB_DEVICE_ID_MATCH_INT_CLASS			\
++		| USB_DEVICE_ID_MATCH_INT_SUBCLASS)
++
++#define USB_VENDOR_ID_GOOGLE		0x18d1
++
++/* Vendor-specific descriptor types */
++#define DT_I2C_CAPS			0x40
++#define DT_I2C_BUS			0x41
++
++/* bmRequestType values, excluding direction */
++#define I2C_USB_REQTYPE_BUS		(USB_TYPE_VENDOR | USB_RECIP_OTHER)
++
++/* Vendor-specific bRequest values */
++#define I2C_USB_REQ_BEGIN_XFER		10
++#define I2C_USB_REQ_GET_XFER_RESULT	13
++
++/* Status/feature bits */
++#define I2C_USB_F_LOOPBACK		0
++#define I2C_USB_F_BUS_RESET		1
++#define I2C_USB_F_BUS_ERROR		2
++#define I2C_USB_F_SMBALERT		3
++#define I2C_USB_F_XFER_DATA		4
++
++/* Supported I2C transfer types */
++#define I2C_USB_XFER_READ		0
++#define I2C_USB_XFER_WRITE		1
++#define I2C_USB_XFER_WRITE_THEN_READ	2
++
++/* Notification types */
++#define I2C_USB_NOTIFY_XFER_COMPLETE	1
++
++/* Notification error codes */
++#define I2C_USB_STATUS_OK		0
++#define I2C_USB_STATUS_NAK		1
++#define I2C_USB_STATUS_TIMEOUT		2
++#define I2C_USB_STATUS_BUS_ERROR	3
++#define I2C_USB_STATUS_PEC_FAIL		4
++
++/*
++ * On-wire format of the I2C Capabilities Functional Descriptor. This is read
++ * from the device as part of the interface descriptor.
++ */
++struct i2c_usb_caps_func_desc {
++	u8	bLength;
++	u8	bDescriptorType;
++	__le16	bcdI2C;
++	__le16	wMaxWriteLength;
++	__le16	wMaxReadLength;
++	u8	bNumberOfBusses;
++	u8	bQueueLength;
++} __packed;
++
++/*
++ * On-wire format of the I2C Bus Functional Descriptor. This is read from the
++ * device as part of the interface descriptor.
++ */
++struct i2c_usb_bus_func_desc {
++	u8	bLength;
++	u8	bDescriptorType;
++	u8	bFirstBusNum;
++	u8	bLastBusNum;
++	__le16	wMinClockRate;
++	__le16	wMaxClockRate;
++	__le16	wMaxSlaveTimeout;
++	u8	bmCapabilities;
++} __packed;
++
++/*
++ * On-wire format of the Transfer Descriptor. This is sent to the device in the
++ * Data-OUT phase of the BeginTransfer request. If wWriteLength is non-zero,
++ * the data payload follows directly after this descriptor.
++ */
++struct i2c_usb_transfer_desc {
++	u8	bLength;
++	u8	bTransferType;
++	__le16	wTransferTag;
++	__le16	wSlaveAddress;
++	__le16	wWriteLength;
++	__le16	wReadLength;
++} __packed;
++
++/*
++ * On-wire format of the Notification Descriptor. This is received from the
++ * notification endpoint of the device upon certain bus events. If wDataLength
++ * is non-zero, the data payload follows directly after this descriptor.
++ */
++struct i2c_usb_notification_desc {
++	u8	bLength;
++	u8	bNotificationType;
++	u8	bBusNumber;
++	u8	bErrorCode;
++	__le16	wTransferTag;
++	__le16	wSlaveAddress;
++	__le16	wDataLength;
++} __packed;
++
++/* Maximum length of a notification packet. */
++#define MAX_NOTIFY_LEN	\
++	(sizeof(struct i2c_usb_notification_desc) + I2C_SMBUS_BLOCK_MAX)
++
++/**
++ * struct i2c_usb_transfer - host representation of a I2C transfer.
++ * @complete Completion indicating that the transfer is complete.
++ * @status Transfer status / error code.
++ * @data_len Number of bytes data payload reported by device.
++ * @tag Tag of the transfer which was reported as completed.
++ */
++struct i2c_usb_transfer {
++	struct completion complete;
++	int	status;
++	size_t	data_len;
++	u16	tag;
++};
++
++/**
++ * struct i2c_usb_bus - I2C bus managed by a I2C USB interface.
++ * @adap I2C adapter registered with the I2C subsystem.
++ * @usbdev USB device to be used for control transfers.
++ * @index wIndex value (in native byte order) to be used for control transfers.
++ *
++ * Each I2C USB interface may have multiple busses. These busses share the same
++ * USB interface and are subject to overall queue length limitations.
++ * Typically, the maximum queue length will be 1, which means that only one bus
++ * can perform an I2C transfer at a time.
++ */
++struct i2c_usb_bus {
++	struct i2c_adapter adap;
++	struct usb_device *usbdev;
++	u16 index;
++};
++
++/**
++ * struct i2c_usb_data - The I2C USB interface.
++ * @tag_gen: Atomic counter for generating wTransferTag for transfers.
++ * @lock: Lock protecting the interface against concurrent access by
++ *	multiple busses.
++ * @disconnected: True if the I2C USB device was disconnected and is
++ *	being cleaned up. All new transfer requests should be aborted.
++ * @xfer Transfer currently in progress. There may only be one transfer in
++ *	progress at any time.
++ * @notify_buf: Buffer for receiving notification interrupts from the device.
++ * @notify_urb: URB used to receive notification interrupts.
++ * @intf: The underlying USB interface.
++ * @max_read_len: Maximum number of bytes which can be read in a single
++ *	request. This limit is set by the device.
++ * @max_write_len: Maximum number of bytes which can be written in a single
++ *	request. This limit is set by the device.
++ * @nr_busses: Number of I2C busses managed by this interface.
++ * @queue_len: Maximum number of pending requests which can be submitted to
++ *	the interface.
++ * @busses: The I2C busses managed by this interface. This is an array of
++ *	length nr_busses.
++ */
++struct i2c_usb_data {
++	atomic_t tag_gen;
++	struct mutex lock;
++	bool disconnected;
++	struct i2c_usb_transfer xfer;
++	struct i2c_usb_notification_desc *notify_buf;
++	struct urb *notify_urb;
++	struct usb_interface *intf;
++	u16 spec_version;
++	unsigned int max_read_len;
++	unsigned int max_write_len;
++	unsigned int nr_busses;
++	unsigned int queue_len;
++	struct i2c_usb_bus *busses;
++};
++
++static const struct usb_device_id i2c_usb_devices[] = {
++	{
++		.match_flags = USB_DEVICE_ID_MATCH_VENDOR_INT_SUBCLASS,
++		.idVendor = USB_VENDOR_ID_GOOGLE,
++		.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
++		.bInterfaceSubClass = 0x21,  /* go/usb-ids */
++	},
++	{ 0 } /* terminating entry */
++};
++
++static inline struct i2c_usb_bus *to_i2c_usb_bus(struct i2c_adapter *adap)
++{
++	return container_of(adap, struct i2c_usb_bus, adap);
++}
++
++/* Generate a new wTransferTag value for use in our transfer descriptor */
++static __le16 i2c_usb_gen_tag(struct i2c_usb_data *intf)
++{
++	return cpu_to_le16(atomic_inc_return(&intf->tag_gen));
++}
++
++static int i2c_usb_get_bus_status(struct i2c_usb_bus *bus)
++{
++	struct usb_device *usbdev = bus->usbdev;
++	int ret;
++	__le16 status;
++	u16 *buf;
++
++	buf = kzalloc(sizeof(u16), GFP_KERNEL);
++	if (!buf)
++		return -ENOMEM;
++
++	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
++			USB_REQ_GET_STATUS, I2C_USB_REQTYPE_BUS | USB_DIR_IN,
++			0, bus->index, buf, sizeof(u16),
++			I2C_USB_CTRL_READ_TIMEOUT_MS);
++
++	status = *buf;
++	kfree(buf);
++
++	if (ret < 0) {
++		dev_err(&bus->adap.dev, "GetBusStatus(%04x) failed: %d\n",
++				bus->index, ret);
++		return ret;
++	}
++	return le16_to_cpu(status);
++}
++
++static int i2c_usb_clear_bus_feature(struct i2c_usb_bus *bus, u16 feature)
++{
++	struct usb_device *usbdev = bus->usbdev;
++	int ret;
++
++	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
++			USB_REQ_CLEAR_FEATURE,
++			I2C_USB_REQTYPE_BUS | USB_DIR_OUT,
++			feature, bus->index, NULL, 0, I2C_USB_CTRL_TIMEOUT_MS);
++	if (ret < 0) {
++		dev_err(&bus->adap.dev,
++				"ClearBusFeature(%u, %04x) failed: %d\n",
++				feature, bus->index, ret);
++		return ret;
++	}
++	return 0;
++}
++
++static int i2c_usb_set_bus_feature(struct i2c_usb_bus *bus, u16 feature)
++{
++	struct usb_device *usbdev = bus->usbdev;
++	int ret;
++
++	ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
++			USB_REQ_SET_FEATURE,
++			I2C_USB_REQTYPE_BUS | USB_DIR_OUT,
++			feature, bus->index, NULL, 0, I2C_USB_CTRL_TIMEOUT_MS);
++	if (ret < 0) {
++		dev_err(&bus->adap.dev,
++				"SetBusFeature(%u, %04x) failed: %d\n",
++				feature, bus->index, ret);
++		return ret;
++	}
++	return 0;
++}
++
++/*
++ * SysFS Attributes. In loopback mode, the device will silently accept and
++ * discard writes, return zeroes for all reads, and echo back the written data
++ * in any write-then-read requests.
++ */
++static ssize_t i2c_usb_show_loopback(struct device *dev,
++		struct device_attribute *attr, char *buf)
++{
++	struct i2c_usb_bus *bus = to_i2c_usb_bus(to_i2c_adapter(dev));
++	int status;
++
++	status = i2c_usb_get_bus_status(bus);
++	if (status < 0)
++		return status;
++	return scnprintf(buf, PAGE_SIZE, "%d\n", status & 1);
++}
++
++static ssize_t i2c_usb_store_loopback(struct device *dev,
++		struct device_attribute *attr, const char *buf, size_t count)
++{
++	struct i2c_usb_bus *bus = to_i2c_usb_bus(to_i2c_adapter(dev));
++	int enabled;
++	int ret;
++
++	ret = kstrtoint(buf, 10, &enabled);
++	if (ret < 0)
++		return ret;
++
++	switch (enabled) {
++	case 0:
++		ret = i2c_usb_clear_bus_feature(bus, I2C_USB_F_LOOPBACK);
++		break;
++	case 1:
++		ret = i2c_usb_set_bus_feature(bus, I2C_USB_F_LOOPBACK);
++		break;
++	default:
++		ret = -EINVAL;
++		break;
++	}
++
++	if (ret < 0)
++		return ret;
++
++	return strnlen(buf, count);
++}
++static DEVICE_ATTR(loopback, S_IRUGO | S_IWUSR, i2c_usb_show_loopback,
++		i2c_usb_store_loopback);
++
++static struct attribute *i2c_usb_attrs[] = {
++	&dev_attr_loopback.attr,
++	NULL,
++};
++static const struct attribute_group i2c_usb_attr_group = {
++	.attrs = i2c_usb_attrs,
++};
++static const struct attribute_group *i2c_usb_attr_groups[] = {
++	&i2c_usb_attr_group,
++	NULL,
++};
++
++static void i2c_usb_process_notification(struct i2c_usb_data *priv,
++		struct i2c_usb_notification_desc *nd,
++		size_t actual_len)
++{
++	struct i2c_usb_bus *bus;
++	unsigned int bus_no;
++
++	if (actual_len < sizeof(*nd)) {
++		dev_warn(&priv->intf->dev, "short notification: %zu < %lu\n",
++				actual_len, sizeof(*nd));
++		return;
++	}
++
++	bus_no = nd->bBusNumber;
++	if (bus_no >= priv->nr_busses) {
++		dev_warn(&priv->intf->dev, "notification for bad bus #%d\n",
++				bus_no);
++		return;
++	}
++	bus = &priv->busses[bus_no];
++
++	if (nd->bNotificationType != I2C_USB_NOTIFY_XFER_COMPLETE) {
++		dev_info(&priv->intf->dev, "unhandled notification type: %u\n",
++				nd->bNotificationType);
++		return;
++	}
++
++	priv->xfer.tag = le16_to_cpu(nd->wTransferTag);
++	priv->xfer.data_len = 0;
++
++	switch (nd->bErrorCode) {
++	case I2C_USB_STATUS_OK:
++		priv->xfer.status = 0;
++		priv->xfer.data_len = le16_to_cpu(nd->wDataLength);
++		dev_vdbg(&bus->adap.dev, "Transfer OK with %zu bytes payload\n",
++				priv->xfer.data_len);
++		break;
++	case I2C_USB_STATUS_NAK:
++		priv->xfer.data_len = le16_to_cpu(nd->wDataLength);
++		priv->xfer.status = priv->xfer.data_len ? 0 : -ENXIO;
++		dev_vdbg(&bus->adap.dev, "Transfer NAKed with %zu bytes payload\n",
++				priv->xfer.data_len);
++		break;
++	case I2C_USB_STATUS_TIMEOUT:
++		dev_dbg(&bus->adap.dev, "Transfer timed out\n");
++		priv->xfer.status = -ETIMEDOUT;
++		break;
++	case I2C_USB_STATUS_BUS_ERROR:
++	default:
++		dev_dbg(&bus->adap.dev, "Transfer error: %u\n", nd->bErrorCode);
++		priv->xfer.status = -EIO;
++		break;
++	}
++
++	complete(&priv->xfer.complete);
++}
++
++/* Interrupt handler. Called whenever our interrupt URB gets filled. */
++static void i2c_usb_notify(struct urb *urb)
++{
++	struct i2c_usb_data *priv = urb->context;
++	struct i2c_usb_notification_desc *nd = urb->transfer_buffer;
++	int status = urb->status;
++
++	switch (status) {
++	case 0:
++		i2c_usb_process_notification(priv, nd, urb->actual_length);
++		break;
++	case -ECONNRESET:
++	case -ENOENT:
++	case -ESHUTDOWN:
++		/* urb killed because device went away or we're shutting down */
++		if (priv->xfer.status == -EINPROGRESS) {
++			priv->xfer.status = status;
++			complete(&priv->xfer.complete);
++		}
++		return;
++	default:
++		dev_warn(&priv->intf->dev, "notification error: %d\n", status);
++		break;
++	}
++
++	status = usb_submit_urb(urb, GFP_ATOMIC);
++	if (status) {
++		dev_err(&priv->intf->dev, "failed to resubmit notify urb: %d\n",
++				status);
++	}
++}
++
++static void i2c_usb_recover(struct i2c_usb_bus *bus)
++{
++	int status;
++
++	status = i2c_usb_get_bus_status(bus);
++	if (status < 0) {
++		dev_err(&bus->adap.dev,
++				"Failed to get bus status: %d\n", status);
++		return;
++	}
++	dev_info(&bus->adap.dev, "Trying to recover from status=0x%x...\n",
++			status);
++	if (status & (1 << I2C_USB_F_XFER_DATA)) {
++		dev_info(&bus->adap.dev, "Clearing XFER_DATA status...\n");
++		/* Best effort */
++		i2c_usb_clear_bus_feature(bus, I2C_USB_F_XFER_DATA);
++	}
++	if (status & (1 << I2C_USB_F_BUS_ERROR)) {
++		dev_err(&bus->adap.dev,
++				"Bus is stuck. Can't recover from this yet.");
++		/*
++		 * TODO(hskinnemoen): Determine if firmware supports Bus Reset
++		 * and if so, do it here.
++		 */
++	}
++}
++
++static int i2c_usb_transfer(
++		struct i2c_usb_bus *bus, struct i2c_usb_transfer_desc *xfer,
++		void *out_buf, size_t out_len,
++		void *read_buf, size_t read_len, size_t *actual_read)
++{
++	struct i2c_usb_data *priv;
++	struct usb_device *usbdev;
++	int ret = -ENOMEM;
++	u16 status;
++	size_t wlen;
++	size_t rlen;
++	void *wbuf;
++	void *rbuf;
++
++	priv = i2c_get_adapdata(&bus->adap);
++	mutex_lock(&priv->lock);
++
++	wlen = sizeof(*xfer) + out_len;
++	wbuf = kmalloc(wlen, GFP_KERNEL);
++	rlen = read_len;
++	rbuf = kmalloc(rlen, GFP_KERNEL);
++	if (!wbuf || !rbuf)
++		goto out;
++
++	memcpy(wbuf, xfer, sizeof(*xfer));
++	if (out_buf)
++		memcpy(wbuf + sizeof(*xfer), out_buf, out_len);
++
++	priv->xfer.status = -EINPROGRESS;
++	init_completion(&priv->xfer.complete);
++
++	/*
++	 * Other core may complete() right after we read disconnected, so make
++	 * sure init_completion() is visible before that.
++	 */
++	smp_mb();
++
++	if (priv->disconnected) {
++		dev_dbg(&bus->adap.dev,
++			"device disconnected; won't start new transfer\n");
++		ret = -ENODEV;
++		goto out;
++	}
++	usbdev = bus->usbdev;
++
++	/*
++	 * Check write and read lengths can be handled by the bus controller
++	 */
++	if (wlen - sizeof(struct i2c_usb_transfer_desc) >
++						priv->max_write_len) {
++		dev_err(&bus->adap.dev, "Write too big:%lu\n",
++			wlen - sizeof(struct i2c_usb_transfer_desc));
++		ret = -EMSGSIZE;
++		goto out;
++	}
++	if (rlen > priv->max_read_len) {
++		dev_err(&bus->adap.dev, "Read too big:%zu\n", rlen);
++		ret = -EMSGSIZE;
++		goto out;
++	}
++
++	ret = i2c_usb_get_bus_status(bus);
++	if (ret < 0)
++		goto out;
++	status = ret;
++	dev_dbg(&bus->adap.dev, "Bus status: 0x%x\n", status);
++	if (status & (1 << I2C_USB_F_XFER_DATA)) {
++		dev_warn(&bus->adap.dev,
++			"previous transfer left buffer dirty; flushing it.\n");
++		ret = i2c_usb_clear_bus_feature(bus, I2C_USB_F_XFER_DATA);
++		if (ret < 0)
++			goto out;
++	}
++	ret = usb_control_msg(
++			usbdev, usb_sndctrlpipe(usbdev, 0),
++			I2C_USB_REQ_BEGIN_XFER,
++			I2C_USB_REQTYPE_BUS | USB_DIR_OUT, 0, bus->index,
++			wbuf, wlen, jiffies_to_msecs(bus->adap.timeout));
++	if (ret < 0) {
++		dev_err(&bus->adap.dev, "BEGIN_XFER failed: %d\n", ret);
++		goto out;
++	}
++
++	ret = wait_for_completion_timeout(
++			&priv->xfer.complete, bus->adap.timeout);
++	if (ret == 0) {
++		dev_err(&bus->adap.dev, "i2c transfer timed out\n");
++		i2c_usb_recover(bus);
++		ret = -ETIMEDOUT;
++		goto out;
++	}
++	ret = i2c_usb_get_bus_status(bus);
++	if (ret < 0) {
++		dev_err(&bus->adap.dev, "Failed to get bus status: %d\n", ret);
++		goto out;
++	}
++	status = ret;
++	ret = priv->xfer.status;
++	if (!ret && rlen > 0 && (status & (1 << I2C_USB_F_XFER_DATA))) {
++		ret = usb_control_msg(
++				usbdev, usb_rcvctrlpipe(usbdev, 0),
++				I2C_USB_REQ_GET_XFER_RESULT,
++				I2C_USB_REQTYPE_BUS | USB_DIR_IN,
++				priv->xfer.tag, bus->index, rbuf, rlen,
++				jiffies_to_msecs(bus->adap.timeout));
++		if (ret < 0) {
++			dev_err(&bus->adap.dev,
++					"GET_XFER_RESULT failed: %d\n", ret);
++		} else {
++			if (actual_read)
++				*actual_read = ret;
++			ret = 0;
++		}
++	} else if (status & (1 << I2C_USB_F_XFER_DATA)) {
++		/* We don't care about the data; just clear it. */
++		int ret2 = i2c_usb_clear_bus_feature(bus, I2C_USB_F_XFER_DATA);
++		if (ret2 < 0) {
++			dev_err(&bus->adap.dev,
++				"Failed to clear XFER_DATA: %d\n", ret2);
++		}
++	} else if (!ret && rlen > 0) {
++		/* Expected data but didn't get any. */
++		ret = -EREMOTEIO;
++	}
++
++	if (read_buf)
++		memcpy(read_buf, rbuf, read_len);
++
++out:
++	kfree(wbuf);
++	kfree(rbuf);
++	priv->xfer.status = 0;
++	mutex_unlock(&priv->lock);
++	return ret;
++}
++
++static int i2c_usb_read(
++		struct i2c_usb_bus *bus, struct i2c_usb_transfer_desc *xfer,
++		void *data, size_t len, size_t *actual_read)
++{
++	xfer->bTransferType = I2C_USB_XFER_READ;
++	xfer->wReadLength = cpu_to_le16(len);
++
++	return i2c_usb_transfer(bus, xfer, NULL, 0,
++				data, len, actual_read);
++}
++
++static int i2c_usb_write_common(
++		struct i2c_usb_bus *bus, struct i2c_usb_transfer_desc *xfer,
++		void *write_data, size_t write_len,
++		void *read_data, size_t read_len, size_t *actual_read)
++{
++	return i2c_usb_transfer(bus, xfer, write_data, write_len,
++					read_data, read_len, actual_read);
++}
++
++static int i2c_usb_write_then_read(
++		struct i2c_usb_bus *bus, struct i2c_usb_transfer_desc *xfer,
++		void *write_data, size_t write_len,
++		void *read_data, size_t read_len, size_t *actual_read)
++{
++	xfer->bTransferType = I2C_USB_XFER_WRITE_THEN_READ;
++	xfer->wWriteLength = cpu_to_le16(write_len);
++	xfer->wReadLength = cpu_to_le16(read_len);
++
++	return i2c_usb_write_common(bus, xfer, write_data, write_len,
++			read_data, read_len, actual_read);
++}
++
++static int i2c_usb_write(
++		struct i2c_usb_bus *bus, struct i2c_usb_transfer_desc *xfer,
++		void *data, size_t len)
++{
++	xfer->bTransferType = I2C_USB_XFER_WRITE;
++	xfer->wWriteLength = cpu_to_le16(len);
++
++	return i2c_usb_write_common(bus, xfer, data, len, NULL, 0, NULL);
++}
++
++static int i2c_usb_master_xfer(
++		struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
++{
++	struct i2c_usb_bus *bus = to_i2c_usb_bus(adap);
++	struct i2c_usb_data *priv = i2c_get_adapdata(adap);
++	int ret = 0;
++	struct i2c_usb_transfer_desc transfer = {
++		.bLength	= sizeof(struct i2c_usb_transfer_desc),
++	};
++
++	transfer.wTransferTag = i2c_usb_gen_tag(priv);
++
++	/* There are certain restrictions we have to deal with */
++	switch (num) {
++	case 1:
++		transfer.wSlaveAddress = cpu_to_le16(msgs[0].addr);
++		if (msgs[0].flags & I2C_M_RD) {
++			size_t actual_read;
++			ret = i2c_usb_read(bus, &transfer, msgs[0].buf,
++					msgs[0].len, &actual_read);
++			if (!ret) {
++				/* Update the length with the actual size */
++				msgs[0].len = actual_read;
++			}
++		} else {
++			ret = i2c_usb_write(bus, &transfer, msgs[0].buf,
++					msgs[0].len);
++		}
++		break;
++	case 2:
++		/* Must be write-then-read to the same address */
++		if (msgs[0].flags & I2C_M_RD)
++			return -EINVAL;
++		if (!(msgs[1].flags & I2C_M_RD))
++			return -EINVAL;
++		if (msgs[0].addr != msgs[1].addr)
++			return -EINVAL;
++		transfer.wSlaveAddress = cpu_to_le16(msgs[0].addr);
++		if (msgs[1].flags & I2C_M_RECV_LEN) {
++			/*
++			 * real data length will be in byte 0 of read block.
++			 * We're guaranteed space for I2C_SMBUS_BLOCK_MAX + 1
++			 * bytes in msgs[1].buf.
++			 */
++			size_t actual_read;
++			ret = i2c_usb_write_then_read(
++				bus, &transfer, msgs[0].buf, msgs[0].len,
++				msgs[1].buf, I2C_SMBUS_BLOCK_MAX + 1,
++				&actual_read);
++			if (!ret) {
++				/*
++				 * Check for garbage, and update the length
++				 * with the actual size.
++				 */
++				if (msgs[1].buf[0] > I2C_SMBUS_BLOCK_MAX) {
++					dev_err(&adap->dev,
++						"bad block length:%d %zu\n",
++						msgs[1].buf[0], actual_read);
++					ret = -EPROTO;
++				} else {
++					msgs[1].len = min(actual_read,
++						(size_t)msgs[1].buf[0] + 1);
++				}
++			}
++		} else {
++			size_t actual_read;
++			ret = i2c_usb_write_then_read(
++				bus, &transfer, msgs[0].buf, msgs[0].len,
++				msgs[1].buf, msgs[1].len, &actual_read);
++			if (!ret) {
++				/* Update the length with the actual size */
++				msgs[1].len = actual_read;
++			}
++		}
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	/* On success, we need to return the number of messages transferred. */
++	if (ret == 0)
++		ret = num;
++	return ret;
++}
++
++static u32 i2c_usb_functionality(struct i2c_adapter *adap)
++{
++	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
++}
++
++static const struct i2c_algorithm i2c_usb_algorithm = {
++	.master_xfer	= i2c_usb_master_xfer,
++	.functionality	= i2c_usb_functionality,
++};
++
++/*
++ * Initialize notification (interrupt) endpoint. This will prepare the
++ * interrupt URB but not submit it.
++ */
++static int i2c_usb_init_notify_ep(struct i2c_usb_data *priv,
++		struct usb_host_interface *altsetting)
++{
++	struct usb_interface *intf = priv->intf;
++	struct usb_device *usb_dev = interface_to_usbdev(intf);
++	const struct usb_endpoint_descriptor *epd;
++	int notify_ep_len;
++
++	/* Verify the notification endpoint */
++	if (altsetting->desc.bNumEndpoints < 1) {
++		dev_dbg(&intf->dev, "No endpoints\n");
++		return -EINVAL;
++	}
++	epd = &altsetting->endpoint->desc;
++	if (!usb_endpoint_is_int_in(epd)) {
++		dev_dbg(&intf->dev, "Bad notify endpoint (bmAttributes=%02x)\n",
++				epd->bmAttributes);
++		return -EINVAL;
++	}
++	notify_ep_len = usb_endpoint_maxp(epd);
++	if (notify_ep_len < sizeof(struct i2c_usb_notification_desc)) {
++		dev_dbg(&intf->dev, "Notify endpoint too small: %d < %lu\n",
++				notify_ep_len,
++				sizeof(struct i2c_usb_notification_desc));
++		return -EINVAL;
++	}
++	if (epd->bInterval < 1) {
++		dev_dbg(&intf->dev, "Bad notify polling interval %d\n",
++				epd->bInterval);
++		return -EINVAL;
++	}
++
++	/* Descriptor seems legit. Let's prepare the notification URB */
++	priv->notify_buf = kzalloc(MAX_NOTIFY_LEN, GFP_KERNEL);
++	if (!priv->notify_buf)
++		return -ENOMEM;
++
++	usb_fill_int_urb(priv->notify_urb, usb_dev,
++			usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
++			priv->notify_buf, MAX_NOTIFY_LEN,
++			i2c_usb_notify, priv, epd->bInterval);
++
++	return 0;
++}
++
++/* Parse a capabilities descriptor. */
++static int i2c_usb_parse_caps(struct i2c_usb_data *priv, void *data)
++{
++	struct i2c_usb_caps_func_desc *capd = data;
++	struct usb_interface *intf = priv->intf;
++	struct usb_device *device = interface_to_usbdev(intf);
++
++	if (priv->nr_busses) {
++		dev_dbg(&intf->dev, "More than one I2C caps descriptor\n");
++		return -EINVAL;
++	}
++	priv->spec_version = le16_to_cpu(capd->bcdI2C);
++	if (capd->bLength < sizeof(*capd)) {
++		dev_dbg(&intf->dev,
++				"I2C caps descriptor too short (%u < %lu)\n",
++				capd->bLength, sizeof(*capd));
++		return -EINVAL;
++	}
++	if (capd->bQueueLength < 1) {
++		dev_dbg(&intf->dev, "I2C interface can't accept requests\n");
++		return -EINVAL;
++	}
++	priv->max_write_len = le16_to_cpu(capd->wMaxWriteLength);
++	priv->max_read_len = le16_to_cpu(capd->wMaxReadLength);
++	priv->nr_busses = capd->bNumberOfBusses;
++	priv->queue_len = capd->bQueueLength;
++	if (!priv->nr_busses) {
++		dev_dbg(&intf->dev, "No I2C busses\n");
++		return -EINVAL;
++	}
++	priv->busses = kzalloc(priv->nr_busses * sizeof(struct i2c_usb_bus),
++			GFP_KERNEL);
++	if (!priv->busses)
++		return -ENOMEM;
++
++	/* Luftig before 0.1.92 only has 16 bytes of r/w buffer */
++	if (device->product) {
++		int parsed;
++		int major;
++		int minor;
++		parsed = sscanf(device->product, "Luftig 0.%d.%d",
++				&major, &minor);
++		if (parsed == 2 && (major < 1 || (major == 1 && minor < 92))) {
++			dev_info(&intf->dev, "Luftig with 16 byte buffer\n");
++			priv->max_write_len = priv->max_read_len = 16;
++		}
++	}
++	return 0;
++}
++
++/* Parse an interface descriptor and its sub-descriptors. */
++static int i2c_usb_parse_intf_desc(struct i2c_usb_data *priv,
++		struct usb_host_interface *altsetting)
++{
++	struct usb_interface *intf = priv->intf;
++	unsigned char *buffer;
++	int buflen;
++	int ret;
++
++	ret = i2c_usb_init_notify_ep(priv, altsetting);
++	if (ret)
++		return ret;
++
++	/*
++	 * Now, take a good look at the functional descriptors and instantiate
++	 * the busses.
++	 */
++	buffer = altsetting->extra;
++	buflen = altsetting->extralen;
++	while (buflen > 0) {
++		/* Don't let buggy firmware trick us into an infinite loop */
++		if (buffer[0] < 2) {
++			dev_dbg(&intf->dev, "Bad descriptor bLength %d\n",
++					buffer[0]);
++			ret = -EINVAL;
++			goto err;
++		}
++		/* or access memory out of bounds. */
++		if (buffer[0] > buflen) {
++			dev_dbg(&intf->dev, "Truncated descriptor: bLength %d > buflen %d\n",
++					buffer[0], buflen);
++			ret = -EINVAL;
++			goto err;
++		}
++		switch (buffer[1]) {
++		case DT_I2C_CAPS:
++			ret = i2c_usb_parse_caps(priv, buffer);
++			if (ret)
++				goto err;
++			break;
++		/* TODO(hskinnemoen) DT_I2C_BUS */
++		default:
++			dev_dbg(&intf->dev, "Ignoring descriptor %02x\n",
++					buffer[1]);
++			break;
++		}
++		buflen -= buffer[0];
++		buffer += buffer[0];
++	}
++
++	return 0;
++
++err:
++	kfree(priv->busses);
++	return ret;
++}
++
++/* Initialize a single bus managed by the USB interface */
++static int i2c_usb_init_bus(struct i2c_usb_data *priv,
++		struct i2c_usb_bus *bus, int busnum)
++{
++	struct i2c_adapter *adap;
++	struct usb_interface *intf = priv->intf;
++	int ret;
++
++	bus->usbdev = interface_to_usbdev(intf);
++
++	adap = &bus->adap;
++	adap->owner = THIS_MODULE;
++	adap->class = I2C_CLASS_HWMON;
++	adap->algo = &i2c_usb_algorithm;
++	adap->dev.parent = &intf->dev;
++	adap->dev.groups = i2c_usb_attr_groups;
++	snprintf(adap->name, sizeof(adap->name), "i2c-usb-%s-%d",
++			dev_name(&intf->dev), busnum);
++	i2c_set_adapdata(adap, priv);
++
++	ret = i2c_add_adapter(adap);
++	if (ret < 0)
++		dev_err(&intf->dev, "failed to add i2c adapter: %d\n", ret);
++
++	dev_info(&adap->dev, "I2C-over-USB adapter %s\n", adap->name);
++
++	return ret;
++}
++
++static void i2c_usb_remove_bus(struct i2c_usb_data *priv,
++		struct i2c_usb_bus *bus)
++{
++	i2c_del_adapter(&bus->adap);
++}
++
++static int i2c_usb_probe(struct usb_interface *intf,
++		const struct usb_device_id *id)
++{
++	struct i2c_usb_data *priv;
++	unsigned int iface_no;
++	int ret = -ENOMEM;
++	int i;
++
++	priv = kzalloc(sizeof(struct i2c_usb_data), GFP_KERNEL);
++	if (!priv)
++		goto err_alloc_priv;
++
++	mutex_init(&priv->lock);
++	priv->intf = intf;
++	usb_set_intfdata(intf, priv);
++
++	priv->notify_urb = usb_alloc_urb(0, GFP_KERNEL);
++	if (!priv->notify_urb)
++		goto err_alloc_notify_urb;
++
++	ret = i2c_usb_parse_intf_desc(priv, intf->cur_altsetting);
++	if (ret < 0)
++		goto err_parse_intf_desc;
++
++	iface_no = intf->cur_altsetting->desc.bInterfaceNumber;
++	for (i = 0; i < priv->nr_busses; i++) {
++		priv->busses[i].index = (i << 8) | iface_no;
++		ret = i2c_usb_init_bus(priv, &priv->busses[i], i);
++		if (ret < 0)
++			goto err_init_bus;
++	}
++
++	ret = usb_submit_urb(priv->notify_urb, GFP_KERNEL);
++	if (ret) {
++		dev_err(&intf->dev, "failed to submit notify urb: %d\n", ret);
++		goto err_submit_urb;
++	}
++
++	dev_info(&intf->dev,
++		"v%x.%02x; %u busses rdlen %u wrlen %u qlen %u\n",
++		(priv->spec_version >> 8) & 0xff, priv->spec_version & 0xff,
++		priv->nr_busses, priv->max_read_len, priv->max_write_len,
++		priv->queue_len);
++
++	return 0;
++
++err_submit_urb:
++err_init_bus:
++	for (; i > 0; i--)
++		i2c_usb_remove_bus(priv, &priv->busses[i]);
++	kfree(priv->busses);
++err_parse_intf_desc:
++	kfree(priv->notify_urb);
++err_alloc_notify_urb:
++	usb_set_intfdata(intf, NULL);
++	kfree(priv);
++err_alloc_priv:
++	return ret;
++}
++
++static void i2c_usb_disconnect(struct usb_interface *interface)
++{
++	struct i2c_usb_data *priv;
++	int i;
++
++	priv = usb_get_intfdata(interface);
++	usb_set_intfdata(interface, NULL);
++
++	mutex_lock(&priv->lock);
++	/* Mark as disconnected before completing any pending transfers. */
++	priv->disconnected = true;
++	smp_mb();
++	usb_kill_urb(priv->notify_urb);
++	mutex_unlock(&priv->lock);
++
++	for (i = 0; i < priv->nr_busses; i++) {
++		struct i2c_usb_bus *bus = &priv->busses[i];
++		i2c_usb_remove_bus(priv, bus);
++	}
++	kfree(priv->busses);
++	usb_free_urb(priv->notify_urb);
++	kfree(priv->notify_buf);
++	kfree(priv);
++}
++
++static int i2c_usb_suspend(struct usb_interface *intf, pm_message_t message)
++{
++	struct i2c_usb_data *priv = usb_get_intfdata(intf);
++
++	if (!priv)
++		return 0;
++
++	mutex_lock(&priv->lock);
++	usb_kill_urb(priv->notify_urb);
++	mutex_unlock(&priv->lock);
++
++	return 0;
++}
++
++static int i2c_usb_resume(struct usb_interface *intf)
++{
++	struct i2c_usb_data *priv = usb_get_intfdata(intf);
++	int ret;
++
++	if (!priv)
++		return 0;
++
++	ret = usb_submit_urb(priv->notify_urb, GFP_KERNEL);
++	if (ret) {
++		dev_err(&intf->dev,
++			"failed to submit notify urb after resume: %d\n", ret);
++	}
++	return ret;
++}
++
++static struct usb_driver i2c_usb_driver = {
++	.name		= "i2c-usb",
++	.id_table	= i2c_usb_devices,
++	.probe		= i2c_usb_probe,
++	.disconnect	= i2c_usb_disconnect,
++	.suspend	= i2c_usb_suspend,
++	.resume		= i2c_usb_resume,
++};
++module_usb_driver(i2c_usb_driver);
++
++MODULE_AUTHOR("Google Inc.");
++MODULE_DESCRIPTION("I2C over USB bus driver");
++MODULE_LICENSE("GPL");
+-- 
+2.49.0.906.g1f30a19c02-goog
+
diff --git a/recipes-kernel/linux/files/0001-platforms-i2c-i2c-usb-update-a-few-nits.patch b/recipes-kernel/linux/files/0001-platforms-i2c-i2c-usb-update-a-few-nits.patch
new file mode 100644
index 0000000..1303cc8
--- /dev/null
+++ b/recipes-kernel/linux/files/0001-platforms-i2c-i2c-usb-update-a-few-nits.patch
@@ -0,0 +1,109 @@
+From 680bd92dfca4664ab197d7e776dc0f7b6429a919 Mon Sep 17 00:00:00 2001
+From: Tom Tung <shes050117@gmail.com>
+Date: Tue, 17 Jun 2025 07:54:38 +0000
+Subject: [PATCH] platforms/i2c: i2c-usb: update a few nits
+
+- Add a license statement.
+- Remove unneeded dynamic allocation for buf and directly passing the variable
+  in i2c_usb_get_bus_status.
+- Initialize all raw pointer variables to NULL.
+- Specify the type in memcpy after calculation.
+
+Tested: build
+Google-Bug-Id: 416173054
+Signed-off-by: Tom Tung <shes050117@gmail.com>
+---
+ drivers/i2c/busses/i2c-usb.c | 35 +++++++++++++++++++----------------
+ 1 file changed, 19 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/i2c/busses/i2c-usb.c b/drivers/i2c/busses/i2c-usb.c
+index 29210020ea7b..0536c0ddf732 100644
+--- a/drivers/i2c/busses/i2c-usb.c
++++ b/drivers/i2c/busses/i2c-usb.c
+@@ -1,4 +1,15 @@
+ /*
++ * Copyright 2013 Google Inc. All Rights Reserved
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details
++ *
+  * I2C-over-USB bus driver. Design doc: go/usb-i2c
+  *
+  * This drivers supports a limited subset of generic i2c commands which is
+@@ -222,20 +233,12 @@ static int i2c_usb_get_bus_status(struct i2c_usb_bus *bus)
+ 	struct usb_device *usbdev = bus->usbdev;
+ 	int ret;
+ 	__le16 status;
+-	u16 *buf;
+-
+-	buf = kzalloc(sizeof(u16), GFP_KERNEL);
+-	if (!buf)
+-		return -ENOMEM;
+ 
+ 	ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
+ 			USB_REQ_GET_STATUS, I2C_USB_REQTYPE_BUS | USB_DIR_IN,
+-			0, bus->index, buf, sizeof(u16),
++			0, bus->index, &status, sizeof(status),
+ 			I2C_USB_CTRL_READ_TIMEOUT_MS);
+ 
+-	status = *buf;
+-	kfree(buf);
+-
+ 	if (ret < 0) {
+ 		dev_err(&bus->adap.dev, "GetBusStatus(%04x) failed: %d\n",
+ 				bus->index, ret);
+@@ -461,14 +464,14 @@ static int i2c_usb_transfer(
+ 		void *out_buf, size_t out_len,
+ 		void *read_buf, size_t read_len, size_t *actual_read)
+ {
+-	struct i2c_usb_data *priv;
+-	struct usb_device *usbdev;
++	struct i2c_usb_data *priv = NULL;
++	struct usb_device *usbdev = NULL;
+ 	int ret = -ENOMEM;
+ 	u16 status;
+ 	size_t wlen;
+ 	size_t rlen;
+-	void *wbuf;
+-	void *rbuf;
++	void *wbuf = NULL;
++	void *rbuf = NULL;
+ 
+ 	priv = i2c_get_adapdata(&bus->adap);
+ 	mutex_lock(&priv->lock);
+@@ -482,7 +485,7 @@ static int i2c_usb_transfer(
+ 
+ 	memcpy(wbuf, xfer, sizeof(*xfer));
+ 	if (out_buf)
+-		memcpy(wbuf + sizeof(*xfer), out_buf, out_len);
++		memcpy((char *)wbuf + sizeof(*xfer), out_buf, out_len);
+ 
+ 	priv->xfer.status = -EINPROGRESS;
+ 	init_completion(&priv->xfer.complete);
+@@ -834,7 +837,7 @@ static int i2c_usb_parse_intf_desc(struct i2c_usb_data *priv,
+ 		struct usb_host_interface *altsetting)
+ {
+ 	struct usb_interface *intf = priv->intf;
+-	unsigned char *buffer;
++	unsigned char *buffer = NULL;
+ 	int buflen;
+ 	int ret;
+ 
+@@ -983,7 +986,7 @@ static int i2c_usb_probe(struct usb_interface *intf,
+ 
+ static void i2c_usb_disconnect(struct usb_interface *interface)
+ {
+-	struct i2c_usb_data *priv;
++	struct i2c_usb_data *priv = NULL;
+ 	int i;
+ 
+ 	priv = usb_get_intfdata(interface);
+-- 
+2.50.0.rc2.696.g1fc2a0284f-goog
+
diff --git a/recipes-kernel/linux/linux-gbmc_aspeedg7.bb b/recipes-kernel/linux/linux-gbmc_aspeedg7.bb
index 983c2e9..f73b0a0 100644
--- a/recipes-kernel/linux/linux-gbmc_aspeedg7.bb
+++ b/recipes-kernel/linux/linux-gbmc_aspeedg7.bb
@@ -19,6 +19,8 @@
   file://0001-hwmon-Add-driver-for-MPS-MPQ8785-Synchronous-Step-Do.patch \
   file://0001-hwmon-pmbus-Add-ltc4286-driver.patch \
   file://0001-hwmon-max34451-Workaround-for-lost-page.patch \
+  file://0001-PRODKERNEL-platforms-i2c-I2C-over-USB-driver.patch \
+  file://0001-platforms-i2c-i2c-usb-update-a-few-nits.patch \
   "
 
 SRC_URI:append:aspeed-g7 = " \