Skip to content

Commit e42066d

Browse files
rfvirgilbroonie
authored andcommitted
ASoC: cs35l56: Handle OTP read latency over SoundWire
Use the late-read buffer in the CS35L56 SoundWire interface to read OTP memory. The OTP memory has a longer access latency than chip registers and cannot guarantee to return the data value in the SoundWire control response if the bus clock is >4.8 MHz. The Cirrus SoundWire peripheral IP exposes the bridge-to-bus read buffer and status bits. For a read from OTP the bridge status bits are polled to wait for the OTP data to be loaded into the read buffer and the data is then read from there. Signed-off-by: Richard Fitzgerald <[email protected]> Fixes: e1830f6 ("ASoC: cs35l56: Add helper functions for amp calibration") Link: https://patch.msgid.link/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 9a1af1e commit e42066d

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

include/sound/cs35l56.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ static inline int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_ba
277277
return 0;
278278
}
279279

280+
static inline bool cs35l56_is_otp_register(unsigned int reg)
281+
{
282+
return (reg >> 16) == 3;
283+
}
284+
280285
extern struct regmap_config cs35l56_regmap_i2c;
281286
extern struct regmap_config cs35l56_regmap_spi;
282287
extern struct regmap_config cs35l56_regmap_sdw;

sound/soc/codecs/cs35l56-sdw.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,79 @@
2323
/* Register addresses are offset when sent over SoundWire */
2424
#define CS35L56_SDW_ADDR_OFFSET 0x8000
2525

26+
/* Cirrus bus bridge registers */
27+
#define CS35L56_SDW_MEM_ACCESS_STATUS 0xd0
28+
#define CS35L56_SDW_MEM_READ_DATA 0xd8
29+
30+
#define CS35L56_SDW_LAST_LATE BIT(3)
31+
#define CS35L56_SDW_CMD_IN_PROGRESS BIT(2)
32+
#define CS35L56_SDW_RDATA_RDY BIT(0)
33+
34+
#define CS35L56_LATE_READ_POLL_US 10
35+
#define CS35L56_LATE_READ_TIMEOUT_US 1000
36+
37+
static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral,
38+
unsigned int mask,
39+
unsigned int match)
40+
{
41+
int ret, val;
42+
43+
ret = read_poll_timeout(sdw_read_no_pm, val,
44+
(val < 0) || ((val & mask) == match),
45+
CS35L56_LATE_READ_POLL_US, CS35L56_LATE_READ_TIMEOUT_US,
46+
false, peripheral, CS35L56_SDW_MEM_ACCESS_STATUS);
47+
if (ret < 0)
48+
return ret;
49+
50+
if (val < 0)
51+
return val;
52+
53+
return 0;
54+
}
55+
56+
static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg,
57+
u8 *buf, size_t val_size)
58+
{
59+
int ret, i;
60+
61+
reg += CS35L56_SDW_ADDR_OFFSET;
62+
63+
for (i = 0; i < val_size; i += sizeof(u32)) {
64+
/* Poll for bus bridge idle */
65+
ret = cs35l56_sdw_poll_mem_status(peripheral,
66+
CS35L56_SDW_CMD_IN_PROGRESS,
67+
0);
68+
if (ret < 0) {
69+
dev_err(&peripheral->dev, "!CMD_IN_PROGRESS fail: %d\n", ret);
70+
return ret;
71+
}
72+
73+
/* Reading LSByte triggers read of register to holding buffer */
74+
sdw_read_no_pm(peripheral, reg + i);
75+
76+
/* Wait for data available */
77+
ret = cs35l56_sdw_poll_mem_status(peripheral,
78+
CS35L56_SDW_RDATA_RDY,
79+
CS35L56_SDW_RDATA_RDY);
80+
if (ret < 0) {
81+
dev_err(&peripheral->dev, "RDATA_RDY fail: %d\n", ret);
82+
return ret;
83+
}
84+
85+
/* Read data from buffer */
86+
ret = sdw_nread_no_pm(peripheral, CS35L56_SDW_MEM_READ_DATA,
87+
sizeof(u32), &buf[i]);
88+
if (ret) {
89+
dev_err(&peripheral->dev, "Late read @%#x failed: %d\n", reg + i, ret);
90+
return ret;
91+
}
92+
93+
swab32s((u32 *)&buf[i]);
94+
}
95+
96+
return 0;
97+
}
98+
2699
static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf)
27100
{
28101
int ret;
@@ -48,6 +121,10 @@ static int cs35l56_sdw_read(void *context, const void *reg_buf,
48121
int ret;
49122

50123
reg = le32_to_cpu(*(const __le32 *)reg_buf);
124+
125+
if (cs35l56_is_otp_register(reg))
126+
return cs35l56_sdw_slow_read(peripheral, reg, buf8, val_size);
127+
51128
reg += CS35L56_SDW_ADDR_OFFSET;
52129

53130
if (val_size == 4)

0 commit comments

Comments
 (0)