Merge branch 'for-6.8/mcp2221' into for-linus
authorJiri Kosina <jkosina@suse.com>
Mon, 8 Jan 2024 20:08:40 +0000 (21:08 +0100)
committerJiri Kosina <jkosina@suse.com>
Mon, 8 Jan 2024 20:08:40 +0000 (21:08 +0100)
- several assorted functional fixes for mcp2221 driver (Hamish Martin)

1  2 
drivers/hid/hid-mcp2221.c

index aef0785c91cc2d19385b54c8689be4e875aa85c9,b242a312c377fa76ce19904112ba47f2b892263e..f9cceaeffd0814411d6024a3dd714444953a80db
@@@ -49,6 -49,7 +49,7 @@@ enum 
        MCP2221_I2C_MASK_ADDR_NACK = 0x40,
        MCP2221_I2C_WRADDRL_SEND = 0x21,
        MCP2221_I2C_ADDR_NACK = 0x25,
+       MCP2221_I2C_READ_PARTIAL = 0x54,
        MCP2221_I2C_READ_COMPL = 0x55,
        MCP2221_ALT_F_NOT_GPIOV = 0xEE,
        MCP2221_ALT_F_NOT_GPIOD = 0xEF,
@@@ -187,6 -188,25 +188,25 @@@ static int mcp_cancel_last_cmd(struct m
        return mcp_send_data_req_status(mcp, mcp->txbuf, 8);
  }
  
+ /* Check if the last command succeeded or failed and return the result.
+  * If the command did fail, cancel that command which will free the i2c bus.
+  */
+ static int mcp_chk_last_cmd_status_free_bus(struct mcp2221 *mcp)
+ {
+       int ret;
+       ret = mcp_chk_last_cmd_status(mcp);
+       if (ret) {
+               /* The last command was a failure.
+                * Send a cancel which will also free the bus.
+                */
+               usleep_range(980, 1000);
+               mcp_cancel_last_cmd(mcp);
+       }
+       return ret;
+ }
  static int mcp_set_i2c_speed(struct mcp2221 *mcp)
  {
        int ret;
@@@ -241,7 -261,7 +261,7 @@@ static int mcp_i2c_write(struct mcp222
                usleep_range(980, 1000);
  
                if (last_status) {
-                       ret = mcp_chk_last_cmd_status(mcp);
+                       ret = mcp_chk_last_cmd_status_free_bus(mcp);
                        if (ret)
                                return ret;
                }
@@@ -278,6 -298,7 +298,7 @@@ static int mcp_i2c_smbus_read(struct mc
  {
        int ret;
        u16 total_len;
+       int retries = 0;
  
        mcp->txbuf[0] = type;
        if (msg) {
        mcp->rxbuf_idx = 0;
  
        do {
+               /* Wait for the data to be read by the device */
+               usleep_range(980, 1000);
                memset(mcp->txbuf, 0, 4);
                mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
  
                ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
-               if (ret)
-                       return ret;
-               ret = mcp_chk_last_cmd_status(mcp);
-               if (ret)
-                       return ret;
-               usleep_range(980, 1000);
+               if (ret) {
+                       if (retries < 5) {
+                               /* The data wasn't ready to read.
+                                * Wait a bit longer and try again.
+                                */
+                               usleep_range(90, 100);
+                               retries++;
+                       } else {
+                               return ret;
+                       }
+               } else {
+                       retries = 0;
+               }
        } while (mcp->rxbuf_idx < total_len);
  
+       usleep_range(980, 1000);
+       ret = mcp_chk_last_cmd_status_free_bus(mcp);
        return ret;
  }
  
@@@ -328,11 -360,6 +360,6 @@@ static int mcp_i2c_xfer(struct i2c_adap
  
        mutex_lock(&mcp->lock);
  
-       /* Setting speed before every transaction is required for mcp2221 */
-       ret = mcp_set_i2c_speed(mcp);
-       if (ret)
-               goto exit;
        if (num == 1) {
                if (msgs->flags & I2C_M_RD) {
                        ret = mcp_i2c_smbus_read(mcp, msgs, MCP2221_I2C_RD_DATA,
@@@ -417,9 -444,7 +444,7 @@@ static int mcp_smbus_write(struct mcp22
        if (last_status) {
                usleep_range(980, 1000);
  
-               ret = mcp_chk_last_cmd_status(mcp);
-               if (ret)
-                       return ret;
+               ret = mcp_chk_last_cmd_status_free_bus(mcp);
        }
  
        return ret;
@@@ -437,10 -462,6 +462,6 @@@ static int mcp_smbus_xfer(struct i2c_ad
  
        mutex_lock(&mcp->lock);
  
-       ret = mcp_set_i2c_speed(mcp);
-       if (ret)
-               goto exit;
        switch (size) {
  
        case I2C_SMBUS_QUICK:
@@@ -791,7 -812,8 +812,8 @@@ static int mcp2221_raw_event(struct hid
                                mcp->status = -EIO;
                                break;
                        }
-                       if (data[2] == MCP2221_I2C_READ_COMPL) {
+                       if (data[2] == MCP2221_I2C_READ_COMPL ||
+                           data[2] == MCP2221_I2C_READ_PARTIAL) {
                                buf = mcp->rxbuf;
                                memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
                                mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
@@@ -1142,29 -1164,33 +1164,35 @@@ static int mcp2221_probe(struct hid_dev
        if (ret)
                return ret;
  
 +      hid_device_io_start(hdev);
 +
        /* Set I2C bus clock diviser */
        if (i2c_clk_freq > 400)
                i2c_clk_freq = 400;
        if (i2c_clk_freq < 50)
                i2c_clk_freq = 50;
        mcp->cur_i2c_clk_div = (12000000 / (i2c_clk_freq * 1000)) - 3;
+       ret = mcp_set_i2c_speed(mcp);
+       if (ret) {
+               hid_err(hdev, "can't set i2c speed: %d\n", ret);
+               return ret;
+       }
  
        mcp->adapter.owner = THIS_MODULE;
        mcp->adapter.class = I2C_CLASS_HWMON;
        mcp->adapter.algo = &mcp_i2c_algo;
        mcp->adapter.retries = 1;
        mcp->adapter.dev.parent = &hdev->dev;
+       ACPI_COMPANION_SET(&mcp->adapter.dev, ACPI_COMPANION(hdev->dev.parent));
        snprintf(mcp->adapter.name, sizeof(mcp->adapter.name),
                        "MCP2221 usb-i2c bridge");
  
 +      i2c_set_adapdata(&mcp->adapter, mcp);
        ret = devm_i2c_add_adapter(&hdev->dev, &mcp->adapter);
        if (ret) {
                hid_err(hdev, "can't add usb-i2c adapter: %d\n", ret);
                return ret;
        }
 -      i2c_set_adapdata(&mcp->adapter, mcp);
  
  #if IS_REACHABLE(CONFIG_GPIOLIB)
        /* Setup GPIO chip */