root/drivers/regulator/rohm-regulator.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 voltage is set to 0 => disable */
        if (uv == 0) {
                if (omask)
                        return regmap_update_bits(regmap, oreg, omask, 0);
        }
        /* 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;
        }
        for (i = 0; i < desc->n_voltages; i++) {
                /* NOTE to next hacker - Does not support pickable ranges */
                if (desc->linear_range_selectors_bitfield)
                        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);
                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;
                }
        }
        if (i == desc->n_voltages)
                pr_warn("Unsupported %s voltage %u\n", prop, uv);

        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;
                        case ROHM_DVS_LEVEL_SNVS:
                                prop = "rohm,dvs-snvs-voltage";
                                reg = dvs->snvs_reg;
                                mask = dvs->snvs_mask;
                                omask = dvs->snvs_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);

/*
 * Few ROHM PMIC ICs have constrains on voltage changing:
 * BD71837 - only buck 1-4 voltages can be changed when they are enabled.
 * Other bucks and all LDOs must be disabled when voltage is changed.
 * BD96801 - LDO voltage levels can be changed when LDOs are disabled.
 */
int rohm_regulator_set_voltage_sel_restricted(struct regulator_dev *rdev,
                                              unsigned int sel)
{
        if (rdev->desc->ops->is_enabled(rdev))
                return -EBUSY;

        return regulator_set_voltage_sel_regmap(rdev, sel);
}
EXPORT_SYMBOL_GPL(rohm_regulator_set_voltage_sel_restricted);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");