#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 ========================= */
#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;
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,
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;
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);
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 */
#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;
}
/* 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;
}
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;
}
iounmap(otp_base);
otp_base = NULL;
}
-#endif /* CONFIG_ARCH_MX5 */
+#endif /* CONFIG_ARCH_MX6 */
+
#endif