Last active
March 23, 2016 08:34
-
-
Save joerg-krause/11fc3fcf7a9cdc1ae661 to your computer and use it in GitHub Desktop.
Linux mxs power and regulator patches
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From d4437fcdf16cfc74bb2388d02cf754461eef42fd Mon Sep 17 00:00:00 2001 | |
From: =?UTF-8?q?J=C3=B6rg=20Krause?= <joerg.krause@embedded.rocks> | |
Date: Tue, 22 Mar 2016 10:35:05 +0100 | |
Subject: [PATCH 100/102] mxs: add power | |
MIME-Version: 1.0 | |
Content-Type: text/plain; charset=UTF-8 | |
Content-Transfer-Encoding: 8bit | |
Signed-off-by: Jörg Krause <joerg.krause@embedded.rocks> | |
--- | |
drivers/power/Kconfig | 10 ++ | |
drivers/power/Makefile | 1 + | |
drivers/power/mxs-power-debug.c | 388 ++++++++++++++++++++++++++++++++++++++++ | |
drivers/power/mxs-power.c | 277 ++++++++++++++++++++++++++++ | |
include/linux/power/mxs_power.h | 99 ++++++++++ | |
5 files changed, 775 insertions(+) | |
create mode 100644 drivers/power/mxs-power-debug.c | |
create mode 100644 drivers/power/mxs-power.c | |
create mode 100644 include/linux/power/mxs_power.h | |
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig | |
index 237d7aa..5fc5f93 100644 | |
--- a/drivers/power/Kconfig | |
+++ b/drivers/power/Kconfig | |
@@ -43,6 +43,16 @@ config MAX8925_POWER | |
Say Y here to enable support for the battery charger in the Maxim | |
MAX8925 PMIC. | |
+config MXS_POWER | |
+ tristate "Freescale MXS power subsystem support" | |
+ depends on ARCH_MXS || COMPILE_TEST | |
+ depends on IIO | |
+ depends on MXS_LRADC | |
+ help | |
+ Say Y here to enable support for the Freescale i.MX23/i.MX28 | |
+ power subsystem. This is a requirement to get access to on-chip | |
+ regulators, battery charger and many more. | |
+ | |
config WM831X_BACKUP | |
tristate "WM831X backup battery charger support" | |
depends on MFD_WM831X | |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile | |
index b656638..04af411 100644 | |
--- a/drivers/power/Makefile | |
+++ b/drivers/power/Makefile | |
@@ -11,6 +11,7 @@ obj-$(CONFIG_PDA_POWER) += pda_power.o | |
obj-$(CONFIG_APM_POWER) += apm_power.o | |
obj-$(CONFIG_AXP20X_POWER) += axp20x_usb_power.o | |
obj-$(CONFIG_MAX8925_POWER) += max8925_power.o | |
+obj-$(CONFIG_MXS_POWER) += mxs-power.o mxs-power-debug.o | |
obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o | |
obj-$(CONFIG_WM831X_POWER) += wm831x_power.o | |
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o | |
diff --git a/drivers/power/mxs-power-debug.c b/drivers/power/mxs-power-debug.c | |
new file mode 100644 | |
index 0000000..c5c2724 | |
--- /dev/null | |
+++ b/drivers/power/mxs-power-debug.c | |
@@ -0,0 +1,388 @@ | |
+/* | |
+ * Freescale MXS power debug | |
+ * | |
+ * Copyright (c) 2015 Stefan Wahren | |
+ * | |
+ * The code contained herein is licensed under the GNU General Public | |
+ * License. You may obtain a copy of the GNU General Public License | |
+ * Version 2 or later at the following locations: | |
+ * | |
+ * http://www.opensource.org/licenses/gpl-license.html | |
+ * http://www.gnu.org/copyleft/gpl.html | |
+ */ | |
+ | |
+#include <linux/debugfs.h> | |
+#include <linux/power/mxs_power.h> | |
+#include <linux/regmap.h> | |
+#include <linux/seq_file.h> | |
+#include <linux/types.h> | |
+ | |
+#ifdef CONFIG_DEBUG_FS | |
+ | |
+static int | |
+mxs_power_ctrl_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_5VCTRL, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ /* only MX23 */ | |
+ seq_printf(s, "CLKGATE: %x\n", (value >> 30) & 1); | |
+ | |
+ seq_printf(s, "PSWITCH_MID_TRAN: %x\n", (value >> 27) & 1); | |
+ seq_printf(s, "DCDC4P2_BO_IRQ: %x\n", (value >> 24) & 1); | |
+ seq_printf(s, "ENIRQ_DCDC4P2_BO: %x\n", (value >> 23) & 1); | |
+ seq_printf(s, "VDD5V_DROOP_IRQ: %x\n", (value >> 22) & 1); | |
+ seq_printf(s, "ENIRQ_VDD5V_DROOP: %x\n", (value >> 21) & 1); | |
+ seq_printf(s, "PSWITCH_IRQ: %x\n", (value >> 20) & 1); | |
+ seq_printf(s, "PSWITCH_IRQ_SRC: %x\n", (value >> 19) & 1); | |
+ seq_printf(s, "POLARITY_PSWITCH: %x\n", (value >> 18) & 1); | |
+ seq_printf(s, "ENIRQ_PSWITCH: %x\n", (value >> 17) & 1); | |
+ seq_printf(s, "POLARITY_DC_OK: %x\n", (value >> 16) & 1); | |
+ seq_printf(s, "DC_OK_IRQ: %x\n", (value >> 15) & 1); | |
+ seq_printf(s, "ENIRQ_DC_OK: %x\n", (value >> 14) & 1); | |
+ seq_printf(s, "BATT_BO_IRQ: %x\n", (value >> 13) & 1); | |
+ seq_printf(s, "ENIRQBATT_BO: %x\n", (value >> 12) & 1); | |
+ seq_printf(s, "VDDIO_BO_IRQ: %x\n", (value >> 11) & 1); | |
+ seq_printf(s, "ENIRQ_VDDIO_BO: %x\n", (value >> 10) & 1); | |
+ seq_printf(s, "VDDA_BO_IRQ: %x\n", (value >> 9) & 1); | |
+ seq_printf(s, "ENIRQ_VDDA_BO: %x\n", (value >> 8) & 1); | |
+ seq_printf(s, "VDDD_BO_IRQ: %x\n", (value >> 7) & 1); | |
+ seq_printf(s, "ENIRQ_VDDD_BO: %x\n", (value >> 6) & 1); | |
+ seq_printf(s, "POLARITY_VBUSVALID: %x\n", (value >> 5) & 1); | |
+ seq_printf(s, "VBUSVALID_IRQ: %x\n", (value >> 4) & 1); | |
+ seq_printf(s, "ENIRQ_VBUS_VALID: %x\n", (value >> 3) & 1); | |
+ seq_printf(s, "POLARITY_VDD5V_GT_VDDIO: %x\n", (value >> 2) & 1); | |
+ seq_printf(s, "VDD5V_GT_VDDIO_IRQ: %x\n", (value >> 1) & 1); | |
+ seq_printf(s, "ENIRQ_VDD5V_GT_VDDIO: %x\n", value & 1); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_5vctrl_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_5VCTRL, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ seq_printf(s, "VBUSDROOP_TRSH: %x\n", (value >> 28) & 3); | |
+ seq_printf(s, "HEADROOM_ADJ: %x\n", (value >> 24) & 7); | |
+ seq_printf(s, "PWD_CHARGE_4P2: %x\n", (value >> 20) & 1); | |
+ seq_printf(s, "CHARGE_4P2_ILIMIT: %x\n", (value >> 12) & 0x3F); | |
+ seq_printf(s, "VBUSVALID_TRSH: %x\n", (value >> 8) & 7); | |
+ seq_printf(s, "PWDN_5VBRNOUT: %x\n", (value >> 7) & 1); | |
+ seq_printf(s, "ENABLE_LINREG_ILIMIT: %x\n", (value >> 6) & 1); | |
+ seq_printf(s, "DCDC_XFER: %x\n", (value >> 5) & 1); | |
+ seq_printf(s, "VBUSVALID_5VDETECT: %x\n", (value >> 4) & 1); | |
+ seq_printf(s, "VBUSVALID_TO_B: %x\n", (value >> 3) & 1); | |
+ seq_printf(s, "ILIMIT_EQ_ZERO: %x\n", (value >> 2) & 1); | |
+ seq_printf(s, "PWRUP_VBUS_CMPS: %x\n", (value >> 1) & 1); | |
+ seq_printf(s, "ENABLE_DCDC: %x\n", value & 1); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_vddd_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_VDDDCTRL, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ seq_printf(s, "ADJTN: %x\n", (value >> 28) & 0xf); | |
+ seq_printf(s, "PWDN_BRNOUT: %x\n", (value >> 23) & 1); | |
+ seq_printf(s, "DISABLE_STEPPING: %x\n", (value >> 22) & 1); | |
+ seq_printf(s, "ENABLE_LINREG: %x\n", (value >> 21) & 1); | |
+ seq_printf(s, "DISABLE_FET: %x\n", (value >> 20) & 1); | |
+ seq_printf(s, "LINREG_OFFSET: %x\n", (value >> 16) & 3); | |
+ seq_printf(s, "BO_OFFSET: %x\n", (value >> 8) & 7); | |
+ seq_printf(s, "TRG: %x\n", value & 0x1f); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_vdda_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_VDDACTRL, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ seq_printf(s, "PWDN_BRNOUT: %x\n", (value >> 19) & 1); | |
+ seq_printf(s, "DISABLE_STEPPING: %x\n", (value >> 18) & 1); | |
+ seq_printf(s, "ENABLE_LINREG: %x\n", (value >> 17) & 1); | |
+ seq_printf(s, "DISABLE_FET: %x\n", (value >> 16) & 1); | |
+ seq_printf(s, "LINREG_OFFSET: %x\n", (value >> 12) & 3); | |
+ seq_printf(s, "BO_OFFSET: %x\n", (value >> 8) & 7); | |
+ seq_printf(s, "TRG: %x\n", value & 0x1f); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_vddio_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_VDDIOCTRL, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ seq_printf(s, "ADJTN: %x\n", (value >> 20) & 0xf); | |
+ seq_printf(s, "PWDN_BRNOUT: %x\n", (value >> 18) & 1); | |
+ seq_printf(s, "DISABLE_STEPPING: %x\n", (value >> 17) & 1); | |
+ seq_printf(s, "DISABLE_FET: %x\n", (value >> 16) & 1); | |
+ seq_printf(s, "LINREG_OFFSET: %x\n", (value >> 12) & 3); | |
+ seq_printf(s, "BO_OFFSET: %x\n", (value >> 8) & 7); | |
+ seq_printf(s, "TRG: %x\n", value & 0x1f); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_dcdc4p2_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_DCDC4P2, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ seq_printf(s, "DROPOUT_CTRL: %x\n", (value >> 28) & 0xf); | |
+ seq_printf(s, "ENABLE_4P2: %x\n", (value >> 23) & 1); | |
+ seq_printf(s, "ENABLE_DCDC: %x\n", (value >> 22) & 1); | |
+ seq_printf(s, "HYST_DIR: %x\n", (value >> 21) & 1); | |
+ seq_printf(s, "HYST_THRESH: %x\n", (value >> 20) & 3); | |
+ seq_printf(s, "TRG: %x\n", (value >> 16) & 7); | |
+ seq_printf(s, "BO: %x\n", (value >> 8) & 0x1f); | |
+ seq_printf(s, "CMPTRIP: %x\n", value & 0x1f); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_misc_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_MISC, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ seq_printf(s, "FREQSEL %x\n", (value >> 4) & 0x7); | |
+ | |
+ /* only MX28 */ | |
+ seq_printf(s, "DISABLEFET_BO_LOGIC %x\n", (value >> 3) & 1); | |
+ | |
+ seq_printf(s, "DELAY_TIMING %x\n", (value >> 2) & 1); | |
+ seq_printf(s, "SEL_PLLCLK %x\n", value & 1); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_sts_mxs_show(struct seq_file *s, void *what) | |
+{ | |
+ struct mxs_power_data *data = s->private; | |
+ u32 value; | |
+ int ret = regmap_read(data->regmap, HW_POWER_STS, &value); | |
+ | |
+ if (ret) | |
+ return ret; | |
+ | |
+ seq_printf(s, "PWRUP_SOURCE %x\n", (value >> 24) & 0x3F); | |
+ seq_printf(s, "PSWITCH %x\n", (value >> 20) & 3); | |
+ seq_printf(s, "THERMAL_WARNING %x\n", (value >> 19) & 1); | |
+ seq_printf(s, "VDDMEM_BO %x\n", (value >> 18) & 1); | |
+ seq_printf(s, "AVALID0_STATUS %x\n", (value >> 17) & 1); | |
+ seq_printf(s, "BVALID0_STATUS %x\n", (value >> 16) & 1); | |
+ seq_printf(s, "VBUSVALID0_STATUS %x\n", (value >> 15) & 1); | |
+ seq_printf(s, "SESSEND0_STATUS %x\n", (value >> 14) & 1); | |
+ seq_printf(s, "BATT_BO %x\n", (value >> 13) & 1); | |
+ seq_printf(s, "VDD5V_FAULT %x\n", (value >> 12) & 1); | |
+ seq_printf(s, "CHRGSTS %x\n", (value >> 11) & 1); | |
+ seq_printf(s, "DCDC_4P2_BO %x\n", (value >> 10) & 1); | |
+ seq_printf(s, "DC_OK %x\n", (value >> 9) & 1); | |
+ seq_printf(s, "VDDIO_BO %x\n", (value >> 8) & 1); | |
+ seq_printf(s, "VDDA_BO %x\n", (value >> 7) & 1); | |
+ seq_printf(s, "VDDD_BO %x\n", (value >> 6) & 1); | |
+ seq_printf(s, "VDD5V_GT_VDDIO %x\n", (value >> 5) & 1); | |
+ seq_printf(s, "VDD5V_DROOP %x\n", (value >> 4) & 1); | |
+ seq_printf(s, "AVALID0 %x\n", (value >> 3) & 1); | |
+ seq_printf(s, "BVALID0 %x\n", (value >> 2) & 1); | |
+ seq_printf(s, "VBUSVALID0 %x\n", (value >> 1) & 1); | |
+ seq_printf(s, "SESSEND0 %x\n", value & 1); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int | |
+mxs_power_ctrl_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_ctrl_mxs_show, inode->i_private); | |
+} | |
+ | |
+static int | |
+mxs_power_5vctrl_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_5vctrl_mxs_show, inode->i_private); | |
+} | |
+ | |
+static int | |
+mxs_power_vddd_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_vddd_mxs_show, inode->i_private); | |
+} | |
+ | |
+static int | |
+mxs_power_vdda_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_vdda_mxs_show, inode->i_private); | |
+} | |
+ | |
+static int | |
+mxs_power_vddio_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_vddio_mxs_show, inode->i_private); | |
+} | |
+ | |
+static int | |
+mxs_power_dcdc4p2_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_dcdc4p2_mxs_show, inode->i_private); | |
+} | |
+ | |
+static int | |
+mxs_power_misc_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_misc_mxs_show, inode->i_private); | |
+} | |
+ | |
+static int | |
+mxs_power_sts_open(struct inode *inode, struct file *file) | |
+{ | |
+ return single_open(file, mxs_power_sts_mxs_show, inode->i_private); | |
+} | |
+ | |
+static const struct file_operations mxs_power_ctrl_ops = { | |
+ .open = mxs_power_ctrl_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+static const struct file_operations mxs_power_5vctrl_ops = { | |
+ .open = mxs_power_5vctrl_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+static const struct file_operations mxs_power_vddd_ops = { | |
+ .open = mxs_power_vddd_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+static const struct file_operations mxs_power_vdda_ops = { | |
+ .open = mxs_power_vdda_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+static const struct file_operations mxs_power_vddio_ops = { | |
+ .open = mxs_power_vddio_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+static const struct file_operations mxs_power_dcdc4p2_ops = { | |
+ .open = mxs_power_dcdc4p2_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+static const struct file_operations mxs_power_misc_ops = { | |
+ .open = mxs_power_misc_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+static const struct file_operations mxs_power_sts_ops = { | |
+ .open = mxs_power_sts_open, | |
+ .read = seq_read, | |
+ .llseek = seq_lseek, | |
+ .release = single_release, | |
+}; | |
+ | |
+void | |
+mxs_power_init_device_debugfs(struct mxs_power_data *data) | |
+{ | |
+ struct dentry *device_root; | |
+ | |
+ device_root = debugfs_create_dir("mxs_power", NULL); | |
+ data->device_root = device_root; | |
+ | |
+ if (IS_ERR(device_root) || !device_root) { | |
+ pr_warn("failed to create debugfs directory for %s\n", | |
+ "mxs_power"); | |
+ return; | |
+ } | |
+ debugfs_create_file("ctrl", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_ctrl_ops); | |
+ debugfs_create_file("5vctrl", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_5vctrl_ops); | |
+ debugfs_create_file("vddd", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_vddd_ops); | |
+ debugfs_create_file("vdda", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_vdda_ops); | |
+ debugfs_create_file("vddio", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_vddio_ops); | |
+ debugfs_create_file("dcdc4p2", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_dcdc4p2_ops); | |
+ debugfs_create_file("misc", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_misc_ops); | |
+ debugfs_create_file("sts", S_IFREG | S_IRUGO, device_root, data, | |
+ &mxs_power_sts_ops); | |
+} | |
+ | |
+void | |
+mxs_power_remove_device_debugfs(struct mxs_power_data *data) | |
+{ | |
+ debugfs_remove_recursive(data->device_root); | |
+} | |
+ | |
+#else /* CONFIG_DEBUG_FS */ | |
+ | |
+void | |
+mxs_power_init_device_debugfs(struct mxs_power_data *data) | |
+{ | |
+} | |
+ | |
+void | |
+mxs_power_remove_device_debugfs(struct mxs_power_data *data) | |
+{ | |
+} | |
+ | |
+#endif | |
diff --git a/drivers/power/mxs-power.c b/drivers/power/mxs-power.c | |
new file mode 100644 | |
index 0000000..a9e64c4 | |
--- /dev/null | |
+++ b/drivers/power/mxs-power.c | |
@@ -0,0 +1,277 @@ | |
+/* | |
+ * Freescale MXS power subsystem | |
+ * | |
+ * Copyright (C) 2014 Stefan Wahren | |
+ * | |
+ * Inspired by imx-bootlets | |
+ */ | |
+ | |
+/* | |
+ * The code contained herein is licensed under the GNU General Public | |
+ * License. You may obtain a copy of the GNU General Public License | |
+ * Version 2 or later at the following locations: | |
+ * | |
+ * http://www.opensource.org/licenses/gpl-license.html | |
+ * http://www.gnu.org/copyleft/gpl.html | |
+ */ | |
+ | |
+#include <linux/device.h> | |
+#include <linux/err.h> | |
+#include <linux/interrupt.h> | |
+#include <linux/kernel.h> | |
+#include <linux/mfd/syscon.h> | |
+#include <linux/module.h> | |
+#include <linux/of.h> | |
+#include <linux/of_platform.h> | |
+#include <linux/platform_device.h> | |
+#include <linux/power_supply.h> | |
+#include <linux/power/mxs_power.h> | |
+#include <linux/stmp_device.h> | |
+#include <linux/types.h> | |
+ | |
+#define BM_POWER_CTRL_POLARITY_VBUSVALID BIT(5) | |
+#define BM_POWER_CTRL_VBUSVALID_IRQ BIT(4) | |
+#define BM_POWER_CTRL_ENIRQ_VBUS_VALID BIT(3) | |
+ | |
+#define BM_POWER_5VCTRL_VBUSVALID_THRESH (7 << 8) | |
+#define BM_POWER_5VCTRL_PWDN_5VBRNOUT BIT(7) | |
+#define BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT BIT(6) | |
+#define BM_POWER_5VCTRL_VBUSVALID_5VDETECT BIT(4) | |
+ | |
+#define HW_POWER_5VCTRL_VBUSVALID_THRESH_4_40V (5 << 8) | |
+ | |
+#define BM_POWER_STS_VBUSVALID0_STATUS BIT(15) | |
+#define BM_POWER_STS_VDD5V_DROOP BIT(4) | |
+ | |
+#define STATUS_5V_CONNECTION BIT(0) | |
+#define STATUS_5V_NEW BIT(1) | |
+ | |
+#define STATUS_NEW_5V_CONNECTION (STATUS_5V_NEW | STATUS_5V_CONNECTION) | |
+#define STATUS_NEW_5V_DISCONNECTION STATUS_5V_NEW | |
+#define STATUS_EXISTING_5V_CONNECTION STATUS_5V_CONNECTION | |
+#define STATUS_EXISTING_5V_DISCONNECTION 0 | |
+ | |
+static enum power_supply_property mxs_power_ac_props[] = { | |
+ POWER_SUPPLY_PROP_ONLINE, | |
+ POWER_SUPPLY_PROP_VOLTAGE_MIN, | |
+}; | |
+ | |
+static int mxs_power_5v_status(struct regmap *map) | |
+{ | |
+ u32 ctrl = 0; | |
+ u32 status = 0; | |
+ int ret; | |
+ | |
+ ret = regmap_read(map, HW_POWER_CTRL, &ctrl); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ ret = regmap_read(map, HW_POWER_STS, &status); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ if (ctrl & BM_POWER_CTRL_POLARITY_VBUSVALID) { | |
+ if ((ctrl & BM_POWER_CTRL_VBUSVALID_IRQ) || | |
+ (status & BM_POWER_STS_VBUSVALID0_STATUS)) | |
+ return STATUS_NEW_5V_CONNECTION; | |
+ | |
+ return STATUS_EXISTING_5V_DISCONNECTION; | |
+ } | |
+ | |
+ if ((ctrl & BM_POWER_CTRL_VBUSVALID_IRQ) || | |
+ !(status & BM_POWER_STS_VBUSVALID0_STATUS) || | |
+ (status & BM_POWER_STS_VDD5V_DROOP)) | |
+ return STATUS_NEW_5V_DISCONNECTION; | |
+ | |
+ return STATUS_EXISTING_5V_CONNECTION; | |
+} | |
+ | |
+static void mxs_5v_work_func(struct work_struct *work) | |
+{ | |
+ struct mxs_power_data *data = | |
+ container_of(work, struct mxs_power_data, poll_5v.work); | |
+ | |
+ switch (mxs_power_5v_status(data->regmap)) { | |
+ case STATUS_NEW_5V_CONNECTION: | |
+ case STATUS_EXISTING_5V_CONNECTION: | |
+ mxs_regmap_clr(data->regmap, HW_POWER_CTRL, | |
+ BM_POWER_CTRL_POLARITY_VBUSVALID); | |
+ break; | |
+ case STATUS_NEW_5V_DISCONNECTION: | |
+ case STATUS_EXISTING_5V_DISCONNECTION: | |
+ mxs_regmap_set(data->regmap, HW_POWER_CTRL, | |
+ BM_POWER_CTRL_POLARITY_VBUSVALID); | |
+ break; | |
+ default: | |
+ return; | |
+ } | |
+ | |
+ mxs_regmap_clr(data->regmap, HW_POWER_CTRL, | |
+ BM_POWER_CTRL_VBUSVALID_IRQ); | |
+ | |
+ mxs_regmap_set(data->regmap, HW_POWER_CTRL, | |
+ BM_POWER_CTRL_ENIRQ_VBUS_VALID); | |
+} | |
+ | |
+static irqreturn_t mxs_irq_vdd5v(int irq, void *cookie) | |
+{ | |
+ struct mxs_power_data *data = (struct mxs_power_data *)cookie; | |
+ | |
+ switch (mxs_power_5v_status(data->regmap)) { | |
+ case STATUS_NEW_5V_CONNECTION: | |
+ pr_info("New 5v connection detected\n"); | |
+ break; | |
+ case STATUS_NEW_5V_DISCONNECTION: | |
+ pr_info("New 5v disconnection detected\n"); | |
+ break; | |
+ default: | |
+ return IRQ_HANDLED; | |
+ } | |
+ | |
+ mxs_regmap_clr(data->regmap, HW_POWER_CTRL, | |
+ BM_POWER_CTRL_ENIRQ_VBUS_VALID); | |
+ | |
+ schedule_delayed_work(&data->poll_5v, msecs_to_jiffies(10)); | |
+ | |
+ return IRQ_HANDLED; | |
+} | |
+ | |
+static int mxs_power_ac_get_property(struct power_supply *psy, | |
+ enum power_supply_property psp, | |
+ union power_supply_propval *val) | |
+{ | |
+ struct mxs_power_data *data = power_supply_get_drvdata(psy); | |
+ u32 v5ctrl = 0; | |
+ int thresh; | |
+ int ret; | |
+ | |
+ switch (psp) { | |
+ case POWER_SUPPLY_PROP_ONLINE: | |
+ ret = mxs_power_5v_status(data->regmap); | |
+ if (ret < 0) | |
+ return ret; | |
+ | |
+ val->intval = (ret & STATUS_5V_CONNECTION) ? 1 : 0; | |
+ ret = 0; | |
+ break; | |
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN: | |
+ ret = regmap_read(data->regmap, HW_POWER_5VCTRL, &v5ctrl); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ thresh = (v5ctrl & BM_POWER_5VCTRL_VBUSVALID_THRESH) >> 8; | |
+ | |
+ if (thresh) | |
+ val->intval = (39 + thresh) * 100000; | |
+ else | |
+ val->intval = 2900000; | |
+ break; | |
+ default: | |
+ ret = -EINVAL; | |
+ break; | |
+ } | |
+ | |
+ return ret; | |
+} | |
+ | |
+static const struct of_device_id of_mxs_power_match[] = { | |
+ { .compatible = "fsl,imx23-power" }, | |
+ { .compatible = "fsl,imx28-power" }, | |
+ { /* end */ } | |
+}; | |
+MODULE_DEVICE_TABLE(of, of_mxs_power_match); | |
+ | |
+static const struct power_supply_desc ac_desc = { | |
+ .properties = mxs_power_ac_props, | |
+ .num_properties = ARRAY_SIZE(mxs_power_ac_props), | |
+ .get_property = mxs_power_ac_get_property, | |
+ .name = "ac", | |
+ .type = POWER_SUPPLY_TYPE_MAINS, | |
+}; | |
+ | |
+static int mxs_power_probe(struct platform_device *pdev) | |
+{ | |
+ struct device *dev = &pdev->dev; | |
+ struct device_node *np = dev->of_node; | |
+ struct mxs_power_data *data; | |
+ struct power_supply_config psy_cfg = {}; | |
+ int ret; | |
+ unsigned int irq; | |
+ | |
+ if (!np) { | |
+ dev_err(dev, "missing device tree\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | |
+ if (!data) | |
+ return -ENOMEM; | |
+ | |
+ data->regmap = syscon_node_to_regmap(np); | |
+ if (IS_ERR(data->regmap)) | |
+ return PTR_ERR(data->regmap); | |
+ | |
+ /* Make sure the current limit of the linregs are disabled. */ | |
+ mxs_regmap_clr(data->regmap, HW_POWER_5VCTRL, | |
+ BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT); | |
+ | |
+ psy_cfg.drv_data = data; | |
+ platform_set_drvdata(pdev, data); | |
+ | |
+ INIT_DELAYED_WORK(&data->poll_5v, mxs_5v_work_func); | |
+ | |
+ irq = platform_get_irq(pdev, 0); | |
+ if (irq < 0) { | |
+ dev_err(dev, "No IRQ resource!\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ ret = devm_request_irq(dev, irq, mxs_irq_vdd5v, IRQF_SHARED, | |
+ "mxs-power", data); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ data->ac = devm_power_supply_register(dev, &ac_desc, &psy_cfg); | |
+ if (IS_ERR(data->ac)) | |
+ return PTR_ERR(data->ac); | |
+ | |
+ switch (mxs_power_5v_status(data->regmap)) { | |
+ case STATUS_NEW_5V_CONNECTION: | |
+ case STATUS_EXISTING_5V_CONNECTION: | |
+ dev_info(dev, "5V = connected\n"); | |
+ break; | |
+ case STATUS_NEW_5V_DISCONNECTION: | |
+ case STATUS_EXISTING_5V_DISCONNECTION: | |
+ dev_info(dev, "5V = disconnected\n"); | |
+ break; | |
+ } | |
+ | |
+ mxs_power_init_device_debugfs(data); | |
+ | |
+ return of_platform_populate(np, NULL, NULL, dev); | |
+} | |
+ | |
+static int mxs_power_remove(struct platform_device *pdev) | |
+{ | |
+ struct mxs_power_data *data = platform_get_drvdata(pdev); | |
+ | |
+ mxs_power_remove_device_debugfs(data); | |
+ cancel_delayed_work(&data->poll_5v); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static struct platform_driver mxs_power_driver = { | |
+ .driver = { | |
+ .name = "mxs_power", | |
+ .of_match_table = of_mxs_power_match, | |
+ }, | |
+ .probe = mxs_power_probe, | |
+ .remove = mxs_power_remove, | |
+}; | |
+ | |
+module_platform_driver(mxs_power_driver); | |
+ | |
+MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); | |
+MODULE_DESCRIPTION("Freescale MXS power subsystem"); | |
+MODULE_LICENSE("GPL v2"); | |
diff --git a/include/linux/power/mxs_power.h b/include/linux/power/mxs_power.h | |
new file mode 100644 | |
index 0000000..014e855 | |
--- /dev/null | |
+++ b/include/linux/power/mxs_power.h | |
@@ -0,0 +1,99 @@ | |
+/* | |
+ * Freescale MXS power subsystem defines | |
+ * | |
+ * Copyright (C) 2015 Stefan Wahren | |
+ * | |
+ * The code contained herein is licensed under the GNU General Public | |
+ * License. You may obtain a copy of the GNU General Public License | |
+ * Version 2 or later at the following locations: | |
+ * | |
+ * http://www.opensource.org/licenses/gpl-license.html | |
+ * http://www.gnu.org/copyleft/gpl.html | |
+ */ | |
+ | |
+#ifndef __POWER_MXS_POWER_H | |
+#define __POWER_MXS_POWER_H | |
+ | |
+#include <linux/power_supply.h> | |
+#include <linux/regmap.h> | |
+#include <linux/stmp_device.h> | |
+ | |
+/* Regulator IDs */ | |
+#define MXS_POWER_DCDC 1 | |
+#define MXS_POWER_VDDIO 2 | |
+#define MXS_POWER_VDDA 3 | |
+#define MXS_POWER_VDDD 4 | |
+ | |
+/* MXS power register address offset */ | |
+#define HW_POWER_CTRL 0x0000 | |
+#define HW_POWER_5VCTRL 0x0010 | |
+#define HW_POWER_VDDDCTRL 0x0040 | |
+#define HW_POWER_VDDACTRL 0x0050 | |
+#define HW_POWER_VDDIOCTRL 0x0060 | |
+#define HW_POWER_DCDC4P2 0x0080 | |
+#define HW_POWER_MISC 0x0090 | |
+#define HW_POWER_STS 0x00c0 | |
+#define HW_POWER_RESET 0x0100 | |
+ | |
+/* Powered by linear regulator. DCDC output is gated off and | |
+ the linreg output is equal to the target. */ | |
+#define HW_POWER_LINREG_DCDC_OFF 1 | |
+ | |
+/* Powered by linear regulator. DCDC output is not gated off | |
+ and is ready for the automatic hardware transistion after a 5V | |
+ event. The converters are not enabled when 5V is present. LinReg output | |
+ is 25mV below target. */ | |
+#define HW_POWER_LINREG_DCDC_READY 2 | |
+ | |
+/* Powered by DCDC converter and the LinReg is on. LinReg output | |
+ is 25mV below target. */ | |
+#define HW_POWER_DCDC_LINREG_ON 3 | |
+ | |
+/* Powered by DCDC converter and the LinReg is off. LinReg output | |
+ is 25mV below target. */ | |
+#define HW_POWER_DCDC_LINREG_OFF 4 | |
+ | |
+/* Powered by DCDC converter and the LinReg is ready for the | |
+ automatic hardware transfer. The LinReg output is not enabled and | |
+ depends on the 5V presence to enable the LinRegs. LinReg offset is 25mV | |
+ below target. */ | |
+#define HW_POWER_DCDC_LINREG_READY 5 | |
+ | |
+/* Powered by an external source when 5V is present. This does not | |
+ necessarily mean the external source is powered by 5V,but the chip needs | |
+ to be aware that 5V is present. */ | |
+#define HW_POWER_EXTERNAL_SOURCE_5V 6 | |
+ | |
+/* Powered by an external source when 5V is not present. This doesn't | |
+ necessarily mean the external source is powered by the battery, but the | |
+ chip needs to be aware that the battery is present */ | |
+#define HW_POWER_EXTERNAL_SOURCE_BATTERY 7 | |
+ | |
+/* Unknown configuration. This is an error. */ | |
+#define HW_POWER_UNKNOWN_SOURCE 8 | |
+ | |
+static inline int mxs_regmap_set(struct regmap *map, unsigned int reg, unsigned int val) | |
+{ | |
+ return regmap_write(map, reg + STMP_OFFSET_REG_SET, val); | |
+} | |
+ | |
+static inline int mxs_regmap_clr(struct regmap *map, unsigned int reg, unsigned int val) | |
+{ | |
+ return regmap_write(map, reg + STMP_OFFSET_REG_CLR, val); | |
+} | |
+ | |
+struct mxs_power_data { | |
+ struct power_supply *ac; | |
+ struct regmap *regmap; | |
+ struct delayed_work poll_5v; | |
+ | |
+#ifdef CONFIG_DEBUG_FS | |
+ struct dentry *device_root; | |
+#endif | |
+}; | |
+ | |
+void mxs_power_init_device_debugfs(struct mxs_power_data *data); | |
+ | |
+void mxs_power_remove_device_debugfs(struct mxs_power_data *data); | |
+ | |
+#endif | |
-- | |
2.7.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 44eda9939d096f56b515562bf95d2ad8bc4ab364 Mon Sep 17 00:00:00 2001 | |
From: =?UTF-8?q?J=C3=B6rg=20Krause?= <joerg.krause@embedded.rocks> | |
Date: Tue, 22 Mar 2016 10:35:35 +0100 | |
Subject: [PATCH 101/102] mxs: add regulator | |
MIME-Version: 1.0 | |
Content-Type: text/plain; charset=UTF-8 | |
Content-Transfer-Encoding: 8bit | |
Signed-off-by: Jörg Krause <joerg.krause@embedded.rocks> | |
--- | |
drivers/regulator/Kconfig | 8 + | |
drivers/regulator/Makefile | 2 + | |
drivers/regulator/mxs-regulator-dcdc.c | 189 +++++++++++++ | |
drivers/regulator/mxs-regulator-ldo.c | 498 +++++++++++++++++++++++++++++++++ | |
4 files changed, 697 insertions(+) | |
create mode 100644 drivers/regulator/mxs-regulator-dcdc.c | |
create mode 100644 drivers/regulator/mxs-regulator-ldo.c | |
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig | |
index 8df0b0e..cdfff93 100644 | |
--- a/drivers/regulator/Kconfig | |
+++ b/drivers/regulator/Kconfig | |
@@ -461,6 +461,14 @@ config REGULATOR_MT6397 | |
This driver supports the control of different power rails of device | |
through regulator interface. | |
+config REGULATOR_MXS | |
+ tristate "Freescale MXS on-chip regulators" | |
+ depends on MXS_POWER || COMPILE_TEST | |
+ help | |
+ Say y here to support Freescale MXS on-chip regulators. | |
+ It is recommended that this option be enabled on i.MX23, | |
+ i.MX28 platform. | |
+ | |
config REGULATOR_PALMAS | |
tristate "TI Palmas PMIC Regulators" | |
depends on MFD_PALMAS | |
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile | |
index 0f81749..2547359 100644 | |
--- a/drivers/regulator/Makefile | |
+++ b/drivers/regulator/Makefile | |
@@ -61,6 +61,8 @@ obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o | |
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o | |
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o | |
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o | |
+obj-$(CONFIG_REGULATOR_MXS) += mxs-regulator-dcdc.o | |
+obj-$(CONFIG_REGULATOR_MXS) += mxs-regulator-ldo.o | |
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o | |
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o | |
obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o | |
diff --git a/drivers/regulator/mxs-regulator-dcdc.c b/drivers/regulator/mxs-regulator-dcdc.c | |
new file mode 100644 | |
index 0000000..4ae9c4c | |
--- /dev/null | |
+++ b/drivers/regulator/mxs-regulator-dcdc.c | |
@@ -0,0 +1,189 @@ | |
+/* | |
+ * Freescale MXS on-chip DC-DC driver | |
+ * | |
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com> | |
+ * | |
+ * Copyright (C) 2015 Stefan Wahren | |
+ * | |
+ * Inspired by imx-bootlets | |
+ */ | |
+ | |
+/* | |
+ * The code contained herein is licensed under the GNU General Public | |
+ * License. You may obtain a copy of the GNU General Public License | |
+ * Version 2 or later at the following locations: | |
+ * | |
+ * http://www.opensource.org/licenses/gpl-license.html | |
+ * http://www.gnu.org/copyleft/gpl.html | |
+ */ | |
+ | |
+#include <linux/delay.h> | |
+#include <linux/device.h> | |
+#include <linux/err.h> | |
+#include <linux/kernel.h> | |
+#include <linux/mfd/syscon.h> | |
+#include <linux/module.h> | |
+#include <linux/of.h> | |
+#include <linux/of_device.h> | |
+#include <linux/platform_device.h> | |
+#include <linux/power/mxs_power.h> | |
+#include <linux/regmap.h> | |
+#include <linux/regulator/driver.h> | |
+#include <linux/regulator/machine.h> | |
+#include <linux/regulator/of_regulator.h> | |
+#include <linux/slab.h> | |
+ | |
+#define SHIFT_FREQSEL 4 | |
+ | |
+#define BM_POWER_MISC_FREQSEL (7 << SHIFT_FREQSEL) | |
+ | |
+/* Recommended DC-DC clock source values */ | |
+#define HW_POWER_MISC_FREQSEL_20000_KHZ 1 | |
+#define HW_POWER_MISC_FREQSEL_24000_KHZ 2 | |
+#define HW_POWER_MISC_FREQSEL_19200_KHZ 3 | |
+ | |
+#define HW_POWER_MISC_SEL_PLLCLK BIT(0) | |
+ | |
+struct mxs_dcdc_info { | |
+ /* regulator descriptor */ | |
+ struct regulator_desc desc; | |
+ | |
+ /* regulator control register */ | |
+ int ctrl_reg; | |
+}; | |
+ | |
+static int mxs_set_dcdc_freq(struct regulator_dev *reg, u32 hz) | |
+{ | |
+ u32 val; | |
+ int ret; | |
+ | |
+ ret = regmap_read(reg->regmap, HW_POWER_MISC, &val); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ val &= ~BM_POWER_MISC_FREQSEL; | |
+ val &= ~HW_POWER_MISC_SEL_PLLCLK; | |
+ | |
+ /* | |
+ * Select the PLL/PFD based frequency that the DC-DC converter uses. | |
+ * The actual switching frequency driving the power inductor is | |
+ * DCDC_CLK/16. Accept only values recommend by Freescale. | |
+ */ | |
+ switch (hz) { | |
+ case 1200000: | |
+ val |= HW_POWER_MISC_FREQSEL_19200_KHZ << SHIFT_FREQSEL; | |
+ break; | |
+ case 1250000: | |
+ val |= HW_POWER_MISC_FREQSEL_20000_KHZ << SHIFT_FREQSEL; | |
+ break; | |
+ case 1500000: | |
+ val |= HW_POWER_MISC_FREQSEL_24000_KHZ << SHIFT_FREQSEL; | |
+ break; | |
+ default: | |
+ dev_warn(®->dev, "Switching freq: %u Hz not supported\n", | |
+ hz); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ /* First program FREQSEL */ | |
+ ret = regmap_write(reg->regmap, HW_POWER_MISC, val); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ /* then set PLL as clock for DC-DC converter */ | |
+ val |= HW_POWER_MISC_SEL_PLLCLK; | |
+ | |
+ return regmap_write(reg->regmap, HW_POWER_MISC, val); | |
+} | |
+ | |
+static struct regulator_ops mxs_dcdc_ops = { | |
+ .is_enabled = regulator_is_enabled_regmap, | |
+}; | |
+ | |
+static const struct mxs_dcdc_info mxs_dcdc = { | |
+ .desc = { | |
+ .name = "dcdc", | |
+ .id = MXS_POWER_DCDC, | |
+ .type = REGULATOR_VOLTAGE, | |
+ .owner = THIS_MODULE, | |
+ .ops = &mxs_dcdc_ops, | |
+ .enable_reg = HW_POWER_STS, | |
+ .enable_mask = (1 << 0), | |
+ }, | |
+}; | |
+ | |
+static const struct of_device_id of_mxs_regulator_dcdc_match[] = { | |
+ { .compatible = "fsl,imx23-dcdc", .data = &mxs_dcdc }, | |
+ { .compatible = "fsl,imx28-dcdc", .data = &mxs_dcdc }, | |
+ { /* end */ } | |
+}; | |
+MODULE_DEVICE_TABLE(of, of_mxs_regulator_dcdc_match); | |
+ | |
+static int mxs_regulator_dcdc_probe(struct platform_device *pdev) | |
+{ | |
+ struct device *dev = &pdev->dev; | |
+ const struct of_device_id *match; | |
+ struct device_node *parent_np; | |
+ struct regulator_dev *rdev = NULL; | |
+ struct mxs_dcdc_info *info; | |
+ struct regulator_init_data *initdata; | |
+ struct regulator_config config = { }; | |
+ u32 switch_freq; | |
+ | |
+ match = of_match_device(of_mxs_regulator_dcdc_match, dev); | |
+ if (!match) { | |
+ /* We do not expect this to happen */ | |
+ dev_err(dev, "%s: Unable to match device\n", __func__); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ info = devm_kmemdup(dev, match->data, sizeof(struct mxs_dcdc_info), | |
+ GFP_KERNEL); | |
+ if (!info) | |
+ return -ENOMEM; | |
+ | |
+ initdata = of_get_regulator_init_data(dev, dev->of_node, &info->desc); | |
+ | |
+ parent_np = of_get_parent(dev->of_node); | |
+ if (!parent_np) | |
+ return -ENODEV; | |
+ config.regmap = syscon_node_to_regmap(parent_np); | |
+ of_node_put(parent_np); | |
+ if (IS_ERR(config.regmap)) | |
+ return PTR_ERR(config.regmap); | |
+ | |
+ config.dev = dev; | |
+ config.init_data = initdata; | |
+ config.driver_data = info; | |
+ config.of_node = dev->of_node; | |
+ | |
+ if (!of_property_read_u32(dev->of_node, "switching-frequency", | |
+ &switch_freq)) | |
+ mxs_set_dcdc_freq(rdev, switch_freq); | |
+ | |
+ rdev = devm_regulator_register(dev, &info->desc, &config); | |
+ if (IS_ERR(rdev)) { | |
+ int ret = PTR_ERR(rdev); | |
+ | |
+ dev_err(dev, "%s: failed to register regulator(%d)\n", | |
+ __func__, ret); | |
+ return ret; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static struct platform_driver mxs_regulator_dcdc_driver = { | |
+ .driver = { | |
+ .name = "mxs_regulator_dcdc", | |
+ .of_match_table = of_mxs_regulator_dcdc_match, | |
+ }, | |
+ .probe = mxs_regulator_dcdc_probe, | |
+}; | |
+ | |
+module_platform_driver(mxs_regulator_dcdc_driver); | |
+ | |
+MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); | |
+MODULE_DESCRIPTION("Freescale MXS on-chip DC-DC driver"); | |
+MODULE_LICENSE("GPL v2"); | |
+MODULE_ALIAS("platform:mxs_regulator_dcdc"); | |
diff --git a/drivers/regulator/mxs-regulator-ldo.c b/drivers/regulator/mxs-regulator-ldo.c | |
new file mode 100644 | |
index 0000000..afbb0ed | |
--- /dev/null | |
+++ b/drivers/regulator/mxs-regulator-ldo.c | |
@@ -0,0 +1,498 @@ | |
+/* | |
+ * Freescale MXS on-chip LDO driver | |
+ * | |
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com> | |
+ * | |
+ * Copyright (C) 2014 Stefan Wahren | |
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. | |
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved. | |
+ * | |
+ * Inspired by imx-bootlets | |
+ */ | |
+ | |
+/* | |
+ * The code contained herein is licensed under the GNU General Public | |
+ * License. You may obtain a copy of the GNU General Public License | |
+ * Version 2 or later at the following locations: | |
+ * | |
+ * http://www.opensource.org/licenses/gpl-license.html | |
+ * http://www.gnu.org/copyleft/gpl.html | |
+ */ | |
+ | |
+#include <linux/delay.h> | |
+#include <linux/device.h> | |
+#include <linux/err.h> | |
+#include <linux/kernel.h> | |
+#include <linux/mfd/syscon.h> | |
+#include <linux/module.h> | |
+#include <linux/of.h> | |
+#include <linux/of_device.h> | |
+#include <linux/platform_device.h> | |
+#include <linux/power/mxs_power.h> | |
+#include <linux/regmap.h> | |
+#include <linux/regulator/driver.h> | |
+#include <linux/regulator/machine.h> | |
+#include <linux/regulator/of_regulator.h> | |
+#include <linux/slab.h> | |
+ | |
+#define BM_POWER_STS_VBUSVALID0_STATUS BIT(15) | |
+#define BM_POWER_STS_DC_OK BIT(9) | |
+ | |
+#define BM_POWER_5VCTRL_ILIMIT_EQ_ZERO BIT(2) | |
+#define BM_POWER_5VCTRL_ENABLE_DCDC BIT(0) | |
+ | |
+#define BM_POWER_LINREG_OFFSET_DCDC_MODE BIT(1) | |
+struct mxs_ldo_info; | |
+ | |
+struct mxs_ldo_info { | |
+ /* regulator descriptor */ | |
+ struct regulator_desc desc; | |
+ | |
+ struct regmap *regmap; | |
+ | |
+ /* regulator control register */ | |
+ unsigned int ctrl_reg; | |
+ | |
+ /* disable DC-DC output */ | |
+ unsigned int disable_fet_mask; | |
+ | |
+ unsigned int disable_stepping_mask; | |
+ | |
+ /* steps between linreg output and DC-DC target */ | |
+ unsigned int linreg_offset_mask; | |
+ u8 linreg_offset_shift; | |
+ | |
+ /* brownout voltage offset */ | |
+ unsigned int bo_offset_mask; | |
+ u8 bo_offset_shift; | |
+ | |
+ /* brownout interrupt status */ | |
+ unsigned int irq_bo; | |
+ | |
+ /* brownout enable interrupt */ | |
+ unsigned int enirq_bo; | |
+ | |
+ /* function which determine power source */ | |
+ u8 (*get_power_source)(struct mxs_ldo_info *); | |
+}; | |
+ | |
+static int mxs_ldo_set_bo_offset(struct regulator_dev *reg, unsigned int offset) | |
+{ | |
+ struct mxs_ldo_info *ldo = rdev_get_drvdata(reg); | |
+ | |
+ if (offset > 7) | |
+ return -EINVAL; | |
+ | |
+ offset <<= ldo->bo_offset_shift; | |
+ | |
+ return regmap_update_bits(ldo->regmap, ldo->ctrl_reg, | |
+ ldo->bo_offset_mask, offset); | |
+} | |
+ | |
+static inline u8 get_linreg_offset(struct mxs_ldo_info *ldo, u32 regs) | |
+{ | |
+ return (regs & ldo->linreg_offset_mask) >> ldo->linreg_offset_shift; | |
+} | |
+ | |
+static u8 get_vddio_power_source(struct mxs_ldo_info *ldo) | |
+{ | |
+ u32 v5ctrl, status, base; | |
+ u8 offset; | |
+ | |
+ if (regmap_read(ldo->regmap, HW_POWER_5VCTRL, &v5ctrl)) | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+ | |
+ if (regmap_read(ldo->regmap, HW_POWER_STS, &status)) | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+ | |
+ if (regmap_read(ldo->regmap, ldo->ctrl_reg, &base)) | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+ | |
+ offset = get_linreg_offset(ldo, base); | |
+ | |
+ /* If VBUS valid then 5 V power supply present */ | |
+ if (status & BM_POWER_STS_VBUSVALID0_STATUS) { | |
+ /* Powered by Linreg, DC-DC is off */ | |
+ if ((base & ldo->disable_fet_mask) && | |
+ !(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)) { | |
+ return HW_POWER_LINREG_DCDC_OFF; | |
+ } | |
+ | |
+ if (v5ctrl & BM_POWER_5VCTRL_ENABLE_DCDC) { | |
+ /* Powered by DC-DC, Linreg is on */ | |
+ if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE) | |
+ return HW_POWER_DCDC_LINREG_ON; | |
+ } else { | |
+ /* Powered by Linreg, DC-DC is ready */ | |
+ if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE) | |
+ return HW_POWER_LINREG_DCDC_READY; | |
+ | |
+ /* Powered by Linreg, DC-DC is off */ | |
+ return HW_POWER_LINREG_DCDC_OFF; | |
+ } | |
+ } else { | |
+ /* Powered by DC-DC, Linreg is on */ | |
+ if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE) | |
+ return HW_POWER_DCDC_LINREG_ON; | |
+ } | |
+ | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+} | |
+ | |
+static u8 get_vdda_vddd_power_source(struct mxs_ldo_info *ldo) | |
+{ | |
+ struct regulator_desc *desc = &ldo->desc; | |
+ u32 v5ctrl, status, base; | |
+ u8 offset; | |
+ | |
+ if (regmap_read(ldo->regmap, HW_POWER_5VCTRL, &v5ctrl)) | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+ | |
+ if (regmap_read(ldo->regmap, HW_POWER_STS, &status)) | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+ | |
+ if (regmap_read(ldo->regmap, ldo->ctrl_reg, &base)) | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+ | |
+ offset = get_linreg_offset(ldo, base); | |
+ | |
+ /* DC-DC output is disabled */ | |
+ if (base & ldo->disable_fet_mask) { | |
+ /* Powered by Linreg, DC-DC is off */ | |
+ if (!(offset & BM_POWER_LINREG_OFFSET_DCDC_MODE)) | |
+ return HW_POWER_LINREG_DCDC_OFF; | |
+ } | |
+ | |
+ /* If VBUS valid then 5 V power supply present */ | |
+ if (status & BM_POWER_STS_VBUSVALID0_STATUS) { | |
+ /* Powered by DC-DC, Linreg is on */ | |
+ if (v5ctrl & BM_POWER_5VCTRL_ENABLE_DCDC) | |
+ return HW_POWER_DCDC_LINREG_ON; | |
+ | |
+ /* Powered by Linreg, DC-DC is ready */ | |
+ if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE) | |
+ return HW_POWER_LINREG_DCDC_READY; | |
+ | |
+ /* Powered by Linreg, DC-DC is off */ | |
+ return HW_POWER_LINREG_DCDC_OFF; | |
+ } | |
+ | |
+ /* DC-DC is on */ | |
+ if (offset & BM_POWER_LINREG_OFFSET_DCDC_MODE) { | |
+ /* Powered by DC-DC, Linreg is on */ | |
+ if (base & desc->enable_mask) | |
+ return HW_POWER_DCDC_LINREG_ON; | |
+ | |
+ /* Powered by DC-DC, Linreg is off */ | |
+ return HW_POWER_DCDC_LINREG_OFF; | |
+ } | |
+ | |
+ return HW_POWER_UNKNOWN_SOURCE; | |
+} | |
+ | |
+static int mxs_ldo_set_voltage_sel(struct regulator_dev *reg, unsigned sel) | |
+{ | |
+ struct mxs_ldo_info *ldo = rdev_get_drvdata(reg); | |
+ struct regulator_desc *desc = &ldo->desc; | |
+ u32 status = 0; | |
+ u32 ctrl; | |
+ int timeout; | |
+ int ret; | |
+ | |
+ ret = regmap_read(reg->regmap, HW_POWER_CTRL, &ctrl); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ ret = mxs_regmap_clr(reg->regmap, HW_POWER_CTRL, ldo->enirq_bo); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ ret = regmap_update_bits(reg->regmap, desc->vsel_reg, desc->vsel_mask, | |
+ sel); | |
+ if (ret) | |
+ goto restore_bo; | |
+ | |
+ if (ldo->get_power_source) { | |
+ switch (ldo->get_power_source(ldo)) { | |
+ case HW_POWER_LINREG_DCDC_OFF: | |
+ case HW_POWER_LINREG_DCDC_READY: | |
+ /* | |
+ * Since the DC-DC converter is off we can't | |
+ * trigger on DC_OK. So wait at least 1 ms | |
+ * for stabilization. | |
+ */ | |
+ usleep_range(1000, 2000); | |
+ ret = 0; | |
+ goto restore_bo; | |
+ } | |
+ } | |
+ | |
+ /* Make sure DC_OK has changed */ | |
+ usleep_range(15, 20); | |
+ | |
+ for (timeout = 0; timeout < 20; timeout++) { | |
+ ret = regmap_read(reg->regmap, HW_POWER_STS, &status); | |
+ | |
+ if (ret) | |
+ break; | |
+ | |
+ /* DC-DC converter control loop has stabilized */ | |
+ if (status & BM_POWER_STS_DC_OK) | |
+ goto restore_bo; | |
+ | |
+ udelay(1); | |
+ } | |
+ | |
+ if (!ret) { | |
+ ret = -ETIMEDOUT; | |
+ dev_warn_ratelimited(®->dev, "%s: timeout status=0x%08x\n", | |
+ __func__, status); | |
+ } | |
+ | |
+ msleep(20); | |
+ | |
+restore_bo: | |
+ | |
+ mxs_regmap_clr(reg->regmap, HW_POWER_CTRL, ldo->irq_bo); | |
+ | |
+ if (ctrl & ldo->enirq_bo) | |
+ mxs_regmap_set(reg->regmap, HW_POWER_CTRL, ldo->enirq_bo); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int mxs_ldo_get_status(struct regulator_dev *reg) | |
+{ | |
+ struct mxs_ldo_info *ldo = rdev_get_drvdata(reg); | |
+ | |
+ if (ldo->get_power_source) { | |
+ switch (ldo->get_power_source(ldo)) { | |
+ case HW_POWER_LINREG_DCDC_OFF: | |
+ case HW_POWER_LINREG_DCDC_READY: | |
+ case HW_POWER_DCDC_LINREG_ON: | |
+ return REGULATOR_STATUS_ON; | |
+ case HW_POWER_DCDC_LINREG_OFF: | |
+ case HW_POWER_DCDC_LINREG_READY: | |
+ return REGULATOR_STATUS_OFF; | |
+ } | |
+ } | |
+ | |
+ return REGULATOR_STATUS_UNDEFINED; | |
+} | |
+ | |
+static struct regulator_ops mxs_vddio_ops = { | |
+ .list_voltage = regulator_list_voltage_linear, | |
+ .map_voltage = regulator_map_voltage_linear, | |
+ .set_voltage_sel = mxs_ldo_set_voltage_sel, | |
+ .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
+ .is_enabled = regulator_is_enabled_regmap, | |
+ .get_status = mxs_ldo_get_status, | |
+}; | |
+ | |
+static struct regulator_ops mxs_vdda_vddd_ops = { | |
+ .list_voltage = regulator_list_voltage_linear, | |
+ .map_voltage = regulator_map_voltage_linear, | |
+ .set_voltage_sel = mxs_ldo_set_voltage_sel, | |
+ .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
+}; | |
+ | |
+static const struct mxs_ldo_info imx23_info_vddio = { | |
+ .desc = { | |
+ .name = "vddio", | |
+ .id = MXS_POWER_VDDIO, | |
+ .type = REGULATOR_VOLTAGE, | |
+ .owner = THIS_MODULE, | |
+ .n_voltages = 0x20, | |
+ .uV_step = 25000, | |
+ .linear_min_sel = 0, | |
+ .min_uV = 2800000, | |
+ .vsel_reg = HW_POWER_VDDIOCTRL, | |
+ .vsel_mask = 0x1f, | |
+ .ops = &mxs_vddio_ops, | |
+ .enable_reg = HW_POWER_5VCTRL, | |
+ .enable_mask = 1 << 2, | |
+ .enable_is_inverted = true, | |
+ }, | |
+ .ctrl_reg = HW_POWER_VDDIOCTRL, | |
+ .disable_fet_mask = 1 << 16, | |
+ .disable_stepping_mask = 1 << 17, | |
+ .linreg_offset_mask = 3 << 12, | |
+ .linreg_offset_shift = 12, | |
+ .bo_offset_mask = 7 << 8, | |
+ .bo_offset_shift = 8, | |
+ .irq_bo = 1 << 11, | |
+ .enirq_bo = 1 << 10, | |
+ .get_power_source = get_vddio_power_source, | |
+}; | |
+ | |
+static const struct mxs_ldo_info imx28_info_vddio = { | |
+ .desc = { | |
+ .name = "vddio", | |
+ .id = MXS_POWER_VDDIO, | |
+ .type = REGULATOR_VOLTAGE, | |
+ .owner = THIS_MODULE, | |
+ .n_voltages = 0x11, | |
+ .uV_step = 50000, | |
+ .linear_min_sel = 0, | |
+ .min_uV = 2800000, | |
+ .vsel_reg = HW_POWER_VDDIOCTRL, | |
+ .vsel_mask = 0x1f, | |
+ .ops = &mxs_vddio_ops, | |
+ .enable_reg = HW_POWER_5VCTRL, | |
+ .enable_mask = 1 << 2, | |
+ .enable_is_inverted = true, | |
+ }, | |
+ .ctrl_reg = HW_POWER_VDDIOCTRL, | |
+ .disable_fet_mask = 1 << 16, | |
+ .disable_stepping_mask = 1 << 17, | |
+ .linreg_offset_mask = 3 << 12, | |
+ .linreg_offset_shift = 12, | |
+ .bo_offset_mask = 7 << 8, | |
+ .bo_offset_shift = 8, | |
+ .irq_bo = 1 << 11, | |
+ .enirq_bo = 1 << 10, | |
+ .get_power_source = get_vddio_power_source, | |
+}; | |
+ | |
+static const struct mxs_ldo_info mxs_info_vdda = { | |
+ .desc = { | |
+ .name = "vdda", | |
+ .id = MXS_POWER_VDDA, | |
+ .type = REGULATOR_VOLTAGE, | |
+ .owner = THIS_MODULE, | |
+ .n_voltages = 0x20, | |
+ .uV_step = 25000, | |
+ .linear_min_sel = 0, | |
+ .min_uV = 1500000, | |
+ .vsel_reg = HW_POWER_VDDACTRL, | |
+ .vsel_mask = 0x1f, | |
+ .ops = &mxs_vdda_vddd_ops, | |
+ .enable_reg = HW_POWER_VDDACTRL, | |
+ .enable_mask = 1 << 17, | |
+ }, | |
+ .ctrl_reg = HW_POWER_VDDACTRL, | |
+ .disable_fet_mask = 1 << 16, | |
+ .disable_stepping_mask = 1 << 18, | |
+ .linreg_offset_mask = 3 << 12, | |
+ .linreg_offset_shift = 12, | |
+ .bo_offset_mask = 7 << 8, | |
+ .bo_offset_shift = 8, | |
+ .irq_bo = 1 << 9, | |
+ .enirq_bo = 1 << 8, | |
+ .get_power_source = get_vdda_vddd_power_source, | |
+}; | |
+ | |
+static const struct mxs_ldo_info mxs_info_vddd = { | |
+ .desc = { | |
+ .name = "vddd", | |
+ .id = MXS_POWER_VDDD, | |
+ .type = REGULATOR_VOLTAGE, | |
+ .owner = THIS_MODULE, | |
+ .n_voltages = 0x20, | |
+ .uV_step = 25000, | |
+ .linear_min_sel = 0, | |
+ .min_uV = 800000, | |
+ .vsel_reg = HW_POWER_VDDDCTRL, | |
+ .vsel_mask = 0x1f, | |
+ .ops = &mxs_vdda_vddd_ops, | |
+ .enable_reg = HW_POWER_VDDDCTRL, | |
+ .enable_mask = 1 << 21, | |
+ }, | |
+ .ctrl_reg = HW_POWER_VDDDCTRL, | |
+ .disable_fet_mask = 1 << 20, | |
+ .disable_stepping_mask = 1 << 22, | |
+ .linreg_offset_mask = 3 << 16, | |
+ .linreg_offset_shift = 16, | |
+ .bo_offset_mask = 7 << 8, | |
+ .bo_offset_shift = 8, | |
+ .irq_bo = 1 << 7, | |
+ .enirq_bo = 1 << 6, | |
+ .get_power_source = get_vdda_vddd_power_source, | |
+}; | |
+ | |
+static const struct of_device_id of_mxs_regulator_ldo_match[] = { | |
+ { .compatible = "fsl,imx23-vddio", .data = &imx23_info_vddio }, | |
+ { .compatible = "fsl,imx23-vdda", .data = &mxs_info_vdda }, | |
+ { .compatible = "fsl,imx23-vddd", .data = &mxs_info_vddd }, | |
+ { .compatible = "fsl,imx28-vddio", .data = &imx28_info_vddio }, | |
+ { .compatible = "fsl,imx28-vdda", .data = &mxs_info_vdda }, | |
+ { .compatible = "fsl,imx28-vddd", .data = &mxs_info_vddd }, | |
+ { /* end */ } | |
+}; | |
+MODULE_DEVICE_TABLE(of, of_mxs_regulator_ldo_match); | |
+ | |
+static int mxs_regulator_ldo_probe(struct platform_device *pdev) | |
+{ | |
+ struct device *dev = &pdev->dev; | |
+ const struct of_device_id *match; | |
+ struct device_node *parent_np; | |
+ struct regulator_dev *rdev = NULL; | |
+ struct mxs_ldo_info *info; | |
+ struct regulator_init_data *initdata; | |
+ struct regulator_config config = { }; | |
+ | |
+ match = of_match_device(of_mxs_regulator_ldo_match, dev); | |
+ if (!match) { | |
+ /* We do not expect this to happen */ | |
+ dev_err(dev, "%s: Unable to match device\n", __func__); | |
+ return -ENODEV; | |
+ } | |
+ | |
+ info = devm_kmemdup(dev, match->data, sizeof(struct mxs_ldo_info), | |
+ GFP_KERNEL); | |
+ if (!info) | |
+ return -ENOMEM; | |
+ | |
+ initdata = of_get_regulator_init_data(dev, dev->of_node, &info->desc); | |
+ | |
+ parent_np = of_get_parent(dev->of_node); | |
+ if (!parent_np) | |
+ return -ENODEV; | |
+ config.regmap = syscon_node_to_regmap(parent_np); | |
+ of_node_put(parent_np); | |
+ if (IS_ERR(config.regmap)) | |
+ return PTR_ERR(config.regmap); | |
+ | |
+ info->regmap = config.regmap; | |
+ config.dev = dev; | |
+ config.init_data = initdata; | |
+ config.driver_data = info; | |
+ config.of_node = dev->of_node; | |
+ | |
+ rdev = devm_regulator_register(dev, &info->desc, &config); | |
+ if (IS_ERR(rdev)) { | |
+ int ret = PTR_ERR(rdev); | |
+ | |
+ dev_err(dev, "%s: failed to register regulator(%d)\n", | |
+ __func__, ret); | |
+ return ret; | |
+ } | |
+ | |
+ if (info->get_power_source) { | |
+ u8 source = info->get_power_source(info); | |
+ | |
+ if (source == HW_POWER_UNKNOWN_SOURCE) | |
+ dev_warn(dev, "%s: Invalid power source\n", | |
+ info->desc.name); | |
+ else | |
+ dev_info(dev, "%s: Current power source (%u)\n", | |
+ info->desc.name, source); | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static struct platform_driver mxs_regulator_ldo_driver = { | |
+ .driver = { | |
+ .name = "mxs_regulator_ldo", | |
+ .of_match_table = of_mxs_regulator_ldo_match, | |
+ }, | |
+ .probe = mxs_regulator_ldo_probe, | |
+}; | |
+ | |
+module_platform_driver(mxs_regulator_ldo_driver); | |
+ | |
+MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); | |
+MODULE_DESCRIPTION("Freescale MXS on-chip LDO driver"); | |
+MODULE_LICENSE("GPL v2"); | |
+MODULE_ALIAS("platform:mxs_regulator_ldo"); | |
-- | |
2.7.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 9ceee681aba63681ada6ac58123f57ae2f48d511 Mon Sep 17 00:00:00 2001 | |
From: =?UTF-8?q?J=C3=B6rg=20Krause?= <joerg.krause@embedded.rocks> | |
Date: Tue, 22 Mar 2016 10:36:46 +0100 | |
Subject: [PATCH 102/102] mxs: dt: enable regulator | |
MIME-Version: 1.0 | |
Content-Type: text/plain; charset=UTF-8 | |
Content-Transfer-Encoding: 8bit | |
Signed-off-by: Jörg Krause <joerg.krause@embedded.rocks> | |
--- | |
arch/arm/boot/dts/imx28.dtsi | 56 +++++++++++++++++++++++++++++++++++++++++--- | |
1 file changed, 53 insertions(+), 3 deletions(-) | |
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi | |
index c5b57d4..f08a667 100644 | |
--- a/arch/arm/boot/dts/imx28.dtsi | |
+++ b/arch/arm/boot/dts/imx28.dtsi | |
@@ -38,12 +38,24 @@ | |
}; | |
cpus { | |
- #address-cells = <0>; | |
+ #address-cells = <1>; | |
#size-cells = <0>; | |
- cpu { | |
+ cpu@0 { | |
compatible = "arm,arm926ej-s"; | |
device_type = "cpu"; | |
+ reg = <0x0>; | |
+ operating-points = < | |
+ /* kHz uV */ | |
+ 64000 1300000 | |
+ 261819 1350000 | |
+ 360000 1350000 | |
+ 392728 1450000 | |
+ 454737 1550000 | |
+ >; | |
+ clocks = <&clks 21>; | |
+ clock-latency = <61036>; /* two CLK32 periods */ | |
+ cpu-supply = <®_vddd>; | |
}; | |
}; | |
@@ -1037,8 +1049,46 @@ | |
}; | |
power: power@80044000 { | |
+ compatible = "fsl,imx28-power", "syscon"; | |
reg = <0x80044000 0x2000>; | |
- status = "disabled"; | |
+ interrupts = <6>; | |
+ | |
+ reg_vddd: regulator@1 { | |
+ compatible = "fsl,imx28-vddd"; | |
+ regulator-name = "vddd"; | |
+ regulator-min-microvolt = <1300000>; | |
+ regulator-max-microvolt = <1550000>; | |
+ vddd-supply = <®_vdda>; | |
+ regulator-boot-on; | |
+ regulator-always-on; | |
+ }; | |
+ | |
+ reg_vdda: regulator@2 { | |
+ compatible = "fsl,imx28-vdda"; | |
+ regulator-name = "vdda"; | |
+ regulator-min-microvolt = <1725000>; | |
+ regulator-max-microvolt = <1950000>; | |
+ vdda-supply = <®_vddio>; | |
+ regulator-boot-on; | |
+ regulator-always-on; | |
+ }; | |
+ | |
+ reg_vddio: regulator@3 { | |
+ compatible = "fsl,imx28-vddio"; | |
+ regulator-name = "vddio"; | |
+ regulator-min-microvolt = <3000000>; | |
+ regulator-max-microvolt = <3550000>; | |
+ regulator-microvolt-offset = <80000>; | |
+ regulator-boot-on; | |
+ regulator-always-on; | |
+ }; | |
+ | |
+ dcdc: regulator@4 { | |
+ compatible = "fsl,imx28-dcdc"; | |
+ regulator-name = "dcdc"; | |
+ regulator-boot-on; | |
+ regulator-always-on; | |
+ }; | |
}; | |
saif1: saif@80046000 { | |
-- | |
2.7.4 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment