aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Kaehlcke <mka@chromium.org>2017-04-07 15:51:58 -0400
committerMark Brown <broonie@kernel.org>2017-04-10 12:30:40 -0400
commit9dee7a72d0c7cdfa2573c48b1e5f928c721d54d5 (patch)
treeef2cd230ca98ee074faf3698ddac6c7380d4b22e
parentd00b74613fb18dfd0a5aa99270ee2e72d5c808d7 (diff)
regulator: Add driver for voltage controlled regulators
The output voltage of a voltage controlled regulator can be controlled through the voltage of another regulator. The current version of this driver assumes that the output voltage is a linear function of the control voltage. Signed-off-by: Matthias Kaehlcke <mka@chromium.org> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--Documentation/devicetree/bindings/regulator/vctrl.txt49
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/vctrl-regulator.c546
4 files changed, 603 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/regulator/vctrl.txt b/Documentation/devicetree/bindings/regulator/vctrl.txt
new file mode 100644
index 000000000000..601328d7fdbb
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/vctrl.txt
@@ -0,0 +1,49 @@
1Bindings for Voltage controlled regulators
2==========================================
3
4Required properties:
5--------------------
6- compatible : must be "vctrl-regulator".
7- regulator-min-microvolt : smallest voltage consumers may set
8- regulator-max-microvolt : largest voltage consumers may set
9- ctrl-supply : The regulator supplying the control voltage.
10- ctrl-voltage-range : an array of two integer values describing the range
11 (min/max) of the control voltage. The values specify
12 the control voltage needed to generate the corresponding
13 regulator-min/max-microvolt output voltage.
14
15Optional properties:
16--------------------
17- ovp-threshold-percent : overvoltage protection (OVP) threshold of the
18 regulator in percent. Some regulators have an OVP
19 circuitry which shuts down the regulator when the
20 actual output voltage deviates beyond a certain
21 margin from the expected value for a given control
22 voltage. On larger voltage decreases this can occur
23 undesiredly since the output voltage does not adjust
24 inmediately to changes in the control voltage. To
25 avoid this situation the vctrl driver breaks down
26 larger voltage decreases into multiple steps, where
27 each step is within the OVP threshold.
28- min-slew-down-rate : Describes how slowly the regulator voltage will decay
29 down in the worst case (lightest expected load).
30 Specified in uV / us (like main regulator ramp rate).
31 This value is required when ovp-threshold-percent is
32 specified.
33
34Example:
35
36 vctrl-reg {
37 compatible = "vctrl-regulator";
38 regulator-name = "vctrl_reg";
39
40 ctrl-supply = <&ctrl_reg>;
41
42 regulator-min-microvolt = <800000>;
43 regulator-max-microvolt = <1500000>;
44
45 ctrl-voltage-range = <200000 500000>;
46
47 min-slew-down-rate = <225>;
48 ovp-threshold-percent = <16>;
49 };
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 936f7ccc9736..da83a3abe288 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -843,6 +843,13 @@ config REGULATOR_TWL4030
843 This driver supports the voltage regulators provided by 843 This driver supports the voltage regulators provided by
844 this family of companion chips. 844 this family of companion chips.
845 845
846config REGULATOR_VCTRL
847 tristate "Voltage controlled regulators"
848 depends on OF
849 help
850 This driver provides support for voltage regulators whose output
851 voltage is controlled by the voltage of another regulator.
852
846config REGULATOR_VEXPRESS 853config REGULATOR_VEXPRESS
847 tristate "Versatile Express regulators" 854 tristate "Versatile Express regulators"
848 depends on VEXPRESS_CONFIG 855 depends on VEXPRESS_CONFIG
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 14294692beb9..e246e148a7f9 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -105,6 +105,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
105obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o 105obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
106obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o 106obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
107obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o 107obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
108obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
108obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o 109obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
109obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o 110obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
110obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o 111obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c
new file mode 100644
index 000000000000..6baadef0ed74
--- /dev/null
+++ b/drivers/regulator/vctrl-regulator.c
@@ -0,0 +1,546 @@
1/*
2 * Driver for voltage controller regulators
3 *
4 * Copyright (C) 2017 Google, Inc.
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/delay.h>
17#include <linux/err.h>
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/of_device.h>
22#include <linux/regulator/driver.h>
23#include <linux/regulator/of_regulator.h>
24#include <linux/sort.h>
25
26struct vctrl_voltage_range {
27 int min_uV;
28 int max_uV;
29};
30
31struct vctrl_voltage_ranges {
32 struct vctrl_voltage_range ctrl;
33 struct vctrl_voltage_range out;
34};
35
36struct vctrl_voltage_table {
37 int ctrl;
38 int out;
39 int ovp_min_sel;
40};
41
42struct vctrl_data {
43 struct regulator_dev *rdev;
44 struct regulator_desc desc;
45 struct regulator *ctrl_reg;
46 bool enabled;
47 unsigned int min_slew_down_rate;
48 unsigned int ovp_threshold;
49 struct vctrl_voltage_ranges vrange;
50 struct vctrl_voltage_table *vtable;
51 unsigned int sel;
52};
53
54static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV)
55{
56 struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
57 struct vctrl_voltage_range *out = &vctrl->vrange.out;
58
59 return ctrl->min_uV +
60 DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) *
61 (ctrl->max_uV - ctrl->min_uV),
62 out->max_uV - out->min_uV);
63}
64
65static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV)
66{
67 struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
68 struct vctrl_voltage_range *out = &vctrl->vrange.out;
69
70 if (ctrl_uV < 0) {
71 pr_err("vctrl: failed to get control voltage\n");
72 return ctrl_uV;
73 }
74
75 if (ctrl_uV < ctrl->min_uV)
76 return out->min_uV;
77
78 if (ctrl_uV > ctrl->max_uV)
79 return out->max_uV;
80
81 return out->min_uV +
82 DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) *
83 (out->max_uV - out->min_uV),
84 ctrl->max_uV - ctrl->min_uV);
85}
86
87static int vctrl_get_voltage(struct regulator_dev *rdev)
88{
89 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
90 int ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
91
92 return vctrl_calc_output_voltage(vctrl, ctrl_uV);
93}
94
95static int vctrl_set_voltage(struct regulator_dev *rdev,
96 int req_min_uV, int req_max_uV,
97 unsigned int *selector)
98{
99 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
100 struct regulator *ctrl_reg = vctrl->ctrl_reg;
101 int orig_ctrl_uV = regulator_get_voltage(ctrl_reg);
102 int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);
103 int ret;
104
105 if (req_min_uV >= uV || !vctrl->ovp_threshold)
106 /* voltage rising or no OVP */
107 return regulator_set_voltage(
108 ctrl_reg,
109 vctrl_calc_ctrl_voltage(vctrl, req_min_uV),
110 vctrl_calc_ctrl_voltage(vctrl, req_max_uV));
111
112 while (uV > req_min_uV) {
113 int max_drop_uV = (uV * vctrl->ovp_threshold) / 100;
114 int next_uV;
115 int next_ctrl_uV;
116 int delay;
117
118 /* Make sure no infinite loop even in crazy cases */
119 if (max_drop_uV == 0)
120 max_drop_uV = 1;
121
122 next_uV = max_t(int, req_min_uV, uV - max_drop_uV);
123 next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV);
124
125 ret = regulator_set_voltage(ctrl_reg,
126 next_ctrl_uV,
127 next_ctrl_uV);
128 if (ret)
129 goto err;
130
131 delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate);
132 usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
133
134 uV = next_uV;
135 }
136
137 return 0;
138
139err:
140 /* Try to go back to original voltage */
141 regulator_set_voltage(ctrl_reg, orig_ctrl_uV, orig_ctrl_uV);
142
143 return ret;
144}
145
146static int vctrl_get_voltage_sel(struct regulator_dev *rdev)
147{
148 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
149
150 return vctrl->sel;
151}
152
153static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
154 unsigned int selector)
155{
156 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
157 struct regulator *ctrl_reg = vctrl->ctrl_reg;
158 unsigned int orig_sel = vctrl->sel;
159 int ret;
160
161 if (selector >= rdev->desc->n_voltages)
162 return -EINVAL;
163
164 if (selector >= vctrl->sel || !vctrl->ovp_threshold) {
165 /* voltage rising or no OVP */
166 ret = regulator_set_voltage(ctrl_reg,
167 vctrl->vtable[selector].ctrl,
168 vctrl->vtable[selector].ctrl);
169 if (!ret)
170 vctrl->sel = selector;
171
172 return ret;
173 }
174
175 while (vctrl->sel != selector) {
176 unsigned int next_sel;
177 int delay;
178
179 if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel)
180 next_sel = selector;
181 else
182 next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel;
183
184 ret = regulator_set_voltage(ctrl_reg,
185 vctrl->vtable[next_sel].ctrl,
186 vctrl->vtable[next_sel].ctrl);
187 if (ret) {
188 dev_err(&rdev->dev,
189 "failed to set control voltage to %duV\n",
190 vctrl->vtable[next_sel].ctrl);
191 goto err;
192 }
193 vctrl->sel = next_sel;
194
195 delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out -
196 vctrl->vtable[next_sel].out,
197 vctrl->min_slew_down_rate);
198 usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
199 }
200
201 return 0;
202
203err:
204 if (vctrl->sel != orig_sel) {
205 /* Try to go back to original voltage */
206 if (!regulator_set_voltage(ctrl_reg,
207 vctrl->vtable[orig_sel].ctrl,
208 vctrl->vtable[orig_sel].ctrl))
209 vctrl->sel = orig_sel;
210 else
211 dev_warn(&rdev->dev,
212 "failed to restore original voltage\n");
213 }
214
215 return ret;
216}
217
218static int vctrl_list_voltage(struct regulator_dev *rdev,
219 unsigned int selector)
220{
221 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
222
223 if (selector >= rdev->desc->n_voltages)
224 return -EINVAL;
225
226 return vctrl->vtable[selector].out;
227}
228
229static int vctrl_parse_dt(struct platform_device *pdev,
230 struct vctrl_data *vctrl)
231{
232 int ret;
233 struct device_node *np = pdev->dev.of_node;
234 u32 pval;
235 u32 vrange_ctrl[2];
236
237 vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl");
238 if (IS_ERR(vctrl->ctrl_reg))
239 return PTR_ERR(vctrl->ctrl_reg);
240
241 ret = of_property_read_u32(np, "ovp-threshold-percent", &pval);
242 if (!ret) {
243 vctrl->ovp_threshold = pval;
244 if (vctrl->ovp_threshold > 100) {
245 dev_err(&pdev->dev,
246 "ovp-threshold-percent (%u) > 100\n",
247 vctrl->ovp_threshold);
248 return -EINVAL;
249 }
250 }
251
252 ret = of_property_read_u32(np, "min-slew-down-rate", &pval);
253 if (!ret) {
254 vctrl->min_slew_down_rate = pval;
255
256 /* We use the value as int and as divider; sanity check */
257 if (vctrl->min_slew_down_rate == 0) {
258 dev_err(&pdev->dev,
259 "min-slew-down-rate must not be 0\n");
260 return -EINVAL;
261 } else if (vctrl->min_slew_down_rate > INT_MAX) {
262 dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n",
263 vctrl->min_slew_down_rate);
264 return -EINVAL;
265 }
266 }
267
268 if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) {
269 dev_err(&pdev->dev,
270 "ovp-threshold-percent requires min-slew-down-rate\n");
271 return -EINVAL;
272 }
273
274 ret = of_property_read_u32(np, "regulator-min-microvolt", &pval);
275 if (ret) {
276 dev_err(&pdev->dev,
277 "failed to read regulator-min-microvolt: %d\n", ret);
278 return ret;
279 }
280 vctrl->vrange.out.min_uV = pval;
281
282 ret = of_property_read_u32(np, "regulator-max-microvolt", &pval);
283 if (ret) {
284 dev_err(&pdev->dev,
285 "failed to read regulator-max-microvolt: %d\n", ret);
286 return ret;
287 }
288 vctrl->vrange.out.max_uV = pval;
289
290 ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl,
291 2);
292 if (ret) {
293 dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n",
294 ret);
295 return ret;
296 }
297
298 if (vrange_ctrl[0] >= vrange_ctrl[1]) {
299 dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n",
300 vrange_ctrl[0], vrange_ctrl[1]);
301 return -EINVAL;
302 }
303
304 vctrl->vrange.ctrl.min_uV = vrange_ctrl[0];
305 vctrl->vrange.ctrl.max_uV = vrange_ctrl[1];
306
307 return 0;
308}
309
310static int vctrl_cmp_ctrl_uV(const void *a, const void *b)
311{
312 const struct vctrl_voltage_table *at = a;
313 const struct vctrl_voltage_table *bt = b;
314
315 return at->ctrl - bt->ctrl;
316}
317
318static int vctrl_init_vtable(struct platform_device *pdev)
319{
320 struct vctrl_data *vctrl = platform_get_drvdata(pdev);
321 struct regulator_desc *rdesc = &vctrl->desc;
322 struct regulator *ctrl_reg = vctrl->ctrl_reg;
323 struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl;
324 int n_voltages;
325 int ctrl_uV;
326 int i, idx_vt;
327
328 n_voltages = regulator_count_voltages(ctrl_reg);
329
330 rdesc->n_voltages = n_voltages;
331
332 /* determine number of steps within the range of the vctrl regulator */
333 for (i = 0; i < n_voltages; i++) {
334 ctrl_uV = regulator_list_voltage(ctrl_reg, i);
335
336 if (ctrl_uV < vrange_ctrl->min_uV ||
337 ctrl_uV > vrange_ctrl->max_uV) {
338 rdesc->n_voltages--;
339 continue;
340 }
341 }
342
343 if (rdesc->n_voltages == 0) {
344 dev_err(&pdev->dev, "invalid configuration\n");
345 return -EINVAL;
346 }
347
348 vctrl->vtable = devm_kmalloc_array(
349 &pdev->dev, sizeof(struct vctrl_voltage_table),
350 rdesc->n_voltages, GFP_KERNEL | __GFP_ZERO);
351 if (!vctrl->vtable)
352 return -ENOMEM;
353
354 /* create mapping control <=> output voltage */
355 for (i = 0, idx_vt = 0; i < n_voltages; i++) {
356 ctrl_uV = regulator_list_voltage(ctrl_reg, i);
357
358 if (ctrl_uV < vrange_ctrl->min_uV ||
359 ctrl_uV > vrange_ctrl->max_uV)
360 continue;
361
362 vctrl->vtable[idx_vt].ctrl = ctrl_uV;
363 vctrl->vtable[idx_vt].out =
364 vctrl_calc_output_voltage(vctrl, ctrl_uV);
365 idx_vt++;
366 }
367
368 /* we rely on the table to be ordered by ascending voltage */
369 sort(vctrl->vtable, rdesc->n_voltages,
370 sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV,
371 NULL);
372
373 /* pre-calculate OVP-safe downward transitions */
374 for (i = n_voltages - 1; i > 0; i--) {
375 int j;
376 int ovp_min_uV = (vctrl->vtable[i].out *
377 (100 - vctrl->ovp_threshold)) / 100;
378
379 for (j = 0; j < i; j++) {
380 if (vctrl->vtable[j].out >= ovp_min_uV) {
381 vctrl->vtable[i].ovp_min_sel = j;
382 break;
383 }
384 }
385
386 if (j == i) {
387 dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n",
388 vctrl->vtable[i].out);
389 /* use next lowest voltage */
390 vctrl->vtable[i].ovp_min_sel = i - 1;
391 }
392 }
393
394 return 0;
395}
396
397static int vctrl_enable(struct regulator_dev *rdev)
398{
399 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
400 int ret = regulator_enable(vctrl->ctrl_reg);
401
402 if (!ret)
403 vctrl->enabled = true;
404
405 return ret;
406}
407
408static int vctrl_disable(struct regulator_dev *rdev)
409{
410 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
411 int ret = regulator_disable(vctrl->ctrl_reg);
412
413 if (!ret)
414 vctrl->enabled = false;
415
416 return ret;
417}
418
419static int vctrl_is_enabled(struct regulator_dev *rdev)
420{
421 struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
422
423 return vctrl->enabled;
424}
425
426static const struct regulator_ops vctrl_ops_cont = {
427 .enable = vctrl_enable,
428 .disable = vctrl_disable,
429 .is_enabled = vctrl_is_enabled,
430 .get_voltage = vctrl_get_voltage,
431 .set_voltage = vctrl_set_voltage,
432};
433
434static const struct regulator_ops vctrl_ops_non_cont = {
435 .enable = vctrl_enable,
436 .disable = vctrl_disable,
437 .is_enabled = vctrl_is_enabled,
438 .set_voltage_sel = vctrl_set_voltage_sel,
439 .get_voltage_sel = vctrl_get_voltage_sel,
440 .list_voltage = vctrl_list_voltage,
441 .map_voltage = regulator_map_voltage_iterate,
442};
443
444static int vctrl_probe(struct platform_device *pdev)
445{
446 struct device_node *np = pdev->dev.of_node;
447 struct vctrl_data *vctrl;
448 const struct regulator_init_data *init_data;
449 struct regulator_desc *rdesc;
450 struct regulator_config cfg = { };
451 struct vctrl_voltage_range *vrange_ctrl;
452 int ctrl_uV;
453 int ret;
454
455 vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data),
456 GFP_KERNEL);
457 if (!vctrl)
458 return -ENOMEM;
459
460 platform_set_drvdata(pdev, vctrl);
461
462 ret = vctrl_parse_dt(pdev, vctrl);
463 if (ret)
464 return ret;
465
466 vrange_ctrl = &vctrl->vrange.ctrl;
467
468 rdesc = &vctrl->desc;
469 rdesc->name = "vctrl";
470 rdesc->type = REGULATOR_VOLTAGE;
471 rdesc->owner = THIS_MODULE;
472
473 if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) ||
474 (regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) {
475 rdesc->continuous_voltage_range = true;
476 rdesc->ops = &vctrl_ops_cont;
477 } else {
478 rdesc->ops = &vctrl_ops_non_cont;
479 }
480
481 init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc);
482 if (!init_data)
483 return -ENOMEM;
484
485 cfg.of_node = np;
486 cfg.dev = &pdev->dev;
487 cfg.driver_data = vctrl;
488 cfg.init_data = init_data;
489
490 if (!rdesc->continuous_voltage_range) {
491 ret = vctrl_init_vtable(pdev);
492 if (ret)
493 return ret;
494
495 ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
496 if (ctrl_uV < 0) {
497 dev_err(&pdev->dev, "failed to get control voltage\n");
498 return ctrl_uV;
499 }
500
501 /* determine current voltage selector from control voltage */
502 if (ctrl_uV < vrange_ctrl->min_uV) {
503 vctrl->sel = 0;
504 } else if (ctrl_uV > vrange_ctrl->max_uV) {
505 vctrl->sel = rdesc->n_voltages - 1;
506 } else {
507 int i;
508
509 for (i = 0; i < rdesc->n_voltages; i++) {
510 if (ctrl_uV == vctrl->vtable[i].ctrl) {
511 vctrl->sel = i;
512 break;
513 }
514 }
515 }
516 }
517
518 vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg);
519 if (IS_ERR(vctrl->rdev)) {
520 ret = PTR_ERR(vctrl->rdev);
521 dev_err(&pdev->dev, "failed to register regulator: %d\n", ret);
522 return ret;
523 }
524
525 return 0;
526}
527
528static const struct of_device_id vctrl_of_match[] = {
529 { .compatible = "vctrl-regulator", },
530 {},
531};
532MODULE_DEVICE_TABLE(of, vctrl_of_match);
533
534static struct platform_driver vctrl_driver = {
535 .probe = vctrl_probe,
536 .driver = {
537 .name = "vctrl-regulator",
538 .of_match_table = of_match_ptr(vctrl_of_match),
539 },
540};
541
542module_platform_driver(vctrl_driver);
543
544MODULE_DESCRIPTION("Voltage Controlled Regulator Driver");
545MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
546MODULE_LICENSE("GPL v2");