summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2020-08-18 11:23:20 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2020-08-18 11:23:20 +0200
commit100b13c2a770b11e48e0b80bc1f24284ef723265 (patch)
treef54756f37af6166554a88359bddba1eacce7a984 /drivers
parent68632898d02ce72818ae90109b6feb466d1acced (diff)
parentf0b293332979618e1077b392bfcdac36d2c317e2 (diff)
downloadbarebox-100b13c2a770b11e48e0b80bc1f24284ef723265.tar.gz
barebox-100b13c2a770b11e48e0b80bc1f24284ef723265.tar.xz
Merge branch 'for-next/imx' into master
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/imx/clk-imx7.c2
-rw-r--r--drivers/mci/imx-esdhc-pbl.c37
-rw-r--r--drivers/nvmem/Kconfig2
-rw-r--r--drivers/nvmem/ocotp.c185
4 files changed, 218 insertions, 8 deletions
diff --git a/drivers/clk/imx/clk-imx7.c b/drivers/clk/imx/clk-imx7.c
index 1f15d7ef11..b6c7c2c3a8 100644
--- a/drivers/clk/imx/clk-imx7.c
+++ b/drivers/clk/imx/clk-imx7.c
@@ -722,10 +722,12 @@ static int imx7_ccm_probe(struct device_d *dev)
clks[IMX7D_OCRAM_S_CLK] = imx_clk_gate4("ocram_s_clk", "ahb_post_div", base + 0x4120, 0);
clks[IMX7D_NAND_USDHC_BUS_ROOT_CLK] = imx_clk_gate4("nand_usdhc_root_clk", "nand_usdhc_post_div", base + 0x4130, 0);
clks[IMX7D_AHB_CHANNEL_ROOT_CLK] = imx_clk_gate4("ahb_root_clk", "ahb_post_div", base + 0x4200, 0);
+ clks[IMX7D_IPG_ROOT_CLK] = imx_clk_divider_flags("ipg_root_clk", "ahb_root_clk", base + 0x9080, 0, 2, CLK_IS_CRITICAL | CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_PARENT);
clks[IMX7D_DRAM_ROOT_CLK] = imx_clk_gate4("dram_root_clk", "dram_post_div", base + 0x4130, 0);
clks[IMX7D_DRAM_PHYM_ROOT_CLK] = imx_clk_gate4("dram_phym_root_clk", "dram_phym_cg", base + 0x4130, 0);
clks[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_gate4("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0);
clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0);
+ clks[IMX7D_OCOTP_CLK] = imx_clk_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0);
clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4420, 0);
clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0);
clks[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0);
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
index 5c382bbcc2..10a0082803 100644
--- a/drivers/mci/imx-esdhc-pbl.c
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -21,6 +21,7 @@
#ifdef CONFIG_ARCH_IMX
#include <mach/atf.h>
#include <mach/imx6-regs.h>
+#include <mach/imx7-regs.h>
#include <mach/imx8mq-regs.h>
#include <mach/imx8mm-regs.h>
#include <mach/imx-header.h>
@@ -266,6 +267,42 @@ int imx6_esdhc_start_image(int instance)
}
/**
+ * imx7_esdhc_start_image - Load and start an image from USDHC controller
+ * @instance: The USDHC controller instance (0..2)
+ *
+ * This uses esdhc_start_image() to load an image from SD/MMC. It is
+ * assumed that the image is the currently running barebox image (This
+ * information is used to calculate the length of the image). The
+ * image is started afterwards.
+ *
+ * Return: If successful, this function does not return. A negative error
+ * code is returned when this function fails.
+ */
+int imx7_esdhc_start_image(int instance)
+{
+ struct esdhc_soc_data data;
+ struct fsl_esdhc_host host;
+
+ switch (instance) {
+ case 0:
+ host.regs = IOMEM(MX7_USDHC1_BASE_ADDR);
+ break;
+ case 1:
+ host.regs = IOMEM(MX7_USDHC2_BASE_ADDR);
+ break;
+ case 2:
+ host.regs = IOMEM(MX7_USDHC3_BASE_ADDR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ imx_esdhc_init(&host, &data);
+
+ return esdhc_load_image(&host, 0x80000000, 0x80000000, 0, SZ_1K, true);
+}
+
+/**
* imx8m_esdhc_load_image - Load and optionally start an image from USDHC controller
* @instance: The USDHC controller instance (0..2)
* @start: Whether to directly start the loaded image
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 968342b281..e4a72b1431 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -18,7 +18,7 @@ config NVMEM_SNVS_LPGPR
config IMX_OCOTP
tristate "i.MX6 On Chip OTP controller"
- depends on ARCH_IMX6 || ARCH_VF610 || ARCH_IMX8MQ
+ depends on ARCH_IMX6 || ARCH_VF610 || ARCH_IMX8M || ARCH_IMX7
depends on OFDEVICE
help
This adds support for the i.MX6 On-Chip OTP controller. Currently the
diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c
index 62f510785b..356e9bf113 100644
--- a/drivers/nvmem/ocotp.c
+++ b/drivers/nvmem/ocotp.c
@@ -48,6 +48,19 @@
#define OCOTP_READ_CTRL 0x30
#define OCOTP_READ_FUSE_DATA 0x40
+#define MX7_OCOTP_DATA0 0x20
+#define MX7_OCOTP_DATA1 0x30
+#define MX7_OCOTP_DATA2 0x40
+#define MX7_OCOTP_DATA3 0x50
+#define MX7_OCOTP_READ_CTRL 0x60
+#define MX7_OCOTP_READ_FUSE_DATA0 0x70
+#define MX7_OCOTP_READ_FUSE_DATA1 0x80
+#define MX7_OCOTP_READ_FUSE_DATA2 0x90
+#define MX7_OCOTP_READ_FUSE_DATA3 0xA0
+
+#define DEF_FSOURCE 1001 /* > 1000 ns */
+#define DEF_STROBE_PROG 10000 /* IPG clocks */
+
/* OCOTP Registers bits and masks */
#define OCOTP_CTRL_WR_UNLOCK 16
#define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77
@@ -86,11 +99,16 @@ enum imx_ocotp_format_mac_direction {
OCOTP_MAC_TO_HW,
};
+struct ocotp_priv;
+
struct imx_ocotp_data {
int num_regs;
u32 (*addr_to_offset)(u32 addr);
void (*format_mac)(u8 *dst, const u8 *src,
enum imx_ocotp_format_mac_direction dir);
+ int (*set_timing)(struct ocotp_priv *priv);
+ int (*fuse_read)(struct ocotp_priv *priv, u32 addr, u32 *pdata);
+ int (*fuse_blow)(struct ocotp_priv *priv, u32 addr, u32 value);
u8 mac_offsets[MAX_MAC_OFFSETS];
u8 mac_offsets_num;
};
@@ -168,6 +186,27 @@ static int imx6_ocotp_set_timing(struct ocotp_priv *priv)
return 0;
}
+static int imx7_ocotp_set_timing(struct ocotp_priv *priv)
+{
+ unsigned long clk_rate;
+ u64 fsource, strobe_prog;
+ u32 timing;
+
+ clk_rate = clk_get_rate(priv->clk);
+
+ fsource = DIV_ROUND_UP_ULL((u64)clk_rate * DEF_FSOURCE,
+ NSEC_PER_SEC) + 1;
+ strobe_prog = DIV_ROUND_CLOSEST_ULL((u64)clk_rate * DEF_STROBE_PROG,
+ NSEC_PER_SEC) + 1;
+
+ timing = strobe_prog & 0x00000FFF;
+ timing |= (fsource << 12) & 0x000FF000;
+
+ writel(timing, priv->base + OCOTP_TIMING);
+
+ return 0;
+}
+
static int imx6_ocotp_wait_busy(struct ocotp_priv *priv, u32 flags)
{
uint64_t start = get_time_ns();
@@ -183,7 +222,7 @@ static int imx6_ocotp_prepare(struct ocotp_priv *priv)
{
int ret;
- ret = imx6_ocotp_set_timing(priv);
+ ret = priv->data->set_timing(priv);
if (ret)
return ret;
@@ -194,7 +233,7 @@ static int imx6_ocotp_prepare(struct ocotp_priv *priv)
return 0;
}
-static int fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
+static int imx6_fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
{
u32 ctrl_reg;
int ret;
@@ -220,6 +259,50 @@ static int fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata)
return 0;
}
+static int imx7_fuse_read_addr(struct ocotp_priv *priv, u32 index, u32 *pdata)
+{
+ u32 ctrl_reg;
+ u32 bank_addr;
+ u16 word;
+ int ret;
+
+ word = index & 0x3;
+ bank_addr = index >> 2;
+
+ writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR);
+
+ ctrl_reg = readl(priv->base + OCOTP_CTRL);
+ ctrl_reg &= ~OCOTP_CTRL_ADDR_MASK;
+ ctrl_reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK;
+ ctrl_reg |= BF(bank_addr, OCOTP_CTRL_ADDR);
+ writel(ctrl_reg, priv->base + OCOTP_CTRL);
+
+ writel(OCOTP_READ_CTRL_READ_FUSE, priv->base + MX7_OCOTP_READ_CTRL);
+ ret = imx6_ocotp_wait_busy(priv, 0);
+ if (ret)
+ return ret;
+
+ if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR)
+ *pdata = 0xbadabada;
+ else
+ switch (word) {
+ case 0:
+ *pdata = readl(priv->base + MX7_OCOTP_READ_FUSE_DATA0);
+ break;
+ case 1:
+ *pdata = readl(priv->base + MX7_OCOTP_READ_FUSE_DATA1);
+ break;
+ case 2:
+ *pdata = readl(priv->base + MX7_OCOTP_READ_FUSE_DATA2);
+ break;
+ case 3:
+ *pdata = readl(priv->base + MX7_OCOTP_READ_FUSE_DATA2);
+ break;
+ }
+
+ return 0;
+}
+
static int imx6_ocotp_read_one_u32(struct ocotp_priv *priv, u32 index, u32 *pdata)
{
int ret;
@@ -231,7 +314,7 @@ static int imx6_ocotp_read_one_u32(struct ocotp_priv *priv, u32 index, u32 *pdat
return ret;
}
- ret = fuse_read_addr(priv, index, pdata);
+ ret = priv->data->fuse_read(priv, index, pdata);
if (ret) {
dev_err(&priv->dev, "failed to read fuse 0x%08x\n", index);
return ret;
@@ -260,19 +343,27 @@ static int imx_ocotp_reg_read(void *ctx, unsigned int reg, unsigned int *val)
return 0;
}
-static int fuse_blow_addr(struct ocotp_priv *priv, u32 addr, u32 value)
+static void imx_ocotp_clear_unlock(struct ocotp_priv *priv, u32 index)
{
u32 ctrl_reg;
- int ret;
writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR);
/* Control register */
ctrl_reg = readl(priv->base + OCOTP_CTRL);
ctrl_reg &= ~OCOTP_CTRL_ADDR_MASK;
- ctrl_reg |= BF(addr, OCOTP_CTRL_ADDR);
+ ctrl_reg |= BF(index, OCOTP_CTRL_ADDR);
ctrl_reg |= BF(OCOTP_CTRL_WR_UNLOCK_KEY, OCOTP_CTRL_WR_UNLOCK);
writel(ctrl_reg, priv->base + OCOTP_CTRL);
+}
+
+static int imx6_fuse_blow_addr(struct ocotp_priv *priv, u32 index, u32 value)
+{
+ int ret;
+
+ imx_ocotp_clear_unlock(priv, index);
+
+ writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR);
writel(value, priv->base + OCOTP_DATA);
ret = imx6_ocotp_wait_busy(priv, 0);
@@ -284,6 +375,53 @@ static int fuse_blow_addr(struct ocotp_priv *priv, u32 addr, u32 value)
return 0;
}
+static int imx7_fuse_blow_addr(struct ocotp_priv *priv, u32 index, u32 value)
+{
+ int ret;
+ int word;
+ int bank_addr;
+
+ bank_addr = index >> 2;
+ word = index & 0x3;
+
+ imx_ocotp_clear_unlock(priv, bank_addr);
+
+ switch(word) {
+ case 0:
+ writel(0, priv->base + MX7_OCOTP_DATA1);
+ writel(0, priv->base + MX7_OCOTP_DATA2);
+ writel(0, priv->base + MX7_OCOTP_DATA3);
+ writel(value, priv->base + MX7_OCOTP_DATA0);
+ break;
+ case 1:
+ writel(value, priv->base + MX7_OCOTP_DATA1);
+ writel(0, priv->base + MX7_OCOTP_DATA2);
+ writel(0, priv->base + MX7_OCOTP_DATA3);
+ writel(0, priv->base + MX7_OCOTP_DATA0);
+ break;
+ case 2:
+ writel(value, priv->base + MX7_OCOTP_DATA2);
+ writel(0, priv->base + MX7_OCOTP_DATA3);
+ writel(0, priv->base + MX7_OCOTP_DATA1);
+ writel(0, priv->base + MX7_OCOTP_DATA0);
+ break;
+ case 3:
+ writel(value, priv->base + MX7_OCOTP_DATA3);
+ writel(0, priv->base + MX7_OCOTP_DATA1);
+ writel(0, priv->base + MX7_OCOTP_DATA2);
+ writel(0, priv->base + MX7_OCOTP_DATA0);
+ break;
+ }
+
+ ret = imx6_ocotp_wait_busy(priv, 0);
+ if (ret)
+ return ret;
+
+ /* Write postamble */
+ udelay(2000);
+ return 0;
+}
+
static int imx6_ocotp_reload_shadow(struct ocotp_priv *priv)
{
dev_info(&priv->dev, "reloading shadow registers...\n");
@@ -304,7 +442,7 @@ static int imx6_ocotp_blow_one_u32(struct ocotp_priv *priv, u32 index, u32 data,
return ret;
}
- ret = fuse_blow_addr(priv, index, data);
+ ret = priv->data->fuse_blow(priv, index, data);
if (ret) {
dev_err(&priv->dev, "fuse blow failed\n");
return ret;
@@ -694,6 +832,7 @@ static struct imx_ocotp_data imx6q_ocotp_data = {
.mac_offsets_num = 1,
.mac_offsets = { MAC_OFFSET_0 },
.format_mac = imx_ocotp_format_mac,
+ .set_timing = imx6_ocotp_set_timing,
};
static struct imx_ocotp_data imx6sl_ocotp_data = {
@@ -702,6 +841,9 @@ static struct imx_ocotp_data imx6sl_ocotp_data = {
.mac_offsets_num = 1,
.mac_offsets = { MAC_OFFSET_0 },
.format_mac = imx_ocotp_format_mac,
+ .set_timing = imx6_ocotp_set_timing,
+ .fuse_blow = imx6_fuse_blow_addr,
+ .fuse_read = imx6_fuse_read_addr,
};
static struct imx_ocotp_data imx6ul_ocotp_data = {
@@ -710,6 +852,9 @@ static struct imx_ocotp_data imx6ul_ocotp_data = {
.mac_offsets_num = 2,
.mac_offsets = { MAC_OFFSET_0, IMX6UL_MAC_OFFSET_1 },
.format_mac = imx_ocotp_format_mac,
+ .set_timing = imx6_ocotp_set_timing,
+ .fuse_blow = imx6_fuse_blow_addr,
+ .fuse_read = imx6_fuse_read_addr,
};
static struct imx_ocotp_data imx6ull_ocotp_data = {
@@ -718,6 +863,9 @@ static struct imx_ocotp_data imx6ull_ocotp_data = {
.mac_offsets_num = 2,
.mac_offsets = { MAC_OFFSET_0, IMX6UL_MAC_OFFSET_1 },
.format_mac = imx_ocotp_format_mac,
+ .set_timing = imx6_ocotp_set_timing,
+ .fuse_blow = imx6_fuse_blow_addr,
+ .fuse_read = imx6_fuse_read_addr,
};
static struct imx_ocotp_data vf610_ocotp_data = {
@@ -726,6 +874,9 @@ static struct imx_ocotp_data vf610_ocotp_data = {
.mac_offsets_num = 2,
.mac_offsets = { MAC_OFFSET_0, MAC_OFFSET_1 },
.format_mac = vf610_ocotp_format_mac,
+ .set_timing = imx6_ocotp_set_timing,
+ .fuse_blow = imx6_fuse_blow_addr,
+ .fuse_read = imx6_fuse_read_addr,
};
static struct imx_ocotp_data imx8mp_ocotp_data = {
@@ -742,6 +893,20 @@ static struct imx_ocotp_data imx8mq_ocotp_data = {
.mac_offsets_num = 1,
.mac_offsets = { 0x90 },
.format_mac = imx_ocotp_format_mac,
+ .set_timing = imx6_ocotp_set_timing,
+ .fuse_blow = imx6_fuse_blow_addr,
+ .fuse_read = imx6_fuse_read_addr,
+};
+
+static struct imx_ocotp_data imx7d_ocotp_data = {
+ .num_regs = 2048,
+ .addr_to_offset = imx6sl_addr_to_offset,
+ .mac_offsets_num = 1,
+ .mac_offsets = { MAC_OFFSET_0, IMX6UL_MAC_OFFSET_1 },
+ .format_mac = imx_ocotp_format_mac,
+ .set_timing = imx7_ocotp_set_timing,
+ .fuse_blow = imx7_fuse_blow_addr,
+ .fuse_read = imx7_fuse_read_addr,
};
static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = {
@@ -761,12 +926,18 @@ static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = {
.compatible = "fsl,imx6ull-ocotp",
.data = &imx6ull_ocotp_data,
}, {
+ .compatible = "fsl,imx7d-ocotp",
+ .data = &imx7d_ocotp_data,
+ }, {
.compatible = "fsl,imx8mp-ocotp",
.data = &imx8mp_ocotp_data,
}, {
.compatible = "fsl,imx8mq-ocotp",
.data = &imx8mq_ocotp_data,
}, {
+ .compatible = "fsl,imx8mm-ocotp",
+ .data = &imx8mq_ocotp_data,
+ }, {
.compatible = "fsl,vf610-ocotp",
.data = &vf610_ocotp_data,
}, {