1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2020 ROHM Semiconductors
3
4#include <linux/errno.h>
5#include <linux/mfd/rohm-generic.h>
6#include <linux/module.h>
7#include <linux/of.h>
8#include <linux/regmap.h>
9#include <linux/regulator/driver.h>
10
11static int set_dvs_level(const struct regulator_desc *desc,
12			 struct device_node *np, struct regmap *regmap,
13			 char *prop, unsigned int reg, unsigned int mask,
14			 unsigned int omask, unsigned int oreg)
15{
16	int ret, i;
17	uint32_t uv;
18
19	ret = of_property_read_u32(np, prop, &uv);
20	if (ret) {
21		if (ret != -EINVAL)
22			return ret;
23		return 0;
24	}
25	/* If voltage is set to 0 => disable */
26	if (uv == 0) {
27		if (omask)
28			return regmap_update_bits(regmap, oreg, omask, 0);
29	}
30	/* Some setups don't allow setting own voltage but do allow enabling */
31	if (!mask) {
32		if (omask)
33			return regmap_update_bits(regmap, oreg, omask, omask);
34
35		return -EINVAL;
36	}
37	for (i = 0; i < desc->n_voltages; i++) {
38		/* NOTE to next hacker - Does not support pickable ranges */
39		if (desc->linear_range_selectors_bitfield)
40			return -EINVAL;
41		if (desc->n_linear_ranges)
42			ret = regulator_desc_list_voltage_linear_range(desc, i);
43		else
44			ret = regulator_desc_list_voltage_linear(desc, i);
45		if (ret < 0)
46			continue;
47		if (ret == uv) {
48			i <<= ffs(desc->vsel_mask) - 1;
49			ret = regmap_update_bits(regmap, reg, mask, i);
50			if (omask && !ret)
51				ret = regmap_update_bits(regmap, oreg, omask,
52							 omask);
53			break;
54		}
55	}
56	return ret;
57}
58
59int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
60			  struct device_node *np,
61			  const struct regulator_desc *desc,
62			  struct regmap *regmap)
63{
64	int i, ret = 0;
65	char *prop;
66	unsigned int reg, mask, omask, oreg = desc->enable_reg;
67
68	for (i = 0; i < ROHM_DVS_LEVEL_VALID_AMOUNT && !ret; i++) {
69		int bit;
70
71		bit = BIT(i);
72		if (dvs->level_map & bit) {
73			switch (bit) {
74			case ROHM_DVS_LEVEL_RUN:
75				prop = "rohm,dvs-run-voltage";
76				reg = dvs->run_reg;
77				mask = dvs->run_mask;
78				omask = dvs->run_on_mask;
79				break;
80			case ROHM_DVS_LEVEL_IDLE:
81				prop = "rohm,dvs-idle-voltage";
82				reg = dvs->idle_reg;
83				mask = dvs->idle_mask;
84				omask = dvs->idle_on_mask;
85				break;
86			case ROHM_DVS_LEVEL_SUSPEND:
87				prop = "rohm,dvs-suspend-voltage";
88				reg = dvs->suspend_reg;
89				mask = dvs->suspend_mask;
90				omask = dvs->suspend_on_mask;
91				break;
92			case ROHM_DVS_LEVEL_LPSR:
93				prop = "rohm,dvs-lpsr-voltage";
94				reg = dvs->lpsr_reg;
95				mask = dvs->lpsr_mask;
96				omask = dvs->lpsr_on_mask;
97				break;
98			case ROHM_DVS_LEVEL_SNVS:
99				prop = "rohm,dvs-snvs-voltage";
100				reg = dvs->snvs_reg;
101				mask = dvs->snvs_mask;
102				omask = dvs->snvs_on_mask;
103				break;
104			default:
105				return -EINVAL;
106			}
107			ret = set_dvs_level(desc, np, regmap, prop, reg, mask,
108					    omask, oreg);
109		}
110	}
111	return ret;
112}
113EXPORT_SYMBOL(rohm_regulator_set_dvs_levels);
114
115/*
116 * Few ROHM PMIC ICs have constrains on voltage changing:
117 * BD71837 - only buck 1-4 voltages can be changed when they are enabled.
118 * Other bucks and all LDOs must be disabled when voltage is changed.
119 * BD96801 - LDO voltage levels can be changed when LDOs are disabled.
120 */
121int rohm_regulator_set_voltage_sel_restricted(struct regulator_dev *rdev,
122					      unsigned int sel)
123{
124	if (rdev->desc->ops->is_enabled(rdev))
125		return -EBUSY;
126
127	return regulator_set_voltage_sel_regmap(rdev, sel);
128}
129EXPORT_SYMBOL_GPL(rohm_regulator_set_voltage_sel_restricted);
130
131MODULE_LICENSE("GPL v2");
132MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
133MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");
134