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