linux/drivers/nvmem/lpc18xx_otp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * NXP LPC18xx/43xx OTP memory NVMEM driver
   4 *
   5 * Copyright (c) 2016 Joachim Eastwood <manabian@gmail.com>
   6 *
   7 * Based on the imx ocotp driver,
   8 * Copyright (c) 2015 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de>
   9 *
  10 * TODO: add support for writing OTP register via API in boot ROM.
  11 */
  12
  13#include <linux/io.h>
  14#include <linux/module.h>
  15#include <linux/nvmem-provider.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/platform_device.h>
  19#include <linux/slab.h>
  20
  21/*
  22 * LPC18xx OTP memory contains 4 banks with 4 32-bit words. Bank 0 starts
  23 * at offset 0 from the base.
  24 *
  25 * Bank 0 contains the part ID for Flashless devices and is reseverd for
  26 * devices with Flash.
  27 * Bank 1/2 is generale purpose or AES key storage for secure devices.
  28 * Bank 3 contains control data, USB ID and generale purpose words.
  29 */
  30#define LPC18XX_OTP_NUM_BANKS           4
  31#define LPC18XX_OTP_WORDS_PER_BANK      4
  32#define LPC18XX_OTP_WORD_SIZE           sizeof(u32)
  33#define LPC18XX_OTP_SIZE                (LPC18XX_OTP_NUM_BANKS * \
  34                                         LPC18XX_OTP_WORDS_PER_BANK * \
  35                                         LPC18XX_OTP_WORD_SIZE)
  36
  37struct lpc18xx_otp {
  38        void __iomem *base;
  39};
  40
  41static int lpc18xx_otp_read(void *context, unsigned int offset,
  42                            void *val, size_t bytes)
  43{
  44        struct lpc18xx_otp *otp = context;
  45        unsigned int count = bytes >> 2;
  46        u32 index = offset >> 2;
  47        u32 *buf = val;
  48        int i;
  49
  50        if (count > (LPC18XX_OTP_SIZE - index))
  51                count = LPC18XX_OTP_SIZE - index;
  52
  53        for (i = index; i < (index + count); i++)
  54                *buf++ = readl(otp->base + i * LPC18XX_OTP_WORD_SIZE);
  55
  56        return 0;
  57}
  58
  59static struct nvmem_config lpc18xx_otp_nvmem_config = {
  60        .name = "lpc18xx-otp",
  61        .read_only = true,
  62        .word_size = LPC18XX_OTP_WORD_SIZE,
  63        .stride = LPC18XX_OTP_WORD_SIZE,
  64        .reg_read = lpc18xx_otp_read,
  65};
  66
  67static int lpc18xx_otp_probe(struct platform_device *pdev)
  68{
  69        struct nvmem_device *nvmem;
  70        struct lpc18xx_otp *otp;
  71        struct resource *res;
  72
  73        otp = devm_kzalloc(&pdev->dev, sizeof(*otp), GFP_KERNEL);
  74        if (!otp)
  75                return -ENOMEM;
  76
  77        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  78        otp->base = devm_ioremap_resource(&pdev->dev, res);
  79        if (IS_ERR(otp->base))
  80                return PTR_ERR(otp->base);
  81
  82        lpc18xx_otp_nvmem_config.size = LPC18XX_OTP_SIZE;
  83        lpc18xx_otp_nvmem_config.dev = &pdev->dev;
  84        lpc18xx_otp_nvmem_config.priv = otp;
  85
  86        nvmem = devm_nvmem_register(&pdev->dev, &lpc18xx_otp_nvmem_config);
  87
  88        return PTR_ERR_OR_ZERO(nvmem);
  89}
  90
  91static const struct of_device_id lpc18xx_otp_dt_ids[] = {
  92        { .compatible = "nxp,lpc1850-otp" },
  93        { },
  94};
  95MODULE_DEVICE_TABLE(of, lpc18xx_otp_dt_ids);
  96
  97static struct platform_driver lpc18xx_otp_driver = {
  98        .probe  = lpc18xx_otp_probe,
  99        .driver = {
 100                .name   = "lpc18xx_otp",
 101                .of_match_table = lpc18xx_otp_dt_ids,
 102        },
 103};
 104module_platform_driver(lpc18xx_otp_driver);
 105
 106MODULE_AUTHOR("Joachim Eastwoood <manabian@gmail.com>");
 107MODULE_DESCRIPTION("NXP LPC18xx OTP NVMEM driver");
 108MODULE_LICENSE("GPL v2");
 109