* [PATCH v1 2/2] power: reset: spacemit-p1: Use atomic I2C transfers for power-off and restart
2026-11-10 6:14 [PATCH v1 1/2] i2c: spacemit: Avoid sleeping during bus reset in atomic context Anand Moon
@ 2026-11-10 6:14 ` Anand Moon
2026-11-10 6:41 ` [PATCH v1 1/2] i2c: spacemit: Avoid sleeping during bus reset in atomic context Troy Mitchell
2026-11-10 6:49 ` Troy Mitchell
2 siblings, 0 replies; 4+ messages in thread
From: Anand Moon @ 2026-11-10 6:14 UTC (permalink / raw)
To: Troy Mitchell, Andi Shyti, Sebastian Reichel, Yixun Lan,
Aurelien Jarno, open list:I2C SUBSYSTEM HOST DRIVERS, open list,
open list:SYSTEM RESET/SHUTDOWN DRIVERS,
open list:RISC-V SPACEMIT SoC Support,
open list:RISC-V SPACEMIT SoC Support
Cc: Anand Moon
The SpacemiT P1 power-off and restart handlers use regmap_set_bits()
to access the PMIC. However, these handlers run in an atomic notifier
context (specifically within an rcu_read_lock() critical section), while
the I2C-backed regmap uses a sleepable mutex.
This triggers a "WARNING: suspicious RCU usage" warning due to an illegal
context switch in the RCU read-side critical section during shutdown on the
Banana Pi BPI-F3:
[ 563.803475] WARNING: suspicious RCU usage
[ 563.807813] 7.3.1-ARCH-RISC-V #1 Not tainted
[ 563.816614] ./include/linux/rcupdate.h:387 Illegal context switch in RCU read-side critical section!
...
[ 563.841445] locks held by shutdown/1: 2, last CPU#0:
[ 563.846694] #0: ffffffff8404fb20 (system_transition_mutex){+.+.}-{4:4}, at: __do_sys_reboot+0x230/0x29e
[ 563.856806] #1: ffffffff84167860 (rcu_read_lock){....}-{1:3}, at: atomic_notifier_call_chain+0x4a/0x1c2
Replace the regmap operations with atomic I2C transfers to avoid sleeping
in an atomic context. Preserve unrelated register bits through
read-modify-write operations and check for atomic I2C support before
registering the handlers.
Obtain the I2C client from the parent device and verify that the adapter
supports atomic transfers before registering the handlers.
Tested on the Banana Pi BPI-F3 (SpacemiT K1).
Assisted-by: LLM
Cc: Troy Mitchell <troy.mitchell@linux.spacemit.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Fixes: 28124cc0fb8c ("driver: reset: spacemit-p1: add driver for poweroff/reboot")
Signed-off-by: Anand Moon <linux.amoon@gmail.com>
---
drivers/power/reset/spacemit-p1-reboot.c | 86 ++++++++++++++++++++----
1 file changed, 74 insertions(+), 12 deletions(-)
diff --git a/drivers/power/reset/spacemit-p1-reboot.c b/drivers/power/reset/spacemit-p1-reboot.c
index c0454b547718..8fcb3eadddf3 100644
--- a/drivers/power/reset/spacemit-p1-reboot.c
+++ b/drivers/power/reset/spacemit-p1-reboot.c
@@ -4,8 +4,9 @@
*/
#include <linux/bits.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
#include <linux/platform_device.h>
-#include <linux/regmap.h>
#include <linux/reboot.h>
/* Power Control Register 2 */
@@ -13,13 +14,68 @@
#define PWR_CTRL2_SHUTDOWN BIT(2) /* Shutdown request */
#define PWR_CTRL2_RST BIT(1) /* Reset request */
+static int spacemit_p1_read_atomic(struct i2c_client *client,
+ u8 reg, u8 *val)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = client->flags & I2C_M_TEN,
+ .len = 1,
+ .buf = ®,
+ },
+ {
+ .addr = client->addr,
+ .flags = (client->flags & I2C_M_TEN) | I2C_M_RD,
+ .len = 1,
+ .buf = val,
+ },
+ };
+ int ret;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+
+ return ret == ARRAY_SIZE(msgs) ? 0 : ret < 0 ? ret : -EIO;
+}
+
+static int spacemit_p1_write_atomic(struct i2c_client *client,
+ u8 reg, u8 val)
+{
+ u8 buf[] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = client->flags & I2C_M_TEN,
+ .len = sizeof(buf),
+ .buf = buf,
+ };
+ int ret;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return ret == 1 ? 0 : ret < 0 ? ret : -EIO;
+}
+
+static int spacemit_p1_update_bits_atomic(struct i2c_client *client,
+ u8 reg, u8 mask)
+{
+ u8 val;
+ int ret;
+
+ ret = spacemit_p1_read_atomic(client, reg, &val);
+ if (ret)
+ return ret;
+
+ return spacemit_p1_write_atomic(client, reg, val | mask);
+}
+
static int spacemit_p1_pwroff_handler(struct sys_off_data *data)
{
- struct regmap *regmap = data->cb_data;
+ struct i2c_client *client = data->cb_data;
int ret;
/* Put the PMIC into shutdown state */
- ret = regmap_set_bits(regmap, PWR_CTRL2, PWR_CTRL2_SHUTDOWN);
+ ret = spacemit_p1_update_bits_atomic(client, PWR_CTRL2,
+ PWR_CTRL2_SHUTDOWN);
if (ret) {
dev_err(data->dev, "shutdown failed: %d\n", ret);
return notifier_from_errno(ret);
@@ -30,11 +86,12 @@ static int spacemit_p1_pwroff_handler(struct sys_off_data *data)
static int spacemit_p1_restart_handler(struct sys_off_data *data)
{
- struct regmap *regmap = data->cb_data;
+ struct i2c_client *client = data->cb_data;
int ret;
/* Put the PMIC into reset state */
- ret = regmap_set_bits(regmap, PWR_CTRL2, PWR_CTRL2_RST);
+ ret = spacemit_p1_update_bits_atomic(client, PWR_CTRL2,
+ PWR_CTRL2_RST);
if (ret) {
dev_err(data->dev, "restart failed: %d\n", ret);
return notifier_from_errno(ret);
@@ -46,21 +103,26 @@ static int spacemit_p1_restart_handler(struct sys_off_data *data)
static int spacemit_p1_reboot_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct regmap *regmap;
+ struct i2c_client *client;
int ret;
- regmap = dev_get_regmap(dev->parent, NULL);
- if (!regmap)
- return -ENODEV;
+ client = i2c_verify_client(dev->parent);
+ if (!client)
+ return dev_err_probe(dev, -ENODEV,
+ "Failed to find parent I2C client\n");
+
+ if (!client->adapter->algo->xfer_atomic)
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "Atomic I2C transfers not supported\n");
- ret = devm_register_power_off_handler(dev, &spacemit_p1_pwroff_handler,
- regmap);
+ ret = devm_register_power_off_handler(dev, spacemit_p1_pwroff_handler,
+ client);
if (ret)
return dev_err_probe(dev, ret,
"Failed to register power off handler\n");
ret = devm_register_restart_handler(dev, spacemit_p1_restart_handler,
- regmap);
+ client);
if (ret)
return dev_err_probe(dev, ret,
"Failed to register restart handler\n");
--
2.56.0
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v1 1/2] i2c: spacemit: Avoid sleeping during bus reset in atomic context
2026-11-10 6:14 [PATCH v1 1/2] i2c: spacemit: Avoid sleeping during bus reset in atomic context Anand Moon
2026-11-10 6:14 ` [PATCH v1 2/2] power: reset: spacemit-p1: Use atomic I2C transfers for power-off and restart Anand Moon
@ 2026-11-10 6:41 ` Troy Mitchell
2026-11-10 6:49 ` Troy Mitchell
2 siblings, 0 replies; 4+ messages in thread
From: Troy Mitchell @ 2026-11-10 6:41 UTC (permalink / raw)
To: Anand Moon
Cc: Troy Mitchell, Andi Shyti, Sebastian Reichel, Yixun Lan,
Aurelien Jarno, linux-i2c, linux-kernel, linux-pm, linux-riscv,
spacemit, Alex Elder
[-- Attachment #1: Type: text/plain, Size: 834 bytes --]
On Tue, 10 Nov 2026 11:44:26 +0530, Anand Moon wrote:
> The spacemit_i2c_conditionally_reset_bus() function uses usleep_range()
> to wait after triggering a bus reset request. However, this function can
> be invoked during atomic I2C transfers (such as power-off or restart
> handlers running in atomic notifier chains), where sleeping is illegal.
>
> Replace the sleep operation with the driver's internal helper,
> spacemit_i2c_delay(), which conditionally uses udelay() when running in
> an atomic context, avoiding "scheduling while atomic" bugs.
This usleep_range() was left behind when the PIO path and
spacemit_i2c_delay() were added, so please add:
Fixes: 5dd75dac1b35 ("i2c: spacemit: introduce pio for k1")
Otherwise, LGTM.
Reviewed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
--
Troy Mitchell
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v1 1/2] i2c: spacemit: Avoid sleeping during bus reset in atomic context
2026-11-10 6:14 [PATCH v1 1/2] i2c: spacemit: Avoid sleeping during bus reset in atomic context Anand Moon
2026-11-10 6:14 ` [PATCH v1 2/2] power: reset: spacemit-p1: Use atomic I2C transfers for power-off and restart Anand Moon
2026-11-10 6:41 ` [PATCH v1 1/2] i2c: spacemit: Avoid sleeping during bus reset in atomic context Troy Mitchell
@ 2026-11-10 6:49 ` Troy Mitchell
2 siblings, 0 replies; 4+ messages in thread
From: Troy Mitchell @ 2026-11-10 6:49 UTC (permalink / raw)
To: Anand Moon, Troy Mitchell, Andi Shyti, Sebastian Reichel,
Yixun Lan, Aurelien Jarno, open list:I2C SUBSYSTEM HOST DRIVERS,
open list, open list:SYSTEM RESET/SHUTDOWN DRIVERS,
open list:RISC-V SPACEMIT SoC Support,
open list:RISC-V SPACEMIT SoC Support
Cc: Alex Elder
[-- Attachment #1: Type: text/plain, Size: 691 bytes --]
On Tue Nov 10, 2026 at 6:14 AM GMT, Anand Moon wrote:
> The spacemit_i2c_conditionally_reset_bus() function uses usleep_range()
> to wait after triggering a bus reset request. However, this function can
> be invoked during atomic I2C transfers (such as power-off or restart
> handlers running in atomic notifier chains), where sleeping is illegal.
>
> Replace the sleep operation with the driver's internal helper,
> spacemit_i2c_delay(), which conditionally uses udelay() when running in
> an atomic context, avoiding "scheduling while atomic" bugs.
>
> Assisted-by: LLM
Also, please drop this tag. The same applies to patch 2.
See commit 1e035297ffda.
--
Troy Mitchell
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread