diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2020-08-18 11:23:20 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2020-08-18 11:23:20 +0200 |
commit | 100b13c2a770b11e48e0b80bc1f24284ef723265 (patch) | |
tree | f54756f37af6166554a88359bddba1eacce7a984 /drivers | |
parent | 68632898d02ce72818ae90109b6feb466d1acced (diff) | |
parent | f0b293332979618e1077b392bfcdac36d2c317e2 (diff) | |
download | barebox-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.c | 2 | ||||
-rw-r--r-- | drivers/mci/imx-esdhc-pbl.c | 37 | ||||
-rw-r--r-- | drivers/nvmem/Kconfig | 2 | ||||
-rw-r--r-- | drivers/nvmem/ocotp.c | 185 |
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, }, { |