1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2015-2016 Reinhard Pfau <reinhard.pfau@gdsys.cc> 4 */ 5 6 #include <config.h> 7 #include <common.h> 8 #include <errno.h> 9 #include <asm/io.h> 10 #include <asm/arch/cpu.h> 11 #include <asm/arch/efuse.h> 12 #include <asm/arch/soc.h> 13 #include <linux/mbus.h> 14 15 #if defined(CONFIG_MVEBU_EFUSE_FAKE) 16 #define DRY_RUN 17 #else 18 #undef DRY_RUN 19 #endif 20 21 #define MBUS_EFUSE_BASE 0xF6000000 22 #define MBUS_EFUSE_SIZE BIT(20) 23 24 #define MVEBU_EFUSE_CONTROL (MVEBU_REGISTER(0xE4008)) 25 26 enum { 27 MVEBU_EFUSE_CTRL_PROGRAM_ENABLE = (1 << 31), 28 }; 29 30 struct mvebu_hd_efuse { 31 u32 bits_31_0; 32 u32 bits_63_32; 33 u32 bit64; 34 u32 reserved0; 35 }; 36 37 #ifndef DRY_RUN 38 static struct mvebu_hd_efuse *efuses = 39 (struct mvebu_hd_efuse *)(MBUS_EFUSE_BASE + 0xF9000); 40 #else 41 static struct mvebu_hd_efuse efuses[EFUSE_LINE_MAX + 1]; 42 #endif 43 44 static int efuse_initialised; 45 46 static struct mvebu_hd_efuse *get_efuse_line(int nr) 47 { 48 if (nr < 0 || nr > 63 || !efuse_initialised) 49 return NULL; 50 51 return efuses + nr; 52 } 53 54 static void enable_efuse_program(void) 55 { 56 #ifndef DRY_RUN 57 setbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); 58 #endif 59 } 60 61 static void disable_efuse_program(void) 62 { 63 #ifndef DRY_RUN 64 clrbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); 65 #endif 66 } 67 68 static int do_prog_efuse(struct mvebu_hd_efuse *efuse, 69 struct efuse_val *new_val, u32 mask0, u32 mask1) 70 { 71 struct efuse_val val; 72 73 val.dwords.d[0] = readl(&efuse->bits_31_0); 74 val.dwords.d[1] = readl(&efuse->bits_63_32); 75 val.lock = readl(&efuse->bit64); 76 77 if (val.lock & 1) 78 return -EPERM; 79 80 val.dwords.d[0] |= (new_val->dwords.d[0] & mask0); 81 val.dwords.d[1] |= (new_val->dwords.d[1] & mask1); 82 val.lock |= new_val->lock; 83 84 writel(val.dwords.d[0], &efuse->bits_31_0); 85 mdelay(1); 86 writel(val.dwords.d[1], &efuse->bits_63_32); 87 mdelay(1); 88 writel(val.lock, &efuse->bit64); 89 mdelay(5); 90 91 return 0; 92 } 93 94 static int prog_efuse(int nr, struct efuse_val *new_val, u32 mask0, u32 mask1) 95 { 96 struct mvebu_hd_efuse *efuse; 97 int res = 0; 98 99 res = mvebu_efuse_init_hw(); 100 if (res) 101 return res; 102 103 efuse = get_efuse_line(nr); 104 if (!efuse) 105 return -ENODEV; 106 107 if (!new_val) 108 return -EINVAL; 109 110 /* only write a fuse line with lock bit */ 111 if (!new_val->lock) 112 return -EINVAL; 113 114 /* according to specs ECC protection bits must be 0 on write */ 115 if (new_val->bytes.d[7] & 0xFE) 116 return -EINVAL; 117 118 if (!new_val->dwords.d[0] && !new_val->dwords.d[1] && (mask0 | mask1)) 119 return 0; 120 121 enable_efuse_program(); 122 123 res = do_prog_efuse(efuse, new_val, mask0, mask1); 124 125 disable_efuse_program(); 126 127 return res; 128 } 129 130 int mvebu_efuse_init_hw(void) 131 { 132 int ret; 133 134 if (efuse_initialised) 135 return 0; 136 137 ret = mvebu_mbus_add_window_by_id( 138 CPU_TARGET_SATA23_DFX, 0xA, MBUS_EFUSE_BASE, MBUS_EFUSE_SIZE); 139 140 if (ret) 141 return ret; 142 143 efuse_initialised = 1; 144 145 return 0; 146 } 147 148 int mvebu_read_efuse(int nr, struct efuse_val *val) 149 { 150 struct mvebu_hd_efuse *efuse; 151 int res; 152 153 res = mvebu_efuse_init_hw(); 154 if (res) 155 return res; 156 157 efuse = get_efuse_line(nr); 158 if (!efuse) 159 return -ENODEV; 160 161 if (!val) 162 return -EINVAL; 163 164 val->dwords.d[0] = readl(&efuse->bits_31_0); 165 val->dwords.d[1] = readl(&efuse->bits_63_32); 166 val->lock = readl(&efuse->bit64); 167 return 0; 168 } 169 170 int mvebu_write_efuse(int nr, struct efuse_val *val) 171 { 172 return prog_efuse(nr, val, ~0, ~0); 173 } 174 175 int mvebu_lock_efuse(int nr) 176 { 177 struct efuse_val val = { 178 .lock = 1, 179 }; 180 181 return prog_efuse(nr, &val, 0, 0); 182 } 183 184 /* 185 * wrapper funcs providing the fuse API 186 * 187 * we use the following mapping: 188 * "bank" -> eFuse line 189 * "word" -> 0: bits 0-31 190 * 1: bits 32-63 191 * 2: bit 64 (lock) 192 */ 193 194 static struct efuse_val prog_val; 195 static int valid_prog_words; 196 197 int fuse_read(u32 bank, u32 word, u32 *val) 198 { 199 struct efuse_val fuse_line; 200 int res; 201 202 if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) 203 return -EINVAL; 204 205 res = mvebu_read_efuse(bank, &fuse_line); 206 if (res) 207 return res; 208 209 if (word < 2) 210 *val = fuse_line.dwords.d[word]; 211 else 212 *val = fuse_line.lock; 213 214 return res; 215 } 216 217 int fuse_sense(u32 bank, u32 word, u32 *val) 218 { 219 /* not supported */ 220 return -ENOSYS; 221 } 222 223 int fuse_prog(u32 bank, u32 word, u32 val) 224 { 225 int res = 0; 226 227 /* 228 * NOTE: Fuse line should be written as whole. 229 * So how can we do that with this API? 230 * For now: remember values for word == 0 and word == 1 and write the 231 * whole line when word == 2. 232 * This implies that we always require all 3 fuse prog cmds (one for 233 * for each word) to write a single fuse line. 234 * Exception is a single write to word 2 which will lock the fuse line. 235 * 236 * Hope that will be OK. 237 */ 238 239 if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) 240 return -EINVAL; 241 242 if (word < 2) { 243 prog_val.dwords.d[word] = val; 244 valid_prog_words |= (1 << word); 245 } else if ((valid_prog_words & 3) == 0 && val) { 246 res = mvebu_lock_efuse(bank); 247 valid_prog_words = 0; 248 } else if ((valid_prog_words & 3) != 3 || !val) { 249 res = -EINVAL; 250 } else { 251 prog_val.lock = val != 0; 252 res = mvebu_write_efuse(bank, &prog_val); 253 valid_prog_words = 0; 254 } 255 256 return res; 257 } 258 259 int fuse_override(u32 bank, u32 word, u32 val) 260 { 261 /* not supported */ 262 return -ENOSYS; 263 } 264