regulator: act8865: operating mode and suspend state support

Implement ->set_mode(), ->get_mode() and ->set_suspend_xx() hooks
for act8865 with unlocked expert registers.

Based on work done by Borris Brezillon on [1].
[1] https://www.spinics.net/lists/kernel/msg2942960.html

Signed-off-by: Raag Jadav <raagjadav@gmail.com>
Link: https://lore.kernel.org/r/1565721176-8955-2-git-send-email-raagjadav@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Raag Jadav 2019-08-14 00:02:55 +05:30 committed by Mark Brown
parent 81eeb0a35c
commit 955741a512
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 186 additions and 1 deletions

View File

@ -19,6 +19,7 @@
#include <linux/power_supply.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regmap.h>
#include <dt-bindings/regulator/active-semi,8865-regulator.h>
/*
* ACT8600 Global Register Map.
@ -90,23 +91,31 @@
*/
#define ACT8865_SYS_MODE 0x00
#define ACT8865_SYS_CTRL 0x01
#define ACT8865_SYS_UNLK_REGS 0x0b
#define ACT8865_DCDC1_VSET1 0x20
#define ACT8865_DCDC1_VSET2 0x21
#define ACT8865_DCDC1_CTRL 0x22
#define ACT8865_DCDC1_SUS 0x24
#define ACT8865_DCDC2_VSET1 0x30
#define ACT8865_DCDC2_VSET2 0x31
#define ACT8865_DCDC2_CTRL 0x32
#define ACT8865_DCDC2_SUS 0x34
#define ACT8865_DCDC3_VSET1 0x40
#define ACT8865_DCDC3_VSET2 0x41
#define ACT8865_DCDC3_CTRL 0x42
#define ACT8865_DCDC3_SUS 0x44
#define ACT8865_LDO1_VSET 0x50
#define ACT8865_LDO1_CTRL 0x51
#define ACT8865_LDO1_SUS 0x52
#define ACT8865_LDO2_VSET 0x54
#define ACT8865_LDO2_CTRL 0x55
#define ACT8865_LDO2_SUS 0x56
#define ACT8865_LDO3_VSET 0x60
#define ACT8865_LDO3_CTRL 0x61
#define ACT8865_LDO3_SUS 0x62
#define ACT8865_LDO4_VSET 0x64
#define ACT8865_LDO4_CTRL 0x65
#define ACT8865_LDO4_SUS 0x66
#define ACT8865_MSTROFF 0x20
/*
@ -225,6 +234,171 @@ static const struct regulator_linear_range act8600_sudcdc_voltage_ranges[] = {
REGULATOR_LINEAR_RANGE(41400000, 248, 255, 0),
};
static int act8865_set_suspend_state(struct regulator_dev *rdev, bool enable)
{
struct regmap *regmap = rdev->regmap;
int id = rdev->desc->id, reg, val;
switch (id) {
case ACT8865_ID_DCDC1:
reg = ACT8865_DCDC1_SUS;
val = 0xa8;
break;
case ACT8865_ID_DCDC2:
reg = ACT8865_DCDC2_SUS;
val = 0xa8;
break;
case ACT8865_ID_DCDC3:
reg = ACT8865_DCDC3_SUS;
val = 0xa8;
break;
case ACT8865_ID_LDO1:
reg = ACT8865_LDO1_SUS;
val = 0xe8;
break;
case ACT8865_ID_LDO2:
reg = ACT8865_LDO2_SUS;
val = 0xe8;
break;
case ACT8865_ID_LDO3:
reg = ACT8865_LDO3_SUS;
val = 0xe8;
break;
case ACT8865_ID_LDO4:
reg = ACT8865_LDO4_SUS;
val = 0xe8;
break;
default:
return -EINVAL;
}
if (enable)
val |= BIT(4);
/*
* Ask the PMIC to enable/disable this output when entering hibernate
* mode.
*/
return regmap_write(regmap, reg, val);
}
static int act8865_set_suspend_enable(struct regulator_dev *rdev)
{
return act8865_set_suspend_state(rdev, true);
}
static int act8865_set_suspend_disable(struct regulator_dev *rdev)
{
return act8865_set_suspend_state(rdev, false);
}
static unsigned int act8865_of_map_mode(unsigned int mode)
{
switch (mode) {
case ACT8865_REGULATOR_MODE_FIXED:
return REGULATOR_MODE_FAST;
case ACT8865_REGULATOR_MODE_NORMAL:
return REGULATOR_MODE_NORMAL;
case ACT8865_REGULATOR_MODE_LOWPOWER:
return REGULATOR_MODE_STANDBY;
default:
return REGULATOR_MODE_INVALID;
}
}
static int act8865_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct regmap *regmap = rdev->regmap;
int id = rdev_get_id(rdev);
int reg, val = 0;
switch (id) {
case ACT8865_ID_DCDC1:
reg = ACT8865_DCDC1_CTRL;
break;
case ACT8865_ID_DCDC2:
reg = ACT8865_DCDC2_CTRL;
break;
case ACT8865_ID_DCDC3:
reg = ACT8865_DCDC3_CTRL;
break;
case ACT8865_ID_LDO1:
reg = ACT8865_LDO1_CTRL;
break;
case ACT8865_ID_LDO2:
reg = ACT8865_LDO2_CTRL;
break;
case ACT8865_ID_LDO3:
reg = ACT8865_LDO3_CTRL;
break;
case ACT8865_ID_LDO4:
reg = ACT8865_LDO4_CTRL;
break;
default:
return -EINVAL;
}
switch (mode) {
case REGULATOR_MODE_FAST:
case REGULATOR_MODE_NORMAL:
if (id <= ACT8865_ID_DCDC3)
val = BIT(5);
break;
case REGULATOR_MODE_STANDBY:
if (id > ACT8865_ID_DCDC3)
val = BIT(5);
break;
default:
return -EINVAL;
}
return regmap_update_bits(regmap, reg, BIT(5), val);
}
static unsigned int act8865_get_mode(struct regulator_dev *rdev)
{
struct regmap *regmap = rdev->regmap;
int id = rdev_get_id(rdev);
int reg, ret, val = 0;
switch (id) {
case ACT8865_ID_DCDC1:
reg = ACT8865_DCDC1_CTRL;
break;
case ACT8865_ID_DCDC2:
reg = ACT8865_DCDC2_CTRL;
break;
case ACT8865_ID_DCDC3:
reg = ACT8865_DCDC3_CTRL;
break;
case ACT8865_ID_LDO1:
reg = ACT8865_LDO1_CTRL;
break;
case ACT8865_ID_LDO2:
reg = ACT8865_LDO2_CTRL;
break;
case ACT8865_ID_LDO3:
reg = ACT8865_LDO3_CTRL;
break;
case ACT8865_ID_LDO4:
reg = ACT8865_LDO4_CTRL;
break;
default:
return -EINVAL;
}
ret = regmap_read(regmap, reg, &val);
if (ret)
return ret;
if (id <= ACT8865_ID_DCDC3 && (val & BIT(5)))
return REGULATOR_MODE_FAST;
else if (id > ACT8865_ID_DCDC3 && !(val & BIT(5)))
return REGULATOR_MODE_NORMAL;
else
return REGULATOR_MODE_STANDBY;
}
static const struct regulator_ops act8865_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
@ -232,7 +406,11 @@ static const struct regulator_ops act8865_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_mode = act8865_set_mode,
.get_mode = act8865_get_mode,
.is_enabled = regulator_is_enabled_regmap,
.set_suspend_enable = act8865_set_suspend_enable,
.set_suspend_disable = act8865_set_suspend_disable,
};
static const struct regulator_ops act8865_ldo_ops = {
@ -242,7 +420,11 @@ static const struct regulator_ops act8865_ldo_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.set_mode = act8865_set_mode,
.get_mode = act8865_get_mode,
.is_enabled = regulator_is_enabled_regmap,
.set_suspend_enable = act8865_set_suspend_enable,
.set_suspend_disable = act8865_set_suspend_disable,
.set_pull_down = regulator_set_pull_down_regmap,
};
@ -256,6 +438,7 @@ static const struct regulator_ops act8865_fixed_ldo_ops = {
[_family##_ID_##_id] = { \
.name = _name, \
.of_match = of_match_ptr(_name), \
.of_map_mode = act8865_of_map_mode, \
.regulators_node = of_match_ptr("regulators"), \
.supply_name = _supply, \
.id = _family##_ID_##_id, \
@ -590,7 +773,9 @@ static int act8865_pmic_probe(struct i2c_client *client,
i2c_set_clientdata(client, act8865);
return 0;
/* Unlock expert registers for ACT8865. */
return type != ACT8865 ? 0 : regmap_write(act8865->regmap,
ACT8865_SYS_UNLK_REGS, 0xef);
}
static const struct i2c_device_id act8865_ids[] = {