1 /* 2 * Copyright (c) 2019-2022, STMicroelectronics - All Rights Reserved 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <errno.h> 9 #include <stddef.h> 10 11 #include <common/debug.h> 12 #include <drivers/delay_timer.h> 13 #include <drivers/spi_nor.h> 14 #include <lib/utils.h> 15 16 #define SR_WIP BIT(0) /* Write in progress */ 17 #define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */ 18 #define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ 19 #define FSR_READY BIT(7) /* Device status, 0 = Busy, 1 = Ready */ 20 21 /* Defined IDs for supported memories */ 22 #define SPANSION_ID 0x01U 23 #define MACRONIX_ID 0xC2U 24 #define MICRON_ID 0x2CU 25 26 #define BANK_SIZE 0x1000000U 27 28 #define SPI_READY_TIMEOUT_US 40000U 29 30 static struct nor_device nor_dev; 31 32 #pragma weak plat_get_nor_data 33 int plat_get_nor_data(struct nor_device *device) 34 { 35 return 0; 36 } 37 38 static int spi_nor_reg(uint8_t reg, uint8_t *buf, size_t len, 39 enum spi_mem_data_dir dir) 40 { 41 struct spi_mem_op op; 42 43 zeromem(&op, sizeof(struct spi_mem_op)); 44 op.cmd.opcode = reg; 45 op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 46 op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 47 op.data.dir = dir; 48 op.data.nbytes = len; 49 op.data.buf = buf; 50 51 return spi_mem_exec_op(&op); 52 } 53 54 static inline int spi_nor_read_id(uint8_t *id) 55 { 56 return spi_nor_reg(SPI_NOR_OP_READ_ID, id, 1U, SPI_MEM_DATA_IN); 57 } 58 59 static inline int spi_nor_read_cr(uint8_t *cr) 60 { 61 return spi_nor_reg(SPI_NOR_OP_READ_CR, cr, 1U, SPI_MEM_DATA_IN); 62 } 63 64 static inline int spi_nor_read_sr(uint8_t *sr) 65 { 66 return spi_nor_reg(SPI_NOR_OP_READ_SR, sr, 1U, SPI_MEM_DATA_IN); 67 } 68 69 static inline int spi_nor_read_fsr(uint8_t *fsr) 70 { 71 return spi_nor_reg(SPI_NOR_OP_READ_FSR, fsr, 1U, SPI_MEM_DATA_IN); 72 } 73 74 static inline int spi_nor_write_en(void) 75 { 76 return spi_nor_reg(SPI_NOR_OP_WREN, NULL, 0U, SPI_MEM_DATA_OUT); 77 } 78 79 /* 80 * Check if device is ready. 81 * 82 * Return 0 if ready, 1 if busy or a negative error code otherwise 83 */ 84 static int spi_nor_ready(void) 85 { 86 uint8_t sr; 87 int ret; 88 89 ret = spi_nor_read_sr(&sr); 90 if (ret != 0) { 91 return ret; 92 } 93 94 if ((nor_dev.flags & SPI_NOR_USE_FSR) != 0U) { 95 uint8_t fsr; 96 97 ret = spi_nor_read_fsr(&fsr); 98 if (ret != 0) { 99 return ret; 100 } 101 102 return (((fsr & FSR_READY) != 0U) && ((sr & SR_WIP) == 0U)) ? 103 0 : 1; 104 } 105 106 return (((sr & SR_WIP) == 0U) ? 0 : 1); 107 } 108 109 static int spi_nor_wait_ready(void) 110 { 111 int ret; 112 uint64_t timeout = timeout_init_us(SPI_READY_TIMEOUT_US); 113 114 while (!timeout_elapsed(timeout)) { 115 ret = spi_nor_ready(); 116 if (ret <= 0) { 117 return ret; 118 } 119 } 120 121 return -ETIMEDOUT; 122 } 123 124 static int spi_nor_macronix_quad_enable(void) 125 { 126 uint8_t sr; 127 int ret; 128 129 ret = spi_nor_read_sr(&sr); 130 if (ret != 0) { 131 return ret; 132 } 133 134 if ((sr & SR_QUAD_EN_MX) != 0U) { 135 return 0; 136 } 137 138 ret = spi_nor_write_en(); 139 if (ret != 0) { 140 return ret; 141 } 142 143 sr |= SR_QUAD_EN_MX; 144 ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1U, SPI_MEM_DATA_OUT); 145 if (ret != 0) { 146 return ret; 147 } 148 149 ret = spi_nor_wait_ready(); 150 if (ret != 0) { 151 return ret; 152 } 153 154 ret = spi_nor_read_sr(&sr); 155 if ((ret != 0) || ((sr & SR_QUAD_EN_MX) == 0U)) { 156 return -EINVAL; 157 } 158 159 return 0; 160 } 161 162 static int spi_nor_write_sr_cr(uint8_t *sr_cr) 163 { 164 int ret; 165 166 ret = spi_nor_write_en(); 167 if (ret != 0) { 168 return ret; 169 } 170 171 ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2U, SPI_MEM_DATA_OUT); 172 if (ret != 0) { 173 return -EINVAL; 174 } 175 176 ret = spi_nor_wait_ready(); 177 if (ret != 0) { 178 return ret; 179 } 180 181 return 0; 182 } 183 184 static int spi_nor_quad_enable(void) 185 { 186 uint8_t sr_cr[2]; 187 int ret; 188 189 ret = spi_nor_read_cr(&sr_cr[1]); 190 if (ret != 0) { 191 return ret; 192 } 193 194 if ((sr_cr[1] & CR_QUAD_EN_SPAN) != 0U) { 195 return 0; 196 } 197 198 sr_cr[1] |= CR_QUAD_EN_SPAN; 199 ret = spi_nor_read_sr(&sr_cr[0]); 200 if (ret != 0) { 201 return ret; 202 } 203 204 ret = spi_nor_write_sr_cr(sr_cr); 205 if (ret != 0) { 206 return ret; 207 } 208 209 ret = spi_nor_read_cr(&sr_cr[1]); 210 if ((ret != 0) || ((sr_cr[1] & CR_QUAD_EN_SPAN) == 0U)) { 211 return -EINVAL; 212 } 213 214 return 0; 215 } 216 217 static int spi_nor_clean_bar(void) 218 { 219 int ret; 220 221 if (nor_dev.selected_bank == 0U) { 222 return 0; 223 } 224 225 nor_dev.selected_bank = 0U; 226 227 ret = spi_nor_write_en(); 228 if (ret != 0) { 229 return ret; 230 } 231 232 return spi_nor_reg(nor_dev.bank_write_cmd, &nor_dev.selected_bank, 233 1U, SPI_MEM_DATA_OUT); 234 } 235 236 static int spi_nor_write_bar(uint32_t offset) 237 { 238 uint8_t selected_bank = offset / BANK_SIZE; 239 int ret; 240 241 if (selected_bank == nor_dev.selected_bank) { 242 return 0; 243 } 244 245 ret = spi_nor_write_en(); 246 if (ret != 0) { 247 return ret; 248 } 249 250 ret = spi_nor_reg(nor_dev.bank_write_cmd, &selected_bank, 251 1U, SPI_MEM_DATA_OUT); 252 if (ret != 0) { 253 return ret; 254 } 255 256 nor_dev.selected_bank = selected_bank; 257 258 return 0; 259 } 260 261 static int spi_nor_read_bar(void) 262 { 263 uint8_t selected_bank = 0U; 264 int ret; 265 266 ret = spi_nor_reg(nor_dev.bank_read_cmd, &selected_bank, 267 1U, SPI_MEM_DATA_IN); 268 if (ret != 0) { 269 return ret; 270 } 271 272 nor_dev.selected_bank = selected_bank; 273 274 return 0; 275 } 276 277 int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length, 278 size_t *length_read) 279 { 280 size_t remain_len; 281 int ret; 282 283 *length_read = 0U; 284 nor_dev.read_op.addr.val = offset; 285 nor_dev.read_op.data.buf = (void *)buffer; 286 287 VERBOSE("%s offset %u length %zu\n", __func__, offset, length); 288 289 while (length != 0U) { 290 if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { 291 ret = spi_nor_write_bar(nor_dev.read_op.addr.val); 292 if (ret != 0) { 293 return ret; 294 } 295 296 remain_len = (BANK_SIZE * (nor_dev.selected_bank + 1)) - 297 nor_dev.read_op.addr.val; 298 nor_dev.read_op.data.nbytes = MIN(length, remain_len); 299 } else { 300 nor_dev.read_op.data.nbytes = length; 301 } 302 303 ret = spi_mem_exec_op(&nor_dev.read_op); 304 if (ret != 0) { 305 spi_nor_clean_bar(); 306 return ret; 307 } 308 309 length -= nor_dev.read_op.data.nbytes; 310 nor_dev.read_op.addr.val += nor_dev.read_op.data.nbytes; 311 nor_dev.read_op.data.buf += nor_dev.read_op.data.nbytes; 312 *length_read += nor_dev.read_op.data.nbytes; 313 } 314 315 if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { 316 ret = spi_nor_clean_bar(); 317 if (ret != 0) { 318 return ret; 319 } 320 } 321 322 return 0; 323 } 324 325 int spi_nor_init(unsigned long long *size, unsigned int *erase_size) 326 { 327 int ret; 328 uint8_t id; 329 330 /* Default read command used */ 331 nor_dev.read_op.cmd.opcode = SPI_NOR_OP_READ; 332 nor_dev.read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 333 nor_dev.read_op.addr.nbytes = 3U; 334 nor_dev.read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 335 nor_dev.read_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; 336 nor_dev.read_op.data.dir = SPI_MEM_DATA_IN; 337 338 if (plat_get_nor_data(&nor_dev) != 0) { 339 return -EINVAL; 340 } 341 342 assert(nor_dev.size != 0U); 343 344 if (nor_dev.size > BANK_SIZE) { 345 nor_dev.flags |= SPI_NOR_USE_BANK; 346 } 347 348 *size = nor_dev.size; 349 350 ret = spi_nor_read_id(&id); 351 if (ret != 0) { 352 return ret; 353 } 354 355 if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { 356 switch (id) { 357 case SPANSION_ID: 358 nor_dev.bank_read_cmd = SPINOR_OP_BRRD; 359 nor_dev.bank_write_cmd = SPINOR_OP_BRWR; 360 break; 361 default: 362 nor_dev.bank_read_cmd = SPINOR_OP_RDEAR; 363 nor_dev.bank_write_cmd = SPINOR_OP_WREAR; 364 break; 365 } 366 } 367 368 if (nor_dev.read_op.data.buswidth == 4U) { 369 switch (id) { 370 case MACRONIX_ID: 371 INFO("Enable Macronix quad support\n"); 372 ret = spi_nor_macronix_quad_enable(); 373 break; 374 case MICRON_ID: 375 break; 376 default: 377 ret = spi_nor_quad_enable(); 378 break; 379 } 380 } 381 382 if ((ret == 0) && ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U)) { 383 ret = spi_nor_read_bar(); 384 } 385 386 return ret; 387 } 388