]>
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. |
746eae6a | 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 MCC |
12 | #include <linux/mfd/hi6421-spmi-pmic.h> |
13 | #include <linux/module.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 | |
d2dfd50a MCC |
30 | static const unsigned int ldo3_voltages[] = { |
31 | 1500000, 1550000, 1600000, 1650000, | |
32 | 1700000, 1725000, 1750000, 1775000, | |
33 | 1800000, 1825000, 1850000, 1875000, | |
34 | 1900000, 1925000, 1950000, 2000000 | |
35 | }; | |
36 | ||
37 | static const unsigned int ldo4_voltages[] = { | |
38 | 1725000, 1750000, 1775000, 1800000, | |
39 | 1825000, 1850000, 1875000, 1900000 | |
40 | }; | |
41 | ||
42 | static const unsigned int ldo9_voltages[] = { | |
43 | 1750000, 1800000, 1825000, 2800000, | |
44 | 2850000, 2950000, 3000000, 3300000 | |
45 | }; | |
46 | ||
47 | static const unsigned int ldo15_voltages[] = { | |
48 | 1800000, 1850000, 2400000, 2600000, | |
49 | 2700000, 2850000, 2950000, 3000000 | |
50 | }; | |
51 | ||
52 | static const unsigned int ldo17_voltages[] = { | |
53 | 2500000, 2600000, 2700000, 2800000, | |
54 | 3000000, 3100000, 3200000, 3300000 | |
55 | }; | |
56 | ||
57 | static const unsigned int ldo34_voltages[] = { | |
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) \ | |
76 | [HI6421V600_##_id] = { \ | |
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, \ | |
83 | .id = HI6421V600_##_id, \ | |
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 | { |
9bc146ac | 101 | struct hi6421_spmi_reg_priv *priv; |
6a5e7aaf | 102 | int ret; |
42f24d9d | 103 | |
9bc146ac | 104 | priv = dev_get_drvdata(rdev->dev.parent); |
42f24d9d | 105 | /* cannot enable more than one regulator at one time */ |
9bc146ac | 106 | mutex_lock(&priv->enable_mutex); |
12ca3b20 | 107 | |
4446e6f3 | 108 | ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, |
fb02e3eb | 109 | rdev->desc->enable_mask, |
643fd47a | 110 | rdev->desc->enable_mask); |
42f24d9d | 111 | |
fd765da0 MCC |
112 | /* Avoid powering up multiple devices at the same time */ |
113 | usleep_range(rdev->desc->off_on_delay, rdev->desc->off_on_delay + 60); | |
114 | ||
9bc146ac | 115 | mutex_unlock(&priv->enable_mutex); |
42f24d9d | 116 | |
6a5e7aaf | 117 | return ret; |
42f24d9d M |
118 | } |
119 | ||
c22aeb94 | 120 | static unsigned int hi6421_spmi_regulator_get_mode(struct regulator_dev *rdev) |
42f24d9d | 121 | { |
d2dfd50a | 122 | struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); |
57c045bc | 123 | unsigned int reg_val; |
42f24d9d | 124 | |
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 | { |
d2dfd50a | 136 | struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); |
57c045bc | 137 | unsigned int val; |
42f24d9d M |
138 | |
139 | switch (mode) { | |
140 | case REGULATOR_MODE_NORMAL: | |
db27f829 | 141 | val = 0; |
42f24d9d M |
142 | break; |
143 | case REGULATOR_MODE_IDLE: | |
57c045bc AL |
144 | if (!sreg->eco_mode_mask) |
145 | return -EINVAL; | |
146 | ||
147 | val = sreg->eco_mode_mask; | |
42f24d9d M |
148 | break; |
149 | default: | |
150 | return -EINVAL; | |
151 | } | |
152 | ||
4446e6f3 | 153 | return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, |
fb02e3eb | 154 | sreg->eco_mode_mask, val); |
42f24d9d M |
155 | } |
156 | ||
1275f3c3 MCC |
157 | static unsigned int |
158 | hi6421_spmi_regulator_get_optimum_mode(struct regulator_dev *rdev, | |
159 | int input_uV, int output_uV, | |
160 | int load_uA) | |
42f24d9d | 161 | { |
d2dfd50a | 162 | struct hi6421_spmi_reg_info *sreg = rdev_get_drvdata(rdev); |
42f24d9d | 163 | |
75d39eb5 | 164 | if (!sreg->eco_uA || ((unsigned int)load_uA > sreg->eco_uA)) |
42f24d9d | 165 | return REGULATOR_MODE_NORMAL; |
2c4fb70d MCC |
166 | |
167 | return REGULATOR_MODE_IDLE; | |
42f24d9d M |
168 | } |
169 | ||
2c4fb70d | 170 | static const struct regulator_ops hi6421_spmi_ldo_rops = { |
2ba53d04 | 171 | .is_enabled = regulator_is_enabled_regmap, |
c22aeb94 | 172 | .enable = hi6421_spmi_regulator_enable, |
4446e6f3 | 173 | .disable = regulator_disable_regmap, |
42f24d9d | 174 | .list_voltage = regulator_list_voltage_table, |
00430f71 | 175 | .map_voltage = regulator_map_voltage_ascend, |
2ba53d04 MCC |
176 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
177 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
c22aeb94 MCC |
178 | .get_mode = hi6421_spmi_regulator_get_mode, |
179 | .set_mode = hi6421_spmi_regulator_set_mode, | |
180 | .get_optimum_mode = hi6421_spmi_regulator_get_optimum_mode, | |
42f24d9d M |
181 | }; |
182 | ||
d2dfd50a MCC |
183 | /* HI6421v600 regulators with known registers */ |
184 | enum hi6421_spmi_regulator_id { | |
185 | HI6421V600_LDO3, | |
186 | HI6421V600_LDO4, | |
187 | HI6421V600_LDO9, | |
188 | HI6421V600_LDO15, | |
189 | HI6421V600_LDO16, | |
190 | HI6421V600_LDO17, | |
191 | HI6421V600_LDO33, | |
192 | HI6421V600_LDO34, | |
193 | }; | |
2c4fb70d | 194 | |
d2dfd50a MCC |
195 | static struct hi6421_spmi_reg_info regulator_info[] = { |
196 | HI6421V600_LDO(LDO3, ldo3_voltages, | |
197 | 0x16, 0x01, 0x51, | |
198 | 20000, 120, | |
199 | 0, 0), | |
200 | HI6421V600_LDO(LDO4, ldo4_voltages, | |
201 | 0x17, 0x01, 0x52, | |
202 | 20000, 120, | |
203 | 0x10, 10000), | |
204 | HI6421V600_LDO(LDO9, ldo9_voltages, | |
205 | 0x1c, 0x01, 0x57, | |
206 | 20000, 360, | |
207 | 0x10, 10000), | |
208 | HI6421V600_LDO(LDO15, ldo15_voltages, | |
209 | 0x21, 0x01, 0x5c, | |
210 | 20000, 360, | |
211 | 0x10, 10000), | |
212 | HI6421V600_LDO(LDO16, ldo15_voltages, | |
213 | 0x22, 0x01, 0x5d, | |
214 | 20000, 360, | |
215 | 0x10, 10000), | |
216 | HI6421V600_LDO(LDO17, ldo17_voltages, | |
217 | 0x23, 0x01, 0x5e, | |
218 | 20000, 120, | |
219 | 0x10, 10000), | |
220 | HI6421V600_LDO(LDO33, ldo17_voltages, | |
221 | 0x32, 0x01, 0x6d, | |
222 | 20000, 120, | |
223 | 0, 0), | |
224 | HI6421V600_LDO(LDO34, ldo34_voltages, | |
225 | 0x33, 0x01, 0x6e, | |
226 | 20000, 120, | |
227 | 0, 0), | |
228 | }; | |
42f24d9d | 229 | |
c22aeb94 | 230 | static int hi6421_spmi_regulator_probe(struct platform_device *pdev) |
42f24d9d | 231 | { |
75937f8f | 232 | struct device *pmic_dev = pdev->dev.parent; |
d2dfd50a | 233 | struct regulator_config config = { }; |
9bc146ac | 234 | struct hi6421_spmi_reg_priv *priv; |
d2dfd50a MCC |
235 | struct hi6421_spmi_reg_info *info; |
236 | struct device *dev = &pdev->dev; | |
c22aeb94 | 237 | struct hi6421_spmi_pmic *pmic; |
d2dfd50a MCC |
238 | struct regulator_dev *rdev; |
239 | int i; | |
75937f8f | 240 | |
75937f8f MCC |
241 | /* |
242 | * This driver is meant to be called by hi6421-spmi-core, | |
243 | * which should first set drvdata. If this doesn't happen, hit | |
244 | * a warn on and return. | |
245 | */ | |
246 | pmic = dev_get_drvdata(pmic_dev); | |
247 | if (WARN_ON(!pmic)) | |
248 | return -ENODEV; | |
249 | ||
9bc146ac AL |
250 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
251 | if (!priv) | |
d2dfd50a | 252 | return -ENOMEM; |
42f24d9d | 253 | |
9bc146ac AL |
254 | mutex_init(&priv->enable_mutex); |
255 | platform_set_drvdata(pdev, priv); | |
42f24d9d | 256 | |
d2dfd50a | 257 | for (i = 0; i < ARRAY_SIZE(regulator_info); i++) { |
d2dfd50a | 258 | info = ®ulator_info[i]; |
42f24d9d | 259 | |
d2dfd50a | 260 | config.dev = pdev->dev.parent; |
9bc146ac | 261 | config.driver_data = info; |
fcd73240 | 262 | config.regmap = pmic->regmap; |
42f24d9d | 263 | |
d2dfd50a MCC |
264 | rdev = devm_regulator_register(dev, &info->desc, &config); |
265 | if (IS_ERR(rdev)) { | |
266 | dev_err(dev, "failed to register %s\n", | |
267 | info->desc.name); | |
268 | return PTR_ERR(rdev); | |
269 | } | |
270 | } | |
42f24d9d M |
271 | |
272 | return 0; | |
273 | } | |
274 | ||
d2dfd50a | 275 | static const struct platform_device_id hi6421_spmi_regulator_table[] = { |
75937f8f MCC |
276 | { .name = "hi6421v600-regulator" }, |
277 | {}, | |
278 | }; | |
d2dfd50a | 279 | MODULE_DEVICE_TABLE(platform, hi6421_spmi_regulator_table); |
75937f8f | 280 | |
d2dfd50a MCC |
281 | static struct platform_driver hi6421_spmi_regulator_driver = { |
282 | .id_table = hi6421_spmi_regulator_table, | |
42f24d9d | 283 | .driver = { |
6a5e7aaf | 284 | .name = "hi6421v600-regulator", |
42f24d9d | 285 | }, |
c22aeb94 | 286 | .probe = hi6421_spmi_regulator_probe, |
42f24d9d | 287 | }; |
d2dfd50a | 288 | module_platform_driver(hi6421_spmi_regulator_driver); |
42f24d9d | 289 | |
6a5e7aaf | 290 | MODULE_DESCRIPTION("Hi6421v600 SPMI regulator driver"); |
42f24d9d M |
291 | MODULE_LICENSE("GPL v2"); |
292 |