uboot/drivers/misc/sifive-otp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * This is a driver for the eMemory EG004K32TQ028XW01 NeoFuse
   4 * One-Time-Programmable (OTP) memory used within the SiFive FU540.
   5 * It is documented in the FU540 manual here:
   6 * https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
   7 *
   8 * Copyright (C) 2018 Philipp Hug <philipp@hug.cx>
   9 * Copyright (C) 2018 Joey Hewitt <joey@joeyhewitt.com>
  10 *
  11 * Copyright (C) 2020 SiFive, Inc
  12 */
  13
  14/*
  15 * The FU540 stores 4096x32 bit (16KiB) values.
  16 * Index 0x00-0xff are reserved for SiFive internal use. (first 1KiB)
  17 * Right now first 1KiB is used to store only serial number.
  18 */
  19
  20#include <common.h>
  21#include <dm/device.h>
  22#include <dm/read.h>
  23#include <linux/bitops.h>
  24#include <linux/delay.h>
  25#include <linux/io.h>
  26#include <misc.h>
  27
  28#define BYTES_PER_FUSE          4
  29
  30#define PA_RESET_VAL            0x00
  31#define PAS_RESET_VAL           0x00
  32#define PAIO_RESET_VAL          0x00
  33#define PDIN_RESET_VAL          0x00
  34#define PTM_RESET_VAL           0x00
  35
  36#define PCLK_ENABLE_VAL                 BIT(0)
  37#define PCLK_DISABLE_VAL                0x00
  38
  39#define PWE_WRITE_ENABLE                BIT(0)
  40#define PWE_WRITE_DISABLE               0x00
  41
  42#define PTM_FUSE_PROGRAM_VAL            BIT(1)
  43
  44#define PCE_ENABLE_INPUT                BIT(0)
  45#define PCE_DISABLE_INPUT               0x00
  46
  47#define PPROG_ENABLE_INPUT              BIT(0)
  48#define PPROG_DISABLE_INPUT             0x00
  49
  50#define PTRIM_ENABLE_INPUT              BIT(0)
  51#define PTRIM_DISABLE_INPUT             0x00
  52
  53#define PDSTB_DEEP_STANDBY_ENABLE       BIT(0)
  54#define PDSTB_DEEP_STANDBY_DISABLE      0x00
  55
  56/* Tpw - Program Pulse width delay */
  57#define TPW_DELAY                       20
  58
  59/* Tpwi - Program Pulse interval delay */
  60#define TPWI_DELAY                      5
  61
  62/* Tasp - Program address setup delay */
  63#define TASP_DELAY                      1
  64
  65/* Tcd - read data access delay */
  66#define TCD_DELAY                       40
  67
  68/* Tkl - clok pulse low delay */
  69#define TKL_DELAY                       10
  70
  71/* Tms - PTM mode setup delay */
  72#define TMS_DELAY                       1
  73
  74struct sifive_otp_regs {
  75        u32 pa;     /* Address input */
  76        u32 paio;   /* Program address input */
  77        u32 pas;    /* Program redundancy cell selection input */
  78        u32 pce;    /* OTP Macro enable input */
  79        u32 pclk;   /* Clock input */
  80        u32 pdin;   /* Write data input */
  81        u32 pdout;  /* Read data output */
  82        u32 pdstb;  /* Deep standby mode enable input (active low) */
  83        u32 pprog;  /* Program mode enable input */
  84        u32 ptc;    /* Test column enable input */
  85        u32 ptm;    /* Test mode enable input */
  86        u32 ptm_rep;/* Repair function test mode enable input */
  87        u32 ptr;    /* Test row enable input */
  88        u32 ptrim;  /* Repair function enable input */
  89        u32 pwe;    /* Write enable input (defines program cycle) */
  90};
  91
  92struct sifive_otp_plat {
  93        struct sifive_otp_regs __iomem *regs;
  94        u32 total_fuses;
  95};
  96
  97/*
  98 * offset and size are assumed aligned to the size of the fuses (32-bit).
  99 */
 100static int sifive_otp_read(struct udevice *dev, int offset,
 101                           void *buf, int size)
 102{
 103        struct sifive_otp_plat *plat = dev_get_plat(dev);
 104        struct sifive_otp_regs *regs = (struct sifive_otp_regs *)plat->regs;
 105
 106        /* Check if offset and size are multiple of BYTES_PER_FUSE */
 107        if ((size % BYTES_PER_FUSE) || (offset % BYTES_PER_FUSE)) {
 108                printf("%s: size and offset must be multiple of 4.\n",
 109                       __func__);
 110                return -EINVAL;
 111        }
 112
 113        int fuseidx = offset / BYTES_PER_FUSE;
 114        int fusecount = size / BYTES_PER_FUSE;
 115
 116        /* check bounds */
 117        if (offset < 0 || size < 0)
 118                return -EINVAL;
 119        if (fuseidx >= plat->total_fuses)
 120                return -EINVAL;
 121        if ((fuseidx + fusecount) > plat->total_fuses)
 122                return -EINVAL;
 123
 124        u32 fusebuf[fusecount];
 125
 126        /* init OTP */
 127        writel(PDSTB_DEEP_STANDBY_ENABLE, &regs->pdstb);
 128        writel(PTRIM_ENABLE_INPUT, &regs->ptrim);
 129        writel(PCE_ENABLE_INPUT, &regs->pce);
 130
 131        /* read all requested fuses */
 132        for (unsigned int i = 0; i < fusecount; i++, fuseidx++) {
 133                writel(fuseidx, &regs->pa);
 134
 135                /* cycle clock to read */
 136                writel(PCLK_ENABLE_VAL, &regs->pclk);
 137                ndelay(TCD_DELAY * 1000);
 138                writel(PCLK_DISABLE_VAL, &regs->pclk);
 139                ndelay(TKL_DELAY * 1000);
 140
 141                /* read the value */
 142                fusebuf[i] = readl(&regs->pdout);
 143        }
 144
 145        /* shut down */
 146        writel(PCE_DISABLE_INPUT, &regs->pce);
 147        writel(PTRIM_DISABLE_INPUT, &regs->ptrim);
 148        writel(PDSTB_DEEP_STANDBY_DISABLE, &regs->pdstb);
 149
 150        /* copy out */
 151        memcpy(buf, fusebuf, size);
 152
 153        return size;
 154}
 155
 156/*
 157 * Caution:
 158 * OTP can be written only once, so use carefully.
 159 *
 160 * offset and size are assumed aligned to the size of the fuses (32-bit).
 161 */
 162static int sifive_otp_write(struct udevice *dev, int offset,
 163                            const void *buf, int size)
 164{
 165        struct sifive_otp_plat *plat = dev_get_plat(dev);
 166        struct sifive_otp_regs *regs = (struct sifive_otp_regs *)plat->regs;
 167
 168        /* Check if offset and size are multiple of BYTES_PER_FUSE */
 169        if ((size % BYTES_PER_FUSE) || (offset % BYTES_PER_FUSE)) {
 170                printf("%s: size and offset must be multiple of 4.\n",
 171                       __func__);
 172                return -EINVAL;
 173        }
 174
 175        int fuseidx = offset / BYTES_PER_FUSE;
 176        int fusecount = size / BYTES_PER_FUSE;
 177        u32 *write_buf = (u32 *)buf;
 178        u32 write_data;
 179        int i, pas, bit;
 180
 181        /* check bounds */
 182        if (offset < 0 || size < 0)
 183                return -EINVAL;
 184        if (fuseidx >= plat->total_fuses)
 185                return -EINVAL;
 186        if ((fuseidx + fusecount) > plat->total_fuses)
 187                return -EINVAL;
 188
 189        /* init OTP */
 190        writel(PDSTB_DEEP_STANDBY_ENABLE, &regs->pdstb);
 191        writel(PTRIM_ENABLE_INPUT, &regs->ptrim);
 192
 193        /* reset registers */
 194        writel(PCLK_DISABLE_VAL, &regs->pclk);
 195        writel(PA_RESET_VAL, &regs->pa);
 196        writel(PAS_RESET_VAL, &regs->pas);
 197        writel(PAIO_RESET_VAL, &regs->paio);
 198        writel(PDIN_RESET_VAL, &regs->pdin);
 199        writel(PWE_WRITE_DISABLE, &regs->pwe);
 200        writel(PTM_FUSE_PROGRAM_VAL, &regs->ptm);
 201        ndelay(TMS_DELAY * 1000);
 202
 203        writel(PCE_ENABLE_INPUT, &regs->pce);
 204        writel(PPROG_ENABLE_INPUT, &regs->pprog);
 205
 206        /* write all requested fuses */
 207        for (i = 0; i < fusecount; i++, fuseidx++) {
 208                writel(fuseidx, &regs->pa);
 209                write_data = *(write_buf++);
 210
 211                for (pas = 0; pas < 2; pas++) {
 212                        writel(pas, &regs->pas);
 213
 214                        for (bit = 0; bit < 32; bit++) {
 215                                writel(bit, &regs->paio);
 216                                writel(((write_data >> bit) & 1),
 217                                       &regs->pdin);
 218                                ndelay(TASP_DELAY * 1000);
 219
 220                                writel(PWE_WRITE_ENABLE, &regs->pwe);
 221                                udelay(TPW_DELAY);
 222                                writel(PWE_WRITE_DISABLE, &regs->pwe);
 223                                udelay(TPWI_DELAY);
 224                        }
 225                }
 226
 227                writel(PAS_RESET_VAL, &regs->pas);
 228        }
 229
 230        /* shut down */
 231        writel(PWE_WRITE_DISABLE, &regs->pwe);
 232        writel(PPROG_DISABLE_INPUT, &regs->pprog);
 233        writel(PCE_DISABLE_INPUT, &regs->pce);
 234        writel(PTM_RESET_VAL, &regs->ptm);
 235
 236        writel(PTRIM_DISABLE_INPUT, &regs->ptrim);
 237        writel(PDSTB_DEEP_STANDBY_DISABLE, &regs->pdstb);
 238
 239        return size;
 240}
 241
 242static int sifive_otp_of_to_plat(struct udevice *dev)
 243{
 244        struct sifive_otp_plat *plat = dev_get_plat(dev);
 245        int ret;
 246
 247        plat->regs = dev_read_addr_ptr(dev);
 248
 249        ret = dev_read_u32(dev, "fuse-count", &plat->total_fuses);
 250        if (ret < 0) {
 251                pr_err("\"fuse-count\" not found\n");
 252                return ret;
 253        }
 254
 255        return 0;
 256}
 257
 258static const struct misc_ops sifive_otp_ops = {
 259        .read = sifive_otp_read,
 260        .write = sifive_otp_write,
 261};
 262
 263static const struct udevice_id sifive_otp_ids[] = {
 264        { .compatible = "sifive,fu540-c000-otp" },
 265        {}
 266};
 267
 268U_BOOT_DRIVER(sifive_otp) = {
 269        .name = "sifive_otp",
 270        .id = UCLASS_MISC,
 271        .of_match = sifive_otp_ids,
 272        .of_to_plat = sifive_otp_of_to_plat,
 273        .plat_auto      = sizeof(struct sifive_otp_plat),
 274        .ops = &sifive_otp_ops,
 275};
 276