2020-01-20 16:44:45 +03:00
|
|
|
// 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;
|
|
|
|
}
|
2021-04-05 14:43:26 +03:00
|
|
|
/* If voltage is set to 0 => disable */
|
2020-01-20 16:44:45 +03:00
|
|
|
if (uv == 0) {
|
|
|
|
if (omask)
|
|
|
|
return regmap_update_bits(regmap, oreg, omask, 0);
|
|
|
|
}
|
2021-04-05 14:43:26 +03:00
|
|
|
/* Some setups don't allow setting own voltage but do allow enabling */
|
|
|
|
if (!mask) {
|
|
|
|
if (omask)
|
|
|
|
return regmap_update_bits(regmap, oreg, omask, omask);
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-01-20 16:44:45 +03:00
|
|
|
for (i = 0; i < desc->n_voltages; i++) {
|
2021-04-05 14:43:26 +03:00
|
|
|
/* NOTE to next hacker - Does not support pickable ranges */
|
|
|
|
if (desc->linear_range_selectors)
|
|
|
|
return -EINVAL;
|
|
|
|
if (desc->n_linear_ranges)
|
|
|
|
ret = regulator_desc_list_voltage_linear_range(desc, i);
|
|
|
|
else
|
|
|
|
ret = regulator_desc_list_voltage_linear(desc, i);
|
2020-01-20 16:44:45 +03:00
|
|
|
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;
|
|
|
|
|
2021-02-12 11:00:23 +03:00
|
|
|
for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) {
|
|
|
|
int bit;
|
|
|
|
|
|
|
|
bit = BIT(i);
|
|
|
|
if (dvs->level_map & bit) {
|
|
|
|
switch (bit) {
|
2020-01-20 16:44:45 +03:00
|
|
|
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;
|
2021-04-05 14:43:44 +03:00
|
|
|
case ROHM_DVS_LEVEL_SNVS:
|
|
|
|
prop = "rohm,dvs-snvs-voltage";
|
|
|
|
reg = dvs->snvs_reg;
|
|
|
|
mask = dvs->snvs_mask;
|
|
|
|
omask = dvs->snvs_on_mask;
|
|
|
|
break;
|
2020-01-20 16:44:45 +03:00
|
|
|
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");
|