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