mxs: add support for 4.1
[openwrt.git] / target / linux / mxs / patches-4.1 / 102-mxs-add-regulator-driver.patch
1 diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
2 index a6f116a..7b525f5 100644
3 --- a/drivers/regulator/Kconfig
4 +++ b/drivers/regulator/Kconfig
5 @@ -450,6 +450,14 @@ config REGULATOR_MT6397
6           This driver supports the control of different power rails of device
7           through regulator interface.
8  
9 +config REGULATOR_MXS
10 +       tristate "Freescale MXS on-chip regulators"
11 +       depends on (MXS_POWER || COMPILE_TEST)
12 +       help
13 +         Say y here to support Freescale MXS on-chip regulators.
14 +         It is recommended that this option be enabled on i.MX23,
15 +         i.MX28 platform.
16 +
17  config REGULATOR_PALMAS
18         tristate "TI Palmas PMIC Regulators"
19         depends on MFD_PALMAS
20 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
21 index 2c4da15..a3ebf23 100644
22 --- a/drivers/regulator/Makefile
23 +++ b/drivers/regulator/Makefile
24 @@ -60,6 +60,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
25  obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
26  obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
27  obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
28 +obj-$(CONFIG_REGULATOR_MXS) += mxs-regulator.o
29  obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
30  obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
31  obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
32 diff --git a/drivers/regulator/mxs-regulator.c b/drivers/regulator/mxs-regulator.c
33 new file mode 100644
34 index 0000000..e53707b
35 --- /dev/null
36 +++ b/drivers/regulator/mxs-regulator.c
37 @@ -0,0 +1,540 @@
38 +/*
39 + * Freescale MXS on-chip regulators
40 + *
41 + * Embedded Alley Solutions, Inc <source@embeddedalley.com>
42 + *
43 + * Copyright (C) 2014 Stefan Wahren
44 + * Copyright (C) 2010 Freescale Semiconductor, Inc.
45 + * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
46 + *
47 + * Inspired by imx-bootlets
48 + */
49 +
50 +/*
51 + * The code contained herein is licensed under the GNU General Public
52 + * License. You may obtain a copy of the GNU General Public License
53 + * Version 2 or later at the following locations:
54 + *
55 + * http://www.opensource.org/licenses/gpl-license.html
56 + * http://www.gnu.org/copyleft/gpl.html
57 + */
58 +
59 +#include <linux/delay.h>
60 +#include <linux/device.h>
61 +#include <linux/err.h>
62 +#include <linux/kernel.h>
63 +#include <linux/mfd/syscon.h>
64 +#include <linux/module.h>
65 +#include <linux/of.h>
66 +#include <linux/of_device.h>
67 +#include <linux/platform_device.h>
68 +#include <linux/regmap.h>
69 +#include <linux/regulator/driver.h>
70 +#include <linux/regulator/machine.h>
71 +#include <linux/regulator/of_regulator.h>
72 +#include <linux/slab.h>
73 +
74 +/* Powered by linear regulator.  DCDC output is gated off and
75 +   the linreg output is equal to the target. */
76 +#define HW_POWER_LINREG_DCDC_OFF               1
77 +
78 +/* Powered by linear regulator.  DCDC output is not gated off
79 +   and is ready for the automatic hardware transistion after a 5V
80 +   event.  The converters are not enabled when 5V is present. LinReg output
81 +   is 25mV below target. */
82 +#define HW_POWER_LINREG_DCDC_READY             2
83 +
84 +/* Powered by DCDC converter and the LinReg is on. LinReg output
85 +   is 25mV below target. */
86 +#define HW_POWER_DCDC_LINREG_ON                        3
87 +
88 +/* Powered by DCDC converter and the LinReg is off. LinReg output
89 +   is 25mV below target. */
90 +#define HW_POWER_DCDC_LINREG_OFF               4
91 +
92 +/* Powered by DCDC converter and the LinReg is ready for the
93 +   automatic hardware transfer.  The LinReg output is not enabled and
94 +   depends on the 5V presence to enable the LinRegs.  LinReg offset is 25mV
95 +   below target. */
96 +#define HW_POWER_DCDC_LINREG_READY             5
97 +
98 +/* Powered by an external source when 5V is present.  This does not
99 +   necessarily mean the external source is powered by 5V,but the chip needs
100 +   to be aware that 5V is present. */
101 +#define HW_POWER_EXTERNAL_SOURCE_5V            6
102 +
103 +/* Powered by an external source when 5V is not present.This doesn't
104 +   necessarily mean the external source is powered by the battery, but the
105 +   chip needs to be aware that the battery is present */
106 +#define HW_POWER_EXTERNAL_SOURCE_BATTERY       7
107 +
108 +/* Unknown configuration.  This is an error. */
109 +#define HW_POWER_UNKNOWN_SOURCE                        8
110 +
111 +/* TODO: Move power register offsets into header file */
112 +#define HW_POWER_5VCTRL                0x00000010
113 +#define HW_POWER_VDDDCTRL      0x00000040
114 +#define HW_POWER_VDDACTRL      0x00000050
115 +#define HW_POWER_VDDIOCTRL     0x00000060
116 +#define HW_POWER_MISC          0x00000090
117 +#define HW_POWER_STS           0x000000c0
118 +
119 +#define BM_POWER_STS_VBUSVALID0_STATUS BIT(15)
120 +#define BM_POWER_STS_DC_OK             BIT(9)
121 +
122 +#define BM_POWER_5VCTRL_ILIMIT_EQ_ZERO BIT(2)
123 +#define BM_POWER_5VCTRL_ENABLE_DCDC    BIT(0)
124 +
125 +#define BM_POWER_LINREG_OFFSET_DCDC_MODE       BIT(1)
126 +
127 +#define SHIFT_FREQSEL                  4
128 +
129 +#define BM_POWER_MISC_FREQSEL          (7 << SHIFT_FREQSEL)
130 +
131 +/* Recommended DC-DC clock source values */
132 +#define HW_POWER_MISC_FREQSEL_20000_KHZ        1
133 +#define HW_POWER_MISC_FREQSEL_24000_KHZ        2
134 +#define HW_POWER_MISC_FREQSEL_19200_KHZ        3
135 +
136 +#define HW_POWER_MISC_SEL_PLLCLK       BIT(0)
137 +
138 +/* Regulator IDs */
139 +#define MXS_DCDC       1
140 +#define MXS_VDDIO      2
141 +#define MXS_VDDA       3
142 +#define MXS_VDDD       4
143 +
144 +struct mxs_reg_info {
145 +       /* regulator descriptor */
146 +       struct regulator_desc desc;
147 +
148 +       /* regulator control register */
149 +       int ctrl_reg;
150 +
151 +       /* disable DC-DC output */
152 +       unsigned int disable_fet_mask;
153 +
154 +       /* steps between linreg output and DC-DC target */
155 +       unsigned int linreg_offset_mask;
156 +       u8 linreg_offset_shift;
157 +
158 +       /* function which determine power source */
159 +       u8 (*get_power_source)(struct regulator_dev *);
160 +};
161 +
162 +static inline u8 get_linreg_offset(struct mxs_reg_info *ldo, u32 regs)
163 +{
164 +       return (regs & ldo->linreg_offset_mask) >> ldo->linreg_offset_shift;
165 +}
166 +
167 +static u8 get_vddio_power_source(struct regulator_dev *reg)
168 +{
169 +       struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
170 +       u32 v5ctrl, status, base;
171 +       u8 offset;
172 +
173 +       if (regmap_read(reg->regmap, HW_POWER_5VCTRL, &v5ctrl))
174 +               return HW_POWER_UNKNOWN_SOURCE;
175 +
176 +       if (regmap_read(reg->regmap, HW_POWER_STS, &status))
177 +               return HW_POWER_UNKNOWN_SOURCE;
178 +
179 +       if (regmap_read(reg->regmap, ldo->ctrl_reg, &base))
180 +               return HW_POWER_UNKNOWN_SOURCE;
181 +
182 +       offset = get_linreg_offset(ldo, base);
183 +
184 +       /* If VBUS valid then 5 V power supply present */
185 +       if (status & BM_POWER_STS_VBUSVALID0_STATUS) {
186 +               /* Powered by Linreg, DC-DC is off */
187 +               if ((base & ldo->disable_fet_mask) &&
188 +                   !(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)) {
189 +                       return HW_POWER_LINREG_DCDC_OFF;
190 +               }
191 +
192 +               if (v5ctrl & BM_POWER_5VCTRL_ENABLE_DCDC) {
193 +                       /* Powered by DC-DC, Linreg is on */
194 +                       if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)
195 +                               return HW_POWER_DCDC_LINREG_ON;
196 +               } else {
197 +                       /* Powered by Linreg, DC-DC is off */
198 +                       if (!(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE))
199 +                               return HW_POWER_LINREG_DCDC_OFF;
200 +               }
201 +       } else {
202 +               /* Powered by DC-DC, Linreg is on */
203 +               if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)
204 +                       return HW_POWER_DCDC_LINREG_ON;
205 +       }
206 +
207 +       return HW_POWER_UNKNOWN_SOURCE;
208 +}
209 +
210 +static u8 get_vdda_vddd_power_source(struct regulator_dev *reg)
211 +{
212 +       struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
213 +       struct regulator_desc *desc = &ldo->desc;
214 +       u32 v5ctrl, status, base;
215 +       u8 offset;
216 +
217 +       if (regmap_read(reg->regmap, HW_POWER_5VCTRL, &v5ctrl))
218 +               return HW_POWER_UNKNOWN_SOURCE;
219 +
220 +       if (regmap_read(reg->regmap, HW_POWER_STS, &status))
221 +               return HW_POWER_UNKNOWN_SOURCE;
222 +
223 +       if (regmap_read(reg->regmap, ldo->ctrl_reg, &base))
224 +               return HW_POWER_UNKNOWN_SOURCE;
225 +
226 +       offset = get_linreg_offset(ldo, base);
227 +
228 +       /* DC-DC output is disabled */
229 +       if (base & ldo->disable_fet_mask) {
230 +               /* Powered by 5 V supply */
231 +               if (status & BM_POWER_STS_VBUSVALID0_STATUS)
232 +                       return HW_POWER_EXTERNAL_SOURCE_5V;
233 +
234 +               /* Powered by Linreg, DC-DC is off */
235 +               if (!(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE))
236 +                       return HW_POWER_LINREG_DCDC_OFF;
237 +       }
238 +
239 +       /* If VBUS valid then 5 V power supply present */
240 +       if (status & BM_POWER_STS_VBUSVALID0_STATUS) {
241 +               /* Powered by DC-DC, Linreg is on */
242 +               if (v5ctrl & BM_POWER_5VCTRL_ENABLE_DCDC)
243 +                       return HW_POWER_DCDC_LINREG_ON;
244 +
245 +               /* Powered by Linreg, DC-DC is off */
246 +               return HW_POWER_LINREG_DCDC_OFF;
247 +       }
248 +
249 +       /* DC-DC is on */
250 +       if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE) {
251 +               /* Powered by DC-DC, Linreg is on */
252 +               if (base & desc->enable_mask)
253 +                       return HW_POWER_DCDC_LINREG_ON;
254 +
255 +               /* Powered by DC-DC, Linreg is off */
256 +               return HW_POWER_DCDC_LINREG_OFF;
257 +       }
258 +
259 +       return HW_POWER_UNKNOWN_SOURCE;
260 +}
261 +
262 +static int mxs_set_dcdc_freq(struct regulator_dev *reg, u32 hz)
263 +{
264 +       struct mxs_reg_info *dcdc = rdev_get_drvdata(reg);
265 +       u32 val;
266 +       int ret;
267 +
268 +       if (dcdc->desc.id != MXS_DCDC) {
269 +               dev_warn(&reg->dev, "Setting switching freq is not supported\n");
270 +               return -EINVAL;
271 +       }
272 +
273 +       ret = regmap_read(reg->regmap, HW_POWER_MISC, &val);
274 +       if (ret)
275 +               return ret;
276 +
277 +       val &= ~BM_POWER_MISC_FREQSEL;
278 +       val &= ~HW_POWER_MISC_SEL_PLLCLK;
279 +
280 +       /*
281 +        * Select the PLL/PFD based frequency that the DC-DC converter uses.
282 +        * The actual switching frequency driving the power inductor is
283 +        * DCDC_CLK/16. Accept only values recommend by Freescale.
284 +        */
285 +       switch (hz) {
286 +       case 1200000:
287 +               val |= HW_POWER_MISC_FREQSEL_19200_KHZ << SHIFT_FREQSEL;
288 +               break;
289 +       case 1250000:
290 +               val |= HW_POWER_MISC_FREQSEL_20000_KHZ << SHIFT_FREQSEL;
291 +               break;
292 +       case 1500000:
293 +               val |= HW_POWER_MISC_FREQSEL_24000_KHZ << SHIFT_FREQSEL;
294 +               break;
295 +       default:
296 +               dev_warn(&reg->dev, "Switching freq: %u Hz not supported\n",
297 +                        hz);
298 +               return -EINVAL;
299 +       }
300 +
301 +       /* First program FREQSEL */
302 +       ret = regmap_write(reg->regmap, HW_POWER_MISC, val);
303 +       if (ret)
304 +               return ret;
305 +
306 +       /* then set PLL as clock for DC-DC converter */
307 +       val |= HW_POWER_MISC_SEL_PLLCLK;
308 +
309 +       return regmap_write(reg->regmap, HW_POWER_MISC, val);
310 +}
311 +
312 +static int mxs_ldo_set_voltage_sel(struct regulator_dev *reg, unsigned sel)
313 +{
314 +       struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
315 +       struct regulator_desc *desc = &ldo->desc;
316 +       u32 status = 0;
317 +       int timeout;
318 +       int ret;
319 +
320 +       ret = regmap_update_bits(reg->regmap, desc->vsel_reg, desc->vsel_mask,
321 +                                sel);
322 +       if (ret)
323 +               return ret;
324 +
325 +       if (ldo->get_power_source) {
326 +               switch (ldo->get_power_source(reg)) {
327 +               case HW_POWER_LINREG_DCDC_OFF:
328 +               case HW_POWER_LINREG_DCDC_READY:
329 +               case HW_POWER_EXTERNAL_SOURCE_5V:
330 +                       /*
331 +                        * Since the DC-DC converter is off we can't
332 +                        * trigger on DC_OK. So wait at least 1 ms
333 +                        * for stabilization.
334 +                        */
335 +                       usleep_range(1000, 2000);
336 +                       return 0;
337 +               }
338 +       }
339 +
340 +       /* Make sure DC_OK has changed */
341 +       usleep_range(15, 20);
342 +
343 +       for (timeout = 0; timeout < 20; timeout++) {
344 +               ret = regmap_read(reg->regmap, HW_POWER_STS, &status);
345 +
346 +               if (ret)
347 +                       break;
348 +
349 +               /* DC-DC converter control loop has stabilized */
350 +               if (status & BM_POWER_STS_DC_OK)
351 +                       return 0;
352 +
353 +               udelay(1);
354 +       }
355 +
356 +       if (!ret)
357 +               dev_warn_ratelimited(&reg->dev, "%s: timeout status=0x%08x\n",
358 +                                    __func__, status);
359 +
360 +       msleep(20);
361 +
362 +       return -ETIMEDOUT;
363 +}
364 +
365 +static int mxs_ldo_is_enabled(struct regulator_dev *reg)
366 +{
367 +       struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
368 +
369 +       if (ldo->get_power_source) {
370 +               switch (ldo->get_power_source(reg)) {
371 +               case HW_POWER_LINREG_DCDC_OFF:
372 +               case HW_POWER_LINREG_DCDC_READY:
373 +               case HW_POWER_DCDC_LINREG_ON:
374 +                       return 1;
375 +               }
376 +       }
377 +
378 +       return 0;
379 +}
380 +
381 +static struct regulator_ops mxs_dcdc_ops = {
382 +       .is_enabled             = regulator_is_enabled_regmap,
383 +};
384 +
385 +static struct regulator_ops mxs_ldo_ops = {
386 +       .list_voltage           = regulator_list_voltage_linear,
387 +       .map_voltage            = regulator_map_voltage_linear,
388 +       .set_voltage_sel        = mxs_ldo_set_voltage_sel,
389 +       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
390 +       .is_enabled             = mxs_ldo_is_enabled,
391 +};
392 +
393 +static const struct mxs_reg_info mxs_info_dcdc = {
394 +       .desc = {
395 +               .name = "dcdc",
396 +               .id = MXS_DCDC,
397 +               .type = REGULATOR_VOLTAGE,
398 +               .owner = THIS_MODULE,
399 +               .ops = &mxs_dcdc_ops,
400 +               .enable_reg = HW_POWER_STS,
401 +               .enable_mask = (1 << 0),
402 +       },
403 +};
404 +
405 +static const struct mxs_reg_info imx23_info_vddio = {
406 +       .desc = {
407 +               .name = "vddio",
408 +               .id = MXS_VDDIO,
409 +               .type = REGULATOR_VOLTAGE,
410 +               .owner = THIS_MODULE,
411 +               .n_voltages = 0x20,
412 +               .uV_step = 25000,
413 +               .linear_min_sel = 0,
414 +               .min_uV = 2800000,
415 +               .vsel_reg = HW_POWER_VDDIOCTRL,
416 +               .vsel_mask = 0x1f,
417 +               .ops = &mxs_ldo_ops,
418 +       },
419 +       .ctrl_reg = HW_POWER_VDDIOCTRL,
420 +       .disable_fet_mask = 1 << 16,
421 +       .linreg_offset_mask = 3 << 12,
422 +       .linreg_offset_shift = 12,
423 +       .get_power_source = get_vddio_power_source,
424 +};
425 +
426 +static const struct mxs_reg_info imx28_info_vddio = {
427 +       .desc = {
428 +               .name = "vddio",
429 +               .id = MXS_VDDIO,
430 +               .type = REGULATOR_VOLTAGE,
431 +               .owner = THIS_MODULE,
432 +               .n_voltages = 0x11,
433 +               .uV_step = 50000,
434 +               .linear_min_sel = 0,
435 +               .min_uV = 2800000,
436 +               .vsel_reg = HW_POWER_VDDIOCTRL,
437 +               .vsel_mask = 0x1f,
438 +               .ops = &mxs_ldo_ops,
439 +       },
440 +       .ctrl_reg = HW_POWER_VDDIOCTRL,
441 +       .disable_fet_mask = 1 << 16,
442 +       .linreg_offset_mask = 3 << 12,
443 +       .linreg_offset_shift = 12,
444 +       .get_power_source = get_vddio_power_source,
445 +};
446 +
447 +static const struct mxs_reg_info mxs_info_vdda = {
448 +       .desc = {
449 +               .name = "vdda",
450 +               .id = MXS_VDDA,
451 +               .type = REGULATOR_VOLTAGE,
452 +               .owner = THIS_MODULE,
453 +               .n_voltages = 0x20,
454 +               .uV_step = 25000,
455 +               .linear_min_sel = 0,
456 +               .min_uV = 1500000,
457 +               .vsel_reg = HW_POWER_VDDACTRL,
458 +               .vsel_mask = 0x1f,
459 +               .ops = &mxs_ldo_ops,
460 +               .enable_mask = (1 << 17),
461 +       },
462 +       .ctrl_reg = HW_POWER_VDDACTRL,
463 +       .disable_fet_mask = 1 << 16,
464 +       .linreg_offset_mask = 3 << 12,
465 +       .linreg_offset_shift = 12,
466 +       .get_power_source = get_vdda_vddd_power_source,
467 +};
468 +
469 +static const struct mxs_reg_info mxs_info_vddd = {
470 +       .desc = {
471 +               .name = "vddd",
472 +               .id = MXS_VDDD,
473 +               .type = REGULATOR_VOLTAGE,
474 +               .owner = THIS_MODULE,
475 +               .n_voltages = 0x20,
476 +               .uV_step = 25000,
477 +               .linear_min_sel = 0,
478 +               .min_uV = 800000,
479 +               .vsel_reg = HW_POWER_VDDDCTRL,
480 +               .vsel_mask = 0x1f,
481 +               .ops = &mxs_ldo_ops,
482 +               .enable_mask = (1 << 21),
483 +       },
484 +       .ctrl_reg = HW_POWER_VDDDCTRL,
485 +       .disable_fet_mask = 1 << 20,
486 +       .linreg_offset_mask = 3 << 16,
487 +       .linreg_offset_shift = 16,
488 +       .get_power_source = get_vdda_vddd_power_source,
489 +};
490 +
491 +static const struct of_device_id of_mxs_regulator_match[] = {
492 +       { .compatible = "fsl,imx23-dcdc",  .data = &mxs_info_dcdc },
493 +       { .compatible = "fsl,imx28-dcdc",  .data = &mxs_info_dcdc },
494 +       { .compatible = "fsl,imx23-vddio", .data = &imx23_info_vddio },
495 +       { .compatible = "fsl,imx23-vdda",  .data = &mxs_info_vdda },
496 +       { .compatible = "fsl,imx23-vddd",  .data = &mxs_info_vddd },
497 +       { .compatible = "fsl,imx28-vddio", .data = &imx28_info_vddio },
498 +       { .compatible = "fsl,imx28-vdda",  .data = &mxs_info_vdda },
499 +       { .compatible = "fsl,imx28-vddd",  .data = &mxs_info_vddd },
500 +       { /* end */ }
501 +};
502 +MODULE_DEVICE_TABLE(of, of_mxs_regulator_match);
503 +
504 +static int mxs_regulator_probe(struct platform_device *pdev)
505 +{
506 +       struct device *dev = &pdev->dev;
507 +       const struct of_device_id *match;
508 +       struct device_node *parent_np;
509 +       struct regulator_dev *rdev = NULL;
510 +       struct mxs_reg_info *info;
511 +       struct regulator_init_data *initdata;
512 +       struct regulator_config config = { };
513 +       u32 switch_freq;
514 +
515 +       match = of_match_device(of_mxs_regulator_match, dev);
516 +       if (!match) {
517 +               /* We do not expect this to happen */
518 +               dev_err(dev, "%s: Unable to match device\n", __func__);
519 +               return -ENODEV;
520 +       }
521 +
522 +       info = devm_kmemdup(dev, match->data, sizeof(struct mxs_reg_info),
523 +                           GFP_KERNEL);
524 +       if (!info)
525 +               return -ENOMEM;
526 +
527 +       initdata = of_get_regulator_init_data(dev, dev->of_node, &info->desc);
528 +       if (!initdata) {
529 +               dev_err(dev, "missing regulator init data\n");
530 +               return -EINVAL;
531 +       }
532 +
533 +       parent_np = of_get_parent(dev->of_node);
534 +       if (!parent_np)
535 +               return -ENODEV;
536 +       config.regmap = syscon_node_to_regmap(parent_np);
537 +       of_node_put(parent_np);
538 +       if (IS_ERR(config.regmap))
539 +               return PTR_ERR(config.regmap);
540 +
541 +       config.dev = dev;
542 +       config.init_data = initdata;
543 +       config.driver_data = info;
544 +       config.of_node = dev->of_node;
545 +
546 +       rdev = devm_regulator_register(dev, &info->desc, &config);
547 +       if (IS_ERR(rdev)) {
548 +               int ret = PTR_ERR(rdev);
549 +
550 +               dev_err(dev, "%s: failed to register regulator(%d)\n",
551 +                       __func__, ret);
552 +               return ret;
553 +       }
554 +
555 +       if (!of_property_read_u32(dev->of_node, "switching-frequency",
556 +                                 &switch_freq))
557 +               mxs_set_dcdc_freq(rdev, switch_freq);
558 +
559 +       platform_set_drvdata(pdev, rdev);
560 +
561 +       return 0;
562 +}
563 +
564 +static struct platform_driver mxs_regulator_driver = {
565 +       .driver = {
566 +               .name   = "mxs_regulator",
567 +               .of_match_table = of_mxs_regulator_match,
568 +       },
569 +       .probe  = mxs_regulator_probe,
570 +};
571 +
572 +module_platform_driver(mxs_regulator_driver);
573 +
574 +MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
575 +MODULE_DESCRIPTION("Freescale MXS regulators");
576 +MODULE_LICENSE("GPL v2");
577 +MODULE_ALIAS("platform:mxs_regulator");