Raspberry Pis have OTP registers which can be accessed through the
videocore firmware. Add a nvmem driver to support these OTP registers.

Signed-off-by: Gregor Herburger <[email protected]>
---
 drivers/nvmem/Kconfig                      |  10 +++
 drivers/nvmem/Makefile                     |   1 +
 drivers/nvmem/raspberrypi-otp.c            | 119 +++++++++++++++++++++++++++++
 include/soc/bcm2835/raspberrypi-firmware.h |   9 +++
 4 files changed, 139 insertions(+)

diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
index 74ddbd0f79b0..4ecfb0dfbe6b 100644
--- a/drivers/nvmem/Kconfig
+++ b/drivers/nvmem/Kconfig
@@ -483,4 +483,14 @@ config NVMEM_QORIQ_EFUSE
          This driver can also be built as a module. If so, the module
          will be called nvmem_qoriq_efuse.
 
+config NVMEM_RASPBERRYPI_OTP
+       tristate "Raspberry Pi OTP support"
+       depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST
+       help
+         This driver provides access to the Raspberry Pi OTP memory via the
+         nvmem subsystem. The driver supports the customer OTP as well as the
+         device specific private key OTP (BCM2712 only).
+
+         This driver can also be built as a module. If so, the module
+         will be called raspberrypi-otp.
 endif
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
index 7252b8ec88d4..8ca2095e068f 100644
--- a/drivers/nvmem/Makefile
+++ b/drivers/nvmem/Makefile
@@ -95,3 +95,4 @@ obj-$(CONFIG_NVMEM_ZYNQMP)            += nvmem_zynqmp_nvmem.o
 nvmem_zynqmp_nvmem-y                   := zynqmp_nvmem.o
 obj-$(CONFIG_NVMEM_QORIQ_EFUSE)                += nvmem-qoriq-efuse.o
 nvmem-qoriq-efuse-y                    := qoriq-efuse.o
+obj-$(CONFIG_NVMEM_RASPBERRYPI_OTP)    += raspberrypi-otp.o
diff --git a/drivers/nvmem/raspberrypi-otp.c b/drivers/nvmem/raspberrypi-otp.c
new file mode 100644
index 000000000000..83624aa85bb3
--- /dev/null
+++ b/drivers/nvmem/raspberrypi-otp.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/overflow.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/platform_device.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+struct rpi_otp_priv {
+       struct rpi_firmware *fw;
+       struct device *dev;
+       u32 read_tag;
+       u32 write_tag;
+};
+
+struct rpi_otp_header {
+       u32 start;
+       u32 count;
+       u32 data[] __counted_by(count);
+};
+
+static int rpi_otp_read(void *context, unsigned int offset, void *buf, size_t 
bytes)
+{
+       struct rpi_otp_priv *priv = context;
+       struct rpi_otp_header *fwbuf;
+       u32 count = bytes / 4;
+       int ret;
+
+       fwbuf = kzalloc(struct_size(fwbuf, data, count), GFP_KERNEL);
+       if (!fwbuf)
+               return -ENOMEM;
+
+       fwbuf->start = offset / 4;
+       fwbuf->count = count;
+
+       ret = rpi_firmware_property(priv->fw, priv->read_tag, fwbuf,
+                                   sizeof(struct rpi_otp_header) + bytes);
+       if (ret)
+               goto out;
+
+       memcpy(buf, fwbuf->data, bytes);
+
+out:
+       kfree(fwbuf);
+       return ret;
+}
+
+static int rpi_otp_write(void *context, unsigned int offset, void *val, size_t 
bytes)
+{
+       struct rpi_otp_priv *priv = context;
+       struct rpi_otp_header *fwbuf;
+       u32 count = bytes / 4;
+       int ret;
+
+       fwbuf = kzalloc(struct_size(fwbuf, data, count), GFP_KERNEL);
+       if (!fwbuf)
+               return -ENOMEM;
+
+       fwbuf->start = offset / 4;
+       fwbuf->count = count;
+       memcpy(fwbuf->data, val, bytes);
+
+       ret = rpi_firmware_property(priv->fw, priv->write_tag, fwbuf,
+                                   sizeof(struct rpi_otp_header) + bytes);
+
+       kfree(fwbuf);
+       return ret;
+}
+
+static int rpi_otp_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct nvmem_device *nvmem;
+       struct rpi_otp_priv *priv;
+       const struct rpi_otp_driver_data *data;
+       struct nvmem_config config = {
+               .read_only = false,
+               .word_size = 4,
+               .stride = 4,
+               .reg_read = rpi_otp_read,
+               .reg_write = rpi_otp_write,
+               .id = NVMEM_DEVID_NONE,
+       };
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       data = dev_get_platdata(dev);
+       if (!data)
+               return -ENODEV;
+
+       priv->fw = dev_get_drvdata(dev->parent);
+       priv->dev = dev;
+       priv->read_tag = data->read_tag;
+       priv->write_tag = data->write_tag;
+       config.dev = dev;
+       config.priv = priv;
+       config.name = data->name;
+       config.size = data->size;
+
+       nvmem = devm_nvmem_register(dev, &config);
+       if (IS_ERR(nvmem))
+               return dev_err_probe(dev, PTR_ERR(nvmem), "error registering 
nvmem config\n");
+
+       return 0;
+}
+
+static struct platform_driver raspberry_otp_driver = {
+       .probe  = rpi_otp_probe,
+       .driver = {
+               .name   = "raspberrypi-otp",
+       },
+};
+module_platform_driver(raspberry_otp_driver);
+
+MODULE_AUTHOR("Gregor Herburger <[email protected]>");
+MODULE_DESCRIPTION("Raspberry Pi OTP driver");
+MODULE_LICENSE("GPL");
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h 
b/include/soc/bcm2835/raspberrypi-firmware.h
index e1f87fbfe554..ae73c9cd19ba 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -92,6 +92,8 @@ enum rpi_firmware_property_tag {
        RPI_FIRMWARE_SET_POE_HAT_VAL =                        0x00030050,
        RPI_FIRMWARE_NOTIFY_XHCI_RESET =                      0x00030058,
        RPI_FIRMWARE_NOTIFY_DISPLAY_DONE =                    0x00030066,
+       RPI_FIRMWARE_GET_PRIVATE_OTP =                        0x00030081,
+       RPI_FIRMWARE_SET_PRIVATE_OTP =                        0x00038081,
 
        /* Dispmanx TAGS */
        RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE =                   0x00040001,
@@ -174,6 +176,13 @@ struct rpi_firmware_clk_rate_request {
                .id = cpu_to_le32(_id),         \
        }
 
+struct rpi_otp_driver_data {
+       const char *name;
+       u32 read_tag;
+       u32 write_tag;
+       int size;
+};
+
 #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
 int rpi_firmware_property(struct rpi_firmware *fw,
                          u32 tag, void *data, size_t len);

-- 
2.47.3


Reply via email to