uboot/arch/arm/mach-mvebu/armada3700/efuse.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) 2017 Marvell International Ltd.
   4 * (C) 2021 Pali Rohár <pali@kernel.org>
   5 */
   6
   7#include <config.h>
   8#include <common.h>
   9#include <asm/io.h>
  10#include <linux/delay.h>
  11#include <mach/mbox.h>
  12#include <mach/soc.h>
  13
  14#define OTP_NB_REG_BASE         ((void __iomem *)MVEBU_REGISTER(0x12600))
  15#define OTP_SB_REG_BASE         ((void __iomem *)MVEBU_REGISTER(0x1A200))
  16
  17#define OTP_CONTROL_OFF         0x00
  18#define   OTP_MODE_BIT          BIT(15)
  19#define   OTP_RPTR_RST_BIT      BIT(14)
  20#define   OTP_POR_B_BIT         BIT(13)
  21#define   OTP_PRDT_BIT          BIT(3)
  22#define OTP_READ_PORT_OFF       0x04
  23#define OTP_READ_POINTER_OFF    0x08
  24#define   OTP_PTR_INC_BIT       BIT(8)
  25
  26static void otp_read_parallel(void __iomem *base, u32 *data, u32 count)
  27{
  28        u32 regval;
  29
  30        /* 1. Clear OTP_MODE_NB to parallel mode */
  31        regval = readl(base + OTP_CONTROL_OFF);
  32        regval &= ~OTP_MODE_BIT;
  33        writel(regval, base + OTP_CONTROL_OFF);
  34
  35        /* 2. Set OTP_POR_B_NB enter normal operation */
  36        regval = readl(base + OTP_CONTROL_OFF);
  37        regval |= OTP_POR_B_BIT;
  38        writel(regval, base + OTP_CONTROL_OFF);
  39
  40        /* 3. Set OTP_PTR_INC_NB to auto-increment pointer after each read */
  41        regval = readl(base + OTP_READ_POINTER_OFF);
  42        regval |= OTP_PTR_INC_BIT;
  43        writel(regval, base + OTP_READ_POINTER_OFF);
  44
  45        /* 4. Set OTP_RPTR_RST_NB, then clear the same field */
  46        regval = readl(base + OTP_CONTROL_OFF);
  47        regval |= OTP_RPTR_RST_BIT;
  48        writel(regval, base + OTP_CONTROL_OFF);
  49
  50        regval = readl(base + OTP_CONTROL_OFF);
  51        regval &= ~OTP_RPTR_RST_BIT;
  52        writel(regval, base + OTP_CONTROL_OFF);
  53
  54        /* 5. Toggle OTP_PRDT_NB
  55         * a. Set OTP_PRDT_NB to 1.
  56         * b. Clear OTP_PRDT_NB to 0.
  57         * c. Wait for a minimum of 100 ns.
  58         * d. Set OTP_PRDT_NB to 1
  59         */
  60        regval = readl(base + OTP_CONTROL_OFF);
  61        regval |= OTP_PRDT_BIT;
  62        writel(regval, base + OTP_CONTROL_OFF);
  63
  64        regval = readl(base + OTP_CONTROL_OFF);
  65        regval &= ~OTP_PRDT_BIT;
  66        writel(regval, base + OTP_CONTROL_OFF);
  67
  68        ndelay(100);
  69
  70        regval = readl(base + OTP_CONTROL_OFF);
  71        regval |= OTP_PRDT_BIT;
  72        writel(regval, base + OTP_CONTROL_OFF);
  73
  74        while (count-- > 0) {
  75                /* 6. Read the content of OTP 32-bits at a time */
  76                ndelay(100000);
  77                *(data++) = readl(base + OTP_READ_PORT_OFF);
  78        }
  79}
  80
  81static int rwtm_otp_read(u8 row, u32 word, u32 *data)
  82{
  83        u32 out[3];
  84        u32 in[2];
  85        int res = -EINVAL;
  86
  87        if (word < 2) {
  88                /*
  89                 * MBOX_CMD_OTP_READ_32B command is supported by Marvell
  90                 * fuse.bin firmware and also by new CZ.NIC wtmi firmware.
  91                 * This command returns raw bits without ECC corrections.
  92                 * It does not provide access to the lock bit.
  93                 */
  94                in[0] = row;
  95                in[1] = word * 32;
  96                res = mbox_do_cmd(MBOX_CMD_OTP_READ_32B, in, 2, out, 1);
  97                if (!res)
  98                        *data = out[0];
  99        } else if (word == 2) {
 100                /*
 101                 * MBOX_CMD_OTP_READ command is supported only by new CZ.NIC
 102                 * wtmi firmware and provides access to all bits, including
 103                 * lock bit without doing ECC corrections. For compatibility
 104                 * with Marvell fuse.bin firmware, use this command only for
 105                 * accessing lock bit.
 106                 */
 107                in[0] = row;
 108                res = mbox_do_cmd(MBOX_CMD_OTP_READ, in, 1, out, 3);
 109                if (!res)
 110                        *data = out[2];
 111        }
 112
 113        return res;
 114}
 115
 116static int rwtm_otp_write(u8 row, u32 word, u32 data)
 117{
 118        u32 in[4];
 119        int res = -EINVAL;
 120
 121        if (word < 2) {
 122                /*
 123                 * MBOX_CMD_OTP_WRITE_32B command is supported by Marvell
 124                 * fuse.bin firmware and also by new CZ.NIC wtmi firmware.
 125                 * This command writes only selected bits to OTP and does
 126                 * not calculate ECC bits. It does not allow to write the
 127                 * lock bit.
 128                 */
 129                in[0] = row;
 130                in[1] = word * 32;
 131                in[2] = data;
 132                res = mbox_do_cmd(MBOX_CMD_OTP_WRITE_32B, in, 3, NULL, 0);
 133        } else if (word == 2 && !(data & ~0x1)) {
 134                /*
 135                 * MBOX_CMD_OTP_WRITE command is supported only by new CZ.NIC
 136                 * wtmi firmware and allows to write any bit to OTP, including
 137                 * the lock bit. It does not calculate or write ECC bits too.
 138                 * For compatibility with Marvell fuse.bin firmware, use this
 139                 * command only for writing the lock bit.
 140                 */
 141                in[0] = row;
 142                in[1] = 0;
 143                in[2] = 0;
 144                in[3] = data;
 145                res = mbox_do_cmd(MBOX_CMD_OTP_WRITE, in, 4, NULL, 0);
 146        }
 147
 148        return res;
 149}
 150
 151/*
 152 * Banks 0-43 are used for accessing Security OTP (44 rows with 67 bits via 44 banks and words 0-2)
 153 * Bank 44 is used for accessing North Bridge OTP (69 bits via words 0-2)
 154 * Bank 45 is used for accessing South Bridge OTP (97 bits via words 0-3)
 155 */
 156
 157#define RWTM_ROWS       44
 158#define RWTM_MAX_BANK   (RWTM_ROWS - 1)
 159#define RWTM_ROW_WORDS  3
 160#define OTP_NB_BANK     RWTM_ROWS
 161#define OTP_NB_WORDS    3
 162#define OTP_SB_BANK     (RWTM_ROWS + 1)
 163#define OTP_SB_WORDS    4
 164
 165int fuse_read(u32 bank, u32 word, u32 *val)
 166{
 167        if (bank <= RWTM_MAX_BANK) {
 168                if (word >= RWTM_ROW_WORDS)
 169                        return -EINVAL;
 170                return rwtm_otp_read(bank, word, val);
 171        } else if (bank == OTP_NB_BANK) {
 172                u32 data[OTP_NB_WORDS];
 173                if (word >= OTP_NB_WORDS)
 174                        return -EINVAL;
 175                otp_read_parallel(OTP_NB_REG_BASE, data, OTP_NB_WORDS);
 176                *val = data[word];
 177                return 0;
 178        } else if (bank == OTP_SB_BANK) {
 179                u32 data[OTP_SB_WORDS];
 180                if (word >= OTP_SB_WORDS)
 181                        return -EINVAL;
 182                otp_read_parallel(OTP_SB_REG_BASE, data, OTP_SB_WORDS);
 183                *val = data[word];
 184                return 0;
 185        } else {
 186                return -EINVAL;
 187        }
 188}
 189
 190int fuse_prog(u32 bank, u32 word, u32 val)
 191{
 192        if (bank <= RWTM_MAX_BANK) {
 193                if (word >= RWTM_ROW_WORDS)
 194                        return -EINVAL;
 195                return rwtm_otp_write(bank, word, val);
 196        } else if (bank == OTP_NB_BANK) {
 197                /* TODO: not implemented yet */
 198                return -ENOSYS;
 199        } else if (bank == OTP_SB_BANK) {
 200                /* TODO: not implemented yet */
 201                return -ENOSYS;
 202        } else {
 203                return -EINVAL;
 204        }
 205}
 206
 207int fuse_sense(u32 bank, u32 word, u32 *val)
 208{
 209        /* not supported */
 210        return -ENOSYS;
 211}
 212
 213int fuse_override(u32 bank, u32 word, u32 val)
 214{
 215        /* not supported */
 216        return -ENOSYS;
 217}
 218