Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2009-2013 ADVANSEE * Benoît Thébaudeau <benoit.thebaudeau@advansee.com> * * Based on the mpc512x iim code: * Copyright 2008 Silicon Turnkey Express, Inc. * Martha Marx <mmarx@silicontkx.com> */ #include <fuse.h> #include <linux/delay.h> #include <linux/errno.h> #include <asm/io.h> #include <asm/arch/imx-regs.h> #if defined(CONFIG_MX51) || defined(CONFIG_MX53) #include <asm/arch/clock.h> #endif /* FSL IIM-specific constants */ #define STAT_BUSY 0x80 #define STAT_PRGD 0x02 #define STAT_SNSD 0x01 #define STATM_PRGD_M 0x02 #define STATM_SNSD_M 0x01 #define ERR_PRGE 0x80 #define ERR_WPE 0x40 #define ERR_OPE 0x20 #define ERR_RPE 0x10 #define ERR_WLRE 0x08 #define ERR_SNSE 0x04 #define ERR_PARITYE 0x02 #define EMASK_PRGE_M 0x80 #define EMASK_WPE_M 0x40 #define EMASK_OPE_M 0x20 #define EMASK_RPE_M 0x10 #define EMASK_WLRE_M 0x08 #define EMASK_SNSE_M 0x04 #define EMASK_PARITYE_M 0x02 #define FCTL_DPC 0x80 #define FCTL_PRG_LENGTH_MASK 0x70 #define FCTL_ESNS_N 0x08 #define FCTL_ESNS_0 0x04 #define FCTL_ESNS_1 0x02 #define FCTL_PRG 0x01 #define UA_A_BANK_MASK 0x38 #define UA_A_ROWH_MASK 0x07 #define LA_A_ROWL_MASK 0xf8 #define LA_A_BIT_MASK 0x07 #define PREV_PROD_REV_MASK 0xf8 #define PREV_PROD_VT_MASK 0x07 /* Select the correct accessors depending on endianness */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define iim_read32 in_le32 #define iim_write32 out_le32 #define iim_clrsetbits32 clrsetbits_le32 #define iim_clrbits32 clrbits_le32 #define iim_setbits32 setbits_le32 #elif __BYTE_ORDER == __BIG_ENDIAN #define iim_read32 in_be32 #define iim_write32 out_be32 #define iim_clrsetbits32 clrsetbits_be32 #define iim_clrbits32 clrbits_be32 #define iim_setbits32 setbits_be32 #else #error Endianess is not defined: please fix to continue #endif /* IIM control registers */ struct fsl_iim { u32 stat; u32 statm; u32 err; u32 emask; u32 fctl; u32 ua; u32 la; u32 sdat; u32 prev; u32 srev; u32 prg_p; u32 scs[0x1f5]; struct { u32 word[0x100]; } bank[8]; }; #if !defined(CONFIG_MX51) && !defined(CONFIG_MX53) #define enable_efuse_prog_supply(enable) #endif static int prepare_access(struct fsl_iim **regs, u32 bank, u32 word, int assert, const char *caller) { *regs = (struct fsl_iim *)IIM_BASE_ADDR; if (bank >= ARRAY_SIZE((*regs)->bank) || word >= ARRAY_SIZE((*regs)->bank[0].word) || !assert) { printf("fsl_iim %s(): Invalid argument\n", caller); return -EINVAL; } return 0; } static void clear_status(struct fsl_iim *regs) { iim_setbits32(®s->stat, 0); iim_setbits32(®s->err, 0); } static void finish_access(struct fsl_iim *regs, u32 *stat, u32 *err) { *stat = iim_read32(®s->stat); *err = iim_read32(®s->err); clear_status(regs); } static int prepare_read(struct fsl_iim **regs, u32 bank, u32 word, u32 *val, const char *caller) { int ret; ret = prepare_access(regs, bank, word, val != NULL, caller); if (ret) return ret; clear_status(*regs); return 0; } int fuse_read(u32 bank, u32 word, u32 *val) { struct fsl_iim *regs; u32 stat, err; int ret; ret = prepare_read(®s, bank, word, val, __func__); if (ret) return ret; *val = iim_read32(®s->bank[bank].word[word]); finish_access(regs, &stat, &err); if (err & ERR_RPE) { puts("fsl_iim fuse_read(): Read protect error\n"); return -EIO; } return 0; } static void direct_access(struct fsl_iim *regs, u32 bank, u32 word, u32 bit, u32 fctl, u32 *stat, u32 *err) { iim_write32(®s->ua, bank << 3 | word >> 5); iim_write32(®s->la, (word << 3 | bit) & 0xff); if (fctl == FCTL_PRG) iim_write32(®s->prg_p, 0xaa); iim_setbits32(®s->fctl, fctl); while (iim_read32(®s->stat) & STAT_BUSY) udelay(20); finish_access(regs, stat, err); } int fuse_sense(u32 bank, u32 word, u32 *val) { struct fsl_iim *regs; u32 stat, err; int ret; ret = prepare_read(®s, bank, word, val, __func__); if (ret) return ret; direct_access(regs, bank, word, 0, FCTL_ESNS_N, &stat, &err); if (err & ERR_SNSE) { puts("fsl_iim fuse_sense(): Explicit sense cycle error\n"); return -EIO; } if (!(stat & STAT_SNSD)) { puts("fsl_iim fuse_sense(): Explicit sense cycle did not complete\n"); return -EIO; } *val = iim_read32(®s->sdat); return 0; } static int prog_bit(struct fsl_iim *regs, u32 bank, u32 word, u32 bit) { u32 stat, err; clear_status(regs); direct_access(regs, bank, word, bit, FCTL_PRG, &stat, &err); iim_write32(®s->prg_p, 0x00); if (err & ERR_PRGE) { puts("fsl_iim fuse_prog(): Program error\n"); return -EIO; } if (err & ERR_WPE) { puts("fsl_iim fuse_prog(): Write protect error\n"); return -EIO; } if (!(stat & STAT_PRGD)) { puts("fsl_iim fuse_prog(): Program did not complete\n"); return -EIO; } return 0; } static int prepare_write(struct fsl_iim **regs, u32 bank, u32 word, u32 val, const char *caller) { return prepare_access(regs, bank, word, !(val & ~0xff), caller); } int fuse_prog(u32 bank, u32 word, u32 val) { struct fsl_iim *regs; u32 bit; int ret; ret = prepare_write(®s, bank, word, val, __func__); if (ret) return ret; enable_efuse_prog_supply(1); for (bit = 0; val; bit++, val >>= 1) if (val & 0x01) { ret = prog_bit(regs, bank, word, bit); if (ret) { enable_efuse_prog_supply(0); return ret; } } enable_efuse_prog_supply(0); return 0; } int fuse_override(u32 bank, u32 word, u32 val) { struct fsl_iim *regs; u32 stat, err; int ret; ret = prepare_write(®s, bank, word, val, __func__); if (ret) return ret; clear_status(regs); iim_write32(®s->bank[bank].word[word], val); finish_access(regs, &stat, &err); if (err & ERR_OPE) { puts("fsl_iim fuse_override(): Override protect error\n"); return -EIO; } return 0; } |