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