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; }