mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-10 22:40:54 -04:00
regulator: add a regulator driver for the PM8008 PMIC
Qualcomm Technologies, Inc. PM8008 is an I2C controlled PMIC containing 7 LDO regulators (two N600 and five P300). Add a PM8008 regulator driver to support PMIC regulator management via the regulator framework. This is a snapshot of the PM8008 regulator driver found in the msm-4.19 kernel branch with a few small changes. The most significant of these was replacing module_platform_driver() with module_init() and module_exit() to avoid a compilation error when compiling the driver as a module. Change-Id: Ie8d96b50558b8dd3084134216dff6393bac16f71 Signed-off-by: Ashay Jaiswal <ashayj@codeaurora.org> Signed-off-by: Umang Agrawal <uagrawal@codeaurora.org> Signed-off-by: David Collins <collinsd@codeaurora.org>
This commit is contained in:
parent
f22a034b4c
commit
e3116a7023
3 changed files with 796 additions and 0 deletions
|
|
@ -750,6 +750,15 @@ config REGULATOR_PWM
|
|||
This driver supports PWM controlled voltage regulators. PWM
|
||||
duty cycle can increase or decrease the voltage.
|
||||
|
||||
config REGULATOR_QCOM_PM8008
|
||||
tristate "Qualcomm Technologies, Inc. PM8008 PMIC regulators"
|
||||
depends on MFD_I2C_PMIC
|
||||
help
|
||||
This driver provides support for the voltage regulators and top-level
|
||||
chip enablement of Qualcomm Technologies, Inc. PM8008 PMIC chips.
|
||||
When all seven of the PM8008 LDO regulators are disabled, the PM8008
|
||||
is put into a low power state.
|
||||
|
||||
config REGULATOR_QCOM_RPM
|
||||
tristate "Qualcomm RPM regulator driver"
|
||||
depends on MFD_QCOM_RPM
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_PM8008) += qcom_pm8008-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
|
||||
|
|
|
|||
786
drivers/regulator/qcom_pm8008-regulator.c
Normal file
786
drivers/regulator/qcom_pm8008-regulator.c
Normal file
|
|
@ -0,0 +1,786 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#define pr_fmt(fmt) "PM8008: %s: " fmt, __func__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define pm8008_err(reg, message, ...) \
|
||||
pr_err("%s: " message, (reg)->rdesc.name, ##__VA_ARGS__)
|
||||
#define pm8008_debug(reg, message, ...) \
|
||||
pr_debug("%s: " message, (reg)->rdesc.name, ##__VA_ARGS__)
|
||||
|
||||
#define STARTUP_DELAY_USEC 20
|
||||
#define VSET_STEP_SIZE_MV 1
|
||||
#define VSET_STEP_MV 8
|
||||
|
||||
#define MISC_BASE 0x900
|
||||
|
||||
#define MISC_CHIP_ENABLE_REG (MISC_BASE + 0x50)
|
||||
#define CHIP_ENABLE_BIT BIT(0)
|
||||
|
||||
#define LDO_ENABLE_REG(base) (base + 0x46)
|
||||
#define ENABLE_BIT BIT(7)
|
||||
|
||||
#define LDO_STATUS1_REG(base) (base + 0x08)
|
||||
#define VREG_READY_BIT BIT(7)
|
||||
#define MODE_STATE_MASK GENMASK(1, 0)
|
||||
#define MODE_STATE_NPM 3
|
||||
#define MODE_STATE_LPM 2
|
||||
#define MODE_STATE_BYPASS 0
|
||||
|
||||
#define LDO_VSET_LB_REG(base) (base + 0x40)
|
||||
|
||||
#define LDO_VSET_VALID_LB_REG(base) (base + 0x42)
|
||||
|
||||
#define LDO_MODE_CTL1_REG(base) (base + 0x45)
|
||||
#define MODE_PRIMARY_MASK GENMASK(2, 0)
|
||||
#define LDO_MODE_NPM 7
|
||||
#define LDO_MODE_LPM 4
|
||||
#define FORCED_BYPASS 2
|
||||
|
||||
#define LDO_STEPPER_CTL_REG(base) (base + 0x3b)
|
||||
#define STEP_RATE_MASK GENMASK(1, 0)
|
||||
|
||||
#define LDO_PD_CTL_REG(base) (base + 0xA0)
|
||||
#define STRONG_PD_EN_BIT BIT(7)
|
||||
|
||||
#define MAX_REG_NAME 20
|
||||
#define PM8008_MAX_LDO 7
|
||||
|
||||
struct pm8008_chip {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regulator_dev *rdev;
|
||||
struct regulator_desc rdesc;
|
||||
|
||||
};
|
||||
|
||||
struct regulator_data {
|
||||
char *name;
|
||||
char *supply_name;
|
||||
int hpm_min_load_ua;
|
||||
int min_dropout_uv;
|
||||
};
|
||||
|
||||
struct pm8008_regulator {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regulator_desc rdesc;
|
||||
struct regulator_dev *rdev;
|
||||
struct regulator *parent_supply;
|
||||
struct regulator *en_supply;
|
||||
struct device_node *of_node;
|
||||
u16 base;
|
||||
int hpm_min_load_ua;
|
||||
int min_dropout_uv;
|
||||
int step_rate;
|
||||
};
|
||||
|
||||
static struct regulator_data reg_data[] = {
|
||||
/* name, parent, min load, headroom */
|
||||
{"pm8008_l1", "vdd_l1_l2", 10000, 225000},
|
||||
{"pm8008_l2", "vdd_l1_l2", 10000, 225000},
|
||||
{"pm8008_l3", "vdd_l3_l4", 10000, 200000},
|
||||
{"pm8008_l4", "vdd_l3_l4", 10000, 200000},
|
||||
{"pm8008_l5", "vdd_l5", 10000, 300000},
|
||||
{"pm8008_l6", "vdd_l6", 10000, 300000},
|
||||
{"pm8008_l7", "vdd_l7", 10000, 300000},
|
||||
};
|
||||
|
||||
/* common functions */
|
||||
static int pm8008_read(struct regmap *regmap, u16 reg, u8 *val, int count)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = regmap_bulk_read(regmap, reg, val, count);
|
||||
if (rc < 0)
|
||||
pr_err("failed to read 0x%04x\n", reg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pm8008_write(struct regmap *regmap, u16 reg, u8 *val, int count)
|
||||
{
|
||||
int rc;
|
||||
|
||||
pr_debug("Writing 0x%02x to 0x%04x\n", val, reg);
|
||||
rc = regmap_bulk_write(regmap, reg, val, count);
|
||||
if (rc < 0)
|
||||
pr_err("failed to write 0x%04x\n", reg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pm8008_masked_write(struct regmap *regmap, u16 reg, u8 mask,
|
||||
u8 val)
|
||||
{
|
||||
int rc;
|
||||
|
||||
pr_debug("Writing 0x%02x to 0x%04x with mask 0x%02x\n", val, reg, mask);
|
||||
rc = regmap_update_bits(regmap, reg, mask, val);
|
||||
if (rc < 0)
|
||||
pr_err("failed to write 0x%02x to 0x%04x with mask 0x%02x\n",
|
||||
val, reg, mask);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* PM8008 LDO Regulator callbacks */
|
||||
static int pm8008_regulator_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
u8 vset_raw[2];
|
||||
int rc;
|
||||
|
||||
rc = pm8008_read(pm8008_reg->regmap,
|
||||
LDO_VSET_VALID_LB_REG(pm8008_reg->base),
|
||||
vset_raw, 2);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to read regulator voltage rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pm8008_debug(pm8008_reg, "VSET read [%x][%x]\n",
|
||||
vset_raw[1], vset_raw[0]);
|
||||
return (vset_raw[1] << 8 | vset_raw[0]) * 1000;
|
||||
}
|
||||
|
||||
static int pm8008_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
u8 reg;
|
||||
|
||||
rc = pm8008_read(pm8008_reg->regmap,
|
||||
LDO_ENABLE_REG(pm8008_reg->base), ®, 1);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg, "failed to read enable reg rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return !!(reg & ENABLE_BIT);
|
||||
}
|
||||
|
||||
static int pm8008_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
int rc, init_mv, delay_us, delay_ms, retry_count = 10;
|
||||
u8 reg;
|
||||
|
||||
rc = regulator_enable(pm8008_reg->en_supply);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to enable en_supply rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (pm8008_reg->parent_supply) {
|
||||
rc = regulator_enable(pm8008_reg->parent_supply);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to enable parent rc=%d\n", rc);
|
||||
regulator_disable(pm8008_reg->en_supply);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = pm8008_masked_write(pm8008_reg->regmap,
|
||||
LDO_ENABLE_REG(pm8008_reg->base),
|
||||
ENABLE_BIT, ENABLE_BIT);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to enable regulator rc=%d\n", rc);
|
||||
goto remove_vote;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for VREG_OK
|
||||
* Read voltage and calculate the delay.
|
||||
*/
|
||||
init_mv = pm8008_regulator_get_voltage(rdev) / 1000;
|
||||
if (init_mv < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to get regulator voltage rc=%d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
delay_us = STARTUP_DELAY_USEC
|
||||
+ DIV_ROUND_UP(init_mv * 1000, pm8008_reg->step_rate);
|
||||
delay_ms = DIV_ROUND_UP(delay_us, 1000);
|
||||
|
||||
/* Retry 10 times for VREG_OK before bailing out */
|
||||
while (retry_count--) {
|
||||
if (delay_ms > 20)
|
||||
msleep(delay_ms);
|
||||
else
|
||||
usleep_range(delay_us, delay_us + 100);
|
||||
|
||||
rc = pm8008_read(pm8008_reg->regmap,
|
||||
LDO_STATUS1_REG(pm8008_reg->base), ®, 1);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to read regulator status rc=%d\n", rc);
|
||||
goto out;
|
||||
}
|
||||
if (reg & VREG_READY_BIT) {
|
||||
pm8008_debug(pm8008_reg, "regulator enabled\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to enable regulator VREG_READY not set\n");
|
||||
out:
|
||||
pm8008_masked_write(pm8008_reg->regmap,
|
||||
LDO_ENABLE_REG(pm8008_reg->base), ENABLE_BIT, 0);
|
||||
remove_vote:
|
||||
rc = regulator_disable(pm8008_reg->en_supply);
|
||||
if (pm8008_reg->parent_supply)
|
||||
rc |= regulator_disable(pm8008_reg->parent_supply);
|
||||
if (rc < 0)
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to disable parent regulator rc=%d\n", rc);
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
static int pm8008_regulator_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
|
||||
rc = pm8008_masked_write(pm8008_reg->regmap,
|
||||
LDO_ENABLE_REG(pm8008_reg->base),
|
||||
ENABLE_BIT, 0);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to disable regulator rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* remove vote from chip enable regulator */
|
||||
rc = regulator_disable(pm8008_reg->en_supply);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to disable en_supply rc=%d\n", rc);
|
||||
}
|
||||
|
||||
/* remove voltage vote from parent regulator */
|
||||
if (pm8008_reg->parent_supply) {
|
||||
rc = regulator_set_voltage(pm8008_reg->parent_supply,
|
||||
0, INT_MAX);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to remove parent voltage rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
rc = regulator_disable(pm8008_reg->parent_supply);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to disable parent rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
pm8008_debug(pm8008_reg, "regulator disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8008_write_voltage(struct pm8008_regulator *pm8008_reg, int min_uv,
|
||||
int max_uv)
|
||||
{
|
||||
int rc = 0, mv;
|
||||
u8 vset_raw[2];
|
||||
|
||||
mv = DIV_ROUND_UP(min_uv, 1000);
|
||||
if (mv * 1000 > max_uv) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"requested voltage above maximum limit\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each LSB of regulator is 1mV and the voltage setpoint
|
||||
* should be multiple of 8mV(step).
|
||||
*/
|
||||
mv = DIV_ROUND_UP(DIV_ROUND_UP(mv, VSET_STEP_MV) * VSET_STEP_MV,
|
||||
VSET_STEP_SIZE_MV);
|
||||
|
||||
vset_raw[0] = mv & 0xff;
|
||||
vset_raw[1] = (mv & 0xff00) >> 8;
|
||||
rc = pm8008_write(pm8008_reg->regmap, LDO_VSET_LB_REG(pm8008_reg->base),
|
||||
vset_raw, 2);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg, "failed to write voltage rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pm8008_debug(pm8008_reg, "VSET=[%x][%x]\n", vset_raw[1], vset_raw[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8008_regulator_set_voltage(struct regulator_dev *rdev,
|
||||
int min_uv, int max_uv, unsigned int *selector)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
int rc = 0;
|
||||
|
||||
if (pm8008_reg->parent_supply) {
|
||||
/* request on parent regulator with headroom */
|
||||
rc = regulator_set_voltage(pm8008_reg->parent_supply,
|
||||
pm8008_reg->min_dropout_uv + min_uv,
|
||||
INT_MAX);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg,
|
||||
"failed to request parent supply voltage rc=%d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = pm8008_write_voltage(pm8008_reg, min_uv, max_uv);
|
||||
if (rc < 0) {
|
||||
/* remove parent's voltage vote */
|
||||
if (pm8008_reg->parent_supply)
|
||||
regulator_set_voltage(pm8008_reg->parent_supply,
|
||||
0, INT_MAX);
|
||||
}
|
||||
|
||||
pm8008_debug(pm8008_reg, "voltage set to %d\n", min_uv);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pm8008_regulator_set_mode(struct regulator_dev *rdev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
u8 val = LDO_MODE_LPM;
|
||||
|
||||
if (mode == REGULATOR_MODE_NORMAL)
|
||||
val = LDO_MODE_NPM;
|
||||
else if (mode == REGULATOR_MODE_IDLE)
|
||||
val = LDO_MODE_LPM;
|
||||
|
||||
rc = pm8008_masked_write(pm8008_reg->regmap,
|
||||
LDO_MODE_CTL1_REG(pm8008_reg->base),
|
||||
MODE_PRIMARY_MASK, val);
|
||||
if (!rc)
|
||||
pm8008_debug(pm8008_reg, "mode set to %d\n", val);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static unsigned int pm8008_regulator_get_mode(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
u8 reg;
|
||||
|
||||
rc = pm8008_read(pm8008_reg->regmap,
|
||||
LDO_STATUS1_REG(pm8008_reg->base), ®, 1);
|
||||
if (rc < 0) {
|
||||
pm8008_err(pm8008_reg, "failed to get mode rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ((reg & MODE_STATE_MASK) == MODE_STATE_NPM)
|
||||
? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
|
||||
}
|
||||
|
||||
static int pm8008_regulator_set_load(struct regulator_dev *rdev, int load_uA)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
int mode;
|
||||
|
||||
if (load_uA >= pm8008_reg->hpm_min_load_ua)
|
||||
mode = REGULATOR_MODE_NORMAL;
|
||||
else
|
||||
mode = REGULATOR_MODE_IDLE;
|
||||
|
||||
return pm8008_regulator_set_mode(rdev, mode);
|
||||
}
|
||||
|
||||
static int pm8008_regulator_set_voltage_time(struct regulator_dev *rdev,
|
||||
int old_uV, int new_uv)
|
||||
{
|
||||
struct pm8008_regulator *pm8008_reg = rdev_get_drvdata(rdev);
|
||||
|
||||
return DIV_ROUND_UP(abs(new_uv - old_uV), pm8008_reg->step_rate);
|
||||
}
|
||||
|
||||
static struct regulator_ops pm8008_regulator_ops = {
|
||||
.enable = pm8008_regulator_enable,
|
||||
.disable = pm8008_regulator_disable,
|
||||
.is_enabled = pm8008_regulator_is_enabled,
|
||||
.set_voltage = pm8008_regulator_set_voltage,
|
||||
.get_voltage = pm8008_regulator_get_voltage,
|
||||
.set_mode = pm8008_regulator_set_mode,
|
||||
.get_mode = pm8008_regulator_get_mode,
|
||||
.set_load = pm8008_regulator_set_load,
|
||||
.set_voltage_time = pm8008_regulator_set_voltage_time,
|
||||
};
|
||||
|
||||
static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
|
||||
const char *name)
|
||||
{
|
||||
struct regulator_config reg_config = {};
|
||||
struct regulator_init_data *init_data;
|
||||
struct device *dev = pm8008_reg->dev;
|
||||
struct device_node *reg_node = pm8008_reg->of_node;
|
||||
char buff[MAX_REG_NAME];
|
||||
int rc, i, init_voltage;
|
||||
u8 reg;
|
||||
|
||||
/* get regulator data */
|
||||
for (i = 0; i < PM8008_MAX_LDO; i++)
|
||||
if (!strcmp(reg_data[i].name, name))
|
||||
break;
|
||||
|
||||
if (i == PM8008_MAX_LDO) {
|
||||
pr_err("Invalid regulator name %s\n", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = of_property_read_u16(reg_node, "reg", &pm8008_reg->base);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: failed to get regulator base rc=%d\n", name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv;
|
||||
of_property_read_u32(reg_node, "qcom,min-dropout-voltage",
|
||||
&pm8008_reg->min_dropout_uv);
|
||||
|
||||
pm8008_reg->hpm_min_load_ua = reg_data[i].hpm_min_load_ua;
|
||||
of_property_read_u32(reg_node, "qcom,hpm-min-load",
|
||||
&pm8008_reg->hpm_min_load_ua);
|
||||
init_voltage = -EINVAL;
|
||||
of_property_read_u32(reg_node, "qcom,init-voltage", &init_voltage);
|
||||
|
||||
if (of_property_read_bool(reg_node, "qcom,strong-pd")) {
|
||||
rc = pm8008_masked_write(pm8008_reg->regmap,
|
||||
LDO_PD_CTL_REG(pm8008_reg->base),
|
||||
STRONG_PD_EN_BIT, STRONG_PD_EN_BIT);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: failed to configure pull down rc=%d\n",
|
||||
name, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* get slew rate */
|
||||
rc = pm8008_read(pm8008_reg->regmap,
|
||||
LDO_STEPPER_CTL_REG(pm8008_reg->base), ®, 1);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: failed to read step rate configuration rc=%d\n",
|
||||
name, rc);
|
||||
return rc;
|
||||
}
|
||||
pm8008_reg->step_rate = 38400 >> (reg & STEP_RATE_MASK);
|
||||
|
||||
scnprintf(buff, MAX_REG_NAME, "%s-supply", reg_data[i].supply_name);
|
||||
if (of_find_property(dev->of_node, buff, NULL)) {
|
||||
pm8008_reg->parent_supply = devm_regulator_get(dev,
|
||||
reg_data[i].supply_name);
|
||||
if (IS_ERR(pm8008_reg->parent_supply)) {
|
||||
rc = PTR_ERR(pm8008_reg->parent_supply);
|
||||
if (rc != -EPROBE_DEFER)
|
||||
pr_err("%s: failed to get parent regulator rc=%d\n",
|
||||
name, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* pm8008_en should be present otherwise fail the regulator probe */
|
||||
pm8008_reg->en_supply = devm_regulator_get(dev, "pm8008_en");
|
||||
if (IS_ERR(pm8008_reg->en_supply)) {
|
||||
rc = PTR_ERR(pm8008_reg->en_supply);
|
||||
pr_err("%s: failed to get chip_en supply\n", name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
init_data = of_get_regulator_init_data(dev, reg_node,
|
||||
&pm8008_reg->rdesc);
|
||||
if (init_data == NULL) {
|
||||
pr_err("%s: failed to get regulator data\n", name);
|
||||
return -ENODATA;
|
||||
}
|
||||
if (!init_data->constraints.name) {
|
||||
pr_err("%s: regulator name missing\n", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* configure the initial voltage for the regulator */
|
||||
if (init_voltage > 0) {
|
||||
rc = pm8008_write_voltage(pm8008_reg, init_voltage,
|
||||
init_data->constraints.max_uV);
|
||||
if (rc < 0)
|
||||
pr_err("%s: failed to set initial voltage rc=%d\n",
|
||||
name, rc);
|
||||
}
|
||||
|
||||
init_data->constraints.input_uV = init_data->constraints.max_uV;
|
||||
init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_STATUS
|
||||
| REGULATOR_CHANGE_VOLTAGE
|
||||
| REGULATOR_CHANGE_MODE
|
||||
| REGULATOR_CHANGE_DRMS;
|
||||
reg_config.dev = dev;
|
||||
reg_config.init_data = init_data;
|
||||
reg_config.driver_data = pm8008_reg;
|
||||
reg_config.of_node = reg_node;
|
||||
|
||||
pm8008_reg->rdesc.type = REGULATOR_VOLTAGE;
|
||||
pm8008_reg->rdesc.ops = &pm8008_regulator_ops;
|
||||
pm8008_reg->rdesc.name = init_data->constraints.name;
|
||||
pm8008_reg->rdesc.n_voltages = 1;
|
||||
|
||||
pm8008_reg->rdev = devm_regulator_register(dev, &pm8008_reg->rdesc,
|
||||
®_config);
|
||||
if (IS_ERR(pm8008_reg->rdev)) {
|
||||
rc = PTR_ERR(pm8008_reg->rdev);
|
||||
pr_err("%s: failed to register regulator rc=%d\n",
|
||||
pm8008_reg->rdesc.name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pr_debug("%s regulator registered\n", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PMIC probe and helper function */
|
||||
static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *name;
|
||||
struct device_node *child;
|
||||
struct pm8008_regulator *pm8008_reg;
|
||||
|
||||
/* parse each subnode and register regulator for regulator child */
|
||||
for_each_available_child_of_node(dev->of_node, child) {
|
||||
pm8008_reg = devm_kzalloc(dev, sizeof(*pm8008_reg), GFP_KERNEL);
|
||||
if (!pm8008_reg)
|
||||
return -ENOMEM;
|
||||
|
||||
pm8008_reg->regmap = regmap;
|
||||
pm8008_reg->of_node = child;
|
||||
pm8008_reg->dev = dev;
|
||||
|
||||
rc = of_property_read_string(child, "regulator-name", &name);
|
||||
if (rc)
|
||||
continue;
|
||||
|
||||
rc = pm8008_register_ldo(pm8008_reg, name);
|
||||
if (rc < 0) {
|
||||
pr_err("failed to register regulator %s rc=%d\n",
|
||||
name, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8008_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!regmap) {
|
||||
pr_err("parent regmap is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = pm8008_parse_regulator(regmap, &pdev->dev);
|
||||
if (rc < 0) {
|
||||
pr_err("failed to parse device tree rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PM8008 chip enable regulator callbacks */
|
||||
static int pm8008_enable_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *chip = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
|
||||
rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG,
|
||||
CHIP_ENABLE_BIT, CHIP_ENABLE_BIT);
|
||||
if (rc < 0) {
|
||||
pm8008_err(chip, "failed to enable chip rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pm8008_debug(chip, "regulator enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8008_enable_regulator_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *chip = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
|
||||
rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG,
|
||||
CHIP_ENABLE_BIT, 0);
|
||||
if (rc < 0) {
|
||||
pm8008_err(chip, "failed to disable chip rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pm8008_debug(chip, "regulator disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8008_enable_regulator_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pm8008_regulator *chip = rdev_get_drvdata(rdev);
|
||||
int rc;
|
||||
u8 reg;
|
||||
|
||||
rc = pm8008_read(chip->regmap, MISC_CHIP_ENABLE_REG, ®, 1);
|
||||
if (rc < 0) {
|
||||
pm8008_err(chip, "failed to get chip state rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return !!(reg & CHIP_ENABLE_BIT);
|
||||
}
|
||||
|
||||
static struct regulator_ops pm8008_enable_reg_ops = {
|
||||
.enable = pm8008_enable_regulator_enable,
|
||||
.disable = pm8008_enable_regulator_disable,
|
||||
.is_enabled = pm8008_enable_regulator_is_enabled,
|
||||
};
|
||||
|
||||
static int pm8008_init_enable_regulator(struct pm8008_chip *chip)
|
||||
{
|
||||
struct regulator_config cfg = {};
|
||||
int rc = 0;
|
||||
|
||||
cfg.dev = chip->dev;
|
||||
cfg.driver_data = chip;
|
||||
|
||||
chip->rdesc.type = REGULATOR_VOLTAGE;
|
||||
chip->rdesc.ops = &pm8008_enable_reg_ops;
|
||||
chip->rdesc.of_match = "qcom,pm8008-chip-en";
|
||||
chip->rdesc.name = "qcom,pm8008-chip-en";
|
||||
|
||||
chip->rdev = devm_regulator_register(chip->dev, &chip->rdesc, &cfg);
|
||||
if (IS_ERR(chip->rdev)) {
|
||||
rc = PTR_ERR(chip->rdev);
|
||||
chip->rdev = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8008_chip_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct pm8008_chip *chip;
|
||||
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!chip->regmap) {
|
||||
pr_err("parent regmap is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
chip->dev = &pdev->dev;
|
||||
|
||||
/* Register chip enable regulator */
|
||||
rc = pm8008_init_enable_regulator(chip);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to register chip enable regulator rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pr_debug("PM8008 chip registered\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8008_chip_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pm8008_chip *chip = platform_get_drvdata(pdev);
|
||||
int rc;
|
||||
|
||||
rc = pm8008_masked_write(chip->regmap, MISC_CHIP_ENABLE_REG,
|
||||
CHIP_ENABLE_BIT, 0);
|
||||
if (rc < 0)
|
||||
pr_err("failed to disable chip rc=%d\n", rc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pm8008_regulator_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,pm8008-regulator",
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver pm8008_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "qcom,pm8008-regulator",
|
||||
.of_match_table = pm8008_regulator_match_table,
|
||||
},
|
||||
.probe = pm8008_regulator_probe,
|
||||
};
|
||||
|
||||
static const struct of_device_id pm8008_chip_match_table[] = {
|
||||
{
|
||||
.compatible = "qcom,pm8008-chip",
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver pm8008_chip_driver = {
|
||||
.driver = {
|
||||
.name = "qcom,pm8008-chip",
|
||||
.of_match_table = pm8008_chip_match_table,
|
||||
},
|
||||
.probe = pm8008_chip_probe,
|
||||
.remove = pm8008_chip_remove,
|
||||
};
|
||||
|
||||
static int __init pm8008_regulator_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = platform_driver_register(&pm8008_chip_driver);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return platform_driver_register(&pm8008_regulator_driver);
|
||||
}
|
||||
module_init(pm8008_regulator_init);
|
||||
|
||||
static void __exit pm8008_regulator_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pm8008_regulator_driver);
|
||||
platform_driver_unregister(&pm8008_chip_driver);
|
||||
}
|
||||
module_exit(pm8008_regulator_exit);
|
||||
|
||||
MODULE_DESCRIPTION("QPNP PM8008 PMIC Regulator Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Loading…
Reference in a new issue