diff options
Diffstat (limited to 'drivers/nvmem/ocotp.c')
-rw-r--r-- | drivers/nvmem/ocotp.c | 532 |
1 files changed, 435 insertions, 97 deletions
diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c index 34e33dee82..c282efefa8 100644 --- a/drivers/nvmem/ocotp.c +++ b/drivers/nvmem/ocotp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ocotp.c - i.MX6 ocotp fusebox driver * @@ -8,15 +9,12 @@ * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>, * Orex Computed Radiography * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include <common.h> +#include <deep-probe.h> #include <driver.h> #include <malloc.h> #include <xfuncs.h> @@ -26,11 +24,18 @@ #include <io.h> #include <of.h> #include <clock.h> -#include <regmap.h> +#include <linux/regmap.h> +#include <linux/bits.h> #include <linux/clk.h> -#include <mach/ocotp.h> #include <machine_id.h> -#include <mach/ocotp-fusemap.h> +#ifdef CONFIG_ARCH_IMX +#include <mach/imx/ocotp.h> +#include <mach/imx/ocotp-fusemap.h> +#else +#include <mach/mxs/ocotp.h> +#include <mach/mxs/ocotp-fusemap.h> +#endif +#include <soc/imx8m/featctrl.h> #include <linux/nvmem-provider.h> /* @@ -48,26 +53,45 @@ #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_ADDR GENMASK(7, 0) +#define OCOTP_CTRL_BUSY BIT(8) +#define OCOTP_CTRL_ERROR BIT(9) +#define OCOTP_CTRL_RELOAD_SHADOWS BIT(10) +#define OCOTP_CTRL_WR_UNLOCK GENMASK(31, 16) #define OCOTP_CTRL_WR_UNLOCK_KEY 0x3E77 -#define OCOTP_CTRL_WR_UNLOCK_MASK 0xFFFF0000 -#define OCOTP_CTRL_ADDR 0 -#define OCOTP_CTRL_ADDR_MASK 0x000000FF -#define OCOTP_CTRL_BUSY (1 << 8) -#define OCOTP_CTRL_ERROR (1 << 9) -#define OCOTP_CTRL_RELOAD_SHADOWS (1 << 10) -#define OCOTP_TIMING_STROBE_READ_MASK 0x003F0000 -#define OCOTP_TIMING_RELAX_MASK 0x0000F000 -#define OCOTP_TIMING_STROBE_PROG_MASK 0x00000FFF -#define OCOTP_TIMING_WAIT_MASK 0x0FC00000 +/* + * i.MX8MP OCOTP CTRL has a different layout. See RM Rev.1 06/2021 + * Section 6.3.5.1.2.4 + */ +#define OCOTP_CTRL_ADDR_8MP GENMASK(8, 0) +#define OCOTP_CTRL_BUSY_8MP BIT(9) +#define OCOTP_CTRL_ERROR_8MP BIT(10) +#define OCOTP_CTRL_RELOAD_SHADOWS_8MP BIT(11) +#define OCOTP_CTRL_WR_UNLOCK_8MP GENMASK(31, 16) -#define OCOTP_READ_CTRL_READ_FUSE 0x00000001 +#define OCOTP_TIMING_STROBE_READ GENMASK(21, 16) +#define OCOTP_TIMING_RELAX GENMASK(15, 12) +#define OCOTP_TIMING_STROBE_PROG GENMASK(11, 0) +#define OCOTP_TIMING_WAIT GENMASK(27, 22) -#define BF(value, field) FIELD_PREP(field##_MASK, value) +#define OCOTP_READ_CTRL_READ_FUSE BIT(1) -#define OCOTP_OFFSET_TO_ADDR(o) (OCOTP_OFFSET_TO_INDEX(o) * 4) +#define OCOTP_OFFSET_TO_ADDR(o) (OCOTP_OFFSET_TO_INDEX(o) * 4) /* Other definitions */ #define IMX6_OTP_DATA_ERROR_VAL 0xBADABADA @@ -77,22 +101,56 @@ #define MAC_OFFSET_0 (0x22 * 4) #define IMX6UL_MAC_OFFSET_1 (0x23 * 4) #define MAC_OFFSET_1 (0x24 * 4) +#define IMX8MP_MAC_OFFSET_1 (0x25 * 4) #define MAX_MAC_OFFSETS 2 #define MAC_BYTES 8 #define UNIQUE_ID_NUM 2 +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + enum imx_ocotp_format_mac_direction { OCOTP_HW_TO_MAC, OCOTP_MAC_TO_HW, }; +struct ocotp_ctrl_reg { + u32 bm_addr; + u32 bm_busy; + u32 bm_error; + u32 bm_reload_shadows; + u32 bm_wr_unlock; +}; + +const struct ocotp_ctrl_reg ocotp_ctrl_reg_default = { + .bm_addr = OCOTP_CTRL_ADDR, + .bm_busy = OCOTP_CTRL_BUSY, + .bm_error = OCOTP_CTRL_ERROR, + .bm_reload_shadows = OCOTP_CTRL_RELOAD_SHADOWS, + .bm_wr_unlock = OCOTP_CTRL_WR_UNLOCK, +}; + +const struct ocotp_ctrl_reg ocotp_ctrl_reg_8mp = { + .bm_addr = OCOTP_CTRL_ADDR_8MP, + .bm_busy = OCOTP_CTRL_BUSY_8MP, + .bm_error = OCOTP_CTRL_ERROR_8MP, + .bm_reload_shadows = OCOTP_CTRL_RELOAD_SHADOWS_8MP, + .bm_wr_unlock = OCOTP_CTRL_WR_UNLOCK_8MP, +}; + +struct ocotp_priv; + struct imx_ocotp_data { - int num_regs; + int nregs; 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; + struct imx8m_featctrl_data *feat; + const struct ocotp_ctrl_reg *ctrl; }; struct ocotp_priv_ethaddr { @@ -106,14 +164,13 @@ struct ocotp_priv { struct regmap *map; void __iomem *base; struct clk *clk; - struct device_d dev; + struct device dev; int permanent_write_enable; int sense_enable; struct ocotp_priv_ethaddr ethaddr[MAX_MAC_OFFSETS]; struct regmap_config map_config; const struct imx_ocotp_data *data; int mac_offset_idx; - struct nvmem_config config; }; static struct ocotp_priv *imx_ocotp; @@ -158,10 +215,31 @@ static int imx6_ocotp_set_timing(struct ocotp_priv *priv) 1000000); strobe_prog += 2 * (relax + 1) - 1; - timing = readl(priv->base + OCOTP_TIMING) & OCOTP_TIMING_WAIT_MASK; - timing |= BF(relax, OCOTP_TIMING_RELAX); - timing |= BF(strobe_read, OCOTP_TIMING_STROBE_READ); - timing |= BF(strobe_prog, OCOTP_TIMING_STROBE_PROG); + timing = readl(priv->base + OCOTP_TIMING) & OCOTP_TIMING_WAIT; + timing |= FIELD_PREP(OCOTP_TIMING_RELAX, relax); + timing |= FIELD_PREP(OCOTP_TIMING_STROBE_READ, strobe_read); + timing |= FIELD_PREP(OCOTP_TIMING_STROBE_PROG, strobe_prog); + + writel(timing, priv->base + OCOTP_TIMING); + + 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); @@ -171,8 +249,9 @@ static int imx6_ocotp_set_timing(struct ocotp_priv *priv) static int imx6_ocotp_wait_busy(struct ocotp_priv *priv, u32 flags) { uint64_t start = get_time_ns(); + u32 bm_ctrl_busy = priv->data->ctrl->bm_busy; - while (readl(priv->base + OCOTP_CTRL) & (OCOTP_CTRL_BUSY | flags)) + while (readl(priv->base + OCOTP_CTRL) & (bm_ctrl_busy | flags)) if (is_timeout(start, MSECOND)) return -ETIMEDOUT; @@ -183,7 +262,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,17 +273,20 @@ 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) { + const u32 bm_ctrl_error = priv->data->ctrl->bm_error; + const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr; + const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock; u32 ctrl_reg; int ret; - writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR); + writel(bm_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(addr, OCOTP_CTRL_ADDR); + ctrl_reg &= ~bm_ctrl_addr; + ctrl_reg &= ~bm_ctrl_wr_unlock; + ctrl_reg |= field_prep(bm_ctrl_addr, addr); writel(ctrl_reg, priv->base + OCOTP_CTRL); writel(OCOTP_READ_CTRL_READ_FUSE, priv->base + OCOTP_READ_CTRL); @@ -212,7 +294,7 @@ static int fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata) if (ret) return ret; - if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR) + if (readl(priv->base + OCOTP_CTRL) & bm_ctrl_error) *pdata = 0xbadabada; else *pdata = readl(priv->base + OCOTP_READ_FUSE_DATA); @@ -220,6 +302,53 @@ 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) +{ + const u32 bm_ctrl_error = priv->data->ctrl->bm_error; + const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr; + const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock; + u32 ctrl_reg; + u32 bank_addr; + u16 word; + int ret; + + word = index & 0x3; + bank_addr = index >> 2; + + writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR); + + ctrl_reg = readl(priv->base + OCOTP_CTRL); + ctrl_reg &= ~bm_ctrl_addr; + ctrl_reg &= ~bm_ctrl_wr_unlock; + ctrl_reg |= field_prep(bm_ctrl_addr, bank_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) & bm_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 +360,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 +389,31 @@ 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) { + const u32 bm_ctrl_error = priv->data->ctrl->bm_error; + const u32 bm_ctrl_addr = priv->data->ctrl->bm_addr; + const u32 bm_ctrl_wr_unlock = priv->data->ctrl->bm_wr_unlock; u32 ctrl_reg; - int ret; - writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR); + writel(bm_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(OCOTP_CTRL_WR_UNLOCK_KEY, OCOTP_CTRL_WR_UNLOCK); + ctrl_reg &= ~bm_ctrl_addr; + ctrl_reg |= field_prep(bm_ctrl_addr, index); + ctrl_reg |= field_prep(bm_ctrl_wr_unlock, OCOTP_CTRL_WR_UNLOCK_KEY); writel(ctrl_reg, priv->base + OCOTP_CTRL); +} + +static int imx6_fuse_blow_addr(struct ocotp_priv *priv, u32 index, u32 value) +{ + const u32 bm_ctrl_error = priv->data->ctrl->bm_error; + int ret; + + imx_ocotp_clear_unlock(priv, index); + + writel(bm_ctrl_error, priv->base + OCOTP_CTRL_CLR); writel(value, priv->base + OCOTP_DATA); ret = imx6_ocotp_wait_busy(priv, 0); @@ -284,18 +425,68 @@ 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) { + const u32 bm_ctrl_reload_shadows = priv->data->ctrl->bm_reload_shadows; + dev_info(&priv->dev, "reloading shadow registers...\n"); - writel(OCOTP_CTRL_RELOAD_SHADOWS, priv->base + OCOTP_CTRL_SET); + writel(bm_ctrl_reload_shadows, priv->base + OCOTP_CTRL_SET); udelay(1); - return imx6_ocotp_wait_busy(priv, OCOTP_CTRL_RELOAD_SHADOWS); + return imx6_ocotp_wait_busy(priv, bm_ctrl_reload_shadows); } static int imx6_ocotp_blow_one_u32(struct ocotp_priv *priv, u32 index, u32 data, u32 *pfused_value) { + const u32 bm_ctrl_error = priv->data->ctrl->bm_error; int ret; ret = imx6_ocotp_prepare(priv); @@ -304,13 +495,13 @@ 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; } - if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR) { + if (readl(priv->base + OCOTP_CTRL) & bm_ctrl_error) { dev_err(&priv->dev, "bad write status\n"); return -EFAULT; } @@ -355,11 +546,17 @@ static void imx_ocotp_field_decode(uint32_t field, unsigned *word, *mask = GENMASK(width, 0); } +static int imx_ocotp_ensure_probed(void); + int imx_ocotp_read_field(uint32_t field, unsigned *value) { unsigned word, bit, mask, val; int ret; + ret = imx_ocotp_ensure_probed(); + if (ret) + return ret; + imx_ocotp_field_decode(field, &word, &bit, &mask); ret = imx_ocotp_reg_read(imx_ocotp, word, &val); @@ -382,6 +579,10 @@ int imx_ocotp_write_field(uint32_t field, unsigned value) unsigned word, bit, mask; int ret; + ret = imx_ocotp_ensure_probed(); + if (ret) + return ret; + imx_ocotp_field_decode(field, &word, &bit, &mask); value &= mask; @@ -399,14 +600,27 @@ int imx_ocotp_write_field(uint32_t field, unsigned value) int imx_ocotp_permanent_write(int enable) { + int ret; + + ret = imx_ocotp_ensure_probed(); + if (ret) + return ret; + imx_ocotp->permanent_write_enable = enable; return 0; } -bool imx_ocotp_sense_enable(bool enable) +int imx_ocotp_sense_enable(bool enable) { - const bool old_value = imx_ocotp->sense_enable; + bool old_value; + int ret; + + ret = imx_ocotp_ensure_probed(); + if (ret) + return ret; + + old_value = imx_ocotp->sense_enable; imx_ocotp->sense_enable = enable; return old_value; } @@ -456,12 +670,12 @@ static int imx_ocotp_read_mac(const struct imx_ocotp_data *data, u8 buf[MAC_BYTES]; int ret; - ret = regmap_bulk_read(map, offset, buf, MAC_BYTES); + ret = regmap_bulk_read(map, offset, buf, MAC_BYTES / 4); if (ret < 0) return ret; - if (offset != IMX6UL_MAC_OFFSET_1) + if (offset != IMX6UL_MAC_OFFSET_1 && offset != IMX8MP_MAC_OFFSET_1) data->format_mac(mac, buf, OCOTP_HW_TO_MAC); else data->format_mac(mac, buf + 2, OCOTP_HW_TO_MAC); @@ -483,11 +697,12 @@ static int imx_ocotp_set_mac(struct param_d *param, void *priv) struct ocotp_priv_ethaddr *ethaddr = priv; int ret; - ret = regmap_bulk_read(ethaddr->map, ethaddr->offset, buf, MAC_BYTES); + ret = regmap_bulk_read(ethaddr->map, ethaddr->offset, buf, MAC_BYTES / 4); if (ret < 0) return ret; - if (ethaddr->offset != IMX6UL_MAC_OFFSET_1) + if (ethaddr->offset != IMX6UL_MAC_OFFSET_1 && + ethaddr->offset != IMX8MP_MAC_OFFSET_1) ethaddr->data->format_mac(buf, ethaddr->value, OCOTP_MAC_TO_HW); else @@ -495,7 +710,7 @@ static int imx_ocotp_set_mac(struct param_d *param, void *priv) OCOTP_MAC_TO_HW); return regmap_bulk_write(ethaddr->map, ethaddr->offset, - buf, MAC_BYTES); + buf, MAC_BYTES / 4); } static struct regmap_bus imx_ocotp_regmap_bus = { @@ -503,19 +718,33 @@ static struct regmap_bus imx_ocotp_regmap_bus = { .reg_read = imx_ocotp_reg_read, }; -static void imx_ocotp_init_dt(struct ocotp_priv *priv) +static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset, + void *data, size_t bytes) +{ + /* Deal with some post processing of nvmem cell data */ + if (id && !strcmp(id, "mac-address")) { + u8 *buf = data; + int i; + + for (i = 0; i < bytes/2; i++) + swap(buf[i], buf[bytes - i - 1]); + } + + return 0; +} + +static int imx_ocotp_init_dt(struct ocotp_priv *priv) { char mac[MAC_BYTES]; const __be32 *prop; - struct device_node *node = priv->dev.parent->device_node; - int len; + struct device_node *node = priv->dev.parent->of_node; + u32 tester3, tester4; + int ret, len = 0; if (!node) - return; + return 0; prop = of_get_property(node, "barebox,provide-mac-address", &len); - if (!prop) - return; for (; len >= MAC_ADDRESS_PROPLEN; len -= MAC_ADDRESS_PROPLEN) { struct device_node *rnode; @@ -533,22 +762,19 @@ static void imx_ocotp_init_dt(struct ocotp_priv *priv) of_eth_register_ethaddr(rnode, mac); } -} -static int imx_ocotp_write(struct device_d *dev, const int offset, - const void *val, int bytes) -{ - struct ocotp_priv *priv = dev->parent->priv; + if (!of_property_read_bool(node, "barebox,feature-controller")) + return 0; - return regmap_bulk_write(priv->map, offset, val, bytes); -} + ret = regmap_read(priv->map, OCOTP_OFFSET_TO_ADDR(0x440), &tester3); + if (ret != 0) + return ret; -static int imx_ocotp_read(struct device_d *dev, const int offset, void *val, - int bytes) -{ - struct ocotp_priv *priv = dev->parent->priv; + ret = regmap_read(priv->map, OCOTP_OFFSET_TO_ADDR(0x450), &tester4); + if (ret != 0) + return ret; - return regmap_bulk_read(priv->map, offset, val, bytes); + return imx8m_feat_ctrl_init(priv->dev.parent, tester3, tester4, priv->data->feat); } static void imx_ocotp_set_unique_machine_id(void) @@ -564,12 +790,7 @@ static void imx_ocotp_set_unique_machine_id(void) machine_id_set_hashable(unique_id_parts, sizeof(unique_id_parts)); } -static const struct nvmem_bus imx_ocotp_nvmem_bus = { - .write = imx_ocotp_write, - .read = imx_ocotp_read, -}; - -static int imx_ocotp_probe(struct device_d *dev) +static int imx_ocotp_probe(struct device *dev) { struct resource *iores; struct ocotp_priv *priv; @@ -600,21 +821,14 @@ static int imx_ocotp_probe(struct device_d *dev) priv->map_config.reg_bits = 32; priv->map_config.val_bits = 32; priv->map_config.reg_stride = 4; - priv->map_config.max_register = data->num_regs - 1; + priv->map_config.max_register = priv->map_config.reg_stride * (data->nregs - 1); priv->map = regmap_init(dev, &imx_ocotp_regmap_bus, priv, &priv->map_config); if (IS_ERR(priv->map)) return PTR_ERR(priv->map); - priv->config.name = "imx-ocotp"; - priv->config.dev = dev; - priv->config.stride = 4; - priv->config.word_size = 4; - priv->config.size = data->num_regs; - priv->config.bus = &imx_ocotp_nvmem_bus; - dev->priv = priv; - - nvmem = nvmem_register(&priv->config); + nvmem = nvmem_regmap_register_with_pp(priv->map, "imx-ocotp", + imx_ocotp_cell_pp); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); @@ -652,10 +866,12 @@ static int imx_ocotp_probe(struct device_d *dev) if (IS_ENABLED(CONFIG_MACHINE_ID)) imx_ocotp_set_unique_machine_id(); - imx_ocotp_init_dt(priv); + ret = imx_ocotp_init_dt(priv); + if (ret) + dev_warn(dev, "feature controller registration failed: %pe\n", + ERR_PTR(ret)); dev_add_param_bool(&(priv->dev), "sense_enable", NULL, NULL, &priv->sense_enable, priv); - return 0; } @@ -689,51 +905,147 @@ static u32 vf610_addr_to_offset(u32 addr) } static struct imx_ocotp_data imx6q_ocotp_data = { - .num_regs = 512, + .nregs = 128, .addr_to_offset = imx6q_addr_to_offset, .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, + .ctrl = &ocotp_ctrl_reg_default, }; static struct imx_ocotp_data imx6sl_ocotp_data = { - .num_regs = 256, + .nregs = 64, .addr_to_offset = imx6sl_addr_to_offset, .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, + .ctrl = &ocotp_ctrl_reg_default, }; static struct imx_ocotp_data imx6ul_ocotp_data = { - .num_regs = 512, + .nregs = 144, .addr_to_offset = imx6q_addr_to_offset, .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, + .ctrl = &ocotp_ctrl_reg_default, }; static struct imx_ocotp_data imx6ull_ocotp_data = { - .num_regs = 256, + .nregs = 80, .addr_to_offset = imx6q_addr_to_offset, .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, + .ctrl = &ocotp_ctrl_reg_default, }; static struct imx_ocotp_data vf610_ocotp_data = { - .num_regs = 512, + .nregs = 128, .addr_to_offset = vf610_addr_to_offset, .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, + .ctrl = &ocotp_ctrl_reg_default, +}; + +static struct imx8m_featctrl_data imx8mp_featctrl_data = { + .tester3.cpu_bitmask = 0xc0000, + .tester3.vpu_bitmask = 0x43000000, + .tester4.npu_bitmask = 0x8, + .tester4.gpu_bitmask = 0xc0, + .tester4.mipi_dsi_bitmask = 0x60000, + .tester4.lvds_bitmask = 0x180000, + .tester4.isp_bitmask = 0x3, + .tester4.dsp_bitmask = 0x10, +}; + +static struct imx_ocotp_data imx8mp_ocotp_data = { + .nregs = 384, + .addr_to_offset = imx6sl_addr_to_offset, + .mac_offsets_num = 2, + .mac_offsets = { 0x90, 0x94 }, + .format_mac = imx_ocotp_format_mac, + .feat = &imx8mp_featctrl_data, + .set_timing = imx6_ocotp_set_timing, + .fuse_blow = imx6_fuse_blow_addr, + .fuse_read = imx6_fuse_read_addr, + .ctrl = &ocotp_ctrl_reg_8mp, }; static struct imx_ocotp_data imx8mq_ocotp_data = { - .num_regs = 2048, + .nregs = 256, + .addr_to_offset = imx6sl_addr_to_offset, + .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, + .ctrl = &ocotp_ctrl_reg_default, +}; + +static struct imx8m_featctrl_data imx8mm_featctrl_data = { + .tester4.vpu_bitmask = 0x1c0000, + .tester4.cpu_bitmask = 0x3, +}; + +static struct imx_ocotp_data imx8mm_ocotp_data = { + .nregs = 256, .addr_to_offset = imx6sl_addr_to_offset, .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, + .feat = &imx8mm_featctrl_data, + .ctrl = &ocotp_ctrl_reg_default, +}; + +static struct imx8m_featctrl_data imx8mn_featctrl_data = { + .tester4.gpu_bitmask = 0x1000000, + .tester4.cpu_bitmask = 0x3, +}; + +static struct imx_ocotp_data imx8mn_ocotp_data = { + .nregs = 256, + .addr_to_offset = imx6sl_addr_to_offset, + .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, + .feat = &imx8mn_featctrl_data, + .ctrl = &ocotp_ctrl_reg_default, +}; + +static struct imx_ocotp_data imx7d_ocotp_data = { + .nregs = 64, + .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, + .ctrl = &ocotp_ctrl_reg_default, }; static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = { @@ -753,17 +1065,43 @@ 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 = &imx8mm_ocotp_data, + }, { + .compatible = "fsl,imx8mn-ocotp", + .data = &imx8mn_ocotp_data, + }, { .compatible = "fsl,vf610-ocotp", .data = &vf610_ocotp_data, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); + +static int imx_ocotp_ensure_probed(void) +{ + if (!imx_ocotp && deep_probe_is_supported()) { + int ret; + + ret = of_devices_ensure_probed_by_dev_id(imx_ocotp_dt_ids); + if (ret) + return ret; + } + + return imx_ocotp ? 0 : -EPROBE_DEFER; +} -static struct driver_d imx_ocotp_driver = { +static struct driver imx_ocotp_driver = { .name = "imx_ocotp", .probe = imx_ocotp_probe, .of_compatible = DRV_OF_COMPAT(imx_ocotp_dt_ids), |