mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
Merge remote-tracking branches 'regulator/topic/qcom-smd', 'regulator/topic/qcom-spmi', 'regulator/topic/rk808', 'regulator/topic/stub' and 'regulator/topic/tol' into regulator-next
This commit is contained in:
commit
aaa9b649d0
@ -24,6 +24,10 @@ Optional properties:
|
||||
- vcc10-supply: The input supply for LDO_REG6
|
||||
- vcc11-supply: The input supply for LDO_REG8
|
||||
- vcc12-supply: The input supply for SWITCH_REG2
|
||||
- dvs-gpios: buck1/2 can be controlled by gpio dvs, this is GPIO specifiers
|
||||
for 2 host gpio's used for dvs. The format of the gpio specifier depends in
|
||||
the gpio controller. If DVS GPIOs aren't present, voltage changes will happen
|
||||
very quickly with no slow ramp time.
|
||||
|
||||
Regulators: All the regulators of RK808 to be instantiated shall be
|
||||
listed in a child node named 'regulators'. Each regulator is represented
|
||||
@ -55,7 +59,9 @@ Example:
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pmic_int>;
|
||||
pinctrl-0 = <&pmic_int &dvs_1 &dvs_2>;
|
||||
dvs-gpios = <&gpio7 11 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio7 15 GPIO_ACTIVE_HIGH>;
|
||||
reg = <0x1b>;
|
||||
rockchip,system-power-controller;
|
||||
wakeup-source;
|
||||
|
@ -91,13 +91,65 @@ see regulator.txt - with additional custom properties described below:
|
||||
- regulator-initial-mode:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Descrption: 1 = Set initial mode to high power mode (HPM), also referred
|
||||
to as NPM. HPM consumes more ground current than LPM, but
|
||||
Description: 2 = Set initial mode to auto mode (automatically select
|
||||
between HPM and LPM); not available on boost type
|
||||
regulators.
|
||||
|
||||
1 = Set initial mode to high power mode (HPM), also referred
|
||||
to as NPM. HPM consumes more ground current than LPM, but
|
||||
it can source significantly higher load current. HPM is not
|
||||
available on boost type regulators. For voltage switch type
|
||||
regulators, HPM implies that over current protection and
|
||||
soft start are active all the time. 0 = Set initial mode to
|
||||
low power mode (LPM).
|
||||
soft start are active all the time.
|
||||
|
||||
0 = Set initial mode to low power mode (LPM).
|
||||
|
||||
- qcom,ocp-max-retries:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Description: Maximum number of times to try toggling a voltage switch
|
||||
off and back on as a result of consecutive over current
|
||||
events.
|
||||
|
||||
- qcom,ocp-retry-delay:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Description: Time to delay in milliseconds between each voltage switch
|
||||
toggle after an over current event takes place.
|
||||
|
||||
- qcom,pin-ctrl-enable:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Description: Bit mask specifying which hardware pins should be used to
|
||||
enable the regulator, if any; supported bits are:
|
||||
0 = ignore all hardware enable signals
|
||||
BIT(0) = follow HW0_EN signal
|
||||
BIT(1) = follow HW1_EN signal
|
||||
BIT(2) = follow HW2_EN signal
|
||||
BIT(3) = follow HW3_EN signal
|
||||
|
||||
- qcom,pin-ctrl-hpm:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Description: Bit mask specifying which hardware pins should be used to
|
||||
force the regulator into high power mode, if any;
|
||||
supported bits are:
|
||||
0 = ignore all hardware enable signals
|
||||
BIT(0) = follow HW0_EN signal
|
||||
BIT(1) = follow HW1_EN signal
|
||||
BIT(2) = follow HW2_EN signal
|
||||
BIT(3) = follow HW3_EN signal
|
||||
BIT(4) = follow PMIC awake state
|
||||
|
||||
- qcom,vs-soft-start-strength:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Description: This property sets the soft start strength for voltage
|
||||
switch type regulators; supported values are:
|
||||
0 = 0.05 uA
|
||||
1 = 0.25 uA
|
||||
2 = 0.55 uA
|
||||
3 = 0.75 uA
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
350
drivers/regulator/qcom_smd-regulator.c
Normal file
350
drivers/regulator/qcom_smd-regulator.c
Normal file
@ -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");
|
@ -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);
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -550,8 +550,24 @@ static inline int regulator_count_voltages(struct regulator *regulator)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int regulator_list_voltage(struct regulator *regulator, unsigned selector)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline int regulator_set_voltage_triplet(struct regulator *regulator,
|
||||
int min_uV, int target_uV,
|
||||
int max_uV)
|
||||
{
|
||||
if (regulator_set_voltage(regulator, target_uV, max_uV) == 0)
|
||||
return 0;
|
||||
|
||||
return regulator_set_voltage(regulator, min_uV, max_uV);
|
||||
}
|
||||
|
||||
static inline int regulator_set_voltage_tol(struct regulator *regulator,
|
||||
int new_uV, int tol_uV)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user