summaryrefslogtreecommitdiff
path: root/drivers/soundwire
diff options
context:
space:
mode:
authorCharles Keepax <ckeepax@opensource.cirrus.com>2023-03-22 19:49:48 +0300
committerVinod Koul <vkoul@kernel.org>2023-04-12 13:00:37 +0300
commitd005ea713dd721c1ce9010e608eb416a3890d6b4 (patch)
tree05cbfbf3f6c0990ba8b9840e00c287f9c439b948 /drivers/soundwire
parentd94e1e01c70375fcc102a8d09a00d28efeeef51c (diff)
downloadlinux-d005ea713dd721c1ce9010e608eb416a3890d6b4.tar.xz
soundwire: bus: Update sdw_nread/nwrite_no_pm to handle page boundaries
Currently issuing a sdw_nread/nwrite_no_pm across a page boundary will silently fail to write correctly as nothing updates the page registers, meaning the same page of the chip will get rewritten with each successive page of data. As the sdw_msg structure contains page information it seems reasonable that a single sdw_msg should always be within one page. It is also mostly simpler to handle the paging at the bus level rather than each master having to handle it in their xfer_msg callback. As such add handling to the bus code to split up a transfer into multiple sdw_msg's when they go across page boundaries. Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://lore.kernel.org/r/20230322164948.566962-3-ckeepax@opensource.cirrus.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
Diffstat (limited to 'drivers/soundwire')
-rw-r--r--drivers/soundwire/bus.c63
1 files changed, 39 insertions, 24 deletions
diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index f1ffb8e0839d..e157a39a82ce 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -386,27 +386,46 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,
* Read/Write IO functions.
*/
+static int sdw_ntransfer_no_pm(struct sdw_slave *slave, u32 addr, u8 flags,
+ size_t count, u8 *val)
+{
+ struct sdw_msg msg;
+ size_t size;
+ int ret;
+
+ while (count) {
+ // Only handle bytes up to next page boundary
+ size = min_t(size_t, count, (SDW_REGADDR + 1) - (addr & SDW_REGADDR));
+
+ ret = sdw_fill_msg(&msg, slave, addr, size, slave->dev_num, flags, val);
+ if (ret < 0)
+ return ret;
+
+ ret = sdw_transfer(slave->bus, &msg);
+ if (ret < 0 && !slave->is_mockup_device)
+ return ret;
+
+ addr += size;
+ val += size;
+ count -= size;
+ }
+
+ return 0;
+}
+
/**
* sdw_nread_no_pm() - Read "n" contiguous SDW Slave registers with no PM
* @slave: SDW Slave
* @addr: Register address
* @count: length
* @val: Buffer for values to be read
+ *
+ * Note that if the message crosses a page boundary each page will be
+ * transferred under a separate invocation of the msg_lock.
*/
int sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
- struct sdw_msg msg;
- int ret;
-
- ret = sdw_fill_msg(&msg, slave, addr, count,
- slave->dev_num, SDW_MSG_FLAG_READ, val);
- if (ret < 0)
- return ret;
-
- ret = sdw_transfer(slave->bus, &msg);
- if (slave->is_mockup_device)
- ret = 0;
- return ret;
+ return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_READ, count, val);
}
EXPORT_SYMBOL(sdw_nread_no_pm);
@@ -416,21 +435,13 @@ EXPORT_SYMBOL(sdw_nread_no_pm);
* @addr: Register address
* @count: length
* @val: Buffer for values to be written
+ *
+ * Note that if the message crosses a page boundary each page will be
+ * transferred under a separate invocation of the msg_lock.
*/
int sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val)
{
- struct sdw_msg msg;
- int ret;
-
- ret = sdw_fill_msg(&msg, slave, addr, count,
- slave->dev_num, SDW_MSG_FLAG_WRITE, (u8 *)val);
- if (ret < 0)
- return ret;
-
- ret = sdw_transfer(slave->bus, &msg);
- if (slave->is_mockup_device)
- ret = 0;
- return ret;
+ return sdw_ntransfer_no_pm(slave, addr, SDW_MSG_FLAG_WRITE, count, (u8 *)val);
}
EXPORT_SYMBOL(sdw_nwrite_no_pm);
@@ -566,6 +577,8 @@ EXPORT_SYMBOL(sdw_update);
*
* This version of the function will take a PM reference to the slave
* device.
+ * Note that if the message crosses a page boundary each page will be
+ * transferred under a separate invocation of the msg_lock.
*/
int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
@@ -593,6 +606,8 @@ EXPORT_SYMBOL(sdw_nread);
*
* This version of the function will take a PM reference to the slave
* device.
+ * Note that if the message crosses a page boundary each page will be
+ * transferred under a separate invocation of the msg_lock.
*/
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val)
{