diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 72dea67722d4..1030066b8200 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -16,6 +16,7 @@ * more details. */ +#include #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include struct rk808_reg_data { int addr; @@ -658,7 +660,7 @@ static void rk808_device_shutdown_prepare(void) } } -static void rk808_device_shutdown(void) +static void rk808_syscore_shutdown(void) { int ret; struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); @@ -676,13 +678,35 @@ static void rk808_device_shutdown(void) regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG, (0x3 << 2), (0x0 << 2)); - if (pm_shutdown) { - ret = pm_shutdown(rk808->regmap); - if (ret) - dev_err(&rk808_i2c_client->dev, "power off error!\n"); + /* + * For PMIC that power off supplies by write register via i2c bus, + * it's better to do power off at syscore shutdown here. + * + * Because when run to kernel's "pm_power_off" call, i2c may has + * been stopped or PMIC may not be able to get i2c transfer while + * there are too many devices are competiting. + */ + if (system_state == SYSTEM_POWER_OFF) { + /* power off supplies ! */ + if (pm_shutdown) { + dev_info(&rk808_i2c_client->dev, "System power off\n"); + ret = pm_shutdown(rk808->regmap); + if (ret) + dev_err(&rk808_i2c_client->dev, + "System power off error!\n"); + mdelay(10); + dev_info(&rk808_i2c_client->dev, + "Cpu should never reach here, stop!\n"); + while (1) + ; + } } } +static struct syscore_ops rk808_syscore_ops = { + .shutdown = rk808_syscore_shutdown, +}; + static ssize_t rk8xx_dbg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -926,7 +950,7 @@ static int rk808_probe(struct i2c_client *client, } if (pm_shutdown_fn) { pm_shutdown = pm_shutdown_fn; - pm_power_off = rk808_device_shutdown; + register_syscore_ops(&rk808_syscore_ops); } } @@ -992,10 +1016,11 @@ static int rk808_remove(struct i2c_client *client) regmap_del_irq_chip(client->irq, rk808->irq_data); mfd_remove_devices(&client->dev); - if (pm_power_off == rk808_device_shutdown) - pm_power_off = NULL; + if (pm_power_off_prepare == rk808_device_shutdown_prepare) pm_power_off_prepare = NULL; + if (pm_shutdown) + unregister_syscore_ops(&rk808_syscore_ops); return 0; }