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 
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)11 static 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 
rohm_regulator_set_dvs_levels(const struct rohm_dvs_config * dvs,struct device_node * np,const struct regulator_desc * desc,struct regmap * regmap)59 int 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 }
113 EXPORT_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  */
rohm_regulator_set_voltage_sel_restricted(struct regulator_dev * rdev,unsigned int sel)121 int 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 }
129 EXPORT_SYMBOL_GPL(rohm_regulator_set_voltage_sel_restricted);
130 
131 MODULE_LICENSE("GPL v2");
132 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
133 MODULE_DESCRIPTION("Generic helpers for ROHM PMIC regulator drivers");
134