18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (C) 2020 ROHM Semiconductors
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/errno.h>
58c2ecf20Sopenharmony_ci#include <linux/mfd/rohm-generic.h>
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/of.h>
88c2ecf20Sopenharmony_ci#include <linux/regmap.h>
98c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic int set_dvs_level(const struct regulator_desc *desc,
128c2ecf20Sopenharmony_ci			 struct device_node *np, struct regmap *regmap,
138c2ecf20Sopenharmony_ci			 char *prop, unsigned int reg, unsigned int mask,
148c2ecf20Sopenharmony_ci			 unsigned int omask, unsigned int oreg)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	int ret, i;
178c2ecf20Sopenharmony_ci	uint32_t uv;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, prop, &uv);
208c2ecf20Sopenharmony_ci	if (ret) {
218c2ecf20Sopenharmony_ci		if (ret != -EINVAL)
228c2ecf20Sopenharmony_ci			return ret;
238c2ecf20Sopenharmony_ci		return 0;
248c2ecf20Sopenharmony_ci	}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (uv == 0) {
278c2ecf20Sopenharmony_ci		if (omask)
288c2ecf20Sopenharmony_ci			return regmap_update_bits(regmap, oreg, omask, 0);
298c2ecf20Sopenharmony_ci	}
308c2ecf20Sopenharmony_ci	for (i = 0; i < desc->n_voltages; i++) {
318c2ecf20Sopenharmony_ci		ret = regulator_desc_list_voltage_linear_range(desc, i);
328c2ecf20Sopenharmony_ci		if (ret < 0)
338c2ecf20Sopenharmony_ci			continue;
348c2ecf20Sopenharmony_ci		if (ret == uv) {
358c2ecf20Sopenharmony_ci			i <<= ffs(desc->vsel_mask) - 1;
368c2ecf20Sopenharmony_ci			ret = regmap_update_bits(regmap, reg, mask, i);
378c2ecf20Sopenharmony_ci			if (omask && !ret)
388c2ecf20Sopenharmony_ci				ret = regmap_update_bits(regmap, oreg, omask,
398c2ecf20Sopenharmony_ci							 omask);
408c2ecf20Sopenharmony_ci			break;
418c2ecf20Sopenharmony_ci		}
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci	return ret;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ciint rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
478c2ecf20Sopenharmony_ci			  struct device_node *np,
488c2ecf20Sopenharmony_ci			  const struct regulator_desc *desc,
498c2ecf20Sopenharmony_ci			  struct regmap *regmap)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	int i, ret = 0;
528c2ecf20Sopenharmony_ci	char *prop;
538c2ecf20Sopenharmony_ci	unsigned int reg, mask, omask, oreg = desc->enable_reg;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) {
568c2ecf20Sopenharmony_ci		int bit;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		bit = BIT(i);
598c2ecf20Sopenharmony_ci		if (dvs->level_map & bit) {
608c2ecf20Sopenharmony_ci			switch (bit) {
618c2ecf20Sopenharmony_ci			case ROHM_DVS_LEVEL_RUN:
628c2ecf20Sopenharmony_ci				prop = "rohm,dvs-run-voltage";
638c2ecf20Sopenharmony_ci				reg = dvs->run_reg;
648c2ecf20Sopenharmony_ci				mask = dvs->run_mask;
658c2ecf20Sopenharmony_ci				omask = dvs->run_on_mask;
668c2ecf20Sopenharmony_ci				break;
678c2ecf20Sopenharmony_ci			case ROHM_DVS_LEVEL_IDLE:
688c2ecf20Sopenharmony_ci				prop = "rohm,dvs-idle-voltage";
698c2ecf20Sopenharmony_ci				reg = dvs->idle_reg;
708c2ecf20Sopenharmony_ci				mask = dvs->idle_mask;
718c2ecf20Sopenharmony_ci				omask = dvs->idle_on_mask;
728c2ecf20Sopenharmony_ci				break;
738c2ecf20Sopenharmony_ci			case ROHM_DVS_LEVEL_SUSPEND:
748c2ecf20Sopenharmony_ci				prop = "rohm,dvs-suspend-voltage";
758c2ecf20Sopenharmony_ci				reg = dvs->suspend_reg;
768c2ecf20Sopenharmony_ci				mask = dvs->suspend_mask;
778c2ecf20Sopenharmony_ci				omask = dvs->suspend_on_mask;
788c2ecf20Sopenharmony_ci				break;
798c2ecf20Sopenharmony_ci			case ROHM_DVS_LEVEL_LPSR:
808c2ecf20Sopenharmony_ci				prop = "rohm,dvs-lpsr-voltage";
818c2ecf20Sopenharmony_ci				reg = dvs->lpsr_reg;
828c2ecf20Sopenharmony_ci				mask = dvs->lpsr_mask;
838c2ecf20Sopenharmony_ci				omask = dvs->lpsr_on_mask;
848c2ecf20Sopenharmony_ci				break;
858c2ecf20Sopenharmony_ci			default:
868c2ecf20Sopenharmony_ci				return -EINVAL;
878c2ecf20Sopenharmony_ci			}
888c2ecf20Sopenharmony_ci			ret = set_dvs_level(desc, np, regmap, prop, reg, mask,
898c2ecf20Sopenharmony_ci					    omask, oreg);
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	return ret;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rohm_regulator_set_dvs_levels);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");
99