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 = " \