diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2012-07-02 11:00:56 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2012-07-02 11:01:46 +0200 |
commit | 1422cd1878f7c9854b9d5c5baa80eb67a0b31d47 (patch) | |
tree | 8a92c8229a1c4018ed16178c7f1ef2369268cc10 /arch/arm/mach-mxs | |
parent | c9fc3209ac6d5738fd0de419bfdcdc7240ab9eb0 (diff) | |
parent | 1d3d060ab79d69c4447692e600193e9a10d7ec57 (diff) | |
download | barebox-1422cd1878f7c9854b9d5c5baa80eb67a0b31d47.tar.gz barebox-1422cd1878f7c9854b9d5c5baa80eb67a0b31d47.tar.xz |
Merge branch 'for-next/mx28-ocotp'
Conflicts:
arch/arm/mach-mxs/ocotp.c
Diffstat (limited to 'arch/arm/mach-mxs')
-rw-r--r-- | arch/arm/mach-mxs/Kconfig | 12 | ||||
-rw-r--r-- | arch/arm/mach-mxs/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-mxs/include/mach/clock-imx23.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-mxs/include/mach/clock-imx28.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-mxs/include/mach/imx28-regs.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-mxs/include/mach/power.h | 8 | ||||
-rw-r--r-- | arch/arm/mach-mxs/ocotp.c | 150 | ||||
-rw-r--r-- | arch/arm/mach-mxs/power.c | 84 | ||||
-rw-r--r-- | arch/arm/mach-mxs/speed-imx23.c | 26 | ||||
-rw-r--r-- | arch/arm/mach-mxs/speed-imx28.c | 26 | ||||
-rw-r--r-- | arch/arm/mach-mxs/usb.c | 30 |
11 files changed, 291 insertions, 50 deletions
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig index 3348a3cef7..9ce7c7a1bd 100644 --- a/arch/arm/mach-mxs/Kconfig +++ b/arch/arm/mach-mxs/Kconfig @@ -61,6 +61,7 @@ config MACH_TX28 config MACH_MX28EVK bool "mx28-evk" + select MXS_OCOTP help Say Y here if you are using the Freescale i.MX28-EVK board @@ -80,6 +81,17 @@ config MXS_OCOTP internal view). Don't use register offsets here, the SET, CLR and TGL registers are not mapped! +config MXS_OCOTP_WRITABLE + bool "OCOTP write support" + depends on MXS_OCOTP + help + Enable this option to add writing to OCOTP. + Warning: blown bits can not be unblown. Use with care. + + Before being actually able to blow the bits, you need to explicitely + enable writing: + ocotp0.permanent_write_enable=1 + endmenu menu "Board specific settings " diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile index 172d928128..37b5b8a037 100644 --- a/arch/arm/mach-mxs/Makefile +++ b/arch/arm/mach-mxs/Makefile @@ -1,4 +1,4 @@ -obj-y += imx.o iomux-imx.o reset-imx.o +obj-y += imx.o iomux-imx.o reset-imx.o power.o obj-$(CONFIG_DRIVER_VIDEO_STM) += imx_lcd_clk.o obj-$(CONFIG_ARCH_IMX23) += speed-imx23.o clocksource-imx23.o usb.o obj-$(CONFIG_ARCH_IMX28) += speed-imx28.o clocksource-imx28.o diff --git a/arch/arm/mach-mxs/include/mach/clock-imx23.h b/arch/arm/mach-mxs/include/mach/clock-imx23.h index 723f343566..4f20e09d3d 100644 --- a/arch/arm/mach-mxs/include/mach/clock-imx23.h +++ b/arch/arm/mach-mxs/include/mach/clock-imx23.h @@ -18,6 +18,7 @@ unsigned imx_get_emiclk(void); unsigned imx_get_ioclk(void); unsigned imx_get_armclk(void); unsigned imx_get_hclk(void); +unsigned imx_set_hclk(unsigned); unsigned imx_get_xclk(void); unsigned imx_get_sspclk(unsigned); unsigned imx_set_sspclk(unsigned, unsigned, int); diff --git a/arch/arm/mach-mxs/include/mach/clock-imx28.h b/arch/arm/mach-mxs/include/mach/clock-imx28.h index 45fb043ac4..613c97b916 100644 --- a/arch/arm/mach-mxs/include/mach/clock-imx28.h +++ b/arch/arm/mach-mxs/include/mach/clock-imx28.h @@ -18,6 +18,7 @@ unsigned imx_get_emiclk(void); unsigned imx_get_ioclk(unsigned); unsigned imx_get_armclk(void); unsigned imx_get_hclk(void); +unsigned imx_set_hclk(unsigned); unsigned imx_get_xclk(void); unsigned imx_get_sspclk(unsigned); unsigned imx_set_sspclk(unsigned, unsigned, int); diff --git a/arch/arm/mach-mxs/include/mach/imx28-regs.h b/arch/arm/mach-mxs/include/mach/imx28-regs.h index 9a2052c159..23ac306c8c 100644 --- a/arch/arm/mach-mxs/include/mach/imx28-regs.h +++ b/arch/arm/mach-mxs/include/mach/imx28-regs.h @@ -32,6 +32,7 @@ #define IMX_OCOTP_BASE 0x8002c000 #define IMX_FB_BASE 0x80030000 #define IMX_CCM_BASE 0x80040000 +#define IMX_POWER_BASE 0x80044000 #define IMX_WDT_BASE 0x80056000 #define IMX_I2C0_BASE 0x80058000 #define IMX_I2C1_BASE 0x8005a000 diff --git a/arch/arm/mach-mxs/include/mach/power.h b/arch/arm/mach-mxs/include/mach/power.h new file mode 100644 index 0000000000..f429b3c31c --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/power.h @@ -0,0 +1,8 @@ +#ifndef __MACH_POWER_H +#define __MACH_POWER_H + +void imx_power_prepare_usbphy(void); +int imx_get_vddio(void); +int imx_set_vddio(int); + +#endif /* __MACH_POWER_H */ diff --git a/arch/arm/mach-mxs/ocotp.c b/arch/arm/mach-mxs/ocotp.c index 86e63dc7de..78244028b9 100644 --- a/arch/arm/mach-mxs/ocotp.c +++ b/arch/arm/mach-mxs/ocotp.c @@ -25,27 +25,45 @@ #include <mach/generic.h> #include <mach/ocotp.h> #include <mach/imx-regs.h> +#include <mach/clock-imx28.h> +#include <mach/power.h> #define DRIVERNAME "ocotp" -#define OCOTP_WORD_OFFSET 0x20 +#define OCOTP_CTRL 0x0 +# define OCOTP_CTRL_ADDR_MASK 0x3f +# define OCOTP_CTRL_BUSY (1 << 8) +# define OCOTP_CTRL_ERROR (1 << 9) +# define OCOTP_CTRL_RD_BANK_OPEN (1 << 12) +# define OCOTP_CTRL_WR_UNLOCK 0x3e770000 + +#define OCOTP_DATA 0x10 -#define BM_OCOTP_CTRL_BUSY (1 << 8) -#define BM_OCOTP_CTRL_ERROR (1 << 9) -#define BM_OCOTP_CTRL_RD_BANK_OPEN (1 << 12) +#define OCOTP_WORD_OFFSET 0x20 struct ocotp_priv { struct cdev cdev; void __iomem *base; }; +static int mxs_ocotp_wait_busy(struct ocotp_priv *priv) +{ + uint64_t start = get_time_ns(); + + /* check both BUSY and ERROR cleared */ + while (readl(priv->base + OCOTP_CTRL) & (OCOTP_CTRL_BUSY | OCOTP_CTRL_ERROR)) + if (is_timeout(start, MSECOND)) + return -ETIMEDOUT; + + return 0; +} + static ssize_t mxs_ocotp_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags) { struct ocotp_priv *priv = cdev->priv; void __iomem *base = priv->base; size_t size = min((loff_t)count, cdev->size - offset); - uint64_t start; int i; /* @@ -54,25 +72,20 @@ static ssize_t mxs_ocotp_cdev_read(struct cdev *cdev, void *buf, size_t count, */ /* try to clear ERROR bit */ - writel(BM_OCOTP_CTRL_ERROR, base + BIT_CLR); + writel(OCOTP_CTRL_ERROR, base + OCOTP_CTRL + BIT_CLR); - /* check both BUSY and ERROR cleared */ - start = get_time_ns(); - while (readl(base) & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)) - if (is_timeout(start, MSECOND)) - return -ETIMEDOUT; + if (mxs_ocotp_wait_busy(priv)) + return -ETIMEDOUT; /* open OCOTP banks for read */ - writel(BM_OCOTP_CTRL_RD_BANK_OPEN, base + BIT_SET); + writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + BIT_SET); /* approximately wait 32 hclk cycles */ udelay(1); /* poll BUSY bit becoming cleared */ - start = get_time_ns(); - while (readl(base) & BM_OCOTP_CTRL_BUSY) - if (is_timeout(start, MSECOND)) - return -ETIMEDOUT; + if (mxs_ocotp_wait_busy(priv)) + return -ETIMEDOUT; for (i = 0; i < size; i++) /* When reading bytewise, we need to hop over the SET/CLR/TGL regs */ @@ -80,16 +93,107 @@ static ssize_t mxs_ocotp_cdev_read(struct cdev *cdev, void *buf, size_t count, (((i + offset) & 0xfc) << 2) + ((i + offset) & 3)); /* close banks for power saving */ - writel(BM_OCOTP_CTRL_RD_BANK_OPEN, base + BIT_CLR); + writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + BIT_CLR); return size; } +static ssize_t mxs_ocotp_cdev_write(struct cdev *cdev, const void *buf, size_t count, + loff_t offset, ulong flags) +{ + struct ocotp_priv *priv = cdev->priv; + void __iomem *base = priv->base; + const char *write_param; + unsigned int write_enabled = 0; + unsigned long old_hclk, aligned_offset; + int old_vddio, num_words, num_bytes, i, ret = 0; + u8 *work_buf; + u32 reg; + + write_param = dev_get_param(cdev->dev, "permanent_write_enable"); + if (write_param) + write_enabled = simple_strtoul(write_param, NULL, 0); + + if (!write_param || !write_enabled) + return -EPERM; + + /* we can only work on u32, so calc some helpers */ + aligned_offset = offset & ~3UL; + num_words = DIV_ROUND_UP(offset - aligned_offset + count, 4); + num_bytes = num_words << 2; + + /* read in all words which will be modified */ + work_buf = xmalloc(num_bytes); + + i = mxs_ocotp_cdev_read(cdev, work_buf, num_bytes, aligned_offset, 0); + if (i != num_bytes) { + ret = -ENXIO; + goto free_mem; + } + + /* modify read words with to be written data */ + for (i = 0; i < count; i++) + work_buf[offset - aligned_offset + i] |= ((u8 *)buf)[i]; + + /* prepare system for OTP write */ + old_hclk = imx_get_hclk(); + old_vddio = imx_get_vddio(); + + imx_set_hclk(24000000); + imx_set_vddio(2800000); + + writel(OCOTP_CTRL_RD_BANK_OPEN, base + OCOTP_CTRL + BIT_CLR); + + if (mxs_ocotp_wait_busy(priv)) { + ret = -ETIMEDOUT; + goto restore_system; + } + + /* write word for word via data register */ + for (i = 0; i < num_words; i++) { + reg = readl(base + OCOTP_CTRL) & ~OCOTP_CTRL_ADDR_MASK; + reg |= OCOTP_CTRL_WR_UNLOCK | ((aligned_offset >> 2) + i); + writel(reg, base + OCOTP_CTRL); + + writel(((u32 *)work_buf)[i], base + OCOTP_DATA); + + if (mxs_ocotp_wait_busy(priv)) { + ret = -ETIMEDOUT; + goto restore_system; + } + + mdelay(2); + } + +restore_system: + imx_set_vddio(old_vddio); + imx_set_hclk(old_hclk); +free_mem: + free(work_buf); + + return ret; +} + static struct file_operations mxs_ocotp_ops = { .read = mxs_ocotp_cdev_read, .lseek = dev_lseek_default, }; +static int mxs_ocotp_write_enable_set(struct device_d *dev, struct param_d *param, + const char *val) +{ + unsigned long write_enable; + + if (!val) + return -EINVAL; + + write_enable = simple_strtoul(val, NULL, 0); + if (write_enable > 1) + return -EINVAL; + + return dev_param_set_generic(dev, param, write_enable ? "1" : "0"); +} + static int mxs_ocotp_probe(struct device_d *dev) { int err; @@ -106,6 +210,18 @@ static int mxs_ocotp_probe(struct device_d *dev) if (err < 0) return err; + if (IS_ENABLED(CONFIG_MXS_OCOTP_WRITABLE)) { + mxs_ocotp_ops.write = mxs_ocotp_cdev_write; + + err = dev_add_param(dev, "permanent_write_enable", + mxs_ocotp_write_enable_set, NULL, 0); + if (err < 0) + return err; + err = dev_set_param(dev, "permanent_write_enable", "0"); + if (err < 0) + return err; + } + return 0; } diff --git a/arch/arm/mach-mxs/power.c b/arch/arm/mach-mxs/power.c new file mode 100644 index 0000000000..f4d0b9e3e6 --- /dev/null +++ b/arch/arm/mach-mxs/power.c @@ -0,0 +1,84 @@ +/* + * i.MX28 power related functions + * + * Copyright 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + * Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de> + * + * 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. + */ +#include <common.h> +#include <io.h> +#include <errno.h> +#include <mach/imx-regs.h> + +#define POWER_CTRL (IMX_POWER_BASE + 0x0) +#define POWER_CTRL_CLKGATE 0x40000000 + +#define POWER_VDDIOCTRL (IMX_POWER_BASE + 0x60) + +#define POWER_STS (IMX_POWER_BASE + 0xc0) +#define POWER_STS_VBUSVALID 0x00000002 +#define POWER_STS_BVALID 0x00000004 +#define POWER_STS_AVALID 0x00000008 + +#define POWER_DEBUG (IMX_POWER_BASE + 0x110) +#define POWER_DEBUG_BVALIDPIOLOCK 0x00000002 +#define POWER_DEBUG_AVALIDPIOLOCK 0x00000004 +#define POWER_DEBUG_VBUSVALIDPIOLOCK 0x00000008 + +#define TRG_MASK 0x1f + +int imx_get_vddio(void) +{ + u32 val; + + val = readl(POWER_VDDIOCTRL) & TRG_MASK; + if (val > 0x10) + val = 0x10; + + return 2800000 + val * 50000; +} + +int imx_set_vddio(int new_voltage_uV) +{ + u32 reg, val; + + if (new_voltage_uV < 2800000 || new_voltage_uV > 3600000) + return -EINVAL; + + val = (new_voltage_uV - 2800000) / 50000; + reg = readl(POWER_VDDIOCTRL) & ~TRG_MASK; + writel(reg | val, POWER_VDDIOCTRL); + + /* + * Wait for power to become stable. We just wait, because DC_OK can + * only detect rising voltages for DCDC. For all other cases, bootlets + * also do simple waiting, although horribly nested. We just take the + * maximum value of all cases from the bootlets and then add some. + */ + mdelay(30); + + return 2800000 + val * 50000; +} + +void imx_power_prepare_usbphy(void) +{ + u32 reg; + + /* + * Set these bits so that we can force the OTG bits high + * so the ARC core operates properly + */ + writel(POWER_CTRL_CLKGATE, POWER_CTRL + BIT_CLR); + + writel(POWER_DEBUG_VBUSVALIDPIOLOCK | + POWER_DEBUG_AVALIDPIOLOCK | + POWER_DEBUG_BVALIDPIOLOCK, POWER_DEBUG + BIT_SET); + + reg = readl(POWER_STS); + reg |= POWER_STS_BVALID | POWER_STS_AVALID | POWER_STS_VBUSVALID; + writel(reg, POWER_STS); +} diff --git a/arch/arm/mach-mxs/speed-imx23.c b/arch/arm/mach-mxs/speed-imx23.c index b10c78643c..f41b9bc75a 100644 --- a/arch/arm/mach-mxs/speed-imx23.c +++ b/arch/arm/mach-mxs/speed-imx23.c @@ -184,12 +184,34 @@ unsigned imx_get_hclk(void) if (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x20) { rate *= readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f; - rate >>= 5U; /* / 32 */ + rate = DIV_ROUND_UP(rate, 32); } else - rate /= readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f; + rate = DIV_ROUND_UP(rate, + readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f); return rate * 1000; } +unsigned imx_set_hclk(unsigned nc) +{ + unsigned root_rate = imx_get_armclk(); + unsigned reg, div; + + div = DIV_ROUND_UP(root_rate, nc); + if ((div == 0) || (div >= 32)) + return 0; + + if ((root_rate < nc) && (root_rate == 64000000)) + div = 3; + + reg = readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & ~0x3f; + writel(reg | div, IMX_CCM_BASE + HW_CLKCTRL_HBUS); + + while (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & (1 << 31)) + ; + + return imx_get_hclk(); +} + /* * Source of UART, debug UART, audio, PWM, dri, timer, digctl */ diff --git a/arch/arm/mach-mxs/speed-imx28.c b/arch/arm/mach-mxs/speed-imx28.c index 67cdbdf81d..2641fb6fb2 100644 --- a/arch/arm/mach-mxs/speed-imx28.c +++ b/arch/arm/mach-mxs/speed-imx28.c @@ -251,12 +251,34 @@ unsigned imx_get_hclk(void) if (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x20) { rate *= readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f; - rate /= 32; + rate = DIV_ROUND_UP(rate, 32); } else - rate /= readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f; + rate = DIV_ROUND_UP(rate, + readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & 0x1f); return rate * 1000; } +unsigned imx_set_hclk(unsigned nc) +{ + unsigned root_rate = imx_get_armclk(); + unsigned reg, div; + + div = DIV_ROUND_UP(root_rate, nc); + if ((div == 0) || (div >= 32)) + return 0; + + if ((root_rate < nc) && (root_rate == 64000000)) + div = 3; + + reg = readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & ~0x3f; + writel(reg | div, IMX_CCM_BASE + HW_CLKCTRL_HBUS); + + while (readl(IMX_CCM_BASE + HW_CLKCTRL_HBUS) & (1 << 31)) + ; + + return imx_get_hclk(); +} + /* * Source of UART, debug UART, audio, PWM, dri, timer, digctl */ diff --git a/arch/arm/mach-mxs/usb.c b/arch/arm/mach-mxs/usb.c index b7a93769b7..aca0e7d654 100644 --- a/arch/arm/mach-mxs/usb.c +++ b/arch/arm/mach-mxs/usb.c @@ -20,19 +20,7 @@ #include <common.h> #include <io.h> #include <mach/imx-regs.h> - -#define POWER_CTRL (IMX_POWER_BASE + 0x0) -#define POWER_CTRL_CLKGATE 0x40000000 - -#define POWER_STS (IMX_POWER_BASE + 0xc0) -#define POWER_STS_VBUSVALID 0x00000002 -#define POWER_STS_BVALID 0x00000004 -#define POWER_STS_AVALID 0x00000008 - -#define POWER_DEBUG (IMX_POWER_BASE + 0x110) -#define POWER_DEBUG_BVALIDPIOLOCK 0x00000002 -#define POWER_DEBUG_AVALIDPIOLOCK 0x00000004 -#define POWER_DEBUG_VBUSVALIDPIOLOCK 0x00000008 +#include <mach/power.h> #define USBPHY_PWD (IMX_USBPHY_BASE + 0x0) @@ -51,21 +39,7 @@ int imx_usb_phy_enable(void) { - u32 reg; - - /* - * Set these bits so that we can force the OTG bits high - * so the ARC core operates properly - */ - writel(POWER_CTRL_CLKGATE, POWER_CTRL + CLR); - - writel(POWER_DEBUG_VBUSVALIDPIOLOCK | - POWER_DEBUG_AVALIDPIOLOCK | - POWER_DEBUG_BVALIDPIOLOCK, POWER_DEBUG + SET); - - reg = readl(POWER_STS); - reg |= POWER_STS_BVALID | POWER_STS_AVALID | POWER_STS_VBUSVALID; - writel(reg, POWER_STS); + imx_power_prepare_usbphy(); /* Reset USBPHY module */ writel(USBPHY_CTRL_SFTRST, USBPHY_CTRL + SET); |