From: Terry Lv Date: Tue, 21 Jun 2011 06:29:52 +0000 (+0800) Subject: ENGR00139235-3: IIM(OCOPT): Enable IIM driver for iMX6Q X-Git-Tag: v3.0.35-fsl_4.1.0~2423 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=57d0c89febf55cede39c2c3e4899e712ef943c23;p=karo-tx-linux.git 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 --- 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 +#include +#include +#include + +#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