Skip to content

Commit 0de6194

Browse files
TroyMitchell911Wolfram Sang
authored andcommitted
i2c: spacemit: ensure SDA is released after bus reset
After performing a conditional bus reset, the controller must ensure that the SDA line is actually released. Previously, the reset routine only performed a single check, which could leave the bus in a locked state in some situations. This patch introduces a loop that toggles the reset cycle and issues a reset request up to SPACEMIT_BUS_RESET_CLK_CNT_MAX times, checking SDA after each attempt. If SDA is released before the maximum count, the function returns early. Otherwise, a warning is emitted. This change improves bus recovery reliability. Fixes: 5ea5584 ("i2c: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com> Reviewed-by: Aurelien Jarno <aurelien@aurel32.net> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
1 parent db7720e commit 0de6194

1 file changed

Lines changed: 20 additions & 1 deletion

File tree

drivers/i2c/busses/i2c-k1.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright (C) 2024-2025 Troy Mitchell <troymitchell988@gmail.com>
44
*/
55

6+
#include <linux/bitfield.h>
67
#include <linux/clk.h>
78
#include <linux/i2c.h>
89
#include <linux/iopoll.h>
@@ -26,7 +27,8 @@
2627
#define SPACEMIT_CR_MODE_FAST BIT(8) /* bus mode (master operation) */
2728
/* Bit 9 is reserved */
2829
#define SPACEMIT_CR_UR BIT(10) /* unit reset */
29-
/* Bits 11-12 are reserved */
30+
#define SPACEMIT_CR_RSTREQ BIT(11) /* i2c bus reset request */
31+
/* Bit 12 is reserved */
3032
#define SPACEMIT_CR_SCLE BIT(13) /* master clock enable */
3133
#define SPACEMIT_CR_IUE BIT(14) /* unit enable */
3234
/* Bits 15-17 are reserved */
@@ -78,6 +80,8 @@
7880
SPACEMIT_SR_ALD)
7981

8082
#define SPACEMIT_RCR_SDA_GLITCH_NOFIX BIT(7) /* bypass the SDA glitch fix */
83+
/* the cycles of SCL during bus reset */
84+
#define SPACEMIT_RCR_FIELD_RST_CYC GENMASK(3, 0)
8185

8286
/* SPACEMIT_IBMR register fields */
8387
#define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */
@@ -91,6 +95,8 @@
9195

9296
#define SPACEMIT_SR_ERR (SPACEMIT_SR_BED | SPACEMIT_SR_RXOV | SPACEMIT_SR_ALD)
9397

98+
#define SPACEMIT_BUS_RESET_CLK_CNT_MAX 9
99+
94100
enum spacemit_i2c_state {
95101
SPACEMIT_STATE_IDLE,
96102
SPACEMIT_STATE_START,
@@ -163,6 +169,7 @@ static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c)
163169
static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c)
164170
{
165171
u32 status;
172+
u8 clk_cnt;
166173

167174
/* if bus is locked, reset unit. 0: locked */
168175
status = readl(i2c->base + SPACEMIT_IBMR);
@@ -172,6 +179,18 @@ static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c)
172179
spacemit_i2c_reset(i2c);
173180
usleep_range(10, 20);
174181

182+
for (clk_cnt = 0; clk_cnt < SPACEMIT_BUS_RESET_CLK_CNT_MAX; clk_cnt++) {
183+
status = readl(i2c->base + SPACEMIT_IBMR);
184+
if (status & SPACEMIT_BMR_SDA)
185+
return;
186+
187+
/* There's nothing left to save here, we are about to exit */
188+
writel(FIELD_PREP(SPACEMIT_RCR_FIELD_RST_CYC, 1),
189+
i2c->base + SPACEMIT_IRCR);
190+
writel(SPACEMIT_CR_RSTREQ, i2c->base + SPACEMIT_ICR);
191+
usleep_range(20, 30);
192+
}
193+
175194
/* check sda again here */
176195
status = readl(i2c->base + SPACEMIT_IBMR);
177196
if (!(status & SPACEMIT_BMR_SDA))

0 commit comments

Comments
 (0)