From 897f3f3e83adb3b6ee84c6b02cecaa0005ce6b78 Mon Sep 17 00:00:00 2001 From: phani-karanam Date: Wed, 21 Feb 2018 14:45:57 +0800 Subject: [PATCH] Adding CP2112 latest patch & Enabling USB_ACM in linux config to create TTY interface for CPU to BMC communication. --- .../configs/x86_64-all/x86_64-all.config | 4 +- .../patches/driver-hid-cp2112-mods.patch | 196 ++++++++++++++++++ .../base/any/kernels/3.16-lts/patches/series | 1 + 3 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 packages/base/any/kernels/3.16-lts/patches/driver-hid-cp2112-mods.patch diff --git a/packages/base/any/kernels/3.16-lts/configs/x86_64-all/x86_64-all.config b/packages/base/any/kernels/3.16-lts/configs/x86_64-all/x86_64-all.config index 24d29cee..c360583d 100644 --- a/packages/base/any/kernels/3.16-lts/configs/x86_64-all/x86_64-all.config +++ b/packages/base/any/kernels/3.16-lts/configs/x86_64-all/x86_64-all.config @@ -2450,7 +2450,7 @@ CONFIG_HID_GENERIC=y # CONFIG_HID_BELKIN is not set # CONFIG_HID_CHERRY is not set # CONFIG_HID_CHICONY is not set -# CONFIG_HID_CP2112 is not set +CONFIG_HID_CP2112=y # CONFIG_HID_CYPRESS is not set # CONFIG_HID_DRAGONRISE is not set # CONFIG_HID_EMS_FF is not set @@ -2561,7 +2561,7 @@ CONFIG_USB_UHCI_HCD=y # # USB Device Class drivers # -# CONFIG_USB_ACM is not set +CONFIG_USB_ACM=y # CONFIG_USB_PRINTER is not set # CONFIG_USB_WDM is not set # CONFIG_USB_TMC is not set diff --git a/packages/base/any/kernels/3.16-lts/patches/driver-hid-cp2112-mods.patch b/packages/base/any/kernels/3.16-lts/patches/driver-hid-cp2112-mods.patch new file mode 100644 index 00000000..e09052db --- /dev/null +++ b/packages/base/any/kernels/3.16-lts/patches/driver-hid-cp2112-mods.patch @@ -0,0 +1,196 @@ +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); + diff --git a/packages/base/any/kernels/3.16-lts/patches/series b/packages/base/any/kernels/3.16-lts/patches/series index e6a083f7..37e24f77 100644 --- a/packages/base/any/kernels/3.16-lts/patches/series +++ b/packages/base/any/kernels/3.16-lts/patches/series @@ -26,4 +26,5 @@ platform-powerpc-dni-7448-r0.patch platform-powerpc-quanta-lb9-r0.patch driver-support-intel-igb-bcm50210-phy.patch driver-igb-netberg-aurora.patch +driver-hid-cp2112-mods.patch gcc-no-pie.patch