blob: 06fd3f31956f6dac9be28e0fcba513b0fcf06106 [file] [log] [blame]
Caleb Connollyc0e6aa62024-07-15 12:08:15 +02001// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
3// Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4
5#define pr_fmt(fmt) "%s: " fmt, __func__
6
7#include <linux/err.h>
Caleb Connolly812aacf2024-07-15 12:08:16 +02008#include <dm/device_compat.h>
9#include <dm/device.h>
10#include <dm/devres.h>
11#include <dm/lists.h>
12#include <power/regulator.h>
13#include <log.h>
Caleb Connollyc0e6aa62024-07-15 12:08:15 +020014
15#include <soc/qcom/cmd-db.h>
16#include <soc/qcom/rpmh.h>
17
18#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
19
20/**
21 * enum rpmh_regulator_type - supported RPMh accelerator types
22 * @VRM: RPMh VRM accelerator which supports voting on enable, voltage,
23 * and mode of LDO, SMPS, and BOB type PMIC regulators.
24 * @XOB: RPMh XOB accelerator which supports voting on the enable state
25 * of PMIC regulators.
26 */
27enum rpmh_regulator_type {
28 VRM,
29 XOB,
30};
31
Caleb Connolly924d9a72024-07-15 12:08:17 +020032enum rpmh_regulator_mode {
33 REGULATOR_MODE_RETENTION,
34 REGULATOR_MODE_LPM,
35 REGULATOR_MODE_AUTO,
36 REGULATOR_MODE_HPM,
37};
38
Caleb Connollyc0e6aa62024-07-15 12:08:15 +020039#define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0
40#define RPMH_REGULATOR_REG_ENABLE 0x4
41#define RPMH_REGULATOR_REG_VRM_MODE 0x8
42
43#define PMIC4_LDO_MODE_RETENTION 4
44#define PMIC4_LDO_MODE_LPM 5
45#define PMIC4_LDO_MODE_HPM 7
46
47#define PMIC4_SMPS_MODE_RETENTION 4
48#define PMIC4_SMPS_MODE_PFM 5
49#define PMIC4_SMPS_MODE_AUTO 6
50#define PMIC4_SMPS_MODE_PWM 7
51
52#define PMIC4_BOB_MODE_PASS 0
53#define PMIC4_BOB_MODE_PFM 1
54#define PMIC4_BOB_MODE_AUTO 2
55#define PMIC4_BOB_MODE_PWM 3
56
57#define PMIC5_LDO_MODE_RETENTION 3
58#define PMIC5_LDO_MODE_LPM 4
59#define PMIC5_LDO_MODE_HPM 7
60
61#define PMIC5_SMPS_MODE_RETENTION 3
62#define PMIC5_SMPS_MODE_PFM 4
63#define PMIC5_SMPS_MODE_AUTO 6
64#define PMIC5_SMPS_MODE_PWM 7
65
66#define PMIC5_BOB_MODE_PASS 2
67#define PMIC5_BOB_MODE_PFM 4
68#define PMIC5_BOB_MODE_AUTO 6
69#define PMIC5_BOB_MODE_PWM 7
70
Caleb Connolly924d9a72024-07-15 12:08:17 +020071
72/**
73 * struct linear_range - table of selector - value pairs
74 *
75 * Define a lookup-table for range of values. Intended to help when looking
76 * for a register value matching certaing physical measure (like voltage).
77 * Usable when increment of one in register always results a constant increment
78 * of the physical measure (like voltage).
79 *
80 * @min: Lowest value in range
81 * @min_sel: Lowest selector for range
82 * @max_sel: Highest selector for range
83 * @step: Value step size
84 */
85struct linear_range {
86 unsigned int min;
87 unsigned int min_sel;
88 unsigned int max_sel;
89 unsigned int step;
90};
91
92/* Initialize struct linear_range for regulators */
93#define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV) \
94{ \
95 .min = _min_uV, \
96 .min_sel = _min_sel, \
97 .max_sel = _max_sel, \
98 .step = _step_uV, \
99}
100
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200101/**
102 * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations
103 * @regulator_type: RPMh accelerator type used to manage this
104 * regulator
105 * @ops: Pointer to regulator ops callback structure
Caleb Connollyc4c9cc02024-07-15 12:08:18 +0200106 * @voltage_range: The single range of voltages supported by this
107 * PMIC regulator type
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200108 * @n_voltages: The number of unique voltage set points defined
Caleb Connollyc4c9cc02024-07-15 12:08:18 +0200109 * by voltage_range
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200110 * @hpm_min_load_uA: Minimum load current in microamps that requires
111 * high power mode (HPM) operation. This is used
112 * for LDO hardware type regulators only.
113 * @pmic_mode_map: Array indexed by regulator framework mode
114 * containing PMIC hardware modes. Must be large
115 * enough to index all framework modes supported
116 * by this regulator hardware type.
117 * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined
118 * in device tree to a regulator framework mode
119 */
120struct rpmh_vreg_hw_data {
121 enum rpmh_regulator_type regulator_type;
Caleb Connollyc4c9cc02024-07-15 12:08:18 +0200122 const struct dm_regulator_ops *ops;
123 struct linear_range voltage_range;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200124 int n_voltages;
125 int hpm_min_load_uA;
Caleb Connollyc4c9cc02024-07-15 12:08:18 +0200126 struct dm_regulator_mode *pmic_mode_map;
127 int n_modes;
128 unsigned int (*of_map_mode)(unsigned int mode);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200129};
130
131/**
132 * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a
133 * single regulator device
134 * @dev: Device pointer for the top-level PMIC RPMh
135 * regulator parent device. This is used as a
136 * handle in RPMh write requests.
137 * @addr: Base address of the regulator resource within
138 * an RPMh accelerator
139 * @rdesc: Regulator descriptor
140 * @hw_data: PMIC regulator configuration data for this RPMh
141 * regulator
142 * @always_wait_for_ack: Boolean flag indicating if a request must always
143 * wait for an ACK from RPMh before continuing even
144 * if it corresponds to a strictly lower power
145 * state (e.g. enabled --> disabled).
146 * @enabled: Flag indicating if the regulator is enabled or
147 * not
148 * @bypassed: Boolean indicating if the regulator is in
149 * bypass (pass-through) mode or not. This is
150 * only used by BOB rpmh-regulator resources.
Caleb Connollyc4c9cc02024-07-15 12:08:18 +0200151 * @uv: Selector used for get_voltage_sel() and
152 * set_value() callbacks
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200153 * @mode: RPMh VRM regulator current framework mode
154 */
155struct rpmh_vreg {
Caleb Connollyc4c9cc02024-07-15 12:08:18 +0200156 struct udevice *dev;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200157 u32 addr;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200158 const struct rpmh_vreg_hw_data *hw_data;
159 bool always_wait_for_ack;
160
161 int enabled;
162 bool bypassed;
Caleb Connollyc4c9cc02024-07-15 12:08:18 +0200163 int uv;
164 int mode;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200165};
166
167/**
168 * struct rpmh_vreg_init_data - initialization data for an RPMh regulator
169 * @name: Name for the regulator which also corresponds
170 * to the device tree subnode name of the regulator
171 * @resource_name: RPMh regulator resource name format string.
172 * This must include exactly one field: '%s' which
173 * is filled at run-time with the PMIC ID provided
174 * by device tree property qcom,pmic-id. Example:
175 * "ldo%s1" for RPMh resource "ldoa1".
176 * @supply_name: Parent supply regulator name
177 * @hw_data: Configuration data for this PMIC regulator type
178 */
179struct rpmh_vreg_init_data {
180 const char *name;
181 const char *resource_name;
182 const char *supply_name;
183 const struct rpmh_vreg_hw_data *hw_data;
184};
185
186/**
187 * rpmh_regulator_send_request() - send the request to RPMh
188 * @vreg: Pointer to the RPMh regulator
189 * @cmd: Pointer to the RPMh command to send
190 * @wait_for_ack: Boolean indicating if execution must wait until the
191 * request has been acknowledged as complete
192 *
193 * Return: 0 on success, errno on failure
194 */
195static int rpmh_regulator_send_request(struct rpmh_vreg *vreg,
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200196 const struct tcs_cmd *cmd, bool wait_for_ack)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200197{
198 int ret;
199
200 if (wait_for_ack || vreg->always_wait_for_ack)
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200201 ret = rpmh_write(vreg->dev->parent, RPMH_ACTIVE_ONLY_STATE, cmd, 1);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200202 else
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200203 ret = rpmh_write_async(vreg->dev->parent, RPMH_ACTIVE_ONLY_STATE, cmd, 1);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200204
205 return ret;
206}
207
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200208static int _rpmh_regulator_vrm_set_value(struct udevice *rdev,
209 int uv, bool wait_for_ack)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200210{
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200211 struct rpmh_vreg *vreg = dev_get_priv(rdev);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200212 struct tcs_cmd cmd = {
213 .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
214 };
215 int ret;
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200216 unsigned int selector;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200217
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200218 selector = (uv - vreg->hw_data->voltage_range.min) / vreg->hw_data->voltage_range.step;
219 cmd.data = DIV_ROUND_UP(vreg->hw_data->voltage_range.min +
220 selector * vreg->hw_data->voltage_range.step, 1000);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200221
222 ret = rpmh_regulator_send_request(vreg, &cmd, wait_for_ack);
223 if (!ret)
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200224 vreg->uv = cmd.data * 1000;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200225
226 return ret;
227}
228
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200229static int rpmh_regulator_vrm_set_value(struct udevice *rdev,
230 int uv)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200231{
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200232 struct rpmh_vreg *vreg = dev_get_priv(rdev);
233
234 debug("%s: set_value %d (current %d)\n", rdev->name, uv, vreg->uv);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200235
236 if (vreg->enabled == -EINVAL) {
237 /*
238 * Cache the voltage and send it later when the regulator is
239 * enabled or disabled.
240 */
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200241 vreg->uv = uv;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200242 return 0;
243 }
244
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200245 return _rpmh_regulator_vrm_set_value(rdev, uv,
246 uv > vreg->uv);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200247}
248
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200249static int rpmh_regulator_vrm_get_value(struct udevice *rdev)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200250{
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200251 struct rpmh_vreg *vreg = dev_get_priv(rdev);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200252
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200253 debug("%s: get_value %d\n", rdev->name, vreg->uv);
254
255 return vreg->uv;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200256}
257
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200258static int rpmh_regulator_is_enabled(struct udevice *rdev)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200259{
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200260 struct rpmh_vreg *vreg = dev_get_priv(rdev);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200261
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200262 debug("%s: is_enabled %d\n", rdev->name, vreg->enabled);
263
264 return vreg->enabled > 0;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200265}
266
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200267static int rpmh_regulator_set_enable_state(struct udevice *rdev,
268 bool enable)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200269{
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200270 struct rpmh_vreg *vreg = dev_get_priv(rdev);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200271 struct tcs_cmd cmd = {
272 .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
273 .data = enable,
274 };
275 int ret;
276
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200277 debug("%s: set_enable %d (current %d)\n", rdev->name, enable,
278 vreg->enabled);
279
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200280 if (vreg->enabled == -EINVAL &&
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200281 vreg->uv != -ENOTRECOVERABLE) {
282 ret = _rpmh_regulator_vrm_set_value(rdev,
283 vreg->uv, true);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200284 if (ret < 0)
285 return ret;
286 }
287
288 ret = rpmh_regulator_send_request(vreg, &cmd, enable);
289 if (!ret)
290 vreg->enabled = enable;
291
292 return ret;
293}
294
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200295static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200296 unsigned int mode, bool bypassed)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200297{
298 struct tcs_cmd cmd = {
299 .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
300 };
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200301 struct dm_regulator_mode *pmic_mode;
302 int i;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200303
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200304 if (mode > REGULATOR_MODE_HPM)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200305 return -EINVAL;
306
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200307 for (i = 0; i < vreg->hw_data->n_modes; i++) {
308 pmic_mode = &vreg->hw_data->pmic_mode_map[i];
309 if (pmic_mode->id == mode)
310 break;
311 }
312 if (pmic_mode->id != mode) {
313 printf("Invalid mode %d\n", mode);
314 return -EINVAL;
315 }
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200316
317 if (bypassed)
318 cmd.data = PMIC4_BOB_MODE_PASS;
319 else
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200320 cmd.data = pmic_mode->id;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200321
322 return rpmh_regulator_send_request(vreg, &cmd, true);
323}
324
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200325static int rpmh_regulator_vrm_set_mode(struct udevice *rdev,
326 int mode)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200327{
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200328 struct rpmh_vreg *vreg = dev_get_priv(rdev);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200329 int ret;
330
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200331 debug("%s: set_mode %d (current %d)\n", rdev->name, mode, vreg->mode);
332
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200333 if (mode == vreg->mode)
334 return 0;
335
336 ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
337 if (!ret)
338 vreg->mode = mode;
339
340 return ret;
341}
342
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200343static int rpmh_regulator_vrm_get_mode(struct udevice *rdev)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200344{
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200345 struct rpmh_vreg *vreg = dev_get_priv(rdev);
346
347 debug("%s: get_mode %d\n", rdev->name, vreg->mode);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200348
349 return vreg->mode;
350}
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200351static const struct dm_regulator_ops rpmh_regulator_vrm_drms_ops = {
352 .get_value = rpmh_regulator_vrm_get_value,
353 .set_value = rpmh_regulator_vrm_set_value,
354 .set_enable = rpmh_regulator_set_enable_state,
355 .get_enable = rpmh_regulator_is_enabled,
356 .set_mode = rpmh_regulator_vrm_set_mode,
357 .get_mode = rpmh_regulator_vrm_get_mode,
358};
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200359
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200360static struct dm_regulator_mode pmic_mode_map_pmic5_ldo[] = {
361 {
362 .id = REGULATOR_MODE_RETENTION,
363 .register_value = PMIC5_LDO_MODE_RETENTION,
364 .name = "PMIC5_LDO_MODE_RETENTION"
365 }, {
366 .id = REGULATOR_MODE_LPM,
367 .register_value = PMIC5_LDO_MODE_LPM,
368 .name = "PMIC5_LDO_MODE_LPM"
369 }, {
370 .id = REGULATOR_MODE_HPM,
371 .register_value = PMIC5_LDO_MODE_HPM,
372 .name = "PMIC5_LDO_MODE_HPM"
373 },
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200374};
375
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200376static const struct rpmh_vreg_hw_data pmic5_pldo = {
377 .regulator_type = VRM,
378 .ops = &rpmh_regulator_vrm_drms_ops,
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200379 .voltage_range = REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000),
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200380 .n_voltages = 256,
381 .hpm_min_load_uA = 10000,
382 .pmic_mode_map = pmic_mode_map_pmic5_ldo,
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200383 .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo),
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200384};
385
386static const struct rpmh_vreg_hw_data pmic5_pldo_lv = {
387 .regulator_type = VRM,
388 .ops = &rpmh_regulator_vrm_drms_ops,
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200389 .voltage_range = REGULATOR_LINEAR_RANGE(1504000, 0, 62, 8000),
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200390 .n_voltages = 63,
391 .hpm_min_load_uA = 10000,
392 .pmic_mode_map = pmic_mode_map_pmic5_ldo,
Caleb Connollyf0e5db52024-07-15 12:08:20 +0200393 .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo),
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200394};
395
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200396#define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \
397{ \
398 .name = _name, \
399 .resource_name = _resource_name, \
400 .hw_data = _hw_data, \
401 .supply_name = _supply_name, \
402}
403
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200404static const struct rpmh_vreg_init_data pm8150_vreg_data[] = {
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200405 RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l13-l16-l17"),
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200406 {}
407};
408
409static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = {
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200410 RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l8"),
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200411 RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l7-l11"),
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200412 {}
413};
414
Caleb Connollyc58644d2024-07-15 12:08:21 +0200415/* probe an individual regulator */
416static int rpmh_regulator_probe(struct udevice *dev)
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200417{
Caleb Connollyc58644d2024-07-15 12:08:21 +0200418 const struct rpmh_vreg_init_data *init_data;
419 struct rpmh_vreg *priv;
420 struct dm_regulator_uclass_plat *plat_data;
421
422 init_data = (const struct rpmh_vreg_init_data *)dev_get_driver_data(dev);
423 priv = dev_get_priv(dev);
424 plat_data = dev_get_uclass_plat(dev);
425
426 priv->dev = dev;
427 priv->addr = cmd_db_read_addr(dev->name);
428 if (!priv->addr) {
429 dev_err(dev, "Failed to read RPMh address for %s\n", dev->name);
430 return -ENODEV;
431 }
432
433 priv->hw_data = init_data->hw_data;
434 priv->enabled = -EINVAL;
435 priv->uv = -ENOTRECOVERABLE;
436 if (ofnode_read_u32(dev_ofnode(dev), "regulator-initial-mode", &priv->mode))
437 priv->mode = -EINVAL;
438
439 plat_data->mode = priv->hw_data->pmic_mode_map;
440 plat_data->mode_count = priv->hw_data->n_modes;
441
442 return 0;
443}
444
445/* for non-drm, xob, or bypass regulators add additional driver definitions */
446U_BOOT_DRIVER(rpmh_regulator_drm) = {
447 .name = "rpmh_regulator_drm",
448 .id = UCLASS_REGULATOR,
449 .probe = rpmh_regulator_probe,
450 .priv_auto = sizeof(struct rpmh_vreg),
451 .ops = &rpmh_regulator_vrm_drms_ops,
452};
453
454/* This driver intentionally only supports a subset of the available regulators.
455 * This function checks to see if a given regulator node in DT matches a regulator
456 * defined in the driver.
457 */
458static const struct rpmh_vreg_init_data *
459vreg_get_init_data(const struct rpmh_vreg_init_data *init_data, ofnode node)
460{
461 const struct rpmh_vreg_init_data *data;
462
463 for (data = init_data; data->name; data++) {
464 if (!strcmp(data->name, ofnode_get_name(node)))
465 return data;
466 }
467
468 return NULL;
469}
470
471static int rpmh_regulators_bind(struct udevice *dev)
472{
473 const struct rpmh_vreg_init_data *init_data, *data;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200474 const char *pmic_id;
Caleb Connollyc58644d2024-07-15 12:08:21 +0200475 char *name;
476 struct driver *drv;
477 ofnode node;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200478 int ret;
Caleb Connollyc58644d2024-07-15 12:08:21 +0200479 size_t namelen;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200480
Caleb Connollyc58644d2024-07-15 12:08:21 +0200481 init_data = (const struct rpmh_vreg_init_data *)dev_get_driver_data(dev);
482 if (!init_data) {
483 dev_err(dev, "No RPMh regulator init data\n");
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200484 return -ENODEV;
Caleb Connollyc58644d2024-07-15 12:08:21 +0200485 }
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200486
Caleb Connollyc58644d2024-07-15 12:08:21 +0200487 pmic_id = ofnode_read_string(dev_ofnode(dev), "qcom,pmic-id");
488 if (!pmic_id) {
489 dev_err(dev, "No PMIC ID\n");
490 return -ENODEV;
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200491 }
492
Caleb Connollyc58644d2024-07-15 12:08:21 +0200493 drv = lists_driver_lookup_name("rpmh_regulator_drm");
494
495 ofnode_for_each_subnode(node, dev_ofnode(dev)) {
496 data = vreg_get_init_data(init_data, node);
497 if (!data)
498 continue;
499
500 /* %s is replaced with pmic_id, so subtract 2, then add 1 for the null terminator */
501 namelen = strlen(data->resource_name) + strlen(pmic_id) - 1;
502 name = devm_kzalloc(dev, namelen, GFP_KERNEL);
503 ret = snprintf(name, namelen, data->resource_name, pmic_id);
504 if (ret < 0 || ret >= namelen) {
505 dev_err(dev, "Failed to create RPMh regulator name\n");
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200506 return -ENOMEM;
507 }
508
Caleb Connollyc58644d2024-07-15 12:08:21 +0200509 ret = device_bind_with_driver_data(dev, drv, name, (ulong)data,
510 node, NULL);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200511 if (ret < 0) {
Caleb Connollyc58644d2024-07-15 12:08:21 +0200512 dev_err(dev, "Failed to bind RPMh regulator %s: %d\n", name, ret);
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200513 return ret;
514 }
515 }
516
517 return 0;
518}
519
Caleb Connollyc58644d2024-07-15 12:08:21 +0200520static const struct udevice_id rpmh_regulator_ids[] = {
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200521 {
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200522 .compatible = "qcom,pm8150-rpmh-regulators",
Caleb Connollyc58644d2024-07-15 12:08:21 +0200523 .data = (ulong)pm8150_vreg_data,
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200524 },
525 {
526 .compatible = "qcom,pm8150l-rpmh-regulators",
Caleb Connollyc58644d2024-07-15 12:08:21 +0200527 .data = (ulong)pm8150l_vreg_data,
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200528 },
Caleb Connollyc58644d2024-07-15 12:08:21 +0200529 { /* sentinal */ },
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200530};
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200531
Caleb Connollyc58644d2024-07-15 12:08:21 +0200532/* Driver for a 'bank' of regulators. This creates devices for each
533 * individual regulator
534 */
535U_BOOT_DRIVER(rpmh_regulators) = {
536 .name = "rpmh_regulators",
537 .id = UCLASS_MISC,
538 .bind = rpmh_regulators_bind,
539 .of_match = rpmh_regulator_ids,
540 .ops = &rpmh_regulator_vrm_drms_ops,
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200541};
Caleb Connollyc0e6aa62024-07-15 12:08:15 +0200542
543MODULE_DESCRIPTION("Qualcomm RPMh regulator driver");
544MODULE_LICENSE("GPL v2");