sx150x-pinctrl: Avoid i2c transfer during kexec

machine_crash_shutdown() calls irq_bus_sync_unlock() with local irq
disabled. sx150x_irq_bus_sync tries to transfer over i2c bus and hangs
because of disabled irqs.

Add panic handler, so that sx150x_irq_bus_sync_unlock can
return before write to iomap register. Mulitple instances of sx150x
device may be present, hence a global flag to indicate
machine_crash_shutdown() context.

Change-Id: If992f730ba36b81b2a231f3605673e8bae278d16
Signed-off-by: Akshay Adiga <aadiga@codeaurora.org>
This commit is contained in:
Akshay Adiga
2020-07-22 11:17:07 +05:30
parent 9499b3ff29
commit 061cfeb8b0

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*
* Copyright (c) 2016, BayLibre, SAS. All rights reserved.
* Author: Neil Armstrong <narmstrong@baylibre.com>
@@ -40,6 +40,9 @@
#include "pinconf.h"
#include "pinctrl-utils.h"
static bool in_kexec_panic;
static struct notifier_block *panic_block;
/* The chip models of sx150x */
enum {
SX150X_123 = 0,
@@ -581,6 +584,14 @@ static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
struct sx150x_pinctrl *pctl =
gpiochip_get_data(irq_data_get_irq_chip_data(d));
/* In crash kexec panic path interrupt is disabled.
* Writing to regmap will need interrupt/polling mode,
* as it goes though i2c.
*/
if (in_kexec_panic) {
mutex_unlock(&pctl->lock);
return;
}
regmap_write(pctl->regmap, pctl->data->reg_irq_mask, pctl->irq.masked);
regmap_write(pctl->regmap, pctl->data->reg_sense, pctl->irq.sense);
mutex_unlock(&pctl->lock);
@@ -1102,6 +1113,14 @@ const struct regmap_config sx150x_regmap_config = {
.volatile_reg = sx150x_reg_volatile,
};
static int sx150x_panic_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
if (crash_kexec_post_notifiers)
in_kexec_panic = 1;
return NOTIFY_DONE;
}
static int sx150x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1301,6 +1320,12 @@ static struct i2c_driver sx150x_driver = {
static int __init sx150x_init(void)
{
panic_block = kzalloc(sizeof(*panic_block), GFP_KERNEL);
if (!panic_block)
return -ENOMEM;
panic_block->notifier_call = sx150x_panic_handler;
atomic_notifier_chain_register(&panic_notifier_list,
panic_block);
return i2c_add_driver(&sx150x_driver);
}
subsys_initcall(sx150x_init);