mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
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:
committed by
Elaine Zhang
parent
af2cb4b882
commit
62e9bedcf2
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user