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 | |
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, propname: prop, out_value: &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(map: regmap, reg: oreg, mask: omask, val: 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(map: regmap, reg: oreg, mask: omask, val: 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, selector: i); |
43 | else |
44 | ret = regulator_desc_list_voltage_linear(desc, selector: i); |
45 | if (ret < 0) |
46 | continue; |
47 | if (ret == uv) { |
48 | i <<= ffs(desc->vsel_mask) - 1; |
49 | ret = regmap_update_bits(map: regmap, reg, mask, val: i); |
50 | if (omask && !ret) |
51 | ret = regmap_update_bits(map: regmap, reg: oreg, mask: omask, |
52 | val: omask); |
53 | break; |
54 | } |
55 | } |
56 | return ret; |
57 | } |
58 | |
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 | */ |
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 | |