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