UPSTREAM: mfd: rk808: Check pm_power_off pointer

The function pointer pm_power_off may point to function from other
module (PSCI for example). If rk808 is removed, pm_power_off is
overwritten to NULL and the system cannot be powered off.

This patch checks if pm_power_off points to a module function.

Change-Id: I00de90d1f5f1cf6587167e59d7e577f484e1fc8c
Signed-off-by: Stefan Mavrodiev <stefan@olimex.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
(cherry picked from commit 7630499464)
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
This commit is contained in:
Stefan Mavrodiev
2019-06-07 15:42:25 +03:00
committed by Elaine Zhang
parent af2cb4b882
commit 62e9bedcf2
2 changed files with 118 additions and 111 deletions

View File

@@ -118,55 +118,6 @@ static bool rk818_is_volatile_reg(struct device *dev, unsigned int reg)
return false; 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 = { static const struct regmap_config rk818_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 8, .val_bits = 8,
@@ -762,32 +713,86 @@ static const struct regmap_irq_chip rk818_irq_chip = {
.init_ack_masked = true, .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 i2c_client *rk808_i2c_client;
static struct rk808_reg_data *suspend_reg, *resume_reg; static struct rk808_reg_data *suspend_reg, *resume_reg;
static int suspend_reg_num, resume_reg_num; static int suspend_reg_num, resume_reg_num;
static void rk808_device_shutdown_prepare(void) static void rk805_device_shutdown(void)
{ {
int ret; int ret;
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
if (!rk808) { if (!rk808)
dev_warn(&rk808_i2c_client->dev,
"have no rk808, so do nothing here\n");
return; return;
}
if (pm_shutdown_prepare) { ret = regmap_update_bits(rk808->regmap,
ret = pm_shutdown_prepare(rk808); RK805_DEV_CTRL_REG,
if (ret) DEV_OFF, DEV_OFF);
dev_err(&rk808_i2c_client->dev, if (ret)
"power off prepare error!\n"); dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
}
} }
static void rk808_syscore_shutdown(void) static void rk805_device_shutdown_prepare(void)
{
int ret;
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
if (!rk808)
return;
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");
}
static void rk808_device_shutdown(void)
{
int ret;
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
if (!rk808)
return;
ret = regmap_update_bits(rk808->regmap,
RK808_DEVCTRL_REG,
DEV_OFF_RST, DEV_OFF_RST);
if (ret)
dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
}
static void rk816_device_shutdown(void)
{
int ret;
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
if (!rk808)
return;
ret = regmap_update_bits(rk808->regmap,
RK816_DEV_CTRL_REG,
DEV_OFF, DEV_OFF);
if (ret)
dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
}
static void rk818_device_shutdown(void)
{
int ret;
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
if (!rk808)
return;
ret = regmap_update_bits(rk808->regmap,
RK818_DEVCTRL_REG,
DEV_OFF, DEV_OFF);
if (ret)
dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
}
static void rk8xx_syscore_shutdown(void)
{ {
int ret; int ret;
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
@@ -813,25 +818,21 @@ static void rk808_syscore_shutdown(void)
* been stopped or PMIC may not be able to get i2c transfer while * been stopped or PMIC may not be able to get i2c transfer while
* there are too many devices are competiting. * there are too many devices are competiting.
*/ */
if (system_state == SYSTEM_POWER_OFF) { if (system_state == SYSTEM_POWER_OFF &&
/* power off supplies ! */ (rk808->variant == RK809_ID || rk808->variant == RK817_ID)) {
if (pm_shutdown) { ret = regmap_update_bits(rk808->regmap,
dev_info(&rk808_i2c_client->dev, "System power off\n"); RK817_SYS_CFG(3),
ret = pm_shutdown(rk808->regmap); RK817_SLPPIN_FUNC_MSK,
if (ret) SLPPIN_DN_FUN);
dev_err(&rk808_i2c_client->dev, if (ret) {
"System power off error!\n"); dev_warn(&rk808_i2c_client->dev,
mdelay(10); "Cannot switch to power down function\n");
dev_info(&rk808_i2c_client->dev,
"Cpu should never reach here, stop!\n");
while (1)
;
} }
} }
} }
static struct syscore_ops rk808_syscore_ops = { static struct syscore_ops rk808_syscore_ops = {
.shutdown = rk808_syscore_shutdown, .shutdown = rk8xx_syscore_shutdown,
}; };
/* /*
@@ -915,10 +916,8 @@ static int rk808_probe(struct i2c_client *client,
struct device_node *np = client->dev.of_node; struct device_node *np = client->dev.of_node;
struct rk808 *rk808; struct rk808 *rk808;
const struct rk808_reg_data *pre_init_reg; const struct rk808_reg_data *pre_init_reg;
const struct regmap_irq_chip *irq_chip, *battery_irq_chip = NULL; const struct regmap_irq_chip *battery_irq_chip = NULL;
const struct mfd_cell *cells; const struct mfd_cell *cells;
int (*pm_shutdown_fn)(struct regmap *regmap) = NULL;
int (*pm_shutdown_prepare_fn)(struct rk808 *rk808) = NULL;
int nr_pre_init_regs; int nr_pre_init_regs;
int nr_cells; int nr_cells;
int pm_off = 0, msb, lsb; int pm_off = 0, msb, lsb;
@@ -960,57 +959,58 @@ static int rk808_probe(struct i2c_client *client,
switch (rk808->variant) { switch (rk808->variant) {
case RK805_ID: case RK805_ID:
rk808->regmap_cfg = &rk805_regmap_config; rk808->regmap_cfg = &rk805_regmap_config;
irq_chip = &rk805_irq_chip; rk808->regmap_irq_chip = &rk805_irq_chip;
pre_init_reg = rk805_pre_init_reg; pre_init_reg = rk805_pre_init_reg;
nr_pre_init_regs = ARRAY_SIZE(rk805_pre_init_reg); nr_pre_init_regs = ARRAY_SIZE(rk805_pre_init_reg);
cells = rk805s; cells = rk805s;
nr_cells = ARRAY_SIZE(rk805s); nr_cells = ARRAY_SIZE(rk805s);
pm_shutdown_prepare_fn = rk805_shutdown_prepare;
suspend_reg = rk805_suspend_reg; suspend_reg = rk805_suspend_reg;
suspend_reg_num = ARRAY_SIZE(rk805_suspend_reg); suspend_reg_num = ARRAY_SIZE(rk805_suspend_reg);
resume_reg = rk805_resume_reg; resume_reg = rk805_resume_reg;
resume_reg_num = ARRAY_SIZE(rk805_resume_reg); resume_reg_num = ARRAY_SIZE(rk805_resume_reg);
rk808->pm_pwroff_fn = rk805_device_shutdown;
rk808->pm_pwroff_prep_fn = rk805_device_shutdown_prepare;
break; break;
case RK808_ID: case RK808_ID:
rk808->regmap_cfg = &rk808_regmap_config; rk808->regmap_cfg = &rk808_regmap_config;
irq_chip = &rk808_irq_chip; rk808->regmap_irq_chip = &rk808_irq_chip;
pre_init_reg = rk808_pre_init_reg; pre_init_reg = rk808_pre_init_reg;
nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg); nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
cells = rk808s; cells = rk808s;
nr_cells = ARRAY_SIZE(rk808s); nr_cells = ARRAY_SIZE(rk808s);
pm_shutdown_fn = rk808_shutdown; rk808->pm_pwroff_fn = rk808_device_shutdown;
break; break;
case RK816_ID: case RK816_ID:
rk808->regmap_cfg = &rk816_regmap_config; rk808->regmap_cfg = &rk816_regmap_config;
irq_chip = &rk816_irq_chip; rk808->regmap_irq_chip = &rk816_irq_chip;
battery_irq_chip = &rk816_battery_irq_chip; battery_irq_chip = &rk816_battery_irq_chip;
pre_init_reg = rk816_pre_init_reg; pre_init_reg = rk816_pre_init_reg;
nr_pre_init_regs = ARRAY_SIZE(rk816_pre_init_reg); nr_pre_init_regs = ARRAY_SIZE(rk816_pre_init_reg);
cells = rk816s; cells = rk816s;
nr_cells = ARRAY_SIZE(rk816s); nr_cells = ARRAY_SIZE(rk816s);
pm_shutdown_fn = rk816_shutdown;
suspend_reg = rk816_suspend_reg; suspend_reg = rk816_suspend_reg;
suspend_reg_num = ARRAY_SIZE(rk816_suspend_reg); suspend_reg_num = ARRAY_SIZE(rk816_suspend_reg);
resume_reg = rk816_resume_reg; resume_reg = rk816_resume_reg;
resume_reg_num = ARRAY_SIZE(rk816_resume_reg); resume_reg_num = ARRAY_SIZE(rk816_resume_reg);
rk808->pm_pwroff_fn = rk816_device_shutdown;
break; break;
case RK818_ID: case RK818_ID:
rk808->regmap_cfg = &rk818_regmap_config; rk808->regmap_cfg = &rk818_regmap_config;
irq_chip = &rk818_irq_chip; rk808->regmap_irq_chip = &rk818_irq_chip;
pre_init_reg = rk818_pre_init_reg; pre_init_reg = rk818_pre_init_reg;
nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg); nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
cells = rk818s; cells = rk818s;
nr_cells = ARRAY_SIZE(rk818s); nr_cells = ARRAY_SIZE(rk818s);
pm_shutdown_fn = rk818_shutdown;
suspend_reg = rk818_suspend_reg; suspend_reg = rk818_suspend_reg;
suspend_reg_num = ARRAY_SIZE(rk818_suspend_reg); suspend_reg_num = ARRAY_SIZE(rk818_suspend_reg);
resume_reg = rk818_resume_reg; resume_reg = rk818_resume_reg;
resume_reg_num = ARRAY_SIZE(rk818_resume_reg); resume_reg_num = ARRAY_SIZE(rk818_resume_reg);
rk808->pm_pwroff_fn = rk818_device_shutdown;
break; break;
case RK809_ID: case RK809_ID:
case RK817_ID: case RK817_ID:
rk808->regmap_cfg = &rk817_regmap_config; rk808->regmap_cfg = &rk817_regmap_config;
irq_chip = &rk817_irq_chip; rk808->regmap_irq_chip = &rk817_irq_chip;
pre_init_reg = rk817_pre_init_reg; pre_init_reg = rk817_pre_init_reg;
nr_pre_init_regs = ARRAY_SIZE(rk817_pre_init_reg); nr_pre_init_regs = ARRAY_SIZE(rk817_pre_init_reg);
cells = rk817s; cells = rk817s;
@@ -1044,7 +1044,7 @@ static int rk808_probe(struct i2c_client *client,
ret = regmap_add_irq_chip(rk808->regmap, client->irq, ret = regmap_add_irq_chip(rk808->regmap, client->irq,
IRQF_ONESHOT | IRQF_SHARED, -1, IRQF_ONESHOT | IRQF_SHARED, -1,
irq_chip, &rk808->irq_data); rk808->regmap_irq_chip, &rk808->irq_data);
if (ret) { if (ret) {
dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); dev_err(&client->dev, "Failed to add irq_chip %d\n", ret);
return ret; return ret;
@@ -1086,23 +1086,6 @@ static int rk808_probe(struct i2c_client *client,
pm_off = of_property_read_bool(np, pm_off = of_property_read_bool(np,
"rockchip,system-power-controller"); "rockchip,system-power-controller");
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); rk8xx_kobj = kobject_create_and_add("rk8xx", NULL);
if (rk8xx_kobj) { if (rk8xx_kobj) {
@@ -1111,6 +1094,20 @@ static int rk808_probe(struct i2c_client *client,
dev_err(&client->dev, "create rk8xx sysfs error\n"); dev_err(&client->dev, "create rk8xx sysfs error\n");
} }
if (pm_off && !pm_power_off) {
rk808_i2c_client = client;
pm_power_off = rk808->pm_pwroff_fn;
}
if (pm_off && !pm_power_off_prepare) {
if (!rk808_i2c_client)
rk808_i2c_client = client;
pm_power_off_prepare = rk808->pm_pwroff_prep_fn;
}
if (!pm_power_off)
pm_power_off = rk808_pm_power_off_dummy;
return 0; return 0;
err_irq: err_irq:
@@ -1127,12 +1124,19 @@ static int rk808_remove(struct i2c_client *client)
regmap_del_irq_chip(client->irq, rk808->irq_data); regmap_del_irq_chip(client->irq, rk808->irq_data);
mfd_remove_devices(&client->dev); mfd_remove_devices(&client->dev);
if (pm_power_off == rk808_pm_power_off_dummy) /**
* 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 (rk808->pm_pwroff_fn && pm_power_off == rk808->pm_pwroff_fn)
pm_power_off = NULL; pm_power_off = NULL;
if (pm_power_off_prepare == rk808_device_shutdown_prepare)
/**
* 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; pm_power_off_prepare = NULL;
if (pm_shutdown)
unregister_syscore_ops(&rk808_syscore_ops);
return 0; return 0;
} }

View File

@@ -718,5 +718,8 @@ struct rk808 {
struct regmap *regmap; struct regmap *regmap;
long variant; long variant;
const struct regmap_config *regmap_cfg; const struct regmap_config *regmap_cfg;
const struct regmap_irq_chip *regmap_irq_chip;
void (*pm_pwroff_fn)(void);
void (*pm_pwroff_prep_fn)(void);
}; };
#endif /* __LINUX_REGULATOR_RK808_H */ #endif /* __LINUX_REGULATOR_RK808_H */