Skip to content

Commit 5bc493b

Browse files
charleskeepaxbroonie
authored andcommitted
regmap: sdw-mbq: Add support for SDCA deferred controls
The SDCA specification allows for controls to be deferred. In the case of a deferred control the device will return COMMAND_IGNORED to the 8-bit operation that would cause the value to commit. Which is the final 8-bits on a write, or the first 8-bits on a read. In the case of receiving a defer, the regmap will poll the SDCA function busy bit, after which the transaction will be retried, returning an error if the function busy does not clear within a chip specific timeout. Since this is common SDCA functionality which is the 99% use-case for MBQs it makes sense to incorporate this functionality into the register map. If no MBQ configuration is specified, the behaviour will default to the existing behaviour. Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com> Link: https://patch.msgid.link/20250107154408.814455-5-ckeepax@opensource.cirrus.com Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev> Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent fdd9ef3 commit 5bc493b

File tree

2 files changed

+130
-14
lines changed

2 files changed

+130
-14
lines changed

drivers/base/regmap/regmap-sdw-mbq.c

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
// Copyright(c) 2020 Intel Corporation.
33

44
#include <linux/bits.h>
5+
#include <linux/delay.h>
56
#include <linux/device.h>
67
#include <linux/errno.h>
8+
#include <linux/iopoll.h>
79
#include <linux/module.h>
810
#include <linux/regmap.h>
911
#include <linux/soundwire/sdw.h>
1012
#include <linux/soundwire/sdw_registers.h>
13+
#include <sound/sdca_function.h>
1114
#include "internal.h"
1215

1316
struct regmap_mbq_context {
@@ -16,6 +19,7 @@ struct regmap_mbq_context {
1619
struct regmap_sdw_mbq_cfg cfg;
1720

1821
int val_size;
22+
bool (*readable_reg)(struct device *dev, unsigned int reg);
1923
};
2024

2125
static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
@@ -31,18 +35,48 @@ static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
3135
return size;
3236
}
3337

34-
static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
38+
static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned int reg)
39+
{
40+
if (ctx->cfg.deferrable)
41+
return ctx->cfg.deferrable(ctx->dev, reg);
42+
43+
return false;
44+
}
45+
46+
static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,
47+
struct regmap_mbq_context *ctx)
48+
{
49+
struct device *dev = &slave->dev;
50+
int val, ret = 0;
51+
52+
dev_dbg(dev, "Deferring transaction for 0x%x\n", reg);
53+
54+
reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0,
55+
SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
56+
57+
if (ctx->readable_reg(dev, reg)) {
58+
ret = read_poll_timeout(sdw_read_no_pm, val,
59+
val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY),
60+
ctx->cfg.timeout_us, ctx->cfg.retry_us,
61+
false, slave, reg);
62+
if (val < 0)
63+
return val;
64+
if (ret)
65+
dev_err(dev, "Function busy timed out 0x%x: %d\n", reg, val);
66+
} else {
67+
fsleep(ctx->cfg.timeout_us);
68+
}
69+
70+
return ret;
71+
}
72+
73+
static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave,
74+
unsigned int reg, unsigned int val,
75+
int mbq_size, bool deferrable)
3576
{
36-
struct regmap_mbq_context *ctx = context;
37-
struct device *dev = ctx->dev;
38-
struct sdw_slave *slave = dev_to_sdw_dev(dev);
39-
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
4077
int shift = mbq_size * BITS_PER_BYTE;
4178
int ret;
4279

43-
if (mbq_size < 0)
44-
return mbq_size;
45-
4680
while (--mbq_size > 0) {
4781
shift -= BITS_PER_BYTE;
4882

@@ -52,24 +86,58 @@ static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int va
5286
return ret;
5387
}
5488

55-
return sdw_write_no_pm(slave, reg, val & 0xff);
89+
ret = sdw_write_no_pm(slave, reg, val & 0xff);
90+
if (deferrable && ret == -ENODATA)
91+
return -EAGAIN;
92+
93+
return ret;
5694
}
5795

58-
static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
96+
static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
5997
{
6098
struct regmap_mbq_context *ctx = context;
6199
struct device *dev = ctx->dev;
62100
struct sdw_slave *slave = dev_to_sdw_dev(dev);
101+
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
63102
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
64-
int shift = BITS_PER_BYTE;
65-
int read;
103+
int ret;
66104

67105
if (mbq_size < 0)
68106
return mbq_size;
69107

108+
/*
109+
* Technically the spec does allow a device to set itself to busy for
110+
* internal reasons, but since it doesn't provide any information on
111+
* how to handle timeouts in that case, for now the code will only
112+
* process a single wait/timeout on function busy and a single retry
113+
* of the transaction.
114+
*/
115+
ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, deferrable);
116+
if (ret == -EAGAIN) {
117+
ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
118+
if (ret)
119+
return ret;
120+
121+
ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, false);
122+
}
123+
124+
return ret;
125+
}
126+
127+
static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave,
128+
unsigned int reg, unsigned int *val,
129+
int mbq_size, bool deferrable)
130+
{
131+
int shift = BITS_PER_BYTE;
132+
int read;
133+
70134
read = sdw_read_no_pm(slave, reg);
71-
if (read < 0)
135+
if (read < 0) {
136+
if (deferrable && read == -ENODATA)
137+
return -EAGAIN;
138+
72139
return read;
140+
}
73141

74142
*val = read;
75143

@@ -85,6 +153,37 @@ static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *va
85153
return 0;
86154
}
87155

156+
static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
157+
{
158+
struct regmap_mbq_context *ctx = context;
159+
struct device *dev = ctx->dev;
160+
struct sdw_slave *slave = dev_to_sdw_dev(dev);
161+
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
162+
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
163+
int ret;
164+
165+
if (mbq_size < 0)
166+
return mbq_size;
167+
168+
/*
169+
* Technically the spec does allow a device to set itself to busy for
170+
* internal reasons, but since it doesn't provide any information on
171+
* how to handle timeouts in that case, for now the code will only
172+
* process a single wait/timeout on function busy and a single retry
173+
* of the transaction.
174+
*/
175+
ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, deferrable);
176+
if (ret == -EAGAIN) {
177+
ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
178+
if (ret)
179+
return ret;
180+
181+
ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, false);
182+
}
183+
184+
return ret;
185+
}
186+
88187
static const struct regmap_bus regmap_sdw_mbq = {
89188
.reg_read = regmap_sdw_mbq_read,
90189
.reg_write = regmap_sdw_mbq_write,
@@ -119,11 +218,13 @@ regmap_sdw_mbq_gen_context(struct device *dev,
119218
return ERR_PTR(-ENOMEM);
120219

121220
ctx->dev = dev;
122-
ctx->val_size = config->val_bits / BITS_PER_BYTE;
123221

124222
if (mbq_config)
125223
ctx->cfg = *mbq_config;
126224

225+
ctx->val_size = config->val_bits / BITS_PER_BYTE;
226+
ctx->readable_reg = config->readable_reg;
227+
127228
return ctx;
128229
}
129230

include/linux/regmap.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,11 +510,26 @@ struct regmap_range_cfg {
510510
* struct regmap_sdw_mbq_cfg - Configuration for Multi-Byte Quantities
511511
*
512512
* @mbq_size: Callback returning the actual size of the given register.
513+
* @deferrable: Callback returning true if the hardware can defer
514+
* transactions to the given register. Deferral should
515+
* only be used by SDCA parts and typically which controls
516+
* are deferrable will be specified in either as a hard
517+
* coded list or from the DisCo tables in the platform
518+
* firmware.
519+
*
520+
* @timeout_us: The time in microseconds after which waiting for a deferred
521+
* transaction should time out.
522+
* @retry_us: The time in microseconds between polls of the function busy
523+
* status whilst waiting for an opportunity to retry a deferred
524+
* transaction.
513525
*
514526
* Provides additional configuration required for SoundWire MBQ register maps.
515527
*/
516528
struct regmap_sdw_mbq_cfg {
517529
int (*mbq_size)(struct device *dev, unsigned int reg);
530+
bool (*deferrable)(struct device *dev, unsigned int reg);
531+
unsigned long timeout_us;
532+
unsigned long retry_us;
518533
};
519534

520535
struct regmap_async;

0 commit comments

Comments
 (0)