forked from mirrors/qmk_userspace
		
	Added external spi flash driver. (#15419)
This commit is contained in:
		
					parent
					
						
							
								00cc64638c
							
						
					
				
			
			
				commit
				
					
						71c0b97bce
					
				
			
		
					 4 changed files with 547 additions and 0 deletions
				
			
		| 
						 | 
					@ -219,6 +219,21 @@ else
 | 
				
			||||||
  endif
 | 
					  endif
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VALID_FLASH_DRIVER_TYPES := spi
 | 
				
			||||||
 | 
					FLASH_DRIVER ?= no
 | 
				
			||||||
 | 
					ifneq ($(strip $(FLASH_DRIVER)), no)
 | 
				
			||||||
 | 
					    ifeq ($(filter $(FLASH_DRIVER),$(VALID_FLASH_DRIVER_TYPES)),)
 | 
				
			||||||
 | 
					        $(error FLASH_DRIVER="$(FLASH_DRIVER)" is not a valid FLASH driver)
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        OPT_DEFS += -DFLASH_ENABLE
 | 
				
			||||||
 | 
					        ifeq ($(strip $(FLASH_DRIVER)), spi)
 | 
				
			||||||
 | 
					            OPT_DEFS += -DFLASH_DRIVER -DFLASH_SPI
 | 
				
			||||||
 | 
					            COMMON_VPATH += $(DRIVER_PATH)/flash
 | 
				
			||||||
 | 
					            SRC += flash_spi.c
 | 
				
			||||||
 | 
					        endif
 | 
				
			||||||
 | 
					    endif
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RGBLIGHT_ENABLE ?= no
 | 
					RGBLIGHT_ENABLE ?= no
 | 
				
			||||||
VALID_RGBLIGHT_TYPES := WS2812 APA102 custom
 | 
					VALID_RGBLIGHT_TYPES := WS2812 APA102 custom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										24
									
								
								docs/flash_driver.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								docs/flash_driver.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					# FLASH Driver Configuration :id=flash-driver-configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The FLASH driver can be swapped out depending on the needs of the keyboard, or whether extra hardware is present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Driver                             | Description
 | 
				
			||||||
 | 
					-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					`FLASH_DRIVER = spi`               | Supports writing to almost all NOR Flash chips. See the driver section below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## SPI FLASH Driver Configuration :id=spi-flash-driver-configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Currently QMK supports almost all NOR Flash chips over SPI. As such, requires a working spi_master driver configuration. You can override the driver configuration via your config.h:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`config.h` override                            | Description                                                                          | Default Value
 | 
				
			||||||
 | 
					-----------------------------------------------|--------------------------------------------------------------------------------------|-----------------
 | 
				
			||||||
 | 
					`#define EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN`  | SPI Slave select pin in order to inform that the FLASH is currently being addressed  | _none_
 | 
				
			||||||
 | 
					`#define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR`     | Clock divisor used to divide the peripheral clock to derive the SPI frequency        | `8`
 | 
				
			||||||
 | 
					`#define EXTERNAL_FLASH_PAGE_SIZE`             | The Page size of the FLASH in bytes, as specified in the datasheet                   | `256`
 | 
				
			||||||
 | 
					`#define EXTERNAL_FLASH_SECTOR_SIZE`           | The sector size of the FLASH in bytes, as specified in the datasheet                 | `(4 * 1024)`
 | 
				
			||||||
 | 
					`#define EXTERNAL_FLASH_BLOCK_SIZE`            | The block size of the FLASH in bytes, as specified in the datasheet                  | `(64 * 1024)`
 | 
				
			||||||
 | 
					`#define EXTERNAL_FLASH_SIZE`                  | The total size of the FLASH in bytes, as specified in the datasheet                  | `(512 * 1024)`
 | 
				
			||||||
 | 
					`#define EXTERNAL_FLASH_ADDRESS_SIZE`          | The Flash address size in bytes, as specified in datasheet                           | `3`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					!> All the above default configurations are based on MX25L4006E NOR Flash.
 | 
				
			||||||
							
								
								
									
										372
									
								
								drivers/flash/flash_spi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										372
									
								
								drivers/flash/flash_spi.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,372 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
 | 
					#include "wait.h"
 | 
				
			||||||
 | 
					#include "debug.h"
 | 
				
			||||||
 | 
					#include "timer.h"
 | 
				
			||||||
 | 
					#include "flash_spi.h"
 | 
				
			||||||
 | 
					#include "spi_master.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The time-out time of spi flash transmission.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_SPI_TIMEOUT
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_SPI_TIMEOUT 1000
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ID comands */
 | 
				
			||||||
 | 
					#define FLASH_CMD_RDID 0x9F /* RDID (Read Identification) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_RES 0xAB  /* RES (Read Electronic ID) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_REMS 0x90 /* REMS (Read Electronic & Device ID) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* register comands */
 | 
				
			||||||
 | 
					#define FLASH_CMD_WRSR 0x01 /* WRSR (Write Status register) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_RDSR 0x05 /* RDSR (Read Status register) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* READ comands */
 | 
				
			||||||
 | 
					#define FLASH_CMD_READ 0x03     /* READ (1 x I/O) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_FASTREAD 0x0B /* FAST READ (Fast read data) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_DREAD 0x3B    /* DREAD (1In/2 Out fast read) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Program comands */
 | 
				
			||||||
 | 
					#define FLASH_CMD_WREN 0x06 /* WREN (Write Enable) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_WRDI 0x04 /* WRDI (Write Disable) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_PP 0x02   /* PP (page program) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Erase comands */
 | 
				
			||||||
 | 
					#define FLASH_CMD_SE 0x20 /* SE (Sector Erase) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_BE 0xD8 /* BE (Block Erase) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_CE 0x60 /* CE (Chip Erase) hex code: 60 or C7 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Mode setting comands */
 | 
				
			||||||
 | 
					#define FLASH_CMD_DP 0xB9  /* DP (Deep Power Down) */
 | 
				
			||||||
 | 
					#define FLASH_CMD_RDP 0xAB /* RDP (Release form Deep Power Down) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Status register */
 | 
				
			||||||
 | 
					#define FLASH_FLAG_WIP 0x01 /* Write in progress bit */
 | 
				
			||||||
 | 
					#define FLASH_FLAG_WEL 0x02 /* Write enable latch bit */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #define DEBUG_FLASH_SPI_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static 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); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static flash_status_t spi_flash_wait_while_busy(void) {
 | 
				
			||||||
 | 
					    uint32_t       deadline = timer_read32() + EXTERNAL_FLASH_SPI_TIMEOUT;
 | 
				
			||||||
 | 
					    flash_status_t response = FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					    uint8_t        retval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        bool res = spi_flash_start();
 | 
				
			||||||
 | 
					        if (!res) {
 | 
				
			||||||
 | 
					            dprint("Failed to start SPI! [spi flash wait while busy]\n");
 | 
				
			||||||
 | 
					            return FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        spi_write(FLASH_CMD_RDSR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        retval = (uint8_t)spi_read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        spi_stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (timer_read32() >= deadline) {
 | 
				
			||||||
 | 
					            response = FLASH_STATUS_TIMEOUT;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } while (retval & FLASH_FLAG_WIP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static flash_status_t spi_flash_write_enable(void) {
 | 
				
			||||||
 | 
					    bool res = spi_flash_start();
 | 
				
			||||||
 | 
					    if (!res) {
 | 
				
			||||||
 | 
					        dprint("Failed to start SPI! [spi flash write enable]\n");
 | 
				
			||||||
 | 
					        return FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spi_write(FLASH_CMD_WREN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spi_stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static flash_status_t spi_flash_write_disable(void) {
 | 
				
			||||||
 | 
					    bool res = spi_flash_start();
 | 
				
			||||||
 | 
					    if (!res) {
 | 
				
			||||||
 | 
					        dprint("Failed to start SPI! [spi flash write disable]\n");
 | 
				
			||||||
 | 
					        return FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spi_write(FLASH_CMD_WRDI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spi_stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* This function is used for read transfer, write transfer and erase transfer. */
 | 
				
			||||||
 | 
					static flash_status_t spi_flash_transaction(uint8_t cmd, uint32_t addr, uint8_t *data, size_t len) {
 | 
				
			||||||
 | 
					    flash_status_t response = FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					    uint8_t        buffer[EXTERNAL_FLASH_ADDRESS_SIZE + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    buffer[0] = cmd;
 | 
				
			||||||
 | 
					    for (int i = 0; i < EXTERNAL_FLASH_ADDRESS_SIZE; ++i) {
 | 
				
			||||||
 | 
					        buffer[EXTERNAL_FLASH_ADDRESS_SIZE - i] = addr & 0xFF;
 | 
				
			||||||
 | 
					        addr >>= 8;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool res = spi_flash_start();
 | 
				
			||||||
 | 
					    if (!res) {
 | 
				
			||||||
 | 
					        dprint("Failed to start SPI! [spi flash transmit]\n");
 | 
				
			||||||
 | 
					        return FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response = spi_transmit(buffer, sizeof(buffer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((!response) && (data != NULL)) {
 | 
				
			||||||
 | 
					        switch (cmd) {
 | 
				
			||||||
 | 
					            case FLASH_CMD_READ:
 | 
				
			||||||
 | 
					                response = spi_receive(data, len);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case FLASH_CMD_PP:
 | 
				
			||||||
 | 
					                response = spi_transmit(data, len);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                response = FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spi_stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flash_init(void) { spi_init(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_erase_chip(void) {
 | 
				
			||||||
 | 
					    flash_status_t response = FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared. */
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash erase chip]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Enable writes. */
 | 
				
			||||||
 | 
					    response = spi_flash_write_enable();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to write-enable! [spi flash erase chip]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Erase Chip. */
 | 
				
			||||||
 | 
					    bool res = spi_flash_start();
 | 
				
			||||||
 | 
					    if (!res) {
 | 
				
			||||||
 | 
					        dprint("Failed to start SPI! [spi flash erase chip]\n");
 | 
				
			||||||
 | 
					        return FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    spi_write(FLASH_CMD_CE);
 | 
				
			||||||
 | 
					    spi_stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared.*/
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash erase chip]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_erase_sector(uint32_t addr) {
 | 
				
			||||||
 | 
					    flash_status_t response = FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Check that the address exceeds the limit. */
 | 
				
			||||||
 | 
					    if ((addr + (EXTERNAL_FLASH_SECTOR_SIZE)) >= (EXTERNAL_FLASH_SIZE) || ((addr % (EXTERNAL_FLASH_SECTOR_SIZE)) != 0)) {
 | 
				
			||||||
 | 
					        dprintf("Flash erase sector address over limit! [addr:0x%x]\n", (uint32_t)addr);
 | 
				
			||||||
 | 
					        return FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared. */
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash erase sector]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Enable writes. */
 | 
				
			||||||
 | 
					    response = spi_flash_write_enable();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to write-enable! [spi flash erase sector]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Erase Sector. */
 | 
				
			||||||
 | 
					    response = spi_flash_transaction(FLASH_CMD_SE, addr, NULL, 0);
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to erase sector! [spi flash erase sector]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared.*/
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash erase sector]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_erase_block(uint32_t addr) {
 | 
				
			||||||
 | 
					    flash_status_t response = FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Check that the address exceeds the limit. */
 | 
				
			||||||
 | 
					    if ((addr + (EXTERNAL_FLASH_BLOCK_SIZE)) >= (EXTERNAL_FLASH_SIZE) || ((addr % (EXTERNAL_FLASH_BLOCK_SIZE)) != 0)) {
 | 
				
			||||||
 | 
					        dprintf("Flash erase block address over limit! [addr:0x%x]\n", (uint32_t)addr);
 | 
				
			||||||
 | 
					        return FLASH_STATUS_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared. */
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash erase block]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Enable writes. */
 | 
				
			||||||
 | 
					    response = spi_flash_write_enable();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to write-enable! [spi flash erase block]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Erase Block. */
 | 
				
			||||||
 | 
					    response = spi_flash_transaction(FLASH_CMD_BE, addr, NULL, 0);
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to erase block! [spi flash erase block]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared.*/
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash erase block]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_read_block(uint32_t addr, void *buf, size_t len) {
 | 
				
			||||||
 | 
					    flash_status_t response = FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					    uint8_t *      read_buf = (uint8_t *)buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared. */
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash read block]\n");
 | 
				
			||||||
 | 
					        memset(read_buf, 0, len);
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Perform read. */
 | 
				
			||||||
 | 
					    response = spi_flash_transaction(FLASH_CMD_READ, addr, read_buf, len);
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to read block! [spi flash read block]\n");
 | 
				
			||||||
 | 
					        memset(read_buf, 0, len);
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(CONSOLE_ENABLE) && defined(DEBUG_FLASH_SPI_OUTPUT)
 | 
				
			||||||
 | 
					    dprintf("[SPI FLASH R] 0x%08lX: ", addr);
 | 
				
			||||||
 | 
					    for (size_t i = 0; i < len; ++i) {
 | 
				
			||||||
 | 
					        dprintf(" %02X", (int)(((uint8_t *)read_buf)[i]));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    dprintf("\n");
 | 
				
			||||||
 | 
					#endif  // DEBUG_FLASH_SPI_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_write_block(uint32_t addr, const void *buf, size_t len) {
 | 
				
			||||||
 | 
					    flash_status_t response  = FLASH_STATUS_SUCCESS;
 | 
				
			||||||
 | 
					    uint8_t *      write_buf = (uint8_t *)buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (len > 0) {
 | 
				
			||||||
 | 
					        uint32_t page_offset  = addr % EXTERNAL_FLASH_PAGE_SIZE;
 | 
				
			||||||
 | 
					        size_t   write_length = EXTERNAL_FLASH_PAGE_SIZE - page_offset;
 | 
				
			||||||
 | 
					        if (write_length > len) {
 | 
				
			||||||
 | 
					            write_length = len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Wait for the write-in-progress bit to be cleared. */
 | 
				
			||||||
 | 
					        response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					        if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					            dprint("Failed to check WIP flag! [spi flash write block]\n");
 | 
				
			||||||
 | 
					            return response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Enable writes. */
 | 
				
			||||||
 | 
					        response = spi_flash_write_enable();
 | 
				
			||||||
 | 
					        if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					            dprint("Failed to write-enable! [spi flash write block]\n");
 | 
				
			||||||
 | 
					            return response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined(CONSOLE_ENABLE) && defined(DEBUG_FLASH_SPI_OUTPUT)
 | 
				
			||||||
 | 
					        dprintf("[SPI FLASH W] 0x%08lX: ", addr);
 | 
				
			||||||
 | 
					        for (size_t i = 0; i < write_length; i++) {
 | 
				
			||||||
 | 
					            dprintf(" %02X", (int)(uint8_t)(write_buf[i]));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dprintf("\n");
 | 
				
			||||||
 | 
					#endif  // DEBUG_FLASH_SPI_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* Perform the write. */
 | 
				
			||||||
 | 
					        response = spi_flash_transaction(FLASH_CMD_PP, addr, write_buf, write_length);
 | 
				
			||||||
 | 
					        if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					            dprint("Failed to write block! [spi flash write block]\n");
 | 
				
			||||||
 | 
					            return response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        write_buf += write_length;
 | 
				
			||||||
 | 
					        addr += write_length;
 | 
				
			||||||
 | 
					        len -= write_length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Wait for the write-in-progress bit to be cleared. */
 | 
				
			||||||
 | 
					    response = spi_flash_wait_while_busy();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to check WIP flag! [spi flash write block]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Disable writes. */
 | 
				
			||||||
 | 
					    response = spi_flash_write_disable();
 | 
				
			||||||
 | 
					    if (response != FLASH_STATUS_SUCCESS) {
 | 
				
			||||||
 | 
					        dprint("Failed to write-disable! [spi flash write block]\n");
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										136
									
								
								drivers/flash/flash_spi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								drivers/flash/flash_spi.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,136 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* All the following default configurations are based on MX25L4006E Nor FLASH. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The slave select pin of the FLASH.
 | 
				
			||||||
 | 
					    This needs to be a normal GPIO pin_t value, such as B14.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN
 | 
				
			||||||
 | 
					#    error "No chip select pin defined -- missing EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The clock divisor for SPI to ensure that the MCU is within the
 | 
				
			||||||
 | 
					    specifications of the FLASH chip. Generally this will be PCLK divided by
 | 
				
			||||||
 | 
					    the intended divisor -- check your clock settings and the datasheet of
 | 
				
			||||||
 | 
					    your FLASH.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_SPI_CLOCK_DIVISOR
 | 
				
			||||||
 | 
					#    ifdef __AVR__
 | 
				
			||||||
 | 
					#        define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR 4
 | 
				
			||||||
 | 
					#    else
 | 
				
			||||||
 | 
					#        define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR 8
 | 
				
			||||||
 | 
					#    endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The SPI mode to communicate with the FLASH.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_SPI_MODE
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_SPI_MODE 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    Whether or not the SPI communication between the MCU and FLASH should be
 | 
				
			||||||
 | 
					    LSB-first.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_SPI_LSBFIRST
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_SPI_LSBFIRST false
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The Flash address size in bytes, as specified in datasheet.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_ADDRESS_SIZE
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_ADDRESS_SIZE 3
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The page size of the FLASH in bytes, as specified in the datasheet.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_PAGE_SIZE
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_PAGE_SIZE 256
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The sector size of the FLASH in bytes, as specified in the datasheet.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_SECTOR_SIZE
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_SECTOR_SIZE (4 * 1024)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The block size of the FLASH in bytes, as specified in the datasheet.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_BLOCK_SIZE
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_BLOCK_SIZE (64 * 1024)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The total size of the FLASH in bytes, as specified in the datasheet.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef EXTERNAL_FLASH_SIZE
 | 
				
			||||||
 | 
					#    define EXTERNAL_FLASH_SIZE (512 * 1024)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The block count of the FLASH, calculated by total FLASH size and block size.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#define EXTERNAL_FLASH_BLOCK_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_BLOCK_SIZE))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The sector count of the FLASH, calculated by total FLASH size and sector size.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#define EXTERNAL_FLASH_SECTOR_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_SECTOR_SIZE))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    The page count of the FLASH, calculated by total FLASH size and page size.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#define EXTERNAL_FLASH_PAGE_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_PAGE_SIZE))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef int16_t flash_status_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FLASH_STATUS_SUCCESS (0)
 | 
				
			||||||
 | 
					#define FLASH_STATUS_ERROR (-1)
 | 
				
			||||||
 | 
					#define FLASH_STATUS_TIMEOUT (-2)
 | 
				
			||||||
 | 
					#define FLASH_STATUS_BAD_ADDRESS (-3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void flash_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_erase_chip(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_erase_block(uint32_t addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_erase_sector(uint32_t addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_read_block(uint32_t addr, void *buf, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					flash_status_t flash_write_block(uint32_t addr, const void *buf, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue