diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 12 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/qcom_smd-regulator.c | 350 | ||||
-rw-r--r-- | drivers/regulator/qcom_spmi-regulator.c | 203 | ||||
-rw-r--r-- | drivers/regulator/rk808-regulator.c | 212 |
5 files changed, 759 insertions, 19 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index f3cdf7a4e487..64bccff557be 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -523,6 +523,18 @@ config REGULATOR_QCOM_RPM Qualcomm RPM as a module. The module will be named "qcom_rpm-regulator". +config REGULATOR_QCOM_SMD_RPM + tristate "Qualcomm SMD based RPM regulator driver" + depends on QCOM_SMD_RPM + help + If you say yes to this option, support will be included for the + regulators exposed by the Resource Power Manager found in Qualcomm + 8974 based devices. + + Say M here if you want to include support for the regulators on the + Qualcomm RPM as a module. The module will be named + "qcom_smd-regulator". + config REGULATOR_QCOM_SPMI tristate "Qualcomm SPMI regulator driver" depends on SPMI || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 8bc702910320..0f8174913c17 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o +obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c new file mode 100644 index 000000000000..9c6167dd2c8b --- /dev/null +++ b/drivers/regulator/qcom_smd-regulator.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2015, Sony Mobile Communications AB. + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/soc/qcom/smd-rpm.h> + +struct qcom_rpm_reg { + struct device *dev; + + struct qcom_smd_rpm *rpm; + + u32 type; + u32 id; + + struct regulator_desc desc; + + int is_enabled; + int uV; +}; + +struct rpm_regulator_req { + u32 key; + u32 nbytes; + u32 value; +}; + +#define RPM_KEY_SWEN 0x6e657773 /* "swen" */ +#define RPM_KEY_UV 0x00007675 /* "uv" */ +#define RPM_KEY_MA 0x0000616d /* "ma" */ + +static int rpm_reg_write_active(struct qcom_rpm_reg *vreg, + struct rpm_regulator_req *req, + size_t size) +{ + return qcom_rpm_smd_write(vreg->rpm, + QCOM_SMD_RPM_ACTIVE_STATE, + vreg->type, + vreg->id, + req, size); +} + +static int rpm_reg_enable(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + int ret; + + req.key = RPM_KEY_SWEN; + req.nbytes = sizeof(u32); + req.value = 1; + + ret = rpm_reg_write_active(vreg, &req, sizeof(req)); + if (!ret) + vreg->is_enabled = 1; + + return ret; +} + +static int rpm_reg_is_enabled(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + + return vreg->is_enabled; +} + +static int rpm_reg_disable(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + int ret; + + req.key = RPM_KEY_SWEN; + req.nbytes = sizeof(u32); + req.value = 0; + + ret = rpm_reg_write_active(vreg, &req, sizeof(req)); + if (!ret) + vreg->is_enabled = 0; + + return ret; +} + +static int rpm_reg_get_voltage(struct regulator_dev *rdev) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + + return vreg->uV; +} + +static int rpm_reg_set_voltage(struct regulator_dev *rdev, + int min_uV, + int max_uV, + unsigned *selector) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + int ret = 0; + + req.key = RPM_KEY_UV; + req.nbytes = sizeof(u32); + req.value = min_uV; + + ret = rpm_reg_write_active(vreg, &req, sizeof(req)); + if (!ret) + vreg->uV = min_uV; + + return ret; +} + +static int rpm_reg_set_load(struct regulator_dev *rdev, int load_uA) +{ + struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); + struct rpm_regulator_req req; + + req.key = RPM_KEY_MA; + req.nbytes = sizeof(u32); + req.value = load_uA; + + return rpm_reg_write_active(vreg, &req, sizeof(req)); +} + +static const struct regulator_ops rpm_smps_ldo_ops = { + .enable = rpm_reg_enable, + .disable = rpm_reg_disable, + .is_enabled = rpm_reg_is_enabled, + + .get_voltage = rpm_reg_get_voltage, + .set_voltage = rpm_reg_set_voltage, + + .set_load = rpm_reg_set_load, +}; + +static const struct regulator_ops rpm_switch_ops = { + .enable = rpm_reg_enable, + .disable = rpm_reg_disable, + .is_enabled = rpm_reg_is_enabled, +}; + +static const struct regulator_desc pm8x41_hfsmps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE( 375000, 0, 95, 12500), + REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000), + }, + .n_linear_ranges = 2, + .n_voltages = 159, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8841_ftsmps = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000), + REGULATOR_LINEAR_RANGE(700000, 185, 339, 10000), + }, + .n_linear_ranges = 2, + .n_voltages = 340, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_boost = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(4000000, 0, 15, 100000), + }, + .n_linear_ranges = 1, + .n_voltages = 16, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_pldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE( 750000, 0, 30, 25000), + REGULATOR_LINEAR_RANGE(1500000, 31, 99, 50000), + }, + .n_linear_ranges = 2, + .n_voltages = 100, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_nldo = { + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500), + }, + .n_linear_ranges = 1, + .n_voltages = 64, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_lnldo = { + .fixed_uV = 1740000, + .n_voltages = 1, + .ops = &rpm_smps_ldo_ops, +}; + +static const struct regulator_desc pm8941_switch = { + .ops = &rpm_switch_ops, +}; + +struct rpm_regulator_data { + const char *name; + u32 type; + u32 id; + const struct regulator_desc *desc; + const char *supply; +}; + +static const struct rpm_regulator_data rpm_pm8841_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPB, 1, &pm8x41_hfsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPB, 2, &pm8841_ftsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPB, 3, &pm8x41_hfsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_SMPB, 4, &pm8841_ftsmps, "vdd_s4" }, + { "s5", QCOM_SMD_RPM_SMPB, 5, &pm8841_ftsmps, "vdd_s5" }, + { "s6", QCOM_SMD_RPM_SMPB, 6, &pm8841_ftsmps, "vdd_s6" }, + { "s7", QCOM_SMD_RPM_SMPB, 7, &pm8841_ftsmps, "vdd_s7" }, + { "s8", QCOM_SMD_RPM_SMPB, 8, &pm8841_ftsmps, "vdd_s8" }, + {} +}; + +static const struct rpm_regulator_data rpm_pm8941_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8x41_hfsmps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8x41_hfsmps, "vdd_s2" }, + { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8x41_hfsmps, "vdd_s3" }, + { "s4", QCOM_SMD_RPM_BOOST, 1, &pm8941_boost }, + + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8941_nldo, "vdd_l1_l3" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8941_nldo, "vdd_l2_lvs1_2_3" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8941_nldo, "vdd_l1_l3" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8941_nldo, "vdd_l4_l11" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8941_lnldo, "vdd_l5_l7" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8941_lnldo, "vdd_l5_l7" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8941_nldo, "vdd_l4_l11" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8941_pldo, "vdd_l6_l12_l14_l15" }, + { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l19", QCOM_SMD_RPM_LDOA, 19, &pm8941_pldo, "vdd_l8_l16_l18_l19" }, + { "l20", QCOM_SMD_RPM_LDOA, 20, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + { "l21", QCOM_SMD_RPM_LDOA, 21, &pm8941_pldo, "vdd_l21" }, + { "l22", QCOM_SMD_RPM_LDOA, 22, &pm8941_pldo, "vdd_l9_l10_l17_l22" }, + { "l23", QCOM_SMD_RPM_LDOA, 23, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + { "l24", QCOM_SMD_RPM_LDOA, 24, &pm8941_pldo, "vdd_l13_l20_l23_l24" }, + + { "lvs1", QCOM_SMD_RPM_VSA, 1, &pm8941_switch, "vdd_l2_lvs1_2_3" }, + { "lvs2", QCOM_SMD_RPM_VSA, 2, &pm8941_switch, "vdd_l2_lvs1_2_3" }, + { "lvs3", QCOM_SMD_RPM_VSA, 3, &pm8941_switch, "vdd_l2_lvs1_2_3" }, + + { "5vs1", QCOM_SMD_RPM_VSA, 4, &pm8941_switch, "vin_5vs" }, + { "5vs2", QCOM_SMD_RPM_VSA, 5, &pm8941_switch, "vin_5vs" }, + + {} +}; + +static const struct of_device_id rpm_of_match[] = { + { .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators }, + { .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators }, + {} +}; +MODULE_DEVICE_TABLE(of, rpm_of_match); + +static int rpm_reg_probe(struct platform_device *pdev) +{ + const struct rpm_regulator_data *reg; + const struct of_device_id *match; + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct qcom_rpm_reg *vreg; + struct qcom_smd_rpm *rpm; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { + dev_err(&pdev->dev, "unable to retrieve handle to rpm\n"); + return -ENODEV; + } + + match = of_match_device(rpm_of_match, &pdev->dev); + for (reg = match->data; reg->name; reg++) { + vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) + return -ENOMEM; + + vreg->dev = &pdev->dev; + vreg->type = reg->type; + vreg->id = reg->id; + vreg->rpm = rpm; + + memcpy(&vreg->desc, reg->desc, sizeof(vreg->desc)); + + vreg->desc.id = -1; + vreg->desc.owner = THIS_MODULE; + vreg->desc.type = REGULATOR_VOLTAGE; + vreg->desc.name = reg->name; + vreg->desc.supply_name = reg->supply; + vreg->desc.of_match = reg->name; + + config.dev = &pdev->dev; + config.driver_data = vreg; + rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", reg->name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static struct platform_driver rpm_reg_driver = { + .probe = rpm_reg_probe, + .driver = { + .name = "qcom_rpm_smd_regulator", + .of_match_table = rpm_of_match, + }, +}; + +static int __init rpm_reg_init(void) +{ + return platform_driver_register(&rpm_reg_driver); +} +subsys_initcall(rpm_reg_init); + +static void __exit rpm_reg_exit(void) +{ + platform_driver_unregister(&rpm_reg_driver); +} +module_exit(rpm_reg_exit) + +MODULE_DESCRIPTION("Qualcomm RPM regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 850a30a95b5b..88a5dc88badc 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -26,6 +26,70 @@ #include <linux/regmap.h> #include <linux/list.h> +/* Pin control enable input pins. */ +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_NONE 0x00 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN0 0x01 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN1 0x02 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN2 0x04 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN3 0x08 +#define SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT 0x10 + +/* Pin control high power mode input pins. */ +#define SPMI_REGULATOR_PIN_CTRL_HPM_NONE 0x00 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN0 0x01 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN1 0x02 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN2 0x04 +#define SPMI_REGULATOR_PIN_CTRL_HPM_EN3 0x08 +#define SPMI_REGULATOR_PIN_CTRL_HPM_SLEEP_B 0x10 +#define SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT 0x20 + +/* + * Used with enable parameters to specify that hardware default register values + * should be left unaltered. + */ +#define SPMI_REGULATOR_USE_HW_DEFAULT 2 + +/* Soft start strength of a voltage switch type regulator */ +enum spmi_vs_soft_start_str { + SPMI_VS_SOFT_START_STR_0P05_UA = 0, + SPMI_VS_SOFT_START_STR_0P25_UA, + SPMI_VS_SOFT_START_STR_0P55_UA, + SPMI_VS_SOFT_START_STR_0P75_UA, + SPMI_VS_SOFT_START_STR_HW_DEFAULT, +}; + +/** + * struct spmi_regulator_init_data - spmi-regulator initialization data + * @pin_ctrl_enable: Bit mask specifying which hardware pins should be + * used to enable the regulator, if any + * Value should be an ORing of + * SPMI_REGULATOR_PIN_CTRL_ENABLE_* constants. If + * the bit specified by + * SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT is + * set, then pin control enable hardware registers + * will not be modified. + * @pin_ctrl_hpm: Bit mask specifying which hardware pins should be + * used to force the regulator into high power + * mode, if any + * Value should be an ORing of + * SPMI_REGULATOR_PIN_CTRL_HPM_* constants. If + * the bit specified by + * SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT is + * set, then pin control mode hardware registers + * will not be modified. + * @vs_soft_start_strength: This parameter sets the soft start strength for + * voltage switch type regulators. Its value + * should be one of SPMI_VS_SOFT_START_STR_*. If + * its value is SPMI_VS_SOFT_START_STR_HW_DEFAULT, + * then the soft start strength will be left at its + * default hardware value. + */ +struct spmi_regulator_init_data { + unsigned pin_ctrl_enable; + unsigned pin_ctrl_hpm; + enum spmi_vs_soft_start_str vs_soft_start_strength; +}; + /* These types correspond to unique register layouts. */ enum spmi_regulator_logical_type { SPMI_REGULATOR_LOGICAL_TYPE_SMPS, @@ -458,6 +522,14 @@ static int spmi_regulator_vs_enable(struct regulator_dev *rdev) return spmi_regulator_common_enable(rdev); } +static int spmi_regulator_vs_ocp(struct regulator_dev *rdev) +{ + struct spmi_regulator *vreg = rdev_get_drvdata(rdev); + u8 reg = SPMI_VS_OCP_OVERRIDE; + + return spmi_vreg_write(vreg, SPMI_VS_REG_OCP, ®, 1); +} + static int spmi_regulator_common_disable(struct regulator_dev *rdev) { struct spmi_regulator *vreg = rdev_get_drvdata(rdev); @@ -504,8 +576,7 @@ static int spmi_regulator_select_voltage(struct spmi_regulator *vreg, * Force uV to be an allowed set point by applying a ceiling function to * the uV value. */ - *voltage_sel = (uV - range->min_uV + range->step_uV - 1) - / range->step_uV; + *voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV); uV = *voltage_sel * range->step_uV + range->min_uV; if (uV > max_uV) { @@ -792,6 +863,9 @@ static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev) if (reg & SPMI_COMMON_MODE_HPM_MASK) return REGULATOR_MODE_NORMAL; + if (reg & SPMI_COMMON_MODE_AUTO_MASK) + return REGULATOR_MODE_FAST; + return REGULATOR_MODE_IDLE; } @@ -799,11 +873,13 @@ static int spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode) { struct spmi_regulator *vreg = rdev_get_drvdata(rdev); - u8 mask = SPMI_COMMON_MODE_HPM_MASK; + u8 mask = SPMI_COMMON_MODE_HPM_MASK | SPMI_COMMON_MODE_AUTO_MASK; u8 val = 0; if (mode == REGULATOR_MODE_NORMAL) - val = mask; + val = SPMI_COMMON_MODE_HPM_MASK; + else if (mode == REGULATOR_MODE_FAST) + val = SPMI_COMMON_MODE_AUTO_MASK; return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask); } @@ -973,6 +1049,7 @@ static struct regulator_ops spmi_vs_ops = { .is_enabled = spmi_regulator_common_is_enabled, .set_pull_down = spmi_regulator_common_set_pull_down, .set_soft_start = spmi_regulator_common_set_soft_start, + .set_over_current_protection = spmi_regulator_vs_ocp, }; static struct regulator_ops spmi_boost_ops = { @@ -1203,10 +1280,111 @@ static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg) return ret; } +static int spmi_regulator_init_registers(struct spmi_regulator *vreg, + const struct spmi_regulator_init_data *data) +{ + int ret; + enum spmi_regulator_logical_type type; + u8 ctrl_reg[8], reg, mask; + + type = vreg->logical_type; + + ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8); + if (ret) + return ret; + + /* Set up enable pin control. */ + if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO + || type == SPMI_REGULATOR_LOGICAL_TYPE_VS) + && !(data->pin_ctrl_enable + & SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_ENABLE] &= + ~SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; + ctrl_reg[SPMI_COMMON_IDX_ENABLE] |= + data->pin_ctrl_enable & SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK; + } + + /* Set up mode pin control. */ + if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO) + && !(data->pin_ctrl_hpm + & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_ALL_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_ALL_MASK; + } + + if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS + && !(data->pin_ctrl_hpm & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + } + + if ((type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS + || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO) + && !(data->pin_ctrl_hpm + & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { + ctrl_reg[SPMI_COMMON_IDX_MODE] &= + ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + ctrl_reg[SPMI_COMMON_IDX_MODE] |= + data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK; + } + + /* Write back any control register values that were modified. */ + ret = spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8); + if (ret) + return ret; + + /* Set soft start strength and over current protection for VS. */ + if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS) { + if (data->vs_soft_start_strength + != SPMI_VS_SOFT_START_STR_HW_DEFAULT) { + reg = data->vs_soft_start_strength + & SPMI_VS_SOFT_START_SEL_MASK; + mask = SPMI_VS_SOFT_START_SEL_MASK; + return spmi_vreg_update_bits(vreg, + SPMI_VS_REG_SOFT_START, + reg, mask); + } + } + + return 0; +} + +static void spmi_regulator_get_dt_config(struct spmi_regulator *vreg, + struct device_node *node, struct spmi_regulator_init_data *data) +{ + /* + * Initialize configuration parameters to use hardware default in case + * no value is specified via device tree. + */ + data->pin_ctrl_enable = SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT; + data->pin_ctrl_hpm = SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT; + data->vs_soft_start_strength = SPMI_VS_SOFT_START_STR_HW_DEFAULT; + + /* These bindings are optional, so it is okay if they aren't found. */ + of_property_read_u32(node, "qcom,ocp-max-retries", + &vreg->ocp_max_retries); + of_property_read_u32(node, "qcom,ocp-retry-delay", + &vreg->ocp_retry_delay_ms); + of_property_read_u32(node, "qcom,pin-ctrl-enable", + &data->pin_ctrl_enable); + of_property_read_u32(node, "qcom,pin-ctrl-hpm", &data->pin_ctrl_hpm); + of_property_read_u32(node, "qcom,vs-soft-start-strength", + &data->vs_soft_start_strength); +} + static unsigned int spmi_regulator_of_map_mode(unsigned int mode) { - if (mode) + if (mode == 1) return REGULATOR_MODE_NORMAL; + if (mode == 2) + return REGULATOR_MODE_FAST; return REGULATOR_MODE_IDLE; } @@ -1215,12 +1393,23 @@ static int spmi_regulator_of_parse(struct device_node *node, const struct regulator_desc *desc, struct regulator_config *config) { + struct spmi_regulator_init_data data = { }; struct spmi_regulator *vreg = config->driver_data; struct device *dev = config->dev; int ret; - vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES; - vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS; + spmi_regulator_get_dt_config(vreg, node, &data); + + if (!vreg->ocp_max_retries) + vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES; + if (!vreg->ocp_retry_delay_ms) + vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS; + + ret = spmi_regulator_init_registers(vreg, &data); + if (ret) { + dev_err(dev, "common initialization failed, ret=%d\n", ret); + return ret; + } if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) { ret = spmi_regulator_ftsmps_init_slew_rate(vreg); diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 3fd44353cc80..d86a3dcd61e2 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -16,12 +16,16 @@ * more details. */ -#include <linux/module.h> +#include <linux/delay.h> +#include <linux/gpio.h> #include <linux/i2c.h> -#include <linux/mfd/rk808.h> +#include <linux/module.h> #include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/mfd/rk808.h> #include <linux/regulator/driver.h> #include <linux/regulator/of_regulator.h> +#include <linux/gpio/consumer.h> /* Field Definitions */ #define RK808_BUCK_VSEL_MASK 0x3f @@ -36,12 +40,25 @@ #define RK808_RAMP_RATE_6MV_PER_US (2 << RK808_RAMP_RATE_OFFSET) #define RK808_RAMP_RATE_10MV_PER_US (3 << RK808_RAMP_RATE_OFFSET) +#define RK808_DVS2_POL BIT(2) +#define RK808_DVS1_POL BIT(1) + /* Offset from XXX_ON_VSEL to XXX_SLP_VSEL */ #define RK808_SLP_REG_OFFSET 1 +/* Offset from XXX_ON_VSEL to XXX_DVS_VSEL */ +#define RK808_DVS_REG_OFFSET 2 + /* Offset from XXX_EN_REG to SLEEP_SET_OFF_XXX */ #define RK808_SLP_SET_OFF_REG_OFFSET 2 +/* max steps for increase voltage of Buck1/2, equal 100mv*/ +#define MAX_STEPS_ONE_TIME 8 + +struct rk808_regulator_data { + struct gpio_desc *dvs_gpio[2]; +}; + static const int rk808_buck_config_regs[] = { RK808_BUCK1_CONFIG_REG, RK808_BUCK2_CONFIG_REG, @@ -70,6 +87,131 @@ static const struct regulator_linear_range rk808_ldo6_voltage_ranges[] = { REGULATOR_LINEAR_RANGE(800000, 0, 17, 100000), }; +static int rk808_buck1_2_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev); + int id = rdev->desc->id - RK808_ID_DCDC1; + struct gpio_desc *gpio = pdata->dvs_gpio[id]; + unsigned int val; + int ret; + + if (!gpio || gpiod_get_value(gpio) == 0) + return regulator_get_voltage_sel_regmap(rdev); + + ret = regmap_read(rdev->regmap, + rdev->desc->vsel_reg + RK808_DVS_REG_OFFSET, + &val); + if (ret != 0) + return ret; + + val &= rdev->desc->vsel_mask; + val >>= ffs(rdev->desc->vsel_mask) - 1; + + return val; +} + +static int rk808_buck1_2_i2c_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) +{ + int ret, delta_sel; + unsigned int old_sel, tmp, val, mask = rdev->desc->vsel_mask; + + ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val); + if (ret != 0) + return ret; + + tmp = val & ~mask; + old_sel = val & mask; + old_sel >>= ffs(mask) - 1; + delta_sel = sel - old_sel; + + /* + * If directly modify the register to change the voltage, we will face + * the risk of overshoot. Put it into a multi-step, can effectively + * avoid this problem, a step is 100mv here. + */ + while (delta_sel > MAX_STEPS_ONE_TIME) { + old_sel += MAX_STEPS_ONE_TIME; + val = old_sel << (ffs(mask) - 1); + val |= tmp; + + /* + * i2c is 400kHz (2.5us per bit) and we must transmit _at least_ + * 3 bytes (24 bits) plus start and stop so 26 bits. So we've + * got more than 65 us between each voltage change and thus + * won't ramp faster than ~1500 uV / us. + */ + ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, val); + delta_sel = sel - old_sel; + } + + sel <<= ffs(mask) - 1; + val = tmp | sel; + ret = regmap_write(rdev->regmap, rdev->desc->vsel_reg, val); + + /* + * When we change the voltage register directly, the ramp rate is about + * 100000uv/us, wait 1us to make sure the target voltage to be stable, + * so we needn't wait extra time after that. + */ + udelay(1); + + return ret; +} + +static int rk808_buck1_2_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) +{ + struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev); + int id = rdev->desc->id - RK808_ID_DCDC1; + struct gpio_desc *gpio = pdata->dvs_gpio[id]; + unsigned int reg = rdev->desc->vsel_reg; + unsigned old_sel; + int ret, gpio_level; + + if (!gpio) + return rk808_buck1_2_i2c_set_voltage_sel(rdev, sel); + + gpio_level = gpiod_get_value(gpio); + if (gpio_level == 0) { + reg += RK808_DVS_REG_OFFSET; + ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &old_sel); + } else { + ret = regmap_read(rdev->regmap, + reg + RK808_DVS_REG_OFFSET, + &old_sel); + } + + if (ret != 0) + return ret; + + sel <<= ffs(rdev->desc->vsel_mask) - 1; + sel |= old_sel & ~rdev->desc->vsel_mask; + + ret = regmap_write(rdev->regmap, reg, sel); + if (ret) + return ret; + + gpiod_set_value(gpio, !gpio_level); + + return ret; +} + +static int rk808_buck1_2_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct rk808_regulator_data *pdata = rdev_get_drvdata(rdev); + int id = rdev->desc->id - RK808_ID_DCDC1; + struct gpio_desc *gpio = pdata->dvs_gpio[id]; + + /* if there is no dvs1/2 pin, we don't need wait extra time here. */ + if (!gpio) + return 0; + + return regulator_set_voltage_time_sel(rdev, old_selector, new_selector); +} + static int rk808_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { unsigned int ramp_value = RK808_RAMP_RATE_10MV_PER_US; @@ -137,8 +279,9 @@ static int rk808_set_suspend_disable(struct regulator_dev *rdev) static struct regulator_ops rk808_buck1_2_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 = regulator_set_voltage_sel_regmap, + .get_voltage_sel = rk808_buck1_2_get_voltage_sel_regmap, + .set_voltage_sel = rk808_buck1_2_set_voltage_sel, + .set_voltage_time_sel = rk808_buck1_2_set_voltage_time_sel, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -380,25 +523,69 @@ static struct of_regulator_match rk808_reg_matches[] = { [RK808_ID_SWITCH2] = { .name = "SWITCH_REG2" }, }; +static int rk808_regulator_dt_parse_pdata(struct device *dev, + struct device *client_dev, + struct regmap *map, + struct rk808_regulator_data *pdata) +{ + struct device_node *np; + int tmp, ret, i; + + np = of_get_child_by_name(client_dev->of_node, "regulators"); + if (!np) + return -ENXIO; + + ret = of_regulator_match(dev, np, rk808_reg_matches, + RK808_NUM_REGULATORS); + if (ret < 0) + goto dt_parse_end; + + for (i = 0; i < ARRAY_SIZE(pdata->dvs_gpio); i++) { + pdata->dvs_gpio[i] = + devm_gpiod_get_index_optional(client_dev, "dvs", i, + GPIOD_OUT_LOW); + if (IS_ERR(pdata->dvs_gpio[i])) { + ret = PTR_ERR(pdata->dvs_gpio[i]); + dev_err(dev, "failed to get dvs%d gpio (%d)\n", i, ret); + goto dt_parse_end; + } + + if (!pdata->dvs_gpio[i]) { + dev_warn(dev, "there is no dvs%d gpio\n", i); + continue; + } + + tmp = i ? RK808_DVS2_POL : RK808_DVS1_POL; + ret = regmap_update_bits(map, RK808_IO_POL_REG, tmp, + gpiod_is_active_low(pdata->dvs_gpio[i]) ? + 0 : tmp); + } + +dt_parse_end: + of_node_put(np); + return ret; +} + static int rk808_regulator_probe(struct platform_device *pdev) { struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); struct i2c_client *client = rk808->i2c; - struct device_node *reg_np; struct regulator_config config = {}; struct regulator_dev *rk808_rdev; + struct rk808_regulator_data *pdata; int ret, i; - reg_np = of_get_child_by_name(client->dev.of_node, "regulators"); - if (!reg_np) - return -ENXIO; + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; - ret = of_regulator_match(&pdev->dev, reg_np, rk808_reg_matches, - RK808_NUM_REGULATORS); - of_node_put(reg_np); + ret = rk808_regulator_dt_parse_pdata(&pdev->dev, &client->dev, + rk808->regmap, pdata); if (ret < 0) return ret; + platform_set_drvdata(pdev, pdata); + /* Instantiate the regulators */ for (i = 0; i < RK808_NUM_REGULATORS; i++) { if (!rk808_reg_matches[i].init_data || @@ -406,7 +593,7 @@ static int rk808_regulator_probe(struct platform_device *pdev) continue; config.dev = &client->dev; - config.driver_data = rk808; + config.driver_data = pdata; config.regmap = rk808->regmap; config.of_node = rk808_reg_matches[i].of_node; config.init_data = rk808_reg_matches[i].init_data; @@ -427,6 +614,7 @@ static struct platform_driver rk808_regulator_driver = { .probe = rk808_regulator_probe, .driver = { .name = "rk808-regulator", + .owner = THIS_MODULE, }, }; |