blob: 6b4643e89caf814d86f8c0ebd92ce8fc0666d348 [file] [log] [blame]
Lionel Debieveed821de2019-09-24 17:39:14 +02001/*
Lionel Debieved9ed3362020-11-24 11:46:42 +01002 * Copyright (c) 2019-2021, STMicroelectronics - All Rights Reserved
Lionel Debieveed821de2019-09-24 17:39:14 +02003 *
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
30static struct nor_device nor_dev;
31
32#pragma weak plat_get_nor_data
33int plat_get_nor_data(struct nor_device *device)
34{
35 return 0;
36}
37
38static 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
54static 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
59static 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
64static 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
69static 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
74static 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 */
84static 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
Lionel Debieved9ed3362020-11-24 11:46:42 +0100106 return (((sr & SR_WIP) == 0U) ? 0 : 1);
Lionel Debieveed821de2019-09-24 17:39:14 +0200107}
108
109static 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
124static 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
Lionel Debieve9f3dd1a2020-11-24 11:44:52 +0100134 if ((sr & SR_QUAD_EN_MX) != 0U) {
Lionel Debieveed821de2019-09-24 17:39:14 +0200135 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;
Lionel Debieved9ed3362020-11-24 11:46:42 +0100144 ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1U, SPI_MEM_DATA_OUT);
Lionel Debieveed821de2019-09-24 17:39:14 +0200145 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
162static 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
Lionel Debieved9ed3362020-11-24 11:46:42 +0100171 ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2U, SPI_MEM_DATA_OUT);
Lionel Debieveed821de2019-09-24 17:39:14 +0200172 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
184static 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
217static 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,
Lionel Debieved9ed3362020-11-24 11:46:42 +0100233 1U, SPI_MEM_DATA_OUT);
Lionel Debieveed821de2019-09-24 17:39:14 +0200234}
235
236static 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,
Lionel Debieved9ed3362020-11-24 11:46:42 +0100251 1U, SPI_MEM_DATA_OUT);
Lionel Debieveed821de2019-09-24 17:39:14 +0200252 if (ret != 0) {
253 return ret;
254 }
255
256 nor_dev.selected_bank = selected_bank;
257
258 return 0;
259}
260
261static int spi_nor_read_bar(void)
262{
Lionel Debieved9ed3362020-11-24 11:46:42 +0100263 uint8_t selected_bank = 0U;
Lionel Debieveed821de2019-09-24 17:39:14 +0200264 int ret;
265
266 ret = spi_nor_reg(nor_dev.bank_read_cmd, &selected_bank,
Lionel Debieved9ed3362020-11-24 11:46:42 +0100267 1U, SPI_MEM_DATA_IN);
Lionel Debieveed821de2019-09-24 17:39:14 +0200268 if (ret != 0) {
269 return ret;
270 }
271
272 nor_dev.selected_bank = selected_bank;
273
274 return 0;
275}
276
277int 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
Lionel Debieved9ed3362020-11-24 11:46:42 +0100283 *length_read = 0U;
Lionel Debieveed821de2019-09-24 17:39:14 +0200284 nor_dev.read_op.addr.val = offset;
285 nor_dev.read_op.data.buf = (void *)buffer;
286
287 VERBOSE("%s offset %i 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
325int spi_nor_init(unsigned long long *size, unsigned int *erase_size)
326{
Lionel Debieved9ed3362020-11-24 11:46:42 +0100327 int ret;
Lionel Debieveed821de2019-09-24 17:39:14 +0200328 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
Lionel Debieved9ed3362020-11-24 11:46:42 +0100342 assert(nor_dev.size != 0U);
Lionel Debieveed821de2019-09-24 17:39:14 +0200343
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:
Lionel Debieve5fd6a192020-04-27 09:50:48 +0200371 INFO("Enable Macronix quad support\n");
Lionel Debieveed821de2019-09-24 17:39:14 +0200372 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}