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