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