From ef4c4aba2ad20d1380d25c33265e4baa31f83ad0 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Wed, 16 Oct 2024 17:42:29 +0800 Subject: [PATCH] regulator: rk801: Add driver and fix voltage scale issue Signed-off-by: Joseph Chen Change-Id: I0fc66ed8e4279a3fbe1ed9d791de745ffaa30891 --- drivers/mfd/rk808.c | 2 +- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/rk801-regulator.c | 669 ++++++++++++++++++++++++++++ include/linux/mfd/rk808.h | 8 +- 5 files changed, 687 insertions(+), 2 deletions(-) create mode 100644 drivers/regulator/rk801-regulator.c diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 9fb002b130ea..4eda6213174c 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -217,7 +217,7 @@ static const struct resource rk817_pwrkey_resources[] = { }; static const struct mfd_cell rk801s[] = { - { .name = "rk808-regulator", }, + { .name = "rk801-regulator", }, { .name = "rk805-pwrkey", .num_resources = ARRAY_SIZE(rk801_key_resources), diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index b85484008c40..ab93accc655c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1053,6 +1053,15 @@ config REGULATOR_RC5T583 through regulator interface. The device supports multiple DCDC/LDO outputs which can be controlled by i2c communication. +config REGULATOR_RK801 + tristate "Rockchip RK801 Power regulators" + depends on MFD_RK808 + help + Select this option to enable the power regulator of ROCKCHIP + PMIC RK801. This driver supports the control of different power rails + of device through regulator interface. The device supports multiple DCDC/LDO + outputs which can be controlled by i2c communication. + config REGULATOR_RK806 tristate "Rockchip RK806 Power regulator" depends on MFD_RK806 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 121fb8cf00f0..8493614fb47f 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) += rpi-panel-attiny-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o +obj-$(CONFIG_REGULATOR_RK801) += rk801-regulator.o obj-$(CONFIG_REGULATOR_RK806) += rk806-regulator.o obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o obj-$(CONFIG_REGULATOR_RK860X) += rk860x-regulator.o diff --git a/drivers/regulator/rk801-regulator.c b/drivers/regulator/rk801-regulator.c new file mode 100644 index 000000000000..ca8cd1300da4 --- /dev/null +++ b/drivers/regulator/rk801-regulator.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Regulator driver for Rockchip RK801 + * + * Copyright (c) 2024, Rockchip Electronics Co., Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Field Definitions */ +#define RK801_BUCK_VSEL_MASK 0x7f +#define RK801_LDO_VSEL_MASK 0x3f +#define ENABLE_MASK(id) (BIT(4 + (id)) | BIT(id)) +#define ENABLE_VAL(id) (BIT(4 + (id)) | BIT(id)) +#define DISABLE_VAL(id) (BIT(4 + (id)) | 0) + +#define RK801_DESC(_id, _match, _supply, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask, _enval, _disval, _etime) \ + { \ + .name = (_match), \ + .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = (_id), \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .owner = THIS_MODULE, \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .enable_val = (_enval), \ + .disable_val = (_disval), \ + .enable_time = (_etime), \ + .ops = &rk801_reg_ops, \ + } + +#define RK801_DESC_SWITCH(_id, _match, _supply, _ereg, _emask, _enval, \ + _disval, _etime) \ + { \ + .name = (_match), \ + .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = (_id), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .enable_val = (_enval), \ + .disable_val = (_disval), \ + .enable_time = (_etime), \ + .owner = THIS_MODULE, \ + .ops = &rk801_switch_ops \ + } + +struct suspend_device { + struct regulator_dev *rdev; + int enable; + int uv; +}; + +struct runtime_device { + int reg_src; + int reg_dst; +}; + +struct rk801_regulator_data { + struct gpio_desc *pwrctrl_gpio; + struct suspend_device sdev[RK801_ID_MAX]; + bool pwrctrl_dvs; +}; + +static const struct linear_range rk801_buck1_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(500000, 0, 80, 12500), /* 0.5v - 1.5v */ + REGULATOR_LINEAR_RANGE(1800000, 81, 82, 400000),/* 1.8v - 2.2v */ + REGULATOR_LINEAR_RANGE(3300000, 83, 83, 0), /* 3.3v */ + REGULATOR_LINEAR_RANGE(5000000, 84, 84, 0), /* 5.0v */ + REGULATOR_LINEAR_RANGE(5250000, 85, 85, 0), /* 5.25v */ +}; + +static const struct linear_range rk801_buck2_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(800000, 0, 2, 50000), /* 0.8v - 0.9v */ + REGULATOR_LINEAR_RANGE(1800000, 3, 4, 400000), /* 1.8v - 2.2v */ + REGULATOR_LINEAR_RANGE(3300000, 5, 5, 0), /* 3.3v */ + REGULATOR_LINEAR_RANGE(5000000, 6, 6, 0), /* 5.0v */ + REGULATOR_LINEAR_RANGE(5250000, 7, 7, 0), /* 5.25v */ +}; + +static const struct linear_range rk801_buck4_voltage_ranges[] = { + REGULATOR_LINEAR_RANGE(500000, 0, 80, 12500), /* 0.5v - 1.5v */ + REGULATOR_LINEAR_RANGE(1800000, 81, 82, 400000),/* 1.8v - 2.2v */ + REGULATOR_LINEAR_RANGE(2500000, 83, 83, 0), /* 2.5v */ + REGULATOR_LINEAR_RANGE(2800000, 84, 84, 0), /* 2.8v */ + REGULATOR_LINEAR_RANGE(3000000, 85, 85, 0), /* 3.0v */ + REGULATOR_LINEAR_RANGE(3300000, 86, 86, 0), /* 3.3v */ +}; + +static unsigned int rk801_regulator_of_map_mode(unsigned int mode) +{ + switch (mode) { + case 1: + return REGULATOR_MODE_FAST; + case 2: + return REGULATOR_MODE_NORMAL; + default: + return REGULATOR_MODE_INVALID; + } +} + +static unsigned int rk801_get_mode(struct regulator_dev *rdev) +{ + unsigned int pmw_mode_msk; + unsigned int val; + int err; + + pmw_mode_msk = BIT(rdev->desc->id); + err = regmap_read(rdev->regmap, RK801_POWER_FPWM_EN_REG, &val); + if (err) + return err; + + if ((val & pmw_mode_msk) == RK801_FPWM_MODE) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static int rk801_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + unsigned int offset = rdev->desc->id; + + switch (mode) { + case REGULATOR_MODE_FAST: + return regmap_update_bits(rdev->regmap, RK801_POWER_FPWM_EN_REG, + BIT(offset), RK801_FPWM_MODE << offset); + case REGULATOR_MODE_NORMAL: + return regmap_update_bits(rdev->regmap, RK801_POWER_FPWM_EN_REG, + BIT(offset), RK801_AUTO_PWM_MODE << offset); + default: + dev_err(&rdev->dev, "do not support this mode\n"); + return -EINVAL; + } + + return 0; +} + +static int rk801_set_suspend_voltage(struct regulator_dev *rdev, int uv) +{ + struct rk801_regulator_data *pdata = rdev_get_drvdata(rdev); + unsigned int reg; + int sel; + + if (pdata->pwrctrl_dvs) { + pdata->sdev[rdev->desc->id].rdev = rdev; + pdata->sdev[rdev->desc->id].uv = uv; + } else { + if (rdev->desc->id < RK801_ID_LDO1) + sel = regulator_map_voltage_linear_range(rdev, uv, uv); + else + sel = regulator_map_voltage_linear(rdev, uv, uv); + if (sel < 0) + return -EINVAL; + + reg = rdev->desc->vsel_reg + RK801_SLP_REG_OFFSET; + + return regmap_update_bits(rdev->regmap, reg, + rdev->desc->vsel_mask, sel); + } + + return 0; +} + +static int rk801_set_suspend_enable(struct regulator_dev *rdev) +{ + struct rk801_regulator_data *pdata = rdev_get_drvdata(rdev); + + if (pdata->pwrctrl_dvs) { + pdata->sdev[rdev->desc->id].rdev = rdev; + pdata->sdev[rdev->desc->id].enable = 1; + } else { + return regmap_update_bits(rdev->regmap, RK801_POWER_SLP_EN_REG, + BIT(rdev->desc->id), BIT(rdev->desc->id)); + } + + return 0; +} + +static int rk801_set_suspend_disable(struct regulator_dev *rdev) +{ + struct rk801_regulator_data *pdata = rdev_get_drvdata(rdev); + + if (pdata->pwrctrl_dvs) { + pdata->sdev[rdev->desc->id].rdev = rdev; + pdata->sdev[rdev->desc->id].enable = 0; + } else { + return regmap_update_bits(rdev->regmap, RK801_POWER_SLP_EN_REG, + BIT(rdev->desc->id), 0); + } + + return 0; +} + +static int rk801_set_enable(struct regulator_dev *rdev) +{ + struct rk801_regulator_data *pdata = rdev_get_drvdata(rdev); + int ret; + + ret = regulator_enable_regmap(rdev); + if (ret) + return ret; + + if (pdata->pwrctrl_dvs) + return regmap_update_bits(rdev->regmap, RK801_POWER_SLP_EN_REG, + BIT(rdev->desc->id), BIT(rdev->desc->id)); + + return 0; +} + +static int rk801_set_disable(struct regulator_dev *rdev) +{ + struct rk801_regulator_data *pdata = rdev_get_drvdata(rdev); + int ret; + + ret = regulator_disable_regmap(rdev); + if (ret) + return ret; + + if (pdata->pwrctrl_dvs) + return regmap_update_bits(rdev->regmap, RK801_POWER_SLP_EN_REG, + BIT(rdev->desc->id), 0); + + return 0; +} + +static int rk801_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + return regulator_set_voltage_time_sel(rdev, old_selector, new_selector) + 32; +} + +static int rk801_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) +{ + struct rk801_regulator_data *pdata = rdev_get_drvdata(rdev); + struct gpio_desc *gpio = pdata->pwrctrl_gpio; + unsigned int reg0 = rdev->desc->vsel_reg; + unsigned int reg1 = rdev->desc->vsel_reg + RK801_SLP_REG_OFFSET; + unsigned int reg; + int ret, gpio_level; + + if (pdata->pwrctrl_dvs) { + gpio_level = gpiod_get_value(gpio); + reg = (gpio_level == 1) ? reg0 : reg1; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + ret = regmap_update_bits(rdev->regmap, reg, rdev->desc->vsel_mask, sel); + if (ret) + return ret; + + udelay(40); /* hw sync */ + + gpiod_set_value(gpio, !gpio_level); + + if (reg == reg0) + ret = regmap_update_bits(rdev->regmap, reg1, + rdev->desc->vsel_mask, sel); + else + ret = regmap_update_bits(rdev->regmap, reg0, + rdev->desc->vsel_mask, sel); + } else { + ret = regulator_set_voltage_sel_regmap(rdev, sel); + } + + return ret; +} + +static const struct regulator_ops rk801_buck_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = rk801_set_voltage_sel, + .set_voltage_time_sel = rk801_set_voltage_time_sel, + .enable = rk801_set_enable, + .disable = rk801_set_disable, + .is_enabled = regulator_is_enabled_regmap, + .set_mode = rk801_set_mode, + .get_mode = rk801_get_mode, + .set_suspend_voltage = rk801_set_suspend_voltage, + .set_suspend_enable = rk801_set_suspend_enable, + .set_suspend_disable = rk801_set_suspend_disable, +}; + +static const struct regulator_ops rk801_reg_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = rk801_set_voltage_sel, + .enable = rk801_set_enable, + .disable = rk801_set_disable, + .is_enabled = regulator_is_enabled_regmap, + .set_suspend_voltage = rk801_set_suspend_voltage, + .set_suspend_enable = rk801_set_suspend_enable, + .set_suspend_disable = rk801_set_suspend_disable, +}; + +static const struct regulator_ops rk801_switch_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = rk801_set_suspend_enable, + .set_suspend_disable = rk801_set_suspend_disable, +}; + +static const struct regulator_desc rk801_reg[] = { + { + .name = "DCDC_REG1", + .supply_name = "vcc1", + .of_match = of_match_ptr("DCDC_REG1"), + .regulators_node = of_match_ptr("regulators"), + .id = RK801_ID_DCDC1, + .ops = &rk801_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 86, + .linear_ranges = rk801_buck1_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk801_buck1_voltage_ranges), + .vsel_reg = RK801_BUCK1_ON_VSEL_REG, + .vsel_mask = RK801_BUCK_VSEL_MASK, + .enable_reg = RK801_POWER_EN0_REG, + .enable_mask = ENABLE_MASK(RK801_ID_DCDC1), + .enable_val = ENABLE_VAL(RK801_ID_DCDC1), + .disable_val = DISABLE_VAL(RK801_ID_DCDC1), + .ramp_delay = 1, + .of_map_mode = rk801_regulator_of_map_mode, + .enable_time = 400, + .owner = THIS_MODULE, + }, { + .name = "DCDC_REG2", + .supply_name = "vcc2", + .of_match = of_match_ptr("DCDC_REG2"), + .regulators_node = of_match_ptr("regulators"), + .id = RK801_ID_DCDC2, + .ops = &rk801_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 8, + .linear_ranges = rk801_buck2_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk801_buck2_voltage_ranges), + .vsel_reg = RK801_BUCK2_ON_VSEL_REG, + .vsel_mask = RK801_BUCK_VSEL_MASK, + .enable_reg = RK801_POWER_EN0_REG, + .enable_mask = ENABLE_MASK(RK801_ID_DCDC2), + .enable_val = ENABLE_VAL(RK801_ID_DCDC2), + .disable_val = DISABLE_VAL(RK801_ID_DCDC2), + .ramp_delay = 1, + .of_map_mode = rk801_regulator_of_map_mode, + .enable_time = 400, + .owner = THIS_MODULE, + }, { + .name = "DCDC_REG3", + .supply_name = "vcc3", + .of_match = of_match_ptr("DCDC_REG3"), + .regulators_node = of_match_ptr("regulators"), + .id = RK801_ID_DCDC3, + .ops = &rk801_switch_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 1, + .enable_reg = RK801_POWER_EN0_REG, + .enable_mask = ENABLE_MASK(RK801_ID_DCDC3), + .enable_val = ENABLE_VAL(RK801_ID_DCDC3), + .disable_val = DISABLE_VAL(RK801_ID_DCDC3), + .of_map_mode = rk801_regulator_of_map_mode, + .enable_time = 400, + .owner = THIS_MODULE, + }, { + .name = "DCDC_REG4", + .supply_name = "vcc4", + .of_match = of_match_ptr("DCDC_REG4"), + .regulators_node = of_match_ptr("regulators"), + .id = RK801_ID_DCDC4, + .ops = &rk801_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 87, + .linear_ranges = rk801_buck4_voltage_ranges, + .n_linear_ranges = ARRAY_SIZE(rk801_buck4_voltage_ranges), + .vsel_reg = RK801_BUCK4_ON_VSEL_REG, + .vsel_mask = RK801_BUCK_VSEL_MASK, + .enable_reg = RK801_POWER_EN0_REG, + .enable_mask = ENABLE_MASK(RK801_ID_DCDC4), + .enable_val = ENABLE_VAL(RK801_ID_DCDC4), + .disable_val = DISABLE_VAL(RK801_ID_DCDC4), + .ramp_delay = 1, + .of_map_mode = rk801_regulator_of_map_mode, + .enable_time = 400, + .owner = THIS_MODULE, + }, + + RK801_DESC(RK801_ID_LDO1, "LDO_REG1", "vcc5", 500, 3400, 50, + RK801_LDO1_ON_VSEL_REG, RK801_LDO_VSEL_MASK, RK801_POWER_EN1_REG, + ENABLE_MASK(0), ENABLE_VAL(0), DISABLE_VAL(0), 400), + RK801_DESC(RK801_ID_LDO2, "LDO_REG2", "vcc6", 500, 3400, 50, + RK801_LDO2_ON_VSEL_REG, RK801_LDO_VSEL_MASK, RK801_POWER_EN1_REG, + ENABLE_MASK(1), ENABLE_VAL(1), DISABLE_VAL(1), 400), + RK801_DESC_SWITCH(RK801_ID_SWITCH, "SWITCH_REG", "vcc7", RK801_POWER_EN1_REG, + ENABLE_MASK(2), ENABLE_VAL(2), DISABLE_VAL(2), 400), +}; + +static int rk801_regulator_dt_parse_pdata(struct device *dev, + struct device *client_dev, + struct regmap *map, + struct rk801_regulator_data *pdata) +{ + struct device_node *np; + int ret = 0; + + np = of_get_child_by_name(client_dev->of_node, "regulators"); + if (!np) + return -ENXIO; + + if (pdata->pwrctrl_dvs) { + pdata->pwrctrl_gpio = devm_gpiod_get_optional(client_dev, + "pwrctrl", GPIOD_OUT_LOW); + if (IS_ERR(pdata->pwrctrl_gpio)) { + ret = PTR_ERR(pdata->pwrctrl_gpio); + dev_err(dev, "failed to get pwrctrl gpio! ret=%d\n", ret); + goto dt_parse_end; + } + + if (!pdata->pwrctrl_gpio) { + dev_err(dev, "there is no pwrctrl gpio!\n"); + ret = -EINVAL; + } + } +dt_parse_end: + of_node_put(np); + + return ret; +} + +static int rk801_regulator_init(struct device *dev) +{ + struct rk808 *rk808 = dev_get_drvdata(dev->parent); + struct rk801_regulator_data *pdata = dev_get_drvdata(dev); + struct runtime_device rdev[] = { + { RK801_BUCK1_ON_VSEL_REG, RK801_BUCK1_SLP_VSEL_REG }, + { RK801_BUCK2_ON_VSEL_REG, RK801_BUCK2_SLP_VSEL_REG }, + { RK801_BUCK4_ON_VSEL_REG, RK801_BUCK4_SLP_VSEL_REG }, + { RK801_LDO1_ON_VSEL_REG, RK801_LDO1_SLP_VSEL_REG }, + { RK801_LDO2_ON_VSEL_REG, RK801_LDO2_SLP_VSEL_REG }, + }; + uint val, en0, en1; + int i, ret; + + /* pwrctrl gpio active high and use sleep function */ + ret = regmap_update_bits(rk808->regmap, RK801_SYS_CFG2_REG, + RK801_SLEEP_POL_MSK, RK801_SLEEP_ACT_H); + if (ret) + return ret; + + ret = regmap_update_bits(rk808->regmap, RK801_SLEEP_CFG_REG, + RK801_SLEEP_FUN_MSK, RK801_SLEEP_FUN); + if (ret) + return ret; + + /* disable buck/pldo slp lp */ + ret = regmap_update_bits(rk808->regmap, RK801_SLP_LP_CONFIG_REG, + RK801_SLP_LP_MASK, 0); + if (ret) + return ret; + + /* copy en/slp_en */ + ret = regmap_read(rk808->regmap, RK801_POWER_EN0_REG, &en0); + if (ret) + return ret; + + ret = regmap_read(rk808->regmap, RK801_POWER_EN1_REG, &en1); + if (ret) + return ret; + + val = (en0 & 0x0f) | ((en1 & 0x0f) << 4); + ret = regmap_write(rk808->regmap, RK801_POWER_SLP_EN_REG, val); + if (ret) + return ret; + + /* copy on_vsel/slp_vsel */ + for (i = 0; i < ARRAY_SIZE(rdev); i++) { + ret = regmap_read(rk808->regmap, rdev[i].reg_src, &val); + if (ret) + return ret; + + ret = regmap_write(rk808->regmap, rdev[i].reg_dst, val); + if (ret) + return ret; + } + + /* init */ + for (i = 0; i < ARRAY_SIZE(pdata->sdev); i++) { + pdata->sdev[i].rdev = NULL; + pdata->sdev[i].uv = -EINVAL; + pdata->sdev[i].enable = -EINVAL; + } + + return 0; +} + +static int rk801_set_suspend_dev(struct suspend_device sdev) +{ + struct regulator_dev *rdev = sdev.rdev; + int enable = sdev.enable; + int uv = sdev.uv; + int val, sel, reg; + int ret, mask; + + if (!rdev) + return 0; + + if (uv != -EINVAL) { + if (rdev->desc->id < RK801_ID_LDO1) + sel = regulator_map_voltage_linear_range(rdev, uv, uv); + else + sel = regulator_map_voltage_linear(rdev, uv, uv); + if (sel < 0) + return -EINVAL; + + reg = rdev->desc->vsel_reg + RK801_SLP_REG_OFFSET; + ret = regmap_update_bits(rdev->regmap, reg, + rdev->desc->vsel_mask, sel); + if (ret) + return ret; + } + + if (enable != -EINVAL) { + mask = BIT(rdev->desc->id); + val = enable ? mask : 0; + ret = regmap_update_bits(rdev->regmap, + RK801_POWER_SLP_EN_REG, mask, val); + if (ret) + return ret; + } + + return 0; +} + +static int rk801_regulator_suspend_late(struct device *dev) +{ + struct rk808 *rk808 = dev_get_drvdata(dev->parent); + struct rk801_regulator_data *pdata = dev_get_drvdata(dev); + struct gpio_desc *gpio = pdata->pwrctrl_gpio; + int i, ret; + + if (!pdata->pwrctrl_dvs) + return 0; + + /* set pwrctrl pin low */ + gpiod_set_value(gpio, 0); + udelay(40); + + /* set buck/ldo/switch suspend */ + for (i = 0; i < ARRAY_SIZE(pdata->sdev); i++) { + ret = rk801_set_suspend_dev(pdata->sdev[i]); + if (ret) + return ret; + } + + /* enable buck/pldo lp enable */ + return regmap_update_bits(rk808->regmap, RK801_SLP_LP_CONFIG_REG, + RK801_SLP_LP_MASK, RK801_SLP_LP_MASK); +} + +static int rk801_regulator_resume_early(struct device *dev) +{ + struct rk801_regulator_data *pdata = dev_get_drvdata(dev); + + if (!pdata->pwrctrl_dvs) + return 0; + + return rk801_regulator_init(dev); +} + +static int rk801_regulator_probe(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct i2c_client *client = rk808->i2c; + struct regulator_config config = {}; + struct regulator_dev *rk801_rdev; + struct rk801_regulator_data *pdata; + int id_lsb, i, ret; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = regmap_read(rk808->regmap, RK801_ID_LSB, &id_lsb); + if (ret) + return ret; + + pdata->pwrctrl_dvs = (id_lsb & 0x0f) < 4; + dev_info(&pdev->dev, "pwrctrl dvs: %d\n", pdata->pwrctrl_dvs); + + ret = rk801_regulator_dt_parse_pdata(&pdev->dev, &client->dev, + rk808->regmap, pdata); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, pdata); + + config.dev = &client->dev; + config.driver_data = pdata; + config.regmap = rk808->regmap; + + ret = rk801_regulator_init(&pdev->dev); + if (ret) + return ret; + + for (i = 0; i < RK801_NUM_REGULATORS; i++) { + rk801_rdev = devm_regulator_register(&pdev->dev, + &rk801_reg[i], &config); + if (IS_ERR(rk801_rdev)) { + dev_err(&client->dev, + "failed to register %d regulator\n", i); + return PTR_ERR(rk801_rdev); + } + } + + return 0; +} + +static const struct dev_pm_ops rk801_pm_ops = { + .suspend_late = pm_sleep_ptr(rk801_regulator_suspend_late), + .resume_early = pm_sleep_ptr(rk801_regulator_resume_early), +}; + +static struct platform_driver rk801_regulator_driver = { + .probe = rk801_regulator_probe, + .driver = { + .name = "rk801-regulator", + .pm = pm_sleep_ptr(&rk801_pm_ops), + }, +}; + +#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT +static int __init rk801_regulator_driver_init(void) +{ + return platform_driver_register(&rk801_regulator_driver); +} +subsys_initcall(rk801_regulator_driver_init); + +static void __exit rk801_regulator_driver_exit(void) +{ + platform_driver_unregister(&rk801_regulator_driver); +} +module_exit(rk801_regulator_driver_exit); +#else +module_platform_driver(rk801_regulator_driver); +#endif + +MODULE_DESCRIPTION("regulator driver for the RK801 PMIC"); +MODULE_AUTHOR("Joseph Chen "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index ad2287268bc7..fbffa38b061c 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -22,11 +22,12 @@ enum rk801_reg { RK801_ID_DCDC1, RK801_ID_DCDC2, - RK801_ID_DCDC3, RK801_ID_DCDC4, + RK801_ID_DCDC3, RK801_ID_LDO1, RK801_ID_LDO2, RK801_ID_SWITCH, + RK801_ID_MAX, }; #define RK801_SLP_REG_OFFSET 5 @@ -107,6 +108,11 @@ enum rk801_reg { #define RK801_IRQ_VDC_RISE_MSK BIT(5) #define RK801_IRQ_VDC_FALL_MSK BIT(6) +/* RK801_SLP_LP_CONFIG_REG */ +#define RK801_BUCK_SLP_LP_EN BIT(3) +#define RK801_PLDO_SLP_LP_EN BIT(1) +#define RK801_SLP_LP_MASK (RK801_PLDO_SLP_LP_EN | RK801_BUCK_SLP_LP_EN) + /* RK801_SLEEP_CFG_REG */ #define RK801_SLEEP_FUN_MSK 0x3 #define RK801_NONE_FUN 0x0