--- /dev/null
+From 998f0633773b3432829fe45d2cd2ffb842f3c78e Mon Sep 17 00:00:00 2001
+From: William-tw Lin <william-tw.lin@mediatek.com>
+Date: Sat, 24 Feb 2024 11:45:07 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Register MediaTek socinfo driver from efuse
+
+The socinfo driver reads chip information from eFuses and does not need
+any devicetree node. Register it from mtk-efuse.
+
+While at it, also add the name for this driver's nvmem_config.
+
+Signed-off-by: William-tw Lin <william-tw.lin@mediatek.com>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 21 ++++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -68,6 +68,7 @@ static int mtk_efuse_probe(struct platfo
+ struct nvmem_config econfig = {};
+ struct mtk_efuse_priv *priv;
+ const struct mtk_efuse_pdata *pdata;
++ struct platform_device *socinfo;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -85,11 +86,20 @@ static int mtk_efuse_probe(struct platfo
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
+ econfig.dev = dev;
++ econfig.name = "mtk-efuse";
+ if (pdata->uses_post_processing)
+ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+ nvmem = devm_nvmem_register(dev, &econfig);
++ if (IS_ERR(nvmem))
++ return PTR_ERR(nvmem);
+
+- return PTR_ERR_OR_ZERO(nvmem);
++ socinfo = platform_device_register_data(&pdev->dev, "mtk-socinfo",
++ PLATFORM_DEVID_AUTO, NULL, 0);
++ if (IS_ERR(socinfo))
++ dev_info(dev, "MediaTek SoC Information will be unavailable\n");
++
++ platform_set_drvdata(pdev, socinfo);
++ return 0;
+ }
+
+ static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
+@@ -108,8 +118,17 @@ static const struct of_device_id mtk_efu
+ };
+ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
+
++static void mtk_efuse_remove(struct platform_device *pdev)
++{
++ struct platform_device *socinfo = platform_get_drvdata(pdev);
++
++ if (!IS_ERR_OR_NULL(socinfo))
++ platform_device_unregister(socinfo);
++}
++
+ static struct platform_driver mtk_efuse_driver = {
+ .probe = mtk_efuse_probe,
++ .remove_new = mtk_efuse_remove,
+ .driver = {
+ .name = "mediatek,efuse",
+ .of_match_table = mtk_efuse_of_match,
--- /dev/null
+From 29be47fcd6a06ea2e79eeeca6e69ad1e23254a69 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:11 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: zynqmp_nvmem_probe cleanup
+
+- Remove static nvmem_config declaration
+- Remove zynqmp_nvmem_data
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 37 ++++++++++++------------------------
+ 1 file changed, 12 insertions(+), 25 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+ * Copyright (C) 2019 Xilinx, Inc.
++ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+ */
+
+ #include <linux/module.h>
+@@ -11,36 +12,25 @@
+
+ #define SILICON_REVISION_MASK 0xF
+
+-struct zynqmp_nvmem_data {
+- struct device *dev;
+- struct nvmem_device *nvmem;
+-};
+
+ static int zynqmp_nvmem_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+ {
++ struct device *dev = context;
+ int ret;
+- int idcode, version;
+- struct zynqmp_nvmem_data *priv = context;
++ int idcode;
++ int version;
+
+ ret = zynqmp_pm_get_chipid(&idcode, &version);
+ if (ret < 0)
+ return ret;
+
+- dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version);
++ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+ *(int *)val = version & SILICON_REVISION_MASK;
+
+ return 0;
+ }
+
+-static struct nvmem_config econfig = {
+- .name = "zynqmp-nvmem",
+- .owner = THIS_MODULE,
+- .word_size = 1,
+- .size = 1,
+- .read_only = true,
+-};
+-
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+ { .compatible = "xlnx,zynqmp-nvmem-fw", },
+ { /* sentinel */ },
+@@ -50,21 +40,18 @@ MODULE_DEVICE_TABLE(of, zynqmp_nvmem_mat
+ static int zynqmp_nvmem_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+- struct zynqmp_nvmem_data *priv;
++ struct nvmem_config econfig = {};
+
+- priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->dev = dev;
++ econfig.name = "zynqmp-nvmem";
++ econfig.owner = THIS_MODULE;
++ econfig.word_size = 1;
++ econfig.size = 1;
+ econfig.dev = dev;
+ econfig.add_legacy_fixed_of_cells = true;
++ econfig.read_only = true;
+ econfig.reg_read = zynqmp_nvmem_read;
+- econfig.priv = priv;
+-
+- priv->nvmem = devm_nvmem_register(dev, &econfig);
+
+- return PTR_ERR_OR_ZERO(priv->nvmem);
++ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
+
+ static struct platform_driver zynqmp_nvmem_driver = {
--- /dev/null
+From 737c0c8d07b5f671c0a33cec95965fcb2d2ea893 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:12 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: Add support to access efuse
+
+Add support to read/write efuse memory map of ZynqMP.
+Below are the offsets of ZynqMP efuse memory map
+ 0 - SOC version(read only)
+ 0xC - 0xFC -ZynqMP specific purpose efuses
+ 0x100 - 0x17F - Physical Unclonable Function(PUF)
+ efuses repurposed as user efuses
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 186 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 176 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -4,6 +4,7 @@
+ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+ */
+
++#include <linux/dma-mapping.h>
+ #include <linux/module.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of.h>
+@@ -11,24 +12,189 @@
+ #include <linux/firmware/xlnx-zynqmp.h>
+
+ #define SILICON_REVISION_MASK 0xF
++#define P_USER_0_64_UPPER_MASK GENMASK(31, 16)
++#define P_USER_127_LOWER_4_BIT_MASK GENMASK(3, 0)
++#define WORD_INBYTES 4
++#define SOC_VER_SIZE 0x4
++#define EFUSE_MEMORY_SIZE 0x177
++#define UNUSED_SPACE 0x8
++#define ZYNQMP_NVMEM_SIZE (SOC_VER_SIZE + UNUSED_SPACE + \
++ EFUSE_MEMORY_SIZE)
++#define SOC_VERSION_OFFSET 0x0
++#define EFUSE_START_OFFSET 0xC
++#define EFUSE_END_OFFSET 0xFC
++#define EFUSE_PUF_START_OFFSET 0x100
++#define EFUSE_PUF_MID_OFFSET 0x140
++#define EFUSE_PUF_END_OFFSET 0x17F
++#define EFUSE_NOT_ENABLED 29
+
++/*
++ * efuse access type
++ */
++enum efuse_access {
++ EFUSE_READ = 0,
++ EFUSE_WRITE
++};
++
++/**
++ * struct xilinx_efuse - the basic structure
++ * @src: address of the buffer to store the data to be write/read
++ * @size: read/write word count
++ * @offset: read/write offset
++ * @flag: 0 - represents efuse read and 1- represents efuse write
++ * @pufuserfuse:0 - represents non-puf efuses, offset is used for read/write
++ * 1 - represents puf user fuse row number.
++ *
++ * this structure stores all the required details to
++ * read/write efuse memory.
++ */
++struct xilinx_efuse {
++ u64 src;
++ u32 size;
++ u32 offset;
++ enum efuse_access flag;
++ u32 pufuserfuse;
++};
++
++static int zynqmp_efuse_access(void *context, unsigned int offset,
++ void *val, size_t bytes, enum efuse_access flag,
++ unsigned int pufflag)
++{
++ struct device *dev = context;
++ struct xilinx_efuse *efuse;
++ dma_addr_t dma_addr;
++ dma_addr_t dma_buf;
++ size_t words = bytes / WORD_INBYTES;
++ int ret;
++ int value;
++ char *data;
+
+-static int zynqmp_nvmem_read(void *context, unsigned int offset,
+- void *val, size_t bytes)
++ if (bytes % WORD_INBYTES != 0) {
++ dev_err(dev, "Bytes requested should be word aligned\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (pufflag == 0 && offset % WORD_INBYTES) {
++ dev_err(dev, "Offset requested should be word aligned\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (pufflag == 1 && flag == EFUSE_WRITE) {
++ memcpy(&value, val, bytes);
++ if ((offset == EFUSE_PUF_START_OFFSET ||
++ offset == EFUSE_PUF_MID_OFFSET) &&
++ value & P_USER_0_64_UPPER_MASK) {
++ dev_err(dev, "Only lower 4 bytes are allowed to be programmed in P_USER_0 & P_USER_64\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (offset == EFUSE_PUF_END_OFFSET &&
++ (value & P_USER_127_LOWER_4_BIT_MASK)) {
++ dev_err(dev, "Only MSB 28 bits are allowed to be programmed for P_USER_127\n");
++ return -EOPNOTSUPP;
++ }
++ }
++
++ efuse = dma_alloc_coherent(dev, sizeof(struct xilinx_efuse),
++ &dma_addr, GFP_KERNEL);
++ if (!efuse)
++ return -ENOMEM;
++
++ data = dma_alloc_coherent(dev, sizeof(bytes),
++ &dma_buf, GFP_KERNEL);
++ if (!data) {
++ ret = -ENOMEM;
++ goto efuse_data_fail;
++ }
++
++ if (flag == EFUSE_WRITE) {
++ memcpy(data, val, bytes);
++ efuse->flag = EFUSE_WRITE;
++ } else {
++ efuse->flag = EFUSE_READ;
++ }
++
++ efuse->src = dma_buf;
++ efuse->size = words;
++ efuse->offset = offset;
++ efuse->pufuserfuse = pufflag;
++
++ zynqmp_pm_efuse_access(dma_addr, (u32 *)&ret);
++ if (ret != 0) {
++ if (ret == EFUSE_NOT_ENABLED) {
++ dev_err(dev, "efuse access is not enabled\n");
++ ret = -EOPNOTSUPP;
++ } else {
++ dev_err(dev, "Error in efuse read %x\n", ret);
++ ret = -EPERM;
++ }
++ goto efuse_access_err;
++ }
++
++ if (flag == EFUSE_READ)
++ memcpy(val, data, bytes);
++efuse_access_err:
++ dma_free_coherent(dev, sizeof(bytes),
++ data, dma_buf);
++efuse_data_fail:
++ dma_free_coherent(dev, sizeof(struct xilinx_efuse),
++ efuse, dma_addr);
++
++ return ret;
++}
++
++static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, size_t bytes)
+ {
+ struct device *dev = context;
+ int ret;
++ int pufflag = 0;
+ int idcode;
+ int version;
+
+- ret = zynqmp_pm_get_chipid(&idcode, &version);
+- if (ret < 0)
+- return ret;
++ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++ pufflag = 1;
++
++ switch (offset) {
++ /* Soc version offset is zero */
++ case SOC_VERSION_OFFSET:
++ if (bytes != SOC_VER_SIZE)
++ return -EOPNOTSUPP;
++
++ ret = zynqmp_pm_get_chipid((u32 *)&idcode, (u32 *)&version);
++ if (ret < 0)
++ return ret;
++
++ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
++ *(int *)val = version & SILICON_REVISION_MASK;
++ break;
++ /* Efuse offset starts from 0xc */
++ case EFUSE_START_OFFSET ... EFUSE_END_OFFSET:
++ case EFUSE_PUF_START_OFFSET ... EFUSE_PUF_END_OFFSET:
++ ret = zynqmp_efuse_access(context, offset, val,
++ bytes, EFUSE_READ, pufflag);
++ break;
++ default:
++ *(u32 *)val = 0xDEADBEEF;
++ ret = 0;
++ break;
++ }
++
++ return ret;
++}
++
++static int zynqmp_nvmem_write(void *context,
++ unsigned int offset, void *val, size_t bytes)
++{
++ int pufflag = 0;
++
++ if (offset < EFUSE_START_OFFSET || offset > EFUSE_PUF_END_OFFSET)
++ return -EOPNOTSUPP;
+
+- dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+- *(int *)val = version & SILICON_REVISION_MASK;
++ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++ pufflag = 1;
+
+- return 0;
++ return zynqmp_efuse_access(context, offset,
++ val, bytes, EFUSE_WRITE, pufflag);
+ }
+
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+@@ -45,11 +211,11 @@ static int zynqmp_nvmem_probe(struct pla
+ econfig.name = "zynqmp-nvmem";
+ econfig.owner = THIS_MODULE;
+ econfig.word_size = 1;
+- econfig.size = 1;
++ econfig.size = ZYNQMP_NVMEM_SIZE;
+ econfig.dev = dev;
+ econfig.add_legacy_fixed_of_cells = true;
+- econfig.read_only = true;
+ econfig.reg_read = zynqmp_nvmem_read;
++ econfig.reg_write = zynqmp_nvmem_write;
+
+ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
--- /dev/null
+From 76c345edef754b16cab81ad9452cc49c09e67066 Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wenst@chromium.org>
+Date: Sat, 24 Feb 2024 11:45:14 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Drop NVMEM device name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT8183 has not one but two efuse devices. The static name and ID
+causes the second efuse device to fail to probe, due to duplicate sysfs
+entries.
+
+With the rework of the mtk-socinfo driver, lookup by name is no longer
+necessary. The custom name can simply be dropped.
+
+Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Tested-by: "Nícolas F. R. A. Prado" <nfraprado@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -86,7 +86,6 @@ static int mtk_efuse_probe(struct platfo
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
+ econfig.dev = dev;
+- econfig.name = "mtk-efuse";
+ if (pdata->uses_post_processing)
+ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+ nvmem = devm_nvmem_register(dev, &econfig);
--- /dev/null
+From def3173d4f17b37cecbd74d7c269a080b0b01598 Mon Sep 17 00:00:00 2001
+From: Markus Schneider-Pargmann <msp@baylibre.com>
+Date: Sat, 24 Feb 2024 11:45:16 +0000
+Subject: [PATCH] nvmem: core: Print error on wrong bits DT property
+
+The algorithms in nvmem core are built with the constraint that
+bit_offset < 8. If bit_offset is greater the results are wrong. Print an
+error if the devicetree 'bits' property is outside of the valid range
+and abort parsing.
+
+Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-12-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -807,6 +807,11 @@ static int nvmem_add_cells_from_dt(struc
+ if (addr && len == (2 * sizeof(u32))) {
+ info.bit_offset = be32_to_cpup(addr++);
+ info.nbits = be32_to_cpup(addr);
++ if (info.bit_offset >= BITS_PER_BYTE || info.nbits < 1) {
++ dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
++ of_node_put(child);
++ return -EINVAL;
++ }
+ }
+
+ info.np = of_node_get(child);
--- /dev/null
+From 998f0633773b3432829fe45d2cd2ffb842f3c78e Mon Sep 17 00:00:00 2001
+From: William-tw Lin <william-tw.lin@mediatek.com>
+Date: Sat, 24 Feb 2024 11:45:07 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Register MediaTek socinfo driver from efuse
+
+The socinfo driver reads chip information from eFuses and does not need
+any devicetree node. Register it from mtk-efuse.
+
+While at it, also add the name for this driver's nvmem_config.
+
+Signed-off-by: William-tw Lin <william-tw.lin@mediatek.com>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 21 ++++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -68,6 +68,7 @@ static int mtk_efuse_probe(struct platfo
+ struct nvmem_config econfig = {};
+ struct mtk_efuse_priv *priv;
+ const struct mtk_efuse_pdata *pdata;
++ struct platform_device *socinfo;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -85,11 +86,20 @@ static int mtk_efuse_probe(struct platfo
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
+ econfig.dev = dev;
++ econfig.name = "mtk-efuse";
+ if (pdata->uses_post_processing)
+ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+ nvmem = devm_nvmem_register(dev, &econfig);
++ if (IS_ERR(nvmem))
++ return PTR_ERR(nvmem);
+
+- return PTR_ERR_OR_ZERO(nvmem);
++ socinfo = platform_device_register_data(&pdev->dev, "mtk-socinfo",
++ PLATFORM_DEVID_AUTO, NULL, 0);
++ if (IS_ERR(socinfo))
++ dev_info(dev, "MediaTek SoC Information will be unavailable\n");
++
++ platform_set_drvdata(pdev, socinfo);
++ return 0;
+ }
+
+ static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
+@@ -108,8 +118,17 @@ static const struct of_device_id mtk_efu
+ };
+ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
+
++static void mtk_efuse_remove(struct platform_device *pdev)
++{
++ struct platform_device *socinfo = platform_get_drvdata(pdev);
++
++ if (!IS_ERR_OR_NULL(socinfo))
++ platform_device_unregister(socinfo);
++}
++
+ static struct platform_driver mtk_efuse_driver = {
+ .probe = mtk_efuse_probe,
++ .remove_new = mtk_efuse_remove,
+ .driver = {
+ .name = "mediatek,efuse",
+ .of_match_table = mtk_efuse_of_match,
--- /dev/null
+From 29be47fcd6a06ea2e79eeeca6e69ad1e23254a69 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:11 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: zynqmp_nvmem_probe cleanup
+
+- Remove static nvmem_config declaration
+- Remove zynqmp_nvmem_data
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 37 ++++++++++++------------------------
+ 1 file changed, 12 insertions(+), 25 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+ * Copyright (C) 2019 Xilinx, Inc.
++ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+ */
+
+ #include <linux/module.h>
+@@ -11,36 +12,25 @@
+
+ #define SILICON_REVISION_MASK 0xF
+
+-struct zynqmp_nvmem_data {
+- struct device *dev;
+- struct nvmem_device *nvmem;
+-};
+
+ static int zynqmp_nvmem_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+ {
++ struct device *dev = context;
+ int ret;
+- int idcode, version;
+- struct zynqmp_nvmem_data *priv = context;
++ int idcode;
++ int version;
+
+ ret = zynqmp_pm_get_chipid(&idcode, &version);
+ if (ret < 0)
+ return ret;
+
+- dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version);
++ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+ *(int *)val = version & SILICON_REVISION_MASK;
+
+ return 0;
+ }
+
+-static struct nvmem_config econfig = {
+- .name = "zynqmp-nvmem",
+- .owner = THIS_MODULE,
+- .word_size = 1,
+- .size = 1,
+- .read_only = true,
+-};
+-
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+ { .compatible = "xlnx,zynqmp-nvmem-fw", },
+ { /* sentinel */ },
+@@ -50,21 +40,18 @@ MODULE_DEVICE_TABLE(of, zynqmp_nvmem_mat
+ static int zynqmp_nvmem_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+- struct zynqmp_nvmem_data *priv;
++ struct nvmem_config econfig = {};
+
+- priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->dev = dev;
++ econfig.name = "zynqmp-nvmem";
++ econfig.owner = THIS_MODULE;
++ econfig.word_size = 1;
++ econfig.size = 1;
+ econfig.dev = dev;
+ econfig.add_legacy_fixed_of_cells = true;
++ econfig.read_only = true;
+ econfig.reg_read = zynqmp_nvmem_read;
+- econfig.priv = priv;
+-
+- priv->nvmem = devm_nvmem_register(dev, &econfig);
+
+- return PTR_ERR_OR_ZERO(priv->nvmem);
++ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
+
+ static struct platform_driver zynqmp_nvmem_driver = {
--- /dev/null
+From 737c0c8d07b5f671c0a33cec95965fcb2d2ea893 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:12 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: Add support to access efuse
+
+Add support to read/write efuse memory map of ZynqMP.
+Below are the offsets of ZynqMP efuse memory map
+ 0 - SOC version(read only)
+ 0xC - 0xFC -ZynqMP specific purpose efuses
+ 0x100 - 0x17F - Physical Unclonable Function(PUF)
+ efuses repurposed as user efuses
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 186 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 176 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -4,6 +4,7 @@
+ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+ */
+
++#include <linux/dma-mapping.h>
+ #include <linux/module.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of.h>
+@@ -11,24 +12,189 @@
+ #include <linux/firmware/xlnx-zynqmp.h>
+
+ #define SILICON_REVISION_MASK 0xF
++#define P_USER_0_64_UPPER_MASK GENMASK(31, 16)
++#define P_USER_127_LOWER_4_BIT_MASK GENMASK(3, 0)
++#define WORD_INBYTES 4
++#define SOC_VER_SIZE 0x4
++#define EFUSE_MEMORY_SIZE 0x177
++#define UNUSED_SPACE 0x8
++#define ZYNQMP_NVMEM_SIZE (SOC_VER_SIZE + UNUSED_SPACE + \
++ EFUSE_MEMORY_SIZE)
++#define SOC_VERSION_OFFSET 0x0
++#define EFUSE_START_OFFSET 0xC
++#define EFUSE_END_OFFSET 0xFC
++#define EFUSE_PUF_START_OFFSET 0x100
++#define EFUSE_PUF_MID_OFFSET 0x140
++#define EFUSE_PUF_END_OFFSET 0x17F
++#define EFUSE_NOT_ENABLED 29
+
++/*
++ * efuse access type
++ */
++enum efuse_access {
++ EFUSE_READ = 0,
++ EFUSE_WRITE
++};
++
++/**
++ * struct xilinx_efuse - the basic structure
++ * @src: address of the buffer to store the data to be write/read
++ * @size: read/write word count
++ * @offset: read/write offset
++ * @flag: 0 - represents efuse read and 1- represents efuse write
++ * @pufuserfuse:0 - represents non-puf efuses, offset is used for read/write
++ * 1 - represents puf user fuse row number.
++ *
++ * this structure stores all the required details to
++ * read/write efuse memory.
++ */
++struct xilinx_efuse {
++ u64 src;
++ u32 size;
++ u32 offset;
++ enum efuse_access flag;
++ u32 pufuserfuse;
++};
++
++static int zynqmp_efuse_access(void *context, unsigned int offset,
++ void *val, size_t bytes, enum efuse_access flag,
++ unsigned int pufflag)
++{
++ struct device *dev = context;
++ struct xilinx_efuse *efuse;
++ dma_addr_t dma_addr;
++ dma_addr_t dma_buf;
++ size_t words = bytes / WORD_INBYTES;
++ int ret;
++ int value;
++ char *data;
+
+-static int zynqmp_nvmem_read(void *context, unsigned int offset,
+- void *val, size_t bytes)
++ if (bytes % WORD_INBYTES != 0) {
++ dev_err(dev, "Bytes requested should be word aligned\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (pufflag == 0 && offset % WORD_INBYTES) {
++ dev_err(dev, "Offset requested should be word aligned\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (pufflag == 1 && flag == EFUSE_WRITE) {
++ memcpy(&value, val, bytes);
++ if ((offset == EFUSE_PUF_START_OFFSET ||
++ offset == EFUSE_PUF_MID_OFFSET) &&
++ value & P_USER_0_64_UPPER_MASK) {
++ dev_err(dev, "Only lower 4 bytes are allowed to be programmed in P_USER_0 & P_USER_64\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (offset == EFUSE_PUF_END_OFFSET &&
++ (value & P_USER_127_LOWER_4_BIT_MASK)) {
++ dev_err(dev, "Only MSB 28 bits are allowed to be programmed for P_USER_127\n");
++ return -EOPNOTSUPP;
++ }
++ }
++
++ efuse = dma_alloc_coherent(dev, sizeof(struct xilinx_efuse),
++ &dma_addr, GFP_KERNEL);
++ if (!efuse)
++ return -ENOMEM;
++
++ data = dma_alloc_coherent(dev, sizeof(bytes),
++ &dma_buf, GFP_KERNEL);
++ if (!data) {
++ ret = -ENOMEM;
++ goto efuse_data_fail;
++ }
++
++ if (flag == EFUSE_WRITE) {
++ memcpy(data, val, bytes);
++ efuse->flag = EFUSE_WRITE;
++ } else {
++ efuse->flag = EFUSE_READ;
++ }
++
++ efuse->src = dma_buf;
++ efuse->size = words;
++ efuse->offset = offset;
++ efuse->pufuserfuse = pufflag;
++
++ zynqmp_pm_efuse_access(dma_addr, (u32 *)&ret);
++ if (ret != 0) {
++ if (ret == EFUSE_NOT_ENABLED) {
++ dev_err(dev, "efuse access is not enabled\n");
++ ret = -EOPNOTSUPP;
++ } else {
++ dev_err(dev, "Error in efuse read %x\n", ret);
++ ret = -EPERM;
++ }
++ goto efuse_access_err;
++ }
++
++ if (flag == EFUSE_READ)
++ memcpy(val, data, bytes);
++efuse_access_err:
++ dma_free_coherent(dev, sizeof(bytes),
++ data, dma_buf);
++efuse_data_fail:
++ dma_free_coherent(dev, sizeof(struct xilinx_efuse),
++ efuse, dma_addr);
++
++ return ret;
++}
++
++static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, size_t bytes)
+ {
+ struct device *dev = context;
+ int ret;
++ int pufflag = 0;
+ int idcode;
+ int version;
+
+- ret = zynqmp_pm_get_chipid(&idcode, &version);
+- if (ret < 0)
+- return ret;
++ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++ pufflag = 1;
++
++ switch (offset) {
++ /* Soc version offset is zero */
++ case SOC_VERSION_OFFSET:
++ if (bytes != SOC_VER_SIZE)
++ return -EOPNOTSUPP;
++
++ ret = zynqmp_pm_get_chipid((u32 *)&idcode, (u32 *)&version);
++ if (ret < 0)
++ return ret;
++
++ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
++ *(int *)val = version & SILICON_REVISION_MASK;
++ break;
++ /* Efuse offset starts from 0xc */
++ case EFUSE_START_OFFSET ... EFUSE_END_OFFSET:
++ case EFUSE_PUF_START_OFFSET ... EFUSE_PUF_END_OFFSET:
++ ret = zynqmp_efuse_access(context, offset, val,
++ bytes, EFUSE_READ, pufflag);
++ break;
++ default:
++ *(u32 *)val = 0xDEADBEEF;
++ ret = 0;
++ break;
++ }
++
++ return ret;
++}
++
++static int zynqmp_nvmem_write(void *context,
++ unsigned int offset, void *val, size_t bytes)
++{
++ int pufflag = 0;
++
++ if (offset < EFUSE_START_OFFSET || offset > EFUSE_PUF_END_OFFSET)
++ return -EOPNOTSUPP;
+
+- dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+- *(int *)val = version & SILICON_REVISION_MASK;
++ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++ pufflag = 1;
+
+- return 0;
++ return zynqmp_efuse_access(context, offset,
++ val, bytes, EFUSE_WRITE, pufflag);
+ }
+
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+@@ -45,11 +211,11 @@ static int zynqmp_nvmem_probe(struct pla
+ econfig.name = "zynqmp-nvmem";
+ econfig.owner = THIS_MODULE;
+ econfig.word_size = 1;
+- econfig.size = 1;
++ econfig.size = ZYNQMP_NVMEM_SIZE;
+ econfig.dev = dev;
+ econfig.add_legacy_fixed_of_cells = true;
+- econfig.read_only = true;
+ econfig.reg_read = zynqmp_nvmem_read;
++ econfig.reg_write = zynqmp_nvmem_write;
+
+ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
--- /dev/null
+From 76c345edef754b16cab81ad9452cc49c09e67066 Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wenst@chromium.org>
+Date: Sat, 24 Feb 2024 11:45:14 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Drop NVMEM device name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT8183 has not one but two efuse devices. The static name and ID
+causes the second efuse device to fail to probe, due to duplicate sysfs
+entries.
+
+With the rework of the mtk-socinfo driver, lookup by name is no longer
+necessary. The custom name can simply be dropped.
+
+Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Tested-by: "Nícolas F. R. A. Prado" <nfraprado@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -86,7 +86,6 @@ static int mtk_efuse_probe(struct platfo
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
+ econfig.dev = dev;
+- econfig.name = "mtk-efuse";
+ if (pdata->uses_post_processing)
+ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+ nvmem = devm_nvmem_register(dev, &econfig);
--- /dev/null
+From def3173d4f17b37cecbd74d7c269a080b0b01598 Mon Sep 17 00:00:00 2001
+From: Markus Schneider-Pargmann <msp@baylibre.com>
+Date: Sat, 24 Feb 2024 11:45:16 +0000
+Subject: [PATCH] nvmem: core: Print error on wrong bits DT property
+
+The algorithms in nvmem core are built with the constraint that
+bit_offset < 8. If bit_offset is greater the results are wrong. Print an
+error if the devicetree 'bits' property is outside of the valid range
+and abort parsing.
+
+Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-12-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -806,6 +806,11 @@ static int nvmem_add_cells_from_dt(struc
+ if (addr && len == (2 * sizeof(u32))) {
+ info.bit_offset = be32_to_cpup(addr++);
+ info.nbits = be32_to_cpup(addr);
++ if (info.bit_offset >= BITS_PER_BYTE || info.nbits < 1) {
++ dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
++ of_node_put(child);
++ return -EINVAL;
++ }
+ }
+
+ info.np = of_node_get(child);
--- /dev/null
+From 998f0633773b3432829fe45d2cd2ffb842f3c78e Mon Sep 17 00:00:00 2001
+From: William-tw Lin <william-tw.lin@mediatek.com>
+Date: Sat, 24 Feb 2024 11:45:07 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Register MediaTek socinfo driver from efuse
+
+The socinfo driver reads chip information from eFuses and does not need
+any devicetree node. Register it from mtk-efuse.
+
+While at it, also add the name for this driver's nvmem_config.
+
+Signed-off-by: William-tw Lin <william-tw.lin@mediatek.com>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 21 ++++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -68,6 +68,7 @@ static int mtk_efuse_probe(struct platfo
+ struct nvmem_config econfig = {};
+ struct mtk_efuse_priv *priv;
+ const struct mtk_efuse_pdata *pdata;
++ struct platform_device *socinfo;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -85,11 +86,20 @@ static int mtk_efuse_probe(struct platfo
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
+ econfig.dev = dev;
++ econfig.name = "mtk-efuse";
+ if (pdata->uses_post_processing)
+ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+ nvmem = devm_nvmem_register(dev, &econfig);
++ if (IS_ERR(nvmem))
++ return PTR_ERR(nvmem);
+
+- return PTR_ERR_OR_ZERO(nvmem);
++ socinfo = platform_device_register_data(&pdev->dev, "mtk-socinfo",
++ PLATFORM_DEVID_AUTO, NULL, 0);
++ if (IS_ERR(socinfo))
++ dev_info(dev, "MediaTek SoC Information will be unavailable\n");
++
++ platform_set_drvdata(pdev, socinfo);
++ return 0;
+ }
+
+ static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = {
+@@ -108,8 +118,17 @@ static const struct of_device_id mtk_efu
+ };
+ MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
+
++static void mtk_efuse_remove(struct platform_device *pdev)
++{
++ struct platform_device *socinfo = platform_get_drvdata(pdev);
++
++ if (!IS_ERR_OR_NULL(socinfo))
++ platform_device_unregister(socinfo);
++}
++
+ static struct platform_driver mtk_efuse_driver = {
+ .probe = mtk_efuse_probe,
++ .remove_new = mtk_efuse_remove,
+ .driver = {
+ .name = "mediatek,efuse",
+ .of_match_table = mtk_efuse_of_match,
--- /dev/null
+From 29be47fcd6a06ea2e79eeeca6e69ad1e23254a69 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:11 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: zynqmp_nvmem_probe cleanup
+
+- Remove static nvmem_config declaration
+- Remove zynqmp_nvmem_data
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-7-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 37 ++++++++++++------------------------
+ 1 file changed, 12 insertions(+), 25 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+ * Copyright (C) 2019 Xilinx, Inc.
++ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+ */
+
+ #include <linux/module.h>
+@@ -11,36 +12,25 @@
+
+ #define SILICON_REVISION_MASK 0xF
+
+-struct zynqmp_nvmem_data {
+- struct device *dev;
+- struct nvmem_device *nvmem;
+-};
+
+ static int zynqmp_nvmem_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+ {
++ struct device *dev = context;
+ int ret;
+- int idcode, version;
+- struct zynqmp_nvmem_data *priv = context;
++ int idcode;
++ int version;
+
+ ret = zynqmp_pm_get_chipid(&idcode, &version);
+ if (ret < 0)
+ return ret;
+
+- dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version);
++ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+ *(int *)val = version & SILICON_REVISION_MASK;
+
+ return 0;
+ }
+
+-static struct nvmem_config econfig = {
+- .name = "zynqmp-nvmem",
+- .owner = THIS_MODULE,
+- .word_size = 1,
+- .size = 1,
+- .read_only = true,
+-};
+-
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+ { .compatible = "xlnx,zynqmp-nvmem-fw", },
+ { /* sentinel */ },
+@@ -50,21 +40,18 @@ MODULE_DEVICE_TABLE(of, zynqmp_nvmem_mat
+ static int zynqmp_nvmem_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+- struct zynqmp_nvmem_data *priv;
++ struct nvmem_config econfig = {};
+
+- priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+-
+- priv->dev = dev;
++ econfig.name = "zynqmp-nvmem";
++ econfig.owner = THIS_MODULE;
++ econfig.word_size = 1;
++ econfig.size = 1;
+ econfig.dev = dev;
+ econfig.add_legacy_fixed_of_cells = true;
++ econfig.read_only = true;
+ econfig.reg_read = zynqmp_nvmem_read;
+- econfig.priv = priv;
+-
+- priv->nvmem = devm_nvmem_register(dev, &econfig);
+
+- return PTR_ERR_OR_ZERO(priv->nvmem);
++ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
+
+ static struct platform_driver zynqmp_nvmem_driver = {
--- /dev/null
+From 737c0c8d07b5f671c0a33cec95965fcb2d2ea893 Mon Sep 17 00:00:00 2001
+From: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Date: Sat, 24 Feb 2024 11:45:12 +0000
+Subject: [PATCH] nvmem: zynqmp_nvmem: Add support to access efuse
+
+Add support to read/write efuse memory map of ZynqMP.
+Below are the offsets of ZynqMP efuse memory map
+ 0 - SOC version(read only)
+ 0xC - 0xFC -ZynqMP specific purpose efuses
+ 0x100 - 0x17F - Physical Unclonable Function(PUF)
+ efuses repurposed as user efuses
+
+Signed-off-by: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
+Acked-by: Kalyani Akula <Kalyani.akula@amd.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-8-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/zynqmp_nvmem.c | 186 +++++++++++++++++++++++++++++++++--
+ 1 file changed, 176 insertions(+), 10 deletions(-)
+
+--- a/drivers/nvmem/zynqmp_nvmem.c
++++ b/drivers/nvmem/zynqmp_nvmem.c
+@@ -4,6 +4,7 @@
+ * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+ */
+
++#include <linux/dma-mapping.h>
+ #include <linux/module.h>
+ #include <linux/nvmem-provider.h>
+ #include <linux/of.h>
+@@ -11,24 +12,189 @@
+ #include <linux/firmware/xlnx-zynqmp.h>
+
+ #define SILICON_REVISION_MASK 0xF
++#define P_USER_0_64_UPPER_MASK GENMASK(31, 16)
++#define P_USER_127_LOWER_4_BIT_MASK GENMASK(3, 0)
++#define WORD_INBYTES 4
++#define SOC_VER_SIZE 0x4
++#define EFUSE_MEMORY_SIZE 0x177
++#define UNUSED_SPACE 0x8
++#define ZYNQMP_NVMEM_SIZE (SOC_VER_SIZE + UNUSED_SPACE + \
++ EFUSE_MEMORY_SIZE)
++#define SOC_VERSION_OFFSET 0x0
++#define EFUSE_START_OFFSET 0xC
++#define EFUSE_END_OFFSET 0xFC
++#define EFUSE_PUF_START_OFFSET 0x100
++#define EFUSE_PUF_MID_OFFSET 0x140
++#define EFUSE_PUF_END_OFFSET 0x17F
++#define EFUSE_NOT_ENABLED 29
+
++/*
++ * efuse access type
++ */
++enum efuse_access {
++ EFUSE_READ = 0,
++ EFUSE_WRITE
++};
++
++/**
++ * struct xilinx_efuse - the basic structure
++ * @src: address of the buffer to store the data to be write/read
++ * @size: read/write word count
++ * @offset: read/write offset
++ * @flag: 0 - represents efuse read and 1- represents efuse write
++ * @pufuserfuse:0 - represents non-puf efuses, offset is used for read/write
++ * 1 - represents puf user fuse row number.
++ *
++ * this structure stores all the required details to
++ * read/write efuse memory.
++ */
++struct xilinx_efuse {
++ u64 src;
++ u32 size;
++ u32 offset;
++ enum efuse_access flag;
++ u32 pufuserfuse;
++};
++
++static int zynqmp_efuse_access(void *context, unsigned int offset,
++ void *val, size_t bytes, enum efuse_access flag,
++ unsigned int pufflag)
++{
++ struct device *dev = context;
++ struct xilinx_efuse *efuse;
++ dma_addr_t dma_addr;
++ dma_addr_t dma_buf;
++ size_t words = bytes / WORD_INBYTES;
++ int ret;
++ int value;
++ char *data;
+
+-static int zynqmp_nvmem_read(void *context, unsigned int offset,
+- void *val, size_t bytes)
++ if (bytes % WORD_INBYTES != 0) {
++ dev_err(dev, "Bytes requested should be word aligned\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (pufflag == 0 && offset % WORD_INBYTES) {
++ dev_err(dev, "Offset requested should be word aligned\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (pufflag == 1 && flag == EFUSE_WRITE) {
++ memcpy(&value, val, bytes);
++ if ((offset == EFUSE_PUF_START_OFFSET ||
++ offset == EFUSE_PUF_MID_OFFSET) &&
++ value & P_USER_0_64_UPPER_MASK) {
++ dev_err(dev, "Only lower 4 bytes are allowed to be programmed in P_USER_0 & P_USER_64\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (offset == EFUSE_PUF_END_OFFSET &&
++ (value & P_USER_127_LOWER_4_BIT_MASK)) {
++ dev_err(dev, "Only MSB 28 bits are allowed to be programmed for P_USER_127\n");
++ return -EOPNOTSUPP;
++ }
++ }
++
++ efuse = dma_alloc_coherent(dev, sizeof(struct xilinx_efuse),
++ &dma_addr, GFP_KERNEL);
++ if (!efuse)
++ return -ENOMEM;
++
++ data = dma_alloc_coherent(dev, sizeof(bytes),
++ &dma_buf, GFP_KERNEL);
++ if (!data) {
++ ret = -ENOMEM;
++ goto efuse_data_fail;
++ }
++
++ if (flag == EFUSE_WRITE) {
++ memcpy(data, val, bytes);
++ efuse->flag = EFUSE_WRITE;
++ } else {
++ efuse->flag = EFUSE_READ;
++ }
++
++ efuse->src = dma_buf;
++ efuse->size = words;
++ efuse->offset = offset;
++ efuse->pufuserfuse = pufflag;
++
++ zynqmp_pm_efuse_access(dma_addr, (u32 *)&ret);
++ if (ret != 0) {
++ if (ret == EFUSE_NOT_ENABLED) {
++ dev_err(dev, "efuse access is not enabled\n");
++ ret = -EOPNOTSUPP;
++ } else {
++ dev_err(dev, "Error in efuse read %x\n", ret);
++ ret = -EPERM;
++ }
++ goto efuse_access_err;
++ }
++
++ if (flag == EFUSE_READ)
++ memcpy(val, data, bytes);
++efuse_access_err:
++ dma_free_coherent(dev, sizeof(bytes),
++ data, dma_buf);
++efuse_data_fail:
++ dma_free_coherent(dev, sizeof(struct xilinx_efuse),
++ efuse, dma_addr);
++
++ return ret;
++}
++
++static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, size_t bytes)
+ {
+ struct device *dev = context;
+ int ret;
++ int pufflag = 0;
+ int idcode;
+ int version;
+
+- ret = zynqmp_pm_get_chipid(&idcode, &version);
+- if (ret < 0)
+- return ret;
++ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++ pufflag = 1;
++
++ switch (offset) {
++ /* Soc version offset is zero */
++ case SOC_VERSION_OFFSET:
++ if (bytes != SOC_VER_SIZE)
++ return -EOPNOTSUPP;
++
++ ret = zynqmp_pm_get_chipid((u32 *)&idcode, (u32 *)&version);
++ if (ret < 0)
++ return ret;
++
++ dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
++ *(int *)val = version & SILICON_REVISION_MASK;
++ break;
++ /* Efuse offset starts from 0xc */
++ case EFUSE_START_OFFSET ... EFUSE_END_OFFSET:
++ case EFUSE_PUF_START_OFFSET ... EFUSE_PUF_END_OFFSET:
++ ret = zynqmp_efuse_access(context, offset, val,
++ bytes, EFUSE_READ, pufflag);
++ break;
++ default:
++ *(u32 *)val = 0xDEADBEEF;
++ ret = 0;
++ break;
++ }
++
++ return ret;
++}
++
++static int zynqmp_nvmem_write(void *context,
++ unsigned int offset, void *val, size_t bytes)
++{
++ int pufflag = 0;
++
++ if (offset < EFUSE_START_OFFSET || offset > EFUSE_PUF_END_OFFSET)
++ return -EOPNOTSUPP;
+
+- dev_dbg(dev, "Read chipid val %x %x\n", idcode, version);
+- *(int *)val = version & SILICON_REVISION_MASK;
++ if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET)
++ pufflag = 1;
+
+- return 0;
++ return zynqmp_efuse_access(context, offset,
++ val, bytes, EFUSE_WRITE, pufflag);
+ }
+
+ static const struct of_device_id zynqmp_nvmem_match[] = {
+@@ -45,11 +211,11 @@ static int zynqmp_nvmem_probe(struct pla
+ econfig.name = "zynqmp-nvmem";
+ econfig.owner = THIS_MODULE;
+ econfig.word_size = 1;
+- econfig.size = 1;
++ econfig.size = ZYNQMP_NVMEM_SIZE;
+ econfig.dev = dev;
+ econfig.add_legacy_fixed_of_cells = true;
+- econfig.read_only = true;
+ econfig.reg_read = zynqmp_nvmem_read;
++ econfig.reg_write = zynqmp_nvmem_write;
+
+ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &econfig));
+ }
--- /dev/null
+From 76c345edef754b16cab81ad9452cc49c09e67066 Mon Sep 17 00:00:00 2001
+From: Chen-Yu Tsai <wenst@chromium.org>
+Date: Sat, 24 Feb 2024 11:45:14 +0000
+Subject: [PATCH] nvmem: mtk-efuse: Drop NVMEM device name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The MT8183 has not one but two efuse devices. The static name and ID
+causes the second efuse device to fail to probe, due to duplicate sysfs
+entries.
+
+With the rework of the mtk-socinfo driver, lookup by name is no longer
+necessary. The custom name can simply be dropped.
+
+Signed-off-by: Chen-Yu Tsai <wenst@chromium.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+Tested-by: "Nícolas F. R. A. Prado" <nfraprado@collabora.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-10-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/mtk-efuse.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/nvmem/mtk-efuse.c
++++ b/drivers/nvmem/mtk-efuse.c
+@@ -86,7 +86,6 @@ static int mtk_efuse_probe(struct platfo
+ econfig.size = resource_size(res);
+ econfig.priv = priv;
+ econfig.dev = dev;
+- econfig.name = "mtk-efuse";
+ if (pdata->uses_post_processing)
+ econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info;
+ nvmem = devm_nvmem_register(dev, &econfig);
--- /dev/null
+From 8ec0faf2572216b4e25d6829cd41cf3ee2dab979 Mon Sep 17 00:00:00 2001
+From: "Ricardo B. Marliere" <ricardo@marliere.net>
+Date: Sat, 24 Feb 2024 11:45:15 +0000
+Subject: [PATCH] nvmem: core: make nvmem_layout_bus_type const
+
+Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type
+a const *"), the driver core can properly handle constant struct
+bus_type, move the nvmem_layout_bus_type variable to be a constant
+structure as well, placing it into read-only memory which can not be
+modified at runtime.
+
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: "Ricardo B. Marliere" <ricardo@marliere.net>
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-11-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/layouts.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/nvmem/layouts.c
++++ b/drivers/nvmem/layouts.c
+@@ -45,7 +45,7 @@ static void nvmem_layout_bus_remove(stru
+ return drv->remove(layout);
+ }
+
+-static struct bus_type nvmem_layout_bus_type = {
++static const struct bus_type nvmem_layout_bus_type = {
+ .name = "nvmem-layout",
+ .match = nvmem_layout_bus_match,
+ .probe = nvmem_layout_bus_probe,
--- /dev/null
+From def3173d4f17b37cecbd74d7c269a080b0b01598 Mon Sep 17 00:00:00 2001
+From: Markus Schneider-Pargmann <msp@baylibre.com>
+Date: Sat, 24 Feb 2024 11:45:16 +0000
+Subject: [PATCH] nvmem: core: Print error on wrong bits DT property
+
+The algorithms in nvmem core are built with the constraint that
+bit_offset < 8. If bit_offset is greater the results are wrong. Print an
+error if the devicetree 'bits' property is outside of the valid range
+and abort parsing.
+
+Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20240224114516.86365-12-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvmem/core.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -805,6 +805,11 @@ static int nvmem_add_cells_from_dt(struc
+ if (addr && len == (2 * sizeof(u32))) {
+ info.bit_offset = be32_to_cpup(addr++);
+ info.nbits = be32_to_cpup(addr);
++ if (info.bit_offset >= BITS_PER_BYTE || info.nbits < 1) {
++ dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
++ of_node_put(child);
++ return -EINVAL;
++ }
+ }
+
+ info.np = of_node_get(child);
static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
{
struct device *dev = &nvmem->dev;
-@@ -814,6 +873,25 @@ static int nvmem_add_cells_from_dt(struc
+@@ -819,6 +878,25 @@ static int nvmem_add_cells_from_dt(struc
if (nvmem->fixup_dt_cell_info)
nvmem->fixup_dt_cell_info(nvmem, &info);
static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
{
struct device *dev = &nvmem->dev;
-@@ -813,6 +872,25 @@ static int nvmem_add_cells_from_dt(struc
+@@ -818,6 +877,25 @@ static int nvmem_add_cells_from_dt(struc
if (nvmem->fixup_dt_cell_info)
nvmem->fixup_dt_cell_info(nvmem, &info);
static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
{
struct device *dev = &nvmem->dev;
-@@ -812,6 +871,25 @@ static int nvmem_add_cells_from_dt(struc
+@@ -817,6 +876,25 @@ static int nvmem_add_cells_from_dt(struc
if (nvmem->fixup_dt_cell_info)
nvmem->fixup_dt_cell_info(nvmem, &info);