c294554111
The ROHM BD718x7 and BD71828 drivers support setting HW state specific voltages from device-tree. This is used also by various in-tree DTS files. These drivers do incorrectly try to compose bit-map using enum values. By a chance this works for first two valid levels having values 1 and 2 - but setting values for the rest of the levels do indicate capability of setting values for first levels as well. Luckily the regulators which support setting values for SUSPEND/LPSR do usually also support setting values for RUN and IDLE too - thus this has not been such a fatal issue. Fix this by defining the old enum values as bits and fixing the parsing code. This allows keeping existing IC specific drivers intact and only slightly changing the rohm-regulator.c Fixes: 21b72156ede8b ("regulator: bd718x7: Split driver to common and bd718x7 specific parts") Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> Acked-by: Lee Jones <lee.jones@linaro.org> Link: https://lore.kernel.org/r/20210212080023.GA880728@localhost.localdomain Signed-off-by: Mark Brown <broonie@kernel.org>
99 lines
2.4 KiB
C
99 lines
2.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2020 ROHM Semiconductors
|
|
|
|
#include <linux/errno.h>
|
|
#include <linux/mfd/rohm-generic.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/regulator/driver.h>
|
|
|
|
static int set_dvs_level(const struct regulator_desc *desc,
|
|
struct device_node *np, struct regmap *regmap,
|
|
char *prop, unsigned int reg, unsigned int mask,
|
|
unsigned int omask, unsigned int oreg)
|
|
{
|
|
int ret, i;
|
|
uint32_t uv;
|
|
|
|
ret = of_property_read_u32(np, prop, &uv);
|
|
if (ret) {
|
|
if (ret != -EINVAL)
|
|
return ret;
|
|
return 0;
|
|
}
|
|
|
|
if (uv == 0) {
|
|
if (omask)
|
|
return regmap_update_bits(regmap, oreg, omask, 0);
|
|
}
|
|
for (i = 0; i < desc->n_voltages; i++) {
|
|
ret = regulator_desc_list_voltage_linear_range(desc, i);
|
|
if (ret < 0)
|
|
continue;
|
|
if (ret == uv) {
|
|
i <<= ffs(desc->vsel_mask) - 1;
|
|
ret = regmap_update_bits(regmap, reg, mask, i);
|
|
if (omask && !ret)
|
|
ret = regmap_update_bits(regmap, oreg, omask,
|
|
omask);
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
|
|
struct device_node *np,
|
|
const struct regulator_desc *desc,
|
|
struct regmap *regmap)
|
|
{
|
|
int i, ret = 0;
|
|
char *prop;
|
|
unsigned int reg, mask, omask, oreg = desc->enable_reg;
|
|
|
|
for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) {
|
|
int bit;
|
|
|
|
bit = BIT(i);
|
|
if (dvs->level_map & bit) {
|
|
switch (bit) {
|
|
case ROHM_DVS_LEVEL_RUN:
|
|
prop = "rohm,dvs-run-voltage";
|
|
reg = dvs->run_reg;
|
|
mask = dvs->run_mask;
|
|
omask = dvs->run_on_mask;
|
|
break;
|
|
case ROHM_DVS_LEVEL_IDLE:
|
|
prop = "rohm,dvs-idle-voltage";
|
|
reg = dvs->idle_reg;
|
|
mask = dvs->idle_mask;
|
|
omask = dvs->idle_on_mask;
|
|
break;
|
|
case ROHM_DVS_LEVEL_SUSPEND:
|
|
prop = "rohm,dvs-suspend-voltage";
|
|
reg = dvs->suspend_reg;
|
|
mask = dvs->suspend_mask;
|
|
omask = dvs->suspend_on_mask;
|
|
break;
|
|
case ROHM_DVS_LEVEL_LPSR:
|
|
prop = "rohm,dvs-lpsr-voltage";
|
|
reg = dvs->lpsr_reg;
|
|
mask = dvs->lpsr_mask;
|
|
omask = dvs->lpsr_on_mask;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
ret = set_dvs_level(desc, np, regmap, prop, reg, mask,
|
|
omask, oreg);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(rohm_regulator_set_dvs_levels);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
|
|
MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");
|