xref: /arm-trusted-firmware/drivers/cfi/v2m/v2m_flash.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <errno.h>
8*91f16700Schasinglulu 
9*91f16700Schasinglulu #include <drivers/cfi/v2m_flash.h>
10*91f16700Schasinglulu #include <lib/mmio.h>
11*91f16700Schasinglulu 
12*91f16700Schasinglulu /*
13*91f16700Schasinglulu  * This file supplies a low level interface to the vexpress NOR flash
14*91f16700Schasinglulu  * memory of juno and fvp. This memory is organized as an interleaved
15*91f16700Schasinglulu  * memory of two chips with a 16 bit word. It means that every 32 bit
16*91f16700Schasinglulu  * access is going to access to two different chips. This is very
17*91f16700Schasinglulu  * important when we send commands or read status of the chips.
18*91f16700Schasinglulu  */
19*91f16700Schasinglulu 
20*91f16700Schasinglulu /*
21*91f16700Schasinglulu  * DWS ready poll retries. The number of retries in this driver have been
22*91f16700Schasinglulu  * obtained empirically from Juno. FVP implements a zero wait state NOR flash
23*91f16700Schasinglulu  * model
24*91f16700Schasinglulu  */
25*91f16700Schasinglulu #define DWS_WORD_PROGRAM_RETRIES	1000
26*91f16700Schasinglulu #define DWS_WORD_ERASE_RETRIES		3000000
27*91f16700Schasinglulu #define DWS_WORD_LOCK_RETRIES		1000
28*91f16700Schasinglulu 
29*91f16700Schasinglulu /* Helper macro to detect end of command */
30*91f16700Schasinglulu #define NOR_CMD_END (NOR_DWS | (NOR_DWS << 16l))
31*91f16700Schasinglulu 
32*91f16700Schasinglulu /* Helper macros to access two flash banks in parallel */
33*91f16700Schasinglulu #define NOR_2X16(d)			((d << 16) | (d & 0xffff))
34*91f16700Schasinglulu 
35*91f16700Schasinglulu static unsigned int nor_status(uintptr_t base_addr)
36*91f16700Schasinglulu {
37*91f16700Schasinglulu 	unsigned long status;
38*91f16700Schasinglulu 
39*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
40*91f16700Schasinglulu 	status = mmio_read_32(base_addr);
41*91f16700Schasinglulu 	status |= status >> 16; /* merge status from both flash banks */
42*91f16700Schasinglulu 
43*91f16700Schasinglulu 	return status & 0xFFFF;
44*91f16700Schasinglulu }
45*91f16700Schasinglulu 
46*91f16700Schasinglulu /*
47*91f16700Schasinglulu  * Poll Write State Machine.
48*91f16700Schasinglulu  * Return values:
49*91f16700Schasinglulu  *    0      = WSM ready
50*91f16700Schasinglulu  *    -EBUSY = WSM busy after the number of retries
51*91f16700Schasinglulu  */
52*91f16700Schasinglulu static int nor_poll_dws(uintptr_t base_addr, unsigned long int retries)
53*91f16700Schasinglulu {
54*91f16700Schasinglulu 	unsigned long status;
55*91f16700Schasinglulu 
56*91f16700Schasinglulu 	do {
57*91f16700Schasinglulu 		nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
58*91f16700Schasinglulu 		status = mmio_read_32(base_addr);
59*91f16700Schasinglulu 		if ((status & NOR_CMD_END) == NOR_CMD_END)
60*91f16700Schasinglulu 			return 0;
61*91f16700Schasinglulu 	} while (retries-- > 0);
62*91f16700Schasinglulu 
63*91f16700Schasinglulu 	return -EBUSY;
64*91f16700Schasinglulu }
65*91f16700Schasinglulu 
66*91f16700Schasinglulu /*
67*91f16700Schasinglulu  * Return values:
68*91f16700Schasinglulu  *    0      = success
69*91f16700Schasinglulu  *    -EPERM = Device protected or Block locked
70*91f16700Schasinglulu  *    -EIO   = General I/O error
71*91f16700Schasinglulu  */
72*91f16700Schasinglulu static int nor_full_status_check(uintptr_t base_addr)
73*91f16700Schasinglulu {
74*91f16700Schasinglulu 	unsigned long status;
75*91f16700Schasinglulu 
76*91f16700Schasinglulu 	/* Full status check */
77*91f16700Schasinglulu 	status = nor_status(base_addr);
78*91f16700Schasinglulu 
79*91f16700Schasinglulu 	if (status & (NOR_PS | NOR_BLS | NOR_ESS | NOR_PSS))
80*91f16700Schasinglulu 		return -EPERM;
81*91f16700Schasinglulu 	if (status & (NOR_VPPS | NOR_ES))
82*91f16700Schasinglulu 		return -EIO;
83*91f16700Schasinglulu 	return 0;
84*91f16700Schasinglulu }
85*91f16700Schasinglulu 
86*91f16700Schasinglulu void nor_send_cmd(uintptr_t base_addr, unsigned long cmd)
87*91f16700Schasinglulu {
88*91f16700Schasinglulu 	mmio_write_32(base_addr, NOR_2X16(cmd));
89*91f16700Schasinglulu }
90*91f16700Schasinglulu 
91*91f16700Schasinglulu /*
92*91f16700Schasinglulu  * This function programs a word in the flash. Be aware that it only
93*91f16700Schasinglulu  * can reset bits that were previously set. It cannot set bits that
94*91f16700Schasinglulu  * were previously reset. The resulting bits = old_bits & new bits.
95*91f16700Schasinglulu  * Return values:
96*91f16700Schasinglulu  *  0 = success
97*91f16700Schasinglulu  *  otherwise it returns a negative value
98*91f16700Schasinglulu  */
99*91f16700Schasinglulu int nor_word_program(uintptr_t base_addr, unsigned long data)
100*91f16700Schasinglulu {
101*91f16700Schasinglulu 	uint32_t status;
102*91f16700Schasinglulu 	int ret;
103*91f16700Schasinglulu 
104*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
105*91f16700Schasinglulu 
106*91f16700Schasinglulu 	/* Set the device in write word mode */
107*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_WORD_PROGRAM);
108*91f16700Schasinglulu 	mmio_write_32(base_addr, data);
109*91f16700Schasinglulu 
110*91f16700Schasinglulu 	ret = nor_poll_dws(base_addr, DWS_WORD_PROGRAM_RETRIES);
111*91f16700Schasinglulu 	if (ret == 0) {
112*91f16700Schasinglulu 		/* Full status check */
113*91f16700Schasinglulu 		nor_send_cmd(base_addr, NOR_CMD_READ_STATUS_REG);
114*91f16700Schasinglulu 		status = mmio_read_32(base_addr);
115*91f16700Schasinglulu 
116*91f16700Schasinglulu 		if (status & (NOR_PS | NOR_BLS)) {
117*91f16700Schasinglulu 			nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
118*91f16700Schasinglulu 			ret = -EPERM;
119*91f16700Schasinglulu 		}
120*91f16700Schasinglulu 	}
121*91f16700Schasinglulu 
122*91f16700Schasinglulu 	if (ret == 0)
123*91f16700Schasinglulu 		ret = nor_full_status_check(base_addr);
124*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
125*91f16700Schasinglulu 
126*91f16700Schasinglulu 	return ret;
127*91f16700Schasinglulu }
128*91f16700Schasinglulu 
129*91f16700Schasinglulu /*
130*91f16700Schasinglulu  * Erase a full 256K block
131*91f16700Schasinglulu  * Return values:
132*91f16700Schasinglulu  *  0 = success
133*91f16700Schasinglulu  *  otherwise it returns a negative value
134*91f16700Schasinglulu  */
135*91f16700Schasinglulu int nor_erase(uintptr_t base_addr)
136*91f16700Schasinglulu {
137*91f16700Schasinglulu 	int ret;
138*91f16700Schasinglulu 
139*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
140*91f16700Schasinglulu 
141*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE);
142*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_BLOCK_ERASE_ACK);
143*91f16700Schasinglulu 
144*91f16700Schasinglulu 	ret = nor_poll_dws(base_addr, DWS_WORD_ERASE_RETRIES);
145*91f16700Schasinglulu 	if (ret == 0)
146*91f16700Schasinglulu 		ret = nor_full_status_check(base_addr);
147*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
148*91f16700Schasinglulu 
149*91f16700Schasinglulu 	return ret;
150*91f16700Schasinglulu }
151*91f16700Schasinglulu 
152*91f16700Schasinglulu /*
153*91f16700Schasinglulu  * Lock a full 256 block
154*91f16700Schasinglulu  * Return values:
155*91f16700Schasinglulu  *  0 = success
156*91f16700Schasinglulu  *  otherwise it returns a negative value
157*91f16700Schasinglulu  */
158*91f16700Schasinglulu int nor_lock(uintptr_t base_addr)
159*91f16700Schasinglulu {
160*91f16700Schasinglulu 	int ret;
161*91f16700Schasinglulu 
162*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
163*91f16700Schasinglulu 
164*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
165*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_LOCK_BLOCK);
166*91f16700Schasinglulu 
167*91f16700Schasinglulu 	ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
168*91f16700Schasinglulu 	if (ret == 0)
169*91f16700Schasinglulu 		ret = nor_full_status_check(base_addr);
170*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
171*91f16700Schasinglulu 
172*91f16700Schasinglulu 	return ret;
173*91f16700Schasinglulu }
174*91f16700Schasinglulu 
175*91f16700Schasinglulu /*
176*91f16700Schasinglulu  * unlock a full 256 block
177*91f16700Schasinglulu  * Return values:
178*91f16700Schasinglulu  *  0 = success
179*91f16700Schasinglulu  *  otherwise it returns a negative value
180*91f16700Schasinglulu  */
181*91f16700Schasinglulu int nor_unlock(uintptr_t base_addr)
182*91f16700Schasinglulu {
183*91f16700Schasinglulu 	int ret;
184*91f16700Schasinglulu 
185*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_CLEAR_STATUS_REG);
186*91f16700Schasinglulu 
187*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_LOCK_UNLOCK);
188*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_UNLOCK_BLOCK);
189*91f16700Schasinglulu 
190*91f16700Schasinglulu 	ret = nor_poll_dws(base_addr, DWS_WORD_LOCK_RETRIES);
191*91f16700Schasinglulu 	if (ret == 0)
192*91f16700Schasinglulu 		ret = nor_full_status_check(base_addr);
193*91f16700Schasinglulu 	nor_send_cmd(base_addr, NOR_CMD_READ_ARRAY);
194*91f16700Schasinglulu 
195*91f16700Schasinglulu 	return ret;
196*91f16700Schasinglulu }
197