diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 213a642869d5..fd755d98af84 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -686,33 +687,17 @@ 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 rk808_pm_power_off(void) +static void rk805_device_shutdown_prepare(void) { int ret; - unsigned int reg, bit; struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); - switch (rk808->variant) { - case RK805_ID: - reg = RK805_DEV_CTRL_REG; - bit = DEV_OFF; - break; - case RK808_ID: - reg = RK808_DEVCTRL_REG, - bit = DEV_OFF_RST; - break; - case RK816_ID: - reg = RK816_DEV_CTRL_REG, - bit = DEV_OFF; - break; - case RK818_ID: - reg = RK818_DEVCTRL_REG; - bit = DEV_OFF; - break; - default: + if (!rk808) return; - } - ret = regmap_update_bits(rk808->regmap, reg, bit, bit); + + ret = regmap_update_bits(rk808->regmap, + RK805_GPIO_IO_POL_REG, + SLP_SD_MSK, SHUTDOWN_FUN); if (ret) dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n"); } @@ -762,10 +747,45 @@ static void rk817_shutdown_prepare(void) mdelay(2); } -static void rk8xx_shutdown(struct i2c_client *client) +static void rk8xx_device_shutdown(void) { - struct rk808 *rk808 = i2c_get_clientdata(client); - int ret = 0; + int ret; + unsigned int reg, bit; + struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); + + switch (rk808->variant) { + case RK805_ID: + reg = RK805_DEV_CTRL_REG; + bit = DEV_OFF; + break; + case RK808_ID: + reg = RK808_DEVCTRL_REG, + bit = DEV_OFF_RST; + break; + case RK816_ID: + reg = RK816_DEV_CTRL_REG; + bit = DEV_OFF; + break; + case RK818_ID: + reg = RK818_DEVCTRL_REG; + bit = DEV_OFF; + break; + default: + return; + } + + ret = regmap_update_bits(rk808->regmap, reg, bit, bit); + if (ret) + dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n"); +} + +/* Called in syscore shutdown */ +static void (*pm_shutdown)(void); + +static void rk8xx_syscore_shutdown(void) +{ + int ret; + struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); if (!rk808) { dev_warn(&rk808_i2c_client->dev, @@ -780,24 +800,104 @@ static void rk8xx_shutdown(struct i2c_client *client) 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) { + if (rk808->variant == RK809_ID || rk808->variant == RK817_ID) { + ret = regmap_update_bits(rk808->regmap, + RK817_SYS_CFG(3), + RK817_SLPPIN_FUNC_MSK, + SLPPIN_DN_FUN); + if (ret) { + dev_warn(&rk808_i2c_client->dev, + "Cannot switch to power down function\n"); + } + } - switch (rk808->variant) { - case RK805_ID: - ret = regmap_update_bits(rk808->regmap, - RK805_GPIO_IO_POL_REG, - SLP_SD_MSK, - SHUTDOWN_FUN); + if (pm_shutdown) { + dev_info(&rk808_i2c_client->dev, "System power off\n"); + pm_shutdown(); + mdelay(10); + dev_info(&rk808_i2c_client->dev, + "Power off failed !\n"); + while (1) + ; + } + } +} + +static struct syscore_ops rk808_syscore_ops = { + .shutdown = rk8xx_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) +{ + pr_info("Dummy power off for RK8xx PMICs, should never reach here!\n"); + + while (1) + ; +} + +static ssize_t rk8xx_dbg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + char cmd; + u32 input[2], addr, data; + struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); + + ret = sscanf(buf, "%c ", &cmd); + if (ret != 1) { + pr_err("Unknown command\n"); + goto out; + } + switch (cmd) { + case 'w': + ret = sscanf(buf, "%c %x %x ", &cmd, &input[0], &input[1]); + if (ret != 3) { + pr_err("error! cmd format: echo w [addr] [value]\n"); + goto out; + }; + addr = input[0] & 0xff; + data = input[1] & 0xff; + pr_info("cmd : %c %x %x\n\n", cmd, input[0], input[1]); + regmap_write(rk808->regmap, addr, data); + regmap_read(rk808->regmap, addr, &data); + pr_info("new: %x %x\n", addr, data); break; - case RK809_ID: - case RK817_ID: - rk817_shutdown_prepare(); + case 'r': + ret = sscanf(buf, "%c %x ", &cmd, &input[0]); + if (ret != 2) { + pr_err("error! cmd format: echo r [addr]\n"); + goto out; + }; + pr_info("cmd : %c %x\n\n", cmd, input[0]); + addr = input[0] & 0xff; + regmap_read(rk808->regmap, addr, &data); + pr_info("%x %x\n", input[0], data); break; default: - return; + pr_err("Unknown command\n"); + break; } - if (ret) - dev_warn(&client->dev, - "Cannot switch to power down function\n"); + +out: + return count; } static int rk817_pinctrl_init(struct device *dev, struct rk808 *rk808) @@ -1003,54 +1103,6 @@ static void rk817_of_property_prepare(struct rk808 *rk808, struct device *dev) dev_err(dev, "failed to register reboot nb\n"); } -static ssize_t rk8xx_dbg_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int ret; - char cmd; - u32 input[2], addr, data; - struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); - - ret = sscanf(buf, "%c ", &cmd); - if (ret != 1) { - pr_err("Unknown command\n"); - goto out; - } - switch (cmd) { - case 'w': - ret = sscanf(buf, "%c %x %x ", &cmd, &input[0], &input[1]); - if (ret != 3) { - pr_err("error! cmd format: echo w [addr] [value]\n"); - goto out; - }; - addr = input[0] & 0xff; - data = input[1] & 0xff; - pr_info("cmd : %c %x %x\n\n", cmd, input[0], input[1]); - regmap_write(rk808->regmap, addr, data); - regmap_read(rk808->regmap, addr, &data); - pr_info("new: %x %x\n", addr, data); - break; - case 'r': - ret = sscanf(buf, "%c %x ", &cmd, &input[0]); - if (ret != 2) { - pr_err("error! cmd format: echo r [addr]\n"); - goto out; - }; - pr_info("cmd : %c %x\n\n", cmd, input[0]); - addr = input[0] & 0xff; - regmap_read(rk808->regmap, addr, &data); - pr_info("%x %x\n", input[0], data); - break; - default: - pr_err("Unknown command\n"); - break; - } - -out: - return count; -} - static struct kobject *rk8xx_kobj; static struct device_attribute rk8xx_attrs = __ATTR(rk8xx_dbg, 0200, NULL, rk8xx_dbg_store); @@ -1077,15 +1129,15 @@ static int rk808_probe(struct i2c_client *client, unsigned char pmic_id_msb, pmic_id_lsb; u8 on_source = 0, off_source = 0; unsigned int on, off; - int msb, lsb; + int pm_off = 0, msb, lsb; int nr_pre_init_regs; int nr_cells; - int ret; int i; void (*of_property_prepare_fn)(struct rk808 *rk808, struct device *dev) = NULL; int (*pinctrl_init)(struct device *dev, struct rk808 *rk808) = NULL; + void (*device_shutdown_fn)(void) = NULL; rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL); if (!rk808) @@ -1132,6 +1184,8 @@ static int rk808_probe(struct i2c_client *client, suspend_reg_num = ARRAY_SIZE(rk805_suspend_reg); resume_reg = rk805_resume_reg; resume_reg_num = ARRAY_SIZE(rk805_resume_reg); + device_shutdown_fn = rk8xx_device_shutdown; + rk808->pm_pwroff_prep_fn = rk805_device_shutdown_prepare; break; case RK808_ID: rk808->regmap_cfg = &rk808_regmap_config; @@ -1140,6 +1194,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); + device_shutdown_fn = rk8xx_device_shutdown; break; case RK816_ID: rk808->regmap_cfg = &rk816_regmap_config; @@ -1155,6 +1210,7 @@ static int rk808_probe(struct i2c_client *client, suspend_reg_num = ARRAY_SIZE(rk816_suspend_reg); resume_reg = rk816_resume_reg; resume_reg_num = ARRAY_SIZE(rk816_resume_reg); + device_shutdown_fn = rk8xx_device_shutdown; break; case RK818_ID: rk808->regmap_cfg = &rk818_regmap_config; @@ -1169,6 +1225,7 @@ static int rk808_probe(struct i2c_client *client, suspend_reg_num = ARRAY_SIZE(rk818_suspend_reg); resume_reg = rk818_resume_reg; resume_reg_num = ARRAY_SIZE(rk818_resume_reg); + device_shutdown_fn = rk8xx_device_shutdown; break; case RK809_ID: case RK817_ID: @@ -1180,6 +1237,7 @@ static int rk808_probe(struct i2c_client *client, nr_cells = ARRAY_SIZE(rk817s); on_source = RK817_ON_SOURCE_REG; off_source = RK817_OFF_SOURCE_REG; + rk808->pm_pwroff_prep_fn = rk817_shutdown_prepare; of_property_prepare_fn = rk817_of_property_prepare; pinctrl_init = rk817_pinctrl_init; break; @@ -1226,9 +1284,9 @@ static int rk808_probe(struct i2c_client *client, for (i = 0; i < nr_pre_init_regs; i++) { ret = regmap_update_bits(rk808->regmap, - pre_init_reg[i].addr, - pre_init_reg[i].mask, - pre_init_reg[i].value); + pre_init_reg[i].addr, + pre_init_reg[i].mask, + pre_init_reg[i].value); if (ret) { dev_err(&client->dev, "0x%x write err\n", @@ -1272,8 +1330,17 @@ static int rk808_probe(struct i2c_client *client, goto err_irq; } - if (of_property_read_bool(np, "rockchip,system-power-controller")) - pm_power_off = rk808_pm_power_off; + pm_off = of_property_read_bool(np, "rockchip,system-power-controller"); + if (pm_off) { + if (!pm_power_off_prepare) + pm_power_off_prepare = rk808->pm_pwroff_prep_fn; + + if (device_shutdown_fn) { + register_syscore_ops(&rk808_syscore_ops); + /* power off system in the syscore shutdown ! */ + pm_shutdown = device_shutdown_fn; + } + } rk8xx_kobj = kobject_create_and_add("rk8xx", NULL); if (rk8xx_kobj) { @@ -1282,6 +1349,9 @@ static int rk808_probe(struct i2c_client *client, dev_err(&client->dev, "create rk8xx sysfs error\n"); } + if (!pm_power_off) + pm_power_off = rk808_pm_power_off_dummy; + return 0; err_irq: @@ -1296,20 +1366,31 @@ static int rk808_remove(struct i2c_client *client) struct rk808 *rk808 = i2c_get_clientdata(client); regmap_del_irq_chip(client->irq, rk808->irq_data); + mfd_remove_devices(&client->dev); /** * pm_power_off may points to a function from another module. * Check if the pointer is set by us and only then overwrite it. */ - if (pm_power_off == rk808_pm_power_off) + if (pm_power_off == rk808_pm_power_off_dummy) pm_power_off = NULL; + /** + * As above, check if the pointer is set by us before overwrite. + */ + if (rk808->pm_pwroff_prep_fn && + pm_power_off_prepare == rk808->pm_pwroff_prep_fn) + pm_power_off_prepare = NULL; + + if (pm_shutdown) + unregister_syscore_ops(&rk808_syscore_ops); + return 0; } static int __maybe_unused rk8xx_suspend(struct device *dev) { - struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev)); + struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); int i, ret = 0; int value; @@ -1372,9 +1453,9 @@ static int __maybe_unused rk8xx_suspend(struct device *dev) static int __maybe_unused rk8xx_resume(struct device *dev) { - struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev)); - int value; + struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); int i, ret = 0; + int value; for (i = 0; i < resume_reg_num; i++) { ret = regmap_update_bits(rk808->regmap, @@ -1434,7 +1515,6 @@ static struct i2c_driver rk808_i2c_driver = { }, .probe = rk808_probe, .remove = rk808_remove, - .shutdown = rk8xx_shutdown, }; #ifdef CONFIG_ROCKCHIP_THUNDER_BOOT diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index 917174f8c2eb..7cdc5dfa4fce 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -1067,6 +1067,7 @@ struct rk808 { long variant; const struct regmap_config *regmap_cfg; const struct regmap_irq_chip *regmap_irq_chip; + void (*pm_pwroff_prep_fn)(void); struct rk808_pin_info *pins; }; #endif /* __LINUX_REGULATOR_RK808_H */