1 /* 2 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <errno.h> 8 #include <mmio.h> 9 #include <norflash.h> 10 11 12 /* 13 * DWS ready poll retries. The number of retries in this driver have been 14 * obtained empirically from Juno. FVP implements a zero wait state NOR flash 15 * model 16 */ 17 #define DWS_WORD_PROGRAM_RETRIES 1000 18 #define DWS_WORD_ERASE_RETRIES 3000000 19 #define DWS_WORD_LOCK_RETRIES 1000 20 21 /* Helper macro to detect end of command */ 22 #define NOR_CMD_END (NOR_DWS | NOR_DWS << 16l) 23 24 /* 25 * This file supplies a low level interface to the vexpress NOR flash 26 * memory of juno and fvp. This memory is organized as an interleaved 27 * memory of two chips with a 16 bit word. It means that every 32 bit 28 * access is going to access to two different chips. This is very 29 * important when we send commands or read status of the chips 30 */ 31 32 /* Helper macros to access two flash banks in parallel */ 33 #define NOR_2X16(d) ((d << 16) | (d & 0xffff)) 34 35 static unsigned int nor_status(uintptr_t base_addr) 36 { 37 unsigned long status; 38 39 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); 40 status = mmio_read_32(base_addr); 41 status |= status >> 16; /* merge status from both flash banks */ 42 43 return status & 0xFFFF; 44 } 45 46 /* 47 * Poll Write State Machine. 48 * Return values: 49 * 0 = WSM ready 50 * -EBUSY = WSM busy after the number of retries 51 */ 52 static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries) 53 { 54 unsigned long status; 55 56 do { 57 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); 58 status = mmio_read_32(base_addr); 59 if ((status & NOR_CMD_END) == NOR_CMD_END) 60 return 0; 61 } while (retries-- > 0); 62 63 return -EBUSY; 64 } 65 66 /* 67 * Return values: 68 * 0 = success 69 * -EPERM = Device protected or Block locked 70 * -EIO = General I/O error 71 */ 72 static int nor_full_status_check(uintptr_t base_addr) 73 { 74 unsigned long status; 75 76 /* Full status check */ 77 status = nor_status(base_addr); 78 79 if (status & (NOR_PS | NOR_BLS | NOR_ESS | NOR_PSS)) 80 return -EPERM; 81 if (status & (NOR_VPPS | NOR_ES)) 82 return -EIO; 83 return 0; 84 } 85 86 void nor_send_cmd(uintptr_t base_addr, unsigned long cmd) 87 { 88 mmio_write_32(base_addr, NOR_2X16(cmd)); 89 } 90 91 /* 92 * This function programs a word in the flash. Be aware that it only 93 * can reset bits that were previously set. It cannot set bits that 94 * were previously reset. The resulting bits = old_bits & new bits. 95 * Return values: 96 * 0 = success 97 * otherwise it returns a negative value 98 */ 99 int nor_word_program(uintptr_t base_addr, unsigned long data) 100 { 101 uint32_t status; 102 int ret; 103 104 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 105 106 /* Set the device in write word mode */ 107 nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM); 108 mmio_write_32(base_addr, data); 109 110 ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES); 111 if (ret == 0) { 112 /* Full status check */ 113 nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG); 114 status = mmio_read_32(base_addr); 115 116 if (status & (NOR_PS | NOR_BLS)) { 117 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 118 ret = -EPERM; 119 } 120 } 121 122 if (ret == 0) 123 ret = nor_full_status_check(base_addr); 124 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 125 126 return ret; 127 } 128 129 /* 130 * Erase a full 256K block 131 * Return values: 132 * 0 = success 133 * otherwise it returns a negative value 134 */ 135 int nor_erase(uintptr_t base_addr) 136 { 137 int ret; 138 139 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 140 141 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE); 142 nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE_ACK); 143 144 ret = nor_poll_dws(base_addr, DWS_WORD_ERASE_RETRIES); 145 if (ret == 0) 146 ret = nor_full_status_check(base_addr); 147 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 148 149 return ret; 150 } 151 152 /* 153 * Lock a full 256 block 154 * Return values: 155 * 0 = success 156 * otherwise it returns a negative value 157 */ 158 int nor_lock(uintptr_t base_addr) 159 { 160 int ret; 161 162 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 163 164 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); 165 nor_send_cmd(base_addr, NOR_LOCK_BLOCK); 166 167 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); 168 if (ret == 0) 169 ret = nor_full_status_check(base_addr); 170 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 171 172 return ret; 173 } 174 175 /* 176 * unlock a full 256 block 177 * Return values: 178 * 0 = success 179 * otherwise it returns a negative value 180 */ 181 int nor_unlock(uintptr_t base_addr) 182 { 183 int ret; 184 185 nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG); 186 187 nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK); 188 nor_send_cmd(base_addr, NOR_UNLOCK_BLOCK); 189 190 ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES); 191 if (ret == 0) 192 ret = nor_full_status_check(base_addr); 193 nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY); 194 195 return ret; 196 } 197