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