162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (C) 2020 ROHM Semiconductors
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/errno.h>
562306a36Sopenharmony_ci#include <linux/mfd/rohm-generic.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/of.h>
862306a36Sopenharmony_ci#include <linux/regmap.h>
962306a36Sopenharmony_ci#include <linux/regulator/driver.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic int set_dvs_level(const struct regulator_desc *desc,
1262306a36Sopenharmony_ci			 struct device_node *np, struct regmap *regmap,
1362306a36Sopenharmony_ci			 char *prop, unsigned int reg, unsigned int mask,
1462306a36Sopenharmony_ci			 unsigned int omask, unsigned int oreg)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	int ret, i;
1762306a36Sopenharmony_ci	uint32_t uv;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	ret = of_property_read_u32(np, prop, &uv);
2062306a36Sopenharmony_ci	if (ret) {
2162306a36Sopenharmony_ci		if (ret != -EINVAL)
2262306a36Sopenharmony_ci			return ret;
2362306a36Sopenharmony_ci		return 0;
2462306a36Sopenharmony_ci	}
2562306a36Sopenharmony_ci	/* If voltage is set to 0 => disable */
2662306a36Sopenharmony_ci	if (uv == 0) {
2762306a36Sopenharmony_ci		if (omask)
2862306a36Sopenharmony_ci			return regmap_update_bits(regmap, oreg, omask, 0);
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci	/* Some setups don't allow setting own voltage but do allow enabling */
3162306a36Sopenharmony_ci	if (!mask) {
3262306a36Sopenharmony_ci		if (omask)
3362306a36Sopenharmony_ci			return regmap_update_bits(regmap, oreg, omask, omask);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci		return -EINVAL;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci	for (i = 0; i < desc->n_voltages; i++) {
3862306a36Sopenharmony_ci		/* NOTE to next hacker - Does not support pickable ranges */
3962306a36Sopenharmony_ci		if (desc->linear_range_selectors_bitfield)
4062306a36Sopenharmony_ci			return -EINVAL;
4162306a36Sopenharmony_ci		if (desc->n_linear_ranges)
4262306a36Sopenharmony_ci			ret = regulator_desc_list_voltage_linear_range(desc, i);
4362306a36Sopenharmony_ci		else
4462306a36Sopenharmony_ci			ret = regulator_desc_list_voltage_linear(desc, i);
4562306a36Sopenharmony_ci		if (ret < 0)
4662306a36Sopenharmony_ci			continue;
4762306a36Sopenharmony_ci		if (ret == uv) {
4862306a36Sopenharmony_ci			i <<= ffs(desc->vsel_mask) - 1;
4962306a36Sopenharmony_ci			ret = regmap_update_bits(regmap, reg, mask, i);
5062306a36Sopenharmony_ci			if (omask && !ret)
5162306a36Sopenharmony_ci				ret = regmap_update_bits(regmap, oreg, omask,
5262306a36Sopenharmony_ci							 omask);
5362306a36Sopenharmony_ci			break;
5462306a36Sopenharmony_ci		}
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	return ret;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciint rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
6062306a36Sopenharmony_ci			  struct device_node *np,
6162306a36Sopenharmony_ci			  const struct regulator_desc *desc,
6262306a36Sopenharmony_ci			  struct regmap *regmap)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	int i, ret = 0;
6562306a36Sopenharmony_ci	char *prop;
6662306a36Sopenharmony_ci	unsigned int reg, mask, omask, oreg = desc->enable_reg;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) {
6962306a36Sopenharmony_ci		int bit;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		bit = BIT(i);
7262306a36Sopenharmony_ci		if (dvs->level_map & bit) {
7362306a36Sopenharmony_ci			switch (bit) {
7462306a36Sopenharmony_ci			case ROHM_DVS_LEVEL_RUN:
7562306a36Sopenharmony_ci				prop = "rohm,dvs-run-voltage";
7662306a36Sopenharmony_ci				reg = dvs->run_reg;
7762306a36Sopenharmony_ci				mask = dvs->run_mask;
7862306a36Sopenharmony_ci				omask = dvs->run_on_mask;
7962306a36Sopenharmony_ci				break;
8062306a36Sopenharmony_ci			case ROHM_DVS_LEVEL_IDLE:
8162306a36Sopenharmony_ci				prop = "rohm,dvs-idle-voltage";
8262306a36Sopenharmony_ci				reg = dvs->idle_reg;
8362306a36Sopenharmony_ci				mask = dvs->idle_mask;
8462306a36Sopenharmony_ci				omask = dvs->idle_on_mask;
8562306a36Sopenharmony_ci				break;
8662306a36Sopenharmony_ci			case ROHM_DVS_LEVEL_SUSPEND:
8762306a36Sopenharmony_ci				prop = "rohm,dvs-suspend-voltage";
8862306a36Sopenharmony_ci				reg = dvs->suspend_reg;
8962306a36Sopenharmony_ci				mask = dvs->suspend_mask;
9062306a36Sopenharmony_ci				omask = dvs->suspend_on_mask;
9162306a36Sopenharmony_ci				break;
9262306a36Sopenharmony_ci			case ROHM_DVS_LEVEL_LPSR:
9362306a36Sopenharmony_ci				prop = "rohm,dvs-lpsr-voltage";
9462306a36Sopenharmony_ci				reg = dvs->lpsr_reg;
9562306a36Sopenharmony_ci				mask = dvs->lpsr_mask;
9662306a36Sopenharmony_ci				omask = dvs->lpsr_on_mask;
9762306a36Sopenharmony_ci				break;
9862306a36Sopenharmony_ci			case ROHM_DVS_LEVEL_SNVS:
9962306a36Sopenharmony_ci				prop = "rohm,dvs-snvs-voltage";
10062306a36Sopenharmony_ci				reg = dvs->snvs_reg;
10162306a36Sopenharmony_ci				mask = dvs->snvs_mask;
10262306a36Sopenharmony_ci				omask = dvs->snvs_on_mask;
10362306a36Sopenharmony_ci				break;
10462306a36Sopenharmony_ci			default:
10562306a36Sopenharmony_ci				return -EINVAL;
10662306a36Sopenharmony_ci			}
10762306a36Sopenharmony_ci			ret = set_dvs_level(desc, np, regmap, prop, reg, mask,
10862306a36Sopenharmony_ci					    omask, oreg);
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci	return ret;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ciEXPORT_SYMBOL(rohm_regulator_set_dvs_levels);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * Few ROHM PMIC ICs have constrains on voltage changing:
11762306a36Sopenharmony_ci * BD71837 - only buck 1-4 voltages can be changed when they are enabled.
11862306a36Sopenharmony_ci * Other bucks and all LDOs must be disabled when voltage is changed.
11962306a36Sopenharmony_ci * BD96801 - LDO voltage levels can be changed when LDOs are disabled.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ciint rohm_regulator_set_voltage_sel_restricted(struct regulator_dev *rdev,
12262306a36Sopenharmony_ci					      unsigned int sel)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	if (rdev->desc->ops->is_enabled(rdev))
12562306a36Sopenharmony_ci		return -EBUSY;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return regulator_set_voltage_sel_regmap(rdev, sel);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rohm_regulator_set_voltage_sel_restricted);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
13262306a36Sopenharmony_ciMODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
13362306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");
134