9b1111fa80
A collection of fixes for the regulator API that have come up since the merge window, including a big batch of fixes from Axel Lin's usual careful and detailed review. The one stand out fix here is Dmitry Baryshkov's fix for an issue where we fail to power on the parents of always on regulators during system startup if they weren't already powered on. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmC/ao0ACgkQJNaLcl1U h9AoqQf9GYtljFSON07MAOWwMgnjuMQ+rl0ZadqKKzq74QMMi4bxVKDWkftQ28/5 Ulk2M/mxRE6C1OEpOJl9ZnG9K0fWpOdnTURkYgW0FsJniEDiF7ZkdoFypwu93jOD 0r+3QCw/Ti9i08pOdlpFpUKU5rp/O9HYmouOTzBOCiM1SMb9TkkX5GBoDVw8+cWd 2PZqKQXEsaK1uNzeaXYw6UO8+IdSpVQRzSEILdtVWyHCNmXDDJWfI3vYeeqDbhYr C+E3UdrO1ftNsOoJv33NqhscMnulkZDZ6H6lgbLX2FyFAe2M9N+AildGlkR5H1GD xH3q3EvkOE2Y2X3zHteLdJ3MoI91cQ== =poWJ -----END PGP SIGNATURE----- Merge tag 'regulator-fix-v5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator Pull regulator fixes from Mark Brown: "A collection of fixes for the regulator API that have come up since the merge window, including a big batch of fixes from Axel Lin's usual careful and detailed review. The one stand out fix here is Dmitry Baryshkov's fix for an issue where we fail to power on the parents of always on regulators during system startup if they weren't already powered on" * tag 'regulator-fix-v5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (21 commits) regulator: rt4801: Fix NULL pointer dereference if priv->enable_gpios is NULL regulator: hi6421v600: Fix .vsel_mask setting regulator: bd718x7: Fix the BUCK7 voltage setting on BD71837 regulator: atc260x: Fix n_voltages and min_sel for pickable linear ranges regulator: rtmv20: Fix to make regcache value first reading back from HW regulator: mt6315: Fix function prototype for mt6315_map_mode regulator: rtmv20: Add Richtek to Kconfig text regulator: rtmv20: Fix .set_current_limit/.get_current_limit callbacks regulator: hisilicon: use the correct HiSilicon copyright regulator: bd71828: Fix .n_voltages settings regulator: bd70528: Fix off-by-one for buck123 .n_voltages setting regulator: max77620: Silence deferred probe error regulator: max77620: Use device_set_of_node_from_dev() regulator: scmi: Fix off-by-one for linear regulators .n_voltages setting regulator: core: resolve supply for boot-on/always-on regulators regulator: fixed: Ensure enable_counter is correct if reg_domain_disable fails regulator: Check ramp_delay_table for regulator_set_ramp_delay_regmap regulator: fan53880: Fix missing n_voltages setting regulator: da9121: Return REGULATOR_MODE_INVALID for invalid mode regulator: fan53555: fix TCS4525 voltage calulation ...
300 lines
7.9 KiB
C
300 lines
7.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// Device driver for regulators in Hisi IC
|
|
//
|
|
// Copyright (c) 2013 Linaro Ltd.
|
|
// Copyright (c) 2011 HiSilicon Ltd.
|
|
// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
|
|
//
|
|
// Guodong Xu <guodong.xu@linaro.org>
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/mfd/hi6421-spmi-pmic.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/spmi.h>
|
|
|
|
struct hi6421_spmi_reg_info {
|
|
struct regulator_desc desc;
|
|
struct hi6421_spmi_pmic *pmic;
|
|
u8 eco_mode_mask;
|
|
u32 eco_uA;
|
|
|
|
/* Serialize regulator enable logic */
|
|
struct mutex enable_mutex;
|
|
};
|
|
|
|
static const unsigned int ldo3_voltages[] = {
|
|
1500000, 1550000, 1600000, 1650000,
|
|
1700000, 1725000, 1750000, 1775000,
|
|
1800000, 1825000, 1850000, 1875000,
|
|
1900000, 1925000, 1950000, 2000000
|
|
};
|
|
|
|
static const unsigned int ldo4_voltages[] = {
|
|
1725000, 1750000, 1775000, 1800000,
|
|
1825000, 1850000, 1875000, 1900000
|
|
};
|
|
|
|
static const unsigned int ldo9_voltages[] = {
|
|
1750000, 1800000, 1825000, 2800000,
|
|
2850000, 2950000, 3000000, 3300000
|
|
};
|
|
|
|
static const unsigned int ldo15_voltages[] = {
|
|
1800000, 1850000, 2400000, 2600000,
|
|
2700000, 2850000, 2950000, 3000000
|
|
};
|
|
|
|
static const unsigned int ldo17_voltages[] = {
|
|
2500000, 2600000, 2700000, 2800000,
|
|
3000000, 3100000, 3200000, 3300000
|
|
};
|
|
|
|
static const unsigned int ldo34_voltages[] = {
|
|
2600000, 2700000, 2800000, 2900000,
|
|
3000000, 3100000, 3200000, 3300000
|
|
};
|
|
|
|
/**
|
|
* HI6421V600_LDO() - specify a LDO power line
|
|
* @_id: LDO id name string
|
|
* @vtable: voltage table
|
|
* @ereg: enable register
|
|
* @emask: enable mask
|
|
* @vreg: voltage select register
|
|
* @odelay: off/on delay time in uS
|
|
* @etime: enable time in uS
|
|
* @ecomask: eco mode mask
|
|
* @ecoamp: eco mode load uppler limit in uA
|
|
*/
|
|
#define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \
|
|
odelay, etime, ecomask, ecoamp) \
|
|
[HI6421V600_##_id] = { \
|
|
.desc = { \
|
|
.name = #_id, \
|
|
.of_match = of_match_ptr(#_id), \
|
|
.regulators_node = of_match_ptr("regulators"), \
|
|
.ops = &hi6421_spmi_ldo_rops, \
|
|
.type = REGULATOR_VOLTAGE, \
|
|
.id = HI6421V600_##_id, \
|
|
.owner = THIS_MODULE, \
|
|
.volt_table = vtable, \
|
|
.n_voltages = ARRAY_SIZE(vtable), \
|
|
.vsel_mask = ARRAY_SIZE(vtable) - 1, \
|
|
.vsel_reg = vreg, \
|
|
.enable_reg = ereg, \
|
|
.enable_mask = emask, \
|
|
.enable_time = etime, \
|
|
.ramp_delay = etime, \
|
|
.off_on_delay = odelay, \
|
|
}, \
|
|
.eco_mode_mask = ecomask, \
|
|
.eco_uA = ecoamp, \
|
|
}
|
|
|
|
static int hi6421_spmi_regulator_enable(struct regulator_dev *rdev)
|
|
{
|
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
|
int ret;
|
|
|
|
/* cannot enable more than one regulator at one time */
|
|
mutex_lock(&sreg->enable_mutex);
|
|
|
|
ret = regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
|
rdev->desc->enable_mask,
|
|
rdev->desc->enable_mask);
|
|
|
|
/* Avoid powering up multiple devices at the same time */
|
|
usleep_range(rdev->desc->off_on_delay, rdev->desc->off_on_delay + 60);
|
|
|
|
mutex_unlock(&sreg->enable_mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hi6421_spmi_regulator_disable(struct regulator_dev *rdev)
|
|
{
|
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
|
|
|
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
|
rdev->desc->enable_mask, 0);
|
|
}
|
|
|
|
static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev)
|
|
{
|
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
|
u32 reg_val;
|
|
|
|
regmap_read(pmic->regmap, rdev->desc->enable_reg, ®_val);
|
|
|
|
if (reg_val & sreg->eco_mode_mask)
|
|
return REGULATOR_MODE_IDLE;
|
|
|
|
return REGULATOR_MODE_NORMAL;
|
|
}
|
|
|
|
static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev,
|
|
unsigned int mode)
|
|
{
|
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
|
struct hi6421_spmi_pmic *pmic = sreg->pmic;
|
|
u32 val;
|
|
|
|
switch (mode) {
|
|
case REGULATOR_MODE_NORMAL:
|
|
val = 0;
|
|
break;
|
|
case REGULATOR_MODE_IDLE:
|
|
val = sreg->eco_mode_mask << (ffs(sreg->eco_mode_mask) - 1);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return regmap_update_bits(pmic->regmap, rdev->desc->enable_reg,
|
|
sreg->eco_mode_mask, val);
|
|
}
|
|
|
|
static unsigned int
|
|
hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev,
|
|
int input_uV, int output_uV,
|
|
int load_uA)
|
|
{
|
|
struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev);
|
|
|
|
if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA))
|
|
return REGULATOR_MODE_NORMAL;
|
|
|
|
return REGULATOR_MODE_IDLE;
|
|
}
|
|
|
|
static const struct regulator_ops hi6421_spmi_ldo_rops = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = hi6421_spmi_regulator_enable,
|
|
.disable = hi6421_spmi_regulator_disable,
|
|
.list_voltage = regulator_list_voltage_table,
|
|
.map_voltage = regulator_map_voltage_iterate,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.get_mode = hi6421_spmi_regulator_get_mode,
|
|
.set_mode = hi6421_spmi_regulator_set_mode,
|
|
.get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode,
|
|
};
|
|
|
|
/* HI6421v600 regulators with known registers */
|
|
enum hi6421_spmi_regulator_id {
|
|
HI6421V600_LDO3,
|
|
HI6421V600_LDO4,
|
|
HI6421V600_LDO9,
|
|
HI6421V600_LDO15,
|
|
HI6421V600_LDO16,
|
|
HI6421V600_LDO17,
|
|
HI6421V600_LDO33,
|
|
HI6421V600_LDO34,
|
|
};
|
|
|
|
static struct hi6421_spmi_reg_info regulator_info[] = {
|
|
HI6421V600_LDO(LDO3, ldo3_voltages,
|
|
0x16, 0x01, 0x51,
|
|
20000, 120,
|
|
0, 0),
|
|
HI6421V600_LDO(LDO4, ldo4_voltages,
|
|
0x17, 0x01, 0x52,
|
|
20000, 120,
|
|
0x10, 10000),
|
|
HI6421V600_LDO(LDO9, ldo9_voltages,
|
|
0x1c, 0x01, 0x57,
|
|
20000, 360,
|
|
0x10, 10000),
|
|
HI6421V600_LDO(LDO15, ldo15_voltages,
|
|
0x21, 0x01, 0x5c,
|
|
20000, 360,
|
|
0x10, 10000),
|
|
HI6421V600_LDO(LDO16, ldo15_voltages,
|
|
0x22, 0x01, 0x5d,
|
|
20000, 360,
|
|
0x10, 10000),
|
|
HI6421V600_LDO(LDO17, ldo17_voltages,
|
|
0x23, 0x01, 0x5e,
|
|
20000, 120,
|
|
0x10, 10000),
|
|
HI6421V600_LDO(LDO33, ldo17_voltages,
|
|
0x32, 0x01, 0x6d,
|
|
20000, 120,
|
|
0, 0),
|
|
HI6421V600_LDO(LDO34, ldo34_voltages,
|
|
0x33, 0x01, 0x6e,
|
|
20000, 120,
|
|
0, 0),
|
|
};
|
|
|
|
static int hi6421_spmi_regulator_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *pmic_dev = pdev->dev.parent;
|
|
struct regulator_config config = { };
|
|
struct hi6421_spmi_reg_info *sreg;
|
|
struct hi6421_spmi_reg_info *info;
|
|
struct device *dev = &pdev->dev;
|
|
struct hi6421_spmi_pmic *pmic;
|
|
struct regulator_dev *rdev;
|
|
int i;
|
|
|
|
/*
|
|
* This driver is meant to be called by hi6421-spmi-core,
|
|
* which should first set drvdata. If this doesn't happen, hit
|
|
* a warn on and return.
|
|
*/
|
|
pmic = dev_get_drvdata(pmic_dev);
|
|
if (WARN_ON(!pmic))
|
|
return -ENODEV;
|
|
|
|
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
|
|
if (!sreg)
|
|
return -ENOMEM;
|
|
|
|
sreg->pmic = pmic;
|
|
mutex_init(&sreg->enable_mutex);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(regulator_info); i++) {
|
|
info = ®ulator_info[i];
|
|
|
|
config.dev = pdev->dev.parent;
|
|
config.driver_data = sreg;
|
|
config.regmap = pmic->regmap;
|
|
|
|
rdev = devm_regulator_register(dev, &info->desc, &config);
|
|
if (IS_ERR(rdev)) {
|
|
dev_err(dev, "failed to register %s\n",
|
|
info->desc.name);
|
|
return PTR_ERR(rdev);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct platform_device_id hi6421_spmi_regulator_table[] = {
|
|
{ .name = "hi6421v600-regulator" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(platform, hi6421_spmi_regulator_table);
|
|
|
|
static struct platform_driver hi6421_spmi_regulator_driver = {
|
|
.id_table = hi6421_spmi_regulator_table,
|
|
.driver = {
|
|
.name = "hi6421v600-regulator",
|
|
},
|
|
.probe = hi6421_spmi_regulator_probe,
|
|
};
|
|
module_platform_driver(hi6421_spmi_regulator_driver);
|
|
|
|
MODULE_DESCRIPTION("Hi6421v600 SPMI regulator driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
|