aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Miao <eric.miao@linaro.org>2012-01-12 10:23:30 +0800
committerEric Miao <eric.miao@linaro.org>2012-01-12 10:23:30 +0800
commit95df9608d81e4290a7a2657eef99229db2ec9aa6 (patch)
tree5eff733607d8561d6744bfe9eb567e505b9b7cd4
parent08998e8698a3b34826e2e36bffbe4bbdc6c5e581 (diff)
parent0aea43bf004cabab7548668106bfe1376288a68c (diff)
Merge branch 'topic/lt-3.2-imx6-misc' into lt-3.2-imx6
* topic/lt-3.2-imx6-misc: imx6q: add device tree support for OCOTP driver imx6q: add support for OCOTP driver imx6q: add support for watchdog using imx2_wdt.c imx6q: add device tree support for IIM driver imx6q: add support for virtual IIM driver imx6q: add device tree support for SNVS RTC imx6q: add support for SNVS RTC Conflicts: arch/arm/mach-imx/clock-imx6q.c
-rw-r--r--arch/arm/boot/dts/imx6q-sabrelite.dts6
-rw-r--r--arch/arm/boot/dts/imx6q.dtsi11
-rw-r--r--arch/arm/mach-imx/clock-imx6q.c1
-rw-r--r--drivers/char/Kconfig22
-rw-r--r--drivers/char/Makefile3
-rw-r--r--drivers/char/fsl_otp.c308
-rw-r--r--drivers/char/fsl_otp.h362
-rw-r--r--drivers/char/mxs_viim.c185
-rw-r--r--drivers/char/regs-ocotp-v3.h367
-rw-r--r--drivers/rtc/Kconfig8
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-snvs.c655
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--include/linux/fsl_devices.h8
14 files changed, 1935 insertions, 4 deletions
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index 08d920de728..fbe43d6cf37 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -22,6 +22,12 @@
};
soc {
+ aips-bus@02000000 { /* AIPS1 */
+ wdog@020bc000 { /* WDOG1 */
+ status = "okay";
+ };
+ };
+
aips-bus@02100000 { /* AIPS2 */
enet@02188000 {
phy-mode = "rgmii";
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index b6c4b728bce..0f8b104476b 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -366,6 +366,7 @@
};
snvs@020cc000 {
+ compatible = "fsl,snvs";
reg = <0x020cc000 0x4000>;
interrupts = <0 19 0x04 0 20 0x04>;
};
@@ -516,11 +517,15 @@
};
ocotp@021bc000 {
- reg = <0x021bc000 0x4000>;
+ compatible = "fsl,imx6q-ocotp";
+ reg = <0x021bc000 0x8000>;
+ interrupts = <0 21 0x04>;
};
- ocotp@021c0000 {
- reg = <0x021c0000 0x4000>;
+ viim@021bc000 {
+ compatible = "fsl,imx6q-viim";
+ reg = <0x021bc000 0x4000>,
+ <0x021c0000 0x4000>;
interrupts = <0 21 0x04>;
};
diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c
index 6f6df33901b..60fb67c0a5a 100644
--- a/arch/arm/mach-imx/clock-imx6q.c
+++ b/arch/arm/mach-imx/clock-imx6q.c
@@ -1918,6 +1918,7 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "gpu3d_clk", gpu3d_core_clk),
_REGISTER_CLOCK(NULL, "gpu2d_clk", gpu2d_core_clk),
_REGISTER_CLOCK(NULL, "gpu3d_shader_clk", gpu3d_shader_clk),
+ _REGISTER_CLOCK(NULL, "ocotp_clk", iim_clk),
};
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 43643033a3a..96251d48435 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -608,6 +608,28 @@ config RAMOOPS
This enables panic and oops messages to be logged to a circular
buffer in RAM where it can be read back at some later point.
+config MXS_VIIM
+ tristate "MXS Virtual IIM device driver"
+ depends on (ARCH_MX50 || SOC_IMX6Q)
+ help
+ Support for access to MXS Virtual IIM device, most people should say N here.
+
+config FSL_OTP
+ tristate "Freescale On-Chip OTP Memory Support"
+ depends on (ARCH_MX23 || ARCH_MX28 || ARCH_MX50 || SOC_IMX6Q)
+ default n
+ help
+ If you say Y here, you will get support for a character device
+ interface into the One Time Programmable memory pages that are
+ stored on the iMX23/28/50 processor. This will not get you access
+ to the secure memory pages however. You will need to write your
+ own secure code and reader for that.
+
+ To compile this driver as a module, choose M here: the module
+ will be called fsl_otp.
+
+ If unsure, it is safe to say Y.
+
config MSM_SMD_PKT
bool "Enable device interface for some SMD packet ports"
default n
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 32762ba769c..a76851883a3 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -61,6 +61,9 @@ obj-$(CONFIG_TCG_TPM) += tpm/
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_RAMOOPS) += ramoops.o
+obj-$(CONFIG_MXS_VIIM) += mxs_viim.o
+obj-$(CONFIG_FSL_OTP) += fsl_otp.o
+
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
diff --git a/drivers/char/fsl_otp.c b/drivers/char/fsl_otp.c
new file mode 100644
index 00000000000..26ff68fcdaf
--- /dev/null
+++ b/drivers/char/fsl_otp.c
@@ -0,0 +1,308 @@
+/*
+ * Freescale On-Chip OTP driver
+ *
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/fcntl.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "fsl_otp.h"
+
+static DEFINE_MUTEX(otp_mutex);
+static struct kobject *otp_kobj;
+static struct attribute **attrs;
+static struct kobj_attribute *kattr;
+static struct attribute_group attr_group;
+static struct mxc_otp_platform_data *otp_data;
+static struct clk *otp_clk;
+
+static inline unsigned int get_reg_index(struct kobj_attribute *tmp)
+{
+ return tmp - kattr;
+}
+
+static int otp_wait_busy(u32 flags)
+{
+ int count;
+ u32 c;
+
+ for (count = 10000; count >= 0; count--) {
+ c = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CTRL);
+ if (!(c & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR | flags)))
+ break;
+ cpu_relax();
+ }
+ if (count < 0)
+ return -ETIMEDOUT;
+ return 0;
+}
+
+static ssize_t otp_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ unsigned int index = get_reg_index(attr);
+ u32 value = 0;
+
+ /* sanity check */
+ if (index >= otp_data->fuse_num)
+ return 0;
+
+ mutex_lock(&otp_mutex);
+
+ if (otp_read_prepare(otp_data)) {
+ mutex_unlock(&otp_mutex);
+ return 0;
+ }
+ value = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CUSTn(index));
+ otp_read_post(otp_data);
+
+ mutex_unlock(&otp_mutex);
+ return sprintf(buf, "0x%x\n", value);
+}
+
+static int otp_write_bits(int addr, u32 data, u32 magic)
+{
+ u32 c; /* for control register */
+
+ /* init the control register */
+ c = __raw_readl(REGS_OCOTP_BASE + HW_OCOTP_CTRL);
+ c &= ~BM_OCOTP_CTRL_ADDR;
+ c |= BF(addr, OCOTP_CTRL_ADDR);
+ c |= BF(magic, OCOTP_CTRL_WR_UNLOCK);
+ __raw_writel(c, REGS_OCOTP_BASE + HW_OCOTP_CTRL);
+
+ /* init the data register */
+ __raw_writel(data, REGS_OCOTP_BASE + HW_OCOTP_DATA);
+ otp_wait_busy(0);
+
+ mdelay(2); /* Write Postamble */
+ return 0;
+}
+
+static ssize_t otp_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int index = get_reg_index(attr);
+ int value;
+
+ /* sanity check */
+ if (index >= otp_data->fuse_num)
+ return 0;
+
+ sscanf(buf, "0x%x", &value);
+
+ mutex_lock(&otp_mutex);
+ if (otp_write_prepare(otp_data)) {
+ mutex_unlock(&otp_mutex);
+ return 0;
+ }
+ otp_write_bits(index, value, 0x3e77);
+ otp_write_post(otp_data);
+ mutex_unlock(&otp_mutex);
+ return count;
+}
+
+static void free_otp_attr(void)
+{
+ kfree(attrs);
+ attrs = NULL;
+
+ kfree(kattr);
+ kattr = NULL;
+}
+
+static int __init alloc_otp_attr(struct mxc_otp_platform_data *pdata)
+{
+ int i;
+
+ otp_data = pdata; /* get private data */
+
+ /* The last one is NULL, which is used to detect the end */
+ attrs = kzalloc((otp_data->fuse_num + 1) * sizeof(attrs[0]),
+ GFP_KERNEL);
+ kattr = kzalloc(otp_data->fuse_num * sizeof(struct kobj_attribute),
+ GFP_KERNEL);
+
+ if (!attrs || !kattr)
+ goto error_out;
+
+ for (i = 0; i < otp_data->fuse_num; i++) {
+ kattr[i].attr.name = pdata->fuse_name[i];
+ kattr[i].attr.mode = 0600;
+ kattr[i].show = otp_show;
+ kattr[i].store = otp_store;
+
+ attrs[i] = &kattr[i].attr;
+ }
+ memset(&attr_group, 0, sizeof(attr_group));
+ attr_group.attrs = attrs;
+ return 0;
+
+error_out:
+ free_otp_attr();
+ return -ENOMEM;
+}
+
+static const struct of_device_id ocotp_dt_ids[];
+
+static int __devinit fsl_otp_probe(struct platform_device *pdev)
+{
+ int retval;
+ struct mxc_otp_platform_data *pdata;
+ const struct of_device_id *of_id =
+ of_match_device(ocotp_dt_ids, &pdev->dev);
+
+ pdata = of_id ? of_id->data : pdev->dev.platform_data;
+ if (pdata == NULL)
+ return -ENODEV;
+
+ /* Enable clock */
+ otp_clk = clk_get(&pdev->dev, "ocotp_clk");
+ if (otp_clk)
+ clk_enable(otp_clk);
+
+ retval = alloc_otp_attr(pdata);
+ if (retval)
+ return retval;
+
+ retval = map_ocotp_addr(pdev);
+ if (retval)
+ goto error;
+
+ otp_kobj = kobject_create_and_add("fsl_otp", NULL);
+ if (!otp_kobj) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ retval = sysfs_create_group(otp_kobj, &attr_group);
+ if (retval)
+ goto error;
+
+ mutex_init(&otp_mutex);
+ dev_info(&pdev->dev, "initialized\n");
+ return 0;
+error:
+ kobject_put(otp_kobj);
+ otp_kobj = NULL;
+ free_otp_attr();
+ unmap_ocotp_addr();
+ return retval;
+}
+
+static int otp_remove(struct platform_device *pdev)
+{
+ struct mxc_otp_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL)
+ return -ENODEV;
+
+ kobject_put(otp_kobj);
+ otp_kobj = NULL;
+
+ free_otp_attr();
+ unmap_ocotp_addr();
+
+ if (otp_clk) {
+ clk_disable(otp_clk);
+ clk_put(otp_clk);
+ }
+
+ return 0;
+}
+
+#define BANK(a, b, c, d, e, f, g, h) \
+{\
+ ("HW_OCOTP_"#a), ("HW_OCOTP_"#b), ("HW_OCOTP_"#c), ("HW_OCOTP_"#d), \
+ ("HW_OCOTP_"#e), ("HW_OCOTP_"#f), ("HW_OCOTP_"#g), ("HW_OCOTP_"#h) \
+}
+
+static char *imx6q_ocotp_fuse_names[16][8] = {
+ BANK(LOCK, CFG0, CFG1, CFG2, CFG3, CFG4, CFG5, CFG6),
+ BANK(MEM0, MEM1, MEM2, MEM3, MEM4, ANA0, ANA1, ANA2),
+ BANK(OTPMK0, SOTPMK1, OTPMK2, OTPMK3, OTPMK4, OTPMK5, OTPMK6, OTPMK7),
+ BANK(SRK0, SRK1, SRK2, SRK3, SRK4, SRK5, SRK6, SRK7),
+ BANK(RESP0, HSJC_RESP1, MAC0, MAC1, HDCP_KSV0, HDCP_KSV1, GP1, GP2),
+ BANK(DTCP_KEY0, DTCP_KEY1, DTCP_KEY2, DTCP_KEY3, DTCP_KEY4, MISC_CONF, FIELD_RETURN, SRK_REVOKE),
+ BANK(HDCP_KEY0, HDCP_KEY1, HDCP_KEY2, HDCP_KEY3, HDCP_KEY4, HDCP_KEY5, HDCP_KEY6, HDCP_KEY7),
+ BANK(HDCP_KEY8, HDCP_KEY9, HDCP_KEY10, HDCP_KEY11, HDCP_KEY12, HDCP_KEY13, HDCP_KEY14, HDCP_KEY15),
+ BANK(HDCP_KEY16, HDCP_KEY17, HDCP_KEY18, HDCP_KEY19, HDCP_KEY20, HDCP_KEY21, HDCP_KEY22, HDCP_KEY23),
+ BANK(HDCP_KEY24, HDCP_KEY25, HDCP_KEY26, HDCP_KEY27, HDCP_KEY28, HDCP_KEY29, HDCP_KEY30, HDCP_KEY31),
+ BANK(HDCP_KEY32, HDCP_KEY33, HDCP_KEY34, HDCP_KEY35, HDCP_KEY36, HDCP_KEY37, HDCP_KEY38, HDCP_KEY39),
+ BANK(HDCP_KEY40, HDCP_KEY41, HDCP_KEY42, HDCP_KEY43, HDCP_KEY44, HDCP_KEY45, HDCP_KEY46, HDCP_KEY47),
+ BANK(HDCP_KEY48, HDCP_KEY49, HDCP_KEY50, HDCP_KEY51, HDCP_KEY52, HDCP_KEY53, HDCP_KEY54, HDCP_KEY55),
+ BANK(HDCP_KEY56, HDCP_KEY57, HDCP_KEY58, HDCP_KEY59, HDCP_KEY60, HDCP_KEY61, HDCP_KEY62, HDCP_KEY63),
+ BANK(HDCP_KEY64, HDCP_KEY65, HDCP_KEY66, HDCP_KEY67, HDCP_KEY68, HDCP_KEY69, HDCP_KEY70, HDCP_KEY71),
+ BANK(CRC0, CRC1, CRC2, CRC3, CRC4, CRC5, CRC6, CRC7),
+};
+
+enum {
+ IMX6Q_OCOTP,
+};
+
+static struct mxc_otp_platform_data ocotp_data[] = {
+ [IMX6Q_OCOTP] = {
+ .fuse_name = (char **)imx6q_ocotp_fuse_names,
+ .fuse_num = 16 * 8,
+ }
+};
+
+static const struct of_device_id ocotp_dt_ids[] = {
+ { .compatible = "fsl,imx6q-ocotp", .data = &ocotp_data[IMX6Q_OCOTP], },
+ { /* sentinel */ }
+};
+
+static struct platform_driver ocotp_driver = {
+ .probe = fsl_otp_probe,
+ .remove = otp_remove,
+ .driver = {
+ .name = "imx-ocotp",
+ .owner = THIS_MODULE,
+ .of_match_table = ocotp_dt_ids,
+ },
+};
+
+static int __init fsl_otp_init(void)
+{
+ return platform_driver_register(&ocotp_driver);
+}
+
+static void __exit fsl_otp_exit(void)
+{
+ platform_driver_unregister(&ocotp_driver);
+}
+module_init(fsl_otp_init);
+module_exit(fsl_otp_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huang Shijie <b32955@freescale.com>");
+MODULE_DESCRIPTION("Common driver for OTP controller");
diff --git a/drivers/char/fsl_otp.h b/drivers/char/fsl_otp.h
new file mode 100644
index 00000000000..255f0b02ecb
--- /dev/null
+++ b/drivers/char/fsl_otp.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __FREESCALE_OTP__
+#define __FREESCALE_OTP__
+
+#define log(a, ...) printk(KERN_INFO "[ %s : %.3d ] "a"\n", \
+ __func__, __LINE__, ## __VA_ARGS__)
+
+static u32 otp_voltage_saved;
+struct regulator *regu;
+static struct clk *otp_clk;
+
+static int otp_wait_busy(u32 flags);
+
+/* IMX23 and IMX28 share most of the defination ========================= */
+#if (defined(CONFIG_ARCH_MX23) || defined(CONFIG_ARCH_MX28))
+
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+#include <mach/device.h>
+#include <mach/regs-ocotp.h>
+#include <mach/regs-power.h>
+
+#if defined(CONFIG_ARCH_MX23)
+#include <mach/mx23.h>
+#else
+#include <mach/mx28.h>
+#endif
+
+#define REGS_OCOTP_BASE (IO_ADDRESS(OCOTP_PHYS_ADDR))
+#define BF(value, field) (((value) << BP_##field) & BM_##field)
+
+static unsigned long otp_hclk_saved;
+
+/* open the bank for the imx23/imx28 */
+static int otp_read_prepare(struct mxc_otp_platform_data *otp_data)
+{
+ int r;
+
+ /* [1] set the HCLK */
+ /* [2] check BUSY and ERROR bit */
+ r = otp_wait_busy(0);
+ if (r < 0)
+ goto error;
+
+ /* [3] open bank */
+ __raw_writel(BM_OCOTP_CTRL_RD_BANK_OPEN,
+ REGS_OCOTP_BASE + HW_OCOTP_CTRL_SET);
+ udelay(10);
+
+ /* [4] wait for BUSY */
+ r = otp_wait_busy(0);
+ return 0;
+error:
+ return r;
+}
+
+static int otp_read_post(struct mxc_otp_platform_data *otp_data)
+{
+ /* [5] close bank */
+ __raw_writel(BM_OCOTP_CTRL_RD_BANK_OPEN,
+ REGS_OCOTP_BASE + HW_OCOTP_CTRL_CLR);
+ return 0;
+}
+
+static int otp_write_prepare(struct mxc_otp_platform_data *otp_data)
+{
+ struct clk *hclk;
+ int err = 0;
+
+ /* [1] HCLK to 24MHz. */
+ hclk = clk_get(NULL, otp_data->clock_name);
+ if (IS_ERR(hclk)) {
+ err = PTR_ERR(hclk);
+ goto out;
+ }
+ /*
+ WARNING ACHTUNG UWAGA
+
+ the code below changes HCLK clock rate to 24M. This is
+ required to write OTP bits (7.2.2 in STMP378x Target
+ Specification), and might affect LCD operation, for example.
+ Moreover, this hacky code changes VDDIO to 2.8V; and resto-
+ res it only on otp_close(). This may affect... anything.
+
+ You are warned now.
+ */
+ otp_hclk_saved = clk_get_rate(hclk);
+ clk_set_rate(hclk, 24000);
+
+ /* [2] The voltage is set to 2.8V */
+ if (otp_data->regulator_name) {
+ regu = regulator_get(NULL, otp_data->regulator_name);
+ otp_voltage_saved = regulator_get_voltage(regu);
+ regulator_set_voltage(regu, otp_data->min_volt, otp_data->max_volt);
+ }
+
+ /* [3] wait for BUSY and ERROR */
+ err = otp_wait_busy(BM_OCOTP_CTRL_RD_BANK_OPEN);
+out:
+ return err;
+}
+
+static int otp_write_post(struct mxc_otp_platform_data *otp_data)
+{
+ struct clk *hclk;
+
+ hclk = clk_get(NULL, otp_data->clock_name);
+
+ /* restore the clock and voltage */
+ clk_set_rate(hclk, otp_hclk_saved);
+ if (otp_data->regulator_name)
+ regulator_set_voltage(regu, otp_voltage_saved, otp_voltage_saved);
+ otp_wait_busy(0);
+
+ /* reset */
+ __raw_writel(BM_OCOTP_CTRL_RELOAD_SHADOWS,
+ REGS_OCOTP_BASE + HW_OCOTP_CTRL_SET);
+ otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS);
+ return 0;
+}
+
+static int __init map_ocotp_addr(struct platform_device *pdev)
+{
+ return 0;
+}
+static void unmap_ocotp_addr(void)
+{
+}
+
+#elif defined(CONFIG_ARCH_MX5) /* IMX5 below ============================= */
+
+#include <mach/hardware.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include "regs-ocotp-v2.h"
+
+static void *otp_base;
+#define REGS_OCOTP_BASE ((unsigned long)otp_base)
+#define HW_OCOTP_CUSTn(n) (0x00000030 + (n) * 0x10)
+#define BF(value, field) (((value) << BP_##field) & BM_##field)
+
+#define DEF_RELEX (15) /* > 10.5ns */
+
+static int set_otp_timing(struct mxc_otp_platform_data *otp_data)
+{
+ struct clk *apb_clk;
+ unsigned long clk_rate = 0;
+ unsigned long relex, sclk_count, rd_busy;
+ u32 timing = 0;
+
+ if (!otp_data->clock_name)
+ return -1;
+
+ /* [1] get the clock. It needs the AHB clock,though doc writes APB.*/
+ apb_clk = clk_get(NULL, otp_data->clock_name);
+ if (IS_ERR(apb_clk)) {
+ log("we can not find the clock");
+ return -1;
+ }
+ clk_rate = clk_get_rate(apb_clk);
+
+ /* do optimization for too many zeros */
+ relex = clk_rate / (1000000000 / DEF_RELEX) + 1;
+ sclk_count = clk_rate / (1000000000 / 5000) + 1 + DEF_RELEX;
+ rd_busy = clk_rate / (1000000000 / 300) + 1;
+
+ timing = BF(relex, OCOTP_TIMING_RELAX);
+ timing |= BF(sclk_count, OCOTP_TIMING_SCLK_COUNT);
+ timing |= BF(rd_busy, OCOTP_TIMING_RD_BUSY);
+
+ __raw_writel(timing, REGS_OCOTP_BASE + HW_OCOTP_TIMING);
+ return 0;
+}
+
+/* IMX5 does not need to open the bank anymore */
+static int otp_read_prepare(struct mxc_otp_platform_data *otp_data)
+{
+ return set_otp_timing(otp_data);
+}
+static int otp_read_post(struct mxc_otp_platform_data *otp_data)
+{
+ return 0;
+}
+
+static int otp_write_prepare(struct mxc_otp_platform_data *otp_data)
+{
+ int ret = 0;
+
+ /* [1] set timing */
+ ret = set_otp_timing(otp_data);
+ if (ret)
+ return ret;
+
+ /* [2] wait */
+ otp_wait_busy(0);
+ return 0;
+}
+static int otp_write_post(struct mxc_otp_platform_data *otp_data)
+{
+ /* Reload all the shadow registers */
+ __raw_writel(BM_OCOTP_CTRL_RELOAD_SHADOWS,
+ REGS_OCOTP_BASE + HW_OCOTP_CTRL_SET);
+ udelay(1);
+ otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS);
+ return 0;
+}
+
+static int __init map_ocotp_addr(struct platform_device *pdev)
+{
+ struct resource *res;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ otp_base = ioremap(res->start, SZ_8K);
+ if (!otp_base) {
+ log("Can not remap the OTP iomem!");
+ return -1;
+ }
+ return 0;
+}
+
+static void unmap_ocotp_addr(void)
+{
+ iounmap(otp_base);
+ otp_base = NULL;
+}
+
+#elif defined(CONFIG_SOC_IMX6Q) /* IMX6 below ============================= */
+
+#include <mach/hardware.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include "regs-ocotp-v3.h"
+
+static void *otp_base;
+
+#define REGS_OCOTP_BASE ((unsigned long)otp_base)
+#define HW_OCOTP_CUSTn(n) (0x00000400 + (n) * 0x10)
+#define BF(value, field) (((value) << BP_##field) & BM_##field)
+
+#define DEF_RELEX (20) /* > 16.5ns */
+
+static int set_otp_timing(struct mxc_otp_platform_data *otp_data)
+{
+ unsigned long clk_rate = 0;
+ unsigned long strobe_read, relex, strobe_prog;
+ u32 timing = 0;
+
+ clk_rate = clk_get_rate(otp_clk);
+
+ /* do optimization for too many zeros */
+ relex = clk_rate / (1000000000 / DEF_RELEX) - 1;
+ strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELEX + 1) - 1;
+ strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELEX + 1) - 1;
+
+ timing = BF(relex, OCOTP_TIMING_RELAX);
+ timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ);
+ timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG);
+
+ __raw_writel(timing, REGS_OCOTP_BASE + HW_OCOTP_TIMING);
+
+ return 0;
+}
+
+/* IMX5 does not need to open the bank anymore */
+static int otp_read_prepare(struct mxc_otp_platform_data *otp_data)
+{
+ int ret = 0;
+ /* [1] set the HCLK */
+ set_otp_timing(otp_data);
+
+ /* [2] check BUSY and ERROR bit */
+ ret = otp_wait_busy(0);
+
+ return ret;
+}
+
+static int otp_read_post(struct mxc_otp_platform_data *otp_data)
+{
+ return 0;
+}
+
+static int otp_write_prepare(struct mxc_otp_platform_data *otp_data)
+{
+ int ret = 0;
+
+ /* [1] set timing */
+ ret = set_otp_timing(otp_data);
+ if (ret)
+ return ret;
+
+ /* [2] wait */
+ ret = otp_wait_busy(0);
+ if (ret < 0)
+ return ret;
+
+ /* [3] The voltage is set to 2.8V, if needed */
+ if (otp_data->regulator_name) {
+ regu = regulator_get(NULL, otp_data->regulator_name);
+ otp_voltage_saved = regulator_get_voltage(regu);
+ regulator_set_voltage(regu, otp_data->min_volt, otp_data->max_volt);
+ /* [4] wait for BUSY and ERROR */
+ ret = otp_wait_busy(0);
+
+ }
+
+ return ret;
+}
+
+static int otp_write_post(struct mxc_otp_platform_data *otp_data)
+{
+ /* restore the clock and voltage */
+ if (otp_data->regulator_name) {
+ regulator_set_voltage(regu, otp_voltage_saved, otp_voltage_saved);
+ otp_wait_busy(0);
+ }
+
+ /* Reload all the shadow registers */
+ __raw_writel(BM_OCOTP_CTRL_RELOAD_SHADOWS,
+ REGS_OCOTP_BASE + HW_OCOTP_CTRL_SET);
+ udelay(1);
+ otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS);
+
+ return 0;
+}
+
+static int __init map_ocotp_addr(struct platform_device *pdev)
+{
+ struct resource *res;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ otp_base = ioremap(res->start, SZ_8K);
+ if (!otp_base) {
+ log("Can not remap the OTP iomem!");
+ return -1;
+ }
+ return 0;
+}
+
+static void unmap_ocotp_addr(void)
+{
+ iounmap(otp_base);
+ otp_base = NULL;
+}
+#endif /* CONFIG_SOC_IMX6Q */
+
+#endif
diff --git a/drivers/char/mxs_viim.c b/drivers/char/mxs_viim.c
new file mode 100644
index 00000000000..ae04b0bd8cc
--- /dev/null
+++ b/drivers/char/mxs_viim.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+static unsigned long iim_reg_base0, iim_reg_end0, iim_reg_size0;
+static unsigned long iim_reg_base1, iim_reg_end1, iim_reg_size1;
+static struct device *iim_dev;
+
+/*!
+ * MXS Virtual IIM interface - memory map function
+ * This function maps one page size VIIM registers from VIIM base address0
+ * if the size of the required virtual memory space is less than or equal to
+ * one page size, otherwise this function will also map one page size VIIM
+ * registers from VIIM base address1.
+ *
+ * @param file struct file *
+ * @param vma structure vm_area_struct *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxs_viim_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ size_t size = vma->vm_end - vma->vm_start;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
+ if (remap_pfn_range(vma,
+ vma->vm_start,
+ iim_reg_base0 >> PAGE_SHIFT,
+ iim_reg_size0,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ if (size > iim_reg_size0) {
+ if (remap_pfn_range(vma,
+ vma->vm_start + iim_reg_size0,
+ iim_reg_base1 >> PAGE_SHIFT,
+ iim_reg_size1,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/*!
+ * MXS Virtual IIM interface - open function
+ *
+ * @param inode struct inode *
+ * @param filp struct file *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxs_viim_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*!
+ * MXS Virtual IIM interface - release function
+ *
+ * @param inode struct inode *
+ * @param filp struct file *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxs_viim_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static const struct file_operations mxs_viim_fops = {
+ .mmap = mxs_viim_mmap,
+ .open = mxs_viim_open,
+ .release = mxs_viim_release,
+};
+
+static struct miscdevice mxs_viim_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mxs_viim",
+ .fops = &mxs_viim_fops,
+};
+
+/*!
+ * This function is called by the driver framework to get virtual iim base/end
+ * address and register iim misc device.
+ *
+ * @param dev The device structure for Virtual IIM passed in by the
+ * driver framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int mxs_viim_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ iim_dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get Virtual IIM resource 0\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base0 = res->start;
+ iim_reg_end0 = res->end;
+ iim_reg_size0 = iim_reg_end0 - iim_reg_base0 + 1;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get Virtual IIM resource 1\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base1 = res->start;
+ iim_reg_end1 = res->end;
+ iim_reg_size1 = iim_reg_end1 - iim_reg_base1 + 1;
+
+ ret = misc_register(&mxs_viim_miscdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mxs_viim_remove(struct platform_device *pdev)
+{
+ misc_deregister(&mxs_viim_miscdev);
+ return 0;
+}
+
+static const struct of_device_id mxs_viim_dt_ids[] = {
+ { .compatible = "fsl,imx6q-viim", },
+ { /* sentinel */ }
+};
+
+
+static struct platform_driver mxs_viim_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imx_viim",
+ .of_match_table = mxs_viim_dt_ids,
+ },
+ .probe = mxs_viim_probe,
+ .remove = mxs_viim_remove,
+};
+
+static int __init mxs_viim_dev_init(void)
+{
+ return platform_driver_register(&mxs_viim_driver);
+}
+
+static void __exit mxs_viim_dev_cleanup(void)
+{
+ platform_driver_unregister(&mxs_viim_driver);
+}
+
+module_init(mxs_viim_dev_init);
+module_exit(mxs_viim_dev_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IMX Virtual IIM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
diff --git a/drivers/char/regs-ocotp-v3.h b/drivers/char/regs-ocotp-v3.h
new file mode 100644
index 00000000000..d3c8de982fc
--- /dev/null
+++ b/drivers/char/regs-ocotp-v3.h
@@ -0,0 +1,367 @@
+/*
+ * Freescale OCOTP Register Definitions
+ *
+ * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This file is created by xml file. Don't Edit it.
+ *
+ * Xml Revision: 1.12
+ * Template revision: 1.3
+ */
+
+#ifndef __ARCH_ARM___OCOTP_H
+#define __ARCH_ARM___OCOTP_H
+
+
+#define HW_OCOTP_CTRL (0x00000000)
+#define HW_OCOTP_CTRL_SET (0x00000004)
+#define HW_OCOTP_CTRL_CLR (0x00000008)
+#define HW_OCOTP_CTRL_TOG (0x0000000c)
+
+#define BP_OCOTP_CTRL_WR_UNLOCK 16
+#define BM_OCOTP_CTRL_WR_UNLOCK 0xFFFF0000
+#define BF_OCOTP_CTRL_WR_UNLOCK(v) \
+ (((v) << 16) & BM_OCOTP_CTRL_WR_UNLOCK)
+#define BV_OCOTP_CTRL_WR_UNLOCK__KEY 0x3E77
+#define BP_OCOTP_CTRL_RSVD1 13
+#define BM_OCOTP_CTRL_RSVD1 0x0000E000
+#define BF_OCOTP_CTRL_RSVD1(v) \
+ (((v) << 13) & BM_OCOTP_CTRL_RSVD1)
+#define BM_OCOTP_CTRL_CRC_FAIL 0x00001000
+#define BM_OCOTP_CTRL_CRC_TEST 0x00000800
+#define BM_OCOTP_CTRL_RELOAD_SHADOWS 0x00000400
+#define BM_OCOTP_CTRL_ERROR 0x00000200
+#define BM_OCOTP_CTRL_BUSY 0x00000100
+#define BM_OCOTP_CTRL_RSVD0 0x00000080
+#define BP_OCOTP_CTRL_ADDR 0
+#define BM_OCOTP_CTRL_ADDR 0x0000007F
+#define BF_OCOTP_CTRL_ADDR(v) \
+ (((v) << 0) & BM_OCOTP_CTRL_ADDR)
+
+#define HW_OCOTP_TIMING (0x00000010)
+
+#define BP_OCOTP_TIMING_RSRVD0 28
+#define BM_OCOTP_TIMING_RSRVD0 0xF0000000
+#define BF_OCOTP_TIMING_RSRVD0(v) \
+ (((v) << 28) & BM_OCOTP_TIMING_RSRVD0)
+#define BP_OCOTP_TIMING_WAIT 22
+#define BM_OCOTP_TIMING_WAIT 0x0FC00000
+#define BF_OCOTP_TIMING_WAIT(v) \
+ (((v) << 22) & BM_OCOTP_TIMING_WAIT)
+#define BP_OCOTP_TIMING_STROBE_READ 16
+#define BM_OCOTP_TIMING_STROBE_READ 0x003F0000
+#define BF_OCOTP_TIMING_STROBE_READ(v) \
+ (((v) << 16) & BM_OCOTP_TIMING_STROBE_READ)
+#define BP_OCOTP_TIMING_RELAX 12
+#define BM_OCOTP_TIMING_RELAX 0x0000F000
+#define BF_OCOTP_TIMING_RELAX(v) \
+ (((v) << 12) & BM_OCOTP_TIMING_RELAX)
+#define BP_OCOTP_TIMING_STROBE_PROG 0
+#define BM_OCOTP_TIMING_STROBE_PROG 0x00000FFF
+#define BF_OCOTP_TIMING_STROBE_PROG(v) \
+ (((v) << 0) & BM_OCOTP_TIMING_STROBE_PROG)
+
+#define HW_OCOTP_DATA (0x00000020)
+
+#define BP_OCOTP_DATA_DATA 0
+#define BM_OCOTP_DATA_DATA 0xFFFFFFFF
+#define BF_OCOTP_DATA_DATA(v) (v)
+
+#define HW_OCOTP_READ_CTRL (0x00000030)
+
+#define BP_OCOTP_READ_CTRL_RSVD0 1
+#define BM_OCOTP_READ_CTRL_RSVD0 0xFFFFFFFE
+#define BF_OCOTP_READ_CTRL_RSVD0(v) \
+ (((v) << 1) & BM_OCOTP_READ_CTRL_RSVD0)
+#define BM_OCOTP_READ_CTRL_READ_FUSE 0x00000001
+
+#define HW_OCOTP_READ_FUSE_DATA (0x00000040)
+
+#define BP_OCOTP_READ_FUSE_DATA_DATA 0
+#define BM_OCOTP_READ_FUSE_DATA_DATA 0xFFFFFFFF
+#define BF_OCOTP_READ_FUSE_DATA_DATA(v) (v)
+
+#define HW_OCOTP_SW_STICKY (0x00000050)
+
+#define BP_OCOTP_SW_STICKY_RSVD0 5
+#define BM_OCOTP_SW_STICKY_RSVD0 0xFFFFFFE0
+#define BF_OCOTP_SW_STICKY_RSVD0(v) \
+ (((v) << 5) & BM_OCOTP_SW_STICKY_RSVD0)
+#define BM_OCOTP_SW_STICKY_JTAG_BLOCK_RELEASE 0x00000010
+#define BM_OCOTP_SW_STICKY_BLOCK_ROM_PART 0x00000008
+#define BM_OCOTP_SW_STICKY_FIELD_RETURN_LOCK 0x00000004
+#define BM_OCOTP_SW_STICKY_SRK_REVOKE_LOCK 0x00000002
+#define BM_OCOTP_SW_STICKY_BLOCK_DTCP_KEY 0x00000001
+
+#define HW_OCOTP_SCS (0x00000060)
+#define HW_OCOTP_SCS_SET (0x00000064)
+#define HW_OCOTP_SCS_CLR (0x00000068)
+#define HW_OCOTP_SCS_TOG (0x0000006c)
+
+#define BM_OCOTP_SCS_LOCK 0x80000000
+#define BP_OCOTP_SCS_SPARE 1
+#define BM_OCOTP_SCS_SPARE 0x7FFFFFFE
+#define BF_OCOTP_SCS_SPARE(v) \
+ (((v) << 1) & BM_OCOTP_SCS_SPARE)
+#define BM_OCOTP_SCS_HAB_JDE 0x00000001
+
+#define HW_OCOTP_CRC_ADDR (0x00000070)
+
+#define BP_OCOTP_CRC_ADDR_RSVD0 19
+#define BM_OCOTP_CRC_ADDR_RSVD0 0xFFF80000
+#define BF_OCOTP_CRC_ADDR_RSVD0(v) \
+ (((v) << 19) & BM_OCOTP_CRC_ADDR_RSVD0)
+#define BP_OCOTP_CRC_ADDR_CRC_ADDR 16
+#define BM_OCOTP_CRC_ADDR_CRC_ADDR 0x00070000
+#define BF_OCOTP_CRC_ADDR_CRC_ADDR(v) \
+ (((v) << 16) & BM_OCOTP_CRC_ADDR_CRC_ADDR)
+#define BP_OCOTP_CRC_ADDR_DATA_END_ADDR 8
+#define BM_OCOTP_CRC_ADDR_DATA_END_ADDR 0x0000FF00
+#define BF_OCOTP_CRC_ADDR_DATA_END_ADDR(v) \
+ (((v) << 8) & BM_OCOTP_CRC_ADDR_DATA_END_ADDR)
+#define BP_OCOTP_CRC_ADDR_DATA_START_ADDR 0
+#define BM_OCOTP_CRC_ADDR_DATA_START_ADDR 0x000000FF
+#define BF_OCOTP_CRC_ADDR_DATA_START_ADDR(v) \
+ (((v) << 0) & BM_OCOTP_CRC_ADDR_DATA_START_ADDR)
+
+#define HW_OCOTP_CRC_VALUE (0x00000080)
+
+#define BP_OCOTP_CRC_VALUE_DATA 0
+#define BM_OCOTP_CRC_VALUE_DATA 0xFFFFFFFF
+#define BF_OCOTP_CRC_VALUE_DATA(v) (v)
+
+#define HW_OCOTP_VERSION (0x00000090)
+
+#define BP_OCOTP_VERSION_MAJOR 24
+#define BM_OCOTP_VERSION_MAJOR 0xFF000000
+#define BF_OCOTP_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_OCOTP_VERSION_MAJOR)
+#define BP_OCOTP_VERSION_MINOR 16
+#define BM_OCOTP_VERSION_MINOR 0x00FF0000
+#define BF_OCOTP_VERSION_MINOR(v) \
+ (((v) << 16) & BM_OCOTP_VERSION_MINOR)
+#define BP_OCOTP_VERSION_STEP 0
+#define BM_OCOTP_VERSION_STEP 0x0000FFFF
+#define BF_OCOTP_VERSION_STEP(v) \
+ (((v) << 0) & BM_OCOTP_VERSION_STEP)
+
+#define HW_OCOTP_LOCK (0x00000400)
+
+#define BP_OCOTP_LOCK_UNALLOCATED 30
+#define BM_OCOTP_LOCK_UNALLOCATED 0xC0000000
+#define BF_OCOTP_LOCK_UNALLOCATED(v) \
+ (((v) << 30) & BM_OCOTP_LOCK_UNALLOCATED)
+#define BP_OCOTP_LOCK_CRC_GP_HI_LOCK 28
+#define BM_OCOTP_LOCK_CRC_GP_HI_LOCK 0x30000000
+#define BF_OCOTP_LOCK_CRC_GP_HI_LOCK(v) \
+ (((v) << 28) & BM_OCOTP_LOCK_CRC_GP_HI_LOCK)
+#define BP_OCOTP_LOCK_CRC_GP_LO_LOCK 26
+#define BM_OCOTP_LOCK_CRC_GP_LO_LOCK 0x0C000000
+#define BF_OCOTP_LOCK_CRC_GP_LO_LOCK(v) \
+ (((v) << 26) & BM_OCOTP_LOCK_CRC_GP_LO_LOCK)
+#define BM_OCOTP_LOCK_PIN 0x02000000
+#define BM_OCOTP_LOCK_RSVD2 0x01000000
+#define BM_OCOTP_LOCK_DTCP_DEV_CERT 0x00800000
+#define BM_OCOTP_LOCK_MISC_CONF 0x00400000
+#define BM_OCOTP_LOCK_HDCP_KEYS 0x00200000
+#define BM_OCOTP_LOCK_HDCP_KSV 0x00100000
+#define BP_OCOTP_LOCK_ANALOG 18
+#define BM_OCOTP_LOCK_ANALOG 0x000C0000
+#define BF_OCOTP_LOCK_ANALOG(v) \
+ (((v) << 18) & BM_OCOTP_LOCK_ANALOG)
+#define BM_OCOTP_LOCK_OTPMK 0x00020000
+#define BM_OCOTP_LOCK_DTCP_KEY 0x00010000
+#define BM_OCOTP_LOCK_RSVD1 0x00008000
+#define BM_OCOTP_LOCK_SRK 0x00004000
+#define BP_OCOTP_LOCK_GP2 12
+#define BM_OCOTP_LOCK_GP2 0x00003000
+#define BF_OCOTP_LOCK_GP2(v) \
+ (((v) << 12) & BM_OCOTP_LOCK_GP2)
+#define BP_OCOTP_LOCK_GP1 10
+#define BM_OCOTP_LOCK_GP1 0x00000C00
+#define BF_OCOTP_LOCK_GP1(v) \
+ (((v) << 10) & BM_OCOTP_LOCK_GP1)
+#define BP_OCOTP_LOCK_MAC_ADDR 8
+#define BM_OCOTP_LOCK_MAC_ADDR 0x00000300
+#define BF_OCOTP_LOCK_MAC_ADDR(v) \
+ (((v) << 8) & BM_OCOTP_LOCK_MAC_ADDR)
+#define BM_OCOTP_LOCK_RSVD0 0x00000080
+#define BM_OCOTP_LOCK_SJC_RESP 0x00000040
+#define BP_OCOTP_LOCK_MEM_TRIM 4
+#define BM_OCOTP_LOCK_MEM_TRIM 0x00000030
+#define BF_OCOTP_LOCK_MEM_TRIM(v) \
+ (((v) << 4) & BM_OCOTP_LOCK_MEM_TRIM)
+#define BP_OCOTP_LOCK_BOOT_CFG 2
+#define BM_OCOTP_LOCK_BOOT_CFG 0x0000000C
+#define BF_OCOTP_LOCK_BOOT_CFG(v) \
+ (((v) << 2) & BM_OCOTP_LOCK_BOOT_CFG)
+#define BP_OCOTP_LOCK_TESTER 0
+#define BM_OCOTP_LOCK_TESTER 0x00000003
+#define BF_OCOTP_LOCK_TESTER(v) \
+ (((v) << 0) & BM_OCOTP_LOCK_TESTER)
+
+/*
+ * multi-register-define name HW_OCOTP_CFGn
+ * base 0x00000410
+ * count 7
+ * offset 0x10
+ */
+#define HW_OCOTP_CFGn(n) (0x00000410 + (n) * 0x10)
+#define BP_OCOTP_CFGn_BITS 0
+#define BM_OCOTP_CFGn_BITS 0xFFFFFFFF
+#define BF_OCOTP_CFGn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_MEMn
+ * base 0x00000480
+ * count 5
+ * offset 0x10
+ */
+#define HW_OCOTP_MEMn(n) (0x00000480 + (n) * 0x10)
+#define BP_OCOTP_MEMn_BITS 0
+#define BM_OCOTP_MEMn_BITS 0xFFFFFFFF
+#define BF_OCOTP_MEMn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_ANAn
+ * base 0x000004D0
+ * count 3
+ * offset 0x10
+ */
+#define HW_OCOTP_ANAn(n) (0x000004d0 + (n) * 0x10)
+#define BP_OCOTP_ANAn_BITS 0
+#define BM_OCOTP_ANAn_BITS 0xFFFFFFFF
+#define BF_OCOTP_ANAn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_OTPMKn
+ * base 0x00000500
+ * count 8
+ * offset 0x10
+ */
+#define HW_OCOTP_OTPMKn(n) (0x00000500 + (n) * 0x10)
+#define BP_OCOTP_OTPMKn_BITS 0
+#define BM_OCOTP_OTPMKn_BITS 0xFFFFFFFF
+#define BF_OCOTP_OTPMKn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_SRKn
+ * base 0x00000580
+ * count 8
+ * offset 0x10
+ */
+#define HW_OCOTP_SRKn(n) (0x00000580 + (n) * 0x10)
+#define BP_OCOTP_SRKn_BITS 0
+#define BM_OCOTP_SRKn_BITS 0xFFFFFFFF
+#define BF_OCOTP_SRKn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_SJC_RESPn
+ * base 0x00000600
+ * count 2
+ * offset 0x10
+ */
+#define HW_OCOTP_SJC_RESPn(n) (0x00000600 + (n) * 0x10)
+#define BP_OCOTP_SJC_RESPn_BITS 0
+#define BM_OCOTP_SJC_RESPn_BITS 0xFFFFFFFF
+#define BF_OCOTP_SJC_RESPn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_MACn
+ * base 0x00000620
+ * count 2
+ * offset 0x10
+ */
+#define HW_OCOTP_MACn(n) (0x00000620 + (n) * 0x10)
+#define BP_OCOTP_MACn_BITS 0
+#define BM_OCOTP_MACn_BITS 0xFFFFFFFF
+#define BF_OCOTP_MACn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_HDCP_KSVn
+ * base 0x00000640
+ * count 2
+ * offset 0x10
+ */
+#define HW_OCOTP_HDCP_KSVn(n) (0x00000640 + (n) * 0x10)
+#define BP_OCOTP_HDCP_KSVn_BITS 0
+#define BM_OCOTP_HDCP_KSVn_BITS 0xFFFFFFFF
+#define BF_OCOTP_HDCP_KSVn_BITS(v) (v)
+
+#define HW_OCOTP_GP1 (0x00000660)
+
+#define BP_OCOTP_GP1_BITS 0
+#define BM_OCOTP_GP1_BITS 0xFFFFFFFF
+#define BF_OCOTP_GP1_BITS(v) (v)
+
+#define HW_OCOTP_GP2 (0x00000670)
+
+#define BP_OCOTP_GP2_BITS 0
+#define BM_OCOTP_GP2_BITS 0xFFFFFFFF
+#define BF_OCOTP_GP2_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_DTCP_KEYn
+ * base 0x00000680
+ * count 5
+ * offset 0x10
+ */
+#define HW_OCOTP_DTCP_KEYn(n) (0x00000680 + (n) * 0x10)
+#define BP_OCOTP_DTCP_KEYn_BITS 0
+#define BM_OCOTP_DTCP_KEYn_BITS 0xFFFFFFFF
+#define BF_OCOTP_DTCP_KEYn_BITS(v) (v)
+
+#define HW_OCOTP_MISC_CONF (0x000006d0)
+
+#define BP_OCOTP_MISC_CONF_BITS 0
+#define BM_OCOTP_MISC_CONF_BITS 0xFFFFFFFF
+#define BF_OCOTP_MISC_CONF_BITS(v) (v)
+
+#define HW_OCOTP_FIELD_RETURN (0x000006e0)
+
+#define BP_OCOTP_FIELD_RETURN_BITS 0
+#define BM_OCOTP_FIELD_RETURN_BITS 0xFFFFFFFF
+#define BF_OCOTP_FIELD_RETURN_BITS(v) (v)
+
+#define HW_OCOTP_SRK_REVOKE (0x000006f0)
+
+#define BP_OCOTP_SRK_REVOKE_BITS 0
+#define BM_OCOTP_SRK_REVOKE_BITS 0xFFFFFFFF
+#define BF_OCOTP_SRK_REVOKE_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_HDCP_KEYn
+ * base 0x00000800
+ * count 72
+ * offset 0x10
+ */
+#define HW_OCOTP_HDCP_KEYn(n) (0x00000800 + (n) * 0x10)
+#define BP_OCOTP_HDCP_KEYn_BITS 0
+#define BM_OCOTP_HDCP_KEYn_BITS 0xFFFFFFFF
+#define BF_OCOTP_HDCP_KEYn_BITS(v) (v)
+
+/*
+ * multi-register-define name HW_OCOTP_CRCn
+ * base 0x00000D00
+ * count 8
+ * offset 0x10
+ */
+#define HW_OCOTP_CRCn(n) (0x00000d00 + (n) * 0x10)
+#define BP_OCOTP_CRCn_BITS 0
+#define BM_OCOTP_CRCn_BITS 0xFFFFFFFF
+#define BF_OCOTP_CRCn_BITS(v) (v)
+#endif /* __ARCH_ARM___OCOTP_H */
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 53eb4e55b28..43edf2218cb 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -634,6 +634,14 @@ config RTC_MXC
This driver can also be built as a module, if so, the module
will be called "rtc-mxc".
+config RTC_DRV_SNVS
+ tristate "Freescale SNVS Real Time Clock"
+ depends on ARCH_MXC
+ depends on RTC_CLASS
+ help
+ If you say yes here you get support for the Freescale SNVS
+ Low Power (LP) RTC module.
+
config RTC_DRV_BQ4802
tristate "TI BQ4802"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e6982335c1..8b30686c6d9 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
+obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
new file mode 100644
index 00000000000..d6e82ceaa56
--- /dev/null
+++ b/drivers/rtc/rtc-snvs.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/*
+ * Implementation based on rtc-ds1553.c
+ */
+
+/*!
+ * @defgroup RTC Real Time Clock (RTC) Driver
+ */
+/*!
+ * @file rtc-snvs.c
+ * @brief Secure Real Time Clock (SRTC) interface in Freescale's SNVS module
+ *
+ * This file contains Real Time Clock interface for Linux. The Freescale
+ * SNVS module's Low Power (LP) SRTC functionality is utilized in this driver,
+ * in non-secure mode.
+ *
+ * @ingroup RTC
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+/* Register definitions */
+#define SNVS_HPSR 0x14 /* HP Status Register */
+#define SNVS_LPCR 0x38 /* LP Control Register */
+#define SNVS_LPSR 0x4C /* LP Status Register */
+#define SNVS_LPSRTCMR 0x50 /* LP Secure Real Time Counter MSB Register */
+#define SNVS_LPSRTCLR 0x54 /* LP Secure Real Time Counter LSB Register */
+#define SNVS_LPTAR 0x58 /* LP Time Alarm Register */
+#define SNVS_LPSMCMR 0x5C /* LP Secure Monotonic Counter MSB Register */
+#define SNVS_LPSMCLR 0x60 /* LP Secure Monotonic Counter LSB Register */
+#define SNVS_LPPGDR 0x64 /* LP Power Glitch Detector Register */
+#define SNVS_LPGPR 0x68 /* LP General Purpose Register */
+
+/* Bit Definitions */
+#define SNVS_HPSR_SSM_ST_MASK 0x00000F00
+#define SNVS_HPSR_SSM_ST_SHIFT 8
+
+#define SNVS_LPCR_SRTC_ENV (1 << 0)
+#define SNVS_LPCR_LPTA_EN (1 << 1)
+#define SNVS_LPCR_LPWUI_EN (1 << 3)
+
+#define SNVS_LPCR_ALL_INT_EN (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN)
+
+#define SNVS_LPSR_LPTA (1 << 0)
+
+#define SNVS_LPPGDR_INIT 0x41736166
+
+/* Other defines */
+#define SSM_ST_CHECK 0x9
+#define SSM_ST_NON_SECURE 0xB
+#define CNTR_TO_SECS_SH 15 /* Converts 47-bit counter to 32-bit seconds */
+
+struct rtc_drv_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ unsigned long baseaddr;
+ int irq;
+ bool irq_enable;
+};
+
+static unsigned long rtc_status;
+
+static DEFINE_SPINLOCK(rtc_lock);
+
+/*!
+ * LP counter register reads should always use this function.
+ * This function reads 2 consective times from LP counter register
+ * until the 2 values match. This is to avoid reading corrupt
+ * value if the counter is in the middle of updating
+ */
+static inline u32 rtc_read_lp_counter(void __iomem *counter_reg)
+{
+ u64 read1, read2;
+ u32 counter_sec;
+
+ do {
+ /* MSB */
+ read1 = __raw_readl(counter_reg);
+ read1 <<= 32;
+ /* LSB */
+ read1 |= __raw_readl(counter_reg + 4);
+
+ /* MSB */
+ read2 = __raw_readl(counter_reg);
+ read2 <<= 32;
+ /* LSB */
+ read2 |= __raw_readl(counter_reg + 4);
+ } while (read1 != read2);
+
+ /* Convert 47-bit counter to 32-bit raw second count */
+ counter_sec = (u32) (read1 >> CNTR_TO_SECS_SH);
+ return counter_sec;
+}
+
+/*!
+ * This function does write synchronization for writes to the lp srtc block.
+ * To take care of the asynchronous CKIL clock, all writes from the IP domain
+ * will be synchronized to the CKIL domain.
+ */
+static inline void rtc_write_sync_lp(void __iomem *ioaddr)
+{
+ unsigned int i, count1, count2, count3;
+
+ /* Wait for 3 CKIL cycles */
+ for (i = 0; i < 3; i++) {
+
+ /* Do consective reads of LSB of counter to ensure integrity */
+ do {
+ count1 = __raw_readl(ioaddr + SNVS_LPSRTCLR);
+ count2 = __raw_readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count1 != count2);
+
+ /* Now wait until counter value changes */
+ do {
+ do {
+ count2 = __raw_readl(ioaddr + SNVS_LPSRTCLR);
+ count3 = __raw_readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count2 != count3);
+ } while (count3 == count1);
+ }
+}
+
+/*!
+ * This function updates the RTC alarm registers and then clears all the
+ * interrupt status bits.
+ *
+ * @param alrm the new alarm value to be updated in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ struct rtc_time alarm_tm, now_tm;
+ unsigned long now, time, lp_cr;
+ int ret;
+
+ now = rtc_read_lp_counter(ioaddr + SNVS_LPSRTCMR);
+ rtc_time_to_tm(now, &now_tm);
+
+ alarm_tm.tm_year = now_tm.tm_year;
+ alarm_tm.tm_mon = now_tm.tm_mon;
+ alarm_tm.tm_mday = now_tm.tm_mday;
+
+ alarm_tm.tm_hour = alrm->tm_hour;
+ alarm_tm.tm_min = alrm->tm_min;
+ alarm_tm.tm_sec = alrm->tm_sec;
+
+ rtc_tm_to_time(&now_tm, &now);
+ rtc_tm_to_time(&alarm_tm, &time);
+
+ if (time < now) {
+ time += 60 * 60 * 24;
+ rtc_time_to_tm(time, &alarm_tm);
+ }
+ ret = rtc_tm_to_time(&alarm_tm, &time);
+
+ /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
+ lp_cr = __raw_readl(ioaddr + SNVS_LPCR);
+ __raw_writel(lp_cr & ~SNVS_LPCR_LPTA_EN, ioaddr + SNVS_LPCR);
+ rtc_write_sync_lp(ioaddr);
+
+ __raw_writel(time, ioaddr + SNVS_LPTAR);
+
+ /* clear alarm interrupt status bit */
+ __raw_writel(SNVS_LPSR_LPTA, ioaddr + SNVS_LPSR);
+
+ return ret;
+}
+
+/*!
+ * This function is the RTC interrupt service routine.
+ *
+ * @param irq RTC IRQ number
+ * @param dev_id device ID which is not used
+ *
+ * @return IRQ_HANDLED as defined in the include/linux/interrupt.h file.
+ */
+static irqreturn_t snvs_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 lp_status, lp_cr;
+ u32 events = 0;
+
+ lp_status = __raw_readl(ioaddr + SNVS_LPSR);
+ lp_cr = __raw_readl(ioaddr + SNVS_LPCR);
+
+ /* update irq data & counter */
+ if (lp_status & SNVS_LPSR_LPTA) {
+ if (lp_cr & SNVS_LPCR_LPTA_EN)
+ events |= (RTC_AF | RTC_IRQF);
+
+ /* disable further lp alarm interrupts */
+ lp_cr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ }
+
+ /* Update interrupt enables */
+ __raw_writel(lp_cr, ioaddr + SNVS_LPCR);
+
+ /* If no interrupts are enabled, turn off interrupts in kernel */
+ if (((lp_cr & SNVS_LPCR_ALL_INT_EN) == 0) && (pdata->irq_enable)) {
+ disable_irq_nosync(pdata->irq);
+ pdata->irq_enable = false;
+ }
+
+ /* clear interrupt status */
+ __raw_writel(lp_status, ioaddr + SNVS_LPSR);
+
+ rtc_write_sync_lp(ioaddr);
+ rtc_update_irq(pdata->rtc, 1, events);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is used to open the RTC driver.
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int snvs_rtc_open(struct device *dev)
+{
+ if (test_and_set_bit(1, &rtc_status))
+ return -EBUSY;
+ return 0;
+}
+
+/*!
+ * clear all interrupts and release the IRQ
+ */
+static void snvs_rtc_release(struct device *dev)
+{
+ rtc_status = 0;
+}
+
+/*!
+ * This function reads the current RTC time into tm in Gregorian date.
+ *
+ * @param tm contains the RTC time value upon return
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ rtc_time_to_tm(rtc_read_lp_counter(ioaddr + SNVS_LPSRTCMR), tm);
+ return 0;
+}
+
+/*!
+ * This function sets the internal RTC time based on tm in Gregorian date.
+ *
+ * @param tm the time value to be set in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long time;
+ int ret;
+ u32 lp_cr;
+
+ ret = rtc_tm_to_time(tm, &time);
+ if (ret != 0)
+ return ret;
+
+ /* Disable RTC first */
+ lp_cr = __raw_readl(ioaddr + SNVS_LPCR) & ~SNVS_LPCR_SRTC_ENV;
+ __raw_writel(lp_cr, ioaddr + SNVS_LPCR);
+ while (__raw_readl(ioaddr + SNVS_LPCR) & SNVS_LPCR_SRTC_ENV)
+ ;
+
+ /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
+ __raw_writel(time << CNTR_TO_SECS_SH, ioaddr + SNVS_LPSRTCLR);
+ __raw_writel(time >> (32 - CNTR_TO_SECS_SH), ioaddr + SNVS_LPSRTCMR);
+
+ /* Enable RTC again */
+ __raw_writel(lp_cr | SNVS_LPCR_SRTC_ENV, ioaddr + SNVS_LPCR);
+ while (!(__raw_readl(ioaddr + SNVS_LPCR) & SNVS_LPCR_SRTC_ENV))
+ ;
+
+ return 0;
+}
+
+/*!
+ * This function reads the current alarm value into the passed in \b alrm
+ * argument. It updates the \b alrm's pending field value based on the whether
+ * an alarm interrupt occurs or not.
+ *
+ * @param alrm contains the RTC alarm value upon return
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ rtc_time_to_tm(__raw_readl(ioaddr + SNVS_LPTAR), &alrm->time);
+ alrm->pending =
+ ((__raw_readl(ioaddr + SNVS_LPSR) & SNVS_LPSR_LPTA) != 0) ? 1 : 0;
+
+ return 0;
+}
+
+/*!
+ * This function sets the RTC alarm based on passed in alrm.
+ *
+ * @param alrm the alarm value to be set in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long lock_flags = 0;
+ u32 lp_cr;
+ int ret;
+
+ if (rtc_valid_tm(&alrm->time)) {
+ if (alrm->time.tm_sec > 59 ||
+ alrm->time.tm_hour > 23 || alrm->time.tm_min > 59) {
+ return -EINVAL;
+ }
+ }
+
+ spin_lock_irqsave(&rtc_lock, lock_flags);
+
+ ret = rtc_update_alarm(dev, &alrm->time);
+ if (ret)
+ goto out;
+
+ lp_cr = __raw_readl(ioaddr + SNVS_LPCR);
+
+ if (alrm->enabled)
+ lp_cr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ else
+ lp_cr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+
+ if (lp_cr & SNVS_LPCR_ALL_INT_EN) {
+ if (!pdata->irq_enable) {
+ enable_irq(pdata->irq);
+ pdata->irq_enable = true;
+ }
+ } else {
+ if (pdata->irq_enable) {
+ disable_irq(pdata->irq);
+ pdata->irq_enable = false;
+ }
+ }
+
+ __raw_writel(lp_cr, ioaddr + SNVS_LPCR);
+
+out:
+ rtc_write_sync_lp(ioaddr);
+ spin_unlock_irqrestore(&rtc_lock, lock_flags);
+ return ret;
+}
+
+/*!
+ * This function is used to provide the content for the /proc/driver/rtc
+ * file.
+ *
+ * @param seq buffer to hold the information that the driver wants to write
+ *
+ * @return The number of bytes written into the rtc file.
+ */
+static int snvs_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ (((__raw_readl(ioaddr + SNVS_LPCR)) & SNVS_LPCR_LPTA_EN) !=
+ 0) ? "yes" : "no");
+
+ return 0;
+}
+
+static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 lp_cr;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(&rtc_lock, lock_flags);
+
+ if (enable) {
+ if (!pdata->irq_enable) {
+ enable_irq(pdata->irq);
+ pdata->irq_enable = true;
+ }
+ lp_cr = __raw_readl(ioaddr + SNVS_LPCR);
+ lp_cr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ __raw_writel(lp_cr, ioaddr + SNVS_LPCR);
+ } else {
+ lp_cr = __raw_readl(ioaddr + SNVS_LPCR);
+ lp_cr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ if (((lp_cr & SNVS_LPCR_ALL_INT_EN) == 0)
+ && (pdata->irq_enable)) {
+ disable_irq(pdata->irq);
+ pdata->irq_enable = false;
+ }
+ __raw_writel(lp_cr, ioaddr + SNVS_LPCR);
+ }
+
+ rtc_write_sync_lp(ioaddr);
+ spin_unlock_irqrestore(&rtc_lock, lock_flags);
+ return 0;
+}
+
+/*!
+ * The RTC driver structure
+ */
+static struct rtc_class_ops snvs_rtc_ops = {
+ .open = snvs_rtc_open,
+ .release = snvs_rtc_release,
+ .read_time = snvs_rtc_read_time,
+ .set_time = snvs_rtc_set_time,
+ .read_alarm = snvs_rtc_read_alarm,
+ .set_alarm = snvs_rtc_set_alarm,
+ .proc = snvs_rtc_proc,
+ .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
+};
+
+/*! SNVS RTC Power management control */
+static int snvs_rtc_probe(struct platform_device *pdev)
+{
+ struct timespec tv;
+ struct resource *res;
+ struct rtc_device *rtc;
+ struct rtc_drv_data *pdata = NULL;
+ void __iomem *ioaddr;
+ u32 lp_cr;
+ int ret = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ioaddr = ioremap(res->start, resource_size(res));
+ if (ioaddr == NULL) {
+ kfree(pdata);
+ return -ENOMEM;
+ }
+
+ pdata->baseaddr = res->start;
+ pdata->ioaddr = ioaddr;
+
+ /* Configure and enable the RTC */
+ pdata->irq = platform_get_irq(pdev, 0);
+ if (pdata->irq >= 0) {
+ if (request_irq(pdata->irq, snvs_rtc_interrupt, IRQF_SHARED,
+ pdev->name, pdev) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ } else {
+ disable_irq(pdata->irq);
+ pdata->irq_enable = false;
+ }
+ }
+
+ /* initialize glitch detect */
+ __raw_writel(SNVS_LPPGDR_INIT, ioaddr + SNVS_LPPGDR);
+ udelay(100);
+
+ /* clear lp interrupt status */
+ __raw_writel(0xFFFFFFFF, ioaddr + SNVS_LPSR);
+
+ /* Enable RTC */
+ lp_cr = __raw_readl(ioaddr + SNVS_LPCR);
+ if ((lp_cr & SNVS_LPCR_SRTC_ENV) == 0)
+ __raw_writel(lp_cr | SNVS_LPCR_SRTC_ENV, ioaddr + SNVS_LPCR);
+
+ udelay(100);
+
+ __raw_writel(0xFFFFFFFF, ioaddr + SNVS_LPSR);
+ udelay(100);
+
+ platform_set_drvdata(pdev, pdata);
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &snvs_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto err_out;
+ }
+
+ pdata->rtc = rtc;
+
+ tv.tv_nsec = 0;
+ tv.tv_sec = rtc_read_lp_counter(ioaddr + SNVS_LPSRTCMR);
+
+ /* By default, devices should wakeup if they can */
+ /* So snvs is set as "should wakeup" as it can */
+ device_init_wakeup(&pdev->dev, 1);
+
+ return ret;
+
+err_out:
+ iounmap(ioaddr);
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdev);
+ kfree(pdata);
+ return ret;
+}
+
+static int __exit snvs_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+ rtc_device_unregister(pdata->rtc);
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdev);
+
+ kfree(pdata);
+ return 0;
+}
+
+/*!
+ * This function is called to save the system time delta relative to
+ * the SNVS RTC when enterring a low power state. This time delta is
+ * then used on resume to adjust the system time to account for time
+ * loss while suspended.
+ *
+ * @param pdev not used
+ * @param state Power state to enter.
+ *
+ * @return The function always returns 0.
+ */
+static int snvs_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ enable_irq_wake(pdata->irq);
+ } else {
+ if (pdata->irq_enable)
+ disable_irq(pdata->irq);
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called to correct the system time based on the
+ * current SNVS RTC time relative to the time delta saved during
+ * suspend.
+ *
+ * @param pdev not used
+ *
+ * @return The function always returns 0.
+ */
+static int snvs_rtc_resume(struct platform_device *pdev)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(pdata->irq);
+ } else {
+ if (pdata->irq_enable)
+ enable_irq(pdata->irq);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id snvs_dt_ids[] = {
+ { .compatible = "fsl,snvs" },
+ { /* sentinel */ }
+};
+
+/*!
+ * Contains pointers to the power management callback functions.
+ */
+static struct platform_driver snvs_rtc_driver = {
+ .driver = {
+ .name = "snvs_rtc",
+ .owner = THIS_MODULE,
+ .of_match_table = snvs_dt_ids,
+ },
+ .probe = snvs_rtc_probe,
+ .remove = __exit_p(snvs_rtc_remove),
+ .suspend = snvs_rtc_suspend,
+ .resume = snvs_rtc_resume,
+};
+
+/*!
+ * This function creates the /proc/driver/rtc file and registers the device RTC
+ * in the /dev/misc directory. It also reads the RTC value from external source
+ * and setup the internal RTC properly.
+ *
+ * @return -1 if RTC is failed to initialize; 0 is successful.
+ */
+static int __init snvs_rtc_init(void)
+{
+ return platform_driver_register(&snvs_rtc_driver);
+}
+
+/*!
+ * This function removes the /proc/driver/rtc file and un-registers the
+ * device RTC from the /dev/misc directory.
+ */
+static void __exit snvs_rtc_exit(void)
+{
+ platform_driver_unregister(&snvs_rtc_driver);
+
+}
+
+module_init(snvs_rtc_init);
+module_exit(snvs_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SNVS Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 79fd606b7cd..7c3064552ef 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -333,7 +333,7 @@ config MAX63XX_WATCHDOG
config IMX2_WDT
tristate "IMX2+ Watchdog"
- depends on IMX_HAVE_PLATFORM_IMX2_WDT
+ depends on IMX_HAVE_PLATFORM_IMX2_WDT || SOC_IMX6Q
help
This is the driver for the hardware watchdog
on the Freescale IMX2 and later processors.
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index fffdf00f87b..76a8c67cda0 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -103,6 +103,14 @@ struct fsl_usb2_platform_data {
#define FLS_USB2_WORKAROUND_ENGCM09152 (1 << 0)
+struct mxc_otp_platform_data {
+ char **fuse_name;
+ char *regulator_name;
+ unsigned int min_volt;
+ unsigned int max_volt;
+ unsigned int fuse_num;
+};
+
struct spi_device;
struct fsl_spi_platform_data {