blob: cecf56088ff733e8a675e45a049129b52b70d6dc [file] [log] [blame]
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +08001/*
2 * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
Hadi Asyrafi0f484b32019-06-17 11:48:58 +08003 * Copyright (c) 2019, Intel Corporation. All rights reserved.
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +08004 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#include <assert.h>
9#include <common/debug.h>
10#include <lib/mmio.h>
11#include <string.h>
12#include <drivers/delay_timer.h>
13#include <drivers/console.h>
14
15#include "cadence_qspi.h"
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +080016
17#define LESS(a, b) (((a) < (b)) ? (a) : (b))
18#define MORE(a, b) (((a) > (b)) ? (a) : (b))
19
20
21uint32_t qspi_device_size;
22int cad_qspi_cs;
23
24int cad_qspi_idle(void)
25{
26 return (mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG)
27 & CAD_QSPI_CFG_IDLE) >> 31;
28}
29
30int cad_qspi_set_baudrate_div(uint32_t div)
31{
32 if (div > 0xf)
33 return CAD_INVALID;
34
35 mmio_clrsetbits_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG,
36 ~CAD_QSPI_CFG_BAUDDIV_MSK,
37 CAD_QSPI_CFG_BAUDDIV(div));
38
39 return 0;
40}
41
42int cad_qspi_configure_dev_size(uint32_t addr_bytes,
43 uint32_t bytes_per_dev, uint32_t bytes_per_block)
44{
45
46 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DEVSZ,
47 CAD_QSPI_DEVSZ_ADDR_BYTES(addr_bytes) |
48 CAD_QSPI_DEVSZ_BYTES_PER_PAGE(bytes_per_dev) |
49 CAD_QSPI_DEVSZ_BYTES_PER_BLOCK(bytes_per_block));
50 return 0;
51}
52
53int cad_qspi_set_read_config(uint32_t opcode, uint32_t instr_type,
54 uint32_t addr_type, uint32_t data_type,
55 uint32_t mode_bit, uint32_t dummy_clk_cycle)
56{
57 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DEVRD,
58 CAD_QSPI_DEV_OPCODE(opcode) |
59 CAD_QSPI_DEV_INST_TYPE(instr_type) |
60 CAD_QSPI_DEV_ADDR_TYPE(addr_type) |
61 CAD_QSPI_DEV_DATA_TYPE(data_type) |
62 CAD_QSPI_DEV_MODE_BIT(mode_bit) |
63 CAD_QSPI_DEV_DUMMY_CLK_CYCLE(dummy_clk_cycle));
64
65 return 0;
66}
67
Hadi Asyrafi0f484b32019-06-17 11:48:58 +080068int cad_qspi_set_write_config(uint32_t opcode, uint32_t addr_type,
69 uint32_t data_type, uint32_t dummy_clk_cycle)
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +080070{
71 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DEVWR,
Hadi Asyrafi0f484b32019-06-17 11:48:58 +080072 CAD_QSPI_DEV_OPCODE(opcode) |
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +080073 CAD_QSPI_DEV_ADDR_TYPE(addr_type) |
74 CAD_QSPI_DEV_DATA_TYPE(data_type) |
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +080075 CAD_QSPI_DEV_DUMMY_CLK_CYCLE(dummy_clk_cycle));
76
77 return 0;
78}
79
80int cad_qspi_timing_config(uint32_t clkphase, uint32_t clkpol, uint32_t csda,
81 uint32_t csdads, uint32_t cseot, uint32_t cssot,
82 uint32_t rddatacap)
83{
84 uint32_t cfg = mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG);
85
86 cfg &= CAD_QSPI_CFG_SELCLKPHASE_CLR_MSK &
87 CAD_QSPI_CFG_SELCLKPOL_CLR_MSK;
88 cfg |= CAD_QSPI_SELCLKPHASE(clkphase) | CAD_QSPI_SELCLKPOL(clkpol);
89
90 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG, cfg);
91
92 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_DELAY,
93 CAD_QSPI_DELAY_CSSOT(cssot) | CAD_QSPI_DELAY_CSEOT(cseot) |
94 CAD_QSPI_DELAY_CSDADS(csdads) | CAD_QSPI_DELAY_CSDA(csda));
95
96 return 0;
97}
98
99int cad_qspi_stig_cmd_helper(int cs, uint32_t cmd)
100{
101 uint32_t count = 0;
102
103 /* chip select */
104 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG,
105 (mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG)
106 & CAD_QSPI_CFG_CS_MSK) | CAD_QSPI_CFG_CS(cs));
107
108 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD, cmd);
109 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD,
110 cmd | CAD_QSPI_FLASHCMD_EXECUTE);
111
112 do {
113 uint32_t reg = mmio_read_32(CAD_QSPI_OFFSET +
114 CAD_QSPI_FLASHCMD);
115 if (!(reg & CAD_QSPI_FLASHCMD_EXECUTE_STAT))
116 break;
117 count++;
118 } while (count < CAD_QSPI_COMMAND_TIMEOUT);
119
120 if (count >= CAD_QSPI_COMMAND_TIMEOUT) {
121 ERROR("Error sending QSPI command %x, timed out\n",
122 cmd);
123 return CAD_QSPI_ERROR;
124 }
125
126 return 0;
127}
128
129int cad_qspi_stig_cmd(uint32_t opcode, uint32_t dummy)
130{
131 if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1)) {
132 ERROR("Faulty dummy bytes\n");
133 return -1;
134 }
135
136 return cad_qspi_stig_cmd_helper(cad_qspi_cs,
137 CAD_QSPI_FLASHCMD_OPCODE(opcode) |
138 CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES(dummy));
139}
140
141int cad_qspi_stig_read_cmd(uint32_t opcode, uint32_t dummy, uint32_t num_bytes,
142 uint32_t *output)
143{
144 if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1)) {
145 ERROR("Faulty dummy byes\n");
146 return -1;
147 }
148
149 if ((num_bytes > 8) || (num_bytes == 0))
150 return -1;
151
152 uint32_t cmd =
153 CAD_QSPI_FLASHCMD_OPCODE(opcode) |
154 CAD_QSPI_FLASHCMD_ENRDDATA(1) |
155 CAD_QSPI_FLASHCMD_NUMRDDATABYTES(num_bytes - 1) |
156 CAD_QSPI_FLASHCMD_ENCMDADDR(0) |
157 CAD_QSPI_FLASHCMD_ENMODEBIT(0) |
158 CAD_QSPI_FLASHCMD_NUMADDRBYTES(0) |
159 CAD_QSPI_FLASHCMD_ENWRDATA(0) |
160 CAD_QSPI_FLASHCMD_NUMWRDATABYTES(0) |
161 CAD_QSPI_FLASHCMD_NUMDUMMYBYTES(dummy);
162
163 if (cad_qspi_stig_cmd_helper(cad_qspi_cs, cmd)) {
Hadi Asyrafi0f484b32019-06-17 11:48:58 +0800164 ERROR("failed to send stig cmd\n");
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800165 return -1;
166 }
167
168 output[0] = mmio_read_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_RDDATA0);
169
170 if (num_bytes > 4) {
171 output[1] = mmio_read_32(CAD_QSPI_OFFSET +
172 CAD_QSPI_FLASHCMD_RDDATA1);
173 }
174
175 return 0;
176}
177
178int cad_qspi_stig_wr_cmd(uint32_t opcode, uint32_t dummy, uint32_t num_bytes,
179 uint32_t *input)
180{
181 if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1)) {
182 ERROR("Faulty dummy byes\n");
183 return -1;
184 }
185
186 if ((num_bytes > 8) || (num_bytes == 0))
187 return -1;
188
189 uint32_t cmd = CAD_QSPI_FLASHCMD_OPCODE(opcode) |
190 CAD_QSPI_FLASHCMD_ENRDDATA(0) |
191 CAD_QSPI_FLASHCMD_NUMRDDATABYTES(0) |
192 CAD_QSPI_FLASHCMD_ENCMDADDR(0) |
193 CAD_QSPI_FLASHCMD_ENMODEBIT(0) |
194 CAD_QSPI_FLASHCMD_NUMADDRBYTES(0) |
195 CAD_QSPI_FLASHCMD_ENWRDATA(1) |
196 CAD_QSPI_FLASHCMD_NUMWRDATABYTES(num_bytes - 1) |
197 CAD_QSPI_FLASHCMD_NUMDUMMYBYTES(dummy);
198
199 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_WRDATA0, input[0]);
200
201 if (num_bytes > 4)
202 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_WRDATA1,
203 input[1]);
204
205 return cad_qspi_stig_cmd_helper(cad_qspi_cs, cmd);
206}
207
208int cad_qspi_stig_addr_cmd(uint32_t opcode, uint32_t dummy, uint32_t addr)
209{
210 uint32_t cmd;
211
212 if (dummy > ((1 << CAD_QSPI_FLASHCMD_NUM_DUMMYBYTES_MAX) - 1))
213 return -1;
214
215 cmd = CAD_QSPI_FLASHCMD_OPCODE(opcode) |
216 CAD_QSPI_FLASHCMD_NUMDUMMYBYTES(dummy) |
217 CAD_QSPI_FLASHCMD_ENCMDADDR(1) |
218 CAD_QSPI_FLASHCMD_NUMADDRBYTES(2);
219
220 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_FLASHCMD_ADDR, addr);
221
222 return cad_qspi_stig_cmd_helper(cad_qspi_cs, cmd);
223}
224
225int cad_qspi_device_bank_select(uint32_t bank)
226{
227 int status = 0;
228
229 status = cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WREN, 0);
230 if (status != 0)
231 return status;
232
233 status = cad_qspi_stig_wr_cmd(CAD_QSPI_STIG_OPCODE_WREN_EXT_REG,
234 0, 1, &bank);
235 if (status != 0)
236 return status;
237
238 return cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WRDIS, 0);
239}
240
241int cad_qspi_device_status(uint32_t *status)
242{
243 return cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDSR, 0, 1, status);
244}
245
246#if CAD_QSPI_MICRON_N25Q_SUPPORT
247int cad_qspi_n25q_enable(void)
248{
249 cad_qspi_set_read_config(QSPI_FAST_READ, CAD_QSPI_INST_SINGLE,
250 CAD_QSPI_ADDR_FASTREAD, CAT_QSPI_ADDR_SINGLE_IO, 1,
251 0);
Hadi Asyrafi0f484b32019-06-17 11:48:58 +0800252 cad_qspi_set_write_config(QSPI_WRITE, 0, 0, 0);
253
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800254 return 0;
255}
256
257int cad_qspi_n25q_wait_for_program_and_erase(int program_only)
258{
259 uint32_t status, flag_sr;
260 int count = 0;
261
262 while (count < CAD_QSPI_COMMAND_TIMEOUT) {
263 status = cad_qspi_device_status(&status);
264 if (status != 0) {
265 ERROR("Error getting device status\n");
266 return -1;
267 }
268 if (!CAD_QSPI_STIG_SR_BUSY(status))
269 break;
270 count++;
271 }
272
273 if (count >= CAD_QSPI_COMMAND_TIMEOUT) {
274 ERROR("Timed out waiting for idle\n");
275 return -1;
276 }
277
278 count = 0;
279
280 while (count < CAD_QSPI_COMMAND_TIMEOUT) {
281 status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDFLGSR,
282 0, 1, &flag_sr);
283 if (status != 0) {
284 ERROR("Error waiting program and erase.\n");
285 return status;
286 }
287
288 if ((program_only &&
289 CAD_QSPI_STIG_FLAGSR_PROGRAMREADY(flag_sr)) ||
290 (!program_only &&
291 CAD_QSPI_STIG_FLAGSR_ERASEREADY(flag_sr)))
292 break;
293 }
294
295 if (count >= CAD_QSPI_COMMAND_TIMEOUT)
296 ERROR("Timed out waiting for program and erase\n");
297
298 if ((program_only && CAD_QSPI_STIG_FLAGSR_PROGRAMERROR(flag_sr)) ||
299 (!program_only &&
300 CAD_QSPI_STIG_FLAGSR_ERASEERROR(flag_sr))) {
301 ERROR("Error programming/erasing flash\n");
302 cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_CLFSR, 0);
303 return -1;
304 }
305
306 return 0;
307}
308#endif
309
310int cad_qspi_indirect_read_start_bank(uint32_t flash_addr, uint32_t num_bytes)
311{
312 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDRDSTADDR, flash_addr);
313 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDRDCNT, num_bytes);
314 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDRD,
315 CAD_QSPI_INDRD_START |
316 CAD_QSPI_INDRD_IND_OPS_DONE);
317
318 return 0;
319}
320
321
322int cad_qspi_indirect_write_start_bank(uint32_t flash_addr,
323 uint32_t num_bytes)
324{
325 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDWRSTADDR, flash_addr);
326 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDWRCNT, num_bytes);
327 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_INDWR,
328 CAD_QSPI_INDWR_START |
329 CAD_QSPI_INDWR_INDDONE);
330
331 return 0;
332}
333
334int cad_qspi_indirect_write_finish(void)
335{
336#if CAD_QSPI_MICRON_N25Q_SUPPORT
337 return cad_qspi_n25q_wait_for_program_and_erase(1);
338#else
339 return 0;
340#endif
341
342}
343
344int cad_qspi_enable(void)
345{
346 int status;
347
348 mmio_setbits_32(CAD_QSPI_OFFSET + CAD_QSPI_CFG, CAD_QSPI_CFG_ENABLE);
349
350#if CAD_QSPI_MICRON_N25Q_SUPPORT
351 status = cad_qspi_n25q_enable();
352 if (status != 0)
353 return status;
354#endif
355 return 0;
356}
357
358int cad_qspi_enable_subsector_bank(uint32_t addr)
359{
360 int status = 0;
361
362 status = cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WREN, 0);
363 if (status != 0)
364 return status;
365
366 status = cad_qspi_stig_addr_cmd(CAD_QSPI_STIG_OPCODE_SUBSEC_ERASE, 0,
367 addr);
368 if (status != 0)
369 return status;
370
371#if CAD_QSPI_MICRON_N25Q_SUPPORT
372 status = cad_qspi_n25q_wait_for_program_and_erase(0);
373#endif
374 return status;
375}
376
377int cad_qspi_erase_subsector(uint32_t addr)
378{
379 int status = 0;
380
381 status = cad_qspi_device_bank_select(addr >> 24);
382 if (status != 0)
383 return status;
384
385 return cad_qspi_enable_subsector_bank(addr);
386}
387
388int cad_qspi_erase_sector(uint32_t addr)
389{
390 int status = 0;
391
392 status = cad_qspi_device_bank_select(addr >> 24);
393 if (status != 0)
394 return status;
395
396 status = cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_WREN, 0);
397 if (status != 0)
398 return status;
399
400 status = cad_qspi_stig_addr_cmd(CAD_QSPI_STIG_OPCODE_SEC_ERASE, 0,
401 addr);
402 if (status != 0)
403 return status;
404
405#if CAD_QSPI_MICRON_N25Q_SUPPORT
406 status = cad_qspi_n25q_wait_for_program_and_erase(0);
407#endif
408 return status;
409}
410
411void cad_qspi_calibration(uint32_t dev_clk, uint32_t qspi_clk_mhz)
412{
413 int status;
414 uint32_t dev_sclk_mhz = 27; /*min value to get biggest 0xF div factor*/
415 uint32_t data_cap_delay;
416 uint32_t sample_rdid;
417 uint32_t rdid;
418 uint32_t div_actual;
419 uint32_t div_bits;
420 int first_pass, last_pass;
421
422 /*1. Set divider to bigger value (slowest SCLK)
423 *2. RDID and save the value
424 */
425 div_actual = (qspi_clk_mhz + (dev_sclk_mhz - 1)) / dev_sclk_mhz;
426 div_bits = (((div_actual + 1) / 2) - 1);
427 status = cad_qspi_set_baudrate_div(0xf);
428
429 status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID,
430 0, 3, &sample_rdid);
431 if (status != 0)
432 return;
433
434 /*3. Set divider to the intended frequency
435 *4. Set the read delay = 0
436 *5. RDID and check whether the value is same as item 2
437 *6. Increase read delay and compared the value against item 2
438 *7. Find the range of read delay that have same as
439 * item 2 and divide it to 2
440 */
441 div_actual = (qspi_clk_mhz + (dev_clk - 1)) / dev_clk;
442 div_bits = (((div_actual + 1) / 2) - 1);
443 status = cad_qspi_set_baudrate_div(div_bits);
444 if (status != 0)
445 return;
446
447 data_cap_delay = 0;
448 first_pass = -1;
449 last_pass = -1;
450
451 do {
452 if (status != 0)
453 break;
454 status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID, 0,
455 3, &rdid);
456 if (status != 0)
457 break;
458 if (rdid == sample_rdid) {
459 if (first_pass == -1)
460 first_pass = data_cap_delay;
461 else
462 last_pass = data_cap_delay;
463 }
464
465 data_cap_delay++;
466
467 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_RDDATACAP,
468 CAD_QSPI_RDDATACAP_BYP(1) |
469 CAD_QSPI_RDDATACAP_DELAY(data_cap_delay));
470
471 } while (data_cap_delay < 0x10);
472
473 if (first_pass > 0) {
474 int diff = first_pass - last_pass;
475
476 data_cap_delay = first_pass + diff / 2;
477 }
478
479 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_RDDATACAP,
480 CAD_QSPI_RDDATACAP_BYP(1) |
481 CAD_QSPI_RDDATACAP_DELAY(data_cap_delay));
482 status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID, 0, 3, &rdid);
483
484 if (status != 0)
485 return;
486}
487
488int cad_qspi_int_disable(uint32_t mask)
489{
490 if (cad_qspi_idle() == 0)
491 return -1;
492
493 if ((CAD_QSPI_INT_STATUS_ALL & mask) == 0)
494 return -1;
495
496 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_IRQMSK, mask);
497 return 0;
498}
499
500void cad_qspi_set_chip_select(int cs)
501{
502 cad_qspi_cs = cs;
503}
504
505int cad_qspi_init(uint32_t desired_clk_freq, uint32_t clk_phase,
506 uint32_t clk_pol, uint32_t csda, uint32_t csdads,
507 uint32_t cseot, uint32_t cssot, uint32_t rddatacap)
508{
509 int status = 0;
510 uint32_t qspi_desired_clk_freq;
511 uint32_t rdid = 0;
512 uint32_t cap_code;
513
514 INFO("Initializing Qspi\n");
515
516 if (cad_qspi_idle() == 0) {
Hadi Asyrafi0f484b32019-06-17 11:48:58 +0800517 ERROR("device not idle\n");
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800518 return -1;
519 }
520
521
522 status = cad_qspi_timing_config(clk_phase, clk_pol, csda, csdads,
523 cseot, cssot, rddatacap);
524
525 if (status != 0) {
526 ERROR("config set timing failure\n");
527 return status;
528 }
529
530 mmio_write_32(CAD_QSPI_OFFSET + CAD_QSPI_REMAPADDR,
531 CAD_QSPI_REMAPADDR_VALUE_SET(0));
532
533 status = cad_qspi_int_disable(CAD_QSPI_INT_STATUS_ALL);
534 if (status != 0) {
535 ERROR("failed disable\n");
536 return status;
537 }
538
539 cad_qspi_set_baudrate_div(0xf);
540 status = cad_qspi_enable();
541 if (status != 0) {
542 ERROR("failed enable\n");
543 return status;
544 }
545
546 qspi_desired_clk_freq = 100;
547 cad_qspi_calibration(qspi_desired_clk_freq, 50000000);
548
549 status = cad_qspi_stig_read_cmd(CAD_QSPI_STIG_OPCODE_RDID, 0, 3,
550 &rdid);
551
552 if (status != 0) {
553 ERROR("Error reading RDID\n");
554 return status;
555 }
556
557 /*
558 * NOTE: The Size code seems to be a form of BCD (binary coded decimal).
559 * The first nibble is the 10's digit and the second nibble is the 1's
560 * digit in the number of bytes.
561 *
562 * Capacity ID samples:
563 * 0x15 : 16 Mb => 2 MiB => 1 << 21 ; BCD=15
564 * 0x16 : 32 Mb => 4 MiB => 1 << 22 ; BCD=16
565 * 0x17 : 64 Mb => 8 MiB => 1 << 23 ; BCD=17
566 * 0x18 : 128 Mb => 16 MiB => 1 << 24 ; BCD=18
567 * 0x19 : 256 Mb => 32 MiB => 1 << 25 ; BCD=19
568 * 0x1a
569 * 0x1b
570 * 0x1c
571 * 0x1d
572 * 0x1e
573 * 0x1f
574 * 0x20 : 512 Mb => 64 MiB => 1 << 26 ; BCD=20
575 * 0x21 : 1024 Mb => 128 MiB => 1 << 27 ; BCD=21
576 */
577
578 cap_code = CAD_QSPI_STIG_RDID_CAPACITYID(rdid);
579
580 if (!(((cap_code >> 4) > 0x9) || ((cap_code & 0xf) > 0x9))) {
581 uint32_t decoded_cap = ((cap_code >> 4) * 10) +
582 (cap_code & 0xf);
583 qspi_device_size = 1 << (decoded_cap + 6);
584 INFO("QSPI Capacity: %x\n\n", qspi_device_size);
585
586 } else {
587 ERROR("Invalid CapacityID encountered: 0x%02x\n",
588 cap_code);
589 return -1;
590 }
591
Hadi Asyrafi0f484b32019-06-17 11:48:58 +0800592 cad_qspi_configure_dev_size(INTEL_QSPI_ADDR_BYTES,
593 INTEL_QSPI_BYTES_PER_DEV,
594 INTEL_BYTES_PER_BLOCK);
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800595
596 INFO("Flash size: %d Bytes\n", qspi_device_size);
597
598 return status;
599}
600
601int cad_qspi_indirect_page_bound_write(uint32_t offset,
602 uint8_t *buffer, uint32_t len)
603{
604 int status = 0, i;
605 uint32_t write_count, write_capacity, *write_data, space,
606 write_fill_level, sram_partition;
607
608 status = cad_qspi_indirect_write_start_bank(offset, len);
609 if (status != 0)
610 return status;
611
612 write_count = 0;
613 sram_partition = CAD_QSPI_SRAMPART_ADDR(mmio_read_32(CAD_QSPI_OFFSET +
614 CAD_QSPI_SRAMPART));
615 write_capacity = (uint32_t) CAD_QSPI_SRAM_FIFO_ENTRY_COUNT -
616 sram_partition;
617
618 while (write_count < len) {
619 write_fill_level = CAD_QSPI_SRAMFILL_INDWRPART(
620 mmio_read_32(CAD_QSPI_OFFSET +
621 CAD_QSPI_SRAMFILL));
622 space = LESS(write_capacity - write_fill_level,
623 (len - write_count) / sizeof(uint32_t));
624 write_data = (uint32_t *)(buffer + write_count);
625 for (i = 0; i < space; ++i)
626 mmio_write_32(CAD_QSPIDATA_OFST, *write_data++);
627
628 write_count += space * sizeof(uint32_t);
629 }
630 return cad_qspi_indirect_write_finish();
631}
632
633int cad_qspi_read_bank(uint8_t *buffer, uint32_t offset, uint32_t size)
634{
635 int status;
636 uint32_t read_count = 0, *read_data;
637 int level = 1, count = 0, i;
638
639 status = cad_qspi_indirect_read_start_bank(offset, size);
640
641 if (status != 0)
642 return status;
643
644 while (read_count < size) {
645 do {
646 level = CAD_QSPI_SRAMFILL_INDRDPART(
647 mmio_read_32(CAD_QSPI_OFFSET +
648 CAD_QSPI_SRAMFILL));
649 read_data = (uint32_t *)(buffer + read_count);
650 for (i = 0; i < level; ++i)
651 *read_data++ = mmio_read_32(CAD_QSPIDATA_OFST);
652
653 read_count += level * sizeof(uint32_t);
654 count++;
655 } while (level > 0);
656 }
657
658 return 0;
659}
660
661int cad_qspi_write_bank(uint32_t offset, uint8_t *buffer, uint32_t size)
662{
663 int status = 0;
664 uint32_t page_offset = offset & (CAD_QSPI_PAGE_SIZE - 1);
665 uint32_t write_size = LESS(size, CAD_QSPI_PAGE_SIZE - page_offset);
666
667 while (size) {
668 status = cad_qspi_indirect_page_bound_write(offset, buffer,
669 write_size);
670 if (status != 0)
671 break;
672
673 offset += write_size;
674 buffer += write_size;
675 size -= write_size;
676 write_size = LESS(size, CAD_QSPI_PAGE_SIZE);
677 }
678 return status;
679}
680
681int cad_qspi_read(void *buffer, uint32_t offset, uint32_t size)
682{
683 uint32_t bank_count, bank_addr, bank_offset, copy_len;
684 uint8_t *read_data;
685 int i, status;
686
687 status = 0;
688
689 if ((offset >= qspi_device_size) ||
690 (offset + size - 1 >= qspi_device_size) ||
Hadi Asyrafi3ceb8d92020-01-13 16:26:22 +0800691 (size == 0)) {
Hadi Asyrafi0f484b32019-06-17 11:48:58 +0800692 ERROR("Invalid read parameter\n");
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800693 return -1;
694 }
695
696 if (CAD_QSPI_INDRD_RD_STAT(mmio_read_32(CAD_QSPI_OFFSET +
697 CAD_QSPI_INDRD))) {
Hadi Asyrafi0f484b32019-06-17 11:48:58 +0800698 ERROR("Read in progress\n");
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800699 return -1;
700 }
701
702 /*
703 * bank_count : Number of bank(s) affected, including partial banks.
704 * bank_addr : Aligned address of the first bank,
705 * including partial bank.
706 * bank_ofst : The offset of the bank to read.
707 * Only used when reading the first bank.
708 */
709 bank_count = CAD_QSPI_BANK_ADDR(offset + size - 1) -
710 CAD_QSPI_BANK_ADDR(offset) + 1;
711 bank_addr = offset & CAD_QSPI_BANK_ADDR_MSK;
712 bank_offset = offset & (CAD_QSPI_BANK_SIZE - 1);
713
714 read_data = (uint8_t *)buffer;
715
716 copy_len = LESS(size, CAD_QSPI_BANK_SIZE - bank_offset);
717
718 for (i = 0; i < bank_count; ++i) {
719 status = cad_qspi_device_bank_select(CAD_QSPI_BANK_ADDR(
720 bank_addr));
721 if (status != 0)
722 break;
723 status = cad_qspi_read_bank(read_data, bank_offset, copy_len);
724 if (status != 0)
725 break;
726
727 bank_addr += CAD_QSPI_BANK_SIZE;
728 read_data += copy_len;
729 size -= copy_len;
730 bank_offset = 0;
731 copy_len = LESS(size, CAD_QSPI_BANK_SIZE);
732 }
733
734 return status;
735}
736
737int cad_qspi_erase(uint32_t offset, uint32_t size)
738{
739 int status = 0;
740 uint32_t subsector_offset = offset & (CAD_QSPI_SUBSECTOR_SIZE - 1);
741 uint32_t erase_size = LESS(size,
742 CAD_QSPI_SUBSECTOR_SIZE - subsector_offset);
743
744 while (size) {
745 status = cad_qspi_erase_subsector(offset);
746 if (status != 0)
747 break;
748
749 offset += erase_size;
750 size -= erase_size;
751 erase_size = LESS(size, CAD_QSPI_SUBSECTOR_SIZE);
752 }
753 return status;
754}
755
756int cad_qspi_write(void *buffer, uint32_t offset, uint32_t size)
757{
758 int status, i;
759 uint32_t bank_count, bank_addr, bank_offset, copy_len;
760 uint8_t *write_data;
761
762 status = 0;
763
764 if ((offset >= qspi_device_size) ||
765 (offset + size - 1 >= qspi_device_size) ||
Hadi Asyrafi3ceb8d92020-01-13 16:26:22 +0800766 (size == 0)) {
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800767 return -2;
Hadi Asyrafi3ceb8d92020-01-13 16:26:22 +0800768 }
Muhammad Hadi Asyrafi Abdul Halim59d501e2019-03-08 19:21:04 +0800769
770 if (CAD_QSPI_INDWR_RDSTAT(mmio_read_32(CAD_QSPI_OFFSET +
771 CAD_QSPI_INDWR))) {
772 ERROR("QSPI Error: Write in progress\n");
773 return -1;
774 }
775
776 bank_count = CAD_QSPI_BANK_ADDR(offset + size - 1) -
777 CAD_QSPI_BANK_ADDR(offset) + 1;
778 bank_addr = offset & CAD_QSPI_BANK_ADDR_MSK;
779 bank_offset = offset & (CAD_QSPI_BANK_SIZE - 1);
780
781 write_data = buffer;
782
783 copy_len = LESS(size, CAD_QSPI_BANK_SIZE - bank_offset);
784
785 for (i = 0; i < bank_count; ++i) {
786 status = cad_qspi_device_bank_select(
787 CAD_QSPI_BANK_ADDR(bank_addr));
788 if (status != 0)
789 break;
790
791 status = cad_qspi_write_bank(bank_offset, write_data,
792 copy_len);
793 if (status != 0)
794 break;
795
796 bank_addr += CAD_QSPI_BANK_SIZE;
797 write_data += copy_len;
798 size -= copy_len;
799 bank_offset = 0;
800
801 copy_len = LESS(size, CAD_QSPI_BANK_SIZE);
802 }
803 return status;
804}
805
806int cad_qspi_update(void *Buffer, uint32_t offset, uint32_t size)
807{
808 int status = 0;
809
810 status = cad_qspi_erase(offset, size);
811 if (status != 0)
812 return status;
813
814 return cad_qspi_write(Buffer, offset, size);
815}
816
817void cad_qspi_reset(void)
818{
819 cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_RESET_EN, 0);
820 cad_qspi_stig_cmd(CAD_QSPI_STIG_OPCODE_RESET_MEM, 0);
821}
822