1 /*
2 * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <errno.h>
8
9 #include <drivers/cfi/v2m_flash.h>
10 #include <lib/mmio.h>
11
12 /*
13 * This file supplies a low level interface to the vexpress NOR flash
14 * memory of juno and fvp. This memory is organized as an interleaved
15 * memory of two chips with a 16 bit word. It means that every 32 bit
16 * access is going to access to two different chips. This is very
17 * important when we send commands or read status of the chips.
18 */
19
20 /*
21 * DWS ready poll retries. The number of retries in this driver have been
22 * obtained empirically from Juno. FVP implements a zero wait state NOR flash
23 * model
24 */
25 #define DWS_WORD_PROGRAM_RETRIES 1000
26 #define DWS_WORD_ERASE_RETRIES 3000000
27 #define DWS_WORD_LOCK_RETRIES 1000
28
29 /* Helper macro to detect end of command */
30 #define NOR_CMD_END (NOR_DWS | (NOR_DWS << 16l))
31
32 /* Helper macros to access two flash banks in parallel */
33 #define NOR_2X16(d) ((d << 16) | (d & 0xffff))
34
nor_status(uintptr_t base_addr)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 */
nor_poll_dws(uintptr_t base_addr,unsigned long int retries)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 */
nor_full_status_check(uintptr_t base_addr)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
nor_send_cmd(uintptr_t base_addr,unsigned long cmd)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 */
nor_word_program(uintptr_t base_addr,unsigned long data)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 */
nor_erase(uintptr_t base_addr)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 */
nor_lock(uintptr_t base_addr)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 */
nor_unlock(uintptr_t base_addr)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