From cf84dc0bb40f4b41f7853f224b37954cf273ee24 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Tue, 21 Jul 2020 09:34:02 +0200 Subject: mfd: rn5t618: Make restart handler atomic safe The restart handler is executed during the shutdown phase which is atomic/irq-less. The i2c framework supports atomic transfers since commit 63b96983a5dd ("i2c: core: introduce callbacks for atomic transfers") to address this use case. Using i2c regmap in that situation is not allowed: [ 165.177465] [ BUG: Invalid wait context ] [ 165.181479] 5.8.0-rc3-00003-g0e9088558027-dirty #11 Not tainted [ 165.187400] ----------------------------- [ 165.191410] systemd-shutdow/1 is trying to lock: [ 165.196030] d85b4438 (rn5t618:170:(&rn5t618_regmap_config)->lock){+.+.}-{3:3}, at: regmap_update_bits_base+0x30/0x70 [ 165.206573] other info that might help us debug this: [ 165.211625] context-{4:4} [ 165.214248] 2 locks held by systemd-shutdow/1: [ 165.218691] #0: c131c47c (system_transition_mutex){+.+.}-{3:3}, at: __do_sys_reboot+0x90/0x204 [ 165.227405] #1: c131efb4 (rcu_read_lock){....}-{1:2}, at: __atomic_notifier_call_chain+0x0/0x118 [ 165.236288] stack backtrace: [ 165.239174] CPU: 0 PID: 1 Comm: systemd-shutdow Not tainted 5.8.0-rc3-00003-g0e9088558027-dirty #11 [ 165.248220] Hardware name: Freescale i.MX6 SoloLite (Device Tree) [ 165.254330] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 165.262084] [] (show_stack) from [] (dump_stack+0xe8/0x120) [ 165.269407] [] (dump_stack) from [] (__lock_acquire+0x81c/0x2ca0) [ 165.277246] [] (__lock_acquire) from [] (lock_acquire+0xe4/0x490) [ 165.285090] [] (lock_acquire) from [] (__mutex_lock+0x74/0x954) [ 165.292756] [] (__mutex_lock) from [] (mutex_lock_nested+0x1c/0x24) [ 165.300769] [] (mutex_lock_nested) from [] (regmap_update_bits_base+0x30/0x70) [ 165.309741] [] (regmap_update_bits_base) from [] (rn5t618_trigger_poweroff_sequence+0x34/0x64) [ 165.320097] [] (rn5t618_trigger_poweroff_sequence) from [] (rn5t618_restart+0xc/0x2c) [ 165.329669] [] (rn5t618_restart) from [] (notifier_call_chain+0x48/0x80) [ 165.338113] [] (notifier_call_chain) from [] (__atomic_notifier_call_chain+0x70/0x118) [ 165.347770] [] (__atomic_notifier_call_chain) from [] (atomic_notifier_call_chain+0x18/0x20) [ 165.357949] [] (atomic_notifier_call_chain) from [] (machine_restart+0x68/0x80) [ 165.367001] [] (machine_restart) from [] (__do_sys_reboot+0x11c/0x204) [ 165.375272] [] (__do_sys_reboot) from [] (ret_fast_syscall+0x0/0x28) [ 165.383364] Exception stack(0xd80a5fa8 to 0xd80a5ff0) [ 165.388420] 5fa0: 00406948 00000000 fee1dead 28121969 01234567 73299b00 [ 165.396602] 5fc0: 00406948 00000000 00000000 00000058 be91abc8 00000000 be91ab60 004056f8 [ 165.404781] 5fe0: 00000058 be91aabc b6ed4d45 b6e56746 Signed-off-by: Andreas Kemnade Signed-off-by: Lee Jones --- drivers/mfd/rn5t618.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'drivers/mfd/rn5t618.c') diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c index 232de50562f9..7497edf6ee41 100644 --- a/drivers/mfd/rn5t618.c +++ b/drivers/mfd/rn5t618.c @@ -77,7 +77,7 @@ static const struct regmap_irq_chip rc5t619_irq_chip = { .mask_invert = true, }; -static struct rn5t618 *rn5t618_pm_power_off; +static struct i2c_client *rn5t618_pm_power_off; static struct notifier_block rn5t618_restart_handler; static int rn5t618_irq_init(struct rn5t618 *rn5t618) @@ -110,13 +110,38 @@ static int rn5t618_irq_init(struct rn5t618 *rn5t618) static void rn5t618_trigger_poweroff_sequence(bool repower) { + int ret; + /* disable automatic repower-on */ - regmap_update_bits(rn5t618_pm_power_off->regmap, RN5T618_REPCNT, - RN5T618_REPCNT_REPWRON, - repower ? RN5T618_REPCNT_REPWRON : 0); + ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_REPCNT); + if (ret < 0) + goto err; + + ret &= ~RN5T618_REPCNT_REPWRON; + if (repower) + ret |= RN5T618_REPCNT_REPWRON; + + ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off, + RN5T618_REPCNT, (u8)ret); + if (ret < 0) + goto err; + /* start power-off sequence */ - regmap_update_bits(rn5t618_pm_power_off->regmap, RN5T618_SLPCNT, - RN5T618_SLPCNT_SWPWROFF, RN5T618_SLPCNT_SWPWROFF); + ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_SLPCNT); + if (ret < 0) + goto err; + + ret |= RN5T618_SLPCNT_SWPWROFF; + + ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off, + RN5T618_SLPCNT, (u8)ret); + if (ret < 0) + goto err; + + return; + +err: + dev_alert(&rn5t618_pm_power_off->dev, "Failed to shutdown (err = %d)\n", ret); } static void rn5t618_power_off(void) @@ -189,7 +214,7 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c) return ret; } - rn5t618_pm_power_off = priv; + rn5t618_pm_power_off = i2c; if (of_device_is_system_power_controller(i2c->dev.of_node)) { if (!pm_power_off) pm_power_off = rn5t618_power_off; @@ -211,9 +236,7 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c) static int rn5t618_i2c_remove(struct i2c_client *i2c) { - struct rn5t618 *priv = i2c_get_clientdata(i2c); - - if (priv == rn5t618_pm_power_off) { + if (i2c == rn5t618_pm_power_off) { rn5t618_pm_power_off = NULL; pm_power_off = NULL; } -- cgit