]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00139235-3: IIM(OCOPT): Enable IIM driver for iMX6Q
authorTerry Lv <r65388@freescale.com>
Tue, 21 Jun 2011 06:29:52 +0000 (14:29 +0800)
committerOliver Wendt <ow@karo-electronics.de>
Mon, 30 Sep 2013 12:09:16 +0000 (14:09 +0200)
Add a new driver for On-Chip OTP controller. The driver
will register all the register names of all the banks to /sys/.
You can use the following commands to manipulate the OTP banks:

read:
#cat HW_OCOTP_MAC0
write:
#echo 0x11223344 > HW_OCOTP_MAC0

Signed-off-by: Terry Lv <r65388@freescale.com>
drivers/char/Kconfig
drivers/char/fsl_otp.c
drivers/char/fsl_otp.h

index b9c85171b4ea958370d282cd9194248600d8b00a..6165732eedc598ca24bd6fca9aa3dab43bc93e8c 100644 (file)
@@ -111,7 +111,7 @@ config BFIN_OTP_WRITE_ENABLE
 
 config FSL_OTP
        tristate "Freescale On-Chip OTP Memory Support"
-       depends on (ARCH_MX23 || ARCH_MX28 || ARCH_MX50)
+       depends on (ARCH_MX23 || ARCH_MX28 || ARCH_MX50 || ARCH_MX6)
        default n
        help
          If you say Y here, you will get support for a character device
@@ -634,7 +634,7 @@ config MSM_SMD_PKT
 
 config MXC_IIM
        tristate "MXC IIM device driver"
-       depends on ARCH_MXC
+       depends on (ARCH_MX51 || ARCH_MX53)
        help
          Support for access to MXC IIM device, most people should say N here.
 
index d083036929f87a7931e1f6cc50e7f5cba0c707c5..5517e555012814549a9ff1f19c9e5753d108028c 100755 (executable)
@@ -74,12 +74,12 @@ static ssize_t otp_show(struct kobject *kobj, struct kobj_attribute *attr,
 
        mutex_lock(&otp_mutex);
 
-       if (otp_read_prepare()) {
+       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_read_post(otp_data);
 
        mutex_unlock(&otp_mutex);
        return sprintf(buf, "0x%x\n", value);
@@ -122,7 +122,7 @@ static ssize_t otp_store(struct kobject *kobj, struct kobj_attribute *attr,
                return 0;
        }
        otp_write_bits(index, value, 0x3e77);
-       otp_write_post();
+       otp_write_post(otp_data);
        mutex_unlock(&otp_mutex);
        return count;
 }
index f1124892d4657e99e7ca5a2cdad037b1942aad95..fa2b21e12a8843af3b8b2727fe6a93e17fec1bd3 100755 (executable)
@@ -21,6 +21,9 @@
 #define log(a, ...) printk(KERN_INFO "[ %s : %.3d ] "a"\n", \
                        __func__, __LINE__,  ## __VA_ARGS__)
 
+static u32 otp_voltage_saved;
+struct regulator *regu;
+
 static int otp_wait_busy(u32 flags);
 
 /* IMX23 and IMX28 share most of the defination ========================= */
@@ -42,11 +45,9 @@ static int otp_wait_busy(u32 flags);
 #define BF(value, field)       (((value) << BP_##field) & BM_##field)
 
 static unsigned long otp_hclk_saved;
-static u32 otp_voltage_saved;
-struct regulator *regu;
 
 /* open the bank for the imx23/imx28 */
-static int otp_read_prepare(void)
+static int otp_read_prepare(struct mxc_otp_platform_data *otp_data)
 {
        int r;
 
@@ -68,7 +69,7 @@ error:
        return r;
 }
 
-static int otp_read_post(void)
+static int otp_read_post(struct mxc_otp_platform_data *otp_data)
 {
        /* [5] close bank */
        __raw_writel(BM_OCOTP_CTRL_RD_BANK_OPEN,
@@ -76,13 +77,13 @@ static int otp_read_post(void)
        return 0;
 }
 
-static int otp_write_prepare(struct fsl_otp_data *otp_data)
+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, "hclk");
+       hclk = clk_get(NULL, otp_data->clock_name);
        if (IS_ERR(hclk)) {
                err = PTR_ERR(hclk);
                goto out;
@@ -102,9 +103,11 @@ static int otp_write_prepare(struct fsl_otp_data *otp_data)
        clk_set_rate(hclk, 24000);
 
        /* [2] The voltage is set to 2.8V */
-       regu = regulator_get(NULL, otp_data->regulator_name);
-       otp_voltage_saved = regulator_get_voltage(regu);
-       regulator_set_voltage(regu, 2800000, 2800000);
+       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);
@@ -112,15 +115,16 @@ out:
        return err;
 }
 
-static int otp_write_post(void)
+static int otp_write_post(struct mxc_otp_platform_data *otp_data)
 {
        struct clk *hclk;
 
-       hclk = clk_get(NULL, "hclk");
+       hclk = clk_get(NULL, otp_data->clock_name);
 
        /* restore the clock and voltage */
        clk_set_rate(hclk, otp_hclk_saved);
-       regulator_set_voltage(regu, otp_voltage_saved, otp_voltage_saved);
+       if (otp_data->regulator_name)
+               regulator_set_voltage(regu, otp_voltage_saved, otp_voltage_saved);
        otp_wait_busy(0);
 
        /* reset */
@@ -152,15 +156,18 @@ static void *otp_base;
 
 #define DEF_RELEX      (15)    /* > 10.5ns */
 
-static int set_otp_timing(void)
+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, "ahb_clk");
+       apb_clk = clk_get(NULL, otp_data->clock_name);
        if (IS_ERR(apb_clk)) {
                log("we can not find the clock");
                return -1;
@@ -181,11 +188,11 @@ static int set_otp_timing(void)
 }
 
 /* IMX5 does not need to open the bank anymore */
-static int otp_read_prepare(void)
+static int otp_read_prepare(struct mxc_otp_platform_data *otp_data)
 {
        return set_otp_timing();
 }
-static int otp_read_post(void)
+static int otp_read_post(struct mxc_otp_platform_data *otp_data)
 {
        return 0;
 }
@@ -203,13 +210,140 @@ static int otp_write_prepare(struct mxc_otp_platform_data *otp_data)
        otp_wait_busy(0);
        return 0;
 }
-static int otp_write_post(void)
+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_ARCH_MX6) /* 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)
+{
+       struct clk *ocotp_clk;
+       unsigned long clk_rate = 0;
+       unsigned long strobe_read, relex, strobe_prog;
+       u32 timing = 0;
+
+       /* [1] get the clock. It needs the IPG clock,though doc writes IPG.*/
+       if (!otp_data->clock_name)
+               return -1;
+
+       ocotp_clk = clk_get(NULL, otp_data->clock_name);
+       if (IS_ERR(ocotp_clk)) {
+               log("we can not find the clock");
+               return -1;
+       }
+       clk_rate = clk_get_rate(ocotp_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;
 }
 
@@ -230,5 +364,6 @@ static void unmap_ocotp_addr(void)
        iounmap(otp_base);
        otp_base = NULL;
 }
-#endif /* CONFIG_ARCH_MX5 */
+#endif /* CONFIG_ARCH_MX6 */
+
 #endif