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