From 83bb9ed30cc76fedd887a42d8219b4030afff6f4 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Mon, 8 Apr 2019 12:37:52 +0800 Subject: [PATCH] mfd: rk808: support power off system in syscore shutdown For PMIC that power off supplies by write register via i2c bus, it's better to do power off at syscore shutdown. 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. This patch effects on PMIC: RK808/RK818/RK816, not including RK805 which power off system by pull up pmic sleep pin in ATF. Change-Id: I2eb139f75abf32a9c239b56a8e65e76c42927e87 Signed-off-by: Joseph Chen --- drivers/mfd/rk808.c | 190 ++++++++++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 53 deletions(-) diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index be9a5c416a95..9bfa7780fcfa 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -27,6 +27,7 @@ #include #include #include +#include struct rk808_reg_data { int addr; @@ -96,6 +97,55 @@ static bool rk818_is_volatile_reg(struct device *dev, unsigned int reg) return false; } +static int rk805_shutdown_prepare(struct rk808 *rk808) +{ + int ret; + + /* close rtc int when power off */ + regmap_update_bits(rk808->regmap, + RK808_INT_STS_MSK_REG1, + (0x3 << 5), (0x3 << 5)); + regmap_update_bits(rk808->regmap, + RK808_RTC_INT_REG, + (0x3 << 2), (0x0 << 2)); + + /* pmic sleep shutdown function */ + ret = regmap_update_bits(rk808->regmap, + RK805_GPIO_IO_POL_REG, + SLP_SD_MSK, SHUTDOWN_FUN); + return ret; +} + +static int rk808_shutdown(struct regmap *regmap) +{ + int ret; + + ret = regmap_update_bits(regmap, + RK808_DEVCTRL_REG, + DEV_OFF_RST, DEV_OFF_RST); + return ret; +} + +static int rk816_shutdown(struct regmap *regmap) +{ + int ret; + + ret = regmap_update_bits(regmap, + RK816_DEV_CTRL_REG, + DEV_OFF, DEV_OFF); + return ret; +} + +static int rk818_shutdown(struct regmap *regmap) +{ + int ret; + + ret = regmap_update_bits(regmap, + RK818_DEVCTRL_REG, + DEV_OFF, DEV_OFF); + return ret; +} + static const struct regmap_config rk818_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -604,29 +654,13 @@ static const struct regmap_irq_chip rk818_irq_chip = { .init_ack_masked = true, }; +static int (*pm_shutdown)(struct regmap *regmap); +static int (*pm_shutdown_prepare)(struct rk808 *rk808); static struct i2c_client *rk808_i2c_client; static struct rk808_reg_data *suspend_reg, *resume_reg; static int suspend_reg_num, resume_reg_num; -static void rk805_device_shutdown(void) -{ - int ret; - struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); - - if (!rk808) { - dev_warn(&rk808_i2c_client->dev, - "have no rk805, so do nothing here\n"); - return; - } - - ret = regmap_update_bits(rk808->regmap, - RK805_DEV_CTRL_REG, - DEV_OFF, DEV_OFF); - if (ret) - dev_err(&rk808_i2c_client->dev, "power off error!\n"); -} - -static void rk808_device_shutdown(void) +static void rk808_device_shutdown_prepare(void) { int ret; struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); @@ -637,47 +671,75 @@ static void rk808_device_shutdown(void) return; } - ret = regmap_update_bits(rk808->regmap, - RK808_DEVCTRL_REG, - DEV_OFF_RST, DEV_OFF_RST); - if (ret) - dev_err(&rk808_i2c_client->dev, "power off error!\n"); + if (pm_shutdown_prepare) { + ret = pm_shutdown_prepare(rk808); + if (ret) + dev_err(&rk808_i2c_client->dev, + "power off prepare error!\n"); + } } -static void rk816_device_shutdown(void) +static void rk808_syscore_shutdown(void) { int ret; struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); if (!rk808) { dev_warn(&rk808_i2c_client->dev, - "have no rk805, so do nothing here\n"); + "have no rk808, so do nothing here\n"); return; } - ret = regmap_update_bits(rk808->regmap, - RK816_DEV_CTRL_REG, - DEV_OFF, DEV_OFF); - if (ret) - dev_err(&rk808_i2c_client->dev, "power off error!\n"); + /* close rtc int when power off */ + regmap_update_bits(rk808->regmap, + RK808_INT_STS_MSK_REG1, + (0x3 << 5), (0x3 << 5)); + regmap_update_bits(rk808->regmap, + RK808_RTC_INT_REG, + (0x3 << 2), (0x0 << 2)); + /* + * 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 void rk818_device_shutdown(void) +static struct syscore_ops rk808_syscore_ops = { + .shutdown = rk808_syscore_shutdown, +}; + +/* + * RK8xx PMICs would do real power off in syscore shutdown, if "pm_power_off" + * is not assigned(e.g. PSCI is not enabled), we have to provide a dummy + * callback for it, otherwise there comes a halt in Reboot system call: + * + * if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) + * cmd = LINUX_REBOOT_CMD_HALT; + */ +static void rk808_pm_power_off_dummy(void) { - int ret; - struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); + pr_info("Dummy power off for RK8xx PMICs, should never reach here!\n"); - if (!rk808) { - dev_warn(&rk808_i2c_client->dev, - "have no rk818, so do nothing here\n"); - return; - } - - ret = regmap_update_bits(rk808->regmap, - RK818_DEVCTRL_REG, - DEV_OFF, DEV_OFF); - if (ret) - dev_err(&rk808_i2c_client->dev, "power off error!\n"); + while (1) + ; } static ssize_t rk8xx_dbg_store(struct device *dev, @@ -745,7 +807,8 @@ static int rk808_probe(struct i2c_client *client, const struct rk808_reg_data *pre_init_reg; const struct regmap_irq_chip *irq_chip, *battery_irq_chip = NULL; const struct mfd_cell *cells; - void (*pm_pwroff_fn)(void); + int (*pm_shutdown_fn)(struct regmap *regmap) = NULL; + int (*pm_shutdown_prepare_fn)(struct rk808 *rk808) = NULL; int nr_pre_init_regs; int nr_cells; int pm_off = 0, msb, lsb; @@ -782,7 +845,7 @@ static int rk808_probe(struct i2c_client *client, nr_pre_init_regs = ARRAY_SIZE(rk805_pre_init_reg); cells = rk805s; nr_cells = ARRAY_SIZE(rk805s); - pm_pwroff_fn = rk805_device_shutdown; + pm_shutdown_prepare_fn = rk805_shutdown_prepare; suspend_reg = rk805_suspend_reg; suspend_reg_num = ARRAY_SIZE(rk805_suspend_reg); resume_reg = rk805_resume_reg; @@ -795,7 +858,7 @@ static int rk808_probe(struct i2c_client *client, nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg); cells = rk808s; nr_cells = ARRAY_SIZE(rk808s); - pm_pwroff_fn = rk808_device_shutdown; + pm_shutdown_fn = rk808_shutdown; break; case RK816_ID: rk808->regmap_cfg = &rk816_regmap_config; @@ -805,7 +868,7 @@ static int rk808_probe(struct i2c_client *client, nr_pre_init_regs = ARRAY_SIZE(rk816_pre_init_reg); cells = rk816s; nr_cells = ARRAY_SIZE(rk816s); - pm_pwroff_fn = rk816_device_shutdown; + pm_shutdown_fn = rk816_shutdown; suspend_reg = rk816_suspend_reg; suspend_reg_num = ARRAY_SIZE(rk816_suspend_reg); resume_reg = rk816_resume_reg; @@ -818,7 +881,7 @@ static int rk808_probe(struct i2c_client *client, nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg); cells = rk818s; nr_cells = ARRAY_SIZE(rk818s); - pm_pwroff_fn = rk818_device_shutdown; + pm_shutdown_fn = rk818_shutdown; suspend_reg = rk818_suspend_reg; suspend_reg_num = ARRAY_SIZE(rk818_suspend_reg); resume_reg = rk818_resume_reg; @@ -889,8 +952,22 @@ static int rk808_probe(struct i2c_client *client, pm_off = of_property_read_bool(np, "rockchip,system-power-controller"); - if (pm_off && !pm_power_off) { - pm_power_off = pm_pwroff_fn; + if (pm_off) { + if (pm_shutdown_prepare_fn) { + pm_shutdown_prepare = pm_shutdown_prepare_fn; + pm_power_off_prepare = rk808_device_shutdown_prepare; + } + if (pm_shutdown_fn) { + pm_shutdown = pm_shutdown_fn; + register_syscore_ops(&rk808_syscore_ops); + } + + /* + * If not assigned(e.g. PSCI is not enable), we provide a + * dummy for it to avoid halt in Reboot system call. + */ + if (!pm_power_off) + pm_power_off = rk808_pm_power_off_dummy; } rk8xx_kobj = kobject_create_and_add("rk8xx", NULL); @@ -954,7 +1031,14 @@ static int rk808_remove(struct i2c_client *client) struct rk808 *rk808 = i2c_get_clientdata(client); regmap_del_irq_chip(client->irq, rk808->irq_data); - pm_power_off = NULL; + mfd_remove_devices(&client->dev); + + if (pm_power_off == rk808_pm_power_off_dummy) + 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; }