summaryrefslogtreecommitdiff
path: root/drivers/char
diff options
context:
space:
mode:
authorTerry Lv <r65388@freescale.com>2011-06-21 14:29:52 +0800
committerJason Liu <r64343@freescale.com>2012-01-09 20:21:22 +0800
commitccc2c15ecd6e4825f54910906c99f8e700c3f26c (patch)
tree0324f711b56b170aa4aa36b8c4a4c29ca222f6c2 /drivers/char
parentb29e85b0f0cbcab4f069de64f68d23dad7aaadf2 (diff)
ENGR00139235-3: IIM(OCOPT): Enable IIM driver for iMX6Q
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>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig4
-rwxr-xr-xdrivers/char/fsl_otp.c6
-rwxr-xr-xdrivers/char/fsl_otp.h171
3 files changed, 158 insertions, 23 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index b9c85171b4ea..6165732eedc5 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -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.
diff --git a/drivers/char/fsl_otp.c b/drivers/char/fsl_otp.c
index d083036929f8..5517e5550128 100755
--- a/drivers/char/fsl_otp.c
+++ b/drivers/char/fsl_otp.c
@@ -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;
}
diff --git a/drivers/char/fsl_otp.h b/drivers/char/fsl_otp.h
index f1124892d465..fa2b21e12a88 100755
--- a/drivers/char/fsl_otp.h
+++ b/drivers/char/fsl_otp.h
@@ -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