112da5cb43
Fix the register for ramp delay of buck1 regulator. Buck1 and buck6
share the field (offset 4) in ramp delay register S2MPA01_REG_RAMP2.
The driver used the same register and field for ramp delay of buck3 and
buck1. This lead to updating of ramp delay of buck3 when setting buck1
and actually the ramp delay of buck1 was never set.
Fixes: f187927146
("regulator: Add support for S2MPA01 regulator")
Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Reviewed-by: Sachin Kamat <sachin.kamat@linaro.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
Cc: <stable@vger.kernel.org>
479 lines
12 KiB
C
479 lines
12 KiB
C
/*
|
|
* Copyright (c) 2013 Samsung Electronics Co., Ltd
|
|
* http://www.samsung.com
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <linux/bug.h>
|
|
#include <linux/err.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/regulator/of_regulator.h>
|
|
#include <linux/mfd/samsung/core.h>
|
|
#include <linux/mfd/samsung/s2mpa01.h>
|
|
|
|
#define S2MPA01_REGULATOR_CNT ARRAY_SIZE(regulators)
|
|
|
|
struct s2mpa01_info {
|
|
int ramp_delay24;
|
|
int ramp_delay3;
|
|
int ramp_delay5;
|
|
int ramp_delay16;
|
|
int ramp_delay7;
|
|
int ramp_delay8910;
|
|
};
|
|
|
|
static int get_ramp_delay(int ramp_delay)
|
|
{
|
|
unsigned char cnt = 0;
|
|
|
|
ramp_delay /= 6250;
|
|
|
|
while (true) {
|
|
ramp_delay = ramp_delay >> 1;
|
|
if (ramp_delay == 0)
|
|
break;
|
|
cnt++;
|
|
}
|
|
|
|
if (cnt > 3)
|
|
cnt = 3;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
static int s2mpa01_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
|
|
unsigned int old_selector,
|
|
unsigned int new_selector)
|
|
{
|
|
struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev);
|
|
unsigned int ramp_delay = 0;
|
|
int old_volt, new_volt;
|
|
|
|
switch (rdev->desc->id) {
|
|
case S2MPA01_BUCK2:
|
|
case S2MPA01_BUCK4:
|
|
ramp_delay = s2mpa01->ramp_delay24;
|
|
break;
|
|
case S2MPA01_BUCK3:
|
|
ramp_delay = s2mpa01->ramp_delay3;
|
|
break;
|
|
case S2MPA01_BUCK5:
|
|
ramp_delay = s2mpa01->ramp_delay5;
|
|
break;
|
|
case S2MPA01_BUCK1:
|
|
case S2MPA01_BUCK6:
|
|
ramp_delay = s2mpa01->ramp_delay16;
|
|
break;
|
|
case S2MPA01_BUCK7:
|
|
ramp_delay = s2mpa01->ramp_delay7;
|
|
break;
|
|
case S2MPA01_BUCK8:
|
|
case S2MPA01_BUCK9:
|
|
case S2MPA01_BUCK10:
|
|
ramp_delay = s2mpa01->ramp_delay8910;
|
|
break;
|
|
}
|
|
|
|
if (ramp_delay == 0)
|
|
ramp_delay = rdev->desc->ramp_delay;
|
|
|
|
old_volt = rdev->desc->min_uV + (rdev->desc->uV_step * old_selector);
|
|
new_volt = rdev->desc->min_uV + (rdev->desc->uV_step * new_selector);
|
|
|
|
return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay);
|
|
}
|
|
|
|
static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
|
|
{
|
|
struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev);
|
|
unsigned int ramp_val, ramp_shift, ramp_reg = S2MPA01_REG_RAMP2;
|
|
unsigned int ramp_enable = 1, enable_shift = 0;
|
|
int ret;
|
|
|
|
switch (rdev->desc->id) {
|
|
case S2MPA01_BUCK1:
|
|
enable_shift = S2MPA01_BUCK1_RAMP_EN_SHIFT;
|
|
if (!ramp_delay) {
|
|
ramp_enable = 0;
|
|
break;
|
|
}
|
|
|
|
if (ramp_delay > s2mpa01->ramp_delay16)
|
|
s2mpa01->ramp_delay16 = ramp_delay;
|
|
else
|
|
ramp_delay = s2mpa01->ramp_delay16;
|
|
|
|
ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT;
|
|
break;
|
|
case S2MPA01_BUCK2:
|
|
enable_shift = S2MPA01_BUCK2_RAMP_EN_SHIFT;
|
|
if (!ramp_delay) {
|
|
ramp_enable = 0;
|
|
break;
|
|
}
|
|
|
|
if (ramp_delay > s2mpa01->ramp_delay24)
|
|
s2mpa01->ramp_delay24 = ramp_delay;
|
|
else
|
|
ramp_delay = s2mpa01->ramp_delay24;
|
|
|
|
ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT;
|
|
ramp_reg = S2MPA01_REG_RAMP1;
|
|
break;
|
|
case S2MPA01_BUCK3:
|
|
enable_shift = S2MPA01_BUCK3_RAMP_EN_SHIFT;
|
|
if (!ramp_delay) {
|
|
ramp_enable = 0;
|
|
break;
|
|
}
|
|
|
|
s2mpa01->ramp_delay3 = ramp_delay;
|
|
ramp_shift = S2MPA01_BUCK3_RAMP_SHIFT;
|
|
ramp_reg = S2MPA01_REG_RAMP1;
|
|
break;
|
|
case S2MPA01_BUCK4:
|
|
enable_shift = S2MPA01_BUCK4_RAMP_EN_SHIFT;
|
|
if (!ramp_delay) {
|
|
ramp_enable = 0;
|
|
break;
|
|
}
|
|
|
|
if (ramp_delay > s2mpa01->ramp_delay24)
|
|
s2mpa01->ramp_delay24 = ramp_delay;
|
|
else
|
|
ramp_delay = s2mpa01->ramp_delay24;
|
|
|
|
ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT;
|
|
ramp_reg = S2MPA01_REG_RAMP1;
|
|
break;
|
|
case S2MPA01_BUCK5:
|
|
s2mpa01->ramp_delay5 = ramp_delay;
|
|
ramp_shift = S2MPA01_BUCK5_RAMP_SHIFT;
|
|
break;
|
|
case S2MPA01_BUCK6:
|
|
if (ramp_delay > s2mpa01->ramp_delay16)
|
|
s2mpa01->ramp_delay16 = ramp_delay;
|
|
else
|
|
ramp_delay = s2mpa01->ramp_delay16;
|
|
|
|
ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT;
|
|
break;
|
|
case S2MPA01_BUCK7:
|
|
s2mpa01->ramp_delay7 = ramp_delay;
|
|
ramp_shift = S2MPA01_BUCK7_RAMP_SHIFT;
|
|
break;
|
|
case S2MPA01_BUCK8:
|
|
case S2MPA01_BUCK9:
|
|
case S2MPA01_BUCK10:
|
|
if (ramp_delay > s2mpa01->ramp_delay8910)
|
|
s2mpa01->ramp_delay8910 = ramp_delay;
|
|
else
|
|
ramp_delay = s2mpa01->ramp_delay8910;
|
|
|
|
ramp_shift = S2MPA01_BUCK8910_RAMP_SHIFT;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if (!ramp_enable)
|
|
goto ramp_disable;
|
|
|
|
ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1,
|
|
1 << enable_shift, 1 << enable_shift);
|
|
if (ret) {
|
|
dev_err(&rdev->dev, "failed to enable ramp rate\n");
|
|
return ret;
|
|
}
|
|
|
|
ramp_val = get_ramp_delay(ramp_delay);
|
|
|
|
return regmap_update_bits(rdev->regmap, ramp_reg, 0x3 << ramp_shift,
|
|
ramp_val << ramp_shift);
|
|
|
|
ramp_disable:
|
|
return regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1,
|
|
1 << enable_shift, 0);
|
|
}
|
|
|
|
static struct regulator_ops s2mpa01_ldo_ops = {
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
|
};
|
|
|
|
static struct regulator_ops s2mpa01_buck_ops = {
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.set_voltage_time_sel = s2mpa01_regulator_set_voltage_time_sel,
|
|
.set_ramp_delay = s2mpa01_set_ramp_delay,
|
|
};
|
|
|
|
#define regulator_desc_ldo1(num) { \
|
|
.name = "LDO"#num, \
|
|
.id = S2MPA01_LDO##num, \
|
|
.ops = &s2mpa01_ldo_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_LDO_MIN, \
|
|
.uV_step = S2MPA01_LDO_STEP1, \
|
|
.n_voltages = S2MPA01_LDO_N_VOLTAGES, \
|
|
.vsel_reg = S2MPA01_REG_L1CTRL + num - 1, \
|
|
.vsel_mask = S2MPA01_LDO_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_L1CTRL + num - 1, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
#define regulator_desc_ldo2(num) { \
|
|
.name = "LDO"#num, \
|
|
.id = S2MPA01_LDO##num, \
|
|
.ops = &s2mpa01_ldo_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_LDO_MIN, \
|
|
.uV_step = S2MPA01_LDO_STEP2, \
|
|
.n_voltages = S2MPA01_LDO_N_VOLTAGES, \
|
|
.vsel_reg = S2MPA01_REG_L1CTRL + num - 1, \
|
|
.vsel_mask = S2MPA01_LDO_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_L1CTRL + num - 1, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
|
|
#define regulator_desc_buck1_4(num) { \
|
|
.name = "BUCK"#num, \
|
|
.id = S2MPA01_BUCK##num, \
|
|
.ops = &s2mpa01_buck_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_BUCK_MIN1, \
|
|
.uV_step = S2MPA01_BUCK_STEP1, \
|
|
.n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
|
|
.ramp_delay = S2MPA01_RAMP_DELAY, \
|
|
.vsel_reg = S2MPA01_REG_B1CTRL2 + (num - 1) * 2, \
|
|
.vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_B1CTRL1 + (num - 1) * 2, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
|
|
#define regulator_desc_buck5 { \
|
|
.name = "BUCK5", \
|
|
.id = S2MPA01_BUCK5, \
|
|
.ops = &s2mpa01_buck_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_BUCK_MIN2, \
|
|
.uV_step = S2MPA01_BUCK_STEP1, \
|
|
.n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
|
|
.ramp_delay = S2MPA01_RAMP_DELAY, \
|
|
.vsel_reg = S2MPA01_REG_B5CTRL2, \
|
|
.vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_B5CTRL1, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
|
|
#define regulator_desc_buck6_7(num) { \
|
|
.name = "BUCK"#num, \
|
|
.id = S2MPA01_BUCK##num, \
|
|
.ops = &s2mpa01_buck_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_BUCK_MIN1, \
|
|
.uV_step = S2MPA01_BUCK_STEP1, \
|
|
.n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
|
|
.ramp_delay = S2MPA01_RAMP_DELAY, \
|
|
.vsel_reg = S2MPA01_REG_B6CTRL2 + (num - 6) * 2, \
|
|
.vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_B6CTRL1 + (num - 6) * 2, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
|
|
#define regulator_desc_buck8 { \
|
|
.name = "BUCK8", \
|
|
.id = S2MPA01_BUCK8, \
|
|
.ops = &s2mpa01_buck_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_BUCK_MIN2, \
|
|
.uV_step = S2MPA01_BUCK_STEP2, \
|
|
.n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
|
|
.ramp_delay = S2MPA01_RAMP_DELAY, \
|
|
.vsel_reg = S2MPA01_REG_B8CTRL2, \
|
|
.vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_B8CTRL1, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
|
|
#define regulator_desc_buck9 { \
|
|
.name = "BUCK9", \
|
|
.id = S2MPA01_BUCK9, \
|
|
.ops = &s2mpa01_buck_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_BUCK_MIN4, \
|
|
.uV_step = S2MPA01_BUCK_STEP2, \
|
|
.n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
|
|
.ramp_delay = S2MPA01_RAMP_DELAY, \
|
|
.vsel_reg = S2MPA01_REG_B9CTRL2, \
|
|
.vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_B9CTRL1, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
|
|
#define regulator_desc_buck10 { \
|
|
.name = "BUCK10", \
|
|
.id = S2MPA01_BUCK10, \
|
|
.ops = &s2mpa01_buck_ops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.owner = THIS_MODULE, \
|
|
.min_uV = S2MPA01_BUCK_MIN3, \
|
|
.uV_step = S2MPA01_BUCK_STEP2, \
|
|
.n_voltages = S2MPA01_BUCK_N_VOLTAGES, \
|
|
.ramp_delay = S2MPA01_RAMP_DELAY, \
|
|
.vsel_reg = S2MPA01_REG_B10CTRL2, \
|
|
.vsel_mask = S2MPA01_BUCK_VSEL_MASK, \
|
|
.enable_reg = S2MPA01_REG_B10CTRL1, \
|
|
.enable_mask = S2MPA01_ENABLE_MASK \
|
|
}
|
|
|
|
static struct regulator_desc regulators[] = {
|
|
regulator_desc_ldo2(1),
|
|
regulator_desc_ldo1(2),
|
|
regulator_desc_ldo1(3),
|
|
regulator_desc_ldo1(4),
|
|
regulator_desc_ldo1(5),
|
|
regulator_desc_ldo2(6),
|
|
regulator_desc_ldo1(7),
|
|
regulator_desc_ldo1(8),
|
|
regulator_desc_ldo1(9),
|
|
regulator_desc_ldo1(10),
|
|
regulator_desc_ldo2(11),
|
|
regulator_desc_ldo1(12),
|
|
regulator_desc_ldo1(13),
|
|
regulator_desc_ldo1(14),
|
|
regulator_desc_ldo1(15),
|
|
regulator_desc_ldo1(16),
|
|
regulator_desc_ldo1(17),
|
|
regulator_desc_ldo1(18),
|
|
regulator_desc_ldo1(19),
|
|
regulator_desc_ldo1(20),
|
|
regulator_desc_ldo1(21),
|
|
regulator_desc_ldo2(22),
|
|
regulator_desc_ldo2(23),
|
|
regulator_desc_ldo1(24),
|
|
regulator_desc_ldo1(25),
|
|
regulator_desc_ldo1(26),
|
|
regulator_desc_buck1_4(1),
|
|
regulator_desc_buck1_4(2),
|
|
regulator_desc_buck1_4(3),
|
|
regulator_desc_buck1_4(4),
|
|
regulator_desc_buck5,
|
|
regulator_desc_buck6_7(6),
|
|
regulator_desc_buck6_7(7),
|
|
regulator_desc_buck8,
|
|
regulator_desc_buck9,
|
|
regulator_desc_buck10,
|
|
};
|
|
|
|
static int s2mpa01_pmic_probe(struct platform_device *pdev)
|
|
{
|
|
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
|
|
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
|
|
struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX];
|
|
struct device_node *reg_np = NULL;
|
|
struct regulator_config config = { };
|
|
struct s2mpa01_info *s2mpa01;
|
|
int i;
|
|
|
|
s2mpa01 = devm_kzalloc(&pdev->dev, sizeof(*s2mpa01), GFP_KERNEL);
|
|
if (!s2mpa01)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < S2MPA01_REGULATOR_CNT; i++)
|
|
rdata[i].name = regulators[i].name;
|
|
|
|
if (iodev->dev->of_node) {
|
|
reg_np = of_get_child_by_name(iodev->dev->of_node,
|
|
"regulators");
|
|
if (!reg_np) {
|
|
dev_err(&pdev->dev,
|
|
"could not find regulators sub-node\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
of_regulator_match(&pdev->dev, reg_np, rdata,
|
|
S2MPA01_REGULATOR_MAX);
|
|
of_node_put(reg_np);
|
|
}
|
|
|
|
platform_set_drvdata(pdev, s2mpa01);
|
|
|
|
config.dev = &pdev->dev;
|
|
config.regmap = iodev->regmap_pmic;
|
|
config.driver_data = s2mpa01;
|
|
|
|
for (i = 0; i < S2MPA01_REGULATOR_MAX; i++) {
|
|
struct regulator_dev *rdev;
|
|
if (pdata)
|
|
config.init_data = pdata->regulators[i].initdata;
|
|
else
|
|
config.init_data = rdata[i].init_data;
|
|
|
|
if (reg_np)
|
|
config.of_node = rdata[i].of_node;
|
|
|
|
rdev = devm_regulator_register(&pdev->dev,
|
|
®ulators[i], &config);
|
|
if (IS_ERR(rdev)) {
|
|
dev_err(&pdev->dev, "regulator init failed for %d\n",
|
|
i);
|
|
return PTR_ERR(rdev);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct platform_device_id s2mpa01_pmic_id[] = {
|
|
{ "s2mpa01-pmic", 0},
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(platform, s2mpa01_pmic_id);
|
|
|
|
static struct platform_driver s2mpa01_pmic_driver = {
|
|
.driver = {
|
|
.name = "s2mpa01-pmic",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = s2mpa01_pmic_probe,
|
|
.id_table = s2mpa01_pmic_id,
|
|
};
|
|
|
|
module_platform_driver(s2mpa01_pmic_driver);
|
|
|
|
/* Module information */
|
|
MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
|
|
MODULE_AUTHOR("Sachin Kamat <sachin.kamat@samsung.com>");
|
|
MODULE_DESCRIPTION("SAMSUNG S2MPA01 Regulator Driver");
|
|
MODULE_LICENSE("GPL");
|