uboot/drivers/misc/mxc_ocotp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2013 ADVANSEE
   4 * Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
   5 *
   6 * Based on Dirk Behme's
   7 * https://github.com/dirkbehme/u-boot-imx6/blob/28b17e9/drivers/misc/imx_otp.c,
   8 * which is based on Freescale's
   9 * http://git.freescale.com/git/cgit.cgi/imx/uboot-imx.git/tree/drivers/misc/imx_otp.c?h=imx_v2009.08_1.1.0&id=9aa74e6,
  10 * which is:
  11 * Copyright (C) 2011 Freescale Semiconductor, Inc.
  12 */
  13
  14#include <common.h>
  15#include <fuse.h>
  16#include <linux/errno.h>
  17#include <asm/io.h>
  18#include <asm/arch/clock.h>
  19#include <asm/arch/imx-regs.h>
  20#include <asm/mach-imx/sys_proto.h>
  21
  22#define BO_CTRL_WR_UNLOCK               16
  23#define BM_CTRL_WR_UNLOCK               0xffff0000
  24#define BV_CTRL_WR_UNLOCK_KEY           0x3e77
  25#define BM_CTRL_ERROR                   0x00000200
  26#define BM_CTRL_BUSY                    0x00000100
  27#define BO_CTRL_ADDR                    0
  28#ifdef CONFIG_MX7
  29#define BM_CTRL_ADDR                    0x0000000f
  30#define BM_CTRL_RELOAD                  0x00000400
  31#elif defined(CONFIG_MX7ULP)
  32#define BM_CTRL_ADDR                    0x000000FF
  33#define BM_CTRL_RELOAD                  0x00000400
  34#define BM_OUT_STATUS_DED                               0x00000400
  35#define BM_OUT_STATUS_LOCKED                    0x00000800
  36#define BM_OUT_STATUS_PROGFAIL                  0x00001000
  37#elif defined(CONFIG_IMX8M)
  38#define BM_CTRL_ADDR                    0x000000ff
  39#else
  40#define BM_CTRL_ADDR                    0x0000007f
  41#endif
  42
  43#ifdef CONFIG_MX7
  44#define BO_TIMING_FSOURCE               12
  45#define BM_TIMING_FSOURCE               0x0007f000
  46#define BV_TIMING_FSOURCE_NS            1001
  47#define BO_TIMING_PROG                  0
  48#define BM_TIMING_PROG                  0x00000fff
  49#define BV_TIMING_PROG_US               10
  50#else
  51#define BO_TIMING_STROBE_READ           16
  52#define BM_TIMING_STROBE_READ           0x003f0000
  53#define BV_TIMING_STROBE_READ_NS        37
  54#define BO_TIMING_RELAX                 12
  55#define BM_TIMING_RELAX                 0x0000f000
  56#define BV_TIMING_RELAX_NS              17
  57#define BO_TIMING_STROBE_PROG           0
  58#define BM_TIMING_STROBE_PROG           0x00000fff
  59#define BV_TIMING_STROBE_PROG_US        10
  60#endif
  61
  62#define BM_READ_CTRL_READ_FUSE          0x00000001
  63
  64#define BF(value, field)                (((value) << BO_##field) & BM_##field)
  65
  66#define WRITE_POSTAMBLE_US              2
  67
  68#if defined(CONFIG_MX6) || defined(CONFIG_VF610)
  69#define FUSE_BANK_SIZE  0x80
  70#ifdef CONFIG_MX6SL
  71#define FUSE_BANKS      8
  72#elif defined(CONFIG_MX6ULL) || defined(CONFIG_MX6SLL)
  73#define FUSE_BANKS      9
  74#else
  75#define FUSE_BANKS      16
  76#endif
  77#elif defined CONFIG_MX7
  78#define FUSE_BANK_SIZE  0x40
  79#define FUSE_BANKS      16
  80#elif defined(CONFIG_MX7ULP)
  81#define FUSE_BANK_SIZE  0x80
  82#define FUSE_BANKS      31
  83#elif defined(CONFIG_IMX8M)
  84#define FUSE_BANK_SIZE  0x40
  85#define FUSE_BANKS      64
  86#else
  87#error "Unsupported architecture\n"
  88#endif
  89
  90#if defined(CONFIG_MX6)
  91
  92/*
  93 * There is a hole in shadow registers address map of size 0x100
  94 * between bank 5 and bank 6 on iMX6QP, iMX6DQ, iMX6SDL, iMX6SX,
  95 * iMX6UL, i.MX6ULL and i.MX6SLL.
  96 * Bank 5 ends at 0x6F0 and Bank 6 starts at 0x800. When reading the fuses,
  97 * we should account for this hole in address space.
  98 *
  99 * Similar hole exists between bank 14 and bank 15 of size
 100 * 0x80 on iMX6QP, iMX6DQ, iMX6SDL and iMX6SX.
 101 * Note: iMX6SL has only 0-7 banks and there is no hole.
 102 * Note: iMX6UL doesn't have this one.
 103 *
 104 * This function is to covert user input to physical bank index.
 105 * Only needed when read fuse, because we use register offset, so
 106 * need to calculate real register offset.
 107 * When write, no need to consider hole, always use the bank/word
 108 * index from fuse map.
 109 */
 110u32 fuse_bank_physical(int index)
 111{
 112        u32 phy_index;
 113
 114        if (is_mx6sl() || is_mx7ulp()) {
 115                phy_index = index;
 116        } else if (is_mx6ul() || is_mx6ull() || is_mx6sll()) {
 117                if ((is_mx6ull() || is_mx6sll()) && index == 8)
 118                        index = 7;
 119
 120                if (index >= 6)
 121                        phy_index = fuse_bank_physical(5) + (index - 6) + 3;
 122                else
 123                        phy_index = index;
 124        } else {
 125                if (index >= 15)
 126                        phy_index = fuse_bank_physical(14) + (index - 15) + 2;
 127                else if (index >= 6)
 128                        phy_index = fuse_bank_physical(5) + (index - 6) + 3;
 129                else
 130                        phy_index = index;
 131        }
 132        return phy_index;
 133}
 134
 135u32 fuse_word_physical(u32 bank, u32 word_index)
 136{
 137        if (is_mx6ull() || is_mx6sll()) {
 138                if (bank == 8)
 139                        word_index = word_index + 4;
 140        }
 141
 142        return word_index;
 143}
 144#else
 145u32 fuse_bank_physical(int index)
 146{
 147        return index;
 148}
 149
 150u32 fuse_word_physical(u32 bank, u32 word_index)
 151{
 152        return word_index;
 153}
 154
 155#endif
 156
 157static void wait_busy(struct ocotp_regs *regs, unsigned int delay_us)
 158{
 159        while (readl(&regs->ctrl) & BM_CTRL_BUSY)
 160                udelay(delay_us);
 161}
 162
 163static void clear_error(struct ocotp_regs *regs)
 164{
 165        writel(BM_CTRL_ERROR, &regs->ctrl_clr);
 166}
 167
 168static int prepare_access(struct ocotp_regs **regs, u32 bank, u32 word,
 169                                int assert, const char *caller)
 170{
 171        *regs = (struct ocotp_regs *)OCOTP_BASE_ADDR;
 172
 173        if (bank >= FUSE_BANKS ||
 174            word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 2 ||
 175            !assert) {
 176                printf("mxc_ocotp %s(): Invalid argument\n", caller);
 177                return -EINVAL;
 178        }
 179
 180        if (is_mx6ull() || is_mx6sll()) {
 181                if ((bank == 7 || bank == 8) &&
 182                    word >= ARRAY_SIZE((*regs)->bank[0].fuse_regs) >> 3) {
 183                        printf("mxc_ocotp %s(): Invalid argument\n", caller);
 184                        return -EINVAL;
 185                }
 186        }
 187
 188        enable_ocotp_clk(1);
 189
 190        wait_busy(*regs, 1);
 191        clear_error(*regs);
 192
 193        return 0;
 194}
 195
 196static int finish_access(struct ocotp_regs *regs, const char *caller)
 197{
 198        u32 err;
 199
 200        err = !!(readl(&regs->ctrl) & BM_CTRL_ERROR);
 201        clear_error(regs);
 202
 203#ifdef CONFIG_MX7ULP
 204        /* Need to power down the OTP memory */
 205        writel(1, &regs->pdn);
 206#endif
 207        if (err) {
 208                printf("mxc_ocotp %s(): Access protect error\n", caller);
 209                return -EIO;
 210        }
 211
 212        return 0;
 213}
 214
 215static int prepare_read(struct ocotp_regs **regs, u32 bank, u32 word, u32 *val,
 216                        const char *caller)
 217{
 218        return prepare_access(regs, bank, word, val != NULL, caller);
 219}
 220
 221int fuse_read(u32 bank, u32 word, u32 *val)
 222{
 223        struct ocotp_regs *regs;
 224        int ret;
 225        u32 phy_bank;
 226        u32 phy_word;
 227
 228        ret = prepare_read(&regs, bank, word, val, __func__);
 229        if (ret)
 230                return ret;
 231
 232        phy_bank = fuse_bank_physical(bank);
 233        phy_word = fuse_word_physical(bank, word);
 234
 235        *val = readl(&regs->bank[phy_bank].fuse_regs[phy_word << 2]);
 236
 237#ifdef CONFIG_MX7ULP
 238        if (readl(&regs->out_status) & BM_OUT_STATUS_DED) {
 239                writel(BM_OUT_STATUS_DED, &regs->out_status_clr);
 240                printf("mxc_ocotp %s(): fuse read wrong\n", __func__);
 241                return -EIO;
 242        }
 243#endif
 244        return finish_access(regs, __func__);
 245}
 246
 247#ifdef CONFIG_MX7
 248static void set_timing(struct ocotp_regs *regs)
 249{
 250        u32 ipg_clk;
 251        u32 fsource, prog;
 252        u32 timing;
 253
 254        ipg_clk = mxc_get_clock(MXC_IPG_CLK);
 255
 256        fsource = DIV_ROUND_UP((ipg_clk / 1000) * BV_TIMING_FSOURCE_NS,
 257                        +       1000000) + 1;
 258        prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_PROG_US, 1000000) + 1;
 259
 260        timing = BF(fsource, TIMING_FSOURCE) | BF(prog, TIMING_PROG);
 261
 262        clrsetbits_le32(&regs->timing, BM_TIMING_FSOURCE | BM_TIMING_PROG,
 263                        timing);
 264}
 265#elif defined(CONFIG_MX7ULP)
 266static void set_timing(struct ocotp_regs *regs)
 267{
 268        /* No timing set for MX7ULP */
 269}
 270
 271#else
 272static void set_timing(struct ocotp_regs *regs)
 273{
 274        u32 ipg_clk;
 275        u32 relax, strobe_read, strobe_prog;
 276        u32 timing;
 277
 278        ipg_clk = mxc_get_clock(MXC_IPG_CLK);
 279
 280        relax = DIV_ROUND_UP(ipg_clk * BV_TIMING_RELAX_NS, 1000000000) - 1;
 281        strobe_read = DIV_ROUND_UP(ipg_clk * BV_TIMING_STROBE_READ_NS,
 282                                        1000000000) + 2 * (relax + 1) - 1;
 283        strobe_prog = DIV_ROUND_CLOSEST(ipg_clk * BV_TIMING_STROBE_PROG_US,
 284                                                1000000) + 2 * (relax + 1) - 1;
 285
 286        timing = BF(strobe_read, TIMING_STROBE_READ) |
 287                        BF(relax, TIMING_RELAX) |
 288                        BF(strobe_prog, TIMING_STROBE_PROG);
 289
 290        clrsetbits_le32(&regs->timing, BM_TIMING_STROBE_READ | BM_TIMING_RELAX |
 291                        BM_TIMING_STROBE_PROG, timing);
 292}
 293#endif
 294
 295static void setup_direct_access(struct ocotp_regs *regs, u32 bank, u32 word,
 296                                int write)
 297{
 298        u32 wr_unlock = write ? BV_CTRL_WR_UNLOCK_KEY : 0;
 299#ifdef CONFIG_MX7
 300        u32 addr = bank;
 301#elif defined CONFIG_IMX8M
 302        u32 addr = bank << 2 | word;
 303#else
 304        u32 addr;
 305        /* Bank 7 and Bank 8 only supports 4 words each for i.MX6ULL */
 306        if ((is_mx6ull() || is_mx6sll()) && (bank > 7)) {
 307                bank = bank - 1;
 308                word += 4;
 309        }
 310        addr = bank << 3 | word;
 311#endif
 312
 313        set_timing(regs);
 314        clrsetbits_le32(&regs->ctrl, BM_CTRL_WR_UNLOCK | BM_CTRL_ADDR,
 315                        BF(wr_unlock, CTRL_WR_UNLOCK) |
 316                        BF(addr, CTRL_ADDR));
 317}
 318
 319int fuse_sense(u32 bank, u32 word, u32 *val)
 320{
 321        struct ocotp_regs *regs;
 322        int ret;
 323
 324        if (is_imx8mq() && is_soc_rev(CHIP_REV_2_1)) {
 325                printf("mxc_ocotp %s(): fuse sense is disabled\n", __func__);
 326                return -EPERM;
 327        }
 328
 329        ret = prepare_read(&regs, bank, word, val, __func__);
 330        if (ret)
 331                return ret;
 332
 333        setup_direct_access(regs, bank, word, false);
 334        writel(BM_READ_CTRL_READ_FUSE, &regs->read_ctrl);
 335        wait_busy(regs, 1);
 336#ifdef CONFIG_MX7
 337        *val = readl((&regs->read_fuse_data0) + (word << 2));
 338#else
 339        *val = readl(&regs->read_fuse_data);
 340#endif
 341
 342#ifdef CONFIG_MX7ULP
 343        if (readl(&regs->out_status) & BM_OUT_STATUS_DED) {
 344                writel(BM_OUT_STATUS_DED, &regs->out_status_clr);
 345                printf("mxc_ocotp %s(): fuse read wrong\n", __func__);
 346                return -EIO;
 347        }
 348#endif
 349
 350        return finish_access(regs, __func__);
 351}
 352
 353static int prepare_write(struct ocotp_regs **regs, u32 bank, u32 word,
 354                                const char *caller)
 355{
 356#ifdef CONFIG_MX7ULP
 357        u32 val;
 358        int ret;
 359
 360        /* Only bank 0 and 1 are redundancy mode, others are ECC mode */
 361        if (bank != 0 && bank != 1) {
 362                if ((soc_rev() < CHIP_REV_2_0) ||
 363                    ((soc_rev() >= CHIP_REV_2_0) &&
 364                    bank != 9 && bank != 10 && bank != 28)) {
 365                        ret = fuse_sense(bank, word, &val);
 366                        if (ret)
 367                                return ret;
 368
 369                        if (val != 0) {
 370                                printf("mxc_ocotp: The word has been programmed, no more write\n");
 371                                return -EPERM;
 372                        }
 373                }
 374        }
 375#endif
 376
 377        return prepare_access(regs, bank, word, true, caller);
 378}
 379
 380int fuse_prog(u32 bank, u32 word, u32 val)
 381{
 382        struct ocotp_regs *regs;
 383        int ret;
 384
 385        ret = prepare_write(&regs, bank, word, __func__);
 386        if (ret)
 387                return ret;
 388
 389        setup_direct_access(regs, bank, word, true);
 390#ifdef CONFIG_MX7
 391        switch (word) {
 392        case 0:
 393                writel(0, &regs->data1);
 394                writel(0, &regs->data2);
 395                writel(0, &regs->data3);
 396                writel(val, &regs->data0);
 397                break;
 398        case 1:
 399                writel(val, &regs->data1);
 400                writel(0, &regs->data2);
 401                writel(0, &regs->data3);
 402                writel(0, &regs->data0);
 403                break;
 404        case 2:
 405                writel(0, &regs->data1);
 406                writel(val, &regs->data2);
 407                writel(0, &regs->data3);
 408                writel(0, &regs->data0);
 409                break;
 410        case 3:
 411                writel(0, &regs->data1);
 412                writel(0, &regs->data2);
 413                writel(val, &regs->data3);
 414                writel(0, &regs->data0);
 415                break;
 416        }
 417        wait_busy(regs, BV_TIMING_PROG_US);
 418#else
 419        writel(val, &regs->data);
 420        wait_busy(regs, BV_TIMING_STROBE_PROG_US);
 421#endif
 422        udelay(WRITE_POSTAMBLE_US);
 423
 424#ifdef CONFIG_MX7ULP
 425        if (readl(&regs->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) {
 426                writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), &regs->out_status_clr);
 427                printf("mxc_ocotp %s(): fuse write is failed\n", __func__);
 428                return -EIO;
 429        }
 430#endif
 431
 432        return finish_access(regs, __func__);
 433}
 434
 435int fuse_override(u32 bank, u32 word, u32 val)
 436{
 437        struct ocotp_regs *regs;
 438        int ret;
 439        u32 phy_bank;
 440        u32 phy_word;
 441
 442        ret = prepare_write(&regs, bank, word, __func__);
 443        if (ret)
 444                return ret;
 445
 446        phy_bank = fuse_bank_physical(bank);
 447        phy_word = fuse_word_physical(bank, word);
 448
 449        writel(val, &regs->bank[phy_bank].fuse_regs[phy_word << 2]);
 450
 451#ifdef CONFIG_MX7ULP
 452        if (readl(&regs->out_status) & (BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED)) {
 453                writel((BM_OUT_STATUS_PROGFAIL | BM_OUT_STATUS_LOCKED), &regs->out_status_clr);
 454                printf("mxc_ocotp %s(): fuse write is failed\n", __func__);
 455                return -EIO;
 456        }
 457#endif
 458
 459        return finish_access(regs, __func__);
 460}
 461