diff options
Diffstat (limited to 'drivers/nvmem/imx-ocotp-scu.c')
-rw-r--r-- | drivers/nvmem/imx-ocotp-scu.c | 120 |
1 files changed, 114 insertions, 6 deletions
diff --git a/drivers/nvmem/imx-ocotp-scu.c b/drivers/nvmem/imx-ocotp-scu.c index 61a17f943f47..03f1ab23ad51 100644 --- a/drivers/nvmem/imx-ocotp-scu.c +++ b/drivers/nvmem/imx-ocotp-scu.c @@ -7,6 +7,7 @@ * Peng Fan <peng.fan@nxp.com> */ +#include <linux/arm-smccc.h> #include <linux/firmware/imx/sci.h> #include <linux/module.h> #include <linux/nvmem-provider.h> @@ -14,14 +15,28 @@ #include <linux/platform_device.h> #include <linux/slab.h> +#define IMX_SIP_OTP 0xC200000A +#define IMX_SIP_OTP_WRITE 0x2 + enum ocotp_devtype { IMX8QXP, IMX8QM, }; +#define ECC_REGION BIT(0) +#define HOLE_REGION BIT(1) + +struct ocotp_region { + u32 start; + u32 end; + u32 flag; +}; + struct ocotp_devtype_data { int devtype; int nregs; + u32 num_region; + struct ocotp_region region[]; }; struct ocotp_priv { @@ -35,16 +50,63 @@ struct imx_sc_msg_misc_fuse_read { u32 word; } __packed; +static DEFINE_MUTEX(scu_ocotp_mutex); + static struct ocotp_devtype_data imx8qxp_data = { .devtype = IMX8QXP, .nregs = 800, + .num_region = 3, + .region = { + {0x10, 0x10f, ECC_REGION}, + {0x110, 0x21F, HOLE_REGION}, + {0x220, 0x31F, ECC_REGION}, + }, }; static struct ocotp_devtype_data imx8qm_data = { .devtype = IMX8QM, .nregs = 800, + .num_region = 2, + .region = { + {0x10, 0x10f, ECC_REGION}, + {0x1a0, 0x1ff, ECC_REGION}, + }, }; +static bool in_hole(void *context, u32 index) +{ + struct ocotp_priv *priv = context; + const struct ocotp_devtype_data *data = priv->data; + int i; + + for (i = 0; i < data->num_region; i++) { + if (data->region[i].flag & HOLE_REGION) { + if ((index >= data->region[i].start) && + (index <= data->region[i].end)) + return true; + } + } + + return false; +} + +static bool in_ecc(void *context, u32 index) +{ + struct ocotp_priv *priv = context; + const struct ocotp_devtype_data *data = priv->data; + int i; + + for (i = 0; i < data->num_region; i++) { + if (data->region[i].flag & ECC_REGION) { + if ((index >= data->region[i].start) && + (index <= data->region[i].end)) + return true; + } + } + + return false; +} + static int imx_sc_misc_otp_fuse_read(struct imx_sc_ipc *ipc, u32 word, u32 *val) { @@ -88,18 +150,19 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset, if (!p) return -ENOMEM; + mutex_lock(&scu_ocotp_mutex); + buf = p; for (i = index; i < (index + count); i++) { - if (priv->data->devtype == IMX8QXP) { - if ((i > 271) && (i < 544)) { - *buf++ = 0; - continue; - } + if (in_hole(context, i)) { + *buf++ = 0; + continue; } ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, i, buf); if (ret) { + mutex_unlock(&scu_ocotp_mutex); kfree(p); return ret; } @@ -108,18 +171,63 @@ static int imx_scu_ocotp_read(void *context, unsigned int offset, memcpy(val, (u8 *)p + offset % 4, bytes); + mutex_unlock(&scu_ocotp_mutex); + kfree(p); return 0; } +static int imx_scu_ocotp_write(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct ocotp_priv *priv = context; + struct arm_smccc_res res; + u32 *buf = val; + u32 tmp; + u32 index; + int ret; + + /* allow only writing one complete OTP word at a time */ + if ((bytes != 4) || (offset % 4)) + return -EINVAL; + + index = offset >> 2; + + if (in_hole(context, index)) + return -EINVAL; + + if (in_ecc(context, index)) { + pr_warn("ECC region, only program once\n"); + mutex_lock(&scu_ocotp_mutex); + ret = imx_sc_misc_otp_fuse_read(priv->nvmem_ipc, index, &tmp); + mutex_unlock(&scu_ocotp_mutex); + if (ret) + return ret; + if (tmp) { + pr_warn("ECC region, already has value: %x\n", tmp); + return -EIO; + } + } + + mutex_lock(&scu_ocotp_mutex); + + arm_smccc_smc(IMX_SIP_OTP, IMX_SIP_OTP_WRITE, index, *buf, + 0, 0, 0, 0, &res); + + mutex_unlock(&scu_ocotp_mutex); + + return res.a0; +} + static struct nvmem_config imx_scu_ocotp_nvmem_config = { .name = "imx-scu-ocotp", - .read_only = true, + .read_only = false, .word_size = 4, .stride = 1, .owner = THIS_MODULE, .reg_read = imx_scu_ocotp_read, + .reg_write = imx_scu_ocotp_write, }; static const struct of_device_id imx_scu_ocotp_dt_ids[] = { |