linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sultan Alsawaf <sultan@kerneltoast.com>
To: jarkko.nikula@linux.intel.com
Cc: aaron.ma@canonical.com, admin@kryma.net,
	andriy.shevchenko@linux.intel.com, benjamin.tissoires@redhat.com,
	hdegoede@redhat.com, hn.chen@weidahitech.com, jikos@kernel.org,
	kai.heng.feng@canonical.com, linux-i2c@vger.kernel.org,
	linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	mika.westerberg@linux.intel.com, sultan@kerneltoast.com,
	vicamo.yang@canonical.com, wsa@kernel.org
Subject: [PATCH v3] i2c: Squash of SMBus block read patchset to save power
Date: Sun, 13 Sep 2020 17:15:23 -0700	[thread overview]
Message-ID: <20200914001523.3878-1-sultan@kerneltoast.com> (raw)
In-Reply-To: <b3b751fc-668d-91e2-220b-0d7edd231e01@linux.intel.com>

From: Sultan Alsawaf <sultan@kerneltoast.com>

This is a squash of the following:

i2c: designware: Fix transfer failures for invalid SMBus block reads

SMBus block reads can be broken because the read function will just skip
over bytes it doesn't like until reaching a byte that conforms to the
length restrictions for block reads. This is problematic when it isn't
known if the incoming payload is indeed a conforming block read.

According to the SMBus specification, block reads will only send the
payload length in the first byte, so we can fix this by only considering
the first byte in a sequence for block read length purposes.

In addition, when the length byte is invalid, the original transfer
length still needs to be adjusted to avoid a controller timeout.

Fixes: c3ae106050b9 ("i2c: designware: Implement support for SMBus block read and write")
Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>

i2c: designware: Ensure tx_buf_len is nonzero for SMBus block reads

The point of adding a byte to len in i2c_dw_recv_len() is to make sure
that tx_buf_len is nonzero, so that i2c_dw_xfer_msg() can let the i2c
controller know that the i2c transaction can end. Otherwise, the i2c
controller will think that the transaction can never end for block
reads, which results in the stop-detection bit never being set and thus
the transaction timing out.

Adding a byte to len is not a reliable way to do this though; sometimes
it lets tx_buf_len become zero, which results in the scenario described
above. Therefore, just directly ensure tx_buf_len cannot be zero to fix
the issue.

Fixes: c3ae106050b9 ("i2c: designware: Implement support for SMBus block read and write")
Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>

i2c: designware: Allow SMBus block reads up to 255 bytes in length

According to the SMBus 3.0 protocol specification, block transfer limits
were increased from 32 bytes to 255 bytes. Remove the obsolete 32-byte
limitation.

Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>

HID: i2c-hid: Use block reads when possible to save power

We have no way of knowing how large an incoming payload is going to be,
so the only strategy available up until now has been to always retrieve
the maximum possible report length over i2c, which can be quite
inefficient. For devices that send reports in block read format, the i2c
controller driver can read the payload length on the fly and terminate
the i2c transaction early, resulting in considerable power savings.

On a Dell Precision 15 5540 with an i9-9880H, resting my finger on the
touchpad causes psys power readings to go up by about 4W and hover there
until I remove my finger. With this patch, my psys readings go from 4.7W
down to 3.1W, yielding about 1.6W in savings. This is because my
touchpad's max report length is 60 bytes, but all of the regular reports
it sends for touch events are only 32 bytes, so the i2c transfer is
roughly halved for the common case.

Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
---
Hi Jarkko,

Sorry for the delayed response. Life gets in the way of the things that really
matter, like kernel hacking ;)

I fixed the issue with the i2c block reads on 5.8. I've squashed all 4 of my i2c
commits into this email for simplicity; please apply this patch on either 5.8 or
5.9 (it applies cleanly to both) and let me know if it works with your i2c-hid
touchscreen. If all is well, I will resubmit these patches individually in one
patchset, in a new thread.

Thanks,
Sultan
 drivers/hid/i2c-hid/i2c-hid-core.c         |  5 ++++-
 drivers/i2c/busses/i2c-designware-master.c | 15 +++++++++------
 2 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index dbd04492825d..66950f472122 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -476,11 +476,14 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
 	int ret;
 	u32 ret_size;
 	int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
+	u16 flags;
 
 	if (size > ihid->bufsize)
 		size = ihid->bufsize;
 
-	ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
+	/* Try to do a block read if the size fits in one byte */
+	flags = size > 255 ? I2C_M_RD : I2C_M_RD | I2C_M_RECV_LEN;
+	ret = i2c_transfer_buffer_flags(ihid->client, ihid->inbuf, size, flags);
 	if (ret != size) {
 		if (ret < 0)
 			return;
diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c
index d6425ad6e6a3..5bd64bd17d94 100644
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -395,8 +395,9 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len)
 	 * Adjust the buffer length and mask the flag
 	 * after receiving the first byte.
 	 */
-	len += (flags & I2C_CLIENT_PEC) ? 2 : 1;
-	dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding);
+	if (flags & I2C_CLIENT_PEC)
+		len++;
+	dev->tx_buf_len = len - min_t(u8, len - 1, dev->rx_outstanding);
 	msgs[dev->msg_read_idx].len = len;
 	msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN;
 
@@ -430,10 +431,12 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 			u32 flags = msgs[dev->msg_read_idx].flags;
 
 			regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
-			/* Ensure length byte is a valid value */
-			if (flags & I2C_M_RECV_LEN &&
-			    tmp <= I2C_SMBUS_BLOCK_MAX && tmp > 0) {
-				len = i2c_dw_recv_len(dev, tmp);
+			if (flags & I2C_M_RECV_LEN) {
+				/* Ensure length byte is a valid value */
+				if (tmp > 0)
+					len = i2c_dw_recv_len(dev, tmp);
+				else
+					len = i2c_dw_recv_len(dev, len);
 			}
 			*buf++ = tmp;
 			dev->rx_outstanding--;
-- 
2.28.0


  reply	other threads:[~2020-09-14  0:15 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-14 21:02 [PATCH 0/2] i2c-hid: Save power by reducing i2c xfers with block reads Sultan Alsawaf
2020-06-14 21:02 ` [PATCH 1/2] i2c: designware: Only check the first byte for SMBus block read length Sultan Alsawaf
2020-06-15  9:40   ` Andy Shevchenko
2020-06-15 16:03     ` Sultan Alsawaf
2020-06-15 16:07       ` Andy Shevchenko
2020-06-15 17:15         ` Sultan Alsawaf
2020-06-16 13:22   ` Jarkko Nikula
2020-06-16 15:43     ` [PATCH v2] " Sultan Alsawaf
2020-06-17 11:08       ` Jarkko Nikula
2020-06-14 21:02 ` [PATCH 2/2] HID: i2c-hid: Use block reads when possible to save power Sultan Alsawaf
2020-06-16 13:43   ` Jarkko Nikula
2020-06-16 15:49     ` [PATCH v2] " Sultan Alsawaf
2020-06-16 16:41       ` Andy Shevchenko
2020-06-16 17:18         ` Andi Shyti
2020-06-16 17:32           ` Sultan Alsawaf
2020-06-16 18:02             ` Andi Shyti
2020-06-16 18:17               ` Sultan Alsawaf
2020-06-17 11:17       ` Jarkko Nikula
2020-06-29 17:43         ` Sultan Alsawaf
2020-07-01  8:04           ` Jarkko Nikula
2020-07-01 15:00             ` Sultan Alsawaf
2020-07-03 11:18               ` Jarkko Nikula
2020-09-14  0:15                 ` Sultan Alsawaf [this message]
     [not found]                   ` <bcf9cd02-13d1-8f87-8ef9-2f05f0b54808@linux.intel.com>
2020-09-15 17:48                     ` [PATCH v3] i2c: Squash of SMBus block read patchset " Sultan Alsawaf
2020-09-16 14:09                       ` Jarkko Nikula
2020-09-15 20:44                   ` Jiri Kosina
2020-06-22  0:58   ` [HID] 0b987151f0: kmsg.i2c_designware_i2c_designware.#: controller_timed_out kernel test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200914001523.3878-1-sultan@kerneltoast.com \
    --to=sultan@kerneltoast.com \
    --cc=aaron.ma@canonical.com \
    --cc=admin@kryma.net \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=benjamin.tissoires@redhat.com \
    --cc=hdegoede@redhat.com \
    --cc=hn.chen@weidahitech.com \
    --cc=jarkko.nikula@linux.intel.com \
    --cc=jikos@kernel.org \
    --cc=kai.heng.feng@canonical.com \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mika.westerberg@linux.intel.com \
    --cc=vicamo.yang@canonical.com \
    --cc=wsa@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).