summaryrefslogtreecommitdiff
path: root/drivers/flash
diff options
context:
space:
mode:
authorJoy Lee <chang.li@westberrytech.com>2022-02-12 04:26:16 +0800
committerGitHub <noreply@github.com>2022-02-11 20:26:16 +0000
commit71c0b97bced7722abaa0adefd57066cc065538b5 (patch)
tree6f50c8a53fb7a7faae949154351be658ce5144e8 /drivers/flash
parent00cc64638c7ffa90b1acc10c420daaa3795a87ba (diff)
Added external spi flash driver. (#15419)
Diffstat (limited to 'drivers/flash')
-rw-r--r--drivers/flash/flash_spi.c372
-rw-r--r--drivers/flash/flash_spi.h136
2 files changed, 508 insertions, 0 deletions
diff --git a/drivers/flash/flash_spi.c b/drivers/flash/flash_spi.c
new file mode 100644
index 0000000000..2fa8891e38
--- /dev/null
+++ b/drivers/flash/flash_spi.c
@@ -0,0 +1,372 @@
1/*
2Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include <string.h>
19
20#include "util.h"
21#include "wait.h"
22#include "debug.h"
23#include "timer.h"
24#include "flash_spi.h"
25#include "spi_master.h"
26
27/*
28 The time-out time of spi flash transmission.
29*/
30#ifndef EXTERNAL_FLASH_SPI_TIMEOUT
31# define EXTERNAL_FLASH_SPI_TIMEOUT 1000
32#endif
33
34/* ID comands */
35#define FLASH_CMD_RDID 0x9F /* RDID (Read Identification) */
36#define FLASH_CMD_RES 0xAB /* RES (Read Electronic ID) */
37#define FLASH_CMD_REMS 0x90 /* REMS (Read Electronic & Device ID) */
38
39/* register comands */
40#define FLASH_CMD_WRSR 0x01 /* WRSR (Write Status register) */
41#define FLASH_CMD_RDSR 0x05 /* RDSR (Read Status register) */
42
43/* READ comands */
44#define FLASH_CMD_READ 0x03 /* READ (1 x I/O) */
45#define FLASH_CMD_FASTREAD 0x0B /* FAST READ (Fast read data) */
46#define FLASH_CMD_DREAD 0x3B /* DREAD (1In/2 Out fast read) */
47
48/* Program comands */
49#define FLASH_CMD_WREN 0x06 /* WREN (Write Enable) */
50#define FLASH_CMD_WRDI 0x04 /* WRDI (Write Disable) */
51#define FLASH_CMD_PP 0x02 /* PP (page program) */
52
53/* Erase comands */
54#define FLASH_CMD_SE 0x20 /* SE (Sector Erase) */
55#define FLASH_CMD_BE 0xD8 /* BE (Block Erase) */
56#define FLASH_CMD_CE 0x60 /* CE (Chip Erase) hex code: 60 or C7 */
57
58/* Mode setting comands */
59#define FLASH_CMD_DP 0xB9 /* DP (Deep Power Down) */
60#define FLASH_CMD_RDP 0xAB /* RDP (Release form Deep Power Down) */
61
62/* Status register */
63#define FLASH_FLAG_WIP 0x01 /* Write in progress bit */
64#define FLASH_FLAG_WEL 0x02 /* Write enable latch bit */
65
66// #define DEBUG_FLASH_SPI_OUTPUT
67
68static bool spi_flash_start(void) { return spi_start(EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN, EXTERNAL_FLASH_SPI_LSBFIRST, EXTERNAL_FLASH_SPI_MODE, EXTERNAL_FLASH_SPI_CLOCK_DIVISOR); }
69
70static flash_status_t spi_flash_wait_while_busy(void) {
71 uint32_t deadline = timer_read32() + EXTERNAL_FLASH_SPI_TIMEOUT;
72 flash_status_t response = FLASH_STATUS_SUCCESS;
73 uint8_t retval;
74
75 do {
76 bool res = spi_flash_start();
77 if (!res) {
78 dprint("Failed to start SPI! [spi flash wait while busy]\n");
79 return FLASH_STATUS_ERROR;
80 }
81
82 spi_write(FLASH_CMD_RDSR);
83
84 retval = (uint8_t)spi_read();
85
86 spi_stop();
87
88 if (timer_read32() >= deadline) {
89 response = FLASH_STATUS_TIMEOUT;
90 break;
91 }
92 } while (retval & FLASH_FLAG_WIP);
93
94 return response;
95}
96
97static flash_status_t spi_flash_write_enable(void) {
98 bool res = spi_flash_start();
99 if (!res) {
100 dprint("Failed to start SPI! [spi flash write enable]\n");
101 return FLASH_STATUS_ERROR;
102 }
103
104 spi_write(FLASH_CMD_WREN);
105
106 spi_stop();
107
108 return FLASH_STATUS_SUCCESS;
109}
110
111static flash_status_t spi_flash_write_disable(void) {
112 bool res = spi_flash_start();
113 if (!res) {
114 dprint("Failed to start SPI! [spi flash write disable]\n");
115 return FLASH_STATUS_ERROR;
116 }
117
118 spi_write(FLASH_CMD_WRDI);
119
120 spi_stop();
121
122 return FLASH_STATUS_SUCCESS;
123}
124
125/* This function is used for read transfer, write transfer and erase transfer. */
126static flash_status_t spi_flash_transaction(uint8_t cmd, uint32_t addr, uint8_t *data, size_t len) {
127 flash_status_t response = FLASH_STATUS_SUCCESS;
128 uint8_t buffer[EXTERNAL_FLASH_ADDRESS_SIZE + 1];
129
130 buffer[0] = cmd;
131 for (int i = 0; i < EXTERNAL_FLASH_ADDRESS_SIZE; ++i) {
132 buffer[EXTERNAL_FLASH_ADDRESS_SIZE - i] = addr & 0xFF;
133 addr >>= 8;
134 }
135
136 bool res = spi_flash_start();
137 if (!res) {
138 dprint("Failed to start SPI! [spi flash transmit]\n");
139 return FLASH_STATUS_ERROR;
140 }
141
142 response = spi_transmit(buffer, sizeof(buffer));
143
144 if ((!response) && (data != NULL)) {
145 switch (cmd) {
146 case FLASH_CMD_READ:
147 response = spi_receive(data, len);
148 break;
149 case FLASH_CMD_PP:
150 response = spi_transmit(data, len);
151 break;
152 default:
153 response = FLASH_STATUS_ERROR;
154 break;
155 }
156 }
157
158 spi_stop();
159
160 return response;
161}
162
163void flash_init(void) { spi_init(); }
164
165flash_status_t flash_erase_chip(void) {
166 flash_status_t response = FLASH_STATUS_SUCCESS;
167
168 /* Wait for the write-in-progress bit to be cleared. */
169 response = spi_flash_wait_while_busy();
170 if (response != FLASH_STATUS_SUCCESS) {
171 dprint("Failed to check WIP flag! [spi flash erase chip]\n");
172 return response;
173 }
174
175 /* Enable writes. */
176 response = spi_flash_write_enable();
177 if (response != FLASH_STATUS_SUCCESS) {
178 dprint("Failed to write-enable! [spi flash erase chip]\n");
179 return response;
180 }
181
182 /* Erase Chip. */
183 bool res = spi_flash_start();
184 if (!res) {
185 dprint("Failed to start SPI! [spi flash erase chip]\n");
186 return FLASH_STATUS_ERROR;
187 }
188 spi_write(FLASH_CMD_CE);
189 spi_stop();
190
191 /* Wait for the write-in-progress bit to be cleared.*/
192 response = spi_flash_wait_while_busy();
193 if (response != FLASH_STATUS_SUCCESS) {
194 dprint("Failed to check WIP flag! [spi flash erase chip]\n");
195 return response;
196 }
197
198 return response;
199}
200
201flash_status_t flash_erase_sector(uint32_t addr) {
202 flash_status_t response = FLASH_STATUS_SUCCESS;
203
204 /* Check that the address exceeds the limit. */
205 if ((addr + (EXTERNAL_FLASH_SECTOR_SIZE)) >= (EXTERNAL_FLASH_SIZE) || ((addr % (EXTERNAL_FLASH_SECTOR_SIZE)) != 0)) {
206 dprintf("Flash erase sector address over limit! [addr:0x%x]\n", (uint32_t)addr);
207 return FLASH_STATUS_ERROR;
208 }
209
210 /* Wait for the write-in-progress bit to be cleared. */
211 response = spi_flash_wait_while_busy();
212 if (response != FLASH_STATUS_SUCCESS) {
213 dprint("Failed to check WIP flag! [spi flash erase sector]\n");
214 return response;
215 }
216
217 /* Enable writes. */
218 response = spi_flash_write_enable();
219 if (response != FLASH_STATUS_SUCCESS) {
220 dprint("Failed to write-enable! [spi flash erase sector]\n");
221 return response;
222 }
223
224 /* Erase Sector. */
225 response = spi_flash_transaction(FLASH_CMD_SE, addr, NULL, 0);
226 if (response != FLASH_STATUS_SUCCESS) {
227 dprint("Failed to erase sector! [spi flash erase sector]\n");
228 return response;
229 }
230
231 /* Wait for the write-in-progress bit to be cleared.*/
232 response = spi_flash_wait_while_busy();
233 if (response != FLASH_STATUS_SUCCESS) {
234 dprint("Failed to check WIP flag! [spi flash erase sector]\n");
235 return response;
236 }
237
238 return response;
239}
240
241flash_status_t flash_erase_block(uint32_t addr) {
242 flash_status_t response = FLASH_STATUS_SUCCESS;
243
244 /* Check that the address exceeds the limit. */
245 if ((addr + (EXTERNAL_FLASH_BLOCK_SIZE)) >= (EXTERNAL_FLASH_SIZE) || ((addr % (EXTERNAL_FLASH_BLOCK_SIZE)) != 0)) {
246 dprintf("Flash erase block address over limit! [addr:0x%x]\n", (uint32_t)addr);
247 return FLASH_STATUS_ERROR;
248 }
249
250 /* Wait for the write-in-progress bit to be cleared. */
251 response = spi_flash_wait_while_busy();
252 if (response != FLASH_STATUS_SUCCESS) {
253 dprint("Failed to check WIP flag! [spi flash erase block]\n");
254 return response;
255 }
256
257 /* Enable writes. */
258 response = spi_flash_write_enable();
259 if (response != FLASH_STATUS_SUCCESS) {
260 dprint("Failed to write-enable! [spi flash erase block]\n");
261 return response;
262 }
263
264 /* Erase Block. */
265 response = spi_flash_transaction(FLASH_CMD_BE, addr, NULL, 0);
266 if (response != FLASH_STATUS_SUCCESS) {
267 dprint("Failed to erase block! [spi flash erase block]\n");
268 return response;
269 }
270
271 /* Wait for the write-in-progress bit to be cleared.*/
272 response = spi_flash_wait_while_busy();
273 if (response != FLASH_STATUS_SUCCESS) {
274 dprint("Failed to check WIP flag! [spi flash erase block]\n");
275 return response;
276 }
277
278 return response;
279}
280
281flash_status_t flash_read_block(uint32_t addr, void *buf, size_t len) {
282 flash_status_t response = FLASH_STATUS_SUCCESS;
283 uint8_t * read_buf = (uint8_t *)buf;
284
285 /* Wait for the write-in-progress bit to be cleared. */
286 response = spi_flash_wait_while_busy();
287 if (response != FLASH_STATUS_SUCCESS) {
288 dprint("Failed to check WIP flag! [spi flash read block]\n");
289 memset(read_buf, 0, len);
290 return response;
291 }
292
293 /* Perform read. */
294 response = spi_flash_transaction(FLASH_CMD_READ, addr, read_buf, len);
295 if (response != FLASH_STATUS_SUCCESS) {
296 dprint("Failed to read block! [spi flash read block]\n");
297 memset(read_buf, 0, len);
298 return response;
299 }
300
301#if defined(CONSOLE_ENABLE) && defined(DEBUG_FLASH_SPI_OUTPUT)
302 dprintf("[SPI FLASH R] 0x%08lX: ", addr);
303 for (size_t i = 0; i < len; ++i) {
304 dprintf(" %02X", (int)(((uint8_t *)read_buf)[i]));
305 }
306 dprintf("\n");
307#endif // DEBUG_FLASH_SPI_OUTPUT
308
309 return response;
310}
311
312flash_status_t flash_write_block(uint32_t addr, const void *buf, size_t len) {
313 flash_status_t response = FLASH_STATUS_SUCCESS;
314 uint8_t * write_buf = (uint8_t *)buf;
315
316 while (len > 0) {
317 uint32_t page_offset = addr % EXTERNAL_FLASH_PAGE_SIZE;
318 size_t write_length = EXTERNAL_FLASH_PAGE_SIZE - page_offset;
319 if (write_length > len) {
320 write_length = len;
321 }
322
323 /* Wait for the write-in-progress bit to be cleared. */
324 response = spi_flash_wait_while_busy();
325 if (response != FLASH_STATUS_SUCCESS) {
326 dprint("Failed to check WIP flag! [spi flash write block]\n");
327 return response;
328 }
329
330 /* Enable writes. */
331 response = spi_flash_write_enable();
332 if (response != FLASH_STATUS_SUCCESS) {
333 dprint("Failed to write-enable! [spi flash write block]\n");
334 return response;
335 }
336
337#if defined(CONSOLE_ENABLE) && defined(DEBUG_FLASH_SPI_OUTPUT)
338 dprintf("[SPI FLASH W] 0x%08lX: ", addr);
339 for (size_t i = 0; i < write_length; i++) {
340 dprintf(" %02X", (int)(uint8_t)(write_buf[i]));
341 }
342 dprintf("\n");
343#endif // DEBUG_FLASH_SPI_OUTPUT
344
345 /* Perform the write. */
346 response = spi_flash_transaction(FLASH_CMD_PP, addr, write_buf, write_length);
347 if (response != FLASH_STATUS_SUCCESS) {
348 dprint("Failed to write block! [spi flash write block]\n");
349 return response;
350 }
351
352 write_buf += write_length;
353 addr += write_length;
354 len -= write_length;
355 }
356
357 /* Wait for the write-in-progress bit to be cleared. */
358 response = spi_flash_wait_while_busy();
359 if (response != FLASH_STATUS_SUCCESS) {
360 dprint("Failed to check WIP flag! [spi flash write block]\n");
361 return response;
362 }
363
364 /* Disable writes. */
365 response = spi_flash_write_disable();
366 if (response != FLASH_STATUS_SUCCESS) {
367 dprint("Failed to write-disable! [spi flash write block]\n");
368 return response;
369 }
370
371 return response;
372}
diff --git a/drivers/flash/flash_spi.h b/drivers/flash/flash_spi.h
new file mode 100644
index 0000000000..abe95e955e
--- /dev/null
+++ b/drivers/flash/flash_spi.h
@@ -0,0 +1,136 @@
1/*
2Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20/* All the following default configurations are based on MX25L4006E Nor FLASH. */
21
22/*
23 The slave select pin of the FLASH.
24 This needs to be a normal GPIO pin_t value, such as B14.
25*/
26#ifndef EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN
27# error "No chip select pin defined -- missing EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN"
28#endif
29
30/*
31 The clock divisor for SPI to ensure that the MCU is within the
32 specifications of the FLASH chip. Generally this will be PCLK divided by
33 the intended divisor -- check your clock settings and the datasheet of
34 your FLASH.
35*/
36#ifndef EXTERNAL_FLASH_SPI_CLOCK_DIVISOR
37# ifdef __AVR__
38# define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR 4
39# else
40# define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR 8
41# endif
42#endif
43
44/*
45 The SPI mode to communicate with the FLASH.
46*/
47#ifndef EXTERNAL_FLASH_SPI_MODE
48# define EXTERNAL_FLASH_SPI_MODE 0
49#endif
50
51/*
52 Whether or not the SPI communication between the MCU and FLASH should be
53 LSB-first.
54*/
55#ifndef EXTERNAL_FLASH_SPI_LSBFIRST
56# define EXTERNAL_FLASH_SPI_LSBFIRST false
57#endif
58
59/*
60 The Flash address size in bytes, as specified in datasheet.
61*/
62#ifndef EXTERNAL_FLASH_ADDRESS_SIZE
63# define EXTERNAL_FLASH_ADDRESS_SIZE 3
64#endif
65
66/*
67 The page size of the FLASH in bytes, as specified in the datasheet.
68*/
69#ifndef EXTERNAL_FLASH_PAGE_SIZE
70# define EXTERNAL_FLASH_PAGE_SIZE 256
71#endif
72
73/*
74 The sector size of the FLASH in bytes, as specified in the datasheet.
75*/
76#ifndef EXTERNAL_FLASH_SECTOR_SIZE
77# define EXTERNAL_FLASH_SECTOR_SIZE (4 * 1024)
78#endif
79
80/*
81 The block size of the FLASH in bytes, as specified in the datasheet.
82*/
83#ifndef EXTERNAL_FLASH_BLOCK_SIZE
84# define EXTERNAL_FLASH_BLOCK_SIZE (64 * 1024)
85#endif
86
87/*
88 The total size of the FLASH in bytes, as specified in the datasheet.
89*/
90#ifndef EXTERNAL_FLASH_SIZE
91# define EXTERNAL_FLASH_SIZE (512 * 1024)
92#endif
93
94/*
95 The block count of the FLASH, calculated by total FLASH size and block size.
96*/
97#define EXTERNAL_FLASH_BLOCK_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_BLOCK_SIZE))
98
99/*
100 The sector count of the FLASH, calculated by total FLASH size and sector size.
101*/
102#define EXTERNAL_FLASH_SECTOR_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_SECTOR_SIZE))
103
104/*
105 The page count of the FLASH, calculated by total FLASH size and page size.
106*/
107#define EXTERNAL_FLASH_PAGE_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_PAGE_SIZE))
108
109typedef int16_t flash_status_t;
110
111#define FLASH_STATUS_SUCCESS (0)
112#define FLASH_STATUS_ERROR (-1)
113#define FLASH_STATUS_TIMEOUT (-2)
114#define FLASH_STATUS_BAD_ADDRESS (-3)
115
116#ifdef __cplusplus
117extern "C" {
118#endif
119
120#include <stdint.h>
121
122void flash_init(void);
123
124flash_status_t flash_erase_chip(void);
125
126flash_status_t flash_erase_block(uint32_t addr);
127
128flash_status_t flash_erase_sector(uint32_t addr);
129
130flash_status_t flash_read_block(uint32_t addr, void *buf, size_t len);
131
132flash_status_t flash_write_block(uint32_t addr, const void *buf, size_t len);
133
134#ifdef __cplusplus
135}
136#endif