rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
hal_flash_device.c
Go to the documentation of this file.
1/*
2 ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file hal_flash_device.c
19 * @brief Micron W25Q serial flash driver code.
20 *
21 * @addtogroup WINBOND_W25Q
22 * @{
23 */
24
25#include <string.h>
26
27#include "hal.h"
28#include "hal_serial_nor.h"
29
30/*===========================================================================*/
31/* Driver local definitions. */
32/*===========================================================================*/
33
34#define PAGE_SIZE 256U
35#define PAGE_MASK (PAGE_SIZE - 1U)
36
37#if W25Q_USE_SUB_SECTORS == TRUE
38/* 4 KB */
39#define SECTOR_SIZE 0x00001000U
40#define CMD_SECTOR_ERASE W25Q_CMD_SECTOR_ERASE
41#else
42/* 64 KB */
43#define SECTOR_SIZE 0x00010000U
44#define CMD_SECTOR_ERASE W25Q_CMD_64K_BLOCK_ERASE
45#endif
46
47/*===========================================================================*/
48/* Driver exported variables. */
49/*===========================================================================*/
50
51/**
52 * @brief W25Q128 descriptor.
53 */
54flash_descriptor_t snor_descriptor = {
55 .attributes = FLASH_ATTR_ERASED_IS_ONE | FLASH_ATTR_REWRITABLE |
56 FLASH_ATTR_SUSPEND_ERASE_CAPABLE,
57 .page_size = PAGE_SIZE,
58 .sectors_count = 0U, /* It is overwritten.*/
59 .sectors = NULL,
60 .sectors_size = SECTOR_SIZE,
61 .address = 0U,
62 .size = 0U /* It is overwritten.*/
63
64};
65
66#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
67#if (WSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__)
68/**
69 * @brief Fast read command for memory mapped mode.
70 */
71const wspi_command_t snor_memmap_read = {
72 .cmd = W25Q_CMD_FAST_READ,
73 .addr = 0,
74 .dummy = W25Q_READ_DUMMY_CYCLES,
75 .cfg = WSPI_CFG_ADDR_SIZE_24 |
76#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
77 WSPI_CFG_CMD_MODE_ONE_LINE |
78 WSPI_CFG_ADDR_MODE_ONE_LINE |
79 WSPI_CFG_DATA_MODE_ONE_LINE |
80#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
81 WSPI_CFG_CMD_MODE_TWO_LINES |
82 WSPI_CFG_ADDR_MODE_TWO_LINES |
83 WSPI_CFG_DATA_MODE_TWO_LINES |
84#else
85 WSPI_CFG_CMD_MODE_FOUR_LINES |
86 WSPI_CFG_ADDR_MODE_FOUR_LINES |
87 WSPI_CFG_DATA_MODE_FOUR_LINES |
88#endif
89 WSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/
90 WSPI_CFG_ALT_SIZE_8 |
91 WSPI_CFG_SIOO
92};
93#endif
94#endif
95
96/*===========================================================================*/
97/* Driver local variables and types. */
98/*===========================================================================*/
99
100#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI
101/* Initial W25Q_CMD_READ_ID command.*/
102static const wspi_command_t w25q_cmd_read_id = {
103 .cmd = W25Q_CMD_READ_JEDEC_ID,
104 .cfg = 0U |
105#if W25Q_SWITCH_WIDTH == TRUE
106 WSPI_CFG_CMD_MODE_ONE_LINE |
107 WSPI_CFG_DATA_MODE_ONE_LINE,
108#else
109#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
110 WSPI_CFG_CMD_MODE_ONE_LINE |
111 WSPI_CFG_DATA_MODE_ONE_LINE,
112#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
113 WSPI_CFG_CMD_MODE_TWO_LINES |
114 WSPI_CFG_DATA_MODE_TWO_LINES,
115#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
116 WSPI_CFG_CMD_MODE_FOUR_LINES |
117 WSPI_CFG_DATA_MODE_FOUR_LINES,
118#else
119 WSPI_CFG_CMD_MODE_EIGHT_LINES |
120 WSPI_CFG_DATA_MODE_EIGHT_LINES,
121#endif
122#endif
123 .addr = 0,
124 .alt = 0,
125 .dummy = 0
126};
127
128/* Initial W25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER command.*/
129static const wspi_command_t w25q_cmd_write_evconf = {
130 .cmd = W25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER,
131 .cfg = 0U |
132#if W25Q_SWITCH_WIDTH == TRUE
133 WSPI_CFG_CMD_MODE_ONE_LINE |
134 WSPI_CFG_DATA_MODE_ONE_LINE,
135#else
136#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
137 WSPI_CFG_CMD_MODE_ONE_LINE |
138 WSPI_CFG_DATA_MODE_ONE_LINE,
139#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
140 WSPI_CFG_CMD_MODE_TWO_LINES |
141 WSPI_CFG_DATA_MODE_TWO_LINES,
142#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
143 WSPI_CFG_CMD_MODE_FOUR_LINES |
144 WSPI_CFG_DATA_MODE_FOUR_LINES,
145#else
146 WSPI_CFG_CMD_MODE_EIGHT_LINES |
147 WSPI_CFG_DATA_MODE_EIGHT_LINES,
148#endif
149#endif
150 .addr = 0,
151 .alt = 0,
152 .dummy = 0
153};
154
155/* Initial W25Q_CMD_WRITE_ENABLE command.*/
156static const wspi_command_t w25q_cmd_write_enable = {
157 .cmd = W25Q_CMD_WRITE_ENABLE,
158 .cfg = 0U |
159#if W25Q_SWITCH_WIDTH == TRUE
160 WSPI_CFG_CMD_MODE_ONE_LINE,
161#else
162#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
163 WSPI_CFG_CMD_MODE_ONE_LINE,
164#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
165 WSPI_CFG_CMD_MODE_TWO_LINES,
166#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
167 WSPI_CFG_CMD_MODE_FOUR_LINES,
168#else
169 WSPI_CFG_CMD_MODE_EIGHT_LINES,
170#endif
171#endif
172 .addr = 0,
173 .alt = 0,
174 .dummy = 0
175};
176
177#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
178
179/*===========================================================================*/
180/* Driver local functions. */
181/*===========================================================================*/
182
183static bool w25q_find_id(const uint8_t *set, size_t size, uint8_t element) {
184 size_t i;
185
186 for (i = 0; i < size; i++) {
187 if (set[i] == element) {
188 return true;
189 }
190 }
191 return false;
192}
193
194static flash_error_t w25q_poll_status(SNORDriver *devp) {
195 int timeout = 100;
196
197 do {
198 uint8_t sts;
199 /* Read status command.*/
200 bus_cmd_receive(devp, W25Q_CMD_READ_STATUS_REGISTER,
201 1, &sts);
202 if ((sts & W25Q_FLAGS_BUSY) == 0U) {
203 break;
204 }
205#if W25Q_NICE_WAITING == TRUE
206 /* TODO: release bus so other users can access it while we are waiting */
207 osalThreadSleepMilliseconds(1);
208#endif
209 } while (--timeout);
210
211 if (timeout <= 0) {
212 return FLASH_ERROR_PROGRAM;
213 }
214
215 /* Checking for errors.*/
216 /* NOP */
217
218 return FLASH_NO_ERROR;
219}
220
221#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
222static void w25q_reset_memory(SNORDriver *devp) {
223
224 /* 1x W25Q_CMD_RESET_ENABLE command.*/
225 static const wspi_command_t cmd_reset_enable_1 = {
226 .cmd = W25Q_CMD_RESET_ENABLE,
227 .cfg = WSPI_CFG_CMD_MODE_ONE_LINE,
228 .addr = 0,
229 .alt = 0,
230 .dummy = 0
231 };
232
233 /* 1x W25Q_CMD_RESET_MEMORY command.*/
234 static const wspi_command_t cmd_reset_1 = {
235 .cmd = W25Q_CMD_RESET,
236 .cfg = WSPI_CFG_CMD_MODE_ONE_LINE,
237 .addr = 0,
238 .alt = 0,
239 .dummy = 0
240 };
241
242 /* If the device is in one bit mode then the following commands are
243 rejected because shorter than 8 bits. If the device is in multiple
244 bits mode then the commands are accepted and the device is reset to
245 one bit mode.*/
246#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
247 /* 4x W25Q_CMD_RESET_ENABLE command.*/
248 static const wspi_command_t cmd_reset_enable_4 = {
249 .cmd = W25Q_CMD_RESET_ENABLE,
250 .cfg = WSPI_CFG_CMD_MODE_FOUR_LINES,
251 .addr = 0,
252 .alt = 0,
253 .dummy = 0
254 };
255
256 /* 4x W25Q_CMD_RESET_MEMORY command.*/
257 static const wspi_command_t cmd_reset_4 = {
258 .cmd = W25Q_CMD_RESET,
259 .cfg = WSPI_CFG_CMD_MODE_FOUR_LINES,
260 .addr = 0,
261 .alt = 0,
262 .dummy = 0
263 };
264
265 wspiCommand(devp, &cmd_reset_enable_4);
266 wspiCommand(devp, &cmd_reset_memory_4);
267#else
268 /* 2x W25Q_CMD_RESET_ENABLE command.*/
269 static const wspi_command_t cmd_reset_enable_2 = {
270 .cmd = W25Q_CMD_RESET_ENABLE,
271 .cfg = WSPI_CFG_CMD_MODE_TWO_LINES,
272 .addr = 0,
273 .alt = 0,
274 .dummy = 0
275 };
276
277 /* 2x W25Q_CMD_RESET_MEMORY command.*/
278 static const wspi_command_t cmd_reset_memory_2 = {
279 .cmd = W25Q_CMD_RESET_MEMORY,
280 .cfg = WSPI_CFG_CMD_MODE_TWO_LINES,
281 .addr = 0,
282 .alt = 0,
283 .dummy = 0
284 };
285
286 wspiCommand(devp, &cmd_reset_enable_2);
287 wspiCommand(devp, &cmd_reset_2);
288#endif
289
290 /* Now the device should be in one bit mode for sure and we perform a
291 device reset.*/
292 wspiCommand(devp, &cmd_reset_enable_1);
293 wspiCommand(devp, &cmd_reset_1);
294}
295#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
296
297static const uint8_t w25q_manufacturer_ids[] = W25Q_SUPPORTED_MANUFACTURE_IDS;
298static const uint8_t w25q_memory_type_ids[] = W25Q_SUPPORTED_MEMORY_TYPE_IDS;
299
300/*===========================================================================*/
301/* Driver exported functions. */
302/*===========================================================================*/
303
304void snor_device_init(SNORDriver *devp) {
305 uint8_t id[3];
306#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_SPI
307 /* Reading device ID.*/
308 bus_cmd_receive(devp, W25Q_CMD_READ_JEDEC_ID,
309 3U, id);
310#else /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
311 /* Attempting a reset of the XIP mode, it could be in an unexpected state
312 because a CPU reset does not reset the memory too.*/
313 snor_reset_xip(devp);
314
315 /* Attempting a reset of the device, it could be in an unexpected state
316 because a CPU reset does not reset the memory too.*/
317 w25q_reset_memory(devp);
318
319 /* Reading device ID and unique ID.*/
320 wspiReceive(devp, &w25q_cmd_read_id,
321 3U, id);
322#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
323
324 /* Checking if the device is white listed.*/
326 sizeof(w25q_manufacturer_ids),
327 id[0]),
328 "invalid manufacturer id");
330 sizeof(w25q_memory_type_ids),
331 id[1]),
332 "invalid memory type id");
333
334 /* Setting up the device size.*/
335 snor_descriptor.sectors_count = (1U << (size_t)id[2]) /
336 SECTOR_SIZE;
337 snor_descriptor.size = (size_t)snor_descriptor.sectors_count * SECTOR_SIZE;
338}
339
340flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset,
341 size_t n, uint8_t *rp) {
342
343#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI
344 /* Fast read command in WSPI mode.*/
345 bus_cmd_addr_dummy_receive(devp, W25Q_CMD_FAST_READ,
346 offset, W25Q_READ_DUMMY_CYCLES, n, rp);
347#else
348 /* Normal read command in SPI mode.*/
349 bus_cmd_addr_receive(devp, W25Q_CMD_READ,
350 offset, n, rp);
351#endif
352
353 return FLASH_NO_ERROR;
354}
355
356flash_error_t snor_device_program(SNORDriver *devp, flash_offset_t offset,
357 size_t n, const uint8_t *pp) {
358
359 /* Data is programmed page by page.*/
360 while (n > 0U) {
361 flash_error_t err;
362
363 /* Data size that can be written in a single program page operation.*/
364 size_t chunk = (size_t)(((offset | PAGE_MASK) + 1U) - offset);
365 if (chunk > n) {
366 chunk = n;
367 }
368
369 /* Enabling write operation.*/
370 bus_cmd(devp, W25Q_CMD_WRITE_ENABLE);
371
372 /* Page program command.*/
373 bus_cmd_addr_send(devp, W25Q_CMD_PAGE_PROGRAM, offset,
374 chunk, pp);
375
376 /* Wait for status and check errors.*/
377 err = w25q_poll_status(devp);
378 if (err != FLASH_NO_ERROR) {
379
380 return err;
381 }
382
383 /* Next page.*/
384 offset += chunk;
385 pp += chunk;
386 n -= chunk;
387 }
388
389 return FLASH_NO_ERROR;
390}
391
392flash_error_t snor_device_start_erase_all(SNORDriver *devp) {
393
394 /* Enabling write operation.*/
395 bus_cmd(devp, W25Q_CMD_WRITE_ENABLE);
396
397 /* Bulk erase command.*/
398 bus_cmd(devp, W25Q_CMD_BULK_ERASE);
399
400 return FLASH_NO_ERROR;
401}
402
403flash_error_t snor_device_start_erase_sector(SNORDriver *devp,
404 flash_sector_t sector) {
405 flash_offset_t offset = (flash_offset_t)(sector * SECTOR_SIZE);
406
407 /* Enabling write operation.*/
408 bus_cmd(devp, W25Q_CMD_WRITE_ENABLE);
409
410 /* Sector erase command.*/
411 bus_cmd_addr(devp, CMD_SECTOR_ERASE, offset);
412
413 return FLASH_NO_ERROR;
414}
415
416flash_error_t snor_device_verify_erase(SNORDriver *devp,
417 flash_sector_t sector) {
418 uint8_t cmpbuf[W25Q_COMPARE_BUFFER_SIZE];
419 flash_offset_t offset;
420 size_t n;
421
422 /* Read command.*/
423 offset = (flash_offset_t)(sector * SECTOR_SIZE);
424 n = SECTOR_SIZE;
425 while (n > 0U) {
426#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI
427 bus_cmd_addr_dummy_receive(devp, W25Q_CMD_FAST_READ,
428 offset, W25Q_READ_DUMMY_CYCLES,
429 W25Q_COMPARE_BUFFER_SIZE, cmpbuf);
430#else
431 /* Normal read command in SPI mode.*/
432 bus_cmd_addr_receive(devp, W25Q_CMD_READ,
433 offset, W25Q_COMPARE_BUFFER_SIZE, cmpbuf);
434#endif
435
436 /* Checking for erased state of current buffer.*/
437 for (size_t i = 0; i < W25Q_COMPARE_BUFFER_SIZE; i++) {
438 if (cmpbuf[i] != 0xFFU) {
439 /* Ready state again.*/
440 devp->state = FLASH_READY;
441
442 return FLASH_ERROR_VERIFY;
443 }
444 }
445
446 offset += W25Q_COMPARE_BUFFER_SIZE;
447 n -= W25Q_COMPARE_BUFFER_SIZE;
448 }
449
450 return FLASH_NO_ERROR;
451}
452
453flash_error_t snor_device_query_erase(SNORDriver *devp, uint32_t *msec) {
454 uint8_t sts;
455 /* Read status command.*/
456 bus_cmd_receive(devp, W25Q_CMD_READ_STATUS_REGISTER,
457 1, &sts);
458
459 /* If the P/E bit is 1 (busy) report that the operation is still in progress.*/
460 if ((sts & W25Q_FLAGS_BUSY) != 0U) {
461
462 /* Recommended time before polling again, this is a simplified
463 implementation.*/
464 if (msec != NULL) {
465 *msec = 1U;
466 }
467
468 return FLASH_BUSY_ERASING;
469 }
470
471 /* Checking for errors.*/
472 /* NOP */
473
474 return FLASH_NO_ERROR;
475}
476
477flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset,
478 size_t n, uint8_t *rp) {
479
480 (void)devp;
481 (void)rp;
482 (void)offset;
483 (void)n;
484
485 return FLASH_NO_ERROR;
486}
487
488#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
489void snor_activate_xip(SNORDriver *devp) {
490}
491
492void snor_reset_xip(SNORDriver *devp) {
493 wspi_command_t cmd;
494 uint8_t buf[1];
495
496 /* Resetting XIP mode by reading one byte without XIP confirmation bit.*/
497 cmd.cmd = 0U;
498 cmd.alt = 0xFFU;
499 cmd.addr = 0U;
500 cmd.dummy = W25Q_READ_DUMMY_CYCLES;
501 cmd.cfg = WSPI_CFG_CMD_MODE_NONE |
502 WSPI_CFG_ADDR_SIZE_24 |
503#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
504 WSPI_CFG_ADDR_MODE_ONE_LINE |
505 WSPI_CFG_DATA_MODE_ONE_LINE |
506#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
507 WSPI_CFG_ADDR_MODE_TWO_LINES |
508 WSPI_CFG_DATA_MODE_TWO_LINES |
509#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
510 WSPI_CFG_ADDR_MODE_FOUR_LINES |
511 WSPI_CFG_DATA_MODE_FOUR_LINES |
512#else
513 WSPI_CFG_ADDR_MODE_EIGHT_LINES |
514 WSPI_CFG_DATA_MODE_EIGHT_LINES |
515#endif
516 WSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/
517 WSPI_CFG_ALT_SIZE_8;
518 wspiReceive(devp, &cmd, 1, buf);
519
520 /* Enabling write operation.*/
521 bus_cmd(devp, W25Q_CMD_WRITE_ENABLE);
522}
523#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
524
525/** @} */
void snor_activate_xip(SNORDriver *devp)
flash_error_t snor_device_verify_erase(SNORDriver *devp, flash_sector_t sector)
flash_error_t snor_device_query_erase(SNORDriver *devp, uint32_t *msec)
flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset, size_t n, uint8_t *rp)
void snor_reset_xip(SNORDriver *devp)
flash_error_t snor_device_start_erase_sector(SNORDriver *devp, flash_sector_t sector)
const wspi_command_t snor_memmap_read
Fast read command for memory mapped mode.
flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset, size_t n, uint8_t *rp)
flash_error_t snor_device_program(SNORDriver *devp, flash_offset_t offset, size_t n, const uint8_t *pp)
flash_descriptor_t snor_descriptor
Flash descriptor.
void snor_device_init(SNORDriver *devp)
flash_error_t snor_device_start_erase_all(SNORDriver *devp)
static const wspi_command_t w25q_cmd_write_enable
static flash_error_t w25q_poll_status(SNORDriver *devp)
static const uint8_t w25q_manufacturer_ids[]
static bool w25q_find_id(const uint8_t *set, size_t size, uint8_t element)
static const wspi_command_t w25q_cmd_write_evconf
static const wspi_command_t w25q_cmd_read_id
static const uint8_t w25q_memory_type_ids[]
static void w25q_reset_memory(SNORDriver *devp)
composite packet size
uint16_t offset
Definition tunerstudio.h:0