Commit | Line | Data |
---|---|---|
b660bf65 | 1 | // SPDX-License-Identifier: GPL-2.0 |
fac4da4f MCC |
2 | // |
3 | // Device driver for regulators in Hisi IC | |
4 | // | |
5 | // Copyright (c) 2013 Linaro Ltd. | |
8d6ee30c | 6 | // Copyright (c) 2011 HiSilicon Ltd. |
e8608f89 | 7 | // Copyright (c) 2020-2021 Huawei Technologies Co., Ltd. |
fac4da4f MCC |
8 | // |
9 | // Guodong Xu <guodong.xu@linaro.org> | |
42f24d9d | 10 | |
d2dfd50a | 11 | #include <linux/delay.h> |
2c4fb70d | 12 | #include <linux/module.h> |
e68ce0fa | 13 | #include <linux/of.h> |
2c4fb70d | 14 | #include <linux/platform_device.h> |
fb02e3eb | 15 | #include <linux/regmap.h> |
42f24d9d | 16 | #include <linux/regulator/driver.h> |
42f24d9d M |
17 | #include <linux/spmi.h> |
18 | ||
9bc146ac AL |
19 | struct hi6421_spmi_reg_priv { |
20 | /* Serialize regulator enable logic */ | |
21 | struct mutex enable_mutex; | |
22 | }; | |
23 | ||
d2dfd50a MCC |
24 | struct hi6421_spmi_reg_info { |
25 | struct regulator_desc desc; | |
d2dfd50a MCC |
26 | u8 eco_mode_mask; |
27 | u32 eco_uA; | |
6436a125 | 28 | }; |
42f24d9d | 29 | |
5e36129f | 30 | static const unsigned int range_1v5_to_2v0[] = { |
d2dfd50a MCC |
31 | 1500000, 1550000, 1600000, 1650000, |
32 | 1700000, 1725000, 1750000, 1775000, | |
33 | 1800000, 1825000, 1850000, 1875000, | |
34 | 1900000, 1925000, 1950000, 2000000 | |
35 | }; | |
36 | ||
5e36129f | 37 | static const unsigned int range_1v725_to_1v9[] = { |
d2dfd50a MCC |
38 | 1725000, 1750000, 1775000, 1800000, |
39 | 1825000, 1850000, 1875000, 1900000 | |
40 | }; | |
41 | ||
5e36129f | 42 | static const unsigned int range_1v75_to_3v3[] = { |
d2dfd50a MCC |
43 | 1750000, 1800000, 1825000, 2800000, |
44 | 2850000, 2950000, 3000000, 3300000 | |
45 | }; | |
46 | ||
5e36129f | 47 | static const unsigned int range_1v8_to_3v0[] = { |
d2dfd50a MCC |
48 | 1800000, 1850000, 2400000, 2600000, |
49 | 2700000, 2850000, 2950000, 3000000 | |
50 | }; | |
51 | ||
5e36129f | 52 | static const unsigned int range_2v5_to_3v3[] = { |
d2dfd50a MCC |
53 | 2500000, 2600000, 2700000, 2800000, |
54 | 3000000, 3100000, 3200000, 3300000 | |
55 | }; | |
56 | ||
5e36129f | 57 | static const unsigned int range_2v6_to_3v3[] = { |
d2dfd50a MCC |
58 | 2600000, 2700000, 2800000, 2900000, |
59 | 3000000, 3100000, 3200000, 3300000 | |
60 | }; | |
61 | ||
54f1155a MCC |
62 | /** |
63 | * HI6421V600_LDO() - specify a LDO power line | |
64 | * @_id: LDO id name string | |
65 | * @vtable: voltage table | |
66 | * @ereg: enable register | |
67 | * @emask: enable mask | |
68 | * @vreg: voltage select register | |
69 | * @odelay: off/on delay time in uS | |
70 | * @etime: enable time in uS | |
71 | * @ecomask: eco mode mask | |
72 | * @ecoamp: eco mode load uppler limit in uA | |
42f24d9d | 73 | */ |
d2dfd50a MCC |
74 | #define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \ |
75 | odelay, etime, ecomask, ecoamp) \ | |
ccb2a74e | 76 | [hi6421v600_##_id] = { \ |
d2dfd50a MCC |
77 | .desc = { \ |
78 | .name = #_id, \ | |
79 | .of_match = of_match_ptr(#_id), \ | |
80 | .regulators_node = of_match_ptr("regulators"), \ | |
81 | .ops = &hi6421_spmi_ldo_rops, \ | |
82 | .type = REGULATOR_VOLTAGE, \ | |
ccb2a74e | 83 | .id = hi6421v600_##_id, \ |
d2dfd50a MCC |
84 | .owner = THIS_MODULE, \ |
85 | .volt_table = vtable, \ | |
86 | .n_voltages = ARRAY_SIZE(vtable), \ | |
50bec7fb | 87 | .vsel_mask = ARRAY_SIZE(vtable) - 1, \ |
d2dfd50a MCC |
88 | .vsel_reg = vreg, \ |
89 | .enable_reg = ereg, \ | |
90 | .enable_mask = emask, \ | |
91 | .enable_time = etime, \ | |
92 | .ramp_delay = etime, \ | |
93 | .off_on_delay = odelay, \ | |
94 | }, \ | |
95 | .eco_mode_mask = ecomask, \ | |
96 | .eco_uA = ecoamp, \ | |
97 | } | |
42f24d9d | 98 | |
c22aeb94 | 99 | static int hi6421_spmi_regulator_enable(struct regulator_dev *rdev) |
42f24d9d | 100 | { |
5db5dd5b | 101 | struct hi6421_spmi_reg_priv *priv = rdev_get_drvdata(rdev); |
6a5e7aaf | 102 | int ret; |
42f24d9d | 103 | |
42f24d9d | 104 | /* cannot enable more than one regulator at one time */ |
9bc146ac | 105 | mutex_lock(&priv->enable_mutex); |
12ca3b20 | 106 | |
4446e6f3 | 107 | ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, |
fb02e3eb | 108 | rdev->desc->enable_mask, |
643fd47a | 109 | rdev->desc->enable_mask); |
42f24d9d | 110 | |
fd765da0 MCC |
111 | /* Avoid powering up multiple devices at the same time */ |
112 | usleep_range(rdev->desc->off_on_delay, rdev->desc->off_on_delay + 60); | |
113 | ||
9bc146ac | 114 | mutex_unlock(&priv->enable_mutex); |
42f24d9d | 115 | |
6a5e7aaf | 116 | return ret; |
42f24d9d M |
117 | } |
118 | ||
c22aeb94 | 119 | static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev) |
42f24d9d | 120 | { |
5db5dd5b | 121 | struct hi6421_spmi_reg_info *sreg; |
57c045bc | 122 | unsigned int reg_val; |
42f24d9d | 123 | |
5db5dd5b | 124 | sreg = container_of(rdev->desc, struct hi6421_spmi_reg_info, desc); |
4446e6f3 | 125 | regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val); |
42f24d9d | 126 | |
32f53804 | 127 | if (reg_val & sreg->eco_mode_mask) |
0b5a562a | 128 | return REGULATOR_MODE_IDLE; |
9a18329f | 129 | |
0b5a562a | 130 | return REGULATOR_MODE_NORMAL; |
42f24d9d M |
131 | } |
132 | ||
c22aeb94 | 133 | static int hi6421_spmi_regulator_set_mode(struct regulator_dev *rdev, |
1275f3c3 | 134 | unsigned int mode) |
42f24d9d | 135 | { |
5db5dd5b | 136 | struct hi6421_spmi_reg_info *sreg; |
57c045bc | 137 | unsigned int val; |
42f24d9d | 138 | |
5db5dd5b | 139 | sreg = container_of(rdev->desc, struct hi6421_spmi_reg_info, desc); |
42f24d9d M |
140 | switch (mode) { |
141 | case REGULATOR_MODE_NORMAL: | |
db27f829 | 142 | val = 0; |
42f24d9d M |
143 | break; |
144 | case REGULATOR_MODE_IDLE: | |
57c045bc AL |
145 | if (!sreg->eco_mode_mask) |
146 | return -EINVAL; | |
147 | ||
148 | val = sreg->eco_mode_mask; | |
42f24d9d M |
149 | break; |
150 | default: | |
151 | return -EINVAL; | |
152 | } | |
153 | ||
4446e6f3 | 154 | return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, |
fb02e3eb | 155 | sreg->eco_mode_mask, val); |
42f24d9d M |
156 | } |
157 | ||
1275f3c3 MCC |
158 | static unsigned int |
159 | hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev, | |
160 | int input_uV, int output_uV, | |
161 | int load_uA) | |
42f24d9d | 162 | { |
5db5dd5b AL |
163 | struct hi6421_spmi_reg_info *sreg; |
164 | ||
165 | sreg = container_of(rdev->desc, struct hi6421_spmi_reg_info, desc); | |
42f24d9d | 166 | |
75d39eb5 | 167 | if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA)) |
42f24d9d | 168 | return REGULATOR_MODE_NORMAL; |
2c4fb70d MCC |
169 | |
170 | return REGULATOR_MODE_IDLE; | |
42f24d9d M |
171 | } |
172 | ||
2c4fb70d | 173 | static const struct regulator_ops hi6421_spmi_ldo_rops = { |
2ba53d04 | 174 | .is_enabled = regulator_is_enabled_regmap, |
c22aeb94 | 175 | .enable = hi6421_spmi_regulator_enable, |
4446e6f3 | 176 | .disable = regulator_disable_regmap, |
42f24d9d | 177 | .list_voltage = regulator_list_voltage_table, |
00430f71 | 178 | .map_voltage = regulator_map_voltage_ascend, |
2ba53d04 MCC |
179 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
180 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
c22aeb94 MCC |
181 | .get_mode = hi6421_spmi_regulator_get_mode, |
182 | .set_mode = hi6421_spmi_regulator_set_mode, | |
183 | .get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode, | |
42f24d9d M |
184 | }; |
185 | ||
d2dfd50a MCC |
186 | /* HI6421v600 regulators with known registers */ |
187 | enum hi6421_spmi_regulator_id { | |
ccb2a74e MCC |
188 | hi6421v600_ldo3, |
189 | hi6421v600_ldo4, | |
190 | hi6421v600_ldo9, | |
191 | hi6421v600_ldo15, | |
192 | hi6421v600_ldo16, | |
193 | hi6421v600_ldo17, | |
194 | hi6421v600_ldo33, | |
195 | hi6421v600_ldo34, | |
d2dfd50a | 196 | }; |
2c4fb70d | 197 | |
d2dfd50a | 198 | static struct hi6421_spmi_reg_info regulator_info[] = { |
5e36129f | 199 | HI6421V600_LDO(ldo3, range_1v5_to_2v0, |
d2dfd50a MCC |
200 | 0x16, 0x01, 0x51, |
201 | 20000, 120, | |
202 | 0, 0), | |
5e36129f | 203 | HI6421V600_LDO(ldo4, range_1v725_to_1v9, |
d2dfd50a MCC |
204 | 0x17, 0x01, 0x52, |
205 | 20000, 120, | |
206 | 0x10, 10000), | |
5e36129f | 207 | HI6421V600_LDO(ldo9, range_1v75_to_3v3, |
d2dfd50a MCC |
208 | 0x1c, 0x01, 0x57, |
209 | 20000, 360, | |
210 | 0x10, 10000), | |
5e36129f | 211 | HI6421V600_LDO(ldo15, range_1v8_to_3v0, |
d2dfd50a MCC |
212 | 0x21, 0x01, 0x5c, |
213 | 20000, 360, | |
214 | 0x10, 10000), | |
5e36129f | 215 | HI6421V600_LDO(ldo16, range_1v8_to_3v0, |
d2dfd50a MCC |
216 | 0x22, 0x01, 0x5d, |
217 | 20000, 360, | |
218 | 0x10, 10000), | |
5e36129f | 219 | HI6421V600_LDO(ldo17, range_2v5_to_3v3, |
d2dfd50a MCC |
220 | 0x23, 0x01, 0x5e, |
221 | 20000, 120, | |
222 | 0x10, 10000), | |
5e36129f | 223 | HI6421V600_LDO(ldo33, range_2v5_to_3v3, |
d2dfd50a MCC |
224 | 0x32, 0x01, 0x6d, |
225 | 20000, 120, | |
226 | 0, 0), | |
5e36129f | 227 | HI6421V600_LDO(ldo34, range_2v6_to_3v3, |
d2dfd50a MCC |
228 | 0x33, 0x01, 0x6e, |
229 | 20000, 120, | |
230 | 0, 0), | |
231 | }; | |
42f24d9d | 232 | |
c22aeb94 | 233 | static int hi6421_spmi_regulator_probe(struct platform_device *pdev) |
42f24d9d | 234 | { |
75937f8f | 235 | struct device *pmic_dev = pdev->dev.parent; |
d2dfd50a | 236 | struct regulator_config config = { }; |
9bc146ac | 237 | struct hi6421_spmi_reg_priv *priv; |
d2dfd50a MCC |
238 | struct hi6421_spmi_reg_info *info; |
239 | struct device *dev = &pdev->dev; | |
e68ce0fa | 240 | struct regmap *regmap; |
d2dfd50a MCC |
241 | struct regulator_dev *rdev; |
242 | int i; | |
75937f8f | 243 | |
75937f8f MCC |
244 | /* |
245 | * This driver is meant to be called by hi6421-spmi-core, | |
246 | * which should first set drvdata. If this doesn't happen, hit | |
247 | * a warn on and return. | |
248 | */ | |
e68ce0fa MCC |
249 | regmap = dev_get_drvdata(pmic_dev); |
250 | if (WARN_ON(!regmap)) | |
75937f8f MCC |
251 | return -ENODEV; |
252 | ||
9bc146ac AL |
253 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
254 | if (!priv) | |
d2dfd50a | 255 | return -ENOMEM; |
42f24d9d | 256 | |
9bc146ac | 257 | mutex_init(&priv->enable_mutex); |
42f24d9d | 258 | |
d2dfd50a | 259 | for (i = 0; i < ARRAY_SIZE(regulator_info); i++) { |
d2dfd50a | 260 | info = ®ulator_info[i]; |
42f24d9d | 261 | |
d2dfd50a | 262 | config.dev = pdev->dev.parent; |
5db5dd5b | 263 | config.driver_data = priv; |
e68ce0fa | 264 | config.regmap = regmap; |
42f24d9d | 265 | |
d2dfd50a MCC |
266 | rdev = devm_regulator_register(dev, &info->desc, &config); |
267 | if (IS_ERR(rdev)) { | |
268 | dev_err(dev, "failed to register %s\n", | |
269 | info->desc.name); | |
270 | return PTR_ERR(rdev); | |
271 | } | |
272 | } | |
42f24d9d M |
273 | |
274 | return 0; | |
275 | } | |
276 | ||
d2dfd50a | 277 | static const struct platform_device_id hi6421_spmi_regulator_table[] = { |
75937f8f MCC |
278 | { .name = "hi6421v600-regulator" }, |
279 | {}, | |
280 | }; | |
d2dfd50a | 281 | MODULE_DEVICE_TABLE(platform, hi6421_spmi_regulator_table); |
75937f8f | 282 | |
d2dfd50a MCC |
283 | static struct platform_driver hi6421_spmi_regulator_driver = { |
284 | .id_table = hi6421_spmi_regulator_table, | |
42f24d9d | 285 | .driver = { |
6a5e7aaf | 286 | .name = "hi6421v600-regulator", |
46600ab1 | 287 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
42f24d9d | 288 | }, |
c22aeb94 | 289 | .probe = hi6421_spmi_regulator_probe, |
42f24d9d | 290 | }; |
d2dfd50a | 291 | module_platform_driver(hi6421_spmi_regulator_driver); |
42f24d9d | 292 | |
6a5e7aaf | 293 | MODULE_DESCRIPTION("Hi6421v600 SPMI regulator driver"); |
42f24d9d M |
294 | MODULE_LICENSE("GPL v2"); |
295 |