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.
10 + tristate "Freescale MXS on-chip regulators"
11 + depends on (MXS_POWER || COMPILE_TEST)
13 + Say y here to support Freescale MXS on-chip regulators.
14 + It is recommended that this option be enabled on i.MX23,
17 config REGULATOR_PALMAS
18 tristate "TI Palmas PMIC Regulators"
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
34 index 0000000..e53707b
36 +++ b/drivers/regulator/mxs-regulator.c
39 + * Freescale MXS on-chip regulators
41 + * Embedded Alley Solutions, Inc <source@embeddedalley.com>
43 + * Copyright (C) 2014 Stefan Wahren
44 + * Copyright (C) 2010 Freescale Semiconductor, Inc.
45 + * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
47 + * Inspired by imx-bootlets
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:
55 + * http://www.opensource.org/licenses/gpl-license.html
56 + * http://www.gnu.org/copyleft/gpl.html
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>
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
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
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
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
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
96 +#define HW_POWER_DCDC_LINREG_READY 5
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
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
108 +/* Unknown configuration. This is an error. */
109 +#define HW_POWER_UNKNOWN_SOURCE 8
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
119 +#define BM_POWER_STS_VBUSVALID0_STATUS BIT(15)
120 +#define BM_POWER_STS_DC_OK BIT(9)
122 +#define BM_POWER_5VCTRL_ILIMIT_EQ_ZERO BIT(2)
123 +#define BM_POWER_5VCTRL_ENABLE_DCDC BIT(0)
125 +#define BM_POWER_LINREG_OFFSET_DCDC_MODE BIT(1)
127 +#define SHIFT_FREQSEL 4
129 +#define BM_POWER_MISC_FREQSEL (7 << SHIFT_FREQSEL)
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
136 +#define HW_POWER_MISC_SEL_PLLCLK BIT(0)
144 +struct mxs_reg_info {
145 + /* regulator descriptor */
146 + struct regulator_desc desc;
148 + /* regulator control register */
151 + /* disable DC-DC output */
152 + unsigned int disable_fet_mask;
154 + /* steps between linreg output and DC-DC target */
155 + unsigned int linreg_offset_mask;
156 + u8 linreg_offset_shift;
158 + /* function which determine power source */
159 + u8 (*get_power_source)(struct regulator_dev *);
162 +static inline u8 get_linreg_offset(struct mxs_reg_info *ldo, u32 regs)
164 + return (regs & ldo->linreg_offset_mask) >> ldo->linreg_offset_shift;
167 +static u8 get_vddio_power_source(struct regulator_dev *reg)
169 + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
170 + u32 v5ctrl, status, base;
173 + if (regmap_read(reg->regmap, HW_POWER_5VCTRL, &v5ctrl))
174 + return HW_POWER_UNKNOWN_SOURCE;
176 + if (regmap_read(reg->regmap, HW_POWER_STS, &status))
177 + return HW_POWER_UNKNOWN_SOURCE;
179 + if (regmap_read(reg->regmap, ldo->ctrl_reg, &base))
180 + return HW_POWER_UNKNOWN_SOURCE;
182 + offset = get_linreg_offset(ldo, base);
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;
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;
197 + /* Powered by Linreg, DC-DC is off */
198 + if (!(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE))
199 + return HW_POWER_LINREG_DCDC_OFF;
202 + /* Powered by DC-DC, Linreg is on */
203 + if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)
204 + return HW_POWER_DCDC_LINREG_ON;
207 + return HW_POWER_UNKNOWN_SOURCE;
210 +static u8 get_vdda_vddd_power_source(struct regulator_dev *reg)
212 + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
213 + struct regulator_desc *desc = &ldo->desc;
214 + u32 v5ctrl, status, base;
217 + if (regmap_read(reg->regmap, HW_POWER_5VCTRL, &v5ctrl))
218 + return HW_POWER_UNKNOWN_SOURCE;
220 + if (regmap_read(reg->regmap, HW_POWER_STS, &status))
221 + return HW_POWER_UNKNOWN_SOURCE;
223 + if (regmap_read(reg->regmap, ldo->ctrl_reg, &base))
224 + return HW_POWER_UNKNOWN_SOURCE;
226 + offset = get_linreg_offset(ldo, base);
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;
234 + /* Powered by Linreg, DC-DC is off */
235 + if (!(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE))
236 + return HW_POWER_LINREG_DCDC_OFF;
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;
245 + /* Powered by Linreg, DC-DC is off */
246 + return HW_POWER_LINREG_DCDC_OFF;
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;
255 + /* Powered by DC-DC, Linreg is off */
256 + return HW_POWER_DCDC_LINREG_OFF;
259 + return HW_POWER_UNKNOWN_SOURCE;
262 +static int mxs_set_dcdc_freq(struct regulator_dev *reg, u32 hz)
264 + struct mxs_reg_info *dcdc = rdev_get_drvdata(reg);
268 + if (dcdc->desc.id != MXS_DCDC) {
269 + dev_warn(®->dev, "Setting switching freq is not supported\n");
273 + ret = regmap_read(reg->regmap, HW_POWER_MISC, &val);
277 + val &= ~BM_POWER_MISC_FREQSEL;
278 + val &= ~HW_POWER_MISC_SEL_PLLCLK;
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.
287 + val |= HW_POWER_MISC_FREQSEL_19200_KHZ << SHIFT_FREQSEL;
290 + val |= HW_POWER_MISC_FREQSEL_20000_KHZ << SHIFT_FREQSEL;
293 + val |= HW_POWER_MISC_FREQSEL_24000_KHZ << SHIFT_FREQSEL;
296 + dev_warn(®->dev, "Switching freq: %u Hz not supported\n",
301 + /* First program FREQSEL */
302 + ret = regmap_write(reg->regmap, HW_POWER_MISC, val);
306 + /* then set PLL as clock for DC-DC converter */
307 + val |= HW_POWER_MISC_SEL_PLLCLK;
309 + return regmap_write(reg->regmap, HW_POWER_MISC, val);
312 +static int mxs_ldo_set_voltage_sel(struct regulator_dev *reg, unsigned sel)
314 + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
315 + struct regulator_desc *desc = &ldo->desc;
320 + ret = regmap_update_bits(reg->regmap, desc->vsel_reg, desc->vsel_mask,
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:
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.
335 + usleep_range(1000, 2000);
340 + /* Make sure DC_OK has changed */
341 + usleep_range(15, 20);
343 + for (timeout = 0; timeout < 20; timeout++) {
344 + ret = regmap_read(reg->regmap, HW_POWER_STS, &status);
349 + /* DC-DC converter control loop has stabilized */
350 + if (status & BM_POWER_STS_DC_OK)
357 + dev_warn_ratelimited(®->dev, "%s: timeout status=0x%08x\n",
365 +static int mxs_ldo_is_enabled(struct regulator_dev *reg)
367 + struct mxs_reg_info *ldo = rdev_get_drvdata(reg);
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:
381 +static struct regulator_ops mxs_dcdc_ops = {
382 + .is_enabled = regulator_is_enabled_regmap,
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,
393 +static const struct mxs_reg_info mxs_info_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),
405 +static const struct mxs_reg_info imx23_info_vddio = {
409 + .type = REGULATOR_VOLTAGE,
410 + .owner = THIS_MODULE,
411 + .n_voltages = 0x20,
413 + .linear_min_sel = 0,
415 + .vsel_reg = HW_POWER_VDDIOCTRL,
417 + .ops = &mxs_ldo_ops,
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,
426 +static const struct mxs_reg_info imx28_info_vddio = {
430 + .type = REGULATOR_VOLTAGE,
431 + .owner = THIS_MODULE,
432 + .n_voltages = 0x11,
434 + .linear_min_sel = 0,
436 + .vsel_reg = HW_POWER_VDDIOCTRL,
438 + .ops = &mxs_ldo_ops,
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,
447 +static const struct mxs_reg_info mxs_info_vdda = {
451 + .type = REGULATOR_VOLTAGE,
452 + .owner = THIS_MODULE,
453 + .n_voltages = 0x20,
455 + .linear_min_sel = 0,
457 + .vsel_reg = HW_POWER_VDDACTRL,
459 + .ops = &mxs_ldo_ops,
460 + .enable_mask = (1 << 17),
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,
469 +static const struct mxs_reg_info mxs_info_vddd = {
473 + .type = REGULATOR_VOLTAGE,
474 + .owner = THIS_MODULE,
475 + .n_voltages = 0x20,
477 + .linear_min_sel = 0,
479 + .vsel_reg = HW_POWER_VDDDCTRL,
481 + .ops = &mxs_ldo_ops,
482 + .enable_mask = (1 << 21),
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,
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 },
502 +MODULE_DEVICE_TABLE(of, of_mxs_regulator_match);
504 +static int mxs_regulator_probe(struct platform_device *pdev)
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 = { };
515 + match = of_match_device(of_mxs_regulator_match, dev);
517 + /* We do not expect this to happen */
518 + dev_err(dev, "%s: Unable to match device\n", __func__);
522 + info = devm_kmemdup(dev, match->data, sizeof(struct mxs_reg_info),
527 + initdata = of_get_regulator_init_data(dev, dev->of_node, &info->desc);
529 + dev_err(dev, "missing regulator init data\n");
533 + parent_np = of_get_parent(dev->of_node);
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);
542 + config.init_data = initdata;
543 + config.driver_data = info;
544 + config.of_node = dev->of_node;
546 + rdev = devm_regulator_register(dev, &info->desc, &config);
547 + if (IS_ERR(rdev)) {
548 + int ret = PTR_ERR(rdev);
550 + dev_err(dev, "%s: failed to register regulator(%d)\n",
555 + if (!of_property_read_u32(dev->of_node, "switching-frequency",
557 + mxs_set_dcdc_freq(rdev, switch_freq);
559 + platform_set_drvdata(pdev, rdev);
564 +static struct platform_driver mxs_regulator_driver = {
566 + .name = "mxs_regulator",
567 + .of_match_table = of_mxs_regulator_match,
569 + .probe = mxs_regulator_probe,
572 +module_platform_driver(mxs_regulator_driver);
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");